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