svg-path-simplify 0.0.9 → 0.1.2
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 +114 -46
- package/debug.cjs +11 -0
- package/dist/svg-path-simplify.esm.js +613 -98
- package/dist/svg-path-simplify.esm.min.js +1 -1
- package/dist/svg-path-simplify.js +613 -98
- package/dist/svg-path-simplify.min.js +1 -1
- package/dist/svg-path-simplify.poly.cjs +29 -0
- package/index.html +5 -1
- package/package.json +36 -16
- package/src/index-node.js +15 -0
- package/src/index-poly.js +27 -0
- package/src/index.js +1 -6
- package/src/pathSimplify-main.js +47 -14
- package/src/svgii/geometry.js +54 -4
- package/src/svgii/geometry_deduceRadius.js +50 -0
- package/src/svgii/pathData_convert.js +339 -5
- package/src/svgii/pathData_refine_round.js +222 -0
- package/src/svgii/pathData_remove_collinear.js +8 -3
- package/src/svgii/pathData_remove_short.js +66 -0
- package/src/svgii/pathData_reorder.js +27 -11
- package/src/svgii/pathData_simplify_refineCorners.js +57 -37
- package/src/svgii/svg_cleanup.js +28 -29
- package/src/svgii/visualize.js +2 -2
- package/testSVG.js +27 -20
- package/dist/svg-path-simplify.node.js +0 -5129
- package/dist/svg-path-simplify.node.min.js +0 -1
- package/src/dom-polyfill.js +0 -29
- package/src/dom-polyfill_back.js +0 -22
package/src/svgii/geometry.js
CHANGED
|
@@ -18,18 +18,68 @@ export function getAngle(p1, p2, normalize = false) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
export function getDeltaAngle(centerPoint, startPoint, endPoint, largeArc = false) {
|
|
22
|
+
|
|
23
|
+
const normalizeAngle = (angle) => {
|
|
24
|
+
let normalized = angle % (2 * Math.PI);
|
|
25
|
+
|
|
26
|
+
if (normalized > Math.PI) {
|
|
27
|
+
normalized -= 2 * Math.PI;
|
|
28
|
+
} else if (normalized <= -Math.PI) {
|
|
29
|
+
normalized += 2 * Math.PI;
|
|
30
|
+
}
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let startAngle = Math.atan2(
|
|
35
|
+
startPoint.y - centerPoint.y,
|
|
36
|
+
startPoint.x - centerPoint.x
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
let endAngle = Math.atan2(
|
|
40
|
+
endPoint.y - centerPoint.y,
|
|
41
|
+
endPoint.x - centerPoint.x
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// Calculate raw delta angle (difference)
|
|
45
|
+
let deltaAngle = endAngle - startAngle;
|
|
46
|
+
|
|
47
|
+
// Normalize the delta angle to range (-π, π]
|
|
48
|
+
deltaAngle = normalizeAngle(deltaAngle);
|
|
49
|
+
|
|
50
|
+
if (largeArc) deltaAngle = Math.PI*2 - Math.abs(deltaAngle);
|
|
51
|
+
|
|
52
|
+
let phi = 180 / Math.PI
|
|
53
|
+
let startAngleDeg = startAngle * phi
|
|
54
|
+
let endAngleDeg = endAngle * phi
|
|
55
|
+
let deltaAngleDeg = deltaAngle * phi
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
startAngle, endAngle, deltaAngle, startAngleDeg,
|
|
59
|
+
endAngleDeg,
|
|
60
|
+
deltaAngleDeg
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
21
71
|
/**
|
|
22
72
|
* based on: Justin C. Round's
|
|
23
73
|
* http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
|
|
24
74
|
*/
|
|
25
75
|
|
|
26
|
-
export function checkLineIntersection(p1=null, p2=null, p3=null, p4=null, exact = true, debug=false) {
|
|
76
|
+
export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, debug = false) {
|
|
27
77
|
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
|
|
28
78
|
let denominator, a, b, numerator1, numerator2;
|
|
29
79
|
let intersectionPoint = {}
|
|
30
80
|
|
|
31
|
-
if(!p1 || !p2 || !p3 || !p4){
|
|
32
|
-
if(debug) console.warn('points missing');
|
|
81
|
+
if (!p1 || !p2 || !p3 || !p4) {
|
|
82
|
+
if (debug) console.warn('points missing');
|
|
33
83
|
return false
|
|
34
84
|
}
|
|
35
85
|
|
|
@@ -39,7 +89,7 @@ export function checkLineIntersection(p1=null, p2=null, p3=null, p4=null, exact
|
|
|
39
89
|
return false;
|
|
40
90
|
}
|
|
41
91
|
} catch {
|
|
42
|
-
if(debug) console.warn('!catch', p1, p2, 'p3:', p3, 'p4:', p4);
|
|
92
|
+
if (debug) console.warn('!catch', p1, p2, 'p3:', p3, 'p4:', p4);
|
|
43
93
|
return false
|
|
44
94
|
}
|
|
45
95
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getDeltaAngle, getDistance } from "./geometry";
|
|
2
|
+
|
|
3
|
+
export function getArcFromPoly(pts) {
|
|
4
|
+
if (pts.length < 3) return false
|
|
5
|
+
|
|
6
|
+
// Pick 3 well-spaced points
|
|
7
|
+
let p1 = pts[0];
|
|
8
|
+
let p2 = pts[Math.floor(pts.length / 2)];
|
|
9
|
+
let p3 = pts[pts.length - 1];
|
|
10
|
+
|
|
11
|
+
let x1 = p1.x, y1 = p1.y;
|
|
12
|
+
let x2 = p2.x, y2 = p2.y;
|
|
13
|
+
let x3 = p3.x, y3 = p3.y;
|
|
14
|
+
|
|
15
|
+
let a = x1 - x2;
|
|
16
|
+
let b = y1 - y2;
|
|
17
|
+
let c = x1 - x3;
|
|
18
|
+
let d = y1 - y3;
|
|
19
|
+
|
|
20
|
+
let e = ((x1 * x1 - x2 * x2) + (y1 * y1 - y2 * y2)) / 2;
|
|
21
|
+
let f = ((x1 * x1 - x3 * x3) + (y1 * y1 - y3 * y3)) / 2;
|
|
22
|
+
|
|
23
|
+
let det = a * d - b * c;
|
|
24
|
+
|
|
25
|
+
if (Math.abs(det) < 1e-10) {
|
|
26
|
+
console.warn("Points are collinear or numerically unstable");
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// find center of arc
|
|
31
|
+
let cx = (d * e - b * f) / det;
|
|
32
|
+
let cy = (-c * e + a * f) / det;
|
|
33
|
+
let centroid = { x: cx, y: cy };
|
|
34
|
+
|
|
35
|
+
// Radius (use start point)
|
|
36
|
+
let r = getDistance(centroid, p1);
|
|
37
|
+
|
|
38
|
+
let angleData = getDeltaAngle(centroid, p1, p3)
|
|
39
|
+
let {deltaAngle, startAngle, endAngle} = angleData;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
centroid,
|
|
43
|
+
r,
|
|
44
|
+
startAngle,
|
|
45
|
+
endAngle,
|
|
46
|
+
deltaAngle
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
@@ -12,8 +12,9 @@ import { renderPoint, renderPath } from "./visualize";
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
import { checkLineIntersection, getAngle, getDistance, getDistAv, getSquareDistance, interpolate, pointAtT, rotatePoint } from './geometry';
|
|
15
|
+
import { checkLineIntersection, getAngle, getDeltaAngle, getDistance, getDistAv, getSquareDistance, interpolate, pointAtT, rotatePoint, toParametricAngle } from './geometry';
|
|
16
16
|
import { getPathArea, getPolygonArea, getRelativeAreaDiff } from './geometry_area';
|
|
17
|
+
import { pathDataToD } from './pathData_stringify';
|
|
17
18
|
import { roundPathData } from './rounding';
|
|
18
19
|
import { renderPoint } from './visualize';
|
|
19
20
|
|
|
@@ -24,13 +25,13 @@ export function revertCubicQuadratic(p0 = {}, cp1 = {}, cp2 = {}, p = {}) {
|
|
|
24
25
|
let cp2X = interpolate(p, cp2, 1.5)
|
|
25
26
|
|
|
26
27
|
let dist0 = getDistAv(p0, p)
|
|
27
|
-
let threshold = dist0 * 0.
|
|
28
|
+
let threshold = dist0 * 0.03;
|
|
28
29
|
let dist1 = getDistAv(cp1X, cp2X)
|
|
29
30
|
|
|
30
31
|
let cp1_Q = null;
|
|
31
32
|
let type = 'C'
|
|
32
33
|
let values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
|
|
33
|
-
let comN = {type, values}
|
|
34
|
+
let comN = { type, values }
|
|
34
35
|
|
|
35
36
|
if (dist1 && threshold && dist1 < threshold) {
|
|
36
37
|
cp1_Q = checkLineIntersection(p0, cp1, p, cp2, false);
|
|
@@ -40,6 +41,7 @@ export function revertCubicQuadratic(p0 = {}, cp1 = {}, cp2 = {}, p = {}) {
|
|
|
40
41
|
comN.values = [cp1_Q.x, cp1_Q.y, p.x, p.y];
|
|
41
42
|
comN.p0 = p0;
|
|
42
43
|
comN.cp1 = cp1_Q;
|
|
44
|
+
comN.cp2 = null;
|
|
43
45
|
comN.p = p
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -62,7 +64,7 @@ export function convertPathData(pathData, {
|
|
|
62
64
|
if (toShorthands) pathData = pathDataToShorthands(pathData);
|
|
63
65
|
|
|
64
66
|
// pre round - before relative conversion to minimize distortions
|
|
65
|
-
if(decimals
|
|
67
|
+
if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
|
|
66
68
|
if (toRelative) pathData = pathDataToRelative(pathData);
|
|
67
69
|
if (decimals > -1) pathData = roundPathData(pathData, decimals);
|
|
68
70
|
|
|
@@ -415,7 +417,7 @@ export function pathDataToShorthands(pathData, decimals = -1, test = false) {
|
|
|
415
417
|
let com = pathData[i];
|
|
416
418
|
let { type, values } = com;
|
|
417
419
|
let valuesLen = values.length;
|
|
418
|
-
let valuesLast = [values[valuesLen-2], values[valuesLen-1]];
|
|
420
|
+
let valuesLast = [values[valuesLen - 2], values[valuesLen - 1]];
|
|
419
421
|
|
|
420
422
|
// previoius command
|
|
421
423
|
let comPrev = pathData[i - 1];
|
|
@@ -602,6 +604,123 @@ export function pathDataToQuadratic(pathData, precision = 0.1) {
|
|
|
602
604
|
}
|
|
603
605
|
|
|
604
606
|
|
|
607
|
+
/**
|
|
608
|
+
* Convert a parametrized SVG arc to cubic Beziers
|
|
609
|
+
* Assumes arc parameters are already resolved
|
|
610
|
+
*/
|
|
611
|
+
export function arcToBezierResolved({
|
|
612
|
+
|
|
613
|
+
// start / end points
|
|
614
|
+
p0 = { x: 0, y: 0 },
|
|
615
|
+
p = { x: 0, y: 0 },
|
|
616
|
+
|
|
617
|
+
// center
|
|
618
|
+
centroid = { x: 0, y: 0 },
|
|
619
|
+
|
|
620
|
+
// radii
|
|
621
|
+
rx = 0,
|
|
622
|
+
ry = 0,
|
|
623
|
+
|
|
624
|
+
// SVG-style rotation
|
|
625
|
+
xAxisRotation = 0,
|
|
626
|
+
radToDegree = false,
|
|
627
|
+
|
|
628
|
+
// optional
|
|
629
|
+
startAngle = null,
|
|
630
|
+
endAngle = null,
|
|
631
|
+
deltaAngle = null
|
|
632
|
+
|
|
633
|
+
} = {}) {
|
|
634
|
+
|
|
635
|
+
if (!rx || !ry) return [];
|
|
636
|
+
|
|
637
|
+
// new pathData
|
|
638
|
+
let pathData = [];
|
|
639
|
+
|
|
640
|
+
// maximum delta for cubic approximations: Math.PI / 2 (90deg)
|
|
641
|
+
const maxSegAngle = 1.5707963267948966
|
|
642
|
+
|
|
643
|
+
// Pomax cubic constant
|
|
644
|
+
const k = 0.551785;
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
// rotation
|
|
648
|
+
let phi = radToDegree
|
|
649
|
+
? xAxisRotation
|
|
650
|
+
: xAxisRotation * Math.PI / 180;
|
|
651
|
+
|
|
652
|
+
let cosphi = Math.cos(phi);
|
|
653
|
+
let sinphi = Math.sin(phi);
|
|
654
|
+
|
|
655
|
+
// helper: transform point to ellipse local space
|
|
656
|
+
const toLocal = ({ x, y }) => {
|
|
657
|
+
const dx = x - centroid.x;
|
|
658
|
+
const dy = y - centroid.y;
|
|
659
|
+
return {
|
|
660
|
+
x: (cosphi * dx + sinphi * dy) / rx,
|
|
661
|
+
y: (-sinphi * dx + cosphi * dy) / ry
|
|
662
|
+
};
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
// derive angles if not provided
|
|
666
|
+
if (startAngle === null || endAngle === null || deltaAngle === null) {
|
|
667
|
+
({ startAngle, endAngle, deltaAngle } = getDeltaAngle(centroid, p0, p))
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
// parametrize for elliptic arcs
|
|
673
|
+
let startAngleParam = rx !== ry ? toParametricAngle(startAngle, rx, ry) : startAngle;
|
|
674
|
+
//let endAngleParam = rx !== ry ? toParametricAngle(endAngle, rx, ry) : endAngle;
|
|
675
|
+
//let deltaAngleParam = endAngleParam - startAngleParam;
|
|
676
|
+
let deltaAngleParam = rx !== ry ? toParametricAngle(deltaAngle, rx, ry) : deltaAngle;
|
|
677
|
+
|
|
678
|
+
let segments = Math.max(1, Math.ceil(Math.abs(deltaAngleParam) / maxSegAngle));
|
|
679
|
+
let angStep = deltaAngleParam / segments;
|
|
680
|
+
|
|
681
|
+
for (let i = 0; i < segments; i++) {
|
|
682
|
+
|
|
683
|
+
const a = Math.abs(angStep) === maxSegAngle ?
|
|
684
|
+
Math.sign(angStep) * k :
|
|
685
|
+
(4 / 3) * Math.tan(angStep / 4);
|
|
686
|
+
|
|
687
|
+
let cos0 = Math.cos(startAngleParam);
|
|
688
|
+
let sin0 = Math.sin(startAngleParam);
|
|
689
|
+
let cos1 = Math.cos(startAngleParam + angStep);
|
|
690
|
+
let sin1 = Math.sin(startAngleParam + angStep);
|
|
691
|
+
|
|
692
|
+
// unit arc → cubic
|
|
693
|
+
let c1 = { x: cos0 - sin0 * a, y: sin0 + cos0 * a };
|
|
694
|
+
let c2 = { x: cos1 + sin1 * a, y: sin1 - cos1 * a };
|
|
695
|
+
let e = { x: cos1, y: sin1 };
|
|
696
|
+
|
|
697
|
+
let values = [];
|
|
698
|
+
|
|
699
|
+
[c1, c2, e].forEach(pt => {
|
|
700
|
+
let x = pt.x * rx;
|
|
701
|
+
let y = pt.y * ry;
|
|
702
|
+
|
|
703
|
+
values.push(
|
|
704
|
+
cosphi * x - sinphi * y + centroid.x,
|
|
705
|
+
sinphi * x + cosphi * y + centroid.y
|
|
706
|
+
);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
pathData.push({
|
|
710
|
+
type: 'C',
|
|
711
|
+
values,
|
|
712
|
+
cp1: { x: values[0], y: values[1] },
|
|
713
|
+
cp2: { x: values[2], y: values[3] },
|
|
714
|
+
p: { x: values[4], y: values[5] },
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
startAngleParam += angStep;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
return pathData;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
|
|
605
724
|
|
|
606
725
|
/**
|
|
607
726
|
* convert arctocommands to cubic bezier
|
|
@@ -858,6 +977,221 @@ export function convertArrayPathData(pathDataArray) {
|
|
|
858
977
|
* cubics to arcs
|
|
859
978
|
*/
|
|
860
979
|
|
|
980
|
+
|
|
981
|
+
export function combineCubicsToArcs(pathData = [], {
|
|
982
|
+
threshold = 0,
|
|
983
|
+
} = {}) {
|
|
984
|
+
|
|
985
|
+
let l = pathData.length;
|
|
986
|
+
let pathDataN = [pathData[0]];
|
|
987
|
+
|
|
988
|
+
for (let i = 1; i < l; i++) {
|
|
989
|
+
let com = pathData[i];
|
|
990
|
+
let { type, cp1 = null, cp2 = null, p0, p } = com;
|
|
991
|
+
let comP = pathData[i - 1];
|
|
992
|
+
let comN = pathData[i + 1] ? pathData[i + 1] : null;
|
|
993
|
+
let comN2 = pathData[i + 2] ? pathData[i + 2] : null;
|
|
994
|
+
|
|
995
|
+
if (type === 'C' && comN && comN.type === 'C') {
|
|
996
|
+
|
|
997
|
+
let thresh = getDistAv(p0, p) * 0.02;
|
|
998
|
+
//thresh = getDistAv(p0, p) * 10000;
|
|
999
|
+
|
|
1000
|
+
let dx1 = Math.abs(p0.x - cp1.x)
|
|
1001
|
+
let dy1 = Math.abs(p0.y - cp1.y)
|
|
1002
|
+
|
|
1003
|
+
let isHorizontal1 = dy1 < thresh;
|
|
1004
|
+
let isVertical1 = dx1 < thresh;
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
let dx2 = Math.abs(comN.p0.x - comN.cp1.x)
|
|
1008
|
+
let dy2 = Math.abs(comN.p0.y - comN.cp1.y)
|
|
1009
|
+
|
|
1010
|
+
let isHorizontal2 = dy2 < thresh;
|
|
1011
|
+
let isVertical2 = dx2 < thresh;
|
|
1012
|
+
|
|
1013
|
+
//console.log(isHorizontal1, isVertical1);
|
|
1014
|
+
|
|
1015
|
+
// check angles
|
|
1016
|
+
let angleDiff1 = (isHorizontal1 || isVertical1) ? 0 : Infinity;
|
|
1017
|
+
let angleDiff2 = (isHorizontal2 || isVertical2) ? 0 : Infinity;
|
|
1018
|
+
|
|
1019
|
+
if (!isHorizontal1 && !isVertical1) {
|
|
1020
|
+
//console.log('get angles', isHorizontal1, isVertical1);
|
|
1021
|
+
let angle1 = getAngle(p0, cp1, true);
|
|
1022
|
+
let angle2 = getAngle(p, cp2, true);
|
|
1023
|
+
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
1024
|
+
angleDiff1 = Math.abs((deltaAngle % 180) - 90);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (!isHorizontal2 && !isVertical2) {
|
|
1028
|
+
//console.log('get angles', isHorizontal1, isVertical1);
|
|
1029
|
+
let angle1 = getAngle(p0, cp1, true);
|
|
1030
|
+
let angle2 = getAngle(p, cp2, true);
|
|
1031
|
+
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
1032
|
+
angleDiff2 = Math.abs((deltaAngle % 180) - 90);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
let isRightAngle1 = angleDiff1 < 3;
|
|
1037
|
+
let isRightAngle2 = angleDiff2 < 3;
|
|
1038
|
+
|
|
1039
|
+
let centroids = [];
|
|
1040
|
+
let poly = [];
|
|
1041
|
+
let rArr = []
|
|
1042
|
+
let largeArc = 0;
|
|
1043
|
+
|
|
1044
|
+
// final on path point
|
|
1045
|
+
let p_a = p
|
|
1046
|
+
|
|
1047
|
+
// 2 possible candidates - test radius
|
|
1048
|
+
if (isRightAngle1 && isRightAngle2) {
|
|
1049
|
+
//renderPoint(markers, com.p)
|
|
1050
|
+
|
|
1051
|
+
let pI = checkLineIntersection(p0, cp1, p, cp2, false);
|
|
1052
|
+
let r1 = getDistance(p0, pI);
|
|
1053
|
+
let r2 = getDistance(p, pI);
|
|
1054
|
+
let rDiff1 = Math.abs(r1 - r2)
|
|
1055
|
+
//let r = r1
|
|
1056
|
+
|
|
1057
|
+
rArr.push(r1, r2)
|
|
1058
|
+
|
|
1059
|
+
poly.push(p0, p)
|
|
1060
|
+
p_a = p
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
// 2 commands can be combined – similar radii
|
|
1064
|
+
if (rDiff1 < thresh) {
|
|
1065
|
+
|
|
1066
|
+
//renderPoint(markers, com.p)
|
|
1067
|
+
|
|
1068
|
+
// add to polygon for sweep
|
|
1069
|
+
poly.push(comN.p)
|
|
1070
|
+
|
|
1071
|
+
// update final point
|
|
1072
|
+
p_a = comN.p
|
|
1073
|
+
|
|
1074
|
+
// approximate/average final center point for final radius
|
|
1075
|
+
let cp1_r = rotatePoint(cp1, p0.x, p0.y, (Math.PI * -0.5))
|
|
1076
|
+
let cp2_r = rotatePoint(cp2, p.x, p.y, (Math.PI * 0.5))
|
|
1077
|
+
|
|
1078
|
+
let cp1_r2 = rotatePoint(comN.cp1, comN.p0.x, comN.p0.y, (Math.PI * -0.5))
|
|
1079
|
+
let cp2_r2 = rotatePoint(comN.cp2, comN.p.x, comN.p.y, (Math.PI * 0.5))
|
|
1080
|
+
|
|
1081
|
+
// assumed centroid
|
|
1082
|
+
let ptC = checkLineIntersection(p0, cp1_r, p, cp2_r, false)
|
|
1083
|
+
let ptC2 = checkLineIntersection(comN.p0, cp1_r2, comN.p, cp2_r2, false)
|
|
1084
|
+
let distC = ptC && ptC2 ? getDistAv(ptC, ptC2) : Infinity
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
// 2 commands can definitely be combined
|
|
1088
|
+
if (distC < thresh) {
|
|
1089
|
+
//renderPoint(markers, ptC, 'cyan', '1.2%', '0.5')
|
|
1090
|
+
//renderPoint(markers, ptC2, 'magenta', '0.5%', '0.5')
|
|
1091
|
+
|
|
1092
|
+
// add to centroid array
|
|
1093
|
+
centroids.push(ptC, ptC2)
|
|
1094
|
+
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
if (comN2 && comN2.type === 'C') {
|
|
1099
|
+
|
|
1100
|
+
let cp1_r3 = rotatePoint(comN2.cp1, comN2.p0.x, comN2.p0.y, (Math.PI * -0.5))
|
|
1101
|
+
let cp2_r3 = rotatePoint(comN2.cp2, comN2.p.x, comN2.p.y, (Math.PI * 0.5))
|
|
1102
|
+
let ptC3 = checkLineIntersection(comN2.p0, cp1_r3, comN2.p, cp2_r3, false)
|
|
1103
|
+
|
|
1104
|
+
let distC2 = ptC && ptC3 ? getDistAv(ptC, ptC3) : Infinity
|
|
1105
|
+
|
|
1106
|
+
// can be combined with 3rd command
|
|
1107
|
+
if (distC2 < thresh) {
|
|
1108
|
+
//renderPoint(markers, ptC3, 'green', '2%', '0.3')
|
|
1109
|
+
|
|
1110
|
+
let r3 = getDistance(ptC3, comN2.p)
|
|
1111
|
+
rArr.push(r3)
|
|
1112
|
+
|
|
1113
|
+
// update final point
|
|
1114
|
+
p_a = comN2.p
|
|
1115
|
+
poly.push(p, comN2.p)
|
|
1116
|
+
|
|
1117
|
+
largeArc = 1;
|
|
1118
|
+
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
//console.log(rDiff1, r, r1, r2);
|
|
1122
|
+
|
|
1123
|
+
} else {
|
|
1124
|
+
pathDataN.push(com)
|
|
1125
|
+
continue
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
// create new arc command
|
|
1132
|
+
if (poly.length > 1) {
|
|
1133
|
+
|
|
1134
|
+
// get average radius
|
|
1135
|
+
//rArr = rArr.sort()
|
|
1136
|
+
let rA = Math.max(...rArr)
|
|
1137
|
+
rA = rArr[0]
|
|
1138
|
+
|
|
1139
|
+
let centroidA;
|
|
1140
|
+
let xArr = centroids.map(pt => pt.x)
|
|
1141
|
+
let yArr = centroids.map(pt => pt.y)
|
|
1142
|
+
|
|
1143
|
+
centroidA = {
|
|
1144
|
+
x: (xArr.reduce((a, b) => a + b, 0)) / centroids.length,
|
|
1145
|
+
y: (yArr.reduce((a, b) => a + b, 0)) / centroids.length
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
//console.log(xArr, centroidA);
|
|
1149
|
+
|
|
1150
|
+
//rA = getDistance(p0, centroids[0])
|
|
1151
|
+
|
|
1152
|
+
rA = getDistance(p0, centroidA)
|
|
1153
|
+
let rA2 = getDistance(p, centroidA)
|
|
1154
|
+
//rA = (rA+rA2) /2
|
|
1155
|
+
//rA = Math.min(rA,rA2)
|
|
1156
|
+
|
|
1157
|
+
// rA = ((Math.min(...rArr) * 2 + Math.max(...rArr)) ) / 3
|
|
1158
|
+
//console.log(rArr, rA);
|
|
1159
|
+
|
|
1160
|
+
let area = getPolygonArea(poly, false)
|
|
1161
|
+
let sweep = area < 0 ? 0 : 1;
|
|
1162
|
+
|
|
1163
|
+
let comA = { type: 'A', values: [rA, rA, 0, largeArc, sweep, p_a.x, p_a.y], p0, p: p_a }
|
|
1164
|
+
|
|
1165
|
+
console.log('comA', comA);
|
|
1166
|
+
|
|
1167
|
+
pathDataN.push(comA)
|
|
1168
|
+
|
|
1169
|
+
i += rArr.length - 1;
|
|
1170
|
+
//i++
|
|
1171
|
+
continue
|
|
1172
|
+
/*
|
|
1173
|
+
*/
|
|
1174
|
+
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
// test angles
|
|
1181
|
+
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
pathDataN.push(com)
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
let d = pathDataToD(pathDataN)
|
|
1188
|
+
console.log(d);
|
|
1189
|
+
|
|
1190
|
+
console.log('pathDataN', pathDataN);
|
|
1191
|
+
return pathDataN
|
|
1192
|
+
|
|
1193
|
+
}
|
|
1194
|
+
|
|
861
1195
|
export function cubicCommandToArc(p0, cp1, cp2, p, tolerance = 7.5) {
|
|
862
1196
|
|
|
863
1197
|
//console.log(p0, cp1, cp2, p, segArea );
|