Canvas animation framework
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.
Mentors: Hooray (get in touch to learn more) |
Started in | 08/2014 (experiments) |
---|---|
Description | Animation Framework for Canvas |
Maintainer(s) | Hooray, Philosopher [1] |
Contributor(s) |
|
Status | discussion & prototyping (feedback & help appreciated!) |
Subforum | http://forum.flightgear.org/viewforum.php?f=71 |
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. |
The FlightGear forum has a subforum related to: Canvas |
Background
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
|
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).
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.
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
— Hooray (Fri Jul 18). Re: Drastic performance drop from custom canvas HUD developm.
(powered by Instant-Cquotes) |
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.
— Hooray (Mon Jul 21). Re: Drastic performance drop from custom canvas HUD developm.
(powered by Instant-Cquotes) |
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
— Hooray (Mon Jul 21). Re: Drastic performance drop from custom canvas HUD developm.
(powered by Instant-Cquotes) |
Design
Note
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 = RotationAnimation.new(controller: 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 = AnimationAttribute.new();
var ColorAttribute = AnimationAttribute.new();
Each AnimationAttribute would implement its own validate() method to ensure that passed state is valid, so that errors cannot propagate:
var HeadingAttribute = AnimationAttribute.new();
HeadingAttribute.validate = func(attr) attr <=360 and >= 0;
var AltitudeAttribute = AnimationAttribute.new();
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)