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.
Files changed (43) hide show
  1. package/README.md +10 -0
  2. package/dist/svg-path-simplify.esm.js +3905 -1533
  3. package/dist/svg-path-simplify.esm.min.js +13 -1
  4. package/dist/svg-path-simplify.js +3923 -1551
  5. package/dist/svg-path-simplify.min.js +13 -1
  6. package/dist/svg-path-simplify.min.js.gz +0 -0
  7. package/index.html +61 -31
  8. package/package.json +3 -5
  9. package/src/constants.js +3 -0
  10. package/src/index-node.js +0 -1
  11. package/src/index.js +26 -0
  12. package/src/pathData_simplify_cubic.js +74 -31
  13. package/src/pathData_simplify_cubicsToArcs.js +566 -0
  14. package/src/pathData_simplify_harmonize_cpts.js +170 -0
  15. package/src/pathData_simplify_revertToquadratics.js +21 -0
  16. package/src/pathSimplify-main.js +253 -86
  17. package/src/poly-fit-curve-schneider.js +570 -0
  18. package/src/simplify_poly_RDP.js +146 -0
  19. package/src/simplify_poly_radial_distance.js +100 -0
  20. package/src/svg_getViewbox.js +1 -1
  21. package/src/svgii/geometry.js +389 -63
  22. package/src/svgii/geometry_area.js +2 -1
  23. package/src/svgii/pathData_analyze.js +259 -212
  24. package/src/svgii/pathData_convert.js +91 -663
  25. package/src/svgii/pathData_fromPoly.js +12 -0
  26. package/src/svgii/pathData_parse.js +90 -89
  27. package/src/svgii/pathData_parse_els.js +3 -0
  28. package/src/svgii/pathData_parse_fontello.js +449 -0
  29. package/src/svgii/pathData_remove_collinear.js +44 -37
  30. package/src/svgii/pathData_reorder.js +2 -1
  31. package/src/svgii/pathData_simplify_redraw.js +343 -0
  32. package/src/svgii/pathData_simplify_refineCorners.js +18 -9
  33. package/src/svgii/pathData_simplify_refineExtremes.js +19 -78
  34. package/src/svgii/pathData_split.js +42 -45
  35. package/src/svgii/pathData_toPolygon.js +130 -4
  36. package/src/svgii/poly_analyze.js +470 -14
  37. package/src/svgii/poly_to_pathdata.js +224 -19
  38. package/src/svgii/rounding.js +55 -112
  39. package/src/svgii/svg_cleanup.js +13 -1
  40. package/src/svgii/visualize.js +8 -3
  41. package/{debug.cjs → tests/debug.cjs} +3 -0
  42. /package/{test.js → tests/test.js} +0 -0
  43. /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
- // Render the svg <path> element
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 = { type: "C", values: [cp1.x, cp1.y, cp2.x, cp2.y, pt1.x, pt1.y],
138
- drawLine,
139
- // add properties for chunk based simplification
140
- isExtreme, isCorner,directionChange
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);
@@ -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 = 0, len = pathData.length; i < len; 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
- let dimA = com.dimA ? +com.dimA.toFixed(8) : type !== 'M' ? +getDistAv(p0, p).toFixed(8) : 0
37
- //let dimA = +getDistAv(p0, p).toFixed(8)
38
- //console.log('dimA', dimA, com.dimA, type);
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
- if (type === 'M') {
47
- M = p;
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 = 40
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
- // Update previous coordinates
121
- p0 = { x: lastX, y: lastY };
64
+ if (decimals < 0) return pathData;
122
65
 
123
- // Track MoveTo for closing paths
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
- // Store ideal precision for next pass
130
- com.decimals = decimals;
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
- maxDecimals = decimals > maxDecimals ? decimals : maxDecimals;
135
- lastDec = decimals;
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
- * round path data
147
- * either by explicit decimal value or
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
- if (valLen && (decimals > -1) ) {
97
+ // Z commands have no values
98
+ if (!valLen) {
99
+ c++; continue
100
+ }
160
101
 
161
- for(let v=0; v<valLen; v++){
162
- //pathData[c].values[v] = values[v] ? +values[v].toFixed(decimals) : values[v];
163
- pathData[c].values[v] = +values[v].toFixed(decimals);
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);
@@ -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
  }
@@ -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%", stroke = "purple", render = true) {
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