brepjs 18.124.8 → 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 +689 -57
- package/dist/brepjs.js +700 -68
- package/dist/csg/builders.d.ts +21 -2
- package/dist/csg/evaluators/extrude.d.ts +5 -0
- 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 +32 -1
- package/package.json +7 -7
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) {
|
|
@@ -5597,14 +5692,76 @@ function mirror$1(target, options) {
|
|
|
5597
5692
|
freeParams: depsOf(target, nE, atE)
|
|
5598
5693
|
};
|
|
5599
5694
|
}
|
|
5695
|
+
function extrude$1(profile, vector) {
|
|
5696
|
+
const ve = asVec3Expr(vector);
|
|
5697
|
+
return {
|
|
5698
|
+
kind: "Extrude",
|
|
5699
|
+
profile,
|
|
5700
|
+
vector: ve,
|
|
5701
|
+
structuralHash: mix(mix(startHash("Extrude"), profile), ve),
|
|
5702
|
+
freeParams: depsOf(profile, ve)
|
|
5703
|
+
};
|
|
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
|
+
}
|
|
5600
5756
|
function compound$1(children) {
|
|
5601
|
-
|
|
5602
|
-
|
|
5757
|
+
const copied = [...children];
|
|
5758
|
+
let h = fnvMixInt32(startHash("Compound"), copied.length);
|
|
5759
|
+
for (const c of copied) h = mix(h, c);
|
|
5603
5760
|
return {
|
|
5604
5761
|
kind: "Compound",
|
|
5605
|
-
children,
|
|
5762
|
+
children: copied,
|
|
5606
5763
|
structuralHash: h,
|
|
5607
|
-
freeParams: depsOf(...
|
|
5764
|
+
freeParams: depsOf(...copied)
|
|
5608
5765
|
};
|
|
5609
5766
|
}
|
|
5610
5767
|
function instance$1(source, placements, fuse = false) {
|
|
@@ -5643,6 +5800,10 @@ function outputKindOf(node) {
|
|
|
5643
5800
|
case "Rotate":
|
|
5644
5801
|
case "Scale":
|
|
5645
5802
|
case "Mirror": return outputKindOf(node.target);
|
|
5803
|
+
case "Extrude":
|
|
5804
|
+
case "Revolve":
|
|
5805
|
+
case "Loft": return "Solid";
|
|
5806
|
+
case "Path": return "Wire";
|
|
5646
5807
|
case "Compound": return "Compound";
|
|
5647
5808
|
case "Instance": return node.fuse ? "Solid" : "Compound";
|
|
5648
5809
|
}
|
|
@@ -5922,6 +6083,291 @@ function evalInstance(node, ctx) {
|
|
|
5922
6083
|
return materialize(instance(r.value, node.placements), { fuse: node.fuse });
|
|
5923
6084
|
}
|
|
5924
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
|
|
6099
|
+
//#region src/csg/evaluators/extrude.ts
|
|
6100
|
+
function evalExtrude(node, ctx) {
|
|
6101
|
+
const v = evalVec3(node.vector, ctx.env, "Extrude.vector");
|
|
6102
|
+
if (!v.ok) return v;
|
|
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);
|
|
6238
|
+
if (!p.ok) return p;
|
|
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
|
+
}
|
|
6369
|
+
}
|
|
6370
|
+
//#endregion
|
|
5925
6371
|
//#region src/csg/evaluate.ts
|
|
5926
6372
|
function dispatch(node, ctx) {
|
|
5927
6373
|
switch (node.kind) {
|
|
@@ -5946,6 +6392,10 @@ function dispatch(node, ctx) {
|
|
|
5946
6392
|
case "Mirror": return evalMirror(node, ctx);
|
|
5947
6393
|
case "Compound": return evalCompound(node, ctx);
|
|
5948
6394
|
case "Instance": return evalInstance(node, ctx);
|
|
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);
|
|
5949
6399
|
}
|
|
5950
6400
|
}
|
|
5951
6401
|
function hashExprValue(h, v) {
|
|
@@ -6482,6 +6932,34 @@ function booleanToJson(n) {
|
|
|
6482
6932
|
function optExprToJson(e) {
|
|
6483
6933
|
return e ? exprToJson(e) : void 0;
|
|
6484
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
|
+
}
|
|
6485
6963
|
function transformToJson(n) {
|
|
6486
6964
|
switch (n.kind) {
|
|
6487
6965
|
case "Translate": return {
|
|
@@ -6512,6 +6990,28 @@ function transformToJson(n) {
|
|
|
6512
6990
|
}
|
|
6513
6991
|
}
|
|
6514
6992
|
function nodeToJson(n) {
|
|
6993
|
+
if (n.kind === "Extrude") return {
|
|
6994
|
+
kind: "Extrude",
|
|
6995
|
+
profile: nodeToJson(n.profile),
|
|
6996
|
+
vector: exprToJson(n.vector)
|
|
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
|
+
};
|
|
6515
7015
|
if (n.kind === "Compound") return {
|
|
6516
7016
|
kind: "Compound",
|
|
6517
7017
|
children: n.children.map(nodeToJson)
|
|
@@ -6663,6 +7163,10 @@ function readNode(j) {
|
|
|
6663
7163
|
case "Mirror": return readTransform(kind, j);
|
|
6664
7164
|
case "Compound": return readCompound(j);
|
|
6665
7165
|
case "Instance": return readInstance(j);
|
|
7166
|
+
case "Extrude": return readExtrude(j);
|
|
7167
|
+
case "Revolve": return readRevolve(j);
|
|
7168
|
+
case "Loft": return readLoft(j);
|
|
7169
|
+
case "Path": return readPath(j);
|
|
6666
7170
|
default: return bad(`unknown node kind: ${String(kind)}`);
|
|
6667
7171
|
}
|
|
6668
7172
|
}
|
|
@@ -6817,6 +7321,100 @@ function readMirror(j, target) {
|
|
|
6817
7321
|
at: at.value
|
|
6818
7322
|
}));
|
|
6819
7323
|
}
|
|
7324
|
+
function readExtrude(j) {
|
|
7325
|
+
const profile = readNode(j["profile"]);
|
|
7326
|
+
if (!profile.ok) return profile;
|
|
7327
|
+
const vector = readExpr(j["vector"]);
|
|
7328
|
+
if (!vector.ok) return vector;
|
|
7329
|
+
return require_errors.ok(extrude$1(profile.value, vector.value));
|
|
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
|
+
}
|
|
6820
7418
|
function readCompound(j) {
|
|
6821
7419
|
const children = readNodeArray(j["children"], "Compound.children");
|
|
6822
7420
|
return children.ok ? require_errors.ok(compound$1(children.value)) : children;
|
|
@@ -6928,6 +7526,21 @@ function optimizeNode(n) {
|
|
|
6928
7526
|
});
|
|
6929
7527
|
case "Compound": return compound$1(n.children.map(optimizeNode).filter((c) => c.kind !== "Empty"));
|
|
6930
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) {
|
|
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)));
|
|
6931
7544
|
}
|
|
6932
7545
|
}
|
|
6933
7546
|
function optimizeFuse(a, b, tol) {
|
|
@@ -7000,7 +7613,8 @@ function rebuildChildren(n, pred, repl) {
|
|
|
7000
7613
|
case "Circle":
|
|
7001
7614
|
case "Line":
|
|
7002
7615
|
case "Vertex":
|
|
7003
|
-
case "Empty":
|
|
7616
|
+
case "Empty":
|
|
7617
|
+
case "Path": return n;
|
|
7004
7618
|
case "Fuse": return fuse$1(walk(n.a, pred, repl), walk(n.b, pred, repl), n.tolerance);
|
|
7005
7619
|
case "Cut": return cut$1(walk(n.a, pred, repl), walk(n.b, pred, repl), n.tolerance);
|
|
7006
7620
|
case "Intersect": return intersect$1(walk(n.a, pred, repl), walk(n.b, pred, repl), n.tolerance);
|
|
@@ -7018,6 +7632,12 @@ function rebuildChildren(n, pred, repl) {
|
|
|
7018
7632
|
});
|
|
7019
7633
|
case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl)));
|
|
7020
7634
|
case "Instance": return instance$1(walk(n.source, pred, repl), n.placements, n.fuse);
|
|
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 });
|
|
7021
7641
|
}
|
|
7022
7642
|
}
|
|
7023
7643
|
function forEachNode(root, fn) {
|
|
@@ -7035,7 +7655,8 @@ function childrenOf(n) {
|
|
|
7035
7655
|
case "Circle":
|
|
7036
7656
|
case "Line":
|
|
7037
7657
|
case "Vertex":
|
|
7038
|
-
case "Empty":
|
|
7658
|
+
case "Empty":
|
|
7659
|
+
case "Path": return [];
|
|
7039
7660
|
case "Fuse":
|
|
7040
7661
|
case "Cut":
|
|
7041
7662
|
case "Intersect": return [n.a, n.b];
|
|
@@ -7047,6 +7668,9 @@ function childrenOf(n) {
|
|
|
7047
7668
|
case "Mirror": return [n.target];
|
|
7048
7669
|
case "Compound": return n.children;
|
|
7049
7670
|
case "Instance": return [n.source];
|
|
7671
|
+
case "Extrude":
|
|
7672
|
+
case "Revolve": return [n.profile];
|
|
7673
|
+
case "Loft": return n.sections;
|
|
7050
7674
|
}
|
|
7051
7675
|
}
|
|
7052
7676
|
function nodeCount(root) {
|
|
@@ -7062,9 +7686,11 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7062
7686
|
CSG_VERSION: () => 1,
|
|
7063
7687
|
Evaluator: () => Evaluator,
|
|
7064
7688
|
add: () => add,
|
|
7689
|
+
arcTo: () => arcTo,
|
|
7065
7690
|
asScalarExpr: () => asScalarExpr,
|
|
7066
7691
|
asVec2Expr: () => asVec2Expr,
|
|
7067
7692
|
asVec3Expr: () => asVec3Expr,
|
|
7693
|
+
bezierTo: () => bezierTo,
|
|
7068
7694
|
binOp: () => binOp,
|
|
7069
7695
|
box: () => box$2,
|
|
7070
7696
|
buildVec: () => buildVec,
|
|
@@ -7075,9 +7701,11 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7075
7701
|
cut: () => cut$1,
|
|
7076
7702
|
cutAll: () => cutAll$1,
|
|
7077
7703
|
cylinder: () => cylinder$2,
|
|
7704
|
+
ellipseArcTo: () => ellipseArcTo,
|
|
7078
7705
|
emptyFace: () => emptyFace,
|
|
7079
7706
|
emptySolid: () => emptySolid,
|
|
7080
7707
|
emptyWire: () => emptyWire,
|
|
7708
|
+
extrude: () => extrude$1,
|
|
7081
7709
|
foldExpr: () => foldExpr,
|
|
7082
7710
|
forEachNode: () => forEachNode,
|
|
7083
7711
|
fromJSON: () => fromJSON,
|
|
@@ -7086,6 +7714,8 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7086
7714
|
instance: () => instance$1,
|
|
7087
7715
|
intersect: () => intersect$1,
|
|
7088
7716
|
line: () => line$1,
|
|
7717
|
+
lineTo: () => lineTo,
|
|
7718
|
+
loft: () => loft$1,
|
|
7089
7719
|
mirror: () => mirror$1,
|
|
7090
7720
|
mul: () => mul,
|
|
7091
7721
|
nodeCount: () => nodeCount,
|
|
@@ -7093,8 +7723,10 @@ var csg_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
|
7093
7723
|
optimize: () => optimize,
|
|
7094
7724
|
outputKindOf: () => outputKindOf,
|
|
7095
7725
|
param: () => param,
|
|
7726
|
+
path: () => path,
|
|
7096
7727
|
polygon: () => polygon$1,
|
|
7097
7728
|
replaceNode: () => replaceNode,
|
|
7729
|
+
revolve: () => revolve$1,
|
|
7098
7730
|
rotate: () => rotate$1,
|
|
7099
7731
|
scale: () => scale$1,
|
|
7100
7732
|
sphere: () => sphere$2,
|