forgecad 0.10.3 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{AdminPage-CK7ObBz3.js → AdminPage-B3L3W1Uo.js} +1 -1
- package/dist/assets/{BenchmarkPage-Ds7Z2doN.js → BenchmarkPage-DXKVXMrJ.js} +2 -2
- package/dist/assets/{BlogPage-DlPbpt6A.js → BlogPage-B7BWxOCg.js} +1 -1
- package/dist/assets/{DocsPage-vZb3b3Y0.js → DocsPage-BPGGwht1.js} +28 -43
- package/dist/assets/{EditorApp-HLoKfe15.js → EditorApp-BWUGCdD5.js} +49 -16
- package/dist/assets/{EmbedViewer--KnqBKrJ.js → EmbedViewer-DygByZS2.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-C_LssmnA.js → LandingPageProofDriven-BoVE7JGY.js} +54 -36
- package/dist/assets/{LegalPage-DGsyo4n1.js → LegalPage-Din8wv8d.js} +2 -2
- package/dist/assets/{PricingPage-BOE27B-R.js → PricingPage-C2PMzmDc.js} +2 -2
- package/dist/assets/{SettingsPage-f47cnk39.js → SettingsPage-BlJDCRe8.js} +1 -1
- package/dist/assets/{app-D6ccu2Xx.js → app-BsRYSfxY.js} +238 -3714
- package/dist/assets/{backendInit-DbTkQN9J.js → backendInit-6C0DLgH0.js} +5972 -1566
- package/dist/assets/cli/{render-BsngirjC.js → render-XXol_ET7.js} +724 -112
- package/dist/assets/{constructionHistoryWorker-PCwXrTDB.js → constructionHistoryWorker-cTHWRJEi.js} +528 -252
- package/dist/assets/{evalWorker-CS63PfZu.js → evalWorker-BssDYW9u.js} +1453 -902
- package/dist/assets/{inspectWorker-Y4cOzNyA.js → inspectWorker-ymhBV4Ll.js} +2635 -1024
- package/dist/assets/{jointPose-AMvCywzS.js → jointPose-B0blBj9A.js} +1 -1
- package/dist/assets/{landing-proof-driven-ORyigZ6p.css → landing-proof-driven-Cpf-MIbI.css} +73 -13
- package/dist/assets/{manifold-Crd_F2qx.js → manifold-B_7QXpGB.js} +1 -1
- package/dist/assets/{manifold-k2kRcc85.js → manifold-CNShmpEJ.js} +1 -1
- package/dist/assets/{manifold-CBry38ly.js → manifold-CYlIm-M6.js} +2 -2
- package/dist/assets/{reportWorker-CWvn0CEv.js → reportWorker-Cb5eyM7D.js} +1407 -892
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +17 -15
- package/dist/docs-raw/component-model.md +2 -2
- package/dist/docs-raw/generated/concepts.md +5 -1
- package/dist/docs-raw/generated/core.md +26 -0
- package/dist/docs-raw/generated/runtime-names.md +1 -1
- package/dist/docs-raw/guides/inspection-bundles.md +1 -1
- package/dist/docs-raw/simulation-workflow.md +1 -1
- package/dist/docs-raw/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
- package/dist/docs-raw/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
- package/dist/docs-raw/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
- package/{dist-skill/website/skills/forgecad-visual-spec.md → dist/docs-raw/skills/forgecad-image-prompt.md} +7 -7
- package/dist/docs-raw/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
- package/{dist-skill/website/skills/forgecad-project.md → dist/docs-raw/skills/forgecad-project-sync.md} +5 -5
- package/dist/docs-raw/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
- package/dist/docs-raw/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
- package/dist/docs-raw/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
- package/dist/docs-raw/skills/index.md +9 -12
- package/dist/index.html +9 -9
- package/dist/llms.txt +7 -7
- package/dist/sitemap.xml +16 -16
- package/dist-cli/{check-compiler-HPF2T2FS.js → check-compiler-4RPB6SB5.js} +1 -1
- package/dist-cli/{check-query-propagation-HYSLTXAB.js → check-query-propagation-KN3DFQTX.js} +1 -1
- package/dist-cli/{chunk-WLUKAW3H.js → chunk-UHBRMYA6.js} +28802 -28152
- package/dist-cli/forgecad.js +660 -9
- package/dist-skill/CONTEXT.md +27 -1
- package/dist-skill/docs/generated/core.md +26 -0
- package/dist-skill/docs/generated/runtime-names.md +1 -1
- package/dist-skill/docs/guides/inspection-bundles.md +1 -1
- package/dist-skill/library/README.md +9 -12
- package/dist-skill/library/{forgecad-make-a-model → forgecad-build-model}/SKILL.md +16 -6
- package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/SKILL.md +4 -4
- package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/master-prompt.md +1 -1
- package/dist-skill/library/{forgecad-model-grader → forgecad-grade-model}/SKILL.md +6 -4
- package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-visual-spec → forgecad-image-prompt}/SKILL.md +5 -5
- package/dist-skill/library/forgecad-image-prompt/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/SKILL.md +4 -4
- package/dist-skill/library/{forgecad-project → forgecad-project-sync}/SKILL.md +3 -3
- package/dist-skill/library/{forgecad-3d-reconstruction → forgecad-reconstruct-cad-file}/SKILL.md +5 -5
- package/dist-skill/library/forgecad-reconstruct-cad-file/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/SKILL.md +10 -10
- package/dist-skill/library/forgecad-reconstruct-from-images/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/SKILL.md +4 -4
- package/dist-skill/website/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
- package/dist-skill/website/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
- package/dist-skill/website/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
- package/{dist/docs-raw/skills/forgecad-visual-spec.md → dist-skill/website/skills/forgecad-image-prompt.md} +7 -7
- package/dist-skill/website/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
- package/{dist/docs-raw/skills/forgecad-project.md → dist-skill/website/skills/forgecad-project-sync.md} +5 -5
- package/dist-skill/website/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
- package/dist-skill/website/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
- package/dist-skill/website/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
- package/dist-skill/website/skills/index.md +9 -12
- package/examples/api/texture-projection.forge.js +75 -0
- package/examples/assets/uv-grid.png +0 -0
- package/package.json +1 -1
- package/dist/docs-raw/skills/forgecad-blockout-model.md +0 -49
- package/dist/docs-raw/skills/forgecad-component-model.md +0 -53
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +0 -60
- package/dist-skill/library/forgecad-3d-reconstruction/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +0 -42
- package/dist-skill/library/forgecad-component-model/SKILL.md +0 -46
- package/dist-skill/library/forgecad-image-replicator/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-model-grader/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +0 -48
- package/dist-skill/library/forgecad-reconstruction-benchmark/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-visual-spec/agents/openai.yaml +0 -4
- package/dist-skill/website/skills/forgecad-blockout-model.md +0 -49
- package/dist-skill/website/skills/forgecad-component-model.md +0 -53
- package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +0 -60
- /package/dist/assets/{landing-proof-driven-DiGqdtWa.js → landing-proof-driven-BxZZh5r5.js} +0 -0
- /package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/default-profiles.md +0 -0
- /package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/summarize_manifest.py +0 -0
- /package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/scripts/compare_images.py +0 -0
- /package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/scripts/mujoco_verify.py +0 -0
package/dist-cli/forgecad.js
CHANGED
|
@@ -37,6 +37,8 @@ import {
|
|
|
37
37
|
circle2d,
|
|
38
38
|
collectProjectFiles,
|
|
39
39
|
collectSimulationModel,
|
|
40
|
+
compileProjection,
|
|
41
|
+
computeGeometryArrays,
|
|
40
42
|
constrainedSketch,
|
|
41
43
|
cylinder,
|
|
42
44
|
describeFaceQueryRef,
|
|
@@ -134,11 +136,12 @@ import {
|
|
|
134
136
|
updateConstraintValue,
|
|
135
137
|
validateSimulationModel,
|
|
136
138
|
wrapOCCTShapeBackend
|
|
137
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-UHBRMYA6.js";
|
|
138
140
|
|
|
139
141
|
// cli/forgecad.ts
|
|
140
142
|
import { Command, Flags, Help, flush as flushOclif, handle as handleOclif, run as runOclif } from "@oclif/core";
|
|
141
|
-
import { readFileSync as readFileSync28 } from "fs";
|
|
143
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync22 } from "fs";
|
|
144
|
+
import { extname as extname15, resolve as resolvePath } from "path";
|
|
142
145
|
|
|
143
146
|
// cli/check-api-contracts.ts
|
|
144
147
|
import assert from "assert/strict";
|
|
@@ -5667,7 +5670,7 @@ function computeMeshInertia(mesh, massKg) {
|
|
|
5667
5670
|
}
|
|
5668
5671
|
|
|
5669
5672
|
// src/studio/project-files/importGraph.ts
|
|
5670
|
-
var FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
5673
|
+
var FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.image|Import\.svgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
5671
5674
|
var REQUIRE_RE = /\brequire\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
5672
5675
|
var ES_IMPORT_RE = /\bfrom\s+(?:"([^"]+)"|'([^']+)')/g;
|
|
5673
5676
|
var VIRTUAL_MODULES = /* @__PURE__ */ new Set(["forgecad", "@forge/runtime", "@forgecad/runtime"]);
|
|
@@ -5686,6 +5689,7 @@ function extractImports(code) {
|
|
|
5686
5689
|
importSvgSketch: "forgeSvg",
|
|
5687
5690
|
"Import.mesh": "forgeMesh",
|
|
5688
5691
|
"Import.step": "forgeMesh",
|
|
5692
|
+
"Import.image": "forgeMesh",
|
|
5689
5693
|
"Import.svgSketch": "forgeSvg",
|
|
5690
5694
|
"Import.dxfSketch": "forgeDxf",
|
|
5691
5695
|
compareWith: "compareWith"
|
|
@@ -5695,7 +5699,7 @@ function extractImports(code) {
|
|
|
5695
5699
|
while ((m = forgeRe.exec(code)) !== null) {
|
|
5696
5700
|
const path5 = m[1] ?? m[2];
|
|
5697
5701
|
const fn = m[0].match(
|
|
5698
|
-
/\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)/
|
|
5702
|
+
/\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.image|Import\.svgSketch|Import\.dxfSketch|compareWith)/
|
|
5699
5703
|
)?.[1];
|
|
5700
5704
|
add2(path5, kindMap[fn] ?? "forgeMesh");
|
|
5701
5705
|
}
|
|
@@ -7563,6 +7567,13 @@ function parsePositiveInteger2(value, flag) {
|
|
|
7563
7567
|
}
|
|
7564
7568
|
return parsed;
|
|
7565
7569
|
}
|
|
7570
|
+
function parseNonNegativeInteger(value, flag) {
|
|
7571
|
+
const parsed = Number.parseInt(value, 10);
|
|
7572
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
7573
|
+
throw new Error(`${flag} must be a non-negative integer (got '${value}')`);
|
|
7574
|
+
}
|
|
7575
|
+
return parsed;
|
|
7576
|
+
}
|
|
7566
7577
|
function parseScanGranularity(value) {
|
|
7567
7578
|
const numeric = parsePositiveInteger2(value, "--scan-granularity");
|
|
7568
7579
|
if (numeric < SCAN_GRANULARITY_MIN || numeric > SCAN_GRANULARITY_MAX) {
|
|
@@ -8133,6 +8144,7 @@ function parseInspectCli(argv, config = {}) {
|
|
|
8133
8144
|
let compareToleranceMm;
|
|
8134
8145
|
const thickness = {};
|
|
8135
8146
|
const roughness = {};
|
|
8147
|
+
const scalarFieldParams = {};
|
|
8136
8148
|
const sectionPlan = { planes: [] };
|
|
8137
8149
|
let sectionOptionsUsed = false;
|
|
8138
8150
|
let sectionOffset;
|
|
@@ -8277,6 +8289,22 @@ function parseInspectCli(argv, config = {}) {
|
|
|
8277
8289
|
i += 1;
|
|
8278
8290
|
continue;
|
|
8279
8291
|
}
|
|
8292
|
+
if (arg === "--bands" || arg === "--iso" || arg === "--iso-width") {
|
|
8293
|
+
const isScalarFieldChannel = channels?.includes("thickness") || channels?.includes("roughness");
|
|
8294
|
+
if (!isScalarFieldChannel) {
|
|
8295
|
+
throw new Error(`${arg} only works with \`forgecad inspect surface thickness\` or \`... roughness\`.`);
|
|
8296
|
+
}
|
|
8297
|
+
if (arg === "--bands") {
|
|
8298
|
+
scalarFieldParams.quantizeBands = parseNonNegativeInteger(readValue2(argv, i, arg), arg);
|
|
8299
|
+
i += 1;
|
|
8300
|
+
} else if (arg === "--iso-width") {
|
|
8301
|
+
scalarFieldParams.isoLineWidthPx = parsePositiveNumber(readValue2(argv, i, arg), arg);
|
|
8302
|
+
i += 1;
|
|
8303
|
+
} else {
|
|
8304
|
+
scalarFieldParams.isoEnabled = true;
|
|
8305
|
+
}
|
|
8306
|
+
continue;
|
|
8307
|
+
}
|
|
8280
8308
|
if (arg === "--plane" && channels?.includes("section")) {
|
|
8281
8309
|
addSectionPlaneRequests(sectionPlan, readValue2(argv, i, arg), arg);
|
|
8282
8310
|
sectionOptionsUsed = true;
|
|
@@ -8524,6 +8552,10 @@ function parseInspectCli(argv, config = {}) {
|
|
|
8524
8552
|
freshServer,
|
|
8525
8553
|
thickness,
|
|
8526
8554
|
roughness,
|
|
8555
|
+
// Only forward heatmap overrides when a flag actually set one; an empty object
|
|
8556
|
+
// would still merge to the viewport default, but undefined keeps the payload and
|
|
8557
|
+
// the inspect bundle byte-identical to the pre-flag behavior.
|
|
8558
|
+
scalarFieldParams: Object.keys(scalarFieldParams).length > 0 ? scalarFieldParams : void 0,
|
|
8527
8559
|
sectionPlan: sectionOptionsUsed ? {
|
|
8528
8560
|
...sectionPlan.planes.length > 0 ? { planes: sectionPlan.planes } : {},
|
|
8529
8561
|
...sectionPlan.offsets != null ? { offsets: sectionPlan.offsets } : {},
|
|
@@ -9772,6 +9804,11 @@ async function runInspectBundleInput(options) {
|
|
|
9772
9804
|
});
|
|
9773
9805
|
try {
|
|
9774
9806
|
const page = await browser.newPage();
|
|
9807
|
+
if (process.env.FORGECAD_INSPECT_PROFILE === "1") {
|
|
9808
|
+
page.on("console", (message) => {
|
|
9809
|
+
console.log(`[browser:${message.type()}] ${message.text()}`);
|
|
9810
|
+
});
|
|
9811
|
+
}
|
|
9775
9812
|
const url = `http://127.0.0.1:${activePort}/cli/render.html`;
|
|
9776
9813
|
await page.goto(url, { waitUntil: "networkidle0", timeout: 15e3 });
|
|
9777
9814
|
await page.waitForFunction("window.__forgeReady === true", { timeout: RENDER_READY_TIMEOUT_MS });
|
|
@@ -9780,6 +9817,7 @@ async function runInspectBundleInput(options) {
|
|
|
9780
9817
|
cameras: options.cameras,
|
|
9781
9818
|
size: options.size,
|
|
9782
9819
|
quality: options.quality,
|
|
9820
|
+
debug: process.env.FORGECAD_INSPECT_PROFILE === "1",
|
|
9783
9821
|
binaryFiles,
|
|
9784
9822
|
activeBackend,
|
|
9785
9823
|
cameraSpec: options.cameraSpec || null,
|
|
@@ -9798,6 +9836,7 @@ async function runInspectBundleInput(options) {
|
|
|
9798
9836
|
cameras: browserRenderOptions.cameras,
|
|
9799
9837
|
size: browserRenderOptions.size,
|
|
9800
9838
|
quality: browserRenderOptions.quality,
|
|
9839
|
+
debug: browserRenderOptions.debug,
|
|
9801
9840
|
channels: browserRenderOptions.channels,
|
|
9802
9841
|
allFiles: files,
|
|
9803
9842
|
fileName: scriptName,
|
|
@@ -9811,6 +9850,7 @@ async function runInspectBundleInput(options) {
|
|
|
9811
9850
|
hide: browserRenderOptions.hide,
|
|
9812
9851
|
thickness: browserRenderOptions.thickness,
|
|
9813
9852
|
roughness: browserRenderOptions.roughness,
|
|
9853
|
+
scalarFieldParams: browserRenderOptions.scalarFieldParams,
|
|
9814
9854
|
sectionPlan: browserRenderOptions.sectionPlan,
|
|
9815
9855
|
cutaway: browserRenderOptions.cutaway,
|
|
9816
9856
|
comparison: browserRenderOptions.comparison,
|
|
@@ -9833,6 +9873,7 @@ async function runInspectBundleInput(options) {
|
|
|
9833
9873
|
channels: options.channels,
|
|
9834
9874
|
thickness: options.thickness,
|
|
9835
9875
|
roughness: options.roughness,
|
|
9876
|
+
scalarFieldParams: options.scalarFieldParams,
|
|
9836
9877
|
sectionPlan: options.sectionPlan,
|
|
9837
9878
|
cutaway: options.cutaway,
|
|
9838
9879
|
jointOverrides: options.jointOverrides,
|
|
@@ -22284,6 +22325,361 @@ async function exportMeshInput(format, scriptPath, outputPath, quality, backend,
|
|
|
22284
22325
|
stats.forEach((line) => console.log(line));
|
|
22285
22326
|
}
|
|
22286
22327
|
|
|
22328
|
+
// src/forge/mesh/textureProjection.ts
|
|
22329
|
+
function bakeProjectedUv(positions, spec) {
|
|
22330
|
+
if (!(positions instanceof Float32Array)) {
|
|
22331
|
+
throw new TypeError("bakeProjectedUv: positions must be a Float32Array.");
|
|
22332
|
+
}
|
|
22333
|
+
if (positions.length % 3 !== 0) {
|
|
22334
|
+
throw new RangeError(
|
|
22335
|
+
`bakeProjectedUv: positions length must be a multiple of 3, got ${positions.length}.`
|
|
22336
|
+
);
|
|
22337
|
+
}
|
|
22338
|
+
const project = compileProjection(spec);
|
|
22339
|
+
const vertexCount = positions.length / 3;
|
|
22340
|
+
const uvs = new Float32Array(vertexCount * 2);
|
|
22341
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
22342
|
+
const x = positions[i * 3];
|
|
22343
|
+
const y = positions[i * 3 + 1];
|
|
22344
|
+
const z = positions[i * 3 + 2];
|
|
22345
|
+
const [u, v] = project(x, y, z);
|
|
22346
|
+
uvs[i * 2] = u;
|
|
22347
|
+
uvs[i * 2 + 1] = v;
|
|
22348
|
+
}
|
|
22349
|
+
return uvs;
|
|
22350
|
+
}
|
|
22351
|
+
|
|
22352
|
+
// src/forge/export/exportGltf.ts
|
|
22353
|
+
var GLTF_FLOAT = 5126;
|
|
22354
|
+
var GLTF_UNSIGNED_INT = 5125;
|
|
22355
|
+
var GLTF_ARRAY_BUFFER = 34962;
|
|
22356
|
+
var GLTF_ELEMENT_ARRAY_BUFFER = 34963;
|
|
22357
|
+
var GLTF_WRAP_REPEAT = 10497;
|
|
22358
|
+
var GLTF_FILTER_LINEAR = 9729;
|
|
22359
|
+
var GLTF_FILTER_LINEAR_MIPMAP_LINEAR = 9987;
|
|
22360
|
+
function parseHexColor01(hex) {
|
|
22361
|
+
if (!hex) return null;
|
|
22362
|
+
const value = hex.trim();
|
|
22363
|
+
if (!value.startsWith("#")) return null;
|
|
22364
|
+
let r;
|
|
22365
|
+
let g;
|
|
22366
|
+
let b;
|
|
22367
|
+
if (value.length === 7) {
|
|
22368
|
+
r = Number.parseInt(value.slice(1, 3), 16);
|
|
22369
|
+
g = Number.parseInt(value.slice(3, 5), 16);
|
|
22370
|
+
b = Number.parseInt(value.slice(5, 7), 16);
|
|
22371
|
+
} else if (value.length === 4) {
|
|
22372
|
+
r = Number.parseInt(value[1] + value[1], 16);
|
|
22373
|
+
g = Number.parseInt(value[2] + value[2], 16);
|
|
22374
|
+
b = Number.parseInt(value[3] + value[3], 16);
|
|
22375
|
+
} else {
|
|
22376
|
+
return null;
|
|
22377
|
+
}
|
|
22378
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null;
|
|
22379
|
+
return { r: srgbToLinear(r / 255), g: srgbToLinear(g / 255), b: srgbToLinear(b / 255) };
|
|
22380
|
+
}
|
|
22381
|
+
function srgbToLinear(c) {
|
|
22382
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
22383
|
+
}
|
|
22384
|
+
function decodeDataUri(dataUri) {
|
|
22385
|
+
const match = /^data:([^;,]+)(;base64)?,(.*)$/s.exec(dataUri);
|
|
22386
|
+
if (!match) {
|
|
22387
|
+
throw new Error(
|
|
22388
|
+
"glTF export: texture image is not a data: URI. Expected the self-contained data URI produced by Import.image(...)."
|
|
22389
|
+
);
|
|
22390
|
+
}
|
|
22391
|
+
const mimeType = match[1] || "application/octet-stream";
|
|
22392
|
+
const isBase64 = match[2] === ";base64";
|
|
22393
|
+
const payload = match[3];
|
|
22394
|
+
if (!isBase64) {
|
|
22395
|
+
throw new Error("glTF export: only base64-encoded texture data URIs are supported.");
|
|
22396
|
+
}
|
|
22397
|
+
let bytes;
|
|
22398
|
+
if (typeof Buffer !== "undefined") {
|
|
22399
|
+
bytes = new Uint8Array(Buffer.from(payload, "base64"));
|
|
22400
|
+
} else {
|
|
22401
|
+
const binary = atob(payload);
|
|
22402
|
+
bytes = new Uint8Array(binary.length);
|
|
22403
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
22404
|
+
}
|
|
22405
|
+
return { mimeType, bytes };
|
|
22406
|
+
}
|
|
22407
|
+
function extractShapeGeometry(obj) {
|
|
22408
|
+
const mesh = obj.shape.getMesh();
|
|
22409
|
+
const geo = computeGeometryArrays(mesh, { skipEdges: true });
|
|
22410
|
+
const positions = geo.positions;
|
|
22411
|
+
const normals = geo.normals;
|
|
22412
|
+
const materialProps = obj.shape.materialProps;
|
|
22413
|
+
const textureDescriptor = materialProps?.texture;
|
|
22414
|
+
let uvs = null;
|
|
22415
|
+
let texture = null;
|
|
22416
|
+
if (geo.uvs) {
|
|
22417
|
+
uvs = geo.uvs;
|
|
22418
|
+
if (textureDescriptor) {
|
|
22419
|
+
texture = decodeDataUri(textureDescriptor.image);
|
|
22420
|
+
}
|
|
22421
|
+
} else if (textureDescriptor) {
|
|
22422
|
+
uvs = bakeProjectedUv(positions, textureDescriptor.projection);
|
|
22423
|
+
texture = decodeDataUri(textureDescriptor.image);
|
|
22424
|
+
}
|
|
22425
|
+
const numVerts = positions.length / 3;
|
|
22426
|
+
const indices = new Uint32Array(numVerts);
|
|
22427
|
+
for (let i = 0; i < numVerts; i++) indices[i] = i;
|
|
22428
|
+
const metalness = typeof materialProps?.metalness === "number" ? materialProps.metalness : 0.05;
|
|
22429
|
+
const roughness = typeof materialProps?.roughness === "number" ? materialProps.roughness : 0.35;
|
|
22430
|
+
const opacity = typeof materialProps?.opacity === "number" ? materialProps.opacity : 1;
|
|
22431
|
+
return {
|
|
22432
|
+
positions,
|
|
22433
|
+
normals,
|
|
22434
|
+
uvs,
|
|
22435
|
+
indices,
|
|
22436
|
+
texture,
|
|
22437
|
+
baseColor: parseHexColor01(obj.color),
|
|
22438
|
+
metalness,
|
|
22439
|
+
roughness,
|
|
22440
|
+
opacity,
|
|
22441
|
+
name: obj.name || "Object"
|
|
22442
|
+
};
|
|
22443
|
+
}
|
|
22444
|
+
function align4(n) {
|
|
22445
|
+
return n + 3 & ~3;
|
|
22446
|
+
}
|
|
22447
|
+
function vec3MinMax(arr) {
|
|
22448
|
+
const min = [Infinity, Infinity, Infinity];
|
|
22449
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
22450
|
+
for (let i = 0; i < arr.length; i += 3) {
|
|
22451
|
+
for (let c = 0; c < 3; c++) {
|
|
22452
|
+
const val = arr[i + c];
|
|
22453
|
+
if (val < min[c]) min[c] = val;
|
|
22454
|
+
if (val > max[c]) max[c] = val;
|
|
22455
|
+
}
|
|
22456
|
+
}
|
|
22457
|
+
return { min, max };
|
|
22458
|
+
}
|
|
22459
|
+
function buildGltf(objects) {
|
|
22460
|
+
if (objects.length === 0) {
|
|
22461
|
+
throw new Error("No shapes provided for glTF export.");
|
|
22462
|
+
}
|
|
22463
|
+
const geometries = objects.map(extractShapeGeometry);
|
|
22464
|
+
const bufferViews = [];
|
|
22465
|
+
const accessors = [];
|
|
22466
|
+
const meshes = [];
|
|
22467
|
+
const nodes = [];
|
|
22468
|
+
const materials = [];
|
|
22469
|
+
const textures = [];
|
|
22470
|
+
const images = [];
|
|
22471
|
+
const samplers = [];
|
|
22472
|
+
const chunks = [];
|
|
22473
|
+
let byteOffset = 0;
|
|
22474
|
+
function pushBufferView(data, target) {
|
|
22475
|
+
const padded = align4(data.byteLength);
|
|
22476
|
+
const view = {
|
|
22477
|
+
buffer: 0,
|
|
22478
|
+
byteOffset,
|
|
22479
|
+
byteLength: data.byteLength
|
|
22480
|
+
};
|
|
22481
|
+
if (target !== void 0) view.target = target;
|
|
22482
|
+
bufferViews.push(view);
|
|
22483
|
+
const chunk = new Uint8Array(padded);
|
|
22484
|
+
chunk.set(data);
|
|
22485
|
+
chunks.push(chunk);
|
|
22486
|
+
byteOffset += padded;
|
|
22487
|
+
return bufferViews.length - 1;
|
|
22488
|
+
}
|
|
22489
|
+
function typedArrayBytes(arr) {
|
|
22490
|
+
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
22491
|
+
}
|
|
22492
|
+
const imageIndexByKey = /* @__PURE__ */ new Map();
|
|
22493
|
+
const samplerIndexByKey = /* @__PURE__ */ new Map();
|
|
22494
|
+
function ensureSampler() {
|
|
22495
|
+
const key = "repeat-linear-mip";
|
|
22496
|
+
const existing = samplerIndexByKey.get(key);
|
|
22497
|
+
if (existing !== void 0) return existing;
|
|
22498
|
+
samplers.push({
|
|
22499
|
+
wrapS: GLTF_WRAP_REPEAT,
|
|
22500
|
+
wrapT: GLTF_WRAP_REPEAT,
|
|
22501
|
+
magFilter: GLTF_FILTER_LINEAR,
|
|
22502
|
+
minFilter: GLTF_FILTER_LINEAR_MIPMAP_LINEAR
|
|
22503
|
+
});
|
|
22504
|
+
const idx = samplers.length - 1;
|
|
22505
|
+
samplerIndexByKey.set(key, idx);
|
|
22506
|
+
return idx;
|
|
22507
|
+
}
|
|
22508
|
+
function ensureTexture(image) {
|
|
22509
|
+
const key = `${image.mimeType}|${image.bytes.byteLength}`;
|
|
22510
|
+
const fpKey = `${key}|${fingerprint2(image.bytes)}`;
|
|
22511
|
+
const existing = imageIndexByKey.get(fpKey);
|
|
22512
|
+
let imageIdx;
|
|
22513
|
+
if (existing !== void 0) {
|
|
22514
|
+
imageIdx = existing;
|
|
22515
|
+
} else {
|
|
22516
|
+
const bufferViewIdx = pushBufferView(image.bytes);
|
|
22517
|
+
images.push({ mimeType: image.mimeType, bufferView: bufferViewIdx });
|
|
22518
|
+
imageIdx = images.length - 1;
|
|
22519
|
+
imageIndexByKey.set(fpKey, imageIdx);
|
|
22520
|
+
}
|
|
22521
|
+
const samplerIdx = ensureSampler();
|
|
22522
|
+
textures.push({ sampler: samplerIdx, source: imageIdx });
|
|
22523
|
+
return textures.length - 1;
|
|
22524
|
+
}
|
|
22525
|
+
for (const geo of geometries) {
|
|
22526
|
+
const attributes = {};
|
|
22527
|
+
const posView = pushBufferView(typedArrayBytes(geo.positions), GLTF_ARRAY_BUFFER);
|
|
22528
|
+
const { min, max } = vec3MinMax(geo.positions);
|
|
22529
|
+
accessors.push({
|
|
22530
|
+
bufferView: posView,
|
|
22531
|
+
componentType: GLTF_FLOAT,
|
|
22532
|
+
count: geo.positions.length / 3,
|
|
22533
|
+
type: "VEC3",
|
|
22534
|
+
min,
|
|
22535
|
+
max
|
|
22536
|
+
});
|
|
22537
|
+
attributes.POSITION = accessors.length - 1;
|
|
22538
|
+
const nView = pushBufferView(typedArrayBytes(geo.normals), GLTF_ARRAY_BUFFER);
|
|
22539
|
+
accessors.push({
|
|
22540
|
+
bufferView: nView,
|
|
22541
|
+
componentType: GLTF_FLOAT,
|
|
22542
|
+
count: geo.normals.length / 3,
|
|
22543
|
+
type: "VEC3"
|
|
22544
|
+
});
|
|
22545
|
+
attributes.NORMAL = accessors.length - 1;
|
|
22546
|
+
if (geo.uvs) {
|
|
22547
|
+
const uvView = pushBufferView(typedArrayBytes(geo.uvs), GLTF_ARRAY_BUFFER);
|
|
22548
|
+
accessors.push({
|
|
22549
|
+
bufferView: uvView,
|
|
22550
|
+
componentType: GLTF_FLOAT,
|
|
22551
|
+
count: geo.uvs.length / 2,
|
|
22552
|
+
type: "VEC2"
|
|
22553
|
+
});
|
|
22554
|
+
attributes.TEXCOORD_0 = accessors.length - 1;
|
|
22555
|
+
}
|
|
22556
|
+
const idxView = pushBufferView(typedArrayBytes(geo.indices), GLTF_ELEMENT_ARRAY_BUFFER);
|
|
22557
|
+
accessors.push({
|
|
22558
|
+
bufferView: idxView,
|
|
22559
|
+
componentType: GLTF_UNSIGNED_INT,
|
|
22560
|
+
count: geo.indices.length,
|
|
22561
|
+
type: "SCALAR"
|
|
22562
|
+
});
|
|
22563
|
+
const indicesAccessor = accessors.length - 1;
|
|
22564
|
+
const color = geo.baseColor;
|
|
22565
|
+
const pbr = {
|
|
22566
|
+
metallicFactor: geo.metalness,
|
|
22567
|
+
roughnessFactor: geo.roughness
|
|
22568
|
+
};
|
|
22569
|
+
if (color) {
|
|
22570
|
+
pbr.baseColorFactor = [color.r, color.g, color.b, geo.opacity];
|
|
22571
|
+
} else if (geo.opacity !== 1) {
|
|
22572
|
+
pbr.baseColorFactor = [1, 1, 1, geo.opacity];
|
|
22573
|
+
}
|
|
22574
|
+
if (geo.texture) {
|
|
22575
|
+
const texIdx = ensureTexture(geo.texture);
|
|
22576
|
+
pbr.baseColorTexture = { index: texIdx, texCoord: 0 };
|
|
22577
|
+
}
|
|
22578
|
+
const material = {
|
|
22579
|
+
name: `${geo.name} material`,
|
|
22580
|
+
pbrMetallicRoughness: pbr
|
|
22581
|
+
};
|
|
22582
|
+
if (geo.opacity !== 1) {
|
|
22583
|
+
material.alphaMode = "BLEND";
|
|
22584
|
+
}
|
|
22585
|
+
materials.push(material);
|
|
22586
|
+
const materialIdx = materials.length - 1;
|
|
22587
|
+
meshes.push({
|
|
22588
|
+
name: geo.name,
|
|
22589
|
+
primitives: [
|
|
22590
|
+
{
|
|
22591
|
+
attributes,
|
|
22592
|
+
indices: indicesAccessor,
|
|
22593
|
+
material: materialIdx,
|
|
22594
|
+
mode: 4
|
|
22595
|
+
// TRIANGLES
|
|
22596
|
+
}
|
|
22597
|
+
]
|
|
22598
|
+
});
|
|
22599
|
+
nodes.push({ name: geo.name, mesh: meshes.length - 1 });
|
|
22600
|
+
}
|
|
22601
|
+
const totalBytes = byteOffset;
|
|
22602
|
+
const binary = new Uint8Array(totalBytes);
|
|
22603
|
+
let cursor = 0;
|
|
22604
|
+
for (const chunk of chunks) {
|
|
22605
|
+
binary.set(chunk, cursor);
|
|
22606
|
+
cursor += chunk.byteLength;
|
|
22607
|
+
}
|
|
22608
|
+
const json = {
|
|
22609
|
+
asset: { version: "2.0", generator: "ForgeCAD glTF Export" },
|
|
22610
|
+
scene: 0,
|
|
22611
|
+
scenes: [{ nodes: nodes.map((_, i) => i) }],
|
|
22612
|
+
nodes,
|
|
22613
|
+
meshes,
|
|
22614
|
+
accessors,
|
|
22615
|
+
bufferViews,
|
|
22616
|
+
materials,
|
|
22617
|
+
buffers: [{ byteLength: totalBytes }]
|
|
22618
|
+
};
|
|
22619
|
+
if (samplers.length > 0) json.samplers = samplers;
|
|
22620
|
+
if (textures.length > 0) json.textures = textures;
|
|
22621
|
+
if (images.length > 0) json.images = images;
|
|
22622
|
+
return { json, binary };
|
|
22623
|
+
}
|
|
22624
|
+
function fingerprint2(bytes) {
|
|
22625
|
+
let h = 2166136261 >>> 0;
|
|
22626
|
+
const step = Math.max(1, Math.floor(bytes.byteLength / 256));
|
|
22627
|
+
for (let i = 0; i < bytes.byteLength; i += step) {
|
|
22628
|
+
h ^= bytes[i];
|
|
22629
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
22630
|
+
}
|
|
22631
|
+
return h ^ bytes.byteLength;
|
|
22632
|
+
}
|
|
22633
|
+
function buildGltfJson(objects) {
|
|
22634
|
+
const { json, binary } = buildGltf(objects);
|
|
22635
|
+
let base64;
|
|
22636
|
+
if (typeof Buffer !== "undefined") {
|
|
22637
|
+
base64 = Buffer.from(binary).toString("base64");
|
|
22638
|
+
} else {
|
|
22639
|
+
let binaryStr = "";
|
|
22640
|
+
for (let i = 0; i < binary.length; i++) binaryStr += String.fromCharCode(binary[i]);
|
|
22641
|
+
base64 = btoa(binaryStr);
|
|
22642
|
+
}
|
|
22643
|
+
json.buffers[0].uri = `data:application/octet-stream;base64,${base64}`;
|
|
22644
|
+
return JSON.stringify(json);
|
|
22645
|
+
}
|
|
22646
|
+
function buildGlb(objects) {
|
|
22647
|
+
const { json, binary } = buildGltf(objects);
|
|
22648
|
+
const jsonStr = JSON.stringify(json);
|
|
22649
|
+
const jsonBytes = utf8Encode(jsonStr);
|
|
22650
|
+
const jsonPadded = align4(jsonBytes.byteLength);
|
|
22651
|
+
const jsonChunk = new Uint8Array(jsonPadded);
|
|
22652
|
+
jsonChunk.set(jsonBytes);
|
|
22653
|
+
jsonChunk.fill(32, jsonBytes.byteLength);
|
|
22654
|
+
const binPadded = align4(binary.byteLength);
|
|
22655
|
+
const binChunk = new Uint8Array(binPadded);
|
|
22656
|
+
binChunk.set(binary);
|
|
22657
|
+
const GLB_HEADER = 12;
|
|
22658
|
+
const CHUNK_HEADER = 8;
|
|
22659
|
+
const totalLength = GLB_HEADER + CHUNK_HEADER + jsonChunk.byteLength + CHUNK_HEADER + binChunk.byteLength;
|
|
22660
|
+
const out = new ArrayBuffer(totalLength);
|
|
22661
|
+
const view = new DataView(out);
|
|
22662
|
+
const bytes = new Uint8Array(out);
|
|
22663
|
+
view.setUint32(0, 1179937895, true);
|
|
22664
|
+
view.setUint32(4, 2, true);
|
|
22665
|
+
view.setUint32(8, totalLength, true);
|
|
22666
|
+
let offset = GLB_HEADER;
|
|
22667
|
+
view.setUint32(offset, jsonChunk.byteLength, true);
|
|
22668
|
+
view.setUint32(offset + 4, 1313821514, true);
|
|
22669
|
+
bytes.set(jsonChunk, offset + CHUNK_HEADER);
|
|
22670
|
+
offset += CHUNK_HEADER + jsonChunk.byteLength;
|
|
22671
|
+
view.setUint32(offset, binChunk.byteLength, true);
|
|
22672
|
+
view.setUint32(offset + 4, 5130562, true);
|
|
22673
|
+
bytes.set(binChunk, offset + CHUNK_HEADER);
|
|
22674
|
+
return out;
|
|
22675
|
+
}
|
|
22676
|
+
function utf8Encode(str) {
|
|
22677
|
+
if (typeof TextEncoder !== "undefined") {
|
|
22678
|
+
return new TextEncoder().encode(str);
|
|
22679
|
+
}
|
|
22680
|
+
return new Uint8Array(Buffer.from(str, "utf-8"));
|
|
22681
|
+
}
|
|
22682
|
+
|
|
22287
22683
|
// cli/forge-mjcf.ts
|
|
22288
22684
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
|
|
22289
22685
|
import { dirname as dirname6, extname as extname7, resolve as resolve23 } from "path";
|
|
@@ -27456,6 +27852,27 @@ var INSTALL_TARGETS = {
|
|
|
27456
27852
|
};
|
|
27457
27853
|
var ALL_INSTALL_TARGETS = Object.keys(INSTALL_TARGETS);
|
|
27458
27854
|
var DEFAULT_INSTALL_TARGET = "agents";
|
|
27855
|
+
var RETIRED_COMPANION_SKILL_NAMES = [
|
|
27856
|
+
"forgecad-3d-reconstruction",
|
|
27857
|
+
"forgecad-assembly-contract",
|
|
27858
|
+
"forgecad-benchmark-reconstruction",
|
|
27859
|
+
"forgecad-blockout",
|
|
27860
|
+
"forgecad-blockout-model",
|
|
27861
|
+
"forgecad-component-model",
|
|
27862
|
+
"forgecad-high-level-spec",
|
|
27863
|
+
"forgecad-image-replicator",
|
|
27864
|
+
"forgecad-lld",
|
|
27865
|
+
"forgecad-make-a-model",
|
|
27866
|
+
"forgecad-model-grader",
|
|
27867
|
+
"forgecad-mujoco-verify",
|
|
27868
|
+
"forgecad-prepare-prompt",
|
|
27869
|
+
"forgecad-project",
|
|
27870
|
+
"forgecad-reconstruction-benchmark",
|
|
27871
|
+
"forgecad-render-inspect",
|
|
27872
|
+
"forgecad-spec-by-walking-through-it",
|
|
27873
|
+
"forgecad-visual-prompt",
|
|
27874
|
+
"forgecad-visual-spec"
|
|
27875
|
+
];
|
|
27459
27876
|
function installUsage() {
|
|
27460
27877
|
return [
|
|
27461
27878
|
"Usage: forgecad skill install [--target agents|claude|codex|opencode|all] [--core-only]",
|
|
@@ -27555,6 +27972,9 @@ If you are running from a source checkout, run: npm run build:skill:forgecad`
|
|
|
27555
27972
|
);
|
|
27556
27973
|
}
|
|
27557
27974
|
mkdirSync6(targetRoot, { recursive: true });
|
|
27975
|
+
for (const name of RETIRED_COMPANION_SKILL_NAMES) {
|
|
27976
|
+
rmSync3(join9(targetRoot, name), { recursive: true, force: true });
|
|
27977
|
+
}
|
|
27558
27978
|
const installed = [];
|
|
27559
27979
|
for (const entry of readdirSync6(srcLibrary, { withFileTypes: true })) {
|
|
27560
27980
|
if (!entry.isDirectory()) continue;
|
|
@@ -28386,15 +28806,27 @@ async function fetchRemoteFiles(projectId) {
|
|
|
28386
28806
|
const entries = await res.json();
|
|
28387
28807
|
return remoteFilesFromInit(entries);
|
|
28388
28808
|
}
|
|
28809
|
+
var SAVE_BODY_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
28810
|
+
var formatMiB = (bytes) => `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
28389
28811
|
async function saveRemoteFile(projectId, filename, content) {
|
|
28812
|
+
const body = JSON.stringify({ filename, content });
|
|
28813
|
+
const bodyBytes = Buffer.byteLength(body, "utf-8");
|
|
28814
|
+
if (bodyBytes > SAVE_BODY_LIMIT_BYTES) {
|
|
28815
|
+
const contentBytes = Buffer.byteLength(content, "utf-8");
|
|
28816
|
+
throw new Error(
|
|
28817
|
+
`Cannot push "${filename}": its upload is ${formatMiB(bodyBytes)} (file content ${formatMiB(contentBytes)}), over the server's ${formatMiB(SAVE_BODY_LIMIT_BYTES)} per-file limit.
|
|
28818
|
+
Each file is uploaded in its own request, so this is a single oversized file, not the total push size.
|
|
28819
|
+
Fixes: split the file into smaller pieces, trim generated/embedded data (e.g. inline images in SVGs), or keep it out of the project (add it to .gitignore-style skips) if it doesn't need to live on the server.`
|
|
28820
|
+
);
|
|
28821
|
+
}
|
|
28390
28822
|
const res = await authenticatedFetch(`/api/projects/${projectId}/save`, {
|
|
28391
28823
|
method: "POST",
|
|
28392
28824
|
headers: { "Content-Type": "application/json" },
|
|
28393
|
-
body
|
|
28825
|
+
body
|
|
28394
28826
|
});
|
|
28395
28827
|
if (!res.ok) {
|
|
28396
|
-
const
|
|
28397
|
-
throw new Error(
|
|
28828
|
+
const body2 = await res.json().catch(() => ({}));
|
|
28829
|
+
throw new Error(body2.error ?? `Failed to save ${filename} (${res.status})`);
|
|
28398
28830
|
}
|
|
28399
28831
|
}
|
|
28400
28832
|
async function deleteRemoteFile(projectId, filename) {
|
|
@@ -37876,6 +38308,135 @@ var PARAM_OPTIONS = [
|
|
|
37876
38308
|
var PARAM_OVERRIDE_OPTIONS = PARAM_OPTIONS.filter(
|
|
37877
38309
|
(option) => option.name === "--param" || option.name === "-p"
|
|
37878
38310
|
);
|
|
38311
|
+
function parseGltfExportArgs(format, argv) {
|
|
38312
|
+
const inputPaths = [];
|
|
38313
|
+
let outputPath;
|
|
38314
|
+
let quality;
|
|
38315
|
+
let backend;
|
|
38316
|
+
const paramOverrides = {};
|
|
38317
|
+
const jointOverrides = {};
|
|
38318
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
38319
|
+
const arg = argv[i];
|
|
38320
|
+
if (arg === "--output" || arg === "-o") {
|
|
38321
|
+
outputPath = argv[i + 1];
|
|
38322
|
+
if (!outputPath) throw new Error("--output requires a file path");
|
|
38323
|
+
i += 1;
|
|
38324
|
+
continue;
|
|
38325
|
+
}
|
|
38326
|
+
if (arg === "--quality" || arg === "-q") {
|
|
38327
|
+
const val = argv[i + 1];
|
|
38328
|
+
if (val !== "default" && val !== "live" && val !== "high") {
|
|
38329
|
+
throw new Error("--quality must be default, live, or high");
|
|
38330
|
+
}
|
|
38331
|
+
quality = val;
|
|
38332
|
+
i += 1;
|
|
38333
|
+
continue;
|
|
38334
|
+
}
|
|
38335
|
+
if (arg === "--backend") {
|
|
38336
|
+
const val = argv[i + 1];
|
|
38337
|
+
if (val !== "manifold" && val !== "occt" && val !== "truck" && val !== "sdf") {
|
|
38338
|
+
throw new Error("--backend must be manifold, occt, truck, or sdf");
|
|
38339
|
+
}
|
|
38340
|
+
backend = val;
|
|
38341
|
+
i += 1;
|
|
38342
|
+
continue;
|
|
38343
|
+
}
|
|
38344
|
+
if (arg === "--joint") {
|
|
38345
|
+
const value = argv[i + 1];
|
|
38346
|
+
if (!value || value.startsWith("--")) throw new Error("--joint requires JointName=Value");
|
|
38347
|
+
addCliJointOverride(value, jointOverrides);
|
|
38348
|
+
i += 1;
|
|
38349
|
+
continue;
|
|
38350
|
+
}
|
|
38351
|
+
if (arg === "--param" || arg === "-p") {
|
|
38352
|
+
const value = argv[i + 1];
|
|
38353
|
+
if (!value || value.startsWith("--")) throw new Error(`${arg} requires Key=Value`);
|
|
38354
|
+
addCliParamOverride(value, paramOverrides);
|
|
38355
|
+
i += 1;
|
|
38356
|
+
continue;
|
|
38357
|
+
}
|
|
38358
|
+
if (arg.startsWith("--")) {
|
|
38359
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
38360
|
+
}
|
|
38361
|
+
inputPaths.push(arg);
|
|
38362
|
+
}
|
|
38363
|
+
requireInputPaths(
|
|
38364
|
+
inputPaths,
|
|
38365
|
+
`Usage: forgecad export ${format} <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--output path] [--quality default|live|high] [--backend manifold|occt|truck|sdf] [--param Key=Value] [--joint JointName=Value]`
|
|
38366
|
+
);
|
|
38367
|
+
requireRenderableInputPaths(inputPaths);
|
|
38368
|
+
requireSingleInputForOutputPath(inputPaths, outputPath);
|
|
38369
|
+
return { inputPaths, outputPath, quality, backend, paramOverrides, jointOverrides };
|
|
38370
|
+
}
|
|
38371
|
+
function defaultGltfOutputPath(scriptPath, sourcePath, format) {
|
|
38372
|
+
const abs = resolvePath(scriptPath);
|
|
38373
|
+
const base = abs.slice(0, abs.length - extname15(abs).length);
|
|
38374
|
+
const stem = base.endsWith(".forge") ? base.slice(0, -6) : base;
|
|
38375
|
+
const target = `${stem}.${format}`;
|
|
38376
|
+
return resolvePath(target) === resolvePath(sourcePath) ? `${stem}.forgecad.${format}` : target;
|
|
38377
|
+
}
|
|
38378
|
+
function extractGltfMeshObjects(result) {
|
|
38379
|
+
return result.objects.filter((obj) => obj.shape).map((obj) => ({
|
|
38380
|
+
name: obj.name,
|
|
38381
|
+
shape: obj.shape,
|
|
38382
|
+
color: obj.color
|
|
38383
|
+
}));
|
|
38384
|
+
}
|
|
38385
|
+
async function exportGltfInput(format, scriptPath, outputPath, quality, backend, paramOverrides, jointOverrides) {
|
|
38386
|
+
const input = loadCliScriptInput(scriptPath);
|
|
38387
|
+
await initCliBackend(resolveCliBackend(backend, input) ?? CLI_DEFAULT_BACKEND);
|
|
38388
|
+
const qualityPreset = quality && quality !== "default" ? quality : void 0;
|
|
38389
|
+
setParamOverrides(paramOverrides);
|
|
38390
|
+
const runResult = runScript(input.code, input.fileName, input.allFiles, {
|
|
38391
|
+
...qualityPreset ? { quality: qualityPreset } : {},
|
|
38392
|
+
readBinaryFile: input.readBinaryFile,
|
|
38393
|
+
assemblyState: jointOverrides
|
|
38394
|
+
});
|
|
38395
|
+
const result = applyCliJointOverrides(runResult, jointOverrides);
|
|
38396
|
+
if (result.error) {
|
|
38397
|
+
throw new Error(`ERROR: ${result.error}`);
|
|
38398
|
+
}
|
|
38399
|
+
const meshObjects = extractGltfMeshObjects(result);
|
|
38400
|
+
if (meshObjects.length === 0) {
|
|
38401
|
+
throw new Error("No 3D shapes found in the script output.");
|
|
38402
|
+
}
|
|
38403
|
+
if (outputPath && resolvePath(outputPath) === input.sourcePath) {
|
|
38404
|
+
throw new Error("Output path would overwrite the input file. Pass a different --output path.");
|
|
38405
|
+
}
|
|
38406
|
+
const target = resolvePath(outputPath ?? defaultGltfOutputPath(scriptPath, input.sourcePath, format));
|
|
38407
|
+
if (format === "glb") {
|
|
38408
|
+
const buffer = buildGlb(meshObjects);
|
|
38409
|
+
writeFileSync22(target, Buffer.from(buffer));
|
|
38410
|
+
} else {
|
|
38411
|
+
const json = buildGltfJson(meshObjects);
|
|
38412
|
+
writeFileSync22(target, json, "utf-8");
|
|
38413
|
+
}
|
|
38414
|
+
const stats = meshObjects.map((obj) => {
|
|
38415
|
+
const mesh = obj.shape.getMesh();
|
|
38416
|
+
const textured = mesh.numProp === 8 || Boolean(obj.shape.materialProps?.texture);
|
|
38417
|
+
return ` ${obj.name}: ${mesh.numTri.toLocaleString()} triangles${textured ? " (textured)" : ""}`;
|
|
38418
|
+
});
|
|
38419
|
+
console.log(`\u2713 Exported ${format.toUpperCase()} to ${target}`);
|
|
38420
|
+
console.log(` ${meshObjects.length} object(s)${quality ? ` [quality: ${quality}]` : ""}`);
|
|
38421
|
+
stats.forEach((line) => console.log(line));
|
|
38422
|
+
}
|
|
38423
|
+
async function runGltfExportCli(format, argv) {
|
|
38424
|
+
const { inputPaths, outputPath, quality, backend, paramOverrides, jointOverrides } = parseGltfExportArgs(format, argv);
|
|
38425
|
+
requireExistingInputPaths(inputPaths);
|
|
38426
|
+
let failures = 0;
|
|
38427
|
+
for (const [index, scriptPath] of inputPaths.entries()) {
|
|
38428
|
+
printBatchHeader(scriptPath, index, inputPaths.length);
|
|
38429
|
+
try {
|
|
38430
|
+
await exportGltfInput(format, scriptPath, outputPath, quality, backend, paramOverrides, jointOverrides);
|
|
38431
|
+
} catch (error) {
|
|
38432
|
+
failures += 1;
|
|
38433
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
38434
|
+
}
|
|
38435
|
+
}
|
|
38436
|
+
if (failures > 0) {
|
|
38437
|
+
process.exit(1);
|
|
38438
|
+
}
|
|
38439
|
+
}
|
|
37879
38440
|
var RENDER_OPTIONS = [
|
|
37880
38441
|
...PARAM_OPTIONS,
|
|
37881
38442
|
{
|
|
@@ -38097,6 +38658,19 @@ var INSPECT_EVIDENCE_OPTIONS = [
|
|
|
38097
38658
|
argument: "required",
|
|
38098
38659
|
valueLabel: "<n>"
|
|
38099
38660
|
},
|
|
38661
|
+
{
|
|
38662
|
+
name: "--bands",
|
|
38663
|
+
description: "Quantize the heatmap fill into N discrete contour bands (0 = smooth ramp)",
|
|
38664
|
+
argument: "required",
|
|
38665
|
+
valueLabel: "<n>"
|
|
38666
|
+
},
|
|
38667
|
+
{ name: "--iso", description: "Draw anti-aliased isolines on the heatmap surface" },
|
|
38668
|
+
{
|
|
38669
|
+
name: "--iso-width",
|
|
38670
|
+
description: "Isoline width in screen pixels (default 1.5)",
|
|
38671
|
+
argument: "required",
|
|
38672
|
+
valueLabel: "<px>"
|
|
38673
|
+
},
|
|
38100
38674
|
{
|
|
38101
38675
|
name: "--with",
|
|
38102
38676
|
description: "Reference model for comparison evidence",
|
|
@@ -38209,6 +38783,11 @@ function inspectEvidenceOptions(definition) {
|
|
|
38209
38783
|
if (definition.channel === "roughness") {
|
|
38210
38784
|
names.add("--roughness-samples");
|
|
38211
38785
|
}
|
|
38786
|
+
if (definition.channel === "thickness" || definition.channel === "roughness") {
|
|
38787
|
+
for (const name of ["--bands", "--iso", "--iso-width"]) {
|
|
38788
|
+
names.add(name);
|
|
38789
|
+
}
|
|
38790
|
+
}
|
|
38212
38791
|
if (definition.channel === "collisions") {
|
|
38213
38792
|
names.add("--joint-sweep");
|
|
38214
38793
|
}
|
|
@@ -39701,6 +40280,78 @@ var commands = [
|
|
|
39701
40280
|
},
|
|
39702
40281
|
run: (args) => runMeshExportCli("stl", args)
|
|
39703
40282
|
},
|
|
40283
|
+
{
|
|
40284
|
+
group: "Export",
|
|
40285
|
+
path: ["export", "glb"],
|
|
40286
|
+
summary: "Export a Forge script to binary glTF (.glb) with PBR materials and embedded textures.",
|
|
40287
|
+
usage: [
|
|
40288
|
+
"forgecad export glb <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--output path] [--quality default|live|high] [--backend manifold|occt|truck|sdf] [--param Key=Value] [--joint JointName=Value]"
|
|
40289
|
+
],
|
|
40290
|
+
examples: [
|
|
40291
|
+
"forgecad export glb examples/products/cup.forge.js",
|
|
40292
|
+
"forgecad export glb examples/products/cup.forge.js --output out/cup.glb",
|
|
40293
|
+
"forgecad export glb examples/products/cup.forge.js examples/api/boolean-operations.forge.js",
|
|
40294
|
+
"forgecad export glb examples/products/cup.forge.js --quality high"
|
|
40295
|
+
],
|
|
40296
|
+
completion: {
|
|
40297
|
+
options: [
|
|
40298
|
+
...PARAM_OPTIONS,
|
|
40299
|
+
{ name: "--output", description: "Output GLB path", argument: "required", valueLabel: "<path>", valueKind: "path" },
|
|
40300
|
+
{
|
|
40301
|
+
name: "--quality",
|
|
40302
|
+
description: "Forge quality preset",
|
|
40303
|
+
argument: "required",
|
|
40304
|
+
valueLabel: "<default|live|high>",
|
|
40305
|
+
values: QUALITY_VALUES
|
|
40306
|
+
},
|
|
40307
|
+
{
|
|
40308
|
+
name: "--backend",
|
|
40309
|
+
description: "Geometry backend (default: manifold)",
|
|
40310
|
+
argument: "required",
|
|
40311
|
+
valueLabel: "<manifold|occt|truck|sdf>",
|
|
40312
|
+
values: BACKEND_VALUES
|
|
40313
|
+
}
|
|
40314
|
+
],
|
|
40315
|
+
positionals: [{ description: "Forge script or CAD asset", valueKind: "renderable", repeatable: true }]
|
|
40316
|
+
},
|
|
40317
|
+
run: (args) => runGltfExportCli("glb", args)
|
|
40318
|
+
},
|
|
40319
|
+
{
|
|
40320
|
+
group: "Export",
|
|
40321
|
+
path: ["export", "gltf"],
|
|
40322
|
+
summary: "Export a Forge script to text glTF (.gltf) with PBR materials and embedded textures.",
|
|
40323
|
+
usage: [
|
|
40324
|
+
"forgecad export gltf <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--output path] [--quality default|live|high] [--backend manifold|occt|truck|sdf] [--param Key=Value] [--joint JointName=Value]"
|
|
40325
|
+
],
|
|
40326
|
+
examples: [
|
|
40327
|
+
"forgecad export gltf examples/products/cup.forge.js",
|
|
40328
|
+
"forgecad export gltf examples/products/cup.forge.js --output out/cup.gltf",
|
|
40329
|
+
"forgecad export gltf examples/products/cup.forge.js examples/api/boolean-operations.forge.js",
|
|
40330
|
+
"forgecad export gltf examples/products/cup.forge.js --quality high"
|
|
40331
|
+
],
|
|
40332
|
+
completion: {
|
|
40333
|
+
options: [
|
|
40334
|
+
...PARAM_OPTIONS,
|
|
40335
|
+
{ name: "--output", description: "Output glTF path", argument: "required", valueLabel: "<path>", valueKind: "path" },
|
|
40336
|
+
{
|
|
40337
|
+
name: "--quality",
|
|
40338
|
+
description: "Forge quality preset",
|
|
40339
|
+
argument: "required",
|
|
40340
|
+
valueLabel: "<default|live|high>",
|
|
40341
|
+
values: QUALITY_VALUES
|
|
40342
|
+
},
|
|
40343
|
+
{
|
|
40344
|
+
name: "--backend",
|
|
40345
|
+
description: "Geometry backend (default: manifold)",
|
|
40346
|
+
argument: "required",
|
|
40347
|
+
valueLabel: "<manifold|occt|truck|sdf>",
|
|
40348
|
+
values: BACKEND_VALUES
|
|
40349
|
+
}
|
|
40350
|
+
],
|
|
40351
|
+
positionals: [{ description: "Forge script or CAD asset", valueKind: "renderable", repeatable: true }]
|
|
40352
|
+
},
|
|
40353
|
+
run: (args) => runGltfExportCli("gltf", args)
|
|
40354
|
+
},
|
|
39704
40355
|
{
|
|
39705
40356
|
group: "Export",
|
|
39706
40357
|
path: ["export", "gcode"],
|
|
@@ -41194,7 +41845,7 @@ var commands = [
|
|
|
41194
41845
|
{ name: "--update", description: "Regenerate compiler snapshots" }
|
|
41195
41846
|
]
|
|
41196
41847
|
},
|
|
41197
|
-
run: async (args) => (await import("./check-compiler-
|
|
41848
|
+
run: async (args) => (await import("./check-compiler-4RPB6SB5.js")).runCheckCompilerCli(args)
|
|
41198
41849
|
},
|
|
41199
41850
|
{
|
|
41200
41851
|
group: "Checks",
|
|
@@ -41217,7 +41868,7 @@ var commands = [
|
|
|
41217
41868
|
{ name: "--update", description: "Regenerate query-propagation snapshots" }
|
|
41218
41869
|
]
|
|
41219
41870
|
},
|
|
41220
|
-
run: async (args) => (await import("./check-query-propagation-
|
|
41871
|
+
run: async (args) => (await import("./check-query-propagation-KN3DFQTX.js")).runCheckQueryPropagationCli(args)
|
|
41221
41872
|
},
|
|
41222
41873
|
{
|
|
41223
41874
|
group: "Checks",
|