@ccpc/math 0.1.0

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 (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -0
  3. package/dist/constants/geom_type.d.ts +13 -0
  4. package/dist/constants/geom_type.d.ts.map +1 -0
  5. package/dist/constants/geom_type.js +17 -0
  6. package/dist/constants/math_const.d.ts +9 -0
  7. package/dist/constants/math_const.d.ts.map +1 -0
  8. package/dist/constants/math_const.js +12 -0
  9. package/dist/core/box2.d.ts +71 -0
  10. package/dist/core/box2.d.ts.map +1 -0
  11. package/dist/core/box2.js +243 -0
  12. package/dist/core/coord2d.d.ts +62 -0
  13. package/dist/core/coord2d.d.ts.map +1 -0
  14. package/dist/core/coord2d.js +155 -0
  15. package/dist/core/geom_base.d.ts +19 -0
  16. package/dist/core/geom_base.d.ts.map +1 -0
  17. package/dist/core/geom_base.js +18 -0
  18. package/dist/core/mat3.d.ts +101 -0
  19. package/dist/core/mat3.d.ts.map +1 -0
  20. package/dist/core/mat3.js +290 -0
  21. package/dist/core/vec2.d.ts +138 -0
  22. package/dist/core/vec2.d.ts.map +1 -0
  23. package/dist/core/vec2.js +297 -0
  24. package/dist/curves/arc2.d.ts +49 -0
  25. package/dist/curves/arc2.d.ts.map +1 -0
  26. package/dist/curves/arc2.js +265 -0
  27. package/dist/curves/bspline2.d.ts +150 -0
  28. package/dist/curves/bspline2.d.ts.map +1 -0
  29. package/dist/curves/bspline2.js +793 -0
  30. package/dist/curves/circle2.d.ts +42 -0
  31. package/dist/curves/circle2.d.ts.map +1 -0
  32. package/dist/curves/circle2.js +135 -0
  33. package/dist/curves/circle_curve2.d.ts +38 -0
  34. package/dist/curves/circle_curve2.d.ts.map +1 -0
  35. package/dist/curves/circle_curve2.js +112 -0
  36. package/dist/curves/curve2.d.ts +214 -0
  37. package/dist/curves/curve2.d.ts.map +1 -0
  38. package/dist/curves/curve2.js +238 -0
  39. package/dist/curves/ellipse2.d.ts +42 -0
  40. package/dist/curves/ellipse2.d.ts.map +1 -0
  41. package/dist/curves/ellipse2.js +125 -0
  42. package/dist/curves/ellipse_arc2.d.ts +49 -0
  43. package/dist/curves/ellipse_arc2.d.ts.map +1 -0
  44. package/dist/curves/ellipse_arc2.js +184 -0
  45. package/dist/curves/ellipse_curve2.d.ts +56 -0
  46. package/dist/curves/ellipse_curve2.d.ts.map +1 -0
  47. package/dist/curves/ellipse_curve2.js +262 -0
  48. package/dist/curves/interval.d.ts +112 -0
  49. package/dist/curves/interval.d.ts.map +1 -0
  50. package/dist/curves/interval.js +200 -0
  51. package/dist/curves/line2.d.ts +64 -0
  52. package/dist/curves/line2.d.ts.map +1 -0
  53. package/dist/curves/line2.js +193 -0
  54. package/dist/curves/period_interval.d.ts +129 -0
  55. package/dist/curves/period_interval.d.ts.map +1 -0
  56. package/dist/curves/period_interval.js +240 -0
  57. package/dist/discretize/discretize_defaults.d.ts +12 -0
  58. package/dist/discretize/discretize_defaults.d.ts.map +1 -0
  59. package/dist/discretize/discretize_defaults.js +12 -0
  60. package/dist/discretize/discretize_engine.d.ts +33 -0
  61. package/dist/discretize/discretize_engine.d.ts.map +1 -0
  62. package/dist/discretize/discretize_engine.js +347 -0
  63. package/dist/discretize/discretize_errors.d.ts +15 -0
  64. package/dist/discretize/discretize_errors.d.ts.map +1 -0
  65. package/dist/discretize/discretize_errors.js +30 -0
  66. package/dist/discretize/discretize_options.d.ts +18 -0
  67. package/dist/discretize/discretize_options.d.ts.map +1 -0
  68. package/dist/discretize/discretize_options.js +19 -0
  69. package/dist/discretize/discretize_types.d.ts +36 -0
  70. package/dist/discretize/discretize_types.d.ts.map +1 -0
  71. package/dist/discretize/discretize_types.js +1 -0
  72. package/dist/discretize/internal/curve_guards.d.ts +35 -0
  73. package/dist/discretize/internal/curve_guards.d.ts.map +1 -0
  74. package/dist/discretize/internal/curve_guards.js +62 -0
  75. package/dist/discretize/internal/postprocess.d.ts +5 -0
  76. package/dist/discretize/internal/postprocess.d.ts.map +1 -0
  77. package/dist/discretize/internal/postprocess.js +109 -0
  78. package/dist/discretize/internal/sampling_utils.d.ts +8 -0
  79. package/dist/discretize/internal/sampling_utils.d.ts.map +1 -0
  80. package/dist/discretize/internal/sampling_utils.js +36 -0
  81. package/dist/discretize/register_builtin_strategies.d.ts +3 -0
  82. package/dist/discretize/register_builtin_strategies.d.ts.map +1 -0
  83. package/dist/discretize/register_builtin_strategies.js +10 -0
  84. package/dist/discretize/strategies/bspline_strategy.d.ts +4 -0
  85. package/dist/discretize/strategies/bspline_strategy.d.ts.map +1 -0
  86. package/dist/discretize/strategies/bspline_strategy.js +115 -0
  87. package/dist/discretize/strategies/circle_strategy.d.ts +7 -0
  88. package/dist/discretize/strategies/circle_strategy.d.ts.map +1 -0
  89. package/dist/discretize/strategies/circle_strategy.js +55 -0
  90. package/dist/discretize/strategies/ellipse_strategy.d.ts +7 -0
  91. package/dist/discretize/strategies/ellipse_strategy.d.ts.map +1 -0
  92. package/dist/discretize/strategies/ellipse_strategy.js +86 -0
  93. package/dist/discretize/strategies/line_strategy.d.ts +4 -0
  94. package/dist/discretize/strategies/line_strategy.d.ts.map +1 -0
  95. package/dist/discretize/strategies/line_strategy.js +40 -0
  96. package/dist/discretize/strategy_registry.d.ts +9 -0
  97. package/dist/discretize/strategy_registry.d.ts.map +1 -0
  98. package/dist/discretize/strategy_registry.js +34 -0
  99. package/dist/index.d.ts +30 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +24 -0
  102. package/dist/intersections/analytic_x_algorithm.d.ts +10 -0
  103. package/dist/intersections/analytic_x_algorithm.d.ts.map +1 -0
  104. package/dist/intersections/analytic_x_algorithm.js +83 -0
  105. package/dist/intersections/curve_x_engine.d.ts +9 -0
  106. package/dist/intersections/curve_x_engine.d.ts.map +1 -0
  107. package/dist/intersections/curve_x_engine.js +27 -0
  108. package/dist/intersections/index.d.ts +5 -0
  109. package/dist/intersections/index.d.ts.map +1 -0
  110. package/dist/intersections/index.js +11 -0
  111. package/dist/intersections/internal/certification.d.ts +34 -0
  112. package/dist/intersections/internal/certification.d.ts.map +1 -0
  113. package/dist/intersections/internal/certification.js +238 -0
  114. package/dist/intersections/internal/interval_clipping.d.ts +29 -0
  115. package/dist/intersections/internal/interval_clipping.d.ts.map +1 -0
  116. package/dist/intersections/internal/interval_clipping.js +123 -0
  117. package/dist/intersections/internal/kind.d.ts +4 -0
  118. package/dist/intersections/internal/kind.d.ts.map +1 -0
  119. package/dist/intersections/internal/kind.js +16 -0
  120. package/dist/intersections/internal/pair.d.ts +9 -0
  121. package/dist/intersections/internal/pair.d.ts.map +1 -0
  122. package/dist/intersections/internal/pair.js +14 -0
  123. package/dist/intersections/internal/result.d.ts +20 -0
  124. package/dist/intersections/internal/result.d.ts.map +1 -0
  125. package/dist/intersections/internal/result.js +125 -0
  126. package/dist/intersections/internal/sampling.d.ts +15 -0
  127. package/dist/intersections/internal/sampling.d.ts.map +1 -0
  128. package/dist/intersections/internal/sampling.js +131 -0
  129. package/dist/intersections/internal/segment.d.ts +32 -0
  130. package/dist/intersections/internal/segment.d.ts.map +1 -0
  131. package/dist/intersections/internal/segment.js +137 -0
  132. package/dist/intersections/internal/tolerance.d.ts +10 -0
  133. package/dist/intersections/internal/tolerance.d.ts.map +1 -0
  134. package/dist/intersections/internal/tolerance.js +20 -0
  135. package/dist/intersections/intersector.d.ts +6 -0
  136. package/dist/intersections/intersector.d.ts.map +1 -0
  137. package/dist/intersections/intersector.js +1 -0
  138. package/dist/intersections/numeric_x_algorithm.d.ts +10 -0
  139. package/dist/intersections/numeric_x_algorithm.d.ts.map +1 -0
  140. package/dist/intersections/numeric_x_algorithm.js +73 -0
  141. package/dist/intersections/solvers/bspline_self_solver.d.ts +7 -0
  142. package/dist/intersections/solvers/bspline_self_solver.d.ts.map +1 -0
  143. package/dist/intersections/solvers/bspline_self_solver.js +308 -0
  144. package/dist/intersections/solvers/line_line_pair_solver.d.ts +7 -0
  145. package/dist/intersections/solvers/line_line_pair_solver.d.ts.map +1 -0
  146. package/dist/intersections/solvers/line_line_pair_solver.js +35 -0
  147. package/dist/intersections/solvers/pair_solvers.d.ts +94 -0
  148. package/dist/intersections/solvers/pair_solvers.d.ts.map +1 -0
  149. package/dist/intersections/solvers/pair_solvers.js +1078 -0
  150. package/dist/intersections/solvers/polyline_pair_intersector.d.ts +51 -0
  151. package/dist/intersections/solvers/polyline_pair_intersector.d.ts.map +1 -0
  152. package/dist/intersections/solvers/polyline_pair_intersector.js +731 -0
  153. package/dist/intersections/types.d.ts +11 -0
  154. package/dist/intersections/types.d.ts.map +1 -0
  155. package/dist/intersections/types.js +1 -0
  156. package/dist/serialize/dump_types.d.ts +101 -0
  157. package/dist/serialize/dump_types.d.ts.map +1 -0
  158. package/dist/serialize/dump_types.js +5 -0
  159. package/dist/serialize/geom_mgr.d.ts +24 -0
  160. package/dist/serialize/geom_mgr.d.ts.map +1 -0
  161. package/dist/serialize/geom_mgr.js +30 -0
  162. package/dist/types/type_define.d.ts +29 -0
  163. package/dist/types/type_define.d.ts.map +1 -0
  164. package/dist/types/type_define.js +10 -0
  165. package/dist/types/type_guard.d.ts +46 -0
  166. package/dist/types/type_guard.d.ts.map +1 -0
  167. package/dist/types/type_guard.js +5 -0
  168. package/dist/utils/math_error.d.ts +16 -0
  169. package/dist/utils/math_error.d.ts.map +1 -0
  170. package/dist/utils/math_error.js +35 -0
  171. package/dist/utils/math_utils.d.ts +9 -0
  172. package/dist/utils/math_utils.d.ts.map +1 -0
  173. package/dist/utils/math_utils.js +25 -0
  174. package/dist/utils/precision.d.ts +29 -0
  175. package/dist/utils/precision.d.ts.map +1 -0
  176. package/dist/utils/precision.js +44 -0
  177. package/package.json +38 -0
@@ -0,0 +1,125 @@
1
+ import { Interval } from '../../curves/interval';
2
+ import { Precision } from '../../utils/precision';
3
+ export function swapCurveXInfos(items) {
4
+ return items.map((item) => ({
5
+ point: item.point.clone(),
6
+ u1: item.u2,
7
+ u2: item.u1,
8
+ isOverlap: item.isOverlap,
9
+ range1: item.range2 ? new Interval(item.range2.start, item.range2.end) : undefined,
10
+ range2: item.range1 ? new Interval(item.range1.start, item.range1.end) : undefined,
11
+ }));
12
+ }
13
+ export function postprocessCurveXInfos(items, pointTol = Precision.CURVE_LENGTH_EPS * 8) {
14
+ const deduped = deduplicateCurveXInfos(items, pointTol);
15
+ const merged = mergeOverlaps(deduped);
16
+ return merged.sort((a, b) => a.u1 - b.u1 || a.u2 - b.u2);
17
+ }
18
+ export function analyzeCurveXInfosQuality(items, pointTol = Precision.CURVE_LENGTH_EPS * 8) {
19
+ const rawCount = items.length;
20
+ const overlapCount = items.filter((x) => x.isOverlap).length;
21
+ const uniquePoints = [];
22
+ for (const item of items) {
23
+ if (item.isOverlap)
24
+ continue;
25
+ const dup = uniquePoints.some((u) => u.point.distanceTo(item.point) <= pointTol);
26
+ if (!dup)
27
+ uniquePoints.push(item);
28
+ }
29
+ const uniquePointCount = uniquePoints.length;
30
+ const uniqueCount = uniquePointCount + overlapCount;
31
+ const duplicatePointCount = Math.max(0, rawCount - overlapCount - uniquePointCount);
32
+ return {
33
+ rawCount,
34
+ uniqueCount,
35
+ overlapCount,
36
+ uniquePointCount,
37
+ duplicatePointCount,
38
+ };
39
+ }
40
+ function deduplicateCurveXInfos(items, pointTol) {
41
+ const sorted = [...items].sort((a, b) => a.u1 - b.u1 || a.u2 - b.u2);
42
+ const ret = [];
43
+ for (const cur of sorted) {
44
+ let merged = false;
45
+ for (const prev of ret) {
46
+ if (!isSameCurveXInfo(prev, cur, pointTol))
47
+ continue;
48
+ if (prev.isOverlap) {
49
+ prev.range1 = mergeRange(prev.range1, cur.range1);
50
+ prev.range2 = mergeRange(prev.range2, cur.range2);
51
+ }
52
+ merged = true;
53
+ break;
54
+ }
55
+ if (!merged)
56
+ ret.push(cur);
57
+ }
58
+ return ret;
59
+ }
60
+ function mergeOverlaps(items) {
61
+ const points = items.filter((x) => !x.isOverlap);
62
+ const overlaps = items.filter((x) => x.isOverlap);
63
+ if (overlaps.length <= 1)
64
+ return [...points, ...overlaps];
65
+ overlaps.sort((a, b) => (a.range1?.start ?? a.u1) - (b.range1?.start ?? b.u1));
66
+ const merged = [normalizeOverlap(overlaps[0])];
67
+ for (let i = 1; i < overlaps.length; i++) {
68
+ const cur = normalizeOverlap(overlaps[i]);
69
+ const prev = merged[merged.length - 1];
70
+ if (!prev.range1 || !cur.range1 || !prev.range2 || !cur.range2) {
71
+ merged.push(cur);
72
+ continue;
73
+ }
74
+ if (cur.range1.start <= prev.range1.end + Precision.CURVE_PARAM_EPS) {
75
+ prev.range1 = new Interval(prev.range1.start, Math.max(prev.range1.end, cur.range1.end));
76
+ prev.range2 = new Interval(prev.range2.start, Math.max(prev.range2.end, cur.range2.end));
77
+ continue;
78
+ }
79
+ merged.push(cur);
80
+ }
81
+ return [...points, ...merged];
82
+ }
83
+ function mergeRange(a, b) {
84
+ if (!a)
85
+ return b ? new Interval(b.start, b.end) : undefined;
86
+ if (!b)
87
+ return new Interval(a.start, a.end);
88
+ return new Interval(Math.min(a.start, b.start), Math.max(a.end, b.end));
89
+ }
90
+ function isSameCurveXInfo(a, b, pointTol) {
91
+ if (a.isOverlap !== b.isOverlap)
92
+ return false;
93
+ const paramTol = Precision.CURVE_PARAM_EPS * 8;
94
+ if (!a.isOverlap && a.point.distanceTo(b.point) <= pointTol) {
95
+ return true;
96
+ }
97
+ if (Math.abs(a.u1 - b.u1) <= paramTol * 8 &&
98
+ Math.abs(a.u2 - b.u2) <= paramTol * 8 &&
99
+ a.point.distanceTo(b.point) <= pointTol * 2) {
100
+ return true;
101
+ }
102
+ if (!a.isOverlap || !a.range1 || !a.range2 || !b.range1 || !b.range2) {
103
+ return false;
104
+ }
105
+ const a1 = normalizeRange(a.range1);
106
+ const a2 = normalizeRange(a.range2);
107
+ const b1 = normalizeRange(b.range1);
108
+ const b2 = normalizeRange(b.range2);
109
+ return (Math.abs(a1.start - b1.start) <= paramTol &&
110
+ Math.abs(a1.end - b1.end) <= paramTol &&
111
+ Math.abs(a2.start - b2.start) <= paramTol &&
112
+ Math.abs(a2.end - b2.end) <= paramTol);
113
+ }
114
+ function normalizeOverlap(item) {
115
+ if (!item.isOverlap)
116
+ return item;
117
+ return {
118
+ ...item,
119
+ range1: item.range1 ? normalizeRange(item.range1) : undefined,
120
+ range2: item.range2 ? normalizeRange(item.range2) : undefined,
121
+ };
122
+ }
123
+ function normalizeRange(x) {
124
+ return x.start <= x.end ? x : new Interval(x.end, x.start);
125
+ }
@@ -0,0 +1,15 @@
1
+ import type { Curve2 } from '../../curves/curve2';
2
+ import { Vec2 } from '../../core/vec2';
3
+ export type CurveSample = {
4
+ u: number;
5
+ p: Vec2;
6
+ };
7
+ export declare function sampleCurveByParam(curve: Curve2, segmentCount: number): CurveSample[];
8
+ export type AdaptiveSampleOptions = {
9
+ maxDepth?: number;
10
+ chordErrorTol?: number;
11
+ tangentAngleTol?: number;
12
+ maxSamples?: number;
13
+ };
14
+ export declare function sampleCurveAdaptive(curve: Curve2, targetSegmentCount: number, options?: AdaptiveSampleOptions): CurveSample[];
15
+ //# sourceMappingURL=sampling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampling.d.ts","sourceRoot":"","sources":["../../../src/intersections/internal/sampling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAItC,MAAM,MAAM,WAAW,GAAG;IACtB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,IAAI,CAAA;CACV,CAAA;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,WAAW,EAAE,CAUrF;AAED,MAAM,MAAM,qBAAqB,GAAG;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,WAAW,EAAE,CA4CjI"}
@@ -0,0 +1,131 @@
1
+ import { Precision } from '../../utils/precision';
2
+ import { MathUtils } from '../../utils/math_utils';
3
+ export function sampleCurveByParam(curve, segmentCount) {
4
+ const range = curve.getRange();
5
+ const total = Math.max(2, segmentCount);
6
+ const samples = [];
7
+ for (let i = 0; i <= total; i++) {
8
+ const t = i / total;
9
+ const u = range.start + (range.end - range.start) * t;
10
+ samples.push({ u, p: curve.pointAt(u) });
11
+ }
12
+ return collapseDuplicateSamplePoints(samples);
13
+ }
14
+ export function sampleCurveAdaptive(curve, targetSegmentCount, options = {}) {
15
+ const range = curve.getRange();
16
+ if (range.length() <= Precision.CURVE_PARAM_EPS) {
17
+ return [{ u: range.start, p: curve.pointAt(range.start) }];
18
+ }
19
+ const bbox = curve.boundingBox();
20
+ const diag = Math.hypot(bbox.width(), bbox.height());
21
+ const scale = Math.max(diag, 1);
22
+ const segCount = Math.max(8, targetSegmentCount);
23
+ const maxDepth = options.maxDepth ?? (Math.ceil(Math.log2(segCount)) + 8);
24
+ const chordErrorTol = options.chordErrorTol ?? Math.max(Precision.CURVE_LENGTH_EPS * 8, scale / (segCount * 10));
25
+ const tangentAngleTol = options.tangentAngleTol ?? 0.22;
26
+ const maxSamples = options.maxSamples ?? segCount * 8;
27
+ const cutParams = collectCutParams(curve);
28
+ const samples = [];
29
+ for (let i = 0; i < cutParams.length - 1; i++) {
30
+ const u0 = cutParams[i];
31
+ const u1 = cutParams[i + 1];
32
+ if (u1 - u0 <= Precision.CURVE_PARAM_EPS)
33
+ continue;
34
+ const p0 = curve.pointAt(u0);
35
+ const p1 = curve.pointAt(u1);
36
+ subdivideAdaptive(curve, u0, p0, u1, p1, 0, maxDepth, chordErrorTol, tangentAngleTol, maxSamples, samples);
37
+ }
38
+ // Ensure terminal endpoint is present.
39
+ const end = cutParams[cutParams.length - 1];
40
+ samples.push({ u: end, p: curve.pointAt(end) });
41
+ return collapseDuplicateSamplePoints(samples);
42
+ }
43
+ function collectCutParams(curve) {
44
+ const range = curve.getRange();
45
+ const cuts = [range.start];
46
+ if (curve.isBSpline()) {
47
+ for (const u of curve.getContinuityBreakParams(Precision.CURVE_PARAM_EPS)) {
48
+ if (u > range.start + Precision.CURVE_PARAM_EPS && u < range.end - Precision.CURVE_PARAM_EPS) {
49
+ cuts.push(u);
50
+ }
51
+ }
52
+ }
53
+ cuts.push(range.end);
54
+ cuts.sort((a, b) => a - b);
55
+ const unique = [];
56
+ for (const u of cuts) {
57
+ if (unique.length === 0 || Math.abs(unique[unique.length - 1] - u) > Precision.CURVE_PARAM_EPS) {
58
+ unique.push(u);
59
+ }
60
+ }
61
+ return unique;
62
+ }
63
+ function subdivideAdaptive(curve, u0, p0, u1, p1, depth, maxDepth, chordErrorTol, tangentAngleTol, maxSamples, out) {
64
+ if (out.length >= maxSamples) {
65
+ out.push({ u: u0, p: p0 });
66
+ return;
67
+ }
68
+ if (!shouldSplit(curve, u0, p0, u1, p1, depth, maxDepth, chordErrorTol, tangentAngleTol)) {
69
+ out.push({ u: u0, p: p0 });
70
+ return;
71
+ }
72
+ const um = 0.5 * (u0 + u1);
73
+ if (um <= u0 + Precision.CURVE_PARAM_EPS || um >= u1 - Precision.CURVE_PARAM_EPS) {
74
+ out.push({ u: u0, p: p0 });
75
+ return;
76
+ }
77
+ const pm = curve.pointAt(um);
78
+ subdivideAdaptive(curve, u0, p0, um, pm, depth + 1, maxDepth, chordErrorTol, tangentAngleTol, maxSamples, out);
79
+ subdivideAdaptive(curve, um, pm, u1, p1, depth + 1, maxDepth, chordErrorTol, tangentAngleTol, maxSamples, out);
80
+ }
81
+ function shouldSplit(curve, u0, p0, u1, p1, depth, maxDepth, chordErrorTol, tangentAngleTol) {
82
+ if (depth >= maxDepth)
83
+ return false;
84
+ if (u1 - u0 <= Precision.CURVE_PARAM_EPS * 2)
85
+ return false;
86
+ const chord = p1.subtracted(p0);
87
+ const chordLen = chord.len();
88
+ if (chordLen <= Precision.CURVE_LENGTH_EPS)
89
+ return true;
90
+ const um = 0.5 * (u0 + u1);
91
+ const pm = curve.pointAt(um);
92
+ const midError = pointToLineDistance(pm, p0, p1, chordLen);
93
+ if (midError > chordErrorTol)
94
+ return true;
95
+ const t0 = curve.tangentAt(u0);
96
+ const t1 = curve.tangentAt(u1);
97
+ if (t0.len() <= Precision.CURVE_NEWTON_EPS || t1.len() <= Precision.CURVE_NEWTON_EPS)
98
+ return true;
99
+ const a0 = vectorAngle(t0, chord);
100
+ const a1 = vectorAngle(t1, chord);
101
+ const ae = vectorAngle(t0, t1);
102
+ return a0 > tangentAngleTol || a1 > tangentAngleTol || ae > tangentAngleTol * 2;
103
+ }
104
+ function pointToLineDistance(p, a, b, abLen) {
105
+ if (abLen <= Precision.CURVE_LENGTH_EPS)
106
+ return p.distanceTo(a);
107
+ const ap = p.subtracted(a);
108
+ const ab = b.subtracted(a);
109
+ return Math.abs(ap.cross(ab)) / abLen;
110
+ }
111
+ function vectorAngle(v1, v2) {
112
+ const l1 = v1.len();
113
+ const l2 = v2.len();
114
+ if (l1 <= Precision.CURVE_NEWTON_EPS || l2 <= Precision.CURVE_NEWTON_EPS)
115
+ return Math.PI;
116
+ const x = v1.dot(v2) / (l1 * l2);
117
+ return Math.acos(MathUtils.clamp(x, -1, 1));
118
+ }
119
+ function collapseDuplicateSamplePoints(samples) {
120
+ if (samples.length <= 1)
121
+ return samples;
122
+ const ret = [samples[0]];
123
+ for (let i = 1; i < samples.length; i++) {
124
+ const prev = ret[ret.length - 1];
125
+ const cur = samples[i];
126
+ if (prev.p.distanceTo(cur.p) <= Precision.CURVE_LENGTH_EPS)
127
+ continue;
128
+ ret.push(cur);
129
+ }
130
+ return ret;
131
+ }
@@ -0,0 +1,32 @@
1
+ import { Vec2 } from '../../core/vec2';
2
+ export type Segment = {
3
+ p0: Vec2;
4
+ p1: Vec2;
5
+ v: Vec2;
6
+ len: number;
7
+ unit: Vec2;
8
+ minX: number;
9
+ minY: number;
10
+ maxX: number;
11
+ maxY: number;
12
+ };
13
+ export type SegmentHit = {
14
+ kind: 'none';
15
+ } | {
16
+ kind: 'point';
17
+ t1: number;
18
+ t2: number;
19
+ } | {
20
+ kind: 'overlap';
21
+ t1s: number;
22
+ t1e: number;
23
+ t2s: number;
24
+ t2e: number;
25
+ };
26
+ export declare function makeSegment(p0: Vec2, p1: Vec2): Segment;
27
+ export declare function segmentBoxesMayIntersect(s1: Segment, s2: Segment, pad?: number): boolean;
28
+ export declare function segmentDistance(s1: Segment, s2: Segment): number;
29
+ export declare function intersectSegments(s1: Segment, s2: Segment): SegmentHit;
30
+ export declare function pointAtSegmentUnit(seg: Segment, t: number): Vec2;
31
+ export declare function lerp(a: number, b: number, t: number): number;
32
+ //# sourceMappingURL=segment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segment.d.ts","sourceRoot":"","sources":["../../../src/intersections/internal/segment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAGtC,MAAM,MAAM,OAAO,GAAG;IAClB,EAAE,EAAE,IAAI,CAAA;IACR,EAAE,EAAE,IAAI,CAAA;IACR,CAAC,EAAE,IAAI,CAAA;IACP,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,UAAU,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAE7E,wBAAgB,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,OAAO,CAmBvD;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,SAA6B,WAElG;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,UAoBvD;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,UAAU,CAqBtE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,QAEzD;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,UAEnD"}
@@ -0,0 +1,137 @@
1
+ import { Vec2 } from '../../core/vec2';
2
+ import { Precision } from '../../utils/precision';
3
+ export function makeSegment(p0, p1) {
4
+ const v = p1.subtracted(p0);
5
+ const len = v.len();
6
+ const unit = len > Precision.CURVE_LENGTH_EPS ? v.scaled(1 / len) : Vec2.zero();
7
+ const minX = Math.min(p0.x, p1.x);
8
+ const minY = Math.min(p0.y, p1.y);
9
+ const maxX = Math.max(p0.x, p1.x);
10
+ const maxY = Math.max(p0.y, p1.y);
11
+ return {
12
+ p0: p0.clone(),
13
+ p1: p1.clone(),
14
+ v,
15
+ len,
16
+ unit,
17
+ minX,
18
+ minY,
19
+ maxX,
20
+ maxY,
21
+ };
22
+ }
23
+ export function segmentBoxesMayIntersect(s1, s2, pad = Precision.CURVE_LENGTH_EPS) {
24
+ return !(s1.maxX + pad < s2.minX || s2.maxX + pad < s1.minX || s1.maxY + pad < s2.minY || s2.maxY + pad < s1.minY);
25
+ }
26
+ export function segmentDistance(s1, s2) {
27
+ if (s1.len <= Precision.CURVE_LENGTH_EPS && s2.len <= Precision.CURVE_LENGTH_EPS) {
28
+ return s1.p0.distanceTo(s2.p0);
29
+ }
30
+ if (s1.len <= Precision.CURVE_LENGTH_EPS) {
31
+ return pointSegmentDistance(s1.p0, s2);
32
+ }
33
+ if (s2.len <= Precision.CURVE_LENGTH_EPS) {
34
+ return pointSegmentDistance(s2.p0, s1);
35
+ }
36
+ const hit = intersectSegments(s1, s2);
37
+ if (hit.kind !== 'none')
38
+ return 0;
39
+ return Math.min(pointSegmentDistance(s1.p0, s2), pointSegmentDistance(s1.p1, s2), pointSegmentDistance(s2.p0, s1), pointSegmentDistance(s2.p1, s1));
40
+ }
41
+ export function intersectSegments(s1, s2) {
42
+ if (s1.len <= Precision.CURVE_LENGTH_EPS || s2.len <= Precision.CURVE_LENGTH_EPS) {
43
+ return { kind: 'none' };
44
+ }
45
+ const cross = s1.v.cross(s2.v);
46
+ const delta = s2.p0.subtracted(s1.p0);
47
+ const collinear = delta.cross(s1.v);
48
+ const crossTol = crossTolerance(s1, s2);
49
+ const collinearTol = collinearTolerance(s1, s2, delta.len());
50
+ const rangeTol = paramRangeTolerance(s1, s2);
51
+ if (Math.abs(cross) <= crossTol) {
52
+ if (Math.abs(collinear) > collinearTol)
53
+ return { kind: 'none' };
54
+ return intersectCollinearSegments(s1, s2, rangeTol);
55
+ }
56
+ const t1Raw = delta.cross(s2.v) / cross;
57
+ const t2Raw = delta.cross(s1.v) / cross;
58
+ if (!inRange01(t1Raw, rangeTol) || !inRange01(t2Raw, rangeTol))
59
+ return { kind: 'none' };
60
+ return { kind: 'point', t1: clamp01(t1Raw), t2: clamp01(t2Raw) };
61
+ }
62
+ export function pointAtSegmentUnit(seg, t) {
63
+ return seg.p0.added(seg.v.scaled(t));
64
+ }
65
+ export function lerp(a, b, t) {
66
+ return a + (b - a) * t;
67
+ }
68
+ function intersectCollinearSegments(s1, s2, tol) {
69
+ const x0 = s2.p0.subtracted(s1.p0).dot(s1.unit);
70
+ const x1 = s2.p1.subtracted(s1.p0).dot(s1.unit);
71
+ const os = Math.max(0, Math.min(x0, x1));
72
+ const oe = Math.min(s1.len, Math.max(x0, x1));
73
+ if (oe < os - tol)
74
+ return { kind: 'none' };
75
+ if (Math.abs(oe - os) <= tol) {
76
+ const pos1 = clamp(os, 0, s1.len);
77
+ const p = s1.p0.added(s1.unit.scaled(pos1));
78
+ const pos2 = clamp(p.subtracted(s2.p0).dot(s2.unit), 0, s2.len);
79
+ return {
80
+ kind: 'point',
81
+ t1: s1.len > 0 ? pos1 / s1.len : 0,
82
+ t2: s2.len > 0 ? pos2 / s2.len : 0,
83
+ };
84
+ }
85
+ const p1s = clamp(os, 0, s1.len);
86
+ const p1e = clamp(oe, 0, s1.len);
87
+ const ps = s1.p0.added(s1.unit.scaled(p1s));
88
+ const pe = s1.p0.added(s1.unit.scaled(p1e));
89
+ const p2s = clamp(ps.subtracted(s2.p0).dot(s2.unit), 0, s2.len);
90
+ const p2e = clamp(pe.subtracted(s2.p0).dot(s2.unit), 0, s2.len);
91
+ return {
92
+ kind: 'overlap',
93
+ t1s: s1.len > 0 ? p1s / s1.len : 0,
94
+ t1e: s1.len > 0 ? p1e / s1.len : 0,
95
+ t2s: s2.len > 0 ? p2s / s2.len : 0,
96
+ t2e: s2.len > 0 ? p2e / s2.len : 0,
97
+ };
98
+ }
99
+ function inRange01(t, eps) {
100
+ return t >= -eps && t <= 1 + eps;
101
+ }
102
+ function clamp01(t) {
103
+ return clamp(t, 0, 1);
104
+ }
105
+ function clamp(x, min, max) {
106
+ return Math.min(max, Math.max(min, x));
107
+ }
108
+ function pointSegmentDistance(p, s) {
109
+ if (s.len <= Precision.CURVE_LENGTH_EPS)
110
+ return p.distanceTo(s.p0);
111
+ const t = clamp(p.subtracted(s.p0).dot(s.v) / (s.len * s.len), 0, 1);
112
+ const q = pointAtSegmentUnit(s, t);
113
+ return p.distanceTo(q);
114
+ }
115
+ function segmentScale(s1, s2) {
116
+ const maxCoord = Math.max(Math.abs(s1.p0.x), Math.abs(s1.p0.y), Math.abs(s1.p1.x), Math.abs(s1.p1.y), Math.abs(s2.p0.x), Math.abs(s2.p0.y), Math.abs(s2.p1.x), Math.abs(s2.p1.y), 1);
117
+ return {
118
+ maxLen: Math.max(s1.len, s2.len, 1),
119
+ maxCoord,
120
+ };
121
+ }
122
+ function crossTolerance(s1, s2) {
123
+ const { maxLen, maxCoord } = segmentScale(s1, s2);
124
+ const lenProd = Math.max(s1.len * s2.len, 1);
125
+ const base = Math.max(Precision.CURVE_LENGTH_EPS * maxLen, maxCoord * 1e-12 * maxLen);
126
+ return Math.max(base, lenProd * 1e-12);
127
+ }
128
+ function collinearTolerance(s1, s2, deltaLen) {
129
+ const { maxLen, maxCoord } = segmentScale(s1, s2);
130
+ const lever = Math.max(deltaLen, maxLen, 1);
131
+ const base = Math.max(Precision.CURVE_LENGTH_EPS * lever, maxCoord * 1e-12 * lever);
132
+ return Math.max(base, maxLen * lever * 1e-12);
133
+ }
134
+ function paramRangeTolerance(s1, s2) {
135
+ const { maxLen } = segmentScale(s1, s2);
136
+ return Math.max(Precision.CURVE_PARAM_EPS, 1e-10 / maxLen);
137
+ }
@@ -0,0 +1,10 @@
1
+ import type { Curve2 } from '../../curves/curve2';
2
+ export type IntersectionTolerance = {
3
+ pointTol: number;
4
+ paramTol: number;
5
+ seedParamTol: number;
6
+ overlapPointTol: number;
7
+ };
8
+ export declare function makeIntersectionTolerance(c1: Curve2, c2: Curve2): IntersectionTolerance;
9
+ export declare function curvePointTolerance(curve: Curve2): number;
10
+ //# sourceMappingURL=tolerance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tolerance.d.ts","sourceRoot":"","sources":["../../../src/intersections/internal/tolerance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAGjD,MAAM,MAAM,qBAAqB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,qBAAqB,CAevF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,UAKhD"}
@@ -0,0 +1,20 @@
1
+ import { Precision } from '../../utils/precision';
2
+ export function makeIntersectionTolerance(c1, c2) {
3
+ const b1 = c1.boundingBox();
4
+ const b2 = c2.boundingBox();
5
+ const diag = Math.hypot(Math.max(b1.maxX, b2.maxX) - Math.min(b1.minX, b2.minX), Math.max(b1.maxY, b2.maxY) - Math.min(b1.minY, b2.minY));
6
+ const scaleTol = (Number.isFinite(diag) && diag > 0) ? diag * 1e-7 : 0;
7
+ const pointTol = Math.max(Precision.CURVE_LENGTH_EPS * 8, scaleTol);
8
+ return {
9
+ pointTol,
10
+ paramTol: Precision.CURVE_PARAM_EPS * 8,
11
+ seedParamTol: Precision.CURVE_PARAM_EPS * 8,
12
+ overlapPointTol: pointTol * 2.5,
13
+ };
14
+ }
15
+ export function curvePointTolerance(curve) {
16
+ const box = curve.boundingBox();
17
+ const diag = Math.hypot(box.width(), box.height());
18
+ const scaleTol = (Number.isFinite(diag) && diag > 0) ? diag * 1e-9 : 0;
19
+ return Math.max(Precision.CURVE_LENGTH_EPS * 8, scaleTol);
20
+ }
@@ -0,0 +1,6 @@
1
+ import type { Curve2 } from '../curves/curve2';
2
+ import type { CurveXInfo } from './types';
3
+ export interface ICurvePairIntersector {
4
+ intersect(c1: Curve2, c2: Curve2): CurveXInfo[];
5
+ }
6
+ //# sourceMappingURL=intersector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intersector.d.ts","sourceRoot":"","sources":["../../src/intersections/intersector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,MAAM,WAAW,qBAAqB;IAClC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,EAAE,CAAA;CAClD"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { Curve2 } from '../curves/curve2';
2
+ import type { CurveXInfo } from './types';
3
+ export declare class NumericXAlgorithm {
4
+ private readonly map;
5
+ private readonly retryMap;
6
+ intersect(c1: Curve2, c2: Curve2): CurveXInfo[];
7
+ private shouldRetry;
8
+ private boxesLikelyIntersect;
9
+ }
10
+ //# sourceMappingURL=numeric_x_algorithm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numeric_x_algorithm.d.ts","sourceRoot":"","sources":["../../src/intersections/numeric_x_algorithm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAgB9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAOnB;IAED,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAOxB;IAEM,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,EAAE;IA2BtD,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,oBAAoB;CAM/B"}
@@ -0,0 +1,73 @@
1
+ import { MathError } from '../utils/math_error';
2
+ import { getCurveKind } from './internal/kind';
3
+ import { normalizePair } from './internal/pair';
4
+ import { analyzeCurveXInfosQuality, postprocessCurveXInfos, swapCurveXInfos } from './internal/result';
5
+ import { makeIntersectionTolerance } from './internal/tolerance';
6
+ import { ArcBSplinePairSolver, BSplineBSplinePairSolver, CircleBSplinePairSolver, EllipseArcBSplinePairSolver, EllipseBSplinePairSolver, LineBSplinePairSolver, } from './solvers/pair_solvers';
7
+ import { PolylinePairIntersector } from './solvers/polyline_pair_intersector';
8
+ export class NumericXAlgorithm {
9
+ constructor() {
10
+ this.map = {
11
+ 'line|bspline': new LineBSplinePairSolver(),
12
+ 'circle|bspline': new CircleBSplinePairSolver(),
13
+ 'arc|bspline': new ArcBSplinePairSolver(),
14
+ 'ellipse|bspline': new EllipseBSplinePairSolver(),
15
+ 'ellipseArc|bspline': new EllipseArcBSplinePairSolver(),
16
+ 'bspline|bspline': new BSplineBSplinePairSolver(),
17
+ };
18
+ this.retryMap = {
19
+ 'line|bspline': new PolylinePairIntersector(640, 96),
20
+ 'circle|bspline': new PolylinePairIntersector(640, 96),
21
+ 'arc|bspline': new PolylinePairIntersector(640, 96),
22
+ 'ellipse|bspline': new PolylinePairIntersector(704, 96),
23
+ 'ellipseArc|bspline': new PolylinePairIntersector(704, 96),
24
+ 'bspline|bspline': new PolylinePairIntersector(768, 96),
25
+ };
26
+ }
27
+ intersect(c1, c2) {
28
+ const k1 = getCurveKind(c1);
29
+ const k2 = getCurveKind(c2);
30
+ const pair = normalizePair(k1, k2);
31
+ const solver = this.map[pair.key];
32
+ if (!solver) {
33
+ MathError.throw(`NumericXAlgorithm: unsupported pair ${c1.getType()}|${c2.getType()}`);
34
+ }
35
+ const tol = makeIntersectionTolerance(c1, c2);
36
+ const diagBefore = PolylinePairIntersector.getDiagnostics();
37
+ const raw = pair.swapped ? solver.intersect(c2, c1) : solver.intersect(c1, c2);
38
+ const ordered = pair.swapped ? swapCurveXInfos(raw) : raw;
39
+ const primary = postprocessCurveXInfos(ordered, tol.pointTol);
40
+ const quality = analyzeCurveXInfosQuality(ordered, tol.pointTol);
41
+ if (!this.shouldRetry(pair.key, c1, c2, quality, diagBefore, PolylinePairIntersector.getDiagnostics())) {
42
+ return primary;
43
+ }
44
+ const retrySolver = this.retryMap[pair.key];
45
+ if (!retrySolver)
46
+ return primary;
47
+ const retryRaw = pair.swapped ? retrySolver.intersect(c2, c1) : retrySolver.intersect(c1, c2);
48
+ if (retryRaw.length === 0)
49
+ return primary;
50
+ const retryOrdered = pair.swapped ? swapCurveXInfos(retryRaw) : retryRaw;
51
+ return postprocessCurveXInfos([...ordered, ...retryOrdered], tol.pointTol);
52
+ }
53
+ shouldRetry(key, c1, c2, quality, before, after) {
54
+ if (!this.retryMap[key])
55
+ return false;
56
+ if (!this.boxesLikelyIntersect(c1, c2))
57
+ return false;
58
+ if (quality.rawCount === 0)
59
+ return true;
60
+ if (quality.duplicatePointCount > 0)
61
+ return true;
62
+ const certificationMissDelta = after.certificationMissCount - before.certificationMissCount;
63
+ const refineFailDelta = after.refineFailureCount - before.refineFailureCount;
64
+ const rejectDelta = after.certificationRejectCount - before.certificationRejectCount;
65
+ return certificationMissDelta > 0 || refineFailDelta > 0 || rejectDelta > 0;
66
+ }
67
+ boxesLikelyIntersect(c1, c2) {
68
+ const t = makeIntersectionTolerance(c1, c2);
69
+ const b1 = c1.boundingBox().expandByScalar(t.pointTol * 2);
70
+ const b2 = c2.boundingBox().expandByScalar(t.pointTol * 2);
71
+ return b1.intersects(b2);
72
+ }
73
+ }
@@ -0,0 +1,7 @@
1
+ import type { BSpline2 } from '../../curves/bspline2';
2
+ import type { CurveXInfo } from '../types';
3
+ export declare class BSplineSelfSolver {
4
+ intersect(curve: BSpline2): CurveXInfo[];
5
+ }
6
+ export declare function intersectBSplineSelf(curve: BSpline2): CurveXInfo[];
7
+ //# sourceMappingURL=bspline_self_solver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bspline_self_solver.d.ts","sourceRoot":"","sources":["../../../src/intersections/solvers/bspline_self_solver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAwB1C,qBAAa,iBAAiB;IACnB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,EAAE;CAmBlD;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,EAAE,CAElE"}