From bc71bf297e532e97c45a2dd09c59f43d0fe7fcf7 Mon Sep 17 00:00:00 2001 From: Richard Harrison <rjh@zaretto.com> Date: Sun, 17 Sep 2017 08:46:45 +0200 Subject: [PATCH] Add Property / object update manager to props Manage updates when a value has changed more than a predetermined amount. This makes updating displays (e.g. canvas), or performing actions based on a property (or value in a hash) changing by more than the preset amount. --- Nasal/props.nas | 222 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/Nasal/props.nas b/Nasal/props.nas index 6387b679f..8ff8bbfef 100644 --- a/Nasal/props.nas +++ b/Nasal/props.nas @@ -346,3 +346,225 @@ var runBinding = func(node, module = nil) { var cmd = node.getNode("command", 1).getValue() or "null"; condition(node.getNode("condition")) ? fgcommand(cmd, node) : 0; } + + #--------------------------------------------------------------------------- + # Property / object update manager + # + # - Manage updates when a value has changed more than a predetermined amount. + # This class is designed to make updating displays (e.g. canvas), or + # performing actions based on a property (or value in a hash) changing + # by more than the preset amount. + # This can make a significant improvement to performance compared to simply + # redrawing a canvas in an update loop. + # - Author : Richard Harrison (rjh@zaretto.com) + #---------------------------------------------------------------------------*/ + +#example usage: +# this is using the hashlist (which works well with an Emesary notification) +# basically when the method is called it will call each section (in the lambda) +# when the value changes by more than the amount specified as the second parameter. +# It is possible to reference multiple elements from the hashlist in each newFromHashList; if either +# one changes then it will result in the lambda being called. +# +# obj.update_items = [ +# UpdateManager.newFromHashList(["VV_x","VV_y"], 0.01, func(val) +# { +# obj.VV.setTranslation (val.VV_x, val.VV_y + pitch_offset); +# }), +# UpdateManager.newFromHashList(["pitch","roll"], 0.025, func(hdp) +# { +# obj.ladder.setTranslation (0.0, hdp.pitch * pitch_factor+pitch_offset); +# obj.ladder.setCenter (118,830 - hdp.pitch * pitch_factor-pitch_offset); +# obj.ladder.setRotation (-hdp.roll_rad); +# obj.roll_pointer.setRotation (hdp.roll_rad); +# }), +# props.UpdateManager.FromProperty("velocities/airspeed-kt", 0.01, func(val) +# { +# obj.ias_range.setTranslation(0, val * ias_range_factor); +# }), +# props.UpdateManager.FromPropertyHashList(["orientation/alpha-indicated-deg", "orientation/side-slip-deg"], 0.1, func(val) +# { +# obj.VV_x = val.property["orientation/side-slip-deg"].getValue()*10; # adjust for view +# obj.VV_y = val.property["orientation/alpha-indicated-deg"].getValue()*10; # adjust for view +# obj.VV.setTranslation (obj.VV_x, obj.VV_y); +# }), +# ] +# +#==== the update loop then becomes ====== +# +# foreach(var update_item; me.update_items) +# { +# # hdp is a data provider that can be used as the hashlist for the property +# # update from hash methods. +# update_item.update(hdp); +# } +# +var UpdateManager = +{ + _updateProperty : func(_property) + { + }, + FromProperty : func(_propname, _delta, _changed_method) + { + var obj = {parents : [UpdateManager] }; + obj.propname = _propname; + obj.property = props.globals.getNode(_propname); + obj.delta = _delta; + obj.curval = obj.property.getValue(); + obj.lastval = obj.curval; + obj.changed = _changed_method; + obj.update = func(obj) + { + me.curval = me.property.getValue(); + if (me.curval != nil) + { + me.localType = me.property.getType(); + if (me.localType == "INT" or me.localType == "LONG" or me.localType == "FLOAT" or me.localType == "DOUBLE") + { + if(me.lastval == nil or math.abs(me.lastval - me.curval) >= me.delta) + { + me.lastval = me.curval; + me.changed(me.curval); + } + } + else if(me.lastval == nil or me.lastval != me.curval) + { + me.lastval = me.curval; + me.changed(me.curval); + } + } + }; + obj.update(obj); + return obj; + }, + + IsNumeric : func(hashkey) + { + me.localType = me.property[hashkey].getType(); + if (me.localType == "UNSPECIFIED") { + print("UpdateManager: warning ",hashkey," is ",ty, " excluding from update"); + me.property[hashkey] = nil; + } + if (me.localType == "INT" or me.localType == "LONG" or me.localType == "FLOAT" or me.localType == "DOUBLE") + return 1; + else + return 0; + }, + + FromPropertyHashList : func(_keylist, _delta, _changed_method) + { + var obj = {parents : [UpdateManager] }; + obj.hashkeylist = _keylist; + obj.delta = _delta; + obj.lastval = {}; + obj.hashkey = nil; + obj.changed = _changed_method; + obj.needs_update = 0; + obj.property = {}; + obj.is_numeric = {}; + foreach (hashkey; obj.hashkeylist) { + obj.property[hashkey] = props.globals.getNode(hashkey); + obj.lastval[hashkey] = nil; +# var ty = obj.property[hashkey].getType(); +# if (ty == "INT" or ty == "LONG" or ty == "FLOAT" or ty == "DOUBLE") { +# obj.is_numeric[hashkey] = 1; +# } else +# obj.is_numeric[hashkey] = 0; +#print("create: ", hashkey," ", ty, " isnum=",obj.is_numeric[hashkey]); +# if (ty == "UNSPECIFIED") +# print("UpdateManager: warning ",hashkey," is ",ty); + } + obj.update = func(obj) + { + if (me.lastval == nil) + me.needs_update = 1; + else { + me.needs_update = 0; + + foreach (hashkey; me.hashkeylist) { + if (me.property[hashkey] != nil) { + me.valIsNumeric = me.IsNumeric(hashkey); + + if (me.lastval[hashkey] == nil + or (me.valIsNumeric and (math.abs(me.lastval[hashkey] - me.property[hashkey].getValue()) >= me.delta)) + or (!me.valIsNumeric and (me.lastval[hashkey] != me.property[hashkey].getValue()))) { + me.needs_update = 1; + break; + } + } + } + } + if (me.needs_update) { + me.changed(me); + foreach (hashkey; me.hashkeylist) { + me.lastval[hashkey] = me.property[hashkey].getValue(); + } + } + } + ; + return obj; + }, + FromHashValue : func(_key, _delta, _changed_method) + { + var obj = {parents : [UpdateManager] }; + obj.hashkey = _key; + obj.delta = _delta; + obj.isnum = _delta != nil; + obj.curval = nil; + obj.lastval = nil; + obj.changed = _changed_method; + obj.update = func(obj) + { + me.curval = obj[me.hashkey]; + if (me.curval != nil) { + if (me.isnum) { + me.curval = num(me.curval); + if (me.lastval == nil or math.abs(me.lastval - me.curval) >= me.delta) { + me.lastval = me.curval; + me.changed(me.curval); + } + } else { + if (me.lastval == nil or me.lastval != me.curval) { + me.lastval = me.curval; + me.changed(me.curval); + } + } + } + } + ; + return obj; + }, + FromHashList : func(_keylist, _delta, _changed_method) + { + var obj = {parents : [UpdateManager] }; + obj.hashkeylist = _keylist; + obj.delta = _delta; + obj.lastval = {}; + obj.hashkey = nil; + obj.changed = _changed_method; + obj.needs_update = 0; + obj.update = func(obj) + { + if (me.lastval == nil) + me.needs_update = 1; + else + me.needs_update = 0; + + if (obj != nil or me.lastval == nil) { + foreach (hashkey; me.hashkeylist) { + if (me.lastval[hashkey] == nil or math.abs(me.lastval[hashkey] - obj[hashkey]) >= me.delta) { + me.needs_update = 1; + break; + } + } + } + if (me.needs_update) { + me.changed(obj); + foreach (hashkey; me.hashkeylist) { + me.lastval[hashkey] = obj[hashkey]; + } + } + }; + return obj; + }, +};