matrix.js 18 KB

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