matrix.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. import { Point } from "./objects/point";
  2. const PI_2 = Math.PI * 2;
  3. /**
  4. * | a | c | tx|
  5. * | b | d | ty|
  6. * | 0 | 0 | 1 |
  7. * ```
  8. */
  9. export class Matrix {
  10. a = 1;
  11. b = 0;
  12. c = 0;
  13. d = 1;
  14. tx = 0;
  15. ty = 0;
  16. constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) {
  17. this.a = a;
  18. this.b = b;
  19. this.c = c;
  20. this.d = d;
  21. this.tx = tx;
  22. this.ty = ty;
  23. }
  24. static createFromDiv(div: HTMLElement) {
  25. const out = new Matrix();
  26. out.setFormDiv(div);
  27. return out;
  28. }
  29. //matrix(1,0,0,1,0,0)
  30. static createFromMatrixStr(str: string) {
  31. const out = new Matrix();
  32. out.setMatrixStr(str);
  33. return out;
  34. }
  35. setMatrixStr(transformMatrix:string) {
  36. const values = transformMatrix.split("(")[1].split(")")[0].split(",");
  37. this.a = +values[0];
  38. this.b = +values[1];
  39. this.c = +values[2];
  40. this.d = +values[3];
  41. this.tx = +values[4];
  42. this.ty = +values[5];
  43. }
  44. setFormDiv(div: HTMLElement) {
  45. let transformMatrix = window
  46. .getComputedStyle(div)
  47. .getPropertyValue("transform");
  48. if (!transformMatrix || transformMatrix === "none") {
  49. transformMatrix = "matrix(1,0,0,1,0,0)";
  50. }
  51. this.setMatrixStr(transformMatrix);
  52. }
  53. applyDiv(div: HTMLElement) {
  54. div.style.transform = this.getMatrixStr();
  55. div.style.transformOrigin = "0 0";
  56. return this;
  57. }
  58. getMatrixStr() {
  59. return `matrix(${this.a},${this.b},${this.c},${this.d},${this.tx},${this.ty})`;
  60. }
  61. fromArray(array: number[]) {
  62. this.a = array[0];
  63. this.b = array[1];
  64. this.c = array[3];
  65. this.d = array[4];
  66. this.tx = array[2];
  67. this.ty = array[5];
  68. }
  69. set(a: number, b: number, c: number, d: number, tx: number, ty: number) {
  70. this.a = a;
  71. this.b = b;
  72. this.c = c;
  73. this.d = d;
  74. this.tx = tx;
  75. this.ty = ty;
  76. return this;
  77. }
  78. array = new Float32Array(9);
  79. toArray(transpose: boolean, out: any) {
  80. if (!this.array) {
  81. this.array = new Float32Array(9);
  82. }
  83. const array = out || this.array;
  84. if (transpose) {
  85. array[0] = this.a;
  86. array[1] = this.b;
  87. array[2] = 0;
  88. array[3] = this.c;
  89. array[4] = this.d;
  90. array[5] = 0;
  91. array[6] = this.tx;
  92. array[7] = this.ty;
  93. array[8] = 1;
  94. } else {
  95. array[0] = this.a;
  96. array[1] = this.c;
  97. array[2] = this.tx;
  98. array[3] = this.b;
  99. array[4] = this.d;
  100. array[5] = this.ty;
  101. array[6] = 0;
  102. array[7] = 0;
  103. array[8] = 1;
  104. }
  105. return array;
  106. }
  107. apply(pos: Point, newPos?: Point) {
  108. newPos = newPos || new Point();
  109. const x = pos.x;
  110. const y = pos.y;
  111. newPos.x = this.a * x + this.c * y + this.tx;
  112. newPos.y = this.b * x + this.d * y + this.ty;
  113. return newPos;
  114. }
  115. applyInverse(pos: Point, newPos?: Point) {
  116. newPos = newPos || new Point();
  117. const id = 1 / (this.a * this.d + this.c * -this.b);
  118. const x = pos.x;
  119. const y = pos.y;
  120. newPos.x =
  121. this.d * id * x +
  122. -this.c * id * y +
  123. (this.ty * this.c - this.tx * this.d) * id;
  124. newPos.y =
  125. this.a * id * y +
  126. -this.b * id * x +
  127. (-this.ty * this.a + this.tx * this.b) * id;
  128. return newPos;
  129. }
  130. translate(x: number, y: number) {
  131. this.tx += x;
  132. this.ty += y;
  133. return this;
  134. }
  135. scale(x: number, y: number) {
  136. this.a *= x;
  137. this.d *= y;
  138. this.c *= x;
  139. this.b *= y;
  140. this.tx *= x;
  141. this.ty *= y;
  142. return this;
  143. }
  144. getScale() {
  145. const a = this.a, b = this.b, c = this.c, d = this.d;
  146. const x = Math.sqrt((a * a) + (b * b));
  147. const y = Math.sqrt((c * c) + (d * d));
  148. return {x, y};
  149. }
  150. rotate(angle: number) {
  151. const cos = Math.cos(angle);
  152. const sin = Math.sin(angle);
  153. const a1 = this.a;
  154. const c1 = this.c;
  155. const tx1 = this.tx;
  156. this.a = a1 * cos - this.b * sin;
  157. this.b = a1 * sin + this.b * cos;
  158. this.c = c1 * cos - this.d * sin;
  159. this.d = c1 * sin + this.d * cos;
  160. this.tx = tx1 * cos - this.ty * sin;
  161. this.ty = tx1 * sin + this.ty * cos;
  162. return this;
  163. }
  164. append(matrix: Matrix) {
  165. const a1 = this.a;
  166. const b1 = this.b;
  167. const c1 = this.c;
  168. const d1 = this.d;
  169. this.a = matrix.a * a1 + matrix.b * c1;
  170. this.b = matrix.a * b1 + matrix.b * d1;
  171. this.c = matrix.c * a1 + matrix.d * c1;
  172. this.d = matrix.c * b1 + matrix.d * d1;
  173. this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx;
  174. this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty;
  175. return this;
  176. }
  177. setTransform(
  178. x: number,
  179. y: number,
  180. pivotX: number,
  181. pivotY: number,
  182. scaleX: number,
  183. scaleY: number,
  184. rotation: number,
  185. skewX: number,
  186. skewY: number
  187. ) {
  188. this.a = Math.cos(rotation + skewY) * scaleX;
  189. this.b = Math.sin(rotation + skewY) * scaleX;
  190. this.c = -Math.sin(rotation - skewX) * scaleY;
  191. this.d = Math.cos(rotation - skewX) * scaleY;
  192. this.tx = x - (pivotX * this.a + pivotY * this.c);
  193. this.ty = y - (pivotX * this.b + pivotY * this.d);
  194. return this;
  195. }
  196. prepend(matrix: Matrix) {
  197. const tx1 = this.tx;
  198. if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1) {
  199. const a1 = this.a;
  200. const c1 = this.c;
  201. this.a = a1 * matrix.a + this.b * matrix.c;
  202. this.b = a1 * matrix.b + this.b * matrix.d;
  203. this.c = c1 * matrix.a + this.d * matrix.c;
  204. this.d = c1 * matrix.b + this.d * matrix.d;
  205. }
  206. this.tx = tx1 * matrix.a + this.ty * matrix.c + matrix.tx;
  207. this.ty = tx1 * matrix.b + this.ty * matrix.d + matrix.ty;
  208. return this;
  209. }
  210. decompose(transform:any)
  211. {
  212. // sort out rotation / skew..
  213. const a = this.a;
  214. const b = this.b;
  215. const c = this.c;
  216. const d = this.d;
  217. const skewX = -Math.atan2(-c, d);
  218. const skewY = Math.atan2(b, a);
  219. const delta = Math.abs(skewX + skewY);
  220. transform.rotation = skewY;
  221. transform.skew.x = transform.skew.y = 0;
  222. if (delta < 0.00001 || Math.abs(PI_2 - delta) < 0.00001)
  223. {
  224. transform.rotation = skewY;
  225. transform.skew.x = transform.skew.y = 0;
  226. }
  227. else
  228. {
  229. transform.rotation = 0;
  230. transform.skew.x = skewX;
  231. transform.skew.y = skewY;
  232. }
  233. // next set scale
  234. transform.scale.x = Math.sqrt((a * a) + (b * b));
  235. transform.scale.y = Math.sqrt((c * c) + (d * d));
  236. // next set position
  237. transform.position.x = this.tx;
  238. transform.position.y = this.ty;
  239. return transform;
  240. }
  241. invert() {
  242. const a1 = this.a;
  243. const b1 = this.b;
  244. const c1 = this.c;
  245. const d1 = this.d;
  246. const tx1 = this.tx;
  247. const n = a1 * d1 - b1 * c1;
  248. this.a = d1 / n;
  249. this.b = -b1 / n;
  250. this.c = -c1 / n;
  251. this.d = a1 / n;
  252. this.tx = (c1 * this.ty - d1 * tx1) / n;
  253. this.ty = -(a1 * this.ty - b1 * tx1) / n;
  254. return this;
  255. }
  256. identity() {
  257. this.a = 1;
  258. this.b = 0;
  259. this.c = 0;
  260. this.d = 1;
  261. this.tx = 0;
  262. this.ty = 0;
  263. return this;
  264. }
  265. clone() {
  266. const matrix = new Matrix();
  267. matrix.a = this.a;
  268. matrix.b = this.b;
  269. matrix.c = this.c;
  270. matrix.d = this.d;
  271. matrix.tx = this.tx;
  272. matrix.ty = this.ty;
  273. return matrix;
  274. }
  275. copyTo(matrix: Matrix) {
  276. matrix.a = this.a;
  277. matrix.b = this.b;
  278. matrix.c = this.c;
  279. matrix.d = this.d;
  280. matrix.tx = this.tx;
  281. matrix.ty = this.ty;
  282. return matrix;
  283. }
  284. copyFrom(matrix: Matrix) {
  285. this.a = matrix.a;
  286. this.b = matrix.b;
  287. this.c = matrix.c;
  288. this.d = matrix.d;
  289. this.tx = matrix.tx;
  290. this.ty = matrix.ty;
  291. return this;
  292. }
  293. static get IDENTITY() {
  294. return new Matrix();
  295. }
  296. static get TEMP_MATRIX() {
  297. return new Matrix();
  298. }
  299. }