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$8(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 flrm = /* @__PURE__ */ hMap(flt, 9, 1);
|
|
4648
4692
|
var 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
|
|
@@ -5125,8 +5169,8 @@ function parse3mf(data) {
|
|
|
5125
5169
|
while ((tMatch = trianglePattern.exec(meshXml)) !== null) {
|
|
5126
5170
|
const v1 = parseInt(tMatch[1], 10) + vertexOffset;
|
|
5127
5171
|
const v2 = parseInt(tMatch[2], 10) + vertexOffset;
|
|
5128
|
-
const
|
|
5129
|
-
allTriIndices.push(v1, v2,
|
|
5172
|
+
const v32 = parseInt(tMatch[3], 10) + vertexOffset;
|
|
5173
|
+
allTriIndices.push(v1, v2, v32);
|
|
5130
5174
|
}
|
|
5131
5175
|
for (let i = 0; i < meshVerts.length; i++) {
|
|
5132
5176
|
allPositions.push(meshVerts[i]);
|
|
@@ -5151,13 +5195,13 @@ function parseMeshFile(data, format) {
|
|
|
5151
5195
|
return parse3mf(data);
|
|
5152
5196
|
}
|
|
5153
5197
|
}
|
|
5154
|
-
const EPS$
|
|
5198
|
+
const EPS$a = 1e-8;
|
|
5155
5199
|
function length$3(v) {
|
|
5156
5200
|
return Math.hypot(v[0], v[1], v[2]);
|
|
5157
5201
|
}
|
|
5158
5202
|
function normalize$7(v) {
|
|
5159
5203
|
const len2 = length$3(v);
|
|
5160
|
-
if (len2 < EPS$
|
|
5204
|
+
if (len2 < EPS$a) throw new Error("Plane normal must be non-zero");
|
|
5161
5205
|
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
5162
5206
|
}
|
|
5163
5207
|
function resolvePlaneOriginNormal(plane) {
|
|
@@ -5179,12 +5223,12 @@ function resolvePlaneOriginNormal(plane) {
|
|
|
5179
5223
|
function rotationToPlaneSpace(normal) {
|
|
5180
5224
|
const n = normalize$7(normal);
|
|
5181
5225
|
const dot2 = n[2];
|
|
5182
|
-
if (dot2 > 1 - EPS$
|
|
5226
|
+
if (dot2 > 1 - EPS$a) {
|
|
5183
5227
|
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
5184
5228
|
}
|
|
5185
5229
|
let axis;
|
|
5186
5230
|
let angle;
|
|
5187
|
-
if (dot2 < -1 + EPS$
|
|
5231
|
+
if (dot2 < -1 + EPS$a) {
|
|
5188
5232
|
axis = [1, 0, 0];
|
|
5189
5233
|
angle = Math.PI;
|
|
5190
5234
|
} else {
|
|
@@ -5509,6 +5553,287 @@ function lowerShellShapeCompilePlanToConcretePlan(plan) {
|
|
|
5509
5553
|
}
|
|
5510
5554
|
return lowerBaseShellPlanToConcretePlan(plan.base, plan.thickness, normalizeShellOpenFaces(plan.openFaces));
|
|
5511
5555
|
}
|
|
5556
|
+
const DEFAULT_MAX_GRID_POINTS = 8e6;
|
|
5557
|
+
const DEFAULT_MIN_EDGE_LENGTH = 0.15;
|
|
5558
|
+
function resolveSdfMeshingSettings(tree, bounds, options = {}) {
|
|
5559
|
+
const quality = options.quality ?? "preview";
|
|
5560
|
+
const minEdgeLength = positiveOrDefault(options.minEdgeLength, DEFAULT_MIN_EDGE_LENGTH);
|
|
5561
|
+
const maxGridPoints = positiveOrDefault(options.maxGridPoints, DEFAULT_MAX_GRID_POINTS);
|
|
5562
|
+
const tolerance = options.tolerance !== void 0 ? requirePositiveFinite$3(options.tolerance, "SDF tolerance") : void 0;
|
|
5563
|
+
const minFeatureSize = options.minFeatureSize !== void 0 ? requirePositiveFinite$3(options.minFeatureSize, "SDF minFeatureSize") : void 0;
|
|
5564
|
+
const maxTriangles = options.maxTriangles !== void 0 ? Math.floor(requirePositiveFinite$3(options.maxTriangles, "SDF maxTriangles")) : void 0;
|
|
5565
|
+
const analysis = analyzeSdfTree(tree);
|
|
5566
|
+
const warnings = [];
|
|
5567
|
+
let edgeLength2;
|
|
5568
|
+
if (options.edgeLength !== void 0) {
|
|
5569
|
+
edgeLength2 = requirePositiveFinite$3(options.edgeLength, "SDF edgeLength");
|
|
5570
|
+
if (edgeLength2 < minEdgeLength) {
|
|
5571
|
+
warnings.push(`edgeLength ${formatMm(edgeLength2)} was clamped to minimum ${formatMm(minEdgeLength)}.`);
|
|
5572
|
+
edgeLength2 = minEdgeLength;
|
|
5573
|
+
}
|
|
5574
|
+
} else {
|
|
5575
|
+
edgeLength2 = resolveDefaultEdgeLength(bounds, quality, minEdgeLength, analysis, options);
|
|
5576
|
+
}
|
|
5577
|
+
if (analysis.minWallThickness < Infinity && analysis.minWallThickness < edgeLength2 * 2) {
|
|
5578
|
+
analysis.riskFlags.add("thin-shell");
|
|
5579
|
+
warnings.push(
|
|
5580
|
+
`shell/wall thickness ${formatMm(analysis.minWallThickness)} is below 2 x edgeLength ${formatMm(edgeLength2)}; thin features may be under-sampled.`
|
|
5581
|
+
);
|
|
5582
|
+
}
|
|
5583
|
+
if (!options.bounds && analysis.hasInfiniteRepeat) {
|
|
5584
|
+
warnings.push("infinite repeat bounds are heuristic; pass .toShape({ bounds }) for predictable clipping.");
|
|
5585
|
+
}
|
|
5586
|
+
if (!options.bounds && analysis.riskFlags.has("noise")) {
|
|
5587
|
+
warnings.push("noise field bounds are heuristic; pass .toShape({ bounds }) for predictable clipping.");
|
|
5588
|
+
}
|
|
5589
|
+
if (!options.bounds && (analysis.riskFlags.has("tpms") || analysis.riskFlags.has("voronoi"))) {
|
|
5590
|
+
warnings.push("TPMS/Voronoi bounds are heuristic unless clipped or passed explicitly.");
|
|
5591
|
+
}
|
|
5592
|
+
if (analysis.hasLegacyTpmsThreshold) {
|
|
5593
|
+
warnings.push("TPMS thickness is using legacy field-threshold units; use wallThickness for approximate millimeters.");
|
|
5594
|
+
}
|
|
5595
|
+
return {
|
|
5596
|
+
quality,
|
|
5597
|
+
edgeLength: edgeLength2,
|
|
5598
|
+
tolerance,
|
|
5599
|
+
minFeatureSize,
|
|
5600
|
+
minEdgeLength,
|
|
5601
|
+
simplify: resolveSimplificationMode(options.simplify, quality, analysis.riskFlags),
|
|
5602
|
+
maxTriangles,
|
|
5603
|
+
maxGridPoints,
|
|
5604
|
+
diagnostics: options.diagnostics === true,
|
|
5605
|
+
treeRiskFlags: [...analysis.riskFlags].sort(),
|
|
5606
|
+
warnings
|
|
5607
|
+
};
|
|
5608
|
+
}
|
|
5609
|
+
function withScaledSdfEdgeLength(settings, edgeLength2) {
|
|
5610
|
+
return { ...settings, edgeLength: Math.max(settings.minEdgeLength, edgeLength2) };
|
|
5611
|
+
}
|
|
5612
|
+
function createSdfMeshingDiagnostics(settings, bounds, paddedBounds) {
|
|
5613
|
+
const grid = estimateSdfGridDimensions(paddedBounds, settings.edgeLength);
|
|
5614
|
+
const estimatedSamples = grid[0] * grid[1] * grid[2];
|
|
5615
|
+
return {
|
|
5616
|
+
bounds: cloneBounds$2(bounds),
|
|
5617
|
+
paddedBounds: cloneBounds$2(paddedBounds),
|
|
5618
|
+
edgeLength: settings.edgeLength,
|
|
5619
|
+
grid,
|
|
5620
|
+
estimatedSamples,
|
|
5621
|
+
estimatedMemoryBytes: estimatedSamples * 8,
|
|
5622
|
+
treeRiskFlags: [...settings.treeRiskFlags],
|
|
5623
|
+
simplification: settings.simplify,
|
|
5624
|
+
capMode: "box",
|
|
5625
|
+
capInset: settings.edgeLength,
|
|
5626
|
+
warnings: [...settings.warnings]
|
|
5627
|
+
};
|
|
5628
|
+
}
|
|
5629
|
+
function assertSdfMeshingBudget(diagnostics, maxGridPoints) {
|
|
5630
|
+
if (diagnostics.estimatedSamples <= maxGridPoints) return;
|
|
5631
|
+
const suggestedEdge = suggestEdgeLengthForSampleBudget(diagnostics.paddedBounds, maxGridPoints);
|
|
5632
|
+
throw new Error(
|
|
5633
|
+
`SDF meshing would sample ${formatCount(diagnostics.estimatedSamples)} grid points (~${formatBytes(
|
|
5634
|
+
diagnostics.estimatedMemoryBytes
|
|
5635
|
+
)}). Reduce bounds or use edgeLength >= ${formatMm(suggestedEdge)}.`
|
|
5636
|
+
);
|
|
5637
|
+
}
|
|
5638
|
+
function estimateSdfGridDimensions(bounds, edgeLength2) {
|
|
5639
|
+
const dx = bounds.max[0] - bounds.min[0];
|
|
5640
|
+
const dy = bounds.max[1] - bounds.min[1];
|
|
5641
|
+
const dz = bounds.max[2] - bounds.min[2];
|
|
5642
|
+
return [
|
|
5643
|
+
Math.max(2, Math.ceil(dx / edgeLength2) + 1),
|
|
5644
|
+
Math.max(2, Math.ceil(dy / edgeLength2) + 1),
|
|
5645
|
+
Math.max(2, Math.ceil(dz / edgeLength2) + 1)
|
|
5646
|
+
];
|
|
5647
|
+
}
|
|
5648
|
+
function logSdfMeshingDiagnostics(prefix, diagnostics) {
|
|
5649
|
+
const warnings = diagnostics.warnings.length > 0 ? `, warnings=${diagnostics.warnings.join(" | ")}` : "";
|
|
5650
|
+
const evaluator = diagnostics.evaluator ? `, evaluator=${diagnostics.evaluator}${diagnostics.evaluatorUnsupportedReason ? ` (${diagnostics.evaluatorUnsupportedReason})` : ""}` : "";
|
|
5651
|
+
console.info(
|
|
5652
|
+
`${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}`
|
|
5653
|
+
);
|
|
5654
|
+
}
|
|
5655
|
+
function resolveDefaultEdgeLength(bounds, quality, minEdgeLength, analysis, options) {
|
|
5656
|
+
const dx = bounds.max[0] - bounds.min[0];
|
|
5657
|
+
const dy = bounds.max[1] - bounds.min[1];
|
|
5658
|
+
const dz = bounds.max[2] - bounds.min[2];
|
|
5659
|
+
const maxDim = Math.max(dx, dy, dz, minEdgeLength);
|
|
5660
|
+
const divisor = quality === "draft" ? 60 : quality === "export" ? 160 : 100;
|
|
5661
|
+
const candidates = [maxDim / divisor];
|
|
5662
|
+
if (options.tolerance !== void 0) candidates.push(requirePositiveFinite$3(options.tolerance, "SDF tolerance") * 2);
|
|
5663
|
+
if (options.minFeatureSize !== void 0) candidates.push(requirePositiveFinite$3(options.minFeatureSize, "SDF minFeatureSize") / 2.5);
|
|
5664
|
+
if (analysis.minMetricTpmsThickness < Infinity) candidates.push(analysis.minMetricTpmsThickness / 2);
|
|
5665
|
+
if (analysis.minTpmsCellSize < Infinity) candidates.push(analysis.minTpmsCellSize / 10);
|
|
5666
|
+
if (analysis.minRepeatSpacing < Infinity) candidates.push(analysis.minRepeatSpacing / 8);
|
|
5667
|
+
if (analysis.minWallThickness < Infinity) candidates.push(analysis.minWallThickness / 2.5);
|
|
5668
|
+
return Math.max(minEdgeLength, Math.min(...candidates.filter((v) => Number.isFinite(v) && v > 0)));
|
|
5669
|
+
}
|
|
5670
|
+
function resolveSimplificationMode(simplify, quality, riskFlags) {
|
|
5671
|
+
if (simplify === false) return "off";
|
|
5672
|
+
if (simplify === true || simplify === "safe") return "safe";
|
|
5673
|
+
if (quality === "export" && riskFlags.size > 0) return "off";
|
|
5674
|
+
return "safe";
|
|
5675
|
+
}
|
|
5676
|
+
function analyzeSdfTree(tree) {
|
|
5677
|
+
const analysis = {
|
|
5678
|
+
riskFlags: /* @__PURE__ */ new Set(),
|
|
5679
|
+
minTpmsCellSize: Infinity,
|
|
5680
|
+
minMetricTpmsThickness: Infinity,
|
|
5681
|
+
minRepeatSpacing: Infinity,
|
|
5682
|
+
minWallThickness: Infinity,
|
|
5683
|
+
hasInfiniteRepeat: false,
|
|
5684
|
+
hasLegacyTpmsThreshold: false
|
|
5685
|
+
};
|
|
5686
|
+
visitSdfNode(tree, analysis);
|
|
5687
|
+
return analysis;
|
|
5688
|
+
}
|
|
5689
|
+
function minPositive(...values) {
|
|
5690
|
+
let result = Infinity;
|
|
5691
|
+
for (const value of values) {
|
|
5692
|
+
if (value !== null && value !== void 0 && Number.isFinite(value) && value > 0) {
|
|
5693
|
+
result = Math.min(result, value);
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
return result === Infinity ? null : result;
|
|
5697
|
+
}
|
|
5698
|
+
function estimateSurfacePatternSpacing(pattern) {
|
|
5699
|
+
switch (pattern.kind) {
|
|
5700
|
+
case "surfacePattern:constant":
|
|
5701
|
+
return null;
|
|
5702
|
+
case "surfacePattern:sineWave":
|
|
5703
|
+
return pattern.wavelength;
|
|
5704
|
+
case "surfacePattern:stripes":
|
|
5705
|
+
return Math.min(pattern.spacing, pattern.width);
|
|
5706
|
+
case "surfacePattern:overUnderWeave":
|
|
5707
|
+
return Math.min(...pattern.spacing, ...pattern.threadWidth);
|
|
5708
|
+
case "surfacePattern:abs":
|
|
5709
|
+
case "surfacePattern:negate":
|
|
5710
|
+
return estimateSurfacePatternSpacing(pattern.child);
|
|
5711
|
+
case "surfacePattern:add":
|
|
5712
|
+
case "surfacePattern:multiply":
|
|
5713
|
+
case "surfacePattern:min":
|
|
5714
|
+
case "surfacePattern:max":
|
|
5715
|
+
return minPositive(...pattern.children.map(estimateSurfacePatternSpacing));
|
|
5716
|
+
case "surfacePattern:clamp":
|
|
5717
|
+
return estimateSurfacePatternSpacing(pattern.child);
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
function visitSdfNode(node, analysis) {
|
|
5721
|
+
switch (node.kind) {
|
|
5722
|
+
case "sdf:union":
|
|
5723
|
+
case "sdf:difference":
|
|
5724
|
+
case "sdf:intersection":
|
|
5725
|
+
case "sdf:smoothUnion":
|
|
5726
|
+
case "sdf:smoothDifference":
|
|
5727
|
+
case "sdf:smoothIntersection":
|
|
5728
|
+
for (const child of node.children) visitSdfNode(child, analysis);
|
|
5729
|
+
break;
|
|
5730
|
+
case "sdf:morph":
|
|
5731
|
+
case "sdf:spatialBlend":
|
|
5732
|
+
visitSdfNode(node.a, analysis);
|
|
5733
|
+
visitSdfNode(node.b, analysis);
|
|
5734
|
+
break;
|
|
5735
|
+
case "sdf:translate":
|
|
5736
|
+
case "sdf:rotate":
|
|
5737
|
+
case "sdf:scale":
|
|
5738
|
+
case "sdf:twist":
|
|
5739
|
+
case "sdf:bend":
|
|
5740
|
+
case "sdf:onion":
|
|
5741
|
+
visitSdfNode(node.child, analysis);
|
|
5742
|
+
break;
|
|
5743
|
+
case "sdf:repeat":
|
|
5744
|
+
analysis.riskFlags.add("repeat");
|
|
5745
|
+
for (let i = 0; i < 3; i++) {
|
|
5746
|
+
const spacing = node.spacing[i];
|
|
5747
|
+
if (spacing > 0) {
|
|
5748
|
+
analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, spacing);
|
|
5749
|
+
if (node.count[i] <= 0) analysis.hasInfiniteRepeat = true;
|
|
5750
|
+
}
|
|
5751
|
+
}
|
|
5752
|
+
visitSdfNode(node.child, analysis);
|
|
5753
|
+
break;
|
|
5754
|
+
case "sdf:circularArray": {
|
|
5755
|
+
analysis.riskFlags.add("repeat");
|
|
5756
|
+
if (node.offset > 0) {
|
|
5757
|
+
analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, 2 * Math.PI * node.offset / node.count);
|
|
5758
|
+
}
|
|
5759
|
+
visitSdfNode(node.child, analysis);
|
|
5760
|
+
break;
|
|
5761
|
+
}
|
|
5762
|
+
case "sdf:shell":
|
|
5763
|
+
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.thickness);
|
|
5764
|
+
visitSdfNode(node.child, analysis);
|
|
5765
|
+
break;
|
|
5766
|
+
case "sdf:displace":
|
|
5767
|
+
case "sdf:surfaceDisplace":
|
|
5768
|
+
analysis.riskFlags.add("displacement");
|
|
5769
|
+
if (node.kind === "sdf:surfaceDisplace" && node.pattern) {
|
|
5770
|
+
const spacing = estimateSurfacePatternSpacing(node.pattern);
|
|
5771
|
+
if (spacing !== null) analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, spacing);
|
|
5772
|
+
}
|
|
5773
|
+
visitSdfNode(node.child, analysis);
|
|
5774
|
+
break;
|
|
5775
|
+
case "sdf:gyroid":
|
|
5776
|
+
case "sdf:schwarzP":
|
|
5777
|
+
case "sdf:diamond":
|
|
5778
|
+
case "sdf:lidinoid":
|
|
5779
|
+
analysis.riskFlags.add("tpms");
|
|
5780
|
+
analysis.minTpmsCellSize = Math.min(analysis.minTpmsCellSize, node.cellSize);
|
|
5781
|
+
if (node.thicknessMode === "metric-approx") {
|
|
5782
|
+
analysis.minMetricTpmsThickness = Math.min(analysis.minMetricTpmsThickness, node.thickness);
|
|
5783
|
+
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.thickness);
|
|
5784
|
+
} else {
|
|
5785
|
+
analysis.hasLegacyTpmsThreshold = true;
|
|
5786
|
+
}
|
|
5787
|
+
break;
|
|
5788
|
+
case "sdf:noise":
|
|
5789
|
+
analysis.riskFlags.add("noise");
|
|
5790
|
+
break;
|
|
5791
|
+
case "sdf:voronoi":
|
|
5792
|
+
analysis.riskFlags.add("voronoi");
|
|
5793
|
+
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.wallThickness);
|
|
5794
|
+
if (node.surfaceChild) visitSdfNode(node.surfaceChild, analysis);
|
|
5795
|
+
break;
|
|
5796
|
+
case "sdf:custom":
|
|
5797
|
+
analysis.riskFlags.add("custom");
|
|
5798
|
+
break;
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
function positiveOrDefault(value, fallback) {
|
|
5802
|
+
if (value === void 0) return fallback;
|
|
5803
|
+
return requirePositiveFinite$3(value, "SDF meshing option");
|
|
5804
|
+
}
|
|
5805
|
+
function requirePositiveFinite$3(value, name) {
|
|
5806
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
5807
|
+
throw new Error(`${name} must be a positive finite number.`);
|
|
5808
|
+
}
|
|
5809
|
+
return value;
|
|
5810
|
+
}
|
|
5811
|
+
function cloneBounds$2(bounds) {
|
|
5812
|
+
return { min: [...bounds.min], max: [...bounds.max] };
|
|
5813
|
+
}
|
|
5814
|
+
function suggestEdgeLengthForSampleBudget(bounds, maxGridPoints) {
|
|
5815
|
+
const dx = bounds.max[0] - bounds.min[0];
|
|
5816
|
+
const dy = bounds.max[1] - bounds.min[1];
|
|
5817
|
+
const dz = bounds.max[2] - bounds.min[2];
|
|
5818
|
+
const volume = Math.max(dx * dy * dz, 1);
|
|
5819
|
+
return Math.cbrt(volume / Math.max(maxGridPoints, 8));
|
|
5820
|
+
}
|
|
5821
|
+
function formatBounds(bounds) {
|
|
5822
|
+
return `[${bounds.min.map(formatNumber$1).join(",")}]-[${bounds.max.map(formatNumber$1).join(",")}]`;
|
|
5823
|
+
}
|
|
5824
|
+
function formatMm(value) {
|
|
5825
|
+
return `${formatNumber$1(value)}mm`;
|
|
5826
|
+
}
|
|
5827
|
+
function formatNumber$1(value) {
|
|
5828
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
5829
|
+
}
|
|
5830
|
+
function formatCount(value) {
|
|
5831
|
+
return Math.round(value).toLocaleString("en-US");
|
|
5832
|
+
}
|
|
5833
|
+
function formatBytes(bytes) {
|
|
5834
|
+
if (bytes < 1024 * 1024) return `${Math.ceil(bytes / 1024)} KB`;
|
|
5835
|
+
return `${Math.ceil(bytes / (1024 * 1024))} MB`;
|
|
5836
|
+
}
|
|
5512
5837
|
const grad3 = new Float64Array([
|
|
5513
5838
|
1,
|
|
5514
5839
|
1,
|
|
@@ -5969,8 +6294,8 @@ function triplanarWeights(nx, ny, nz, sharpness) {
|
|
|
5969
6294
|
const inv = 1 / sum2;
|
|
5970
6295
|
return { wx: wx * inv, wy: wy * inv, wz: wz * inv };
|
|
5971
6296
|
}
|
|
5972
|
-
const { atan2, acos, cos: cos$
|
|
5973
|
-
const DEG$
|
|
6297
|
+
const { atan2, acos, cos: cos$3, sin: sin$3, sqrt: sqrt$3, PI: PI$3 } = Math;
|
|
6298
|
+
const DEG$3 = PI$3 / 180;
|
|
5974
6299
|
const IDENTITY = (p2) => p2;
|
|
5975
6300
|
function analyzeUV(node, override) {
|
|
5976
6301
|
if (override) {
|
|
@@ -5997,10 +6322,10 @@ function analyzeNodeUV(node, toLocal) {
|
|
|
5997
6322
|
return analyzeNodeUV(node.child, next);
|
|
5998
6323
|
}
|
|
5999
6324
|
case "sdf:rotate": {
|
|
6000
|
-
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$
|
|
6001
|
-
const cx = cos$
|
|
6002
|
-
const cy = cos$
|
|
6003
|
-
const cz = cos$
|
|
6325
|
+
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$3);
|
|
6326
|
+
const cx = cos$3(rx), sx = sin$3(rx);
|
|
6327
|
+
const cy = cos$3(ry), sy = sin$3(ry);
|
|
6328
|
+
const cz = cos$3(rz), sz = sin$3(rz);
|
|
6004
6329
|
const prev = toLocal;
|
|
6005
6330
|
const next = (p2) => {
|
|
6006
6331
|
const pp = prev(p2);
|
|
@@ -6057,7 +6382,7 @@ function compileUVFunction(analysis) {
|
|
|
6057
6382
|
return (p2) => {
|
|
6058
6383
|
const lp = toLocal(p2);
|
|
6059
6384
|
const u2 = atan2(lp[1], lp[0]) * R;
|
|
6060
|
-
const len2 = sqrt$
|
|
6385
|
+
const len2 = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1] + lp[2] * lp[2]);
|
|
6061
6386
|
const v = acos(clampUnit(lp[2] / (len2 || 1))) * R;
|
|
6062
6387
|
return [u2, v];
|
|
6063
6388
|
};
|
|
@@ -6077,23 +6402,23 @@ function compileUVFunction(analysis) {
|
|
|
6077
6402
|
return (p2) => {
|
|
6078
6403
|
const lp = toLocal(p2);
|
|
6079
6404
|
const u2 = atan2(lp[1], lp[0]) * R;
|
|
6080
|
-
const xyDist = sqrt$
|
|
6405
|
+
const xyDist = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1]) - R;
|
|
6081
6406
|
const v = atan2(lp[2], xyDist) * r;
|
|
6082
6407
|
return [u2, v];
|
|
6083
6408
|
};
|
|
6084
6409
|
}
|
|
6085
6410
|
}
|
|
6086
6411
|
}
|
|
6087
|
-
const { abs: abs$1, cos: cos$
|
|
6088
|
-
const TAU = 2 * PI$
|
|
6089
|
-
const GRAD_EPS = 1e-9;
|
|
6412
|
+
const { abs: abs$1, cos: cos$2, sin: sin$2, sqrt: sqrt$2, PI: PI$2 } = Math;
|
|
6413
|
+
const TAU$1 = 2 * PI$2;
|
|
6414
|
+
const GRAD_EPS$1 = 1e-9;
|
|
6090
6415
|
function gyroidValueAndGradient(x2, y2, z2, cellSize) {
|
|
6091
|
-
const s = TAU / cellSize;
|
|
6416
|
+
const s = TAU$1 / cellSize;
|
|
6092
6417
|
const xs = x2 * s;
|
|
6093
6418
|
const ys = y2 * s;
|
|
6094
6419
|
const zs = z2 * s;
|
|
6095
|
-
const sx = sin$
|
|
6096
|
-
const cx = cos$
|
|
6420
|
+
const sx = sin$2(xs), sy = sin$2(ys), sz = sin$2(zs);
|
|
6421
|
+
const cx = cos$2(xs), cy = cos$2(ys), cz = cos$2(zs);
|
|
6097
6422
|
return {
|
|
6098
6423
|
value: sx * cy + sy * cz + sz * cx,
|
|
6099
6424
|
gx: s * (cx * cy - sz * sx),
|
|
@@ -6102,24 +6427,24 @@ function gyroidValueAndGradient(x2, y2, z2, cellSize) {
|
|
|
6102
6427
|
};
|
|
6103
6428
|
}
|
|
6104
6429
|
function schwarzPValueAndGradient(x2, y2, z2, cellSize) {
|
|
6105
|
-
const s = TAU / cellSize;
|
|
6430
|
+
const s = TAU$1 / cellSize;
|
|
6106
6431
|
const xs = x2 * s;
|
|
6107
6432
|
const ys = y2 * s;
|
|
6108
6433
|
const zs = z2 * s;
|
|
6109
6434
|
return {
|
|
6110
|
-
value: cos$
|
|
6111
|
-
gx: -s * sin$
|
|
6112
|
-
gy: -s * sin$
|
|
6113
|
-
gz: -s * sin$
|
|
6435
|
+
value: cos$2(xs) + cos$2(ys) + cos$2(zs),
|
|
6436
|
+
gx: -s * sin$2(xs),
|
|
6437
|
+
gy: -s * sin$2(ys),
|
|
6438
|
+
gz: -s * sin$2(zs)
|
|
6114
6439
|
};
|
|
6115
6440
|
}
|
|
6116
6441
|
function diamondValueAndGradient(x2, y2, z2, cellSize) {
|
|
6117
|
-
const s = TAU / cellSize;
|
|
6442
|
+
const s = TAU$1 / cellSize;
|
|
6118
6443
|
const xs = x2 * s;
|
|
6119
6444
|
const ys = y2 * s;
|
|
6120
6445
|
const zs = z2 * s;
|
|
6121
|
-
const sx = sin$
|
|
6122
|
-
const cx = cos$
|
|
6446
|
+
const sx = sin$2(xs), sy = sin$2(ys), sz = sin$2(zs);
|
|
6447
|
+
const cx = cos$2(xs), cy = cos$2(ys), cz = cos$2(zs);
|
|
6123
6448
|
return {
|
|
6124
6449
|
value: sx * sy * sz + sx * cy * cz + cx * sy * cz + cx * cy * sz,
|
|
6125
6450
|
gx: s * (cx * sy * sz + cx * cy * cz - sx * sy * cz - sx * cy * sz),
|
|
@@ -6128,12 +6453,12 @@ function diamondValueAndGradient(x2, y2, z2, cellSize) {
|
|
|
6128
6453
|
};
|
|
6129
6454
|
}
|
|
6130
6455
|
function lidinoidValueAndGradient(x2, y2, z2, cellSize) {
|
|
6131
|
-
const s = TAU / cellSize;
|
|
6456
|
+
const s = TAU$1 / cellSize;
|
|
6132
6457
|
const sx2 = x2 * s, sy2 = y2 * s, sz2 = z2 * s;
|
|
6133
|
-
const sx = sin$
|
|
6134
|
-
const cx = cos$
|
|
6135
|
-
const s2x = sin$
|
|
6136
|
-
const c2x = cos$
|
|
6458
|
+
const sx = sin$2(sx2), sy = sin$2(sy2), sz = sin$2(sz2);
|
|
6459
|
+
const cx = cos$2(sx2), cy = cos$2(sy2), cz = cos$2(sz2);
|
|
6460
|
+
const s2x = sin$2(2 * sx2), s2y = sin$2(2 * sy2), s2z = sin$2(2 * sz2);
|
|
6461
|
+
const c2x = cos$2(2 * sx2), c2y = cos$2(2 * sy2), c2z = cos$2(2 * sz2);
|
|
6137
6462
|
const val = s2x * cy * sz + s2y * cz * sx + s2z * cx * sy - c2x * c2y - c2y * c2z - c2z * c2x + 0.3;
|
|
6138
6463
|
return {
|
|
6139
6464
|
value: val,
|
|
@@ -6156,8 +6481,8 @@ function lidinoid$1(x2, y2, z2, cellSize, thickness, thicknessMode) {
|
|
|
6156
6481
|
}
|
|
6157
6482
|
function tpmsDistance({ value, gx, gy, gz }, thickness, thicknessMode) {
|
|
6158
6483
|
if (thicknessMode !== "metric-approx") return abs$1(value) - thickness;
|
|
6159
|
-
const grad = sqrt$
|
|
6160
|
-
return abs$1(value) / Math.max(grad, GRAD_EPS) - thickness * 0.5;
|
|
6484
|
+
const grad = sqrt$2(gx * gx + gy * gy + gz * gz);
|
|
6485
|
+
return abs$1(value) / Math.max(grad, GRAD_EPS$1) - thickness * 0.5;
|
|
6161
6486
|
}
|
|
6162
6487
|
function mix(h) {
|
|
6163
6488
|
h = (h ^ h >>> 16) * 2246822507 | 0;
|
|
@@ -6253,76 +6578,76 @@ function seededWorley3Surface(seed) {
|
|
|
6253
6578
|
const s = seed | 0;
|
|
6254
6579
|
return (x2, y2, z2, nx, ny, nz, threshold) => worleySurface(x2, y2, z2, s, nx, ny, nz, threshold);
|
|
6255
6580
|
}
|
|
6256
|
-
const { abs, cos, max, min, sin, sqrt, PI } = Math;
|
|
6257
|
-
const DEG$
|
|
6581
|
+
const { abs, cos: cos$1, max: max$1, min, sin: sin$1, sqrt: sqrt$1, PI: PI$1 } = Math;
|
|
6582
|
+
const DEG$2 = PI$1 / 180;
|
|
6258
6583
|
function clamp$b(v, lo, hi) {
|
|
6259
6584
|
return v < lo ? lo : v > hi ? hi : v;
|
|
6260
6585
|
}
|
|
6261
|
-
function length2(x2, y2) {
|
|
6262
|
-
return sqrt(x2 * x2 + y2 * y2);
|
|
6586
|
+
function length2$1(x2, y2) {
|
|
6587
|
+
return sqrt$1(x2 * x2 + y2 * y2);
|
|
6263
6588
|
}
|
|
6264
|
-
function length3(x2, y2, z2) {
|
|
6265
|
-
return sqrt(x2 * x2 + y2 * y2 + z2 * z2);
|
|
6589
|
+
function length3$1(x2, y2, z2) {
|
|
6590
|
+
return sqrt$1(x2 * x2 + y2 * y2 + z2 * z2);
|
|
6266
6591
|
}
|
|
6267
|
-
function sdSphere(px, py, pz, r) {
|
|
6268
|
-
return length3(px, py, pz) - r;
|
|
6592
|
+
function sdSphere$1(px, py, pz, r) {
|
|
6593
|
+
return length3$1(px, py, pz) - r;
|
|
6269
6594
|
}
|
|
6270
|
-
function sdBox(px, py, pz, hx, hy, hz) {
|
|
6595
|
+
function sdBox$1(px, py, pz, hx, hy, hz) {
|
|
6271
6596
|
const dx = abs(px) - hx;
|
|
6272
6597
|
const dy = abs(py) - hy;
|
|
6273
6598
|
const dz = abs(pz) - hz;
|
|
6274
|
-
return length3(max(dx, 0), max(dy, 0), max(dz, 0)) + min(max(dx, dy, dz), 0);
|
|
6599
|
+
return length3$1(max$1(dx, 0), max$1(dy, 0), max$1(dz, 0)) + min(max$1(dx, dy, dz), 0);
|
|
6275
6600
|
}
|
|
6276
|
-
function sdCylinder(px, py, pz, h, r) {
|
|
6277
|
-
const dx = length2(px, py) - r;
|
|
6601
|
+
function sdCylinder$1(px, py, pz, h, r) {
|
|
6602
|
+
const dx = length2$1(px, py) - r;
|
|
6278
6603
|
const dz = abs(pz) - h * 0.5;
|
|
6279
|
-
return length2(max(dx, 0), max(dz, 0)) + min(max(dx, dz), 0);
|
|
6604
|
+
return length2$1(max$1(dx, 0), max$1(dz, 0)) + min(max$1(dx, dz), 0);
|
|
6280
6605
|
}
|
|
6281
|
-
function sdTorus(px, py, pz, R, r) {
|
|
6282
|
-
const qx = length2(px, py) - R;
|
|
6283
|
-
return length2(qx, pz) - r;
|
|
6606
|
+
function sdTorus$1(px, py, pz, R, r) {
|
|
6607
|
+
const qx = length2$1(px, py) - R;
|
|
6608
|
+
return length2$1(qx, pz) - r;
|
|
6284
6609
|
}
|
|
6285
|
-
function sdCapsule(px, py, pz, h, r) {
|
|
6610
|
+
function sdCapsule$1(px, py, pz, h, r) {
|
|
6286
6611
|
const halfH = h * 0.5;
|
|
6287
6612
|
const cz = clamp$b(pz, -halfH, halfH);
|
|
6288
|
-
return length3(px, py, pz - cz) - r;
|
|
6613
|
+
return length3$1(px, py, pz - cz) - r;
|
|
6289
6614
|
}
|
|
6290
|
-
function sdCone(px, py, pz, h, r) {
|
|
6291
|
-
const q = length2(px, py);
|
|
6292
|
-
const cLen = length2(h, r);
|
|
6615
|
+
function sdCone$1(px, py, pz, h, r) {
|
|
6616
|
+
const q = length2$1(px, py);
|
|
6617
|
+
const cLen = length2$1(h, r);
|
|
6293
6618
|
const nx = h / cLen;
|
|
6294
6619
|
const nz = -r / cLen;
|
|
6295
|
-
const d2 = max(nx * q + nz * (pz - h), -pz, pz - h);
|
|
6620
|
+
const d2 = max$1(nx * q + nz * (pz - h), -pz, pz - h);
|
|
6296
6621
|
return d2;
|
|
6297
6622
|
}
|
|
6298
|
-
function sdTaperedSegment(px, py, pz, ax, ay, az, bx, by, bz, ra, rb) {
|
|
6623
|
+
function sdTaperedSegment$1(px, py, pz, ax, ay, az, bx, by, bz, ra, rb) {
|
|
6299
6624
|
const vx = bx - ax;
|
|
6300
6625
|
const vy = by - ay;
|
|
6301
6626
|
const vz = bz - az;
|
|
6302
6627
|
const len2 = vx * vx + vy * vy + vz * vz;
|
|
6303
|
-
if (len2 <= 1e-12) return sdSphere(px - ax, py - ay, pz - az, max(ra, rb));
|
|
6628
|
+
if (len2 <= 1e-12) return sdSphere$1(px - ax, py - ay, pz - az, max$1(ra, rb));
|
|
6304
6629
|
const h = clamp$b(((px - ax) * vx + (py - ay) * vy + (pz - az) * vz) / len2, 0, 1);
|
|
6305
|
-
return length3(px - (ax + vx * h), py - (ay + vy * h), pz - (az + vz * h)) - (ra + (rb - ra) * h);
|
|
6630
|
+
return length3$1(px - (ax + vx * h), py - (ay + vy * h), pz - (az + vz * h)) - (ra + (rb - ra) * h);
|
|
6306
6631
|
}
|
|
6307
6632
|
function sdPolylineSweep3(node, x2, y2, z2) {
|
|
6308
6633
|
let d2 = 1e20;
|
|
6309
6634
|
for (let i = 0; i < node.points.length - 1; i++) {
|
|
6310
6635
|
const a2 = node.points[i];
|
|
6311
6636
|
const b = node.points[i + 1];
|
|
6312
|
-
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]);
|
|
6313
|
-
d2 = i === 0 ? segment : smin(d2, segment, node.blend);
|
|
6637
|
+
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]);
|
|
6638
|
+
d2 = i === 0 ? segment : smin$1(d2, segment, node.blend);
|
|
6314
6639
|
}
|
|
6315
6640
|
return d2;
|
|
6316
6641
|
}
|
|
6317
|
-
function smin(a2, b, k2) {
|
|
6642
|
+
function smin$1(a2, b, k2) {
|
|
6318
6643
|
if (k2 <= 0) return min(a2, b);
|
|
6319
|
-
const h = max(k2 - abs(a2 - b), 0) / k2;
|
|
6644
|
+
const h = max$1(k2 - abs(a2 - b), 0) / k2;
|
|
6320
6645
|
return min(a2, b) - h * h * h * k2 * (1 / 6);
|
|
6321
6646
|
}
|
|
6322
|
-
function smax(a2, b, k2) {
|
|
6323
|
-
return -smin(-a2, -b, k2);
|
|
6647
|
+
function smax$1(a2, b, k2) {
|
|
6648
|
+
return -smin$1(-a2, -b, k2);
|
|
6324
6649
|
}
|
|
6325
|
-
function repeatCoord(v, spacing, count) {
|
|
6650
|
+
function repeatCoord$1(v, spacing, count) {
|
|
6326
6651
|
if (spacing <= 0) return v;
|
|
6327
6652
|
if (count > 0) {
|
|
6328
6653
|
const center = (count - 1) * 0.5;
|
|
@@ -6331,31 +6656,138 @@ function repeatCoord(v, spacing, count) {
|
|
|
6331
6656
|
}
|
|
6332
6657
|
return v - spacing * Math.round(v / spacing);
|
|
6333
6658
|
}
|
|
6659
|
+
function positiveMod(v, period) {
|
|
6660
|
+
return (v % period + period) % period;
|
|
6661
|
+
}
|
|
6662
|
+
function evalStripesPattern(u2, v, directionX, directionY, spacing, width, depth) {
|
|
6663
|
+
const coord = u2 * directionX + v * directionY;
|
|
6664
|
+
const d2 = abs(coord - Math.round(coord / spacing) * spacing);
|
|
6665
|
+
const profile = max$1(0, 1 - d2 / (width * 0.5));
|
|
6666
|
+
return -(profile * profile) * depth;
|
|
6667
|
+
}
|
|
6668
|
+
function evalOverUnderWeavePattern(u2, v, spacingX, spacingY, widthX, widthY, depth, underScale) {
|
|
6669
|
+
const su = u2 / spacingX;
|
|
6670
|
+
const sv = v / spacingY;
|
|
6671
|
+
let pU = max$1(0, 1 - abs(su - Math.round(su)) * spacingX / (widthX * 0.5));
|
|
6672
|
+
let pV = max$1(0, 1 - abs(sv - Math.round(sv)) * spacingY / (widthY * 0.5));
|
|
6673
|
+
pU *= pU;
|
|
6674
|
+
pV *= pV;
|
|
6675
|
+
const checker = (Math.round(su) & 65535) + (Math.round(sv) & 65535) & 1;
|
|
6676
|
+
const top = checker ? pV : pU;
|
|
6677
|
+
const bot = checker ? pU : pV;
|
|
6678
|
+
return -max$1(top, bot * underScale) * depth;
|
|
6679
|
+
}
|
|
6680
|
+
function compileTypedSurfacePattern(pattern) {
|
|
6681
|
+
switch (pattern.kind) {
|
|
6682
|
+
case "surfacePattern:constant":
|
|
6683
|
+
return () => pattern.value;
|
|
6684
|
+
case "surfacePattern:sineWave": {
|
|
6685
|
+
const { direction: direction2, wavelength, amplitude, phase, bias } = pattern;
|
|
6686
|
+
const frequency = 2 * PI$1 / wavelength;
|
|
6687
|
+
return (u2, v) => bias + sin$1((u2 * direction2[0] + v * direction2[1]) * frequency + phase) * amplitude;
|
|
6688
|
+
}
|
|
6689
|
+
case "surfacePattern:stripes": {
|
|
6690
|
+
const { direction: direction2, spacing, width, depth } = pattern;
|
|
6691
|
+
return (u2, v) => evalStripesPattern(u2, v, direction2[0], direction2[1], spacing, width, depth);
|
|
6692
|
+
}
|
|
6693
|
+
case "surfacePattern:overUnderWeave": {
|
|
6694
|
+
const { spacing, threadWidth, depth, underScale } = pattern;
|
|
6695
|
+
return (u2, v) => evalOverUnderWeavePattern(u2, v, spacing[0], spacing[1], threadWidth[0], threadWidth[1], depth, underScale);
|
|
6696
|
+
}
|
|
6697
|
+
case "surfacePattern:abs": {
|
|
6698
|
+
const child = compileTypedSurfacePattern(pattern.child);
|
|
6699
|
+
return (u2, v) => abs(child(u2, v));
|
|
6700
|
+
}
|
|
6701
|
+
case "surfacePattern:negate": {
|
|
6702
|
+
const child = compileTypedSurfacePattern(pattern.child);
|
|
6703
|
+
return (u2, v) => -child(u2, v);
|
|
6704
|
+
}
|
|
6705
|
+
case "surfacePattern:add": {
|
|
6706
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
6707
|
+
return (u2, v) => children.reduce((sum2, child) => sum2 + child(u2, v), 0);
|
|
6708
|
+
}
|
|
6709
|
+
case "surfacePattern:multiply": {
|
|
6710
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
6711
|
+
return (u2, v) => children.reduce((product, child) => product * child(u2, v), 1);
|
|
6712
|
+
}
|
|
6713
|
+
case "surfacePattern:min": {
|
|
6714
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
6715
|
+
if (children.length === 0) return () => 0;
|
|
6716
|
+
return (u2, v) => children.reduce((value, child) => min(value, child(u2, v)), Infinity);
|
|
6717
|
+
}
|
|
6718
|
+
case "surfacePattern:max": {
|
|
6719
|
+
const children = pattern.children.map(compileTypedSurfacePattern);
|
|
6720
|
+
if (children.length === 0) return () => 0;
|
|
6721
|
+
return (u2, v) => children.reduce((value, child) => max$1(value, child(u2, v)), -Infinity);
|
|
6722
|
+
}
|
|
6723
|
+
case "surfacePattern:clamp": {
|
|
6724
|
+
const child = compileTypedSurfacePattern(pattern.child);
|
|
6725
|
+
return (u2, v) => clamp$b(child(u2, v), pattern.min, pattern.max);
|
|
6726
|
+
}
|
|
6727
|
+
}
|
|
6728
|
+
}
|
|
6729
|
+
function estimateSurfacePatternAmplitude(pattern) {
|
|
6730
|
+
switch (pattern.kind) {
|
|
6731
|
+
case "surfacePattern:constant":
|
|
6732
|
+
return abs(pattern.value);
|
|
6733
|
+
case "surfacePattern:sineWave":
|
|
6734
|
+
return abs(pattern.bias) + abs(pattern.amplitude);
|
|
6735
|
+
case "surfacePattern:stripes":
|
|
6736
|
+
case "surfacePattern:overUnderWeave":
|
|
6737
|
+
return pattern.depth;
|
|
6738
|
+
case "surfacePattern:abs":
|
|
6739
|
+
case "surfacePattern:negate":
|
|
6740
|
+
return estimateSurfacePatternAmplitude(pattern.child);
|
|
6741
|
+
case "surfacePattern:add": {
|
|
6742
|
+
const amplitudes = pattern.children.map(estimateSurfacePatternAmplitude);
|
|
6743
|
+
return amplitudes.every((value) => value !== null) ? amplitudes.reduce((sum2, value) => sum2 + value, 0) : null;
|
|
6744
|
+
}
|
|
6745
|
+
case "surfacePattern:multiply": {
|
|
6746
|
+
const amplitudes = pattern.children.map(estimateSurfacePatternAmplitude);
|
|
6747
|
+
return amplitudes.every((value) => value !== null) ? amplitudes.reduce((product, value) => product * value, 1) : null;
|
|
6748
|
+
}
|
|
6749
|
+
case "surfacePattern:min":
|
|
6750
|
+
case "surfacePattern:max": {
|
|
6751
|
+
const amplitudes = pattern.children.map(estimateSurfacePatternAmplitude);
|
|
6752
|
+
return amplitudes.every((value) => value !== null) ? max$1(...amplitudes) : null;
|
|
6753
|
+
}
|
|
6754
|
+
case "surfacePattern:clamp":
|
|
6755
|
+
return max$1(abs(pattern.min), abs(pattern.max));
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6758
|
+
function compileSurfacePattern(node) {
|
|
6759
|
+
if (node.pattern) return compileTypedSurfacePattern(node.pattern);
|
|
6760
|
+
const constEntries = Object.entries(node.constants ?? {});
|
|
6761
|
+
const constNames = constEntries.map(([k2]) => k2);
|
|
6762
|
+
const constValues = constEntries.map(([, v]) => v);
|
|
6763
|
+
const patternFn = new Function("u", "v", ...constNames, `return (${node.patternBody});`);
|
|
6764
|
+
return (u2, v) => patternFn(u2, v, ...constValues);
|
|
6765
|
+
}
|
|
6334
6766
|
function compileSdfNode3(node) {
|
|
6335
6767
|
switch (node.kind) {
|
|
6336
6768
|
case "sdf:sphere": {
|
|
6337
6769
|
const r = node.radius;
|
|
6338
|
-
return (x2, y2, z2) => sdSphere(x2, y2, z2, r);
|
|
6770
|
+
return (x2, y2, z2) => sdSphere$1(x2, y2, z2, r);
|
|
6339
6771
|
}
|
|
6340
6772
|
case "sdf:box": {
|
|
6341
6773
|
const [hx, hy, hz] = node.halfExtents;
|
|
6342
|
-
return (x2, y2, z2) => sdBox(x2, y2, z2, hx, hy, hz);
|
|
6774
|
+
return (x2, y2, z2) => sdBox$1(x2, y2, z2, hx, hy, hz);
|
|
6343
6775
|
}
|
|
6344
6776
|
case "sdf:cylinder": {
|
|
6345
6777
|
const { height: h, radius: r } = node;
|
|
6346
|
-
return (x2, y2, z2) => sdCylinder(x2, y2, z2, h, r);
|
|
6778
|
+
return (x2, y2, z2) => sdCylinder$1(x2, y2, z2, h, r);
|
|
6347
6779
|
}
|
|
6348
6780
|
case "sdf:torus": {
|
|
6349
6781
|
const { majorRadius: R, minorRadius: r } = node;
|
|
6350
|
-
return (x2, y2, z2) => sdTorus(x2, y2, z2, R, r);
|
|
6782
|
+
return (x2, y2, z2) => sdTorus$1(x2, y2, z2, R, r);
|
|
6351
6783
|
}
|
|
6352
6784
|
case "sdf:capsule": {
|
|
6353
6785
|
const { height: h, radius: r } = node;
|
|
6354
|
-
return (x2, y2, z2) => sdCapsule(x2, y2, z2, h, r);
|
|
6786
|
+
return (x2, y2, z2) => sdCapsule$1(x2, y2, z2, h, r);
|
|
6355
6787
|
}
|
|
6356
6788
|
case "sdf:cone": {
|
|
6357
6789
|
const { height: h, radius: r } = node;
|
|
6358
|
-
return (x2, y2, z2) => sdCone(x2, y2, z2, h, r);
|
|
6790
|
+
return (x2, y2, z2) => sdCone$1(x2, y2, z2, h, r);
|
|
6359
6791
|
}
|
|
6360
6792
|
case "sdf:polylineSweep": {
|
|
6361
6793
|
return (x2, y2, z2) => sdPolylineSweep3(node, x2, y2, z2);
|
|
@@ -6372,7 +6804,7 @@ function compileSdfNode3(node) {
|
|
|
6372
6804
|
const fns = node.children.map(compileSdfNode3);
|
|
6373
6805
|
return (x2, y2, z2) => {
|
|
6374
6806
|
let d2 = fns[0](x2, y2, z2);
|
|
6375
|
-
for (let i = 1; i < fns.length; i++) d2 = max(d2, -fns[i](x2, y2, z2));
|
|
6807
|
+
for (let i = 1; i < fns.length; i++) d2 = max$1(d2, -fns[i](x2, y2, z2));
|
|
6376
6808
|
return d2;
|
|
6377
6809
|
};
|
|
6378
6810
|
}
|
|
@@ -6380,7 +6812,7 @@ function compileSdfNode3(node) {
|
|
|
6380
6812
|
const fns = node.children.map(compileSdfNode3);
|
|
6381
6813
|
return (x2, y2, z2) => {
|
|
6382
6814
|
let d2 = fns[0](x2, y2, z2);
|
|
6383
|
-
for (let i = 1; i < fns.length; i++) d2 = max(d2, fns[i](x2, y2, z2));
|
|
6815
|
+
for (let i = 1; i < fns.length; i++) d2 = max$1(d2, fns[i](x2, y2, z2));
|
|
6384
6816
|
return d2;
|
|
6385
6817
|
};
|
|
6386
6818
|
}
|
|
@@ -6389,7 +6821,7 @@ function compileSdfNode3(node) {
|
|
|
6389
6821
|
const k2 = node.radius;
|
|
6390
6822
|
return (x2, y2, z2) => {
|
|
6391
6823
|
let d2 = fns[0](x2, y2, z2);
|
|
6392
|
-
for (let i = 1; i < fns.length; i++) d2 = smin(d2, fns[i](x2, y2, z2), k2);
|
|
6824
|
+
for (let i = 1; i < fns.length; i++) d2 = smin$1(d2, fns[i](x2, y2, z2), k2);
|
|
6393
6825
|
return d2;
|
|
6394
6826
|
};
|
|
6395
6827
|
}
|
|
@@ -6398,7 +6830,7 @@ function compileSdfNode3(node) {
|
|
|
6398
6830
|
const k2 = node.radius;
|
|
6399
6831
|
return (x2, y2, z2) => {
|
|
6400
6832
|
let d2 = fns[0](x2, y2, z2);
|
|
6401
|
-
for (let i = 1; i < fns.length; i++) d2 = smax(d2, -fns[i](x2, y2, z2), k2);
|
|
6833
|
+
for (let i = 1; i < fns.length; i++) d2 = smax$1(d2, -fns[i](x2, y2, z2), k2);
|
|
6402
6834
|
return d2;
|
|
6403
6835
|
};
|
|
6404
6836
|
}
|
|
@@ -6407,7 +6839,7 @@ function compileSdfNode3(node) {
|
|
|
6407
6839
|
const k2 = node.radius;
|
|
6408
6840
|
return (x2, y2, z2) => {
|
|
6409
6841
|
let d2 = fns[0](x2, y2, z2);
|
|
6410
|
-
for (let i = 1; i < fns.length; i++) d2 = smax(d2, fns[i](x2, y2, z2), k2);
|
|
6842
|
+
for (let i = 1; i < fns.length; i++) d2 = smax$1(d2, fns[i](x2, y2, z2), k2);
|
|
6411
6843
|
return d2;
|
|
6412
6844
|
};
|
|
6413
6845
|
}
|
|
@@ -6425,10 +6857,10 @@ function compileSdfNode3(node) {
|
|
|
6425
6857
|
}
|
|
6426
6858
|
case "sdf:rotate": {
|
|
6427
6859
|
const fn = compileSdfNode3(node.child);
|
|
6428
|
-
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$
|
|
6429
|
-
const cx = cos(rx), sx = sin(rx);
|
|
6430
|
-
const cy = cos(ry), sy = sin(ry);
|
|
6431
|
-
const cz = cos(rz), sz = sin(rz);
|
|
6860
|
+
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$2);
|
|
6861
|
+
const cx = cos$1(rx), sx = sin$1(rx);
|
|
6862
|
+
const cy = cos$1(ry), sy = sin$1(ry);
|
|
6863
|
+
const cz = cos$1(rz), sz = sin$1(rz);
|
|
6432
6864
|
return (x2, y2, z2) => {
|
|
6433
6865
|
const x1 = cz * x2 + sz * y2;
|
|
6434
6866
|
const y1 = -sz * x2 + cz * y2;
|
|
@@ -6447,10 +6879,10 @@ function compileSdfNode3(node) {
|
|
|
6447
6879
|
}
|
|
6448
6880
|
case "sdf:twist": {
|
|
6449
6881
|
const fn = compileSdfNode3(node.child);
|
|
6450
|
-
const k2 = node.degreesPerUnit * DEG$
|
|
6882
|
+
const k2 = node.degreesPerUnit * DEG$2;
|
|
6451
6883
|
return (x2, y2, z2) => {
|
|
6452
6884
|
const angle = k2 * z2;
|
|
6453
|
-
const c2 = cos(angle), s = sin(angle);
|
|
6885
|
+
const c2 = cos$1(angle), s = sin$1(angle);
|
|
6454
6886
|
return fn(c2 * x2 - s * y2, s * x2 + c2 * y2, z2);
|
|
6455
6887
|
};
|
|
6456
6888
|
}
|
|
@@ -6459,7 +6891,7 @@ function compileSdfNode3(node) {
|
|
|
6459
6891
|
const r = node.radius;
|
|
6460
6892
|
return (x2, y2, z2) => {
|
|
6461
6893
|
const angle = x2 / r;
|
|
6462
|
-
const c2 = cos(angle), s = sin(angle);
|
|
6894
|
+
const c2 = cos$1(angle), s = sin$1(angle);
|
|
6463
6895
|
return fn((r + y2) * s, (r + y2) * c2 - r, z2);
|
|
6464
6896
|
};
|
|
6465
6897
|
}
|
|
@@ -6467,7 +6899,19 @@ function compileSdfNode3(node) {
|
|
|
6467
6899
|
const fn = compileSdfNode3(node.child);
|
|
6468
6900
|
const [sx, sy, sz] = node.spacing;
|
|
6469
6901
|
const [cx, cy, cz] = node.count;
|
|
6470
|
-
return (x2, y2, z2) => fn(repeatCoord(x2, sx, cx), repeatCoord(y2, sy, cy), repeatCoord(z2, sz, cz));
|
|
6902
|
+
return (x2, y2, z2) => fn(repeatCoord$1(x2, sx, cx), repeatCoord$1(y2, sy, cy), repeatCoord$1(z2, sz, cz));
|
|
6903
|
+
}
|
|
6904
|
+
case "sdf:circularArray": {
|
|
6905
|
+
const fn = compileSdfNode3(node.child);
|
|
6906
|
+
const da = 2 * PI$1 / node.count;
|
|
6907
|
+
const offset2 = node.offset;
|
|
6908
|
+
return (x2, y2, z2) => {
|
|
6909
|
+
const r = length2$1(x2, y2);
|
|
6910
|
+
const a2 = positiveMod(Math.atan2(y2, x2), da);
|
|
6911
|
+
const d1 = fn(cos$1(a2 - da) * r - offset2, sin$1(a2 - da) * r, z2);
|
|
6912
|
+
const d2 = fn(cos$1(a2) * r - offset2, sin$1(a2) * r, z2);
|
|
6913
|
+
return min(d1, d2);
|
|
6914
|
+
};
|
|
6471
6915
|
}
|
|
6472
6916
|
case "sdf:shell": {
|
|
6473
6917
|
const fn = compileSdfNode3(node.child);
|
|
@@ -6484,10 +6928,7 @@ function compileSdfNode3(node) {
|
|
|
6484
6928
|
}
|
|
6485
6929
|
case "sdf:surfaceDisplace": {
|
|
6486
6930
|
const childFn = compileSdfNode3(node.child);
|
|
6487
|
-
const
|
|
6488
|
-
const constNames = constEntries.map(([k2]) => k2);
|
|
6489
|
-
const constValues = constEntries.map(([, v]) => v);
|
|
6490
|
-
const patternFn = new Function("u", "v", ...constNames, `return (${node.patternBody});`);
|
|
6931
|
+
const patternFn = compileSurfacePattern(node);
|
|
6491
6932
|
const uvMode = node.uvMode && node.uvMode !== "auto" ? node.uvMode : void 0;
|
|
6492
6933
|
const analysis = analyzeUV(node.child, uvMode);
|
|
6493
6934
|
const uvFn = compileUVFunction(analysis);
|
|
@@ -6499,7 +6940,7 @@ function compileSdfNode3(node) {
|
|
|
6499
6940
|
p2[1] = y2;
|
|
6500
6941
|
p2[2] = z2;
|
|
6501
6942
|
const [u2, v] = uvFn(p2);
|
|
6502
|
-
return d2 + patternFn(u2, v
|
|
6943
|
+
return d2 + patternFn(u2, v);
|
|
6503
6944
|
};
|
|
6504
6945
|
}
|
|
6505
6946
|
const sharpness = node.triplanarSharpness ?? 4;
|
|
@@ -6510,9 +6951,9 @@ function compileSdfNode3(node) {
|
|
|
6510
6951
|
const gy = childFn(x2, y2 + eps, z2) - childFn(x2, y2 - eps, z2);
|
|
6511
6952
|
const gz = childFn(x2, y2, z2 + eps) - childFn(x2, y2, z2 - eps);
|
|
6512
6953
|
const { wx, wy, wz } = triplanarWeights(gx, gy, gz, sharpness);
|
|
6513
|
-
const hX = patternFn(y2, z2
|
|
6514
|
-
const hY = patternFn(x2, z2
|
|
6515
|
-
const hZ = patternFn(x2, y2
|
|
6954
|
+
const hX = patternFn(y2, z2);
|
|
6955
|
+
const hY = patternFn(x2, z2);
|
|
6956
|
+
const hZ = patternFn(x2, y2);
|
|
6516
6957
|
return d2 + wx * hX + wy * hY + wz * hZ;
|
|
6517
6958
|
};
|
|
6518
6959
|
}
|
|
@@ -6582,7 +7023,7 @@ function compileSdfNode3(node) {
|
|
|
6582
7023
|
const gx = gradFn(x2 + eps, y2, z2) - gradFn(x2 - eps, y2, z2);
|
|
6583
7024
|
const gy = gradFn(x2, y2 + eps, z2) - gradFn(x2, y2 - eps, z2);
|
|
6584
7025
|
const gz = gradFn(x2, y2, z2 + eps) - gradFn(x2, y2, z2 - eps);
|
|
6585
|
-
const glen = sqrt(gx * gx + gy * gy + gz * gz);
|
|
7026
|
+
const glen = sqrt$1(gx * gx + gy * gy + gz * gz);
|
|
6586
7027
|
let nx = 0, ny = 0, nz = 0;
|
|
6587
7028
|
if (glen > 1e-10) {
|
|
6588
7029
|
const invG = 1 / glen;
|
|
@@ -6644,7 +7085,7 @@ function estimateSdfBounds(node) {
|
|
|
6644
7085
|
for (const point2 of node.points) {
|
|
6645
7086
|
for (let i = 0; i < 3; i++) {
|
|
6646
7087
|
minPoint[i] = min(minPoint[i], point2[i]);
|
|
6647
|
-
maxPoint[i] = max(maxPoint[i], point2[i]);
|
|
7088
|
+
maxPoint[i] = max$1(maxPoint[i], point2[i]);
|
|
6648
7089
|
}
|
|
6649
7090
|
}
|
|
6650
7091
|
return padBounds({ min: minPoint, max: maxPoint }, pad);
|
|
@@ -6675,7 +7116,7 @@ function estimateSdfBounds(node) {
|
|
|
6675
7116
|
}
|
|
6676
7117
|
case "sdf:rotate": {
|
|
6677
7118
|
const b = estimateSdfBounds(node.child);
|
|
6678
|
-
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])));
|
|
7119
|
+
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])));
|
|
6679
7120
|
return { min: [-r, -r, -r], max: [r, r, r] };
|
|
6680
7121
|
}
|
|
6681
7122
|
case "sdf:scale": {
|
|
@@ -6689,7 +7130,7 @@ function estimateSdfBounds(node) {
|
|
|
6689
7130
|
case "sdf:twist":
|
|
6690
7131
|
case "sdf:bend": {
|
|
6691
7132
|
const b = estimateSdfBounds(node.child);
|
|
6692
|
-
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;
|
|
7133
|
+
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;
|
|
6693
7134
|
return { min: [-r, -r, -r], max: [r, r, r] };
|
|
6694
7135
|
}
|
|
6695
7136
|
case "sdf:repeat": {
|
|
@@ -6710,16 +7151,29 @@ function estimateSdfBounds(node) {
|
|
|
6710
7151
|
const [zMin, zMax] = expand(sz, cz, b.min[2], b.max[2]);
|
|
6711
7152
|
return { min: [xMin, yMin, zMin], max: [xMax, yMax, zMax] };
|
|
6712
7153
|
}
|
|
7154
|
+
case "sdf:circularArray": {
|
|
7155
|
+
const b = estimateSdfBounds(node.child);
|
|
7156
|
+
const x0 = b.min[0] + node.offset;
|
|
7157
|
+
const x1 = b.max[0] + node.offset;
|
|
7158
|
+
const y0 = b.min[1];
|
|
7159
|
+
const y1 = b.max[1];
|
|
7160
|
+
const r = max$1(length2$1(x0, y0), length2$1(x0, y1), length2$1(x1, y0), length2$1(x1, y1));
|
|
7161
|
+
return { min: [-r, -r, b.min[2]], max: [r, r, b.max[2]] };
|
|
7162
|
+
}
|
|
6713
7163
|
case "sdf:shell": {
|
|
6714
7164
|
const b = estimateSdfBounds(node.child);
|
|
6715
7165
|
const t = node.thickness * 0.5;
|
|
6716
7166
|
return padBounds(b, t);
|
|
6717
7167
|
}
|
|
6718
|
-
case "sdf:displace":
|
|
6719
|
-
case "sdf:surfaceDisplace": {
|
|
7168
|
+
case "sdf:displace": {
|
|
6720
7169
|
const b = estimateSdfBounds(node.child);
|
|
6721
7170
|
return padBounds(b, 5);
|
|
6722
7171
|
}
|
|
7172
|
+
case "sdf:surfaceDisplace": {
|
|
7173
|
+
const b = estimateSdfBounds(node.child);
|
|
7174
|
+
if (!node.pattern) return padBounds(b, 5);
|
|
7175
|
+
return padBounds(b, estimateSurfacePatternAmplitude(node.pattern) ?? 5);
|
|
7176
|
+
}
|
|
6723
7177
|
case "sdf:onion": {
|
|
6724
7178
|
const b = estimateSdfBounds(node.child);
|
|
6725
7179
|
return padBounds(b, node.layers * node.thickness);
|
|
@@ -6756,7 +7210,7 @@ function unionBounds(bounds, pad) {
|
|
|
6756
7210
|
for (const b of bounds) {
|
|
6757
7211
|
for (let i = 0; i < 3; i++) {
|
|
6758
7212
|
result.min[i] = min(result.min[i], b.min[i]);
|
|
6759
|
-
result.max[i] = max(result.max[i], b.max[i]);
|
|
7213
|
+
result.max[i] = max$1(result.max[i], b.max[i]);
|
|
6760
7214
|
}
|
|
6761
7215
|
}
|
|
6762
7216
|
if (pad > 0) return padBounds(result, pad);
|
|
@@ -6769,7 +7223,7 @@ function intersectBounds(bounds, pad) {
|
|
|
6769
7223
|
};
|
|
6770
7224
|
for (const b of bounds) {
|
|
6771
7225
|
for (let i = 0; i < 3; i++) {
|
|
6772
|
-
result.min[i] = max(result.min[i], b.min[i]);
|
|
7226
|
+
result.min[i] = max$1(result.min[i], b.min[i]);
|
|
6773
7227
|
result.max[i] = min(result.max[i], b.max[i]);
|
|
6774
7228
|
}
|
|
6775
7229
|
}
|
|
@@ -6785,242 +7239,544 @@ function padBounds(b, pad) {
|
|
|
6785
7239
|
max: [b.max[0] + pad, b.max[1] + pad, b.max[2] + pad]
|
|
6786
7240
|
};
|
|
6787
7241
|
}
|
|
6788
|
-
const
|
|
6789
|
-
const
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
7242
|
+
const { PI } = Math;
|
|
7243
|
+
const DEG$1 = PI / 180;
|
|
7244
|
+
const TAU = 2 * PI;
|
|
7245
|
+
const GRAD_EPS = 1e-9;
|
|
7246
|
+
const Op = {
|
|
7247
|
+
Const: 0,
|
|
7248
|
+
Neg: 1,
|
|
7249
|
+
Abs: 2,
|
|
7250
|
+
Sqrt: 3,
|
|
7251
|
+
Sin: 4,
|
|
7252
|
+
Cos: 5,
|
|
7253
|
+
Round: 6,
|
|
7254
|
+
Add: 7,
|
|
7255
|
+
Sub: 8,
|
|
7256
|
+
Mul: 9,
|
|
7257
|
+
Div: 10,
|
|
7258
|
+
Min: 11,
|
|
7259
|
+
Max: 12
|
|
7260
|
+
};
|
|
7261
|
+
class UnsupportedSdfProgramNodeError extends Error {
|
|
7262
|
+
constructor(message) {
|
|
7263
|
+
super(message);
|
|
7264
|
+
this.name = "UnsupportedSdfProgramNodeError";
|
|
6808
7265
|
}
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
);
|
|
7266
|
+
}
|
|
7267
|
+
class SdfProgramBuilder {
|
|
7268
|
+
constructor() {
|
|
7269
|
+
__publicField(this, "opcodes", []);
|
|
7270
|
+
__publicField(this, "argA", []);
|
|
7271
|
+
__publicField(this, "argB", []);
|
|
7272
|
+
__publicField(this, "argC", []);
|
|
7273
|
+
__publicField(this, "constants", []);
|
|
7274
|
+
__publicField(this, "x", 0);
|
|
7275
|
+
__publicField(this, "y", 1);
|
|
7276
|
+
__publicField(this, "z", 2);
|
|
6814
7277
|
}
|
|
6815
|
-
|
|
6816
|
-
|
|
7278
|
+
constant(value) {
|
|
7279
|
+
const index2 = this.constants.length;
|
|
7280
|
+
this.constants.push(value);
|
|
7281
|
+
return this.push(Op.Const, 0, 0, index2);
|
|
6817
7282
|
}
|
|
6818
|
-
|
|
6819
|
-
|
|
7283
|
+
neg(a2) {
|
|
7284
|
+
return this.push(Op.Neg, a2);
|
|
6820
7285
|
}
|
|
6821
|
-
|
|
6822
|
-
|
|
7286
|
+
abs(a2) {
|
|
7287
|
+
return this.push(Op.Abs, a2);
|
|
6823
7288
|
}
|
|
6824
|
-
|
|
6825
|
-
|
|
7289
|
+
sqrt(a2) {
|
|
7290
|
+
return this.push(Op.Sqrt, a2);
|
|
7291
|
+
}
|
|
7292
|
+
sin(a2) {
|
|
7293
|
+
return this.push(Op.Sin, a2);
|
|
7294
|
+
}
|
|
7295
|
+
cos(a2) {
|
|
7296
|
+
return this.push(Op.Cos, a2);
|
|
7297
|
+
}
|
|
7298
|
+
round(a2) {
|
|
7299
|
+
return this.push(Op.Round, a2);
|
|
7300
|
+
}
|
|
7301
|
+
add(a2, b) {
|
|
7302
|
+
return this.push(Op.Add, a2, b);
|
|
7303
|
+
}
|
|
7304
|
+
sub(a2, b) {
|
|
7305
|
+
return this.push(Op.Sub, a2, b);
|
|
7306
|
+
}
|
|
7307
|
+
mul(a2, b) {
|
|
7308
|
+
return this.push(Op.Mul, a2, b);
|
|
7309
|
+
}
|
|
7310
|
+
div(a2, b) {
|
|
7311
|
+
return this.push(Op.Div, a2, b);
|
|
7312
|
+
}
|
|
7313
|
+
min(a2, b) {
|
|
7314
|
+
return this.push(Op.Min, a2, b);
|
|
7315
|
+
}
|
|
7316
|
+
max(a2, b) {
|
|
7317
|
+
return this.push(Op.Max, a2, b);
|
|
7318
|
+
}
|
|
7319
|
+
finalize(output) {
|
|
7320
|
+
return {
|
|
7321
|
+
opcodes: Uint8Array.from(this.opcodes),
|
|
7322
|
+
argA: Int32Array.from(this.argA),
|
|
7323
|
+
argB: Int32Array.from(this.argB),
|
|
7324
|
+
argC: Int32Array.from(this.argC),
|
|
7325
|
+
constants: Float64Array.from(this.constants),
|
|
7326
|
+
output,
|
|
7327
|
+
slotCount: this.opcodes.length + 3
|
|
7328
|
+
};
|
|
7329
|
+
}
|
|
7330
|
+
push(op, a2 = 0, b = 0, c2 = 0) {
|
|
7331
|
+
const slot2 = this.opcodes.length + 3;
|
|
7332
|
+
this.opcodes.push(op);
|
|
7333
|
+
this.argA.push(a2);
|
|
7334
|
+
this.argB.push(b);
|
|
7335
|
+
this.argC.push(c2);
|
|
7336
|
+
return slot2;
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
function compileSdfProgramEvaluator3(program) {
|
|
7340
|
+
const lines = ["const { abs, sqrt, sin, cos, round, min, max } = Math;", "let v0 = x;", "let v1 = y;", "let v2 = z;"];
|
|
7341
|
+
const { opcodes, argA, argB, argC, constants, output } = program;
|
|
7342
|
+
for (let i = 0; i < opcodes.length; i++) {
|
|
7343
|
+
const slot2 = `v${i + 3}`;
|
|
7344
|
+
const a2 = `v${argA[i]}`;
|
|
7345
|
+
const b = `v${argB[i]}`;
|
|
7346
|
+
switch (opcodes[i]) {
|
|
7347
|
+
case Op.Const:
|
|
7348
|
+
lines.push(`let ${slot2} = ${numberLiteral(constants[argC[i]])};`);
|
|
7349
|
+
break;
|
|
7350
|
+
case Op.Neg:
|
|
7351
|
+
lines.push(`let ${slot2} = -${a2};`);
|
|
7352
|
+
break;
|
|
7353
|
+
case Op.Abs:
|
|
7354
|
+
lines.push(`let ${slot2} = abs(${a2});`);
|
|
7355
|
+
break;
|
|
7356
|
+
case Op.Sqrt:
|
|
7357
|
+
lines.push(`let ${slot2} = sqrt(${a2});`);
|
|
7358
|
+
break;
|
|
7359
|
+
case Op.Sin:
|
|
7360
|
+
lines.push(`let ${slot2} = sin(${a2});`);
|
|
7361
|
+
break;
|
|
7362
|
+
case Op.Cos:
|
|
7363
|
+
lines.push(`let ${slot2} = cos(${a2});`);
|
|
7364
|
+
break;
|
|
7365
|
+
case Op.Round:
|
|
7366
|
+
lines.push(`let ${slot2} = round(${a2});`);
|
|
7367
|
+
break;
|
|
7368
|
+
case Op.Add:
|
|
7369
|
+
lines.push(`let ${slot2} = ${a2} + ${b};`);
|
|
7370
|
+
break;
|
|
7371
|
+
case Op.Sub:
|
|
7372
|
+
lines.push(`let ${slot2} = ${a2} - ${b};`);
|
|
7373
|
+
break;
|
|
7374
|
+
case Op.Mul:
|
|
7375
|
+
lines.push(`let ${slot2} = ${a2} * ${b};`);
|
|
7376
|
+
break;
|
|
7377
|
+
case Op.Div:
|
|
7378
|
+
lines.push(`let ${slot2} = ${a2} / ${b};`);
|
|
7379
|
+
break;
|
|
7380
|
+
case Op.Min:
|
|
7381
|
+
lines.push(`let ${slot2} = min(${a2}, ${b});`);
|
|
7382
|
+
break;
|
|
7383
|
+
case Op.Max:
|
|
7384
|
+
lines.push(`let ${slot2} = max(${a2}, ${b});`);
|
|
7385
|
+
break;
|
|
7386
|
+
default:
|
|
7387
|
+
throw new Error(`Unknown SdfProgram opcode ${opcodes[i]} at instruction ${i}.`);
|
|
7388
|
+
}
|
|
6826
7389
|
}
|
|
7390
|
+
lines.push(`return v${output};`);
|
|
7391
|
+
return new Function("Math", `return function sdfProgramEval(x, y, z) {
|
|
7392
|
+
${lines.join("\n")}
|
|
7393
|
+
};`)(Math);
|
|
7394
|
+
}
|
|
7395
|
+
function numberLiteral(value) {
|
|
7396
|
+
if (Number.isNaN(value)) return "NaN";
|
|
7397
|
+
if (value === Number.POSITIVE_INFINITY) return "Infinity";
|
|
7398
|
+
if (value === Number.NEGATIVE_INFINITY) return "-Infinity";
|
|
7399
|
+
return String(value);
|
|
7400
|
+
}
|
|
7401
|
+
function clampSlot(b, v, lo, hi) {
|
|
7402
|
+
return b.min(b.max(v, b.constant(lo)), b.constant(hi));
|
|
7403
|
+
}
|
|
7404
|
+
function length2(b, x2, y2) {
|
|
7405
|
+
return b.sqrt(b.add(b.mul(x2, x2), b.mul(y2, y2)));
|
|
7406
|
+
}
|
|
7407
|
+
function length3(b, x2, y2, z2) {
|
|
7408
|
+
return b.sqrt(b.add(b.add(b.mul(x2, x2), b.mul(y2, y2)), b.mul(z2, z2)));
|
|
7409
|
+
}
|
|
7410
|
+
function smin(b, a2, child, k2) {
|
|
7411
|
+
if (k2 <= 0) return b.min(a2, child);
|
|
7412
|
+
const h = b.div(b.max(b.sub(b.constant(k2), b.abs(b.sub(a2, child))), b.constant(0)), b.constant(k2));
|
|
7413
|
+
return b.sub(b.min(a2, child), b.mul(b.mul(b.mul(h, h), h), b.constant(k2 / 6)));
|
|
7414
|
+
}
|
|
7415
|
+
function smax(b, a2, child, k2) {
|
|
7416
|
+
return b.neg(smin(b, b.neg(a2), b.neg(child), k2));
|
|
7417
|
+
}
|
|
7418
|
+
function emitScaledTrig(b, x2, y2, z2, cellSize) {
|
|
7419
|
+
const s = b.constant(TAU / cellSize);
|
|
7420
|
+
const xs = b.mul(x2, s);
|
|
7421
|
+
const ys = b.mul(y2, s);
|
|
7422
|
+
const zs = b.mul(z2, s);
|
|
6827
7423
|
return {
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
7424
|
+
scale: s,
|
|
7425
|
+
sx: b.sin(xs),
|
|
7426
|
+
sy: b.sin(ys),
|
|
7427
|
+
sz: b.sin(zs),
|
|
7428
|
+
cx: b.cos(xs),
|
|
7429
|
+
cy: b.cos(ys),
|
|
7430
|
+
cz: b.cos(zs),
|
|
7431
|
+
sx2: b.sin(b.mul(b.constant(2), xs)),
|
|
7432
|
+
sy2: b.sin(b.mul(b.constant(2), ys)),
|
|
7433
|
+
sz2: b.sin(b.mul(b.constant(2), zs)),
|
|
7434
|
+
cx2: b.cos(b.mul(b.constant(2), xs)),
|
|
7435
|
+
cy2: b.cos(b.mul(b.constant(2), ys)),
|
|
7436
|
+
cz2: b.cos(b.mul(b.constant(2), zs))
|
|
6839
7437
|
};
|
|
6840
7438
|
}
|
|
6841
|
-
function
|
|
6842
|
-
|
|
7439
|
+
function emitGyroidValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7440
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
7441
|
+
return {
|
|
7442
|
+
value: b.add(b.add(b.mul(t.sx, t.cy), b.mul(t.sy, t.cz)), b.mul(t.sz, t.cx)),
|
|
7443
|
+
gx: b.mul(t.scale, b.sub(b.mul(t.cx, t.cy), b.mul(t.sz, t.sx))),
|
|
7444
|
+
gy: b.mul(t.scale, b.add(b.neg(b.mul(t.sx, t.sy)), b.mul(t.cy, t.cz))),
|
|
7445
|
+
gz: b.mul(t.scale, b.add(b.neg(b.mul(t.sy, t.sz)), b.mul(t.cz, t.cx)))
|
|
7446
|
+
};
|
|
6843
7447
|
}
|
|
6844
|
-
function
|
|
6845
|
-
const
|
|
6846
|
-
const estimatedSamples = grid[0] * grid[1] * grid[2];
|
|
7448
|
+
function emitSchwarzPValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7449
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
6847
7450
|
return {
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
estimatedSamples,
|
|
6853
|
-
estimatedMemoryBytes: estimatedSamples * 8,
|
|
6854
|
-
treeRiskFlags: [...settings.treeRiskFlags],
|
|
6855
|
-
simplification: settings.simplify,
|
|
6856
|
-
capMode: "box",
|
|
6857
|
-
capInset: settings.edgeLength,
|
|
6858
|
-
warnings: [...settings.warnings]
|
|
7451
|
+
value: b.add(b.add(t.cx, t.cy), t.cz),
|
|
7452
|
+
gx: b.neg(b.mul(t.scale, t.sx)),
|
|
7453
|
+
gy: b.neg(b.mul(t.scale, t.sy)),
|
|
7454
|
+
gz: b.neg(b.mul(t.scale, t.sz))
|
|
6859
7455
|
};
|
|
6860
7456
|
}
|
|
6861
|
-
function
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
)
|
|
7457
|
+
function emitDiamondValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7458
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
7459
|
+
return {
|
|
7460
|
+
value: b.add(
|
|
7461
|
+
b.add(b.mul(b.mul(t.sx, t.sy), t.sz), b.mul(b.mul(t.sx, t.cy), t.cz)),
|
|
7462
|
+
b.add(b.mul(b.mul(t.cx, t.sy), t.cz), b.mul(b.mul(t.cx, t.cy), t.sz))
|
|
7463
|
+
),
|
|
7464
|
+
gx: b.mul(
|
|
7465
|
+
t.scale,
|
|
7466
|
+
b.add(
|
|
7467
|
+
b.add(b.mul(b.mul(t.cx, t.sy), t.sz), b.mul(b.mul(t.cx, t.cy), t.cz)),
|
|
7468
|
+
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)))
|
|
7469
|
+
)
|
|
7470
|
+
),
|
|
7471
|
+
gy: b.mul(
|
|
7472
|
+
t.scale,
|
|
7473
|
+
b.add(
|
|
7474
|
+
b.add(b.mul(b.mul(t.sx, t.cy), t.sz), b.neg(b.mul(b.mul(t.sx, t.sy), t.cz))),
|
|
7475
|
+
b.add(b.mul(b.mul(t.cx, t.cy), t.cz), b.neg(b.mul(b.mul(t.cx, t.sy), t.sz)))
|
|
7476
|
+
)
|
|
7477
|
+
),
|
|
7478
|
+
gz: b.mul(
|
|
7479
|
+
t.scale,
|
|
7480
|
+
b.add(
|
|
7481
|
+
b.add(b.mul(b.mul(t.sx, t.sy), t.cz), b.neg(b.mul(b.mul(t.sx, t.cy), t.sz))),
|
|
7482
|
+
b.add(b.neg(b.mul(b.mul(t.cx, t.sy), t.sz)), b.mul(b.mul(t.cx, t.cy), t.cz))
|
|
7483
|
+
)
|
|
7484
|
+
)
|
|
7485
|
+
};
|
|
7486
|
+
}
|
|
7487
|
+
function emitLidinoidValueAndGradient(b, x2, y2, z2, cellSize) {
|
|
7488
|
+
const t = emitScaledTrig(b, x2, y2, z2, cellSize);
|
|
7489
|
+
const value = b.add(
|
|
7490
|
+
b.sub(
|
|
7491
|
+
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)),
|
|
7492
|
+
b.add(b.add(b.mul(t.cx2, t.cy2), b.mul(t.cy2, t.cz2)), b.mul(t.cz2, t.cx2))
|
|
7493
|
+
),
|
|
7494
|
+
b.constant(0.3)
|
|
6868
7495
|
);
|
|
7496
|
+
return {
|
|
7497
|
+
value,
|
|
7498
|
+
gx: b.mul(
|
|
7499
|
+
t.scale,
|
|
7500
|
+
b.add(
|
|
7501
|
+
b.add(
|
|
7502
|
+
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)),
|
|
7503
|
+
b.neg(b.mul(b.mul(t.sz2, t.sx), t.sy))
|
|
7504
|
+
),
|
|
7505
|
+
b.add(b.mul(b.mul(b.constant(2), t.sx2), t.cy2), b.mul(b.mul(b.constant(2), t.cz2), t.sx2))
|
|
7506
|
+
)
|
|
7507
|
+
),
|
|
7508
|
+
gy: b.mul(
|
|
7509
|
+
t.scale,
|
|
7510
|
+
b.add(
|
|
7511
|
+
b.add(
|
|
7512
|
+
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))),
|
|
7513
|
+
b.mul(b.mul(t.sz2, t.cx), t.cy)
|
|
7514
|
+
),
|
|
7515
|
+
b.add(b.mul(b.mul(b.constant(2), t.cx2), t.sy2), b.mul(b.mul(b.constant(2), t.sy2), t.cz2))
|
|
7516
|
+
)
|
|
7517
|
+
),
|
|
7518
|
+
gz: b.mul(
|
|
7519
|
+
t.scale,
|
|
7520
|
+
b.add(
|
|
7521
|
+
b.add(
|
|
7522
|
+
b.add(b.mul(b.mul(t.sx2, t.cy), t.cz), b.neg(b.mul(b.mul(t.sy2, t.sz), t.sx))),
|
|
7523
|
+
b.mul(b.mul(b.constant(2), t.cz2), b.mul(t.cx, t.sy))
|
|
7524
|
+
),
|
|
7525
|
+
b.add(b.mul(b.mul(b.constant(2), t.cy2), t.sz2), b.mul(b.mul(b.constant(2), t.sz2), t.cx2))
|
|
7526
|
+
)
|
|
7527
|
+
)
|
|
7528
|
+
};
|
|
6869
7529
|
}
|
|
6870
|
-
function
|
|
6871
|
-
|
|
6872
|
-
const
|
|
6873
|
-
|
|
6874
|
-
return [
|
|
6875
|
-
Math.max(2, Math.ceil(dx / edgeLength2) + 1),
|
|
6876
|
-
Math.max(2, Math.ceil(dy / edgeLength2) + 1),
|
|
6877
|
-
Math.max(2, Math.ceil(dz / edgeLength2) + 1)
|
|
6878
|
-
];
|
|
7530
|
+
function emitTpmsDistance(b, { value, gx, gy, gz }, thickness, thicknessMode) {
|
|
7531
|
+
if (thicknessMode !== "metric-approx") return b.sub(b.abs(value), b.constant(thickness));
|
|
7532
|
+
const grad = length3(b, gx, gy, gz);
|
|
7533
|
+
return b.sub(b.div(b.abs(value), b.max(grad, b.constant(GRAD_EPS))), b.constant(thickness * 0.5));
|
|
6879
7534
|
}
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
7535
|
+
const { cos, max, sin, sqrt } = Math;
|
|
7536
|
+
function emitSdfProgramNode(b, node, x2, y2, z2) {
|
|
7537
|
+
switch (node.kind) {
|
|
7538
|
+
case "sdf:sphere":
|
|
7539
|
+
return sdSphere(b, x2, y2, z2, node.radius);
|
|
7540
|
+
case "sdf:box": {
|
|
7541
|
+
const [hx, hy, hz] = node.halfExtents;
|
|
7542
|
+
return sdBox(b, x2, y2, z2, hx, hy, hz);
|
|
7543
|
+
}
|
|
7544
|
+
case "sdf:cylinder":
|
|
7545
|
+
return sdCylinder(b, x2, y2, z2, node.height, node.radius);
|
|
7546
|
+
case "sdf:torus":
|
|
7547
|
+
return sdTorus(b, x2, y2, z2, node.majorRadius, node.minorRadius);
|
|
7548
|
+
case "sdf:capsule":
|
|
7549
|
+
return sdCapsule(b, x2, y2, z2, node.height, node.radius);
|
|
7550
|
+
case "sdf:cone":
|
|
7551
|
+
return sdCone(b, x2, y2, z2, node.height, node.radius);
|
|
7552
|
+
case "sdf:polylineSweep":
|
|
7553
|
+
return sdPolylineSweep(b, node, x2, y2, z2);
|
|
7554
|
+
case "sdf:union":
|
|
7555
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => b.min(a2, child));
|
|
7556
|
+
case "sdf:difference":
|
|
7557
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => b.max(a2, b.neg(child)));
|
|
7558
|
+
case "sdf:intersection":
|
|
7559
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => b.max(a2, child));
|
|
7560
|
+
case "sdf:smoothUnion":
|
|
7561
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => smin(b, a2, child, node.radius));
|
|
7562
|
+
case "sdf:smoothDifference":
|
|
7563
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => smax(b, a2, b.neg(child), node.radius));
|
|
7564
|
+
case "sdf:smoothIntersection":
|
|
7565
|
+
return foldChildren(b, node.children, x2, y2, z2, (a2, child) => smax(b, a2, child, node.radius));
|
|
7566
|
+
case "sdf:morph": {
|
|
7567
|
+
const a2 = emitSdfProgramNode(b, node.a, x2, y2, z2);
|
|
7568
|
+
const childB = emitSdfProgramNode(b, node.b, x2, y2, z2);
|
|
7569
|
+
return b.add(b.mul(a2, b.constant(1 - node.t)), b.mul(childB, b.constant(node.t)));
|
|
7570
|
+
}
|
|
7571
|
+
case "sdf:translate": {
|
|
7572
|
+
const [ox, oy, oz] = node.offset;
|
|
7573
|
+
return emitSdfProgramNode(b, node.child, b.sub(x2, b.constant(ox)), b.sub(y2, b.constant(oy)), b.sub(z2, b.constant(oz)));
|
|
7574
|
+
}
|
|
7575
|
+
case "sdf:rotate":
|
|
7576
|
+
return emitRotated(b, node, x2, y2, z2);
|
|
7577
|
+
case "sdf:scale": {
|
|
7578
|
+
const inv = 1 / node.factor;
|
|
7579
|
+
const child = emitSdfProgramNode(b, node.child, b.mul(x2, b.constant(inv)), b.mul(y2, b.constant(inv)), b.mul(z2, b.constant(inv)));
|
|
7580
|
+
return b.mul(child, b.constant(node.factor));
|
|
7581
|
+
}
|
|
7582
|
+
case "sdf:twist": {
|
|
7583
|
+
const angle = b.mul(b.constant(node.degreesPerUnit * DEG$1), z2);
|
|
7584
|
+
const c2 = b.cos(angle);
|
|
7585
|
+
const s = b.sin(angle);
|
|
7586
|
+
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);
|
|
7587
|
+
}
|
|
7588
|
+
case "sdf:bend": {
|
|
7589
|
+
const angle = b.div(x2, b.constant(node.radius));
|
|
7590
|
+
const c2 = b.cos(angle);
|
|
7591
|
+
const s = b.sin(angle);
|
|
7592
|
+
const radiusPlusY = b.add(b.constant(node.radius), y2);
|
|
7593
|
+
return emitSdfProgramNode(b, node.child, b.mul(radiusPlusY, s), b.sub(b.mul(radiusPlusY, c2), b.constant(node.radius)), z2);
|
|
7594
|
+
}
|
|
7595
|
+
case "sdf:repeat": {
|
|
7596
|
+
const [sx, sy, sz] = node.spacing;
|
|
7597
|
+
const [cx, cy, cz] = node.count;
|
|
7598
|
+
return emitSdfProgramNode(b, node.child, repeatCoord(b, x2, sx, cx), repeatCoord(b, y2, sy, cy), repeatCoord(b, z2, sz, cz));
|
|
7599
|
+
}
|
|
7600
|
+
case "sdf:shell": {
|
|
7601
|
+
const child = emitSdfProgramNode(b, node.child, x2, y2, z2);
|
|
7602
|
+
return b.sub(b.abs(child), b.constant(node.thickness * 0.5));
|
|
7603
|
+
}
|
|
7604
|
+
case "sdf:onion": {
|
|
7605
|
+
let d2 = emitSdfProgramNode(b, node.child, x2, y2, z2);
|
|
7606
|
+
for (let i = 0; i < node.layers; i++) d2 = b.sub(b.abs(d2), b.constant(node.thickness));
|
|
7607
|
+
return d2;
|
|
7608
|
+
}
|
|
7609
|
+
case "sdf:gyroid":
|
|
7610
|
+
return emitTpmsDistance(b, emitGyroidValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
7611
|
+
case "sdf:schwarzP":
|
|
7612
|
+
return emitTpmsDistance(b, emitSchwarzPValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
7613
|
+
case "sdf:diamond":
|
|
7614
|
+
return emitTpmsDistance(b, emitDiamondValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
7615
|
+
case "sdf:lidinoid":
|
|
7616
|
+
return emitTpmsDistance(b, emitLidinoidValueAndGradient(b, x2, y2, z2, node.cellSize), node.thickness, node.thicknessMode);
|
|
7617
|
+
default:
|
|
7618
|
+
throw new UnsupportedSdfProgramNodeError(`SdfProgram does not support node kind ${node.kind} yet.`);
|
|
7619
|
+
}
|
|
6885
7620
|
}
|
|
6886
|
-
function
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
const maxDim = Math.max(dx, dy, dz, minEdgeLength);
|
|
6891
|
-
const divisor = quality === "draft" ? 60 : quality === "export" ? 160 : 100;
|
|
6892
|
-
const candidates = [maxDim / divisor];
|
|
6893
|
-
if (options.tolerance !== void 0) candidates.push(requirePositiveFinite$2(options.tolerance, "SDF tolerance") * 2);
|
|
6894
|
-
if (options.minFeatureSize !== void 0) candidates.push(requirePositiveFinite$2(options.minFeatureSize, "SDF minFeatureSize") / 2.5);
|
|
6895
|
-
if (analysis.minMetricTpmsThickness < Infinity) candidates.push(analysis.minMetricTpmsThickness / 2);
|
|
6896
|
-
if (analysis.minTpmsCellSize < Infinity) candidates.push(analysis.minTpmsCellSize / 10);
|
|
6897
|
-
if (analysis.minRepeatSpacing < Infinity) candidates.push(analysis.minRepeatSpacing / 8);
|
|
6898
|
-
if (analysis.minWallThickness < Infinity) candidates.push(analysis.minWallThickness / 2.5);
|
|
6899
|
-
return Math.max(minEdgeLength, Math.min(...candidates.filter((v) => Number.isFinite(v) && v > 0)));
|
|
7621
|
+
function foldChildren(b, children, x2, y2, z2, combine2) {
|
|
7622
|
+
let d2 = emitSdfProgramNode(b, children[0], x2, y2, z2);
|
|
7623
|
+
for (let i = 1; i < children.length; i++) d2 = combine2(d2, emitSdfProgramNode(b, children[i], x2, y2, z2));
|
|
7624
|
+
return d2;
|
|
6900
7625
|
}
|
|
6901
|
-
function
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
7626
|
+
function emitRotated(b, node, x2, y2, z2) {
|
|
7627
|
+
const [rx, ry, rz] = node.degrees.map((d2) => d2 * DEG$1);
|
|
7628
|
+
const cx = cos(rx);
|
|
7629
|
+
const sx = sin(rx);
|
|
7630
|
+
const cy = cos(ry);
|
|
7631
|
+
const sy = sin(ry);
|
|
7632
|
+
const cz = cos(rz);
|
|
7633
|
+
const sz = sin(rz);
|
|
7634
|
+
const x1 = b.add(b.mul(b.constant(cz), x2), b.mul(b.constant(sz), y2));
|
|
7635
|
+
const y1 = b.sub(b.mul(b.constant(cz), y2), b.mul(b.constant(sz), x2));
|
|
7636
|
+
const x22 = b.sub(b.mul(b.constant(cy), x1), b.mul(b.constant(sy), z2));
|
|
7637
|
+
const z22 = b.add(b.mul(b.constant(sy), x1), b.mul(b.constant(cy), z2));
|
|
7638
|
+
const y22 = b.add(b.mul(b.constant(cx), y1), b.mul(b.constant(sx), z22));
|
|
7639
|
+
const z3 = b.sub(b.mul(b.constant(cx), z22), b.mul(b.constant(sx), y1));
|
|
7640
|
+
return emitSdfProgramNode(b, node.child, x22, y22, z3);
|
|
7641
|
+
}
|
|
7642
|
+
function sdSphere(b, x2, y2, z2, r) {
|
|
7643
|
+
return b.sub(length3(b, x2, y2, z2), b.constant(r));
|
|
7644
|
+
}
|
|
7645
|
+
function sdBox(b, x2, y2, z2, hx, hy, hz) {
|
|
7646
|
+
const dx = b.sub(b.abs(x2), b.constant(hx));
|
|
7647
|
+
const dy = b.sub(b.abs(y2), b.constant(hy));
|
|
7648
|
+
const dz = b.sub(b.abs(z2), b.constant(hz));
|
|
7649
|
+
const outside = length3(b, b.max(dx, b.constant(0)), b.max(dy, b.constant(0)), b.max(dz, b.constant(0)));
|
|
7650
|
+
const inside = b.min(b.max(b.max(dx, dy), dz), b.constant(0));
|
|
7651
|
+
return b.add(outside, inside);
|
|
7652
|
+
}
|
|
7653
|
+
function sdCylinder(b, x2, y2, z2, h, r) {
|
|
7654
|
+
const dx = b.sub(length2(b, x2, y2), b.constant(r));
|
|
7655
|
+
const dz = b.sub(b.abs(z2), b.constant(h * 0.5));
|
|
7656
|
+
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)));
|
|
7657
|
+
}
|
|
7658
|
+
function sdTorus(b, x2, y2, z2, majorRadius, minorRadius) {
|
|
7659
|
+
return b.sub(length2(b, b.sub(length2(b, x2, y2), b.constant(majorRadius)), z2), b.constant(minorRadius));
|
|
7660
|
+
}
|
|
7661
|
+
function sdCapsule(b, x2, y2, z2, h, r) {
|
|
7662
|
+
const cz = clampSlot(b, z2, -h * 0.5, h * 0.5);
|
|
7663
|
+
return b.sub(length3(b, x2, y2, b.sub(z2, cz)), b.constant(r));
|
|
7664
|
+
}
|
|
7665
|
+
function sdCone(b, x2, y2, z2, h, r) {
|
|
7666
|
+
const q = length2(b, x2, y2);
|
|
7667
|
+
const cLen = sqrt(h * h + r * r);
|
|
7668
|
+
const side = b.add(b.mul(b.constant(h / cLen), q), b.mul(b.constant(-r / cLen), b.sub(z2, b.constant(h))));
|
|
7669
|
+
return b.max(b.max(side, b.neg(z2)), b.sub(z2, b.constant(h)));
|
|
7670
|
+
}
|
|
7671
|
+
function sdPolylineSweep(b, node, x2, y2, z2) {
|
|
7672
|
+
let d2 = sdTaperedSegment(b, x2, y2, z2, node.points[0], node.points[1], node.radii[0], node.radii[1]);
|
|
7673
|
+
for (let i = 1; i < node.points.length - 1; i++) {
|
|
7674
|
+
const segment = sdTaperedSegment(b, x2, y2, z2, node.points[i], node.points[i + 1], node.radii[i], node.radii[i + 1]);
|
|
7675
|
+
d2 = smin(b, d2, segment, node.blend);
|
|
7676
|
+
}
|
|
7677
|
+
return d2;
|
|
6906
7678
|
}
|
|
6907
|
-
function
|
|
6908
|
-
const
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
7679
|
+
function sdTaperedSegment(b, x2, y2, z2, a2, end, ra, rb) {
|
|
7680
|
+
const vx = end[0] - a2[0];
|
|
7681
|
+
const vy = end[1] - a2[1];
|
|
7682
|
+
const vz = end[2] - a2[2];
|
|
7683
|
+
const len2 = vx * vx + vy * vy + vz * vz;
|
|
7684
|
+
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));
|
|
7685
|
+
const h = clampSlot(
|
|
7686
|
+
b,
|
|
7687
|
+
b.div(
|
|
7688
|
+
b.add(
|
|
7689
|
+
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))),
|
|
7690
|
+
b.mul(b.sub(z2, b.constant(a2[2])), b.constant(vz))
|
|
7691
|
+
),
|
|
7692
|
+
b.constant(len2)
|
|
7693
|
+
),
|
|
7694
|
+
0,
|
|
7695
|
+
1
|
|
7696
|
+
);
|
|
7697
|
+
const sx = b.sub(x2, b.add(b.constant(a2[0]), b.mul(b.constant(vx), h)));
|
|
7698
|
+
const sy = b.sub(y2, b.add(b.constant(a2[1]), b.mul(b.constant(vy), h)));
|
|
7699
|
+
const sz = b.sub(z2, b.add(b.constant(a2[2]), b.mul(b.constant(vz), h)));
|
|
7700
|
+
const radius = b.add(b.constant(ra), b.mul(b.constant(rb - ra), h));
|
|
7701
|
+
return b.sub(length3(b, sx, sy, sz), radius);
|
|
6919
7702
|
}
|
|
6920
|
-
function
|
|
7703
|
+
function repeatCoord(b, v, spacing, count) {
|
|
7704
|
+
if (spacing <= 0) return v;
|
|
7705
|
+
if (count > 0) {
|
|
7706
|
+
const center = (count - 1) * 0.5;
|
|
7707
|
+
const index2 = clampSlot(b, b.round(b.add(b.div(v, b.constant(spacing)), b.constant(center))), 0, count - 1);
|
|
7708
|
+
return b.sub(v, b.mul(b.sub(index2, b.constant(center)), b.constant(spacing)));
|
|
7709
|
+
}
|
|
7710
|
+
return b.sub(v, b.mul(b.constant(spacing), b.round(b.div(v, b.constant(spacing)))));
|
|
7711
|
+
}
|
|
7712
|
+
function getUnsupportedSdfProgramReason(node) {
|
|
6921
7713
|
switch (node.kind) {
|
|
7714
|
+
case "sdf:displace":
|
|
7715
|
+
return "displace uses a dynamic JavaScript function body";
|
|
7716
|
+
case "sdf:surfaceDisplace":
|
|
7717
|
+
return "surfaceDisplace uses dynamic UV/pattern evaluation";
|
|
7718
|
+
case "sdf:spatialBlend":
|
|
7719
|
+
return "spatialBlend uses a dynamic JavaScript blend function";
|
|
7720
|
+
case "sdf:noise":
|
|
7721
|
+
return "noise depends on table-based simplex evaluation";
|
|
7722
|
+
case "sdf:voronoi":
|
|
7723
|
+
return "voronoi depends on table-based Worley evaluation";
|
|
7724
|
+
case "sdf:custom":
|
|
7725
|
+
return "custom uses a dynamic JavaScript function body";
|
|
7726
|
+
case "sdf:polylineSweep":
|
|
7727
|
+
if (node.points.length < 2) return "polylineSweep needs at least two points";
|
|
7728
|
+
if (node.points.length !== node.radii.length) return "polylineSweep point/radius counts differ";
|
|
7729
|
+
return void 0;
|
|
6922
7730
|
case "sdf:union":
|
|
6923
7731
|
case "sdf:difference":
|
|
6924
7732
|
case "sdf:intersection":
|
|
6925
7733
|
case "sdf:smoothUnion":
|
|
6926
7734
|
case "sdf:smoothDifference":
|
|
6927
7735
|
case "sdf:smoothIntersection":
|
|
6928
|
-
for (const child of node.children)
|
|
6929
|
-
|
|
7736
|
+
for (const child of node.children) {
|
|
7737
|
+
const reason = getUnsupportedSdfProgramReason(child);
|
|
7738
|
+
if (reason) return reason;
|
|
7739
|
+
}
|
|
7740
|
+
return void 0;
|
|
6930
7741
|
case "sdf:morph":
|
|
6931
|
-
|
|
6932
|
-
visitSdfNode(node.a, analysis);
|
|
6933
|
-
visitSdfNode(node.b, analysis);
|
|
6934
|
-
break;
|
|
7742
|
+
return getUnsupportedSdfProgramReason(node.a) ?? getUnsupportedSdfProgramReason(node.b);
|
|
6935
7743
|
case "sdf:translate":
|
|
6936
7744
|
case "sdf:rotate":
|
|
6937
7745
|
case "sdf:scale":
|
|
6938
7746
|
case "sdf:twist":
|
|
6939
7747
|
case "sdf:bend":
|
|
6940
|
-
case "sdf:onion":
|
|
6941
|
-
visitSdfNode(node.child, analysis);
|
|
6942
|
-
break;
|
|
6943
7748
|
case "sdf:repeat":
|
|
6944
|
-
analysis.riskFlags.add("repeat");
|
|
6945
|
-
for (let i = 0; i < 3; i++) {
|
|
6946
|
-
const spacing = node.spacing[i];
|
|
6947
|
-
if (spacing > 0) {
|
|
6948
|
-
analysis.minRepeatSpacing = Math.min(analysis.minRepeatSpacing, spacing);
|
|
6949
|
-
if (node.count[i] <= 0) analysis.hasInfiniteRepeat = true;
|
|
6950
|
-
}
|
|
6951
|
-
}
|
|
6952
|
-
visitSdfNode(node.child, analysis);
|
|
6953
|
-
break;
|
|
6954
7749
|
case "sdf:shell":
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
case "sdf:surfaceDisplace":
|
|
6960
|
-
analysis.riskFlags.add("displacement");
|
|
6961
|
-
visitSdfNode(node.child, analysis);
|
|
6962
|
-
break;
|
|
6963
|
-
case "sdf:gyroid":
|
|
6964
|
-
case "sdf:schwarzP":
|
|
6965
|
-
case "sdf:diamond":
|
|
6966
|
-
case "sdf:lidinoid":
|
|
6967
|
-
analysis.riskFlags.add("tpms");
|
|
6968
|
-
analysis.minTpmsCellSize = Math.min(analysis.minTpmsCellSize, node.cellSize);
|
|
6969
|
-
if (node.thicknessMode === "metric-approx") {
|
|
6970
|
-
analysis.minMetricTpmsThickness = Math.min(analysis.minMetricTpmsThickness, node.thickness);
|
|
6971
|
-
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.thickness);
|
|
6972
|
-
} else {
|
|
6973
|
-
analysis.hasLegacyTpmsThreshold = true;
|
|
6974
|
-
}
|
|
6975
|
-
break;
|
|
6976
|
-
case "sdf:noise":
|
|
6977
|
-
analysis.riskFlags.add("noise");
|
|
6978
|
-
break;
|
|
6979
|
-
case "sdf:voronoi":
|
|
6980
|
-
analysis.riskFlags.add("voronoi");
|
|
6981
|
-
analysis.minWallThickness = Math.min(analysis.minWallThickness, node.wallThickness);
|
|
6982
|
-
if (node.surfaceChild) visitSdfNode(node.surfaceChild, analysis);
|
|
6983
|
-
break;
|
|
6984
|
-
case "sdf:custom":
|
|
6985
|
-
analysis.riskFlags.add("custom");
|
|
6986
|
-
break;
|
|
7750
|
+
case "sdf:onion":
|
|
7751
|
+
return getUnsupportedSdfProgramReason(node.child);
|
|
7752
|
+
default:
|
|
7753
|
+
return void 0;
|
|
6987
7754
|
}
|
|
6988
7755
|
}
|
|
6989
|
-
function
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
}
|
|
6993
|
-
function requirePositiveFinite$2(value, name) {
|
|
6994
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
6995
|
-
throw new Error(`${name} must be a positive finite number.`);
|
|
7756
|
+
function compileSdfProgram(node) {
|
|
7757
|
+
const unsupportedReason = getUnsupportedSdfProgramReason(node);
|
|
7758
|
+
if (unsupportedReason) {
|
|
7759
|
+
throw new UnsupportedSdfProgramNodeError(`SdfProgram does not support this tree yet: ${unsupportedReason}.`);
|
|
6996
7760
|
}
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
function cloneBounds$2(bounds) {
|
|
7000
|
-
return { min: [...bounds.min], max: [...bounds.max] };
|
|
7001
|
-
}
|
|
7002
|
-
function suggestEdgeLengthForSampleBudget(bounds, maxGridPoints) {
|
|
7003
|
-
const dx = bounds.max[0] - bounds.min[0];
|
|
7004
|
-
const dy = bounds.max[1] - bounds.min[1];
|
|
7005
|
-
const dz = bounds.max[2] - bounds.min[2];
|
|
7006
|
-
const volume = Math.max(dx * dy * dz, 1);
|
|
7007
|
-
return Math.cbrt(volume / Math.max(maxGridPoints, 8));
|
|
7761
|
+
const builder = new SdfProgramBuilder();
|
|
7762
|
+
return builder.finalize(emitSdfProgramNode(builder, node, builder.x, builder.y, builder.z));
|
|
7008
7763
|
}
|
|
7009
|
-
function
|
|
7010
|
-
return
|
|
7011
|
-
}
|
|
7012
|
-
function formatMm(value) {
|
|
7013
|
-
return `${formatNumber$1(value)}mm`;
|
|
7014
|
-
}
|
|
7015
|
-
function formatNumber$1(value) {
|
|
7016
|
-
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7017
|
-
}
|
|
7018
|
-
function formatCount(value) {
|
|
7019
|
-
return Math.round(value).toLocaleString("en-US");
|
|
7764
|
+
function compileSdfProgram3(node) {
|
|
7765
|
+
return compileSdfProgramEvaluator3(compileSdfProgram(node));
|
|
7020
7766
|
}
|
|
7021
|
-
function
|
|
7022
|
-
|
|
7023
|
-
|
|
7767
|
+
function compileSdfMaterializationEvaluator3(node) {
|
|
7768
|
+
const unsupportedReason = getUnsupportedSdfProgramReason(node);
|
|
7769
|
+
if (unsupportedReason) {
|
|
7770
|
+
return {
|
|
7771
|
+
fn: compileSdfNode3(node),
|
|
7772
|
+
engine: "closure",
|
|
7773
|
+
unsupportedReason
|
|
7774
|
+
};
|
|
7775
|
+
}
|
|
7776
|
+
return {
|
|
7777
|
+
fn: compileSdfProgram3(node),
|
|
7778
|
+
engine: "program"
|
|
7779
|
+
};
|
|
7024
7780
|
}
|
|
7025
7781
|
function midpoint$3(a2, b) {
|
|
7026
7782
|
return [(a2[0] + b[0]) / 2, (a2[1] + b[1]) / 2, (a2[2] + b[2]) / 2];
|
|
@@ -7034,7 +7790,7 @@ function scale$6(v, s) {
|
|
|
7034
7790
|
function sub$7(a2, b) {
|
|
7035
7791
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
7036
7792
|
}
|
|
7037
|
-
function cross$
|
|
7793
|
+
function cross$8(a2, b) {
|
|
7038
7794
|
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]];
|
|
7039
7795
|
}
|
|
7040
7796
|
function makeEdge(name, start, end, faceName, curve) {
|
|
@@ -7070,7 +7826,7 @@ function buildSurfaceSheetTopology(boundaries, options = {}) {
|
|
|
7070
7826
|
const center = options.center ?? average$1(corners);
|
|
7071
7827
|
const uAxis = normalizeAxis$1(sub$7(midpoint$3(u1Start, u1End), midpoint$3(u0Start, u0End)));
|
|
7072
7828
|
const vAxis = normalizeAxis$1(sub$7(midpoint$3(v1Start, v1End), midpoint$3(v0Start, v0End)));
|
|
7073
|
-
const normal = normalizeAxis$1(options.normal ?? cross$
|
|
7829
|
+
const normal = normalizeAxis$1(options.normal ?? cross$8(uAxis, vAxis));
|
|
7074
7830
|
const faces = /* @__PURE__ */ new Map();
|
|
7075
7831
|
faces.set(faceName, {
|
|
7076
7832
|
name: faceName,
|
|
@@ -7486,7 +8242,7 @@ function buildCircleExtrusionTopology(circ, height, center = false) {
|
|
|
7486
8242
|
);
|
|
7487
8243
|
return { faces, edges };
|
|
7488
8244
|
}
|
|
7489
|
-
function requireFinite$
|
|
8245
|
+
function requireFinite$9(v, label) {
|
|
7490
8246
|
if (!Number.isFinite(v)) throw new Error(`nurbsSurface: ${label} must be finite, got ${v}`);
|
|
7491
8247
|
}
|
|
7492
8248
|
function normalizeSurfaceTessellation(tessellation) {
|
|
@@ -7496,11 +8252,11 @@ function normalizeSurfaceTessellation(tessellation) {
|
|
|
7496
8252
|
throw new Error(`nurbsSurface: tessellation.mode must be "uniform" or "adaptive", got ${mode}`);
|
|
7497
8253
|
}
|
|
7498
8254
|
if (tessellation.tolerance !== void 0) {
|
|
7499
|
-
requireFinite$
|
|
8255
|
+
requireFinite$9(tessellation.tolerance, "tessellation.tolerance");
|
|
7500
8256
|
if (tessellation.tolerance <= 0) throw new Error("nurbsSurface: tessellation.tolerance must be > 0");
|
|
7501
8257
|
}
|
|
7502
|
-
if (tessellation.minResolution !== void 0) requireFinite$
|
|
7503
|
-
if (tessellation.maxResolution !== void 0) requireFinite$
|
|
8258
|
+
if (tessellation.minResolution !== void 0) requireFinite$9(tessellation.minResolution, "tessellation.minResolution");
|
|
8259
|
+
if (tessellation.maxResolution !== void 0) requireFinite$9(tessellation.maxResolution, "tessellation.maxResolution");
|
|
7504
8260
|
const minResolution = tessellation.minResolution === void 0 ? void 0 : Math.max(2, Math.round(tessellation.minResolution));
|
|
7505
8261
|
const maxResolution = tessellation.maxResolution === void 0 ? void 0 : Math.max(2, Math.round(tessellation.maxResolution));
|
|
7506
8262
|
if (minResolution !== void 0 && maxResolution !== void 0 && minResolution > maxResolution) {
|
|
@@ -7519,10 +8275,10 @@ function normalizeSurfaceDomain(domain) {
|
|
|
7519
8275
|
const uMax = domain.uMax ?? 1;
|
|
7520
8276
|
const vMin = domain.vMin ?? 0;
|
|
7521
8277
|
const vMax = domain.vMax ?? 1;
|
|
7522
|
-
requireFinite$
|
|
7523
|
-
requireFinite$
|
|
7524
|
-
requireFinite$
|
|
7525
|
-
requireFinite$
|
|
8278
|
+
requireFinite$9(uMin, "domain.uMin");
|
|
8279
|
+
requireFinite$9(uMax, "domain.uMax");
|
|
8280
|
+
requireFinite$9(vMin, "domain.vMin");
|
|
8281
|
+
requireFinite$9(vMax, "domain.vMax");
|
|
7526
8282
|
if (uMin < 0 || uMax > 1 || vMin < 0 || vMax > 1) {
|
|
7527
8283
|
throw new Error("nurbsSurface: domain bounds must stay within [0, 1]");
|
|
7528
8284
|
}
|
|
@@ -7534,8 +8290,8 @@ function normalizeSurfaceDomain(domain) {
|
|
|
7534
8290
|
function normalizeTrimLoop(loop, label) {
|
|
7535
8291
|
if (loop.length < 3) throw new Error(`nurbsSurface: ${label} requires at least 3 points`);
|
|
7536
8292
|
const normalized = loop.map(([u2, v], idx) => {
|
|
7537
|
-
requireFinite$
|
|
7538
|
-
requireFinite$
|
|
8293
|
+
requireFinite$9(u2, `${label}[${idx}][0]`);
|
|
8294
|
+
requireFinite$9(v, `${label}[${idx}][1]`);
|
|
7539
8295
|
if (u2 < 0 || u2 > 1 || v < 0 || v > 1) throw new Error(`nurbsSurface: ${label}[${idx}] must stay within [0, 1]`);
|
|
7540
8296
|
return [u2, v];
|
|
7541
8297
|
});
|
|
@@ -7558,8 +8314,8 @@ function normalizeTrimCurve(curve, label) {
|
|
|
7558
8314
|
throw new Error(`nurbsSurface: ${label} needs at least ${degree + 1} control points for degree=${degree}`);
|
|
7559
8315
|
}
|
|
7560
8316
|
const normalizedControlPoints = controlPoints.map(([u2, v], idx) => {
|
|
7561
|
-
requireFinite$
|
|
7562
|
-
requireFinite$
|
|
8317
|
+
requireFinite$9(u2, `${label}.controlPoints[${idx}][0]`);
|
|
8318
|
+
requireFinite$9(v, `${label}.controlPoints[${idx}][1]`);
|
|
7563
8319
|
if (u2 < 0 || u2 > 1 || v < 0 || v > 1) {
|
|
7564
8320
|
throw new Error(`nurbsSurface: ${label}.controlPoints[${idx}] must stay within [0, 1]`);
|
|
7565
8321
|
}
|
|
@@ -7570,7 +8326,7 @@ function normalizeTrimCurve(curve, label) {
|
|
|
7570
8326
|
throw new Error(`nurbsSurface: ${label}.weights length must match controlPoints length`);
|
|
7571
8327
|
}
|
|
7572
8328
|
for (let idx = 0; idx < weights.length; idx += 1) {
|
|
7573
|
-
requireFinite$
|
|
8329
|
+
requireFinite$9(weights[idx], `${label}.weights[${idx}]`);
|
|
7574
8330
|
if (weights[idx] <= 0) throw new Error(`nurbsSurface: ${label}.weights[${idx}] must be > 0`);
|
|
7575
8331
|
}
|
|
7576
8332
|
const knots = curve.knots ?? generateClampedKnots(controlPoints.length, degree);
|
|
@@ -7578,7 +8334,7 @@ function normalizeTrimCurve(curve, label) {
|
|
|
7578
8334
|
throw new Error(`nurbsSurface: ${label}.knots.length should be ${controlPoints.length + degree + 1}, got ${knots.length}`);
|
|
7579
8335
|
}
|
|
7580
8336
|
for (let idx = 0; idx < knots.length; idx += 1) {
|
|
7581
|
-
requireFinite$
|
|
8337
|
+
requireFinite$9(knots[idx], `${label}.knots[${idx}]`);
|
|
7582
8338
|
if (idx > 0 && knots[idx] < knots[idx - 1]) throw new Error(`nurbsSurface: ${label}.knots must be non-decreasing`);
|
|
7583
8339
|
}
|
|
7584
8340
|
if (knots[degree] >= knots[controlPoints.length]) {
|
|
@@ -7758,16 +8514,16 @@ class NurbsSurface {
|
|
|
7758
8514
|
for (let i = 0; i < nU; i++) {
|
|
7759
8515
|
if (controlGrid[i].length !== nV) throw new Error(`nurbsSurface: row ${i} has ${controlGrid[i].length} points, expected ${nV}`);
|
|
7760
8516
|
for (let j = 0; j < nV; j++) {
|
|
7761
|
-
requireFinite$
|
|
7762
|
-
requireFinite$
|
|
7763
|
-
requireFinite$
|
|
8517
|
+
requireFinite$9(controlGrid[i][j][0], `controlGrid[${i}][${j}][0]`);
|
|
8518
|
+
requireFinite$9(controlGrid[i][j][1], `controlGrid[${i}][${j}][1]`);
|
|
8519
|
+
requireFinite$9(controlGrid[i][j][2], `controlGrid[${i}][${j}][2]`);
|
|
7764
8520
|
}
|
|
7765
8521
|
}
|
|
7766
8522
|
const weightsGrid = options.weights ?? controlGrid.map((row) => row.map(() => 1));
|
|
7767
8523
|
for (let i = 0; i < nU; i++) {
|
|
7768
8524
|
if (weightsGrid[i].length !== nV) throw new Error(`nurbsSurface: weights row ${i} length mismatch`);
|
|
7769
8525
|
for (let j = 0; j < nV; j++) {
|
|
7770
|
-
requireFinite$
|
|
8526
|
+
requireFinite$9(weightsGrid[i][j], `weights[${i}][${j}]`);
|
|
7771
8527
|
if (weightsGrid[i][j] <= 0) throw new Error(`nurbsSurface: weights[${i}][${j}] must be > 0`);
|
|
7772
8528
|
}
|
|
7773
8529
|
}
|
|
@@ -8972,6 +9728,7 @@ function buildSweepLevelSetInput(profilePolygons, pathInput, options) {
|
|
|
8972
9728
|
edgeLength: options.edgeLength
|
|
8973
9729
|
};
|
|
8974
9730
|
}
|
|
9731
|
+
const EPS$9 = 1e-9;
|
|
8975
9732
|
function resamplePolygon(poly, targetCount) {
|
|
8976
9733
|
if (poly.length < 2) return poly;
|
|
8977
9734
|
if (targetCount <= 0) return [];
|
|
@@ -9009,6 +9766,78 @@ function resamplePolygon(poly, targetCount) {
|
|
|
9009
9766
|
}
|
|
9010
9767
|
return out;
|
|
9011
9768
|
}
|
|
9769
|
+
function resamplePolygonByAngle(poly, targetCount, center = polygonCentroid$2(poly)) {
|
|
9770
|
+
if (poly.length < 3 || targetCount <= 0) return null;
|
|
9771
|
+
if (!isConvexPolygon(poly)) return null;
|
|
9772
|
+
const out = [];
|
|
9773
|
+
for (let index2 = 0; index2 < targetCount; index2 += 1) {
|
|
9774
|
+
const angle = index2 / targetCount * Math.PI * 2;
|
|
9775
|
+
const point2 = rayPolygonIntersection(center, [Math.cos(angle), Math.sin(angle)], poly);
|
|
9776
|
+
if (!point2) return null;
|
|
9777
|
+
out.push(point2);
|
|
9778
|
+
}
|
|
9779
|
+
return out;
|
|
9780
|
+
}
|
|
9781
|
+
function rayPolygonIntersection(origin, direction2, poly) {
|
|
9782
|
+
let bestT = Infinity;
|
|
9783
|
+
let best = null;
|
|
9784
|
+
for (let index2 = 0; index2 < poly.length; index2 += 1) {
|
|
9785
|
+
const a2 = poly[index2];
|
|
9786
|
+
const b = poly[(index2 + 1) % poly.length];
|
|
9787
|
+
const edge = [b[0] - a2[0], b[1] - a2[1]];
|
|
9788
|
+
const denom = cross$7(direction2, edge);
|
|
9789
|
+
if (Math.abs(denom) < EPS$9) continue;
|
|
9790
|
+
const delta = [a2[0] - origin[0], a2[1] - origin[1]];
|
|
9791
|
+
const rayT = cross$7(delta, edge) / denom;
|
|
9792
|
+
const edgeT = cross$7(delta, direction2) / denom;
|
|
9793
|
+
if (rayT >= -EPS$9 && edgeT >= -EPS$9 && edgeT <= 1 + EPS$9 && rayT < bestT) {
|
|
9794
|
+
bestT = rayT;
|
|
9795
|
+
best = [origin[0] + direction2[0] * rayT, origin[1] + direction2[1] * rayT];
|
|
9796
|
+
}
|
|
9797
|
+
}
|
|
9798
|
+
return best;
|
|
9799
|
+
}
|
|
9800
|
+
function polygonCentroid$2(poly) {
|
|
9801
|
+
let area2 = 0;
|
|
9802
|
+
let cx = 0;
|
|
9803
|
+
let cy = 0;
|
|
9804
|
+
for (let index2 = 0; index2 < poly.length; index2 += 1) {
|
|
9805
|
+
const a2 = poly[index2];
|
|
9806
|
+
const b = poly[(index2 + 1) % poly.length];
|
|
9807
|
+
const crossValue = cross$7(a2, b);
|
|
9808
|
+
area2 += crossValue;
|
|
9809
|
+
cx += (a2[0] + b[0]) * crossValue;
|
|
9810
|
+
cy += (a2[1] + b[1]) * crossValue;
|
|
9811
|
+
}
|
|
9812
|
+
if (Math.abs(area2) < EPS$9) return averagePoint(poly);
|
|
9813
|
+
return [cx / (3 * area2), cy / (3 * area2)];
|
|
9814
|
+
}
|
|
9815
|
+
function averagePoint(poly) {
|
|
9816
|
+
let x2 = 0;
|
|
9817
|
+
let y2 = 0;
|
|
9818
|
+
for (const point2 of poly) {
|
|
9819
|
+
x2 += point2[0];
|
|
9820
|
+
y2 += point2[1];
|
|
9821
|
+
}
|
|
9822
|
+
return [x2 / poly.length, y2 / poly.length];
|
|
9823
|
+
}
|
|
9824
|
+
function isConvexPolygon(poly) {
|
|
9825
|
+
let sign2 = 0;
|
|
9826
|
+
for (let index2 = 0; index2 < poly.length; index2 += 1) {
|
|
9827
|
+
const a2 = poly[index2];
|
|
9828
|
+
const b = poly[(index2 + 1) % poly.length];
|
|
9829
|
+
const c2 = poly[(index2 + 2) % poly.length];
|
|
9830
|
+
const turn = cross$7([b[0] - a2[0], b[1] - a2[1]], [c2[0] - b[0], c2[1] - b[1]]);
|
|
9831
|
+
if (Math.abs(turn) < EPS$9) continue;
|
|
9832
|
+
const currentSign = Math.sign(turn);
|
|
9833
|
+
if (sign2 !== 0 && currentSign !== sign2) return false;
|
|
9834
|
+
sign2 = currentSign;
|
|
9835
|
+
}
|
|
9836
|
+
return sign2 !== 0;
|
|
9837
|
+
}
|
|
9838
|
+
function cross$7(a2, b) {
|
|
9839
|
+
return a2[0] * b[1] - a2[1] * b[0];
|
|
9840
|
+
}
|
|
9012
9841
|
function loftStitched(profiles2, heights, wasm) {
|
|
9013
9842
|
if (profiles2.length < 2) return null;
|
|
9014
9843
|
const classified = profiles2.map((loops) => classifyLoops(loops));
|
|
@@ -9137,8 +9966,10 @@ function stitchSingleLoopLoft(loops, heights, wasm) {
|
|
|
9137
9966
|
maxPoints = Math.max(maxPoints, loop.length);
|
|
9138
9967
|
}
|
|
9139
9968
|
const N = Math.max(maxPoints, 24);
|
|
9969
|
+
const angularSamples = normalizedLoops.map((loop) => resamplePolygonByAngle(loop, N));
|
|
9970
|
+
const useAngularSamples = angularSamples.every((samples) => samples != null);
|
|
9140
9971
|
const resampled = normalizedLoops.map((loop, i) => {
|
|
9141
|
-
const pts2d = resamplePolygon(loop, N);
|
|
9972
|
+
const pts2d = useAngularSamples ? angularSamples[i] : resamplePolygon(loop, N);
|
|
9142
9973
|
const z2 = heights[i];
|
|
9143
9974
|
return pts2d.map(([x2, y2]) => [x2, y2, z2]);
|
|
9144
9975
|
});
|
|
@@ -9157,8 +9988,8 @@ function stitchSingleLoopLoft(loops, heights, wasm) {
|
|
|
9157
9988
|
const v0 = baseIdx + j;
|
|
9158
9989
|
const v1 = nextIdx + j;
|
|
9159
9990
|
const v2 = nextIdx + j1;
|
|
9160
|
-
const
|
|
9161
|
-
triangles.push(v0,
|
|
9991
|
+
const v32 = baseIdx + j1;
|
|
9992
|
+
triangles.push(v0, v32, v2);
|
|
9162
9993
|
triangles.push(v0, v2, v1);
|
|
9163
9994
|
}
|
|
9164
9995
|
}
|
|
@@ -9199,7 +10030,7 @@ let _wasm$1 = null;
|
|
|
9199
10030
|
async function initManifoldWasm() {
|
|
9200
10031
|
if (_wasm$1) return _wasm$1;
|
|
9201
10032
|
performance.mark("manifold:start");
|
|
9202
|
-
const Module = (await import("./manifold-
|
|
10033
|
+
const Module = (await import("./manifold-DpBXFS2K.js")).default;
|
|
9203
10034
|
performance.mark("manifold:imported");
|
|
9204
10035
|
const wasm = await Module();
|
|
9205
10036
|
wasm.setup();
|
|
@@ -9481,8 +10312,8 @@ function stitchLoopAlongPath(loop, _path, frames, wasm) {
|
|
|
9481
10312
|
const v0 = baseIdx + j;
|
|
9482
10313
|
const v1 = nextIdx + j;
|
|
9483
10314
|
const v2 = nextIdx + j1;
|
|
9484
|
-
const
|
|
9485
|
-
triangles.push(v0,
|
|
10315
|
+
const v32 = baseIdx + j1;
|
|
10316
|
+
triangles.push(v0, v32, v2);
|
|
9486
10317
|
triangles.push(v0, v2, v1);
|
|
9487
10318
|
}
|
|
9488
10319
|
}
|
|
@@ -10828,8 +11659,16 @@ function lowerShapeCompilePlanToManifold(plan, wasm) {
|
|
|
10828
11659
|
case "importedMesh":
|
|
10829
11660
|
return lowerImportedMeshToManifold(plan.fileData, plan.format, plan.filePath, wasm);
|
|
10830
11661
|
case "sdf": {
|
|
10831
|
-
const
|
|
10832
|
-
return lowerSdfToManifold(
|
|
11662
|
+
const evaluator = compileSdfMaterializationEvaluator3(plan.tree);
|
|
11663
|
+
return lowerSdfToManifold(
|
|
11664
|
+
evaluator.fn,
|
|
11665
|
+
plan.bounds,
|
|
11666
|
+
plan.edgeLength,
|
|
11667
|
+
wasm,
|
|
11668
|
+
plan.meshing,
|
|
11669
|
+
evaluator.engine,
|
|
11670
|
+
evaluator.unsupportedReason
|
|
11671
|
+
);
|
|
10833
11672
|
}
|
|
10834
11673
|
case "fromSlices":
|
|
10835
11674
|
return lowerFromSlicesToManifold(plan, wasm);
|
|
@@ -10847,8 +11686,12 @@ function lowerShapeCompilePlanToManifold(plan, wasm) {
|
|
|
10847
11686
|
assertExhaustive(plan);
|
|
10848
11687
|
}
|
|
10849
11688
|
}
|
|
10850
|
-
function lowerSdfToManifold(evalFn, bounds, edgeLength2, wasm, meshing) {
|
|
11689
|
+
function lowerSdfToManifold(evalFn, bounds, edgeLength2, wasm, meshing, evaluatorEngine, evaluatorUnsupportedReason) {
|
|
10851
11690
|
const diagnostics = (meshing == null ? void 0 : meshing.diagnostics) ? { ...meshing.diagnostics } : void 0;
|
|
11691
|
+
if (diagnostics && evaluatorEngine) {
|
|
11692
|
+
diagnostics.evaluator = evaluatorEngine;
|
|
11693
|
+
if (evaluatorUnsupportedReason) diagnostics.evaluatorUnsupportedReason = evaluatorUnsupportedReason;
|
|
11694
|
+
}
|
|
10852
11695
|
const inset = edgeLength2;
|
|
10853
11696
|
const cappedEvalFn = (x2, y2, z2) => {
|
|
10854
11697
|
const bx = Math.max(bounds.min[0] + inset - x2, x2 - bounds.max[0] + inset);
|
|
@@ -16294,9 +17137,9 @@ function requireClipper() {
|
|
|
16294
17137
|
if (ClipperLib2.use_xyz) j.OffPt.Z = OffPt.Z;
|
|
16295
17138
|
this.m_Joins.push(j);
|
|
16296
17139
|
};
|
|
16297
|
-
ClipperLib2.Clipper.prototype.AddGhostJoin = function(
|
|
17140
|
+
ClipperLib2.Clipper.prototype.AddGhostJoin = function(Op2, OffPt) {
|
|
16298
17141
|
var j = new ClipperLib2.Join();
|
|
16299
|
-
j.OutPt1 =
|
|
17142
|
+
j.OutPt1 = Op2;
|
|
16300
17143
|
j.OffPt.X = OffPt.X;
|
|
16301
17144
|
j.OffPt.Y = OffPt.Y;
|
|
16302
17145
|
if (ClipperLib2.use_xyz) j.OffPt.Z = OffPt.Z;
|
|
@@ -19198,7 +20041,7 @@ function requireClipper() {
|
|
|
19198
20041
|
}
|
|
19199
20042
|
var clipperExports = requireClipper();
|
|
19200
20043
|
var ClipperLib = /* @__PURE__ */ getDefaultExportFromCjs(clipperExports);
|
|
19201
|
-
let f$
|
|
20044
|
+
let f$3 = class f {
|
|
19202
20045
|
constructor(t, e) {
|
|
19203
20046
|
this.next = null, this.key = t, this.data = e, this.left = null, this.right = null;
|
|
19204
20047
|
}
|
|
@@ -19207,7 +20050,7 @@ function d(n, t) {
|
|
|
19207
20050
|
return n > t ? 1 : n < t ? -1 : 0;
|
|
19208
20051
|
}
|
|
19209
20052
|
function u$1(n, t, e) {
|
|
19210
|
-
const r = new f$
|
|
20053
|
+
const r = new f$3(null, null);
|
|
19211
20054
|
let l = r, i = r;
|
|
19212
20055
|
for (; ; ) {
|
|
19213
20056
|
const o = e(n, t.key);
|
|
@@ -19230,7 +20073,7 @@ function u$1(n, t, e) {
|
|
|
19230
20073
|
return l.right = t.left, i.left = t.right, t.left = r.right, t.right = r.left, t;
|
|
19231
20074
|
}
|
|
19232
20075
|
function c(n, t, e, r) {
|
|
19233
|
-
const l = new f$
|
|
20076
|
+
const l = new f$3(n, t);
|
|
19234
20077
|
if (e === null)
|
|
19235
20078
|
return l.left = l.right = null, l;
|
|
19236
20079
|
e = u$1(n, e, r);
|
|
@@ -19271,7 +20114,7 @@ class z {
|
|
|
19271
20114
|
* Adds a key, if it is not present in the tree
|
|
19272
20115
|
*/
|
|
19273
20116
|
add(t, e) {
|
|
19274
|
-
const r = new f$
|
|
20117
|
+
const r = new f$3(t, e);
|
|
19275
20118
|
this._root === null && (r.left = r.right = null, this._size++, this._root = r);
|
|
19276
20119
|
const l = this._comparator, i = u$1(t, this._root, l), o = l(t, i.key);
|
|
19277
20120
|
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;
|
|
@@ -19484,23 +20327,23 @@ class z {
|
|
|
19484
20327
|
function a(n, t, e, r) {
|
|
19485
20328
|
const l = r - e;
|
|
19486
20329
|
if (l > 0) {
|
|
19487
|
-
const i = e + Math.floor(l / 2), o = n[i], s = t[i], h = new f$
|
|
20330
|
+
const i = e + Math.floor(l / 2), o = n[i], s = t[i], h = new f$3(o, s);
|
|
19488
20331
|
return h.left = a(n, t, e, i), h.right = a(n, t, i + 1, r), h;
|
|
19489
20332
|
}
|
|
19490
20333
|
return null;
|
|
19491
20334
|
}
|
|
19492
20335
|
function x(n, t) {
|
|
19493
|
-
const e = new f$
|
|
20336
|
+
const e = new f$3(null, null);
|
|
19494
20337
|
let r = e;
|
|
19495
20338
|
for (let l = 0; l < n.length; l++)
|
|
19496
|
-
r = r.next = new f$
|
|
20339
|
+
r = r.next = new f$3(n[l], t[l]);
|
|
19497
20340
|
return r.next = null, e.next;
|
|
19498
20341
|
}
|
|
19499
20342
|
function k(n) {
|
|
19500
20343
|
let t = n;
|
|
19501
20344
|
const e = [];
|
|
19502
20345
|
let r = false;
|
|
19503
|
-
const l = new f$
|
|
20346
|
+
const l = new f$3(null, null);
|
|
19504
20347
|
let i = l;
|
|
19505
20348
|
for (; !r; )
|
|
19506
20349
|
t ? (e.push(t), t = t.left) : e.length > 0 ? (t = i = i.next = e.pop(), t = t.right) : r = true;
|
|
@@ -19515,7 +20358,7 @@ function p(n, t, e) {
|
|
|
19515
20358
|
return null;
|
|
19516
20359
|
}
|
|
19517
20360
|
function y(n, t, e) {
|
|
19518
|
-
const r = new f$
|
|
20361
|
+
const r = new f$3(null, null);
|
|
19519
20362
|
let l = r, i = n, o = t;
|
|
19520
20363
|
for (; i !== null && o !== null; )
|
|
19521
20364
|
e(i.key, o.key) < 0 ? (l.next = i, i = i.next) : (l.next = o, o = o.next), l = l.next;
|
|
@@ -24500,7 +25343,13 @@ function normalizeTruckShapeForBooleanInput(shape) {
|
|
|
24500
25343
|
return normalized;
|
|
24501
25344
|
}
|
|
24502
25345
|
function lowerSdfPlan(plan) {
|
|
24503
|
-
|
|
25346
|
+
var _a3, _b3, _c2;
|
|
25347
|
+
const evaluator = compileSdfMaterializationEvaluator3(plan.tree);
|
|
25348
|
+
if ((_a3 = plan.meshing) == null ? void 0 : _a3.diagnostics) {
|
|
25349
|
+
plan.meshing.diagnostics.evaluator = evaluator.engine;
|
|
25350
|
+
if (evaluator.unsupportedReason) plan.meshing.diagnostics.evaluatorUnsupportedReason = evaluator.unsupportedReason;
|
|
25351
|
+
}
|
|
25352
|
+
const evalFn = evaluator.fn;
|
|
24504
25353
|
const inset = plan.edgeLength;
|
|
24505
25354
|
const cappedEvalFn = (x2, y2, z2) => {
|
|
24506
25355
|
const bx = Math.max(plan.bounds.min[0] + inset - x2, x2 - plan.bounds.max[0] + inset);
|
|
@@ -24512,14 +25361,18 @@ function lowerSdfPlan(plan) {
|
|
|
24512
25361
|
assertSdfMeshBudget(mesh, plan);
|
|
24513
25362
|
let surfaceNetsError;
|
|
24514
25363
|
try {
|
|
24515
|
-
|
|
25364
|
+
const shape = lowerExtractedSdfMesh(mesh, cappedEvalFn, true);
|
|
25365
|
+
if ((_b3 = plan.meshing) == null ? void 0 : _b3.diagnostics) logSdfMeshingDiagnostics("SDF meshing result", plan.meshing.diagnostics);
|
|
25366
|
+
return shape;
|
|
24516
25367
|
} catch (error) {
|
|
24517
25368
|
surfaceNetsError = error;
|
|
24518
25369
|
}
|
|
24519
25370
|
const tetraMesh = marchingTetrahedra(cappedEvalFn, plan.bounds, plan.edgeLength);
|
|
24520
25371
|
assertSdfMeshBudget(tetraMesh, plan);
|
|
24521
25372
|
try {
|
|
24522
|
-
|
|
25373
|
+
const shape = lowerExtractedSdfMesh(tetraMesh, cappedEvalFn, false);
|
|
25374
|
+
if ((_c2 = plan.meshing) == null ? void 0 : _c2.diagnostics) logSdfMeshingDiagnostics("SDF meshing result", plan.meshing.diagnostics);
|
|
25375
|
+
return shape;
|
|
24523
25376
|
} catch (error) {
|
|
24524
25377
|
throw new Error(
|
|
24525
25378
|
`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)}`
|
|
@@ -24883,7 +25736,9 @@ function lowerOffsetSolidPlan(plan) {
|
|
|
24883
25736
|
if (base.kind === "transform") {
|
|
24884
25737
|
return lowerTransformedOffsetSolidPlan(base, plan.thickness);
|
|
24885
25738
|
}
|
|
24886
|
-
return truckUnsupported(
|
|
25739
|
+
return truckUnsupported(
|
|
25740
|
+
`compile plan "${plan.kind}" for non-vertical-prism/non-revolved/non-loft/non-straight-sweep/non-straight-variable-sweep solids`
|
|
25741
|
+
);
|
|
24887
25742
|
}
|
|
24888
25743
|
function lowerLoftPlan(plan) {
|
|
24889
25744
|
return wrapTruckShapeBackend(
|
|
@@ -32185,6 +33040,37 @@ function mergeSketchPlacementModel(sketches) {
|
|
|
32185
33040
|
}
|
|
32186
33041
|
return first;
|
|
32187
33042
|
}
|
|
33043
|
+
function normalizeSceneTags(value, label = "tags") {
|
|
33044
|
+
if (value == null) return [];
|
|
33045
|
+
const rawTags = typeof value === "string" ? [value] : value;
|
|
33046
|
+
if (!Array.isArray(rawTags)) {
|
|
33047
|
+
throw new Error(`${label} must be a string or array of strings`);
|
|
33048
|
+
}
|
|
33049
|
+
const out = [];
|
|
33050
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33051
|
+
rawTags.forEach((tag, index2) => {
|
|
33052
|
+
if (typeof tag !== "string") {
|
|
33053
|
+
throw new Error(`${label}[${index2}] must be a string`);
|
|
33054
|
+
}
|
|
33055
|
+
const trimmed = tag.trim();
|
|
33056
|
+
if (!trimmed || seen.has(trimmed)) return;
|
|
33057
|
+
seen.add(trimmed);
|
|
33058
|
+
out.push(trimmed);
|
|
33059
|
+
});
|
|
33060
|
+
return out;
|
|
33061
|
+
}
|
|
33062
|
+
function mergeSceneTags(...values) {
|
|
33063
|
+
const out = [];
|
|
33064
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33065
|
+
values.forEach((value) => {
|
|
33066
|
+
normalizeSceneTags(value).forEach((tag) => {
|
|
33067
|
+
if (seen.has(tag)) return;
|
|
33068
|
+
seen.add(tag);
|
|
33069
|
+
out.push(tag);
|
|
33070
|
+
});
|
|
33071
|
+
});
|
|
33072
|
+
return out;
|
|
33073
|
+
}
|
|
32188
33074
|
const _groupPlacementRefs = /* @__PURE__ */ new WeakMap();
|
|
32189
33075
|
const _groupExplodeHint = /* @__PURE__ */ new WeakMap();
|
|
32190
33076
|
function getGroupRefs(g2) {
|
|
@@ -32208,7 +33094,7 @@ function transformGroupRefs(source, dest, matrix) {
|
|
|
32208
33094
|
}
|
|
32209
33095
|
return dest;
|
|
32210
33096
|
}
|
|
32211
|
-
function requireFiniteAngle(v, method) {
|
|
33097
|
+
function requireFiniteAngle$1(v, method) {
|
|
32212
33098
|
if (typeof v !== "number" || !Number.isFinite(v))
|
|
32213
33099
|
throw new Error(`${method} angleDeg must be a finite number, got ${typeof v === "number" ? v : typeof v}`);
|
|
32214
33100
|
}
|
|
@@ -32269,31 +33155,46 @@ function resolveNamedGroupChild(item) {
|
|
|
32269
33155
|
function normalizeGroupInputs(items) {
|
|
32270
33156
|
const children = [];
|
|
32271
33157
|
const childNames = [];
|
|
33158
|
+
const childTags = [];
|
|
32272
33159
|
items.forEach((item) => {
|
|
32273
33160
|
if (isNamedGroupChild(item)) {
|
|
32274
33161
|
children.push(resolveNamedGroupChild(item));
|
|
32275
33162
|
childNames.push(normalizeChildName(item.name));
|
|
33163
|
+
childTags.push(normalizeSceneTags(item.tags, `group(...) named item "${item.name}" tags`));
|
|
32276
33164
|
return;
|
|
32277
33165
|
}
|
|
32278
33166
|
children.push(item);
|
|
32279
33167
|
childNames.push(void 0);
|
|
33168
|
+
childTags.push([]);
|
|
32280
33169
|
});
|
|
32281
|
-
return { children, childNames };
|
|
33170
|
+
return { children, childNames, childTags };
|
|
32282
33171
|
}
|
|
32283
33172
|
class ShapeGroup {
|
|
32284
|
-
constructor(children, childNames) {
|
|
33173
|
+
constructor(children, childNames, childTags) {
|
|
32285
33174
|
__publicField(this, "children");
|
|
32286
33175
|
__publicField(this, "childNames");
|
|
33176
|
+
__publicField(this, "childTags");
|
|
32287
33177
|
if (childNames && childNames.length !== children.length) {
|
|
32288
33178
|
throw new Error("ShapeGroup childNames must match children length");
|
|
32289
33179
|
}
|
|
33180
|
+
if (childTags && childTags.length !== children.length) {
|
|
33181
|
+
throw new Error("ShapeGroup childTags must match children length");
|
|
33182
|
+
}
|
|
32290
33183
|
this.children = [...children];
|
|
32291
33184
|
this.childNames = this.children.map((_2, index2) => normalizeChildName(childNames == null ? void 0 : childNames[index2]));
|
|
33185
|
+
this.childTags = this.children.map((_2, index2) => normalizeSceneTags(childTags == null ? void 0 : childTags[index2], "ShapeGroup childTags"));
|
|
32292
33186
|
}
|
|
32293
33187
|
/** Return the optional name of the child at `index`. */
|
|
32294
33188
|
childName(index2) {
|
|
32295
33189
|
return this.childNames[index2];
|
|
32296
33190
|
}
|
|
33191
|
+
/**
|
|
33192
|
+
* Return tags attached to the child at `index`.
|
|
33193
|
+
* @internal
|
|
33194
|
+
*/
|
|
33195
|
+
tagsForChild(index2) {
|
|
33196
|
+
return [...this.childTags[index2] ?? []];
|
|
33197
|
+
}
|
|
32297
33198
|
/**
|
|
32298
33199
|
* Return the named child by name. Throws if not found.
|
|
32299
33200
|
* Useful when importing a multipart group and working on components individually.
|
|
@@ -32308,13 +33209,13 @@ class ShapeGroup {
|
|
|
32308
33209
|
}
|
|
32309
33210
|
/** Apply fn to all children, producing a new ShapeGroup that also copies placement refs. */
|
|
32310
33211
|
mapChildren(fn) {
|
|
32311
|
-
const next = new ShapeGroup(this.children.map(fn), this.childNames);
|
|
33212
|
+
const next = new ShapeGroup(this.children.map(fn), this.childNames, this.childTags);
|
|
32312
33213
|
copyGroupPorts(this, next);
|
|
32313
33214
|
return copyGroupRefs(this, next);
|
|
32314
33215
|
}
|
|
32315
33216
|
/** Apply fn to all children and also transform placement refs by the given matrix. */
|
|
32316
33217
|
mapChildrenTransform(fn, matrix) {
|
|
32317
|
-
const next = new ShapeGroup(this.children.map(fn), this.childNames);
|
|
33218
|
+
const next = new ShapeGroup(this.children.map(fn), this.childNames, this.childTags);
|
|
32318
33219
|
transformGroupPortsHelper(this, next, matrix);
|
|
32319
33220
|
return transformGroupRefs(this, next, matrix);
|
|
32320
33221
|
}
|
|
@@ -32441,25 +33342,25 @@ class ShapeGroup {
|
|
|
32441
33342
|
/** Rotate the group around an arbitrary axis through the origin. */
|
|
32442
33343
|
rotate(axis, angleDeg, options) {
|
|
32443
33344
|
requireRotateAxis(axis, "ShapeGroup.rotate()");
|
|
32444
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotate()");
|
|
33345
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotate()");
|
|
32445
33346
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotate()");
|
|
32446
33347
|
return this.rotateAroundAxis(axis, angleDeg, options == null ? void 0 : options.pivot);
|
|
32447
33348
|
}
|
|
32448
33349
|
/** Rotate the group around the X axis. */
|
|
32449
33350
|
rotateX(angleDeg, options) {
|
|
32450
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotateX()");
|
|
33351
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotateX()");
|
|
32451
33352
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotateX()");
|
|
32452
33353
|
return this.rotateAroundAxis([1, 0, 0], angleDeg, options == null ? void 0 : options.pivot);
|
|
32453
33354
|
}
|
|
32454
33355
|
/** Rotate the group around the Y axis. */
|
|
32455
33356
|
rotateY(angleDeg, options) {
|
|
32456
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotateY()");
|
|
33357
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotateY()");
|
|
32457
33358
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotateY()");
|
|
32458
33359
|
return this.rotateAroundAxis([0, 1, 0], angleDeg, options == null ? void 0 : options.pivot);
|
|
32459
33360
|
}
|
|
32460
33361
|
/** Rotate the group around the Z axis. */
|
|
32461
33362
|
rotateZ(angleDeg, options) {
|
|
32462
|
-
requireFiniteAngle(angleDeg, "ShapeGroup.rotateZ()");
|
|
33363
|
+
requireFiniteAngle$1(angleDeg, "ShapeGroup.rotateZ()");
|
|
32463
33364
|
if (options == null ? void 0 : options.pivot) requireVec3Pivot(options.pivot, "ShapeGroup.rotateZ()");
|
|
32464
33365
|
return this.rotateAroundAxis([0, 0, 1], angleDeg, options == null ? void 0 : options.pivot);
|
|
32465
33366
|
}
|
|
@@ -32502,7 +33403,8 @@ class ShapeGroup {
|
|
|
32502
33403
|
"ShapeGroup.transform only supports 3D children (Shape/ShapeGroup). For Sketch children, use 2D transforms (translate/rotate/scale/mirror)."
|
|
32503
33404
|
);
|
|
32504
33405
|
}),
|
|
32505
|
-
this.childNames
|
|
33406
|
+
this.childNames,
|
|
33407
|
+
this.childTags
|
|
32506
33408
|
);
|
|
32507
33409
|
transformGroupPortsHelper(this, next, matrix);
|
|
32508
33410
|
return transformGroupRefs(this, next, matrix);
|
|
@@ -32570,7 +33472,7 @@ class ShapeGroup {
|
|
|
32570
33472
|
* ```
|
|
32571
33473
|
*/
|
|
32572
33474
|
withReferences(refs) {
|
|
32573
|
-
const next = new ShapeGroup(this.children, this.childNames);
|
|
33475
|
+
const next = new ShapeGroup(this.children, this.childNames, this.childTags);
|
|
32574
33476
|
const merged = applyPlacementReferenceInput(getGroupRefs(this), refs);
|
|
32575
33477
|
return setGroupRefs(next, merged);
|
|
32576
33478
|
}
|
|
@@ -32638,7 +33540,7 @@ class ShapeGroup {
|
|
|
32638
33540
|
/** Attach named connectors — attachment points that survive transforms.
|
|
32639
33541
|
* Connectors can be bare (position + orientation) or typed (with connectorType/gender for compatibility matching). */
|
|
32640
33542
|
withConnectors(connectors) {
|
|
32641
|
-
const next = new ShapeGroup(this.children, this.childNames);
|
|
33543
|
+
const next = new ShapeGroup(this.children, this.childNames, this.childTags);
|
|
32642
33544
|
copyGroupRefs(this, next);
|
|
32643
33545
|
const existing = getGroupPorts(this);
|
|
32644
33546
|
const incoming = normalizeConnectorMapInput(connectors);
|
|
@@ -32688,7 +33590,7 @@ class ShapeGroup {
|
|
|
32688
33590
|
}
|
|
32689
33591
|
function group(...items) {
|
|
32690
33592
|
const normalized = normalizeGroupInputs(items);
|
|
32691
|
-
return new ShapeGroup(normalized.children, normalized.childNames);
|
|
33593
|
+
return new ShapeGroup(normalized.children, normalized.childNames, normalized.childTags);
|
|
32692
33594
|
}
|
|
32693
33595
|
function getTargetPortsForGroup(target) {
|
|
32694
33596
|
if (target instanceof Shape) {
|
|
@@ -34859,7 +35761,7 @@ function buildSdfFunctionDefinition(source, options) {
|
|
|
34859
35761
|
jsExpression: expression,
|
|
34860
35762
|
...shader.ok ? { shaderExpression: shader.expression } : { shaderUnsupportedReason: shader.reason },
|
|
34861
35763
|
raymarchStepLimit: resolveRaymarchStepLimit(options.bounds, options.maxStep),
|
|
34862
|
-
...options.lipschitz !== void 0 ? { raymarchLipschitz: requirePositiveFinite$
|
|
35764
|
+
...options.lipschitz !== void 0 ? { raymarchLipschitz: requirePositiveFinite$2(options.lipschitz, "sdf.fromFunction() lipschitz") } : {}
|
|
34863
35765
|
};
|
|
34864
35766
|
}
|
|
34865
35767
|
function extractSdfExpression(source) {
|
|
@@ -35027,7 +35929,7 @@ function formatNumericLiteralsForGlsl(source) {
|
|
|
35027
35929
|
return result;
|
|
35028
35930
|
}
|
|
35029
35931
|
function resolveRaymarchStepLimit(bounds, maxStep) {
|
|
35030
|
-
if (maxStep !== void 0) return requirePositiveFinite$
|
|
35932
|
+
if (maxStep !== void 0) return requirePositiveFinite$2(maxStep, "sdf.fromFunction() maxStep");
|
|
35031
35933
|
const dx = bounds.max[0] - bounds.min[0];
|
|
35032
35934
|
const dy = bounds.max[1] - bounds.min[1];
|
|
35033
35935
|
const dz = bounds.max[2] - bounds.min[2];
|
|
@@ -35035,7 +35937,7 @@ function resolveRaymarchStepLimit(bounds, maxStep) {
|
|
|
35035
35937
|
if (!Number.isFinite(diagonal) || diagonal <= 0) return 0.1;
|
|
35036
35938
|
return Math.max(0.025, Math.min(0.5, diagonal / 240));
|
|
35037
35939
|
}
|
|
35038
|
-
function requirePositiveFinite$
|
|
35940
|
+
function requirePositiveFinite$2(value, label) {
|
|
35039
35941
|
if (!Number.isFinite(value) || value <= 0) throw new Error(`${label} must be a positive finite number.`);
|
|
35040
35942
|
return value;
|
|
35041
35943
|
}
|
|
@@ -35062,6 +35964,199 @@ class SurfacePattern {
|
|
|
35062
35964
|
this.constants = constants;
|
|
35063
35965
|
}
|
|
35064
35966
|
}
|
|
35967
|
+
const typedSurfacePatterns = /* @__PURE__ */ new WeakMap();
|
|
35968
|
+
function getTypedSurfacePattern(pattern) {
|
|
35969
|
+
return typedSurfacePatterns.get(pattern);
|
|
35970
|
+
}
|
|
35971
|
+
class Pattern2D extends SurfacePattern {
|
|
35972
|
+
constructor(body) {
|
|
35973
|
+
super(body);
|
|
35974
|
+
}
|
|
35975
|
+
/** Add this pattern to one or more patterns or constant height offsets. */
|
|
35976
|
+
add(...patterns) {
|
|
35977
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
35978
|
+
}
|
|
35979
|
+
/** Subtract another pattern or constant height offset from this pattern. */
|
|
35980
|
+
subtract(pattern) {
|
|
35981
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
35982
|
+
}
|
|
35983
|
+
/** Multiply this pattern by one or more patterns or numeric scale factors. */
|
|
35984
|
+
multiply(...patterns) {
|
|
35985
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
35986
|
+
}
|
|
35987
|
+
/** Keep the lower height between this pattern and one or more other patterns. */
|
|
35988
|
+
min(...patterns) {
|
|
35989
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
35990
|
+
}
|
|
35991
|
+
/** Keep the higher height between this pattern and one or more other patterns. */
|
|
35992
|
+
max(...patterns) {
|
|
35993
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
35994
|
+
}
|
|
35995
|
+
/** Limit pattern height to the inclusive `[min, max]` range in millimeters. */
|
|
35996
|
+
clamp(min2, max2) {
|
|
35997
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
35998
|
+
}
|
|
35999
|
+
/** Convert negative heights to positive heights. */
|
|
36000
|
+
abs() {
|
|
36001
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36002
|
+
}
|
|
36003
|
+
/** Flip the pattern height sign. */
|
|
36004
|
+
negate() {
|
|
36005
|
+
throw new Error("Pattern2D values are created by sdf.pattern2d().");
|
|
36006
|
+
}
|
|
36007
|
+
}
|
|
36008
|
+
class Pattern2DImpl extends Pattern2D {
|
|
36009
|
+
constructor(node) {
|
|
36010
|
+
super(emitSurfacePatternJsExpression(node));
|
|
36011
|
+
__publicField(this, "node");
|
|
36012
|
+
this.node = node;
|
|
36013
|
+
typedSurfacePatterns.set(this, node);
|
|
36014
|
+
}
|
|
36015
|
+
add(...patterns) {
|
|
36016
|
+
return new Pattern2DImpl({ kind: "surfacePattern:add", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36017
|
+
}
|
|
36018
|
+
subtract(pattern) {
|
|
36019
|
+
return this.add(new Pattern2DImpl({ kind: "surfacePattern:negate", child: patternNodeFromInput(pattern) }));
|
|
36020
|
+
}
|
|
36021
|
+
multiply(...patterns) {
|
|
36022
|
+
return new Pattern2DImpl({ kind: "surfacePattern:multiply", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36023
|
+
}
|
|
36024
|
+
min(...patterns) {
|
|
36025
|
+
return new Pattern2DImpl({ kind: "surfacePattern:min", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36026
|
+
}
|
|
36027
|
+
max(...patterns) {
|
|
36028
|
+
return new Pattern2DImpl({ kind: "surfacePattern:max", children: [this.node, ...patterns.map(patternNodeFromInput)] });
|
|
36029
|
+
}
|
|
36030
|
+
clamp(min2, max2) {
|
|
36031
|
+
const lo = requireFinite$8(min2, "Pattern2D.clamp() min");
|
|
36032
|
+
const hi = requireFinite$8(max2, "Pattern2D.clamp() max");
|
|
36033
|
+
if (lo > hi) throw new Error(`Pattern2D.clamp() min must be <= max. Received: ${lo} > ${hi}`);
|
|
36034
|
+
return new Pattern2DImpl({ kind: "surfacePattern:clamp", child: this.node, min: lo, max: hi });
|
|
36035
|
+
}
|
|
36036
|
+
abs() {
|
|
36037
|
+
return new Pattern2DImpl({ kind: "surfacePattern:abs", child: this.node });
|
|
36038
|
+
}
|
|
36039
|
+
negate() {
|
|
36040
|
+
return new Pattern2DImpl({ kind: "surfacePattern:negate", child: this.node });
|
|
36041
|
+
}
|
|
36042
|
+
}
|
|
36043
|
+
class Pattern2DBuilder {
|
|
36044
|
+
/** Create a constant-height pattern in millimeters. */
|
|
36045
|
+
constant(value = 0) {
|
|
36046
|
+
return new Pattern2DImpl({ kind: "surfacePattern:constant", value: requireFinite$8(value, "sdf.pattern2d().constant() value") });
|
|
36047
|
+
}
|
|
36048
|
+
/** Create a sinusoidal wave pattern in UV space. */
|
|
36049
|
+
sineWave(options) {
|
|
36050
|
+
return new Pattern2DImpl({
|
|
36051
|
+
kind: "surfacePattern:sineWave",
|
|
36052
|
+
direction: normalizeDirection$1(options.direction ?? [1, 0], "sdf.pattern2d().sineWave() direction"),
|
|
36053
|
+
wavelength: requirePositiveFinite$1(options.wavelength, "sdf.pattern2d().sineWave() wavelength"),
|
|
36054
|
+
amplitude: requireFinite$8(options.amplitude ?? 1, "sdf.pattern2d().sineWave() amplitude"),
|
|
36055
|
+
phase: requireFinite$8(options.phase ?? 0, "sdf.pattern2d().sineWave() phase"),
|
|
36056
|
+
bias: requireFinite$8(options.bias ?? 0, "sdf.pattern2d().sineWave() bias")
|
|
36057
|
+
});
|
|
36058
|
+
}
|
|
36059
|
+
/** Create recessed stripe bands in UV space. */
|
|
36060
|
+
stripes(options) {
|
|
36061
|
+
return new Pattern2DImpl({
|
|
36062
|
+
kind: "surfacePattern:stripes",
|
|
36063
|
+
direction: normalizeDirection$1(options.direction ?? [1, 0], "sdf.pattern2d().stripes() direction"),
|
|
36064
|
+
spacing: requirePositiveFinite$1(options.spacing, "sdf.pattern2d().stripes() spacing"),
|
|
36065
|
+
width: requirePositiveFinite$1(options.width, "sdf.pattern2d().stripes() width"),
|
|
36066
|
+
depth: requireNonNegativeFinite$1(options.depth ?? 1, "sdf.pattern2d().stripes() depth")
|
|
36067
|
+
});
|
|
36068
|
+
}
|
|
36069
|
+
/** Create an over-under woven relief pattern in UV space. */
|
|
36070
|
+
overUnderWeave(options) {
|
|
36071
|
+
return new Pattern2DImpl({
|
|
36072
|
+
kind: "surfacePattern:overUnderWeave",
|
|
36073
|
+
spacing: normalizeVec2(options.spacing, "sdf.pattern2d().overUnderWeave() spacing", requirePositiveFinite$1),
|
|
36074
|
+
threadWidth: normalizeVec2(options.threadWidth, "sdf.pattern2d().overUnderWeave() threadWidth", requirePositiveFinite$1),
|
|
36075
|
+
depth: requireNonNegativeFinite$1(options.depth ?? 0.8, "sdf.pattern2d().overUnderWeave() depth"),
|
|
36076
|
+
underScale: requireNonNegativeFinite$1(options.underScale ?? 0.15, "sdf.pattern2d().overUnderWeave() underScale")
|
|
36077
|
+
});
|
|
36078
|
+
}
|
|
36079
|
+
}
|
|
36080
|
+
function pattern2d() {
|
|
36081
|
+
return new Pattern2DBuilder();
|
|
36082
|
+
}
|
|
36083
|
+
function patternNodeFromInput(input) {
|
|
36084
|
+
if (input instanceof SurfacePattern) {
|
|
36085
|
+
const node = getTypedSurfacePattern(input);
|
|
36086
|
+
if (node) return node;
|
|
36087
|
+
}
|
|
36088
|
+
if (typeof input === "number") {
|
|
36089
|
+
return { kind: "surfacePattern:constant", value: requireFinite$8(input, "Pattern2D numeric input") };
|
|
36090
|
+
}
|
|
36091
|
+
throw new Error("Pattern2D composition expects another typed Pattern2D or a number.");
|
|
36092
|
+
}
|
|
36093
|
+
function requireFinite$8(value, label) {
|
|
36094
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
36095
|
+
throw new Error(`${label} must be a finite number. Received: ${String(value)}`);
|
|
36096
|
+
}
|
|
36097
|
+
return value;
|
|
36098
|
+
}
|
|
36099
|
+
function requirePositiveFinite$1(value, label) {
|
|
36100
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
36101
|
+
throw new Error(`${label} must be a positive finite number. Received: ${String(value)}`);
|
|
36102
|
+
}
|
|
36103
|
+
return value;
|
|
36104
|
+
}
|
|
36105
|
+
function requireNonNegativeFinite$1(value, label) {
|
|
36106
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
36107
|
+
throw new Error(`${label} must be a non-negative finite number. Received: ${String(value)}`);
|
|
36108
|
+
}
|
|
36109
|
+
return value;
|
|
36110
|
+
}
|
|
36111
|
+
function normalizeVec2(value, label, validate) {
|
|
36112
|
+
if (typeof value === "number") {
|
|
36113
|
+
const n = validate(value, label);
|
|
36114
|
+
return [n, n];
|
|
36115
|
+
}
|
|
36116
|
+
return [validate(value[0], `${label}[0]`), validate(value[1], `${label}[1]`)];
|
|
36117
|
+
}
|
|
36118
|
+
function normalizeDirection$1(value, label) {
|
|
36119
|
+
const x2 = requireFinite$8(value[0], `${label}[0]`);
|
|
36120
|
+
const y2 = requireFinite$8(value[1], `${label}[1]`);
|
|
36121
|
+
const length4 = Math.hypot(x2, y2);
|
|
36122
|
+
if (length4 <= 0) throw new Error(`${label} must not be the zero vector.`);
|
|
36123
|
+
return [x2 / length4, y2 / length4];
|
|
36124
|
+
}
|
|
36125
|
+
function f$2(value) {
|
|
36126
|
+
if (!Number.isFinite(value)) return "0";
|
|
36127
|
+
return Number(value.toPrecision(12)).toString();
|
|
36128
|
+
}
|
|
36129
|
+
function emitSurfacePatternJsExpression(node) {
|
|
36130
|
+
switch (node.kind) {
|
|
36131
|
+
case "surfacePattern:constant":
|
|
36132
|
+
return f$2(node.value);
|
|
36133
|
+
case "surfacePattern:sineWave": {
|
|
36134
|
+
const coord = `(u * ${f$2(node.direction[0])} + v * ${f$2(node.direction[1])})`;
|
|
36135
|
+
const phase = `(${coord} * ${f$2(2 * Math.PI / node.wavelength)} + ${f$2(node.phase)})`;
|
|
36136
|
+
return `(${f$2(node.bias)} + Math.sin(${phase}) * ${f$2(node.amplitude)})`;
|
|
36137
|
+
}
|
|
36138
|
+
case "surfacePattern:stripes": {
|
|
36139
|
+
const coord = `(u * ${f$2(node.direction[0])} + v * ${f$2(node.direction[1])})`;
|
|
36140
|
+
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)};})()`;
|
|
36141
|
+
}
|
|
36142
|
+
case "surfacePattern:overUnderWeave":
|
|
36143
|
+
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)};})()`;
|
|
36144
|
+
case "surfacePattern:abs":
|
|
36145
|
+
return `Math.abs(${emitSurfacePatternJsExpression(node.child)})`;
|
|
36146
|
+
case "surfacePattern:negate":
|
|
36147
|
+
return `(-(${emitSurfacePatternJsExpression(node.child)}))`;
|
|
36148
|
+
case "surfacePattern:add":
|
|
36149
|
+
return node.children.length === 0 ? "0" : `(${node.children.map(emitSurfacePatternJsExpression).join(" + ")})`;
|
|
36150
|
+
case "surfacePattern:multiply":
|
|
36151
|
+
return node.children.length === 0 ? "1" : `(${node.children.map(emitSurfacePatternJsExpression).join(" * ")})`;
|
|
36152
|
+
case "surfacePattern:min":
|
|
36153
|
+
return node.children.length === 0 ? "0" : `Math.min(${node.children.map(emitSurfacePatternJsExpression).join(", ")})`;
|
|
36154
|
+
case "surfacePattern:max":
|
|
36155
|
+
return node.children.length === 0 ? "0" : `Math.max(${node.children.map(emitSurfacePatternJsExpression).join(", ")})`;
|
|
36156
|
+
case "surfacePattern:clamp":
|
|
36157
|
+
return `Math.min(${f$2(node.max)}, Math.max(${f$2(node.min)}, ${emitSurfacePatternJsExpression(node.child)}))`;
|
|
36158
|
+
}
|
|
36159
|
+
}
|
|
35065
36160
|
const SCULPT_MATERIAL_PRESETS = {
|
|
35066
36161
|
ceramic: {
|
|
35067
36162
|
color: "#f4f0e6",
|
|
@@ -35131,6 +36226,18 @@ function requirePositiveFinite(value, label) {
|
|
|
35131
36226
|
}
|
|
35132
36227
|
return value;
|
|
35133
36228
|
}
|
|
36229
|
+
function requirePositiveInteger(value, label) {
|
|
36230
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value < 1) {
|
|
36231
|
+
throw new Error(`${label} must be a positive integer. Received: ${String(value)}`);
|
|
36232
|
+
}
|
|
36233
|
+
return value;
|
|
36234
|
+
}
|
|
36235
|
+
function requireNonNegativeFinite(value, label) {
|
|
36236
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
36237
|
+
throw new Error(`${label} must be a non-negative finite number. Received: ${String(value)}`);
|
|
36238
|
+
}
|
|
36239
|
+
return value;
|
|
36240
|
+
}
|
|
35134
36241
|
function resolveBlendRadius(input, label, fallback = 4) {
|
|
35135
36242
|
if (typeof input === "number") return requirePositiveFinite(input, `${label} radius`);
|
|
35136
36243
|
if ((input == null ? void 0 : input.radius) !== void 0) return requirePositiveFinite(input.radius, `${label} radius`);
|
|
@@ -35377,6 +36484,29 @@ class SdfShape {
|
|
|
35377
36484
|
clipBox(x2, y2, z2) {
|
|
35378
36485
|
return this.intersect(box$1(x2, y2, z2));
|
|
35379
36486
|
}
|
|
36487
|
+
/** Keep only the material where this shape overlaps another SDF pattern. */
|
|
36488
|
+
fillWith(pattern) {
|
|
36489
|
+
if (!(pattern instanceof SdfShape)) {
|
|
36490
|
+
throw new Error("SdfShape.fillWith() expects an SdfShape pattern, such as sdf.gyroid({ cellSize, wallThickness }).");
|
|
36491
|
+
}
|
|
36492
|
+
return this.intersect(pattern);
|
|
36493
|
+
}
|
|
36494
|
+
/** Keep only the gyroid lattice inside this shape. */
|
|
36495
|
+
fillWithGyroid(options) {
|
|
36496
|
+
return this.fillWith(gyroid(options));
|
|
36497
|
+
}
|
|
36498
|
+
/** Keep only the Schwarz-P lattice inside this shape. */
|
|
36499
|
+
fillWithSchwarzP(options) {
|
|
36500
|
+
return this.fillWith(schwarzP(options));
|
|
36501
|
+
}
|
|
36502
|
+
/** Keep only the diamond TPMS lattice inside this shape. */
|
|
36503
|
+
fillWithDiamond(options) {
|
|
36504
|
+
return this.fillWith(diamond(options));
|
|
36505
|
+
}
|
|
36506
|
+
/** Keep only the lidinoid TPMS lattice inside this shape. */
|
|
36507
|
+
fillWithLidinoid(options) {
|
|
36508
|
+
return this.fillWith(lidinoid(options));
|
|
36509
|
+
}
|
|
35380
36510
|
/** Smooth union — blends shapes together with a smooth radius. */
|
|
35381
36511
|
smoothUnion(other, radius) {
|
|
35382
36512
|
return this.withNode({ kind: "sdf:smoothUnion", children: [this._node, other._node], radius });
|
|
@@ -35437,6 +36567,21 @@ class SdfShape {
|
|
|
35437
36567
|
repeat(spacing, count) {
|
|
35438
36568
|
return this.withNode({ kind: "sdf:repeat", child: this._node, spacing, count: count ?? [0, 0, 0] });
|
|
35439
36569
|
}
|
|
36570
|
+
/**
|
|
36571
|
+
* Arrange this SDF in a circular array around the Z axis.
|
|
36572
|
+
*
|
|
36573
|
+
* The source shape is translated by `offset` in +X before arraying. This uses
|
|
36574
|
+
* angular domain folding, so evaluation stays O(1): the source SDF is sampled
|
|
36575
|
+
* twice no matter how many copies are requested.
|
|
36576
|
+
*/
|
|
36577
|
+
circularArray(count, offset2 = 0) {
|
|
36578
|
+
return this.withNode({
|
|
36579
|
+
kind: "sdf:circularArray",
|
|
36580
|
+
child: this._node,
|
|
36581
|
+
count: requirePositiveInteger(count, "SdfShape.circularArray() count"),
|
|
36582
|
+
offset: requireNonNegativeFinite(offset2, "SdfShape.circularArray() offset")
|
|
36583
|
+
});
|
|
36584
|
+
}
|
|
35440
36585
|
/** Hollow out, keeping only a shell of given thickness. */
|
|
35441
36586
|
shell(thickness) {
|
|
35442
36587
|
return this.withNode({ kind: "sdf:shell", child: this._node, thickness });
|
|
@@ -35448,8 +36593,8 @@ class SdfShape {
|
|
|
35448
36593
|
* // Function displacement
|
|
35449
36594
|
* shape.displace((x, y, z) => Math.sin(x) * 0.5)
|
|
35450
36595
|
*
|
|
35451
|
-
* // Pattern displacement
|
|
35452
|
-
* shape.displace(sdf.
|
|
36596
|
+
* // Pattern displacement from a 3D SDF field
|
|
36597
|
+
* shape.displace(sdf.knurl({ pitch: 2, depth: 0.3 }))
|
|
35453
36598
|
* ```
|
|
35454
36599
|
*/
|
|
35455
36600
|
displace(fn, constants) {
|
|
@@ -35473,10 +36618,18 @@ class SdfShape {
|
|
|
35473
36618
|
* UV coordinates are in **surface millimeters** — patterns defined with `spacing: 3`
|
|
35474
36619
|
* always produce 3mm spacing, regardless of shape size.
|
|
35475
36620
|
*
|
|
36621
|
+
* Prefer `sdf.pattern2d()` or built-in surface patterns when the relief should
|
|
36622
|
+
* stay on the native shader and meshing path. Callback functions are supported
|
|
36623
|
+
* for experimentation, but they are opaque to the typed pattern optimizer.
|
|
36624
|
+
*
|
|
35476
36625
|
* ```js
|
|
35477
|
-
* //
|
|
36626
|
+
* // Native typed pattern — auto-detects sphere UV
|
|
36627
|
+
* const p = sdf.pattern2d()
|
|
36628
|
+
* const ribs = p.stripes({ spacing: 3, width: 0.8, depth: 0.35 })
|
|
36629
|
+
* .add(p.sineWave({ direction: [0, 1], wavelength: 14, amplitude: 0.08 }))
|
|
36630
|
+
*
|
|
35478
36631
|
* sdf.sphere(27).shell(3)
|
|
35479
|
-
* .surfaceDisplace(
|
|
36632
|
+
* .surfaceDisplace(ribs)
|
|
35480
36633
|
* .toShape()
|
|
35481
36634
|
*
|
|
35482
36635
|
* // Custom 2D pattern via function
|
|
@@ -35486,15 +36639,18 @@ class SdfShape {
|
|
|
35486
36639
|
surfaceDisplace(pattern, options) {
|
|
35487
36640
|
let body;
|
|
35488
36641
|
let constants;
|
|
36642
|
+
let typedPattern;
|
|
35489
36643
|
if (pattern instanceof SurfacePattern) {
|
|
35490
36644
|
body = pattern.body;
|
|
35491
36645
|
constants = pattern.constants;
|
|
36646
|
+
typedPattern = getTypedSurfacePattern(pattern);
|
|
35492
36647
|
} else {
|
|
35493
36648
|
body = extractFunctionBody(pattern);
|
|
35494
36649
|
}
|
|
35495
36650
|
return this.withNode({
|
|
35496
36651
|
kind: "sdf:surfaceDisplace",
|
|
35497
36652
|
child: this._node,
|
|
36653
|
+
...typedPattern ? { pattern: typedPattern } : {},
|
|
35498
36654
|
patternBody: body,
|
|
35499
36655
|
constants,
|
|
35500
36656
|
...(options == null ? void 0 : options.uv) ? { uvMode: options.uv } : {},
|
|
@@ -35713,24 +36869,10 @@ function weave(options) {
|
|
|
35713
36869
|
});
|
|
35714
36870
|
}
|
|
35715
36871
|
function basketWeave(options) {
|
|
35716
|
-
const SP = (options == null ? void 0 : options.spacing) ?? 3;
|
|
35717
|
-
const TW = (options == null ? void 0 : options.threadWidth) ?? 1.5;
|
|
35718
|
-
const D2 = (options == null ? void 0 : options.depth) ?? 0.8;
|
|
35719
|
-
|
|
35720
|
-
const body = `(function() {
|
|
35721
|
-
var su = u / ${SP};
|
|
35722
|
-
var sv = v / ${SP};
|
|
35723
|
-
var du = Math.abs(su - Math.round(su)) * ${SP};
|
|
35724
|
-
var dv = Math.abs(sv - Math.round(sv)) * ${SP};
|
|
35725
|
-
var hw = ${hw};
|
|
35726
|
-
var pU = Math.max(0, 1 - du / hw); pU *= pU;
|
|
35727
|
-
var pV = Math.max(0, 1 - dv / hw); pV *= pV;
|
|
35728
|
-
var checker = ((Math.round(su) & 65535) + (Math.round(sv) & 65535)) & 1;
|
|
35729
|
-
var top = checker ? pV : pU;
|
|
35730
|
-
var bot = checker ? pU : pV;
|
|
35731
|
-
return -(top > bot * 0.15 ? top : bot * 0.15) * ${D2};
|
|
35732
|
-
})()`;
|
|
35733
|
-
return new SurfacePattern(body);
|
|
36872
|
+
const SP = requirePositiveFinite((options == null ? void 0 : options.spacing) ?? 3, "sdf.basketWeave() spacing");
|
|
36873
|
+
const TW = requirePositiveFinite((options == null ? void 0 : options.threadWidth) ?? 1.5, "sdf.basketWeave() threadWidth");
|
|
36874
|
+
const D2 = requireNonNegativeFinite((options == null ? void 0 : options.depth) ?? 0.8, "sdf.basketWeave() depth");
|
|
36875
|
+
return pattern2d().overUnderWeave({ spacing: SP, threadWidth: TW, depth: D2 });
|
|
35734
36876
|
}
|
|
35735
36877
|
function fromFunction(fn, options) {
|
|
35736
36878
|
if (!options || typeof options !== "object") {
|
|
@@ -35763,6 +36905,9 @@ function bend(shape, radius) {
|
|
|
35763
36905
|
function repeat(shape, spacing, count) {
|
|
35764
36906
|
return shape.repeat(spacing, count);
|
|
35765
36907
|
}
|
|
36908
|
+
function circularArray(shape, count, offset2 = 0) {
|
|
36909
|
+
return shape.circularArray(count, offset2);
|
|
36910
|
+
}
|
|
35766
36911
|
function resolveTpmsOptions(options) {
|
|
35767
36912
|
const wallThickness = options.wallThickness;
|
|
35768
36913
|
const thickness = wallThickness ?? options.thickness;
|
|
@@ -36203,12 +37348,16 @@ const sdf = {
|
|
|
36203
37348
|
weave,
|
|
36204
37349
|
/** Basket weave surface pattern — threads with over-under crossings in UV space. Returns a SurfacePattern for use with `.surfaceDisplace()`. */
|
|
36205
37350
|
basketWeave,
|
|
37351
|
+
/** Create typed, composable 2D surface patterns for `.surfaceDisplace()`. */
|
|
37352
|
+
pattern2d,
|
|
36206
37353
|
/** Twist an SDF shape around the Z axis. */
|
|
36207
37354
|
twist,
|
|
36208
37355
|
/** Bend an SDF shape around the Z axis. */
|
|
36209
37356
|
bend,
|
|
36210
37357
|
/** Repeat an SDF shape in space. */
|
|
36211
37358
|
repeat,
|
|
37359
|
+
/** Arrange an SDF shape in a circular array around the Z axis with O(1) folded-domain evaluation. */
|
|
37360
|
+
circularArray,
|
|
36212
37361
|
/** A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`. */
|
|
36213
37362
|
SurfacePattern,
|
|
36214
37363
|
/** Create a custom SDF from one expression; shader-safe expressions raymarch directly. */
|
|
@@ -41128,13 +42277,16 @@ class SolvedAssembly {
|
|
|
41128
42277
|
* @category Assembly
|
|
41129
42278
|
*/
|
|
41130
42279
|
toGroup() {
|
|
42280
|
+
var _a3;
|
|
41131
42281
|
const children = [];
|
|
41132
42282
|
const childNames = [];
|
|
41133
|
-
|
|
42283
|
+
const childTags = [];
|
|
42284
|
+
for (const [name, rec] of this.parts) {
|
|
41134
42285
|
children.push(this.getPart(name));
|
|
41135
42286
|
childNames.push(name);
|
|
42287
|
+
childTags.push(normalizeSceneTags((_a3 = rec.metadata) == null ? void 0 : _a3.tags, `Assembly part "${name}" metadata.tags`));
|
|
41136
42288
|
}
|
|
41137
|
-
return new ShapeGroup(children, childNames);
|
|
42289
|
+
return new ShapeGroup(children, childNames, childTags);
|
|
41138
42290
|
}
|
|
41139
42291
|
/**
|
|
41140
42292
|
* Return an array of named scene objects for the viewport renderer.
|
|
@@ -41168,17 +42320,18 @@ class SolvedAssembly {
|
|
|
41168
42320
|
const used = usedByPart.get(partName);
|
|
41169
42321
|
if (used && used.length > 0) markShapePortsUsed(shape, used);
|
|
41170
42322
|
};
|
|
41171
|
-
const appendGroupChildren = (grp, prefix, partName, out2) => {
|
|
42323
|
+
const appendGroupChildren = (grp, prefix, partName, out2, inheritedTags = []) => {
|
|
41172
42324
|
grp.children.forEach((child, index2) => {
|
|
41173
42325
|
const childName = grp.childName(index2);
|
|
41174
42326
|
const label = childName ? `${prefix}.${childName}` : `${prefix}.${index2 + 1}`;
|
|
42327
|
+
const tags = mergeSceneTags(inheritedTags, grp.tagsForChild(index2));
|
|
41175
42328
|
if (child instanceof ShapeGroup) {
|
|
41176
|
-
appendGroupChildren(child, label, partName, out2);
|
|
42329
|
+
appendGroupChildren(child, label, partName, out2, tags);
|
|
41177
42330
|
return;
|
|
41178
42331
|
}
|
|
41179
42332
|
if (child instanceof Shape) {
|
|
41180
42333
|
markUsedOnShape(child, partName);
|
|
41181
|
-
out2.push({ name: label, shape: child });
|
|
42334
|
+
out2.push({ name: label, shape: child, ...tags.length > 0 ? { tags } : {} });
|
|
41182
42335
|
}
|
|
41183
42336
|
});
|
|
41184
42337
|
};
|
|
@@ -41544,7 +42697,7 @@ class Assembly {
|
|
|
41544
42697
|
*
|
|
41545
42698
|
* @param name - Unique part name (must not already exist)
|
|
41546
42699
|
* @param part - The `Shape` or `ShapeGroup` geometry
|
|
41547
|
-
* @param options - Optional `{ transform, metadata }` (material, process, qty, etc.)
|
|
42700
|
+
* @param options - Optional `{ transform, metadata }` (material, process, qty, tags, etc.)
|
|
41548
42701
|
* @returns `this` for chaining
|
|
41549
42702
|
* @category Assembly
|
|
41550
42703
|
*/
|
|
@@ -42498,15 +43651,18 @@ class ImportedAssembly {
|
|
|
42498
43651
|
* Any stored placement offset and placement references are forwarded to the group.
|
|
42499
43652
|
*/
|
|
42500
43653
|
toGroup(state) {
|
|
43654
|
+
var _a3;
|
|
42501
43655
|
const solved = this._assembly.solve(state);
|
|
42502
43656
|
const def = this._assembly.describe();
|
|
42503
43657
|
const children = [];
|
|
42504
43658
|
const childNames = [];
|
|
43659
|
+
const childTags = [];
|
|
42505
43660
|
for (const p2 of def.parts) {
|
|
42506
43661
|
children.push(solved.getPart(p2.name));
|
|
42507
43662
|
childNames.push(p2.name);
|
|
43663
|
+
childTags.push(normalizeSceneTags((_a3 = p2.metadata) == null ? void 0 : _a3.tags, `Assembly part "${p2.name}" metadata.tags`));
|
|
42508
43664
|
}
|
|
42509
|
-
let result = new ShapeGroup(children, childNames);
|
|
43665
|
+
let result = new ShapeGroup(children, childNames, childTags);
|
|
42510
43666
|
const [dx, dy, dz] = this._offset;
|
|
42511
43667
|
if (dx !== 0 || dy !== 0 || dz !== 0) {
|
|
42512
43668
|
result = result.translate(dx, dy, dz);
|
|
@@ -43052,7 +44208,7 @@ class GCodeBuilder {
|
|
|
43052
44208
|
this.lines.push("G1 E-0.8 F1800 ; retract");
|
|
43053
44209
|
this.lines.push("");
|
|
43054
44210
|
const safeZ = Math.min(maxZ + 5, p2.bedZ - 1);
|
|
43055
|
-
this.lines.push(`G1 Z${
|
|
44211
|
+
this.lines.push(`G1 Z${f$1(safeZ)} F900 ; lift nozzle above print`);
|
|
43056
44212
|
this.lines.push("");
|
|
43057
44213
|
this.lines.push("M140 S0 ; bed off");
|
|
43058
44214
|
this.lines.push("M104 S0 ; hotend off");
|
|
@@ -43096,7 +44252,7 @@ class GCodeBuilder {
|
|
|
43096
44252
|
travelTo(x2, y2, z2) {
|
|
43097
44253
|
this.retract();
|
|
43098
44254
|
const from = [...this.pos];
|
|
43099
|
-
this.lines.push(`G0 X${
|
|
44255
|
+
this.lines.push(`G0 X${f$1(x2)} Y${f$1(y2)} Z${f$1(z2)} F${Math.round(this.profile.travelSpeed)}`);
|
|
43100
44256
|
if (this.posInitialized) {
|
|
43101
44257
|
this._segments.push({ from, to: [x2, y2, z2], extrude: false, speed: this.profile.travelSpeed });
|
|
43102
44258
|
}
|
|
@@ -43128,7 +44284,7 @@ class GCodeBuilder {
|
|
|
43128
44284
|
const beadArea = this.profile.layerHeight * this.profile.nozzle;
|
|
43129
44285
|
const eIncrement = beadArea * dist4 / this.filamentArea;
|
|
43130
44286
|
this.e += eIncrement;
|
|
43131
|
-
this.lines.push(`G1 X${
|
|
44287
|
+
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)}`);
|
|
43132
44288
|
if (this.posInitialized) {
|
|
43133
44289
|
this._segments.push({ from, to: [x2, y2, z2], extrude: true, speed: this.currentSpeed });
|
|
43134
44290
|
}
|
|
@@ -43226,13 +44382,13 @@ class GCodeBuilder {
|
|
|
43226
44382
|
retract() {
|
|
43227
44383
|
if (this.retracted) return;
|
|
43228
44384
|
this.e -= this.profile.retractionDistance;
|
|
43229
|
-
this.lines.push(`G1 E${
|
|
44385
|
+
this.lines.push(`G1 E${f$1(this.e)} F${Math.round(this.profile.retractionSpeed)}`);
|
|
43230
44386
|
this.retracted = true;
|
|
43231
44387
|
}
|
|
43232
44388
|
unretract() {
|
|
43233
44389
|
if (!this.retracted) return;
|
|
43234
44390
|
this.e += this.profile.retractionDistance;
|
|
43235
|
-
this.lines.push(`G1 E${
|
|
44391
|
+
this.lines.push(`G1 E${f$1(this.e)} F${Math.round(this.profile.retractionSpeed)}`);
|
|
43236
44392
|
this.retracted = false;
|
|
43237
44393
|
}
|
|
43238
44394
|
// ---- Bounds tracking ----
|
|
@@ -43303,7 +44459,7 @@ class GCodeBuilder {
|
|
|
43303
44459
|
return this.lines.join("\n") + "\n";
|
|
43304
44460
|
}
|
|
43305
44461
|
}
|
|
43306
|
-
function
|
|
44462
|
+
function f$1(n) {
|
|
43307
44463
|
return n.toFixed(5).replace(/\.?0+$/, "");
|
|
43308
44464
|
}
|
|
43309
44465
|
function bambuModelName(preset) {
|
|
@@ -45447,10 +46603,8 @@ class PathBuilder {
|
|
|
45447
46603
|
if (radius <= 0) throw new Error("fillet: radius must be positive");
|
|
45448
46604
|
const n = this.segs.length;
|
|
45449
46605
|
if (n < 2) throw new Error("fillet: need at least 2 segments before a fillet");
|
|
45450
|
-
const prev = this.segs[n - 2];
|
|
45451
46606
|
const curr = this.segs[n - 1];
|
|
45452
|
-
|
|
45453
|
-
const { trimA, trimB, arcSeg } = this.computeFilletGeom(radius);
|
|
46607
|
+
const { trimA, arcSeg } = this.computeFilletGeom(radius);
|
|
45454
46608
|
if (!arcSeg) throw new Error("fillet: cannot fillet these segments (parallel or degenerate)");
|
|
45455
46609
|
this.trimLastSegEnd(n - 2, trimA[0], trimA[1]);
|
|
45456
46610
|
const trimmedSeg = { ...curr };
|
|
@@ -45522,7 +46676,6 @@ class PathBuilder {
|
|
|
45522
46676
|
}
|
|
45523
46677
|
getSegDirAt(seg, which) {
|
|
45524
46678
|
if (seg.kind === "line" || seg.kind === "move") {
|
|
45525
|
-
this.segs.length;
|
|
45526
46679
|
const idx = this.segs.indexOf(seg);
|
|
45527
46680
|
if (seg.kind === "line") {
|
|
45528
46681
|
let sx, sy;
|
|
@@ -45764,6 +46917,41 @@ class PathBuilder {
|
|
|
45764
46917
|
}
|
|
45765
46918
|
return pts;
|
|
45766
46919
|
}
|
|
46920
|
+
/**
|
|
46921
|
+
* Return the open path as a sampled 2D polyline.
|
|
46922
|
+
*
|
|
46923
|
+
* This is for construction geometry such as guide rails, measured centerlines,
|
|
46924
|
+
* and curve-driven helpers where the authored path should stay open instead of
|
|
46925
|
+
* becoming a filled sketch or stroked profile.
|
|
46926
|
+
*
|
|
46927
|
+
* **Example**
|
|
46928
|
+
*
|
|
46929
|
+
* ```ts
|
|
46930
|
+
* const rail = path()
|
|
46931
|
+
* .moveTo(24, 0)
|
|
46932
|
+
* .bezierTo(32, 44, 28, 92, 18, 120)
|
|
46933
|
+
* .toPolyline();
|
|
46934
|
+
* ```
|
|
46935
|
+
*
|
|
46936
|
+
* @returns A sampled open polyline.
|
|
46937
|
+
* @category Path Builder
|
|
46938
|
+
*/
|
|
46939
|
+
toPolyline() {
|
|
46940
|
+
const moveCount = this.segs.filter((seg) => seg.kind === "move").length;
|
|
46941
|
+
if (moveCount > 1) {
|
|
46942
|
+
throw new Error("path().toPolyline() supports one continuous open path. Use separate path() builders for separate rails.");
|
|
46943
|
+
}
|
|
46944
|
+
const pts = [];
|
|
46945
|
+
for (const point2 of this.tessellate()) {
|
|
46946
|
+
if (!point2.every(Number.isFinite)) throw new Error("path().toPolyline() produced a non-finite point");
|
|
46947
|
+
const previous = pts[pts.length - 1];
|
|
46948
|
+
if (!previous || Math.hypot(point2[0] - previous[0], point2[1] - previous[1]) > 1e-9) {
|
|
46949
|
+
pts.push(point2);
|
|
46950
|
+
}
|
|
46951
|
+
}
|
|
46952
|
+
if (pts.length < 2) throw new Error("path().toPolyline() needs at least 2 points");
|
|
46953
|
+
return pts;
|
|
46954
|
+
}
|
|
45767
46955
|
// ── Output ────────────────────────────────────────────────────────────────
|
|
45768
46956
|
/**
|
|
45769
46957
|
* Close the path and return a filled `Sketch`.
|
|
@@ -46814,7 +48002,8 @@ function explode(items, options = {}) {
|
|
|
46814
48002
|
if (child instanceof ShapeGroup) return explodeGroup(child, p2, depth + 1, total, groupCenter, motion.branchDirection);
|
|
46815
48003
|
return explodeLeaf(child, explodeAdd(total, leafMotion(child, p2, depth + 1, groupCenter, motion.branchDirection).offset));
|
|
46816
48004
|
}),
|
|
46817
|
-
grp.childNames
|
|
48005
|
+
grp.childNames,
|
|
48006
|
+
grp.children.map((_2, i) => grp.tagsForChild(i))
|
|
46818
48007
|
);
|
|
46819
48008
|
};
|
|
46820
48009
|
const explodeItemNode = (item, path2, depth, inherited, parentCenter, parentDirection) => {
|
|
@@ -46850,7 +48039,8 @@ function explode(items, options = {}) {
|
|
|
46850
48039
|
if (child instanceof ShapeGroup) return explodeGroup(child, p2, 1, [0, 0, 0], rootCenter, void 0);
|
|
46851
48040
|
return explodeLeaf(child, nodeMotion(child, p2, 1, rootCenter, void 0).offset);
|
|
46852
48041
|
}),
|
|
46853
|
-
items.childNames
|
|
48042
|
+
items.childNames,
|
|
48043
|
+
items.children.map((_2, i) => items.tagsForChild(i))
|
|
46854
48044
|
);
|
|
46855
48045
|
}
|
|
46856
48046
|
return items.map((item, i) => {
|
|
@@ -47619,6 +48809,398 @@ function spurGear(options) {
|
|
|
47619
48809
|
});
|
|
47620
48810
|
return attachGearMeta(shapeWithConnectors, meta2);
|
|
47621
48811
|
}
|
|
48812
|
+
function requirePositive$7(scope, name, value) {
|
|
48813
|
+
if (!isFinitePositive(value)) throw new Error(`${scope}: "${name}" must be > 0`);
|
|
48814
|
+
}
|
|
48815
|
+
function requireOptionalBore(scope, boreDiameter, maxDiameter) {
|
|
48816
|
+
const bore = boreDiameter ?? 0;
|
|
48817
|
+
if (!Number.isFinite(bore) || bore < 0) throw new Error(`${scope}: "boreDiameter" must be >= 0`);
|
|
48818
|
+
if (bore > 0 && bore >= maxDiameter) throw new Error(`${scope}: bore is too large for the body`);
|
|
48819
|
+
return bore;
|
|
48820
|
+
}
|
|
48821
|
+
function resolveSegments(segments) {
|
|
48822
|
+
if (segments === void 0) return void 0;
|
|
48823
|
+
if (!Number.isInteger(segments) || segments < 12) throw new Error('gear body: "segments" must be an integer >= 12');
|
|
48824
|
+
return segments;
|
|
48825
|
+
}
|
|
48826
|
+
function cutBore$1(shape, boreDiameter) {
|
|
48827
|
+
if (boreDiameter <= 0) return shape;
|
|
48828
|
+
const bounds = shape.boundingBox();
|
|
48829
|
+
const height = bounds.max[2] - bounds.min[2] + 2;
|
|
48830
|
+
const cutter = cylinder(height, boreDiameter * 0.5, void 0, 64).translate(0, 0, bounds.min[2] - 1);
|
|
48831
|
+
return shape.subtract(cutter);
|
|
48832
|
+
}
|
|
48833
|
+
function gearBodyDisk(options) {
|
|
48834
|
+
requirePositive$7("gearBodyDisk", "outerRadius", options.outerRadius);
|
|
48835
|
+
requirePositive$7("gearBodyDisk", "faceWidth", options.faceWidth);
|
|
48836
|
+
const bore = requireOptionalBore("gearBodyDisk", options.boreDiameter, options.outerRadius * 2);
|
|
48837
|
+
const segments = resolveSegments(options.segments);
|
|
48838
|
+
const outer = circle2d(options.outerRadius, segments);
|
|
48839
|
+
const profile = bore > 0 ? difference2d(outer, circle2d(bore * 0.5, segments)) : outer;
|
|
48840
|
+
return sketchExtrude(profile, options.faceWidth);
|
|
48841
|
+
}
|
|
48842
|
+
function gearBodyDiskWithHub(options) {
|
|
48843
|
+
requirePositive$7("gearBodyDiskWithHub", "hubDiameter", options.hubDiameter);
|
|
48844
|
+
if (options.hubDiameter >= options.outerRadius * 2) {
|
|
48845
|
+
throw new Error('gearBodyDiskWithHub: "hubDiameter" must be smaller than the outer diameter');
|
|
48846
|
+
}
|
|
48847
|
+
const bore = requireOptionalBore("gearBodyDiskWithHub", options.boreDiameter, options.hubDiameter);
|
|
48848
|
+
const base = gearBodyDisk({ ...options, boreDiameter: 0 });
|
|
48849
|
+
const hubFaceWidth = options.hubFaceWidth ?? options.faceWidth * 1.5;
|
|
48850
|
+
requirePositive$7("gearBodyDiskWithHub", "hubFaceWidth", hubFaceWidth);
|
|
48851
|
+
const hub = cylinder(hubFaceWidth, options.hubDiameter * 0.5, void 0, options.segments).translate(
|
|
48852
|
+
0,
|
|
48853
|
+
0,
|
|
48854
|
+
(options.faceWidth - hubFaceWidth) * 0.5
|
|
48855
|
+
);
|
|
48856
|
+
return cutBore$1(base.add(hub), bore);
|
|
48857
|
+
}
|
|
48858
|
+
function gearBodySpoked(options) {
|
|
48859
|
+
requirePositive$7("gearBodySpoked", "outerRadius", options.outerRadius);
|
|
48860
|
+
requirePositive$7("gearBodySpoked", "faceWidth", options.faceWidth);
|
|
48861
|
+
requirePositive$7("gearBodySpoked", "rimWidth", options.rimWidth);
|
|
48862
|
+
requirePositive$7("gearBodySpoked", "hubDiameter", options.hubDiameter);
|
|
48863
|
+
requirePositive$7("gearBodySpoked", "spokeWidth", options.spokeWidth);
|
|
48864
|
+
if (!Number.isInteger(options.spokeCount) || options.spokeCount < 2) {
|
|
48865
|
+
throw new Error('gearBodySpoked: "spokeCount" must be an integer >= 2');
|
|
48866
|
+
}
|
|
48867
|
+
const hubRadius = options.hubDiameter * 0.5;
|
|
48868
|
+
const rimInnerRadius = options.outerRadius - options.rimWidth;
|
|
48869
|
+
if (rimInnerRadius <= hubRadius) throw new Error("gearBodySpoked: rim overlaps the hub");
|
|
48870
|
+
const bore = requireOptionalBore("gearBodySpoked", options.boreDiameter, options.hubDiameter);
|
|
48871
|
+
const segments = resolveSegments(options.segments);
|
|
48872
|
+
const rim = difference2d(circle2d(options.outerRadius, segments), circle2d(rimInnerRadius, segments));
|
|
48873
|
+
const hub = circle2d(hubRadius, segments);
|
|
48874
|
+
const spokeLength = rimInnerRadius - hubRadius + options.spokeWidth;
|
|
48875
|
+
const spokeCenter = hubRadius + spokeLength * 0.5 - options.spokeWidth * 0.5;
|
|
48876
|
+
const spoke = sketchTranslate(rect(spokeLength, options.spokeWidth), spokeCenter, 0);
|
|
48877
|
+
const spokes = [];
|
|
48878
|
+
for (let i = 0; i < options.spokeCount; i++) {
|
|
48879
|
+
spokes.push(sketchRotateAround(spoke, 360 / options.spokeCount * i, [0, 0]));
|
|
48880
|
+
}
|
|
48881
|
+
const profile = bore > 0 ? difference2d(union2d(rim, hub, ...spokes), circle2d(bore * 0.5, segments)) : union2d(rim, hub, ...spokes);
|
|
48882
|
+
return sketchExtrude(profile, options.faceWidth);
|
|
48883
|
+
}
|
|
48884
|
+
function gearBodyFromProfile(profile, options) {
|
|
48885
|
+
if (!(profile instanceof Sketch)) throw new Error('gearBodyFromProfile: "profile" must be a Sketch');
|
|
48886
|
+
requirePositive$7("gearBodyFromProfile", "faceWidth", options.faceWidth);
|
|
48887
|
+
const bore = options.boreDiameter ?? 0;
|
|
48888
|
+
if (!Number.isFinite(bore) || bore < 0) throw new Error('gearBodyFromProfile: "boreDiameter" must be >= 0');
|
|
48889
|
+
return cutBore$1(sketchExtrude(profile, options.faceWidth), bore);
|
|
48890
|
+
}
|
|
48891
|
+
function requirePositive$6(scope, name, value) {
|
|
48892
|
+
if (!isFinitePositive(value)) throw new Error(`${scope}: "${name}" must be > 0`);
|
|
48893
|
+
}
|
|
48894
|
+
function requireFiniteAngle(scope, name, value) {
|
|
48895
|
+
if (value !== void 0 && !Number.isFinite(value)) throw new Error(`${scope}: "${name}" must be finite`);
|
|
48896
|
+
}
|
|
48897
|
+
function cutBore(shape, boreDiameter) {
|
|
48898
|
+
if (boreDiameter <= 0) return shape;
|
|
48899
|
+
const bounds = shape.boundingBox();
|
|
48900
|
+
const height = bounds.max[2] - bounds.min[2] + 2;
|
|
48901
|
+
const cutter = cylinder(height, boreDiameter * 0.5, void 0, 64).translate(0, 0, bounds.min[2] - 1);
|
|
48902
|
+
return shape.subtract(cutter);
|
|
48903
|
+
}
|
|
48904
|
+
function bodyOuterRadius(shape) {
|
|
48905
|
+
const bounds = shape.boundingBox();
|
|
48906
|
+
return Math.max(Math.abs(bounds.min[0]), Math.abs(bounds.max[0]), Math.abs(bounds.min[1]), Math.abs(bounds.max[1]));
|
|
48907
|
+
}
|
|
48908
|
+
function buildSpurTeethRegion(options, name, faceWidth) {
|
|
48909
|
+
const scope = "driveWheel.addSpurTeethBetween";
|
|
48910
|
+
const teethOnFullCircle = options.teethOnFullCircle;
|
|
48911
|
+
if (!Number.isInteger(teethOnFullCircle) || teethOnFullCircle < 6) {
|
|
48912
|
+
throw new Error(`${scope}: "teethOnFullCircle" must be an integer >= 6`);
|
|
48913
|
+
}
|
|
48914
|
+
const toothCount = options.toothCount;
|
|
48915
|
+
if (!Number.isInteger(toothCount) || toothCount < 1 || toothCount > teethOnFullCircle) {
|
|
48916
|
+
throw new Error(`${scope}: "toothCount" must be an integer in [1, teethOnFullCircle]`);
|
|
48917
|
+
}
|
|
48918
|
+
const firstTooth = options.firstTooth ?? 0;
|
|
48919
|
+
if (!Number.isInteger(firstTooth) || firstTooth < 0 || firstTooth >= teethOnFullCircle) {
|
|
48920
|
+
throw new Error(`${scope}: "firstTooth" must be an integer in [0, teethOnFullCircle)`);
|
|
48921
|
+
}
|
|
48922
|
+
let normalized;
|
|
48923
|
+
try {
|
|
48924
|
+
normalized = normalizeSpurGearOptions({ ...options, teeth: teethOnFullCircle, faceWidth, boreDiameter: 0 });
|
|
48925
|
+
} catch (error) {
|
|
48926
|
+
remapErrorPrefix(error, "spurGear", scope);
|
|
48927
|
+
}
|
|
48928
|
+
const gearMeta = buildSpurGearMeta(normalized);
|
|
48929
|
+
const pitchStepDeg = 360 / teethOnFullCircle;
|
|
48930
|
+
const fromAngleDeg = firstTooth * pitchStepDeg - pitchStepDeg * 0.5;
|
|
48931
|
+
const toAngleDeg = (firstTooth + toothCount - 1) * pitchStepDeg + pitchStepDeg * 0.5;
|
|
48932
|
+
const profile = buildSpurToothRegionProfile(gearMeta, firstTooth, toothCount, normalized.segmentsPerTooth);
|
|
48933
|
+
return {
|
|
48934
|
+
shape: sketchExtrude(profile, faceWidth),
|
|
48935
|
+
gearMeta,
|
|
48936
|
+
meta: {
|
|
48937
|
+
name,
|
|
48938
|
+
kind: "spurTeeth",
|
|
48939
|
+
fromAngleDeg,
|
|
48940
|
+
toAngleDeg,
|
|
48941
|
+
outerRadius: gearMeta.outerRadius,
|
|
48942
|
+
rootRadius: gearMeta.rootRadius,
|
|
48943
|
+
pitchRadius: gearMeta.pitchRadius,
|
|
48944
|
+
module: normalized.module,
|
|
48945
|
+
teethOnFullCircle,
|
|
48946
|
+
toothCount,
|
|
48947
|
+
faceWidth
|
|
48948
|
+
}
|
|
48949
|
+
};
|
|
48950
|
+
}
|
|
48951
|
+
function buildSolidArcRegion(options, name, faceWidth) {
|
|
48952
|
+
const scope = "driveWheel.addSolidArcBetween";
|
|
48953
|
+
requirePositive$6(scope, "outerRadius", options.outerRadius);
|
|
48954
|
+
const innerRadius = options.innerRadius ?? 0;
|
|
48955
|
+
if (!Number.isFinite(innerRadius) || innerRadius < 0) throw new Error(`${scope}: "innerRadius" must be >= 0`);
|
|
48956
|
+
if (innerRadius >= options.outerRadius) throw new Error(`${scope}: "innerRadius" must be smaller than "outerRadius"`);
|
|
48957
|
+
const sweepDeg = normalizedSweep(scope, options.fromAngleDeg, options.toAngleDeg);
|
|
48958
|
+
return {
|
|
48959
|
+
shape: sketchExtrude(buildSolidArcProfile(options, sweepDeg), faceWidth),
|
|
48960
|
+
meta: {
|
|
48961
|
+
name,
|
|
48962
|
+
kind: "solidArc",
|
|
48963
|
+
fromAngleDeg: options.fromAngleDeg,
|
|
48964
|
+
toAngleDeg: options.fromAngleDeg + sweepDeg,
|
|
48965
|
+
innerRadius,
|
|
48966
|
+
outerRadius: options.outerRadius,
|
|
48967
|
+
faceWidth
|
|
48968
|
+
}
|
|
48969
|
+
};
|
|
48970
|
+
}
|
|
48971
|
+
function normalizedSweep(scope, fromAngleDeg, toAngleDeg) {
|
|
48972
|
+
if (!Number.isFinite(fromAngleDeg)) throw new Error(`${scope}: "fromAngleDeg" must be finite`);
|
|
48973
|
+
if (!Number.isFinite(toAngleDeg)) throw new Error(`${scope}: "toAngleDeg" must be finite`);
|
|
48974
|
+
let sweep2 = toAngleDeg - fromAngleDeg;
|
|
48975
|
+
while (sweep2 <= 0) sweep2 += 360;
|
|
48976
|
+
if (sweep2 > 360 + EPSILON$1) throw new Error(`${scope}: angular sweep must be <= 360 degrees`);
|
|
48977
|
+
return Math.min(360, sweep2);
|
|
48978
|
+
}
|
|
48979
|
+
function buildSpurToothRegionProfile(meta2, firstTooth, toothCount, segmentsPerTooth) {
|
|
48980
|
+
const tooth = createSpurToothSketch(meta2, segmentsPerTooth);
|
|
48981
|
+
const teeth = [];
|
|
48982
|
+
for (let i = 0; i < toothCount; i++) {
|
|
48983
|
+
teeth.push(sketchRotateAround(tooth, 360 / meta2.teeth * (firstTooth + i), [0, 0]));
|
|
48984
|
+
}
|
|
48985
|
+
return union2d(...teeth);
|
|
48986
|
+
}
|
|
48987
|
+
function buildSolidArcProfile(options, sweepDeg) {
|
|
48988
|
+
const innerRadius = options.innerRadius ?? 0;
|
|
48989
|
+
const segments = options.segments ?? Math.max(16, Math.ceil(sweepDeg / 6));
|
|
48990
|
+
if (!Number.isInteger(segments) || segments < 4) throw new Error('driveWheel.addSolidArcBetween: "segments" must be an integer >= 4');
|
|
48991
|
+
if (Math.abs(sweepDeg - 360) < EPSILON$1) {
|
|
48992
|
+
const outer = circle2d(options.outerRadius, segments);
|
|
48993
|
+
return innerRadius > 0 ? difference2d(outer, circle2d(innerRadius, segments)) : outer;
|
|
48994
|
+
}
|
|
48995
|
+
const start = options.fromAngleDeg * Math.PI / 180;
|
|
48996
|
+
const end = start + sweepDeg * Math.PI / 180;
|
|
48997
|
+
const pts = [];
|
|
48998
|
+
if (innerRadius <= 0) pts.push([0, 0]);
|
|
48999
|
+
addArcPoints(pts, options.outerRadius, start, end, segments, true, true);
|
|
49000
|
+
if (innerRadius > 0) addArcPoints(pts, innerRadius, end, start, segments, true, true);
|
|
49001
|
+
return polygon(pts);
|
|
49002
|
+
}
|
|
49003
|
+
const DRIVE_WHEEL_META_KEY = Symbol.for("forgecad.library.driveWheelMeta");
|
|
49004
|
+
function attachDriveWheelMeta(shape, meta2) {
|
|
49005
|
+
shape[DRIVE_WHEEL_META_KEY] = meta2;
|
|
49006
|
+
return shape;
|
|
49007
|
+
}
|
|
49008
|
+
function readDriveWheelMeta(shape) {
|
|
49009
|
+
const meta2 = shape[DRIVE_WHEEL_META_KEY];
|
|
49010
|
+
return meta2 ?? null;
|
|
49011
|
+
}
|
|
49012
|
+
class DriveWheelBuilder {
|
|
49013
|
+
constructor(options = {}) {
|
|
49014
|
+
__publicField(this, "body");
|
|
49015
|
+
__publicField(this, "faceWidth");
|
|
49016
|
+
__publicField(this, "boreDiameter");
|
|
49017
|
+
__publicField(this, "regions", []);
|
|
49018
|
+
if (options.body !== void 0 && !(options.body instanceof Shape)) throw new Error('driveWheel: "body" must be a Shape');
|
|
49019
|
+
if (options.faceWidth !== void 0) requirePositive$6("driveWheel", "faceWidth", options.faceWidth);
|
|
49020
|
+
const boreDiameter = options.boreDiameter ?? 0;
|
|
49021
|
+
if (!Number.isFinite(boreDiameter) || boreDiameter < 0) throw new Error('driveWheel: "boreDiameter" must be >= 0');
|
|
49022
|
+
this.body = options.body;
|
|
49023
|
+
this.faceWidth = options.faceWidth;
|
|
49024
|
+
this.boreDiameter = boreDiameter;
|
|
49025
|
+
}
|
|
49026
|
+
/**
|
|
49027
|
+
* Add an involute spur-tooth window on part of the pitch circle.
|
|
49028
|
+
*/
|
|
49029
|
+
addSpurTeethBetween(options) {
|
|
49030
|
+
const faceWidth = this.resolveFaceWidth("driveWheel.addSpurTeethBetween", options.faceWidth);
|
|
49031
|
+
this.regions.push(buildSpurTeethRegion(options, this.resolveName("teeth", options.name), faceWidth));
|
|
49032
|
+
return this;
|
|
49033
|
+
}
|
|
49034
|
+
/**
|
|
49035
|
+
* Add a constant-radius solid arc region such as a dwell, stop, or pusher.
|
|
49036
|
+
*/
|
|
49037
|
+
addSolidArcBetween(options) {
|
|
49038
|
+
const faceWidth = this.resolveFaceWidth("driveWheel.addSolidArcBetween", options.faceWidth);
|
|
49039
|
+
this.regions.push(buildSolidArcRegion(options, this.resolveName("arc", options.name), faceWidth));
|
|
49040
|
+
return this;
|
|
49041
|
+
}
|
|
49042
|
+
/**
|
|
49043
|
+
* Add a fully custom region shape while preserving region metadata.
|
|
49044
|
+
*/
|
|
49045
|
+
addShapeRegion(name, shape, options = {}) {
|
|
49046
|
+
const scope = "driveWheel.addShapeRegion";
|
|
49047
|
+
if (typeof name !== "string" || name.trim().length === 0) throw new Error(`${scope}: "name" must be a non-empty string`);
|
|
49048
|
+
if (!(shape instanceof Shape)) throw new Error(`${scope}: "shape" must be a Shape`);
|
|
49049
|
+
requireFiniteAngle(scope, "fromAngleDeg", options.fromAngleDeg);
|
|
49050
|
+
requireFiniteAngle(scope, "toAngleDeg", options.toAngleDeg);
|
|
49051
|
+
if (options.innerRadius !== void 0 && (!Number.isFinite(options.innerRadius) || options.innerRadius < 0)) {
|
|
49052
|
+
throw new Error(`${scope}: "innerRadius" must be >= 0`);
|
|
49053
|
+
}
|
|
49054
|
+
if (options.outerRadius !== void 0) requirePositive$6(scope, "outerRadius", options.outerRadius);
|
|
49055
|
+
this.regions.push({
|
|
49056
|
+
shape: shape.clone(),
|
|
49057
|
+
meta: {
|
|
49058
|
+
name: this.resolveName("region", name),
|
|
49059
|
+
kind: "custom",
|
|
49060
|
+
...options
|
|
49061
|
+
}
|
|
49062
|
+
});
|
|
49063
|
+
return this;
|
|
49064
|
+
}
|
|
49065
|
+
/**
|
|
49066
|
+
* Build the final wheel shape with a bore connector and region metadata.
|
|
49067
|
+
*/
|
|
49068
|
+
build() {
|
|
49069
|
+
var _a3, _b3;
|
|
49070
|
+
if (this.regions.length === 0 && this.body === void 0) {
|
|
49071
|
+
throw new Error("driveWheel: add a body or at least one region before build()");
|
|
49072
|
+
}
|
|
49073
|
+
const faceWidth = this.resolveBuildFaceWidth();
|
|
49074
|
+
const firstGearRegion = (_a3 = this.regions.find((region) => region.gearMeta)) == null ? void 0 : _a3.gearMeta;
|
|
49075
|
+
if (firstGearRegion && this.boreDiameter * 0.5 >= firstGearRegion.rootRadius - EPSILON$1) {
|
|
49076
|
+
throw new Error("driveWheel: bore is too large for the first spur-tooth region");
|
|
49077
|
+
}
|
|
49078
|
+
const body = ((_b3 = this.body) == null ? void 0 : _b3.clone()) ?? gearBodyDisk({ outerRadius: (firstGearRegion == null ? void 0 : firstGearRegion.rootRadius) ?? this.defaultBodyRadius(), faceWidth });
|
|
49079
|
+
let combined = body;
|
|
49080
|
+
for (const region of this.regions) combined = combined.add(region.shape);
|
|
49081
|
+
combined = cutBore(combined, this.boreDiameter);
|
|
49082
|
+
const withConnectors = combined.withConnectors({
|
|
49083
|
+
bore: connectorFactory(
|
|
49084
|
+
"drive-wheel-bore",
|
|
49085
|
+
{ origin: [0, 0, faceWidth / 2], axis: [0, 0, 1], kind: "revolute" },
|
|
49086
|
+
this.measurements(faceWidth)
|
|
49087
|
+
)
|
|
49088
|
+
});
|
|
49089
|
+
return attachDriveWheelMeta(withConnectors, {
|
|
49090
|
+
kind: "driveWheel",
|
|
49091
|
+
faceWidth,
|
|
49092
|
+
boreDiameter: this.boreDiameter,
|
|
49093
|
+
regions: this.regionMetadata(body, faceWidth)
|
|
49094
|
+
});
|
|
49095
|
+
}
|
|
49096
|
+
measurements(faceWidth) {
|
|
49097
|
+
var _a3;
|
|
49098
|
+
const firstGearRegion = (_a3 = this.regions.find((region) => region.gearMeta)) == null ? void 0 : _a3.gearMeta;
|
|
49099
|
+
return {
|
|
49100
|
+
faceWidth,
|
|
49101
|
+
boreDiameter: this.boreDiameter,
|
|
49102
|
+
regionCount: this.regions.length,
|
|
49103
|
+
...firstGearRegion ? {
|
|
49104
|
+
module: firstGearRegion.module,
|
|
49105
|
+
teethOnFullCircle: firstGearRegion.teeth,
|
|
49106
|
+
pitchRadius: firstGearRegion.pitchRadius,
|
|
49107
|
+
outerRadius: firstGearRegion.outerRadius
|
|
49108
|
+
} : {}
|
|
49109
|
+
};
|
|
49110
|
+
}
|
|
49111
|
+
regionMetadata(body, faceWidth) {
|
|
49112
|
+
return [
|
|
49113
|
+
{ name: "body", kind: "body", outerRadius: bodyOuterRadius(body), faceWidth },
|
|
49114
|
+
...this.regions.map((region) => ({ ...region.meta }))
|
|
49115
|
+
];
|
|
49116
|
+
}
|
|
49117
|
+
resolveFaceWidth(scope, localFaceWidth) {
|
|
49118
|
+
const faceWidth = localFaceWidth ?? this.faceWidth;
|
|
49119
|
+
if (faceWidth === void 0) throw new Error(`${scope}: "faceWidth" is required unless driveWheel({ faceWidth }) was set`);
|
|
49120
|
+
requirePositive$6(scope, "faceWidth", faceWidth);
|
|
49121
|
+
if (this.faceWidth !== void 0 && localFaceWidth !== void 0 && Math.abs(this.faceWidth - localFaceWidth) > EPSILON$1) {
|
|
49122
|
+
throw new Error(`${scope}: region faceWidth must match driveWheel faceWidth`);
|
|
49123
|
+
}
|
|
49124
|
+
return faceWidth;
|
|
49125
|
+
}
|
|
49126
|
+
resolveBuildFaceWidth() {
|
|
49127
|
+
var _a3;
|
|
49128
|
+
const faceWidth = this.faceWidth ?? ((_a3 = this.regions.find((region) => region.meta.faceWidth !== void 0)) == null ? void 0 : _a3.meta.faceWidth);
|
|
49129
|
+
if (faceWidth === void 0) throw new Error('driveWheel: "faceWidth" is required before build()');
|
|
49130
|
+
return faceWidth;
|
|
49131
|
+
}
|
|
49132
|
+
defaultBodyRadius() {
|
|
49133
|
+
const outerRadius = this.regions.reduce((max2, region) => Math.max(max2, region.meta.outerRadius ?? 0), 0);
|
|
49134
|
+
if (outerRadius <= 0) throw new Error('driveWheel: "body" is required when regions do not define an outer radius');
|
|
49135
|
+
return outerRadius;
|
|
49136
|
+
}
|
|
49137
|
+
resolveName(prefix, requested) {
|
|
49138
|
+
const base = (requested == null ? void 0 : requested.trim()) || prefix;
|
|
49139
|
+
if (this.regions.every((region) => region.meta.name !== base)) return base;
|
|
49140
|
+
for (let i = 2; ; i++) {
|
|
49141
|
+
const candidate = `${base}${i}`;
|
|
49142
|
+
if (this.regions.every((region) => region.meta.name !== candidate)) return candidate;
|
|
49143
|
+
}
|
|
49144
|
+
}
|
|
49145
|
+
}
|
|
49146
|
+
function driveWheel(options = {}) {
|
|
49147
|
+
return new DriveWheelBuilder(options);
|
|
49148
|
+
}
|
|
49149
|
+
function normalizeSectorGearOptions(options) {
|
|
49150
|
+
const teethOnFullCircle = options.teethOnFullCircle;
|
|
49151
|
+
if (!Number.isInteger(teethOnFullCircle) || teethOnFullCircle < 6) {
|
|
49152
|
+
throw new Error('sectorGear: "teethOnFullCircle" must be an integer >= 6');
|
|
49153
|
+
}
|
|
49154
|
+
const toothCount = options.toothCount;
|
|
49155
|
+
if (!Number.isInteger(toothCount) || toothCount < 1 || toothCount > teethOnFullCircle) {
|
|
49156
|
+
throw new Error('sectorGear: "toothCount" must be an integer in [1, teethOnFullCircle]');
|
|
49157
|
+
}
|
|
49158
|
+
const firstTooth = options.firstTooth ?? 0;
|
|
49159
|
+
if (!Number.isInteger(firstTooth) || firstTooth < 0 || firstTooth >= teethOnFullCircle) {
|
|
49160
|
+
throw new Error('sectorGear: "firstTooth" must be an integer in [0, teethOnFullCircle)');
|
|
49161
|
+
}
|
|
49162
|
+
return {
|
|
49163
|
+
...normalizeSpurGearOptions({ ...options, teeth: teethOnFullCircle }),
|
|
49164
|
+
teethOnFullCircle,
|
|
49165
|
+
toothCount,
|
|
49166
|
+
firstTooth,
|
|
49167
|
+
boreDiameter: options.boreDiameter ?? 0
|
|
49168
|
+
};
|
|
49169
|
+
}
|
|
49170
|
+
function sectorGear(options) {
|
|
49171
|
+
const normalized = normalizeSectorGearOptions(options);
|
|
49172
|
+
if (options.body !== void 0 && !(options.body instanceof Shape)) {
|
|
49173
|
+
throw new Error('sectorGear: "body" must be a Shape');
|
|
49174
|
+
}
|
|
49175
|
+
const spurMeta = buildSpurGearMeta(normalized);
|
|
49176
|
+
const pitchStepDeg = 360 / normalized.teethOnFullCircle;
|
|
49177
|
+
const activeAngleStartDeg = normalized.firstTooth * pitchStepDeg - pitchStepDeg * 0.5;
|
|
49178
|
+
const activeAngleEndDeg = (normalized.firstTooth + normalized.toothCount - 1) * pitchStepDeg + pitchStepDeg * 0.5;
|
|
49179
|
+
const meta2 = {
|
|
49180
|
+
...spurMeta,
|
|
49181
|
+
kind: "sector",
|
|
49182
|
+
teethOnFullCircle: normalized.teethOnFullCircle,
|
|
49183
|
+
firstTooth: normalized.firstTooth,
|
|
49184
|
+
toothCount: normalized.toothCount,
|
|
49185
|
+
activeAngleStartDeg,
|
|
49186
|
+
activeAngleEndDeg
|
|
49187
|
+
};
|
|
49188
|
+
const wheel = driveWheel({ body: options.body, faceWidth: normalized.faceWidth, boreDiameter: normalized.boreDiameter }).addSpurTeethBetween({
|
|
49189
|
+
name: "teeth",
|
|
49190
|
+
module: normalized.module,
|
|
49191
|
+
teethOnFullCircle: normalized.teethOnFullCircle,
|
|
49192
|
+
toothCount: normalized.toothCount,
|
|
49193
|
+
firstTooth: normalized.firstTooth,
|
|
49194
|
+
pressureAngleDeg: normalized.pressureAngleDeg,
|
|
49195
|
+
faceWidth: normalized.faceWidth,
|
|
49196
|
+
backlash: normalized.backlash,
|
|
49197
|
+
clearance: normalized.clearance,
|
|
49198
|
+
addendum: normalized.addendum,
|
|
49199
|
+
dedendum: normalized.dedendum,
|
|
49200
|
+
segmentsPerTooth: normalized.segmentsPerTooth
|
|
49201
|
+
}).build();
|
|
49202
|
+
return attachGearMeta(wheel, meta2);
|
|
49203
|
+
}
|
|
47622
49204
|
function normalizeSideGearOptions(options) {
|
|
47623
49205
|
let normalizedSpur;
|
|
47624
49206
|
try {
|
|
@@ -48598,6 +50180,12 @@ function boltPattern(options) {
|
|
|
48598
50180
|
}
|
|
48599
50181
|
};
|
|
48600
50182
|
}
|
|
50183
|
+
const gearBodies = {
|
|
50184
|
+
disk: gearBodyDisk,
|
|
50185
|
+
diskWithHub: gearBodyDiskWithHub,
|
|
50186
|
+
spoked: gearBodySpoked,
|
|
50187
|
+
fromProfile: gearBodyFromProfile
|
|
50188
|
+
};
|
|
48601
50189
|
function thread(diameter, pitch, length4, options) {
|
|
48602
50190
|
const r = diameter / 2;
|
|
48603
50191
|
const depth = (options == null ? void 0 : options.depth) ?? pitch * 0.35;
|
|
@@ -48763,7 +50351,23 @@ const partLibrary = {
|
|
|
48763
50351
|
gearRatio,
|
|
48764
50352
|
rackRatio,
|
|
48765
50353
|
planetaryRatio,
|
|
48766
|
-
boltPattern
|
|
50354
|
+
boltPattern,
|
|
50355
|
+
/** Start a composable exceptional gear or drive wheel. */
|
|
50356
|
+
driveWheel,
|
|
50357
|
+
/** Read functional-region metadata from a drive wheel shape. */
|
|
50358
|
+
readDriveWheelMeta,
|
|
50359
|
+
/** Involute sector gear with teeth on only part of the pitch circle. */
|
|
50360
|
+
sectorGear,
|
|
50361
|
+
/** Gear body preset namespace: disk, diskWithHub, spoked, and fromProfile. */
|
|
50362
|
+
gearBodies,
|
|
50363
|
+
/** Solid disk/ring gear body, independent from any tooth geometry. */
|
|
50364
|
+
gearBodyDisk,
|
|
50365
|
+
/** Disk gear body with a raised center hub. */
|
|
50366
|
+
gearBodyDiskWithHub,
|
|
50367
|
+
/** Spoked gear body with an outer rim, center hub, and radial spokes. */
|
|
50368
|
+
gearBodySpoked,
|
|
50369
|
+
/** Extrude a custom 2D profile into a gear body. */
|
|
50370
|
+
gearBodyFromProfile
|
|
48767
50371
|
};
|
|
48768
50372
|
/**
|
|
48769
50373
|
* @license
|
|
@@ -57266,7 +58870,7 @@ function requireFinite$7(value, label) {
|
|
|
57266
58870
|
}
|
|
57267
58871
|
return value;
|
|
57268
58872
|
}
|
|
57269
|
-
function requireVec3$
|
|
58873
|
+
function requireVec3$3(value, label) {
|
|
57270
58874
|
if (!Array.isArray(value) || value.length !== 3) {
|
|
57271
58875
|
throw new Error(`${label} must be [x, y, z]`);
|
|
57272
58876
|
}
|
|
@@ -57310,7 +58914,7 @@ function normalizeOptions(options) {
|
|
|
57310
58914
|
out.size = requireFinite$7(options.size, "Viewport.label options.size");
|
|
57311
58915
|
if (out.size <= 0) throw new Error("Viewport.label options.size must be positive");
|
|
57312
58916
|
}
|
|
57313
|
-
if (options.offset !== void 0) out.offset = requireVec3$
|
|
58917
|
+
if (options.offset !== void 0) out.offset = requireVec3$3(options.offset, "Viewport.label options.offset");
|
|
57314
58918
|
if (options.anchor !== void 0) {
|
|
57315
58919
|
if (!VALID_ANCHORS.has(options.anchor)) {
|
|
57316
58920
|
throw new Error(`Viewport.label options.anchor must be one of: ${Array.from(VALID_ANCHORS).join(", ")}`);
|
|
@@ -57327,7 +58931,7 @@ function collectRenderLabel(text, at, options) {
|
|
|
57327
58931
|
if (typeof text !== "string" || text.trim().length === 0) {
|
|
57328
58932
|
throw new Error("Viewport.label text must be a non-empty string");
|
|
57329
58933
|
}
|
|
57330
|
-
const normalizedAt = requireVec3$
|
|
58934
|
+
const normalizedAt = requireVec3$3(at, "Viewport.label at");
|
|
57331
58935
|
const normalizedOptions = normalizeOptions(options);
|
|
57332
58936
|
_collected$4.push({
|
|
57333
58937
|
id: `render-label-${_nextId++}`,
|
|
@@ -57522,7 +59126,7 @@ function requireFinite$6(value, label) {
|
|
|
57522
59126
|
}
|
|
57523
59127
|
return value;
|
|
57524
59128
|
}
|
|
57525
|
-
function requireVec3$
|
|
59129
|
+
function requireVec3$2(value, label) {
|
|
57526
59130
|
if (!Array.isArray(value) || value.length !== 3) {
|
|
57527
59131
|
throw new Error(`${label} must be [x, y, z]`);
|
|
57528
59132
|
}
|
|
@@ -57550,9 +59154,9 @@ const VALID_ENVIRONMENT_PRESETS = /* @__PURE__ */ new Set([
|
|
|
57550
59154
|
]);
|
|
57551
59155
|
function validateCamera(cam, label) {
|
|
57552
59156
|
const out = {};
|
|
57553
|
-
if (cam.position !== void 0) out.position = requireVec3$
|
|
57554
|
-
if (cam.target !== void 0) out.target = requireVec3$
|
|
57555
|
-
if (cam.up !== void 0) out.up = requireVec3$
|
|
59157
|
+
if (cam.position !== void 0) out.position = requireVec3$2(cam.position, `${label}.position`);
|
|
59158
|
+
if (cam.target !== void 0) out.target = requireVec3$2(cam.target, `${label}.target`);
|
|
59159
|
+
if (cam.up !== void 0) out.up = requireVec3$2(cam.up, `${label}.up`);
|
|
57556
59160
|
if (cam.fov !== void 0) {
|
|
57557
59161
|
out.fov = requireFinite$6(cam.fov, `${label}.fov`);
|
|
57558
59162
|
if (out.fov <= 0 || out.fov >= 180) throw new Error(`${label}.fov must be between 0 and 180`);
|
|
@@ -57687,8 +59291,8 @@ function validateLight(light, label) {
|
|
|
57687
59291
|
const out = { type: light.type };
|
|
57688
59292
|
if (light.color !== void 0) out.color = requireColor(light.color, `${label}.color`);
|
|
57689
59293
|
if (light.intensity !== void 0) out.intensity = requireFinite$6(light.intensity, `${label}.intensity`);
|
|
57690
|
-
if (light.position !== void 0) out.position = requireVec3$
|
|
57691
|
-
if (light.target !== void 0) out.target = requireVec3$
|
|
59294
|
+
if (light.position !== void 0) out.position = requireVec3$2(light.position, `${label}.position`);
|
|
59295
|
+
if (light.target !== void 0) out.target = requireVec3$2(light.target, `${label}.target`);
|
|
57692
59296
|
if (light.groundColor !== void 0) out.groundColor = requireColor(light.groundColor, `${label}.groundColor`);
|
|
57693
59297
|
if (light.skyColor !== void 0) out.skyColor = requireColor(light.skyColor, `${label}.skyColor`);
|
|
57694
59298
|
if (light.angle !== void 0) out.angle = requireFinite$6(light.angle, `${label}.angle`);
|
|
@@ -58234,7 +59838,7 @@ class ProductStationBuilder {
|
|
|
58234
59838
|
this.profileValue = profileFromSketch(sketch, "custom", width, depth);
|
|
58235
59839
|
return this;
|
|
58236
59840
|
}
|
|
58237
|
-
/**
|
|
59841
|
+
/** Set the station crown amount for soft product-section intent. */
|
|
58238
59842
|
crown(amount) {
|
|
58239
59843
|
if (!Number.isFinite(amount)) throw new Error("station.crown(amount) requires a finite number");
|
|
58240
59844
|
this.crownValue = amount;
|
|
@@ -59220,7 +60824,7 @@ function scale$1(v, s) {
|
|
|
59220
60824
|
function dot$2(a2, b) {
|
|
59221
60825
|
return a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
|
|
59222
60826
|
}
|
|
59223
|
-
function lerp$
|
|
60827
|
+
function lerp$4(a2, b, t) {
|
|
59224
60828
|
return a2 + (b - a2) * t;
|
|
59225
60829
|
}
|
|
59226
60830
|
function frameMatrix$1(x2, y2, z2, p2) {
|
|
@@ -59231,7 +60835,7 @@ function axisVector(axis, sign2 = 1) {
|
|
|
59231
60835
|
if (axis === "Y") return [0, sign2, 0];
|
|
59232
60836
|
return [0, 0, sign2];
|
|
59233
60837
|
}
|
|
59234
|
-
function axisPosition(axis, point2) {
|
|
60838
|
+
function axisPosition$1(axis, point2) {
|
|
59235
60839
|
return point2[AXIS_INDEX[axis]];
|
|
59236
60840
|
}
|
|
59237
60841
|
function crossPointForStation(axis, point2) {
|
|
@@ -59239,7 +60843,7 @@ function crossPointForStation(axis, point2) {
|
|
|
59239
60843
|
if (axis === "Y") return [point2[0], -point2[2]];
|
|
59240
60844
|
return [point2[1], point2[2]];
|
|
59241
60845
|
}
|
|
59242
|
-
function orientLoftToAxis(shape, axis) {
|
|
60846
|
+
function orientLoftToAxis$1(shape, axis) {
|
|
59243
60847
|
if (axis === "Z") return shape;
|
|
59244
60848
|
if (axis === "Y") return shape.rotateX(-90);
|
|
59245
60849
|
return shape.transform([0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1]);
|
|
@@ -59296,9 +60900,9 @@ function interpolateQuery(a2, b, t) {
|
|
|
59296
60900
|
}
|
|
59297
60901
|
return {
|
|
59298
60902
|
side: sideA,
|
|
59299
|
-
u: lerp$
|
|
59300
|
-
v: lerp$
|
|
59301
|
-
offset: lerp$
|
|
60903
|
+
u: lerp$4(a2.u ?? 0.5, b.u ?? 0.5, t),
|
|
60904
|
+
v: lerp$4(a2.v ?? 0.5, b.v ?? 0.5, t),
|
|
60905
|
+
offset: lerp$4(a2.offset ?? 0, b.offset ?? 0, t)
|
|
59302
60906
|
};
|
|
59303
60907
|
}
|
|
59304
60908
|
function resolvePathQueries(points) {
|
|
@@ -59365,8 +60969,8 @@ class ProductSkin {
|
|
|
59365
60969
|
this.stations = stations;
|
|
59366
60970
|
this.rails = rails;
|
|
59367
60971
|
for (const [name2, query] of Object.entries(refs)) this.refQueries.set(name2, cloneQuery(query));
|
|
59368
|
-
this.axisMin = Math.min(...stations.map((station) => axisPosition(axis, station.center)));
|
|
59369
|
-
this.axisMax = Math.max(...stations.map((station) => axisPosition(axis, station.center)));
|
|
60972
|
+
this.axisMin = Math.min(...stations.map((station) => axisPosition$1(axis, station.center)));
|
|
60973
|
+
this.axisMax = Math.max(...stations.map((station) => axisPosition$1(axis, station.center)));
|
|
59370
60974
|
this.diagnosticsValue = {
|
|
59371
60975
|
...diagnostics,
|
|
59372
60976
|
stationNames: stations.map((station) => station.name),
|
|
@@ -59423,24 +61027,24 @@ class ProductSkin {
|
|
|
59423
61027
|
}
|
|
59424
61028
|
/** Interpolate center, width, and depth at a normalized v or absolute axis value. */
|
|
59425
61029
|
stationAt(vOrAxis) {
|
|
59426
|
-
const axisValue = vOrAxis >= 0 && vOrAxis <= 1 ? lerp$
|
|
61030
|
+
const axisValue = vOrAxis >= 0 && vOrAxis <= 1 ? lerp$4(this.axisMin, this.axisMax, vOrAxis) : clamp$6(vOrAxis, this.axisMin, this.axisMax);
|
|
59427
61031
|
const sorted = this.stations;
|
|
59428
61032
|
for (let index2 = 0; index2 < sorted.length - 1; index2 += 1) {
|
|
59429
61033
|
const a2 = sorted[index2];
|
|
59430
61034
|
const b = sorted[index2 + 1];
|
|
59431
|
-
const aAxis = axisPosition(this.axis, a2.center);
|
|
59432
|
-
const bAxis = axisPosition(this.axis, b.center);
|
|
61035
|
+
const aAxis = axisPosition$1(this.axis, a2.center);
|
|
61036
|
+
const bAxis = axisPosition$1(this.axis, b.center);
|
|
59433
61037
|
if (axisValue < aAxis - EPS$5 || axisValue > bAxis + EPS$5) continue;
|
|
59434
61038
|
const span = Math.max(EPS$5, bAxis - aAxis);
|
|
59435
61039
|
const t = clamp$6((axisValue - aAxis) / span, 0, 1);
|
|
59436
61040
|
return {
|
|
59437
61041
|
axisValue,
|
|
59438
|
-
center: [lerp$
|
|
59439
|
-
width: lerp$
|
|
59440
|
-
depth: lerp$
|
|
61042
|
+
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)],
|
|
61043
|
+
width: lerp$4(a2.profile.width, b.profile.width, t),
|
|
61044
|
+
depth: lerp$4(a2.profile.depth, b.profile.depth, t),
|
|
59441
61045
|
dWidth: (b.profile.width - a2.profile.width) / span,
|
|
59442
61046
|
dDepth: (b.profile.depth - a2.profile.depth) / span,
|
|
59443
|
-
exponent: lerp$
|
|
61047
|
+
exponent: lerp$4(profileExponent(a2), profileExponent(b), t),
|
|
59444
61048
|
kind: a2.profile.kind === b.profile.kind ? a2.profile.kind : "custom"
|
|
59445
61049
|
};
|
|
59446
61050
|
}
|
|
@@ -59562,10 +61166,10 @@ class ProductSkinBuilder {
|
|
|
59562
61166
|
}
|
|
59563
61167
|
/** Set named cross-section stations for the product skin. */
|
|
59564
61168
|
stations(stations) {
|
|
59565
|
-
this.stationsValue = stations.map(toStationSpec).sort((a2, b) => axisPosition(this.axisValue, a2.center) - axisPosition(this.axisValue, b.center));
|
|
61169
|
+
this.stationsValue = stations.map(toStationSpec).sort((a2, b) => axisPosition$1(this.axisValue, a2.center) - axisPosition$1(this.axisValue, b.center));
|
|
59566
61170
|
return this;
|
|
59567
61171
|
}
|
|
59568
|
-
/** Attach guide rails
|
|
61172
|
+
/** Attach named guide rails for product-skin construction and downstream surface references. */
|
|
59569
61173
|
rails(rails) {
|
|
59570
61174
|
this.railsValue = { ...rails };
|
|
59571
61175
|
return this;
|
|
@@ -59599,7 +61203,7 @@ class ProductSkinBuilder {
|
|
|
59599
61203
|
this.edgeLengthValue = value;
|
|
59600
61204
|
return this;
|
|
59601
61205
|
}
|
|
59602
|
-
/**
|
|
61206
|
+
/** Record intended wall thickness for product design metadata. Use explicit shelling when the model needs real inner-wall geometry. */
|
|
59603
61207
|
wall(thickness) {
|
|
59604
61208
|
if (!Number.isFinite(thickness) || thickness <= 0) throw new Error("Product.skin().wall(thickness) requires a positive finite number");
|
|
59605
61209
|
this.wallValue = thickness;
|
|
@@ -59612,9 +61216,9 @@ class ProductSkinBuilder {
|
|
|
59612
61216
|
const [x2, y2] = crossPointForStation(this.axisValue, station.center);
|
|
59613
61217
|
return station.profile.sketch.translate(x2, y2);
|
|
59614
61218
|
});
|
|
59615
|
-
const heights = this.stationsValue.map((station) => axisPosition(this.axisValue, station.center));
|
|
61219
|
+
const heights = this.stationsValue.map((station) => axisPosition$1(this.axisValue, station.center));
|
|
59616
61220
|
let shape = loft(localProfiles, heights, { edgeLength: this.edgeLengthValue });
|
|
59617
|
-
shape = orientLoftToAxis(shape, this.axisValue);
|
|
61221
|
+
shape = orientLoftToAxis$1(shape, this.axisValue);
|
|
59618
61222
|
if (this.colorValue) shape = shape.color(this.colorValue);
|
|
59619
61223
|
shape = applyMaterial(shape, this.materialValue).as(this.name);
|
|
59620
61224
|
const warnings = [];
|
|
@@ -60273,7 +61877,7 @@ function requirePositive$3(value, label) {
|
|
|
60273
61877
|
function clamp$5(value, min2, max2) {
|
|
60274
61878
|
return Math.max(min2, Math.min(max2, value));
|
|
60275
61879
|
}
|
|
60276
|
-
function lerp$
|
|
61880
|
+
function lerp$3(a2, b, t) {
|
|
60277
61881
|
return a2 + (b - a2) * t;
|
|
60278
61882
|
}
|
|
60279
61883
|
function add(a2, b) {
|
|
@@ -60323,19 +61927,19 @@ function transformLocal(point2, tangentAcross, normal, tangentAlong, x2, y2, z2
|
|
|
60323
61927
|
function interpolateCylinder(a2, b, t, mode) {
|
|
60324
61928
|
let delta = b.angle - a2.angle;
|
|
60325
61929
|
if (mode === "shortest" && Math.abs(delta) > 180) delta -= Math.sign(delta) * 360;
|
|
60326
|
-
return { kind: "cylinder", angle: a2.angle + delta * t, z: lerp$
|
|
61930
|
+
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) };
|
|
60327
61931
|
}
|
|
60328
61932
|
function interpolatePlane(a2, b, t) {
|
|
60329
|
-
return { kind: "plane", x: lerp$
|
|
61933
|
+
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) };
|
|
60330
61934
|
}
|
|
60331
61935
|
function interpolateProductSkin(a2, b, t) {
|
|
60332
61936
|
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.");
|
|
60333
61937
|
return {
|
|
60334
61938
|
kind: "productSkin",
|
|
60335
61939
|
side: a2.side ?? b.side,
|
|
60336
|
-
u: lerp$
|
|
60337
|
-
v: lerp$
|
|
60338
|
-
offset: lerp$
|
|
61940
|
+
u: lerp$3(a2.u ?? 0.5, b.u ?? 0.5, t),
|
|
61941
|
+
v: lerp$3(a2.v ?? 0.5, b.v ?? 0.5, t),
|
|
61942
|
+
offset: lerp$3(a2.offset ?? 0, b.offset ?? 0, t)
|
|
60339
61943
|
};
|
|
60340
61944
|
}
|
|
60341
61945
|
class SurfacePath {
|
|
@@ -60658,11 +62262,11 @@ function coordinateOnSide(coordinate, side, label) {
|
|
|
60658
62262
|
return { ...coordinate, kind: "productSkin", side };
|
|
60659
62263
|
}
|
|
60660
62264
|
class ProductSkinCarrier {
|
|
60661
|
-
constructor(skin, name = skin.name,
|
|
62265
|
+
constructor(skin, name = skin.name, sideValue2, offsetValue = 0) {
|
|
60662
62266
|
__publicField(this, "kind", "productSkin");
|
|
60663
62267
|
this.skin = skin;
|
|
60664
62268
|
this.name = name;
|
|
60665
|
-
this.sideValue =
|
|
62269
|
+
this.sideValue = sideValue2;
|
|
60666
62270
|
this.offsetValue = offsetValue;
|
|
60667
62271
|
}
|
|
60668
62272
|
surface(side) {
|
|
@@ -61433,7 +63037,7 @@ function counterboresForPlate(spec2, width, height, thickness, diagnostics) {
|
|
|
61433
63037
|
function minWidthAcrossAlongRange(widthAtT, length4, minAlong, maxAlong) {
|
|
61434
63038
|
let minWidth = Number.POSITIVE_INFINITY;
|
|
61435
63039
|
for (let index2 = 0; index2 <= 8; index2 += 1) {
|
|
61436
|
-
const along = lerp$
|
|
63040
|
+
const along = lerp$3(minAlong, maxAlong, index2 / 8);
|
|
61437
63041
|
const t = Math.max(0, Math.min(1, (along + length4 / 2) / Math.max(length4, 1e-8)));
|
|
61438
63042
|
minWidth = Math.min(minWidth, widthAtT(t));
|
|
61439
63043
|
}
|
|
@@ -61733,7 +63337,7 @@ function pathParameterAtDistance(samples, distance2) {
|
|
|
61733
63337
|
const segmentLength = Math.hypot(b.point[0] - a2.point[0], b.point[1] - a2.point[1], b.point[2] - a2.point[2]);
|
|
61734
63338
|
if (traveled + segmentLength >= distance2) {
|
|
61735
63339
|
const localT = segmentLength <= 1e-8 ? 0 : (distance2 - traveled) / segmentLength;
|
|
61736
|
-
return lerp$
|
|
63340
|
+
return lerp$3(a2.t, b.t, localT);
|
|
61737
63341
|
}
|
|
61738
63342
|
traveled += segmentLength;
|
|
61739
63343
|
}
|
|
@@ -61786,7 +63390,7 @@ function compileBandFootprintMesh(path2, input) {
|
|
|
61786
63390
|
const width = input.widthAt(t);
|
|
61787
63391
|
const along = distance2 - length4 / 2;
|
|
61788
63392
|
for (let acrossIndex = 0; acrossIndex <= acrossSegments; acrossIndex += 1) {
|
|
61789
|
-
const across = lerp$
|
|
63393
|
+
const across = lerp$3(-width / 2, width / 2, acrossIndex / acrossSegments);
|
|
61790
63394
|
mesh.vertices.push(pointAtProfile([across, along], false));
|
|
61791
63395
|
}
|
|
61792
63396
|
}
|
|
@@ -61796,7 +63400,7 @@ function compileBandFootprintMesh(path2, input) {
|
|
|
61796
63400
|
const width = input.widthAt(t);
|
|
61797
63401
|
const along = distance2 - length4 / 2;
|
|
61798
63402
|
for (let acrossIndex = 0; acrossIndex <= acrossSegments; acrossIndex += 1) {
|
|
61799
|
-
const across = lerp$
|
|
63403
|
+
const across = lerp$3(-width / 2, width / 2, acrossIndex / acrossSegments);
|
|
61800
63404
|
mesh.vertices.push(pointAtProfile([across, along], true));
|
|
61801
63405
|
}
|
|
61802
63406
|
}
|
|
@@ -61808,7 +63412,7 @@ function compileBandFootprintMesh(path2, input) {
|
|
|
61808
63412
|
const width = input.widthAt(t);
|
|
61809
63413
|
const along = distance2 - length4 / 2;
|
|
61810
63414
|
for (let acrossIndex = 0; acrossIndex < acrossSegments; acrossIndex += 1) {
|
|
61811
|
-
const across = lerp$
|
|
63415
|
+
const across = lerp$3(-width / 2, width / 2, (acrossIndex + 0.5) / acrossSegments);
|
|
61812
63416
|
filled[alongIndex][acrossIndex] = !holes.some((hole2) => pointInProfileLoop([across, along], hole2));
|
|
61813
63417
|
}
|
|
61814
63418
|
}
|
|
@@ -62741,7 +64345,7 @@ class SurfaceMemberBuilder {
|
|
|
62741
64345
|
this.record.features.push({ ...normalizeFeature(name, feature), type: "counterbore" });
|
|
62742
64346
|
return this;
|
|
62743
64347
|
}
|
|
62744
|
-
/** Add a named anchor at a carrier surface coordinate for
|
|
64348
|
+
/** Add a named anchor at a carrier surface coordinate for explicit member joins. */
|
|
62745
64349
|
anchorAt(name, coordinate) {
|
|
62746
64350
|
if (!name.trim()) throw new Error("SurfaceMemberBuilder.anchorAt(name, coordinate) requires a non-empty name");
|
|
62747
64351
|
const explicitAnchors = this.record.spec.explicitAnchors ?? [];
|
|
@@ -65704,7 +67308,7 @@ const Constraint = {
|
|
|
65704
67308
|
return builder.constrain({ type: "length", line: resolveLineId(builder, line2), value });
|
|
65705
67309
|
}
|
|
65706
67310
|
};
|
|
65707
|
-
function requireVec3(v, label) {
|
|
67311
|
+
function requireVec3$1(v, label) {
|
|
65708
67312
|
if (!Array.isArray(v) || v.length !== 3 || !Number.isFinite(v[0]) || !Number.isFinite(v[1]) || !Number.isFinite(v[2])) {
|
|
65709
67313
|
throw new Error(`${label} must be a [number, number, number] with finite values, got ${JSON.stringify(v)}`);
|
|
65710
67314
|
}
|
|
@@ -65717,24 +67321,24 @@ function requireFiniteNumber(n, label) {
|
|
|
65717
67321
|
return n;
|
|
65718
67322
|
}
|
|
65719
67323
|
function distance$1(a2, b) {
|
|
65720
|
-
requireVec3(a2, "a");
|
|
65721
|
-
requireVec3(b, "b");
|
|
67324
|
+
requireVec3$1(a2, "a");
|
|
67325
|
+
requireVec3$1(b, "b");
|
|
65722
67326
|
return Math.hypot(b[0] - a2[0], b[1] - a2[1], b[2] - a2[2]);
|
|
65723
67327
|
}
|
|
65724
67328
|
function midpoint$1(a2, b) {
|
|
65725
|
-
requireVec3(a2, "a");
|
|
65726
|
-
requireVec3(b, "b");
|
|
67329
|
+
requireVec3$1(a2, "a");
|
|
67330
|
+
requireVec3$1(b, "b");
|
|
65727
67331
|
return [(a2[0] + b[0]) / 2, (a2[1] + b[1]) / 2, (a2[2] + b[2]) / 2];
|
|
65728
67332
|
}
|
|
65729
|
-
function lerp(a2, b, t) {
|
|
65730
|
-
requireVec3(a2, "a");
|
|
65731
|
-
requireVec3(b, "b");
|
|
67333
|
+
function lerp$2(a2, b, t) {
|
|
67334
|
+
requireVec3$1(a2, "a");
|
|
67335
|
+
requireVec3$1(b, "b");
|
|
65732
67336
|
requireFiniteNumber(t, "t");
|
|
65733
67337
|
return [a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t];
|
|
65734
67338
|
}
|
|
65735
67339
|
function direction(a2, b) {
|
|
65736
|
-
requireVec3(a2, "a");
|
|
65737
|
-
requireVec3(b, "b");
|
|
67340
|
+
requireVec3$1(a2, "a");
|
|
67341
|
+
requireVec3$1(b, "b");
|
|
65738
67342
|
const dx = b[0] - a2[0];
|
|
65739
67343
|
const dy = b[1] - a2[1];
|
|
65740
67344
|
const dz = b[2] - a2[2];
|
|
@@ -65745,8 +67349,8 @@ function direction(a2, b) {
|
|
|
65745
67349
|
return [dx / len2, dy / len2, dz / len2];
|
|
65746
67350
|
}
|
|
65747
67351
|
function offset(point2, dir, amount) {
|
|
65748
|
-
requireVec3(point2, "point");
|
|
65749
|
-
requireVec3(dir, "dir");
|
|
67352
|
+
requireVec3$1(point2, "point");
|
|
67353
|
+
requireVec3$1(dir, "dir");
|
|
65750
67354
|
requireFiniteNumber(amount, "amount");
|
|
65751
67355
|
return [point2[0] + dir[0] * amount, point2[1] + dir[1] * amount, point2[2] + dir[2] * amount];
|
|
65752
67356
|
}
|
|
@@ -65756,7 +67360,7 @@ const Points = {
|
|
|
65756
67360
|
/** Center point between two 3D points. */
|
|
65757
67361
|
midpoint: midpoint$1,
|
|
65758
67362
|
/** Linearly interpolate between two 3D points. t=0 returns a, t=1 returns b. */
|
|
65759
|
-
lerp,
|
|
67363
|
+
lerp: lerp$2,
|
|
65760
67364
|
/** Unit direction vector from a to b. Throws if a and b are the same point. */
|
|
65761
67365
|
direction,
|
|
65762
67366
|
/** Move a point along a direction vector by a given amount. */
|
|
@@ -70888,9 +72492,84 @@ class ConstraintSketch extends Sketch {
|
|
|
70888
72492
|
* Select the single arrangement region that contains the given seed point.
|
|
70889
72493
|
* Throws if no region contains the seed.
|
|
70890
72494
|
*/
|
|
70891
|
-
detectArrangementRegion(
|
|
72495
|
+
detectArrangementRegion(_seed) {
|
|
70892
72496
|
throw new Error("Not implemented");
|
|
70893
72497
|
}
|
|
72498
|
+
/**
|
|
72499
|
+
* Return the solved constrained path as a sampled 2D polyline.
|
|
72500
|
+
*
|
|
72501
|
+
* Use this when a construction rail was authored with `constrainedSketch()`
|
|
72502
|
+
* and should feed another operation such as `Loft.pathOnXz(...)`.
|
|
72503
|
+
* The sketch must contain exactly one profile path.
|
|
72504
|
+
*
|
|
72505
|
+
* @param samples - Samples per curved segment. Default 32.
|
|
72506
|
+
* @returns The solved path as an open polyline.
|
|
72507
|
+
*/
|
|
72508
|
+
toPolyline(samples = 32) {
|
|
72509
|
+
if (!Number.isFinite(samples) || samples < 2) throw new Error("ConstraintSketch.toPolyline() samples must be at least 2");
|
|
72510
|
+
const profileLoops = this.definition.loops.filter((loop) => loop.type === "profile");
|
|
72511
|
+
if (profileLoops.length !== 1) {
|
|
72512
|
+
throw new Error("ConstraintSketch.toPolyline() requires exactly one profile path");
|
|
72513
|
+
}
|
|
72514
|
+
const sampleCount = Math.max(2, Math.round(samples));
|
|
72515
|
+
const pointMap = new Map(this.definition.points.map((point2) => [point2.id, point2]));
|
|
72516
|
+
const lineMap = new Map(this.definition.lines.map((line2) => [line2.id, line2]));
|
|
72517
|
+
const arcMap = new Map(this.definition.arcs.map((arc) => [arc.id, arc]));
|
|
72518
|
+
const bezierMap = new Map(this.definition.beziers.map((bezier) => [bezier.id, bezier]));
|
|
72519
|
+
const points = [];
|
|
72520
|
+
const appendStart = (point2, label) => {
|
|
72521
|
+
const previous = points[points.length - 1];
|
|
72522
|
+
if (!previous) {
|
|
72523
|
+
points.push(point2);
|
|
72524
|
+
return;
|
|
72525
|
+
}
|
|
72526
|
+
if (Math.hypot(point2[0] - previous[0], point2[1] - previous[1]) > 1e-6) {
|
|
72527
|
+
throw new Error(`ConstraintSketch.toPolyline() profile path is not continuous at ${label}`);
|
|
72528
|
+
}
|
|
72529
|
+
};
|
|
72530
|
+
const appendPoint = (point2) => {
|
|
72531
|
+
const previous = points[points.length - 1];
|
|
72532
|
+
if (!previous || Math.hypot(point2[0] - previous[0], point2[1] - previous[1]) > 1e-9) points.push(point2);
|
|
72533
|
+
};
|
|
72534
|
+
const requirePoint = (id, label) => {
|
|
72535
|
+
const point2 = pointMap.get(id);
|
|
72536
|
+
if (!point2) throw new Error(`ConstraintSketch.toPolyline() missing ${label}`);
|
|
72537
|
+
return [point2.x, point2.y];
|
|
72538
|
+
};
|
|
72539
|
+
for (const segment of profileLoops[0].segments) {
|
|
72540
|
+
if (segment.kind === "line") {
|
|
72541
|
+
const line2 = lineMap.get(segment.line);
|
|
72542
|
+
if (!line2) throw new Error(`ConstraintSketch.toPolyline() missing line "${segment.line}"`);
|
|
72543
|
+
appendStart(requirePoint(line2.a, `line "${segment.line}" start point`), `line "${segment.line}"`);
|
|
72544
|
+
appendPoint(requirePoint(line2.b, `line "${segment.line}" end point`));
|
|
72545
|
+
} else if (segment.kind === "arc") {
|
|
72546
|
+
const arc = arcMap.get(segment.arc);
|
|
72547
|
+
if (!arc) throw new Error(`ConstraintSketch.toPolyline() missing arc "${segment.arc}"`);
|
|
72548
|
+
const center = requirePoint(arc.center, `arc "${segment.arc}" center point`);
|
|
72549
|
+
const start = requirePoint(arc.start, `arc "${segment.arc}" start point`);
|
|
72550
|
+
const end = requirePoint(arc.end, `arc "${segment.arc}" end point`);
|
|
72551
|
+
appendStart(start, `arc "${segment.arc}"`);
|
|
72552
|
+
const startAngle = Math.atan2(start[1] - center[1], start[0] - center[0]);
|
|
72553
|
+
const endAngle = Math.atan2(end[1] - center[1], end[0] - center[0]);
|
|
72554
|
+
for (const point2 of tessellateArc(center[0], center[1], arc.radius, startAngle, endAngle, arc.clockwise, sampleCount)) {
|
|
72555
|
+
appendPoint(point2);
|
|
72556
|
+
}
|
|
72557
|
+
} else {
|
|
72558
|
+
const bezier = bezierMap.get(segment.bezier);
|
|
72559
|
+
if (!bezier) throw new Error(`ConstraintSketch.toPolyline() missing bezier "${segment.bezier}"`);
|
|
72560
|
+
const p0 = requirePoint(bezier.p0, `bezier "${segment.bezier}" start point`);
|
|
72561
|
+
const p1 = requirePoint(bezier.p1, `bezier "${segment.bezier}" first control point`);
|
|
72562
|
+
const p2 = requirePoint(bezier.p2, `bezier "${segment.bezier}" second control point`);
|
|
72563
|
+
const p3 = requirePoint(bezier.p3, `bezier "${segment.bezier}" end point`);
|
|
72564
|
+
appendStart(p0, `bezier "${segment.bezier}"`);
|
|
72565
|
+
for (const point2 of tessellateBezier(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], sampleCount)) {
|
|
72566
|
+
appendPoint(point2);
|
|
72567
|
+
}
|
|
72568
|
+
}
|
|
72569
|
+
}
|
|
72570
|
+
if (points.length < 2) throw new Error("ConstraintSketch.toPolyline() needs at least 2 points");
|
|
72571
|
+
return points;
|
|
72572
|
+
}
|
|
70894
72573
|
/**
|
|
70895
72574
|
* Re-solve the sketch after changing the value of one existing constraint.
|
|
70896
72575
|
*
|
|
@@ -74017,8 +75696,8 @@ tinf_build_bits_base(dist_bits, dist_base, 2, 1);
|
|
|
74017
75696
|
length_bits[28] = 0;
|
|
74018
75697
|
length_base[28] = 258;
|
|
74019
75698
|
var tinyInflate = tinf_uncompress;
|
|
74020
|
-
function derive(v0, v1, v2,
|
|
74021
|
-
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) *
|
|
75699
|
+
function derive(v0, v1, v2, v32, t) {
|
|
75700
|
+
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;
|
|
74022
75701
|
}
|
|
74023
75702
|
function BoundingBox() {
|
|
74024
75703
|
this.x1 = Number.NaN;
|
|
@@ -86175,6 +87854,295 @@ function polygonVertices(sides, radius, options) {
|
|
|
86175
87854
|
centerY: options == null ? void 0 : options.centerY
|
|
86176
87855
|
});
|
|
86177
87856
|
}
|
|
87857
|
+
const LOFT_GUIDE_EPS = 1e-8;
|
|
87858
|
+
function orientLoftToAxis(shape, axis) {
|
|
87859
|
+
if (axis === "Z") return shape;
|
|
87860
|
+
if (axis === "Y") return shape.rotateX(-90);
|
|
87861
|
+
return shape.transform([0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1]);
|
|
87862
|
+
}
|
|
87863
|
+
function buildRailEvaluators(rails, axis, start, end, railSamples) {
|
|
87864
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87865
|
+
return rails.map((rail2) => {
|
|
87866
|
+
if (seen.has(rail2.side)) throw new Error(`Loft.withGuideRails() received more than one ${rail2.side} rail`);
|
|
87867
|
+
seen.add(rail2.side);
|
|
87868
|
+
const sampled = sampleRailPath(rail2.path, railSamples);
|
|
87869
|
+
if (sampled.length < 2) throw new Error("Loft guide rails require at least two points");
|
|
87870
|
+
const points = sampled.map((point2) => ({ position: axisPosition(axis, point2), cross: crossPointForAxis(axis, point2) }));
|
|
87871
|
+
const ordered = points[points.length - 1].position >= points[0].position ? points : [...points].reverse();
|
|
87872
|
+
validateRailCoverage(ordered, start, end);
|
|
87873
|
+
return { side: rail2.side, points: ordered };
|
|
87874
|
+
});
|
|
87875
|
+
}
|
|
87876
|
+
function railCrossAt(rail2, position) {
|
|
87877
|
+
const points = rail2.points;
|
|
87878
|
+
if (position <= points[0].position + LOFT_GUIDE_EPS) return points[0].cross;
|
|
87879
|
+
const last = points[points.length - 1];
|
|
87880
|
+
if (position >= last.position - LOFT_GUIDE_EPS) return last.cross;
|
|
87881
|
+
for (let index2 = 0; index2 < points.length - 1; index2 += 1) {
|
|
87882
|
+
const a2 = points[index2];
|
|
87883
|
+
const b = points[index2 + 1];
|
|
87884
|
+
if (position >= a2.position - LOFT_GUIDE_EPS && position <= b.position + LOFT_GUIDE_EPS) {
|
|
87885
|
+
const t = (position - a2.position) / (b.position - a2.position);
|
|
87886
|
+
return [lerp$1(a2.cross[0], b.cross[0], t), lerp$1(a2.cross[1], b.cross[1], t)];
|
|
87887
|
+
}
|
|
87888
|
+
}
|
|
87889
|
+
throw new Error("Loft guide rail does not cover requested station position");
|
|
87890
|
+
}
|
|
87891
|
+
function validateRailCoverage(points, start, end) {
|
|
87892
|
+
for (let index2 = 1; index2 < points.length; index2 += 1) {
|
|
87893
|
+
if (points[index2].position - points[index2 - 1].position < LOFT_GUIDE_EPS) {
|
|
87894
|
+
throw new Error("Loft guide rails must be monotone along the loft axis");
|
|
87895
|
+
}
|
|
87896
|
+
}
|
|
87897
|
+
if (points[0].position - start > LOFT_GUIDE_EPS || end - points[points.length - 1].position > LOFT_GUIDE_EPS) {
|
|
87898
|
+
throw new Error("Loft guide rails must cover the full station range");
|
|
87899
|
+
}
|
|
87900
|
+
}
|
|
87901
|
+
function sampleRailPath(path2, samples) {
|
|
87902
|
+
if (Array.isArray(path2)) return path2.map((point2, index2) => requireVec3(point2, `Loft guide rail point ${index2}`));
|
|
87903
|
+
if (path2 instanceof Curve3D || path2 instanceof HermiteCurve3D || path2 instanceof QuinticHermiteCurve3D || path2 instanceof NurbsCurve3D) {
|
|
87904
|
+
return path2.sample(Math.max(2, Math.round(samples))).map((point2, index2) => requireVec3(point2, `Loft guide rail sample ${index2}`));
|
|
87905
|
+
}
|
|
87906
|
+
throw new Error("Loft guide rail path must be a Vec3[] or ForgeCAD 3D curve");
|
|
87907
|
+
}
|
|
87908
|
+
function requireVec3(point2, label) {
|
|
87909
|
+
if (!Array.isArray(point2) || point2.length !== 3 || !point2.every(Number.isFinite)) {
|
|
87910
|
+
throw new Error(`${label} must be a finite [x, y, z] point`);
|
|
87911
|
+
}
|
|
87912
|
+
return [point2[0], point2[1], point2[2]];
|
|
87913
|
+
}
|
|
87914
|
+
function axisPosition(axis, point2) {
|
|
87915
|
+
if (axis === "X") return point2[0];
|
|
87916
|
+
if (axis === "Y") return point2[1];
|
|
87917
|
+
return point2[2];
|
|
87918
|
+
}
|
|
87919
|
+
function crossPointForAxis(axis, point2) {
|
|
87920
|
+
if (axis === "X") return [point2[1], point2[2]];
|
|
87921
|
+
if (axis === "Y") return [point2[0], -point2[2]];
|
|
87922
|
+
return [point2[0], point2[1]];
|
|
87923
|
+
}
|
|
87924
|
+
function lerp$1(a2, b, t) {
|
|
87925
|
+
return a2 + (b - a2) * t;
|
|
87926
|
+
}
|
|
87927
|
+
function loftWithGuideRails(stations, rails, options = {}) {
|
|
87928
|
+
if (stations.length < 2) throw new Error("Loft.withGuideRails() requires at least two stations");
|
|
87929
|
+
if (rails.length === 0) throw new Error("Loft.withGuideRails() requires at least one guide rail");
|
|
87930
|
+
const sortedStations = sortedValidStations(stations);
|
|
87931
|
+
const axis = options.axis ?? "Z";
|
|
87932
|
+
const start = sortedStations[0].position;
|
|
87933
|
+
const end = sortedStations[sortedStations.length - 1].position;
|
|
87934
|
+
const railEvaluators = buildRailEvaluators(rails, axis, start, end, options.railSamples ?? 64);
|
|
87935
|
+
const positions = generatedPositions(sortedStations, options.samples);
|
|
87936
|
+
const profiles2 = positions.map((position) => {
|
|
87937
|
+
const source = profileForPosition(sortedStations, position);
|
|
87938
|
+
const bounds = boundsForPosition(sortedStations, position);
|
|
87939
|
+
return fitProfileToBounds(source, applyRailsToBounds(bounds, railEvaluators, position));
|
|
87940
|
+
});
|
|
87941
|
+
const shape = loft(profiles2, positions, {
|
|
87942
|
+
edgeLength: options.edgeLength,
|
|
87943
|
+
boundsPadding: options.boundsPadding
|
|
87944
|
+
});
|
|
87945
|
+
return orientLoftToAxis(shape, axis);
|
|
87946
|
+
}
|
|
87947
|
+
function sortedValidStations(stations) {
|
|
87948
|
+
const sorted = [...stations].sort((a2, b) => a2.position - b.position);
|
|
87949
|
+
for (let index2 = 0; index2 < sorted.length; index2 += 1) {
|
|
87950
|
+
if (!Number.isFinite(sorted[index2].position)) throw new Error("Loft.withGuideRails station position must be finite");
|
|
87951
|
+
if (!(sorted[index2].profile instanceof Sketch)) throw new Error("Loft.withGuideRails() stations must use Sketch profiles");
|
|
87952
|
+
if (index2 > 0 && sorted[index2].position - sorted[index2 - 1].position < LOFT_GUIDE_EPS) {
|
|
87953
|
+
throw new Error("Loft.withGuideRails() requires unique, strictly increasing station positions");
|
|
87954
|
+
}
|
|
87955
|
+
}
|
|
87956
|
+
return sorted;
|
|
87957
|
+
}
|
|
87958
|
+
function generatedPositions(stations, samples) {
|
|
87959
|
+
const count = Math.max(2, Math.round(samples ?? Math.max(9, (stations.length - 1) * 8 + 1)));
|
|
87960
|
+
const start = stations[0].position;
|
|
87961
|
+
const end = stations[stations.length - 1].position;
|
|
87962
|
+
const values = /* @__PURE__ */ new Set();
|
|
87963
|
+
const positions = [];
|
|
87964
|
+
const addPosition = (position) => {
|
|
87965
|
+
const key = position.toFixed(9);
|
|
87966
|
+
if (!values.has(key)) {
|
|
87967
|
+
values.add(key);
|
|
87968
|
+
positions.push(position);
|
|
87969
|
+
}
|
|
87970
|
+
};
|
|
87971
|
+
for (let index2 = 0; index2 < count; index2 += 1) addPosition(start + (end - start) * index2 / (count - 1));
|
|
87972
|
+
for (const station of stations) addPosition(station.position);
|
|
87973
|
+
return positions.sort((a2, b) => a2 - b);
|
|
87974
|
+
}
|
|
87975
|
+
function profileForPosition(stations, position) {
|
|
87976
|
+
for (let index2 = 0; index2 < stations.length - 1; index2 += 1) {
|
|
87977
|
+
if (position <= stations[index2 + 1].position + LOFT_GUIDE_EPS) return stations[index2].profile;
|
|
87978
|
+
}
|
|
87979
|
+
return stations[stations.length - 1].profile;
|
|
87980
|
+
}
|
|
87981
|
+
function boundsForPosition(stations, position) {
|
|
87982
|
+
if (position <= stations[0].position + LOFT_GUIDE_EPS) return sketchBounds(stations[0].profile);
|
|
87983
|
+
const last = stations[stations.length - 1];
|
|
87984
|
+
if (position >= last.position - LOFT_GUIDE_EPS) return sketchBounds(last.profile);
|
|
87985
|
+
for (let index2 = 0; index2 < stations.length - 1; index2 += 1) {
|
|
87986
|
+
const a2 = stations[index2];
|
|
87987
|
+
const b = stations[index2 + 1];
|
|
87988
|
+
if (position >= a2.position - LOFT_GUIDE_EPS && position <= b.position + LOFT_GUIDE_EPS) {
|
|
87989
|
+
return lerpBounds(sketchBounds(a2.profile), sketchBounds(b.profile), (position - a2.position) / (b.position - a2.position));
|
|
87990
|
+
}
|
|
87991
|
+
}
|
|
87992
|
+
return sketchBounds(last.profile);
|
|
87993
|
+
}
|
|
87994
|
+
function applyRailsToBounds(bounds, rails, position) {
|
|
87995
|
+
const centerRail = rails.find((rail2) => rail2.side === "center");
|
|
87996
|
+
const center = centerRail ? railCrossAt(centerRail, position) : void 0;
|
|
87997
|
+
const next = { ...bounds };
|
|
87998
|
+
applyAxisRail(next, "X", sideValue(rails, "left", position, 0), sideValue(rails, "right", position, 0), center == null ? void 0 : center[0]);
|
|
87999
|
+
applyAxisRail(next, "Y", sideValue(rails, "back", position, 1), sideValue(rails, "front", position, 1), center == null ? void 0 : center[1]);
|
|
88000
|
+
if (next.maxX - next.minX < LOFT_GUIDE_EPS || next.maxY - next.minY < LOFT_GUIDE_EPS) {
|
|
88001
|
+
throw new Error("Loft.withGuideRails() guide rails produced a non-positive section size");
|
|
88002
|
+
}
|
|
88003
|
+
return next;
|
|
88004
|
+
}
|
|
88005
|
+
function sideValue(rails, side, position, crossIndex) {
|
|
88006
|
+
const rail2 = rails.find((entry) => entry.side === side);
|
|
88007
|
+
return rail2 ? railCrossAt(rail2, position)[crossIndex] : void 0;
|
|
88008
|
+
}
|
|
88009
|
+
function applyAxisRail(bounds, axis, minRail, maxRail, center) {
|
|
88010
|
+
const minKey = axis === "X" ? "minX" : "minY";
|
|
88011
|
+
const maxKey = axis === "X" ? "maxX" : "maxY";
|
|
88012
|
+
const width = bounds[maxKey] - bounds[minKey];
|
|
88013
|
+
if (minRail != null && maxRail != null) {
|
|
88014
|
+
if (maxRail - minRail < LOFT_GUIDE_EPS) throw new Error("Loft.withGuideRails() opposite guide rails crossed");
|
|
88015
|
+
if (center != null && Math.abs((minRail + maxRail) / 2 - center) > 1e-5) {
|
|
88016
|
+
throw new Error("Loft.withGuideRails() center rail conflicts with opposite side rails");
|
|
88017
|
+
}
|
|
88018
|
+
bounds[minKey] = minRail;
|
|
88019
|
+
bounds[maxKey] = maxRail;
|
|
88020
|
+
} else if (maxRail != null) {
|
|
88021
|
+
bounds[maxKey] = maxRail;
|
|
88022
|
+
bounds[minKey] = center != null ? 2 * center - maxRail : maxRail - width;
|
|
88023
|
+
} else if (minRail != null) {
|
|
88024
|
+
bounds[minKey] = minRail;
|
|
88025
|
+
bounds[maxKey] = center != null ? 2 * center - minRail : minRail + width;
|
|
88026
|
+
} else if (center != null) {
|
|
88027
|
+
bounds[minKey] = center - width / 2;
|
|
88028
|
+
bounds[maxKey] = center + width / 2;
|
|
88029
|
+
}
|
|
88030
|
+
}
|
|
88031
|
+
function fitProfileToBounds(profile, target) {
|
|
88032
|
+
const source = sketchBounds(profile);
|
|
88033
|
+
const sourceWidth = source.maxX - source.minX;
|
|
88034
|
+
const sourceDepth = source.maxY - source.minY;
|
|
88035
|
+
if (sourceWidth < LOFT_GUIDE_EPS || sourceDepth < LOFT_GUIDE_EPS) {
|
|
88036
|
+
throw new Error("Loft.withGuideRails() station profiles must have positive bounds");
|
|
88037
|
+
}
|
|
88038
|
+
const sourceCenter = [(source.minX + source.maxX) / 2, (source.minY + source.maxY) / 2];
|
|
88039
|
+
const targetCenter = [(target.minX + target.maxX) / 2, (target.minY + target.maxY) / 2];
|
|
88040
|
+
return profile.scaleAround(sourceCenter, [(target.maxX - target.minX) / sourceWidth, (target.maxY - target.minY) / sourceDepth]).translate(targetCenter[0] - sourceCenter[0], targetCenter[1] - sourceCenter[1]);
|
|
88041
|
+
}
|
|
88042
|
+
function sketchBounds(profile) {
|
|
88043
|
+
const bounds = profile.bounds();
|
|
88044
|
+
return { minX: bounds.min[0], maxX: bounds.max[0], minY: bounds.min[1], maxY: bounds.max[1] };
|
|
88045
|
+
}
|
|
88046
|
+
function lerpBounds(a2, b, t) {
|
|
88047
|
+
return {
|
|
88048
|
+
minX: lerp(a2.minX, b.minX, t),
|
|
88049
|
+
maxX: lerp(a2.maxX, b.maxX, t),
|
|
88050
|
+
minY: lerp(a2.minY, b.minY, t),
|
|
88051
|
+
maxY: lerp(a2.maxY, b.maxY, t)
|
|
88052
|
+
};
|
|
88053
|
+
}
|
|
88054
|
+
function lerp(a2, b, t) {
|
|
88055
|
+
return a2 + (b - a2) * t;
|
|
88056
|
+
}
|
|
88057
|
+
function mapLoftPath2D(path2, label, mapper) {
|
|
88058
|
+
const points = sampleLoftPath2D(path2, label);
|
|
88059
|
+
return points.map((point2, index2) => {
|
|
88060
|
+
if (!Array.isArray(point2) || point2.length !== 2 || !point2.every(Number.isFinite)) {
|
|
88061
|
+
throw new Error(`${label} point ${index2} must be a finite [x, y] point`);
|
|
88062
|
+
}
|
|
88063
|
+
return mapper([point2[0], point2[1]]);
|
|
88064
|
+
});
|
|
88065
|
+
}
|
|
88066
|
+
function sampleLoftPath2D(path2, label) {
|
|
88067
|
+
if (Array.isArray(path2)) {
|
|
88068
|
+
if (path2.length < 2) throw new Error(`${label} requires at least two [x, y] points`);
|
|
88069
|
+
return path2;
|
|
88070
|
+
}
|
|
88071
|
+
if (!path2 || typeof path2 !== "object" || typeof path2.toPolyline !== "function") {
|
|
88072
|
+
throw new Error(`${label} requires a 2D path, solved constrained path, or [x, y] point array`);
|
|
88073
|
+
}
|
|
88074
|
+
const points = path2.toPolyline();
|
|
88075
|
+
if (!Array.isArray(points) || points.length < 2) throw new Error(`${label} path must produce at least two [x, y] points`);
|
|
88076
|
+
return points;
|
|
88077
|
+
}
|
|
88078
|
+
const Loft = {
|
|
88079
|
+
/** Create a loft station from a 2D profile and an axis position. */
|
|
88080
|
+
station(profile, position) {
|
|
88081
|
+
if (!Number.isFinite(position)) throw new Error("Loft.station position must be finite");
|
|
88082
|
+
return { profile, position };
|
|
88083
|
+
},
|
|
88084
|
+
/** Create a guide rail that constrains the section-local negative-X side. */
|
|
88085
|
+
leftRail(path2) {
|
|
88086
|
+
return { side: "left", path: path2 };
|
|
88087
|
+
},
|
|
88088
|
+
/** Create a guide rail that constrains the section-local positive-X side. */
|
|
88089
|
+
rightRail(path2) {
|
|
88090
|
+
return { side: "right", path: path2 };
|
|
88091
|
+
},
|
|
88092
|
+
/** Create a guide rail that constrains the section-local positive-Y side. */
|
|
88093
|
+
frontRail(path2) {
|
|
88094
|
+
return { side: "front", path: path2 };
|
|
88095
|
+
},
|
|
88096
|
+
/** Create a guide rail that constrains the section-local negative-Y side. */
|
|
88097
|
+
backRail(path2) {
|
|
88098
|
+
return { side: "back", path: path2 };
|
|
88099
|
+
},
|
|
88100
|
+
/** Create a guide rail that moves section centers along the loft. */
|
|
88101
|
+
centerRail(path2) {
|
|
88102
|
+
return { side: "center", path: path2 };
|
|
88103
|
+
},
|
|
88104
|
+
/**
|
|
88105
|
+
* Place a 2D guide path onto the XZ plane.
|
|
88106
|
+
*
|
|
88107
|
+
* The path's first coordinate becomes X and its second coordinate becomes Z.
|
|
88108
|
+
* Use this for left/right silhouette rails authored with `path()` or `constrainedSketch()`.
|
|
88109
|
+
*/
|
|
88110
|
+
pathOnXz(path2, y2 = 0) {
|
|
88111
|
+
if (!Number.isFinite(y2)) throw new Error("Loft.pathOnXz y must be finite");
|
|
88112
|
+
return mapLoftPath2D(path2, "Loft.pathOnXz", ([x2, z2]) => [x2, y2, z2]);
|
|
88113
|
+
},
|
|
88114
|
+
/**
|
|
88115
|
+
* Place a 2D guide path onto the YZ plane.
|
|
88116
|
+
*
|
|
88117
|
+
* The path's first coordinate becomes Y and its second coordinate becomes Z.
|
|
88118
|
+
* Use this for front/back crown rails authored with `path()` or `constrainedSketch()`.
|
|
88119
|
+
*/
|
|
88120
|
+
pathOnYz(path2, x2 = 0) {
|
|
88121
|
+
if (!Number.isFinite(x2)) throw new Error("Loft.pathOnYz x must be finite");
|
|
88122
|
+
return mapLoftPath2D(path2, "Loft.pathOnYz", ([y2, z2]) => [x2, y2, z2]);
|
|
88123
|
+
},
|
|
88124
|
+
/**
|
|
88125
|
+
* Place a 2D guide path onto the XY plane.
|
|
88126
|
+
*
|
|
88127
|
+
* The path's first coordinate becomes X and its second coordinate becomes Y.
|
|
88128
|
+
* Use this when lofting along X or Y and a rail lives in a horizontal sketch plane.
|
|
88129
|
+
*/
|
|
88130
|
+
pathOnXy(path2, z2 = 0) {
|
|
88131
|
+
if (!Number.isFinite(z2)) throw new Error("Loft.pathOnXy z must be finite");
|
|
88132
|
+
return mapLoftPath2D(path2, "Loft.pathOnXy", ([x2, y2]) => [x2, y2, z2]);
|
|
88133
|
+
},
|
|
88134
|
+
/**
|
|
88135
|
+
* Loft through profile stations while forcing generated sections to follow guide rails.
|
|
88136
|
+
*
|
|
88137
|
+
* Stations define the cross-section family. Guide rails define the side or center
|
|
88138
|
+
* paths the loft must pass through. With opposite side rails, the section is scaled
|
|
88139
|
+
* to touch both rails. With one side rail, the section keeps its interpolated size
|
|
88140
|
+
* unless a center rail is also present.
|
|
88141
|
+
*/
|
|
88142
|
+
withGuideRails(stations, rails, options = {}) {
|
|
88143
|
+
return loftWithGuideRails(stations, rails, options);
|
|
88144
|
+
}
|
|
88145
|
+
};
|
|
86178
88146
|
let collectedHighlights = [];
|
|
86179
88147
|
function resetHighlights() {
|
|
86180
88148
|
collectedHighlights = [];
|
|
@@ -302829,6 +304797,7 @@ function classifySdfPreviewNode(node) {
|
|
|
302829
304797
|
case "sdf:twist":
|
|
302830
304798
|
case "sdf:bend":
|
|
302831
304799
|
case "sdf:repeat":
|
|
304800
|
+
case "sdf:circularArray":
|
|
302832
304801
|
case "sdf:shell":
|
|
302833
304802
|
case "sdf:onion":
|
|
302834
304803
|
return classifySdfPreviewNode(node.child);
|
|
@@ -302838,10 +304807,7 @@ function classifySdfPreviewNode(node) {
|
|
|
302838
304807
|
reason: "This SDF uses a custom JavaScript displacement function that cannot be compiled for raymarch preview."
|
|
302839
304808
|
};
|
|
302840
304809
|
case "sdf:surfaceDisplace":
|
|
302841
|
-
return
|
|
302842
|
-
mode: "unsupported",
|
|
302843
|
-
reason: "This SDF uses surface displacement that is not yet available in the raymarch shader."
|
|
302844
|
-
};
|
|
304810
|
+
return classifySurfaceDisplacePreviewNode(node);
|
|
302845
304811
|
case "sdf:spatialBlend":
|
|
302846
304812
|
return {
|
|
302847
304813
|
mode: "unsupported",
|
|
@@ -302867,6 +304833,79 @@ ${node.shaderUnsupportedReason}` : ""}`
|
|
|
302867
304833
|
};
|
|
302868
304834
|
}
|
|
302869
304835
|
}
|
|
304836
|
+
function classifySurfaceDisplacePreviewNode(node) {
|
|
304837
|
+
if (!node.pattern) {
|
|
304838
|
+
return {
|
|
304839
|
+
mode: "unsupported",
|
|
304840
|
+
reason: "This SDF uses a custom JavaScript surface pattern that cannot be compiled for raymarch preview."
|
|
304841
|
+
};
|
|
304842
|
+
}
|
|
304843
|
+
const childResult = classifySdfPreviewNode(node.child);
|
|
304844
|
+
if (childResult.mode !== "raymarch") return childResult;
|
|
304845
|
+
const uv = analyzeShaderSurfaceUv(node.child, "p", node.uvMode);
|
|
304846
|
+
if (uv.mode === "triplanar") {
|
|
304847
|
+
return {
|
|
304848
|
+
mode: "unsupported",
|
|
304849
|
+
reason: "Typed surface displacement raymarch preview currently supports sphere, cylinder, and torus UV mappings."
|
|
304850
|
+
};
|
|
304851
|
+
}
|
|
304852
|
+
return { mode: "raymarch" };
|
|
304853
|
+
}
|
|
304854
|
+
function f2(value) {
|
|
304855
|
+
if (!Number.isFinite(value)) return "0.0";
|
|
304856
|
+
const text = Number(value.toPrecision(9)).toString();
|
|
304857
|
+
return text.includes(".") || text.includes("e") ? text : `${text}.0`;
|
|
304858
|
+
}
|
|
304859
|
+
function v3(value) {
|
|
304860
|
+
return `vec3(${f2(value[0])}, ${f2(value[1])}, ${f2(value[2])})`;
|
|
304861
|
+
}
|
|
304862
|
+
function analyzeShaderSurfaceUv(node, p2, override) {
|
|
304863
|
+
const analysis = analyzeShaderSurfaceUvNode(node, p2);
|
|
304864
|
+
if (!override || override === "auto") return analysis;
|
|
304865
|
+
if (override === "triplanar") return { mode: "triplanar" };
|
|
304866
|
+
if (analysis.mode === "triplanar") return analysis;
|
|
304867
|
+
if (override === analysis.mode) return analysis;
|
|
304868
|
+
if (override === "sphere" || override === "cylinder") {
|
|
304869
|
+
return { mode: override, localPoint: analysis.localPoint, radius: analysis.radius };
|
|
304870
|
+
}
|
|
304871
|
+
return analysis.mode === "torus" ? analysis : { mode: "triplanar" };
|
|
304872
|
+
}
|
|
304873
|
+
function analyzeShaderSurfaceUvNode(node, p2) {
|
|
304874
|
+
switch (node.kind) {
|
|
304875
|
+
case "sdf:sphere":
|
|
304876
|
+
return { mode: "sphere", localPoint: p2, radius: node.radius };
|
|
304877
|
+
case "sdf:cylinder":
|
|
304878
|
+
return { mode: "cylinder", localPoint: p2, radius: node.radius };
|
|
304879
|
+
case "sdf:torus":
|
|
304880
|
+
return { mode: "torus", localPoint: p2, radius: node.minorRadius, majorRadius: node.majorRadius };
|
|
304881
|
+
case "sdf:translate":
|
|
304882
|
+
return analyzeShaderSurfaceUvNode(node.child, `(${p2} - ${v3(node.offset)})`);
|
|
304883
|
+
case "sdf:rotate":
|
|
304884
|
+
return analyzeShaderSurfaceUvNode(node.child, `rotateInvEuler(${p2}, ${v3(node.degrees)})`);
|
|
304885
|
+
case "sdf:scale": {
|
|
304886
|
+
const result = analyzeShaderSurfaceUvNode(node.child, `(${p2} / ${f2(node.factor)})`);
|
|
304887
|
+
if (result.mode === "triplanar") return result;
|
|
304888
|
+
return {
|
|
304889
|
+
...result,
|
|
304890
|
+
radius: result.radius * node.factor,
|
|
304891
|
+
...result.mode === "torus" ? { majorRadius: result.majorRadius * node.factor } : {}
|
|
304892
|
+
};
|
|
304893
|
+
}
|
|
304894
|
+
case "sdf:shell":
|
|
304895
|
+
return analyzeShaderSurfaceUvNode(node.child, p2);
|
|
304896
|
+
case "sdf:union":
|
|
304897
|
+
case "sdf:smoothUnion":
|
|
304898
|
+
case "sdf:intersection":
|
|
304899
|
+
case "sdf:smoothIntersection":
|
|
304900
|
+
case "sdf:difference":
|
|
304901
|
+
case "sdf:smoothDifference":
|
|
304902
|
+
return node.children.length > 0 ? analyzeShaderSurfaceUvNode(node.children[0], p2) : { mode: "triplanar" };
|
|
304903
|
+
case "sdf:morph":
|
|
304904
|
+
return analyzeShaderSurfaceUvNode(node.a, p2);
|
|
304905
|
+
default:
|
|
304906
|
+
return { mode: "triplanar" };
|
|
304907
|
+
}
|
|
304908
|
+
}
|
|
302870
304909
|
function describeScriptResultType(value) {
|
|
302871
304910
|
var _a3, _b3;
|
|
302872
304911
|
if (value == null) return String(value);
|
|
@@ -302927,7 +304966,7 @@ function mapScriptResultToScene(args) {
|
|
|
302927
304966
|
var _a3;
|
|
302928
304967
|
const objects = [];
|
|
302929
304968
|
const shapeDimensions = [];
|
|
302930
|
-
const pushShape = (shape, name, groupName, color, treePath) => {
|
|
304969
|
+
const pushShape = (shape, name, groupName, color, treePath, tags = []) => {
|
|
302931
304970
|
const objectId = `obj-${objects.length + 1}`;
|
|
302932
304971
|
objects.push({
|
|
302933
304972
|
id: objectId,
|
|
@@ -302938,7 +304977,8 @@ function mapScriptResultToScene(args) {
|
|
|
302938
304977
|
materialProps: shape.materialProps,
|
|
302939
304978
|
geometryInfo: shape.geometryInfo(),
|
|
302940
304979
|
groupName,
|
|
302941
|
-
treePath: treePath && treePath.length > 0 ? [...treePath] : [name]
|
|
304980
|
+
treePath: treePath && treePath.length > 0 ? [...treePath] : [name],
|
|
304981
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
302942
304982
|
});
|
|
302943
304983
|
const dims = getShapeDimensions(shape);
|
|
302944
304984
|
dims.forEach((dim2) => {
|
|
@@ -302962,7 +305002,7 @@ function mapScriptResultToScene(args) {
|
|
|
302962
305002
|
});
|
|
302963
305003
|
}
|
|
302964
305004
|
};
|
|
302965
|
-
const pushSketch = (sketch, name, groupName, treePath) => {
|
|
305005
|
+
const pushSketch = (sketch, name, groupName, treePath, tags = []) => {
|
|
302966
305006
|
const meta2 = sketch instanceof ConstraintSketch ? sketch.constraintMeta : void 0;
|
|
302967
305007
|
objects.push({
|
|
302968
305008
|
id: `obj-${objects.length + 1}`,
|
|
@@ -302973,10 +305013,11 @@ function mapScriptResultToScene(args) {
|
|
|
302973
305013
|
sketchMeta: meta2,
|
|
302974
305014
|
color: sketch.colorHex,
|
|
302975
305015
|
groupName,
|
|
302976
|
-
treePath: treePath && treePath.length > 0 ? [...treePath] : [name]
|
|
305016
|
+
treePath: treePath && treePath.length > 0 ? [...treePath] : [name],
|
|
305017
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
302977
305018
|
});
|
|
302978
305019
|
};
|
|
302979
|
-
const pushSdf = (sdfShape, name, groupName, treePath, color) => {
|
|
305020
|
+
const pushSdf = (sdfShape, name, groupName, treePath, color, tags = []) => {
|
|
302980
305021
|
const preview = classifySdfPreviewNode(sdfShape._node);
|
|
302981
305022
|
const displayColor = color || sdfShape.colorHex;
|
|
302982
305023
|
const data = {
|
|
@@ -302996,7 +305037,8 @@ function mapScriptResultToScene(args) {
|
|
|
302996
305037
|
materialProps: sdfShape.materialProps,
|
|
302997
305038
|
geometryInfo: null,
|
|
302998
305039
|
groupName,
|
|
302999
|
-
treePath: treePath && treePath.length > 0 ? [...treePath] : [name]
|
|
305040
|
+
treePath: treePath && treePath.length > 0 ? [...treePath] : [name],
|
|
305041
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
303000
305042
|
});
|
|
303001
305043
|
};
|
|
303002
305044
|
const isNamedObject = (item) => {
|
|
@@ -303013,18 +305055,24 @@ function mapScriptResultToScene(args) {
|
|
|
303013
305055
|
const rootGroupChildLabel = (grp, index2) => {
|
|
303014
305056
|
return shapeGroupChildSegment(grp, index2, true);
|
|
303015
305057
|
};
|
|
303016
|
-
const flattenGroupChild = (child, label, groupName, treePath) => {
|
|
305058
|
+
const flattenGroupChild = (child, label, groupName, treePath, tags = []) => {
|
|
303017
305059
|
const resolvedTreePath = treePath && treePath.length > 0 ? treePath : [label];
|
|
303018
305060
|
if (child instanceof ShapeGroup) {
|
|
303019
305061
|
child.children.forEach((nested, i) => {
|
|
303020
|
-
flattenGroupChild(
|
|
305062
|
+
flattenGroupChild(
|
|
305063
|
+
nested,
|
|
305064
|
+
groupChildLabel(child, label, i),
|
|
305065
|
+
groupName,
|
|
305066
|
+
[...resolvedTreePath, shapeGroupChildSegment(child, i)],
|
|
305067
|
+
mergeSceneTags(tags, child.tagsForChild(i))
|
|
305068
|
+
);
|
|
303021
305069
|
});
|
|
303022
305070
|
return;
|
|
303023
305071
|
}
|
|
303024
305072
|
if (child instanceof Shape) {
|
|
303025
|
-
pushShape(child, label, groupName, void 0, resolvedTreePath);
|
|
305073
|
+
pushShape(child, label, groupName, void 0, resolvedTreePath, tags);
|
|
303026
305074
|
} else if (child instanceof Sketch) {
|
|
303027
|
-
pushSketch(child, label, groupName, resolvedTreePath);
|
|
305075
|
+
pushSketch(child, label, groupName, resolvedTreePath, tags);
|
|
303028
305076
|
}
|
|
303029
305077
|
};
|
|
303030
305078
|
const isPlainObject2 = (value) => {
|
|
@@ -303033,34 +305081,40 @@ function mapScriptResultToScene(args) {
|
|
|
303033
305081
|
return proto2 === Object.prototype || proto2 === null;
|
|
303034
305082
|
};
|
|
303035
305083
|
const joinName = (path2) => path2.join(".");
|
|
303036
|
-
const processRenderableTree = (value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = [], seen = /* @__PURE__ */ new WeakSet()) => {
|
|
305084
|
+
const processRenderableTree = (value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = [], inheritedTags = [], seen = /* @__PURE__ */ new WeakSet()) => {
|
|
303037
305085
|
const segment = fallbackSegment.trim().length > 0 ? fallbackSegment : fallbackLabel;
|
|
303038
305086
|
const treePath = [...parentTreePath, segment];
|
|
303039
305087
|
const name = joinName(treePath) || fallbackLabel;
|
|
303040
305088
|
if (value instanceof Assembly) {
|
|
303041
|
-
value.solve().toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath));
|
|
305089
|
+
value.solve().toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath, inheritedTags));
|
|
303042
305090
|
return;
|
|
303043
305091
|
}
|
|
303044
305092
|
if (value instanceof SolvedAssembly) {
|
|
303045
|
-
value.toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath));
|
|
305093
|
+
value.toSceneObjects().forEach((item, index2) => processNamedItem(item, `${name}.${index2 + 1}`, `${index2 + 1}`, name, treePath, inheritedTags));
|
|
303046
305094
|
return;
|
|
303047
305095
|
}
|
|
303048
305096
|
if (value instanceof ShapeGroup) {
|
|
303049
305097
|
value.children.forEach((child, i) => {
|
|
303050
|
-
flattenGroupChild(
|
|
305098
|
+
flattenGroupChild(
|
|
305099
|
+
child,
|
|
305100
|
+
groupChildLabel(value, name, i),
|
|
305101
|
+
parentGroup,
|
|
305102
|
+
[...treePath, shapeGroupChildSegment(value, i)],
|
|
305103
|
+
mergeSceneTags(inheritedTags, value.tagsForChild(i))
|
|
305104
|
+
);
|
|
303051
305105
|
});
|
|
303052
305106
|
return;
|
|
303053
305107
|
}
|
|
303054
305108
|
if (value instanceof Shape) {
|
|
303055
|
-
pushShape(value, name, parentGroup, void 0, treePath);
|
|
305109
|
+
pushShape(value, name, parentGroup, void 0, treePath, inheritedTags);
|
|
303056
305110
|
return;
|
|
303057
305111
|
}
|
|
303058
305112
|
if (value instanceof Sketch) {
|
|
303059
|
-
pushSketch(value, name, parentGroup, treePath);
|
|
305113
|
+
pushSketch(value, name, parentGroup, treePath, inheritedTags);
|
|
303060
305114
|
return;
|
|
303061
305115
|
}
|
|
303062
305116
|
if (value instanceof SdfShape) {
|
|
303063
|
-
pushSdf(value, name, parentGroup, treePath);
|
|
305117
|
+
pushSdf(value, name, parentGroup, treePath, void 0, inheritedTags);
|
|
303064
305118
|
return;
|
|
303065
305119
|
}
|
|
303066
305120
|
if (value instanceof GCodeBuilder) {
|
|
@@ -303071,7 +305125,8 @@ function mapScriptResultToScene(args) {
|
|
|
303071
305125
|
sketch: null,
|
|
303072
305126
|
toolpath: value.build(),
|
|
303073
305127
|
geometryInfo: null,
|
|
303074
|
-
treePath
|
|
305128
|
+
treePath,
|
|
305129
|
+
...inheritedTags.length > 0 ? { tags: [...inheritedTags] } : {}
|
|
303075
305130
|
});
|
|
303076
305131
|
return;
|
|
303077
305132
|
}
|
|
@@ -303081,30 +305136,38 @@ function mapScriptResultToScene(args) {
|
|
|
303081
305136
|
value.forEach((item, index2) => {
|
|
303082
305137
|
const childSegment = `${index2 + 1}`;
|
|
303083
305138
|
const childLabel = `${name}.${childSegment}`;
|
|
303084
|
-
processRenderableTree(item, childLabel, childSegment, parentGroup, treePath, seen);
|
|
305139
|
+
processRenderableTree(item, childLabel, childSegment, parentGroup, treePath, inheritedTags, seen);
|
|
303085
305140
|
});
|
|
303086
305141
|
return;
|
|
303087
305142
|
}
|
|
303088
305143
|
if (isNamedObject(value)) {
|
|
303089
|
-
processNamedItem(value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath);
|
|
305144
|
+
processNamedItem(value, fallbackLabel, fallbackSegment, parentGroup, parentTreePath, inheritedTags);
|
|
303090
305145
|
return;
|
|
303091
305146
|
}
|
|
303092
305147
|
if (isPlainObject2(value)) {
|
|
303093
305148
|
if (seen.has(value)) return;
|
|
303094
305149
|
seen.add(value);
|
|
303095
305150
|
Object.entries(value).forEach(([key, entry]) => {
|
|
303096
|
-
processRenderableTree(entry, key, key, parentGroup, treePath, seen);
|
|
305151
|
+
processRenderableTree(entry, key, key, parentGroup, treePath, inheritedTags, seen);
|
|
303097
305152
|
});
|
|
303098
305153
|
}
|
|
303099
305154
|
};
|
|
303100
|
-
const processNamedItem = (item, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = []) => {
|
|
305155
|
+
const processNamedItem = (item, fallbackLabel, fallbackSegment, parentGroup, parentTreePath = [], inheritedTags = []) => {
|
|
305156
|
+
var _a4;
|
|
303101
305157
|
const name = typeof item.name === "string" && item.name.trim().length > 0 ? item.name : fallbackLabel;
|
|
303102
305158
|
const localSegment = typeof item.name === "string" && item.name.trim().length > 0 ? item.name : fallbackSegment;
|
|
303103
305159
|
const treePath = [...parentTreePath, localSegment];
|
|
303104
305160
|
const grp = parentGroup;
|
|
305161
|
+
const tags = mergeSceneTags(inheritedTags, (_a4 = item.metadata) == null ? void 0 : _a4.tags, item.tags);
|
|
303105
305162
|
if (item.group instanceof ShapeGroup) {
|
|
303106
305163
|
item.group.children.forEach((child, i) => {
|
|
303107
|
-
flattenGroupChild(
|
|
305164
|
+
flattenGroupChild(
|
|
305165
|
+
child,
|
|
305166
|
+
groupChildLabel(item.group, name, i),
|
|
305167
|
+
name,
|
|
305168
|
+
[...treePath, shapeGroupChildSegment(item.group, i)],
|
|
305169
|
+
mergeSceneTags(tags, item.group.tagsForChild(i))
|
|
305170
|
+
);
|
|
303108
305171
|
});
|
|
303109
305172
|
return;
|
|
303110
305173
|
}
|
|
@@ -303114,39 +305177,48 @@ function mapScriptResultToScene(args) {
|
|
|
303114
305177
|
const childTreePath = [...treePath, `${i + 1}`];
|
|
303115
305178
|
if (child instanceof ShapeGroup) {
|
|
303116
305179
|
child.children.forEach((nested, nestedIndex) => {
|
|
303117
|
-
flattenGroupChild(
|
|
303118
|
-
|
|
303119
|
-
|
|
303120
|
-
|
|
305180
|
+
flattenGroupChild(
|
|
305181
|
+
nested,
|
|
305182
|
+
groupChildLabel(child, name, nestedIndex),
|
|
305183
|
+
name,
|
|
305184
|
+
[...treePath, shapeGroupChildSegment(child, nestedIndex)],
|
|
305185
|
+
mergeSceneTags(tags, child.tagsForChild(nestedIndex))
|
|
305186
|
+
);
|
|
303121
305187
|
});
|
|
303122
305188
|
} else if (child instanceof Shape) {
|
|
303123
|
-
pushShape(child, childLabel, name, void 0, childTreePath);
|
|
305189
|
+
pushShape(child, childLabel, name, void 0, childTreePath, tags);
|
|
303124
305190
|
} else if (child instanceof Sketch) {
|
|
303125
|
-
pushSketch(child, childLabel, name, childTreePath);
|
|
305191
|
+
pushSketch(child, childLabel, name, childTreePath, tags);
|
|
303126
305192
|
} else if (child instanceof SdfShape) {
|
|
303127
|
-
pushSdf(child, childLabel, name, childTreePath);
|
|
305193
|
+
pushSdf(child, childLabel, name, childTreePath, void 0, tags);
|
|
303128
305194
|
} else if (isNamedObject(child)) {
|
|
303129
|
-
processNamedItem(child, childLabel, `${i + 1}`, name, treePath);
|
|
305195
|
+
processNamedItem(child, childLabel, `${i + 1}`, name, treePath, tags);
|
|
303130
305196
|
}
|
|
303131
305197
|
});
|
|
303132
305198
|
return;
|
|
303133
305199
|
}
|
|
303134
305200
|
if (item.shape instanceof ShapeGroup) {
|
|
303135
305201
|
item.shape.children.forEach(
|
|
303136
|
-
(child, i) => flattenGroupChild(
|
|
305202
|
+
(child, i) => flattenGroupChild(
|
|
305203
|
+
child,
|
|
305204
|
+
groupChildLabel(item.shape, name, i),
|
|
305205
|
+
name,
|
|
305206
|
+
[...treePath, shapeGroupChildSegment(item.shape, i)],
|
|
305207
|
+
mergeSceneTags(tags, item.shape.tagsForChild(i))
|
|
305208
|
+
)
|
|
303137
305209
|
);
|
|
303138
305210
|
return;
|
|
303139
305211
|
}
|
|
303140
305212
|
if (item.shape instanceof Shape) {
|
|
303141
|
-
pushShape(item.shape, name, grp, item.color, treePath);
|
|
305213
|
+
pushShape(item.shape, name, grp, item.color, treePath, tags);
|
|
303142
305214
|
return;
|
|
303143
305215
|
}
|
|
303144
305216
|
if (item.shape instanceof SdfShape) {
|
|
303145
|
-
pushSdf(item.shape, name, grp, treePath, item.color);
|
|
305217
|
+
pushSdf(item.shape, name, grp, treePath, item.color, tags);
|
|
303146
305218
|
return;
|
|
303147
305219
|
}
|
|
303148
305220
|
if (item.sdf instanceof SdfShape) {
|
|
303149
|
-
pushSdf(item.sdf, name, grp, treePath, item.color);
|
|
305221
|
+
pushSdf(item.sdf, name, grp, treePath, item.color, tags);
|
|
303150
305222
|
return;
|
|
303151
305223
|
}
|
|
303152
305224
|
if (item.sketch instanceof Sketch) {
|
|
@@ -303160,7 +305232,8 @@ function mapScriptResultToScene(args) {
|
|
|
303160
305232
|
sketchMeta: meta2,
|
|
303161
305233
|
color: item.color || item.sketch.colorHex,
|
|
303162
305234
|
groupName: grp,
|
|
303163
|
-
treePath
|
|
305235
|
+
treePath,
|
|
305236
|
+
...tags.length > 0 ? { tags: [...tags] } : {}
|
|
303164
305237
|
});
|
|
303165
305238
|
}
|
|
303166
305239
|
};
|
|
@@ -303174,14 +305247,20 @@ function mapScriptResultToScene(args) {
|
|
|
303174
305247
|
} else if (result instanceof ShapeGroup) {
|
|
303175
305248
|
result.children.forEach((child, i) => {
|
|
303176
305249
|
const label = rootGroupChildLabel(result, i);
|
|
303177
|
-
flattenGroupChild(child, label, void 0, [label]);
|
|
305250
|
+
flattenGroupChild(child, label, void 0, [label], result.tagsForChild(i));
|
|
303178
305251
|
});
|
|
303179
305252
|
} else if (Array.isArray(result)) {
|
|
303180
305253
|
result.forEach((item, index2) => {
|
|
303181
305254
|
const label = `Object ${index2 + 1}`;
|
|
303182
305255
|
if (item instanceof ShapeGroup) {
|
|
303183
305256
|
item.children.forEach((child, i) => {
|
|
303184
|
-
flattenGroupChild(
|
|
305257
|
+
flattenGroupChild(
|
|
305258
|
+
child,
|
|
305259
|
+
groupChildLabel(item, label, i),
|
|
305260
|
+
void 0,
|
|
305261
|
+
[label, shapeGroupChildSegment(item, i)],
|
|
305262
|
+
item.tagsForChild(i)
|
|
305263
|
+
);
|
|
303185
305264
|
});
|
|
303186
305265
|
return;
|
|
303187
305266
|
}
|
|
@@ -303214,7 +305293,7 @@ function mapScriptResultToScene(args) {
|
|
|
303214
305293
|
} else if (defaultValue instanceof ShapeGroup) {
|
|
303215
305294
|
defaultValue.children.forEach((child, i) => {
|
|
303216
305295
|
const label = rootGroupChildLabel(defaultValue, i);
|
|
303217
|
-
flattenGroupChild(child, label, void 0, [label]);
|
|
305296
|
+
flattenGroupChild(child, label, void 0, [label], defaultValue.tagsForChild(i));
|
|
303218
305297
|
});
|
|
303219
305298
|
} else if (defaultValue instanceof Shape) {
|
|
303220
305299
|
pushShape(defaultValue, args.fileName, void 0, void 0, [args.fileName]);
|
|
@@ -303260,7 +305339,8 @@ function mapScriptResultToScene(args) {
|
|
|
303260
305339
|
name: `${mock2.name} (mock)`,
|
|
303261
305340
|
shape: mock2.shape,
|
|
303262
305341
|
sketch: null,
|
|
303263
|
-
mock: true
|
|
305342
|
+
mock: true,
|
|
305343
|
+
tags: ["mock"]
|
|
303264
305344
|
});
|
|
303265
305345
|
}
|
|
303266
305346
|
const hasSdfLeaves = objects.some((obj) => obj.sdf);
|
|
@@ -303531,6 +305611,7 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
303531
305611
|
nurbsSurface,
|
|
303532
305612
|
spline2d,
|
|
303533
305613
|
spline3d,
|
|
305614
|
+
Loft,
|
|
303534
305615
|
loft,
|
|
303535
305616
|
loftAlongSpine,
|
|
303536
305617
|
sweep,
|