# # 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 pixels position. var (tx, ty) = elem.getTranslation(); elem.setTranslation(tx + dx, ty + dy); }, rotate: func(elem, deg, center){ # rotates the element degrees around
. 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 / 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 / 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; }, };