flo-mat 4.1.2 → 4.1.4

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 (80) hide show
  1. package/__tests__/find-2-prong/get-medial/dd-get-medial-point-coeffs-same-curve.spec.ts +1 -1
  2. package/__tests__/get-mats1.ts +23 -22
  3. package/__tests__/specific-cases/specific-cases.spec.ts +1 -14
  4. package/__tests__/specific-cases/test-svgs/achispado.svg +37 -24
  5. package/browser/index.js +408 -198
  6. package/browser/index.min.js +1 -1
  7. package/node/cp-node/fs/insert-cp-node.d.ts +12 -1
  8. package/node/cp-node/fs/insert-cp-node.js +21 -6
  9. package/node/cp-node/fs/insert-cp-node.js.map +1 -1
  10. package/node/cp-node/fs/is-order-correct.d.ts +9 -1
  11. package/node/cp-node/fs/is-order-correct.js +51 -10
  12. package/node/cp-node/fs/is-order-correct.js.map +1 -1
  13. package/node/cp-node/fs/remove-cp-node.js +1 -1
  14. package/node/cp-node/fs/remove-cp-node.js.map +1 -1
  15. package/node/cp-node/stringification/from-stringifyable.js +2 -1
  16. package/node/cp-node/stringification/from-stringifyable.js.map +1 -1
  17. package/node/cp-node/stringification/to-stringifyable.js +2 -2
  18. package/node/cp-node/stringification/to-stringifyable.js.map +1 -1
  19. package/node/debug/functions/check-ordering.d.ts +4 -0
  20. package/node/debug/functions/check-ordering.js +43 -0
  21. package/node/debug/functions/check-ordering.js.map +1 -0
  22. package/node/find-2-prong/add-2-prong.js +4 -2
  23. package/node/find-2-prong/add-2-prong.js.map +1 -1
  24. package/node/find-2-prong/find-and-add-2-prongs.js +1 -1
  25. package/node/find-2-prong/find-and-add-2-prongs.js.map +1 -1
  26. package/node/find-2-prong/get-initial-bezier-pieces.js +2 -2
  27. package/node/find-2-prong/get-initial-bezier-pieces.js.map +1 -1
  28. package/node/find-3-prong/find-3-prong-for-delta3s.d.ts +1 -1
  29. package/node/find-3-prong/find-3-prong-for-delta3s.js.map +1 -1
  30. package/node/find-3-prong/find-3-prong.d.ts +1 -1
  31. package/node/find-3-prong/find-3-prong.js.map +1 -1
  32. package/node/find-3-prong/find-and-add-3-prong.js +1 -1
  33. package/node/find-3-prong/find-and-add-3-prong.js.map +1 -1
  34. package/node/find-3-prong/find-and-add-3-prongs-on-loop.d.ts +1 -1
  35. package/node/find-3-prong/find-and-add-3-prongs-on-loop.js +6 -2
  36. package/node/find-3-prong/find-and-add-3-prongs-on-loop.js.map +1 -1
  37. package/node/find-3-prong/three-prong-info.d.ts +1 -1
  38. package/node/find-mat/create-initial-cp-tree.js +2 -2
  39. package/node/find-mat/create-initial-cp-tree.js.map +1 -1
  40. package/node/find-mat/find-mat.js +2 -2
  41. package/node/find-mat/find-mat.js.map +1 -1
  42. package/node/find-mat/get-for-1-prongs-on-loop.js +3 -1
  43. package/node/find-mat/get-for-1-prongs-on-loop.js.map +1 -1
  44. package/node/mat/add-to-cp-tree.d.ts +4 -1
  45. package/node/mat/add-to-cp-tree.js +11 -4
  46. package/node/mat/add-to-cp-tree.js.map +1 -1
  47. package/node/mat/get-boundary-piece-beziers.d.ts +2 -2
  48. package/node/mat/get-boundary-piece-beziers.js +1 -1
  49. package/node/mat/get-boundary-piece-beziers.js.map +1 -1
  50. package/node/mat/get-closeby-cp-if-exist.js +2 -2
  51. package/node/mat/get-closeby-cp-if-exist.js.map +1 -1
  52. package/node/mat/{get-cp-node-to-left-or-same.d.ts → get-cp-node-neighbors.d.ts} +2 -2
  53. package/node/mat/get-cp-node-neighbors.js +31 -0
  54. package/node/mat/get-cp-node-neighbors.js.map +1 -0
  55. package/package.json +11 -11
  56. package/src/cp-node/fs/insert-cp-node.ts +22 -7
  57. package/src/cp-node/fs/is-order-correct copy.ts +60 -0
  58. package/src/cp-node/fs/is-order-correct.ts +61 -13
  59. package/src/cp-node/fs/remove-cp-node.ts +1 -1
  60. package/src/cp-node/stringification/from-stringifyable.ts +2 -1
  61. package/src/cp-node/stringification/to-stringifyable.ts +2 -2
  62. package/src/debug/functions/check-ordering.ts +44 -61
  63. package/src/find-2-prong/add-2-prong.ts +3 -1
  64. package/src/find-2-prong/find-and-add-2-prongs.ts +1 -1
  65. package/src/find-2-prong/get-initial-bezier-pieces.ts +2 -2
  66. package/src/find-3-prong/find-3-prong-for-delta3s.ts +14 -6
  67. package/src/find-3-prong/find-3-prong.ts +1 -1
  68. package/src/find-3-prong/find-and-add-3-prong.ts +1 -4
  69. package/src/find-3-prong/find-and-add-3-prongs-on-loop.ts +6 -3
  70. package/src/find-3-prong/three-prong-info.ts +1 -1
  71. package/src/find-mat/create-initial-cp-tree.ts +2 -2
  72. package/src/find-mat/find-mat.ts +3 -2
  73. package/src/find-mat/get-for-1-prongs-on-loop.ts +3 -1
  74. package/src/mat/add-to-cp-tree copy.ts +71 -0
  75. package/src/mat/add-to-cp-tree.ts +12 -4
  76. package/src/mat/get-boundary-piece-beziers.ts +2 -2
  77. package/src/mat/get-closeby-cp-if-exist.ts +2 -2
  78. package/src/mat/{get-cp-node-to-left-or-same.ts → get-cp-node-neighbors.ts} +12 -10
  79. package/node/mat/get-cp-node-to-left-or-same.js +0 -30
  80. package/node/mat/get-cp-node-to-left-or-same.js.map +0 -1
package/browser/index.js CHANGED
@@ -1903,7 +1903,7 @@ function removeCpNode(cpNode, meta) {
1903
1903
  prev.next = next;
1904
1904
  next.prev = prev;
1905
1905
  const cpTree = cpTrees.get(cpNode.pointOnShape.curve.loop);
1906
- cpTree.remove(cpNode, false);
1906
+ cpTree.remove(cpNode);
1907
1907
  }
1908
1908
 
1909
1909
 
@@ -2722,8 +2722,8 @@ function toPowerBasis_1stDerivative(ps) {
2722
2722
  function toPowerBasis3_1stDerivative(ps) {
2723
2723
  const [[x0, y0], [x1, y1], [x2, y2], [x3, y3]] = ps;
2724
2724
  return [[
2725
- 3 * ((x3 - x0) + 3 * (x1 - x2)),
2726
- 6 * ((x2 + x0) - 2 * x1),
2725
+ 3 * ((x3 - x0) + 3 * (x1 - x2)), // max === 24*max(c); max 5 bit-length increase
2726
+ 6 * ((x2 + x0) - 2 * x1), // max === 24*max(c); max 5 bit-length increase
2727
2727
  3 * (x1 - x0)
2728
2728
  ], [
2729
2729
  3 * ((y3 - y0) + 3 * (y1 - y2)),
@@ -19053,9 +19053,9 @@ function toPowerBasis(ps) {
19053
19053
  function to_power_basis_toPowerBasis3(ps) {
19054
19054
  const [[x0, y0], [x1, y1], [x2, y2], [x3, y3]] = ps;
19055
19055
  return [[
19056
- (x3 - x0) + 3 * (x1 - x2),
19057
- 3 * ((x2 + x0) - 2 * x1),
19058
- 3 * (x1 - x0),
19056
+ (x3 - x0) + 3 * (x1 - x2), // max 3 bit-length increase
19057
+ 3 * ((x2 + x0) - 2 * x1), // max 4 bit-length increase
19058
+ 3 * (x1 - x0), // max 3 bit-length increase
19059
19059
  x0
19060
19060
  ], [
19061
19061
  (y3 - y0) + 3 * (y1 - y2),
@@ -19082,10 +19082,10 @@ function to_power_basis_toPowerBasis1(ps) {
19082
19082
  const [[x0, y0], [x1, y1]] = ps;
19083
19083
  return [[
19084
19084
  x1 - x0,
19085
- x0,
19085
+ x0
19086
19086
  ], [
19087
19087
  y1 - y0,
19088
- y0,
19088
+ y0
19089
19089
  ]];
19090
19090
  }
19091
19091
  /** @internal */
@@ -23541,12 +23541,7 @@ function getCurvatureExtrema(ps) {
23541
23541
  const dp2_ = Horner(dp2, t);
23542
23542
  const p1_ = Horner(p1, t);
23543
23543
  const secondDerivative = p1_ * dp2_;
23544
- if (secondDerivative >= 0) {
23545
- minima.push(t);
23546
- }
23547
- else {
23548
- maxima.push(t);
23549
- }
23544
+ (secondDerivative >= 0 ? minima : maxima).push(t);
23550
23545
  }
23551
23546
  const inflections = roots(p1, 0, 1)?.map(r => r.t) || [];
23552
23547
  return { minima, maxima, inflections };
@@ -23558,23 +23553,6 @@ function getCurvatureExtrema(ps) {
23558
23553
 
23559
23554
 
23560
23555
  const { sqrt: curvature_sqrt } = Math;
23561
- /**
23562
- * Returns the numerator and denominator of curvature `[N,D]`, i.e. `κ = N/D`
23563
- *
23564
- * @param ps
23565
- * @param t
23566
- */
23567
- function curvatureND(ps, t) {
23568
- const [dX, dY] = toPowerBasis_1stDerivative(ps);
23569
- const [ddX, ddY] = toPowerBasis_2ndDerivative(ps);
23570
- const dx = Horner(dX, t);
23571
- const dy = Horner(dY, t);
23572
- const ddx = Horner(ddX, t);
23573
- const ddy = Horner(ddY, t);
23574
- const a = dx * ddy - dy * ddx;
23575
- const b = curvature_sqrt((dx * dx + dy * dy) ** 3);
23576
- return [a, b];
23577
- }
23578
23556
  /**
23579
23557
  * Returns the curvature `κ` of the given linear, quadratic or cubic bezier
23580
23558
  * curve at a specific given parameter value `t`.
@@ -23590,16 +23568,6 @@ function curvature(ps, t) {
23590
23568
  const [N, D] = curvatureND(ps, t);
23591
23569
  return N / D;
23592
23570
  }
23593
- /**
23594
- * Returns the radius of curvature.
23595
- *
23596
- * @param ps
23597
- * @param t
23598
- */
23599
- function radiusOfCurvature(ps, t) {
23600
- const [N, D] = curvatureND(ps, t);
23601
- return D / N;
23602
- }
23603
23571
  /**
23604
23572
  * Alias for [[κ]].
23605
23573
  *
@@ -23614,6 +23582,33 @@ function radiusOfCurvature(ps, t) {
23614
23582
  * @doc
23615
23583
  */
23616
23584
  const κ = (/* unused pure expression or super */ null && (curvature));
23585
+ /**
23586
+ * Returns the radius of curvature.
23587
+ *
23588
+ * @param ps
23589
+ * @param t
23590
+ */
23591
+ function radiusOfCurvature(ps, t) {
23592
+ const [N, D] = curvatureND(ps, t);
23593
+ return D / N;
23594
+ }
23595
+ /**
23596
+ * Returns the numerator and denominator of curvature `[N,D]`, i.e. `κ = N/D`
23597
+ *
23598
+ * @param ps
23599
+ * @param t
23600
+ */
23601
+ function curvatureND(ps, t) {
23602
+ const [dX, dY] = toPowerBasis_1stDerivative(ps);
23603
+ const [ddX, ddY] = toPowerBasis_2ndDerivative(ps);
23604
+ const dx = Horner(dX, t);
23605
+ const dy = Horner(dY, t);
23606
+ const ddx = Horner(ddX, t);
23607
+ const ddy = Horner(ddY, t);
23608
+ const a = dx * ddy - dy * ddx;
23609
+ const b = curvature_sqrt((dx * dx + dy * dy) ** 3);
23610
+ return [a, b];
23611
+ }
23617
23612
 
23618
23613
  //# sourceMappingURL=curvature.js.map
23619
23614
  ;// ./node_modules/flo-boolean/node/get-critical-points/get-excessive-curvatures.js
@@ -24900,7 +24895,7 @@ class Heap {
24900
24895
  }
24901
24896
 
24902
24897
  //# sourceMappingURL=heap.js.map
24903
- ;// ./node_modules/flo-bezier3/node/simultaneous-properties/hausdorff-distance/hausdorff-distance-one-sided.js
24898
+ ;// ./node_modules/flo-bezier3/node/simultaneous-properties/hausdorff-distance/hausdorff-distance-one-sided-extra.js
24904
24899
 
24905
24900
 
24906
24901
 
@@ -24912,26 +24907,15 @@ class Heap {
24912
24907
 
24913
24908
 
24914
24909
  /** @internal */
24915
- const { max: hausdorff_distance_one_sided_max } = Math;
24916
- // We need to calculate `H(A,B)`, the two sided Hausdorff distance between
24917
- // the bezier curves `A` and `B` which equals `max(h(A,B), h(B,A))`, where
24918
- // `h(A,B)` is the one sided Hausdorff distance from `A` to `B`
24919
- // Let: ωf(σ) = sup{ |f(t) − f(t′)| : t, t′ ∈ [a,b] with |t − t′| ≤ σ }
24920
- // where: δS = max[ (1 ≤ i ≤ M+1)(ti − ti−1) ] is the maximum distance between
24921
- // two consecutive parameter values
24922
- // Lemma 2.1:
24923
- // h(A,S) ≤ ωf(δS/2) and
24924
- // h(B,T) ≤ ωg(δT/2)
24925
- // Theorem 2.3: (both curves discretized)
24926
- // |h(S,T) − h(A,B)| ≤ max[ h(A,S), h(B,T) ]
24927
- // ≤ max[ ωf(δS/2), ωg(δT/2) ]
24928
- //
24929
- // (only A discretized)
24930
- // |h(S,B) − h(A,B)| ≤ ωf(δS/2)
24910
+ const { max: hausdorff_distance_one_sided_extra_max } = Math;
24931
24911
  /**
24932
24912
  * Calculates and returns an accurate approximation to the one-sided Hausdorff
24933
- * distance from the bezier curve `A` to the bezier curve `B`.
24913
+ * distance from the bezier curve `A` to the bezier curve `B`, together with the
24914
+ * parameter values and points on both curves where the distance is
24915
+ * (approximately) achieved.
24934
24916
  *
24917
+ * * this is the implementation backing `hausdorffDistanceOneSided`, which simply
24918
+ * returns the `.d` property of this function's result
24935
24919
  * * partially based off [Computing the Hausdorff distance between two sets of parametric curves](https://www.semanticscholar.org/paper/COMPUTING-THE-HAUSDORFF-DISTANCE-BETWEEN-TWO-SETS-Kim-McLean/d2bd6529c4b118e389e1db209d8f1bf7467f9016)
24936
24920
  *
24937
24921
  * @param A a bezier curve (the 'from' curve) given by an ordered array of its
@@ -24949,21 +24933,25 @@ const { max: hausdorff_distance_one_sided_max } = Math;
24949
24933
  *
24950
24934
  * @doc mdx
24951
24935
  */
24952
- function hausdorffDistanceOneSided(A, B, tolerance, maxIterations = 50) {
24936
+ function hausdorffDistanceOneSidedExtra(A, B, tolerance, maxIterations = 50) {
24953
24937
  if (A.length === 1) {
24954
- return closestPointOnBezier(B, A[0]).d;
24938
+ const c = closestPointOnBezier(B, A[0]);
24939
+ return { d: c.d, tA: 0, pA: A[0], tB: c.t, pB: c.p };
24955
24940
  }
24956
24941
  if (B.length === 1) {
24957
- return furthestPointOnBezier(A, B[0]).d;
24942
+ const f = furthestPointOnBezier(A, B[0]);
24943
+ return { d: f.d, tA: f.t, pA: f.p, tB: 0, pB: B[0] };
24958
24944
  }
24959
24945
  if (A.length === 2 && B.length === 2) {
24960
24946
  // Seperately handle the simple case of two lines
24961
24947
  // Find minimum distance from endpoints of A to B:
24962
- const EA0 = closestPointOnBezier(B, A[0]).d;
24963
- const EA1 = closestPointOnBezier(B, A[A.length - 1]).d;
24964
- return EA0 > EA1 ? EA0 : EA1;
24948
+ const c0 = closestPointOnBezier(B, A[0]);
24949
+ const c1 = closestPointOnBezier(B, A[A.length - 1]);
24950
+ return c0.d > c1.d
24951
+ ? { d: c0.d, tA: 0, pA: A[0], tB: c0.t, pB: c0.p }
24952
+ : { d: c1.d, tA: 1, pA: A[A.length - 1], tB: c1.t, pB: c1.p };
24965
24953
  }
24966
- const l = hausdorff_distance_one_sided_max(maxAbsCoordinate(A), maxAbsCoordinate(B));
24954
+ const l = hausdorff_distance_one_sided_extra_max(maxAbsCoordinate(A), maxAbsCoordinate(B));
24967
24955
  tolerance = tolerance || l / 1000_000;
24968
24956
  // an array of intervals
24969
24957
  const [eL, eR] = calcHErrorBound(A, 0, 1);
@@ -24976,8 +24964,8 @@ function hausdorffDistanceOneSided(A, B, tolerance, maxIterations = 50) {
24976
24964
  const heap = new Heap(hausdorffCompare);
24977
24965
  heap.insert(initialInterval);
24978
24966
  let j = 0;
24979
- let bestHUpper = Infinity;
24980
24967
  let bestHLower = -Infinity;
24968
+ let bestTA = 0; // parameter on `A` achieving the running max lower bound
24981
24969
  while (true) {
24982
24970
  const interval = heap.heap[0]; // peek max
24983
24971
  const { tS, tE, hL, hR } = interval;
@@ -24989,24 +24977,37 @@ function hausdorffDistanceOneSided(A, B, tolerance, maxIterations = 50) {
24989
24977
  const pB = closestPointOnBezier(B, pM).p;
24990
24978
  const hM = distanceBetween(pM, pB);
24991
24979
  //---------------------------------------
24992
- const h = hausdorff_distance_one_sided_max(hL, hM, hR);
24993
- if (h > bestHLower) {
24994
- bestHLower = h;
24980
+ // track *which* of the three parameters (tS, tM, tE) achieves the max
24981
+ let hBest = hL, tABest = tS;
24982
+ if (hM > hBest) {
24983
+ hBest = hM;
24984
+ tABest = tM;
24985
+ }
24986
+ if (hR > hBest) {
24987
+ hBest = hR;
24988
+ tABest = tE;
24989
+ }
24990
+ if (hBest > bestHLower) {
24991
+ bestHLower = hBest;
24992
+ bestTA = tABest;
24995
24993
  }
24996
- bestHUpper = getMaxHausdorff(interval);
24994
+ const bestHUpper = getMaxHausdorff(interval);
24997
24995
  if (bestHUpper - bestHLower < tolerance) {
24998
24996
  // The lower bound is by far the best approximation for difficult cases (see the paper).
24999
- return bestHLower;
24997
+ break;
25000
24998
  }
25001
24999
  if (j++ > maxIterations) {
25002
25000
  // The lower bound is by far the best approximation for difficult cases (see the paper).
25003
- return bestHLower;
25001
+ break;
25004
25002
  }
25005
25003
  const iL = { tS, tE: tM, hL, hR: hM, hEL: ELL, hER: ELR };
25006
25004
  const iR = { tS: tM, tE, hL: hM, hR, hEL: ERL, hER: ERR };
25007
25005
  heap.swapMinOrMax(iL);
25008
25006
  heap.insert(iR);
25009
25007
  }
25008
+ const pA = evalDeCasteljau(A, bestTA);
25009
+ const cB = closestPointOnBezier(B, pA);
25010
+ return { d: bestHLower, tA: bestTA, pA, tB: cB.t, pB: cB.p };
25010
25011
  }
25011
25012
  // Let: ωf(σ) = sup{ |f(t) − f(t′)| : t, t′ ∈ [a,b] with |t − t′| ≤ σ }
25012
25013
  //
@@ -25028,28 +25029,51 @@ function calcHErrorBound(A, tS, tE) {
25028
25029
  return [eL, eR];
25029
25030
  }
25030
25031
 
25031
- // Quokka tests
25032
- // const ls = [
25033
- // [[0,0],[1,1]],
25034
- // [[1,1],[2,1]],
25035
- // [[2,1],[3,2]],
25036
- // [[3,2],[4,1]],
25037
- // [[4,1],[5,0]]
25038
- // ];
25039
- // const b = [[0,0],[1,1],[4,1],[5,0]];
25040
- // ls.map(l => hausdorffDistanceOneSided(l,b));//?
25041
- // ls.map(l => hausdorffDistanceOneSided(b,l));//?
25042
- // const q = [[0,0],[1,1],[2,0]];
25043
- // const ts = Array.from({length: 4}, (_,i) => i/4);
25044
- // ts.push(1);
25045
- // const cs: number[][][] = [];
25046
- // for (let i=0; i<ts.length - 1; i++) {
25047
- // const t1 = ts[i];//?
25048
- // const t2 = ts[1+i];//?
25049
- // const c = fromTo(q, t1, t2);
25050
- // cs.push(c);
25051
- // }
25052
- // hausdorffDistanceOneSided(cs[3], q);//?
25032
+ //# sourceMappingURL=hausdorff-distance-one-sided-extra.js.map
25033
+ ;// ./node_modules/flo-bezier3/node/simultaneous-properties/hausdorff-distance/hausdorff-distance-one-sided.js
25034
+
25035
+ // We need to calculate `H(A,B)`, the two sided Hausdorff distance between
25036
+ // the bezier curves `A` and `B` which equals `max(h(A,B), h(B,A))`, where
25037
+ // `h(A,B)` is the one sided Hausdorff distance from `A` to `B`
25038
+ // Let: ωf(σ) = sup{ |f(t) − f(t′)| : t, t′ ∈ [a,b] with |t − t′| ≤ σ }
25039
+ // where: δS = max[ (1 ≤ i ≤ M+1)(ti − ti−1) ] is the maximum distance between
25040
+ // two consecutive parameter values
25041
+ // Lemma 2.1:
25042
+ // h(A,S) ≤ ωf(δS/2) and
25043
+ // h(B,T) ≤ ωg(δT/2)
25044
+ // Theorem 2.3: (both curves discretized)
25045
+ // |h(S,T) − h(A,B)| ≤ max[ h(A,S), h(B,T) ]
25046
+ // ≤ max[ ωf(δS/2), ωg(δT/2) ]
25047
+ //
25048
+ // (only A discretized)
25049
+ // |h(S,B) − h(A,B)| ≤ ωf(δS/2)
25050
+ /**
25051
+ * Calculates and returns an accurate approximation to the one-sided Hausdorff
25052
+ * distance from the bezier curve `A` to the bezier curve `B`.
25053
+ *
25054
+ * * to also obtain the parameter values and points at which the distance is
25055
+ * achieved, use `hausdorffDistanceOneSidedExtra`
25056
+ * * partially based off [Computing the Hausdorff distance between two sets of parametric curves](https://www.semanticscholar.org/paper/COMPUTING-THE-HAUSDORFF-DISTANCE-BETWEEN-TWO-SETS-Kim-McLean/d2bd6529c4b118e389e1db209d8f1bf7467f9016)
25057
+ *
25058
+ * @param A a bezier curve (the 'from' curve) given by an ordered array of its
25059
+ * control points e.g. `[[0,0],[1,1],[2,1],[2,0]]`
25060
+ * @param B a bezier curve (the 'to' curve) given by an ordered array of its
25061
+ * control points e.g. `[[0,0],[1,1],[2,1],[2,0]]`
25062
+ * @param tolerance optional; defaults to `max(maxAbsCoordinate(A),maxAbsCoordinate(B))/1000_000`;
25063
+ * if the calculated absolute error bound is less than this, the result is
25064
+ * returned; this is *not* a hard tolerance and the bound can be less
25065
+ * accurate in hard cases (due to the `maxIterations` parameter). Luckily
25066
+ * however, specifically the lower bound will be very accurate due to
25067
+ * its fast convergence in such hard cases (see the paper)
25068
+ * @param maxIterations optional; defaults to `50`; if the desired guaranteed error bound
25069
+ * has not been achieved after `maxIterations` then the result will be returned
25070
+ *
25071
+ * @doc mdx
25072
+ */
25073
+ function hausdorffDistanceOneSided(A, B, tolerance, maxIterations = 50) {
25074
+ return hausdorffDistanceOneSidedExtra(A, B, tolerance, maxIterations).d;
25075
+ }
25076
+
25053
25077
  //# sourceMappingURL=hausdorff-distance-one-sided.js.map
25054
25078
  ;// ./src/cp-node/fs/get-non-terminating-on-circle.ts
25055
25079
 
@@ -25109,24 +25133,32 @@ function getBranches(cpNode) {
25109
25133
 
25110
25134
 
25111
25135
  ;// ./node_modules/flo-ll-rb-tree/node/rb-tree/rb-tree.js
25112
- const BLACK = 1;
25113
- const RED = 0;
25114
- class Node {
25115
- datum;
25116
- color = RED;
25117
- parent;
25118
- left;
25119
- right;
25120
- constructor(datum) {
25121
- this.datum = datum;
25122
- }
25136
+
25137
+ function createNode(datum) {
25138
+ return {
25139
+ color: (/* inlined export .RED */0),
25140
+ parent: undefined,
25141
+ left: undefined,
25142
+ right: undefined,
25143
+ datum
25144
+ };
25123
25145
  }
25124
25146
  class RbTree {
25125
25147
  compare;
25126
25148
  root;
25149
+ size;
25150
+ minNode;
25151
+ maxNode;
25152
+ minNodeStale = true;
25153
+ maxNodeStale = true;
25127
25154
  constructor(compare) {
25128
25155
  this.compare = compare;
25129
25156
  this.root = undefined;
25157
+ this.size = 0;
25158
+ this.minNode = undefined;
25159
+ this.maxNode = undefined;
25160
+ this.minNodeStale = true;
25161
+ this.maxNodeStale = true;
25130
25162
  }
25131
25163
  isEmpty() {
25132
25164
  return this.root === undefined;
@@ -25143,9 +25175,12 @@ class RbTree {
25143
25175
  return undefined;
25144
25176
  }
25145
25177
  insert(datum) {
25178
+ this.minNodeStale = true;
25179
+ this.maxNodeStale = true;
25146
25180
  if (this.root === undefined) {
25147
- this.root = new Node(datum);
25148
- this.root.color = BLACK;
25181
+ this.root = createNode(datum);
25182
+ this.root.color = (/* inlined export .BLACK */1);
25183
+ this.size = 1;
25149
25184
  return;
25150
25185
  }
25151
25186
  let parent;
@@ -25159,7 +25194,7 @@ class RbTree {
25159
25194
  }
25160
25195
  node = c < 0 ? node.left : node.right;
25161
25196
  }
25162
- const inserted = new Node(datum);
25197
+ const inserted = createNode(datum);
25163
25198
  inserted.parent = parent;
25164
25199
  if (this.compare(datum, parent.datum) < 0) {
25165
25200
  parent.left = inserted;
@@ -25167,15 +25202,19 @@ class RbTree {
25167
25202
  else {
25168
25203
  parent.right = inserted;
25169
25204
  }
25205
+ this.size++;
25170
25206
  this.fixInsert(inserted);
25171
25207
  }
25172
- remove(datum, _all = false, _compareStrict) {
25208
+ remove(datum) {
25173
25209
  const node = this.find(datum);
25174
25210
  if (node === undefined) {
25175
25211
  return undefined;
25176
25212
  }
25177
25213
  const removed = node.datum;
25178
25214
  this.removeNode(node);
25215
+ this.size--;
25216
+ this.minNodeStale = true;
25217
+ this.maxNodeStale = true;
25179
25218
  return removed;
25180
25219
  }
25181
25220
  findBounds(datum) {
@@ -25194,21 +25233,116 @@ class RbTree {
25194
25233
  }
25195
25234
  return bounds;
25196
25235
  }
25197
- getMinNode(node) {
25198
- let curr = node === undefined ? this.root : node;
25236
+ getMinNode() {
25237
+ if (!this.minNodeStale) {
25238
+ return this.minNode;
25239
+ }
25240
+ let curr = this.root;
25199
25241
  while (curr && curr.left) {
25200
25242
  curr = curr.left;
25201
25243
  }
25244
+ this.minNode = curr;
25245
+ this.minNodeStale = false;
25202
25246
  return curr;
25203
25247
  }
25204
- getMaxNode(node) {
25205
- let curr = node === undefined ? this.root : node;
25248
+ getMaxNode() {
25249
+ if (!this.maxNodeStale) {
25250
+ return this.maxNode;
25251
+ }
25252
+ let curr = this.root;
25206
25253
  while (curr && curr.right) {
25207
25254
  curr = curr.right;
25208
25255
  }
25256
+ this.maxNode = curr;
25257
+ this.maxNodeStale = false;
25209
25258
  return curr;
25210
25259
  }
25260
+ /**
25261
+ * Checks that the tree satisfies the binary search tree ordering property
25262
+ * and the red-black constraints (root is black, no red node has a red
25263
+ * child, and every root-to-leaf path has the same number of black nodes).
25264
+ *
25265
+ * Returns `true` if all checks pass, `false` otherwise.
25266
+ */
25267
+ checkIntegrity() {
25268
+ if (this.root === undefined) {
25269
+ return true;
25270
+ }
25271
+ if (this.root.color !== (/* inlined export .BLACK */1)) {
25272
+ return false;
25273
+ }
25274
+ const compare = this.compare;
25275
+ // in-order predecessor for the BST ordering check
25276
+ let prev = undefined;
25277
+ // Returns the black-height of the subtree, or -1 if a violation was found.
25278
+ function check(node) {
25279
+ if (node === undefined) {
25280
+ return 1;
25281
+ }
25282
+ const { left, right } = node;
25283
+ // no red node has a red child
25284
+ if (node.color === (/* inlined export .RED */0) &&
25285
+ (left?.color === (/* inlined export .RED */0) || right?.color === (/* inlined export .RED */0))) {
25286
+ return -1;
25287
+ }
25288
+ const leftHeight = check(left);
25289
+ if (leftHeight === -1) {
25290
+ return -1;
25291
+ }
25292
+ // BST ordering (in-order traversal must be strictly increasing)
25293
+ if (prev !== undefined && compare(prev.datum, node.datum) >= 0) {
25294
+ return -1;
25295
+ }
25296
+ prev = node;
25297
+ const rightHeight = check(right);
25298
+ if (rightHeight === -1) {
25299
+ return -1;
25300
+ }
25301
+ // equal black-height on both sides
25302
+ if (leftHeight !== rightHeight) {
25303
+ return -1;
25304
+ }
25305
+ return leftHeight + (node.color === (/* inlined export .BLACK */1) ? 1 : 0);
25306
+ }
25307
+ return check(this.root) !== -1;
25308
+ }
25309
+ toStr(nodeToStrFunc) {
25310
+ let treeStr = '';
25311
+ if (this.root === undefined) {
25312
+ return treeStr;
25313
+ }
25314
+ f(this.root);
25315
+ return treeStr;
25316
+ function f(node) {
25317
+ treeStr += nodeToStrFunc(node);
25318
+ if (node.left !== undefined) {
25319
+ treeStr += '(';
25320
+ f(node.left);
25321
+ treeStr += ')';
25322
+ }
25323
+ if (node.right !== undefined) {
25324
+ treeStr += '[';
25325
+ f(node.right);
25326
+ treeStr += ']';
25327
+ }
25328
+ }
25329
+ }
25330
+ toArr() {
25331
+ const values = [];
25332
+ f(this.root);
25333
+ return values;
25334
+ function f(node) {
25335
+ if (node === undefined) {
25336
+ return;
25337
+ }
25338
+ f(node.left);
25339
+ values.push(node.datum);
25340
+ f(node.right);
25341
+ }
25342
+ }
25211
25343
  removeNode(z) {
25344
+ this.minNodeStale = true;
25345
+ this.maxNodeStale = true;
25212
25346
  let y = z;
25213
25347
  let yOriginalColor = y.color;
25214
25348
  let x;
@@ -25224,7 +25358,10 @@ class RbTree {
25224
25358
  this.transplant(z, z.left);
25225
25359
  }
25226
25360
  else {
25227
- y = this.getMinNode(z.right);
25361
+ y = z.right;
25362
+ while (y.left) {
25363
+ y = y.left;
25364
+ }
25228
25365
  yOriginalColor = y.color;
25229
25366
  x = y.right;
25230
25367
  if (y.parent === z) {
@@ -25244,21 +25381,21 @@ class RbTree {
25244
25381
  y.left.parent = y;
25245
25382
  y.color = z.color;
25246
25383
  }
25247
- if (yOriginalColor === BLACK) {
25384
+ if (yOriginalColor === (/* inlined export .BLACK */1)) {
25248
25385
  this.fixDelete(x, xParent);
25249
25386
  }
25250
25387
  }
25251
25388
  fixInsert(z) {
25252
25389
  let node = z;
25253
- while (node.parent && node.parent.color === RED) {
25390
+ while (node.parent && node.parent.color === (/* inlined export .RED */0)) {
25254
25391
  const parent = node.parent;
25255
25392
  const grandParent = parent.parent;
25256
25393
  if (parent === grandParent.left) {
25257
25394
  const uncle = grandParent.right;
25258
- if (this.colorOf(uncle) === RED) {
25259
- parent.color = BLACK;
25260
- uncle.color = BLACK;
25261
- grandParent.color = RED;
25395
+ if (this.colorOf(uncle) === (/* inlined export .RED */0)) {
25396
+ parent.color = (/* inlined export .BLACK */1);
25397
+ uncle.color = (/* inlined export .BLACK */1);
25398
+ grandParent.color = (/* inlined export .RED */0);
25262
25399
  node = grandParent;
25263
25400
  }
25264
25401
  else {
@@ -25266,17 +25403,17 @@ class RbTree {
25266
25403
  node = parent;
25267
25404
  this.rotateLeft(node);
25268
25405
  }
25269
- node.parent.color = BLACK;
25270
- grandParent.color = RED;
25406
+ node.parent.color = (/* inlined export .BLACK */1);
25407
+ grandParent.color = (/* inlined export .RED */0);
25271
25408
  this.rotateRight(grandParent);
25272
25409
  }
25273
25410
  }
25274
25411
  else {
25275
25412
  const uncle = grandParent.left;
25276
- if (this.colorOf(uncle) === RED) {
25277
- parent.color = BLACK;
25278
- uncle.color = BLACK;
25279
- grandParent.color = RED;
25413
+ if (this.colorOf(uncle) === (/* inlined export .RED */0)) {
25414
+ parent.color = (/* inlined export .BLACK */1);
25415
+ uncle.color = (/* inlined export .BLACK */1);
25416
+ grandParent.color = (/* inlined export .RED */0);
25280
25417
  node = grandParent;
25281
25418
  }
25282
25419
  else {
@@ -25284,43 +25421,43 @@ class RbTree {
25284
25421
  node = parent;
25285
25422
  this.rotateRight(node);
25286
25423
  }
25287
- node.parent.color = BLACK;
25288
- grandParent.color = RED;
25424
+ node.parent.color = (/* inlined export .BLACK */1);
25425
+ grandParent.color = (/* inlined export .RED */0);
25289
25426
  this.rotateLeft(grandParent);
25290
25427
  }
25291
25428
  }
25292
25429
  }
25293
- this.root.color = BLACK;
25430
+ this.root.color = (/* inlined export .BLACK */1);
25294
25431
  }
25295
25432
  fixDelete(x, parent) {
25296
25433
  let node = x;
25297
25434
  let nodeParent = parent;
25298
- while (node !== this.root && this.colorOf(node) === BLACK) {
25435
+ while (node !== this.root && this.colorOf(node) === (/* inlined export .BLACK */1)) {
25299
25436
  if (nodeParent === undefined) {
25300
25437
  break;
25301
25438
  }
25302
25439
  if (node === nodeParent?.left) {
25303
25440
  let sibling = nodeParent.right;
25304
- if (this.colorOf(sibling) === RED) {
25305
- sibling.color = BLACK;
25306
- nodeParent.color = RED;
25441
+ if (this.colorOf(sibling) === (/* inlined export .RED */0)) {
25442
+ sibling.color = (/* inlined export .BLACK */1);
25443
+ nodeParent.color = (/* inlined export .RED */0);
25307
25444
  this.rotateLeft(nodeParent);
25308
25445
  sibling = nodeParent.right;
25309
25446
  }
25310
- if (this.colorOf(sibling?.left) === BLACK && this.colorOf(sibling?.right) === BLACK) {
25447
+ if (this.colorOf(sibling?.left) === (/* inlined export .BLACK */1) && this.colorOf(sibling?.right) === (/* inlined export .BLACK */1)) {
25311
25448
  if (sibling) {
25312
- sibling.color = RED;
25449
+ sibling.color = (/* inlined export .RED */0);
25313
25450
  }
25314
25451
  node = nodeParent;
25315
25452
  nodeParent = node?.parent;
25316
25453
  }
25317
25454
  else {
25318
- if (this.colorOf(sibling?.right) === BLACK) {
25455
+ if (this.colorOf(sibling?.right) === (/* inlined export .BLACK */1)) {
25319
25456
  if (sibling?.left) {
25320
- sibling.left.color = BLACK;
25457
+ sibling.left.color = (/* inlined export .BLACK */1);
25321
25458
  }
25322
25459
  if (sibling) {
25323
- sibling.color = RED;
25460
+ sibling.color = (/* inlined export .RED */0);
25324
25461
  this.rotateRight(sibling);
25325
25462
  }
25326
25463
  sibling = nodeParent.right;
@@ -25328,9 +25465,9 @@ class RbTree {
25328
25465
  if (sibling) {
25329
25466
  sibling.color = nodeParent.color;
25330
25467
  }
25331
- nodeParent.color = BLACK;
25468
+ nodeParent.color = (/* inlined export .BLACK */1);
25332
25469
  if (sibling?.right) {
25333
- sibling.right.color = BLACK;
25470
+ sibling.right.color = (/* inlined export .BLACK */1);
25334
25471
  }
25335
25472
  this.rotateLeft(nodeParent);
25336
25473
  node = this.root;
@@ -25339,26 +25476,26 @@ class RbTree {
25339
25476
  }
25340
25477
  else {
25341
25478
  let sibling = nodeParent?.left;
25342
- if (this.colorOf(sibling) === RED) {
25343
- sibling.color = BLACK;
25344
- nodeParent.color = RED;
25479
+ if (this.colorOf(sibling) === (/* inlined export .RED */0)) {
25480
+ sibling.color = (/* inlined export .BLACK */1);
25481
+ nodeParent.color = (/* inlined export .RED */0);
25345
25482
  this.rotateRight(nodeParent);
25346
25483
  sibling = nodeParent.left;
25347
25484
  }
25348
- if (this.colorOf(sibling?.left) === BLACK && this.colorOf(sibling?.right) === BLACK) {
25485
+ if (this.colorOf(sibling?.left) === (/* inlined export .BLACK */1) && this.colorOf(sibling?.right) === (/* inlined export .BLACK */1)) {
25349
25486
  if (sibling) {
25350
- sibling.color = RED;
25487
+ sibling.color = (/* inlined export .RED */0);
25351
25488
  }
25352
25489
  node = nodeParent;
25353
25490
  nodeParent = nodeParent?.parent;
25354
25491
  }
25355
25492
  else {
25356
- if (this.colorOf(sibling?.left) === BLACK) {
25493
+ if (this.colorOf(sibling?.left) === (/* inlined export .BLACK */1)) {
25357
25494
  if (sibling?.right) {
25358
- sibling.right.color = BLACK;
25495
+ sibling.right.color = (/* inlined export .BLACK */1);
25359
25496
  }
25360
25497
  if (sibling) {
25361
- sibling.color = RED;
25498
+ sibling.color = (/* inlined export .RED */0);
25362
25499
  this.rotateLeft(sibling);
25363
25500
  }
25364
25501
  sibling = nodeParent?.left;
@@ -25366,9 +25503,9 @@ class RbTree {
25366
25503
  if (sibling) {
25367
25504
  sibling.color = nodeParent.color;
25368
25505
  }
25369
- nodeParent.color = BLACK;
25506
+ nodeParent.color = (/* inlined export .BLACK */1);
25370
25507
  if (sibling?.left) {
25371
- sibling.left.color = BLACK;
25508
+ sibling.left.color = (/* inlined export .BLACK */1);
25372
25509
  }
25373
25510
  this.rotateRight(nodeParent);
25374
25511
  node = this.root;
@@ -25377,7 +25514,7 @@ class RbTree {
25377
25514
  }
25378
25515
  }
25379
25516
  if (node) {
25380
- node.color = BLACK;
25517
+ node.color = (/* inlined export .BLACK */1);
25381
25518
  }
25382
25519
  }
25383
25520
  transplant(u, v) {
@@ -25433,7 +25570,7 @@ class RbTree {
25433
25570
  x.parent = y;
25434
25571
  }
25435
25572
  colorOf(node) {
25436
- return node ? node.color : BLACK;
25573
+ return node ? node.color : (/* inlined export .BLACK */1);
25437
25574
  }
25438
25575
  }
25439
25576
 
@@ -26706,7 +26843,7 @@ function find3ProngForDelta3s(δs, idx, k, curvePiecess, maxCoordPowerOf2) {
26706
26843
  ;// ./src/mat/get-boundary-piece-beziers.ts
26707
26844
 
26708
26845
  /**
26709
- * Returns the ordered bezier pieces between from the given boundary piece.
26846
+ * Returns the ordered bezier pieces between the two given `CpNode`s.
26710
26847
  *
26711
26848
  * @param cpNodes - An ordered pair that represents the start and end points of
26712
26849
  * the boundary piece
@@ -26904,7 +27041,7 @@ function find3Prong(δs, maxCoordPowerOf2) {
26904
27041
  }
26905
27042
 
26906
27043
 
26907
- ;// ./src/mat/get-cp-node-to-left-or-same.ts
27044
+ ;// ./src/mat/get-cp-node-neighbors.ts
26908
27045
  /**
26909
27046
  * @internal
26910
27047
  *
@@ -26918,50 +27055,107 @@ function find3Prong(δs, maxCoordPowerOf2) {
26918
27055
  * @param order
26919
27056
  * @param order2
26920
27057
  */
26921
- function getCpNodeToLeftOrSame(cpTree, pos, order, order2) {
27058
+ function getCpNodeNeighbors(cpTree, pos, order, order2) {
26922
27059
  const pointOnShape = { ...pos, order, order2 };
26923
27060
  const cps = cpTree.findBounds({ pointOnShape });
26924
- if (cps[0] === undefined && cps[1] === undefined) {
27061
+ const [cp0, cp1] = cps;
27062
+ if (cp0 === undefined && cp1 === undefined) {
26925
27063
  // The tree is still empty
26926
27064
  return undefined;
26927
27065
  }
26928
- if (cps[0] === undefined || cps[1] === undefined) {
26929
- // Smaller than all -> cptree.min() === cps[1].data OR
26930
- // Larger than all -> cptree.max() === cps[0].data
26931
- // return cpTree.max(cpTree.root);
26932
- return cpTree.getMaxNode()?.datum;
27066
+ if (cp0 === undefined || cp1 === undefined) {
27067
+ // Smaller than all OR larger than all
27068
+ const minNode = cpTree.getMinNode()?.datum;
27069
+ const maxNode = cpTree.getMaxNode()?.datum;
27070
+ return [maxNode, minNode];
26933
27071
  }
26934
- return cps[0].datum;
27072
+ return [cp0.datum, cp1.datum];
26935
27073
  }
26936
27074
 
26937
27075
 
26938
27076
  ;// ./src/cp-node/fs/is-order-correct.ts
26939
27077
 
26940
- function isOrderCorrect(cpTree, pos, next) {
26941
- const c = comparePoss(pos, next.pointOnShape);
26942
- if (c < 0) {
27078
+ /**
27079
+ *
27080
+ * @param cpTree
27081
+ * @param pos
27082
+ * @param succ
27083
+ * @param alreadyInserted set to `true` if the given `pos` has already been
27084
+ * inserted, `false` otherwise.
27085
+ */
27086
+ function isOrderCorrect(cpTree, pred, pos, succ, alreadyInserted) {
27087
+ if (cpTree.size <= (alreadyInserted ? 2 : 1)) {
26943
27088
  return true;
26944
27089
  }
26945
- if (c === 0) {
27090
+ const a = pred.pointOnShape;
27091
+ const b = pos;
27092
+ const c = succ.pointOnShape;
27093
+ // Let a = pred, b = pos, and c = succ.
27094
+ // For a cyclic order we need:
27095
+ // `(a < b && b < c)` || `(b < c && c < a)` || `(c < a && a < b)`
27096
+ // Let `X = a < b, Y = b < c, Z = c < a` then
27097
+ // `(X && Y)` || `(Y && Z)` || `(Z && X)`
27098
+ const _X = comparePoss(a, b);
27099
+ if (_X === 0) { // two points must have different order
27100
+ // console.log('aaa');
27101
+ // throw 'a';
27102
+ return false;
27103
+ }
27104
+ const _Y = comparePoss(b, c);
27105
+ if (_Y === 0) { // two points must have different order
27106
+ // console.log('bbb');
27107
+ // throw 'b';
26946
27108
  return false;
26947
27109
  }
26948
- const minNode = cpTree.getMinNode();
26949
- if (minNode === undefined) { // if tree not empty
27110
+ const _Z = comparePoss(c, a);
27111
+ if (_Z === 0) {
27112
+ // console.log('bbb');
27113
+ // throw 'c';
27114
+ return false;
27115
+ } // impossible anyway at this point
27116
+ // Now neither `_X` nor `_Y` nor `_Z` === `0`
27117
+ const X = _X < 0;
27118
+ const Y = _Y < 0;
27119
+ const Z = _Z < 0;
27120
+ // Cyclic order condition
27121
+ if ((X && Y) || (Y && Z) || (Z && X)) {
26950
27122
  return true;
26951
27123
  }
26952
- const min = minNode.datum;
26953
- const max = cpTree.getMaxNode().datum;
26954
- // larger than all -> crossing zero on loop
26955
- return (comparePoss(max.pointOnShape, pos) < 0 && min === next);
27124
+ // throw 'd';
27125
+ // console.log(_X, _Y, _Z);
27126
+ // console.log(cpTree.toArr().map(v => {
27127
+ // return {
27128
+ // curveIdx: v.pointOnShape.curve.idx,
27129
+ // t: v.pointOnShape.t,
27130
+ // order: v.pointOnShape.order,
27131
+ // order2: v.pointOnShape.order2
27132
+ // }
27133
+ // }));
27134
+ return false;
26956
27135
  }
26957
27136
 
26958
27137
 
26959
27138
  ;// ./src/cp-node/fs/insert-cp-node.ts
26960
27139
 
26961
- function insertCpNode(insertIfOrderWrong, isHoleClosing, isIntersection, cpTree, pos, _prev, lastInsertId) {
26962
- if (_prev !== undefined &&
26963
- !isOrderCorrect(cpTree, pos, _prev.next)) {
26964
- if (!insertIfOrderWrong) {
27140
+ /**
27141
+ *
27142
+ * @param insertIfOrderWrong order cannot be checked for hole-closers since they're on different loops
27143
+ * @param isHoleClosing
27144
+ * @param isIntersection
27145
+ * @param cpTree
27146
+ * @param pos
27147
+ * @param pred predecessor (to `pos`) on sampe loop (found via `cpTree`)
27148
+ * @param succ successor (to `pos`) on sampe loop (found via `cpTree`)
27149
+ * @param lastInsertId
27150
+ */
27151
+ function insertCpNode(insertIfOrderWrong, isHoleClosing, isIntersection, cpTree, pos, pred, succ, lastInsertId) {
27152
+ if (pred !== undefined && succ !== undefined) {
27153
+ // !isOrderCorrect(cpTree, pos, pred.next, false)) {
27154
+ const orderCorrect = isOrderCorrect(cpTree, pred, pos, succ, false);
27155
+ // if (!orderCorrect) {
27156
+ // console.log('aaaaa');
27157
+ // }
27158
+ if (!insertIfOrderWrong && !orderCorrect) {
26965
27159
  return undefined;
26966
27160
  }
26967
27161
  }
@@ -26976,8 +27170,8 @@ function insertCpNode(insertIfOrderWrong, isHoleClosing, isIntersection, cpTree,
26976
27170
  prevOnCircle: undefined
26977
27171
  };
26978
27172
  lastInsertId.id++;
26979
- const prev = _prev === undefined ? cpNode : _prev;
26980
- const next = _prev === undefined ? cpNode : prev.next;
27173
+ const prev = pred === undefined ? cpNode : pred;
27174
+ const next = pred === undefined ? cpNode : prev.next;
26981
27175
  next.prev = cpNode;
26982
27176
  prev.next = cpNode;
26983
27177
  cpNode.prev = prev;
@@ -27018,6 +27212,9 @@ function byAngle(circle) {
27018
27212
 
27019
27213
 
27020
27214
  /**
27215
+ * Insert the given `poss` into the `cpTree` (within `meta`) and returns the
27216
+ * newly inserted `CpNode`s.
27217
+ *
27021
27218
  * @param circle
27022
27219
  * @param orders
27023
27220
  * @param cpTrees
@@ -27033,10 +27230,14 @@ function addToCpTree(insertIfOrderIsWrong, isHoleClosing, circle, orders, meta,
27033
27230
  const order = orders[i];
27034
27231
  const cpTree = cpTrees.get(ppos.curve.loop);
27035
27232
  const pos = { ...ppos, circle, order, order2: 0 };
27036
- const pred = neighbors === undefined
27037
- ? getCpNodeToLeftOrSame(cpTree, ppos, order, 0)
27233
+ const r = neighbors === undefined
27234
+ ? getCpNodeNeighbors(cpTree, ppos, order, 0)
27038
27235
  : neighbors[i];
27039
- const cpNode = insertCpNode(insertIfOrderIsWrong, isHoleClosing, false, cpTree, pos, pred, meta.lastInsertId);
27236
+ const pred = r?.[0];
27237
+ const succ = r?.[1];
27238
+ const cpNode = insertCpNode(insertIfOrderIsWrong, isHoleClosing, false, cpTree, pos, pred,
27239
+ // pred?.next,
27240
+ succ, meta.lastInsertId);
27040
27241
  if (cpNode === undefined) {
27041
27242
  anyFailed = true;
27042
27243
  }
@@ -27071,7 +27272,7 @@ function getCloseByCpIfExist(meta, pos, circle, order, order2, forProngCount) {
27071
27272
  const DISTANCE_THRESHOLD = 2 ** (maxCoordPowerOf2 - 40); // approx. 1e-9 for 1024 x 1024 shapes
27072
27273
  const DISTANCE_THRESHOLD_2 = 2 ** (maxCoordPowerOf2 - 46);
27073
27274
  const cpTree = cpTrees.get(pos.curve.loop);
27074
- const cur = getCpNodeToLeftOrSame(cpTree, pos, order, order2);
27275
+ const cur = getCpNodeNeighbors(cpTree, pos, order, order2)?.[0];
27075
27276
  if (!cur) {
27076
27277
  return undefined;
27077
27278
  }
@@ -27142,7 +27343,7 @@ function findAndAdd3Prong(meta, visitedCps) {
27142
27343
  }
27143
27344
  }
27144
27345
  const cpNodes = closeBysFor3Prong.length === 0
27145
- ? addToCpTree(true, false, circle, orders, meta, poss, δ3s.map(v => v[0])).cpNodes
27346
+ ? addToCpTree(true, false, circle, orders, meta, poss, δ3s).cpNodes
27146
27347
  : [];
27147
27348
  return { closeBysFor3Prong, cpNodes: cpNodes };
27148
27349
  }
@@ -27206,7 +27407,7 @@ function findAndAdd3Prongs(meta, cpStart) {
27206
27407
  *
27207
27408
  * @param meta
27208
27409
  * @param cpStart The CpNode to start traversing from.
27209
- */
27410
+ */
27210
27411
  function findAndAdd3ProngsOnLoop(meta, cpStart) {
27211
27412
  if (cpStart === undefined) {
27212
27413
  return undefined;
@@ -27217,7 +27418,11 @@ function findAndAdd3ProngsOnLoop(meta, cpStart) {
27217
27418
  reLoop = false;
27218
27419
  const cpNodeSet = new Set(getAllOnLoop(cpStart));
27219
27420
  for (const cpNode of cpNodeSet) {
27220
- const { closeBysFor3Prong, addedCpNodes } = findAndAdd3Prongs(meta, cpNode);
27421
+ const threeProngs = findAndAdd3Prongs(meta, cpNode);
27422
+ if (threeProngs === undefined) {
27423
+ return getFirstItemOfSet(cpNodeGoodSet);
27424
+ }
27425
+ const { closeBysFor3Prong, addedCpNodes } = threeProngs;
27221
27426
  if (addedCpNodes.length > 0) {
27222
27427
  reLoop = true;
27223
27428
  }
@@ -27280,8 +27485,8 @@ function createInitialCpTree(loops, sharpCornerss, lastInsertId) {
27280
27485
  const { curve } = ppos;
27281
27486
  const pos1 = { ...ppos, curve, t: 1, circle, order: -1, order2: 0 };
27282
27487
  const pos2 = { ...ppos, curve: curve.next, t: 0, circle, order: +1, order2: 0 };
27283
- cpNode1 = insertCpNode(true, false, false, cpTree, pos1, cpNode2, lastInsertId);
27284
- cpNode2 = insertCpNode(true, false, false, cpTree, pos2, cpNode1, lastInsertId);
27488
+ cpNode1 = insertCpNode(true, false, false, cpTree, pos1, cpNode2, undefined, /* unused */ lastInsertId);
27489
+ cpNode2 = insertCpNode(true, false, false, cpTree, pos2, cpNode1, undefined, /* unused */ lastInsertId);
27285
27490
  cpNode1.prevOnCircle = cpNode2;
27286
27491
  cpNode2.prevOnCircle = cpNode1;
27287
27492
  cpNode1.nextOnCircle = cpNode2;
@@ -27434,7 +27639,7 @@ function getInitialCurvePieces(angle, isHoleClosing, loop, loops, meta, yPos, xO
27434
27639
  ? +1
27435
27640
  : calcPosOrder(xO, yPos)
27436
27641
  : 0;
27437
- const cpNode = getCpNodeToLeftOrSame(cpTrees.get(loop), yPos, order, 0);
27642
+ const cpNode = getCpNodeNeighbors(cpTrees.get(loop), yPos, order, 0)?.[0];
27438
27643
  if (!cpNode ||
27439
27644
  // The special case if there is only a single sharp corner or
27440
27645
  // two-way terminating 2-prong currently in the MAT. Don't remove!
@@ -29295,12 +29500,14 @@ function closeHole(meta, cpNodes) {
29295
29500
  const [cpNodeSource, cpNodeAntipode] = cpNodes;
29296
29501
  // Antipode - create and insert twin
29297
29502
  const posAntipode = cpNodeAntipode.pointOnShape;
29298
- const cpNodeAntipodeTwin = insertCpNode(true, true, false, cpTrees.get(posAntipode.curve.loop), { ...posAntipode, order2: +1 }, cpNodeAntipode, meta.lastInsertId);
29503
+ const cpNodeAntipodeTwin = insertCpNode(true, true, false, cpTrees.get(posAntipode.curve.loop), { ...posAntipode, order2: +1 }, cpNodeAntipode, undefined, // unused
29504
+ meta.lastInsertId);
29299
29505
  cpNodeAntipodeTwin.holeCloserTwin = cpNodeAntipode;
29300
29506
  cpNodeAntipode.holeCloserTwin = cpNodeAntipodeTwin;
29301
29507
  // Source - create and insert twin
29302
29508
  const posSource = cpNodeSource.pointOnShape;
29303
- const cpNodeSourceTwin = insertCpNode(true, true, false, cpTrees.get(posSource.curve.loop), { ...cpNodeSource.pointOnShape, order2: -1 }, cpNodeSource.prev, meta.lastInsertId);
29509
+ const cpNodeSourceTwin = insertCpNode(true, true, false, cpTrees.get(posSource.curve.loop), { ...posSource, order2: -1 }, cpNodeSource.prev, undefined, // unused
29510
+ meta.lastInsertId);
29304
29511
  cpNodeSourceTwin.holeCloserTwin = cpNodeSource;
29305
29512
  cpNodeSource.holeCloserTwin = cpNodeSourceTwin;
29306
29513
  // Connect graph
@@ -29337,7 +29544,7 @@ function findAndAdd2Prongs(meta, angleIncrement, for2Prongs, for1Prong) {
29337
29544
  const angleIncrement_ = angleIncrement * find_and_add_2_prongs_PI / 180;
29338
29545
  for (let i = 0; i < for2Prongs.length; i++) {
29339
29546
  const pos = for2Prongs[i];
29340
- const angles = getValidAngles(angleIncrement_, pos);
29547
+ const angles = for1Prong ? [0] : getValidAngles(angleIncrement_, pos);
29341
29548
  for (const angle of angles) {
29342
29549
  const twoProngInfo = find2Prong_(false, for1Prong, angle, pos);
29343
29550
  if (twoProngInfo) {
@@ -29872,7 +30079,9 @@ function getFor1ProngsOnLoop(minBezLength) {
29872
30079
  return { curve, t, isSource: true, p, pDd };
29873
30080
  }));
29874
30081
  }
29875
- return poss;
30082
+ return poss
30083
+ // keep away from edges
30084
+ .filter(pos => pos.t > 2 ** -20 && pos.t < 2 ** -20);
29876
30085
  };
29877
30086
  }
29878
30087
 
@@ -30077,10 +30286,10 @@ function findMat(loops, maxCoordPowerOf2, options) {
30077
30286
  }
30078
30287
  if (typeof _debug_ !== 'undefined') {
30079
30288
  if (_debug_.directives.stopAfterThreeProngs) {
30080
- return { cpNode: cpNode, meta };
30289
+ return { cpNode, meta };
30081
30290
  }
30082
30291
  }
30083
- // if (cpNode !== undefined) { checkOrdering(cpTrees, cpNode); }
30292
+ // if (cpNode !== undefined) { checkOrdering(meta, cpNode); }
30084
30293
  const mat = { cpNode, meta };
30085
30294
  addDebugInfo4();
30086
30295
  return mat;
@@ -30872,8 +31081,8 @@ function toStringifyable(cpNode) {
30872
31081
  loopSet.add(loop);
30873
31082
  }
30874
31083
  const posSimplified = {
30875
- curveIdx, loopIdx: loop.idx, p, t, isSource,
30876
- circle, order, order2, pDd,
31084
+ curveIdx, loopIdx: loop.idx, p, pDd, t, isSource,
31085
+ circle, order, order2
30877
31086
  };
30878
31087
  cpNodes_.push({
30879
31088
  id, isHoleClosing, isIntersection,
@@ -30918,6 +31127,7 @@ function loopFromStringifyable(loopStringifyable) {
30918
31127
  function fromStringifyable(cpNodesStringifyable) {
30919
31128
  const { cpNodes: _cpNodes, loops: _loops } = cpNodesStringifyable;
30920
31129
  const loops = _loops.map(loopFromStringifyable);
31130
+ const loopsByIdx = new Map(loops.map(loop => [loop.idx, loop]));
30921
31131
  //--------------------------------------------------------------------------
30922
31132
  // First pass: build all CpNode objects with proper `cp`.
30923
31133
  // Edges still hold number ids at this point.
@@ -30926,7 +31136,7 @@ function fromStringifyable(cpNodesStringifyable) {
30926
31136
  const { id, pointOnShape, isHoleClosing, isIntersection } = cpNode_;
30927
31137
  const { circle, order, order2 } = pointOnShape;
30928
31138
  const { curveIdx, loopIdx, p, pDd, t, isSource } = pointOnShape;
30929
- const curve = loops[loopIdx].curves[curveIdx];
31139
+ const curve = loopsByIdx.get(loopIdx).curves[curveIdx];
30930
31140
  const pointOnShape_ = {
30931
31141
  circle,
30932
31142
  curve, p, pDd, t, isSource,