svg-path-simplify 0.4.2 → 0.4.4
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/CHANGELOG.md +21 -0
- package/README.md +7 -4
- package/dist/svg-path-simplify.esm.js +3593 -1279
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +3594 -1278
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +1017 -538
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/dist/svg-path-simplify.poly.cjs +9 -8
- package/docs/privacy-webapp.md +24 -0
- package/index.html +331 -152
- package/package.json +1 -1
- package/src/constants.js +4 -0
- package/src/css_parse.js +317 -0
- package/src/detect_input.js +76 -28
- package/src/index.js +8 -0
- package/src/pathData_simplify_cubic.js +26 -16
- package/src/pathData_simplify_harmonize_cpts.js +77 -1
- package/src/pathData_simplify_revertToquadratics.js +0 -1
- package/src/pathSimplify-main.js +304 -276
- package/src/pathSimplify-only-pathdata.js +7 -2
- package/src/pathSimplify-presets.js +254 -0
- package/src/poly-fit-curve-schneider.js +14 -7
- package/src/simplify_poly_RC.js +102 -0
- package/src/simplify_poly_RDP.js +109 -1
- package/src/simplify_poly_radial_distance.js +3 -3
- package/src/string_helpers.js +130 -4
- package/src/svg-getAttributes.js +4 -2
- package/src/svgii/convert_units.js +1 -1
- package/src/svgii/geometry.js +322 -5
- package/src/svgii/geometry_bbox_element.js +1 -1
- package/src/svgii/geometry_deduceRadius.js +116 -27
- package/src/svgii/geometry_length.js +253 -0
- package/src/svgii/pathData_analyze.js +18 -0
- package/src/svgii/pathData_convert.js +193 -89
- package/src/svgii/pathData_fix_directions.js +12 -14
- package/src/svgii/pathData_fromPoly.js +3 -3
- package/src/svgii/pathData_getLength.js +86 -0
- package/src/svgii/pathData_parse.js +2 -0
- package/src/svgii/pathData_parse_els.js +66 -68
- package/src/svgii/pathData_reorder.js +122 -16
- package/src/svgii/pathData_simplify_refineCorners.js +130 -35
- package/src/svgii/pathData_simplify_refine_round.js +420 -0
- package/src/svgii/pathData_split_to_groups.js +168 -0
- package/src/svgii/pathData_stringify.js +26 -64
- package/src/svgii/pathData_toPolygon.js +3 -4
- package/src/svgii/poly_analyze.js +61 -0
- package/src/svgii/poly_normalize.js +11 -2
- package/src/svgii/poly_to_pathdata.js +85 -24
- package/src/svgii/rounding.js +80 -78
- package/src/svgii/svg_cleanup.js +421 -619
- package/src/svgii/svg_cleanup_convertPathLength.js +39 -0
- package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
- package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
- package/src/svgii/svg_cleanup_remove_els_and_atts.js +77 -0
- package/src/svgii/svg_cleanup_ungroup.js +36 -0
- package/src/svgii/svg_el_parse_style_props.js +72 -47
- package/src/svgii/svg_getElementLength.js +67 -0
- package/src/svgii/svg_validate.js +220 -0
- package/tests/testSVG.js +14 -1
- package/src/svgii/pathData_refine_round.js +0 -222
|
@@ -17,54 +17,10 @@ import { pathDataToD } from './pathData_stringify';
|
|
|
17
17
|
import { roundPathData } from './rounding';
|
|
18
18
|
import { renderPoint } from './visualize';
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
toAbsolute = true,
|
|
25
|
-
toLonghands = true,
|
|
26
|
-
|
|
27
|
-
// not necessary unless you need cubics only
|
|
28
|
-
quadraticToCubic = false,
|
|
29
|
-
|
|
30
|
-
// mostly a fallback if arc calculations fail
|
|
31
|
-
arcToCubic = false,
|
|
32
|
-
// arc to cubic precision - adds more segments for better precision
|
|
33
|
-
arcAccuracy = 4,
|
|
34
|
-
} = {}
|
|
35
|
-
) {
|
|
36
|
-
|
|
37
|
-
// is already array
|
|
38
|
-
let isArray = Array.isArray(d);
|
|
39
|
-
|
|
40
|
-
// normalize native pathData to regular array
|
|
41
|
-
let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
|
|
42
|
-
/*
|
|
43
|
-
if (hasConstructor) {
|
|
44
|
-
d = d.map(com => { return { type: com.type, values: com.values } })
|
|
45
|
-
console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
|
|
46
|
-
}
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
let pathDataObj = isArray ? d : parsePathDataString(d);
|
|
50
|
-
|
|
51
|
-
let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
|
|
52
|
-
let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
|
|
53
|
-
|
|
54
|
-
//console.log('???quadraticToCubic', quadraticToCubic);
|
|
55
|
-
|
|
56
|
-
// normalize
|
|
57
|
-
pathData = normalizePathData(pathData,
|
|
58
|
-
{
|
|
59
|
-
toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
|
|
60
|
-
hasRelatives, hasShorthands, hasQuadratics, hasArcs
|
|
61
|
-
},
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
return pathData;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
20
|
+
/**
|
|
21
|
+
* wrapper function for
|
|
22
|
+
* all path data conversion
|
|
23
|
+
*/
|
|
68
24
|
export function convertPathData(pathData, {
|
|
69
25
|
toShorthands = true,
|
|
70
26
|
toLonghands = false,
|
|
@@ -80,6 +36,7 @@ export function convertPathData(pathData, {
|
|
|
80
36
|
hasShorthands = true,
|
|
81
37
|
hasQuadratics = true,
|
|
82
38
|
hasArcs = true,
|
|
39
|
+
isPoly = false,
|
|
83
40
|
optimizeArcs = true,
|
|
84
41
|
testTypes = false
|
|
85
42
|
|
|
@@ -103,7 +60,9 @@ export function convertPathData(pathData, {
|
|
|
103
60
|
|
|
104
61
|
// some params exclude each other
|
|
105
62
|
toRelative = toAbsolute ? false : toRelative;
|
|
106
|
-
|
|
63
|
+
//toAbsolute = !toRelative ? true : toAbsolute;
|
|
64
|
+
toShorthands = toLonghands ? false : toShorthands;
|
|
65
|
+
//toLonghands = !toShorthands ? true : toLonghands;
|
|
107
66
|
|
|
108
67
|
|
|
109
68
|
if (toAbsolute) pathData = pathDataToAbsolute(pathData);
|
|
@@ -117,28 +76,31 @@ export function convertPathData(pathData, {
|
|
|
117
76
|
//if(decimals>-1 && decimals<2) pathData = roundPathData(pathData, decimals);
|
|
118
77
|
if (toShorthands) pathData = pathDataToShorthands(pathData);
|
|
119
78
|
|
|
79
|
+
//console.log('hasArcs', hasArcs, arcToCubic, pathData);
|
|
120
80
|
if (hasArcs && arcToCubic) pathData = pathDataArcsToCubics(pathData)
|
|
121
81
|
|
|
122
82
|
//console.log(toShorthands, toRelative, decimals);
|
|
123
83
|
if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
|
|
124
84
|
|
|
125
|
-
if(toMixed) toRelative = true
|
|
85
|
+
if (toMixed) toRelative = true
|
|
126
86
|
|
|
127
87
|
// pre round - before relative conversion to minimize distortions
|
|
128
88
|
if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
|
|
129
89
|
|
|
130
90
|
// clone absolute pathdata
|
|
131
|
-
if(toMixed){
|
|
91
|
+
if (toMixed) {
|
|
132
92
|
pathDataAbs = JSON.parse(JSON.stringify(pathData))
|
|
133
93
|
}
|
|
134
94
|
|
|
135
95
|
if (toRelative) pathData = pathDataToRelative(pathData);
|
|
96
|
+
|
|
97
|
+
// final rounding
|
|
136
98
|
if (decimals > -1) pathData = roundPathData(pathData, decimals);
|
|
137
99
|
|
|
138
100
|
|
|
139
101
|
// choose most compact commands: relative or absolute
|
|
140
|
-
if(toMixed){
|
|
141
|
-
for(let i=0; i<pathData.length; i++){
|
|
102
|
+
if (toMixed) {
|
|
103
|
+
for (let i = 0; i < pathData.length; i++) {
|
|
142
104
|
let com = pathData[i]
|
|
143
105
|
let comA = pathDataAbs[i]
|
|
144
106
|
// compare Lengths
|
|
@@ -148,7 +110,7 @@ export function convertPathData(pathData, {
|
|
|
148
110
|
let lenR = comStr.length;
|
|
149
111
|
let lenA = comStrA.length;
|
|
150
112
|
|
|
151
|
-
if(lenA<lenR){
|
|
113
|
+
if (lenA < lenR) {
|
|
152
114
|
//console.log('absolute is shorter', comStrA, comStr);
|
|
153
115
|
pathData[i] = pathDataAbs[i]
|
|
154
116
|
}
|
|
@@ -158,6 +120,56 @@ export function convertPathData(pathData, {
|
|
|
158
120
|
return pathData
|
|
159
121
|
}
|
|
160
122
|
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
export function parsePathDataNormalized(d,
|
|
127
|
+
{
|
|
128
|
+
// necessary for most calculations
|
|
129
|
+
toAbsolute = true,
|
|
130
|
+
toLonghands = true,
|
|
131
|
+
|
|
132
|
+
// not necessary unless you need cubics only
|
|
133
|
+
quadraticToCubic = false,
|
|
134
|
+
|
|
135
|
+
// mostly a fallback if arc calculations fail
|
|
136
|
+
arcToCubic = false,
|
|
137
|
+
// arc to cubic precision - adds more segments for better precision
|
|
138
|
+
arcAccuracy = 4,
|
|
139
|
+
} = {}
|
|
140
|
+
) {
|
|
141
|
+
|
|
142
|
+
// is already array
|
|
143
|
+
let isArray = Array.isArray(d);
|
|
144
|
+
|
|
145
|
+
// normalize native pathData to regular array
|
|
146
|
+
let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
|
|
147
|
+
/*
|
|
148
|
+
if (hasConstructor) {
|
|
149
|
+
d = d.map(com => { return { type: com.type, values: com.values } })
|
|
150
|
+
console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
|
|
151
|
+
}
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
let pathDataObj = isArray ? d : parsePathDataString(d);
|
|
155
|
+
|
|
156
|
+
let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
|
|
157
|
+
let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
|
|
158
|
+
|
|
159
|
+
//console.log('???quadraticToCubic', quadraticToCubic);
|
|
160
|
+
|
|
161
|
+
// normalize
|
|
162
|
+
pathData = normalizePathData(pathData,
|
|
163
|
+
{
|
|
164
|
+
toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
|
|
165
|
+
hasRelatives, hasShorthands, hasQuadratics, hasArcs
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return pathData;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
161
173
|
/**
|
|
162
174
|
*
|
|
163
175
|
* @param {*} pathData
|
|
@@ -166,50 +178,100 @@ export function convertPathData(pathData, {
|
|
|
166
178
|
|
|
167
179
|
export function optimizeArcPathData(pathData = []) {
|
|
168
180
|
|
|
169
|
-
|
|
181
|
+
//return pathData
|
|
170
182
|
|
|
171
|
-
|
|
183
|
+
let remove = []
|
|
184
|
+
let l = pathData.length;
|
|
185
|
+
let pathDataN = [];
|
|
186
|
+
|
|
187
|
+
for (let i = 0; i < l; i++) {
|
|
188
|
+
let com = pathData[i];
|
|
172
189
|
let { type, values } = com;
|
|
173
|
-
if (type === 'A') {
|
|
174
|
-
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
175
|
-
let comPrev = pathData[i - 1]
|
|
176
|
-
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
177
|
-
let M = { x: x0, y: y0 };
|
|
178
|
-
let p = { x, y };
|
|
179
|
-
//largeArc=true
|
|
180
|
-
//let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
|
|
181
|
-
|
|
182
|
-
if(rx===0 || ry===0){
|
|
183
|
-
pathData[i]= null
|
|
184
|
-
remove.push(i)
|
|
185
|
-
//console.log('!!!');
|
|
186
|
-
}
|
|
187
190
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
if (type !== 'A') {
|
|
192
|
+
pathDataN.push(com)
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
191
195
|
|
|
192
|
-
// rx~==ry
|
|
193
|
-
if (diff < 0.01) {
|
|
194
196
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
198
|
+
let comPrev = pathData[i - 1]
|
|
199
|
+
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
200
|
+
let M = { x: x0, y: y0 };
|
|
201
|
+
let p = { x, y };
|
|
202
|
+
//largeArc=true
|
|
203
|
+
//let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
|
|
199
204
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
if (rx === 0 || ry === 0) {
|
|
206
|
+
pathData[i] = null
|
|
207
|
+
remove.push(i)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// test for elliptic
|
|
211
|
+
let rat = rx / ry
|
|
212
|
+
let error = rx !== ry ? Math.abs(1 - rat) : 0
|
|
213
|
+
|
|
214
|
+
if (error > 0.01) {
|
|
215
|
+
//console.log('is elliptic');
|
|
216
|
+
pathDataN.push(com)
|
|
217
|
+
continue
|
|
218
|
+
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// xAxis rotation is futile for circular arcs - reset
|
|
222
|
+
com.values[2] = 0
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* test semi circles
|
|
226
|
+
* rx and ry are large enough
|
|
227
|
+
*/
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
// 1. horizontal or vertical
|
|
231
|
+
let thresh = getDistManhattan(M, p) * 0.001;
|
|
232
|
+
let diffX = Math.abs(x - x0)
|
|
233
|
+
let diffY = Math.abs(y - y0)
|
|
234
|
+
|
|
235
|
+
let isHorizontal = diffY < thresh
|
|
236
|
+
let isVertical = diffX < thresh
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
// minify rx and ry
|
|
240
|
+
if (isHorizontal || isVertical) {
|
|
241
|
+
|
|
242
|
+
// check if semi circle
|
|
243
|
+
let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
|
|
244
|
+
|
|
245
|
+
// is semicircle we can simplify rx
|
|
246
|
+
if (!needsTrueR) {
|
|
247
|
+
//console.log('needsTrueR', needsTrueR, diffX, rx, diffY, ry);
|
|
248
|
+
rx = rx >= 1 ? 1 : (rx > 0.5 ? 0.5 : rx);
|
|
207
249
|
}
|
|
250
|
+
|
|
251
|
+
com.values[0] = rx
|
|
252
|
+
com.values[1] = rx
|
|
253
|
+
pathDataN.push(com)
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
|
|
208
257
|
}
|
|
209
|
-
})
|
|
210
258
|
|
|
211
|
-
|
|
212
|
-
|
|
259
|
+
// 2. get true radius - if rx ~= diameter/distance we have a semicircle
|
|
260
|
+
let r = getDistance(M, p) * 0.5
|
|
261
|
+
error = rx / r
|
|
262
|
+
//console.log('rx', rx, r, error);
|
|
263
|
+
|
|
264
|
+
if (error < 0.5) {
|
|
265
|
+
rx = r >= 1 ? 1 : (r > 0.5 ? 0.5 : r);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
com.values[0] = rx;
|
|
269
|
+
com.values[1] = rx;
|
|
270
|
+
pathDataN.push(com)
|
|
271
|
+
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return pathDataN;
|
|
213
275
|
}
|
|
214
276
|
|
|
215
277
|
|
|
@@ -277,6 +339,48 @@ export function normalizePathData(pathData = [],
|
|
|
277
339
|
}
|
|
278
340
|
*/
|
|
279
341
|
|
|
342
|
+
export function convertSmallArcsToLinetos(pathData) {
|
|
343
|
+
|
|
344
|
+
let l = pathData.length;
|
|
345
|
+
|
|
346
|
+
// add fist command
|
|
347
|
+
let pathDataN = [pathData[0]]
|
|
348
|
+
|
|
349
|
+
for (let i = 1; i < l; i++) {
|
|
350
|
+
let com = pathData[i];
|
|
351
|
+
let comPrev = pathData[i - 1];
|
|
352
|
+
let comN = pathData[i + 1] || null;
|
|
353
|
+
|
|
354
|
+
if (!comN) {
|
|
355
|
+
pathDataN.push(com);
|
|
356
|
+
break
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
let { type, values, extreme = false, p0, p, dimA = 0 } = com;
|
|
360
|
+
// for short segment detection
|
|
361
|
+
let dimAN = comN.dimA;
|
|
362
|
+
let dimA0 = comPrev.dimA + dimA + dimAN;
|
|
363
|
+
let thresh = 0.05
|
|
364
|
+
let isShort = dimA < dimA0 * thresh;
|
|
365
|
+
//let isShortN = dimAN < dimA0 * thresh;
|
|
366
|
+
|
|
367
|
+
if (type === 'A' && isShort && values[0] < 1 && values[1] < 1) {
|
|
368
|
+
|
|
369
|
+
//renderPoint(markers, p0, 'green', '0.1%')
|
|
370
|
+
//renderPoint(markers, p, 'magenta', '0.1%')
|
|
371
|
+
com.type = 'L';
|
|
372
|
+
com.values = [p.x, p.y];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
pathDataN.push(com)
|
|
376
|
+
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
return pathDataN;
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
}
|
|
280
384
|
|
|
281
385
|
|
|
282
386
|
|
|
@@ -10,6 +10,7 @@ import { getPolygonArea } from "./geometry_area";
|
|
|
10
10
|
import { getPolyBBox } from "./geometry_bbox";
|
|
11
11
|
import { reversePathData } from "./pathData_reverse";
|
|
12
12
|
import { getPathDataPolyPrecise } from "./pathData_toPolygon";
|
|
13
|
+
import { renderPoint, renderPoly } from "./visualize";
|
|
13
14
|
|
|
14
15
|
export function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
|
|
15
16
|
|
|
@@ -42,6 +43,7 @@ export function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
|
|
|
42
43
|
let ptMid = { x: bb.left + bb.width / 2, y: bb.top + bb.height / 2 }
|
|
43
44
|
let inPoly = isPointInPolygon(ptMid, prev.pts, bb0)
|
|
44
45
|
|
|
46
|
+
|
|
45
47
|
if (inPoly) {
|
|
46
48
|
polys[j].inter += 1
|
|
47
49
|
poly.includedIn.push(i)
|
|
@@ -52,29 +54,25 @@ export function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
|
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
// reverse paths
|
|
55
|
-
for (let i = 0; i < l; i++) {
|
|
57
|
+
for (let i = 0; l && i < l; i++) {
|
|
56
58
|
|
|
57
59
|
let poly = polys[i]
|
|
58
60
|
let { cw, includedIn, includes } = poly
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|| !includedIn.length && !cw && toClockwise
|
|
63
|
-
) {
|
|
64
|
-
pathDataArr[i].pathData = reversePathData(pathDataArr[i].pathData);
|
|
65
|
-
polys[i].cw = polys[i].cw ? false : true
|
|
66
|
-
cw = polys[i].cw
|
|
67
|
-
}
|
|
62
|
+
let len = includes.length;
|
|
63
|
+
//console.log('try reverse', includes);
|
|
68
64
|
|
|
69
65
|
// reverse inner sub paths
|
|
70
|
-
for (let j = 0; j <
|
|
66
|
+
for (let j = 0; len && j < len; j++) {
|
|
71
67
|
let ind = includes[j];
|
|
72
68
|
let child = polys[ind];
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
// nothing to do
|
|
71
|
+
if (child.cw !== cw) continue
|
|
72
|
+
|
|
73
|
+
pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
|
|
74
|
+
polys[ind].cw = polys[ind].cw ? false : true
|
|
75
|
+
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
78
|
|
|
@@ -3,17 +3,17 @@ export function pathDataFromPoly(pts, closed = true) {
|
|
|
3
3
|
let pathData = []
|
|
4
4
|
let subPath = []
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
// complex polygon
|
|
8
7
|
if (Array.isArray(pts[0])) {
|
|
9
|
-
pts.forEach(sub => {
|
|
8
|
+
pts.forEach(sub => {
|
|
10
9
|
subPath = [
|
|
11
10
|
{ type: 'M', values: [sub[0].x, sub[0].y] },
|
|
12
11
|
...sub.slice(1).map(pt => { return { type: 'L', values: [pt.x, pt.y] } })
|
|
13
12
|
];
|
|
13
|
+
if (closed) subPath.push({ type: 'Z', values: [] })
|
|
14
14
|
pathData.push(...subPath)
|
|
15
15
|
})
|
|
16
|
-
}else{
|
|
16
|
+
} else {
|
|
17
17
|
pathData = [
|
|
18
18
|
{ type: 'M', values: [pts[0].x, pts[0].y] },
|
|
19
19
|
...pts.slice(1).map(pt => { return { type: 'L', values: [pt.x, pt.y] } })
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { deg2rad } from "../constants";
|
|
2
|
+
import { svgArcToCenterParam, toParametricAngle } from "./geometry";
|
|
3
|
+
import { getCircleArcLength, getEllipseLengthLG, getLegendreGaussValues, getLength, waArr_global } from "./geometry_length";
|
|
4
|
+
import { getPathDataVerbose } from "./pathData_analyze";
|
|
5
|
+
import { splitSubpaths } from "./pathData_split";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function getPathDataLength(pathData = []) {
|
|
9
|
+
let len = 0
|
|
10
|
+
let pathDataArr = splitSubpaths(pathData);
|
|
11
|
+
|
|
12
|
+
for (let i = 0; i < pathDataArr.length; i++) {
|
|
13
|
+
let pathData = pathDataArr[i]
|
|
14
|
+
|
|
15
|
+
// add verbose point data if not present
|
|
16
|
+
if (pathData[0].p === undefined) pathData = getPathDataVerbose(pathData);
|
|
17
|
+
|
|
18
|
+
// Calculate Legendre Gauss weight and abscissa values
|
|
19
|
+
if (!waArr_global.length) {
|
|
20
|
+
//console.log('no LG');
|
|
21
|
+
let waArr = getLegendreGaussValues(48)
|
|
22
|
+
waArr.forEach(wa => {
|
|
23
|
+
waArr_global.push(wa)
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let waArr = waArr_global;
|
|
28
|
+
|
|
29
|
+
pathData.forEach(com => {
|
|
30
|
+
let { type, values, p0, p, cp1 = null, cp2 = null } = com;
|
|
31
|
+
let pts = [p0]
|
|
32
|
+
if (type === 'C' || type === 'Q') pts.push(cp1)
|
|
33
|
+
if (type === 'C') pts.push(cp2)
|
|
34
|
+
pts.push(p)
|
|
35
|
+
let comLen = 0
|
|
36
|
+
|
|
37
|
+
if (type === 'A') {
|
|
38
|
+
|
|
39
|
+
// get parametrized arc properties
|
|
40
|
+
let [largeArc, sweep] = [com.values[3], com.values[4]];
|
|
41
|
+
let arcData = svgArcToCenterParam(p0.x, p0.y, com.values[0], com.values[1], com.values[2], largeArc, sweep, p.x, p.y, false)
|
|
42
|
+
let { cx, cy, rx, ry, startAngle, endAngle, deltaAngle, xAxisRotation } = arcData
|
|
43
|
+
|
|
44
|
+
//is circle
|
|
45
|
+
if (rx === ry) {
|
|
46
|
+
comLen = getCircleArcLength(rx, Math.abs(deltaAngle))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// is ellipse
|
|
50
|
+
else {
|
|
51
|
+
xAxisRotation = xAxisRotation * deg2rad;
|
|
52
|
+
startAngle = toParametricAngle((startAngle - xAxisRotation), rx, ry)
|
|
53
|
+
endAngle = toParametricAngle((endAngle - xAxisRotation), rx, ry)
|
|
54
|
+
|
|
55
|
+
// recalculate parametrized delta
|
|
56
|
+
let deltaAngle_param = endAngle - startAngle;
|
|
57
|
+
|
|
58
|
+
let signChange = deltaAngle > 0 && deltaAngle_param < 0 || deltaAngle < 0 && deltaAngle_param > 0;
|
|
59
|
+
|
|
60
|
+
//deltaAngle = xAxisRotation>0 ? endAngle- startAngle: deltaAngle;
|
|
61
|
+
deltaAngle = signChange ? deltaAngle : deltaAngle_param;
|
|
62
|
+
|
|
63
|
+
// adjust end angle
|
|
64
|
+
if (sweep && startAngle > endAngle) {
|
|
65
|
+
endAngle += Math.PI * 2
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!sweep && startAngle < endAngle) {
|
|
69
|
+
endAngle -= Math.PI * 2
|
|
70
|
+
}
|
|
71
|
+
comLen = getEllipseLengthLG(rx, ry, startAngle, endAngle, waArr)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
else {
|
|
76
|
+
comLen = getLength(pts, {
|
|
77
|
+
t: 1,
|
|
78
|
+
waArr
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
len += comLen;
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return len;
|
|
86
|
+
}
|
|
@@ -103,8 +103,10 @@ const sanitizeArc = (val='', valueIndex=0) => {
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
export function parsePathDataString(d, debug = true, limit=0) {
|
|
106
|
+
if(!d) return []
|
|
106
107
|
d = d.trim();
|
|
107
108
|
|
|
109
|
+
|
|
108
110
|
if(limit) console.log('!!!limit', limit);
|
|
109
111
|
|
|
110
112
|
let pathDataObj = {
|