1
0
Fork 0
fgdata/Nasal/canvas/api/element.nas
2023-04-17 15:13:18 +01:00

251 lines
7 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) {
var obj = {
parents: [Element, PropertyElement, ghost],
_node: props.wrapNode(ghost._node_ghost),
};
return obj;
},
getType: func () {
return me._node.getName();
},
# 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 = Group._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);
return me;
},
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().setGeoPosition(lat, 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.getTranslation();
},
# 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;
},
# Set size in pixels
# Use either with x, y size:
# e.setSize(<x>, <y>)
# or with a vector containing x, y:
# e.setSize([<x>, <y>])
setSize: func {
if (size(arg) == 1) {
var (x, y) = arg[0];
} else {
var (x, y) = arg;
}
var (sx, sy) = me.getScale();
var (curx, cury) = me.getSize();
me.setScale(x / (curx / sy), y / (cury / sy));
},
#return vector [sx, sy] with dimensions of bounding box
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 or (size(me._center) >= 1 and me._center[0] == nil) or (size(me._center) >= 2 and me._center[1] == 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);
}
},
};