svg-path-commander 0.1.8 → 0.1.10-alpha3
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 +2538 -1861
- package/dist/svg-path-commander.esm.min.js +2 -2
- package/dist/svg-path-commander.js +2564 -1880
- package/dist/svg-path-commander.min.js +2 -2
- package/package.json +4 -4
- package/src/convert/pathToAbsolute.js +101 -92
- package/src/convert/pathToCurve.js +45 -41
- package/src/convert/pathToRelative.js +98 -89
- package/src/convert/pathToString.js +14 -6
- package/src/index.js +3 -6
- package/src/math/distanceSquareRoot.js +14 -6
- package/src/math/epsilon.js +8 -1
- package/src/math/midPoint.js +13 -7
- package/src/math/polygonArea.js +25 -18
- package/src/math/polygonLength.js +19 -13
- package/src/math/rotateVector.js +14 -0
- package/src/options/options.js +11 -7
- package/src/parser/finalizeSegment.js +33 -0
- package/src/{util → parser}/invalidPathValue.js +2 -2
- package/src/parser/isArcCommand.js +10 -0
- package/src/parser/isDigit.js +9 -0
- package/src/{util → parser}/isDigitStart.js +13 -6
- package/src/{util → parser}/isPathCommand.js +25 -19
- package/src/{util → parser}/isSpace.js +15 -9
- package/src/parser/paramsCount.js +8 -0
- package/src/parser/parsePathString.js +41 -0
- package/src/parser/scanFlag.js +26 -0
- package/src/parser/scanParam.js +97 -0
- package/src/parser/scanSegment.js +70 -0
- package/src/parser/skipSpaces.js +15 -0
- package/src/parser/svgPathArray.js +23 -0
- package/src/process/arcToCubic.js +114 -111
- package/src/process/clonePath.js +15 -8
- package/src/process/fixArc.js +23 -0
- package/src/{util → process}/getSVGMatrix.js +62 -55
- package/src/process/lineToCubic.js +28 -17
- package/src/process/normalizePath.js +49 -41
- package/src/process/normalizeSegment.js +43 -35
- package/src/process/optimizePath.js +24 -14
- package/src/{util → process}/projection2d.js +30 -21
- package/src/process/quadToCubic.js +22 -11
- package/src/process/reverseCurve.js +20 -13
- package/src/process/reversePath.js +90 -83
- package/src/process/roundPath.js +27 -21
- package/src/process/segmentToCubic.js +42 -31
- package/src/process/shorthandToCubic.js +16 -6
- package/src/process/shorthandToQuad.js +16 -6
- package/src/process/splitCubic.js +26 -20
- package/src/process/splitPath.js +19 -10
- package/src/{util → process}/transformEllipse.js +73 -65
- package/src/process/transformPath.js +138 -125
- package/src/{class/SVGPathCommander.js → svg-path-commander.js} +193 -201
- package/src/util/createPath.js +17 -8
- package/src/util/getCubicSize.js +64 -50
- package/src/util/getDrawDirection.js +13 -6
- package/src/util/getPathArea.js +54 -30
- package/src/util/getPathBBox.js +60 -50
- package/src/util/getPathLength.js +20 -13
- package/src/util/getPointAtLength.js +35 -28
- package/src/util/getPointAtSegLength.js +28 -14
- package/src/util/getSegArcLength.js +24 -21
- package/src/util/getSegCubicLength.js +52 -31
- package/src/util/getSegLineLength.js +14 -6
- package/src/util/getSegQuadLength.js +31 -19
- package/src/util/isAbsoluteArray.js +13 -5
- package/src/util/isCurveArray.js +13 -5
- package/src/util/isNormalizedArray.js +17 -8
- package/src/util/isPathArray.js +14 -8
- package/src/util/isRelativeArray.js +13 -6
- package/src/util/isValidPath.js +26 -0
- package/src/util/shapeToPath.js +187 -0
- package/src/util/util.js +65 -60
- package/src/util/version.js +9 -0
- package/types/index.d.ts +841 -2
- package/types/types.d.ts +83 -0
- package/src/process/finalizeSegment.js +0 -26
- package/src/process/parsePathString.js +0 -35
- package/src/process/scanFlag.js +0 -20
- package/src/process/scanParam.js +0 -93
- package/src/process/scanSegment.js +0 -64
- package/src/process/skipSpaces.js +0 -7
- package/src/util/DOMMatrix.js +0 -5
- package/src/util/fixArc.js +0 -13
- package/src/util/isArcCommand.js +0 -4
- package/src/util/isDigit.js +0 -3
- package/src/util/paramsCount.js +0 -3
- package/src/util/rotateVector.js +0 -5
- package/src/util/svgPathArray.js +0 -10
- package/types/class/SVGPathCommander.d.ts +0 -91
- package/types/convert/pathToAbsolute.d.ts +0 -1
- package/types/convert/pathToCurve.d.ts +0 -1
- package/types/convert/pathToRelative.d.ts +0 -1
- package/types/convert/pathToString.d.ts +0 -1
- package/types/math/distanceSquareRoot.d.ts +0 -1
- package/types/math/epsilon.d.ts +0 -2
- package/types/math/midPoint.d.ts +0 -1
- package/types/math/polygonArea.d.ts +0 -1
- package/types/math/polygonLength.d.ts +0 -1
- package/types/options/options.d.ts +0 -6
- package/types/process/arcToCubic.d.ts +0 -1
- package/types/process/clonePath.d.ts +0 -1
- package/types/process/finalizeSegment.d.ts +0 -1
- package/types/process/lineToCubic.d.ts +0 -1
- package/types/process/normalizePath.d.ts +0 -1
- package/types/process/normalizeSegment.d.ts +0 -1
- package/types/process/optimizePath.d.ts +0 -1
- package/types/process/parsePathString.d.ts +0 -1
- package/types/process/quadToCubic.d.ts +0 -1
- package/types/process/reverseCurve.d.ts +0 -1
- package/types/process/reversePath.d.ts +0 -1
- package/types/process/roundPath.d.ts +0 -1
- package/types/process/scanFlag.d.ts +0 -1
- package/types/process/scanParam.d.ts +0 -1
- package/types/process/scanSegment.d.ts +0 -1
- package/types/process/segmentToCubic.d.ts +0 -1
- package/types/process/shorthandToCubic.d.ts +0 -4
- package/types/process/shorthandToQuad.d.ts +0 -4
- package/types/process/skipSpaces.d.ts +0 -1
- package/types/process/splitCubic.d.ts +0 -1
- package/types/process/splitPath.d.ts +0 -1
- package/types/process/transformPath.d.ts +0 -1
- package/types/util/DOMMatrix.d.ts +0 -2
- package/types/util/createPath.d.ts +0 -1
- package/types/util/fixArc.d.ts +0 -1
- package/types/util/getCubicSize.d.ts +0 -10
- package/types/util/getDrawDirection.d.ts +0 -1
- package/types/util/getPathArea.d.ts +0 -1
- package/types/util/getPathBBox.d.ts +0 -19
- package/types/util/getPathLength.d.ts +0 -1
- package/types/util/getPointAtLength.d.ts +0 -1
- package/types/util/getPointAtSegLength.d.ts +0 -4
- package/types/util/getSVGMatrix.d.ts +0 -1
- package/types/util/getSegArcLength.d.ts +0 -1
- package/types/util/getSegCubicLength.d.ts +0 -1
- package/types/util/getSegLineLength.d.ts +0 -1
- package/types/util/getSegQuadLength.d.ts +0 -1
- package/types/util/invalidPathValue.d.ts +0 -2
- package/types/util/isAbsoluteArray.d.ts +0 -1
- package/types/util/isArcCommand.d.ts +0 -1
- package/types/util/isCurveArray.d.ts +0 -1
- package/types/util/isDigit.d.ts +0 -1
- package/types/util/isDigitStart.d.ts +0 -1
- package/types/util/isNormalizedArray.d.ts +0 -1
- package/types/util/isPathArray.d.ts +0 -1
- package/types/util/isPathCommand.d.ts +0 -1
- package/types/util/isRelativeArray.d.ts +0 -1
- package/types/util/isSpace.d.ts +0 -1
- package/types/util/paramsCount.d.ts +0 -14
- package/types/util/projection2d.d.ts +0 -1
- package/types/util/rotateVector.d.ts +0 -4
- package/types/util/svgPathArray.d.ts +0 -12
- package/types/util/transformEllipse.d.ts +0 -5
- package/types/util/util.d.ts +0 -55
|
@@ -1,1628 +1,2331 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* SVGPathCommander v0.1.
|
|
2
|
+
* SVGPathCommander v0.1.10alpha3 (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
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* SVGPathCommander default options
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
6
10
|
const SVGPCO = {
|
|
7
|
-
origin:
|
|
11
|
+
origin: [0, 0],
|
|
8
12
|
decimals: 4,
|
|
9
13
|
round: 1,
|
|
10
14
|
};
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @type {Object.<string, number>}
|
|
18
|
+
*/
|
|
19
|
+
const paramsCount = {
|
|
20
|
+
a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0,
|
|
21
|
+
};
|
|
17
22
|
|
|
18
23
|
/**
|
|
19
|
-
*
|
|
24
|
+
* Breaks the parsing of a pathString once a segment is finalized.
|
|
20
25
|
*
|
|
21
|
-
*
|
|
22
|
-
* the result is a 3D matrix. Otherwise, a TypeError exception is thrown.
|
|
23
|
-
*
|
|
24
|
-
* @param {Number[]} array an `Array` to feed values from.
|
|
25
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
26
|
+
* @param {SVGPC.parserPathArray} path the `parserPathArray` instance
|
|
26
27
|
*/
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
function finalizeSegment(path) {
|
|
29
|
+
let pathCommand = path.pathValue[path.segmentStart];
|
|
30
|
+
let pathComLK = pathCommand.toLowerCase();
|
|
31
|
+
let params = path.data;
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
// Process duplicated commands (without comand name)
|
|
34
|
+
if (pathComLK === 'm' && params.length > 2) {
|
|
35
|
+
path.segments.push([pathCommand, params[0], params[1]]);
|
|
36
|
+
params = params.slice(2);
|
|
37
|
+
pathComLK = 'l';
|
|
38
|
+
pathCommand = (pathCommand === 'm') ? 'l' : 'L';
|
|
39
|
+
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
if (pathComLK === 'r') {
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
path.segments.push([pathCommand].concat(params));
|
|
44
|
+
} else {
|
|
45
|
+
while (params.length >= paramsCount[pathComLK]) {
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
path.segments.push([pathCommand].concat(params.splice(0, paramsCount[pathComLK])));
|
|
48
|
+
if (!paramsCount[pathComLK]) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
39
54
|
|
|
40
|
-
|
|
41
|
-
m.c = m21;
|
|
55
|
+
const invalidPathValue = 'Invalid path value';
|
|
42
56
|
|
|
43
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Validates an A (arc-to) specific path command value.
|
|
59
|
+
* Usually a `large-arc-flag` or `sweep-flag`.
|
|
60
|
+
*
|
|
61
|
+
* @param {SVGPC.parserPathArray} path the `parserPathArray` instance
|
|
62
|
+
*/
|
|
63
|
+
function scanFlag(path) {
|
|
64
|
+
const { index } = path;
|
|
65
|
+
const ch = path.pathValue.charCodeAt(index);
|
|
44
66
|
|
|
45
|
-
|
|
46
|
-
|
|
67
|
+
if (ch === 0x30/* 0 */) {
|
|
68
|
+
path.param = 0;
|
|
69
|
+
path.index += 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
47
72
|
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
if (ch === 0x31/* 1 */) {
|
|
74
|
+
path.param = 1;
|
|
75
|
+
path.index += 1;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
50
78
|
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
path.err = `${invalidPathValue}: invalid Arc flag ${ch}, expecting 0 or 1 at index ${index}`;
|
|
80
|
+
}
|
|
53
81
|
|
|
54
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Checks if a character is a digit.
|
|
84
|
+
*
|
|
85
|
+
* @param {number} code the character to check
|
|
86
|
+
* @returns {boolean} check result
|
|
87
|
+
*/
|
|
88
|
+
function isDigit(code) {
|
|
89
|
+
return (code >= 48 && code <= 57); // 0..9
|
|
90
|
+
}
|
|
55
91
|
|
|
56
|
-
|
|
57
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Validates every character of the path string,
|
|
94
|
+
* every path command, negative numbers or floating point numbers.
|
|
95
|
+
*
|
|
96
|
+
* @param {SVGPC.parserPathArray} path the `parserPathArray` instance
|
|
97
|
+
*/
|
|
98
|
+
function scanParam(path) {
|
|
99
|
+
const { max, pathValue, index: start } = path;
|
|
100
|
+
let index = start;
|
|
101
|
+
let zeroFirst = false;
|
|
102
|
+
let hasCeiling = false;
|
|
103
|
+
let hasDecimal = false;
|
|
104
|
+
let hasDot = false;
|
|
105
|
+
let ch;
|
|
58
106
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
m.m34 = m34;
|
|
66
|
-
m.m44 = m44;
|
|
67
|
-
} else if (a.length === 6) {
|
|
68
|
-
const [m11, m12, m21, m22, m41, m42] = a;
|
|
107
|
+
if (index >= max) {
|
|
108
|
+
// path.err = 'SvgPath: missed param (at pos ' + index + ')';
|
|
109
|
+
path.err = `${invalidPathValue} at ${index}: missing param ${pathValue[index]}`;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
ch = pathValue.charCodeAt(index);
|
|
69
113
|
|
|
70
|
-
|
|
71
|
-
|
|
114
|
+
if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
|
|
115
|
+
index += 1;
|
|
116
|
+
ch = (index < max) ? pathValue.charCodeAt(index) : 0;
|
|
117
|
+
}
|
|
72
118
|
|
|
73
|
-
|
|
74
|
-
|
|
119
|
+
// This logic is shamelessly borrowed from Esprima
|
|
120
|
+
// https://github.com/ariya/esprimas
|
|
121
|
+
if (!isDigit(ch) && ch !== 0x2E/* . */) {
|
|
122
|
+
// path.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
|
|
123
|
+
path.err = `${invalidPathValue} at index ${index}: ${pathValue[index]} is not a number`;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
75
126
|
|
|
76
|
-
|
|
77
|
-
|
|
127
|
+
if (ch !== 0x2E/* . */) {
|
|
128
|
+
zeroFirst = (ch === 0x30/* 0 */);
|
|
129
|
+
index += 1;
|
|
78
130
|
|
|
79
|
-
|
|
80
|
-
m.d = m22;
|
|
131
|
+
ch = (index < max) ? pathValue.charCodeAt(index) : 0;
|
|
81
132
|
|
|
82
|
-
|
|
83
|
-
|
|
133
|
+
if (zeroFirst && index < max) {
|
|
134
|
+
// decimal number starts with '0' such as '09' is illegal.
|
|
135
|
+
if (ch && isDigit(ch)) {
|
|
136
|
+
// path.err = 'SvgPath: numbers started with `0` such as `09`
|
|
137
|
+
// are illegal (at pos ' + start + ')';
|
|
138
|
+
path.err = `${invalidPathValue} at index ${start}: ${pathValue[start]} illegal number`;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
84
142
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
143
|
+
while (index < max && isDigit(pathValue.charCodeAt(index))) {
|
|
144
|
+
index += 1;
|
|
145
|
+
hasCeiling = true;
|
|
146
|
+
}
|
|
147
|
+
ch = (index < max) ? pathValue.charCodeAt(index) : 0;
|
|
89
148
|
}
|
|
90
|
-
return m;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Creates a new mutable `CSSMatrix` object given an existing matrix or a
|
|
95
|
-
* `DOMMatrix` *Object* which provides the values for its properties.
|
|
96
|
-
*
|
|
97
|
-
* @param {CSSMatrix | DOMMatrix} m the source matrix to feed values from.
|
|
98
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
99
|
-
*/
|
|
100
|
-
function fromMatrix(m) {
|
|
101
|
-
return fromArray(
|
|
102
|
-
[m.m11, m.m12, m.m13, m.m14,
|
|
103
|
-
m.m21, m.m22, m.m23, m.m24,
|
|
104
|
-
m.m31, m.m32, m.m33, m.m34,
|
|
105
|
-
m.m41, m.m42, m.m43, m.m44],
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
149
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
function fromString(source) {
|
|
119
|
-
const str = String(source).replace(/\s/g, '');
|
|
120
|
-
let m = new CSSMatrix();
|
|
121
|
-
let is2D = true;
|
|
122
|
-
const tramsformObject = str.split(')').filter((f) => f).map((fn) => {
|
|
123
|
-
const [prop, value] = fn.split('(');
|
|
124
|
-
const components = value.split(',')
|
|
125
|
-
.map((n) => (n.includes('rad') ? parseFloat(n) * (180 / Math.PI) : parseFloat(n)));
|
|
126
|
-
const [x, y, z, a] = components;
|
|
150
|
+
if (ch === 0x2E/* . */) {
|
|
151
|
+
hasDot = true;
|
|
152
|
+
index += 1;
|
|
153
|
+
while (isDigit(pathValue.charCodeAt(index))) {
|
|
154
|
+
index += 1;
|
|
155
|
+
hasDecimal = true;
|
|
156
|
+
}
|
|
157
|
+
ch = (index < max) ? pathValue.charCodeAt(index) : 0;
|
|
158
|
+
}
|
|
127
159
|
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|| (prop === 'translate3d' && [x, y, z].every((n) => !Number.isNaN(+n)) && z)
|
|
133
|
-
|| (prop === 'scale3d' && [x, y, z].every((n) => !Number.isNaN(+n) && n !== x))
|
|
134
|
-
) {
|
|
135
|
-
is2D = false;
|
|
160
|
+
if (ch === 0x65/* e */ || ch === 0x45/* E */) {
|
|
161
|
+
if (hasDot && !hasCeiling && !hasDecimal) {
|
|
162
|
+
path.err = `${invalidPathValue} at index ${index}: ${pathValue[index]} invalid float exponent`;
|
|
163
|
+
return;
|
|
136
164
|
}
|
|
137
|
-
return { prop, components };
|
|
138
|
-
});
|
|
139
165
|
|
|
140
|
-
|
|
141
|
-
const { prop, components } = tf;
|
|
142
|
-
const [x, y, z, a] = components;
|
|
143
|
-
const xyz = [x, y, z];
|
|
144
|
-
const xyza = [x, y, z, a];
|
|
166
|
+
index += 1;
|
|
145
167
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
} else if (['translate', 'translate3d'].some((p) => prop === p) && x) {
|
|
154
|
-
m = m.translate(x, y || 0, z || 0);
|
|
155
|
-
} else if (prop === 'rotate3d' && xyza.every((n) => !Number.isNaN(+n)) && a) {
|
|
156
|
-
m = m.rotateAxisAngle(x, y, z, a);
|
|
157
|
-
} else if (prop === 'scale3d' && xyz.every((n) => !Number.isNaN(+n)) && xyz.some((n) => n !== 1)) {
|
|
158
|
-
m = m.scale(x, y, z);
|
|
159
|
-
} else if (prop === 'rotate' && x) {
|
|
160
|
-
m = m.rotate(0, 0, x);
|
|
161
|
-
} else if (prop === 'scale' && !Number.isNaN(x) && x !== 1) {
|
|
162
|
-
const nosy = Number.isNaN(+y);
|
|
163
|
-
const sy = nosy ? x : y;
|
|
164
|
-
m = m.scale(x, sy, 1);
|
|
165
|
-
} else if (prop === 'skew' && (x || y)) {
|
|
166
|
-
m = x ? m.skewX(x) : m;
|
|
167
|
-
m = y ? m.skewY(y) : m;
|
|
168
|
-
} else if (/[XYZ]/.test(prop) && x) {
|
|
169
|
-
if (prop.includes('skew')) {
|
|
170
|
-
// @ts-ignore unfortunately
|
|
171
|
-
m = m[prop](x);
|
|
172
|
-
} else {
|
|
173
|
-
const fn = prop.replace(/[XYZ]/, '');
|
|
174
|
-
const axis = prop.replace(fn, '');
|
|
175
|
-
const idx = ['X', 'Y', 'Z'].indexOf(axis);
|
|
176
|
-
const axeValues = [
|
|
177
|
-
idx === 0 ? x : 0,
|
|
178
|
-
idx === 1 ? x : 0,
|
|
179
|
-
idx === 2 ? x : 0];
|
|
180
|
-
// @ts-ignore unfortunately
|
|
181
|
-
m = m[fn](...axeValues);
|
|
168
|
+
ch = (index < max) ? pathValue.charCodeAt(index) : 0;
|
|
169
|
+
if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
|
|
170
|
+
index += 1;
|
|
171
|
+
}
|
|
172
|
+
if (index < max && isDigit(pathValue.charCodeAt(index))) {
|
|
173
|
+
while (index < max && isDigit(pathValue.charCodeAt(index))) {
|
|
174
|
+
index += 1;
|
|
182
175
|
}
|
|
176
|
+
} else {
|
|
177
|
+
// path.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
|
|
178
|
+
path.err = `${invalidPathValue} at index ${index}: ${pathValue[index]} invalid float exponent`;
|
|
179
|
+
return;
|
|
183
180
|
}
|
|
184
|
-
}
|
|
181
|
+
}
|
|
185
182
|
|
|
186
|
-
|
|
183
|
+
path.index = index;
|
|
184
|
+
path.param = +path.pathValue.slice(start, index);
|
|
187
185
|
}
|
|
188
186
|
|
|
189
|
-
// Transform Functions
|
|
190
|
-
// https://www.w3.org/TR/css-transforms-1/#transform-functions
|
|
191
|
-
|
|
192
187
|
/**
|
|
193
|
-
*
|
|
194
|
-
* This method is equivalent to the CSS `translate3d()` function.
|
|
195
|
-
*
|
|
196
|
-
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translate3d
|
|
188
|
+
* Checks if the character is a space.
|
|
197
189
|
*
|
|
198
|
-
* @param {
|
|
199
|
-
* @
|
|
200
|
-
* @param {Number} z the `z-axis` position.
|
|
201
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
190
|
+
* @param {number} ch the character to check
|
|
191
|
+
* @returns {boolean} check result
|
|
202
192
|
*/
|
|
203
|
-
function
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
193
|
+
function isSpace(ch) {
|
|
194
|
+
const specialSpaces = [
|
|
195
|
+
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
|
|
196
|
+
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF];
|
|
197
|
+
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) // Line terminators
|
|
198
|
+
// White spaces
|
|
199
|
+
|| (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0)
|
|
200
|
+
|| (ch >= 0x1680 && specialSpaces.indexOf(ch) >= 0);
|
|
211
201
|
}
|
|
212
202
|
|
|
213
203
|
/**
|
|
214
|
-
*
|
|
204
|
+
* Points the parser to the next character in the
|
|
205
|
+
* path string every time it encounters any kind of
|
|
206
|
+
* space character.
|
|
215
207
|
*
|
|
216
|
-
*
|
|
208
|
+
* @param {SVGPC.parserPathArray} path the `parserPathArray` instance
|
|
209
|
+
*/
|
|
210
|
+
function skipSpaces(path) {
|
|
211
|
+
const { pathValue, max } = path;
|
|
212
|
+
while (path.index < max && isSpace(pathValue.charCodeAt(path.index))) {
|
|
213
|
+
path.index += 1;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Checks if the character is a path command.
|
|
217
219
|
*
|
|
218
|
-
* @param {
|
|
219
|
-
* @
|
|
220
|
-
* @param {Number} rz the `z-axis` rotation.
|
|
221
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
220
|
+
* @param {any} code the character to check
|
|
221
|
+
* @returns {boolean} check result
|
|
222
222
|
*/
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
m.a = m11;
|
|
243
|
-
|
|
244
|
-
m.m12 = m12;
|
|
245
|
-
m.b = m12;
|
|
246
|
-
|
|
247
|
-
m.m13 = siny;
|
|
248
|
-
|
|
249
|
-
const m21 = sinx * siny * cosz + cosx * sinz;
|
|
250
|
-
m.m21 = m21;
|
|
251
|
-
m.c = m21;
|
|
252
|
-
|
|
253
|
-
const m22 = cosx * cosz - sinx * siny * sinz;
|
|
254
|
-
m.m22 = m22;
|
|
255
|
-
m.d = m22;
|
|
256
|
-
|
|
257
|
-
m.m23 = -sinx * cosy;
|
|
258
|
-
|
|
259
|
-
m.m31 = sinx * sinz - cosx * siny * cosz;
|
|
260
|
-
m.m32 = sinx * cosz + cosx * siny * sinz;
|
|
261
|
-
m.m33 = cosx * cosy;
|
|
223
|
+
function isPathCommand(code) {
|
|
224
|
+
// eslint-disable-next-line no-bitwise -- Impossible to satisfy
|
|
225
|
+
switch (code | 0x20) {
|
|
226
|
+
case 0x6D/* m */:
|
|
227
|
+
case 0x7A/* z */:
|
|
228
|
+
case 0x6C/* l */:
|
|
229
|
+
case 0x68/* h */:
|
|
230
|
+
case 0x76/* v */:
|
|
231
|
+
case 0x63/* c */:
|
|
232
|
+
case 0x73/* s */:
|
|
233
|
+
case 0x71/* q */:
|
|
234
|
+
case 0x74/* t */:
|
|
235
|
+
case 0x61/* a */:
|
|
236
|
+
case 0x72/* r */:
|
|
237
|
+
return true;
|
|
238
|
+
default:
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
262
242
|
|
|
263
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Checks if the character is or belongs to a number.
|
|
245
|
+
* [0-9]|+|-|.
|
|
246
|
+
*
|
|
247
|
+
* @param {number} code the character to check
|
|
248
|
+
* @returns {boolean} check result
|
|
249
|
+
*/
|
|
250
|
+
function isDigitStart(code) {
|
|
251
|
+
return (code >= 48 && code <= 57) /* 0..9 */
|
|
252
|
+
|| code === 0x2B /* + */
|
|
253
|
+
|| code === 0x2D /* - */
|
|
254
|
+
|| code === 0x2E; /* . */
|
|
264
255
|
}
|
|
265
256
|
|
|
266
257
|
/**
|
|
267
|
-
*
|
|
268
|
-
* This method is equivalent to the CSS `rotate3d()` function.
|
|
258
|
+
* Checks if the character is an A (arc-to) path command.
|
|
269
259
|
*
|
|
270
|
-
*
|
|
260
|
+
* @param {number} code the character to check
|
|
261
|
+
* @returns {boolean} check result
|
|
262
|
+
*/
|
|
263
|
+
function isArcCommand(code) {
|
|
264
|
+
// eslint-disable-next-line no-bitwise -- Impossible to satisfy
|
|
265
|
+
return (code | 0x20) === 0x61;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Scans every character in the path string to determine
|
|
270
|
+
* where a segment starts and where it ends.
|
|
271
271
|
*
|
|
272
|
-
* @param {
|
|
273
|
-
* @param {Number} y the `y-axis` vector length.
|
|
274
|
-
* @param {Number} z the `z-axis` vector length.
|
|
275
|
-
* @param {Number} alpha the value in degrees of the rotation.
|
|
276
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
272
|
+
* @param {SVGPC.parserPathArray} path the `parserPathArray` instance
|
|
277
273
|
*/
|
|
278
|
-
function
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
const cosA = Math.cos(angle);
|
|
283
|
-
const sinA2 = sinA * sinA;
|
|
284
|
-
const length = Math.sqrt(x * x + y * y + z * z);
|
|
285
|
-
let X = x;
|
|
286
|
-
let Y = y;
|
|
287
|
-
let Z = z;
|
|
274
|
+
function scanSegment(path) {
|
|
275
|
+
const { max, pathValue, index } = path;
|
|
276
|
+
const cmdCode = pathValue.charCodeAt(index);
|
|
277
|
+
const reqParams = paramsCount[pathValue[index].toLowerCase()];
|
|
288
278
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
} else {
|
|
295
|
-
X /= length;
|
|
296
|
-
Y /= length;
|
|
297
|
-
Z /= length;
|
|
279
|
+
path.segmentStart = index;
|
|
280
|
+
|
|
281
|
+
if (!isPathCommand(cmdCode)) {
|
|
282
|
+
path.err = `${invalidPathValue}: ${pathValue[index]} not a path command`;
|
|
283
|
+
return;
|
|
298
284
|
}
|
|
299
285
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const z2 = Z * Z;
|
|
286
|
+
path.index += 1;
|
|
287
|
+
skipSpaces(path);
|
|
303
288
|
|
|
304
|
-
|
|
305
|
-
m.m11 = m11;
|
|
306
|
-
m.a = m11;
|
|
289
|
+
path.data = [];
|
|
307
290
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
291
|
+
if (!reqParams) {
|
|
292
|
+
// Z
|
|
293
|
+
finalizeSegment(path);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
311
296
|
|
|
312
|
-
|
|
297
|
+
for (;;) {
|
|
298
|
+
for (let i = reqParams; i > 0; i -= 1) {
|
|
299
|
+
if (isArcCommand(cmdCode) && (i === 3 || i === 4)) scanFlag(path);
|
|
300
|
+
else scanParam(path);
|
|
313
301
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
302
|
+
if (path.err.length) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
path.data.push(path.param);
|
|
317
306
|
|
|
318
|
-
|
|
319
|
-
m.m22 = m22;
|
|
320
|
-
m.d = m22;
|
|
307
|
+
skipSpaces(path);
|
|
321
308
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
309
|
+
// after ',' param is mandatory
|
|
310
|
+
if (path.index < max && pathValue.charCodeAt(path.index) === 0x2C/* , */) {
|
|
311
|
+
path.index += 1;
|
|
312
|
+
skipSpaces(path);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
326
315
|
|
|
327
|
-
|
|
316
|
+
if (path.index >= path.max) {
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Stop on next segment
|
|
321
|
+
if (!isDigitStart(pathValue.charCodeAt(path.index))) {
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
finalizeSegment(path);
|
|
328
327
|
}
|
|
329
328
|
|
|
329
|
+
// @ts-nocheck
|
|
330
330
|
/**
|
|
331
|
-
*
|
|
332
|
-
* This method is equivalent to the CSS `scale3d()` function.
|
|
331
|
+
* Returns a clone of an existing `pathArray`.
|
|
333
332
|
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
* @param {Number} x the `x-axis` scale.
|
|
337
|
-
* @param {Number} y the `y-axis` scale.
|
|
338
|
-
* @param {Number} z the `z-axis` scale.
|
|
339
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
333
|
+
* @param {SVGPC.pathArray | string[]} path the source `pathArray`
|
|
334
|
+
* @returns {SVGPC.pathArray} the cloned `pathArray`
|
|
340
335
|
*/
|
|
341
|
-
function
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
m.m33 = z;
|
|
350
|
-
return m;
|
|
336
|
+
function clonePath(path) {
|
|
337
|
+
return path.map((x) => {
|
|
338
|
+
if (Array.isArray(x)) {
|
|
339
|
+
return clonePath(x);
|
|
340
|
+
}
|
|
341
|
+
return !Number.isNaN(+x) ? +x : x;
|
|
342
|
+
});
|
|
351
343
|
}
|
|
352
344
|
|
|
353
345
|
/**
|
|
354
|
-
*
|
|
355
|
-
* returns it. This method is equivalent to the CSS `skewX()` function.
|
|
346
|
+
* The `parserPathArray` used by the parser.
|
|
356
347
|
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* @param {Number} angle the angle in degrees.
|
|
360
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
348
|
+
* @param {string} pathString
|
|
361
349
|
*/
|
|
362
|
-
function
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
350
|
+
function SVGPathArray(pathString) {
|
|
351
|
+
/** @type {[string, ...number[]][]} */
|
|
352
|
+
this.segments = [];
|
|
353
|
+
/** @type {string} */
|
|
354
|
+
this.pathValue = pathString;
|
|
355
|
+
/** @type {number} */
|
|
356
|
+
this.max = pathString.length;
|
|
357
|
+
/** @type {number} */
|
|
358
|
+
this.index = 0;
|
|
359
|
+
/** @type {number} */
|
|
360
|
+
this.param = 0.0;
|
|
361
|
+
/** @type {number} */
|
|
362
|
+
this.segmentStart = 0;
|
|
363
|
+
/** @type {any} */
|
|
364
|
+
this.data = [];
|
|
365
|
+
/** @type {string} */
|
|
366
|
+
this.err = '';
|
|
369
367
|
}
|
|
370
368
|
|
|
371
369
|
/**
|
|
372
|
-
*
|
|
373
|
-
* returns it. This method is equivalent to the CSS `skewY()` function.
|
|
374
|
-
*
|
|
375
|
-
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skewY
|
|
370
|
+
* Iterates an array to check if it's an actual `pathArray`.
|
|
376
371
|
*
|
|
377
|
-
* @param {
|
|
378
|
-
* @
|
|
372
|
+
* @param {string | SVGPC.pathArray} path the `pathArray` to be checked
|
|
373
|
+
* @returns {boolean} iteration result
|
|
379
374
|
*/
|
|
380
|
-
function
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
m.b = t;
|
|
386
|
-
return m;
|
|
375
|
+
function isPathArray(path) {
|
|
376
|
+
return Array.isArray(path) && path.every((seg) => {
|
|
377
|
+
const lk = seg[0].toLowerCase();
|
|
378
|
+
return paramsCount[lk] === seg.length - 1 && /[achlmqstvz]/gi.test(lk);
|
|
379
|
+
});
|
|
387
380
|
}
|
|
388
381
|
|
|
389
382
|
/**
|
|
390
|
-
*
|
|
391
|
-
*
|
|
383
|
+
* Parses a path string value and returns an array
|
|
384
|
+
* of segments we like to call `pathArray`.
|
|
392
385
|
*
|
|
393
|
-
* @param {
|
|
394
|
-
* @
|
|
395
|
-
* @return {CSSMatrix} the resulted matrix.
|
|
386
|
+
* @param {string | SVGPC.pathArray} pathInput the string to be parsed
|
|
387
|
+
* @returns {SVGPC.pathArray} the resulted `pathArray`
|
|
396
388
|
*/
|
|
397
|
-
function
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
389
|
+
function parsePathString(pathInput) {
|
|
390
|
+
if (isPathArray(pathInput)) {
|
|
391
|
+
// @ts-ignore
|
|
392
|
+
return clonePath(pathInput);
|
|
393
|
+
}
|
|
394
|
+
// @ts-ignore
|
|
395
|
+
const path = new SVGPathArray(pathInput);
|
|
402
396
|
|
|
403
|
-
|
|
404
|
-
const m22 = m2.m21 * m1.m12 + m2.m22 * m1.m22 + m2.m23 * m1.m32 + m2.m24 * m1.m42;
|
|
405
|
-
const m23 = m2.m21 * m1.m13 + m2.m22 * m1.m23 + m2.m23 * m1.m33 + m2.m24 * m1.m43;
|
|
406
|
-
const m24 = m2.m21 * m1.m14 + m2.m22 * m1.m24 + m2.m23 * m1.m34 + m2.m24 * m1.m44;
|
|
397
|
+
skipSpaces(path);
|
|
407
398
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const m34 = m2.m31 * m1.m14 + m2.m32 * m1.m24 + m2.m33 * m1.m34 + m2.m34 * m1.m44;
|
|
399
|
+
while (path.index < path.max && !path.err.length) {
|
|
400
|
+
scanSegment(path);
|
|
401
|
+
}
|
|
412
402
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
403
|
+
if (path.err.length) {
|
|
404
|
+
path.segments = [];
|
|
405
|
+
} else if (path.segments.length) {
|
|
406
|
+
if ('mM'.indexOf(path.segments[0][0]) < 0) {
|
|
407
|
+
path.err = `${invalidPathValue}: missing M/m`;
|
|
408
|
+
path.segments = [];
|
|
409
|
+
} else {
|
|
410
|
+
path.segments[0][0] = 'M';
|
|
411
|
+
}
|
|
412
|
+
}
|
|
417
413
|
|
|
418
|
-
return
|
|
419
|
-
[m11, m12, m13, m14,
|
|
420
|
-
m21, m22, m23, m24,
|
|
421
|
-
m31, m32, m33, m34,
|
|
422
|
-
m41, m42, m43, m44],
|
|
423
|
-
);
|
|
414
|
+
return path.segments;
|
|
424
415
|
}
|
|
425
416
|
|
|
426
417
|
/**
|
|
427
|
-
*
|
|
428
|
-
* with
|
|
418
|
+
* Iterates an array to check if it's a `pathArray`
|
|
419
|
+
* with all absolute values.
|
|
429
420
|
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
421
|
+
* @param {string | SVGPC.pathArray} path the `pathArray` to be checked
|
|
422
|
+
* @returns {boolean} iteration result
|
|
432
423
|
*/
|
|
424
|
+
function isAbsoluteArray(path) {
|
|
425
|
+
return Array.isArray(path) && isPathArray(path)
|
|
426
|
+
&& path.every((x) => x[0] === x[0].toUpperCase());
|
|
427
|
+
}
|
|
433
428
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
// array 6
|
|
446
|
-
m.a = 1; m.b = 0;
|
|
447
|
-
m.c = 0; m.d = 1;
|
|
448
|
-
m.e = 0; m.f = 0;
|
|
449
|
-
// array 16
|
|
450
|
-
m.m11 = 1; m.m12 = 0; m.m13 = 0; m.m14 = 0;
|
|
451
|
-
m.m21 = 0; m.m22 = 1; m.m23 = 0; m.m24 = 0;
|
|
452
|
-
m.m31 = 0; m.m32 = 0; m.m33 = 1; m.m34 = 0;
|
|
453
|
-
m.m41 = 0; m.m42 = 0; m.m43 = 0; m.m44 = 1;
|
|
454
|
-
|
|
455
|
-
if (args && args.length) {
|
|
456
|
-
let ARGS = args;
|
|
429
|
+
/**
|
|
430
|
+
* Parses a path string value or object and returns an array
|
|
431
|
+
* of segments, all converted to absolute values.
|
|
432
|
+
*
|
|
433
|
+
* @param {string | SVGPC.pathArray} pathInput the path string | object
|
|
434
|
+
* @returns {SVGPC.pathArray} the resulted `pathArray` with absolute values
|
|
435
|
+
*/
|
|
436
|
+
function pathToAbsolute(pathInput) {
|
|
437
|
+
if (Array.isArray(pathInput) && isAbsoluteArray(pathInput)) {
|
|
438
|
+
return clonePath(pathInput);
|
|
439
|
+
}
|
|
457
440
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return m;
|
|
468
|
-
}
|
|
441
|
+
const path = parsePathString(pathInput);
|
|
442
|
+
const ii = path.length;
|
|
443
|
+
/** @type {SVGPC.pathArray} */
|
|
444
|
+
const resultArray = [];
|
|
445
|
+
let x = 0;
|
|
446
|
+
let y = 0;
|
|
447
|
+
let mx = 0;
|
|
448
|
+
let my = 0;
|
|
449
|
+
let start = 0;
|
|
469
450
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
451
|
+
if (path[0][0] === 'M') {
|
|
452
|
+
x = +path[0][1];
|
|
453
|
+
y = +path[0][2];
|
|
454
|
+
mx = x;
|
|
455
|
+
my = y;
|
|
456
|
+
start += 1;
|
|
457
|
+
resultArray.push(['M', x, y]);
|
|
477
458
|
}
|
|
478
459
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
*/
|
|
486
|
-
get isIdentity() {
|
|
487
|
-
const m = this;
|
|
488
|
-
return (m.m11 === 1 && m.m12 === 0 && m.m13 === 0 && m.m14 === 0
|
|
489
|
-
&& m.m21 === 0 && m.m22 === 1 && m.m23 === 0 && m.m24 === 0
|
|
490
|
-
&& m.m31 === 0 && m.m32 === 0 && m.m33 === 1 && m.m34 === 0
|
|
491
|
-
&& m.m41 === 0 && m.m42 === 0 && m.m43 === 0 && m.m44 === 1);
|
|
492
|
-
}
|
|
460
|
+
for (let i = start; i < ii; i += 1) {
|
|
461
|
+
const segment = path[i];
|
|
462
|
+
const [pathCommand] = segment;
|
|
463
|
+
const absCommand = pathCommand.toUpperCase();
|
|
464
|
+
const absoluteSegment = [];
|
|
465
|
+
let newSeg = [];
|
|
493
466
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
* and `false` if the matrix is 3D.
|
|
497
|
-
*
|
|
498
|
-
* @return {Boolean} the current property value
|
|
499
|
-
*/
|
|
500
|
-
get is2D() {
|
|
501
|
-
const m = this;
|
|
502
|
-
return (m.m31 === 0 && m.m32 === 0 && m.m33 === 1 && m.m34 === 0 && m.m43 === 0 && m.m44 === 1);
|
|
503
|
-
}
|
|
467
|
+
if (pathCommand !== absCommand) {
|
|
468
|
+
absoluteSegment[0] = absCommand;
|
|
504
469
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
470
|
+
switch (absCommand) {
|
|
471
|
+
case 'A':
|
|
472
|
+
newSeg = segment.slice(1, -2).concat([+segment[6] + x, +segment[7] + y]);
|
|
473
|
+
for (let j = 0; j < newSeg.length; j += 1) {
|
|
474
|
+
absoluteSegment.push(newSeg[j]);
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
case 'V':
|
|
478
|
+
absoluteSegment[1] = +segment[1] + y;
|
|
479
|
+
break;
|
|
480
|
+
case 'H':
|
|
481
|
+
absoluteSegment[1] = +segment[1] + x;
|
|
482
|
+
break;
|
|
483
|
+
default:
|
|
484
|
+
if (absCommand === 'M') {
|
|
485
|
+
mx = +segment[1] + x;
|
|
486
|
+
my = +segment[2] + y;
|
|
487
|
+
}
|
|
488
|
+
// for is here to stay for eslint
|
|
489
|
+
for (let j = 1; j < segment.length; j += 1) {
|
|
490
|
+
absoluteSegment.push(+segment[j] + (j % 2 ? x : y));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
} else {
|
|
494
|
+
for (let j = 0; j < segment.length; j += 1) {
|
|
495
|
+
absoluteSegment.push(segment[j]);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// @ts-ignore
|
|
499
|
+
resultArray.push(absoluteSegment);
|
|
513
500
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
* * 6/16 elements *Array*,
|
|
530
|
-
* * CSSMatrix | DOMMatrix instance.
|
|
531
|
-
*/
|
|
532
|
-
setMatrixValue(source) {
|
|
533
|
-
const m = this;
|
|
501
|
+
const segLength = absoluteSegment.length;
|
|
502
|
+
switch (absCommand) {
|
|
503
|
+
case 'Z':
|
|
504
|
+
x = mx;
|
|
505
|
+
y = my;
|
|
506
|
+
break;
|
|
507
|
+
case 'H':
|
|
508
|
+
x = +absoluteSegment[1];
|
|
509
|
+
break;
|
|
510
|
+
case 'V':
|
|
511
|
+
y = +absoluteSegment[1];
|
|
512
|
+
break;
|
|
513
|
+
default:
|
|
514
|
+
x = +absoluteSegment[segLength - 2];
|
|
515
|
+
y = +absoluteSegment[segLength - 1];
|
|
534
516
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
// CSS transform string source
|
|
540
|
-
} if (typeof source === 'string' && source.length && source !== 'none') {
|
|
541
|
-
return fromString(source);
|
|
542
|
-
// [Arguments list | Array] come here
|
|
543
|
-
} if (Array.isArray(source)) {
|
|
544
|
-
// @ts-ignore
|
|
545
|
-
return fromArray(source);
|
|
517
|
+
if (absCommand === 'M') {
|
|
518
|
+
mx = x;
|
|
519
|
+
my = y;
|
|
520
|
+
}
|
|
546
521
|
}
|
|
547
|
-
return m;
|
|
548
522
|
}
|
|
549
523
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
* using the appropriate `CSS` matrix notation.
|
|
553
|
-
*
|
|
554
|
-
* The 16 items in the array 3D matrix array are *transposed* in row-major order.
|
|
555
|
-
*
|
|
556
|
-
* matrix3d *matrix3d(m11, m12, m13, m14, m21, ...)*
|
|
557
|
-
* matrix *matrix(a, b, c, d, e, f)*
|
|
558
|
-
*
|
|
559
|
-
* @return {String} a string representation of the matrix
|
|
560
|
-
*/
|
|
561
|
-
toString() {
|
|
562
|
-
const m = this;
|
|
563
|
-
const values = m.toArray().join(',');
|
|
564
|
-
const type = m.is2D ? 'matrix' : 'matrix3d';
|
|
565
|
-
return `${type}(${values})`;
|
|
566
|
-
}
|
|
524
|
+
return resultArray;
|
|
525
|
+
}
|
|
567
526
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
let result;
|
|
527
|
+
/**
|
|
528
|
+
* Iterates an array to check if it's a `pathArray`
|
|
529
|
+
* with relative values.
|
|
530
|
+
*
|
|
531
|
+
* @param {string | SVGPC.pathArray} path the `pathArray` to be checked
|
|
532
|
+
* @returns {boolean} iteration result
|
|
533
|
+
*/
|
|
534
|
+
function isRelativeArray(path) {
|
|
535
|
+
return Array.isArray(path) && isPathArray(path)
|
|
536
|
+
&& path.slice(1).every((seg) => seg[0] === seg[0].toLowerCase());
|
|
537
|
+
}
|
|
580
538
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
return result.map((n) => (Math.abs(n) < 1e-6 ? 0 : ((n * pow6) >> 0) / pow6));
|
|
539
|
+
/**
|
|
540
|
+
* Parses a path string value or object and returns an array
|
|
541
|
+
* of segments, all converted to relative values.
|
|
542
|
+
*
|
|
543
|
+
* @param {string | SVGPC.pathArray} pathInput the path string | object
|
|
544
|
+
* @returns {SVGPC.pathArray} the resulted `pathArray` with relative values
|
|
545
|
+
*/
|
|
546
|
+
function pathToRelative(pathInput) {
|
|
547
|
+
if (Array.isArray(pathInput) && isRelativeArray(pathInput)) {
|
|
548
|
+
return clonePath(pathInput);
|
|
592
549
|
}
|
|
593
550
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
*/
|
|
604
|
-
toJSON() {
|
|
605
|
-
return JSON.parse(JSON.stringify(this));
|
|
606
|
-
}
|
|
551
|
+
const path = parsePathString(pathInput);
|
|
552
|
+
const ii = path.length;
|
|
553
|
+
/** @type {SVGPC.pathArray} */
|
|
554
|
+
const resultArray = [];
|
|
555
|
+
let x = 0;
|
|
556
|
+
let y = 0;
|
|
557
|
+
let mx = 0;
|
|
558
|
+
let my = 0;
|
|
559
|
+
let start = 0;
|
|
607
560
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
*/
|
|
616
|
-
multiply(m2) {
|
|
617
|
-
return Multiply(this, m2);
|
|
561
|
+
if (path[0][0] === 'M') {
|
|
562
|
+
x = +path[0][1];
|
|
563
|
+
y = +path[0][2];
|
|
564
|
+
mx = x;
|
|
565
|
+
my = y;
|
|
566
|
+
start += 1;
|
|
567
|
+
resultArray.push(['M', x, y]);
|
|
618
568
|
}
|
|
619
569
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
* @param {number} x X component of the translation value.
|
|
627
|
-
* @param {number} y Y component of the translation value.
|
|
628
|
-
* @param {number} z Z component of the translation value.
|
|
629
|
-
* @return {CSSMatrix} The result matrix
|
|
630
|
-
*/
|
|
631
|
-
translate(x, y, z) {
|
|
632
|
-
const X = x;
|
|
633
|
-
let Y = y;
|
|
634
|
-
let Z = z;
|
|
635
|
-
if (Z == null) Z = 0;
|
|
636
|
-
if (Y == null) Y = 0;
|
|
637
|
-
return Multiply(this, Translate(X, Y, Z));
|
|
638
|
-
}
|
|
570
|
+
for (let i = start; i < ii; i += 1) {
|
|
571
|
+
const segment = path[i];
|
|
572
|
+
const [pathCommand] = segment;
|
|
573
|
+
const relativeCommand = pathCommand.toLowerCase();
|
|
574
|
+
const relativeSegment = []; // this a test to please TS
|
|
575
|
+
let newSeg = [];
|
|
639
576
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
*
|
|
646
|
-
* @param {number} x The X component of the scale value.
|
|
647
|
-
* @param {number} y The Y component of the scale value.
|
|
648
|
-
* @param {number} z The Z component of the scale value.
|
|
649
|
-
* @return {CSSMatrix} The result matrix
|
|
650
|
-
*/
|
|
651
|
-
scale(x, y, z) {
|
|
652
|
-
const X = x;
|
|
653
|
-
let Y = y;
|
|
654
|
-
let Z = z;
|
|
655
|
-
if (Y == null) Y = x;
|
|
656
|
-
if (Z == null) Z = x;
|
|
577
|
+
if (pathCommand !== relativeCommand) {
|
|
578
|
+
relativeSegment[0] = relativeCommand;
|
|
579
|
+
switch (relativeCommand) {
|
|
580
|
+
case 'a':
|
|
581
|
+
newSeg = segment.slice(1, -2).concat([+segment[6] - x, +segment[7] - y]);
|
|
657
582
|
|
|
658
|
-
|
|
583
|
+
for (let j = 0; j < newSeg.length; j += 1) {
|
|
584
|
+
relativeSegment.push(newSeg[j]);
|
|
585
|
+
}
|
|
586
|
+
break;
|
|
587
|
+
case 'v':
|
|
588
|
+
relativeSegment[1] = +segment[1] - y;
|
|
589
|
+
break;
|
|
590
|
+
default:
|
|
591
|
+
// for is here to stay for eslint
|
|
592
|
+
for (let j = 1; j < segment.length; j += 1) {
|
|
593
|
+
relativeSegment.push(+segment[j] - (j % 2 ? x : y));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (relativeCommand === 'm') {
|
|
597
|
+
mx = +segment[1];
|
|
598
|
+
my = +segment[2];
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
} else {
|
|
602
|
+
if (pathCommand === 'm') {
|
|
603
|
+
mx = +segment[1] + x;
|
|
604
|
+
my = +segment[2] + y;
|
|
605
|
+
}
|
|
606
|
+
for (let j = 0; j < segment.length; j += 1) {
|
|
607
|
+
relativeSegment.push(segment[j]);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// @ts-ignore
|
|
611
|
+
resultArray.push(relativeSegment);
|
|
612
|
+
|
|
613
|
+
const segLength = relativeSegment.length;
|
|
614
|
+
switch (relativeSegment[0]) {
|
|
615
|
+
case 'z':
|
|
616
|
+
x = mx;
|
|
617
|
+
y = my;
|
|
618
|
+
break;
|
|
619
|
+
case 'h':
|
|
620
|
+
x += +relativeSegment[segLength - 1];
|
|
621
|
+
break;
|
|
622
|
+
case 'v':
|
|
623
|
+
y += +relativeSegment[segLength - 1];
|
|
624
|
+
break;
|
|
625
|
+
default:
|
|
626
|
+
x += +resultArray[i][segLength - 2];
|
|
627
|
+
y += +resultArray[i][segLength - 1];
|
|
628
|
+
}
|
|
659
629
|
}
|
|
660
630
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
631
|
+
return resultArray;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Rounds the values of a `pathArray` instance to
|
|
636
|
+
* a specified amount of decimals and returns it.
|
|
637
|
+
*
|
|
638
|
+
* @param {SVGPC.pathArray} path the source `pathArray`
|
|
639
|
+
* @param {null | number} round the amount of decimals to round numbers to
|
|
640
|
+
* @returns {SVGPC.pathArray} the resulted `pathArray` with rounded values
|
|
641
|
+
*/
|
|
642
|
+
function roundPath(path, round) {
|
|
643
|
+
const { round: defaultRound, decimals: defaultDecimals } = SVGPCO;
|
|
644
|
+
const decimalsOption = round && !Number.isNaN(+round) ? +round
|
|
645
|
+
: defaultRound && defaultDecimals;
|
|
646
|
+
|
|
647
|
+
if (!decimalsOption) return clonePath(path);
|
|
648
|
+
|
|
649
|
+
// @ts-ignore
|
|
650
|
+
return path.map((seg) => seg.map((c) => {
|
|
651
|
+
const nr = +c;
|
|
652
|
+
const dc = 10 ** decimalsOption;
|
|
653
|
+
if (!Number.isNaN(nr)) {
|
|
654
|
+
return nr % 1 === 0 ? nr : Math.round(nr * dc) / dc;
|
|
655
|
+
}
|
|
656
|
+
return c;
|
|
657
|
+
}));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Returns a valid `d` attribute string value created
|
|
662
|
+
* by rounding values and concatenating the `pathArray` segments.
|
|
663
|
+
*
|
|
664
|
+
* @param {SVGPC.pathArray} path the `pathArray` object
|
|
665
|
+
* @param {number | null} round amount of decimals to round values to
|
|
666
|
+
* @returns {string} the concatenated path string
|
|
667
|
+
*/
|
|
668
|
+
function pathToString(path, round) {
|
|
669
|
+
return roundPath(path, round)
|
|
670
|
+
.map((x) => x[0].concat(x.slice(1).join(' '))).join('');
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Returns the missing control point from an
|
|
675
|
+
* T (shorthand quadratic bezier) segment.
|
|
676
|
+
*
|
|
677
|
+
* @param {number} x1 curve start x
|
|
678
|
+
* @param {number} y1 curve start y
|
|
679
|
+
* @param {number} qx control point x
|
|
680
|
+
* @param {number} qy control point y
|
|
681
|
+
* @param {string} prevCommand the previous path command
|
|
682
|
+
* @returns {{qx: number, qy: number}}} the missing control point
|
|
683
|
+
*/
|
|
684
|
+
function shorthandToQuad(x1, y1, qx, qy, prevCommand) {
|
|
685
|
+
return 'QT'.indexOf(prevCommand) > -1
|
|
686
|
+
? { qx: x1 * 2 - qx, qy: y1 * 2 - qy }
|
|
687
|
+
: { qx: x1, qy: y1 };
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Returns the missing control point from an
|
|
692
|
+
* S (shorthand cubic bezier) segment.
|
|
693
|
+
*
|
|
694
|
+
* @param {number} x1 curve start x
|
|
695
|
+
* @param {number} y1 curve start y
|
|
696
|
+
* @param {number} x2 curve end x
|
|
697
|
+
* @param {number} y2 curve end y
|
|
698
|
+
* @param {string} prevCommand the previous path command
|
|
699
|
+
* @returns {{x1: number, y1: number}}} the missing control point
|
|
700
|
+
*/
|
|
701
|
+
function shorthandToCubic(x1, y1, x2, y2, prevCommand) {
|
|
702
|
+
return 'CS'.indexOf(prevCommand) > -1
|
|
703
|
+
? { x1: x1 * 2 - x2, y1: y1 * 2 - y2 }
|
|
704
|
+
: { x1, y1 };
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Normalizes a single segment of a `pathArray` object.
|
|
709
|
+
*
|
|
710
|
+
* @param {SVGPC.pathSegment} segment the segment object
|
|
711
|
+
* @param {any} params the coordinates of the previous segment
|
|
712
|
+
* @param {String} prevCommand the path command of the previous segment
|
|
713
|
+
* @returns {any} the normalized segment
|
|
714
|
+
*/
|
|
715
|
+
function normalizeSegment(segment, params, prevCommand) {
|
|
716
|
+
const [pathCommand] = segment;
|
|
717
|
+
const xy = segment.slice(1);
|
|
718
|
+
let result = segment.slice();
|
|
719
|
+
|
|
720
|
+
if ('TQ'.indexOf(segment[0]) < 0) {
|
|
721
|
+
// optional but good to be cautious
|
|
722
|
+
params.qx = null;
|
|
723
|
+
params.qy = null;
|
|
680
724
|
}
|
|
681
725
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
726
|
+
if (pathCommand === 'H') {
|
|
727
|
+
result = ['L', segment[1], params.y1];
|
|
728
|
+
} else if (pathCommand === 'V') {
|
|
729
|
+
result = ['L', params.x1, segment[1]];
|
|
730
|
+
} else if (pathCommand === 'S') {
|
|
731
|
+
const { x1, y1 } = shorthandToCubic(params.x1, params.y1, params.x2, params.y2, prevCommand);
|
|
732
|
+
params.x1 = x1;
|
|
733
|
+
params.y1 = y1;
|
|
734
|
+
result = ['C', x1, y1].concat(xy);
|
|
735
|
+
} else if (pathCommand === 'T') {
|
|
736
|
+
const { qx, qy } = shorthandToQuad(params.x1, params.y1, params.qx, params.qy, prevCommand);
|
|
737
|
+
params.qx = qx;
|
|
738
|
+
params.qy = qy;
|
|
739
|
+
result = ['Q', qx, qy].concat(xy);
|
|
740
|
+
} else if (pathCommand === 'Q') {
|
|
741
|
+
const [nqx, nqy] = xy;
|
|
742
|
+
params.qx = nqx;
|
|
743
|
+
params.qy = nqy;
|
|
744
|
+
}
|
|
745
|
+
return result;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Iterates an array to check if it's a `pathArray`
|
|
750
|
+
* with all segments are in non-shorthand notation
|
|
751
|
+
* with absolute values.
|
|
752
|
+
*
|
|
753
|
+
* @param {string | SVGPC.pathArray} path the `pathArray` to be checked
|
|
754
|
+
* @returns {boolean} iteration result
|
|
755
|
+
*/
|
|
756
|
+
function isNormalizedArray(path) {
|
|
757
|
+
return Array.isArray(path) && isPathArray(path) && path.every((seg) => {
|
|
758
|
+
const lk = seg[0].toLowerCase();
|
|
759
|
+
return paramsCount[lk] === seg.length - 1 && ('ACLMQZ').includes(seg[0]); // achlmqstvz
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Normalizes a `path` object for further processing:
|
|
765
|
+
* * convert segments to absolute values
|
|
766
|
+
* * convert shorthand path commands to their non-shorthand notation
|
|
767
|
+
*
|
|
768
|
+
* @param {string | SVGPC.pathArray} pathInput the string to be parsed or 'pathArray'
|
|
769
|
+
* @returns {SVGPC.pathArray} the normalized `pathArray`
|
|
770
|
+
*/
|
|
771
|
+
function normalizePath(pathInput) { // path|pathString
|
|
772
|
+
if (Array.isArray(pathInput) && isNormalizedArray(pathInput)) {
|
|
773
|
+
return clonePath(pathInput);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const path = pathToAbsolute(pathInput);
|
|
777
|
+
const params = {
|
|
778
|
+
x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null,
|
|
779
|
+
};
|
|
780
|
+
const allPathCommands = [];
|
|
781
|
+
const ii = path.length;
|
|
782
|
+
let prevCommand = '';
|
|
783
|
+
let segment;
|
|
784
|
+
let seglen;
|
|
785
|
+
|
|
786
|
+
for (let i = 0; i < ii; i += 1) {
|
|
787
|
+
// save current path command
|
|
788
|
+
const [pathCommand] = path[i];
|
|
789
|
+
|
|
790
|
+
// Save current path command
|
|
791
|
+
allPathCommands[i] = pathCommand;
|
|
792
|
+
// Get previous path command
|
|
793
|
+
if (i) prevCommand = allPathCommands[i - 1];
|
|
794
|
+
// Previous path command is inputted to processSegment
|
|
795
|
+
path[i] = normalizeSegment(path[i], params, prevCommand);
|
|
796
|
+
|
|
797
|
+
segment = path[i];
|
|
798
|
+
seglen = segment.length;
|
|
799
|
+
|
|
800
|
+
params.x1 = +segment[seglen - 2];
|
|
801
|
+
params.y1 = +segment[seglen - 1];
|
|
802
|
+
params.x2 = +(segment[seglen - 4]) || params.x1;
|
|
803
|
+
params.y2 = +(segment[seglen - 3]) || params.y1;
|
|
804
|
+
}
|
|
805
|
+
return path;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Reverses all segments and their values of a `pathArray`
|
|
810
|
+
* and returns a new instance.
|
|
811
|
+
*
|
|
812
|
+
* @param {SVGPC.pathArray} pathInput the source `pathArray`
|
|
813
|
+
* @returns {SVGPC.pathArray} the reversed `pathArray`
|
|
814
|
+
*/
|
|
815
|
+
function reversePath(pathInput) {
|
|
816
|
+
const absolutePath = pathToAbsolute(pathInput);
|
|
817
|
+
const isClosed = absolutePath.slice(-1)[0][0] === 'Z';
|
|
818
|
+
let reversedPath = [];
|
|
819
|
+
let segLength = 0;
|
|
820
|
+
|
|
821
|
+
reversedPath = normalizePath(absolutePath).map((segment, i) => {
|
|
822
|
+
segLength = segment.length;
|
|
823
|
+
return {
|
|
824
|
+
seg: absolutePath[i], // absolute
|
|
825
|
+
n: segment, // normalized
|
|
826
|
+
c: absolutePath[i][0], // pathCommand
|
|
827
|
+
x: segment[segLength - 2], // x
|
|
828
|
+
y: segment[segLength - 1], // y
|
|
829
|
+
};
|
|
830
|
+
}).map((seg, i, path) => {
|
|
831
|
+
const segment = seg.seg;
|
|
832
|
+
const data = seg.n;
|
|
833
|
+
const prevSeg = i && path[i - 1];
|
|
834
|
+
const nextSeg = path[i + 1] && path[i + 1];
|
|
835
|
+
const pathCommand = seg.c;
|
|
836
|
+
const pLen = path.length;
|
|
837
|
+
const x = i ? path[i - 1].x : path[pLen - 1].x;
|
|
838
|
+
const y = i ? path[i - 1].y : path[pLen - 1].y;
|
|
839
|
+
let result = [];
|
|
840
|
+
|
|
841
|
+
switch (pathCommand) {
|
|
842
|
+
case 'M':
|
|
843
|
+
result = isClosed ? ['Z'] : [pathCommand, x, y];
|
|
844
|
+
break;
|
|
845
|
+
case 'A':
|
|
846
|
+
result = segment.slice(0, -3).concat([(segment[5] === 1 ? 0 : 1), x, y]);
|
|
847
|
+
break;
|
|
848
|
+
case 'C':
|
|
849
|
+
if (nextSeg && nextSeg.c === 'S') {
|
|
850
|
+
result = ['S', segment[1], segment[2], x, y];
|
|
851
|
+
} else {
|
|
852
|
+
result = [pathCommand, segment[3], segment[4], segment[1], segment[2], x, y];
|
|
853
|
+
}
|
|
854
|
+
break;
|
|
855
|
+
case 'S':
|
|
856
|
+
if ((prevSeg && 'CS'.indexOf(prevSeg.c) > -1) && (!nextSeg || (nextSeg && nextSeg.c !== 'S'))) {
|
|
857
|
+
result = ['C', data[3], data[4], data[1], data[2], x, y];
|
|
858
|
+
} else {
|
|
859
|
+
result = [pathCommand, data[1], data[2], x, y];
|
|
860
|
+
}
|
|
861
|
+
break;
|
|
862
|
+
case 'Q':
|
|
863
|
+
if (nextSeg && nextSeg.c === 'T') {
|
|
864
|
+
result = ['T', x, y];
|
|
865
|
+
} else {
|
|
866
|
+
result = segment.slice(0, -2).concat([x, y]);
|
|
867
|
+
}
|
|
868
|
+
break;
|
|
869
|
+
case 'T':
|
|
870
|
+
if ((prevSeg && 'QT'.indexOf(prevSeg.c) > -1) && (!nextSeg || (nextSeg && nextSeg.c !== 'T'))) {
|
|
871
|
+
result = ['Q', data[1], data[2], x, y];
|
|
872
|
+
} else {
|
|
873
|
+
result = [pathCommand, x, y];
|
|
874
|
+
}
|
|
875
|
+
break;
|
|
876
|
+
case 'Z':
|
|
877
|
+
result = ['M', x, y];
|
|
878
|
+
break;
|
|
879
|
+
case 'H':
|
|
880
|
+
result = [pathCommand, x];
|
|
881
|
+
break;
|
|
882
|
+
case 'V':
|
|
883
|
+
result = [pathCommand, y];
|
|
884
|
+
break;
|
|
885
|
+
default:
|
|
886
|
+
result = segment.slice(0, -2).concat([x, y]);
|
|
697
887
|
}
|
|
698
|
-
|
|
888
|
+
|
|
889
|
+
return result;
|
|
890
|
+
});
|
|
891
|
+
// @ts-ignore
|
|
892
|
+
return isClosed ? reversedPath.reverse()
|
|
893
|
+
: [reversedPath[0]].concat(reversedPath.slice(1).reverse());
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Split a path into an `Array` of sub-path strings.
|
|
898
|
+
*
|
|
899
|
+
* In the process, values are converted to absolute
|
|
900
|
+
* for visual consistency.
|
|
901
|
+
*
|
|
902
|
+
* @param {SVGPC.pathArray | string} pathInput the cubic-bezier parameters
|
|
903
|
+
* @return {string[]} an array with all sub-path strings
|
|
904
|
+
*/
|
|
905
|
+
function splitPath(pathInput) {
|
|
906
|
+
return pathToString(pathToAbsolute(pathInput), 0)
|
|
907
|
+
.replace(/(m|M)/g, '|$1')
|
|
908
|
+
.split('|')
|
|
909
|
+
.map((s) => s.trim())
|
|
910
|
+
.filter((s) => s);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Optimizes a `pathArray` object:
|
|
915
|
+
* * convert segments to absolute and relative values
|
|
916
|
+
* * create a new `pathArray` with elements with shortest segments
|
|
917
|
+
* from absolute and relative `pathArray`s
|
|
918
|
+
*
|
|
919
|
+
* @param {string | SVGPC.pathArray} pathInput a string or `pathArray`
|
|
920
|
+
* @param {number | null} round the amount of decimals to round values to
|
|
921
|
+
* @returns {SVGPC.pathArray} the optimized `pathArray`
|
|
922
|
+
*/
|
|
923
|
+
function optimizePath(pathInput, round) {
|
|
924
|
+
const absolutePath = roundPath(pathToAbsolute(pathInput), round);
|
|
925
|
+
const relativePath = roundPath(pathToRelative(pathInput), round);
|
|
926
|
+
return absolutePath.map((x, i) => {
|
|
927
|
+
if (i) {
|
|
928
|
+
return x.join('').length < relativePath[i].join('').length ? x : relativePath[i];
|
|
929
|
+
}
|
|
930
|
+
return x;
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* A global namespace for epsilon.
|
|
936
|
+
*
|
|
937
|
+
* @type {Number}
|
|
938
|
+
*/
|
|
939
|
+
const epsilon = 1e-9;
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Returns an {x,y} vector rotated by a given
|
|
943
|
+
* angle in radian.
|
|
944
|
+
*
|
|
945
|
+
* @param {number} x the initial vector x
|
|
946
|
+
* @param {number} y the initial vector y
|
|
947
|
+
* @param {number} rad the radian vector angle
|
|
948
|
+
* @returns {{x: number, y: number}} the rotated vector
|
|
949
|
+
*/
|
|
950
|
+
function rotateVector(x, y, rad) {
|
|
951
|
+
const X = x * Math.cos(rad) - y * Math.sin(rad);
|
|
952
|
+
const Y = x * Math.sin(rad) + y * Math.cos(rad);
|
|
953
|
+
return { x: X, y: Y };
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Converts A (arc-to) segments to C (cubic-bezier-to).
|
|
958
|
+
*
|
|
959
|
+
* For more information of where this math came from visit:
|
|
960
|
+
* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
|
961
|
+
*
|
|
962
|
+
* @param {number} X1 the starting x position
|
|
963
|
+
* @param {number} Y1 the starting y position
|
|
964
|
+
* @param {number} RX x-radius of the arc
|
|
965
|
+
* @param {number} RY y-radius of the arc
|
|
966
|
+
* @param {number} angle x-axis-rotation of the arc
|
|
967
|
+
* @param {number} LAF large-arc-flag of the arc
|
|
968
|
+
* @param {number} SF sweep-flag of the arc
|
|
969
|
+
* @param {number} X2 the ending x position
|
|
970
|
+
* @param {number} Y2 the ending y position
|
|
971
|
+
* @param {number[] | null} recursive the parameters needed to split arc into 2 segments
|
|
972
|
+
* @return {any} the resulting cubic-bezier segment(s)
|
|
973
|
+
*/
|
|
974
|
+
// export default function arcToCubic(x1, y1, rx, ry, angle, LAF, SF, x2, y2, recursive) {
|
|
975
|
+
function arcToCubic(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, recursive) {
|
|
976
|
+
let x1 = X1; let y1 = Y1; let rx = RX; let ry = RY; let x2 = X2; let y2 = Y2;
|
|
977
|
+
// for more information of where this Math came from visit:
|
|
978
|
+
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
|
979
|
+
const d120 = (Math.PI * 120) / 180;
|
|
980
|
+
|
|
981
|
+
const rad = (Math.PI / 180) * (+angle || 0);
|
|
982
|
+
let res = [];
|
|
983
|
+
let xy;
|
|
984
|
+
let f1;
|
|
985
|
+
let f2;
|
|
986
|
+
let cx;
|
|
987
|
+
let cy;
|
|
988
|
+
|
|
989
|
+
if (!recursive) {
|
|
990
|
+
xy = rotateVector(x1, y1, -rad);
|
|
991
|
+
x1 = xy.x;
|
|
992
|
+
y1 = xy.y;
|
|
993
|
+
xy = rotateVector(x2, y2, -rad);
|
|
994
|
+
x2 = xy.x;
|
|
995
|
+
y2 = xy.y;
|
|
996
|
+
|
|
997
|
+
const x = (x1 - x2) / 2;
|
|
998
|
+
const y = (y1 - y2) / 2;
|
|
999
|
+
let h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
|
|
1000
|
+
if (h > 1) {
|
|
1001
|
+
h = Math.sqrt(h);
|
|
1002
|
+
rx *= h;
|
|
1003
|
+
ry *= h;
|
|
1004
|
+
}
|
|
1005
|
+
const rx2 = rx * rx;
|
|
1006
|
+
const ry2 = ry * ry;
|
|
1007
|
+
|
|
1008
|
+
const k = (LAF === SF ? -1 : 1)
|
|
1009
|
+
* Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x)
|
|
1010
|
+
/ (rx2 * y * y + ry2 * x * x)));
|
|
1011
|
+
|
|
1012
|
+
cx = ((k * rx * y) / ry) + ((x1 + x2) / 2);
|
|
1013
|
+
cy = ((k * -ry * x) / rx) + ((y1 + y2) / 2);
|
|
1014
|
+
// eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
|
|
1015
|
+
f1 = (Math.asin((((y1 - cy) / ry))) * (10 ** 9) >> 0) / (10 ** 9);
|
|
1016
|
+
// eslint-disable-next-line no-bitwise -- Impossible to satisfy no-bitwise
|
|
1017
|
+
f2 = (Math.asin((((y2 - cy) / ry))) * (10 ** 9) >> 0) / (10 ** 9);
|
|
1018
|
+
|
|
1019
|
+
f1 = x1 < cx ? Math.PI - f1 : f1;
|
|
1020
|
+
f2 = x2 < cx ? Math.PI - f2 : f2;
|
|
1021
|
+
if (f1 < 0) (f1 = Math.PI * 2 + f1);
|
|
1022
|
+
if (f2 < 0) (f2 = Math.PI * 2 + f2);
|
|
1023
|
+
if (SF && f1 > f2) {
|
|
1024
|
+
f1 -= Math.PI * 2;
|
|
1025
|
+
}
|
|
1026
|
+
if (!SF && f2 > f1) {
|
|
1027
|
+
f2 -= Math.PI * 2;
|
|
1028
|
+
}
|
|
1029
|
+
} else {
|
|
1030
|
+
[f1, f2, cx, cy] = recursive;
|
|
1031
|
+
}
|
|
1032
|
+
let df = f2 - f1;
|
|
1033
|
+
if (Math.abs(df) > d120) {
|
|
1034
|
+
const f2old = f2;
|
|
1035
|
+
const x2old = x2;
|
|
1036
|
+
const y2old = y2;
|
|
1037
|
+
f2 = f1 + d120 * (SF && f2 > f1 ? 1 : -1);
|
|
1038
|
+
x2 = cx + rx * Math.cos(f2);
|
|
1039
|
+
y2 = cy + ry * Math.sin(f2);
|
|
1040
|
+
res = arcToCubic(x2, y2, rx, ry, angle, 0, SF, x2old, y2old, [f2, f2old, cx, cy]);
|
|
1041
|
+
}
|
|
1042
|
+
df = f2 - f1;
|
|
1043
|
+
const c1 = Math.cos(f1);
|
|
1044
|
+
const s1 = Math.sin(f1);
|
|
1045
|
+
const c2 = Math.cos(f2);
|
|
1046
|
+
const s2 = Math.sin(f2);
|
|
1047
|
+
const t = Math.tan(df / 4);
|
|
1048
|
+
const hx = (4 / 3) * rx * t;
|
|
1049
|
+
const hy = (4 / 3) * ry * t;
|
|
1050
|
+
const m1 = [x1, y1];
|
|
1051
|
+
const m2 = [x1 + hx * s1, y1 - hy * c1];
|
|
1052
|
+
const m3 = [x2 + hx * s2, y2 - hy * c2];
|
|
1053
|
+
const m4 = [x2, y2];
|
|
1054
|
+
m2[0] = 2 * m1[0] - m2[0];
|
|
1055
|
+
m2[1] = 2 * m1[1] - m2[1];
|
|
1056
|
+
if (recursive) {
|
|
1057
|
+
return [m2, m3, m4].concat(res);
|
|
699
1058
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
* @return {CSSMatrix} The `CSSMatrix` result
|
|
707
|
-
*/
|
|
708
|
-
skewX(angle) {
|
|
709
|
-
return Multiply(this, SkewX(angle));
|
|
1059
|
+
res = [m2, m3, m4].concat(res).join().split(',');
|
|
1060
|
+
const newres = [];
|
|
1061
|
+
for (let i = 0, ii = res.length; i < ii; i += 1) {
|
|
1062
|
+
newres[i] = i % 2
|
|
1063
|
+
// @ts-ignore
|
|
1064
|
+
? rotateVector(res[i - 1], res[i], rad).y : rotateVector(res[i], res[i + 1], rad).x;
|
|
710
1065
|
}
|
|
1066
|
+
return newres;
|
|
1067
|
+
}
|
|
711
1068
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1069
|
+
/**
|
|
1070
|
+
* Converts a Q (quadratic-bezier) segment to C (cubic-bezier).
|
|
1071
|
+
*
|
|
1072
|
+
* @param {Number} x1 curve start x
|
|
1073
|
+
* @param {Number} y1 curve start y
|
|
1074
|
+
* @param {Number} qx control point x
|
|
1075
|
+
* @param {Number} qy control point y
|
|
1076
|
+
* @param {Number} x2 curve end x
|
|
1077
|
+
* @param {Number} y2 curve end y
|
|
1078
|
+
* @returns {Number[]} the cubic-bezier segment
|
|
1079
|
+
*/
|
|
1080
|
+
function quadToCubic(x1, y1, qx, qy, x2, y2) {
|
|
1081
|
+
const r13 = 1 / 3;
|
|
1082
|
+
const r23 = 2 / 3;
|
|
1083
|
+
return [
|
|
1084
|
+
r13 * x1 + r23 * qx, // cpx1
|
|
1085
|
+
r13 * y1 + r23 * qy, // cpy1
|
|
1086
|
+
r13 * x2 + r23 * qx, // cpx2
|
|
1087
|
+
r13 * y2 + r23 * qy, // cpy2
|
|
1088
|
+
x2, y2, // x,y
|
|
1089
|
+
];
|
|
1090
|
+
}
|
|
722
1091
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1092
|
+
/**
|
|
1093
|
+
* Returns the {x,y} coordinates of a point at a
|
|
1094
|
+
* given length of a cubic-bezier segment.
|
|
1095
|
+
*
|
|
1096
|
+
* @param {number} p1x the starting point X
|
|
1097
|
+
* @param {number} p1y the starting point Y
|
|
1098
|
+
* @param {number} c1x the first control point X
|
|
1099
|
+
* @param {number} c1y the first control point Y
|
|
1100
|
+
* @param {number} c2x the second control point X
|
|
1101
|
+
* @param {number} c2y the second control point Y
|
|
1102
|
+
* @param {number} p2x the ending point X
|
|
1103
|
+
* @param {number} p2y the ending point Y
|
|
1104
|
+
* @param {number} t a [0-1] ratio
|
|
1105
|
+
* @returns {{x: number, y: number}} the requested {x,y} coordinates
|
|
1106
|
+
*/
|
|
1107
|
+
function getPointAtSegLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
|
|
1108
|
+
const t1 = 1 - t;
|
|
1109
|
+
return {
|
|
1110
|
+
x: (t1 ** 3) * p1x
|
|
1111
|
+
+ t1 * t1 * 3 * t * c1x
|
|
1112
|
+
+ t1 * 3 * t * t * c2x
|
|
1113
|
+
+ (t ** 3) * p2x,
|
|
1114
|
+
y: (t1 ** 3) * p1y
|
|
1115
|
+
+ t1 * t1 * 3 * t * c1y
|
|
1116
|
+
+ t1 * 3 * t * t * c2y
|
|
1117
|
+
+ (t ** 3) * p2y,
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
739
1120
|
|
|
740
|
-
|
|
741
|
-
|
|
1121
|
+
/**
|
|
1122
|
+
* Returns the coordinates of a specified distance
|
|
1123
|
+
* ratio between two points.
|
|
1124
|
+
*
|
|
1125
|
+
* @param {Number[]} a the first point coordinates
|
|
1126
|
+
* @param {Number[]} b the second point coordinates
|
|
1127
|
+
* @param {Number} t the ratio
|
|
1128
|
+
* @returns {Number[]} the midpoint coordinates
|
|
1129
|
+
*/
|
|
1130
|
+
function midPoint(a, b, t) {
|
|
1131
|
+
const [ax, ay] = a; const [bx, by] = b;
|
|
1132
|
+
return [ax + (bx - ax) * t, ay + (by - ay) * t];
|
|
1133
|
+
}
|
|
742
1134
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
1135
|
+
/**
|
|
1136
|
+
* Converts an L (line-to) segment to C (cubic-bezier).
|
|
1137
|
+
*
|
|
1138
|
+
* @param {number} x1 line start x
|
|
1139
|
+
* @param {number} y1 line start y
|
|
1140
|
+
* @param {number} x2 line end x
|
|
1141
|
+
* @param {number} y2 line end y
|
|
1142
|
+
* @returns {number[]} the cubic-bezier segment
|
|
1143
|
+
*/
|
|
1144
|
+
function lineToCubic(x1, y1, x2, y2) {
|
|
1145
|
+
const t = 0.5;
|
|
1146
|
+
const p0 = [x1, y1];
|
|
1147
|
+
const p1 = [x2, y2];
|
|
1148
|
+
const p2 = midPoint(p0, p1, t);
|
|
1149
|
+
const p3 = midPoint(p1, p2, t);
|
|
1150
|
+
const p4 = midPoint(p2, p3, t);
|
|
1151
|
+
const p5 = midPoint(p3, p4, t);
|
|
1152
|
+
const p6 = midPoint(p4, p5, t);
|
|
1153
|
+
// @ts-ignore
|
|
1154
|
+
const cp1 = getPointAtSegLength.apply(0, p0.concat(p2, p4, p6, t));
|
|
1155
|
+
// @ts-ignore
|
|
1156
|
+
const cp2 = getPointAtSegLength.apply(0, p6.concat(p5, p3, p1, 0));
|
|
1157
|
+
|
|
1158
|
+
return [cp1.x, cp1.y, cp2.x, cp2.y, x2, y2];
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Converts any segment to C (cubic-bezier).
|
|
1163
|
+
*
|
|
1164
|
+
* @param {SVGPC.pathSegment} segment the source segment
|
|
1165
|
+
* @param {SVGPC.parserParams} params the source segment parameters
|
|
1166
|
+
* @returns {SVGPC.pathSegment} the cubic-bezier segment
|
|
1167
|
+
*/
|
|
1168
|
+
function segmentToCubic(segment, params) {
|
|
1169
|
+
if ('TQ'.indexOf(segment[0]) < 0) {
|
|
1170
|
+
params.qx = null;
|
|
1171
|
+
params.qy = null;
|
|
749
1172
|
}
|
|
750
1173
|
|
|
751
|
-
|
|
752
|
-
* Transforms the specified vector using the matrix, returning a new
|
|
753
|
-
* {x,y,z,w} Tuple *Object* comprising the transformed vector.
|
|
754
|
-
* Neither the matrix nor the original vector are altered.
|
|
755
|
-
*
|
|
756
|
-
* @param {{x: number, y: number, z: number, w: number}} t Tuple with `{x,y,z,w}` components
|
|
757
|
-
* @return {{x: number, y: number, z: number, w: number}} the resulting Tuple
|
|
758
|
-
*/
|
|
759
|
-
transform(t) {
|
|
760
|
-
const m = this;
|
|
761
|
-
const x = m.m11 * t.x + m.m12 * t.y + m.m13 * t.z + m.m14 * t.w;
|
|
762
|
-
const y = m.m21 * t.x + m.m22 * t.y + m.m23 * t.z + m.m24 * t.w;
|
|
763
|
-
const z = m.m31 * t.x + m.m32 * t.y + m.m33 * t.z + m.m34 * t.w;
|
|
764
|
-
const w = m.m41 * t.x + m.m42 * t.y + m.m43 * t.z + m.m44 * t.w;
|
|
1174
|
+
const [s1, s2] = segment.slice(1);
|
|
765
1175
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1176
|
+
switch (segment[0]) {
|
|
1177
|
+
case 'M':
|
|
1178
|
+
params.x = +s1;
|
|
1179
|
+
params.y = +s2;
|
|
1180
|
+
return segment;
|
|
1181
|
+
case 'A':
|
|
1182
|
+
// @ts-ignore
|
|
1183
|
+
return ['C'].concat(arcToCubic.apply(0, [params.x1, params.y1].concat(segment.slice(1))));
|
|
1184
|
+
case 'Q':
|
|
1185
|
+
params.qx = +s1;
|
|
1186
|
+
params.qy = +s2;
|
|
1187
|
+
// @ts-ignore
|
|
1188
|
+
return ['C'].concat(quadToCubic.apply(0, [params.x1, params.y1].concat(segment.slice(1))));
|
|
1189
|
+
case 'L':
|
|
1190
|
+
// @ts-ignore
|
|
1191
|
+
return ['C'].concat(lineToCubic(params.x1, params.y1, segment[1], segment[2]));
|
|
1192
|
+
case 'Z':
|
|
1193
|
+
// @ts-ignore
|
|
1194
|
+
return ['C'].concat(lineToCubic(params.x1, params.y1, params.x, params.y));
|
|
772
1195
|
}
|
|
1196
|
+
return segment;
|
|
773
1197
|
}
|
|
774
1198
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
CSSMatrix.Multiply = Multiply;
|
|
783
|
-
CSSMatrix.fromArray = fromArray;
|
|
784
|
-
CSSMatrix.fromMatrix = fromMatrix;
|
|
785
|
-
CSSMatrix.fromString = fromString;
|
|
786
|
-
|
|
787
|
-
const CSS3Matrix = typeof DOMMatrix !== 'undefined' ? DOMMatrix : CSSMatrix;
|
|
1199
|
+
/**
|
|
1200
|
+
* Splits an extended A (arc-to) segment into two cubic-bezier segments.
|
|
1201
|
+
*
|
|
1202
|
+
* @param {SVGPC.pathArray} path the `pathArray` this segment belongs to
|
|
1203
|
+
* @param {string[]} allPathCommands all previous path commands
|
|
1204
|
+
* @param {Number} i the index of the segment
|
|
1205
|
+
*/
|
|
788
1206
|
|
|
789
|
-
function fixArc(
|
|
790
|
-
if (
|
|
791
|
-
|
|
792
|
-
const
|
|
1207
|
+
function fixArc(path, allPathCommands, i) {
|
|
1208
|
+
if (path[i].length > 7) {
|
|
1209
|
+
path[i].shift();
|
|
1210
|
+
const segment = path[i];
|
|
793
1211
|
let ni = i; // ESLint
|
|
794
|
-
while (
|
|
1212
|
+
while (segment.length) {
|
|
795
1213
|
// if created multiple C:s, their original seg is saved
|
|
796
1214
|
allPathCommands[i] = 'A';
|
|
797
|
-
|
|
1215
|
+
// path.splice(i++, 0, ['C'].concat(segment.splice(0, 6)));
|
|
1216
|
+
// @ts-ignore
|
|
1217
|
+
path.splice(ni += 1, 0, ['C'].concat(segment.splice(0, 6)));
|
|
798
1218
|
}
|
|
799
|
-
|
|
1219
|
+
path.splice(i, 1);
|
|
800
1220
|
}
|
|
801
1221
|
}
|
|
802
1222
|
|
|
803
|
-
var
|
|
804
|
-
a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0,
|
|
805
|
-
};
|
|
806
|
-
|
|
807
|
-
function isPathArray(pathArray) {
|
|
808
|
-
return Array.isArray(pathArray) && pathArray.every((seg) => {
|
|
809
|
-
const pathCommand = seg[0].toLowerCase();
|
|
810
|
-
return paramsCount[pathCommand] === seg.length - 1 && /[achlmrqstvz]/gi.test(pathCommand);
|
|
811
|
-
});
|
|
812
|
-
}
|
|
1223
|
+
var version$1 = "0.0.16alpha4";
|
|
813
1224
|
|
|
814
|
-
|
|
815
|
-
return isPathArray(pathArray) && pathArray.slice(1).every((seg) => seg[0] === 'C');
|
|
816
|
-
}
|
|
1225
|
+
// @ts-ignore
|
|
817
1226
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
return !Number.isNaN(+x) ? +x : x;
|
|
824
|
-
});
|
|
825
|
-
}
|
|
1227
|
+
/**
|
|
1228
|
+
* A global namespace for library version.
|
|
1229
|
+
* @type {string}
|
|
1230
|
+
*/
|
|
1231
|
+
const DMVersion = version$1;
|
|
826
1232
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1233
|
+
// DOMMatrix Static methods
|
|
1234
|
+
// * `fromFloat64Array` and `fromFloat32Array` methods are not supported;
|
|
1235
|
+
// * `fromArray` a more simple implementation, should also accept float[32/64]Array;
|
|
1236
|
+
// * `fromMatrix` load values from another CSSMatrix/DOMMatrix instance;
|
|
1237
|
+
// * `fromString` parses and loads values from any valid CSS transform string.
|
|
831
1238
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
1239
|
+
/**
|
|
1240
|
+
* Creates a new mutable `CSSMatrix` object given an array of floating point values.
|
|
1241
|
+
*
|
|
1242
|
+
* This static method invalidates arrays that contain non-number elements.
|
|
1243
|
+
*
|
|
1244
|
+
* If the array has six values, the result is a 2D matrix; if the array has 16 values,
|
|
1245
|
+
* the result is a 3D matrix. Otherwise, a TypeError exception is thrown.
|
|
1246
|
+
*
|
|
1247
|
+
* @param {number[]} array an `Array` to feed values from.
|
|
1248
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1249
|
+
*/
|
|
1250
|
+
function fromArray(array) {
|
|
1251
|
+
if (!array.every((n) => !Number.isNaN(n))) {
|
|
1252
|
+
throw TypeError(`CSSMatrix: "${array}" must only have numbers.`);
|
|
838
1253
|
}
|
|
1254
|
+
const m = new CSSMatrix();
|
|
1255
|
+
const a = Array.from(array);
|
|
839
1256
|
|
|
840
|
-
if (
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
if (!paramsCount[pathComLK]) {
|
|
846
|
-
break;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
1257
|
+
if (a.length === 16) {
|
|
1258
|
+
const [m11, m12, m13, m14,
|
|
1259
|
+
m21, m22, m23, m24,
|
|
1260
|
+
m31, m32, m33, m34,
|
|
1261
|
+
m41, m42, m43, m44] = a;
|
|
851
1262
|
|
|
852
|
-
|
|
1263
|
+
m.m11 = m11;
|
|
1264
|
+
m.a = m11;
|
|
853
1265
|
|
|
854
|
-
|
|
855
|
-
|
|
1266
|
+
m.m21 = m21;
|
|
1267
|
+
m.c = m21;
|
|
856
1268
|
|
|
857
|
-
|
|
858
|
-
state.param = 0;
|
|
859
|
-
state.index += 1;
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
1269
|
+
m.m31 = m31;
|
|
862
1270
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
state.index += 1;
|
|
866
|
-
return;
|
|
867
|
-
}
|
|
1271
|
+
m.m41 = m41;
|
|
1272
|
+
m.e = m41;
|
|
868
1273
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
}
|
|
1274
|
+
m.m12 = m12;
|
|
1275
|
+
m.b = m12;
|
|
872
1276
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
1277
|
+
m.m22 = m22;
|
|
1278
|
+
m.d = m22;
|
|
876
1279
|
|
|
877
|
-
|
|
878
|
-
const start = state.index;
|
|
879
|
-
const { max } = state;
|
|
880
|
-
let index = start;
|
|
881
|
-
let zeroFirst = false;
|
|
882
|
-
let hasCeiling = false;
|
|
883
|
-
let hasDecimal = false;
|
|
884
|
-
let hasDot = false;
|
|
885
|
-
let ch;
|
|
1280
|
+
m.m32 = m32;
|
|
886
1281
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
state.err = `${invalidPathValue}: missing param ${state.pathValue[index]}`;
|
|
890
|
-
return;
|
|
891
|
-
}
|
|
892
|
-
ch = state.pathValue.charCodeAt(index);
|
|
1282
|
+
m.m42 = m42;
|
|
1283
|
+
m.f = m42;
|
|
893
1284
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1285
|
+
m.m13 = m13;
|
|
1286
|
+
m.m23 = m23;
|
|
1287
|
+
m.m33 = m33;
|
|
1288
|
+
m.m43 = m43;
|
|
1289
|
+
m.m14 = m14;
|
|
1290
|
+
m.m24 = m24;
|
|
1291
|
+
m.m34 = m34;
|
|
1292
|
+
m.m44 = m44;
|
|
1293
|
+
} else if (a.length === 6) {
|
|
1294
|
+
const [m11, m12, m21, m22, m41, m42] = a;
|
|
1295
|
+
|
|
1296
|
+
m.m11 = m11;
|
|
1297
|
+
m.a = m11;
|
|
1298
|
+
|
|
1299
|
+
m.m12 = m12;
|
|
1300
|
+
m.b = m12;
|
|
898
1301
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
if (!isDigit(ch) && ch !== 0x2E/* . */) {
|
|
902
|
-
// state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
|
|
903
|
-
state.err = `${invalidPathValue} at index ${index}: ${state.pathValue[index]} is not a number`;
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
1302
|
+
m.m21 = m21;
|
|
1303
|
+
m.c = m21;
|
|
906
1304
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
index += 1;
|
|
1305
|
+
m.m22 = m22;
|
|
1306
|
+
m.d = m22;
|
|
910
1307
|
|
|
911
|
-
|
|
1308
|
+
m.m41 = m41;
|
|
1309
|
+
m.e = m41;
|
|
912
1310
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
}
|
|
921
|
-
}
|
|
1311
|
+
m.m42 = m42;
|
|
1312
|
+
m.f = m42;
|
|
1313
|
+
} else {
|
|
1314
|
+
throw new TypeError('CSSMatrix: expecting an Array of 6/16 values.');
|
|
1315
|
+
}
|
|
1316
|
+
return m;
|
|
1317
|
+
}
|
|
922
1318
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1319
|
+
/**
|
|
1320
|
+
* Creates a new mutable `CSSMatrix` instance given an existing matrix or a
|
|
1321
|
+
* `DOMMatrix` instance which provides the values for its properties.
|
|
1322
|
+
*
|
|
1323
|
+
* @param {CSSMatrix | DOMMatrix | DMNS.jsonMatrix} m the source matrix to feed values from.
|
|
1324
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1325
|
+
*/
|
|
1326
|
+
function fromMatrix(m) {
|
|
1327
|
+
const keys = [
|
|
1328
|
+
'm11', 'm12', 'm13', 'm14',
|
|
1329
|
+
'm21', 'm22', 'm23', 'm24',
|
|
1330
|
+
'm31', 'm32', 'm33', 'm34',
|
|
1331
|
+
'm41', 'm42', 'm43', 'm44'];
|
|
1332
|
+
if ([CSSMatrix, DOMMatrix].some((x) => m instanceof x)
|
|
1333
|
+
|| (typeof m === 'object' && keys.every((k) => k in m))) {
|
|
1334
|
+
return fromArray(
|
|
1335
|
+
[m.m11, m.m12, m.m13, m.m14,
|
|
1336
|
+
m.m21, m.m22, m.m23, m.m24,
|
|
1337
|
+
m.m31, m.m32, m.m33, m.m34,
|
|
1338
|
+
m.m41, m.m42, m.m43, m.m44],
|
|
1339
|
+
);
|
|
928
1340
|
}
|
|
1341
|
+
throw TypeError(`CSSMatrix: "${m}" is not a DOMMatrix / CSSMatrix compatible object.`);
|
|
1342
|
+
}
|
|
929
1343
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1344
|
+
/**
|
|
1345
|
+
* Creates a new mutable `CSSMatrix` instance given any valid CSS transform string.
|
|
1346
|
+
*
|
|
1347
|
+
* * `matrix(a, b, c, d, e, f)` - valid matrix() transform function
|
|
1348
|
+
* * `matrix3d(m11, m12, m13, ...m44)` - valid matrix3d() transform function
|
|
1349
|
+
* * `translate(tx, ty) rotateX(alpha)` - any valid transform function(s)
|
|
1350
|
+
*
|
|
1351
|
+
* @copyright thednp © 2021
|
|
1352
|
+
*
|
|
1353
|
+
* @param {string} source valid CSS transform string syntax.
|
|
1354
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1355
|
+
*/
|
|
1356
|
+
function fromString(source) {
|
|
1357
|
+
if (typeof source !== 'string') {
|
|
1358
|
+
throw TypeError(`CSSMatrix: "${source}" is not a string.`);
|
|
938
1359
|
}
|
|
1360
|
+
const str = String(source).replace(/\s/g, '');
|
|
1361
|
+
let m = new CSSMatrix();
|
|
1362
|
+
let is2D = true;
|
|
1363
|
+
const tramsformObject = str.split(')').filter((f) => f).map((fn) => {
|
|
1364
|
+
const [prop, value] = fn.split('(');
|
|
1365
|
+
const components = value.split(',')
|
|
1366
|
+
.map((n) => (n.includes('rad') ? parseFloat(n) * (180 / Math.PI) : parseFloat(n)));
|
|
1367
|
+
const [x, y, z, a] = components;
|
|
939
1368
|
|
|
940
|
-
|
|
941
|
-
if (
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1369
|
+
// don't add perspective if is2D
|
|
1370
|
+
if (is2D && (prop === 'matrix3d' // only modify is2D once
|
|
1371
|
+
|| (prop === 'rotate3d' && [x, y].every((n) => !Number.isNaN(+n) && n !== 0) && a)
|
|
1372
|
+
|| (['rotateX', 'rotateY'].includes(prop) && x)
|
|
1373
|
+
|| (prop === 'translate3d' && [x, y, z].every((n) => !Number.isNaN(+n)) && z)
|
|
1374
|
+
|| (prop === 'scale3d' && [x, y, z].every((n) => !Number.isNaN(+n) && n !== x))
|
|
1375
|
+
)) {
|
|
1376
|
+
is2D = false;
|
|
945
1377
|
}
|
|
1378
|
+
return { prop, components };
|
|
1379
|
+
});
|
|
946
1380
|
|
|
947
|
-
|
|
1381
|
+
tramsformObject.forEach((tf) => {
|
|
1382
|
+
const { prop, components } = tf;
|
|
1383
|
+
const [x, y, z, a] = components;
|
|
1384
|
+
const xyz = [x, y, z];
|
|
1385
|
+
const xyza = [x, y, z, a];
|
|
948
1386
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1387
|
+
if (prop === 'perspective' && !is2D) {
|
|
1388
|
+
m.m34 = -1 / x;
|
|
1389
|
+
} else if (prop.includes('matrix')) {
|
|
1390
|
+
const values = components.map((n) => (Math.abs(n) < 1e-6 ? 0 : n));
|
|
1391
|
+
if ([6, 16].includes(values.length)) {
|
|
1392
|
+
m = m.multiply(fromArray(values));
|
|
1393
|
+
}
|
|
1394
|
+
} else if (['translate', 'translate3d'].some((p) => prop === p) && x) {
|
|
1395
|
+
m = m.translate(x, y || 0, z || 0);
|
|
1396
|
+
} else if (prop === 'rotate3d' && xyza.every((n) => !Number.isNaN(+n)) && a) {
|
|
1397
|
+
m = m.rotateAxisAngle(x, y, z, a);
|
|
1398
|
+
} else if (prop === 'scale3d' && xyz.every((n) => !Number.isNaN(+n)) && xyz.some((n) => n !== 1)) {
|
|
1399
|
+
m = m.scale(x, y, z);
|
|
1400
|
+
} else if (prop === 'rotate' && x) {
|
|
1401
|
+
m = m.rotate(0, 0, x);
|
|
1402
|
+
} else if (prop === 'scale' && !Number.isNaN(x) && x !== 1) {
|
|
1403
|
+
const nosy = Number.isNaN(+y);
|
|
1404
|
+
const sy = nosy ? x : y;
|
|
1405
|
+
m = m.scale(x, sy, 1);
|
|
1406
|
+
} else if (prop === 'skew' && (x || y)) {
|
|
1407
|
+
m = x ? m.skewX(x) : m;
|
|
1408
|
+
m = y ? m.skewY(y) : m;
|
|
1409
|
+
} else if (/[XYZ]/.test(prop) && x) {
|
|
1410
|
+
if (prop.includes('skew')) {
|
|
1411
|
+
// @ts-ignore unfortunately
|
|
1412
|
+
m = m[prop](x);
|
|
1413
|
+
} else {
|
|
1414
|
+
const fn = prop.replace(/[XYZ]/, '');
|
|
1415
|
+
const axis = prop.replace(fn, '');
|
|
1416
|
+
const idx = ['X', 'Y', 'Z'].indexOf(axis);
|
|
1417
|
+
const axeValues = [
|
|
1418
|
+
idx === 0 ? x : 0,
|
|
1419
|
+
idx === 1 ? x : 0,
|
|
1420
|
+
idx === 2 ? x : 0];
|
|
1421
|
+
// @ts-ignore unfortunately
|
|
1422
|
+
m = m[fn](...axeValues);
|
|
956
1423
|
}
|
|
957
|
-
} else {
|
|
958
|
-
// state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
|
|
959
|
-
state.err = `${invalidPathValue}: ${state.pathValue[index]} invalid float exponent`;
|
|
960
|
-
return;
|
|
961
1424
|
}
|
|
962
|
-
}
|
|
1425
|
+
});
|
|
963
1426
|
|
|
964
|
-
|
|
965
|
-
state.param = +state.pathValue.slice(start, index);
|
|
1427
|
+
return m;
|
|
966
1428
|
}
|
|
967
1429
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
|
|
971
|
-
0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF];
|
|
972
|
-
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) // Line terminators
|
|
973
|
-
// White spaces
|
|
974
|
-
|| (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0)
|
|
975
|
-
|| (ch >= 0x1680 && specialSpaces.indexOf(ch) >= 0);
|
|
976
|
-
}
|
|
1430
|
+
// Transform Functions
|
|
1431
|
+
// https://www.w3.org/TR/css-transforms-1/#transform-functions
|
|
977
1432
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1433
|
+
/**
|
|
1434
|
+
* Creates a new `CSSMatrix` for the translation matrix and returns it.
|
|
1435
|
+
* This method is equivalent to the CSS `translate3d()` function.
|
|
1436
|
+
*
|
|
1437
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translate3d
|
|
1438
|
+
*
|
|
1439
|
+
* @param {number} x the `x-axis` position.
|
|
1440
|
+
* @param {number} y the `y-axis` position.
|
|
1441
|
+
* @param {number} z the `z-axis` position.
|
|
1442
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1443
|
+
*/
|
|
1444
|
+
function Translate(x, y, z) {
|
|
1445
|
+
const m = new CSSMatrix();
|
|
1446
|
+
m.m41 = x;
|
|
1447
|
+
m.e = x;
|
|
1448
|
+
m.m42 = y;
|
|
1449
|
+
m.f = y;
|
|
1450
|
+
m.m43 = z;
|
|
1451
|
+
return m;
|
|
982
1452
|
}
|
|
983
1453
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
return false;
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Creates a new `CSSMatrix` for the rotation matrix and returns it.
|
|
1456
|
+
*
|
|
1457
|
+
* http://en.wikipedia.org/wiki/Rotation_matrix
|
|
1458
|
+
*
|
|
1459
|
+
* @param {number} rx the `x-axis` rotation.
|
|
1460
|
+
* @param {number} ry the `y-axis` rotation.
|
|
1461
|
+
* @param {number} rz the `z-axis` rotation.
|
|
1462
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1463
|
+
*/
|
|
1464
|
+
function Rotate(rx, ry, rz) {
|
|
1465
|
+
const m = new CSSMatrix();
|
|
1466
|
+
const degToRad = Math.PI / 180;
|
|
1467
|
+
const radX = rx * degToRad;
|
|
1468
|
+
const radY = ry * degToRad;
|
|
1469
|
+
const radZ = rz * degToRad;
|
|
1003
1470
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1471
|
+
// minus sin() because of right-handed system
|
|
1472
|
+
const cosx = Math.cos(radX);
|
|
1473
|
+
const sinx = -Math.sin(radX);
|
|
1474
|
+
const cosy = Math.cos(radY);
|
|
1475
|
+
const siny = -Math.sin(radY);
|
|
1476
|
+
const cosz = Math.cos(radZ);
|
|
1477
|
+
const sinz = -Math.sin(radZ);
|
|
1010
1478
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
return (code | 0x20) === 0x61;
|
|
1014
|
-
}
|
|
1479
|
+
const m11 = cosy * cosz;
|
|
1480
|
+
const m12 = -cosy * sinz;
|
|
1015
1481
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
const cmdCode = state.pathValue.charCodeAt(state.index);
|
|
1019
|
-
const reqParams = paramsCount[state.pathValue[state.index].toLowerCase()];
|
|
1482
|
+
m.m11 = m11;
|
|
1483
|
+
m.a = m11;
|
|
1020
1484
|
|
|
1021
|
-
|
|
1485
|
+
m.m12 = m12;
|
|
1486
|
+
m.b = m12;
|
|
1022
1487
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1488
|
+
m.m13 = siny;
|
|
1489
|
+
|
|
1490
|
+
const m21 = sinx * siny * cosz + cosx * sinz;
|
|
1491
|
+
m.m21 = m21;
|
|
1492
|
+
m.c = m21;
|
|
1493
|
+
|
|
1494
|
+
const m22 = cosx * cosz - sinx * siny * sinz;
|
|
1495
|
+
m.m22 = m22;
|
|
1496
|
+
m.d = m22;
|
|
1497
|
+
|
|
1498
|
+
m.m23 = -sinx * cosy;
|
|
1499
|
+
|
|
1500
|
+
m.m31 = sinx * sinz - cosx * siny * cosz;
|
|
1501
|
+
m.m32 = sinx * cosz + cosx * siny * sinz;
|
|
1502
|
+
m.m33 = cosx * cosy;
|
|
1503
|
+
|
|
1504
|
+
return m;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
/**
|
|
1508
|
+
* Creates a new `CSSMatrix` for the rotation matrix and returns it.
|
|
1509
|
+
* This method is equivalent to the CSS `rotate3d()` function.
|
|
1510
|
+
*
|
|
1511
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate3d
|
|
1512
|
+
*
|
|
1513
|
+
* @param {number} x the `x-axis` vector length.
|
|
1514
|
+
* @param {number} y the `y-axis` vector length.
|
|
1515
|
+
* @param {number} z the `z-axis` vector length.
|
|
1516
|
+
* @param {number} alpha the value in degrees of the rotation.
|
|
1517
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1518
|
+
*/
|
|
1519
|
+
function RotateAxisAngle(x, y, z, alpha) {
|
|
1520
|
+
const m = new CSSMatrix();
|
|
1521
|
+
const angle = alpha * (Math.PI / 360);
|
|
1522
|
+
const sinA = Math.sin(angle);
|
|
1523
|
+
const cosA = Math.cos(angle);
|
|
1524
|
+
const sinA2 = sinA * sinA;
|
|
1525
|
+
const length = Math.sqrt(x * x + y * y + z * z);
|
|
1526
|
+
let X = x;
|
|
1527
|
+
let Y = y;
|
|
1528
|
+
let Z = z;
|
|
1529
|
+
|
|
1530
|
+
if (length === 0) {
|
|
1531
|
+
// bad vector length, use something reasonable
|
|
1532
|
+
X = 0;
|
|
1533
|
+
Y = 0;
|
|
1534
|
+
Z = 1;
|
|
1535
|
+
} else {
|
|
1536
|
+
X /= length;
|
|
1537
|
+
Y /= length;
|
|
1538
|
+
Z /= length;
|
|
1026
1539
|
}
|
|
1027
1540
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1541
|
+
const x2 = X * X;
|
|
1542
|
+
const y2 = Y * Y;
|
|
1543
|
+
const z2 = Z * Z;
|
|
1030
1544
|
|
|
1031
|
-
|
|
1545
|
+
const m11 = 1 - 2 * (y2 + z2) * sinA2;
|
|
1546
|
+
m.m11 = m11;
|
|
1547
|
+
m.a = m11;
|
|
1032
1548
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
return;
|
|
1037
|
-
}
|
|
1549
|
+
const m12 = 2 * (X * Y * sinA2 + Z * sinA * cosA);
|
|
1550
|
+
m.m12 = m12;
|
|
1551
|
+
m.b = m12;
|
|
1038
1552
|
|
|
1039
|
-
|
|
1040
|
-
for (let i = reqParams; i > 0; i -= 1) {
|
|
1041
|
-
if (isArcCommand(cmdCode) && (i === 3 || i === 4)) scanFlag(state);
|
|
1042
|
-
else scanParam(state);
|
|
1553
|
+
m.m13 = 2 * (X * Z * sinA2 - Y * sinA * cosA);
|
|
1043
1554
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
state.data.push(state.param);
|
|
1555
|
+
const m21 = 2 * (Y * X * sinA2 - Z * sinA * cosA);
|
|
1556
|
+
m.m21 = m21;
|
|
1557
|
+
m.c = m21;
|
|
1048
1558
|
|
|
1049
|
-
|
|
1559
|
+
const m22 = 1 - 2 * (z2 + x2) * sinA2;
|
|
1560
|
+
m.m22 = m22;
|
|
1561
|
+
m.d = m22;
|
|
1050
1562
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1563
|
+
m.m23 = 2 * (Y * Z * sinA2 + X * sinA * cosA);
|
|
1564
|
+
m.m31 = 2 * (Z * X * sinA2 + Y * sinA * cosA);
|
|
1565
|
+
m.m32 = 2 * (Z * Y * sinA2 - X * sinA * cosA);
|
|
1566
|
+
m.m33 = 1 - 2 * (x2 + y2) * sinA2;
|
|
1057
1567
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
}
|
|
1568
|
+
return m;
|
|
1569
|
+
}
|
|
1061
1570
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1571
|
+
/**
|
|
1572
|
+
* Creates a new `CSSMatrix` for the scale matrix and returns it.
|
|
1573
|
+
* This method is equivalent to the CSS `scale3d()` function.
|
|
1574
|
+
*
|
|
1575
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale3d
|
|
1576
|
+
*
|
|
1577
|
+
* @param {number} x the `x-axis` scale.
|
|
1578
|
+
* @param {number} y the `y-axis` scale.
|
|
1579
|
+
* @param {number} z the `z-axis` scale.
|
|
1580
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1581
|
+
*/
|
|
1582
|
+
function Scale(x, y, z) {
|
|
1583
|
+
const m = new CSSMatrix();
|
|
1584
|
+
m.m11 = x;
|
|
1585
|
+
m.a = x;
|
|
1067
1586
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1587
|
+
m.m22 = y;
|
|
1588
|
+
m.d = y;
|
|
1070
1589
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
this.pathValue = pathString;
|
|
1074
|
-
this.max = pathString.length;
|
|
1075
|
-
this.index = 0;
|
|
1076
|
-
this.param = 0.0;
|
|
1077
|
-
this.segmentStart = 0;
|
|
1078
|
-
this.data = [];
|
|
1079
|
-
this.err = '';
|
|
1590
|
+
m.m33 = z;
|
|
1591
|
+
return m;
|
|
1080
1592
|
}
|
|
1081
1593
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1594
|
+
/**
|
|
1595
|
+
* Creates a new `CSSMatrix` for the shear of the `x-axis` rotation matrix and
|
|
1596
|
+
* returns it. This method is equivalent to the CSS `skewX()` function.
|
|
1597
|
+
*
|
|
1598
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skewX
|
|
1599
|
+
*
|
|
1600
|
+
* @param {number} angle the angle in degrees.
|
|
1601
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1602
|
+
*/
|
|
1603
|
+
function SkewX(angle) {
|
|
1604
|
+
const m = new CSSMatrix();
|
|
1605
|
+
const radA = (angle * Math.PI) / 180;
|
|
1606
|
+
const t = Math.tan(radA);
|
|
1607
|
+
m.m21 = t;
|
|
1608
|
+
m.c = t;
|
|
1609
|
+
return m;
|
|
1610
|
+
}
|
|
1087
1611
|
|
|
1088
|
-
|
|
1612
|
+
/**
|
|
1613
|
+
* Creates a new `CSSMatrix` for the shear of the `y-axis` rotation matrix and
|
|
1614
|
+
* returns it. This method is equivalent to the CSS `skewY()` function.
|
|
1615
|
+
*
|
|
1616
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skewY
|
|
1617
|
+
*
|
|
1618
|
+
* @param {number} angle the angle in degrees.
|
|
1619
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1620
|
+
*/
|
|
1621
|
+
function SkewY(angle) {
|
|
1622
|
+
const m = new CSSMatrix();
|
|
1623
|
+
const radA = (angle * Math.PI) / 180;
|
|
1624
|
+
const t = Math.tan(radA);
|
|
1625
|
+
m.m12 = t;
|
|
1626
|
+
m.b = t;
|
|
1627
|
+
return m;
|
|
1628
|
+
}
|
|
1089
1629
|
|
|
1090
|
-
|
|
1630
|
+
/**
|
|
1631
|
+
* Creates a new `CSSMatrix` resulted from the multiplication of two matrixes
|
|
1632
|
+
* and returns it. Both matrixes are not changed.
|
|
1633
|
+
*
|
|
1634
|
+
* @param {CSSMatrix} m1 the first matrix.
|
|
1635
|
+
* @param {CSSMatrix} m2 the second matrix.
|
|
1636
|
+
* @return {CSSMatrix} the resulted matrix.
|
|
1637
|
+
*/
|
|
1638
|
+
function Multiply(m1, m2) {
|
|
1639
|
+
const m11 = m2.m11 * m1.m11 + m2.m12 * m1.m21 + m2.m13 * m1.m31 + m2.m14 * m1.m41;
|
|
1640
|
+
const m12 = m2.m11 * m1.m12 + m2.m12 * m1.m22 + m2.m13 * m1.m32 + m2.m14 * m1.m42;
|
|
1641
|
+
const m13 = m2.m11 * m1.m13 + m2.m12 * m1.m23 + m2.m13 * m1.m33 + m2.m14 * m1.m43;
|
|
1642
|
+
const m14 = m2.m11 * m1.m14 + m2.m12 * m1.m24 + m2.m13 * m1.m34 + m2.m14 * m1.m44;
|
|
1091
1643
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1644
|
+
const m21 = m2.m21 * m1.m11 + m2.m22 * m1.m21 + m2.m23 * m1.m31 + m2.m24 * m1.m41;
|
|
1645
|
+
const m22 = m2.m21 * m1.m12 + m2.m22 * m1.m22 + m2.m23 * m1.m32 + m2.m24 * m1.m42;
|
|
1646
|
+
const m23 = m2.m21 * m1.m13 + m2.m22 * m1.m23 + m2.m23 * m1.m33 + m2.m24 * m1.m43;
|
|
1647
|
+
const m24 = m2.m21 * m1.m14 + m2.m22 * m1.m24 + m2.m23 * m1.m34 + m2.m24 * m1.m44;
|
|
1095
1648
|
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
// state.err = 'Path string should start with `M` or `m`';
|
|
1101
|
-
state.err = `${invalidPathValue}: missing M/m`;
|
|
1102
|
-
state.segments = [];
|
|
1103
|
-
} else {
|
|
1104
|
-
state.segments[0][0] = 'M';
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1649
|
+
const m31 = m2.m31 * m1.m11 + m2.m32 * m1.m21 + m2.m33 * m1.m31 + m2.m34 * m1.m41;
|
|
1650
|
+
const m32 = m2.m31 * m1.m12 + m2.m32 * m1.m22 + m2.m33 * m1.m32 + m2.m34 * m1.m42;
|
|
1651
|
+
const m33 = m2.m31 * m1.m13 + m2.m32 * m1.m23 + m2.m33 * m1.m33 + m2.m34 * m1.m43;
|
|
1652
|
+
const m34 = m2.m31 * m1.m14 + m2.m32 * m1.m24 + m2.m33 * m1.m34 + m2.m34 * m1.m44;
|
|
1107
1653
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1654
|
+
const m41 = m2.m41 * m1.m11 + m2.m42 * m1.m21 + m2.m43 * m1.m31 + m2.m44 * m1.m41;
|
|
1655
|
+
const m42 = m2.m41 * m1.m12 + m2.m42 * m1.m22 + m2.m43 * m1.m32 + m2.m44 * m1.m42;
|
|
1656
|
+
const m43 = m2.m41 * m1.m13 + m2.m42 * m1.m23 + m2.m43 * m1.m33 + m2.m44 * m1.m43;
|
|
1657
|
+
const m44 = m2.m41 * m1.m14 + m2.m42 * m1.m24 + m2.m43 * m1.m34 + m2.m44 * m1.m44;
|
|
1110
1658
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1659
|
+
return fromArray(
|
|
1660
|
+
[m11, m12, m13, m14,
|
|
1661
|
+
m21, m22, m23, m24,
|
|
1662
|
+
m31, m32, m33, m34,
|
|
1663
|
+
m41, m42, m43, m44],
|
|
1664
|
+
);
|
|
1113
1665
|
}
|
|
1114
1666
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
const resultArray = [];
|
|
1123
|
-
let x = 0;
|
|
1124
|
-
let y = 0;
|
|
1125
|
-
let mx = 0;
|
|
1126
|
-
let my = 0;
|
|
1127
|
-
let start = 0;
|
|
1128
|
-
|
|
1129
|
-
if (pathArray[0][0] === 'M') {
|
|
1130
|
-
x = +pathArray[0][1];
|
|
1131
|
-
y = +pathArray[0][2];
|
|
1132
|
-
mx = x;
|
|
1133
|
-
my = y;
|
|
1134
|
-
start += 1;
|
|
1135
|
-
resultArray.push(['M', x, y]);
|
|
1136
|
-
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Creates and returns a new `DOMMatrix` compatible *Object*
|
|
1669
|
+
* with equivalent instance methods.
|
|
1670
|
+
*
|
|
1671
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix
|
|
1672
|
+
* https://github.com/thednp/DOMMatrix/
|
|
1673
|
+
*/
|
|
1137
1674
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1675
|
+
class CSSMatrix {
|
|
1676
|
+
/**
|
|
1677
|
+
* @constructor
|
|
1678
|
+
* @param {any} args accepts all parameter configurations:
|
|
1679
|
+
*
|
|
1680
|
+
* * valid CSS transform string,
|
|
1681
|
+
* * CSSMatrix/DOMMatrix instance,
|
|
1682
|
+
* * a 6/16 elements *Array*.
|
|
1683
|
+
*/
|
|
1684
|
+
constructor(...args) {
|
|
1685
|
+
const m = this;
|
|
1686
|
+
// array 6
|
|
1687
|
+
m.a = 1; m.b = 0;
|
|
1688
|
+
m.c = 0; m.d = 1;
|
|
1689
|
+
m.e = 0; m.f = 0;
|
|
1690
|
+
// array 16
|
|
1691
|
+
m.m11 = 1; m.m12 = 0; m.m13 = 0; m.m14 = 0;
|
|
1692
|
+
m.m21 = 0; m.m22 = 1; m.m23 = 0; m.m24 = 0;
|
|
1693
|
+
m.m31 = 0; m.m32 = 0; m.m33 = 1; m.m34 = 0;
|
|
1694
|
+
m.m41 = 0; m.m42 = 0; m.m43 = 0; m.m44 = 1;
|
|
1145
1695
|
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1696
|
+
if (args && args.length) {
|
|
1697
|
+
let ARGS = args;
|
|
1148
1698
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
break;
|
|
1156
|
-
case 'V':
|
|
1157
|
-
absoluteSegment[1] = +segment[1] + y;
|
|
1158
|
-
break;
|
|
1159
|
-
case 'H':
|
|
1160
|
-
absoluteSegment[1] = +segment[1] + x;
|
|
1161
|
-
break;
|
|
1162
|
-
default:
|
|
1163
|
-
if (absCommand === 'M') {
|
|
1164
|
-
mx = +segment[1] + x;
|
|
1165
|
-
my = +segment[2] + y;
|
|
1166
|
-
}
|
|
1167
|
-
// for is here to stay for eslint
|
|
1168
|
-
for (let j = 1; j < segment.length; j += 1) {
|
|
1169
|
-
absoluteSegment.push(+segment[j] + (j % 2 ? x : y));
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
} else {
|
|
1173
|
-
for (let j = 0; j < segment.length; j += 1) {
|
|
1174
|
-
absoluteSegment.push(segment[j]);
|
|
1699
|
+
if (args instanceof Array) {
|
|
1700
|
+
if ((args[0] instanceof Array && [16, 6].includes(args[0].length))
|
|
1701
|
+
|| typeof args[0] === 'string'
|
|
1702
|
+
|| [CSSMatrix, DOMMatrix].some((x) => args[0] instanceof x)) {
|
|
1703
|
+
[ARGS] = args;
|
|
1704
|
+
}
|
|
1175
1705
|
}
|
|
1706
|
+
return m.setMatrixValue(ARGS);
|
|
1176
1707
|
}
|
|
1708
|
+
return m;
|
|
1709
|
+
}
|
|
1177
1710
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
break;
|
|
1187
|
-
case 'V':
|
|
1188
|
-
y = +absoluteSegment[1];
|
|
1189
|
-
break;
|
|
1190
|
-
default:
|
|
1191
|
-
x = +absoluteSegment[segLength - 2];
|
|
1192
|
-
y = +absoluteSegment[segLength - 1];
|
|
1711
|
+
/**
|
|
1712
|
+
* Sets a new `Boolean` flag value for `this.isIdentity` matrix property.
|
|
1713
|
+
*
|
|
1714
|
+
* @param {Boolean} value sets a new flag for this property
|
|
1715
|
+
*/
|
|
1716
|
+
set isIdentity(value) {
|
|
1717
|
+
this.isIdentity = value;
|
|
1718
|
+
}
|
|
1193
1719
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1720
|
+
/**
|
|
1721
|
+
* A `Boolean` whose value is `true` if the matrix is the identity matrix. The identity
|
|
1722
|
+
* matrix is one in which every value is 0 except those on the main diagonal from top-left
|
|
1723
|
+
* to bottom-right corner (in other words, where the offsets in each direction are equal).
|
|
1724
|
+
*
|
|
1725
|
+
* @return {Boolean} the current property value
|
|
1726
|
+
*/
|
|
1727
|
+
get isIdentity() {
|
|
1728
|
+
const m = this;
|
|
1729
|
+
return (m.m11 === 1 && m.m12 === 0 && m.m13 === 0 && m.m14 === 0
|
|
1730
|
+
&& m.m21 === 0 && m.m22 === 1 && m.m23 === 0 && m.m24 === 0
|
|
1731
|
+
&& m.m31 === 0 && m.m32 === 0 && m.m33 === 1 && m.m34 === 0
|
|
1732
|
+
&& m.m41 === 0 && m.m42 === 0 && m.m43 === 0 && m.m44 === 1);
|
|
1199
1733
|
}
|
|
1200
1734
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1735
|
+
/**
|
|
1736
|
+
* A `Boolean` flag whose value is `true` if the matrix was initialized as a 2D matrix
|
|
1737
|
+
* and `false` if the matrix is 3D.
|
|
1738
|
+
*
|
|
1739
|
+
* @return {Boolean} the current property value
|
|
1740
|
+
*/
|
|
1741
|
+
get is2D() {
|
|
1742
|
+
const m = this;
|
|
1743
|
+
return (m.m31 === 0 && m.m32 === 0 && m.m33 === 1 && m.m34 === 0 && m.m43 === 0 && m.m44 === 1);
|
|
1744
|
+
}
|
|
1210
1745
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1746
|
+
/**
|
|
1747
|
+
* Sets a new `Boolean` flag value for `this.is2D` matrix property.
|
|
1748
|
+
*
|
|
1749
|
+
* @param {Boolean} value sets a new flag for this property
|
|
1750
|
+
*/
|
|
1751
|
+
set is2D(value) {
|
|
1752
|
+
this.is2D = value;
|
|
1753
|
+
}
|
|
1217
1754
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1755
|
+
/**
|
|
1756
|
+
* The `setMatrixValue` method replaces the existing matrix with one computed
|
|
1757
|
+
* in the browser. EG: `matrix(1,0.25,-0.25,1,0,0)`
|
|
1758
|
+
*
|
|
1759
|
+
* The method accepts any *Array* values, the result of
|
|
1760
|
+
* `DOMMatrix` instance method `toFloat64Array()` / `toFloat32Array()` calls
|
|
1761
|
+
* or `CSSMatrix` instance method `toArray()`.
|
|
1762
|
+
*
|
|
1763
|
+
* This method expects valid *matrix()* / *matrix3d()* string values, as well
|
|
1764
|
+
* as other transform functions like *translateX(10px)*.
|
|
1765
|
+
*
|
|
1766
|
+
* @param {string | number[] | CSSMatrix | DOMMatrix} source
|
|
1767
|
+
* @return {CSSMatrix} the matrix instance
|
|
1768
|
+
*/
|
|
1769
|
+
setMatrixValue(source) {
|
|
1770
|
+
const m = this;
|
|
1222
1771
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1772
|
+
// new CSSMatrix(CSSMatrix | DOMMatrix)
|
|
1773
|
+
if ([DOMMatrix, CSSMatrix].some((x) => source instanceof x)) {
|
|
1774
|
+
// @ts-ignore
|
|
1775
|
+
return fromMatrix(source);
|
|
1776
|
+
// CSS transform string source
|
|
1777
|
+
} if (typeof source === 'string' && source.length && source !== 'none') {
|
|
1778
|
+
return fromString(source);
|
|
1779
|
+
// [Arguments list | Array] come here
|
|
1780
|
+
} if (Array.isArray(source)) {
|
|
1781
|
+
return fromArray(source);
|
|
1782
|
+
}
|
|
1783
|
+
return m;
|
|
1227
1784
|
}
|
|
1228
1785
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
} else if (pathCommand === 'Q') {
|
|
1244
|
-
const [nqx, nqy] = xy;
|
|
1245
|
-
params.qx = nqx;
|
|
1246
|
-
params.qy = nqy;
|
|
1786
|
+
/**
|
|
1787
|
+
* Creates and returns a string representation of the matrix in `CSS` matrix syntax,
|
|
1788
|
+
* using the appropriate `CSS` matrix notation.
|
|
1789
|
+
*
|
|
1790
|
+
* matrix3d *matrix3d(m11, m12, m13, m14, m21, ...)*
|
|
1791
|
+
* matrix *matrix(a, b, c, d, e, f)*
|
|
1792
|
+
*
|
|
1793
|
+
* @return {string} a string representation of the matrix
|
|
1794
|
+
*/
|
|
1795
|
+
toString() {
|
|
1796
|
+
const m = this;
|
|
1797
|
+
const values = m.toArray().join(',');
|
|
1798
|
+
const type = m.is2D ? 'matrix' : 'matrix3d';
|
|
1799
|
+
return `${type}(${values})`;
|
|
1247
1800
|
}
|
|
1248
|
-
return result;
|
|
1249
|
-
}
|
|
1250
1801
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1802
|
+
/**
|
|
1803
|
+
* Returns an *Array* containing all 16 elements which comprise the matrix.
|
|
1804
|
+
* The method can return either the elements.
|
|
1805
|
+
*
|
|
1806
|
+
* Other methods make use of this method to feed their output values from this matrix.
|
|
1807
|
+
*
|
|
1808
|
+
* @return {number[]} an *Array* representation of the matrix
|
|
1809
|
+
*/
|
|
1810
|
+
toArray() {
|
|
1811
|
+
const m = this;
|
|
1812
|
+
const pow6 = (10 ** 6);
|
|
1813
|
+
let result;
|
|
1257
1814
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1815
|
+
if (m.is2D) {
|
|
1816
|
+
result = [m.a, m.b, m.c, m.d, m.e, m.f];
|
|
1817
|
+
} else {
|
|
1818
|
+
result = [m.m11, m.m12, m.m13, m.m14,
|
|
1819
|
+
m.m21, m.m22, m.m23, m.m24,
|
|
1820
|
+
m.m31, m.m32, m.m33, m.m34,
|
|
1821
|
+
m.m41, m.m42, m.m43, m.m44];
|
|
1822
|
+
}
|
|
1823
|
+
// clean up the numbers
|
|
1824
|
+
// eslint-disable-next-line -- no-bitwise
|
|
1825
|
+
return result.map((n) => (Math.abs(n) < 1e-6 ? 0 : ((n * pow6) >> 0) / pow6));
|
|
1261
1826
|
}
|
|
1262
1827
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
// Save current path command
|
|
1278
|
-
allPathCommands[i] = pathCommand;
|
|
1279
|
-
// Get previous path command
|
|
1280
|
-
if (i) prevCommand = allPathCommands[i - 1];
|
|
1281
|
-
// Previous path command is inputted to processSegment
|
|
1282
|
-
pathArray[i] = normalizeSegment(pathArray[i], params, prevCommand);
|
|
1828
|
+
/**
|
|
1829
|
+
* Returns a JSON representation of the `CSSMatrix` instance, a standard *Object*
|
|
1830
|
+
* that includes `{a,b,c,d,e,f}` and `{m11,m12,m13,..m44}` properties and
|
|
1831
|
+
* excludes `is2D` & `isIdentity` properties.
|
|
1832
|
+
*
|
|
1833
|
+
* The result can also be used as a second parameter for the `fromMatrix` static method
|
|
1834
|
+
* to load values into a matrix instance.
|
|
1835
|
+
*
|
|
1836
|
+
* @return {DMNS.jsonMatrix} an *Object* with all matrix values.
|
|
1837
|
+
*/
|
|
1838
|
+
toJSON() {
|
|
1839
|
+
return JSON.parse(JSON.stringify(this));
|
|
1840
|
+
}
|
|
1283
1841
|
|
|
1284
|
-
|
|
1285
|
-
|
|
1842
|
+
/**
|
|
1843
|
+
* The Multiply method returns a new CSSMatrix which is the result of this
|
|
1844
|
+
* matrix multiplied by the passed matrix, with the passed matrix to the right.
|
|
1845
|
+
* This matrix is not modified.
|
|
1846
|
+
*
|
|
1847
|
+
* @param {CSSMatrix | DOMMatrix | DMNS.jsonMatrix} m2 CSSMatrix
|
|
1848
|
+
* @return {CSSMatrix} The resulted matrix.
|
|
1849
|
+
*/
|
|
1850
|
+
multiply(m2) {
|
|
1851
|
+
// @ts-ignore - we only access [m11, m12, ... m44] values
|
|
1852
|
+
return Multiply(this, m2);
|
|
1853
|
+
}
|
|
1286
1854
|
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1855
|
+
/**
|
|
1856
|
+
* The translate method returns a new matrix which is this matrix post
|
|
1857
|
+
* multiplied by a translation matrix containing the passed values. If the z
|
|
1858
|
+
* component is undefined, a 0 value is used in its place. This matrix is not
|
|
1859
|
+
* modified.
|
|
1860
|
+
*
|
|
1861
|
+
* @param {number} x X component of the translation value.
|
|
1862
|
+
* @param {number | null} y Y component of the translation value.
|
|
1863
|
+
* @param {number | null} z Z component of the translation value.
|
|
1864
|
+
* @return {CSSMatrix} The resulted matrix
|
|
1865
|
+
*/
|
|
1866
|
+
translate(x, y, z) {
|
|
1867
|
+
const X = x;
|
|
1868
|
+
let Y = y;
|
|
1869
|
+
let Z = z;
|
|
1870
|
+
if (Z == null) Z = 0;
|
|
1871
|
+
if (Y == null) Y = 0;
|
|
1872
|
+
return Multiply(this, Translate(X, Y, Z));
|
|
1291
1873
|
}
|
|
1292
|
-
return pathArray;
|
|
1293
|
-
}
|
|
1294
1874
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1875
|
+
/**
|
|
1876
|
+
* The scale method returns a new matrix which is this matrix post multiplied by
|
|
1877
|
+
* a scale matrix containing the passed values. If the z component is undefined,
|
|
1878
|
+
* a 1 value is used in its place. If the y component is undefined, the x
|
|
1879
|
+
* component value is used in its place. This matrix is not modified.
|
|
1880
|
+
*
|
|
1881
|
+
* @param {number} x The X component of the scale value.
|
|
1882
|
+
* @param {number | null} y The Y component of the scale value.
|
|
1883
|
+
* @param {number | null} z The Z component of the scale value.
|
|
1884
|
+
* @return {CSSMatrix} The resulted matrix
|
|
1885
|
+
*/
|
|
1886
|
+
scale(x, y, z) {
|
|
1887
|
+
const X = x;
|
|
1888
|
+
let Y = y;
|
|
1889
|
+
let Z = z;
|
|
1890
|
+
if (Y == null) Y = x;
|
|
1891
|
+
if (Z == null) Z = x;
|
|
1300
1892
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
// LAF = largeArcFlag, SF = sweepFlag
|
|
1893
|
+
return Multiply(this, Scale(X, Y, Z));
|
|
1894
|
+
}
|
|
1304
1895
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1896
|
+
/**
|
|
1897
|
+
* The rotate method returns a new matrix which is this matrix post multiplied
|
|
1898
|
+
* by each of 3 rotation matrices about the major axes, first X, then Y, then Z.
|
|
1899
|
+
* If the y and z components are undefined, the x value is used to rotate the
|
|
1900
|
+
* object about the z axis, as though the vector (0,0,x) were passed. All
|
|
1901
|
+
* rotation values are in degrees. This matrix is not modified.
|
|
1902
|
+
*
|
|
1903
|
+
* @param {number} rx The X component of the rotation, or Z if Y and Z are null.
|
|
1904
|
+
* @param {number | null} ry The (optional) Y component of the rotation value.
|
|
1905
|
+
* @param {number | null} rz The (optional) Z component of the rotation value.
|
|
1906
|
+
* @return {CSSMatrix} The resulted matrix
|
|
1907
|
+
*/
|
|
1908
|
+
rotate(rx, ry, rz) {
|
|
1909
|
+
let RX = rx;
|
|
1910
|
+
let RY = ry;
|
|
1911
|
+
let RZ = rz;
|
|
1912
|
+
if (RY == null) RY = 0;
|
|
1913
|
+
if (RZ == null) { RZ = RX; RX = 0; }
|
|
1914
|
+
return Multiply(this, Rotate(RX, RY, RZ));
|
|
1915
|
+
}
|
|
1320
1916
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1917
|
+
/**
|
|
1918
|
+
* The rotateAxisAngle method returns a new matrix which is this matrix post
|
|
1919
|
+
* multiplied by a rotation matrix with the given axis and `angle`. The right-hand
|
|
1920
|
+
* rule is used to determine the direction of rotation. All rotation values are
|
|
1921
|
+
* in degrees. This matrix is not modified.
|
|
1922
|
+
*
|
|
1923
|
+
* @param {number} x The X component of the axis vector.
|
|
1924
|
+
* @param {number} y The Y component of the axis vector.
|
|
1925
|
+
* @param {number} z The Z component of the axis vector.
|
|
1926
|
+
* @param {number} angle The angle of rotation about the axis vector, in degrees.
|
|
1927
|
+
* @return {CSSMatrix} The resulted matrix
|
|
1928
|
+
*/
|
|
1929
|
+
rotateAxisAngle(x, y, z, angle) {
|
|
1930
|
+
if ([x, y, z, angle].some((n) => Number.isNaN(n))) {
|
|
1931
|
+
throw new TypeError('CSSMatrix: expecting 4 values');
|
|
1336
1932
|
}
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1933
|
+
return Multiply(this, RotateAxisAngle(x, y, z, angle));
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
/**
|
|
1937
|
+
* Specifies a skew transformation along the `x-axis` by the given angle.
|
|
1938
|
+
* This matrix is not modified.
|
|
1939
|
+
*
|
|
1940
|
+
* @param {number} angle The angle amount in degrees to skew.
|
|
1941
|
+
* @return {CSSMatrix} The resulted matrix
|
|
1942
|
+
*/
|
|
1943
|
+
skewX(angle) {
|
|
1944
|
+
return Multiply(this, SkewX(angle));
|
|
1945
|
+
}
|
|
1342
1946
|
|
|
1343
|
-
|
|
1344
|
-
|
|
1947
|
+
/**
|
|
1948
|
+
* Specifies a skew transformation along the `y-axis` by the given angle.
|
|
1949
|
+
* This matrix is not modified.
|
|
1950
|
+
*
|
|
1951
|
+
* @param {number} angle The angle amount in degrees to skew.
|
|
1952
|
+
* @return {CSSMatrix} The resulted matrix
|
|
1953
|
+
*/
|
|
1954
|
+
skewY(angle) {
|
|
1955
|
+
return Multiply(this, SkewY(angle));
|
|
1956
|
+
}
|
|
1345
1957
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1958
|
+
/**
|
|
1959
|
+
* Transforms a specified point using the matrix, returning a new
|
|
1960
|
+
* Tuple *Object* comprising of the transformed point.
|
|
1961
|
+
* Neither the matrix nor the original point are altered.
|
|
1962
|
+
*
|
|
1963
|
+
* The method is equivalent with `transformPoint()` method
|
|
1964
|
+
* of the `DOMMatrix` constructor.
|
|
1965
|
+
*
|
|
1966
|
+
* @copyright thednp © 2021
|
|
1967
|
+
*
|
|
1968
|
+
* @param {DMNS.PointTuple | DOMPoint} v Tuple or DOMPoint
|
|
1969
|
+
* @return {DMNS.PointTuple} the resulting Tuple
|
|
1970
|
+
*/
|
|
1971
|
+
transformPoint(v) {
|
|
1972
|
+
const M = this;
|
|
1973
|
+
let m = Translate(v.x, v.y, v.z);
|
|
1974
|
+
|
|
1975
|
+
m.m44 = v.w || 1;
|
|
1976
|
+
m = M.multiply(m);
|
|
1350
1977
|
|
|
1351
|
-
|
|
1352
|
-
|
|
1978
|
+
return {
|
|
1979
|
+
x: m.m41,
|
|
1980
|
+
y: m.m42,
|
|
1981
|
+
z: m.m43,
|
|
1982
|
+
w: m.m44,
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1353
1985
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1986
|
+
/**
|
|
1987
|
+
* Transforms a specified vector using the matrix, returning a new
|
|
1988
|
+
* {x,y,z,w} Tuple *Object* comprising the transformed vector.
|
|
1989
|
+
* Neither the matrix nor the original vector are altered.
|
|
1990
|
+
*
|
|
1991
|
+
* @param {DMNS.PointTuple} t Tuple with `{x,y,z,w}` components
|
|
1992
|
+
* @return {DMNS.PointTuple} the resulting Tuple
|
|
1993
|
+
*/
|
|
1994
|
+
transform(t) {
|
|
1995
|
+
const m = this;
|
|
1996
|
+
const x = m.m11 * t.x + m.m12 * t.y + m.m13 * t.z + m.m14 * t.w;
|
|
1997
|
+
const y = m.m21 * t.x + m.m22 * t.y + m.m23 * t.z + m.m24 * t.w;
|
|
1998
|
+
const z = m.m31 * t.x + m.m32 * t.y + m.m33 * t.z + m.m34 * t.w;
|
|
1999
|
+
const w = m.m41 * t.x + m.m42 * t.y + m.m43 * t.z + m.m44 * t.w;
|
|
1356
2000
|
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
}
|
|
1363
|
-
} else {
|
|
1364
|
-
const [r1, r2, r3, r4] = recursive;
|
|
1365
|
-
f1 = r1;
|
|
1366
|
-
f2 = r2;
|
|
1367
|
-
cx = r3;
|
|
1368
|
-
cy = r4;
|
|
2001
|
+
return {
|
|
2002
|
+
x: x / w,
|
|
2003
|
+
y: y / w,
|
|
2004
|
+
z: z / w,
|
|
2005
|
+
w,
|
|
2006
|
+
};
|
|
1369
2007
|
}
|
|
2008
|
+
}
|
|
1370
2009
|
|
|
1371
|
-
|
|
2010
|
+
// Add Transform Functions to CSSMatrix object
|
|
2011
|
+
CSSMatrix.Translate = Translate;
|
|
2012
|
+
CSSMatrix.Rotate = Rotate;
|
|
2013
|
+
CSSMatrix.RotateAxisAngle = RotateAxisAngle;
|
|
2014
|
+
CSSMatrix.Scale = Scale;
|
|
2015
|
+
CSSMatrix.SkewX = SkewX;
|
|
2016
|
+
CSSMatrix.SkewY = SkewY;
|
|
2017
|
+
CSSMatrix.Multiply = Multiply;
|
|
2018
|
+
CSSMatrix.fromArray = fromArray;
|
|
2019
|
+
CSSMatrix.fromMatrix = fromMatrix;
|
|
2020
|
+
CSSMatrix.fromString = fromString;
|
|
2021
|
+
CSSMatrix.Version = DMVersion;
|
|
1372
2022
|
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
2023
|
+
/**
|
|
2024
|
+
* Returns a transformation matrix to apply to `<path>` elements.
|
|
2025
|
+
*
|
|
2026
|
+
* @param {SVGPC.transformObject} transform the `transformObject`
|
|
2027
|
+
* @returns {CSSMatrix} a new transformation matrix
|
|
2028
|
+
*/
|
|
2029
|
+
function getSVGMatrix(transform) {
|
|
2030
|
+
let matrix = new CSSMatrix();
|
|
2031
|
+
const { origin } = transform;
|
|
2032
|
+
const originX = +origin[0];
|
|
2033
|
+
const originY = +origin[1];
|
|
2034
|
+
const { translate } = transform;
|
|
2035
|
+
const { rotate } = transform;
|
|
2036
|
+
const { skew } = transform;
|
|
2037
|
+
const { scale } = transform;
|
|
1377
2038
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
2039
|
+
// set translate
|
|
2040
|
+
if ((Array.isArray(translate) && translate.some((x) => +x !== 0)) || !Number.isNaN(translate)) {
|
|
2041
|
+
matrix = Array.isArray(translate)
|
|
2042
|
+
? matrix.translate(+translate[0] || 0, +translate[1] || 0, +translate[2] || 0)
|
|
2043
|
+
// @ts-ignore
|
|
2044
|
+
: matrix.translate(+translate || 0);
|
|
1382
2045
|
}
|
|
1383
2046
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
const t = Math.tan(df / 4);
|
|
1390
|
-
const hx = (4 / 3) * RX * t;
|
|
1391
|
-
const hy = (4 / 3) * RY * t;
|
|
1392
|
-
const m1 = [X1, Y1];
|
|
1393
|
-
const m2 = [X1 + hx * s1, Y1 - hy * c1];
|
|
1394
|
-
const m3 = [X2 + hx * s2, Y2 - hy * c2];
|
|
1395
|
-
const m4 = [X2, Y2];
|
|
1396
|
-
m2[0] = 2 * m1[0] - m2[0];
|
|
1397
|
-
m2[1] = 2 * m1[1] - m2[1];
|
|
2047
|
+
if (rotate || skew || scale) {
|
|
2048
|
+
// set SVG transform-origin, always defined
|
|
2049
|
+
// matrix = matrix.translate(+originX,+originY,+originZ)
|
|
2050
|
+
// @ts-ignore
|
|
2051
|
+
matrix = matrix.translate(+originX, +originY);
|
|
1398
2052
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
return rotateVector(res[i - 1], rz, rad).y;
|
|
2053
|
+
// set rotation
|
|
2054
|
+
if (rotate) {
|
|
2055
|
+
matrix = Array.isArray(rotate) && rotate.some((x) => +x !== 0)
|
|
2056
|
+
? matrix.rotate(+rotate[0] || 0, +rotate[1] || 0, +rotate[2] || 0)
|
|
2057
|
+
// @ts-ignore
|
|
2058
|
+
: matrix.rotate(+rotate || 0);
|
|
1406
2059
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
2060
|
+
// set skew(s)
|
|
2061
|
+
if (Array.isArray(skew) && skew.some((x) => +x !== 0)) {
|
|
2062
|
+
if (Array.isArray(skew)) {
|
|
2063
|
+
matrix = skew[0] ? matrix.skewX(+skew[0] || 0) : matrix;
|
|
2064
|
+
matrix = skew[1] ? matrix.skewY(+skew[1] || 0) : matrix;
|
|
2065
|
+
} else {
|
|
2066
|
+
matrix = matrix.skewX(+skew || 0);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
// set scale
|
|
2070
|
+
if (!Number.isNaN(scale) || (Array.isArray(scale) && scale.some((x) => +x !== 1))) {
|
|
2071
|
+
matrix = Array.isArray(scale)
|
|
2072
|
+
? (matrix.scale(+scale[0] || 1, +scale[1] || 1, +scale[2] || 1))
|
|
2073
|
+
// @ts-ignore
|
|
2074
|
+
: matrix.scale(+scale || 1);
|
|
2075
|
+
}
|
|
2076
|
+
// set SVG transform-origin
|
|
2077
|
+
// matrix = matrix.translate(-originX,-originY,-originZ)
|
|
2078
|
+
// @ts-ignore
|
|
2079
|
+
matrix = matrix.translate(-originX, -originY);
|
|
2080
|
+
}
|
|
2081
|
+
return matrix;
|
|
1409
2082
|
}
|
|
1410
2083
|
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
2084
|
+
/**
|
|
2085
|
+
* Apply a 2D transformation matrix to an ellipse.
|
|
2086
|
+
*
|
|
2087
|
+
* @param {number[]} m the 2D transformation matrix
|
|
2088
|
+
* @param {number} rx ellipse radius X
|
|
2089
|
+
* @param {number} ry ellipse radius Y
|
|
2090
|
+
* @param {number} ax ellipse rotation angle
|
|
2091
|
+
*/
|
|
2092
|
+
function transformEllipse(m, rx, ry, ax) {
|
|
2093
|
+
// We consider the current ellipse as image of the unit circle
|
|
2094
|
+
// by first scale(rx,ry) and then rotate(ax) ...
|
|
2095
|
+
// So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle.
|
|
2096
|
+
const c = Math.cos((ax * Math.PI) / 180);
|
|
2097
|
+
const s = Math.sin((ax * Math.PI) / 180);
|
|
2098
|
+
const ma = [
|
|
2099
|
+
rx * (m[0] * c + m[2] * s),
|
|
2100
|
+
rx * (m[1] * c + m[3] * s),
|
|
2101
|
+
ry * (-m[0] * s + m[2] * c),
|
|
2102
|
+
ry * (-m[1] * s + m[3] * c),
|
|
1420
2103
|
];
|
|
1421
|
-
}
|
|
1422
2104
|
|
|
1423
|
-
//
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
+ t1 * t1 * 3 * t * c1x
|
|
1429
|
-
+ t1 * 3 * t * t * c2x
|
|
1430
|
-
+ (t ** 3) * p2x,
|
|
1431
|
-
y: (t1 ** 3) * p1y
|
|
1432
|
-
+ t1 * t1 * 3 * t * c1y
|
|
1433
|
-
+ t1 * 3 * t * t * c2y
|
|
1434
|
-
+ (t ** 3) * p2y,
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
2105
|
+
// ma * transpose(ma) = [ J L ]
|
|
2106
|
+
// [ L K ]
|
|
2107
|
+
// L is calculated later (if the image is not a circle)
|
|
2108
|
+
const J = ma[0] * ma[0] + ma[2] * ma[2];
|
|
2109
|
+
const K = ma[1] * ma[1] + ma[3] * ma[3];
|
|
1437
2110
|
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
const bx = b[0];
|
|
1442
|
-
const by = b[1];
|
|
1443
|
-
return [ax + (bx - ax) * t, ay + (by - ay) * t];
|
|
1444
|
-
}
|
|
2111
|
+
// the discriminant of the characteristic polynomial of ma * transpose(ma)
|
|
2112
|
+
let D = ((ma[0] - ma[3]) * (ma[0] - ma[3]) + (ma[2] + ma[1]) * (ma[2] + ma[1]))
|
|
2113
|
+
* ((ma[0] + ma[3]) * (ma[0] + ma[3]) + (ma[2] - ma[1]) * (ma[2] - ma[1]));
|
|
1445
2114
|
|
|
1446
|
-
|
|
1447
|
-
const
|
|
1448
|
-
const p0 = [x1, y1];
|
|
1449
|
-
const p1 = [x2, y2];
|
|
1450
|
-
const p2 = midPoint(p0, p1, t);
|
|
1451
|
-
const p3 = midPoint(p1, p2, t);
|
|
1452
|
-
const p4 = midPoint(p2, p3, t);
|
|
1453
|
-
const p5 = midPoint(p3, p4, t);
|
|
1454
|
-
const p6 = midPoint(p4, p5, t);
|
|
1455
|
-
const cp1 = getPointAtSegLength.apply(0, p0.concat(p2, p4, p6, t));
|
|
1456
|
-
const cp2 = getPointAtSegLength.apply(0, p6.concat(p5, p3, p1, 0));
|
|
2115
|
+
// the "mean eigenvalue"
|
|
2116
|
+
const JK = (J + K) / 2;
|
|
1457
2117
|
|
|
1458
|
-
|
|
1459
|
-
|
|
2118
|
+
// check if the image is (almost) a circle
|
|
2119
|
+
if (D < epsilon * JK) {
|
|
2120
|
+
// if it is
|
|
2121
|
+
const rxy = Math.sqrt(JK);
|
|
1460
2122
|
|
|
1461
|
-
|
|
1462
|
-
if ('TQ'.indexOf(segment[0]) < 0) {
|
|
1463
|
-
params.qx = null;
|
|
1464
|
-
params.qy = null;
|
|
2123
|
+
return { rx: rxy, ry: rxy, ax: 0 };
|
|
1465
2124
|
}
|
|
1466
2125
|
|
|
1467
|
-
|
|
2126
|
+
// if it is not a circle
|
|
2127
|
+
const L = ma[0] * ma[1] + ma[2] * ma[3];
|
|
1468
2128
|
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
2129
|
+
D = Math.sqrt(D);
|
|
2130
|
+
|
|
2131
|
+
// {l1,l2} = the two eigen values of ma * transpose(ma)
|
|
2132
|
+
const l1 = JK + D / 2;
|
|
2133
|
+
const l2 = JK - D / 2;
|
|
2134
|
+
// the x - axis - rotation angle is the argument of the l1 - eigenvector
|
|
2135
|
+
let AX = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon) ? 90
|
|
2136
|
+
: Math.atan(Math.abs(L) > Math.abs(l1 - K) ? (l1 - J) / L
|
|
2137
|
+
: ((L / (l1 - K))) * 180) / Math.PI;
|
|
2138
|
+
let RX;
|
|
2139
|
+
let RY;
|
|
2140
|
+
|
|
2141
|
+
// if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90
|
|
2142
|
+
if (AX >= 0) {
|
|
2143
|
+
// if ax in [0,90]
|
|
2144
|
+
RX = Math.sqrt(l1);
|
|
2145
|
+
RY = Math.sqrt(l2);
|
|
2146
|
+
} else {
|
|
2147
|
+
// if ax in ]-90,0[ => exchange axes
|
|
2148
|
+
AX += 90;
|
|
2149
|
+
RX = Math.sqrt(l2);
|
|
2150
|
+
RY = Math.sqrt(l1);
|
|
1484
2151
|
}
|
|
1485
|
-
|
|
2152
|
+
|
|
2153
|
+
return { rx: RX, ry: RY, ax: AX };
|
|
1486
2154
|
}
|
|
1487
2155
|
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
2156
|
+
/**
|
|
2157
|
+
* Returns the [x,y] projected coordinates for a given an [x,y] point
|
|
2158
|
+
* and an [x,y,z] perspective origin point.
|
|
2159
|
+
*
|
|
2160
|
+
* Equation found here =>
|
|
2161
|
+
* http://en.wikipedia.org/wiki/3D_projection#Diagram
|
|
2162
|
+
* Details =>
|
|
2163
|
+
* https://stackoverflow.com/questions/23792505/predicted-rendering-of-css-3d-transformed-pixel
|
|
2164
|
+
*
|
|
2165
|
+
* @param {SVGPC.CSSMatrix} m the transformation matrix
|
|
2166
|
+
* @param {Number[]} point2D the initial [x,y] coordinates
|
|
2167
|
+
* @param {number[]} origin the initial [x,y] coordinates
|
|
2168
|
+
* @returns {Number[]} the projected [x,y] coordinates
|
|
2169
|
+
*/
|
|
2170
|
+
function projection2d(m, point2D, origin) {
|
|
2171
|
+
const point3D = m.transformPoint({
|
|
2172
|
+
x: point2D[0], y: point2D[1], z: 0, w: 1,
|
|
2173
|
+
});
|
|
2174
|
+
const originX = origin[0] || 0;
|
|
2175
|
+
const originY = origin[1] || 0;
|
|
2176
|
+
const originZ = origin[2] || 0;
|
|
2177
|
+
const relativePositionX = point3D.x - originX;
|
|
2178
|
+
const relativePositionY = point3D.y - originY;
|
|
2179
|
+
const relativePositionZ = point3D.z - originZ;
|
|
2180
|
+
|
|
2181
|
+
return [
|
|
2182
|
+
relativePositionX * (Math.abs(originZ) / Math.abs(relativePositionZ)) + originX,
|
|
2183
|
+
relativePositionY * (Math.abs(originZ) / Math.abs(relativePositionZ)) + originY,
|
|
2184
|
+
];
|
|
2185
|
+
}
|
|
1492
2186
|
|
|
1493
|
-
|
|
2187
|
+
/**
|
|
2188
|
+
* Apply a 2D / 3D transformation to a `pathArray` instance.
|
|
2189
|
+
*
|
|
2190
|
+
* Since *SVGElement* doesn't support 3D transformation, this function
|
|
2191
|
+
* creates a 2D projection of the <path> element.
|
|
2192
|
+
*
|
|
2193
|
+
* @param {SVGPC.pathArray} path the `pathArray` to apply transformation
|
|
2194
|
+
* @param {any} transform the transform functions `Object`
|
|
2195
|
+
* @returns {SVGPC.pathArray} the resulted `pathArray`
|
|
2196
|
+
*/
|
|
2197
|
+
function transformPath(path, transform) {
|
|
2198
|
+
let x = 0; let y = 0; let i; let j; let ii; let jj; let lx; let ly; let te;
|
|
2199
|
+
const absolutePath = pathToAbsolute(path);
|
|
2200
|
+
const normalizedPath = normalizePath(absolutePath);
|
|
2201
|
+
const matrixInstance = getSVGMatrix(transform);
|
|
2202
|
+
const transformProps = Object.keys(transform);
|
|
2203
|
+
const { origin } = transform;
|
|
2204
|
+
const {
|
|
2205
|
+
a, b, c, d, e, f,
|
|
2206
|
+
} = matrixInstance;
|
|
2207
|
+
const matrix2d = [a, b, c, d, e, f];
|
|
1494
2208
|
const params = {
|
|
1495
2209
|
x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null,
|
|
1496
2210
|
};
|
|
1497
|
-
|
|
2211
|
+
let segment = [];
|
|
2212
|
+
let seglen = 0;
|
|
1498
2213
|
let pathCommand = '';
|
|
1499
|
-
|
|
1500
|
-
let
|
|
1501
|
-
|
|
2214
|
+
/** @type {any} */
|
|
2215
|
+
let transformedPath = [];
|
|
2216
|
+
const allPathCommands = []; // needed for arc to curve transformation
|
|
2217
|
+
let result = [];
|
|
1502
2218
|
|
|
1503
|
-
|
|
1504
|
-
|
|
2219
|
+
if (!matrixInstance.isIdentity) {
|
|
2220
|
+
for (i = 0, ii = absolutePath.length; i < ii; i += 1) {
|
|
2221
|
+
segment = absolutePath[i];
|
|
1505
2222
|
|
|
1506
|
-
|
|
1507
|
-
pathArray[i] = segmentToCubic(pathArray[i], params);
|
|
2223
|
+
if (absolutePath[i]) [pathCommand] = segment;
|
|
1508
2224
|
|
|
1509
|
-
|
|
1510
|
-
|
|
2225
|
+
// REPLACE Arc path commands with Cubic Beziers
|
|
2226
|
+
// we don't have any scripting know-how on 3d ellipse transformation
|
|
2227
|
+
/// ////////////////////////////////////////
|
|
2228
|
+
allPathCommands[i] = pathCommand;
|
|
1511
2229
|
|
|
1512
|
-
|
|
1513
|
-
|
|
2230
|
+
// Arcs don't work very well with 3D transformations or skews
|
|
2231
|
+
if (pathCommand === 'A' && (!matrixInstance.is2D || !['skewX', 'skewY'].find((p) => transformProps.includes(p)))) {
|
|
2232
|
+
segment = segmentToCubic(normalizedPath[i], params);
|
|
1514
2233
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
params.x2 = +(segment[seglen - 4]) || params.x1;
|
|
1518
|
-
params.y2 = +(segment[seglen - 3]) || params.y1;
|
|
1519
|
-
}
|
|
2234
|
+
absolutePath[i] = segmentToCubic(normalizedPath[i], params);
|
|
2235
|
+
fixArc(absolutePath, allPathCommands, i);
|
|
1520
2236
|
|
|
1521
|
-
|
|
1522
|
-
|
|
2237
|
+
normalizedPath[i] = segmentToCubic(normalizedPath[i], params);
|
|
2238
|
+
fixArc(normalizedPath, allPathCommands, i);
|
|
2239
|
+
ii = Math.max(absolutePath.length, normalizedPath.length);
|
|
2240
|
+
}
|
|
2241
|
+
/// ////////////////////////////////////////
|
|
1523
2242
|
|
|
1524
|
-
|
|
2243
|
+
segment = normalizedPath[i];
|
|
2244
|
+
seglen = segment.length;
|
|
1525
2245
|
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
}
|
|
2246
|
+
params.x1 = +segment[seglen - 2];
|
|
2247
|
+
params.y1 = +segment[seglen - 1];
|
|
2248
|
+
params.x2 = +(segment[seglen - 4]) || params.x1;
|
|
2249
|
+
params.y2 = +(segment[seglen - 3]) || params.y1;
|
|
2250
|
+
// @ts-ignore
|
|
2251
|
+
result = { s: absolutePath[i], c: absolutePath[i][0] };
|
|
1532
2252
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
my = seg[0] === 'M' ? seg[2] : my;
|
|
1542
|
-
x = mx;
|
|
1543
|
-
y = my;
|
|
1544
|
-
return 0;
|
|
1545
|
-
default:
|
|
1546
|
-
len = getCubicSegArea.apply(0, [x, y].concat(seg.slice(1)));
|
|
1547
|
-
[x, y] = seg.slice(-2);
|
|
1548
|
-
return len;
|
|
2253
|
+
if (pathCommand !== 'Z') {
|
|
2254
|
+
// @ts-ignore
|
|
2255
|
+
result.x = params.x1;
|
|
2256
|
+
// @ts-ignore
|
|
2257
|
+
result.y = params.y1;
|
|
2258
|
+
}
|
|
2259
|
+
// @ts-ignore
|
|
2260
|
+
transformedPath = transformedPath.concat(result);
|
|
1549
2261
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
// returns the cubic bezier segment length
|
|
1560
|
-
function getSegCubicLength(x1, y1, x2, y2, x3, y3, x4, y4, z) {
|
|
1561
|
-
let Z;
|
|
1562
|
-
if (z === null || Number.isNaN(+z)) Z = 1;
|
|
2262
|
+
// @ts-ignore
|
|
2263
|
+
return transformedPath.map((seg) => {
|
|
2264
|
+
pathCommand = seg.c;
|
|
2265
|
+
segment = seg.s;
|
|
2266
|
+
switch (pathCommand) {
|
|
2267
|
+
case 'A': // only apply to 2D transformations
|
|
2268
|
+
te = transformEllipse(matrix2d, segment[1], segment[2], segment[3]);
|
|
1563
2269
|
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
2270
|
+
if (matrix2d[0] * matrix2d[3] - matrix2d[1] * matrix2d[2] < 0) {
|
|
2271
|
+
segment[5] = +segment[5] ? 0 : 1;
|
|
2272
|
+
}
|
|
1567
2273
|
|
|
1568
|
-
|
|
1569
|
-
const Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678,
|
|
1570
|
-
-0.5873, 0.5873, -0.7699, 0.7699,
|
|
1571
|
-
-0.9041, 0.9041, -0.9816, 0.9816];
|
|
1572
|
-
const Cvalues = [0.2491, 0.2491, 0.2335, 0.2335,
|
|
1573
|
-
0.2032, 0.2032, 0.1601, 0.1601,
|
|
1574
|
-
0.1069, 0.1069, 0.0472, 0.0472];
|
|
2274
|
+
[lx, ly] = projection2d(matrixInstance, [segment[6], segment[7]], origin);
|
|
1575
2275
|
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
});
|
|
1582
|
-
return z2 * sum;
|
|
1583
|
-
}
|
|
2276
|
+
if ((x === lx && y === ly) || (te.rx < epsilon * te.ry) || (te.ry < epsilon * te.rx)) {
|
|
2277
|
+
segment = ['L', lx, ly];
|
|
2278
|
+
} else {
|
|
2279
|
+
segment = [pathCommand, te.rx, te.ry, te.ax, segment[4], segment[5], lx, ly];
|
|
2280
|
+
}
|
|
1584
2281
|
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
// pathToCurve version
|
|
1588
|
-
function getPathLength(pathArray) {
|
|
1589
|
-
let totalLength = 0;
|
|
1590
|
-
pathToCurve(pathArray).forEach((s, i, curveArray) => {
|
|
1591
|
-
totalLength += s[0] !== 'M' ? getSegCubicLength.apply(0, curveArray[i - 1].slice(-2).concat(s.slice(1))) : 0;
|
|
1592
|
-
});
|
|
1593
|
-
return totalLength;
|
|
1594
|
-
}
|
|
2282
|
+
x = lx; y = ly;
|
|
2283
|
+
return segment;
|
|
1595
2284
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
2285
|
+
case 'L':
|
|
2286
|
+
case 'H':
|
|
2287
|
+
case 'V':
|
|
1599
2288
|
|
|
1600
|
-
|
|
1601
|
-
// almost equivalent to shape.getTotalLength()
|
|
1602
|
-
function getPointAtLength(pathArray, length) {
|
|
1603
|
-
let totalLength = 0;
|
|
1604
|
-
let segLen;
|
|
1605
|
-
let data;
|
|
1606
|
-
let result;
|
|
2289
|
+
[lx, ly] = projection2d(matrixInstance, [seg.x, seg.y], origin);
|
|
1607
2290
|
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
2291
|
+
if (x !== lx && y !== ly) {
|
|
2292
|
+
segment = ['L', lx, ly];
|
|
2293
|
+
} else if (y === ly) {
|
|
2294
|
+
segment = ['H', lx];
|
|
2295
|
+
} else if (x === lx) {
|
|
2296
|
+
segment = ['V', ly];
|
|
2297
|
+
}
|
|
1612
2298
|
|
|
1613
|
-
|
|
1614
|
-
result = { x: data[0], y: data[1] };
|
|
1615
|
-
} else if (totalLength > length && length > totalLength - segLen) {
|
|
1616
|
-
result = getPointAtSegLength.apply(0, data.concat(1 - (totalLength - length) / segLen));
|
|
1617
|
-
} else {
|
|
1618
|
-
result = null;
|
|
1619
|
-
}
|
|
2299
|
+
x = lx; y = ly; // now update x and y
|
|
1620
2300
|
|
|
1621
|
-
|
|
1622
|
-
|
|
2301
|
+
return segment;
|
|
2302
|
+
default:
|
|
2303
|
+
for (j = 1, jj = segment.length; j < jj; j += 2) {
|
|
2304
|
+
// compute line coordinates without altering previous coordinates
|
|
2305
|
+
[x, y] = projection2d(matrixInstance, [segment[j], segment[j + 1]], origin);
|
|
2306
|
+
segment[j] = x;
|
|
2307
|
+
segment[j + 1] = y;
|
|
2308
|
+
}
|
|
2309
|
+
return segment;
|
|
2310
|
+
}
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
return clonePath(absolutePath);
|
|
1623
2314
|
}
|
|
1624
2315
|
|
|
1625
|
-
|
|
2316
|
+
/**
|
|
2317
|
+
* Returns the cubic-bezier segment length.
|
|
2318
|
+
*
|
|
2319
|
+
* @param {number} p1x the starting point X
|
|
2320
|
+
* @param {number} p1y the starting point Y
|
|
2321
|
+
* @param {number} c1x the first control point X
|
|
2322
|
+
* @param {number} c1y the first control point Y
|
|
2323
|
+
* @param {number} c2x the second control point X
|
|
2324
|
+
* @param {number} c2y the second control point Y
|
|
2325
|
+
* @param {number} p2x the ending point X
|
|
2326
|
+
* @param {number} p2y the ending point Y
|
|
2327
|
+
* @returns {SVGPC.segmentLimits} the length of the cubic-bezier segment
|
|
2328
|
+
*/
|
|
1626
2329
|
function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
1627
2330
|
let a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x);
|
|
1628
2331
|
let b = 2 * (c1x - p1x) - 2 * (c2x - c1x);
|
|
@@ -1632,8 +2335,9 @@ function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
1632
2335
|
const y = [p1y, p2y];
|
|
1633
2336
|
const x = [p1x, p2x];
|
|
1634
2337
|
let dot;
|
|
1635
|
-
|
|
2338
|
+
// @ts-ignore
|
|
1636
2339
|
if (Math.abs(t1) > '1e12') t1 = 0.5;
|
|
2340
|
+
// @ts-ignore
|
|
1637
2341
|
if (Math.abs(t2) > '1e12') t2 = 0.5;
|
|
1638
2342
|
|
|
1639
2343
|
if (t1 > 0 && t1 < 1) {
|
|
@@ -1651,8 +2355,9 @@ function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
1651
2355
|
c = p1y - c1y;
|
|
1652
2356
|
t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
1653
2357
|
t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
|
|
1654
|
-
|
|
2358
|
+
// @ts-ignore
|
|
1655
2359
|
if (Math.abs(t1) > '1e12') t1 = 0.5;
|
|
2360
|
+
// @ts-ignore
|
|
1656
2361
|
if (Math.abs(t2) > '1e12') t2 = 0.5;
|
|
1657
2362
|
|
|
1658
2363
|
if (t1 > 0 && t1 < 1) {
|
|
@@ -1671,549 +2376,532 @@ function getCubicSize(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
|
|
|
1671
2376
|
};
|
|
1672
2377
|
}
|
|
1673
2378
|
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
let X = [];
|
|
1685
|
-
let Y = [];
|
|
1686
|
-
|
|
1687
|
-
pathCurve.forEach((segment) => {
|
|
1688
|
-
const [s1, s2] = segment.slice(-2);
|
|
1689
|
-
if (segment[0] === 'M') {
|
|
1690
|
-
x = s1;
|
|
1691
|
-
y = s2;
|
|
1692
|
-
X.push(s1);
|
|
1693
|
-
Y.push(s2);
|
|
1694
|
-
} else {
|
|
1695
|
-
const dim = getCubicSize.apply(0, [x, y].concat(segment.slice(1)));
|
|
1696
|
-
X = X.concat(dim.min.x, dim.max.x);
|
|
1697
|
-
Y = Y.concat(dim.min.y, dim.max.y);
|
|
1698
|
-
x = s1;
|
|
1699
|
-
y = s2;
|
|
1700
|
-
}
|
|
1701
|
-
});
|
|
1702
|
-
|
|
1703
|
-
const xTop = Math.min.apply(0, X);
|
|
1704
|
-
const yTop = Math.min.apply(0, Y);
|
|
1705
|
-
const xBot = Math.max.apply(0, X);
|
|
1706
|
-
const yBot = Math.max.apply(0, Y);
|
|
1707
|
-
const width = xBot - xTop;
|
|
1708
|
-
const height = yBot - yTop;
|
|
1709
|
-
|
|
1710
|
-
return {
|
|
1711
|
-
x: xTop,
|
|
1712
|
-
y: yTop,
|
|
1713
|
-
x2: xBot,
|
|
1714
|
-
y2: yBot,
|
|
1715
|
-
width,
|
|
1716
|
-
height,
|
|
1717
|
-
cx: xTop + width / 2,
|
|
1718
|
-
cy: yTop + height / 2,
|
|
1719
|
-
};
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
function isRelativeArray(pathInput) {
|
|
1723
|
-
return isPathArray(pathInput)
|
|
1724
|
-
&& pathInput.slice(1).every((seg) => seg[0] === seg[0].toLowerCase());
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
function roundPath(pathArray, round) {
|
|
1728
|
-
const decimalsOption = !Number.isNaN(+round) ? +round : SVGPCO.round && SVGPCO.decimals;
|
|
1729
|
-
let result;
|
|
1730
|
-
|
|
1731
|
-
if (decimalsOption) {
|
|
1732
|
-
result = pathArray.map((seg) => seg.map((c) => {
|
|
1733
|
-
const nr = +c;
|
|
1734
|
-
const dc = 10 ** decimalsOption;
|
|
1735
|
-
if (nr) {
|
|
1736
|
-
return nr % 1 === 0 ? nr : Math.round(nr * dc) / dc;
|
|
1737
|
-
}
|
|
1738
|
-
return c;
|
|
1739
|
-
}));
|
|
1740
|
-
} else {
|
|
1741
|
-
result = clonePath(pathArray);
|
|
1742
|
-
}
|
|
1743
|
-
return result;
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
function pathToString(pathArray, round) {
|
|
1747
|
-
return roundPath(pathArray, round)
|
|
1748
|
-
.map((x) => x[0].concat(x.slice(1).join(' '))).join('');
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
function splitPath(pathInput) {
|
|
1752
|
-
return pathToString(pathToAbsolute(pathInput))
|
|
1753
|
-
.replace(/(m|M)/g, '|$1')
|
|
1754
|
-
.split('|')
|
|
1755
|
-
.map((s) => s.trim())
|
|
1756
|
-
.filter((s) => s);
|
|
2379
|
+
/**
|
|
2380
|
+
* Iterates an array to check if it's a `pathArray`
|
|
2381
|
+
* with all C (cubic bezier) segments.
|
|
2382
|
+
*
|
|
2383
|
+
* @param {string | SVGPC.pathArray} path the `Array` to be checked
|
|
2384
|
+
* @returns {boolean} iteration result
|
|
2385
|
+
*/
|
|
2386
|
+
function isCurveArray(path) {
|
|
2387
|
+
return Array.isArray(path) && isPathArray(path)
|
|
2388
|
+
&& path.slice(1).every((seg) => seg[0] === 'C');
|
|
1757
2389
|
}
|
|
1758
2390
|
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
let my = 0;
|
|
1771
|
-
let start = 0;
|
|
1772
|
-
|
|
1773
|
-
if (pathArray[0][0] === 'M') {
|
|
1774
|
-
x = +pathArray[0][1];
|
|
1775
|
-
y = +pathArray[0][2];
|
|
1776
|
-
mx = x;
|
|
1777
|
-
my = y;
|
|
1778
|
-
start += 1;
|
|
1779
|
-
resultArray.push(['M', x, y]);
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
for (let i = start; i < ii; i += 1) {
|
|
1783
|
-
const segment = pathArray[i];
|
|
1784
|
-
const [pathCommand] = segment;
|
|
1785
|
-
const relativeCommand = pathCommand.toLowerCase();
|
|
1786
|
-
const relativeSegment = [];
|
|
1787
|
-
let newSeg = [];
|
|
1788
|
-
resultArray.push(relativeSegment);
|
|
2391
|
+
/**
|
|
2392
|
+
* Parses a path string value or 'pathArray' and returns a new one
|
|
2393
|
+
* in which all segments are converted to cubic-bezier.
|
|
2394
|
+
*
|
|
2395
|
+
* @param {string | SVGPC.pathArray} pathInput the string to be parsed or object
|
|
2396
|
+
* @returns {SVGPC.pathArray} the resulted `pathArray` converted to cubic-bezier
|
|
2397
|
+
*/
|
|
2398
|
+
function pathToCurve(pathInput) {
|
|
2399
|
+
if (Array.isArray(pathInput) && isCurveArray(pathInput)) {
|
|
2400
|
+
return clonePath(pathInput);
|
|
2401
|
+
}
|
|
1789
2402
|
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
2403
|
+
const path = normalizePath(pathInput);
|
|
2404
|
+
const params = {
|
|
2405
|
+
x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0, qx: null, qy: null,
|
|
2406
|
+
};
|
|
2407
|
+
const allPathCommands = [];
|
|
2408
|
+
let pathCommand = '';
|
|
2409
|
+
let ii = path.length;
|
|
1795
2410
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
case 'v':
|
|
1801
|
-
relativeSegment[1] = +segment[1] - y;
|
|
1802
|
-
break;
|
|
1803
|
-
default:
|
|
1804
|
-
// for is here to stay for eslint
|
|
1805
|
-
for (let j = 1; j < segment.length; j += 1) {
|
|
1806
|
-
relativeSegment.push(+segment[j] - (j % 2 ? x : y));
|
|
1807
|
-
}
|
|
2411
|
+
for (let i = 0; i < ii; i += 1) {
|
|
2412
|
+
const segment = path[i];
|
|
2413
|
+
const seglen = segment.length;
|
|
2414
|
+
if (segment) [pathCommand] = segment;
|
|
1808
2415
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
my = +segment[2];
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
} else {
|
|
1815
|
-
if (pathCommand === 'm') {
|
|
1816
|
-
mx = +segment[1] + x;
|
|
1817
|
-
my = +segment[2] + y;
|
|
1818
|
-
}
|
|
1819
|
-
for (let j = 0; j < segment.length; j += 1) {
|
|
1820
|
-
relativeSegment.push(segment[j]);
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
2416
|
+
allPathCommands[i] = pathCommand;
|
|
2417
|
+
path[i] = segmentToCubic(segment, params);
|
|
1823
2418
|
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
x += relativeSegment[segLength - 1];
|
|
1832
|
-
break;
|
|
1833
|
-
case 'v':
|
|
1834
|
-
y += relativeSegment[segLength - 1];
|
|
1835
|
-
break;
|
|
1836
|
-
default:
|
|
1837
|
-
x += resultArray[i][segLength - 2];
|
|
1838
|
-
y += resultArray[i][segLength - 1];
|
|
1839
|
-
}
|
|
2419
|
+
fixArc(path, allPathCommands, i);
|
|
2420
|
+
ii = path.length; // solves curveArrays ending in Z
|
|
2421
|
+
|
|
2422
|
+
params.x1 = +segment[seglen - 2];
|
|
2423
|
+
params.y1 = +segment[seglen - 1];
|
|
2424
|
+
params.x2 = +(segment[seglen - 4]) || params.x1;
|
|
2425
|
+
params.y2 = +(segment[seglen - 3]) || params.y1;
|
|
1840
2426
|
}
|
|
1841
2427
|
|
|
1842
|
-
return
|
|
2428
|
+
return path;
|
|
1843
2429
|
}
|
|
1844
2430
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
2431
|
+
/**
|
|
2432
|
+
* Returns the bounding box of a shape.
|
|
2433
|
+
*
|
|
2434
|
+
* @param {SVGPC.pathArray} path the shape `pathArray`
|
|
2435
|
+
* @returns {SVGPC.pathBBox} the length of the cubic-bezier segment
|
|
2436
|
+
*/
|
|
2437
|
+
function getPathBBox(path) {
|
|
2438
|
+
if (!path) {
|
|
2439
|
+
return {
|
|
2440
|
+
x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0, cx: 0, cy: 0,
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
const pathCurve = pathToCurve(path);
|
|
2444
|
+
// @ts-ignore
|
|
2445
|
+
let x = 0; let y = 0; let X = []; let Y = [];
|
|
2446
|
+
|
|
2447
|
+
pathCurve.forEach((segment) => {
|
|
2448
|
+
const [s1, s2] = segment.slice(-2);
|
|
2449
|
+
if (segment[0] === 'M') {
|
|
2450
|
+
x = +s1;
|
|
2451
|
+
y = +s2;
|
|
2452
|
+
X.push(s1);
|
|
2453
|
+
Y.push(s2);
|
|
2454
|
+
} else {
|
|
2455
|
+
// @ts-ignore
|
|
2456
|
+
const dim = getCubicSize.apply(0, [x, y].concat(segment.slice(1)));
|
|
2457
|
+
// @ts-ignore
|
|
2458
|
+
X = X.concat(dim.min.x, dim.max.x);
|
|
2459
|
+
// @ts-ignore
|
|
2460
|
+
Y = Y.concat(dim.min.y, dim.max.y);
|
|
2461
|
+
x = +s1;
|
|
2462
|
+
y = +s2;
|
|
1851
2463
|
}
|
|
1852
|
-
return x;
|
|
1853
2464
|
});
|
|
1854
|
-
}
|
|
1855
2465
|
|
|
1856
|
-
//
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2466
|
+
// @ts-ignore
|
|
2467
|
+
const xTop = Math.min.apply(0, X);
|
|
2468
|
+
// @ts-ignore
|
|
2469
|
+
const yTop = Math.min.apply(0, Y);
|
|
2470
|
+
// @ts-ignore
|
|
2471
|
+
const xBot = Math.max.apply(0, X);
|
|
2472
|
+
// @ts-ignore
|
|
2473
|
+
const yBot = Math.max.apply(0, Y);
|
|
2474
|
+
const width = xBot - xTop;
|
|
2475
|
+
const height = yBot - yTop;
|
|
1864
2476
|
|
|
1865
|
-
return
|
|
1866
|
-
|
|
1867
|
-
|
|
2477
|
+
return {
|
|
2478
|
+
width,
|
|
2479
|
+
height,
|
|
2480
|
+
x: xTop,
|
|
2481
|
+
y: yTop,
|
|
2482
|
+
x2: xBot,
|
|
2483
|
+
y2: yBot,
|
|
2484
|
+
cx: xTop + width / 2,
|
|
2485
|
+
cy: yTop + height / 2,
|
|
2486
|
+
};
|
|
1868
2487
|
}
|
|
1869
2488
|
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
const pathCommand = seg.c;
|
|
1891
|
-
const pLen = pathArray.length;
|
|
1892
|
-
const x = i ? pathArray[i - 1].x : pathArray[pLen - 1].x;
|
|
1893
|
-
const y = i ? pathArray[i - 1].y : pathArray[pLen - 1].y;
|
|
1894
|
-
let result = [];
|
|
2489
|
+
/**
|
|
2490
|
+
* Returns the area of a single segment shape.
|
|
2491
|
+
*
|
|
2492
|
+
* http://objectmix.com/graphics/133553-area-closed-bezier-curve.html
|
|
2493
|
+
*
|
|
2494
|
+
* @param {number} x0 the starting point X
|
|
2495
|
+
* @param {number} y0 the starting point Y
|
|
2496
|
+
* @param {number} x1 the first control point X
|
|
2497
|
+
* @param {number} y1 the first control point Y
|
|
2498
|
+
* @param {number} x2 the second control point X
|
|
2499
|
+
* @param {number} y2 the second control point Y
|
|
2500
|
+
* @param {number} x3 the ending point X
|
|
2501
|
+
* @param {number} y3 the ending point Y
|
|
2502
|
+
* @returns {number} the area of the cubic-bezier segment
|
|
2503
|
+
*/
|
|
2504
|
+
function getCubicSegArea(x0, y0, x1, y1, x2, y2, x3, y3) {
|
|
2505
|
+
return (3 * ((y3 - y0) * (x1 + x2) - (x3 - x0) * (y1 + y2)
|
|
2506
|
+
+ (y1 * (x0 - x2)) - (x1 * (y0 - y2))
|
|
2507
|
+
+ (y3 * (x2 + x0 / 3)) - (x3 * (y2 + y0 / 3)))) / 20;
|
|
2508
|
+
}
|
|
1895
2509
|
|
|
1896
|
-
|
|
2510
|
+
/**
|
|
2511
|
+
* Returns the area of a shape.
|
|
2512
|
+
* @author Jürg Lehni & Jonathan Puckey
|
|
2513
|
+
*
|
|
2514
|
+
* => https://github.com/paperjs/paper.js/blob/develop/src/path/Path.js
|
|
2515
|
+
*
|
|
2516
|
+
* @param {SVGPC.pathArray} path the shape `pathArray`
|
|
2517
|
+
* @returns {number} the length of the cubic-bezier segment
|
|
2518
|
+
*/
|
|
2519
|
+
function getPathArea(path) {
|
|
2520
|
+
let x = 0; let y = 0;
|
|
2521
|
+
let mx = 0; let my = 0;
|
|
2522
|
+
let len = 0;
|
|
2523
|
+
return pathToCurve(path).map((seg) => {
|
|
2524
|
+
switch (seg[0]) {
|
|
1897
2525
|
case 'M':
|
|
1898
|
-
result = isClosed ? ['Z'] : [pathCommand, x, y];
|
|
1899
|
-
break;
|
|
1900
|
-
case 'A':
|
|
1901
|
-
result = segment.slice(0, -3).concat([(segment[5] === 1 ? 0 : 1), x, y]);
|
|
1902
|
-
break;
|
|
1903
|
-
case 'C':
|
|
1904
|
-
if (nextSeg && nextSeg.c === 'S') {
|
|
1905
|
-
result = ['S', segment[1], segment[2], x, y];
|
|
1906
|
-
} else {
|
|
1907
|
-
result = [pathCommand, segment[3], segment[4], segment[1], segment[2], x, y];
|
|
1908
|
-
}
|
|
1909
|
-
break;
|
|
1910
|
-
case 'S':
|
|
1911
|
-
if ((prevSeg && 'CS'.indexOf(prevSeg.c) > -1) && (!nextSeg || (nextSeg && nextSeg.c !== 'S'))) {
|
|
1912
|
-
result = ['C', data[3], data[4], data[1], data[2], x, y];
|
|
1913
|
-
} else {
|
|
1914
|
-
result = [pathCommand, data[1], data[2], x, y];
|
|
1915
|
-
}
|
|
1916
|
-
break;
|
|
1917
|
-
case 'Q':
|
|
1918
|
-
if (nextSeg && nextSeg.c === 'T') {
|
|
1919
|
-
result = ['T', x, y];
|
|
1920
|
-
} else {
|
|
1921
|
-
result = segment.slice(0, -2).concat([x, y]);
|
|
1922
|
-
}
|
|
1923
|
-
break;
|
|
1924
|
-
case 'T':
|
|
1925
|
-
if ((prevSeg && 'QT'.indexOf(prevSeg.c) > -1) && (!nextSeg || (nextSeg && nextSeg.c !== 'T'))) {
|
|
1926
|
-
result = ['Q', data[1], data[2], x, y];
|
|
1927
|
-
} else {
|
|
1928
|
-
result = [pathCommand, x, y];
|
|
1929
|
-
}
|
|
1930
|
-
break;
|
|
1931
2526
|
case 'Z':
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
case 'V':
|
|
1938
|
-
result = [pathCommand, y];
|
|
1939
|
-
break;
|
|
2527
|
+
mx = seg[0] === 'M' ? seg[1] : mx;
|
|
2528
|
+
my = seg[0] === 'M' ? seg[2] : my;
|
|
2529
|
+
x = mx;
|
|
2530
|
+
y = my;
|
|
2531
|
+
return 0;
|
|
1940
2532
|
default:
|
|
1941
|
-
|
|
2533
|
+
// @ts-ignore
|
|
2534
|
+
len = getCubicSegArea.apply(0, [x, y].concat(seg.slice(1)));
|
|
2535
|
+
// @ts-ignore
|
|
2536
|
+
[x, y] = seg.slice(-2);
|
|
2537
|
+
return len;
|
|
1942
2538
|
}
|
|
1943
|
-
|
|
1944
|
-
return result;
|
|
1945
|
-
});
|
|
1946
|
-
|
|
1947
|
-
return isClosed ? reversedPath.reverse()
|
|
1948
|
-
: [reversedPath[0]].concat(reversedPath.slice(1).reverse());
|
|
2539
|
+
}).reduce((a, b) => a + b, 0);
|
|
1949
2540
|
}
|
|
1950
2541
|
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
const
|
|
1961
|
-
const
|
|
1962
|
-
|
|
1963
|
-
const { scale } = transformObject;
|
|
1964
|
-
|
|
1965
|
-
// !isNaN(perspective) && perspective && (matrix.m34 = -1/perspective)
|
|
1966
|
-
|
|
1967
|
-
// set translate
|
|
1968
|
-
if (!Number.isNaN(translate) || (Array.isArray(translate) && translate.some((x) => +x !== 0))) {
|
|
1969
|
-
matrix = Array.isArray(translate)
|
|
1970
|
-
? matrix.translate(+translate[0] || 0, +translate[1] || 0, +translate[2] || 0)
|
|
1971
|
-
: matrix.translate(+translate || 0, 0, 0);
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
|
-
if (rotate || skew || scale) {
|
|
1975
|
-
// set SVG transform-origin, always defined
|
|
1976
|
-
// matrix = matrix.translate(+originX,+originY,+originZ)
|
|
1977
|
-
matrix = matrix.translate(+originX, +originY);
|
|
1978
|
-
|
|
1979
|
-
// set rotation
|
|
1980
|
-
if (rotate) {
|
|
1981
|
-
matrix = Array.isArray(rotate) && rotate.some((x) => +x !== 0)
|
|
1982
|
-
? matrix.rotate(+rotate[0] || 0, +rotate[1] || 0, +rotate[2] || 0)
|
|
1983
|
-
: matrix.rotate(+rotate || 0);
|
|
1984
|
-
}
|
|
1985
|
-
// set skew(s)
|
|
1986
|
-
if (Array.isArray(skew) && skew.some((x) => +x !== 0)) {
|
|
1987
|
-
if (Array.isArray(skew)) {
|
|
1988
|
-
matrix = skew[0] ? matrix.skewX(+skew[0] || 0) : matrix;
|
|
1989
|
-
matrix = skew[1] ? matrix.skewY(+skew[1] || 0) : matrix;
|
|
1990
|
-
} else {
|
|
1991
|
-
matrix = matrix.skewX(+skew || 0);
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
// set scale
|
|
1995
|
-
if (!Number.isNaN(scale) || (Array.isArray(scale) && scale.some((x) => +x !== 1))) {
|
|
1996
|
-
matrix = Array.isArray(scale)
|
|
1997
|
-
? (matrix.scale(+scale[0] || 1, +scale[1] || 1, +scale[2] || 1))
|
|
1998
|
-
: matrix.scale(+scale || 1);
|
|
1999
|
-
}
|
|
2000
|
-
// set SVG transform-origin
|
|
2001
|
-
// matrix = matrix.translate(-originX,-originY,-originZ)
|
|
2002
|
-
matrix = matrix.translate(-originX, -originY);
|
|
2003
|
-
}
|
|
2004
|
-
return matrix;
|
|
2542
|
+
/**
|
|
2543
|
+
* @param {number} p1
|
|
2544
|
+
* @param {number} p2
|
|
2545
|
+
* @param {number} p3
|
|
2546
|
+
* @param {number} p4
|
|
2547
|
+
* @param {number} t a [0-1] ratio
|
|
2548
|
+
* @returns {number}
|
|
2549
|
+
*/
|
|
2550
|
+
function base3(p1, p2, p3, p4, t) {
|
|
2551
|
+
const t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4;
|
|
2552
|
+
const t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
|
|
2553
|
+
return t * t2 - 3 * p1 + 3 * p2;
|
|
2005
2554
|
}
|
|
2006
2555
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2556
|
+
/**
|
|
2557
|
+
* Returns the C (cubic-bezier) segment length.
|
|
2558
|
+
*
|
|
2559
|
+
* @param {number} x1 the starting point X
|
|
2560
|
+
* @param {number} y1 the starting point Y
|
|
2561
|
+
* @param {number} x2 the first control point X
|
|
2562
|
+
* @param {number} y2 the first control point Y
|
|
2563
|
+
* @param {number} x3 the second control point X
|
|
2564
|
+
* @param {number} y3 the second control point Y
|
|
2565
|
+
* @param {number} x4 the ending point X
|
|
2566
|
+
* @param {number} y4 the ending point Y
|
|
2567
|
+
* @param {number} z a [0-1] ratio
|
|
2568
|
+
* @returns {number} the cubic-bezier segment length
|
|
2569
|
+
*/
|
|
2570
|
+
function getSegCubicLength(x1, y1, x2, y2, x3, y3, x4, y4, z) {
|
|
2571
|
+
let Z = z;
|
|
2572
|
+
if (z === null || Number.isNaN(+z)) Z = 1;
|
|
2019
2573
|
|
|
2020
|
-
//
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
const J = ma[0] * ma[0] + ma[2] * ma[2];
|
|
2024
|
-
const K = ma[1] * ma[1] + ma[3] * ma[3];
|
|
2574
|
+
// Z = Z > 1 ? 1 : Z < 0 ? 0 : Z;
|
|
2575
|
+
if (Z > 1) Z = 1;
|
|
2576
|
+
if (Z < 0) Z = 0;
|
|
2025
2577
|
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2578
|
+
const z2 = Z / 2; let ct = 0; let xbase = 0; let ybase = 0; let sum = 0;
|
|
2579
|
+
const Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678,
|
|
2580
|
+
-0.5873, 0.5873, -0.7699, 0.7699,
|
|
2581
|
+
-0.9041, 0.9041, -0.9816, 0.9816];
|
|
2582
|
+
const Cvalues = [0.2491, 0.2491, 0.2335, 0.2335,
|
|
2583
|
+
0.2032, 0.2032, 0.1601, 0.1601,
|
|
2584
|
+
0.1069, 0.1069, 0.0472, 0.0472];
|
|
2029
2585
|
|
|
2030
|
-
|
|
2031
|
-
|
|
2586
|
+
Tvalues.forEach((T, i) => {
|
|
2587
|
+
ct = z2 * T + z2;
|
|
2588
|
+
xbase = base3(x1, x2, x3, x4, ct);
|
|
2589
|
+
ybase = base3(y1, y2, y3, y4, ct);
|
|
2590
|
+
sum += Cvalues[i] * Math.sqrt(xbase * xbase + ybase * ybase);
|
|
2591
|
+
});
|
|
2592
|
+
return z2 * sum;
|
|
2593
|
+
}
|
|
2032
2594
|
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2595
|
+
/**
|
|
2596
|
+
* Returns the shape total length,
|
|
2597
|
+
* or the equivalent to `shape.getTotalLength()`
|
|
2598
|
+
* pathToCurve version
|
|
2599
|
+
*
|
|
2600
|
+
* @param {SVGPC.pathArray} path the ending point Y
|
|
2601
|
+
* @returns {number} the shape total length
|
|
2602
|
+
*/
|
|
2603
|
+
function getPathLength(path) {
|
|
2604
|
+
let totalLength = 0;
|
|
2605
|
+
pathToCurve(path).forEach((s, i, curveArray) => {
|
|
2606
|
+
totalLength += s[0] === 'M' ? 0
|
|
2607
|
+
// @ts-ignore
|
|
2608
|
+
: getSegCubicLength.apply(0, curveArray[i - 1].slice(-2).concat(s.slice(1)));
|
|
2609
|
+
});
|
|
2610
|
+
return totalLength;
|
|
2611
|
+
}
|
|
2037
2612
|
|
|
2038
|
-
|
|
2039
|
-
|
|
2613
|
+
/**
|
|
2614
|
+
* Check if a path is drawn clockwise and returns true if so,
|
|
2615
|
+
* false otherwise.
|
|
2616
|
+
*
|
|
2617
|
+
* @param {string | SVGPC.pathArray} path the path string or `pathArray`
|
|
2618
|
+
* @returns {boolean} true when clockwise or false if not
|
|
2619
|
+
*/
|
|
2620
|
+
function getDrawDirection(path) {
|
|
2621
|
+
return getPathArea(pathToCurve(path)) >= 0;
|
|
2622
|
+
}
|
|
2040
2623
|
|
|
2041
|
-
|
|
2042
|
-
|
|
2624
|
+
/**
|
|
2625
|
+
* Returns [x,y] coordinates of a point at a given length of a shape.
|
|
2626
|
+
*
|
|
2627
|
+
* @param {string | SVGPC.pathArray} path the `pathArray` to look into
|
|
2628
|
+
* @param {number} length the length of the shape to look at
|
|
2629
|
+
* @returns {number[]} the requested [x,y] coordinates
|
|
2630
|
+
*/
|
|
2631
|
+
function getPointAtLength(path, length) {
|
|
2632
|
+
let totalLength = 0;
|
|
2633
|
+
let segLen;
|
|
2634
|
+
let data;
|
|
2635
|
+
let result;
|
|
2636
|
+
// @ts-ignore
|
|
2637
|
+
return pathToCurve(path).map((seg, i, curveArray) => {
|
|
2638
|
+
data = i ? curveArray[i - 1].slice(-2).concat(seg.slice(1)) : seg.slice(1);
|
|
2639
|
+
// @ts-ignore
|
|
2640
|
+
segLen = i ? getSegCubicLength.apply(0, data) : 0;
|
|
2641
|
+
totalLength += segLen;
|
|
2043
2642
|
|
|
2044
|
-
|
|
2643
|
+
if (i === 0) {
|
|
2644
|
+
result = { x: data[0], y: data[1] };
|
|
2645
|
+
} else if (totalLength > length && length > totalLength - segLen) {
|
|
2646
|
+
// @ts-ignore
|
|
2647
|
+
result = getPointAtSegLength.apply(0, data.concat(1 - (totalLength - length) / segLen));
|
|
2648
|
+
} else {
|
|
2649
|
+
result = null;
|
|
2650
|
+
}
|
|
2045
2651
|
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
// the x - axis - rotation angle is the argument of the l1 - eigenvector
|
|
2050
|
-
let AX = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon) ? 90
|
|
2051
|
-
: Math.atan(Math.abs(L) > Math.abs(l1 - K) ? (l1 - J) / L
|
|
2052
|
-
: ((L / (l1 - K))) * 180) / Math.PI;
|
|
2053
|
-
let RX;
|
|
2054
|
-
let RY;
|
|
2652
|
+
return result;
|
|
2653
|
+
}).filter((x) => x).slice(-1)[0]; // isolate last segment
|
|
2654
|
+
}
|
|
2055
2655
|
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2656
|
+
/**
|
|
2657
|
+
* Parses a path string value to determine its validity
|
|
2658
|
+
* then returns true if it's valid or false otherwise.
|
|
2659
|
+
*
|
|
2660
|
+
* @param {string} pathString the path string to be parsed
|
|
2661
|
+
* @returns {boolean} the path string validity
|
|
2662
|
+
*/
|
|
2663
|
+
function isValidPath(pathString) {
|
|
2664
|
+
if (typeof pathString !== 'string') {
|
|
2665
|
+
return false;
|
|
2066
2666
|
}
|
|
2067
2667
|
|
|
2068
|
-
|
|
2069
|
-
}
|
|
2668
|
+
const path = new SVGPathArray(pathString);
|
|
2070
2669
|
|
|
2071
|
-
|
|
2072
|
-
// this will return the xy projected location
|
|
2073
|
-
// Using the equation found here: http://en.wikipedia.org/wiki/3D_projection#Diagram
|
|
2074
|
-
// https://stackoverflow.com/questions/23792505/predicted-rendering-of-css-3d-transformed-pixel
|
|
2670
|
+
skipSpaces(path);
|
|
2075
2671
|
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
});
|
|
2080
|
-
const originX = origin[0] || 0;
|
|
2081
|
-
const originY = origin[1] || 0;
|
|
2082
|
-
const originZ = origin[2] || 0;
|
|
2083
|
-
const relativePositionX = point3D.x - originX;
|
|
2084
|
-
const relativePositionY = point3D.y - originY;
|
|
2085
|
-
const relativePositionZ = point3D.z - originZ;
|
|
2672
|
+
while (path.index < path.max && !path.err.length) {
|
|
2673
|
+
scanSegment(path);
|
|
2674
|
+
}
|
|
2086
2675
|
|
|
2087
|
-
return [
|
|
2088
|
-
relativePositionX * (Math.abs(originZ) / Math.abs(relativePositionZ)) + originX,
|
|
2089
|
-
relativePositionY * (Math.abs(originZ) / Math.abs(relativePositionZ)) + originY,
|
|
2090
|
-
];
|
|
2676
|
+
return !path.err.length && 'mM'.includes(path.segments[0][0]);
|
|
2091
2677
|
}
|
|
2092
2678
|
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2679
|
+
/**
|
|
2680
|
+
* Supported shapes and their specific parameters.
|
|
2681
|
+
*/
|
|
2682
|
+
const shapeParams = {
|
|
2683
|
+
circle: ['cx', 'cy', 'r'],
|
|
2684
|
+
ellipse: ['cx', 'cy', 'rx', 'ry'],
|
|
2685
|
+
rect: ['width', 'height', 'x', 'y', 'rx', 'ry'],
|
|
2686
|
+
polygon: ['points'],
|
|
2687
|
+
polyline: ['points'],
|
|
2688
|
+
glyph: [],
|
|
2689
|
+
};
|
|
2690
|
+
|
|
2691
|
+
/**
|
|
2692
|
+
* Returns a new `pathArray` from line attributes.
|
|
2693
|
+
*
|
|
2694
|
+
* @param {SVGPC.lineAttr} attr shape configuration
|
|
2695
|
+
* @return {SVGPC.pathArray} a new line `pathArray`
|
|
2696
|
+
*/
|
|
2697
|
+
function getLinePath(attr) {
|
|
2100
2698
|
const {
|
|
2101
|
-
|
|
2102
|
-
} =
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
x1: 0, y1: 0, x2: 0, y2: 0, x: 0, y: 0,
|
|
2106
|
-
};
|
|
2107
|
-
let segment = [];
|
|
2108
|
-
let seglen = 0;
|
|
2109
|
-
let pathCommand = '';
|
|
2110
|
-
let transformedPath = [];
|
|
2111
|
-
const allPathCommands = []; // needed for arc to curve transformation
|
|
2112
|
-
let result = [];
|
|
2699
|
+
x1, y1, x2, y2,
|
|
2700
|
+
} = attr;
|
|
2701
|
+
return [['M', +x1, +y1], ['L', +x2, +y2]];
|
|
2702
|
+
}
|
|
2113
2703
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2704
|
+
/**
|
|
2705
|
+
* Returns a new `pathArray` like from polyline/polygon attributes.
|
|
2706
|
+
*
|
|
2707
|
+
* @param {SVGPC.polyAttr} attr shape configuration
|
|
2708
|
+
* @return {SVGPC.pathArray} a new polygon/polyline `pathArray`
|
|
2709
|
+
*/
|
|
2710
|
+
function getPolyPath(attr) {
|
|
2711
|
+
/** @type {SVGPC.pathArray} */
|
|
2712
|
+
const pathArray = [];
|
|
2713
|
+
const points = attr.points.split(/[\s|,]/).map(Number);
|
|
2117
2714
|
|
|
2118
|
-
|
|
2715
|
+
let index = 0;
|
|
2716
|
+
while (index < points.length) {
|
|
2717
|
+
pathArray.push([(index ? 'L' : 'M'), (points[index]), (points[index + 1])]);
|
|
2718
|
+
index += 2;
|
|
2719
|
+
}
|
|
2119
2720
|
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
/// ////////////////////////////////////////
|
|
2123
|
-
allPathCommands[i] = pathCommand;
|
|
2721
|
+
return attr.type === 'polygon' ? pathArray.concat([['z']]) : pathArray;
|
|
2722
|
+
}
|
|
2124
2723
|
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2724
|
+
/**
|
|
2725
|
+
* Returns a new `pathArray` from circle attributes.
|
|
2726
|
+
*
|
|
2727
|
+
* @param {SVGPC.circleAttr} attr shape configuration
|
|
2728
|
+
* @return {SVGPC.pathArray} a circle `pathArray`
|
|
2729
|
+
*/
|
|
2730
|
+
function getCirclePath(attr) {
|
|
2731
|
+
const {
|
|
2732
|
+
cx, cy, r,
|
|
2733
|
+
} = attr;
|
|
2128
2734
|
|
|
2129
|
-
|
|
2130
|
-
|
|
2735
|
+
return [
|
|
2736
|
+
['M', (cx - r), cy],
|
|
2737
|
+
['a', r, r, 0, 1, 0, (2 * r), 0],
|
|
2738
|
+
['a', r, r, 0, 1, 0, (-2 * r), 0],
|
|
2739
|
+
];
|
|
2740
|
+
}
|
|
2131
2741
|
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2742
|
+
/**
|
|
2743
|
+
* Returns a new `pathArray` from ellipse attributes.
|
|
2744
|
+
*
|
|
2745
|
+
* @param {SVGPC.ellipseAttr} attr shape configuration
|
|
2746
|
+
* @return {SVGPC.pathArray} an ellipse `pathArray`
|
|
2747
|
+
*/
|
|
2748
|
+
function getEllipsePath(attr) {
|
|
2749
|
+
const {
|
|
2750
|
+
cx, cy, rx, ry,
|
|
2751
|
+
} = attr;
|
|
2137
2752
|
|
|
2138
|
-
|
|
2139
|
-
|
|
2753
|
+
return [
|
|
2754
|
+
['M', (cx - rx), cy],
|
|
2755
|
+
['a', rx, ry, 0, 1, 0, (2 * rx), 0],
|
|
2756
|
+
['a', rx, ry, 0, 1, 0, (-2 * rx), 0],
|
|
2757
|
+
];
|
|
2758
|
+
}
|
|
2140
2759
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2760
|
+
/**
|
|
2761
|
+
* Returns a new `pathArray` like from rect attributes.
|
|
2762
|
+
*
|
|
2763
|
+
* @param {SVGPC.rectAttr} attr object with properties above
|
|
2764
|
+
* @return {SVGPC.pathArray} a new `pathArray` from `<rect>` attributes
|
|
2765
|
+
*/
|
|
2766
|
+
function getRectanglePath(attr) {
|
|
2767
|
+
const x = +attr.x || 0;
|
|
2768
|
+
const y = +attr.y || 0;
|
|
2769
|
+
const w = +attr.width;
|
|
2770
|
+
const h = +attr.height;
|
|
2771
|
+
let rx = +attr.rx;
|
|
2772
|
+
let ry = +attr.ry;
|
|
2773
|
+
|
|
2774
|
+
// Validity checks from http://www.w3.org/TR/SVG/shapes.html#RectElement:
|
|
2775
|
+
if (rx || ry) {
|
|
2776
|
+
rx = !rx ? ry : rx;
|
|
2777
|
+
ry = !ry ? rx : ry;
|
|
2778
|
+
|
|
2779
|
+
if (rx * 2 > w) rx -= (rx * 2 - w) / 2;
|
|
2780
|
+
if (ry * 2 > h) ry -= (ry * 2 - h) / 2;
|
|
2781
|
+
|
|
2782
|
+
return [
|
|
2783
|
+
['M', x + rx, y],
|
|
2784
|
+
['h', w - rx * 2],
|
|
2785
|
+
['s', rx, 0, rx, ry],
|
|
2786
|
+
['v', h - ry * 2],
|
|
2787
|
+
['s', 0, ry, -rx, ry],
|
|
2788
|
+
['h', -w + rx * 2],
|
|
2789
|
+
['s', -rx, 0, -rx, -ry],
|
|
2790
|
+
['v', -h + ry * 2],
|
|
2791
|
+
['s', 0, -ry, rx, -ry],
|
|
2792
|
+
];
|
|
2793
|
+
}
|
|
2145
2794
|
|
|
2146
|
-
|
|
2795
|
+
return [
|
|
2796
|
+
['M', x, y],
|
|
2797
|
+
['h', w],
|
|
2798
|
+
['v', h],
|
|
2799
|
+
['H', x],
|
|
2800
|
+
['Z'],
|
|
2801
|
+
];
|
|
2802
|
+
}
|
|
2147
2803
|
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2804
|
+
/**
|
|
2805
|
+
* Returns a new `<path>` element created from attributes of a `<line>`, `<polyline>`,
|
|
2806
|
+
* `<polygon>`, `<rect>`, `<ellipse>`, `<circle>` or `<glyph>`. If `replace` parameter
|
|
2807
|
+
* is `true`, it will replace the target.
|
|
2808
|
+
*
|
|
2809
|
+
* The newly created `<path>` element keeps all non-specific
|
|
2810
|
+
* attributes like `class`, `fill`, etc.
|
|
2811
|
+
*
|
|
2812
|
+
* @param {SVGPC.shapeTypes} element target shape
|
|
2813
|
+
* @param {boolean} replace option to replace target
|
|
2814
|
+
* @return {?SVGPathElement} the newly created `<path>` element
|
|
2815
|
+
*/
|
|
2816
|
+
function shapeToPath(element, replace) {
|
|
2817
|
+
const supportedShapes = Object.keys(shapeParams).concat(['glyph']);
|
|
2154
2818
|
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
switch (pathCommand) {
|
|
2159
|
-
case 'A': // only apply to 2D transformations
|
|
2160
|
-
te = transformEllipse(matrix2d, segment[1], segment[2], segment[3]);
|
|
2819
|
+
if (!supportedShapes.some((s) => element.tagName === s)) {
|
|
2820
|
+
throw TypeError(`shapeToPath: ${element} is not SVGElement`);
|
|
2821
|
+
}
|
|
2161
2822
|
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2823
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
2824
|
+
const type = element.tagName;
|
|
2825
|
+
/** @ts-ignore */
|
|
2826
|
+
const shapeAttrs = shapeParams[type];
|
|
2165
2827
|
|
|
2166
|
-
|
|
2828
|
+
// set config
|
|
2829
|
+
const config = {};
|
|
2830
|
+
config.type = type;
|
|
2831
|
+
/** @ts-ignore */
|
|
2832
|
+
shapeAttrs.forEach((p) => { config[p] = element.getAttribute(p); });
|
|
2167
2833
|
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
}
|
|
2834
|
+
// set no-specific shape attributes: fill, stroke, etc
|
|
2835
|
+
Object.values(element.attributes).forEach(({ name, value }) => {
|
|
2836
|
+
if (!shapeAttrs.includes(name)) path.setAttribute(name, value);
|
|
2837
|
+
});
|
|
2173
2838
|
|
|
2174
|
-
|
|
2175
|
-
|
|
2839
|
+
// set d
|
|
2840
|
+
let description;
|
|
2841
|
+
/** @ts-ignore */
|
|
2842
|
+
if (type === 'circle') description = pathToString(getCirclePath(config));
|
|
2843
|
+
/** @ts-ignore */
|
|
2844
|
+
else if (type === 'ellipse') description = pathToString(getEllipsePath(config));
|
|
2845
|
+
/** @ts-ignore */
|
|
2846
|
+
else if (['polyline', 'polygon'].includes(type)) description = pathToString(getPolyPath(config));
|
|
2847
|
+
/** @ts-ignore */
|
|
2848
|
+
else if (type === 'rect') description = pathToString(getRectanglePath(config));
|
|
2849
|
+
/** @ts-ignore */
|
|
2850
|
+
else if (type === 'line') description = pathToString(getLinePath(config));
|
|
2851
|
+
else if (type === 'glyph') description = element.getAttribute('d');
|
|
2852
|
+
|
|
2853
|
+
// replace target element
|
|
2854
|
+
if (description) {
|
|
2855
|
+
path.setAttribute('d', description);
|
|
2856
|
+
if (replace) {
|
|
2857
|
+
element.before(path, element);
|
|
2858
|
+
element.remove();
|
|
2859
|
+
}
|
|
2860
|
+
return path;
|
|
2861
|
+
}
|
|
2862
|
+
return null;
|
|
2863
|
+
}
|
|
2176
2864
|
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2865
|
+
/**
|
|
2866
|
+
* Reverses all segments and their values from a `pathArray`
|
|
2867
|
+
* which consists of only C (cubic-bezier) path commands.
|
|
2868
|
+
*
|
|
2869
|
+
* @param {SVGPC.pathArray} path the source `pathArray`
|
|
2870
|
+
* @returns {SVGPC.pathArray} the reversed `pathArray`
|
|
2871
|
+
*/
|
|
2872
|
+
function reverseCurve(path) {
|
|
2873
|
+
const rotatedCurve = path.slice(1)
|
|
2874
|
+
.map((x, i, curveOnly) => (!i
|
|
2875
|
+
? path[0].slice(1).concat(x.slice(1))
|
|
2876
|
+
: curveOnly[i - 1].slice(-2).concat(x.slice(1))))
|
|
2877
|
+
.map((x) => x.map((_, i) => x[x.length - i - 2 * (1 - (i % 2))]))
|
|
2878
|
+
.reverse();
|
|
2180
2879
|
|
|
2181
|
-
|
|
2880
|
+
// @ts-ignore
|
|
2881
|
+
return [['M'].concat(rotatedCurve[0].slice(0, 2))]
|
|
2882
|
+
// @ts-ignore
|
|
2883
|
+
.concat(rotatedCurve.map((x) => ['C'].concat(x.slice(2))));
|
|
2884
|
+
}
|
|
2182
2885
|
|
|
2183
|
-
|
|
2184
|
-
segment = ['L', lx, ly];
|
|
2185
|
-
} else if (y === ly) {
|
|
2186
|
-
segment = ['H', lx];
|
|
2187
|
-
} else if (x === lx) {
|
|
2188
|
-
segment = ['V', ly];
|
|
2189
|
-
}
|
|
2886
|
+
var version = "0.1.10alpha3";
|
|
2190
2887
|
|
|
2191
|
-
|
|
2888
|
+
// @ts-ignore
|
|
2192
2889
|
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
segment[j] = x;
|
|
2199
|
-
segment[j + 1] = y;
|
|
2200
|
-
}
|
|
2201
|
-
return segment;
|
|
2202
|
-
}
|
|
2203
|
-
});
|
|
2204
|
-
return transformedPath;
|
|
2205
|
-
}
|
|
2206
|
-
return clonePath(absolutePath);
|
|
2207
|
-
}
|
|
2890
|
+
/**
|
|
2891
|
+
* A global namespace for library version.
|
|
2892
|
+
* @type {string}
|
|
2893
|
+
*/
|
|
2894
|
+
const Version = version;
|
|
2208
2895
|
|
|
2209
2896
|
const Util = {
|
|
2210
|
-
CSSMatrix
|
|
2897
|
+
CSSMatrix,
|
|
2211
2898
|
parsePathString,
|
|
2212
2899
|
isPathArray,
|
|
2213
2900
|
isCurveArray,
|
|
2214
2901
|
isAbsoluteArray,
|
|
2215
2902
|
isRelativeArray,
|
|
2216
2903
|
isNormalizedArray,
|
|
2904
|
+
isValidPath,
|
|
2217
2905
|
pathToAbsolute,
|
|
2218
2906
|
pathToRelative,
|
|
2219
2907
|
pathToCurve,
|
|
@@ -2232,57 +2920,44 @@ const Util = {
|
|
|
2232
2920
|
normalizePath,
|
|
2233
2921
|
transformPath,
|
|
2234
2922
|
getSVGMatrix,
|
|
2923
|
+
shapeToPath,
|
|
2235
2924
|
options: SVGPCO,
|
|
2925
|
+
Version,
|
|
2236
2926
|
};
|
|
2237
2927
|
|
|
2928
|
+
// import isPathArray from './util/isPathArray';
|
|
2929
|
+
|
|
2238
2930
|
/**
|
|
2239
2931
|
* Creates a new SVGPathCommander instance.
|
|
2932
|
+
*
|
|
2933
|
+
* @author thednp <https://github.com/thednp/svg-path-commander>
|
|
2240
2934
|
* @class
|
|
2241
2935
|
*/
|
|
2242
2936
|
class SVGPathCommander {
|
|
2243
2937
|
/**
|
|
2244
2938
|
* @constructor
|
|
2245
|
-
* @param {
|
|
2246
|
-
* @param {
|
|
2939
|
+
* @param {string} pathValue the path string
|
|
2940
|
+
* @param {any} config instance options
|
|
2247
2941
|
*/
|
|
2248
2942
|
constructor(pathValue, config) {
|
|
2249
2943
|
const options = config || {};
|
|
2250
|
-
|
|
2251
|
-
// const roundOption = +options.round === 0 || options.round === false ? 0 : SVGPCO.round;
|
|
2944
|
+
|
|
2252
2945
|
let { round } = SVGPCO;
|
|
2253
2946
|
const { round: roundOption } = options;
|
|
2254
|
-
if (+roundOption === 0 || roundOption === false) {
|
|
2947
|
+
if ((roundOption && +roundOption === 0) || roundOption === false) {
|
|
2255
2948
|
round = 0;
|
|
2256
2949
|
}
|
|
2257
2950
|
|
|
2258
2951
|
const { decimals } = round && (options || SVGPCO);
|
|
2259
|
-
const { origin } = options;
|
|
2260
2952
|
|
|
2261
2953
|
// set instance options
|
|
2262
|
-
/**
|
|
2263
|
-
* @type {Boolean | Number}
|
|
2264
|
-
*/
|
|
2265
2954
|
this.round = round === 0 ? 0 : decimals;
|
|
2266
2955
|
// ZERO | FALSE will disable rounding numbers
|
|
2267
2956
|
|
|
2268
|
-
|
|
2269
|
-
const { x, y } = origin;
|
|
2270
|
-
if ([x, y].every((n) => !Number.isNaN(n))) {
|
|
2271
|
-
/**
|
|
2272
|
-
* @type {Object | null}
|
|
2273
|
-
*/
|
|
2274
|
-
this.origin = origin;
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
|
|
2278
|
-
/**
|
|
2279
|
-
* @type {Object}
|
|
2280
|
-
*/
|
|
2957
|
+
/** @type {SVGPC.pathArray} */
|
|
2281
2958
|
this.segments = parsePathString(pathValue);
|
|
2282
2959
|
|
|
2283
|
-
/**
|
|
2284
|
-
* @type {String}
|
|
2285
|
-
*/
|
|
2960
|
+
/** * @type {string} */
|
|
2286
2961
|
this.pathValue = pathValue;
|
|
2287
2962
|
|
|
2288
2963
|
return this;
|
|
@@ -2309,25 +2984,30 @@ class SVGPathCommander {
|
|
|
2309
2984
|
}
|
|
2310
2985
|
|
|
2311
2986
|
/**
|
|
2312
|
-
* Reverse
|
|
2313
|
-
* @param {
|
|
2987
|
+
* Reverse the order of the segments and their values.
|
|
2988
|
+
* @param {boolean | number} onlySubpath option to reverse all sub-paths except first
|
|
2314
2989
|
* @public
|
|
2315
2990
|
*/
|
|
2316
2991
|
reverse(onlySubpath) {
|
|
2317
2992
|
this.toAbsolute();
|
|
2318
2993
|
|
|
2319
2994
|
const { segments } = this;
|
|
2320
|
-
const
|
|
2995
|
+
const split = splitPath(this.toString());
|
|
2996
|
+
const subPath = split.length > 1 ? split : 0;
|
|
2997
|
+
|
|
2321
2998
|
const absoluteMultiPath = subPath && clonePath(subPath)
|
|
2322
2999
|
.map((x, i) => {
|
|
2323
3000
|
if (onlySubpath) {
|
|
3001
|
+
// @ts-ignore
|
|
2324
3002
|
return i ? reversePath(x) : parsePathString(x);
|
|
2325
3003
|
}
|
|
3004
|
+
// @ts-ignore
|
|
2326
3005
|
return reversePath(x);
|
|
2327
3006
|
});
|
|
2328
3007
|
|
|
2329
3008
|
let path = [];
|
|
2330
3009
|
if (subPath) {
|
|
3010
|
+
// @ts-ignore
|
|
2331
3011
|
path = absoluteMultiPath.flat(1);
|
|
2332
3012
|
} else {
|
|
2333
3013
|
path = onlySubpath ? segments : reversePath(segments);
|
|
@@ -2339,7 +3019,7 @@ class SVGPathCommander {
|
|
|
2339
3019
|
|
|
2340
3020
|
/**
|
|
2341
3021
|
* Normalize path in 2 steps:
|
|
2342
|
-
* * convert pathArray(s) to absolute values
|
|
3022
|
+
* * convert `pathArray`(s) to absolute values
|
|
2343
3023
|
* * convert shorthand notation to standard notation
|
|
2344
3024
|
* @public
|
|
2345
3025
|
*/
|
|
@@ -2350,7 +3030,7 @@ class SVGPathCommander {
|
|
|
2350
3030
|
}
|
|
2351
3031
|
|
|
2352
3032
|
/**
|
|
2353
|
-
* Optimize pathArray values:
|
|
3033
|
+
* Optimize `pathArray` values:
|
|
2354
3034
|
* * convert segments to absolute and/or relative values
|
|
2355
3035
|
* * select segments with shortest resulted string
|
|
2356
3036
|
* * round values to the specified `decimals` option value
|
|
@@ -2364,31 +3044,27 @@ class SVGPathCommander {
|
|
|
2364
3044
|
}
|
|
2365
3045
|
|
|
2366
3046
|
/**
|
|
2367
|
-
* Transform path using values from an `Object`
|
|
2368
|
-
*
|
|
3047
|
+
* Transform path using values from an `Object` defined as `transformObject`.
|
|
3048
|
+
* @see SVGPC.transformObject for a quick refference
|
|
2369
3049
|
*
|
|
2370
|
-
* {
|
|
2371
|
-
* origin: {x, y, z},
|
|
2372
|
-
* translate: {x, y, z},
|
|
2373
|
-
* rotate: {x, y, z},
|
|
2374
|
-
* skew: {x, y, z},
|
|
2375
|
-
* scale: {x, y, z}
|
|
2376
|
-
* }
|
|
2377
|
-
* @param {Object} source a transform `Object`as described above
|
|
3050
|
+
* @param {Object.<string, (number | number[])>} source a `transformObject`as described above
|
|
2378
3051
|
* @public
|
|
2379
3052
|
*/
|
|
2380
3053
|
transform(source) {
|
|
2381
|
-
|
|
3054
|
+
if (!source || typeof source !== 'object' || (typeof source === 'object'
|
|
3055
|
+
&& !['translate', 'rotate', 'skew', 'scale'].some((x) => x in source))) return this;
|
|
3056
|
+
|
|
3057
|
+
const transform = source || {};
|
|
2382
3058
|
const { segments } = this;
|
|
2383
3059
|
|
|
2384
3060
|
// if origin is not specified
|
|
2385
3061
|
// it's important that we have one
|
|
2386
|
-
if (!
|
|
3062
|
+
if (!transform.origin) {
|
|
2387
3063
|
const BBox = getPathBBox(segments);
|
|
2388
|
-
|
|
3064
|
+
transform.origin = [BBox.cx, BBox.cy, BBox.cx];
|
|
2389
3065
|
}
|
|
2390
3066
|
|
|
2391
|
-
this.segments = transformPath(segments,
|
|
3067
|
+
this.segments = transformPath(segments, transform);
|
|
2392
3068
|
return this;
|
|
2393
3069
|
}
|
|
2394
3070
|
|
|
@@ -2421,6 +3097,7 @@ class SVGPathCommander {
|
|
|
2421
3097
|
}
|
|
2422
3098
|
}
|
|
2423
3099
|
|
|
3100
|
+
// @ts-ignore
|
|
2424
3101
|
Object.keys(Util).forEach((x) => { SVGPathCommander[x] = Util[x]; });
|
|
2425
3102
|
|
|
2426
3103
|
export { SVGPathCommander as default };
|