svg-path-simplify 0.4.5 → 0.4.7
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/CHANGELOG.md +14 -0
- package/README.md +24 -14
- package/dist/svg-path-simplify.esm.js +1620 -1216
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1620 -1215
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +298 -665
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/dist/svg-path-simplify.poly.cjs +8 -9
- package/drawing.svg +62 -0
- package/index.html +11 -5
- package/package.json +1 -1
- package/src/index.js +3 -1
- package/src/pathData_get_intersections.js +777 -0
- package/src/pathData_offset.js +201 -0
- package/src/pathData_simplify_cubic.js +14 -34
- package/src/pathData_simplify_cubic_extrapolate.js +147 -8
- package/src/pathData_simplify_cubicsToArcs.js +65 -21
- package/src/pathSimplify-main.js +259 -60
- package/src/pathSimplify-presets.js +1 -0
- package/src/poly-fit-curve-schneider.js +171 -132
- package/src/poly-fit-curve-schneider_check_bulge.js +131 -0
- package/src/poly-offset.js +133 -0
- package/src/simplify_poly_RC.js +29 -7
- package/src/svgii/geometry.js +130 -27
- package/src/svgii/geometry_bbox.js +1 -1
- package/src/svgii/pathData_analyze.js +6 -28
- package/src/svgii/pathData_convert.js +70 -15
- package/src/svgii/pathData_remove_collinear.js +32 -36
- package/src/svgii/pathData_simplify_refineExtremes.js +1 -3
- package/src/svgii/pathData_stringify.js +132 -6
- package/src/svgii/pathData_toPolygon.js +33 -23
- package/src/svgii/poly_analyze.js +272 -125
- package/src/svgii/poly_analyze_cleanup.js +311 -0
- package/src/svgii/poly_analyze_getTangents.js +125 -0
- package/src/svgii/poly_analyze_get_chunks.js +3 -1
- package/src/svgii/poly_normalize.js +12 -0
- package/src/svgii/poly_to_pathdata.js +477 -8
- package/src/svgii/rounding.js +29 -39
- package/src/svgii/svg_cleanup.js +42 -54
- package/src/svgii/svg_cleanup_normalize_transforms.js +1 -1
- package/src/svgii/visualize.js +33 -2
|
@@ -5,7 +5,11 @@ import { checkLineIntersection, getAngle, getDeltaAngle, getDeltaAngle2, getDist
|
|
|
5
5
|
import { getPolygonArea } from "./geometry_area";
|
|
6
6
|
import { getPolyBBox } from "./geometry_bbox";
|
|
7
7
|
import { pathDataFromPoly } from "./pathData_fromPoly";
|
|
8
|
+
import { refineAdjacentExtremes } from "./pathData_simplify_refineExtremes";
|
|
8
9
|
import { pathDataToD } from "./pathData_stringify";
|
|
10
|
+
import { cleanupPolyKeypoints, refineAdjacentPolyExtremes } from "./poly_analyze_cleanup";
|
|
11
|
+
import { getTangents } from "./poly_analyze_getTangents";
|
|
12
|
+
import { roundPoly } from "./rounding";
|
|
9
13
|
import { renderPath, renderPoint, renderPoly } from "./visualize";
|
|
10
14
|
|
|
11
15
|
|
|
@@ -19,7 +23,7 @@ export function getPolyCentroid(pts) {
|
|
|
19
23
|
y += pt.y
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
let centroid = {x: x/l, y:y/l}
|
|
26
|
+
let centroid = { x: x / l, y: y / l }
|
|
23
27
|
return centroid
|
|
24
28
|
//console.log(centroid);
|
|
25
29
|
|
|
@@ -27,20 +31,20 @@ export function getPolyCentroid(pts) {
|
|
|
27
31
|
|
|
28
32
|
export function getPolyCentroidWeighted(points) {
|
|
29
33
|
if (!points || points.length === 0) return null;
|
|
30
|
-
|
|
34
|
+
|
|
31
35
|
let totalWeight = 0;
|
|
32
36
|
let sumX = 0;
|
|
33
37
|
let sumY = 0;
|
|
34
|
-
|
|
38
|
+
|
|
35
39
|
for (const point of points) {
|
|
36
40
|
let weight = point.weight || 1; // default weight = 1
|
|
37
41
|
sumX += point.x * weight;
|
|
38
42
|
sumY += point.y * weight;
|
|
39
43
|
totalWeight += weight;
|
|
40
44
|
}
|
|
41
|
-
|
|
45
|
+
|
|
42
46
|
if (totalWeight === 0) return null;
|
|
43
|
-
|
|
47
|
+
|
|
44
48
|
return {
|
|
45
49
|
x: sumX / totalWeight,
|
|
46
50
|
y: sumY / totalWeight
|
|
@@ -49,7 +53,7 @@ export function getPolyCentroidWeighted(points) {
|
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
|
|
52
|
-
export function detectRegularPolygon(pts, centroid={x:0, y:0}) {
|
|
56
|
+
export function detectRegularPolygon(pts, centroid = { x: 0, y: 0 }) {
|
|
53
57
|
let rSq = getSquareDistance(pts[0], centroid);
|
|
54
58
|
let isRegular = true;
|
|
55
59
|
|
|
@@ -57,8 +61,8 @@ export function detectRegularPolygon(pts, centroid={x:0, y:0}) {
|
|
|
57
61
|
let pt1 = pts[i];
|
|
58
62
|
let dist = getSquareDistance(pt1, centroid);
|
|
59
63
|
|
|
60
|
-
let diff = Math.abs(rSq-dist);
|
|
61
|
-
let diffRel = diff/rSq
|
|
64
|
+
let diff = Math.abs(rSq - dist);
|
|
65
|
+
let diffRel = diff / rSq
|
|
62
66
|
//console.log('diffRel', diffRel);
|
|
63
67
|
|
|
64
68
|
if (diffRel > 0.05) {
|
|
@@ -79,82 +83,237 @@ export function analyzePoly(pts, {
|
|
|
79
83
|
debug = false
|
|
80
84
|
} = {}) {
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// bounding box of this sub poly
|
|
85
|
-
let bb0 = getPolyBBox(pts);
|
|
86
|
+
//console.log(width, height);
|
|
86
87
|
|
|
88
|
+
let l = pts.length;
|
|
89
|
+
let left = x;
|
|
90
|
+
let top = y;
|
|
91
|
+
let right = x + width;
|
|
92
|
+
let bottom = y + height;
|
|
87
93
|
|
|
88
94
|
if (!width || !height) {
|
|
89
|
-
({ x, y, width, height } =
|
|
95
|
+
({ x, y, width, height, top, bottom, left, right } = getPolyBBox(pts));
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
// round
|
|
99
|
+
[x, y, width, height, top, bottom, left, right] = [x, y, width, height, top, bottom, left, right].map(val => +val.toFixed(8))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// bounding box of this sub poly
|
|
103
|
+
let bb0 = { x, y, top, left, width, height, right, bottom }
|
|
104
|
+
|
|
105
|
+
|
|
92
106
|
//console.log(polyArea);
|
|
93
|
-
let thresh = (width + height) * 0.01
|
|
107
|
+
let thresh = (width + height) * 0.01;
|
|
108
|
+
|
|
109
|
+
// threshold for horizontal or vertical detection
|
|
110
|
+
let thresh2 = thresh * 0.75
|
|
111
|
+
|
|
112
|
+
let dims = [];
|
|
94
113
|
|
|
95
114
|
//console.log(thresh);
|
|
96
115
|
|
|
97
|
-
//
|
|
116
|
+
//pts = roundPoly(pts, 3)
|
|
117
|
+
//console.log(pts);
|
|
118
|
+
|
|
119
|
+
/*
|
|
120
|
+
pts.forEach(pt=>{
|
|
121
|
+
renderPoint(markers, pt, 'red', '2.5%')
|
|
122
|
+
})
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 1st run:
|
|
127
|
+
* collect more details
|
|
128
|
+
* area for sign change detection
|
|
129
|
+
* deltas and distances
|
|
130
|
+
*/
|
|
98
131
|
for (let i = 0; i < l; i++) {
|
|
99
132
|
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
100
133
|
let p1 = pts[i];
|
|
101
134
|
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
102
135
|
|
|
103
136
|
let area = getPolygonArea([p0, p1, p2], false);
|
|
137
|
+
let dx = i > 0 ? +(p1.x - p0.x).toFixed(7) : 0
|
|
138
|
+
let dy = i > 0 ? +(p1.y - p0.y).toFixed(7) : 0
|
|
139
|
+
|
|
140
|
+
let dx2 = +(p2.x - p0.x).toFixed(7)
|
|
141
|
+
let dy2 = +(p2.y - p0.y).toFixed(7)
|
|
142
|
+
|
|
143
|
+
|
|
104
144
|
p1.area = area;
|
|
105
|
-
p1.dist = getDistManhattan(p0, p1)
|
|
145
|
+
p1.dist = i > 0 ? getDistManhattan(p0, p1) : 0;
|
|
146
|
+
// add dist for long/short segment detection
|
|
147
|
+
dims.push(p1.dist);
|
|
106
148
|
p1.idx = i
|
|
149
|
+
p1.dx = dx
|
|
150
|
+
p1.dy = dy
|
|
151
|
+
p1.dx2 = dx2
|
|
152
|
+
p1.dy2 = dy2
|
|
153
|
+
|
|
107
154
|
//pts[i] = p1
|
|
108
155
|
}
|
|
109
156
|
|
|
110
157
|
|
|
111
|
-
|
|
158
|
+
/**
|
|
159
|
+
* find average segment length
|
|
160
|
+
* for long/short segment detection
|
|
161
|
+
*/
|
|
162
|
+
dims = dims.filter(Boolean).sort((a, b) => a - b)
|
|
163
|
+
let lenD = dims.length;
|
|
164
|
+
let dimMin = dims[0];
|
|
165
|
+
let dimMax = dims[lenD - 1];
|
|
166
|
+
//let idxMid = Math.abs(dims.length*0.5);
|
|
167
|
+
let dimAv = dims.reduce((a, b) => a + b, 0) / lenD;
|
|
168
|
+
let dimShort = (dimMin + dimAv) * 0.5
|
|
169
|
+
let dimLong = dimAv * 2;
|
|
170
|
+
//console.log('dims', dims, 'dimAv', dimAv, 'dimMin', dimMin, 'dimMax', dimMax, 'dimShort', dimShort);
|
|
171
|
+
//console.log(pts);
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
// round to adjust for minor deviations
|
|
176
|
+
let idx_q = Math.ceil(lenD * 0.25);
|
|
177
|
+
let dim_mid = dims[Math.floor(lenD * 0.5)]
|
|
178
|
+
let dims_min = dims.slice(0, Math.floor(lenD * 0.25));
|
|
179
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
180
|
+
|
|
181
|
+
let threshold = 75
|
|
182
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length
|
|
183
|
+
|
|
184
|
+
// clamp
|
|
185
|
+
decimalsAuto = Math.min(Math.max(0, decimalsAuto), 8)
|
|
186
|
+
//console.log('decimalsAuto', decimalsAuto);
|
|
187
|
+
|
|
188
|
+
pts = roundPoly(pts, 2)
|
|
189
|
+
console.log(pts);
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* analyze topology:
|
|
195
|
+
* find significant commands:
|
|
196
|
+
* extremes, inflections etc.
|
|
197
|
+
*/
|
|
112
198
|
for (let i = 0; i < l; i++) {
|
|
113
|
-
let p02 = i > 1 ? pts[i - 2] : pts[l - 1];
|
|
199
|
+
//let p02 = i > 1 ? pts[i - 2] : pts[l - 1];
|
|
114
200
|
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
115
201
|
let p1 = pts[i];
|
|
116
202
|
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
117
203
|
|
|
118
|
-
|
|
119
|
-
let max = getSquareDistance(p0, p2) * 0.01
|
|
120
|
-
|
|
121
|
-
let area0 = Math.abs(p0.area)
|
|
204
|
+
// convert area to absolute for flatness checks
|
|
122
205
|
let area1 = Math.abs(p1.area)
|
|
123
|
-
let area2 = Math.abs(p2.area)
|
|
124
|
-
let isCloseExtreme = false
|
|
125
206
|
let isCorner = false;
|
|
207
|
+
let isSemiExtreme = false;
|
|
208
|
+
let isShort = false;
|
|
209
|
+
let isLong = false;
|
|
126
210
|
|
|
211
|
+
/**
|
|
212
|
+
* detect short or long
|
|
213
|
+
*/
|
|
214
|
+
if (p1.dist > dimLong) {
|
|
215
|
+
isLong = true;
|
|
216
|
+
}
|
|
127
217
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
//console.log(bb);
|
|
132
|
-
//let extremeLocal = (p1.x < left || p1.x > right || p1.y < top || p1.y > bottom)
|
|
218
|
+
if (p1.dist < dimShort) {
|
|
219
|
+
isShort = true;
|
|
220
|
+
}
|
|
133
221
|
|
|
134
|
-
let
|
|
135
|
-
let isNear = dist < thresh * 5
|
|
222
|
+
let flat = !p1.area || area1 < thresh
|
|
136
223
|
|
|
137
224
|
|
|
138
225
|
|
|
139
226
|
/**
|
|
140
227
|
* check extremes
|
|
141
228
|
*/
|
|
142
|
-
|
|
143
229
|
let isExtreme = false;
|
|
144
230
|
|
|
231
|
+
//console.log(bb0.top, p1.x, p1.y);
|
|
232
|
+
|
|
145
233
|
// 1. total extreme
|
|
146
|
-
|
|
147
|
-
|
|
234
|
+
let isTop = p1.y === bb0.top;
|
|
235
|
+
let isBottom = p1.y === bb0.bottom;
|
|
236
|
+
let isLeft = p1.x === bb0.left;
|
|
237
|
+
let isRight = p1.x === bb0.right;
|
|
238
|
+
|
|
239
|
+
if (i === 0) {
|
|
240
|
+
//console.log(p1, bb0);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (isTop || isBottom || isLeft || isRight) {
|
|
244
|
+
isExtreme = true;
|
|
245
|
+
//renderPoint(markers, p1, 'cyan', '2.75%')
|
|
148
246
|
}
|
|
149
247
|
|
|
150
248
|
// 1.2 horizontal or vertical
|
|
151
|
-
|
|
152
|
-
let
|
|
249
|
+
/*
|
|
250
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x);
|
|
251
|
+
let isVertical = isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y)
|
|
252
|
+
|
|
153
253
|
|
|
154
254
|
if ((isHorizontal || isVertical)) {
|
|
155
|
-
|
|
255
|
+
|
|
256
|
+
let diffX = Math.abs(p0.x - p1.x)
|
|
257
|
+
let diffY = Math.abs(p0.y - p1.y)
|
|
258
|
+
|
|
259
|
+
//renderPoint(markers, p0, 'cyan', '2.75%')
|
|
260
|
+
|
|
261
|
+
if (isLong) {
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (isLong && (diffY < thresh2) && diffX > thresh) {
|
|
265
|
+
p0.isExtreme = true;
|
|
266
|
+
p0.isHorizontal = true;
|
|
267
|
+
}
|
|
268
|
+
else if (isLong && (diffX < thresh2) && diffY > thresh) {
|
|
269
|
+
p0.isExtreme = true;
|
|
270
|
+
p0.isVertical = true;
|
|
271
|
+
}
|
|
272
|
+
|
|
156
273
|
isExtreme = true
|
|
157
274
|
}
|
|
275
|
+
*/
|
|
276
|
+
|
|
277
|
+
let dx = Math.abs(p0.x - p1.x)
|
|
278
|
+
let dy = Math.abs(p0.y - p1.y)
|
|
279
|
+
|
|
280
|
+
let vh_thresh = thresh * 0.05
|
|
281
|
+
// vh_thresh = thresh * 0.25
|
|
282
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x) || (dy <= vh_thresh);
|
|
283
|
+
let isVertical = (isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y) || (dx <= vh_thresh))
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
// renderPoint(markers, p1, 'red', '0.5%')
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
if (p1.y === p0.y) {
|
|
290
|
+
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (dy > 0) {
|
|
294
|
+
//console.log(p1, dy);
|
|
295
|
+
//renderPoint(markers, p1, 'red', '3%')
|
|
296
|
+
//renderPoint(markers, p0, 'blue', '3%')
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
if ((isHorizontal || isVertical)) {
|
|
302
|
+
|
|
303
|
+
//renderPoint(markers, p1, 'red', '3%')
|
|
304
|
+
if (isLong && isHorizontal) {
|
|
305
|
+
p0.isExtreme = true;
|
|
306
|
+
p0.isHorizontal = true;
|
|
307
|
+
|
|
308
|
+
}
|
|
309
|
+
else if (isLong && isVertical) {
|
|
310
|
+
p0.isExtreme = true;
|
|
311
|
+
p0.isVertical = true;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
isExtreme = true
|
|
315
|
+
}
|
|
316
|
+
|
|
158
317
|
|
|
159
318
|
|
|
160
319
|
// 1.3 is local or absolute extreme
|
|
@@ -169,12 +328,15 @@ export function analyzePoly(pts, {
|
|
|
169
328
|
}
|
|
170
329
|
|
|
171
330
|
|
|
172
|
-
|
|
173
331
|
/**
|
|
174
332
|
* 2. sign changes
|
|
175
333
|
*/
|
|
176
334
|
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0)
|
|
177
|
-
let isDirChange = signChange && !flat && !p0.isDirChange
|
|
335
|
+
let isDirChange = signChange && !flat && !p0.isDirChange && isLong
|
|
336
|
+
|
|
337
|
+
if (isDirChange) {
|
|
338
|
+
//renderPoint(markers, p1, 'green', '3%')
|
|
339
|
+
}
|
|
178
340
|
|
|
179
341
|
|
|
180
342
|
|
|
@@ -182,7 +344,6 @@ export function analyzePoly(pts, {
|
|
|
182
344
|
* 3. corners
|
|
183
345
|
*/
|
|
184
346
|
|
|
185
|
-
//isDirChange &&
|
|
186
347
|
if (isExtreme) {
|
|
187
348
|
|
|
188
349
|
let delta = getDeltaAngle(p1, p2, p0)
|
|
@@ -193,125 +354,79 @@ export function analyzePoly(pts, {
|
|
|
193
354
|
if (isCornerDelta) {
|
|
194
355
|
//console.log(deltaAngleDeg);
|
|
195
356
|
isCorner = true;
|
|
357
|
+
//console.log( p1.dx, p1.dy, p1.dx2, p1.dy2);
|
|
196
358
|
}
|
|
197
359
|
|
|
198
360
|
}
|
|
199
361
|
|
|
200
362
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if ((isHorizontal || isVertical)) {
|
|
206
|
-
renderPoint(markers, p1, 'blue', '2%', '0.5')
|
|
207
|
-
renderPoint(markers, p0, 'blue', '2%', '0.5')
|
|
363
|
+
if (isExtreme && !isCorner) {
|
|
364
|
+
//console.log('dy', p1.dy, p1.dy2);
|
|
365
|
+
if ((Math.abs(p1.dy2) < thresh2) && Math.abs(p1.dx2) > thresh) {
|
|
366
|
+
isHorizontal = true
|
|
208
367
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
renderPoint(markers, p1, 'cyan', '1.5%', '0.5')
|
|
368
|
+
else if (Math.abs(p1.dx2) < thresh2 && Math.abs(p1.dy2) > thresh) {
|
|
369
|
+
isVertical = true
|
|
212
370
|
}
|
|
371
|
+
}
|
|
213
372
|
|
|
214
|
-
if (isDirChange) {
|
|
215
|
-
renderPoint(markers, p1, 'orange', '0.75%', '0.5')
|
|
216
|
-
}
|
|
217
373
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
374
|
+
/**
|
|
375
|
+
* semi extremes
|
|
376
|
+
* ~ 45deg tangent
|
|
377
|
+
*/
|
|
378
|
+
let diffX = Math.abs(p1.dx2)
|
|
379
|
+
let diffY = Math.abs(p1.dy2)
|
|
380
|
+
//let ratDelta = Math.abs(0.5-diffX/diffY)
|
|
381
|
+
let ratDelta = (diffX / diffY)
|
|
382
|
+
|
|
383
|
+
if (ratDelta > 0.8 && ratDelta <= 1.2) {
|
|
384
|
+
isSemiExtreme = true;
|
|
221
385
|
}
|
|
222
|
-
*/
|
|
223
386
|
|
|
224
387
|
|
|
225
388
|
p1.isCorner = isCorner;
|
|
226
389
|
p1.isExtreme = isExtreme;
|
|
390
|
+
p1.isSemiExtreme = isSemiExtreme;
|
|
391
|
+
p1.isLong = isLong;
|
|
392
|
+
p1.isShort = isShort;
|
|
393
|
+
|
|
227
394
|
p1.isHorizontal = isHorizontal;
|
|
228
395
|
p1.isVertical = isVertical;
|
|
229
396
|
p1.isDirChange = isDirChange;
|
|
230
397
|
|
|
231
398
|
//renderPoint(markers, p1, 'red', '2%', '0.5')
|
|
232
399
|
|
|
233
|
-
|
|
234
400
|
}
|
|
235
401
|
|
|
236
402
|
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
let exclude = []
|
|
240
|
-
let filterExtremes = true;
|
|
241
|
-
|
|
242
|
-
if (filterExtremes) {
|
|
243
|
-
for (let i = 0; i < pts.length; i++) {
|
|
244
|
-
let p = pts[i]
|
|
245
|
-
let p1 = pts[i + 1] || null
|
|
246
|
-
let p2 = pts[i + 2] || null
|
|
247
|
-
|
|
248
|
-
let extremes = []
|
|
249
|
-
|
|
250
|
-
if (p1 && p1.isExtreme && p.isExtreme && !p.isCorner) {
|
|
251
|
-
let has2nd = p1.dist < thresh * 2 && !p1.isCorner
|
|
252
|
-
let has3rd = p2 && p2.isExtreme && p2.dist < thresh * 2 && !p2.isCorner
|
|
253
|
-
let lastExt = p1
|
|
254
|
-
|
|
255
|
-
if (has2nd && !has3rd) {
|
|
256
|
-
extremes.push(p, p1)
|
|
257
|
-
//renderPoint(markers, p1, 'magenta', '1%', '0.5')
|
|
258
|
-
} else if (has3rd) {
|
|
259
|
-
extremes.push(p, p1, p2)
|
|
260
|
-
/*
|
|
261
|
-
renderPoint(markers, p, 'green', '1%', '0.5')
|
|
262
|
-
renderPoint(markers, p1, 'red', '1%', '0.5')
|
|
263
|
-
renderPoint(markers, p2, 'blue', '1%', '0.5')
|
|
264
|
-
*/
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (extremes.length) {
|
|
268
|
-
// average extreme
|
|
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
|
|
278
|
-
}
|
|
279
|
-
}
|
|
403
|
+
// add tangents
|
|
404
|
+
getTangents(pts, { x, y, width, height })
|
|
280
405
|
|
|
281
|
-
|
|
282
|
-
exclude.push(pts1.length)
|
|
283
|
-
}
|
|
406
|
+
refineAdjacentPolyExtremes(pts)
|
|
284
407
|
|
|
408
|
+
// filter adjacent significant points
|
|
409
|
+
cleanupPolyKeypoints(pts);
|
|
285
410
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
// remove last nearby extreme
|
|
289
|
-
let l2 = pts1.length;
|
|
290
|
-
let p0 = pts1[0]
|
|
291
|
-
let pL = pts1[l2 - 1]
|
|
292
|
-
let near0 = getDistManhattan(p0, pL) < thresh * 2
|
|
293
|
-
if (p0.isExtreme && pL.isExtreme && near0) {
|
|
294
|
-
pL.x = p0.x
|
|
295
|
-
pL.y = p0.y
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
pts = pts1
|
|
300
|
-
}
|
|
301
|
-
|
|
411
|
+
renderPolyTopology(pts)
|
|
302
412
|
|
|
303
413
|
return pts
|
|
304
414
|
}
|
|
305
415
|
|
|
416
|
+
/*
|
|
306
417
|
|
|
307
|
-
|
|
418
|
+
*/
|
|
308
419
|
|
|
309
420
|
|
|
310
421
|
|
|
311
422
|
// just for visualization
|
|
312
|
-
function renderPolyTopology(pts) {
|
|
423
|
+
function renderPolyTopology(pts, showTangents = true) {
|
|
313
424
|
|
|
314
425
|
let l = pts.length
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
//renderPoint(markers, pts[0], 'green', '1.5%', '0.5')
|
|
429
|
+
|
|
315
430
|
// render
|
|
316
431
|
for (let i = 0; i < l; i++) {
|
|
317
432
|
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
@@ -319,24 +434,38 @@ function renderPolyTopology(pts) {
|
|
|
319
434
|
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
320
435
|
|
|
321
436
|
|
|
322
|
-
renderPoly(markers, [p0, p1, p2], '0.5%', 'none', (p1.area > 0 ? 'green' : 'blue'), true)
|
|
437
|
+
//renderPoly(markers, [p0, p1, p2], '0.5%', 'none', (p1.area > 0 ? 'green' : 'blue'), true)
|
|
323
438
|
|
|
324
439
|
|
|
325
440
|
if (p1.isDirChange) {
|
|
326
441
|
renderPoint(markers, p1, 'orange', '1%', '0.75')
|
|
327
442
|
}
|
|
328
443
|
|
|
444
|
+
if (p1.isSemiExtreme) {
|
|
445
|
+
renderPoint(markers, p1, 'red', '1%', '0.5')
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/*
|
|
449
|
+
if (p1.isLong && (p1.isDirChange || p1.isExtreme || p1.isCorner || p1.isSemiExtreme)) {
|
|
450
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5')
|
|
451
|
+
}
|
|
452
|
+
*/
|
|
453
|
+
|
|
454
|
+
if (p1.isDirChange) {
|
|
455
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5')
|
|
456
|
+
}
|
|
457
|
+
|
|
329
458
|
|
|
330
459
|
if (p1.isExtreme) {
|
|
331
460
|
renderPoint(markers, p1, 'cyan', '1%', '0.5')
|
|
332
461
|
}
|
|
333
462
|
|
|
334
463
|
if (p1.isHorizontal) {
|
|
335
|
-
|
|
464
|
+
renderPoint(markers, p1, 'blue', '1.5%', '0.25')
|
|
336
465
|
}
|
|
337
466
|
|
|
338
467
|
if (p1.isVertical) {
|
|
339
|
-
|
|
468
|
+
renderPoint(markers, p1, 'purple', '1.5%', '0.25')
|
|
340
469
|
}
|
|
341
470
|
|
|
342
471
|
|
|
@@ -345,8 +474,26 @@ function renderPolyTopology(pts) {
|
|
|
345
474
|
}
|
|
346
475
|
|
|
347
476
|
//pts[i] = p1
|
|
477
|
+
|
|
478
|
+
if (showTangents && (p1.isCorner || p1.isSemiExtreme || p1.isDirChange || p1.isExtreme)) {
|
|
479
|
+
renderPoint(markers, p1.tangentL, 'darkred', '0.5%')
|
|
480
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '0.5%')
|
|
481
|
+
|
|
482
|
+
/*
|
|
483
|
+
if (p1.isDirChange) {
|
|
484
|
+
renderPoint(markers, p1.tangentL, 'darkred', '1.5%')
|
|
485
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '1.5%')
|
|
486
|
+
}
|
|
487
|
+
*/
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
}
|
|
492
|
+
|
|
348
493
|
}
|
|
349
494
|
|
|
495
|
+
|
|
496
|
+
|
|
350
497
|
}
|
|
351
498
|
|
|
352
499
|
|