matrix.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.matrixToTranslation = exports.matrixToRotation = exports.matrixToScale = exports.decomposeMatrix = exports.parseTransformString = exports.matrixToTransformString = exports.transformStringToMatrix = exports.createSVGTransform = exports.createSVGMatrix = exports.createSVGPoint = void 0;
  4. const elem_1 = require("./elem");
  5. const transformRegex = /(\w+)\(([^,)]+),?([^)]+)?\)/gi;
  6. const transformSeparatorRegex = /[ ,]+/;
  7. const transformationListRegex = /^(\w+)\((.*)\)/;
  8. /**
  9. * Returns a SVG point object initialized with the `x` and `y` coordinates.
  10. * @see https://developer.mozilla.org/en/docs/Web/API/SVGPoint
  11. */
  12. function createSVGPoint(x, y) {
  13. const svgDocument = (0, elem_1.createSvgElement)('svg');
  14. const p = svgDocument.createSVGPoint();
  15. p.x = x;
  16. p.y = y;
  17. return p;
  18. }
  19. exports.createSVGPoint = createSVGPoint;
  20. /**
  21. * Returns the SVG transformation matrix initialized with the given matrix.
  22. *
  23. * The given matrix is an object of the form:
  24. * {
  25. * a: number
  26. * b: number
  27. * c: number
  28. * d: number
  29. * e: number
  30. * f: number
  31. * }
  32. *
  33. * @see https://developer.mozilla.org/en/docs/Web/API/SVGMatrix
  34. */
  35. function createSVGMatrix(matrix) {
  36. const svgDocument = (0, elem_1.createSvgElement)('svg');
  37. const mat = svgDocument.createSVGMatrix();
  38. if (matrix != null) {
  39. const source = matrix;
  40. const target = mat;
  41. // eslint-disable-next-line
  42. for (const key in source) {
  43. target[key] = source[key];
  44. }
  45. }
  46. return mat;
  47. }
  48. exports.createSVGMatrix = createSVGMatrix;
  49. /**
  50. * Returns a SVG transform object.
  51. * @see https://developer.mozilla.org/en/docs/Web/API/SVGTransform
  52. */
  53. function createSVGTransform(matrix) {
  54. const svgDocument = (0, elem_1.createSvgElement)('svg');
  55. if (matrix != null) {
  56. if (!(matrix instanceof DOMMatrix)) {
  57. matrix = createSVGMatrix(matrix); // eslint-disable-line
  58. }
  59. return svgDocument.createSVGTransformFromMatrix(matrix);
  60. }
  61. return svgDocument.createSVGTransform();
  62. }
  63. exports.createSVGTransform = createSVGTransform;
  64. /**
  65. * Returns the SVG transformation matrix built from the `transformString`.
  66. *
  67. * E.g. 'translate(10,10) scale(2,2)' will result in matrix:
  68. * `{ a: 2, b: 0, c: 0, d: 2, e: 10, f: 10}`
  69. */
  70. function transformStringToMatrix(transform) {
  71. let mat = createSVGMatrix();
  72. const matches = transform != null && transform.match(transformRegex);
  73. if (!matches) {
  74. return mat;
  75. }
  76. for (let i = 0, n = matches.length; i < n; i += 1) {
  77. const transformationString = matches[i];
  78. const transformationMatch = transformationString.match(transformationListRegex);
  79. if (transformationMatch) {
  80. let sx;
  81. let sy;
  82. let tx;
  83. let ty;
  84. let angle;
  85. let ctm = createSVGMatrix();
  86. const args = transformationMatch[2].split(transformSeparatorRegex);
  87. switch (transformationMatch[1].toLowerCase()) {
  88. case 'scale':
  89. sx = parseFloat(args[0]);
  90. sy = args[1] === undefined ? sx : parseFloat(args[1]);
  91. ctm = ctm.scaleNonUniform(sx, sy);
  92. break;
  93. case 'translate':
  94. tx = parseFloat(args[0]);
  95. ty = parseFloat(args[1]);
  96. ctm = ctm.translate(tx, ty);
  97. break;
  98. case 'rotate':
  99. angle = parseFloat(args[0]);
  100. tx = parseFloat(args[1]) || 0;
  101. ty = parseFloat(args[2]) || 0;
  102. if (tx !== 0 || ty !== 0) {
  103. ctm = ctm.translate(tx, ty).rotate(angle).translate(-tx, -ty);
  104. }
  105. else {
  106. ctm = ctm.rotate(angle);
  107. }
  108. break;
  109. case 'skewx':
  110. angle = parseFloat(args[0]);
  111. ctm = ctm.skewX(angle);
  112. break;
  113. case 'skewy':
  114. angle = parseFloat(args[0]);
  115. ctm = ctm.skewY(angle);
  116. break;
  117. case 'matrix':
  118. ctm.a = parseFloat(args[0]);
  119. ctm.b = parseFloat(args[1]);
  120. ctm.c = parseFloat(args[2]);
  121. ctm.d = parseFloat(args[3]);
  122. ctm.e = parseFloat(args[4]);
  123. ctm.f = parseFloat(args[5]);
  124. break;
  125. default:
  126. continue;
  127. }
  128. mat = mat.multiply(ctm);
  129. }
  130. }
  131. return mat;
  132. }
  133. exports.transformStringToMatrix = transformStringToMatrix;
  134. function matrixToTransformString(matrix) {
  135. const m = matrix || {};
  136. const a = m.a != null ? m.a : 1;
  137. const b = m.b != null ? m.b : 0;
  138. const c = m.c != null ? m.c : 0;
  139. const d = m.d != null ? m.d : 1;
  140. const e = m.e != null ? m.e : 0;
  141. const f = m.f != null ? m.f : 0;
  142. return `matrix(${a},${b},${c},${d},${e},${f})`;
  143. }
  144. exports.matrixToTransformString = matrixToTransformString;
  145. function parseTransformString(transform) {
  146. let translation;
  147. let rotation;
  148. let scale;
  149. if (transform) {
  150. const separator = transformSeparatorRegex;
  151. // Allow reading transform string with a single matrix
  152. if (transform.trim().indexOf('matrix') >= 0) {
  153. const matrix = transformStringToMatrix(transform);
  154. const decomposedMatrix = decomposeMatrix(matrix);
  155. translation = [decomposedMatrix.translateX, decomposedMatrix.translateY];
  156. rotation = [decomposedMatrix.rotation];
  157. scale = [decomposedMatrix.scaleX, decomposedMatrix.scaleY];
  158. const transformations = [];
  159. if (translation[0] !== 0 || translation[1] !== 0) {
  160. transformations.push(`translate(${translation.join(',')})`);
  161. }
  162. if (scale[0] !== 1 || scale[1] !== 1) {
  163. transformations.push(`scale(${scale.join(',')})`);
  164. }
  165. if (rotation[0] !== 0) {
  166. transformations.push(`rotate(${rotation[0]})`);
  167. }
  168. transform = transformations.join(' '); // eslint-disable-line
  169. }
  170. else {
  171. const translateMatch = transform.match(/translate\((.*?)\)/);
  172. if (translateMatch) {
  173. translation = translateMatch[1].split(separator);
  174. }
  175. const rotateMatch = transform.match(/rotate\((.*?)\)/);
  176. if (rotateMatch) {
  177. rotation = rotateMatch[1].split(separator);
  178. }
  179. const scaleMatch = transform.match(/scale\((.*?)\)/);
  180. if (scaleMatch) {
  181. scale = scaleMatch[1].split(separator);
  182. }
  183. }
  184. }
  185. const sx = scale && scale[0] ? parseFloat(scale[0]) : 1;
  186. return {
  187. raw: transform || '',
  188. translation: {
  189. tx: translation && translation[0]
  190. ? parseInt(translation[0], 10)
  191. : 0,
  192. ty: translation && translation[1]
  193. ? parseInt(translation[1], 10)
  194. : 0,
  195. },
  196. rotation: {
  197. angle: rotation && rotation[0] ? parseInt(rotation[0], 10) : 0,
  198. cx: rotation && rotation[1]
  199. ? parseInt(rotation[1], 10)
  200. : undefined,
  201. cy: rotation && rotation[2]
  202. ? parseInt(rotation[2], 10)
  203. : undefined,
  204. },
  205. scale: {
  206. sx,
  207. sy: scale && scale[1] ? parseFloat(scale[1]) : sx,
  208. },
  209. };
  210. }
  211. exports.parseTransformString = parseTransformString;
  212. function deltaTransformPoint(matrix, point) {
  213. const dx = point.x * matrix.a + point.y * matrix.c + 0;
  214. const dy = point.x * matrix.b + point.y * matrix.d + 0;
  215. return { x: dx, y: dy };
  216. }
  217. /**
  218. * Decomposes the SVG transformation matrix into separate transformations.
  219. *
  220. * Returns an object of the form:
  221. * {
  222. * translateX: number
  223. * translateY: number
  224. * scaleX: number
  225. * scaleY: number
  226. * skewX: number
  227. * skewY: number
  228. * rotation: number
  229. * }
  230. *
  231. * @see https://developer.mozilla.org/en/docs/Web/API/SVGMatrix
  232. */
  233. function decomposeMatrix(matrix) {
  234. // @see https://gist.github.com/2052247
  235. const px = deltaTransformPoint(matrix, { x: 0, y: 1 });
  236. const py = deltaTransformPoint(matrix, { x: 1, y: 0 });
  237. const skewX = (180 / Math.PI) * Math.atan2(px.y, px.x) - 90;
  238. const skewY = (180 / Math.PI) * Math.atan2(py.y, py.x);
  239. return {
  240. skewX,
  241. skewY,
  242. translateX: matrix.e,
  243. translateY: matrix.f,
  244. scaleX: Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b),
  245. scaleY: Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d),
  246. rotation: skewX,
  247. };
  248. }
  249. exports.decomposeMatrix = decomposeMatrix;
  250. function matrixToScale(matrix) {
  251. let a;
  252. let b;
  253. let c;
  254. let d;
  255. if (matrix) {
  256. a = matrix.a == null ? 1 : matrix.a;
  257. d = matrix.d == null ? 1 : matrix.d;
  258. b = matrix.b;
  259. c = matrix.c;
  260. }
  261. else {
  262. a = d = 1;
  263. }
  264. return {
  265. sx: b ? Math.sqrt(a * a + b * b) : a,
  266. sy: c ? Math.sqrt(c * c + d * d) : d,
  267. };
  268. }
  269. exports.matrixToScale = matrixToScale;
  270. function matrixToRotation(matrix) {
  271. let p = { x: 0, y: 1 };
  272. if (matrix) {
  273. p = deltaTransformPoint(matrix, p);
  274. }
  275. const deg = (((180 * Math.atan2(p.y, p.x)) / Math.PI) % 360) - 90;
  276. const angle = (deg % 360) + (deg < 0 ? 360 : 0);
  277. return {
  278. angle,
  279. };
  280. }
  281. exports.matrixToRotation = matrixToRotation;
  282. function matrixToTranslation(matrix) {
  283. return {
  284. tx: (matrix && matrix.e) || 0,
  285. ty: (matrix && matrix.f) || 0,
  286. };
  287. }
  288. exports.matrixToTranslation = matrixToTranslation;
  289. //# sourceMappingURL=matrix.js.map