1
0
Fork 0
fgdata/Nasal/canvas/api/element.nas
2020-04-30 08:59:25 +01:00

229 lines
6.4 KiB
Text

#-------------------------------------------------------------------------------
# canvas.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);
},
# Get the canvas this element is placed on
getCanvas: func() {
wrapCanvas(me._getCanvas());
},
# Check if elements represent same instance
#
# @param el Other Element or element ghost
equals: func(el) {
return me._node.equals(el._node_ghost);
},
# 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) {
# tf[0] is reserved for setRotation, so min. index 1 is used here
var node = me._node.addChild("tf", 1);
return Transform.new(node, vals);
},
# Shortcut for setting translation
setTranslation: func {
me._getTf().setTranslation(arg);
return me;
},
# Get translation set with #setTranslation
getTranslation: func() {
if (me["_tf"] == nil)
return [0, 0];
return [me._tf.e.getValue(), me._tf.f.getValue()];
},
# 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.
# tf-rot-index can be set to change the slot to be used. This is used for
# example by the SVG parser to apply the rotation after all
# transformations defined in the SVG file.
me["_tf_rot"] = Transform.new(
me._node.getNode("tf["~me.get("tf-rot-index", 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));
},
getColorFill: func {
me.get("fill");
},
getTransformedBounds: func {
me.getTightBoundingBox();
},
# Calculate the transformation center based on bounding box and center-offset
updateCenter: func {
me.update();
var bb = me.getTightBoundingBox();
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;
},
getSize: func {
var bb = me.getTightBoundingBox();
return [bb[2] - bb[0], bb[3] - bb[1]];
},
# convert bounding box vector into clip string (yes, different order)
boundingbox2clip: func(bb) {
return sprintf("rect(%d,%d,%d,%d)", bb[1], bb[2], bb[3], bb[0])
},
# set clip by bounding box
# bounding_box: [xmin, ymin, xmax, ymax]
setClipByBoundingBox: func(bounding_box, clip_frame = nil) {
if (clip_frame == nil) { clip_frame = Element.PARENT; }
me.set("clip", me.boundingbox2clip(bounding_box));
me.set("clip-frame", clip_frame);
return me;
},
# set clipping by bounding box of another element
setClipByElement: func(clip_elem) {
clip_elem.update();
var bounds = clip_elem.getTightBoundingBox();
me.setClipByBoundingBox(bounds, canvas.Element.PARENT);
},
# 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);
}
},
};