matrix.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.gfx.matrix"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.gfx.matrix"] = true;
  8. dojo.provide("dojox.gfx.matrix");
  9. (function(){
  10. var m = dojox.gfx.matrix;
  11. // candidates for dojox.math:
  12. var _degToRadCache = {};
  13. m._degToRad = function(degree){
  14. return _degToRadCache[degree] || (_degToRadCache[degree] = (Math.PI * degree / 180));
  15. };
  16. m._radToDeg = function(radian){ return radian / Math.PI * 180; };
  17. m.Matrix2D = function(arg){
  18. // summary: a 2D matrix object
  19. // description: Normalizes a 2D matrix-like object. If arrays is passed,
  20. // all objects of the array are normalized and multiplied sequentially.
  21. // arg: Object
  22. // a 2D matrix-like object, a number, or an array of such objects
  23. if(arg){
  24. if(typeof arg == "number"){
  25. this.xx = this.yy = arg;
  26. }else if(arg instanceof Array){
  27. if(arg.length > 0){
  28. var matrix = m.normalize(arg[0]);
  29. // combine matrices
  30. for(var i = 1; i < arg.length; ++i){
  31. var l = matrix, r = dojox.gfx.matrix.normalize(arg[i]);
  32. matrix = new m.Matrix2D();
  33. matrix.xx = l.xx * r.xx + l.xy * r.yx;
  34. matrix.xy = l.xx * r.xy + l.xy * r.yy;
  35. matrix.yx = l.yx * r.xx + l.yy * r.yx;
  36. matrix.yy = l.yx * r.xy + l.yy * r.yy;
  37. matrix.dx = l.xx * r.dx + l.xy * r.dy + l.dx;
  38. matrix.dy = l.yx * r.dx + l.yy * r.dy + l.dy;
  39. }
  40. dojo.mixin(this, matrix);
  41. }
  42. }else{
  43. dojo.mixin(this, arg);
  44. }
  45. }
  46. };
  47. // the default (identity) matrix, which is used to fill in missing values
  48. dojo.extend(m.Matrix2D, {xx: 1, xy: 0, yx: 0, yy: 1, dx: 0, dy: 0});
  49. dojo.mixin(m, {
  50. // summary: class constants, and methods of dojox.gfx.matrix
  51. // matrix constants
  52. // identity: dojox.gfx.matrix.Matrix2D
  53. // an identity matrix constant: identity * (x, y) == (x, y)
  54. identity: new m.Matrix2D(),
  55. // flipX: dojox.gfx.matrix.Matrix2D
  56. // a matrix, which reflects points at x = 0 line: flipX * (x, y) == (-x, y)
  57. flipX: new m.Matrix2D({xx: -1}),
  58. // flipY: dojox.gfx.matrix.Matrix2D
  59. // a matrix, which reflects points at y = 0 line: flipY * (x, y) == (x, -y)
  60. flipY: new m.Matrix2D({yy: -1}),
  61. // flipXY: dojox.gfx.matrix.Matrix2D
  62. // a matrix, which reflects points at the origin of coordinates: flipXY * (x, y) == (-x, -y)
  63. flipXY: new m.Matrix2D({xx: -1, yy: -1}),
  64. // matrix creators
  65. translate: function(a, b){
  66. // summary: forms a translation matrix
  67. // description: The resulting matrix is used to translate (move) points by specified offsets.
  68. // a: Number: an x coordinate value
  69. // b: Number: a y coordinate value
  70. if(arguments.length > 1){
  71. return new m.Matrix2D({dx: a, dy: b}); // dojox.gfx.matrix.Matrix2D
  72. }
  73. // branch
  74. // a: dojox.gfx.Point: a point-like object, which specifies offsets for both dimensions
  75. // b: null
  76. return new m.Matrix2D({dx: a.x, dy: a.y}); // dojox.gfx.matrix.Matrix2D
  77. },
  78. scale: function(a, b){
  79. // summary: forms a scaling matrix
  80. // description: The resulting matrix is used to scale (magnify) points by specified offsets.
  81. // a: Number: a scaling factor used for the x coordinate
  82. // b: Number: a scaling factor used for the y coordinate
  83. if(arguments.length > 1){
  84. return new m.Matrix2D({xx: a, yy: b}); // dojox.gfx.matrix.Matrix2D
  85. }
  86. if(typeof a == "number"){
  87. // branch
  88. // a: Number: a uniform scaling factor used for the both coordinates
  89. // b: null
  90. return new m.Matrix2D({xx: a, yy: a}); // dojox.gfx.matrix.Matrix2D
  91. }
  92. // branch
  93. // a: dojox.gfx.Point: a point-like object, which specifies scale factors for both dimensions
  94. // b: null
  95. return new m.Matrix2D({xx: a.x, yy: a.y}); // dojox.gfx.matrix.Matrix2D
  96. },
  97. rotate: function(angle){
  98. // summary: forms a rotating matrix
  99. // description: The resulting matrix is used to rotate points
  100. // around the origin of coordinates (0, 0) by specified angle.
  101. // angle: Number: an angle of rotation in radians (>0 for CW)
  102. var c = Math.cos(angle);
  103. var s = Math.sin(angle);
  104. return new m.Matrix2D({xx: c, xy: -s, yx: s, yy: c}); // dojox.gfx.matrix.Matrix2D
  105. },
  106. rotateg: function(degree){
  107. // summary: forms a rotating matrix
  108. // description: The resulting matrix is used to rotate points
  109. // around the origin of coordinates (0, 0) by specified degree.
  110. // See dojox.gfx.matrix.rotate() for comparison.
  111. // degree: Number: an angle of rotation in degrees (>0 for CW)
  112. return m.rotate(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D
  113. },
  114. skewX: function(angle) {
  115. // summary: forms an x skewing matrix
  116. // description: The resulting matrix is used to skew points in the x dimension
  117. // around the origin of coordinates (0, 0) by specified angle.
  118. // angle: Number: an skewing angle in radians
  119. return new m.Matrix2D({xy: Math.tan(angle)}); // dojox.gfx.matrix.Matrix2D
  120. },
  121. skewXg: function(degree){
  122. // summary: forms an x skewing matrix
  123. // description: The resulting matrix is used to skew points in the x dimension
  124. // around the origin of coordinates (0, 0) by specified degree.
  125. // See dojox.gfx.matrix.skewX() for comparison.
  126. // degree: Number: an skewing angle in degrees
  127. return m.skewX(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D
  128. },
  129. skewY: function(angle){
  130. // summary: forms a y skewing matrix
  131. // description: The resulting matrix is used to skew points in the y dimension
  132. // around the origin of coordinates (0, 0) by specified angle.
  133. // angle: Number: an skewing angle in radians
  134. return new m.Matrix2D({yx: Math.tan(angle)}); // dojox.gfx.matrix.Matrix2D
  135. },
  136. skewYg: function(degree){
  137. // summary: forms a y skewing matrix
  138. // description: The resulting matrix is used to skew points in the y dimension
  139. // around the origin of coordinates (0, 0) by specified degree.
  140. // See dojox.gfx.matrix.skewY() for comparison.
  141. // degree: Number: an skewing angle in degrees
  142. return m.skewY(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D
  143. },
  144. reflect: function(a, b){
  145. // summary: forms a reflection matrix
  146. // description: The resulting matrix is used to reflect points around a vector,
  147. // which goes through the origin.
  148. // a: dojox.gfx.Point: a point-like object, which specifies a vector of reflection
  149. // b: null
  150. if(arguments.length == 1){
  151. b = a.y;
  152. a = a.x;
  153. }
  154. // branch
  155. // a: Number: an x coordinate value
  156. // b: Number: a y coordinate value
  157. // make a unit vector
  158. var a2 = a * a, b2 = b * b, n2 = a2 + b2, xy = 2 * a * b / n2;
  159. return new m.Matrix2D({xx: 2 * a2 / n2 - 1, xy: xy, yx: xy, yy: 2 * b2 / n2 - 1}); // dojox.gfx.matrix.Matrix2D
  160. },
  161. project: function(a, b){
  162. // summary: forms an orthogonal projection matrix
  163. // description: The resulting matrix is used to project points orthogonally on a vector,
  164. // which goes through the origin.
  165. // a: dojox.gfx.Point: a point-like object, which specifies a vector of projection
  166. // b: null
  167. if(arguments.length == 1){
  168. b = a.y;
  169. a = a.x;
  170. }
  171. // branch
  172. // a: Number: an x coordinate value
  173. // b: Number: a y coordinate value
  174. // make a unit vector
  175. var a2 = a * a, b2 = b * b, n2 = a2 + b2, xy = a * b / n2;
  176. return new m.Matrix2D({xx: a2 / n2, xy: xy, yx: xy, yy: b2 / n2}); // dojox.gfx.matrix.Matrix2D
  177. },
  178. // ensure matrix 2D conformance
  179. normalize: function(matrix){
  180. // summary: converts an object to a matrix, if necessary
  181. // description: Converts any 2D matrix-like object or an array of
  182. // such objects to a valid dojox.gfx.matrix.Matrix2D object.
  183. // matrix: Object: an object, which is converted to a matrix, if necessary
  184. return (matrix instanceof m.Matrix2D) ? matrix : new m.Matrix2D(matrix); // dojox.gfx.matrix.Matrix2D
  185. },
  186. // common operations
  187. clone: function(matrix){
  188. // summary: creates a copy of a 2D matrix
  189. // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object to be cloned
  190. var obj = new m.Matrix2D();
  191. for(var i in matrix){
  192. if(typeof(matrix[i]) == "number" && typeof(obj[i]) == "number" && obj[i] != matrix[i]) obj[i] = matrix[i];
  193. }
  194. return obj; // dojox.gfx.matrix.Matrix2D
  195. },
  196. invert: function(matrix){
  197. // summary: inverts a 2D matrix
  198. // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object to be inverted
  199. var M = m.normalize(matrix),
  200. D = M.xx * M.yy - M.xy * M.yx,
  201. M = new m.Matrix2D({
  202. xx: M.yy/D, xy: -M.xy/D,
  203. yx: -M.yx/D, yy: M.xx/D,
  204. dx: (M.xy * M.dy - M.yy * M.dx) / D,
  205. dy: (M.yx * M.dx - M.xx * M.dy) / D
  206. });
  207. return M; // dojox.gfx.matrix.Matrix2D
  208. },
  209. _multiplyPoint: function(matrix, x, y){
  210. // summary: applies a matrix to a point
  211. // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied
  212. // x: Number: an x coordinate of a point
  213. // y: Number: a y coordinate of a point
  214. return {x: matrix.xx * x + matrix.xy * y + matrix.dx, y: matrix.yx * x + matrix.yy * y + matrix.dy}; // dojox.gfx.Point
  215. },
  216. multiplyPoint: function(matrix, /* Number||Point */ a, /* Number, optional */ b){
  217. // summary: applies a matrix to a point
  218. // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied
  219. // a: Number: an x coordinate of a point
  220. // b: Number: a y coordinate of a point
  221. var M = m.normalize(matrix);
  222. if(typeof a == "number" && typeof b == "number"){
  223. return m._multiplyPoint(M, a, b); // dojox.gfx.Point
  224. }
  225. // branch
  226. // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied
  227. // a: dojox.gfx.Point: a point
  228. // b: null
  229. return m._multiplyPoint(M, a.x, a.y); // dojox.gfx.Point
  230. },
  231. multiply: function(matrix){
  232. // summary: combines matrices by multiplying them sequentially in the given order
  233. // matrix: dojox.gfx.matrix.Matrix2D...: a 2D matrix-like object,
  234. // all subsequent arguments are matrix-like objects too
  235. var M = m.normalize(matrix);
  236. // combine matrices
  237. for(var i = 1; i < arguments.length; ++i){
  238. var l = M, r = m.normalize(arguments[i]);
  239. M = new m.Matrix2D();
  240. M.xx = l.xx * r.xx + l.xy * r.yx;
  241. M.xy = l.xx * r.xy + l.xy * r.yy;
  242. M.yx = l.yx * r.xx + l.yy * r.yx;
  243. M.yy = l.yx * r.xy + l.yy * r.yy;
  244. M.dx = l.xx * r.dx + l.xy * r.dy + l.dx;
  245. M.dy = l.yx * r.dx + l.yy * r.dy + l.dy;
  246. }
  247. return M; // dojox.gfx.matrix.Matrix2D
  248. },
  249. // high level operations
  250. _sandwich: function(matrix, x, y){
  251. // summary: applies a matrix at a centrtal point
  252. // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object, which is applied at a central point
  253. // x: Number: an x component of the central point
  254. // y: Number: a y component of the central point
  255. return m.multiply(m.translate(x, y), matrix, m.translate(-x, -y)); // dojox.gfx.matrix.Matrix2D
  256. },
  257. scaleAt: function(a, b, c, d){
  258. // summary: scales a picture using a specified point as a center of scaling
  259. // description: Compare with dojox.gfx.matrix.scale().
  260. // a: Number: a scaling factor used for the x coordinate
  261. // b: Number: a scaling factor used for the y coordinate
  262. // c: Number: an x component of a central point
  263. // d: Number: a y component of a central point
  264. // accepts several signatures:
  265. // 1) uniform scale factor, Point
  266. // 2) uniform scale factor, x, y
  267. // 3) x scale, y scale, Point
  268. // 4) x scale, y scale, x, y
  269. switch(arguments.length){
  270. case 4:
  271. // a and b are scale factor components, c and d are components of a point
  272. return m._sandwich(m.scale(a, b), c, d); // dojox.gfx.matrix.Matrix2D
  273. case 3:
  274. if(typeof c == "number"){
  275. // branch
  276. // a: Number: a uniform scaling factor used for both coordinates
  277. // b: Number: an x component of a central point
  278. // c: Number: a y component of a central point
  279. // d: null
  280. return m._sandwich(m.scale(a), b, c); // dojox.gfx.matrix.Matrix2D
  281. }
  282. // branch
  283. // a: Number: a scaling factor used for the x coordinate
  284. // b: Number: a scaling factor used for the y coordinate
  285. // c: dojox.gfx.Point: a central point
  286. // d: null
  287. return m._sandwich(m.scale(a, b), c.x, c.y); // dojox.gfx.matrix.Matrix2D
  288. }
  289. // branch
  290. // a: Number: a uniform scaling factor used for both coordinates
  291. // b: dojox.gfx.Point: a central point
  292. // c: null
  293. // d: null
  294. return m._sandwich(m.scale(a), b.x, b.y); // dojox.gfx.matrix.Matrix2D
  295. },
  296. rotateAt: function(angle, a, b){
  297. // summary: rotates a picture using a specified point as a center of rotation
  298. // description: Compare with dojox.gfx.matrix.rotate().
  299. // angle: Number: an angle of rotation in radians (>0 for CW)
  300. // a: Number: an x component of a central point
  301. // b: Number: a y component of a central point
  302. // accepts several signatures:
  303. // 1) rotation angle in radians, Point
  304. // 2) rotation angle in radians, x, y
  305. if(arguments.length > 2){
  306. return m._sandwich(m.rotate(angle), a, b); // dojox.gfx.matrix.Matrix2D
  307. }
  308. // branch
  309. // angle: Number: an angle of rotation in radians (>0 for CCW)
  310. // a: dojox.gfx.Point: a central point
  311. // b: null
  312. return m._sandwich(m.rotate(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D
  313. },
  314. rotategAt: function(degree, a, b){
  315. // summary: rotates a picture using a specified point as a center of rotation
  316. // description: Compare with dojox.gfx.matrix.rotateg().
  317. // degree: Number: an angle of rotation in degrees (>0 for CW)
  318. // a: Number: an x component of a central point
  319. // b: Number: a y component of a central point
  320. // accepts several signatures:
  321. // 1) rotation angle in degrees, Point
  322. // 2) rotation angle in degrees, x, y
  323. if(arguments.length > 2){
  324. return m._sandwich(m.rotateg(degree), a, b); // dojox.gfx.matrix.Matrix2D
  325. }
  326. // branch
  327. // degree: Number: an angle of rotation in degrees (>0 for CCW)
  328. // a: dojox.gfx.Point: a central point
  329. // b: null
  330. return m._sandwich(m.rotateg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D
  331. },
  332. skewXAt: function(angle, a, b){
  333. // summary: skews a picture along the x axis using a specified point as a center of skewing
  334. // description: Compare with dojox.gfx.matrix.skewX().
  335. // angle: Number: an skewing angle in radians
  336. // a: Number: an x component of a central point
  337. // b: Number: a y component of a central point
  338. // accepts several signatures:
  339. // 1) skew angle in radians, Point
  340. // 2) skew angle in radians, x, y
  341. if(arguments.length > 2){
  342. return m._sandwich(m.skewX(angle), a, b); // dojox.gfx.matrix.Matrix2D
  343. }
  344. // branch
  345. // angle: Number: an skewing angle in radians
  346. // a: dojox.gfx.Point: a central point
  347. // b: null
  348. return m._sandwich(m.skewX(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D
  349. },
  350. skewXgAt: function(degree, a, b){
  351. // summary: skews a picture along the x axis using a specified point as a center of skewing
  352. // description: Compare with dojox.gfx.matrix.skewXg().
  353. // degree: Number: an skewing angle in degrees
  354. // a: Number: an x component of a central point
  355. // b: Number: a y component of a central point
  356. // accepts several signatures:
  357. // 1) skew angle in degrees, Point
  358. // 2) skew angle in degrees, x, y
  359. if(arguments.length > 2){
  360. return m._sandwich(m.skewXg(degree), a, b); // dojox.gfx.matrix.Matrix2D
  361. }
  362. // branch
  363. // degree: Number: an skewing angle in degrees
  364. // a: dojox.gfx.Point: a central point
  365. // b: null
  366. return m._sandwich(m.skewXg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D
  367. },
  368. skewYAt: function(angle, a, b){
  369. // summary: skews a picture along the y axis using a specified point as a center of skewing
  370. // description: Compare with dojox.gfx.matrix.skewY().
  371. // angle: Number: an skewing angle in radians
  372. // a: Number: an x component of a central point
  373. // b: Number: a y component of a central point
  374. // accepts several signatures:
  375. // 1) skew angle in radians, Point
  376. // 2) skew angle in radians, x, y
  377. if(arguments.length > 2){
  378. return m._sandwich(m.skewY(angle), a, b); // dojox.gfx.matrix.Matrix2D
  379. }
  380. // branch
  381. // angle: Number: an skewing angle in radians
  382. // a: dojox.gfx.Point: a central point
  383. // b: null
  384. return m._sandwich(m.skewY(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D
  385. },
  386. skewYgAt: function(/* Number */ degree, /* Number||Point */ a, /* Number, optional */ b){
  387. // summary: skews a picture along the y axis using a specified point as a center of skewing
  388. // description: Compare with dojox.gfx.matrix.skewYg().
  389. // degree: Number: an skewing angle in degrees
  390. // a: Number: an x component of a central point
  391. // b: Number: a y component of a central point
  392. // accepts several signatures:
  393. // 1) skew angle in degrees, Point
  394. // 2) skew angle in degrees, x, y
  395. if(arguments.length > 2){
  396. return m._sandwich(m.skewYg(degree), a, b); // dojox.gfx.matrix.Matrix2D
  397. }
  398. // branch
  399. // degree: Number: an skewing angle in degrees
  400. // a: dojox.gfx.Point: a central point
  401. // b: null
  402. return m._sandwich(m.skewYg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D
  403. }
  404. //TODO: rect-to-rect mapping, scale-to-fit (isotropic and anisotropic versions)
  405. });
  406. })();
  407. // propagate Matrix2D up
  408. dojox.gfx.Matrix2D = dojox.gfx.matrix.Matrix2D;
  409. }