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
@@ -0,0 +1,201 @@
1
+ import { findPathDataIntersections } from "./pathData_get_intersections";
2
+ import { getParallelPoly } from "./poly-offset";
3
+ import { checkLineIntersection, pointAtT } from "./svgii/geometry";
4
+ import { getPolygonArea } from "./svgii/geometry_area";
5
+ import { pathDataToD } from "./svgii/pathData_stringify";
6
+ import { renderPath, renderPoint, renderPoly } from "./svgii/visualize";
7
+
8
+ export function offsetPathData(pathData = [], strokeWidth = 18, side = top) {
9
+
10
+ console.log('offsetPathData');
11
+ //alert('off')
12
+
13
+ let pathDataN = [];
14
+ let l = pathData.length;
15
+ let closed = pathData[l - 1].type.toLowerCase() === 'z'
16
+
17
+ for (let i = 0; i < l; i++) {
18
+ let comPrev = i > 0 ? pathData[i - 1] : null;
19
+ let com = pathData[i];
20
+ let { type, values, p0, cp1, cp2, p } = com;
21
+ //let comN = com
22
+
23
+ if (type === 'C') {
24
+
25
+ //split path
26
+ let segments = pointAtT([p0, cp1, cp2, p], 0.5, false, true).segments;
27
+ //console.log(segments);
28
+
29
+ let pts = [segments[0].p0];
30
+ segments.forEach(com => {
31
+ let { p0, cp1, cp2, p } = com;
32
+
33
+ pts.push(cp1, cp2, p)
34
+
35
+ })
36
+
37
+ let area = getPolygonArea(pts)
38
+ let sweep = area < 0 ? 0 : 1
39
+ let polyOffset = getParallelPoly(pts, strokeWidth, sweep, closed)
40
+ let { polyTop, polyBottom } = polyOffset
41
+ //console.log(polyOffset);
42
+
43
+ com.polyTop = polyTop
44
+ com.polyBottom = polyBottom
45
+ com.t = 0
46
+ com.segs = []
47
+ com.inter = null
48
+
49
+
50
+ // convert pts to cpts
51
+ let pathData2 = [
52
+ { type: 'M', values: [polyBottom[0].x, polyBottom[0].y] }
53
+ ]
54
+
55
+ p0 = polyBottom[0];
56
+ for (let j = 3; j < polyBottom.length; j += 3) {
57
+ let [cp1, cp2, p] = [polyBottom[j - 2], polyBottom[j - 1], polyBottom[j]]
58
+ pathData2.push(
59
+ { type: 'C', values: [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y] }
60
+ )
61
+ com.segs.push({ p0, cp1, cp2, p })
62
+ p0 = p;
63
+ }
64
+
65
+ let d = pathDataToD(pathData2);
66
+ renderPath(markers, d, '#ccc')
67
+
68
+
69
+ if (comPrev && comPrev.polyBottom) {
70
+ let polyprev = comPrev.polyBottom
71
+ console.log(comPrev);
72
+ let pt1 = polyprev[polyprev.length - 1]
73
+ let pt2 = polyprev[polyprev.length - 2]
74
+
75
+ let pt3 = polyBottom[0]
76
+ let pt4 = polyBottom[1]
77
+
78
+ let ptI = checkLineIntersection(pt4, pt3, pt2, pt1)
79
+ if (ptI) {
80
+ //console.log(ptI);
81
+ //comPrev.t = ptI.t2 * 1.2
82
+ comPrev.segs[comPrev.segs.length - 1].t = ptI.t2 * 1.2
83
+
84
+ comPrev.t = ptI.t2 * 1.2
85
+ com.t = 1 - ptI.t2 * 1.2
86
+ com.segs[0].t = 1 - ptI.t2 * 1.2
87
+
88
+ comPrev.inter = ptI
89
+ //com.inter = ptI
90
+
91
+ //console.log('com.t', com.t);
92
+ }
93
+
94
+ // adjust intersection
95
+ // comPrev.polyBottom[polyprev.length-1] = ptI
96
+
97
+ polyprev[polyprev.length - 1] = ptI
98
+ polyBottom[0] = ptI
99
+
100
+
101
+ /*
102
+ renderPoint(markers, pt1, 'purple')
103
+ renderPoint(markers, pt2, 'magenta')
104
+ renderPoint(markers, pt3, 'green')
105
+ renderPoint(markers, pt4, 'cyan')
106
+ renderPoint(markers, ptI, 'red')
107
+ */
108
+
109
+ }
110
+
111
+
112
+
113
+ }
114
+
115
+ //pathDataN.push(comN)
116
+
117
+ }
118
+
119
+
120
+ //console.log(pathData);
121
+
122
+ // convert pts to cpts
123
+ /*
124
+ pathDataN = [
125
+ { type: 'M', values: [pathData[1].polyBottom[0].x, pathData[1].polyBottom[0].y] }
126
+ ]
127
+ */
128
+
129
+ pathDataN = [
130
+ { type: 'M', values: [pathData[0].p0.x, pathData[0].p0.y] }
131
+ ]
132
+
133
+
134
+ console.log(pathData);
135
+
136
+ for (let i = 1; i < l; i++) {
137
+ let com = pathData[i];
138
+ let comPrev = pathData[i - 1];
139
+ let comN = pathData[i+1] ? pathData[i+1] : null;
140
+ let { type, values, p0 = null, cp1 = null, cp2 = null, p, polyTop = null, polyBottom = null, segs = [], inter = null, t = 0 } = com;
141
+
142
+ // has intersection
143
+ if (inter) {
144
+
145
+ let s = t > 0.5 ? segs[0] : segs[1]
146
+ let s2 = t > 0.5 ? comN.segs[1] : comN.segs[0]
147
+ //let s2 =
148
+
149
+ console.log('segs', i, segs, s);
150
+
151
+ let pd1 = [
152
+ { type: 'M', values: [s.p0.x, s.p0.y] },
153
+ { type: 'C', values: [s.cp1.x, s.cp1.y, s.cp2.x, s.cp2.y, s.p.x, s.p.y] }
154
+ ]
155
+
156
+ let pd2 = [
157
+ { type: 'M', values: [s2.p0.x, s2.p0.y] },
158
+ { type: 'C', values: [s2.cp1.x, s2.cp1.y, s2.cp2.x, s2.cp2.y, s2.p.x, s2.p.y] }
159
+ ]
160
+
161
+
162
+ let d1 = pathDataToD(pd1);
163
+ renderPath(markers, d1, 'purple')
164
+
165
+ let d2 = pathDataToD(pd2);
166
+ renderPath(markers, d2, 'orange')
167
+
168
+
169
+ // find intersections
170
+ let intersections = findPathDataIntersections(pd1, pd2)
171
+ console.log('inter', intersections);
172
+
173
+ intersections.forEach(int=>{
174
+ let ptI1=pointAtT(int.cpts1, int.t1)
175
+ let ptI2=pointAtT(int.cpts2, int.t2)
176
+
177
+ renderPoint(markers, ptI1, 'orange', '2%')
178
+ renderPoint(markers, ptI2, 'red', '1%')
179
+
180
+
181
+ })
182
+
183
+
184
+ //renderPoint(markers, inter)
185
+
186
+ }
187
+
188
+ pathDataN.push(com)
189
+
190
+
191
+ }
192
+
193
+
194
+ //let d = pathDataToD(pathDataN);
195
+ //renderPath(markers, d, 'red')
196
+ //console.log(d);
197
+
198
+
199
+
200
+ return pathDataN
201
+ }
@@ -1,7 +1,8 @@
1
1
  import { getCombinedByDominant } from "./pathData_simplify_cubic_extrapolate";
2
- import { getDistance, getSquareDistance, checkLineIntersection, pointAtT, interpolate, getDistManhattan } from "./svgii/geometry";
2
+ import { getDistance, getSquareDistance, checkLineIntersection, pointAtT, interpolate, getDistManhattan, getPerpendicularPoint } from "./svgii/geometry";
3
3
  import { getBezierArea, getPolygonArea } from "./svgii/geometry_area";
4
- import { renderPoint } from "./svgii/visualize";
4
+ import { pathDataToD } from "./svgii/pathData_stringify";
5
+ import { renderPath, renderPoint, renderPoly } from "./svgii/visualize";
5
6
 
6
7
 
7
8
 
@@ -43,10 +44,13 @@ export function simplifyPathDataCubic(pathData, {
43
44
 
44
45
  // try simplification
45
46
  else {
47
+
48
+
46
49
  //renderPoint(markers, p, 'magenta', '1%')
47
50
  let combined = combineCubicPairs(com, comN, { tolerance })
48
51
  let error = 0;
49
52
 
53
+
50
54
  // combining successful! try next segment
51
55
  if (combined.length === 1) {
52
56
  com = combined[0]
@@ -134,12 +138,13 @@ export function combineCubicPairs(com1, com2, {
134
138
  // quit if t is start
135
139
  if (!t) return commands;
136
140
 
141
+ // get averaged threshold
137
142
  let distAv1 = getDistManhattan(com1.p0, com1.p);
138
143
  let distAv2 = getDistManhattan(com2.p0, com2.p);
139
144
  let distMin = Math.max(0, Math.min(distAv1, distAv2))
140
145
 
141
-
142
- let distScale = 0.08
146
+ //let distScale = 0.08
147
+ let distScale = 0.075
143
148
  let maxDist = distMin * distScale * tolerance
144
149
 
145
150
  // get hypothetical combined command
@@ -148,7 +153,6 @@ export function combineCubicPairs(com1, com2, {
148
153
  // test new point-at-t against original mid segment starting point
149
154
  let ptI = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t)
150
155
 
151
-
152
156
  let dist0 = getDistManhattan(com1.p, ptI)
153
157
  let dist1 = 0, dist2 = 0;
154
158
  let close = dist0 < maxDist;
@@ -157,7 +161,6 @@ export function combineCubicPairs(com1, com2, {
157
161
  // collect error data
158
162
  let error = dist0;
159
163
 
160
-
161
164
  if (close) {
162
165
 
163
166
  /**
@@ -173,7 +176,6 @@ export function combineCubicPairs(com1, com2, {
173
176
  let ptI_seg1 = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t2)
174
177
  dist1 = getDistManhattan(ptM_seg1, ptI_seg1)
175
178
 
176
-
177
179
  error += dist1;
178
180
 
179
181
  if (dist1 < maxDist) {
@@ -190,17 +192,6 @@ export function combineCubicPairs(com1, com2, {
190
192
 
191
193
  if (error < maxDist) success = true;
192
194
 
193
-
194
- /*
195
- renderPoint(markers, ptM_seg1, 'cyan')
196
- renderPoint(markers, pt, 'orange', '1.5%', '1')
197
- renderPoint(markers, ptM_seg2, 'orange')
198
-
199
- renderPoint(markers, com1.p, 'green')
200
- //renderPoint(markers, com2.p, 'green')
201
- renderPoint(markers, ptI_seg1, 'purple')
202
- */
203
-
204
195
  }
205
196
 
206
197
  } // end 1st try
@@ -221,21 +212,6 @@ export function combineCubicPairs(com1, com2, {
221
212
  comS.directionChange = com2.directionChange;
222
213
  comS.corner = com2.corner;
223
214
 
224
-
225
- if (comS.extreme || comS.corner) {
226
- //renderPoint(markers, comS.p)
227
- }
228
-
229
- /*
230
- comS.extreme = com1.extreme;
231
- comS.directionChange = com1.directionChange;
232
- comS.corner = com1.corner;
233
- */
234
-
235
-
236
-
237
- //comS.directionChange = com1.directionChange ? true : (com2.directionChange);
238
-
239
215
  comS.values = [comS.cp1.x, comS.cp1.y, comS.cp2.x, comS.cp2.y, comS.p.x, comS.p.y]
240
216
 
241
217
  // relative error
@@ -308,6 +284,7 @@ export function findSplitT(com1, com2) {
308
284
  // distances between 1st and 2nd segment cpt to mid point
309
285
  let l1 = getDistManhattan(com1.cp2, com1.p)
310
286
 
287
+
311
288
  // exit for zero length control point vectors
312
289
  if (l1 === 0) {
313
290
  //console.log('!quit1');
@@ -331,5 +308,8 @@ export function findSplitT(com1, com2) {
331
308
  }
332
309
  */
333
310
 
334
- return l1 / l3
311
+ let t = l1 / l3;
312
+ //console.log('t', t);
313
+
314
+ return t;
335
315
  }
@@ -1,10 +1,147 @@
1
- import { checkLineIntersection, getAngle, getDistAv, getSquareDistance, interpolate, pointAtT, rotatePoint } from "./svgii/geometry";
1
+ import { checkLineIntersection, getAngle, getDistance, getDistAv, getDistManhattan, getSquareDistance, interpolate, pointAtT, rotatePoint } from "./svgii/geometry";
2
2
  import { getPathArea } from "./svgii/geometry_area";
3
3
  import { pathDataToD } from "./svgii/pathData_stringify";
4
4
  import { renderPath, renderPoint } from "./svgii/visualize";
5
5
 
6
+
7
+
8
+
9
+
6
10
  export function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, debug = false) {
7
11
 
12
+ // if combining fails return original commands
13
+ let commands = [com1, com2]
14
+
15
+ // detect dominant
16
+ let dist1 = getDistManhattan(com1.p0, com1.p)
17
+ let dist2 = getDistManhattan(com2.p0, com2.p)
18
+ let thresh = (dist1 + dist2) * 0.5 * 0.075 * tolerance
19
+
20
+ // take longer command
21
+ let reverse = dist1 < dist2;
22
+ //let reverse = dist1 > dist2;
23
+
24
+ // backup original commands
25
+ let com1_o = JSON.parse(JSON.stringify(com1))
26
+ let com2_o = JSON.parse(JSON.stringify(com2))
27
+
28
+ // intersection of control tangents
29
+ let ptI = checkLineIntersection(com1_o.p0, com1_o.cp1, com2_o.p, com2_o.cp2, false, true)
30
+
31
+ // no intersection - we can't combine
32
+ if (!ptI) {
33
+ return commands
34
+ }
35
+
36
+ if (reverse) {
37
+ let com2_R = {
38
+ p0: { x: com1.p.x, y: com1.p.y },
39
+ cp1: { x: com1.cp2.x, y: com1.cp2.y },
40
+ cp2: { x: com1.cp1.x, y: com1.cp1.y },
41
+ p: { x: com1.p0.x, y: com1.p0.y },
42
+ }
43
+
44
+ let com1_R = {
45
+ p0: { x: com2.p.x, y: com2.p.y },
46
+ cp1: { x: com2.cp2.x, y: com2.cp2.y },
47
+ cp2: { x: com2.cp1.x, y: com2.cp1.y },
48
+ p: { x: com2.p0.x, y: com2.p0.y },
49
+ }
50
+
51
+ com1 = com1_R;
52
+ com2 = com2_R;
53
+ }
54
+
55
+
56
+ // etsimate t for extrapolation
57
+ let PtI = checkLineIntersection(com1.cp2, com1.p, com2.p, com2.cp2, false, true)
58
+ let cp1_I = interpolate(com1.p, PtI, 0.666)
59
+
60
+ //renderPoint(markers, cp1_I)
61
+ //renderPoint(markers, com1.p0, 'green', '2%')
62
+
63
+ let dist1_2 = getDistManhattan(com1.cp2, com1.p)
64
+ let dist2_2 = getDistManhattan(com1.cp2, cp1_I)
65
+ let t = dist2_2 / dist1_2
66
+
67
+ // extrapolate
68
+ let segs = pointAtT([com1.p0, com1.cp1, com1.cp2, com1.p], t, false, true).segments
69
+
70
+ //console.log(segs);
71
+ let seg = segs[0]
72
+
73
+
74
+ // if it worked - points should be nearby
75
+ let dist = getDistManhattan(seg.p, com2.p)
76
+
77
+ // if close enough adjust
78
+ if (dist < thresh) {
79
+ let angle = getAngle(seg.p, seg.cp2)
80
+ let angle2 = getAngle(com2.p, com2.cp2)
81
+ //let angleDiff = Math.abs(angle2 - angle)
82
+ let angleDiff = (angle2 - angle)
83
+
84
+ //let angleRat = angle / angle2;
85
+ //angleRat = (1-angleDiff) / angle2;
86
+
87
+ // adjust cp angle
88
+ seg.cp2 = rotatePoint(seg.cp2, seg.p.x, seg.p.y, angleDiff)
89
+ let dist1 = getDistManhattan(seg.p, seg.cp2)
90
+
91
+ // copy original final point coordinates
92
+ seg.p = com2.p
93
+
94
+ // after rotation
95
+ let dist2 = getDistManhattan(seg.p, seg.cp2)
96
+ let scale = dist2 / dist1
97
+
98
+ // adjust tangent length
99
+ seg.cp2 = interpolate(seg.p, seg.cp2, scale)
100
+
101
+ // reverse back
102
+ if (reverse) {
103
+ seg = {
104
+ p0: seg.p,
105
+ p: seg.p0,
106
+ cp1: seg.cp2,
107
+ cp2: seg.cp1,
108
+ }
109
+ }
110
+
111
+ commands = [
112
+ {
113
+ type: 'C',
114
+ values: [seg.cp1.x, seg.cp1.y, seg.cp2.x, seg.cp2.y, seg.p.x, seg.p.y],
115
+ p0: seg.p0,
116
+ cp1: seg.cp1,
117
+ cp2: seg.cp2,
118
+ p: seg.p,
119
+ extreme: com2_o.extreme,
120
+ corner: com2_o.corner,
121
+ directionChange: com2_o.directionChange,
122
+ dimA: getDistManhattan(seg.p0, seg.p),
123
+ error: dist
124
+
125
+ }
126
+ ];
127
+
128
+ }
129
+
130
+ //console.log('commands', reverse, commands);
131
+ return commands
132
+
133
+ }
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+ export function getCombinedByDominant_back(com1, com2, maxDist = 0, tolerance = 1, debug = false) {
142
+
143
+ //console.log('getCombinedByDominant');
144
+
8
145
  // cubic Bézier derivative
9
146
  const cubicDerivative = (p0, p1, p2, p3, t) => {
10
147
  let mt = 1 - t;
@@ -25,8 +162,8 @@ export function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, de
25
162
  let commands = [com1, com2]
26
163
 
27
164
  // detect dominant
28
- let dist1 = getDistAv(com1.p0, com1.p)
29
- let dist2 = getDistAv(com2.p0, com2.p)
165
+ let dist1 = getDistManhattan(com1.p0, com1.p)
166
+ let dist2 = getDistManhattan(com2.p0, com2.p)
30
167
 
31
168
  let reverse = dist1 > dist2;
32
169
 
@@ -91,10 +228,12 @@ export function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, de
91
228
  let dP = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, t0);
92
229
  let r = sub(P, com1.p0);
93
230
 
94
- //let t0_2 = t0 - dot(r, dP) / dot(dP, dP);
95
231
 
232
+ //let t0_2 = t0 - dot(r, dP) / dot(dP, dP);
96
233
  t0 -= dot(r, dP) / dot(dP, dP);
97
234
 
235
+ //console.log(t0, t2);
236
+
98
237
  // construct merged cubic over [t0, 1]
99
238
  let Q0 = pointAtT([com2.p0, com2.cp1, com2.cp2, com2.p], t0);
100
239
  let Q3 = com2.p;
@@ -145,8 +284,8 @@ export function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, de
145
284
 
146
285
  //let tscale =(1 + t0)
147
286
  //console.log('tscale', tscale);
148
- let cp1_2 = interpolate(result.p0, ptI_1, 1.333 )
149
- let cp2_2 = interpolate(result.p, ptI_2, 1.333 )
287
+ let cp1_2 = interpolate(result.p0, ptI_1, 1.333)
288
+ let cp2_2 = interpolate(result.p, ptI_2, 1.333)
150
289
 
151
290
  // test self intersections and exit
152
291
  let cp_intersection = checkLineIntersection(com1_o.p0, cp1_2, com2_o.p, cp2_2, true)
@@ -203,7 +342,7 @@ export function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, de
203
342
  //console.log('distS', distS, maxDist );
204
343
 
205
344
  // not close enough - exit
206
- if (distSplit > maxDist * tolerance ) {
345
+ if (distSplit > maxDist * tolerance) {
207
346
  //renderPoint(markers, ptSplit, 'cyan', '1%')
208
347
  //renderPoint(markers, com1.p, 'red', '0.5%')
209
348
  return commands;
@@ -241,7 +380,7 @@ export function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, de
241
380
  if (areaDiff < 0.05 * tolerance) {
242
381
  commands = [result];
243
382
  //console.log('areaDiff', areaDiff);
244
- }
383
+ }
245
384
  }
246
385
 
247
386
 
@@ -1,5 +1,6 @@
1
- import { checkLineIntersection, getAngle, getDistManhattan, getDistance, rotatePoint } from "./svgii/geometry";
1
+ import { checkLineIntersection, getAngle, getAngleFromDelta, getDistManhattan, getDistance, rotatePoint } from "./svgii/geometry";
2
2
  import { getPathArea, getPolygonArea, getRelativeAreaDiff } from "./svgii/geometry_area";
3
+ import { renderPoint } from "./svgii/visualize";
3
4
  //import { cubicCommandToArc } from "./svgii/pathData_convert";
4
5
 
5
6
  export function pathDataCubicsToArc(pathData, { areaThreshold = 2.5 } = {}) {
@@ -14,7 +15,7 @@ export function pathDataCubicsToArc(pathData, { areaThreshold = 2.5 } = {}) {
14
15
  let comAN = cubicCommandToArc(comN.p0, comN.cp1, comN.cp2, comN.p, areaThreshold)
15
16
 
16
17
 
17
- if (comA.isArc && comAN.isArc) {
18
+ if (comA.isArc && comAN.isArc) {
18
19
 
19
20
  let dist = getDistManhattan(p0, comN.p);
20
21
  let maxDist = dist * 0.01;
@@ -30,8 +31,18 @@ export function pathDataCubicsToArc(pathData, { areaThreshold = 2.5 } = {}) {
30
31
  let sweep = area < 0 ? 0 : 1;
31
32
 
32
33
  if (vertical || horizontal) {
34
+
33
35
  rx = Math.min(rx, comAN.rx)
34
36
  ry = Math.min(ry, comAN.ry)
37
+
38
+ let diffR = Math.abs(rx - ry) / rx;
39
+ let isSemiCircle = diffR < 0.025
40
+ //console.log('isSemiCircle', isSemiCircle, diffR);
41
+ if (isSemiCircle) {
42
+ rx = rx > 1 ? 1 : Math.min(rx, ry);
43
+ ry = rx;
44
+ }
45
+
35
46
  pathData[c] = null;
36
47
  pathData[c + 1].type = 'A';
37
48
  pathData[c + 1].values = [rx, ry, 0, 0, sweep, comN.p.x, comN.p.y];
@@ -58,40 +69,74 @@ export function cubicCommandToArc(p0, cp1, cp2, p, tolerance = 7.5) {
58
69
  let arcSegArea = 0, isArc = false
59
70
 
60
71
  // check angles
61
- let angle1 = getAngle(p0, cp1, true);
62
- let angle2 = getAngle(p, cp2, true);
72
+ let dx1 = (cp1.x - p0.x)
73
+ let dy1 = (cp1.y - p0.y)
74
+ let dx2 = (cp2.x - p.x)
75
+ let dy2 = (cp2.y - p.y)
76
+
77
+
78
+ //let angle1 = getAngle(p0, cp1, true);
79
+ //let angle2 = getAngle(p, cp2, true);
80
+
81
+ let thresh = getDistManhattan(p0, p) * 0.001
82
+
83
+ let isVertical1 = dx1 < thresh
84
+ let isVertical2 = dx2 < thresh
85
+
86
+ let isHorizontal1 = dy1 < thresh
87
+ let isHorizontal2 = dy2 < thresh
88
+
89
+ let isRightAngle = (isVertical1 || isVertical2) && (isHorizontal1 || isHorizontal2);
90
+
91
+ /*
92
+ let angle1 = getAngleFromDelta(dx1, dy1, true);
93
+ let angle2 = getAngleFromDelta(dx2, dy2, true);
63
94
  let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
64
95
 
65
96
 
66
97
  let angleDiff = Math.abs((deltaAngle % 180) - 90);
67
98
  let isRightAngle = angleDiff < 3;
99
+ */
68
100
 
69
101
 
70
102
  let rx = 0
71
103
  let ry = 0
72
- let ptC;
104
+ let ptC = p0;
105
+ let r1 = 0, r2 = 0
73
106
 
74
107
  if (isRightAngle) {
75
- // point between cps
76
-
77
-
78
- // center point
79
- let cp1_r = rotatePoint(cp1, p0.x, p0.y, (Math.PI * -0.5))
80
- let cp2_r = rotatePoint(cp2, p.x, p.y, (Math.PI * 0.5))
81
-
82
- // assumed centroid
83
- ptC = checkLineIntersection(p0, cp1_r, p, cp2_r, false)
84
- //renderPoint(markers, ptC, 'red', '1%', '0.5' )
85
-
86
-
87
108
 
109
+ if (isHorizontal1 && isVertical2) {
110
+ ptC = { x: p0.x, y: p.y }
111
+ r1 = Math.abs(p.x-p0.x)
112
+ r2 = Math.abs(p.y-p0.y)
113
+ }
114
+ else if (isHorizontal2 && isVertical1) {
115
+ ptC = { x: p.x, y: p0.y }
116
+ r2 = Math.abs(p0.x-p.x)
117
+ r1 = Math.abs(p0.y-p.y)
118
+ }
88
119
 
89
- let pI = checkLineIntersection(p0, cp1, p, cp2, false);
120
+ /*
121
+ if (r1 && r2) {
122
+ //console.log('r right', r1, r2);
123
+ }
124
+ //renderPoint(markers, ptC, 'red', '1%', '0.5')
125
+ //let pI = checkLineIntersection(p0, cp1, p, cp2, false);
126
+ */
90
127
 
91
- if (pI) {
128
+ if (r1 && r2) {
92
129
 
130
+ /*
93
131
  let r1 = getDistance(p0, pI);
94
132
  let r2 = getDistance(p, pI);
133
+ */
134
+
135
+ //r1 = getDistance(p0, pI);
136
+ //let r12 = isHorizontal1 Math.abs(dx1);
137
+ //r2 = getDistance(p, pI);
138
+ //console.log('rEx', r1, r2);
139
+
95
140
 
96
141
  let rMax = +Math.max(r1, r2).toFixed(8);
97
142
  let rMin = +Math.min(r1, r2).toFixed(8);
@@ -109,7 +154,6 @@ export function cubicCommandToArc(p0, cp1, cp2, p, tolerance = 7.5) {
109
154
  let circular = (100 / rx * Math.abs(rx - ry)) < 5;
110
155
 
111
156
  if (circular) {
112
- //rx = (rx+ry)/2
113
157
  rx = rMax
114
158
  ry = rx;
115
159
  }
@@ -136,7 +180,7 @@ export function cubicCommandToArc(p0, cp1, cp2, p, tolerance = 7.5) {
136
180
  arcSegArea = (Math.PI * (rx * ry)) / 4
137
181
 
138
182
  // subtract polygon between start, end and center point
139
- arcSegArea -= Math.abs(getPolygonArea([p0, p, pI]))
183
+ arcSegArea -= Math.abs(getPolygonArea([p0, p, ptC]))
140
184
 
141
185
  let areaDiff = getRelativeAreaDiff(comArea, arcSegArea);
142
186