svg-path-simplify 0.4.4 → 0.4.6
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 +19 -0
- package/README.md +9 -1
- package/dist/svg-path-simplify.esm.js +1649 -1205
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1649 -1204
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +301 -637
- 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 +13 -8
- 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 +19 -21
- package/src/pathData_simplify_cubic_extrapolate.js +147 -8
- package/src/pathData_simplify_cubicsToArcs.js +65 -21
- package/src/pathSimplify-main.js +267 -64
- package/src/pathSimplify-presets.js +1 -1
- 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/svg-getAttributes.js +1 -1
- package/src/svgii/geometry.js +130 -27
- package/src/svgii/geometry_bbox.js +1 -1
- package/src/svgii/geometry_length.js +1 -1
- package/src/svgii/pathData_analyze.js +6 -28
- package/src/svgii/pathData_convert.js +63 -15
- package/src/svgii/pathData_remove_collinear.js +32 -36
- package/src/svgii/pathData_reorder.js +3 -2
- 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 +21 -8
- package/src/svgii/poly_to_pathdata.js +477 -8
- package/src/svgii/rounding.js +35 -14
- package/src/svgii/svg_cleanup.js +44 -51
- package/src/svgii/svg_cleanup_convertPathLength.js +5 -0
- package/src/svgii/svg_cleanup_normalize_transforms.js +2 -2
- package/src/svgii/visualize.js +33 -2
- package/v/0.4.5/index.html +1040 -0
- package/v/0.4.5/svg-path-simplify.js +13227 -0
package/src/simplify_poly_RC.js
CHANGED
|
@@ -2,12 +2,34 @@ import { getSquareDistance, reducePoints } from "./svgii/geometry";
|
|
|
2
2
|
import { getPolygonArea } from "./svgii/geometry_area";
|
|
3
3
|
import { getPolyBBox } from "./svgii/geometry_bbox";
|
|
4
4
|
|
|
5
|
-
export function simplifyRC(pts, quality = 1, shiftStart = true) {
|
|
6
|
-
|
|
7
|
-
if (pts.length < 4) return pts;
|
|
8
5
|
|
|
6
|
+
export function removeCoincidingVertices(pts = []) {
|
|
9
7
|
let l = pts.length;
|
|
8
|
+
if (!l) return pts;
|
|
9
|
+
|
|
10
|
+
let ptsN = [pts[0]];
|
|
11
|
+
let pt1, pt2;
|
|
12
|
+
|
|
13
|
+
for (let i = 1; i < l; i++) {
|
|
14
|
+
pt1 = pts[i - 1];
|
|
15
|
+
pt2 = pts[i];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 1. Skip zero-length segments
|
|
19
|
+
*/
|
|
20
|
+
if (pt1.x === pt2.x && pt1.y === pt2.y) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
ptsN.push(pt2)
|
|
24
|
+
}
|
|
25
|
+
return ptsN
|
|
26
|
+
|
|
27
|
+
}
|
|
10
28
|
|
|
29
|
+
export function simplifyRC(pts = [], quality = 1, shiftStart = true) {
|
|
30
|
+
|
|
31
|
+
let l = pts.length;
|
|
32
|
+
if (l < 4) return pts;
|
|
11
33
|
|
|
12
34
|
// starting point
|
|
13
35
|
let M = pts[0];
|
|
@@ -72,7 +94,7 @@ export function simplifyRC(pts, quality = 1, shiftStart = true) {
|
|
|
72
94
|
let thresh = getSquareDistance(pt0, pt2) * 0.005;
|
|
73
95
|
|
|
74
96
|
// flat
|
|
75
|
-
if (
|
|
97
|
+
if (area <= thresh && i < l - 1) {
|
|
76
98
|
//console.log(area, thresh, pt0, pt1, pt2, i);
|
|
77
99
|
pt0 = pt1;
|
|
78
100
|
continue
|
|
@@ -93,10 +115,10 @@ export function simplifyRC(pts, quality = 1, shiftStart = true) {
|
|
|
93
115
|
}
|
|
94
116
|
|
|
95
117
|
// 1st and last are colinear
|
|
96
|
-
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length-1]], true)
|
|
97
|
-
let thresh0 = getSquareDistance
|
|
118
|
+
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length - 1]], true)
|
|
119
|
+
let thresh0 = getSquareDistance(ptsSmp[1], ptsSmp[ptsSmp.length - 1]) * 0.005
|
|
98
120
|
// remove first point
|
|
99
|
-
if(area0 < thresh0) ptsSmp.shift()
|
|
121
|
+
if (area0 < thresh0) ptsSmp.shift()
|
|
100
122
|
|
|
101
123
|
return ptsSmp;
|
|
102
124
|
}
|
package/src/svg-getAttributes.js
CHANGED
|
@@ -8,7 +8,7 @@ export function getElementAtts(el, {x=0, y=0, width=0, height=0}={}){
|
|
|
8
8
|
attributes.forEach(att=>{
|
|
9
9
|
//let value = normalizeUnits(att.nodeValue, {x, y, width, height});
|
|
10
10
|
let value = normalizeUnits(el.getAttribute(att), {x, y, width, height});
|
|
11
|
-
atts[att
|
|
11
|
+
atts[att] = value
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
return atts
|
package/src/svgii/geometry.js
CHANGED
|
@@ -17,14 +17,26 @@ export const {
|
|
|
17
17
|
|
|
18
18
|
// get angle helper
|
|
19
19
|
export function getAngle(p1, p2, normalize = false) {
|
|
20
|
-
let angle = atan2(p2.y - p1.y, p2.x - p1.x);
|
|
20
|
+
let angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
|
|
21
21
|
// normalize negative angles
|
|
22
22
|
if (normalize && angle < 0) angle += Math.PI * 2
|
|
23
23
|
return angle
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function getAngleFromDelta(dx, dy, normalize = false) {
|
|
27
|
+
let angle = Math.atan2(dy, dx);
|
|
28
|
+
// normalize negative angles
|
|
29
|
+
if (normalize && angle < 0) angle += Math.PI * 2
|
|
30
|
+
return angle
|
|
31
|
+
}
|
|
26
32
|
|
|
27
33
|
|
|
34
|
+
export function getPerpendicularPoint(A, B, C) {
|
|
35
|
+
let dx = C.x - A.x;
|
|
36
|
+
let dy = C.y - A.y;
|
|
37
|
+
let t = ((B.x - A.x) * dx + (B.y - A.y) * dy) / (dx * dx + dy * dy);
|
|
38
|
+
return { x: A.x + dx * t, y: A.y + dy * t };
|
|
39
|
+
}
|
|
28
40
|
|
|
29
41
|
|
|
30
42
|
|
|
@@ -117,6 +129,60 @@ export function getDeltaAngle(centerPoint, startPoint, endPoint, largeArc = fals
|
|
|
117
129
|
|
|
118
130
|
|
|
119
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Returns an array of intersection points.
|
|
134
|
+
* 0 points: No intersection
|
|
135
|
+
* 1 point: Line is tangent
|
|
136
|
+
* 2 points: Line is a secant
|
|
137
|
+
*/
|
|
138
|
+
export function lineCircleIntersection(p1 = {x: 0,y: 0}, p2 = {x: 0,y: 0}, centroid = {x: 0,y: 0}, r = 1, exact = true, ignoreStartAndEnd=true
|
|
139
|
+
) {
|
|
140
|
+
let error = 1e-8;
|
|
141
|
+
r *= 1 + error
|
|
142
|
+
let [x1, y1] = [p1.x, p1.y];
|
|
143
|
+
let [x2, y2] = [p2.x, p2.y];
|
|
144
|
+
let [cx, cy] = [centroid.x, centroid.y];
|
|
145
|
+
let dx = x2 - x1;
|
|
146
|
+
let dy = y2 - y1;
|
|
147
|
+
let a = dx * dx + dy * dy;
|
|
148
|
+
let b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
|
|
149
|
+
let c = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy) - r * r;
|
|
150
|
+
let discriminant = b * b - 4 * a * c;
|
|
151
|
+
let intersections = [];
|
|
152
|
+
// No intersection
|
|
153
|
+
if (discriminant < 0) {
|
|
154
|
+
return [];
|
|
155
|
+
} else {
|
|
156
|
+
|
|
157
|
+
let t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
|
|
158
|
+
let t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
|
|
159
|
+
// Sort t values to maintain drawing direction order
|
|
160
|
+
let tValues = [t1, t2].sort((a, b) => a - b);
|
|
161
|
+
|
|
162
|
+
let tMin = ignoreStartAndEnd ? 0.01 : 0;
|
|
163
|
+
let tMax = ignoreStartAndEnd ? 0.99 : 1;
|
|
164
|
+
|
|
165
|
+
tValues.forEach(t => {
|
|
166
|
+
//let onSegment = !exact ? true : t >= 0 && t <= 1;
|
|
167
|
+
let onSegment = !exact ? true : t > tMin && t < tMax;
|
|
168
|
+
if (onSegment) {
|
|
169
|
+
let pt = {
|
|
170
|
+
x: x1 + t * dx,
|
|
171
|
+
y: y1 + t * dy,
|
|
172
|
+
t: t
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
intersections.push(pt);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/*
|
|
180
|
+
intersections.forEach(pt => {
|
|
181
|
+
renderPoint(svg, pt, 'green', '1.5%')
|
|
182
|
+
})
|
|
183
|
+
*/
|
|
184
|
+
return intersections;
|
|
185
|
+
}
|
|
120
186
|
|
|
121
187
|
|
|
122
188
|
|
|
@@ -164,10 +230,14 @@ export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null
|
|
|
164
230
|
a = numerator1 / denominator;
|
|
165
231
|
b = numerator2 / denominator;
|
|
166
232
|
|
|
233
|
+
//console.log('denominator', denominator, a, b);
|
|
234
|
+
|
|
167
235
|
// if we cast these lines infinitely in both directions, they intersect here:
|
|
168
236
|
intersectionPoint = {
|
|
169
237
|
x: p1.x + (a * (p2.x - p1.x)),
|
|
170
|
-
y: p1.y + (a * (p2.y - p1.y))
|
|
238
|
+
y: p1.y + (a * (p2.y - p1.y)),
|
|
239
|
+
t1: a,
|
|
240
|
+
t2: b
|
|
171
241
|
}
|
|
172
242
|
|
|
173
243
|
let intersection = false;
|
|
@@ -277,13 +347,13 @@ export function pointAtT(pts, t = 0.5, getTangent = false, getCpts = false, retu
|
|
|
277
347
|
if (t === 0 && !shortCp1) {
|
|
278
348
|
pt.x = p0.x;
|
|
279
349
|
pt.y = p0.y;
|
|
280
|
-
pt.angle = getAngle(p0, cp1)
|
|
350
|
+
if (getTangent) pt.angle = getAngle(p0, cp1);
|
|
281
351
|
}
|
|
282
352
|
|
|
283
353
|
else if (t === 1 && !shortCp2) {
|
|
284
354
|
pt.x = p.x;
|
|
285
355
|
pt.y = p.y;
|
|
286
|
-
pt.angle = getAngle(cp2, p)
|
|
356
|
+
if (getTangent) pt.angle = getAngle(cp2, p);
|
|
287
357
|
}
|
|
288
358
|
|
|
289
359
|
else {
|
|
@@ -300,18 +370,39 @@ export function pointAtT(pts, t = 0.5, getTangent = false, getCpts = false, retu
|
|
|
300
370
|
pt = interpolate(m3, m4, t);
|
|
301
371
|
|
|
302
372
|
// add angles
|
|
303
|
-
pt.angle = getAngle(m3, m4)
|
|
373
|
+
if (getTangent) pt.angle = getAngle(m3, m4);
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
renderPoint(markers, m0, 'cyan')
|
|
377
|
+
renderPoint(markers, m3, 'magenta')
|
|
378
|
+
renderPoint(markers, pt, 'green')
|
|
379
|
+
renderPoint(markers, m4, 'cyan')
|
|
380
|
+
renderPoint(markers, m2, 'magenta')
|
|
381
|
+
*/
|
|
382
|
+
|
|
304
383
|
|
|
305
384
|
// add control points
|
|
306
|
-
if (getCpts)
|
|
385
|
+
if (getCpts) {
|
|
386
|
+
pt.cpts = [m1, m2, m3, m4];
|
|
387
|
+
pt.segments = [
|
|
388
|
+
{ p0, cp1: m0, cp2: m3, p: pt },
|
|
389
|
+
{ p0: pt, cp1: m4, cp2: m2, p }
|
|
390
|
+
]
|
|
391
|
+
}
|
|
307
392
|
} else {
|
|
308
393
|
m1 = interpolate(p0, cp1, t);
|
|
309
394
|
m2 = interpolate(cp1, p, t);
|
|
310
395
|
pt = interpolate(m1, m2, t);
|
|
311
|
-
pt.angle = getAngle(m1, m2);
|
|
396
|
+
if (getTangent) pt.angle = getAngle(m1, m2);
|
|
312
397
|
|
|
313
398
|
// add control points
|
|
314
|
-
if (getCpts)
|
|
399
|
+
if (getCpts) {
|
|
400
|
+
pt.cpts = [m1, m2];
|
|
401
|
+
pt.segments = [
|
|
402
|
+
{ p0, cp1: m1, p: pt },
|
|
403
|
+
{ p0: p, cp1: m2, p }
|
|
404
|
+
]
|
|
405
|
+
}
|
|
315
406
|
}
|
|
316
407
|
}
|
|
317
408
|
|
|
@@ -401,7 +492,7 @@ export function pointAtT(pts, t = 0.5, getTangent = false, getCpts = false, retu
|
|
|
401
492
|
* get vertices from path command final on-path points
|
|
402
493
|
*/
|
|
403
494
|
|
|
404
|
-
export function getPathDataVertices(pathData=[], includeCpts = false, decimals = -1) {
|
|
495
|
+
export function getPathDataVertices(pathData = [], includeCpts = false, decimals = -1) {
|
|
405
496
|
let polyPoints = [];
|
|
406
497
|
//console.log(pathData);
|
|
407
498
|
|
|
@@ -459,6 +550,8 @@ export function getPathDataVertices(pathData) {
|
|
|
459
550
|
export function svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2, normalize = true
|
|
460
551
|
) {
|
|
461
552
|
|
|
553
|
+
//console.log('params', [x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2]);
|
|
554
|
+
|
|
462
555
|
// helper for angle calculation
|
|
463
556
|
const getAngle = (cx, cy, x, y, normalize = true) => {
|
|
464
557
|
let angle = Math.atan2(y - cy, x - cx);
|
|
@@ -494,7 +587,8 @@ export function svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, swe
|
|
|
494
587
|
|
|
495
588
|
if (rx == 0 || ry == 0) {
|
|
496
589
|
// invalid arguments
|
|
497
|
-
|
|
590
|
+
console.warn("rx and ry can not be 0");
|
|
591
|
+
return arcData
|
|
498
592
|
}
|
|
499
593
|
|
|
500
594
|
|
|
@@ -508,10 +602,10 @@ export function svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, swe
|
|
|
508
602
|
let s_phi = !phi ? 0 : Math.sin(phi);
|
|
509
603
|
let c_phi = !phi ? 1 : Math.cos(phi);
|
|
510
604
|
|
|
511
|
-
let hd_x = (x1 - x2)
|
|
512
|
-
let hd_y = (y1 - y2)
|
|
513
|
-
let hs_x = (x1 + x2)
|
|
514
|
-
let hs_y = (y1 + y2)
|
|
605
|
+
let hd_x = (x1 - x2) * 0.5;
|
|
606
|
+
let hd_y = (y1 - y2) * 0.5;
|
|
607
|
+
let hs_x = (x1 + x2) * 0.5;
|
|
608
|
+
let hs_y = (y1 + y2) * 0.5;
|
|
515
609
|
|
|
516
610
|
// F6.5.1
|
|
517
611
|
let x1_ = !phi ? hd_x : c_phi * hd_x + s_phi * hd_y;
|
|
@@ -533,10 +627,11 @@ export function svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, swe
|
|
|
533
627
|
let rxry = rx * ry;
|
|
534
628
|
let rxy1_ = rx * y1_;
|
|
535
629
|
let ryx1_ = ry * x1_;
|
|
536
|
-
let sum_of_sq = rxy1_
|
|
630
|
+
let sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
|
|
537
631
|
if (!sum_of_sq) {
|
|
538
|
-
|
|
539
|
-
|
|
632
|
+
console.warn("start point can not be same as end point");
|
|
633
|
+
return arcData
|
|
634
|
+
|
|
540
635
|
}
|
|
541
636
|
let coe = Math.sqrt(Math.abs((rxry * rxry - sum_of_sq) / sum_of_sq));
|
|
542
637
|
if (largeArc === sweep) {
|
|
@@ -973,7 +1068,11 @@ export function getSemiExtremesT(p0, cp1, cp2, p, angleRad = Math.PI / 2) {
|
|
|
973
1068
|
|
|
974
1069
|
export function getBezierExtremeT(pts, { addExtremes = true, addSemiExtremes = false } = {}) {
|
|
975
1070
|
let tArr = pts.length === 4 ? cubicBezierExtremeT(pts[0], pts[1], pts[2], pts[3], { addExtremes, addSemiExtremes }) : quadraticBezierExtremeT(pts[0], pts[1], pts[2], { addExtremes, addSemiExtremes });
|
|
976
|
-
|
|
1071
|
+
if (tArr.length) {
|
|
1072
|
+
tArr = tArr.map(t => +t.toFixed(9)).sort();
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
return tArr
|
|
977
1076
|
}
|
|
978
1077
|
|
|
979
1078
|
|
|
@@ -985,14 +1084,14 @@ export function getBezierExtremeT(pts, { addExtremes = true, addSemiExtremes = f
|
|
|
985
1084
|
*/
|
|
986
1085
|
|
|
987
1086
|
export function getArcExtemesParam({
|
|
988
|
-
cx=0, cy=0, rx=0, ry=0,
|
|
989
|
-
p=null,
|
|
990
|
-
p0=null,
|
|
991
|
-
endAngle=0,
|
|
992
|
-
deltaAngle=0,
|
|
1087
|
+
cx = 0, cy = 0, rx = 0, ry = 0,
|
|
1088
|
+
p = null,
|
|
1089
|
+
p0 = null,
|
|
1090
|
+
endAngle = 0,
|
|
1091
|
+
deltaAngle = 0,
|
|
993
1092
|
|
|
994
1093
|
|
|
995
|
-
}={}) {
|
|
1094
|
+
} = {}) {
|
|
996
1095
|
// compute point on ellipse from angle around ellipse (theta)
|
|
997
1096
|
const arc = (theta, cx, cy, rx, ry, alpha) => {
|
|
998
1097
|
// theta is angle in radians around arc
|
|
@@ -1602,16 +1701,19 @@ export function intersectLines(p1, p2, p3, p4) {
|
|
|
1602
1701
|
* pythagorean theorem
|
|
1603
1702
|
*/
|
|
1604
1703
|
export function getDistance(p1, p2, isArray = false) {
|
|
1605
|
-
//if(Array.isArray(p1)) isArray = true;
|
|
1606
1704
|
|
|
1607
|
-
//console.log(p1, p2);
|
|
1608
1705
|
let dx = isArray ? p2[0] - p1[0] : (p2.x - p1.x);
|
|
1609
1706
|
let dy = isArray ? p2[1] - p1[1] : (p2.y - p1.y);
|
|
1610
1707
|
|
|
1611
|
-
|
|
1708
|
+
/*
|
|
1709
|
+
let sqrt2 = 1.4142135623730951
|
|
1710
|
+
return dx===dy ? Math.abs(dx) * sqrt2 : Math.sqrt(dx * dx + dy * dy);
|
|
1711
|
+
*/
|
|
1712
|
+
|
|
1612
1713
|
return Math.sqrt(dx * dx + dy * dy);
|
|
1613
1714
|
}
|
|
1614
1715
|
|
|
1716
|
+
|
|
1615
1717
|
// just an alias
|
|
1616
1718
|
export function lineLength(p1, p2) {
|
|
1617
1719
|
return getDistance(p1, p2)
|
|
@@ -1632,6 +1734,7 @@ export function getSquareDistance(p1, p2) {
|
|
|
1632
1734
|
* sloppy but fast
|
|
1633
1735
|
*/
|
|
1634
1736
|
export function getDistManhattan(pt1, pt2) {
|
|
1737
|
+
//console.log(pt1, pt2);
|
|
1635
1738
|
let dx = Math.abs(pt2.x - pt1.x);
|
|
1636
1739
|
let dy = Math.abs(pt2.y - pt1.y);
|
|
1637
1740
|
return dx + dy;
|
|
@@ -218,7 +218,7 @@ export function getPathDataBBox(pathData) {
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
let bbox = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin }
|
|
221
|
+
let bbox = { x: xMin, y: yMin, right: xMax, width: xMax - xMin, bottom: yMax, height: yMax - yMin }
|
|
222
222
|
return bbox
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -111,7 +111,7 @@ export function getLength(pts, {
|
|
|
111
111
|
|
|
112
112
|
// LG weight/abscissae generator
|
|
113
113
|
export function getLegendreGaussValues(n, x1 = -1, x2 = 1) {
|
|
114
|
-
console.log('add new LG', n);
|
|
114
|
+
//console.log('add new LG', n);
|
|
115
115
|
|
|
116
116
|
let waArr = []
|
|
117
117
|
let z1, z, xm, xl, pp, p3, p2, p1;
|
|
@@ -21,7 +21,7 @@ export function analyzePathData(pathData = [], {
|
|
|
21
21
|
detectDirection = true,
|
|
22
22
|
detectSemiExtremes = false,
|
|
23
23
|
debug = false,
|
|
24
|
-
addSquareLength =
|
|
24
|
+
addSquareLength = false,
|
|
25
25
|
addArea = true,
|
|
26
26
|
|
|
27
27
|
} = {}) {
|
|
@@ -87,7 +87,8 @@ export function analyzePathData(pathData = [], {
|
|
|
87
87
|
// check flatness of command
|
|
88
88
|
let toleranceFlat = 0.01;
|
|
89
89
|
let thresholdLength = dimA * 0.1
|
|
90
|
-
let threshold = thresholdLength * 0.01
|
|
90
|
+
//let threshold = thresholdLength * 0.01
|
|
91
|
+
let threshold = dimA * 0.005
|
|
91
92
|
let areaThresh = squareDist * toleranceFlat;
|
|
92
93
|
let isFlat = Math.abs(cptArea) < areaThresh;
|
|
93
94
|
|
|
@@ -105,7 +106,7 @@ export function analyzePathData(pathData = [], {
|
|
|
105
106
|
let hasExtremes = false;
|
|
106
107
|
|
|
107
108
|
//!isFlat &&
|
|
108
|
-
if (isBezier) {
|
|
109
|
+
if (detectExtremes && isBezier) {
|
|
109
110
|
|
|
110
111
|
|
|
111
112
|
let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x)
|
|
@@ -116,7 +117,6 @@ export function analyzePathData(pathData = [], {
|
|
|
116
117
|
let horizontal = (dy === 0 || dy <= threshold) && dx > 0
|
|
117
118
|
let vertical = (dx === 0 || dx <= threshold) && dy > 0
|
|
118
119
|
|
|
119
|
-
|
|
120
120
|
if (horizontal || vertical) {
|
|
121
121
|
hasExtremes = true;
|
|
122
122
|
}
|
|
@@ -152,7 +152,7 @@ export function analyzePathData(pathData = [], {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// check extremes introduce by small arcs
|
|
155
|
-
else if(isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
|
|
155
|
+
else if(detectExtremes && isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
|
|
156
156
|
let distN = comN ? comN.dimA : 0
|
|
157
157
|
let isShort = com.dimA < (comPrev.dimA + distN) * 0.1;
|
|
158
158
|
let smallRadius = com.values[0] === com.values[1] && (com.values[0] < 1)
|
|
@@ -170,29 +170,7 @@ export function analyzePathData(pathData = [], {
|
|
|
170
170
|
if (hasExtremes) com.extreme = true
|
|
171
171
|
|
|
172
172
|
// Corners and semi extremes
|
|
173
|
-
if (isBezier && isBezierN) {
|
|
174
|
-
|
|
175
|
-
// semi extremes
|
|
176
|
-
if (detectSemiExtremes && !com.extreme) {
|
|
177
|
-
|
|
178
|
-
let dx1 = Math.abs(p.x - cp2.x)
|
|
179
|
-
let dy1 = Math.abs(p.y - cp2.y)
|
|
180
|
-
let hasSemiExtreme = false;
|
|
181
|
-
|
|
182
|
-
// exclude extremes or small deltas
|
|
183
|
-
if (dx1 && dy1 && dx1 > thresholdLength || dy1 > thresholdLength) {
|
|
184
|
-
let ang1 = getAngle(cp2, p)
|
|
185
|
-
let ang2 = getAngle(p, comN.cp1)
|
|
186
|
-
|
|
187
|
-
let ang3 = Math.abs(ang1 + ang2) / 2
|
|
188
|
-
hasSemiExtreme = isMultipleOf45(ang3)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (hasSemiExtreme) {
|
|
192
|
-
com.semiExtreme = true;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
173
|
+
if (detectCorners && isBezier && isBezierN) {
|
|
196
174
|
|
|
197
175
|
/**
|
|
198
176
|
* Detect direction change points
|
|
@@ -10,7 +10,7 @@ import { renderPoint, renderPath } from "./visualize";
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
import { checkLineIntersection, getAngle, getDeltaAngle, getDistance, getDistAv, getDistManhattan, getSquareDistance, interpolate, pointAtT, rotatePoint, toParametricAngle } from './geometry';
|
|
13
|
+
import { checkLineIntersection, getAngle, getDeltaAngle, getDistance, getDistAv, getDistManhattan, getSquareDistance, interpolate, pointAtT, rotatePoint, svgArcToCenterParam, toParametricAngle } from './geometry';
|
|
14
14
|
import { getPathArea, getPolygonArea, getRelativeAreaDiff } from './geometry_area';
|
|
15
15
|
import { parsePathDataString } from './pathData_parse';
|
|
16
16
|
import { pathDataToD } from './pathData_stringify';
|
|
@@ -70,7 +70,12 @@ export function convertPathData(pathData, {
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
// minify semicircle radii
|
|
73
|
-
if (optimizeArcs)
|
|
73
|
+
if (optimizeArcs) {
|
|
74
|
+
pathData = optimizeArcPathData(pathData);
|
|
75
|
+
} else {
|
|
76
|
+
// get true absolute radii
|
|
77
|
+
pathData = pathDataToTrueArcValues(pathData);
|
|
78
|
+
}
|
|
74
79
|
|
|
75
80
|
|
|
76
81
|
//if(decimals>-1 && decimals<2) pathData = roundPathData(pathData, decimals);
|
|
@@ -171,9 +176,49 @@ export function parsePathDataNormalized(d,
|
|
|
171
176
|
|
|
172
177
|
|
|
173
178
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
|
|
179
|
+
* Converts minified arc
|
|
180
|
+
* values to true rx and ry values
|
|
181
|
+
*/
|
|
182
|
+
export function pathDataToTrueArcValues(pathData = []) {
|
|
183
|
+
|
|
184
|
+
//return pathData
|
|
185
|
+
|
|
186
|
+
let remove = []
|
|
187
|
+
let l = pathData.length;
|
|
188
|
+
let pathDataN = [pathData[0]];
|
|
189
|
+
|
|
190
|
+
for (let i = 1; i < l; i++) {
|
|
191
|
+
let com = pathData[i];
|
|
192
|
+
let comPrev = pathData[i-1];
|
|
193
|
+
let { type, values } = com;
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if (type === 'A') {
|
|
197
|
+
|
|
198
|
+
// previous commands final on-path point
|
|
199
|
+
let [x1, y1] = comPrev.values.slice(-2)
|
|
200
|
+
let [rx, ry, xAxisRotation, largeArc, sweep, x2, y2] = values;
|
|
201
|
+
let arcData = svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2)
|
|
202
|
+
|
|
203
|
+
// set true arc values
|
|
204
|
+
com.values[0] = arcData.rx
|
|
205
|
+
com.values[1] = arcData.ry
|
|
206
|
+
//pathDataN.push(com)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
pathDataN.push(com)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
return pathDataN;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Minify arc radii
|
|
219
|
+
* for semi circles
|
|
220
|
+
* returns smaller rx and ry
|
|
221
|
+
* values
|
|
177
222
|
*/
|
|
178
223
|
|
|
179
224
|
export function optimizeArcPathData(pathData = []) {
|
|
@@ -240,7 +285,7 @@ export function optimizeArcPathData(pathData = []) {
|
|
|
240
285
|
if (isHorizontal || isVertical) {
|
|
241
286
|
|
|
242
287
|
// check if semi circle
|
|
243
|
-
let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
|
|
288
|
+
let needsTrueR = isHorizontal ? rx * 1.9 > diffX : ry * 1.9 > diffY;
|
|
244
289
|
|
|
245
290
|
// is semicircle we can simplify rx
|
|
246
291
|
if (!needsTrueR) {
|
|
@@ -1037,8 +1082,8 @@ export function arcToBezier(p0, values, splitSegments = 1) {
|
|
|
1037
1082
|
let phi = rotation ? rotation * TAU / 360 : 0;
|
|
1038
1083
|
let sinphi = phi ? Math.sin(phi) : 0
|
|
1039
1084
|
let cosphi = phi ? Math.cos(phi) : 1
|
|
1040
|
-
let pxp = cosphi * (p0.x - x)
|
|
1041
|
-
let pyp = -sinphi * (p0.x - x)
|
|
1085
|
+
let pxp = cosphi * (p0.x - x) *0.5 + sinphi * (p0.y - y) *0.5
|
|
1086
|
+
let pyp = -sinphi * (p0.x - x) *0.5 + cosphi * (p0.y - y) *0.5
|
|
1042
1087
|
|
|
1043
1088
|
if (pxp === 0 && pyp === 0) {
|
|
1044
1089
|
return []
|
|
@@ -1074,8 +1119,8 @@ export function arcToBezier(p0, values, splitSegments = 1) {
|
|
|
1074
1119
|
|
|
1075
1120
|
let centerxp = radicant ? radicant * rx / ry * pyp : 0
|
|
1076
1121
|
let centeryp = radicant ? radicant * -ry / rx * pxp : 0
|
|
1077
|
-
let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x)
|
|
1078
|
-
let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y)
|
|
1122
|
+
let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x) * 0.5
|
|
1123
|
+
let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y) * 0.5
|
|
1079
1124
|
|
|
1080
1125
|
let vx1 = (pxp - centerxp) / rx
|
|
1081
1126
|
let vy1 = (pyp - centeryp) / ry
|
|
@@ -1097,18 +1142,20 @@ export function arcToBezier(p0, values, splitSegments = 1) {
|
|
|
1097
1142
|
ang2 = vectorAngle(vx1, vy1, vx2, vy2)
|
|
1098
1143
|
|
|
1099
1144
|
if (sweepFlag === 0 && ang2 > 0) {
|
|
1100
|
-
ang2 -= Math.PI * 2
|
|
1145
|
+
//ang2 -= Math.PI * 2
|
|
1146
|
+
ang2 -= TAU
|
|
1101
1147
|
}
|
|
1102
1148
|
else if (sweepFlag === 1 && ang2 < 0) {
|
|
1103
|
-
ang2 += Math.PI * 2
|
|
1149
|
+
//ang2 += Math.PI * 2
|
|
1150
|
+
ang2 += TAU
|
|
1104
1151
|
}
|
|
1105
1152
|
|
|
1106
1153
|
|
|
1107
1154
|
//ratio must be at least 1
|
|
1108
|
-
let ratio = +(Math.abs(ang2) / (TAU
|
|
1155
|
+
let ratio = +(Math.abs(ang2) / (TAU * 0.25)).toFixed(0) || 1
|
|
1109
1156
|
|
|
1110
1157
|
|
|
1111
|
-
// increase segments for more
|
|
1158
|
+
// increase segments for more accurate length calculations
|
|
1112
1159
|
let segments = ratio * splitSegments;
|
|
1113
1160
|
ang2 /= segments
|
|
1114
1161
|
let pathDataArc = [];
|
|
@@ -1121,7 +1168,8 @@ export function arcToBezier(p0, values, splitSegments = 1) {
|
|
|
1121
1168
|
const k = 0.551785
|
|
1122
1169
|
let a = ang2 === angle90 ? k :
|
|
1123
1170
|
(
|
|
1124
|
-
ang2 === -angle90 ? -k : 4 / 3 * Math.tan(ang2 / 4)
|
|
1171
|
+
//ang2 === -angle90 ? -k : 4 / 3 * Math.tan(ang2 / 4)
|
|
1172
|
+
ang2 === -angle90 ? -k : 1.33333 * Math.tan(ang2 * 0.25)
|
|
1125
1173
|
);
|
|
1126
1174
|
|
|
1127
1175
|
let cos2 = ang2 ? Math.cos(ang2) : 1;
|