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.
Files changed (48) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +9 -1
  3. package/dist/svg-path-simplify.esm.js +1649 -1205
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +1649 -1204
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +301 -637
  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 +13 -8
  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 +19 -21
  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 +267 -64
  20. package/src/pathSimplify-presets.js +1 -1
  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/svg-getAttributes.js +1 -1
  26. package/src/svgii/geometry.js +130 -27
  27. package/src/svgii/geometry_bbox.js +1 -1
  28. package/src/svgii/geometry_length.js +1 -1
  29. package/src/svgii/pathData_analyze.js +6 -28
  30. package/src/svgii/pathData_convert.js +63 -15
  31. package/src/svgii/pathData_remove_collinear.js +32 -36
  32. package/src/svgii/pathData_reorder.js +3 -2
  33. package/src/svgii/pathData_simplify_refineExtremes.js +1 -3
  34. package/src/svgii/pathData_stringify.js +132 -6
  35. package/src/svgii/pathData_toPolygon.js +33 -23
  36. package/src/svgii/poly_analyze.js +272 -125
  37. package/src/svgii/poly_analyze_cleanup.js +311 -0
  38. package/src/svgii/poly_analyze_getTangents.js +125 -0
  39. package/src/svgii/poly_analyze_get_chunks.js +3 -1
  40. package/src/svgii/poly_normalize.js +21 -8
  41. package/src/svgii/poly_to_pathdata.js +477 -8
  42. package/src/svgii/rounding.js +35 -14
  43. package/src/svgii/svg_cleanup.js +44 -51
  44. package/src/svgii/svg_cleanup_convertPathLength.js +5 -0
  45. package/src/svgii/svg_cleanup_normalize_transforms.js +2 -2
  46. package/src/svgii/visualize.js +33 -2
  47. package/v/0.4.5/index.html +1040 -0
  48. package/v/0.4.5/svg-path-simplify.js +13227 -0
@@ -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]
@@ -56,7 +60,7 @@ export function simplifyPathDataCubic(pathData, {
56
60
  error += com.error;
57
61
 
58
62
  // find next candidates
59
- for (let n = i + 1; error < tolerance && n < l; n++) {
63
+ for (let n = i + offset; error < tolerance && n < l; n++) {
60
64
  let comN = pathData[n]
61
65
 
62
66
  if (comN.type !== 'C' ||
@@ -66,6 +70,7 @@ export function simplifyPathDataCubic(pathData, {
66
70
  (keepExtremes && com.extreme)
67
71
  )
68
72
  ) {
73
+
69
74
  break
70
75
  }
71
76
 
@@ -73,6 +78,7 @@ export function simplifyPathDataCubic(pathData, {
73
78
 
74
79
  // failure - could not be combined - exit loop
75
80
  if (combined.length > 1) {
81
+ //renderPoint(markers, com.p, 'orange', '1%', '0.5')
76
82
  break
77
83
  }
78
84
 
@@ -86,6 +92,7 @@ export function simplifyPathDataCubic(pathData, {
86
92
 
87
93
  // return combined
88
94
  com = combined[0]
95
+
89
96
  }
90
97
 
91
98
  //console.log('tests', log, offset);
@@ -131,12 +138,13 @@ export function combineCubicPairs(com1, com2, {
131
138
  // quit if t is start
132
139
  if (!t) return commands;
133
140
 
141
+ // get averaged threshold
134
142
  let distAv1 = getDistManhattan(com1.p0, com1.p);
135
143
  let distAv2 = getDistManhattan(com2.p0, com2.p);
136
144
  let distMin = Math.max(0, Math.min(distAv1, distAv2))
137
145
 
138
-
139
- let distScale = 0.08
146
+ //let distScale = 0.08
147
+ let distScale = 0.075
140
148
  let maxDist = distMin * distScale * tolerance
141
149
 
142
150
  // get hypothetical combined command
@@ -145,7 +153,6 @@ export function combineCubicPairs(com1, com2, {
145
153
  // test new point-at-t against original mid segment starting point
146
154
  let ptI = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t)
147
155
 
148
-
149
156
  let dist0 = getDistManhattan(com1.p, ptI)
150
157
  let dist1 = 0, dist2 = 0;
151
158
  let close = dist0 < maxDist;
@@ -154,7 +161,6 @@ export function combineCubicPairs(com1, com2, {
154
161
  // collect error data
155
162
  let error = dist0;
156
163
 
157
-
158
164
  if (close) {
159
165
 
160
166
  /**
@@ -170,7 +176,6 @@ export function combineCubicPairs(com1, com2, {
170
176
  let ptI_seg1 = pointAtT([comS.p0, comS.cp1, comS.cp2, comS.p], t2)
171
177
  dist1 = getDistManhattan(ptM_seg1, ptI_seg1)
172
178
 
173
-
174
179
  error += dist1;
175
180
 
176
181
  if (dist1 < maxDist) {
@@ -187,17 +192,6 @@ export function combineCubicPairs(com1, com2, {
187
192
 
188
193
  if (error < maxDist) success = true;
189
194
 
190
-
191
- /*
192
- renderPoint(markers, ptM_seg1, 'cyan')
193
- renderPoint(markers, pt, 'orange', '1.5%', '1')
194
- renderPoint(markers, ptM_seg2, 'orange')
195
-
196
- renderPoint(markers, com1.p, 'green')
197
- //renderPoint(markers, com2.p, 'green')
198
- renderPoint(markers, ptI_seg1, 'purple')
199
- */
200
-
201
195
  }
202
196
 
203
197
  } // end 1st try
@@ -213,9 +207,9 @@ export function combineCubicPairs(com1, com2, {
213
207
 
214
208
  comS.dimA = getDistManhattan(comS.p0, comS.p);
215
209
  comS.type = 'C';
210
+
216
211
  comS.extreme = com2.extreme;
217
212
  comS.directionChange = com2.directionChange;
218
- //comS.directionChange = com1.directionChange ? true : (com2.directionChange);
219
213
  comS.corner = com2.corner;
220
214
 
221
215
  comS.values = [comS.cp1.x, comS.cp1.y, comS.cp2.x, comS.cp2.y, comS.p.x, comS.p.y]
@@ -290,6 +284,7 @@ export function findSplitT(com1, com2) {
290
284
  // distances between 1st and 2nd segment cpt to mid point
291
285
  let l1 = getDistManhattan(com1.cp2, com1.p)
292
286
 
287
+
293
288
  // exit for zero length control point vectors
294
289
  if (l1 === 0) {
295
290
  //console.log('!quit1');
@@ -313,5 +308,8 @@ export function findSplitT(com1, com2) {
313
308
  }
314
309
  */
315
310
 
316
- return l1 / l3
311
+ let t = l1 / l3;
312
+ //console.log('t', t);
313
+
314
+ return t;
317
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