transformation-matrix.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. import {
  2. createTypedArray,
  3. } from '../utils/helpers/arrays';
  4. /*!
  5. Transformation Matrix v2.0
  6. (c) Epistemex 2014-2015
  7. www.epistemex.com
  8. By Ken Fyrstenberg
  9. Contributions by leeoniya.
  10. License: MIT, header required.
  11. */
  12. /**
  13. * 2D transformation matrix object initialized with identity matrix.
  14. *
  15. * The matrix can synchronize a canvas context by supplying the context
  16. * as an argument, or later apply current absolute transform to an
  17. * existing context.
  18. *
  19. * All values are handled as floating point values.
  20. *
  21. * @param {CanvasRenderingContext2D} [context] - Optional context to sync with Matrix
  22. * @prop {number} a - scale x
  23. * @prop {number} b - shear y
  24. * @prop {number} c - shear x
  25. * @prop {number} d - scale y
  26. * @prop {number} e - translate x
  27. * @prop {number} f - translate y
  28. * @prop {CanvasRenderingContext2D|null} [context=null] - set or get current canvas context
  29. * @constructor
  30. */
  31. const Matrix = (function () {
  32. var _cos = Math.cos;
  33. var _sin = Math.sin;
  34. var _tan = Math.tan;
  35. var _rnd = Math.round;
  36. function reset() {
  37. this.props[0] = 1;
  38. this.props[1] = 0;
  39. this.props[2] = 0;
  40. this.props[3] = 0;
  41. this.props[4] = 0;
  42. this.props[5] = 1;
  43. this.props[6] = 0;
  44. this.props[7] = 0;
  45. this.props[8] = 0;
  46. this.props[9] = 0;
  47. this.props[10] = 1;
  48. this.props[11] = 0;
  49. this.props[12] = 0;
  50. this.props[13] = 0;
  51. this.props[14] = 0;
  52. this.props[15] = 1;
  53. return this;
  54. }
  55. function rotate(angle) {
  56. if (angle === 0) {
  57. return this;
  58. }
  59. var mCos = _cos(angle);
  60. var mSin = _sin(angle);
  61. return this._t(mCos, -mSin, 0, 0, mSin, mCos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
  62. }
  63. function rotateX(angle) {
  64. if (angle === 0) {
  65. return this;
  66. }
  67. var mCos = _cos(angle);
  68. var mSin = _sin(angle);
  69. return this._t(1, 0, 0, 0, 0, mCos, -mSin, 0, 0, mSin, mCos, 0, 0, 0, 0, 1);
  70. }
  71. function rotateY(angle) {
  72. if (angle === 0) {
  73. return this;
  74. }
  75. var mCos = _cos(angle);
  76. var mSin = _sin(angle);
  77. return this._t(mCos, 0, mSin, 0, 0, 1, 0, 0, -mSin, 0, mCos, 0, 0, 0, 0, 1);
  78. }
  79. function rotateZ(angle) {
  80. if (angle === 0) {
  81. return this;
  82. }
  83. var mCos = _cos(angle);
  84. var mSin = _sin(angle);
  85. return this._t(mCos, -mSin, 0, 0, mSin, mCos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
  86. }
  87. function shear(sx, sy) {
  88. return this._t(1, sy, sx, 1, 0, 0);
  89. }
  90. function skew(ax, ay) {
  91. return this.shear(_tan(ax), _tan(ay));
  92. }
  93. function skewFromAxis(ax, angle) {
  94. var mCos = _cos(angle);
  95. var mSin = _sin(angle);
  96. return this._t(mCos, mSin, 0, 0, -mSin, mCos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
  97. ._t(1, 0, 0, 0, _tan(ax), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
  98. ._t(mCos, -mSin, 0, 0, mSin, mCos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
  99. // return this._t(mCos, mSin, -mSin, mCos, 0, 0)._t(1, 0, _tan(ax), 1, 0, 0)._t(mCos, -mSin, mSin, mCos, 0, 0);
  100. }
  101. function scale(sx, sy, sz) {
  102. if (!sz && sz !== 0) {
  103. sz = 1;
  104. }
  105. if (sx === 1 && sy === 1 && sz === 1) {
  106. return this;
  107. }
  108. return this._t(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1);
  109. }
  110. function setTransform(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {
  111. this.props[0] = a;
  112. this.props[1] = b;
  113. this.props[2] = c;
  114. this.props[3] = d;
  115. this.props[4] = e;
  116. this.props[5] = f;
  117. this.props[6] = g;
  118. this.props[7] = h;
  119. this.props[8] = i;
  120. this.props[9] = j;
  121. this.props[10] = k;
  122. this.props[11] = l;
  123. this.props[12] = m;
  124. this.props[13] = n;
  125. this.props[14] = o;
  126. this.props[15] = p;
  127. return this;
  128. }
  129. function translate(tx, ty, tz) {
  130. tz = tz || 0;
  131. if (tx !== 0 || ty !== 0 || tz !== 0) {
  132. return this._t(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1);
  133. }
  134. return this;
  135. }
  136. function transform(a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2) {
  137. var _p = this.props;
  138. if (a2 === 1 && b2 === 0 && c2 === 0 && d2 === 0 && e2 === 0 && f2 === 1 && g2 === 0 && h2 === 0 && i2 === 0 && j2 === 0 && k2 === 1 && l2 === 0) {
  139. // NOTE: commenting this condition because TurboFan deoptimizes code when present
  140. // if(m2 !== 0 || n2 !== 0 || o2 !== 0){
  141. _p[12] = _p[12] * a2 + _p[15] * m2;
  142. _p[13] = _p[13] * f2 + _p[15] * n2;
  143. _p[14] = _p[14] * k2 + _p[15] * o2;
  144. _p[15] *= p2;
  145. // }
  146. this._identityCalculated = false;
  147. return this;
  148. }
  149. var a1 = _p[0];
  150. var b1 = _p[1];
  151. var c1 = _p[2];
  152. var d1 = _p[3];
  153. var e1 = _p[4];
  154. var f1 = _p[5];
  155. var g1 = _p[6];
  156. var h1 = _p[7];
  157. var i1 = _p[8];
  158. var j1 = _p[9];
  159. var k1 = _p[10];
  160. var l1 = _p[11];
  161. var m1 = _p[12];
  162. var n1 = _p[13];
  163. var o1 = _p[14];
  164. var p1 = _p[15];
  165. /* matrix order (canvas compatible):
  166. * ace
  167. * bdf
  168. * 001
  169. */
  170. _p[0] = a1 * a2 + b1 * e2 + c1 * i2 + d1 * m2;
  171. _p[1] = a1 * b2 + b1 * f2 + c1 * j2 + d1 * n2;
  172. _p[2] = a1 * c2 + b1 * g2 + c1 * k2 + d1 * o2;
  173. _p[3] = a1 * d2 + b1 * h2 + c1 * l2 + d1 * p2;
  174. _p[4] = e1 * a2 + f1 * e2 + g1 * i2 + h1 * m2;
  175. _p[5] = e1 * b2 + f1 * f2 + g1 * j2 + h1 * n2;
  176. _p[6] = e1 * c2 + f1 * g2 + g1 * k2 + h1 * o2;
  177. _p[7] = e1 * d2 + f1 * h2 + g1 * l2 + h1 * p2;
  178. _p[8] = i1 * a2 + j1 * e2 + k1 * i2 + l1 * m2;
  179. _p[9] = i1 * b2 + j1 * f2 + k1 * j2 + l1 * n2;
  180. _p[10] = i1 * c2 + j1 * g2 + k1 * k2 + l1 * o2;
  181. _p[11] = i1 * d2 + j1 * h2 + k1 * l2 + l1 * p2;
  182. _p[12] = m1 * a2 + n1 * e2 + o1 * i2 + p1 * m2;
  183. _p[13] = m1 * b2 + n1 * f2 + o1 * j2 + p1 * n2;
  184. _p[14] = m1 * c2 + n1 * g2 + o1 * k2 + p1 * o2;
  185. _p[15] = m1 * d2 + n1 * h2 + o1 * l2 + p1 * p2;
  186. this._identityCalculated = false;
  187. return this;
  188. }
  189. function multiply(matrix) {
  190. var matrixProps = matrix.props;
  191. return this.transform(
  192. matrixProps[0],
  193. matrixProps[1],
  194. matrixProps[2],
  195. matrixProps[3],
  196. matrixProps[4],
  197. matrixProps[5],
  198. matrixProps[6],
  199. matrixProps[7],
  200. matrixProps[8],
  201. matrixProps[9],
  202. matrixProps[10],
  203. matrixProps[11],
  204. matrixProps[12],
  205. matrixProps[13],
  206. matrixProps[14],
  207. matrixProps[15]
  208. );
  209. }
  210. function isIdentity() {
  211. if (!this._identityCalculated) {
  212. this._identity = !(this.props[0] !== 1 || this.props[1] !== 0 || this.props[2] !== 0 || this.props[3] !== 0 || this.props[4] !== 0 || this.props[5] !== 1 || this.props[6] !== 0 || this.props[7] !== 0 || this.props[8] !== 0 || this.props[9] !== 0 || this.props[10] !== 1 || this.props[11] !== 0 || this.props[12] !== 0 || this.props[13] !== 0 || this.props[14] !== 0 || this.props[15] !== 1);
  213. this._identityCalculated = true;
  214. }
  215. return this._identity;
  216. }
  217. function equals(matr) {
  218. var i = 0;
  219. while (i < 16) {
  220. if (matr.props[i] !== this.props[i]) {
  221. return false;
  222. }
  223. i += 1;
  224. }
  225. return true;
  226. }
  227. function clone(matr) {
  228. var i;
  229. for (i = 0; i < 16; i += 1) {
  230. matr.props[i] = this.props[i];
  231. }
  232. return matr;
  233. }
  234. function cloneFromProps(props) {
  235. var i;
  236. for (i = 0; i < 16; i += 1) {
  237. this.props[i] = props[i];
  238. }
  239. }
  240. function applyToPoint(x, y, z) {
  241. return {
  242. x: x * this.props[0] + y * this.props[4] + z * this.props[8] + this.props[12],
  243. y: x * this.props[1] + y * this.props[5] + z * this.props[9] + this.props[13],
  244. z: x * this.props[2] + y * this.props[6] + z * this.props[10] + this.props[14],
  245. };
  246. /* return {
  247. x: x * me.a + y * me.c + me.e,
  248. y: x * me.b + y * me.d + me.f
  249. }; */
  250. }
  251. function applyToX(x, y, z) {
  252. return x * this.props[0] + y * this.props[4] + z * this.props[8] + this.props[12];
  253. }
  254. function applyToY(x, y, z) {
  255. return x * this.props[1] + y * this.props[5] + z * this.props[9] + this.props[13];
  256. }
  257. function applyToZ(x, y, z) {
  258. return x * this.props[2] + y * this.props[6] + z * this.props[10] + this.props[14];
  259. }
  260. function getInverseMatrix() {
  261. var determinant = this.props[0] * this.props[5] - this.props[1] * this.props[4];
  262. var a = this.props[5] / determinant;
  263. var b = -this.props[1] / determinant;
  264. var c = -this.props[4] / determinant;
  265. var d = this.props[0] / determinant;
  266. var e = (this.props[4] * this.props[13] - this.props[5] * this.props[12]) / determinant;
  267. var f = -(this.props[0] * this.props[13] - this.props[1] * this.props[12]) / determinant;
  268. var inverseMatrix = new Matrix();
  269. inverseMatrix.props[0] = a;
  270. inverseMatrix.props[1] = b;
  271. inverseMatrix.props[4] = c;
  272. inverseMatrix.props[5] = d;
  273. inverseMatrix.props[12] = e;
  274. inverseMatrix.props[13] = f;
  275. return inverseMatrix;
  276. }
  277. function inversePoint(pt) {
  278. var inverseMatrix = this.getInverseMatrix();
  279. return inverseMatrix.applyToPointArray(pt[0], pt[1], pt[2] || 0);
  280. }
  281. function inversePoints(pts) {
  282. var i;
  283. var len = pts.length;
  284. var retPts = [];
  285. for (i = 0; i < len; i += 1) {
  286. retPts[i] = inversePoint(pts[i]);
  287. }
  288. return retPts;
  289. }
  290. function applyToTriplePoints(pt1, pt2, pt3) {
  291. var arr = createTypedArray('float32', 6);
  292. if (this.isIdentity()) {
  293. arr[0] = pt1[0];
  294. arr[1] = pt1[1];
  295. arr[2] = pt2[0];
  296. arr[3] = pt2[1];
  297. arr[4] = pt3[0];
  298. arr[5] = pt3[1];
  299. } else {
  300. var p0 = this.props[0];
  301. var p1 = this.props[1];
  302. var p4 = this.props[4];
  303. var p5 = this.props[5];
  304. var p12 = this.props[12];
  305. var p13 = this.props[13];
  306. arr[0] = pt1[0] * p0 + pt1[1] * p4 + p12;
  307. arr[1] = pt1[0] * p1 + pt1[1] * p5 + p13;
  308. arr[2] = pt2[0] * p0 + pt2[1] * p4 + p12;
  309. arr[3] = pt2[0] * p1 + pt2[1] * p5 + p13;
  310. arr[4] = pt3[0] * p0 + pt3[1] * p4 + p12;
  311. arr[5] = pt3[0] * p1 + pt3[1] * p5 + p13;
  312. }
  313. return arr;
  314. }
  315. function applyToPointArray(x, y, z) {
  316. var arr;
  317. if (this.isIdentity()) {
  318. arr = [x, y, z];
  319. } else {
  320. arr = [
  321. x * this.props[0] + y * this.props[4] + z * this.props[8] + this.props[12],
  322. x * this.props[1] + y * this.props[5] + z * this.props[9] + this.props[13],
  323. x * this.props[2] + y * this.props[6] + z * this.props[10] + this.props[14],
  324. ];
  325. }
  326. return arr;
  327. }
  328. function applyToPointStringified(x, y) {
  329. if (this.isIdentity()) {
  330. return x + ',' + y;
  331. }
  332. var _p = this.props;
  333. return Math.round((x * _p[0] + y * _p[4] + _p[12]) * 100) / 100 + ',' + Math.round((x * _p[1] + y * _p[5] + _p[13]) * 100) / 100;
  334. }
  335. function toCSS() {
  336. // Doesn't make much sense to add this optimization. If it is an identity matrix, it's very likely this will get called only once since it won't be keyframed.
  337. /* if(this.isIdentity()) {
  338. return '';
  339. } */
  340. var i = 0;
  341. var props = this.props;
  342. var cssValue = 'matrix3d(';
  343. var v = 10000;
  344. while (i < 16) {
  345. cssValue += _rnd(props[i] * v) / v;
  346. cssValue += i === 15 ? ')' : ',';
  347. i += 1;
  348. }
  349. return cssValue;
  350. }
  351. function roundMatrixProperty(val) {
  352. var v = 10000;
  353. if ((val < 0.000001 && val > 0) || (val > -0.000001 && val < 0)) {
  354. return _rnd(val * v) / v;
  355. }
  356. return val;
  357. }
  358. function to2dCSS() {
  359. // Doesn't make much sense to add this optimization. If it is an identity matrix, it's very likely this will get called only once since it won't be keyframed.
  360. /* if(this.isIdentity()) {
  361. return '';
  362. } */
  363. var props = this.props;
  364. var _a = roundMatrixProperty(props[0]);
  365. var _b = roundMatrixProperty(props[1]);
  366. var _c = roundMatrixProperty(props[4]);
  367. var _d = roundMatrixProperty(props[5]);
  368. var _e = roundMatrixProperty(props[12]);
  369. var _f = roundMatrixProperty(props[13]);
  370. return 'matrix(' + _a + ',' + _b + ',' + _c + ',' + _d + ',' + _e + ',' + _f + ')';
  371. }
  372. return function () {
  373. this.reset = reset;
  374. this.rotate = rotate;
  375. this.rotateX = rotateX;
  376. this.rotateY = rotateY;
  377. this.rotateZ = rotateZ;
  378. this.skew = skew;
  379. this.skewFromAxis = skewFromAxis;
  380. this.shear = shear;
  381. this.scale = scale;
  382. this.setTransform = setTransform;
  383. this.translate = translate;
  384. this.transform = transform;
  385. this.multiply = multiply;
  386. this.applyToPoint = applyToPoint;
  387. this.applyToX = applyToX;
  388. this.applyToY = applyToY;
  389. this.applyToZ = applyToZ;
  390. this.applyToPointArray = applyToPointArray;
  391. this.applyToTriplePoints = applyToTriplePoints;
  392. this.applyToPointStringified = applyToPointStringified;
  393. this.toCSS = toCSS;
  394. this.to2dCSS = to2dCSS;
  395. this.clone = clone;
  396. this.cloneFromProps = cloneFromProps;
  397. this.equals = equals;
  398. this.inversePoints = inversePoints;
  399. this.inversePoint = inversePoint;
  400. this.getInverseMatrix = getInverseMatrix;
  401. this._t = this.transform;
  402. this.isIdentity = isIdentity;
  403. this._identity = true;
  404. this._identityCalculated = false;
  405. this.props = createTypedArray('float32', 16);
  406. this.reset();
  407. };
  408. }());
  409. export default Matrix;