Canvas properties

From FlightGear wiki
Jump to navigation Jump to search

A property-driven 2D drawing API for FlightGear.

Status 07/2013

Tom is currently working on a 2D drawing API. The basic idea is to draw arbitrary 2D shapes and text to a texture and place this texture on some model in the scene (eg. display, HUD,...). This so called canvas should be controlled only by using the property tree.

Currently it is possible to draw text, arbitrary OpenVG paths, use vector and raster images and transparently create geographic maps, transform them, change styles and all of this through the property tree. There's also a Nasal API which provides an object-orientated wrapper around the property tree interface to the canvas.

In 07/2013, Tom added a decoration engine, and improved the C++ window manager to allow creating shadows and window decoration with a single line of Nasal code. Meanwhile, Canvas textures can be used in cockpits/aircraft, GUI windows and dialogs and as part of the scenery.

The long term idea is to eventually port some other 2D elements to this backend (eg the HUD and 2D panels) so they use OSG (and osgText) natively, and hence reduce the amount of C++ code we have for these jobs. (And increase our chances of working with never OpenGL versions that forbid old style GL calls) Long-term here means 'after 2.8 at least'.

In the meantime the more testing and feedback we can get the better. In particular this system should offer a much more efficient way to build CDU interfaces than many text animations, but as always it needs people to experiment and report what features are missing to make their life easier. Creating a 'fgcanvas' client analogous to 'fgpanel' should be doable too, since the canvas simply renders to a single osg-Camera. Unlike fgpanel this will require OSG instead of raw GL of course, but that's the price we pay for unifying the rendering backend.

Try it out

The latest information on the canvas subsystem can be found in README.canvas.

Demonstration of the new Canvas drawing API on the Cessna 172P.
  1. Launch FlightGear with the plane c172p-canvas (--aircraft=c172p-canvas)
  2. Observe a nice display in front of you with some animated text elements
  3. If it doesn't look like the picture on the right please report a bug (As comment on the merge request or on the Talk page)
  4. And if it works try out modifying the demo, integrate it into new planes, report more bugs and give me some feedback :)

Nasal API

  • fgdata/Nasal/canvas/api.nas (Contains the implementation of the API)
  • fgdata/Aircraft/Instruments-3d/canvas-test/canvas-test.nas (Demonstrates how to use the API)

See also Howto:Add a 2D canvas instrument to your aircraft


  • Support a Scenery Placement mode Done Done (WIP: [1])
  • Support drawing to textures for aircraft instruments Done Done
  • Support drawing to aircraft HUDs Done Done
  • Support drawing to GUI widgets Done Done
  • Support creating GUI windows Done Done
  • TheTom: "If each widget/tool/whatever don't creates an own canvas, but instead just attaches itself to a given group infinite nesting of "canases" would be easily possible already with the current implementation."Done Done

Planned Features

  • Support implementing GUI widgets using Canvas/Nasal Pending Pending (see Canvas Widgets)
  • Static image files: should be handled like any other element on the canvas. Only instead of specifying a path you'll have to set the file system path to an image which will be loaded with osg. Done Done
  • Support placing canvas textures into the scenery, so that dynamic scenery features (i.e. a visual docking guidance system or runway skid marks) can be implemented using the canvas system (being discussed and planned as of 08/2012) Done Done [2] (WIP)
  • Hooray: "yes, sounds like the best and most powerful design to me, because everything would still be a canvas, but nesting/recursion would be supported, i.e. "piping" the output from one canvas into another canvas group, so that canvases can be reused and nested" Done Done
  • TheTom: "Maybe I should add means to define clipping regions to guarantee widgets can't draw outside their assigned regions, i.e. perform clipping only if there is actually a clipping region specified." Done Done
  • Omega95: "Supporting 3D objects to be projected onto canvas" [3]

Supporting distributed master/slave setups

I'd just thought I'd share this, so that we are aware of some shortcomings, and ideas on addressing them:

There's a handful of threads where we talked about configuring multi-instance setups. This

We have a bunch of options for configuring multiple fgfs instances in a master/slave fashion, where an arbitrary number of slaves can be driven through a single master instance, important properties are sync'ed/replicated through a socket connection. Specific subsystems often have custom hard-coded protocols, for syncing certain state (joystick, fdm, controls etc) This is an old feature, which is still working - but many more recent features do not properly/fully support it. The canvas being one of them. However, it still is an important feature, that is also used at various event to demonstrate FlightGear to new users, such as e.g. FSWeekend and LinuxTag.

The underlying issue is pretty common in FG, can be seen in many places - such as clouds/weather not being in sync, AI traffic, animations/effects etc

Often, this requires lots of work to fix properly, because subsystems were not really designed to serialize internal state via the property tree, so there's lots of "private" state that never shows up in the tree, and there are no hooks to send events to sync things.

However, the Canvas is a different "beast", because it is already built on top of the property tree, much moreso than any other subsystem probably.

A while ago, we talked about the idea of supporting something like FGCanvas, analogous to FGPanel - which is also just about synchronizing properties in a master/slave fashion once you think about it.

Back then, we briefly talked about the idea of "mounting" a property tree in a remote instance to sync state properly and to formalize master/slave relationships.

I have been able to get this somewhat working by using the telnet protocol, and its "subscribe" command to get updates for the /canvas branch in the main instance - the power of canvas & the property tree makes that pretty simple. However, it's really crude, because it will get all update notifications, without being selective - because instances don't know anything about the master, and about stuff that would be redundant to do.

So, that's a use-case for which the canvas wasn't really designed obviously: basically, you end up with two canvas subsystems, without the 2nd/slaved system being aware that it is a slave, and that it should not do certain things, and instead rely on CERTAIN data from the master. Instead, the 2nd canvas system may override some received state, with its own internal state currently. In other words, I may be having multiple canvas instances in each fgfs instance, that is not aware of what the other one is doing, as long as it's a really simple instrument/dialog, it doesn't matter - but things fall quickly apart once things become more sophisticated.

To address this, I am thinking in terms of adding a property attribute to mark certain properties as master/slave, so that a sync/replication mechanism at the property tree level knows exactly which properties to send/request, and which ones to compute locally.

Also, I would want to formalize properties as being "input" or "output" properties, so that the canvas system knows for example that a certain property is a "readonly slave" property, i.e. comes from the master and cannot be affected locally.

Even if we should not use a manual sync method, the HLA work will also benefit from subsystems having a concept of properties being "master" or "slave". Torsten's fgpanel work is another obvious example that would benefit from knowing more about properties being master or slave - equally, the multiplayer and replay/flight recorder subsystems could benefit from such info.

IIRC you are using PropertyObjects<> - so my current idea would be to extend that class and allow such attributes to be specified via enums, i.e. to mark properties as master/slave and input/output, so that certain code paths can be made optional, because they should only be done on the master or slave side.

The sync mechanism/protocol could then traverse the canvas tree and check which properties it needs to subscribe to from the master, and which ones are client-side

Given that more and more systems are going to switch over to the canvas (HUD, instruments, MFDs), it would make sense to come up with a mechanism that allows multi-instance setups to be properly sync'ed - especially because this is something that all the hard-coded MFDs are also not very good at (NavDisplay, wxradar etc)

In other words, the question would be how to better support use-cases where an aircraft that relies on canvas-MFDs, like zhr C130J for example, is simulated by multiple inter-connected fgfs instances - another valid scenario would be our existing "dual-pilot support", which goes to show that we should keep in mind such synchronized master/slave setups.

Thoughts / better ideas ?

Adressing modes: by-index, by-name (property mounting)

As a first step I've now moved /canvas/texture[x] to /canvas/by-index/texture[x]. Later we can than add links from eg. /canvas/gui/texture[x] to /canvas/by-index/texture[y]. I just don't know how to best link it yet. Either make /canvas/gui/texture[x] a string property which contains the path to the texture[y] node or an alias to a node inside the texture or even modify the property nodes to support real links (whith all its problems like loops and the like). Another possibililty is to add support for jumping to paths specified in properties in an enhanced property dialog.

Referring to Remote Properties and the mentioned "mounting" support:

For displaying one would just need to mount the canvas branch inside the remote EICAS FlightGear instance. If also interaction back to the main FG instance is needed it would also be needed to write to a remote tree. This can be handled the same if remote mounting also supports writing in all directions. So, yes with fill mounting support this would be possible

One would just have to decide on which instance of FG the calculations of the ECIAS and other displays should take place, but with full mounting support this should be easily distributable as computing power is available.

Serializing a canvas to a buffer or file

Cquote1.png I am also thinking about some kind of one-time canvas, where the contents are just rendered once and afterwards can be used in an other canvas as a texture and maybe even stored on the hard disk an reused the next time FlightGear is started up.[1]
— Thomas Geymayer
  1. Thomas Geymayer (22-09-2012). Airport Selection feedback.

See FGPlot#Feature_Requests: provide a new Nasal API to serialize a canvas texture (or group) to an osg::Image in $FG_HOME, so that graphs can be easily saved and shared/posted without users having to take full screen shots and then having to edit the image [4]

providing a hook to get a binary dump of the a canvas texture (i.e. as a PNG file)

By adding a hook (Nasal extension function) to access the texture of the canvas as a binary dump, Nasal scripts could not only write a canvas texture to the file system, but they could even mime-encode it and send it to the browser (via the built-in HTTP server), so that canvas textures could be shown inside the browser for example, basically canvas textures could be used outside the fgfs process by providing a handle to access the dump.

These two features alone would make it possible for people to draw to a canvas, save it to the file system and even chaining canvases by using the output of one canvas, as the input for another one (a while ago, we talked about the lack of support for chaining od_gauge systems).

And serializing a canvas is trivial because it's just a property tree, so it can be done via save_properties()

In addition, being able to serialize canvases to files on disk, would also help us keeping our docs (the manual) more up to date, because our GUI being canvas-driven, screen shots could be created in an automated/scripted fashion, so that the LaTex docs could automatically show the latest GUI dialogs.

Another use-case would be screen shots of individual dialogs, currently users need to capture the whole screen and then cut out the relevant part (see [5]) - it would be awesome if we could have some dedicated API to capture a canvas and write it to a file, so that only relevant stuff ends up in the file, rather than the whole screen.

Smart Labels for OSGText elements

Looking at the NavDisplay/Map code, the canvas system could use another text-placement mode which tries to be "smart" by using unoccupied screen space for labels, rather than cluttering the displays with lots of labels covering other labels and symbols. i.e. implicit rather than explicit label placement?

It's not yet sure how to address this easily, probably that would require bounding box calculations for existing "drawables", so that free spots can be found. Maybe there's a better way? Maybe OSG has some code to get the area required by a drawable ? There's an open source code on codeproject, titled "Smart Labels"

Smart labels should probabaly be added as a "group-aware" element, so that they may look at other groups in order to come up with all bounding boxes for their drawables, so that free space can be found - taking into account certain groups ("layers"). Also, smart labels should probably be able to adjust font or background color, in order to be still readable when overlapping with other symbols?
Note that PAL requires GEOS, both use an autotools based build system.

Also see: