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.
@@ -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 { simplifyRDP } from "../simplify_poly_RDP";
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 = simplifyRDP(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&&!keepCorners ? pts : analyzePoly(pts, {
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&&!keepCorners ? [polyAnalyzed] : getPolyChunks(polyAnalyzed, {keepCorners,keepExtremes, keepInflections});
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 = fitCurveN(chunk, tolerance)
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
- refineAdjacentExtremes(pathData)
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
- {x:comN.values[0], y: p.y} :
192
- (vertical2 ? {x:p.x, y:comN.values[1]} : {x:comN.values[0], y:comN.values[1]})
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
@@ -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
@@ -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