Canvas animation framework

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.

Note: While this article is based on considerable community feedback, there's nobody working on this currently.
So if you'd like to help in one way or another, please get in touch or just help improve the article in the meantime!
Useful Skills:
Property Tree, Nasal scripting, Canvas, CanvasMFD, 777, 787


Mentors: Hooray (get in touch to learn more)
It's possible that this article hasn't been updated in a while, so to catch up with the latest developments, you are advised not to start working on anything directly related to this without first coordinating your ideas with fellow FlightGear contributors using the FlightGear developers mailing list or the FlightGear forums.

Canvas Animation Framework
Started in 08/2014 (experiments)
Description Animation Framework for Canvas
Maintainer(s) Hooray, Philosopher [1]
Status discussion & prototyping (feedback & help appreciated!)

Note  Whenever possible, please refrain from modeling complex systems, like an FDM, autopilot or Route Manager with Nasal. This is primarily to help reduce Nasal overhead (especially GC overhead). It will also help to unify duplicated code. The FlightGear/SimGear code base already contains fairly generic and efficient systems and helpers, implemented in C++, that merely need to be better generalized and exposed to Nasal so that they can be used elsewhere. For example, this would enable Scripted AI Objects to use full FDM implementations and/or built-in route manager systems.

Technically, this is also the correct approach, as it allows us to easily reuse existing code that is known to be stable and working correctly, .

For details on exposing these C++ systems to Nasal, please refer to Nasal/CppBind. If in doubt, please get in touch via the mailing list or the forum first.


Cquote1.png as I said, my interest is to get some working - if someone provides a 'system' I'll use it but I'm not interested in writing it myself :D
— zakalawe (Sat Jan 18). Re: SVG editing skills needed.
(powered by Instant-Cquotes)

mapping vs. SVG animation

It would be really nice to have two sides to navdisplay.mfd: a map (your/my area) and svg ui (Gijs' and aircraft devs' responsibility) 0.o.

am all for supporting the two use-cases you mentioned - then again, the old code is fragile enough, so I wouldn't add to it - the main thing the old code was all about was establishing the MVC separation so that draw routines can be reused - and that's basically done, so no need to keep the old cruft around ...
Regarding the navdisplay stuff itself - well, it's pretty much the original code still, i.e. the design is only just evolving - algorithmically, it's still not very optimized - MapStructure only helps with mapping aspects obviously.
I was going to suggest the "SVG/element animation" separation too - but I think Gijs etc already need to handle quite a bit of re-thinking, because things are no longer as straightforward. In the long run, it will probably happen over time - especially once other things are generalized, especially for generic PFD/EICAS/CDU/EFB functionality - these would all benefit from a corresponding "symbol animation" abstraction eventually like TheTom mentioned on the forum: PFD: A common framework in the background
Honestly, it would have been great to have such a framework to implement MapStructure on top of it - but obviously, it's a chicken/egg issue :-)
Currently, the main danger here is that people are going to start using copy & paste again to adopt things like Gijs' PFD, like mentioned by Zakalawe: if someone provides a 'system' I'll use it but I'm not interested in writing it myself
So the real question is if people can be convinced to team up and come up with a generic and reusable design, or follow the original PFD/ND approach instead - interestingly, most of TheTom's work in this area (MCDU/EICAS) seems pretty reusable, also outside aircraft, i.e. usable via the GUI.

After having looked at navdisplay.mfd, I think we only need to separate a few things using io.include() to make this happen automatically over time - all the 747/Boeing-specific stuff would be moved into "config" hashes and factored out of navdisplay.mfd - into something like "boeing747.nd" (navdisplay.styles for now) that would leave us with very little code remaining in navdisplay - basically a single class and a few methods with foreach loops (well, once update() has been cleaned up at least). 30}% completed

So we would end up with a layered design like this:

  • boeing.nd (config hashes via io.include)
  • navdisplay (down-stripped code, aircraft-agnostic)
  • MapStructure (needs no changes)
  • Symbol/Animation Layer (shared with PFD, ND, CDU, EFB, would be developed/grown over time), some animations currently implemented manually in the PFD/ND code:
    • rotate (heading,bearing, azimuth)
    • scale
    • colorize
    • update text labels (font size, font type, font color)
  • canvas APIs (as is)
  • canvas subsystem (as is)
  • OSG / Shiva (as is)

Canvas doesn't currently have any native support for animations, which is why animations tend to be implemented using Nasal-space workarounds, such as timers and listeners that trigger an update, resulting for example in a change in size, color, style (font) etc or by hiding/showing certain groups, or changing a raster image.

This works well enough, so there's little need for any dedicated animation support currently - however, it makes sense to encapsulate all required functionality, so that we end up with a single framework that handles all animation needs on top of the Nasal main Canvas APIs.

The main point being that we don't want people to come up with their own animation workarounds, but instead use a centrally-maintained "animation library" for the most common types of animations (rotation, scaling, colors etc). In turn, this could be very generic and lightweight, flexible and compact, but also heavily optimized using caching and awareness of styling etc.

In the long term, this would also help ensure that we can easily upgrade/optimize animations by editing/maintaining a single place/file, rather than possibly dozens of files all over the place in the base package (and quite possibly 3rd party hangars) - for example, once certain animations are better supported by native Canvas primitives, we would only need to update the animation framework itself, instead of all *.nas/*.xml files using a certain type of animaation in $FG_ROOT

Most of these animations actually exist already in various places, so primarily, these need to be unified, generalized and moved to the Canvas namespace - ideally, using helper classes. This should help reduce the amount of specialized, but conceptually idential, code in other places, including other frameworks like NavDisplay or MapStructure that would preferably be adapted to make use of such an animation framework.

Once of the things we've been discussing here is to encapsulate animations also to help localize all animation-specific use cases of Nasal timers/listeners - due to Nasal GC issues, people generally don't want to add even more Nasal code to the main loop - having possibly hundreds of Nasal-animated elements in the GUI, cockpit instruments, HUDs etc may obviously become a potential issue at some point.

However, as long as all Canvas animations are handled by a single back-end, we can trivially benchmark/profile and optimize this, if necessary also re-implement it using Canvas extensions (native C++ code), or possibly by introducing support for SGExpression/SGCondition/SGStateMachine or Property Rules, which would mean that animations would be handled by C++ code at run-time, but typically set up and configured via Nasal-space data structures.

Most existing examples can be found in Gijs' PFD, ND and D-LEON's Avidyne (extra 500), and the upcoming Garmin GPSMap 196. The NavDisplay and MapStructure frameworks should eventually also manage all animations through such a common back-end.

Cquote1.png 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.

— Philosopher (Sat Aug 16). Canvas animations.
(powered by Instant-Cquotes)
Cquote1.png I'd prefer people to collaborate in order to contribute to a single unified framework, instead of us always having to give out the same optimization hints that we're already using in other places - we're using such constructs for a reason, and it doesn't help if people always need to re-discover certain findings, instead of applying what we told them
Cquote1.png The real issue remains however - as can be seen here (and in other threads), people generally copy & adapt existing code without understanding its performance implications, which is exactly why I'd prefer to have a less API-centric workflow, and make it more focused on having actual frameworks for different purposes like handling NDs, PFDs, CDUs or HUDs - under the hood, those would obviously share quite a bit of overlapping functionalities, but at least that would free people from having to understand low-level details - we've seen a number of instances such as the extra500, the EFB or your recent work, that clearly demonstrates, that we shouldn't focus on lower-level APIs, but should really provide frameworks, analogous to MapStructure - those are easy to maintain and optimize, even if that should mean that they'll be partially (or even entirely) re-implemented through C++ extensions to provide better performance - but as long as people "dabble" with lower-level APIs, we have no way to holistically optimize things in terms of animation handling etc.
Cquote1.png Coming up with a simple Nasal/Canvas animation framework that's designed around SVG elements, would not be very difficult - we have quite a few examples meanwhile, so that some APIs can be extracted, generalized and unified: Canvas_Animation_Framework

That would allow us to provide a single back-end, and easily optimize that over time, instead of having to maintain a number of different code bases using Canvas. Frankly, any other suggestions simply don't scale at all - and your work demonstrates that fairly well, and all the mess that went on when the RM code was updated, is another excellent example for the underlying problem, i.e. why we literally need to SHIELD aircraft developers from implementation details, including lower-level APIs.



F-JJTH's gpsmap196 GUI dialog showing the panel page

This is currently being worked on by F-JJTH & Hooray as part of working on Garmin GPSMap 196

Note  Also see

We want to support the notion of an "AnimatedSymbol" but also an "AnimatedLayer" - to some degree, this could be heavily influenced by MapStructure and the way the ND code makes use of canvas transformations for updating things like

  • size/scale
  • color
  • rotation
  • style (font)

It should be possible to create vectors of animations that can be combined and applied in a single update.

var Animation = {};

var RotationAnimation = {};

var Compass = func getprop("/orientation/heading-deg"), group: compass );

Each animation object would implement its own attributes (size, color, rotation, font) by providing corresponding methods that need to be implemented by child classes inheriting from the corresponding superclass.

var AnimationAttribute = {};

var HeadingAttribute =;
var ColorAttribute =;

Each AnimationAttribute would implement its own validate() method to ensure that passed state is valid, so that errors cannot propagate:

var HeadingAttribute =;
HeadingAttribute.validate = func(attr) attr <=360 and >= 0;

var AltitudeAttribute =;
Altitude.validate = func(attr) attr >= 0;

Based on the lessons learnt from MapStructure, adopting a MVC-approach would make sense, i.e. controllers would handle animation (e.g. /orientation/heading-deg), models would contain the actual data to be shown (e.g. compass rose), while the view would be a handle for a canvas group that renders/updates the actual group.

Animation controllers can be driven by:

  • timers
  • listeners
  • or both (i.e. a listener triggering/controlling a maketimer object)

More complex animations would be implemented on top of vectors of Animation objects, each using a separate canvas group that can be individually hidden/shown or animated via timers/listeners (or PropertyRules to reduce Nasal/GC overhead)