172 lines
6.6 KiB
Text
172 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;
|
||
|
},
|
||
|
};
|