Library globals

Source canvas . draw . transform.nas

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
#
# 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;
    },
};