geometry.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. const { inspect } = require('util')
  2. /*
  3. * vendored in order to fix its dependence on the window global [cds 2020/08/04]
  4. * otherwise unchanged from https://github.com/jarek-foksa/geometry-polyfill/tree/f36bbc8f4bc43539d980687904ce46c8e915543d
  5. */
  6. // @info
  7. // DOMPoint polyfill
  8. // @src
  9. // https://drafts.fxtf.org/geometry/#DOMPoint
  10. // https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_point_read_only.cc
  11. class DOMPoint {
  12. constructor(x = 0, y = 0, z = 0, w = 1) {
  13. this.x = x
  14. this.y = y
  15. this.z = z
  16. this.w = w
  17. }
  18. static fromPoint(otherPoint) {
  19. return new DOMPoint(
  20. otherPoint.x,
  21. otherPoint.y,
  22. otherPoint.z !== undefined ? otherPoint.z : 0,
  23. otherPoint.w !== undefined ? otherPoint.w : 1,
  24. )
  25. }
  26. matrixTransform(matrix) {
  27. if ((matrix.is2D || matrix instanceof SVGMatrix) && this.z === 0 && this.w === 1) {
  28. return new DOMPoint(
  29. this.x * matrix.a + this.y * matrix.c + matrix.e,
  30. this.x * matrix.b + this.y * matrix.d + matrix.f,
  31. 0,
  32. 1,
  33. )
  34. } else {
  35. return new DOMPoint(
  36. this.x * matrix.m11 + this.y * matrix.m21 + this.z * matrix.m31 + this.w * matrix.m41,
  37. this.x * matrix.m12 + this.y * matrix.m22 + this.z * matrix.m32 + this.w * matrix.m42,
  38. this.x * matrix.m13 + this.y * matrix.m23 + this.z * matrix.m33 + this.w * matrix.m43,
  39. this.x * matrix.m14 + this.y * matrix.m24 + this.z * matrix.m34 + this.w * matrix.m44,
  40. )
  41. }
  42. }
  43. toJSON() {
  44. return {
  45. x: this.x,
  46. y: this.y,
  47. z: this.z,
  48. w: this.w,
  49. }
  50. }
  51. }
  52. // @info
  53. // DOMRect polyfill
  54. // @src
  55. // https://drafts.fxtf.org/geometry/#DOMRect
  56. // https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_rect_read_only.cc
  57. class DOMRect {
  58. constructor(x = 0, y = 0, width = 0, height = 0) {
  59. this.x = x
  60. this.y = y
  61. this.width = width
  62. this.height = height
  63. }
  64. static fromRect(otherRect) {
  65. return new DOMRect(otherRect.x, otherRect.y, otherRect.width, otherRect.height)
  66. }
  67. get top() {
  68. return this.y
  69. }
  70. get left() {
  71. return this.x
  72. }
  73. get right() {
  74. return this.x + this.width
  75. }
  76. get bottom() {
  77. return this.y + this.height
  78. }
  79. toJSON() {
  80. return {
  81. x: this.x,
  82. y: this.y,
  83. width: this.width,
  84. height: this.height,
  85. top: this.top,
  86. left: this.left,
  87. right: this.right,
  88. bottom: this.bottom,
  89. }
  90. }
  91. }
  92. for (const propertyName of ['top', 'right', 'bottom', 'left']) {
  93. const propertyDescriptor = Object.getOwnPropertyDescriptor(DOMRect.prototype, propertyName)
  94. propertyDescriptor.enumerable = true
  95. Object.defineProperty(DOMRect.prototype, propertyName, propertyDescriptor)
  96. }
  97. // @info
  98. // DOMMatrix polyfill (SVG 2)
  99. // @src
  100. // https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
  101. // https://github.com/tocharomera/generativecanvas/blob/master/node-canvas/lib/DOMMatrix.js
  102. const M11 = 0,
  103. M12 = 1,
  104. M13 = 2,
  105. M14 = 3
  106. const M21 = 4,
  107. M22 = 5,
  108. M23 = 6,
  109. M24 = 7
  110. const M31 = 8,
  111. M32 = 9,
  112. M33 = 10,
  113. M34 = 11
  114. const M41 = 12,
  115. M42 = 13,
  116. M43 = 14,
  117. M44 = 15
  118. const A = M11,
  119. B = M12
  120. const C = M21,
  121. D = M22
  122. const E = M41,
  123. F = M42
  124. const DEGREE_PER_RAD = 180 / Math.PI
  125. const RAD_PER_DEGREE = Math.PI / 180
  126. const VALUES = Symbol('values')
  127. const IS_2D = Symbol('is2D')
  128. function parseMatrix(init) {
  129. let parsed = init.replace(/matrix\(/, '').split(/,/, 7)
  130. if (parsed.length !== 6) {
  131. throw new Error(`Failed to parse ${init}`)
  132. }
  133. parsed = parsed.map(parseFloat)
  134. return [parsed[0], parsed[1], 0, 0, parsed[2], parsed[3], 0, 0, 0, 0, 1, 0, parsed[4], parsed[5], 0, 1]
  135. }
  136. function parseMatrix3d(init) {
  137. const parsed = init.replace(/matrix3d\(/, '').split(/,/, 17)
  138. if (parsed.length !== 16) {
  139. throw new Error(`Failed to parse ${init}`)
  140. }
  141. return parsed.map(parseFloat)
  142. }
  143. function parseTransform(tform) {
  144. const type = tform.split(/\(/, 1)[0]
  145. if (type === 'matrix') {
  146. return parseMatrix(tform)
  147. } else if (type === 'matrix3d') {
  148. return parseMatrix3d(tform)
  149. } else {
  150. throw new Error(`${type} parsing not implemented`)
  151. }
  152. }
  153. const setNumber2D = (receiver, index, value) => {
  154. if (typeof value !== 'number') {
  155. throw new TypeError('Expected number')
  156. }
  157. receiver[VALUES][index] = value
  158. }
  159. const setNumber3D = (receiver, index, value) => {
  160. if (typeof value !== 'number') {
  161. throw new TypeError('Expected number')
  162. }
  163. if (index === M33 || index === M44) {
  164. if (value !== 1) {
  165. receiver[IS_2D] = false
  166. }
  167. } else if (value !== 0) {
  168. receiver[IS_2D] = false
  169. }
  170. receiver[VALUES][index] = value
  171. }
  172. const newInstance = (values) => {
  173. const instance = Object.create(DOMMatrix.prototype)
  174. instance.constructor = DOMMatrix
  175. instance[IS_2D] = true
  176. instance[VALUES] = values
  177. return instance
  178. }
  179. const multiply = (first, second) => {
  180. const dest = new Float64Array(16)
  181. for (let i = 0; i < 4; i++) {
  182. for (let j = 0; j < 4; j++) {
  183. let sum = 0
  184. for (let k = 0; k < 4; k++) {
  185. sum += first[i * 4 + k] * second[k * 4 + j]
  186. }
  187. dest[i * 4 + j] = sum
  188. }
  189. }
  190. return dest
  191. }
  192. class DOMMatrix {
  193. get m11() {
  194. return this[VALUES][M11]
  195. }
  196. set m11(value) {
  197. setNumber2D(this, M11, value)
  198. }
  199. get m12() {
  200. return this[VALUES][M12]
  201. }
  202. set m12(value) {
  203. setNumber2D(this, M12, value)
  204. }
  205. get m13() {
  206. return this[VALUES][M13]
  207. }
  208. set m13(value) {
  209. setNumber3D(this, M13, value)
  210. }
  211. get m14() {
  212. return this[VALUES][M14]
  213. }
  214. set m14(value) {
  215. setNumber3D(this, M14, value)
  216. }
  217. get m21() {
  218. return this[VALUES][M21]
  219. }
  220. set m21(value) {
  221. setNumber2D(this, M21, value)
  222. }
  223. get m22() {
  224. return this[VALUES][M22]
  225. }
  226. set m22(value) {
  227. setNumber2D(this, M22, value)
  228. }
  229. get m23() {
  230. return this[VALUES][M23]
  231. }
  232. set m23(value) {
  233. setNumber3D(this, M23, value)
  234. }
  235. get m24() {
  236. return this[VALUES][M24]
  237. }
  238. set m24(value) {
  239. setNumber3D(this, M24, value)
  240. }
  241. get m31() {
  242. return this[VALUES][M31]
  243. }
  244. set m31(value) {
  245. setNumber3D(this, M31, value)
  246. }
  247. get m32() {
  248. return this[VALUES][M32]
  249. }
  250. set m32(value) {
  251. setNumber3D(this, M32, value)
  252. }
  253. get m33() {
  254. return this[VALUES][M33]
  255. }
  256. set m33(value) {
  257. setNumber3D(this, M33, value)
  258. }
  259. get m34() {
  260. return this[VALUES][M34]
  261. }
  262. set m34(value) {
  263. setNumber3D(this, M34, value)
  264. }
  265. get m41() {
  266. return this[VALUES][M41]
  267. }
  268. set m41(value) {
  269. setNumber2D(this, M41, value)
  270. }
  271. get m42() {
  272. return this[VALUES][M42]
  273. }
  274. set m42(value) {
  275. setNumber2D(this, M42, value)
  276. }
  277. get m43() {
  278. return this[VALUES][M43]
  279. }
  280. set m43(value) {
  281. setNumber3D(this, M43, value)
  282. }
  283. get m44() {
  284. return this[VALUES][M44]
  285. }
  286. set m44(value) {
  287. setNumber3D(this, M44, value)
  288. }
  289. get a() {
  290. return this[VALUES][A]
  291. }
  292. set a(value) {
  293. setNumber2D(this, A, value)
  294. }
  295. get b() {
  296. return this[VALUES][B]
  297. }
  298. set b(value) {
  299. setNumber2D(this, B, value)
  300. }
  301. get c() {
  302. return this[VALUES][C]
  303. }
  304. set c(value) {
  305. setNumber2D(this, C, value)
  306. }
  307. get d() {
  308. return this[VALUES][D]
  309. }
  310. set d(value) {
  311. setNumber2D(this, D, value)
  312. }
  313. get e() {
  314. return this[VALUES][E]
  315. }
  316. set e(value) {
  317. setNumber2D(this, E, value)
  318. }
  319. get f() {
  320. return this[VALUES][F]
  321. }
  322. set f(value) {
  323. setNumber2D(this, F, value)
  324. }
  325. get is2D() {
  326. return this[IS_2D]
  327. }
  328. get isIdentity() {
  329. const values = this[VALUES]
  330. return (
  331. values[M11] === 1 &&
  332. values[M12] === 0 &&
  333. values[M13] === 0 &&
  334. values[M14] === 0 &&
  335. values[M21] === 0 &&
  336. values[M22] === 1 &&
  337. values[M23] === 0 &&
  338. values[M24] === 0 &&
  339. values[M31] === 0 &&
  340. values[M32] === 0 &&
  341. values[M33] === 1 &&
  342. values[M34] === 0 &&
  343. values[M41] === 0 &&
  344. values[M42] === 0 &&
  345. values[M43] === 0 &&
  346. values[M44] === 1
  347. )
  348. }
  349. static fromMatrix(init) {
  350. if (init instanceof DOMMatrix) {
  351. return new DOMMatrix(init[VALUES])
  352. } else if (init instanceof SVGMatrix) {
  353. return new DOMMatrix([init.a, init.b, init.c, init.d, init.e, init.f])
  354. } else {
  355. throw new TypeError('Expected DOMMatrix')
  356. }
  357. }
  358. static fromFloat32Array(init) {
  359. if (!(init instanceof Float32Array)) throw new TypeError('Expected Float32Array')
  360. return new DOMMatrix(init)
  361. }
  362. static fromFloat64Array(init) {
  363. if (!(init instanceof Float64Array)) throw new TypeError('Expected Float64Array')
  364. return new DOMMatrix(init)
  365. }
  366. // @type
  367. // (Float64Array) => void
  368. constructor(init) {
  369. this[IS_2D] = true
  370. this[VALUES] = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
  371. // Parse CSS transformList
  372. if (typeof init === 'string') {
  373. if (init === '') {
  374. return
  375. } else {
  376. const tforms = init.split(/\)\s+/, 20).map(parseTransform)
  377. if (tforms.length === 0) {
  378. return
  379. }
  380. init = tforms[0]
  381. for (let i = 1; i < tforms.length; i++) {
  382. init = multiply(tforms[i], init)
  383. }
  384. }
  385. }
  386. let i = 0
  387. if (init && init.length === 6) {
  388. setNumber2D(this, A, init[i++])
  389. setNumber2D(this, B, init[i++])
  390. setNumber2D(this, C, init[i++])
  391. setNumber2D(this, D, init[i++])
  392. setNumber2D(this, E, init[i++])
  393. setNumber2D(this, F, init[i++])
  394. } else if (init && init.length === 16) {
  395. setNumber2D(this, M11, init[i++])
  396. setNumber2D(this, M12, init[i++])
  397. setNumber3D(this, M13, init[i++])
  398. setNumber3D(this, M14, init[i++])
  399. setNumber2D(this, M21, init[i++])
  400. setNumber2D(this, M22, init[i++])
  401. setNumber3D(this, M23, init[i++])
  402. setNumber3D(this, M24, init[i++])
  403. setNumber3D(this, M31, init[i++])
  404. setNumber3D(this, M32, init[i++])
  405. setNumber3D(this, M33, init[i++])
  406. setNumber3D(this, M34, init[i++])
  407. setNumber2D(this, M41, init[i++])
  408. setNumber2D(this, M42, init[i++])
  409. setNumber3D(this, M43, init[i++])
  410. setNumber3D(this, M44, init[i])
  411. } else if (init !== undefined) {
  412. throw new TypeError('Expected string or array.')
  413. }
  414. }
  415. dump() {
  416. const mat = this[VALUES]
  417. console.info([mat.slice(0, 4), mat.slice(4, 8), mat.slice(8, 12), mat.slice(12, 16)])
  418. }
  419. [inspect.custom](depth) {
  420. if (depth < 0) return '[DOMMatrix]'
  421. const { a, b, c, d, e, f, is2D, isIdentity } = this
  422. if (this.is2D) {
  423. return `DOMMatrix ${inspect({ a, b, c, d, e, f, is2D, isIdentity }, { colors: true })}`
  424. } else {
  425. const { m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, is2D, isIdentity } = this
  426. return `DOMMatrix ${inspect(
  427. {
  428. a,
  429. b,
  430. c,
  431. d,
  432. e,
  433. f,
  434. m11,
  435. m12,
  436. m13,
  437. m14,
  438. m21,
  439. m22,
  440. m23,
  441. m24,
  442. m31,
  443. m32,
  444. m33,
  445. m34,
  446. m41,
  447. m42,
  448. m43,
  449. m44,
  450. is2D,
  451. isIdentity,
  452. },
  453. { colors: true },
  454. )}`
  455. }
  456. }
  457. multiply(other) {
  458. return newInstance(this[VALUES]).multiplySelf(other)
  459. }
  460. multiplySelf(other) {
  461. this[VALUES] = multiply(other[VALUES], this[VALUES])
  462. if (!other.is2D) {
  463. this[IS_2D] = false
  464. }
  465. return this
  466. }
  467. preMultiplySelf(other) {
  468. this[VALUES] = multiply(this[VALUES], other[VALUES])
  469. if (!other.is2D) {
  470. this[IS_2D] = false
  471. }
  472. return this
  473. }
  474. translate(tx, ty, tz) {
  475. return newInstance(this[VALUES]).translateSelf(tx, ty, tz)
  476. }
  477. translateSelf(tx = 0, ty = 0, tz = 0) {
  478. this[VALUES] = multiply([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1], this[VALUES])
  479. if (tz !== 0) {
  480. this[IS_2D] = false
  481. }
  482. return this
  483. }
  484. scale(scaleX, scaleY, scaleZ, originX, originY, originZ) {
  485. return newInstance(this[VALUES]).scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
  486. }
  487. scale3d(scale, originX, originY, originZ) {
  488. return newInstance(this[VALUES]).scale3dSelf(scale, originX, originY, originZ)
  489. }
  490. scale3dSelf(scale, originX, originY, originZ) {
  491. return this.scaleSelf(scale, scale, scale, originX, originY, originZ)
  492. }
  493. scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ) {
  494. // Not redundant with translate's checks because we need to negate the values later.
  495. if (typeof originX !== 'number') originX = 0
  496. if (typeof originY !== 'number') originY = 0
  497. if (typeof originZ !== 'number') originZ = 0
  498. this.translateSelf(originX, originY, originZ)
  499. if (typeof scaleX !== 'number') scaleX = 1
  500. if (typeof scaleY !== 'number') scaleY = scaleX
  501. if (typeof scaleZ !== 'number') scaleZ = 1
  502. this[VALUES] = multiply([scaleX, 0, 0, 0, 0, scaleY, 0, 0, 0, 0, scaleZ, 0, 0, 0, 0, 1], this[VALUES])
  503. this.translateSelf(-originX, -originY, -originZ)
  504. if (scaleZ !== 1 || originZ !== 0) {
  505. this[IS_2D] = false
  506. }
  507. return this
  508. }
  509. rotateFromVector(x, y) {
  510. return newInstance(this[VALUES]).rotateFromVectorSelf(x, y)
  511. }
  512. rotateFromVectorSelf(x = 0, y = 0) {
  513. const theta = x === 0 && y === 0 ? 0 : Math.atan2(y, x) * DEGREE_PER_RAD
  514. return this.rotateSelf(theta)
  515. }
  516. rotate(rotX, rotY, rotZ) {
  517. return newInstance(this[VALUES]).rotateSelf(rotX, rotY, rotZ)
  518. }
  519. rotateSelf(rotX, rotY, rotZ) {
  520. if (rotY === undefined && rotZ === undefined) {
  521. rotZ = rotX
  522. rotX = rotY = 0
  523. }
  524. if (typeof rotY !== 'number') rotY = 0
  525. if (typeof rotZ !== 'number') rotZ = 0
  526. if (rotX !== 0 || rotY !== 0) {
  527. this[IS_2D] = false
  528. }
  529. rotX *= RAD_PER_DEGREE
  530. rotY *= RAD_PER_DEGREE
  531. rotZ *= RAD_PER_DEGREE
  532. let c = Math.cos(rotZ)
  533. let s = Math.sin(rotZ)
  534. this[VALUES] = multiply([c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
  535. c = Math.cos(rotY)
  536. s = Math.sin(rotY)
  537. this[VALUES] = multiply([c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1], this[VALUES])
  538. c = Math.cos(rotX)
  539. s = Math.sin(rotX)
  540. this[VALUES] = multiply([1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1], this[VALUES])
  541. return this
  542. }
  543. rotateAxisAngle(x, y, z, angle) {
  544. return newInstance(this[VALUES]).rotateAxisAngleSelf(x, y, z, angle)
  545. }
  546. rotateAxisAngleSelf(x = 0, y = 0, z = 0, angle = 0) {
  547. const length = Math.sqrt(x * x + y * y + z * z)
  548. if (length === 0) {
  549. return this
  550. }
  551. if (length !== 1) {
  552. x /= length
  553. y /= length
  554. z /= length
  555. }
  556. angle *= RAD_PER_DEGREE
  557. const c = Math.cos(angle)
  558. const s = Math.sin(angle)
  559. const t = 1 - c
  560. const tx = t * x
  561. const ty = t * y
  562. this[VALUES] = multiply(
  563. [
  564. tx * x + c,
  565. tx * y + s * z,
  566. tx * z - s * y,
  567. 0,
  568. tx * y - s * z,
  569. ty * y + c,
  570. ty * z + s * x,
  571. 0,
  572. tx * z + s * y,
  573. ty * z - s * x,
  574. t * z * z + c,
  575. 0,
  576. 0,
  577. 0,
  578. 0,
  579. 1,
  580. ],
  581. this[VALUES],
  582. )
  583. if (x !== 0 || y !== 0) {
  584. this[IS_2D] = false
  585. }
  586. return this
  587. }
  588. skewX(sx) {
  589. return newInstance(this[VALUES]).skewXSelf(sx)
  590. }
  591. skewXSelf(sx) {
  592. if (typeof sx !== 'number') {
  593. return this
  594. }
  595. const t = Math.tan(sx * RAD_PER_DEGREE)
  596. this[VALUES] = multiply([1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
  597. return this
  598. }
  599. skewY(sy) {
  600. return newInstance(this[VALUES]).skewYSelf(sy)
  601. }
  602. skewYSelf(sy) {
  603. if (typeof sy !== 'number') {
  604. return this
  605. }
  606. const t = Math.tan(sy * RAD_PER_DEGREE)
  607. this[VALUES] = multiply([1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
  608. return this
  609. }
  610. flipX() {
  611. return newInstance(multiply([-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES]))
  612. }
  613. flipY() {
  614. return newInstance(multiply([1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES]))
  615. }
  616. inverse() {
  617. return newInstance(this[VALUES].slice()).invertSelf()
  618. }
  619. invertSelf() {
  620. if (this[IS_2D]) {
  621. const det = this[VALUES][A] * this[VALUES][D] - this[VALUES][B] * this[VALUES][C]
  622. // Invertable
  623. if (det !== 0) {
  624. const newA = this[VALUES][D] / det
  625. const newB = -this[VALUES][B] / det
  626. const newC = -this[VALUES][C] / det
  627. const newD = this[VALUES][A] / det
  628. const newE = (this[VALUES][C] * this[VALUES][F] - this[VALUES][D] * this[VALUES][E]) / det
  629. const newF = (this[VALUES][B] * this[VALUES][E] - this[VALUES][A] * this[VALUES][F]) / det
  630. this.a = newA
  631. this.b = newB
  632. this.c = newC
  633. this.d = newD
  634. this.e = newE
  635. this.f = newF
  636. return this
  637. }
  638. // Not invertable
  639. else {
  640. this[IS_2D] = false
  641. this[VALUES] = [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]
  642. return this
  643. }
  644. } else {
  645. throw new Error('3D matrix inversion is not implemented.')
  646. }
  647. }
  648. setMatrixValue(transformList) {
  649. const temp = new DOMMatrix(transformList)
  650. this[VALUES] = temp[VALUES]
  651. this[IS_2D] = temp[IS_2D]
  652. return this
  653. }
  654. transformPoint(point) {
  655. const x = point.x || 0
  656. const y = point.y || 0
  657. const z = point.z || 0
  658. const w = point.w || 1
  659. const values = this[VALUES]
  660. const nx = values[M11] * x + values[M21] * y + values[M31] * z + values[M41] * w
  661. const ny = values[M12] * x + values[M22] * y + values[M32] * z + values[M42] * w
  662. const nz = values[M13] * x + values[M23] * y + values[M33] * z + values[M43] * w
  663. const nw = values[M14] * x + values[M24] * y + values[M34] * z + values[M44] * w
  664. return new DOMPoint(nx, ny, nz, nw)
  665. }
  666. toFloat32Array() {
  667. return Float32Array.from(this[VALUES])
  668. }
  669. toFloat64Array() {
  670. return this[VALUES].slice(0)
  671. }
  672. toJSON() {
  673. return {
  674. a: this.a,
  675. b: this.b,
  676. c: this.c,
  677. d: this.d,
  678. e: this.e,
  679. f: this.f,
  680. m11: this.m11,
  681. m12: this.m12,
  682. m13: this.m13,
  683. m14: this.m14,
  684. m21: this.m21,
  685. m22: this.m22,
  686. m23: this.m23,
  687. m24: this.m24,
  688. m31: this.m31,
  689. m32: this.m32,
  690. m33: this.m33,
  691. m34: this.m34,
  692. m41: this.m41,
  693. m42: this.m42,
  694. m43: this.m43,
  695. m44: this.m44,
  696. is2D: this.is2D,
  697. isIdentity: this.isIdentity,
  698. }
  699. }
  700. toString() {
  701. if (this.is2D) {
  702. return `matrix(${this.a}, ${this.b}, ${this.c}, ${this.d}, ${this.e}, ${this.f})`
  703. } else {
  704. return `matrix3d(${this[VALUES].join(', ')})`
  705. }
  706. }
  707. }
  708. for (const propertyName of [
  709. 'a',
  710. 'b',
  711. 'c',
  712. 'd',
  713. 'e',
  714. 'f',
  715. 'm11',
  716. 'm12',
  717. 'm13',
  718. 'm14',
  719. 'm21',
  720. 'm22',
  721. 'm23',
  722. 'm24',
  723. 'm31',
  724. 'm32',
  725. 'm33',
  726. 'm34',
  727. 'm41',
  728. 'm42',
  729. 'm43',
  730. 'm44',
  731. 'is2D',
  732. 'isIdentity',
  733. ]) {
  734. const propertyDescriptor = Object.getOwnPropertyDescriptor(DOMMatrix.prototype, propertyName)
  735. propertyDescriptor.enumerable = true
  736. Object.defineProperty(DOMMatrix.prototype, propertyName, propertyDescriptor)
  737. }
  738. module.exports = { DOMPoint, DOMMatrix, DOMRect }