svg-path-simplify 0.3.0 → 0.3.5
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/.github/ISSUE_TEMPLATE/bug_report.yml +28 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +35 -0
- package/README.md +3 -1
- package/dist/svg-path-simplify.esm.js +2804 -2761
- package/dist/svg-path-simplify.esm.min.js +5 -7
- package/dist/svg-path-simplify.js +2804 -2761
- 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 +546 -203
- package/package.json +1 -1
- package/site.webmanifest +21 -0
- package/src/detect_input.js +30 -3
- package/src/pathSimplify-main.js +134 -67
- 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/svg_flatten_transforms.js +43 -0
- 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-styles-getTransforms.js +43 -5
- package/src/svgii/svg-styles-to-attributes.js +81 -3
- package/src/svgii/svg_cleanup.js +21 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { rad2Deg } from "../constants";
|
|
2
|
-
import {
|
|
3
|
-
import { simplifyRD } from "../simplify_poly_radial_distance";
|
|
2
|
+
//import { simplifyPolyRDP } from "../simplify_poly_RDP";
|
|
3
|
+
//import { simplifyRD } from "../simplify_poly_radial_distance";
|
|
4
4
|
import { checkLineIntersection, getAngle, getDeltaAngle, getDeltaAngle2, getDistManhattan, getPointOnEllipse, getSquareDistance, pointAtT, reducePoints, rotatePoint } from "./geometry";
|
|
5
5
|
import { getPolygonArea } from "./geometry_area";
|
|
6
6
|
import { getPolyBBox } from "./geometry_bbox";
|
|
@@ -9,6 +9,7 @@ import { pathDataToD } from "./pathData_stringify";
|
|
|
9
9
|
import { renderPath, renderPoint, renderPoly } from "./visualize";
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
|
|
12
13
|
export function analyzePoly(pts, {
|
|
13
14
|
x = 0,
|
|
14
15
|
y = 0,
|
|
@@ -18,9 +19,6 @@ export function analyzePoly(pts, {
|
|
|
18
19
|
} = {}) {
|
|
19
20
|
|
|
20
21
|
let l = pts.length;
|
|
21
|
-
//let polyArea = getPolygonArea(pts, true)
|
|
22
|
-
|
|
23
|
-
//let bb0 = {x:0, y:0, width:0, height:0}
|
|
24
22
|
|
|
25
23
|
// bounding box of this sub poly
|
|
26
24
|
let bb0 = getPolyBBox(pts);
|
|
@@ -35,7 +33,7 @@ export function analyzePoly(pts, {
|
|
|
35
33
|
|
|
36
34
|
//console.log(thresh);
|
|
37
35
|
|
|
38
|
-
// get areas
|
|
36
|
+
// get areas an distances
|
|
39
37
|
for (let i = 0; i < l; i++) {
|
|
40
38
|
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
41
39
|
let p1 = pts[i];
|
|
@@ -44,12 +42,11 @@ export function analyzePoly(pts, {
|
|
|
44
42
|
let area = getPolygonArea([p0, p1, p2], false);
|
|
45
43
|
p1.area = area;
|
|
46
44
|
p1.dist = getDistManhattan(p0, p1)
|
|
45
|
+
p1.idx = i
|
|
47
46
|
//pts[i] = p1
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
// pts= pts.reverse();
|
|
51
49
|
|
|
52
|
-
let remove = []
|
|
53
50
|
|
|
54
51
|
for (let i = 0; i < l; i++) {
|
|
55
52
|
let p02 = i > 1 ? pts[i - 2] : pts[l - 1];
|
|
@@ -58,7 +55,6 @@ export function analyzePoly(pts, {
|
|
|
58
55
|
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
59
56
|
|
|
60
57
|
|
|
61
|
-
let bb = getPolyBBox([p0, p2]);
|
|
62
58
|
let max = getSquareDistance(p0, p2) * 0.01
|
|
63
59
|
|
|
64
60
|
let area0 = Math.abs(p0.area)
|
|
@@ -72,157 +68,97 @@ export function analyzePoly(pts, {
|
|
|
72
68
|
|
|
73
69
|
|
|
74
70
|
//console.log(bb);
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
// is local or absolute extreme
|
|
78
|
-
let extremeLocal = (p1.x <= left || p1.x >= right || p1.y <= top || p1.y >= bottom)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
let isExtreme = (
|
|
82
|
-
// extremeLocal ||
|
|
83
|
-
(p1.x === bb0.left || p1.x === bb0.right || p1.y === bb0.top || p1.y === bb0.bottom)
|
|
84
|
-
);
|
|
85
|
-
|
|
71
|
+
//let extremeLocal = (p1.x < left || p1.x > right || p1.y < top || p1.y > bottom)
|
|
86
72
|
|
|
87
73
|
let dist = getDistManhattan(p1, p0)
|
|
88
74
|
let isNear = dist < thresh * 5
|
|
89
75
|
|
|
90
76
|
|
|
91
77
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
78
|
+
/**
|
|
79
|
+
* check extremes
|
|
80
|
+
*/
|
|
95
81
|
|
|
82
|
+
let isExtreme = false;
|
|
96
83
|
|
|
97
|
-
|
|
98
|
-
if ((
|
|
84
|
+
// 1. total extreme
|
|
85
|
+
if ((p1.x === bb0.left || p1.x === bb0.right || p1.y === bb0.top || p1.y === bb0.bottom)) {
|
|
99
86
|
isExtreme = true
|
|
100
87
|
}
|
|
101
88
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0)
|
|
106
|
-
let isDirChange = signChange && !flat
|
|
107
|
-
isCorner = isDirChange
|
|
108
|
-
|
|
109
|
-
// isDirChange = signChange && signChange3
|
|
89
|
+
// 1.2 horizontal or vertical
|
|
90
|
+
let isHorizontal = p1.y === p0.y && p1.x !== p0.x;
|
|
91
|
+
let isVertical = p1.x === p0.x && p1.y !== p0.y
|
|
110
92
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
//p1.isDirChange=false
|
|
115
|
-
isExtreme = true;
|
|
93
|
+
if ((isHorizontal || isVertical)) {
|
|
94
|
+
p0.isExtreme = true
|
|
95
|
+
isExtreme = true
|
|
116
96
|
}
|
|
117
97
|
|
|
118
98
|
|
|
99
|
+
// 1.3 is local or absolute extreme
|
|
100
|
+
let bb = getPolyBBox([p0, p2]); // local bb
|
|
101
|
+
let { left, right, top, bottom } = bb;
|
|
119
102
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
p0.isExtreme = false
|
|
126
|
-
isExtreme = false
|
|
103
|
+
//let extremeLocal = (p1.x <= left || p1.x >= right || p1.y <= top || p1.y >= bottom)
|
|
104
|
+
let extremeLocal = (p1.x < left || p1.x > right || p1.y < top || p1.y > bottom)
|
|
105
|
+
if (!isExtreme && extremeLocal) {
|
|
106
|
+
isExtreme = true
|
|
107
|
+
//renderPoint(markers, p1, 'blue', '2%', '0.5')
|
|
127
108
|
}
|
|
128
109
|
|
|
129
110
|
|
|
130
|
-
/*
|
|
131
|
-
if (isExtreme && p0.isExtreme && signChange && isNear) {
|
|
132
|
-
//console.log(p0);
|
|
133
|
-
//renderPoint(markers, p1, 'red', '2%', '1')
|
|
134
|
-
isCorner = true
|
|
135
|
-
}
|
|
136
|
-
*/
|
|
137
111
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
//p0.isExtreme = true
|
|
144
|
-
p1.isExtreme = false
|
|
145
|
-
isExtreme=false
|
|
146
|
-
isCloseExtreme = true
|
|
147
|
-
remove.push(i)
|
|
148
|
-
}
|
|
112
|
+
/**
|
|
113
|
+
* 2. sign changes
|
|
114
|
+
*/
|
|
115
|
+
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0)
|
|
116
|
+
let isDirChange = signChange && !flat && !p0.isDirChange
|
|
149
117
|
|
|
150
|
-
}
|
|
151
|
-
*/
|
|
152
118
|
|
|
153
119
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
p1.isDirChange = false
|
|
158
|
-
}
|
|
120
|
+
/**
|
|
121
|
+
* 3. corners
|
|
122
|
+
*/
|
|
159
123
|
|
|
124
|
+
//isDirChange &&
|
|
125
|
+
if (isExtreme) {
|
|
160
126
|
|
|
127
|
+
let delta = getDeltaAngle(p1, p2, p0)
|
|
128
|
+
let { deltaAngleDeg } = delta
|
|
129
|
+
deltaAngleDeg = Math.abs(deltaAngleDeg)
|
|
161
130
|
|
|
131
|
+
let isCornerDelta = deltaAngleDeg > 10 && deltaAngleDeg < 160
|
|
132
|
+
if (isCornerDelta) {
|
|
133
|
+
//console.log(deltaAngleDeg);
|
|
134
|
+
isCorner = true;
|
|
135
|
+
}
|
|
162
136
|
|
|
163
|
-
/*
|
|
164
|
-
let areaChange = area2*1.5<area1 && area0*1.5<area1;
|
|
165
|
-
if(areaChange){
|
|
166
|
-
//renderPoint(markers, p1, 'red', '1.75%', '1')
|
|
167
137
|
}
|
|
168
|
-
*/
|
|
169
|
-
|
|
170
138
|
|
|
171
139
|
|
|
172
140
|
/*
|
|
173
|
-
let
|
|
174
|
-
|
|
175
|
-
console.log('thresh2', p1.dist, thresh2, thresh);
|
|
176
|
-
|
|
177
|
-
if( isLong && isDirChange){
|
|
178
|
-
renderPoint(markers, p0, 'red', '1.75%', '1')
|
|
179
|
-
renderPoint(markers, p1, 'green', '1.75%', '1')
|
|
180
|
-
}
|
|
181
|
-
*/
|
|
182
|
-
|
|
183
|
-
// corner after extreme
|
|
184
|
-
if (p0.isExtreme && area1 > area2 && !isDirChange && !p1.isHorizontal) {
|
|
185
|
-
}
|
|
186
|
-
|
|
141
|
+
let debug = true
|
|
142
|
+
if (debug) {
|
|
187
143
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
let delta = getDeltaAngle(p1, p0, p2)
|
|
192
|
-
delta = Math.abs(delta.deltaAngleDeg)
|
|
193
|
-
if (delta > 160) {
|
|
194
|
-
//renderPoint(markers, p1, 'blue', '2.75%', '1')
|
|
195
|
-
isCorner = false;
|
|
196
|
-
} else {
|
|
197
|
-
isCorner = true;
|
|
144
|
+
if ((isHorizontal || isVertical)) {
|
|
145
|
+
renderPoint(markers, p1, 'blue', '2%', '0.5')
|
|
146
|
+
renderPoint(markers, p0, 'blue', '2%', '0.5')
|
|
198
147
|
}
|
|
199
|
-
console.log(delta);
|
|
200
|
-
}
|
|
201
|
-
*/
|
|
202
|
-
|
|
203
148
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
let delta = getDeltaAngle(p1, p2, p0)
|
|
207
|
-
let { deltaAngleDeg } = delta
|
|
208
|
-
deltaAngleDeg = Math.abs(deltaAngleDeg)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// not a corner
|
|
212
|
-
if (deltaAngleDeg < 3 || deltaAngleDeg > 160) {
|
|
213
|
-
//renderPoint(markers, p1, 'blue', '2.75%', '1')
|
|
214
|
-
isCorner = false;
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
//console.log(deltaAngleDeg, delta);
|
|
218
|
-
isCorner = true;
|
|
149
|
+
if (isExtreme) {
|
|
150
|
+
renderPoint(markers, p1, 'cyan', '1.5%', '0.5')
|
|
219
151
|
}
|
|
220
|
-
}
|
|
221
152
|
|
|
222
|
-
|
|
223
|
-
|
|
153
|
+
if (isDirChange) {
|
|
154
|
+
renderPoint(markers, p1, 'orange', '0.75%', '0.5')
|
|
155
|
+
}
|
|
224
156
|
|
|
157
|
+
if (isCorner) {
|
|
158
|
+
renderPoint(markers, p1, 'magenta', '2.75%', '0.5')
|
|
159
|
+
}
|
|
225
160
|
}
|
|
161
|
+
*/
|
|
226
162
|
|
|
227
163
|
|
|
228
164
|
p1.isCorner = isCorner;
|
|
@@ -231,165 +167,85 @@ export function analyzePoly(pts, {
|
|
|
231
167
|
p1.isVertical = isVertical;
|
|
232
168
|
p1.isDirChange = isDirChange;
|
|
233
169
|
|
|
234
|
-
|
|
170
|
+
//renderPoint(markers, p1, 'red', '2%', '0.5')
|
|
235
171
|
|
|
236
172
|
|
|
173
|
+
}
|
|
174
|
+
|
|
237
175
|
|
|
238
176
|
// filter adjacent extremes
|
|
239
177
|
let pts1 = []
|
|
240
178
|
let exclude = []
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
let
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
179
|
+
let filterExtremes = true;
|
|
180
|
+
|
|
181
|
+
if (filterExtremes) {
|
|
182
|
+
for (let i = 0; i < pts.length; i++) {
|
|
183
|
+
let p = pts[i]
|
|
184
|
+
let p1 = pts[i + 1] || null
|
|
185
|
+
let p2 = pts[i + 2] || null
|
|
186
|
+
|
|
187
|
+
let extremes = []
|
|
188
|
+
|
|
189
|
+
if (p1 && p1.isExtreme && p.isExtreme && !p.isCorner) {
|
|
190
|
+
let has2nd = p1.dist < thresh * 2 && !p1.isCorner
|
|
191
|
+
let has3rd = p2 && p2.isExtreme && p2.dist < thresh * 2 && !p2.isCorner
|
|
192
|
+
let lastExt = p1
|
|
193
|
+
|
|
194
|
+
if (has2nd && !has3rd) {
|
|
195
|
+
extremes.push(p, p1)
|
|
196
|
+
//renderPoint(markers, p1, 'magenta', '1%', '0.5')
|
|
197
|
+
} else if (has3rd) {
|
|
198
|
+
extremes.push(p, p1, p2)
|
|
199
|
+
/*
|
|
200
|
+
renderPoint(markers, p, 'green', '1%', '0.5')
|
|
201
|
+
renderPoint(markers, p1, 'red', '1%', '0.5')
|
|
202
|
+
renderPoint(markers, p2, 'blue', '1%', '0.5')
|
|
203
|
+
*/
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (extremes.length) {
|
|
207
|
+
// average extreme
|
|
208
|
+
//console.log(extremes);
|
|
209
|
+
let x = extremes.reduce((a, b) => a + b.x, 0) / extremes.length
|
|
210
|
+
let y = extremes.reduce((a, b) => a + b.y, 0) / extremes.length
|
|
211
|
+
|
|
212
|
+
///extremes.length
|
|
213
|
+
p.x = x
|
|
214
|
+
p.y = y
|
|
215
|
+
//console.log(x);
|
|
216
|
+
i += extremes.length - 1
|
|
217
|
+
}
|
|
265
218
|
}
|
|
266
219
|
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
//console.log(extremes);
|
|
270
|
-
let x = extremes.reduce((a, b) => a + b.x, 0) / extremes.length
|
|
271
|
-
let y = extremes.reduce((a, b) => a + b.y, 0) / extremes.length
|
|
272
|
-
|
|
273
|
-
///extremes.length
|
|
274
|
-
p.x = x
|
|
275
|
-
p.y = y
|
|
276
|
-
//console.log(x);
|
|
277
|
-
i += extremes.length - 1
|
|
220
|
+
if (p.isExtreme || p.isCorner || p.isDirChange) {
|
|
221
|
+
exclude.push(pts1.length)
|
|
278
222
|
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (p.isExtreme || p.isCorner || p.isDirChange) {
|
|
282
|
-
exclude.push(pts1.length)
|
|
283
|
-
}
|
|
284
223
|
|
|
285
224
|
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// remove last nearby extreme
|
|
290
|
-
let l2 = pts1.length;
|
|
291
|
-
let p0 = pts1[0]
|
|
292
|
-
let pL = pts1[l2 - 1]
|
|
293
|
-
let near0 = getDistManhattan(p0, pL) < thresh * 2
|
|
294
|
-
if (p0.isExtreme && pL.isExtreme && near0) {
|
|
295
|
-
pL.x = p0.x
|
|
296
|
-
pL.y = p0.y
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
//console.log(exclude);
|
|
301
|
-
//pts1 = simplifyRD(pts1, {quality:0.7, exclude})
|
|
302
|
-
|
|
303
|
-
// simplify via RDP - exclude extremes
|
|
304
|
-
//pts1 = simplifyRDP(pts1, {quality:0.85, exclude})
|
|
305
|
-
|
|
306
|
-
/*
|
|
307
|
-
pts1.forEach(pt=>{
|
|
308
|
-
renderPoint(markers, pt, 'green', '1%', '0.5')
|
|
309
|
-
})
|
|
310
|
-
*/
|
|
311
|
-
|
|
312
|
-
pts = pts1
|
|
313
|
-
//console.log(pts1);
|
|
314
|
-
|
|
315
|
-
// test render
|
|
316
|
-
//renderPolyTopology(pts)
|
|
317
|
-
//return pathDataFromPoly(pts)
|
|
318
|
-
|
|
319
|
-
return pts
|
|
320
|
-
}
|
|
321
|
-
|
|
225
|
+
pts1.push(p)
|
|
322
226
|
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
let chunks = [];
|
|
333
|
-
let chunk = [pts[0]];
|
|
334
|
-
|
|
335
|
-
let l = pts.length
|
|
336
|
-
|
|
337
|
-
// render
|
|
338
|
-
for (let i = 1; i < l; i++) {
|
|
339
|
-
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
340
|
-
let p1 = pts[i];
|
|
341
|
-
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
342
|
-
|
|
343
|
-
chunk.push(p1)
|
|
344
|
-
|
|
345
|
-
// start new chunk
|
|
346
|
-
if (i > 0 &&
|
|
347
|
-
(keepExtremes && p2.isExtreme || keepCorners && p2.isCorner)
|
|
348
|
-
) {
|
|
349
|
-
chunks.push(chunk)
|
|
350
|
-
chunk = []
|
|
227
|
+
// remove last nearby extreme
|
|
228
|
+
let l2 = pts1.length;
|
|
229
|
+
let p0 = pts1[0]
|
|
230
|
+
let pL = pts1[l2 - 1]
|
|
231
|
+
let near0 = getDistManhattan(p0, pL) < thresh * 2
|
|
232
|
+
if (p0.isExtreme && pL.isExtreme && near0) {
|
|
233
|
+
pL.x = p0.x
|
|
234
|
+
pL.y = p0.y
|
|
235
|
+
}
|
|
351
236
|
}
|
|
352
237
|
|
|
353
|
-
|
|
238
|
+
pts = pts1
|
|
354
239
|
}
|
|
355
240
|
|
|
356
|
-
// chunk is empty - extremes or corners
|
|
357
|
-
if(!chunks.length && pts.length>1){
|
|
358
|
-
chunks = [pts]
|
|
359
|
-
}
|
|
360
241
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
// test render
|
|
364
|
-
//renderchunks(chunks)
|
|
365
|
-
|
|
366
|
-
return chunks;
|
|
242
|
+
return pts
|
|
367
243
|
}
|
|
368
244
|
|
|
369
|
-
function renderchunks(chunks) {
|
|
370
|
-
|
|
371
|
-
//console.log('renderchunks', chunks);
|
|
372
|
-
|
|
373
|
-
chunks.forEach((chunk, i) => {
|
|
374
|
-
|
|
375
|
-
let stroke = i % 2 === 0 ? 'orange' : 'blue';
|
|
376
|
-
let pathData = [{ type: 'M', values: [chunk[0].x, chunk[0].y] }]
|
|
377
|
-
let d = `M`
|
|
378
|
-
|
|
379
|
-
chunk.forEach(pt => {
|
|
380
245
|
|
|
381
|
-
pathData.push({ type: 'L', values: [pt.x, pt.y] })
|
|
382
|
-
d += ` ${[pt.x, pt.y].join(' ')}`
|
|
383
246
|
|
|
384
|
-
})
|
|
385
247
|
|
|
386
|
-
d = pathDataToD(pathData)
|
|
387
|
-
//console.log(d);
|
|
388
248
|
|
|
389
|
-
renderPath(markers, d, stroke, '2%', '0.5')
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
}
|
|
393
249
|
|
|
394
250
|
// just for visualization
|
|
395
251
|
function renderPolyTopology(pts) {
|
|
@@ -423,7 +279,6 @@ function renderPolyTopology(pts) {
|
|
|
423
279
|
}
|
|
424
280
|
|
|
425
281
|
|
|
426
|
-
|
|
427
282
|
if (p1.isCorner) {
|
|
428
283
|
renderPoint(markers, p1, 'magenta', '1%', '1')
|
|
429
284
|
}
|
|
@@ -433,182 +288,6 @@ function renderPolyTopology(pts) {
|
|
|
433
288
|
|
|
434
289
|
}
|
|
435
290
|
|
|
436
|
-
export function analyzePoly2(pts, debug = false) {
|
|
437
|
-
|
|
438
|
-
let l = pts.length;
|
|
439
|
-
let polyArea = getPolygonArea(pts, true)
|
|
440
|
-
let bb0 = getPolyBBox(pts)
|
|
441
|
-
//console.log(polyArea);
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
// get areas
|
|
445
|
-
for (let i = 0; i < l; i++) {
|
|
446
|
-
let pt0 = i > 0 ? pts[i - 1] : null;
|
|
447
|
-
let pt1 = pts[i];
|
|
448
|
-
let pt2 = i < l - 1 ? pts[i + 1] : null;
|
|
449
|
-
|
|
450
|
-
if (!pt0 || !pt2) continue
|
|
451
|
-
|
|
452
|
-
let area = getPolygonArea([pt0, pt1, pt2], false);
|
|
453
|
-
let ang1 = getAngle(pt1, pt0, true);
|
|
454
|
-
let ang2 = getAngle(pt1, pt2, true);
|
|
455
|
-
let delta = Math.abs(ang1 - ang2);
|
|
456
|
-
let deltaDeg = delta * 180 / Math.PI;
|
|
457
|
-
|
|
458
|
-
//console.log(bb0);
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* get local extremes
|
|
462
|
-
* my coincide with corners or
|
|
463
|
-
* direction changes
|
|
464
|
-
*/
|
|
465
|
-
let { left, right, top, bottom } = getPolyBBox([pt0, pt2]);
|
|
466
|
-
let isExtreme = ((pt1.x < left || pt1.x > right || pt1.y < top || pt1.y > bottom) ||
|
|
467
|
-
(pt1.x === bb0.left || pt1.x === bb0.right || pt1.y === bb0.top || pt1.y === bb0.bottom)
|
|
468
|
-
);
|
|
469
|
-
//let isHorizontal = !pt0.isHorizontal ? pt1.y === pt0.y && pt1.x!== pt0.x : false;
|
|
470
|
-
let isHorizontal = pt1.y === pt0.y && pt1.x !== pt0.x;
|
|
471
|
-
let isVertical = pt1.x === pt0.x && pt1.y !== pt0.y
|
|
472
|
-
|
|
473
|
-
if (pt0.isHorizontal) {
|
|
474
|
-
//isHorizontal=false
|
|
475
|
-
//renderPoint(markers, pt0)
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* check corners by
|
|
481
|
-
* adjacent angle differences
|
|
482
|
-
*/
|
|
483
|
-
let isCorner = deltaDeg < 120 || deltaDeg > 270;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* get direction changes
|
|
488
|
-
* e.g the spine of a "S" shape
|
|
489
|
-
*/
|
|
490
|
-
let directionChange = pt0.isCorner === false && ((pt0.area < 0 && area > 0) || (pt0.area > 0 && area < 0));
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (pt0.isExtreme &&
|
|
495
|
-
(pt1.y === pt0.y || pt1.x === pt0.x)
|
|
496
|
-
) {
|
|
497
|
-
isExtreme = true;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if (directionChange && isExtreme) {
|
|
502
|
-
isCorner = true;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// if segment is too large relative to total area - don't interpret as corner
|
|
506
|
-
let areaRat = Math.abs(area / polyArea);
|
|
507
|
-
|
|
508
|
-
if (areaRat > 0.2) {
|
|
509
|
-
isCorner = false;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
/**
|
|
514
|
-
* visualize significant points for
|
|
515
|
-
* debugging
|
|
516
|
-
*/
|
|
517
|
-
|
|
518
|
-
/*
|
|
519
|
-
*/
|
|
520
|
-
|
|
521
|
-
if (debug) {
|
|
522
|
-
|
|
523
|
-
if ((isExtreme && isCorner)) {
|
|
524
|
-
//isExtreme = false;
|
|
525
|
-
directionChange = false;
|
|
526
|
-
//isCorner = false;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
if (isHorizontal) {
|
|
530
|
-
renderPoint(markers, pt0, 'blue', '1%', '0.5');
|
|
531
|
-
renderPoint(markers, pt1, 'red', '1%', '0.5');
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (isVertical) {
|
|
536
|
-
//renderPoint(markers, pt1, 'blue', '2%', '0.5');
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
if (isExtreme) {
|
|
541
|
-
renderPoint(markers, pt1, 'cyan', '2%');
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
if (isCorner) {
|
|
545
|
-
renderPoint(markers, pt1, 'purple', '0.5%');
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
if (directionChange) {
|
|
549
|
-
//renderPoint(markers, pt1, 'orange', '1.5%', '0.5');
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* save point analysis properties
|
|
557
|
-
* to point objects
|
|
558
|
-
*/
|
|
559
|
-
pt1.isHorizontal = isHorizontal;
|
|
560
|
-
pt1.isVertical = isVertical;
|
|
561
|
-
pt1.isExtreme = isExtreme;
|
|
562
|
-
pt1.isCorner = isCorner;
|
|
563
|
-
pt1.directionChange = directionChange;
|
|
564
|
-
|
|
565
|
-
pt1.area = area;
|
|
566
|
-
pt1.delta = delta;
|
|
567
|
-
pt1.deltaDeg = deltaDeg;
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
//getControlPoints(pts)
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
return pts
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
export function getPathDataChunks(pathData) {
|
|
586
|
-
|
|
587
|
-
let chunks = [[]];
|
|
588
|
-
let lastType = 'M'
|
|
589
|
-
let ind = 0;
|
|
590
|
-
let wasExtreme, wasCorner, wasDirectionchange;
|
|
591
|
-
|
|
592
|
-
pathData.forEach(com => {
|
|
593
|
-
|
|
594
|
-
let { isCorner, isExtreme, directionChange, type } = com;
|
|
595
|
-
|
|
596
|
-
if (type !== lastType || wasExtreme || wasCorner || directionChange || wasDirectionchange) {
|
|
597
|
-
chunks.push([])
|
|
598
|
-
ind++
|
|
599
|
-
}
|
|
600
|
-
chunks[ind].push(com)
|
|
601
|
-
|
|
602
|
-
wasExtreme = isExtreme
|
|
603
|
-
wasCorner = isCorner
|
|
604
|
-
wasDirectionchange = directionChange;
|
|
605
|
-
lastType = type
|
|
606
|
-
})
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return chunks;
|
|
610
|
-
|
|
611
|
-
}
|
|
612
291
|
|
|
613
292
|
|
|
614
293
|
|