1
0
Fork 0
fgdata/Nasal/canvas/api.nas
Thomas Geymayer e7900e3791 Canvas: remove API for FlightGear 2.8
The old API is not used with newer versions of FG. If an old
version of FG is used, also the according version of fgdata
should be used, which also includes the correct API wrappers.
2014-01-20 23:25:15 +01:00

1068 lines
27 KiB
Text

# Internal helper
var _getColor = func(color)
{
if( size(color) == 1 )
var color = color[0];
if( typeof(color) == 'scalar' )
return color;
if( typeof(color) != "vector" )
return debug.warn("Wrong type for color");
if( size(color) < 3 or size(color) > 4 )
return debug.warn("Color needs 3 or 4 values (RGB or RGBA)");
var str = 'rgb';
if( size(color) == 4 )
str ~= 'a';
str ~= '(';
# rgb = [0,255], a = [0,1]
for(var i = 0; i < size(color); i += 1)
str ~= (i > 0 ? ',' : '') ~ (i < 3 ? int(color[i] * 255) : color[i]);
return str ~ ')';
};
var _arg2valarray = func
{
var ret = arg;
while ( typeof(ret) == "vector"
and size(ret) == 1 and typeof(ret[0]) == "vector" )
ret = ret[0];
return ret;
}
# Transform
# ==============================================================================
# A transformation matrix which is used to transform an #Element on the canvas.
# The dimensions of the matrix are 3x3 where the last row is always 0 0 1:
#
# a c e
# b d f
# 0 0 1
#
# See http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined for details.
#
var Transform = {
new: func(node, vals = nil)
{
var m = {
parents: [Transform],
_node: node,
a: node.getNode("m[0]", 1),
b: node.getNode("m[1]", 1),
c: node.getNode("m[2]", 1),
d: node.getNode("m[3]", 1),
e: node.getNode("m[4]", 1),
f: node.getNode("m[5]", 1)
};
var use_vals = typeof(vals) == 'vector' and size(vals) == 6;
# initialize to identity matrix
m.a.setDoubleValue(use_vals ? vals[0] : 1);
m.b.setDoubleValue(use_vals ? vals[1] : 0);
m.c.setDoubleValue(use_vals ? vals[2] : 0);
m.d.setDoubleValue(use_vals ? vals[3] : 1);
m.e.setDoubleValue(use_vals ? vals[4] : 0);
m.f.setDoubleValue(use_vals ? vals[5] : 0);
return m;
},
setTranslation: func
{
var trans = _arg2valarray(arg);
me.e.setDoubleValue(trans[0]);
me.f.setDoubleValue(trans[1]);
return me;
},
# Set rotation (Optionally around a specified point instead of (0,0))
#
# setRotation(rot)
# setRotation(rot, cx, cy)
#
# @note If using with rotation center different to (0,0) don't use
# #setTranslation as it would interfere with the rotation.
setRotation: func(angle)
{
var center = _arg2valarray(arg);
var s = math.sin(angle);
var c = math.cos(angle);
me.a.setDoubleValue(c);
me.b.setDoubleValue(s);
me.c.setDoubleValue(-s);
me.d.setDoubleValue(c);
if( size(center) == 2 )
{
me.e.setDoubleValue( (-center[0] * c) + (center[1] * s) + center[0] );
me.f.setDoubleValue( (-center[0] * s) - (center[1] * c) + center[1] );
}
return me;
},
# Set scale (either as parameters or array)
#
# If only one parameter is given its value is used for both x and y
# setScale(x, y)
# setScale([x, y])
setScale: func
{
var scale = _arg2valarray(arg);
me.a.setDoubleValue(scale[0]);
me.d.setDoubleValue(size(scale) >= 2 ? scale[1] : scale[0]);
return me;
},
getScale: func()
{
# TODO handle rotation
return [me.a.getValue(), me.d.getValue()];
}
};
# Element
# ==============================================================================
# Baseclass for all elements on a canvas
#
var Element = {
# Reference frames (for "clip" coordinates)
GLOBAL: 0,
PARENT: 1,
LOCAL: 2,
# Constructor
#
# @param ghost Element ghost as retrieved from core methods
new: func(ghost)
{
return {
parents: [PropertyElement, Element, ghost],
_node: props.wrapNode(ghost._node_ghost)
};
},
# Get parent group/element
getParent: func()
{
var parent_ghost = me._getParent();
if( parent_ghost == nil )
return nil;
var type = props.wrapNode(parent_ghost._node_ghost).getName();
var factory = me._getFactory(type);
if( factory == nil )
return parent_ghost;
return factory(parent_ghost);
},
# Check if elements represent same instance
#
# @param el Other Element or element ghost
equals: func(el)
{
return me._node.equals(el._node_ghost);
},
# Trigger an update of the element
#
# Elements are automatically updated once a frame, with a delay of one frame.
# If you wan't to get an element updated in the current frame you have to use
# this method.
update: func()
{
me.setInt("update", 1);
},
# Hide/Show element
#
# @param visible Whether the element should be visible
setVisible: func(visible = 1)
{
me.setBool("visible", visible);
},
getVisible: func me.getBool("visible"),
# Hide element (Shortcut for setVisible(0))
hide: func me.setVisible(0),
# Show element (Shortcut for setVisible(1))
show: func me.setVisible(1),
# Toggle element visibility
toggleVisibility: func me.setVisible( !me.getVisible() ),
#
setGeoPosition: func(lat, lon)
{
me._getTf()._node.getNode("m-geo[4]", 1).setValue("N" ~ lat);
me._getTf()._node.getNode("m-geo[5]", 1).setValue("E" ~ lon);
return me;
},
# Create a new transformation matrix
#
# @param vals Default values (Vector of 6 elements)
createTransform: func(vals = nil)
{
var node = me._node.addChild("tf", 1); # tf[0] is reserved for
# setRotation
return Transform.new(node, vals);
},
# Shortcut for setting translation
setTranslation: func { me._getTf().setTranslation(arg); return me; },
# Set rotation around transformation center (see #setCenter).
#
# @note This replaces the the existing transformation. For additional scale or
# translation use additional transforms (see #createTransform).
setRotation: func(rot)
{
if( me['_tf_rot'] == nil )
# always use the first matrix slot to ensure correct rotation
# around transformation center.
me['_tf_rot'] = Transform.new(me._node.getNode("tf[0]", 1));
me._tf_rot.setRotation(rot, me.getCenter());
return me;
},
# Shortcut for setting scale
setScale: func { me._getTf().setScale(arg); return me; },
# Shortcut for getting scale
getScale: func me._getTf().getScale(),
# Set the fill/background/boundingbox color
#
# @param color Vector of 3 or 4 values in [0, 1]
setColorFill: func me.set('fill', _getColor(arg)),
#
getBoundingBox: func()
{
var bb = me._node.getNode("bounding-box");
if( bb != nil )
{
var min_x = bb.getNode("min-x").getValue();
if( min_x != nil )
return [ min_x,
bb.getNode("min-y").getValue(),
bb.getNode("max-x").getValue(),
bb.getNode("max-y").getValue() ];
}
return [0, 0, 0, 0];
},
# Calculate the transformation center based on bounding box and center-offset
updateCenter: func
{
me.update();
var bb = me.getTransformedBounds();
if( bb[0] > bb[2] or bb[1] > bb[3] )
return;
me._setupCenterNodes
(
(bb[0] + bb[2]) / 2 + (me.get("center-offset-x") or 0),
(bb[1] + bb[3]) / 2 + (me.get("center-offset-y") or 0)
);
return me;
},
# Set transformation center (currently only used for rotation)
setCenter: func()
{
var center = _arg2valarray(arg);
if( size(center) != 2 )
return debug.warn("invalid arg");
me._setupCenterNodes(center[0], center[1]);
return me;
},
# Get transformation center
getCenter: func()
{
var center = [0, 0];
me._setupCenterNodes();
if( me._center[0] != nil )
center[0] = me._center[0].getValue() or 0;
if( me._center[1] != nil )
center[1] = me._center[1].getValue() or 0;
return center;
},
# Internal Transform for convenience transform functions
_getTf: func
{
if( me['_tf'] == nil )
me['_tf'] = me.createTransform();
return me._tf;
},
_setupCenterNodes: func(cx = nil, cy = nil)
{
if( me["_center"] == nil )
me["_center"] = [
me._node.getNode("center[0]", cx != nil),
me._node.getNode("center[1]", cy != nil)
];
if( cx != nil )
me._center[0].setDoubleValue(cx);
if( cy != nil )
me._center[1].setDoubleValue(cy);
}
};
# Group
# ==============================================================================
# Class for a group element on a canvas
#
var Group = {
# public:
new: func(ghost)
{
return { parents: [Group, Element.new(ghost)] };
},
# Create a child of given type with specified id.
# type can be group, text
createChild: func(type, id = nil)
{
var ghost = me._createChild(type, id);
var factory = me._getFactory(type);
if( factory == nil )
return ghost;
return factory(ghost);
},
# Create multiple children of given type
createChildren: func(type, count)
{
var factory = me._getFactory(type);
if( factory == nil )
return [];
var nodes = props._addChildren(me._node._g, [type, count, 0, 0]);
for(var i = 0; i < count; i += 1)
nodes[i] = factory( me._getChild(nodes[i]) );
return nodes;
},
# Create a path child drawing a (rounded) rectangle
#
# @param x Position of left border
# @param y Position of top border
# @param w Width
# @param h Height
# @param cfg Optional settings (eg. {"border-top-radius": 5})
rect: func(x, y, w, h, cfg = nil)
{
return me.createChild("path").rect(x, y, w, h, cfg);
},
# Get a vector of all child elements
getChildren: func()
{
var children = [];
foreach(var c; me._node.getChildren())
if( me._isElementNode(c) )
append(children, me._wrapElement(c));
return children;
},
# Get first child with given id (breadth-first search)
#
# @note Use with care as it can take several miliseconds (for me eg. ~2ms).
# TODO check with new C++ implementation
getElementById: func(id)
{
var ghost = me._getElementById(id);
if( ghost == nil )
return nil;
var node = props.wrapNode(ghost._node_ghost);
var factory = me._getFactory( node.getName() );
if( factory == nil )
return ghost;
return factory(ghost);
},
# Remove all children
removeAllChildren: func()
{
foreach(var type; keys(me._element_factories))
me._node.removeChildren(type, 0);
return me;
},
# private:
_isElementNode: func(el)
{
# element nodes have type NONE and valid element names (those in the factory
# list)
return el.getType() == "NONE"
and me._element_factories[ el.getName() ] != nil;
},
_wrapElement: func(node)
{
# Create element from existing node
return me._element_factories[ node.getName() ]( me._getChild(node._g) );
},
_getFactory: func(type)
{
var factory = me._element_factories[type];
if( factory == nil )
debug.dump("canvas.Group.createChild(): unknown type (" ~ type ~ ")");
return factory;
}
};
# Map
# ==============================================================================
# Class for a group element on a canvas with possibly geopgraphic positions
# which automatically get projected according to the specified projection.
#
var Map = {
df_controller: nil,
new: func(ghost)
{
return { parents: [Map, Group.new(ghost)] };
},
del: func()
{
#print("canvas.Map.del()");
call(func {
me.controller.del(me);
}, var err=[]);
if (size(err)) {
debug.printerror(err);
setsize(err, 0);
}
call(func {
foreach (var l; me.layers)
call(l[0].del, nil, l[0]);
setsize(me.layers, 0);
}, err);
if (size(err)) {
debug.printerror(err);
setsize(err, 0);
}
# call inherited 'del'
me.parents = subvec(me.parents,1);
me.del();
},
setController: func(controller=nil)
{
if (controller == nil)
controller = Map.df_controller;
elsif (typeof(controller) != 'hash')
controller = Map.Controller.get(controller);
if (controller.parents[0] != Map.Controller)
die("OOP error");
me.controller = controller.new(me);
return me;
},
addLayer: func(factory, type_arg=nil, priority=nil)
{
if (!contains(me, "layers"))
me.layers = {};
if(contains(me.layers, type_arg))
print("addLayer() warning: overwriting existing layer:", type_arg);
# print("addLayer():", type_arg);
# Argument handling
if (type_arg != nil)
var type = factory.get(type_arg);
else var type = factory;
me.layers[type_arg]= type.new(me);
if (priority == nil)
priority = type.df_priority;
if (priority != nil)
me.layers[type_arg].setInt("z-index", priority);
return me;
},
getLayer: func(type_arg) me.layers[type_arg],
setPos: func(lat, lon, hdg=nil, range=nil)
{
me.set("ref-lat", lat);
me.set("ref-lon", lon);
if (hdg != nil)
me.set("hdg", hdg);
if (range != nil)
me.set("range", range);
},
# Update each layer on this Map. Called by
# me.controller.
update: func
{
foreach (var l; keys(me.layers)) {
var layer = me.layers[l];
call(layer.update, arg, layer);
}
return me;
},
};
# Text
# ==============================================================================
# Class for a text element on a canvas
#
var Text = {
new: func(ghost)
{
return { parents: [Text, Element.new(ghost)] };
},
# Set the text
setText: func(text)
{
me.set("text", typeof(text) == 'scalar' ? text : "");
},
# Set alignment
#
# @param align String, one of:
# left-top
# left-center
# left-bottom
# center-top
# center-center
# center-bottom
# right-top
# right-center
# right-bottom
# left-baseline
# center-baseline
# right-baseline
# left-bottom-baseline
# center-bottom-baseline
# right-bottom-baseline
#
setAlignment: func(align)
{
me.set("alignment", align);
},
# Set the font size
setFontSize: func(size, aspect = 1)
{
me.setDouble("character-size", size);
me.setDouble("character-aspect-ratio", aspect);
},
# Set font (by name of font file)
setFont: func(name)
{
me.set("font", name);
},
# Enumeration of values for drawing mode:
TEXT: 1, # The text itself
BOUNDINGBOX: 2, # A bounding box (only lines)
FILLEDBOUNDINGBOX: 4, # A filled bounding box
ALIGNMENT: 8, # Draw a marker (cross) at the position of the text
# Set draw mode. Binary combination of the values above. Since I haven't found
# a bitwise or we have to use a + instead.
#
# eg. my_text.setDrawMode(Text.TEXT + Text.BOUNDINGBOX);
setDrawMode: func(mode)
{
me.setInt("draw-mode", mode);
},
# Set bounding box padding
setPadding: func(pad)
{
me.setDouble("padding", pad);
},
setMaxWidth: func(w)
{
me.setDouble("max-width", w);
},
setColor: func me.set('fill', _getColor(arg)),
setColorFill: func me.set('background', _getColor(arg))
};
# Path
# ==============================================================================
# Class for an (OpenVG) path element on a canvas
#
var Path = {
# Path segment commands (VGPathCommand)
VG_CLOSE_PATH: 0,
VG_MOVE_TO: 2,
VG_MOVE_TO_ABS: 2,
VG_MOVE_TO_REL: 3,
VG_LINE_TO: 4,
VG_LINE_TO_ABS: 4,
VG_LINE_TO_REL: 5,
VG_HLINE_TO: 6,
VG_HLINE_TO_ABS: 6,
VG_HLINE_TO_REL: 7,
VG_VLINE_TO: 8,
VG_VLINE_TO_ABS: 8,
VG_VLINE_TO_REL: 9,
VG_QUAD_TO: 10,
VG_QUAD_TO_ABS: 10,
VG_QUAD_TO_REL: 11,
VG_CUBIC_TO: 12,
VG_CUBIC_TO_ABS: 12,
VG_CUBIC_TO_REL: 13,
VG_SQUAD_TO: 14,
VG_SQUAD_TO_ABS: 14,
VG_SQUAD_TO_REL: 15,
VG_SCUBIC_TO: 16,
VG_SCUBIC_TO_ABS: 16,
VG_SCUBIC_TO_REL: 17,
VG_SCCWARC_TO: 20, # Note that CC and CCW commands are swapped. This is
VG_SCCWARC_TO_ABS:20, # needed due to the different coordinate systems used.
VG_SCCWARC_TO_REL:21, # In OpenVG values along the y-axis increase from bottom
VG_SCWARC_TO: 18, # to top, whereas in the Canvas system it is flipped.
VG_SCWARC_TO_ABS: 18,
VG_SCWARC_TO_REL: 19,
VG_LCCWARC_TO: 24,
VG_LCCWARC_TO_ABS:24,
VG_LCCWARC_TO_REL:25,
VG_LCWARC_TO: 22,
VG_LCWARC_TO_ABS: 22,
VG_LCWARC_TO_REL: 23,
# Number of coordinates per command
num_coords: [
0, 0, # VG_CLOSE_PATH
2, 2, # VG_MOVE_TO
2, 2, # VG_LINE_TO
1, 1, # VG_HLINE_TO
1, 1, # VG_VLINE_TO
4, 4, # VG_QUAD_TO
6, 6, # VG_CUBIC_TO
2, 2, # VG_SQUAD_TO
4, 4, # VG_SCUBIC_TO
5, 5, # VG_SCCWARC_TO
5, 5, # VG_SCWARC_TO
5, 5, # VG_LCCWARC_TO
5, 5 # VG_LCWARC_TO
],
#
new: func(ghost)
{
return {
parents: [Path, Element.new(ghost)],
_first_cmd: 0,
_first_coord: 0,
_last_cmd: -1,
_last_coord: -1
};
},
# Remove all existing path data
reset: func
{
me._node.removeChildren('cmd', 0);
me._node.removeChildren('coord', 0);
me._node.removeChildren('coord-geo', 0);
me._first_cmd = 0;
me._first_coord = 0;
me._last_cmd = -1;
me._last_coord = -1;
return me;
},
# Set the path data (commands and coordinates)
setData: func(cmds, coords)
{
me.reset();
me._node.setValues({cmd: cmds, coord: coords});
me._last_cmd = size(cmds) - 1;
me._last_coord = size(coords) - 1;
return me;
},
setDataGeo: func(cmds, coords)
{
me.reset();
me._node.setValues({cmd: cmds, 'coord-geo': coords});
me._last_cmd = size(cmds) - 1;
me._last_coord = size(coords) - 1;
return me;
},
# Add a path segment
addSegment: func(cmd, coords...)
{
var coords = _arg2valarray(coords);
var num_coords = me.num_coords[cmd];
if( size(coords) != num_coords )
debug.warn
(
"Invalid number of arguments (expected " ~ num_coords ~ ")"
);
else
{
me.setInt("cmd[" ~ (me._last_cmd += 1) ~ "]", cmd);
for(var i = 0; i < num_coords; i += 1)
me.setDouble("coord[" ~ (me._last_coord += 1) ~ "]", coords[i]);
}
return me;
},
# Remove first segment
pop_front: func me._removeSegment(1),
# Remove last segment
pop_back: func me._removeSegment(0),
# Get the number of segments
getNumSegments: func()
{
return me._last_cmd - me._first_cmd + 1;
},
# Get the number of coordinates (each command has 0..n coords)
getNumCoords: func()
{
return me._last_coord - me._first_coord + 1;
},
# Move path cursor
moveTo: func me.addSegment(me.VG_MOVE_TO_ABS, arg),
move: func me.addSegment(me.VG_MOVE_TO_REL, arg),
# Add a line
lineTo: func me.addSegment(me.VG_LINE_TO_ABS, arg),
line: func me.addSegment(me.VG_LINE_TO_REL, arg),
# Add a horizontal line
horizTo: func me.addSegment(me.VG_HLINE_TO_ABS, arg),
horiz: func me.addSegment(me.VG_HLINE_TO_REL, arg),
# Add a vertical line
vertTo: func me.addSegment(me.VG_VLINE_TO_ABS, arg),
vert: func me.addSegment(me.VG_VLINE_TO_REL, arg),
# Add a quadratic Bézier curve
quadTo: func me.addSegment(me.VG_QUAD_TO_ABS, arg),
quad: func me.addSegment(me.VG_QUAD_TO_REL, arg),
# Add a cubic Bézier curve
cubicTo: func me.addSegment(me.VG_CUBIC_TO_ABS, arg),
cubic: func me.addSegment(me.VG_CUBIC_TO_REL, arg),
# Add a smooth quadratic Bézier curve
squadTo: func me.addSegment(me.VG_SQUAD_TO_ABS, arg),
squad: func me.addSegment(me.VG_SQUAD_TO_REL, arg),
# Add a smooth cubic Bézier curve
scubicTo: func me.addSegment(me.VG_SCUBIC_TO_ABS, arg),
scubic: func me.addSegment(me.VG_SCUBIC_TO_REL, arg),
# Draw an elliptical arc (shorter counter-clockwise arc)
arcSmallCCWTo: func me.addSegment(me.VG_SCCWARC_TO_ABS, arg),
arcSmallCCW: func me.addSegment(me.VG_SCCWARC_TO_REL, arg),
# Draw an elliptical arc (shorter clockwise arc)
arcSmallCWTo: func me.addSegment(me.VG_SCWARC_TO_ABS, arg),
arcSmallCW: func me.addSegment(me.VG_SCWARC_TO_REL, arg),
# Draw an elliptical arc (longer counter-clockwise arc)
arcLargeCCWTo: func me.addSegment(me.VG_LCCWARC_TO_ABS, arg),
arcLargeCCW: func me.addSegment(me.VG_LCCWARC_TO_REL, arg),
# Draw an elliptical arc (shorter clockwise arc)
arcLargeCWTo: func me.addSegment(me.VG_LCWARC_TO_ABS, arg),
arcLargeCW: func me.addSegment(me.VG_LCWARC_TO_REL, arg),
# Close the path (implicit lineTo to first point of path)
close: func me.addSegment(me.VG_CLOSE_PATH),
# Add a (rounded) rectangle to the path
#
# @param x Position of left border
# @param y Position of top border
# @param w Width
# @param h Height
# @param cfg Optional settings (eg. {"border-top-radius": 5})
rect: func(x, y, w, h, cfg = nil)
{
var opts = (cfg != nil) ? cfg : {};
# resolve border-[top-,bottom-][left-,right-]radius
var br = opts["border-radius"];
if( typeof(br) == 'scalar' )
br = [br, br];
var _parseRadius = func(id)
{
if( (var r = opts["border-" ~ id ~ "-radius"]) == nil )
{
# parse top, bottom, left, right separate if no value specified for
# single corner
foreach(var s; ["top", "bottom", "left", "right"])
{
if( id.starts_with(s ~ "-") )
{
r = opts["border-" ~ s ~ "-radius"];
break;
}
}
}
if( r == nil )
return br;
else if( typeof(r) == 'scalar' )
return [r, r];
else
return r;
};
# top-left
if( (var r = _parseRadius("top-left")) != nil )
{
me.moveTo(x, y + r[1])
.arcSmallCWTo(r[0], r[1], 0, x + r[0], y);
}
else
me.moveTo(x, y);
# top-right
if( (r = _parseRadius("top-right")) != nil )
{
me.horizTo(x + w - r[0])
.arcSmallCWTo(r[0], r[1], 0, x + w, y + r[1]);
}
else
me.horizTo(x + w);
# bottom-right
if( (r = _parseRadius("bottom-right")) != nil )
{
me.vertTo(y + h - r[1])
.arcSmallCWTo(r[0], r[1], 0, x + w - r[0], y + h);
}
else
me.vertTo(y + h);
# bottom-left
if( (r = _parseRadius("bottom-left")) != nil )
{
me.horizTo(x + r[0])
.arcSmallCWTo(r[0], r[1], 0, x, y + h - r[1]);
}
else
me.horizTo(x);
return me.close();
},
setColor: func me.setStroke(_getColor(arg)),
setColorFill: func me.setFill(_getColor(arg)),
setFill: func(fill)
{
me.set('fill', fill);
},
setStroke: func(stroke)
{
me.set('stroke', stroke);
},
setStrokeLineWidth: func(width)
{
me.setDouble('stroke-width', width);
},
# Set stroke linecap
#
# @param linecap String, "butt", "round" or "square"
#
# See http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty for details
setStrokeLineCap: func(linecap)
{
me.set('stroke-linecap', linecap);
},
# Set stroke dasharray
#
# @param pattern Vector, Vector of alternating dash and gap lengths
# [on1, off1, on2, ...]
setStrokeDashArray: func(pattern)
{
if( typeof(pattern) == 'vector' )
me.set('stroke-dasharray', string.join(',', pattern));
else
debug.warn("setStrokeDashArray: vector expected!");
return me;
},
# private:
_removeSegment: func(front)
{
if( me.getNumSegments() < 1 )
{
debug.warn("No segment available");
return me;
}
var cmd = front ? me._first_cmd : me._last_cmd;
var num_coords = me.num_coords[ me.get("cmd[" ~ cmd ~ "]") ];
if( me.getNumCoords() < num_coords )
{
debug.warn("To few coords available");
}
me._node.removeChild("cmd", cmd);
var first_coord = front ? me._first_coord : me._last_coord - num_coords + 1;
for(var i = 0; i < num_coords; i += 1)
me._node.removeChild("coord", first_coord + i);
if( front )
{
me._first_cmd += 1;
me._first_coord += num_coords;
}
else
{
me._last_cmd -= 1;
me._last_coord -= num_coords;
}
return me;
},
};
# Image
# ==============================================================================
# Class for an image element on a canvas
#
var Image = {
new: func(ghost)
{
return {parents: [Image, Element.new(ghost)]};
},
# Set image file to be used
#
# @param file Path to file or canvas (Use canvas://... for canvas, eg.
# canvas://by-index/texture[0])
setFile: func(file)
{
me.set("file", file);
},
# Set rectangular region of source image to be used
#
# @param left Rectangle minimum x coordinate
# @param top Rectangle minimum y coordinate
# @param right Rectangle maximum x coordinate
# @param bottom Rectangle maximum y coordinate
# @param normalized Whether to use normalized ([0,1]) or image
# ([0, image_width]/[0, image_height]) coordinates
setSourceRect: func(left, top, right, bottom, normalized = 1)
{
me._node.getNode("source", 1).setValues({
left: left,
top: top,
right: right,
bottom: bottom,
normalized: normalized
});
return me;
},
# Set size of image element
#
# @param width
# @param height
# - or -
# @param size ([width, height])
setSize: func
{
me._node.setValues({size: _arg2valarray(arg)});
return me;
}
};
# Element factories used by #Group elements to create children
Group._element_factories = {
"group": Group.new,
"map": Map.new,
"text": Text.new,
"path": Path.new,
"image": Image.new
};
# Canvas
# ==============================================================================
# Class for a canvas
#
var Canvas = {
# Place this canvas somewhere onto the object. Pass criterions for placement
# as a hash, eg:
#
# my_canvas.addPlacement({
# "texture": "EICAS.png",
# "node": "PFD-Screen",
# "parent": "Some parent name"
# });
#
# Note that we can choose whichever of the three filter criterions we use for
# matching the target object for our placement. If none of the three fields is
# given every texture of the model will be replaced.
addPlacement: func(vals)
{
var placement = me.texture.addChild("placement", 0, 0);
placement.setValues(vals);
return placement;
},
# Create a new group with the given name
#
# @param id Optional id/name for the group
createGroup: func(id = nil)
{
return Group.new(me._createGroup(id));
},
# Get the group with the given name
getGroup: func(id)
{
return Group.new(me._getGroup(id));
},
# Set the background color
#
# @param color Vector of 3 or 4 values in [0, 1]
setColorBackground: func () { me.texture.getNode('background', 1).setValue(_getColor(arg)); me; },
# Get path of canvas to be used eg. in Image::setFile
getPath: func()
{
return "canvas://by-index/texture[" ~ me.texture.getIndex() ~ "]";
},
# Destructor
#
# releases associated canvas and makes this object unusable
del: func
{
me.texture.remove();
me.parents = nil; # ensure all ghosts get destroyed
}
};
var wrapCanvas = func(canvas_ghost)
{
var m = {
parents: [PropertyElement, Canvas, canvas_ghost],
texture: props.wrapNode(canvas_ghost._node_ghost)
};
m._node = m.texture;
return m;
}
# Create a new canvas. Pass parameters as hash, eg:
#
# var my_canvas = canvas.new({
# "name": "PFD-Test",
# "size": [512, 512],
# "view": [768, 1024],
# "mipmapping": 1
# });
var new = func(vals)
{
var m = wrapCanvas(_newCanvasGhost());
m.texture.setValues(vals);
return m;
};
# Get the first existing canvas with the given name
#
# @param name Name of the canvas
# @return #Canvas, if canvas with #name exists
# nil, otherwise
var get = func(arg)
{
if( isa(arg, props.Node) )
var node = arg;
else if( typeof(arg) == "hash" )
var node = props.Node.new(arg);
else
die("canvas.new: Invalid argument.");
var canvas_ghost = _getCanvasGhost(node._g);
if( canvas_ghost == nil )
return nil;
return wrapCanvas(canvas_ghost);
};
var getDesktop = func()
{
return Group.new(_getDesktopGhost());
};