svg-path-commander 0.1.10-alpha2 → 0.1.11-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.
- package/dist/svg-path-commander.esm.js +478 -454
- package/dist/svg-path-commander.esm.min.js +2 -2
- package/dist/svg-path-commander.js +490 -468
- package/dist/svg-path-commander.min.js +2 -2
- package/package.json +5 -5
- package/src/convert/pathToAbsolute.js +7 -5
- package/src/convert/pathToCurve.js +5 -5
- package/src/convert/pathToRelative.js +7 -6
- package/src/convert/pathToString.js +3 -3
- package/src/math/epsilon.js +1 -1
- package/src/math/rotateVector.js +3 -2
- package/src/parser/finalizeSegment.js +14 -17
- package/src/parser/isArcCommand.js +1 -1
- package/src/parser/isDigit.js +1 -1
- package/src/parser/isDigitStart.js +1 -1
- package/src/parser/isPathCommand.js +1 -1
- package/src/parser/isSpace.js +5 -6
- package/src/parser/parsePathString.js +5 -5
- package/src/parser/{svgPathArray.js → pathParser.js} +3 -3
- package/src/parser/scanFlag.js +1 -1
- package/src/parser/scanParam.js +1 -1
- package/src/parser/scanSegment.js +1 -1
- package/src/parser/skipSpaces.js +1 -1
- package/src/process/arcToCubic.js +59 -71
- package/src/process/clonePath.js +4 -5
- package/src/process/fixArc.js +3 -1
- package/src/process/getSVGMatrix.js +8 -6
- package/src/process/lineToCubic.js +7 -5
- package/src/process/normalizePath.js +4 -3
- package/src/process/normalizeSegment.js +6 -6
- package/src/process/optimizePath.js +7 -4
- package/src/process/projection2d.js +1 -1
- package/src/process/quadToCubic.js +7 -7
- package/src/process/reverseCurve.js +5 -4
- package/src/process/reversePath.js +2 -2
- package/src/process/roundPath.js +27 -12
- package/src/process/segmentToCubic.js +4 -4
- package/src/process/shorthandToCubic.js +6 -6
- package/src/process/shorthandToQuad.js +6 -6
- package/src/process/splitCubic.js +2 -2
- package/src/process/splitPath.js +1 -1
- package/src/process/transformPath.js +2 -2
- package/src/svg-path-commander.js +20 -20
- package/src/util/getCubicSize.js +5 -3
- package/src/util/getDrawDirection.js +1 -1
- package/src/util/getPathArea.js +6 -4
- package/src/util/getPathBBox.js +16 -12
- package/src/util/getPathLength.js +3 -2
- package/src/util/getPointAtLength.js +6 -4
- package/src/util/getPointAtSegLength.js +9 -9
- package/src/util/getSegArcLength.js +2 -2
- package/src/util/getSegCubicLength.js +19 -11
- package/src/util/getSegLineLength.js +5 -5
- package/src/util/getSegQuadLength.js +8 -8
- package/src/util/isAbsoluteArray.js +1 -1
- package/src/util/isCurveArray.js +1 -1
- package/src/util/isNormalizedArray.js +1 -1
- package/src/util/isPathArray.js +1 -1
- package/src/util/isRelativeArray.js +1 -1
- package/src/util/isValidPath.js +2 -2
- package/src/util/shapeToPath.js +23 -20
- package/types/convert/pathToAbsolute.d.ts +1 -0
- package/types/convert/pathToCurve.d.ts +1 -0
- package/types/convert/pathToRelative.d.ts +1 -0
- package/types/convert/pathToString.d.ts +1 -0
- package/types/index.d.ts +110 -834
- package/types/math/epsilon.d.ts +2 -0
- package/types/math/midPoint.d.ts +1 -0
- package/types/math/rotateVector.d.ts +4 -0
- package/types/options/options.d.ts +6 -0
- package/types/parser/finalizeSegment.d.ts +1 -0
- package/types/parser/invalidPathValue.d.ts +2 -0
- package/types/parser/isArcCommand.d.ts +1 -0
- package/types/parser/isDigit.d.ts +1 -0
- package/types/parser/isDigitStart.d.ts +1 -0
- package/types/parser/isPathCommand.d.ts +1 -0
- package/types/parser/isSpace.d.ts +1 -0
- package/types/parser/paramsCount.d.ts +4 -0
- package/types/parser/parsePathString.d.ts +1 -0
- package/types/parser/pathParser.d.ts +12 -0
- package/types/parser/scanFlag.d.ts +1 -0
- package/types/parser/scanParam.d.ts +1 -0
- package/types/parser/scanSegment.d.ts +1 -0
- package/types/parser/skipSpaces.d.ts +1 -0
- package/types/process/arcToCubic.d.ts +1 -0
- package/types/process/clonePath.d.ts +1 -0
- package/types/process/fixArc.d.ts +1 -0
- package/types/process/getSVGMatrix.d.ts +2 -0
- package/types/process/lineToCubic.d.ts +1 -0
- package/types/process/normalizePath.d.ts +1 -0
- package/types/process/normalizeSegment.d.ts +1 -0
- package/types/process/optimizePath.d.ts +1 -0
- package/types/process/projection2d.d.ts +1 -0
- package/types/process/quadToCubic.d.ts +1 -0
- package/types/process/reverseCurve.d.ts +1 -0
- package/types/process/reversePath.d.ts +1 -0
- package/types/process/roundPath.d.ts +1 -0
- package/types/process/segmentToCubic.d.ts +1 -0
- package/types/process/shorthandToCubic.d.ts +4 -0
- package/types/process/shorthandToQuad.d.ts +4 -0
- package/types/process/splitPath.d.ts +1 -0
- package/types/process/transformEllipse.d.ts +5 -0
- package/types/process/transformPath.d.ts +1 -0
- package/types/svg-path-commander.d.ts +18 -0
- package/types/util/getCubicSize.d.ts +1 -0
- package/types/util/getDrawDirection.d.ts +1 -0
- package/types/util/getPathArea.d.ts +1 -0
- package/types/util/getPathBBox.d.ts +1 -0
- package/types/util/getPathLength.d.ts +1 -0
- package/types/util/getPointAtLength.d.ts +1 -0
- package/types/util/getPointAtSegLength.d.ts +4 -0
- package/types/util/getSegCubicLength.d.ts +1 -0
- package/types/util/isAbsoluteArray.d.ts +1 -0
- package/types/util/isCurveArray.d.ts +1 -0
- package/types/util/isNormalizedArray.d.ts +1 -0
- package/types/util/isPathArray.d.ts +1 -0
- package/types/util/isRelativeArray.d.ts +1 -0
- package/types/util/isValidPath.d.ts +1 -0
- package/types/util/shapeToPath.d.ts +6 -0
- package/types/util/util.d.ts +61 -0
- package/types/util/version.d.ts +2 -0
- package/src/index.js +0 -3
- package/types/types.d.ts +0 -83
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* SVGPathCommander v0.1.
|
|
2
|
+
* SVGPathCommander v0.1.11alpha2 (http://thednp.github.io/svg-path-commander)
|
|
3
3
|
* Copyright 2021 © thednp
|
|
4
4
|
* Licensed under MIT (https://github.com/thednp/svg-path-commander/blob/master/LICENSE)
|
|
5
5
|
*/
|
|
@@ -23,29 +23,26 @@ const paramsCount = {
|
|
|
23
23
|
/**
|
|
24
24
|
* Breaks the parsing of a pathString once a segment is finalized.
|
|
25
25
|
*
|
|
26
|
-
* @param {
|
|
26
|
+
* @param {SVGPathCommander.PathParser} path the `PathParser` instance
|
|
27
27
|
*/
|
|
28
28
|
function finalizeSegment(path) {
|
|
29
29
|
let pathCommand = path.pathValue[path.segmentStart];
|
|
30
|
-
let
|
|
31
|
-
let
|
|
30
|
+
let LK = pathCommand.toLowerCase();
|
|
31
|
+
let { data } = path;
|
|
32
32
|
|
|
33
33
|
// Process duplicated commands (without comand name)
|
|
34
|
-
if (
|
|
35
|
-
path.segments.push([pathCommand,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
pathCommand =
|
|
34
|
+
if (LK === 'm' && data.length > 2) {
|
|
35
|
+
path.segments.push([pathCommand, data[0], data[1]]);
|
|
36
|
+
data = data.slice(2);
|
|
37
|
+
LK = 'l';
|
|
38
|
+
pathCommand = pathCommand === 'm' ? 'l' : 'L';
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!paramsCount[pathComLK]) {
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
41
|
+
while (data.length >= paramsCount[LK]) {
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
path.segments.push([pathCommand].concat(data.splice(0, paramsCount[LK])));
|
|
44
|
+
if (!paramsCount[LK]) {
|
|
45
|
+
break;
|
|
49
46
|
}
|
|
50
47
|
}
|
|
51
48
|
}
|
|
@@ -56,7 +53,7 @@ const invalidPathValue = 'Invalid path value';
|
|
|
56
53
|
* Validates an A (arc-to) specific path command value.
|
|
57
54
|
* Usually a `large-arc-flag` or `sweep-flag`.
|
|
58
55
|
*
|
|
59
|
-
* @param {
|
|
56
|
+
* @param {SVGPathCommander.PathParser} path the `PathParser` instance
|
|
60
57
|
*/
|
|
61
58
|
function scanFlag(path) {
|
|
62
59
|
const { index } = path;
|
|
@@ -80,7 +77,7 @@ function scanFlag(path) {
|
|
|
80
77
|
/**
|
|
81
78
|
* Checks if a character is a digit.
|
|
82
79
|
*
|
|
83
|
-
* @param {
|
|
80
|
+
* @param {number} code the character to check
|
|
84
81
|
* @returns {boolean} check result
|
|
85
82
|
*/
|
|
86
83
|
function isDigit(code) {
|
|
@@ -91,7 +88,7 @@ function isDigit(code) {
|
|
|
91
88
|
* Validates every character of the path string,
|
|
92
89
|
* every path command, negative numbers or floating point numbers.
|
|
93
90
|
*
|
|
94
|
-
* @param {
|
|
91
|
+
* @param {SVGPathCommander.PathParser} path the `PathParser` instance
|
|
95
92
|
*/
|
|
96
93
|
function scanParam(path) {
|
|
97
94
|
const { max, pathValue, index: start } = path;
|
|
@@ -185,18 +182,17 @@ function scanParam(path) {
|
|
|
185
182
|
/**
|
|
186
183
|
* Checks if the character is a space.
|
|
187
184
|
*
|
|
188
|
-
* @param {
|
|
185
|
+
* @param {number} ch the character to check
|
|
189
186
|
* @returns {boolean} check result
|
|
190
187
|
*/
|
|
191
|
-
function isSpace(
|
|
188
|
+
function isSpace(ch) {
|
|
192
189
|
const specialSpaces = [
|
|
193
190
|
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
|
|
194
191
|
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF];
|
|
195
|
-
// Line terminators
|
|
196
|
-
return (code === 0x0A) || (code === 0x0D) || (code === 0x2028) || (code === 0x2029)
|
|
192
|
+
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) // Line terminators
|
|
197
193
|
// White spaces
|
|
198
|
-
|| (
|
|
199
|
-
|| (
|
|
194
|
+
|| (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0)
|
|
195
|
+
|| (ch >= 0x1680 && specialSpaces.indexOf(ch) >= 0);
|
|
200
196
|
}
|
|
201
197
|
|
|
202
198
|
/**
|
|
@@ -204,7 +200,7 @@ function isSpace(code) {
|
|
|
204
200
|
* path string every time it encounters any kind of
|
|
205
201
|
* space character.
|
|
206
202
|
*
|
|
207
|
-
* @param {
|
|
203
|
+
* @param {SVGPathCommander.PathParser} path the `PathParser` instance
|
|
208
204
|
*/
|
|
209
205
|
function skipSpaces(path) {
|
|
210
206
|
const { pathValue, max } = path;
|
|
@@ -232,7 +228,7 @@ function isPathCommand(code) {
|
|
|
232
228
|
case 0x71/* q */:
|
|
233
229
|
case 0x74/* t */:
|
|
234
230
|
case 0x61/* a */:
|
|
235
|
-
case 0x72/* r */:
|
|
231
|
+
// case 0x72/* r */: // R is not supported
|
|
236
232
|
return true;
|
|
237
233
|
default:
|
|
238
234
|
return false;
|
|
@@ -243,7 +239,7 @@ function isPathCommand(code) {
|
|
|
243
239
|
* Checks if the character is or belongs to a number.
|
|
244
240
|
* [0-9]|+|-|.
|
|
245
241
|
*
|
|
246
|
-
* @param {
|
|
242
|
+
* @param {number} code the character to check
|
|
247
243
|
* @returns {boolean} check result
|
|
248
244
|
*/
|
|
249
245
|
function isDigitStart(code) {
|
|
@@ -256,7 +252,7 @@ function isDigitStart(code) {
|
|
|
256
252
|
/**
|
|
257
253
|
* Checks if the character is an A (arc-to) path command.
|
|
258
254
|
*
|
|
259
|
-
* @param {
|
|
255
|
+
* @param {number} code the character to check
|
|
260
256
|
* @returns {boolean} check result
|
|
261
257
|
*/
|
|
262
258
|
function isArcCommand(code) {
|
|
@@ -268,7 +264,7 @@ function isArcCommand(code) {
|
|
|
268
264
|
* Scans every character in the path string to determine
|
|
269
265
|
* where a segment starts and where it ends.
|
|
270
266
|
*
|
|
271
|
-
* @param {
|
|
267
|
+
* @param {SVGPathCommander.PathParser} path the `PathParser` instance
|
|
272
268
|
*/
|
|
273
269
|
function scanSegment(path) {
|
|
274
270
|
const { max, pathValue, index } = path;
|
|
@@ -325,29 +321,28 @@ function scanSegment(path) {
|
|
|
325
321
|
finalizeSegment(path);
|
|
326
322
|
}
|
|
327
323
|
|
|
328
|
-
// @ts-nocheck
|
|
329
324
|
/**
|
|
330
325
|
* Returns a clone of an existing `pathArray`.
|
|
331
326
|
*
|
|
332
|
-
* @param {
|
|
333
|
-
* @returns {
|
|
327
|
+
* @param {SVGPathCommander.pathArray | any[] | string} path the source `pathArray`
|
|
328
|
+
* @returns {any} the cloned `pathArray`
|
|
334
329
|
*/
|
|
335
330
|
function clonePath(path) {
|
|
336
|
-
return path.map((x) => {
|
|
331
|
+
return Array.isArray(path) ? path.map((x) => {
|
|
337
332
|
if (Array.isArray(x)) {
|
|
338
333
|
return clonePath(x);
|
|
339
334
|
}
|
|
340
335
|
return !Number.isNaN(+x) ? +x : x;
|
|
341
|
-
});
|
|
336
|
+
}) : path;
|
|
342
337
|
}
|
|
343
338
|
|
|
344
339
|
/**
|
|
345
|
-
* The `
|
|
340
|
+
* The `PathParser` used by the parser.
|
|
346
341
|
*
|
|
347
342
|
* @param {string} pathString
|
|
348
343
|
*/
|
|
349
|
-
function
|
|
350
|
-
/** @type {
|
|
344
|
+
function PathParser(pathString) {
|
|
345
|
+
/** @type {SVGPathCommander.pathArray} */
|
|
351
346
|
this.segments = [];
|
|
352
347
|
/** @type {string} */
|
|
353
348
|
this.pathValue = pathString;
|
|
@@ -368,7 +363,7 @@ function SVGPathArray(pathString) {
|
|
|
368
363
|
/**
|
|
369
364
|
* Iterates an array to check if it's an actual `pathArray`.
|
|
370
365
|
*
|
|
371
|
-
* @param {string |
|
|
366
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
372
367
|
* @returns {boolean} iteration result
|
|
373
368
|
*/
|
|
374
369
|
function isPathArray(path) {
|
|
@@ -382,15 +377,15 @@ function isPathArray(path) {
|
|
|
382
377
|
* Parses a path string value and returns an array
|
|
383
378
|
* of segments we like to call `pathArray`.
|
|
384
379
|
*
|
|
385
|
-
* @param {
|
|
386
|
-
* @returns {
|
|
380
|
+
* @param {SVGPathCommander.pathArray | string} pathInput the string to be parsed
|
|
381
|
+
* @returns {SVGPathCommander.pathArray} the resulted `pathArray`
|
|
387
382
|
*/
|
|
388
383
|
function parsePathString(pathInput) {
|
|
389
384
|
if (isPathArray(pathInput)) {
|
|
390
385
|
return clonePath(pathInput);
|
|
391
386
|
}
|
|
392
387
|
|
|
393
|
-
const path = new
|
|
388
|
+
const path = new PathParser(`${pathInput}`); // TS expects string
|
|
394
389
|
|
|
395
390
|
skipSpaces(path);
|
|
396
391
|
|
|
@@ -401,7 +396,7 @@ function parsePathString(pathInput) {
|
|
|
401
396
|
if (path.err.length) {
|
|
402
397
|
path.segments = [];
|
|
403
398
|
} else if (path.segments.length) {
|
|
404
|
-
if ('mM'.
|
|
399
|
+
if (!'mM'.includes(path.segments[0][0])) {
|
|
405
400
|
path.err = `${invalidPathValue}: missing M/m`;
|
|
406
401
|
path.segments = [];
|
|
407
402
|
} else {
|
|
@@ -416,7 +411,7 @@ function parsePathString(pathInput) {
|
|
|
416
411
|
* Iterates an array to check if it's a `pathArray`
|
|
417
412
|
* with all absolute values.
|
|
418
413
|
*
|
|
419
|
-
* @param {string |
|
|
414
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
420
415
|
* @returns {boolean} iteration result
|
|
421
416
|
*/
|
|
422
417
|
function isAbsoluteArray(path) {
|
|
@@ -428,17 +423,17 @@ function isAbsoluteArray(path) {
|
|
|
428
423
|
* Parses a path string value or object and returns an array
|
|
429
424
|
* of segments, all converted to absolute values.
|
|
430
425
|
*
|
|
431
|
-
* @param {
|
|
432
|
-
* @returns {
|
|
426
|
+
* @param {SVGPathCommander.pathArray | string} pathInput the path string | object
|
|
427
|
+
* @returns {SVGPathCommander.pathArray} the resulted `pathArray` with absolute values
|
|
433
428
|
*/
|
|
434
429
|
function pathToAbsolute(pathInput) {
|
|
435
|
-
if (
|
|
430
|
+
if (isAbsoluteArray(pathInput)) {
|
|
436
431
|
return clonePath(pathInput);
|
|
437
432
|
}
|
|
438
433
|
|
|
439
434
|
const path = parsePathString(pathInput);
|
|
440
435
|
const ii = path.length;
|
|
441
|
-
/** @type {
|
|
436
|
+
/** @type {SVGPathCommander.pathArray} */
|
|
442
437
|
const resultArray = [];
|
|
443
438
|
let x = 0;
|
|
444
439
|
let y = 0;
|
|
@@ -459,6 +454,8 @@ function pathToAbsolute(pathInput) {
|
|
|
459
454
|
const segment = path[i];
|
|
460
455
|
const [pathCommand] = segment;
|
|
461
456
|
const absCommand = pathCommand.toUpperCase();
|
|
457
|
+
/** @type {SVGPathCommander.pathSegment} */
|
|
458
|
+
// @ts-ignore -- trust me
|
|
462
459
|
const absoluteSegment = [];
|
|
463
460
|
let newSeg = [];
|
|
464
461
|
|
|
@@ -493,7 +490,7 @@ function pathToAbsolute(pathInput) {
|
|
|
493
490
|
absoluteSegment.push(segment[j]);
|
|
494
491
|
}
|
|
495
492
|
}
|
|
496
|
-
|
|
493
|
+
|
|
497
494
|
resultArray.push(absoluteSegment);
|
|
498
495
|
|
|
499
496
|
const segLength = absoluteSegment.length;
|
|
@@ -526,7 +523,7 @@ function pathToAbsolute(pathInput) {
|
|
|
526
523
|
* Iterates an array to check if it's a `pathArray`
|
|
527
524
|
* with relative values.
|
|
528
525
|
*
|
|
529
|
-
* @param {string |
|
|
526
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
530
527
|
* @returns {boolean} iteration result
|
|
531
528
|
*/
|
|
532
529
|
function isRelativeArray(path) {
|
|
@@ -538,17 +535,17 @@ function isRelativeArray(path) {
|
|
|
538
535
|
* Parses a path string value or object and returns an array
|
|
539
536
|
* of segments, all converted to relative values.
|
|
540
537
|
*
|
|
541
|
-
* @param {string |
|
|
542
|
-
* @returns {
|
|
538
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the path string | object
|
|
539
|
+
* @returns {SVGPathCommander.pathArray} the resulted `pathArray` with relative values
|
|
543
540
|
*/
|
|
544
541
|
function pathToRelative(pathInput) {
|
|
545
|
-
if (
|
|
542
|
+
if (isRelativeArray(pathInput)) {
|
|
546
543
|
return clonePath(pathInput);
|
|
547
544
|
}
|
|
548
545
|
|
|
549
546
|
const path = parsePathString(pathInput);
|
|
550
547
|
const ii = path.length;
|
|
551
|
-
/** @type {
|
|
548
|
+
/** @type {SVGPathCommander.pathArray} */
|
|
552
549
|
const resultArray = [];
|
|
553
550
|
let x = 0;
|
|
554
551
|
let y = 0;
|
|
@@ -569,7 +566,9 @@ function pathToRelative(pathInput) {
|
|
|
569
566
|
const segment = path[i];
|
|
570
567
|
const [pathCommand] = segment;
|
|
571
568
|
const relativeCommand = pathCommand.toLowerCase();
|
|
572
|
-
|
|
569
|
+
/** @type {SVGPathCommander.pathSegment} */
|
|
570
|
+
// @ts-ignore -- trust me DON'T CHANGE
|
|
571
|
+
const relativeSegment = [];
|
|
573
572
|
let newSeg = [];
|
|
574
573
|
|
|
575
574
|
if (pathCommand !== relativeCommand) {
|
|
@@ -605,7 +604,6 @@ function pathToRelative(pathInput) {
|
|
|
605
604
|
relativeSegment.push(segment[j]);
|
|
606
605
|
}
|
|
607
606
|
}
|
|
608
|
-
// @ts-ignore
|
|
609
607
|
resultArray.push(relativeSegment);
|
|
610
608
|
|
|
611
609
|
const segLength = relativeSegment.length;
|
|
@@ -633,51 +631,66 @@ function pathToRelative(pathInput) {
|
|
|
633
631
|
* Rounds the values of a `pathArray` instance to
|
|
634
632
|
* a specified amount of decimals and returns it.
|
|
635
633
|
*
|
|
636
|
-
* @param {
|
|
637
|
-
* @param {
|
|
638
|
-
* @returns {
|
|
634
|
+
* @param {SVGPathCommander.pathArray} path the source `pathArray`
|
|
635
|
+
* @param {number | boolean | null} round the amount of decimals to round numbers to
|
|
636
|
+
* @returns {SVGPathCommander.pathArray} the resulted `pathArray` with rounded values
|
|
639
637
|
*/
|
|
640
638
|
function roundPath(path, round) {
|
|
641
639
|
const { round: defaultRound, decimals: defaultDecimals } = SVGPCO;
|
|
642
640
|
const decimalsOption = round && !Number.isNaN(+round) ? +round
|
|
643
641
|
: defaultRound && defaultDecimals;
|
|
644
642
|
|
|
645
|
-
if (!decimalsOption) return clonePath(path);
|
|
643
|
+
if (round === false || (!defaultRound && !decimalsOption)) return clonePath(path);
|
|
646
644
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
645
|
+
const dc = 10 ** decimalsOption;
|
|
646
|
+
/** @type {SVGPathCommander.pathArray} */
|
|
647
|
+
const result = [];
|
|
648
|
+
const pl = path.length;
|
|
649
|
+
/** @type {SVGPathCommander.pathSegment} */
|
|
650
|
+
let segment;
|
|
651
|
+
/** @type {number} */
|
|
652
|
+
let n = 0;
|
|
653
|
+
let pi = [];
|
|
654
|
+
|
|
655
|
+
// FOR works best with TS
|
|
656
|
+
for (let i = 0; i < pl; i += 1) {
|
|
657
|
+
pi = path[i];
|
|
658
|
+
segment = [''];
|
|
659
|
+
for (let j = 0; j < pi.length; j += 1) {
|
|
660
|
+
if (!j) segment[j] = pi[j];
|
|
661
|
+
else {
|
|
662
|
+
n = +pi[j];
|
|
663
|
+
segment.push(!j || n % 1 === 0 ? n : Math.round(n * dc) / dc);
|
|
664
|
+
}
|
|
653
665
|
}
|
|
654
|
-
|
|
655
|
-
}
|
|
666
|
+
result.push(segment);
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
656
669
|
}
|
|
657
670
|
|
|
658
671
|
/**
|
|
659
672
|
* Returns a valid `d` attribute string value created
|
|
660
673
|
* by rounding values and concatenating the `pathArray` segments.
|
|
661
674
|
*
|
|
662
|
-
* @param {
|
|
663
|
-
* @param {
|
|
675
|
+
* @param {SVGPathCommander.pathArray} path the `pathArray` object
|
|
676
|
+
* @param {any} round amount of decimals to round values to
|
|
664
677
|
* @returns {string} the concatenated path string
|
|
665
678
|
*/
|
|
666
679
|
function pathToString(path, round) {
|
|
667
680
|
return roundPath(path, round)
|
|
668
|
-
.map((x) => x[0]
|
|
681
|
+
.map((x) => x[0] + (x.slice(1).join(' '))).join('');
|
|
669
682
|
}
|
|
670
683
|
|
|
671
684
|
/**
|
|
672
685
|
* Returns the missing control point from an
|
|
673
686
|
* T (shorthand quadratic bezier) segment.
|
|
674
687
|
*
|
|
675
|
-
* @param {
|
|
676
|
-
* @param {
|
|
677
|
-
* @param {
|
|
678
|
-
* @param {
|
|
679
|
-
* @param {
|
|
680
|
-
* @returns {
|
|
688
|
+
* @param {number} x1 curve start x
|
|
689
|
+
* @param {number} y1 curve start y
|
|
690
|
+
* @param {number} qx control point x
|
|
691
|
+
* @param {number} qy control point y
|
|
692
|
+
* @param {string} prevCommand the previous path command
|
|
693
|
+
* @returns {{qx: number, qy: number}}} the missing control point
|
|
681
694
|
*/
|
|
682
695
|
function shorthandToQuad(x1, y1, qx, qy, prevCommand) {
|
|
683
696
|
return 'QT'.indexOf(prevCommand) > -1
|
|
@@ -689,12 +702,12 @@ function shorthandToQuad(x1, y1, qx, qy, prevCommand) {
|
|
|
689
702
|
* Returns the missing control point from an
|
|
690
703
|
* S (shorthand cubic bezier) segment.
|
|
691
704
|
*
|
|
692
|
-
* @param {
|
|
693
|
-
* @param {
|
|
694
|
-
* @param {
|
|
695
|
-
* @param {
|
|
696
|
-
* @param {
|
|
697
|
-
* @returns {
|
|
705
|
+
* @param {number} x1 curve start x
|
|
706
|
+
* @param {number} y1 curve start y
|
|
707
|
+
* @param {number} x2 curve end x
|
|
708
|
+
* @param {number} y2 curve end y
|
|
709
|
+
* @param {string} prevCommand the previous path command
|
|
710
|
+
* @returns {{x1: number, y1: number}}} the missing control point
|
|
698
711
|
*/
|
|
699
712
|
function shorthandToCubic(x1, y1, x2, y2, prevCommand) {
|
|
700
713
|
return 'CS'.indexOf(prevCommand) > -1
|
|
@@ -705,17 +718,17 @@ function shorthandToCubic(x1, y1, x2, y2, prevCommand) {
|
|
|
705
718
|
/**
|
|
706
719
|
* Normalizes a single segment of a `pathArray` object.
|
|
707
720
|
*
|
|
708
|
-
* @param {
|
|
709
|
-
* @param {
|
|
710
|
-
* @param {
|
|
711
|
-
* @returns {
|
|
721
|
+
* @param {SVGPathCommander.pathSegment} segment the segment object
|
|
722
|
+
* @param {any} params the coordinates of the previous segment
|
|
723
|
+
* @param {string} prevCommand the path command of the previous segment
|
|
724
|
+
* @returns {any} the normalized segment
|
|
712
725
|
*/
|
|
713
726
|
function normalizeSegment(segment, params, prevCommand) {
|
|
714
727
|
const [pathCommand] = segment;
|
|
715
728
|
const xy = segment.slice(1);
|
|
716
|
-
let result = segment;
|
|
729
|
+
let result = segment.slice();
|
|
717
730
|
|
|
718
|
-
if ('TQ'.
|
|
731
|
+
if (!'TQ'.includes(segment[0])) {
|
|
719
732
|
// optional but good to be cautious
|
|
720
733
|
params.qx = null;
|
|
721
734
|
params.qy = null;
|
|
@@ -748,7 +761,7 @@ function normalizeSegment(segment, params, prevCommand) {
|
|
|
748
761
|
* with all segments are in non-shorthand notation
|
|
749
762
|
* with absolute values.
|
|
750
763
|
*
|
|
751
|
-
* @param {string |
|
|
764
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to be checked
|
|
752
765
|
* @returns {boolean} iteration result
|
|
753
766
|
*/
|
|
754
767
|
function isNormalizedArray(path) {
|
|
@@ -763,8 +776,8 @@ function isNormalizedArray(path) {
|
|
|
763
776
|
* * convert segments to absolute values
|
|
764
777
|
* * convert shorthand path commands to their non-shorthand notation
|
|
765
778
|
*
|
|
766
|
-
* @param {string |
|
|
767
|
-
* @returns {
|
|
779
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
780
|
+
* @returns {SVGPathCommander.pathArray} the normalized `pathArray`
|
|
768
781
|
*/
|
|
769
782
|
function normalizePath(pathInput) { // path|pathString
|
|
770
783
|
if (Array.isArray(pathInput) && isNormalizedArray(pathInput)) {
|
|
@@ -777,13 +790,14 @@ function normalizePath(pathInput) { // path|pathString
|
|
|
777
790
|
};
|
|
778
791
|
const allPathCommands = [];
|
|
779
792
|
const ii = path.length;
|
|
793
|
+
let pathCommand = '';
|
|
780
794
|
let prevCommand = '';
|
|
781
795
|
let segment;
|
|
782
796
|
let seglen;
|
|
783
797
|
|
|
784
798
|
for (let i = 0; i < ii; i += 1) {
|
|
785
799
|
// save current path command
|
|
786
|
-
|
|
800
|
+
[pathCommand] = path[i];
|
|
787
801
|
|
|
788
802
|
// Save current path command
|
|
789
803
|
allPathCommands[i] = pathCommand;
|
|
@@ -807,8 +821,8 @@ function normalizePath(pathInput) { // path|pathString
|
|
|
807
821
|
* Reverses all segments and their values of a `pathArray`
|
|
808
822
|
* and returns a new instance.
|
|
809
823
|
*
|
|
810
|
-
* @param {
|
|
811
|
-
* @returns {
|
|
824
|
+
* @param {SVGPathCommander.pathArray} pathInput the source `pathArray`
|
|
825
|
+
* @returns {SVGPathCommander.pathArray} the reversed `pathArray`
|
|
812
826
|
*/
|
|
813
827
|
function reversePath(pathInput) {
|
|
814
828
|
const absolutePath = pathToAbsolute(pathInput);
|
|
@@ -897,7 +911,7 @@ function reversePath(pathInput) {
|
|
|
897
911
|
* In the process, values are converted to absolute
|
|
898
912
|
* for visual consistency.
|
|
899
913
|
*
|
|
900
|
-
* @param {
|
|
914
|
+
* @param {SVGPathCommander.pathArray | string} pathInput the cubic-bezier parameters
|
|
901
915
|
* @return {string[]} an array with all sub-path strings
|
|
902
916
|
*/
|
|
903
917
|
function splitPath(pathInput) {
|
|
@@ -914,16 +928,19 @@ function splitPath(pathInput) {
|
|
|
914
928
|
* * create a new `pathArray` with elements with shortest segments
|
|
915
929
|
* from absolute and relative `pathArray`s
|
|
916
930
|
*
|
|
917
|
-
* @param {string |
|
|
918
|
-
* @param {number | null} round the amount of decimals to round values to
|
|
919
|
-
* @returns {
|
|
931
|
+
* @param {string | SVGPathCommander.pathArray} pathInput a string or `pathArray`
|
|
932
|
+
* @param {number | boolean | null} round the amount of decimals to round values to
|
|
933
|
+
* @returns {SVGPathCommander.pathArray} the optimized `pathArray`
|
|
920
934
|
*/
|
|
921
935
|
function optimizePath(pathInput, round) {
|
|
922
936
|
const absolutePath = roundPath(pathToAbsolute(pathInput), round);
|
|
923
937
|
const relativePath = roundPath(pathToRelative(pathInput), round);
|
|
938
|
+
|
|
924
939
|
return absolutePath.map((x, i) => {
|
|
925
940
|
if (i) {
|
|
926
|
-
return x.join('').length < relativePath[i].join('').length
|
|
941
|
+
return x.join('').length < relativePath[i].join('').length
|
|
942
|
+
? x
|
|
943
|
+
: relativePath[i];
|
|
927
944
|
}
|
|
928
945
|
return x;
|
|
929
946
|
});
|
|
@@ -932,7 +949,7 @@ function optimizePath(pathInput, round) {
|
|
|
932
949
|
/**
|
|
933
950
|
* A global namespace for epsilon.
|
|
934
951
|
*
|
|
935
|
-
* @type {
|
|
952
|
+
* @type {number}
|
|
936
953
|
*/
|
|
937
954
|
const epsilon = 1e-9;
|
|
938
955
|
|
|
@@ -940,8 +957,9 @@ const epsilon = 1e-9;
|
|
|
940
957
|
* Returns an {x,y} vector rotated by a given
|
|
941
958
|
* angle in radian.
|
|
942
959
|
*
|
|
943
|
-
* @param {
|
|
944
|
-
* @param {
|
|
960
|
+
* @param {number} x the initial vector x
|
|
961
|
+
* @param {number} y the initial vector y
|
|
962
|
+
* @param {number} rad the radian vector angle
|
|
945
963
|
* @returns {{x: number, y: number}} the rotated vector
|
|
946
964
|
*/
|
|
947
965
|
function rotateVector(x, y, rad) {
|
|
@@ -956,29 +974,27 @@ function rotateVector(x, y, rad) {
|
|
|
956
974
|
* For more information of where this math came from visit:
|
|
957
975
|
* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
|
958
976
|
*
|
|
959
|
-
* @param {number}
|
|
960
|
-
* @param {number}
|
|
961
|
-
* @param {number}
|
|
962
|
-
* @param {number}
|
|
977
|
+
* @param {number} X1 the starting x position
|
|
978
|
+
* @param {number} Y1 the starting y position
|
|
979
|
+
* @param {number} RX x-radius of the arc
|
|
980
|
+
* @param {number} RY y-radius of the arc
|
|
963
981
|
* @param {number} angle x-axis-rotation of the arc
|
|
964
982
|
* @param {number} LAF large-arc-flag of the arc
|
|
965
983
|
* @param {number} SF sweep-flag of the arc
|
|
966
|
-
* @param {number}
|
|
967
|
-
* @param {number}
|
|
984
|
+
* @param {number} X2 the ending x position
|
|
985
|
+
* @param {number} Y2 the ending y position
|
|
968
986
|
* @param {number[] | null} recursive the parameters needed to split arc into 2 segments
|
|
969
|
-
* @return {
|
|
987
|
+
* @return {any} the resulting cubic-bezier segment(s)
|
|
970
988
|
*/
|
|
971
|
-
function arcToCubic(x1, y1, rx, ry, angle, LAF, SF, x2, y2, recursive) {
|
|
989
|
+
// export default function arcToCubic(x1, y1, rx, ry, angle, LAF, SF, x2, y2, recursive) {
|
|
990
|
+
function arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, recursive) {
|
|
991
|
+
let x1 = X1; let y1 = Y1; let rx = RX; let ry = RY; let x2 = X2; let y2 = Y2;
|
|
992
|
+
// for more information of where this Math came from visit:
|
|
993
|
+
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
|
972
994
|
const d120 = (Math.PI * 120) / 180;
|
|
973
|
-
|
|
974
|
-
|
|
995
|
+
|
|
996
|
+
const rad = (Math.PI / 180) * (+angle || 0);
|
|
975
997
|
let res = [];
|
|
976
|
-
let X1 = x1;
|
|
977
|
-
let X2 = x2;
|
|
978
|
-
let Y1 = y1;
|
|
979
|
-
let Y2 = y2;
|
|
980
|
-
let RX = rx;
|
|
981
|
-
let RY = ry;
|
|
982
998
|
let xy;
|
|
983
999
|
let f1;
|
|
984
1000
|
let f2;
|
|
@@ -986,41 +1002,39 @@ function arcToCubic(x1, y1, rx, ry, angle, LAF, SF, x2, y2, recursive) {
|
|
|
986
1002
|
let cy;
|
|
987
1003
|
|
|
988
1004
|
if (!recursive) {
|
|
989
|
-
xy = rotateVector(
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
xy = rotateVector(
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
const x = (
|
|
997
|
-
const y = (
|
|
998
|
-
let h = (x
|
|
1005
|
+
xy = rotateVector(x1, y1, -rad);
|
|
1006
|
+
x1 = xy.x;
|
|
1007
|
+
y1 = xy.y;
|
|
1008
|
+
xy = rotateVector(x2, y2, -rad);
|
|
1009
|
+
x2 = xy.x;
|
|
1010
|
+
y2 = xy.y;
|
|
1011
|
+
|
|
1012
|
+
const x = (x1 - x2) / 2;
|
|
1013
|
+
const y = (y1 - y2) / 2;
|
|
1014
|
+
let h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
|
|
999
1015
|
if (h > 1) {
|
|
1000
1016
|
h = Math.sqrt(h);
|
|
1001
|
-
|
|
1002
|
-
|
|
1017
|
+
rx *= h;
|
|
1018
|
+
ry *= h;
|
|
1003
1019
|
}
|
|
1004
|
-
const rx2 =
|
|
1005
|
-
const ry2 =
|
|
1006
|
-
const k = (LAF === SF ? -1 : 1)
|
|
1007
|
-
* Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x)
|
|
1008
|
-
/ (rx2 * y * y + ry2 * x * x)));
|
|
1020
|
+
const rx2 = rx * rx;
|
|
1021
|
+
const ry2 = ry * ry;
|
|
1009
1022
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1023
|
+
const k = (LAF === SF ? -1 : 1)
|
|
1024
|
+
* Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x)
|
|
1025
|
+
/ (rx2 * y * y + ry2 * x * x)));
|
|
1012
1026
|
|
|
1027
|
+
cx = ((k * rx * y) / ry) + ((x1 + x2) / 2);
|
|
1028
|
+
cy = ((k * -ry * x) / rx) + ((y1 + y2) / 2);
|
|
1013
1029
|
// eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
|
|
1014
|
-
f1 = Math.asin((((
|
|
1030
|
+
f1 = (Math.asin((((y1 - cy) / ry))) * (10 ** 9) >> 0) / (10 ** 9);
|
|
1015
1031
|
// eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
|
|
1016
|
-
f2 = Math.asin((((
|
|
1017
|
-
|
|
1018
|
-
f1 = X1 < cx ? Math.PI - f1 : f1;
|
|
1019
|
-
f2 = X2 < cx ? Math.PI - f2 : f2;
|
|
1020
|
-
|
|
1021
|
-
if (f1 < 0) { f1 = Math.PI * 2 + f1; }
|
|
1022
|
-
if (f2 < 0) { f2 = Math.PI * 2 + f2; }
|
|
1032
|
+
f2 = (Math.asin((((y2 - cy) / ry))) * (10 ** 9) >> 0) / (10 ** 9);
|
|
1023
1033
|
|
|
1034
|
+
f1 = x1 < cx ? Math.PI - f1 : f1;
|
|
1035
|
+
f2 = x2 < cx ? Math.PI - f2 : f2;
|
|
1036
|
+
if (f1 < 0) (f1 = Math.PI * 2 + f1);
|
|
1037
|
+
if (f2 < 0) (f2 = Math.PI * 2 + f2);
|
|
1024
1038
|
if (SF && f1 > f2) {
|
|
1025
1039
|
f1 -= Math.PI * 2;
|
|
1026
1040
|
}
|
|
@@ -1028,63 +1042,55 @@ function arcToCubic(x1, y1, rx, ry, angle, LAF, SF, x2, y2, recursive) {
|
|
|
1028
1042
|
f2 -= Math.PI * 2;
|
|
1029
1043
|
}
|
|
1030
1044
|
} else {
|
|
1031
|
-
|
|
1032
|
-
f1 = r1;
|
|
1033
|
-
f2 = r2;
|
|
1034
|
-
cx = r3;
|
|
1035
|
-
cy = r4;
|
|
1045
|
+
[f1, f2, cx, cy] = recursive;
|
|
1036
1046
|
}
|
|
1037
|
-
|
|
1038
1047
|
let df = f2 - f1;
|
|
1039
|
-
|
|
1040
1048
|
if (Math.abs(df) > d120) {
|
|
1041
1049
|
const f2old = f2;
|
|
1042
|
-
const x2old =
|
|
1043
|
-
const y2old =
|
|
1044
|
-
|
|
1050
|
+
const x2old = x2;
|
|
1051
|
+
const y2old = y2;
|
|
1045
1052
|
f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1);
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
res = arcToCubic(
|
|
1053
|
+
x2 = cx + rx * Math.cos(f2);
|
|
1054
|
+
y2 = cy + ry * Math.sin(f2);
|
|
1055
|
+
res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]);
|
|
1049
1056
|
}
|
|
1050
|
-
|
|
1051
1057
|
df = f2 - f1;
|
|
1052
1058
|
const c1 = Math.cos(f1);
|
|
1053
1059
|
const s1 = Math.sin(f1);
|
|
1054
1060
|
const c2 = Math.cos(f2);
|
|
1055
1061
|
const s2 = Math.sin(f2);
|
|
1056
1062
|
const t = Math.tan(df / 4);
|
|
1057
|
-
const hx = (4 / 3) *
|
|
1058
|
-
const hy = (4 / 3) *
|
|
1059
|
-
const m1 = [
|
|
1060
|
-
const m2 = [
|
|
1061
|
-
const m3 = [
|
|
1062
|
-
const m4 = [
|
|
1063
|
+
const hx = (4 / 3) * rx * t;
|
|
1064
|
+
const hy = (4 / 3) * ry * t;
|
|
1065
|
+
const m1 = [x1, y1];
|
|
1066
|
+
const m2 = [x1 + hx * s1, y1 - hy * c1];
|
|
1067
|
+
const m3 = [x2 + hx * s2, y2 - hy * c2];
|
|
1068
|
+
const m4 = [x2, y2];
|
|
1063
1069
|
m2[0] = 2 * m1[0] - m2[0];
|
|
1064
1070
|
m2[1] = 2 * m1[1] - m2[1];
|
|
1065
|
-
|
|
1066
1071
|
if (recursive) {
|
|
1067
|
-
return [m2, m3, m4].concat(res)
|
|
1072
|
+
return [m2, m3, m4].concat(res);
|
|
1068
1073
|
}
|
|
1069
|
-
res = [m2, m3, m4].concat(res).join().split(',')
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
}
|
|
1074
|
+
res = [m2, m3, m4].concat(res).join().split(',');
|
|
1075
|
+
const newres = [];
|
|
1076
|
+
for (let i = 0, ii = res.length; i < ii; i += 1) {
|
|
1077
|
+
newres[i] = i % 2
|
|
1078
|
+
// @ts-ignore
|
|
1079
|
+
? rotateVector(res[i - 1], res[i], rad).y : rotateVector(res[i], res[i + 1], rad).x;
|
|
1080
|
+
}
|
|
1081
|
+
return newres;
|
|
1076
1082
|
}
|
|
1077
1083
|
|
|
1078
1084
|
/**
|
|
1079
1085
|
* Converts a Q (quadratic-bezier) segment to C (cubic-bezier).
|
|
1080
1086
|
*
|
|
1081
|
-
* @param {
|
|
1082
|
-
* @param {
|
|
1083
|
-
* @param {
|
|
1084
|
-
* @param {
|
|
1085
|
-
* @param {
|
|
1086
|
-
* @param {
|
|
1087
|
-
* @returns {
|
|
1087
|
+
* @param {number} x1 curve start x
|
|
1088
|
+
* @param {number} y1 curve start y
|
|
1089
|
+
* @param {number} qx control point x
|
|
1090
|
+
* @param {number} qy control point y
|
|
1091
|
+
* @param {number} x2 curve end x
|
|
1092
|
+
* @param {number} y2 curve end y
|
|
1093
|
+
* @returns {number[]} the cubic-bezier segment
|
|
1088
1094
|
*/
|
|
1089
1095
|
function quadToCubic(x1, y1, qx, qy, x2, y2) {
|
|
1090
1096
|
const r13 = 1 / 3;
|
|
@@ -1102,15 +1108,15 @@ function quadToCubic(x1, y1, qx, qy, x2, y2) {
|
|
|
1102
1108
|
* Returns the {x,y} coordinates of a point at a
|
|
1103
1109
|
* given length of a cubic-bezier segment.
|
|
1104
1110
|
*
|
|
1105
|
-
* @param {
|
|
1106
|
-
* @param {
|
|
1107
|
-
* @param {
|
|
1108
|
-
* @param {
|
|
1109
|
-
* @param {
|
|
1110
|
-
* @param {
|
|
1111
|
-
* @param {
|
|
1112
|
-
* @param {
|
|
1113
|
-
* @param {
|
|
1111
|
+
* @param {number} p1x the starting point X
|
|
1112
|
+
* @param {number} p1y the starting point Y
|
|
1113
|
+
* @param {number} c1x the first control point X
|
|
1114
|
+
* @param {number} c1y the first control point Y
|
|
1115
|
+
* @param {number} c2x the second control point X
|
|
1116
|
+
* @param {number} c2y the second control point Y
|
|
1117
|
+
* @param {number} p2x the ending point X
|
|
1118
|
+
* @param {number} p2y the ending point Y
|
|
1119
|
+
* @param {number} t a [0-1] ratio
|
|
1114
1120
|
* @returns {{x: number, y: number}} the requested {x,y} coordinates
|
|
1115
1121
|
*/
|
|
1116
1122
|
function getPointAtSegLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
|
|
@@ -1144,11 +1150,11 @@ function midPoint(a, b, t) {
|
|
|
1144
1150
|
/**
|
|
1145
1151
|
* Converts an L (line-to) segment to C (cubic-bezier).
|
|
1146
1152
|
*
|
|
1147
|
-
* @param {
|
|
1148
|
-
* @param {
|
|
1149
|
-
* @param {
|
|
1150
|
-
* @param {
|
|
1151
|
-
* @returns {
|
|
1153
|
+
* @param {number} x1 line start x
|
|
1154
|
+
* @param {number} y1 line start y
|
|
1155
|
+
* @param {number} x2 line end x
|
|
1156
|
+
* @param {number} y2 line end y
|
|
1157
|
+
* @returns {number[]} the cubic-bezier segment
|
|
1152
1158
|
*/
|
|
1153
1159
|
function lineToCubic(x1, y1, x2, y2) {
|
|
1154
1160
|
const t = 0.5;
|
|
@@ -1159,7 +1165,9 @@ function lineToCubic(x1, y1, x2, y2) {
|
|
|
1159
1165
|
const p4 = midPoint(p2, p3, t);
|
|
1160
1166
|
const p5 = midPoint(p3, p4, t);
|
|
1161
1167
|
const p6 = midPoint(p4, p5, t);
|
|
1168
|
+
// @ts-ignore -- rest operator won't fix
|
|
1162
1169
|
const cp1 = getPointAtSegLength.apply(0, p0.concat(p2, p4, p6, t));
|
|
1170
|
+
// @ts-ignore
|
|
1163
1171
|
const cp2 = getPointAtSegLength.apply(0, p6.concat(p5, p3, p1, 0));
|
|
1164
1172
|
|
|
1165
1173
|
return [cp1.x, cp1.y, cp2.x, cp2.y, x2, y2];
|
|
@@ -1168,12 +1176,12 @@ function lineToCubic(x1, y1, x2, y2) {
|
|
|
1168
1176
|
/**
|
|
1169
1177
|
* Converts any segment to C (cubic-bezier).
|
|
1170
1178
|
*
|
|
1171
|
-
* @param {
|
|
1172
|
-
* @param {
|
|
1173
|
-
* @returns {
|
|
1179
|
+
* @param {SVGPathCommander.pathSegment} segment the source segment
|
|
1180
|
+
* @param {SVGPathCommander.parserParams} params the source segment parameters
|
|
1181
|
+
* @returns {SVGPathCommander.pathSegment} the cubic-bezier segment
|
|
1174
1182
|
*/
|
|
1175
1183
|
function segmentToCubic(segment, params) {
|
|
1176
|
-
if ('TQ'.
|
|
1184
|
+
if (!'TQ'.includes(segment[0])) {
|
|
1177
1185
|
params.qx = null;
|
|
1178
1186
|
params.qy = null;
|
|
1179
1187
|
}
|
|
@@ -1206,7 +1214,7 @@ function segmentToCubic(segment, params) {
|
|
|
1206
1214
|
/**
|
|
1207
1215
|
* Splits an extended A (arc-to) segment into two cubic-bezier segments.
|
|
1208
1216
|
*
|
|
1209
|
-
* @param {
|
|
1217
|
+
* @param {SVGPathCommander.pathArray} path the `pathArray` this segment belongs to
|
|
1210
1218
|
* @param {string[]} allPathCommands all previous path commands
|
|
1211
1219
|
* @param {Number} i the index of the segment
|
|
1212
1220
|
*/
|
|
@@ -1219,31 +1227,22 @@ function fixArc(path, allPathCommands, i) {
|
|
|
1219
1227
|
while (segment.length) {
|
|
1220
1228
|
// if created multiple C:s, their original seg is saved
|
|
1221
1229
|
allPathCommands[i] = 'A';
|
|
1230
|
+
// path.splice(i++, 0, ['C'].concat(segment.splice(0, 6)));
|
|
1231
|
+
// @ts-ignore -- cannot fix
|
|
1222
1232
|
path.splice(ni += 1, 0, ['C'].concat(segment.splice(0, 6)));
|
|
1223
1233
|
}
|
|
1224
1234
|
path.splice(i, 1);
|
|
1225
1235
|
}
|
|
1226
1236
|
}
|
|
1227
1237
|
|
|
1228
|
-
var version$1 = "0.0.16alpha4";
|
|
1229
|
-
|
|
1230
|
-
// @ts-ignore
|
|
1231
|
-
|
|
1232
|
-
/**
|
|
1233
|
-
* A global namespace for library version.
|
|
1234
|
-
* @type {string}
|
|
1235
|
-
*/
|
|
1236
|
-
const DMVersion = version$1;
|
|
1237
|
-
|
|
1238
1238
|
// DOMMatrix Static methods
|
|
1239
|
-
// * `fromFloat64Array` and `fromFloat32Array
|
|
1240
|
-
// * `fromArray` a more simple implementation, should also accept
|
|
1241
|
-
// * `fromMatrix` load values from another CSSMatrix/DOMMatrix instance;
|
|
1242
|
-
// * `fromString` parses and loads values from any valid CSS transform string.
|
|
1239
|
+
// * `fromFloat64Array` and `fromFloat32Array are not implemented;
|
|
1240
|
+
// * `fromArray` is a more simple implementation, should also accept Float[32/64]Array;
|
|
1241
|
+
// * `fromMatrix` load values from another CSSMatrix/DOMMatrix instance or JSON object;
|
|
1242
|
+
// * `fromString` parses and loads values from any valid CSS transform string (TransformList).
|
|
1243
1243
|
|
|
1244
1244
|
/**
|
|
1245
|
-
* Creates a new mutable `CSSMatrix`
|
|
1246
|
-
*
|
|
1245
|
+
* Creates a new mutable `CSSMatrix` instance given an array of 16/6 floating point values.
|
|
1247
1246
|
* This static method invalidates arrays that contain non-number elements.
|
|
1248
1247
|
*
|
|
1249
1248
|
* If the array has six values, the result is a 2D matrix; if the array has 16 values,
|
|
@@ -1253,12 +1252,12 @@ const DMVersion = version$1;
|
|
|
1253
1252
|
* @return {CSSMatrix} the resulted matrix.
|
|
1254
1253
|
*/
|
|
1255
1254
|
function fromArray(array) {
|
|
1256
|
-
if (!array.every((n) => !Number.isNaN(n))) {
|
|
1257
|
-
throw TypeError(`CSSMatrix: "${array}" must only have numbers.`);
|
|
1258
|
-
}
|
|
1259
1255
|
const m = new CSSMatrix();
|
|
1260
1256
|
const a = Array.from(array);
|
|
1261
1257
|
|
|
1258
|
+
if (!a.every((n) => !Number.isNaN(n))) {
|
|
1259
|
+
throw TypeError(`CSSMatrix: "${array}" must only have numbers.`);
|
|
1260
|
+
}
|
|
1262
1261
|
if (a.length === 16) {
|
|
1263
1262
|
const [m11, m12, m13, m14,
|
|
1264
1263
|
m21, m22, m23, m24,
|
|
@@ -1296,25 +1295,25 @@ function fromArray(array) {
|
|
|
1296
1295
|
m.m34 = m34;
|
|
1297
1296
|
m.m44 = m44;
|
|
1298
1297
|
} else if (a.length === 6) {
|
|
1299
|
-
const [
|
|
1298
|
+
const [M11, M12, M21, M22, M41, M42] = a;
|
|
1300
1299
|
|
|
1301
|
-
m.m11 =
|
|
1302
|
-
m.a =
|
|
1300
|
+
m.m11 = M11;
|
|
1301
|
+
m.a = M11;
|
|
1303
1302
|
|
|
1304
|
-
m.m12 =
|
|
1305
|
-
m.b =
|
|
1303
|
+
m.m12 = M12;
|
|
1304
|
+
m.b = M12;
|
|
1306
1305
|
|
|
1307
|
-
m.m21 =
|
|
1308
|
-
m.c =
|
|
1306
|
+
m.m21 = M21;
|
|
1307
|
+
m.c = M21;
|
|
1309
1308
|
|
|
1310
|
-
m.m22 =
|
|
1311
|
-
m.d =
|
|
1309
|
+
m.m22 = M22;
|
|
1310
|
+
m.d = M22;
|
|
1312
1311
|
|
|
1313
|
-
m.m41 =
|
|
1314
|
-
m.e =
|
|
1312
|
+
m.m41 = M41;
|
|
1313
|
+
m.e = M41;
|
|
1315
1314
|
|
|
1316
|
-
m.m42 =
|
|
1317
|
-
m.f =
|
|
1315
|
+
m.m42 = M42;
|
|
1316
|
+
m.f = M42;
|
|
1318
1317
|
} else {
|
|
1319
1318
|
throw new TypeError('CSSMatrix: expecting an Array of 6/16 values.');
|
|
1320
1319
|
}
|
|
@@ -1325,17 +1324,12 @@ function fromArray(array) {
|
|
|
1325
1324
|
* Creates a new mutable `CSSMatrix` instance given an existing matrix or a
|
|
1326
1325
|
* `DOMMatrix` instance which provides the values for its properties.
|
|
1327
1326
|
*
|
|
1328
|
-
* @param {CSSMatrix | DOMMatrix |
|
|
1327
|
+
* @param {CSSMatrix | DOMMatrix | CSSMatrix.JSONMatrix} m the source matrix to feed values from.
|
|
1329
1328
|
* @return {CSSMatrix} the resulted matrix.
|
|
1330
1329
|
*/
|
|
1331
1330
|
function fromMatrix(m) {
|
|
1332
|
-
const keys =
|
|
1333
|
-
|
|
1334
|
-
'm21', 'm22', 'm23', 'm24',
|
|
1335
|
-
'm31', 'm32', 'm33', 'm34',
|
|
1336
|
-
'm41', 'm42', 'm43', 'm44'];
|
|
1337
|
-
if ([CSSMatrix, DOMMatrix].some((x) => m instanceof x)
|
|
1338
|
-
|| (typeof m === 'object' && keys.every((k) => k in m))) {
|
|
1331
|
+
const keys = Object.keys(new CSSMatrix());
|
|
1332
|
+
if (typeof m === 'object' && keys.every((k) => k in m)) {
|
|
1339
1333
|
return fromArray(
|
|
1340
1334
|
[m.m11, m.m12, m.m13, m.m14,
|
|
1341
1335
|
m.m21, m.m22, m.m23, m.m24,
|
|
@@ -1343,11 +1337,12 @@ function fromMatrix(m) {
|
|
|
1343
1337
|
m.m41, m.m42, m.m43, m.m44],
|
|
1344
1338
|
);
|
|
1345
1339
|
}
|
|
1346
|
-
throw TypeError(`CSSMatrix: "${m}" is not a DOMMatrix / CSSMatrix compatible object.`);
|
|
1340
|
+
throw TypeError(`CSSMatrix: "${m}" is not a DOMMatrix / CSSMatrix / JSON compatible object.`);
|
|
1347
1341
|
}
|
|
1348
1342
|
|
|
1349
1343
|
/**
|
|
1350
|
-
* Creates a new mutable `CSSMatrix`
|
|
1344
|
+
* Creates a new mutable `CSSMatrix` given any valid CSS transform string,
|
|
1345
|
+
* or what we call `TransformList`:
|
|
1351
1346
|
*
|
|
1352
1347
|
* * `matrix(a, b, c, d, e, f)` - valid matrix() transform function
|
|
1353
1348
|
* * `matrix3d(m11, m12, m13, ...m44)` - valid matrix3d() transform function
|
|
@@ -1364,55 +1359,62 @@ function fromString(source) {
|
|
|
1364
1359
|
}
|
|
1365
1360
|
const str = String(source).replace(/\s/g, '');
|
|
1366
1361
|
let m = new CSSMatrix();
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1362
|
+
const invalidStringError = `CSSMatrix: invalid transform string "${source}"`;
|
|
1363
|
+
|
|
1364
|
+
// const px = ['perspective'];
|
|
1365
|
+
// const length = ['translate', 'translate3d', 'translateX', 'translateY', 'translateZ'];
|
|
1366
|
+
// const deg = ['rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'skew', 'skewX', 'skewY'];
|
|
1367
|
+
// const abs = ['scale', 'scale3d', 'matrix', 'matrix3d'];
|
|
1368
|
+
// const transformFunctions = px.concat(length, deg, abs);
|
|
1369
|
+
|
|
1370
|
+
str.split(')').filter((f) => f).forEach((tf) => {
|
|
1371
|
+
const [prop, value] = tf.split('(');
|
|
1372
|
+
|
|
1373
|
+
// invalidate empty string
|
|
1374
|
+
if (!value) throw TypeError(invalidStringError);
|
|
1375
|
+
|
|
1370
1376
|
const components = value.split(',')
|
|
1371
1377
|
.map((n) => (n.includes('rad') ? parseFloat(n) * (180 / Math.PI) : parseFloat(n)));
|
|
1372
|
-
const [x, y, z, a] = components;
|
|
1373
|
-
|
|
1374
|
-
// don't add perspective if is2D
|
|
1375
|
-
if (is2D && (prop === 'matrix3d' // only modify is2D once
|
|
1376
|
-
|| (prop === 'rotate3d' && [x, y].every((n) => !Number.isNaN(+n) && n !== 0) && a)
|
|
1377
|
-
|| (['rotateX', 'rotateY'].includes(prop) && x)
|
|
1378
|
-
|| (prop === 'translate3d' && [x, y, z].every((n) => !Number.isNaN(+n)) && z)
|
|
1379
|
-
|| (prop === 'scale3d' && [x, y, z].every((n) => !Number.isNaN(+n) && n !== x))
|
|
1380
|
-
)) {
|
|
1381
|
-
is2D = false;
|
|
1382
|
-
}
|
|
1383
|
-
return { prop, components };
|
|
1384
|
-
});
|
|
1385
1378
|
|
|
1386
|
-
tramsformObject.forEach((tf) => {
|
|
1387
|
-
const { prop, components } = tf;
|
|
1388
1379
|
const [x, y, z, a] = components;
|
|
1389
1380
|
const xyz = [x, y, z];
|
|
1390
1381
|
const xyza = [x, y, z, a];
|
|
1391
1382
|
|
|
1392
|
-
|
|
1383
|
+
// single number value expected
|
|
1384
|
+
if (prop === 'perspective' && x && [y, z].every((n) => n === undefined)) {
|
|
1393
1385
|
m.m34 = -1 / x;
|
|
1394
|
-
|
|
1386
|
+
// 6/16 number values expected
|
|
1387
|
+
} else if (prop.includes('matrix') && [6, 16].includes(components.length)
|
|
1388
|
+
&& components.every((n) => !Number.isNaN(+n))) {
|
|
1395
1389
|
const values = components.map((n) => (Math.abs(n) < 1e-6 ? 0 : n));
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1390
|
+
m = m.multiply(fromArray(values));
|
|
1391
|
+
// 3 values expected
|
|
1392
|
+
} else if (prop === 'translate3d' && xyz.every((n) => !Number.isNaN(+n))) {
|
|
1393
|
+
m = m.translate(x, y, z);
|
|
1394
|
+
// single/double number value(s) expected
|
|
1395
|
+
} else if (prop === 'translate' && x && z === undefined) {
|
|
1396
|
+
m = m.translate(x, y || 0, 0);
|
|
1397
|
+
// all 4 values expected
|
|
1401
1398
|
} else if (prop === 'rotate3d' && xyza.every((n) => !Number.isNaN(+n)) && a) {
|
|
1402
1399
|
m = m.rotateAxisAngle(x, y, z, a);
|
|
1400
|
+
// single value expected
|
|
1401
|
+
} else if (prop === 'rotate' && x && [y, z].every((n) => n === undefined)) {
|
|
1402
|
+
m = m.rotate(0, 0, x);
|
|
1403
|
+
// 4 values expected
|
|
1403
1404
|
} else if (prop === 'scale3d' && xyz.every((n) => !Number.isNaN(+n)) && xyz.some((n) => n !== 1)) {
|
|
1404
1405
|
m = m.scale(x, y, z);
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
} else if (prop === 'scale' && !Number.isNaN(x) && x !== 1) {
|
|
1406
|
+
// single value expected
|
|
1407
|
+
} else if (prop === 'scale' && !Number.isNaN(x) && x !== 1 && z === undefined) {
|
|
1408
1408
|
const nosy = Number.isNaN(+y);
|
|
1409
1409
|
const sy = nosy ? x : y;
|
|
1410
1410
|
m = m.scale(x, sy, 1);
|
|
1411
|
-
|
|
1412
|
-
|
|
1411
|
+
// single/double value expected
|
|
1412
|
+
} else if (prop === 'skew' && x && z === undefined) {
|
|
1413
|
+
m = m.skewX(x);
|
|
1413
1414
|
m = y ? m.skewY(y) : m;
|
|
1414
|
-
} else if (/[XYZ]/.test(prop) && x)
|
|
1415
|
-
|
|
1415
|
+
} else if (/[XYZ]/.test(prop) && x && [y, z].every((n) => n === undefined) // a single value expected
|
|
1416
|
+
&& ['translate', 'rotate', 'scale', 'skew'].some((p) => prop.includes(p))) {
|
|
1417
|
+
if (['skewX', 'skewY'].includes(prop)) {
|
|
1416
1418
|
// @ts-ignore unfortunately
|
|
1417
1419
|
m = m[prop](x);
|
|
1418
1420
|
} else {
|
|
@@ -1426,6 +1428,8 @@ function fromString(source) {
|
|
|
1426
1428
|
// @ts-ignore unfortunately
|
|
1427
1429
|
m = m[fn](...axeValues);
|
|
1428
1430
|
}
|
|
1431
|
+
} else {
|
|
1432
|
+
throw TypeError(invalidStringError);
|
|
1429
1433
|
}
|
|
1430
1434
|
});
|
|
1431
1435
|
|
|
@@ -1575,7 +1579,8 @@ function RotateAxisAngle(x, y, z, alpha) {
|
|
|
1575
1579
|
|
|
1576
1580
|
/**
|
|
1577
1581
|
* Creates a new `CSSMatrix` for the scale matrix and returns it.
|
|
1578
|
-
* This method is equivalent to the CSS `scale3d()` function
|
|
1582
|
+
* This method is equivalent to the CSS `scale3d()` function, except it doesn't
|
|
1583
|
+
* accept {x, y, z} transform origin parameters.
|
|
1579
1584
|
*
|
|
1580
1585
|
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale3d
|
|
1581
1586
|
*
|
|
@@ -1670,18 +1675,18 @@ function Multiply(m1, m2) {
|
|
|
1670
1675
|
}
|
|
1671
1676
|
|
|
1672
1677
|
/**
|
|
1673
|
-
* Creates and returns a new `DOMMatrix` compatible
|
|
1674
|
-
* with equivalent instance
|
|
1678
|
+
* Creates and returns a new `DOMMatrix` compatible instance
|
|
1679
|
+
* with equivalent instance.
|
|
1675
1680
|
*
|
|
1676
1681
|
* https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix
|
|
1677
1682
|
* https://github.com/thednp/DOMMatrix/
|
|
1683
|
+
* @class
|
|
1678
1684
|
*/
|
|
1679
1685
|
|
|
1680
1686
|
class CSSMatrix {
|
|
1681
1687
|
/**
|
|
1682
1688
|
* @constructor
|
|
1683
1689
|
* @param {any} args accepts all parameter configurations:
|
|
1684
|
-
*
|
|
1685
1690
|
* * valid CSS transform string,
|
|
1686
1691
|
* * CSSMatrix/DOMMatrix instance,
|
|
1687
1692
|
* * a 6/16 elements *Array*.
|
|
@@ -1699,15 +1704,8 @@ class CSSMatrix {
|
|
|
1699
1704
|
m.m41 = 0; m.m42 = 0; m.m43 = 0; m.m44 = 1;
|
|
1700
1705
|
|
|
1701
1706
|
if (args && args.length) {
|
|
1702
|
-
|
|
1707
|
+
const ARGS = [16, 6].some((l) => l === args.length) ? args : args[0];
|
|
1703
1708
|
|
|
1704
|
-
if (args instanceof Array) {
|
|
1705
|
-
if ((args[0] instanceof Array && [16, 6].includes(args[0].length))
|
|
1706
|
-
|| typeof args[0] === 'string'
|
|
1707
|
-
|| [CSSMatrix, DOMMatrix].some((x) => args[0] instanceof x)) {
|
|
1708
|
-
[ARGS] = args;
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
1709
|
return m.setMatrixValue(ARGS);
|
|
1712
1710
|
}
|
|
1713
1711
|
return m;
|
|
@@ -1716,7 +1714,7 @@ class CSSMatrix {
|
|
|
1716
1714
|
/**
|
|
1717
1715
|
* Sets a new `Boolean` flag value for `this.isIdentity` matrix property.
|
|
1718
1716
|
*
|
|
1719
|
-
* @param {
|
|
1717
|
+
* @param {boolean} value sets a new flag for this property
|
|
1720
1718
|
*/
|
|
1721
1719
|
set isIdentity(value) {
|
|
1722
1720
|
this.isIdentity = value;
|
|
@@ -1727,7 +1725,7 @@ class CSSMatrix {
|
|
|
1727
1725
|
* matrix is one in which every value is 0 except those on the main diagonal from top-left
|
|
1728
1726
|
* to bottom-right corner (in other words, where the offsets in each direction are equal).
|
|
1729
1727
|
*
|
|
1730
|
-
* @return {
|
|
1728
|
+
* @return {boolean} the current property value
|
|
1731
1729
|
*/
|
|
1732
1730
|
get isIdentity() {
|
|
1733
1731
|
const m = this;
|
|
@@ -1741,7 +1739,7 @@ class CSSMatrix {
|
|
|
1741
1739
|
* A `Boolean` flag whose value is `true` if the matrix was initialized as a 2D matrix
|
|
1742
1740
|
* and `false` if the matrix is 3D.
|
|
1743
1741
|
*
|
|
1744
|
-
* @return {
|
|
1742
|
+
* @return {boolean} the current property value
|
|
1745
1743
|
*/
|
|
1746
1744
|
get is2D() {
|
|
1747
1745
|
const m = this;
|
|
@@ -1751,7 +1749,7 @@ class CSSMatrix {
|
|
|
1751
1749
|
/**
|
|
1752
1750
|
* Sets a new `Boolean` flag value for `this.is2D` matrix property.
|
|
1753
1751
|
*
|
|
1754
|
-
* @param {
|
|
1752
|
+
* @param {boolean} value sets a new flag for this property
|
|
1755
1753
|
*/
|
|
1756
1754
|
set is2D(value) {
|
|
1757
1755
|
this.is2D = value;
|
|
@@ -1774,47 +1772,31 @@ class CSSMatrix {
|
|
|
1774
1772
|
setMatrixValue(source) {
|
|
1775
1773
|
const m = this;
|
|
1776
1774
|
|
|
1777
|
-
// new CSSMatrix(CSSMatrix | DOMMatrix)
|
|
1778
|
-
if ([DOMMatrix, CSSMatrix].some((x) => source instanceof x)) {
|
|
1779
|
-
// @ts-ignore
|
|
1780
|
-
return fromMatrix(source);
|
|
1781
|
-
// CSS transform string source
|
|
1782
|
-
} if (typeof source === 'string' && source.length && source !== 'none') {
|
|
1783
|
-
return fromString(source);
|
|
1784
1775
|
// [Arguments list | Array] come here
|
|
1785
|
-
|
|
1776
|
+
if ([Array, Float64Array, Float32Array].some((a) => source instanceof a)) {
|
|
1786
1777
|
return fromArray(source);
|
|
1787
1778
|
}
|
|
1779
|
+
// CSS transform string source - TransformList
|
|
1780
|
+
if (typeof source === 'string' && source.length && source !== 'none') {
|
|
1781
|
+
return fromString(source);
|
|
1782
|
+
}
|
|
1783
|
+
// new CSSMatrix(CSSMatrix | DOMMatrix | JSON)
|
|
1784
|
+
if (typeof source === 'object') {
|
|
1785
|
+
return fromMatrix(source);
|
|
1786
|
+
}
|
|
1788
1787
|
return m;
|
|
1789
1788
|
}
|
|
1790
1789
|
|
|
1791
1790
|
/**
|
|
1792
|
-
*
|
|
1793
|
-
*
|
|
1794
|
-
*
|
|
1795
|
-
* matrix3d *matrix3d(m11, m12, m13, m14, m21, ...)*
|
|
1796
|
-
* matrix *matrix(a, b, c, d, e, f)*
|
|
1797
|
-
*
|
|
1798
|
-
* @return {string} a string representation of the matrix
|
|
1799
|
-
*/
|
|
1800
|
-
toString() {
|
|
1801
|
-
const m = this;
|
|
1802
|
-
const values = m.toArray().join(',');
|
|
1803
|
-
const type = m.is2D ? 'matrix' : 'matrix3d';
|
|
1804
|
-
return `${type}(${values})`;
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
/**
|
|
1808
|
-
* Returns an *Array* containing all 16 elements which comprise the matrix.
|
|
1809
|
-
* The method can return either the elements.
|
|
1810
|
-
*
|
|
1811
|
-
* Other methods make use of this method to feed their output values from this matrix.
|
|
1791
|
+
* Returns an *Array* containing elements which comprise the matrix.
|
|
1792
|
+
* The method can return either the 16 elements or the 6 elements
|
|
1793
|
+
* depending on the value of the `is2D` property.
|
|
1812
1794
|
*
|
|
1813
1795
|
* @return {number[]} an *Array* representation of the matrix
|
|
1814
1796
|
*/
|
|
1815
1797
|
toArray() {
|
|
1816
1798
|
const m = this;
|
|
1817
|
-
const
|
|
1799
|
+
const pow = (10 ** 6);
|
|
1818
1800
|
let result;
|
|
1819
1801
|
|
|
1820
1802
|
if (m.is2D) {
|
|
@@ -1827,21 +1809,39 @@ class CSSMatrix {
|
|
|
1827
1809
|
}
|
|
1828
1810
|
// clean up the numbers
|
|
1829
1811
|
// eslint-disable-next-line -- no-bitwise
|
|
1830
|
-
return result.map((n) => (Math.abs(n) < 1e-6 ? 0 : ((n *
|
|
1812
|
+
return result.map((n) => (Math.abs(n) < 1e-6 ? 0 : ((n * pow) >> 0) / pow));
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
/**
|
|
1816
|
+
* Creates and returns a string representation of the matrix in `CSS` matrix syntax,
|
|
1817
|
+
* using the appropriate `CSS` matrix notation.
|
|
1818
|
+
*
|
|
1819
|
+
* matrix3d *matrix3d(m11, m12, m13, m14, m21, ...)*
|
|
1820
|
+
* matrix *matrix(a, b, c, d, e, f)*
|
|
1821
|
+
*
|
|
1822
|
+
* @return {string} a string representation of the matrix
|
|
1823
|
+
*/
|
|
1824
|
+
toString() {
|
|
1825
|
+
const m = this;
|
|
1826
|
+
const values = m.toArray();
|
|
1827
|
+
const type = m.is2D ? 'matrix' : 'matrix3d';
|
|
1828
|
+
return `${type}(${values})`;
|
|
1831
1829
|
}
|
|
1832
1830
|
|
|
1833
1831
|
/**
|
|
1834
1832
|
* Returns a JSON representation of the `CSSMatrix` instance, a standard *Object*
|
|
1835
|
-
* that includes `{a,b,c,d,e,f}` and `{m11,m12,m13,..m44}` properties
|
|
1836
|
-
*
|
|
1833
|
+
* that includes `{a,b,c,d,e,f}` and `{m11,m12,m13,..m44}` properties as well
|
|
1834
|
+
* as the `is2D` & `isIdentity` properties.
|
|
1837
1835
|
*
|
|
1838
1836
|
* The result can also be used as a second parameter for the `fromMatrix` static method
|
|
1839
|
-
* to load values into
|
|
1837
|
+
* to load values into another matrix instance.
|
|
1840
1838
|
*
|
|
1841
|
-
* @return {
|
|
1839
|
+
* @return {CSSMatrix.JSONMatrix} an *Object* with all matrix values.
|
|
1842
1840
|
*/
|
|
1843
1841
|
toJSON() {
|
|
1844
|
-
|
|
1842
|
+
const m = this;
|
|
1843
|
+
const { is2D, isIdentity } = m;
|
|
1844
|
+
return { ...m, is2D, isIdentity };
|
|
1845
1845
|
}
|
|
1846
1846
|
|
|
1847
1847
|
/**
|
|
@@ -1849,11 +1849,10 @@ class CSSMatrix {
|
|
|
1849
1849
|
* matrix multiplied by the passed matrix, with the passed matrix to the right.
|
|
1850
1850
|
* This matrix is not modified.
|
|
1851
1851
|
*
|
|
1852
|
-
* @param {CSSMatrix | DOMMatrix |
|
|
1852
|
+
* @param {CSSMatrix | DOMMatrix | CSSMatrix.JSONMatrix} m2 CSSMatrix
|
|
1853
1853
|
* @return {CSSMatrix} The resulted matrix.
|
|
1854
1854
|
*/
|
|
1855
1855
|
multiply(m2) {
|
|
1856
|
-
// @ts-ignore - we only access [m11, m12, ... m44] values
|
|
1857
1856
|
return Multiply(this, m2);
|
|
1858
1857
|
}
|
|
1859
1858
|
|
|
@@ -1864,16 +1863,16 @@ class CSSMatrix {
|
|
|
1864
1863
|
* modified.
|
|
1865
1864
|
*
|
|
1866
1865
|
* @param {number} x X component of the translation value.
|
|
1867
|
-
* @param {number
|
|
1868
|
-
* @param {number
|
|
1866
|
+
* @param {number=} y Y component of the translation value.
|
|
1867
|
+
* @param {number=} z Z component of the translation value.
|
|
1869
1868
|
* @return {CSSMatrix} The resulted matrix
|
|
1870
1869
|
*/
|
|
1871
1870
|
translate(x, y, z) {
|
|
1872
1871
|
const X = x;
|
|
1873
1872
|
let Y = y;
|
|
1874
1873
|
let Z = z;
|
|
1875
|
-
if (Z
|
|
1876
|
-
if (Y
|
|
1874
|
+
if (Z === undefined) Z = 0;
|
|
1875
|
+
if (Y === undefined) Y = 0;
|
|
1877
1876
|
return Multiply(this, Translate(X, Y, Z));
|
|
1878
1877
|
}
|
|
1879
1878
|
|
|
@@ -1884,16 +1883,16 @@ class CSSMatrix {
|
|
|
1884
1883
|
* component value is used in its place. This matrix is not modified.
|
|
1885
1884
|
*
|
|
1886
1885
|
* @param {number} x The X component of the scale value.
|
|
1887
|
-
* @param {number
|
|
1888
|
-
* @param {number
|
|
1886
|
+
* @param {number=} y The Y component of the scale value.
|
|
1887
|
+
* @param {number=} z The Z component of the scale value.
|
|
1889
1888
|
* @return {CSSMatrix} The resulted matrix
|
|
1890
1889
|
*/
|
|
1891
1890
|
scale(x, y, z) {
|
|
1892
1891
|
const X = x;
|
|
1893
1892
|
let Y = y;
|
|
1894
1893
|
let Z = z;
|
|
1895
|
-
if (Y
|
|
1896
|
-
if (Z
|
|
1894
|
+
if (Y === undefined) Y = x;
|
|
1895
|
+
if (Z === undefined) Z = 1; // Z must be 1 if undefined
|
|
1897
1896
|
|
|
1898
1897
|
return Multiply(this, Scale(X, Y, Z));
|
|
1899
1898
|
}
|
|
@@ -1906,16 +1905,16 @@ class CSSMatrix {
|
|
|
1906
1905
|
* rotation values are in degrees. This matrix is not modified.
|
|
1907
1906
|
*
|
|
1908
1907
|
* @param {number} rx The X component of the rotation, or Z if Y and Z are null.
|
|
1909
|
-
* @param {number
|
|
1910
|
-
* @param {number
|
|
1908
|
+
* @param {number=} ry The (optional) Y component of the rotation value.
|
|
1909
|
+
* @param {number=} rz The (optional) Z component of the rotation value.
|
|
1911
1910
|
* @return {CSSMatrix} The resulted matrix
|
|
1912
1911
|
*/
|
|
1913
1912
|
rotate(rx, ry, rz) {
|
|
1914
1913
|
let RX = rx;
|
|
1915
1914
|
let RY = ry;
|
|
1916
1915
|
let RZ = rz;
|
|
1917
|
-
if (RY
|
|
1918
|
-
if (RZ
|
|
1916
|
+
if (RY === undefined) RY = 0;
|
|
1917
|
+
if (RZ === undefined) { RZ = RX; RX = 0; }
|
|
1919
1918
|
return Multiply(this, Rotate(RX, RY, RZ));
|
|
1920
1919
|
}
|
|
1921
1920
|
|
|
@@ -1970,8 +1969,8 @@ class CSSMatrix {
|
|
|
1970
1969
|
*
|
|
1971
1970
|
* @copyright thednp © 2021
|
|
1972
1971
|
*
|
|
1973
|
-
* @param {
|
|
1974
|
-
* @return {
|
|
1972
|
+
* @param {CSSMatrix.PointTuple | DOMPoint} v Tuple or DOMPoint
|
|
1973
|
+
* @return {CSSMatrix.PointTuple} the resulting Tuple
|
|
1975
1974
|
*/
|
|
1976
1975
|
transformPoint(v) {
|
|
1977
1976
|
const M = this;
|
|
@@ -1993,8 +1992,8 @@ class CSSMatrix {
|
|
|
1993
1992
|
* {x,y,z,w} Tuple *Object* comprising the transformed vector.
|
|
1994
1993
|
* Neither the matrix nor the original vector are altered.
|
|
1995
1994
|
*
|
|
1996
|
-
* @param {
|
|
1997
|
-
* @return {
|
|
1995
|
+
* @param {CSSMatrix.PointTuple} t Tuple with `{x,y,z,w}` components
|
|
1996
|
+
* @return {CSSMatrix.PointTuple} the resulting Tuple
|
|
1998
1997
|
*/
|
|
1999
1998
|
transform(t) {
|
|
2000
1999
|
const m = this;
|
|
@@ -2013,29 +2012,31 @@ class CSSMatrix {
|
|
|
2013
2012
|
}
|
|
2014
2013
|
|
|
2015
2014
|
// Add Transform Functions to CSSMatrix object
|
|
2016
|
-
|
|
2017
|
-
CSSMatrix
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2015
|
+
// without creating a TypeScript namespace.
|
|
2016
|
+
Object.assign(CSSMatrix, {
|
|
2017
|
+
Translate,
|
|
2018
|
+
Rotate,
|
|
2019
|
+
RotateAxisAngle,
|
|
2020
|
+
Scale,
|
|
2021
|
+
SkewX,
|
|
2022
|
+
SkewY,
|
|
2023
|
+
Multiply,
|
|
2024
|
+
fromArray,
|
|
2025
|
+
fromMatrix,
|
|
2026
|
+
fromString,
|
|
2027
|
+
});
|
|
2027
2028
|
|
|
2028
2029
|
/**
|
|
2029
2030
|
* Returns a transformation matrix to apply to `<path>` elements.
|
|
2030
2031
|
*
|
|
2031
|
-
* @param {
|
|
2032
|
+
* @param {SVGPathCommander.transformObject} transform the `transformObject`
|
|
2032
2033
|
* @returns {CSSMatrix} a new transformation matrix
|
|
2033
2034
|
*/
|
|
2034
2035
|
function getSVGMatrix(transform) {
|
|
2035
2036
|
let matrix = new CSSMatrix();
|
|
2036
2037
|
const { origin } = transform;
|
|
2037
|
-
const originX =
|
|
2038
|
-
const originY =
|
|
2038
|
+
const originX = origin[0];
|
|
2039
|
+
const originY = origin[1];
|
|
2039
2040
|
const { translate } = transform;
|
|
2040
2041
|
const { rotate } = transform;
|
|
2041
2042
|
const { skew } = transform;
|
|
@@ -2045,19 +2046,20 @@ function getSVGMatrix(transform) {
|
|
|
2045
2046
|
if ((Array.isArray(translate) && translate.some((x) => +x !== 0)) || !Number.isNaN(translate)) {
|
|
2046
2047
|
matrix = Array.isArray(translate)
|
|
2047
2048
|
? matrix.translate(+translate[0] || 0, +translate[1] || 0, +translate[2] || 0)
|
|
2048
|
-
: matrix.translate(+translate || 0);
|
|
2049
|
+
: matrix.translate(+translate || 0, 0, 0);
|
|
2049
2050
|
}
|
|
2050
2051
|
|
|
2051
2052
|
if (rotate || skew || scale) {
|
|
2052
2053
|
// set SVG transform-origin, always defined
|
|
2053
2054
|
// matrix = matrix.translate(+originX,+originY,+originZ)
|
|
2055
|
+
// @ts-ignore -- SVG transform origin is always 2D
|
|
2054
2056
|
matrix = matrix.translate(+originX, +originY);
|
|
2055
2057
|
|
|
2056
2058
|
// set rotation
|
|
2057
2059
|
if (rotate) {
|
|
2058
2060
|
matrix = Array.isArray(rotate) && rotate.some((x) => +x !== 0)
|
|
2059
2061
|
? matrix.rotate(+rotate[0] || 0, +rotate[1] || 0, +rotate[2] || 0)
|
|
2060
|
-
: matrix.rotate(+rotate || 0);
|
|
2062
|
+
: matrix.rotate(0, 0, +rotate || 0);
|
|
2061
2063
|
}
|
|
2062
2064
|
// set skew(s)
|
|
2063
2065
|
if (Array.isArray(skew) && skew.some((x) => +x !== 0)) {
|
|
@@ -2072,10 +2074,11 @@ function getSVGMatrix(transform) {
|
|
|
2072
2074
|
if (!Number.isNaN(scale) || (Array.isArray(scale) && scale.some((x) => +x !== 1))) {
|
|
2073
2075
|
matrix = Array.isArray(scale)
|
|
2074
2076
|
? (matrix.scale(+scale[0] || 1, +scale[1] || 1, +scale[2] || 1))
|
|
2075
|
-
: matrix.scale(+scale || 1);
|
|
2077
|
+
: matrix.scale(+scale || 1, +scale || 1, +scale || 1);
|
|
2076
2078
|
}
|
|
2077
2079
|
// set SVG transform-origin
|
|
2078
2080
|
// matrix = matrix.translate(-originX,-originY,-originZ)
|
|
2081
|
+
// @ts-ignore -- SVG transform origin is always 2D
|
|
2079
2082
|
matrix = matrix.translate(-originX, -originY);
|
|
2080
2083
|
}
|
|
2081
2084
|
return matrix;
|
|
@@ -2162,7 +2165,7 @@ function transformEllipse(m, rx, ry, ax) {
|
|
|
2162
2165
|
* Details =>
|
|
2163
2166
|
* https://stackoverflow.com/questions/23792505/predicted-rendering-of-css-3d-transformed-pixel
|
|
2164
2167
|
*
|
|
2165
|
-
* @param {
|
|
2168
|
+
* @param {SVGPathCommander.CSSMatrix} m the transformation matrix
|
|
2166
2169
|
* @param {Number[]} point2D the initial [x,y] coordinates
|
|
2167
2170
|
* @param {number[]} origin the initial [x,y] coordinates
|
|
2168
2171
|
* @returns {Number[]} the projected [x,y] coordinates
|
|
@@ -2190,9 +2193,9 @@ function projection2d(m, point2D, origin) {
|
|
|
2190
2193
|
* Since *SVGElement* doesn't support 3D transformation, this function
|
|
2191
2194
|
* creates a 2D projection of the <path> element.
|
|
2192
2195
|
*
|
|
2193
|
-
* @param {
|
|
2196
|
+
* @param {SVGPathCommander.pathArray} path the `pathArray` to apply transformation
|
|
2194
2197
|
* @param {any} transform the transform functions `Object`
|
|
2195
|
-
* @returns {
|
|
2198
|
+
* @returns {SVGPathCommander.pathArray} the resulted `pathArray`
|
|
2196
2199
|
*/
|
|
2197
2200
|
function transformPath(path, transform) {
|
|
2198
2201
|
let x = 0; let y = 0; let i; let j; let ii; let jj; let lx; let ly; let te;
|
|
@@ -2324,7 +2327,7 @@ function transformPath(path, transform) {
|
|
|
2324
2327
|
* @param {number} c2y the second control point Y
|
|
2325
2328
|
* @param {number} p2x the ending point X
|
|
2326
2329
|
* @param {number} p2y the ending point Y
|
|
2327
|
-
* @returns {
|
|
2330
|
+
* @returns {SVGPathCommander.segmentLimits} the length of the cubic-bezier segment
|
|
2328
2331
|
*/
|
|
2329
2332
|
function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
2330
2333
|
let a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x);
|
|
@@ -2335,8 +2338,9 @@ function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
2335
2338
|
const y = [p1y, p2y];
|
|
2336
2339
|
const x = [p1x, p2x];
|
|
2337
2340
|
let dot;
|
|
2338
|
-
|
|
2341
|
+
// @ts-ignore
|
|
2339
2342
|
if (Math.abs(t1) > '1e12') t1 = 0.5;
|
|
2343
|
+
// @ts-ignore
|
|
2340
2344
|
if (Math.abs(t2) > '1e12') t2 = 0.5;
|
|
2341
2345
|
|
|
2342
2346
|
if (t1 > 0 && t1 < 1) {
|
|
@@ -2354,8 +2358,9 @@ function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
2354
2358
|
c = p1y - c1y;
|
|
2355
2359
|
t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
2356
2360
|
t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
2357
|
-
|
|
2361
|
+
// @ts-ignore
|
|
2358
2362
|
if (Math.abs(t1) > '1e12') t1 = 0.5;
|
|
2363
|
+
// @ts-ignore
|
|
2359
2364
|
if (Math.abs(t2) > '1e12') t2 = 0.5;
|
|
2360
2365
|
|
|
2361
2366
|
if (t1 > 0 && t1 < 1) {
|
|
@@ -2378,7 +2383,7 @@ function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
2378
2383
|
* Iterates an array to check if it's a `pathArray`
|
|
2379
2384
|
* with all C (cubic bezier) segments.
|
|
2380
2385
|
*
|
|
2381
|
-
* @param {string |
|
|
2386
|
+
* @param {string | SVGPathCommander.pathArray} path the `Array` to be checked
|
|
2382
2387
|
* @returns {boolean} iteration result
|
|
2383
2388
|
*/
|
|
2384
2389
|
function isCurveArray(path) {
|
|
@@ -2390,11 +2395,11 @@ function isCurveArray(path) {
|
|
|
2390
2395
|
* Parses a path string value or 'pathArray' and returns a new one
|
|
2391
2396
|
* in which all segments are converted to cubic-bezier.
|
|
2392
2397
|
*
|
|
2393
|
-
* @param {string |
|
|
2394
|
-
* @returns {
|
|
2398
|
+
* @param {string | SVGPathCommander.pathArray} pathInput the string to be parsed or object
|
|
2399
|
+
* @returns {SVGPathCommander.pathArray} the resulted `pathArray` converted to cubic-bezier
|
|
2395
2400
|
*/
|
|
2396
2401
|
function pathToCurve(pathInput) {
|
|
2397
|
-
if (
|
|
2402
|
+
if (isCurveArray(pathInput)) {
|
|
2398
2403
|
return clonePath(pathInput);
|
|
2399
2404
|
}
|
|
2400
2405
|
|
|
@@ -2402,15 +2407,15 @@ function pathToCurve(pathInput) {
|
|
|
2402
2407
|
const params = {
|
|
2403
2408
|
x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null,
|
|
2404
2409
|
};
|
|
2410
|
+
/** @type {string[]} */
|
|
2405
2411
|
const allPathCommands = [];
|
|
2406
|
-
let pathCommand = '';
|
|
2412
|
+
let pathCommand = ''; // ts-lint
|
|
2407
2413
|
let ii = path.length;
|
|
2408
2414
|
|
|
2409
2415
|
for (let i = 0; i < ii; i += 1) {
|
|
2410
2416
|
const segment = path[i];
|
|
2411
2417
|
const seglen = segment.length;
|
|
2412
2418
|
if (segment) [pathCommand] = segment;
|
|
2413
|
-
|
|
2414
2419
|
allPathCommands[i] = pathCommand;
|
|
2415
2420
|
path[i] = segmentToCubic(segment, params);
|
|
2416
2421
|
|
|
@@ -2429,41 +2434,45 @@ function pathToCurve(pathInput) {
|
|
|
2429
2434
|
/**
|
|
2430
2435
|
* Returns the bounding box of a shape.
|
|
2431
2436
|
*
|
|
2432
|
-
* @param {
|
|
2433
|
-
* @returns {
|
|
2437
|
+
* @param {SVGPathCommander.pathArray} path the shape `pathArray`
|
|
2438
|
+
* @returns {SVGPathCommander.pathBBox} the length of the cubic-bezier segment
|
|
2434
2439
|
*/
|
|
2435
2440
|
function getPathBBox(path) {
|
|
2436
2441
|
if (!path) {
|
|
2437
2442
|
return {
|
|
2438
|
-
x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0,
|
|
2443
|
+
x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0, cx: 0, cy: 0,
|
|
2439
2444
|
};
|
|
2440
2445
|
}
|
|
2441
2446
|
const pathCurve = pathToCurve(path);
|
|
2442
|
-
|
|
2443
|
-
let x = 0;
|
|
2444
|
-
let y = 0;
|
|
2445
|
-
let X = [];
|
|
2446
|
-
let Y = [];
|
|
2447
|
+
// @ts-ignore
|
|
2448
|
+
let x = 0; let y = 0; let X = []; let Y = [];
|
|
2447
2449
|
|
|
2448
2450
|
pathCurve.forEach((segment) => {
|
|
2449
2451
|
const [s1, s2] = segment.slice(-2);
|
|
2450
2452
|
if (segment[0] === 'M') {
|
|
2451
|
-
x = s1;
|
|
2452
|
-
y = s2;
|
|
2453
|
+
x = +s1;
|
|
2454
|
+
y = +s2;
|
|
2453
2455
|
X.push(s1);
|
|
2454
2456
|
Y.push(s2);
|
|
2455
2457
|
} else {
|
|
2458
|
+
// @ts-ignore
|
|
2456
2459
|
const dim = getCubicSize.apply(0, [x, y].concat(segment.slice(1)));
|
|
2460
|
+
// @ts-ignore
|
|
2457
2461
|
X = X.concat(dim.min.x, dim.max.x);
|
|
2462
|
+
// @ts-ignore
|
|
2458
2463
|
Y = Y.concat(dim.min.y, dim.max.y);
|
|
2459
|
-
x = s1;
|
|
2460
|
-
y = s2;
|
|
2464
|
+
x = +s1;
|
|
2465
|
+
y = +s2;
|
|
2461
2466
|
}
|
|
2462
2467
|
});
|
|
2463
2468
|
|
|
2469
|
+
// @ts-ignore
|
|
2464
2470
|
const xTop = Math.min.apply(0, X);
|
|
2471
|
+
// @ts-ignore
|
|
2465
2472
|
const yTop = Math.min.apply(0, Y);
|
|
2473
|
+
// @ts-ignore
|
|
2466
2474
|
const xBot = Math.max.apply(0, X);
|
|
2475
|
+
// @ts-ignore
|
|
2467
2476
|
const yBot = Math.max.apply(0, Y);
|
|
2468
2477
|
const width = xBot - xTop;
|
|
2469
2478
|
const height = yBot - yTop;
|
|
@@ -2507,8 +2516,8 @@ function getCubicSegArea(x0, y0, x1, y1, x2, y2, x3, y3) {
|
|
|
2507
2516
|
*
|
|
2508
2517
|
* => https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js
|
|
2509
2518
|
*
|
|
2510
|
-
* @param {
|
|
2511
|
-
* @returns {
|
|
2519
|
+
* @param {SVGPathCommander.pathArray} path the shape `pathArray`
|
|
2520
|
+
* @returns {number} the length of the cubic-bezier segment
|
|
2512
2521
|
*/
|
|
2513
2522
|
function getPathArea(path) {
|
|
2514
2523
|
let x = 0; let y = 0;
|
|
@@ -2518,19 +2527,29 @@ function getPathArea(path) {
|
|
|
2518
2527
|
switch (seg[0]) {
|
|
2519
2528
|
case 'M':
|
|
2520
2529
|
case 'Z':
|
|
2521
|
-
|
|
2522
|
-
my = seg[0] === 'M' ? seg[2] : my;
|
|
2530
|
+
// @ts-ignore
|
|
2531
|
+
mx = seg[0] === 'M' ? seg[1] : mx; my = seg[0] === 'M' ? seg[2] : my;
|
|
2523
2532
|
x = mx;
|
|
2524
2533
|
y = my;
|
|
2525
2534
|
return 0;
|
|
2526
2535
|
default:
|
|
2536
|
+
// @ts-ignore
|
|
2527
2537
|
len = getCubicSegArea.apply(0, [x, y].concat(seg.slice(1)));
|
|
2538
|
+
// @ts-ignore
|
|
2528
2539
|
[x, y] = seg.slice(-2);
|
|
2529
2540
|
return len;
|
|
2530
2541
|
}
|
|
2531
2542
|
}).reduce((a, b) => a + b, 0);
|
|
2532
2543
|
}
|
|
2533
2544
|
|
|
2545
|
+
/**
|
|
2546
|
+
* @param {number} p1
|
|
2547
|
+
* @param {number} p2
|
|
2548
|
+
* @param {number} p3
|
|
2549
|
+
* @param {number} p4
|
|
2550
|
+
* @param {number} t a [0-1] ratio
|
|
2551
|
+
* @returns {number}
|
|
2552
|
+
*/
|
|
2534
2553
|
function base3(p1, p2, p3, p4, t) {
|
|
2535
2554
|
const t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4;
|
|
2536
2555
|
const t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
|
|
@@ -2540,19 +2559,19 @@ function base3(p1, p2, p3, p4, t) {
|
|
|
2540
2559
|
/**
|
|
2541
2560
|
* Returns the C (cubic-bezier) segment length.
|
|
2542
2561
|
*
|
|
2543
|
-
* @param {
|
|
2544
|
-
* @param {
|
|
2545
|
-
* @param {
|
|
2546
|
-
* @param {
|
|
2547
|
-
* @param {
|
|
2548
|
-
* @param {
|
|
2549
|
-
* @param {
|
|
2550
|
-
* @param {
|
|
2551
|
-
* @param {
|
|
2552
|
-
* @returns {
|
|
2562
|
+
* @param {number} x1 the starting point X
|
|
2563
|
+
* @param {number} y1 the starting point Y
|
|
2564
|
+
* @param {number} x2 the first control point X
|
|
2565
|
+
* @param {number} y2 the first control point Y
|
|
2566
|
+
* @param {number} x3 the second control point X
|
|
2567
|
+
* @param {number} y3 the second control point Y
|
|
2568
|
+
* @param {number} x4 the ending point X
|
|
2569
|
+
* @param {number} y4 the ending point Y
|
|
2570
|
+
* @param {number} z a [0-1] ratio
|
|
2571
|
+
* @returns {number} the cubic-bezier segment length
|
|
2553
2572
|
*/
|
|
2554
2573
|
function getSegCubicLength(x1, y1, x2, y2, x3, y3, x4, y4, z) {
|
|
2555
|
-
let Z;
|
|
2574
|
+
let Z = z;
|
|
2556
2575
|
if (z === null || Number.isNaN(+z)) Z = 1;
|
|
2557
2576
|
|
|
2558
2577
|
// Z = Z > 1 ? 1 : Z < 0 ? 0 : Z;
|
|
@@ -2581,13 +2600,14 @@ function getSegCubicLength(x1, y1, x2, y2, x3, y3, x4, y4, z) {
|
|
|
2581
2600
|
* or the equivalent to `shape.getTotalLength()`
|
|
2582
2601
|
* pathToCurve version
|
|
2583
2602
|
*
|
|
2584
|
-
* @param {
|
|
2585
|
-
* @returns {
|
|
2603
|
+
* @param {SVGPathCommander.pathArray} path the ending point Y
|
|
2604
|
+
* @returns {number} the shape total length
|
|
2586
2605
|
*/
|
|
2587
2606
|
function getPathLength(path) {
|
|
2588
2607
|
let totalLength = 0;
|
|
2589
2608
|
pathToCurve(path).forEach((s, i, curveArray) => {
|
|
2590
2609
|
totalLength += s[0] === 'M' ? 0
|
|
2610
|
+
// @ts-ignore
|
|
2591
2611
|
: getSegCubicLength.apply(0, curveArray[i - 1].slice(-2).concat(s.slice(1)));
|
|
2592
2612
|
});
|
|
2593
2613
|
return totalLength;
|
|
@@ -2597,7 +2617,7 @@ function getPathLength(path) {
|
|
|
2597
2617
|
* Check if a path is drawn clockwise and returns true if so,
|
|
2598
2618
|
* false otherwise.
|
|
2599
2619
|
*
|
|
2600
|
-
* @param {string |
|
|
2620
|
+
* @param {string | SVGPathCommander.pathArray} path the path string or `pathArray`
|
|
2601
2621
|
* @returns {boolean} true when clockwise or false if not
|
|
2602
2622
|
*/
|
|
2603
2623
|
function getDrawDirection(path) {
|
|
@@ -2607,24 +2627,26 @@ function getDrawDirection(path) {
|
|
|
2607
2627
|
/**
|
|
2608
2628
|
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
2609
2629
|
*
|
|
2610
|
-
* @param {string |
|
|
2611
|
-
* @param {
|
|
2612
|
-
* @returns {
|
|
2630
|
+
* @param {string | SVGPathCommander.pathArray} path the `pathArray` to look into
|
|
2631
|
+
* @param {number} length the length of the shape to look at
|
|
2632
|
+
* @returns {number[]} the requested [x,y] coordinates
|
|
2613
2633
|
*/
|
|
2614
2634
|
function getPointAtLength(path, length) {
|
|
2615
2635
|
let totalLength = 0;
|
|
2616
2636
|
let segLen;
|
|
2617
2637
|
let data;
|
|
2618
2638
|
let result;
|
|
2619
|
-
|
|
2639
|
+
// @ts-ignore
|
|
2620
2640
|
return pathToCurve(path).map((seg, i, curveArray) => {
|
|
2621
2641
|
data = i ? curveArray[i - 1].slice(-2).concat(seg.slice(1)) : seg.slice(1);
|
|
2642
|
+
// @ts-ignore
|
|
2622
2643
|
segLen = i ? getSegCubicLength.apply(0, data) : 0;
|
|
2623
2644
|
totalLength += segLen;
|
|
2624
2645
|
|
|
2625
2646
|
if (i === 0) {
|
|
2626
2647
|
result = { x: data[0], y: data[1] };
|
|
2627
2648
|
} else if (totalLength > length && length > totalLength - segLen) {
|
|
2649
|
+
// @ts-ignore
|
|
2628
2650
|
result = getPointAtSegLength.apply(0, data.concat(1 - (totalLength - length) / segLen));
|
|
2629
2651
|
} else {
|
|
2630
2652
|
result = null;
|
|
@@ -2646,7 +2668,7 @@ function isValidPath(pathString) {
|
|
|
2646
2668
|
return false;
|
|
2647
2669
|
}
|
|
2648
2670
|
|
|
2649
|
-
const path = new
|
|
2671
|
+
const path = new PathParser(pathString);
|
|
2650
2672
|
|
|
2651
2673
|
skipSpaces(path);
|
|
2652
2674
|
|
|
@@ -2659,6 +2681,7 @@ function isValidPath(pathString) {
|
|
|
2659
2681
|
|
|
2660
2682
|
/**
|
|
2661
2683
|
* Supported shapes and their specific parameters.
|
|
2684
|
+
* @type {Object.<string, string[]>}
|
|
2662
2685
|
*/
|
|
2663
2686
|
const shapeParams = {
|
|
2664
2687
|
circle: ['cx', 'cy', 'r'],
|
|
@@ -2672,8 +2695,8 @@ const shapeParams = {
|
|
|
2672
2695
|
/**
|
|
2673
2696
|
* Returns a new `pathArray` from line attributes.
|
|
2674
2697
|
*
|
|
2675
|
-
* @param {
|
|
2676
|
-
* @
|
|
2698
|
+
* @param {SVGPathCommander.lineAttr} attr shape configuration
|
|
2699
|
+
* @returns {SVGPathCommander.pathArray} a new line `pathArray`
|
|
2677
2700
|
*/
|
|
2678
2701
|
function getLinePath(attr) {
|
|
2679
2702
|
const {
|
|
@@ -2685,11 +2708,11 @@ function getLinePath(attr) {
|
|
|
2685
2708
|
/**
|
|
2686
2709
|
* Returns a new `pathArray` like from polyline/polygon attributes.
|
|
2687
2710
|
*
|
|
2688
|
-
* @param {
|
|
2689
|
-
* @return {
|
|
2711
|
+
* @param {SVGPathCommander.polyAttr} attr shape configuration
|
|
2712
|
+
* @return {SVGPathCommander.pathArray} a new polygon/polyline `pathArray`
|
|
2690
2713
|
*/
|
|
2691
2714
|
function getPolyPath(attr) {
|
|
2692
|
-
/** @type {
|
|
2715
|
+
/** @type {SVGPathCommander.pathArray} */
|
|
2693
2716
|
const pathArray = [];
|
|
2694
2717
|
const points = attr.points.split(/[\s|,]/).map(Number);
|
|
2695
2718
|
|
|
@@ -2705,8 +2728,8 @@ function getPolyPath(attr) {
|
|
|
2705
2728
|
/**
|
|
2706
2729
|
* Returns a new `pathArray` from circle attributes.
|
|
2707
2730
|
*
|
|
2708
|
-
* @param {
|
|
2709
|
-
* @return {
|
|
2731
|
+
* @param {SVGPathCommander.circleAttr} attr shape configuration
|
|
2732
|
+
* @return {SVGPathCommander.pathArray} a circle `pathArray`
|
|
2710
2733
|
*/
|
|
2711
2734
|
function getCirclePath(attr) {
|
|
2712
2735
|
const {
|
|
@@ -2723,8 +2746,8 @@ function getCirclePath(attr) {
|
|
|
2723
2746
|
/**
|
|
2724
2747
|
* Returns a new `pathArray` from ellipse attributes.
|
|
2725
2748
|
*
|
|
2726
|
-
* @param {
|
|
2727
|
-
* @return {
|
|
2749
|
+
* @param {SVGPathCommander.ellipseAttr} attr shape configuration
|
|
2750
|
+
* @return {SVGPathCommander.pathArray} an ellipse `pathArray`
|
|
2728
2751
|
*/
|
|
2729
2752
|
function getEllipsePath(attr) {
|
|
2730
2753
|
const {
|
|
@@ -2741,8 +2764,8 @@ function getEllipsePath(attr) {
|
|
|
2741
2764
|
/**
|
|
2742
2765
|
* Returns a new `pathArray` like from rect attributes.
|
|
2743
2766
|
*
|
|
2744
|
-
* @param {
|
|
2745
|
-
* @return {
|
|
2767
|
+
* @param {SVGPathCommander.rectAttr} attr object with properties above
|
|
2768
|
+
* @return {SVGPathCommander.pathArray} a new `pathArray` from `<rect>` attributes
|
|
2746
2769
|
*/
|
|
2747
2770
|
function getRectanglePath(attr) {
|
|
2748
2771
|
const x = +attr.x || 0;
|
|
@@ -2790,7 +2813,7 @@ function getRectanglePath(attr) {
|
|
|
2790
2813
|
* The newly created `<path>` element keeps all non-specific
|
|
2791
2814
|
* attributes like `class`, `fill`, etc.
|
|
2792
2815
|
*
|
|
2793
|
-
* @param {
|
|
2816
|
+
* @param {SVGPathCommander.shapeTypes} element target shape
|
|
2794
2817
|
* @param {boolean} replace option to replace target
|
|
2795
2818
|
* @return {?SVGPathElement} the newly created `<path>` element
|
|
2796
2819
|
*/
|
|
@@ -2803,12 +2826,10 @@ function shapeToPath(element, replace) {
|
|
|
2803
2826
|
|
|
2804
2827
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
2805
2828
|
const type = element.tagName;
|
|
2806
|
-
/** @type {string[]} */
|
|
2807
2829
|
const shapeAttrs = shapeParams[type];
|
|
2808
|
-
|
|
2809
|
-
// set config
|
|
2810
2830
|
const config = {};
|
|
2811
2831
|
config.type = type;
|
|
2832
|
+
|
|
2812
2833
|
shapeAttrs.forEach((p) => { config[p] = element.getAttribute(p); });
|
|
2813
2834
|
|
|
2814
2835
|
// set no-specific shape attributes: fill, stroke, etc
|
|
@@ -2818,11 +2839,14 @@ function shapeToPath(element, replace) {
|
|
|
2818
2839
|
|
|
2819
2840
|
// set d
|
|
2820
2841
|
let description;
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
else if (type === '
|
|
2842
|
+
const { round, decimals } = SVGPCO;
|
|
2843
|
+
const rounding = round && decimals ? decimals : null;
|
|
2844
|
+
|
|
2845
|
+
if (type === 'circle') description = pathToString(getCirclePath(config), rounding);
|
|
2846
|
+
else if (type === 'ellipse') description = pathToString(getEllipsePath(config), rounding);
|
|
2847
|
+
else if (['polyline', 'polygon'].includes(type)) description = pathToString(getPolyPath(config), rounding);
|
|
2848
|
+
else if (type === 'rect') description = pathToString(getRectanglePath(config), rounding);
|
|
2849
|
+
else if (type === 'line') description = pathToString(getLinePath(config), rounding);
|
|
2826
2850
|
else if (type === 'glyph') description = element.getAttribute('d');
|
|
2827
2851
|
|
|
2828
2852
|
// replace target element
|
|
@@ -2841,8 +2865,8 @@ function shapeToPath(element, replace) {
|
|
|
2841
2865
|
* Reverses all segments and their values from a `pathArray`
|
|
2842
2866
|
* which consists of only C (cubic-bezier) path commands.
|
|
2843
2867
|
*
|
|
2844
|
-
* @param {
|
|
2845
|
-
* @returns {
|
|
2868
|
+
* @param {SVGPathCommander.pathArray} path the source `pathArray`
|
|
2869
|
+
* @returns {SVGPathCommander.pathArray} the reversed `pathArray`
|
|
2846
2870
|
*/
|
|
2847
2871
|
function reverseCurve(path) {
|
|
2848
2872
|
const rotatedCurve = path.slice(1)
|
|
@@ -2852,12 +2876,13 @@ function reverseCurve(path) {
|
|
|
2852
2876
|
.map((x) => x.map((_, i) => x[x.length - i - 2 * (1 - (i % 2))]))
|
|
2853
2877
|
.reverse();
|
|
2854
2878
|
|
|
2855
|
-
|
|
2856
|
-
|
|
2879
|
+
// @ts-ignore
|
|
2880
|
+
return [['M'].concat(rotatedCurve[0].slice(0, 2))]
|
|
2881
|
+
// @ts-ignore
|
|
2857
2882
|
.concat(rotatedCurve.map((x) => ['C'].concat(x.slice(2))));
|
|
2858
2883
|
}
|
|
2859
2884
|
|
|
2860
|
-
var version = "0.1.
|
|
2885
|
+
var version = "0.1.11alpha2";
|
|
2861
2886
|
|
|
2862
2887
|
// @ts-ignore
|
|
2863
2888
|
|
|
@@ -2899,8 +2924,6 @@ const Util = {
|
|
|
2899
2924
|
Version,
|
|
2900
2925
|
};
|
|
2901
2926
|
|
|
2902
|
-
// import isPathArray from './util/isPathArray';
|
|
2903
|
-
|
|
2904
2927
|
/**
|
|
2905
2928
|
* Creates a new SVGPathCommander instance.
|
|
2906
2929
|
*
|
|
@@ -2911,7 +2934,7 @@ class SVGPathCommander {
|
|
|
2911
2934
|
/**
|
|
2912
2935
|
* @constructor
|
|
2913
2936
|
* @param {string} pathValue the path string
|
|
2914
|
-
* @param {
|
|
2937
|
+
* @param {SVGPathCommander.options} config instance options
|
|
2915
2938
|
*/
|
|
2916
2939
|
constructor(pathValue, config) {
|
|
2917
2940
|
const options = config || {};
|
|
@@ -2922,13 +2945,13 @@ class SVGPathCommander {
|
|
|
2922
2945
|
round = 0;
|
|
2923
2946
|
}
|
|
2924
2947
|
|
|
2925
|
-
const { decimals } = round
|
|
2948
|
+
const { decimals } = round ? (options || SVGPCO) : { decimals: false };
|
|
2926
2949
|
|
|
2927
2950
|
// set instance options
|
|
2928
|
-
this.round =
|
|
2951
|
+
this.round = decimals;
|
|
2929
2952
|
// ZERO | FALSE will disable rounding numbers
|
|
2930
2953
|
|
|
2931
|
-
/** @type {
|
|
2954
|
+
/** @type {SVGPathCommander.pathArray} */
|
|
2932
2955
|
this.segments = parsePathString(pathValue);
|
|
2933
2956
|
|
|
2934
2957
|
/** * @type {string} */
|
|
@@ -2968,20 +2991,21 @@ class SVGPathCommander {
|
|
|
2968
2991
|
const { segments } = this;
|
|
2969
2992
|
const split = splitPath(this.toString());
|
|
2970
2993
|
const subPath = split.length > 1 ? split : 0;
|
|
2994
|
+
/**
|
|
2995
|
+
* @param {import("../types").pathArray} x
|
|
2996
|
+
* @param {number} i
|
|
2997
|
+
*/
|
|
2998
|
+
const reverser = (x, i) => {
|
|
2999
|
+
if (onlySubpath) {
|
|
3000
|
+
return i ? reversePath(x) : parsePathString(x);
|
|
3001
|
+
}
|
|
3002
|
+
return reversePath(x);
|
|
3003
|
+
};
|
|
2971
3004
|
|
|
2972
|
-
const absoluteMultiPath = subPath && clonePath(subPath)
|
|
2973
|
-
.map((x, i) => {
|
|
2974
|
-
if (onlySubpath) {
|
|
2975
|
-
// @ts-ignore
|
|
2976
|
-
return i ? reversePath(x) : parsePathString(x);
|
|
2977
|
-
}
|
|
2978
|
-
// @ts-ignore
|
|
2979
|
-
return reversePath(x);
|
|
2980
|
-
});
|
|
3005
|
+
const absoluteMultiPath = subPath && clonePath(subPath).map(reverser);
|
|
2981
3006
|
|
|
2982
3007
|
let path = [];
|
|
2983
3008
|
if (subPath) {
|
|
2984
|
-
// @ts-ignore
|
|
2985
3009
|
path = absoluteMultiPath.flat(1);
|
|
2986
3010
|
} else {
|
|
2987
3011
|
path = onlySubpath ? segments : reversePath(segments);
|
|
@@ -3019,7 +3043,7 @@ class SVGPathCommander {
|
|
|
3019
3043
|
|
|
3020
3044
|
/**
|
|
3021
3045
|
* Transform path using values from an `Object` defined as `transformObject`.
|
|
3022
|
-
* @see
|
|
3046
|
+
* @see SVGPathCommander.transformObject for a quick refference
|
|
3023
3047
|
*
|
|
3024
3048
|
* @param {Object.<string, (number | number[])>} source a `transformObject`as described above
|
|
3025
3049
|
* @public
|
|
@@ -3035,7 +3059,7 @@ class SVGPathCommander {
|
|
|
3035
3059
|
// it's important that we have one
|
|
3036
3060
|
if (!transform.origin) {
|
|
3037
3061
|
const BBox = getPathBBox(segments);
|
|
3038
|
-
transform.origin = [BBox.cx, BBox.cy
|
|
3062
|
+
transform.origin = [+BBox.cx, +BBox.cy];
|
|
3039
3063
|
}
|
|
3040
3064
|
|
|
3041
3065
|
this.segments = transformPath(segments, transform);
|
|
@@ -3071,7 +3095,7 @@ class SVGPathCommander {
|
|
|
3071
3095
|
}
|
|
3072
3096
|
}
|
|
3073
3097
|
|
|
3074
|
-
//
|
|
3075
|
-
Object.
|
|
3098
|
+
// Export Util to global
|
|
3099
|
+
Object.assign(SVGPathCommander, Util);
|
|
3076
3100
|
|
|
3077
3101
|
export { SVGPathCommander as default };
|