2 var _getColor = func(color)
7 if( typeof(color) == 'scalar' )
9 if( typeof(color) != "vector" )
10 return debug.warn("Wrong type for color");
12 if( size(color) < 3 or size(color) > 4 )
13 return debug.warn("Color needs 3 or 4 values (RGB or RGBA)");
16 if( size(color) == 4 )
20 # rgb = [0,255], a = [0,1]
21 for(var i = 0; i < size(color); i += 1)
22 str ~= (i > 0 ? ',' : '') ~ (i < 3 ? int(color[i] * 255) : color[i]);
27 var _arg2valarray = func
30 while ( typeof(ret) == "vector"
31 and size(ret) == 1 and typeof(ret[0]) == "vector" )
37 # ==============================================================================
38 # A transformation matrix which is used to transform an #Element on the canvas.
39 # The dimensions of the matrix are 3x3 where the last row is always 0 0 1:
45 # See http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined for details.
48 new: func(node, vals = nil)
53 a: node.getNode("m[0]", 1),
54 b: node.getNode("m[1]", 1),
55 c: node.getNode("m[2]", 1),
56 d: node.getNode("m[3]", 1),
57 e: node.getNode("m[4]", 1),
58 f: node.getNode("m[5]", 1)
61 var use_vals = typeof(vals) == 'vector' and size(vals) == 6;
63 # initialize to identity matrix
64 m.a.setDoubleValue(use_vals ? vals[0] : 1);
65 m.b.setDoubleValue(use_vals ? vals[1] : 0);
66 m.c.setDoubleValue(use_vals ? vals[2] : 0);
67 m.d.setDoubleValue(use_vals ? vals[3] : 1);
68 m.e.setDoubleValue(use_vals ? vals[4] : 0);
69 m.f.setDoubleValue(use_vals ? vals[5] : 0);
75 var trans = _arg2valarray(arg);
77 me.e.setDoubleValue(trans[0]);
78 me.f.setDoubleValue(trans[1]);
82 # Set rotation (Optionally around a specified point instead of (0,0))
85 # setRotation(rot, cx, cy)
87 # @note If using with rotation center different to (0,0) don't use
88 # #setTranslation as it would interfere with the rotation.
89 setRotation: func(angle)
91 var center = _arg2valarray(arg);
93 var s = math.sin(angle);
94 var c = math.cos(angle);
96 me.a.setDoubleValue(c);
97 me.b.setDoubleValue(s);
98 me.c.setDoubleValue(-s);
99 me.d.setDoubleValue(c);
101 if( size(center) == 2 )
103 me.e.setDoubleValue( (-center[0] * c) + (center[1] * s) + center[0] );
104 me.f.setDoubleValue( (-center[0] * s) - (center[1] * c) + center[1] );
109 # Set scale (either as parameters or array)
111 # If only one parameter is given its value is used for both x and y
116 var scale = _arg2valarray(arg);
118 me.a.setDoubleValue(scale[0]);
119 me.d.setDoubleValue(size(scale) >= 2 ? scale[1] : scale[0]);
125 # TODO handle rotation
126 return [me.a.getValue(), me.d.getValue()];
131 # ==============================================================================
132 # Baseclass for all elements on a canvas
135 # Reference frames (for "clip" coordinates)
142 # @param ghost Element ghost as retrieved from core methods
146 parents: [PropertyElement, Element, ghost],
147 _node: props.wrapNode(ghost._node_ghost)
150 # Get parent group/element
153 var parent_ghost = me._getParent();
154 if( parent_ghost == nil )
157 var type = props.wrapNode(parent_ghost._node_ghost).getName();
158 var factory = me._getFactory(type);
162 return factory(parent_ghost);
164 # Get the canvas this element is placed on
167 wrapCanvas(me._getCanvas());
169 # Check if elements represent same instance
171 # @param el Other Element or element ghost
174 return me._node.equals(el._node_ghost);
178 # @param visible Whether the element should be visible
179 setVisible: func(visible = 1)
181 me.setBool("visible", visible);
183 getVisible: func me.getBool("visible"),
184 # Hide element (Shortcut for setVisible(0))
185 hide: func me.setVisible(0),
186 # Show element (Shortcut for setVisible(1))
187 show: func me.setVisible(1),
188 # Toggle element visibility
189 toggleVisibility: func me.setVisible( !me.getVisible() ),
191 setGeoPosition: func(lat, lon)
193 me._getTf()._node.getNode("m-geo[4]", 1).setValue("N" ~ lat);
194 me._getTf()._node.getNode("m-geo[5]", 1).setValue("E" ~ lon);
197 # Create a new transformation matrix
199 # @param vals Default values (Vector of 6 elements)
200 createTransform: func(vals = nil)
202 var node = me._node.addChild("tf", 1); # tf[0] is reserved for
204 return Transform.new(node, vals);
206 # Shortcut for setting translation
207 setTranslation: func { me._getTf().setTranslation(arg); return me; },
208 # Get translation set with #setTranslation
209 getTranslation: func()
211 if( me['_tf'] == nil )
214 return [me._tf.e.getValue(), me._tf.f.getValue()];
216 # Set rotation around transformation center (see #setCenter).
218 # @note This replaces the the existing transformation. For additional scale or
219 # translation use additional transforms (see #createTransform).
220 setRotation: func(rot)
222 if( me['_tf_rot'] == nil )
223 # always use the first matrix slot to ensure correct rotation
224 # around transformation center.
225 # tf-rot-index can be set to change the slot to be used. This is used for
226 # example by the SVG parser to apply the rotation after all
227 # transformations defined in the SVG file.
228 me['_tf_rot'] = Transform.new(
229 me._node.getNode("tf[" ~ me.get("tf-rot-index", 0) ~ "]", 1)
232 me._tf_rot.setRotation(rot, me.getCenter());
235 # Shortcut for setting scale
236 setScale: func { me._getTf().setScale(arg); return me; },
237 # Shortcut for getting scale
238 getScale: func me._getTf().getScale(),
239 # Set the fill/background/boundingbox color
241 # @param color Vector of 3 or 4 values in [0, 1]
242 setColorFill: func me.set('fill', _getColor(arg)),
243 getColorFill: func me.get('fill'),
245 getTransformedBounds: func me.getTightBoundingBox(),
246 # Calculate the transformation center based on bounding box and center-offset
250 var bb = me.getTightBoundingBox();
252 if( bb[0] > bb[2] or bb[1] > bb[3] )
257 (bb[0] + bb[2]) / 2 + (me.get("center-offset-x") or 0),
258 (bb[1] + bb[3]) / 2 + (me.get("center-offset-y") or 0)
262 # Set transformation center (currently only used for rotation)
265 var center = _arg2valarray(arg);
266 if( size(center) != 2 )
267 return debug.warn("invalid arg");
269 me._setupCenterNodes(center[0], center[1]);
272 # Get transformation center
276 me._setupCenterNodes();
278 if( me._center[0] != nil )
279 center[0] = me._center[0].getValue() or 0;
280 if( me._center[1] != nil )
281 center[1] = me._center[1].getValue() or 0;
285 # Internal Transform for convenience transform functions
288 if( me['_tf'] == nil )
289 me['_tf'] = me.createTransform();
292 _setupCenterNodes: func(cx = nil, cy = nil)
294 if( me["_center"] == nil )
296 me._node.getNode("center[0]", cx != nil),
297 me._node.getNode("center[1]", cy != nil)
301 me._center[0].setDoubleValue(cx);
303 me._center[1].setDoubleValue(cy);
308 # ==============================================================================
309 # Class for a group element on a canvas
315 return { parents: [Group, Element.new(ghost)] };
317 # Create a child of given type with specified id.
318 # type can be group, text
319 createChild: func(type, id = nil)
321 var ghost = me._createChild(type, id);
322 var factory = me._getFactory(type);
326 return factory(ghost);
328 # Create multiple children of given type
329 createChildren: func(type, count)
331 var factory = me._getFactory(type);
335 var nodes = props._addChildren(me._node._g, [type, count, 0, 0]);
336 for(var i = 0; i < count; i += 1)
337 nodes[i] = factory( me._getChild(nodes[i]) );
341 # Create a path child drawing a (rounded) rectangle
343 # @param x Position of left border
344 # @param y Position of top border
347 # @param cfg Optional settings (eg. {"border-top-radius": 5})
348 rect: func(x, y, w, h, cfg = nil)
350 return me.createChild("path").rect(x, y, w, h, cfg);
352 # Get a vector of all child elements
357 foreach(var c; me._node.getChildren())
358 if( me._isElementNode(c) )
359 append(children, me._wrapElement(c));
363 # Get first child with given id (breadth-first search)
365 # @note Use with care as it can take several miliseconds (for me eg. ~2ms).
366 # TODO check with new C++ implementation
367 getElementById: func(id)
369 var ghost = me._getElementById(id);
373 var node = props.wrapNode(ghost._node_ghost);
374 var factory = me._getFactory( node.getName() );
378 return factory(ghost);
380 # Remove all children
381 removeAllChildren: func()
383 foreach(var type; keys(me._element_factories))
384 me._node.removeChildren(type, 0);
388 _isElementNode: func(el)
390 # element nodes have type NONE and valid element names (those in the factory
392 return el.getType() == "NONE"
393 and me._element_factories[ el.getName() ] != nil;
395 _wrapElement: func(node)
397 # Create element from existing node
398 return me._element_factories[ node.getName() ]( me._getChild(node._g) );
400 _getFactory: func(type)
402 var factory = me._element_factories[type];
405 debug.dump("canvas.Group.createChild(): unknown type (" ~ type ~ ")");
412 # ==============================================================================
413 # Class for a group element on a canvas with possibly geopgraphic positions
414 # which automatically get projected according to the specified projection.
415 # Each map consists of an arbitrary number of layers (canvas groups)
421 return { parents: [Map, Group.new(ghost)], layers:{}, controller:nil }.setController();
425 #print("canvas.Map.del()");
426 if (me.controller != nil)
427 me.controller.del(me);
428 foreach (var k; keys(me.layers)) {
430 delete(me.layers, k);
432 # call inherited 'del'
433 me.parents = subvec(me.parents,1);
436 setController: func(controller=nil, arg...)
438 if (me.controller != nil) me.controller.del(me);
439 if (controller == nil)
440 controller = Map.df_controller;
441 elsif (typeof(controller) != 'hash')
442 controller = Map.Controller.get(controller);
444 if (controller == nil) {
447 if (!isa(controller, Map.Controller))
448 die("OOP error: controller needs to inherit from Map.Controller");
449 me.controller = call(controller.new, [me]~arg, controller, var err=[]); # try...
451 if (err[0] != "No such member: new") # ... and either catch or rethrow
454 me.controller = controller;
455 } elsif (me.controller == nil) {
456 me.controller = controller;
457 } elsif (me.controller != controller and !isa(me.controller, controller))
458 die("OOP error: created instance needs to inherit from or be the specific controller class");
463 addLayer: func(factory, type_arg=nil, priority=nil, style=nil, options=nil, visible=1)
465 if(contains(me.layers, type_arg))
466 printlog("warn", "addLayer() warning: overwriting existing layer:", type_arg);
469 if (type_arg != nil) {
470 var layer = factory.new(type:type_arg, group:me, map:me, style:style, options:options, visible:visible);
471 var type = factory.get(type_arg);
474 var layer = factory.new(group:me, map:me, style:style, options:options, visible:visible);
476 var key = factory.type;
478 me.layers[type_arg] = layer;
481 priority = type.df_priority;
483 layer.group.setInt("z-index", priority);
485 return layer; # return new layer to caller() so that we can directly work with it, i.e. to register event handlers (panning/zooming)
487 getLayer: func(type_arg) me.layers[type_arg],
489 setRange: func(range) me.set("range",range),
490 getRange: func me.get('range'),
492 setPos: func(lat, lon, hdg=nil, range=nil, alt=nil)
494 # TODO: also propage setPos events to layers and symbols (e.g. for offset maps)
495 me.set("ref-lat", lat);
496 me.set("ref-lon", lon);
502 me.set("altitude", alt);
506 return [me.get("ref-lat"),
512 getLat: func me.get("ref-lat"),
513 getLon: func me.get("ref-lon"),
514 getHdg: func me.get("hdg"),
515 getAlt: func me.get("altitude"),
516 getRange: func me.get("range"),
517 getLatLon: func [me.get("ref-lat"), me.get("ref-lon")],
518 # N.B.: This always returns the same geo.Coord object,
519 # so its values can and will change at any time (call
520 # update() on the coord to ensure it is up-to-date,
521 # which basically calls this method again).
524 var (lat, lon) = (me.get("ref-lat"),
526 var alt = me.get("altitude");
527 if (lat == nil or lon == nil) {
528 if (contains(me, "coord")) {
529 debug.warn("canvas.Map: lost ref-lat and/or ref-lon source");
533 if (!contains(me, "coord")) {
534 me.coord = geo.Coord.new();
536 me.coord.update = func m.getPosCoord();
538 me.coord.set_latlon(lat,lon,alt or 0);
541 # Update each layer on this Map. Called by
543 update: func(predicate=nil)
546 foreach (var l; keys(me.layers)) {
547 var layer = me.layers[l];
548 # Only update if the predicate allows
549 if (predicate == nil or predicate(layer))
552 printlog(_MP_dbg_lvl, "Took "~((systime()-t)*1000)~"ms to update map()");
553 me.setBool("update", 1); # update any coordinates that changed, to avoid floating labels etc.
559 # ==============================================================================
560 # Class for a text element on a canvas
565 return { parents: [Text, Element.new(ghost)] };
570 me.set("text", typeof(text) == 'scalar' ? text : "");
572 appendText: func(text)
574 me.set("text", (me.get("text") or "") ~ (typeof(text) == 'scalar' ? text : ""));
578 # @param align String, one of:
591 # left-bottom-baseline
592 # center-bottom-baseline
593 # right-bottom-baseline
595 setAlignment: func(align)
597 me.set("alignment", align);
600 setFontSize: func(size, aspect = 1)
602 me.setDouble("character-size", size);
603 me.setDouble("character-aspect-ratio", aspect);
605 # Set font (by name of font file)
608 me.set("font", name);
610 # Enumeration of values for drawing mode:
611 TEXT: 0x01, # The text itself
612 BOUNDINGBOX: 0x02, # A bounding box (only lines)
613 FILLEDBOUNDINGBOX: 0x04, # A filled bounding box
614 ALIGNMENT: 0x08, # Draw a marker (cross) at the position of the text
615 # Set draw mode. Binary combination of the values above. Since I haven't found
616 # a bitwise or we have to use a + instead.
618 # eg. my_text.setDrawMode(Text.TEXT + Text.BOUNDINGBOX);
619 setDrawMode: func(mode)
621 me.setInt("draw-mode", mode);
623 # Set bounding box padding
624 setPadding: func(pad)
626 me.setDouble("padding", pad);
630 me.setDouble("max-width", w);
632 setColor: func me.set('fill', _getColor(arg)),
633 getColor: func me.get('fill'),
635 setColorFill: func me.set('background', _getColor(arg)),
636 getColorFill: func me.get('background'),
640 # ==============================================================================
641 # Class for an (OpenVG) path element on a canvas
644 # Path segment commands (VGPathCommand)
668 VG_SCUBIC_TO_ABS: 16,
669 VG_SCUBIC_TO_REL: 17,
670 VG_SCCWARC_TO: 20, # Note that CC and CCW commands are swapped. This is
671 VG_SCCWARC_TO_ABS:20, # needed due to the different coordinate systems used.
672 VG_SCCWARC_TO_REL:21, # In OpenVG values along the y-axis increase from bottom
673 VG_SCWARC_TO: 18, # to top, whereas in the Canvas system it is flipped.
674 VG_SCWARC_TO_ABS: 18,
675 VG_SCWARC_TO_REL: 19,
677 VG_LCCWARC_TO_ABS:24,
678 VG_LCCWARC_TO_REL:25,
680 VG_LCWARC_TO_ABS: 22,
681 VG_LCWARC_TO_REL: 23,
683 # Number of coordinates per command
685 0, 0, # VG_CLOSE_PATH
694 5, 5, # VG_SCCWARC_TO
696 5, 5, # VG_LCCWARC_TO
704 parents: [Path, Element.new(ghost)],
711 # Remove all existing path data
714 me._node.removeChildren('cmd', 0);
715 me._node.removeChildren('coord', 0);
716 me._node.removeChildren('coord-geo', 0);
723 # Set the path data (commands and coordinates)
724 setData: func(cmds, coords)
727 me._node.setValues({cmd: cmds, coord: coords});
728 me._last_cmd = size(cmds) - 1;
729 me._last_coord = size(coords) - 1;
732 setDataGeo: func(cmds, coords)
735 me._node.setValues({cmd: cmds, 'coord-geo': coords});
736 me._last_cmd = size(cmds) - 1;
737 me._last_coord = size(coords) - 1;
741 addSegment: func(cmd, coords...)
743 var coords = _arg2valarray(coords);
744 var num_coords = me.num_coords[cmd];
745 if( size(coords) != num_coords )
748 "Invalid number of arguments (expected " ~ num_coords ~ ")"
752 me.setInt("cmd[" ~ (me._last_cmd += 1) ~ "]", cmd);
753 for(var i = 0; i < num_coords; i += 1)
754 me.setDouble("coord[" ~ (me._last_coord += 1) ~ "]", coords[i]);
759 addSegmentGeo: func(cmd, coords...)
761 var coords = _arg2valarray(coords);
762 var num_coords = me.num_coords[cmd];
763 if( size(coords) != num_coords )
766 "Invalid number of arguments (expected " ~ num_coords ~ ")"
770 me.setInt("cmd[" ~ (me._last_cmd += 1) ~ "]", cmd);
771 for(var i = 0; i < num_coords; i += 1)
772 me.set("coord-geo[" ~ (me._last_coord += 1) ~ "]", coords[i]);
777 # Remove first segment
778 pop_front: func me._removeSegment(1),
779 # Remove last segment
780 pop_back: func me._removeSegment(0),
781 # Get the number of segments
782 getNumSegments: func()
784 return me._last_cmd - me._first_cmd + 1;
786 # Get the number of coordinates (each command has 0..n coords)
789 return me._last_coord - me._first_coord + 1;
792 moveTo: func me.addSegment(me.VG_MOVE_TO_ABS, arg),
793 move: func me.addSegment(me.VG_MOVE_TO_REL, arg),
795 lineTo: func me.addSegment(me.VG_LINE_TO_ABS, arg),
796 line: func me.addSegment(me.VG_LINE_TO_REL, arg),
797 # Add a horizontal line
798 horizTo: func me.addSegment(me.VG_HLINE_TO_ABS, arg),
799 horiz: func me.addSegment(me.VG_HLINE_TO_REL, arg),
800 # Add a vertical line
801 vertTo: func me.addSegment(me.VG_VLINE_TO_ABS, arg),
802 vert: func me.addSegment(me.VG_VLINE_TO_REL, arg),
803 # Add a quadratic Bézier curve
804 quadTo: func me.addSegment(me.VG_QUAD_TO_ABS, arg),
805 quad: func me.addSegment(me.VG_QUAD_TO_REL, arg),
806 # Add a cubic Bézier curve
807 cubicTo: func me.addSegment(me.VG_CUBIC_TO_ABS, arg),
808 cubic: func me.addSegment(me.VG_CUBIC_TO_REL, arg),
809 # Add a smooth quadratic Bézier curve
810 squadTo: func me.addSegment(me.VG_SQUAD_TO_ABS, arg),
811 squad: func me.addSegment(me.VG_SQUAD_TO_REL, arg),
812 # Add a smooth cubic Bézier curve
813 scubicTo: func me.addSegment(me.VG_SCUBIC_TO_ABS, arg),
814 scubic: func me.addSegment(me.VG_SCUBIC_TO_REL, arg),
815 # Draw an elliptical arc (shorter counter-clockwise arc)
816 arcSmallCCWTo: func me.addSegment(me.VG_SCCWARC_TO_ABS, arg),
817 arcSmallCCW: func me.addSegment(me.VG_SCCWARC_TO_REL, arg),
818 # Draw an elliptical arc (shorter clockwise arc)
819 arcSmallCWTo: func me.addSegment(me.VG_SCWARC_TO_ABS, arg),
820 arcSmallCW: func me.addSegment(me.VG_SCWARC_TO_REL, arg),
821 # Draw an elliptical arc (longer counter-clockwise arc)
822 arcLargeCCWTo: func me.addSegment(me.VG_LCCWARC_TO_ABS, arg),
823 arcLargeCCW: func me.addSegment(me.VG_LCCWARC_TO_REL, arg),
824 # Draw an elliptical arc (shorter clockwise arc)
825 arcLargeCWTo: func me.addSegment(me.VG_LCWARC_TO_ABS, arg),
826 arcLargeCW: func me.addSegment(me.VG_LCWARC_TO_REL, arg),
827 # Close the path (implicit lineTo to first point of path)
828 close: func me.addSegment(me.VG_CLOSE_PATH),
830 # Add a (rounded) rectangle to the path
832 # @param x Position of left border
833 # @param y Position of top border
836 # @param cfg Optional settings (eg. {"border-top-radius": 5})
837 rect: func(x, y, w, h, cfg = nil)
839 var opts = (cfg != nil) ? cfg : {};
841 # resolve border-[top-,bottom-][left-,right-]radius
842 var br = opts["border-radius"];
843 if( typeof(br) == 'scalar' )
846 var _parseRadius = func(id)
848 if( (var r = opts["border-" ~ id ~ "-radius"]) == nil )
850 # parse top, bottom, left, right separate if no value specified for
852 foreach(var s; ["top", "bottom", "left", "right"])
854 if( id.starts_with(s ~ "-") )
856 r = opts["border-" ~ s ~ "-radius"];
864 else if( typeof(r) == 'scalar' )
871 if( (var r = _parseRadius("top-left")) != nil )
873 me.moveTo(x, y + r[1])
874 .arcSmallCWTo(r[0], r[1], 0, x + r[0], y);
880 if( (r = _parseRadius("top-right")) != nil )
882 me.horizTo(x + w - r[0])
883 .arcSmallCWTo(r[0], r[1], 0, x + w, y + r[1]);
889 if( (r = _parseRadius("bottom-right")) != nil )
891 me.vertTo(y + h - r[1])
892 .arcSmallCWTo(r[0], r[1], 0, x + w - r[0], y + h);
898 if( (r = _parseRadius("bottom-left")) != nil )
901 .arcSmallCWTo(r[0], r[1], 0, x, y + h - r[1]);
909 setColor: func me.setStroke(_getColor(arg)),
910 getColor: func me.getStroke(),
912 setColorFill: func me.setFill(_getColor(arg)),
913 getColorFill: func me.getColorFill(),
916 me.set('fill', fill);
918 setStroke: func(stroke)
920 me.set('stroke', stroke);
922 getStroke: func me.get('stroke'),
924 setStrokeLineWidth: func(width)
926 me.setDouble('stroke-width', width);
930 # @param linecap String, "butt", "round" or "square"
932 # See http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty for details
933 setStrokeLineCap: func(linecap)
935 me.set('stroke-linecap', linecap);
937 # Set stroke linejoin
939 # @param linejoin String, "miter", "round" or "bevel"
941 # See http://www.w3.org/TR/SVG/painting.html#StrokeLinejoinProperty for details
942 setStrokeLineJoin: func(linejoin)
944 me.set('stroke-linejoin', linejoin);
946 # Set stroke dasharray
947 # Set stroke dasharray
949 # @param pattern Vector, Vector of alternating dash and gap lengths
950 # [on1, off1, on2, ...]
951 setStrokeDashArray: func(pattern)
953 if( typeof(pattern) == 'vector' )
954 me.set('stroke-dasharray', string.join(',', pattern));
956 debug.warn("setStrokeDashArray: vector expected!");
962 _removeSegment: func(front)
964 if( me.getNumSegments() < 1 )
966 debug.warn("No segment available");
970 var cmd = front ? me._first_cmd : me._last_cmd;
971 var num_coords = me.num_coords[ me.get("cmd[" ~ cmd ~ "]") ];
972 if( me.getNumCoords() < num_coords )
974 debug.warn("To few coords available");
977 me._node.removeChild("cmd", cmd);
979 var first_coord = front ? me._first_coord : me._last_coord - num_coords + 1;
980 for(var i = 0; i < num_coords; i += 1)
981 me._node.removeChild("coord", first_coord + i);
986 me._first_coord += num_coords;
991 me._last_coord -= num_coords;
999 # ==============================================================================
1000 # Class for an image element on a canvas
1005 return {parents: [Image, Element.new(ghost)]};
1007 # Set image file to be used
1009 # @param file Path to file or canvas (Use canvas://... for canvas, eg.
1010 # canvas://by-index/texture[0])
1013 me.set("src", file);
1015 # Set rectangular region of source image to be used
1017 # @param left Rectangle minimum x coordinate
1018 # @param top Rectangle minimum y coordinate
1019 # @param right Rectangle maximum x coordinate
1020 # @param bottom Rectangle maximum y coordinate
1021 # @param normalized Whether to use normalized ([0,1]) or image
1022 # ([0, image_width]/[0, image_height]) coordinates
1025 # Work with both positional arguments and named arguments.
1026 # Support first argument being a vector instead of four separate ones.
1029 elsif (size(arg) and size(arg) < 4 and typeof(arg[0]) == 'vector')
1030 arg = arg[0]~arg[1:];
1031 if (!contains(caller(0)[0], "normalized")) {
1033 var normalized = arg[4];
1034 else var normalized = 1;
1037 var (left,top,right,bottom) = arg;
1039 me._node.getNode("source", 1).setValues({
1044 normalized: normalized
1048 # Set size of image element
1053 # @param size ([width, height])
1056 me._node.setValues({size: _arg2valarray(arg)});
1061 # Element factories used by #Group elements to create children
1062 Group._element_factories = {
1071 # ==============================================================================
1072 # Class for a canvas
1075 # Place this canvas somewhere onto the object. Pass criterions for placement
1078 # my_canvas.addPlacement({
1079 # "texture": "EICAS.png",
1080 # "node": "PFD-Screen",
1081 # "parent": "Some parent name"
1084 # Note that we can choose whichever of the three filter criterions we use for
1085 # matching the target object for our placement. If none of the three fields is
1086 # given every texture of the model will be replaced.
1087 addPlacement: func(vals)
1089 var placement = me._node.addChild("placement", 0, 0);
1090 placement.setValues(vals);
1093 # Create a new group with the given name
1095 # @param id Optional id/name for the group
1096 createGroup: func(id = nil)
1098 return Group.new(me._createGroup(id));
1100 # Get the group with the given name
1103 return Group.new(me._getGroup(id));
1105 # Set the background color
1107 # @param color Vector of 3 or 4 values in [0, 1]
1108 setColorBackground: func me.set('background', _getColor(arg)),
1109 getColorBackground: func me.get('background'),
1110 # Get path of canvas to be used eg. in Image::setFile
1113 return "canvas://by-index/texture[" ~ me._node.getIndex() ~ "]";
1117 # releases associated canvas and makes this object unusable
1121 me.parents = nil; # ensure all ghosts get destroyed
1125 # @param g Canvas ghost
1126 var wrapCanvas = func(g)
1128 if( g != nil and g._impl == nil )
1130 parents: [PropertyElement, Canvas],
1131 _node: props.wrapNode(g._node_ghost)
1136 # Create a new canvas. Pass parameters as hash, eg:
1138 # var my_canvas = canvas.new({
1139 # "name": "PFD-Test",
1140 # "size": [512, 512],
1141 # "view": [768, 1024],
1144 var new = func(vals)
1146 var m = wrapCanvas(_newCanvasGhost());
1147 m._node.setValues(vals);
1151 # Get the first existing canvas with the given name
1153 # @param name Name of the canvas
1154 # @return #Canvas, if canvas with #name exists
1158 if( isa(arg, props.Node) )
1160 else if( typeof(arg) == "hash" )
1161 var node = props.Node.new(arg);
1163 die("canvas.new: Invalid argument.");
1165 return wrapCanvas(_getCanvasGhost(node._g));
1168 var getDesktop = func()
1170 return Group.new(_getDesktopGhost());