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
|
@@ -7310,7 +7310,7 @@ function add$3(a, b) {
|
|
|
7310
7310
|
function scale$4(v, s) {
|
|
7311
7311
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
7312
7312
|
}
|
|
7313
|
-
function sub$
|
|
7313
|
+
function sub$5(a, b) {
|
|
7314
7314
|
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
7315
7315
|
}
|
|
7316
7316
|
function cross$4(a, b) {
|
|
@@ -7349,8 +7349,8 @@ function buildSurfaceSheetTopology(boundaries, options = {}) {
|
|
|
7349
7349
|
const [v1Start, v1End] = boundaries.v1;
|
|
7350
7350
|
const corners = [u0Start, u0End, u1Start, u1End];
|
|
7351
7351
|
const center = options.center ?? average$1(corners);
|
|
7352
|
-
const uAxis = normalizeAxis$1(sub$
|
|
7353
|
-
const vAxis = normalizeAxis$1(sub$
|
|
7352
|
+
const uAxis = normalizeAxis$1(sub$5(midpoint$3(u1Start, u1End), midpoint$3(u0Start, u0End)));
|
|
7353
|
+
const vAxis = normalizeAxis$1(sub$5(midpoint$3(v1Start, v1End), midpoint$3(v0Start, v0End)));
|
|
7354
7354
|
const normal = normalizeAxis$1(options.normal ?? cross$4(uAxis, vAxis));
|
|
7355
7355
|
const faces = /* @__PURE__ */ new Map();
|
|
7356
7356
|
faces.set(faceName, {
|
|
@@ -7382,7 +7382,7 @@ function attachSurfaceSheetTopology(shape, boundaries, options = {}) {
|
|
|
7382
7382
|
});
|
|
7383
7383
|
return shape;
|
|
7384
7384
|
}
|
|
7385
|
-
function requireFinite$
|
|
7385
|
+
function requireFinite$7(v, label) {
|
|
7386
7386
|
if (!Number.isFinite(v)) throw new Error(`nurbsSurface: ${label} must be finite, got ${v}`);
|
|
7387
7387
|
}
|
|
7388
7388
|
class NurbsSurface {
|
|
@@ -7408,16 +7408,16 @@ class NurbsSurface {
|
|
|
7408
7408
|
for (let i = 0; i < nU; i++) {
|
|
7409
7409
|
if (controlGrid[i].length !== nV) throw new Error(`nurbsSurface: row ${i} has ${controlGrid[i].length} points, expected ${nV}`);
|
|
7410
7410
|
for (let j = 0; j < nV; j++) {
|
|
7411
|
-
requireFinite$
|
|
7412
|
-
requireFinite$
|
|
7413
|
-
requireFinite$
|
|
7411
|
+
requireFinite$7(controlGrid[i][j][0], `controlGrid[${i}][${j}][0]`);
|
|
7412
|
+
requireFinite$7(controlGrid[i][j][1], `controlGrid[${i}][${j}][1]`);
|
|
7413
|
+
requireFinite$7(controlGrid[i][j][2], `controlGrid[${i}][${j}][2]`);
|
|
7414
7414
|
}
|
|
7415
7415
|
}
|
|
7416
7416
|
const weightsGrid = options.weights ?? controlGrid.map((row) => row.map(() => 1));
|
|
7417
7417
|
for (let i = 0; i < nU; i++) {
|
|
7418
7418
|
if (weightsGrid[i].length !== nV) throw new Error(`nurbsSurface: weights row ${i} length mismatch`);
|
|
7419
7419
|
for (let j = 0; j < nV; j++) {
|
|
7420
|
-
requireFinite$
|
|
7420
|
+
requireFinite$7(weightsGrid[i][j], `weights[${i}][${j}]`);
|
|
7421
7421
|
if (weightsGrid[i][j] <= 0) throw new Error(`nurbsSurface: weights[${i}][${j}] must be > 0`);
|
|
7422
7422
|
}
|
|
7423
7423
|
}
|
|
@@ -8825,7 +8825,7 @@ async function initManifoldWasm() {
|
|
|
8825
8825
|
if (_wasm) return _wasm;
|
|
8826
8826
|
performance.mark("manifold:start");
|
|
8827
8827
|
const Module = (await __vitePreload(async () => {
|
|
8828
|
-
const { default: __vite_default__ } = await import("./manifold-
|
|
8828
|
+
const { default: __vite_default__ } = await import("./manifold-CYWZMfjB.js");
|
|
8829
8829
|
return { default: __vite_default__ };
|
|
8830
8830
|
}, true ? [] : void 0)).default;
|
|
8831
8831
|
performance.mark("manifold:imported");
|
|
@@ -9048,7 +9048,7 @@ function sweepStitched(profilePolygons, pathPoints, up, wasm) {
|
|
|
9048
9048
|
function computeParallelTransportFrames(path2, preferredUp) {
|
|
9049
9049
|
const n = path2.length;
|
|
9050
9050
|
const frames = [];
|
|
9051
|
-
const firstTangent = normalize$6(sub$
|
|
9051
|
+
const firstTangent = normalize$6(sub$4(path2[1], path2[0]));
|
|
9052
9052
|
if (!firstTangent) return null;
|
|
9053
9053
|
let x = normalize$6(cross$3(preferredUp, firstTangent));
|
|
9054
9054
|
if (!x || length(x) < 1e-8) {
|
|
@@ -9062,18 +9062,18 @@ function computeParallelTransportFrames(path2, preferredUp) {
|
|
|
9062
9062
|
const prevT = frames[i - 1].t;
|
|
9063
9063
|
let nextT;
|
|
9064
9064
|
if (i < n - 1) {
|
|
9065
|
-
const t1 = normalize$6(sub$
|
|
9066
|
-
const t2 = normalize$6(sub$
|
|
9065
|
+
const t1 = normalize$6(sub$4(path2[i], path2[i - 1]));
|
|
9066
|
+
const t2 = normalize$6(sub$4(path2[i + 1], path2[i]));
|
|
9067
9067
|
if (!t1 || !t2) return null;
|
|
9068
9068
|
nextT = normalize$6(add$2(t1, t2)) || t1;
|
|
9069
9069
|
} else {
|
|
9070
|
-
const nt = normalize$6(sub$
|
|
9070
|
+
const nt = normalize$6(sub$4(path2[i], path2[i - 1]));
|
|
9071
9071
|
if (!nt) return null;
|
|
9072
9072
|
nextT = nt;
|
|
9073
9073
|
}
|
|
9074
9074
|
const v = cross$3(prevT, nextT);
|
|
9075
9075
|
const vLen = length(v);
|
|
9076
|
-
const c = dot$
|
|
9076
|
+
const c = dot$5(prevT, nextT);
|
|
9077
9077
|
if (vLen > 1e-10) {
|
|
9078
9078
|
const axis = scale$3(v, 1 / vLen);
|
|
9079
9079
|
x = rotateVector(frames[i - 1].x, axis, c, vLen);
|
|
@@ -9147,7 +9147,7 @@ function signedArea$5(loop) {
|
|
|
9147
9147
|
}
|
|
9148
9148
|
return area2 * 0.5;
|
|
9149
9149
|
}
|
|
9150
|
-
function sub$
|
|
9150
|
+
function sub$4(a, b) {
|
|
9151
9151
|
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
9152
9152
|
}
|
|
9153
9153
|
function add$2(a, b) {
|
|
@@ -9156,7 +9156,7 @@ function add$2(a, b) {
|
|
|
9156
9156
|
function scale$3(v, s) {
|
|
9157
9157
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
9158
9158
|
}
|
|
9159
|
-
function dot$
|
|
9159
|
+
function dot$5(a, b) {
|
|
9160
9160
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
9161
9161
|
}
|
|
9162
9162
|
function cross$3(a, b) {
|
|
@@ -9175,7 +9175,7 @@ function normalize$6(v) {
|
|
|
9175
9175
|
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
9176
9176
|
}
|
|
9177
9177
|
function rotateVector(v, axis, c, s) {
|
|
9178
|
-
const kDotV = dot$
|
|
9178
|
+
const kDotV = dot$5(axis, v);
|
|
9179
9179
|
const kCrossV = cross$3(axis, v);
|
|
9180
9180
|
return [
|
|
9181
9181
|
v[0] * c + kCrossV[0] * s + axis[0] * kDotV * (1 - c),
|
|
@@ -12876,7 +12876,7 @@ function normalizeFaceSelector(selector) {
|
|
|
12876
12876
|
function cross$2(a, b) {
|
|
12877
12877
|
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]];
|
|
12878
12878
|
}
|
|
12879
|
-
function dot$
|
|
12879
|
+
function dot$4(a, b) {
|
|
12880
12880
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
12881
12881
|
}
|
|
12882
12882
|
function normVec3(v) {
|
|
@@ -12911,11 +12911,11 @@ function clusterMeshFaces(shape) {
|
|
|
12911
12911
|
if (!normal) continue;
|
|
12912
12912
|
const crossLen = Math.sqrt(rawCross[0] * rawCross[0] + rawCross[1] * rawCross[1] + rawCross[2] * rawCross[2]);
|
|
12913
12913
|
const triArea = crossLen / 2;
|
|
12914
|
-
const planeOffset = dot$
|
|
12914
|
+
const planeOffset = dot$4(normal, v0);
|
|
12915
12915
|
const triCentroid = [(v0[0] + v1[0] + v2[0]) / 3, (v0[1] + v1[1] + v2[1]) / 3, (v0[2] + v1[2] + v2[2]) / 3];
|
|
12916
12916
|
let merged = false;
|
|
12917
12917
|
for (const c of clusters) {
|
|
12918
|
-
if (dot$
|
|
12918
|
+
if (dot$4(c.normal, normal) > NORMAL_COS_EPS$1 && Math.abs(c.planeOffset - planeOffset) < PLANE_OFFSET_EPS$1) {
|
|
12919
12919
|
c.centroidSum[0] += triCentroid[0];
|
|
12920
12920
|
c.centroidSum[1] += triCentroid[1];
|
|
12921
12921
|
c.centroidSum[2] += triCentroid[2];
|
|
@@ -12957,7 +12957,7 @@ function queryMeshFaces(shape, query) {
|
|
|
12957
12957
|
let clusters = clusterMeshFaces(shape);
|
|
12958
12958
|
if (query.normal) {
|
|
12959
12959
|
const qn = query.normal;
|
|
12960
|
-
clusters = clusters.filter((c) => dot$
|
|
12960
|
+
clusters = clusters.filter((c) => dot$4(c.normal, qn) > NORMAL_COS_EPS$1);
|
|
12961
12961
|
}
|
|
12962
12962
|
if (query.planar !== false) {
|
|
12963
12963
|
clusters = clusters.filter((c) => c.normal !== null);
|
|
@@ -12973,7 +12973,7 @@ function queryMeshFace(shape, query) {
|
|
|
12973
12973
|
let clusters = clusterMeshFaces(shape);
|
|
12974
12974
|
if (query.normal) {
|
|
12975
12975
|
const qn = query.normal;
|
|
12976
|
-
clusters = clusters.filter((c) => dot$
|
|
12976
|
+
clusters = clusters.filter((c) => dot$4(c.normal, qn) > NORMAL_COS_EPS$1);
|
|
12977
12977
|
}
|
|
12978
12978
|
if (query.planar !== false) {
|
|
12979
12979
|
clusters = clusters.filter((c) => c.normal !== null);
|
|
@@ -19925,6 +19925,7 @@ Fix: pass a path like [[0,0,0], [20,0,8,4], [40,0,0,2]].`
|
|
|
19925
19925
|
const curveMode = shouldSmoothCurve(options, defaultCurve) && sourcePoints.length >= 3;
|
|
19926
19926
|
const normalized = compactPathPoints(resampleCurve(sourcePoints, options, defaultCurve));
|
|
19927
19927
|
const minRadius = Math.min(...normalized.map((point2) => point2.radius));
|
|
19928
|
+
const hasExplicitBlend = options.blend !== void 0;
|
|
19928
19929
|
const blendRadius = positiveFinite$1(options.blend, "Sculpt.tube() blend", Math.max(0.5, minRadius));
|
|
19929
19930
|
let segmentCount = 0;
|
|
19930
19931
|
for (let i = 0; i < normalized.length - 1; i += 1) {
|
|
@@ -19933,8 +19934,8 @@ Fix: pass a path like [[0,0,0], [20,0,8,4], [40,0,0,2]].`
|
|
|
19933
19934
|
if (segmentCount === 0) {
|
|
19934
19935
|
throw new Error("Sculpt.tube() points must include at least one non-zero-length segment.");
|
|
19935
19936
|
}
|
|
19936
|
-
const
|
|
19937
|
-
let out = polylineSweep(normalized,
|
|
19937
|
+
const effectiveBlendRadius = curveMode && !hasExplicitBlend ? Math.max(blendRadius, minRadius * 1.5) : blendRadius;
|
|
19938
|
+
let out = polylineSweep(normalized, effectiveBlendRadius);
|
|
19938
19939
|
if (options.polish !== void 0) out = out.polish(options.polish);
|
|
19939
19940
|
return out;
|
|
19940
19941
|
}
|
|
@@ -22326,7 +22327,7 @@ function intersection(...inputs) {
|
|
|
22326
22327
|
nextPlan
|
|
22327
22328
|
);
|
|
22328
22329
|
}
|
|
22329
|
-
var define_process_env_default = {};
|
|
22330
|
+
var define_process_env_default$2 = {};
|
|
22330
22331
|
let _wasm_solve = null;
|
|
22331
22332
|
let _wasm_get_profile = null;
|
|
22332
22333
|
let _solverMemory = null;
|
|
@@ -22361,7 +22362,7 @@ function readInitialConsoleDebug() {
|
|
|
22361
22362
|
return false;
|
|
22362
22363
|
}
|
|
22363
22364
|
function readEnvFlag(name) {
|
|
22364
|
-
const value = typeof process !== "undefined" ? define_process_env_default == null ? void 0 : define_process_env_default[name] : void 0;
|
|
22365
|
+
const value = typeof process !== "undefined" ? define_process_env_default$2 == null ? void 0 : define_process_env_default$2[name] : void 0;
|
|
22365
22366
|
if (typeof value !== "string") return false;
|
|
22366
22367
|
return ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
22367
22368
|
}
|
|
@@ -23648,7 +23649,7 @@ class MateBuilder {
|
|
|
23648
23649
|
return this.constraints.reduce((sum, c) => sum + (CONSTRAINT_EQUATIONS[c.type] ?? 0), 0);
|
|
23649
23650
|
}
|
|
23650
23651
|
}
|
|
23651
|
-
let _collected$
|
|
23652
|
+
let _collected$8 = null;
|
|
23652
23653
|
const isAxis = (value) => value === "x" || value === "y" || value === "z";
|
|
23653
23654
|
const normalizeDirection = (value, label) => {
|
|
23654
23655
|
if (value === "radial" || isAxis(value)) return value;
|
|
@@ -23707,16 +23708,16 @@ const mergeDirective = (target, patch, label) => {
|
|
|
23707
23708
|
return out;
|
|
23708
23709
|
};
|
|
23709
23710
|
function resetExplodeView() {
|
|
23710
|
-
_collected$
|
|
23711
|
+
_collected$8 = null;
|
|
23711
23712
|
}
|
|
23712
23713
|
function getCollectedExplodeView() {
|
|
23713
|
-
return _collected$
|
|
23714
|
+
return _collected$8 ? cloneOptions(_collected$8) : null;
|
|
23714
23715
|
}
|
|
23715
23716
|
function explodeView(options = {}) {
|
|
23716
23717
|
if (!options || typeof options !== "object") {
|
|
23717
23718
|
throw new Error("explodeView(options) expects an options object");
|
|
23718
23719
|
}
|
|
23719
|
-
const next = _collected$
|
|
23720
|
+
const next = _collected$8 ? cloneOptions(_collected$8) : {};
|
|
23720
23721
|
if (options.enabled !== void 0) {
|
|
23721
23722
|
if (typeof options.enabled !== "boolean") throw new Error("explodeView.enabled must be a boolean");
|
|
23722
23723
|
next.enabled = options.enabled;
|
|
@@ -23764,9 +23765,9 @@ function explodeView(options = {}) {
|
|
|
23764
23765
|
});
|
|
23765
23766
|
next.byPath = byPath;
|
|
23766
23767
|
}
|
|
23767
|
-
_collected$
|
|
23768
|
+
_collected$8 = next;
|
|
23768
23769
|
}
|
|
23769
|
-
let _collected$
|
|
23770
|
+
let _collected$7 = null;
|
|
23770
23771
|
const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
23771
23772
|
const isVec3$1 = (value) => Array.isArray(value) && value.length === 3 && isFiniteNumber(value[0]) && isFiniteNumber(value[1]) && isFiniteNumber(value[2]);
|
|
23772
23773
|
const normalizeAxis = (axis) => {
|
|
@@ -24099,22 +24100,22 @@ const cloneCollected = (value) => ({
|
|
|
24099
24100
|
defaultAnimation: value.defaultAnimation
|
|
24100
24101
|
});
|
|
24101
24102
|
function resetJointsView() {
|
|
24102
|
-
_collected$
|
|
24103
|
+
_collected$7 = null;
|
|
24103
24104
|
}
|
|
24104
24105
|
function getCollectedJointsView() {
|
|
24105
|
-
return _collected$
|
|
24106
|
+
return _collected$7 ? cloneCollected(_collected$7) : null;
|
|
24106
24107
|
}
|
|
24107
24108
|
function saveJointsView() {
|
|
24108
|
-
return _collected$
|
|
24109
|
+
return _collected$7 ? cloneCollected(_collected$7) : null;
|
|
24109
24110
|
}
|
|
24110
24111
|
function restoreJointsView(state) {
|
|
24111
|
-
_collected$
|
|
24112
|
+
_collected$7 = state;
|
|
24112
24113
|
}
|
|
24113
24114
|
function jointsView(options = {}) {
|
|
24114
24115
|
if (!options || typeof options !== "object") {
|
|
24115
24116
|
throw new Error("jointsView(options) expects an options object");
|
|
24116
24117
|
}
|
|
24117
|
-
const next = _collected$
|
|
24118
|
+
const next = _collected$7 ? cloneCollected(_collected$7) : { joints: [], couplings: [], animations: [] };
|
|
24118
24119
|
if (options.enabled !== void 0) {
|
|
24119
24120
|
if (typeof options.enabled !== "boolean") {
|
|
24120
24121
|
throw new Error("jointsView.enabled must be a boolean");
|
|
@@ -24169,8 +24170,14 @@ function jointsView(options = {}) {
|
|
|
24169
24170
|
if (next.defaultAnimation && !next.animations.some((animation) => animation.name === next.defaultAnimation)) {
|
|
24170
24171
|
throw new Error(`jointsView defaultAnimation "${next.defaultAnimation}" does not exist in animations`);
|
|
24171
24172
|
}
|
|
24172
|
-
_collected$
|
|
24173
|
+
_collected$7 = next;
|
|
24173
24174
|
}
|
|
24175
|
+
var define_process_env_default$1 = {};
|
|
24176
|
+
const SWEEP_JOINT_DEFAULT_STEP_LIMIT = {
|
|
24177
|
+
live: 1,
|
|
24178
|
+
default: 4,
|
|
24179
|
+
high: Number.POSITIVE_INFINITY
|
|
24180
|
+
};
|
|
24174
24181
|
let collectedAssemblies = [];
|
|
24175
24182
|
function resetCollectedAssemblies() {
|
|
24176
24183
|
collectedAssemblies = [];
|
|
@@ -24211,6 +24218,33 @@ function collisionShape(part) {
|
|
|
24211
24218
|
if (shapes.length === 1) return shapes[0];
|
|
24212
24219
|
return union(...shapes);
|
|
24213
24220
|
}
|
|
24221
|
+
function boundsOverlap(a, b) {
|
|
24222
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
24223
|
+
if (a.max[axis] <= b.min[axis] || b.max[axis] <= a.min[axis]) return false;
|
|
24224
|
+
}
|
|
24225
|
+
return true;
|
|
24226
|
+
}
|
|
24227
|
+
function readAssemblyPerfEnv(name) {
|
|
24228
|
+
return typeof process !== "undefined" ? define_process_env_default$1 == null ? void 0 : define_process_env_default$1[name] : void 0;
|
|
24229
|
+
}
|
|
24230
|
+
function resolveSweepJointStepLimit() {
|
|
24231
|
+
if (readAssemblyPerfEnv("FORGECAD_ALLOW_FULL_SWEEP_JOINT") === "1") return Number.POSITIVE_INFINITY;
|
|
24232
|
+
const override = readAssemblyPerfEnv("FORGECAD_SWEEP_JOINT_STEP_LIMIT");
|
|
24233
|
+
if (override != null && override.trim() !== "") {
|
|
24234
|
+
const parsed = Number(override);
|
|
24235
|
+
if (Number.isFinite(parsed) && parsed >= 1) return Math.floor(parsed);
|
|
24236
|
+
}
|
|
24237
|
+
return SWEEP_JOINT_DEFAULT_STEP_LIMIT[getForgeQualityPreset()];
|
|
24238
|
+
}
|
|
24239
|
+
function boundSweepJointSteps(jointName, requestedSteps) {
|
|
24240
|
+
const limit = resolveSweepJointStepLimit();
|
|
24241
|
+
if (!Number.isFinite(limit) || requestedSteps <= limit) return requestedSteps;
|
|
24242
|
+
const bounded = Math.max(1, Math.floor(limit));
|
|
24243
|
+
emitRuntimeWarning(
|
|
24244
|
+
`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.`
|
|
24245
|
+
);
|
|
24246
|
+
return bounded;
|
|
24247
|
+
}
|
|
24214
24248
|
const FASTENER_PATTERN = /\b(bolt|screw|nut|washer|pin|rivet|fastener|standoff|insert)\b/i;
|
|
24215
24249
|
function isFastenerName(name) {
|
|
24216
24250
|
return FASTENER_PATTERN.test(name);
|
|
@@ -24543,16 +24577,23 @@ class SolvedAssembly {
|
|
|
24543
24577
|
const minOverlap = options.minOverlapVolume ?? 0.1;
|
|
24544
24578
|
const ignore = new Set((options.ignorePairs ?? []).map(([a, b]) => [a, b].sort().join("|")));
|
|
24545
24579
|
const findings = [];
|
|
24546
|
-
|
|
24547
|
-
|
|
24548
|
-
|
|
24549
|
-
|
|
24580
|
+
const entries = [];
|
|
24581
|
+
for (const name of names) {
|
|
24582
|
+
const shape = collisionShape(this.getPart(name));
|
|
24583
|
+
if (!shape) continue;
|
|
24584
|
+
try {
|
|
24585
|
+
entries.push({ name, shape, bounds: shape.boundingBox() });
|
|
24586
|
+
} catch {
|
|
24587
|
+
}
|
|
24588
|
+
}
|
|
24589
|
+
for (let i = 0; i < entries.length; i++) {
|
|
24590
|
+
for (let j = i + 1; j < entries.length; j++) {
|
|
24591
|
+
const aName = entries[i].name;
|
|
24592
|
+
const bName = entries[j].name;
|
|
24550
24593
|
if (ignore.has([aName, bName].sort().join("|"))) continue;
|
|
24551
|
-
|
|
24552
|
-
const b = collisionShape(this.getPart(bName));
|
|
24553
|
-
if (!a || !b) continue;
|
|
24594
|
+
if (!boundsOverlap(entries[i].bounds, entries[j].bounds)) continue;
|
|
24554
24595
|
try {
|
|
24555
|
-
const hit =
|
|
24596
|
+
const hit = entries[i].shape.intersect(entries[j].shape);
|
|
24556
24597
|
if (hit.isEmpty()) continue;
|
|
24557
24598
|
const vol = hit.volume();
|
|
24558
24599
|
if (vol > minOverlap) {
|
|
@@ -25529,7 +25570,7 @@ class Assembly {
|
|
|
25529
25570
|
if (this.jointCouplings.has(jointName)) {
|
|
25530
25571
|
throw new Error(`Cannot sweep coupled joint "${jointName}". Sweep one of its source joints instead.`);
|
|
25531
25572
|
}
|
|
25532
|
-
const n = Math.max(1, Math.floor(steps));
|
|
25573
|
+
const n = boundSweepJointSteps(jointName, Math.max(1, Math.floor(steps)));
|
|
25533
25574
|
const frames = [];
|
|
25534
25575
|
for (let i = 0; i <= n; i++) {
|
|
25535
25576
|
const t = n === 0 ? 0 : i / n;
|
|
@@ -25945,12 +25986,12 @@ function bom(quantity, description, opts) {
|
|
|
25945
25986
|
metadata
|
|
25946
25987
|
});
|
|
25947
25988
|
}
|
|
25948
|
-
let _collected$
|
|
25989
|
+
let _collected$6 = [];
|
|
25949
25990
|
function resetCutPlanes() {
|
|
25950
|
-
_collected$
|
|
25991
|
+
_collected$6 = [];
|
|
25951
25992
|
}
|
|
25952
25993
|
function getCollectedCutPlanes() {
|
|
25953
|
-
return _collected$
|
|
25994
|
+
return _collected$6.slice();
|
|
25954
25995
|
}
|
|
25955
25996
|
function normalizeExcludedObjectNames(input) {
|
|
25956
25997
|
if (input === void 0) return void 0;
|
|
@@ -25966,16 +26007,16 @@ function cutPlane(name, normal, offsetOrOptions = 0, maybeOptions = {}) {
|
|
|
25966
26007
|
const offset2 = Number.isFinite(rawOffset) ? rawOffset : 0;
|
|
25967
26008
|
const options = usingOffsetArg ? maybeOptions : offsetOrOptions;
|
|
25968
26009
|
const excludeObjectNames = normalizeExcludedObjectNames(options.exclude);
|
|
25969
|
-
_collected$
|
|
26010
|
+
_collected$6.push({ name, normal, offset: offset2, excludeObjectNames });
|
|
25970
26011
|
}
|
|
25971
|
-
let _collected$
|
|
26012
|
+
let _collected$5 = [];
|
|
25972
26013
|
let _counter$1 = 0;
|
|
25973
26014
|
function resetMocks() {
|
|
25974
|
-
_collected$
|
|
26015
|
+
_collected$5 = [];
|
|
25975
26016
|
_counter$1 = 0;
|
|
25976
26017
|
}
|
|
25977
26018
|
function getCollectedMocks() {
|
|
25978
|
-
return _collected$
|
|
26019
|
+
return _collected$5.slice();
|
|
25979
26020
|
}
|
|
25980
26021
|
function mock(shape, name) {
|
|
25981
26022
|
if (!shape || typeof shape !== "object") {
|
|
@@ -25983,7 +26024,7 @@ function mock(shape, name) {
|
|
|
25983
26024
|
}
|
|
25984
26025
|
_counter$1 += 1;
|
|
25985
26026
|
const displayName = name && typeof name === "string" && name.trim().length > 0 ? name.trim() : `Mock ${_counter$1}`;
|
|
25986
|
-
_collected$
|
|
26027
|
+
_collected$5.push({
|
|
25987
26028
|
id: `mock-${_counter$1}`,
|
|
25988
26029
|
name: displayName,
|
|
25989
26030
|
shape
|
|
@@ -30185,7 +30226,7 @@ function shapeToBounds(shape) {
|
|
|
30185
30226
|
max: [bb.max[0], bb.max[1], bb.max[2]]
|
|
30186
30227
|
};
|
|
30187
30228
|
}
|
|
30188
|
-
function sub$
|
|
30229
|
+
function sub$3(a, b) {
|
|
30189
30230
|
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
30190
30231
|
}
|
|
30191
30232
|
function addVec(a, b) {
|
|
@@ -30194,7 +30235,7 @@ function addVec(a, b) {
|
|
|
30194
30235
|
function scale$2(v, s) {
|
|
30195
30236
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
30196
30237
|
}
|
|
30197
|
-
function dot$
|
|
30238
|
+
function dot$3(a, b) {
|
|
30198
30239
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
30199
30240
|
}
|
|
30200
30241
|
function cross$1(a, b) {
|
|
@@ -30218,9 +30259,9 @@ function pipeRoute(points, radius, options) {
|
|
|
30218
30259
|
const bends = new Array(points.length).fill(null);
|
|
30219
30260
|
for (let i = 1; i < points.length - 1; i++) {
|
|
30220
30261
|
const prev = points[i - 1], cur = points[i], next = points[i + 1];
|
|
30221
|
-
const dIn = normalize$3(sub$
|
|
30222
|
-
const dOut = normalize$3(sub$
|
|
30223
|
-
const dotVal = clampDot(dot$
|
|
30262
|
+
const dIn = normalize$3(sub$3(cur, prev));
|
|
30263
|
+
const dOut = normalize$3(sub$3(next, cur));
|
|
30264
|
+
const dotVal = clampDot(dot$3(dIn, dOut));
|
|
30224
30265
|
const bendAngle = Math.acos(dotVal);
|
|
30225
30266
|
if (bendAngle < 1e-6) {
|
|
30226
30267
|
continue;
|
|
@@ -30239,7 +30280,7 @@ function pipeRoute(points, radius, options) {
|
|
|
30239
30280
|
}
|
|
30240
30281
|
const parts = [];
|
|
30241
30282
|
const makeSeg = (a, b) => {
|
|
30242
|
-
const d = sub$
|
|
30283
|
+
const d = sub$3(b, a);
|
|
30243
30284
|
const len2 = vecLen(d);
|
|
30244
30285
|
if (len2 < 0.01) return null;
|
|
30245
30286
|
const dir = normalize$3(d);
|
|
@@ -30284,7 +30325,7 @@ function pipeRoute(points, radius, options) {
|
|
|
30284
30325
|
const innerBend = buildShapeFromCompilePlan(innerPlan);
|
|
30285
30326
|
bendShape = bendShape.subtract(innerBend);
|
|
30286
30327
|
}
|
|
30287
|
-
const radialDir = normalize$3(sub$
|
|
30328
|
+
const radialDir = normalize$3(sub$3(info.startPt, info.center));
|
|
30288
30329
|
const tangentDir = cross$1(info.axis, radialDir);
|
|
30289
30330
|
const c = info.center;
|
|
30290
30331
|
bendShape = bendShape.transform([
|
|
@@ -30345,7 +30386,7 @@ function elbow(pipeRadius, bendRadius, angle, options) {
|
|
|
30345
30386
|
if (fromDir && toDir) {
|
|
30346
30387
|
const nFrom = normalize$3(fromDir);
|
|
30347
30388
|
const nTo = normalize$3(toDir);
|
|
30348
|
-
const d = clampDot(dot$
|
|
30389
|
+
const d = clampDot(dot$3(nFrom, nTo));
|
|
30349
30390
|
angleDeg = Math.acos(d) * 180 / Math.PI;
|
|
30350
30391
|
}
|
|
30351
30392
|
if (angleDeg < 0.01) throw new Error("elbow: angle too small");
|
|
@@ -30416,20 +30457,20 @@ function assertFinitePositive(apiName, name, value) {
|
|
|
30416
30457
|
function add$1(a, b) {
|
|
30417
30458
|
return [a[0] + b[0], a[1] + b[1]];
|
|
30418
30459
|
}
|
|
30419
|
-
function sub$
|
|
30460
|
+
function sub$2(a, b) {
|
|
30420
30461
|
return [a[0] - b[0], a[1] - b[1]];
|
|
30421
30462
|
}
|
|
30422
30463
|
function scale$1(v, s) {
|
|
30423
30464
|
return [v[0] * s, v[1] * s];
|
|
30424
30465
|
}
|
|
30425
|
-
function dot$
|
|
30466
|
+
function dot$2(a, b) {
|
|
30426
30467
|
return a[0] * b[0] + a[1] * b[1];
|
|
30427
30468
|
}
|
|
30428
30469
|
function len(v) {
|
|
30429
30470
|
return Math.hypot(v[0], v[1]);
|
|
30430
30471
|
}
|
|
30431
30472
|
function dist$2(a, b) {
|
|
30432
|
-
return len(sub$
|
|
30473
|
+
return len(sub$2(b, a));
|
|
30433
30474
|
}
|
|
30434
30475
|
function norm$1(v) {
|
|
30435
30476
|
const l = len(v);
|
|
@@ -30454,7 +30495,7 @@ function chooseSweepDeg(center, start, end, incomingDir) {
|
|
|
30454
30495
|
const endAngle = angleOf(center, end);
|
|
30455
30496
|
const cwTangent = tangentAt(startAngle, true);
|
|
30456
30497
|
const ccwTangent = tangentAt(startAngle, false);
|
|
30457
|
-
const clockwise = dot$
|
|
30498
|
+
const clockwise = dot$2(cwTangent, incomingDir) >= dot$2(ccwTangent, incomingDir);
|
|
30458
30499
|
const sweep2 = clockwise ? -normalizePositiveRadians(startAngle - endAngle) : normalizePositiveRadians(endAngle - startAngle);
|
|
30459
30500
|
return sweep2 * 180 / Math.PI;
|
|
30460
30501
|
}
|
|
@@ -30497,8 +30538,8 @@ function normalizePulleyAsCircle(pulley, index, radiusOverride) {
|
|
|
30497
30538
|
);
|
|
30498
30539
|
}
|
|
30499
30540
|
function commonTangents(a, b, mode) {
|
|
30500
|
-
const delta = sub$
|
|
30501
|
-
const z = dot$
|
|
30541
|
+
const delta = sub$2(b.center, a.center);
|
|
30542
|
+
const z = dot$2(delta, delta);
|
|
30502
30543
|
if (z < EPS$5) {
|
|
30503
30544
|
throw new Error(`beltDrive: pulleys "${a.name}" and "${b.name}" have the same center.`);
|
|
30504
30545
|
}
|
|
@@ -30528,8 +30569,8 @@ function commonTangents(a, b, mode) {
|
|
|
30528
30569
|
});
|
|
30529
30570
|
}
|
|
30530
30571
|
function buildSegmentsForTangentOrder(a, b, t0, t1) {
|
|
30531
|
-
const span0Dir = norm$1(sub$
|
|
30532
|
-
const span1Dir = norm$1(sub$
|
|
30572
|
+
const span0Dir = norm$1(sub$2(t0.b, t0.a));
|
|
30573
|
+
const span1Dir = norm$1(sub$2(t1.a, t1.b));
|
|
30533
30574
|
const bSweepDeg = chooseSweepDeg(b.center, t0.b, t1.b, span0Dir);
|
|
30534
30575
|
const aSweepDeg = chooseSweepDeg(a.center, t1.a, t0.a, span1Dir);
|
|
30535
30576
|
const span0 = {
|
|
@@ -73204,6 +73245,130 @@ function shapeToGeometryFallback(shape) {
|
|
|
73204
73245
|
const edges = new EdgesGeometry(solid, 1);
|
|
73205
73246
|
return { solid, edges, hasSmoothNormals: false };
|
|
73206
73247
|
}
|
|
73248
|
+
let _collected$4 = [];
|
|
73249
|
+
let _nextId = 1;
|
|
73250
|
+
function resetRenderLabels() {
|
|
73251
|
+
_collected$4 = [];
|
|
73252
|
+
_nextId = 1;
|
|
73253
|
+
}
|
|
73254
|
+
function getCollectedRenderLabels() {
|
|
73255
|
+
return _collected$4.map((label) => ({ ...label, at: [...label.at], offset: [...label.offset] }));
|
|
73256
|
+
}
|
|
73257
|
+
function requireFinite$6(value, label) {
|
|
73258
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
73259
|
+
throw new Error(`${label} must be a finite number`);
|
|
73260
|
+
}
|
|
73261
|
+
return value;
|
|
73262
|
+
}
|
|
73263
|
+
function requireVec3$2(value, label) {
|
|
73264
|
+
if (!Array.isArray(value) || value.length !== 3) {
|
|
73265
|
+
throw new Error(`${label} must be [x, y, z]`);
|
|
73266
|
+
}
|
|
73267
|
+
return [requireFinite$6(value[0], `${label}[0]`), requireFinite$6(value[1], `${label}[1]`), requireFinite$6(value[2], `${label}[2]`)];
|
|
73268
|
+
}
|
|
73269
|
+
function optionalColor(value, label) {
|
|
73270
|
+
if (value === void 0) return void 0;
|
|
73271
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
73272
|
+
throw new Error(`${label} must be a non-empty CSS color string`);
|
|
73273
|
+
}
|
|
73274
|
+
return value.trim();
|
|
73275
|
+
}
|
|
73276
|
+
const VALID_ANCHORS = /* @__PURE__ */ new Set([
|
|
73277
|
+
"center",
|
|
73278
|
+
"top",
|
|
73279
|
+
"bottom",
|
|
73280
|
+
"left",
|
|
73281
|
+
"right",
|
|
73282
|
+
"top-left",
|
|
73283
|
+
"top-right",
|
|
73284
|
+
"bottom-left",
|
|
73285
|
+
"bottom-right"
|
|
73286
|
+
]);
|
|
73287
|
+
function normalizeOptions(options) {
|
|
73288
|
+
if (options === void 0) {
|
|
73289
|
+
return { offset: [0, 0, 0], anchor: "center", alwaysOnTop: true };
|
|
73290
|
+
}
|
|
73291
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
73292
|
+
throw new Error("Viewport.label options must be an object");
|
|
73293
|
+
}
|
|
73294
|
+
const out = {
|
|
73295
|
+
offset: [0, 0, 0],
|
|
73296
|
+
anchor: "center",
|
|
73297
|
+
alwaysOnTop: true
|
|
73298
|
+
};
|
|
73299
|
+
const color = optionalColor(options.color, "Viewport.label options.color");
|
|
73300
|
+
if (color !== void 0) out.color = color;
|
|
73301
|
+
const background = optionalColor(options.background, "Viewport.label options.background");
|
|
73302
|
+
if (background !== void 0) out.background = background;
|
|
73303
|
+
if (options.size !== void 0) {
|
|
73304
|
+
out.size = requireFinite$6(options.size, "Viewport.label options.size");
|
|
73305
|
+
if (out.size <= 0) throw new Error("Viewport.label options.size must be positive");
|
|
73306
|
+
}
|
|
73307
|
+
if (options.offset !== void 0) out.offset = requireVec3$2(options.offset, "Viewport.label options.offset");
|
|
73308
|
+
if (options.anchor !== void 0) {
|
|
73309
|
+
if (!VALID_ANCHORS.has(options.anchor)) {
|
|
73310
|
+
throw new Error(`Viewport.label options.anchor must be one of: ${Array.from(VALID_ANCHORS).join(", ")}`);
|
|
73311
|
+
}
|
|
73312
|
+
out.anchor = options.anchor;
|
|
73313
|
+
}
|
|
73314
|
+
if (options.alwaysOnTop !== void 0) {
|
|
73315
|
+
if (typeof options.alwaysOnTop !== "boolean") throw new Error("Viewport.label options.alwaysOnTop must be a boolean");
|
|
73316
|
+
out.alwaysOnTop = options.alwaysOnTop;
|
|
73317
|
+
}
|
|
73318
|
+
return out;
|
|
73319
|
+
}
|
|
73320
|
+
function collectRenderLabel(text, at, options) {
|
|
73321
|
+
if (typeof text !== "string" || text.trim().length === 0) {
|
|
73322
|
+
throw new Error("Viewport.label text must be a non-empty string");
|
|
73323
|
+
}
|
|
73324
|
+
const normalizedAt = requireVec3$2(at, "Viewport.label at");
|
|
73325
|
+
const normalizedOptions = normalizeOptions(options);
|
|
73326
|
+
_collected$4.push({
|
|
73327
|
+
id: `render-label-${_nextId++}`,
|
|
73328
|
+
text: text.trim(),
|
|
73329
|
+
at: normalizedAt,
|
|
73330
|
+
...normalizedOptions
|
|
73331
|
+
});
|
|
73332
|
+
}
|
|
73333
|
+
const Viewport = {
|
|
73334
|
+
/**
|
|
73335
|
+
* Add a render-only viewport label at a world-space point.
|
|
73336
|
+
*
|
|
73337
|
+
* **Details**
|
|
73338
|
+
*
|
|
73339
|
+
* `Viewport.label()` is for explanatory text that helps a viewer understand
|
|
73340
|
+
* the model. It does not create sketches, meshes, B-rep topology, exported
|
|
73341
|
+
* text, or face labels, so it stays off the OCCT path. Use `text2d()` only
|
|
73342
|
+
* when the letters should become manufactured geometry, such as raised
|
|
73343
|
+
* lettering, engraved serial numbers, or exported nameplates.
|
|
73344
|
+
*
|
|
73345
|
+
* Labels are collected during script execution and rendered by the viewport
|
|
73346
|
+
* as lightweight overlay annotations. They are ignored by exports and do not
|
|
73347
|
+
* appear in `objects`.
|
|
73348
|
+
*
|
|
73349
|
+
* **Example**
|
|
73350
|
+
*
|
|
73351
|
+
* ```js
|
|
73352
|
+
* Viewport.label('Bearing bore', [0, 0, 18], {
|
|
73353
|
+
* color: '#f8fafc',
|
|
73354
|
+
* background: '#0f172acc',
|
|
73355
|
+
* offset: [0, 0, 8],
|
|
73356
|
+
* anchor: 'bottom',
|
|
73357
|
+
* });
|
|
73358
|
+
*
|
|
73359
|
+
* return box(40, 30, 12);
|
|
73360
|
+
* ```
|
|
73361
|
+
*
|
|
73362
|
+
* @param text - Label text to display in the viewport
|
|
73363
|
+
* @param at - World-space anchor point `[x, y, z]`
|
|
73364
|
+
* @param options - Visual label options
|
|
73365
|
+
* @returns void
|
|
73366
|
+
* @category Viewport Labels
|
|
73367
|
+
*/
|
|
73368
|
+
label(text, at, options) {
|
|
73369
|
+
collectRenderLabel(text, at, options);
|
|
73370
|
+
}
|
|
73371
|
+
};
|
|
73207
73372
|
const DEFAULT_RENDER_STYLE = "classic";
|
|
73208
73373
|
const RENDER_STYLE_OPTIONS = [
|
|
73209
73374
|
{
|
|
@@ -73614,6 +73779,120 @@ function validateCamera(cam, label) {
|
|
|
73614
73779
|
}
|
|
73615
73780
|
return out;
|
|
73616
73781
|
}
|
|
73782
|
+
function validateViewCamera(cam, label) {
|
|
73783
|
+
const validated = validateCamera(cam, label);
|
|
73784
|
+
if (!validated.position) {
|
|
73785
|
+
throw new Error(`${label}.position is required for named render views`);
|
|
73786
|
+
}
|
|
73787
|
+
if (!validated.target) {
|
|
73788
|
+
throw new Error(`${label}.target is required for named render views`);
|
|
73789
|
+
}
|
|
73790
|
+
return {
|
|
73791
|
+
...validated,
|
|
73792
|
+
position: validated.position,
|
|
73793
|
+
target: validated.target
|
|
73794
|
+
};
|
|
73795
|
+
}
|
|
73796
|
+
function validateViews(views, label) {
|
|
73797
|
+
if (!views || typeof views !== "object" || Array.isArray(views)) {
|
|
73798
|
+
throw new Error(`${label} must be an object mapping view names to cameras`);
|
|
73799
|
+
}
|
|
73800
|
+
const out = {};
|
|
73801
|
+
for (const [name, view] of Object.entries(views)) {
|
|
73802
|
+
if (!name.trim()) {
|
|
73803
|
+
throw new Error(`${label} names must be non-empty strings`);
|
|
73804
|
+
}
|
|
73805
|
+
const viewLabel = `${label}.${name}`;
|
|
73806
|
+
if (!view || typeof view !== "object" || Array.isArray(view)) {
|
|
73807
|
+
throw new Error(`${viewLabel} must be a camera object or an object with a camera property`);
|
|
73808
|
+
}
|
|
73809
|
+
const hasExplicitCamera = Object.prototype.hasOwnProperty.call(view, "camera");
|
|
73810
|
+
if (hasExplicitCamera) {
|
|
73811
|
+
const camera = view.camera;
|
|
73812
|
+
if (!camera || typeof camera !== "object" || Array.isArray(camera)) {
|
|
73813
|
+
throw new Error(`${viewLabel}.camera must be an object`);
|
|
73814
|
+
}
|
|
73815
|
+
out[name] = { camera: validateViewCamera(camera, `${viewLabel}.camera`) };
|
|
73816
|
+
continue;
|
|
73817
|
+
}
|
|
73818
|
+
out[name] = { camera: validateViewCamera(view, viewLabel) };
|
|
73819
|
+
}
|
|
73820
|
+
return out;
|
|
73821
|
+
}
|
|
73822
|
+
function requireString(value, label) {
|
|
73823
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
73824
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
73825
|
+
}
|
|
73826
|
+
return value.trim();
|
|
73827
|
+
}
|
|
73828
|
+
function optionalString(value, label) {
|
|
73829
|
+
if (value === void 0) return void 0;
|
|
73830
|
+
return requireString(value, label);
|
|
73831
|
+
}
|
|
73832
|
+
function validateJourneyStep(step, label) {
|
|
73833
|
+
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
73834
|
+
throw new Error(`${label} must be an object`);
|
|
73835
|
+
}
|
|
73836
|
+
const out = {
|
|
73837
|
+
id: requireString(step.id, `${label}.id`)
|
|
73838
|
+
};
|
|
73839
|
+
const title = optionalString(step.title, `${label}.title`);
|
|
73840
|
+
if (title !== void 0) out.title = title;
|
|
73841
|
+
const focus = optionalString(step.focus, `${label}.focus`);
|
|
73842
|
+
if (focus !== void 0) out.focus = focus;
|
|
73843
|
+
const caption = optionalString(step.caption, `${label}.caption`);
|
|
73844
|
+
if (caption !== void 0) out.caption = caption;
|
|
73845
|
+
if (step.camera !== void 0) {
|
|
73846
|
+
if (!step.camera || typeof step.camera !== "object" || Array.isArray(step.camera)) {
|
|
73847
|
+
throw new Error(`${label}.camera must be an object`);
|
|
73848
|
+
}
|
|
73849
|
+
out.camera = validateViewCamera(step.camera, `${label}.camera`);
|
|
73850
|
+
}
|
|
73851
|
+
return out;
|
|
73852
|
+
}
|
|
73853
|
+
function validateJourney(journey, label) {
|
|
73854
|
+
if (!journey || typeof journey !== "object" || Array.isArray(journey)) {
|
|
73855
|
+
throw new Error(`${label} must be an object`);
|
|
73856
|
+
}
|
|
73857
|
+
if (!Array.isArray(journey.steps) || journey.steps.length === 0) {
|
|
73858
|
+
throw new Error(`${label}.steps must be a non-empty array`);
|
|
73859
|
+
}
|
|
73860
|
+
const out = {
|
|
73861
|
+
steps: journey.steps.map((step, index) => validateJourneyStep(step, `${label}.steps[${index}]`))
|
|
73862
|
+
};
|
|
73863
|
+
const title = optionalString(journey.title, `${label}.title`);
|
|
73864
|
+
if (title !== void 0) out.title = title;
|
|
73865
|
+
const startsAt = optionalString(journey.startsAt, `${label}.startsAt`);
|
|
73866
|
+
if (startsAt !== void 0) out.startsAt = startsAt;
|
|
73867
|
+
if (journey.behavior !== void 0) {
|
|
73868
|
+
if (journey.behavior !== "opt-in" && journey.behavior !== "auto") {
|
|
73869
|
+
throw new Error(`${label}.behavior must be "opt-in" or "auto"`);
|
|
73870
|
+
}
|
|
73871
|
+
out.behavior = journey.behavior;
|
|
73872
|
+
}
|
|
73873
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
73874
|
+
for (const step of out.steps) {
|
|
73875
|
+
if (seen2.has(step.id)) {
|
|
73876
|
+
throw new Error(`${label}.steps contains duplicate step id "${step.id}"`);
|
|
73877
|
+
}
|
|
73878
|
+
seen2.add(step.id);
|
|
73879
|
+
}
|
|
73880
|
+
if (out.startsAt && !seen2.has(out.startsAt)) {
|
|
73881
|
+
throw new Error(`${label}.startsAt "${out.startsAt}" does not match any step id`);
|
|
73882
|
+
}
|
|
73883
|
+
return out;
|
|
73884
|
+
}
|
|
73885
|
+
function validateJourneys(journeys, label) {
|
|
73886
|
+
if (!journeys || typeof journeys !== "object" || Array.isArray(journeys)) {
|
|
73887
|
+
throw new Error(`${label} must be an object mapping journey ids to journey configs`);
|
|
73888
|
+
}
|
|
73889
|
+
const out = {};
|
|
73890
|
+
for (const [id, journey] of Object.entries(journeys)) {
|
|
73891
|
+
const normalizedId = requireString(id, `${label} journey id`);
|
|
73892
|
+
out[normalizedId] = validateJourney(journey, `${label}.${normalizedId}`);
|
|
73893
|
+
}
|
|
73894
|
+
return out;
|
|
73895
|
+
}
|
|
73617
73896
|
function validateLight(light, label) {
|
|
73618
73897
|
if (!light || typeof light !== "object") throw new Error(`${label} must be an object`);
|
|
73619
73898
|
if (!VALID_LIGHT_TYPES.has(light.type)) {
|
|
@@ -73757,7 +74036,18 @@ function scene(options) {
|
|
|
73757
74036
|
if (!options || typeof options !== "object") {
|
|
73758
74037
|
throw new Error("scene(options) expects an options object");
|
|
73759
74038
|
}
|
|
73760
|
-
const current = _collected$3 ? { ..._collected$3 } : {
|
|
74039
|
+
const current = _collected$3 ? { ..._collected$3 } : {
|
|
74040
|
+
background: null,
|
|
74041
|
+
camera: null,
|
|
74042
|
+
views: null,
|
|
74043
|
+
journeys: null,
|
|
74044
|
+
lights: null,
|
|
74045
|
+
environment: null,
|
|
74046
|
+
fog: null,
|
|
74047
|
+
postProcessing: null,
|
|
74048
|
+
ground: null,
|
|
74049
|
+
capture: null
|
|
74050
|
+
};
|
|
73761
74051
|
if (options.background !== void 0) {
|
|
73762
74052
|
current.background = validateBackground(options.background, "scene.background");
|
|
73763
74053
|
}
|
|
@@ -73768,6 +74058,14 @@ function scene(options) {
|
|
|
73768
74058
|
const validated = validateCamera(options.camera, "scene.camera");
|
|
73769
74059
|
current.camera = current.camera ? { ...current.camera, ...validated } : validated;
|
|
73770
74060
|
}
|
|
74061
|
+
if (options.views !== void 0) {
|
|
74062
|
+
const validated = validateViews(options.views, "scene.views");
|
|
74063
|
+
current.views = current.views ? { ...current.views, ...validated } : validated;
|
|
74064
|
+
}
|
|
74065
|
+
if (options.journeys !== void 0) {
|
|
74066
|
+
const validated = validateJourneys(options.journeys, "scene.journeys");
|
|
74067
|
+
current.journeys = current.journeys ? { ...current.journeys, ...validated } : validated;
|
|
74068
|
+
}
|
|
73771
74069
|
if (options.lights !== void 0) {
|
|
73772
74070
|
if (!Array.isArray(options.lights)) {
|
|
73773
74071
|
throw new Error("scene.lights must be an array");
|
|
@@ -73808,6 +74106,74 @@ function scene(options) {
|
|
|
73808
74106
|
}
|
|
73809
74107
|
_collected$3 = current;
|
|
73810
74108
|
}
|
|
74109
|
+
const targetPath = (target) => {
|
|
74110
|
+
var _a3;
|
|
74111
|
+
const path2 = (_a3 = target.treePath) == null ? void 0 : _a3.filter((entry) => entry.trim());
|
|
74112
|
+
return path2 && path2.length > 0 ? path2.join("/") : target.name;
|
|
74113
|
+
};
|
|
74114
|
+
const hasErrorDiagnostic = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.level === "error");
|
|
74115
|
+
function resolveJourneyFocus(focus, targets) {
|
|
74116
|
+
return targets.filter((target) => target.name === focus || targetPath(target) === focus);
|
|
74117
|
+
}
|
|
74118
|
+
function formatAvailableTargets(targets) {
|
|
74119
|
+
return targets.map((target) => targetPath(target)).filter((value, index, values) => values.indexOf(value) === index).sort().slice(0, 8);
|
|
74120
|
+
}
|
|
74121
|
+
function resolveSceneJourneyTargets(config, targets) {
|
|
74122
|
+
if (!(config == null ? void 0 : config.journeys)) return config;
|
|
74123
|
+
const journeys = {};
|
|
74124
|
+
for (const [journeyId, journey] of Object.entries(config.journeys)) {
|
|
74125
|
+
const journeyDiagnostics = [...journey.diagnostics ?? []];
|
|
74126
|
+
const steps = journey.steps.map((step) => {
|
|
74127
|
+
const stepDiagnostics = [...step.diagnostics ?? []];
|
|
74128
|
+
let resolvedFocusId = null;
|
|
74129
|
+
let resolvedFocusPath = null;
|
|
74130
|
+
if (step.focus) {
|
|
74131
|
+
const matches = resolveJourneyFocus(step.focus, targets);
|
|
74132
|
+
if (matches.length === 1) {
|
|
74133
|
+
resolvedFocusId = matches[0].id;
|
|
74134
|
+
resolvedFocusPath = targetPath(matches[0]);
|
|
74135
|
+
} else if (matches.length === 0) {
|
|
74136
|
+
stepDiagnostics.push({
|
|
74137
|
+
level: "error",
|
|
74138
|
+
stepId: step.id,
|
|
74139
|
+
message: `focus "${step.focus}" did not match any returned object by name or tree path.`,
|
|
74140
|
+
suggestions: formatAvailableTargets(targets)
|
|
74141
|
+
});
|
|
74142
|
+
} else {
|
|
74143
|
+
stepDiagnostics.push({
|
|
74144
|
+
level: "error",
|
|
74145
|
+
stepId: step.id,
|
|
74146
|
+
message: `focus "${step.focus}" matched ${matches.length} objects. Use a slash-separated tree path.`,
|
|
74147
|
+
suggestions: matches.map((match) => targetPath(match))
|
|
74148
|
+
});
|
|
74149
|
+
}
|
|
74150
|
+
} else if (!step.camera) {
|
|
74151
|
+
stepDiagnostics.push({
|
|
74152
|
+
level: "warning",
|
|
74153
|
+
stepId: step.id,
|
|
74154
|
+
message: "step has no focus or explicit camera, so the viewer can show the caption but cannot move the camera."
|
|
74155
|
+
});
|
|
74156
|
+
}
|
|
74157
|
+
journeyDiagnostics.push(...stepDiagnostics);
|
|
74158
|
+
return {
|
|
74159
|
+
...step,
|
|
74160
|
+
resolvedFocusId,
|
|
74161
|
+
resolvedFocusPath,
|
|
74162
|
+
diagnostics: stepDiagnostics.length > 0 ? stepDiagnostics : void 0
|
|
74163
|
+
};
|
|
74164
|
+
});
|
|
74165
|
+
journeys[journeyId] = {
|
|
74166
|
+
...journey,
|
|
74167
|
+
steps,
|
|
74168
|
+
valid: !hasErrorDiagnostic(journeyDiagnostics),
|
|
74169
|
+
diagnostics: journeyDiagnostics.length > 0 ? journeyDiagnostics : void 0
|
|
74170
|
+
};
|
|
74171
|
+
}
|
|
74172
|
+
return {
|
|
74173
|
+
...config,
|
|
74174
|
+
journeys
|
|
74175
|
+
};
|
|
74176
|
+
}
|
|
73811
74177
|
const validateColor = (value, label) => {
|
|
73812
74178
|
if (typeof value !== "string") throw new Error(`${label} must be a string`);
|
|
73813
74179
|
const trimmed = value.trim();
|
|
@@ -75053,9 +75419,15 @@ function cross(a, b) {
|
|
|
75053
75419
|
function add(a, b) {
|
|
75054
75420
|
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
75055
75421
|
}
|
|
75422
|
+
function sub$1(a, b) {
|
|
75423
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
75424
|
+
}
|
|
75056
75425
|
function scale(v, s) {
|
|
75057
75426
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
75058
75427
|
}
|
|
75428
|
+
function dot$1(a, b) {
|
|
75429
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
75430
|
+
}
|
|
75059
75431
|
function lerp$1(a, b, t) {
|
|
75060
75432
|
return a + (b - a) * t;
|
|
75061
75433
|
}
|
|
@@ -75088,6 +75460,65 @@ function sideVectors(axis) {
|
|
|
75088
75460
|
function cloneQuery(query) {
|
|
75089
75461
|
return { side: query.side, u: query.u, v: query.v, offset: query.offset };
|
|
75090
75462
|
}
|
|
75463
|
+
function normalizedSide(side) {
|
|
75464
|
+
return side === "back" ? "rear" : side;
|
|
75465
|
+
}
|
|
75466
|
+
function profileExponent(station) {
|
|
75467
|
+
if (station.profile.kind === "superEllipse") return station.profile.exponent ?? 3.2;
|
|
75468
|
+
if (station.profile.kind === "roundedRect") return 4.5;
|
|
75469
|
+
return 2;
|
|
75470
|
+
}
|
|
75471
|
+
function superEllipsePoint(rx, ry, exponent, angle) {
|
|
75472
|
+
const cos2 = Math.cos(angle);
|
|
75473
|
+
const sin2 = Math.sin(angle);
|
|
75474
|
+
const x = rx * Math.sign(cos2) * Math.abs(cos2) ** (2 / exponent);
|
|
75475
|
+
const y = ry * Math.sign(sin2) * Math.abs(sin2) ** (2 / exponent);
|
|
75476
|
+
const nx = Math.sign(x) * Math.abs(x / Math.max(rx, EPS$4)) ** Math.max(exponent - 1, 1e-3);
|
|
75477
|
+
const ny = Math.sign(y) * Math.abs(y / Math.max(ry, EPS$4)) ** Math.max(exponent - 1, 1e-3);
|
|
75478
|
+
const nLen = Math.hypot(nx, ny);
|
|
75479
|
+
return {
|
|
75480
|
+
point: [x, y],
|
|
75481
|
+
normal: nLen < EPS$4 ? [Math.sign(cos2), Math.sign(sin2)] : [nx / nLen, ny / nLen]
|
|
75482
|
+
};
|
|
75483
|
+
}
|
|
75484
|
+
function angleForSide(side, u) {
|
|
75485
|
+
const t = clamp$4(u, 0, 1);
|
|
75486
|
+
if (side === "right") return -Math.PI / 2 + t * Math.PI;
|
|
75487
|
+
if (side === "left") return Math.PI / 2 + t * Math.PI;
|
|
75488
|
+
if (side === "top") return Math.PI - t * Math.PI;
|
|
75489
|
+
if (side === "bottom") return Math.PI + t * Math.PI;
|
|
75490
|
+
return null;
|
|
75491
|
+
}
|
|
75492
|
+
function sideSpan(side, width, depth) {
|
|
75493
|
+
if (side === "left" || side === "right") return Math.max(depth, EPS$4);
|
|
75494
|
+
if (side === "top" || side === "bottom") return Math.max(width, EPS$4);
|
|
75495
|
+
return Math.max(width, depth, EPS$4);
|
|
75496
|
+
}
|
|
75497
|
+
function interpolateQuery(a, b, t) {
|
|
75498
|
+
const sideA = normalizedSide(a.side);
|
|
75499
|
+
const sideB = normalizedSide(b.side);
|
|
75500
|
+
if (sideA !== sideB) {
|
|
75501
|
+
throw new Error(
|
|
75502
|
+
`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.`
|
|
75503
|
+
);
|
|
75504
|
+
}
|
|
75505
|
+
return {
|
|
75506
|
+
side: sideA,
|
|
75507
|
+
u: lerp$1(a.u ?? 0.5, b.u ?? 0.5, t),
|
|
75508
|
+
v: lerp$1(a.v ?? 0.5, b.v ?? 0.5, t),
|
|
75509
|
+
offset: lerp$1(a.offset ?? 0, b.offset ?? 0, t)
|
|
75510
|
+
};
|
|
75511
|
+
}
|
|
75512
|
+
function resolvePathQueries(points) {
|
|
75513
|
+
return points.map((point2) => point2 instanceof ProductSurfaceRef ? point2.querySpec() : cloneQuery(point2));
|
|
75514
|
+
}
|
|
75515
|
+
function orientGridToNormal(grid, desiredNormal) {
|
|
75516
|
+
if (grid.length < 2 || grid[0].length < 2) return grid;
|
|
75517
|
+
const widthEdge = sub$1(grid[grid.length - 1][0], grid[0][0]);
|
|
75518
|
+
const lengthEdge = sub$1(grid[0][grid[0].length - 1], grid[0][0]);
|
|
75519
|
+
const actual = norm(cross(widthEdge, lengthEdge));
|
|
75520
|
+
return dot$1(actual, desiredNormal) < 0 ? [...grid].reverse() : grid;
|
|
75521
|
+
}
|
|
75091
75522
|
function isStationBuilder(input) {
|
|
75092
75523
|
return typeof input.toSpec === "function";
|
|
75093
75524
|
}
|
|
@@ -75189,6 +75620,15 @@ class ProductSkin {
|
|
|
75189
75620
|
curveOnSurface(name, points) {
|
|
75190
75621
|
return points.map((point2, index) => new ProductSurfaceRef(this, { u: 0.5, v: 0.5, ...point2 }, `${name}/${index}`));
|
|
75191
75622
|
}
|
|
75623
|
+
/**
|
|
75624
|
+
* Create a fluent surface helper for refs and conformal features on one side of this skin.
|
|
75625
|
+
*
|
|
75626
|
+
* Use this when several refs or ribbons share the same skin side; side-local helpers keep
|
|
75627
|
+
* path points concise and make it harder to mix sides accidentally.
|
|
75628
|
+
*/
|
|
75629
|
+
surface(side) {
|
|
75630
|
+
return new ProductSurfaceBuilder(this, side);
|
|
75631
|
+
}
|
|
75192
75632
|
/** Interpolate center, width, and depth at a normalized v or absolute axis value. */
|
|
75193
75633
|
stationAt(vOrAxis) {
|
|
75194
75634
|
const axisValue = vOrAxis >= 0 && vOrAxis <= 1 ? lerp$1(this.axisMin, this.axisMax, vOrAxis) : clamp$4(vOrAxis, this.axisMin, this.axisMax);
|
|
@@ -75207,7 +75647,9 @@ class ProductSkin {
|
|
|
75207
75647
|
width: lerp$1(a.profile.width, b.profile.width, t),
|
|
75208
75648
|
depth: lerp$1(a.profile.depth, b.profile.depth, t),
|
|
75209
75649
|
dWidth: (b.profile.width - a.profile.width) / span,
|
|
75210
|
-
dDepth: (b.profile.depth - a.profile.depth) / span
|
|
75650
|
+
dDepth: (b.profile.depth - a.profile.depth) / span,
|
|
75651
|
+
exponent: lerp$1(profileExponent(a), profileExponent(b), t),
|
|
75652
|
+
kind: a.profile.kind === b.profile.kind ? a.profile.kind : "custom"
|
|
75211
75653
|
};
|
|
75212
75654
|
}
|
|
75213
75655
|
const last = sorted[sorted.length - 1];
|
|
@@ -75217,12 +75659,14 @@ class ProductSkin {
|
|
|
75217
75659
|
width: last.profile.width,
|
|
75218
75660
|
depth: last.profile.depth,
|
|
75219
75661
|
dWidth: 0,
|
|
75220
|
-
dDepth: 0
|
|
75662
|
+
dDepth: 0,
|
|
75663
|
+
exponent: profileExponent(last),
|
|
75664
|
+
kind: last.profile.kind
|
|
75221
75665
|
};
|
|
75222
75666
|
}
|
|
75223
75667
|
/** Build a local surface frame from a side/u/v query. */
|
|
75224
75668
|
frame(query) {
|
|
75225
|
-
const side = query.side
|
|
75669
|
+
const side = normalizedSide(query.side);
|
|
75226
75670
|
const offset2 = query.offset ?? 0;
|
|
75227
75671
|
const basis = sideVectors(this.axis);
|
|
75228
75672
|
const isFrontCap = side === "front";
|
|
@@ -75246,11 +75690,31 @@ class ProductSkin {
|
|
|
75246
75690
|
}
|
|
75247
75691
|
const station = this.stationAt(query.v ?? 0.5);
|
|
75248
75692
|
const u = clamp$4(query.u ?? 0.5, 0, 1) - 0.5;
|
|
75693
|
+
const sideAngle = angleForSide(side, query.u ?? 0.5);
|
|
75249
75694
|
let crossA = 0;
|
|
75250
75695
|
let crossB = 0;
|
|
75251
75696
|
let normal = [0, 0, 1];
|
|
75252
75697
|
let tangentU = basis.crossA;
|
|
75253
|
-
if (
|
|
75698
|
+
if (sideAngle != null) {
|
|
75699
|
+
const section = superEllipsePoint(station.width / 2, station.depth / 2, station.exponent, sideAngle);
|
|
75700
|
+
crossA = section.point[0];
|
|
75701
|
+
crossB = section.point[1];
|
|
75702
|
+
normal = norm(add(scale(basis.crossA, section.normal[0]), scale(basis.crossB, section.normal[1])));
|
|
75703
|
+
const delta = 2e-3;
|
|
75704
|
+
const prev = superEllipsePoint(
|
|
75705
|
+
station.width / 2,
|
|
75706
|
+
station.depth / 2,
|
|
75707
|
+
station.exponent,
|
|
75708
|
+
angleForSide(side, clamp$4((query.u ?? 0.5) - delta, 0, 1)) ?? sideAngle
|
|
75709
|
+
).point;
|
|
75710
|
+
const next = superEllipsePoint(
|
|
75711
|
+
station.width / 2,
|
|
75712
|
+
station.depth / 2,
|
|
75713
|
+
station.exponent,
|
|
75714
|
+
angleForSide(side, clamp$4((query.u ?? 0.5) + delta, 0, 1)) ?? sideAngle
|
|
75715
|
+
).point;
|
|
75716
|
+
tangentU = norm(add(scale(basis.crossA, next[0] - prev[0]), scale(basis.crossB, next[1] - prev[1])));
|
|
75717
|
+
} else if (side === "right") {
|
|
75254
75718
|
crossA = station.width / 2;
|
|
75255
75719
|
crossB = u * station.depth;
|
|
75256
75720
|
normal = basis.crossA;
|
|
@@ -75272,7 +75736,7 @@ class ProductSkin {
|
|
|
75272
75736
|
tangentU = basis.crossA;
|
|
75273
75737
|
}
|
|
75274
75738
|
normal = norm(normal);
|
|
75275
|
-
tangentU = norm(tangentU);
|
|
75739
|
+
tangentU = norm(sub$1(tangentU, scale(normal, dot$1(tangentU, normal))));
|
|
75276
75740
|
const tangentV = norm(cross(normal, tangentU));
|
|
75277
75741
|
const point2 = add(add(station.center, add(scale(basis.crossA, crossA), scale(basis.crossB, crossB))), scale(normal, offset2));
|
|
75278
75742
|
return {
|
|
@@ -75592,6 +76056,303 @@ class ProductHandleBuilder {
|
|
|
75592
76056
|
return new ProductHandleFeature(grip, upperPad, lowerPad);
|
|
75593
76057
|
}
|
|
75594
76058
|
}
|
|
76059
|
+
class ProductSurfaceBuilder {
|
|
76060
|
+
constructor(skin, side) {
|
|
76061
|
+
this.skin = skin;
|
|
76062
|
+
this.side = side;
|
|
76063
|
+
}
|
|
76064
|
+
/** Create a ref on this skin side. */
|
|
76065
|
+
ref(u = 0.5, v = 0.5, offset2) {
|
|
76066
|
+
return Product.ref(this.skin, {
|
|
76067
|
+
side: this.side,
|
|
76068
|
+
u,
|
|
76069
|
+
v,
|
|
76070
|
+
...offset2 != null ? { offset: offset2 } : {}
|
|
76071
|
+
});
|
|
76072
|
+
}
|
|
76073
|
+
/** Create a side/u/v query on this skin side. */
|
|
76074
|
+
uv(u = 0.5, v = 0.5, offset2) {
|
|
76075
|
+
return {
|
|
76076
|
+
side: this.side,
|
|
76077
|
+
u,
|
|
76078
|
+
v,
|
|
76079
|
+
...offset2 != null ? { offset: offset2 } : {}
|
|
76080
|
+
};
|
|
76081
|
+
}
|
|
76082
|
+
/**
|
|
76083
|
+
* Start a conformal ribbon on this skin side.
|
|
76084
|
+
*
|
|
76085
|
+
* Path points use side-local `u`/`v` coordinates; this builder supplies the side.
|
|
76086
|
+
* The returned ProductRibbonBuilder is already bound to the source skin and can be further
|
|
76087
|
+
* configured before build(). Use `widthSamples` >= 3 when the ribbon must visibly wrap over
|
|
76088
|
+
* curved product sections instead of behaving like a flat strip.
|
|
76089
|
+
*/
|
|
76090
|
+
ribbon(name, points, options = {}) {
|
|
76091
|
+
if (points.length < 2) throw new Error("Product.surface(...).ribbon(name, points) requires at least two path points");
|
|
76092
|
+
const path2 = points.map((point2) => ({
|
|
76093
|
+
side: this.side,
|
|
76094
|
+
u: point2.u ?? 0.5,
|
|
76095
|
+
v: point2.v ?? 0.5,
|
|
76096
|
+
...point2.offset != null ? { offset: point2.offset } : {}
|
|
76097
|
+
}));
|
|
76098
|
+
return new ProductRibbonBuilder(name).on(this.skin, path2, options);
|
|
76099
|
+
}
|
|
76100
|
+
}
|
|
76101
|
+
class ProductRibbonBuilder {
|
|
76102
|
+
constructor(name) {
|
|
76103
|
+
__publicField(this, "skinValue");
|
|
76104
|
+
__publicField(this, "queryPath", []);
|
|
76105
|
+
__publicField(this, "refPath", []);
|
|
76106
|
+
__publicField(this, "widthValue", 6);
|
|
76107
|
+
__publicField(this, "thicknessValue", 0.8);
|
|
76108
|
+
__publicField(this, "offsetValue", 0.25);
|
|
76109
|
+
__publicField(this, "samplesValue", 24);
|
|
76110
|
+
__publicField(this, "widthSamplesValue", 5);
|
|
76111
|
+
__publicField(this, "resolutionValue");
|
|
76112
|
+
__publicField(this, "materialValue");
|
|
76113
|
+
__publicField(this, "colorValue");
|
|
76114
|
+
__publicField(this, "lastDiagnosticsValue");
|
|
76115
|
+
this.name = name;
|
|
76116
|
+
if (!name || !name.trim()) throw new Error("Product.ribbon(name) requires a non-empty name");
|
|
76117
|
+
}
|
|
76118
|
+
/**
|
|
76119
|
+
* Follow a ProductSkin with side/u/v path queries or refs.
|
|
76120
|
+
*
|
|
76121
|
+
* This is the highest-fidelity mode because every interpolated sample is resolved through
|
|
76122
|
+
* ProductSkin.frame(), so the ribbon bends along the selected side as station width/depth changes.
|
|
76123
|
+
* All query path points must stay on one side; split side transitions into separate ribbons.
|
|
76124
|
+
*/
|
|
76125
|
+
on(skin, points, options = {}) {
|
|
76126
|
+
if (points.length < 2) throw new Error("Product.ribbon().on(skin, points) requires at least two path points");
|
|
76127
|
+
this.skinValue = skin;
|
|
76128
|
+
this.queryPath = resolvePathQueries(points);
|
|
76129
|
+
this.refPath = [];
|
|
76130
|
+
return this.applyOptions(options);
|
|
76131
|
+
}
|
|
76132
|
+
/**
|
|
76133
|
+
* Follow explicit surface refs.
|
|
76134
|
+
*
|
|
76135
|
+
* Useful for named refs or paths assembled elsewhere. The builder resolves each ref frame and
|
|
76136
|
+
* interpolates between those frames; use on(skin, points) when you need full skin-side sampling
|
|
76137
|
+
* between sparse control points.
|
|
76138
|
+
*/
|
|
76139
|
+
fromRefs(points, options = {}) {
|
|
76140
|
+
if (points.length < 2) throw new Error("Product.ribbon().fromRefs(points) requires at least two refs");
|
|
76141
|
+
this.skinValue = void 0;
|
|
76142
|
+
this.queryPath = [];
|
|
76143
|
+
this.refPath = [...points];
|
|
76144
|
+
return this.applyOptions(options);
|
|
76145
|
+
}
|
|
76146
|
+
/** Set ribbon width in millimeters. */
|
|
76147
|
+
width(width) {
|
|
76148
|
+
if (!Number.isFinite(width) || width <= 0) throw new Error("Product.ribbon().width(width) requires a positive finite number");
|
|
76149
|
+
this.widthValue = width;
|
|
76150
|
+
return this;
|
|
76151
|
+
}
|
|
76152
|
+
/** Set solid thickness outward from the source surface in millimeters. */
|
|
76153
|
+
thickness(thickness) {
|
|
76154
|
+
if (!Number.isFinite(thickness) || thickness <= 0)
|
|
76155
|
+
throw new Error("Product.ribbon().thickness(thickness) requires a positive finite number");
|
|
76156
|
+
this.thicknessValue = thickness;
|
|
76157
|
+
return this;
|
|
76158
|
+
}
|
|
76159
|
+
/** Set positive clearance between the source surface and the ribbon's inner face. */
|
|
76160
|
+
offset(offset2) {
|
|
76161
|
+
if (!Number.isFinite(offset2)) throw new Error("Product.ribbon().offset(offset) requires a finite number");
|
|
76162
|
+
this.offsetValue = offset2;
|
|
76163
|
+
return this;
|
|
76164
|
+
}
|
|
76165
|
+
/** Set samples along the path. */
|
|
76166
|
+
samples(samples) {
|
|
76167
|
+
if (!Number.isFinite(samples) || samples < 2) throw new Error("Product.ribbon().samples(samples) requires a value >= 2");
|
|
76168
|
+
this.samplesValue = Math.round(samples);
|
|
76169
|
+
return this;
|
|
76170
|
+
}
|
|
76171
|
+
/** Set samples across the width. Use 3+ to bend over curved cross-sections. */
|
|
76172
|
+
widthSamples(samples) {
|
|
76173
|
+
if (!Number.isFinite(samples) || samples < 2) throw new Error("Product.ribbon().widthSamples(samples) requires a value >= 2");
|
|
76174
|
+
this.widthSamplesValue = Math.round(samples);
|
|
76175
|
+
return this;
|
|
76176
|
+
}
|
|
76177
|
+
/** Set NURBS tessellation resolution. */
|
|
76178
|
+
resolution(resolution) {
|
|
76179
|
+
if (!Number.isFinite(resolution) || resolution < 2) throw new Error("Product.ribbon().resolution(resolution) requires a value >= 2");
|
|
76180
|
+
this.resolutionValue = Math.round(resolution);
|
|
76181
|
+
return this;
|
|
76182
|
+
}
|
|
76183
|
+
/** Apply a product material preset. */
|
|
76184
|
+
material(material) {
|
|
76185
|
+
this.materialValue = material;
|
|
76186
|
+
return this;
|
|
76187
|
+
}
|
|
76188
|
+
/** Apply a simple color override. */
|
|
76189
|
+
color(color) {
|
|
76190
|
+
this.colorValue = color;
|
|
76191
|
+
return this;
|
|
76192
|
+
}
|
|
76193
|
+
/** Build a conformal ribbon as a thin NURBS surface solid. */
|
|
76194
|
+
build(options = {}) {
|
|
76195
|
+
return this.buildWithDiagnostics(options).shape;
|
|
76196
|
+
}
|
|
76197
|
+
/**
|
|
76198
|
+
* Build a conformal ribbon and return surface-feature diagnostics.
|
|
76199
|
+
*
|
|
76200
|
+
* Use this while validating API usage or model fidelity; diagnostics report sampling counts,
|
|
76201
|
+
* side-span clamping, lowering mode, and warnings that should be visible in reviews.
|
|
76202
|
+
*/
|
|
76203
|
+
buildWithDiagnostics(options = {}) {
|
|
76204
|
+
this.applyOptions(options);
|
|
76205
|
+
const gridResult = this.skinValue ? this.buildSkinGrid(this.skinValue, this.queryPath) : this.buildRefGrid(this.refPath);
|
|
76206
|
+
const desiredNormal = this.centerDesiredNormal();
|
|
76207
|
+
let ribbon = nurbsSurface(orientGridToNormal(gridResult.grid, desiredNormal), {
|
|
76208
|
+
degreeU: Math.min(3, this.widthSamplesValue - 1),
|
|
76209
|
+
degreeV: Math.min(3, this.samplesValue - 1),
|
|
76210
|
+
thickness: this.thicknessValue,
|
|
76211
|
+
resolution: this.resolutionValue ?? Math.max(this.samplesValue, this.widthSamplesValue, 12),
|
|
76212
|
+
approximate: true
|
|
76213
|
+
}).as(this.name);
|
|
76214
|
+
if (this.colorValue) ribbon = ribbon.color(this.colorValue);
|
|
76215
|
+
const shape = applyMaterial(ribbon, this.materialValue);
|
|
76216
|
+
this.lastDiagnosticsValue = gridResult.diagnostics;
|
|
76217
|
+
return { shape, diagnostics: this.cloneDiagnostics(gridResult.diagnostics) };
|
|
76218
|
+
}
|
|
76219
|
+
/** Return diagnostics from the most recent build, if this builder has been built. */
|
|
76220
|
+
diagnostics() {
|
|
76221
|
+
return this.lastDiagnosticsValue ? this.cloneDiagnostics(this.lastDiagnosticsValue) : void 0;
|
|
76222
|
+
}
|
|
76223
|
+
applyOptions(options) {
|
|
76224
|
+
if (options.width != null) this.width(options.width);
|
|
76225
|
+
if (options.thickness != null) this.thickness(options.thickness);
|
|
76226
|
+
if (options.offset != null) this.offset(options.offset);
|
|
76227
|
+
if (options.samples != null) this.samples(options.samples);
|
|
76228
|
+
if (options.widthSamples != null) this.widthSamples(options.widthSamples);
|
|
76229
|
+
if (options.resolution != null) this.resolution(options.resolution);
|
|
76230
|
+
if (options.material) this.material(options.material);
|
|
76231
|
+
if (options.color) this.color(options.color);
|
|
76232
|
+
return this;
|
|
76233
|
+
}
|
|
76234
|
+
centerDesiredNormal() {
|
|
76235
|
+
if (this.skinValue && this.queryPath.length > 0) {
|
|
76236
|
+
const mid = this.samplePathQuery(0.5);
|
|
76237
|
+
return this.skinValue.frame({ ...mid, offset: (mid.offset ?? 0) + this.offsetValue }).normal;
|
|
76238
|
+
}
|
|
76239
|
+
if (this.refPath.length > 0) return this.refPath[Math.floor(this.refPath.length / 2)].frame({ offset: this.offsetValue }).normal;
|
|
76240
|
+
return [0, 0, 1];
|
|
76241
|
+
}
|
|
76242
|
+
samplePathQuery(t) {
|
|
76243
|
+
if (this.queryPath.length < 2) throw new Error("Product.ribbon().on(...) must be called before .build()");
|
|
76244
|
+
const segmentCount = this.queryPath.length - 1;
|
|
76245
|
+
const scaled = clamp$4(t, 0, 1) * segmentCount;
|
|
76246
|
+
const segment = Math.min(segmentCount - 1, Math.floor(scaled));
|
|
76247
|
+
const localT = scaled - segment;
|
|
76248
|
+
return interpolateQuery(this.queryPath[segment], this.queryPath[segment + 1], localT);
|
|
76249
|
+
}
|
|
76250
|
+
buildSkinGrid(skin, path2) {
|
|
76251
|
+
if (path2.length < 2) throw new Error("Product.ribbon().on(skin, points) must be called before .build()");
|
|
76252
|
+
const side = normalizedSide(path2[0].side);
|
|
76253
|
+
if (side === "front" || side === "rear") {
|
|
76254
|
+
throw new Error(
|
|
76255
|
+
"Product.ribbon().on(...) supports side ribbons on left/right/top/bottom surfaces. Use Product.panel() for front/rear caps."
|
|
76256
|
+
);
|
|
76257
|
+
}
|
|
76258
|
+
for (const point2 of path2) {
|
|
76259
|
+
if (normalizedSide(point2.side) !== side) {
|
|
76260
|
+
throw new Error("Product.ribbon().on(...) currently supports one side per ribbon. Split ribbons at side transitions.");
|
|
76261
|
+
}
|
|
76262
|
+
}
|
|
76263
|
+
const rows = Array.from({ length: this.widthSamplesValue }, () => []);
|
|
76264
|
+
let clampedUCount = 0;
|
|
76265
|
+
let maxUClampDistance = 0;
|
|
76266
|
+
for (let i = 0; i < this.samplesValue; i += 1) {
|
|
76267
|
+
const along = this.samplesValue === 1 ? 0 : i / (this.samplesValue - 1);
|
|
76268
|
+
const center = this.samplePathQuery(along);
|
|
76269
|
+
const station = skin.stationAt(center.v ?? 0.5);
|
|
76270
|
+
const span = sideSpan(side, station.width, station.depth);
|
|
76271
|
+
for (let j = 0; j < this.widthSamplesValue; j += 1) {
|
|
76272
|
+
const across = this.widthSamplesValue === 1 ? 0 : j / (this.widthSamplesValue - 1) - 0.5;
|
|
76273
|
+
const rawU = (center.u ?? 0.5) + across * this.widthValue / span;
|
|
76274
|
+
const u = clamp$4(rawU, 0, 1);
|
|
76275
|
+
const clampDistance = Math.abs(rawU - u) * span;
|
|
76276
|
+
if (clampDistance > EPS$4) {
|
|
76277
|
+
clampedUCount += 1;
|
|
76278
|
+
maxUClampDistance = Math.max(maxUClampDistance, clampDistance);
|
|
76279
|
+
}
|
|
76280
|
+
const query = {
|
|
76281
|
+
...center,
|
|
76282
|
+
side,
|
|
76283
|
+
u,
|
|
76284
|
+
offset: (center.offset ?? 0) + this.offsetValue + this.thicknessValue
|
|
76285
|
+
};
|
|
76286
|
+
rows[j].push(skin.frame(query).point);
|
|
76287
|
+
}
|
|
76288
|
+
}
|
|
76289
|
+
return {
|
|
76290
|
+
grid: rows,
|
|
76291
|
+
diagnostics: this.makeDiagnostics({
|
|
76292
|
+
skin: skin.name,
|
|
76293
|
+
side,
|
|
76294
|
+
pathPointCount: path2.length,
|
|
76295
|
+
clampedUCount,
|
|
76296
|
+
maxUClampDistance
|
|
76297
|
+
})
|
|
76298
|
+
};
|
|
76299
|
+
}
|
|
76300
|
+
buildRefGrid(refs) {
|
|
76301
|
+
if (refs.length < 2) throw new Error("Product.ribbon().fromRefs(points) must be called before .build()");
|
|
76302
|
+
const frames = refs.map((ref) => ref.frame({ offset: this.offsetValue + this.thicknessValue }));
|
|
76303
|
+
const rows = Array.from({ length: this.widthSamplesValue }, () => []);
|
|
76304
|
+
for (const frame of frames) {
|
|
76305
|
+
for (let j = 0; j < this.widthSamplesValue; j += 1) {
|
|
76306
|
+
const across = this.widthSamplesValue === 1 ? 0 : j / (this.widthSamplesValue - 1) - 0.5;
|
|
76307
|
+
rows[j].push(add(frame.point, scale(frame.tangentU, across * this.widthValue)));
|
|
76308
|
+
}
|
|
76309
|
+
}
|
|
76310
|
+
this.samplesValue = refs.length;
|
|
76311
|
+
return {
|
|
76312
|
+
grid: rows,
|
|
76313
|
+
diagnostics: this.makeDiagnostics({
|
|
76314
|
+
pathPointCount: refs.length,
|
|
76315
|
+
clampedUCount: 0,
|
|
76316
|
+
maxUClampDistance: 0
|
|
76317
|
+
})
|
|
76318
|
+
};
|
|
76319
|
+
}
|
|
76320
|
+
makeDiagnostics(input) {
|
|
76321
|
+
const resolution = this.resolutionValue ?? Math.max(this.samplesValue, this.widthSamplesValue, 12);
|
|
76322
|
+
const warnings = [];
|
|
76323
|
+
if (input.clampedUCount > 0) {
|
|
76324
|
+
warnings.push(
|
|
76325
|
+
`Ribbon '${this.name}' was clipped to the ${input.side ?? "surface"} UV bounds at ${input.clampedUCount} sampled point(s).`
|
|
76326
|
+
);
|
|
76327
|
+
}
|
|
76328
|
+
if (this.samplesValue < 8) warnings.push(`Ribbon '${this.name}' uses low along-path sampling; increase samples() for smoother bends.`);
|
|
76329
|
+
if (this.widthSamplesValue < 3)
|
|
76330
|
+
warnings.push(`Ribbon '${this.name}' uses low width sampling; use widthSamples(3+) to show cross-surface curvature.`);
|
|
76331
|
+
return {
|
|
76332
|
+
name: this.name,
|
|
76333
|
+
...input.skin ? { skin: input.skin } : {},
|
|
76334
|
+
...input.side ? { side: input.side } : {},
|
|
76335
|
+
pathPointCount: input.pathPointCount,
|
|
76336
|
+
width: this.widthValue,
|
|
76337
|
+
thickness: this.thicknessValue,
|
|
76338
|
+
offset: this.offsetValue,
|
|
76339
|
+
samples: this.samplesValue,
|
|
76340
|
+
widthSamples: this.widthSamplesValue,
|
|
76341
|
+
resolution,
|
|
76342
|
+
lowering: "nurbsSurface",
|
|
76343
|
+
expectedFidelity: "mixed",
|
|
76344
|
+
clampedUCount: input.clampedUCount,
|
|
76345
|
+
maxUClampDistance: input.maxUClampDistance,
|
|
76346
|
+
warnings
|
|
76347
|
+
};
|
|
76348
|
+
}
|
|
76349
|
+
cloneDiagnostics(diagnostics) {
|
|
76350
|
+
return {
|
|
76351
|
+
...diagnostics,
|
|
76352
|
+
warnings: [...diagnostics.warnings]
|
|
76353
|
+
};
|
|
76354
|
+
}
|
|
76355
|
+
}
|
|
75595
76356
|
const Product = {
|
|
75596
76357
|
/** Start a named product skin builder. */
|
|
75597
76358
|
skin(name) {
|
|
@@ -75664,10 +76425,27 @@ const Product = {
|
|
|
75664
76425
|
ref(skin, query) {
|
|
75665
76426
|
return new ProductSurfaceRef(skin, query);
|
|
75666
76427
|
},
|
|
76428
|
+
/**
|
|
76429
|
+
* Create a fluent surface helper for refs and conformal features on one side of a skin.
|
|
76430
|
+
*
|
|
76431
|
+
* Equivalent to skin.surface(side), useful when writing in Product.* namespace style.
|
|
76432
|
+
*/
|
|
76433
|
+
surface(skin, side) {
|
|
76434
|
+
return skin.surface(side);
|
|
76435
|
+
},
|
|
75667
76436
|
/** Start a panel feature builder. */
|
|
75668
76437
|
panel(name) {
|
|
75669
76438
|
return new ProductPanelBuilder(name);
|
|
75670
76439
|
},
|
|
76440
|
+
/**
|
|
76441
|
+
* Start a conformal ribbon/trim builder for details that should bend with a ProductSkin.
|
|
76442
|
+
*
|
|
76443
|
+
* Call .on(skin, points) for side/u/v sampling or .fromRefs(points) for explicit surface refs,
|
|
76444
|
+
* then configure width, thickness, offset, sampling, material, and color before build().
|
|
76445
|
+
*/
|
|
76446
|
+
ribbon(name) {
|
|
76447
|
+
return new ProductRibbonBuilder(name);
|
|
76448
|
+
},
|
|
75671
76449
|
/** Start a spout/nozzle feature builder. */
|
|
75672
76450
|
spout(name) {
|
|
75673
76451
|
return new ProductSpoutBuilder(name);
|
|
@@ -76810,6 +77588,7 @@ function cameraTrajectory(defOrFn, options) {
|
|
|
76810
77588
|
throw new Error('cameraTrajectory(): each keyframe must have either an "orbit" or "position" property');
|
|
76811
77589
|
}
|
|
76812
77590
|
}
|
|
77591
|
+
var define_process_env_default = {};
|
|
76813
77592
|
function resolveEdges(shape, edges) {
|
|
76814
77593
|
if (!edges) {
|
|
76815
77594
|
return selectEdges(shape);
|
|
@@ -76831,6 +77610,76 @@ function isEdgeSegment(value) {
|
|
|
76831
77610
|
function isEdgeReferenceLike(value) {
|
|
76832
77611
|
return typeof value === "object" && value !== null && "edges" in value && typeof value.edges === "function";
|
|
76833
77612
|
}
|
|
77613
|
+
const BROAD_EDGE_FEATURE_DEFAULT_BUDGET = {
|
|
77614
|
+
live: 0,
|
|
77615
|
+
default: 12,
|
|
77616
|
+
high: Number.POSITIVE_INFINITY
|
|
77617
|
+
};
|
|
77618
|
+
let broadEdgeFeatureBudget = null;
|
|
77619
|
+
function readBroadEdgeFeatureEnv(name) {
|
|
77620
|
+
return typeof process !== "undefined" ? define_process_env_default == null ? void 0 : define_process_env_default[name] : void 0;
|
|
77621
|
+
}
|
|
77622
|
+
function resolveBroadEdgeFeatureBudget() {
|
|
77623
|
+
if (readBroadEdgeFeatureEnv("FORGECAD_ALLOW_BROAD_EDGE_FEATURES") === "1") return Number.POSITIVE_INFINITY;
|
|
77624
|
+
const override = readBroadEdgeFeatureEnv("FORGECAD_BROAD_EDGE_FEATURE_BUDGET");
|
|
77625
|
+
if (override != null && override.trim() !== "") {
|
|
77626
|
+
const parsed = Number(override);
|
|
77627
|
+
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
|
|
77628
|
+
}
|
|
77629
|
+
return BROAD_EDGE_FEATURE_DEFAULT_BUDGET[getForgeQualityPreset()];
|
|
77630
|
+
}
|
|
77631
|
+
function resetBroadEdgeFeatureBudget() {
|
|
77632
|
+
broadEdgeFeatureBudget = null;
|
|
77633
|
+
}
|
|
77634
|
+
function remainingBroadEdgeFeatureBudget() {
|
|
77635
|
+
if (broadEdgeFeatureBudget === null) broadEdgeFeatureBudget = resolveBroadEdgeFeatureBudget();
|
|
77636
|
+
return broadEdgeFeatureBudget;
|
|
77637
|
+
}
|
|
77638
|
+
function consumeBroadEdgeFeatureBudget(edgeCount) {
|
|
77639
|
+
const remaining = remainingBroadEdgeFeatureBudget();
|
|
77640
|
+
if (!Number.isFinite(remaining)) return true;
|
|
77641
|
+
if (edgeCount > remaining) return false;
|
|
77642
|
+
broadEdgeFeatureBudget = Math.max(0, remaining - edgeCount);
|
|
77643
|
+
return true;
|
|
77644
|
+
}
|
|
77645
|
+
function shouldSkipBroadEdgeFeature(operation, edgeCount) {
|
|
77646
|
+
if (consumeBroadEdgeFeatureBudget(edgeCount)) return false;
|
|
77647
|
+
const remaining = Math.max(0, remainingBroadEdgeFeatureBudget());
|
|
77648
|
+
emitRuntimeWarning(
|
|
77649
|
+
`${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.`
|
|
77650
|
+
);
|
|
77651
|
+
return true;
|
|
77652
|
+
}
|
|
77653
|
+
function shouldSkipExhaustedBroadEdgeFeature(operation) {
|
|
77654
|
+
const remaining = remainingBroadEdgeFeatureBudget();
|
|
77655
|
+
if (!Number.isFinite(remaining) || remaining > 0) return false;
|
|
77656
|
+
emitRuntimeWarning(
|
|
77657
|
+
`${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.`
|
|
77658
|
+
);
|
|
77659
|
+
return true;
|
|
77660
|
+
}
|
|
77661
|
+
function estimateSelectorlessEdgeCount(plan) {
|
|
77662
|
+
if (!plan) return null;
|
|
77663
|
+
switch (plan.kind) {
|
|
77664
|
+
case "box":
|
|
77665
|
+
return 12;
|
|
77666
|
+
case "queryOwner":
|
|
77667
|
+
case "transform":
|
|
77668
|
+
return estimateSelectorlessEdgeCount(plan.base);
|
|
77669
|
+
default:
|
|
77670
|
+
return null;
|
|
77671
|
+
}
|
|
77672
|
+
}
|
|
77673
|
+
function shouldSkipUnestimatedBroadEdgeFeature(operation, target) {
|
|
77674
|
+
const remaining = remainingBroadEdgeFeatureBudget();
|
|
77675
|
+
if (!Number.isFinite(remaining)) return false;
|
|
77676
|
+
const estimatedEdges = estimateSelectorlessEdgeCount(getShapeCompilePlan(target));
|
|
77677
|
+
if (estimatedEdges !== null && estimatedEdges <= remaining) return false;
|
|
77678
|
+
emitRuntimeWarning(
|
|
77679
|
+
`${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.`
|
|
77680
|
+
);
|
|
77681
|
+
return true;
|
|
77682
|
+
}
|
|
76834
77683
|
function edgesToTargets(edges) {
|
|
76835
77684
|
return edges.map((e) => ({
|
|
76836
77685
|
midpoint: [e.midpoint[0], e.midpoint[1], e.midpoint[2]],
|
|
@@ -76844,10 +77693,13 @@ function fillet(shape, radius, edges, segments = 16) {
|
|
|
76844
77693
|
throw new Error("fillet() requires a positive finite radius.");
|
|
76845
77694
|
}
|
|
76846
77695
|
const target = shape;
|
|
77696
|
+
if (edges === void 0 && shouldSkipExhaustedBroadEdgeFeature("fillet")) return target;
|
|
77697
|
+
if (edges === void 0 && shouldSkipUnestimatedBroadEdgeFeature("fillet", target)) return target;
|
|
76847
77698
|
const resolvedEdges = resolveEdges(target, edges);
|
|
76848
77699
|
if (resolvedEdges.length === 0) {
|
|
76849
77700
|
throw new Error("fillet(): no edges match the given selection.");
|
|
76850
77701
|
}
|
|
77702
|
+
if (edges === void 0 && shouldSkipBroadEdgeFeature("fillet", resolvedEdges.length)) return target;
|
|
76851
77703
|
const basePlan = getShapeCompilePlan(target);
|
|
76852
77704
|
const plan = {
|
|
76853
77705
|
kind: "filletEdges",
|
|
@@ -76867,10 +77719,13 @@ function chamfer(shape, size, edges) {
|
|
|
76867
77719
|
throw new Error("chamfer() requires a positive finite size.");
|
|
76868
77720
|
}
|
|
76869
77721
|
const target = shape;
|
|
77722
|
+
if (edges === void 0 && shouldSkipExhaustedBroadEdgeFeature("chamfer")) return target;
|
|
77723
|
+
if (edges === void 0 && shouldSkipUnestimatedBroadEdgeFeature("chamfer", target)) return target;
|
|
76870
77724
|
const resolvedEdges = resolveEdges(target, edges);
|
|
76871
77725
|
if (resolvedEdges.length === 0) {
|
|
76872
77726
|
throw new Error("chamfer(): no edges match the given selection.");
|
|
76873
77727
|
}
|
|
77728
|
+
if (edges === void 0 && shouldSkipBroadEdgeFeature("chamfer", resolvedEdges.length)) return target;
|
|
76874
77729
|
const basePlan = getShapeCompilePlan(target);
|
|
76875
77730
|
const plan = {
|
|
76876
77731
|
kind: "chamferEdges",
|
|
@@ -100016,6 +100871,7 @@ const verify = {
|
|
|
100016
100871
|
};
|
|
100017
100872
|
function resetExecutionSession(logs) {
|
|
100018
100873
|
resetCollectedAssemblies();
|
|
100874
|
+
resetBroadEdgeFeatureBudget();
|
|
100019
100875
|
resetParams();
|
|
100020
100876
|
resetShapeQueryOwnerIds();
|
|
100021
100877
|
resetDimensions();
|
|
@@ -100024,6 +100880,7 @@ function resetExecutionSession(logs) {
|
|
|
100024
100880
|
resetSheetStock();
|
|
100025
100881
|
resetRobotExport();
|
|
100026
100882
|
resetCutPlanes();
|
|
100883
|
+
resetRenderLabels();
|
|
100027
100884
|
resetCameraTrajectory();
|
|
100028
100885
|
resetExplodeView();
|
|
100029
100886
|
resetJointsView();
|
|
@@ -100087,6 +100944,7 @@ function collectSuccessfulExecutionSnapshot(args) {
|
|
|
100087
100944
|
bom: getCollectedBom(),
|
|
100088
100945
|
sheetStock: getCollectedSheetStock(),
|
|
100089
100946
|
cutPlanes: getCollectedCutPlanes(),
|
|
100947
|
+
renderLabels: getCollectedRenderLabels(),
|
|
100090
100948
|
cameraTrajectory: getCollectedCameraTrajectory(),
|
|
100091
100949
|
explodeView: getCollectedExplodeView(),
|
|
100092
100950
|
jointsView: getCollectedJointsView(),
|
|
@@ -100110,6 +100968,7 @@ function collectFailedExecutionSnapshot(args) {
|
|
|
100110
100968
|
bom: getCollectedBom(),
|
|
100111
100969
|
sheetStock: getCollectedSheetStock(),
|
|
100112
100970
|
cutPlanes: getCollectedCutPlanes(),
|
|
100971
|
+
renderLabels: getCollectedRenderLabels(),
|
|
100113
100972
|
cameraTrajectory: getCollectedCameraTrajectory(),
|
|
100114
100973
|
explodeView: getCollectedExplodeView(),
|
|
100115
100974
|
jointsView: getCollectedJointsView(),
|
|
@@ -100255,13 +101114,15 @@ function formatLogArg(value) {
|
|
|
100255
101114
|
return `[Log serialization failed: ${formatLogError(error)}]`;
|
|
100256
101115
|
}
|
|
100257
101116
|
}
|
|
100258
|
-
function makeSandboxConsole(collectedLogs) {
|
|
101117
|
+
function makeSandboxConsole(collectedLogs, mirror) {
|
|
100259
101118
|
const capture = (level) => (...args) => {
|
|
101119
|
+
const formattedArgs = args.map(formatLogArg);
|
|
100260
101120
|
collectedLogs.push({
|
|
100261
101121
|
level,
|
|
100262
|
-
args:
|
|
101122
|
+
args: formattedArgs,
|
|
100263
101123
|
timestamp: Date.now()
|
|
100264
101124
|
});
|
|
101125
|
+
mirror == null ? void 0 : mirror(level, formattedArgs);
|
|
100265
101126
|
};
|
|
100266
101127
|
return { log: capture("log"), warn: capture("warn"), error: capture("error"), info: capture("info") };
|
|
100267
101128
|
}
|
|
@@ -100423,6 +101284,18 @@ function buildFileIndex(allFiles) {
|
|
|
100423
101284
|
}
|
|
100424
101285
|
return fileIndex;
|
|
100425
101286
|
}
|
|
101287
|
+
function hasPathExtension(path2) {
|
|
101288
|
+
const fileName = path2.split("/").pop() ?? path2;
|
|
101289
|
+
return /\.[^/.]+$/.test(fileName);
|
|
101290
|
+
}
|
|
101291
|
+
function explicitExtensionHint(requestedName, resolvedPath, fileIndex) {
|
|
101292
|
+
if (!resolvedPath || hasPathExtension(resolvedPath)) return "";
|
|
101293
|
+
const requestedBase = requestedName.trim();
|
|
101294
|
+
const suggestions = [".forge.js", ".js"].map((ext) => ({ ext, resolved: `${resolvedPath}${ext}` })).filter(({ resolved }) => fileIndex.has(resolved)).map(({ ext }) => `"${requestedBase}${ext}"`);
|
|
101295
|
+
if (suggestions.length === 0) return "";
|
|
101296
|
+
const joined = suggestions.length === 1 ? suggestions[0] : suggestions.join(" or ");
|
|
101297
|
+
return ` Did you mean ${joined}? ForgeCAD requires explicit file extensions in project imports.`;
|
|
101298
|
+
}
|
|
100426
101299
|
function resolveImportSource(fromFile, requestedName, allFiles, options) {
|
|
100427
101300
|
if (typeof requestedName !== "string" || requestedName.trim().length === 0) {
|
|
100428
101301
|
throw new Error("Import path must be a non-empty string");
|
|
@@ -100431,7 +101304,8 @@ function resolveImportSource(fromFile, requestedName, allFiles, options) {
|
|
|
100431
101304
|
const lookupKey = options.fileIndex.get(resolvedPath);
|
|
100432
101305
|
if (!lookupKey) {
|
|
100433
101306
|
const suffix = resolvedPath && resolvedPath !== requestedName ? ` (resolved to "${resolvedPath}" from "${fromFile}")` : ` (from "${fromFile}")`;
|
|
100434
|
-
|
|
101307
|
+
const hint = explicitExtensionHint(requestedName, resolvedPath, options.fileIndex);
|
|
101308
|
+
throw new Error(`File not found: "${requestedName}"${suffix}.${hint}`);
|
|
100435
101309
|
}
|
|
100436
101310
|
const source = allFiles[lookupKey];
|
|
100437
101311
|
if (typeof source !== "string") {
|
|
@@ -154712,7 +155586,7 @@ ${lanes.join("\n")}
|
|
|
154712
155586
|
}
|
|
154713
155587
|
const normalizedSourcePaths = getPathsRelativeToRootDirs(sourceDirectory, rootDirs, getCanonicalFileName);
|
|
154714
155588
|
const relativePaths = flatMap(normalizedSourcePaths, (sourcePath) => {
|
|
154715
|
-
return map(normalizedTargetPaths, (
|
|
155589
|
+
return map(normalizedTargetPaths, (targetPath2) => ensurePathIsNonModuleName(getRelativePathFromDirectory(sourcePath, targetPath2, getCanonicalFileName)));
|
|
154716
155590
|
});
|
|
154717
155591
|
const shortest = min2(relativePaths, compareNumberOfDirectorySeparators);
|
|
154718
155592
|
if (!shortest) {
|
|
@@ -311282,6 +312156,36 @@ function extractUnusedTopLevelVarNames(code) {
|
|
|
311282
312156
|
}
|
|
311283
312157
|
return declaredNames.filter((n) => topLevelNames.has(n) && (!usedByOthers.has(n) || explicitImplicitResultNames.has(n)));
|
|
311284
312158
|
}
|
|
312159
|
+
function collectBindingNameLocations(node, sourceFile, names) {
|
|
312160
|
+
if (typescriptExports.isIdentifier(node)) {
|
|
312161
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
312162
|
+
names.push({ name: node.text, line: line2 + 1, column: character + 1 });
|
|
312163
|
+
return;
|
|
312164
|
+
}
|
|
312165
|
+
for (const element of node.elements) {
|
|
312166
|
+
if (typescriptExports.isBindingElement(element)) {
|
|
312167
|
+
collectBindingNameLocations(element.name, sourceFile, names);
|
|
312168
|
+
}
|
|
312169
|
+
}
|
|
312170
|
+
}
|
|
312171
|
+
function findTopLevelRuntimeGlobalCollision(code, runtimeGlobalNames) {
|
|
312172
|
+
const runtimeGlobals = new Set(runtimeGlobalNames);
|
|
312173
|
+
const sourceFile = typescriptExports.createSourceFile("__runtime-globals.js", code, typescriptExports.ScriptTarget.ES2020, false, typescriptExports.ScriptKind.JS);
|
|
312174
|
+
const declarations = [];
|
|
312175
|
+
for (const statement of sourceFile.statements) {
|
|
312176
|
+
if (typescriptExports.isVariableStatement(statement)) {
|
|
312177
|
+
const isLexical = (statement.declarationList.flags & (typescriptExports.NodeFlags.Let | typescriptExports.NodeFlags.Const)) !== 0;
|
|
312178
|
+
if (!isLexical) continue;
|
|
312179
|
+
for (const decl of statement.declarationList.declarations) {
|
|
312180
|
+
collectBindingNameLocations(decl.name, sourceFile, declarations);
|
|
312181
|
+
}
|
|
312182
|
+
} else if (typescriptExports.isClassDeclaration(statement) && statement.name) {
|
|
312183
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(statement.name.getStart(sourceFile));
|
|
312184
|
+
declarations.push({ name: statement.name.text, line: line2 + 1, column: character + 1 });
|
|
312185
|
+
}
|
|
312186
|
+
}
|
|
312187
|
+
return declarations.find((declaration) => runtimeGlobals.has(declaration.name)) ?? null;
|
|
312188
|
+
}
|
|
311285
312189
|
function createForgeRuntimeModule(bindings) {
|
|
311286
312190
|
const runtime = { ...bindings };
|
|
311287
312191
|
Object.defineProperty(runtime, "__esModule", { value: true });
|
|
@@ -312437,6 +313341,12 @@ function withConstructorChainLockdown(fn) {
|
|
|
312437
313341
|
}
|
|
312438
313342
|
}
|
|
312439
313343
|
function executeFile(code, fileName, allFiles, visited, scope = {}, options, executionMode = "script", moduleCacheEntry) {
|
|
313344
|
+
var _a3, _b3, _c2, _d2, _e2, _f;
|
|
313345
|
+
(_a3 = options.debug) == null ? void 0 : _a3.call(options, "executeFile:start", {
|
|
313346
|
+
fileName,
|
|
313347
|
+
executionMode,
|
|
313348
|
+
scope: scope.namePrefix ?? fileName
|
|
313349
|
+
});
|
|
312440
313350
|
const trackCircularImports = executionMode === "script";
|
|
312441
313351
|
if (trackCircularImports) {
|
|
312442
313352
|
if (visited.has(fileName)) {
|
|
@@ -312514,13 +313424,13 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
312514
313424
|
});
|
|
312515
313425
|
};
|
|
312516
313426
|
const importStep = (name) => {
|
|
312517
|
-
var
|
|
313427
|
+
var _a4;
|
|
312518
313428
|
if (typeof name !== "string" || name.trim().length === 0) {
|
|
312519
313429
|
throw new Error("importStep() requires a non-empty file path string");
|
|
312520
313430
|
}
|
|
312521
313431
|
const resolvedPath = resolveImportPath(fileName, name.trim());
|
|
312522
313432
|
rejectPathTraversal("importStep", name, resolvedPath);
|
|
312523
|
-
const ext = ((
|
|
313433
|
+
const ext = ((_a4 = resolvedPath.split(".").pop()) == null ? void 0 : _a4.toLowerCase()) ?? "";
|
|
312524
313434
|
if (ext !== "step" && ext !== "stp") {
|
|
312525
313435
|
throw new Error(`importStep("${name}"): unsupported extension ".${ext}". Expected .step or .stp`);
|
|
312526
313436
|
}
|
|
@@ -312549,7 +313459,39 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
312549
313459
|
setShapeTopology(shape, topo);
|
|
312550
313460
|
return shape;
|
|
312551
313461
|
};
|
|
312552
|
-
const sandboxConsole = makeSandboxConsole(_collectedLogs)
|
|
313462
|
+
const sandboxConsole = makeSandboxConsole(_collectedLogs, options.debug ? (level, args) => {
|
|
313463
|
+
var _a4;
|
|
313464
|
+
return (_a4 = options.debug) == null ? void 0 : _a4.call(options, "console", { level, args });
|
|
313465
|
+
} : void 0);
|
|
313466
|
+
const runtimeVerify = options.debug ? {
|
|
313467
|
+
...verify,
|
|
313468
|
+
that(label, check2, message) {
|
|
313469
|
+
var _a4, _b4;
|
|
313470
|
+
(_a4 = options.debug) == null ? void 0 : _a4.call(options, "verify:that:start", { label });
|
|
313471
|
+
const verifyStart = performance.now();
|
|
313472
|
+
try {
|
|
313473
|
+
return verify.that(label, check2, message);
|
|
313474
|
+
} finally {
|
|
313475
|
+
(_b4 = options.debug) == null ? void 0 : _b4.call(options, "verify:that:end", {
|
|
313476
|
+
label,
|
|
313477
|
+
ms: Number((performance.now() - verifyStart).toFixed(1))
|
|
313478
|
+
});
|
|
313479
|
+
}
|
|
313480
|
+
},
|
|
313481
|
+
equal(label, actual, expected, tolerance = 0, message) {
|
|
313482
|
+
var _a4, _b4;
|
|
313483
|
+
(_a4 = options.debug) == null ? void 0 : _a4.call(options, "verify:equal:start", { label, actual, expected, tolerance });
|
|
313484
|
+
const verifyStart = performance.now();
|
|
313485
|
+
try {
|
|
313486
|
+
return verify.equal(label, actual, expected, tolerance, message);
|
|
313487
|
+
} finally {
|
|
313488
|
+
(_b4 = options.debug) == null ? void 0 : _b4.call(options, "verify:equal:end", {
|
|
313489
|
+
label,
|
|
313490
|
+
ms: Number((performance.now() - verifyStart).toFixed(1))
|
|
313491
|
+
});
|
|
313492
|
+
}
|
|
313493
|
+
}
|
|
313494
|
+
} : verify;
|
|
312553
313495
|
setShowLabelsHighlight(highlight);
|
|
312554
313496
|
const runtimeBindings = {
|
|
312555
313497
|
box: trackedBox,
|
|
@@ -312690,7 +313632,8 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
312690
313632
|
jointsView,
|
|
312691
313633
|
viewConfig,
|
|
312692
313634
|
scene,
|
|
312693
|
-
|
|
313635
|
+
Viewport,
|
|
313636
|
+
verify: runtimeVerify,
|
|
312694
313637
|
spec,
|
|
312695
313638
|
mock,
|
|
312696
313639
|
gcode,
|
|
@@ -312828,8 +313771,21 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
|
|
|
312828
313771
|
throw error;
|
|
312829
313772
|
}
|
|
312830
313773
|
};
|
|
312831
|
-
const compiled = compileScript(code, fileName, options);
|
|
312832
313774
|
const bindingNames = Object.keys(runtimeBindings);
|
|
313775
|
+
const collision = findTopLevelRuntimeGlobalCollision(code, bindingNames);
|
|
313776
|
+
if (collision) {
|
|
313777
|
+
throw new Error(
|
|
313778
|
+
`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}").`
|
|
313779
|
+
);
|
|
313780
|
+
}
|
|
313781
|
+
(_b3 = options.debug) == null ? void 0 : _b3.call(options, "executeFile:compile:start", { fileName, executionMode });
|
|
313782
|
+
const compileStart = performance.now();
|
|
313783
|
+
const compiled = compileScript(code, fileName, options);
|
|
313784
|
+
(_c2 = options.debug) == null ? void 0 : _c2.call(options, "executeFile:compile:end", {
|
|
313785
|
+
fileName,
|
|
313786
|
+
executionMode,
|
|
313787
|
+
ms: Number((performance.now() - compileStart).toFixed(1))
|
|
313788
|
+
});
|
|
312833
313789
|
const bindingValues = bindingNames.map((name) => runtimeBindings[name]);
|
|
312834
313790
|
let scriptCode = compiled.code;
|
|
312835
313791
|
if (executionMode === "script") {
|
|
@@ -312855,12 +313811,20 @@ ${scriptCode}
|
|
|
312855
313811
|
exports: executionMode === "module" && moduleCacheEntry ? moduleCacheEntry.exports : {}
|
|
312856
313812
|
};
|
|
312857
313813
|
const initialExportsRef = moduleValue.exports;
|
|
313814
|
+
(_d2 = options.debug) == null ? void 0 : _d2.call(options, "executeFile:invoke:start", { fileName, executionMode });
|
|
313815
|
+
const invokeStart = performance.now();
|
|
312858
313816
|
const returnValue = withConstructorChainLockdown(
|
|
312859
313817
|
() => runWithParamScope(
|
|
312860
313818
|
scope,
|
|
312861
313819
|
() => fn(moduleValue.exports, moduleValue, requireModule, fileName, dirnamePath(fileName), ...bindingValues)
|
|
312862
313820
|
)
|
|
312863
313821
|
);
|
|
313822
|
+
(_e2 = options.debug) == null ? void 0 : _e2.call(options, "executeFile:invoke:end", {
|
|
313823
|
+
fileName,
|
|
313824
|
+
executionMode,
|
|
313825
|
+
ms: Number((performance.now() - invokeStart).toFixed(1)),
|
|
313826
|
+
returned: returnValue === void 0 ? "undefined" : returnValue === null ? "null" : typeof returnValue
|
|
313827
|
+
});
|
|
312864
313828
|
if (executionMode === "module") {
|
|
312865
313829
|
const hasExports = hasExplicitModuleExports(moduleValue.exports, initialExportsRef);
|
|
312866
313830
|
if (returnValue !== void 0 && hasExports) {
|
|
@@ -312899,12 +313863,14 @@ ${scriptCode}
|
|
|
312899
313863
|
}
|
|
312900
313864
|
return returnValue;
|
|
312901
313865
|
} finally {
|
|
313866
|
+
(_f = options.debug) == null ? void 0 : _f.call(options, "executeFile:end", { fileName, executionMode });
|
|
312902
313867
|
if (trackCircularImports) {
|
|
312903
313868
|
visited.delete(fileName);
|
|
312904
313869
|
}
|
|
312905
313870
|
}
|
|
312906
313871
|
}
|
|
312907
313872
|
function runScript(code, fileName = "main.forge.js", allFiles = {}, options = {}) {
|
|
313873
|
+
var _a3, _b3;
|
|
312908
313874
|
_collectedLogs = [];
|
|
312909
313875
|
resetExecutionSession(_collectedLogs);
|
|
312910
313876
|
const t0 = performance.now();
|
|
@@ -312913,14 +313879,25 @@ function runScript(code, fileName = "main.forge.js", allFiles = {}, options = {}
|
|
|
312913
313879
|
fileIndex: buildFileIndex(allFiles),
|
|
312914
313880
|
compiledFiles: persistentCompiledFiles,
|
|
312915
313881
|
moduleCache: /* @__PURE__ */ new Map(),
|
|
312916
|
-
readBinaryFile: options.readBinaryFile
|
|
313882
|
+
readBinaryFile: options.readBinaryFile,
|
|
313883
|
+
debug: options.debug
|
|
312917
313884
|
};
|
|
312918
313885
|
const quality = resolveForgeQualityPreset(options.quality);
|
|
313886
|
+
(_a3 = options.debug) == null ? void 0 : _a3.call(options, "runScript:start", { fileName, quality, fileCount: Object.keys(allFiles).length });
|
|
312919
313887
|
try {
|
|
312920
313888
|
return runWithForgeQuality(quality, () => {
|
|
313889
|
+
var _a4, _b4, _c2, _d2, _e2, _f, _g, _h, _i, _j;
|
|
313890
|
+
(_a4 = options.debug) == null ? void 0 : _a4.call(options, "runScript:execute:start", { fileName });
|
|
313891
|
+
const executeStart = performance.now();
|
|
312921
313892
|
const result = executeFile(code, fileName, allFiles, /* @__PURE__ */ new Set(), {}, execOptions);
|
|
313893
|
+
(_b4 = options.debug) == null ? void 0 : _b4.call(options, "runScript:execute:end", {
|
|
313894
|
+
fileName,
|
|
313895
|
+
ms: Number((performance.now() - executeStart).toFixed(1)),
|
|
313896
|
+
resultType: result === void 0 ? "undefined" : result === null ? "null" : typeof result
|
|
313897
|
+
});
|
|
312922
313898
|
const highlights = getCollectedHighlights();
|
|
312923
313899
|
const mocks = getCollectedMocks();
|
|
313900
|
+
(_c2 = options.debug) == null ? void 0 : _c2.call(options, "runScript:map:start", { highlights: highlights.length, mocks: mocks.length });
|
|
312924
313901
|
const mapped = mapScriptResultToScene({
|
|
312925
313902
|
result,
|
|
312926
313903
|
fileName,
|
|
@@ -312929,24 +313906,43 @@ function runScript(code, fileName = "main.forge.js", allFiles = {}, options = {}
|
|
|
312929
313906
|
mocks,
|
|
312930
313907
|
logs: _collectedLogs
|
|
312931
313908
|
});
|
|
313909
|
+
(_d2 = options.debug) == null ? void 0 : _d2.call(options, "runScript:map:end", {
|
|
313910
|
+
objects: mapped.objects.length,
|
|
313911
|
+
hasShape: Boolean(mapped.shape),
|
|
313912
|
+
hasSketch: Boolean(mapped.sketch),
|
|
313913
|
+
hasError: Boolean(mapped.error)
|
|
313914
|
+
});
|
|
313915
|
+
(_e2 = options.debug) == null ? void 0 : _e2.call(options, "runScript:explodeHints:start", { objects: mapped.objects.length });
|
|
312932
313916
|
autoFillExplodeHints(mapped.objects);
|
|
313917
|
+
(_f = options.debug) == null ? void 0 : _f.call(options, "runScript:explodeHints:end");
|
|
313918
|
+
(_g = options.debug) == null ? void 0 : _g.call(options, "runScript:snapshot:start", { objects: mapped.objects.length });
|
|
313919
|
+
const snapshot = collectSuccessfulExecutionSnapshot({
|
|
313920
|
+
quality,
|
|
313921
|
+
objects: mapped.objects,
|
|
313922
|
+
logs: _collectedLogs,
|
|
313923
|
+
extraDimensions: mapped.extraDimensions,
|
|
313924
|
+
highlights,
|
|
313925
|
+
mocks
|
|
313926
|
+
});
|
|
313927
|
+
(_h = options.debug) == null ? void 0 : _h.call(options, "runScript:snapshot:end", {
|
|
313928
|
+
params: snapshot.params.length,
|
|
313929
|
+
cutPlanes: snapshot.cutPlanes.length,
|
|
313930
|
+
verifications: snapshot.verifications.length
|
|
313931
|
+
});
|
|
313932
|
+
(_i = options.debug) == null ? void 0 : _i.call(options, "runScript:sceneTargets:start");
|
|
313933
|
+
snapshot.sceneConfig = resolveSceneJourneyTargets(snapshot.sceneConfig, mapped.objects);
|
|
313934
|
+
(_j = options.debug) == null ? void 0 : _j.call(options, "runScript:sceneTargets:end");
|
|
312933
313935
|
return {
|
|
312934
313936
|
shape: mapped.shape,
|
|
312935
313937
|
sketch: mapped.sketch,
|
|
312936
313938
|
objects: mapped.objects,
|
|
312937
|
-
...
|
|
312938
|
-
quality,
|
|
312939
|
-
objects: mapped.objects,
|
|
312940
|
-
logs: _collectedLogs,
|
|
312941
|
-
extraDimensions: mapped.extraDimensions,
|
|
312942
|
-
highlights,
|
|
312943
|
-
mocks
|
|
312944
|
-
}),
|
|
313939
|
+
...snapshot,
|
|
312945
313940
|
error: mapped.error,
|
|
312946
313941
|
timeMs: performance.now() - t0
|
|
312947
313942
|
};
|
|
312948
313943
|
});
|
|
312949
313944
|
} catch (e) {
|
|
313945
|
+
(_b3 = options.debug) == null ? void 0 : _b3.call(options, "runScript:error", { error: (e == null ? void 0 : e.message) || String(e) });
|
|
312950
313946
|
const msg = e.message || String(e);
|
|
312951
313947
|
const stack = e.stack || "";
|
|
312952
313948
|
let lineInfo = "";
|