svg-path-simplify 0.1.3 → 0.2.2
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 +10 -0
- package/dist/svg-path-simplify.esm.js +3905 -1533
- package/dist/svg-path-simplify.esm.min.js +13 -1
- package/dist/svg-path-simplify.js +3923 -1551
- package/dist/svg-path-simplify.min.js +13 -1
- package/dist/svg-path-simplify.min.js.gz +0 -0
- package/index.html +61 -31
- package/package.json +3 -5
- package/src/constants.js +3 -0
- package/src/index-node.js +0 -1
- package/src/index.js +26 -0
- package/src/pathData_simplify_cubic.js +74 -31
- package/src/pathData_simplify_cubicsToArcs.js +566 -0
- package/src/pathData_simplify_harmonize_cpts.js +170 -0
- package/src/pathData_simplify_revertToquadratics.js +21 -0
- package/src/pathSimplify-main.js +253 -86
- package/src/poly-fit-curve-schneider.js +570 -0
- package/src/simplify_poly_RDP.js +146 -0
- package/src/simplify_poly_radial_distance.js +100 -0
- package/src/svg_getViewbox.js +1 -1
- package/src/svgii/geometry.js +389 -63
- package/src/svgii/geometry_area.js +2 -1
- package/src/svgii/pathData_analyze.js +259 -212
- package/src/svgii/pathData_convert.js +91 -663
- package/src/svgii/pathData_fromPoly.js +12 -0
- package/src/svgii/pathData_parse.js +90 -89
- package/src/svgii/pathData_parse_els.js +3 -0
- package/src/svgii/pathData_parse_fontello.js +449 -0
- package/src/svgii/pathData_remove_collinear.js +44 -37
- package/src/svgii/pathData_reorder.js +2 -1
- package/src/svgii/pathData_simplify_redraw.js +343 -0
- package/src/svgii/pathData_simplify_refineCorners.js +18 -9
- package/src/svgii/pathData_simplify_refineExtremes.js +19 -78
- package/src/svgii/pathData_split.js +42 -45
- package/src/svgii/pathData_toPolygon.js +130 -4
- package/src/svgii/poly_analyze.js +470 -14
- package/src/svgii/poly_to_pathdata.js +224 -19
- package/src/svgii/rounding.js +55 -112
- package/src/svgii/svg_cleanup.js +13 -1
- package/src/svgii/visualize.js +8 -3
- package/{debug.cjs → tests/debug.cjs} +3 -0
- /package/{test.js → tests/test.js} +0 -0
- /package/{testSVG.js → tests/testSVG.js} +0 -0
|
@@ -5,17 +5,221 @@
|
|
|
5
5
|
* https://francoisromain.medium.com/smooth-a-svg-path-with-cubic-bezier-curves-e37b49d46c74
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { checkLineIntersection, interpolate, mirrorCpts } from "./geometry";
|
|
8
|
+
import { checkLineIntersection, getDistManhattan, interpolate, mirrorCpts } from "./geometry";
|
|
9
9
|
import { getPolyBBox } from "./geometry_bbox";
|
|
10
|
-
import { isClosedPolygon } from "./poly_analyze";
|
|
10
|
+
import { analyzePoly, getPolyChunks, isClosedPolygon } from "./poly_analyze";
|
|
11
|
+
import { fitCurveN } from "../poly-fit-curve-schneider";
|
|
12
|
+
import { renderPath, renderPoint, renderPoly } from "./visualize";
|
|
13
|
+
import { simplifyRDP } from "../simplify_poly_RDP";
|
|
14
|
+
import { pathDataFromPoly } from "./pathData_fromPoly";
|
|
15
|
+
|
|
16
|
+
export function simplifyPolygonToPathData(pts, {
|
|
17
|
+
debug = false,
|
|
18
|
+
width = 0,
|
|
19
|
+
height = 0,
|
|
20
|
+
denoise = 0.9,
|
|
21
|
+
keepCorners=true,
|
|
22
|
+
keepExtremes=true,
|
|
23
|
+
keepInflections=false,
|
|
24
|
+
manhattan = false,
|
|
25
|
+
absolute = false,
|
|
26
|
+
closed = true,
|
|
27
|
+
tolerance = 1
|
|
28
|
+
} = {}) {
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
denoise = 0
|
|
32
|
+
|
|
33
|
+
// denoise via RDP
|
|
34
|
+
if (denoise && denoise !== 1) {
|
|
35
|
+
pts = simplifyRDP(pts, {
|
|
36
|
+
width,
|
|
37
|
+
height,
|
|
38
|
+
quality: denoise,
|
|
39
|
+
manhattan,
|
|
40
|
+
absolute
|
|
41
|
+
})
|
|
11
42
|
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// get topology of poly
|
|
46
|
+
let polyAnalyzed = !keepExtremes&&!keepCorners ? pts : analyzePoly(pts, {
|
|
47
|
+
debug: false
|
|
48
|
+
//width,
|
|
49
|
+
//height
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
// split into segment chunks
|
|
54
|
+
let chunks = !keepExtremes&&!keepCorners ? [polyAnalyzed] : getPolyChunks(polyAnalyzed, {keepCorners,keepExtremes, keepInflections});
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// Schneider curve fit
|
|
58
|
+
let threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5
|
|
59
|
+
|
|
60
|
+
threshold=2
|
|
61
|
+
|
|
62
|
+
let polyPath = simplifyPolyChunks(chunks, {
|
|
63
|
+
closed,
|
|
64
|
+
tolerance: threshold
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return polyPath;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* convert polygon chunks
|
|
73
|
+
* to cubic beziers
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
export function simplifyPolyChunks(chunks = [], {
|
|
77
|
+
closed = true,
|
|
78
|
+
tolerance = 1,
|
|
79
|
+
} = {}) {
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
let l = chunks.length;
|
|
83
|
+
|
|
84
|
+
// new pathData
|
|
85
|
+
let pathData = [{ type: 'M', values: [chunks[0][0].x, chunks[0][0].y] }]
|
|
86
|
+
//let pathData = []
|
|
87
|
+
//console.log('chunks', chunks);
|
|
88
|
+
|
|
89
|
+
for (let i = 0; i < l; i++) {
|
|
90
|
+
|
|
91
|
+
let chunk = chunks[i];
|
|
92
|
+
let chunkN = chunks[i + 1] ? chunks[i + 1] : null
|
|
93
|
+
let segments = []
|
|
94
|
+
let chunklen = chunk.length
|
|
95
|
+
let pLast = chunk[chunk.length - 1]
|
|
96
|
+
|
|
97
|
+
// add from next command
|
|
98
|
+
if (chunkN) {
|
|
99
|
+
chunk.push(chunkN[0])
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// nothing to simplify
|
|
103
|
+
if (chunklen < 2 || (chunklen === 2 && chunk[1].isExtreme) ) {
|
|
104
|
+
pLast = chunk[chunk.length - 1]
|
|
105
|
+
segments = chunk.map(com => { return { type: 'L', values: [com.x, com.y] } })
|
|
106
|
+
|
|
107
|
+
} else {
|
|
108
|
+
segments = fitCurveN(chunk, tolerance)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// remove first segment to connect to last segment
|
|
112
|
+
pathData.push(...segments)
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if (closed) pathData.push({ type: 'Z', values: [] })
|
|
118
|
+
|
|
119
|
+
//console.log('!!!pathData', pathData);
|
|
120
|
+
|
|
121
|
+
// refine extremes
|
|
122
|
+
refineAdjacentExtremes(pathData)
|
|
123
|
+
return pathData
|
|
124
|
+
|
|
125
|
+
}
|
|
12
126
|
|
|
13
|
-
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
/* fix almost colinear control point tangents */
|
|
130
|
+
export function refineAdjacentExtremes(pathData) {
|
|
131
|
+
let l = pathData.length;
|
|
132
|
+
|
|
133
|
+
for (let i = 1; i < l; i++) {
|
|
134
|
+
let com = pathData[i]
|
|
135
|
+
let comN = pathData[i + 1] || null
|
|
136
|
+
let { type, values } = com;
|
|
137
|
+
|
|
138
|
+
if (type === 'C' && comN && comN.type === 'C') {
|
|
139
|
+
let valuesN = comN.values
|
|
140
|
+
let cp1_1 = { x: values[0], y: values[1] }
|
|
141
|
+
let cp2_1 = { x: values[2], y: values[3] }
|
|
142
|
+
let p = { x: values[4], y: values[5] }
|
|
143
|
+
|
|
144
|
+
let cp1_2 = { x: valuesN[0], y: valuesN[1] }
|
|
145
|
+
let cp2_2 = { x: valuesN[2], y: valuesN[3] }
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
let dx1 = Math.abs(cp2_1.x - p.x)
|
|
149
|
+
let dy1 = Math.abs(cp2_1.y - p.y)
|
|
150
|
+
|
|
151
|
+
let dx2 = Math.abs(cp1_2.x - p.x)
|
|
152
|
+
let dy2 = Math.abs(cp1_2.y - p.y)
|
|
153
|
+
|
|
154
|
+
let dist1 = getDistManhattan(cp1_2, cp2_1) * 0.02
|
|
155
|
+
|
|
156
|
+
// is almost horizontal
|
|
157
|
+
let horizontal1 = dy1 < dist1 && dx1 > dist1
|
|
158
|
+
let horizontal2 = dy2 < dist1 && dx2 > dist1
|
|
159
|
+
let vertical1 = dx1 < dist1 && dy1 > dist1
|
|
160
|
+
let vertical2 = dx2 < dist1 && dy2 > dist1
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
let distCpO=0, distCpN=0, t=1;
|
|
164
|
+
let cp2N, cp1N;
|
|
165
|
+
|
|
166
|
+
if (horizontal1 || vertical1) {
|
|
167
|
+
|
|
168
|
+
// adjust cp2 to horizontal
|
|
169
|
+
cp2N = horizontal1 ? {x:com.values[2], y: p.y} : (vertical1 ? {x:p.x, y:com.values[3]} : {x:com.values[2], y:com.values[3]})
|
|
170
|
+
|
|
171
|
+
/*
|
|
172
|
+
// adjust length
|
|
173
|
+
distCpO = getDistManhattan(cp2_1, p)
|
|
174
|
+
distCpN = getDistManhattan(cp2N, p)
|
|
175
|
+
|
|
176
|
+
// interpolate
|
|
177
|
+
t = distCpN/distCpO
|
|
178
|
+
console.log(t, distCpO, distCpN);
|
|
179
|
+
cp2N = t>0.97 && t<0.99 ? interpolate(p, cp2N, t) : cp2N
|
|
180
|
+
*/
|
|
181
|
+
com.values[2] = cp2N.x
|
|
182
|
+
com.values[3] = cp2N.y
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if (horizontal2 || vertical2) {
|
|
188
|
+
// adjust cp1 to horizontal
|
|
189
|
+
|
|
190
|
+
cp1N = horizontal2 ?
|
|
191
|
+
{x:comN.values[0], y: p.y} :
|
|
192
|
+
(vertical2 ? {x:p.x, y:comN.values[1]} : {x:comN.values[0], y:comN.values[1]})
|
|
193
|
+
|
|
194
|
+
/*
|
|
195
|
+
// adjust length
|
|
196
|
+
distCpO = getDistManhattan(cp1_2, p)
|
|
197
|
+
distCpN = getDistManhattan(cp1N, p)
|
|
198
|
+
|
|
199
|
+
let sign = distCpO>distCpN ? -1 : 1
|
|
200
|
+
|
|
201
|
+
// interpolate
|
|
202
|
+
t = distCpN/distCpO
|
|
203
|
+
cp1N = t>0.97 && t<0.99 ? interpolate(p, cp1N, t) : cp1N;
|
|
204
|
+
//console.log(t, sign, distCpO, distCpN);
|
|
205
|
+
*/
|
|
206
|
+
|
|
207
|
+
pathData[i + 1].values[0] = cp1N.x
|
|
208
|
+
pathData[i + 1].values[1] = cp1N.y
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
// old Render the svg <path> element
|
|
14
218
|
export function getCurvePathData(pts, t = 0.666, closed = 'auto', keepCorners = true) {
|
|
15
219
|
|
|
16
220
|
|
|
17
221
|
//auto detect closed polygon
|
|
18
|
-
if(closed==='auto'){
|
|
222
|
+
if (closed === 'auto') {
|
|
19
223
|
closed = isClosedPolygon(pts)
|
|
20
224
|
}
|
|
21
225
|
|
|
@@ -51,7 +255,7 @@ export function getCurvePathData(pts, t = 0.666, closed = 'auto', keepCorners =
|
|
|
51
255
|
|
|
52
256
|
// collect smoothed pathData
|
|
53
257
|
let pathData = [];
|
|
54
|
-
pathData.push({ type: "M", values: [pts[0].x, pts[0].y], p0:{x:pts[0].x, y:pts[0].y} });
|
|
258
|
+
pathData.push({ type: "M", values: [pts[0].x, pts[0].y], p0: { x: pts[0].x, y: pts[0].y } });
|
|
55
259
|
|
|
56
260
|
let cp2_0 = pts[0];
|
|
57
261
|
let l = pts.length;
|
|
@@ -69,7 +273,7 @@ export function getCurvePathData(pts, t = 0.666, closed = 'auto', keepCorners =
|
|
|
69
273
|
let cp1 = controlPoint(pt0, ptPrev, pt1, false, t);
|
|
70
274
|
let cp2 = controlPoint(pt1, pt0, ptNext, true, t);
|
|
71
275
|
|
|
72
|
-
let {isExtreme, isCorner,directionChange} = pt1;
|
|
276
|
+
let { isExtreme, isCorner, directionChange } = pt1;
|
|
73
277
|
|
|
74
278
|
// get cp vector intersections
|
|
75
279
|
let cpI = checkLineIntersection(pt0, cp1, pt1, cp2, false);
|
|
@@ -95,7 +299,7 @@ export function getCurvePathData(pts, t = 0.666, closed = 'auto', keepCorners =
|
|
|
95
299
|
cpI = !interH ? cpI : (cpI2 ? cpI2 : null)
|
|
96
300
|
|
|
97
301
|
//&& i < l - 3
|
|
98
|
-
if (cpI
|
|
302
|
+
if (cpI) {
|
|
99
303
|
cp1 = interpolate(pt0, cpI, t)
|
|
100
304
|
cp2 = interpolate(pt1, cpI, t)
|
|
101
305
|
//renderPoint(svg, cpI, 'magenta')
|
|
@@ -134,18 +338,19 @@ export function getCurvePathData(pts, t = 0.666, closed = 'auto', keepCorners =
|
|
|
134
338
|
// update last cp2
|
|
135
339
|
cp2_0 = cp2;
|
|
136
340
|
|
|
137
|
-
let com = {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
341
|
+
let com = {
|
|
342
|
+
type: "C", values: [cp1.x, cp1.y, cp2.x, cp2.y, pt1.x, pt1.y],
|
|
343
|
+
drawLine,
|
|
344
|
+
// add properties for chunk based simplification
|
|
345
|
+
isExtreme, isCorner, directionChange
|
|
346
|
+
};
|
|
142
347
|
|
|
143
348
|
|
|
144
349
|
let values = com.values
|
|
145
350
|
com.p0 = pt0
|
|
146
|
-
com.cp1 = {x:values[0], y:values[1]}
|
|
147
|
-
com.cp2 = {x:values[2], y:values[3]}
|
|
148
|
-
com.p = {x:values[4], y:values[5]}
|
|
351
|
+
com.cp1 = { x: values[0], y: values[1] }
|
|
352
|
+
com.cp2 = { x: values[2], y: values[3] }
|
|
353
|
+
com.p = { x: values[4], y: values[5] }
|
|
149
354
|
|
|
150
355
|
|
|
151
356
|
pathData.push(com);
|
|
@@ -161,10 +366,10 @@ export function getCurvePathData(pts, t = 0.666, closed = 'auto', keepCorners =
|
|
|
161
366
|
pathData[1].values = [valuesLastC[0], valuesLastC[1], ...valuesFirstC.slice(2)]
|
|
162
367
|
let values0 = pathData[0].values
|
|
163
368
|
let values = pathData[1].values
|
|
164
|
-
pathData[1].p0 = {x:values0[0], y:values0[1]}
|
|
165
|
-
pathData[1].cp1 = {x:values[0], y:values[1]}
|
|
166
|
-
pathData[1].cp2 = {x:values[2], y:values[3]}
|
|
167
|
-
pathData[1].p = {x:values[4], y:values[5]}
|
|
369
|
+
pathData[1].p0 = { x: values0[0], y: values0[1] }
|
|
370
|
+
pathData[1].cp1 = { x: values[0], y: values[1] }
|
|
371
|
+
pathData[1].cp2 = { x: values[2], y: values[3] }
|
|
372
|
+
pathData[1].p = { x: values[4], y: values[5] }
|
|
168
373
|
|
|
169
374
|
// delete last curveto
|
|
170
375
|
pathData = pathData.slice(0, pathData.length - 1);
|
package/src/svgii/rounding.js
CHANGED
|
@@ -4,66 +4,42 @@
|
|
|
4
4
|
* for further rounding/optimizations
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { getDistAv } from "./geometry";
|
|
7
|
+
import { getDistAv, getDistManhattan } from "./geometry";
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
export function detectAccuracy(pathData) {
|
|
11
11
|
|
|
12
|
-
// Reference first MoveTo command (M)
|
|
13
|
-
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
14
|
-
let p0 = M
|
|
15
|
-
let p = M
|
|
16
|
-
pathData[0].decimals = 0
|
|
17
|
-
let lastDec = 0;
|
|
18
|
-
let maxDecimals = 0
|
|
19
12
|
let minDim = Infinity
|
|
20
|
-
let maxDim = 0
|
|
21
|
-
|
|
22
|
-
//console.log('detectAccuracy');
|
|
23
|
-
|
|
24
|
-
//let dims = new Set();
|
|
25
13
|
let dims = [];
|
|
14
|
+
//console.log(pathData);
|
|
26
15
|
|
|
27
16
|
// add average distances
|
|
28
|
-
for (let i =
|
|
17
|
+
for (let i = 1, len = pathData.length; i < len; i++) {
|
|
29
18
|
let com = pathData[i];
|
|
30
|
-
let { type, values } = com;
|
|
31
|
-
|
|
32
|
-
let lastVals = values.length ? values.slice(-2) : [M.x, M.y];
|
|
33
|
-
p = { x: lastVals[0], y: lastVals[1] }
|
|
19
|
+
let { type, values, p0 = null, p = null, dimA = 0 } = com;
|
|
34
20
|
|
|
35
21
|
// use existing averave dimension value or calculate
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (dimA) dims.push(dimA);
|
|
41
|
-
|
|
42
|
-
if (dimA && dimA < minDim) minDim = dimA;
|
|
43
|
-
if (dimA && dimA > maxDim) maxDim = dimA;
|
|
22
|
+
if (values.length && p && p0) {
|
|
23
|
+
//console.log(com);
|
|
24
|
+
dimA = dimA ? dimA : getDistManhattan(p0, p);
|
|
44
25
|
|
|
26
|
+
//let dimA = +getDistAv(p0, p).toFixed(8)
|
|
27
|
+
//console.log('dimA', dimA, com.dimA, type);
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
if (dimA) dims.push(dimA);
|
|
30
|
+
if (dimA && dimA < minDim) minDim = dimA;
|
|
31
|
+
//if (dimA && dimA > maxDim) maxDim = dimA;
|
|
48
32
|
}
|
|
49
|
-
p0 = p;
|
|
50
|
-
}
|
|
51
33
|
|
|
34
|
+
}
|
|
52
35
|
|
|
53
36
|
let dim_min = dims.sort()
|
|
54
|
-
|
|
55
|
-
/*
|
|
56
|
-
let minVal = dim_min.length > 15 ?
|
|
57
|
-
(dim_min[0] + dim_min[2]) / 2 :
|
|
58
|
-
dim_min[0];
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
let sliceIdx = Math.ceil(dim_min.length / 10);
|
|
37
|
+
let sliceIdx = Math.ceil(dim_min.length / 8);
|
|
62
38
|
dim_min = dim_min.slice(0, sliceIdx);
|
|
63
39
|
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
64
40
|
|
|
65
|
-
let threshold =
|
|
66
|
-
let decimalsAuto = minVal > threshold*1.5 ? 0 : Math.floor(threshold / minVal).toString().length
|
|
41
|
+
let threshold = 75
|
|
42
|
+
let decimalsAuto = minVal > threshold * 1.5 ? 0 : Math.floor(threshold / minVal).toString().length
|
|
67
43
|
|
|
68
44
|
// clamp
|
|
69
45
|
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
@@ -71,98 +47,65 @@ export function detectAccuracy(pathData) {
|
|
|
71
47
|
}
|
|
72
48
|
|
|
73
49
|
|
|
50
|
+
export function roundTo(num = 0, decimals = 3) {
|
|
51
|
+
if (!decimals) return Math.round(num);
|
|
52
|
+
let factor = 10 ** decimals;
|
|
53
|
+
return Math.round(num * factor) / factor;
|
|
54
|
+
}
|
|
74
55
|
|
|
75
|
-
export function detectAccuracy_back(pathData) {
|
|
76
|
-
|
|
77
|
-
// Reference first MoveTo command (M)
|
|
78
|
-
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
79
|
-
let p0 = { ...M };
|
|
80
|
-
pathData[0].decimals = 0
|
|
81
|
-
let lastDec = 0;
|
|
82
|
-
let maxDecimals = 0
|
|
83
|
-
|
|
84
|
-
for (let i = 1, len = pathData.length; i < len; i++) {
|
|
85
|
-
let com = pathData[i];
|
|
86
|
-
let { type, values } = com;
|
|
87
|
-
|
|
88
|
-
let lastVals = values.length ? values.slice(-2) : [M.x, M.y];
|
|
89
|
-
let lastX = lastVals[0];
|
|
90
|
-
let lastY = lastVals[1];
|
|
91
|
-
|
|
92
|
-
if (type === 'Z' || type === 'z') {
|
|
93
|
-
lastX = M.x;
|
|
94
|
-
lastY = M.y;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
let w = Math.abs(p0.x - lastX);
|
|
98
|
-
let h = Math.abs(p0.y - lastY);
|
|
99
|
-
let dimA = (w + h) / 2 || 0;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Determine decimal places dynamically
|
|
103
|
-
let decimals = (type !== 'Z' && type !== 'z') ? Math.ceil((1 / dimA)).toString().length + 1 : 0;
|
|
104
|
-
|
|
105
|
-
//console.log(type, dimA, decimals);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (dimA === 0) {
|
|
109
|
-
//console.log('zero length');
|
|
110
|
-
decimals = lastDec;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
else if (decimals && dimA < 0.5) {
|
|
114
|
-
decimals++
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
//console.log('dimA', type, dimA, decimals);
|
|
118
56
|
|
|
57
|
+
/**
|
|
58
|
+
* round path data
|
|
59
|
+
* either by explicit decimal value or
|
|
60
|
+
* based on suggested accuracy in path data
|
|
61
|
+
*/
|
|
62
|
+
export function roundPathData(pathData, decimals = -1) {
|
|
119
63
|
|
|
120
|
-
|
|
121
|
-
p0 = { x: lastX, y: lastY };
|
|
64
|
+
if (decimals < 0) return pathData;
|
|
122
65
|
|
|
123
|
-
|
|
124
|
-
if (type === 'M') {
|
|
125
|
-
M = { x: values[0], y: values[1] };
|
|
126
|
-
com.decimals = decimals;
|
|
127
|
-
} else {
|
|
66
|
+
let len = pathData.length;
|
|
128
67
|
|
|
129
|
-
|
|
130
|
-
|
|
68
|
+
for (let c = 0; c < len; c++) {
|
|
69
|
+
//let com = pathData[c];
|
|
70
|
+
let values = pathData[c].values
|
|
71
|
+
let valLen = values.length;
|
|
72
|
+
if (!valLen) continue
|
|
131
73
|
|
|
74
|
+
for (let v = 0; v < valLen; v++) {
|
|
75
|
+
//pathData[c].values[v] = +values[v].toFixed(decimals);
|
|
76
|
+
pathData[c].values[v] = roundTo(values[v], decimals);
|
|
132
77
|
}
|
|
78
|
+
};
|
|
133
79
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// set max decimal for M
|
|
139
|
-
return maxDecimals
|
|
140
|
-
//pathData[0].decimals = maxDecimals
|
|
141
|
-
//return pathData
|
|
80
|
+
//console.log(pathData);
|
|
81
|
+
return pathData;
|
|
142
82
|
}
|
|
143
83
|
|
|
144
84
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
* based on suggested accuracy in path data
|
|
149
|
-
*/
|
|
150
|
-
export function roundPathData(pathData, decimals = -1) {
|
|
85
|
+
export function roundPathData_(pathData, decimals = -1) {
|
|
86
|
+
|
|
87
|
+
if (decimals < 0) return pathData;
|
|
151
88
|
|
|
152
89
|
let len = pathData.length;
|
|
90
|
+
let c = 0;
|
|
91
|
+
while (c < len) {
|
|
153
92
|
|
|
154
|
-
for (let c = 0; c < len; c++) {
|
|
155
93
|
//let com = pathData[c];
|
|
156
94
|
let values = pathData[c].values
|
|
157
95
|
let valLen = values.length;
|
|
158
96
|
|
|
159
|
-
|
|
97
|
+
// Z commands have no values
|
|
98
|
+
if (!valLen) {
|
|
99
|
+
c++; continue
|
|
100
|
+
}
|
|
160
101
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
102
|
+
let v = 0;
|
|
103
|
+
while (v < valLen) {
|
|
104
|
+
pathData[c].values[v] = roundTo(values[v], decimals);
|
|
105
|
+
v++
|
|
165
106
|
}
|
|
107
|
+
c++
|
|
108
|
+
|
|
166
109
|
};
|
|
167
110
|
|
|
168
111
|
//console.log(pathData);
|
package/src/svgii/svg_cleanup.js
CHANGED
|
@@ -14,14 +14,19 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
14
14
|
removeHidden = true,
|
|
15
15
|
removeUnused = true,
|
|
16
16
|
} = {}) {
|
|
17
|
+
|
|
17
18
|
svgMarkup = cleanSvgPrologue(svgMarkup);
|
|
18
19
|
|
|
19
20
|
// replace namespaced refs
|
|
20
21
|
svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
|
|
22
|
+
//console.log('!!!svgMarkup', svgMarkup);
|
|
23
|
+
|
|
21
24
|
|
|
22
25
|
let svg = new DOMParser()
|
|
26
|
+
//.parseFromString(svgMarkup, "image/svg+xml")
|
|
23
27
|
.parseFromString(svgMarkup, "text/html")
|
|
24
28
|
.querySelector("svg");
|
|
29
|
+
//console.log(svg);
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin'];
|
|
@@ -30,6 +35,7 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
30
35
|
let removeEls = ['metadata', 'script']
|
|
31
36
|
|
|
32
37
|
let els = svg.querySelectorAll('*')
|
|
38
|
+
|
|
33
39
|
els.forEach(el => {
|
|
34
40
|
let name = el.nodeName;
|
|
35
41
|
// remove hidden elements
|
|
@@ -44,10 +50,16 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
44
50
|
}
|
|
45
51
|
})
|
|
46
52
|
|
|
53
|
+
//console.log('svg', svg);
|
|
54
|
+
//return
|
|
55
|
+
|
|
47
56
|
if (returnDom) return svg
|
|
48
57
|
|
|
49
58
|
let markup = stringifySVG(svg)
|
|
50
|
-
//console.log(markup);
|
|
59
|
+
//console.log('!!!markup', markup);
|
|
60
|
+
|
|
61
|
+
//return
|
|
62
|
+
|
|
51
63
|
|
|
52
64
|
return markup;
|
|
53
65
|
}
|
package/src/svgii/visualize.js
CHANGED
|
@@ -250,11 +250,16 @@ export function renderPath(svg, d = '', stroke = 'green', strokeWidth = '1%', op
|
|
|
250
250
|
|
|
251
251
|
|
|
252
252
|
// debug helper: render lines
|
|
253
|
-
export function renderPoly(svg, pts, strokeWidth = "1%",
|
|
253
|
+
export function renderPoly(svg, pts, stroke = "purple", strokeWidth = "1%", fillOpacity="0.5", fill="none", polygon=true , render = true) {
|
|
254
254
|
pts = pts.map(pt => { return [pt.x, pt.y] }).flat().join(' ');
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
polygon = pts.length===2 ? false : polygon;
|
|
258
|
+
|
|
259
|
+
let poly =polygon ?
|
|
260
|
+
`<polygon stroke-width="${strokeWidth}" points="${pts}" stroke="${stroke}" fill="${fill}" fill-opacity="${fillOpacity}" />`:
|
|
261
|
+
`<polyline stroke-width="${strokeWidth}" points="${pts}" stroke="${stroke}" fill="${fill}" fill-opacity="${fillOpacity}" />`;
|
|
255
262
|
|
|
256
|
-
let poly =
|
|
257
|
-
`<polyline stroke-width="${strokeWidth}" points="${pts}" stroke="${stroke}" />`;
|
|
258
263
|
|
|
259
264
|
if (render) {
|
|
260
265
|
svg.insertAdjacentHTML("beforeend", poly);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// debug.cjs
|
|
2
|
+
/*
|
|
2
3
|
const { createRequire } = require('module');
|
|
3
4
|
const requireModule = createRequire(__filename);
|
|
4
5
|
|
|
@@ -9,3 +10,5 @@ console.log('require.resolve →', requireModule.resolve('svg-path-simplify'));
|
|
|
9
10
|
console.log('imported keys →', Object.keys(mod));
|
|
10
11
|
console.log('default keys →', mod.default && Object.keys(mod.default));
|
|
11
12
|
})();
|
|
13
|
+
|
|
14
|
+
*/
|
|
File without changes
|
|
File without changes
|