1
0
Fork 0
fgdata/Nasal/canvas/draw/transform.nas
2020-03-08 21:02:44 +01:00

171 lines
6.6 KiB
Text

#
# canvas.transform library
# created 12/2018 by jsb
# based on plot2D.nas from the oscilloscope add-on by R. Leibner
#
# Contains functions to transform existing canvas elements.
var transform = {
_xy: func(elem, uv){
# returns [x, y]: intrinsic coords of the absolute(u, v)
var (tx, ty) = elem.getTranslation();
var (sx, sy) = elem.getScale();
return [(uv[0] - tx)/sx, (uv[1] - ty)/sy];
},
move: func(elem, dx, dy){
# moves the element <dx, dy> pixels position.
var (tx, ty) = elem.getTranslation();
elem.setTranslation(tx + dx, ty + dy);
},
rotate: func(elem, deg, center){
# rotates the element <deg> degrees around <center>.
var c = me._xy(elem, center);
elem.setCenter(c).setRotation(-deg * D2R);
},
flipX: func(elem, xaxis = 0) {
elem.updateCenter();
var (sx, sy) = elem.getScale();
var (tx, ty) = elem.getTranslation();
var (xmin, ymin, xmax, ymax) = elem.getTightBoundingBox();
if (xaxis == 0) {
xaxis = tx + sx*(xmax + xmin)/2;
}
elem.setScale(-sx, sy);
elem.setTranslation(2*xaxis - tx, ty);
return elem;
},
flipY: func(elem, yaxis = 0) {
elem.updateCenter();
var (sx, sy) = elem.getScale();
var (tx, ty) = elem.getTranslation();
var (xmin, ymin, xmax, ymax) = elem.getTightBoundingBox();
if (yaxis == 0) {
yaxis = ty + sy*(ymax + ymin)/2;
}
elem.setScale(sx, -sy);
elem.setTranslation(tx, 2*yaxis - ty);
return elem;
},
# Aligns the element, moving it horizontaly to ref.
# params:
# elem element to be moved.
# ref reference may be an integer or another element.
# alignment as string: may be 'left-left', 'left-center', 'left-right',
# 'center-left', 'center-center', 'center-right',
# 'right-left', 'right-center', 'right-right'.
# If ref is a single number, the 2nd word is ignored.
alignX: func(elem, ref, alignment) {
elem.updateCenter();
var (sx, sy) = elem.getScale();
var (tx, ty) = elem.getTranslation();
var (xmin, ymin, xmax, ymax) = elem.getTightBoundingBox();
var a = split('-', alignment)[0];
var x = a == 'left' ? xmin : a == 'right' ? Xmax : (xmin + xmax)/2;
if(typeof(ref) == 'scalar') var uRef = ref;
else {
ref.updateCenter();
var (sRx, sRy) = ref.getScale();
var (tRx, tRy) = ref.getTranslation();
var (xmin, ymin, xmax, ymax) = ref.getTightBoundingBox();
var aR = split('-', alignment)[1];
var uRef = aR =='left' ? tRx+sRx*xmin : aR =='right' ? tRx+sRx*xmax : tRx+sRx*(xmin+xmax)/2;
}
elem.setTranslation(uRef-x*sx, ty);
return elem;
},
# Aligns the element, moving it vertically to ref.
# params:
# elem element to be moved.
# ref reference may be an integer or another element.
# alignment as string: may be 'top-top', 'top-center', 'top-bottom',
# 'center-top', 'center-center', 'center-bottom',
# 'bottom-top', 'bottom-center', 'bottom-bottom'.
# text elements also accept 'baseline' as reference.
# If ref is a single number, the 2nd word is ignored.
alignY: func(elem, ref, alignment) {
elem.updateCenter();
var (sx, sy) = elem.getScale();
var (tx, ty) = elem.getTranslation();
var (Xmin, Ymin, Xmax, Ymax) = elem.getTightBoundingBox();
var a = split('-', alignment)[0];
var y = a == 'top' ? Ymin : a == 'bottom' ? Ymax : (Ymin+Ymax)/2;
if(typeof(ref) =='scalar') var vRef = ref;
else {
ref.updateCenter();
var (sRx, sRy) = ref.getScale();
var (tRx, tRy) = ref.getTranslation();
var (Xmin, Ymin, Xmax, Ymax) = ref.getTightBoundingBox();
var aR = split('-', alignment)[1];
var vRef = aR =='top' ? tRy+sRy*Ymin : aR =='bottom' ? tRy+sRy*Ymax : tRy+sRy*(Ymin+Ymax)/2;
}
elem.setTranslation(tx, vRef-y*sy);
return elem;
},
# center as [x,y] in pixels, otherwise in place
rotate180: func(elem, center = nil) {
if(center == nil){
me.flipX(elem);
me.flipY(elem);
}
else {
me.flipX(elem, center[0]);
me.flipY(elem, center[1]);
}
return elem;
},
# Stretch element horizontally
# params:
# elem element to be stretched.
# factor the <new-width>/<old-width> ratio.
# ref the relative point to keep inplace. May be 'left', 'center' or 'right'.
scaleX: func(elem, factor, ref = 'left') {
elem.updateCenter();
var (sx, sy) = elem.getScale();
var (tx, ty) = elem.getTranslation();
var (xmin, ymin, xmax, ymax) = elem.getTightBoundingBox();
var x = (ref == 'left') ? xmin : (ref == 'right') ? xmax : (xmin + xmax)/2;
var u = tx + x*sx;
print("scaleX: "~factor~"; sx="~sx~" sy="~sy~" tx="~tx~" ty="~ty,
sprintf(" BB %1.3e, %1.3e, %1.3e, %1.3e, ", xmin, ymin, xmax ,ymax),
" u="~u);
elem.setScale(sx*factor, sy);
elem.setTranslation(u-x*sx*factor, ty);
return elem;
},
# strech element vertically
# params:
# elem element to be stretched.
# factor the <new-height>/<old-height> ratio.
# ref the relative point to keep inplace. May be 'top', 'center' or 'bottom'.
scaleY: func(elem, factor, ref = 'top') {
elem.updateCenter();
var (sx, sy) = elem.getScale();
var (tx, ty) = elem.getTranslation();
var (xmin, ymin, xmax, ymax) = elem.getTightBoundingBox();
var y = (ref =='top') ? ymin : (ref == 'bottom') ? ymax : (ymin + ymax)/2;
var v = ty + y*sy;
elem.setScale(sx, sy*factor);
elem.setTranslation(tx, v-y*sy*factor);
return elem;
},
# factors as [Xfactor, Yfactor] .
# ref the relative point to keep inplace:
# may be 'left-top', 'left-center', 'left-bottom',
# 'center-top', 'center-center', 'center-bottom',
# 'right-top', 'right-center', 'right-bottom'.
resize: func(elem, factors, ref = 'left-top') {
me.scaleX(elem, factors[0], split('-', ref)[0]);
me.scaleY(elem, factors[1], split('-', ref)[1]);
return elem;
},
};