forgecad 0.9.2 → 0.9.3
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/LICENSE +7 -5
- package/README.md +1 -1
- package/README.public.md +24 -2
- package/dist/assets/{AdminPage-Bs4PiK00.js → AdminPage-4jihcEk_.js} +1 -1
- package/dist/assets/{BlogPage-DVmgN0ma.js → BlogPage-BvzruKtw.js} +1 -1
- package/dist/assets/{DocsPage-BP6wlsBN.js → DocsPage-DHbd-WS-.js} +13 -13
- package/dist/assets/{EditorApp-Arw2NnGJ.js → EditorApp-C5P2rBfh.js} +433 -84
- package/dist/assets/{EditorApp-VY9lXx0N.css → EditorApp-DS0AIUrZ.css} +25 -0
- package/dist/assets/{EmbedViewer-qgQiOahL.js → EmbedViewer-B70wQwlE.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-DvhtmWOz.js → LandingPageProofDriven-DIsYTnep.js} +1 -1
- package/dist/assets/{PricingPage-Ck3CP2ti.css → PricingPage-BMedqFef.css} +48 -0
- package/dist/assets/{PricingPage-657oLvWh.js → PricingPage-YPOr12pP.js} +34 -6
- package/dist/assets/{SettingsPage-wNy3_2yn.js → SettingsPage-rntoyJ3b.js} +10 -13
- package/dist/assets/{app-BdBoMQeO.js → app-CWucmnLZ.js} +801 -1208
- package/dist/assets/cli/{render-Ci3jjyT1.js → render-DZHmUySW.js} +214 -23
- package/dist/assets/copy-CQKQppF-.js +8 -0
- package/dist/assets/{evalWorker-CMCAbK8r.js → evalWorker-C3dKxi9Y.js} +1117 -95
- package/dist/assets/{manifold-BMn-8Vf8.js → manifold-CQ3FhfWB.js} +1 -1
- package/dist/assets/{manifold-jlYQ6E5R.js → manifold-CU0G1yYL.js} +1 -1
- package/dist/assets/{manifold-DbyILno4.js → manifold-CYWZMfjB.js} +2 -2
- package/dist/assets/{renderSceneState-DAnqvxSt.js → renderSceneState-BBUrnsUN.js} +1 -1
- package/dist/assets/{reportWorker-BcRVMHK-.js → reportWorker-BhZ7DjxQ.js} +1091 -95
- package/dist/assets/{sectionPlaneMath-DXJ_TdIW.js → sectionPlaneMath-BxfokaJE.js} +1091 -95
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +182 -89
- package/dist/docs-raw/API/core/concepts.md +26 -0
- package/dist/docs-raw/CLI.md +58 -37
- package/dist/docs-raw/INDEX.md +81 -64
- package/dist/docs-raw/cli-monetization.md +9 -8
- package/dist/docs-raw/generated/concepts.md +111 -4
- package/dist/docs-raw/generated/core.md +2 -0
- package/dist/docs-raw/generated/curves.md +480 -1
- package/dist/docs-raw/generated/output.md +1 -0
- package/dist/docs-raw/generated/sketch.md +2 -0
- package/dist/docs-raw/generated/viewport.md +81 -3
- package/dist/docs-raw/product/user-outreach-email-templates.md +159 -0
- package/dist/docs-raw/skills/forgecad-image-replicator.md +1 -1
- package/dist/docs-raw/skills/forgecad-make-a-model.md +33 -4
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +1 -1
- package/dist/docs-raw/skills/forgecad-project.md +1 -1
- package/dist/docs-raw/skills/forgecad-render-inspect.md +1 -1
- package/dist/docs-raw/skills/forgecad.md +2 -1
- package/dist/docs-raw/welcome.md +85 -137
- package/dist/index.html +1 -1
- package/dist/llms.txt +4 -3
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +1413 -219
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-skill/CONTEXT.md +594 -5
- package/dist-skill/SKILL-dev.md +2 -1
- package/dist-skill/SKILL.md +2 -1
- package/dist-skill/docs/API/core/concepts.md +26 -0
- package/dist-skill/docs/CLI.md +58 -37
- package/dist-skill/docs/generated/core.md +2 -0
- package/dist-skill/docs/generated/curves.md +480 -1
- package/dist-skill/docs/generated/output.md +1 -0
- package/dist-skill/docs/generated/sketch.md +2 -0
- package/dist-skill/docs/generated/viewport.md +81 -3
- package/dist-skill/docs-dev/API/core/concepts.md +26 -0
- package/dist-skill/docs-dev/CLI.md +58 -37
- package/dist-skill/docs-dev/generated/core.md +2 -0
- package/dist-skill/docs-dev/generated/curves.md +480 -1
- package/dist-skill/docs-dev/generated/output.md +1 -0
- package/dist-skill/docs-dev/generated/sketch.md +2 -0
- package/dist-skill/docs-dev/generated/viewport.md +81 -3
- package/dist-skill/library/README.md +0 -1
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +1 -1
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +33 -4
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
- package/dist-skill/library/forgecad-project/SKILL.md +1 -1
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +1 -1
- package/examples/api/conformal-product-ribbon.forge.js +77 -0
- package/examples/api/render-labels.forge.js +33 -0
- package/examples/api/text2d-basics.forge.js +6 -3
- package/package.json +1 -1
- package/dist-skill/library/forgecad-deep-dive/SKILL.md +0 -120
- package/dist-skill/library/forgecad-deep-dive/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-deep-dive/references/output-shape.md +0 -64
|
@@ -6736,7 +6736,7 @@ function add$3(a, b) {
|
|
|
6736
6736
|
function scale$4(v, s) {
|
|
6737
6737
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
6738
6738
|
}
|
|
6739
|
-
function sub$
|
|
6739
|
+
function sub$5(a, b) {
|
|
6740
6740
|
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
6741
6741
|
}
|
|
6742
6742
|
function cross$4(a, b) {
|
|
@@ -6775,8 +6775,8 @@ function buildSurfaceSheetTopology(boundaries, options = {}) {
|
|
|
6775
6775
|
const [v1Start, v1End] = boundaries.v1;
|
|
6776
6776
|
const corners = [u0Start, u0End, u1Start, u1End];
|
|
6777
6777
|
const center = options.center ?? average$1(corners);
|
|
6778
|
-
const uAxis = normalizeAxis$1(sub$
|
|
6779
|
-
const vAxis = normalizeAxis$1(sub$
|
|
6778
|
+
const uAxis = normalizeAxis$1(sub$5(midpoint$3(u1Start, u1End), midpoint$3(u0Start, u0End)));
|
|
6779
|
+
const vAxis = normalizeAxis$1(sub$5(midpoint$3(v1Start, v1End), midpoint$3(v0Start, v0End)));
|
|
6780
6780
|
const normal = normalizeAxis$1(options.normal ?? cross$4(uAxis, vAxis));
|
|
6781
6781
|
const faces = /* @__PURE__ */ new Map();
|
|
6782
6782
|
faces.set(faceName, {
|
|
@@ -6808,7 +6808,7 @@ function attachSurfaceSheetTopology(shape, boundaries, options = {}) {
|
|
|
6808
6808
|
});
|
|
6809
6809
|
return shape;
|
|
6810
6810
|
}
|
|
6811
|
-
function requireFinite$
|
|
6811
|
+
function requireFinite$7(v, label) {
|
|
6812
6812
|
if (!Number.isFinite(v)) throw new Error(`nurbsSurface: ${label} must be finite, got ${v}`);
|
|
6813
6813
|
}
|
|
6814
6814
|
class NurbsSurface {
|
|
@@ -6834,16 +6834,16 @@ class NurbsSurface {
|
|
|
6834
6834
|
for (let i = 0; i < nU; i++) {
|
|
6835
6835
|
if (controlGrid[i].length !== nV) throw new Error(`nurbsSurface: row ${i} has ${controlGrid[i].length} points, expected ${nV}`);
|
|
6836
6836
|
for (let j = 0; j < nV; j++) {
|
|
6837
|
-
requireFinite$
|
|
6838
|
-
requireFinite$
|
|
6839
|
-
requireFinite$
|
|
6837
|
+
requireFinite$7(controlGrid[i][j][0], `controlGrid[${i}][${j}][0]`);
|
|
6838
|
+
requireFinite$7(controlGrid[i][j][1], `controlGrid[${i}][${j}][1]`);
|
|
6839
|
+
requireFinite$7(controlGrid[i][j][2], `controlGrid[${i}][${j}][2]`);
|
|
6840
6840
|
}
|
|
6841
6841
|
}
|
|
6842
6842
|
const weightsGrid = options.weights ?? controlGrid.map((row) => row.map(() => 1));
|
|
6843
6843
|
for (let i = 0; i < nU; i++) {
|
|
6844
6844
|
if (weightsGrid[i].length !== nV) throw new Error(`nurbsSurface: weights row ${i} length mismatch`);
|
|
6845
6845
|
for (let j = 0; j < nV; j++) {
|
|
6846
|
-
requireFinite$
|
|
6846
|
+
requireFinite$7(weightsGrid[i][j], `weights[${i}][${j}]`);
|
|
6847
6847
|
if (weightsGrid[i][j] <= 0) throw new Error(`nurbsSurface: weights[${i}][${j}] must be > 0`);
|
|
6848
6848
|
}
|
|
6849
6849
|
}
|
|
@@ -8250,7 +8250,7 @@ let _wasm = null;
|
|
|
8250
8250
|
async function initManifoldWasm() {
|
|
8251
8251
|
if (_wasm) return _wasm;
|
|
8252
8252
|
performance.mark("manifold:start");
|
|
8253
|
-
const Module = (await import("./manifold-
|
|
8253
|
+
const Module = (await import("./manifold-CU0G1yYL.js")).default;
|
|
8254
8254
|
performance.mark("manifold:imported");
|
|
8255
8255
|
const wasm = await Module();
|
|
8256
8256
|
wasm.setup();
|
|
@@ -8471,7 +8471,7 @@ function sweepStitched(profilePolygons, pathPoints, up, wasm) {
|
|
|
8471
8471
|
function computeParallelTransportFrames(path2, preferredUp) {
|
|
8472
8472
|
const n = path2.length;
|
|
8473
8473
|
const frames = [];
|
|
8474
|
-
const firstTangent = normalize$6(sub$
|
|
8474
|
+
const firstTangent = normalize$6(sub$4(path2[1], path2[0]));
|
|
8475
8475
|
if (!firstTangent) return null;
|
|
8476
8476
|
let x = normalize$6(cross$3(preferredUp, firstTangent));
|
|
8477
8477
|
if (!x || length(x) < 1e-8) {
|
|
@@ -8485,18 +8485,18 @@ function computeParallelTransportFrames(path2, preferredUp) {
|
|
|
8485
8485
|
const prevT = frames[i - 1].t;
|
|
8486
8486
|
let nextT;
|
|
8487
8487
|
if (i < n - 1) {
|
|
8488
|
-
const t1 = normalize$6(sub$
|
|
8489
|
-
const t2 = normalize$6(sub$
|
|
8488
|
+
const t1 = normalize$6(sub$4(path2[i], path2[i - 1]));
|
|
8489
|
+
const t2 = normalize$6(sub$4(path2[i + 1], path2[i]));
|
|
8490
8490
|
if (!t1 || !t2) return null;
|
|
8491
8491
|
nextT = normalize$6(add$2(t1, t2)) || t1;
|
|
8492
8492
|
} else {
|
|
8493
|
-
const nt = normalize$6(sub$
|
|
8493
|
+
const nt = normalize$6(sub$4(path2[i], path2[i - 1]));
|
|
8494
8494
|
if (!nt) return null;
|
|
8495
8495
|
nextT = nt;
|
|
8496
8496
|
}
|
|
8497
8497
|
const v = cross$3(prevT, nextT);
|
|
8498
8498
|
const vLen = length(v);
|
|
8499
|
-
const c = dot$
|
|
8499
|
+
const c = dot$5(prevT, nextT);
|
|
8500
8500
|
if (vLen > 1e-10) {
|
|
8501
8501
|
const axis = scale$3(v, 1 / vLen);
|
|
8502
8502
|
x = rotateVector(frames[i - 1].x, axis, c, vLen);
|
|
@@ -8570,7 +8570,7 @@ function signedArea$4(loop) {
|
|
|
8570
8570
|
}
|
|
8571
8571
|
return area * 0.5;
|
|
8572
8572
|
}
|
|
8573
|
-
function sub$
|
|
8573
|
+
function sub$4(a, b) {
|
|
8574
8574
|
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
8575
8575
|
}
|
|
8576
8576
|
function add$2(a, b) {
|
|
@@ -8579,7 +8579,7 @@ function add$2(a, b) {
|
|
|
8579
8579
|
function scale$3(v, s) {
|
|
8580
8580
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
8581
8581
|
}
|
|
8582
|
-
function dot$
|
|
8582
|
+
function dot$5(a, b) {
|
|
8583
8583
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
8584
8584
|
}
|
|
8585
8585
|
function cross$3(a, b) {
|
|
@@ -8598,7 +8598,7 @@ function normalize$6(v) {
|
|
|
8598
8598
|
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
8599
8599
|
}
|
|
8600
8600
|
function rotateVector(v, axis, c, s) {
|
|
8601
|
-
const kDotV = dot$
|
|
8601
|
+
const kDotV = dot$5(axis, v);
|
|
8602
8602
|
const kCrossV = cross$3(axis, v);
|
|
8603
8603
|
return [
|
|
8604
8604
|
v[0] * c + kCrossV[0] * s + axis[0] * kDotV * (1 - c),
|
|
@@ -12296,7 +12296,7 @@ function normalizeFaceSelector(selector) {
|
|
|
12296
12296
|
function cross$2(a, b) {
|
|
12297
12297
|
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
12298
12298
|
}
|
|
12299
|
-
function dot$
|
|
12299
|
+
function dot$4(a, b) {
|
|
12300
12300
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
12301
12301
|
}
|
|
12302
12302
|
function normVec3(v) {
|
|
@@ -12331,11 +12331,11 @@ function clusterMeshFaces(shape) {
|
|
|
12331
12331
|
if (!normal) continue;
|
|
12332
12332
|
const crossLen = Math.sqrt(rawCross[0] * rawCross[0] + rawCross[1] * rawCross[1] + rawCross[2] * rawCross[2]);
|
|
12333
12333
|
const triArea = crossLen / 2;
|
|
12334
|
-
const planeOffset = dot$
|
|
12334
|
+
const planeOffset = dot$4(normal, v0);
|
|
12335
12335
|
const triCentroid = [(v0[0] + v1[0] + v2[0]) / 3, (v0[1] + v1[1] + v2[1]) / 3, (v0[2] + v1[2] + v2[2]) / 3];
|
|
12336
12336
|
let merged = false;
|
|
12337
12337
|
for (const c of clusters) {
|
|
12338
|
-
if (dot$
|
|
12338
|
+
if (dot$4(c.normal, normal) > NORMAL_COS_EPS$1 && Math.abs(c.planeOffset - planeOffset) < PLANE_OFFSET_EPS$1) {
|
|
12339
12339
|
c.centroidSum[0] += triCentroid[0];
|
|
12340
12340
|
c.centroidSum[1] += triCentroid[1];
|
|
12341
12341
|
c.centroidSum[2] += triCentroid[2];
|
|
@@ -12377,7 +12377,7 @@ function queryMeshFaces(shape, query) {
|
|
|
12377
12377
|
let clusters = clusterMeshFaces(shape);
|
|
12378
12378
|
if (query.normal) {
|
|
12379
12379
|
const qn = query.normal;
|
|
12380
|
-
clusters = clusters.filter((c) => dot$
|
|
12380
|
+
clusters = clusters.filter((c) => dot$4(c.normal, qn) > NORMAL_COS_EPS$1);
|
|
12381
12381
|
}
|
|
12382
12382
|
if (query.planar !== false) {
|
|
12383
12383
|
clusters = clusters.filter((c) => c.normal !== null);
|
|
@@ -12393,7 +12393,7 @@ function queryMeshFace(shape, query) {
|
|
|
12393
12393
|
let clusters = clusterMeshFaces(shape);
|
|
12394
12394
|
if (query.normal) {
|
|
12395
12395
|
const qn = query.normal;
|
|
12396
|
-
clusters = clusters.filter((c) => dot$
|
|
12396
|
+
clusters = clusters.filter((c) => dot$4(c.normal, qn) > NORMAL_COS_EPS$1);
|
|
12397
12397
|
}
|
|
12398
12398
|
if (query.planar !== false) {
|
|
12399
12399
|
clusters = clusters.filter((c) => c.normal !== null);
|
|
@@ -19341,6 +19341,7 @@ Fix: pass a path like [[0,0,0], [20,0,8,4], [40,0,0,2]].`
|
|
|
19341
19341
|
const curveMode = shouldSmoothCurve(options, defaultCurve) && sourcePoints.length >= 3;
|
|
19342
19342
|
const normalized = compactPathPoints(resampleCurve(sourcePoints, options, defaultCurve));
|
|
19343
19343
|
const minRadius = Math.min(...normalized.map((point2) => point2.radius));
|
|
19344
|
+
const hasExplicitBlend = options.blend !== void 0;
|
|
19344
19345
|
const blendRadius = positiveFinite(options.blend, "Sculpt.tube() blend", Math.max(0.5, minRadius));
|
|
19345
19346
|
let segmentCount = 0;
|
|
19346
19347
|
for (let i = 0; i < normalized.length - 1; i += 1) {
|
|
@@ -19349,8 +19350,8 @@ Fix: pass a path like [[0,0,0], [20,0,8,4], [40,0,0,2]].`
|
|
|
19349
19350
|
if (segmentCount === 0) {
|
|
19350
19351
|
throw new Error("Sculpt.tube() points must include at least one non-zero-length segment.");
|
|
19351
19352
|
}
|
|
19352
|
-
const
|
|
19353
|
-
let out = polylineSweep(normalized,
|
|
19353
|
+
const effectiveBlendRadius = curveMode && !hasExplicitBlend ? Math.max(blendRadius, minRadius * 1.5) : blendRadius;
|
|
19354
|
+
let out = polylineSweep(normalized, effectiveBlendRadius);
|
|
19354
19355
|
if (options.polish !== void 0) out = out.polish(options.polish);
|
|
19355
19356
|
return out;
|
|
19356
19357
|
}
|
|
@@ -21738,7 +21739,7 @@ function intersection(...inputs) {
|
|
|
21738
21739
|
nextPlan
|
|
21739
21740
|
);
|
|
21740
21741
|
}
|
|
21741
|
-
var define_process_env_default = {};
|
|
21742
|
+
var define_process_env_default$2 = {};
|
|
21742
21743
|
let _wasm_solve = null;
|
|
21743
21744
|
let _wasm_get_profile = null;
|
|
21744
21745
|
let _solverMemory = null;
|
|
@@ -21773,7 +21774,7 @@ function readInitialConsoleDebug() {
|
|
|
21773
21774
|
return false;
|
|
21774
21775
|
}
|
|
21775
21776
|
function readEnvFlag(name) {
|
|
21776
|
-
const value = typeof process !== "undefined" ? define_process_env_default == null ? void 0 : define_process_env_default[name] : void 0;
|
|
21777
|
+
const value = typeof process !== "undefined" ? define_process_env_default$2 == null ? void 0 : define_process_env_default$2[name] : void 0;
|
|
21777
21778
|
if (typeof value !== "string") return false;
|
|
21778
21779
|
return ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
21779
21780
|
}
|
|
@@ -23038,7 +23039,7 @@ class MateBuilder {
|
|
|
23038
23039
|
return this.constraints.reduce((sum, c) => sum + (CONSTRAINT_EQUATIONS[c.type] ?? 0), 0);
|
|
23039
23040
|
}
|
|
23040
23041
|
}
|
|
23041
|
-
let _collected$
|
|
23042
|
+
let _collected$8 = null;
|
|
23042
23043
|
const isAxis = (value) => value === "x" || value === "y" || value === "z";
|
|
23043
23044
|
const normalizeDirection = (value, label) => {
|
|
23044
23045
|
if (value === "radial" || isAxis(value)) return value;
|
|
@@ -23097,16 +23098,16 @@ const mergeDirective = (target, patch, label) => {
|
|
|
23097
23098
|
return out;
|
|
23098
23099
|
};
|
|
23099
23100
|
function resetExplodeView() {
|
|
23100
|
-
_collected$
|
|
23101
|
+
_collected$8 = null;
|
|
23101
23102
|
}
|
|
23102
23103
|
function getCollectedExplodeView() {
|
|
23103
|
-
return _collected$
|
|
23104
|
+
return _collected$8 ? cloneOptions(_collected$8) : null;
|
|
23104
23105
|
}
|
|
23105
23106
|
function explodeView(options = {}) {
|
|
23106
23107
|
if (!options || typeof options !== "object") {
|
|
23107
23108
|
throw new Error("explodeView(options) expects an options object");
|
|
23108
23109
|
}
|
|
23109
|
-
const next = _collected$
|
|
23110
|
+
const next = _collected$8 ? cloneOptions(_collected$8) : {};
|
|
23110
23111
|
if (options.enabled !== void 0) {
|
|
23111
23112
|
if (typeof options.enabled !== "boolean") throw new Error("explodeView.enabled must be a boolean");
|
|
23112
23113
|
next.enabled = options.enabled;
|
|
@@ -23154,9 +23155,9 @@ function explodeView(options = {}) {
|
|
|
23154
23155
|
});
|
|
23155
23156
|
next.byPath = byPath;
|
|
23156
23157
|
}
|
|
23157
|
-
_collected$
|
|
23158
|
+
_collected$8 = next;
|
|
23158
23159
|
}
|
|
23159
|
-
let _collected$
|
|
23160
|
+
let _collected$7 = null;
|
|
23160
23161
|
const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
23161
23162
|
const isVec3$1 = (value) => Array.isArray(value) && value.length === 3 && isFiniteNumber(value[0]) && isFiniteNumber(value[1]) && isFiniteNumber(value[2]);
|
|
23162
23163
|
const normalizeAxis = (axis) => {
|
|
@@ -23446,22 +23447,22 @@ const cloneCollected = (value) => ({
|
|
|
23446
23447
|
defaultAnimation: value.defaultAnimation
|
|
23447
23448
|
});
|
|
23448
23449
|
function resetJointsView() {
|
|
23449
|
-
_collected$
|
|
23450
|
+
_collected$7 = null;
|
|
23450
23451
|
}
|
|
23451
23452
|
function getCollectedJointsView() {
|
|
23452
|
-
return _collected$
|
|
23453
|
+
return _collected$7 ? cloneCollected(_collected$7) : null;
|
|
23453
23454
|
}
|
|
23454
23455
|
function saveJointsView() {
|
|
23455
|
-
return _collected$
|
|
23456
|
+
return _collected$7 ? cloneCollected(_collected$7) : null;
|
|
23456
23457
|
}
|
|
23457
23458
|
function restoreJointsView(state) {
|
|
23458
|
-
_collected$
|
|
23459
|
+
_collected$7 = state;
|
|
23459
23460
|
}
|
|
23460
23461
|
function jointsView(options = {}) {
|
|
23461
23462
|
if (!options || typeof options !== "object") {
|
|
23462
23463
|
throw new Error("jointsView(options) expects an options object");
|
|
23463
23464
|
}
|
|
23464
|
-
const next = _collected$
|
|
23465
|
+
const next = _collected$7 ? cloneCollected(_collected$7) : { joints: [], couplings: [], animations: [] };
|
|
23465
23466
|
if (options.enabled !== void 0) {
|
|
23466
23467
|
if (typeof options.enabled !== "boolean") {
|
|
23467
23468
|
throw new Error("jointsView.enabled must be a boolean");
|
|
@@ -23516,8 +23517,14 @@ function jointsView(options = {}) {
|
|
|
23516
23517
|
if (next.defaultAnimation && !next.animations.some((animation) => animation.name === next.defaultAnimation)) {
|
|
23517
23518
|
throw new Error(`jointsView defaultAnimation "${next.defaultAnimation}" does not exist in animations`);
|
|
23518
23519
|
}
|
|
23519
|
-
_collected$
|
|
23520
|
+
_collected$7 = next;
|
|
23520
23521
|
}
|
|
23522
|
+
var define_process_env_default$1 = {};
|
|
23523
|
+
const SWEEP_JOINT_DEFAULT_STEP_LIMIT = {
|
|
23524
|
+
live: 1,
|
|
23525
|
+
default: 4,
|
|
23526
|
+
high: Number.POSITIVE_INFINITY
|
|
23527
|
+
};
|
|
23521
23528
|
let collectedAssemblies = [];
|
|
23522
23529
|
function resetCollectedAssemblies() {
|
|
23523
23530
|
collectedAssemblies = [];
|
|
@@ -23558,6 +23565,33 @@ function collisionShape(part) {
|
|
|
23558
23565
|
if (shapes.length === 1) return shapes[0];
|
|
23559
23566
|
return union(...shapes);
|
|
23560
23567
|
}
|
|
23568
|
+
function boundsOverlap(a, b) {
|
|
23569
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
23570
|
+
if (a.max[axis] <= b.min[axis] || b.max[axis] <= a.min[axis]) return false;
|
|
23571
|
+
}
|
|
23572
|
+
return true;
|
|
23573
|
+
}
|
|
23574
|
+
function readAssemblyPerfEnv(name) {
|
|
23575
|
+
return typeof process !== "undefined" ? define_process_env_default$1 == null ? void 0 : define_process_env_default$1[name] : void 0;
|
|
23576
|
+
}
|
|
23577
|
+
function resolveSweepJointStepLimit() {
|
|
23578
|
+
if (readAssemblyPerfEnv("FORGECAD_ALLOW_FULL_SWEEP_JOINT") === "1") return Number.POSITIVE_INFINITY;
|
|
23579
|
+
const override = readAssemblyPerfEnv("FORGECAD_SWEEP_JOINT_STEP_LIMIT");
|
|
23580
|
+
if (override != null && override.trim() !== "") {
|
|
23581
|
+
const parsed = Number(override);
|
|
23582
|
+
if (Number.isFinite(parsed) && parsed >= 1) return Math.floor(parsed);
|
|
23583
|
+
}
|
|
23584
|
+
return SWEEP_JOINT_DEFAULT_STEP_LIMIT[getForgeQualityPreset()];
|
|
23585
|
+
}
|
|
23586
|
+
function boundSweepJointSteps(jointName, requestedSteps) {
|
|
23587
|
+
const limit = resolveSweepJointStepLimit();
|
|
23588
|
+
if (!Number.isFinite(limit) || requestedSteps <= limit) return requestedSteps;
|
|
23589
|
+
const bounded = Math.max(1, Math.floor(limit));
|
|
23590
|
+
emitRuntimeWarning(
|
|
23591
|
+
`sweepJoint("${jointName}") requested ${requestedSteps} step(s), exceeding the ${getForgeQualityPreset()} quality sweep limit (${bounded}). Using ${bounded} step(s) for responsiveness. Use high quality/export, set FORGECAD_SWEEP_JOINT_STEP_LIMIT, or set FORGECAD_ALLOW_FULL_SWEEP_JOINT=1 for the full sweep.`
|
|
23592
|
+
);
|
|
23593
|
+
return bounded;
|
|
23594
|
+
}
|
|
23561
23595
|
const FASTENER_PATTERN = /\b(bolt|screw|nut|washer|pin|rivet|fastener|standoff|insert)\b/i;
|
|
23562
23596
|
function isFastenerName(name) {
|
|
23563
23597
|
return FASTENER_PATTERN.test(name);
|
|
@@ -23890,16 +23924,23 @@ class SolvedAssembly {
|
|
|
23890
23924
|
const minOverlap = options.minOverlapVolume ?? 0.1;
|
|
23891
23925
|
const ignore = new Set((options.ignorePairs ?? []).map(([a, b]) => [a, b].sort().join("|")));
|
|
23892
23926
|
const findings = [];
|
|
23893
|
-
|
|
23894
|
-
|
|
23895
|
-
|
|
23896
|
-
|
|
23927
|
+
const entries = [];
|
|
23928
|
+
for (const name of names) {
|
|
23929
|
+
const shape = collisionShape(this.getPart(name));
|
|
23930
|
+
if (!shape) continue;
|
|
23931
|
+
try {
|
|
23932
|
+
entries.push({ name, shape, bounds: shape.boundingBox() });
|
|
23933
|
+
} catch {
|
|
23934
|
+
}
|
|
23935
|
+
}
|
|
23936
|
+
for (let i = 0; i < entries.length; i++) {
|
|
23937
|
+
for (let j = i + 1; j < entries.length; j++) {
|
|
23938
|
+
const aName = entries[i].name;
|
|
23939
|
+
const bName = entries[j].name;
|
|
23897
23940
|
if (ignore.has([aName, bName].sort().join("|"))) continue;
|
|
23898
|
-
|
|
23899
|
-
const b = collisionShape(this.getPart(bName));
|
|
23900
|
-
if (!a || !b) continue;
|
|
23941
|
+
if (!boundsOverlap(entries[i].bounds, entries[j].bounds)) continue;
|
|
23901
23942
|
try {
|
|
23902
|
-
const hit =
|
|
23943
|
+
const hit = entries[i].shape.intersect(entries[j].shape);
|
|
23903
23944
|
if (hit.isEmpty()) continue;
|
|
23904
23945
|
const vol = hit.volume();
|
|
23905
23946
|
if (vol > minOverlap) {
|
|
@@ -24876,7 +24917,7 @@ class Assembly {
|
|
|
24876
24917
|
if (this.jointCouplings.has(jointName)) {
|
|
24877
24918
|
throw new Error(`Cannot sweep coupled joint "${jointName}". Sweep one of its source joints instead.`);
|
|
24878
24919
|
}
|
|
24879
|
-
const n = Math.max(1, Math.floor(steps));
|
|
24920
|
+
const n = boundSweepJointSteps(jointName, Math.max(1, Math.floor(steps)));
|
|
24880
24921
|
const frames = [];
|
|
24881
24922
|
for (let i = 0; i <= n; i++) {
|
|
24882
24923
|
const t = n === 0 ? 0 : i / n;
|
|
@@ -25292,12 +25333,12 @@ function bom(quantity, description, opts) {
|
|
|
25292
25333
|
metadata
|
|
25293
25334
|
});
|
|
25294
25335
|
}
|
|
25295
|
-
let _collected$
|
|
25336
|
+
let _collected$6 = [];
|
|
25296
25337
|
function resetCutPlanes() {
|
|
25297
|
-
_collected$
|
|
25338
|
+
_collected$6 = [];
|
|
25298
25339
|
}
|
|
25299
25340
|
function getCollectedCutPlanes() {
|
|
25300
|
-
return _collected$
|
|
25341
|
+
return _collected$6.slice();
|
|
25301
25342
|
}
|
|
25302
25343
|
function normalizeExcludedObjectNames(input) {
|
|
25303
25344
|
if (input === void 0) return void 0;
|
|
@@ -25313,16 +25354,16 @@ function cutPlane(name, normal, offsetOrOptions = 0, maybeOptions = {}) {
|
|
|
25313
25354
|
const offset2 = Number.isFinite(rawOffset) ? rawOffset : 0;
|
|
25314
25355
|
const options = usingOffsetArg ? maybeOptions : offsetOrOptions;
|
|
25315
25356
|
const excludeObjectNames = normalizeExcludedObjectNames(options.exclude);
|
|
25316
|
-
_collected$
|
|
25357
|
+
_collected$6.push({ name, normal, offset: offset2, excludeObjectNames });
|
|
25317
25358
|
}
|
|
25318
|
-
let _collected$
|
|
25359
|
+
let _collected$5 = [];
|
|
25319
25360
|
let _counter$1 = 0;
|
|
25320
25361
|
function resetMocks() {
|
|
25321
|
-
_collected$
|
|
25362
|
+
_collected$5 = [];
|
|
25322
25363
|
_counter$1 = 0;
|
|
25323
25364
|
}
|
|
25324
25365
|
function getCollectedMocks() {
|
|
25325
|
-
return _collected$
|
|
25366
|
+
return _collected$5.slice();
|
|
25326
25367
|
}
|
|
25327
25368
|
function mock(shape, name) {
|
|
25328
25369
|
if (!shape || typeof shape !== "object") {
|
|
@@ -25330,7 +25371,7 @@ function mock(shape, name) {
|
|
|
25330
25371
|
}
|
|
25331
25372
|
_counter$1 += 1;
|
|
25332
25373
|
const displayName = name && typeof name === "string" && name.trim().length > 0 ? name.trim() : `Mock ${_counter$1}`;
|
|
25333
|
-
_collected$
|
|
25374
|
+
_collected$5.push({
|
|
25334
25375
|
id: `mock-${_counter$1}`,
|
|
25335
25376
|
name: displayName,
|
|
25336
25377
|
shape
|
|
@@ -29471,7 +29512,7 @@ function shapeToBounds(shape) {
|
|
|
29471
29512
|
max: [bb.max[0], bb.max[1], bb.max[2]]
|
|
29472
29513
|
};
|
|
29473
29514
|
}
|
|
29474
|
-
function sub$
|
|
29515
|
+
function sub$3(a, b) {
|
|
29475
29516
|
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
29476
29517
|
}
|
|
29477
29518
|
function addVec(a, b) {
|
|
@@ -29480,7 +29521,7 @@ function addVec(a, b) {
|
|
|
29480
29521
|
function scale$2(v, s) {
|
|
29481
29522
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
29482
29523
|
}
|
|
29483
|
-
function dot$
|
|
29524
|
+
function dot$3(a, b) {
|
|
29484
29525
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
29485
29526
|
}
|
|
29486
29527
|
function cross$1(a, b) {
|
|
@@ -29504,9 +29545,9 @@ function pipeRoute(points, radius, options) {
|
|
|
29504
29545
|
const bends = new Array(points.length).fill(null);
|
|
29505
29546
|
for (let i = 1; i < points.length - 1; i++) {
|
|
29506
29547
|
const prev = points[i - 1], cur = points[i], next = points[i + 1];
|
|
29507
|
-
const dIn = normalize$3(sub$
|
|
29508
|
-
const dOut = normalize$3(sub$
|
|
29509
|
-
const dotVal = clampDot(dot$
|
|
29548
|
+
const dIn = normalize$3(sub$3(cur, prev));
|
|
29549
|
+
const dOut = normalize$3(sub$3(next, cur));
|
|
29550
|
+
const dotVal = clampDot(dot$3(dIn, dOut));
|
|
29510
29551
|
const bendAngle = Math.acos(dotVal);
|
|
29511
29552
|
if (bendAngle < 1e-6) {
|
|
29512
29553
|
continue;
|
|
@@ -29525,7 +29566,7 @@ function pipeRoute(points, radius, options) {
|
|
|
29525
29566
|
}
|
|
29526
29567
|
const parts = [];
|
|
29527
29568
|
const makeSeg = (a, b) => {
|
|
29528
|
-
const d = sub$
|
|
29569
|
+
const d = sub$3(b, a);
|
|
29529
29570
|
const len2 = vecLen(d);
|
|
29530
29571
|
if (len2 < 0.01) return null;
|
|
29531
29572
|
const dir = normalize$3(d);
|
|
@@ -29570,7 +29611,7 @@ function pipeRoute(points, radius, options) {
|
|
|
29570
29611
|
const innerBend = buildShapeFromCompilePlan(innerPlan);
|
|
29571
29612
|
bendShape = bendShape.subtract(innerBend);
|
|
29572
29613
|
}
|
|
29573
|
-
const radialDir = normalize$3(sub$
|
|
29614
|
+
const radialDir = normalize$3(sub$3(info.startPt, info.center));
|
|
29574
29615
|
const tangentDir = cross$1(info.axis, radialDir);
|
|
29575
29616
|
const c = info.center;
|
|
29576
29617
|
bendShape = bendShape.transform([
|
|
@@ -29631,7 +29672,7 @@ function elbow(pipeRadius, bendRadius, angle, options) {
|
|
|
29631
29672
|
if (fromDir && toDir) {
|
|
29632
29673
|
const nFrom = normalize$3(fromDir);
|
|
29633
29674
|
const nTo = normalize$3(toDir);
|
|
29634
|
-
const d = clampDot(dot$
|
|
29675
|
+
const d = clampDot(dot$3(nFrom, nTo));
|
|
29635
29676
|
angleDeg = Math.acos(d) * 180 / Math.PI;
|
|
29636
29677
|
}
|
|
29637
29678
|
if (angleDeg < 0.01) throw new Error("elbow: angle too small");
|
|
@@ -29702,20 +29743,20 @@ function assertFinitePositive(apiName, name, value) {
|
|
|
29702
29743
|
function add$1(a, b) {
|
|
29703
29744
|
return [a[0] + b[0], a[1] + b[1]];
|
|
29704
29745
|
}
|
|
29705
|
-
function sub$
|
|
29746
|
+
function sub$2(a, b) {
|
|
29706
29747
|
return [a[0] - b[0], a[1] - b[1]];
|
|
29707
29748
|
}
|
|
29708
29749
|
function scale$1(v, s) {
|
|
29709
29750
|
return [v[0] * s, v[1] * s];
|
|
29710
29751
|
}
|
|
29711
|
-
function dot$
|
|
29752
|
+
function dot$2(a, b) {
|
|
29712
29753
|
return a[0] * b[0] + a[1] * b[1];
|
|
29713
29754
|
}
|
|
29714
29755
|
function len(v) {
|
|
29715
29756
|
return Math.hypot(v[0], v[1]);
|
|
29716
29757
|
}
|
|
29717
29758
|
function dist$2(a, b) {
|
|
29718
|
-
return len(sub$
|
|
29759
|
+
return len(sub$2(b, a));
|
|
29719
29760
|
}
|
|
29720
29761
|
function norm$2(v) {
|
|
29721
29762
|
const l = len(v);
|
|
@@ -29740,7 +29781,7 @@ function chooseSweepDeg(center, start, end, incomingDir) {
|
|
|
29740
29781
|
const endAngle = angleOf(center, end);
|
|
29741
29782
|
const cwTangent = tangentAt(startAngle, true);
|
|
29742
29783
|
const ccwTangent = tangentAt(startAngle, false);
|
|
29743
|
-
const clockwise = dot$
|
|
29784
|
+
const clockwise = dot$2(cwTangent, incomingDir) >= dot$2(ccwTangent, incomingDir);
|
|
29744
29785
|
const sweep2 = clockwise ? -normalizePositiveRadians(startAngle - endAngle) : normalizePositiveRadians(endAngle - startAngle);
|
|
29745
29786
|
return sweep2 * 180 / Math.PI;
|
|
29746
29787
|
}
|
|
@@ -29783,8 +29824,8 @@ function normalizePulleyAsCircle(pulley, index, radiusOverride) {
|
|
|
29783
29824
|
);
|
|
29784
29825
|
}
|
|
29785
29826
|
function commonTangents(a, b, mode) {
|
|
29786
|
-
const delta = sub$
|
|
29787
|
-
const z = dot$
|
|
29827
|
+
const delta = sub$2(b.center, a.center);
|
|
29828
|
+
const z = dot$2(delta, delta);
|
|
29788
29829
|
if (z < EPS$5) {
|
|
29789
29830
|
throw new Error(`beltDrive: pulleys "${a.name}" and "${b.name}" have the same center.`);
|
|
29790
29831
|
}
|
|
@@ -29814,8 +29855,8 @@ function commonTangents(a, b, mode) {
|
|
|
29814
29855
|
});
|
|
29815
29856
|
}
|
|
29816
29857
|
function buildSegmentsForTangentOrder(a, b, t0, t1) {
|
|
29817
|
-
const span0Dir = norm$2(sub$
|
|
29818
|
-
const span1Dir = norm$2(sub$
|
|
29858
|
+
const span0Dir = norm$2(sub$2(t0.b, t0.a));
|
|
29859
|
+
const span1Dir = norm$2(sub$2(t1.a, t1.b));
|
|
29819
29860
|
const bSweepDeg = chooseSweepDeg(b.center, t0.b, t1.b, span0Dir);
|
|
29820
29861
|
const aSweepDeg = chooseSweepDeg(a.center, t1.a, t0.a, span1Dir);
|
|
29821
29862
|
const span0 = {
|
|
@@ -40252,6 +40293,130 @@ function shapeToGeometryFallback(shape) {
|
|
|
40252
40293
|
const edges = new EdgesGeometry(solid, 1);
|
|
40253
40294
|
return { solid, edges, hasSmoothNormals: false };
|
|
40254
40295
|
}
|
|
40296
|
+
let _collected$4 = [];
|
|
40297
|
+
let _nextId = 1;
|
|
40298
|
+
function resetRenderLabels() {
|
|
40299
|
+
_collected$4 = [];
|
|
40300
|
+
_nextId = 1;
|
|
40301
|
+
}
|
|
40302
|
+
function getCollectedRenderLabels() {
|
|
40303
|
+
return _collected$4.map((label) => ({ ...label, at: [...label.at], offset: [...label.offset] }));
|
|
40304
|
+
}
|
|
40305
|
+
function requireFinite$6(value, label) {
|
|
40306
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
40307
|
+
throw new Error(`${label} must be a finite number`);
|
|
40308
|
+
}
|
|
40309
|
+
return value;
|
|
40310
|
+
}
|
|
40311
|
+
function requireVec3$2(value, label) {
|
|
40312
|
+
if (!Array.isArray(value) || value.length !== 3) {
|
|
40313
|
+
throw new Error(`${label} must be [x, y, z]`);
|
|
40314
|
+
}
|
|
40315
|
+
return [requireFinite$6(value[0], `${label}[0]`), requireFinite$6(value[1], `${label}[1]`), requireFinite$6(value[2], `${label}[2]`)];
|
|
40316
|
+
}
|
|
40317
|
+
function optionalColor(value, label) {
|
|
40318
|
+
if (value === void 0) return void 0;
|
|
40319
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
40320
|
+
throw new Error(`${label} must be a non-empty CSS color string`);
|
|
40321
|
+
}
|
|
40322
|
+
return value.trim();
|
|
40323
|
+
}
|
|
40324
|
+
const VALID_ANCHORS = /* @__PURE__ */ new Set([
|
|
40325
|
+
"center",
|
|
40326
|
+
"top",
|
|
40327
|
+
"bottom",
|
|
40328
|
+
"left",
|
|
40329
|
+
"right",
|
|
40330
|
+
"top-left",
|
|
40331
|
+
"top-right",
|
|
40332
|
+
"bottom-left",
|
|
40333
|
+
"bottom-right"
|
|
40334
|
+
]);
|
|
40335
|
+
function normalizeOptions(options) {
|
|
40336
|
+
if (options === void 0) {
|
|
40337
|
+
return { offset: [0, 0, 0], anchor: "center", alwaysOnTop: true };
|
|
40338
|
+
}
|
|
40339
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
40340
|
+
throw new Error("Viewport.label options must be an object");
|
|
40341
|
+
}
|
|
40342
|
+
const out = {
|
|
40343
|
+
offset: [0, 0, 0],
|
|
40344
|
+
anchor: "center",
|
|
40345
|
+
alwaysOnTop: true
|
|
40346
|
+
};
|
|
40347
|
+
const color = optionalColor(options.color, "Viewport.label options.color");
|
|
40348
|
+
if (color !== void 0) out.color = color;
|
|
40349
|
+
const background = optionalColor(options.background, "Viewport.label options.background");
|
|
40350
|
+
if (background !== void 0) out.background = background;
|
|
40351
|
+
if (options.size !== void 0) {
|
|
40352
|
+
out.size = requireFinite$6(options.size, "Viewport.label options.size");
|
|
40353
|
+
if (out.size <= 0) throw new Error("Viewport.label options.size must be positive");
|
|
40354
|
+
}
|
|
40355
|
+
if (options.offset !== void 0) out.offset = requireVec3$2(options.offset, "Viewport.label options.offset");
|
|
40356
|
+
if (options.anchor !== void 0) {
|
|
40357
|
+
if (!VALID_ANCHORS.has(options.anchor)) {
|
|
40358
|
+
throw new Error(`Viewport.label options.anchor must be one of: ${Array.from(VALID_ANCHORS).join(", ")}`);
|
|
40359
|
+
}
|
|
40360
|
+
out.anchor = options.anchor;
|
|
40361
|
+
}
|
|
40362
|
+
if (options.alwaysOnTop !== void 0) {
|
|
40363
|
+
if (typeof options.alwaysOnTop !== "boolean") throw new Error("Viewport.label options.alwaysOnTop must be a boolean");
|
|
40364
|
+
out.alwaysOnTop = options.alwaysOnTop;
|
|
40365
|
+
}
|
|
40366
|
+
return out;
|
|
40367
|
+
}
|
|
40368
|
+
function collectRenderLabel(text, at, options) {
|
|
40369
|
+
if (typeof text !== "string" || text.trim().length === 0) {
|
|
40370
|
+
throw new Error("Viewport.label text must be a non-empty string");
|
|
40371
|
+
}
|
|
40372
|
+
const normalizedAt = requireVec3$2(at, "Viewport.label at");
|
|
40373
|
+
const normalizedOptions = normalizeOptions(options);
|
|
40374
|
+
_collected$4.push({
|
|
40375
|
+
id: `render-label-${_nextId++}`,
|
|
40376
|
+
text: text.trim(),
|
|
40377
|
+
at: normalizedAt,
|
|
40378
|
+
...normalizedOptions
|
|
40379
|
+
});
|
|
40380
|
+
}
|
|
40381
|
+
const Viewport = {
|
|
40382
|
+
/**
|
|
40383
|
+
* Add a render-only viewport label at a world-space point.
|
|
40384
|
+
*
|
|
40385
|
+
* **Details**
|
|
40386
|
+
*
|
|
40387
|
+
* `Viewport.label()` is for explanatory text that helps a viewer understand
|
|
40388
|
+
* the model. It does not create sketches, meshes, B-rep topology, exported
|
|
40389
|
+
* text, or face labels, so it stays off the OCCT path. Use `text2d()` only
|
|
40390
|
+
* when the letters should become manufactured geometry, such as raised
|
|
40391
|
+
* lettering, engraved serial numbers, or exported nameplates.
|
|
40392
|
+
*
|
|
40393
|
+
* Labels are collected during script execution and rendered by the viewport
|
|
40394
|
+
* as lightweight overlay annotations. They are ignored by exports and do not
|
|
40395
|
+
* appear in `objects`.
|
|
40396
|
+
*
|
|
40397
|
+
* **Example**
|
|
40398
|
+
*
|
|
40399
|
+
* ```js
|
|
40400
|
+
* Viewport.label('Bearing bore', [0, 0, 18], {
|
|
40401
|
+
* color: '#f8fafc',
|
|
40402
|
+
* background: '#0f172acc',
|
|
40403
|
+
* offset: [0, 0, 8],
|
|
40404
|
+
* anchor: 'bottom',
|
|
40405
|
+
* });
|
|
40406
|
+
*
|
|
40407
|
+
* return box(40, 30, 12);
|
|
40408
|
+
* ```
|
|
40409
|
+
*
|
|
40410
|
+
* @param text - Label text to display in the viewport
|
|
40411
|
+
* @param at - World-space anchor point `[x, y, z]`
|
|
40412
|
+
* @param options - Visual label options
|
|
40413
|
+
* @returns void
|
|
40414
|
+
* @category Viewport Labels
|
|
40415
|
+
*/
|
|
40416
|
+
label(text, at, options) {
|
|
40417
|
+
collectRenderLabel(text, at, options);
|
|
40418
|
+
}
|
|
40419
|
+
};
|
|
40255
40420
|
const RENDER_STYLE_OPTIONS = [
|
|
40256
40421
|
{
|
|
40257
40422
|
id: "classic",
|
|
@@ -40438,6 +40603,120 @@ function validateCamera(cam, label) {
|
|
|
40438
40603
|
}
|
|
40439
40604
|
return out;
|
|
40440
40605
|
}
|
|
40606
|
+
function validateViewCamera(cam, label) {
|
|
40607
|
+
const validated = validateCamera(cam, label);
|
|
40608
|
+
if (!validated.position) {
|
|
40609
|
+
throw new Error(`${label}.position is required for named render views`);
|
|
40610
|
+
}
|
|
40611
|
+
if (!validated.target) {
|
|
40612
|
+
throw new Error(`${label}.target is required for named render views`);
|
|
40613
|
+
}
|
|
40614
|
+
return {
|
|
40615
|
+
...validated,
|
|
40616
|
+
position: validated.position,
|
|
40617
|
+
target: validated.target
|
|
40618
|
+
};
|
|
40619
|
+
}
|
|
40620
|
+
function validateViews(views, label) {
|
|
40621
|
+
if (!views || typeof views !== "object" || Array.isArray(views)) {
|
|
40622
|
+
throw new Error(`${label} must be an object mapping view names to cameras`);
|
|
40623
|
+
}
|
|
40624
|
+
const out = {};
|
|
40625
|
+
for (const [name, view] of Object.entries(views)) {
|
|
40626
|
+
if (!name.trim()) {
|
|
40627
|
+
throw new Error(`${label} names must be non-empty strings`);
|
|
40628
|
+
}
|
|
40629
|
+
const viewLabel = `${label}.${name}`;
|
|
40630
|
+
if (!view || typeof view !== "object" || Array.isArray(view)) {
|
|
40631
|
+
throw new Error(`${viewLabel} must be a camera object or an object with a camera property`);
|
|
40632
|
+
}
|
|
40633
|
+
const hasExplicitCamera = Object.prototype.hasOwnProperty.call(view, "camera");
|
|
40634
|
+
if (hasExplicitCamera) {
|
|
40635
|
+
const camera = view.camera;
|
|
40636
|
+
if (!camera || typeof camera !== "object" || Array.isArray(camera)) {
|
|
40637
|
+
throw new Error(`${viewLabel}.camera must be an object`);
|
|
40638
|
+
}
|
|
40639
|
+
out[name] = { camera: validateViewCamera(camera, `${viewLabel}.camera`) };
|
|
40640
|
+
continue;
|
|
40641
|
+
}
|
|
40642
|
+
out[name] = { camera: validateViewCamera(view, viewLabel) };
|
|
40643
|
+
}
|
|
40644
|
+
return out;
|
|
40645
|
+
}
|
|
40646
|
+
function requireString(value, label) {
|
|
40647
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
40648
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
40649
|
+
}
|
|
40650
|
+
return value.trim();
|
|
40651
|
+
}
|
|
40652
|
+
function optionalString(value, label) {
|
|
40653
|
+
if (value === void 0) return void 0;
|
|
40654
|
+
return requireString(value, label);
|
|
40655
|
+
}
|
|
40656
|
+
function validateJourneyStep(step, label) {
|
|
40657
|
+
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
40658
|
+
throw new Error(`${label} must be an object`);
|
|
40659
|
+
}
|
|
40660
|
+
const out = {
|
|
40661
|
+
id: requireString(step.id, `${label}.id`)
|
|
40662
|
+
};
|
|
40663
|
+
const title = optionalString(step.title, `${label}.title`);
|
|
40664
|
+
if (title !== void 0) out.title = title;
|
|
40665
|
+
const focus = optionalString(step.focus, `${label}.focus`);
|
|
40666
|
+
if (focus !== void 0) out.focus = focus;
|
|
40667
|
+
const caption = optionalString(step.caption, `${label}.caption`);
|
|
40668
|
+
if (caption !== void 0) out.caption = caption;
|
|
40669
|
+
if (step.camera !== void 0) {
|
|
40670
|
+
if (!step.camera || typeof step.camera !== "object" || Array.isArray(step.camera)) {
|
|
40671
|
+
throw new Error(`${label}.camera must be an object`);
|
|
40672
|
+
}
|
|
40673
|
+
out.camera = validateViewCamera(step.camera, `${label}.camera`);
|
|
40674
|
+
}
|
|
40675
|
+
return out;
|
|
40676
|
+
}
|
|
40677
|
+
function validateJourney(journey, label) {
|
|
40678
|
+
if (!journey || typeof journey !== "object" || Array.isArray(journey)) {
|
|
40679
|
+
throw new Error(`${label} must be an object`);
|
|
40680
|
+
}
|
|
40681
|
+
if (!Array.isArray(journey.steps) || journey.steps.length === 0) {
|
|
40682
|
+
throw new Error(`${label}.steps must be a non-empty array`);
|
|
40683
|
+
}
|
|
40684
|
+
const out = {
|
|
40685
|
+
steps: journey.steps.map((step, index) => validateJourneyStep(step, `${label}.steps[${index}]`))
|
|
40686
|
+
};
|
|
40687
|
+
const title = optionalString(journey.title, `${label}.title`);
|
|
40688
|
+
if (title !== void 0) out.title = title;
|
|
40689
|
+
const startsAt = optionalString(journey.startsAt, `${label}.startsAt`);
|
|
40690
|
+
if (startsAt !== void 0) out.startsAt = startsAt;
|
|
40691
|
+
if (journey.behavior !== void 0) {
|
|
40692
|
+
if (journey.behavior !== "opt-in" && journey.behavior !== "auto") {
|
|
40693
|
+
throw new Error(`${label}.behavior must be "opt-in" or "auto"`);
|
|
40694
|
+
}
|
|
40695
|
+
out.behavior = journey.behavior;
|
|
40696
|
+
}
|
|
40697
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40698
|
+
for (const step of out.steps) {
|
|
40699
|
+
if (seen.has(step.id)) {
|
|
40700
|
+
throw new Error(`${label}.steps contains duplicate step id "${step.id}"`);
|
|
40701
|
+
}
|
|
40702
|
+
seen.add(step.id);
|
|
40703
|
+
}
|
|
40704
|
+
if (out.startsAt && !seen.has(out.startsAt)) {
|
|
40705
|
+
throw new Error(`${label}.startsAt "${out.startsAt}" does not match any step id`);
|
|
40706
|
+
}
|
|
40707
|
+
return out;
|
|
40708
|
+
}
|
|
40709
|
+
function validateJourneys(journeys, label) {
|
|
40710
|
+
if (!journeys || typeof journeys !== "object" || Array.isArray(journeys)) {
|
|
40711
|
+
throw new Error(`${label} must be an object mapping journey ids to journey configs`);
|
|
40712
|
+
}
|
|
40713
|
+
const out = {};
|
|
40714
|
+
for (const [id, journey] of Object.entries(journeys)) {
|
|
40715
|
+
const normalizedId = requireString(id, `${label} journey id`);
|
|
40716
|
+
out[normalizedId] = validateJourney(journey, `${label}.${normalizedId}`);
|
|
40717
|
+
}
|
|
40718
|
+
return out;
|
|
40719
|
+
}
|
|
40441
40720
|
function validateLight(light, label) {
|
|
40442
40721
|
if (!light || typeof light !== "object") throw new Error(`${label} must be an object`);
|
|
40443
40722
|
if (!VALID_LIGHT_TYPES.has(light.type)) {
|
|
@@ -40581,7 +40860,18 @@ function scene(options) {
|
|
|
40581
40860
|
if (!options || typeof options !== "object") {
|
|
40582
40861
|
throw new Error("scene(options) expects an options object");
|
|
40583
40862
|
}
|
|
40584
|
-
const current = _collected$3 ? { ..._collected$3 } : {
|
|
40863
|
+
const current = _collected$3 ? { ..._collected$3 } : {
|
|
40864
|
+
background: null,
|
|
40865
|
+
camera: null,
|
|
40866
|
+
views: null,
|
|
40867
|
+
journeys: null,
|
|
40868
|
+
lights: null,
|
|
40869
|
+
environment: null,
|
|
40870
|
+
fog: null,
|
|
40871
|
+
postProcessing: null,
|
|
40872
|
+
ground: null,
|
|
40873
|
+
capture: null
|
|
40874
|
+
};
|
|
40585
40875
|
if (options.background !== void 0) {
|
|
40586
40876
|
current.background = validateBackground(options.background, "scene.background");
|
|
40587
40877
|
}
|
|
@@ -40592,6 +40882,14 @@ function scene(options) {
|
|
|
40592
40882
|
const validated = validateCamera(options.camera, "scene.camera");
|
|
40593
40883
|
current.camera = current.camera ? { ...current.camera, ...validated } : validated;
|
|
40594
40884
|
}
|
|
40885
|
+
if (options.views !== void 0) {
|
|
40886
|
+
const validated = validateViews(options.views, "scene.views");
|
|
40887
|
+
current.views = current.views ? { ...current.views, ...validated } : validated;
|
|
40888
|
+
}
|
|
40889
|
+
if (options.journeys !== void 0) {
|
|
40890
|
+
const validated = validateJourneys(options.journeys, "scene.journeys");
|
|
40891
|
+
current.journeys = current.journeys ? { ...current.journeys, ...validated } : validated;
|
|
40892
|
+
}
|
|
40595
40893
|
if (options.lights !== void 0) {
|
|
40596
40894
|
if (!Array.isArray(options.lights)) {
|
|
40597
40895
|
throw new Error("scene.lights must be an array");
|
|
@@ -40632,6 +40930,74 @@ function scene(options) {
|
|
|
40632
40930
|
}
|
|
40633
40931
|
_collected$3 = current;
|
|
40634
40932
|
}
|
|
40933
|
+
const targetPath = (target) => {
|
|
40934
|
+
var _a3;
|
|
40935
|
+
const path2 = (_a3 = target.treePath) == null ? void 0 : _a3.filter((entry) => entry.trim());
|
|
40936
|
+
return path2 && path2.length > 0 ? path2.join("/") : target.name;
|
|
40937
|
+
};
|
|
40938
|
+
const hasErrorDiagnostic = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.level === "error");
|
|
40939
|
+
function resolveJourneyFocus(focus, targets) {
|
|
40940
|
+
return targets.filter((target) => target.name === focus || targetPath(target) === focus);
|
|
40941
|
+
}
|
|
40942
|
+
function formatAvailableTargets(targets) {
|
|
40943
|
+
return targets.map((target) => targetPath(target)).filter((value, index, values) => values.indexOf(value) === index).sort().slice(0, 8);
|
|
40944
|
+
}
|
|
40945
|
+
function resolveSceneJourneyTargets(config, targets) {
|
|
40946
|
+
if (!(config == null ? void 0 : config.journeys)) return config;
|
|
40947
|
+
const journeys = {};
|
|
40948
|
+
for (const [journeyId, journey] of Object.entries(config.journeys)) {
|
|
40949
|
+
const journeyDiagnostics = [...journey.diagnostics ?? []];
|
|
40950
|
+
const steps = journey.steps.map((step) => {
|
|
40951
|
+
const stepDiagnostics = [...step.diagnostics ?? []];
|
|
40952
|
+
let resolvedFocusId = null;
|
|
40953
|
+
let resolvedFocusPath = null;
|
|
40954
|
+
if (step.focus) {
|
|
40955
|
+
const matches = resolveJourneyFocus(step.focus, targets);
|
|
40956
|
+
if (matches.length === 1) {
|
|
40957
|
+
resolvedFocusId = matches[0].id;
|
|
40958
|
+
resolvedFocusPath = targetPath(matches[0]);
|
|
40959
|
+
} else if (matches.length === 0) {
|
|
40960
|
+
stepDiagnostics.push({
|
|
40961
|
+
level: "error",
|
|
40962
|
+
stepId: step.id,
|
|
40963
|
+
message: `focus "${step.focus}" did not match any returned object by name or tree path.`,
|
|
40964
|
+
suggestions: formatAvailableTargets(targets)
|
|
40965
|
+
});
|
|
40966
|
+
} else {
|
|
40967
|
+
stepDiagnostics.push({
|
|
40968
|
+
level: "error",
|
|
40969
|
+
stepId: step.id,
|
|
40970
|
+
message: `focus "${step.focus}" matched ${matches.length} objects. Use a slash-separated tree path.`,
|
|
40971
|
+
suggestions: matches.map((match) => targetPath(match))
|
|
40972
|
+
});
|
|
40973
|
+
}
|
|
40974
|
+
} else if (!step.camera) {
|
|
40975
|
+
stepDiagnostics.push({
|
|
40976
|
+
level: "warning",
|
|
40977
|
+
stepId: step.id,
|
|
40978
|
+
message: "step has no focus or explicit camera, so the viewer can show the caption but cannot move the camera."
|
|
40979
|
+
});
|
|
40980
|
+
}
|
|
40981
|
+
journeyDiagnostics.push(...stepDiagnostics);
|
|
40982
|
+
return {
|
|
40983
|
+
...step,
|
|
40984
|
+
resolvedFocusId,
|
|
40985
|
+
resolvedFocusPath,
|
|
40986
|
+
diagnostics: stepDiagnostics.length > 0 ? stepDiagnostics : void 0
|
|
40987
|
+
};
|
|
40988
|
+
});
|
|
40989
|
+
journeys[journeyId] = {
|
|
40990
|
+
...journey,
|
|
40991
|
+
steps,
|
|
40992
|
+
valid: !hasErrorDiagnostic(journeyDiagnostics),
|
|
40993
|
+
diagnostics: journeyDiagnostics.length > 0 ? journeyDiagnostics : void 0
|
|
40994
|
+
};
|
|
40995
|
+
}
|
|
40996
|
+
return {
|
|
40997
|
+
...config,
|
|
40998
|
+
journeys
|
|
40999
|
+
};
|
|
41000
|
+
}
|
|
40635
41001
|
const validateColor = (value, label) => {
|
|
40636
41002
|
if (typeof value !== "string") throw new Error(`${label} must be a string`);
|
|
40637
41003
|
const trimmed = value.trim();
|
|
@@ -41877,9 +42243,15 @@ function cross(a, b) {
|
|
|
41877
42243
|
function add(a, b) {
|
|
41878
42244
|
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
41879
42245
|
}
|
|
42246
|
+
function sub$1(a, b) {
|
|
42247
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
42248
|
+
}
|
|
41880
42249
|
function scale(v, s) {
|
|
41881
42250
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
41882
42251
|
}
|
|
42252
|
+
function dot$1(a, b) {
|
|
42253
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
42254
|
+
}
|
|
41883
42255
|
function lerp$1(a, b, t) {
|
|
41884
42256
|
return a + (b - a) * t;
|
|
41885
42257
|
}
|
|
@@ -41912,6 +42284,65 @@ function sideVectors(axis) {
|
|
|
41912
42284
|
function cloneQuery(query) {
|
|
41913
42285
|
return { side: query.side, u: query.u, v: query.v, offset: query.offset };
|
|
41914
42286
|
}
|
|
42287
|
+
function normalizedSide(side) {
|
|
42288
|
+
return side === "back" ? "rear" : side;
|
|
42289
|
+
}
|
|
42290
|
+
function profileExponent(station) {
|
|
42291
|
+
if (station.profile.kind === "superEllipse") return station.profile.exponent ?? 3.2;
|
|
42292
|
+
if (station.profile.kind === "roundedRect") return 4.5;
|
|
42293
|
+
return 2;
|
|
42294
|
+
}
|
|
42295
|
+
function superEllipsePoint(rx, ry, exponent, angle) {
|
|
42296
|
+
const cos2 = Math.cos(angle);
|
|
42297
|
+
const sin2 = Math.sin(angle);
|
|
42298
|
+
const x = rx * Math.sign(cos2) * Math.abs(cos2) ** (2 / exponent);
|
|
42299
|
+
const y = ry * Math.sign(sin2) * Math.abs(sin2) ** (2 / exponent);
|
|
42300
|
+
const nx = Math.sign(x) * Math.abs(x / Math.max(rx, EPS$4)) ** Math.max(exponent - 1, 1e-3);
|
|
42301
|
+
const ny = Math.sign(y) * Math.abs(y / Math.max(ry, EPS$4)) ** Math.max(exponent - 1, 1e-3);
|
|
42302
|
+
const nLen = Math.hypot(nx, ny);
|
|
42303
|
+
return {
|
|
42304
|
+
point: [x, y],
|
|
42305
|
+
normal: nLen < EPS$4 ? [Math.sign(cos2), Math.sign(sin2)] : [nx / nLen, ny / nLen]
|
|
42306
|
+
};
|
|
42307
|
+
}
|
|
42308
|
+
function angleForSide(side, u) {
|
|
42309
|
+
const t = clamp$5(u, 0, 1);
|
|
42310
|
+
if (side === "right") return -Math.PI / 2 + t * Math.PI;
|
|
42311
|
+
if (side === "left") return Math.PI / 2 + t * Math.PI;
|
|
42312
|
+
if (side === "top") return Math.PI - t * Math.PI;
|
|
42313
|
+
if (side === "bottom") return Math.PI + t * Math.PI;
|
|
42314
|
+
return null;
|
|
42315
|
+
}
|
|
42316
|
+
function sideSpan(side, width, depth) {
|
|
42317
|
+
if (side === "left" || side === "right") return Math.max(depth, EPS$4);
|
|
42318
|
+
if (side === "top" || side === "bottom") return Math.max(width, EPS$4);
|
|
42319
|
+
return Math.max(width, depth, EPS$4);
|
|
42320
|
+
}
|
|
42321
|
+
function interpolateQuery(a, b, t) {
|
|
42322
|
+
const sideA = normalizedSide(a.side);
|
|
42323
|
+
const sideB = normalizedSide(b.side);
|
|
42324
|
+
if (sideA !== sideB) {
|
|
42325
|
+
throw new Error(
|
|
42326
|
+
`Product.ribbon().on(...) currently samples one skin side per ribbon; got '${a.side}' then '${b.side}'. Split this into separate ribbons at the side transition.`
|
|
42327
|
+
);
|
|
42328
|
+
}
|
|
42329
|
+
return {
|
|
42330
|
+
side: sideA,
|
|
42331
|
+
u: lerp$1(a.u ?? 0.5, b.u ?? 0.5, t),
|
|
42332
|
+
v: lerp$1(a.v ?? 0.5, b.v ?? 0.5, t),
|
|
42333
|
+
offset: lerp$1(a.offset ?? 0, b.offset ?? 0, t)
|
|
42334
|
+
};
|
|
42335
|
+
}
|
|
42336
|
+
function resolvePathQueries(points) {
|
|
42337
|
+
return points.map((point2) => point2 instanceof ProductSurfaceRef ? point2.querySpec() : cloneQuery(point2));
|
|
42338
|
+
}
|
|
42339
|
+
function orientGridToNormal(grid, desiredNormal) {
|
|
42340
|
+
if (grid.length < 2 || grid[0].length < 2) return grid;
|
|
42341
|
+
const widthEdge = sub$1(grid[grid.length - 1][0], grid[0][0]);
|
|
42342
|
+
const lengthEdge = sub$1(grid[0][grid[0].length - 1], grid[0][0]);
|
|
42343
|
+
const actual = norm$1(cross(widthEdge, lengthEdge));
|
|
42344
|
+
return dot$1(actual, desiredNormal) < 0 ? [...grid].reverse() : grid;
|
|
42345
|
+
}
|
|
41915
42346
|
function isStationBuilder(input) {
|
|
41916
42347
|
return typeof input.toSpec === "function";
|
|
41917
42348
|
}
|
|
@@ -42013,6 +42444,15 @@ class ProductSkin {
|
|
|
42013
42444
|
curveOnSurface(name, points) {
|
|
42014
42445
|
return points.map((point2, index) => new ProductSurfaceRef(this, { u: 0.5, v: 0.5, ...point2 }, `${name}/${index}`));
|
|
42015
42446
|
}
|
|
42447
|
+
/**
|
|
42448
|
+
* Create a fluent surface helper for refs and conformal features on one side of this skin.
|
|
42449
|
+
*
|
|
42450
|
+
* Use this when several refs or ribbons share the same skin side; side-local helpers keep
|
|
42451
|
+
* path points concise and make it harder to mix sides accidentally.
|
|
42452
|
+
*/
|
|
42453
|
+
surface(side) {
|
|
42454
|
+
return new ProductSurfaceBuilder(this, side);
|
|
42455
|
+
}
|
|
42016
42456
|
/** Interpolate center, width, and depth at a normalized v or absolute axis value. */
|
|
42017
42457
|
stationAt(vOrAxis) {
|
|
42018
42458
|
const axisValue = vOrAxis >= 0 && vOrAxis <= 1 ? lerp$1(this.axisMin, this.axisMax, vOrAxis) : clamp$5(vOrAxis, this.axisMin, this.axisMax);
|
|
@@ -42031,7 +42471,9 @@ class ProductSkin {
|
|
|
42031
42471
|
width: lerp$1(a.profile.width, b.profile.width, t),
|
|
42032
42472
|
depth: lerp$1(a.profile.depth, b.profile.depth, t),
|
|
42033
42473
|
dWidth: (b.profile.width - a.profile.width) / span,
|
|
42034
|
-
dDepth: (b.profile.depth - a.profile.depth) / span
|
|
42474
|
+
dDepth: (b.profile.depth - a.profile.depth) / span,
|
|
42475
|
+
exponent: lerp$1(profileExponent(a), profileExponent(b), t),
|
|
42476
|
+
kind: a.profile.kind === b.profile.kind ? a.profile.kind : "custom"
|
|
42035
42477
|
};
|
|
42036
42478
|
}
|
|
42037
42479
|
const last = sorted[sorted.length - 1];
|
|
@@ -42041,12 +42483,14 @@ class ProductSkin {
|
|
|
42041
42483
|
width: last.profile.width,
|
|
42042
42484
|
depth: last.profile.depth,
|
|
42043
42485
|
dWidth: 0,
|
|
42044
|
-
dDepth: 0
|
|
42486
|
+
dDepth: 0,
|
|
42487
|
+
exponent: profileExponent(last),
|
|
42488
|
+
kind: last.profile.kind
|
|
42045
42489
|
};
|
|
42046
42490
|
}
|
|
42047
42491
|
/** Build a local surface frame from a side/u/v query. */
|
|
42048
42492
|
frame(query) {
|
|
42049
|
-
const side = query.side
|
|
42493
|
+
const side = normalizedSide(query.side);
|
|
42050
42494
|
const offset2 = query.offset ?? 0;
|
|
42051
42495
|
const basis = sideVectors(this.axis);
|
|
42052
42496
|
const isFrontCap = side === "front";
|
|
@@ -42070,11 +42514,31 @@ class ProductSkin {
|
|
|
42070
42514
|
}
|
|
42071
42515
|
const station = this.stationAt(query.v ?? 0.5);
|
|
42072
42516
|
const u = clamp$5(query.u ?? 0.5, 0, 1) - 0.5;
|
|
42517
|
+
const sideAngle = angleForSide(side, query.u ?? 0.5);
|
|
42073
42518
|
let crossA = 0;
|
|
42074
42519
|
let crossB = 0;
|
|
42075
42520
|
let normal = [0, 0, 1];
|
|
42076
42521
|
let tangentU = basis.crossA;
|
|
42077
|
-
if (
|
|
42522
|
+
if (sideAngle != null) {
|
|
42523
|
+
const section = superEllipsePoint(station.width / 2, station.depth / 2, station.exponent, sideAngle);
|
|
42524
|
+
crossA = section.point[0];
|
|
42525
|
+
crossB = section.point[1];
|
|
42526
|
+
normal = norm$1(add(scale(basis.crossA, section.normal[0]), scale(basis.crossB, section.normal[1])));
|
|
42527
|
+
const delta = 2e-3;
|
|
42528
|
+
const prev = superEllipsePoint(
|
|
42529
|
+
station.width / 2,
|
|
42530
|
+
station.depth / 2,
|
|
42531
|
+
station.exponent,
|
|
42532
|
+
angleForSide(side, clamp$5((query.u ?? 0.5) - delta, 0, 1)) ?? sideAngle
|
|
42533
|
+
).point;
|
|
42534
|
+
const next = superEllipsePoint(
|
|
42535
|
+
station.width / 2,
|
|
42536
|
+
station.depth / 2,
|
|
42537
|
+
station.exponent,
|
|
42538
|
+
angleForSide(side, clamp$5((query.u ?? 0.5) + delta, 0, 1)) ?? sideAngle
|
|
42539
|
+
).point;
|
|
42540
|
+
tangentU = norm$1(add(scale(basis.crossA, next[0] - prev[0]), scale(basis.crossB, next[1] - prev[1])));
|
|
42541
|
+
} else if (side === "right") {
|
|
42078
42542
|
crossA = station.width / 2;
|
|
42079
42543
|
crossB = u * station.depth;
|
|
42080
42544
|
normal = basis.crossA;
|
|
@@ -42096,7 +42560,7 @@ class ProductSkin {
|
|
|
42096
42560
|
tangentU = basis.crossA;
|
|
42097
42561
|
}
|
|
42098
42562
|
normal = norm$1(normal);
|
|
42099
|
-
tangentU = norm$1(tangentU);
|
|
42563
|
+
tangentU = norm$1(sub$1(tangentU, scale(normal, dot$1(tangentU, normal))));
|
|
42100
42564
|
const tangentV = norm$1(cross(normal, tangentU));
|
|
42101
42565
|
const point2 = add(add(station.center, add(scale(basis.crossA, crossA), scale(basis.crossB, crossB))), scale(normal, offset2));
|
|
42102
42566
|
return {
|
|
@@ -42416,6 +42880,303 @@ class ProductHandleBuilder {
|
|
|
42416
42880
|
return new ProductHandleFeature(grip, upperPad, lowerPad);
|
|
42417
42881
|
}
|
|
42418
42882
|
}
|
|
42883
|
+
class ProductSurfaceBuilder {
|
|
42884
|
+
constructor(skin, side) {
|
|
42885
|
+
this.skin = skin;
|
|
42886
|
+
this.side = side;
|
|
42887
|
+
}
|
|
42888
|
+
/** Create a ref on this skin side. */
|
|
42889
|
+
ref(u = 0.5, v = 0.5, offset2) {
|
|
42890
|
+
return Product.ref(this.skin, {
|
|
42891
|
+
side: this.side,
|
|
42892
|
+
u,
|
|
42893
|
+
v,
|
|
42894
|
+
...offset2 != null ? { offset: offset2 } : {}
|
|
42895
|
+
});
|
|
42896
|
+
}
|
|
42897
|
+
/** Create a side/u/v query on this skin side. */
|
|
42898
|
+
uv(u = 0.5, v = 0.5, offset2) {
|
|
42899
|
+
return {
|
|
42900
|
+
side: this.side,
|
|
42901
|
+
u,
|
|
42902
|
+
v,
|
|
42903
|
+
...offset2 != null ? { offset: offset2 } : {}
|
|
42904
|
+
};
|
|
42905
|
+
}
|
|
42906
|
+
/**
|
|
42907
|
+
* Start a conformal ribbon on this skin side.
|
|
42908
|
+
*
|
|
42909
|
+
* Path points use side-local `u`/`v` coordinates; this builder supplies the side.
|
|
42910
|
+
* The returned ProductRibbonBuilder is already bound to the source skin and can be further
|
|
42911
|
+
* configured before build(). Use `widthSamples` >= 3 when the ribbon must visibly wrap over
|
|
42912
|
+
* curved product sections instead of behaving like a flat strip.
|
|
42913
|
+
*/
|
|
42914
|
+
ribbon(name, points, options = {}) {
|
|
42915
|
+
if (points.length < 2) throw new Error("Product.surface(...).ribbon(name, points) requires at least two path points");
|
|
42916
|
+
const path2 = points.map((point2) => ({
|
|
42917
|
+
side: this.side,
|
|
42918
|
+
u: point2.u ?? 0.5,
|
|
42919
|
+
v: point2.v ?? 0.5,
|
|
42920
|
+
...point2.offset != null ? { offset: point2.offset } : {}
|
|
42921
|
+
}));
|
|
42922
|
+
return new ProductRibbonBuilder(name).on(this.skin, path2, options);
|
|
42923
|
+
}
|
|
42924
|
+
}
|
|
42925
|
+
class ProductRibbonBuilder {
|
|
42926
|
+
constructor(name) {
|
|
42927
|
+
__publicField(this, "skinValue");
|
|
42928
|
+
__publicField(this, "queryPath", []);
|
|
42929
|
+
__publicField(this, "refPath", []);
|
|
42930
|
+
__publicField(this, "widthValue", 6);
|
|
42931
|
+
__publicField(this, "thicknessValue", 0.8);
|
|
42932
|
+
__publicField(this, "offsetValue", 0.25);
|
|
42933
|
+
__publicField(this, "samplesValue", 24);
|
|
42934
|
+
__publicField(this, "widthSamplesValue", 5);
|
|
42935
|
+
__publicField(this, "resolutionValue");
|
|
42936
|
+
__publicField(this, "materialValue");
|
|
42937
|
+
__publicField(this, "colorValue");
|
|
42938
|
+
__publicField(this, "lastDiagnosticsValue");
|
|
42939
|
+
this.name = name;
|
|
42940
|
+
if (!name || !name.trim()) throw new Error("Product.ribbon(name) requires a non-empty name");
|
|
42941
|
+
}
|
|
42942
|
+
/**
|
|
42943
|
+
* Follow a ProductSkin with side/u/v path queries or refs.
|
|
42944
|
+
*
|
|
42945
|
+
* This is the highest-fidelity mode because every interpolated sample is resolved through
|
|
42946
|
+
* ProductSkin.frame(), so the ribbon bends along the selected side as station width/depth changes.
|
|
42947
|
+
* All query path points must stay on one side; split side transitions into separate ribbons.
|
|
42948
|
+
*/
|
|
42949
|
+
on(skin, points, options = {}) {
|
|
42950
|
+
if (points.length < 2) throw new Error("Product.ribbon().on(skin, points) requires at least two path points");
|
|
42951
|
+
this.skinValue = skin;
|
|
42952
|
+
this.queryPath = resolvePathQueries(points);
|
|
42953
|
+
this.refPath = [];
|
|
42954
|
+
return this.applyOptions(options);
|
|
42955
|
+
}
|
|
42956
|
+
/**
|
|
42957
|
+
* Follow explicit surface refs.
|
|
42958
|
+
*
|
|
42959
|
+
* Useful for named refs or paths assembled elsewhere. The builder resolves each ref frame and
|
|
42960
|
+
* interpolates between those frames; use on(skin, points) when you need full skin-side sampling
|
|
42961
|
+
* between sparse control points.
|
|
42962
|
+
*/
|
|
42963
|
+
fromRefs(points, options = {}) {
|
|
42964
|
+
if (points.length < 2) throw new Error("Product.ribbon().fromRefs(points) requires at least two refs");
|
|
42965
|
+
this.skinValue = void 0;
|
|
42966
|
+
this.queryPath = [];
|
|
42967
|
+
this.refPath = [...points];
|
|
42968
|
+
return this.applyOptions(options);
|
|
42969
|
+
}
|
|
42970
|
+
/** Set ribbon width in millimeters. */
|
|
42971
|
+
width(width) {
|
|
42972
|
+
if (!Number.isFinite(width) || width <= 0) throw new Error("Product.ribbon().width(width) requires a positive finite number");
|
|
42973
|
+
this.widthValue = width;
|
|
42974
|
+
return this;
|
|
42975
|
+
}
|
|
42976
|
+
/** Set solid thickness outward from the source surface in millimeters. */
|
|
42977
|
+
thickness(thickness) {
|
|
42978
|
+
if (!Number.isFinite(thickness) || thickness <= 0)
|
|
42979
|
+
throw new Error("Product.ribbon().thickness(thickness) requires a positive finite number");
|
|
42980
|
+
this.thicknessValue = thickness;
|
|
42981
|
+
return this;
|
|
42982
|
+
}
|
|
42983
|
+
/** Set positive clearance between the source surface and the ribbon's inner face. */
|
|
42984
|
+
offset(offset2) {
|
|
42985
|
+
if (!Number.isFinite(offset2)) throw new Error("Product.ribbon().offset(offset) requires a finite number");
|
|
42986
|
+
this.offsetValue = offset2;
|
|
42987
|
+
return this;
|
|
42988
|
+
}
|
|
42989
|
+
/** Set samples along the path. */
|
|
42990
|
+
samples(samples) {
|
|
42991
|
+
if (!Number.isFinite(samples) || samples < 2) throw new Error("Product.ribbon().samples(samples) requires a value >= 2");
|
|
42992
|
+
this.samplesValue = Math.round(samples);
|
|
42993
|
+
return this;
|
|
42994
|
+
}
|
|
42995
|
+
/** Set samples across the width. Use 3+ to bend over curved cross-sections. */
|
|
42996
|
+
widthSamples(samples) {
|
|
42997
|
+
if (!Number.isFinite(samples) || samples < 2) throw new Error("Product.ribbon().widthSamples(samples) requires a value >= 2");
|
|
42998
|
+
this.widthSamplesValue = Math.round(samples);
|
|
42999
|
+
return this;
|
|
43000
|
+
}
|
|
43001
|
+
/** Set NURBS tessellation resolution. */
|
|
43002
|
+
resolution(resolution) {
|
|
43003
|
+
if (!Number.isFinite(resolution) || resolution < 2) throw new Error("Product.ribbon().resolution(resolution) requires a value >= 2");
|
|
43004
|
+
this.resolutionValue = Math.round(resolution);
|
|
43005
|
+
return this;
|
|
43006
|
+
}
|
|
43007
|
+
/** Apply a product material preset. */
|
|
43008
|
+
material(material) {
|
|
43009
|
+
this.materialValue = material;
|
|
43010
|
+
return this;
|
|
43011
|
+
}
|
|
43012
|
+
/** Apply a simple color override. */
|
|
43013
|
+
color(color) {
|
|
43014
|
+
this.colorValue = color;
|
|
43015
|
+
return this;
|
|
43016
|
+
}
|
|
43017
|
+
/** Build a conformal ribbon as a thin NURBS surface solid. */
|
|
43018
|
+
build(options = {}) {
|
|
43019
|
+
return this.buildWithDiagnostics(options).shape;
|
|
43020
|
+
}
|
|
43021
|
+
/**
|
|
43022
|
+
* Build a conformal ribbon and return surface-feature diagnostics.
|
|
43023
|
+
*
|
|
43024
|
+
* Use this while validating API usage or model fidelity; diagnostics report sampling counts,
|
|
43025
|
+
* side-span clamping, lowering mode, and warnings that should be visible in reviews.
|
|
43026
|
+
*/
|
|
43027
|
+
buildWithDiagnostics(options = {}) {
|
|
43028
|
+
this.applyOptions(options);
|
|
43029
|
+
const gridResult = this.skinValue ? this.buildSkinGrid(this.skinValue, this.queryPath) : this.buildRefGrid(this.refPath);
|
|
43030
|
+
const desiredNormal = this.centerDesiredNormal();
|
|
43031
|
+
let ribbon = nurbsSurface(orientGridToNormal(gridResult.grid, desiredNormal), {
|
|
43032
|
+
degreeU: Math.min(3, this.widthSamplesValue - 1),
|
|
43033
|
+
degreeV: Math.min(3, this.samplesValue - 1),
|
|
43034
|
+
thickness: this.thicknessValue,
|
|
43035
|
+
resolution: this.resolutionValue ?? Math.max(this.samplesValue, this.widthSamplesValue, 12),
|
|
43036
|
+
approximate: true
|
|
43037
|
+
}).as(this.name);
|
|
43038
|
+
if (this.colorValue) ribbon = ribbon.color(this.colorValue);
|
|
43039
|
+
const shape = applyMaterial(ribbon, this.materialValue);
|
|
43040
|
+
this.lastDiagnosticsValue = gridResult.diagnostics;
|
|
43041
|
+
return { shape, diagnostics: this.cloneDiagnostics(gridResult.diagnostics) };
|
|
43042
|
+
}
|
|
43043
|
+
/** Return diagnostics from the most recent build, if this builder has been built. */
|
|
43044
|
+
diagnostics() {
|
|
43045
|
+
return this.lastDiagnosticsValue ? this.cloneDiagnostics(this.lastDiagnosticsValue) : void 0;
|
|
43046
|
+
}
|
|
43047
|
+
applyOptions(options) {
|
|
43048
|
+
if (options.width != null) this.width(options.width);
|
|
43049
|
+
if (options.thickness != null) this.thickness(options.thickness);
|
|
43050
|
+
if (options.offset != null) this.offset(options.offset);
|
|
43051
|
+
if (options.samples != null) this.samples(options.samples);
|
|
43052
|
+
if (options.widthSamples != null) this.widthSamples(options.widthSamples);
|
|
43053
|
+
if (options.resolution != null) this.resolution(options.resolution);
|
|
43054
|
+
if (options.material) this.material(options.material);
|
|
43055
|
+
if (options.color) this.color(options.color);
|
|
43056
|
+
return this;
|
|
43057
|
+
}
|
|
43058
|
+
centerDesiredNormal() {
|
|
43059
|
+
if (this.skinValue && this.queryPath.length > 0) {
|
|
43060
|
+
const mid = this.samplePathQuery(0.5);
|
|
43061
|
+
return this.skinValue.frame({ ...mid, offset: (mid.offset ?? 0) + this.offsetValue }).normal;
|
|
43062
|
+
}
|
|
43063
|
+
if (this.refPath.length > 0) return this.refPath[Math.floor(this.refPath.length / 2)].frame({ offset: this.offsetValue }).normal;
|
|
43064
|
+
return [0, 0, 1];
|
|
43065
|
+
}
|
|
43066
|
+
samplePathQuery(t) {
|
|
43067
|
+
if (this.queryPath.length < 2) throw new Error("Product.ribbon().on(...) must be called before .build()");
|
|
43068
|
+
const segmentCount = this.queryPath.length - 1;
|
|
43069
|
+
const scaled = clamp$5(t, 0, 1) * segmentCount;
|
|
43070
|
+
const segment = Math.min(segmentCount - 1, Math.floor(scaled));
|
|
43071
|
+
const localT = scaled - segment;
|
|
43072
|
+
return interpolateQuery(this.queryPath[segment], this.queryPath[segment + 1], localT);
|
|
43073
|
+
}
|
|
43074
|
+
buildSkinGrid(skin, path2) {
|
|
43075
|
+
if (path2.length < 2) throw new Error("Product.ribbon().on(skin, points) must be called before .build()");
|
|
43076
|
+
const side = normalizedSide(path2[0].side);
|
|
43077
|
+
if (side === "front" || side === "rear") {
|
|
43078
|
+
throw new Error(
|
|
43079
|
+
"Product.ribbon().on(...) supports side ribbons on left/right/top/bottom surfaces. Use Product.panel() for front/rear caps."
|
|
43080
|
+
);
|
|
43081
|
+
}
|
|
43082
|
+
for (const point2 of path2) {
|
|
43083
|
+
if (normalizedSide(point2.side) !== side) {
|
|
43084
|
+
throw new Error("Product.ribbon().on(...) currently supports one side per ribbon. Split ribbons at side transitions.");
|
|
43085
|
+
}
|
|
43086
|
+
}
|
|
43087
|
+
const rows = Array.from({ length: this.widthSamplesValue }, () => []);
|
|
43088
|
+
let clampedUCount = 0;
|
|
43089
|
+
let maxUClampDistance = 0;
|
|
43090
|
+
for (let i = 0; i < this.samplesValue; i += 1) {
|
|
43091
|
+
const along = this.samplesValue === 1 ? 0 : i / (this.samplesValue - 1);
|
|
43092
|
+
const center = this.samplePathQuery(along);
|
|
43093
|
+
const station = skin.stationAt(center.v ?? 0.5);
|
|
43094
|
+
const span = sideSpan(side, station.width, station.depth);
|
|
43095
|
+
for (let j = 0; j < this.widthSamplesValue; j += 1) {
|
|
43096
|
+
const across = this.widthSamplesValue === 1 ? 0 : j / (this.widthSamplesValue - 1) - 0.5;
|
|
43097
|
+
const rawU = (center.u ?? 0.5) + across * this.widthValue / span;
|
|
43098
|
+
const u = clamp$5(rawU, 0, 1);
|
|
43099
|
+
const clampDistance = Math.abs(rawU - u) * span;
|
|
43100
|
+
if (clampDistance > EPS$4) {
|
|
43101
|
+
clampedUCount += 1;
|
|
43102
|
+
maxUClampDistance = Math.max(maxUClampDistance, clampDistance);
|
|
43103
|
+
}
|
|
43104
|
+
const query = {
|
|
43105
|
+
...center,
|
|
43106
|
+
side,
|
|
43107
|
+
u,
|
|
43108
|
+
offset: (center.offset ?? 0) + this.offsetValue + this.thicknessValue
|
|
43109
|
+
};
|
|
43110
|
+
rows[j].push(skin.frame(query).point);
|
|
43111
|
+
}
|
|
43112
|
+
}
|
|
43113
|
+
return {
|
|
43114
|
+
grid: rows,
|
|
43115
|
+
diagnostics: this.makeDiagnostics({
|
|
43116
|
+
skin: skin.name,
|
|
43117
|
+
side,
|
|
43118
|
+
pathPointCount: path2.length,
|
|
43119
|
+
clampedUCount,
|
|
43120
|
+
maxUClampDistance
|
|
43121
|
+
})
|
|
43122
|
+
};
|
|
43123
|
+
}
|
|
43124
|
+
buildRefGrid(refs) {
|
|
43125
|
+
if (refs.length < 2) throw new Error("Product.ribbon().fromRefs(points) must be called before .build()");
|
|
43126
|
+
const frames = refs.map((ref) => ref.frame({ offset: this.offsetValue + this.thicknessValue }));
|
|
43127
|
+
const rows = Array.from({ length: this.widthSamplesValue }, () => []);
|
|
43128
|
+
for (const frame of frames) {
|
|
43129
|
+
for (let j = 0; j < this.widthSamplesValue; j += 1) {
|
|
43130
|
+
const across = this.widthSamplesValue === 1 ? 0 : j / (this.widthSamplesValue - 1) - 0.5;
|
|
43131
|
+
rows[j].push(add(frame.point, scale(frame.tangentU, across * this.widthValue)));
|
|
43132
|
+
}
|
|
43133
|
+
}
|
|
43134
|
+
this.samplesValue = refs.length;
|
|
43135
|
+
return {
|
|
43136
|
+
grid: rows,
|
|
43137
|
+
diagnostics: this.makeDiagnostics({
|
|
43138
|
+
pathPointCount: refs.length,
|
|
43139
|
+
clampedUCount: 0,
|
|
43140
|
+
maxUClampDistance: 0
|
|
43141
|
+
})
|
|
43142
|
+
};
|
|
43143
|
+
}
|
|
43144
|
+
makeDiagnostics(input) {
|
|
43145
|
+
const resolution = this.resolutionValue ?? Math.max(this.samplesValue, this.widthSamplesValue, 12);
|
|
43146
|
+
const warnings = [];
|
|
43147
|
+
if (input.clampedUCount > 0) {
|
|
43148
|
+
warnings.push(
|
|
43149
|
+
`Ribbon '${this.name}' was clipped to the ${input.side ?? "surface"} UV bounds at ${input.clampedUCount} sampled point(s).`
|
|
43150
|
+
);
|
|
43151
|
+
}
|
|
43152
|
+
if (this.samplesValue < 8) warnings.push(`Ribbon '${this.name}' uses low along-path sampling; increase samples() for smoother bends.`);
|
|
43153
|
+
if (this.widthSamplesValue < 3)
|
|
43154
|
+
warnings.push(`Ribbon '${this.name}' uses low width sampling; use widthSamples(3+) to show cross-surface curvature.`);
|
|
43155
|
+
return {
|
|
43156
|
+
name: this.name,
|
|
43157
|
+
...input.skin ? { skin: input.skin } : {},
|
|
43158
|
+
...input.side ? { side: input.side } : {},
|
|
43159
|
+
pathPointCount: input.pathPointCount,
|
|
43160
|
+
width: this.widthValue,
|
|
43161
|
+
thickness: this.thicknessValue,
|
|
43162
|
+
offset: this.offsetValue,
|
|
43163
|
+
samples: this.samplesValue,
|
|
43164
|
+
widthSamples: this.widthSamplesValue,
|
|
43165
|
+
resolution,
|
|
43166
|
+
lowering: "nurbsSurface",
|
|
43167
|
+
expectedFidelity: "mixed",
|
|
43168
|
+
clampedUCount: input.clampedUCount,
|
|
43169
|
+
maxUClampDistance: input.maxUClampDistance,
|
|
43170
|
+
warnings
|
|
43171
|
+
};
|
|
43172
|
+
}
|
|
43173
|
+
cloneDiagnostics(diagnostics) {
|
|
43174
|
+
return {
|
|
43175
|
+
...diagnostics,
|
|
43176
|
+
warnings: [...diagnostics.warnings]
|
|
43177
|
+
};
|
|
43178
|
+
}
|
|
43179
|
+
}
|
|
42419
43180
|
const Product = {
|
|
42420
43181
|
/** Start a named product skin builder. */
|
|
42421
43182
|
skin(name) {
|
|
@@ -42488,10 +43249,27 @@ const Product = {
|
|
|
42488
43249
|
ref(skin, query) {
|
|
42489
43250
|
return new ProductSurfaceRef(skin, query);
|
|
42490
43251
|
},
|
|
43252
|
+
/**
|
|
43253
|
+
* Create a fluent surface helper for refs and conformal features on one side of a skin.
|
|
43254
|
+
*
|
|
43255
|
+
* Equivalent to skin.surface(side), useful when writing in Product.* namespace style.
|
|
43256
|
+
*/
|
|
43257
|
+
surface(skin, side) {
|
|
43258
|
+
return skin.surface(side);
|
|
43259
|
+
},
|
|
42491
43260
|
/** Start a panel feature builder. */
|
|
42492
43261
|
panel(name) {
|
|
42493
43262
|
return new ProductPanelBuilder(name);
|
|
42494
43263
|
},
|
|
43264
|
+
/**
|
|
43265
|
+
* Start a conformal ribbon/trim builder for details that should bend with a ProductSkin.
|
|
43266
|
+
*
|
|
43267
|
+
* Call .on(skin, points) for side/u/v sampling or .fromRefs(points) for explicit surface refs,
|
|
43268
|
+
* then configure width, thickness, offset, sampling, material, and color before build().
|
|
43269
|
+
*/
|
|
43270
|
+
ribbon(name) {
|
|
43271
|
+
return new ProductRibbonBuilder(name);
|
|
43272
|
+
},
|
|
42495
43273
|
/** Start a spout/nozzle feature builder. */
|
|
42496
43274
|
spout(name) {
|
|
42497
43275
|
return new ProductSpoutBuilder(name);
|
|
@@ -45352,6 +46130,7 @@ function cameraTrajectory(defOrFn, options) {
|
|
|
45352
46130
|
throw new Error('cameraTrajectory(): each keyframe must have either an "orbit" or "position" property');
|
|
45353
46131
|
}
|
|
45354
46132
|
}
|
|
46133
|
+
var define_process_env_default = {};
|
|
45355
46134
|
function resolveEdges(shape, edges) {
|
|
45356
46135
|
if (!edges) {
|
|
45357
46136
|
return selectEdges(shape);
|
|
@@ -45373,6 +46152,76 @@ function isEdgeSegment(value) {
|
|
|
45373
46152
|
function isEdgeReferenceLike(value) {
|
|
45374
46153
|
return typeof value === "object" && value !== null && "edges" in value && typeof value.edges === "function";
|
|
45375
46154
|
}
|
|
46155
|
+
const BROAD_EDGE_FEATURE_DEFAULT_BUDGET = {
|
|
46156
|
+
live: 0,
|
|
46157
|
+
default: 12,
|
|
46158
|
+
high: Number.POSITIVE_INFINITY
|
|
46159
|
+
};
|
|
46160
|
+
let broadEdgeFeatureBudget = null;
|
|
46161
|
+
function readBroadEdgeFeatureEnv(name) {
|
|
46162
|
+
return typeof process !== "undefined" ? define_process_env_default == null ? void 0 : define_process_env_default[name] : void 0;
|
|
46163
|
+
}
|
|
46164
|
+
function resolveBroadEdgeFeatureBudget() {
|
|
46165
|
+
if (readBroadEdgeFeatureEnv("FORGECAD_ALLOW_BROAD_EDGE_FEATURES") === "1") return Number.POSITIVE_INFINITY;
|
|
46166
|
+
const override = readBroadEdgeFeatureEnv("FORGECAD_BROAD_EDGE_FEATURE_BUDGET");
|
|
46167
|
+
if (override != null && override.trim() !== "") {
|
|
46168
|
+
const parsed = Number(override);
|
|
46169
|
+
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
|
|
46170
|
+
}
|
|
46171
|
+
return BROAD_EDGE_FEATURE_DEFAULT_BUDGET[getForgeQualityPreset()];
|
|
46172
|
+
}
|
|
46173
|
+
function resetBroadEdgeFeatureBudget() {
|
|
46174
|
+
broadEdgeFeatureBudget = null;
|
|
46175
|
+
}
|
|
46176
|
+
function remainingBroadEdgeFeatureBudget() {
|
|
46177
|
+
if (broadEdgeFeatureBudget === null) broadEdgeFeatureBudget = resolveBroadEdgeFeatureBudget();
|
|
46178
|
+
return broadEdgeFeatureBudget;
|
|
46179
|
+
}
|
|
46180
|
+
function consumeBroadEdgeFeatureBudget(edgeCount) {
|
|
46181
|
+
const remaining = remainingBroadEdgeFeatureBudget();
|
|
46182
|
+
if (!Number.isFinite(remaining)) return true;
|
|
46183
|
+
if (edgeCount > remaining) return false;
|
|
46184
|
+
broadEdgeFeatureBudget = Math.max(0, remaining - edgeCount);
|
|
46185
|
+
return true;
|
|
46186
|
+
}
|
|
46187
|
+
function shouldSkipBroadEdgeFeature(operation, edgeCount) {
|
|
46188
|
+
if (consumeBroadEdgeFeatureBudget(edgeCount)) return false;
|
|
46189
|
+
const remaining = Math.max(0, remainingBroadEdgeFeatureBudget());
|
|
46190
|
+
emitRuntimeWarning(
|
|
46191
|
+
`${operation}() without an edge selector matched ${edgeCount} edge(s), exceeding the remaining broad edge-feature budget (${remaining}). Skipped this cosmetic edge finish for responsiveness. Pass an explicit edge selector, use high quality/export, or set FORGECAD_BROAD_EDGE_FEATURE_BUDGET to opt into more broad edge finishing.`
|
|
46192
|
+
);
|
|
46193
|
+
return true;
|
|
46194
|
+
}
|
|
46195
|
+
function shouldSkipExhaustedBroadEdgeFeature(operation) {
|
|
46196
|
+
const remaining = remainingBroadEdgeFeatureBudget();
|
|
46197
|
+
if (!Number.isFinite(remaining) || remaining > 0) return false;
|
|
46198
|
+
emitRuntimeWarning(
|
|
46199
|
+
`${operation}() without an edge selector was skipped because the broad edge-feature budget is exhausted. Skipped this cosmetic edge finish for responsiveness. Pass an explicit edge selector, use high quality/export, or set FORGECAD_BROAD_EDGE_FEATURE_BUDGET to opt into more broad edge finishing.`
|
|
46200
|
+
);
|
|
46201
|
+
return true;
|
|
46202
|
+
}
|
|
46203
|
+
function estimateSelectorlessEdgeCount(plan) {
|
|
46204
|
+
if (!plan) return null;
|
|
46205
|
+
switch (plan.kind) {
|
|
46206
|
+
case "box":
|
|
46207
|
+
return 12;
|
|
46208
|
+
case "queryOwner":
|
|
46209
|
+
case "transform":
|
|
46210
|
+
return estimateSelectorlessEdgeCount(plan.base);
|
|
46211
|
+
default:
|
|
46212
|
+
return null;
|
|
46213
|
+
}
|
|
46214
|
+
}
|
|
46215
|
+
function shouldSkipUnestimatedBroadEdgeFeature(operation, target) {
|
|
46216
|
+
const remaining = remainingBroadEdgeFeatureBudget();
|
|
46217
|
+
if (!Number.isFinite(remaining)) return false;
|
|
46218
|
+
const estimatedEdges = estimateSelectorlessEdgeCount(getShapeCompilePlan(target));
|
|
46219
|
+
if (estimatedEdges !== null && estimatedEdges <= remaining) return false;
|
|
46220
|
+
emitRuntimeWarning(
|
|
46221
|
+
`${operation}() without an edge selector was skipped before broad edge enumeration because this shape is too complex for the remaining broad edge-feature budget (${Math.max(0, remaining)}). Skipped this cosmetic edge finish for responsiveness. Pass an explicit edge selector, use high quality/export, or set FORGECAD_BROAD_EDGE_FEATURE_BUDGET to opt into more broad edge finishing.`
|
|
46222
|
+
);
|
|
46223
|
+
return true;
|
|
46224
|
+
}
|
|
45376
46225
|
function edgesToTargets(edges) {
|
|
45377
46226
|
return edges.map((e) => ({
|
|
45378
46227
|
midpoint: [e.midpoint[0], e.midpoint[1], e.midpoint[2]],
|
|
@@ -45386,10 +46235,13 @@ function fillet(shape, radius, edges, segments = 16) {
|
|
|
45386
46235
|
throw new Error("fillet() requires a positive finite radius.");
|
|
45387
46236
|
}
|
|
45388
46237
|
const target = shape;
|
|
46238
|
+
if (edges === void 0 && shouldSkipExhaustedBroadEdgeFeature("fillet")) return target;
|
|
46239
|
+
if (edges === void 0 && shouldSkipUnestimatedBroadEdgeFeature("fillet", target)) return target;
|
|
45389
46240
|
const resolvedEdges = resolveEdges(target, edges);
|
|
45390
46241
|
if (resolvedEdges.length === 0) {
|
|
45391
46242
|
throw new Error("fillet(): no edges match the given selection.");
|
|
45392
46243
|
}
|
|
46244
|
+
if (edges === void 0 && shouldSkipBroadEdgeFeature("fillet", resolvedEdges.length)) return target;
|
|
45393
46245
|
const basePlan = getShapeCompilePlan(target);
|
|
45394
46246
|
const plan = {
|
|
45395
46247
|
kind: "filletEdges",
|
|
@@ -45409,10 +46261,13 @@ function chamfer(shape, size, edges) {
|
|
|
45409
46261
|
throw new Error("chamfer() requires a positive finite size.");
|
|
45410
46262
|
}
|
|
45411
46263
|
const target = shape;
|
|
46264
|
+
if (edges === void 0 && shouldSkipExhaustedBroadEdgeFeature("chamfer")) return target;
|
|
46265
|
+
if (edges === void 0 && shouldSkipUnestimatedBroadEdgeFeature("chamfer", target)) return target;
|
|
45412
46266
|
const resolvedEdges = resolveEdges(target, edges);
|
|
45413
46267
|
if (resolvedEdges.length === 0) {
|
|
45414
46268
|
throw new Error("chamfer(): no edges match the given selection.");
|
|
45415
46269
|
}
|
|
46270
|
+
if (edges === void 0 && shouldSkipBroadEdgeFeature("chamfer", resolvedEdges.length)) return target;
|
|
45416
46271
|
const basePlan = getShapeCompilePlan(target);
|
|
45417
46272
|
const plan = {
|
|
45418
46273
|
kind: "chamferEdges",
|
|
@@ -68100,6 +68955,7 @@ const verify = {
|
|
|
68100
68955
|
};
|
|
68101
68956
|
function resetExecutionSession(logs) {
|
|
68102
68957
|
resetCollectedAssemblies();
|
|
68958
|
+
resetBroadEdgeFeatureBudget();
|
|
68103
68959
|
resetParams();
|
|
68104
68960
|
resetShapeQueryOwnerIds();
|
|
68105
68961
|
resetDimensions();
|
|
@@ -68108,6 +68964,7 @@ function resetExecutionSession(logs) {
|
|
|
68108
68964
|
resetSheetStock();
|
|
68109
68965
|
resetRobotExport();
|
|
68110
68966
|
resetCutPlanes();
|
|
68967
|
+
resetRenderLabels();
|
|
68111
68968
|
resetCameraTrajectory();
|
|
68112
68969
|
resetExplodeView();
|
|
68113
68970
|
resetJointsView();
|
|
@@ -68171,6 +69028,7 @@ function collectSuccessfulExecutionSnapshot(args) {
|
|
|
68171
69028
|
bom: getCollectedBom(),
|
|
68172
69029
|
sheetStock: getCollectedSheetStock(),
|
|
68173
69030
|
cutPlanes: getCollectedCutPlanes(),
|
|
69031
|
+
renderLabels: getCollectedRenderLabels(),
|
|
68174
69032
|
cameraTrajectory: getCollectedCameraTrajectory(),
|
|
68175
69033
|
explodeView: getCollectedExplodeView(),
|
|
68176
69034
|
jointsView: getCollectedJointsView(),
|
|
@@ -68194,6 +69052,7 @@ function collectFailedExecutionSnapshot(args) {
|
|
|
68194
69052
|
bom: getCollectedBom(),
|
|
68195
69053
|
sheetStock: getCollectedSheetStock(),
|
|
68196
69054
|
cutPlanes: getCollectedCutPlanes(),
|
|
69055
|
+
renderLabels: getCollectedRenderLabels(),
|
|
68197
69056
|
cameraTrajectory: getCollectedCameraTrajectory(),
|
|
68198
69057
|
explodeView: getCollectedExplodeView(),
|
|
68199
69058
|
jointsView: getCollectedJointsView(),
|
|
@@ -68339,13 +69198,15 @@ function formatLogArg(value) {
|
|
|
68339
69198
|
return `[Log serialization failed: ${formatLogError(error)}]`;
|
|
68340
69199
|
}
|
|
68341
69200
|
}
|
|
68342
|
-
function makeSandboxConsole(collectedLogs) {
|
|
69201
|
+
function makeSandboxConsole(collectedLogs, mirror) {
|
|
68343
69202
|
const capture = (level) => (...args) => {
|
|
69203
|
+
const formattedArgs = args.map(formatLogArg);
|
|
68344
69204
|
collectedLogs.push({
|
|
68345
69205
|
level,
|
|
68346
|
-
args:
|
|
69206
|
+
args: formattedArgs,
|
|
68347
69207
|
timestamp: Date.now()
|
|
68348
69208
|
});
|
|
69209
|
+
mirror == null ? void 0 : mirror(level, formattedArgs);
|
|
68349
69210
|
};
|
|
68350
69211
|
return { log: capture("log"), warn: capture("warn"), error: capture("error"), info: capture("info") };
|
|
68351
69212
|
}
|
|
@@ -68507,6 +69368,18 @@ function buildFileIndex(allFiles) {
|
|
|
68507
69368
|
}
|
|
68508
69369
|
return fileIndex;
|
|
68509
69370
|
}
|
|
69371
|
+
function hasPathExtension(path2) {
|
|
69372
|
+
const fileName = path2.split("/").pop() ?? path2;
|
|
69373
|
+
return /\.[^/.]+$/.test(fileName);
|
|
69374
|
+
}
|
|
69375
|
+
function explicitExtensionHint(requestedName, resolvedPath, fileIndex) {
|
|
69376
|
+
if (!resolvedPath || hasPathExtension(resolvedPath)) return "";
|
|
69377
|
+
const requestedBase = requestedName.trim();
|
|
69378
|
+
const suggestions = [".forge.js", ".js"].map((ext) => ({ ext, resolved: `${resolvedPath}${ext}` })).filter(({ resolved }) => fileIndex.has(resolved)).map(({ ext }) => `"${requestedBase}${ext}"`);
|
|
69379
|
+
if (suggestions.length === 0) return "";
|
|
69380
|
+
const joined = suggestions.length === 1 ? suggestions[0] : suggestions.join(" or ");
|
|
69381
|
+
return ` Did you mean ${joined}? ForgeCAD requires explicit file extensions in project imports.`;
|
|
69382
|
+
}
|
|
68510
69383
|
function resolveImportSource(fromFile, requestedName, allFiles, options) {
|
|
68511
69384
|
if (typeof requestedName !== "string" || requestedName.trim().length === 0) {
|
|
68512
69385
|
throw new Error("Import path must be a non-empty string");
|
|
@@ -68515,7 +69388,8 @@ function resolveImportSource(fromFile, requestedName, allFiles, options) {
|
|
|
68515
69388
|
const lookupKey = options.fileIndex.get(resolvedPath);
|
|
68516
69389
|
if (!lookupKey) {
|
|
68517
69390
|
const suffix = resolvedPath && resolvedPath !== requestedName ? ` (resolved to "${resolvedPath}" from "${fromFile}")` : ` (from "${fromFile}")`;
|
|
68518
|
-
|
|
69391
|
+
const hint = explicitExtensionHint(requestedName, resolvedPath, options.fileIndex);
|
|
69392
|
+
throw new Error(`File not found: "${requestedName}"${suffix}.${hint}`);
|
|
68519
69393
|
}
|
|
68520
69394
|
const source = allFiles[lookupKey];
|
|
68521
69395
|
if (typeof source !== "string") {
|
|
@@ -122821,7 +123695,7 @@ ${lanes.join("\n")}
|
|
|
122821
123695
|
}
|
|
122822
123696
|
const normalizedSourcePaths = getPathsRelativeToRootDirs(sourceDirectory, rootDirs, getCanonicalFileName);
|
|
122823
123697
|
const relativePaths = flatMap(normalizedSourcePaths, (sourcePath) => {
|
|
122824
|
-
return map(normalizedTargetPaths, (
|
|
123698
|
+
return map(normalizedTargetPaths, (targetPath2) => ensurePathIsNonModuleName(getRelativePathFromDirectory(sourcePath, targetPath2, getCanonicalFileName)));
|
|
122825
123699
|
});
|
|
122826
123700
|
const shortest = min2(relativePaths, compareNumberOfDirectorySeparators);
|
|
122827
123701
|
if (!shortest) {
|
|
@@ -279391,6 +280265,36 @@ function extractUnusedTopLevelVarNames(code) {
|
|
|
279391
280265
|
}
|
|
279392
280266
|
return declaredNames.filter((n) => topLevelNames.has(n) && (!usedByOthers.has(n) || explicitImplicitResultNames.has(n)));
|
|
279393
280267
|
}
|
|
280268
|
+
function collectBindingNameLocations(node, sourceFile, names) {
|
|
280269
|
+
if (typescriptExports.isIdentifier(node)) {
|
|
280270
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
280271
|
+
names.push({ name: node.text, line: line2 + 1, column: character + 1 });
|
|
280272
|
+
return;
|
|
280273
|
+
}
|
|
280274
|
+
for (const element of node.elements) {
|
|
280275
|
+
if (typescriptExports.isBindingElement(element)) {
|
|
280276
|
+
collectBindingNameLocations(element.name, sourceFile, names);
|
|
280277
|
+
}
|
|
280278
|
+
}
|
|
280279
|
+
}
|
|
280280
|
+
function findTopLevelRuntimeGlobalCollision(code, runtimeGlobalNames) {
|
|
280281
|
+
const runtimeGlobals = new Set(runtimeGlobalNames);
|
|
280282
|
+
const sourceFile = typescriptExports.createSourceFile("__runtime-globals.js", code, typescriptExports.ScriptTarget.ES2020, false, typescriptExports.ScriptKind.JS);
|
|
280283
|
+
const declarations = [];
|
|
280284
|
+
for (const statement of sourceFile.statements) {
|
|
280285
|
+
if (typescriptExports.isVariableStatement(statement)) {
|
|
280286
|
+
const isLexical = (statement.declarationList.flags & (typescriptExports.NodeFlags.Let | typescriptExports.NodeFlags.Const)) !== 0;
|
|
280287
|
+
if (!isLexical) continue;
|
|
280288
|
+
for (const decl of statement.declarationList.declarations) {
|
|
280289
|
+
collectBindingNameLocations(decl.name, sourceFile, declarations);
|
|
280290
|
+
}
|
|
280291
|
+
} else if (typescriptExports.isClassDeclaration(statement) && statement.name) {
|
|
280292
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(statement.name.getStart(sourceFile));
|
|
280293
|
+
declarations.push({ name: statement.name.text, line: line2 + 1, column: character + 1 });
|
|
280294
|
+
}
|
|
280295
|
+
}
|
|
280296
|
+
return declarations.find((declaration) => runtimeGlobals.has(declaration.name)) ?? null;
|
|
280297
|
+
}
|
|
279394
280298
|
function createForgeRuntimeModule(bindings) {
|
|
279395
280299
|
const runtime = { ...bindings };
|
|
279396
280300
|
Object.defineProperty(runtime, "__esModule", { value: true });
|
|
@@ -279931,6 +280835,12 @@ function withConstructorChainLockdown(fn) {
|
|
|
279931
280835
|
}
|
|
279932
280836
|
}
|
|
279933
280837
|
function executeFile(code, fileName, allFiles, visited, scope = {}, options, executionMode = "script", moduleCacheEntry) {
|
|
280838
|
+
var _a3, _b3, _c2, _d2, _e2, _f;
|
|
280839
|
+
(_a3 = options.debug) == null ? void 0 : _a3.call(options, "executeFile:start", {
|
|
280840
|
+
fileName,
|
|
280841
|
+
executionMode,
|
|
280842
|
+
scope: scope.namePrefix ?? fileName
|
|
280843
|
+
});
|
|
279934
280844
|
const trackCircularImports = executionMode === "script";
|
|
279935
280845
|
if (trackCircularImports) {
|
|
279936
280846
|
if (visited.has(fileName)) {
|
|
@@ -280008,13 +280918,13 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
280008
280918
|
});
|
|
280009
280919
|
};
|
|
280010
280920
|
const importStep = (name) => {
|
|
280011
|
-
var
|
|
280921
|
+
var _a4;
|
|
280012
280922
|
if (typeof name !== "string" || name.trim().length === 0) {
|
|
280013
280923
|
throw new Error("importStep() requires a non-empty file path string");
|
|
280014
280924
|
}
|
|
280015
280925
|
const resolvedPath = resolveImportPath(fileName, name.trim());
|
|
280016
280926
|
rejectPathTraversal("importStep", name, resolvedPath);
|
|
280017
|
-
const ext = ((
|
|
280927
|
+
const ext = ((_a4 = resolvedPath.split(".").pop()) == null ? void 0 : _a4.toLowerCase()) ?? "";
|
|
280018
280928
|
if (ext !== "step" && ext !== "stp") {
|
|
280019
280929
|
throw new Error(`importStep("${name}"): unsupported extension ".${ext}". Expected .step or .stp`);
|
|
280020
280930
|
}
|
|
@@ -280043,7 +280953,39 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
280043
280953
|
setShapeTopology(shape, topo);
|
|
280044
280954
|
return shape;
|
|
280045
280955
|
};
|
|
280046
|
-
const sandboxConsole = makeSandboxConsole(_collectedLogs)
|
|
280956
|
+
const sandboxConsole = makeSandboxConsole(_collectedLogs, options.debug ? (level, args) => {
|
|
280957
|
+
var _a4;
|
|
280958
|
+
return (_a4 = options.debug) == null ? void 0 : _a4.call(options, "console", { level, args });
|
|
280959
|
+
} : void 0);
|
|
280960
|
+
const runtimeVerify = options.debug ? {
|
|
280961
|
+
...verify,
|
|
280962
|
+
that(label, check2, message) {
|
|
280963
|
+
var _a4, _b4;
|
|
280964
|
+
(_a4 = options.debug) == null ? void 0 : _a4.call(options, "verify:that:start", { label });
|
|
280965
|
+
const verifyStart = performance.now();
|
|
280966
|
+
try {
|
|
280967
|
+
return verify.that(label, check2, message);
|
|
280968
|
+
} finally {
|
|
280969
|
+
(_b4 = options.debug) == null ? void 0 : _b4.call(options, "verify:that:end", {
|
|
280970
|
+
label,
|
|
280971
|
+
ms: Number((performance.now() - verifyStart).toFixed(1))
|
|
280972
|
+
});
|
|
280973
|
+
}
|
|
280974
|
+
},
|
|
280975
|
+
equal(label, actual, expected, tolerance = 0, message) {
|
|
280976
|
+
var _a4, _b4;
|
|
280977
|
+
(_a4 = options.debug) == null ? void 0 : _a4.call(options, "verify:equal:start", { label, actual, expected, tolerance });
|
|
280978
|
+
const verifyStart = performance.now();
|
|
280979
|
+
try {
|
|
280980
|
+
return verify.equal(label, actual, expected, tolerance, message);
|
|
280981
|
+
} finally {
|
|
280982
|
+
(_b4 = options.debug) == null ? void 0 : _b4.call(options, "verify:equal:end", {
|
|
280983
|
+
label,
|
|
280984
|
+
ms: Number((performance.now() - verifyStart).toFixed(1))
|
|
280985
|
+
});
|
|
280986
|
+
}
|
|
280987
|
+
}
|
|
280988
|
+
} : verify;
|
|
280047
280989
|
setShowLabelsHighlight(highlight);
|
|
280048
280990
|
const runtimeBindings = {
|
|
280049
280991
|
box: trackedBox,
|
|
@@ -280184,7 +281126,8 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
280184
281126
|
jointsView,
|
|
280185
281127
|
viewConfig,
|
|
280186
281128
|
scene,
|
|
280187
|
-
|
|
281129
|
+
Viewport,
|
|
281130
|
+
verify: runtimeVerify,
|
|
280188
281131
|
spec,
|
|
280189
281132
|
mock,
|
|
280190
281133
|
gcode,
|
|
@@ -280322,8 +281265,21 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
280322
281265
|
throw error;
|
|
280323
281266
|
}
|
|
280324
281267
|
};
|
|
280325
|
-
const compiled = compileScript(code, fileName, options);
|
|
280326
281268
|
const bindingNames = Object.keys(runtimeBindings);
|
|
281269
|
+
const collision = findTopLevelRuntimeGlobalCollision(code, bindingNames);
|
|
281270
|
+
if (collision) {
|
|
281271
|
+
throw new Error(
|
|
281272
|
+
`Local declaration "${collision.name}" at ${fileName}:${collision.line}:${collision.column} collides with the built-in ForgeCAD runtime name "${collision.name}". Use the ForgeCAD API directly, or rename your local variable/import (for example, import the helper module object instead of destructuring "${collision.name}").`
|
|
281273
|
+
);
|
|
281274
|
+
}
|
|
281275
|
+
(_b3 = options.debug) == null ? void 0 : _b3.call(options, "executeFile:compile:start", { fileName, executionMode });
|
|
281276
|
+
const compileStart = performance.now();
|
|
281277
|
+
const compiled = compileScript(code, fileName, options);
|
|
281278
|
+
(_c2 = options.debug) == null ? void 0 : _c2.call(options, "executeFile:compile:end", {
|
|
281279
|
+
fileName,
|
|
281280
|
+
executionMode,
|
|
281281
|
+
ms: Number((performance.now() - compileStart).toFixed(1))
|
|
281282
|
+
});
|
|
280327
281283
|
const bindingValues = bindingNames.map((name) => runtimeBindings[name]);
|
|
280328
281284
|
let scriptCode = compiled.code;
|
|
280329
281285
|
if (executionMode === "script") {
|
|
@@ -280349,12 +281305,20 @@ ${scriptCode}
|
|
|
280349
281305
|
exports: executionMode === "module" && moduleCacheEntry ? moduleCacheEntry.exports : {}
|
|
280350
281306
|
};
|
|
280351
281307
|
const initialExportsRef = moduleValue.exports;
|
|
281308
|
+
(_d2 = options.debug) == null ? void 0 : _d2.call(options, "executeFile:invoke:start", { fileName, executionMode });
|
|
281309
|
+
const invokeStart = performance.now();
|
|
280352
281310
|
const returnValue = withConstructorChainLockdown(
|
|
280353
281311
|
() => runWithParamScope(
|
|
280354
281312
|
scope,
|
|
280355
281313
|
() => fn(moduleValue.exports, moduleValue, requireModule, fileName, dirnamePath(fileName), ...bindingValues)
|
|
280356
281314
|
)
|
|
280357
281315
|
);
|
|
281316
|
+
(_e2 = options.debug) == null ? void 0 : _e2.call(options, "executeFile:invoke:end", {
|
|
281317
|
+
fileName,
|
|
281318
|
+
executionMode,
|
|
281319
|
+
ms: Number((performance.now() - invokeStart).toFixed(1)),
|
|
281320
|
+
returned: returnValue === void 0 ? "undefined" : returnValue === null ? "null" : typeof returnValue
|
|
281321
|
+
});
|
|
280358
281322
|
if (executionMode === "module") {
|
|
280359
281323
|
const hasExports = hasExplicitModuleExports(moduleValue.exports, initialExportsRef);
|
|
280360
281324
|
if (returnValue !== void 0 && hasExports) {
|
|
@@ -280393,12 +281357,14 @@ ${scriptCode}
|
|
|
280393
281357
|
}
|
|
280394
281358
|
return returnValue;
|
|
280395
281359
|
} finally {
|
|
281360
|
+
(_f = options.debug) == null ? void 0 : _f.call(options, "executeFile:end", { fileName, executionMode });
|
|
280396
281361
|
if (trackCircularImports) {
|
|
280397
281362
|
visited.delete(fileName);
|
|
280398
281363
|
}
|
|
280399
281364
|
}
|
|
280400
281365
|
}
|
|
280401
281366
|
function runScript(code, fileName = "main.forge.js", allFiles = {}, options = {}) {
|
|
281367
|
+
var _a3, _b3;
|
|
280402
281368
|
_collectedLogs = [];
|
|
280403
281369
|
resetExecutionSession(_collectedLogs);
|
|
280404
281370
|
const t0 = performance.now();
|
|
@@ -280407,14 +281373,25 @@ function runScript(code, fileName = "main.forge.js", allFiles = {}, options = {}
|
|
|
280407
281373
|
fileIndex: buildFileIndex(allFiles),
|
|
280408
281374
|
compiledFiles: persistentCompiledFiles,
|
|
280409
281375
|
moduleCache: /* @__PURE__ */ new Map(),
|
|
280410
|
-
readBinaryFile: options.readBinaryFile
|
|
281376
|
+
readBinaryFile: options.readBinaryFile,
|
|
281377
|
+
debug: options.debug
|
|
280411
281378
|
};
|
|
280412
281379
|
const quality = resolveForgeQualityPreset(options.quality);
|
|
281380
|
+
(_a3 = options.debug) == null ? void 0 : _a3.call(options, "runScript:start", { fileName, quality, fileCount: Object.keys(allFiles).length });
|
|
280413
281381
|
try {
|
|
280414
281382
|
return runWithForgeQuality(quality, () => {
|
|
281383
|
+
var _a4, _b4, _c2, _d2, _e2, _f, _g, _h, _i, _j;
|
|
281384
|
+
(_a4 = options.debug) == null ? void 0 : _a4.call(options, "runScript:execute:start", { fileName });
|
|
281385
|
+
const executeStart = performance.now();
|
|
280415
281386
|
const result = executeFile(code, fileName, allFiles, /* @__PURE__ */ new Set(), {}, execOptions);
|
|
281387
|
+
(_b4 = options.debug) == null ? void 0 : _b4.call(options, "runScript:execute:end", {
|
|
281388
|
+
fileName,
|
|
281389
|
+
ms: Number((performance.now() - executeStart).toFixed(1)),
|
|
281390
|
+
resultType: result === void 0 ? "undefined" : result === null ? "null" : typeof result
|
|
281391
|
+
});
|
|
280416
281392
|
const highlights = getCollectedHighlights();
|
|
280417
281393
|
const mocks = getCollectedMocks();
|
|
281394
|
+
(_c2 = options.debug) == null ? void 0 : _c2.call(options, "runScript:map:start", { highlights: highlights.length, mocks: mocks.length });
|
|
280418
281395
|
const mapped = mapScriptResultToScene({
|
|
280419
281396
|
result,
|
|
280420
281397
|
fileName,
|
|
@@ -280423,24 +281400,43 @@ function runScript(code, fileName = "main.forge.js", allFiles = {}, options = {}
|
|
|
280423
281400
|
mocks,
|
|
280424
281401
|
logs: _collectedLogs
|
|
280425
281402
|
});
|
|
281403
|
+
(_d2 = options.debug) == null ? void 0 : _d2.call(options, "runScript:map:end", {
|
|
281404
|
+
objects: mapped.objects.length,
|
|
281405
|
+
hasShape: Boolean(mapped.shape),
|
|
281406
|
+
hasSketch: Boolean(mapped.sketch),
|
|
281407
|
+
hasError: Boolean(mapped.error)
|
|
281408
|
+
});
|
|
281409
|
+
(_e2 = options.debug) == null ? void 0 : _e2.call(options, "runScript:explodeHints:start", { objects: mapped.objects.length });
|
|
280426
281410
|
autoFillExplodeHints(mapped.objects);
|
|
281411
|
+
(_f = options.debug) == null ? void 0 : _f.call(options, "runScript:explodeHints:end");
|
|
281412
|
+
(_g = options.debug) == null ? void 0 : _g.call(options, "runScript:snapshot:start", { objects: mapped.objects.length });
|
|
281413
|
+
const snapshot = collectSuccessfulExecutionSnapshot({
|
|
281414
|
+
quality,
|
|
281415
|
+
objects: mapped.objects,
|
|
281416
|
+
logs: _collectedLogs,
|
|
281417
|
+
extraDimensions: mapped.extraDimensions,
|
|
281418
|
+
highlights,
|
|
281419
|
+
mocks
|
|
281420
|
+
});
|
|
281421
|
+
(_h = options.debug) == null ? void 0 : _h.call(options, "runScript:snapshot:end", {
|
|
281422
|
+
params: snapshot.params.length,
|
|
281423
|
+
cutPlanes: snapshot.cutPlanes.length,
|
|
281424
|
+
verifications: snapshot.verifications.length
|
|
281425
|
+
});
|
|
281426
|
+
(_i = options.debug) == null ? void 0 : _i.call(options, "runScript:sceneTargets:start");
|
|
281427
|
+
snapshot.sceneConfig = resolveSceneJourneyTargets(snapshot.sceneConfig, mapped.objects);
|
|
281428
|
+
(_j = options.debug) == null ? void 0 : _j.call(options, "runScript:sceneTargets:end");
|
|
280427
281429
|
return {
|
|
280428
281430
|
shape: mapped.shape,
|
|
280429
281431
|
sketch: mapped.sketch,
|
|
280430
281432
|
objects: mapped.objects,
|
|
280431
|
-
...
|
|
280432
|
-
quality,
|
|
280433
|
-
objects: mapped.objects,
|
|
280434
|
-
logs: _collectedLogs,
|
|
280435
|
-
extraDimensions: mapped.extraDimensions,
|
|
280436
|
-
highlights,
|
|
280437
|
-
mocks
|
|
280438
|
-
}),
|
|
281433
|
+
...snapshot,
|
|
280439
281434
|
error: mapped.error,
|
|
280440
281435
|
timeMs: performance.now() - t0
|
|
280441
281436
|
};
|
|
280442
281437
|
});
|
|
280443
281438
|
} catch (e) {
|
|
281439
|
+
(_b3 = options.debug) == null ? void 0 : _b3.call(options, "runScript:error", { error: (e == null ? void 0 : e.message) || String(e) });
|
|
280444
281440
|
const msg = e.message || String(e);
|
|
280445
281441
|
const stack = e.stack || "";
|
|
280446
281442
|
let lineInfo = "";
|