Board index FlightGear Development Canvas

Canvas animations

Canvas is FlightGear's new fully scriptable 2D drawing system that will allow you to easily create new instruments, HUDs and even GUI dialogs and custom GUI widgets, without having to write C++ code and without having to rebuild FlightGear.

Canvas animations

Postby Philosopher » Sat Aug 16, 2014 3:01 am

I'm accepting input on a Canvas animation system :) (you know who you are)

My thoughts were that it should basically resemble the C++ 3D animation system and be invisible (enough) that it could easily be replaced with more C++ should we need more performance (or just 'cause). Exposing SGExpression to Nasal would be helpful soon but not necessary yet.

This is my current prototype – I'm missing an expr() function but it should otherwise work.
Code: Select all
var _get_xml = func {
   var repl = 0;
   if (!size(arg)) {
      if (!contains(caller(0)[0], "xml"))
      { var xml = caller(1)[0]["xml"]; var repl=1 }
   } else var xml = arg[0];
   if (typeof(xml) == 'scalar') {
      var path = resolvepath(xml);
      if (!path) die("could not reopve path: '"~xml~"'");
      xml = io.read_properties(path);
   }
   if (!isa(xml, props.Node))
      die("bad xml arg");
   if (repl) caller(1)[0].xml = xml;
   return xml;
}

var AnimWrapElement = {
   new: func(name, group, interval, on_update, on_create, on_destroy) {
      var me = { parents:[AnimWrapElement, canvas],
                 name:name, interval:interval,
                 on_update:on_update, gr:group,
                 on_create:on_create,
                 on_destroy:on_destroy };
      if (isa(group, canvas.Canvas))
         me.canvas = group;
      else me.canvas = group.getCanvas();
      me.on_create(me);
      me.loop = maketimer(interval, me, me.update);
      return me;
   },
   start: func() {
      me.loop.start();
      return me;
   },
   update: func() {
      var ou = me.on_update;
      if (typeof(ou) == 'hash')
         ou = values(ou);
      foreach (var f; ou) {
         if (typeof(f) == 'func')
            f(me);
         else {
            if (typeof(f) == 'vector')
               ou ~= f;
            elsif (typeof(f)== 'hash')
               ou ~= values(f);
            if (size(ou) > 30)
               die("max size of callbacks exceeded");
         }
      }
      return me;
   },
   stop: func() {
      me.loop.stop();
      return me;
   },
   del: func() {
      me.stop();
      me.on_destroy();
      me.gr.del():
      return nil;
   },
};
var create_elements = func(xml, group=nil, interval=0, options=nil) {
   _get_xml();
   var res = [];
   foreach (var n; xml.getChildren("element"))
      append(res, create_element(n, group, interval, options));
   return res;
}
var create_element = func(xml, group=nil, interval=0, options=nil) {
   _get_xml();
   if (group == nil) group = canvas.new();
   var svg = canvas.parse_svg(group, xml.getValue("path"), options);
   var animations = create_animations(xml, group);
   var ou = { "animations": animations, "": [] };
   var cr = var de = func{};
   if ((var sc=xml.getValue("nasal/load")) != nil) {
      var code = compile(sc, "<canvas animation load>");
      if (typeof(code) == 'func')
         cr = code;
   }
   if ((var sc=xml.getValue("nasal/unload")) != nil) {
      var code = compile(sc, "<canvas animation unload>");
      if (typeof(code) == 'func')
         de = code;
   }
   var instr = AnimWrapElement.new(xml.getValue("name"),
                                   group, interval, ou, cr, de);
   return instr;
}
var create_animations = func(xml, group) {
   _get_xml();
   var res = [];
   foreach (var n; xml.getChildren("animation"))
      append(res, create_animation(n, group));
   return res;
}
var create_animation = func(xml, group) {
   _get_xml();
   var matrix = func matrix = group.createTransform();
   foreach (var m; keys(xml))
      if (substr(m,0,3)=='get' and typeof(xml[m])=='func')
         caller(0)[0][m] =
           (func{ func call(xml[m], arg, xml) })();
   var getVal = getValue;
   var getN = getNode;
   var type = getVal("type");
   if ((var e_id=getVal("id")) != nil)
      group = group.findElementById(e_id);
   if (type == "translate") {
      matrix();
      var x = expr(getN("x"));
      var y = expr(getN("y"));
      return func(instr) {
         matrix.setTranslation(x(),y());
      };
   } elsif (type == "color") {
      var r = expr(getN("r"));
      var g = expr(getN("g"));
      var b = expr(getN("b"));
      return func(instr) {
         group.setColor(r(),g(),b());
      };
   } elsif (type == "rotate") {
      matrix();
      var rot = expr(getN("rotation"));
      return func(instr) {
         matrix.setRotation(rot());
      };
   } elsif (type == "scale") {
      matrix();
      var scale = expr(getN("scale"));
      return func(instr) {
         matrix.setScale(scale());
      };
   } else die("unknown animation type: '"~type~"'");
}

Very small sample XML:
Code: Select all
<PropertyList>
<element>
   <name>747-PFD-pilot</name>
   <svg-path>Aircaft/747-400/Instruments/PFD.svg</svg-path>
   <animation>
      <id>artificial-horizon</id>
      <type>translate</type>
      <y>
         <mul>
            <property>/orientation/pitch-deg</property>
            <value>6.666666666</value>
         </mul>
      </y>
   </animation>
</element>
</PropertyList>


We could easily extend this to add a <map> element with <layer>s (aka MapStructure) or even more PFD ideas, but this is just a way to easily animate an SVG.

Obviously there's a lot of work to be done here but my question for you is: is this the general way we want to do it?
Thanks,
Philosopher
(inactive but lurking occasionally...)
Philosopher
 
Posts: 1590
Joined: Sun Aug 12, 2012 6:29 pm
Location: Stuck in my head...
Callsign: AFTI
Version: Git
OS: Mac OS X 10.7.5

Re: Canvas animations

Postby Hooray » Sat Aug 16, 2014 9:22 am

right, I think the main requirement is, like you say, that things should be sufficiently encapsulated, so that we can protoype a simple animation system in scripting space, but still easily agument/re-implement it partially via C++ additions once the need arises (i.e. due to better performance): http://wiki.flightgear.org/Canvas_Animation_Framework

Given that a very simple animation system would mainly need to deal with "events/triggers" (i.e. timers & listeners) and "actions", we might even be able to reuse some of galvedro's sophisticated failure management modules, because those already deal with both concepts via the property tree (sent a heads-up to him for some feedback).

From a use-case/features standpoint, I would also suggest to closely model this after existing code, i.e. the PFD/ND/EICAS or HUD code.
And while a few lower-level functions would be useful obviously, I would be inclined to directly introduce a SVG-focused layers that deals with SVG images and individual elements (groups).
I guess Tom would have some feedback, too - because the GUI might also benefit from a very simple animation framework that can be easily augmented if necessary.

Supporting SGCondition/SGExpression or possibly the state machine stuff in SimGear would be cool - even though I am not sure if the latter has already been exposed to Nasal - but this would definitely be a good idea, as Nasal scripts would really just be the "glue" code to tie properties and events/signal together to animate a certain canvas/group.

While I haven't yet run your code, it's looking pretty good actually - even though I would hope that we can support versioning at the XML level, which is one of the main issues in all our XML files, i.e. we cannot easily introduce new/breaking changes, because we never establish 1) the type of PropertyList XML file (e.g. canvas-animation) and 2) the version of the "DTD/schema" used.

From a functionality standpoint, I would hope to support recursion, so that things can be nested, and maybe even new animation types registered by chaining together custom XML blocks - I once prototyped something like this for the missions/tutorial system, so that the system could be largely configured/customized and maintained by using primarily XML and custom building blocks: http://wiki.flightgear.org/FlightGear_M ... _Framework

The main idea here being that aircraft developers/fgdata contributors could help develop/maintain all required building blocks, without necessarily having to be intimately familiar with Nasal/Canvas coding - but if that is something we want to pursue here, this would involve a fair bit of "meta-ness". However, supporting such functionality, would also mean that people could easily grow a library of animations for all kinds of purposes, including even custom Canvas splash screens.
And personally, I'd kinda prefer having a generic framework that can be used for all main use-cases (instruments, HUD, GUI etc).

Required features should probably evolve automatically over time, especially once we start looking at the existing PFD/ND and MapStructure code, i.e. to determine what could greatly simplify the existing code - "navdisplay.styles" is 100% declarative so all those hashes could be easily turned into XML using ~30 lines of Nasal code eventually.

Otherwise, inter-operability between existing Nasal code and a "new" XML-based animation system would need some kind of "bridge" - in the case of the XML-based extension framework I played with a while ago, I extended the concept to allow Nasal to actually run "xml-call" function invocation: http://wiki.flightgear.org/Canvas_Glass_Cockpit_Efforts

I am not saying that any of these ideas should necessarily be supported - but maybe this helps us to come up with a flexible and sufficiently extensible framework.

We could easily extend this to add a <map> element with <layer>s (aka MapStructure) or even more PFD ideas, but this is just a way to easily animate an SVG.

right, that's touching the whole "de-skilling" debate we've been seeing with regard to our ND/PFD and MapStructure code - it would definitely be a good idea to keep such feedback in mind. But for starters, even just having some kind of framework to grow a library of common animations and actually adopt/enforce them, would be a good step. And we should also look at external/standalone efforts like the Avidyne Entegra R9. Identifying required building blocks and actually patching existing code, would also help us optimize performance over time, i.e. by adding native hooks at some point.

And from a prototyping standpoint, it would make sense to fully support reset/re-init, so that animations can be reloaded from disk without requiring a FlightGear restart, i.e. setting up listeners to clean up resources (timers/listeners) and re-initializing the system.

Functionality-wise, I really like the idea to closely model this after existing FG animation systems - and looking at README.xmlpanel or README.hud should be a good idea, because it would help us re-implement 2D panels/HUDs sooner or later, which is another thing that core developers are hoping to be able to do in order to unify the 2D rendering back-end, last I checked, there wasn't much missing to make this happen:
http://wiki.flightgear.org/Howto:Parsin ... the_Canvas
Image
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 10751
Joined: Tue Mar 25, 2008 8:40 am

Re: Canvas animations

Postby Hooray » Sun Aug 17, 2014 2:44 pm

It would also make sense to look at Torsten's recent commits/work - he's started adopting Canvas and is developing a SVG-based EFIS.

Structurally, all his code is much cleaner and much more generic than our original PFD/ND related code, but it's all written from scratch - and he's also using a few standard coding patterns - I find the code to be pretty readable and accessible: https://gitorious.org/fg/fgdata/source/ ... S/EFIS.nas
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 10751
Joined: Tue Mar 25, 2008 8:40 am

Re: Canvas animations

Postby Hooray » Wed Aug 20, 2014 7:42 pm

:lol:

https://gitorious.org/fg/fgdata/commit/ ... bae652690a
Torsten wrote: invent some kind of animation system for svg - working trajectory marker (aka FPV) on the PFD - make the screen emissive - use common input properties for screens - start with pfd config screen - extract screen-logic into separate files as plugins - some more live for the HSI and PFD


all this is kinda funny, because I sent a PM to him suggesting exactly that, but he still hasn't read it ... even though this is clearly overlapping with a lot of stuff now that he seems entirely unaware of :?
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 10751
Joined: Tue Mar 25, 2008 8:40 am


Return to Canvas

Who is online

Users browsing this forum: No registered users and 0 guests