Canvas SVG Intro

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.
screen shot showing a slightly modified version of svg.nas to help newcomers understand how SVG handling works inside FG and how to hook up SVG to FlightGear properties [1]

The following article is going to provide an introduction to explain how SVG files and properties are hanging together in FlightGear. So if you're wondering how a FlightGear property is added to an SVG file, which is in turn shown inside FlightGear, here's the basic workflow.

First things first, an SVG file is nothing more than a plain text file containing XML markup, pretty much like our existing PropertyList XML files. The main difference being that SVG/XML is an actual standard to contain/describe image data. Since these files are plain text, the corresponding text markup needs to describe the contents of the image. That's the main difference to raster images (which contain pixel data), SVG images contain vector data. If in doubt, read up the details on wikipedia or look up a few intro videos on youtube.

Now, when it comes to FlightGear, the corresponding artwork (SVG or OpenVG) will typically be put into a Canvas element that is then looked up afterwards by name/identifier (SVG id), at that point you can animate/update and modify each element as needed - basically, this works like you said via something like getprop/props.nas - however, for the sake of efficiency, there are some smart algorithms being used to make this a little more efficient and abstract - the latter is primarily done to be able to reuse the same code for different avionics/aircraft, but also for things like the built-in canvas map dialog.

The key point here being that image data loaded from an SVG/XML file "as is", would simply be "anonymous" - but since we typically don't just want to have an image "blob" loaded into FlightGear, but rather need a means to access/modify the whole image or parts of it, we need to assign those parts to variable names/identifiers.

So, in general, there is the equivalent of getprop() call whose values is then used to update a canvas element. In particular however, things are a little more sophisticated in most cases - in those cases where things work exactly like that, efficiency is sacrificed

These identifiers (IDs) or "keys" are basically arbitrary (made-up). They were made up by the people who created/edited the artwork file (SVG file). If something is non-static, i.e. needs to be changed at runtime, you need to assign a unique identifier to the corresponding element in the SVG file. Once you have such an identifer assigned, you can look up the element in Nasal/Canvas space and get a handle to the rendered element to easily update/animate and render (hide/show) the corresponding element. In the case of a heading or bearing, those elements would end up being Canvas text elements.

To see for yourself, just open the SVG file in Inkscape and look for the identifier(s) of interest. Once you have a basic understanding of XML/SVG, you could also just use a conventional text editor to do so, i.e. you don't need Inkscape for that.

However, for starters it would make more sense to use the Nasal console in conjunction with a simple hand-edited SVG file to learn and understand how this works conceptually - it's only afterwards doing that, that you will want to look at an existing framework like the ND stuff.

You can create a simple SVG file using 5-10 lines of SVG/XML - and you could use another 20 lines of code to load that file inside FlightGear and render its to a dialog - we can walk you through the whole process, and it will go a long way to help you understand more sophisticated examples like the existing ND


For some additional background reading, I'd suggest to work through some of these articles to get a better understanding how things are hanging together - ideally in the given order:

You don't need to read/understand all of this, but it will probably fill in quite a few blanks you are currently facing, and who knows, maybe you'll end up understanding much more than you were hoping to even ask for. However, just to be clear, tinkering even just one rainy weekend with a few SVG files and examples will go a really long way, and will make everything else much easier.

Obviously, if you don't have any previous coding experience, then facing Nasal may seem daunting at first. And so is facing SVG without prior exposure to XML or HTML like languages. So it definitely does help having at least /some/ experience working with JavaScript and HTML, because the FG side of things may feel much more familiar afterwards.

If you are lacking _any_ coding experience at all, I would suggest to ignore FG for one or two weekends and simply play around with HTML and SVG - ideally working through some basic W3C tutorials to create SVG files manually and show them in your browser. The next thing you will want to do is to add some dynamic "action" to an SVG file to animate it - that workflow will basically work like in FG.

Thus, once you understand how to manually create an SVG file, set up 3 different elements with different IDs and then load that inside your browser to change some text/color/attribute, you would be in a much better position to apply this knowledge in the wider context of FG

<!--

var (width,height) = (400,100);
var title = 'SVG Intro';

# create a new window, dimensions are WIDTH x HEIGHT, using the dialog decoration (i.e. titlebar)
var window = canvas.Window.new([width,height],"dialog").set('title',title);

# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

# change the background color 
# myCanvas.set("background", "#ffaac0");

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

var filename = "/Nasal/canvas/map/Images/boxes.svg";
var svg_symbol = root.createChild('group');
canvas.parsesvg(svg_symbol, filename);



-->

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 100">
  <title>SVG Intro</title>
  <text x="50" y="50" text-anchor="middle" font-size="30px" fill="black">Hello World</text>
  <rect id="box1" x="0" y="0" width="100" height="100" fill="red" />
  <rect id="box2" x="100" y="0" width="100" height="100" fill="green" />
  <rect id="box3" x="200" y="0" width="100" height="100" fill="yellow" />
  <rect id="box4" x="300" y="0" width="100" height="100" fill="blue" />
</svg>

To test the whole thing, execute the following code via the Nasal console:

var (width,height) = (400,100);
var title = 'SVG Intro';

# create a new window, dimensions are WIDTH x HEIGHT, using the dialog decoration (i.e. titlebar)
var window = canvas.Window.new([width,height],"dialog").set('title',title);

# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

# change the background color 
# myCanvas.set("background", "#ffaac0");

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

var filename = "/Nasal/canvas/map/Images/boxes.svg";
var svg_symbol = root.createChild('group');
canvas.parsesvg(svg_symbol, filename);

References

References