svg-path-simplify 0.3.0 → 0.3.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/README.md +3 -1
- package/dist/svg-path-simplify.esm.js +2786 -2764
- package/dist/svg-path-simplify.esm.min.js +5 -7
- package/dist/svg-path-simplify.js +2786 -2764
- package/dist/svg-path-simplify.min.js +5 -7
- package/dist/svg-path-simplify.pathdata.esm.js +31 -2
- package/dist/svg-path-simplify.pathdata.esm.min.js +5 -5
- package/index.html +209 -158
- package/package.json +1 -1
- package/src/detect_input.js +30 -3
- package/src/pathSimplify-main.js +131 -66
- package/src/poly-fit-curve-schneider.js +266 -175
- package/src/simplify_poly_RDP.js +101 -1
- package/src/simplify_poly_radial_distance.js +85 -2
- package/src/svgii/geometry.js +1 -0
- package/src/svgii/pathData_fromPoly.js +22 -7
- package/src/svgii/pathData_toPolygon.js +122 -230
- package/src/svgii/poly_analyze.js +114 -435
- package/src/svgii/poly_analyze_get_chunks.js +67 -0
- package/src/svgii/poly_normalize.js +51 -0
- package/src/svgii/poly_to_pathdata.js +51 -24
- package/src/svgii/rounding.js +36 -0
- package/src/svgii/svg_cleanup.js +10 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
// split in chunks based on significant points
|
|
3
|
+
|
|
4
|
+
import { pathDataToD } from "./pathData_stringify";
|
|
5
|
+
import { renderPath } from "./visualize";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function getPolyChunks(pts,
|
|
9
|
+
{ closed = true,
|
|
10
|
+
keepCorners = true,
|
|
11
|
+
keepExtremes = true,
|
|
12
|
+
keepInflections = false
|
|
13
|
+
} = {}
|
|
14
|
+
) {
|
|
15
|
+
let chunks = [[pts[0]]];
|
|
16
|
+
//let chunk = [pts[0]];
|
|
17
|
+
let idx = 0
|
|
18
|
+
let lastChunk = chunks[idx]
|
|
19
|
+
|
|
20
|
+
let l = pts.length
|
|
21
|
+
|
|
22
|
+
// render
|
|
23
|
+
for (let i = 1; i < l; i++) {
|
|
24
|
+
let p0 = i > 0 ? pts[i] : pts[l - 1];
|
|
25
|
+
let p1 = pts[i];
|
|
26
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
27
|
+
|
|
28
|
+
// start new chunk
|
|
29
|
+
// keepInflections && p1.isDirChange
|
|
30
|
+
if ((keepExtremes && p1.isExtreme || keepCorners && p1.isCorner )) {
|
|
31
|
+
idx++
|
|
32
|
+
chunks.push([])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
lastChunk = chunks[idx]
|
|
37
|
+
lastChunk.push(p1)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// test render
|
|
41
|
+
//renderchunks(chunks)
|
|
42
|
+
|
|
43
|
+
return chunks;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
function renderchunks(chunks) {
|
|
49
|
+
|
|
50
|
+
chunks.forEach((chunk, i) => {
|
|
51
|
+
|
|
52
|
+
let stroke = i % 2 === 0 ? 'orange' : 'blue';
|
|
53
|
+
let pathData = [{ type: 'M', values: [chunk[0].x, chunk[0].y] }]
|
|
54
|
+
let d = `M`
|
|
55
|
+
|
|
56
|
+
chunk.forEach(pt => {
|
|
57
|
+
|
|
58
|
+
pathData.push({ type: 'L', values: [pt.x, pt.y] })
|
|
59
|
+
d += ` ${[pt.x, pt.y].join(' ')}`
|
|
60
|
+
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
d = pathDataToD(pathData)
|
|
64
|
+
renderPath(markers, d, stroke, '2%', '0.5')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function normalizePoly(pts, {
|
|
2
|
+
toObject = true,
|
|
3
|
+
toArray = false,
|
|
4
|
+
flatten = false
|
|
5
|
+
} = {}) {
|
|
6
|
+
|
|
7
|
+
if (flatten) pts = pts.flat(2);
|
|
8
|
+
let poly = toArray ? polyPtsToArray(pts) : polyArrayToObject(pts)
|
|
9
|
+
return poly
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export function polyArrayToObject(pts) {
|
|
14
|
+
|
|
15
|
+
// is point object array
|
|
16
|
+
if (pts[0].x !== undefined && pts[0].y !== undefined) return pts
|
|
17
|
+
|
|
18
|
+
let poly = [];
|
|
19
|
+
|
|
20
|
+
// complex poly object array
|
|
21
|
+
if (Array.isArray(pts[0]) && pts[0][0].x !== undefined && pts[0][0].y !== undefined) {
|
|
22
|
+
return pts
|
|
23
|
+
}
|
|
24
|
+
// complex poly value array
|
|
25
|
+
else if (Array.isArray(pts[0][0]) && pts[0][0].length === 2) {
|
|
26
|
+
pts.forEach(sub => {
|
|
27
|
+
poly.push(sub.map(pt => { return { x: pt[0], y: pt[1] } }))
|
|
28
|
+
})
|
|
29
|
+
return poly
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return pts.map(pt => { return { x: pt[0], y: pt[1] } })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
export function polyPtsToArray(pts) {
|
|
37
|
+
|
|
38
|
+
// is already coordinate array
|
|
39
|
+
if (!Array.isArray(pts[0][0]) && pts[0].length === 2) return pts
|
|
40
|
+
|
|
41
|
+
let poly = [];
|
|
42
|
+
if (Array.isArray(pts[0][0]) && pts[0][0].length === 2) {
|
|
43
|
+
pts.forEach(sub => {
|
|
44
|
+
poly.push(sub.map(pt => [pt.x, pt.y]))
|
|
45
|
+
})
|
|
46
|
+
return poly
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
poly = Array.from(pts).map(pt => [pt.x, pt.y])
|
|
50
|
+
return poly
|
|
51
|
+
}
|
|
@@ -7,61 +7,84 @@
|
|
|
7
7
|
|
|
8
8
|
import { checkLineIntersection, getDistManhattan, interpolate, mirrorCpts } from "./geometry";
|
|
9
9
|
import { getPolyBBox } from "./geometry_bbox";
|
|
10
|
-
import { analyzePoly, getPolyChunks, isClosedPolygon } from "./poly_analyze";
|
|
11
|
-
import { fitCurveN } from "../poly-fit-curve-schneider";
|
|
12
10
|
import { renderPath, renderPoint, renderPoly } from "./visualize";
|
|
13
|
-
import {
|
|
11
|
+
import { simplifyPolyRDP } from "../simplify_poly_RDP";
|
|
14
12
|
import { pathDataFromPoly } from "./pathData_fromPoly";
|
|
13
|
+
import { getPolyChunks } from "./poly_analyze_get_chunks";
|
|
14
|
+
import { analyzePoly, isClosedPolygon } from "./poly_analyze";
|
|
15
|
+
import { fitCurveSchneider } from "../poly-fit-curve-schneider";
|
|
16
|
+
import { simplifyPolyRD } from "../simplify_poly_radial_distance";
|
|
17
|
+
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
export function simplifyPolygonToPathData(pts, {
|
|
17
21
|
debug = false,
|
|
18
22
|
width = 0,
|
|
19
23
|
height = 0,
|
|
20
24
|
denoise = 0.9,
|
|
21
|
-
keepCorners=true,
|
|
22
|
-
keepExtremes=true,
|
|
23
|
-
keepInflections=false,
|
|
25
|
+
keepCorners = true,
|
|
26
|
+
keepExtremes = true,
|
|
27
|
+
keepInflections = false,
|
|
24
28
|
manhattan = false,
|
|
25
29
|
absolute = false,
|
|
26
30
|
closed = true,
|
|
27
|
-
tolerance = 1
|
|
31
|
+
tolerance = 1,
|
|
32
|
+
simplifyRD = 1,
|
|
33
|
+
simplifyRDP = 1,
|
|
28
34
|
} = {}) {
|
|
29
35
|
|
|
30
36
|
|
|
31
|
-
denoise = 0
|
|
32
37
|
|
|
38
|
+
/*
|
|
33
39
|
// denoise via RDP
|
|
34
40
|
if (denoise && denoise !== 1) {
|
|
35
|
-
pts =
|
|
41
|
+
pts = simplifyPolyRDP(pts, {
|
|
36
42
|
width,
|
|
37
43
|
height,
|
|
38
44
|
quality: denoise,
|
|
39
45
|
manhattan,
|
|
40
46
|
absolute
|
|
41
47
|
})
|
|
48
|
+
}
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
// simplify polygon
|
|
54
|
+
if (simplifyRD != 1) {
|
|
55
|
+
pts = simplifyPolyRD(pts, { quality: simplifyRD+'px' })
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
|
|
59
|
+
if (simplifyRDP != 1) {
|
|
60
|
+
pts = simplifyPolyRDP(pts, { quality: simplifyRDP+'px' })
|
|
43
61
|
}
|
|
62
|
+
*/
|
|
63
|
+
|
|
44
64
|
|
|
45
65
|
// get topology of poly
|
|
46
|
-
let polyAnalyzed = !keepExtremes
|
|
66
|
+
let polyAnalyzed = !keepExtremes && !keepCorners ? pts : analyzePoly(pts, {
|
|
47
67
|
debug: false
|
|
48
68
|
//width,
|
|
49
69
|
//height
|
|
50
70
|
})
|
|
51
71
|
|
|
72
|
+
//console.log(polyAnalyzed, polyAnalyzed2);
|
|
73
|
+
//return
|
|
52
74
|
|
|
53
75
|
// split into segment chunks
|
|
54
|
-
let chunks = !keepExtremes
|
|
76
|
+
let chunks = !keepExtremes && !keepCorners ? [polyAnalyzed] : getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections });
|
|
55
77
|
|
|
56
78
|
|
|
57
79
|
// Schneider curve fit
|
|
58
80
|
let threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5
|
|
59
81
|
|
|
60
|
-
threshold=2
|
|
82
|
+
//threshold = 2
|
|
61
83
|
|
|
62
84
|
let polyPath = simplifyPolyChunks(chunks, {
|
|
63
85
|
closed,
|
|
64
|
-
tolerance: threshold
|
|
86
|
+
tolerance: threshold,
|
|
87
|
+
keepCorners
|
|
65
88
|
});
|
|
66
89
|
|
|
67
90
|
return polyPath;
|
|
@@ -75,6 +98,7 @@ export function simplifyPolygonToPathData(pts, {
|
|
|
75
98
|
|
|
76
99
|
export function simplifyPolyChunks(chunks = [], {
|
|
77
100
|
closed = true,
|
|
101
|
+
keepCorners = true,
|
|
78
102
|
tolerance = 1,
|
|
79
103
|
} = {}) {
|
|
80
104
|
|
|
@@ -100,12 +124,14 @@ export function simplifyPolyChunks(chunks = [], {
|
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
// nothing to simplify
|
|
103
|
-
if (chunklen < 2 || (chunklen === 2 && chunk[1].isExtreme)
|
|
127
|
+
if (chunklen < 2 || (chunklen === 2 && chunk[1].isExtreme)) {
|
|
104
128
|
pLast = chunk[chunk.length - 1]
|
|
105
129
|
segments = chunk.map(com => { return { type: 'L', values: [com.x, com.y] } })
|
|
106
130
|
|
|
107
131
|
} else {
|
|
108
|
-
segments =
|
|
132
|
+
segments = fitCurveSchneider(chunk, {
|
|
133
|
+
maxError: tolerance, keepCorners
|
|
134
|
+
})
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
// remove first segment to connect to last segment
|
|
@@ -115,11 +141,12 @@ export function simplifyPolyChunks(chunks = [], {
|
|
|
115
141
|
|
|
116
142
|
|
|
117
143
|
if (closed) pathData.push({ type: 'Z', values: [] })
|
|
118
|
-
|
|
119
|
-
//console.log('!!!pathData', pathData);
|
|
144
|
+
//console.log('!!!pathData from poly', pathData);
|
|
120
145
|
|
|
121
146
|
// refine extremes
|
|
122
|
-
|
|
147
|
+
let refineExtremes = false;
|
|
148
|
+
//refineExtremes=true;
|
|
149
|
+
if (refineExtremes) refineAdjacentExtremes(pathData)
|
|
123
150
|
return pathData
|
|
124
151
|
|
|
125
152
|
}
|
|
@@ -160,13 +187,13 @@ export function refineAdjacentExtremes(pathData) {
|
|
|
160
187
|
let vertical2 = dx2 < dist1 && dy2 > dist1
|
|
161
188
|
|
|
162
189
|
|
|
163
|
-
let distCpO=0, distCpN=0, t=1;
|
|
190
|
+
let distCpO = 0, distCpN = 0, t = 1;
|
|
164
191
|
let cp2N, cp1N;
|
|
165
192
|
|
|
166
193
|
if (horizontal1 || vertical1) {
|
|
167
194
|
|
|
168
195
|
// 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]})
|
|
196
|
+
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
197
|
|
|
171
198
|
/*
|
|
172
199
|
// adjust length
|
|
@@ -182,14 +209,14 @@ export function refineAdjacentExtremes(pathData) {
|
|
|
182
209
|
com.values[3] = cp2N.y
|
|
183
210
|
|
|
184
211
|
}
|
|
185
|
-
|
|
212
|
+
|
|
186
213
|
|
|
187
214
|
if (horizontal2 || vertical2) {
|
|
188
215
|
// adjust cp1 to horizontal
|
|
189
216
|
|
|
190
|
-
cp1N = horizontal2 ?
|
|
191
|
-
|
|
192
|
-
|
|
217
|
+
cp1N = horizontal2 ?
|
|
218
|
+
{ x: comN.values[0], y: p.y } :
|
|
219
|
+
(vertical2 ? { x: p.x, y: comN.values[1] } : { x: comN.values[0], y: comN.values[1] })
|
|
193
220
|
|
|
194
221
|
/*
|
|
195
222
|
// adjust length
|
package/src/svgii/rounding.js
CHANGED
|
@@ -7,6 +7,42 @@
|
|
|
7
7
|
import { getDistAv, getDistManhattan } from "./geometry";
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
export function detectAccuracyPoly(pts) {
|
|
12
|
+
|
|
13
|
+
let minDim = Infinity
|
|
14
|
+
let dims = [];
|
|
15
|
+
//console.log(pathData);
|
|
16
|
+
|
|
17
|
+
// add average distances
|
|
18
|
+
for (let i = 1, len = pts.length; i < len; i++) {
|
|
19
|
+
let pt = pts[i];
|
|
20
|
+
let { p0 = null, p = null, dimA = 0 } = pt;
|
|
21
|
+
|
|
22
|
+
// use existing averave dimension value or calculate
|
|
23
|
+
if ( p && p0) {
|
|
24
|
+
dimA = dimA ? dimA : getDistManhattan(p0, p);
|
|
25
|
+
|
|
26
|
+
if (dimA) dims.push(dimA);
|
|
27
|
+
if (dimA && dimA < minDim) minDim = dimA;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let dim_min = dims.sort()
|
|
32
|
+
let sliceIdx = Math.ceil(dim_min.length / 8);
|
|
33
|
+
dim_min = dim_min.slice(0, sliceIdx);
|
|
34
|
+
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
35
|
+
|
|
36
|
+
let threshold = 75
|
|
37
|
+
let decimalsAuto = minVal > threshold * 1.5 ? 0 : Math.floor(threshold / minVal).toString().length
|
|
38
|
+
// clamp
|
|
39
|
+
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
10
46
|
export function detectAccuracy(pathData) {
|
|
11
47
|
|
|
12
48
|
let minDim = Infinity
|
package/src/svgii/svg_cleanup.js
CHANGED
|
@@ -113,6 +113,16 @@ export function cleanUpSVG(svgMarkup, {
|
|
|
113
113
|
//console.log('!!!svgMarkup', svgMarkup);
|
|
114
114
|
|
|
115
115
|
|
|
116
|
+
// remove empty defs
|
|
117
|
+
let defs = svg.querySelectorAll('defs')
|
|
118
|
+
defs.forEach(def=>{
|
|
119
|
+
let children=[...def.children]
|
|
120
|
+
let attributes = [...def.attributes]
|
|
121
|
+
if(!attributes.length && !children.length){
|
|
122
|
+
def.remove()
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
116
126
|
if (returnDom) return svg
|
|
117
127
|
let markup = stringifySVG(svg)
|
|
118
128
|
|