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