forgecad 0.9.4 → 0.9.6
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/assets/{AdminPage-jwoEgwE_.js → AdminPage-Da6hhpJx.js} +1 -1
- package/dist/assets/{BlogPage-Ck7g3ue2.js → BlogPage-Bl_sKeWb.js} +1 -1
- package/dist/assets/{DocsPage-9WaRC14b.js → DocsPage-Blz3Tp4j.js} +1 -6
- package/dist/assets/EditorApp-CuiPbtn5.js +12754 -0
- package/dist/assets/{EmbedViewer-37_PfMwv.js → EmbedViewer-BFG6-Ufm.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-CO8WL0CY.js → LandingPageProofDriven-DB9fQd5P.js} +1 -1
- package/dist/assets/{PricingPage-DADKGuOa.js → PricingPage-BMxYT_F0.js} +1 -1
- package/dist/assets/{SettingsPage-DKKI4W49.js → SettingsPage-VVQNrCAg.js} +1 -1
- package/dist/assets/{app-CwI02pTA.js → app-Dl9ymBWC.js} +355 -36
- package/dist/assets/cli/{render-Kw5hLEcL.js → render-CFtwKCCY.js} +203 -862
- package/dist/assets/{sectionPlaneMath-C8N0w8o3.js → distance-BEC2RjJi.js} +4150 -801
- package/dist/assets/{evalWorker-D6ub3kfS.js → evalWorker-CRvbzTXm.js} +2611 -528
- package/dist/assets/{manifold-CwDdMKyc.js → manifold-B9QSr-qP.js} +1 -1
- package/dist/assets/{manifold-DTvmxSDf.js → manifold-DpBXFS2K.js} +1 -1
- package/dist/assets/{manifold-lru0jwVw.js → manifold-DzZ4VRPs.js} +2 -2
- package/dist/assets/{renderSceneState-tvtNKNRi.js → renderSceneState-BuAXF2jh.js} +1 -1
- package/dist/assets/{reportWorker-DeqktDGt.js → reportWorker-BNWEnRg1.js} +2606 -525
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +0 -1
- package/dist/docs-raw/API/core/concepts.md +11 -1
- package/dist/docs-raw/CLI.md +64 -13
- package/dist/docs-raw/beta-operations.md +4 -0
- package/dist/docs-raw/deployment.md +38 -23
- package/dist/docs-raw/generated/assembly.md +8 -3
- package/dist/docs-raw/generated/concepts.md +126 -46
- package/dist/docs-raw/generated/core.md +97 -47
- package/dist/docs-raw/generated/curves.md +113 -595
- package/dist/docs-raw/generated/lib.md +40 -3
- package/dist/docs-raw/generated/output.md +6 -1
- package/dist/docs-raw/generated/sdf.md +50 -4
- package/dist/docs-raw/generated/sketch.md +9 -1
- package/dist/docs-raw/generated/viewport.md +1 -9
- package/dist/docs-raw/guides/inspection-bundles.md +40 -9
- package/dist/docs-raw/runbook.md +3 -3
- package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -0
- package/dist/docs-raw/skills/forgecad-image-replicator.md +3 -1
- package/dist/docs-raw/skills/forgecad-make-a-model.md +48 -4
- package/dist/docs-raw/skills/forgecad-render-inspect.md +3 -1
- package/dist/docs-raw/skills/forgecad-visual-spec.md +2 -0
- package/dist/docs-raw/skills/forgecad.md +2 -1
- package/dist/docs-raw/skills/index.md +0 -1
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +6 -6
- package/dist-cli/blender/render.py +43 -8
- package/dist-cli/forgecad.js +5729 -2015
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-skill/CONTEXT.md +372 -667
- package/dist-skill/SKILL-dev.md +2 -1
- package/dist-skill/SKILL.md +2 -1
- package/dist-skill/docs/API/core/concepts.md +11 -1
- package/dist-skill/docs/CLI.md +64 -13
- package/dist-skill/docs/generated/assembly.md +8 -3
- package/dist-skill/docs/generated/core.md +97 -47
- package/dist-skill/docs/generated/curves.md +113 -595
- package/dist-skill/docs/generated/lib.md +40 -3
- package/dist-skill/docs/generated/output.md +6 -1
- package/dist-skill/docs/generated/sdf.md +50 -4
- package/dist-skill/docs/generated/sketch.md +9 -1
- package/dist-skill/docs/generated/viewport.md +1 -9
- package/dist-skill/docs/guides/inspection-bundles.md +40 -9
- package/dist-skill/docs-dev/API/core/concepts.md +11 -1
- package/dist-skill/docs-dev/CLI.md +64 -13
- package/dist-skill/docs-dev/generated/assembly.md +8 -3
- package/dist-skill/docs-dev/generated/core.md +97 -47
- package/dist-skill/docs-dev/generated/curves.md +113 -595
- package/dist-skill/docs-dev/generated/lib.md +40 -3
- package/dist-skill/docs-dev/generated/output.md +6 -1
- package/dist-skill/docs-dev/generated/sdf.md +50 -4
- package/dist-skill/docs-dev/generated/sketch.md +9 -1
- package/dist-skill/docs-dev/generated/viewport.md +1 -9
- package/dist-skill/docs-dev/guides/inspection-bundles.md +40 -9
- package/dist-skill/library/README.md +0 -1
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -0
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +3 -1
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +48 -4
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
- package/dist-skill/library/forgecad-visual-spec/SKILL.md +2 -0
- package/examples/api/drive-wheel-regions.forge.js +43 -0
- package/examples/api/guided-loft-olive-oil-bottle.forge.js +135 -0
- package/examples/api/sdf-circular-array-knurling.forge.js +19 -0
- package/examples/api/sdf-pattern2d-ceramic-ripple-set.forge.js +83 -0
- package/examples/api/sdf-pattern2d-grip-tread.forge.js +72 -0
- package/examples/api/sdf-pattern2d-orbital-jewelry.forge.js +62 -0
- package/examples/api/sdf-surface-basket-weave.forge.js +67 -0
- package/examples/api/sector-gear-body.forge.js +34 -0
- package/package.json +20 -2
- package/dist/assets/EditorApp-Dja2jMmW.js +0 -12509
- package/dist/docs-raw/skills/forgecad-api-dogfood.md +0 -130
- package/dist-skill/library/forgecad-api-dogfood/SKILL.md +0 -125
|
@@ -542,6 +542,47 @@ function cloneSdfFunctionConstants(constants) {
|
|
|
542
542
|
if (!constants) return void 0;
|
|
543
543
|
return Object.fromEntries(Object.entries(constants).map(([key, value]) => [key, cloneSdfFunctionConstant(value)]));
|
|
544
544
|
}
|
|
545
|
+
function cloneSdfSurfacePatternNode(pattern) {
|
|
546
|
+
switch (pattern.kind) {
|
|
547
|
+
case "surfacePattern:constant":
|
|
548
|
+
return { kind: "surfacePattern:constant", value: pattern.value };
|
|
549
|
+
case "surfacePattern:sineWave":
|
|
550
|
+
return {
|
|
551
|
+
kind: "surfacePattern:sineWave",
|
|
552
|
+
direction: [...pattern.direction],
|
|
553
|
+
wavelength: pattern.wavelength,
|
|
554
|
+
amplitude: pattern.amplitude,
|
|
555
|
+
phase: pattern.phase,
|
|
556
|
+
bias: pattern.bias
|
|
557
|
+
};
|
|
558
|
+
case "surfacePattern:stripes":
|
|
559
|
+
return {
|
|
560
|
+
kind: "surfacePattern:stripes",
|
|
561
|
+
direction: [...pattern.direction],
|
|
562
|
+
spacing: pattern.spacing,
|
|
563
|
+
width: pattern.width,
|
|
564
|
+
depth: pattern.depth
|
|
565
|
+
};
|
|
566
|
+
case "surfacePattern:overUnderWeave":
|
|
567
|
+
return {
|
|
568
|
+
kind: "surfacePattern:overUnderWeave",
|
|
569
|
+
spacing: [...pattern.spacing],
|
|
570
|
+
threadWidth: [...pattern.threadWidth],
|
|
571
|
+
depth: pattern.depth,
|
|
572
|
+
underScale: pattern.underScale
|
|
573
|
+
};
|
|
574
|
+
case "surfacePattern:abs":
|
|
575
|
+
case "surfacePattern:negate":
|
|
576
|
+
return { kind: pattern.kind, child: cloneSdfSurfacePatternNode(pattern.child) };
|
|
577
|
+
case "surfacePattern:add":
|
|
578
|
+
case "surfacePattern:multiply":
|
|
579
|
+
case "surfacePattern:min":
|
|
580
|
+
case "surfacePattern:max":
|
|
581
|
+
return { kind: pattern.kind, children: pattern.children.map(cloneSdfSurfacePatternNode) };
|
|
582
|
+
case "surfacePattern:clamp":
|
|
583
|
+
return { kind: "surfacePattern:clamp", child: cloneSdfSurfacePatternNode(pattern.child), min: pattern.min, max: pattern.max };
|
|
584
|
+
}
|
|
585
|
+
}
|
|
545
586
|
function cloneSdfNode(node) {
|
|
546
587
|
switch (node.kind) {
|
|
547
588
|
// Primitives — plain value types
|
|
@@ -592,6 +633,8 @@ function cloneSdfNode(node) {
|
|
|
592
633
|
return { kind: "sdf:bend", child: cloneSdfNode(node.child), radius: node.radius };
|
|
593
634
|
case "sdf:repeat":
|
|
594
635
|
return { kind: "sdf:repeat", child: cloneSdfNode(node.child), spacing: [...node.spacing], count: [...node.count] };
|
|
636
|
+
case "sdf:circularArray":
|
|
637
|
+
return { kind: "sdf:circularArray", child: cloneSdfNode(node.child), count: node.count, offset: node.offset };
|
|
595
638
|
case "sdf:shell":
|
|
596
639
|
return { kind: "sdf:shell", child: cloneSdfNode(node.child), thickness: node.thickness };
|
|
597
640
|
case "sdf:displace":
|
|
@@ -605,6 +648,7 @@ function cloneSdfNode(node) {
|
|
|
605
648
|
return {
|
|
606
649
|
kind: "sdf:surfaceDisplace",
|
|
607
650
|
child: cloneSdfNode(node.child),
|
|
651
|
+
...node.pattern ? { pattern: cloneSdfSurfacePatternNode(node.pattern) } : {},
|
|
608
652
|
patternBody: node.patternBody,
|
|
609
653
|
...node.constants ? { constants: cloneSdfFunctionConstants(node.constants) } : {},
|
|
610
654
|
...node.uvMode ? { uvMode: node.uvMode } : {},
|
|
@@ -677,7 +721,7 @@ function cloneSdfNode(node) {
|
|
|
677
721
|
}
|
|
678
722
|
}
|
|
679
723
|
const SHEET_METAL_EDGES = ["top", "right", "bottom", "left"];
|
|
680
|
-
const EPS$
|
|
724
|
+
const EPS$d = 1e-9;
|
|
681
725
|
function isFinitePositive$3(value) {
|
|
682
726
|
return Number.isFinite(value) && value > 0;
|
|
683
727
|
}
|
|
@@ -718,7 +762,7 @@ function edgeDisplayName(edge) {
|
|
|
718
762
|
return `sheetMetal().flange("${edge}", ...)`;
|
|
719
763
|
}
|
|
720
764
|
function normalizeAngle(angleDeg) {
|
|
721
|
-
return Math.abs(angleDeg) <= EPS$
|
|
765
|
+
return Math.abs(angleDeg) <= EPS$d ? 0 : angleDeg;
|
|
722
766
|
}
|
|
723
767
|
function validateSheetMetalModel(model) {
|
|
724
768
|
if (!isFinitePositive$3(model.panel.width) || !isFinitePositive$3(model.panel.height)) {
|
|
@@ -730,7 +774,7 @@ function validateSheetMetalModel(model) {
|
|
|
730
774
|
if (!isFiniteNonNegative(model.bendRadius)) {
|
|
731
775
|
return "sheetMetal() requires a finite non-negative bendRadius.";
|
|
732
776
|
}
|
|
733
|
-
if (model.bendRadius <= EPS$
|
|
777
|
+
if (model.bendRadius <= EPS$d) {
|
|
734
778
|
return "sheetMetal() v1 requires a positive bendRadius so the bend region stays explicit instead of collapsing into a sharp fold.";
|
|
735
779
|
}
|
|
736
780
|
if (model.bendAllowance.kind !== "k-factor") {
|
|
@@ -792,7 +836,7 @@ function deriveSheetMetalModel(model) {
|
|
|
792
836
|
const trimEnd = flanges.has(adjacent.end) ? model.cornerRelief.size : 0;
|
|
793
837
|
const fullLength = edge === "top" || edge === "bottom" ? model.panel.width : model.panel.height;
|
|
794
838
|
const span = fullLength - trimStart - trimEnd;
|
|
795
|
-
if (!(span > EPS$
|
|
839
|
+
if (!(span > EPS$d)) {
|
|
796
840
|
throw new Error(
|
|
797
841
|
`${edgeDisplayName(edge)} loses all usable span after applying the defended rectangular corner relief size ${model.cornerRelief.size}.`
|
|
798
842
|
);
|
|
@@ -850,7 +894,7 @@ function transformPlacement(origin, u2, v, normal) {
|
|
|
850
894
|
};
|
|
851
895
|
}
|
|
852
896
|
function translatePlan(plan, x2, y2, z2) {
|
|
853
|
-
if (Math.abs(x2) <= EPS$
|
|
897
|
+
if (Math.abs(x2) <= EPS$d && Math.abs(y2) <= EPS$d && Math.abs(z2) <= EPS$d) return cloneShapeCompilePlan(plan);
|
|
854
898
|
return appendShapeCompileTransform(cloneShapeCompilePlan(plan), {
|
|
855
899
|
kind: "translate",
|
|
856
900
|
x: x2,
|
|
@@ -1294,7 +1338,7 @@ function cloneShapeWorkplanePlacement(placement) {
|
|
|
1294
1338
|
placement: cloneSketchPlacementModel(placement.placement)
|
|
1295
1339
|
};
|
|
1296
1340
|
}
|
|
1297
|
-
const EPS$
|
|
1341
|
+
const EPS$c = 1e-10;
|
|
1298
1342
|
function subVec3(a2, b) {
|
|
1299
1343
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
1300
1344
|
}
|
|
@@ -1320,7 +1364,7 @@ function projectRadial(v, axis) {
|
|
|
1320
1364
|
function signedAngleAroundAxis(from, to, axis) {
|
|
1321
1365
|
const fromLen = lengthVec3$1(from);
|
|
1322
1366
|
const toLen = lengthVec3$1(to);
|
|
1323
|
-
if (fromLen < EPS$
|
|
1367
|
+
if (fromLen < EPS$c || toLen < EPS$c) return 0;
|
|
1324
1368
|
const fn = scaleVec3(from, 1 / fromLen);
|
|
1325
1369
|
const tn = scaleVec3(to, 1 / toLen);
|
|
1326
1370
|
const sin2 = dotVec3$4(axis, crossVec3$2(fn, tn));
|
|
@@ -1341,19 +1385,19 @@ function solveRotateAroundAngle(axis, pivot, movingPoint, targetPoint, options =
|
|
|
1341
1385
|
const targetDecomp = projectRadial(target, unitAxis);
|
|
1342
1386
|
const movingRadialLen = lengthVec3$1(movingDecomp.radial);
|
|
1343
1387
|
const targetRadialLen = lengthVec3$1(targetDecomp.radial);
|
|
1344
|
-
if (movingRadialLen < EPS$
|
|
1345
|
-
if (mode === "line" && targetRadialLen >= EPS$
|
|
1388
|
+
if (movingRadialLen < EPS$c) {
|
|
1389
|
+
if (mode === "line" && targetRadialLen >= EPS$c) {
|
|
1346
1390
|
throw new Error("rotateAroundTo(...): moving point lies on the rotation axis, so line alignment is impossible");
|
|
1347
1391
|
}
|
|
1348
1392
|
return 0;
|
|
1349
1393
|
}
|
|
1350
1394
|
if (mode === "plane") {
|
|
1351
|
-
if (targetRadialLen < EPS$
|
|
1395
|
+
if (targetRadialLen < EPS$c) {
|
|
1352
1396
|
throw new Error("rotateAroundTo(...): target point lies on the rotation axis, so the target plane is undefined");
|
|
1353
1397
|
}
|
|
1354
1398
|
return signedAngleAroundAxis(movingDecomp.radial, targetDecomp.radial, unitAxis);
|
|
1355
1399
|
}
|
|
1356
|
-
if (targetRadialLen < EPS$
|
|
1400
|
+
if (targetRadialLen < EPS$c) {
|
|
1357
1401
|
throw new Error("rotateAroundTo(...): target line lies on the rotation axis, but the moving point does not");
|
|
1358
1402
|
}
|
|
1359
1403
|
const axialTol = 1e-8 * Math.max(1, Math.abs(movingDecomp.axial), Math.abs(targetDecomp.axial));
|
|
@@ -1393,7 +1437,7 @@ function multiplyMat4(a2, b) {
|
|
|
1393
1437
|
}
|
|
1394
1438
|
function normalizeVec3$5(v) {
|
|
1395
1439
|
const len2 = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
1396
|
-
if (len2 < EPS$
|
|
1440
|
+
if (len2 < EPS$c) throw new Error("Axis must be non-zero");
|
|
1397
1441
|
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
1398
1442
|
}
|
|
1399
1443
|
function transformPoint$1(m2, p2, w2) {
|
|
@@ -1423,7 +1467,7 @@ function invertMat4(m2) {
|
|
|
1423
1467
|
const b10 = a21 * a33 - a23 * a31;
|
|
1424
1468
|
const b11 = a22 * a33 - a23 * a32;
|
|
1425
1469
|
const det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
|
|
1426
|
-
if (Math.abs(det) < EPS$
|
|
1470
|
+
if (Math.abs(det) < EPS$c) throw new Error("Transform matrix is not invertible");
|
|
1427
1471
|
const invDet = 1 / det;
|
|
1428
1472
|
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;
|
|
1429
1473
|
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * invDet;
|
|
@@ -3180,14 +3224,14 @@ function sweepPathToPolylineAdaptive(path2, baseSamples = 48) {
|
|
|
3180
3224
|
pts.push(evalPathAt(path2, 1));
|
|
3181
3225
|
return pts;
|
|
3182
3226
|
}
|
|
3183
|
-
const EPS$
|
|
3227
|
+
const EPS$b = 1e-8;
|
|
3184
3228
|
const SUPPORTED_VERTICAL_EDGE_NAMES = ["vert-bl", "vert-br", "vert-tr", "vert-tl"];
|
|
3185
3229
|
function midpoint$4(start, end) {
|
|
3186
3230
|
return [(start[0] + end[0]) * 0.5, (start[1] + end[1]) * 0.5, (start[2] + end[2]) * 0.5];
|
|
3187
3231
|
}
|
|
3188
3232
|
function normalize$7(v) {
|
|
3189
3233
|
const len2 = Math.hypot(v[0], v[1], v[2]);
|
|
3190
|
-
if (len2 <= EPS$
|
|
3234
|
+
if (len2 <= EPS$b) throw new Error("Edge feature selection requires a non-zero direction vector");
|
|
3191
3235
|
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
3192
3236
|
}
|
|
3193
3237
|
function subtract(a2, b) {
|
|
@@ -3269,7 +3313,7 @@ function rigidTransformForEdgeStep(step) {
|
|
|
3269
3313
|
case "mirror": {
|
|
3270
3314
|
const [nx0, ny0, nz0] = [step.normalX, step.normalY, step.normalZ];
|
|
3271
3315
|
const len2 = Math.hypot(nx0, ny0, nz0);
|
|
3272
|
-
if (len2 <= EPS$
|
|
3316
|
+
if (len2 <= EPS$b) return Transform.identity();
|
|
3273
3317
|
const nx = nx0 / len2;
|
|
3274
3318
|
const ny = ny0 / len2;
|
|
3275
3319
|
const nz = nz0 / len2;
|
|
@@ -3580,7 +3624,7 @@ function isRectangleProfile(points) {
|
|
|
3580
3624
|
return [next[0] - point2[0], next[1] - point2[1]];
|
|
3581
3625
|
});
|
|
3582
3626
|
const lengths2 = vectors.map(([x2, y2]) => Math.hypot(x2, y2));
|
|
3583
|
-
if (lengths2.some((length4) => length4 <= EPS$
|
|
3627
|
+
if (lengths2.some((length4) => length4 <= EPS$b)) return false;
|
|
3584
3628
|
const dot01 = vectors[0][0] * vectors[1][0] + vectors[0][1] * vectors[1][1];
|
|
3585
3629
|
const dot12 = vectors[1][0] * vectors[2][0] + vectors[1][1] * vectors[2][1];
|
|
3586
3630
|
const dot23 = vectors[2][0] * vectors[3][0] + vectors[2][1] * vectors[3][1];
|
|
@@ -4646,7 +4690,7 @@ for (var i = 0; i < 32; ++i)
|
|
|
4646
4690
|
fdt[i] = 5;
|
|
4647
4691
|
var flm = /* @__PURE__ */ hMap(flt, 9, 0), flrm = /* @__PURE__ */ hMap(flt, 9, 1);
|
|
4648
4692
|
var fdm = /* @__PURE__ */ hMap(fdt, 5, 0), fdrm = /* @__PURE__ */ hMap(fdt, 5, 1);
|
|
4649
|
-
var max$
|
|
4693
|
+
var max$2 = function(a2) {
|
|
4650
4694
|
var m2 = a2[0];
|
|
4651
4695
|
for (var i = 1; i < a2.length; ++i) {
|
|
4652
4696
|
if (a2[i] > m2)
|
|
@@ -4746,7 +4790,7 @@ var inflt = function(dat, st, buf, dict) {
|
|
|
4746
4790
|
clt[clim[i]] = bits(dat, pos + i * 3, 7);
|
|
4747
4791
|
}
|
|
4748
4792
|
pos += hcLen * 3;
|
|
4749
|
-
var clb = max$
|
|
4793
|
+
var clb = max$2(clt), clbmsk = (1 << clb) - 1;
|
|
4750
4794
|
var clm = hMap(clt, clb, 1);
|
|
4751
4795
|
for (var i = 0; i < tl; ) {
|
|
4752
4796
|
var r = clm[bits(dat, pos, clbmsk)];
|
|
@@ -4767,8 +4811,8 @@ var inflt = function(dat, st, buf, dict) {
|
|
|
4767
4811
|
}
|
|
4768
4812
|
}
|
|
4769
4813
|
var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
4770
|
-
lbt = max$
|
|
4771
|
-
dbt = max$
|
|
4814
|
+
lbt = max$2(lt);
|
|
4815
|
+
dbt = max$2(dt);
|
|
4772
4816
|
lm = hMap(lt, lbt, 1);
|
|
4773
4817
|
dm = hMap(dt, dbt, 1);
|
|
4774
4818
|
} else
|
|
@@ -5621,8 +5665,8 @@ function parse3mf(data) {
|
|
|
5621
5665
|
while ((tMatch = trianglePattern.exec(meshXml)) !== null) {
|
|
5622
5666
|
const v1 = parseInt(tMatch[1], 10) + vertexOffset;
|
|
5623
5667
|
const v2 = parseInt(tMatch[2], 10) + vertexOffset;
|
|
5624
|
-
const
|
|
5625
|
-
allTriIndices.push(v1, v2,
|
|
5668
|
+
const v32 = parseInt(tMatch[3], 10) + vertexOffset;
|
|
5669
|
+
allTriIndices.push(v1, v2, v32);
|
|
5626
5670
|
}
|
|
5627
5671
|
for (let i = 0; i < meshVerts.length; i++) {
|
|
5628
5672
|
allPositions.push(meshVerts[i]);
|
|
@@ -5647,13 +5691,13 @@ function parseMeshFile(data, format) {
|
|
|
5647
5691
|
return parse3mf(data);
|
|
5648
5692
|
}
|
|
5649
5693
|
}
|
|
5650
|
-
const EPS$
|
|
5694
|
+
const EPS$a = 1e-8;
|
|
5651
5695
|
function length$3(v) {
|
|
5652
5696
|
return Math.hypot(v[0], v[1], v[2]);
|
|
5653
5697
|
}
|
|
5654
5698
|
function normalize$6(v) {
|
|
5655
5699
|
const len2 = length$3(v);
|
|
5656
|
-
if (len2 < EPS$
|
|
5700
|
+
if (len2 < EPS$a) throw new Error("Plane normal must be non-zero");
|
|
5657
5701
|
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
5658
5702
|
}
|
|
5659
5703
|
function resolvePlaneOriginNormal(plane) {
|
|
@@ -5675,12 +5719,12 @@ function resolvePlaneOriginNormal(plane) {
|
|
|
5675
5719
|
function rotationToPlaneSpace(normal) {
|
|
5676
5720
|
const n = normalize$6(normal);
|
|
5677
5721
|
const dot2 = n[2];
|
|
5678
|
-
if (dot2 > 1 - EPS$
|
|
5722
|
+
if (dot2 > 1 - EPS$a) {
|
|
5679
5723
|
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
5680
5724
|
}
|
|
5681
5725
|
let axis;
|
|
5682
5726
|
let angle;
|
|
5683
|
-
if (dot2 < -1 + EPS$
|
|
5727
|
+
if (dot2 < -1 + EPS$a) {
|
|
5684
5728
|
axis = [1, 0, 0];
|
|
5685
5729
|
angle = Math.PI;
|
|
5686
5730
|
} else {
|
|
@@ -6005,6 +6049,287 @@ function lowerShellShapeCompilePlanToConcretePlan(plan) {
|
|
|
6005
6049
|
}
|
|
6006
6050
|
return lowerBaseShellPlanToConcretePlan(plan.base, plan.thickness, normalizeShellOpenFaces(plan.openFaces));
|
|
6007
6051
|
}
|
|
6052
|
+
const DEFAULT_MAX_GRID_POINTS = 8e6;
|
|
6053
|
+
const DEFAULT_MIN_EDGE_LENGTH = 0.15;
|
|
6054
|
+
function resolveSdfMeshingSettings(tree, bounds, options = {}) {
|
|
6055
|
+
const quality = options.quality ?? "preview";
|
|
6056
|
+
const minEdgeLength = positiveOrDefault(options.minEdgeLength, DEFAULT_MIN_EDGE_LENGTH);
|
|
6057
|
+
const maxGridPoints = positiveOrDefault(options.maxGridPoints, DEFAULT_MAX_GRID_POINTS);
|
|
6058
|
+
const tolerance = options.tolerance !== void 0 ? requirePositiveFinite$3(options.tolerance, "SDF tolerance") : void 0;
|
|
6059
|
+
const minFeatureSize = options.minFeatureSize !== void 0 ? requirePositiveFinite$3(options.minFeatureSize, "SDF minFeatureSize") : void 0;
|
|
6060
|
+
const maxTriangles = options.maxTriangles !== void 0 ? Math.floor(requirePositiveFinite$3(options.maxTriangles, "SDF maxTriangles")) : void 0;
|
|
6061
|
+
const analysis = analyzeSdfTree(tree);
|
|
6062
|
+
const warnings = [];
|
|
6063
|
+
let edgeLength2;
|
|
6064
|
+
if (options.edgeLength !== void 0) {
|
|
6065
|
+
edgeLength2 = requirePositiveFinite$3(options.edgeLength, "SDF edgeLength");
|
|
6066
|
+
if (edgeLength2 < minEdgeLength) {
|
|
6067
|
+
warnings.push(`edgeLength ${formatMm(edgeLength2)} was clamped to minimum ${formatMm(minEdgeLength)}.`);
|
|
6068
|
+
edgeLength2 = minEdgeLength;
|
|
6069
|
+
}
|
|
6070
|
+
} else {
|
|
6071
|
+
edgeLength2 = resolveDefaultEdgeLength(bounds, quality, minEdgeLength, analysis, options);
|
|
6072
|
+
}
|
|
6073
|
+
if (analysis.minWallThickness < Infinity && analysis.minWallThickness < edgeLength2 * 2) {
|
|
6074
|
+
analysis.riskFlags.add("thin-shell");
|
|
6075
|
+
warnings.push(
|
|
6076
|
+
`shell/wall thickness ${formatMm(analysis.minWallThickness)} is below 2 x edgeLength ${formatMm(edgeLength2)}; thin features may be under-sampled.`
|
|
6077
|
+
);
|
|
6078
|
+
}
|
|
6079
|
+
if (!options.bounds && analysis.hasInfiniteRepeat) {
|
|
6080
|
+
warnings.push("infinite repeat bounds are heuristic; pass .toShape({ bounds }) for predictable clipping.");
|
|
6081
|
+
}
|
|
6082
|
+
if (!options.bounds && analysis.riskFlags.has("noise")) {
|
|
6083
|
+
warnings.push("noise field bounds are heuristic; pass .toShape({ bounds }) for predictable clipping.");
|
|
6084
|
+
}
|
|
6085
|
+
if (!options.bounds && (analysis.riskFlags.has("tpms") || analysis.riskFlags.has("voronoi"))) {
|
|
6086
|
+
warnings.push("TPMS/Voronoi bounds are heuristic unless clipped or passed explicitly.");
|
|
6087
|
+
}
|
|
6088
|
+
if (analysis.hasLegacyTpmsThreshold) {
|
|
6089
|
+
warnings.push("TPMS thickness is using legacy field-threshold units; use wallThickness for approximate millimeters.");
|
|
6090
|
+
}
|
|
6091
|
+
return {
|
|
6092
|
+
quality,
|
|
6093
|
+
edgeLength: edgeLength2,
|
|
6094
|
+
tolerance,
|
|
6095
|
+
minFeatureSize,
|
|
6096
|
+
minEdgeLength,
|
|
6097
|
+
simplify: resolveSimplificationMode(options.simplify, quality, analysis.riskFlags),
|
|
6098
|
+
maxTriangles,
|
|
6099
|
+
maxGridPoints,
|
|
6100
|
+
diagnostics: options.diagnostics === true,
|
|
6101
|
+
treeRiskFlags: [...analysis.riskFlags].sort(),
|
|
6102
|
+
warnings
|
|
6103
|
+
};
|
|
6104
|
+
}
|
|
6105
|
+
function withScaledSdfEdgeLength(settings, edgeLength2) {
|
|
6106
|
+
return { ...settings, edgeLength: Math.max(settings.minEdgeLength, edgeLength2) };
|
|
6107
|
+
}
|
|
6108
|
+
function createSdfMeshingDiagnostics(settings, bounds, paddedBounds) {
|
|
6109
|
+
const grid = estimateSdfGridDimensions(paddedBounds, settings.edgeLength);
|
|
6110
|
+
const estimatedSamples = grid[0] * grid[1] * grid[2];
|
|
6111
|
+
return {
|
|
6112
|
+
bounds: cloneBounds$2(bounds),
|
|
6113
|
+
paddedBounds: cloneBounds$2(paddedBounds),
|
|
6114
|
+
edgeLength: settings.edgeLength,
|
|
6115
|
+
grid,
|
|
6116
|
+
estimatedSamples,
|
|
6117
|
+
estimatedMemoryBytes: estimatedSamples * 8,
|
|
6118
|
+
treeRiskFlags: [...settings.treeRiskFlags],
|
|
6119
|
+
simplification: settings.simplify,
|
|
6120
|
+
capMode: "box",
|
|
6121
|
+
capInset: settings.edgeLength,
|
|
6122
|
+
warnings: [...settings.warnings]
|
|
6123
|
+
};
|
|
6124
|
+
}
|
|
6125
|
+
function assertSdfMeshingBudget(diagnostics, maxGridPoints) {
|
|
6126
|
+
if (diagnostics.estimatedSamples <= maxGridPoints) return;
|
|
6127
|
+
const suggestedEdge = suggestEdgeLengthForSampleBudget(diagnostics.paddedBounds, maxGridPoints);
|
|
6128
|
+
throw new Error(
|
|
6129
|
+
`SDF meshing would sample ${formatCount(diagnostics.estimatedSamples)} grid points (~${formatBytes(
|
|
6130
|
+
diagnostics.estimatedMemoryBytes
|
|
6131
|
+
)}). Reduce bounds or use edgeLength >= ${formatMm(suggestedEdge)}.`
|
|
6132
|
+
);
|
|
6133
|
+
}
|
|
6134
|
+
function estimateSdfGridDimensions(bounds, edgeLength2) {
|
|
6135
|
+
const dx = bounds.max[0] - bounds.min[0];
|
|
6136
|
+
const dy = bounds.max[1] - bounds.min[1];
|
|
6137
|
+
const dz = bounds.max[2] - bounds.min[2];
|
|
6138
|
+
return [
|
|
6139
|
+
Math.max(2, Math.ceil(dx / edgeLength2) + 1),
|
|
6140
|
+
Math.max(2, Math.ceil(dy / edgeLength2) + 1),
|
|
6141
|
+
Math.max(2, Math.ceil(dz / edgeLength2) + 1)
|
|
6142
|
+
];
|
|
6143
|
+
}
|
|
6144
|
+
function logSdfMeshingDiagnostics(prefix, diagnostics) {
|
|
6145
|
+
const warnings = diagnostics.warnings.length > 0 ? `, warnings=${diagnostics.warnings.join(" | ")}` : "";
|
|
6146
|
+
const evaluator = diagnostics.evaluator ? `, evaluator=${diagnostics.evaluator}${diagnostics.evaluatorUnsupportedReason ? ` (${diagnostics.evaluatorUnsupportedReason})` : ""}` : "";
|
|
6147
|
+
console.info(
|
|
6148
|
+
`${prefix}: bounds=${formatBounds(diagnostics.bounds)}, paddedBounds=${formatBounds(diagnostics.paddedBounds)}, edgeLength=${formatMm(diagnostics.edgeLength)}, grid=${diagnostics.grid.join("x")}, estimatedSamples=${formatCount(diagnostics.estimatedSamples)}, treeRisk=${diagnostics.treeRiskFlags.join("+") || "none"}, simplify=${diagnostics.simplification}${evaluator}, capMode=${diagnostics.capMode}, capInset=${formatMm(diagnostics.capInset)}${warnings}`
|
|
6149
|
+
);
|
|
6150
|
+
}
|
|
6151
|
+
function resolveDefaultEdgeLength(bounds, quality, minEdgeLength, analysis, options) {
|
|
6152
|
+
const dx = bounds.max[0] - bounds.min[0];
|
|
6153
|
+
const dy = bounds.max[1] - bounds.min[1];
|
|
6154
|
+
const dz = bounds.max[2] - bounds.min[2];
|
|
6155
|
+
const maxDim = Math.max(dx, dy, dz, minEdgeLength);
|
|
6156
|
+
const divisor = quality === "draft" ? 60 : quality === "export" ? 160 : 100;
|
|
6157
|
+
const candidates = [maxDim / divisor];
|
|
6158
|
+
if (options.tolerance !== void 0) candidates.push(requirePositiveFinite$3(options.tolerance, "SDF tolerance") * 2);
|
|
6159
|
+
if (options.minFeatureSize !== void 0) candidates.push(requirePositiveFinite$3(options.minFeatureSize, "SDF minFeatureSize") / 2.5);
|
|
6160
|
+
if (analysis.minMetricTpmsThickness < Infinity) candidates.push(analysis.minMetricTpmsThickness / 2);
|
|
6161
|
+
if (analysis.minTpmsCellSize < Infinity) candidates.push(analysis.minTpmsCellSize / 10);
|
|
6162
|
+
if (analysis.minRepeatSpacing < Infinity) candidates.push(analysis.minRepeatSpacing / 8);
|
|
6163
|
+
if (analysis.minWallThickness < Infinity) candidates.push(analysis.minWallThickness / 2.5);
|
|
6164
|
+
return Math.max(minEdgeLength, Math.min(...candidates.filter((v) => Number.isFinite(v) && v > 0)));
|
|
6165
|
+
}
|
|
6166
|
+
function resolveSimplificationMode(simplify, quality, riskFlags) {
|
|
6167
|
+
if (simplify === false) return "off";
|
|
6168
|
+
if (simplify === true || simplify === "safe") return "safe";
|
|
6169
|
+
if (quality === "export" && riskFlags.size > 0) return "off";
|
|
6170
|
+
return "safe";
|
|
6171
|
+
}
|
|
6172
|
+
function analyzeSdfTree(tree) {
|
|
6173
|
+
const analysis = {
|
|
6174
|
+
riskFlags: /* @__PURE__ */ new Set(),
|
|
6175
|
+
minTpmsCellSize: Infinity,
|
|
6176
|
+
minMetricTpmsThickness: Infinity,
|
|
6177
|
+
minRepeatSpacing: Infinity,
|
|
6178
|
+
minWallThickness: Infinity,
|
|
6179
|
+
hasInfiniteRepeat: false,
|
|
6180
|
+
hasLegacyTpmsThreshold: false
|
|
6181
|
+
};
|
|
6182
|
+
visitSdfNode(tree, analysis);
|
|
6183
|
+
return analysis;
|
|
6184
|
+
}
|
|
6185
|
+
function minPositive(...values) {
|
|
6186
|
+
let result = Infinity;
|
|
6187
|
+
for (const value of values) {
|
|
6188
|
+
if (value !== null && value !== void 0 && Number.isFinite(value) && value > 0) {
|
|
6189
|
+
result = Math.min(result, value);
|
|
6190
|
+
}
|
|
6191
|
+
}
|
|
6192
|
+
return result === Infinity ? null : result;
|
|
6193
|
+
}
|
|
6194
|
+
function estimateSurfacePatternSpacing(pattern) {
|
|
6195
|
+
switch (pattern.kind) {
|
|
6196
|
+
case "surfacePattern:constant":
|
|
6197
|
+
return null;
|
|
6198
|
+
case "surfacePattern:sineWave":
|
|
6199
|
+
return pattern.wavelength;
|
|
6200
|
+
case "surfacePattern:stripes":
|
|
6201
|
+
return Math.min(pattern.spacing, pattern.width);
|
|
6202
|
+
case "surfacePattern:overUnderWeave":
|
|
6203
|
+
return Math.min(...pattern.spacing, ...pattern.threadWidth);
|
|
6204
|
+
case "surfacePattern:abs":
|
|
6205
|
+
case "surfacePattern:negate":
|
|
6206
|
+
return estimateSurfacePatternSpacing(pattern.child);
|
|
6207
|
+
case "surfacePattern:add":
|
|
6208
|
+
case "surfacePattern:multiply":
|
|
6209
|
+
case "surfacePattern:min":
|
|
6210
|
+
case "surfacePattern:max":
|
|
6211
|
+
return minPositive(...pattern.children.map(estimateSurfacePatternSpacing));
|
|
6212
|
+
case "surfacePattern:clamp":
|
|
6213
|
+
return estimateSurfacePatternSpacing(pattern.child);
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
function visitSdfNode(node, analysis) {
|
|
6217
|
+
switch (node.kind) {
|
|
6218
|
+
case "sdf:union":
|
|
6219
|
+
case "sdf:difference":
|
|
6220
|
+
case "sdf:intersection":
|
|
6221
|
+
case "sdf:smoothUnion":
|
|
6222
|
+
case "sdf:smoothDifference":
|
|
6223
|
+
case "sdf:smoothIntersection":
|
|
6224
|
+
for (const child of node.children) visitSdfNode(child, analysis);
|
|
6225
|
+
break;
|
|
6226
|
+
case "sdf:morph":
|
|
6227
|
+
case "sdf:spatialBlend":
|
|
6228
|
+
visitSdfNode(node.a, analysis);
|
|
6229
|
+
visitSdfNode(node.b, analysis);
|
|
6230
|
+
break;
|
|
6231
|
+
case "sdf:translate":
|
|
6232
|
+
case "sdf:rotate":
|
|
6233
|
+
case "sdf:scale":
|
|
6234
|
+
case "sdf:twist":
|
|
6235
|
+
case "sdf:bend":
|
|
6236
|
+
case "sdf:onion":
|
|
6237
|
+
visitSdfNode(node.child, analysis);
|
|
6238
|
+
break;
|
|
6239
|
+
case "sdf:repeat":
|
|
6240
|
+
analysis.riskFlags.add("repeat");
|
|
6241
|
+
for (let i = 0; i < 3; i++) {
|
|
6242
|
+
const spacing = node.spacing[i];
|
|
6243
|
+
if (spacing > 0) {
|
|
6244
|
+
analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, spacing);
|
|
6245
|
+
if (node.count[i] <= 0) analysis.hasInfiniteRepeat = true;
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
visitSdfNode(node.child, analysis);
|
|
6249
|
+
break;
|
|
6250
|
+
case "sdf:circularArray": {
|
|
6251
|
+
analysis.riskFlags.add("repeat");
|
|
6252
|
+
if (node.offset > 0) {
|
|
6253
|
+
analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, 2 * Math.PI * node.offset / node.count);
|
|
6254
|
+
}
|
|
6255
|
+
visitSdfNode(node.child, analysis);
|
|
6256
|
+
break;
|
|
6257
|
+
}
|
|
6258
|
+
case "sdf:shell":
|
|
6259
|
+
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.thickness);
|
|
6260
|
+
visitSdfNode(node.child, analysis);
|
|
6261
|
+
break;
|
|
6262
|
+
case "sdf:displace":
|
|
6263
|
+
case "sdf:surfaceDisplace":
|
|
6264
|
+
analysis.riskFlags.add("displacement");
|
|
6265
|
+
if (node.kind === "sdf:surfaceDisplace" && node.pattern) {
|
|
6266
|
+
const spacing = estimateSurfacePatternSpacing(node.pattern);
|
|
6267
|
+
if (spacing !== null) analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, spacing);
|
|
6268
|
+
}
|
|
6269
|
+
visitSdfNode(node.child, analysis);
|
|
6270
|
+
break;
|
|
6271
|
+
case "sdf:gyroid":
|
|
6272
|
+
case "sdf:schwarzP":
|
|
6273
|
+
case "sdf:diamond":
|
|
6274
|
+
case "sdf:lidinoid":
|
|
6275
|
+
analysis.riskFlags.add("tpms");
|
|
6276
|
+
analysis.minTpmsCellSize = Math.min(analysis.minTpmsCellSize, node.cellSize);
|
|
6277
|
+
if (node.thicknessMode === "metric-approx") {
|
|
6278
|
+
analysis.minMetricTpmsThickness = Math.min(analysis.minMetricTpmsThickness, node.thickness);
|
|
6279
|
+
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.thickness);
|
|
6280
|
+
} else {
|
|
6281
|
+
analysis.hasLegacyTpmsThreshold = true;
|
|
6282
|
+
}
|
|
6283
|
+
break;
|
|
6284
|
+
case "sdf:noise":
|
|
6285
|
+
analysis.riskFlags.add("noise");
|
|
6286
|
+
break;
|
|
6287
|
+
case "sdf:voronoi":
|
|
6288
|
+
analysis.riskFlags.add("voronoi");
|
|
6289
|
+
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.wallThickness);
|
|
6290
|
+
if (node.surfaceChild) visitSdfNode(node.surfaceChild, analysis);
|
|
6291
|
+
break;
|
|
6292
|
+
case "sdf:custom":
|
|
6293
|
+
analysis.riskFlags.add("custom");
|
|
6294
|
+
break;
|
|
6295
|
+
}
|
|
6296
|
+
}
|
|
6297
|
+
function positiveOrDefault(value, fallback) {
|
|
6298
|
+
if (value === void 0) return fallback;
|
|
6299
|
+
return requirePositiveFinite$3(value, "SDF meshing option");
|
|
6300
|
+
}
|
|
6301
|
+
function requirePositiveFinite$3(value, name) {
|
|
6302
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
6303
|
+
throw new Error(`${name} must be a positive finite number.`);
|
|
6304
|
+
}
|
|
6305
|
+
return value;
|
|
6306
|
+
}
|
|
6307
|
+
function cloneBounds$2(bounds) {
|
|
6308
|
+
return { min: [...bounds.min], max: [...bounds.max] };
|
|
6309
|
+
}
|
|
6310
|
+
function suggestEdgeLengthForSampleBudget(bounds, maxGridPoints) {
|
|
6311
|
+
const dx = bounds.max[0] - bounds.min[0];
|
|
6312
|
+
const dy = bounds.max[1] - bounds.min[1];
|
|
6313
|
+
const dz = bounds.max[2] - bounds.min[2];
|
|
6314
|
+
const volume = Math.max(dx * dy * dz, 1);
|
|
6315
|
+
return Math.cbrt(volume / Math.max(maxGridPoints, 8));
|
|
6316
|
+
}
|
|
6317
|
+
function formatBounds(bounds) {
|
|
6318
|
+
return `[${bounds.min.map(formatNumber).join(",")}]-[${bounds.max.map(formatNumber).join(",")}]`;
|
|
6319
|
+
}
|
|
6320
|
+
function formatMm(value) {
|
|
6321
|
+
return `${formatNumber(value)}mm`;
|
|
6322
|
+
}
|
|
6323
|
+
function formatNumber(value) {
|
|
6324
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
6325
|
+
}
|
|
6326
|
+
function formatCount(value) {
|
|
6327
|
+
return Math.round(value).toLocaleString("en-US");
|
|
6328
|
+
}
|
|
6329
|
+
function formatBytes(bytes) {
|
|
6330
|
+
if (bytes < 1024 * 1024) return `${Math.ceil(bytes / 1024)} KB`;
|
|
6331
|
+
return `${Math.ceil(bytes / (1024 * 1024))} MB`;
|
|
6332
|
+
}
|
|
6008
6333
|
const grad3 = new Float64Array([
|
|
6009
6334
|
1,
|
|
6010
6335
|
1,
|
|
@@ -6465,8 +6790,8 @@ function triplanarWeights(nx, ny, nz, sharpness) {
|
|
|
6465
6790
|
const inv = 1 / sum2;
|
|
6466
6791
|
return { wx: wx * inv, wy: wy * inv, wz: wz * inv };
|
|
6467
6792
|
}
|
|
6468
|
-
const { atan2, acos, cos: cos$
|
|
6469
|
-
const DEG$
|
|
6793
|
+
const { atan2, acos, cos: cos$3, sin: sin$3, sqrt: sqrt$3, PI: PI$3 } = Math;
|
|
6794
|
+
const DEG$3 = PI$3 / 180;
|
|
6470
6795
|
const IDENTITY = (p2) => p2;
|
|
6471
6796
|
function analyzeUV(node, override) {
|
|
6472
6797
|
if (override) {
|
|
@@ -6493,10 +6818,10 @@ function analyzeNodeUV(node, toLocal) {
|
|
|
6493
6818
|
return analyzeNodeUV(node.child, next);
|
|
6494
6819
|
}
|
|
6495
6820
|
case "sdf:rotate": {
|
|
6496
|
-
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$
|
|
6497
|
-
const cx = cos$
|
|
6498
|
-
const cy = cos$
|
|
6499
|
-
const cz = cos$
|
|
6821
|
+
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$3);
|
|
6822
|
+
const cx = cos$3(rx), sx = sin$3(rx);
|
|
6823
|
+
const cy = cos$3(ry), sy = sin$3(ry);
|
|
6824
|
+
const cz = cos$3(rz), sz = sin$3(rz);
|
|
6500
6825
|
const prev = toLocal;
|
|
6501
6826
|
const next = (p2) => {
|
|
6502
6827
|
const pp = prev(p2);
|
|
@@ -6553,7 +6878,7 @@ function compileUVFunction(analysis) {
|
|
|
6553
6878
|
return (p2) => {
|
|
6554
6879
|
const lp = toLocal(p2);
|
|
6555
6880
|
const u2 = atan2(lp[1], lp[0]) * R;
|
|
6556
|
-
const len2 = sqrt$
|
|
6881
|
+
const len2 = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1] + lp[2] * lp[2]);
|
|
6557
6882
|
const v = acos(clampUnit(lp[2] / (len2 || 1))) * R;
|
|
6558
6883
|
return [u2, v];
|
|
6559
6884
|
};
|
|
@@ -6573,23 +6898,23 @@ function compileUVFunction(analysis) {
|
|
|
6573
6898
|
return (p2) => {
|
|
6574
6899
|
const lp = toLocal(p2);
|
|
6575
6900
|
const u2 = atan2(lp[1], lp[0]) * R;
|
|
6576
|
-
const xyDist = sqrt$
|
|
6901
|
+
const xyDist = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1]) - R;
|
|
6577
6902
|
const v = atan2(lp[2], xyDist) * r;
|
|
6578
6903
|
return [u2, v];
|
|
6579
6904
|
};
|
|
6580
6905
|
}
|
|
6581
6906
|
}
|
|
6582
6907
|
}
|
|
6583
|
-
const { abs: abs$1, cos: cos$
|
|
6584
|
-
const TAU = 2 * PI$
|
|
6585
|
-
const GRAD_EPS = 1e-9;
|
|
6908
|
+
const { abs: abs$1, cos: cos$2, sin: sin$2, sqrt: sqrt$2, PI: PI$2 } = Math;
|
|
6909
|
+
const TAU$1 = 2 * PI$2;
|
|
6910
|
+
const GRAD_EPS$1 = 1e-9;
|
|
6586
6911
|
function gyroidValueAndGradient(x2, y2, z2, cellSize) {
|
|
6587
|
-
const s = TAU / cellSize;
|
|
6912
|
+
const s = TAU$1 / cellSize;
|
|
6588
6913
|
const xs = x2 * s;
|
|
6589
6914
|
const ys = y2 * s;
|
|
6590
6915
|
const zs = z2 * s;
|
|
6591
|
-
const sx = sin$
|
|
6592
|
-
const cx = cos$
|
|
6916
|
+
const sx = sin$2(xs), sy = sin$2(ys), sz = sin$2(zs);
|
|
6917
|
+
const cx = cos$2(xs), cy = cos$2(ys), cz = cos$2(zs);
|
|
6593
6918
|
return {
|
|
6594
6919
|
value: sx * cy + sy * cz + sz * cx,
|
|
6595
6920
|
gx: s * (cx * cy - sz * sx),
|
|
@@ -6598,24 +6923,24 @@ function gyroidValueAndGradient(x2, y2, z2, cellSize) {
|
|
|
6598
6923
|
};
|
|
6599
6924
|
}
|
|
6600
6925
|
function schwarzPValueAndGradient(x2, y2, z2, cellSize) {
|
|
6601
|
-
const s = TAU / cellSize;
|
|
6926
|
+
const s = TAU$1 / cellSize;
|
|
6602
6927
|
const xs = x2 * s;
|
|
6603
6928
|
const ys = y2 * s;
|
|
6604
6929
|
const zs = z2 * s;
|
|
6605
6930
|
return {
|
|
6606
|
-
value: cos$
|
|
6607
|
-
gx: -s * sin$
|
|
6608
|
-
gy: -s * sin$
|
|
6609
|
-
gz: -s * sin$
|
|
6931
|
+
value: cos$2(xs) + cos$2(ys) + cos$2(zs),
|
|
6932
|
+
gx: -s * sin$2(xs),
|
|
6933
|
+
gy: -s * sin$2(ys),
|
|
6934
|
+
gz: -s * sin$2(zs)
|
|
6610
6935
|
};
|
|
6611
6936
|
}
|
|
6612
6937
|
function diamondValueAndGradient(x2, y2, z2, cellSize) {
|
|
6613
|
-
const s = TAU / cellSize;
|
|
6938
|
+
const s = TAU$1 / cellSize;
|
|
6614
6939
|
const xs = x2 * s;
|
|
6615
6940
|
const ys = y2 * s;
|
|
6616
6941
|
const zs = z2 * s;
|
|
6617
|
-
const sx = sin$
|
|
6618
|
-
const cx = cos$
|
|
6942
|
+
const sx = sin$2(xs), sy = sin$2(ys), sz = sin$2(zs);
|
|
6943
|
+
const cx = cos$2(xs), cy = cos$2(ys), cz = cos$2(zs);
|
|
6619
6944
|
return {
|
|
6620
6945
|
value: sx * sy * sz + sx * cy * cz + cx * sy * cz + cx * cy * sz,
|
|
6621
6946
|
gx: s * (cx * sy * sz + cx * cy * cz - sx * sy * cz - sx * cy * sz),
|
|
@@ -6624,12 +6949,12 @@ function diamondValueAndGradient(x2, y2, z2, cellSize) {
|
|
|
6624
6949
|
};
|
|
6625
6950
|
}
|
|
6626
6951
|
function lidinoidValueAndGradient(x2, y2, z2, cellSize) {
|
|
6627
|
-
const s = TAU / cellSize;
|
|
6952
|
+
const s = TAU$1 / cellSize;
|
|
6628
6953
|
const sx2 = x2 * s, sy2 = y2 * s, sz2 = z2 * s;
|
|
6629
|
-
const sx = sin$
|
|
6630
|
-
const cx = cos$
|
|
6631
|
-
const s2x = sin$
|
|
6632
|
-
const c2x = cos$
|
|
6954
|
+
const sx = sin$2(sx2), sy = sin$2(sy2), sz = sin$2(sz2);
|
|
6955
|
+
const cx = cos$2(sx2), cy = cos$2(sy2), cz = cos$2(sz2);
|
|
6956
|
+
const s2x = sin$2(2 * sx2), s2y = sin$2(2 * sy2), s2z = sin$2(2 * sz2);
|
|
6957
|
+
const c2x = cos$2(2 * sx2), c2y = cos$2(2 * sy2), c2z = cos$2(2 * sz2);
|
|
6633
6958
|
const val = s2x * cy * sz + s2y * cz * sx + s2z * cx * sy - c2x * c2y - c2y * c2z - c2z * c2x + 0.3;
|
|
6634
6959
|
return {
|
|
6635
6960
|
value: val,
|
|
@@ -6652,8 +6977,8 @@ function lidinoid$1(x2, y2, z2, cellSize, thickness, thicknessMode) {
|
|
|
6652
6977
|
}
|
|
6653
6978
|
function tpmsDistance({ value, gx, gy, gz }, thickness, thicknessMode) {
|
|
6654
6979
|
if (thicknessMode !== "metric-approx") return abs$1(value) - thickness;
|
|
6655
|
-
const grad = sqrt$
|
|
6656
|
-
return abs$1(value) / Math.max(grad, GRAD_EPS) - thickness * 0.5;
|
|
6980
|
+
const grad = sqrt$2(gx * gx + gy * gy + gz * gz);
|
|
6981
|
+
return abs$1(value) / Math.max(grad, GRAD_EPS$1) - thickness * 0.5;
|
|
6657
6982
|
}
|
|
6658
6983
|
function mix(h) {
|
|
6659
6984
|
h = (h ^ h >>> 16) * 2246822507 | 0;
|
|
@@ -6749,76 +7074,76 @@ function seededWorley3Surface(seed) {
|
|
|
6749
7074
|
const s = seed | 0;
|
|
6750
7075
|
return (x2, y2, z2, nx, ny, nz, threshold) => worleySurface(x2, y2, z2, s, nx, ny, nz, threshold);
|
|
6751
7076
|
}
|
|
6752
|
-
const { abs, cos, max, min, sin, sqrt, PI } = Math;
|
|
6753
|
-
const DEG$
|
|
7077
|
+
const { abs, cos: cos$1, max: max$1, min, sin: sin$1, sqrt: sqrt$1, PI: PI$1 } = Math;
|
|
7078
|
+
const DEG$2 = PI$1 / 180;
|
|
6754
7079
|
function clamp$a(v, lo, hi) {
|
|
6755
7080
|
return v < lo ? lo : v > hi ? hi : v;
|
|
6756
7081
|
}
|
|
6757
|
-
function length2(x2, y2) {
|
|
6758
|
-
return sqrt(x2 * x2 + y2 * y2);
|
|
7082
|
+
function length2$1(x2, y2) {
|
|
7083
|
+
return sqrt$1(x2 * x2 + y2 * y2);
|
|
6759
7084
|
}
|
|
6760
|
-
function length3(x2, y2, z2) {
|
|
6761
|
-
return sqrt(x2 * x2 + y2 * y2 + z2 * z2);
|
|
7085
|
+
function length3$1(x2, y2, z2) {
|
|
7086
|
+
return sqrt$1(x2 * x2 + y2 * y2 + z2 * z2);
|
|
6762
7087
|
}
|
|
6763
|
-
function sdSphere(px, py, pz, r) {
|
|
6764
|
-
return length3(px, py, pz) - r;
|
|
7088
|
+
function sdSphere$1(px, py, pz, r) {
|
|
7089
|
+
return length3$1(px, py, pz) - r;
|
|
6765
7090
|
}
|
|
6766
|
-
function sdBox(px, py, pz, hx, hy, hz) {
|
|
7091
|
+
function sdBox$1(px, py, pz, hx, hy, hz) {
|
|
6767
7092
|
const dx = abs(px) - hx;
|
|
6768
7093
|
const dy = abs(py) - hy;
|
|
6769
7094
|
const dz = abs(pz) - hz;
|
|
6770
|
-
return length3(max(dx, 0), max(dy, 0), max(dz, 0)) + min(max(dx, dy, dz), 0);
|
|
7095
|
+
return length3$1(max$1(dx, 0), max$1(dy, 0), max$1(dz, 0)) + min(max$1(dx, dy, dz), 0);
|
|
6771
7096
|
}
|
|
6772
|
-
function sdCylinder(px, py, pz, h, r) {
|
|
6773
|
-
const dx = length2(px, py) - r;
|
|
7097
|
+
function sdCylinder$1(px, py, pz, h, r) {
|
|
7098
|
+
const dx = length2$1(px, py) - r;
|
|
6774
7099
|
const dz = abs(pz) - h * 0.5;
|
|
6775
|
-
return length2(max(dx, 0), max(dz, 0)) + min(max(dx, dz), 0);
|
|
7100
|
+
return length2$1(max$1(dx, 0), max$1(dz, 0)) + min(max$1(dx, dz), 0);
|
|
6776
7101
|
}
|
|
6777
|
-
function sdTorus(px, py, pz, R, r) {
|
|
6778
|
-
const qx = length2(px, py) - R;
|
|
6779
|
-
return length2(qx, pz) - r;
|
|
7102
|
+
function sdTorus$1(px, py, pz, R, r) {
|
|
7103
|
+
const qx = length2$1(px, py) - R;
|
|
7104
|
+
return length2$1(qx, pz) - r;
|
|
6780
7105
|
}
|
|
6781
|
-
function sdCapsule(px, py, pz, h, r) {
|
|
7106
|
+
function sdCapsule$1(px, py, pz, h, r) {
|
|
6782
7107
|
const halfH = h * 0.5;
|
|
6783
7108
|
const cz = clamp$a(pz, -halfH, halfH);
|
|
6784
|
-
return length3(px, py, pz - cz) - r;
|
|
7109
|
+
return length3$1(px, py, pz - cz) - r;
|
|
6785
7110
|
}
|
|
6786
|
-
function sdCone(px, py, pz, h, r) {
|
|
6787
|
-
const q = length2(px, py);
|
|
6788
|
-
const cLen = length2(h, r);
|
|
7111
|
+
function sdCone$1(px, py, pz, h, r) {
|
|
7112
|
+
const q = length2$1(px, py);
|
|
7113
|
+
const cLen = length2$1(h, r);
|
|
6789
7114
|
const nx = h / cLen;
|
|
6790
7115
|
const nz = -r / cLen;
|
|
6791
|
-
const d2 = max(nx * q + nz * (pz - h), -pz, pz - h);
|
|
7116
|
+
const d2 = max$1(nx * q + nz * (pz - h), -pz, pz - h);
|
|
6792
7117
|
return d2;
|
|
6793
7118
|
}
|
|
6794
|
-
function sdTaperedSegment(px, py, pz, ax, ay, az, bx, by, bz, ra, rb) {
|
|
7119
|
+
function sdTaperedSegment$1(px, py, pz, ax, ay, az, bx, by, bz, ra, rb) {
|
|
6795
7120
|
const vx = bx - ax;
|
|
6796
7121
|
const vy = by - ay;
|
|
6797
7122
|
const vz = bz - az;
|
|
6798
7123
|
const len2 = vx * vx + vy * vy + vz * vz;
|
|
6799
|
-
if (len2 <= 1e-12) return sdSphere(px - ax, py - ay, pz - az, max(ra, rb));
|
|
7124
|
+
if (len2 <= 1e-12) return sdSphere$1(px - ax, py - ay, pz - az, max$1(ra, rb));
|
|
6800
7125
|
const h = clamp$a(((px - ax) * vx + (py - ay) * vy + (pz - az) * vz) / len2, 0, 1);
|
|
6801
|
-
return length3(px - (ax + vx * h), py - (ay + vy * h), pz - (az + vz * h)) - (ra + (rb - ra) * h);
|
|
7126
|
+
return length3$1(px - (ax + vx * h), py - (ay + vy * h), pz - (az + vz * h)) - (ra + (rb - ra) * h);
|
|
6802
7127
|
}
|
|
6803
7128
|
function sdPolylineSweep3(node, x2, y2, z2) {
|
|
6804
7129
|
let d2 = 1e20;
|
|
6805
7130
|
for (let i = 0; i < node.points.length - 1; i++) {
|
|
6806
7131
|
const a2 = node.points[i];
|
|
6807
7132
|
const b = node.points[i + 1];
|
|
6808
|
-
const segment = sdTaperedSegment(x2, y2, z2, a2[0], a2[1], a2[2], b[0], b[1], b[2], node.radii[i], node.radii[i + 1]);
|
|
6809
|
-
d2 = i === 0 ? segment : smin(d2, segment, node.blend);
|
|
7133
|
+
const segment = sdTaperedSegment$1(x2, y2, z2, a2[0], a2[1], a2[2], b[0], b[1], b[2], node.radii[i], node.radii[i + 1]);
|
|
7134
|
+
d2 = i === 0 ? segment : smin$1(d2, segment, node.blend);
|
|
6810
7135
|
}
|
|
6811
7136
|
return d2;
|
|
6812
7137
|
}
|
|
6813
|
-
function smin(a2, b, k2) {
|
|
7138
|
+
function smin$1(a2, b, k2) {
|
|
6814
7139
|
if (k2 <= 0) return min(a2, b);
|
|
6815
|
-
const h = max(k2 - abs(a2 - b), 0) / k2;
|
|
7140
|
+
const h = max$1(k2 - abs(a2 - b), 0) / k2;
|
|
6816
7141
|
return min(a2, b) - h * h * h * k2 * (1 / 6);
|
|
6817
7142
|
}
|
|
6818
|
-
function smax(a2, b, k2) {
|
|
6819
|
-
return -smin(-a2, -b, k2);
|
|
7143
|
+
function smax$1(a2, b, k2) {
|
|
7144
|
+
return -smin$1(-a2, -b, k2);
|
|
6820
7145
|
}
|
|
6821
|
-
function repeatCoord(v, spacing, count) {
|
|
7146
|
+
function repeatCoord$1(v, spacing, count) {
|
|
6822
7147
|
if (spacing <= 0) return v;
|
|
6823
7148
|
if (count > 0) {
|
|
6824
7149
|
const center = (count - 1) * 0.5;
|
|
@@ -6827,31 +7152,138 @@ function repeatCoord(v, spacing, count) {
|
|
|
6827
7152
|
}
|
|
6828
7153
|
return v - spacing * Math.round(v / spacing);
|
|
6829
7154
|
}
|
|
7155
|
+
function positiveMod(v, period) {
|
|
7156
|
+
return (v % period + period) % period;
|
|
7157
|
+
}
|
|
7158
|
+
function evalStripesPattern(u2, v, directionX, directionY, spacing, width, depth) {
|
|
7159
|
+
const coord = u2 * directionX + v * directionY;
|
|
7160
|
+
const d2 = abs(coord - Math.round(coord / spacing) * spacing);
|
|
7161
|
+
const profile = max$1(0, 1 - d2 / (width * 0.5));
|
|
7162
|
+
return -(profile * profile) * depth;
|
|
7163
|
+
}
|
|
7164
|
+
function evalOverUnderWeavePattern(u2, v, spacingX, spacingY, widthX, widthY, depth, underScale) {
|
|
7165
|
+
const su = u2 / spacingX;
|
|
7166
|
+
const sv = v / spacingY;
|
|
7167
|
+
let pU = max$1(0, 1 - abs(su - Math.round(su)) * spacingX / (widthX * 0.5));
|
|
7168
|
+
let pV = max$1(0, 1 - abs(sv - Math.round(sv)) * spacingY / (widthY * 0.5));
|
|
7169
|
+
pU *= pU;
|
|
7170
|
+
pV *= pV;
|
|
7171
|
+
const checker = (Math.round(su) & 65535) + (Math.round(sv) & 65535) & 1;
|
|
7172
|
+
const top = checker ? pV : pU;
|
|
7173
|
+
const bot = checker ? pU : pV;
|
|
7174
|
+
return -max$1(top, bot * underScale) * depth;
|
|
7175
|
+
}
|
|
7176
|
+
function compileTypedSurfacePattern(pattern) {
|
|
7177
|
+
switch (pattern.kind) {
|
|
7178
|
+
case "surfacePattern:constant":
|
|
7179
|
+
return () => pattern.value;
|
|
7180
|
+
case "surfacePattern:sineWave": {
|
|
7181
|
+
const { direction: direction2, wavelength, amplitude, phase, bias } = pattern;
|
|
7182
|
+
const frequency = 2 * PI$1 / wavelength;
|
|
7183
|
+
return (u2, v) => bias + sin$1((u2 * direction2[0] + v * direction2[1]) * frequency + phase) * amplitude;
|
|
7184
|
+
}
|
|
7185
|
+
case "surfacePattern:stripes": {
|
|
7186
|
+
const { direction: direction2, spacing, width, depth } = pattern;
|
|
7187
|
+
return (u2, v) => evalStripesPattern(u2, v, direction2[0], direction2[1], spacing, width, depth);
|
|
7188
|
+
}
|
|
7189
|
+
case "surfacePattern:overUnderWeave": {
|
|
7190
|
+
const { spacing, threadWidth, depth, underScale } = pattern;
|
|
7191
|
+
return (u2, v) => evalOverUnderWeavePattern(u2, v, spacing[0], spacing[1], threadWidth[0], threadWidth[1], depth, underScale);
|
|
7192
|
+
}
|
|
7193
|
+
case "surfacePattern:abs": {
|
|
7194
|
+
const child = compileTypedSurfacePattern(pattern.child);
|
|
7195
|
+
return (u2, v) => abs(child(u2, v));
|
|
7196
|
+
}
|
|
7197
|
+
case "surfacePattern:negate": {
|
|
7198
|
+
const child = compileTypedSurfacePattern(pattern.child);
|
|
7199
|
+
return (u2, v) => -child(u2, v);
|
|
7200
|
+
}
|
|
7201
|
+
case "surfacePattern:add": {
|
|
7202
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
7203
|
+
return (u2, v) => children.reduce((sum2, child) => sum2 + child(u2, v), 0);
|
|
7204
|
+
}
|
|
7205
|
+
case "surfacePattern:multiply": {
|
|
7206
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
7207
|
+
return (u2, v) => children.reduce((product, child) => product * child(u2, v), 1);
|
|
7208
|
+
}
|
|
7209
|
+
case "surfacePattern:min": {
|
|
7210
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
7211
|
+
if (children.length === 0) return () => 0;
|
|
7212
|
+
return (u2, v) => children.reduce((value, child) => min(value, child(u2, v)), Infinity);
|
|
7213
|
+
}
|
|
7214
|
+
case "surfacePattern:max": {
|
|
7215
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
7216
|
+
if (children.length === 0) return () => 0;
|
|
7217
|
+
return (u2, v) => children.reduce((value, child) => max$1(value, child(u2, v)), -Infinity);
|
|
7218
|
+
}
|
|
7219
|
+
case "surfacePattern:clamp": {
|
|
7220
|
+
const child = compileTypedSurfacePattern(pattern.child);
|
|
7221
|
+
return (u2, v) => clamp$a(child(u2, v), pattern.min, pattern.max);
|
|
7222
|
+
}
|
|
7223
|
+
}
|
|
7224
|
+
}
|
|
7225
|
+
function estimateSurfacePatternAmplitude(pattern) {
|
|
7226
|
+
switch (pattern.kind) {
|
|
7227
|
+
case "surfacePattern:constant":
|
|
7228
|
+
return abs(pattern.value);
|
|
7229
|
+
case "surfacePattern:sineWave":
|
|
7230
|
+
return abs(pattern.bias) + abs(pattern.amplitude);
|
|
7231
|
+
case "surfacePattern:stripes":
|
|
7232
|
+
case "surfacePattern:overUnderWeave":
|
|
7233
|
+
return pattern.depth;
|
|
7234
|
+
case "surfacePattern:abs":
|
|
7235
|
+
case "surfacePattern:negate":
|
|
7236
|
+
return estimateSurfacePatternAmplitude(pattern.child);
|
|
7237
|
+
case "surfacePattern:add": {
|
|
7238
|
+
const amplitudes = pattern.children.map(estimateSurfacePatternAmplitude);
|
|
7239
|
+
return amplitudes.every((value) => value !== null) ? amplitudes.reduce((sum2, value) => sum2 + value, 0) : null;
|
|
7240
|
+
}
|
|
7241
|
+
case "surfacePattern:multiply": {
|
|
7242
|
+
const amplitudes = pattern.children.map(estimateSurfacePatternAmplitude);
|
|
7243
|
+
return amplitudes.every((value) => value !== null) ? amplitudes.reduce((product, value) => product * value, 1) : null;
|
|
7244
|
+
}
|
|
7245
|
+
case "surfacePattern:min":
|
|
7246
|
+
case "surfacePattern:max": {
|
|
7247
|
+
const amplitudes = pattern.children.map(estimateSurfacePatternAmplitude);
|
|
7248
|
+
return amplitudes.every((value) => value !== null) ? max$1(...amplitudes) : null;
|
|
7249
|
+
}
|
|
7250
|
+
case "surfacePattern:clamp":
|
|
7251
|
+
return max$1(abs(pattern.min), abs(pattern.max));
|
|
7252
|
+
}
|
|
7253
|
+
}
|
|
7254
|
+
function compileSurfacePattern(node) {
|
|
7255
|
+
if (node.pattern) return compileTypedSurfacePattern(node.pattern);
|
|
7256
|
+
const constEntries = Object.entries(node.constants ?? {});
|
|
7257
|
+
const constNames = constEntries.map(([k2]) => k2);
|
|
7258
|
+
const constValues = constEntries.map(([, v]) => v);
|
|
7259
|
+
const patternFn = new Function("u", "v", ...constNames, `return (${node.patternBody});`);
|
|
7260
|
+
return (u2, v) => patternFn(u2, v, ...constValues);
|
|
7261
|
+
}
|
|
6830
7262
|
function compileSdfNode3(node) {
|
|
6831
7263
|
switch (node.kind) {
|
|
6832
7264
|
case "sdf:sphere": {
|
|
6833
7265
|
const r = node.radius;
|
|
6834
|
-
return (x2, y2, z2) => sdSphere(x2, y2, z2, r);
|
|
7266
|
+
return (x2, y2, z2) => sdSphere$1(x2, y2, z2, r);
|
|
6835
7267
|
}
|
|
6836
7268
|
case "sdf:box": {
|
|
6837
7269
|
const [hx, hy, hz] = node.halfExtents;
|
|
6838
|
-
return (x2, y2, z2) => sdBox(x2, y2, z2, hx, hy, hz);
|
|
7270
|
+
return (x2, y2, z2) => sdBox$1(x2, y2, z2, hx, hy, hz);
|
|
6839
7271
|
}
|
|
6840
7272
|
case "sdf:cylinder": {
|
|
6841
7273
|
const { height: h, radius: r } = node;
|
|
6842
|
-
return (x2, y2, z2) => sdCylinder(x2, y2, z2, h, r);
|
|
7274
|
+
return (x2, y2, z2) => sdCylinder$1(x2, y2, z2, h, r);
|
|
6843
7275
|
}
|
|
6844
7276
|
case "sdf:torus": {
|
|
6845
7277
|
const { majorRadius: R, minorRadius: r } = node;
|
|
6846
|
-
return (x2, y2, z2) => sdTorus(x2, y2, z2, R, r);
|
|
7278
|
+
return (x2, y2, z2) => sdTorus$1(x2, y2, z2, R, r);
|
|
6847
7279
|
}
|
|
6848
7280
|
case "sdf:capsule": {
|
|
6849
7281
|
const { height: h, radius: r } = node;
|
|
6850
|
-
return (x2, y2, z2) => sdCapsule(x2, y2, z2, h, r);
|
|
7282
|
+
return (x2, y2, z2) => sdCapsule$1(x2, y2, z2, h, r);
|
|
6851
7283
|
}
|
|
6852
7284
|
case "sdf:cone": {
|
|
6853
7285
|
const { height: h, radius: r } = node;
|
|
6854
|
-
return (x2, y2, z2) => sdCone(x2, y2, z2, h, r);
|
|
7286
|
+
return (x2, y2, z2) => sdCone$1(x2, y2, z2, h, r);
|
|
6855
7287
|
}
|
|
6856
7288
|
case "sdf:polylineSweep": {
|
|
6857
7289
|
return (x2, y2, z2) => sdPolylineSweep3(node, x2, y2, z2);
|
|
@@ -6868,7 +7300,7 @@ function compileSdfNode3(node) {
|
|
|
6868
7300
|
const fns = node.children.map(compileSdfNode3);
|
|
6869
7301
|
return (x2, y2, z2) => {
|
|
6870
7302
|
let d2 = fns[0](x2, y2, z2);
|
|
6871
|
-
for (let i = 1; i < fns.length; i++) d2 = max(d2, -fns[i](x2, y2, z2));
|
|
7303
|
+
for (let i = 1; i < fns.length; i++) d2 = max$1(d2, -fns[i](x2, y2, z2));
|
|
6872
7304
|
return d2;
|
|
6873
7305
|
};
|
|
6874
7306
|
}
|
|
@@ -6876,7 +7308,7 @@ function compileSdfNode3(node) {
|
|
|
6876
7308
|
const fns = node.children.map(compileSdfNode3);
|
|
6877
7309
|
return (x2, y2, z2) => {
|
|
6878
7310
|
let d2 = fns[0](x2, y2, z2);
|
|
6879
|
-
for (let i = 1; i < fns.length; i++) d2 = max(d2, fns[i](x2, y2, z2));
|
|
7311
|
+
for (let i = 1; i < fns.length; i++) d2 = max$1(d2, fns[i](x2, y2, z2));
|
|
6880
7312
|
return d2;
|
|
6881
7313
|
};
|
|
6882
7314
|
}
|
|
@@ -6885,7 +7317,7 @@ function compileSdfNode3(node) {
|
|
|
6885
7317
|
const k2 = node.radius;
|
|
6886
7318
|
return (x2, y2, z2) => {
|
|
6887
7319
|
let d2 = fns[0](x2, y2, z2);
|
|
6888
|
-
for (let i = 1; i < fns.length; i++) d2 = smin(d2, fns[i](x2, y2, z2), k2);
|
|
7320
|
+
for (let i = 1; i < fns.length; i++) d2 = smin$1(d2, fns[i](x2, y2, z2), k2);
|
|
6889
7321
|
return d2;
|
|
6890
7322
|
};
|
|
6891
7323
|
}
|
|
@@ -6894,7 +7326,7 @@ function compileSdfNode3(node) {
|
|
|
6894
7326
|
const k2 = node.radius;
|
|
6895
7327
|
return (x2, y2, z2) => {
|
|
6896
7328
|
let d2 = fns[0](x2, y2, z2);
|
|
6897
|
-
for (let i = 1; i < fns.length; i++) d2 = smax(d2, -fns[i](x2, y2, z2), k2);
|
|
7329
|
+
for (let i = 1; i < fns.length; i++) d2 = smax$1(d2, -fns[i](x2, y2, z2), k2);
|
|
6898
7330
|
return d2;
|
|
6899
7331
|
};
|
|
6900
7332
|
}
|
|
@@ -6903,7 +7335,7 @@ function compileSdfNode3(node) {
|
|
|
6903
7335
|
const k2 = node.radius;
|
|
6904
7336
|
return (x2, y2, z2) => {
|
|
6905
7337
|
let d2 = fns[0](x2, y2, z2);
|
|
6906
|
-
for (let i = 1; i < fns.length; i++) d2 = smax(d2, fns[i](x2, y2, z2), k2);
|
|
7338
|
+
for (let i = 1; i < fns.length; i++) d2 = smax$1(d2, fns[i](x2, y2, z2), k2);
|
|
6907
7339
|
return d2;
|
|
6908
7340
|
};
|
|
6909
7341
|
}
|
|
@@ -6921,10 +7353,10 @@ function compileSdfNode3(node) {
|
|
|
6921
7353
|
}
|
|
6922
7354
|
case "sdf:rotate": {
|
|
6923
7355
|
const fn = compileSdfNode3(node.child);
|
|
6924
|
-
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$
|
|
6925
|
-
const cx = cos(rx), sx = sin(rx);
|
|
6926
|
-
const cy = cos(ry), sy = sin(ry);
|
|
6927
|
-
const cz = cos(rz), sz = sin(rz);
|
|
7356
|
+
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$2);
|
|
7357
|
+
const cx = cos$1(rx), sx = sin$1(rx);
|
|
7358
|
+
const cy = cos$1(ry), sy = sin$1(ry);
|
|
7359
|
+
const cz = cos$1(rz), sz = sin$1(rz);
|
|
6928
7360
|
return (x2, y2, z2) => {
|
|
6929
7361
|
const x1 = cz * x2 + sz * y2;
|
|
6930
7362
|
const y1 = -sz * x2 + cz * y2;
|
|
@@ -6943,10 +7375,10 @@ function compileSdfNode3(node) {
|
|
|
6943
7375
|
}
|
|
6944
7376
|
case "sdf:twist": {
|
|
6945
7377
|
const fn = compileSdfNode3(node.child);
|
|
6946
|
-
const k2 = node.degreesPerUnit * DEG$
|
|
7378
|
+
const k2 = node.degreesPerUnit * DEG$2;
|
|
6947
7379
|
return (x2, y2, z2) => {
|
|
6948
7380
|
const angle = k2 * z2;
|
|
6949
|
-
const c2 = cos(angle), s = sin(angle);
|
|
7381
|
+
const c2 = cos$1(angle), s = sin$1(angle);
|
|
6950
7382
|
return fn(c2 * x2 - s * y2, s * x2 + c2 * y2, z2);
|
|
6951
7383
|
};
|
|
6952
7384
|
}
|
|
@@ -6955,7 +7387,7 @@ function compileSdfNode3(node) {
|
|
|
6955
7387
|
const r = node.radius;
|
|
6956
7388
|
return (x2, y2, z2) => {
|
|
6957
7389
|
const angle = x2 / r;
|
|
6958
|
-
const c2 = cos(angle), s = sin(angle);
|
|
7390
|
+
const c2 = cos$1(angle), s = sin$1(angle);
|
|
6959
7391
|
return fn((r + y2) * s, (r + y2) * c2 - r, z2);
|
|
6960
7392
|
};
|
|
6961
7393
|
}
|
|
@@ -6963,7 +7395,19 @@ function compileSdfNode3(node) {
|
|
|
6963
7395
|
const fn = compileSdfNode3(node.child);
|
|
6964
7396
|
const [sx, sy, sz] = node.spacing;
|
|
6965
7397
|
const [cx, cy, cz] = node.count;
|
|
6966
|
-
return (x2, y2, z2) => fn(repeatCoord(x2, sx, cx), repeatCoord(y2, sy, cy), repeatCoord(z2, sz, cz));
|
|
7398
|
+
return (x2, y2, z2) => fn(repeatCoord$1(x2, sx, cx), repeatCoord$1(y2, sy, cy), repeatCoord$1(z2, sz, cz));
|
|
7399
|
+
}
|
|
7400
|
+
case "sdf:circularArray": {
|
|
7401
|
+
const fn = compileSdfNode3(node.child);
|
|
7402
|
+
const da = 2 * PI$1 / node.count;
|
|
7403
|
+
const offset2 = node.offset;
|
|
7404
|
+
return (x2, y2, z2) => {
|
|
7405
|
+
const r = length2$1(x2, y2);
|
|
7406
|
+
const a2 = positiveMod(Math.atan2(y2, x2), da);
|
|
7407
|
+
const d1 = fn(cos$1(a2 - da) * r - offset2, sin$1(a2 - da) * r, z2);
|
|
7408
|
+
const d2 = fn(cos$1(a2) * r - offset2, sin$1(a2) * r, z2);
|
|
7409
|
+
return min(d1, d2);
|
|
7410
|
+
};
|
|
6967
7411
|
}
|
|
6968
7412
|
case "sdf:shell": {
|
|
6969
7413
|
const fn = compileSdfNode3(node.child);
|
|
@@ -6980,10 +7424,7 @@ function compileSdfNode3(node) {
|
|
|
6980
7424
|
}
|
|
6981
7425
|
case "sdf:surfaceDisplace": {
|
|
6982
7426
|
const childFn = compileSdfNode3(node.child);
|
|
6983
|
-
const
|
|
6984
|
-
const constNames = constEntries.map(([k2]) => k2);
|
|
6985
|
-
const constValues = constEntries.map(([, v]) => v);
|
|
6986
|
-
const patternFn = new Function("u", "v", ...constNames, `return (${node.patternBody});`);
|
|
7427
|
+
const patternFn = compileSurfacePattern(node);
|
|
6987
7428
|
const uvMode = node.uvMode && node.uvMode !== "auto" ? node.uvMode : void 0;
|
|
6988
7429
|
const analysis = analyzeUV(node.child, uvMode);
|
|
6989
7430
|
const uvFn = compileUVFunction(analysis);
|
|
@@ -6995,7 +7436,7 @@ function compileSdfNode3(node) {
|
|
|
6995
7436
|
p2[1] = y2;
|
|
6996
7437
|
p2[2] = z2;
|
|
6997
7438
|
const [u2, v] = uvFn(p2);
|
|
6998
|
-
return d2 + patternFn(u2, v
|
|
7439
|
+
return d2 + patternFn(u2, v);
|
|
6999
7440
|
};
|
|
7000
7441
|
}
|
|
7001
7442
|
const sharpness = node.triplanarSharpness ?? 4;
|
|
@@ -7006,9 +7447,9 @@ function compileSdfNode3(node) {
|
|
|
7006
7447
|
const gy = childFn(x2, y2 + eps, z2) - childFn(x2, y2 - eps, z2);
|
|
7007
7448
|
const gz = childFn(x2, y2, z2 + eps) - childFn(x2, y2, z2 - eps);
|
|
7008
7449
|
const { wx, wy, wz } = triplanarWeights(gx, gy, gz, sharpness);
|
|
7009
|
-
const hX = patternFn(y2, z2
|
|
7010
|
-
const hY = patternFn(x2, z2
|
|
7011
|
-
const hZ = patternFn(x2, y2
|
|
7450
|
+
const hX = patternFn(y2, z2);
|
|
7451
|
+
const hY = patternFn(x2, z2);
|
|
7452
|
+
const hZ = patternFn(x2, y2);
|
|
7012
7453
|
return d2 + wx * hX + wy * hY + wz * hZ;
|
|
7013
7454
|
};
|
|
7014
7455
|
}
|
|
@@ -7078,7 +7519,7 @@ function compileSdfNode3(node) {
|
|
|
7078
7519
|
const gx = gradFn(x2 + eps, y2, z2) - gradFn(x2 - eps, y2, z2);
|
|
7079
7520
|
const gy = gradFn(x2, y2 + eps, z2) - gradFn(x2, y2 - eps, z2);
|
|
7080
7521
|
const gz = gradFn(x2, y2, z2 + eps) - gradFn(x2, y2, z2 - eps);
|
|
7081
|
-
const glen = sqrt(gx * gx + gy * gy + gz * gz);
|
|
7522
|
+
const glen = sqrt$1(gx * gx + gy * gy + gz * gz);
|
|
7082
7523
|
let nx = 0, ny = 0, nz = 0;
|
|
7083
7524
|
if (glen > 1e-10) {
|
|
7084
7525
|
const invG = 1 / glen;
|
|
@@ -7140,7 +7581,7 @@ function estimateSdfBounds(node) {
|
|
|
7140
7581
|
for (const point2 of node.points) {
|
|
7141
7582
|
for (let i = 0; i < 3; i++) {
|
|
7142
7583
|
minPoint[i] = min(minPoint[i], point2[i]);
|
|
7143
|
-
maxPoint[i] = max(maxPoint[i], point2[i]);
|
|
7584
|
+
maxPoint[i] = max$1(maxPoint[i], point2[i]);
|
|
7144
7585
|
}
|
|
7145
7586
|
}
|
|
7146
7587
|
return padBounds({ min: minPoint, max: maxPoint }, pad);
|
|
@@ -7171,7 +7612,7 @@ function estimateSdfBounds(node) {
|
|
|
7171
7612
|
}
|
|
7172
7613
|
case "sdf:rotate": {
|
|
7173
7614
|
const b = estimateSdfBounds(node.child);
|
|
7174
|
-
const r = length3(max(abs(b.min[0]), abs(b.max[0])), max(abs(b.min[1]), abs(b.max[1])), max(abs(b.min[2]), abs(b.max[2])));
|
|
7615
|
+
const r = length3$1(max$1(abs(b.min[0]), abs(b.max[0])), max$1(abs(b.min[1]), abs(b.max[1])), max$1(abs(b.min[2]), abs(b.max[2])));
|
|
7175
7616
|
return { min: [-r, -r, -r], max: [r, r, r] };
|
|
7176
7617
|
}
|
|
7177
7618
|
case "sdf:scale": {
|
|
@@ -7185,7 +7626,7 @@ function estimateSdfBounds(node) {
|
|
|
7185
7626
|
case "sdf:twist":
|
|
7186
7627
|
case "sdf:bend": {
|
|
7187
7628
|
const b = estimateSdfBounds(node.child);
|
|
7188
|
-
const r = length3(max(abs(b.min[0]), abs(b.max[0])), max(abs(b.min[1]), abs(b.max[1])), max(abs(b.min[2]), abs(b.max[2]))) * 1.5;
|
|
7629
|
+
const r = length3$1(max$1(abs(b.min[0]), abs(b.max[0])), max$1(abs(b.min[1]), abs(b.max[1])), max$1(abs(b.min[2]), abs(b.max[2]))) * 1.5;
|
|
7189
7630
|
return { min: [-r, -r, -r], max: [r, r, r] };
|
|
7190
7631
|
}
|
|
7191
7632
|
case "sdf:repeat": {
|
|
@@ -7206,16 +7647,29 @@ function estimateSdfBounds(node) {
|
|
|
7206
7647
|
const [zMin, zMax] = expand(sz, cz, b.min[2], b.max[2]);
|
|
7207
7648
|
return { min: [xMin, yMin, zMin], max: [xMax, yMax, zMax] };
|
|
7208
7649
|
}
|
|
7650
|
+
case "sdf:circularArray": {
|
|
7651
|
+
const b = estimateSdfBounds(node.child);
|
|
7652
|
+
const x0 = b.min[0] + node.offset;
|
|
7653
|
+
const x1 = b.max[0] + node.offset;
|
|
7654
|
+
const y0 = b.min[1];
|
|
7655
|
+
const y1 = b.max[1];
|
|
7656
|
+
const r = max$1(length2$1(x0, y0), length2$1(x0, y1), length2$1(x1, y0), length2$1(x1, y1));
|
|
7657
|
+
return { min: [-r, -r, b.min[2]], max: [r, r, b.max[2]] };
|
|
7658
|
+
}
|
|
7209
7659
|
case "sdf:shell": {
|
|
7210
7660
|
const b = estimateSdfBounds(node.child);
|
|
7211
7661
|
const t = node.thickness * 0.5;
|
|
7212
7662
|
return padBounds(b, t);
|
|
7213
7663
|
}
|
|
7214
|
-
case "sdf:displace":
|
|
7215
|
-
case "sdf:surfaceDisplace": {
|
|
7664
|
+
case "sdf:displace": {
|
|
7216
7665
|
const b = estimateSdfBounds(node.child);
|
|
7217
7666
|
return padBounds(b, 5);
|
|
7218
7667
|
}
|
|
7668
|
+
case "sdf:surfaceDisplace": {
|
|
7669
|
+
const b = estimateSdfBounds(node.child);
|
|
7670
|
+
if (!node.pattern) return padBounds(b, 5);
|
|
7671
|
+
return padBounds(b, estimateSurfacePatternAmplitude(node.pattern) ?? 5);
|
|
7672
|
+
}
|
|
7219
7673
|
case "sdf:onion": {
|
|
7220
7674
|
const b = estimateSdfBounds(node.child);
|
|
7221
7675
|
return padBounds(b, node.layers * node.thickness);
|
|
@@ -7252,7 +7706,7 @@ function unionBounds(bounds, pad) {
|
|
|
7252
7706
|
for (const b of bounds) {
|
|
7253
7707
|
for (let i = 0; i < 3; i++) {
|
|
7254
7708
|
result.min[i] = min(result.min[i], b.min[i]);
|
|
7255
|
-
result.max[i] = max(result.max[i], b.max[i]);
|
|
7709
|
+
result.max[i] = max$1(result.max[i], b.max[i]);
|
|
7256
7710
|
}
|
|
7257
7711
|
}
|
|
7258
7712
|
if (pad > 0) return padBounds(result, pad);
|
|
@@ -7265,7 +7719,7 @@ function intersectBounds(bounds, pad) {
|
|
|
7265
7719
|
};
|
|
7266
7720
|
for (const b of bounds) {
|
|
7267
7721
|
for (let i = 0; i < 3; i++) {
|
|
7268
|
-
result.min[i] = max(result.min[i], b.min[i]);
|
|
7722
|
+
result.min[i] = max$1(result.min[i], b.min[i]);
|
|
7269
7723
|
result.max[i] = min(result.max[i], b.max[i]);
|
|
7270
7724
|
}
|
|
7271
7725
|
}
|
|
@@ -7281,242 +7735,544 @@ function padBounds(b, pad) {
|
|
|
7281
7735
|
max: [b.max[0] + pad, b.max[1] + pad, b.max[2] + pad]
|
|
7282
7736
|
};
|
|
7283
7737
|
}
|
|
7284
|
-
const
|
|
7285
|
-
const
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7738
|
+
const { PI } = Math;
|
|
7739
|
+
const DEG$1 = PI / 180;
|
|
7740
|
+
const TAU = 2 * PI;
|
|
7741
|
+
const GRAD_EPS = 1e-9;
|
|
7742
|
+
const Op = {
|
|
7743
|
+
Const: 0,
|
|
7744
|
+
Neg: 1,
|
|
7745
|
+
Abs: 2,
|
|
7746
|
+
Sqrt: 3,
|
|
7747
|
+
Sin: 4,
|
|
7748
|
+
Cos: 5,
|
|
7749
|
+
Round: 6,
|
|
7750
|
+
Add: 7,
|
|
7751
|
+
Sub: 8,
|
|
7752
|
+
Mul: 9,
|
|
7753
|
+
Div: 10,
|
|
7754
|
+
Min: 11,
|
|
7755
|
+
Max: 12
|
|
7756
|
+
};
|
|
7757
|
+
class UnsupportedSdfProgramNodeError extends Error {
|
|
7758
|
+
constructor(message) {
|
|
7759
|
+
super(message);
|
|
7760
|
+
this.name = "UnsupportedSdfProgramNodeError";
|
|
7304
7761
|
}
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
);
|
|
7762
|
+
}
|
|
7763
|
+
class SdfProgramBuilder {
|
|
7764
|
+
constructor() {
|
|
7765
|
+
__publicField(this, "opcodes", []);
|
|
7766
|
+
__publicField(this, "argA", []);
|
|
7767
|
+
__publicField(this, "argB", []);
|
|
7768
|
+
__publicField(this, "argC", []);
|
|
7769
|
+
__publicField(this, "constants", []);
|
|
7770
|
+
__publicField(this, "x", 0);
|
|
7771
|
+
__publicField(this, "y", 1);
|
|
7772
|
+
__publicField(this, "z", 2);
|
|
7310
7773
|
}
|
|
7311
|
-
|
|
7312
|
-
|
|
7774
|
+
constant(value) {
|
|
7775
|
+
const index2 = this.constants.length;
|
|
7776
|
+
this.constants.push(value);
|
|
7777
|
+
return this.push(Op.Const, 0, 0, index2);
|
|
7313
7778
|
}
|
|
7314
|
-
|
|
7315
|
-
|
|
7779
|
+
neg(a2) {
|
|
7780
|
+
return this.push(Op.Neg, a2);
|
|
7316
7781
|
}
|
|
7317
|
-
|
|
7318
|
-
|
|
7782
|
+
abs(a2) {
|
|
7783
|
+
return this.push(Op.Abs, a2);
|
|
7319
7784
|
}
|
|
7320
|
-
|
|
7321
|
-
|
|
7785
|
+
sqrt(a2) {
|
|
7786
|
+
return this.push(Op.Sqrt, a2);
|
|
7787
|
+
}
|
|
7788
|
+
sin(a2) {
|
|
7789
|
+
return this.push(Op.Sin, a2);
|
|
7790
|
+
}
|
|
7791
|
+
cos(a2) {
|
|
7792
|
+
return this.push(Op.Cos, a2);
|
|
7793
|
+
}
|
|
7794
|
+
round(a2) {
|
|
7795
|
+
return this.push(Op.Round, a2);
|
|
7796
|
+
}
|
|
7797
|
+
add(a2, b) {
|
|
7798
|
+
return this.push(Op.Add, a2, b);
|
|
7799
|
+
}
|
|
7800
|
+
sub(a2, b) {
|
|
7801
|
+
return this.push(Op.Sub, a2, b);
|
|
7802
|
+
}
|
|
7803
|
+
mul(a2, b) {
|
|
7804
|
+
return this.push(Op.Mul, a2, b);
|
|
7805
|
+
}
|
|
7806
|
+
div(a2, b) {
|
|
7807
|
+
return this.push(Op.Div, a2, b);
|
|
7808
|
+
}
|
|
7809
|
+
min(a2, b) {
|
|
7810
|
+
return this.push(Op.Min, a2, b);
|
|
7811
|
+
}
|
|
7812
|
+
max(a2, b) {
|
|
7813
|
+
return this.push(Op.Max, a2, b);
|
|
7814
|
+
}
|
|
7815
|
+
finalize(output) {
|
|
7816
|
+
return {
|
|
7817
|
+
opcodes: Uint8Array.from(this.opcodes),
|
|
7818
|
+
argA: Int32Array.from(this.argA),
|
|
7819
|
+
argB: Int32Array.from(this.argB),
|
|
7820
|
+
argC: Int32Array.from(this.argC),
|
|
7821
|
+
constants: Float64Array.from(this.constants),
|
|
7822
|
+
output,
|
|
7823
|
+
slotCount: this.opcodes.length + 3
|
|
7824
|
+
};
|
|
7825
|
+
}
|
|
7826
|
+
push(op, a2 = 0, b = 0, c2 = 0) {
|
|
7827
|
+
const slot2 = this.opcodes.length + 3;
|
|
7828
|
+
this.opcodes.push(op);
|
|
7829
|
+
this.argA.push(a2);
|
|
7830
|
+
this.argB.push(b);
|
|
7831
|
+
this.argC.push(c2);
|
|
7832
|
+
return slot2;
|
|
7833
|
+
}
|
|
7834
|
+
}
|
|
7835
|
+
function compileSdfProgramEvaluator3(program) {
|
|
7836
|
+
const lines = ["const { abs, sqrt, sin, cos, round, min, max } = Math;", "let v0 = x;", "let v1 = y;", "let v2 = z;"];
|
|
7837
|
+
const { opcodes, argA, argB, argC, constants, output } = program;
|
|
7838
|
+
for (let i = 0; i < opcodes.length; i++) {
|
|
7839
|
+
const slot2 = `v${i + 3}`;
|
|
7840
|
+
const a2 = `v${argA[i]}`;
|
|
7841
|
+
const b = `v${argB[i]}`;
|
|
7842
|
+
switch (opcodes[i]) {
|
|
7843
|
+
case Op.Const:
|
|
7844
|
+
lines.push(`let ${slot2} = ${numberLiteral(constants[argC[i]])};`);
|
|
7845
|
+
break;
|
|
7846
|
+
case Op.Neg:
|
|
7847
|
+
lines.push(`let ${slot2} = -${a2};`);
|
|
7848
|
+
break;
|
|
7849
|
+
case Op.Abs:
|
|
7850
|
+
lines.push(`let ${slot2} = abs(${a2});`);
|
|
7851
|
+
break;
|
|
7852
|
+
case Op.Sqrt:
|
|
7853
|
+
lines.push(`let ${slot2} = sqrt(${a2});`);
|
|
7854
|
+
break;
|
|
7855
|
+
case Op.Sin:
|
|
7856
|
+
lines.push(`let ${slot2} = sin(${a2});`);
|
|
7857
|
+
break;
|
|
7858
|
+
case Op.Cos:
|
|
7859
|
+
lines.push(`let ${slot2} = cos(${a2});`);
|
|
7860
|
+
break;
|
|
7861
|
+
case Op.Round:
|
|
7862
|
+
lines.push(`let ${slot2} = round(${a2});`);
|
|
7863
|
+
break;
|
|
7864
|
+
case Op.Add:
|
|
7865
|
+
lines.push(`let ${slot2} = ${a2} + ${b};`);
|
|
7866
|
+
break;
|
|
7867
|
+
case Op.Sub:
|
|
7868
|
+
lines.push(`let ${slot2} = ${a2} - ${b};`);
|
|
7869
|
+
break;
|
|
7870
|
+
case Op.Mul:
|
|
7871
|
+
lines.push(`let ${slot2} = ${a2} * ${b};`);
|
|
7872
|
+
break;
|
|
7873
|
+
case Op.Div:
|
|
7874
|
+
lines.push(`let ${slot2} = ${a2} / ${b};`);
|
|
7875
|
+
break;
|
|
7876
|
+
case Op.Min:
|
|
7877
|
+
lines.push(`let ${slot2} = min(${a2}, ${b});`);
|
|
7878
|
+
break;
|
|
7879
|
+
case Op.Max:
|
|
7880
|
+
lines.push(`let ${slot2} = max(${a2}, ${b});`);
|
|
7881
|
+
break;
|
|
7882
|
+
default:
|
|
7883
|
+
throw new Error(`Unknown SdfProgram opcode ${opcodes[i]} at instruction ${i}.`);
|
|
7884
|
+
}
|
|
7322
7885
|
}
|
|
7886
|
+
lines.push(`return v${output};`);
|
|
7887
|
+
return new Function("Math", `return function sdfProgramEval(x, y, z) {
|
|
7888
|
+
${lines.join("\n")}
|
|
7889
|
+
};`)(Math);
|
|
7890
|
+
}
|
|
7891
|
+
function numberLiteral(value) {
|
|
7892
|
+
if (Number.isNaN(value)) return "NaN";
|
|
7893
|
+
if (value === Number.POSITIVE_INFINITY) return "Infinity";
|
|
7894
|
+
if (value === Number.NEGATIVE_INFINITY) return "-Infinity";
|
|
7895
|
+
return String(value);
|
|
7896
|
+
}
|
|
7897
|
+
function clampSlot(b, v, lo, hi) {
|
|
7898
|
+
return b.min(b.max(v, b.constant(lo)), b.constant(hi));
|
|
7899
|
+
}
|
|
7900
|
+
function length2(b, x2, y2) {
|
|
7901
|
+
return b.sqrt(b.add(b.mul(x2, x2), b.mul(y2, y2)));
|
|
7902
|
+
}
|
|
7903
|
+
function length3(b, x2, y2, z2) {
|
|
7904
|
+
return b.sqrt(b.add(b.add(b.mul(x2, x2), b.mul(y2, y2)), b.mul(z2, z2)));
|
|
7905
|
+
}
|
|
7906
|
+
function smin(b, a2, child, k2) {
|
|
7907
|
+
if (k2 <= 0) return b.min(a2, child);
|
|
7908
|
+
const h = b.div(b.max(b.sub(b.constant(k2), b.abs(b.sub(a2, child))), b.constant(0)), b.constant(k2));
|
|
7909
|
+
return b.sub(b.min(a2, child), b.mul(b.mul(b.mul(h, h), h), b.constant(k2 / 6)));
|
|
7910
|
+
}
|
|
7911
|
+
function smax(b, a2, child, k2) {
|
|
7912
|
+
return b.neg(smin(b, b.neg(a2), b.neg(child), k2));
|
|
7913
|
+
}
|
|
7914
|
+
function emitScaledTrig(b, x2, y2, z2, cellSize) {
|
|
7915
|
+
const s = b.constant(TAU / cellSize);
|
|
7916
|
+
const xs = b.mul(x2, s);
|
|
7917
|
+
const ys = b.mul(y2, s);
|
|
7918
|
+
const zs = b.mul(z2, s);
|
|
7323
7919
|
return {
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7920
|
+
scale: s,
|
|
7921
|
+
sx: b.sin(xs),
|
|
7922
|
+
sy: b.sin(ys),
|
|
7923
|
+
sz: b.sin(zs),
|
|
7924
|
+
cx: b.cos(xs),
|
|
7925
|
+
cy: b.cos(ys),
|
|
7926
|
+
cz: b.cos(zs),
|
|
7927
|
+
sx2: b.sin(b.mul(b.constant(2), xs)),
|
|
7928
|
+
sy2: b.sin(b.mul(b.constant(2), ys)),
|
|
7929
|
+
sz2: b.sin(b.mul(b.constant(2), zs)),
|
|
7930
|
+
cx2: b.cos(b.mul(b.constant(2), xs)),
|
|
7931
|
+
cy2: b.cos(b.mul(b.constant(2), ys)),
|
|
7932
|
+
cz2: b.cos(b.mul(b.constant(2), zs))
|
|
7335
7933
|
};
|
|
7336
7934
|
}
|
|
7337
|
-
function
|
|
7338
|
-
|
|
7935
|
+
function emitGyroidValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7936
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
7937
|
+
return {
|
|
7938
|
+
value: b.add(b.add(b.mul(t.sx, t.cy), b.mul(t.sy, t.cz)), b.mul(t.sz, t.cx)),
|
|
7939
|
+
gx: b.mul(t.scale, b.sub(b.mul(t.cx, t.cy), b.mul(t.sz, t.sx))),
|
|
7940
|
+
gy: b.mul(t.scale, b.add(b.neg(b.mul(t.sx, t.sy)), b.mul(t.cy, t.cz))),
|
|
7941
|
+
gz: b.mul(t.scale, b.add(b.neg(b.mul(t.sy, t.sz)), b.mul(t.cz, t.cx)))
|
|
7942
|
+
};
|
|
7339
7943
|
}
|
|
7340
|
-
function
|
|
7341
|
-
const
|
|
7342
|
-
const estimatedSamples = grid[0] * grid[1] * grid[2];
|
|
7944
|
+
function emitSchwarzPValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7945
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
7343
7946
|
return {
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
estimatedSamples,
|
|
7349
|
-
estimatedMemoryBytes: estimatedSamples * 8,
|
|
7350
|
-
treeRiskFlags: [...settings.treeRiskFlags],
|
|
7351
|
-
simplification: settings.simplify,
|
|
7352
|
-
capMode: "box",
|
|
7353
|
-
capInset: settings.edgeLength,
|
|
7354
|
-
warnings: [...settings.warnings]
|
|
7947
|
+
value: b.add(b.add(t.cx, t.cy), t.cz),
|
|
7948
|
+
gx: b.neg(b.mul(t.scale, t.sx)),
|
|
7949
|
+
gy: b.neg(b.mul(t.scale, t.sy)),
|
|
7950
|
+
gz: b.neg(b.mul(t.scale, t.sz))
|
|
7355
7951
|
};
|
|
7356
7952
|
}
|
|
7357
|
-
function
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
)
|
|
7953
|
+
function emitDiamondValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7954
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
7955
|
+
return {
|
|
7956
|
+
value: b.add(
|
|
7957
|
+
b.add(b.mul(b.mul(t.sx, t.sy), t.sz), b.mul(b.mul(t.sx, t.cy), t.cz)),
|
|
7958
|
+
b.add(b.mul(b.mul(t.cx, t.sy), t.cz), b.mul(b.mul(t.cx, t.cy), t.sz))
|
|
7959
|
+
),
|
|
7960
|
+
gx: b.mul(
|
|
7961
|
+
t.scale,
|
|
7962
|
+
b.add(
|
|
7963
|
+
b.add(b.mul(b.mul(t.cx, t.sy), t.sz), b.mul(b.mul(t.cx, t.cy), t.cz)),
|
|
7964
|
+
b.add(b.neg(b.mul(b.mul(t.sx, t.sy), t.cz)), b.neg(b.mul(b.mul(t.sx, t.cy), t.sz)))
|
|
7965
|
+
)
|
|
7966
|
+
),
|
|
7967
|
+
gy: b.mul(
|
|
7968
|
+
t.scale,
|
|
7969
|
+
b.add(
|
|
7970
|
+
b.add(b.mul(b.mul(t.sx, t.cy), t.sz), b.neg(b.mul(b.mul(t.sx, t.sy), t.cz))),
|
|
7971
|
+
b.add(b.mul(b.mul(t.cx, t.cy), t.cz), b.neg(b.mul(b.mul(t.cx, t.sy), t.sz)))
|
|
7972
|
+
)
|
|
7973
|
+
),
|
|
7974
|
+
gz: b.mul(
|
|
7975
|
+
t.scale,
|
|
7976
|
+
b.add(
|
|
7977
|
+
b.add(b.mul(b.mul(t.sx, t.sy), t.cz), b.neg(b.mul(b.mul(t.sx, t.cy), t.sz))),
|
|
7978
|
+
b.add(b.neg(b.mul(b.mul(t.cx, t.sy), t.sz)), b.mul(b.mul(t.cx, t.cy), t.cz))
|
|
7979
|
+
)
|
|
7980
|
+
)
|
|
7981
|
+
};
|
|
7982
|
+
}
|
|
7983
|
+
function emitLidinoidValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7984
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
7985
|
+
const value = b.add(
|
|
7986
|
+
b.sub(
|
|
7987
|
+
b.add(b.add(b.mul(b.mul(t.sx2, t.cy), t.sz), b.mul(b.mul(t.sy2, t.cz), t.sx)), b.mul(b.mul(t.sz2, t.cx), t.sy)),
|
|
7988
|
+
b.add(b.add(b.mul(t.cx2, t.cy2), b.mul(t.cy2, t.cz2)), b.mul(t.cz2, t.cx2))
|
|
7989
|
+
),
|
|
7990
|
+
b.constant(0.3)
|
|
7364
7991
|
);
|
|
7992
|
+
return {
|
|
7993
|
+
value,
|
|
7994
|
+
gx: b.mul(
|
|
7995
|
+
t.scale,
|
|
7996
|
+
b.add(
|
|
7997
|
+
b.add(
|
|
7998
|
+
b.add(b.mul(b.mul(b.constant(2), t.cx2), b.mul(t.cy, t.sz)), b.mul(b.mul(t.sy2, t.cz), t.cx)),
|
|
7999
|
+
b.neg(b.mul(b.mul(t.sz2, t.sx), t.sy))
|
|
8000
|
+
),
|
|
8001
|
+
b.add(b.mul(b.mul(b.constant(2), t.sx2), t.cy2), b.mul(b.mul(b.constant(2), t.cz2), t.sx2))
|
|
8002
|
+
)
|
|
8003
|
+
),
|
|
8004
|
+
gy: b.mul(
|
|
8005
|
+
t.scale,
|
|
8006
|
+
b.add(
|
|
8007
|
+
b.add(
|
|
8008
|
+
b.add(b.neg(b.mul(b.mul(t.sx2, t.sy), t.sz)), b.mul(b.mul(b.constant(2), t.cy2), b.mul(t.cz, t.sx))),
|
|
8009
|
+
b.mul(b.mul(t.sz2, t.cx), t.cy)
|
|
8010
|
+
),
|
|
8011
|
+
b.add(b.mul(b.mul(b.constant(2), t.cx2), t.sy2), b.mul(b.mul(b.constant(2), t.sy2), t.cz2))
|
|
8012
|
+
)
|
|
8013
|
+
),
|
|
8014
|
+
gz: b.mul(
|
|
8015
|
+
t.scale,
|
|
8016
|
+
b.add(
|
|
8017
|
+
b.add(
|
|
8018
|
+
b.add(b.mul(b.mul(t.sx2, t.cy), t.cz), b.neg(b.mul(b.mul(t.sy2, t.sz), t.sx))),
|
|
8019
|
+
b.mul(b.mul(b.constant(2), t.cz2), b.mul(t.cx, t.sy))
|
|
8020
|
+
),
|
|
8021
|
+
b.add(b.mul(b.mul(b.constant(2), t.cy2), t.sz2), b.mul(b.mul(b.constant(2), t.sz2), t.cx2))
|
|
8022
|
+
)
|
|
8023
|
+
)
|
|
8024
|
+
};
|
|
7365
8025
|
}
|
|
7366
|
-
function
|
|
7367
|
-
|
|
7368
|
-
const
|
|
7369
|
-
|
|
7370
|
-
return [
|
|
7371
|
-
Math.max(2, Math.ceil(dx / edgeLength2) + 1),
|
|
7372
|
-
Math.max(2, Math.ceil(dy / edgeLength2) + 1),
|
|
7373
|
-
Math.max(2, Math.ceil(dz / edgeLength2) + 1)
|
|
7374
|
-
];
|
|
8026
|
+
function emitTpmsDistance(b, { value, gx, gy, gz }, thickness, thicknessMode) {
|
|
8027
|
+
if (thicknessMode !== "metric-approx") return b.sub(b.abs(value), b.constant(thickness));
|
|
8028
|
+
const grad = length3(b, gx, gy, gz);
|
|
8029
|
+
return b.sub(b.div(b.abs(value), b.max(grad, b.constant(GRAD_EPS))), b.constant(thickness * 0.5));
|
|
7375
8030
|
}
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
8031
|
+
const { cos, max, sin, sqrt } = Math;
|
|
8032
|
+
function emitSdfProgramNode(b, node, x2, y2, z2) {
|
|
8033
|
+
switch (node.kind) {
|
|
8034
|
+
case "sdf:sphere":
|
|
8035
|
+
return sdSphere(b, x2, y2, z2, node.radius);
|
|
8036
|
+
case "sdf:box": {
|
|
8037
|
+
const [hx, hy, hz] = node.halfExtents;
|
|
8038
|
+
return sdBox(b, x2, y2, z2, hx, hy, hz);
|
|
8039
|
+
}
|
|
8040
|
+
case "sdf:cylinder":
|
|
8041
|
+
return sdCylinder(b, x2, y2, z2, node.height, node.radius);
|
|
8042
|
+
case "sdf:torus":
|
|
8043
|
+
return sdTorus(b, x2, y2, z2, node.majorRadius, node.minorRadius);
|
|
8044
|
+
case "sdf:capsule":
|
|
8045
|
+
return sdCapsule(b, x2, y2, z2, node.height, node.radius);
|
|
8046
|
+
case "sdf:cone":
|
|
8047
|
+
return sdCone(b, x2, y2, z2, node.height, node.radius);
|
|
8048
|
+
case "sdf:polylineSweep":
|
|
8049
|
+
return sdPolylineSweep(b, node, x2, y2, z2);
|
|
8050
|
+
case "sdf:union":
|
|
8051
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => b.min(a2, child));
|
|
8052
|
+
case "sdf:difference":
|
|
8053
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => b.max(a2, b.neg(child)));
|
|
8054
|
+
case "sdf:intersection":
|
|
8055
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => b.max(a2, child));
|
|
8056
|
+
case "sdf:smoothUnion":
|
|
8057
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => smin(b, a2, child, node.radius));
|
|
8058
|
+
case "sdf:smoothDifference":
|
|
8059
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => smax(b, a2, b.neg(child), node.radius));
|
|
8060
|
+
case "sdf:smoothIntersection":
|
|
8061
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => smax(b, a2, child, node.radius));
|
|
8062
|
+
case "sdf:morph": {
|
|
8063
|
+
const a2 = emitSdfProgramNode(b, node.a, x2, y2, z2);
|
|
8064
|
+
const childB = emitSdfProgramNode(b, node.b, x2, y2, z2);
|
|
8065
|
+
return b.add(b.mul(a2, b.constant(1 - node.t)), b.mul(childB, b.constant(node.t)));
|
|
8066
|
+
}
|
|
8067
|
+
case "sdf:translate": {
|
|
8068
|
+
const [ox, oy, oz] = node.offset;
|
|
8069
|
+
return emitSdfProgramNode(b, node.child, b.sub(x2, b.constant(ox)), b.sub(y2, b.constant(oy)), b.sub(z2, b.constant(oz)));
|
|
8070
|
+
}
|
|
8071
|
+
case "sdf:rotate":
|
|
8072
|
+
return emitRotated(b, node, x2, y2, z2);
|
|
8073
|
+
case "sdf:scale": {
|
|
8074
|
+
const inv = 1 / node.factor;
|
|
8075
|
+
const child = emitSdfProgramNode(b, node.child, b.mul(x2, b.constant(inv)), b.mul(y2, b.constant(inv)), b.mul(z2, b.constant(inv)));
|
|
8076
|
+
return b.mul(child, b.constant(node.factor));
|
|
8077
|
+
}
|
|
8078
|
+
case "sdf:twist": {
|
|
8079
|
+
const angle = b.mul(b.constant(node.degreesPerUnit * DEG$1), z2);
|
|
8080
|
+
const c2 = b.cos(angle);
|
|
8081
|
+
const s = b.sin(angle);
|
|
8082
|
+
return emitSdfProgramNode(b, node.child, b.sub(b.mul(c2, x2), b.mul(s, y2)), b.add(b.mul(s, x2), b.mul(c2, y2)), z2);
|
|
8083
|
+
}
|
|
8084
|
+
case "sdf:bend": {
|
|
8085
|
+
const angle = b.div(x2, b.constant(node.radius));
|
|
8086
|
+
const c2 = b.cos(angle);
|
|
8087
|
+
const s = b.sin(angle);
|
|
8088
|
+
const radiusPlusY = b.add(b.constant(node.radius), y2);
|
|
8089
|
+
return emitSdfProgramNode(b, node.child, b.mul(radiusPlusY, s), b.sub(b.mul(radiusPlusY, c2), b.constant(node.radius)), z2);
|
|
8090
|
+
}
|
|
8091
|
+
case "sdf:repeat": {
|
|
8092
|
+
const [sx, sy, sz] = node.spacing;
|
|
8093
|
+
const [cx, cy, cz] = node.count;
|
|
8094
|
+
return emitSdfProgramNode(b, node.child, repeatCoord(b, x2, sx, cx), repeatCoord(b, y2, sy, cy), repeatCoord(b, z2, sz, cz));
|
|
8095
|
+
}
|
|
8096
|
+
case "sdf:shell": {
|
|
8097
|
+
const child = emitSdfProgramNode(b, node.child, x2, y2, z2);
|
|
8098
|
+
return b.sub(b.abs(child), b.constant(node.thickness * 0.5));
|
|
8099
|
+
}
|
|
8100
|
+
case "sdf:onion": {
|
|
8101
|
+
let d2 = emitSdfProgramNode(b, node.child, x2, y2, z2);
|
|
8102
|
+
for (let i = 0; i < node.layers; i++) d2 = b.sub(b.abs(d2), b.constant(node.thickness));
|
|
8103
|
+
return d2;
|
|
8104
|
+
}
|
|
8105
|
+
case "sdf:gyroid":
|
|
8106
|
+
return emitTpmsDistance(b, emitGyroidValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
8107
|
+
case "sdf:schwarzP":
|
|
8108
|
+
return emitTpmsDistance(b, emitSchwarzPValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
8109
|
+
case "sdf:diamond":
|
|
8110
|
+
return emitTpmsDistance(b, emitDiamondValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
8111
|
+
case "sdf:lidinoid":
|
|
8112
|
+
return emitTpmsDistance(b, emitLidinoidValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
8113
|
+
default:
|
|
8114
|
+
throw new UnsupportedSdfProgramNodeError(`SdfProgram does not support node kind ${node.kind} yet.`);
|
|
8115
|
+
}
|
|
7381
8116
|
}
|
|
7382
|
-
function
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
const maxDim = Math.max(dx, dy, dz, minEdgeLength);
|
|
7387
|
-
const divisor = quality === "draft" ? 60 : quality === "export" ? 160 : 100;
|
|
7388
|
-
const candidates = [maxDim / divisor];
|
|
7389
|
-
if (options.tolerance !== void 0) candidates.push(requirePositiveFinite$2(options.tolerance, "SDF tolerance") * 2);
|
|
7390
|
-
if (options.minFeatureSize !== void 0) candidates.push(requirePositiveFinite$2(options.minFeatureSize, "SDF minFeatureSize") / 2.5);
|
|
7391
|
-
if (analysis.minMetricTpmsThickness < Infinity) candidates.push(analysis.minMetricTpmsThickness / 2);
|
|
7392
|
-
if (analysis.minTpmsCellSize < Infinity) candidates.push(analysis.minTpmsCellSize / 10);
|
|
7393
|
-
if (analysis.minRepeatSpacing < Infinity) candidates.push(analysis.minRepeatSpacing / 8);
|
|
7394
|
-
if (analysis.minWallThickness < Infinity) candidates.push(analysis.minWallThickness / 2.5);
|
|
7395
|
-
return Math.max(minEdgeLength, Math.min(...candidates.filter((v) => Number.isFinite(v) && v > 0)));
|
|
8117
|
+
function foldChildren(b, children, x2, y2, z2, combine2) {
|
|
8118
|
+
let d2 = emitSdfProgramNode(b, children[0], x2, y2, z2);
|
|
8119
|
+
for (let i = 1; i < children.length; i++) d2 = combine2(d2, emitSdfProgramNode(b, children[i], x2, y2, z2));
|
|
8120
|
+
return d2;
|
|
7396
8121
|
}
|
|
7397
|
-
function
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
8122
|
+
function emitRotated(b, node, x2, y2, z2) {
|
|
8123
|
+
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$1);
|
|
8124
|
+
const cx = cos(rx);
|
|
8125
|
+
const sx = sin(rx);
|
|
8126
|
+
const cy = cos(ry);
|
|
8127
|
+
const sy = sin(ry);
|
|
8128
|
+
const cz = cos(rz);
|
|
8129
|
+
const sz = sin(rz);
|
|
8130
|
+
const x1 = b.add(b.mul(b.constant(cz), x2), b.mul(b.constant(sz), y2));
|
|
8131
|
+
const y1 = b.sub(b.mul(b.constant(cz), y2), b.mul(b.constant(sz), x2));
|
|
8132
|
+
const x22 = b.sub(b.mul(b.constant(cy), x1), b.mul(b.constant(sy), z2));
|
|
8133
|
+
const z22 = b.add(b.mul(b.constant(sy), x1), b.mul(b.constant(cy), z2));
|
|
8134
|
+
const y22 = b.add(b.mul(b.constant(cx), y1), b.mul(b.constant(sx), z22));
|
|
8135
|
+
const z3 = b.sub(b.mul(b.constant(cx), z22), b.mul(b.constant(sx), y1));
|
|
8136
|
+
return emitSdfProgramNode(b, node.child, x22, y22, z3);
|
|
8137
|
+
}
|
|
8138
|
+
function sdSphere(b, x2, y2, z2, r) {
|
|
8139
|
+
return b.sub(length3(b, x2, y2, z2), b.constant(r));
|
|
8140
|
+
}
|
|
8141
|
+
function sdBox(b, x2, y2, z2, hx, hy, hz) {
|
|
8142
|
+
const dx = b.sub(b.abs(x2), b.constant(hx));
|
|
8143
|
+
const dy = b.sub(b.abs(y2), b.constant(hy));
|
|
8144
|
+
const dz = b.sub(b.abs(z2), b.constant(hz));
|
|
8145
|
+
const outside = length3(b, b.max(dx, b.constant(0)), b.max(dy, b.constant(0)), b.max(dz, b.constant(0)));
|
|
8146
|
+
const inside = b.min(b.max(b.max(dx, dy), dz), b.constant(0));
|
|
8147
|
+
return b.add(outside, inside);
|
|
8148
|
+
}
|
|
8149
|
+
function sdCylinder(b, x2, y2, z2, h, r) {
|
|
8150
|
+
const dx = b.sub(length2(b, x2, y2), b.constant(r));
|
|
8151
|
+
const dz = b.sub(b.abs(z2), b.constant(h * 0.5));
|
|
8152
|
+
return b.add(length2(b, b.max(dx, b.constant(0)), b.max(dz, b.constant(0))), b.min(b.max(dx, dz), b.constant(0)));
|
|
8153
|
+
}
|
|
8154
|
+
function sdTorus(b, x2, y2, z2, majorRadius, minorRadius) {
|
|
8155
|
+
return b.sub(length2(b, b.sub(length2(b, x2, y2), b.constant(majorRadius)), z2), b.constant(minorRadius));
|
|
8156
|
+
}
|
|
8157
|
+
function sdCapsule(b, x2, y2, z2, h, r) {
|
|
8158
|
+
const cz = clampSlot(b, z2, -h * 0.5, h * 0.5);
|
|
8159
|
+
return b.sub(length3(b, x2, y2, b.sub(z2, cz)), b.constant(r));
|
|
8160
|
+
}
|
|
8161
|
+
function sdCone(b, x2, y2, z2, h, r) {
|
|
8162
|
+
const q = length2(b, x2, y2);
|
|
8163
|
+
const cLen = sqrt(h * h + r * r);
|
|
8164
|
+
const side = b.add(b.mul(b.constant(h / cLen), q), b.mul(b.constant(-r / cLen), b.sub(z2, b.constant(h))));
|
|
8165
|
+
return b.max(b.max(side, b.neg(z2)), b.sub(z2, b.constant(h)));
|
|
8166
|
+
}
|
|
8167
|
+
function sdPolylineSweep(b, node, x2, y2, z2) {
|
|
8168
|
+
let d2 = sdTaperedSegment(b, x2, y2, z2, node.points[0], node.points[1], node.radii[0], node.radii[1]);
|
|
8169
|
+
for (let i = 1; i < node.points.length - 1; i++) {
|
|
8170
|
+
const segment = sdTaperedSegment(b, x2, y2, z2, node.points[i], node.points[i + 1], node.radii[i], node.radii[i + 1]);
|
|
8171
|
+
d2 = smin(b, d2, segment, node.blend);
|
|
8172
|
+
}
|
|
8173
|
+
return d2;
|
|
7402
8174
|
}
|
|
7403
|
-
function
|
|
7404
|
-
const
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
8175
|
+
function sdTaperedSegment(b, x2, y2, z2, a2, end, ra, rb) {
|
|
8176
|
+
const vx = end[0] - a2[0];
|
|
8177
|
+
const vy = end[1] - a2[1];
|
|
8178
|
+
const vz = end[2] - a2[2];
|
|
8179
|
+
const len2 = vx * vx + vy * vy + vz * vz;
|
|
8180
|
+
if (len2 <= 1e-12) return sdSphere(b, b.sub(x2, b.constant(a2[0])), b.sub(y2, b.constant(a2[1])), b.sub(z2, b.constant(a2[2])), max(ra, rb));
|
|
8181
|
+
const h = clampSlot(
|
|
8182
|
+
b,
|
|
8183
|
+
b.div(
|
|
8184
|
+
b.add(
|
|
8185
|
+
b.add(b.mul(b.sub(x2, b.constant(a2[0])), b.constant(vx)), b.mul(b.sub(y2, b.constant(a2[1])), b.constant(vy))),
|
|
8186
|
+
b.mul(b.sub(z2, b.constant(a2[2])), b.constant(vz))
|
|
8187
|
+
),
|
|
8188
|
+
b.constant(len2)
|
|
8189
|
+
),
|
|
8190
|
+
0,
|
|
8191
|
+
1
|
|
8192
|
+
);
|
|
8193
|
+
const sx = b.sub(x2, b.add(b.constant(a2[0]), b.mul(b.constant(vx), h)));
|
|
8194
|
+
const sy = b.sub(y2, b.add(b.constant(a2[1]), b.mul(b.constant(vy), h)));
|
|
8195
|
+
const sz = b.sub(z2, b.add(b.constant(a2[2]), b.mul(b.constant(vz), h)));
|
|
8196
|
+
const radius = b.add(b.constant(ra), b.mul(b.constant(rb - ra), h));
|
|
8197
|
+
return b.sub(length3(b, sx, sy, sz), radius);
|
|
7415
8198
|
}
|
|
7416
|
-
function
|
|
8199
|
+
function repeatCoord(b, v, spacing, count) {
|
|
8200
|
+
if (spacing <= 0) return v;
|
|
8201
|
+
if (count > 0) {
|
|
8202
|
+
const center = (count - 1) * 0.5;
|
|
8203
|
+
const index2 = clampSlot(b, b.round(b.add(b.div(v, b.constant(spacing)), b.constant(center))), 0, count - 1);
|
|
8204
|
+
return b.sub(v, b.mul(b.sub(index2, b.constant(center)), b.constant(spacing)));
|
|
8205
|
+
}
|
|
8206
|
+
return b.sub(v, b.mul(b.constant(spacing), b.round(b.div(v, b.constant(spacing)))));
|
|
8207
|
+
}
|
|
8208
|
+
function getUnsupportedSdfProgramReason(node) {
|
|
7417
8209
|
switch (node.kind) {
|
|
8210
|
+
case "sdf:displace":
|
|
8211
|
+
return "displace uses a dynamic JavaScript function body";
|
|
8212
|
+
case "sdf:surfaceDisplace":
|
|
8213
|
+
return "surfaceDisplace uses dynamic UV/pattern evaluation";
|
|
8214
|
+
case "sdf:spatialBlend":
|
|
8215
|
+
return "spatialBlend uses a dynamic JavaScript blend function";
|
|
8216
|
+
case "sdf:noise":
|
|
8217
|
+
return "noise depends on table-based simplex evaluation";
|
|
8218
|
+
case "sdf:voronoi":
|
|
8219
|
+
return "voronoi depends on table-based Worley evaluation";
|
|
8220
|
+
case "sdf:custom":
|
|
8221
|
+
return "custom uses a dynamic JavaScript function body";
|
|
8222
|
+
case "sdf:polylineSweep":
|
|
8223
|
+
if (node.points.length < 2) return "polylineSweep needs at least two points";
|
|
8224
|
+
if (node.points.length !== node.radii.length) return "polylineSweep point/radius counts differ";
|
|
8225
|
+
return void 0;
|
|
7418
8226
|
case "sdf:union":
|
|
7419
8227
|
case "sdf:difference":
|
|
7420
8228
|
case "sdf:intersection":
|
|
7421
8229
|
case "sdf:smoothUnion":
|
|
7422
8230
|
case "sdf:smoothDifference":
|
|
7423
8231
|
case "sdf:smoothIntersection":
|
|
7424
|
-
for (const child of node.children)
|
|
7425
|
-
|
|
8232
|
+
for (const child of node.children) {
|
|
8233
|
+
const reason = getUnsupportedSdfProgramReason(child);
|
|
8234
|
+
if (reason) return reason;
|
|
8235
|
+
}
|
|
8236
|
+
return void 0;
|
|
7426
8237
|
case "sdf:morph":
|
|
7427
|
-
|
|
7428
|
-
visitSdfNode(node.a, analysis);
|
|
7429
|
-
visitSdfNode(node.b, analysis);
|
|
7430
|
-
break;
|
|
8238
|
+
return getUnsupportedSdfProgramReason(node.a) ?? getUnsupportedSdfProgramReason(node.b);
|
|
7431
8239
|
case "sdf:translate":
|
|
7432
8240
|
case "sdf:rotate":
|
|
7433
8241
|
case "sdf:scale":
|
|
7434
8242
|
case "sdf:twist":
|
|
7435
8243
|
case "sdf:bend":
|
|
7436
|
-
case "sdf:onion":
|
|
7437
|
-
visitSdfNode(node.child, analysis);
|
|
7438
|
-
break;
|
|
7439
8244
|
case "sdf:repeat":
|
|
7440
|
-
analysis.riskFlags.add("repeat");
|
|
7441
|
-
for (let i = 0; i < 3; i++) {
|
|
7442
|
-
const spacing = node.spacing[i];
|
|
7443
|
-
if (spacing > 0) {
|
|
7444
|
-
analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, spacing);
|
|
7445
|
-
if (node.count[i] <= 0) analysis.hasInfiniteRepeat = true;
|
|
7446
|
-
}
|
|
7447
|
-
}
|
|
7448
|
-
visitSdfNode(node.child, analysis);
|
|
7449
|
-
break;
|
|
7450
8245
|
case "sdf:shell":
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
case "sdf:surfaceDisplace":
|
|
7456
|
-
analysis.riskFlags.add("displacement");
|
|
7457
|
-
visitSdfNode(node.child, analysis);
|
|
7458
|
-
break;
|
|
7459
|
-
case "sdf:gyroid":
|
|
7460
|
-
case "sdf:schwarzP":
|
|
7461
|
-
case "sdf:diamond":
|
|
7462
|
-
case "sdf:lidinoid":
|
|
7463
|
-
analysis.riskFlags.add("tpms");
|
|
7464
|
-
analysis.minTpmsCellSize = Math.min(analysis.minTpmsCellSize, node.cellSize);
|
|
7465
|
-
if (node.thicknessMode === "metric-approx") {
|
|
7466
|
-
analysis.minMetricTpmsThickness = Math.min(analysis.minMetricTpmsThickness, node.thickness);
|
|
7467
|
-
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.thickness);
|
|
7468
|
-
} else {
|
|
7469
|
-
analysis.hasLegacyTpmsThreshold = true;
|
|
7470
|
-
}
|
|
7471
|
-
break;
|
|
7472
|
-
case "sdf:noise":
|
|
7473
|
-
analysis.riskFlags.add("noise");
|
|
7474
|
-
break;
|
|
7475
|
-
case "sdf:voronoi":
|
|
7476
|
-
analysis.riskFlags.add("voronoi");
|
|
7477
|
-
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.wallThickness);
|
|
7478
|
-
if (node.surfaceChild) visitSdfNode(node.surfaceChild, analysis);
|
|
7479
|
-
break;
|
|
7480
|
-
case "sdf:custom":
|
|
7481
|
-
analysis.riskFlags.add("custom");
|
|
7482
|
-
break;
|
|
8246
|
+
case "sdf:onion":
|
|
8247
|
+
return getUnsupportedSdfProgramReason(node.child);
|
|
8248
|
+
default:
|
|
8249
|
+
return void 0;
|
|
7483
8250
|
}
|
|
7484
8251
|
}
|
|
7485
|
-
function
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
}
|
|
7489
|
-
function requirePositiveFinite$2(value, name) {
|
|
7490
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
7491
|
-
throw new Error(`${name} must be a positive finite number.`);
|
|
8252
|
+
function compileSdfProgram(node) {
|
|
8253
|
+
const unsupportedReason = getUnsupportedSdfProgramReason(node);
|
|
8254
|
+
if (unsupportedReason) {
|
|
8255
|
+
throw new UnsupportedSdfProgramNodeError(`SdfProgram does not support this tree yet: ${unsupportedReason}.`);
|
|
7492
8256
|
}
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
function cloneBounds$2(bounds) {
|
|
7496
|
-
return { min: [...bounds.min], max: [...bounds.max] };
|
|
7497
|
-
}
|
|
7498
|
-
function suggestEdgeLengthForSampleBudget(bounds, maxGridPoints) {
|
|
7499
|
-
const dx = bounds.max[0] - bounds.min[0];
|
|
7500
|
-
const dy = bounds.max[1] - bounds.min[1];
|
|
7501
|
-
const dz = bounds.max[2] - bounds.min[2];
|
|
7502
|
-
const volume = Math.max(dx * dy * dz, 1);
|
|
7503
|
-
return Math.cbrt(volume / Math.max(maxGridPoints, 8));
|
|
8257
|
+
const builder = new SdfProgramBuilder();
|
|
8258
|
+
return builder.finalize(emitSdfProgramNode(builder, node, builder.x, builder.y, builder.z));
|
|
7504
8259
|
}
|
|
7505
|
-
function
|
|
7506
|
-
return
|
|
7507
|
-
}
|
|
7508
|
-
function formatMm(value) {
|
|
7509
|
-
return `${formatNumber(value)}mm`;
|
|
7510
|
-
}
|
|
7511
|
-
function formatNumber(value) {
|
|
7512
|
-
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7513
|
-
}
|
|
7514
|
-
function formatCount(value) {
|
|
7515
|
-
return Math.round(value).toLocaleString("en-US");
|
|
8260
|
+
function compileSdfProgram3(node) {
|
|
8261
|
+
return compileSdfProgramEvaluator3(compileSdfProgram(node));
|
|
7516
8262
|
}
|
|
7517
|
-
function
|
|
7518
|
-
|
|
7519
|
-
|
|
8263
|
+
function compileSdfMaterializationEvaluator3(node) {
|
|
8264
|
+
const unsupportedReason = getUnsupportedSdfProgramReason(node);
|
|
8265
|
+
if (unsupportedReason) {
|
|
8266
|
+
return {
|
|
8267
|
+
fn: compileSdfNode3(node),
|
|
8268
|
+
engine: "closure",
|
|
8269
|
+
unsupportedReason
|
|
8270
|
+
};
|
|
8271
|
+
}
|
|
8272
|
+
return {
|
|
8273
|
+
fn: compileSdfProgram3(node),
|
|
8274
|
+
engine: "program"
|
|
8275
|
+
};
|
|
7520
8276
|
}
|
|
7521
8277
|
function midpoint$3(a2, b) {
|
|
7522
8278
|
return [(a2[0] + b[0]) / 2, (a2[1] + b[1]) / 2, (a2[2] + b[2]) / 2];
|
|
@@ -7530,7 +8286,7 @@ function scale$6(v, s) {
|
|
|
7530
8286
|
function sub$7(a2, b) {
|
|
7531
8287
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
7532
8288
|
}
|
|
7533
|
-
function cross$
|
|
8289
|
+
function cross$8(a2, b) {
|
|
7534
8290
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
7535
8291
|
}
|
|
7536
8292
|
function makeEdge(name, start, end, faceName, curve) {
|
|
@@ -7566,7 +8322,7 @@ function buildSurfaceSheetTopology(boundaries, options = {}) {
|
|
|
7566
8322
|
const center = options.center ?? average$1(corners);
|
|
7567
8323
|
const uAxis = normalizeAxis$1(sub$7(midpoint$3(u1Start, u1End), midpoint$3(u0Start, u0End)));
|
|
7568
8324
|
const vAxis = normalizeAxis$1(sub$7(midpoint$3(v1Start, v1End), midpoint$3(v0Start, v0End)));
|
|
7569
|
-
const normal = normalizeAxis$1(options.normal ?? cross$
|
|
8325
|
+
const normal = normalizeAxis$1(options.normal ?? cross$8(uAxis, vAxis));
|
|
7570
8326
|
const faces = /* @__PURE__ */ new Map();
|
|
7571
8327
|
faces.set(faceName, {
|
|
7572
8328
|
name: faceName,
|
|
@@ -7982,7 +8738,7 @@ function buildCircleExtrusionTopology(circ, height, center = false) {
|
|
|
7982
8738
|
);
|
|
7983
8739
|
return { faces, edges };
|
|
7984
8740
|
}
|
|
7985
|
-
function requireFinite$
|
|
8741
|
+
function requireFinite$9(v, label) {
|
|
7986
8742
|
if (!Number.isFinite(v)) throw new Error(`nurbsSurface: ${label} must be finite, got ${v}`);
|
|
7987
8743
|
}
|
|
7988
8744
|
function normalizeSurfaceTessellation(tessellation) {
|
|
@@ -7992,11 +8748,11 @@ function normalizeSurfaceTessellation(tessellation) {
|
|
|
7992
8748
|
throw new Error(`nurbsSurface: tessellation.mode must be "uniform" or "adaptive", got ${mode}`);
|
|
7993
8749
|
}
|
|
7994
8750
|
if (tessellation.tolerance !== void 0) {
|
|
7995
|
-
requireFinite$
|
|
8751
|
+
requireFinite$9(tessellation.tolerance, "tessellation.tolerance");
|
|
7996
8752
|
if (tessellation.tolerance <= 0) throw new Error("nurbsSurface: tessellation.tolerance must be > 0");
|
|
7997
8753
|
}
|
|
7998
|
-
if (tessellation.minResolution !== void 0) requireFinite$
|
|
7999
|
-
if (tessellation.maxResolution !== void 0) requireFinite$
|
|
8754
|
+
if (tessellation.minResolution !== void 0) requireFinite$9(tessellation.minResolution, "tessellation.minResolution");
|
|
8755
|
+
if (tessellation.maxResolution !== void 0) requireFinite$9(tessellation.maxResolution, "tessellation.maxResolution");
|
|
8000
8756
|
const minResolution = tessellation.minResolution === void 0 ? void 0 : Math.max(2, Math.round(tessellation.minResolution));
|
|
8001
8757
|
const maxResolution = tessellation.maxResolution === void 0 ? void 0 : Math.max(2, Math.round(tessellation.maxResolution));
|
|
8002
8758
|
if (minResolution !== void 0 && maxResolution !== void 0 && minResolution > maxResolution) {
|
|
@@ -8015,10 +8771,10 @@ function normalizeSurfaceDomain(domain) {
|
|
|
8015
8771
|
const uMax = domain.uMax ?? 1;
|
|
8016
8772
|
const vMin = domain.vMin ?? 0;
|
|
8017
8773
|
const vMax = domain.vMax ?? 1;
|
|
8018
|
-
requireFinite$
|
|
8019
|
-
requireFinite$
|
|
8020
|
-
requireFinite$
|
|
8021
|
-
requireFinite$
|
|
8774
|
+
requireFinite$9(uMin, "domain.uMin");
|
|
8775
|
+
requireFinite$9(uMax, "domain.uMax");
|
|
8776
|
+
requireFinite$9(vMin, "domain.vMin");
|
|
8777
|
+
requireFinite$9(vMax, "domain.vMax");
|
|
8022
8778
|
if (uMin < 0 || uMax > 1 || vMin < 0 || vMax > 1) {
|
|
8023
8779
|
throw new Error("nurbsSurface: domain bounds must stay within [0, 1]");
|
|
8024
8780
|
}
|
|
@@ -8030,8 +8786,8 @@ function normalizeSurfaceDomain(domain) {
|
|
|
8030
8786
|
function normalizeTrimLoop(loop, label) {
|
|
8031
8787
|
if (loop.length < 3) throw new Error(`nurbsSurface: ${label} requires at least 3 points`);
|
|
8032
8788
|
const normalized = loop.map(([u2, v], idx) => {
|
|
8033
|
-
requireFinite$
|
|
8034
|
-
requireFinite$
|
|
8789
|
+
requireFinite$9(u2, `${label}[${idx}][0]`);
|
|
8790
|
+
requireFinite$9(v, `${label}[${idx}][1]`);
|
|
8035
8791
|
if (u2 < 0 || u2 > 1 || v < 0 || v > 1) throw new Error(`nurbsSurface: ${label}[${idx}] must stay within [0, 1]`);
|
|
8036
8792
|
return [u2, v];
|
|
8037
8793
|
});
|
|
@@ -8054,8 +8810,8 @@ function normalizeTrimCurve(curve, label) {
|
|
|
8054
8810
|
throw new Error(`nurbsSurface: ${label} needs at least ${degree + 1} control points for degree=${degree}`);
|
|
8055
8811
|
}
|
|
8056
8812
|
const normalizedControlPoints = controlPoints.map(([u2, v], idx) => {
|
|
8057
|
-
requireFinite$
|
|
8058
|
-
requireFinite$
|
|
8813
|
+
requireFinite$9(u2, `${label}.controlPoints[${idx}][0]`);
|
|
8814
|
+
requireFinite$9(v, `${label}.controlPoints[${idx}][1]`);
|
|
8059
8815
|
if (u2 < 0 || u2 > 1 || v < 0 || v > 1) {
|
|
8060
8816
|
throw new Error(`nurbsSurface: ${label}.controlPoints[${idx}] must stay within [0, 1]`);
|
|
8061
8817
|
}
|
|
@@ -8066,7 +8822,7 @@ function normalizeTrimCurve(curve, label) {
|
|
|
8066
8822
|
throw new Error(`nurbsSurface: ${label}.weights length must match controlPoints length`);
|
|
8067
8823
|
}
|
|
8068
8824
|
for (let idx = 0; idx < weights.length; idx += 1) {
|
|
8069
|
-
requireFinite$
|
|
8825
|
+
requireFinite$9(weights[idx], `${label}.weights[${idx}]`);
|
|
8070
8826
|
if (weights[idx] <= 0) throw new Error(`nurbsSurface: ${label}.weights[${idx}] must be > 0`);
|
|
8071
8827
|
}
|
|
8072
8828
|
const knots = curve.knots ?? generateClampedKnots(controlPoints.length, degree);
|
|
@@ -8074,7 +8830,7 @@ function normalizeTrimCurve(curve, label) {
|
|
|
8074
8830
|
throw new Error(`nurbsSurface: ${label}.knots.length should be ${controlPoints.length + degree + 1}, got ${knots.length}`);
|
|
8075
8831
|
}
|
|
8076
8832
|
for (let idx = 0; idx < knots.length; idx += 1) {
|
|
8077
|
-
requireFinite$
|
|
8833
|
+
requireFinite$9(knots[idx], `${label}.knots[${idx}]`);
|
|
8078
8834
|
if (idx > 0 && knots[idx] < knots[idx - 1]) throw new Error(`nurbsSurface: ${label}.knots must be non-decreasing`);
|
|
8079
8835
|
}
|
|
8080
8836
|
if (knots[degree] >= knots[controlPoints.length]) {
|
|
@@ -8254,16 +9010,16 @@ class NurbsSurface {
|
|
|
8254
9010
|
for (let i = 0; i < nU; i++) {
|
|
8255
9011
|
if (controlGrid[i].length !== nV) throw new Error(`nurbsSurface: row ${i} has ${controlGrid[i].length} points, expected ${nV}`);
|
|
8256
9012
|
for (let j = 0; j < nV; j++) {
|
|
8257
|
-
requireFinite$
|
|
8258
|
-
requireFinite$
|
|
8259
|
-
requireFinite$
|
|
9013
|
+
requireFinite$9(controlGrid[i][j][0], `controlGrid[${i}][${j}][0]`);
|
|
9014
|
+
requireFinite$9(controlGrid[i][j][1], `controlGrid[${i}][${j}][1]`);
|
|
9015
|
+
requireFinite$9(controlGrid[i][j][2], `controlGrid[${i}][${j}][2]`);
|
|
8260
9016
|
}
|
|
8261
9017
|
}
|
|
8262
9018
|
const weightsGrid = options.weights ?? controlGrid.map((row) => row.map(() => 1));
|
|
8263
9019
|
for (let i = 0; i < nU; i++) {
|
|
8264
9020
|
if (weightsGrid[i].length !== nV) throw new Error(`nurbsSurface: weights row ${i} length mismatch`);
|
|
8265
9021
|
for (let j = 0; j < nV; j++) {
|
|
8266
|
-
requireFinite$
|
|
9022
|
+
requireFinite$9(weightsGrid[i][j], `weights[${i}][${j}]`);
|
|
8267
9023
|
if (weightsGrid[i][j] <= 0) throw new Error(`nurbsSurface: weights[${i}][${j}] must be > 0`);
|
|
8268
9024
|
}
|
|
8269
9025
|
}
|
|
@@ -9468,6 +10224,7 @@ function buildSweepLevelSetInput(profilePolygons, pathInput, options) {
|
|
|
9468
10224
|
edgeLength: options.edgeLength
|
|
9469
10225
|
};
|
|
9470
10226
|
}
|
|
10227
|
+
const EPS$9 = 1e-9;
|
|
9471
10228
|
function resamplePolygon(poly, targetCount) {
|
|
9472
10229
|
if (poly.length < 2) return poly;
|
|
9473
10230
|
if (targetCount <= 0) return [];
|
|
@@ -9505,6 +10262,78 @@ function resamplePolygon(poly, targetCount) {
|
|
|
9505
10262
|
}
|
|
9506
10263
|
return out;
|
|
9507
10264
|
}
|
|
10265
|
+
function resamplePolygonByAngle(poly, targetCount, center = polygonCentroid$2(poly)) {
|
|
10266
|
+
if (poly.length < 3 || targetCount <= 0) return null;
|
|
10267
|
+
if (!isConvexPolygon(poly)) return null;
|
|
10268
|
+
const out = [];
|
|
10269
|
+
for (let index2 = 0; index2 < targetCount; index2 += 1) {
|
|
10270
|
+
const angle = index2 / targetCount * Math.PI * 2;
|
|
10271
|
+
const point2 = rayPolygonIntersection(center, [Math.cos(angle), Math.sin(angle)], poly);
|
|
10272
|
+
if (!point2) return null;
|
|
10273
|
+
out.push(point2);
|
|
10274
|
+
}
|
|
10275
|
+
return out;
|
|
10276
|
+
}
|
|
10277
|
+
function rayPolygonIntersection(origin, direction2, poly) {
|
|
10278
|
+
let bestT = Infinity;
|
|
10279
|
+
let best = null;
|
|
10280
|
+
for (let index2 = 0; index2 < poly.length; index2 += 1) {
|
|
10281
|
+
const a2 = poly[index2];
|
|
10282
|
+
const b = poly[(index2 + 1) % poly.length];
|
|
10283
|
+
const edge = [b[0] - a2[0], b[1] - a2[1]];
|
|
10284
|
+
const denom = cross$7(direction2, edge);
|
|
10285
|
+
if (Math.abs(denom) < EPS$9) continue;
|
|
10286
|
+
const delta = [a2[0] - origin[0], a2[1] - origin[1]];
|
|
10287
|
+
const rayT = cross$7(delta, edge) / denom;
|
|
10288
|
+
const edgeT = cross$7(delta, direction2) / denom;
|
|
10289
|
+
if (rayT >= -EPS$9 && edgeT >= -EPS$9 && edgeT <= 1 + EPS$9 && rayT < bestT) {
|
|
10290
|
+
bestT = rayT;
|
|
10291
|
+
best = [origin[0] + direction2[0] * rayT, origin[1] + direction2[1] * rayT];
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
10294
|
+
return best;
|
|
10295
|
+
}
|
|
10296
|
+
function polygonCentroid$2(poly) {
|
|
10297
|
+
let area2 = 0;
|
|
10298
|
+
let cx = 0;
|
|
10299
|
+
let cy = 0;
|
|
10300
|
+
for (let index2 = 0; index2 < poly.length; index2 += 1) {
|
|
10301
|
+
const a2 = poly[index2];
|
|
10302
|
+
const b = poly[(index2 + 1) % poly.length];
|
|
10303
|
+
const crossValue = cross$7(a2, b);
|
|
10304
|
+
area2 += crossValue;
|
|
10305
|
+
cx += (a2[0] + b[0]) * crossValue;
|
|
10306
|
+
cy += (a2[1] + b[1]) * crossValue;
|
|
10307
|
+
}
|
|
10308
|
+
if (Math.abs(area2) < EPS$9) return averagePoint(poly);
|
|
10309
|
+
return [cx / (3 * area2), cy / (3 * area2)];
|
|
10310
|
+
}
|
|
10311
|
+
function averagePoint(poly) {
|
|
10312
|
+
let x2 = 0;
|
|
10313
|
+
let y2 = 0;
|
|
10314
|
+
for (const point2 of poly) {
|
|
10315
|
+
x2 += point2[0];
|
|
10316
|
+
y2 += point2[1];
|
|
10317
|
+
}
|
|
10318
|
+
return [x2 / poly.length, y2 / poly.length];
|
|
10319
|
+
}
|
|
10320
|
+
function isConvexPolygon(poly) {
|
|
10321
|
+
let sign2 = 0;
|
|
10322
|
+
for (let index2 = 0; index2 < poly.length; index2 += 1) {
|
|
10323
|
+
const a2 = poly[index2];
|
|
10324
|
+
const b = poly[(index2 + 1) % poly.length];
|
|
10325
|
+
const c2 = poly[(index2 + 2) % poly.length];
|
|
10326
|
+
const turn = cross$7([b[0] - a2[0], b[1] - a2[1]], [c2[0] - b[0], c2[1] - b[1]]);
|
|
10327
|
+
if (Math.abs(turn) < EPS$9) continue;
|
|
10328
|
+
const currentSign = Math.sign(turn);
|
|
10329
|
+
if (sign2 !== 0 && currentSign !== sign2) return false;
|
|
10330
|
+
sign2 = currentSign;
|
|
10331
|
+
}
|
|
10332
|
+
return sign2 !== 0;
|
|
10333
|
+
}
|
|
10334
|
+
function cross$7(a2, b) {
|
|
10335
|
+
return a2[0] * b[1] - a2[1] * b[0];
|
|
10336
|
+
}
|
|
9508
10337
|
function loftStitched(profiles2, heights, wasm) {
|
|
9509
10338
|
if (profiles2.length < 2) return null;
|
|
9510
10339
|
const classified = profiles2.map((loops) => classifyLoops(loops));
|
|
@@ -9633,8 +10462,10 @@ function stitchSingleLoopLoft(loops, heights, wasm) {
|
|
|
9633
10462
|
maxPoints = Math.max(maxPoints, loop.length);
|
|
9634
10463
|
}
|
|
9635
10464
|
const N = Math.max(maxPoints, 24);
|
|
10465
|
+
const angularSamples = normalizedLoops.map((loop) => resamplePolygonByAngle(loop, N));
|
|
10466
|
+
const useAngularSamples = angularSamples.every((samples) => samples != null);
|
|
9636
10467
|
const resampled = normalizedLoops.map((loop, i) => {
|
|
9637
|
-
const pts2d = resamplePolygon(loop, N);
|
|
10468
|
+
const pts2d = useAngularSamples ? angularSamples[i] : resamplePolygon(loop, N);
|
|
9638
10469
|
const z2 = heights[i];
|
|
9639
10470
|
return pts2d.map(([x2, y2]) => [x2, y2, z2]);
|
|
9640
10471
|
});
|
|
@@ -9653,8 +10484,8 @@ function stitchSingleLoopLoft(loops, heights, wasm) {
|
|
|
9653
10484
|
const v0 = baseIdx + j;
|
|
9654
10485
|
const v1 = nextIdx + j;
|
|
9655
10486
|
const v2 = nextIdx + j1;
|
|
9656
|
-
const
|
|
9657
|
-
triangles.push(v0,
|
|
10487
|
+
const v32 = baseIdx + j1;
|
|
10488
|
+
triangles.push(v0, v32, v2);
|
|
9658
10489
|
triangles.push(v0, v2, v1);
|
|
9659
10490
|
}
|
|
9660
10491
|
}
|
|
@@ -9695,7 +10526,7 @@ let _wasm$1 = null;
|
|
|
9695
10526
|
async function initManifoldWasm() {
|
|
9696
10527
|
if (_wasm$1) return _wasm$1;
|
|
9697
10528
|
performance.mark("manifold:start");
|
|
9698
|
-
const Module = (await import("./manifold-
|
|
10529
|
+
const Module = (await import("./manifold-B9QSr-qP.js")).default;
|
|
9699
10530
|
performance.mark("manifold:imported");
|
|
9700
10531
|
const wasm = await Module();
|
|
9701
10532
|
wasm.setup();
|
|
@@ -9965,8 +10796,8 @@ function stitchLoopAlongPath(loop, _path, frames, wasm) {
|
|
|
9965
10796
|
const v0 = baseIdx + j;
|
|
9966
10797
|
const v1 = nextIdx + j;
|
|
9967
10798
|
const v2 = nextIdx + j1;
|
|
9968
|
-
const
|
|
9969
|
-
triangles.push(v0,
|
|
10799
|
+
const v32 = baseIdx + j1;
|
|
10800
|
+
triangles.push(v0, v32, v2);
|
|
9970
10801
|
triangles.push(v0, v2, v1);
|
|
9971
10802
|
}
|
|
9972
10803
|
}
|
|
@@ -11312,8 +12143,16 @@ function lowerShapeCompilePlanToManifold(plan, wasm) {
|
|
|
11312
12143
|
case "importedMesh":
|
|
11313
12144
|
return lowerImportedMeshToManifold(plan.fileData, plan.format, plan.filePath, wasm);
|
|
11314
12145
|
case "sdf": {
|
|
11315
|
-
const
|
|
11316
|
-
return lowerSdfToManifold(
|
|
12146
|
+
const evaluator = compileSdfMaterializationEvaluator3(plan.tree);
|
|
12147
|
+
return lowerSdfToManifold(
|
|
12148
|
+
evaluator.fn,
|
|
12149
|
+
plan.bounds,
|
|
12150
|
+
plan.edgeLength,
|
|
12151
|
+
wasm,
|
|
12152
|
+
plan.meshing,
|
|
12153
|
+
evaluator.engine,
|
|
12154
|
+
evaluator.unsupportedReason
|
|
12155
|
+
);
|
|
11317
12156
|
}
|
|
11318
12157
|
case "fromSlices":
|
|
11319
12158
|
return lowerFromSlicesToManifold(plan, wasm);
|
|
@@ -11331,8 +12170,12 @@ function lowerShapeCompilePlanToManifold(plan, wasm) {
|
|
|
11331
12170
|
assertExhaustive(plan);
|
|
11332
12171
|
}
|
|
11333
12172
|
}
|
|
11334
|
-
function lowerSdfToManifold(evalFn, bounds, edgeLength2, wasm, meshing) {
|
|
12173
|
+
function lowerSdfToManifold(evalFn, bounds, edgeLength2, wasm, meshing, evaluatorEngine, evaluatorUnsupportedReason) {
|
|
11335
12174
|
const diagnostics = (meshing == null ? void 0 : meshing.diagnostics) ? { ...meshing.diagnostics } : void 0;
|
|
12175
|
+
if (diagnostics && evaluatorEngine) {
|
|
12176
|
+
diagnostics.evaluator = evaluatorEngine;
|
|
12177
|
+
if (evaluatorUnsupportedReason) diagnostics.evaluatorUnsupportedReason = evaluatorUnsupportedReason;
|
|
12178
|
+
}
|
|
11336
12179
|
const inset = edgeLength2;
|
|
11337
12180
|
const cappedEvalFn = (x2, y2, z2) => {
|
|
11338
12181
|
const bx = Math.max(bounds.min[0] + inset - x2, x2 - bounds.max[0] + inset);
|
|
@@ -16783,9 +17626,9 @@ function requireClipper() {
|
|
|
16783
17626
|
if (ClipperLib2.use_xyz) j.OffPt.Z = OffPt.Z;
|
|
16784
17627
|
this.m_Joins.push(j);
|
|
16785
17628
|
};
|
|
16786
|
-
ClipperLib2.Clipper.prototype.AddGhostJoin = function(
|
|
17629
|
+
ClipperLib2.Clipper.prototype.AddGhostJoin = function(Op2, OffPt) {
|
|
16787
17630
|
var j = new ClipperLib2.Join();
|
|
16788
|
-
j.OutPt1 =
|
|
17631
|
+
j.OutPt1 = Op2;
|
|
16789
17632
|
j.OffPt.X = OffPt.X;
|
|
16790
17633
|
j.OffPt.Y = OffPt.Y;
|
|
16791
17634
|
if (ClipperLib2.use_xyz) j.OffPt.Z = OffPt.Z;
|
|
@@ -19687,7 +20530,7 @@ function requireClipper() {
|
|
|
19687
20530
|
}
|
|
19688
20531
|
var clipperExports = requireClipper();
|
|
19689
20532
|
var ClipperLib = /* @__PURE__ */ getDefaultExportFromCjs(clipperExports);
|
|
19690
|
-
let f$
|
|
20533
|
+
let f$3 = class f {
|
|
19691
20534
|
constructor(t, e) {
|
|
19692
20535
|
this.next = null, this.key = t, this.data = e, this.left = null, this.right = null;
|
|
19693
20536
|
}
|
|
@@ -19696,7 +20539,7 @@ function d(n, t) {
|
|
|
19696
20539
|
return n > t ? 1 : n < t ? -1 : 0;
|
|
19697
20540
|
}
|
|
19698
20541
|
function u$1(n, t, e) {
|
|
19699
|
-
const r = new f$
|
|
20542
|
+
const r = new f$3(null, null);
|
|
19700
20543
|
let l = r, i = r;
|
|
19701
20544
|
for (; ; ) {
|
|
19702
20545
|
const o = e(n, t.key);
|
|
@@ -19719,7 +20562,7 @@ function u$1(n, t, e) {
|
|
|
19719
20562
|
return l.right = t.left, i.left = t.right, t.left = r.right, t.right = r.left, t;
|
|
19720
20563
|
}
|
|
19721
20564
|
function c(n, t, e, r) {
|
|
19722
|
-
const l = new f$
|
|
20565
|
+
const l = new f$3(n, t);
|
|
19723
20566
|
if (e === null)
|
|
19724
20567
|
return l.left = l.right = null, l;
|
|
19725
20568
|
e = u$1(n, e, r);
|
|
@@ -19760,7 +20603,7 @@ class z {
|
|
|
19760
20603
|
* Adds a key, if it is not present in the tree
|
|
19761
20604
|
*/
|
|
19762
20605
|
add(t, e) {
|
|
19763
|
-
const r = new f$
|
|
20606
|
+
const r = new f$3(t, e);
|
|
19764
20607
|
this._root === null && (r.left = r.right = null, this._size++, this._root = r);
|
|
19765
20608
|
const l = this._comparator, i = u$1(t, this._root, l), o = l(t, i.key);
|
|
19766
20609
|
return o === 0 ? this._root = i : (o < 0 ? (r.left = i.left, r.right = i, i.left = null) : o > 0 && (r.right = i.right, r.left = i, i.right = null), this._size++, this._root = r), this._root;
|
|
@@ -19973,23 +20816,23 @@ class z {
|
|
|
19973
20816
|
function a(n, t, e, r) {
|
|
19974
20817
|
const l = r - e;
|
|
19975
20818
|
if (l > 0) {
|
|
19976
|
-
const i = e + Math.floor(l / 2), o = n[i], s = t[i], h = new f$
|
|
20819
|
+
const i = e + Math.floor(l / 2), o = n[i], s = t[i], h = new f$3(o, s);
|
|
19977
20820
|
return h.left = a(n, t, e, i), h.right = a(n, t, i + 1, r), h;
|
|
19978
20821
|
}
|
|
19979
20822
|
return null;
|
|
19980
20823
|
}
|
|
19981
20824
|
function x(n, t) {
|
|
19982
|
-
const e = new f$
|
|
20825
|
+
const e = new f$3(null, null);
|
|
19983
20826
|
let r = e;
|
|
19984
20827
|
for (let l = 0; l < n.length; l++)
|
|
19985
|
-
r = r.next = new f$
|
|
20828
|
+
r = r.next = new f$3(n[l], t[l]);
|
|
19986
20829
|
return r.next = null, e.next;
|
|
19987
20830
|
}
|
|
19988
20831
|
function k(n) {
|
|
19989
20832
|
let t = n;
|
|
19990
20833
|
const e = [];
|
|
19991
20834
|
let r = false;
|
|
19992
|
-
const l = new f$
|
|
20835
|
+
const l = new f$3(null, null);
|
|
19993
20836
|
let i = l;
|
|
19994
20837
|
for (; !r; )
|
|
19995
20838
|
t ? (e.push(t), t = t.left) : e.length > 0 ? (t = i = i.next = e.pop(), t = t.right) : r = true;
|
|
@@ -20004,7 +20847,7 @@ function p(n, t, e) {
|
|
|
20004
20847
|
return null;
|
|
20005
20848
|
}
|
|
20006
20849
|
function y(n, t, e) {
|
|
20007
|
-
const r = new f$
|
|
20850
|
+
const r = new f$3(null, null);
|
|
20008
20851
|
let l = r, i = n, o = t;
|
|
20009
20852
|
for (; i !== null && o !== null; )
|
|
20010
20853
|
e(i.key, o.key) < 0 ? (l.next = i, i = i.next) : (l.next = o, o = o.next), l = l.next;
|
|
@@ -24992,7 +25835,13 @@ function normalizeTruckShapeForBooleanInput(shape) {
|
|
|
24992
25835
|
return normalized;
|
|
24993
25836
|
}
|
|
24994
25837
|
function lowerSdfPlan(plan) {
|
|
24995
|
-
|
|
25838
|
+
var _a3, _b3, _c2;
|
|
25839
|
+
const evaluator = compileSdfMaterializationEvaluator3(plan.tree);
|
|
25840
|
+
if ((_a3 = plan.meshing) == null ? void 0 : _a3.diagnostics) {
|
|
25841
|
+
plan.meshing.diagnostics.evaluator = evaluator.engine;
|
|
25842
|
+
if (evaluator.unsupportedReason) plan.meshing.diagnostics.evaluatorUnsupportedReason = evaluator.unsupportedReason;
|
|
25843
|
+
}
|
|
25844
|
+
const evalFn = evaluator.fn;
|
|
24996
25845
|
const inset = plan.edgeLength;
|
|
24997
25846
|
const cappedEvalFn = (x2, y2, z2) => {
|
|
24998
25847
|
const bx = Math.max(plan.bounds.min[0] + inset - x2, x2 - plan.bounds.max[0] + inset);
|
|
@@ -25004,14 +25853,18 @@ function lowerSdfPlan(plan) {
|
|
|
25004
25853
|
assertSdfMeshBudget(mesh, plan);
|
|
25005
25854
|
let surfaceNetsError;
|
|
25006
25855
|
try {
|
|
25007
|
-
|
|
25856
|
+
const shape = lowerExtractedSdfMesh(mesh, cappedEvalFn, true);
|
|
25857
|
+
if ((_b3 = plan.meshing) == null ? void 0 : _b3.diagnostics) logSdfMeshingDiagnostics("SDF meshing result", plan.meshing.diagnostics);
|
|
25858
|
+
return shape;
|
|
25008
25859
|
} catch (error) {
|
|
25009
25860
|
surfaceNetsError = error;
|
|
25010
25861
|
}
|
|
25011
25862
|
const tetraMesh = marchingTetrahedra(cappedEvalFn, plan.bounds, plan.edgeLength);
|
|
25012
25863
|
assertSdfMeshBudget(tetraMesh, plan);
|
|
25013
25864
|
try {
|
|
25014
|
-
|
|
25865
|
+
const shape = lowerExtractedSdfMesh(tetraMesh, cappedEvalFn, false);
|
|
25866
|
+
if ((_c2 = plan.meshing) == null ? void 0 : _c2.diagnostics) logSdfMeshingDiagnostics("SDF meshing result", plan.meshing.diagnostics);
|
|
25867
|
+
return shape;
|
|
25015
25868
|
} catch (error) {
|
|
25016
25869
|
throw new Error(
|
|
25017
25870
|
`Truck backend does not support compile plan "sdf" for this materialized field yet: Surface Nets failed with ${surfaceNetsError instanceof Error ? surfaceNetsError.message : String(surfaceNetsError)}; marching tetrahedra failed with ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -25375,7 +26228,9 @@ function lowerOffsetSolidPlan(plan) {
|
|
|
25375
26228
|
if (base.kind === "transform") {
|
|
25376
26229
|
return lowerTransformedOffsetSolidPlan(base, plan.thickness);
|
|
25377
26230
|
}
|
|
25378
|
-
return truckUnsupported(
|
|
26231
|
+
return truckUnsupported(
|
|
26232
|
+
`compile plan "${plan.kind}" for non-vertical-prism/non-revolved/non-loft/non-straight-sweep/non-straight-variable-sweep solids`
|
|
26233
|
+
);
|
|
25379
26234
|
}
|
|
25380
26235
|
function lowerLoftPlan(plan) {
|
|
25381
26236
|
return wrapTruckShapeBackend(
|
|
@@ -32677,6 +33532,37 @@ function mergeSketchPlacementModel(sketches) {
|
|
|
32677
33532
|
}
|
|
32678
33533
|
return first;
|
|
32679
33534
|
}
|
|
33535
|
+
function normalizeSceneTags(value, label = "tags") {
|
|
33536
|
+
if (value == null) return [];
|
|
33537
|
+
const rawTags = typeof value === "string" ? [value] : value;
|
|
33538
|
+
if (!Array.isArray(rawTags)) {
|
|
33539
|
+
throw new Error(`${label} must be a string or array of strings`);
|
|
33540
|
+
}
|
|
33541
|
+
const out = [];
|
|
33542
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33543
|
+
rawTags.forEach((tag, index2) => {
|
|
33544
|
+
if (typeof tag !== "string") {
|
|
33545
|
+
throw new Error(`${label}[${index2}] must be a string`);
|
|
33546
|
+
}
|
|
33547
|
+
const trimmed = tag.trim();
|
|
33548
|
+
if (!trimmed || seen.has(trimmed)) return;
|
|
33549
|
+
seen.add(trimmed);
|
|
33550
|
+
out.push(trimmed);
|
|
33551
|
+
});
|
|
33552
|
+
return out;
|
|
33553
|
+
}
|
|
33554
|
+
function mergeSceneTags(...values) {
|
|
33555
|
+
const out = [];
|
|
33556
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33557
|
+
values.forEach((value) => {
|
|
33558
|
+
normalizeSceneTags(value).forEach((tag) => {
|
|
33559
|
+
if (seen.has(tag)) return;
|
|
33560
|
+
seen.add(tag);
|
|
33561
|
+
out.push(tag);
|
|
33562
|
+
});
|
|
33563
|
+
});
|
|
33564
|
+
return out;
|
|
33565
|
+
}
|
|
32680
33566
|
const _groupPlacementRefs = /* @__PURE__ */ new WeakMap();
|
|
32681
33567
|
const _groupExplodeHint = /* @__PURE__ */ new WeakMap();
|
|
32682
33568
|
function getGroupRefs(g2) {
|
|
@@ -32700,7 +33586,7 @@ function transformGroupRefs(source, dest, matrix) {
|
|
|
32700
33586
|
}
|
|
32701
33587
|
return dest;
|
|
32702
33588
|
}
|
|
32703
|
-
function requireFiniteAngle(v, method) {
|
|
33589
|
+
function requireFiniteAngle$1(v, method) {
|
|
32704
33590
|
if (typeof v !== "number" || !Number.isFinite(v))
|
|
32705
33591
|
throw new Error(`${method} angleDeg must be a finite number, got ${typeof v === "number" ? v : typeof v}`);
|
|
32706
33592
|
}
|
|
@@ -32761,31 +33647,46 @@ function resolveNamedGroupChild(item) {
|
|
|
32761
33647
|
function normalizeGroupInputs(items) {
|
|
32762
33648
|
const children = [];
|
|
32763
33649
|
const childNames = [];
|
|
33650
|
+
const childTags = [];
|
|
32764
33651
|
items.forEach((item) => {
|
|
32765
33652
|
if (isNamedGroupChild(item)) {
|
|
32766
33653
|
children.push(resolveNamedGroupChild(item));
|
|
32767
33654
|
childNames.push(normalizeChildName(item.name));
|
|
33655
|
+
childTags.push(normalizeSceneTags(item.tags, `group(...) named item "${item.name}" tags`));
|
|
32768
33656
|
return;
|
|
32769
33657
|
}
|
|
32770
33658
|
children.push(item);
|
|
32771
33659
|
childNames.push(void 0);
|
|
33660
|
+
childTags.push([]);
|
|
32772
33661
|
});
|
|
32773
|
-
return { children, childNames };
|
|
33662
|
+
return { children, childNames, childTags };
|
|
32774
33663
|
}
|
|
32775
33664
|
class ShapeGroup {
|
|
32776
|
-
constructor(children, childNames) {
|
|
33665
|
+
constructor(children, childNames, childTags) {
|
|
32777
33666
|
__publicField(this, "children");
|
|
32778
33667
|
__publicField(this, "childNames");
|
|
33668
|
+
__publicField(this, "childTags");
|
|
32779
33669
|
if (childNames && childNames.length !== children.length) {
|
|
32780
33670
|
throw new Error("ShapeGroup childNames must match children length");
|
|
32781
33671
|
}
|
|
33672
|
+
if (childTags && childTags.length !== children.length) {
|
|
33673
|
+
throw new Error("ShapeGroup childTags must match children length");
|
|
33674
|
+
}
|
|
32782
33675
|
this.children = [...children];
|
|
32783
33676
|
this.childNames = this.children.map((_2, index2) => normalizeChildName(childNames == null ? void 0 : childNames[index2]));
|
|
33677
|
+
this.childTags = this.children.map((_2, index2) => normalizeSceneTags(childTags == null ? void 0 : childTags[index2], "ShapeGroup childTags"));
|
|
32784
33678
|
}
|
|
32785
33679
|
/** Return the optional name of the child at `index`. */
|
|
32786
33680
|
childName(index2) {
|
|
32787
33681
|
return this.childNames[index2];
|
|
32788
33682
|
}
|
|
33683
|
+
/**
|
|
33684
|
+
* Return tags attached to the child at `index`.
|
|
33685
|
+
* @internal
|
|
33686
|
+
*/
|
|
33687
|
+
tagsForChild(index2) {
|
|
33688
|
+
return [...this.childTags[index2] ?? []];
|
|
33689
|
+
}
|
|
32789
33690
|
/**
|
|
32790
33691
|
* Return the named child by name. Throws if not found.
|
|
32791
33692
|
* Useful when importing a multipart group and working on components individually.
|
|
@@ -32800,13 +33701,13 @@ class ShapeGroup {
|
|
|
32800
33701
|
}
|
|
32801
33702
|
/** Apply fn to all children, producing a new ShapeGroup that also copies placement refs. */
|
|
32802
33703
|
mapChildren(fn) {
|
|
32803
|
-
const next = new ShapeGroup(this.children.map(fn), this.childNames);
|
|
33704
|
+
const next = new ShapeGroup(this.children.map(fn), this.childNames, this.childTags);
|
|
32804
33705
|
copyGroupPorts(this, next);
|
|
32805
33706
|
return copyGroupRefs(this, next);
|
|
32806
33707
|
}
|
|
32807
33708
|
/** Apply fn to all children and also transform placement refs by the given matrix. */
|
|
32808
33709
|
mapChildrenTransform(fn, matrix) {
|
|
32809
|
-
const next = new ShapeGroup(this.children.map(fn), this.childNames);
|
|
33710
|
+
const next = new ShapeGroup(this.children.map(fn), this.childNames, this.childTags);
|
|
32810
33711
|
transformGroupPortsHelper(this, next, matrix);
|
|
32811
33712
|
return transformGroupRefs(this, next, matrix);
|
|
32812
33713
|
}
|
|
@@ -32933,25 +33834,25 @@ class ShapeGroup {
|
|
|
32933
33834
|
/** Rotate the group around an arbitrary axis through the origin. */
|
|
32934
33835
|
rotate(axis, angleDeg, options) {
|
|
32935
33836
|
requireRotateAxis(axis, "ShapeGroup.rotate()");
|
|
32936
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotate()");
|
|
33837
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotate()");
|
|
32937
33838
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotate()");
|
|
32938
33839
|
return this.rotateAroundAxis(axis, angleDeg, options == null ? void 0 : options.pivot);
|
|
32939
33840
|
}
|
|
32940
33841
|
/** Rotate the group around the X axis. */
|
|
32941
33842
|
rotateX(angleDeg, options) {
|
|
32942
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotateX()");
|
|
33843
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotateX()");
|
|
32943
33844
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotateX()");
|
|
32944
33845
|
return this.rotateAroundAxis([1, 0, 0], angleDeg, options == null ? void 0 : options.pivot);
|
|
32945
33846
|
}
|
|
32946
33847
|
/** Rotate the group around the Y axis. */
|
|
32947
33848
|
rotateY(angleDeg, options) {
|
|
32948
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotateY()");
|
|
33849
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotateY()");
|
|
32949
33850
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotateY()");
|
|
32950
33851
|
return this.rotateAroundAxis([0, 1, 0], angleDeg, options == null ? void 0 : options.pivot);
|
|
32951
33852
|
}
|
|
32952
33853
|
/** Rotate the group around the Z axis. */
|
|
32953
33854
|
rotateZ(angleDeg, options) {
|
|
32954
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotateZ()");
|
|
33855
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotateZ()");
|
|
32955
33856
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotateZ()");
|
|
32956
33857
|
return this.rotateAroundAxis([0, 0, 1], angleDeg, options == null ? void 0 : options.pivot);
|
|
32957
33858
|
}
|
|
@@ -32994,7 +33895,8 @@ class ShapeGroup {
|
|
|
32994
33895
|
"ShapeGroup.transform only supports 3D children (Shape/ShapeGroup). For Sketch children, use 2D transforms (translate/rotate/scale/mirror)."
|
|
32995
33896
|
);
|
|
32996
33897
|
}),
|
|
32997
|
-
this.childNames
|
|
33898
|
+
this.childNames,
|
|
33899
|
+
this.childTags
|
|
32998
33900
|
);
|
|
32999
33901
|
transformGroupPortsHelper(this, next, matrix);
|
|
33000
33902
|
return transformGroupRefs(this, next, matrix);
|
|
@@ -33062,7 +33964,7 @@ class ShapeGroup {
|
|
|
33062
33964
|
* ```
|
|
33063
33965
|
*/
|
|
33064
33966
|
withReferences(refs) {
|
|
33065
|
-
const next = new ShapeGroup(this.children, this.childNames);
|
|
33967
|
+
const next = new ShapeGroup(this.children, this.childNames, this.childTags);
|
|
33066
33968
|
const merged = applyPlacementReferenceInput(getGroupRefs(this), refs);
|
|
33067
33969
|
return setGroupRefs(next, merged);
|
|
33068
33970
|
}
|
|
@@ -33130,7 +34032,7 @@ class ShapeGroup {
|
|
|
33130
34032
|
/** Attach named connectors — attachment points that survive transforms.
|
|
33131
34033
|
* Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching). */
|
|
33132
34034
|
withConnectors(connectors) {
|
|
33133
|
-
const next = new ShapeGroup(this.children, this.childNames);
|
|
34035
|
+
const next = new ShapeGroup(this.children, this.childNames, this.childTags);
|
|
33134
34036
|
copyGroupRefs(this, next);
|
|
33135
34037
|
const existing = getGroupPorts(this);
|
|
33136
34038
|
const incoming = normalizeConnectorMapInput(connectors);
|
|
@@ -33180,7 +34082,7 @@ class ShapeGroup {
|
|
|
33180
34082
|
}
|
|
33181
34083
|
function group(...items) {
|
|
33182
34084
|
const normalized = normalizeGroupInputs(items);
|
|
33183
|
-
return new ShapeGroup(normalized.children, normalized.childNames);
|
|
34085
|
+
return new ShapeGroup(normalized.children, normalized.childNames, normalized.childTags);
|
|
33184
34086
|
}
|
|
33185
34087
|
function getTargetPortsForGroup(target) {
|
|
33186
34088
|
if (target instanceof Shape) {
|
|
@@ -35355,7 +36257,7 @@ function buildSdfFunctionDefinition(source, options) {
|
|
|
35355
36257
|
jsExpression: expression,
|
|
35356
36258
|
...shader.ok ? { shaderExpression: shader.expression } : { shaderUnsupportedReason: shader.reason },
|
|
35357
36259
|
raymarchStepLimit: resolveRaymarchStepLimit(options.bounds, options.maxStep),
|
|
35358
|
-
...options.lipschitz !== void 0 ? { raymarchLipschitz: requirePositiveFinite$
|
|
36260
|
+
...options.lipschitz !== void 0 ? { raymarchLipschitz: requirePositiveFinite$2(options.lipschitz, "sdf.fromFunction() lipschitz") } : {}
|
|
35359
36261
|
};
|
|
35360
36262
|
}
|
|
35361
36263
|
function extractSdfExpression(source) {
|
|
@@ -35523,7 +36425,7 @@ function formatNumericLiteralsForGlsl(source) {
|
|
|
35523
36425
|
return result;
|
|
35524
36426
|
}
|
|
35525
36427
|
function resolveRaymarchStepLimit(bounds, maxStep) {
|
|
35526
|
-
if (maxStep !== void 0) return requirePositiveFinite$
|
|
36428
|
+
if (maxStep !== void 0) return requirePositiveFinite$2(maxStep, "sdf.fromFunction() maxStep");
|
|
35527
36429
|
const dx = bounds.max[0] - bounds.min[0];
|
|
35528
36430
|
const dy = bounds.max[1] - bounds.min[1];
|
|
35529
36431
|
const dz = bounds.max[2] - bounds.min[2];
|
|
@@ -35531,7 +36433,7 @@ function resolveRaymarchStepLimit(bounds, maxStep) {
|
|
|
35531
36433
|
if (!Number.isFinite(diagonal) || diagonal <= 0) return 0.1;
|
|
35532
36434
|
return Math.max(0.025, Math.min(0.5, diagonal / 240));
|
|
35533
36435
|
}
|
|
35534
|
-
function requirePositiveFinite$
|
|
36436
|
+
function requirePositiveFinite$2(value, label) {
|
|
35535
36437
|
if (!Number.isFinite(value) || value <= 0) throw new Error(`${label} must be a positive finite number.`);
|
|
35536
36438
|
return value;
|
|
35537
36439
|
}
|
|
@@ -35558,6 +36460,199 @@ class SurfacePattern {
|
|
|
35558
36460
|
this.constants = constants;
|
|
35559
36461
|
}
|
|
35560
36462
|
}
|
|
36463
|
+
const typedSurfacePatterns = /* @__PURE__ */ new WeakMap();
|
|
36464
|
+
function getTypedSurfacePattern(pattern) {
|
|
36465
|
+
return typedSurfacePatterns.get(pattern);
|
|
36466
|
+
}
|
|
36467
|
+
class Pattern2D extends SurfacePattern {
|
|
36468
|
+
constructor(body) {
|
|
36469
|
+
super(body);
|
|
36470
|
+
}
|
|
36471
|
+
/** Add this pattern to one or more patterns or constant height offsets. */
|
|
36472
|
+
add(...patterns) {
|
|
36473
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36474
|
+
}
|
|
36475
|
+
/** Subtract another pattern or constant height offset from this pattern. */
|
|
36476
|
+
subtract(pattern) {
|
|
36477
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36478
|
+
}
|
|
36479
|
+
/** Multiply this pattern by one or more patterns or numeric scale factors. */
|
|
36480
|
+
multiply(...patterns) {
|
|
36481
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36482
|
+
}
|
|
36483
|
+
/** Keep the lower height between this pattern and one or more other patterns. */
|
|
36484
|
+
min(...patterns) {
|
|
36485
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36486
|
+
}
|
|
36487
|
+
/** Keep the higher height between this pattern and one or more other patterns. */
|
|
36488
|
+
max(...patterns) {
|
|
36489
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36490
|
+
}
|
|
36491
|
+
/** Limit pattern height to the inclusive `[min, max]` range in millimeters. */
|
|
36492
|
+
clamp(min2, max2) {
|
|
36493
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36494
|
+
}
|
|
36495
|
+
/** Convert negative heights to positive heights. */
|
|
36496
|
+
abs() {
|
|
36497
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36498
|
+
}
|
|
36499
|
+
/** Flip the pattern height sign. */
|
|
36500
|
+
negate() {
|
|
36501
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36502
|
+
}
|
|
36503
|
+
}
|
|
36504
|
+
class Pattern2DImpl extends Pattern2D {
|
|
36505
|
+
constructor(node) {
|
|
36506
|
+
super(emitSurfacePatternJsExpression(node));
|
|
36507
|
+
__publicField(this, "node");
|
|
36508
|
+
this.node = node;
|
|
36509
|
+
typedSurfacePatterns.set(this, node);
|
|
36510
|
+
}
|
|
36511
|
+
add(...patterns) {
|
|
36512
|
+
return new Pattern2DImpl({ kind: "surfacePattern:add", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36513
|
+
}
|
|
36514
|
+
subtract(pattern) {
|
|
36515
|
+
return this.add(new Pattern2DImpl({ kind: "surfacePattern:negate", child: patternNodeFromInput(pattern) }));
|
|
36516
|
+
}
|
|
36517
|
+
multiply(...patterns) {
|
|
36518
|
+
return new Pattern2DImpl({ kind: "surfacePattern:multiply", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36519
|
+
}
|
|
36520
|
+
min(...patterns) {
|
|
36521
|
+
return new Pattern2DImpl({ kind: "surfacePattern:min", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36522
|
+
}
|
|
36523
|
+
max(...patterns) {
|
|
36524
|
+
return new Pattern2DImpl({ kind: "surfacePattern:max", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36525
|
+
}
|
|
36526
|
+
clamp(min2, max2) {
|
|
36527
|
+
const lo = requireFinite$8(min2, "Pattern2D.clamp() min");
|
|
36528
|
+
const hi = requireFinite$8(max2, "Pattern2D.clamp() max");
|
|
36529
|
+
if (lo > hi) throw new Error(`Pattern2D.clamp() min must be <= max. Received: ${lo} > ${hi}`);
|
|
36530
|
+
return new Pattern2DImpl({ kind: "surfacePattern:clamp", child: this.node, min: lo, max: hi });
|
|
36531
|
+
}
|
|
36532
|
+
abs() {
|
|
36533
|
+
return new Pattern2DImpl({ kind: "surfacePattern:abs", child: this.node });
|
|
36534
|
+
}
|
|
36535
|
+
negate() {
|
|
36536
|
+
return new Pattern2DImpl({ kind: "surfacePattern:negate", child: this.node });
|
|
36537
|
+
}
|
|
36538
|
+
}
|
|
36539
|
+
class Pattern2DBuilder {
|
|
36540
|
+
/** Create a constant-height pattern in millimeters. */
|
|
36541
|
+
constant(value = 0) {
|
|
36542
|
+
return new Pattern2DImpl({ kind: "surfacePattern:constant", value: requireFinite$8(value, "sdf.pattern2d().constant() value") });
|
|
36543
|
+
}
|
|
36544
|
+
/** Create a sinusoidal wave pattern in UV space. */
|
|
36545
|
+
sineWave(options) {
|
|
36546
|
+
return new Pattern2DImpl({
|
|
36547
|
+
kind: "surfacePattern:sineWave",
|
|
36548
|
+
direction: normalizeDirection$1(options.direction ?? [1, 0], "sdf.pattern2d().sineWave() direction"),
|
|
36549
|
+
wavelength: requirePositiveFinite$1(options.wavelength, "sdf.pattern2d().sineWave() wavelength"),
|
|
36550
|
+
amplitude: requireFinite$8(options.amplitude ?? 1, "sdf.pattern2d().sineWave() amplitude"),
|
|
36551
|
+
phase: requireFinite$8(options.phase ?? 0, "sdf.pattern2d().sineWave() phase"),
|
|
36552
|
+
bias: requireFinite$8(options.bias ?? 0, "sdf.pattern2d().sineWave() bias")
|
|
36553
|
+
});
|
|
36554
|
+
}
|
|
36555
|
+
/** Create recessed stripe bands in UV space. */
|
|
36556
|
+
stripes(options) {
|
|
36557
|
+
return new Pattern2DImpl({
|
|
36558
|
+
kind: "surfacePattern:stripes",
|
|
36559
|
+
direction: normalizeDirection$1(options.direction ?? [1, 0], "sdf.pattern2d().stripes() direction"),
|
|
36560
|
+
spacing: requirePositiveFinite$1(options.spacing, "sdf.pattern2d().stripes() spacing"),
|
|
36561
|
+
width: requirePositiveFinite$1(options.width, "sdf.pattern2d().stripes() width"),
|
|
36562
|
+
depth: requireNonNegativeFinite$1(options.depth ?? 1, "sdf.pattern2d().stripes() depth")
|
|
36563
|
+
});
|
|
36564
|
+
}
|
|
36565
|
+
/** Create an over-under woven relief pattern in UV space. */
|
|
36566
|
+
overUnderWeave(options) {
|
|
36567
|
+
return new Pattern2DImpl({
|
|
36568
|
+
kind: "surfacePattern:overUnderWeave",
|
|
36569
|
+
spacing: normalizeVec2(options.spacing, "sdf.pattern2d().overUnderWeave() spacing", requirePositiveFinite$1),
|
|
36570
|
+
threadWidth: normalizeVec2(options.threadWidth, "sdf.pattern2d().overUnderWeave() threadWidth", requirePositiveFinite$1),
|
|
36571
|
+
depth: requireNonNegativeFinite$1(options.depth ?? 0.8, "sdf.pattern2d().overUnderWeave() depth"),
|
|
36572
|
+
underScale: requireNonNegativeFinite$1(options.underScale ?? 0.15, "sdf.pattern2d().overUnderWeave() underScale")
|
|
36573
|
+
});
|
|
36574
|
+
}
|
|
36575
|
+
}
|
|
36576
|
+
function pattern2d() {
|
|
36577
|
+
return new Pattern2DBuilder();
|
|
36578
|
+
}
|
|
36579
|
+
function patternNodeFromInput(input) {
|
|
36580
|
+
if (input instanceof SurfacePattern) {
|
|
36581
|
+
const node = getTypedSurfacePattern(input);
|
|
36582
|
+
if (node) return node;
|
|
36583
|
+
}
|
|
36584
|
+
if (typeof input === "number") {
|
|
36585
|
+
return { kind: "surfacePattern:constant", value: requireFinite$8(input, "Pattern2D numeric input") };
|
|
36586
|
+
}
|
|
36587
|
+
throw new Error("Pattern2D composition expects another typed Pattern2D or a number.");
|
|
36588
|
+
}
|
|
36589
|
+
function requireFinite$8(value, label) {
|
|
36590
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
36591
|
+
throw new Error(`${label} must be a finite number. Received: ${String(value)}`);
|
|
36592
|
+
}
|
|
36593
|
+
return value;
|
|
36594
|
+
}
|
|
36595
|
+
function requirePositiveFinite$1(value, label) {
|
|
36596
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
36597
|
+
throw new Error(`${label} must be a positive finite number. Received: ${String(value)}`);
|
|
36598
|
+
}
|
|
36599
|
+
return value;
|
|
36600
|
+
}
|
|
36601
|
+
function requireNonNegativeFinite$1(value, label) {
|
|
36602
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
36603
|
+
throw new Error(`${label} must be a non-negative finite number. Received: ${String(value)}`);
|
|
36604
|
+
}
|
|
36605
|
+
return value;
|
|
36606
|
+
}
|
|
36607
|
+
function normalizeVec2(value, label, validate) {
|
|
36608
|
+
if (typeof value === "number") {
|
|
36609
|
+
const n = validate(value, label);
|
|
36610
|
+
return [n, n];
|
|
36611
|
+
}
|
|
36612
|
+
return [validate(value[0], `${label}[0]`), validate(value[1], `${label}[1]`)];
|
|
36613
|
+
}
|
|
36614
|
+
function normalizeDirection$1(value, label) {
|
|
36615
|
+
const x2 = requireFinite$8(value[0], `${label}[0]`);
|
|
36616
|
+
const y2 = requireFinite$8(value[1], `${label}[1]`);
|
|
36617
|
+
const length4 = Math.hypot(x2, y2);
|
|
36618
|
+
if (length4 <= 0) throw new Error(`${label} must not be the zero vector.`);
|
|
36619
|
+
return [x2 / length4, y2 / length4];
|
|
36620
|
+
}
|
|
36621
|
+
function f$2(value) {
|
|
36622
|
+
if (!Number.isFinite(value)) return "0";
|
|
36623
|
+
return Number(value.toPrecision(12)).toString();
|
|
36624
|
+
}
|
|
36625
|
+
function emitSurfacePatternJsExpression(node) {
|
|
36626
|
+
switch (node.kind) {
|
|
36627
|
+
case "surfacePattern:constant":
|
|
36628
|
+
return f$2(node.value);
|
|
36629
|
+
case "surfacePattern:sineWave": {
|
|
36630
|
+
const coord = `(u * ${f$2(node.direction[0])} + v * ${f$2(node.direction[1])})`;
|
|
36631
|
+
const phase = `(${coord} * ${f$2(2 * Math.PI / node.wavelength)} + ${f$2(node.phase)})`;
|
|
36632
|
+
return `(${f$2(node.bias)} + Math.sin(${phase}) * ${f$2(node.amplitude)})`;
|
|
36633
|
+
}
|
|
36634
|
+
case "surfacePattern:stripes": {
|
|
36635
|
+
const coord = `(u * ${f$2(node.direction[0])} + v * ${f$2(node.direction[1])})`;
|
|
36636
|
+
return `(function(){var c=${coord};var d=Math.abs(c - Math.round(c / ${f$2(node.spacing)}) * ${f$2(node.spacing)});var p=Math.max(0, 1 - d / ${f$2(node.width * 0.5)});return -(p * p) * ${f$2(node.depth)};})()`;
|
|
36637
|
+
}
|
|
36638
|
+
case "surfacePattern:overUnderWeave":
|
|
36639
|
+
return `(function(){var su=u/${f$2(node.spacing[0])};var sv=v/${f$2(node.spacing[1])};var du=Math.abs(su - Math.round(su))*${f$2(node.spacing[0])};var dv=Math.abs(sv - Math.round(sv))*${f$2(node.spacing[1])};var pU=Math.max(0,1-du/${f$2(node.threadWidth[0] * 0.5)});pU*=pU;var pV=Math.max(0,1-dv/${f$2(node.threadWidth[1] * 0.5)});pV*=pV;var checker=((Math.round(su)&65535)+(Math.round(sv)&65535))&1;var top=checker?pV:pU;var bot=checker?pU:pV;return -Math.max(top,bot*${f$2(node.underScale)})*${f$2(node.depth)};})()`;
|
|
36640
|
+
case "surfacePattern:abs":
|
|
36641
|
+
return `Math.abs(${emitSurfacePatternJsExpression(node.child)})`;
|
|
36642
|
+
case "surfacePattern:negate":
|
|
36643
|
+
return `(-(${emitSurfacePatternJsExpression(node.child)}))`;
|
|
36644
|
+
case "surfacePattern:add":
|
|
36645
|
+
return node.children.length === 0 ? "0" : `(${node.children.map(emitSurfacePatternJsExpression).join(" + ")})`;
|
|
36646
|
+
case "surfacePattern:multiply":
|
|
36647
|
+
return node.children.length === 0 ? "1" : `(${node.children.map(emitSurfacePatternJsExpression).join(" * ")})`;
|
|
36648
|
+
case "surfacePattern:min":
|
|
36649
|
+
return node.children.length === 0 ? "0" : `Math.min(${node.children.map(emitSurfacePatternJsExpression).join(", ")})`;
|
|
36650
|
+
case "surfacePattern:max":
|
|
36651
|
+
return node.children.length === 0 ? "0" : `Math.max(${node.children.map(emitSurfacePatternJsExpression).join(", ")})`;
|
|
36652
|
+
case "surfacePattern:clamp":
|
|
36653
|
+
return `Math.min(${f$2(node.max)}, Math.max(${f$2(node.min)}, ${emitSurfacePatternJsExpression(node.child)}))`;
|
|
36654
|
+
}
|
|
36655
|
+
}
|
|
35561
36656
|
const SCULPT_MATERIAL_PRESETS = {
|
|
35562
36657
|
ceramic: {
|
|
35563
36658
|
color: "#f4f0e6",
|
|
@@ -35627,6 +36722,18 @@ function requirePositiveFinite(value, label) {
|
|
|
35627
36722
|
}
|
|
35628
36723
|
return value;
|
|
35629
36724
|
}
|
|
36725
|
+
function requirePositiveInteger(value, label) {
|
|
36726
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value < 1) {
|
|
36727
|
+
throw new Error(`${label} must be a positive integer. Received: ${String(value)}`);
|
|
36728
|
+
}
|
|
36729
|
+
return value;
|
|
36730
|
+
}
|
|
36731
|
+
function requireNonNegativeFinite(value, label) {
|
|
36732
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
36733
|
+
throw new Error(`${label} must be a non-negative finite number. Received: ${String(value)}`);
|
|
36734
|
+
}
|
|
36735
|
+
return value;
|
|
36736
|
+
}
|
|
35630
36737
|
function resolveBlendRadius(input, label, fallback = 4) {
|
|
35631
36738
|
if (typeof input === "number") return requirePositiveFinite(input, `${label} radius`);
|
|
35632
36739
|
if ((input == null ? void 0 : input.radius) !== void 0) return requirePositiveFinite(input.radius, `${label} radius`);
|
|
@@ -35873,6 +36980,29 @@ class SdfShape {
|
|
|
35873
36980
|
clipBox(x2, y2, z2) {
|
|
35874
36981
|
return this.intersect(box$1(x2, y2, z2));
|
|
35875
36982
|
}
|
|
36983
|
+
/** Keep only the material where this shape overlaps another SDF pattern. */
|
|
36984
|
+
fillWith(pattern) {
|
|
36985
|
+
if (!(pattern instanceof SdfShape)) {
|
|
36986
|
+
throw new Error("SdfShape.fillWith() expects an SdfShape pattern, such as sdf.gyroid({ cellSize, wallThickness }).");
|
|
36987
|
+
}
|
|
36988
|
+
return this.intersect(pattern);
|
|
36989
|
+
}
|
|
36990
|
+
/** Keep only the gyroid lattice inside this shape. */
|
|
36991
|
+
fillWithGyroid(options) {
|
|
36992
|
+
return this.fillWith(gyroid(options));
|
|
36993
|
+
}
|
|
36994
|
+
/** Keep only the Schwarz-P lattice inside this shape. */
|
|
36995
|
+
fillWithSchwarzP(options) {
|
|
36996
|
+
return this.fillWith(schwarzP(options));
|
|
36997
|
+
}
|
|
36998
|
+
/** Keep only the diamond TPMS lattice inside this shape. */
|
|
36999
|
+
fillWithDiamond(options) {
|
|
37000
|
+
return this.fillWith(diamond(options));
|
|
37001
|
+
}
|
|
37002
|
+
/** Keep only the lidinoid TPMS lattice inside this shape. */
|
|
37003
|
+
fillWithLidinoid(options) {
|
|
37004
|
+
return this.fillWith(lidinoid(options));
|
|
37005
|
+
}
|
|
35876
37006
|
/** Smooth union — blends shapes together with a smooth radius. */
|
|
35877
37007
|
smoothUnion(other, radius) {
|
|
35878
37008
|
return this.withNode({ kind: "sdf:smoothUnion", children: [this._node, other._node], radius });
|
|
@@ -35933,6 +37063,21 @@ class SdfShape {
|
|
|
35933
37063
|
repeat(spacing, count) {
|
|
35934
37064
|
return this.withNode({ kind: "sdf:repeat", child: this._node, spacing, count: count ?? [0, 0, 0] });
|
|
35935
37065
|
}
|
|
37066
|
+
/**
|
|
37067
|
+
* Arrange this SDF in a circular array around the Z axis.
|
|
37068
|
+
*
|
|
37069
|
+
* The source shape is translated by `offset` in +X before arraying. This uses
|
|
37070
|
+
* angular domain folding, so evaluation stays O(1): the source SDF is sampled
|
|
37071
|
+
* twice no matter how many copies are requested.
|
|
37072
|
+
*/
|
|
37073
|
+
circularArray(count, offset2 = 0) {
|
|
37074
|
+
return this.withNode({
|
|
37075
|
+
kind: "sdf:circularArray",
|
|
37076
|
+
child: this._node,
|
|
37077
|
+
count: requirePositiveInteger(count, "SdfShape.circularArray() count"),
|
|
37078
|
+
offset: requireNonNegativeFinite(offset2, "SdfShape.circularArray() offset")
|
|
37079
|
+
});
|
|
37080
|
+
}
|
|
35936
37081
|
/** Hollow out, keeping only a shell of given thickness. */
|
|
35937
37082
|
shell(thickness) {
|
|
35938
37083
|
return this.withNode({ kind: "sdf:shell", child: this._node, thickness });
|
|
@@ -35944,8 +37089,8 @@ class SdfShape {
|
|
|
35944
37089
|
* // Function displacement
|
|
35945
37090
|
* shape.displace((x, y, z) => Math.sin(x) * 0.5)
|
|
35946
37091
|
*
|
|
35947
|
-
* // Pattern displacement
|
|
35948
|
-
* shape.displace(sdf.
|
|
37092
|
+
* // Pattern displacement from a 3D SDF field
|
|
37093
|
+
* shape.displace(sdf.knurl({ pitch: 2, depth: 0.3 }))
|
|
35949
37094
|
* ```
|
|
35950
37095
|
*/
|
|
35951
37096
|
displace(fn, constants) {
|
|
@@ -35969,10 +37114,18 @@ class SdfShape {
|
|
|
35969
37114
|
* UV coordinates are in **surface millimeters** — patterns defined with `spacing: 3`
|
|
35970
37115
|
* always produce 3mm spacing, regardless of shape size.
|
|
35971
37116
|
*
|
|
37117
|
+
* Prefer `sdf.pattern2d()` or built-in surface patterns when the relief should
|
|
37118
|
+
* stay on the native shader and meshing path. Callback functions are supported
|
|
37119
|
+
* for experimentation, but they are opaque to the typed pattern optimizer.
|
|
37120
|
+
*
|
|
35972
37121
|
* ```js
|
|
35973
|
-
* //
|
|
37122
|
+
* // Native typed pattern — auto-detects sphere UV
|
|
37123
|
+
* const p = sdf.pattern2d()
|
|
37124
|
+
* const ribs = p.stripes({ spacing: 3, width: 0.8, depth: 0.35 })
|
|
37125
|
+
* .add(p.sineWave({ direction: [0, 1], wavelength: 14, amplitude: 0.08 }))
|
|
37126
|
+
*
|
|
35974
37127
|
* sdf.sphere(27).shell(3)
|
|
35975
|
-
* .surfaceDisplace(
|
|
37128
|
+
* .surfaceDisplace(ribs)
|
|
35976
37129
|
* .toShape()
|
|
35977
37130
|
*
|
|
35978
37131
|
* // Custom 2D pattern via function
|
|
@@ -35982,15 +37135,18 @@ class SdfShape {
|
|
|
35982
37135
|
surfaceDisplace(pattern, options) {
|
|
35983
37136
|
let body;
|
|
35984
37137
|
let constants;
|
|
37138
|
+
let typedPattern;
|
|
35985
37139
|
if (pattern instanceof SurfacePattern) {
|
|
35986
37140
|
body = pattern.body;
|
|
35987
37141
|
constants = pattern.constants;
|
|
37142
|
+
typedPattern = getTypedSurfacePattern(pattern);
|
|
35988
37143
|
} else {
|
|
35989
37144
|
body = extractFunctionBody(pattern);
|
|
35990
37145
|
}
|
|
35991
37146
|
return this.withNode({
|
|
35992
37147
|
kind: "sdf:surfaceDisplace",
|
|
35993
37148
|
child: this._node,
|
|
37149
|
+
...typedPattern ? { pattern: typedPattern } : {},
|
|
35994
37150
|
patternBody: body,
|
|
35995
37151
|
constants,
|
|
35996
37152
|
...(options == null ? void 0 : options.uv) ? { uvMode: options.uv } : {},
|
|
@@ -36209,24 +37365,10 @@ function weave(options) {
|
|
|
36209
37365
|
});
|
|
36210
37366
|
}
|
|
36211
37367
|
function basketWeave(options) {
|
|
36212
|
-
const SP = (options == null ? void 0 : options.spacing) ?? 3;
|
|
36213
|
-
const TW = (options == null ? void 0 : options.threadWidth) ?? 1.5;
|
|
36214
|
-
const D2 = (options == null ? void 0 : options.depth) ?? 0.8;
|
|
36215
|
-
|
|
36216
|
-
const body = `(function() {
|
|
36217
|
-
var su = u / ${SP};
|
|
36218
|
-
var sv = v / ${SP};
|
|
36219
|
-
var du = Math.abs(su - Math.round(su)) * ${SP};
|
|
36220
|
-
var dv = Math.abs(sv - Math.round(sv)) * ${SP};
|
|
36221
|
-
var hw = ${hw};
|
|
36222
|
-
var pU = Math.max(0, 1 - du / hw); pU *= pU;
|
|
36223
|
-
var pV = Math.max(0, 1 - dv / hw); pV *= pV;
|
|
36224
|
-
var checker = ((Math.round(su) & 65535) + (Math.round(sv) & 65535)) & 1;
|
|
36225
|
-
var top = checker ? pV : pU;
|
|
36226
|
-
var bot = checker ? pU : pV;
|
|
36227
|
-
return -(top > bot * 0.15 ? top : bot * 0.15) * ${D2};
|
|
36228
|
-
})()`;
|
|
36229
|
-
return new SurfacePattern(body);
|
|
37368
|
+
const SP = requirePositiveFinite((options == null ? void 0 : options.spacing) ?? 3, "sdf.basketWeave() spacing");
|
|
37369
|
+
const TW = requirePositiveFinite((options == null ? void 0 : options.threadWidth) ?? 1.5, "sdf.basketWeave() threadWidth");
|
|
37370
|
+
const D2 = requireNonNegativeFinite((options == null ? void 0 : options.depth) ?? 0.8, "sdf.basketWeave() depth");
|
|
37371
|
+
return pattern2d().overUnderWeave({ spacing: SP, threadWidth: TW, depth: D2 });
|
|
36230
37372
|
}
|
|
36231
37373
|
function fromFunction(fn, options) {
|
|
36232
37374
|
if (!options || typeof options !== "object") {
|
|
@@ -36259,6 +37401,9 @@ function bend(shape, radius) {
|
|
|
36259
37401
|
function repeat(shape, spacing, count) {
|
|
36260
37402
|
return shape.repeat(spacing, count);
|
|
36261
37403
|
}
|
|
37404
|
+
function circularArray(shape, count, offset2 = 0) {
|
|
37405
|
+
return shape.circularArray(count, offset2);
|
|
37406
|
+
}
|
|
36262
37407
|
function resolveTpmsOptions(options) {
|
|
36263
37408
|
const wallThickness = options.wallThickness;
|
|
36264
37409
|
const thickness = wallThickness ?? options.thickness;
|
|
@@ -36699,12 +37844,16 @@ const sdf = {
|
|
|
36699
37844
|
weave,
|
|
36700
37845
|
/** Basket weave surface pattern — threads with over-under crossings in UV space. Returns a SurfacePattern for use with `.surfaceDisplace()`. */
|
|
36701
37846
|
basketWeave,
|
|
37847
|
+
/** Create typed, composable 2D surface patterns for `.surfaceDisplace()`. */
|
|
37848
|
+
pattern2d,
|
|
36702
37849
|
/** Twist an SDF shape around the Z axis. */
|
|
36703
37850
|
twist,
|
|
36704
37851
|
/** Bend an SDF shape around the Z axis. */
|
|
36705
37852
|
bend,
|
|
36706
37853
|
/** Repeat an SDF shape in space. */
|
|
36707
37854
|
repeat,
|
|
37855
|
+
/** Arrange an SDF shape in a circular array around the Z axis with O(1) folded-domain evaluation. */
|
|
37856
|
+
circularArray,
|
|
36708
37857
|
/** A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`. */
|
|
36709
37858
|
SurfacePattern,
|
|
36710
37859
|
/** Create a custom SDF from one expression; shader-safe expressions raymarch directly. */
|
|
@@ -41638,13 +42787,16 @@ class SolvedAssembly {
|
|
|
41638
42787
|
* @category Assembly
|
|
41639
42788
|
*/
|
|
41640
42789
|
toGroup() {
|
|
42790
|
+
var _a3;
|
|
41641
42791
|
const children = [];
|
|
41642
42792
|
const childNames = [];
|
|
41643
|
-
|
|
42793
|
+
const childTags = [];
|
|
42794
|
+
for (const [name, rec] of this.parts) {
|
|
41644
42795
|
children.push(this.getPart(name));
|
|
41645
42796
|
childNames.push(name);
|
|
42797
|
+
childTags.push(normalizeSceneTags((_a3 = rec.metadata) == null ? void 0 : _a3.tags, `Assembly part "${name}" metadata.tags`));
|
|
41646
42798
|
}
|
|
41647
|
-
return new ShapeGroup(children, childNames);
|
|
42799
|
+
return new ShapeGroup(children, childNames, childTags);
|
|
41648
42800
|
}
|
|
41649
42801
|
/**
|
|
41650
42802
|
* Return an array of named scene objects for the viewport renderer.
|
|
@@ -41678,17 +42830,18 @@ class SolvedAssembly {
|
|
|
41678
42830
|
const used = usedByPart.get(partName);
|
|
41679
42831
|
if (used && used.length > 0) markShapePortsUsed(shape, used);
|
|
41680
42832
|
};
|
|
41681
|
-
const appendGroupChildren = (grp, prefix, partName, out2) => {
|
|
42833
|
+
const appendGroupChildren = (grp, prefix, partName, out2, inheritedTags = []) => {
|
|
41682
42834
|
grp.children.forEach((child, index2) => {
|
|
41683
42835
|
const childName = grp.childName(index2);
|
|
41684
42836
|
const label = childName ? `${prefix}.${childName}` : `${prefix}.${index2 + 1}`;
|
|
42837
|
+
const tags = mergeSceneTags(inheritedTags, grp.tagsForChild(index2));
|
|
41685
42838
|
if (child instanceof ShapeGroup) {
|
|
41686
|
-
appendGroupChildren(child, label, partName, out2);
|
|
42839
|
+
appendGroupChildren(child, label, partName, out2, tags);
|
|
41687
42840
|
return;
|
|
41688
42841
|
}
|
|
41689
42842
|
if (child instanceof Shape) {
|
|
41690
42843
|
markUsedOnShape(child, partName);
|
|
41691
|
-
out2.push({ name: label, shape: child });
|
|
42844
|
+
out2.push({ name: label, shape: child, ...tags.length > 0 ? { tags } : {} });
|
|
41692
42845
|
}
|
|
41693
42846
|
});
|
|
41694
42847
|
};
|
|
@@ -42054,7 +43207,7 @@ class Assembly {
|
|
|
42054
43207
|
*
|
|
42055
43208
|
* @param name - Unique part name (must not already exist)
|
|
42056
43209
|
* @param part - The `Shape` or `ShapeGroup` geometry
|
|
42057
|
-
* @param options - Optional `{ transform, metadata }` (material, process, qty, etc.)
|
|
43210
|
+
* @param options - Optional `{ transform, metadata }` (material, process, qty, tags, etc.)
|
|
42058
43211
|
* @returns `this` for chaining
|
|
42059
43212
|
* @category Assembly
|
|
42060
43213
|
*/
|
|
@@ -43008,15 +44161,18 @@ class ImportedAssembly {
|
|
|
43008
44161
|
* Any stored placement offset and placement references are forwarded to the group.
|
|
43009
44162
|
*/
|
|
43010
44163
|
toGroup(state) {
|
|
44164
|
+
var _a3;
|
|
43011
44165
|
const solved = this._assembly.solve(state);
|
|
43012
44166
|
const def = this._assembly.describe();
|
|
43013
44167
|
const children = [];
|
|
43014
44168
|
const childNames = [];
|
|
44169
|
+
const childTags = [];
|
|
43015
44170
|
for (const p2 of def.parts) {
|
|
43016
44171
|
children.push(solved.getPart(p2.name));
|
|
43017
44172
|
childNames.push(p2.name);
|
|
44173
|
+
childTags.push(normalizeSceneTags((_a3 = p2.metadata) == null ? void 0 : _a3.tags, `Assembly part "${p2.name}" metadata.tags`));
|
|
43018
44174
|
}
|
|
43019
|
-
let result = new ShapeGroup(children, childNames);
|
|
44175
|
+
let result = new ShapeGroup(children, childNames, childTags);
|
|
43020
44176
|
const [dx, dy, dz] = this._offset;
|
|
43021
44177
|
if (dx !== 0 || dy !== 0 || dz !== 0) {
|
|
43022
44178
|
result = result.translate(dx, dy, dz);
|
|
@@ -43386,8 +44542,8 @@ function buildPure3mfBuffer(objects, options = {}) {
|
|
|
43386
44542
|
for (let t = 0; t < numTri; t++) {
|
|
43387
44543
|
const v1 = triVerts[t * 3];
|
|
43388
44544
|
const v2 = triVerts[t * 3 + 1];
|
|
43389
|
-
const
|
|
43390
|
-
xmlParts.push(` <triangle v1="${v1}" v2="${v2}" v3="${
|
|
44545
|
+
const v32 = triVerts[t * 3 + 2];
|
|
44546
|
+
xmlParts.push(` <triangle v1="${v1}" v2="${v2}" v3="${v32}" />`);
|
|
43391
44547
|
}
|
|
43392
44548
|
xmlParts.push(" </triangles>");
|
|
43393
44549
|
xmlParts.push(" </mesh>");
|
|
@@ -43813,7 +44969,7 @@ class GCodeBuilder {
|
|
|
43813
44969
|
this.lines.push("G1 E-0.8 F1800 ; retract");
|
|
43814
44970
|
this.lines.push("");
|
|
43815
44971
|
const safeZ = Math.min(maxZ + 5, p2.bedZ - 1);
|
|
43816
|
-
this.lines.push(`G1 Z${
|
|
44972
|
+
this.lines.push(`G1 Z${f$1(safeZ)} F900 ; lift nozzle above print`);
|
|
43817
44973
|
this.lines.push("");
|
|
43818
44974
|
this.lines.push("M140 S0 ; bed off");
|
|
43819
44975
|
this.lines.push("M104 S0 ; hotend off");
|
|
@@ -43857,7 +45013,7 @@ class GCodeBuilder {
|
|
|
43857
45013
|
travelTo(x2, y2, z2) {
|
|
43858
45014
|
this.retract();
|
|
43859
45015
|
const from = [...this.pos];
|
|
43860
|
-
this.lines.push(`G0 X${
|
|
45016
|
+
this.lines.push(`G0 X${f$1(x2)} Y${f$1(y2)} Z${f$1(z2)} F${Math.round(this.profile.travelSpeed)}`);
|
|
43861
45017
|
if (this.posInitialized) {
|
|
43862
45018
|
this._segments.push({ from, to: [x2, y2, z2], extrude: false, speed: this.profile.travelSpeed });
|
|
43863
45019
|
}
|
|
@@ -43889,7 +45045,7 @@ class GCodeBuilder {
|
|
|
43889
45045
|
const beadArea = this.profile.layerHeight * this.profile.nozzle;
|
|
43890
45046
|
const eIncrement = beadArea * dist4 / this.filamentArea;
|
|
43891
45047
|
this.e += eIncrement;
|
|
43892
|
-
this.lines.push(`G1 X${
|
|
45048
|
+
this.lines.push(`G1 X${f$1(x2)} Y${f$1(y2)} Z${f$1(z2)} E${f$1(this.e)} F${Math.round(this.currentSpeed)}`);
|
|
43893
45049
|
if (this.posInitialized) {
|
|
43894
45050
|
this._segments.push({ from, to: [x2, y2, z2], extrude: true, speed: this.currentSpeed });
|
|
43895
45051
|
}
|
|
@@ -43987,13 +45143,13 @@ class GCodeBuilder {
|
|
|
43987
45143
|
retract() {
|
|
43988
45144
|
if (this.retracted) return;
|
|
43989
45145
|
this.e -= this.profile.retractionDistance;
|
|
43990
|
-
this.lines.push(`G1 E${
|
|
45146
|
+
this.lines.push(`G1 E${f$1(this.e)} F${Math.round(this.profile.retractionSpeed)}`);
|
|
43991
45147
|
this.retracted = true;
|
|
43992
45148
|
}
|
|
43993
45149
|
unretract() {
|
|
43994
45150
|
if (!this.retracted) return;
|
|
43995
45151
|
this.e += this.profile.retractionDistance;
|
|
43996
|
-
this.lines.push(`G1 E${
|
|
45152
|
+
this.lines.push(`G1 E${f$1(this.e)} F${Math.round(this.profile.retractionSpeed)}`);
|
|
43997
45153
|
this.retracted = false;
|
|
43998
45154
|
}
|
|
43999
45155
|
// ---- Bounds tracking ----
|
|
@@ -44064,7 +45220,7 @@ class GCodeBuilder {
|
|
|
44064
45220
|
return this.lines.join("\n") + "\n";
|
|
44065
45221
|
}
|
|
44066
45222
|
}
|
|
44067
|
-
function
|
|
45223
|
+
function f$1(n) {
|
|
44068
45224
|
return n.toFixed(5).replace(/\.?0+$/, "");
|
|
44069
45225
|
}
|
|
44070
45226
|
function bambuModelName(preset) {
|
|
@@ -46208,10 +47364,8 @@ class PathBuilder {
|
|
|
46208
47364
|
if (radius <= 0) throw new Error("fillet: radius must be positive");
|
|
46209
47365
|
const n = this.segs.length;
|
|
46210
47366
|
if (n < 2) throw new Error("fillet: need at least 2 segments before a fillet");
|
|
46211
|
-
const prev = this.segs[n - 2];
|
|
46212
47367
|
const curr = this.segs[n - 1];
|
|
46213
|
-
|
|
46214
|
-
const { trimA, trimB, arcSeg } = this.computeFilletGeom(radius);
|
|
47368
|
+
const { trimA, arcSeg } = this.computeFilletGeom(radius);
|
|
46215
47369
|
if (!arcSeg) throw new Error("fillet: cannot fillet these segments (parallel or degenerate)");
|
|
46216
47370
|
this.trimLastSegEnd(n - 2, trimA[0], trimA[1]);
|
|
46217
47371
|
const trimmedSeg = { ...curr };
|
|
@@ -46283,7 +47437,6 @@ class PathBuilder {
|
|
|
46283
47437
|
}
|
|
46284
47438
|
getSegDirAt(seg, which) {
|
|
46285
47439
|
if (seg.kind === "line" || seg.kind === "move") {
|
|
46286
|
-
this.segs.length;
|
|
46287
47440
|
const idx = this.segs.indexOf(seg);
|
|
46288
47441
|
if (seg.kind === "line") {
|
|
46289
47442
|
let sx, sy;
|
|
@@ -46525,6 +47678,41 @@ class PathBuilder {
|
|
|
46525
47678
|
}
|
|
46526
47679
|
return pts;
|
|
46527
47680
|
}
|
|
47681
|
+
/**
|
|
47682
|
+
* Return the open path as a sampled 2D polyline.
|
|
47683
|
+
*
|
|
47684
|
+
* This is for construction geometry such as guide rails, measured centerlines,
|
|
47685
|
+
* and curve-driven helpers where the authored path should stay open instead of
|
|
47686
|
+
* becoming a filled sketch or stroked profile.
|
|
47687
|
+
*
|
|
47688
|
+
* **Example**
|
|
47689
|
+
*
|
|
47690
|
+
* ```ts
|
|
47691
|
+
* const rail = path()
|
|
47692
|
+
* .moveTo(24, 0)
|
|
47693
|
+
* .bezierTo(32, 44, 28, 92, 18, 120)
|
|
47694
|
+
* .toPolyline();
|
|
47695
|
+
* ```
|
|
47696
|
+
*
|
|
47697
|
+
* @returns A sampled open polyline.
|
|
47698
|
+
* @category Path Builder
|
|
47699
|
+
*/
|
|
47700
|
+
toPolyline() {
|
|
47701
|
+
const moveCount = this.segs.filter((seg) => seg.kind === "move").length;
|
|
47702
|
+
if (moveCount > 1) {
|
|
47703
|
+
throw new Error("path().toPolyline() supports one continuous open path. Use separate path() builders for separate rails.");
|
|
47704
|
+
}
|
|
47705
|
+
const pts = [];
|
|
47706
|
+
for (const point2 of this.tessellate()) {
|
|
47707
|
+
if (!point2.every(Number.isFinite)) throw new Error("path().toPolyline() produced a non-finite point");
|
|
47708
|
+
const previous = pts[pts.length - 1];
|
|
47709
|
+
if (!previous || Math.hypot(point2[0] - previous[0], point2[1] - previous[1]) > 1e-9) {
|
|
47710
|
+
pts.push(point2);
|
|
47711
|
+
}
|
|
47712
|
+
}
|
|
47713
|
+
if (pts.length < 2) throw new Error("path().toPolyline() needs at least 2 points");
|
|
47714
|
+
return pts;
|
|
47715
|
+
}
|
|
46528
47716
|
// ── Output ────────────────────────────────────────────────────────────────
|
|
46529
47717
|
/**
|
|
46530
47718
|
* Close the path and return a filled `Sketch`.
|
|
@@ -47575,7 +48763,8 @@ function explode(items, options = {}) {
|
|
|
47575
48763
|
if (child instanceof ShapeGroup) return explodeGroup(child, p2, depth + 1, total, groupCenter, motion.branchDirection);
|
|
47576
48764
|
return explodeLeaf(child, explodeAdd(total, leafMotion(child, p2, depth + 1, groupCenter, motion.branchDirection).offset));
|
|
47577
48765
|
}),
|
|
47578
|
-
grp.childNames
|
|
48766
|
+
grp.childNames,
|
|
48767
|
+
grp.children.map((_2, i) => grp.tagsForChild(i))
|
|
47579
48768
|
);
|
|
47580
48769
|
};
|
|
47581
48770
|
const explodeItemNode = (item, path2, depth, inherited, parentCenter, parentDirection) => {
|
|
@@ -47611,7 +48800,8 @@ function explode(items, options = {}) {
|
|
|
47611
48800
|
if (child instanceof ShapeGroup) return explodeGroup(child, p2, 1, [0, 0, 0], rootCenter, void 0);
|
|
47612
48801
|
return explodeLeaf(child, nodeMotion(child, p2, 1, rootCenter, void 0).offset);
|
|
47613
48802
|
}),
|
|
47614
|
-
items.childNames
|
|
48803
|
+
items.childNames,
|
|
48804
|
+
items.children.map((_2, i) => items.tagsForChild(i))
|
|
47615
48805
|
);
|
|
47616
48806
|
}
|
|
47617
48807
|
return items.map((item, i) => {
|
|
@@ -48380,6 +49570,398 @@ function spurGear(options) {
|
|
|
48380
49570
|
});
|
|
48381
49571
|
return attachGearMeta(shapeWithConnectors, meta2);
|
|
48382
49572
|
}
|
|
49573
|
+
function requirePositive$7(scope, name, value) {
|
|
49574
|
+
if (!isFinitePositive(value)) throw new Error(`${scope}: "${name}" must be > 0`);
|
|
49575
|
+
}
|
|
49576
|
+
function requireOptionalBore(scope, boreDiameter, maxDiameter) {
|
|
49577
|
+
const bore = boreDiameter ?? 0;
|
|
49578
|
+
if (!Number.isFinite(bore) || bore < 0) throw new Error(`${scope}: "boreDiameter" must be >= 0`);
|
|
49579
|
+
if (bore > 0 && bore >= maxDiameter) throw new Error(`${scope}: bore is too large for the body`);
|
|
49580
|
+
return bore;
|
|
49581
|
+
}
|
|
49582
|
+
function resolveSegments(segments) {
|
|
49583
|
+
if (segments === void 0) return void 0;
|
|
49584
|
+
if (!Number.isInteger(segments) || segments < 12) throw new Error('gear body: "segments" must be an integer >= 12');
|
|
49585
|
+
return segments;
|
|
49586
|
+
}
|
|
49587
|
+
function cutBore$1(shape, boreDiameter) {
|
|
49588
|
+
if (boreDiameter <= 0) return shape;
|
|
49589
|
+
const bounds = shape.boundingBox();
|
|
49590
|
+
const height = bounds.max[2] - bounds.min[2] + 2;
|
|
49591
|
+
const cutter = cylinder(height, boreDiameter * 0.5, void 0, 64).translate(0, 0, bounds.min[2] - 1);
|
|
49592
|
+
return shape.subtract(cutter);
|
|
49593
|
+
}
|
|
49594
|
+
function gearBodyDisk(options) {
|
|
49595
|
+
requirePositive$7("gearBodyDisk", "outerRadius", options.outerRadius);
|
|
49596
|
+
requirePositive$7("gearBodyDisk", "faceWidth", options.faceWidth);
|
|
49597
|
+
const bore = requireOptionalBore("gearBodyDisk", options.boreDiameter, options.outerRadius * 2);
|
|
49598
|
+
const segments = resolveSegments(options.segments);
|
|
49599
|
+
const outer = circle2d(options.outerRadius, segments);
|
|
49600
|
+
const profile = bore > 0 ? difference2d(outer, circle2d(bore * 0.5, segments)) : outer;
|
|
49601
|
+
return sketchExtrude(profile, options.faceWidth);
|
|
49602
|
+
}
|
|
49603
|
+
function gearBodyDiskWithHub(options) {
|
|
49604
|
+
requirePositive$7("gearBodyDiskWithHub", "hubDiameter", options.hubDiameter);
|
|
49605
|
+
if (options.hubDiameter >= options.outerRadius * 2) {
|
|
49606
|
+
throw new Error('gearBodyDiskWithHub: "hubDiameter" must be smaller than the outer diameter');
|
|
49607
|
+
}
|
|
49608
|
+
const bore = requireOptionalBore("gearBodyDiskWithHub", options.boreDiameter, options.hubDiameter);
|
|
49609
|
+
const base = gearBodyDisk({ ...options, boreDiameter: 0 });
|
|
49610
|
+
const hubFaceWidth = options.hubFaceWidth ?? options.faceWidth * 1.5;
|
|
49611
|
+
requirePositive$7("gearBodyDiskWithHub", "hubFaceWidth", hubFaceWidth);
|
|
49612
|
+
const hub = cylinder(hubFaceWidth, options.hubDiameter * 0.5, void 0, options.segments).translate(
|
|
49613
|
+
0,
|
|
49614
|
+
0,
|
|
49615
|
+
(options.faceWidth - hubFaceWidth) * 0.5
|
|
49616
|
+
);
|
|
49617
|
+
return cutBore$1(base.add(hub), bore);
|
|
49618
|
+
}
|
|
49619
|
+
function gearBodySpoked(options) {
|
|
49620
|
+
requirePositive$7("gearBodySpoked", "outerRadius", options.outerRadius);
|
|
49621
|
+
requirePositive$7("gearBodySpoked", "faceWidth", options.faceWidth);
|
|
49622
|
+
requirePositive$7("gearBodySpoked", "rimWidth", options.rimWidth);
|
|
49623
|
+
requirePositive$7("gearBodySpoked", "hubDiameter", options.hubDiameter);
|
|
49624
|
+
requirePositive$7("gearBodySpoked", "spokeWidth", options.spokeWidth);
|
|
49625
|
+
if (!Number.isInteger(options.spokeCount) || options.spokeCount < 2) {
|
|
49626
|
+
throw new Error('gearBodySpoked: "spokeCount" must be an integer >= 2');
|
|
49627
|
+
}
|
|
49628
|
+
const hubRadius = options.hubDiameter * 0.5;
|
|
49629
|
+
const rimInnerRadius = options.outerRadius - options.rimWidth;
|
|
49630
|
+
if (rimInnerRadius <= hubRadius) throw new Error("gearBodySpoked: rim overlaps the hub");
|
|
49631
|
+
const bore = requireOptionalBore("gearBodySpoked", options.boreDiameter, options.hubDiameter);
|
|
49632
|
+
const segments = resolveSegments(options.segments);
|
|
49633
|
+
const rim = difference2d(circle2d(options.outerRadius, segments), circle2d(rimInnerRadius, segments));
|
|
49634
|
+
const hub = circle2d(hubRadius, segments);
|
|
49635
|
+
const spokeLength = rimInnerRadius - hubRadius + options.spokeWidth;
|
|
49636
|
+
const spokeCenter = hubRadius + spokeLength * 0.5 - options.spokeWidth * 0.5;
|
|
49637
|
+
const spoke = sketchTranslate(rect(spokeLength, options.spokeWidth), spokeCenter, 0);
|
|
49638
|
+
const spokes = [];
|
|
49639
|
+
for (let i = 0; i < options.spokeCount; i++) {
|
|
49640
|
+
spokes.push(sketchRotateAround(spoke, 360 / options.spokeCount * i, [0, 0]));
|
|
49641
|
+
}
|
|
49642
|
+
const profile = bore > 0 ? difference2d(union2d(rim, hub, ...spokes), circle2d(bore * 0.5, segments)) : union2d(rim, hub, ...spokes);
|
|
49643
|
+
return sketchExtrude(profile, options.faceWidth);
|
|
49644
|
+
}
|
|
49645
|
+
function gearBodyFromProfile(profile, options) {
|
|
49646
|
+
if (!(profile instanceof Sketch)) throw new Error('gearBodyFromProfile: "profile" must be a Sketch');
|
|
49647
|
+
requirePositive$7("gearBodyFromProfile", "faceWidth", options.faceWidth);
|
|
49648
|
+
const bore = options.boreDiameter ?? 0;
|
|
49649
|
+
if (!Number.isFinite(bore) || bore < 0) throw new Error('gearBodyFromProfile: "boreDiameter" must be >= 0');
|
|
49650
|
+
return cutBore$1(sketchExtrude(profile, options.faceWidth), bore);
|
|
49651
|
+
}
|
|
49652
|
+
function requirePositive$6(scope, name, value) {
|
|
49653
|
+
if (!isFinitePositive(value)) throw new Error(`${scope}: "${name}" must be > 0`);
|
|
49654
|
+
}
|
|
49655
|
+
function requireFiniteAngle(scope, name, value) {
|
|
49656
|
+
if (value !== void 0 && !Number.isFinite(value)) throw new Error(`${scope}: "${name}" must be finite`);
|
|
49657
|
+
}
|
|
49658
|
+
function cutBore(shape, boreDiameter) {
|
|
49659
|
+
if (boreDiameter <= 0) return shape;
|
|
49660
|
+
const bounds = shape.boundingBox();
|
|
49661
|
+
const height = bounds.max[2] - bounds.min[2] + 2;
|
|
49662
|
+
const cutter = cylinder(height, boreDiameter * 0.5, void 0, 64).translate(0, 0, bounds.min[2] - 1);
|
|
49663
|
+
return shape.subtract(cutter);
|
|
49664
|
+
}
|
|
49665
|
+
function bodyOuterRadius(shape) {
|
|
49666
|
+
const bounds = shape.boundingBox();
|
|
49667
|
+
return Math.max(Math.abs(bounds.min[0]), Math.abs(bounds.max[0]), Math.abs(bounds.min[1]), Math.abs(bounds.max[1]));
|
|
49668
|
+
}
|
|
49669
|
+
function buildSpurTeethRegion(options, name, faceWidth) {
|
|
49670
|
+
const scope = "driveWheel.addSpurTeethBetween";
|
|
49671
|
+
const teethOnFullCircle = options.teethOnFullCircle;
|
|
49672
|
+
if (!Number.isInteger(teethOnFullCircle) || teethOnFullCircle < 6) {
|
|
49673
|
+
throw new Error(`${scope}: "teethOnFullCircle" must be an integer >= 6`);
|
|
49674
|
+
}
|
|
49675
|
+
const toothCount = options.toothCount;
|
|
49676
|
+
if (!Number.isInteger(toothCount) || toothCount < 1 || toothCount > teethOnFullCircle) {
|
|
49677
|
+
throw new Error(`${scope}: "toothCount" must be an integer in [1, teethOnFullCircle]`);
|
|
49678
|
+
}
|
|
49679
|
+
const firstTooth = options.firstTooth ?? 0;
|
|
49680
|
+
if (!Number.isInteger(firstTooth) || firstTooth < 0 || firstTooth >= teethOnFullCircle) {
|
|
49681
|
+
throw new Error(`${scope}: "firstTooth" must be an integer in [0, teethOnFullCircle)`);
|
|
49682
|
+
}
|
|
49683
|
+
let normalized;
|
|
49684
|
+
try {
|
|
49685
|
+
normalized = normalizeSpurGearOptions({ ...options, teeth: teethOnFullCircle, faceWidth, boreDiameter: 0 });
|
|
49686
|
+
} catch (error) {
|
|
49687
|
+
remapErrorPrefix(error, "spurGear", scope);
|
|
49688
|
+
}
|
|
49689
|
+
const gearMeta = buildSpurGearMeta(normalized);
|
|
49690
|
+
const pitchStepDeg = 360 / teethOnFullCircle;
|
|
49691
|
+
const fromAngleDeg = firstTooth * pitchStepDeg - pitchStepDeg * 0.5;
|
|
49692
|
+
const toAngleDeg = (firstTooth + toothCount - 1) * pitchStepDeg + pitchStepDeg * 0.5;
|
|
49693
|
+
const profile = buildSpurToothRegionProfile(gearMeta, firstTooth, toothCount, normalized.segmentsPerTooth);
|
|
49694
|
+
return {
|
|
49695
|
+
shape: sketchExtrude(profile, faceWidth),
|
|
49696
|
+
gearMeta,
|
|
49697
|
+
meta: {
|
|
49698
|
+
name,
|
|
49699
|
+
kind: "spurTeeth",
|
|
49700
|
+
fromAngleDeg,
|
|
49701
|
+
toAngleDeg,
|
|
49702
|
+
outerRadius: gearMeta.outerRadius,
|
|
49703
|
+
rootRadius: gearMeta.rootRadius,
|
|
49704
|
+
pitchRadius: gearMeta.pitchRadius,
|
|
49705
|
+
module: normalized.module,
|
|
49706
|
+
teethOnFullCircle,
|
|
49707
|
+
toothCount,
|
|
49708
|
+
faceWidth
|
|
49709
|
+
}
|
|
49710
|
+
};
|
|
49711
|
+
}
|
|
49712
|
+
function buildSolidArcRegion(options, name, faceWidth) {
|
|
49713
|
+
const scope = "driveWheel.addSolidArcBetween";
|
|
49714
|
+
requirePositive$6(scope, "outerRadius", options.outerRadius);
|
|
49715
|
+
const innerRadius = options.innerRadius ?? 0;
|
|
49716
|
+
if (!Number.isFinite(innerRadius) || innerRadius < 0) throw new Error(`${scope}: "innerRadius" must be >= 0`);
|
|
49717
|
+
if (innerRadius >= options.outerRadius) throw new Error(`${scope}: "innerRadius" must be smaller than "outerRadius"`);
|
|
49718
|
+
const sweepDeg = normalizedSweep(scope, options.fromAngleDeg, options.toAngleDeg);
|
|
49719
|
+
return {
|
|
49720
|
+
shape: sketchExtrude(buildSolidArcProfile(options, sweepDeg), faceWidth),
|
|
49721
|
+
meta: {
|
|
49722
|
+
name,
|
|
49723
|
+
kind: "solidArc",
|
|
49724
|
+
fromAngleDeg: options.fromAngleDeg,
|
|
49725
|
+
toAngleDeg: options.fromAngleDeg + sweepDeg,
|
|
49726
|
+
innerRadius,
|
|
49727
|
+
outerRadius: options.outerRadius,
|
|
49728
|
+
faceWidth
|
|
49729
|
+
}
|
|
49730
|
+
};
|
|
49731
|
+
}
|
|
49732
|
+
function normalizedSweep(scope, fromAngleDeg, toAngleDeg) {
|
|
49733
|
+
if (!Number.isFinite(fromAngleDeg)) throw new Error(`${scope}: "fromAngleDeg" must be finite`);
|
|
49734
|
+
if (!Number.isFinite(toAngleDeg)) throw new Error(`${scope}: "toAngleDeg" must be finite`);
|
|
49735
|
+
let sweep2 = toAngleDeg - fromAngleDeg;
|
|
49736
|
+
while (sweep2 <= 0) sweep2 += 360;
|
|
49737
|
+
if (sweep2 > 360 + EPSILON$1) throw new Error(`${scope}: angular sweep must be <= 360 degrees`);
|
|
49738
|
+
return Math.min(360, sweep2);
|
|
49739
|
+
}
|
|
49740
|
+
function buildSpurToothRegionProfile(meta2, firstTooth, toothCount, segmentsPerTooth) {
|
|
49741
|
+
const tooth = createSpurToothSketch(meta2, segmentsPerTooth);
|
|
49742
|
+
const teeth = [];
|
|
49743
|
+
for (let i = 0; i < toothCount; i++) {
|
|
49744
|
+
teeth.push(sketchRotateAround(tooth, 360 / meta2.teeth * (firstTooth + i), [0, 0]));
|
|
49745
|
+
}
|
|
49746
|
+
return union2d(...teeth);
|
|
49747
|
+
}
|
|
49748
|
+
function buildSolidArcProfile(options, sweepDeg) {
|
|
49749
|
+
const innerRadius = options.innerRadius ?? 0;
|
|
49750
|
+
const segments = options.segments ?? Math.max(16, Math.ceil(sweepDeg / 6));
|
|
49751
|
+
if (!Number.isInteger(segments) || segments < 4) throw new Error('driveWheel.addSolidArcBetween: "segments" must be an integer >= 4');
|
|
49752
|
+
if (Math.abs(sweepDeg - 360) < EPSILON$1) {
|
|
49753
|
+
const outer = circle2d(options.outerRadius, segments);
|
|
49754
|
+
return innerRadius > 0 ? difference2d(outer, circle2d(innerRadius, segments)) : outer;
|
|
49755
|
+
}
|
|
49756
|
+
const start = options.fromAngleDeg * Math.PI / 180;
|
|
49757
|
+
const end = start + sweepDeg * Math.PI / 180;
|
|
49758
|
+
const pts = [];
|
|
49759
|
+
if (innerRadius <= 0) pts.push([0, 0]);
|
|
49760
|
+
addArcPoints(pts, options.outerRadius, start, end, segments, true, true);
|
|
49761
|
+
if (innerRadius > 0) addArcPoints(pts, innerRadius, end, start, segments, true, true);
|
|
49762
|
+
return polygon(pts);
|
|
49763
|
+
}
|
|
49764
|
+
const DRIVE_WHEEL_META_KEY = Symbol.for("forgecad.library.driveWheelMeta");
|
|
49765
|
+
function attachDriveWheelMeta(shape, meta2) {
|
|
49766
|
+
shape[DRIVE_WHEEL_META_KEY] = meta2;
|
|
49767
|
+
return shape;
|
|
49768
|
+
}
|
|
49769
|
+
function readDriveWheelMeta(shape) {
|
|
49770
|
+
const meta2 = shape[DRIVE_WHEEL_META_KEY];
|
|
49771
|
+
return meta2 ?? null;
|
|
49772
|
+
}
|
|
49773
|
+
class DriveWheelBuilder {
|
|
49774
|
+
constructor(options = {}) {
|
|
49775
|
+
__publicField(this, "body");
|
|
49776
|
+
__publicField(this, "faceWidth");
|
|
49777
|
+
__publicField(this, "boreDiameter");
|
|
49778
|
+
__publicField(this, "regions", []);
|
|
49779
|
+
if (options.body !== void 0 && !(options.body instanceof Shape)) throw new Error('driveWheel: "body" must be a Shape');
|
|
49780
|
+
if (options.faceWidth !== void 0) requirePositive$6("driveWheel", "faceWidth", options.faceWidth);
|
|
49781
|
+
const boreDiameter = options.boreDiameter ?? 0;
|
|
49782
|
+
if (!Number.isFinite(boreDiameter) || boreDiameter < 0) throw new Error('driveWheel: "boreDiameter" must be >= 0');
|
|
49783
|
+
this.body = options.body;
|
|
49784
|
+
this.faceWidth = options.faceWidth;
|
|
49785
|
+
this.boreDiameter = boreDiameter;
|
|
49786
|
+
}
|
|
49787
|
+
/**
|
|
49788
|
+
* Add an involute spur-tooth window on part of the pitch circle.
|
|
49789
|
+
*/
|
|
49790
|
+
addSpurTeethBetween(options) {
|
|
49791
|
+
const faceWidth = this.resolveFaceWidth("driveWheel.addSpurTeethBetween", options.faceWidth);
|
|
49792
|
+
this.regions.push(buildSpurTeethRegion(options, this.resolveName("teeth", options.name), faceWidth));
|
|
49793
|
+
return this;
|
|
49794
|
+
}
|
|
49795
|
+
/**
|
|
49796
|
+
* Add a constant-radius solid arc region such as a dwell, stop, or pusher.
|
|
49797
|
+
*/
|
|
49798
|
+
addSolidArcBetween(options) {
|
|
49799
|
+
const faceWidth = this.resolveFaceWidth("driveWheel.addSolidArcBetween", options.faceWidth);
|
|
49800
|
+
this.regions.push(buildSolidArcRegion(options, this.resolveName("arc", options.name), faceWidth));
|
|
49801
|
+
return this;
|
|
49802
|
+
}
|
|
49803
|
+
/**
|
|
49804
|
+
* Add a fully custom region shape while preserving region metadata.
|
|
49805
|
+
*/
|
|
49806
|
+
addShapeRegion(name, shape, options = {}) {
|
|
49807
|
+
const scope = "driveWheel.addShapeRegion";
|
|
49808
|
+
if (typeof name !== "string" || name.trim().length === 0) throw new Error(`${scope}: "name" must be a non-empty string`);
|
|
49809
|
+
if (!(shape instanceof Shape)) throw new Error(`${scope}: "shape" must be a Shape`);
|
|
49810
|
+
requireFiniteAngle(scope, "fromAngleDeg", options.fromAngleDeg);
|
|
49811
|
+
requireFiniteAngle(scope, "toAngleDeg", options.toAngleDeg);
|
|
49812
|
+
if (options.innerRadius !== void 0 && (!Number.isFinite(options.innerRadius) || options.innerRadius < 0)) {
|
|
49813
|
+
throw new Error(`${scope}: "innerRadius" must be >= 0`);
|
|
49814
|
+
}
|
|
49815
|
+
if (options.outerRadius !== void 0) requirePositive$6(scope, "outerRadius", options.outerRadius);
|
|
49816
|
+
this.regions.push({
|
|
49817
|
+
shape: shape.clone(),
|
|
49818
|
+
meta: {
|
|
49819
|
+
name: this.resolveName("region", name),
|
|
49820
|
+
kind: "custom",
|
|
49821
|
+
...options
|
|
49822
|
+
}
|
|
49823
|
+
});
|
|
49824
|
+
return this;
|
|
49825
|
+
}
|
|
49826
|
+
/**
|
|
49827
|
+
* Build the final wheel shape with a bore connector and region metadata.
|
|
49828
|
+
*/
|
|
49829
|
+
build() {
|
|
49830
|
+
var _a3, _b3;
|
|
49831
|
+
if (this.regions.length === 0 && this.body === void 0) {
|
|
49832
|
+
throw new Error("driveWheel: add a body or at least one region before build()");
|
|
49833
|
+
}
|
|
49834
|
+
const faceWidth = this.resolveBuildFaceWidth();
|
|
49835
|
+
const firstGearRegion = (_a3 = this.regions.find((region) => region.gearMeta)) == null ? void 0 : _a3.gearMeta;
|
|
49836
|
+
if (firstGearRegion && this.boreDiameter * 0.5 >= firstGearRegion.rootRadius - EPSILON$1) {
|
|
49837
|
+
throw new Error("driveWheel: bore is too large for the first spur-tooth region");
|
|
49838
|
+
}
|
|
49839
|
+
const body = ((_b3 = this.body) == null ? void 0 : _b3.clone()) ?? gearBodyDisk({ outerRadius: (firstGearRegion == null ? void 0 : firstGearRegion.rootRadius) ?? this.defaultBodyRadius(), faceWidth });
|
|
49840
|
+
let combined = body;
|
|
49841
|
+
for (const region of this.regions) combined = combined.add(region.shape);
|
|
49842
|
+
combined = cutBore(combined, this.boreDiameter);
|
|
49843
|
+
const withConnectors = combined.withConnectors({
|
|
49844
|
+
bore: connectorFactory(
|
|
49845
|
+
"drive-wheel-bore",
|
|
49846
|
+
{ origin: [0, 0, faceWidth / 2], axis: [0, 0, 1], kind: "revolute" },
|
|
49847
|
+
this.measurements(faceWidth)
|
|
49848
|
+
)
|
|
49849
|
+
});
|
|
49850
|
+
return attachDriveWheelMeta(withConnectors, {
|
|
49851
|
+
kind: "driveWheel",
|
|
49852
|
+
faceWidth,
|
|
49853
|
+
boreDiameter: this.boreDiameter,
|
|
49854
|
+
regions: this.regionMetadata(body, faceWidth)
|
|
49855
|
+
});
|
|
49856
|
+
}
|
|
49857
|
+
measurements(faceWidth) {
|
|
49858
|
+
var _a3;
|
|
49859
|
+
const firstGearRegion = (_a3 = this.regions.find((region) => region.gearMeta)) == null ? void 0 : _a3.gearMeta;
|
|
49860
|
+
return {
|
|
49861
|
+
faceWidth,
|
|
49862
|
+
boreDiameter: this.boreDiameter,
|
|
49863
|
+
regionCount: this.regions.length,
|
|
49864
|
+
...firstGearRegion ? {
|
|
49865
|
+
module: firstGearRegion.module,
|
|
49866
|
+
teethOnFullCircle: firstGearRegion.teeth,
|
|
49867
|
+
pitchRadius: firstGearRegion.pitchRadius,
|
|
49868
|
+
outerRadius: firstGearRegion.outerRadius
|
|
49869
|
+
} : {}
|
|
49870
|
+
};
|
|
49871
|
+
}
|
|
49872
|
+
regionMetadata(body, faceWidth) {
|
|
49873
|
+
return [
|
|
49874
|
+
{ name: "body", kind: "body", outerRadius: bodyOuterRadius(body), faceWidth },
|
|
49875
|
+
...this.regions.map((region) => ({ ...region.meta }))
|
|
49876
|
+
];
|
|
49877
|
+
}
|
|
49878
|
+
resolveFaceWidth(scope, localFaceWidth) {
|
|
49879
|
+
const faceWidth = localFaceWidth ?? this.faceWidth;
|
|
49880
|
+
if (faceWidth === void 0) throw new Error(`${scope}: "faceWidth" is required unless driveWheel({ faceWidth }) was set`);
|
|
49881
|
+
requirePositive$6(scope, "faceWidth", faceWidth);
|
|
49882
|
+
if (this.faceWidth !== void 0 && localFaceWidth !== void 0 && Math.abs(this.faceWidth - localFaceWidth) > EPSILON$1) {
|
|
49883
|
+
throw new Error(`${scope}: region faceWidth must match driveWheel faceWidth`);
|
|
49884
|
+
}
|
|
49885
|
+
return faceWidth;
|
|
49886
|
+
}
|
|
49887
|
+
resolveBuildFaceWidth() {
|
|
49888
|
+
var _a3;
|
|
49889
|
+
const faceWidth = this.faceWidth ?? ((_a3 = this.regions.find((region) => region.meta.faceWidth !== void 0)) == null ? void 0 : _a3.meta.faceWidth);
|
|
49890
|
+
if (faceWidth === void 0) throw new Error('driveWheel: "faceWidth" is required before build()');
|
|
49891
|
+
return faceWidth;
|
|
49892
|
+
}
|
|
49893
|
+
defaultBodyRadius() {
|
|
49894
|
+
const outerRadius = this.regions.reduce((max2, region) => Math.max(max2, region.meta.outerRadius ?? 0), 0);
|
|
49895
|
+
if (outerRadius <= 0) throw new Error('driveWheel: "body" is required when regions do not define an outer radius');
|
|
49896
|
+
return outerRadius;
|
|
49897
|
+
}
|
|
49898
|
+
resolveName(prefix, requested) {
|
|
49899
|
+
const base = (requested == null ? void 0 : requested.trim()) || prefix;
|
|
49900
|
+
if (this.regions.every((region) => region.meta.name !== base)) return base;
|
|
49901
|
+
for (let i = 2; ; i++) {
|
|
49902
|
+
const candidate = `${base}${i}`;
|
|
49903
|
+
if (this.regions.every((region) => region.meta.name !== candidate)) return candidate;
|
|
49904
|
+
}
|
|
49905
|
+
}
|
|
49906
|
+
}
|
|
49907
|
+
function driveWheel(options = {}) {
|
|
49908
|
+
return new DriveWheelBuilder(options);
|
|
49909
|
+
}
|
|
49910
|
+
function normalizeSectorGearOptions(options) {
|
|
49911
|
+
const teethOnFullCircle = options.teethOnFullCircle;
|
|
49912
|
+
if (!Number.isInteger(teethOnFullCircle) || teethOnFullCircle < 6) {
|
|
49913
|
+
throw new Error('sectorGear: "teethOnFullCircle" must be an integer >= 6');
|
|
49914
|
+
}
|
|
49915
|
+
const toothCount = options.toothCount;
|
|
49916
|
+
if (!Number.isInteger(toothCount) || toothCount < 1 || toothCount > teethOnFullCircle) {
|
|
49917
|
+
throw new Error('sectorGear: "toothCount" must be an integer in [1, teethOnFullCircle]');
|
|
49918
|
+
}
|
|
49919
|
+
const firstTooth = options.firstTooth ?? 0;
|
|
49920
|
+
if (!Number.isInteger(firstTooth) || firstTooth < 0 || firstTooth >= teethOnFullCircle) {
|
|
49921
|
+
throw new Error('sectorGear: "firstTooth" must be an integer in [0, teethOnFullCircle)');
|
|
49922
|
+
}
|
|
49923
|
+
return {
|
|
49924
|
+
...normalizeSpurGearOptions({ ...options, teeth: teethOnFullCircle }),
|
|
49925
|
+
teethOnFullCircle,
|
|
49926
|
+
toothCount,
|
|
49927
|
+
firstTooth,
|
|
49928
|
+
boreDiameter: options.boreDiameter ?? 0
|
|
49929
|
+
};
|
|
49930
|
+
}
|
|
49931
|
+
function sectorGear(options) {
|
|
49932
|
+
const normalized = normalizeSectorGearOptions(options);
|
|
49933
|
+
if (options.body !== void 0 && !(options.body instanceof Shape)) {
|
|
49934
|
+
throw new Error('sectorGear: "body" must be a Shape');
|
|
49935
|
+
}
|
|
49936
|
+
const spurMeta = buildSpurGearMeta(normalized);
|
|
49937
|
+
const pitchStepDeg = 360 / normalized.teethOnFullCircle;
|
|
49938
|
+
const activeAngleStartDeg = normalized.firstTooth * pitchStepDeg - pitchStepDeg * 0.5;
|
|
49939
|
+
const activeAngleEndDeg = (normalized.firstTooth + normalized.toothCount - 1) * pitchStepDeg + pitchStepDeg * 0.5;
|
|
49940
|
+
const meta2 = {
|
|
49941
|
+
...spurMeta,
|
|
49942
|
+
kind: "sector",
|
|
49943
|
+
teethOnFullCircle: normalized.teethOnFullCircle,
|
|
49944
|
+
firstTooth: normalized.firstTooth,
|
|
49945
|
+
toothCount: normalized.toothCount,
|
|
49946
|
+
activeAngleStartDeg,
|
|
49947
|
+
activeAngleEndDeg
|
|
49948
|
+
};
|
|
49949
|
+
const wheel = driveWheel({ body: options.body, faceWidth: normalized.faceWidth, boreDiameter: normalized.boreDiameter }).addSpurTeethBetween({
|
|
49950
|
+
name: "teeth",
|
|
49951
|
+
module: normalized.module,
|
|
49952
|
+
teethOnFullCircle: normalized.teethOnFullCircle,
|
|
49953
|
+
toothCount: normalized.toothCount,
|
|
49954
|
+
firstTooth: normalized.firstTooth,
|
|
49955
|
+
pressureAngleDeg: normalized.pressureAngleDeg,
|
|
49956
|
+
faceWidth: normalized.faceWidth,
|
|
49957
|
+
backlash: normalized.backlash,
|
|
49958
|
+
clearance: normalized.clearance,
|
|
49959
|
+
addendum: normalized.addendum,
|
|
49960
|
+
dedendum: normalized.dedendum,
|
|
49961
|
+
segmentsPerTooth: normalized.segmentsPerTooth
|
|
49962
|
+
}).build();
|
|
49963
|
+
return attachGearMeta(wheel, meta2);
|
|
49964
|
+
}
|
|
48383
49965
|
function normalizeSideGearOptions(options) {
|
|
48384
49966
|
let normalizedSpur;
|
|
48385
49967
|
try {
|
|
@@ -49359,6 +50941,12 @@ function boltPattern(options) {
|
|
|
49359
50941
|
}
|
|
49360
50942
|
};
|
|
49361
50943
|
}
|
|
50944
|
+
const gearBodies = {
|
|
50945
|
+
disk: gearBodyDisk,
|
|
50946
|
+
diskWithHub: gearBodyDiskWithHub,
|
|
50947
|
+
spoked: gearBodySpoked,
|
|
50948
|
+
fromProfile: gearBodyFromProfile
|
|
50949
|
+
};
|
|
49362
50950
|
function thread(diameter, pitch, length4, options) {
|
|
49363
50951
|
const r = diameter / 2;
|
|
49364
50952
|
const depth = (options == null ? void 0 : options.depth) ?? pitch * 0.35;
|
|
@@ -49524,7 +51112,23 @@ const partLibrary = {
|
|
|
49524
51112
|
gearRatio,
|
|
49525
51113
|
rackRatio,
|
|
49526
51114
|
planetaryRatio,
|
|
49527
|
-
boltPattern
|
|
51115
|
+
boltPattern,
|
|
51116
|
+
/** Start a composable exceptional gear or drive wheel. */
|
|
51117
|
+
driveWheel,
|
|
51118
|
+
/** Read functional-region metadata from a drive wheel shape. */
|
|
51119
|
+
readDriveWheelMeta,
|
|
51120
|
+
/** Involute sector gear with teeth on only part of the pitch circle. */
|
|
51121
|
+
sectorGear,
|
|
51122
|
+
/** Gear body preset namespace: disk, diskWithHub, spoked, and fromProfile. */
|
|
51123
|
+
gearBodies,
|
|
51124
|
+
/** Solid disk/ring gear body, independent from any tooth geometry. */
|
|
51125
|
+
gearBodyDisk,
|
|
51126
|
+
/** Disk gear body with a raised center hub. */
|
|
51127
|
+
gearBodyDiskWithHub,
|
|
51128
|
+
/** Spoked gear body with an outer rim, center hub, and radial spokes. */
|
|
51129
|
+
gearBodySpoked,
|
|
51130
|
+
/** Extrude a custom 2D profile into a gear body. */
|
|
51131
|
+
gearBodyFromProfile
|
|
49528
51132
|
};
|
|
49529
51133
|
/**
|
|
49530
51134
|
* @license
|
|
@@ -50881,7 +52485,7 @@ function requireFinite$7(value, label) {
|
|
|
50881
52485
|
}
|
|
50882
52486
|
return value;
|
|
50883
52487
|
}
|
|
50884
|
-
function requireVec3$
|
|
52488
|
+
function requireVec3$3(value, label) {
|
|
50885
52489
|
if (!Array.isArray(value) || value.length !== 3) {
|
|
50886
52490
|
throw new Error(`${label} must be [x, y, z]`);
|
|
50887
52491
|
}
|
|
@@ -50925,7 +52529,7 @@ function normalizeOptions(options) {
|
|
|
50925
52529
|
out.size = requireFinite$7(options.size, "Viewport.label options.size");
|
|
50926
52530
|
if (out.size <= 0) throw new Error("Viewport.label options.size must be positive");
|
|
50927
52531
|
}
|
|
50928
|
-
if (options.offset !== void 0) out.offset = requireVec3$
|
|
52532
|
+
if (options.offset !== void 0) out.offset = requireVec3$3(options.offset, "Viewport.label options.offset");
|
|
50929
52533
|
if (options.anchor !== void 0) {
|
|
50930
52534
|
if (!VALID_ANCHORS.has(options.anchor)) {
|
|
50931
52535
|
throw new Error(`Viewport.label options.anchor must be one of: ${Array.from(VALID_ANCHORS).join(", ")}`);
|
|
@@ -50942,7 +52546,7 @@ function collectRenderLabel(text, at, options) {
|
|
|
50942
52546
|
if (typeof text !== "string" || text.trim().length === 0) {
|
|
50943
52547
|
throw new Error("Viewport.label text must be a non-empty string");
|
|
50944
52548
|
}
|
|
50945
|
-
const normalizedAt = requireVec3$
|
|
52549
|
+
const normalizedAt = requireVec3$3(at, "Viewport.label at");
|
|
50946
52550
|
const normalizedOptions = normalizeOptions(options);
|
|
50947
52551
|
_collected$4.push({
|
|
50948
52552
|
id: `render-label-${_nextId++}`,
|
|
@@ -51137,7 +52741,7 @@ function requireFinite$6(value, label) {
|
|
|
51137
52741
|
}
|
|
51138
52742
|
return value;
|
|
51139
52743
|
}
|
|
51140
|
-
function requireVec3$
|
|
52744
|
+
function requireVec3$2(value, label) {
|
|
51141
52745
|
if (!Array.isArray(value) || value.length !== 3) {
|
|
51142
52746
|
throw new Error(`${label} must be [x, y, z]`);
|
|
51143
52747
|
}
|
|
@@ -51165,9 +52769,9 @@ const VALID_ENVIRONMENT_PRESETS = /* @__PURE__ */ new Set([
|
|
|
51165
52769
|
]);
|
|
51166
52770
|
function validateCamera(cam, label) {
|
|
51167
52771
|
const out = {};
|
|
51168
|
-
if (cam.position !== void 0) out.position = requireVec3$
|
|
51169
|
-
if (cam.target !== void 0) out.target = requireVec3$
|
|
51170
|
-
if (cam.up !== void 0) out.up = requireVec3$
|
|
52772
|
+
if (cam.position !== void 0) out.position = requireVec3$2(cam.position, `${label}.position`);
|
|
52773
|
+
if (cam.target !== void 0) out.target = requireVec3$2(cam.target, `${label}.target`);
|
|
52774
|
+
if (cam.up !== void 0) out.up = requireVec3$2(cam.up, `${label}.up`);
|
|
51171
52775
|
if (cam.fov !== void 0) {
|
|
51172
52776
|
out.fov = requireFinite$6(cam.fov, `${label}.fov`);
|
|
51173
52777
|
if (out.fov <= 0 || out.fov >= 180) throw new Error(`${label}.fov must be between 0 and 180`);
|
|
@@ -51302,8 +52906,8 @@ function validateLight(light, label) {
|
|
|
51302
52906
|
const out = { type: light.type };
|
|
51303
52907
|
if (light.color !== void 0) out.color = requireColor(light.color, `${label}.color`);
|
|
51304
52908
|
if (light.intensity !== void 0) out.intensity = requireFinite$6(light.intensity, `${label}.intensity`);
|
|
51305
|
-
if (light.position !== void 0) out.position = requireVec3$
|
|
51306
|
-
if (light.target !== void 0) out.target = requireVec3$
|
|
52909
|
+
if (light.position !== void 0) out.position = requireVec3$2(light.position, `${label}.position`);
|
|
52910
|
+
if (light.target !== void 0) out.target = requireVec3$2(light.target, `${label}.target`);
|
|
51307
52911
|
if (light.groundColor !== void 0) out.groundColor = requireColor(light.groundColor, `${label}.groundColor`);
|
|
51308
52912
|
if (light.skyColor !== void 0) out.skyColor = requireColor(light.skyColor, `${label}.skyColor`);
|
|
51309
52913
|
if (light.angle !== void 0) out.angle = requireFinite$6(light.angle, `${label}.angle`);
|
|
@@ -51849,7 +53453,7 @@ class ProductStationBuilder {
|
|
|
51849
53453
|
this.profileValue = profileFromSketch(sketch, "custom", width, depth);
|
|
51850
53454
|
return this;
|
|
51851
53455
|
}
|
|
51852
|
-
/**
|
|
53456
|
+
/** Set the station crown amount for soft product-section intent. */
|
|
51853
53457
|
crown(amount) {
|
|
51854
53458
|
if (!Number.isFinite(amount)) throw new Error("station.crown(amount) requires a finite number");
|
|
51855
53459
|
this.crownValue = amount;
|
|
@@ -52835,7 +54439,7 @@ function scale$1(v, s) {
|
|
|
52835
54439
|
function dot$2(a2, b) {
|
|
52836
54440
|
return a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
|
|
52837
54441
|
}
|
|
52838
|
-
function lerp$
|
|
54442
|
+
function lerp$4(a2, b, t) {
|
|
52839
54443
|
return a2 + (b - a2) * t;
|
|
52840
54444
|
}
|
|
52841
54445
|
function frameMatrix$1(x2, y2, z2, p2) {
|
|
@@ -52846,7 +54450,7 @@ function axisVector(axis, sign2 = 1) {
|
|
|
52846
54450
|
if (axis === "Y") return [0, sign2, 0];
|
|
52847
54451
|
return [0, 0, sign2];
|
|
52848
54452
|
}
|
|
52849
|
-
function axisPosition(axis, point2) {
|
|
54453
|
+
function axisPosition$1(axis, point2) {
|
|
52850
54454
|
return point2[AXIS_INDEX[axis]];
|
|
52851
54455
|
}
|
|
52852
54456
|
function crossPointForStation(axis, point2) {
|
|
@@ -52854,7 +54458,7 @@ function crossPointForStation(axis, point2) {
|
|
|
52854
54458
|
if (axis === "Y") return [point2[0], -point2[2]];
|
|
52855
54459
|
return [point2[1], point2[2]];
|
|
52856
54460
|
}
|
|
52857
|
-
function orientLoftToAxis(shape, axis) {
|
|
54461
|
+
function orientLoftToAxis$1(shape, axis) {
|
|
52858
54462
|
if (axis === "Z") return shape;
|
|
52859
54463
|
if (axis === "Y") return shape.rotateX(-90);
|
|
52860
54464
|
return shape.transform([0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1]);
|
|
@@ -52911,9 +54515,9 @@ function interpolateQuery(a2, b, t) {
|
|
|
52911
54515
|
}
|
|
52912
54516
|
return {
|
|
52913
54517
|
side: sideA,
|
|
52914
|
-
u: lerp$
|
|
52915
|
-
v: lerp$
|
|
52916
|
-
offset: lerp$
|
|
54518
|
+
u: lerp$4(a2.u ?? 0.5, b.u ?? 0.5, t),
|
|
54519
|
+
v: lerp$4(a2.v ?? 0.5, b.v ?? 0.5, t),
|
|
54520
|
+
offset: lerp$4(a2.offset ?? 0, b.offset ?? 0, t)
|
|
52917
54521
|
};
|
|
52918
54522
|
}
|
|
52919
54523
|
function resolvePathQueries(points) {
|
|
@@ -52980,8 +54584,8 @@ class ProductSkin {
|
|
|
52980
54584
|
this.stations = stations;
|
|
52981
54585
|
this.rails = rails;
|
|
52982
54586
|
for (const [name2, query] of Object.entries(refs)) this.refQueries.set(name2, cloneQuery(query));
|
|
52983
|
-
this.axisMin = Math.min(...stations.map((station) => axisPosition(axis, station.center)));
|
|
52984
|
-
this.axisMax = Math.max(...stations.map((station) => axisPosition(axis, station.center)));
|
|
54587
|
+
this.axisMin = Math.min(...stations.map((station) => axisPosition$1(axis, station.center)));
|
|
54588
|
+
this.axisMax = Math.max(...stations.map((station) => axisPosition$1(axis, station.center)));
|
|
52985
54589
|
this.diagnosticsValue = {
|
|
52986
54590
|
...diagnostics,
|
|
52987
54591
|
stationNames: stations.map((station) => station.name),
|
|
@@ -53038,24 +54642,24 @@ class ProductSkin {
|
|
|
53038
54642
|
}
|
|
53039
54643
|
/** Interpolate center, width, and depth at a normalized v or absolute axis value. */
|
|
53040
54644
|
stationAt(vOrAxis) {
|
|
53041
|
-
const axisValue = vOrAxis >= 0 && vOrAxis <= 1 ? lerp$
|
|
54645
|
+
const axisValue = vOrAxis >= 0 && vOrAxis <= 1 ? lerp$4(this.axisMin, this.axisMax, vOrAxis) : clamp$5(vOrAxis, this.axisMin, this.axisMax);
|
|
53042
54646
|
const sorted = this.stations;
|
|
53043
54647
|
for (let index2 = 0; index2 < sorted.length - 1; index2 += 1) {
|
|
53044
54648
|
const a2 = sorted[index2];
|
|
53045
54649
|
const b = sorted[index2 + 1];
|
|
53046
|
-
const aAxis = axisPosition(this.axis, a2.center);
|
|
53047
|
-
const bAxis = axisPosition(this.axis, b.center);
|
|
54650
|
+
const aAxis = axisPosition$1(this.axis, a2.center);
|
|
54651
|
+
const bAxis = axisPosition$1(this.axis, b.center);
|
|
53048
54652
|
if (axisValue < aAxis - EPS$5 || axisValue > bAxis + EPS$5) continue;
|
|
53049
54653
|
const span = Math.max(EPS$5, bAxis - aAxis);
|
|
53050
54654
|
const t = clamp$5((axisValue - aAxis) / span, 0, 1);
|
|
53051
54655
|
return {
|
|
53052
54656
|
axisValue,
|
|
53053
|
-
center: [lerp$
|
|
53054
|
-
width: lerp$
|
|
53055
|
-
depth: lerp$
|
|
54657
|
+
center: [lerp$4(a2.center[0], b.center[0], t), lerp$4(a2.center[1], b.center[1], t), lerp$4(a2.center[2], b.center[2], t)],
|
|
54658
|
+
width: lerp$4(a2.profile.width, b.profile.width, t),
|
|
54659
|
+
depth: lerp$4(a2.profile.depth, b.profile.depth, t),
|
|
53056
54660
|
dWidth: (b.profile.width - a2.profile.width) / span,
|
|
53057
54661
|
dDepth: (b.profile.depth - a2.profile.depth) / span,
|
|
53058
|
-
exponent: lerp$
|
|
54662
|
+
exponent: lerp$4(profileExponent(a2), profileExponent(b), t),
|
|
53059
54663
|
kind: a2.profile.kind === b.profile.kind ? a2.profile.kind : "custom"
|
|
53060
54664
|
};
|
|
53061
54665
|
}
|
|
@@ -53177,10 +54781,10 @@ class ProductSkinBuilder {
|
|
|
53177
54781
|
}
|
|
53178
54782
|
/** Set named cross-section stations for the product skin. */
|
|
53179
54783
|
stations(stations) {
|
|
53180
|
-
this.stationsValue = stations.map(toStationSpec).sort((a2, b) => axisPosition(this.axisValue, a2.center) - axisPosition(this.axisValue, b.center));
|
|
54784
|
+
this.stationsValue = stations.map(toStationSpec).sort((a2, b) => axisPosition$1(this.axisValue, a2.center) - axisPosition$1(this.axisValue, b.center));
|
|
53181
54785
|
return this;
|
|
53182
54786
|
}
|
|
53183
|
-
/** Attach guide rails
|
|
54787
|
+
/** Attach named guide rails for product-skin construction and downstream surface references. */
|
|
53184
54788
|
rails(rails) {
|
|
53185
54789
|
this.railsValue = { ...rails };
|
|
53186
54790
|
return this;
|
|
@@ -53214,7 +54818,7 @@ class ProductSkinBuilder {
|
|
|
53214
54818
|
this.edgeLengthValue = value;
|
|
53215
54819
|
return this;
|
|
53216
54820
|
}
|
|
53217
|
-
/**
|
|
54821
|
+
/** Record intended wall thickness for product design metadata. Use explicit shelling when the model needs real inner-wall geometry. */
|
|
53218
54822
|
wall(thickness) {
|
|
53219
54823
|
if (!Number.isFinite(thickness) || thickness <= 0) throw new Error("Product.skin().wall(thickness) requires a positive finite number");
|
|
53220
54824
|
this.wallValue = thickness;
|
|
@@ -53227,9 +54831,9 @@ class ProductSkinBuilder {
|
|
|
53227
54831
|
const [x2, y2] = crossPointForStation(this.axisValue, station.center);
|
|
53228
54832
|
return station.profile.sketch.translate(x2, y2);
|
|
53229
54833
|
});
|
|
53230
|
-
const heights = this.stationsValue.map((station) => axisPosition(this.axisValue, station.center));
|
|
54834
|
+
const heights = this.stationsValue.map((station) => axisPosition$1(this.axisValue, station.center));
|
|
53231
54835
|
let shape = loft(localProfiles, heights, { edgeLength: this.edgeLengthValue });
|
|
53232
|
-
shape = orientLoftToAxis(shape, this.axisValue);
|
|
54836
|
+
shape = orientLoftToAxis$1(shape, this.axisValue);
|
|
53233
54837
|
if (this.colorValue) shape = shape.color(this.colorValue);
|
|
53234
54838
|
shape = applyMaterial(shape, this.materialValue).as(this.name);
|
|
53235
54839
|
const warnings = [];
|
|
@@ -53888,7 +55492,7 @@ function requirePositive$3(value, label) {
|
|
|
53888
55492
|
function clamp$4(value, min2, max2) {
|
|
53889
55493
|
return Math.max(min2, Math.min(max2, value));
|
|
53890
55494
|
}
|
|
53891
|
-
function lerp$
|
|
55495
|
+
function lerp$3(a2, b, t) {
|
|
53892
55496
|
return a2 + (b - a2) * t;
|
|
53893
55497
|
}
|
|
53894
55498
|
function add(a2, b) {
|
|
@@ -53938,19 +55542,19 @@ function transformLocal(point2, tangentAcross, normal, tangentAlong, x2, y2, z2
|
|
|
53938
55542
|
function interpolateCylinder(a2, b, t, mode) {
|
|
53939
55543
|
let delta = b.angle - a2.angle;
|
|
53940
55544
|
if (mode === "shortest" && Math.abs(delta) > 180) delta -= Math.sign(delta) * 360;
|
|
53941
|
-
return { kind: "cylinder", angle: a2.angle + delta * t, z: lerp$
|
|
55545
|
+
return { kind: "cylinder", angle: a2.angle + delta * t, z: lerp$3(a2.z, b.z, t), offset: lerp$3(a2.offset ?? 0, b.offset ?? 0, t) };
|
|
53942
55546
|
}
|
|
53943
55547
|
function interpolatePlane(a2, b, t) {
|
|
53944
|
-
return { kind: "plane", x: lerp$
|
|
55548
|
+
return { kind: "plane", x: lerp$3(a2.x, b.x, t), y: lerp$3(a2.y, b.y, t), offset: lerp$3(a2.offset ?? 0, b.offset ?? 0, t) };
|
|
53945
55549
|
}
|
|
53946
55550
|
function interpolateProductSkin(a2, b, t) {
|
|
53947
55551
|
if ((a2.side ?? b.side) !== (b.side ?? a2.side)) throw new Error("SurfacePath on ProductSkin currently supports one side per path; split side transitions into separate members.");
|
|
53948
55552
|
return {
|
|
53949
55553
|
kind: "productSkin",
|
|
53950
55554
|
side: a2.side ?? b.side,
|
|
53951
|
-
u: lerp$
|
|
53952
|
-
v: lerp$
|
|
53953
|
-
offset: lerp$
|
|
55555
|
+
u: lerp$3(a2.u ?? 0.5, b.u ?? 0.5, t),
|
|
55556
|
+
v: lerp$3(a2.v ?? 0.5, b.v ?? 0.5, t),
|
|
55557
|
+
offset: lerp$3(a2.offset ?? 0, b.offset ?? 0, t)
|
|
53954
55558
|
};
|
|
53955
55559
|
}
|
|
53956
55560
|
class SurfacePath {
|
|
@@ -54273,11 +55877,11 @@ function coordinateOnSide(coordinate, side, label) {
|
|
|
54273
55877
|
return { ...coordinate, kind: "productSkin", side };
|
|
54274
55878
|
}
|
|
54275
55879
|
class ProductSkinCarrier {
|
|
54276
|
-
constructor(skin, name = skin.name,
|
|
55880
|
+
constructor(skin, name = skin.name, sideValue2, offsetValue = 0) {
|
|
54277
55881
|
__publicField(this, "kind", "productSkin");
|
|
54278
55882
|
this.skin = skin;
|
|
54279
55883
|
this.name = name;
|
|
54280
|
-
this.sideValue =
|
|
55884
|
+
this.sideValue = sideValue2;
|
|
54281
55885
|
this.offsetValue = offsetValue;
|
|
54282
55886
|
}
|
|
54283
55887
|
surface(side) {
|
|
@@ -55048,7 +56652,7 @@ function counterboresForPlate(spec2, width, height, thickness, diagnostics) {
|
|
|
55048
56652
|
function minWidthAcrossAlongRange(widthAtT, length4, minAlong, maxAlong) {
|
|
55049
56653
|
let minWidth = Number.POSITIVE_INFINITY;
|
|
55050
56654
|
for (let index2 = 0; index2 <= 8; index2 += 1) {
|
|
55051
|
-
const along = lerp$
|
|
56655
|
+
const along = lerp$3(minAlong, maxAlong, index2 / 8);
|
|
55052
56656
|
const t = Math.max(0, Math.min(1, (along + length4 / 2) / Math.max(length4, 1e-8)));
|
|
55053
56657
|
minWidth = Math.min(minWidth, widthAtT(t));
|
|
55054
56658
|
}
|
|
@@ -55348,7 +56952,7 @@ function pathParameterAtDistance(samples, distance2) {
|
|
|
55348
56952
|
const segmentLength = Math.hypot(b.point[0] - a2.point[0], b.point[1] - a2.point[1], b.point[2] - a2.point[2]);
|
|
55349
56953
|
if (traveled + segmentLength >= distance2) {
|
|
55350
56954
|
const localT = segmentLength <= 1e-8 ? 0 : (distance2 - traveled) / segmentLength;
|
|
55351
|
-
return lerp$
|
|
56955
|
+
return lerp$3(a2.t, b.t, localT);
|
|
55352
56956
|
}
|
|
55353
56957
|
traveled += segmentLength;
|
|
55354
56958
|
}
|
|
@@ -55401,7 +57005,7 @@ function compileBandFootprintMesh(path2, input) {
|
|
|
55401
57005
|
const width = input.widthAt(t);
|
|
55402
57006
|
const along = distance2 - length4 / 2;
|
|
55403
57007
|
for (let acrossIndex = 0; acrossIndex <= acrossSegments; acrossIndex += 1) {
|
|
55404
|
-
const across = lerp$
|
|
57008
|
+
const across = lerp$3(-width / 2, width / 2, acrossIndex / acrossSegments);
|
|
55405
57009
|
mesh.vertices.push(pointAtProfile([across, along], false));
|
|
55406
57010
|
}
|
|
55407
57011
|
}
|
|
@@ -55411,7 +57015,7 @@ function compileBandFootprintMesh(path2, input) {
|
|
|
55411
57015
|
const width = input.widthAt(t);
|
|
55412
57016
|
const along = distance2 - length4 / 2;
|
|
55413
57017
|
for (let acrossIndex = 0; acrossIndex <= acrossSegments; acrossIndex += 1) {
|
|
55414
|
-
const across = lerp$
|
|
57018
|
+
const across = lerp$3(-width / 2, width / 2, acrossIndex / acrossSegments);
|
|
55415
57019
|
mesh.vertices.push(pointAtProfile([across, along], true));
|
|
55416
57020
|
}
|
|
55417
57021
|
}
|
|
@@ -55423,7 +57027,7 @@ function compileBandFootprintMesh(path2, input) {
|
|
|
55423
57027
|
const width = input.widthAt(t);
|
|
55424
57028
|
const along = distance2 - length4 / 2;
|
|
55425
57029
|
for (let acrossIndex = 0; acrossIndex < acrossSegments; acrossIndex += 1) {
|
|
55426
|
-
const across = lerp$
|
|
57030
|
+
const across = lerp$3(-width / 2, width / 2, (acrossIndex + 0.5) / acrossSegments);
|
|
55427
57031
|
filled[alongIndex][acrossIndex] = !holes.some((hole2) => pointInProfileLoop([across, along], hole2));
|
|
55428
57032
|
}
|
|
55429
57033
|
}
|
|
@@ -56356,7 +57960,7 @@ class SurfaceMemberBuilder {
|
|
|
56356
57960
|
this.record.features.push({ ...normalizeFeature(name, feature), type: "counterbore" });
|
|
56357
57961
|
return this;
|
|
56358
57962
|
}
|
|
56359
|
-
/** Add a named anchor at a carrier surface coordinate for
|
|
57963
|
+
/** Add a named anchor at a carrier surface coordinate for explicit member joins. */
|
|
56360
57964
|
anchorAt(name, coordinate) {
|
|
56361
57965
|
if (!name.trim()) throw new Error("SurfaceMemberBuilder.anchorAt(name, coordinate) requires a non-empty name");
|
|
56362
57966
|
const explicitAnchors = this.record.spec.explicitAnchors ?? [];
|
|
@@ -57505,7 +59109,7 @@ const Constraint = {
|
|
|
57505
59109
|
return builder.constrain({ type: "length", line: resolveLineId(builder, line2), value });
|
|
57506
59110
|
}
|
|
57507
59111
|
};
|
|
57508
|
-
function requireVec3(v, label) {
|
|
59112
|
+
function requireVec3$1(v, label) {
|
|
57509
59113
|
if (!Array.isArray(v) || v.length !== 3 || !Number.isFinite(v[0]) || !Number.isFinite(v[1]) || !Number.isFinite(v[2])) {
|
|
57510
59114
|
throw new Error(`${label} must be a [number, number, number] with finite values, got ${JSON.stringify(v)}`);
|
|
57511
59115
|
}
|
|
@@ -57518,24 +59122,24 @@ function requireFiniteNumber(n, label) {
|
|
|
57518
59122
|
return n;
|
|
57519
59123
|
}
|
|
57520
59124
|
function distance$1(a2, b) {
|
|
57521
|
-
requireVec3(a2, "a");
|
|
57522
|
-
requireVec3(b, "b");
|
|
59125
|
+
requireVec3$1(a2, "a");
|
|
59126
|
+
requireVec3$1(b, "b");
|
|
57523
59127
|
return Math.hypot(b[0] - a2[0], b[1] - a2[1], b[2] - a2[2]);
|
|
57524
59128
|
}
|
|
57525
59129
|
function midpoint$1(a2, b) {
|
|
57526
|
-
requireVec3(a2, "a");
|
|
57527
|
-
requireVec3(b, "b");
|
|
59130
|
+
requireVec3$1(a2, "a");
|
|
59131
|
+
requireVec3$1(b, "b");
|
|
57528
59132
|
return [(a2[0] + b[0]) / 2, (a2[1] + b[1]) / 2, (a2[2] + b[2]) / 2];
|
|
57529
59133
|
}
|
|
57530
|
-
function lerp(a2, b, t) {
|
|
57531
|
-
requireVec3(a2, "a");
|
|
57532
|
-
requireVec3(b, "b");
|
|
59134
|
+
function lerp$2(a2, b, t) {
|
|
59135
|
+
requireVec3$1(a2, "a");
|
|
59136
|
+
requireVec3$1(b, "b");
|
|
57533
59137
|
requireFiniteNumber(t, "t");
|
|
57534
59138
|
return [a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t];
|
|
57535
59139
|
}
|
|
57536
59140
|
function direction(a2, b) {
|
|
57537
|
-
requireVec3(a2, "a");
|
|
57538
|
-
requireVec3(b, "b");
|
|
59141
|
+
requireVec3$1(a2, "a");
|
|
59142
|
+
requireVec3$1(b, "b");
|
|
57539
59143
|
const dx = b[0] - a2[0];
|
|
57540
59144
|
const dy = b[1] - a2[1];
|
|
57541
59145
|
const dz = b[2] - a2[2];
|
|
@@ -57546,8 +59150,8 @@ function direction(a2, b) {
|
|
|
57546
59150
|
return [dx / len2, dy / len2, dz / len2];
|
|
57547
59151
|
}
|
|
57548
59152
|
function offset(point2, dir, amount) {
|
|
57549
|
-
requireVec3(point2, "point");
|
|
57550
|
-
requireVec3(dir, "dir");
|
|
59153
|
+
requireVec3$1(point2, "point");
|
|
59154
|
+
requireVec3$1(dir, "dir");
|
|
57551
59155
|
requireFiniteNumber(amount, "amount");
|
|
57552
59156
|
return [point2[0] + dir[0] * amount, point2[1] + dir[1] * amount, point2[2] + dir[2] * amount];
|
|
57553
59157
|
}
|
|
@@ -57557,7 +59161,7 @@ const Points = {
|
|
|
57557
59161
|
/** Center point between two 3D points. */
|
|
57558
59162
|
midpoint: midpoint$1,
|
|
57559
59163
|
/** Linearly interpolate between two 3D points. t=0 returns a, t=1 returns b. */
|
|
57560
|
-
lerp,
|
|
59164
|
+
lerp: lerp$2,
|
|
57561
59165
|
/** Unit direction vector from a to b. Throws if a and b are the same point. */
|
|
57562
59166
|
direction,
|
|
57563
59167
|
/** Move a point along a direction vector by a given amount. */
|
|
@@ -62689,9 +64293,84 @@ class ConstraintSketch extends Sketch {
|
|
|
62689
64293
|
* Select the single arrangement region that contains the given seed point.
|
|
62690
64294
|
* Throws if no region contains the seed.
|
|
62691
64295
|
*/
|
|
62692
|
-
detectArrangementRegion(
|
|
64296
|
+
detectArrangementRegion(_seed) {
|
|
62693
64297
|
throw new Error("Not implemented");
|
|
62694
64298
|
}
|
|
64299
|
+
/**
|
|
64300
|
+
* Return the solved constrained path as a sampled 2D polyline.
|
|
64301
|
+
*
|
|
64302
|
+
* Use this when a construction rail was authored with `constrainedSketch()`
|
|
64303
|
+
* and should feed another operation such as `Loft.pathOnXz(...)`.
|
|
64304
|
+
* The sketch must contain exactly one profile path.
|
|
64305
|
+
*
|
|
64306
|
+
* @param samples - Samples per curved segment. Default 32.
|
|
64307
|
+
* @returns The solved path as an open polyline.
|
|
64308
|
+
*/
|
|
64309
|
+
toPolyline(samples = 32) {
|
|
64310
|
+
if (!Number.isFinite(samples) || samples < 2) throw new Error("ConstraintSketch.toPolyline() samples must be at least 2");
|
|
64311
|
+
const profileLoops = this.definition.loops.filter((loop) => loop.type === "profile");
|
|
64312
|
+
if (profileLoops.length !== 1) {
|
|
64313
|
+
throw new Error("ConstraintSketch.toPolyline() requires exactly one profile path");
|
|
64314
|
+
}
|
|
64315
|
+
const sampleCount = Math.max(2, Math.round(samples));
|
|
64316
|
+
const pointMap = new Map(this.definition.points.map((point2) => [point2.id, point2]));
|
|
64317
|
+
const lineMap = new Map(this.definition.lines.map((line2) => [line2.id, line2]));
|
|
64318
|
+
const arcMap = new Map(this.definition.arcs.map((arc) => [arc.id, arc]));
|
|
64319
|
+
const bezierMap = new Map(this.definition.beziers.map((bezier) => [bezier.id, bezier]));
|
|
64320
|
+
const points = [];
|
|
64321
|
+
const appendStart = (point2, label) => {
|
|
64322
|
+
const previous = points[points.length - 1];
|
|
64323
|
+
if (!previous) {
|
|
64324
|
+
points.push(point2);
|
|
64325
|
+
return;
|
|
64326
|
+
}
|
|
64327
|
+
if (Math.hypot(point2[0] - previous[0], point2[1] - previous[1]) > 1e-6) {
|
|
64328
|
+
throw new Error(`ConstraintSketch.toPolyline() profile path is not continuous at ${label}`);
|
|
64329
|
+
}
|
|
64330
|
+
};
|
|
64331
|
+
const appendPoint = (point2) => {
|
|
64332
|
+
const previous = points[points.length - 1];
|
|
64333
|
+
if (!previous || Math.hypot(point2[0] - previous[0], point2[1] - previous[1]) > 1e-9) points.push(point2);
|
|
64334
|
+
};
|
|
64335
|
+
const requirePoint = (id, label) => {
|
|
64336
|
+
const point2 = pointMap.get(id);
|
|
64337
|
+
if (!point2) throw new Error(`ConstraintSketch.toPolyline() missing ${label}`);
|
|
64338
|
+
return [point2.x, point2.y];
|
|
64339
|
+
};
|
|
64340
|
+
for (const segment of profileLoops[0].segments) {
|
|
64341
|
+
if (segment.kind === "line") {
|
|
64342
|
+
const line2 = lineMap.get(segment.line);
|
|
64343
|
+
if (!line2) throw new Error(`ConstraintSketch.toPolyline() missing line "${segment.line}"`);
|
|
64344
|
+
appendStart(requirePoint(line2.a, `line "${segment.line}" start point`), `line "${segment.line}"`);
|
|
64345
|
+
appendPoint(requirePoint(line2.b, `line "${segment.line}" end point`));
|
|
64346
|
+
} else if (segment.kind === "arc") {
|
|
64347
|
+
const arc = arcMap.get(segment.arc);
|
|
64348
|
+
if (!arc) throw new Error(`ConstraintSketch.toPolyline() missing arc "${segment.arc}"`);
|
|
64349
|
+
const center = requirePoint(arc.center, `arc "${segment.arc}" center point`);
|
|
64350
|
+
const start = requirePoint(arc.start, `arc "${segment.arc}" start point`);
|
|
64351
|
+
const end = requirePoint(arc.end, `arc "${segment.arc}" end point`);
|
|
64352
|
+
appendStart(start, `arc "${segment.arc}"`);
|
|
64353
|
+
const startAngle = Math.atan2(start[1] - center[1], start[0] - center[0]);
|
|
64354
|
+
const endAngle = Math.atan2(end[1] - center[1], end[0] - center[0]);
|
|
64355
|
+
for (const point2 of tessellateArc(center[0], center[1], arc.radius, startAngle, endAngle, arc.clockwise, sampleCount)) {
|
|
64356
|
+
appendPoint(point2);
|
|
64357
|
+
}
|
|
64358
|
+
} else {
|
|
64359
|
+
const bezier = bezierMap.get(segment.bezier);
|
|
64360
|
+
if (!bezier) throw new Error(`ConstraintSketch.toPolyline() missing bezier "${segment.bezier}"`);
|
|
64361
|
+
const p0 = requirePoint(bezier.p0, `bezier "${segment.bezier}" start point`);
|
|
64362
|
+
const p1 = requirePoint(bezier.p1, `bezier "${segment.bezier}" first control point`);
|
|
64363
|
+
const p2 = requirePoint(bezier.p2, `bezier "${segment.bezier}" second control point`);
|
|
64364
|
+
const p3 = requirePoint(bezier.p3, `bezier "${segment.bezier}" end point`);
|
|
64365
|
+
appendStart(p0, `bezier "${segment.bezier}"`);
|
|
64366
|
+
for (const point2 of tessellateBezier(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], sampleCount)) {
|
|
64367
|
+
appendPoint(point2);
|
|
64368
|
+
}
|
|
64369
|
+
}
|
|
64370
|
+
}
|
|
64371
|
+
if (points.length < 2) throw new Error("ConstraintSketch.toPolyline() needs at least 2 points");
|
|
64372
|
+
return points;
|
|
64373
|
+
}
|
|
62695
64374
|
/**
|
|
62696
64375
|
* Re-solve the sketch after changing the value of one existing constraint.
|
|
62697
64376
|
*
|
|
@@ -65818,8 +67497,8 @@ tinf_build_bits_base(dist_bits, dist_base, 2, 1);
|
|
|
65818
67497
|
length_bits[28] = 0;
|
|
65819
67498
|
length_base[28] = 258;
|
|
65820
67499
|
var tinyInflate = tinf_uncompress;
|
|
65821
|
-
function derive(v0, v1, v2,
|
|
65822
|
-
return Math.pow(1 - t, 3) * v0 + 3 * Math.pow(1 - t, 2) * t * v1 + 3 * (1 - t) * Math.pow(t, 2) * v2 + Math.pow(t, 3) *
|
|
67500
|
+
function derive(v0, v1, v2, v32, t) {
|
|
67501
|
+
return Math.pow(1 - t, 3) * v0 + 3 * Math.pow(1 - t, 2) * t * v1 + 3 * (1 - t) * Math.pow(t, 2) * v2 + Math.pow(t, 3) * v32;
|
|
65823
67502
|
}
|
|
65824
67503
|
function BoundingBox() {
|
|
65825
67504
|
this.x1 = Number.NaN;
|
|
@@ -77976,6 +79655,295 @@ function polygonVertices(sides, radius, options) {
|
|
|
77976
79655
|
centerY: options == null ? void 0 : options.centerY
|
|
77977
79656
|
});
|
|
77978
79657
|
}
|
|
79658
|
+
const LOFT_GUIDE_EPS = 1e-8;
|
|
79659
|
+
function orientLoftToAxis(shape, axis) {
|
|
79660
|
+
if (axis === "Z") return shape;
|
|
79661
|
+
if (axis === "Y") return shape.rotateX(-90);
|
|
79662
|
+
return shape.transform([0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1]);
|
|
79663
|
+
}
|
|
79664
|
+
function buildRailEvaluators(rails, axis, start, end, railSamples) {
|
|
79665
|
+
const seen = /* @__PURE__ */ new Set();
|
|
79666
|
+
return rails.map((rail2) => {
|
|
79667
|
+
if (seen.has(rail2.side)) throw new Error(`Loft.withGuideRails() received more than one ${rail2.side} rail`);
|
|
79668
|
+
seen.add(rail2.side);
|
|
79669
|
+
const sampled = sampleRailPath(rail2.path, railSamples);
|
|
79670
|
+
if (sampled.length < 2) throw new Error("Loft guide rails require at least two points");
|
|
79671
|
+
const points = sampled.map((point2) => ({ position: axisPosition(axis, point2), cross: crossPointForAxis(axis, point2) }));
|
|
79672
|
+
const ordered = points[points.length - 1].position >= points[0].position ? points : [...points].reverse();
|
|
79673
|
+
validateRailCoverage(ordered, start, end);
|
|
79674
|
+
return { side: rail2.side, points: ordered };
|
|
79675
|
+
});
|
|
79676
|
+
}
|
|
79677
|
+
function railCrossAt(rail2, position) {
|
|
79678
|
+
const points = rail2.points;
|
|
79679
|
+
if (position <= points[0].position + LOFT_GUIDE_EPS) return points[0].cross;
|
|
79680
|
+
const last = points[points.length - 1];
|
|
79681
|
+
if (position >= last.position - LOFT_GUIDE_EPS) return last.cross;
|
|
79682
|
+
for (let index2 = 0; index2 < points.length - 1; index2 += 1) {
|
|
79683
|
+
const a2 = points[index2];
|
|
79684
|
+
const b = points[index2 + 1];
|
|
79685
|
+
if (position >= a2.position - LOFT_GUIDE_EPS && position <= b.position + LOFT_GUIDE_EPS) {
|
|
79686
|
+
const t = (position - a2.position) / (b.position - a2.position);
|
|
79687
|
+
return [lerp$1(a2.cross[0], b.cross[0], t), lerp$1(a2.cross[1], b.cross[1], t)];
|
|
79688
|
+
}
|
|
79689
|
+
}
|
|
79690
|
+
throw new Error("Loft guide rail does not cover requested station position");
|
|
79691
|
+
}
|
|
79692
|
+
function validateRailCoverage(points, start, end) {
|
|
79693
|
+
for (let index2 = 1; index2 < points.length; index2 += 1) {
|
|
79694
|
+
if (points[index2].position - points[index2 - 1].position < LOFT_GUIDE_EPS) {
|
|
79695
|
+
throw new Error("Loft guide rails must be monotone along the loft axis");
|
|
79696
|
+
}
|
|
79697
|
+
}
|
|
79698
|
+
if (points[0].position - start > LOFT_GUIDE_EPS || end - points[points.length - 1].position > LOFT_GUIDE_EPS) {
|
|
79699
|
+
throw new Error("Loft guide rails must cover the full station range");
|
|
79700
|
+
}
|
|
79701
|
+
}
|
|
79702
|
+
function sampleRailPath(path2, samples) {
|
|
79703
|
+
if (Array.isArray(path2)) return path2.map((point2, index2) => requireVec3(point2, `Loft guide rail point ${index2}`));
|
|
79704
|
+
if (path2 instanceof Curve3D || path2 instanceof HermiteCurve3D || path2 instanceof QuinticHermiteCurve3D || path2 instanceof NurbsCurve3D) {
|
|
79705
|
+
return path2.sample(Math.max(2, Math.round(samples))).map((point2, index2) => requireVec3(point2, `Loft guide rail sample ${index2}`));
|
|
79706
|
+
}
|
|
79707
|
+
throw new Error("Loft guide rail path must be a Vec3[] or ForgeCAD 3D curve");
|
|
79708
|
+
}
|
|
79709
|
+
function requireVec3(point2, label) {
|
|
79710
|
+
if (!Array.isArray(point2) || point2.length !== 3 || !point2.every(Number.isFinite)) {
|
|
79711
|
+
throw new Error(`${label} must be a finite [x, y, z] point`);
|
|
79712
|
+
}
|
|
79713
|
+
return [point2[0], point2[1], point2[2]];
|
|
79714
|
+
}
|
|
79715
|
+
function axisPosition(axis, point2) {
|
|
79716
|
+
if (axis === "X") return point2[0];
|
|
79717
|
+
if (axis === "Y") return point2[1];
|
|
79718
|
+
return point2[2];
|
|
79719
|
+
}
|
|
79720
|
+
function crossPointForAxis(axis, point2) {
|
|
79721
|
+
if (axis === "X") return [point2[1], point2[2]];
|
|
79722
|
+
if (axis === "Y") return [point2[0], -point2[2]];
|
|
79723
|
+
return [point2[0], point2[1]];
|
|
79724
|
+
}
|
|
79725
|
+
function lerp$1(a2, b, t) {
|
|
79726
|
+
return a2 + (b - a2) * t;
|
|
79727
|
+
}
|
|
79728
|
+
function loftWithGuideRails(stations, rails, options = {}) {
|
|
79729
|
+
if (stations.length < 2) throw new Error("Loft.withGuideRails() requires at least two stations");
|
|
79730
|
+
if (rails.length === 0) throw new Error("Loft.withGuideRails() requires at least one guide rail");
|
|
79731
|
+
const sortedStations = sortedValidStations(stations);
|
|
79732
|
+
const axis = options.axis ?? "Z";
|
|
79733
|
+
const start = sortedStations[0].position;
|
|
79734
|
+
const end = sortedStations[sortedStations.length - 1].position;
|
|
79735
|
+
const railEvaluators = buildRailEvaluators(rails, axis, start, end, options.railSamples ?? 64);
|
|
79736
|
+
const positions = generatedPositions(sortedStations, options.samples);
|
|
79737
|
+
const profiles2 = positions.map((position) => {
|
|
79738
|
+
const source = profileForPosition(sortedStations, position);
|
|
79739
|
+
const bounds = boundsForPosition(sortedStations, position);
|
|
79740
|
+
return fitProfileToBounds(source, applyRailsToBounds(bounds, railEvaluators, position));
|
|
79741
|
+
});
|
|
79742
|
+
const shape = loft(profiles2, positions, {
|
|
79743
|
+
edgeLength: options.edgeLength,
|
|
79744
|
+
boundsPadding: options.boundsPadding
|
|
79745
|
+
});
|
|
79746
|
+
return orientLoftToAxis(shape, axis);
|
|
79747
|
+
}
|
|
79748
|
+
function sortedValidStations(stations) {
|
|
79749
|
+
const sorted = [...stations].sort((a2, b) => a2.position - b.position);
|
|
79750
|
+
for (let index2 = 0; index2 < sorted.length; index2 += 1) {
|
|
79751
|
+
if (!Number.isFinite(sorted[index2].position)) throw new Error("Loft.withGuideRails station position must be finite");
|
|
79752
|
+
if (!(sorted[index2].profile instanceof Sketch)) throw new Error("Loft.withGuideRails() stations must use Sketch profiles");
|
|
79753
|
+
if (index2 > 0 && sorted[index2].position - sorted[index2 - 1].position < LOFT_GUIDE_EPS) {
|
|
79754
|
+
throw new Error("Loft.withGuideRails() requires unique, strictly increasing station positions");
|
|
79755
|
+
}
|
|
79756
|
+
}
|
|
79757
|
+
return sorted;
|
|
79758
|
+
}
|
|
79759
|
+
function generatedPositions(stations, samples) {
|
|
79760
|
+
const count = Math.max(2, Math.round(samples ?? Math.max(9, (stations.length - 1) * 8 + 1)));
|
|
79761
|
+
const start = stations[0].position;
|
|
79762
|
+
const end = stations[stations.length - 1].position;
|
|
79763
|
+
const values = /* @__PURE__ */ new Set();
|
|
79764
|
+
const positions = [];
|
|
79765
|
+
const addPosition = (position) => {
|
|
79766
|
+
const key = position.toFixed(9);
|
|
79767
|
+
if (!values.has(key)) {
|
|
79768
|
+
values.add(key);
|
|
79769
|
+
positions.push(position);
|
|
79770
|
+
}
|
|
79771
|
+
};
|
|
79772
|
+
for (let index2 = 0; index2 < count; index2 += 1) addPosition(start + (end - start) * index2 / (count - 1));
|
|
79773
|
+
for (const station of stations) addPosition(station.position);
|
|
79774
|
+
return positions.sort((a2, b) => a2 - b);
|
|
79775
|
+
}
|
|
79776
|
+
function profileForPosition(stations, position) {
|
|
79777
|
+
for (let index2 = 0; index2 < stations.length - 1; index2 += 1) {
|
|
79778
|
+
if (position <= stations[index2 + 1].position + LOFT_GUIDE_EPS) return stations[index2].profile;
|
|
79779
|
+
}
|
|
79780
|
+
return stations[stations.length - 1].profile;
|
|
79781
|
+
}
|
|
79782
|
+
function boundsForPosition(stations, position) {
|
|
79783
|
+
if (position <= stations[0].position + LOFT_GUIDE_EPS) return sketchBounds(stations[0].profile);
|
|
79784
|
+
const last = stations[stations.length - 1];
|
|
79785
|
+
if (position >= last.position - LOFT_GUIDE_EPS) return sketchBounds(last.profile);
|
|
79786
|
+
for (let index2 = 0; index2 < stations.length - 1; index2 += 1) {
|
|
79787
|
+
const a2 = stations[index2];
|
|
79788
|
+
const b = stations[index2 + 1];
|
|
79789
|
+
if (position >= a2.position - LOFT_GUIDE_EPS && position <= b.position + LOFT_GUIDE_EPS) {
|
|
79790
|
+
return lerpBounds(sketchBounds(a2.profile), sketchBounds(b.profile), (position - a2.position) / (b.position - a2.position));
|
|
79791
|
+
}
|
|
79792
|
+
}
|
|
79793
|
+
return sketchBounds(last.profile);
|
|
79794
|
+
}
|
|
79795
|
+
function applyRailsToBounds(bounds, rails, position) {
|
|
79796
|
+
const centerRail = rails.find((rail2) => rail2.side === "center");
|
|
79797
|
+
const center = centerRail ? railCrossAt(centerRail, position) : void 0;
|
|
79798
|
+
const next = { ...bounds };
|
|
79799
|
+
applyAxisRail(next, "X", sideValue(rails, "left", position, 0), sideValue(rails, "right", position, 0), center == null ? void 0 : center[0]);
|
|
79800
|
+
applyAxisRail(next, "Y", sideValue(rails, "back", position, 1), sideValue(rails, "front", position, 1), center == null ? void 0 : center[1]);
|
|
79801
|
+
if (next.maxX - next.minX < LOFT_GUIDE_EPS || next.maxY - next.minY < LOFT_GUIDE_EPS) {
|
|
79802
|
+
throw new Error("Loft.withGuideRails() guide rails produced a non-positive section size");
|
|
79803
|
+
}
|
|
79804
|
+
return next;
|
|
79805
|
+
}
|
|
79806
|
+
function sideValue(rails, side, position, crossIndex) {
|
|
79807
|
+
const rail2 = rails.find((entry) => entry.side === side);
|
|
79808
|
+
return rail2 ? railCrossAt(rail2, position)[crossIndex] : void 0;
|
|
79809
|
+
}
|
|
79810
|
+
function applyAxisRail(bounds, axis, minRail, maxRail, center) {
|
|
79811
|
+
const minKey = axis === "X" ? "minX" : "minY";
|
|
79812
|
+
const maxKey = axis === "X" ? "maxX" : "maxY";
|
|
79813
|
+
const width = bounds[maxKey] - bounds[minKey];
|
|
79814
|
+
if (minRail != null && maxRail != null) {
|
|
79815
|
+
if (maxRail - minRail < LOFT_GUIDE_EPS) throw new Error("Loft.withGuideRails() opposite guide rails crossed");
|
|
79816
|
+
if (center != null && Math.abs((minRail + maxRail) / 2 - center) > 1e-5) {
|
|
79817
|
+
throw new Error("Loft.withGuideRails() center rail conflicts with opposite side rails");
|
|
79818
|
+
}
|
|
79819
|
+
bounds[minKey] = minRail;
|
|
79820
|
+
bounds[maxKey] = maxRail;
|
|
79821
|
+
} else if (maxRail != null) {
|
|
79822
|
+
bounds[maxKey] = maxRail;
|
|
79823
|
+
bounds[minKey] = center != null ? 2 * center - maxRail : maxRail - width;
|
|
79824
|
+
} else if (minRail != null) {
|
|
79825
|
+
bounds[minKey] = minRail;
|
|
79826
|
+
bounds[maxKey] = center != null ? 2 * center - minRail : minRail + width;
|
|
79827
|
+
} else if (center != null) {
|
|
79828
|
+
bounds[minKey] = center - width / 2;
|
|
79829
|
+
bounds[maxKey] = center + width / 2;
|
|
79830
|
+
}
|
|
79831
|
+
}
|
|
79832
|
+
function fitProfileToBounds(profile, target) {
|
|
79833
|
+
const source = sketchBounds(profile);
|
|
79834
|
+
const sourceWidth = source.maxX - source.minX;
|
|
79835
|
+
const sourceDepth = source.maxY - source.minY;
|
|
79836
|
+
if (sourceWidth < LOFT_GUIDE_EPS || sourceDepth < LOFT_GUIDE_EPS) {
|
|
79837
|
+
throw new Error("Loft.withGuideRails() station profiles must have positive bounds");
|
|
79838
|
+
}
|
|
79839
|
+
const sourceCenter = [(source.minX + source.maxX) / 2, (source.minY + source.maxY) / 2];
|
|
79840
|
+
const targetCenter = [(target.minX + target.maxX) / 2, (target.minY + target.maxY) / 2];
|
|
79841
|
+
return profile.scaleAround(sourceCenter, [(target.maxX - target.minX) / sourceWidth, (target.maxY - target.minY) / sourceDepth]).translate(targetCenter[0] - sourceCenter[0], targetCenter[1] - sourceCenter[1]);
|
|
79842
|
+
}
|
|
79843
|
+
function sketchBounds(profile) {
|
|
79844
|
+
const bounds = profile.bounds();
|
|
79845
|
+
return { minX: bounds.min[0], maxX: bounds.max[0], minY: bounds.min[1], maxY: bounds.max[1] };
|
|
79846
|
+
}
|
|
79847
|
+
function lerpBounds(a2, b, t) {
|
|
79848
|
+
return {
|
|
79849
|
+
minX: lerp(a2.minX, b.minX, t),
|
|
79850
|
+
maxX: lerp(a2.maxX, b.maxX, t),
|
|
79851
|
+
minY: lerp(a2.minY, b.minY, t),
|
|
79852
|
+
maxY: lerp(a2.maxY, b.maxY, t)
|
|
79853
|
+
};
|
|
79854
|
+
}
|
|
79855
|
+
function lerp(a2, b, t) {
|
|
79856
|
+
return a2 + (b - a2) * t;
|
|
79857
|
+
}
|
|
79858
|
+
function mapLoftPath2D(path2, label, mapper) {
|
|
79859
|
+
const points = sampleLoftPath2D(path2, label);
|
|
79860
|
+
return points.map((point2, index2) => {
|
|
79861
|
+
if (!Array.isArray(point2) || point2.length !== 2 || !point2.every(Number.isFinite)) {
|
|
79862
|
+
throw new Error(`${label} point ${index2} must be a finite [x, y] point`);
|
|
79863
|
+
}
|
|
79864
|
+
return mapper([point2[0], point2[1]]);
|
|
79865
|
+
});
|
|
79866
|
+
}
|
|
79867
|
+
function sampleLoftPath2D(path2, label) {
|
|
79868
|
+
if (Array.isArray(path2)) {
|
|
79869
|
+
if (path2.length < 2) throw new Error(`${label} requires at least two [x, y] points`);
|
|
79870
|
+
return path2;
|
|
79871
|
+
}
|
|
79872
|
+
if (!path2 || typeof path2 !== "object" || typeof path2.toPolyline !== "function") {
|
|
79873
|
+
throw new Error(`${label} requires a 2D path, solved constrained path, or [x, y] point array`);
|
|
79874
|
+
}
|
|
79875
|
+
const points = path2.toPolyline();
|
|
79876
|
+
if (!Array.isArray(points) || points.length < 2) throw new Error(`${label} path must produce at least two [x, y] points`);
|
|
79877
|
+
return points;
|
|
79878
|
+
}
|
|
79879
|
+
const Loft = {
|
|
79880
|
+
/** Create a loft station from a 2D profile and an axis position. */
|
|
79881
|
+
station(profile, position) {
|
|
79882
|
+
if (!Number.isFinite(position)) throw new Error("Loft.station position must be finite");
|
|
79883
|
+
return { profile, position };
|
|
79884
|
+
},
|
|
79885
|
+
/** Create a guide rail that constrains the section-local negative-X side. */
|
|
79886
|
+
leftRail(path2) {
|
|
79887
|
+
return { side: "left", path: path2 };
|
|
79888
|
+
},
|
|
79889
|
+
/** Create a guide rail that constrains the section-local positive-X side. */
|
|
79890
|
+
rightRail(path2) {
|
|
79891
|
+
return { side: "right", path: path2 };
|
|
79892
|
+
},
|
|
79893
|
+
/** Create a guide rail that constrains the section-local positive-Y side. */
|
|
79894
|
+
frontRail(path2) {
|
|
79895
|
+
return { side: "front", path: path2 };
|
|
79896
|
+
},
|
|
79897
|
+
/** Create a guide rail that constrains the section-local negative-Y side. */
|
|
79898
|
+
backRail(path2) {
|
|
79899
|
+
return { side: "back", path: path2 };
|
|
79900
|
+
},
|
|
79901
|
+
/** Create a guide rail that moves section centers along the loft. */
|
|
79902
|
+
centerRail(path2) {
|
|
79903
|
+
return { side: "center", path: path2 };
|
|
79904
|
+
},
|
|
79905
|
+
/**
|
|
79906
|
+
* Place a 2D guide path onto the XZ plane.
|
|
79907
|
+
*
|
|
79908
|
+
* The path's first coordinate becomes X and its second coordinate becomes Z.
|
|
79909
|
+
* Use this for left/right silhouette rails authored with `path()` or `constrainedSketch()`.
|
|
79910
|
+
*/
|
|
79911
|
+
pathOnXz(path2, y2 = 0) {
|
|
79912
|
+
if (!Number.isFinite(y2)) throw new Error("Loft.pathOnXz y must be finite");
|
|
79913
|
+
return mapLoftPath2D(path2, "Loft.pathOnXz", ([x2, z2]) => [x2, y2, z2]);
|
|
79914
|
+
},
|
|
79915
|
+
/**
|
|
79916
|
+
* Place a 2D guide path onto the YZ plane.
|
|
79917
|
+
*
|
|
79918
|
+
* The path's first coordinate becomes Y and its second coordinate becomes Z.
|
|
79919
|
+
* Use this for front/back crown rails authored with `path()` or `constrainedSketch()`.
|
|
79920
|
+
*/
|
|
79921
|
+
pathOnYz(path2, x2 = 0) {
|
|
79922
|
+
if (!Number.isFinite(x2)) throw new Error("Loft.pathOnYz x must be finite");
|
|
79923
|
+
return mapLoftPath2D(path2, "Loft.pathOnYz", ([y2, z2]) => [x2, y2, z2]);
|
|
79924
|
+
},
|
|
79925
|
+
/**
|
|
79926
|
+
* Place a 2D guide path onto the XY plane.
|
|
79927
|
+
*
|
|
79928
|
+
* The path's first coordinate becomes X and its second coordinate becomes Y.
|
|
79929
|
+
* Use this when lofting along X or Y and a rail lives in a horizontal sketch plane.
|
|
79930
|
+
*/
|
|
79931
|
+
pathOnXy(path2, z2 = 0) {
|
|
79932
|
+
if (!Number.isFinite(z2)) throw new Error("Loft.pathOnXy z must be finite");
|
|
79933
|
+
return mapLoftPath2D(path2, "Loft.pathOnXy", ([x2, y2]) => [x2, y2, z2]);
|
|
79934
|
+
},
|
|
79935
|
+
/**
|
|
79936
|
+
* Loft through profile stations while forcing generated sections to follow guide rails.
|
|
79937
|
+
*
|
|
79938
|
+
* Stations define the cross-section family. Guide rails define the side or center
|
|
79939
|
+
* paths the loft must pass through. With opposite side rails, the section is scaled
|
|
79940
|
+
* to touch both rails. With one side rail, the section keeps its interpolated size
|
|
79941
|
+
* unless a center rail is also present.
|
|
79942
|
+
*/
|
|
79943
|
+
withGuideRails(stations, rails, options = {}) {
|
|
79944
|
+
return loftWithGuideRails(stations, rails, options);
|
|
79945
|
+
}
|
|
79946
|
+
};
|
|
77979
79947
|
let collectedHighlights = [];
|
|
77980
79948
|
function resetHighlights() {
|
|
77981
79949
|
collectedHighlights = [];
|
|
@@ -294630,6 +296598,7 @@ function classifySdfPreviewNode(node) {
|
|
|
294630
296598
|
case "sdf:twist":
|
|
294631
296599
|
case "sdf:bend":
|
|
294632
296600
|
case "sdf:repeat":
|
|
296601
|
+
case "sdf:circularArray":
|
|
294633
296602
|
case "sdf:shell":
|
|
294634
296603
|
case "sdf:onion":
|
|
294635
296604
|
return classifySdfPreviewNode(node.child);
|
|
@@ -294639,10 +296608,7 @@ function classifySdfPreviewNode(node) {
|
|
|
294639
296608
|
reason: "This SDF uses a custom JavaScript displacement function that cannot be compiled for raymarch preview."
|
|
294640
296609
|
};
|
|
294641
296610
|
case "sdf:surfaceDisplace":
|
|
294642
|
-
return
|
|
294643
|
-
mode: "unsupported",
|
|
294644
|
-
reason: "This SDF uses surface displacement that is not yet available in the raymarch shader."
|
|
294645
|
-
};
|
|
296611
|
+
return classifySurfaceDisplacePreviewNode(node);
|
|
294646
296612
|
case "sdf:spatialBlend":
|
|
294647
296613
|
return {
|
|
294648
296614
|
mode: "unsupported",
|
|
@@ -294668,6 +296634,79 @@ ${node.shaderUnsupportedReason}` : ""}`
|
|
|
294668
296634
|
};
|
|
294669
296635
|
}
|
|
294670
296636
|
}
|
|
296637
|
+
function classifySurfaceDisplacePreviewNode(node) {
|
|
296638
|
+
if (!node.pattern) {
|
|
296639
|
+
return {
|
|
296640
|
+
mode: "unsupported",
|
|
296641
|
+
reason: "This SDF uses a custom JavaScript surface pattern that cannot be compiled for raymarch preview."
|
|
296642
|
+
};
|
|
296643
|
+
}
|
|
296644
|
+
const childResult = classifySdfPreviewNode(node.child);
|
|
296645
|
+
if (childResult.mode !== "raymarch") return childResult;
|
|
296646
|
+
const uv = analyzeShaderSurfaceUv(node.child, "p", node.uvMode);
|
|
296647
|
+
if (uv.mode === "triplanar") {
|
|
296648
|
+
return {
|
|
296649
|
+
mode: "unsupported",
|
|
296650
|
+
reason: "Typed surface displacement raymarch preview currently supports sphere, cylinder, and torus UV mappings."
|
|
296651
|
+
};
|
|
296652
|
+
}
|
|
296653
|
+
return { mode: "raymarch" };
|
|
296654
|
+
}
|
|
296655
|
+
function f2(value) {
|
|
296656
|
+
if (!Number.isFinite(value)) return "0.0";
|
|
296657
|
+
const text = Number(value.toPrecision(9)).toString();
|
|
296658
|
+
return text.includes(".") || text.includes("e") ? text : `${text}.0`;
|
|
296659
|
+
}
|
|
296660
|
+
function v3(value) {
|
|
296661
|
+
return `vec3(${f2(value[0])}, ${f2(value[1])}, ${f2(value[2])})`;
|
|
296662
|
+
}
|
|
296663
|
+
function analyzeShaderSurfaceUv(node, p2, override) {
|
|
296664
|
+
const analysis = analyzeShaderSurfaceUvNode(node, p2);
|
|
296665
|
+
if (!override || override === "auto") return analysis;
|
|
296666
|
+
if (override === "triplanar") return { mode: "triplanar" };
|
|
296667
|
+
if (analysis.mode === "triplanar") return analysis;
|
|
296668
|
+
if (override === analysis.mode) return analysis;
|
|
296669
|
+
if (override === "sphere" || override === "cylinder") {
|
|
296670
|
+
return { mode: override, localPoint: analysis.localPoint, radius: analysis.radius };
|
|
296671
|
+
}
|
|
296672
|
+
return analysis.mode === "torus" ? analysis : { mode: "triplanar" };
|
|
296673
|
+
}
|
|
296674
|
+
function analyzeShaderSurfaceUvNode(node, p2) {
|
|
296675
|
+
switch (node.kind) {
|
|
296676
|
+
case "sdf:sphere":
|
|
296677
|
+
return { mode: "sphere", localPoint: p2, radius: node.radius };
|
|
296678
|
+
case "sdf:cylinder":
|
|
296679
|
+
return { mode: "cylinder", localPoint: p2, radius: node.radius };
|
|
296680
|
+
case "sdf:torus":
|
|
296681
|
+
return { mode: "torus", localPoint: p2, radius: node.minorRadius, majorRadius: node.majorRadius };
|
|
296682
|
+
case "sdf:translate":
|
|
296683
|
+
return analyzeShaderSurfaceUvNode(node.child, `(${p2} - ${v3(node.offset)})`);
|
|
296684
|
+
case "sdf:rotate":
|
|
296685
|
+
return analyzeShaderSurfaceUvNode(node.child, `rotateInvEuler(${p2}, ${v3(node.degrees)})`);
|
|
296686
|
+
case "sdf:scale": {
|
|
296687
|
+
const result = analyzeShaderSurfaceUvNode(node.child, `(${p2} / ${f2(node.factor)})`);
|
|
296688
|
+
if (result.mode === "triplanar") return result;
|
|
296689
|
+
return {
|
|
296690
|
+
...result,
|
|
296691
|
+
radius: result.radius * node.factor,
|
|
296692
|
+
...result.mode === "torus" ? { majorRadius: result.majorRadius * node.factor } : {}
|
|
296693
|
+
};
|
|
296694
|
+
}
|
|
296695
|
+
case "sdf:shell":
|
|
296696
|
+
return analyzeShaderSurfaceUvNode(node.child, p2);
|
|
296697
|
+
case "sdf:union":
|
|
296698
|
+
case "sdf:smoothUnion":
|
|
296699
|
+
case "sdf:intersection":
|
|
296700
|
+
case "sdf:smoothIntersection":
|
|
296701
|
+
case "sdf:difference":
|
|
296702
|
+
case "sdf:smoothDifference":
|
|
296703
|
+
return node.children.length > 0 ? analyzeShaderSurfaceUvNode(node.children[0], p2) : { mode: "triplanar" };
|
|
296704
|
+
case "sdf:morph":
|
|
296705
|
+
return analyzeShaderSurfaceUvNode(node.a, p2);
|
|
296706
|
+
default:
|
|
296707
|
+
return { mode: "triplanar" };
|
|
296708
|
+
}
|
|
296709
|
+
}
|
|
294671
296710
|
function describeScriptResultType(value) {
|
|
294672
296711
|
var _a3, _b3;
|
|
294673
296712
|
if (value == null) return String(value);
|
|
@@ -294728,7 +296767,7 @@ function mapScriptResultToScene(args) {
|
|
|
294728
296767
|
var _a3;
|
|
294729
296768
|
const objects = [];
|
|
294730
296769
|
const shapeDimensions = [];
|
|
294731
|
-
const pushShape = (shape, name, groupName, color, treePath) => {
|
|
296770
|
+
const pushShape = (shape, name, groupName, color, treePath, tags = []) => {
|
|
294732
296771
|
const objectId = `obj-${objects.length + 1}`;
|
|
294733
296772
|
objects.push({
|
|
294734
296773
|
id: objectId,
|
|
@@ -294739,7 +296778,8 @@ function mapScriptResultToScene(args) {
|
|
|
294739
296778
|
materialProps: shape.materialProps,
|
|
294740
296779
|
geometryInfo: shape.geometryInfo(),
|
|
294741
296780
|
groupName,
|
|
294742
|
-
treePath: treePath && treePath.length > 0 ? [...treePath] : [name]
|
|
296781
|
+
treePath: treePath && treePath.length > 0 ? [...treePath] : [name],
|
|
296782
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
294743
296783
|
});
|
|
294744
296784
|
const dims = getShapeDimensions(shape);
|
|
294745
296785
|
dims.forEach((dim2) => {
|
|
@@ -294763,7 +296803,7 @@ function mapScriptResultToScene(args) {
|
|
|
294763
296803
|
});
|
|
294764
296804
|
}
|
|
294765
296805
|
};
|
|
294766
|
-
const pushSketch = (sketch, name, groupName, treePath) => {
|
|
296806
|
+
const pushSketch = (sketch, name, groupName, treePath, tags = []) => {
|
|
294767
296807
|
const meta2 = sketch instanceof ConstraintSketch ? sketch.constraintMeta : void 0;
|
|
294768
296808
|
objects.push({
|
|
294769
296809
|
id: `obj-${objects.length + 1}`,
|
|
@@ -294774,10 +296814,11 @@ function mapScriptResultToScene(args) {
|
|
|
294774
296814
|
sketchMeta: meta2,
|
|
294775
296815
|
color: sketch.colorHex,
|
|
294776
296816
|
groupName,
|
|
294777
|
-
treePath: treePath && treePath.length > 0 ? [...treePath] : [name]
|
|
296817
|
+
treePath: treePath && treePath.length > 0 ? [...treePath] : [name],
|
|
296818
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
294778
296819
|
});
|
|
294779
296820
|
};
|
|
294780
|
-
const pushSdf = (sdfShape, name, groupName, treePath, color) => {
|
|
296821
|
+
const pushSdf = (sdfShape, name, groupName, treePath, color, tags = []) => {
|
|
294781
296822
|
const preview = classifySdfPreviewNode(sdfShape._node);
|
|
294782
296823
|
const displayColor = color || sdfShape.colorHex;
|
|
294783
296824
|
const data = {
|
|
@@ -294797,7 +296838,8 @@ function mapScriptResultToScene(args) {
|
|
|
294797
296838
|
materialProps: sdfShape.materialProps,
|
|
294798
296839
|
geometryInfo: null,
|
|
294799
296840
|
groupName,
|
|
294800
|
-
treePath: treePath && treePath.length > 0 ? [...treePath] : [name]
|
|
296841
|
+
treePath: treePath && treePath.length > 0 ? [...treePath] : [name],
|
|
296842
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
294801
296843
|
});
|
|
294802
296844
|
};
|
|
294803
296845
|
const isNamedObject = (item) => {
|
|
@@ -294814,18 +296856,24 @@ function mapScriptResultToScene(args) {
|
|
|
294814
296856
|
const rootGroupChildLabel = (grp, index2) => {
|
|
294815
296857
|
return shapeGroupChildSegment(grp, index2, true);
|
|
294816
296858
|
};
|
|
294817
|
-
const flattenGroupChild = (child, label, groupName, treePath) => {
|
|
296859
|
+
const flattenGroupChild = (child, label, groupName, treePath, tags = []) => {
|
|
294818
296860
|
const resolvedTreePath = treePath && treePath.length > 0 ? treePath : [label];
|
|
294819
296861
|
if (child instanceof ShapeGroup) {
|
|
294820
296862
|
child.children.forEach((nested, i) => {
|
|
294821
|
-
flattenGroupChild(
|
|
296863
|
+
flattenGroupChild(
|
|
296864
|
+
nested,
|
|
296865
|
+
groupChildLabel(child, label, i),
|
|
296866
|
+
groupName,
|
|
296867
|
+
[...resolvedTreePath, shapeGroupChildSegment(child, i)],
|
|
296868
|
+
mergeSceneTags(tags, child.tagsForChild(i))
|
|
296869
|
+
);
|
|
294822
296870
|
});
|
|
294823
296871
|
return;
|
|
294824
296872
|
}
|
|
294825
296873
|
if (child instanceof Shape) {
|
|
294826
|
-
pushShape(child, label, groupName, void 0, resolvedTreePath);
|
|
296874
|
+
pushShape(child, label, groupName, void 0, resolvedTreePath, tags);
|
|
294827
296875
|
} else if (child instanceof Sketch) {
|
|
294828
|
-
pushSketch(child, label, groupName, resolvedTreePath);
|
|
296876
|
+
pushSketch(child, label, groupName, resolvedTreePath, tags);
|
|
294829
296877
|
}
|
|
294830
296878
|
};
|
|
294831
296879
|
const isPlainObject2 = (value) => {
|
|
@@ -294834,34 +296882,40 @@ function mapScriptResultToScene(args) {
|
|
|
294834
296882
|
return proto2 === Object.prototype || proto2 === null;
|
|
294835
296883
|
};
|
|
294836
296884
|
const joinName = (path2) => path2.join(".");
|
|
294837
|
-
const processRenderableTree = (value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = [], seen = /* @__PURE__ */ new WeakSet()) => {
|
|
296885
|
+
const processRenderableTree = (value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = [], inheritedTags = [], seen = /* @__PURE__ */ new WeakSet()) => {
|
|
294838
296886
|
const segment = fallbackSegment.trim().length > 0 ? fallbackSegment : fallbackLabel;
|
|
294839
296887
|
const treePath = [...parentTreePath, segment];
|
|
294840
296888
|
const name = joinName(treePath) || fallbackLabel;
|
|
294841
296889
|
if (value instanceof Assembly) {
|
|
294842
|
-
value.solve().toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath));
|
|
296890
|
+
value.solve().toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath, inheritedTags));
|
|
294843
296891
|
return;
|
|
294844
296892
|
}
|
|
294845
296893
|
if (value instanceof SolvedAssembly) {
|
|
294846
|
-
value.toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath));
|
|
296894
|
+
value.toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath, inheritedTags));
|
|
294847
296895
|
return;
|
|
294848
296896
|
}
|
|
294849
296897
|
if (value instanceof ShapeGroup) {
|
|
294850
296898
|
value.children.forEach((child, i) => {
|
|
294851
|
-
flattenGroupChild(
|
|
296899
|
+
flattenGroupChild(
|
|
296900
|
+
child,
|
|
296901
|
+
groupChildLabel(value, name, i),
|
|
296902
|
+
parentGroup,
|
|
296903
|
+
[...treePath, shapeGroupChildSegment(value, i)],
|
|
296904
|
+
mergeSceneTags(inheritedTags, value.tagsForChild(i))
|
|
296905
|
+
);
|
|
294852
296906
|
});
|
|
294853
296907
|
return;
|
|
294854
296908
|
}
|
|
294855
296909
|
if (value instanceof Shape) {
|
|
294856
|
-
pushShape(value, name, parentGroup, void 0, treePath);
|
|
296910
|
+
pushShape(value, name, parentGroup, void 0, treePath, inheritedTags);
|
|
294857
296911
|
return;
|
|
294858
296912
|
}
|
|
294859
296913
|
if (value instanceof Sketch) {
|
|
294860
|
-
pushSketch(value, name, parentGroup, treePath);
|
|
296914
|
+
pushSketch(value, name, parentGroup, treePath, inheritedTags);
|
|
294861
296915
|
return;
|
|
294862
296916
|
}
|
|
294863
296917
|
if (value instanceof SdfShape) {
|
|
294864
|
-
pushSdf(value, name, parentGroup, treePath);
|
|
296918
|
+
pushSdf(value, name, parentGroup, treePath, void 0, inheritedTags);
|
|
294865
296919
|
return;
|
|
294866
296920
|
}
|
|
294867
296921
|
if (value instanceof GCodeBuilder) {
|
|
@@ -294872,7 +296926,8 @@ function mapScriptResultToScene(args) {
|
|
|
294872
296926
|
sketch: null,
|
|
294873
296927
|
toolpath: value.build(),
|
|
294874
296928
|
geometryInfo: null,
|
|
294875
|
-
treePath
|
|
296929
|
+
treePath,
|
|
296930
|
+
...inheritedTags.length > 0 ? { tags: [...inheritedTags] } : {}
|
|
294876
296931
|
});
|
|
294877
296932
|
return;
|
|
294878
296933
|
}
|
|
@@ -294882,30 +296937,38 @@ function mapScriptResultToScene(args) {
|
|
|
294882
296937
|
value.forEach((item, index2) => {
|
|
294883
296938
|
const childSegment = `${index2 + 1}`;
|
|
294884
296939
|
const childLabel = `${name}.${childSegment}`;
|
|
294885
|
-
processRenderableTree(item, childLabel, childSegment, parentGroup, treePath, seen);
|
|
296940
|
+
processRenderableTree(item, childLabel, childSegment, parentGroup, treePath, inheritedTags, seen);
|
|
294886
296941
|
});
|
|
294887
296942
|
return;
|
|
294888
296943
|
}
|
|
294889
296944
|
if (isNamedObject(value)) {
|
|
294890
|
-
processNamedItem(value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath);
|
|
296945
|
+
processNamedItem(value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath, inheritedTags);
|
|
294891
296946
|
return;
|
|
294892
296947
|
}
|
|
294893
296948
|
if (isPlainObject2(value)) {
|
|
294894
296949
|
if (seen.has(value)) return;
|
|
294895
296950
|
seen.add(value);
|
|
294896
296951
|
Object.entries(value).forEach(([key, entry]) => {
|
|
294897
|
-
processRenderableTree(entry, key, key, parentGroup, treePath, seen);
|
|
296952
|
+
processRenderableTree(entry, key, key, parentGroup, treePath, inheritedTags, seen);
|
|
294898
296953
|
});
|
|
294899
296954
|
}
|
|
294900
296955
|
};
|
|
294901
|
-
const processNamedItem = (item, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = []) => {
|
|
296956
|
+
const processNamedItem = (item, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = [], inheritedTags = []) => {
|
|
296957
|
+
var _a4;
|
|
294902
296958
|
const name = typeof item.name === "string" && item.name.trim().length > 0 ? item.name : fallbackLabel;
|
|
294903
296959
|
const localSegment = typeof item.name === "string" && item.name.trim().length > 0 ? item.name : fallbackSegment;
|
|
294904
296960
|
const treePath = [...parentTreePath, localSegment];
|
|
294905
296961
|
const grp = parentGroup;
|
|
296962
|
+
const tags = mergeSceneTags(inheritedTags, (_a4 = item.metadata) == null ? void 0 : _a4.tags, item.tags);
|
|
294906
296963
|
if (item.group instanceof ShapeGroup) {
|
|
294907
296964
|
item.group.children.forEach((child, i) => {
|
|
294908
|
-
flattenGroupChild(
|
|
296965
|
+
flattenGroupChild(
|
|
296966
|
+
child,
|
|
296967
|
+
groupChildLabel(item.group, name, i),
|
|
296968
|
+
name,
|
|
296969
|
+
[...treePath, shapeGroupChildSegment(item.group, i)],
|
|
296970
|
+
mergeSceneTags(tags, item.group.tagsForChild(i))
|
|
296971
|
+
);
|
|
294909
296972
|
});
|
|
294910
296973
|
return;
|
|
294911
296974
|
}
|
|
@@ -294915,39 +296978,48 @@ function mapScriptResultToScene(args) {
|
|
|
294915
296978
|
const childTreePath = [...treePath, `${i + 1}`];
|
|
294916
296979
|
if (child instanceof ShapeGroup) {
|
|
294917
296980
|
child.children.forEach((nested, nestedIndex) => {
|
|
294918
|
-
flattenGroupChild(
|
|
294919
|
-
|
|
294920
|
-
|
|
294921
|
-
|
|
296981
|
+
flattenGroupChild(
|
|
296982
|
+
nested,
|
|
296983
|
+
groupChildLabel(child, name, nestedIndex),
|
|
296984
|
+
name,
|
|
296985
|
+
[...treePath, shapeGroupChildSegment(child, nestedIndex)],
|
|
296986
|
+
mergeSceneTags(tags, child.tagsForChild(nestedIndex))
|
|
296987
|
+
);
|
|
294922
296988
|
});
|
|
294923
296989
|
} else if (child instanceof Shape) {
|
|
294924
|
-
pushShape(child, childLabel, name, void 0, childTreePath);
|
|
296990
|
+
pushShape(child, childLabel, name, void 0, childTreePath, tags);
|
|
294925
296991
|
} else if (child instanceof Sketch) {
|
|
294926
|
-
pushSketch(child, childLabel, name, childTreePath);
|
|
296992
|
+
pushSketch(child, childLabel, name, childTreePath, tags);
|
|
294927
296993
|
} else if (child instanceof SdfShape) {
|
|
294928
|
-
pushSdf(child, childLabel, name, childTreePath);
|
|
296994
|
+
pushSdf(child, childLabel, name, childTreePath, void 0, tags);
|
|
294929
296995
|
} else if (isNamedObject(child)) {
|
|
294930
|
-
processNamedItem(child, childLabel, `${i + 1}`, name, treePath);
|
|
296996
|
+
processNamedItem(child, childLabel, `${i + 1}`, name, treePath, tags);
|
|
294931
296997
|
}
|
|
294932
296998
|
});
|
|
294933
296999
|
return;
|
|
294934
297000
|
}
|
|
294935
297001
|
if (item.shape instanceof ShapeGroup) {
|
|
294936
297002
|
item.shape.children.forEach(
|
|
294937
|
-
(child, i) => flattenGroupChild(
|
|
297003
|
+
(child, i) => flattenGroupChild(
|
|
297004
|
+
child,
|
|
297005
|
+
groupChildLabel(item.shape, name, i),
|
|
297006
|
+
name,
|
|
297007
|
+
[...treePath, shapeGroupChildSegment(item.shape, i)],
|
|
297008
|
+
mergeSceneTags(tags, item.shape.tagsForChild(i))
|
|
297009
|
+
)
|
|
294938
297010
|
);
|
|
294939
297011
|
return;
|
|
294940
297012
|
}
|
|
294941
297013
|
if (item.shape instanceof Shape) {
|
|
294942
|
-
pushShape(item.shape, name, grp, item.color, treePath);
|
|
297014
|
+
pushShape(item.shape, name, grp, item.color, treePath, tags);
|
|
294943
297015
|
return;
|
|
294944
297016
|
}
|
|
294945
297017
|
if (item.shape instanceof SdfShape) {
|
|
294946
|
-
pushSdf(item.shape, name, grp, treePath, item.color);
|
|
297018
|
+
pushSdf(item.shape, name, grp, treePath, item.color, tags);
|
|
294947
297019
|
return;
|
|
294948
297020
|
}
|
|
294949
297021
|
if (item.sdf instanceof SdfShape) {
|
|
294950
|
-
pushSdf(item.sdf, name, grp, treePath, item.color);
|
|
297022
|
+
pushSdf(item.sdf, name, grp, treePath, item.color, tags);
|
|
294951
297023
|
return;
|
|
294952
297024
|
}
|
|
294953
297025
|
if (item.sketch instanceof Sketch) {
|
|
@@ -294961,7 +297033,8 @@ function mapScriptResultToScene(args) {
|
|
|
294961
297033
|
sketchMeta: meta2,
|
|
294962
297034
|
color: item.color || item.sketch.colorHex,
|
|
294963
297035
|
groupName: grp,
|
|
294964
|
-
treePath
|
|
297036
|
+
treePath,
|
|
297037
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
294965
297038
|
});
|
|
294966
297039
|
}
|
|
294967
297040
|
};
|
|
@@ -294975,14 +297048,20 @@ function mapScriptResultToScene(args) {
|
|
|
294975
297048
|
} else if (result instanceof ShapeGroup) {
|
|
294976
297049
|
result.children.forEach((child, i) => {
|
|
294977
297050
|
const label = rootGroupChildLabel(result, i);
|
|
294978
|
-
flattenGroupChild(child, label, void 0, [label]);
|
|
297051
|
+
flattenGroupChild(child, label, void 0, [label], result.tagsForChild(i));
|
|
294979
297052
|
});
|
|
294980
297053
|
} else if (Array.isArray(result)) {
|
|
294981
297054
|
result.forEach((item, index2) => {
|
|
294982
297055
|
const label = `Object ${index2 + 1}`;
|
|
294983
297056
|
if (item instanceof ShapeGroup) {
|
|
294984
297057
|
item.children.forEach((child, i) => {
|
|
294985
|
-
flattenGroupChild(
|
|
297058
|
+
flattenGroupChild(
|
|
297059
|
+
child,
|
|
297060
|
+
groupChildLabel(item, label, i),
|
|
297061
|
+
void 0,
|
|
297062
|
+
[label, shapeGroupChildSegment(item, i)],
|
|
297063
|
+
item.tagsForChild(i)
|
|
297064
|
+
);
|
|
294986
297065
|
});
|
|
294987
297066
|
return;
|
|
294988
297067
|
}
|
|
@@ -295015,7 +297094,7 @@ function mapScriptResultToScene(args) {
|
|
|
295015
297094
|
} else if (defaultValue instanceof ShapeGroup) {
|
|
295016
297095
|
defaultValue.children.forEach((child, i) => {
|
|
295017
297096
|
const label = rootGroupChildLabel(defaultValue, i);
|
|
295018
|
-
flattenGroupChild(child, label, void 0, [label]);
|
|
297097
|
+
flattenGroupChild(child, label, void 0, [label], defaultValue.tagsForChild(i));
|
|
295019
297098
|
});
|
|
295020
297099
|
} else if (defaultValue instanceof Shape) {
|
|
295021
297100
|
pushShape(defaultValue, args.fileName, void 0, void 0, [args.fileName]);
|
|
@@ -295061,7 +297140,8 @@ function mapScriptResultToScene(args) {
|
|
|
295061
297140
|
name: `${mock2.name} (mock)`,
|
|
295062
297141
|
shape: mock2.shape,
|
|
295063
297142
|
sketch: null,
|
|
295064
|
-
mock: true
|
|
297143
|
+
mock: true,
|
|
297144
|
+
tags: ["mock"]
|
|
295065
297145
|
});
|
|
295066
297146
|
}
|
|
295067
297147
|
const hasSdfLeaves = objects.some((obj) => obj.sdf);
|
|
@@ -295332,6 +297412,7 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
295332
297412
|
nurbsSurface,
|
|
295333
297413
|
spline2d,
|
|
295334
297414
|
spline3d,
|
|
297415
|
+
Loft,
|
|
295335
297416
|
loft,
|
|
295336
297417
|
loftAlongSpine,
|
|
295337
297418
|
sweep,
|
|
@@ -296189,7 +298270,8 @@ function serializeRunResult(result, solverDebug = null) {
|
|
|
296189
298270
|
geometryInfo: obj.geometryInfo,
|
|
296190
298271
|
sketchMeta: obj.sketchMeta,
|
|
296191
298272
|
groupName: obj.groupName,
|
|
296192
|
-
treePath: obj.treePath
|
|
298273
|
+
treePath: obj.treePath,
|
|
298274
|
+
tags: obj.tags
|
|
296193
298275
|
};
|
|
296194
298276
|
if (serialized2.shapeData) {
|
|
296195
298277
|
transferables.push(
|
|
@@ -296235,6 +298317,7 @@ function serializeRunMetadata(result) {
|
|
|
296235
298317
|
sketchMeta: obj.sketchMeta,
|
|
296236
298318
|
groupName: obj.groupName,
|
|
296237
298319
|
treePath: obj.treePath,
|
|
298320
|
+
tags: obj.tags,
|
|
296238
298321
|
mock: obj.mock,
|
|
296239
298322
|
serverShapeRef: obj.serverShapeRef,
|
|
296240
298323
|
exactState: obj.exactState
|