123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- if (!dojo._hasResource["dojox.gfx.decompose"]) { // _hasResource checks added
- // by build. Do not use
- // _hasResource directly in
- // your code.
- dojo._hasResource["dojox.gfx.decompose"] = true;
- dojo.provide("dojox.gfx.decompose");
- dojo.require("dojox.gfx.matrix");
- (function() {
- var m = dojox.gfx.matrix;
- var eq = function(/* Number */a, /* Number */b) {
- // summary: compare two FP numbers for equality
- return Math.abs(a - b) <= 1e-6 * (Math.abs(a) + Math.abs(b)); // Boolean
- };
- var calcFromValues = function(/* Number */r1, /* Number */m1, /* Number */
- r2, /* Number */m2) {
- // summary: uses two close FP ration and their original magnitudes
- // to approximate the result
- if (!isFinite(r1)) {
- return r2; // Number
- } else if (!isFinite(r2)) {
- return r1; // Number
- }
- m1 = Math.abs(m1), m2 = Math.abs(m2);
- return (m1 * r1 + m2 * r2) / (m1 + m2); // Number
- };
- var transpose = function(/* dojox.gfx.matrix.Matrix2D */matrix) {
- // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object
- var M = new m.Matrix2D(matrix);
- return dojo.mixin(M, {
- dx : 0,
- dy : 0,
- xy : M.yx,
- yx : M.xy
- }); // dojox.gfx.matrix.Matrix2D
- };
- var scaleSign = function(/* dojox.gfx.matrix.Matrix2D */matrix) {
- return (matrix.xx * matrix.yy < 0 || matrix.xy * matrix.yx > 0)
- ? -1
- : 1; // Number
- };
- var eigenvalueDecomposition = function(
- /* dojox.gfx.matrix.Matrix2D */matrix) {
- // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object
- var M = m.normalize(matrix), b = -M.xx - M.yy, c = M.xx * M.yy
- - M.xy * M.yx, d = Math.sqrt(b * b - 4 * c), l1 = -(b + (b < 0
- ? -d
- : d))
- / 2, l2 = c / l1, vx1 = M.xy / (l1 - M.xx), vy1 = 1, vx2 = M.xy
- / (l2 - M.xx), vy2 = 1;
- if (eq(l1, l2)) {
- vx1 = 1, vy1 = 0, vx2 = 0, vy2 = 1;
- }
- if (!isFinite(vx1)) {
- vx1 = 1, vy1 = (l1 - M.xx) / M.xy;
- if (!isFinite(vy1)) {
- vx1 = (l1 - M.yy) / M.yx, vy1 = 1;
- if (!isFinite(vx1)) {
- vx1 = 1, vy1 = M.yx / (l1 - M.yy);
- }
- }
- }
- if (!isFinite(vx2)) {
- vx2 = 1, vy2 = (l2 - M.xx) / M.xy;
- if (!isFinite(vy2)) {
- vx2 = (l2 - M.yy) / M.yx, vy2 = 1;
- if (!isFinite(vx2)) {
- vx2 = 1, vy2 = M.yx / (l2 - M.yy);
- }
- }
- }
- var d1 = Math.sqrt(vx1 * vx1 + vy1 * vy1), d2 = Math.sqrt(vx2 * vx2
- + vy2 * vy2);
- if (!isFinite(vx1 /= d1)) {
- vx1 = 0;
- }
- if (!isFinite(vy1 /= d1)) {
- vy1 = 0;
- }
- if (!isFinite(vx2 /= d2)) {
- vx2 = 0;
- }
- if (!isFinite(vy2 /= d2)) {
- vy2 = 0;
- }
- return { // Object
- value1 : l1,
- value2 : l2,
- vector1 : {
- x : vx1,
- y : vy1
- },
- vector2 : {
- x : vx2,
- y : vy2
- }
- };
- };
- var decomposeSR = function(/* dojox.gfx.matrix.Matrix2D */M, /* Object */
- result) {
- // summary: decomposes a matrix into [scale, rotate]; no checks are
- // done.
- var sign = scaleSign(M), a = result.angle1 = (Math
- .atan2(M.yx, M.yy) + Math.atan2(-sign * M.xy, sign * M.xx))
- / 2, cos = Math.cos(a), sin = Math.sin(a);
- result.sx = calcFromValues(M.xx / cos, cos, -M.xy / sin, sin);
- result.sy = calcFromValues(M.yy / cos, cos, M.yx / sin, sin);
- return result; // Object
- };
- var decomposeRS = function(/* dojox.gfx.matrix.Matrix2D */M, /* Object */
- result) {
- // summary: decomposes a matrix into [rotate, scale]; no checks are
- // done
- var sign = scaleSign(M), a = result.angle2 = (Math.atan2(sign
- * M.yx, sign * M.xx) + Math.atan2(-M.xy, M.yy))
- / 2, cos = Math.cos(a), sin = Math.sin(a);
- result.sx = calcFromValues(M.xx / cos, cos, M.yx / sin, sin);
- result.sy = calcFromValues(M.yy / cos, cos, -M.xy / sin, sin);
- return result; // Object
- };
- dojox.gfx.decompose = function(matrix) {
- // summary: decompose a 2D matrix into translation, scaling, and
- // rotation components
- // description: this function decompose a matrix into four logical
- // components:
- // translation, rotation, scaling, and one more rotation using SVD.
- // The components should be applied in following order:
- // | [translate, rotate(angle2), scale, rotate(angle1)]
- // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object
- var M = m.normalize(matrix), result = {
- dx : M.dx,
- dy : M.dy,
- sx : 1,
- sy : 1,
- angle1 : 0,
- angle2 : 0
- };
- // detect case: [scale]
- if (eq(M.xy, 0) && eq(M.yx, 0)) {
- return dojo.mixin(result, {
- sx : M.xx,
- sy : M.yy
- }); // Object
- }
- // detect case: [scale, rotate]
- if (eq(M.xx * M.yx, -M.xy * M.yy)) {
- return decomposeSR(M, result); // Object
- }
- // detect case: [rotate, scale]
- if (eq(M.xx * M.xy, -M.yx * M.yy)) {
- return decomposeRS(M, result); // Object
- }
- // do SVD
- var MT = transpose(M), u = eigenvalueDecomposition([M, MT]), v = eigenvalueDecomposition([
- MT, M]), U = new m.Matrix2D({
- xx : u.vector1.x,
- xy : u.vector2.x,
- yx : u.vector1.y,
- yy : u.vector2.y
- }), VT = new m.Matrix2D({
- xx : v.vector1.x,
- xy : v.vector1.y,
- yx : v.vector2.x,
- yy : v.vector2.y
- }), S = new m.Matrix2D([m.invert(U), M, m.invert(VT)]);
- decomposeSR(VT, result);
- S.xx *= result.sx;
- S.yy *= result.sy;
- decomposeRS(U, result);
- S.xx *= result.sx;
- S.yy *= result.sy;
- return dojo.mixin(result, {
- sx : S.xx,
- sy : S.yy
- }); // Object
- };
- })();
- }
|