source: Dev/trunk/src/client/dojox/gfx/decompose.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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