forgecad 0.9.16 → 0.10.0
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-CXvls4-J.js → AdminPage-DwYHz72L.js} +1 -1
- package/dist/assets/{BenchmarkPage-B27zk8xL.js → BenchmarkPage-a9_f-1US.js} +1 -1
- package/dist/assets/{BlogPage-CMAVvgQL.js → BlogPage-DodHpvmf.js} +1 -1
- package/dist/assets/{DocsPage-knf4I4h7.js → DocsPage-B5LePEuj.js} +8 -858
- package/dist/assets/EditorApp-QXsAISLR.js +16307 -0
- package/dist/assets/{EmbedViewer-D7ZGlFjx.js → EmbedViewer-DdEHGUMU.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-CnevhTE8.js → LandingPageProofDriven-yhhOodbf.js} +1 -1
- package/dist/assets/{LegalPage-BPTUmqeg.js → LegalPage-5RbKRGYK.js} +1 -1
- package/dist/assets/{PricingPage-B0D4goG_.js → PricingPage-E3Rma7aV.js} +1 -1
- package/dist/assets/{SettingsPage-CFF-UgjI.js → SettingsPage-BJZcM97j.js} +1 -1
- package/dist/assets/{app-T0pDcSX4.js → app-DSYrDg0V.js} +733 -205
- package/dist/assets/cli/{render-C5pcIISc.js → render-ZMHR9HkV.js} +19 -46
- package/dist/assets/{constructionHistoryWorker-Ba2Hm58b.js → constructionHistoryWorker-AwMMWSxg.js} +1103 -349
- package/dist/assets/{evalWorker-vkx310U2.js → evalWorker-DbNs7Dkp.js} +3798 -1622
- package/dist/assets/{inspectWorker-BuTJDVX6.js → inspectWorker-CZsCFtQT.js} +1163 -409
- package/dist/assets/{jointPose-B_Cgedn9.js → jointPose-DO6mnXn_.js} +1 -1
- package/dist/assets/{manifold-BWgsjmAM.js → manifold-BGlQBBH9.js} +1 -1
- package/dist/assets/{manifold-rZexZI0G.js → manifold-BU-tJwQh.js} +1 -1
- package/dist/assets/{manifold-D6IFSkhH.js → manifold-fy2MV7K1.js} +2 -2
- package/dist/assets/{reportWorker-0AGij1Ru.js → reportWorker-DO6hcQbh.js} +7155 -2437
- package/dist/assets/{scalar-sampling-budget-J5cuzxT1.js → scalar-sampling-budget-o90NSNmF.js} +3940 -1742
- package/dist/assets/{scanProxyWorker-Vl4Wxa1y.js → scanProxyWorker-2GtDLk-R.js} +1 -1
- package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +3 -3
- package/dist/docs-raw/AI/usage.md +1 -1
- package/dist/docs-raw/CLI.md +63 -241
- package/dist/docs-raw/README.md +6 -0
- package/dist/docs-raw/component-model.md +17 -150
- package/dist/docs-raw/generated/assembly.md +139 -598
- package/dist/docs-raw/generated/concepts.md +245 -3501
- package/dist/docs-raw/generated/core.md +277 -1251
- package/dist/docs-raw/generated/curves.md +387 -1608
- package/dist/docs-raw/generated/legacy.md +162 -0
- package/dist/docs-raw/generated/lib.md +227 -85
- package/dist/docs-raw/generated/output.md +38 -73
- package/dist/docs-raw/generated/runtime-names.md +23 -23
- package/dist/docs-raw/generated/sdf.md +68 -284
- package/dist/docs-raw/generated/sheet-metal.md +68 -335
- package/dist/docs-raw/generated/sketch.md +240 -1161
- package/dist/docs-raw/generated/viewport.md +75 -316
- package/dist/docs-raw/generated/wood.md +21 -49
- package/dist/docs-raw/guides/coordinate-system.md +4 -42
- package/dist/docs-raw/guides/inspection-bundles.md +44 -442
- package/dist/docs-raw/guides/joint-design.md +18 -79
- package/dist/docs-raw/guides/positioning.md +21 -143
- package/dist/docs-raw/guides/scene-presentation.md +89 -0
- package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
- package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
- package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
- package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
- package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
- package/dist/docs-raw/skills/forgecad-lld.md +19 -113
- package/dist/docs-raw/skills/forgecad-make-a-model.md +112 -532
- package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
- package/dist/docs-raw/skills/forgecad-project.md +13 -131
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
- package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
- package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
- package/dist/docs-raw/skills/forgecad.md +19 -18
- package/dist/docs-raw/skills/index.md +2 -0
- package/dist/docs-raw/welcome.md +2 -2
- package/dist/index.html +1 -1
- package/dist/llms.txt +1 -2
- package/dist/sitemap.xml +13 -13
- package/dist-cli/{check-compiler-SYQ2PWOB.js → check-compiler-JTVBITCR.js} +1 -1
- package/dist-cli/{check-query-propagation-HIAGV62W.js → check-query-propagation-3FFLSMVN.js} +1 -1
- package/dist-cli/{chunk-SPZE3DUY.js → chunk-OAN5T4XD.js} +4412 -2212
- package/dist-cli/forgecad.js +507 -179
- package/dist-skill/CONTEXT.md +2172 -8377
- package/dist-skill/SKILL.md +15 -15
- package/dist-skill/docs/API/core/concepts.md +27 -157
- package/dist-skill/docs/CLI.md +63 -241
- package/dist-skill/docs/generated/assembly.md +138 -549
- package/dist-skill/docs/generated/core.md +277 -1251
- package/dist-skill/docs/generated/curves.md +387 -1609
- package/dist-skill/docs/generated/lib.md +227 -85
- package/dist-skill/docs/generated/output.md +38 -73
- package/dist-skill/docs/generated/runtime-names.md +16 -21
- package/dist-skill/docs/generated/sdf.md +68 -284
- package/dist-skill/docs/generated/sheet-metal.md +68 -335
- package/dist-skill/docs/generated/sketch.md +240 -1160
- package/dist-skill/docs/generated/viewport.md +75 -223
- package/dist-skill/docs/generated/wood.md +21 -49
- package/dist-skill/docs/guides/coordinate-system.md +4 -42
- package/dist-skill/docs/guides/inspection-bundles.md +44 -442
- package/dist-skill/docs/guides/joint-design.md +18 -79
- package/dist-skill/docs/guides/positioning.md +21 -143
- package/dist-skill/docs/guides/scene-presentation.md +89 -0
- package/dist-skill/docs/guides/surface-members.md +26 -0
- package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
- package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
- package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
- package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +110 -532
- package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
- package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
- package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
- package/dist-skill/library/forgecad-project/SKILL.md +13 -133
- package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
- package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
- package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
- package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
- package/dist-skill/website/skills/forgecad-component-model.md +53 -0
- package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
- package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
- package/dist-skill/website/skills/forgecad-lld.md +41 -0
- package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
- package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
- package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
- package/dist-skill/website/skills/forgecad-project.md +26 -0
- package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
- package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
- package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
- package/dist-skill/website/skills/forgecad.md +122 -0
- package/dist-skill/website/skills/index.md +26 -0
- package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
- package/examples/api/conformal-product-ribbon.forge.js +1 -1
- package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
- package/examples/api/extrude-options.forge.js +4 -2
- package/examples/api/field-loft-drive-tip.forge.js +40 -0
- package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
- package/examples/api/highlight-debug.forge.js +10 -10
- package/examples/api/mesh-import-slats.forge.js +1 -1
- package/examples/api/real-product-curves.forge.js +1 -1
- package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
- package/examples/api/sdf-shapes.forge.js +2 -5
- package/examples/api/sketch-rounding-strategies.forge.js +6 -6
- package/examples/api/surface-member-bottle-cage.forge.js +3 -3
- package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
- package/examples/api/surface-member-razor-inlay.forge.js +1 -1
- package/examples/api/variable-sweep-test.forge.js +3 -3
- package/examples/mechanical/airplane-propeller.forge.js +74 -39
- package/examples/nurbs-surface.forge.js +1 -1
- package/examples/products/iphone.forge.js +1 -1
- package/package.json +1 -1
- package/dist/assets/EditorApp-BHMQlJ-D.js +0 -14686
- package/dist/docs-raw/guides/geometry-conventions.md +0 -52
- package/dist/docs-raw/guides/modeling-recipes.md +0 -78
- package/dist-skill/docs/guides/geometry-conventions.md +0 -52
- package/dist-skill/docs/guides/modeling-recipes.md +0 -78
- package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
package/dist-cli/forgecad.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CLI_DEFAULT_BACKEND,
|
|
5
5
|
COMPILER_REGRESSION_CORPUS,
|
|
6
6
|
FILLET_EDGE_WORKFLOW_CODE,
|
|
7
|
+
Loft,
|
|
7
8
|
MathUtils,
|
|
8
9
|
OCCTUnsupportedError,
|
|
9
10
|
Rectangle2D,
|
|
@@ -33,7 +34,6 @@ import {
|
|
|
33
34
|
chamferTrackedEdge,
|
|
34
35
|
circle2d,
|
|
35
36
|
collectProjectFiles,
|
|
36
|
-
composeChain,
|
|
37
37
|
constrainedSketch,
|
|
38
38
|
cylinder,
|
|
39
39
|
describeFaceQueryRef,
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
getLastSolveTrail,
|
|
64
64
|
getShapeCompilePlan,
|
|
65
65
|
getShapeDimensions,
|
|
66
|
+
getShapeLineageToken,
|
|
66
67
|
getShapePrimaryQueryOwner,
|
|
67
68
|
getShapeQueryOwners,
|
|
68
69
|
getShapeRuntimeBackend,
|
|
@@ -126,7 +127,7 @@ import {
|
|
|
126
127
|
union2d,
|
|
127
128
|
updateConstraintValue,
|
|
128
129
|
wrapOCCTShapeBackend
|
|
129
|
-
} from "./chunk-
|
|
130
|
+
} from "./chunk-OAN5T4XD.js";
|
|
130
131
|
|
|
131
132
|
// cli/forgecad.ts
|
|
132
133
|
import { Command, Flags, Help, flush as flushOclif, handle as handleOclif, run as runOclif } from "@oclif/core";
|
|
@@ -234,6 +235,18 @@ function checkBooleanErrors() {
|
|
|
234
235
|
/union2d\(\) argument 2: expected a Sketch, got undefined/
|
|
235
236
|
);
|
|
236
237
|
}
|
|
238
|
+
function checkLoftFieldApiContract() {
|
|
239
|
+
const fieldLoft = Loft.field([circle2d(4, 32), roundedRect(8, 2, 0.5)], [0, 8], { edgeLength: 0.8, maxTriangles: 5e3 });
|
|
240
|
+
assert(fieldLoft.volume() > 0, "Loft.field() should build a positive-volume field loft");
|
|
241
|
+
assert.throws(
|
|
242
|
+
() => Loft.field([circle2d(4, 16), roundedRect(8, 2, 0.5)], [0, 8], { maxTriangles: 0.5 }),
|
|
243
|
+
/Loft\.field\(\) maxTriangles must be a positive integer/
|
|
244
|
+
);
|
|
245
|
+
assert.throws(
|
|
246
|
+
() => Loft.field([circle2d(4, 16), roundedRect(8, 2, 0.5)], [0, 8], { edgeLength: 1.2, maxTriangles: 1 }).volume(),
|
|
247
|
+
/above maxTriangles=1/
|
|
248
|
+
);
|
|
249
|
+
}
|
|
237
250
|
function checkEdgeFinishSubsetErrors() {
|
|
238
251
|
const base = Rectangle2D.fromDimensions(-24, -16, 48, 32).extrude(18);
|
|
239
252
|
const once2 = filletTrackedEdge(base, base.edge("vert-br"), 4, [-1, -1]);
|
|
@@ -340,7 +353,7 @@ function checkSculptApiContracts() {
|
|
|
340
353
|
"Sculpt.tube() should return an SdfShape"
|
|
341
354
|
);
|
|
342
355
|
assert(
|
|
343
|
-
Sculpt.
|
|
356
|
+
Sculpt.tube(
|
|
344
357
|
[
|
|
345
358
|
[0, 0, 0, 2],
|
|
346
359
|
[12, 0, 0, 5],
|
|
@@ -348,7 +361,7 @@ function checkSculptApiContracts() {
|
|
|
348
361
|
],
|
|
349
362
|
{ blend: 2 }
|
|
350
363
|
) instanceof SdfShape,
|
|
351
|
-
"Sculpt.
|
|
364
|
+
"Sculpt.tube() should accept [x, y, z, radius] control points"
|
|
352
365
|
);
|
|
353
366
|
assert(
|
|
354
367
|
Sculpt.curve(
|
|
@@ -361,7 +374,7 @@ function checkSculptApiContracts() {
|
|
|
361
374
|
) instanceof SdfShape,
|
|
362
375
|
"Sculpt.curve() should accept object control points and produce a smoothed variable sweep"
|
|
363
376
|
);
|
|
364
|
-
assert(Sculpt.
|
|
377
|
+
assert(Sculpt.disk(8, 1.5) instanceof SdfShape, "Sculpt.disk() should return a thin circular SDF shape");
|
|
365
378
|
assert(
|
|
366
379
|
Sculpt.blend([Sculpt.sphere(8), Sculpt.box(12, 8, 6, { radius: 2 })], { radius: 3 }).polish("ceramic") instanceof SdfShape,
|
|
367
380
|
"Sculpt.blend([...], options).polish() should stay in SDF space"
|
|
@@ -379,7 +392,7 @@ scene(Sculpt.look('gallery'));
|
|
|
379
392
|
return Sculpt
|
|
380
393
|
.blend([
|
|
381
394
|
Sculpt.curve([[0, 0, 0, 2], [8, 0, 6, 4], [16, 4, 0, 2]], { segments: 5 }),
|
|
382
|
-
Sculpt.box(10, 8, 6, { radius: 2 }).carve(Sculpt.
|
|
395
|
+
Sculpt.box(10, 8, 6, { radius: 2 }).carve(Sculpt.disk(3, 8).tilt(90, 'x'), { radius: 1.2 }).at(8, 0, 0),
|
|
383
396
|
], { radius: 3 })
|
|
384
397
|
.polish('glass');
|
|
385
398
|
`;
|
|
@@ -567,6 +580,7 @@ async function runCheckApiContractsCli() {
|
|
|
567
580
|
checkTrackedShapeInterop();
|
|
568
581
|
checkSketchBooleanForms();
|
|
569
582
|
checkBooleanErrors();
|
|
583
|
+
checkLoftFieldApiContract();
|
|
570
584
|
checkEdgeFinishSubsetErrors();
|
|
571
585
|
checkSheetMetalApiContracts();
|
|
572
586
|
checkSandboxBindings();
|
|
@@ -2085,7 +2099,7 @@ return [
|
|
|
2085
2099
|
assert2.equal(facetedManifest.objects.length, 4, "Expected all segmented shapes to export as faceted objects");
|
|
2086
2100
|
assert2.equal(facetedManifest.fallbacks.length, 4, "Expected faceted fallbacks for all segmented runtime-hint shapes");
|
|
2087
2101
|
}
|
|
2088
|
-
function checkBroadNativeEdgeFinishesStayOutOfCadQueryExactSubset() {
|
|
2102
|
+
async function checkBroadNativeEdgeFinishesStayOutOfCadQueryExactSubset() {
|
|
2089
2103
|
const files = {
|
|
2090
2104
|
"main.forge.js": `
|
|
2091
2105
|
const base = box(30, 20, 15);
|
|
@@ -2098,7 +2112,7 @@ return [
|
|
|
2098
2112
|
`
|
|
2099
2113
|
};
|
|
2100
2114
|
const previousBackend = getActiveBackend();
|
|
2101
|
-
|
|
2115
|
+
await activateBackend("truck");
|
|
2102
2116
|
let result = null;
|
|
2103
2117
|
try {
|
|
2104
2118
|
result = runScript(files["main.forge.js"], "main.forge.js", files);
|
|
@@ -2188,7 +2202,7 @@ async function runCheckBrepExportCli() {
|
|
|
2188
2202
|
checkPlaneTrimAndSplitStayExactExportable();
|
|
2189
2203
|
checkLoftAndSweepExportEndToEnd();
|
|
2190
2204
|
checkSegmentedRuntimeHintsStayOutOfExactSubset();
|
|
2191
|
-
checkBroadNativeEdgeFinishesStayOutOfCadQueryExactSubset();
|
|
2205
|
+
await checkBroadNativeEdgeFinishesStayOutOfCadQueryExactSubset();
|
|
2192
2206
|
console.log("\u2713 BREP export invariants passed");
|
|
2193
2207
|
}
|
|
2194
2208
|
runDirectCliMain(import.meta.url, "cli/check-brep-export.ts", () => runCheckBrepExportCli());
|
|
@@ -5387,6 +5401,10 @@ function applyViewportJointOverridesToResult(result, overrides) {
|
|
|
5387
5401
|
objects: result.objects.map(transformObject)
|
|
5388
5402
|
};
|
|
5389
5403
|
}
|
|
5404
|
+
function formatJointNumber(value) {
|
|
5405
|
+
const rounded = Number(value.toFixed(6));
|
|
5406
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
5407
|
+
}
|
|
5390
5408
|
|
|
5391
5409
|
// cli/joint-overrides.ts
|
|
5392
5410
|
function parseJointFlags(argv) {
|
|
@@ -5414,6 +5432,57 @@ function applyCliJointOverrides(result, overrides) {
|
|
|
5414
5432
|
return applyViewportJointOverridesToResult(result, overrides);
|
|
5415
5433
|
}
|
|
5416
5434
|
|
|
5435
|
+
// cli/joint-sweep.ts
|
|
5436
|
+
var MAX_CLI_JOINT_SWEEP_SAMPLES = 101;
|
|
5437
|
+
var NUMBER_PATTERN = String.raw`[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?`;
|
|
5438
|
+
var SWEEP_PATTERN = new RegExp(String.raw`^\s*([^=]+?)\s*=\s*(${NUMBER_PATTERN})\s*\.\.\s*(${NUMBER_PATTERN})\s*:\s*(\d+)\s*$`);
|
|
5439
|
+
function normalizeSweepNumber(value) {
|
|
5440
|
+
const rounded = Number(value.toPrecision(12));
|
|
5441
|
+
return Object.is(rounded, -0) ? 0 : rounded;
|
|
5442
|
+
}
|
|
5443
|
+
function sampleCliJointSweepValues(start, end, samples) {
|
|
5444
|
+
if (samples < 2) return [normalizeSweepNumber(start)];
|
|
5445
|
+
return Array.from({ length: samples }, (_, index) => normalizeSweepNumber(start + (end - start) * index / (samples - 1)));
|
|
5446
|
+
}
|
|
5447
|
+
function parseCliJointSweep(raw, flag = "--joint-sweep") {
|
|
5448
|
+
const match = SWEEP_PATTERN.exec(raw);
|
|
5449
|
+
if (!match) {
|
|
5450
|
+
throw new Error(`Invalid ${flag} format: "${raw}". Expected JointName=start..end:samples.`);
|
|
5451
|
+
}
|
|
5452
|
+
const joint2 = match[1].trim();
|
|
5453
|
+
const start = Number(match[2]);
|
|
5454
|
+
const end = Number(match[3]);
|
|
5455
|
+
const samples = Number(match[4]);
|
|
5456
|
+
if (!joint2) throw new Error(`${flag} joint name cannot be blank.`);
|
|
5457
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) throw new Error(`${flag} range values must be finite.`);
|
|
5458
|
+
if (start === end) throw new Error(`${flag} range must span two different values.`);
|
|
5459
|
+
if (!Number.isSafeInteger(samples) || samples < 2 || samples > MAX_CLI_JOINT_SWEEP_SAMPLES) {
|
|
5460
|
+
throw new Error(`${flag} samples must be an integer between 2 and ${MAX_CLI_JOINT_SWEEP_SAMPLES}.`);
|
|
5461
|
+
}
|
|
5462
|
+
return {
|
|
5463
|
+
raw,
|
|
5464
|
+
joint: joint2,
|
|
5465
|
+
start,
|
|
5466
|
+
end,
|
|
5467
|
+
samples,
|
|
5468
|
+
values: sampleCliJointSweepValues(start, end, samples)
|
|
5469
|
+
};
|
|
5470
|
+
}
|
|
5471
|
+
function describeCliJointSweep(sweep) {
|
|
5472
|
+
return `${sweep.joint}=${formatJointNumber(sweep.start)}..${formatJointNumber(sweep.end)}:${sweep.samples}`;
|
|
5473
|
+
}
|
|
5474
|
+
function buildCliJointSweepPoses(sweep, baseOverrides = {}) {
|
|
5475
|
+
return sweep.values.map((value, index) => ({
|
|
5476
|
+
index: index + 1,
|
|
5477
|
+
joint: sweep.joint,
|
|
5478
|
+
value,
|
|
5479
|
+
overrides: {
|
|
5480
|
+
...baseOverrides,
|
|
5481
|
+
[sweep.joint]: value
|
|
5482
|
+
}
|
|
5483
|
+
}));
|
|
5484
|
+
}
|
|
5485
|
+
|
|
5417
5486
|
// cli/render-cli-inputs.ts
|
|
5418
5487
|
import { existsSync as existsSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
|
|
5419
5488
|
import { homedir } from "os";
|
|
@@ -5539,7 +5608,7 @@ function computeMeshInertia(mesh, massKg) {
|
|
|
5539
5608
|
}
|
|
5540
5609
|
|
|
5541
5610
|
// src/studio/project-files/importGraph.ts
|
|
5542
|
-
var FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
5611
|
+
var FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
5543
5612
|
var REQUIRE_RE = /\brequire\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
5544
5613
|
var ES_IMPORT_RE = /\bfrom\s+(?:"([^"]+)"|'([^']+)')/g;
|
|
5545
5614
|
var VIRTUAL_MODULES = /* @__PURE__ */ new Set(["forgecad", "@forge/runtime", "@forgecad/runtime"]);
|
|
@@ -5556,6 +5625,9 @@ function extractImports(code) {
|
|
|
5556
5625
|
importMesh: "forgeMesh",
|
|
5557
5626
|
importStep: "forgeMesh",
|
|
5558
5627
|
importSvgSketch: "forgeSvg",
|
|
5628
|
+
"Import.mesh": "forgeMesh",
|
|
5629
|
+
"Import.step": "forgeMesh",
|
|
5630
|
+
"Import.svgSketch": "forgeSvg",
|
|
5559
5631
|
"Import.dxfSketch": "forgeDxf",
|
|
5560
5632
|
compareWith: "compareWith"
|
|
5561
5633
|
};
|
|
@@ -5563,7 +5635,9 @@ function extractImports(code) {
|
|
|
5563
5635
|
const forgeRe = new RegExp(FORGE_IMPORT_RE.source, FORGE_IMPORT_RE.flags);
|
|
5564
5636
|
while ((m = forgeRe.exec(code)) !== null) {
|
|
5565
5637
|
const path5 = m[1] ?? m[2];
|
|
5566
|
-
const fn = m[0].match(
|
|
5638
|
+
const fn = m[0].match(
|
|
5639
|
+
/\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)/
|
|
5640
|
+
)?.[1];
|
|
5567
5641
|
add2(path5, kindMap[fn] ?? "forgeMesh");
|
|
5568
5642
|
}
|
|
5569
5643
|
const reqRe = new RegExp(REQUIRE_RE.source, REQUIRE_RE.flags);
|
|
@@ -5673,7 +5747,7 @@ function replaceCliInputExtension(path5, newExt) {
|
|
|
5673
5747
|
return path5 + newExt;
|
|
5674
5748
|
}
|
|
5675
5749
|
function buildDirectCadImportScript(fileName, kind) {
|
|
5676
|
-
const importFn = kind === "step" ? "
|
|
5750
|
+
const importFn = kind === "step" ? "Import.step" : "Import.mesh";
|
|
5677
5751
|
const importOptions = kind === "mesh" && extname(fileName).toLowerCase() === ".3mf" ? ", { separateObjects: true }" : "";
|
|
5678
5752
|
const objectName = basename3(fileName);
|
|
5679
5753
|
return [
|
|
@@ -7915,6 +7989,7 @@ Options:
|
|
|
7915
7989
|
--param <Key=Value> Override a script parameter value. Repeatable.
|
|
7916
7990
|
-p <Key=Value> Shorthand for --param
|
|
7917
7991
|
--joint <JointName=Value> Override a Motion tab joint value. Repeatable.
|
|
7992
|
+
--joint-sweep <Joint=-10..10:5> Sweep one Motion tab joint for fit interference evidence.
|
|
7918
7993
|
--size <px> Image size in pixels (default: ${DEFAULT_SIZE})
|
|
7919
7994
|
--quality <default|live|high> Mesh/render quality (default: default)
|
|
7920
7995
|
--render-style <${RENDER_STYLE_LABEL}>
|
|
@@ -7992,6 +8067,7 @@ function parseInspectCli(argv, config = {}) {
|
|
|
7992
8067
|
let cutawayPlaneSource = null;
|
|
7993
8068
|
const paramOverrides = {};
|
|
7994
8069
|
const jointOverrides = {};
|
|
8070
|
+
let jointSweep = null;
|
|
7995
8071
|
const setCutawayNormal = (normal, source) => {
|
|
7996
8072
|
if (!cutaway) throw new Error(`${source} only works with cutaway inspection.`);
|
|
7997
8073
|
if (cutawayPlaneSource) {
|
|
@@ -8012,6 +8088,12 @@ function parseInspectCli(argv, config = {}) {
|
|
|
8012
8088
|
i += 1;
|
|
8013
8089
|
continue;
|
|
8014
8090
|
}
|
|
8091
|
+
if (arg === "--joint-sweep") {
|
|
8092
|
+
if (jointSweep) throw new Error("Pass --joint-sweep only once.");
|
|
8093
|
+
jointSweep = parseCliJointSweep(readValue2(argv, i, arg), arg);
|
|
8094
|
+
i += 1;
|
|
8095
|
+
continue;
|
|
8096
|
+
}
|
|
8015
8097
|
if (arg === "--output" || arg === "--out") {
|
|
8016
8098
|
flagOutputDir = readValue2(argv, i, arg);
|
|
8017
8099
|
i += 1;
|
|
@@ -8301,6 +8383,12 @@ function parseInspectCli(argv, config = {}) {
|
|
|
8301
8383
|
if (hasCompareOptions && !channels.includes("comparison")) {
|
|
8302
8384
|
throw new Error("Comparison options only work with `forgecad inspect compare overlay`.");
|
|
8303
8385
|
}
|
|
8386
|
+
if (jointSweep && !channels.includes("collisions")) {
|
|
8387
|
+
throw new Error("--joint-sweep only works with `forgecad inspect fit interference`.");
|
|
8388
|
+
}
|
|
8389
|
+
if (jointSweep && Object.prototype.hasOwnProperty.call(jointOverrides, jointSweep.joint)) {
|
|
8390
|
+
throw new Error(`Pass either --joint ${jointSweep.joint}=... or --joint-sweep ${jointSweep.raw}, not both.`);
|
|
8391
|
+
}
|
|
8304
8392
|
if (sectionOptionsUsed && !channels.includes("section")) {
|
|
8305
8393
|
throw new Error("Section options only work with `forgecad inspect sections`.");
|
|
8306
8394
|
}
|
|
@@ -8375,6 +8463,7 @@ function parseInspectCli(argv, config = {}) {
|
|
|
8375
8463
|
cutaway,
|
|
8376
8464
|
paramOverrides,
|
|
8377
8465
|
jointOverrides,
|
|
8466
|
+
jointSweep,
|
|
8378
8467
|
comparison: hasCompareOptions ? {
|
|
8379
8468
|
compareWith,
|
|
8380
8469
|
align: compareAlign,
|
|
@@ -8623,6 +8712,70 @@ function buildViewPathEntries(pathsByView) {
|
|
|
8623
8712
|
])
|
|
8624
8713
|
);
|
|
8625
8714
|
}
|
|
8715
|
+
function collisionSweepPoseKey(pose) {
|
|
8716
|
+
return `pose-${String(pose.index).padStart(3, "0")}`;
|
|
8717
|
+
}
|
|
8718
|
+
function buildCollisionSweepSummary(options, poseResults) {
|
|
8719
|
+
const sweep = options.jointSweep;
|
|
8720
|
+
const poses = poseResults.map(({ pose, result }) => {
|
|
8721
|
+
const collisions = result.collisions ?? {};
|
|
8722
|
+
return {
|
|
8723
|
+
index: pose.index,
|
|
8724
|
+
joint: pose.joint,
|
|
8725
|
+
value: pose.value,
|
|
8726
|
+
overrides: pose.overrides,
|
|
8727
|
+
collisionCount: collisions.collisionCount ?? 0,
|
|
8728
|
+
candidatePairCount: collisions.candidatePairCount ?? 0,
|
|
8729
|
+
testedPairCount: collisions.testedPairCount ?? 0,
|
|
8730
|
+
skippedPairCount: collisions.skippedPairCount ?? 0,
|
|
8731
|
+
warnings: collisions.warnings ?? [],
|
|
8732
|
+
collisions: collisions.collisions ?? [],
|
|
8733
|
+
views: collisions.views ?? {}
|
|
8734
|
+
};
|
|
8735
|
+
});
|
|
8736
|
+
const worstCollisions = poses.flatMap(
|
|
8737
|
+
(pose) => pose.collisions.map((collision) => ({
|
|
8738
|
+
poseIndex: pose.index,
|
|
8739
|
+
joint: pose.joint,
|
|
8740
|
+
value: pose.value,
|
|
8741
|
+
collision
|
|
8742
|
+
}))
|
|
8743
|
+
).sort((a, b) => (b.collision.overlapVolume ?? 0) - (a.collision.overlapVolume ?? 0)).slice(0, 10);
|
|
8744
|
+
const maxCollisionCount = poses.reduce((max, pose) => Math.max(max, pose.collisionCount), 0);
|
|
8745
|
+
const maxOverlapVolume = worstCollisions[0]?.collision?.overlapVolume ?? 0;
|
|
8746
|
+
return {
|
|
8747
|
+
method: "cli-joint-sweep-v1",
|
|
8748
|
+
status: maxCollisionCount > 0 ? "fail" : "pass",
|
|
8749
|
+
expression: describeCliJointSweep(sweep),
|
|
8750
|
+
rawExpression: sweep.raw,
|
|
8751
|
+
joint: sweep.joint,
|
|
8752
|
+
start: sweep.start,
|
|
8753
|
+
end: sweep.end,
|
|
8754
|
+
sampleCount: sweep.samples,
|
|
8755
|
+
values: sweep.values,
|
|
8756
|
+
baseOverrides: options.jointOverrides ?? {},
|
|
8757
|
+
poseCount: poses.length,
|
|
8758
|
+
collidingPoseCount: poses.filter((pose) => pose.collisionCount > 0).length,
|
|
8759
|
+
maxCollisionCount,
|
|
8760
|
+
maxOverlapVolume,
|
|
8761
|
+
worstCollisions,
|
|
8762
|
+
warnings: Array.from(new Set(poses.flatMap((pose) => pose.warnings))),
|
|
8763
|
+
poses
|
|
8764
|
+
};
|
|
8765
|
+
}
|
|
8766
|
+
function buildCollisionSweepManifest(sweep, emittedSweepPaths) {
|
|
8767
|
+
if (!sweep) return null;
|
|
8768
|
+
return {
|
|
8769
|
+
...sweep,
|
|
8770
|
+
poses: sweep.poses.map((pose) => {
|
|
8771
|
+
const { views: _rawViews, ...rest } = pose;
|
|
8772
|
+
return {
|
|
8773
|
+
...rest,
|
|
8774
|
+
views: buildViewPathEntries(emittedSweepPaths?.[collisionSweepPoseKey(pose)])
|
|
8775
|
+
};
|
|
8776
|
+
})
|
|
8777
|
+
};
|
|
8778
|
+
}
|
|
8626
8779
|
function collectInspectViewNames(emittedPaths) {
|
|
8627
8780
|
const names = [];
|
|
8628
8781
|
const add2 = (value) => {
|
|
@@ -8939,11 +9092,14 @@ function buildInspectManifest({ options, result, scriptPath, projectRoot, emitte
|
|
|
8939
9092
|
};
|
|
8940
9093
|
}
|
|
8941
9094
|
if (emittedPaths.collisions) {
|
|
9095
|
+
const jointSweep = buildCollisionSweepManifest(result.collisions?.jointSweep, emittedPaths.collisionSweep);
|
|
9096
|
+
const collisionStatus = jointSweep ? (result.collisions?.collisionCount ?? 0) > 0 || jointSweep.status === "fail" ? "fail" : "pass" : null;
|
|
8942
9097
|
evidence.collisions = {
|
|
8943
9098
|
format: "png",
|
|
8944
9099
|
encoding: "rgb8-collision-highlight",
|
|
8945
9100
|
backgroundColor: [0, 0, 0],
|
|
8946
9101
|
decode: "source objects are translucent ghost geometry; boolean intersection volumes use per-finding collisions[].color palette entries",
|
|
9102
|
+
...collisionStatus ? { status: collisionStatus } : {},
|
|
8947
9103
|
method: result.collisions?.method,
|
|
8948
9104
|
options: result.collisions?.options,
|
|
8949
9105
|
broadphase: result.collisions?.broadphase,
|
|
@@ -8960,6 +9116,7 @@ function buildInspectManifest({ options, result, scriptPath, projectRoot, emitte
|
|
|
8960
9116
|
objects: result.collisions?.objects ?? [],
|
|
8961
9117
|
collisions: result.collisions?.collisions ?? [],
|
|
8962
9118
|
warnings: result.collisions?.warnings ?? [],
|
|
9119
|
+
...jointSweep ? { jointSweep } : {},
|
|
8963
9120
|
views: buildViewPathEntries(emittedPaths.collisions)
|
|
8964
9121
|
};
|
|
8965
9122
|
}
|
|
@@ -9493,81 +9650,85 @@ async function runInspectBundleCli(argv, config) {
|
|
|
9493
9650
|
await page.goto(url, { waitUntil: "networkidle0", timeout: 15e3 });
|
|
9494
9651
|
await page.waitForFunction("window.__forgeReady === true", { timeout: RENDER_READY_TIMEOUT_MS });
|
|
9495
9652
|
await page.exposeFunction("__forgeInspectProgress", logInspectProgress);
|
|
9496
|
-
const
|
|
9497
|
-
|
|
9653
|
+
const baseRenderOptions = {
|
|
9654
|
+
cameras: options.cameras,
|
|
9655
|
+
size: options.size,
|
|
9656
|
+
quality: options.quality,
|
|
9657
|
+
binaryFiles,
|
|
9658
|
+
activeBackend,
|
|
9659
|
+
cameraSpec: options.cameraSpec || null,
|
|
9660
|
+
viewName: options.viewName || null,
|
|
9661
|
+
sceneSpec: options.sceneSpec || null,
|
|
9662
|
+
background: options.background || null,
|
|
9663
|
+
focus: options.focus || null,
|
|
9664
|
+
hide: options.hide || null,
|
|
9665
|
+
renderStyle: options.renderStyle,
|
|
9666
|
+
edges: options.edges,
|
|
9667
|
+
paramOverrides: options.paramOverrides
|
|
9668
|
+
};
|
|
9669
|
+
const renderInspectInBrowser = (renderOptions) => page.evaluate(
|
|
9670
|
+
(scriptCode, files, scriptName, browserRenderOptions) => {
|
|
9498
9671
|
return window.__forgeRender(scriptCode, {
|
|
9499
|
-
cameras:
|
|
9500
|
-
size:
|
|
9501
|
-
quality:
|
|
9502
|
-
channels:
|
|
9672
|
+
cameras: browserRenderOptions.cameras,
|
|
9673
|
+
size: browserRenderOptions.size,
|
|
9674
|
+
quality: browserRenderOptions.quality,
|
|
9675
|
+
channels: browserRenderOptions.channels,
|
|
9503
9676
|
allFiles: files,
|
|
9504
9677
|
fileName: scriptName,
|
|
9505
|
-
binaryFiles:
|
|
9506
|
-
activeBackend:
|
|
9507
|
-
cameraSpec:
|
|
9508
|
-
viewName:
|
|
9509
|
-
sceneSpec:
|
|
9510
|
-
background:
|
|
9511
|
-
focus:
|
|
9512
|
-
hide:
|
|
9513
|
-
thickness:
|
|
9514
|
-
roughness:
|
|
9515
|
-
sectionPlan:
|
|
9516
|
-
cutaway:
|
|
9517
|
-
comparison:
|
|
9678
|
+
binaryFiles: browserRenderOptions.binaryFiles,
|
|
9679
|
+
activeBackend: browserRenderOptions.activeBackend,
|
|
9680
|
+
cameraSpec: browserRenderOptions.cameraSpec,
|
|
9681
|
+
viewName: browserRenderOptions.viewName,
|
|
9682
|
+
sceneSpec: browserRenderOptions.sceneSpec,
|
|
9683
|
+
background: browserRenderOptions.background,
|
|
9684
|
+
focus: browserRenderOptions.focus,
|
|
9685
|
+
hide: browserRenderOptions.hide,
|
|
9686
|
+
thickness: browserRenderOptions.thickness,
|
|
9687
|
+
roughness: browserRenderOptions.roughness,
|
|
9688
|
+
sectionPlan: browserRenderOptions.sectionPlan,
|
|
9689
|
+
cutaway: browserRenderOptions.cutaway,
|
|
9690
|
+
comparison: browserRenderOptions.comparison,
|
|
9518
9691
|
progress: "inspect",
|
|
9519
9692
|
renderMode: "solid",
|
|
9520
|
-
edges:
|
|
9521
|
-
renderStyle:
|
|
9693
|
+
edges: browserRenderOptions.edges,
|
|
9694
|
+
renderStyle: browserRenderOptions.renderStyle,
|
|
9522
9695
|
respectAuthoredSceneStyle: false,
|
|
9523
|
-
paramOverrides:
|
|
9524
|
-
jointOverrides:
|
|
9696
|
+
paramOverrides: browserRenderOptions.paramOverrides,
|
|
9697
|
+
jointOverrides: browserRenderOptions.jointOverrides
|
|
9525
9698
|
});
|
|
9526
9699
|
},
|
|
9527
9700
|
input.code,
|
|
9528
9701
|
input.allFiles,
|
|
9529
9702
|
input.fileName,
|
|
9530
|
-
|
|
9531
|
-
cameras: options.cameras,
|
|
9532
|
-
size: options.size,
|
|
9533
|
-
quality: options.quality,
|
|
9534
|
-
channels: options.channels,
|
|
9535
|
-
binaryFiles,
|
|
9536
|
-
activeBackend,
|
|
9537
|
-
cameraSpec: options.cameraSpec || null,
|
|
9538
|
-
viewName: options.viewName || null,
|
|
9539
|
-
sceneSpec: options.sceneSpec || null,
|
|
9540
|
-
background: options.background || null,
|
|
9541
|
-
focus: options.focus || null,
|
|
9542
|
-
hide: options.hide || null,
|
|
9543
|
-
thickness: options.thickness,
|
|
9544
|
-
roughness: options.roughness,
|
|
9545
|
-
sectionPlan: options.sectionPlan,
|
|
9546
|
-
cutaway: options.cutaway,
|
|
9547
|
-
renderStyle: options.renderStyle,
|
|
9548
|
-
edges: options.edges,
|
|
9549
|
-
paramOverrides: options.paramOverrides,
|
|
9550
|
-
jointOverrides: options.jointOverrides,
|
|
9551
|
-
comparison: comparisonInput ? {
|
|
9552
|
-
code: comparisonInput.code,
|
|
9553
|
-
allFiles: comparisonInput.allFiles,
|
|
9554
|
-
fileName: comparisonInput.fileName,
|
|
9555
|
-
binaryFiles: comparisonBinaryFiles,
|
|
9556
|
-
activeBackend: comparisonActiveBackend,
|
|
9557
|
-
displayPath: options.comparison.compareWith,
|
|
9558
|
-
sourcePath: comparisonInput.sourcePath,
|
|
9559
|
-
align: options.comparison.align,
|
|
9560
|
-
view: options.comparison.view,
|
|
9561
|
-
samples: options.comparison.samples,
|
|
9562
|
-
toleranceMm: options.comparison.toleranceMm
|
|
9563
|
-
} : options.comparison ? {
|
|
9564
|
-
align: options.comparison.align,
|
|
9565
|
-
view: options.comparison.view,
|
|
9566
|
-
samples: options.comparison.samples,
|
|
9567
|
-
toleranceMm: options.comparison.toleranceMm
|
|
9568
|
-
} : null
|
|
9569
|
-
}
|
|
9703
|
+
renderOptions
|
|
9570
9704
|
);
|
|
9705
|
+
const result = await renderInspectInBrowser({
|
|
9706
|
+
...baseRenderOptions,
|
|
9707
|
+
channels: options.channels,
|
|
9708
|
+
thickness: options.thickness,
|
|
9709
|
+
roughness: options.roughness,
|
|
9710
|
+
sectionPlan: options.sectionPlan,
|
|
9711
|
+
cutaway: options.cutaway,
|
|
9712
|
+
jointOverrides: options.jointOverrides,
|
|
9713
|
+
comparison: comparisonInput ? {
|
|
9714
|
+
code: comparisonInput.code,
|
|
9715
|
+
allFiles: comparisonInput.allFiles,
|
|
9716
|
+
fileName: comparisonInput.fileName,
|
|
9717
|
+
binaryFiles: comparisonBinaryFiles,
|
|
9718
|
+
activeBackend: comparisonActiveBackend,
|
|
9719
|
+
displayPath: options.comparison.compareWith,
|
|
9720
|
+
sourcePath: comparisonInput.sourcePath,
|
|
9721
|
+
align: options.comparison.align,
|
|
9722
|
+
view: options.comparison.view,
|
|
9723
|
+
samples: options.comparison.samples,
|
|
9724
|
+
toleranceMm: options.comparison.toleranceMm
|
|
9725
|
+
} : options.comparison ? {
|
|
9726
|
+
align: options.comparison.align,
|
|
9727
|
+
view: options.comparison.view,
|
|
9728
|
+
samples: options.comparison.samples,
|
|
9729
|
+
toleranceMm: options.comparison.toleranceMm
|
|
9730
|
+
} : null
|
|
9731
|
+
});
|
|
9571
9732
|
if (!result.ok) {
|
|
9572
9733
|
console.error("Script error:", result.error);
|
|
9573
9734
|
process.exit(1);
|
|
@@ -9605,6 +9766,33 @@ async function runInspectBundleCli(argv, config) {
|
|
|
9605
9766
|
}
|
|
9606
9767
|
}
|
|
9607
9768
|
}
|
|
9769
|
+
if (options.jointSweep && requested.has("collisions")) {
|
|
9770
|
+
const sweepPoses = buildCliJointSweepPoses(options.jointSweep, options.jointOverrides);
|
|
9771
|
+
const poseResults = [];
|
|
9772
|
+
console.log(`[inspect] Sweeping joint: ${describeCliJointSweep(options.jointSweep)}`);
|
|
9773
|
+
for (const pose of sweepPoses) {
|
|
9774
|
+
console.log(`[inspect] Sweep pose ${pose.index}/${sweepPoses.length}: ${pose.joint}=${pose.value}`);
|
|
9775
|
+
const sweepResult = await renderInspectInBrowser({
|
|
9776
|
+
...baseRenderOptions,
|
|
9777
|
+
channels: ["collisions"],
|
|
9778
|
+
thickness: {},
|
|
9779
|
+
roughness: {},
|
|
9780
|
+
sectionPlan: null,
|
|
9781
|
+
cutaway: null,
|
|
9782
|
+
comparison: null,
|
|
9783
|
+
jointOverrides: pose.overrides
|
|
9784
|
+
});
|
|
9785
|
+
if (!sweepResult.ok) {
|
|
9786
|
+
console.error("Script error:", sweepResult.error);
|
|
9787
|
+
process.exit(1);
|
|
9788
|
+
}
|
|
9789
|
+
poseResults.push({ pose, result: sweepResult });
|
|
9790
|
+
}
|
|
9791
|
+
result.collisions = {
|
|
9792
|
+
...result.collisions ?? {},
|
|
9793
|
+
jointSweep: buildCollisionSweepSummary(options, poseResults)
|
|
9794
|
+
};
|
|
9795
|
+
}
|
|
9608
9796
|
const emittedPaths = {};
|
|
9609
9797
|
const writeViewChannel = async (channel, source, getPng = (entry) => entry) => {
|
|
9610
9798
|
const label = displayInspectChannel(channel, options);
|
|
@@ -9678,6 +9866,25 @@ async function runInspectBundleCli(argv, config) {
|
|
|
9678
9866
|
}
|
|
9679
9867
|
if (requested.has("collisions")) {
|
|
9680
9868
|
await writeViewChannel("collisions", result.collisions?.views);
|
|
9869
|
+
if (result.collisions?.jointSweep) {
|
|
9870
|
+
console.log("[inspect] Writing fit interference joint sweep files...");
|
|
9871
|
+
emittedPaths.collisionSweep = {};
|
|
9872
|
+
for (const pose of result.collisions.jointSweep.poses) {
|
|
9873
|
+
const poseKey = collisionSweepPoseKey(pose);
|
|
9874
|
+
emittedPaths.collisionSweep[poseKey] = {};
|
|
9875
|
+
const usedFileStems = /* @__PURE__ */ new Set();
|
|
9876
|
+
for (const [view, png] of Object.entries(pose.views ?? {})) {
|
|
9877
|
+
if (!png) throw new Error(`Renderer did not emit required joint sweep collision view: ${poseKey}/${view}`);
|
|
9878
|
+
const relPath = toPortablePath(
|
|
9879
|
+
join4("evidence", "collisions", "sweep", poseKey, `${sanitizeViewFileStem(view, usedFileStems)}.png`)
|
|
9880
|
+
);
|
|
9881
|
+
await mkdir(dirname(join4(tempDir, relPath)), { recursive: true });
|
|
9882
|
+
await writeFile(join4(tempDir, relPath), pngBufferFromDataUrl(png));
|
|
9883
|
+
emittedPaths.collisionSweep[poseKey][view] = relPath;
|
|
9884
|
+
}
|
|
9885
|
+
}
|
|
9886
|
+
console.log("[inspect] Wrote fit interference joint sweep files.");
|
|
9887
|
+
}
|
|
9681
9888
|
}
|
|
9682
9889
|
if (requested.has("thickness")) {
|
|
9683
9890
|
await writeViewChannel("thickness", result.thickness?.views);
|
|
@@ -9734,6 +9941,15 @@ async function runInspectBundleCli(argv, config) {
|
|
|
9734
9941
|
printFramingWarnings(result.framing);
|
|
9735
9942
|
console.log(` Size: ${sz[0]} \xD7 ${sz[1]} \xD7 ${sz[2]} mm`);
|
|
9736
9943
|
console.log(` Volume: ${result.volume.toFixed(1)} mm\xB3`);
|
|
9944
|
+
if (manifest.evidence?.collisions?.jointSweep) {
|
|
9945
|
+
const sweep = manifest.evidence.collisions.jointSweep;
|
|
9946
|
+
console.log(
|
|
9947
|
+
` Joint sweep: ${sweep.status} (${sweep.collidingPoseCount}/${sweep.poseCount} colliding pose(s), max ${sweep.maxCollisionCount} collision(s))`
|
|
9948
|
+
);
|
|
9949
|
+
}
|
|
9950
|
+
if (options.jointSweep && manifest.evidence?.collisions?.status === "fail") {
|
|
9951
|
+
process.exitCode = 1;
|
|
9952
|
+
}
|
|
9737
9953
|
} finally {
|
|
9738
9954
|
await browser.close();
|
|
9739
9955
|
}
|
|
@@ -9813,7 +10029,7 @@ function writeParentImportFixture(root, withManifest) {
|
|
|
9813
10029
|
resolve9(root, "part/finger.forge.js"),
|
|
9814
10030
|
`
|
|
9815
10031
|
const pose = Param.choice("Static Pose", "open-display", ["open-display", "curl"]);
|
|
9816
|
-
const showStaticTendons =
|
|
10032
|
+
const showStaticTendons = Param.bool("Show Static Tendons", true);
|
|
9817
10033
|
|
|
9818
10034
|
return box(pose === "curl" ? 8 : 10, showStaticTendons ? 6 : 4, 3);
|
|
9819
10035
|
`,
|
|
@@ -12261,15 +12477,29 @@ function pairKey(a, b) {
|
|
|
12261
12477
|
}
|
|
12262
12478
|
function resolveIntentionalOverlaps(allowances, entries, findings) {
|
|
12263
12479
|
const entryByShape = /* @__PURE__ */ new Map();
|
|
12264
|
-
|
|
12480
|
+
const entriesByLineage = /* @__PURE__ */ new Map();
|
|
12481
|
+
for (const entry of entries) {
|
|
12482
|
+
entryByShape.set(entry.shape, entry);
|
|
12483
|
+
const token = getShapeLineageToken(entry.shape);
|
|
12484
|
+
const lineageEntries = entriesByLineage.get(token) ?? [];
|
|
12485
|
+
lineageEntries.push(entry);
|
|
12486
|
+
entriesByLineage.set(token, lineageEntries);
|
|
12487
|
+
}
|
|
12265
12488
|
const findingByPair = /* @__PURE__ */ new Map();
|
|
12266
12489
|
for (const finding2 of findings) {
|
|
12267
12490
|
findingByPair.set(pairKey(finding2.sourceId, finding2.targetId), finding2);
|
|
12268
12491
|
}
|
|
12492
|
+
const resolveVisibleEntry = (shape) => {
|
|
12493
|
+
const exact = entryByShape.get(shape);
|
|
12494
|
+
if (exact) return exact;
|
|
12495
|
+
if (!shape || typeof shape !== "object") return void 0;
|
|
12496
|
+
const lineageEntries = entriesByLineage.get(getShapeLineageToken(shape)) ?? [];
|
|
12497
|
+
return lineageEntries.length === 1 ? lineageEntries[0] : void 0;
|
|
12498
|
+
};
|
|
12269
12499
|
const allowedCollisionKeys = /* @__PURE__ */ new Set();
|
|
12270
12500
|
const reports = allowances.map((allowance) => {
|
|
12271
|
-
const source =
|
|
12272
|
-
const target =
|
|
12501
|
+
const source = resolveVisibleEntry(allowance.a);
|
|
12502
|
+
const target = resolveVisibleEntry(allowance.b);
|
|
12273
12503
|
const base = {
|
|
12274
12504
|
label: allowance.label,
|
|
12275
12505
|
reason: allowance.reason,
|
|
@@ -16737,7 +16967,51 @@ var SKILL_SUPPRESSED_RUNTIME_NAMES = /* @__PURE__ */ new Set([
|
|
|
16737
16967
|
"nurbs3d",
|
|
16738
16968
|
"spline3d",
|
|
16739
16969
|
"transitionCurve",
|
|
16740
|
-
"transitionSurface"
|
|
16970
|
+
"transitionSurface",
|
|
16971
|
+
// Lean-API sweep (2026-06): soft-deprecated globals (legacy.md) and
|
|
16972
|
+
// hard-removed throwing stubs. Still reserved in human-authored scripts,
|
|
16973
|
+
// never taught to agents.
|
|
16974
|
+
"assemblyInstructions",
|
|
16975
|
+
"assemblyPreview",
|
|
16976
|
+
"bomToCsv",
|
|
16977
|
+
"boolParam",
|
|
16978
|
+
"chamferTrackedEdge",
|
|
16979
|
+
"choiceParam",
|
|
16980
|
+
"circle",
|
|
16981
|
+
"combine",
|
|
16982
|
+
"COMMON_KERFS",
|
|
16983
|
+
"composeChain",
|
|
16984
|
+
"Constraint",
|
|
16985
|
+
"Counterbore",
|
|
16986
|
+
"degrees",
|
|
16987
|
+
"dimLine",
|
|
16988
|
+
"filletCorners",
|
|
16989
|
+
"filletTrackedEdge",
|
|
16990
|
+
"fingerJoint",
|
|
16991
|
+
"flatPanel",
|
|
16992
|
+
"flatPart",
|
|
16993
|
+
"formatInstructions",
|
|
16994
|
+
"highlight",
|
|
16995
|
+
"importMesh",
|
|
16996
|
+
"importStep",
|
|
16997
|
+
"importSvgSketch",
|
|
16998
|
+
"jointsView",
|
|
16999
|
+
"laserKit",
|
|
17000
|
+
"line",
|
|
17001
|
+
"listParam",
|
|
17002
|
+
"lookupKerf",
|
|
17003
|
+
"nurbsSurface",
|
|
17004
|
+
"point",
|
|
17005
|
+
"ProductHandleBuilder",
|
|
17006
|
+
"ProductHandleFeature",
|
|
17007
|
+
"ProductSpoutBuilder",
|
|
17008
|
+
"radians",
|
|
17009
|
+
"Ribs",
|
|
17010
|
+
"Slot",
|
|
17011
|
+
"star",
|
|
17012
|
+
"surfacePatch",
|
|
17013
|
+
"tabSlot",
|
|
17014
|
+
"viewConfig"
|
|
16741
17015
|
]);
|
|
16742
17016
|
function checkRuntimeNamesDoc(runtimeNames) {
|
|
16743
17017
|
const collisionReservedNames = runtimeNames.filter((name) => name !== "showLabels");
|
|
@@ -21912,7 +22186,7 @@ function computeLinkFrameWorlds(assembly2, jointValues) {
|
|
|
21912
22186
|
const visit = (partName, world) => {
|
|
21913
22187
|
worlds.set(partName, world);
|
|
21914
22188
|
(jointsByParent.get(partName) ?? []).forEach((joint2) => {
|
|
21915
|
-
const childWorld =
|
|
22189
|
+
const childWorld = Transform.compose(motionTransform(joint2, jointValues[joint2.name] ?? joint2.defaultValue), joint2.frame, world);
|
|
21916
22190
|
visit(joint2.child, childWorld);
|
|
21917
22191
|
});
|
|
21918
22192
|
};
|
|
@@ -23667,7 +23941,7 @@ import http from "http";
|
|
|
23667
23941
|
import path3 from "path";
|
|
23668
23942
|
|
|
23669
23943
|
// server/importAnalysis.ts
|
|
23670
|
-
var FORGE_IMPORT_RE2 = /\b(?:importMesh|importStep|importSvgSketch|Import\.dxfSketch)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
23944
|
+
var FORGE_IMPORT_RE2 = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
23671
23945
|
var REQUIRE_RE2 = /\brequire\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
23672
23946
|
var ES_IMPORT_RE2 = /\bfrom\s+(?:"([^"]+)"|'([^']+)')/g;
|
|
23673
23947
|
var VIRTUAL_MODULES2 = /* @__PURE__ */ new Set(["forgecad", "@forge/runtime", "@forgecad/runtime"]);
|
|
@@ -23684,13 +23958,16 @@ function extractImports2(code) {
|
|
|
23684
23958
|
importMesh: "forgeMesh",
|
|
23685
23959
|
importStep: "forgeMesh",
|
|
23686
23960
|
importSvgSketch: "forgeSvg",
|
|
23961
|
+
"Import.mesh": "forgeMesh",
|
|
23962
|
+
"Import.step": "forgeMesh",
|
|
23963
|
+
"Import.svgSketch": "forgeSvg",
|
|
23687
23964
|
"Import.dxfSketch": "forgeDxf"
|
|
23688
23965
|
};
|
|
23689
23966
|
let m;
|
|
23690
23967
|
const forgeRe = new RegExp(FORGE_IMPORT_RE2.source, FORGE_IMPORT_RE2.flags);
|
|
23691
23968
|
while ((m = forgeRe.exec(code)) !== null) {
|
|
23692
23969
|
const path5 = m[1] ?? m[2];
|
|
23693
|
-
const fn = m[0].match(/\b(importMesh|importStep|importSvgSketch|Import\.dxfSketch)/)?.[1];
|
|
23970
|
+
const fn = m[0].match(/\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch)/)?.[1];
|
|
23694
23971
|
add2(path5, kindMap[fn] ?? "forgeMesh");
|
|
23695
23972
|
}
|
|
23696
23973
|
const reqRe = new RegExp(REQUIRE_RE2.source, REQUIRE_RE2.flags);
|
|
@@ -24176,11 +24453,39 @@ function requireCliAuth() {
|
|
|
24176
24453
|
}
|
|
24177
24454
|
|
|
24178
24455
|
// cli/project.ts
|
|
24179
|
-
var SOURCE_EXTS = [
|
|
24456
|
+
var SOURCE_EXTS = [
|
|
24457
|
+
".forge.js",
|
|
24458
|
+
".sketch.js",
|
|
24459
|
+
".js",
|
|
24460
|
+
".mjs",
|
|
24461
|
+
".cjs",
|
|
24462
|
+
".jsx",
|
|
24463
|
+
".ts",
|
|
24464
|
+
".tsx",
|
|
24465
|
+
".json",
|
|
24466
|
+
".md",
|
|
24467
|
+
".txt",
|
|
24468
|
+
".svg",
|
|
24469
|
+
".dxf",
|
|
24470
|
+
".html",
|
|
24471
|
+
".css",
|
|
24472
|
+
".scss",
|
|
24473
|
+
".yml",
|
|
24474
|
+
".yaml",
|
|
24475
|
+
".toml",
|
|
24476
|
+
".xml",
|
|
24477
|
+
".csv",
|
|
24478
|
+
".ini"
|
|
24479
|
+
];
|
|
24180
24480
|
var MESH_EXTS = [".stl", ".obj", ".3mf"];
|
|
24181
24481
|
var ALL_EXTS = [...SOURCE_EXTS, ...MESH_EXTS];
|
|
24482
|
+
var SKIPPED_PROJECT_DIRECTORY_NAMES = /* @__PURE__ */ new Set([".next", ".turbo", "build", "coverage", "dist", "node_modules"]);
|
|
24182
24483
|
function isSourceFile(name) {
|
|
24183
|
-
|
|
24484
|
+
const lower2 = name.toLowerCase();
|
|
24485
|
+
return SOURCE_EXTS.some((ext) => lower2.endsWith(ext));
|
|
24486
|
+
}
|
|
24487
|
+
function shouldSkipProjectDirectory(name) {
|
|
24488
|
+
return name.startsWith(".") || SKIPPED_PROJECT_DIRECTORY_NAMES.has(name);
|
|
24184
24489
|
}
|
|
24185
24490
|
var MANIFEST_FILE2 = "forgecad.json";
|
|
24186
24491
|
function manifestPath(projectDir) {
|
|
@@ -24219,8 +24524,9 @@ function scanLocalFiles(projectDir) {
|
|
|
24219
24524
|
const rel = prefix ? `${prefix}/${item.name}` : item.name;
|
|
24220
24525
|
const full = join10(dir, item.name);
|
|
24221
24526
|
if (item.isDirectory()) {
|
|
24527
|
+
if (shouldSkipProjectDirectory(item.name)) continue;
|
|
24222
24528
|
walk(full, rel);
|
|
24223
|
-
} else if (item.isFile() && isSourceFile(item.name)) {
|
|
24529
|
+
} else if (item.isFile() && rel !== MANIFEST_FILE2 && isSourceFile(item.name)) {
|
|
24224
24530
|
const content = readFileSync17(full, "utf-8");
|
|
24225
24531
|
files.push({ path: rel, content, hash: contentHash(content) });
|
|
24226
24532
|
}
|
|
@@ -24440,7 +24746,7 @@ import { existsSync as existsSync13, readdirSync as readdirSync8, statSync as st
|
|
|
24440
24746
|
import { createServer as createServer2 } from "net";
|
|
24441
24747
|
import { join as join11 } from "path";
|
|
24442
24748
|
var startedComputeServer = null;
|
|
24443
|
-
var REQUIRED_ENGINE_VERSION = "native-occt-node-api-0.
|
|
24749
|
+
var REQUIRED_ENGINE_VERSION = "native-occt-node-api-0.3.0";
|
|
24444
24750
|
var REQUIRED_PLAN_KINDS = [
|
|
24445
24751
|
"box",
|
|
24446
24752
|
"cylinder",
|
|
@@ -24461,7 +24767,11 @@ function npmCommand() {
|
|
|
24461
24767
|
return process.platform === "win32" ? "npm.cmd" : "npm";
|
|
24462
24768
|
}
|
|
24463
24769
|
function isLocalComputeUrl(url) {
|
|
24464
|
-
|
|
24770
|
+
const hostname = listenHostForUrl(url);
|
|
24771
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
24772
|
+
}
|
|
24773
|
+
function listenHostForUrl(url) {
|
|
24774
|
+
return url.hostname.replace(/^\[|\]$/g, "");
|
|
24465
24775
|
}
|
|
24466
24776
|
async function isHealthy(computeUrl) {
|
|
24467
24777
|
try {
|
|
@@ -24525,6 +24835,7 @@ function computeUrlWithPort(url, port) {
|
|
|
24525
24835
|
return next.toString().replace(/\/$/, "");
|
|
24526
24836
|
}
|
|
24527
24837
|
async function pickComputeUrl(preferred) {
|
|
24838
|
+
const listenHost = listenHostForUrl(preferred);
|
|
24528
24839
|
const preferredPort = Number.parseInt(preferred.port || (preferred.protocol === "https:" ? "443" : "80"), 10);
|
|
24529
24840
|
for (let port = preferredPort; port < preferredPort + 10; port += 1) {
|
|
24530
24841
|
const candidate = computeUrlWithPort(preferred, port);
|
|
@@ -24533,7 +24844,7 @@ async function pickComputeUrl(preferred) {
|
|
|
24533
24844
|
const server = await new Promise((resolve40, reject) => {
|
|
24534
24845
|
const probe = createServer2();
|
|
24535
24846
|
probe.once("error", reject);
|
|
24536
|
-
probe.listen(port,
|
|
24847
|
+
probe.listen(port, listenHost, () => resolve40(probe));
|
|
24537
24848
|
});
|
|
24538
24849
|
await new Promise((resolve40) => server.close(() => resolve40()));
|
|
24539
24850
|
return candidate;
|
|
@@ -24574,10 +24885,11 @@ function startLocalNativeComputeServer(options) {
|
|
|
24574
24885
|
const computeUrl = await pickComputeUrl(parsed);
|
|
24575
24886
|
const selected = new URL(computeUrl);
|
|
24576
24887
|
const port = selected.port || (selected.protocol === "https:" ? "443" : "80");
|
|
24888
|
+
const host = listenHostForUrl(selected);
|
|
24577
24889
|
console.log(`[compute] Starting native OCCT backend at ${computeUrl}`);
|
|
24578
24890
|
const child = spawn3("npx", ["tsx", "apps/backend/src/server.ts"], {
|
|
24579
24891
|
cwd: options.packageRoot,
|
|
24580
|
-
env: { ...process.env, FORGE_BACKEND_PORT: port },
|
|
24892
|
+
env: { ...process.env, FORGE_BACKEND_PORT: port, FORGE_BACKEND_HOST: host },
|
|
24581
24893
|
stdio: ["ignore", "inherit", "inherit"]
|
|
24582
24894
|
});
|
|
24583
24895
|
await waitForHealthy(computeUrl, child);
|
|
@@ -24876,12 +25188,42 @@ function updateLocalAgentContextLatestBundle(projectRoot, filePath, latestBundle
|
|
|
24876
25188
|
}
|
|
24877
25189
|
|
|
24878
25190
|
// cli/forge-studio-server.ts
|
|
24879
|
-
var PROJECT_FILE_EXTS = [
|
|
24880
|
-
|
|
25191
|
+
var PROJECT_FILE_EXTS = [
|
|
25192
|
+
".forge.js",
|
|
25193
|
+
".sketch.js",
|
|
25194
|
+
".js",
|
|
25195
|
+
".mjs",
|
|
25196
|
+
".cjs",
|
|
25197
|
+
".jsx",
|
|
25198
|
+
".ts",
|
|
25199
|
+
".tsx",
|
|
25200
|
+
".json",
|
|
25201
|
+
".md",
|
|
25202
|
+
".txt",
|
|
25203
|
+
".svg",
|
|
25204
|
+
".dxf",
|
|
25205
|
+
".html",
|
|
25206
|
+
".css",
|
|
25207
|
+
".scss",
|
|
25208
|
+
".yml",
|
|
25209
|
+
".yaml",
|
|
25210
|
+
".toml",
|
|
25211
|
+
".xml",
|
|
25212
|
+
".csv",
|
|
25213
|
+
".ini"
|
|
25214
|
+
];
|
|
25215
|
+
var isProjectFile = (name) => {
|
|
25216
|
+
const lower2 = name.toLowerCase();
|
|
25217
|
+
return PROJECT_FILE_EXTS.some((ext) => lower2.endsWith(ext));
|
|
25218
|
+
};
|
|
24881
25219
|
var MESH_FILE_EXTS = [".stl", ".obj", ".3mf"];
|
|
24882
25220
|
var EXACT_IMPORT_FILE_EXTS = [".step", ".stp"];
|
|
24883
25221
|
var BINARY_PROJECT_FILE_EXTS = [...MESH_FILE_EXTS, ...EXACT_IMPORT_FILE_EXTS];
|
|
24884
25222
|
var isBinaryProjectFile = (name) => BINARY_PROJECT_FILE_EXTS.some((ext) => name.toLowerCase().endsWith(ext));
|
|
25223
|
+
var SKIPPED_PROJECT_DIRECTORY_NAMES2 = /* @__PURE__ */ new Set([".next", ".turbo", "build", "coverage", "dist", "node_modules"]);
|
|
25224
|
+
var shouldSkipProjectDirectory2 = (name) => name.startsWith(".") || SKIPPED_PROJECT_DIRECTORY_NAMES2.has(name);
|
|
25225
|
+
var LOCAL_PROJECT_MANIFEST_FILE = "forgecad.json";
|
|
25226
|
+
var isLocalProjectManifestPath = (rel) => rel.toLowerCase() === LOCAL_PROJECT_MANIFEST_FILE;
|
|
24885
25227
|
var MIME = {
|
|
24886
25228
|
".html": "text/html; charset=utf-8",
|
|
24887
25229
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -24965,9 +25307,10 @@ function scanProjectFiles(projectDir) {
|
|
|
24965
25307
|
if (item.name.startsWith(".")) continue;
|
|
24966
25308
|
const rel = prefix ? `${prefix}/${item.name}` : item.name;
|
|
24967
25309
|
if (item.isDirectory()) {
|
|
25310
|
+
if (shouldSkipProjectDirectory2(item.name)) continue;
|
|
24968
25311
|
folderSet.add(rel);
|
|
24969
25312
|
scan(path3.join(dir, item.name), rel);
|
|
24970
|
-
} else if (item.isFile() && isProjectFile(item.name)) {
|
|
25313
|
+
} else if (item.isFile() && !isLocalProjectManifestPath(rel) && isProjectFile(item.name)) {
|
|
24971
25314
|
files[rel] = fs2.readFileSync(path3.join(dir, item.name), "utf-8");
|
|
24972
25315
|
} else if (item.isFile() && isBinaryProjectFile(item.name)) {
|
|
24973
25316
|
files[rel] = "";
|
|
@@ -24989,9 +25332,10 @@ function scanProjectFileListing(projectDir) {
|
|
|
24989
25332
|
if (item.name.startsWith(".")) continue;
|
|
24990
25333
|
const rel = prefix ? `${prefix}/${item.name}` : item.name;
|
|
24991
25334
|
if (item.isDirectory()) {
|
|
25335
|
+
if (shouldSkipProjectDirectory2(item.name)) continue;
|
|
24992
25336
|
folders.push(rel);
|
|
24993
25337
|
scan(path3.join(dir, item.name), rel);
|
|
24994
|
-
} else if (item.isFile() && (isProjectFile(item.name) || isBinaryProjectFile(item.name))) {
|
|
25338
|
+
} else if (item.isFile() && !isLocalProjectManifestPath(rel) && (isProjectFile(item.name) || isBinaryProjectFile(item.name))) {
|
|
24995
25339
|
files.push(rel);
|
|
24996
25340
|
}
|
|
24997
25341
|
}
|
|
@@ -25012,6 +25356,7 @@ function resolveProjectFile(projectDir, filename, opts = {}) {
|
|
|
25012
25356
|
if (rel.startsWith("..") || path3.isAbsolute(rel)) {
|
|
25013
25357
|
throw new Error(`Path "${filename}" is outside the project root`);
|
|
25014
25358
|
}
|
|
25359
|
+
if (isLocalProjectManifestPath(rel.replace(/\\/g, "/"))) throw new Error("Project manifest is not editable");
|
|
25015
25360
|
return { filePath, filename: rel.replace(/\\/g, "/") };
|
|
25016
25361
|
}
|
|
25017
25362
|
function resolveProjectDirectory(projectDir, dirPath) {
|
|
@@ -25025,17 +25370,6 @@ function resolveProjectDirectory(projectDir, dirPath) {
|
|
|
25025
25370
|
}
|
|
25026
25371
|
return { dirPath: resolved, dirname: rel.replace(/\\/g, "/") };
|
|
25027
25372
|
}
|
|
25028
|
-
function directoryTreeContainsFile(dirPath) {
|
|
25029
|
-
for (const item of fs2.readdirSync(dirPath, { withFileTypes: true })) {
|
|
25030
|
-
const childPath2 = path3.join(dirPath, item.name);
|
|
25031
|
-
if (item.isDirectory()) {
|
|
25032
|
-
if (directoryTreeContainsFile(childPath2)) return true;
|
|
25033
|
-
} else {
|
|
25034
|
-
return true;
|
|
25035
|
-
}
|
|
25036
|
-
}
|
|
25037
|
-
return false;
|
|
25038
|
-
}
|
|
25039
25373
|
function serveStatic(distDir, req, res) {
|
|
25040
25374
|
const absDistDir = path3.resolve(distDir);
|
|
25041
25375
|
const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
@@ -25171,10 +25505,14 @@ data: ${JSON.stringify(data)}
|
|
|
25171
25505
|
}
|
|
25172
25506
|
for (const proj of projects) {
|
|
25173
25507
|
const abs = path3.resolve(proj.dir);
|
|
25174
|
-
const watcher = chokidar.watch(abs, {
|
|
25508
|
+
const watcher = chokidar.watch(abs, {
|
|
25509
|
+
ignoreInitial: true,
|
|
25510
|
+
ignored: (candidate) => path3.relative(abs, candidate).split(/[\\/]/).some(shouldSkipProjectDirectory2)
|
|
25511
|
+
});
|
|
25175
25512
|
watcherReadyPromises.push(waitForWatcherReady(watcher));
|
|
25176
25513
|
watcher.on("add", (f2) => {
|
|
25177
25514
|
const rel = path3.relative(abs, f2).replace(/\\/g, "/");
|
|
25515
|
+
if (isLocalProjectManifestPath(rel)) return;
|
|
25178
25516
|
if (isProjectFile(f2)) {
|
|
25179
25517
|
try {
|
|
25180
25518
|
broadcastToProject(proj.id, "change", { filename: rel, content: fs2.readFileSync(f2, "utf-8") });
|
|
@@ -25186,6 +25524,7 @@ data: ${JSON.stringify(data)}
|
|
|
25186
25524
|
});
|
|
25187
25525
|
watcher.on("change", (f2) => {
|
|
25188
25526
|
const rel = path3.relative(abs, f2).replace(/\\/g, "/");
|
|
25527
|
+
if (isLocalProjectManifestPath(rel)) return;
|
|
25189
25528
|
if (isProjectFile(f2)) {
|
|
25190
25529
|
try {
|
|
25191
25530
|
broadcastToProject(proj.id, "change", { filename: rel, content: fs2.readFileSync(f2, "utf-8") });
|
|
@@ -25196,8 +25535,10 @@ data: ${JSON.stringify(data)}
|
|
|
25196
25535
|
}
|
|
25197
25536
|
});
|
|
25198
25537
|
watcher.on("unlink", (f2) => {
|
|
25538
|
+
const rel = path3.relative(abs, f2).replace(/\\/g, "/");
|
|
25539
|
+
if (isLocalProjectManifestPath(rel)) return;
|
|
25199
25540
|
if (!isProjectFile(f2) && !isBinaryProjectFile(f2)) return;
|
|
25200
|
-
broadcastToProject(proj.id, "delete", { filename:
|
|
25541
|
+
broadcastToProject(proj.id, "delete", { filename: rel });
|
|
25201
25542
|
broadcastProjectSnapshot(proj);
|
|
25202
25543
|
});
|
|
25203
25544
|
watcher.on("addDir", () => broadcastProjectSnapshot(proj));
|
|
@@ -25604,10 +25945,6 @@ data: ${JSON.stringify(data)}
|
|
|
25604
25945
|
sendJson(res, 400, { error: "Path is not a directory" });
|
|
25605
25946
|
return;
|
|
25606
25947
|
}
|
|
25607
|
-
if (directoryTreeContainsFile(resolved.dirPath)) {
|
|
25608
|
-
sendJson(res, 409, { error: "Directory is not empty" });
|
|
25609
|
-
return;
|
|
25610
|
-
}
|
|
25611
25948
|
fs2.rmSync(resolved.dirPath, { recursive: true, force: true });
|
|
25612
25949
|
sendJson(res, 200, { success: true });
|
|
25613
25950
|
}).catch((e) => sendJson(res, 500, { error: e.message }));
|
|
@@ -26428,7 +26765,7 @@ function computeLinkFrameWorlds2(assembly2, jointValues) {
|
|
|
26428
26765
|
const visit = (partName, world) => {
|
|
26429
26766
|
worlds.set(partName, world);
|
|
26430
26767
|
(jointsByParent.get(partName) ?? []).forEach((joint2) => {
|
|
26431
|
-
const childWorld =
|
|
26768
|
+
const childWorld = Transform.compose(motionTransform2(joint2, jointValues[joint2.name] ?? joint2.defaultValue), joint2.frame, world);
|
|
26432
26769
|
visit(joint2.child, childWorld);
|
|
26433
26770
|
});
|
|
26434
26771
|
};
|
|
@@ -31469,6 +31806,23 @@ var RENDER_OPTIONS = [
|
|
|
31469
31806
|
},
|
|
31470
31807
|
{ name: "--output", description: "Output file path", argument: "required", valueLabel: "<path>", valueKind: "png" }
|
|
31471
31808
|
];
|
|
31809
|
+
var RENDER_MODEL_OPTIONS = [
|
|
31810
|
+
...RENDER_OPTIONS,
|
|
31811
|
+
{
|
|
31812
|
+
name: "--quality",
|
|
31813
|
+
description: "Mesh quality preset",
|
|
31814
|
+
argument: "required",
|
|
31815
|
+
valueLabel: "<default|live|high>",
|
|
31816
|
+
values: QUALITY_VALUES
|
|
31817
|
+
},
|
|
31818
|
+
{
|
|
31819
|
+
name: "--backend",
|
|
31820
|
+
description: "Geometry backend (default: manifold; STEP inputs default to OCCT)",
|
|
31821
|
+
argument: "required",
|
|
31822
|
+
valueLabel: "<manifold|occt|truck>",
|
|
31823
|
+
values: BACKEND_VALUES
|
|
31824
|
+
}
|
|
31825
|
+
];
|
|
31472
31826
|
var LS_OPTIONS = [
|
|
31473
31827
|
{ name: "--tree", description: "Print targets as a compact object tree" },
|
|
31474
31828
|
{ name: "--long", description: "Include geometry metrics such as bounds, volume, area, bodies, and triangle counts" },
|
|
@@ -31516,6 +31870,12 @@ var SHOW_OPTIONS = [
|
|
|
31516
31870
|
];
|
|
31517
31871
|
var INSPECT_EVIDENCE_OPTIONS = [
|
|
31518
31872
|
...PARAM_OPTIONS,
|
|
31873
|
+
{
|
|
31874
|
+
name: "--joint-sweep",
|
|
31875
|
+
description: "Sweep one Motion tab joint for fit interference evidence",
|
|
31876
|
+
argument: "required",
|
|
31877
|
+
valueLabel: "<JointName=start..end:samples>"
|
|
31878
|
+
},
|
|
31519
31879
|
{
|
|
31520
31880
|
name: "--output",
|
|
31521
31881
|
description: "Preferred output bundle directory; a non-colliding sibling is created if needed",
|
|
@@ -31707,6 +32067,9 @@ function inspectEvidenceOptions(definition) {
|
|
|
31707
32067
|
if (definition.channel === "roughness") {
|
|
31708
32068
|
names.add("--roughness-samples");
|
|
31709
32069
|
}
|
|
32070
|
+
if (definition.channel === "collisions") {
|
|
32071
|
+
names.add("--joint-sweep");
|
|
32072
|
+
}
|
|
31710
32073
|
if (definition.channel === "comparison") {
|
|
31711
32074
|
for (const name of ["--with", "--compare-with", "--compare-align", "--compare-view", "--compare-samples", "--compare-tolerance-mm"]) {
|
|
31712
32075
|
names.add(name);
|
|
@@ -32591,23 +32954,16 @@ var commands = [
|
|
|
32591
32954
|
group: "Modeling",
|
|
32592
32955
|
path: ["run"],
|
|
32593
32956
|
summary: "Execute a Forge script quickly and print the inner-loop build summary: build count, verification results, parameters, and timing.",
|
|
32594
|
-
description: "The fast validation command
|
|
32957
|
+
description: "The fast validation command: runs the script with the real geometry kernel (no browser) and reports build status, object count, `verify.*` pass/fail with expected vs actual values (non-fatal \u2014 the model still renders), parameter values, script logs, and timing. Run it frequently while editing.\n\nA bare `forgecad run` skips expensive diagnostics. Opt in with `--details` (volumes/bounds), `--history` (construction tree), `--features` (feature tallies), `--solver-profile` (constraint solver timing), or `--connectivity` (physical connected components \u2014 bbox contact is evidence, exact geometry is checked by default). `--quality live|default|high` selects the same geometry quality profile as the editor and export tools; `live` is fastest for large models.\n\nDirect `.stl`/`.obj`/`.3mf`/`.step`/`.stp` inputs are imported automatically; STEP/STP auto-selects OCCT unless you pass `--backend`. For deeper confidence gates, prefer `inspect mechanical-integrity`, `check print`, or `inspect fit interference` instead of turning `run` into a catch-all audit command.",
|
|
32595
32958
|
usage: [
|
|
32596
32959
|
"forgecad run <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [--details] [--history] [--features] [--connectivity] [--connectivity-tolerance <mm>] [--focus [names]] [--hide names] [--journeys] [--journeys-json] [--param Key=Value] [--joint JointName=Value] [--debug-imports] [--verbose] [--backend manifold|occt|truck] [--quality live|default|high] [--solver-profile] [--solver-debug-out <dir>]"
|
|
32597
32960
|
],
|
|
32598
32961
|
examples: [
|
|
32599
|
-
"forgecad run
|
|
32600
|
-
"forgecad run
|
|
32601
|
-
'forgecad run
|
|
32602
|
-
|
|
32603
|
-
|
|
32604
|
-
"forgecad run examples/products/cup.forge.js --connectivity",
|
|
32605
|
-
"forgecad run examples/products/cup.forge.js --journeys",
|
|
32606
|
-
"forgecad run examples/products/cup.forge.js --backend occt",
|
|
32607
|
-
"forgecad run examples/products/cup.forge.js --backend truck --quality live",
|
|
32608
|
-
"forgecad run examples/products/cup.forge.js --debug-imports",
|
|
32609
|
-
'forgecad run examples/products/cup.forge.js -p "Wall Thickness=3" -p "Body Height=200"',
|
|
32610
|
-
"forgecad run examples/constraints/06-complex-spectrogram.forge.js --solver-debug-out tmp/spectrogram-debug"
|
|
32962
|
+
"forgecad run model.forge.js",
|
|
32963
|
+
"forgecad run model.forge.js --details --history",
|
|
32964
|
+
'forgecad run model.forge.js --focus "Bench.Slat*"',
|
|
32965
|
+
"forgecad run model.forge.js --connectivity --quality live",
|
|
32966
|
+
'forgecad run model.forge.js -p "Wall Thickness=3" -p "Body Height=200"'
|
|
32611
32967
|
],
|
|
32612
32968
|
completion: {
|
|
32613
32969
|
options: [
|
|
@@ -32678,14 +33034,7 @@ var commands = [
|
|
|
32678
33034
|
summary: "Render a Forge scene. Use a subcommand \u2014 `3d`, `views`, `section`, `wireframe`, `sketch`, or `hq`.",
|
|
32679
33035
|
description: "`forgecad render` is a group of rendering subcommands. Pick one based on what you want:\n\n- `render 3d` \u2014 standard viewport PNG, the usual way to visually verify geometry\n- `render views` \u2014 list named cameras declared with `scene({ views })`\n- `render wireframe` \u2014 edges only, no shading\n- `render section` \u2014 2D cross-section cut by a plane (SVG or PNG)\n- `render sketch` \u2014 2D sketch script to PNG\n- `render hq` \u2014 path-traced via Blender Cycles, for documentation and marketing shots",
|
|
32680
33036
|
usage: ["forgecad render <subcommand> <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [options]"],
|
|
32681
|
-
examples: [
|
|
32682
|
-
"forgecad render 3d examples/products/cup.forge.js",
|
|
32683
|
-
"forgecad inspect fit interference examples/api/static-assembly-connectors.forge.js --camera iso",
|
|
32684
|
-
"forgecad render views examples/products/cup.forge.js",
|
|
32685
|
-
"forgecad render wireframe examples/products/cup.forge.js",
|
|
32686
|
-
"forgecad render section examples/furniture/01-table.forge.js --plane XZ",
|
|
32687
|
-
"forgecad render hq examples/products/cup.forge.js --preset dramatic"
|
|
32688
|
-
],
|
|
33037
|
+
examples: ["forgecad render 3d model.forge.js", "forgecad render section model.forge.js --plane XZ"],
|
|
32689
33038
|
run: async (_args) => {
|
|
32690
33039
|
console.error("`forgecad render` requires a subcommand.");
|
|
32691
33040
|
console.error("");
|
|
@@ -32705,28 +33054,19 @@ var commands = [
|
|
|
32705
33054
|
group: "Modeling",
|
|
32706
33055
|
path: ["render", "3d"],
|
|
32707
33056
|
summary: "Render a Forge scene to PNG using the real viewport renderer.",
|
|
32708
|
-
description: "Launches
|
|
33057
|
+
description: "Launches headless Chrome, renders the scene with the same WebGL viewport as the editor, and saves a PNG. The output path defaults to `<script-name>.png` next to the input; the input can be a `.forge.js` script or a direct `.stl`/`.obj`/`.3mf`/`.step`/`.stp` asset.\n\n`--camera` accepts built-in views (`front`, `top`, `iso`), `azimuth:elevation` angles, or an exact `proj/pos/target/up/fov` camera spec \u2014 pass it multiple times to render several viewpoints in one run. `--view` selects a named camera declared in `scene({ views })`; `--camera-json <file>` or `--scene <file>` give exact reproducible cameras without shell escaping. `--focus`/`--hide` filter visible objects; `--edges=<off|thin|bold>` controls the edge overlay.\n\nThis is the standard way to visually verify geometry from the CLI or in agent workflows. For path-traced quality use `render hq`; for edges only use `render wireframe`.",
|
|
32709
33058
|
usage: [
|
|
32710
33059
|
"forgecad render 3d <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [output.png] [--param Key=Value] [--joint JointName=Value] [--focus [names]] [--hide names] [--edges off|thin|bold] [options]"
|
|
32711
33060
|
],
|
|
32712
33061
|
examples: [
|
|
32713
|
-
"forgecad render 3d
|
|
32714
|
-
"forgecad render 3d
|
|
32715
|
-
'forgecad render 3d
|
|
32716
|
-
'forgecad render 3d examples/api/static-assembly-connectors.forge.js --hide "Bench.Slat0,Bench.Slat1"',
|
|
32717
|
-
"forgecad render 3d model.forge.js --view hero",
|
|
32718
|
-
"forgecad render 3d model.forge.js --camera 45:30",
|
|
33062
|
+
"forgecad render 3d model.forge.js",
|
|
33063
|
+
"forgecad render 3d model.forge.js --camera front --camera side --edges bold",
|
|
33064
|
+
'forgecad render 3d model.forge.js --focus "Bench.Slat*" -p "Wall Thickness=3"',
|
|
32719
33065
|
'forgecad render 3d model.forge.js --camera "proj=perspective;pos=200,-160,120;target=0,0,20;up=0,0,1;fov=38"',
|
|
32720
|
-
"forgecad render 3d model.forge.js --camera-json camera.json",
|
|
32721
|
-
"forgecad render 3d model.forge.js --scene scene.json",
|
|
32722
|
-
"forgecad render 3d model.forge.js --camera front --camera side",
|
|
32723
|
-
'forgecad render 3d model.forge.js -p "Wall Thickness=3"',
|
|
32724
|
-
"forgecad render 3d model.forge.js --edges bold",
|
|
32725
|
-
"forgecad render 3d model.forge.js --edges off",
|
|
32726
33066
|
"forgecad render 3d bracket.3mf bracket.png"
|
|
32727
33067
|
],
|
|
32728
33068
|
completion: {
|
|
32729
|
-
options:
|
|
33069
|
+
options: RENDER_MODEL_OPTIONS,
|
|
32730
33070
|
positionals: [
|
|
32731
33071
|
{ description: "Forge script or CAD asset", valueKind: "renderable" },
|
|
32732
33072
|
{ description: "output PNG path", valueKind: "png" }
|
|
@@ -32747,7 +33087,7 @@ var commands = [
|
|
|
32747
33087
|
"forgecad render wireframe examples/products/cup.forge.js --camera iso"
|
|
32748
33088
|
],
|
|
32749
33089
|
completion: {
|
|
32750
|
-
options:
|
|
33090
|
+
options: RENDER_MODEL_OPTIONS,
|
|
32751
33091
|
positionals: [
|
|
32752
33092
|
{ description: "Forge script or CAD asset", valueKind: "renderable" },
|
|
32753
33093
|
{ description: "output PNG path", valueKind: "png" }
|
|
@@ -32791,17 +33131,14 @@ var commands = [
|
|
|
32791
33131
|
group: "Modeling",
|
|
32792
33132
|
path: ["render", "hq"],
|
|
32793
33133
|
summary: "High-quality render via Blender Cycles \u2014 path-traced, HDRI, material presets.",
|
|
32794
|
-
description: "Exports the scene to Blender and renders with Cycles (path tracer)
|
|
33134
|
+
description: "Exports the scene to Blender and renders with Cycles (path tracer); requires Blender on PATH. Output defaults to `<script-name>-hq.png`.\n\n`--preset` picks the look (`studio`, `dramatic`, `clay`, `glass`, `metallic`, `toon`, `xray`, `normals`, `silhouette`, and more); `--samples` (default 256) trades quality vs speed; `--transparent` gives a compositing-ready background. Camera control (`--view`, `--camera`, `--camera-json`, `--scene <file>`) matches `render 3d`.",
|
|
32795
33135
|
usage: [
|
|
32796
33136
|
"forgecad render hq <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [output.png] [--param Key=Value] [--joint JointName=Value] [options]"
|
|
32797
33137
|
],
|
|
32798
33138
|
examples: [
|
|
32799
|
-
"forgecad render hq
|
|
32800
|
-
"forgecad render hq
|
|
32801
|
-
"forgecad render hq
|
|
32802
|
-
"forgecad render hq examples/products/cup.forge.js hero.png --camera-json camera.json",
|
|
32803
|
-
"forgecad render hq examples/products/cup.forge.js --preset clay --size 2048",
|
|
32804
|
-
"forgecad render hq examples/products/cup.forge.js --transparent --preset glass"
|
|
33139
|
+
"forgecad render hq model.forge.js",
|
|
33140
|
+
"forgecad render hq model.forge.js hero.png --preset dramatic --samples 1024",
|
|
33141
|
+
"forgecad render hq model.forge.js --transparent --preset glass --size 2048"
|
|
32805
33142
|
],
|
|
32806
33143
|
completion: {
|
|
32807
33144
|
options: [
|
|
@@ -32908,17 +33245,14 @@ var commands = [
|
|
|
32908
33245
|
group: "Modeling",
|
|
32909
33246
|
path: ["capture", "gif"],
|
|
32910
33247
|
summary: "Capture an animated GIF from a script via orbit, named joint playback, or section sweep.",
|
|
32911
|
-
description: "Renders an animated sequence
|
|
33248
|
+
description: "Renders an animated sequence: `--capture orbit` (default) for a turntable, `--capture animation --animation <name>` for a named joint clip, or `--capture section-sweep` to move a clipping plane through the model. `--cut-plane` keeps a static cross-section visible while animating; the orbit base or fixed camera comes from `--view`, `--camera`, `--camera-json`, or `--scene <file>`.",
|
|
32912
33249
|
usage: [
|
|
32913
33250
|
"forgecad capture gif <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [output.gif] [options] [--param Key=Value] [--joint JointName=Value]"
|
|
32914
33251
|
],
|
|
32915
33252
|
examples: [
|
|
32916
|
-
"forgecad capture gif
|
|
32917
|
-
|
|
32918
|
-
'forgecad capture gif model.forge.js out/
|
|
32919
|
-
"forgecad capture gif model.forge.js out/front.gif --camera front",
|
|
32920
|
-
"forgecad capture gif model.forge.js out/hero.gif --view hero",
|
|
32921
|
-
"forgecad capture gif examples/3d-printer.forge.js out/sweep.gif --capture section-sweep --sweep-plane YZ"
|
|
33253
|
+
"forgecad capture gif model.forge.js",
|
|
33254
|
+
"forgecad capture gif model.forge.js out/sweep.gif --capture section-sweep --sweep-plane YZ",
|
|
33255
|
+
'forgecad capture gif model.forge.js out/hero.gif --view hero --cut-plane "Front Section"'
|
|
32922
33256
|
],
|
|
32923
33257
|
completion: {
|
|
32924
33258
|
options: CAPTURE_COMMON_OPTIONS,
|
|
@@ -32938,12 +33272,8 @@ var commands = [
|
|
|
32938
33272
|
"forgecad capture mp4 <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [output.mp4] [options] [--param Key=Value] [--joint JointName=Value]"
|
|
32939
33273
|
],
|
|
32940
33274
|
examples: [
|
|
32941
|
-
"forgecad capture mp4
|
|
32942
|
-
"forgecad capture mp4
|
|
32943
|
-
'forgecad capture mp4 model.forge.js out/raw.mp4 --param "Output=raw-sdf"',
|
|
32944
|
-
"forgecad capture mp4 model.forge.js out/front.mp4 --camera front",
|
|
32945
|
-
"forgecad capture mp4 model.forge.js out/hero.mp4 --view hero",
|
|
32946
|
-
"forgecad capture mp4 examples/3d-printer.forge.js out/sweep.mp4 --capture section-sweep --sweep-plane YZ --sweep-frames 180"
|
|
33275
|
+
"forgecad capture mp4 model.forge.js out/four-bar.mp4 --view iso",
|
|
33276
|
+
"forgecad capture mp4 model.forge.js out/sweep.mp4 --capture section-sweep --sweep-plane YZ --sweep-frames 180"
|
|
32947
33277
|
],
|
|
32948
33278
|
completion: {
|
|
32949
33279
|
options: CAPTURE_COMMON_OPTIONS,
|
|
@@ -32958,15 +33288,14 @@ var commands = [
|
|
|
32958
33288
|
group: "Modeling",
|
|
32959
33289
|
path: ["render", "section"],
|
|
32960
33290
|
summary: "Render a 2D cross-section of a 3D model (cut by a plane) to SVG or PNG.",
|
|
32961
|
-
description: "Cuts all shapes
|
|
33291
|
+
description: "Cuts all shapes with an axis-aligned plane (default XY at Z=0; `--plane XZ|YZ` reorients, `--offset` shifts the cut) and writes a 2D cross-section drawing. The file extension picks the format: `.svg` (default, vector) or `.png` (rasterized at `--size` pixels); `--edges=<off|thin|bold>` sets the outline stroke. Useful for verifying internal geometry, wall thicknesses, and fits that aren't visible in 3D renders.",
|
|
32962
33292
|
usage: [
|
|
32963
33293
|
"forgecad render section <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [output.svg|.png] [--param Key=Value] [--joint JointName=Value] [--plane XY|XZ|YZ] [--offset <number>] [--size <px>] [--edges off|thin|bold] [--background <color>]"
|
|
32964
33294
|
],
|
|
32965
33295
|
examples: [
|
|
32966
|
-
"forgecad render section
|
|
32967
|
-
"forgecad render section
|
|
32968
|
-
"forgecad render section
|
|
32969
|
-
"forgecad render section examples/furniture/01-table.forge.js out/bold.svg --edges bold"
|
|
33296
|
+
"forgecad render section model.forge.js",
|
|
33297
|
+
"forgecad render section model.forge.js out/section.svg --plane XZ --offset 10",
|
|
33298
|
+
"forgecad render section model.forge.js out/section.png --size 2048"
|
|
32970
33299
|
],
|
|
32971
33300
|
completion: {
|
|
32972
33301
|
options: [
|
|
@@ -34081,13 +34410,12 @@ var commands = [
|
|
|
34081
34410
|
group: "Inspect",
|
|
34082
34411
|
path: ["inspect", "sketch"],
|
|
34083
34412
|
summary: "Inspect returned sketches and profile regions used by returned shapes.",
|
|
34084
|
-
description: "
|
|
34413
|
+
description: "External inspection: runs the model, then reads returned `Sketch`/`ConstraintSketch` objects and profile-bearing shape compile plans (extrude, cut, revolve) \u2014 model code never calls an inspection API. Reports filled selectable regions (sorted largest-first, run-local ids like `R0`) and excluded hole interiors. `--seed x,y` is the stable selection mechanism (not region ids): it dry-runs which region a point selector would consume, and seed failures exit nonzero (outside every region, on a boundary, inside a hole, ambiguous, or incompatible operation). `--operation extrude` checks only whether the selected filled region can be consumed by extrusion.",
|
|
34085
34414
|
usage: [
|
|
34086
34415
|
"forgecad inspect sketch <model.forge.js> [--json] [--object <name-or-path>] [--seed <x,y>] [--operation extrude] [--param Key=Value] [--joint JointName=Value] [--backend manifold|occt|truck] [--quality live|default|high]"
|
|
34087
34416
|
],
|
|
34088
34417
|
examples: [
|
|
34089
|
-
"forgecad inspect sketch
|
|
34090
|
-
'forgecad inspect sketch model.forge.js --object "Profile" --seed 45,15',
|
|
34418
|
+
"forgecad inspect sketch model.forge.js",
|
|
34091
34419
|
'forgecad inspect sketch model.forge.js --json --object "Body" --seed 45,15 --operation extrude'
|
|
34092
34420
|
],
|
|
34093
34421
|
completion: {
|
|
@@ -34218,7 +34546,7 @@ var commands = [
|
|
|
34218
34546
|
group: "Inspect",
|
|
34219
34547
|
path: ["inspect", "mechanical-integrity"],
|
|
34220
34548
|
summary: "Inspect generated ForgeCAD models for mechanical integrity failures.",
|
|
34221
|
-
description: "Scans a Forge script or a folder of generated projects and
|
|
34549
|
+
description: "Scans a Forge script or a folder of generated projects and flags timeouts, runtime errors, missing `verify.*` checks, missing executed mechanical-interface checks, fragmented named groups, uncontracted manual assemblies, and (when requested) positive-volume collisions and excessive physical component counts. Details suggest concrete repair patterns. With `--collisions`, the largest overlapping object pairs are listed by volume so the highest-risk interfaces get repaired first; exhausting the exact-check pair or time budget fails the file instead of silently passing a partial check, and truncated script-side joint-sweep validation fails explicitly. Script `console.warn()` diagnostics are reported as warnings; use `verify.*` checks or `console.error()` for gate-failing contracts.",
|
|
34222
34550
|
usage: [
|
|
34223
34551
|
"forgecad inspect mechanical-integrity <script-or-dir> [--json] [--param Key=Value] [--joint JointName=Value] [--timeout-ms <ms>] [--jobs <n>] [--min-verifications <n>] [--max-components <n>] [--collisions] [--min-overlap-volume <mm3>] [--collision-pair-limit <n>] [--collision-time-budget-ms <ms>] [--sweep-joint-step-limit <n>] [--all-forge] [--backend manifold|occt|truck]"
|
|
34224
34552
|
],
|
|
@@ -34385,7 +34713,7 @@ var commands = [
|
|
|
34385
34713
|
{ name: "--update", description: "Regenerate compiler snapshots" }
|
|
34386
34714
|
]
|
|
34387
34715
|
},
|
|
34388
|
-
run: async (args) => (await import("./check-compiler-
|
|
34716
|
+
run: async (args) => (await import("./check-compiler-JTVBITCR.js")).runCheckCompilerCli(args)
|
|
34389
34717
|
},
|
|
34390
34718
|
{
|
|
34391
34719
|
group: "Checks",
|
|
@@ -34408,7 +34736,7 @@ var commands = [
|
|
|
34408
34736
|
{ name: "--update", description: "Regenerate query-propagation snapshots" }
|
|
34409
34737
|
]
|
|
34410
34738
|
},
|
|
34411
|
-
run: async (args) => (await import("./check-query-propagation-
|
|
34739
|
+
run: async (args) => (await import("./check-query-propagation-3FFLSMVN.js")).runCheckQueryPropagationCli(args)
|
|
34412
34740
|
},
|
|
34413
34741
|
{
|
|
34414
34742
|
group: "Checks",
|