source: Dev/branches/rest-dojo-ui/client/dojox/gfx/decompose.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 4.6 KB
Line 
1define(["./_base", "dojo/_base/lang", "./matrix"],
2  function (g, lang, m){
3        /*===== g = dojox.gfx =====*/
4        function eq(/* Number */ a, /* Number */ b){
5                // summary: compare two FP numbers for equality
6                return Math.abs(a - b) <= 1e-6 * (Math.abs(a) + Math.abs(b));   // Boolean
7        }
8
9        function calcFromValues(/* Number */ r1, /* Number */ m1, /* Number */ r2, /* Number */ m2){
10                // summary: uses two close FP ration and their original magnitudes to approximate the result
11                if(!isFinite(r1)){
12                        return r2;      // Number
13                }else if(!isFinite(r2)){
14                        return r1;      // Number
15                }
16                m1 = Math.abs(m1); m2 = Math.abs(m2);
17                return (m1 * r1 + m2 * r2) / (m1 + m2); // Number
18        }
19
20        function transpose(/* dojox.gfx.matrix.Matrix2D */ matrix){
21                // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object
22                var M = new m.Matrix2D(matrix);
23                return lang.mixin(M, {dx: 0, dy: 0, xy: M.yx, yx: M.xy});       // dojox.gfx.matrix.Matrix2D
24        }
25
26        function scaleSign(/* dojox.gfx.matrix.Matrix2D */ matrix){
27                return (matrix.xx * matrix.yy < 0 || matrix.xy * matrix.yx > 0) ? -1 : 1;       // Number
28        }
29
30        function eigenvalueDecomposition(/* dojox.gfx.matrix.Matrix2D */ matrix){
31                // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object
32                var M = m.normalize(matrix),
33                        b = -M.xx - M.yy,
34                        c = M.xx * M.yy - M.xy * M.yx,
35                        d = Math.sqrt(b * b - 4 * c),
36                        l1 = -(b + (b < 0 ? -d : d)) / 2,
37                        l2 = c / l1,
38                        vx1 = M.xy / (l1 - M.xx), vy1 = 1,
39                        vx2 = M.xy / (l2 - M.xx), vy2 = 1;
40                if(eq(l1, l2)){
41                        vx1 = 1, vy1 = 0, vx2 = 0, vy2 = 1;
42                }
43                if(!isFinite(vx1)){
44                        vx1 = 1, vy1 = (l1 - M.xx) / M.xy;
45                        if(!isFinite(vy1)){
46                                vx1 = (l1 - M.yy) / M.yx, vy1 = 1;
47                                if(!isFinite(vx1)){
48                                        vx1 = 1, vy1 = M.yx / (l1 - M.yy);
49                                }
50                        }
51                }
52                if(!isFinite(vx2)){
53                        vx2 = 1, vy2 = (l2 - M.xx) / M.xy;
54                        if(!isFinite(vy2)){
55                                vx2 = (l2 - M.yy) / M.yx, vy2 = 1;
56                                if(!isFinite(vx2)){
57                                        vx2 = 1, vy2 = M.yx / (l2 - M.yy);
58                                }
59                        }
60                }
61                var d1 = Math.sqrt(vx1 * vx1 + vy1 * vy1),
62                        d2 = Math.sqrt(vx2 * vx2 + vy2 * vy2);
63                if(!isFinite(vx1 /= d1)){ vx1 = 0; }
64                if(!isFinite(vy1 /= d1)){ vy1 = 0; }
65                if(!isFinite(vx2 /= d2)){ vx2 = 0; }
66                if(!isFinite(vy2 /= d2)){ vy2 = 0; }
67                return {        // Object
68                        value1: l1,
69                        value2: l2,
70                        vector1: {x: vx1, y: vy1},
71                        vector2: {x: vx2, y: vy2}
72                };
73        }
74
75        function decomposeSR(/* dojox.gfx.matrix.Matrix2D */ M, /* Object */ result){
76                // summary: decomposes a matrix into [scale, rotate]; no checks are done.
77                var sign = scaleSign(M),
78                        a = result.angle1 = (Math.atan2(M.yx, M.yy) + Math.atan2(-sign * M.xy, sign * M.xx)) / 2,
79                        cos = Math.cos(a), sin = Math.sin(a);
80                result.sx = calcFromValues(M.xx / cos, cos, -M.xy / sin, sin);
81                result.sy = calcFromValues(M.yy / cos, cos,  M.yx / sin, sin);
82                return result;  // Object
83        }
84
85        function decomposeRS(/* dojox.gfx.matrix.Matrix2D */ M, /* Object */ result){
86                // summary: decomposes a matrix into [rotate, scale]; no checks are done
87                var sign = scaleSign(M),
88                        a = result.angle2 = (Math.atan2(sign * M.yx, sign * M.xx) + Math.atan2(-M.xy, M.yy)) / 2,
89                        cos = Math.cos(a), sin = Math.sin(a);
90                result.sx = calcFromValues(M.xx / cos, cos,  M.yx / sin, sin);
91                result.sy = calcFromValues(M.yy / cos, cos, -M.xy / sin, sin);
92                return result;  // Object
93        }
94
95        return g.decompose = function(matrix){
96                // summary: Decompose a 2D matrix into translation, scaling, and rotation components.
97                // description: This function decompose a matrix into four logical components:
98                //      translation, rotation, scaling, and one more rotation using SVD.
99                //      The components should be applied in following order:
100                //      | [translate, rotate(angle2), scale, rotate(angle1)]
101                // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object
102                var M = m.normalize(matrix),
103                        result = {dx: M.dx, dy: M.dy, sx: 1, sy: 1, angle1: 0, angle2: 0};
104                // detect case: [scale]
105                if(eq(M.xy, 0) && eq(M.yx, 0)){
106                        return lang.mixin(result, {sx: M.xx, sy: M.yy});        // Object
107                }
108                // detect case: [scale, rotate]
109                if(eq(M.xx * M.yx, -M.xy * M.yy)){
110                        return decomposeSR(M, result);  // Object
111                }
112                // detect case: [rotate, scale]
113                if(eq(M.xx * M.xy, -M.yx * M.yy)){
114                        return decomposeRS(M, result);  // Object
115                }
116                // do SVD
117                var     MT = transpose(M),
118                        u  = eigenvalueDecomposition([M, MT]),
119                        v  = eigenvalueDecomposition([MT, M]),
120                        U  = new m.Matrix2D({xx: u.vector1.x, xy: u.vector2.x, yx: u.vector1.y, yy: u.vector2.y}),
121                        VT = new m.Matrix2D({xx: v.vector1.x, xy: v.vector1.y, yx: v.vector2.x, yy: v.vector2.y}),
122                        S = new m.Matrix2D([m.invert(U), M, m.invert(VT)]);
123                decomposeSR(VT, result);
124                S.xx *= result.sx;
125                S.yy *= result.sy;
126                decomposeRS(U, result);
127                S.xx *= result.sx;
128                S.yy *= result.sy;
129                return lang.mixin(result, {sx: S.xx, sy: S.yy});        // Object
130        };
131});
Note: See TracBrowser for help on using the repository browser.