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.
Files changed (42) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +24 -14
  3. package/dist/svg-path-simplify.esm.js +1620 -1216
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +1620 -1215
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +298 -665
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
  9. package/dist/svg-path-simplify.poly.cjs +8 -9
  10. package/drawing.svg +62 -0
  11. package/index.html +11 -5
  12. package/package.json +1 -1
  13. package/src/index.js +3 -1
  14. package/src/pathData_get_intersections.js +777 -0
  15. package/src/pathData_offset.js +201 -0
  16. package/src/pathData_simplify_cubic.js +14 -34
  17. package/src/pathData_simplify_cubic_extrapolate.js +147 -8
  18. package/src/pathData_simplify_cubicsToArcs.js +65 -21
  19. package/src/pathSimplify-main.js +259 -60
  20. package/src/pathSimplify-presets.js +1 -0
  21. package/src/poly-fit-curve-schneider.js +171 -132
  22. package/src/poly-fit-curve-schneider_check_bulge.js +131 -0
  23. package/src/poly-offset.js +133 -0
  24. package/src/simplify_poly_RC.js +29 -7
  25. package/src/svgii/geometry.js +130 -27
  26. package/src/svgii/geometry_bbox.js +1 -1
  27. package/src/svgii/pathData_analyze.js +6 -28
  28. package/src/svgii/pathData_convert.js +70 -15
  29. package/src/svgii/pathData_remove_collinear.js +32 -36
  30. package/src/svgii/pathData_simplify_refineExtremes.js +1 -3
  31. package/src/svgii/pathData_stringify.js +132 -6
  32. package/src/svgii/pathData_toPolygon.js +33 -23
  33. package/src/svgii/poly_analyze.js +272 -125
  34. package/src/svgii/poly_analyze_cleanup.js +311 -0
  35. package/src/svgii/poly_analyze_getTangents.js +125 -0
  36. package/src/svgii/poly_analyze_get_chunks.js +3 -1
  37. package/src/svgii/poly_normalize.js +12 -0
  38. package/src/svgii/poly_to_pathdata.js +477 -8
  39. package/src/svgii/rounding.js +29 -39
  40. package/src/svgii/svg_cleanup.js +42 -54
  41. package/src/svgii/svg_cleanup_normalize_transforms.js +1 -1
  42. package/src/svgii/visualize.js +33 -2
@@ -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 ( area <= thresh && i<l-1) {
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 (ptsSmp[1], ptsSmp[ptsSmp.length-1]) * 0.005
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
  }
@@ -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) pt.cpts = [m1, m2, m3, m4];
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) pt.cpts = [m1, m2];
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
- throw Error("rx and ry can not be 0");
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) / 2;
512
- let hd_y = (y1 - y2) / 2;
513
- let hs_x = (x1 + x2) / 2;
514
- let hs_y = (y1 + y2) / 2;
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_ ** 2 + ryx1_ ** 2; // sum of square
630
+ let sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
537
631
  if (!sum_of_sq) {
538
- //console.log('error:', rx, ry, rxy1_, ryx1_);
539
- throw Error("start point can not be same as end point");
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
- return tArr;
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
- //console.log('dx', dx, dy, p1, p2);
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
 
@@ -21,7 +21,7 @@ export function analyzePathData(pathData = [], {
21
21
  detectDirection = true,
22
22
  detectSemiExtremes = false,
23
23
  debug = false,
24
- addSquareLength = true,
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) pathData = optimizeArcPathData(pathData);
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);
@@ -153,6 +158,8 @@ export function parsePathDataNormalized(d,
153
158
 
154
159
  let pathDataObj = isArray ? d : parsePathDataString(d);
155
160
 
161
+ //console.log('???', JSON.parse(JSON.stringify(pathDataObj)));
162
+
156
163
  let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
157
164
  let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
158
165
 
@@ -171,9 +178,49 @@ export function parsePathDataNormalized(d,
171
178
 
172
179
 
173
180
  /**
174
- *
175
- * @param {*} pathData
176
- * @returns
181
+ * Converts minified arc
182
+ * values to true rx and ry values
183
+ */
184
+ export function pathDataToTrueArcValues(pathData = []) {
185
+
186
+ //return pathData
187
+
188
+ let remove = []
189
+ let l = pathData.length;
190
+ let pathDataN = [pathData[0]];
191
+
192
+ for (let i = 1; i < l; i++) {
193
+ let com = pathData[i];
194
+ let comPrev = pathData[i-1];
195
+ let { type, values } = com;
196
+
197
+
198
+ if (type === 'A') {
199
+
200
+ // previous commands final on-path point
201
+ let [x1, y1] = comPrev.values.slice(-2)
202
+ let [rx, ry, xAxisRotation, largeArc, sweep, x2, y2] = values;
203
+ let arcData = svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2)
204
+
205
+ // set true arc values
206
+ com.values[0] = arcData.rx
207
+ com.values[1] = arcData.ry
208
+ //pathDataN.push(com)
209
+ }
210
+
211
+ pathDataN.push(com)
212
+
213
+
214
+ }
215
+ return pathDataN;
216
+ }
217
+
218
+
219
+ /**
220
+ * Minify arc radii
221
+ * for semi circles
222
+ * returns smaller rx and ry
223
+ * values
177
224
  */
178
225
 
179
226
  export function optimizeArcPathData(pathData = []) {
@@ -196,6 +243,11 @@ export function optimizeArcPathData(pathData = []) {
196
243
 
197
244
  let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
198
245
  let comPrev = pathData[i - 1]
246
+
247
+ // force absolute
248
+ rx = Math.abs(rx);
249
+ ry = Math.abs(ry);
250
+
199
251
  let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
200
252
  let M = { x: x0, y: y0 };
201
253
  let p = { x, y };
@@ -240,7 +292,7 @@ export function optimizeArcPathData(pathData = []) {
240
292
  if (isHorizontal || isVertical) {
241
293
 
242
294
  // check if semi circle
243
- let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
295
+ let needsTrueR = isHorizontal ? rx * 1.9 > diffX : ry * 1.9 > diffY;
244
296
 
245
297
  // is semicircle we can simplify rx
246
298
  if (!needsTrueR) {
@@ -1037,8 +1089,8 @@ export function arcToBezier(p0, values, splitSegments = 1) {
1037
1089
  let phi = rotation ? rotation * TAU / 360 : 0;
1038
1090
  let sinphi = phi ? Math.sin(phi) : 0
1039
1091
  let cosphi = phi ? Math.cos(phi) : 1
1040
- let pxp = cosphi * (p0.x - x) / 2 + sinphi * (p0.y - y) / 2
1041
- let pyp = -sinphi * (p0.x - x) / 2 + cosphi * (p0.y - y) / 2
1092
+ let pxp = cosphi * (p0.x - x) *0.5 + sinphi * (p0.y - y) *0.5
1093
+ let pyp = -sinphi * (p0.x - x) *0.5 + cosphi * (p0.y - y) *0.5
1042
1094
 
1043
1095
  if (pxp === 0 && pyp === 0) {
1044
1096
  return []
@@ -1074,8 +1126,8 @@ export function arcToBezier(p0, values, splitSegments = 1) {
1074
1126
 
1075
1127
  let centerxp = radicant ? radicant * rx / ry * pyp : 0
1076
1128
  let centeryp = radicant ? radicant * -ry / rx * pxp : 0
1077
- let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x) / 2
1078
- let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y) / 2
1129
+ let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x) * 0.5
1130
+ let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y) * 0.5
1079
1131
 
1080
1132
  let vx1 = (pxp - centerxp) / rx
1081
1133
  let vy1 = (pyp - centeryp) / ry
@@ -1097,18 +1149,20 @@ export function arcToBezier(p0, values, splitSegments = 1) {
1097
1149
  ang2 = vectorAngle(vx1, vy1, vx2, vy2)
1098
1150
 
1099
1151
  if (sweepFlag === 0 && ang2 > 0) {
1100
- ang2 -= Math.PI * 2
1152
+ //ang2 -= Math.PI * 2
1153
+ ang2 -= TAU
1101
1154
  }
1102
1155
  else if (sweepFlag === 1 && ang2 < 0) {
1103
- ang2 += Math.PI * 2
1156
+ //ang2 += Math.PI * 2
1157
+ ang2 += TAU
1104
1158
  }
1105
1159
 
1106
1160
 
1107
1161
  //ratio must be at least 1
1108
- let ratio = +(Math.abs(ang2) / (TAU / 4)).toFixed(0) || 1
1162
+ let ratio = +(Math.abs(ang2) / (TAU * 0.25)).toFixed(0) || 1
1109
1163
 
1110
1164
 
1111
- // increase segments for more accureate length calculations
1165
+ // increase segments for more accurate length calculations
1112
1166
  let segments = ratio * splitSegments;
1113
1167
  ang2 /= segments
1114
1168
  let pathDataArc = [];
@@ -1121,7 +1175,8 @@ export function arcToBezier(p0, values, splitSegments = 1) {
1121
1175
  const k = 0.551785
1122
1176
  let a = ang2 === angle90 ? k :
1123
1177
  (
1124
- ang2 === -angle90 ? -k : 4 / 3 * Math.tan(ang2 / 4)
1178
+ //ang2 === -angle90 ? -k : 4 / 3 * Math.tan(ang2 / 4)
1179
+ ang2 === -angle90 ? -k : 1.33333 * Math.tan(ang2 * 0.25)
1125
1180
  );
1126
1181
 
1127
1182
  let cos2 = ang2 ? Math.cos(ang2) : 1;