mfd.nas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. # ==============================================================================
  2. # Original Boeing 747-400 pfd by Gijs de Rooy
  3. # Modified for 737-800 by Michael Soitanen
  4. # Modified for EC145 by litzi
  5. # ==============================================================================
  6. # This is a generic approach to canvas MFD's
  7. # the property rule counter must be global
  8. var proprulecounter = 0;
  9. var Canvas_mfd = {
  10. new: func(id, sizepx, viewpx)
  11. {
  12. var m = { parents: [Canvas_mfd] };
  13. m.display = canvas.new({
  14. "name": "MFD"~id,
  15. "size": sizepx,
  16. "view": viewpx,
  17. "mipmapping": 1
  18. });
  19. m.id = id;
  20. return m;
  21. },
  22. add_page: func(_id, svg_filename) {
  23. me[_id] = Page.new(me.display, svg_filename);
  24. me[_id].pagename = _id;
  25. me[_id].mfd_id = me.id;
  26. me[_id].objectcontext = "helionix.mfd[" ~ me.id ~ "][\"" ~_id~ "\"]";
  27. me[_id].ifactive = "if (" ~ me[_id].objectcontext ~ ".active) ";
  28. # return a reference to the new page object
  29. return me[_id];
  30. }
  31. };
  32. var Page = {
  33. new: func(display, svg_filename)
  34. {
  35. var m = { parents: [Page] };
  36. # keep a reference to main parent mfd object
  37. m.mfd = display;
  38. # create the new page in canvas as a new group
  39. m.page = display.createGroup();
  40. canvas.parsesvg(m.page, svg_filename, {'font-mapper': font_mapper});
  41. m.ncopy = 0;
  42. m.active = 1;
  43. m.mystack = [];
  44. return m;
  45. },
  46. hide: func()
  47. {
  48. if (me.active) {
  49. me.page.set("visible", 0);
  50. me.active = 0;
  51. }
  52. },
  53. show: func()
  54. {
  55. if (!me.active) {
  56. if (DEBUG) print("page ... refresh");
  57. me.page.set("visible", 1);
  58. me.active = 1;
  59. # force refresh for all sensors
  60. adc._refresh_();
  61. }
  62. },
  63. add_by_id: func(id) {
  64. if (!contains(me, id)) {
  65. me[id] = me.page.getElementById(id);
  66. if (me[id] == nil) {
  67. print("WARNING: Non existing SVG ID ",id," !");
  68. return nil;
  69. }
  70. # create a new unity tranform to use it for triggering the prop rule canvas update
  71. me[id].nodepath = me[id]._node.getPath() ~ "/tf[1]/m";
  72. me[id].nodepathval = getprop(me[id].nodepath);
  73. me[id].objectcontext = me.objectcontext ~ "[\"" ~ id ~ "\"]";
  74. if (DEBUG) print("add: ",id, " -> ", me[id]._node.getPath());
  75. # search for a clipping element for this id (taken from F16 HUD code)
  76. var theclip = me.page.getElementById(id~"_clip");
  77. if (theclip != nil) {
  78. me.setClipByElement(id, theclip);
  79. theclip.setVisible(0); #hide the clipping mask element
  80. }
  81. return 1;
  82. }
  83. return 1;
  84. },
  85. setClipByElement: func (id, cl) {
  86. cl.update();
  87. var bb = cl.getTightBoundingBox();
  88. var clstr = sprintf("rect(%d,%d,%d,%d)", bb[1], bb[2], bb[3], bb[0]);
  89. me[id].set("clip", clstr);
  90. #setting clip-frame results in strange offset of clipped region!
  91. #me[id].set("clip-frame", canvas.Element.PARENT);
  92. if (DEBUG) print("clipping: ",id," -> ", clstr);
  93. },
  94. add_direct: func(id, s, fu) {
  95. # for tranforms translation, rotation
  96. # svg element id is bound to sensor s.
  97. # fu is the fuction that implements the
  98. # actual transformation. the function must
  99. # accept arguments f(o,c) with o the object
  100. # and c the center of rotation (a two element vector)
  101. me.add_by_id(id);
  102. var c1 = me[id].getCenter();
  103. var thisobj = me[id].createTransform();
  104. s._register_( {o: thisobj, c: c1, f: fu}, me );
  105. },
  106. add_directS: func(id, s, fu) {
  107. # for tranforms scale
  108. # svg element id is bound to sensor s.
  109. # fu is the fuction that implements the
  110. # actual transformation. the function must
  111. # accept arguments f(o,c) with o the object
  112. # and c the center (a two element vector)
  113. me.add_by_id(id);
  114. var c1 = me[id].getCenter();
  115. me[id].createTransform().setTranslation(-c1[0],-c1[1]);
  116. var thisobj = me[id].createTransform();
  117. me[id].createTransform().setTranslation(c1[0],c1[1]);
  118. s._register_( {o: thisobj, c: c1, f: fu}, me );
  119. },
  120. add_directTV: func(id, s, fu, v=nil) {
  121. # for setText and setVisible
  122. # svg element id is bound to sensor s.
  123. # fu is the fuction that implements the
  124. # actual method for the object. the function must
  125. # accept arguments f(o,c) with o the object
  126. # and a second argument)
  127. me.add_by_id(id);
  128. s._register_( {o: me[id], c: v, f: fu}, me );
  129. },
  130. add_trans: func(id, type, params, disable_rules=0) {
  131. me.add_trans_grp([id,], type, params, disable_rules=0);
  132. },
  133. add_trans_grp: func(ids, type, params, disable_rules=0) {
  134. var elements = [];
  135. var p = {};
  136. me.init_params(p, params);
  137. var is_const = (p.sensor==nil);
  138. if (is_const) {
  139. foreach (var e; ids) {
  140. me.add_by_id(e);
  141. var c = me[e].getCenter();
  142. var x = me[e].createTransform();
  143. # set the contant transformations for the elements
  144. if (DEBUG)
  145. print(e, " transf. is a constant");
  146. if (type == "x-shift")
  147. me[e].createTransform().setTranslation(p.offset, 0);
  148. elsif (type == "y-shift")
  149. me[e].createTransform().setTranslation(0, p.offset);
  150. elsif (type == "rotation")
  151. me[e].setRotation(p.offset*D2R,c[0],c[1]);
  152. elsif (type == "x-scale") {
  153. me[e].setTranslation(-c[0],-c[1]);
  154. me[e].createTransform().setScale([p.offset, 1]);
  155. me[e].createTransform().setTranslation(c[0],c[1]);
  156. } elsif (type == "y-scale") {
  157. me[e].setTranslation(-c[0],-c[1]);
  158. me[e].createTransform().setScale([1, p.offset]);
  159. me[e].createTransform().setTranslation(c[0],c[1]);
  160. }
  161. }
  162. return;
  163. } elsif (p.sensor == nil) {
  164. print("canvas mfd: use of function DEPRECATED use a sensor instead. ");
  165. debug.dump(ids);
  166. return;
  167. } else {
  168. # if a sensor is already referenced, use this
  169. s = p.sensor;
  170. }
  171. foreach (var e; ids) {
  172. # avoid stack overflows, del self reference to sensor
  173. p.sensor = nil;
  174. me.add_by_id(e);
  175. var myfilter = compile_filter_function(p);
  176. var c = me[e].getCenter();
  177. # compose of callback for sensor to the transform element
  178. if (type == "x-shift") {
  179. s._register_code( me.ifactive, myfilter ~ me.newstack(e) ~ ".setTranslation(x, 0);" );
  180. } elsif (type == "y-shift") {
  181. s._register_code( me.ifactive, myfilter ~ me.newstack(e) ~ ".setTranslation(0, x);" );
  182. } elsif (type == "rotation") {
  183. s._register_code( me.ifactive, myfilter ~ me.newstack(e) ~".setRotation(x*D2R," ~c[0]~ "," ~c[1]~ ");" );
  184. } elsif (type == "x-scale") {
  185. me[e].createTransform().setTranslation(-c[0],-c[1]);
  186. s._register_code( me.ifactive, myfilter ~ me.newstack(e) ~ ".setScale( [x,1] );" );
  187. me[e].createTransform().setTranslation(c[0],c[1]);
  188. } elsif (type == "y-scale") {
  189. me[e].createTransform().setTranslation(-c[0],-c[1]);
  190. s._register_code( me.ifactive, myfilter ~ me.newstack(e) ~ ".setScale( [1,x] );" );
  191. me[e].createTransform().setTranslation(c[0],c[1]);
  192. }
  193. }
  194. },
  195. newstack: func(e) {
  196. # make new transform on stack and return reference
  197. append(me.mystack, me[e].createTransform());
  198. return me.objectcontext ~ ".mystack[" ~ (size(me.mystack)-1) ~ "]"
  199. },
  200. add_text: func(id, params) {
  201. me.add_text_grp([id,],params);
  202. },
  203. add_text_grp: func(ids, params) {
  204. var elements = [];
  205. var p = {};
  206. me.init_params(p, params);
  207. var is_const = (p.sensor==nil);
  208. if (is_const) {
  209. foreach (var i; ids) {
  210. me.add_by_id(i);
  211. me[i].setText(""~p.offset);
  212. }
  213. return;
  214. }
  215. if (p.sensor == nil) {
  216. print("canvas mfd: use of function DEPRECATED use a sensor instead.");
  217. debug.dump(ids);
  218. return;
  219. } else {
  220. # if a sensor is already referenced, use this
  221. var s = p.sensor;
  222. }
  223. foreach (var i; ids) {
  224. me.add_by_id(i);
  225. me[i].enableFast();
  226. # register callback method for text update to the sensor s
  227. #if (p.format == nil) {
  228. # non-numeric value
  229. #s._register_code( me.ifactive, me[i].objectcontext ~ ".setTextFast(arg[0]);" );
  230. #} else {
  231. var myfilter = compile_filter_function(p);
  232. s._register_code( me.ifactive, myfilter ~ me[i].objectcontext ~ ".setTextFast(x);" );
  233. #}
  234. }
  235. },
  236. add_color_range: func(i, s, params, fill=0) {
  237. me.add_by_id(i);
  238. # color range is only possible via fixed slow timer
  239. # to avoid performace issues from callbacls
  240. if (!contains(me,"color_timer_data"))
  241. me.color_timer_data = {};
  242. me.color_timer_data[i] = [s, params, fill];
  243. if (!contains(me,"color_timer")) {
  244. me.colortimer = maketimer(1/2, func {
  245. foreach(var p; keys(me.color_timer_data) ) {
  246. var sens = me.color_timer_data[p][0];
  247. var c = me.color_timer_data[p][1];
  248. var f = me.color_timer_data[p][2];
  249. var v = sens.val or 0;
  250. if (v <= c[0][0])
  251. if (f) me[p].setColorFill(c[0][1]); else me[p].setColor(c[0][1]);
  252. elsif (v >= c[2][0])
  253. if (f) me[p].setColorFill(c[2][1]); else me[p].setColor(c[2][1]);
  254. else
  255. if (f) me[p].setColorFill(c[1][1]); else me[p].setColor(c[1][1]);
  256. }
  257. });
  258. me.colortimer.start();
  259. }
  260. },
  261. add_cond: func(id, params) {
  262. me.add_cond_grp([id,], params);
  263. },
  264. add_cond_grp: func(ids, params) {
  265. var elements = [];
  266. var p = {};
  267. me.init_params(p, params);
  268. var is_const = (p.sensor==nil);
  269. if (is_const) {
  270. foreach (var i; ids) {
  271. me.add_by_id(i);
  272. me[i].setVisible(p.offset);
  273. }
  274. return;
  275. }
  276. if (p.sensor == nil) {
  277. print("canvas mfd: use of function DEPRECATED use a sensor instead.");
  278. debug.dump(ids);
  279. return;
  280. } else {
  281. # if a sensor is already referenced, use this
  282. #p["type"] = "BOOL";
  283. var s=p.sensor;
  284. }
  285. foreach (var i ; ids) {
  286. me.add_by_id(i);
  287. # register callback code to the element at the sensor s
  288. var myfilter = compile_filter_function(p);
  289. if (p.notequal != nil) {
  290. #for string use no filter
  291. #if (num(p.notequal) == nil)
  292. #s._register_code( me.ifactive, me[i].objectcontext ~ ".setVisible(arg[0] !=\"" ~ p.notequal ~ "\");" );
  293. #else
  294. s._register_code( me.ifactive, myfilter ~ me[i].objectcontext ~ ".setVisible(x !=\"" ~ p.notequal ~ "\");" );
  295. } elsif (p.lessthan != nil) {
  296. s._register_code( me.ifactive, myfilter ~ me[i].objectcontext ~ ".setVisible(x <" ~ p.lessthan ~ ");" );
  297. } elsif (p.greaterthan != nil) {
  298. s._register_code( me.ifactive, myfilter ~ me[i].objectcontext ~ ".setVisible(x >" ~ p.greaterthan ~ ");" );
  299. } elsif (p.between != nil) {
  300. s._register_code( me.ifactive, myfilter ~ me[i].objectcontext ~ ".setVisible(x >" ~ p.between[0] ~ " and x<" ~ p.between[1] ~");" );
  301. } elsif (p.notin != nil) {
  302. s._register_code( me.ifactive, myfilter ~ me[i].objectcontext ~ ".setVisible(x <" ~ p.notin[0] ~ " or x>" ~ p.notin[1] ~");" );
  303. } else {
  304. #for string use no filter
  305. #if (num(p.equals) == nil)
  306. #s._register_code( me.ifactive, me[i].objectcontext ~ ".setVisible(x ==\"" ~ p.equals ~ "\");" );
  307. #else
  308. s._register_code( me.ifactive, myfilter ~ me[i].objectcontext ~ ".setVisible(x ==\"" ~ p.equals ~ "\");" );
  309. }
  310. }
  311. },
  312. init_params: func(x, params) {
  313. # note: optional named arguments did not work, therefore a hash is used
  314. # init some transformation default parameters
  315. foreach (var a; keys(params)) {
  316. if (-1 == isin(a, ["sensor", "equals", "lessthan", "greaterthan", "notequal", "between", "name", "scale",
  317. "offset", "mod", "trunc", "max", "min", "format", "trunc", "abs", "notin", "funcof"])) {
  318. #debug.dump(caller(0));
  319. die("unknown parameter '"~a~"' in "~caller(0)[2]);
  320. }
  321. }
  322. # for the sensor:
  323. #x["function"] = contains(params, "function") ? params.function : nil;
  324. #x["prop"] = contains(params, "prop") ? params.prop : nil;
  325. x["sensor"] = contains(params, "sensor") ? params.sensor: nil;
  326. x["equals"] = contains(params, "equals") ? params.equals: 1;
  327. x["lessthan"] = contains(params, "lessthan") ? params.lessthan: nil;
  328. x["greaterthan"] = contains(params, "greaterthan") ? params.greaterthan: nil;
  329. x["notequal"] = contains(params, "notequal") ? params.notequal: nil;
  330. x["between"] = contains(params, "between") ? params.between: nil;
  331. x["notin"] = contains(params, "notin") ? params.notin: nil;
  332. x["name"] = contains(params, "name") ? params.name: nil;
  333. x["timer"] = contains(params, "timer") ? params.timer: 0;
  334. # for the filter:
  335. x["scale"] = contains(params, "scale") ? params.scale : 1.0;
  336. x["offset"] = contains(params, "offset") ? params.offset : 0.0;
  337. x["mod"] = contains(params, "mod") ? params.mod : nil;
  338. x["max"] = contains(params, "max") ? params.max : 1e20;
  339. x["min"] = contains(params, "min") ? params.min : -1e20;
  340. x["format"] = contains(params, "format") ? params.format: nil;
  341. x["trunc"] = contains(params, "trunc") ? ((params.trunc == "int") ? 1 : 0) : 0;
  342. x["abs"] = contains(params, "trunc") ? ((params.trunc == "abs") ? 1 : 0) : 0;
  343. x["funcof"] = contains(params, "funcof") ? params.funcof : nil;
  344. },
  345. copy_element: func(svgid, post, dx=nil) {
  346. var newid = svgid~post;
  347. if (contains(me, newid)) {
  348. die("canvas_MFD.copy_element: newid already exists");
  349. }
  350. dx = (dx==nil) ? [0,0] : dx;
  351. if (!contains(me, svgid)) {
  352. me.add_by_id(svgid);
  353. }
  354. var basenode = me[svgid]._node.getPath();
  355. var srcnode = props.globals.getNode(basenode);
  356. var xnode = substr(basenode, 0, find("group", basenode));
  357. var dest = splitpath(basenode,0,-2) ~ "group[99]/group["~ me.ncopy ~"]";
  358. var destnode = props.globals.initNode(dest);
  359. # copy the element
  360. props.copy(srcnode, destnode, 1);
  361. # set the new ids
  362. change_all_ids(destnode, post);
  363. #setprop(dest~"/id", newid);
  364. if (!contains(me, newid)) {
  365. me.add_by_id(newid);
  366. }
  367. me[newid].setTranslation(dx);
  368. me.ncopy += 1;
  369. },
  370. link2rule: func(svgid, propIn, x) {
  371. # === EXPERIMENTAL ! ===
  372. # make calculation of transform in a property rule
  373. # to reduce Nasal overhead
  374. # the output is aliased to the correct tranformation
  375. # matrix.
  376. #
  377. # however, the aliased property does not trigger an automatic
  378. # update of the canvas. Therefore an update by a timer is needed
  379. # which makes the pos. effect on performance relatively minor
  380. if (proprulecounter > 199)
  381. die("canvas mfd: max. number of property rules (200) exceeded");
  382. if (!contains(me, svgid))
  383. me.add_by_id(svgid);
  384. if (!contains(me[svgid],"xmfdtimer")) {
  385. # add a timer to periodically trigger the transform. only ONE timer per svg element is needed
  386. me[svgid].xmfdtimer = maketimer(1/30, func setprop(me[svgid].nodepath, me[svgid].nodepathval));
  387. me[svgid].xmfdtimer.start();
  388. }
  389. var proproot = "canvas/rules/rule[" ~ proprulecounter ~ "]/";
  390. #print (svgid," ", me[trans.id].a.getPath());
  391. props.globals.getNode(proproot~"active", 1).setBoolValue(1);
  392. props.globals.getNode(proproot~"r-active", 1).setBoolValue(0);
  393. setprop(proproot~"name", svgid);
  394. setprop(proproot~"offset", (x.scale==0) ? -x.offset : -x.offset/x.scale);
  395. setprop(proproot~"scale", x.scale);
  396. setprop(proproot~"min", x.min);
  397. setprop(proproot~"max", x.max);
  398. setprop(proproot~"periodmin", (x.mod==nil) ? -10000000000000000000 : 0);
  399. setprop(proproot~"periodmax", (x.mod==nil) ? 10000000000000000000 : x.mod);
  400. setprop(proproot~"abs", x.abs);
  401. var nodeIn = nil;
  402. var propOut = proproot~"out";
  403. # alias the input node:
  404. nodeIn = props.globals.getNode(proproot~"in");
  405. if (nodeIn==nil)
  406. die("canvas mfd: Cannot alias transform to property rule. Is the property rule file loaded?")
  407. else
  408. nodeIn.alias(propIn);
  409. # alias the correct output node/transform matrix element:
  410. #tfm : {x-scale:"a", y-scale:"d", x-shift:"e",y-shift:"f"},
  411. var T = me[svgid].createTransform();
  412. if (x.type == "x-shift")
  413. #x-shift
  414. T.e.alias(propOut);
  415. elsif (x.type == "y-shift")
  416. #y-shift
  417. T.f.alias(propOut);
  418. elsif (x.type == "x-scale")
  419. #x-scale
  420. T.a.alias(propOut);
  421. elsif (x.type == "y-scale")
  422. #y-scale
  423. T.d.alias(propOut);
  424. elsif (x.type == "rotation") {
  425. # setup the rotation matrix
  426. T.a.alias(proproot~"cos");
  427. T.b.alias(proproot~"sin");
  428. T.c.alias(proproot~"m-sin");
  429. T.d.alias(proproot~"cos");
  430. props.globals.getNode(proproot~"r-active", 1).setBoolValue(1);
  431. }
  432. proprulecounter += 1;
  433. return 1;
  434. },
  435. del: func () {
  436. foreach(var x; me.sensors)
  437. x._del_();
  438. print("mfd ... del");
  439. }
  440. };