SVGCanvas
SVGCanvas is a base class to populate a canvas from SVG and animate elements easily. The source file is $FGDATA/Nasal/canvas/api/svgcanvas.nas
Usage examples
A simple usecase is a speed booklet as it can be found in the CRJ700 (versions since 2020). A SVG is used as a layout template, data is filled in by Nasal code from tables.
A shortened code sample:
var canvas_settings = {
"size": [1024,1024],
"view": [1024,1024],
};
var window_size = [512,512];
var speedbooklet_svg = "speedbooklet.svg";
# class Booklet extends SVGCanvas
var Booklet =
{
bgcolor: [0.9, 0.9, 0.9, 1], #background color for canvas
new: func(name, svgfile, speedtable) {
var obj = {
parents: [me, canvas.SVGCanvas.new(name, canvas_settings)],
# put all element IDs you wish to animate in this vector (use a SVG editor like inkscape
# to find/edit ID strings in SVG file, the strings used here must match the IDs in the SVG file)
svg_keys: [
"title", "tonns", "kilogram", "weight_unit", "overweight",
"v1_8", "vr_8", "v2_8", "vt",
"v1_20", "vr_20", "v2_20",
"vref_0", "vref_1", "vref_8", "vref_20", "vref_30", "vref_45",
"page", "left", "right",
],
_pageN: props.getNode("/sim/gui/speedbooklet/page",1),
#the speedtable class is not described in detail to keep this short, it stores data for the pages of the booklet
speedtable: speedtable,
};
obj.getCanvas().setColorBackground(me.bgcolor);
obj.loadSVG(svgfile, obj.svg_keys);
obj.init();
return obj;
},
init: func() {
# support mouse click for the page change buttons
me["left"].addEventListener("click", func(e) { me.prevPage(); });
me["right"].addEventListener("click", func(e) { me.nextPage(); });
me._pageN.setIntValue(0);
return ;
},
del: func() {
if (me._L) removelistener(me._L);
call(canvas.SVGCanvas.del, [], me, var err = []);
return nil;
},
nextPage: func() {
if (me._pageN.getValue() < me.speedtable.getNumberOfPages() - 1) {
me._pageN.increment();
me.update();
}
return me;
},
prevPage: func() {
if (me._pageN.getValue() > 0) {
me._pageN.decrement();
me.update();
}
return me;
},
# update elements
update: func() {
var number = me._pageN.getValue();
var speeds = me.speedtable.getPage(number);
foreach (var key; keys(speeds)) {
if (vecindex(me.svg_keys, key) != nil)
me.updateTextElement(key, sprintf("%d", speeds[key]));
}
me["overweight"].setVisible(me.speedtable.isOverMLW(speeds.weight));
me.updateTextElement("tonns", sprintf("%d", int(speeds.weight/1000)));
me.updateTextElement("kilogram", sprintf("%03d", math.mod(speeds.weight, 1000)));
me.updateTextElement("page", sprintf("%2d", number + 1));
},
};
var window_title = "Speedbooklet "~aero;
var book = Booklet.new(window_title, speedbooklet_svg, st);
book.asWindow(window_size);
Class functions
new()
new(name, settings=nil);
Create a new SVGCanvas
- name
- Name of the canvas.
- setting
- Hash of canvas settings.
Example
var myCanvas = SVGCanvas.new("mySVG");
myCanvas.loadsvg("myfile.svg", ["foo", "bar"]);
del()
Destructor. Remove window (if any) and canvas.
loadSVG()
loadSVG(file, svg_keys, options=nil);
loads SVG file and create canvas.element objects for given IDs
- file
- filename of SVG to load
- svg_keys
- Vector of id strings. For each id a member will create in the SVGCanvas object. If there is an SVG object named <id>_clip, it will be automatically setup as clip for <id>
- options
- Optional hash with options passed to the SVG parser (canvas.parsesvg)
Example
var myCanvas = SVGCanvas.new("mySVG");
myCanvas.loadsvg("myfile.svg", ["foo", "bar"]);
asWindow()
asWindow(window_size);
opens the canvas in a window
- window_size
- vector [size_x, size_y] passed to canvas.Window.new
Example
var myCanvas = SVGCanvas.new("mySVG");
myCanvas.loadsvg("myfile.svg", ["foo", "bar"]);
getPath()
wrapper for canvas.getPath()
getCanvas()
return the canvas object
getRoot()
return the top level canvas group element created by new()
updateTextElement()
updateTextElement(svgkey, text, color=nil);
update text and color of a canvas text element
- svgkey
- Name (ID) of the text element.
- text
- New value for text element
- color
- Optional new color. Can be either a vector e.g. [r,g,b] or a color name from canvas.colors
Semi-private class methods
The following methods are ment for creating derived classes, not to be called directly. They all return listener functions ("handlers") to be used in a setlistener() call. The idea is to easily animate canvas elements based on properties that change "irregularly" and "not too often", e.g. not every frame. For properties that change regularly and/or very often, an update function called by a timer may be more efficient. If you plan for a rather complex display with regular updates, see Canvas EFIS Framework which extends SVGCanvas.
_makeListener_showHide()
_makeListener_showHide(svgkeys, value=nil);
returns generic listener to show/hide element(s) based on property node value
- svgkeys
- string (single ID) or vector of stings (IDs). Hint: if possible, group elements in SVG file and animate group instead of individual elements.
- value
- optional value to trigger show(); otherwise node.value will be implicitly treated as bool
_makeListener_rotate()
_makeListener_rotate(svgkeys, factors=nil);
returns listener to set rotation of element(s) based on property node value (optionally multiplied by a factor).
- svgkeys
- string (single ID) or vector of stings (IDs). Hint: if possible, group elements in SVG file and animate group instead of individual elements.
- factors
- optional, number (if svgkeys is a single key) or hash of numbers {"svgkey" : factor}, missing keys will be treated as 1, e.g. rotate by property value.
Example
# this is from an EICAS display class in the CRJ700 (see CRJ700-family/Nasal/EFIS/*) extending EFISCanvas (see /Nasal/modules/canvas_efis/efis-canvas.nas) which in turn uses SVGCanvas
# note: for the rudder trim a factor -1 is used to match the animation with the property
setlistener("controls/flight/rudder-trim", me._makeListener_rotate("rudderTrim", -1), 1, 0);
setlistener("controls/flight/aileron-trim", me._makeListener_rotate("ailTrim"), 1, 0);
_makeListener_translate()
_makeListener_translate(svgkeys, fx, fy);
returns listener to set rotation of element(s) based on property node value (optionally multiplied by a factor).
- svgkeys
- string (single ID) or vector of stings (IDs). Hint: if possible, group elements in SVG file and animate group instead of individual elements.
- fx
- number or hash of numbers {"svgkey" : factor}, missing keys will be treated as 0 (=no op)
- fy
- number or hash of numbers {"svgkey" : factor}, missing keys will be treated as 0 (=no op)
Example
# this is from an EICAS display class in the CRJ700 (see CRJ700-family/Nasal/EFIS/*) extending EFISCanvas (see /Nasal/modules/canvas_efis/efis-canvas.nas) which in turn uses SVGCanvas
# two elements are animated by a single property, the translation in x direction is zero and in y direction the property is multiplied by -139.46 (which can probably be found using a SVG editor and check the size of the element)
setlistener("/surface-positions/spoiler-ob-ground-pos-norm", me._makeListener_translate(["spoilerIndL3","spoilerIndR3"], 0, -139.46), 1, 0);
_makeListener_setColor()
_makeListener_setColor(svgkeys, color_true, color_false);
returns generic listener to cange color of element(s) based on property node value
- svgkeys
- string (single ID) or vector of stings (IDs). Hint: if possible, group elements in SVG file and animate group instead of individual elements.
- color_true
- Color to be set if node evaluates to true, can be either a vector e.g. [r,g,b] or a color name from canvas.colors
- color_false
- Color to be set, if node evaluates to false, can be either a vector e.g. [r,g,b] or a color name from canvas.colors
Example
# this is from an EICAS display class in the CRJ700 (see CRJ700-family/Nasal/EFIS/*) extending EFISCanvas (see /Nasal/modules/canvas_efis/efis-canvas.nas) which in turn uses SVGCanvas
# change the color of pump symbol with the ID "xflowPump" to either yellow or white depending on the inop property
setlistener("systems/fuel/xflow-pump/inop", me._makeListener_setColor("xflowPump", me.colors["amber"], me.colors["white"]), 1, 0);
_makeListener_updateText()
_makeListener_updateText(svgkeys, format="%s", default="");
Returns a listener that calls updateTextElement(key, sprintf(format, n.getValue() or default));
- svgkeys
- string (single ID) or vector of stings (IDs).
- format
- optional format sting for sprintf
- default
- text to use if node.getValue() fails
Example
# this is from an EICAS display class in the CRJ700 (see CRJ700-family/Nasal/EFIS/*) extending EFISCanvas (see /Nasal/modules/canvas_efis/efis-canvas.nas) which in turn uses SVGCanvas
setlistener("/controls/autoflight/speed-select", me._makeListener_updateText("iasref.text", "%d", 0), 1, 0);