brepjs 18.125.0 → 18.126.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brepjs.cjs +655 -65
- package/dist/brepjs.js +660 -70
- package/dist/csg/builders.d.ts +20 -2
- package/dist/csg/evaluators/loft.d.ts +5 -0
- package/dist/csg/evaluators/path.d.ts +5 -0
- package/dist/csg/evaluators/profileFace.d.ts +8 -0
- package/dist/csg/evaluators/revolve.d.ts +5 -0
- package/dist/csg/expressions.d.ts +1 -0
- package/dist/csg/index.d.ts +3 -2
- package/dist/csg/segments.d.ts +44 -0
- package/dist/csg/types.d.ts +26 -1
- package/package.json +1 -1
package/dist/brepjs.cjs
CHANGED
|
@@ -254,8 +254,8 @@ function repairMesh(mesh, opts, id) {
|
|
|
254
254
|
if (require_errors.isErr(engine)) return engine;
|
|
255
255
|
try {
|
|
256
256
|
try {
|
|
257
|
-
var _usingCtx$
|
|
258
|
-
const repaired = _usingCtx$
|
|
257
|
+
var _usingCtx$10 = require_shapeTypes._usingCtx();
|
|
258
|
+
const repaired = _usingCtx$10.u(engine.value.repair_mesh(mesh.vertices, mesh.triangles, resolution, padding));
|
|
259
259
|
const vertexCount = repaired.positions.length / 3;
|
|
260
260
|
return require_errors.ok({
|
|
261
261
|
vertices: repaired.positions,
|
|
@@ -269,9 +269,9 @@ function repairMesh(mesh, opts, id) {
|
|
|
269
269
|
}]
|
|
270
270
|
});
|
|
271
271
|
} catch (_) {
|
|
272
|
-
_usingCtx$
|
|
272
|
+
_usingCtx$10.e = _;
|
|
273
273
|
} finally {
|
|
274
|
-
_usingCtx$
|
|
274
|
+
_usingCtx$10.d();
|
|
275
275
|
}
|
|
276
276
|
} catch (cause) {
|
|
277
277
|
return require_errors.err(require_errors.computationError("VOXEL_REPAIR_FAILED", cause instanceof Error ? cause.message : "voxel repair failed (grid too large?).", cause));
|
|
@@ -350,12 +350,12 @@ function offsetMesh(mesh, distance, opts, id) {
|
|
|
350
350
|
if (require_errors.isErr(engine)) return engine;
|
|
351
351
|
try {
|
|
352
352
|
try {
|
|
353
|
-
var _usingCtx$
|
|
354
|
-
return meshFromResult(_usingCtx$
|
|
353
|
+
var _usingCtx$9 = require_shapeTypes._usingCtx();
|
|
354
|
+
return meshFromResult(_usingCtx$9.u(engine.value.offset_mesh(mesh.vertices, mesh.triangles, distance, params.value.resolution, params.value.padding)));
|
|
355
355
|
} catch (_) {
|
|
356
|
-
_usingCtx$
|
|
356
|
+
_usingCtx$9.e = _;
|
|
357
357
|
} finally {
|
|
358
|
-
_usingCtx$
|
|
358
|
+
_usingCtx$9.d();
|
|
359
359
|
}
|
|
360
360
|
} catch (cause) {
|
|
361
361
|
return require_errors.err(require_errors.computationError("VOXEL_OFFSET_FAILED", cause instanceof Error ? cause.message : "voxel offset failed (grid too large?).", cause));
|
|
@@ -480,9 +480,9 @@ function resolveGridParams$1(opts) {
|
|
|
480
480
|
*/
|
|
481
481
|
function meshFromField(field) {
|
|
482
482
|
try {
|
|
483
|
-
var _usingCtx$
|
|
483
|
+
var _usingCtx$8 = require_shapeTypes._usingCtx();
|
|
484
484
|
const rawResult = field.contour();
|
|
485
|
-
const { positions, normals, indices } = _usingCtx$
|
|
485
|
+
const { positions, normals, indices } = _usingCtx$8.u({
|
|
486
486
|
value: rawResult,
|
|
487
487
|
[Symbol.dispose]() {
|
|
488
488
|
rawResult.free();
|
|
@@ -502,9 +502,9 @@ function meshFromField(field) {
|
|
|
502
502
|
}]
|
|
503
503
|
});
|
|
504
504
|
} catch (_) {
|
|
505
|
-
_usingCtx$
|
|
505
|
+
_usingCtx$8.e = _;
|
|
506
506
|
} finally {
|
|
507
|
-
_usingCtx$
|
|
507
|
+
_usingCtx$8.d();
|
|
508
508
|
}
|
|
509
509
|
}
|
|
510
510
|
function fieldDeletable(raw) {
|
|
@@ -1072,13 +1072,13 @@ function latticeInfill(mesh, opts, id) {
|
|
|
1072
1072
|
const { tag, period, thickness, resolution, padding } = resolved.value;
|
|
1073
1073
|
try {
|
|
1074
1074
|
try {
|
|
1075
|
-
var _usingCtx$
|
|
1076
|
-
const repaired = _usingCtx$
|
|
1075
|
+
var _usingCtx$7 = require_shapeTypes._usingCtx();
|
|
1076
|
+
const repaired = _usingCtx$7.u(engine.value.lattice_infill(mesh.vertices, mesh.triangles, resolution, padding, tag, period, thickness));
|
|
1077
1077
|
return require_errors.ok(toMesh(repaired));
|
|
1078
1078
|
} catch (_) {
|
|
1079
|
-
_usingCtx$
|
|
1079
|
+
_usingCtx$7.e = _;
|
|
1080
1080
|
} finally {
|
|
1081
|
-
_usingCtx$
|
|
1081
|
+
_usingCtx$7.d();
|
|
1082
1082
|
}
|
|
1083
1083
|
} catch (cause) {
|
|
1084
1084
|
return require_errors.err(require_errors.computationError("LATTICE_INFILL_FAILED", cause instanceof Error ? cause.message : "lattice infill failed (grid too large?).", cause));
|
|
@@ -1557,7 +1557,7 @@ function polyhedron(points, faces, options = {}) {
|
|
|
1557
1557
|
}
|
|
1558
1558
|
//#endregion
|
|
1559
1559
|
//#region src/operations/straightSkeleton.ts
|
|
1560
|
-
var EPS = 1e-10;
|
|
1560
|
+
var EPS$1 = 1e-10;
|
|
1561
1561
|
function cross2(ax, ay, bx, by) {
|
|
1562
1562
|
return ax * by - ay * bx;
|
|
1563
1563
|
}
|
|
@@ -1595,7 +1595,7 @@ function bisector(poly, i) {
|
|
|
1595
1595
|
const e2x = next.x - cur.x;
|
|
1596
1596
|
const e2y = next.y - cur.y;
|
|
1597
1597
|
const e2l = len2(e2x, e2y);
|
|
1598
|
-
if (e1l < EPS || e2l < EPS) return {
|
|
1598
|
+
if (e1l < EPS$1 || e2l < EPS$1) return {
|
|
1599
1599
|
dx: 0,
|
|
1600
1600
|
dy: 0
|
|
1601
1601
|
};
|
|
@@ -1606,14 +1606,14 @@ function bisector(poly, i) {
|
|
|
1606
1606
|
let bx = n1x + n2x;
|
|
1607
1607
|
let by = n1y + n2y;
|
|
1608
1608
|
const bl = len2(bx, by);
|
|
1609
|
-
if (bl < EPS) return {
|
|
1609
|
+
if (bl < EPS$1) return {
|
|
1610
1610
|
dx: n1x,
|
|
1611
1611
|
dy: n1y
|
|
1612
1612
|
};
|
|
1613
1613
|
bx /= bl;
|
|
1614
1614
|
by /= bl;
|
|
1615
1615
|
const cosHalf = dot2(bx, by, n1x, n1y);
|
|
1616
|
-
const speed = Math.abs(cosHalf) > EPS ? 1 / cosHalf : 1;
|
|
1616
|
+
const speed = Math.abs(cosHalf) > EPS$1 ? 1 / cosHalf : 1;
|
|
1617
1617
|
return {
|
|
1618
1618
|
dx: bx * speed,
|
|
1619
1619
|
dy: by * speed
|
|
@@ -1675,14 +1675,14 @@ function bisectorIntersectTime(a, b) {
|
|
|
1675
1675
|
const ddy = a.by - b.by;
|
|
1676
1676
|
const dxp = b.x - a.x;
|
|
1677
1677
|
const dyp = b.y - a.y;
|
|
1678
|
-
if (Math.abs(ddx) < EPS && Math.abs(ddy) < EPS) return null;
|
|
1678
|
+
if (Math.abs(ddx) < EPS$1 && Math.abs(ddy) < EPS$1) return null;
|
|
1679
1679
|
let t;
|
|
1680
1680
|
if (Math.abs(ddx) > Math.abs(ddy)) t = dxp / ddx;
|
|
1681
1681
|
else t = dyp / ddy;
|
|
1682
|
-
if (t < EPS) return null;
|
|
1682
|
+
if (t < EPS$1) return null;
|
|
1683
1683
|
const otherDd = Math.abs(ddx) > Math.abs(ddy) ? ddy : ddx;
|
|
1684
1684
|
const otherDp = Math.abs(ddx) > Math.abs(ddy) ? dyp : dxp;
|
|
1685
|
-
if (Math.abs(otherDd) > EPS) {
|
|
1685
|
+
if (Math.abs(otherDd) > EPS$1) {
|
|
1686
1686
|
const t2 = otherDp / otherDd;
|
|
1687
1687
|
if (Math.abs(t - t2) > 1e-4 * Math.max(1, Math.abs(t))) return null;
|
|
1688
1688
|
}
|
|
@@ -1693,16 +1693,16 @@ function raySplitTime(node, eA, eB) {
|
|
|
1693
1693
|
const edx = eB.x - eA.x;
|
|
1694
1694
|
const edy = eB.y - eA.y;
|
|
1695
1695
|
const el = len2(edx, edy);
|
|
1696
|
-
if (el < EPS) return null;
|
|
1696
|
+
if (el < EPS$1) return null;
|
|
1697
1697
|
const enx = -edy / el;
|
|
1698
1698
|
const eny = edx / el;
|
|
1699
1699
|
const d0 = (node.x - eA.x) * enx + (node.y - eA.y) * eny;
|
|
1700
1700
|
const relBx = node.bx - (eA.bx + eB.bx) / 2;
|
|
1701
1701
|
const relBy = node.by - (eA.by + eB.by) / 2;
|
|
1702
1702
|
const dRate = relBx * enx + relBy * eny;
|
|
1703
|
-
if (Math.abs(dRate) < EPS) return null;
|
|
1703
|
+
if (Math.abs(dRate) < EPS$1) return null;
|
|
1704
1704
|
const t = -d0 / dRate;
|
|
1705
|
-
if (t < EPS) return null;
|
|
1705
|
+
if (t < EPS$1) return null;
|
|
1706
1706
|
const px = node.x + t * node.bx;
|
|
1707
1707
|
const py = node.y + t * node.by;
|
|
1708
1708
|
const ax = eA.x + t * eA.bx;
|
|
@@ -1712,7 +1712,7 @@ function raySplitTime(node, eA, eB) {
|
|
|
1712
1712
|
const segDx = bxx - ax;
|
|
1713
1713
|
const segDy = byy - ay;
|
|
1714
1714
|
const segL = len2(segDx, segDy);
|
|
1715
|
-
if (segL < EPS) return t;
|
|
1715
|
+
if (segL < EPS$1) return t;
|
|
1716
1716
|
const s = dot2(px - ax, py - ay, segDx, segDy) / (segL * segL);
|
|
1717
1717
|
if (s < -.01 || s > 1.01) return null;
|
|
1718
1718
|
return t;
|
|
@@ -1722,7 +1722,7 @@ function computeEvents(lavNodes) {
|
|
|
1722
1722
|
for (const node of lavNodes) {
|
|
1723
1723
|
if (!node.active || !node.next) continue;
|
|
1724
1724
|
const t = bisectorIntersectTime(node, node.next);
|
|
1725
|
-
if (t !== null && t > EPS) {
|
|
1725
|
+
if (t !== null && t > EPS$1) {
|
|
1726
1726
|
const x = node.x + t * node.bx;
|
|
1727
1727
|
const y = node.y + t * node.by;
|
|
1728
1728
|
events.push({
|
|
@@ -1739,7 +1739,7 @@ function computeEvents(lavNodes) {
|
|
|
1739
1739
|
let count = 0;
|
|
1740
1740
|
while (cur !== node.prev && cur !== node && count < 1e3) {
|
|
1741
1741
|
const st = raySplitTime(node, cur, cur.next);
|
|
1742
|
-
if (st !== null && st > EPS) {
|
|
1742
|
+
if (st !== null && st > EPS$1) {
|
|
1743
1743
|
const x = node.x + st * node.bx;
|
|
1744
1744
|
const y = node.y + st * node.by;
|
|
1745
1745
|
events.push({
|
|
@@ -1795,7 +1795,7 @@ function computeStraightSkeletonImpl(polygon) {
|
|
|
1795
1795
|
const b = a.next;
|
|
1796
1796
|
const c = b.next;
|
|
1797
1797
|
const t = bisectorIntersectTime(a, b);
|
|
1798
|
-
const time = t !== null && t > EPS ? t : 0;
|
|
1798
|
+
const time = t !== null && t > EPS$1 ? t : 0;
|
|
1799
1799
|
const mx = (a.x + b.x + c.x) / 3 + time * (a.bx + b.bx + c.bx) / 3;
|
|
1800
1800
|
const my = (a.y + b.y + c.y) / 3 + time * (a.by + b.by + c.by) / 3;
|
|
1801
1801
|
const nodeIdx = skeletonNodes.length;
|
|
@@ -1934,7 +1934,7 @@ function computeStraightSkeletonImpl(polygon) {
|
|
|
1934
1934
|
if (!sn) continue;
|
|
1935
1935
|
const lastVert = faceVerts[faceVerts.length - 1];
|
|
1936
1936
|
if (!lastVert) continue;
|
|
1937
|
-
if (len2(sn.x - lastVert.x, sn.y - lastVert.y) > EPS) {
|
|
1937
|
+
if (len2(sn.x - lastVert.x, sn.y - lastVert.y) > EPS$1) {
|
|
1938
1938
|
faceVerts.push({
|
|
1939
1939
|
x: sn.x,
|
|
1940
1940
|
y: sn.y
|
|
@@ -3901,7 +3901,7 @@ function drill(shape, options) {
|
|
|
3901
3901
|
*/
|
|
3902
3902
|
function pocket(shape, options) {
|
|
3903
3903
|
try {
|
|
3904
|
-
var _usingCtx$
|
|
3904
|
+
var _usingCtx$6 = require_shapeTypes._usingCtx();
|
|
3905
3905
|
const s = resolve(shape);
|
|
3906
3906
|
const { profile, depth } = options;
|
|
3907
3907
|
if (depth <= 0) return require_errors.err(require_errors.validationError("POCKET_INVALID_DEPTH", "Pocket depth must be positive"));
|
|
@@ -3910,7 +3910,7 @@ function pocket(shape, options) {
|
|
|
3910
3910
|
const targetFace = targetResult.value;
|
|
3911
3911
|
const normal = require_faceFns.normalAt(targetFace);
|
|
3912
3912
|
const center = require_faceFns.faceCenter(targetFace);
|
|
3913
|
-
const scope = _usingCtx$
|
|
3913
|
+
const scope = _usingCtx$6.u(new require_shapeTypes.DisposalScope());
|
|
3914
3914
|
const { wire: w, owned } = toWire(profile);
|
|
3915
3915
|
if (owned) scope.register(w);
|
|
3916
3916
|
const faceResult = require_surfaceBuilders.makeFace(w);
|
|
@@ -3923,9 +3923,9 @@ function pocket(shape, options) {
|
|
|
3923
3923
|
scope.register(toolResult.value);
|
|
3924
3924
|
return require_solidBuilders.cut(s, toolResult.value, { unsafe: true });
|
|
3925
3925
|
} catch (_) {
|
|
3926
|
-
_usingCtx$
|
|
3926
|
+
_usingCtx$6.e = _;
|
|
3927
3927
|
} finally {
|
|
3928
|
-
_usingCtx$
|
|
3928
|
+
_usingCtx$6.d();
|
|
3929
3929
|
}
|
|
3930
3930
|
}
|
|
3931
3931
|
/**
|
|
@@ -4515,12 +4515,12 @@ function makeInternalGearProfileWire(params) {
|
|
|
4515
4515
|
}
|
|
4516
4516
|
function makeProfileWire(params, isInternal) {
|
|
4517
4517
|
try {
|
|
4518
|
-
var _usingCtx$
|
|
4518
|
+
var _usingCtx$5 = require_shapeTypes._usingCtx();
|
|
4519
4519
|
const { teeth, moduleSize, pressureAngle, shift, clearance, flankThinning, samples = adaptiveSampleCount(moduleSize) } = params;
|
|
4520
4520
|
if (teeth < 4) return require_errors.err(require_errors.validationError("GEAR_TEETH_TOO_FEW", `gear needs ≥ 4 teeth, got ${teeth}`));
|
|
4521
4521
|
if (moduleSize <= 0) return require_errors.err(require_errors.validationError("GEAR_MODULE_NONPOSITIVE", `module must be > 0, got ${moduleSize}`));
|
|
4522
4522
|
const tm = gearGeometry(teeth, moduleSize, pressureAngle, shift, clearance, flankThinning, isInternal);
|
|
4523
|
-
const scope = _usingCtx$
|
|
4523
|
+
const scope = _usingCtx$5.u(new require_shapeTypes.DisposalScope());
|
|
4524
4524
|
const allEdges = [];
|
|
4525
4525
|
for (let i = 0; i < teeth; i++) {
|
|
4526
4526
|
const periodEdges = buildToothPeriodEdges(tm, i, teeth, samples, scope);
|
|
@@ -4531,9 +4531,9 @@ function makeProfileWire(params, isInternal) {
|
|
|
4531
4531
|
if (require_errors.isErr(wireResult)) return wireResult;
|
|
4532
4532
|
return require_errors.ok(wireResult.value);
|
|
4533
4533
|
} catch (_) {
|
|
4534
|
-
_usingCtx$
|
|
4534
|
+
_usingCtx$5.e = _;
|
|
4535
4535
|
} finally {
|
|
4536
|
-
_usingCtx$
|
|
4536
|
+
_usingCtx$5.d();
|
|
4537
4537
|
}
|
|
4538
4538
|
}
|
|
4539
4539
|
//#endregion
|
|
@@ -4714,8 +4714,8 @@ function resolvePlanetaryParams(params) {
|
|
|
4714
4714
|
}
|
|
4715
4715
|
function makeOuterCircleWire(radius) {
|
|
4716
4716
|
try {
|
|
4717
|
-
var _usingCtx$
|
|
4718
|
-
const circleEdge = _usingCtx$
|
|
4717
|
+
var _usingCtx$4 = require_shapeTypes._usingCtx();
|
|
4718
|
+
const circleEdge = _usingCtx$4.u(new require_shapeTypes.DisposalScope()).register(require_surfaceBuilders.makeCircle(radius, [
|
|
4719
4719
|
0,
|
|
4720
4720
|
0,
|
|
4721
4721
|
0
|
|
@@ -4728,9 +4728,9 @@ function makeOuterCircleWire(radius) {
|
|
|
4728
4728
|
if (require_errors.isErr(wireResult)) return wireResult;
|
|
4729
4729
|
return require_errors.ok(wireResult.value);
|
|
4730
4730
|
} catch (_) {
|
|
4731
|
-
_usingCtx$
|
|
4731
|
+
_usingCtx$4.e = _;
|
|
4732
4732
|
} finally {
|
|
4733
|
-
_usingCtx$
|
|
4733
|
+
_usingCtx$4.d();
|
|
4734
4734
|
}
|
|
4735
4735
|
}
|
|
4736
4736
|
function finalizeExternalSolid(wire, thickness, bore, geom, diagnostics) {
|
|
@@ -5327,6 +5327,14 @@ function evalVec3(expr, env, where) {
|
|
|
5327
5327
|
c
|
|
5328
5328
|
]);
|
|
5329
5329
|
}
|
|
5330
|
+
function evalVec2(expr, env, where) {
|
|
5331
|
+
const r = evalExpr(expr, env);
|
|
5332
|
+
if (!r.ok) return r;
|
|
5333
|
+
const v = expectVecLen(r.value, 2, where);
|
|
5334
|
+
if (!v.ok) return v;
|
|
5335
|
+
const [a, b] = v.value;
|
|
5336
|
+
return require_errors.ok([a, b]);
|
|
5337
|
+
}
|
|
5330
5338
|
function projectEnv(env, deps) {
|
|
5331
5339
|
if (deps.size === 0) return Object.freeze({});
|
|
5332
5340
|
const out = {};
|
|
@@ -5337,6 +5345,91 @@ function projectEnv(env, deps) {
|
|
|
5337
5345
|
return Object.freeze(out);
|
|
5338
5346
|
}
|
|
5339
5347
|
//#endregion
|
|
5348
|
+
//#region src/csg/segments.ts
|
|
5349
|
+
/**
|
|
5350
|
+
* Serializable 2D segment vocabulary — endpoint-parametrized (SVG-style)
|
|
5351
|
+
* curve segments shared by the Path node (open spines) and, later, Profile
|
|
5352
|
+
* contours. Positions and radii are expressions, so `freeParams` propagates
|
|
5353
|
+
* and segment data hashes with the same FNV machinery as every node field.
|
|
5354
|
+
*/
|
|
5355
|
+
function lineTo(to) {
|
|
5356
|
+
return {
|
|
5357
|
+
kind: "Line",
|
|
5358
|
+
to: asVec2Expr(to)
|
|
5359
|
+
};
|
|
5360
|
+
}
|
|
5361
|
+
function arcTo(to, radius, options) {
|
|
5362
|
+
return {
|
|
5363
|
+
kind: "Arc",
|
|
5364
|
+
to: asVec2Expr(to),
|
|
5365
|
+
radius: asScalarExpr(radius),
|
|
5366
|
+
largeArc: options?.largeArc ?? false,
|
|
5367
|
+
clockwise: options?.clockwise ?? false
|
|
5368
|
+
};
|
|
5369
|
+
}
|
|
5370
|
+
function bezierTo(controls, to) {
|
|
5371
|
+
return {
|
|
5372
|
+
kind: "Bezier",
|
|
5373
|
+
controls: controls.map(asVec2Expr),
|
|
5374
|
+
to: asVec2Expr(to)
|
|
5375
|
+
};
|
|
5376
|
+
}
|
|
5377
|
+
function ellipseArcTo(to, radii, options) {
|
|
5378
|
+
return {
|
|
5379
|
+
kind: "EllipseArc",
|
|
5380
|
+
to: asVec2Expr(to),
|
|
5381
|
+
radii: asVec2Expr(radii),
|
|
5382
|
+
rotation: asScalarExpr(options?.rotation ?? 0),
|
|
5383
|
+
largeArc: options?.largeArc ?? false,
|
|
5384
|
+
clockwise: options?.clockwise ?? false
|
|
5385
|
+
};
|
|
5386
|
+
}
|
|
5387
|
+
function hashSegments(h0, segments) {
|
|
5388
|
+
let h = fnvMixInt32(h0, segments.length);
|
|
5389
|
+
const mix = (acc, e) => fnvMixHash(acc, e.structuralHash);
|
|
5390
|
+
for (const s of segments) {
|
|
5391
|
+
h = fnvMixString(h, s.kind);
|
|
5392
|
+
h = mix(h, s.to);
|
|
5393
|
+
if (s.kind === "Arc") {
|
|
5394
|
+
h = mix(h, s.radius);
|
|
5395
|
+
h = fnvMixBool(fnvMixBool(h, s.largeArc), s.clockwise);
|
|
5396
|
+
} else if (s.kind === "Bezier") {
|
|
5397
|
+
h = fnvMixInt32(h, s.controls.length);
|
|
5398
|
+
for (const c of s.controls) h = mix(h, c);
|
|
5399
|
+
} else if (s.kind === "EllipseArc") {
|
|
5400
|
+
h = mix(h, s.radii);
|
|
5401
|
+
h = mix(h, s.rotation);
|
|
5402
|
+
h = fnvMixBool(fnvMixBool(h, s.largeArc), s.clockwise);
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
return h;
|
|
5406
|
+
}
|
|
5407
|
+
function segmentFreeParams(segments) {
|
|
5408
|
+
const out = [];
|
|
5409
|
+
for (const s of segments) {
|
|
5410
|
+
out.push(s.to);
|
|
5411
|
+
if (s.kind === "Arc") out.push(s.radius);
|
|
5412
|
+
else if (s.kind === "Bezier") out.push(...s.controls);
|
|
5413
|
+
else if (s.kind === "EllipseArc") out.push(s.radii, s.rotation);
|
|
5414
|
+
}
|
|
5415
|
+
return out;
|
|
5416
|
+
}
|
|
5417
|
+
function foldSegment(s, foldExpr) {
|
|
5418
|
+
switch (s.kind) {
|
|
5419
|
+
case "Line": return lineTo(foldExpr(s.to));
|
|
5420
|
+
case "Arc": return arcTo(foldExpr(s.to), foldExpr(s.radius), {
|
|
5421
|
+
largeArc: s.largeArc,
|
|
5422
|
+
clockwise: s.clockwise
|
|
5423
|
+
});
|
|
5424
|
+
case "Bezier": return bezierTo(s.controls.map(foldExpr), foldExpr(s.to));
|
|
5425
|
+
case "EllipseArc": return ellipseArcTo(foldExpr(s.to), foldExpr(s.radii), {
|
|
5426
|
+
rotation: foldExpr(s.rotation),
|
|
5427
|
+
largeArc: s.largeArc,
|
|
5428
|
+
clockwise: s.clockwise
|
|
5429
|
+
});
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
//#endregion
|
|
5340
5433
|
//#region src/csg/builders.ts
|
|
5341
5434
|
var EMPTY_DEPS = /* @__PURE__ */ new Set();
|
|
5342
5435
|
function startHash(tag) {
|
|
@@ -5513,29 +5606,31 @@ function intersect$1(a, b, tolerance) {
|
|
|
5513
5606
|
};
|
|
5514
5607
|
}
|
|
5515
5608
|
function fuseAll$1(shapes, tolerance) {
|
|
5516
|
-
|
|
5517
|
-
|
|
5609
|
+
const copied = [...shapes];
|
|
5610
|
+
let h = fnvMixInt32(startHash("FuseAll"), copied.length);
|
|
5611
|
+
for (const s of copied) h = mix(h, s);
|
|
5518
5612
|
h = mixOptNumber(h, tolerance);
|
|
5519
5613
|
return {
|
|
5520
5614
|
kind: "FuseAll",
|
|
5521
|
-
shapes,
|
|
5615
|
+
shapes: copied,
|
|
5522
5616
|
tolerance,
|
|
5523
5617
|
structuralHash: h,
|
|
5524
|
-
freeParams: depsOf(...
|
|
5618
|
+
freeParams: depsOf(...copied)
|
|
5525
5619
|
};
|
|
5526
5620
|
}
|
|
5527
5621
|
function cutAll$1(base, tools, tolerance) {
|
|
5622
|
+
const copied = [...tools];
|
|
5528
5623
|
let h = mix(startHash("CutAll"), base);
|
|
5529
|
-
h = fnvMixInt32(h,
|
|
5530
|
-
for (const t of
|
|
5624
|
+
h = fnvMixInt32(h, copied.length);
|
|
5625
|
+
for (const t of copied) h = mix(h, t);
|
|
5531
5626
|
h = mixOptNumber(h, tolerance);
|
|
5532
5627
|
return {
|
|
5533
5628
|
kind: "CutAll",
|
|
5534
5629
|
base,
|
|
5535
|
-
tools,
|
|
5630
|
+
tools: copied,
|
|
5536
5631
|
tolerance,
|
|
5537
5632
|
structuralHash: h,
|
|
5538
|
-
freeParams: depsOf(base, ...
|
|
5633
|
+
freeParams: depsOf(base, ...copied)
|
|
5539
5634
|
};
|
|
5540
5635
|
}
|
|
5541
5636
|
function optVec3$1(v) {
|
|
@@ -5607,14 +5702,66 @@ function extrude$1(profile, vector) {
|
|
|
5607
5702
|
freeParams: depsOf(profile, ve)
|
|
5608
5703
|
};
|
|
5609
5704
|
}
|
|
5705
|
+
/** Open (or incidentally closed) planar path in the XY plane at z = 0,
|
|
5706
|
+
* producing a Wire. Segments come from `lineTo`/`arcTo`/`bezierTo`/
|
|
5707
|
+
* `ellipseArcTo`. */
|
|
5708
|
+
function path(start, segments) {
|
|
5709
|
+
const se = asVec2Expr(start);
|
|
5710
|
+
const copied = [...segments];
|
|
5711
|
+
let h = mix(startHash("Path"), se);
|
|
5712
|
+
h = hashSegments(h, copied);
|
|
5713
|
+
return {
|
|
5714
|
+
kind: "Path",
|
|
5715
|
+
start: se,
|
|
5716
|
+
segments: copied,
|
|
5717
|
+
structuralHash: h,
|
|
5718
|
+
freeParams: depsOf(se, ...segmentFreeParams(copied))
|
|
5719
|
+
};
|
|
5720
|
+
}
|
|
5721
|
+
/** Loft through two or more face-producing sections (default ruled). */
|
|
5722
|
+
function loft$1(sections, options) {
|
|
5723
|
+
const copied = [...sections];
|
|
5724
|
+
const ruled = options?.ruled ?? true;
|
|
5725
|
+
let h = fnvMixInt32(startHash("Loft"), copied.length);
|
|
5726
|
+
for (const s of copied) h = mix(h, s);
|
|
5727
|
+
h = fnvMixBool(h, ruled);
|
|
5728
|
+
return {
|
|
5729
|
+
kind: "Loft",
|
|
5730
|
+
sections: copied,
|
|
5731
|
+
ruled,
|
|
5732
|
+
structuralHash: h,
|
|
5733
|
+
freeParams: depsOf(...copied)
|
|
5734
|
+
};
|
|
5735
|
+
}
|
|
5736
|
+
/** Angle is in degrees (matching `rotate`); defaults to a full revolution
|
|
5737
|
+
* around the Z axis at the origin. Evaluation clamps angles above 360 to one
|
|
5738
|
+
* revolution (kernels diverge past 2*pi) and rejects non-positive angles. */
|
|
5739
|
+
function revolve$1(profile, angle = 360, options) {
|
|
5740
|
+
const ae = asScalarExpr(angle);
|
|
5741
|
+
const axisE = optVec3$1(options?.axis);
|
|
5742
|
+
const atE = optVec3$1(options?.at);
|
|
5743
|
+
let h = mix(mix(startHash("Revolve"), profile), ae);
|
|
5744
|
+
h = mixOptExpr(h, axisE);
|
|
5745
|
+
h = mixOptExpr(h, atE);
|
|
5746
|
+
return {
|
|
5747
|
+
kind: "Revolve",
|
|
5748
|
+
profile,
|
|
5749
|
+
angle: ae,
|
|
5750
|
+
axis: axisE,
|
|
5751
|
+
at: atE,
|
|
5752
|
+
structuralHash: h,
|
|
5753
|
+
freeParams: depsOf(profile, ae, axisE, atE)
|
|
5754
|
+
};
|
|
5755
|
+
}
|
|
5610
5756
|
function compound$1(children) {
|
|
5611
|
-
|
|
5612
|
-
|
|
5757
|
+
const copied = [...children];
|
|
5758
|
+
let h = fnvMixInt32(startHash("Compound"), copied.length);
|
|
5759
|
+
for (const c of copied) h = mix(h, c);
|
|
5613
5760
|
return {
|
|
5614
5761
|
kind: "Compound",
|
|
5615
|
-
children,
|
|
5762
|
+
children: copied,
|
|
5616
5763
|
structuralHash: h,
|
|
5617
|
-
freeParams: depsOf(...
|
|
5764
|
+
freeParams: depsOf(...copied)
|
|
5618
5765
|
};
|
|
5619
5766
|
}
|
|
5620
5767
|
function instance$1(source, placements, fuse = false) {
|
|
@@ -5653,7 +5800,10 @@ function outputKindOf(node) {
|
|
|
5653
5800
|
case "Rotate":
|
|
5654
5801
|
case "Scale":
|
|
5655
5802
|
case "Mirror": return outputKindOf(node.target);
|
|
5656
|
-
case "Extrude":
|
|
5803
|
+
case "Extrude":
|
|
5804
|
+
case "Revolve":
|
|
5805
|
+
case "Loft": return "Solid";
|
|
5806
|
+
case "Path": return "Wire";
|
|
5657
5807
|
case "Compound": return "Compound";
|
|
5658
5808
|
case "Instance": return node.fuse ? "Solid" : "Compound";
|
|
5659
5809
|
}
|
|
@@ -5933,17 +6083,289 @@ function evalInstance(node, ctx) {
|
|
|
5933
6083
|
return materialize(instance(r.value, node.placements), { fuse: node.fuse });
|
|
5934
6084
|
}
|
|
5935
6085
|
//#endregion
|
|
6086
|
+
//#region src/csg/evaluators/profileFace.ts
|
|
6087
|
+
/** Evaluate a profile child and prove it is an oriented planar face — the
|
|
6088
|
+
* input contract shared by every profile-consuming feature node. */
|
|
6089
|
+
function resolveProfileFace(ctx, profile, where) {
|
|
6090
|
+
const p = ctx.evalNode(profile);
|
|
6091
|
+
if (!p.ok) return p;
|
|
6092
|
+
if (!require_shapeTypes.isFace(p.value)) return require_errors.err(require_errors.typeCastError("CSG_NOT_FACE", `${where}: node did not produce a Face`));
|
|
6093
|
+
const oriented = require_validityTypes.orientedFace(p.value);
|
|
6094
|
+
if (!oriented.ok) return require_errors.err(require_errors.typeCastError("CSG_NOT_FACE", `${where}: ${oriented.error}`));
|
|
6095
|
+
if (!require_validityTypes.isPlanarFace(oriented.value)) return require_errors.err(require_errors.typeCastError("CSG_NOT_FACE", `${where}: face is not planar`));
|
|
6096
|
+
return require_errors.ok(oriented.value);
|
|
6097
|
+
}
|
|
6098
|
+
//#endregion
|
|
5936
6099
|
//#region src/csg/evaluators/extrude.ts
|
|
5937
6100
|
function evalExtrude(node, ctx) {
|
|
5938
6101
|
const v = evalVec3(node.vector, ctx.env, "Extrude.vector");
|
|
5939
6102
|
if (!v.ok) return v;
|
|
5940
|
-
const
|
|
6103
|
+
const face = resolveProfileFace(ctx, node.profile, "Extrude.profile");
|
|
6104
|
+
if (!face.ok) return face;
|
|
6105
|
+
return require_textBlueprints.extrude(face.value, v.value);
|
|
6106
|
+
}
|
|
6107
|
+
//#endregion
|
|
6108
|
+
//#region src/csg/evaluators/revolve.ts
|
|
6109
|
+
function evalRevolve(node, ctx) {
|
|
6110
|
+
const a = evalScalar(node.angle, ctx.env, "Revolve.angle");
|
|
6111
|
+
if (!a.ok) return a;
|
|
6112
|
+
if (a.value <= 0) return require_errors.err(require_errors.validationError("CSG_REVOLVE_ANGLE", `Revolve.angle must be positive (degrees), got ${a.value}`));
|
|
6113
|
+
const deg = Math.min(a.value, 360);
|
|
6114
|
+
const axis = node.axis ? evalVec3(node.axis, ctx.env, "Revolve.axis") : require_errors.ok([
|
|
6115
|
+
0,
|
|
6116
|
+
0,
|
|
6117
|
+
1
|
|
6118
|
+
]);
|
|
6119
|
+
if (!axis.ok) return axis;
|
|
6120
|
+
const at = node.at ? evalVec3(node.at, ctx.env, "Revolve.at") : require_errors.ok([
|
|
6121
|
+
0,
|
|
6122
|
+
0,
|
|
6123
|
+
0
|
|
6124
|
+
]);
|
|
6125
|
+
if (!at.ok) return at;
|
|
6126
|
+
const face = resolveProfileFace(ctx, node.profile, "Revolve.profile");
|
|
6127
|
+
if (!face.ok) return face;
|
|
6128
|
+
return require_textBlueprints.revolve(face.value, at.value, axis.value, deg * Math.PI / 180);
|
|
6129
|
+
}
|
|
6130
|
+
//#endregion
|
|
6131
|
+
//#region src/csg/evaluators/loft.ts
|
|
6132
|
+
function evalLoft(node, ctx) {
|
|
6133
|
+
try {
|
|
6134
|
+
var _usingCtx$3 = require_shapeTypes._usingCtx();
|
|
6135
|
+
if (node.sections.length < 2) return require_errors.err(require_errors.validationError("CSG_LOFT_SECTIONS", `Loft requires at least 2 sections, got ${node.sections.length}`));
|
|
6136
|
+
const scope = _usingCtx$3.u(new require_shapeTypes.DisposalScope());
|
|
6137
|
+
const wires = [];
|
|
6138
|
+
for (const [i, section] of node.sections.entries()) {
|
|
6139
|
+
const face = resolveProfileFace(ctx, section, `Loft.sections[${i}]`);
|
|
6140
|
+
if (!face.ok) return face;
|
|
6141
|
+
const w = require_faceFns.outerWire(face.value);
|
|
6142
|
+
scope.register(w);
|
|
6143
|
+
wires.push(w);
|
|
6144
|
+
}
|
|
6145
|
+
return require_loftFns.loft(wires, { ruled: node.ruled });
|
|
6146
|
+
} catch (_) {
|
|
6147
|
+
_usingCtx$3.e = _;
|
|
6148
|
+
} finally {
|
|
6149
|
+
_usingCtx$3.d();
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
//#endregion
|
|
6153
|
+
//#region src/csg/evaluators/path.ts
|
|
6154
|
+
var EPS = 1e-9;
|
|
6155
|
+
function v3(p) {
|
|
6156
|
+
return [
|
|
6157
|
+
p[0],
|
|
6158
|
+
p[1],
|
|
6159
|
+
0
|
|
6160
|
+
];
|
|
6161
|
+
}
|
|
6162
|
+
function fail(msg) {
|
|
6163
|
+
return require_errors.err(require_errors.validationError("CSG_PATH_SEGMENT", `Path: ${msg}`));
|
|
6164
|
+
}
|
|
6165
|
+
/** SVG-style endpoint circular arc: recover the on-arc midpoint so the edge
|
|
6166
|
+
* can be built as a three-point arc. */
|
|
6167
|
+
function circularArcMidpoint(from, to, r, largeArc, clockwise) {
|
|
6168
|
+
const [x1, y1] = from;
|
|
6169
|
+
const [x2, y2] = to;
|
|
6170
|
+
const dx = x2 - x1;
|
|
6171
|
+
const dy = y2 - y1;
|
|
6172
|
+
const d = Math.hypot(dx, dy);
|
|
6173
|
+
if (d < EPS) return fail("Arc: coincident endpoints");
|
|
6174
|
+
if (d > 2 * r + 1e-6) return fail(`Arc: radius ${r} too small for chord ${d}`);
|
|
6175
|
+
const h = Math.sqrt(Math.max(0, r * r - d / 2 * (d / 2)));
|
|
6176
|
+
const ux = dx / d;
|
|
6177
|
+
const uy = dy / d;
|
|
6178
|
+
const sign = clockwise === largeArc ? 1 : -1;
|
|
6179
|
+
const cx = (x1 + x2) / 2 + sign * h * -uy;
|
|
6180
|
+
const cy = (y1 + y2) / 2 + sign * h * ux;
|
|
6181
|
+
const a0 = Math.atan2(y1 - cy, x1 - cx);
|
|
6182
|
+
let a1 = Math.atan2(y2 - cy, x2 - cx);
|
|
6183
|
+
if (clockwise) while (a1 >= a0 - EPS) a1 -= 2 * Math.PI;
|
|
6184
|
+
else while (a1 <= a0 + EPS) a1 += 2 * Math.PI;
|
|
6185
|
+
const amid = (a0 + a1) / 2;
|
|
6186
|
+
return require_errors.ok([cx + r * Math.cos(amid), cy + r * Math.sin(amid)]);
|
|
6187
|
+
}
|
|
6188
|
+
/** SVG F.6.5 endpoint-to-center conversion (math y-up; sweep = !clockwise).
|
|
6189
|
+
* Angles are relative to the rotated x-axis. */
|
|
6190
|
+
function ellipseArcParams(from, to, radii, phi, largeArc, clockwise) {
|
|
6191
|
+
const sweep = !clockwise;
|
|
6192
|
+
let [rx, ry] = radii;
|
|
6193
|
+
if (rx <= 0 || ry <= 0) return fail("EllipseArc: non-positive radius");
|
|
6194
|
+
const cosPhi = Math.cos(phi);
|
|
6195
|
+
const sinPhi = Math.sin(phi);
|
|
6196
|
+
const hx = (from[0] - to[0]) / 2;
|
|
6197
|
+
const hy = (from[1] - to[1]) / 2;
|
|
6198
|
+
const x1p = cosPhi * hx + sinPhi * hy;
|
|
6199
|
+
const y1p = -sinPhi * hx + cosPhi * hy;
|
|
6200
|
+
const lambda = x1p * x1p / (rx * rx) + y1p * y1p / (ry * ry);
|
|
6201
|
+
if (lambda > 1) {
|
|
6202
|
+
const s = Math.sqrt(lambda);
|
|
6203
|
+
rx *= s;
|
|
6204
|
+
ry *= s;
|
|
6205
|
+
}
|
|
6206
|
+
const num = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;
|
|
6207
|
+
const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;
|
|
6208
|
+
if (den < EPS) return fail("EllipseArc: coincident endpoints");
|
|
6209
|
+
const coef = (largeArc !== sweep ? 1 : -1) * Math.sqrt(Math.max(0, num / den));
|
|
6210
|
+
const cxp = coef * (rx * y1p / ry);
|
|
6211
|
+
const cyp = coef * (-(ry * x1p) / rx);
|
|
6212
|
+
const cx = cosPhi * cxp - sinPhi * cyp + (from[0] + to[0]) / 2;
|
|
6213
|
+
const cy = sinPhi * cxp + cosPhi * cyp + (from[1] + to[1]) / 2;
|
|
6214
|
+
const theta1 = Math.atan2((y1p - cyp) / ry, (x1p - cxp) / rx);
|
|
6215
|
+
let dTheta = Math.atan2((-y1p - cyp) / ry, (-x1p - cxp) / rx) - theta1;
|
|
6216
|
+
if (!sweep && dTheta > 0) dTheta -= 2 * Math.PI;
|
|
6217
|
+
if (sweep && dTheta < 0) dTheta += 2 * Math.PI;
|
|
6218
|
+
const t1 = dTheta >= 0 ? theta1 : theta1 + dTheta;
|
|
6219
|
+
const t2 = dTheta >= 0 ? theta1 + dTheta : theta1;
|
|
6220
|
+
return require_errors.ok({
|
|
6221
|
+
center: [cx, cy],
|
|
6222
|
+
theta1: t1,
|
|
6223
|
+
theta2: t2,
|
|
6224
|
+
rx,
|
|
6225
|
+
ry
|
|
6226
|
+
});
|
|
6227
|
+
}
|
|
6228
|
+
function ellipseEdge(from, seg, ctx) {
|
|
6229
|
+
if (seg.kind !== "EllipseArc") return fail("internal: not an ellipse segment");
|
|
6230
|
+
const to = evalVec2(seg.to, ctx.env, "Path.EllipseArc.to");
|
|
6231
|
+
if (!to.ok) return to;
|
|
6232
|
+
const radii = evalVec2(seg.radii, ctx.env, "Path.EllipseArc.radii");
|
|
6233
|
+
if (!radii.ok) return radii;
|
|
6234
|
+
const rotDeg = evalScalar(seg.rotation, ctx.env, "Path.EllipseArc.rotation");
|
|
6235
|
+
if (!rotDeg.ok) return rotDeg;
|
|
6236
|
+
const phi = rotDeg.value * Math.PI / 180;
|
|
6237
|
+
const p = ellipseArcParams(from, to.value, radii.value, phi, seg.largeArc, seg.clockwise);
|
|
5941
6238
|
if (!p.ok) return p;
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
if (
|
|
5946
|
-
|
|
6239
|
+
const { center, theta1, theta2, rx, ry } = p.value;
|
|
6240
|
+
let arc;
|
|
6241
|
+
let psi;
|
|
6242
|
+
if (rx >= ry) {
|
|
6243
|
+
arc = require_surfaceBuilders.makeEllipseArc(rx, ry, theta1, theta2, v3(center), [
|
|
6244
|
+
0,
|
|
6245
|
+
0,
|
|
6246
|
+
1
|
|
6247
|
+
]);
|
|
6248
|
+
psi = phi;
|
|
6249
|
+
} else {
|
|
6250
|
+
arc = require_surfaceBuilders.makeEllipseArc(ry, rx, theta1 - Math.PI / 2, theta2 - Math.PI / 2, v3(center), [
|
|
6251
|
+
0,
|
|
6252
|
+
0,
|
|
6253
|
+
1
|
|
6254
|
+
]);
|
|
6255
|
+
psi = phi + Math.PI / 2;
|
|
6256
|
+
}
|
|
6257
|
+
if (!arc.ok) return arc;
|
|
6258
|
+
let edge = arc.value;
|
|
6259
|
+
if (Math.abs(psi) > EPS) try {
|
|
6260
|
+
var _usingCtx$2 = require_shapeTypes._usingCtx();
|
|
6261
|
+
const rotated = rotateEdgeAbout(_usingCtx$2.u(edge), psi * 180 / Math.PI, center);
|
|
6262
|
+
if (!rotated.ok) return rotated;
|
|
6263
|
+
edge = rotated.value;
|
|
6264
|
+
} catch (_) {
|
|
6265
|
+
_usingCtx$2.e = _;
|
|
6266
|
+
} finally {
|
|
6267
|
+
_usingCtx$2.d();
|
|
6268
|
+
}
|
|
6269
|
+
if (seg.clockwise) try {
|
|
6270
|
+
var _usingCtx3 = require_shapeTypes._usingCtx();
|
|
6271
|
+
const tmp = _usingCtx3.u(edge);
|
|
6272
|
+
edge = require_curveFns.flipOrientation(tmp);
|
|
6273
|
+
} catch (_) {
|
|
6274
|
+
_usingCtx3.e = _;
|
|
6275
|
+
} finally {
|
|
6276
|
+
_usingCtx3.d();
|
|
6277
|
+
}
|
|
6278
|
+
return require_errors.ok(edge);
|
|
6279
|
+
}
|
|
6280
|
+
/** Rotate an edge about the Z axis at `center` (degrees) via a cheap location
|
|
6281
|
+
* re-tag; returns a fresh, independently-disposable handle. Kernels without
|
|
6282
|
+
* an edge-relocation path (brepkit) surface as a Result error, not a throw. */
|
|
6283
|
+
function rotateEdgeAbout(edge, angleDeg, center) {
|
|
6284
|
+
const kernel = require_shapeTypes.getKernel();
|
|
6285
|
+
const { handle, dispose } = kernel.composeTransform([{
|
|
6286
|
+
type: "rotate",
|
|
6287
|
+
angle: angleDeg,
|
|
6288
|
+
axis: [
|
|
6289
|
+
0,
|
|
6290
|
+
0,
|
|
6291
|
+
1
|
|
6292
|
+
],
|
|
6293
|
+
center: [
|
|
6294
|
+
center[0],
|
|
6295
|
+
center[1],
|
|
6296
|
+
0
|
|
6297
|
+
]
|
|
6298
|
+
}]);
|
|
6299
|
+
try {
|
|
6300
|
+
return require_errors.ok(require_shapeTypes.castShape(kernel.locate(edge.wrapped, handle)));
|
|
6301
|
+
} catch (e) {
|
|
6302
|
+
return require_errors.err(require_errors.kernelError("CSG_PATH_ELLIPSE_ROTATE", "Path: kernel cannot relocate ellipse-arc edges", e, { operation: "evalPath" }));
|
|
6303
|
+
} finally {
|
|
6304
|
+
dispose();
|
|
6305
|
+
}
|
|
6306
|
+
}
|
|
6307
|
+
function segmentEnd(seg, ctx) {
|
|
6308
|
+
return evalVec2(seg.to, ctx.env, `Path.${seg.kind}.to`);
|
|
6309
|
+
}
|
|
6310
|
+
function segmentToEdge(from, seg, ctx) {
|
|
6311
|
+
switch (seg.kind) {
|
|
6312
|
+
case "Line": {
|
|
6313
|
+
const to = segmentEnd(seg, ctx);
|
|
6314
|
+
if (!to.ok) return to;
|
|
6315
|
+
if (Math.hypot(to.value[0] - from[0], to.value[1] - from[1]) < EPS) return fail("Line: zero length");
|
|
6316
|
+
return require_errors.ok(require_surfaceBuilders.makeLine(v3(from), v3(to.value)));
|
|
6317
|
+
}
|
|
6318
|
+
case "Arc": {
|
|
6319
|
+
const to = segmentEnd(seg, ctx);
|
|
6320
|
+
if (!to.ok) return to;
|
|
6321
|
+
const r = evalScalar(seg.radius, ctx.env, "Path.Arc.radius");
|
|
6322
|
+
if (!r.ok) return r;
|
|
6323
|
+
const mid = circularArcMidpoint(from, to.value, r.value, seg.largeArc, seg.clockwise);
|
|
6324
|
+
if (!mid.ok) return mid;
|
|
6325
|
+
return require_errors.ok(require_surfaceBuilders.makeThreePointArc(v3(from), v3(mid.value), v3(to.value)));
|
|
6326
|
+
}
|
|
6327
|
+
case "Bezier": {
|
|
6328
|
+
const to = segmentEnd(seg, ctx);
|
|
6329
|
+
if (!to.ok) return to;
|
|
6330
|
+
const controls = [];
|
|
6331
|
+
for (const c of seg.controls) {
|
|
6332
|
+
const cv = evalVec2(c, ctx.env, "Path.Bezier.control");
|
|
6333
|
+
if (!cv.ok) return cv;
|
|
6334
|
+
controls.push(v3(cv.value));
|
|
6335
|
+
}
|
|
6336
|
+
return require_surfaceBuilders.makeBezierCurve([
|
|
6337
|
+
v3(from),
|
|
6338
|
+
...controls,
|
|
6339
|
+
v3(to.value)
|
|
6340
|
+
]);
|
|
6341
|
+
}
|
|
6342
|
+
case "EllipseArc": return ellipseEdge(from, seg, ctx);
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
function evalPath(node, ctx) {
|
|
6346
|
+
try {
|
|
6347
|
+
var _usingCtx4 = require_shapeTypes._usingCtx();
|
|
6348
|
+
if (node.segments.length === 0) return fail("at least one segment required");
|
|
6349
|
+
const start = evalVec2(node.start, ctx.env, "Path.start");
|
|
6350
|
+
if (!start.ok) return start;
|
|
6351
|
+
const scope = _usingCtx4.u(new require_shapeTypes.DisposalScope());
|
|
6352
|
+
const edges = [];
|
|
6353
|
+
let cur = start.value;
|
|
6354
|
+
for (const seg of node.segments) {
|
|
6355
|
+
const e = segmentToEdge(cur, seg, ctx);
|
|
6356
|
+
if (!e.ok) return e;
|
|
6357
|
+
scope.register(e.value);
|
|
6358
|
+
edges.push(e.value);
|
|
6359
|
+
const end = segmentEnd(seg, ctx);
|
|
6360
|
+
if (!end.ok) return end;
|
|
6361
|
+
cur = end.value;
|
|
6362
|
+
}
|
|
6363
|
+
return require_surfaceBuilders.assembleWire(edges);
|
|
6364
|
+
} catch (_) {
|
|
6365
|
+
_usingCtx4.e = _;
|
|
6366
|
+
} finally {
|
|
6367
|
+
_usingCtx4.d();
|
|
6368
|
+
}
|
|
5947
6369
|
}
|
|
5948
6370
|
//#endregion
|
|
5949
6371
|
//#region src/csg/evaluate.ts
|
|
@@ -5971,6 +6393,9 @@ function dispatch(node, ctx) {
|
|
|
5971
6393
|
case "Compound": return evalCompound(node, ctx);
|
|
5972
6394
|
case "Instance": return evalInstance(node, ctx);
|
|
5973
6395
|
case "Extrude": return evalExtrude(node, ctx);
|
|
6396
|
+
case "Revolve": return evalRevolve(node, ctx);
|
|
6397
|
+
case "Loft": return evalLoft(node, ctx);
|
|
6398
|
+
case "Path": return evalPath(node, ctx);
|
|
5974
6399
|
}
|
|
5975
6400
|
}
|
|
5976
6401
|
function hashExprValue(h, v) {
|
|
@@ -6507,6 +6932,34 @@ function booleanToJson(n) {
|
|
|
6507
6932
|
function optExprToJson(e) {
|
|
6508
6933
|
return e ? exprToJson(e) : void 0;
|
|
6509
6934
|
}
|
|
6935
|
+
function segmentToJson(s) {
|
|
6936
|
+
switch (s.kind) {
|
|
6937
|
+
case "Line": return {
|
|
6938
|
+
kind: "Line",
|
|
6939
|
+
to: exprToJson(s.to)
|
|
6940
|
+
};
|
|
6941
|
+
case "Arc": return {
|
|
6942
|
+
kind: "Arc",
|
|
6943
|
+
to: exprToJson(s.to),
|
|
6944
|
+
radius: exprToJson(s.radius),
|
|
6945
|
+
largeArc: s.largeArc,
|
|
6946
|
+
clockwise: s.clockwise
|
|
6947
|
+
};
|
|
6948
|
+
case "Bezier": return {
|
|
6949
|
+
kind: "Bezier",
|
|
6950
|
+
controls: s.controls.map(exprToJson),
|
|
6951
|
+
to: exprToJson(s.to)
|
|
6952
|
+
};
|
|
6953
|
+
case "EllipseArc": return {
|
|
6954
|
+
kind: "EllipseArc",
|
|
6955
|
+
to: exprToJson(s.to),
|
|
6956
|
+
radii: exprToJson(s.radii),
|
|
6957
|
+
rotation: exprToJson(s.rotation),
|
|
6958
|
+
largeArc: s.largeArc,
|
|
6959
|
+
clockwise: s.clockwise
|
|
6960
|
+
};
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6510
6963
|
function transformToJson(n) {
|
|
6511
6964
|
switch (n.kind) {
|
|
6512
6965
|
case "Translate": return {
|
|
@@ -6542,6 +6995,23 @@ function nodeToJson(n) {
|
|
|
6542
6995
|
profile: nodeToJson(n.profile),
|
|
6543
6996
|
vector: exprToJson(n.vector)
|
|
6544
6997
|
};
|
|
6998
|
+
if (n.kind === "Revolve") return {
|
|
6999
|
+
kind: "Revolve",
|
|
7000
|
+
profile: nodeToJson(n.profile),
|
|
7001
|
+
angle: exprToJson(n.angle),
|
|
7002
|
+
axis: optExprToJson(n.axis),
|
|
7003
|
+
at: optExprToJson(n.at)
|
|
7004
|
+
};
|
|
7005
|
+
if (n.kind === "Loft") return {
|
|
7006
|
+
kind: "Loft",
|
|
7007
|
+
sections: n.sections.map(nodeToJson),
|
|
7008
|
+
ruled: n.ruled
|
|
7009
|
+
};
|
|
7010
|
+
if (n.kind === "Path") return {
|
|
7011
|
+
kind: "Path",
|
|
7012
|
+
start: exprToJson(n.start),
|
|
7013
|
+
segments: n.segments.map(segmentToJson)
|
|
7014
|
+
};
|
|
6545
7015
|
if (n.kind === "Compound") return {
|
|
6546
7016
|
kind: "Compound",
|
|
6547
7017
|
children: n.children.map(nodeToJson)
|
|
@@ -6694,6 +7164,9 @@ function readNode(j) {
|
|
|
6694
7164
|
case "Compound": return readCompound(j);
|
|
6695
7165
|
case "Instance": return readInstance(j);
|
|
6696
7166
|
case "Extrude": return readExtrude(j);
|
|
7167
|
+
case "Revolve": return readRevolve(j);
|
|
7168
|
+
case "Loft": return readLoft(j);
|
|
7169
|
+
case "Path": return readPath(j);
|
|
6697
7170
|
default: return bad(`unknown node kind: ${String(kind)}`);
|
|
6698
7171
|
}
|
|
6699
7172
|
}
|
|
@@ -6855,6 +7328,93 @@ function readExtrude(j) {
|
|
|
6855
7328
|
if (!vector.ok) return vector;
|
|
6856
7329
|
return require_errors.ok(extrude$1(profile.value, vector.value));
|
|
6857
7330
|
}
|
|
7331
|
+
function readRevolve(j) {
|
|
7332
|
+
const profile = readNode(j["profile"]);
|
|
7333
|
+
if (!profile.ok) return profile;
|
|
7334
|
+
const angle = readExpr(j["angle"]);
|
|
7335
|
+
if (!angle.ok) return angle;
|
|
7336
|
+
const axis = readOptExpr(j, "axis");
|
|
7337
|
+
if (!axis.ok) return axis;
|
|
7338
|
+
const at = readOptExpr(j, "at");
|
|
7339
|
+
if (!at.ok) return at;
|
|
7340
|
+
return require_errors.ok(revolve$1(profile.value, angle.value, {
|
|
7341
|
+
axis: axis.value,
|
|
7342
|
+
at: at.value
|
|
7343
|
+
}));
|
|
7344
|
+
}
|
|
7345
|
+
function readLoft(j) {
|
|
7346
|
+
const sections = readNodeArray(j["sections"], "Loft.sections");
|
|
7347
|
+
if (!sections.ok) return sections;
|
|
7348
|
+
const ruled = j["ruled"];
|
|
7349
|
+
if (ruled !== void 0 && typeof ruled !== "boolean") return bad("Loft.ruled: not a boolean");
|
|
7350
|
+
return require_errors.ok(loft$1(sections.value, { ruled }));
|
|
7351
|
+
}
|
|
7352
|
+
function readFlag(j, key) {
|
|
7353
|
+
const v = j[key];
|
|
7354
|
+
if (v === void 0) return require_errors.ok(false);
|
|
7355
|
+
return typeof v === "boolean" ? require_errors.ok(v) : bad(`${key}: not a boolean`);
|
|
7356
|
+
}
|
|
7357
|
+
function readSegment(j) {
|
|
7358
|
+
if (!isObj(j)) return bad("segment: not an object");
|
|
7359
|
+
const to = readExpr(j["to"]);
|
|
7360
|
+
if (!to.ok) return to;
|
|
7361
|
+
const kind = j["kind"];
|
|
7362
|
+
switch (kind) {
|
|
7363
|
+
case "Line": return require_errors.ok(lineTo(to.value));
|
|
7364
|
+
case "Arc": {
|
|
7365
|
+
const radius = readExpr(j["radius"]);
|
|
7366
|
+
if (!radius.ok) return radius;
|
|
7367
|
+
const largeArc = readFlag(j, "largeArc");
|
|
7368
|
+
if (!largeArc.ok) return largeArc;
|
|
7369
|
+
const clockwise = readFlag(j, "clockwise");
|
|
7370
|
+
if (!clockwise.ok) return clockwise;
|
|
7371
|
+
return require_errors.ok(arcTo(to.value, radius.value, {
|
|
7372
|
+
largeArc: largeArc.value,
|
|
7373
|
+
clockwise: clockwise.value
|
|
7374
|
+
}));
|
|
7375
|
+
}
|
|
7376
|
+
case "Bezier": {
|
|
7377
|
+
const raw = j["controls"];
|
|
7378
|
+
if (!Array.isArray(raw)) return bad("Bezier.controls: not array");
|
|
7379
|
+
const controls = [];
|
|
7380
|
+
for (const c of raw) {
|
|
7381
|
+
const r = readExpr(c);
|
|
7382
|
+
if (!r.ok) return r;
|
|
7383
|
+
controls.push(r.value);
|
|
7384
|
+
}
|
|
7385
|
+
return require_errors.ok(bezierTo(controls, to.value));
|
|
7386
|
+
}
|
|
7387
|
+
case "EllipseArc": {
|
|
7388
|
+
const radii = readExpr(j["radii"]);
|
|
7389
|
+
if (!radii.ok) return radii;
|
|
7390
|
+
const rotation = readExpr(j["rotation"]);
|
|
7391
|
+
if (!rotation.ok) return rotation;
|
|
7392
|
+
const largeArc = readFlag(j, "largeArc");
|
|
7393
|
+
if (!largeArc.ok) return largeArc;
|
|
7394
|
+
const clockwise = readFlag(j, "clockwise");
|
|
7395
|
+
if (!clockwise.ok) return clockwise;
|
|
7396
|
+
return require_errors.ok(ellipseArcTo(to.value, radii.value, {
|
|
7397
|
+
rotation: rotation.value,
|
|
7398
|
+
largeArc: largeArc.value,
|
|
7399
|
+
clockwise: clockwise.value
|
|
7400
|
+
}));
|
|
7401
|
+
}
|
|
7402
|
+
default: return bad(`unknown segment kind: ${String(kind)}`);
|
|
7403
|
+
}
|
|
7404
|
+
}
|
|
7405
|
+
function readPath(j) {
|
|
7406
|
+
const start = readExpr(j["start"]);
|
|
7407
|
+
if (!start.ok) return start;
|
|
7408
|
+
const raw = j["segments"];
|
|
7409
|
+
if (!Array.isArray(raw)) return bad("Path.segments: not array");
|
|
7410
|
+
const segments = [];
|
|
7411
|
+
for (const s of raw) {
|
|
7412
|
+
const r = readSegment(s);
|
|
7413
|
+
if (!r.ok) return r;
|
|
7414
|
+
segments.push(r.value);
|
|
7415
|
+
}
|
|
7416
|
+
return require_errors.ok(path(start.value, segments));
|
|
7417
|
+
}
|
|
6858
7418
|
function readCompound(j) {
|
|
6859
7419
|
const children = readNodeArray(j["children"], "Compound.children");
|
|
6860
7420
|
return children.ok ? require_errors.ok(compound$1(children.value)) : children;
|
|
@@ -6966,7 +7526,21 @@ function optimizeNode(n) {
|
|
|
6966
7526
|
});
|
|
6967
7527
|
case "Compound": return compound$1(n.children.map(optimizeNode).filter((c) => c.kind !== "Empty"));
|
|
6968
7528
|
case "Instance": return instance$1(optimizeNode(n.source), n.placements, n.fuse);
|
|
7529
|
+
case "Extrude":
|
|
7530
|
+
case "Revolve":
|
|
7531
|
+
case "Loft":
|
|
7532
|
+
case "Path": return optimizeFeature(n);
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7535
|
+
function optimizeFeature(n) {
|
|
7536
|
+
switch (n.kind) {
|
|
6969
7537
|
case "Extrude": return extrude$1(optimizeNode(n.profile), foldExpr(n.vector));
|
|
7538
|
+
case "Revolve": return revolve$1(optimizeNode(n.profile), foldExpr(n.angle), {
|
|
7539
|
+
axis: n.axis ? foldExpr(n.axis) : void 0,
|
|
7540
|
+
at: n.at ? foldExpr(n.at) : void 0
|
|
7541
|
+
});
|
|
7542
|
+
case "Loft": return loft$1(n.sections.map(optimizeNode), { ruled: n.ruled });
|
|
7543
|
+
case "Path": return path(foldExpr(n.start), n.segments.map((s) => foldSegment(s, foldExpr)));
|
|
6970
7544
|
}
|
|
6971
7545
|
}
|
|
6972
7546
|
function optimizeFuse(a, b, tol) {
|
|
@@ -7039,7 +7613,8 @@ function rebuildChildren(n, pred, repl) {
|
|
|
7039
7613
|
case "Circle":
|
|
7040
7614
|
case "Line":
|
|
7041
7615
|
case "Vertex":
|
|
7042
|
-
case "Empty":
|
|
7616
|
+
case "Empty":
|
|
7617
|
+
case "Path": return n;
|
|
7043
7618
|
case "Fuse": return fuse$1(walk(n.a, pred, repl), walk(n.b, pred, repl), n.tolerance);
|
|
7044
7619
|
case "Cut": return cut$1(walk(n.a, pred, repl), walk(n.b, pred, repl), n.tolerance);
|
|
7045
7620
|
case "Intersect": return intersect$1(walk(n.a, pred, repl), walk(n.b, pred, repl), n.tolerance);
|
|
@@ -7058,6 +7633,11 @@ function rebuildChildren(n, pred, repl) {
|
|
|
7058
7633
|
case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl)));
|
|
7059
7634
|
case "Instance": return instance$1(walk(n.source, pred, repl), n.placements, n.fuse);
|
|
7060
7635
|
case "Extrude": return extrude$1(walk(n.profile, pred, repl), n.vector);
|
|
7636
|
+
case "Revolve": return revolve$1(walk(n.profile, pred, repl), n.angle, {
|
|
7637
|
+
axis: n.axis,
|
|
7638
|
+
at: n.at
|
|
7639
|
+
});
|
|
7640
|
+
case "Loft": return loft$1(n.sections.map((s) => walk(s, pred, repl)), { ruled: n.ruled });
|
|
7061
7641
|
}
|
|
7062
7642
|
}
|
|
7063
7643
|
function forEachNode(root, fn) {
|
|
@@ -7075,7 +7655,8 @@ function childrenOf(n) {
|
|
|
7075
7655
|
case "Circle":
|
|
7076
7656
|
case "Line":
|
|
7077
7657
|
case "Vertex":
|
|
7078
|
-
case "Empty":
|
|
7658
|
+
case "Empty":
|
|
7659
|
+
case "Path": return [];
|
|
7079
7660
|
case "Fuse":
|
|
7080
7661
|
case "Cut":
|
|
7081
7662
|
case "Intersect": return [n.a, n.b];
|
|
@@ -7087,7 +7668,9 @@ function childrenOf(n) {
|
|
|
7087
7668
|
case "Mirror": return [n.target];
|
|
7088
7669
|
case "Compound": return n.children;
|
|
7089
7670
|
case "Instance": return [n.source];
|
|
7090
|
-
case "Extrude":
|
|
7671
|
+
case "Extrude":
|
|
7672
|
+
case "Revolve": return [n.profile];
|
|
7673
|
+
case "Loft": return n.sections;
|
|
7091
7674
|
}
|
|
7092
7675
|
}
|
|
7093
7676
|
function nodeCount(root) {
|
|
@@ -7103,9 +7686,11 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7103
7686
|
CSG_VERSION: () => 1,
|
|
7104
7687
|
Evaluator: () => Evaluator,
|
|
7105
7688
|
add: () => add,
|
|
7689
|
+
arcTo: () => arcTo,
|
|
7106
7690
|
asScalarExpr: () => asScalarExpr,
|
|
7107
7691
|
asVec2Expr: () => asVec2Expr,
|
|
7108
7692
|
asVec3Expr: () => asVec3Expr,
|
|
7693
|
+
bezierTo: () => bezierTo,
|
|
7109
7694
|
binOp: () => binOp,
|
|
7110
7695
|
box: () => box$2,
|
|
7111
7696
|
buildVec: () => buildVec,
|
|
@@ -7116,6 +7701,7 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7116
7701
|
cut: () => cut$1,
|
|
7117
7702
|
cutAll: () => cutAll$1,
|
|
7118
7703
|
cylinder: () => cylinder$2,
|
|
7704
|
+
ellipseArcTo: () => ellipseArcTo,
|
|
7119
7705
|
emptyFace: () => emptyFace,
|
|
7120
7706
|
emptySolid: () => emptySolid,
|
|
7121
7707
|
emptyWire: () => emptyWire,
|
|
@@ -7128,6 +7714,8 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7128
7714
|
instance: () => instance$1,
|
|
7129
7715
|
intersect: () => intersect$1,
|
|
7130
7716
|
line: () => line$1,
|
|
7717
|
+
lineTo: () => lineTo,
|
|
7718
|
+
loft: () => loft$1,
|
|
7131
7719
|
mirror: () => mirror$1,
|
|
7132
7720
|
mul: () => mul,
|
|
7133
7721
|
nodeCount: () => nodeCount,
|
|
@@ -7135,8 +7723,10 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7135
7723
|
optimize: () => optimize,
|
|
7136
7724
|
outputKindOf: () => outputKindOf,
|
|
7137
7725
|
param: () => param,
|
|
7726
|
+
path: () => path,
|
|
7138
7727
|
polygon: () => polygon$1,
|
|
7139
7728
|
replaceNode: () => replaceNode,
|
|
7729
|
+
revolve: () => revolve$1,
|
|
7140
7730
|
rotate: () => rotate$1,
|
|
7141
7731
|
scale: () => scale$1,
|
|
7142
7732
|
sphere: () => sphere$2,
|