Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 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
}