brepjs 8.0.0 → 8.0.2

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 (124) hide show
  1. package/dist/2d/blueprints/booleanHelpers.d.ts +32 -0
  2. package/dist/2d/blueprints/booleanHelpers.d.ts.map +1 -0
  3. package/dist/2d/blueprints/booleanOperations.d.ts +5 -3
  4. package/dist/2d/blueprints/booleanOperations.d.ts.map +1 -1
  5. package/dist/2d/blueprints/intersectionSegments.d.ts +12 -0
  6. package/dist/2d/blueprints/intersectionSegments.d.ts.map +1 -0
  7. package/dist/2d/blueprints/segmentAssembly.d.ts +31 -0
  8. package/dist/2d/blueprints/segmentAssembly.d.ts.map +1 -0
  9. package/dist/2d.cjs +2 -2
  10. package/dist/2d.js +8 -8
  11. package/dist/{Blueprint-D3JfGJTz.js → Blueprint-B9fhnpFp.js} +117 -30
  12. package/dist/{Blueprint-CVctc41Z.cjs → Blueprint-VGbo3izk.cjs} +111 -24
  13. package/dist/{boolean2D-BdZATaHs.cjs → boolean2D-B1XrGVgx.cjs} +426 -345
  14. package/dist/{boolean2D-hOw5Qay5.js → boolean2D-_WiqPxWZ.js} +391 -310
  15. package/dist/{booleanFns-BBSVKhL2.cjs → booleanFns-BxW-N3rP.cjs} +12 -16
  16. package/dist/{booleanFns-CqehfzcK.js → booleanFns-CkccZ7UL.js} +14 -18
  17. package/dist/brepjs.cjs +133 -62
  18. package/dist/brepjs.js +290 -217
  19. package/dist/{cast-DQaUibmm.js → cast-C4Ff_1Qe.js} +2 -2
  20. package/dist/{cast-DkB0GKmQ.cjs → cast-DIiyxDLo.cjs} +2 -2
  21. package/dist/core/disposal.d.ts +1 -1
  22. package/dist/core/disposal.d.ts.map +1 -1
  23. package/dist/core.cjs +3 -3
  24. package/dist/core.js +3 -3
  25. package/dist/cornerFinder-BndBNtJE.cjs +58 -0
  26. package/dist/cornerFinder-DzGzfiqb.js +59 -0
  27. package/dist/curveBuilders-BUoFO1UG.cjs +196 -0
  28. package/dist/curveBuilders-CBlIWlbU.js +197 -0
  29. package/dist/{curveFns-BilyYL_s.cjs → curveFns-BrJDkaWi.cjs} +31 -44
  30. package/dist/{curveFns-CdVE4da7.js → curveFns-BshHA9Ys.js} +31 -44
  31. package/dist/{drawFns-921SkhDL.js → drawFns-Btmlh_Oz.js} +13 -14
  32. package/dist/{drawFns-CUyx50gi.cjs → drawFns-D2eDcf4k.cjs} +58 -59
  33. package/dist/{faceFns-DHu-2JpA.js → faceFns-DDzCECn3.js} +3 -3
  34. package/dist/{faceFns-BwK7FP7N.cjs → faceFns-NDRFeekj.cjs} +3 -3
  35. package/dist/helpers-Ck8GJ58k.cjs +203 -0
  36. package/dist/helpers-jku2V1DY.js +204 -0
  37. package/dist/io.cjs +4 -4
  38. package/dist/io.js +4 -4
  39. package/dist/kernel/occtAdapter.d.ts +1 -0
  40. package/dist/kernel/occtAdapter.d.ts.map +1 -1
  41. package/dist/kernel/sweepOps.d.ts +8 -0
  42. package/dist/kernel/sweepOps.d.ts.map +1 -1
  43. package/dist/kernel/types.d.ts +1 -0
  44. package/dist/kernel/types.d.ts.map +1 -1
  45. package/dist/loft-Bk9EM0gZ.js +373 -0
  46. package/dist/loft-DJXwxV_L.cjs +372 -0
  47. package/dist/{measurement-C5JGCuUP.js → measurement-DlXaTzKc.js} +3 -3
  48. package/dist/{measurement-fxm_pW7x.cjs → measurement-LcGh4wV0.cjs} +3 -3
  49. package/dist/measurement.cjs +1 -1
  50. package/dist/measurement.js +1 -1
  51. package/dist/{meshFns-AqAjTTVl.js → meshFns-Djzdn-CS.js} +1 -1
  52. package/dist/{meshFns-BhrZGi6w.cjs → meshFns-c8lDKfYy.cjs} +1 -1
  53. package/dist/{occtBoundary-du8_ex-p.cjs → occtBoundary-6kQSl3cF.cjs} +21 -0
  54. package/dist/{occtBoundary-CwegMzqc.js → occtBoundary-CqXvDhZY.js} +26 -5
  55. package/dist/operations/extrude.d.ts.map +1 -1
  56. package/dist/operations/extrudeFns.d.ts.map +1 -1
  57. package/dist/operations/extrudeUtils.d.ts +17 -0
  58. package/dist/operations/extrudeUtils.d.ts.map +1 -1
  59. package/dist/{operations-C1rWoba2.js → operations-CrQlFDHc.js} +30 -7
  60. package/dist/{operations-BP1wVDw0.cjs → operations-Do-WZGXc.cjs} +30 -7
  61. package/dist/operations.cjs +2 -2
  62. package/dist/operations.js +4 -4
  63. package/dist/query/cornerFinder.d.ts +48 -0
  64. package/dist/query/cornerFinder.d.ts.map +1 -0
  65. package/dist/query/directionUtils.d.ts +6 -0
  66. package/dist/query/directionUtils.d.ts.map +1 -0
  67. package/dist/query/edgeFinder.d.ts +15 -0
  68. package/dist/query/edgeFinder.d.ts.map +1 -0
  69. package/dist/query/faceFinder.d.ts +15 -0
  70. package/dist/query/faceFinder.d.ts.map +1 -0
  71. package/dist/query/finderCore.d.ts +35 -0
  72. package/dist/query/finderCore.d.ts.map +1 -0
  73. package/dist/query/finderFns.d.ts +21 -106
  74. package/dist/query/finderFns.d.ts.map +1 -1
  75. package/dist/query/shapeDistanceFilter.d.ts +11 -0
  76. package/dist/query/shapeDistanceFilter.d.ts.map +1 -0
  77. package/dist/query/vertexFinder.d.ts +16 -0
  78. package/dist/query/vertexFinder.d.ts.map +1 -0
  79. package/dist/query/wireFinder.d.ts +10 -0
  80. package/dist/query/wireFinder.d.ts.map +1 -0
  81. package/dist/query.cjs +42 -5
  82. package/dist/query.js +40 -2
  83. package/dist/{shapeFns-BrF97sKt.js → shapeFns-DQtpzndX.js} +17 -18
  84. package/dist/{shapeFns-BvOndshS.cjs → shapeFns-cN4qGpbO.cjs} +6 -7
  85. package/dist/{shapeTypes-DKhwEnUM.cjs → shapeTypes-BJ3Hmskg.cjs} +24 -20
  86. package/dist/{shapeTypes-BlSElW8z.js → shapeTypes-C9sUsmEW.js} +32 -28
  87. package/dist/sketching/Sketcher.d.ts.map +1 -1
  88. package/dist/sketching/Sketcher2d.d.ts +12 -4
  89. package/dist/sketching/Sketcher2d.d.ts.map +1 -1
  90. package/dist/sketching/ellipseUtils.d.ts +29 -0
  91. package/dist/sketching/ellipseUtils.d.ts.map +1 -0
  92. package/dist/sketching.cjs +2 -2
  93. package/dist/sketching.js +2 -2
  94. package/dist/topology/booleanFns.d.ts.map +1 -1
  95. package/dist/topology/curveBuilders.d.ts +75 -0
  96. package/dist/topology/curveBuilders.d.ts.map +1 -0
  97. package/dist/topology/curveFns.d.ts.map +1 -1
  98. package/dist/topology/primitiveFns.d.ts.map +1 -1
  99. package/dist/topology/shapeFns.d.ts.map +1 -1
  100. package/dist/topology/shapeHelpers.d.ts +6 -173
  101. package/dist/topology/shapeHelpers.d.ts.map +1 -1
  102. package/dist/topology/shapeUtils.d.ts +13 -0
  103. package/dist/topology/shapeUtils.d.ts.map +1 -0
  104. package/dist/topology/solidBuilders.d.ts +70 -0
  105. package/dist/topology/solidBuilders.d.ts.map +1 -0
  106. package/dist/topology/surfaceBuilders.d.ts +35 -0
  107. package/dist/topology/surfaceBuilders.d.ts.map +1 -0
  108. package/dist/topology/wrapperFns.d.ts +1 -0
  109. package/dist/topology/wrapperFns.d.ts.map +1 -1
  110. package/dist/{topology-tFzqSrGH.js → topology-CtfUZwLR.js} +8 -8
  111. package/dist/{topology-CIooytHH.cjs → topology-DXq8dLsi.cjs} +8 -8
  112. package/dist/topology.cjs +7 -7
  113. package/dist/topology.js +31 -31
  114. package/dist/{vectors-CBuaMeZv.js → vectors-BVgXsYWl.js} +1 -1
  115. package/dist/{vectors-ChWEZPwy.cjs → vectors-DK2hEKcI.cjs} +1 -1
  116. package/dist/vectors.cjs +2 -2
  117. package/dist/vectors.js +2 -2
  118. package/package.json +1 -1
  119. package/dist/loft-BzWFokmC.cjs +0 -178
  120. package/dist/loft-CtG5nMq5.js +0 -179
  121. package/dist/query-V6nV-VfL.js +0 -396
  122. package/dist/query-hMSmOWJP.cjs +0 -395
  123. package/dist/shapeHelpers-B2SXz1p4.cjs +0 -488
  124. package/dist/shapeHelpers-BcoZf2N9.js +0 -489
@@ -1,17 +1,17 @@
1
1
  "use strict";
2
- const vectors = require("./vectors-ChWEZPwy.cjs");
3
- const shapeHelpers = require("./shapeHelpers-B2SXz1p4.cjs");
2
+ const vectors = require("./vectors-DK2hEKcI.cjs");
4
3
  const errors = require("./errors-DK1VAdP4.cjs");
5
- const cast = require("./cast-DkB0GKmQ.cjs");
6
- const occtBoundary = require("./occtBoundary-du8_ex-p.cjs");
4
+ const cast = require("./cast-DIiyxDLo.cjs");
5
+ const occtBoundary = require("./occtBoundary-6kQSl3cF.cjs");
7
6
  const vecOps = require("./vecOps-CjRL1jau.cjs");
8
- const loft = require("./loft-BzWFokmC.cjs");
9
- const shapeTypes = require("./shapeTypes-DKhwEnUM.cjs");
10
- const curveFns = require("./curveFns-BilyYL_s.cjs");
11
- const faceFns = require("./faceFns-BwK7FP7N.cjs");
7
+ const loft = require("./loft-DJXwxV_L.cjs");
8
+ const shapeTypes = require("./shapeTypes-BJ3Hmskg.cjs");
9
+ const curveFns = require("./curveFns-BrJDkaWi.cjs");
10
+ const Blueprint = require("./Blueprint-VGbo3izk.cjs");
11
+ const faceFns = require("./faceFns-NDRFeekj.cjs");
12
12
  const result = require("./result.cjs");
13
- const query = require("./query-hMSmOWJP.cjs");
14
- const Blueprint = require("./Blueprint-CVctc41Z.cjs");
13
+ const helpers = require("./helpers-Ck8GJ58k.cjs");
14
+ const curveBuilders = require("./curveBuilders-BUoFO1UG.cjs");
15
15
  function* pointsIteration(intersector) {
16
16
  const nPoints = intersector.NbPoints();
17
17
  if (!nPoints) return;
@@ -51,10 +51,10 @@ const intersectCurves = (first, second, precision = 1e-9) => {
51
51
  } catch (e) {
52
52
  return errors.err(errors.computationError("INTERSECTION_FAILED", "Intersections failed between curves", e));
53
53
  }
54
- const segmentsAsPoints = commonSegments.filter((c) => query.samePoint(c.firstPoint, c.lastPoint, precision)).map((c) => c.firstPoint);
54
+ const segmentsAsPoints = commonSegments.filter((c) => helpers.samePoint(c.firstPoint, c.lastPoint, precision)).map((c) => c.firstPoint);
55
55
  if (segmentsAsPoints.length) {
56
56
  intersections.push(...segmentsAsPoints);
57
- commonSegments = commonSegments.filter((c) => !query.samePoint(c.firstPoint, c.lastPoint, precision));
57
+ commonSegments = commonSegments.filter((c) => !helpers.samePoint(c.firstPoint, c.lastPoint, precision));
58
58
  }
59
59
  const commonSegmentsPoints = commonSegments.flatMap((c) => [c.firstPoint, c.lastPoint]);
60
60
  return errors.ok({ intersections, commonSegments, commonSegmentsPoints });
@@ -73,12 +73,12 @@ const selfIntersections = (curve, precision = 1e-9) => {
73
73
  return errors.ok(intersections);
74
74
  };
75
75
  const offsetEndPoints = (firstPoint, lastPoint, offset) => {
76
- const tangent = query.normalize2d(query.subtract2d(lastPoint, firstPoint));
76
+ const tangent = helpers.normalize2d(helpers.subtract2d(lastPoint, firstPoint));
77
77
  const normal = [tangent[1], -tangent[0]];
78
78
  const offsetVec = [normal[0] * offset, normal[1] * offset];
79
79
  return {
80
- firstPoint: query.add2d(firstPoint, offsetVec),
81
- lastPoint: query.add2d(lastPoint, offsetVec)
80
+ firstPoint: helpers.add2d(firstPoint, offsetVec),
81
+ lastPoint: helpers.add2d(lastPoint, offsetVec)
82
82
  };
83
83
  };
84
84
  const make2dOffset = (curve, offset) => {
@@ -94,8 +94,8 @@ const make2dOffset = (curve, offset) => {
94
94
  const centerPos = r(circle.Location());
95
95
  const center = [centerPos.X(), centerPos.Y()];
96
96
  const offsetViaCenter = (point) => {
97
- const [x, y] = query.normalize2d(query.subtract2d(point, center));
98
- return query.add2d(point, [orientedOffset * x, orientedOffset * y]);
97
+ const [x, y] = helpers.normalize2d(helpers.subtract2d(point, center));
98
+ return helpers.add2d(point, [orientedOffset * x, orientedOffset * y]);
99
99
  };
100
100
  return {
101
101
  collapsed: true,
@@ -136,7 +136,7 @@ const make2dOffset = (curve, offset) => {
136
136
  return approximation;
137
137
  };
138
138
  function removeCorner(firstCurve, secondCurve, radius) {
139
- const sinAngle = query.crossProduct2d(firstCurve.tangentAt(1), secondCurve.tangentAt(0));
139
+ const sinAngle = helpers.crossProduct2d(firstCurve.tangentAt(1), secondCurve.tangentAt(0));
140
140
  if (Math.abs(sinAngle) < 1e-10) return null;
141
141
  const orientationCorrection = sinAngle > 0 ? -1 : 1;
142
142
  const offset = Math.abs(radius) * orientationCorrection;
@@ -156,8 +156,8 @@ function removeCorner(firstCurve, secondCurve, radius) {
156
156
  const center = potentialCenter;
157
157
  const splitForFillet = (curve, offsetCurve) => {
158
158
  const [x, y] = offsetCurve.tangentAt(center);
159
- const normal = query.normalize2d([-y, x]);
160
- const splitPoint = query.add2d(center, query.scalarMultiply2d(normal, offset));
159
+ const normal = helpers.normalize2d([-y, x]);
160
+ const splitPoint = helpers.add2d(center, helpers.scalarMultiply2d(normal, offset));
161
161
  const splitParam = errors.unwrap(curve.parameter(splitPoint, 1e-6));
162
162
  return curve.splitAt([splitParam]);
163
163
  };
@@ -182,9 +182,9 @@ function chamferCurves(firstCurve, secondCurve, radius) {
182
182
  return [first, Blueprint.make2dSegmentCurve(first.lastPoint, second.firstPoint), second];
183
183
  }
184
184
  function dogboneFilletCurves(firstCurve, secondCurve, radius) {
185
- const tgt1 = query.normalize2d(firstCurve.tangentAt(1));
186
- const tgt2 = query.normalize2d(secondCurve.tangentAt(0));
187
- const sinAngle = query.crossProduct2d(tgt1, tgt2);
185
+ const tgt1 = helpers.normalize2d(firstCurve.tangentAt(1));
186
+ const tgt2 = helpers.normalize2d(secondCurve.tangentAt(0));
187
+ const sinAngle = helpers.crossProduct2d(tgt1, tgt2);
188
188
  const a = Math.asin(sinAngle);
189
189
  if (Math.abs(sinAngle) < 1e-10) return [firstCurve, secondCurve];
190
190
  const orientationCorrection = sinAngle > 0 ? -1 : 1;
@@ -997,9 +997,9 @@ class Sketch {
997
997
  face() {
998
998
  let face;
999
999
  if (!this.baseFace) {
1000
- face = errors.unwrap(shapeHelpers.makeFace(this.wire));
1000
+ face = errors.unwrap(Blueprint.makeFace(this.wire));
1001
1001
  } else {
1002
- face = shapeHelpers.makeNewFaceWithinFace(this.baseFace, this.wire);
1002
+ face = Blueprint.makeNewFaceWithinFace(this.baseFace, this.wire);
1003
1003
  }
1004
1004
  return face;
1005
1005
  }
@@ -1016,7 +1016,7 @@ class Sketch {
1016
1016
  * (defaults to the sketch origin)
1017
1017
  */
1018
1018
  revolve(revolutionAxis, { origin } = {}) {
1019
- const face = errors.unwrap(shapeHelpers.makeFace(this.wire));
1019
+ const face = errors.unwrap(Blueprint.makeFace(this.wire));
1020
1020
  const solid = errors.unwrap(loft.revolution(face, origin || this.defaultOrigin, revolutionAxis));
1021
1021
  face.delete();
1022
1022
  this.delete();
@@ -1058,7 +1058,7 @@ class Sketch {
1058
1058
  this.delete();
1059
1059
  return solid2;
1060
1060
  }
1061
- const face = errors.unwrap(shapeHelpers.makeFace(this.wire));
1061
+ const face = errors.unwrap(Blueprint.makeFace(this.wire));
1062
1062
  const solid = loft.basicFaceExtrusion(face, [...extrusionVec]);
1063
1063
  gc();
1064
1064
  this.delete();
@@ -1124,13 +1124,13 @@ const defaultsSplineOptions = (config) => {
1124
1124
  const { endTangent: endTgt, startFactor = 1, endFactor = 1, startTangent: startTgt } = conf;
1125
1125
  let endTangent;
1126
1126
  if (typeof endTgt === "number") {
1127
- endTangent = query.polarToCartesian(1, endTgt * vecOps.DEG2RAD);
1127
+ endTangent = helpers.polarToCartesian(1, endTgt * vecOps.DEG2RAD);
1128
1128
  } else {
1129
1129
  endTangent = endTgt;
1130
1130
  }
1131
1131
  let startTangent;
1132
1132
  if (typeof startTgt === "number") {
1133
- startTangent = query.polarToCartesian(1, startTgt * vecOps.DEG2RAD);
1133
+ startTangent = helpers.polarToCartesian(1, startTgt * vecOps.DEG2RAD);
1134
1134
  } else {
1135
1135
  startTangent = startTgt;
1136
1136
  }
@@ -1221,9 +1221,57 @@ function convertSvgEllipseParams([x1, y1], [x2, y2], rx, ry, phi, fA, fS) {
1221
1221
  };
1222
1222
  return outputObj;
1223
1223
  }
1224
+ function normalizeEllipseRadii(horizontalRadius, verticalRadius, rotation) {
1225
+ if (horizontalRadius < verticalRadius) {
1226
+ return {
1227
+ majorRadius: verticalRadius,
1228
+ minorRadius: horizontalRadius,
1229
+ rotationAngle: rotation + 90
1230
+ };
1231
+ }
1232
+ return {
1233
+ majorRadius: horizontalRadius,
1234
+ minorRadius: verticalRadius,
1235
+ rotationAngle: rotation
1236
+ };
1237
+ }
1238
+ function makeEllipseArcFromSvgParams(startUV, endUV, majorRadius, minorRadius, rotationAngleDeg, longAxis, sweep, convertToUV) {
1239
+ const radRotationAngle = rotationAngleDeg * vecOps.DEG2RAD;
1240
+ const convertAxis = (ax) => helpers.distance2d(convertToUV(ax));
1241
+ const r1 = convertAxis(helpers.polarToCartesian(majorRadius, radRotationAngle));
1242
+ const r2 = convertAxis(helpers.polarToCartesian(minorRadius, radRotationAngle + Math.PI / 2));
1243
+ const xDir = helpers.normalize2d(convertToUV(helpers.rotate2d([1, 0], radRotationAngle)));
1244
+ const [, newRotationAngle] = helpers.cartesianToPolar(xDir);
1245
+ const { cx, cy, startAngle, endAngle, clockwise, rx, ry } = convertSvgEllipseParams(
1246
+ startUV,
1247
+ endUV,
1248
+ r1,
1249
+ r2,
1250
+ newRotationAngle,
1251
+ longAxis,
1252
+ sweep
1253
+ );
1254
+ const arc = Blueprint.make2dEllipseArc(
1255
+ rx,
1256
+ ry,
1257
+ clockwise ? startAngle : endAngle,
1258
+ clockwise ? endAngle : startAngle,
1259
+ [cx, cy],
1260
+ xDir
1261
+ );
1262
+ if (!clockwise) {
1263
+ arc.reverse();
1264
+ }
1265
+ return arc;
1266
+ }
1267
+ const cornerModeFns = {
1268
+ chamfer: chamferCurves,
1269
+ dogbone: dogboneFilletCurves,
1270
+ fillet: filletCurves
1271
+ };
1224
1272
  function buildCornerFunction(radius, mode) {
1225
1273
  if (typeof radius === "function") return radius;
1226
- const makeFn = mode === "chamfer" ? chamferCurves : mode === "dogbone" ? dogboneFilletCurves : filletCurves;
1274
+ const makeFn = cornerModeFns[mode];
1227
1275
  return (first, second) => makeFn(first, second, radius);
1228
1276
  }
1229
1277
  class BaseSketcher2d {
@@ -1237,12 +1285,37 @@ class BaseSketcher2d {
1237
1285
  this._nextCorner = null;
1238
1286
  this.pendingCurves = [];
1239
1287
  }
1288
+ // ── Coordinate conversion (overridden by FaceSketcher for UV mapping) ──
1240
1289
  _convertToUV([x, y]) {
1241
1290
  return [x, y];
1242
1291
  }
1243
1292
  _convertFromUV([u, v]) {
1244
1293
  return [u, v];
1245
1294
  }
1295
+ // ── Internal helpers ──
1296
+ /** Return the last curve in the pending list, or null if empty. */
1297
+ _lastCurve() {
1298
+ const len = this.pendingCurves.length;
1299
+ if (len === 0) return null;
1300
+ return this.pendingCurves[len - 1];
1301
+ }
1302
+ /** Require that a previous curve exists, returning it or throwing. */
1303
+ _requireLastCurve(caller, action) {
1304
+ const curve = this._lastCurve();
1305
+ if (!curve) result.bug(caller, `You need a previous curve to ${action}`);
1306
+ return curve;
1307
+ }
1308
+ /** Resolve a relative offset from the current pointer position. */
1309
+ _resolveRelative(xDist, yDist) {
1310
+ return [this.pointer[0] + xDist, this.pointer[1] + yDist];
1311
+ }
1312
+ /** Save a curve, advance the pointer to the given end point, and return `this`. */
1313
+ _saveCurveAndAdvance(curve, end) {
1314
+ this.saveCurve(curve);
1315
+ this.pointer = end;
1316
+ return this;
1317
+ }
1318
+ // ── Drawing state ──
1246
1319
  /**
1247
1320
  * Returns the current pen position as [x, y] coordinates
1248
1321
  *
@@ -1261,11 +1334,10 @@ class BaseSketcher2d {
1261
1334
  * @category Drawing State
1262
1335
  */
1263
1336
  get penAngle() {
1264
- if (this.pendingCurves.length === 0) return 0;
1265
- const lastCurve = this.pendingCurves[this.pendingCurves.length - 1];
1337
+ const lastCurve = this._lastCurve();
1338
+ if (!lastCurve) return 0;
1266
1339
  const [dx, dy] = lastCurve.tangentAt(1);
1267
- const angleInRadians = Math.atan2(dy, dx);
1268
- return angleInRadians * vecOps.RAD2DEG;
1340
+ return Math.atan2(dy, dx) * vecOps.RAD2DEG;
1269
1341
  }
1270
1342
  /** Move the pen to an absolute 2D position before drawing any curves. */
1271
1343
  movePointerTo(point) {
@@ -1286,16 +1358,15 @@ class BaseSketcher2d {
1286
1358
  this.pendingCurves.push(...this._nextCorner(previousCurve, curve));
1287
1359
  this._nextCorner = null;
1288
1360
  }
1361
+ // ── Line segments ──
1289
1362
  /** Draw a straight line to an absolute 2D point. */
1290
1363
  lineTo(point) {
1291
1364
  const curve = Blueprint.make2dSegmentCurve(this._convertToUV(this.pointer), this._convertToUV(point));
1292
- this.pointer = point;
1293
- this.saveCurve(curve);
1294
- return this;
1365
+ return this._saveCurveAndAdvance(curve, point);
1295
1366
  }
1296
1367
  /** Draw a straight line by relative horizontal and vertical distances. */
1297
1368
  line(xDist, yDist) {
1298
- return this.lineTo([this.pointer[0] + xDist, this.pointer[1] + yDist]);
1369
+ return this.lineTo(this._resolveRelative(xDist, yDist));
1299
1370
  }
1300
1371
  /** Draw a vertical line of the given signed distance. */
1301
1372
  vLine(distance) {
@@ -1315,41 +1386,35 @@ class BaseSketcher2d {
1315
1386
  }
1316
1387
  /** Draw a line to a point given in polar coordinates [r, theta] from the origin. */
1317
1388
  polarLineTo([r, theta]) {
1318
- const angleInRads = theta * vecOps.DEG2RAD;
1319
- const point = query.polarToCartesian(r, angleInRads);
1320
- return this.lineTo(point);
1389
+ return this.lineTo(helpers.polarToCartesian(r, theta * vecOps.DEG2RAD));
1321
1390
  }
1322
1391
  /** Draw a line in polar coordinates (distance and angle in degrees) from the current point. */
1323
1392
  polarLine(distance, angle) {
1324
- const angleInRads = angle * vecOps.DEG2RAD;
1325
- const [x, y] = query.polarToCartesian(distance, angleInRads);
1393
+ const [x, y] = helpers.polarToCartesian(distance, angle * vecOps.DEG2RAD);
1326
1394
  return this.line(x, y);
1327
1395
  }
1328
1396
  /** Draw a line tangent to the previous curve, extending by the given distance. */
1329
1397
  tangentLine(distance) {
1330
- const previousCurve = this.pendingCurves.length ? this.pendingCurves[this.pendingCurves.length - 1] : null;
1331
- if (!previousCurve)
1332
- result.bug("Sketcher2d.tangentLine", "You need a previous curve to sketch a tangent line");
1333
- const direction = query.normalize2d(this._convertFromUV(previousCurve.tangentAt(1)));
1398
+ const previousCurve = this._requireLastCurve("Sketcher2d.tangentLine", "sketch a tangent line");
1399
+ const direction = helpers.normalize2d(this._convertFromUV(previousCurve.tangentAt(1)));
1334
1400
  return this.line(direction[0] * distance, direction[1] * distance);
1335
1401
  }
1402
+ // ── Three-point arcs ──
1336
1403
  /** Draw a circular arc passing through a mid-point to an absolute end point. */
1337
1404
  threePointsArcTo(end, midPoint) {
1338
- this.saveCurve(
1339
- Blueprint.make2dThreePointArc(
1340
- this._convertToUV(this.pointer),
1341
- this._convertToUV(midPoint),
1342
- this._convertToUV(end)
1343
- )
1405
+ const curve = Blueprint.make2dThreePointArc(
1406
+ this._convertToUV(this.pointer),
1407
+ this._convertToUV(midPoint),
1408
+ this._convertToUV(end)
1344
1409
  );
1345
- this.pointer = end;
1346
- return this;
1410
+ return this._saveCurveAndAdvance(curve, end);
1347
1411
  }
1348
1412
  /** Draw a circular arc through a via-point to an end point, both as relative distances. */
1349
1413
  threePointsArc(xDist, yDist, viaXDist, viaYDist) {
1350
1414
  const [x0, y0] = this.pointer;
1351
1415
  return this.threePointsArcTo([x0 + xDist, y0 + yDist], [x0 + viaXDist, y0 + viaYDist]);
1352
1416
  }
1417
+ // ── Sagitta arcs ──
1353
1418
  /** Draw a circular arc to an absolute end point, bulging by the given sagitta. */
1354
1419
  sagittaArcTo(end, sagitta) {
1355
1420
  const [x0, y0] = this.pointer;
@@ -1366,19 +1431,16 @@ class BaseSketcher2d {
1366
1431
  midX + sagDirX / sagDirLen * sagitta,
1367
1432
  midY + sagDirY / sagDirLen * sagitta
1368
1433
  ];
1369
- this.saveCurve(
1370
- Blueprint.make2dThreePointArc(
1371
- this._convertToUV(this.pointer),
1372
- this._convertToUV(sagPoint),
1373
- this._convertToUV(end)
1374
- )
1434
+ const curve = Blueprint.make2dThreePointArc(
1435
+ this._convertToUV(this.pointer),
1436
+ this._convertToUV(sagPoint),
1437
+ this._convertToUV(end)
1375
1438
  );
1376
- this.pointer = end;
1377
- return this;
1439
+ return this._saveCurveAndAdvance(curve, end);
1378
1440
  }
1379
1441
  /** Draw a circular arc to a relative end point, bulging by the given sagitta. */
1380
1442
  sagittaArc(xDist, yDist, sagitta) {
1381
- return this.sagittaArcTo([xDist + this.pointer[0], yDist + this.pointer[1]], sagitta);
1443
+ return this.sagittaArcTo(this._resolveRelative(xDist, yDist), sagitta);
1382
1444
  }
1383
1445
  /** Draw a vertical sagitta arc of the given distance and bulge. */
1384
1446
  vSagittaArc(distance, sagitta) {
@@ -1388,16 +1450,16 @@ class BaseSketcher2d {
1388
1450
  hSagittaArc(distance, sagitta) {
1389
1451
  return this.sagittaArc(distance, 0, sagitta);
1390
1452
  }
1453
+ // ── Bulge arcs ──
1391
1454
  /** Draw an arc to an absolute end point using a bulge factor (sagitta as fraction of half-chord). */
1392
1455
  bulgeArcTo(end, bulge) {
1393
1456
  if (!bulge) return this.lineTo(end);
1394
- const halfChord = query.distance2d(this.pointer, end) / 2;
1395
- const bulgeAsSagitta = -bulge * halfChord;
1396
- return this.sagittaArcTo(end, bulgeAsSagitta);
1457
+ const halfChord = helpers.distance2d(this.pointer, end) / 2;
1458
+ return this.sagittaArcTo(end, -bulge * halfChord);
1397
1459
  }
1398
1460
  /** Draw an arc to a relative end point using a bulge factor. */
1399
1461
  bulgeArc(xDist, yDist, bulge) {
1400
- return this.bulgeArcTo([xDist + this.pointer[0], yDist + this.pointer[1]], bulge);
1462
+ return this.bulgeArcTo(this._resolveRelative(xDist, yDist), bulge);
1401
1463
  }
1402
1464
  /** Draw a vertical bulge arc of the given distance and bulge factor. */
1403
1465
  vBulgeArc(distance, bulge) {
@@ -1407,71 +1469,45 @@ class BaseSketcher2d {
1407
1469
  hBulgeArc(distance, bulge) {
1408
1470
  return this.bulgeArc(distance, 0, bulge);
1409
1471
  }
1472
+ // ── Tangent arcs ──
1410
1473
  /** Draw a circular arc tangent to the previous curve, ending at an absolute point. */
1411
1474
  tangentArcTo(end) {
1412
- const previousCurve = this.pendingCurves.length ? this.pendingCurves[this.pendingCurves.length - 1] : null;
1413
- if (!previousCurve)
1414
- result.bug("Sketcher2d.tangentArc", "You need a previous curve to sketch a tangent arc");
1415
- this.saveCurve(
1416
- Blueprint.make2dTangentArc(
1417
- this._convertToUV(this.pointer),
1418
- previousCurve.tangentAt(1),
1419
- this._convertToUV(end)
1420
- )
1475
+ const previousCurve = this._requireLastCurve("Sketcher2d.tangentArc", "sketch a tangent arc");
1476
+ const curve = Blueprint.make2dTangentArc(
1477
+ this._convertToUV(this.pointer),
1478
+ previousCurve.tangentAt(1),
1479
+ this._convertToUV(end)
1421
1480
  );
1422
- this.pointer = end;
1423
- return this;
1481
+ return this._saveCurveAndAdvance(curve, end);
1424
1482
  }
1425
1483
  /** Draw a circular arc tangent to the previous curve, ending at a relative offset. */
1426
1484
  tangentArc(xDist, yDist) {
1427
- const [x0, y0] = this.pointer;
1428
- return this.tangentArcTo([xDist + x0, yDist + y0]);
1485
+ return this.tangentArcTo(this._resolveRelative(xDist, yDist));
1429
1486
  }
1487
+ // ── Ellipse arcs ──
1430
1488
  /** Draw an elliptical arc to an absolute end point (SVG-style parameters). */
1431
1489
  ellipseTo(end, horizontalRadius, verticalRadius, rotation = 0, longAxis = false, sweep = false) {
1432
- let rotationAngle = rotation;
1433
- let majorRadius = horizontalRadius;
1434
- let minorRadius = verticalRadius;
1435
- if (horizontalRadius < verticalRadius) {
1436
- rotationAngle = rotation + 90;
1437
- majorRadius = verticalRadius;
1438
- minorRadius = horizontalRadius;
1439
- }
1440
- const radRotationAngle = rotationAngle * vecOps.DEG2RAD;
1441
- const convertAxis = (ax) => query.distance2d(this._convertToUV(ax));
1442
- const r1 = convertAxis(query.polarToCartesian(majorRadius, radRotationAngle));
1443
- const r2 = convertAxis(query.polarToCartesian(minorRadius, radRotationAngle + Math.PI / 2));
1444
- const xDir = query.normalize2d(this._convertToUV(query.rotate2d([1, 0], radRotationAngle)));
1445
- const [, newRotationAngle] = query.cartesianToPolar(xDir);
1446
- const { cx, cy, startAngle, endAngle, clockwise, rx, ry } = convertSvgEllipseParams(
1490
+ const { majorRadius, minorRadius, rotationAngle } = normalizeEllipseRadii(
1491
+ horizontalRadius,
1492
+ verticalRadius,
1493
+ rotation
1494
+ );
1495
+ const arc = makeEllipseArcFromSvgParams(
1447
1496
  this._convertToUV(this.pointer),
1448
1497
  this._convertToUV(end),
1449
- r1,
1450
- r2,
1451
- newRotationAngle,
1498
+ majorRadius,
1499
+ minorRadius,
1500
+ rotationAngle,
1452
1501
  longAxis,
1453
- sweep
1454
- );
1455
- const arc = Blueprint.make2dEllipseArc(
1456
- rx,
1457
- ry,
1458
- clockwise ? startAngle : endAngle,
1459
- clockwise ? endAngle : startAngle,
1460
- [cx, cy],
1461
- xDir
1502
+ sweep,
1503
+ (p) => this._convertToUV(p)
1462
1504
  );
1463
- if (!clockwise) {
1464
- arc.reverse();
1465
- }
1466
- this.saveCurve(arc);
1467
- this.pointer = end;
1468
- return this;
1505
+ return this._saveCurveAndAdvance(arc, end);
1469
1506
  }
1470
1507
  /** Draw an elliptical arc to a relative end point (SVG-style parameters). */
1471
1508
  ellipse(xDist, yDist, horizontalRadius, verticalRadius, rotation = 0, longAxis = false, sweep = false) {
1472
- const [x0, y0] = this.pointer;
1473
1509
  return this.ellipseTo(
1474
- [xDist + x0, yDist + y0],
1510
+ this._resolveRelative(xDist, yDist),
1475
1511
  horizontalRadius,
1476
1512
  verticalRadius,
1477
1513
  rotation,
@@ -1481,32 +1517,24 @@ class BaseSketcher2d {
1481
1517
  }
1482
1518
  /** Draw a half-ellipse arc to an absolute end point with a given minor radius. */
1483
1519
  halfEllipseTo(end, minorRadius, sweep = false) {
1484
- const angle = query.polarAngle2d(end, this.pointer);
1485
- const dist = query.distance2d(end, this.pointer);
1520
+ const angle = helpers.polarAngle2d(end, this.pointer);
1521
+ const dist = helpers.distance2d(end, this.pointer);
1486
1522
  return this.ellipseTo(end, dist / 2, minorRadius, angle * vecOps.RAD2DEG, true, sweep);
1487
1523
  }
1488
1524
  /** Draw a half-ellipse arc to a relative end point with a given minor radius. */
1489
1525
  halfEllipse(xDist, yDist, minorRadius, sweep = false) {
1490
- const [x0, y0] = this.pointer;
1491
- return this.halfEllipseTo([x0 + xDist, y0 + yDist], minorRadius, sweep);
1526
+ return this.halfEllipseTo(this._resolveRelative(xDist, yDist), minorRadius, sweep);
1492
1527
  }
1528
+ // ── Bezier curves ──
1493
1529
  /** Draw a Bezier curve to an absolute end point through one or more control points. */
1494
1530
  bezierCurveTo(end, controlPoints) {
1495
- let cp;
1496
- if (controlPoints.length === 2 && !Array.isArray(controlPoints[0])) {
1497
- cp = [controlPoints];
1498
- } else {
1499
- cp = controlPoints;
1500
- }
1501
- this.saveCurve(
1502
- Blueprint.make2dBezierCurve(
1503
- this._convertToUV(this.pointer),
1504
- cp.map((point) => this._convertToUV(point)),
1505
- this._convertToUV(end)
1506
- )
1531
+ const cp = controlPoints.length === 2 && !Array.isArray(controlPoints[0]) ? [controlPoints] : controlPoints;
1532
+ const curve = Blueprint.make2dBezierCurve(
1533
+ this._convertToUV(this.pointer),
1534
+ cp.map((point) => this._convertToUV(point)),
1535
+ this._convertToUV(end)
1507
1536
  );
1508
- this.pointer = end;
1509
- return this;
1537
+ return this._saveCurveAndAdvance(curve, end);
1510
1538
  }
1511
1539
  /** Draw a quadratic Bezier curve to an absolute end point with a single control point. */
1512
1540
  quadraticBezierCurveTo(end, controlPoint) {
@@ -1516,11 +1544,12 @@ class BaseSketcher2d {
1516
1544
  cubicBezierCurveTo(end, startControlPoint, endControlPoint) {
1517
1545
  return this.bezierCurveTo(end, [startControlPoint, endControlPoint]);
1518
1546
  }
1547
+ // ── Smooth splines ──
1519
1548
  /** Draw a smooth cubic Bezier spline to an absolute end point, blending tangent with the previous curve. */
1520
1549
  smoothSplineTo(end, config) {
1521
1550
  const { endTangent, startTangent, startFactor, endFactor } = defaultsSplineOptions(config);
1522
- const previousCurve = this.pendingCurves.length ? this.pendingCurves[this.pendingCurves.length - 1] : null;
1523
- const defaultDistance = query.distance2d(this.pointer, end) * 0.25;
1551
+ const previousCurve = this._lastCurve();
1552
+ const defaultDistance = helpers.distance2d(this.pointer, end) * 0.25;
1524
1553
  let startPoleDirection;
1525
1554
  if (startTangent) {
1526
1555
  startPoleDirection = startTangent;
@@ -1529,7 +1558,7 @@ class BaseSketcher2d {
1529
1558
  } else {
1530
1559
  startPoleDirection = this._convertFromUV(previousCurve.tangentAt(1));
1531
1560
  }
1532
- startPoleDirection = query.normalize2d(startPoleDirection);
1561
+ startPoleDirection = helpers.normalize2d(startPoleDirection);
1533
1562
  const startControl = [
1534
1563
  this.pointer[0] + startPoleDirection[0] * startFactor * defaultDistance,
1535
1564
  this.pointer[1] + startPoleDirection[1] * startFactor * defaultDistance
@@ -1540,7 +1569,7 @@ class BaseSketcher2d {
1540
1569
  } else {
1541
1570
  endPoleDirection = endTangent;
1542
1571
  }
1543
- endPoleDirection = query.normalize2d(endPoleDirection);
1572
+ endPoleDirection = helpers.normalize2d(endPoleDirection);
1544
1573
  const endControl = [
1545
1574
  end[0] - endPoleDirection[0] * endFactor * defaultDistance,
1546
1575
  end[1] - endPoleDirection[1] * endFactor * defaultDistance
@@ -1549,8 +1578,9 @@ class BaseSketcher2d {
1549
1578
  }
1550
1579
  /** Draw a smooth cubic Bezier spline to a relative end point, blending tangent with the previous curve. */
1551
1580
  smoothSpline(xDist, yDist, splineConfig) {
1552
- return this.smoothSplineTo([xDist + this.pointer[0], yDist + this.pointer[1]], splineConfig);
1581
+ return this.smoothSplineTo(this._resolveRelative(xDist, yDist), splineConfig);
1553
1582
  }
1583
+ // ── Corner treatments ──
1554
1584
  /**
1555
1585
  * Changes the corner between the previous and next segments.
1556
1586
  */
@@ -1568,13 +1598,14 @@ class BaseSketcher2d {
1568
1598
  result.bug("Sketcher2d._customCornerLastWithFirst", "Not enough curves to close and fillet");
1569
1599
  this.pendingCurves.push(...buildCornerFunction(radius, mode)(previousCurve, curve));
1570
1600
  }
1601
+ // ── Close / mirror helpers ──
1571
1602
  _closeSketch() {
1572
- if (!query.samePoint(this.pointer, this.firstPoint)) {
1603
+ if (!helpers.samePoint(this.pointer, this.firstPoint)) {
1573
1604
  this.lineTo(this.firstPoint);
1574
1605
  }
1575
1606
  }
1576
1607
  _closeWithMirror() {
1577
- if (query.samePoint(this.pointer, this.firstPoint))
1608
+ if (helpers.samePoint(this.pointer, this.firstPoint))
1578
1609
  result.bug(
1579
1610
  "Sketcher2d._closeWithMirror",
1580
1611
  "Cannot close with a mirror when the sketch is already closed"
@@ -1588,9 +1619,9 @@ class BaseSketcher2d {
1588
1619
  (c) => new Blueprint.Curve2D(c.innerCurve.Mirrored_2(mirrorAxis))
1589
1620
  );
1590
1621
  mirroredCurves.reverse();
1591
- mirroredCurves.forEach((c) => {
1622
+ for (const c of mirroredCurves) {
1592
1623
  c.reverse();
1593
- });
1624
+ }
1594
1625
  this.pendingCurves.push(...mirroredCurves);
1595
1626
  this.pointer = this.firstPoint;
1596
1627
  }
@@ -1625,7 +1656,7 @@ class FaceSketcher extends BaseSketcher2d {
1625
1656
  const edges = this.pendingCurves.map((curve) => {
1626
1657
  return r(shapeTypes.createEdge(r(new oc.BRepBuilderAPI_MakeEdge_30(curve.wrapped, geomSurf)).Edge()));
1627
1658
  });
1628
- const wire = errors.unwrap(shapeHelpers.assembleWire(edges));
1659
+ const wire = errors.unwrap(curveBuilders.assembleWire(edges));
1629
1660
  oc.BRepLib.BuildCurves3d_2(wire.wrapped);
1630
1661
  gc();
1631
1662
  return wire;
@@ -1758,78 +1789,101 @@ const roundedRectangleBlueprint = (width, height, r = 0) => {
1758
1789
  addFillet(rx, -ry);
1759
1790
  return sk.close();
1760
1791
  };
1761
- const samePoint = (x, y) => query.samePoint(x, y, query.PRECISION_INTERSECTION);
1762
- const curveMidPoint = (curve) => {
1792
+ const samePoint = (x, y) => helpers.samePoint(x, y, helpers.PRECISION_INTERSECTION);
1793
+ function hashPoint(p) {
1794
+ return `${p[0].toFixed(9)},${p[1].toFixed(9)}`;
1795
+ }
1796
+ function hashSegment(first, last) {
1797
+ const h1 = hashPoint(first);
1798
+ const h2 = hashPoint(last);
1799
+ return h1 < h2 ? `${h1}|${h2}` : `${h2}|${h1}`;
1800
+ }
1801
+ function startOfSegment(s) {
1802
+ const first = s[0];
1803
+ if (first === void 0) {
1804
+ result.bug("startOfSegment", "empty segment");
1805
+ }
1806
+ return first.firstPoint;
1807
+ }
1808
+ function endOfSegment(s) {
1809
+ const last = s[s.length - 1];
1810
+ if (last === void 0) {
1811
+ result.bug("endOfSegment", "empty segment");
1812
+ }
1813
+ return last.lastPoint;
1814
+ }
1815
+ function reverseSegment(segment) {
1816
+ return [...segment].reverse().map((curve) => {
1817
+ const newCurve = curve.clone();
1818
+ newCurve.reverse();
1819
+ return newCurve;
1820
+ });
1821
+ }
1822
+ function reverseSegments(segments) {
1823
+ return [...segments].reverse().map(reverseSegment);
1824
+ }
1825
+ function curveMidPoint(curve) {
1763
1826
  const midParameter = (curve.lastParameter + curve.firstParameter) / 2;
1764
1827
  return curve.value(midParameter);
1765
- };
1766
- const rotateToStartAt = (curves, point) => {
1767
- const pointHash = hashPoint(point);
1768
- let startIndex = -1;
1828
+ }
1829
+ function findCurveIndexByStartPoint(curves, point) {
1830
+ const targetHash = hashPoint(point);
1769
1831
  for (let i = 0; i < curves.length; i++) {
1770
1832
  const curve = curves[i];
1771
- if (hashPoint(curve.firstPoint) === pointHash && samePoint(point, curve.firstPoint)) {
1772
- startIndex = i;
1773
- break;
1833
+ if (curve === void 0) continue;
1834
+ if (hashPoint(curve.firstPoint) === targetHash && samePoint(point, curve.firstPoint)) {
1835
+ return i;
1774
1836
  }
1775
1837
  }
1776
- if (startIndex <= 0) return curves;
1777
- return curves.slice(startIndex).concat(curves.slice(0, startIndex));
1778
- };
1779
- const rotateToStartAtSegment = (curves, segment) => {
1780
- const segFirstHash = hashPoint(segment.firstPoint);
1781
- const segLastHash = hashPoint(segment.lastPoint);
1782
- const onSegment = (curve) => {
1783
- return samePoint(segment.firstPoint, curve.firstPoint) && samePoint(segment.lastPoint, curve.lastPoint);
1784
- };
1785
- let startIndex = -1;
1838
+ return -1;
1839
+ }
1840
+ function findCurveIndexBySegment(curves, segFirstHash, segLastHash, matchesFn) {
1786
1841
  for (let i = 0; i < curves.length; i++) {
1787
1842
  const curve = curves[i];
1788
- if (hashPoint(curve.firstPoint) === segFirstHash && hashPoint(curve.lastPoint) === segLastHash && onSegment(curve)) {
1789
- startIndex = i;
1790
- break;
1843
+ if (curve === void 0) continue;
1844
+ if (hashPoint(curve.firstPoint) === segFirstHash && hashPoint(curve.lastPoint) === segLastHash && matchesFn(curve)) {
1845
+ return i;
1791
1846
  }
1792
1847
  }
1848
+ return -1;
1849
+ }
1850
+ function rotateArray(arr, startIndex) {
1851
+ if (startIndex <= 0) return arr;
1852
+ return arr.slice(startIndex).concat(arr.slice(0, startIndex));
1853
+ }
1854
+ function rotateToStartAt(curves, point) {
1855
+ const startIndex = findCurveIndexByStartPoint(curves, point);
1856
+ return rotateArray(curves, startIndex);
1857
+ }
1858
+ function rotateToStartAtSegment(curves, segment) {
1859
+ const segFirstHash = hashPoint(segment.firstPoint);
1860
+ const segLastHash = hashPoint(segment.lastPoint);
1861
+ const onSegment = (curve) => samePoint(segment.firstPoint, curve.firstPoint) && samePoint(segment.lastPoint, curve.lastPoint);
1862
+ let startIndex = findCurveIndexBySegment(curves, segFirstHash, segLastHash, onSegment);
1863
+ if (startIndex !== -1) {
1864
+ return rotateArray(curves, startIndex);
1865
+ }
1866
+ const reversed = reverseSegment(curves);
1867
+ startIndex = findCurveIndexBySegment(reversed, segFirstHash, segLastHash, onSegment);
1793
1868
  if (startIndex === -1) {
1794
- curves = reverseSegment(curves);
1795
- for (let i = 0; i < curves.length; i++) {
1796
- const curve = curves[i];
1797
- if (hashPoint(curve.firstPoint) === segFirstHash && hashPoint(curve.lastPoint) === segLastHash && onSegment(curve)) {
1798
- startIndex = i;
1799
- break;
1800
- }
1801
- }
1802
- if (startIndex === -1) {
1803
- result.bug("rotateToStartAtSegment", "Failed to rotate to segment start");
1804
- }
1869
+ result.bug("rotateToStartAtSegment", "failed to rotate to segment start");
1805
1870
  }
1806
- if (startIndex <= 0) return curves;
1807
- return curves.slice(startIndex).concat(curves.slice(0, startIndex));
1808
- };
1809
- const hashPoint = (p) => `${p[0].toFixed(9)},${p[1].toFixed(9)}`;
1810
- const hashSegment = (first, last) => {
1811
- const h1 = hashPoint(first);
1812
- const h2 = hashPoint(last);
1813
- return h1 < h2 ? `${h1}|${h2}` : `${h2}|${h1}`;
1814
- };
1871
+ return rotateArray(reversed, startIndex);
1872
+ }
1815
1873
  function* createSegmentOnPoints(curves, allIntersections, allCommonSegments) {
1816
1874
  const intersectionSet = new Set(allIntersections.map(hashPoint));
1817
1875
  const commonSegmentSet = new Set(
1818
1876
  allCommonSegments.map((seg) => hashSegment(seg.firstPoint, seg.lastPoint))
1819
1877
  );
1820
- const endsAtIntersection = (curve) => {
1821
- return intersectionSet.has(hashPoint(curve.lastPoint));
1822
- };
1823
- const isCommonSegment = (curve) => {
1824
- return commonSegmentSet.has(hashSegment(curve.firstPoint, curve.lastPoint));
1825
- };
1826
1878
  let currentCurves = [];
1827
1879
  for (const curve of curves) {
1828
- if (endsAtIntersection(curve)) {
1880
+ const endsAtIntersection = intersectionSet.has(hashPoint(curve.lastPoint));
1881
+ const isCommon = commonSegmentSet.has(hashSegment(curve.firstPoint, curve.lastPoint));
1882
+ if (endsAtIntersection) {
1829
1883
  currentCurves.push(curve);
1830
1884
  yield currentCurves;
1831
1885
  currentCurves = [];
1832
- } else if (isCommonSegment(curve)) {
1886
+ } else if (isCommon) {
1833
1887
  if (currentCurves.length) {
1834
1888
  yield currentCurves;
1835
1889
  currentCurves = [];
@@ -1843,73 +1897,85 @@ function* createSegmentOnPoints(curves, allIntersections, allCommonSegments) {
1843
1897
  yield currentCurves;
1844
1898
  }
1845
1899
  }
1846
- const startOfSegment = (s) => {
1847
- return s[0].firstPoint;
1848
- };
1849
- const endOfSegment = (s) => {
1850
- return s[s.length - 1].lastPoint;
1851
- };
1852
- const reverseSegment = (segment) => {
1853
- return [...segment].reverse().map((curve) => {
1854
- const newCurve = curve.clone();
1855
- newCurve.reverse();
1856
- return newCurve;
1857
- });
1858
- };
1859
- const reverseSegments = (s) => {
1860
- return [...s].reverse().map(reverseSegment);
1861
- };
1862
- function removeNonCrossingPoint(allIntersections, segmentedCurve, blueprintToCheck) {
1900
+ function removeNonCrossingPoints(allIntersections, segmentedCurve, blueprintToCheck) {
1863
1901
  return allIntersections.filter((intersection) => {
1864
- const segmentsOfIntersection = segmentedCurve.filter((s) => {
1865
- return samePoint(s.firstPoint, intersection) || samePoint(s.lastPoint, intersection);
1866
- });
1867
- if (segmentsOfIntersection.length % 2) {
1868
- result.bug("removeNonCrossingPoint", "Odd number of segments at intersection point (expected even)");
1902
+ const touching = segmentedCurve.filter(
1903
+ (s) => samePoint(s.firstPoint, intersection) || samePoint(s.lastPoint, intersection)
1904
+ );
1905
+ if (touching.length % 2) {
1906
+ result.bug(
1907
+ "removeNonCrossingPoints",
1908
+ "Odd number of segments at intersection point (expected even)"
1909
+ );
1869
1910
  }
1870
- const isInside = segmentsOfIntersection.map((segment) => {
1871
- return blueprintToCheck.isInside(curveMidPoint(segment));
1872
- });
1873
- const segmentsOnTheSameSide = isInside.every((i) => i) || !isInside.some((i) => i);
1874
- return !segmentsOnTheSameSide;
1911
+ const insideFlags = touching.map(
1912
+ (segment) => blueprintToCheck.isInside(curveMidPoint(segment))
1913
+ );
1914
+ const allSameSide = insideFlags.every(Boolean) || insideFlags.every((f) => !f);
1915
+ return !allSameSide;
1875
1916
  });
1876
1917
  }
1877
- function blueprintsIntersectionSegments(first, second) {
1878
- let allIntersections = [];
1918
+ function findAllIntersections(first, second) {
1919
+ const allIntersections = [];
1879
1920
  const allCommonSegments = [];
1880
- const firstCurvePoints = new Array(first.curves.length).fill(0).map(() => []);
1881
- const secondCurvePoints = new Array(second.curves.length).fill(0).map(() => []);
1921
+ const firstCurvePoints = first.curves.map(() => []);
1922
+ const secondCurvePoints = second.curves.map(() => []);
1882
1923
  first.curves.forEach((thisCurve, firstIndex) => {
1883
1924
  second.curves.forEach((otherCurve, secondIndex) => {
1884
- const { intersections, commonSegments, commonSegmentsPoints: commonSegmentsPoints2 } = errors.unwrap(
1885
- intersectCurves(thisCurve, otherCurve, query.PRECISION_INTERSECTION / 100)
1925
+ const { intersections, commonSegments, commonSegmentsPoints } = errors.unwrap(
1926
+ intersectCurves(thisCurve, otherCurve, helpers.PRECISION_INTERSECTION / 100)
1886
1927
  );
1887
1928
  allIntersections.push(...intersections);
1888
- firstCurvePoints[firstIndex].push(...intersections);
1889
- secondCurvePoints[secondIndex].push(...intersections);
1929
+ firstCurvePoints[firstIndex]?.push(...intersections);
1930
+ secondCurvePoints[secondIndex]?.push(...intersections);
1890
1931
  allCommonSegments.push(...commonSegments);
1891
- allIntersections.push(...commonSegmentsPoints2);
1892
- firstCurvePoints[firstIndex].push(...commonSegmentsPoints2);
1893
- secondCurvePoints[secondIndex].push(...commonSegmentsPoints2);
1932
+ allIntersections.push(...commonSegmentsPoints);
1933
+ firstCurvePoints[firstIndex]?.push(...commonSegmentsPoints);
1934
+ secondCurvePoints[secondIndex]?.push(...commonSegmentsPoints);
1894
1935
  });
1895
1936
  });
1896
- allIntersections = Blueprint.removeDuplicatePoints(allIntersections, query.PRECISION_INTERSECTION);
1897
- if (!allIntersections.length || allIntersections.length === 1) return null;
1898
- const cutCurve = ([curve, intersections]) => {
1899
- if (!intersections.length) return [curve];
1900
- return curve.splitAt(intersections, query.PRECISION_INTERSECTION / 100);
1937
+ return {
1938
+ allIntersections: Blueprint.removeDuplicatePoints(allIntersections, helpers.PRECISION_INTERSECTION),
1939
+ allCommonSegments,
1940
+ firstCurvePoints,
1941
+ secondCurvePoints
1901
1942
  };
1902
- let firstCurveSegments = shapeHelpers.zip([first.curves, firstCurvePoints]).flatMap(cutCurve);
1903
- let secondCurveSegments = shapeHelpers.zip([second.curves, secondCurvePoints]).flatMap(cutCurve);
1943
+ }
1944
+ function splitCurvesAtIntersections(curves, curvePoints) {
1945
+ return Blueprint.zip([curves, curvePoints]).flatMap(
1946
+ ([curve, intersections]) => {
1947
+ if (intersections.length === 0) return [curve];
1948
+ return curve.splitAt(intersections, helpers.PRECISION_INTERSECTION / 100);
1949
+ }
1950
+ );
1951
+ }
1952
+ function isCommonSegmentMatch(commonSegmentsPoints, segmentStart, segmentEnd) {
1953
+ return commonSegmentsPoints.some(([startPoint, endPoint]) => {
1954
+ if (startPoint === void 0 || endPoint === void 0) return false;
1955
+ return samePoint(startPoint, segmentStart) && samePoint(endPoint, segmentEnd) || samePoint(startPoint, segmentEnd) && samePoint(startPoint, segmentStart);
1956
+ });
1957
+ }
1958
+ function blueprintsIntersectionSegments(first, second) {
1959
+ const {
1960
+ allIntersections: rawIntersections,
1961
+ allCommonSegments,
1962
+ firstCurvePoints,
1963
+ secondCurvePoints
1964
+ } = findAllIntersections(first, second);
1965
+ if (rawIntersections.length <= 1) return null;
1966
+ let firstCurveSegments = splitCurvesAtIntersections(first.curves, firstCurvePoints);
1967
+ let secondCurveSegments = splitCurvesAtIntersections(second.curves, secondCurvePoints);
1904
1968
  const commonSegmentsPoints = allCommonSegments.map((c) => [c.firstPoint, c.lastPoint]);
1905
- allIntersections = removeNonCrossingPoint(allIntersections, firstCurveSegments, second);
1906
- if (!allIntersections.length && !allCommonSegments.length) return null;
1907
- if (!allCommonSegments.length) {
1969
+ const allIntersections = removeNonCrossingPoints(rawIntersections, firstCurveSegments, second);
1970
+ if (allIntersections.length === 0 && allCommonSegments.length === 0) return null;
1971
+ if (allCommonSegments.length === 0) {
1908
1972
  const startAt = allIntersections[0];
1973
+ if (startAt === void 0) return null;
1909
1974
  firstCurveSegments = rotateToStartAt(firstCurveSegments, startAt);
1910
1975
  secondCurveSegments = rotateToStartAt(secondCurveSegments, startAt);
1911
1976
  } else {
1912
1977
  const startSegment = allCommonSegments[0];
1978
+ if (startSegment === void 0) return null;
1913
1979
  firstCurveSegments = rotateToStartAtSegment(firstCurveSegments, startSegment);
1914
1980
  secondCurveSegments = rotateToStartAtSegment(secondCurveSegments, startSegment);
1915
1981
  }
@@ -1919,130 +1985,145 @@ function blueprintsIntersectionSegments(first, second) {
1919
1985
  let secondIntersectedSegments = Array.from(
1920
1986
  createSegmentOnPoints(secondCurveSegments, allIntersections, allCommonSegments)
1921
1987
  );
1922
- if (!samePoint(
1923
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1924
- endOfSegment(secondIntersectedSegments[0]),
1925
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1926
- endOfSegment(firstIntersectedSegments[0])
1927
- ) || // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1928
- allCommonSegments.length > 0 && secondIntersectedSegments[0].length !== 1) {
1929
- secondIntersectedSegments = reverseSegments(secondIntersectedSegments);
1930
- }
1931
- return shapeHelpers.zip([firstIntersectedSegments, secondIntersectedSegments]).map(
1988
+ const firstSeg = firstIntersectedSegments[0];
1989
+ const secondSeg = secondIntersectedSegments[0];
1990
+ if (firstSeg !== void 0 && secondSeg !== void 0) {
1991
+ const endpointsMismatch = !samePoint(endOfSegment(secondSeg), endOfSegment(firstSeg));
1992
+ const commonSegmentLengthMismatch = allCommonSegments.length > 0 && secondSeg.length !== 1;
1993
+ if (endpointsMismatch || commonSegmentLengthMismatch) {
1994
+ secondIntersectedSegments = reverseSegments(secondIntersectedSegments);
1995
+ }
1996
+ }
1997
+ return Blueprint.zip([firstIntersectedSegments, secondIntersectedSegments]).map(
1932
1998
  ([first2, second2]) => {
1933
- const firstSegment = first2;
1934
- const secondSegment = second2;
1935
- const currentStart = startOfSegment(firstSegment);
1936
- const currentEnd = endOfSegment(firstSegment);
1937
- if (commonSegmentsPoints.find(([startPoint, endPoint]) => {
1938
- return (
1939
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1940
- samePoint(startPoint, currentStart) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1941
- samePoint(endPoint, currentEnd) || // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1942
- samePoint(startPoint, currentEnd) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1943
- samePoint(startPoint, currentStart)
1944
- );
1945
- })) {
1946
- return [firstSegment, "same"];
1999
+ if (first2 === void 0 || second2 === void 0) {
2000
+ result.bug("blueprintsIntersectionSegments", "Mismatched segment counts between blueprints");
2001
+ }
2002
+ const currentStart = startOfSegment(first2);
2003
+ const currentEnd = endOfSegment(first2);
2004
+ if (isCommonSegmentMatch(commonSegmentsPoints, currentStart, currentEnd)) {
2005
+ return [first2, "same"];
1947
2006
  }
1948
- return [firstSegment, secondSegment];
2007
+ return [first2, second2];
1949
2008
  }
1950
2009
  );
1951
2010
  }
1952
- const splitPaths = (curves) => {
2011
+ function splitPaths(curves) {
1953
2012
  const startPoints = curves.map((c) => c.firstPoint);
1954
- let endPoints = curves.map((c) => c.lastPoint);
1955
- endPoints = endPoints.slice(-1).concat(endPoints.slice(0, -1));
1956
- const discontinuities = shapeHelpers.zip([startPoints, endPoints]).map(([startPoint, endPoint], index) => {
1957
- if (!samePoint(startPoint, endPoint)) {
1958
- return index;
1959
- }
1960
- return null;
2013
+ const shiftedEndPoints = curves.map((c) => c.lastPoint);
2014
+ const endPoints = shiftedEndPoints.slice(-1).concat(shiftedEndPoints.slice(0, -1));
2015
+ const discontinuities = Blueprint.zip([startPoints, endPoints]).map(([startPoint, endPoint], index) => {
2016
+ if (startPoint === void 0 || endPoint === void 0) return null;
2017
+ return samePoint(startPoint, endPoint) ? null : index;
1961
2018
  }).filter((f) => f !== null);
1962
- if (!discontinuities.length) return [curves];
1963
- const paths = shapeHelpers.zip([discontinuities.slice(0, -1), discontinuities.slice(1)]).map(
1964
- ([start, end]) => {
1965
- return curves.slice(start, end);
1966
- }
2019
+ if (discontinuities.length === 0) return [curves];
2020
+ const paths = Blueprint.zip([discontinuities.slice(0, -1), discontinuities.slice(1)]).map(
2021
+ ([start, end]) => curves.slice(start, end)
1967
2022
  );
1968
2023
  let lastPath = curves.slice(discontinuities[discontinuities.length - 1]);
1969
- if (discontinuities[0] !== 0) {
1970
- lastPath = lastPath.concat(curves.slice(0, discontinuities[0]));
2024
+ const firstDiscontinuity = discontinuities[0];
2025
+ if (firstDiscontinuity !== void 0 && firstDiscontinuity !== 0) {
2026
+ lastPath = lastPath.concat(curves.slice(0, firstDiscontinuity));
1971
2027
  }
1972
2028
  paths.push(lastPath);
1973
2029
  return paths;
1974
- };
1975
- function booleanOperation(first, second, {
1976
- firstInside,
1977
- secondInside
1978
- }) {
1979
- const segments = blueprintsIntersectionSegments(first, second);
1980
- if (!segments) {
1981
- const firstBlueprintPoint = curveMidPoint(first.curves[0]);
1982
- const firstCurveInSecond = second.isInside(firstBlueprintPoint);
1983
- const secondBlueprintPoint = curveMidPoint(second.curves[0]);
1984
- const secondCurveInFirst = first.isInside(secondBlueprintPoint);
1985
- return {
1986
- identical: false,
1987
- firstCurveInSecond,
1988
- secondCurveInFirst
1989
- };
2030
+ }
2031
+ function handleSameSegment(firstSegment, segmentsIn, lastWasSame) {
2032
+ if (segmentsIn === 1) {
2033
+ return { curves: [...firstSegment], segmentsIn: 1, lastWasSame: null };
1990
2034
  }
1991
- if (segments.every(([, secondSegment]) => secondSegment === "same")) {
1992
- return { identical: true };
2035
+ if (segmentsIn === 2 || segmentsIn === 0) {
2036
+ return { curves: [], segmentsIn: null, lastWasSame: null };
1993
2037
  }
1994
- let lastWasSame = null;
1995
- let segmentsIn = null;
1996
- const s = segments.flatMap(([firstSegment, secondSegment]) => {
1997
- let segments2 = [];
1998
- let segmentsOut = 0;
1999
- if (secondSegment === "same") {
2000
- if (segmentsIn === 1) {
2001
- segmentsIn = 1;
2002
- return [...firstSegment];
2003
- }
2004
- if (segmentsIn === 2 || segmentsIn === 0) {
2005
- segmentsIn = null;
2006
- return [];
2007
- }
2008
- if (segmentsIn === null) {
2009
- if (!lastWasSame) lastWasSame = firstSegment;
2010
- else lastWasSame = [...lastWasSame, ...firstSegment];
2011
- return [];
2012
- }
2013
- return [];
2014
- }
2015
- const firstSegmentPoint = curveMidPoint(firstSegment[0]);
2016
- const firstSegmentInSecondShape = second.isInside(firstSegmentPoint);
2017
- if (firstInside === "keep" && firstSegmentInSecondShape || firstInside === "remove" && !firstSegmentInSecondShape) {
2038
+ if (segmentsIn === null) {
2039
+ const accumulated = lastWasSame ? [...lastWasSame, ...firstSegment] : firstSegment;
2040
+ return { curves: [], segmentsIn: null, lastWasSame: accumulated };
2041
+ }
2042
+ return { curves: [], segmentsIn, lastWasSame };
2043
+ }
2044
+ function selectSegments(firstSegment, secondSegment, first, second, config, segmentsIn, lastWasSame) {
2045
+ let segments = [];
2046
+ let segmentsOut = 0;
2047
+ const firstCurve = firstSegment[0];
2048
+ if (firstCurve !== void 0) {
2049
+ const firstSegmentPoint = curveMidPoint(firstCurve);
2050
+ const firstInSecond = second.isInside(firstSegmentPoint);
2051
+ if (config.firstInside === "keep" && firstInSecond || config.firstInside === "remove" && !firstInSecond) {
2018
2052
  segmentsOut += 1;
2019
- segments2.push(...firstSegment);
2053
+ segments.push(...firstSegment);
2020
2054
  }
2021
- const secondSegmentPoint = curveMidPoint(secondSegment[0]);
2022
- const secondSegmentInFirstShape = first.isInside(secondSegmentPoint);
2023
- if (secondInside === "keep" && secondSegmentInFirstShape || secondInside === "remove" && !secondSegmentInFirstShape) {
2055
+ }
2056
+ const secondCurve = secondSegment[0];
2057
+ if (secondCurve !== void 0) {
2058
+ const secondSegmentPoint = curveMidPoint(secondCurve);
2059
+ const secondInFirst = first.isInside(secondSegmentPoint);
2060
+ if (config.secondInside === "keep" && secondInFirst || config.secondInside === "remove" && !secondInFirst) {
2024
2061
  let segmentsToAdd = secondSegment;
2025
2062
  if (segmentsOut === 1) {
2026
2063
  segmentsToAdd = reverseSegment(secondSegment);
2027
2064
  }
2028
2065
  segmentsOut += 1;
2029
- segments2.push(...segmentsToAdd);
2030
- }
2031
- if (segmentsIn === null && segmentsOut === 1 && lastWasSame) {
2032
- segments2 = [...lastWasSame, ...segments2];
2033
- }
2034
- if (segmentsOut === 1) {
2035
- segmentsIn = segmentsOut;
2036
- lastWasSame = null;
2066
+ segments.push(...segmentsToAdd);
2037
2067
  }
2038
- return segments2;
2068
+ }
2069
+ if (segmentsIn === null && segmentsOut === 1 && lastWasSame !== null) {
2070
+ segments = [...lastWasSame, ...segments];
2071
+ }
2072
+ const newSegmentsIn = segmentsOut === 1 ? segmentsOut : segmentsIn;
2073
+ const newLastWasSame = segmentsOut === 1 ? null : lastWasSame;
2074
+ return { curves: segments, segmentsIn: newSegmentsIn, lastWasSame: newLastWasSame };
2075
+ }
2076
+ function booleanOperation(first, second, config) {
2077
+ const segments = blueprintsIntersectionSegments(first, second);
2078
+ if (segments === null) {
2079
+ return buildNoIntersectionResult(first, second);
2080
+ }
2081
+ if (segments.every(([, secondSegment]) => secondSegment === "same")) {
2082
+ return { identical: true };
2083
+ }
2084
+ let segmentsIn = null;
2085
+ let lastWasSame = null;
2086
+ const assembledCurves = segments.flatMap(([firstSegment, secondSegment]) => {
2087
+ if (secondSegment === "same") {
2088
+ const result22 = handleSameSegment(firstSegment, segmentsIn, lastWasSame);
2089
+ segmentsIn = result22.segmentsIn;
2090
+ lastWasSame = result22.lastWasSame;
2091
+ return result22.curves;
2092
+ }
2093
+ const result2 = selectSegments(
2094
+ firstSegment,
2095
+ secondSegment,
2096
+ first,
2097
+ second,
2098
+ config,
2099
+ segmentsIn,
2100
+ lastWasSame
2101
+ );
2102
+ segmentsIn = result2.segmentsIn;
2103
+ lastWasSame = result2.lastWasSame;
2104
+ return result2.curves;
2039
2105
  });
2040
- const paths = splitPaths(s).filter((b) => b.length).map((b) => new Blueprint.Blueprint(b));
2106
+ const paths = splitPaths(assembledCurves).filter((b) => b.length > 0).map((b) => new Blueprint.Blueprint(b));
2041
2107
  if (paths.length === 0) return null;
2042
- if (paths.length === 1) return paths[0];
2108
+ if (paths.length === 1) {
2109
+ const single = paths[0];
2110
+ if (single === void 0) return null;
2111
+ return single;
2112
+ }
2043
2113
  return organiseBlueprints(paths);
2044
2114
  }
2045
- const fuseBlueprints = (first, second) => {
2115
+ function buildNoIntersectionResult(first, second) {
2116
+ const firstCurve = first.curves[0];
2117
+ const secondCurve = second.curves[0];
2118
+ const firstCurveInSecond = firstCurve !== void 0 && second.isInside(curveMidPoint(firstCurve));
2119
+ const secondCurveInFirst = secondCurve !== void 0 && first.isInside(curveMidPoint(secondCurve));
2120
+ return {
2121
+ identical: false,
2122
+ firstCurveInSecond,
2123
+ secondCurveInFirst
2124
+ };
2125
+ }
2126
+ function fuseBlueprints(first, second) {
2046
2127
  const result2 = booleanOperation(first, second, {
2047
2128
  firstInside: "remove",
2048
2129
  secondInside: "remove"
@@ -2058,8 +2139,8 @@ const fuseBlueprints = (first, second) => {
2058
2139
  return first.clone();
2059
2140
  }
2060
2141
  return new Blueprints([first, second]);
2061
- };
2062
- const cutBlueprints = (first, second) => {
2142
+ }
2143
+ function cutBlueprints(first, second) {
2063
2144
  const result2 = booleanOperation(first, second, {
2064
2145
  firstInside: "remove",
2065
2146
  secondInside: "keep"
@@ -2075,8 +2156,8 @@ const cutBlueprints = (first, second) => {
2075
2156
  return new Blueprints([new CompoundBlueprint([first, second])]);
2076
2157
  }
2077
2158
  return first.clone();
2078
- };
2079
- const intersectBlueprints = (first, second) => {
2159
+ }
2160
+ function intersectBlueprints(first, second) {
2080
2161
  const result2 = booleanOperation(first, second, {
2081
2162
  firstInside: "keep",
2082
2163
  secondInside: "keep"
@@ -2092,7 +2173,7 @@ const intersectBlueprints = (first, second) => {
2092
2173
  return second.clone();
2093
2174
  }
2094
2175
  return null;
2095
- };
2176
+ }
2096
2177
  const genericIntersects = (first, second) => {
2097
2178
  if (first instanceof Blueprint.Blueprint && second instanceof Blueprint.Blueprint) {
2098
2179
  let allIntersections = [];