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.
- package/dist/2d/blueprints/booleanHelpers.d.ts +32 -0
- package/dist/2d/blueprints/booleanHelpers.d.ts.map +1 -0
- package/dist/2d/blueprints/booleanOperations.d.ts +5 -3
- package/dist/2d/blueprints/booleanOperations.d.ts.map +1 -1
- package/dist/2d/blueprints/intersectionSegments.d.ts +12 -0
- package/dist/2d/blueprints/intersectionSegments.d.ts.map +1 -0
- package/dist/2d/blueprints/segmentAssembly.d.ts +31 -0
- package/dist/2d/blueprints/segmentAssembly.d.ts.map +1 -0
- package/dist/2d.cjs +2 -2
- package/dist/2d.js +8 -8
- package/dist/{Blueprint-D3JfGJTz.js → Blueprint-B9fhnpFp.js} +117 -30
- package/dist/{Blueprint-CVctc41Z.cjs → Blueprint-VGbo3izk.cjs} +111 -24
- package/dist/{boolean2D-BdZATaHs.cjs → boolean2D-B1XrGVgx.cjs} +426 -345
- package/dist/{boolean2D-hOw5Qay5.js → boolean2D-_WiqPxWZ.js} +391 -310
- package/dist/{booleanFns-BBSVKhL2.cjs → booleanFns-BxW-N3rP.cjs} +12 -16
- package/dist/{booleanFns-CqehfzcK.js → booleanFns-CkccZ7UL.js} +14 -18
- package/dist/brepjs.cjs +133 -62
- package/dist/brepjs.js +290 -217
- package/dist/{cast-DQaUibmm.js → cast-C4Ff_1Qe.js} +2 -2
- package/dist/{cast-DkB0GKmQ.cjs → cast-DIiyxDLo.cjs} +2 -2
- package/dist/core/disposal.d.ts +1 -1
- package/dist/core/disposal.d.ts.map +1 -1
- package/dist/core.cjs +3 -3
- package/dist/core.js +3 -3
- package/dist/cornerFinder-BndBNtJE.cjs +58 -0
- package/dist/cornerFinder-DzGzfiqb.js +59 -0
- package/dist/curveBuilders-BUoFO1UG.cjs +196 -0
- package/dist/curveBuilders-CBlIWlbU.js +197 -0
- package/dist/{curveFns-BilyYL_s.cjs → curveFns-BrJDkaWi.cjs} +31 -44
- package/dist/{curveFns-CdVE4da7.js → curveFns-BshHA9Ys.js} +31 -44
- package/dist/{drawFns-921SkhDL.js → drawFns-Btmlh_Oz.js} +13 -14
- package/dist/{drawFns-CUyx50gi.cjs → drawFns-D2eDcf4k.cjs} +58 -59
- package/dist/{faceFns-DHu-2JpA.js → faceFns-DDzCECn3.js} +3 -3
- package/dist/{faceFns-BwK7FP7N.cjs → faceFns-NDRFeekj.cjs} +3 -3
- package/dist/helpers-Ck8GJ58k.cjs +203 -0
- package/dist/helpers-jku2V1DY.js +204 -0
- package/dist/io.cjs +4 -4
- package/dist/io.js +4 -4
- package/dist/kernel/occtAdapter.d.ts +1 -0
- package/dist/kernel/occtAdapter.d.ts.map +1 -1
- package/dist/kernel/sweepOps.d.ts +8 -0
- package/dist/kernel/sweepOps.d.ts.map +1 -1
- package/dist/kernel/types.d.ts +1 -0
- package/dist/kernel/types.d.ts.map +1 -1
- package/dist/loft-Bk9EM0gZ.js +373 -0
- package/dist/loft-DJXwxV_L.cjs +372 -0
- package/dist/{measurement-C5JGCuUP.js → measurement-DlXaTzKc.js} +3 -3
- package/dist/{measurement-fxm_pW7x.cjs → measurement-LcGh4wV0.cjs} +3 -3
- package/dist/measurement.cjs +1 -1
- package/dist/measurement.js +1 -1
- package/dist/{meshFns-AqAjTTVl.js → meshFns-Djzdn-CS.js} +1 -1
- package/dist/{meshFns-BhrZGi6w.cjs → meshFns-c8lDKfYy.cjs} +1 -1
- package/dist/{occtBoundary-du8_ex-p.cjs → occtBoundary-6kQSl3cF.cjs} +21 -0
- package/dist/{occtBoundary-CwegMzqc.js → occtBoundary-CqXvDhZY.js} +26 -5
- package/dist/operations/extrude.d.ts.map +1 -1
- package/dist/operations/extrudeFns.d.ts.map +1 -1
- package/dist/operations/extrudeUtils.d.ts +17 -0
- package/dist/operations/extrudeUtils.d.ts.map +1 -1
- package/dist/{operations-C1rWoba2.js → operations-CrQlFDHc.js} +30 -7
- package/dist/{operations-BP1wVDw0.cjs → operations-Do-WZGXc.cjs} +30 -7
- package/dist/operations.cjs +2 -2
- package/dist/operations.js +4 -4
- package/dist/query/cornerFinder.d.ts +48 -0
- package/dist/query/cornerFinder.d.ts.map +1 -0
- package/dist/query/directionUtils.d.ts +6 -0
- package/dist/query/directionUtils.d.ts.map +1 -0
- package/dist/query/edgeFinder.d.ts +15 -0
- package/dist/query/edgeFinder.d.ts.map +1 -0
- package/dist/query/faceFinder.d.ts +15 -0
- package/dist/query/faceFinder.d.ts.map +1 -0
- package/dist/query/finderCore.d.ts +35 -0
- package/dist/query/finderCore.d.ts.map +1 -0
- package/dist/query/finderFns.d.ts +21 -106
- package/dist/query/finderFns.d.ts.map +1 -1
- package/dist/query/shapeDistanceFilter.d.ts +11 -0
- package/dist/query/shapeDistanceFilter.d.ts.map +1 -0
- package/dist/query/vertexFinder.d.ts +16 -0
- package/dist/query/vertexFinder.d.ts.map +1 -0
- package/dist/query/wireFinder.d.ts +10 -0
- package/dist/query/wireFinder.d.ts.map +1 -0
- package/dist/query.cjs +42 -5
- package/dist/query.js +40 -2
- package/dist/{shapeFns-BrF97sKt.js → shapeFns-DQtpzndX.js} +17 -18
- package/dist/{shapeFns-BvOndshS.cjs → shapeFns-cN4qGpbO.cjs} +6 -7
- package/dist/{shapeTypes-DKhwEnUM.cjs → shapeTypes-BJ3Hmskg.cjs} +24 -20
- package/dist/{shapeTypes-BlSElW8z.js → shapeTypes-C9sUsmEW.js} +32 -28
- package/dist/sketching/Sketcher.d.ts.map +1 -1
- package/dist/sketching/Sketcher2d.d.ts +12 -4
- package/dist/sketching/Sketcher2d.d.ts.map +1 -1
- package/dist/sketching/ellipseUtils.d.ts +29 -0
- package/dist/sketching/ellipseUtils.d.ts.map +1 -0
- package/dist/sketching.cjs +2 -2
- package/dist/sketching.js +2 -2
- package/dist/topology/booleanFns.d.ts.map +1 -1
- package/dist/topology/curveBuilders.d.ts +75 -0
- package/dist/topology/curveBuilders.d.ts.map +1 -0
- package/dist/topology/curveFns.d.ts.map +1 -1
- package/dist/topology/primitiveFns.d.ts.map +1 -1
- package/dist/topology/shapeFns.d.ts.map +1 -1
- package/dist/topology/shapeHelpers.d.ts +6 -173
- package/dist/topology/shapeHelpers.d.ts.map +1 -1
- package/dist/topology/shapeUtils.d.ts +13 -0
- package/dist/topology/shapeUtils.d.ts.map +1 -0
- package/dist/topology/solidBuilders.d.ts +70 -0
- package/dist/topology/solidBuilders.d.ts.map +1 -0
- package/dist/topology/surfaceBuilders.d.ts +35 -0
- package/dist/topology/surfaceBuilders.d.ts.map +1 -0
- package/dist/topology/wrapperFns.d.ts +1 -0
- package/dist/topology/wrapperFns.d.ts.map +1 -1
- package/dist/{topology-tFzqSrGH.js → topology-CtfUZwLR.js} +8 -8
- package/dist/{topology-CIooytHH.cjs → topology-DXq8dLsi.cjs} +8 -8
- package/dist/topology.cjs +7 -7
- package/dist/topology.js +31 -31
- package/dist/{vectors-CBuaMeZv.js → vectors-BVgXsYWl.js} +1 -1
- package/dist/{vectors-ChWEZPwy.cjs → vectors-DK2hEKcI.cjs} +1 -1
- package/dist/vectors.cjs +2 -2
- package/dist/vectors.js +2 -2
- package/package.json +1 -1
- package/dist/loft-BzWFokmC.cjs +0 -178
- package/dist/loft-CtG5nMq5.js +0 -179
- package/dist/query-V6nV-VfL.js +0 -396
- package/dist/query-hMSmOWJP.cjs +0 -395
- package/dist/shapeHelpers-B2SXz1p4.cjs +0 -488
- package/dist/shapeHelpers-BcoZf2N9.js +0 -489
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const vectors = require("./vectors-
|
|
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-
|
|
6
|
-
const occtBoundary = require("./occtBoundary-
|
|
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-
|
|
9
|
-
const shapeTypes = require("./shapeTypes-
|
|
10
|
-
const curveFns = require("./curveFns-
|
|
11
|
-
const
|
|
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
|
|
14
|
-
const
|
|
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) =>
|
|
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) => !
|
|
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 =
|
|
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:
|
|
81
|
-
lastPoint:
|
|
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] =
|
|
98
|
-
return
|
|
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 =
|
|
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 =
|
|
160
|
-
const splitPoint =
|
|
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 =
|
|
186
|
-
const tgt2 =
|
|
187
|
-
const sinAngle =
|
|
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(
|
|
1000
|
+
face = errors.unwrap(Blueprint.makeFace(this.wire));
|
|
1001
1001
|
} else {
|
|
1002
|
-
face =
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
1265
|
-
|
|
1337
|
+
const lastCurve = this._lastCurve();
|
|
1338
|
+
if (!lastCurve) return 0;
|
|
1266
1339
|
const [dx, dy] = lastCurve.tangentAt(1);
|
|
1267
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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
|
|
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.
|
|
1331
|
-
|
|
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
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
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.
|
|
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
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
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.
|
|
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(
|
|
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 =
|
|
1395
|
-
|
|
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(
|
|
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.
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
1485
|
-
const dist =
|
|
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
|
-
|
|
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
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
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.
|
|
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.
|
|
1523
|
-
const defaultDistance =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 (!
|
|
1603
|
+
if (!helpers.samePoint(this.pointer, this.firstPoint)) {
|
|
1573
1604
|
this.lineTo(this.firstPoint);
|
|
1574
1605
|
}
|
|
1575
1606
|
}
|
|
1576
1607
|
_closeWithMirror() {
|
|
1577
|
-
if (
|
|
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
|
-
|
|
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(
|
|
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) =>
|
|
1762
|
-
|
|
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
|
-
|
|
1767
|
-
const
|
|
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 (
|
|
1772
|
-
|
|
1773
|
-
|
|
1833
|
+
if (curve === void 0) continue;
|
|
1834
|
+
if (hashPoint(curve.firstPoint) === targetHash && samePoint(point, curve.firstPoint)) {
|
|
1835
|
+
return i;
|
|
1774
1836
|
}
|
|
1775
1837
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
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 (
|
|
1789
|
-
|
|
1790
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1807
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
if (
|
|
1868
|
-
result.bug(
|
|
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
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
const
|
|
1874
|
-
return !
|
|
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
|
|
1878
|
-
|
|
1918
|
+
function findAllIntersections(first, second) {
|
|
1919
|
+
const allIntersections = [];
|
|
1879
1920
|
const allCommonSegments = [];
|
|
1880
|
-
const firstCurvePoints =
|
|
1881
|
-
const secondCurvePoints =
|
|
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
|
|
1885
|
-
intersectCurves(thisCurve, otherCurve,
|
|
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]
|
|
1889
|
-
secondCurvePoints[secondIndex]
|
|
1929
|
+
firstCurvePoints[firstIndex]?.push(...intersections);
|
|
1930
|
+
secondCurvePoints[secondIndex]?.push(...intersections);
|
|
1890
1931
|
allCommonSegments.push(...commonSegments);
|
|
1891
|
-
allIntersections.push(...
|
|
1892
|
-
firstCurvePoints[firstIndex]
|
|
1893
|
-
secondCurvePoints[secondIndex]
|
|
1932
|
+
allIntersections.push(...commonSegmentsPoints);
|
|
1933
|
+
firstCurvePoints[firstIndex]?.push(...commonSegmentsPoints);
|
|
1934
|
+
secondCurvePoints[secondIndex]?.push(...commonSegmentsPoints);
|
|
1894
1935
|
});
|
|
1895
1936
|
});
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1937
|
+
return {
|
|
1938
|
+
allIntersections: Blueprint.removeDuplicatePoints(allIntersections, helpers.PRECISION_INTERSECTION),
|
|
1939
|
+
allCommonSegments,
|
|
1940
|
+
firstCurvePoints,
|
|
1941
|
+
secondCurvePoints
|
|
1901
1942
|
};
|
|
1902
|
-
|
|
1903
|
-
|
|
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 =
|
|
1906
|
-
if (
|
|
1907
|
-
if (
|
|
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
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
}
|
|
1931
|
-
return
|
|
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
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
const
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
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 [
|
|
2007
|
+
return [first2, second2];
|
|
1949
2008
|
}
|
|
1950
2009
|
);
|
|
1951
2010
|
}
|
|
1952
|
-
|
|
2011
|
+
function splitPaths(curves) {
|
|
1953
2012
|
const startPoints = curves.map((c) => c.firstPoint);
|
|
1954
|
-
|
|
1955
|
-
endPoints =
|
|
1956
|
-
const discontinuities =
|
|
1957
|
-
if (
|
|
1958
|
-
|
|
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 (
|
|
1963
|
-
const paths =
|
|
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
|
-
|
|
1970
|
-
|
|
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
|
|
1976
|
-
|
|
1977
|
-
|
|
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 (
|
|
1992
|
-
return {
|
|
2035
|
+
if (segmentsIn === 2 || segmentsIn === 0) {
|
|
2036
|
+
return { curves: [], segmentsIn: null, lastWasSame: null };
|
|
1993
2037
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
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
|
-
|
|
2053
|
+
segments.push(...firstSegment);
|
|
2020
2054
|
}
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = [];
|