svg-path-commander 1.0.5 → 2.0.0-alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +26 -4
  2. package/dist/svg-path-commander.cjs +2 -0
  3. package/dist/svg-path-commander.cjs.map +1 -0
  4. package/dist/svg-path-commander.d.ts +454 -0
  5. package/dist/svg-path-commander.js +2 -3762
  6. package/dist/svg-path-commander.js.map +1 -0
  7. package/dist/svg-path-commander.mjs +1194 -0
  8. package/dist/svg-path-commander.mjs.map +1 -0
  9. package/package.json +39 -42
  10. package/src/convert/pathToAbsolute.ts +101 -0
  11. package/src/convert/{pathToCurve.js → pathToCurve.ts} +12 -14
  12. package/src/convert/pathToRelative.ts +92 -0
  13. package/src/convert/pathToString.ts +17 -0
  14. package/src/{svg-path-commander.js → index.ts} +143 -66
  15. package/src/interface.ts +129 -0
  16. package/src/math/distanceSquareRoot.ts +13 -0
  17. package/src/math/midPoint.ts +16 -0
  18. package/src/math/{polygonArea.js → polygonArea.ts} +6 -4
  19. package/src/math/{polygonLength.js → polygonLength.ts} +5 -3
  20. package/src/math/rotateVector.ts +16 -0
  21. package/src/options/options.ts +9 -0
  22. package/src/parser/error.ts +2 -0
  23. package/src/parser/finalizeSegment.ts +31 -0
  24. package/src/parser/{invalidPathValue.js → invalidPathValue.ts} +0 -0
  25. package/src/parser/{isArcCommand.js → isArcCommand.ts} +5 -4
  26. package/src/parser/isDigit.ts +12 -0
  27. package/src/parser/isDigitStart.ts +14 -0
  28. package/src/parser/isPathCommand.ts +28 -0
  29. package/src/parser/isSpace.ts +23 -0
  30. package/src/parser/paramsCount.ts +16 -0
  31. package/src/parser/paramsParser.ts +14 -0
  32. package/src/parser/{parsePathString.js → parsePathString.ts} +13 -7
  33. package/src/parser/pathParser.ts +29 -0
  34. package/src/parser/{scanFlag.js → scanFlag.ts} +8 -5
  35. package/src/parser/{scanParam.js → scanParam.ts} +12 -11
  36. package/src/parser/{scanSegment.js → scanSegment.ts} +9 -5
  37. package/src/parser/{skipSpaces.js → skipSpaces.ts} +5 -3
  38. package/src/process/{arcToCubic.js → arcToCubic.ts} +42 -28
  39. package/src/process/fixArc.ts +23 -0
  40. package/src/process/getSVGMatrix.ts +70 -0
  41. package/src/process/lineToCubic.ts +17 -0
  42. package/src/process/{normalizePath.js → normalizePath.ts} +10 -10
  43. package/src/process/normalizeSegment.ts +47 -0
  44. package/src/process/{optimizePath.js → optimizePath.ts} +18 -21
  45. package/src/process/{projection2d.js → projection2d.ts} +16 -15
  46. package/src/process/quadToCubic.ts +31 -0
  47. package/src/process/reverseCurve.ts +21 -0
  48. package/src/process/reversePath.ts +101 -0
  49. package/src/process/roundPath.ts +29 -0
  50. package/src/process/segmentToCubic.ts +46 -0
  51. package/src/process/shortenSegment.ts +79 -0
  52. package/src/process/splitCubic.ts +28 -0
  53. package/src/process/{splitPath.js → splitPath.ts} +9 -8
  54. package/src/process/{transformPath.js → transformPath.ts} +55 -48
  55. package/src/types.ts +193 -0
  56. package/src/util/getClosestPoint.ts +15 -0
  57. package/src/util/{getDrawDirection.js → getDrawDirection.ts} +7 -4
  58. package/src/util/getPathArea.ts +70 -0
  59. package/src/util/{getPathBBox.js → getPathBBox.ts} +16 -5
  60. package/src/util/getPointAtLength.ts +14 -0
  61. package/src/util/{getPropertiesAtLength.js → getPropertiesAtLength.ts} +28 -19
  62. package/src/util/{getPropertiesAtPoint.js → getPropertiesAtPoint.ts} +16 -12
  63. package/src/util/getSegmentAtLength.ts +15 -0
  64. package/src/util/getSegmentOfPoint.ts +18 -0
  65. package/src/util/{getTotalLength.js → getTotalLength.ts} +6 -4
  66. package/src/util/isAbsoluteArray.ts +18 -0
  67. package/src/util/{isCurveArray.js → isCurveArray.ts} +6 -4
  68. package/src/util/{isNormalizedArray.js → isNormalizedArray.ts} +4 -2
  69. package/src/util/isPathArray.ts +19 -0
  70. package/src/util/isPointInStroke.ts +15 -0
  71. package/src/util/isRelativeArray.ts +18 -0
  72. package/src/util/{isValidPath.js → isValidPath.ts} +5 -4
  73. package/src/util/{pathLengthFactory.js → pathLengthFactory.ts} +38 -31
  74. package/src/util/{segmentArcFactory.js → segmentArcFactory.ts} +71 -55
  75. package/src/util/segmentCubicFactory.ts +114 -0
  76. package/src/util/{segmentLineFactory.js → segmentLineFactory.ts} +10 -8
  77. package/src/util/{segmentQuadFactory.js → segmentQuadFactory.ts} +47 -32
  78. package/src/util/shapeToPath.ts +214 -0
  79. package/dist/svg-path-commander.es5.js +0 -3875
  80. package/dist/svg-path-commander.es5.min.js +0 -2
  81. package/dist/svg-path-commander.esm.js +0 -3754
  82. package/dist/svg-path-commander.esm.min.js +0 -2
  83. package/dist/svg-path-commander.min.js +0 -2
  84. package/src/convert/pathToAbsolute.js +0 -86
  85. package/src/convert/pathToRelative.js +0 -84
  86. package/src/convert/pathToString.js +0 -14
  87. package/src/index.js +0 -10
  88. package/src/math/distanceSquareRoot.js +0 -14
  89. package/src/math/epsilon.js +0 -8
  90. package/src/math/midPoint.js +0 -13
  91. package/src/math/rotateVector.js +0 -14
  92. package/src/options/options.js +0 -10
  93. package/src/parser/error.js +0 -2
  94. package/src/parser/finalizeSegment.js +0 -28
  95. package/src/parser/isDigit.js +0 -9
  96. package/src/parser/isDigitStart.js +0 -13
  97. package/src/parser/isPathCommand.js +0 -25
  98. package/src/parser/isSpace.js +0 -16
  99. package/src/parser/paramsCount.js +0 -9
  100. package/src/parser/paramsParser.js +0 -8
  101. package/src/parser/pathParser.js +0 -24
  102. package/src/process/clonePath.js +0 -9
  103. package/src/process/fixArc.js +0 -21
  104. package/src/process/fixPath.js +0 -31
  105. package/src/process/getSVGMatrix.js +0 -61
  106. package/src/process/lineToCubic.js +0 -30
  107. package/src/process/normalizeSegment.js +0 -45
  108. package/src/process/quadToCubic.js +0 -22
  109. package/src/process/reverseCurve.js +0 -18
  110. package/src/process/reversePath.js +0 -89
  111. package/src/process/roundPath.js +0 -26
  112. package/src/process/segmentToCubic.js +0 -46
  113. package/src/process/shortenSegment.js +0 -58
  114. package/src/process/splitCubic.js +0 -26
  115. package/src/util/getClosestPoint.js +0 -12
  116. package/src/util/getPathArea.js +0 -47
  117. package/src/util/getPointAtLength.js +0 -12
  118. package/src/util/getSegmentAtLength.js +0 -11
  119. package/src/util/getSegmentOfPoint.js +0 -12
  120. package/src/util/isAbsoluteArray.js +0 -14
  121. package/src/util/isPathArray.js +0 -14
  122. package/src/util/isPointInStroke.js +0 -13
  123. package/src/util/isRelativeArray.js +0 -14
  124. package/src/util/segmentCubicFactory.js +0 -97
  125. package/src/util/shapeToPath.js +0 -204
  126. package/src/util/util.js +0 -82
  127. package/src/version.js +0 -8
  128. package/types/index.d.ts +0 -120
  129. package/types/more/modules.ts +0 -82
  130. package/types/more/svg.d.ts +0 -211
  131. package/types/svg-path-commander.d.ts +0 -1089
@@ -1,3875 +0,0 @@
1
- /*!
2
- * SVGPathCommander v1.0.5 (http://thednp.github.io/svg-path-commander)
3
- * Copyright 2022 © thednp
4
- * Licensed under MIT (https://github.com/thednp/svg-path-commander/blob/master/LICENSE)
5
- */
6
- (function (global, factory) {
7
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8
- typeof define === 'function' && define.amd ? define(factory) :
9
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SVGPathCommander = factory());
10
- })(this, (function () { 'use strict';
11
-
12
- /**
13
- * SVGPathCommander default options
14
- * @type {SVGPath.options}
15
- */
16
- var defaultOptions = {
17
- origin: [0, 0, 0],
18
- round: 4,
19
- };
20
-
21
- /**
22
- * Segment params length
23
- * @type {Record<string, number>}
24
- */
25
- var paramsCount = {
26
- a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0,
27
- };
28
-
29
- /**
30
- * Breaks the parsing of a pathString once a segment is finalized.
31
- *
32
- * @param {SVGPath.PathParser} path the `PathParser` instance
33
- */
34
- function finalizeSegment(path) {
35
- var pathCommand = path.pathValue[path.segmentStart];
36
- var LK = pathCommand.toLowerCase();
37
- var data = path.data;
38
-
39
- while (data.length >= paramsCount[LK]) {
40
- // overloaded `moveTo`
41
- // https://github.com/rveciana/svg-path-properties/blob/master/src/parse.ts
42
- if (LK === 'm' && data.length > 2) {
43
- path.segments.push([pathCommand ].concat( data.splice(0, 2)));
44
- LK = 'l';
45
- pathCommand = pathCommand === 'm' ? 'l' : 'L';
46
- } else {
47
- path.segments.push([pathCommand ].concat( data.splice(0, paramsCount[LK])));
48
- }
49
-
50
- if (!paramsCount[LK]) {
51
- break;
52
- }
53
- }
54
- }
55
-
56
- var error = 'SVGPathCommander error';
57
-
58
- /**
59
- * Validates an A (arc-to) specific path command value.
60
- * Usually a `large-arc-flag` or `sweep-flag`.
61
- *
62
- * @param {SVGPath.PathParser} path the `PathParser` instance
63
- */
64
- function scanFlag(path) {
65
- var index = path.index;
66
- var pathValue = path.pathValue;
67
- var code = pathValue.charCodeAt(index);
68
-
69
- if (code === 0x30/* 0 */) {
70
- path.param = 0;
71
- path.index += 1;
72
- return;
73
- }
74
-
75
- if (code === 0x31/* 1 */) {
76
- path.param = 1;
77
- path.index += 1;
78
- return;
79
- }
80
-
81
- path.err = error + ": invalid Arc flag \"" + (pathValue[index]) + "\", expecting 0 or 1 at index " + index;
82
- }
83
-
84
- /**
85
- * Checks if a character is a digit.
86
- *
87
- * @param {number} code the character to check
88
- * @returns {boolean} check result
89
- */
90
- function isDigit(code) {
91
- return (code >= 48 && code <= 57); // 0..9
92
- }
93
-
94
- var invalidPathValue = 'Invalid path value';
95
-
96
- /**
97
- * Validates every character of the path string,
98
- * every path command, negative numbers or floating point numbers.
99
- *
100
- * @param {SVGPath.PathParser} path the `PathParser` instance
101
- */
102
- function scanParam(path) {
103
- var max = path.max;
104
- var pathValue = path.pathValue;
105
- var start = path.index;
106
- var index = start;
107
- var zeroFirst = false;
108
- var hasCeiling = false;
109
- var hasDecimal = false;
110
- var hasDot = false;
111
- var ch;
112
-
113
- if (index >= max) {
114
- // path.err = 'SvgPath: missed param (at pos ' + index + ')';
115
- path.err = error + ": " + invalidPathValue + " at index " + index + ", \"pathValue\" is missing param";
116
- return;
117
- }
118
- ch = pathValue.charCodeAt(index);
119
-
120
- if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
121
- index += 1;
122
- // ch = (index < max) ? pathValue.charCodeAt(index) : 0;
123
- ch = pathValue.charCodeAt(index);
124
- }
125
-
126
- // This logic is shamelessly borrowed from Esprima
127
- // https://github.com/ariya/esprimas
128
- if (!isDigit(ch) && ch !== 0x2E/* . */) {
129
- // path.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
130
- path.err = error + ": " + invalidPathValue + " at index " + index + ", \"" + (pathValue[index]) + "\" is not a number";
131
- return;
132
- }
133
-
134
- if (ch !== 0x2E/* . */) {
135
- zeroFirst = (ch === 0x30/* 0 */);
136
- index += 1;
137
-
138
- ch = pathValue.charCodeAt(index);
139
-
140
- if (zeroFirst && index < max) {
141
- // decimal number starts with '0' such as '09' is illegal.
142
- if (ch && isDigit(ch)) {
143
- // path.err = 'SvgPath: numbers started with `0` such as `09`
144
- // are illegal (at pos ' + start + ')';
145
- path.err = error + ": " + invalidPathValue + " at index " + start + ", \"" + (pathValue[start]) + "\" illegal number";
146
- return;
147
- }
148
- }
149
-
150
- while (index < max && isDigit(pathValue.charCodeAt(index))) {
151
- index += 1;
152
- hasCeiling = true;
153
- }
154
-
155
- ch = pathValue.charCodeAt(index);
156
- }
157
-
158
- if (ch === 0x2E/* . */) {
159
- hasDot = true;
160
- index += 1;
161
- while (isDigit(pathValue.charCodeAt(index))) {
162
- index += 1;
163
- hasDecimal = true;
164
- }
165
-
166
- ch = pathValue.charCodeAt(index);
167
- }
168
-
169
- if (ch === 0x65/* e */ || ch === 0x45/* E */) {
170
- if (hasDot && !hasCeiling && !hasDecimal) {
171
- path.err = error + ": " + invalidPathValue + " at index " + index + ", \"" + (pathValue[index]) + "\" invalid float exponent";
172
- return;
173
- }
174
-
175
- index += 1;
176
-
177
- ch = pathValue.charCodeAt(index);
178
-
179
- if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
180
- index += 1;
181
- }
182
- if (index < max && isDigit(pathValue.charCodeAt(index))) {
183
- while (index < max && isDigit(pathValue.charCodeAt(index))) {
184
- index += 1;
185
- }
186
- } else {
187
- path.err = error + ": " + invalidPathValue + " at index " + index + ", \"" + (pathValue[index]) + "\" invalid integer exponent";
188
- return;
189
- }
190
- }
191
-
192
- path.index = index;
193
- path.param = +path.pathValue.slice(start, index);
194
- }
195
-
196
- /**
197
- * Checks if the character is a space.
198
- *
199
- * @param {number} ch the character to check
200
- * @returns {boolean} check result
201
- */
202
- function isSpace(ch) {
203
- var specialSpaces = [
204
- 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
205
- 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF];
206
- /* istanbul ignore next */
207
- return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) // Line terminators
208
- // White spaces
209
- || (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0)
210
- || (ch >= 0x1680 && specialSpaces.includes(ch));
211
- }
212
-
213
- /**
214
- * Points the parser to the next character in the
215
- * path string every time it encounters any kind of
216
- * space character.
217
- *
218
- * @param {SVGPath.PathParser} path the `PathParser` instance
219
- */
220
- function skipSpaces(path) {
221
- var pathValue = path.pathValue;
222
- var max = path.max;
223
- while (path.index < max && isSpace(pathValue.charCodeAt(path.index))) {
224
- path.index += 1;
225
- }
226
- }
227
-
228
- /**
229
- * Checks if the character is a path command.
230
- *
231
- * @param {any} code the character to check
232
- * @returns {boolean} check result
233
- */
234
- function isPathCommand(code) {
235
- // eslint-disable-next-line no-bitwise -- Impossible to satisfy
236
- switch (code | 0x20) {
237
- case 0x6D/* m */:
238
- case 0x7A/* z */:
239
- case 0x6C/* l */:
240
- case 0x68/* h */:
241
- case 0x76/* v */:
242
- case 0x63/* c */:
243
- case 0x73/* s */:
244
- case 0x71/* q */:
245
- case 0x74/* t */:
246
- case 0x61/* a */:
247
- // case 0x72/* r */:
248
- return true;
249
- default:
250
- return false;
251
- }
252
- }
253
-
254
- /**
255
- * Checks if the character is or belongs to a number.
256
- * [0-9]|+|-|.
257
- *
258
- * @param {number} code the character to check
259
- * @returns {boolean} check result
260
- */
261
- function isDigitStart(code) {
262
- return (code >= 48 && code <= 57) /* 0..9 */
263
- || code === 0x2B /* + */
264
- || code === 0x2D /* - */
265
- || code === 0x2E; /* . */
266
- }
267
-
268
- /**
269
- * Checks if the character is an A (arc-to) path command.
270
- *
271
- * @param {number} code the character to check
272
- * @returns {boolean} check result
273
- */
274
- function isArcCommand(code) {
275
- // eslint-disable-next-line no-bitwise -- Impossible to satisfy
276
- return (code | 0x20) === 0x61;
277
- }
278
-
279
- /**
280
- * Scans every character in the path string to determine
281
- * where a segment starts and where it ends.
282
- *
283
- * @param {SVGPath.PathParser} path the `PathParser` instance
284
- */
285
- function scanSegment(path) {
286
- var max = path.max;
287
- var pathValue = path.pathValue;
288
- var index = path.index;
289
- var cmdCode = pathValue.charCodeAt(index);
290
- var reqParams = paramsCount[pathValue[index].toLowerCase()];
291
-
292
- path.segmentStart = index;
293
-
294
- if (!isPathCommand(cmdCode)) {
295
- path.err = error + ": " + invalidPathValue + " \"" + (pathValue[index]) + "\" is not a path command";
296
- return;
297
- }
298
-
299
- path.index += 1;
300
- skipSpaces(path);
301
-
302
- path.data = [];
303
-
304
- if (!reqParams) {
305
- // Z
306
- finalizeSegment(path);
307
- return;
308
- }
309
-
310
- for (;;) {
311
- for (var i = reqParams; i > 0; i -= 1) {
312
- if (isArcCommand(cmdCode) && (i === 3 || i === 4)) { scanFlag(path); }
313
- else { scanParam(path); }
314
-
315
- if (path.err.length) {
316
- return;
317
- }
318
- path.data.push(path.param);
319
-
320
- skipSpaces(path);
321
-
322
- // after ',' param is mandatory
323
- if (path.index < max && pathValue.charCodeAt(path.index) === 0x2C/* , */) {
324
- path.index += 1;
325
- skipSpaces(path);
326
- }
327
- }
328
-
329
- if (path.index >= path.max) {
330
- break;
331
- }
332
-
333
- // Stop on next segment
334
- if (!isDigitStart(pathValue.charCodeAt(path.index))) {
335
- break;
336
- }
337
- }
338
-
339
- finalizeSegment(path);
340
- }
341
-
342
- /**
343
- * Returns a clone of an existing `pathArray`.
344
- *
345
- * @param {SVGPath.pathArray | SVGPath.pathSegment} path the source `pathArray`
346
- * @returns {any} the cloned `pathArray`
347
- */
348
- function clonePath(path) {
349
- return path.map(function (x) { return (Array.isArray(x) ? [].concat( x ) : x); });
350
- }
351
-
352
- /**
353
- * The `PathParser` is used by the `parsePathString` static method
354
- * to generate a `pathArray`.
355
- *
356
- * @param {string} pathString
357
- */
358
- function PathParser(pathString) {
359
- /** @type {SVGPath.pathArray} */
360
- this.segments = [];
361
- /** @type {string} */
362
- this.pathValue = pathString;
363
- /** @type {number} */
364
- this.max = pathString.length;
365
- /** @type {number} */
366
- this.index = 0;
367
- /** @type {number} */
368
- this.param = 0.0;
369
- /** @type {number} */
370
- this.segmentStart = 0;
371
- /** @type {any} */
372
- this.data = [];
373
- /** @type {string} */
374
- this.err = '';
375
- }
376
-
377
- /**
378
- * Iterates an array to check if it's an actual `pathArray`.
379
- *
380
- * @param {string | SVGPath.pathArray} path the `pathArray` to be checked
381
- * @returns {boolean} iteration result
382
- */
383
- function isPathArray(path) {
384
- return Array.isArray(path) && path.every(function (seg) {
385
- var lk = seg[0].toLowerCase();
386
- return paramsCount[lk] === seg.length - 1 && 'achlmqstvz'.includes(lk);
387
- });
388
- }
389
-
390
- /**
391
- * Parses a path string value and returns an array
392
- * of segments we like to call `pathArray`.
393
- *
394
- * @param {SVGPath.pathArray | string} pathInput the string to be parsed
395
- * @returns {SVGPath.pathArray | string} the resulted `pathArray` or error string
396
- */
397
- function parsePathString(pathInput) {
398
- if (isPathArray(pathInput)) {
399
- return clonePath(pathInput);
400
- }
401
-
402
- var path = new PathParser(pathInput);
403
-
404
- skipSpaces(path);
405
-
406
- while (path.index < path.max && !path.err.length) {
407
- scanSegment(path);
408
- }
409
-
410
- return path.err ? path.err : path.segments;
411
- }
412
-
413
- /**
414
- * Iterates an array to check if it's a `pathArray`
415
- * with all absolute values.
416
- *
417
- * @param {string | SVGPath.pathArray} path the `pathArray` to be checked
418
- * @returns {boolean} iteration result
419
- */
420
- function isAbsoluteArray(path) {
421
- return isPathArray(path)
422
- // `isPathArray` also checks if it's `Array`
423
- && path.every(function (ref) {
424
- var x = ref[0];
425
-
426
- return x === x.toUpperCase();
427
- });
428
- }
429
-
430
- /**
431
- * Parses a path string value or object and returns an array
432
- * of segments, all converted to absolute values.
433
- *
434
- * @param {string | SVGPath.pathArray} pathInput the path string | object
435
- * @returns {SVGPath.absoluteArray} the resulted `pathArray` with absolute values
436
- */
437
- function pathToAbsolute(pathInput) {
438
- /* istanbul ignore else */
439
- if (isAbsoluteArray(pathInput)) {
440
- // `isAbsoluteArray` checks if it's `pathArray`
441
- return clonePath(pathInput);
442
- }
443
-
444
- var path = parsePathString(pathInput);
445
- var x = 0; var y = 0;
446
- var mx = 0; var my = 0;
447
-
448
- // the `absoluteSegment[]` is for sure an `absolutePath`
449
- return path.map(function (segment) {
450
- var assign, assign$1, assign$2;
451
-
452
- var values = segment.slice(1).map(Number);
453
- var pathCommand = segment[0];
454
- /** @type {SVGPath.absoluteCommand} */
455
- var absCommand = pathCommand.toUpperCase();
456
-
457
- if (pathCommand === 'M') {
458
- (assign = values, x = assign[0], y = assign[1]);
459
- mx = x;
460
- my = y;
461
- return ['M', x, y];
462
- }
463
- /** @type {SVGPath.absoluteSegment} */
464
- var absoluteSegment = [];
465
-
466
- if (pathCommand !== absCommand) {
467
- switch (absCommand) {
468
- case 'A':
469
- absoluteSegment = [
470
- absCommand, values[0], values[1], values[2],
471
- values[3], values[4], values[5] + x, values[6] + y];
472
- break;
473
- case 'V':
474
- absoluteSegment = [absCommand, values[0] + y];
475
- break;
476
- case 'H':
477
- absoluteSegment = [absCommand, values[0] + x];
478
- break;
479
- default: {
480
- // use brakets for `eslint: no-case-declaration`
481
- // https://stackoverflow.com/a/50753272/803358
482
- var absValues = values.map(function (n, j) { return n + (j % 2 ? y : x); });
483
- // for n, l, c, s, q, t
484
- absoluteSegment = [absCommand ].concat( absValues);
485
- }
486
- }
487
- } else {
488
- absoluteSegment = [absCommand ].concat( values);
489
- }
490
-
491
- var segLength = absoluteSegment.length;
492
- switch (absCommand) {
493
- case 'Z':
494
- x = mx;
495
- y = my;
496
- break;
497
- case 'H':
498
- (assign$1 = absoluteSegment, x = assign$1[1]);
499
- break;
500
- case 'V':
501
- (assign$2 = absoluteSegment, y = assign$2[1]);
502
- break;
503
- default:
504
- x = absoluteSegment[segLength - 2];
505
- y = absoluteSegment[segLength - 1];
506
-
507
- if (absCommand === 'M') {
508
- mx = x;
509
- my = y;
510
- }
511
- }
512
- return absoluteSegment;
513
- });
514
- }
515
-
516
- /**
517
- * Iterates an array to check if it's a `pathArray`
518
- * with relative values.
519
- *
520
- * @param {string | SVGPath.pathArray} path the `pathArray` to be checked
521
- * @returns {boolean} iteration result
522
- */
523
- function isRelativeArray(path) {
524
- return isPathArray(path)
525
- // `isPathArray` checks if it's `Array`
526
- && path.slice(1).every(function (ref) {
527
- var pc = ref[0];
528
-
529
- return pc === pc.toLowerCase();
530
- });
531
- }
532
-
533
- /**
534
- * Parses a path string value or object and returns an array
535
- * of segments, all converted to relative values.
536
- *
537
- * @param {string | SVGPath.pathArray} pathInput the path string | object
538
- * @returns {SVGPath.relativeArray} the resulted `pathArray` with relative values
539
- */
540
- function pathToRelative(pathInput) {
541
- /* istanbul ignore else */
542
- if (isRelativeArray(pathInput)) {
543
- return clonePath(pathInput);
544
- }
545
-
546
- var path = parsePathString(pathInput);
547
- var x = 0; var y = 0;
548
- var mx = 0; var my = 0;
549
-
550
- return path.map(function (segment) {
551
- var assign;
552
-
553
- var values = segment.slice(1).map(Number);
554
- var pathCommand = segment[0];
555
- /** @type {SVGPath.relativeCommand} */
556
- var relativeCommand = pathCommand.toLowerCase();
557
-
558
- if (pathCommand === 'M') {
559
- (assign = values, x = assign[0], y = assign[1]);
560
- mx = x;
561
- my = y;
562
- return ['M', x, y];
563
- }
564
-
565
- /** @type {SVGPath.relativeSegment} */
566
- var relativeSegment = [];
567
-
568
- if (pathCommand !== relativeCommand) {
569
- switch (relativeCommand) {
570
- case 'a':
571
- relativeSegment = [
572
- relativeCommand, values[0], values[1], values[2],
573
- values[3], values[4], values[5] - x, values[6] - y];
574
- break;
575
- case 'v':
576
- relativeSegment = [relativeCommand, values[0] - y];
577
- break;
578
- case 'h':
579
- relativeSegment = [relativeCommand, values[0] - x];
580
- break;
581
- default: {
582
- // use brakets for `eslint: no-case-declaration`
583
- // https://stackoverflow.com/a/50753272/803358
584
- var relValues = values.map(function (n, j) { return n - (j % 2 ? y : x); });
585
- relativeSegment = [relativeCommand ].concat( relValues);
586
- }
587
- }
588
- } else {
589
- if (pathCommand === 'm') {
590
- mx = values[0] + x;
591
- my = values[1] + y;
592
- }
593
- relativeSegment = [relativeCommand ].concat( values);
594
- }
595
-
596
- var segLength = relativeSegment.length;
597
- switch (relativeCommand) {
598
- case 'z':
599
- x = mx;
600
- y = my;
601
- break;
602
- case 'h':
603
- x += relativeSegment[1];
604
- break;
605
- case 'v':
606
- y += relativeSegment[1];
607
- break;
608
- default:
609
- x += relativeSegment[segLength - 2];
610
- y += relativeSegment[segLength - 1];
611
- }
612
- return relativeSegment;
613
- });
614
- }
615
-
616
- /**
617
- * Splits an extended A (arc-to) segment into two cubic-bezier segments.
618
- *
619
- * @param {SVGPath.pathArray} path the `pathArray` this segment belongs to
620
- * @param {string[]} allPathCommands all previous path commands
621
- * @param {number} i the segment index
622
- */
623
-
624
- function fixArc(path, allPathCommands, i) {
625
- if (path[i].length > 7) {
626
- path[i].shift();
627
- var segment = path[i];
628
- var ni = i; // ESLint
629
- while (segment.length) {
630
- // if created multiple C:s, their original seg is saved
631
- allPathCommands[i] = 'A';
632
- path.splice(ni += 1, 0, ['C' ].concat( segment.splice(0, 6)));
633
- }
634
- path.splice(i, 1);
635
- }
636
- }
637
-
638
- /**
639
- * Iterates an array to check if it's a `pathArray`
640
- * with all segments are in non-shorthand notation
641
- * with absolute values.
642
- *
643
- * @param {string | SVGPath.pathArray} path the `pathArray` to be checked
644
- * @returns {boolean} iteration result
645
- */
646
- function isNormalizedArray(path) {
647
- // `isAbsoluteArray` also checks if it's `Array`
648
- return isAbsoluteArray(path) && path.every(function (ref) {
649
- var pc = ref[0];
650
-
651
- return 'ACLMQZ'.includes(pc);
652
- });
653
- }
654
-
655
- /**
656
- * Iterates an array to check if it's a `pathArray`
657
- * with all C (cubic bezier) segments.
658
- *
659
- * @param {string | SVGPath.pathArray} path the `Array` to be checked
660
- * @returns {boolean} iteration result
661
- */
662
- function isCurveArray(path) {
663
- // `isPathArray` also checks if it's `Array`
664
- return isNormalizedArray(path) && path.every(function (ref) {
665
- var pc = ref[0];
666
-
667
- return 'MC'.includes(pc);
668
- });
669
- }
670
-
671
- /**
672
- * Normalizes a single segment of a `pathArray` object.
673
- *
674
- * @param {SVGPath.pathSegment} segment the segment object
675
- * @param {any} params the coordinates of the previous segment
676
- * @returns {SVGPath.normalSegment} the normalized segment
677
- */
678
- function normalizeSegment(segment, params) {
679
- var pathCommand = segment[0];
680
- var px1 = params.x1;
681
- var py1 = params.y1;
682
- var px2 = params.x2;
683
- var py2 = params.y2;
684
- var values = segment.slice(1).map(Number);
685
- var result = segment;
686
-
687
- if (!'TQ'.includes(pathCommand)) {
688
- // optional but good to be cautious
689
- params.qx = null;
690
- params.qy = null;
691
- }
692
-
693
- if (pathCommand === 'H') {
694
- result = ['L', segment[1], py1];
695
- } else if (pathCommand === 'V') {
696
- result = ['L', px1, segment[1]];
697
- } else if (pathCommand === 'S') {
698
- var x1 = px1 * 2 - px2;
699
- var y1 = py1 * 2 - py2;
700
- params.x1 = x1;
701
- params.y1 = y1;
702
- result = ['C', x1, y1 ].concat( values);
703
- } else if (pathCommand === 'T') {
704
- var qx = px1 * 2 - params.qx;
705
- var qy = py1 * 2 - params.qy;
706
- params.qx = qx;
707
- params.qy = qy;
708
- result = ['Q', qx, qy ].concat( values);
709
- } else if (pathCommand === 'Q') {
710
- var nqx = values[0];
711
- var nqy = values[1];
712
- params.qx = nqx;
713
- params.qy = nqy;
714
- }
715
-
716
- return result;
717
- }
718
-
719
- /**
720
- * @type {SVGPath.parserParams}
721
- */
722
- var paramsParser = {
723
- x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null,
724
- };
725
-
726
- /**
727
- * Normalizes a `path` object for further processing:
728
- * * convert segments to absolute values
729
- * * convert shorthand path commands to their non-shorthand notation
730
- *
731
- * @param {string | SVGPath.pathArray} pathInput the string to be parsed or 'pathArray'
732
- * @returns {SVGPath.normalArray} the normalized `pathArray`
733
- */
734
- function normalizePath(pathInput) {
735
- var assign;
736
-
737
- if (isNormalizedArray(pathInput)) {
738
- return clonePath(pathInput);
739
- }
740
-
741
- /** @type {SVGPath.normalArray} */
742
- var path = pathToAbsolute(pathInput);
743
- var params = Object.assign({}, paramsParser);
744
- var allPathCommands = [];
745
- var ii = path.length;
746
- var pathCommand = '';
747
-
748
- for (var i = 0; i < ii; i += 1) {
749
- (assign = path[i], pathCommand = assign[0]);
750
-
751
- // Save current path command
752
- allPathCommands[i] = pathCommand;
753
- path[i] = normalizeSegment(path[i], params);
754
-
755
- var segment = path[i];
756
- var seglen = segment.length;
757
-
758
- params.x1 = +segment[seglen - 2];
759
- params.y1 = +segment[seglen - 1];
760
- params.x2 = +(segment[seglen - 4]) || params.x1;
761
- params.y2 = +(segment[seglen - 3]) || params.y1;
762
- }
763
-
764
- return path;
765
- }
766
-
767
- /**
768
- * Returns an {x,y} vector rotated by a given
769
- * angle in radian.
770
- *
771
- * @param {number} x the initial vector x
772
- * @param {number} y the initial vector y
773
- * @param {number} rad the radian vector angle
774
- * @returns {{x: number, y: number}} the rotated vector
775
- */
776
- function rotateVector(x, y, rad) {
777
- var X = x * Math.cos(rad) - y * Math.sin(rad);
778
- var Y = x * Math.sin(rad) + y * Math.cos(rad);
779
- return { x: X, y: Y };
780
- }
781
-
782
- /**
783
- * Converts A (arc-to) segments to C (cubic-bezier-to).
784
- *
785
- * For more information of where this math came from visit:
786
- * http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
787
- *
788
- * @param {number} X1 the starting x position
789
- * @param {number} Y1 the starting y position
790
- * @param {number} RX x-radius of the arc
791
- * @param {number} RY y-radius of the arc
792
- * @param {number} angle x-axis-rotation of the arc
793
- * @param {number} LAF large-arc-flag of the arc
794
- * @param {number} SF sweep-flag of the arc
795
- * @param {number} X2 the ending x position
796
- * @param {number} Y2 the ending y position
797
- * @param {number[]=} recursive the parameters needed to split arc into 2 segments
798
- * @return {number[]} the resulting cubic-bezier segment(s)
799
- */
800
- function arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, recursive) {
801
- var assign;
802
-
803
- var x1 = X1; var y1 = Y1; var rx = RX; var ry = RY; var x2 = X2; var y2 = Y2;
804
- // for more information of where this Math came from visit:
805
- // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
806
- var d120 = (Math.PI * 120) / 180;
807
-
808
- var rad = (Math.PI / 180) * (+angle || 0);
809
- /** @type {number[]} */
810
- var res = [];
811
- var xy;
812
- var f1;
813
- var f2;
814
- var cx;
815
- var cy;
816
-
817
- if (!recursive) {
818
- xy = rotateVector(x1, y1, -rad);
819
- x1 = xy.x;
820
- y1 = xy.y;
821
- xy = rotateVector(x2, y2, -rad);
822
- x2 = xy.x;
823
- y2 = xy.y;
824
-
825
- var x = (x1 - x2) / 2;
826
- var y = (y1 - y2) / 2;
827
- var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
828
- if (h > 1) {
829
- h = Math.sqrt(h);
830
- rx *= h;
831
- ry *= h;
832
- }
833
- var rx2 = rx * rx;
834
- var ry2 = ry * ry;
835
-
836
- var k = (LAF === SF ? -1 : 1)
837
- * Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x)
838
- / (rx2 * y * y + ry2 * x * x)));
839
-
840
- cx = ((k * rx * y) / ry) + ((x1 + x2) / 2);
841
- cy = ((k * -ry * x) / rx) + ((y1 + y2) / 2);
842
- // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
843
- f1 = Math.asin((((y1 - cy) / ry) * (Math.pow( 10, 9 )) >> 0) / (Math.pow( 10, 9 )));
844
- // eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
845
- f2 = Math.asin((((y2 - cy) / ry) * (Math.pow( 10, 9 )) >> 0) / (Math.pow( 10, 9 )));
846
-
847
- f1 = x1 < cx ? Math.PI - f1 : f1;
848
- f2 = x2 < cx ? Math.PI - f2 : f2;
849
- if (f1 < 0) { (f1 = Math.PI * 2 + f1); }
850
- if (f2 < 0) { (f2 = Math.PI * 2 + f2); }
851
- if (SF && f1 > f2) {
852
- f1 -= Math.PI * 2;
853
- }
854
- if (!SF && f2 > f1) {
855
- f2 -= Math.PI * 2;
856
- }
857
- } else {
858
- (assign = recursive, f1 = assign[0], f2 = assign[1], cx = assign[2], cy = assign[3]);
859
- }
860
- var df = f2 - f1;
861
- if (Math.abs(df) > d120) {
862
- var f2old = f2;
863
- var x2old = x2;
864
- var y2old = y2;
865
- f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1);
866
- x2 = cx + rx * Math.cos(f2);
867
- y2 = cy + ry * Math.sin(f2);
868
- res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]);
869
- }
870
- df = f2 - f1;
871
- var c1 = Math.cos(f1);
872
- var s1 = Math.sin(f1);
873
- var c2 = Math.cos(f2);
874
- var s2 = Math.sin(f2);
875
- var t = Math.tan(df / 4);
876
- var hx = (4 / 3) * rx * t;
877
- var hy = (4 / 3) * ry * t;
878
- var m1 = [x1, y1];
879
- var m2 = [x1 + hx * s1, y1 - hy * c1];
880
- var m3 = [x2 + hx * s2, y2 - hy * c2];
881
- var m4 = [x2, y2];
882
- m2[0] = 2 * m1[0] - m2[0];
883
- m2[1] = 2 * m1[1] - m2[1];
884
- if (recursive) {
885
- return m2.concat( m3, m4, res);
886
- }
887
- res = m2.concat( m3, m4, res);
888
- var newres = [];
889
- for (var i = 0, ii = res.length; i < ii; i += 1) {
890
- newres[i] = i % 2
891
- ? rotateVector(res[i - 1], res[i], rad).y
892
- : rotateVector(res[i], res[i + 1], rad).x;
893
- }
894
- return newres;
895
- }
896
-
897
- /**
898
- * Converts a Q (quadratic-bezier) segment to C (cubic-bezier).
899
- *
900
- * @param {number} x1 curve start x
901
- * @param {number} y1 curve start y
902
- * @param {number} qx control point x
903
- * @param {number} qy control point y
904
- * @param {number} x2 curve end x
905
- * @param {number} y2 curve end y
906
- * @returns {number[]} the cubic-bezier segment
907
- */
908
- function quadToCubic(x1, y1, qx, qy, x2, y2) {
909
- var r13 = 1 / 3;
910
- var r23 = 2 / 3;
911
- return [
912
- r13 * x1 + r23 * qx, // cpx1
913
- r13 * y1 + r23 * qy, // cpy1
914
- r13 * x2 + r23 * qx, // cpx2
915
- r13 * y2 + r23 * qy, // cpy2
916
- x2, y2 ];
917
- }
918
-
919
- /**
920
- * Returns the coordinates of a specified distance
921
- * ratio between two points.
922
- *
923
- * @param {[number, number]} a the first point coordinates
924
- * @param {[number, number]} b the second point coordinates
925
- * @param {number} t the ratio
926
- * @returns {[number, number]} the midpoint coordinates
927
- */
928
- function midPoint(a, b, t) {
929
- var ax = a[0];
930
- var ay = a[1]; var bx = b[0];
931
- var by = b[1];
932
- return [ax + (bx - ax) * t, ay + (by - ay) * t];
933
- }
934
-
935
- /**
936
- * Returns the square root of the distance
937
- * between two given points.
938
- *
939
- * @param {[number, number]} a the first point coordinates
940
- * @param {[number, number]} b the second point coordinates
941
- * @returns {number} the distance value
942
- */
943
- function distanceSquareRoot(a, b) {
944
- return Math.sqrt(
945
- (a[0] - b[0]) * (a[0] - b[0])
946
- + (a[1] - b[1]) * (a[1] - b[1])
947
- );
948
- }
949
-
950
- /**
951
- * Returns a {x,y} point at a given length, the total length and
952
- * the minimum and maximum {x,y} coordinates of a line (L,V,H,Z) segment.
953
- *
954
- * @param {number} x1 the starting point X
955
- * @param {number} y1 the starting point Y
956
- * @param {number} x2 the ending point X
957
- * @param {number} y2 the ending point Y
958
- * @param {number=} distance the distance to point
959
- * @returns {SVGPath.lengthFactory} the segment length, point, min & max
960
- */
961
- function segmentLineFactory(x1, y1, x2, y2, distance) {
962
- var length = distanceSquareRoot([x1, y1], [x2, y2]);
963
- var point = { x: 0, y: 0 };
964
-
965
- /* istanbul ignore else */
966
- if (typeof distance === 'number') {
967
- if (distance <= 0) {
968
- point = { x: x1, y: y1 };
969
- } else if (distance >= length) {
970
- point = { x: x2, y: y2 };
971
- } else {
972
- var ref = midPoint([x1, y1], [x2, y2], distance / length);
973
- var x = ref[0];
974
- var y = ref[1];
975
- point = { x: x, y: y };
976
- }
977
- }
978
-
979
- return {
980
- length: length,
981
- point: point,
982
- min: {
983
- x: Math.min(x1, x2),
984
- y: Math.min(y1, y2),
985
- },
986
- max: {
987
- x: Math.max(x1, x2),
988
- y: Math.max(y1, y2),
989
- },
990
- };
991
- }
992
-
993
- /**
994
- * Converts an L (line-to) segment to C (cubic-bezier).
995
- *
996
- * @param {number} x1 line start x
997
- * @param {number} y1 line start y
998
- * @param {number} x2 line end x
999
- * @param {number} y2 line end y
1000
- * @returns {number[]} the cubic-bezier segment
1001
- */
1002
- function lineToCubic(x1, y1, x2, y2) {
1003
- var t = 0.5;
1004
- /** @type {[number, number]} */
1005
- var p0 = [x1, y1];
1006
- /** @type {[number, number]} */
1007
- var p1 = [x2, y2];
1008
- var p2 = midPoint(p0, p1, t);
1009
- var p3 = midPoint(p1, p2, t);
1010
- var p4 = midPoint(p2, p3, t);
1011
- var p5 = midPoint(p3, p4, t);
1012
- var p6 = midPoint(p4, p5, t);
1013
- var seg1 = p0.concat( p2, p4, p6, [t]);
1014
- var cp1 = segmentLineFactory.apply(void 0, seg1).point;
1015
- var seg2 = p6.concat( p5, p3, p1, [0]);
1016
- var cp2 = segmentLineFactory.apply(void 0, seg2).point;
1017
-
1018
- return [cp1.x, cp1.y, cp2.x, cp2.y, x2, y2];
1019
- }
1020
-
1021
- /**
1022
- * Converts any segment to C (cubic-bezier).
1023
- *
1024
- * @param {SVGPath.pathSegment} segment the source segment
1025
- * @param {SVGPath.parserParams} params the source segment parameters
1026
- * @returns {SVGPath.cubicSegment | SVGPath.MSegment} the cubic-bezier segment
1027
- */
1028
- function segmentToCubic(segment, params) {
1029
- var pathCommand = segment[0];
1030
- var values = segment.slice(1).map(Number);
1031
- var x = values[0];
1032
- var y = values[1];
1033
- var args;
1034
- var px1 = params.x1;
1035
- var py1 = params.y1;
1036
- var px = params.x;
1037
- var py = params.y;
1038
-
1039
- if (!'TQ'.includes(pathCommand)) {
1040
- params.qx = null;
1041
- params.qy = null;
1042
- }
1043
-
1044
- switch (pathCommand) {
1045
- case 'M':
1046
- params.x = x;
1047
- params.y = y;
1048
- return segment;
1049
- case 'A':
1050
- args = [px1, py1 ].concat( values);
1051
- return ['C' ].concat( arcToCubic.apply(void 0, args));
1052
- case 'Q':
1053
- params.qx = x;
1054
- params.qy = y;
1055
- args = [px1, py1 ].concat( values);
1056
- return ['C' ].concat( quadToCubic.apply(void 0, args));
1057
- case 'L':
1058
- return ['C' ].concat( lineToCubic(px1, py1, x, y));
1059
- case 'Z':
1060
- return ['C' ].concat( lineToCubic(px1, py1, px, py));
1061
- }
1062
- return segment;
1063
- }
1064
-
1065
- /**
1066
- * Parses a path string value or 'pathArray' and returns a new one
1067
- * in which all segments are converted to cubic-bezier.
1068
- *
1069
- * In addition, un-necessary `Z` segment is removed if previous segment
1070
- * extends to the `M` segment.
1071
- *
1072
- * @param {string | SVGPath.pathArray} pathInput the string to be parsed or 'pathArray'
1073
- * @returns {SVGPath.curveArray} the resulted `pathArray` converted to cubic-bezier
1074
- */
1075
- function pathToCurve(pathInput) {
1076
- var assign;
1077
-
1078
- /* istanbul ignore else */
1079
- if (isCurveArray(pathInput)) {
1080
- // `isCurveArray` checks if it's `pathArray`
1081
- return clonePath(pathInput);
1082
- }
1083
-
1084
- // const path = fixPath(normalizePath(pathInput));
1085
- var path = normalizePath(pathInput);
1086
- var params = Object.assign({}, paramsParser);
1087
- var allPathCommands = [];
1088
- var pathCommand = ''; // ts-lint
1089
- var ii = path.length;
1090
-
1091
- for (var i = 0; i < ii; i += 1) {
1092
- (assign = path[i], pathCommand = assign[0]);
1093
- allPathCommands[i] = pathCommand;
1094
-
1095
- path[i] = segmentToCubic(path[i], params);
1096
-
1097
- fixArc(path, allPathCommands, i);
1098
- ii = path.length;
1099
-
1100
- var segment = path[i];
1101
- var seglen = segment.length;
1102
- params.x1 = +segment[seglen - 2];
1103
- params.y1 = +segment[seglen - 1];
1104
- params.x2 = +(segment[seglen - 4]) || params.x1;
1105
- params.y2 = +(segment[seglen - 3]) || params.y1;
1106
- }
1107
-
1108
- return path;
1109
- }
1110
-
1111
- /**
1112
- * Rounds the values of a `pathArray` instance to
1113
- * a specified amount of decimals and returns it.
1114
- *
1115
- * @param {SVGPath.pathArray} path the source `pathArray`
1116
- * @param {number | 'off'} roundOption the amount of decimals to round numbers to
1117
- * @returns {SVGPath.pathArray} the resulted `pathArray` with rounded values
1118
- */
1119
- function roundPath(path, roundOption) {
1120
- var round = defaultOptions.round;
1121
- if (roundOption === 'off' || round === 'off') { return clonePath(path); }
1122
- // round = roundOption >= 1 ? roundOption : round;
1123
- // allow for ZERO decimals
1124
- round = roundOption >= 0 ? roundOption : round;
1125
- // to round values to the power
1126
- // the `round` value must be integer
1127
- var pow = typeof round === 'number' && round >= 1 ? (Math.pow( 10, round )) : 1;
1128
-
1129
- return path.map(function (pi) {
1130
- var values = pi.slice(1).map(Number)
1131
- .map(function (n) { return (round ? (Math.round(n * pow) / pow) : Math.round(n)); });
1132
- return [pi[0] ].concat( values);
1133
- });
1134
- }
1135
-
1136
- /**
1137
- * Returns a valid `d` attribute string value created
1138
- * by rounding values and concatenating the `pathArray` segments.
1139
- *
1140
- * @param {SVGPath.pathArray} path the `pathArray` object
1141
- * @param {number | 'off'} round amount of decimals to round values to
1142
- * @returns {string} the concatenated path string
1143
- */
1144
- function pathToString(path, round) {
1145
- return roundPath(path, round)
1146
- .map(function (x) { return x[0] + x.slice(1).join(' '); }).join('');
1147
- }
1148
-
1149
- /**
1150
- * Reverses all segments of a `pathArray` and returns a new `pathArray` instance.
1151
- *
1152
- * @param {SVGPath.pathArray} pathInput the source `pathArray`
1153
- * @returns {SVGPath.pathArray} the reversed `pathArray`
1154
- */
1155
- function reversePath(pathInput) {
1156
- var absolutePath = pathToAbsolute(pathInput);
1157
- var isClosed = absolutePath.slice(-1)[0][0] === 'Z';
1158
-
1159
- var reversedPath = normalizePath(absolutePath).map(function (segment, i) {
1160
- var ref = segment.slice(-2).map(Number);
1161
- var x = ref[0];
1162
- var y = ref[1];
1163
- return {
1164
- seg: absolutePath[i], // absolute
1165
- n: segment, // normalized
1166
- c: absolutePath[i][0], // pathCommand
1167
- x: x, // x
1168
- y: y, // y
1169
- };
1170
- }).map(function (seg, i, path) {
1171
- var segment = seg.seg;
1172
- var data = seg.n;
1173
- var prevSeg = i && path[i - 1];
1174
- var nextSeg = path[i + 1];
1175
- var pathCommand = seg.c;
1176
- var pLen = path.length;
1177
- /** @type {number} */
1178
- var x = i ? path[i - 1].x : path[pLen - 1].x;
1179
- var y = i ? path[i - 1].y : path[pLen - 1].y;
1180
- /** @type {SVGPath.pathSegment} */
1181
- var result = [];
1182
-
1183
- switch (pathCommand) {
1184
- case 'M':
1185
- result = isClosed ? ['Z'] : [pathCommand, x, y];
1186
- break;
1187
- case 'A':
1188
- result = [pathCommand ].concat( segment.slice(1, -3), [(segment[5] === 1 ? 0 : 1)], [x], [y]);
1189
- break;
1190
- case 'C':
1191
- if (nextSeg && nextSeg.c === 'S') {
1192
- result = ['S', segment[1], segment[2], x, y];
1193
- } else {
1194
- result = [pathCommand, segment[3], segment[4], segment[1], segment[2], x, y];
1195
- }
1196
- break;
1197
- case 'S':
1198
- if ((prevSeg && 'CS'.includes(prevSeg.c)) && (!nextSeg || nextSeg.c !== 'S')) {
1199
- result = ['C', data[3], data[4], data[1], data[2], x, y];
1200
- } else {
1201
- result = [pathCommand, data[1], data[2], x, y];
1202
- }
1203
- break;
1204
- case 'Q':
1205
- if (nextSeg && nextSeg.c === 'T') {
1206
- result = ['T', x, y];
1207
- } else {
1208
- result = [pathCommand ].concat( segment.slice(1, -2), [x], [y]);
1209
- }
1210
- break;
1211
- case 'T':
1212
- if ((prevSeg && 'QT'.includes(prevSeg.c)) && (!nextSeg || nextSeg.c !== 'T')) {
1213
- result = ['Q', data[1], data[2], x, y];
1214
- } else {
1215
- result = [pathCommand, x, y];
1216
- }
1217
- break;
1218
- case 'Z':
1219
- result = ['M', x, y];
1220
- break;
1221
- case 'H':
1222
- result = [pathCommand, x];
1223
- break;
1224
- case 'V':
1225
- result = [pathCommand, y];
1226
- break;
1227
- default:
1228
- result = [pathCommand ].concat( segment.slice(1, -2), [x], [y]);
1229
- }
1230
-
1231
- return result;
1232
- });
1233
-
1234
- return isClosed ? reversedPath.reverse()
1235
- : [reversedPath[0] ].concat( reversedPath.slice(1).reverse());
1236
- }
1237
-
1238
- /**
1239
- * Split a path into an `Array` of sub-path strings.
1240
- *
1241
- * In the process, values are converted to absolute
1242
- * for visual consistency.
1243
- *
1244
- * @param {SVGPath.pathArray} pathInput the source `pathArray`
1245
- * @return {SVGPath.pathArray[]} an array with all sub-path strings
1246
- */
1247
- function splitPath(pathInput) {
1248
- /** @type {SVGPath.pathArray[]} */
1249
- var composite = [];
1250
- /** @type {SVGPath.pathArray} */
1251
- var path;
1252
- var pi = -1;
1253
-
1254
- pathInput.forEach(function (seg) {
1255
- if (seg[0] === 'M') {
1256
- path = [seg];
1257
- pi += 1;
1258
- } else {
1259
- path = path.concat( [seg]);
1260
- }
1261
- composite[pi] = path;
1262
- });
1263
-
1264
- return composite;
1265
- }
1266
-
1267
- /**
1268
- * Shorten a single segment of a `pathArray` object.
1269
- *
1270
- * @param {SVGPath.absoluteSegment} segment the `absoluteSegment` object
1271
- * @param {SVGPath.normalSegment} normalSegment the `normalSegment` object
1272
- * @param {any} params the coordinates of the previous segment
1273
- * @param {string} prevCommand the path command of the previous segment
1274
- * @returns {SVGPath.shortSegment | SVGPath.pathSegment} the shortened segment
1275
- */
1276
- function shortenSegment(segment, normalSegment, params, prevCommand) {
1277
- var pathCommand = segment[0];
1278
- var round4 = function (/** @type {number} */n) { return Math.round(n * (Math.pow( 10, 4 ))) / Math.pow( 10, 4 ); };
1279
- var segmentValues = segment.slice(1).map(function (n) { return +n; });
1280
- var normalValues = normalSegment.slice(1).map(function (n) { return +n; });
1281
- var px1 = params.x1;
1282
- var py1 = params.y1;
1283
- var px2 = params.x2;
1284
- var py2 = params.y2;
1285
- var px = params.x;
1286
- var py = params.y;
1287
- var result = segment;
1288
- var ref = normalValues.slice(-2);
1289
- var x = ref[0];
1290
- var y = ref[1];
1291
-
1292
- if (!'TQ'.includes(pathCommand)) {
1293
- // optional but good to be cautious
1294
- params.qx = null;
1295
- params.qy = null;
1296
- }
1297
-
1298
- if (['V', 'H', 'S', 'T', 'Z'].includes(pathCommand)) {
1299
- result = [pathCommand ].concat( segmentValues);
1300
- } else if (pathCommand === 'L') {
1301
- if (round4(px) === round4(x)) {
1302
- result = ['V', y];
1303
- } else if (round4(py) === round4(y)) {
1304
- result = ['H', x];
1305
- }
1306
- } else if (pathCommand === 'C') {
1307
- var x1 = normalValues[0];
1308
- var y1 = normalValues[1];
1309
-
1310
- if ('CS'.includes(prevCommand)
1311
- && ((round4(x1) === round4(px1 * 2 - px2) && round4(y1) === round4(py1 * 2 - py2))
1312
- || (round4(px1) === round4(px2 * 2 - px) && round4(py1) === round4(py2 * 2 - py)))) {
1313
- result = ['S' ].concat( normalValues.slice(-4));
1314
- }
1315
- params.x1 = x1;
1316
- params.y1 = y1;
1317
- } else if (pathCommand === 'Q') {
1318
- var qx = normalValues[0];
1319
- var qy = normalValues[1];
1320
- params.qx = qx;
1321
- params.qy = qy;
1322
-
1323
- if ('QT'.includes(prevCommand)
1324
- && ((round4(qx) === round4(px1 * 2 - px2) && round4(qy) === round4(py1 * 2 - py2))
1325
- || (round4(px1) === round4(px2 * 2 - px) && round4(py1) === round4(py2 * 2 - py)))) {
1326
- result = ['T' ].concat( normalValues.slice(-2));
1327
- }
1328
- }
1329
-
1330
- return result;
1331
- }
1332
-
1333
- /**
1334
- * Optimizes a `pathArray` object:
1335
- * * convert segments to shorthand if possible
1336
- * * select shortest segments from absolute and relative `pathArray`s
1337
- *
1338
- * TO DO
1339
- * * implement `auto` for rounding values based on pathBBox
1340
- * * also revers path check if it's smaller string, maybe?
1341
- *
1342
- * @param {SVGPath.pathArray} pathInput a string or `pathArray`
1343
- * @param {number | 'off'} round the amount of decimals to round values to
1344
- * @returns {SVGPath.pathArray} the optimized `pathArray`
1345
- */
1346
- function optimizePath(pathInput, round) {
1347
- var assign, assign$1, assign$2, assign$3;
1348
-
1349
- var path = pathToAbsolute(pathInput);
1350
- var normalPath = normalizePath(path);
1351
- var params = Object.assign({}, paramsParser);
1352
- var allPathCommands = [];
1353
- var ii = path.length;
1354
- var pathCommand = '';
1355
- var prevCommand = '';
1356
- var x = 0;
1357
- var y = 0;
1358
- var mx = 0;
1359
- var my = 0;
1360
-
1361
- for (var i = 0; i < ii; i += 1) {
1362
- (assign = path[i], pathCommand = assign[0]);
1363
-
1364
- // Save current path command
1365
- allPathCommands[i] = pathCommand;
1366
- // Get previous path command for `shortenSegment`
1367
- if (i) { prevCommand = allPathCommands[i - 1]; }
1368
- path[i] = shortenSegment(path[i], normalPath[i], params, prevCommand);
1369
-
1370
- var segment = path[i];
1371
- var seglen = segment.length;
1372
-
1373
- // update C, S, Q, T specific params
1374
- params.x1 = +segment[seglen - 2];
1375
- params.y1 = +segment[seglen - 1];
1376
- params.x2 = +(segment[seglen - 4]) || params.x1;
1377
- params.y2 = +(segment[seglen - 3]) || params.y1;
1378
-
1379
- // update x, y params
1380
- switch (pathCommand) {
1381
- case 'Z':
1382
- x = mx;
1383
- y = my;
1384
- break;
1385
- case 'H':
1386
- (assign$1 = segment, x = assign$1[1]);
1387
- break;
1388
- case 'V':
1389
- (assign$2 = segment, y = assign$2[1]);
1390
- break;
1391
- default:
1392
- (assign$3 = segment.slice(-2).map(Number), x = assign$3[0], y = assign$3[1]);
1393
-
1394
- if (pathCommand === 'M') {
1395
- mx = x;
1396
- my = y;
1397
- }
1398
- }
1399
- params.x = x;
1400
- params.y = y;
1401
- }
1402
-
1403
- var absolutePath = roundPath(path, round);
1404
- var relativePath = roundPath(pathToRelative(path), round);
1405
-
1406
- return absolutePath.map(function (a, i) {
1407
- if (i) {
1408
- return a.join('').length < relativePath[i].join('').length
1409
- ? a : relativePath[i];
1410
- }
1411
- return a;
1412
- });
1413
- }
1414
-
1415
- // DOMMatrix Static methods
1416
- // * `fromArray` is a more simple implementation, should also accept Float[32/64]Array;
1417
- // * `fromMatrix` load values from another CSSMatrix/DOMMatrix instance or JSON object;
1418
- // * `fromString` parses and loads values from any valid CSS transform string (TransformList).
1419
-
1420
- /**
1421
- * Creates a new mutable `CSSMatrix` instance given an array of 16/6 floating point values.
1422
- * This static method invalidates arrays that contain non-number elements.
1423
- *
1424
- * If the array has six values, the result is a 2D matrix; if the array has 16 values,
1425
- * the result is a 3D matrix. Otherwise, a TypeError exception is thrown.
1426
- *
1427
- * @param {CSSM.matrix | CSSM.matrix3d} array an `Array` to feed values from.
1428
- * @return {CSSMatrix} the resulted matrix.
1429
- */
1430
- function fromArray(array) {
1431
- var m = new CSSMatrix();
1432
- var a = Array.from(array);
1433
-
1434
- if (!a.every(function (n) { return !Number.isNaN(n); })) {
1435
- throw TypeError(("CSSMatrix: \"" + array + "\" must only have numbers."));
1436
- }
1437
- if (a.length === 16) {
1438
- var m11 = a[0];
1439
- var m12 = a[1];
1440
- var m13 = a[2];
1441
- var m14 = a[3];
1442
- var m21 = a[4];
1443
- var m22 = a[5];
1444
- var m23 = a[6];
1445
- var m24 = a[7];
1446
- var m31 = a[8];
1447
- var m32 = a[9];
1448
- var m33 = a[10];
1449
- var m34 = a[11];
1450
- var m41 = a[12];
1451
- var m42 = a[13];
1452
- var m43 = a[14];
1453
- var m44 = a[15];
1454
-
1455
- m.m11 = m11;
1456
- m.a = m11;
1457
-
1458
- m.m21 = m21;
1459
- m.c = m21;
1460
-
1461
- m.m31 = m31;
1462
-
1463
- m.m41 = m41;
1464
- m.e = m41;
1465
-
1466
- m.m12 = m12;
1467
- m.b = m12;
1468
-
1469
- m.m22 = m22;
1470
- m.d = m22;
1471
-
1472
- m.m32 = m32;
1473
-
1474
- m.m42 = m42;
1475
- m.f = m42;
1476
-
1477
- m.m13 = m13;
1478
- m.m23 = m23;
1479
- m.m33 = m33;
1480
- m.m43 = m43;
1481
- m.m14 = m14;
1482
- m.m24 = m24;
1483
- m.m34 = m34;
1484
- m.m44 = m44;
1485
- } else if (a.length === 6) {
1486
- var M11 = a[0];
1487
- var M12 = a[1];
1488
- var M21 = a[2];
1489
- var M22 = a[3];
1490
- var M41 = a[4];
1491
- var M42 = a[5];
1492
-
1493
- m.m11 = M11;
1494
- m.a = M11;
1495
-
1496
- m.m12 = M12;
1497
- m.b = M12;
1498
-
1499
- m.m21 = M21;
1500
- m.c = M21;
1501
-
1502
- m.m22 = M22;
1503
- m.d = M22;
1504
-
1505
- m.m41 = M41;
1506
- m.e = M41;
1507
-
1508
- m.m42 = M42;
1509
- m.f = M42;
1510
- } else {
1511
- throw new TypeError('CSSMatrix: expecting an Array of 6/16 values.');
1512
- }
1513
- return m;
1514
- }
1515
-
1516
- /**
1517
- * Creates a new mutable `CSSMatrix` instance given an existing matrix or a
1518
- * `DOMMatrix` instance which provides the values for its properties.
1519
- *
1520
- * @param {CSSMatrix | DOMMatrix | CSSM.JSONMatrix} m the source matrix to feed values from.
1521
- * @return {CSSMatrix} the resulted matrix.
1522
- */
1523
- function fromMatrix(m) {
1524
- var keys = Object.keys(new CSSMatrix());
1525
- if (typeof m === 'object' && keys.every(function (k) { return k in m; })) {
1526
- return fromArray(
1527
- [m.m11, m.m12, m.m13, m.m14,
1528
- m.m21, m.m22, m.m23, m.m24,
1529
- m.m31, m.m32, m.m33, m.m34,
1530
- m.m41, m.m42, m.m43, m.m44]
1531
- );
1532
- }
1533
- throw TypeError(("CSSMatrix: \"" + (JSON.stringify(m)) + "\" is not a DOMMatrix / CSSMatrix / JSON compatible object."));
1534
- }
1535
-
1536
- /**
1537
- * Creates a new mutable `CSSMatrix` given any valid CSS transform string,
1538
- * or what we call `TransformList`:
1539
- *
1540
- * * `matrix(a, b, c, d, e, f)` - valid matrix() transform function
1541
- * * `matrix3d(m11, m12, m13, ...m44)` - valid matrix3d() transform function
1542
- * * `translate(tx, ty) rotateX(alpha)` - any valid transform function(s)
1543
- *
1544
- * @copyright thednp © 2021
1545
- *
1546
- * @param {string} source valid CSS transform string syntax.
1547
- * @return {CSSMatrix} the resulted matrix.
1548
- */
1549
- function fromString(source) {
1550
- if (typeof source !== 'string') {
1551
- throw TypeError(("CSSMatrix: \"" + source + "\" is not a string."));
1552
- }
1553
- var str = String(source).replace(/\s/g, '');
1554
- var m = new CSSMatrix();
1555
- var invalidStringError = "CSSMatrix: invalid transform string \"" + source + "\"";
1556
-
1557
- // const px = ['perspective'];
1558
- // const length = ['translate', 'translate3d', 'translateX', 'translateY', 'translateZ'];
1559
- // const deg = ['rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'skew', 'skewX', 'skewY'];
1560
- // const abs = ['scale', 'scale3d', 'matrix', 'matrix3d'];
1561
- // const transformFunctions = px.concat(length, deg, abs);
1562
-
1563
- str.split(')').filter(function (f) { return f; }).forEach(function (tf) {
1564
- var ref = tf.split('(');
1565
- var prop = ref[0];
1566
- var value = ref[1];
1567
-
1568
- // invalidate empty string
1569
- if (!value) { throw TypeError(invalidStringError); }
1570
-
1571
- var components = value.split(',')
1572
- .map(function (n) { return (n.includes('rad') ? parseFloat(n) * (180 / Math.PI) : parseFloat(n)); });
1573
-
1574
- var x = components[0];
1575
- var y = components[1];
1576
- var z = components[2];
1577
- var a = components[3];
1578
- var xyz = [x, y, z];
1579
- var xyza = [x, y, z, a];
1580
-
1581
- // single number value expected
1582
- if (prop === 'perspective' && x && [y, z].every(function (n) { return n === undefined; })) {
1583
- m.m34 = -1 / x;
1584
- // 6/16 number values expected
1585
- } else if (prop.includes('matrix') && [6, 16].includes(components.length)
1586
- && components.every(function (n) { return !Number.isNaN(+n); })) {
1587
- var values = components.map(function (n) { return (Math.abs(n) < 1e-6 ? 0 : n); });
1588
- // @ts-ignore -- conditions should suffice
1589
- m = m.multiply(fromArray(values));
1590
- // 3 values expected
1591
- } else if (prop === 'translate3d' && xyz.every(function (n) { return !Number.isNaN(+n); })) {
1592
- m = m.translate(x, y, z);
1593
- // single/double number value(s) expected
1594
- } else if (prop === 'translate' && x && z === undefined) {
1595
- m = m.translate(x, y || 0, 0);
1596
- // all 4 values expected
1597
- } else if (prop === 'rotate3d' && xyza.every(function (n) { return !Number.isNaN(+n); }) && a) {
1598
- m = m.rotateAxisAngle(x, y, z, a);
1599
- // single value expected
1600
- } else if (prop === 'rotate' && x && [y, z].every(function (n) { return n === undefined; })) {
1601
- m = m.rotate(0, 0, x);
1602
- // 3 values expected
1603
- } else if (prop === 'scale3d' && xyz.every(function (n) { return !Number.isNaN(+n); }) && xyz.some(function (n) { return n !== 1; })) {
1604
- m = m.scale(x, y, z);
1605
- // single value expected
1606
- } else if (prop === 'scale' && !Number.isNaN(x) && x !== 1 && z === undefined) {
1607
- var nosy = Number.isNaN(+y);
1608
- var sy = nosy ? x : y;
1609
- m = m.scale(x, sy, 1);
1610
- // single/double value expected
1611
- } else if (prop === 'skew' && (x || (!Number.isNaN(x) && y)) && z === undefined) {
1612
- m = m.skew(x, y || 0);
1613
- } else if (/[XYZ]/.test(prop) && x && [y, z].every(function (n) { return n === undefined; }) // a single value expected
1614
- && ['translate', 'rotate', 'scale', 'skew'].some(function (p) { return prop.includes(p); })) {
1615
- if (['skewX', 'skewY'].includes(prop)) {
1616
- // @ts-ignore unfortunately
1617
- m = m[prop](x);
1618
- } else {
1619
- var fn = prop.replace(/[XYZ]/, '');
1620
- var axis = prop.replace(fn, '');
1621
- var idx = ['X', 'Y', 'Z'].indexOf(axis);
1622
- var def = fn === 'scale' ? 1 : 0;
1623
- var axeValues = [
1624
- idx === 0 ? x : def,
1625
- idx === 1 ? x : def,
1626
- idx === 2 ? x : def];
1627
- // @ts-ignore unfortunately
1628
- m = m[fn].apply(m, axeValues);
1629
- }
1630
- } else {
1631
- throw TypeError(invalidStringError);
1632
- }
1633
- });
1634
-
1635
- return m;
1636
- }
1637
-
1638
- /**
1639
- * Returns an *Array* containing elements which comprise the matrix.
1640
- * The method can return either the 16 elements or the 6 elements
1641
- * depending on the value of the `is2D` parameter.
1642
- *
1643
- * @param {CSSMatrix | DOMMatrix | CSSM.JSONMatrix} m the source matrix to feed values from.
1644
- * @param {boolean=} is2D *Array* representation of the matrix
1645
- * @return {CSSM.matrix | CSSM.matrix3d} an *Array* representation of the matrix
1646
- */
1647
- function toArray(m, is2D) {
1648
- if (is2D) {
1649
- return [m.a, m.b, m.c, m.d, m.e, m.f];
1650
- }
1651
- return [m.m11, m.m12, m.m13, m.m14,
1652
- m.m21, m.m22, m.m23, m.m24,
1653
- m.m31, m.m32, m.m33, m.m34,
1654
- m.m41, m.m42, m.m43, m.m44];
1655
- }
1656
-
1657
- // Transform Functions
1658
- // https://www.w3.org/TR/css-transforms-1/#transform-functions
1659
-
1660
- /**
1661
- * Creates a new `CSSMatrix` for the translation matrix and returns it.
1662
- * This method is equivalent to the CSS `translate3d()` function.
1663
- *
1664
- * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translate3d
1665
- *
1666
- * @param {number} x the `x-axis` position.
1667
- * @param {number} y the `y-axis` position.
1668
- * @param {number} z the `z-axis` position.
1669
- * @return {CSSMatrix} the resulted matrix.
1670
- */
1671
- function Translate(x, y, z) {
1672
- var m = new CSSMatrix();
1673
- m.m41 = x;
1674
- m.e = x;
1675
- m.m42 = y;
1676
- m.f = y;
1677
- m.m43 = z;
1678
- return m;
1679
- }
1680
-
1681
- /**
1682
- * Creates a new `CSSMatrix` for the rotation matrix and returns it.
1683
- *
1684
- * http://en.wikipedia.org/wiki/Rotation_matrix
1685
- *
1686
- * @param {number} rx the `x-axis` rotation.
1687
- * @param {number} ry the `y-axis` rotation.
1688
- * @param {number} rz the `z-axis` rotation.
1689
- * @return {CSSMatrix} the resulted matrix.
1690
- */
1691
- function Rotate(rx, ry, rz) {
1692
- var m = new CSSMatrix();
1693
- var degToRad = Math.PI / 180;
1694
- var radX = rx * degToRad;
1695
- var radY = ry * degToRad;
1696
- var radZ = rz * degToRad;
1697
-
1698
- // minus sin() because of right-handed system
1699
- var cosx = Math.cos(radX);
1700
- var sinx = -Math.sin(radX);
1701
- var cosy = Math.cos(radY);
1702
- var siny = -Math.sin(radY);
1703
- var cosz = Math.cos(radZ);
1704
- var sinz = -Math.sin(radZ);
1705
-
1706
- var m11 = cosy * cosz;
1707
- var m12 = -cosy * sinz;
1708
-
1709
- m.m11 = m11;
1710
- m.a = m11;
1711
-
1712
- m.m12 = m12;
1713
- m.b = m12;
1714
-
1715
- m.m13 = siny;
1716
-
1717
- var m21 = sinx * siny * cosz + cosx * sinz;
1718
- m.m21 = m21;
1719
- m.c = m21;
1720
-
1721
- var m22 = cosx * cosz - sinx * siny * sinz;
1722
- m.m22 = m22;
1723
- m.d = m22;
1724
-
1725
- m.m23 = -sinx * cosy;
1726
-
1727
- m.m31 = sinx * sinz - cosx * siny * cosz;
1728
- m.m32 = sinx * cosz + cosx * siny * sinz;
1729
- m.m33 = cosx * cosy;
1730
-
1731
- return m;
1732
- }
1733
-
1734
- /**
1735
- * Creates a new `CSSMatrix` for the rotation matrix and returns it.
1736
- * This method is equivalent to the CSS `rotate3d()` function.
1737
- *
1738
- * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate3d
1739
- *
1740
- * @param {number} x the `x-axis` vector length.
1741
- * @param {number} y the `y-axis` vector length.
1742
- * @param {number} z the `z-axis` vector length.
1743
- * @param {number} alpha the value in degrees of the rotation.
1744
- * @return {CSSMatrix} the resulted matrix.
1745
- */
1746
- function RotateAxisAngle(x, y, z, alpha) {
1747
- var m = new CSSMatrix();
1748
- var length = Math.sqrt(x * x + y * y + z * z);
1749
-
1750
- if (length === 0) {
1751
- // bad vector length, return identity
1752
- return m;
1753
- }
1754
-
1755
- var X = x / length;
1756
- var Y = y / length;
1757
- var Z = z / length;
1758
-
1759
- var angle = alpha * (Math.PI / 360);
1760
- var sinA = Math.sin(angle);
1761
- var cosA = Math.cos(angle);
1762
- var sinA2 = sinA * sinA;
1763
- var x2 = X * X;
1764
- var y2 = Y * Y;
1765
- var z2 = Z * Z;
1766
-
1767
- var m11 = 1 - 2 * (y2 + z2) * sinA2;
1768
- m.m11 = m11;
1769
- m.a = m11;
1770
-
1771
- var m12 = 2 * (X * Y * sinA2 + Z * sinA * cosA);
1772
- m.m12 = m12;
1773
- m.b = m12;
1774
-
1775
- m.m13 = 2 * (X * Z * sinA2 - Y * sinA * cosA);
1776
-
1777
- var m21 = 2 * (Y * X * sinA2 - Z * sinA * cosA);
1778
- m.m21 = m21;
1779
- m.c = m21;
1780
-
1781
- var m22 = 1 - 2 * (z2 + x2) * sinA2;
1782
- m.m22 = m22;
1783
- m.d = m22;
1784
-
1785
- m.m23 = 2 * (Y * Z * sinA2 + X * sinA * cosA);
1786
- m.m31 = 2 * (Z * X * sinA2 + Y * sinA * cosA);
1787
- m.m32 = 2 * (Z * Y * sinA2 - X * sinA * cosA);
1788
- m.m33 = 1 - 2 * (x2 + y2) * sinA2;
1789
-
1790
- return m;
1791
- }
1792
-
1793
- /**
1794
- * Creates a new `CSSMatrix` for the scale matrix and returns it.
1795
- * This method is equivalent to the CSS `scale3d()` function, except it doesn't
1796
- * accept {x, y, z} transform origin parameters.
1797
- *
1798
- * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale3d
1799
- *
1800
- * @param {number} x the `x-axis` scale.
1801
- * @param {number} y the `y-axis` scale.
1802
- * @param {number} z the `z-axis` scale.
1803
- * @return {CSSMatrix} the resulted matrix.
1804
- */
1805
- function Scale(x, y, z) {
1806
- var m = new CSSMatrix();
1807
- m.m11 = x;
1808
- m.a = x;
1809
-
1810
- m.m22 = y;
1811
- m.d = y;
1812
-
1813
- m.m33 = z;
1814
- return m;
1815
- }
1816
-
1817
- /**
1818
- * Creates a new `CSSMatrix` for the shear of both the `x-axis` and`y-axis`
1819
- * matrix and returns it. This method is equivalent to the CSS `skew()` function.
1820
- *
1821
- * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skew
1822
- *
1823
- * @param {number} angleX the X-angle in degrees.
1824
- * @param {number} angleY the Y-angle in degrees.
1825
- * @return {CSSMatrix} the resulted matrix.
1826
- */
1827
- function Skew(angleX, angleY) {
1828
- var m = new CSSMatrix();
1829
- if (angleX) {
1830
- var radX = (angleX * Math.PI) / 180;
1831
- var tX = Math.tan(radX);
1832
- m.m21 = tX;
1833
- m.c = tX;
1834
- }
1835
- if (angleY) {
1836
- var radY = (angleY * Math.PI) / 180;
1837
- var tY = Math.tan(radY);
1838
- m.m12 = tY;
1839
- m.b = tY;
1840
- }
1841
- return m;
1842
- }
1843
-
1844
- /**
1845
- * Creates a new `CSSMatrix` for the shear of the `x-axis` rotation matrix and
1846
- * returns it. This method is equivalent to the CSS `skewX()` function.
1847
- *
1848
- * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skewX
1849
- *
1850
- * @param {number} angle the angle in degrees.
1851
- * @return {CSSMatrix} the resulted matrix.
1852
- */
1853
- function SkewX(angle) {
1854
- return Skew(angle, 0);
1855
- }
1856
-
1857
- /**
1858
- * Creates a new `CSSMatrix` for the shear of the `y-axis` rotation matrix and
1859
- * returns it. This method is equivalent to the CSS `skewY()` function.
1860
- *
1861
- * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skewY
1862
- *
1863
- * @param {number} angle the angle in degrees.
1864
- * @return {CSSMatrix} the resulted matrix.
1865
- */
1866
- function SkewY(angle) {
1867
- return Skew(0, angle);
1868
- }
1869
-
1870
- /**
1871
- * Creates a new `CSSMatrix` resulted from the multiplication of two matrixes
1872
- * and returns it. Both matrixes are not changed.
1873
- *
1874
- * @param {CSSMatrix | DOMMatrix | CSSM.JSONMatrix} m1 the first matrix.
1875
- * @param {CSSMatrix | DOMMatrix | CSSM.JSONMatrix} m2 the second matrix.
1876
- * @return {CSSMatrix} the resulted matrix.
1877
- */
1878
- function Multiply(m1, m2) {
1879
- var m11 = m2.m11 * m1.m11 + m2.m12 * m1.m21 + m2.m13 * m1.m31 + m2.m14 * m1.m41;
1880
- var m12 = m2.m11 * m1.m12 + m2.m12 * m1.m22 + m2.m13 * m1.m32 + m2.m14 * m1.m42;
1881
- var m13 = m2.m11 * m1.m13 + m2.m12 * m1.m23 + m2.m13 * m1.m33 + m2.m14 * m1.m43;
1882
- var m14 = m2.m11 * m1.m14 + m2.m12 * m1.m24 + m2.m13 * m1.m34 + m2.m14 * m1.m44;
1883
-
1884
- var m21 = m2.m21 * m1.m11 + m2.m22 * m1.m21 + m2.m23 * m1.m31 + m2.m24 * m1.m41;
1885
- var m22 = m2.m21 * m1.m12 + m2.m22 * m1.m22 + m2.m23 * m1.m32 + m2.m24 * m1.m42;
1886
- var m23 = m2.m21 * m1.m13 + m2.m22 * m1.m23 + m2.m23 * m1.m33 + m2.m24 * m1.m43;
1887
- var m24 = m2.m21 * m1.m14 + m2.m22 * m1.m24 + m2.m23 * m1.m34 + m2.m24 * m1.m44;
1888
-
1889
- var m31 = m2.m31 * m1.m11 + m2.m32 * m1.m21 + m2.m33 * m1.m31 + m2.m34 * m1.m41;
1890
- var m32 = m2.m31 * m1.m12 + m2.m32 * m1.m22 + m2.m33 * m1.m32 + m2.m34 * m1.m42;
1891
- var m33 = m2.m31 * m1.m13 + m2.m32 * m1.m23 + m2.m33 * m1.m33 + m2.m34 * m1.m43;
1892
- var m34 = m2.m31 * m1.m14 + m2.m32 * m1.m24 + m2.m33 * m1.m34 + m2.m34 * m1.m44;
1893
-
1894
- var m41 = m2.m41 * m1.m11 + m2.m42 * m1.m21 + m2.m43 * m1.m31 + m2.m44 * m1.m41;
1895
- var m42 = m2.m41 * m1.m12 + m2.m42 * m1.m22 + m2.m43 * m1.m32 + m2.m44 * m1.m42;
1896
- var m43 = m2.m41 * m1.m13 + m2.m42 * m1.m23 + m2.m43 * m1.m33 + m2.m44 * m1.m43;
1897
- var m44 = m2.m41 * m1.m14 + m2.m42 * m1.m24 + m2.m43 * m1.m34 + m2.m44 * m1.m44;
1898
-
1899
- return fromArray(
1900
- [m11, m12, m13, m14,
1901
- m21, m22, m23, m24,
1902
- m31, m32, m33, m34,
1903
- m41, m42, m43, m44]
1904
- );
1905
- }
1906
-
1907
- /**
1908
- * Creates and returns a new `DOMMatrix` compatible instance
1909
- * with equivalent instance.
1910
- * @class CSSMatrix
1911
- *
1912
- * @author thednp <https://github.com/thednp/DOMMatrix/>
1913
- * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix
1914
- */
1915
-
1916
- var CSSMatrix = function CSSMatrix() {
1917
- var args = [], len = arguments.length;
1918
- while ( len-- ) args[ len ] = arguments[ len ];
1919
-
1920
- var m = this;
1921
- // array 6
1922
- m.a = 1; m.b = 0;
1923
- m.c = 0; m.d = 1;
1924
- m.e = 0; m.f = 0;
1925
- // array 16
1926
- m.m11 = 1; m.m12 = 0; m.m13 = 0; m.m14 = 0;
1927
- m.m21 = 0; m.m22 = 1; m.m23 = 0; m.m24 = 0;
1928
- m.m31 = 0; m.m32 = 0; m.m33 = 1; m.m34 = 0;
1929
- m.m41 = 0; m.m42 = 0; m.m43 = 0; m.m44 = 1;
1930
-
1931
- if (args.length) {
1932
- var ARGS = [16, 6].some(function (l) { return l === args.length; }) ? args : args[0];
1933
-
1934
- return m.setMatrixValue(ARGS);
1935
- }
1936
- return m;
1937
- };
1938
-
1939
- var prototypeAccessors = { isIdentity: { configurable: true },is2D: { configurable: true } };
1940
-
1941
- /**
1942
- * A `Boolean` whose value is `true` if the matrix is the identity matrix. The identity
1943
- * matrix is one in which every value is 0 except those on the main diagonal from top-left
1944
- * to bottom-right corner (in other words, where the offsets in each direction are equal).
1945
- *
1946
- * @return {boolean} the current property value
1947
- */
1948
- prototypeAccessors.isIdentity.get = function () {
1949
- var m = this;
1950
- return (m.m11 === 1 && m.m12 === 0 && m.m13 === 0 && m.m14 === 0
1951
- && m.m21 === 0 && m.m22 === 1 && m.m23 === 0 && m.m24 === 0
1952
- && m.m31 === 0 && m.m32 === 0 && m.m33 === 1 && m.m34 === 0
1953
- && m.m41 === 0 && m.m42 === 0 && m.m43 === 0 && m.m44 === 1);
1954
- };
1955
-
1956
- /**
1957
- * A `Boolean` flag whose value is `true` if the matrix was initialized as a 2D matrix
1958
- * and `false` if the matrix is 3D.
1959
- *
1960
- * @return {boolean} the current property value
1961
- */
1962
- prototypeAccessors.is2D.get = function () {
1963
- var m = this;
1964
- return (m.m31 === 0 && m.m32 === 0 && m.m33 === 1 && m.m34 === 0 && m.m43 === 0 && m.m44 === 1);
1965
- };
1966
-
1967
- /**
1968
- * The `setMatrixValue` method replaces the existing matrix with one computed
1969
- * in the browser. EG: `matrix(1,0.25,-0.25,1,0,0)`
1970
- *
1971
- * The method accepts any *Array* values, the result of
1972
- * `DOMMatrix` instance method `toFloat64Array()` / `toFloat32Array()` calls
1973
- *or `CSSMatrix` instance method `toArray()`.
1974
- *
1975
- * This method expects valid *matrix()* / *matrix3d()* string values, as well
1976
- * as other transform functions like *translateX(10px)*.
1977
- *
1978
- * @param {string | CSSM.matrix | CSSM.matrix3d | CSSMatrix | DOMMatrix | CSSM.JSONMatrix} source
1979
- * @return {CSSMatrix} the matrix instance
1980
- */
1981
- CSSMatrix.prototype.setMatrixValue = function setMatrixValue (source) {
1982
- var m = this;
1983
-
1984
- // CSS transform string source - TransformList first
1985
- if (typeof source === 'string' && source.length && source !== 'none') {
1986
- return fromString(source);
1987
- }
1988
- // [Arguments list | Array] come second
1989
- if ([Array, Float64Array, Float32Array].some(function (a) { return source instanceof a; })) {
1990
- // @ts-ignore
1991
- return fromArray(source);
1992
- }
1993
- // new CSSMatrix(CSSMatrix | DOMMatrix | JSON) last
1994
- if ([CSSMatrix, DOMMatrix, Object].some(function (a) { return source instanceof a; })) {
1995
- // @ts-ignore
1996
- return fromMatrix(source);
1997
- }
1998
-
1999
- return m;
2000
- };
2001
-
2002
- /**
2003
- * Returns a *Float32Array* containing elements which comprise the matrix.
2004
- * The method can return either the 16 elements or the 6 elements
2005
- * depending on the value of the `is2D` parameter.
2006
- *
2007
- * @param {boolean=} is2D *Array* representation of the matrix
2008
- * @return {Float32Array} an *Array* representation of the matrix
2009
- */
2010
- CSSMatrix.prototype.toFloat32Array = function toFloat32Array (is2D) {
2011
- return Float32Array.from(toArray(this, is2D));
2012
- };
2013
-
2014
- /**
2015
- * Returns a *Float64Array* containing elements which comprise the matrix.
2016
- * The method can return either the 16 elements or the 6 elements
2017
- * depending on the value of the `is2D` parameter.
2018
- *
2019
- * @param {boolean=} is2D *Array* representation of the matrix
2020
- * @return {Float64Array} an *Array* representation of the matrix
2021
- */
2022
- CSSMatrix.prototype.toFloat64Array = function toFloat64Array (is2D) {
2023
- return Float64Array.from(toArray(this, is2D));
2024
- };
2025
-
2026
- /**
2027
- * Creates and returns a string representation of the matrix in `CSS` matrix syntax,
2028
- * using the appropriate `CSS` matrix notation.
2029
- *
2030
- * matrix3d *matrix3d(m11, m12, m13, m14, m21, ...)*
2031
- * matrix *matrix(a, b, c, d, e, f)*
2032
- *
2033
- * @return {string} a string representation of the matrix
2034
- */
2035
- CSSMatrix.prototype.toString = function toString () {
2036
- var m = this;
2037
- var is2D = m.is2D;
2038
- var values = m.toFloat64Array(is2D).join(', ');
2039
- var type = is2D ? 'matrix' : 'matrix3d';
2040
- return (type + "(" + values + ")");
2041
- };
2042
-
2043
- /**
2044
- * Returns a JSON representation of the `CSSMatrix` instance, a standard *Object*
2045
- * that includes `{a,b,c,d,e,f}` and `{m11,m12,m13,..m44}` properties as well
2046
- * as the `is2D` & `isIdentity` properties.
2047
- *
2048
- * The result can also be used as a second parameter for the `fromMatrix` static method
2049
- * to load values into another matrix instance.
2050
- *
2051
- * @return {CSSM.JSONMatrix} an *Object* with all matrix values.
2052
- */
2053
- CSSMatrix.prototype.toJSON = function toJSON () {
2054
- var m = this;
2055
- var is2D = m.is2D;
2056
- var isIdentity = m.isIdentity;
2057
- return Object.assign({}, m, {is2D: is2D, isIdentity: isIdentity});
2058
- };
2059
-
2060
- /**
2061
- * The Multiply method returns a new CSSMatrix which is the result of this
2062
- * matrix multiplied by the passed matrix, with the passed matrix to the right.
2063
- * This matrix is not modified.
2064
- *
2065
- * @param {CSSMatrix | DOMMatrix | CSSM.JSONMatrix} m2 CSSMatrix
2066
- * @return {CSSMatrix} The resulted matrix.
2067
- */
2068
- CSSMatrix.prototype.multiply = function multiply (m2) {
2069
- return Multiply(this, m2);
2070
- };
2071
-
2072
- /**
2073
- * The translate method returns a new matrix which is this matrix post
2074
- * multiplied by a translation matrix containing the passed values. If the z
2075
- * component is undefined, a 0 value is used in its place. This matrix is not
2076
- * modified.
2077
- *
2078
- * @param {number} x X component of the translation value.
2079
- * @param {number=} y Y component of the translation value.
2080
- * @param {number=} z Z component of the translation value.
2081
- * @return {CSSMatrix} The resulted matrix
2082
- */
2083
- CSSMatrix.prototype.translate = function translate (x, y, z) {
2084
- var X = x;
2085
- var Y = y;
2086
- var Z = z;
2087
- if (Y === undefined) { Y = 0; }
2088
- if (Z === undefined) { Z = 0; }
2089
- return Multiply(this, Translate(X, Y, Z));
2090
- };
2091
-
2092
- /**
2093
- * The scale method returns a new matrix which is this matrix post multiplied by
2094
- * a scale matrix containing the passed values. If the z component is undefined,
2095
- * a 1 value is used in its place. If the y component is undefined, the x
2096
- * component value is used in its place. This matrix is not modified.
2097
- *
2098
- * @param {number} x The X component of the scale value.
2099
- * @param {number=} y The Y component of the scale value.
2100
- * @param {number=} z The Z component of the scale value.
2101
- * @return {CSSMatrix} The resulted matrix
2102
- */
2103
- CSSMatrix.prototype.scale = function scale (x, y, z) {
2104
- var X = x;
2105
- var Y = y;
2106
- var Z = z;
2107
- if (Y === undefined) { Y = x; }
2108
- if (Z === undefined) { Z = 1; } // Z must be 1 if undefined
2109
-
2110
- return Multiply(this, Scale(X, Y, Z));
2111
- };
2112
-
2113
- /**
2114
- * The rotate method returns a new matrix which is this matrix post multiplied
2115
- * by each of 3 rotation matrices about the major axes, first X, then Y, then Z.
2116
- * If the y and z components are undefined, the x value is used to rotate the
2117
- * object about the z axis, as though the vector (0,0,x) were passed. All
2118
- * rotation values are in degrees. This matrix is not modified.
2119
- *
2120
- * @param {number} rx The X component of the rotation, or Z if Y and Z are null.
2121
- * @param {number=} ry The (optional) Y component of the rotation value.
2122
- * @param {number=} rz The (optional) Z component of the rotation value.
2123
- * @return {CSSMatrix} The resulted matrix
2124
- */
2125
- CSSMatrix.prototype.rotate = function rotate (rx, ry, rz) {
2126
- var RX = rx;
2127
- var RY = ry || 0;
2128
- var RZ = rz || 0;
2129
-
2130
- if (typeof rx === 'number' && ry === undefined && rz === undefined) {
2131
- RZ = RX; RX = 0; RY = 0;
2132
- }
2133
-
2134
- return Multiply(this, Rotate(RX, RY, RZ));
2135
- };
2136
-
2137
- /**
2138
- * The rotateAxisAngle method returns a new matrix which is this matrix post
2139
- * multiplied by a rotation matrix with the given axis and `angle`. The right-hand
2140
- * rule is used to determine the direction of rotation. All rotation values are
2141
- * in degrees. This matrix is not modified.
2142
- *
2143
- * @param {number} x The X component of the axis vector.
2144
- * @param {number} y The Y component of the axis vector.
2145
- * @param {number} z The Z component of the axis vector.
2146
- * @param {number} angle The angle of rotation about the axis vector, in degrees.
2147
- * @return {CSSMatrix} The resulted matrix
2148
- */
2149
- CSSMatrix.prototype.rotateAxisAngle = function rotateAxisAngle (x, y, z, angle) {
2150
- if ([x, y, z, angle].some(function (n) { return Number.isNaN(+n); })) {
2151
- throw new TypeError('CSSMatrix: expecting 4 values');
2152
- }
2153
- return Multiply(this, RotateAxisAngle(x, y, z, angle));
2154
- };
2155
-
2156
- /**
2157
- * Specifies a skew transformation along the `x-axis` by the given angle.
2158
- * This matrix is not modified.
2159
- *
2160
- * @param {number} angle The angle amount in degrees to skew.
2161
- * @return {CSSMatrix} The resulted matrix
2162
- */
2163
- CSSMatrix.prototype.skewX = function skewX (angle) {
2164
- return Multiply(this, SkewX(angle));
2165
- };
2166
-
2167
- /**
2168
- * Specifies a skew transformation along the `y-axis` by the given angle.
2169
- * This matrix is not modified.
2170
- *
2171
- * @param {number} angle The angle amount in degrees to skew.
2172
- * @return {CSSMatrix} The resulted matrix
2173
- */
2174
- CSSMatrix.prototype.skewY = function skewY (angle) {
2175
- return Multiply(this, SkewY(angle));
2176
- };
2177
-
2178
- /**
2179
- * Specifies a skew transformation along both the `x-axis` and `y-axis`.
2180
- * This matrix is not modified.
2181
- *
2182
- * @param {number} angleX The X-angle amount in degrees to skew.
2183
- * @param {number} angleY The angle amount in degrees to skew.
2184
- * @return {CSSMatrix} The resulted matrix
2185
- */
2186
- CSSMatrix.prototype.skew = function skew (angleX, angleY) {
2187
- return Multiply(this, Skew(angleX, angleY));
2188
- };
2189
-
2190
- /**
2191
- * Transforms a specified vector using the matrix, returning a new
2192
- * {x,y,z,w} Tuple *Object* comprising the transformed vector.
2193
- * Neither the matrix nor the original vector are altered.
2194
- *
2195
- * The method is equivalent with `transformPoint()` method
2196
- * of the `DOMMatrix` constructor.
2197
- *
2198
- * @param {CSSM.PointTuple | DOMPoint} t Tuple with `{x,y,z,w}` components
2199
- * @return {CSSM.PointTuple | DOMPoint} the resulting Tuple
2200
- */
2201
- CSSMatrix.prototype.transformPoint = function transformPoint (t) {
2202
- var m = this;
2203
-
2204
- var x = m.m11 * t.x + m.m21 * t.y + m.m31 * t.z + m.m41 * t.w;
2205
- var y = m.m12 * t.x + m.m22 * t.y + m.m32 * t.z + m.m42 * t.w;
2206
- var z = m.m13 * t.x + m.m23 * t.y + m.m33 * t.z + m.m43 * t.w;
2207
- var w = m.m14 * t.x + m.m24 * t.y + m.m34 * t.z + m.m44 * t.w;
2208
-
2209
- return t instanceof DOMPoint
2210
- ? new DOMPoint(x, y, z, w)
2211
- : {
2212
- x: x, y: y, z: z, w: w,
2213
- };
2214
- };
2215
-
2216
- Object.defineProperties( CSSMatrix.prototype, prototypeAccessors );
2217
-
2218
- // Add Transform Functions to CSSMatrix object
2219
- // without creating a TypeScript namespace.
2220
- Object.assign(CSSMatrix, {
2221
- Translate: Translate,
2222
- Rotate: Rotate,
2223
- RotateAxisAngle: RotateAxisAngle,
2224
- Scale: Scale,
2225
- SkewX: SkewX,
2226
- SkewY: SkewY,
2227
- Skew: Skew,
2228
- Multiply: Multiply,
2229
- fromArray: fromArray,
2230
- fromMatrix: fromMatrix,
2231
- fromString: fromString,
2232
- toArray: toArray,
2233
- });
2234
-
2235
- var version$1 = "1.0.3";
2236
-
2237
- /**
2238
- * A global namespace for library version.
2239
- * @type {string}
2240
- */
2241
- var Version$1 = version$1;
2242
-
2243
- /** @typedef {import('../types/index')} */
2244
-
2245
- Object.assign(CSSMatrix, { Version: Version$1 });
2246
-
2247
- /**
2248
- * Returns a transformation matrix to apply to `<path>` elements.
2249
- *
2250
- * @see SVGPath.transformObject
2251
- *
2252
- * @param {SVGPath.transformObject} transform the `transformObject`
2253
- * @returns {CSSMatrix} a new transformation matrix
2254
- */
2255
- function getSVGMatrix(transform) {
2256
- var matrix = new CSSMatrix();
2257
- var origin = transform.origin;
2258
- var originX = origin[0];
2259
- var originY = origin[1];
2260
- var translate = transform.translate;
2261
- var rotate = transform.rotate;
2262
- var skew = transform.skew;
2263
- var scale = transform.scale;
2264
-
2265
- // set translate
2266
- if (Array.isArray(translate) && translate.every(function (x) { return !Number.isNaN(+x); })
2267
- && translate.some(function (x) { return x !== 0; })) {
2268
- matrix = matrix.translate.apply(matrix, translate);
2269
- } else if (typeof translate === 'number' && !Number.isNaN(translate)) {
2270
- matrix = matrix.translate(translate);
2271
- }
2272
-
2273
- if (rotate || skew || scale) {
2274
- // set SVG transform-origin, always defined
2275
- matrix = matrix.translate(originX, originY);
2276
-
2277
- // set rotation
2278
- if (Array.isArray(rotate) && rotate.every(function (x) { return !Number.isNaN(+x); })
2279
- && rotate.some(function (x) { return x !== 0; })) {
2280
- matrix = matrix.rotate.apply(matrix, rotate);
2281
- } else if (typeof rotate === 'number' && !Number.isNaN(rotate)) {
2282
- matrix = matrix.rotate(rotate);
2283
- }
2284
-
2285
- // set skew(s)
2286
- if (Array.isArray(skew) && skew.every(function (x) { return !Number.isNaN(+x); })
2287
- && skew.some(function (x) { return x !== 0; })) {
2288
- matrix = skew[0] ? matrix.skewX(skew[0]) : matrix;
2289
- matrix = skew[1] ? matrix.skewY(skew[1]) : matrix;
2290
- } else if (typeof skew === 'number' && !Number.isNaN(skew)) {
2291
- matrix = matrix.skewX(skew);
2292
- }
2293
-
2294
- // set scale
2295
- if (Array.isArray(scale) && scale.every(function (x) { return !Number.isNaN(+x); })
2296
- && scale.some(function (x) { return x !== 1; })) {
2297
- matrix = matrix.scale.apply(matrix, scale);
2298
- } else if (typeof scale === 'number' && !Number.isNaN(scale)) {
2299
- matrix = matrix.scale(scale);
2300
- }
2301
- // set SVG transform-origin
2302
- matrix = matrix.translate(-originX, -originY);
2303
- }
2304
-
2305
- return matrix;
2306
- }
2307
-
2308
- /**
2309
- * Transforms a specified point using a matrix, returning a new
2310
- * Tuple *Object* comprising of the transformed point.
2311
- * Neither the matrix nor the original point are altered.
2312
- *
2313
- * @copyright thednp © 2021
2314
- *
2315
- * @param {SVGPath.CSSMatrix} M CSSMatrix instance
2316
- * @param {[number, number, number, number]} v Tuple
2317
- * @return {[number, number, number, number]} the resulting Tuple
2318
- */
2319
- function translatePoint(M, v) {
2320
- var assign;
2321
-
2322
- var m = Translate.apply(void 0, v);
2323
-
2324
- (assign = v, m.m44 = assign[3]);
2325
- m = M.multiply(m);
2326
-
2327
- return [m.m41, m.m42, m.m43, m.m44];
2328
- }
2329
-
2330
- /**
2331
- * Returns the [x,y] projected coordinates for a given an [x,y] point
2332
- * and an [x,y,z] perspective origin point.
2333
- *
2334
- * Equation found here =>
2335
- * http://en.wikipedia.org/wiki/3D_projection#Diagram
2336
- * Details =>
2337
- * https://stackoverflow.com/questions/23792505/predicted-rendering-of-css-3d-transformed-pixel
2338
- *
2339
- * @param {SVGPath.CSSMatrix} m the transformation matrix
2340
- * @param {[number, number]} point2D the initial [x,y] coordinates
2341
- * @param {number[]} origin the [x,y,z] transform origin
2342
- * @returns {[number, number]} the projected [x,y] coordinates
2343
- */
2344
- function projection2d(m, point2D, origin) {
2345
- var originX = origin[0];
2346
- var originY = origin[1];
2347
- var originZ = origin[2];
2348
- var ref = translatePoint(m, point2D.concat( [0], [1]));
2349
- var x = ref[0];
2350
- var y = ref[1];
2351
- var z = ref[2];
2352
-
2353
- var relativePositionX = x - originX;
2354
- var relativePositionY = y - originY;
2355
- var relativePositionZ = z - originZ;
2356
-
2357
- return [
2358
- // protect against division by ZERO
2359
- relativePositionX * (Math.abs(originZ) / Math.abs(relativePositionZ) || 1) + originX,
2360
- relativePositionY * (Math.abs(originZ) / Math.abs(relativePositionZ) || 1) + originY ];
2361
- }
2362
-
2363
- /**
2364
- * Apply a 2D / 3D transformation to a `pathArray` instance.
2365
- *
2366
- * Since *SVGElement* doesn't support 3D transformation, this function
2367
- * creates a 2D projection of the <path> element.
2368
- *
2369
- * @param {SVGPath.pathArray} path the `pathArray` to apply transformation
2370
- * @param {SVGPath.transformObject} transform the transform functions `Object`
2371
- * @returns {SVGPath.pathArray} the resulted `pathArray`
2372
- */
2373
- function transformPath(path, transform) {
2374
- var assign;
2375
-
2376
- var x = 0; var y = 0; var i; var j; var ii; var jj; var lx; var ly;
2377
- var absolutePath = pathToAbsolute(path);
2378
- var transformProps = transform && Object.keys(transform);
2379
-
2380
- // when used as a static method, invalidate somehow
2381
- if (!transform || !transformProps.length) { return clonePath(absolutePath); }
2382
-
2383
- var normalizedPath = normalizePath(absolutePath);
2384
- // transform origin is extremely important
2385
- if (!transform.origin) {
2386
- var defaultOrigin = defaultOptions.origin;
2387
- Object.assign(transform, { origin: defaultOrigin });
2388
- }
2389
- var matrixInstance = getSVGMatrix(transform);
2390
- var origin = transform.origin;
2391
- var params = Object.assign({}, paramsParser);
2392
- /** @type {SVGPath.pathSegment} */
2393
- var segment = [];
2394
- var seglen = 0;
2395
- var pathCommand = '';
2396
- /** @type {SVGPath.pathTransformList[]} */
2397
- var transformedPath = [];
2398
- var allPathCommands = []; // needed for arc to curve transformation
2399
-
2400
- if (!matrixInstance.isIdentity) {
2401
- for (i = 0, ii = absolutePath.length; i < ii; i += 1) {
2402
- segment = absolutePath[i];
2403
-
2404
- /* istanbul ignore else */
2405
- if (absolutePath[i]) { (assign = segment, pathCommand = assign[0]); }
2406
-
2407
- // REPLACE Arc path commands with Cubic Beziers
2408
- // we don't have any scripting know-how on 3d ellipse transformation
2409
- // Arc segments don't work 3D transformations or skews
2410
- /// ////////////////////////////////////////
2411
- allPathCommands[i] = pathCommand;
2412
-
2413
- if (pathCommand === 'A') {
2414
- segment = segmentToCubic(normalizedPath[i], params);
2415
-
2416
- absolutePath[i] = segmentToCubic(normalizedPath[i], params);
2417
- fixArc(absolutePath, allPathCommands, i);
2418
-
2419
- normalizedPath[i] = segmentToCubic(normalizedPath[i], params);
2420
- fixArc(normalizedPath, allPathCommands, i);
2421
- ii = Math.max(absolutePath.length, normalizedPath.length);
2422
- }
2423
-
2424
- /// ////////////////////////////////////////
2425
- segment = normalizedPath[i];
2426
- seglen = segment.length;
2427
-
2428
- params.x1 = +segment[seglen - 2];
2429
- params.y1 = +segment[seglen - 1];
2430
- params.x2 = +(segment[seglen - 4]) || params.x1;
2431
- params.y2 = +(segment[seglen - 3]) || params.y1;
2432
-
2433
- /** @type {SVGPath.pathTransformList} */
2434
- var result = {
2435
- s: absolutePath[i], c: absolutePath[i][0], x: params.x1, y: params.y1,
2436
- };
2437
-
2438
- transformedPath = transformedPath.concat( [result]);
2439
- }
2440
-
2441
- return transformedPath.map(function (seg) {
2442
- var assign, assign$1;
2443
-
2444
- pathCommand = seg.c;
2445
- segment = seg.s;
2446
- switch (pathCommand) {
2447
- case 'L':
2448
- case 'H':
2449
- case 'V':
2450
- (assign = projection2d(matrixInstance, [seg.x, seg.y], origin), lx = assign[0], ly = assign[1]);
2451
-
2452
- /* istanbul ignore else */
2453
- if (x !== lx && y !== ly) {
2454
- segment = ['L', lx, ly];
2455
- } else if (y === ly) {
2456
- segment = ['H', lx];
2457
- } else if (x === lx) {
2458
- segment = ['V', ly];
2459
- }
2460
-
2461
- x = lx; y = ly; // now update x and y
2462
-
2463
- return segment;
2464
- default:
2465
-
2466
- for (j = 1, jj = segment.length; j < jj; j += 2) {
2467
- (assign$1 = projection2d(matrixInstance, [+segment[j], +segment[j + 1]], origin), x = assign$1[0], y = assign$1[1]);
2468
- segment[j] = x;
2469
- segment[j + 1] = y;
2470
- }
2471
-
2472
- return segment;
2473
- }
2474
- });
2475
- }
2476
- return clonePath(absolutePath);
2477
- }
2478
-
2479
- /**
2480
- *
2481
- * @param {{x: number, y: number}} v0
2482
- * @param {{x: number, y: number}} v1
2483
- * @returns {{x: number, y: number}}
2484
- */
2485
- function angleBetween(v0, v1) {
2486
- var v0x = v0.x;
2487
- var v0y = v0.y;
2488
- var v1x = v1.x;
2489
- var v1y = v1.y;
2490
- var p = v0x * v1x + v0y * v1y;
2491
- var n = Math.sqrt((Math.pow( v0x, 2 ) + Math.pow( v0y, 2 )) * (Math.pow( v1x, 2 ) + Math.pow( v1y, 2 )));
2492
- var sign = v0x * v1y - v0y * v1x < 0 ? -1 : 1;
2493
- var angle = sign * Math.acos(p / n);
2494
-
2495
- return angle;
2496
- }
2497
-
2498
- /**
2499
- * Returns a {x,y} point at a given length, the total length and
2500
- * the minimum and maximum {x,y} coordinates of a C (cubic-bezier) segment.
2501
- * @see https://github.com/MadLittleMods/svg-curve-lib/blob/master/src/js/svg-curve-lib.js
2502
- *
2503
- * @param {number} x1 the starting x position
2504
- * @param {number} y1 the starting y position
2505
- * @param {number} RX x-radius of the arc
2506
- * @param {number} RY y-radius of the arc
2507
- * @param {number} angle x-axis-rotation of the arc
2508
- * @param {number} LAF large-arc-flag of the arc
2509
- * @param {number} SF sweep-flag of the arc
2510
- * @param {number} x the ending x position
2511
- * @param {number} y the ending y position
2512
- * @param {number} t the point distance
2513
- * @returns {{x: number, y: number}} the requested point
2514
- */
2515
- function getPointAtArcSegmentLength(x1, y1, RX, RY, angle, LAF, SF, x, y, t) {
2516
- var abs = Math.abs;
2517
- var sin = Math.sin;
2518
- var cos = Math.cos;
2519
- var sqrt = Math.sqrt;
2520
- var PI = Math.PI;
2521
- var rx = abs(RX);
2522
- var ry = abs(RY);
2523
- var xRot = ((angle % 360) + 360) % 360;
2524
- var xRotRad = xRot * (PI / 180);
2525
-
2526
- if (x1 === x && y1 === y) {
2527
- return { x: x1, y: y1 };
2528
- }
2529
-
2530
- if (rx === 0 || ry === 0) {
2531
- return segmentLineFactory(x1, y1, x, y, t).point;
2532
- }
2533
-
2534
- var dx = (x1 - x) / 2;
2535
- var dy = (y1 - y) / 2;
2536
-
2537
- var transformedPoint = {
2538
- x: cos(xRotRad) * dx + sin(xRotRad) * dy,
2539
- y: -sin(xRotRad) * dx + cos(xRotRad) * dy,
2540
- };
2541
-
2542
- var radiiCheck = Math.pow( transformedPoint.x, 2 ) / Math.pow( rx, 2 ) + Math.pow( transformedPoint.y, 2 ) / Math.pow( ry, 2 );
2543
-
2544
- if (radiiCheck > 1) {
2545
- rx *= sqrt(radiiCheck);
2546
- ry *= sqrt(radiiCheck);
2547
- }
2548
-
2549
- var cSquareNumerator = Math.pow( rx, 2 ) * Math.pow( ry, 2 )
2550
- - Math.pow( rx, 2 ) * Math.pow( transformedPoint.y, 2 )
2551
- - Math.pow( ry, 2 ) * Math.pow( transformedPoint.x, 2 );
2552
-
2553
- var cSquareRootDenom = Math.pow( rx, 2 ) * Math.pow( transformedPoint.y, 2 )
2554
- + Math.pow( ry, 2 ) * Math.pow( transformedPoint.x, 2 );
2555
-
2556
- var cRadicand = cSquareNumerator / cSquareRootDenom;
2557
- cRadicand = cRadicand < 0 ? 0 : cRadicand;
2558
- var cCoef = (LAF !== SF ? 1 : -1) * sqrt(cRadicand);
2559
- var transformedCenter = {
2560
- x: cCoef * ((rx * transformedPoint.y) / ry),
2561
- y: cCoef * (-(ry * transformedPoint.x) / rx),
2562
- };
2563
-
2564
- var center = {
2565
- x: cos(xRotRad) * transformedCenter.x
2566
- - sin(xRotRad) * transformedCenter.y + (x1 + x) / 2,
2567
- y: sin(xRotRad) * transformedCenter.x
2568
- + cos(xRotRad) * transformedCenter.y + (y1 + y) / 2,
2569
- };
2570
-
2571
- var startVector = {
2572
- x: (transformedPoint.x - transformedCenter.x) / rx,
2573
- y: (transformedPoint.y - transformedCenter.y) / ry,
2574
- };
2575
-
2576
- var startAngle = angleBetween({ x: 1, y: 0 }, startVector);
2577
-
2578
- var endVector = {
2579
- x: (-transformedPoint.x - transformedCenter.x) / rx,
2580
- y: (-transformedPoint.y - transformedCenter.y) / ry,
2581
- };
2582
-
2583
- var sweepAngle = angleBetween(startVector, endVector);
2584
- if (!SF && sweepAngle > 0) {
2585
- sweepAngle -= 2 * PI;
2586
- } else if (SF && sweepAngle < 0) {
2587
- sweepAngle += 2 * PI;
2588
- }
2589
- sweepAngle %= 2 * PI;
2590
-
2591
- var alpha = startAngle + sweepAngle * t;
2592
- var ellipseComponentX = rx * cos(alpha);
2593
- var ellipseComponentY = ry * sin(alpha);
2594
-
2595
- var point = {
2596
- x: cos(xRotRad) * ellipseComponentX
2597
- - sin(xRotRad) * ellipseComponentY
2598
- + center.x,
2599
- y: sin(xRotRad) * ellipseComponentX
2600
- + cos(xRotRad) * ellipseComponentY
2601
- + center.y,
2602
- };
2603
-
2604
- // to be used later
2605
- // point.ellipticalArcStartAngle = startAngle;
2606
- // point.ellipticalArcEndAngle = startAngle + sweepAngle;
2607
- // point.ellipticalArcAngle = alpha;
2608
-
2609
- // point.ellipticalArcCenter = center;
2610
- // point.resultantRx = rx;
2611
- // point.resultantRy = ry;
2612
-
2613
- return point;
2614
- }
2615
-
2616
- /**
2617
- * Returns a {x,y} point at a given length, the total length and
2618
- * the shape minimum and maximum {x,y} coordinates of an A (arc-to) segment.
2619
- *
2620
- * @param {number} X1 the starting x position
2621
- * @param {number} Y1 the starting y position
2622
- * @param {number} RX x-radius of the arc
2623
- * @param {number} RY y-radius of the arc
2624
- * @param {number} angle x-axis-rotation of the arc
2625
- * @param {number} LAF large-arc-flag of the arc
2626
- * @param {number} SF sweep-flag of the arc
2627
- * @param {number} X2 the ending x position
2628
- * @param {number} Y2 the ending y position
2629
- * @param {number} distance the point distance
2630
- * @returns {SVGPath.lengthFactory} the segment length, point, min & max
2631
- */
2632
- function segmentArcFactory(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, distance) {
2633
- var assign;
2634
-
2635
- var distanceIsNumber = typeof distance === 'number';
2636
- var x = X1; var y = Y1;
2637
- var LENGTH = 0;
2638
- var prev = [x, y, LENGTH];
2639
- var cur = [x, y];
2640
- var t = 0;
2641
- var POINT = { x: 0, y: 0 };
2642
- var POINTS = [{ x: x, y: y }];
2643
-
2644
- if (distanceIsNumber && distance <= 0) {
2645
- POINT = { x: x, y: y };
2646
- }
2647
-
2648
- var sampleSize = 300;
2649
- for (var j = 0; j <= sampleSize; j += 1) {
2650
- t = j / sampleSize;
2651
-
2652
- ((assign = getPointAtArcSegmentLength(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, t), x = assign.x, y = assign.y));
2653
- POINTS = POINTS.concat( [{ x: x, y: y }]);
2654
- LENGTH += distanceSquareRoot(cur, [x, y]);
2655
- cur = [x, y];
2656
-
2657
- if (distanceIsNumber && LENGTH > distance && distance > prev[2]) {
2658
- var dv = (LENGTH - distance) / (LENGTH - prev[2]);
2659
-
2660
- POINT = {
2661
- x: cur[0] * (1 - dv) + prev[0] * dv,
2662
- y: cur[1] * (1 - dv) + prev[1] * dv,
2663
- };
2664
- }
2665
- prev = [x, y, LENGTH];
2666
- }
2667
-
2668
- if (distanceIsNumber && distance >= LENGTH) {
2669
- POINT = { x: X2, y: Y2 };
2670
- }
2671
-
2672
- return {
2673
- length: LENGTH,
2674
- point: POINT,
2675
- min: {
2676
- x: Math.min.apply(Math, POINTS.map(function (n) { return n.x; })),
2677
- y: Math.min.apply(Math, POINTS.map(function (n) { return n.y; })),
2678
- },
2679
- max: {
2680
- x: Math.max.apply(Math, POINTS.map(function (n) { return n.x; })),
2681
- y: Math.max.apply(Math, POINTS.map(function (n) { return n.y; })),
2682
- },
2683
- };
2684
- }
2685
-
2686
- /**
2687
- * Returns a {x,y} point at a given length, the total length and
2688
- * the minimum and maximum {x,y} coordinates of a C (cubic-bezier) segment.
2689
- *
2690
- * @param {number} x1 the starting point X
2691
- * @param {number} y1 the starting point Y
2692
- * @param {number} c1x the first control point X
2693
- * @param {number} c1y the first control point Y
2694
- * @param {number} c2x the second control point X
2695
- * @param {number} c2y the second control point Y
2696
- * @param {number} x2 the ending point X
2697
- * @param {number} y2 the ending point Y
2698
- * @param {number} t a [0-1] ratio
2699
- * @returns {{x: number, y: number}} the cubic-bezier segment length
2700
- */
2701
- function getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t) {
2702
- var t1 = 1 - t;
2703
- return {
2704
- x: (Math.pow( t1, 3 )) * x1
2705
- + 3 * (Math.pow( t1, 2 )) * t * c1x
2706
- + 3 * t1 * (Math.pow( t, 2 )) * c2x
2707
- + (Math.pow( t, 3 )) * x2,
2708
- y: (Math.pow( t1, 3 )) * y1
2709
- + 3 * (Math.pow( t1, 2 )) * t * c1y
2710
- + 3 * t1 * (Math.pow( t, 2 )) * c2y
2711
- + (Math.pow( t, 3 )) * y2,
2712
- };
2713
- }
2714
-
2715
- /**
2716
- * Returns the length of a C (cubic-bezier) segment
2717
- * or an {x,y} point at a given length.
2718
- *
2719
- * @param {number} x1 the starting point X
2720
- * @param {number} y1 the starting point Y
2721
- * @param {number} c1x the first control point X
2722
- * @param {number} c1y the first control point Y
2723
- * @param {number} c2x the second control point X
2724
- * @param {number} c2y the second control point Y
2725
- * @param {number} x2 the ending point X
2726
- * @param {number} y2 the ending point Y
2727
- * @param {number=} distance the point distance
2728
- * @returns {SVGPath.lengthFactory} the segment length, point, min & max
2729
- */
2730
- function segmentCubicFactory(x1, y1, c1x, c1y, c2x, c2y, x2, y2, distance) {
2731
- var assign;
2732
-
2733
- var distanceIsNumber = typeof distance === 'number';
2734
- var x = x1; var y = y1;
2735
- var LENGTH = 0;
2736
- var prev = [x, y, LENGTH];
2737
- var cur = [x, y];
2738
- var t = 0;
2739
- var POINT = { x: 0, y: 0 };
2740
- var POINTS = [{ x: x, y: y }];
2741
-
2742
- if (distanceIsNumber && distance <= 0) {
2743
- POINT = { x: x, y: y };
2744
- }
2745
-
2746
- var sampleSize = 300;
2747
- for (var j = 0; j <= sampleSize; j += 1) {
2748
- t = j / sampleSize;
2749
-
2750
- ((assign = getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t), x = assign.x, y = assign.y));
2751
- POINTS = POINTS.concat( [{ x: x, y: y }]);
2752
- LENGTH += distanceSquareRoot(cur, [x, y]);
2753
- cur = [x, y];
2754
-
2755
- if (distanceIsNumber && LENGTH > distance && distance > prev[2]) {
2756
- var dv = (LENGTH - distance) / (LENGTH - prev[2]);
2757
-
2758
- POINT = {
2759
- x: cur[0] * (1 - dv) + prev[0] * dv,
2760
- y: cur[1] * (1 - dv) + prev[1] * dv,
2761
- };
2762
- }
2763
- prev = [x, y, LENGTH];
2764
- }
2765
-
2766
- if (distanceIsNumber && distance >= LENGTH) {
2767
- POINT = { x: x2, y: y2 };
2768
- }
2769
-
2770
- return {
2771
- length: LENGTH,
2772
- point: POINT,
2773
- min: {
2774
- x: Math.min.apply(Math, POINTS.map(function (n) { return n.x; })),
2775
- y: Math.min.apply(Math, POINTS.map(function (n) { return n.y; })),
2776
- },
2777
- max: {
2778
- x: Math.max.apply(Math, POINTS.map(function (n) { return n.x; })),
2779
- y: Math.max.apply(Math, POINTS.map(function (n) { return n.y; })),
2780
- },
2781
- };
2782
- }
2783
-
2784
- /**
2785
- * Returns the {x,y} coordinates of a point at a
2786
- * given length of a quadratic-bezier segment.
2787
- *
2788
- * @see https://github.com/substack/point-at-length
2789
- *
2790
- * @param {number} x1 the starting point X
2791
- * @param {number} y1 the starting point Y
2792
- * @param {number} cx the control point X
2793
- * @param {number} cy the control point Y
2794
- * @param {number} x2 the ending point X
2795
- * @param {number} y2 the ending point Y
2796
- * @param {number} t a [0-1] ratio
2797
- * @returns {{x: number, y: number}} the requested {x,y} coordinates
2798
- */
2799
- function getPointAtQuadSegmentLength(x1, y1, cx, cy, x2, y2, t) {
2800
- var t1 = 1 - t;
2801
- return {
2802
- x: (Math.pow( t1, 2 )) * x1
2803
- + 2 * t1 * t * cx
2804
- + (Math.pow( t, 2 )) * x2,
2805
- y: (Math.pow( t1, 2 )) * y1
2806
- + 2 * t1 * t * cy
2807
- + (Math.pow( t, 2 )) * y2,
2808
- };
2809
- }
2810
-
2811
- /**
2812
- * Returns a {x,y} point at a given length, the total length and
2813
- * the minimum and maximum {x,y} coordinates of a Q (quadratic-bezier) segment.
2814
- *
2815
- * @param {number} x1 the starting point X
2816
- * @param {number} y1 the starting point Y
2817
- * @param {number} qx the control point X
2818
- * @param {number} qy the control point Y
2819
- * @param {number} x2 the ending point X
2820
- * @param {number} y2 the ending point Y
2821
- * @param {number=} distance the distance to point
2822
- * @returns {SVGPath.lengthFactory} the segment length, point, min & max
2823
- */
2824
- function segmentQuadFactory(x1, y1, qx, qy, x2, y2, distance) {
2825
- var assign;
2826
-
2827
- var distanceIsNumber = typeof distance === 'number';
2828
- var x = x1; var y = y1;
2829
- var LENGTH = 0;
2830
- var prev = [x, y, LENGTH];
2831
- var cur = [x, y];
2832
- var t = 0;
2833
- var POINT = { x: 0, y: 0 };
2834
- var POINTS = [{ x: x, y: y }];
2835
-
2836
- if (distanceIsNumber && distance <= 0) {
2837
- POINT = { x: x, y: y };
2838
- }
2839
-
2840
- var sampleSize = 300;
2841
- for (var j = 0; j <= sampleSize; j += 1) {
2842
- t = j / sampleSize;
2843
-
2844
- ((assign = getPointAtQuadSegmentLength(x1, y1, qx, qy, x2, y2, t), x = assign.x, y = assign.y));
2845
- POINTS = POINTS.concat( [{ x: x, y: y }]);
2846
- LENGTH += distanceSquareRoot(cur, [x, y]);
2847
- cur = [x, y];
2848
-
2849
- if (distanceIsNumber && LENGTH > distance && distance > prev[2]) {
2850
- var dv = (LENGTH - distance) / (LENGTH - prev[2]);
2851
-
2852
- POINT = {
2853
- x: cur[0] * (1 - dv) + prev[0] * dv,
2854
- y: cur[1] * (1 - dv) + prev[1] * dv,
2855
- };
2856
- }
2857
- prev = [x, y, LENGTH];
2858
- }
2859
-
2860
- /* istanbul ignore else */
2861
- if (distanceIsNumber && distance >= LENGTH) {
2862
- POINT = { x: x2, y: y2 };
2863
- }
2864
-
2865
- return {
2866
- length: LENGTH,
2867
- point: POINT,
2868
- min: {
2869
- x: Math.min.apply(Math, POINTS.map(function (n) { return n.x; })),
2870
- y: Math.min.apply(Math, POINTS.map(function (n) { return n.y; })),
2871
- },
2872
- max: {
2873
- x: Math.max.apply(Math, POINTS.map(function (n) { return n.x; })),
2874
- y: Math.max.apply(Math, POINTS.map(function (n) { return n.y; })),
2875
- },
2876
- };
2877
- }
2878
-
2879
- /**
2880
- * Returns a {x,y} point at a given length
2881
- * of a shape, the shape total length and
2882
- * the shape minimum and maximum {x,y} coordinates.
2883
- *
2884
- * @param {string | SVGPath.pathArray} pathInput the `pathArray` to look into
2885
- * @param {number=} distance the length of the shape to look at
2886
- * @returns {SVGPath.lengthFactory} the path length, point, min & max
2887
- */
2888
- function pathLengthFactory(pathInput, distance) {
2889
- var assign, assign$1, assign$2, assign$3, assign$4, assign$5, assign$6, assign$7;
2890
-
2891
- var path = normalizePath(pathInput);
2892
- var distanceIsNumber = typeof distance === 'number';
2893
- var isM;
2894
- var data = [];
2895
- var pathCommand;
2896
- var x = 0;
2897
- var y = 0;
2898
- var mx = 0;
2899
- var my = 0;
2900
- var seg;
2901
- var MIN = [];
2902
- var MAX = [];
2903
- var length = 0;
2904
- var min = { x: 0, y: 0 };
2905
- var max = min;
2906
- var point = min;
2907
- var POINT = min;
2908
- var LENGTH = 0;
2909
-
2910
- for (var i = 0, ll = path.length; i < ll; i += 1) {
2911
- seg = path[i];
2912
- (assign = seg, pathCommand = assign[0]);
2913
- isM = pathCommand === 'M';
2914
- data = !isM ? [x, y ].concat( seg.slice(1)) : data;
2915
-
2916
- // this segment is always ZERO
2917
- /* istanbul ignore else */
2918
- if (isM) {
2919
- // remember mx, my for Z
2920
- (assign$1 = seg, mx = assign$1[1], my = assign$1[2]);
2921
- min = { x: mx, y: my };
2922
- max = min;
2923
- length = 0;
2924
-
2925
- if (distanceIsNumber && distance < 0.001) {
2926
- POINT = min;
2927
- }
2928
- } else if (pathCommand === 'L') {
2929
- ((assign$2 = segmentLineFactory.apply(void 0, data.concat( [(distance || 0) - LENGTH] )), length = assign$2.length, min = assign$2.min, max = assign$2.max, point = assign$2.point));
2930
- } else if (pathCommand === 'A') {
2931
- ((assign$3 = segmentArcFactory.apply(void 0, data.concat( [(distance || 0) - LENGTH] )), length = assign$3.length, min = assign$3.min, max = assign$3.max, point = assign$3.point));
2932
- } else if (pathCommand === 'C') {
2933
- ((assign$4 = segmentCubicFactory.apply(void 0, data.concat( [(distance || 0) - LENGTH] )), length = assign$4.length, min = assign$4.min, max = assign$4.max, point = assign$4.point));
2934
- } else if (pathCommand === 'Q') {
2935
- ((assign$5 = segmentQuadFactory.apply(void 0, data.concat( [(distance || 0) - LENGTH] )), length = assign$5.length, min = assign$5.min, max = assign$5.max, point = assign$5.point));
2936
- } else if (pathCommand === 'Z') {
2937
- data = [x, y, mx, my];
2938
- ((assign$6 = segmentLineFactory.apply(void 0, data.concat( [(distance || 0) - LENGTH] )), length = assign$6.length, min = assign$6.min, max = assign$6.max, point = assign$6.point));
2939
- }
2940
-
2941
- if (distanceIsNumber && LENGTH < distance && LENGTH + length >= distance) {
2942
- POINT = point;
2943
- }
2944
-
2945
- MAX = MAX.concat( [max]);
2946
- MIN = MIN.concat( [min]);
2947
- LENGTH += length;
2948
-
2949
- (assign$7 = pathCommand !== 'Z' ? seg.slice(-2) : [mx, my], x = assign$7[0], y = assign$7[1]);
2950
- }
2951
-
2952
- // native `getPointAtLength` behavior when the given distance
2953
- // is higher than total length
2954
- if (distanceIsNumber && distance >= LENGTH) {
2955
- POINT = { x: x, y: y };
2956
- }
2957
-
2958
- return {
2959
- length: LENGTH,
2960
- point: POINT,
2961
- min: {
2962
- x: Math.min.apply(Math, MIN.map(function (n) { return n.x; })),
2963
- y: Math.min.apply(Math, MIN.map(function (n) { return n.y; })),
2964
- },
2965
- max: {
2966
- x: Math.max.apply(Math, MAX.map(function (n) { return n.x; })),
2967
- y: Math.max.apply(Math, MAX.map(function (n) { return n.y; })),
2968
- },
2969
- };
2970
- }
2971
-
2972
- /**
2973
- * Returns the bounding box of a shape.
2974
- *
2975
- * @param {SVGPath.pathArray=} path the shape `pathArray`
2976
- * @returns {SVGPath.pathBBox} the length of the cubic-bezier segment
2977
- */
2978
- function getPathBBox(path) {
2979
- if (!path) {
2980
- return {
2981
- x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0, cx: 0, cy: 0, cz: 0,
2982
- };
2983
- }
2984
-
2985
- var ref = pathLengthFactory(path);
2986
- var ref_min = ref.min;
2987
- var xMin = ref_min.x;
2988
- var yMin = ref_min.y;
2989
- var ref_max = ref.max;
2990
- var xMax = ref_max.x;
2991
- var yMax = ref_max.y;
2992
-
2993
- var width = xMax - xMin;
2994
- var height = yMax - yMin;
2995
-
2996
- return {
2997
- width: width,
2998
- height: height,
2999
- x: xMin,
3000
- y: yMin,
3001
- x2: xMax,
3002
- y2: yMax,
3003
- cx: xMin + width / 2,
3004
- cy: yMin + height / 2,
3005
- // an estimted guess
3006
- cz: Math.max(width, height) + Math.min(width, height) / 2,
3007
- };
3008
- }
3009
-
3010
- /**
3011
- * Returns the shape total length, or the equivalent to `shape.getTotalLength()`.
3012
- *
3013
- * The `normalizePath` version is lighter, faster, more efficient and more accurate
3014
- * with paths that are not `curveArray`.
3015
- *
3016
- * @param {string | SVGPath.pathArray} pathInput the target `pathArray`
3017
- * @returns {number} the shape total length
3018
- */
3019
- function getTotalLength(pathInput) {
3020
- return pathLengthFactory(pathInput).length;
3021
- }
3022
-
3023
- /**
3024
- * Returns [x,y] coordinates of a point at a given length of a shape.
3025
- *
3026
- * @param {string | SVGPath.pathArray} pathInput the `pathArray` to look into
3027
- * @param {number} distance the length of the shape to look at
3028
- * @returns {{x: number, y: number}} the requested {x, y} point coordinates
3029
- */
3030
- function getPointAtLength(pathInput, distance) {
3031
- return pathLengthFactory(pathInput, distance).point;
3032
- }
3033
-
3034
- /**
3035
- * Creates a new SVGPathCommander instance with the following properties:
3036
- * * segments: `pathArray`
3037
- * * round: number
3038
- * * origin: [number, number, number?]
3039
- *
3040
- * @class
3041
- * @author thednp <https://github.com/thednp/svg-path-commander>
3042
- * @returns {SVGPathCommander} a new SVGPathCommander instance
3043
- */
3044
- var SVGPathCommander = function SVGPathCommander(pathValue, config) {
3045
- var assign;
3046
-
3047
- var instanceOptions = config || {};
3048
-
3049
- var undefPath = typeof pathValue === 'undefined';
3050
-
3051
- if (undefPath || !pathValue.length) {
3052
- throw TypeError((error + ": \"pathValue\" is " + (undefPath ? 'undefined' : 'empty')));
3053
- }
3054
-
3055
- var segments = parsePathString(pathValue);
3056
- if (typeof segments === 'string') {
3057
- throw TypeError(segments);
3058
- }
3059
-
3060
- /**
3061
- * @type {SVGPath.pathArray}
3062
- */
3063
- this.segments = segments;
3064
-
3065
- var ref = this.getBBox();
3066
- var width = ref.width;
3067
- var height = ref.height;
3068
- var cx = ref.cx;
3069
- var cy = ref.cy;
3070
- var cz = ref.cz;
3071
-
3072
- // set instance options.round
3073
- var roundOption = instanceOptions.round;
3074
- var originOption = instanceOptions.origin;
3075
- var round;
3076
-
3077
- if (roundOption === 'auto') {
3078
- var pathScale = (("" + (Math.floor(Math.max(width, height))))).length;
3079
- round = pathScale >= 4 ? 0 : 4 - pathScale;
3080
- } else if (Number.isInteger(roundOption) || roundOption === 'off') {
3081
- round = roundOption;
3082
- } else {
3083
- ((assign = defaultOptions, round = assign.round));
3084
- }
3085
-
3086
- // set instance options.origin
3087
- // the SVGPathCommander class will always override the default origin
3088
- var origin;
3089
- if (Array.isArray(originOption) && originOption.length >= 2) {
3090
- var ref$1 = originOption.map(Number);
3091
- var originX = ref$1[0];
3092
- var originY = ref$1[1];
3093
- var originZ = ref$1[2];
3094
- origin = [
3095
- !Number.isNaN(originX) ? originX : cx,
3096
- !Number.isNaN(originY) ? originY : cy,
3097
- !Number.isNaN(originZ) ? originZ : cz ];
3098
- } else {
3099
- origin = [cx, cy, cz];
3100
- }
3101
-
3102
- /** @type {number | 'off'} */
3103
- this.round = round;
3104
- /** @type {[number, number, number=]} */
3105
- this.origin = origin;
3106
-
3107
- return this;
3108
- };
3109
-
3110
- /**
3111
- * Returns the path bounding box, equivalent to native `path.getBBox()`.
3112
- * @public
3113
- * @returns {SVGPath.pathBBox}
3114
- */
3115
- SVGPathCommander.prototype.getBBox = function getBBox () {
3116
- return getPathBBox(this.segments);
3117
- };
3118
-
3119
- /**
3120
- * Returns the total path length, equivalent to native `path.getTotalLength()`.
3121
- * @public
3122
- * @returns {number}
3123
- */
3124
- SVGPathCommander.prototype.getTotalLength = function getTotalLength$1 () {
3125
- return getTotalLength(this.segments);
3126
- };
3127
-
3128
- /**
3129
- * Returns an `{x,y}` point in the path stroke at a given length,
3130
- * equivalent to the native `path.getPointAtLength()`.
3131
- *
3132
- * @public
3133
- * @param {number} length the length
3134
- * @returns {{x: number, y:number}} the requested point
3135
- */
3136
- SVGPathCommander.prototype.getPointAtLength = function getPointAtLength$1 (length) {
3137
- return getPointAtLength(this.segments, length);
3138
- };
3139
-
3140
- /**
3141
- * Convert path to absolute values
3142
- * @public
3143
- */
3144
- SVGPathCommander.prototype.toAbsolute = function toAbsolute () {
3145
- var ref = this;
3146
- var segments = ref.segments;
3147
- this.segments = pathToAbsolute(segments);
3148
- return this;
3149
- };
3150
-
3151
- /**
3152
- * Convert path to relative values
3153
- * @public
3154
- */
3155
- SVGPathCommander.prototype.toRelative = function toRelative () {
3156
- var ref = this;
3157
- var segments = ref.segments;
3158
- this.segments = pathToRelative(segments);
3159
- return this;
3160
- };
3161
-
3162
- /**
3163
- * Convert path to cubic-bezier values. In addition, un-necessary `Z`
3164
- * segment is removed if previous segment extends to the `M` segment.
3165
- *
3166
- * @public
3167
- */
3168
- SVGPathCommander.prototype.toCurve = function toCurve () {
3169
- var ref = this;
3170
- var segments = ref.segments;
3171
- this.segments = pathToCurve(segments);
3172
- return this;
3173
- };
3174
-
3175
- /**
3176
- * Reverse the order of the segments and their values.
3177
- * @param {boolean} onlySubpath option to reverse all sub-paths except first
3178
- * @public
3179
- */
3180
- SVGPathCommander.prototype.reverse = function reverse (onlySubpath) {
3181
- this.toAbsolute();
3182
-
3183
- var ref = this;
3184
- var segments = ref.segments;
3185
- var split = splitPath(segments);
3186
- var subPath = split.length > 1 ? split : 0;
3187
-
3188
- var absoluteMultiPath = subPath && clonePath(subPath).map(function (x, i) {
3189
- if (onlySubpath) {
3190
- return i ? reversePath(x) : parsePathString(x);
3191
- }
3192
- return reversePath(x);
3193
- });
3194
-
3195
- var path = [];
3196
- if (subPath) {
3197
- path = absoluteMultiPath.flat(1);
3198
- } else {
3199
- path = onlySubpath ? segments : reversePath(segments);
3200
- }
3201
-
3202
- this.segments = clonePath(path);
3203
- return this;
3204
- };
3205
-
3206
- /**
3207
- * Normalize path in 2 steps:
3208
- * * convert `pathArray`(s) to absolute values
3209
- * * convert shorthand notation to standard notation
3210
- * @public
3211
- */
3212
- SVGPathCommander.prototype.normalize = function normalize () {
3213
- var ref = this;
3214
- var segments = ref.segments;
3215
- this.segments = normalizePath(segments);
3216
- return this;
3217
- };
3218
-
3219
- /**
3220
- * Optimize `pathArray` values:
3221
- * * convert segments to absolute and/or relative values
3222
- * * select segments with shortest resulted string
3223
- * * round values to the specified `decimals` option value
3224
- * @public
3225
- */
3226
- SVGPathCommander.prototype.optimize = function optimize () {
3227
- var ref = this;
3228
- var segments = ref.segments;
3229
-
3230
- this.segments = optimizePath(segments, this.round);
3231
- return this;
3232
- };
3233
-
3234
- /**
3235
- * Transform path using values from an `Object` defined as `transformObject`.
3236
- * @see SVGPath.transformObject for a quick refference
3237
- *
3238
- * @param {SVGPath.transformObject} source a `transformObject`as described above
3239
- * @public
3240
- */
3241
- SVGPathCommander.prototype.transform = function transform (source) {
3242
- if (!source || typeof source !== 'object' || (typeof source === 'object'
3243
- && !['translate', 'rotate', 'skew', 'scale'].some(function (x) { return x in source; }))) { return this; }
3244
-
3245
- /** @type {SVGPath.transformObject} */
3246
- var transform = {};
3247
- Object.keys(source).forEach(function (fn) {
3248
- transform[fn] = Array.isArray(source[fn]) ? [].concat( source[fn] ) : Number(source[fn]);
3249
- });
3250
- var ref = this;
3251
- var segments = ref.segments;
3252
-
3253
- // if origin is not specified
3254
- // it's important that we have one
3255
- var ref$1 = this.origin;
3256
- var cx = ref$1[0];
3257
- var cy = ref$1[1];
3258
- var cz = ref$1[2];
3259
- var origin = transform.origin;
3260
-
3261
- if (Array.isArray(origin) && origin.length >= 2) {
3262
- var ref$2 = origin.map(Number);
3263
- var originX = ref$2[0];
3264
- var originY = ref$2[1];
3265
- var originZ = ref$2[2];
3266
- transform.origin = [
3267
- !Number.isNaN(originX) ? originX : cx,
3268
- !Number.isNaN(originY) ? originY : cy,
3269
- originZ || cz ];
3270
- } else {
3271
- transform.origin = [cx, cy, cz];
3272
- }
3273
-
3274
- this.segments = transformPath(segments, transform);
3275
- return this;
3276
- };
3277
-
3278
- /**
3279
- * Rotate path 180deg vertically
3280
- * @public
3281
- */
3282
- SVGPathCommander.prototype.flipX = function flipX () {
3283
- this.transform({ rotate: [0, 180, 0] });
3284
- return this;
3285
- };
3286
-
3287
- /**
3288
- * Rotate path 180deg horizontally
3289
- * @public
3290
- */
3291
- SVGPathCommander.prototype.flipY = function flipY () {
3292
- this.transform({ rotate: [180, 0, 0] });
3293
- return this;
3294
- };
3295
-
3296
- /**
3297
- * Export the current path to be used
3298
- * for the `d` (description) attribute.
3299
- * @public
3300
- * @return {String} the path string
3301
- */
3302
- SVGPathCommander.prototype.toString = function toString () {
3303
- return pathToString(this.segments, this.round);
3304
- };
3305
-
3306
- /**
3307
- * Returns the area of a single cubic-bezier segment.
3308
- *
3309
- * http://objectmix.com/graphics/133553-area-closed-bezier-curve.html
3310
- *
3311
- * @param {number} x1 the starting point X
3312
- * @param {number} y1 the starting point Y
3313
- * @param {number} c1x the first control point X
3314
- * @param {number} c1y the first control point Y
3315
- * @param {number} c2x the second control point X
3316
- * @param {number} c2y the second control point Y
3317
- * @param {number} x2 the ending point X
3318
- * @param {number} y2 the ending point Y
3319
- * @returns {number} the area of the cubic-bezier segment
3320
- */
3321
- function getCubicSegArea(x1, y1, c1x, c1y, c2x, c2y, x2, y2) {
3322
- return (3 * ((y2 - y1) * (c1x + c2x) - (x2 - x1) * (c1y + c2y)
3323
- + (c1y * (x1 - c2x)) - (c1x * (y1 - c2y))
3324
- + (y2 * (c2x + x1 / 3)) - (x2 * (c2y + y1 / 3)))) / 20;
3325
- }
3326
-
3327
- /**
3328
- * Returns the area of a shape.
3329
- * @author Jürg Lehni & Jonathan Puckey
3330
- *
3331
- * @see https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js
3332
- *
3333
- * @param {SVGPath.pathArray} path the shape `pathArray`
3334
- * @returns {number} the length of the cubic-bezier segment
3335
- */
3336
- function getPathArea(path) {
3337
- var x = 0; var y = 0; var len = 0;
3338
-
3339
- return pathToCurve(path).map(function (seg) {
3340
- var assign, assign$1;
3341
-
3342
- switch (seg[0]) {
3343
- case 'M':
3344
- (assign = seg, x = assign[1], y = assign[2]);
3345
- return 0;
3346
- default:
3347
- len = getCubicSegArea.apply(void 0, [ x, y ].concat( seg.slice(1) ));
3348
- (assign$1 = seg.slice(-2), x = assign$1[0], y = assign$1[1]);
3349
- return len;
3350
- }
3351
- }).reduce(function (a, b) { return a + b; }, 0);
3352
- }
3353
-
3354
- /**
3355
- * Check if a path is drawn clockwise and returns true if so,
3356
- * false otherwise.
3357
- *
3358
- * @param {SVGPath.pathArray} path the path string or `pathArray`
3359
- * @returns {boolean} true when clockwise or false if not
3360
- */
3361
- function getDrawDirection(path) {
3362
- return getPathArea(pathToCurve(path)) >= 0;
3363
- }
3364
-
3365
- /**
3366
- * Returns the segment, its index and length as well as
3367
- * the length to that segment at a given length in a path.
3368
- *
3369
- * @param {string | SVGPath.pathArray} pathInput target `pathArray`
3370
- * @param {number=} distance the given length
3371
- * @returns {SVGPath.segmentProperties=} the requested properties
3372
- */
3373
- function getPropertiesAtLength(pathInput, distance) {
3374
- var pathArray = parsePathString(pathInput);
3375
-
3376
- if (typeof pathArray === 'string') {
3377
- throw TypeError(pathArray);
3378
- }
3379
-
3380
- var pathTemp = [].concat( pathArray );
3381
- var pathLength = getTotalLength(pathTemp);
3382
- var index = pathTemp.length - 1;
3383
- var lengthAtSegment = 0;
3384
- var length = 0;
3385
- var segment = pathArray[0];
3386
- var ref = segment.slice(-2);
3387
- var x = ref[0];
3388
- var y = ref[1];
3389
- var point = { x: x, y: y };
3390
-
3391
- // If the path is empty, return 0.
3392
- if (index <= 0 || !distance || !Number.isFinite(distance)) {
3393
- return {
3394
- segment: segment, index: 0, length: length, point: point, lengthAtSegment: lengthAtSegment,
3395
- };
3396
- }
3397
-
3398
- if (distance >= pathLength) {
3399
- pathTemp = pathArray.slice(0, -1);
3400
- lengthAtSegment = getTotalLength(pathTemp);
3401
- length = pathLength - lengthAtSegment;
3402
- return {
3403
- segment: pathArray[index], index: index, length: length, lengthAtSegment: lengthAtSegment,
3404
- };
3405
- }
3406
-
3407
- var segments = [];
3408
- while (index > 0) {
3409
- segment = pathTemp[index];
3410
- pathTemp = pathTemp.slice(0, -1);
3411
- lengthAtSegment = getTotalLength(pathTemp);
3412
- length = pathLength - lengthAtSegment;
3413
- pathLength = lengthAtSegment;
3414
- segments.push({
3415
- segment: segment, index: index, length: length, lengthAtSegment: lengthAtSegment,
3416
- });
3417
- index -= 1;
3418
- }
3419
-
3420
- return segments.find(function (ref) {
3421
- var l = ref.lengthAtSegment;
3422
-
3423
- return l <= distance;
3424
- });
3425
- }
3426
-
3427
- /**
3428
- * Returns the point and segment in path closest to a given point as well as
3429
- * the distance to the path stroke.
3430
- * @see https://bl.ocks.org/mbostock/8027637
3431
- *
3432
- * @param {string | SVGPath.pathArray} pathInput target `pathArray`
3433
- * @param {{x: number, y: number}} point the given point
3434
- * @returns {SVGPath.pointProperties} the requested properties
3435
- */
3436
- function getPropertiesAtPoint(pathInput, point) {
3437
- var path = (parsePathString(pathInput));
3438
- var normalPath = normalizePath(path);
3439
- var pathLength = getTotalLength(path);
3440
- /** @param {{x: number, y: number}} p */
3441
- var distanceTo = function (p) {
3442
- var dx = p.x - point.x;
3443
- var dy = p.y - point.y;
3444
- return dx * dx + dy * dy;
3445
- };
3446
- var precision = 8;
3447
- var scan;
3448
- var scanDistance = 0;
3449
- var closest;
3450
- var bestLength = 0;
3451
- var bestDistance = Infinity;
3452
-
3453
- // linear scan for coarse approximation
3454
- for (var scanLength = 0; scanLength <= pathLength; scanLength += precision) {
3455
- scan = getPointAtLength(normalPath, scanLength);
3456
- scanDistance = distanceTo(scan);
3457
- if (scanDistance < bestDistance) {
3458
- closest = scan;
3459
- bestLength = scanLength;
3460
- bestDistance = scanDistance;
3461
- }
3462
- }
3463
-
3464
- // binary search for precise estimate
3465
- precision /= 2;
3466
- var before;
3467
- var after;
3468
- var beforeLength = 0;
3469
- var afterLength = 0;
3470
- var beforeDistance = 0;
3471
- var afterDistance = 0;
3472
-
3473
- while (precision > 0.5) {
3474
- beforeLength = bestLength - precision;
3475
- before = getPointAtLength(normalPath, beforeLength);
3476
- beforeDistance = distanceTo(before);
3477
- afterLength = bestLength + precision;
3478
- after = getPointAtLength(normalPath, afterLength);
3479
- afterDistance = distanceTo(after);
3480
- if (beforeLength >= 0 && beforeDistance < bestDistance) {
3481
- closest = before;
3482
- bestLength = beforeLength;
3483
- bestDistance = beforeDistance;
3484
- } else if (afterLength <= pathLength && afterDistance < bestDistance) {
3485
- closest = after;
3486
- bestLength = afterLength;
3487
- bestDistance = afterDistance;
3488
- } else {
3489
- precision /= 2;
3490
- }
3491
- }
3492
-
3493
- var segment = getPropertiesAtLength(path, bestLength);
3494
- var distance = Math.sqrt(bestDistance);
3495
-
3496
- return { closest: closest, distance: distance, segment: segment };
3497
- }
3498
-
3499
- /**
3500
- * Returns the point in path closest to a given point.
3501
- *
3502
- * @param {string | SVGPath.pathArray} pathInput target `pathArray`
3503
- * @param {{x: number, y: number}} point the given point
3504
- * @returns {{x: number, y: number}} the best match
3505
- */
3506
- function getClosestPoint(pathInput, point) {
3507
- return getPropertiesAtPoint(pathInput, point).closest;
3508
- }
3509
-
3510
- /**
3511
- * Returns the path segment which contains a given point.
3512
- *
3513
- * @param {string | SVGPath.pathArray} path the `pathArray` to look into
3514
- * @param {{x: number, y: number}} point the point of the shape to look for
3515
- * @returns {SVGPath.pathSegment?} the requested segment
3516
- */
3517
- function getSegmentOfPoint(path, point) {
3518
- return getPropertiesAtPoint(path, point).segment;
3519
- }
3520
-
3521
- /**
3522
- * Returns the segment at a given length.
3523
- * @param {string | SVGPath.pathArray} pathInput the target `pathArray`
3524
- * @param {number} distance the distance in path to look at
3525
- * @returns {SVGPath.pathSegment?} the requested segment
3526
- */
3527
- function getSegmentAtLength(pathInput, distance) {
3528
- return getPropertiesAtLength(pathInput, distance).segment;
3529
- }
3530
-
3531
- /**
3532
- * Checks if a given point is in the stroke of a path.
3533
- *
3534
- * @param {string | SVGPath.pathArray} pathInput target path
3535
- * @param {{x: number, y: number}} point the given `{x,y}` point
3536
- * @returns {boolean} the query result
3537
- */
3538
- function isPointInStroke(pathInput, point) {
3539
- var ref = getPropertiesAtPoint(pathInput, point);
3540
- var distance = ref.distance;
3541
- return Math.abs(distance) < 0.001; // 0.01 might be more permissive
3542
- }
3543
-
3544
- /**
3545
- * Parses a path string value to determine its validity
3546
- * then returns true if it's valid or false otherwise.
3547
- *
3548
- * @param {string} pathString the path string to be parsed
3549
- * @returns {boolean} the path string validity
3550
- */
3551
- function isValidPath(pathString) {
3552
- if (typeof pathString !== 'string') {
3553
- return false;
3554
- }
3555
-
3556
- var path = new PathParser(pathString);
3557
-
3558
- skipSpaces(path);
3559
-
3560
- while (path.index < path.max && !path.err.length) {
3561
- scanSegment(path);
3562
- }
3563
-
3564
- return !path.err.length && 'mM'.includes(path.segments[0][0]);
3565
- }
3566
-
3567
- /**
3568
- * Supported shapes and their specific parameters.
3569
- * @type {Object.<string, string[]>}
3570
- */
3571
- var shapeParams = {
3572
- line: ['x1', 'y1', 'x2', 'y2'],
3573
- circle: ['cx', 'cy', 'r'],
3574
- ellipse: ['cx', 'cy', 'rx', 'ry'],
3575
- rect: ['width', 'height', 'x', 'y', 'rx', 'ry'],
3576
- polygon: ['points'],
3577
- polyline: ['points'],
3578
- glyph: ['d'],
3579
- };
3580
-
3581
- /**
3582
- * Returns a new `pathArray` from line attributes.
3583
- *
3584
- * @param {SVGPath.lineAttr} attr shape configuration
3585
- * @returns {SVGPath.pathArray} a new line `pathArray`
3586
- */
3587
- function getLinePath(attr) {
3588
- var x1 = attr.x1;
3589
- var y1 = attr.y1;
3590
- var x2 = attr.x2;
3591
- var y2 = attr.y2;
3592
- return [['M', x1, y1], ['L', x2, y2]];
3593
- }
3594
-
3595
- /**
3596
- * Returns a new `pathArray` like from polyline/polygon attributes.
3597
- *
3598
- * @param {SVGPath.polyAttr} attr shape configuration
3599
- * @return {SVGPath.pathArray} a new polygon/polyline `pathArray`
3600
- */
3601
- function getPolyPath(attr) {
3602
- /** @type {SVGPath.pathArray} */
3603
- var pathArray = [];
3604
- var points = (attr.points || '').trim().split(/[\s|,]/).map(Number);
3605
-
3606
- var index = 0;
3607
- while (index < points.length) {
3608
- pathArray.push([(index ? 'L' : 'M'), (points[index]), (points[index + 1])]);
3609
- index += 2;
3610
- }
3611
-
3612
- return attr.type === 'polygon' ? pathArray.concat( [['z']]) : pathArray;
3613
- }
3614
-
3615
- /**
3616
- * Returns a new `pathArray` from circle attributes.
3617
- *
3618
- * @param {SVGPath.circleAttr} attr shape configuration
3619
- * @return {SVGPath.pathArray} a circle `pathArray`
3620
- */
3621
- function getCirclePath(attr) {
3622
- var cx = attr.cx;
3623
- var cy = attr.cy;
3624
- var r = attr.r;
3625
-
3626
- return [
3627
- ['M', (cx - r), cy],
3628
- ['a', r, r, 0, 1, 0, (2 * r), 0],
3629
- ['a', r, r, 0, 1, 0, (-2 * r), 0] ];
3630
- }
3631
-
3632
- /**
3633
- * Returns a new `pathArray` from ellipse attributes.
3634
- *
3635
- * @param {SVGPath.ellipseAttr} attr shape configuration
3636
- * @return {SVGPath.pathArray} an ellipse `pathArray`
3637
- */
3638
- function getEllipsePath(attr) {
3639
- var cx = attr.cx;
3640
- var cy = attr.cy;
3641
- var rx = attr.rx;
3642
- var ry = attr.ry;
3643
-
3644
- return [
3645
- ['M', (cx - rx), cy],
3646
- ['a', rx, ry, 0, 1, 0, (2 * rx), 0],
3647
- ['a', rx, ry, 0, 1, 0, (-2 * rx), 0] ];
3648
- }
3649
-
3650
- /**
3651
- * Returns a new `pathArray` like from rect attributes.
3652
- *
3653
- * @param {SVGPath.rectAttr} attr object with properties above
3654
- * @return {SVGPath.pathArray} a new `pathArray` from `<rect>` attributes
3655
- */
3656
- function getRectanglePath(attr) {
3657
- var x = +attr.x || 0;
3658
- var y = +attr.y || 0;
3659
- var w = +attr.width;
3660
- var h = +attr.height;
3661
- var rx = +attr.rx;
3662
- var ry = +attr.ry;
3663
-
3664
- // Validity checks from http://www.w3.org/TR/SVG/shapes.html#RectElement:
3665
- if (rx || ry) {
3666
- rx = !rx ? ry : rx;
3667
- ry = !ry ? rx : ry;
3668
-
3669
- /* istanbul ignore else */
3670
- if (rx * 2 > w) { rx -= (rx * 2 - w) / 2; }
3671
- /* istanbul ignore else */
3672
- if (ry * 2 > h) { ry -= (ry * 2 - h) / 2; }
3673
-
3674
- return [
3675
- ['M', x + rx, y],
3676
- ['h', w - rx * 2],
3677
- ['s', rx, 0, rx, ry],
3678
- ['v', h - ry * 2],
3679
- ['s', 0, ry, -rx, ry],
3680
- ['h', -w + rx * 2],
3681
- ['s', -rx, 0, -rx, -ry],
3682
- ['v', -h + ry * 2],
3683
- ['s', 0, -ry, rx, -ry] ];
3684
- }
3685
-
3686
- return [
3687
- ['M', x, y],
3688
- ['h', w],
3689
- ['v', h],
3690
- ['H', x],
3691
- ['Z'] ];
3692
- }
3693
-
3694
- /**
3695
- * Returns a new `<path>` element created from attributes of a `<line>`, `<polyline>`,
3696
- * `<polygon>`, `<rect>`, `<ellipse>`, `<circle>` or `<glyph>`. If `replace` parameter
3697
- * is `true`, it will replace the target.
3698
- *
3699
- * It can also work with an options object,
3700
- * @see SVGPath.shapeOps
3701
- *
3702
- * The newly created `<path>` element keeps all non-specific
3703
- * attributes like `class`, `fill`, etc.
3704
- *
3705
- * @param {SVGPath.shapeTypes | SVGPath.shapeOps} element target shape
3706
- * @param {boolean=} replace option to replace target
3707
- * @return {SVGPathElement | boolean} the newly created `<path>` element
3708
- */
3709
- function shapeToPath(element, replace) {
3710
- var supportedShapes = Object.keys(shapeParams);
3711
- var tagName = element.tagName;
3712
-
3713
- if (tagName && !supportedShapes.some(function (s) { return tagName === s; })) {
3714
- throw TypeError((error + ": \"" + tagName + "\" is not SVGElement"));
3715
- }
3716
-
3717
- var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
3718
- /** @type {string} */
3719
- var type = tagName || element.type;
3720
- /** @type {any} disables TS checking for something that's specific to shape */
3721
- var config = {};
3722
- config.type = type;
3723
- var shapeAttrs = shapeParams[type];
3724
-
3725
- if (tagName) {
3726
- shapeAttrs.forEach(function (p) { config[p] = element.getAttribute(p); });
3727
- // set no-specific shape attributes: fill, stroke, etc
3728
- Object.values(element.attributes).forEach(function (ref) {
3729
- var name = ref.name;
3730
- var value = ref.value;
3731
-
3732
- if (!shapeAttrs.includes(name)) { path.setAttribute(name, value); }
3733
- });
3734
- } else {
3735
- Object.assign(config, element);
3736
- // set no-specific shape attributes: fill, stroke, etc
3737
- Object.keys(config).forEach(function (k) {
3738
- if (!shapeAttrs.includes(k) && k !== 'type') {
3739
- path.setAttribute(k.replace(/[A-Z]/g, function (m) { return ("-" + (m.toLowerCase())); }), config[k]);
3740
- }
3741
- });
3742
- }
3743
-
3744
- // set d
3745
- var description;
3746
- var round = defaultOptions.round;
3747
-
3748
- /* istanbul ignore else */
3749
- if (type === 'circle') { description = pathToString(getCirclePath(config), round); }
3750
- else if (type === 'ellipse') { description = pathToString(getEllipsePath(config), round); }
3751
- else if (['polyline', 'polygon'].includes(type)) { description = pathToString(getPolyPath(config), round); }
3752
- else if (type === 'rect') { description = pathToString(getRectanglePath(config), round); }
3753
- else if (type === 'line') { description = pathToString(getLinePath(config), round); }
3754
- else if (type === 'glyph') { description = tagName ? element.getAttribute('d') : element.d; }
3755
-
3756
- // replace target element
3757
- if (isValidPath(description)) {
3758
- path.setAttribute('d', description);
3759
- if (replace && tagName) {
3760
- element.before(path, element);
3761
- element.remove();
3762
- }
3763
- return path;
3764
- }
3765
- return false;
3766
- }
3767
-
3768
- /**
3769
- * Reverses all segments of a `pathArray`
3770
- * which consists of only C (cubic-bezier) path commands.
3771
- *
3772
- * @param {SVGPath.curveArray} path the source `pathArray`
3773
- * @returns {SVGPath.curveArray} the reversed `pathArray`
3774
- */
3775
- function reverseCurve(path) {
3776
- var rotatedCurve = path.slice(1)
3777
- .map(function (x, i, curveOnly) { return (!i
3778
- ? path[0].slice(1).concat( x.slice(1))
3779
- : curveOnly[i - 1].slice(-2).concat( x.slice(1))); })
3780
- .map(function (x) { return x.map(function (_, i) { return x[x.length - i - 2 * (1 - (i % 2))]; }); })
3781
- .reverse();
3782
-
3783
- return [['M' ].concat( rotatedCurve[0].slice(0, 2)) ].concat( rotatedCurve.map(function (x) { return ['C' ].concat( x.slice(2)); }));
3784
- }
3785
-
3786
- /**
3787
- * Checks a `pathArray` for an unnecessary `Z` segment
3788
- * and returns a new `pathArray` without it.
3789
- *
3790
- * The `pathInput` must be a single path, without
3791
- * sub-paths. For multi-path `<path>` elements,
3792
- * use `splitPath` first and apply this utility on each
3793
- * sub-path separately.
3794
- *
3795
- * @param {SVGPath.pathArray | string} pathInput the `pathArray` source
3796
- * @return {SVGPath.pathArray} a fixed `pathArray`
3797
- */
3798
- function fixPath(pathInput) {
3799
- var pathArray = parsePathString(pathInput);
3800
- var normalArray = normalizePath(pathArray);
3801
- var length = pathArray.length;
3802
- var isClosed = normalArray.slice(-1)[0][0] === 'Z';
3803
- var segBeforeZ = isClosed ? length - 2 : length - 1;
3804
-
3805
- var ref = normalArray[0].slice(1);
3806
- var mx = ref[0];
3807
- var my = ref[1];
3808
- var ref$1 = normalArray[segBeforeZ].slice(-2);
3809
- var x = ref$1[0];
3810
- var y = ref$1[1];
3811
-
3812
- /* istanbul ignore else */
3813
- if (isClosed && mx === x && my === y) {
3814
- return pathArray.slice(0, -1);
3815
- }
3816
- return pathArray;
3817
- }
3818
-
3819
- /**
3820
- * @interface
3821
- */
3822
- var Util = {
3823
- CSSMatrix: CSSMatrix,
3824
- parsePathString: parsePathString,
3825
- isPathArray: isPathArray,
3826
- isCurveArray: isCurveArray,
3827
- isAbsoluteArray: isAbsoluteArray,
3828
- isRelativeArray: isRelativeArray,
3829
- isNormalizedArray: isNormalizedArray,
3830
- isValidPath: isValidPath,
3831
- pathToAbsolute: pathToAbsolute,
3832
- pathToRelative: pathToRelative,
3833
- pathToCurve: pathToCurve,
3834
- pathToString: pathToString,
3835
- getDrawDirection: getDrawDirection,
3836
- getPathArea: getPathArea,
3837
- getPathBBox: getPathBBox,
3838
- pathLengthFactory: pathLengthFactory,
3839
- getTotalLength: getTotalLength,
3840
- getPointAtLength: getPointAtLength,
3841
- getClosestPoint: getClosestPoint,
3842
- getSegmentOfPoint: getSegmentOfPoint,
3843
- getPropertiesAtPoint: getPropertiesAtPoint,
3844
- getPropertiesAtLength: getPropertiesAtLength,
3845
- getSegmentAtLength: getSegmentAtLength,
3846
- isPointInStroke: isPointInStroke,
3847
- clonePath: clonePath,
3848
- splitPath: splitPath,
3849
- fixPath: fixPath,
3850
- roundPath: roundPath,
3851
- optimizePath: optimizePath,
3852
- reverseCurve: reverseCurve,
3853
- reversePath: reversePath,
3854
- normalizePath: normalizePath,
3855
- transformPath: transformPath,
3856
- shapeToPath: shapeToPath,
3857
- options: defaultOptions,
3858
- };
3859
-
3860
- var version = "1.0.5";
3861
-
3862
- /**
3863
- * A global namespace for library version.
3864
- * @type {string}
3865
- */
3866
- var Version = version;
3867
-
3868
- /** @typedef {import('../types/index')} */
3869
-
3870
- // Export to global
3871
- Object.assign(SVGPathCommander, Util, { Version: Version });
3872
-
3873
- return SVGPathCommander;
3874
-
3875
- }));