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,16 +1,16 @@
|
|
|
1
|
-
import { a as createPlane } from "./vectors-
|
|
2
|
-
import { o as makeFace, u as makeNewFaceWithinFace, a as assembleWire, z as zip } from "./shapeHelpers-BcoZf2N9.js";
|
|
1
|
+
import { a as createPlane } from "./vectors-BVgXsYWl.js";
|
|
3
2
|
import { l as ok, e as err, b as computationError, u as unwrap, g as isOk } from "./errors-wGhcJMpB.js";
|
|
4
|
-
import { d as downcast } from "./cast-
|
|
5
|
-
import { g as getKernel, a as toVec3 } from "./occtBoundary-
|
|
3
|
+
import { d as downcast } from "./cast-C4Ff_1Qe.js";
|
|
4
|
+
import { g as getKernel, a as toVec3 } from "./occtBoundary-CqXvDhZY.js";
|
|
6
5
|
import { n as vecScale, j as vecNormalize, b as vecCross, D as DEG2RAD, R as RAD2DEG } from "./vecOps-ZDdZWbwT.js";
|
|
7
|
-
import { r as revolution,
|
|
8
|
-
import {
|
|
9
|
-
import { e as curveStartPoint, a as curveTangentAt, c as curveIsClosed } from "./curveFns-
|
|
10
|
-
import { u as
|
|
6
|
+
import { r as revolution, k as complexExtrude, t as twistExtrude, i as basicFaceExtrusion, j as genericSweep, l as loft } from "./loft-Bk9EM0gZ.js";
|
|
7
|
+
import { p as gcWithScope, u as createFace, n as createWire, t as localGC, o as createEdge } from "./shapeTypes-C9sUsmEW.js";
|
|
8
|
+
import { e as curveStartPoint, a as curveTangentAt, c as curveIsClosed } from "./curveFns-BshHA9Ys.js";
|
|
9
|
+
import { C as Curve2D, i as make2dSegmentCurve, r as approximateAsBSpline, j as make2dArcFromCenter, s as isPoint2D, o as make2dCircle, t as make2dThreePointArc, e as BoundingBox2d, v as viewbox, u as asSVG, B as Blueprint, m as makeFace, d as makeNewFaceWithinFace, w as make2dEllipseArc, x as make2dTangentArc, y as make2dBezierCurve, f as axis2d, z as zip, A as removeDuplicatePoints } from "./Blueprint-B9fhnpFp.js";
|
|
10
|
+
import { u as uvBounds, p as pointOnSurface, n as normalAt } from "./faceFns-DDzCECn3.js";
|
|
11
11
|
import { bug } from "./result.js";
|
|
12
|
-
import { s as samePoint$1, n as normalize2d,
|
|
13
|
-
import {
|
|
12
|
+
import { s as samePoint$1, n as normalize2d, h as subtract2d, i as add2d, j as crossProduct2d, k as scalarMultiply2d, b as polarToCartesian, r as rotate2d, l as cartesianToPolar, d as distance2d, p as polarAngle2d, m as PRECISION_INTERSECTION } from "./helpers-jku2V1DY.js";
|
|
13
|
+
import { a as assembleWire } from "./curveBuilders-CBlIWlbU.js";
|
|
14
14
|
function* pointsIteration(intersector) {
|
|
15
15
|
const nPoints = intersector.NbPoints();
|
|
16
16
|
if (!nPoints) return;
|
|
@@ -1220,9 +1220,57 @@ function convertSvgEllipseParams([x1, y1], [x2, y2], rx, ry, phi, fA, fS) {
|
|
|
1220
1220
|
};
|
|
1221
1221
|
return outputObj;
|
|
1222
1222
|
}
|
|
1223
|
+
function normalizeEllipseRadii(horizontalRadius, verticalRadius, rotation) {
|
|
1224
|
+
if (horizontalRadius < verticalRadius) {
|
|
1225
|
+
return {
|
|
1226
|
+
majorRadius: verticalRadius,
|
|
1227
|
+
minorRadius: horizontalRadius,
|
|
1228
|
+
rotationAngle: rotation + 90
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
return {
|
|
1232
|
+
majorRadius: horizontalRadius,
|
|
1233
|
+
minorRadius: verticalRadius,
|
|
1234
|
+
rotationAngle: rotation
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
function makeEllipseArcFromSvgParams(startUV, endUV, majorRadius, minorRadius, rotationAngleDeg, longAxis, sweep, convertToUV) {
|
|
1238
|
+
const radRotationAngle = rotationAngleDeg * DEG2RAD;
|
|
1239
|
+
const convertAxis = (ax) => distance2d(convertToUV(ax));
|
|
1240
|
+
const r1 = convertAxis(polarToCartesian(majorRadius, radRotationAngle));
|
|
1241
|
+
const r2 = convertAxis(polarToCartesian(minorRadius, radRotationAngle + Math.PI / 2));
|
|
1242
|
+
const xDir = normalize2d(convertToUV(rotate2d([1, 0], radRotationAngle)));
|
|
1243
|
+
const [, newRotationAngle] = cartesianToPolar(xDir);
|
|
1244
|
+
const { cx, cy, startAngle, endAngle, clockwise, rx, ry } = convertSvgEllipseParams(
|
|
1245
|
+
startUV,
|
|
1246
|
+
endUV,
|
|
1247
|
+
r1,
|
|
1248
|
+
r2,
|
|
1249
|
+
newRotationAngle,
|
|
1250
|
+
longAxis,
|
|
1251
|
+
sweep
|
|
1252
|
+
);
|
|
1253
|
+
const arc = make2dEllipseArc(
|
|
1254
|
+
rx,
|
|
1255
|
+
ry,
|
|
1256
|
+
clockwise ? startAngle : endAngle,
|
|
1257
|
+
clockwise ? endAngle : startAngle,
|
|
1258
|
+
[cx, cy],
|
|
1259
|
+
xDir
|
|
1260
|
+
);
|
|
1261
|
+
if (!clockwise) {
|
|
1262
|
+
arc.reverse();
|
|
1263
|
+
}
|
|
1264
|
+
return arc;
|
|
1265
|
+
}
|
|
1266
|
+
const cornerModeFns = {
|
|
1267
|
+
chamfer: chamferCurves,
|
|
1268
|
+
dogbone: dogboneFilletCurves,
|
|
1269
|
+
fillet: filletCurves
|
|
1270
|
+
};
|
|
1223
1271
|
function buildCornerFunction(radius, mode) {
|
|
1224
1272
|
if (typeof radius === "function") return radius;
|
|
1225
|
-
const makeFn = mode
|
|
1273
|
+
const makeFn = cornerModeFns[mode];
|
|
1226
1274
|
return (first, second) => makeFn(first, second, radius);
|
|
1227
1275
|
}
|
|
1228
1276
|
class BaseSketcher2d {
|
|
@@ -1236,12 +1284,37 @@ class BaseSketcher2d {
|
|
|
1236
1284
|
this._nextCorner = null;
|
|
1237
1285
|
this.pendingCurves = [];
|
|
1238
1286
|
}
|
|
1287
|
+
// ── Coordinate conversion (overridden by FaceSketcher for UV mapping) ──
|
|
1239
1288
|
_convertToUV([x, y]) {
|
|
1240
1289
|
return [x, y];
|
|
1241
1290
|
}
|
|
1242
1291
|
_convertFromUV([u, v]) {
|
|
1243
1292
|
return [u, v];
|
|
1244
1293
|
}
|
|
1294
|
+
// ── Internal helpers ──
|
|
1295
|
+
/** Return the last curve in the pending list, or null if empty. */
|
|
1296
|
+
_lastCurve() {
|
|
1297
|
+
const len = this.pendingCurves.length;
|
|
1298
|
+
if (len === 0) return null;
|
|
1299
|
+
return this.pendingCurves[len - 1];
|
|
1300
|
+
}
|
|
1301
|
+
/** Require that a previous curve exists, returning it or throwing. */
|
|
1302
|
+
_requireLastCurve(caller, action) {
|
|
1303
|
+
const curve = this._lastCurve();
|
|
1304
|
+
if (!curve) bug(caller, `You need a previous curve to ${action}`);
|
|
1305
|
+
return curve;
|
|
1306
|
+
}
|
|
1307
|
+
/** Resolve a relative offset from the current pointer position. */
|
|
1308
|
+
_resolveRelative(xDist, yDist) {
|
|
1309
|
+
return [this.pointer[0] + xDist, this.pointer[1] + yDist];
|
|
1310
|
+
}
|
|
1311
|
+
/** Save a curve, advance the pointer to the given end point, and return `this`. */
|
|
1312
|
+
_saveCurveAndAdvance(curve, end) {
|
|
1313
|
+
this.saveCurve(curve);
|
|
1314
|
+
this.pointer = end;
|
|
1315
|
+
return this;
|
|
1316
|
+
}
|
|
1317
|
+
// ── Drawing state ──
|
|
1245
1318
|
/**
|
|
1246
1319
|
* Returns the current pen position as [x, y] coordinates
|
|
1247
1320
|
*
|
|
@@ -1260,11 +1333,10 @@ class BaseSketcher2d {
|
|
|
1260
1333
|
* @category Drawing State
|
|
1261
1334
|
*/
|
|
1262
1335
|
get penAngle() {
|
|
1263
|
-
|
|
1264
|
-
|
|
1336
|
+
const lastCurve = this._lastCurve();
|
|
1337
|
+
if (!lastCurve) return 0;
|
|
1265
1338
|
const [dx, dy] = lastCurve.tangentAt(1);
|
|
1266
|
-
|
|
1267
|
-
return angleInRadians * RAD2DEG;
|
|
1339
|
+
return Math.atan2(dy, dx) * RAD2DEG;
|
|
1268
1340
|
}
|
|
1269
1341
|
/** Move the pen to an absolute 2D position before drawing any curves. */
|
|
1270
1342
|
movePointerTo(point) {
|
|
@@ -1285,16 +1357,15 @@ class BaseSketcher2d {
|
|
|
1285
1357
|
this.pendingCurves.push(...this._nextCorner(previousCurve, curve));
|
|
1286
1358
|
this._nextCorner = null;
|
|
1287
1359
|
}
|
|
1360
|
+
// ── Line segments ──
|
|
1288
1361
|
/** Draw a straight line to an absolute 2D point. */
|
|
1289
1362
|
lineTo(point) {
|
|
1290
1363
|
const curve = make2dSegmentCurve(this._convertToUV(this.pointer), this._convertToUV(point));
|
|
1291
|
-
this.
|
|
1292
|
-
this.saveCurve(curve);
|
|
1293
|
-
return this;
|
|
1364
|
+
return this._saveCurveAndAdvance(curve, point);
|
|
1294
1365
|
}
|
|
1295
1366
|
/** Draw a straight line by relative horizontal and vertical distances. */
|
|
1296
1367
|
line(xDist, yDist) {
|
|
1297
|
-
return this.lineTo(
|
|
1368
|
+
return this.lineTo(this._resolveRelative(xDist, yDist));
|
|
1298
1369
|
}
|
|
1299
1370
|
/** Draw a vertical line of the given signed distance. */
|
|
1300
1371
|
vLine(distance) {
|
|
@@ -1314,41 +1385,35 @@ class BaseSketcher2d {
|
|
|
1314
1385
|
}
|
|
1315
1386
|
/** Draw a line to a point given in polar coordinates [r, theta] from the origin. */
|
|
1316
1387
|
polarLineTo([r, theta]) {
|
|
1317
|
-
|
|
1318
|
-
const point = polarToCartesian(r, angleInRads);
|
|
1319
|
-
return this.lineTo(point);
|
|
1388
|
+
return this.lineTo(polarToCartesian(r, theta * DEG2RAD));
|
|
1320
1389
|
}
|
|
1321
1390
|
/** Draw a line in polar coordinates (distance and angle in degrees) from the current point. */
|
|
1322
1391
|
polarLine(distance, angle) {
|
|
1323
|
-
const
|
|
1324
|
-
const [x, y] = polarToCartesian(distance, angleInRads);
|
|
1392
|
+
const [x, y] = polarToCartesian(distance, angle * DEG2RAD);
|
|
1325
1393
|
return this.line(x, y);
|
|
1326
1394
|
}
|
|
1327
1395
|
/** Draw a line tangent to the previous curve, extending by the given distance. */
|
|
1328
1396
|
tangentLine(distance) {
|
|
1329
|
-
const previousCurve = this.
|
|
1330
|
-
if (!previousCurve)
|
|
1331
|
-
bug("Sketcher2d.tangentLine", "You need a previous curve to sketch a tangent line");
|
|
1397
|
+
const previousCurve = this._requireLastCurve("Sketcher2d.tangentLine", "sketch a tangent line");
|
|
1332
1398
|
const direction = normalize2d(this._convertFromUV(previousCurve.tangentAt(1)));
|
|
1333
1399
|
return this.line(direction[0] * distance, direction[1] * distance);
|
|
1334
1400
|
}
|
|
1401
|
+
// ── Three-point arcs ──
|
|
1335
1402
|
/** Draw a circular arc passing through a mid-point to an absolute end point. */
|
|
1336
1403
|
threePointsArcTo(end, midPoint) {
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
this._convertToUV(end)
|
|
1342
|
-
)
|
|
1404
|
+
const curve = make2dThreePointArc(
|
|
1405
|
+
this._convertToUV(this.pointer),
|
|
1406
|
+
this._convertToUV(midPoint),
|
|
1407
|
+
this._convertToUV(end)
|
|
1343
1408
|
);
|
|
1344
|
-
this.
|
|
1345
|
-
return this;
|
|
1409
|
+
return this._saveCurveAndAdvance(curve, end);
|
|
1346
1410
|
}
|
|
1347
1411
|
/** Draw a circular arc through a via-point to an end point, both as relative distances. */
|
|
1348
1412
|
threePointsArc(xDist, yDist, viaXDist, viaYDist) {
|
|
1349
1413
|
const [x0, y0] = this.pointer;
|
|
1350
1414
|
return this.threePointsArcTo([x0 + xDist, y0 + yDist], [x0 + viaXDist, y0 + viaYDist]);
|
|
1351
1415
|
}
|
|
1416
|
+
// ── Sagitta arcs ──
|
|
1352
1417
|
/** Draw a circular arc to an absolute end point, bulging by the given sagitta. */
|
|
1353
1418
|
sagittaArcTo(end, sagitta) {
|
|
1354
1419
|
const [x0, y0] = this.pointer;
|
|
@@ -1365,19 +1430,16 @@ class BaseSketcher2d {
|
|
|
1365
1430
|
midX + sagDirX / sagDirLen * sagitta,
|
|
1366
1431
|
midY + sagDirY / sagDirLen * sagitta
|
|
1367
1432
|
];
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
this._convertToUV(end)
|
|
1373
|
-
)
|
|
1433
|
+
const curve = make2dThreePointArc(
|
|
1434
|
+
this._convertToUV(this.pointer),
|
|
1435
|
+
this._convertToUV(sagPoint),
|
|
1436
|
+
this._convertToUV(end)
|
|
1374
1437
|
);
|
|
1375
|
-
this.
|
|
1376
|
-
return this;
|
|
1438
|
+
return this._saveCurveAndAdvance(curve, end);
|
|
1377
1439
|
}
|
|
1378
1440
|
/** Draw a circular arc to a relative end point, bulging by the given sagitta. */
|
|
1379
1441
|
sagittaArc(xDist, yDist, sagitta) {
|
|
1380
|
-
return this.sagittaArcTo(
|
|
1442
|
+
return this.sagittaArcTo(this._resolveRelative(xDist, yDist), sagitta);
|
|
1381
1443
|
}
|
|
1382
1444
|
/** Draw a vertical sagitta arc of the given distance and bulge. */
|
|
1383
1445
|
vSagittaArc(distance, sagitta) {
|
|
@@ -1387,16 +1449,16 @@ class BaseSketcher2d {
|
|
|
1387
1449
|
hSagittaArc(distance, sagitta) {
|
|
1388
1450
|
return this.sagittaArc(distance, 0, sagitta);
|
|
1389
1451
|
}
|
|
1452
|
+
// ── Bulge arcs ──
|
|
1390
1453
|
/** Draw an arc to an absolute end point using a bulge factor (sagitta as fraction of half-chord). */
|
|
1391
1454
|
bulgeArcTo(end, bulge) {
|
|
1392
1455
|
if (!bulge) return this.lineTo(end);
|
|
1393
1456
|
const halfChord = distance2d(this.pointer, end) / 2;
|
|
1394
|
-
|
|
1395
|
-
return this.sagittaArcTo(end, bulgeAsSagitta);
|
|
1457
|
+
return this.sagittaArcTo(end, -bulge * halfChord);
|
|
1396
1458
|
}
|
|
1397
1459
|
/** Draw an arc to a relative end point using a bulge factor. */
|
|
1398
1460
|
bulgeArc(xDist, yDist, bulge) {
|
|
1399
|
-
return this.bulgeArcTo(
|
|
1461
|
+
return this.bulgeArcTo(this._resolveRelative(xDist, yDist), bulge);
|
|
1400
1462
|
}
|
|
1401
1463
|
/** Draw a vertical bulge arc of the given distance and bulge factor. */
|
|
1402
1464
|
vBulgeArc(distance, bulge) {
|
|
@@ -1406,71 +1468,45 @@ class BaseSketcher2d {
|
|
|
1406
1468
|
hBulgeArc(distance, bulge) {
|
|
1407
1469
|
return this.bulgeArc(distance, 0, bulge);
|
|
1408
1470
|
}
|
|
1471
|
+
// ── Tangent arcs ──
|
|
1409
1472
|
/** Draw a circular arc tangent to the previous curve, ending at an absolute point. */
|
|
1410
1473
|
tangentArcTo(end) {
|
|
1411
|
-
const previousCurve = this.
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
this._convertToUV(this.pointer),
|
|
1417
|
-
previousCurve.tangentAt(1),
|
|
1418
|
-
this._convertToUV(end)
|
|
1419
|
-
)
|
|
1474
|
+
const previousCurve = this._requireLastCurve("Sketcher2d.tangentArc", "sketch a tangent arc");
|
|
1475
|
+
const curve = make2dTangentArc(
|
|
1476
|
+
this._convertToUV(this.pointer),
|
|
1477
|
+
previousCurve.tangentAt(1),
|
|
1478
|
+
this._convertToUV(end)
|
|
1420
1479
|
);
|
|
1421
|
-
this.
|
|
1422
|
-
return this;
|
|
1480
|
+
return this._saveCurveAndAdvance(curve, end);
|
|
1423
1481
|
}
|
|
1424
1482
|
/** Draw a circular arc tangent to the previous curve, ending at a relative offset. */
|
|
1425
1483
|
tangentArc(xDist, yDist) {
|
|
1426
|
-
|
|
1427
|
-
return this.tangentArcTo([xDist + x0, yDist + y0]);
|
|
1484
|
+
return this.tangentArcTo(this._resolveRelative(xDist, yDist));
|
|
1428
1485
|
}
|
|
1486
|
+
// ── Ellipse arcs ──
|
|
1429
1487
|
/** Draw an elliptical arc to an absolute end point (SVG-style parameters). */
|
|
1430
1488
|
ellipseTo(end, horizontalRadius, verticalRadius, rotation = 0, longAxis = false, sweep = false) {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
minorRadius = horizontalRadius;
|
|
1438
|
-
}
|
|
1439
|
-
const radRotationAngle = rotationAngle * DEG2RAD;
|
|
1440
|
-
const convertAxis = (ax) => distance2d(this._convertToUV(ax));
|
|
1441
|
-
const r1 = convertAxis(polarToCartesian(majorRadius, radRotationAngle));
|
|
1442
|
-
const r2 = convertAxis(polarToCartesian(minorRadius, radRotationAngle + Math.PI / 2));
|
|
1443
|
-
const xDir = normalize2d(this._convertToUV(rotate2d([1, 0], radRotationAngle)));
|
|
1444
|
-
const [, newRotationAngle] = cartesianToPolar(xDir);
|
|
1445
|
-
const { cx, cy, startAngle, endAngle, clockwise, rx, ry } = convertSvgEllipseParams(
|
|
1489
|
+
const { majorRadius, minorRadius, rotationAngle } = normalizeEllipseRadii(
|
|
1490
|
+
horizontalRadius,
|
|
1491
|
+
verticalRadius,
|
|
1492
|
+
rotation
|
|
1493
|
+
);
|
|
1494
|
+
const arc = makeEllipseArcFromSvgParams(
|
|
1446
1495
|
this._convertToUV(this.pointer),
|
|
1447
1496
|
this._convertToUV(end),
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1497
|
+
majorRadius,
|
|
1498
|
+
minorRadius,
|
|
1499
|
+
rotationAngle,
|
|
1451
1500
|
longAxis,
|
|
1452
|
-
sweep
|
|
1453
|
-
|
|
1454
|
-
const arc = make2dEllipseArc(
|
|
1455
|
-
rx,
|
|
1456
|
-
ry,
|
|
1457
|
-
clockwise ? startAngle : endAngle,
|
|
1458
|
-
clockwise ? endAngle : startAngle,
|
|
1459
|
-
[cx, cy],
|
|
1460
|
-
xDir
|
|
1501
|
+
sweep,
|
|
1502
|
+
(p) => this._convertToUV(p)
|
|
1461
1503
|
);
|
|
1462
|
-
|
|
1463
|
-
arc.reverse();
|
|
1464
|
-
}
|
|
1465
|
-
this.saveCurve(arc);
|
|
1466
|
-
this.pointer = end;
|
|
1467
|
-
return this;
|
|
1504
|
+
return this._saveCurveAndAdvance(arc, end);
|
|
1468
1505
|
}
|
|
1469
1506
|
/** Draw an elliptical arc to a relative end point (SVG-style parameters). */
|
|
1470
1507
|
ellipse(xDist, yDist, horizontalRadius, verticalRadius, rotation = 0, longAxis = false, sweep = false) {
|
|
1471
|
-
const [x0, y0] = this.pointer;
|
|
1472
1508
|
return this.ellipseTo(
|
|
1473
|
-
|
|
1509
|
+
this._resolveRelative(xDist, yDist),
|
|
1474
1510
|
horizontalRadius,
|
|
1475
1511
|
verticalRadius,
|
|
1476
1512
|
rotation,
|
|
@@ -1486,26 +1522,18 @@ class BaseSketcher2d {
|
|
|
1486
1522
|
}
|
|
1487
1523
|
/** Draw a half-ellipse arc to a relative end point with a given minor radius. */
|
|
1488
1524
|
halfEllipse(xDist, yDist, minorRadius, sweep = false) {
|
|
1489
|
-
|
|
1490
|
-
return this.halfEllipseTo([x0 + xDist, y0 + yDist], minorRadius, sweep);
|
|
1525
|
+
return this.halfEllipseTo(this._resolveRelative(xDist, yDist), minorRadius, sweep);
|
|
1491
1526
|
}
|
|
1527
|
+
// ── Bezier curves ──
|
|
1492
1528
|
/** Draw a Bezier curve to an absolute end point through one or more control points. */
|
|
1493
1529
|
bezierCurveTo(end, controlPoints) {
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
}
|
|
1500
|
-
this.saveCurve(
|
|
1501
|
-
make2dBezierCurve(
|
|
1502
|
-
this._convertToUV(this.pointer),
|
|
1503
|
-
cp.map((point) => this._convertToUV(point)),
|
|
1504
|
-
this._convertToUV(end)
|
|
1505
|
-
)
|
|
1530
|
+
const cp = controlPoints.length === 2 && !Array.isArray(controlPoints[0]) ? [controlPoints] : controlPoints;
|
|
1531
|
+
const curve = make2dBezierCurve(
|
|
1532
|
+
this._convertToUV(this.pointer),
|
|
1533
|
+
cp.map((point) => this._convertToUV(point)),
|
|
1534
|
+
this._convertToUV(end)
|
|
1506
1535
|
);
|
|
1507
|
-
this.
|
|
1508
|
-
return this;
|
|
1536
|
+
return this._saveCurveAndAdvance(curve, end);
|
|
1509
1537
|
}
|
|
1510
1538
|
/** Draw a quadratic Bezier curve to an absolute end point with a single control point. */
|
|
1511
1539
|
quadraticBezierCurveTo(end, controlPoint) {
|
|
@@ -1515,10 +1543,11 @@ class BaseSketcher2d {
|
|
|
1515
1543
|
cubicBezierCurveTo(end, startControlPoint, endControlPoint) {
|
|
1516
1544
|
return this.bezierCurveTo(end, [startControlPoint, endControlPoint]);
|
|
1517
1545
|
}
|
|
1546
|
+
// ── Smooth splines ──
|
|
1518
1547
|
/** Draw a smooth cubic Bezier spline to an absolute end point, blending tangent with the previous curve. */
|
|
1519
1548
|
smoothSplineTo(end, config) {
|
|
1520
1549
|
const { endTangent, startTangent, startFactor, endFactor } = defaultsSplineOptions(config);
|
|
1521
|
-
const previousCurve = this.
|
|
1550
|
+
const previousCurve = this._lastCurve();
|
|
1522
1551
|
const defaultDistance = distance2d(this.pointer, end) * 0.25;
|
|
1523
1552
|
let startPoleDirection;
|
|
1524
1553
|
if (startTangent) {
|
|
@@ -1548,8 +1577,9 @@ class BaseSketcher2d {
|
|
|
1548
1577
|
}
|
|
1549
1578
|
/** Draw a smooth cubic Bezier spline to a relative end point, blending tangent with the previous curve. */
|
|
1550
1579
|
smoothSpline(xDist, yDist, splineConfig) {
|
|
1551
|
-
return this.smoothSplineTo(
|
|
1580
|
+
return this.smoothSplineTo(this._resolveRelative(xDist, yDist), splineConfig);
|
|
1552
1581
|
}
|
|
1582
|
+
// ── Corner treatments ──
|
|
1553
1583
|
/**
|
|
1554
1584
|
* Changes the corner between the previous and next segments.
|
|
1555
1585
|
*/
|
|
@@ -1567,6 +1597,7 @@ class BaseSketcher2d {
|
|
|
1567
1597
|
bug("Sketcher2d._customCornerLastWithFirst", "Not enough curves to close and fillet");
|
|
1568
1598
|
this.pendingCurves.push(...buildCornerFunction(radius, mode)(previousCurve, curve));
|
|
1569
1599
|
}
|
|
1600
|
+
// ── Close / mirror helpers ──
|
|
1570
1601
|
_closeSketch() {
|
|
1571
1602
|
if (!samePoint$1(this.pointer, this.firstPoint)) {
|
|
1572
1603
|
this.lineTo(this.firstPoint);
|
|
@@ -1587,9 +1618,9 @@ class BaseSketcher2d {
|
|
|
1587
1618
|
(c) => new Curve2D(c.innerCurve.Mirrored_2(mirrorAxis))
|
|
1588
1619
|
);
|
|
1589
1620
|
mirroredCurves.reverse();
|
|
1590
|
-
|
|
1621
|
+
for (const c of mirroredCurves) {
|
|
1591
1622
|
c.reverse();
|
|
1592
|
-
}
|
|
1623
|
+
}
|
|
1593
1624
|
this.pendingCurves.push(...mirroredCurves);
|
|
1594
1625
|
this.pointer = this.firstPoint;
|
|
1595
1626
|
}
|
|
@@ -1758,77 +1789,100 @@ const roundedRectangleBlueprint = (width, height, r = 0) => {
|
|
|
1758
1789
|
return sk.close();
|
|
1759
1790
|
};
|
|
1760
1791
|
const samePoint = (x, y) => samePoint$1(x, y, PRECISION_INTERSECTION);
|
|
1761
|
-
|
|
1792
|
+
function hashPoint(p) {
|
|
1793
|
+
return `${p[0].toFixed(9)},${p[1].toFixed(9)}`;
|
|
1794
|
+
}
|
|
1795
|
+
function hashSegment(first, last) {
|
|
1796
|
+
const h1 = hashPoint(first);
|
|
1797
|
+
const h2 = hashPoint(last);
|
|
1798
|
+
return h1 < h2 ? `${h1}|${h2}` : `${h2}|${h1}`;
|
|
1799
|
+
}
|
|
1800
|
+
function startOfSegment(s) {
|
|
1801
|
+
const first = s[0];
|
|
1802
|
+
if (first === void 0) {
|
|
1803
|
+
bug("startOfSegment", "empty segment");
|
|
1804
|
+
}
|
|
1805
|
+
return first.firstPoint;
|
|
1806
|
+
}
|
|
1807
|
+
function endOfSegment(s) {
|
|
1808
|
+
const last = s[s.length - 1];
|
|
1809
|
+
if (last === void 0) {
|
|
1810
|
+
bug("endOfSegment", "empty segment");
|
|
1811
|
+
}
|
|
1812
|
+
return last.lastPoint;
|
|
1813
|
+
}
|
|
1814
|
+
function reverseSegment(segment) {
|
|
1815
|
+
return [...segment].reverse().map((curve) => {
|
|
1816
|
+
const newCurve = curve.clone();
|
|
1817
|
+
newCurve.reverse();
|
|
1818
|
+
return newCurve;
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
function reverseSegments(segments) {
|
|
1822
|
+
return [...segments].reverse().map(reverseSegment);
|
|
1823
|
+
}
|
|
1824
|
+
function curveMidPoint(curve) {
|
|
1762
1825
|
const midParameter = (curve.lastParameter + curve.firstParameter) / 2;
|
|
1763
1826
|
return curve.value(midParameter);
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
const
|
|
1767
|
-
let startIndex = -1;
|
|
1827
|
+
}
|
|
1828
|
+
function findCurveIndexByStartPoint(curves, point) {
|
|
1829
|
+
const targetHash = hashPoint(point);
|
|
1768
1830
|
for (let i = 0; i < curves.length; i++) {
|
|
1769
1831
|
const curve = curves[i];
|
|
1770
|
-
if (
|
|
1771
|
-
|
|
1772
|
-
|
|
1832
|
+
if (curve === void 0) continue;
|
|
1833
|
+
if (hashPoint(curve.firstPoint) === targetHash && samePoint(point, curve.firstPoint)) {
|
|
1834
|
+
return i;
|
|
1773
1835
|
}
|
|
1774
1836
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
const rotateToStartAtSegment = (curves, segment) => {
|
|
1779
|
-
const segFirstHash = hashPoint(segment.firstPoint);
|
|
1780
|
-
const segLastHash = hashPoint(segment.lastPoint);
|
|
1781
|
-
const onSegment = (curve) => {
|
|
1782
|
-
return samePoint(segment.firstPoint, curve.firstPoint) && samePoint(segment.lastPoint, curve.lastPoint);
|
|
1783
|
-
};
|
|
1784
|
-
let startIndex = -1;
|
|
1837
|
+
return -1;
|
|
1838
|
+
}
|
|
1839
|
+
function findCurveIndexBySegment(curves, segFirstHash, segLastHash, matchesFn) {
|
|
1785
1840
|
for (let i = 0; i < curves.length; i++) {
|
|
1786
1841
|
const curve = curves[i];
|
|
1787
|
-
if (
|
|
1788
|
-
|
|
1789
|
-
|
|
1842
|
+
if (curve === void 0) continue;
|
|
1843
|
+
if (hashPoint(curve.firstPoint) === segFirstHash && hashPoint(curve.lastPoint) === segLastHash && matchesFn(curve)) {
|
|
1844
|
+
return i;
|
|
1790
1845
|
}
|
|
1791
1846
|
}
|
|
1847
|
+
return -1;
|
|
1848
|
+
}
|
|
1849
|
+
function rotateArray(arr, startIndex) {
|
|
1850
|
+
if (startIndex <= 0) return arr;
|
|
1851
|
+
return arr.slice(startIndex).concat(arr.slice(0, startIndex));
|
|
1852
|
+
}
|
|
1853
|
+
function rotateToStartAt(curves, point) {
|
|
1854
|
+
const startIndex = findCurveIndexByStartPoint(curves, point);
|
|
1855
|
+
return rotateArray(curves, startIndex);
|
|
1856
|
+
}
|
|
1857
|
+
function rotateToStartAtSegment(curves, segment) {
|
|
1858
|
+
const segFirstHash = hashPoint(segment.firstPoint);
|
|
1859
|
+
const segLastHash = hashPoint(segment.lastPoint);
|
|
1860
|
+
const onSegment = (curve) => samePoint(segment.firstPoint, curve.firstPoint) && samePoint(segment.lastPoint, curve.lastPoint);
|
|
1861
|
+
let startIndex = findCurveIndexBySegment(curves, segFirstHash, segLastHash, onSegment);
|
|
1862
|
+
if (startIndex !== -1) {
|
|
1863
|
+
return rotateArray(curves, startIndex);
|
|
1864
|
+
}
|
|
1865
|
+
const reversed = reverseSegment(curves);
|
|
1866
|
+
startIndex = findCurveIndexBySegment(reversed, segFirstHash, segLastHash, onSegment);
|
|
1792
1867
|
if (startIndex === -1) {
|
|
1793
|
-
|
|
1794
|
-
for (let i = 0; i < curves.length; i++) {
|
|
1795
|
-
const curve = curves[i];
|
|
1796
|
-
if (hashPoint(curve.firstPoint) === segFirstHash && hashPoint(curve.lastPoint) === segLastHash && onSegment(curve)) {
|
|
1797
|
-
startIndex = i;
|
|
1798
|
-
break;
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
if (startIndex === -1) {
|
|
1802
|
-
bug("rotateToStartAtSegment", "Failed to rotate to segment start");
|
|
1803
|
-
}
|
|
1868
|
+
bug("rotateToStartAtSegment", "failed to rotate to segment start");
|
|
1804
1869
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
};
|
|
1808
|
-
const hashPoint = (p) => `${p[0].toFixed(9)},${p[1].toFixed(9)}`;
|
|
1809
|
-
const hashSegment = (first, last) => {
|
|
1810
|
-
const h1 = hashPoint(first);
|
|
1811
|
-
const h2 = hashPoint(last);
|
|
1812
|
-
return h1 < h2 ? `${h1}|${h2}` : `${h2}|${h1}`;
|
|
1813
|
-
};
|
|
1870
|
+
return rotateArray(reversed, startIndex);
|
|
1871
|
+
}
|
|
1814
1872
|
function* createSegmentOnPoints(curves, allIntersections, allCommonSegments) {
|
|
1815
1873
|
const intersectionSet = new Set(allIntersections.map(hashPoint));
|
|
1816
1874
|
const commonSegmentSet = new Set(
|
|
1817
1875
|
allCommonSegments.map((seg) => hashSegment(seg.firstPoint, seg.lastPoint))
|
|
1818
1876
|
);
|
|
1819
|
-
const endsAtIntersection = (curve) => {
|
|
1820
|
-
return intersectionSet.has(hashPoint(curve.lastPoint));
|
|
1821
|
-
};
|
|
1822
|
-
const isCommonSegment = (curve) => {
|
|
1823
|
-
return commonSegmentSet.has(hashSegment(curve.firstPoint, curve.lastPoint));
|
|
1824
|
-
};
|
|
1825
1877
|
let currentCurves = [];
|
|
1826
1878
|
for (const curve of curves) {
|
|
1827
|
-
|
|
1879
|
+
const endsAtIntersection = intersectionSet.has(hashPoint(curve.lastPoint));
|
|
1880
|
+
const isCommon = commonSegmentSet.has(hashSegment(curve.firstPoint, curve.lastPoint));
|
|
1881
|
+
if (endsAtIntersection) {
|
|
1828
1882
|
currentCurves.push(curve);
|
|
1829
1883
|
yield currentCurves;
|
|
1830
1884
|
currentCurves = [];
|
|
1831
|
-
} else if (
|
|
1885
|
+
} else if (isCommon) {
|
|
1832
1886
|
if (currentCurves.length) {
|
|
1833
1887
|
yield currentCurves;
|
|
1834
1888
|
currentCurves = [];
|
|
@@ -1842,73 +1896,85 @@ function* createSegmentOnPoints(curves, allIntersections, allCommonSegments) {
|
|
|
1842
1896
|
yield currentCurves;
|
|
1843
1897
|
}
|
|
1844
1898
|
}
|
|
1845
|
-
|
|
1846
|
-
return s[0].firstPoint;
|
|
1847
|
-
};
|
|
1848
|
-
const endOfSegment = (s) => {
|
|
1849
|
-
return s[s.length - 1].lastPoint;
|
|
1850
|
-
};
|
|
1851
|
-
const reverseSegment = (segment) => {
|
|
1852
|
-
return [...segment].reverse().map((curve) => {
|
|
1853
|
-
const newCurve = curve.clone();
|
|
1854
|
-
newCurve.reverse();
|
|
1855
|
-
return newCurve;
|
|
1856
|
-
});
|
|
1857
|
-
};
|
|
1858
|
-
const reverseSegments = (s) => {
|
|
1859
|
-
return [...s].reverse().map(reverseSegment);
|
|
1860
|
-
};
|
|
1861
|
-
function removeNonCrossingPoint(allIntersections, segmentedCurve, blueprintToCheck) {
|
|
1899
|
+
function removeNonCrossingPoints(allIntersections, segmentedCurve, blueprintToCheck) {
|
|
1862
1900
|
return allIntersections.filter((intersection) => {
|
|
1863
|
-
const
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
if (
|
|
1867
|
-
bug(
|
|
1901
|
+
const touching = segmentedCurve.filter(
|
|
1902
|
+
(s) => samePoint(s.firstPoint, intersection) || samePoint(s.lastPoint, intersection)
|
|
1903
|
+
);
|
|
1904
|
+
if (touching.length % 2) {
|
|
1905
|
+
bug(
|
|
1906
|
+
"removeNonCrossingPoints",
|
|
1907
|
+
"Odd number of segments at intersection point (expected even)"
|
|
1908
|
+
);
|
|
1868
1909
|
}
|
|
1869
|
-
const
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
const
|
|
1873
|
-
return !
|
|
1910
|
+
const insideFlags = touching.map(
|
|
1911
|
+
(segment) => blueprintToCheck.isInside(curveMidPoint(segment))
|
|
1912
|
+
);
|
|
1913
|
+
const allSameSide = insideFlags.every(Boolean) || insideFlags.every((f) => !f);
|
|
1914
|
+
return !allSameSide;
|
|
1874
1915
|
});
|
|
1875
1916
|
}
|
|
1876
|
-
function
|
|
1877
|
-
|
|
1917
|
+
function findAllIntersections(first, second) {
|
|
1918
|
+
const allIntersections = [];
|
|
1878
1919
|
const allCommonSegments = [];
|
|
1879
|
-
const firstCurvePoints =
|
|
1880
|
-
const secondCurvePoints =
|
|
1920
|
+
const firstCurvePoints = first.curves.map(() => []);
|
|
1921
|
+
const secondCurvePoints = second.curves.map(() => []);
|
|
1881
1922
|
first.curves.forEach((thisCurve, firstIndex) => {
|
|
1882
1923
|
second.curves.forEach((otherCurve, secondIndex) => {
|
|
1883
|
-
const { intersections, commonSegments, commonSegmentsPoints
|
|
1924
|
+
const { intersections, commonSegments, commonSegmentsPoints } = unwrap(
|
|
1884
1925
|
intersectCurves(thisCurve, otherCurve, PRECISION_INTERSECTION / 100)
|
|
1885
1926
|
);
|
|
1886
1927
|
allIntersections.push(...intersections);
|
|
1887
|
-
firstCurvePoints[firstIndex]
|
|
1888
|
-
secondCurvePoints[secondIndex]
|
|
1928
|
+
firstCurvePoints[firstIndex]?.push(...intersections);
|
|
1929
|
+
secondCurvePoints[secondIndex]?.push(...intersections);
|
|
1889
1930
|
allCommonSegments.push(...commonSegments);
|
|
1890
|
-
allIntersections.push(...
|
|
1891
|
-
firstCurvePoints[firstIndex]
|
|
1892
|
-
secondCurvePoints[secondIndex]
|
|
1931
|
+
allIntersections.push(...commonSegmentsPoints);
|
|
1932
|
+
firstCurvePoints[firstIndex]?.push(...commonSegmentsPoints);
|
|
1933
|
+
secondCurvePoints[secondIndex]?.push(...commonSegmentsPoints);
|
|
1893
1934
|
});
|
|
1894
1935
|
});
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1936
|
+
return {
|
|
1937
|
+
allIntersections: removeDuplicatePoints(allIntersections, PRECISION_INTERSECTION),
|
|
1938
|
+
allCommonSegments,
|
|
1939
|
+
firstCurvePoints,
|
|
1940
|
+
secondCurvePoints
|
|
1900
1941
|
};
|
|
1901
|
-
|
|
1902
|
-
|
|
1942
|
+
}
|
|
1943
|
+
function splitCurvesAtIntersections(curves, curvePoints) {
|
|
1944
|
+
return zip([curves, curvePoints]).flatMap(
|
|
1945
|
+
([curve, intersections]) => {
|
|
1946
|
+
if (intersections.length === 0) return [curve];
|
|
1947
|
+
return curve.splitAt(intersections, PRECISION_INTERSECTION / 100);
|
|
1948
|
+
}
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
function isCommonSegmentMatch(commonSegmentsPoints, segmentStart, segmentEnd) {
|
|
1952
|
+
return commonSegmentsPoints.some(([startPoint, endPoint]) => {
|
|
1953
|
+
if (startPoint === void 0 || endPoint === void 0) return false;
|
|
1954
|
+
return samePoint(startPoint, segmentStart) && samePoint(endPoint, segmentEnd) || samePoint(startPoint, segmentEnd) && samePoint(startPoint, segmentStart);
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
function blueprintsIntersectionSegments(first, second) {
|
|
1958
|
+
const {
|
|
1959
|
+
allIntersections: rawIntersections,
|
|
1960
|
+
allCommonSegments,
|
|
1961
|
+
firstCurvePoints,
|
|
1962
|
+
secondCurvePoints
|
|
1963
|
+
} = findAllIntersections(first, second);
|
|
1964
|
+
if (rawIntersections.length <= 1) return null;
|
|
1965
|
+
let firstCurveSegments = splitCurvesAtIntersections(first.curves, firstCurvePoints);
|
|
1966
|
+
let secondCurveSegments = splitCurvesAtIntersections(second.curves, secondCurvePoints);
|
|
1903
1967
|
const commonSegmentsPoints = allCommonSegments.map((c) => [c.firstPoint, c.lastPoint]);
|
|
1904
|
-
allIntersections =
|
|
1905
|
-
if (
|
|
1906
|
-
if (
|
|
1968
|
+
const allIntersections = removeNonCrossingPoints(rawIntersections, firstCurveSegments, second);
|
|
1969
|
+
if (allIntersections.length === 0 && allCommonSegments.length === 0) return null;
|
|
1970
|
+
if (allCommonSegments.length === 0) {
|
|
1907
1971
|
const startAt = allIntersections[0];
|
|
1972
|
+
if (startAt === void 0) return null;
|
|
1908
1973
|
firstCurveSegments = rotateToStartAt(firstCurveSegments, startAt);
|
|
1909
1974
|
secondCurveSegments = rotateToStartAt(secondCurveSegments, startAt);
|
|
1910
1975
|
} else {
|
|
1911
1976
|
const startSegment = allCommonSegments[0];
|
|
1977
|
+
if (startSegment === void 0) return null;
|
|
1912
1978
|
firstCurveSegments = rotateToStartAtSegment(firstCurveSegments, startSegment);
|
|
1913
1979
|
secondCurveSegments = rotateToStartAtSegment(secondCurveSegments, startSegment);
|
|
1914
1980
|
}
|
|
@@ -1918,130 +1984,145 @@ function blueprintsIntersectionSegments(first, second) {
|
|
|
1918
1984
|
let secondIntersectedSegments = Array.from(
|
|
1919
1985
|
createSegmentOnPoints(secondCurveSegments, allIntersections, allCommonSegments)
|
|
1920
1986
|
);
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1987
|
+
const firstSeg = firstIntersectedSegments[0];
|
|
1988
|
+
const secondSeg = secondIntersectedSegments[0];
|
|
1989
|
+
if (firstSeg !== void 0 && secondSeg !== void 0) {
|
|
1990
|
+
const endpointsMismatch = !samePoint(endOfSegment(secondSeg), endOfSegment(firstSeg));
|
|
1991
|
+
const commonSegmentLengthMismatch = allCommonSegments.length > 0 && secondSeg.length !== 1;
|
|
1992
|
+
if (endpointsMismatch || commonSegmentLengthMismatch) {
|
|
1993
|
+
secondIntersectedSegments = reverseSegments(secondIntersectedSegments);
|
|
1994
|
+
}
|
|
1929
1995
|
}
|
|
1930
1996
|
return zip([firstIntersectedSegments, secondIntersectedSegments]).map(
|
|
1931
1997
|
([first2, second2]) => {
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
const currentStart = startOfSegment(firstSegment);
|
|
1935
|
-
const currentEnd = endOfSegment(firstSegment);
|
|
1936
|
-
if (commonSegmentsPoints.find(([startPoint, endPoint]) => {
|
|
1937
|
-
return (
|
|
1938
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1939
|
-
samePoint(startPoint, currentStart) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1940
|
-
samePoint(endPoint, currentEnd) || // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1941
|
-
samePoint(startPoint, currentEnd) && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1942
|
-
samePoint(startPoint, currentStart)
|
|
1943
|
-
);
|
|
1944
|
-
})) {
|
|
1945
|
-
return [firstSegment, "same"];
|
|
1998
|
+
if (first2 === void 0 || second2 === void 0) {
|
|
1999
|
+
bug("blueprintsIntersectionSegments", "Mismatched segment counts between blueprints");
|
|
1946
2000
|
}
|
|
1947
|
-
|
|
2001
|
+
const currentStart = startOfSegment(first2);
|
|
2002
|
+
const currentEnd = endOfSegment(first2);
|
|
2003
|
+
if (isCommonSegmentMatch(commonSegmentsPoints, currentStart, currentEnd)) {
|
|
2004
|
+
return [first2, "same"];
|
|
2005
|
+
}
|
|
2006
|
+
return [first2, second2];
|
|
1948
2007
|
}
|
|
1949
2008
|
);
|
|
1950
2009
|
}
|
|
1951
|
-
|
|
2010
|
+
function splitPaths(curves) {
|
|
1952
2011
|
const startPoints = curves.map((c) => c.firstPoint);
|
|
1953
|
-
|
|
1954
|
-
endPoints =
|
|
2012
|
+
const shiftedEndPoints = curves.map((c) => c.lastPoint);
|
|
2013
|
+
const endPoints = shiftedEndPoints.slice(-1).concat(shiftedEndPoints.slice(0, -1));
|
|
1955
2014
|
const discontinuities = zip([startPoints, endPoints]).map(([startPoint, endPoint], index) => {
|
|
1956
|
-
if (
|
|
1957
|
-
|
|
1958
|
-
}
|
|
1959
|
-
return null;
|
|
2015
|
+
if (startPoint === void 0 || endPoint === void 0) return null;
|
|
2016
|
+
return samePoint(startPoint, endPoint) ? null : index;
|
|
1960
2017
|
}).filter((f) => f !== null);
|
|
1961
|
-
if (
|
|
2018
|
+
if (discontinuities.length === 0) return [curves];
|
|
1962
2019
|
const paths = zip([discontinuities.slice(0, -1), discontinuities.slice(1)]).map(
|
|
1963
|
-
([start, end]) =>
|
|
1964
|
-
return curves.slice(start, end);
|
|
1965
|
-
}
|
|
2020
|
+
([start, end]) => curves.slice(start, end)
|
|
1966
2021
|
);
|
|
1967
2022
|
let lastPath = curves.slice(discontinuities[discontinuities.length - 1]);
|
|
1968
|
-
|
|
1969
|
-
|
|
2023
|
+
const firstDiscontinuity = discontinuities[0];
|
|
2024
|
+
if (firstDiscontinuity !== void 0 && firstDiscontinuity !== 0) {
|
|
2025
|
+
lastPath = lastPath.concat(curves.slice(0, firstDiscontinuity));
|
|
1970
2026
|
}
|
|
1971
2027
|
paths.push(lastPath);
|
|
1972
2028
|
return paths;
|
|
1973
|
-
}
|
|
1974
|
-
function
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
}) {
|
|
1978
|
-
const segments = blueprintsIntersectionSegments(first, second);
|
|
1979
|
-
if (!segments) {
|
|
1980
|
-
const firstBlueprintPoint = curveMidPoint(first.curves[0]);
|
|
1981
|
-
const firstCurveInSecond = second.isInside(firstBlueprintPoint);
|
|
1982
|
-
const secondBlueprintPoint = curveMidPoint(second.curves[0]);
|
|
1983
|
-
const secondCurveInFirst = first.isInside(secondBlueprintPoint);
|
|
1984
|
-
return {
|
|
1985
|
-
identical: false,
|
|
1986
|
-
firstCurveInSecond,
|
|
1987
|
-
secondCurveInFirst
|
|
1988
|
-
};
|
|
2029
|
+
}
|
|
2030
|
+
function handleSameSegment(firstSegment, segmentsIn, lastWasSame) {
|
|
2031
|
+
if (segmentsIn === 1) {
|
|
2032
|
+
return { curves: [...firstSegment], segmentsIn: 1, lastWasSame: null };
|
|
1989
2033
|
}
|
|
1990
|
-
if (
|
|
1991
|
-
return {
|
|
2034
|
+
if (segmentsIn === 2 || segmentsIn === 0) {
|
|
2035
|
+
return { curves: [], segmentsIn: null, lastWasSame: null };
|
|
1992
2036
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
if (segmentsIn === null) {
|
|
2008
|
-
if (!lastWasSame) lastWasSame = firstSegment;
|
|
2009
|
-
else lastWasSame = [...lastWasSame, ...firstSegment];
|
|
2010
|
-
return [];
|
|
2011
|
-
}
|
|
2012
|
-
return [];
|
|
2013
|
-
}
|
|
2014
|
-
const firstSegmentPoint = curveMidPoint(firstSegment[0]);
|
|
2015
|
-
const firstSegmentInSecondShape = second.isInside(firstSegmentPoint);
|
|
2016
|
-
if (firstInside === "keep" && firstSegmentInSecondShape || firstInside === "remove" && !firstSegmentInSecondShape) {
|
|
2037
|
+
if (segmentsIn === null) {
|
|
2038
|
+
const accumulated = lastWasSame ? [...lastWasSame, ...firstSegment] : firstSegment;
|
|
2039
|
+
return { curves: [], segmentsIn: null, lastWasSame: accumulated };
|
|
2040
|
+
}
|
|
2041
|
+
return { curves: [], segmentsIn, lastWasSame };
|
|
2042
|
+
}
|
|
2043
|
+
function selectSegments(firstSegment, secondSegment, first, second, config, segmentsIn, lastWasSame) {
|
|
2044
|
+
let segments = [];
|
|
2045
|
+
let segmentsOut = 0;
|
|
2046
|
+
const firstCurve = firstSegment[0];
|
|
2047
|
+
if (firstCurve !== void 0) {
|
|
2048
|
+
const firstSegmentPoint = curveMidPoint(firstCurve);
|
|
2049
|
+
const firstInSecond = second.isInside(firstSegmentPoint);
|
|
2050
|
+
if (config.firstInside === "keep" && firstInSecond || config.firstInside === "remove" && !firstInSecond) {
|
|
2017
2051
|
segmentsOut += 1;
|
|
2018
|
-
|
|
2052
|
+
segments.push(...firstSegment);
|
|
2019
2053
|
}
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2054
|
+
}
|
|
2055
|
+
const secondCurve = secondSegment[0];
|
|
2056
|
+
if (secondCurve !== void 0) {
|
|
2057
|
+
const secondSegmentPoint = curveMidPoint(secondCurve);
|
|
2058
|
+
const secondInFirst = first.isInside(secondSegmentPoint);
|
|
2059
|
+
if (config.secondInside === "keep" && secondInFirst || config.secondInside === "remove" && !secondInFirst) {
|
|
2023
2060
|
let segmentsToAdd = secondSegment;
|
|
2024
2061
|
if (segmentsOut === 1) {
|
|
2025
2062
|
segmentsToAdd = reverseSegment(secondSegment);
|
|
2026
2063
|
}
|
|
2027
2064
|
segmentsOut += 1;
|
|
2028
|
-
|
|
2029
|
-
}
|
|
2030
|
-
if (segmentsIn === null && segmentsOut === 1 && lastWasSame) {
|
|
2031
|
-
segments2 = [...lastWasSame, ...segments2];
|
|
2032
|
-
}
|
|
2033
|
-
if (segmentsOut === 1) {
|
|
2034
|
-
segmentsIn = segmentsOut;
|
|
2035
|
-
lastWasSame = null;
|
|
2065
|
+
segments.push(...segmentsToAdd);
|
|
2036
2066
|
}
|
|
2037
|
-
|
|
2067
|
+
}
|
|
2068
|
+
if (segmentsIn === null && segmentsOut === 1 && lastWasSame !== null) {
|
|
2069
|
+
segments = [...lastWasSame, ...segments];
|
|
2070
|
+
}
|
|
2071
|
+
const newSegmentsIn = segmentsOut === 1 ? segmentsOut : segmentsIn;
|
|
2072
|
+
const newLastWasSame = segmentsOut === 1 ? null : lastWasSame;
|
|
2073
|
+
return { curves: segments, segmentsIn: newSegmentsIn, lastWasSame: newLastWasSame };
|
|
2074
|
+
}
|
|
2075
|
+
function booleanOperation(first, second, config) {
|
|
2076
|
+
const segments = blueprintsIntersectionSegments(first, second);
|
|
2077
|
+
if (segments === null) {
|
|
2078
|
+
return buildNoIntersectionResult(first, second);
|
|
2079
|
+
}
|
|
2080
|
+
if (segments.every(([, secondSegment]) => secondSegment === "same")) {
|
|
2081
|
+
return { identical: true };
|
|
2082
|
+
}
|
|
2083
|
+
let segmentsIn = null;
|
|
2084
|
+
let lastWasSame = null;
|
|
2085
|
+
const assembledCurves = segments.flatMap(([firstSegment, secondSegment]) => {
|
|
2086
|
+
if (secondSegment === "same") {
|
|
2087
|
+
const result2 = handleSameSegment(firstSegment, segmentsIn, lastWasSame);
|
|
2088
|
+
segmentsIn = result2.segmentsIn;
|
|
2089
|
+
lastWasSame = result2.lastWasSame;
|
|
2090
|
+
return result2.curves;
|
|
2091
|
+
}
|
|
2092
|
+
const result = selectSegments(
|
|
2093
|
+
firstSegment,
|
|
2094
|
+
secondSegment,
|
|
2095
|
+
first,
|
|
2096
|
+
second,
|
|
2097
|
+
config,
|
|
2098
|
+
segmentsIn,
|
|
2099
|
+
lastWasSame
|
|
2100
|
+
);
|
|
2101
|
+
segmentsIn = result.segmentsIn;
|
|
2102
|
+
lastWasSame = result.lastWasSame;
|
|
2103
|
+
return result.curves;
|
|
2038
2104
|
});
|
|
2039
|
-
const paths = splitPaths(
|
|
2105
|
+
const paths = splitPaths(assembledCurves).filter((b) => b.length > 0).map((b) => new Blueprint(b));
|
|
2040
2106
|
if (paths.length === 0) return null;
|
|
2041
|
-
if (paths.length === 1)
|
|
2107
|
+
if (paths.length === 1) {
|
|
2108
|
+
const single = paths[0];
|
|
2109
|
+
if (single === void 0) return null;
|
|
2110
|
+
return single;
|
|
2111
|
+
}
|
|
2042
2112
|
return organiseBlueprints(paths);
|
|
2043
2113
|
}
|
|
2044
|
-
|
|
2114
|
+
function buildNoIntersectionResult(first, second) {
|
|
2115
|
+
const firstCurve = first.curves[0];
|
|
2116
|
+
const secondCurve = second.curves[0];
|
|
2117
|
+
const firstCurveInSecond = firstCurve !== void 0 && second.isInside(curveMidPoint(firstCurve));
|
|
2118
|
+
const secondCurveInFirst = secondCurve !== void 0 && first.isInside(curveMidPoint(secondCurve));
|
|
2119
|
+
return {
|
|
2120
|
+
identical: false,
|
|
2121
|
+
firstCurveInSecond,
|
|
2122
|
+
secondCurveInFirst
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function fuseBlueprints(first, second) {
|
|
2045
2126
|
const result = booleanOperation(first, second, {
|
|
2046
2127
|
firstInside: "remove",
|
|
2047
2128
|
secondInside: "remove"
|
|
@@ -2057,8 +2138,8 @@ const fuseBlueprints = (first, second) => {
|
|
|
2057
2138
|
return first.clone();
|
|
2058
2139
|
}
|
|
2059
2140
|
return new Blueprints([first, second]);
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2141
|
+
}
|
|
2142
|
+
function cutBlueprints(first, second) {
|
|
2062
2143
|
const result = booleanOperation(first, second, {
|
|
2063
2144
|
firstInside: "remove",
|
|
2064
2145
|
secondInside: "keep"
|
|
@@ -2074,8 +2155,8 @@ const cutBlueprints = (first, second) => {
|
|
|
2074
2155
|
return new Blueprints([new CompoundBlueprint([first, second])]);
|
|
2075
2156
|
}
|
|
2076
2157
|
return first.clone();
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2158
|
+
}
|
|
2159
|
+
function intersectBlueprints(first, second) {
|
|
2079
2160
|
const result = booleanOperation(first, second, {
|
|
2080
2161
|
firstInside: "keep",
|
|
2081
2162
|
secondInside: "keep"
|
|
@@ -2091,7 +2172,7 @@ const intersectBlueprints = (first, second) => {
|
|
|
2091
2172
|
return second.clone();
|
|
2092
2173
|
}
|
|
2093
2174
|
return null;
|
|
2094
|
-
}
|
|
2175
|
+
}
|
|
2095
2176
|
const genericIntersects = (first, second) => {
|
|
2096
2177
|
if (first instanceof Blueprint && second instanceof Blueprint) {
|
|
2097
2178
|
let allIntersections = [];
|