forgecad 0.10.3 → 0.10.5
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-raksfnNA.js} +1 -1
- package/dist/assets/{BenchmarkPage-Ds7Z2doN.js → BenchmarkPage-DP3RxhPs.js} +2 -2
- package/dist/assets/{BlogPage-DlPbpt6A.js → BlogPage-D7Dos-vl.js} +1 -1
- package/dist/assets/{DocsPage-vZb3b3Y0.js → DocsPage-DO1kvBns.js} +34 -43
- package/dist/assets/{EditorApp-HLoKfe15.js → EditorApp-DQJmcmRT.js} +51 -17
- package/dist/assets/{EmbedViewer--KnqBKrJ.js → EmbedViewer-DFDUhOma.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-C_LssmnA.js → LandingPageProofDriven-DbE_tp8-.js} +54 -36
- package/dist/assets/{LegalPage-DGsyo4n1.js → LegalPage-CominSso.js} +2 -2
- package/dist/assets/{PricingPage-BOE27B-R.js → PricingPage-CcVIN9yj.js} +2 -2
- package/dist/assets/{SettingsPage-f47cnk39.js → SettingsPage-DLWcP289.js} +1 -1
- package/dist/assets/{app-D6ccu2Xx.js → app-xW3hOdq9.js} +1343 -4004
- package/dist/assets/{backendInit-DbTkQN9J.js → backendInit-mDHk97u7.js} +12346 -3803
- package/dist/assets/cli/{render-BsngirjC.js → render--SIU27W_.js} +1909 -146
- package/dist/assets/{constructionHistoryWorker-PCwXrTDB.js → constructionHistoryWorker-uEe_Q7Kg.js} +2362 -835
- package/dist/assets/{evalWorker-CS63PfZu.js → evalWorker-BqyDHDcI.js} +7755 -3127
- package/dist/assets/{forgecad_geometry-CZ_IfuvA.js → forgecad_geometry-D8rWX7nQ.js} +1 -1
- package/dist/assets/{forgecad_geometry_bg-C3rQHfwg.wasm → forgecad_geometry_bg-ObqfqjJT.wasm} +0 -0
- package/dist/assets/{inspectWorker-Y4cOzNyA.js → inspectWorker-UXMxlcR8.js} +6550 -2943
- package/dist/assets/{jointPose-AMvCywzS.js → jointPose-bYMlwU3v.js} +1 -1
- package/dist/assets/{landing-proof-driven-ORyigZ6p.css → landing-proof-driven-_u4v_xQb.css} +71 -11
- package/dist/assets/{manifold-Crd_F2qx.js → manifold-BR7UYI4P.js} +1 -1
- package/dist/assets/{manifold-CBry38ly.js → manifold-CyOV5B9S.js} +2 -2
- package/dist/assets/{manifold-k2kRcc85.js → manifold-D4d5NQst.js} +1 -1
- package/dist/assets/{reportWorker-CWvn0CEv.js → reportWorker-DsaICZsn.js} +7320 -2827
- 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/CLI.md +4 -2
- package/dist/docs-raw/component-model.md +2 -2
- package/dist/docs-raw/generated/assembly.md +76 -3
- package/dist/docs-raw/generated/concepts.md +36 -5
- package/dist/docs-raw/generated/core.md +185 -21
- package/dist/docs-raw/generated/curves.md +344 -6
- package/dist/docs-raw/generated/runtime-names.md +12 -12
- package/dist/docs-raw/generated/sketch.md +16 -3
- package/dist/docs-raw/guides/inspection-bundles.md +5 -3
- package/dist/docs-raw/guides/structural-fea.md +224 -0
- 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/forgecad.md +1 -0
- 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-7YAHVXYM.js} +1 -1
- package/dist-cli/{check-query-propagation-HYSLTXAB.js → check-query-propagation-ZRR6IOJW.js} +1 -1
- package/dist-cli/{chunk-WLUKAW3H.js → chunk-VNM67DIV.js} +29671 -24865
- package/dist-cli/forgecad.js +5906 -714
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +853 -45
- package/dist-skill/SKILL.md +1 -0
- package/dist-skill/docs/CLI.md +4 -2
- package/dist-skill/docs/generated/assembly.md +73 -3
- package/dist-skill/docs/generated/core.md +185 -21
- package/dist-skill/docs/generated/curves.md +343 -6
- package/dist-skill/docs/generated/runtime-names.md +12 -12
- package/dist-skill/docs/generated/sketch.md +16 -3
- package/dist-skill/docs/guides/inspection-bundles.md +5 -3
- package/dist-skill/docs/guides/structural-fea.md +224 -0
- 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/forgecad.md +1 -0
- package/dist-skill/website/skills/index.md +9 -12
- package/examples/analysis/structural-stress-fea.forge.js +19 -0
- package/examples/api/blend-full-round.forge.js +37 -0
- package/examples/api/blend-variable-radius.forge.js +51 -0
- package/examples/api/curve-project-and-intersect.forge.js +59 -0
- package/examples/api/extrude-up-to-face.forge.js +47 -0
- package/examples/api/spoon-full-tang-handle.forge.js +148 -0
- package/examples/api/surface-boundarynet-dished-bowl.forge.js +63 -0
- package/examples/api/surface-fill-interior-constraints.forge.js +59 -0
- package/examples/api/texture-projection.forge.js +75 -0
- package/examples/assets/uv-grid.png +0 -0
- package/package.json +4 -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-DNPRKL_p.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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference-based extrude end conditions.
|
|
3
|
+
*
|
|
4
|
+
* Instead of guessing a numeric height, terminate an extrusion against a real
|
|
5
|
+
* reference: a face on another part, an arbitrary plane, or a vertex. The extent
|
|
6
|
+
* collapses to a numeric distance before the solid is built, so this works on
|
|
7
|
+
* every backend.
|
|
8
|
+
*
|
|
9
|
+
* - `extrude({ upToFace })` — flush with another shape's planar face.
|
|
10
|
+
* - `extrude({ upToPlane, offset })` — to a plane, then a margin past it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
scene({
|
|
14
|
+
camera: { position: [120, -140, 90], target: [0, 0, 25], fov: 32 },
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// A lid sitting above the build plate; its bottom face is the termination target.
|
|
18
|
+
const lid = box(60, 60, 6).translate(0, 0, 40).color('#9aa7b4');
|
|
19
|
+
|
|
20
|
+
// Boss grown from the plate exactly up to the lid's underside (z = 40).
|
|
21
|
+
const boss = circle2d(10).extrude({ upToFace: lid.face('bottom') }).color('#d98c5f');
|
|
22
|
+
|
|
23
|
+
// A post that rises to a plane at z = 30, then 4 mm past it (a press-fit stub).
|
|
24
|
+
const post = rect(10, 10)
|
|
25
|
+
.translate(22, 0)
|
|
26
|
+
.extrude({ upToPlane: { normal: [0, 0, 1], offset: 30 }, offset: 4 })
|
|
27
|
+
.color('#5f9ed9');
|
|
28
|
+
|
|
29
|
+
// A pin that stops at the plane through a vertex.
|
|
30
|
+
const pin = circle2d(4)
|
|
31
|
+
.translate(-22, 0)
|
|
32
|
+
.extrude({ upToVertex: [0, 0, 18] })
|
|
33
|
+
.color('#6fb86f');
|
|
34
|
+
|
|
35
|
+
verify.notEmpty('boss is a solid', boss);
|
|
36
|
+
verify.boundingBoxSize('boss reaches the lid underside (height 40)', boss, [20, 20, 40], 1);
|
|
37
|
+
verify.boundingBoxSize('post stops 4 mm past the z=30 plane', post, [10, 10, 34], 1);
|
|
38
|
+
verify.boundingBoxSize('pin stops at the z=18 vertex plane', pin, [8, 8, 18], 1);
|
|
39
|
+
|
|
40
|
+
return [
|
|
41
|
+
{ name: 'extrude up-to references', group: [
|
|
42
|
+
{ name: 'Lid', shape: lid },
|
|
43
|
+
{ name: 'Boss (upToFace)', shape: boss },
|
|
44
|
+
{ name: 'Post (upToPlane + offset)', shape: post },
|
|
45
|
+
{ name: 'Pin (upToVertex)', shape: pin },
|
|
46
|
+
] },
|
|
47
|
+
];
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spoon — v4 smooth-clamp metal bowl + full-tang polymer handle.
|
|
3
|
+
*
|
|
4
|
+
* Built like a full-tang kitchen knife, in two materials:
|
|
5
|
+
* - METAL HEAD: the smooth-clamp dished bowl (see the spoon tutorial / step5_v4),
|
|
6
|
+
* flowing into a flat steel TANG that runs the whole length of the handle.
|
|
7
|
+
* - POLYMER GRIP: a comfortable rounded handle (variableSweep) that is SPLIT by the
|
|
8
|
+
* tang into a top + bottom scale, so the steel tang reads as a liner line around
|
|
9
|
+
* the handle's equator — the classic full-tang look.
|
|
10
|
+
* - Exposed steel BOLSTER where the bowl meets the handle, a steel BUTT at the end,
|
|
11
|
+
* and three steel RIVETS pinning the scales to the tang.
|
|
12
|
+
*
|
|
13
|
+
* Two materials are returned as a colored group (steel + dark polymer).
|
|
14
|
+
*
|
|
15
|
+
* The bowl recipe is the key idea from the build: a smooth-clamped parabola
|
|
16
|
+
* f(s) = s / sqrt(1 + (s/S)^2), s = (y/hw)^2
|
|
17
|
+
* which is a pure (round) parabola in the visible bowl but C-infinity smooth, so the
|
|
18
|
+
* wall has no kink to ring around — the rim stays glassy, not wavy.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
scene({
|
|
22
|
+
camera: { position: [95, -260, 250], target: [20, 2, -6], fov: 36 },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ───────────────────────── 1. the v4 metal bowl ─────────────────────────
|
|
26
|
+
const H = 5.5; // rim level
|
|
27
|
+
const S = 5.0; // smooth-clamp knee
|
|
28
|
+
const stations = [
|
|
29
|
+
{ x: -92, hw: 0, lowZ: 5.0 },
|
|
30
|
+
{ x: -90.5, hw: 12, lowZ: 2.0 },
|
|
31
|
+
{ x: -88, hw: 17.5, lowZ: -0.5 },
|
|
32
|
+
{ x: -82, hw: 22.5, lowZ: -4.0 },
|
|
33
|
+
{ x: -67, hw: 28, lowZ: -8.5 },
|
|
34
|
+
{ x: -48, hw: 32, lowZ: -10.2 }, // widest & deepest
|
|
35
|
+
{ x: -28, hw: 29, lowZ: -8.4 },
|
|
36
|
+
{ x: -6, hw: 13, lowZ: -2.0 },
|
|
37
|
+
{ x: 12, hw: 8.4, lowZ: 2.5 },
|
|
38
|
+
{ x: 24, hw: 7.1, lowZ: 4.0 }, // neck
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
function smoothField(key) {
|
|
42
|
+
const pts = Curve.Fit(stations.map((s) => [s.x, s[key], 0]), { tolerance: 0.0005 }).sample(600);
|
|
43
|
+
return (x) => {
|
|
44
|
+
if (x <= pts[0][0]) return pts[0][1];
|
|
45
|
+
if (x >= pts[pts.length - 1][0]) return pts[pts.length - 1][1];
|
|
46
|
+
for (let i = 1; i < pts.length; i += 1) {
|
|
47
|
+
if (x <= pts[i][0]) {
|
|
48
|
+
const t = (x - pts[i - 1][0]) / (pts[i][0] - pts[i - 1][0]);
|
|
49
|
+
return pts[i - 1][1] + (pts[i][1] - pts[i - 1][1]) * t;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return pts[pts.length - 1][1];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const lowAt = smoothField('lowZ');
|
|
56
|
+
const hwAt = smoothField('hw');
|
|
57
|
+
|
|
58
|
+
function dishZ(x, y) {
|
|
59
|
+
const hw = hwAt(x);
|
|
60
|
+
const lz = lowAt(x);
|
|
61
|
+
if (hw < 1e-6) return lz + (H - lz) * S;
|
|
62
|
+
const s = (y / hw) * (y / hw);
|
|
63
|
+
const f = s / Math.sqrt(1 + (s / S) * (s / S)); // smooth-clamp parabola
|
|
64
|
+
return lz + (H - lz) * f;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const X0 = -97, X1 = 28, Y0 = -40, Y1 = 40;
|
|
68
|
+
const NX = 52, NY = 38;
|
|
69
|
+
const grid = [];
|
|
70
|
+
for (let i = 0; i < NX; i += 1) {
|
|
71
|
+
const x = X0 + ((X1 - X0) * i) / (NX - 1);
|
|
72
|
+
const row = [];
|
|
73
|
+
for (let j = 0; j < NY; j += 1) {
|
|
74
|
+
const y = Y0 + ((Y1 - Y0) * j) / (NY - 1);
|
|
75
|
+
row.push([x, y, dishZ(x, y)]);
|
|
76
|
+
}
|
|
77
|
+
grid.push(row);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const bowl = Surface.Net()
|
|
81
|
+
.cage(grid)
|
|
82
|
+
.degree(3, 3)
|
|
83
|
+
.toSheet()
|
|
84
|
+
.thicken(1.2, { resolution: 160 })
|
|
85
|
+
.trimByPlane([0, 0, -1], -H); // exact level rim
|
|
86
|
+
|
|
87
|
+
// ───────────────────────── 2. the rounded grip volume ─────────────────────────
|
|
88
|
+
const spine = Curve.Fit(
|
|
89
|
+
[
|
|
90
|
+
[16, 0, 4.0],
|
|
91
|
+
[34, 0, 4.4],
|
|
92
|
+
[74, 0, 4.2],
|
|
93
|
+
[110, 0, 3.5],
|
|
94
|
+
],
|
|
95
|
+
{ tolerance: 0.02 },
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const gripProfile = (width, thickness) => roundedRect(width, thickness, thickness / 2);
|
|
99
|
+
|
|
100
|
+
const grip = variableSweep(
|
|
101
|
+
spine,
|
|
102
|
+
[
|
|
103
|
+
{ t: 0.0, profile: gripProfile(15.0, 7.0) },
|
|
104
|
+
{ t: 0.16, profile: gripProfile(16.6, 9.0) },
|
|
105
|
+
{ t: 0.62, profile: gripProfile(13.6, 9.8) },
|
|
106
|
+
{ t: 1.0, profile: gripProfile(16.2, 9.2) },
|
|
107
|
+
],
|
|
108
|
+
{ edgeLength: 0.65, samples: 90, up: [0, 0, 1] },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// ───────────────────────── 3. split into tang + scales ─────────────────────────
|
|
112
|
+
const TANG_Z = 4.0; // grip equator
|
|
113
|
+
const TANG_T = 3.2; // tang thickness
|
|
114
|
+
const tangSlab = box(240, 70, TANG_T).translate(20, 0, TANG_Z); // flat slab at the equator
|
|
115
|
+
|
|
116
|
+
// Tang = grip ∩ slab (flush to the grip outline), fused into the bowl → metal head.
|
|
117
|
+
const tang = grip.intersect(tangSlab);
|
|
118
|
+
const metalHead = union(bowl, tang);
|
|
119
|
+
|
|
120
|
+
// Polymer scales = grip − slab, with the bolster (front) and butt (back) trimmed off
|
|
121
|
+
// so the steel shows there.
|
|
122
|
+
const bolsterCut = box(60, 70, 60).translate(2, 0, TANG_Z); // expose metal for x < 32
|
|
123
|
+
const buttCut = box(30, 70, 60).translate(121, 0, TANG_Z); // expose metal for x > 106
|
|
124
|
+
const scales = grip.subtract(tangSlab).subtract(bolsterCut).subtract(buttCut);
|
|
125
|
+
|
|
126
|
+
// ───────────────────────── 4. rivets ─────────────────────────
|
|
127
|
+
const rivetXs = [46, 72, 96];
|
|
128
|
+
const rivets = rivetXs.map((rx) =>
|
|
129
|
+
cylinder(14, 1.7).translate(rx, 0, TANG_Z - 7), // cylinder(height, radius); base at 0, along Z
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// ───────────────────────── 5. materials & assembly ─────────────────────────
|
|
133
|
+
const steel = (shape) => shape.color('#c9d0d8').material({ roughness: 0.22, metalness: 0.86 });
|
|
134
|
+
const polymer = (shape) => shape.color('#141619').material({ roughness: 0.55, metalness: 0.0 });
|
|
135
|
+
|
|
136
|
+
const wholeForMeasure = union(metalHead, scales, ...rivets);
|
|
137
|
+
|
|
138
|
+
verify.notEmpty('metal head (bowl + tang) is solid', metalHead);
|
|
139
|
+
verify.notEmpty('polymer scales are solid', scales);
|
|
140
|
+
verify.boundingBoxSize('full-tang spoon keeps its proportions', wholeForMeasure, [203, 69, 23], 16);
|
|
141
|
+
verify.lessThan('mid-bowl is deeper than the neck', dishZ(-48, 0), dishZ(24, 0) - 8);
|
|
142
|
+
verify.greaterThan('bowl has real spoon width', hwAt(-48) * 2, 50);
|
|
143
|
+
|
|
144
|
+
return [
|
|
145
|
+
steel(metalHead),
|
|
146
|
+
polymer(scales),
|
|
147
|
+
...rivets.map(steel),
|
|
148
|
+
];
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smooth closed-rim dished bowl built with ONLY the Boundary Surface primitive.
|
|
3
|
+
*
|
|
4
|
+
* This is the headline case for `Surface.BoundaryNet()`'s closed/periodic form:
|
|
5
|
+
* a dished bowl whose around-rim seam must be tangent-continuous (no G0 kink).
|
|
6
|
+
*
|
|
7
|
+
* Construction (a class-A "boundary surface" cage):
|
|
8
|
+
* - U runs radially, rim -> center (rings of decreasing radius, dishing down).
|
|
9
|
+
* - V runs around the rim; the first and last V columns coincide so the loop
|
|
10
|
+
* closes in position.
|
|
11
|
+
* - `.closedV()` welds those two ends into one smooth seam, so the surface has
|
|
12
|
+
* NO crease where the around-rim parameterization wraps.
|
|
13
|
+
*
|
|
14
|
+
* The innermost ring is a small non-degenerate disk (not a single pole), so the
|
|
15
|
+
* floor rounds over cleanly and `.thicken()` stays watertight.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
scene({
|
|
19
|
+
camera: { position: [140, -150, 120], target: [0, 0, -14], fov: 32 },
|
|
20
|
+
views: {
|
|
21
|
+
iso: { position: [140, -150, 120], target: [0, 0, -14], fov: 32 },
|
|
22
|
+
top: { position: [0, 0, 230], target: [0, 0, -12], fov: 32 },
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const RIM_RADIUS = 60;
|
|
27
|
+
const DEPTH = 22; // dish depth at the floor (down -z)
|
|
28
|
+
const AROUND = 48; // around-rim samples (V); seam wraps here
|
|
29
|
+
const RINGS = 10; // radial rings rim -> center (U)
|
|
30
|
+
const CENTER_SCALE = 0.02; // innermost ring radius as a fraction of the rim (near-closed floor, no hard pole)
|
|
31
|
+
|
|
32
|
+
// Radial profile: round dished bottom. t = 0 at rim, 1 at center.
|
|
33
|
+
// radius shrinks to the floor; z dips so the rim tangent rolls in and the floor
|
|
34
|
+
// is flat (cosine dish: flat tangent at BOTH the rim and the floor -> no throat).
|
|
35
|
+
function ringRadius(t) {
|
|
36
|
+
return RIM_RADIUS * (CENTER_SCALE + (1 - CENTER_SCALE) * (1 - t));
|
|
37
|
+
}
|
|
38
|
+
function ringDepth(t) {
|
|
39
|
+
return -DEPTH * 0.5 * (1 - Math.cos(Math.PI * t));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Build the (RINGS x AROUND+1) cage. Each row is a ring; columns go around.
|
|
43
|
+
// Column 0 and column AROUND are the SAME point so the V loop closes.
|
|
44
|
+
const cage = [];
|
|
45
|
+
for (let r = 0; r < RINGS; r++) {
|
|
46
|
+
const t = r / (RINGS - 1);
|
|
47
|
+
const radius = ringRadius(t);
|
|
48
|
+
const z = ringDepth(t);
|
|
49
|
+
const ring = [];
|
|
50
|
+
for (let a = 0; a <= AROUND; a++) {
|
|
51
|
+
const angle = (a / AROUND) * 2 * Math.PI; // a=0 and a=AROUND coincide
|
|
52
|
+
ring.push([radius * Math.cos(angle), radius * Math.sin(angle), z]);
|
|
53
|
+
}
|
|
54
|
+
cage.push(ring);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The Boundary Surface: Gordon fill of the cage, with the around-rim seam welded
|
|
58
|
+
// into a tangent-continuous loop via the closed/periodic form.
|
|
59
|
+
const bowl = Surface.BoundaryNet().cage(cage).degree(3, 3).closedV().thicken(1.2);
|
|
60
|
+
|
|
61
|
+
bowl.color('#b9c4cc');
|
|
62
|
+
|
|
63
|
+
return bowl;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* n-sided filled surface pulled onto interior features with `Surface.Fill`.
|
|
3
|
+
*
|
|
4
|
+
* `Surface.Fill` already fills any closed loop of n >= 3 boundary curves
|
|
5
|
+
* (energy-minimizing constrained fill on the OCCT backend). Two optional fields
|
|
6
|
+
* shape the INTERIOR of that fill without changing the rim:
|
|
7
|
+
*
|
|
8
|
+
* - `through` — interior constraint curves the surface must pass through. They
|
|
9
|
+
* are NOT part of the boundary loop; they pull the fill onto an internal
|
|
10
|
+
* feature line (here, a raised diagonal spine/crease).
|
|
11
|
+
* - `points` — isolated interior points the surface must interpolate (here, a
|
|
12
|
+
* single pinned bump lifted above the rim plane).
|
|
13
|
+
*
|
|
14
|
+
* The result is an open sheet, so `.thicken()` turns it into a thin solid.
|
|
15
|
+
* (Interior constraints require the default OCCT exact backend.)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
scene({
|
|
19
|
+
camera: { position: [120, -150, 110], target: [25, 25, 6], fov: 32 },
|
|
20
|
+
environment: { preset: 'studio' },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// A near-planar pentagon rim at z = 0 (five corners, five exact edges).
|
|
24
|
+
const corners = [
|
|
25
|
+
[0, 0, 0],
|
|
26
|
+
[50, 0, 0],
|
|
27
|
+
[62, 38, 0],
|
|
28
|
+
[25, 60, 0],
|
|
29
|
+
[-12, 38, 0],
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function edge(a, b) {
|
|
33
|
+
return Curve.Fit([corners[a], corners[b]]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const boundaries = [
|
|
37
|
+
{ name: 'a', curve: edge(0, 1) },
|
|
38
|
+
{ name: 'b', curve: edge(1, 2) },
|
|
39
|
+
{ name: 'c', curve: edge(2, 3) },
|
|
40
|
+
{ name: 'd', curve: edge(3, 4) },
|
|
41
|
+
{ name: 'e', curve: edge(4, 0) },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// Interior crease the fill must ride over: a raised diagonal spine.
|
|
45
|
+
const spine = Curve.Fit([
|
|
46
|
+
[8, 8, 3],
|
|
47
|
+
[28, 30, 7],
|
|
48
|
+
[50, 48, 3],
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
const skin = Surface.Fill({
|
|
52
|
+
boundaries,
|
|
53
|
+
through: [{ curve: spine }], // pull the fill onto the internal feature line
|
|
54
|
+
points: [[22, 14, 6]], // pin an interior bump above the rim plane
|
|
55
|
+
}).thicken(1.2);
|
|
56
|
+
|
|
57
|
+
skin.color('#bcd0d8');
|
|
58
|
+
|
|
59
|
+
return skin;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Bitmap Texturing: Projected UV
|
|
2
|
+
//
|
|
3
|
+
// Wrap an imported bitmap onto a surface with `Shape.wrapTexture(image,
|
|
4
|
+
// projection)`. The projection (a `Wrap.*` helper) decides how each point of the
|
|
5
|
+
// surface maps into the image — no UV unwrapping, no Math.sin/cos.
|
|
6
|
+
//
|
|
7
|
+
// The image's colored edges (top=red, right=green, bottom=blue, left=orange) and
|
|
8
|
+
// the dark top-left corner wedge make the mapping orientation easy to read on
|
|
9
|
+
// every shape.
|
|
10
|
+
//
|
|
11
|
+
// Two key properties:
|
|
12
|
+
// 1. AUTO-FIT — a bare `Wrap.flat/box/aroundCylinder` (no width/height/size)
|
|
13
|
+
// maps ONE copy of the image across the shape's extent. No magic numbers:
|
|
14
|
+
// the fit is read from the shape's bounding box.
|
|
15
|
+
// 2. WORLD-SPACE & BOOLEAN-SAFE — the UV is derived from each vertex's final
|
|
16
|
+
// world position at render time, so the texture survives cuts/unions AND so
|
|
17
|
+
// you must texture a shape *after* positioning it (auto-fit reads the final
|
|
18
|
+
// world box; see the rightmost part, textured *then* bored).
|
|
19
|
+
|
|
20
|
+
const tile = Param.number("Tile repeat", 1, { min: 1, max: 6, step: 1 });
|
|
21
|
+
|
|
22
|
+
const art = Import.image("assets/uv-grid.png");
|
|
23
|
+
|
|
24
|
+
// Laid out left-to-right in a tidy row on the z = 0 ground plane, ~25 mm gaps so
|
|
25
|
+
// nothing overlaps. box() and sphere() are origin-centered (lift them by half
|
|
26
|
+
// their height to sit on the ground); cylinder(height, radius) already has its
|
|
27
|
+
// base at z = 0, so the can needs no z lift.
|
|
28
|
+
//
|
|
29
|
+
// In every case: build → translate → wrapTexture LAST, so auto-fit reads the
|
|
30
|
+
// final world bounding box.
|
|
31
|
+
|
|
32
|
+
// 1. FLAT — lay the image onto one axis-aligned face (a logo on a plate's top).
|
|
33
|
+
// No width/height: auto-fit maps the face span to the image once. `repeat`
|
|
34
|
+
// tiles that single fitted copy.
|
|
35
|
+
const plate = box(50, 50, 8)
|
|
36
|
+
.translate(-140, 0, 4)
|
|
37
|
+
.wrapTexture(art, Wrap.flat({ onto: "top", repeat: [tile, tile] }));
|
|
38
|
+
|
|
39
|
+
// 2. AROUND A CYLINDER — wrap the image like a can label. `axis` is the cylinder
|
|
40
|
+
// axis; the seam falls on the -X side (the atan2 wrap at ±π). No height:
|
|
41
|
+
// auto-fit maps the can's full height to the image once.
|
|
42
|
+
const can = cylinder(55, 20)
|
|
43
|
+
.translate(-70, 0, 0)
|
|
44
|
+
.wrapTexture(art, Wrap.aroundCylinder({ axis: "z" }));
|
|
45
|
+
|
|
46
|
+
// 3. ONTO A SPHERE — longitude/latitude mapping, like a globe. Auto-fit centers
|
|
47
|
+
// the projection on the sphere wherever it sits.
|
|
48
|
+
const globe = sphere(26)
|
|
49
|
+
.translate(0, 0, 26)
|
|
50
|
+
.wrapTexture(art, Wrap.onSphere());
|
|
51
|
+
|
|
52
|
+
// 4. BOX (cube-map) — the dominant face axis picks one of three planar maps, so
|
|
53
|
+
// each of the six sides shows one copy of the image. No size: auto-fit reads
|
|
54
|
+
// the box extent.
|
|
55
|
+
const crate = box(40, 40, 40)
|
|
56
|
+
.translate(70, 0, 20)
|
|
57
|
+
.wrapTexture(art, Wrap.box());
|
|
58
|
+
|
|
59
|
+
// 5. BOOLEAN-SAFE — position the plate, texture it (auto-fit over the solid
|
|
60
|
+
// plate), THEN subtract a bore. The image keeps flowing across the top and
|
|
61
|
+
// around the freshly-cut hole because the UV is re-derived from world position
|
|
62
|
+
// every frame, not baked into the original mesh. (Texture-then-cut, no
|
|
63
|
+
// translate after — the world-space UV must be locked before the boolean.)
|
|
64
|
+
const bored = box(50, 50, 16)
|
|
65
|
+
.translate(140, 0, 8)
|
|
66
|
+
.wrapTexture(art, Wrap.flat({ onto: "top", repeat: [tile, tile] }))
|
|
67
|
+
.subtract(cylinder(40, 12).translate(140, 0, -12)); // through-hole, centered on the plate
|
|
68
|
+
|
|
69
|
+
return [
|
|
70
|
+
{ name: "Flat (plate top)", shape: plate },
|
|
71
|
+
{ name: "Cylinder (label)", shape: can },
|
|
72
|
+
{ name: "Sphere (globe)", shape: globe },
|
|
73
|
+
{ name: "Box (cube-map)", shape: crate },
|
|
74
|
+
{ name: "Boolean-safe (bored)", shape: bored },
|
|
75
|
+
];
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgecad",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.5",
|
|
4
4
|
"description": "Code-first parametric CAD for JavaScript/TypeScript, in the browser and CLI.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://forgecad.io",
|
|
@@ -44,6 +44,9 @@
|
|
|
44
44
|
"export": {
|
|
45
45
|
"description": "Export Forge models to manufacturing, simulation, and exchange formats."
|
|
46
46
|
},
|
|
47
|
+
"fea": {
|
|
48
|
+
"description": "Run, inspect, render, compare, and check structural FEA studies."
|
|
49
|
+
},
|
|
47
50
|
"inspect": {
|
|
48
51
|
"description": "Inspect ForgeCAD models with targeted visual, physical, and manufacturing evidence."
|
|
49
52
|
},
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
<!-- Generated by scripts/build-forgecad-skill.mjs — do not edit. Edit agent-skill-library/forgecad-blockout-model/SKILL.md instead. -->
|
|
2
|
-
|
|
3
|
-
# forgecad-blockout-model
|
|
4
|
-
|
|
5
|
-
Create rough high-level ForgeCAD concept models from simple primitives to explore layout, proportions, motion, and part relationships without production detail. Use when asked for a quick model sketch, blockout, spatial mockup, or intuitive low-detail 3D concept.
|
|
6
|
-
|
|
7
|
-
| Field | Value |
|
|
8
|
-
| --- | --- |
|
|
9
|
-
| Installed by | `forgecad skill install` |
|
|
10
|
-
| Source | `agent-skill-library/forgecad-blockout-model/SKILL.md` |
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Block Out a Model
|
|
15
|
-
|
|
16
|
-
A blockout is a spatial planning artifact: 3-7 simple masses that answer where parts go, whether a mechanism makes spatial sense, and what the silhouette, footprint, or motion envelope looks like. Not for print-ready geometry, exact fit, tolerances, or detail work.
|
|
17
|
-
|
|
18
|
-
| Need | Skill |
|
|
19
|
-
|------|-------|
|
|
20
|
-
| High-level 3D idea using simple masses | `forgecad-blockout-model` |
|
|
21
|
-
| Written concept or architecture before CAD | `forgecad-spec-by-walking-through-it` |
|
|
22
|
-
| Accurate, detailed, parametric ForgeCAD model | `forgecad-make-a-model` |
|
|
23
|
-
|
|
24
|
-
### Method
|
|
25
|
-
|
|
26
|
-
- Load the `forgecad` skill first; read its Core API and CLI docs. Load nothing else unless the concept demands it.
|
|
27
|
-
- Translate the idea into 3-7 conceptual parts BEFORE writing geometry: masses and zones (base, arm, payload, sweep volume, keep-out, hand access).
|
|
28
|
-
- One primitive stands in for many eventual details. A bounding box beats a fake detailed part. Never add detail as a substitute for clarity — simplify or reposition instead.
|
|
29
|
-
- Use round-number dimensions; "believably shaped" beats numerically correct. Name uncertainty honestly: `armLengthGuess`, `baseWidthApprox`, `clearanceEnvelope`.
|
|
30
|
-
- At most a handful of `param()` values, for comparing proportions. Do not parameterize every dimension.
|
|
31
|
-
- Color by meaning; keep each conceptual part visually distinct via color or opacity. Return named shape objects so the viewer can inspect the concept part-by-part.
|
|
32
|
-
- Ghost geometry: transparent volumes only for REAL physical envelopes — sweep arcs, keep-out volumes, approximate payloads, reach/access zones. Never decorative teaching overlays. Exaggerate tiny clearances when needed for readability.
|
|
33
|
-
- Even a blockout represents the object in its normal assembled state: no labels, legends, cutaways, or permanently exploded layouts (full rule in the `forgecad` skill).
|
|
34
|
-
- Leave out: fasteners and screw holes, wall thicknesses, fillets and blend radii, polished materials, hidden internal structure that does not affect the concept.
|
|
35
|
-
|
|
36
|
-
### Verify
|
|
37
|
-
|
|
38
|
-
Rendering is mandatory even for rough models. Run the script, then render from 2-3 angles with the installed `forgecad` CLI (syntax: the `forgecad` skill's CLI docs). Judge by the reader test:
|
|
39
|
-
|
|
40
|
-
- Can someone unfamiliar with the idea tell what each mass represents?
|
|
41
|
-
- Are proportions believable enough to discuss?
|
|
42
|
-
- Is motion or interference visible where it matters?
|
|
43
|
-
- Are unknowns shown honestly, not hidden behind fake detail?
|
|
44
|
-
|
|
45
|
-
If any answer is no, simplify or add clearer ghost volumes.
|
|
46
|
-
|
|
47
|
-
### Handoff
|
|
48
|
-
|
|
49
|
-
File placement and naming follow `forgecad-make-a-model` conventions; suffix the filename `-blockout` or `-concept` unless the user supplied a clearer name. Once the high-level questions are answered, stop. If the next question is real fit, tunable dimensions, part details, or manufacturing logic, switch to `forgecad-make-a-model` instead of refining the blockout indefinitely.
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
<!-- Generated by scripts/build-forgecad-skill.mjs — do not edit. Edit agent-skill-library/forgecad-component-model/SKILL.md instead. -->
|
|
2
|
-
|
|
3
|
-
# forgecad-component-model
|
|
4
|
-
|
|
5
|
-
Enforce the ForgeCAD Component Model when building multi-part assemblies. Parts build at origin, connectors position them, data flows down from parent. Use when building or reviewing any multi-file ForgeCAD project.
|
|
6
|
-
|
|
7
|
-
| Field | Value |
|
|
8
|
-
| --- | --- |
|
|
9
|
-
| Installed by | `forgecad skill install` |
|
|
10
|
-
| Source | `agent-skill-library/forgecad-component-model/SKILL.md` |
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Component Model
|
|
15
|
-
|
|
16
|
-
The React of CAD: a part is a function from props to `{ shape, connectors, metadata }`, built at origin in local space. Parts never position themselves — the assembly positions them via connectors.
|
|
17
|
-
|
|
18
|
-
### Rules
|
|
19
|
-
|
|
20
|
-
1. **Parts build at origin.** Geometry starts at `[0,0,0]` in local coordinates; no assembly-space offsets; internal structure derives from the part's own props only.
|
|
21
|
-
2. **Connectors are the only interface.** Declare via `.withConnectors({})`; axes point outward, mating is face-to-face (exception: prismatic joints share a co-directional slide axis). Mechanics: the forgecad skill's `docs/guides/positioning.md` and `docs/generated/assembly.md`.
|
|
22
|
-
3. **Assembly is pure composition.** Zero `translate()` to position structural parts, zero coordinate math — connect connectors, pass props down, read metadata up.
|
|
23
|
-
4. **Data flows down, never sideways.** Props down via `require('./part.forge.js', { Height: 20 })` overrides; metadata up via the part's return object; siblings NEVER import each other — the assembly mediates all sibling communication.
|
|
24
|
-
5. **Validate with `verify.*`, never `console.log` + `if`.**
|
|
25
|
-
|
|
26
|
-
### Part return shape
|
|
27
|
-
|
|
28
|
-
```js
|
|
29
|
-
return { shape, boltPattern, pinionZ }; // shape + metadata the parent may route to siblings
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### File structure
|
|
33
|
-
|
|
34
|
-
Default: ONE file per project-specific assembly — parts as sections, shared data as variables. Split only for cross-project reuse or past ~300 lines. Never split for "organization".
|
|
35
|
-
|
|
36
|
-
### Anti-patterns (reject on review)
|
|
37
|
-
|
|
38
|
-
- `shared-dims.js` — a file that only computes derived dimensions; the assembly derives and passes them.
|
|
39
|
-
- Sibling `require()` — e.g. `require('./motor-mount.forge.js')` inside `cover-plate.forge.js`; route through the parent.
|
|
40
|
-
- Assembly-space coordinates inside a part — a part knowing `pinionZ = 14` from a sibling's geometry; receive it as a prop.
|
|
41
|
-
- `translate()` to position a structural part in an assembly — add a connector instead.
|
|
42
|
-
- `console.log` + `if` validation — use `verify.*`.
|
|
43
|
-
- Bare `connector.neutral()` outside a reusable component library with compatibility checking.
|
|
44
|
-
|
|
45
|
-
### Design gate
|
|
46
|
-
|
|
47
|
-
Before committing any multi-part assembly:
|
|
48
|
-
|
|
49
|
-
1. Can you understand each part without reading other files?
|
|
50
|
-
2. Does the assembly contain zero coordinate math?
|
|
51
|
-
3. Do all inter-part relationships flow through connectors and props?
|
|
52
|
-
|
|
53
|
-
If any answer is no, refactor.
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
<!-- Generated by scripts/build-forgecad-skill.mjs — do not edit. Edit agent-skill-library/forgecad-reconstruction-benchmark/SKILL.md instead. -->
|
|
2
|
-
|
|
3
|
-
# forgecad-reconstruction-benchmark
|
|
4
|
-
|
|
5
|
-
Solve ForgeCAD CAD reconstruction benchmark or RL episodes in a prepared workspace by rebuilding a visible reference asset as readable parametric ForgeCAD in the fixed submission path, using visual and geometric self-checks while respecting sandbox limits.
|
|
6
|
-
|
|
7
|
-
| Field | Value |
|
|
8
|
-
| --- | --- |
|
|
9
|
-
| Installed by | `forgecad skill install` |
|
|
10
|
-
| Source | `agent-skill-library/forgecad-reconstruction-benchmark/SKILL.md` |
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## ForgeCAD Reconstruction Benchmark
|
|
15
|
-
|
|
16
|
-
Benchmark adaptation of `forgecad-3d-reconstruction`: same reconstruction loop, but with a fixed submission path, a local CLI wrapper, sandbox limits, and a short wall-clock budget. This sheet owns only those deltas — follow `forgecad-3d-reconstruction` for the reconstruction craft.
|
|
17
|
-
|
|
18
|
-
### Workspace
|
|
19
|
-
|
|
20
|
-
Read first: `AGENTS.md`, `task/instructions.md`, this skill, and `agent-home/.agents/skills/forgecad/SKILL.md` (API + CLI reference).
|
|
21
|
-
|
|
22
|
-
- Reference asset (evidence only): `task/reference/*`
|
|
23
|
-
- Final answer: `submission/main.forge.js`, unless `task/task.json` overrides
|
|
24
|
-
- CLI: the local wrapper `./bin/forgecad`, never global commands
|
|
25
|
-
- Scratch renders/notes: `outputs/`
|
|
26
|
-
- Stay inside the prepared workspace.
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
./bin/forgecad run submission/main.forge.js
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Rules and Done Criteria
|
|
33
|
-
|
|
34
|
-
- Deliverable is readable parametric ForgeCAD source at the required submission path. A clean parametric approximation beats an unreadable mesh mimic.
|
|
35
|
-
- The final source must not call `Import.mesh`, `Import.step`, `importMesh`, `importStep`, `readFile`, `readFileSync`, or any equivalent asset-embedding trick, and must contain no `compareWith(...)`, external reference paths, base64 payloads, or debug probes.
|
|
36
|
-
- Sandbox: allowed local tools only — no web search, no broad home-directory access, no subagents, no remote services.
|
|
37
|
-
- Before exit: the run command above succeeds; visual/section evidence checked after the last meaningful edit; model recognizable against the reference from the main views.
|
|
38
|
-
|
|
39
|
-
### Budget Discipline
|
|
40
|
-
|
|
41
|
-
A runnable first candidate beats a perfect investigation with no model.
|
|
42
|
-
|
|
43
|
-
1. Run the starter submission once before editing so syntax/runtime problems surface early.
|
|
44
|
-
2. Inspect only the highest-value reference evidence: dimensions, volume, object count, one visual or sectional view. If a reference render/inspect command fails or stalls, continue from task instructions, filename, starter code, and mechanical inference — do not burn the budget retrying.
|
|
45
|
-
3. Write a short reconstruction brief in `outputs/brief.md` (fields per `forgecad-3d-reconstruction`).
|
|
46
|
-
4. Edit the submission early: coarse bbox, orientation, and main masses first, then iterate coarse-to-fine per `forgecad-3d-reconstruction`.
|
|
47
|
-
5. 3MF references: the `forgecad run` source-structure table is evidence — account for every substantial 3MF item in the reconstruction (the final model may be one part or many).
|
|
48
|
-
6. `inspect section` probes are replayable against the candidate via `inspect replay` — the cheap way to verify an exact local measurement transfers. Mechanics: `forgecad-render-inspect`.
|
|
49
|
-
|
|
50
|
-
### Pointers
|
|
51
|
-
|
|
52
|
-
- `forgecad-3d-reconstruction` — the evidence → brief → coarse → fine loop and metric diagnosis
|
|
53
|
-
- `forgecad-render-inspect` — inspection bundles, section probes, replay
|
|
54
|
-
- forgecad skill at `agent-home/.agents/skills/forgecad/` — API and CLI syntax
|
|
55
|
-
- `forgecad-make-a-model` — general quality patterns only; its date-based file-placement workflow does not apply in benchmark workspaces
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
## Bundled Files
|
|
59
|
-
|
|
60
|
-
- `agents/openai.yaml`
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: forgecad-blockout-model
|
|
3
|
-
description: Create rough high-level ForgeCAD concept models from simple primitives to explore layout, proportions, motion, and part relationships without production detail. Use when asked for a quick model sketch, blockout, spatial mockup, or intuitive low-detail 3D concept.
|
|
4
|
-
forgecad-public: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Block Out a Model
|
|
8
|
-
|
|
9
|
-
A blockout is a spatial planning artifact: 3-7 simple masses that answer where parts go, whether a mechanism makes spatial sense, and what the silhouette, footprint, or motion envelope looks like. Not for print-ready geometry, exact fit, tolerances, or detail work.
|
|
10
|
-
|
|
11
|
-
| Need | Skill |
|
|
12
|
-
|------|-------|
|
|
13
|
-
| High-level 3D idea using simple masses | `forgecad-blockout-model` |
|
|
14
|
-
| Written concept or architecture before CAD | `forgecad-spec-by-walking-through-it` |
|
|
15
|
-
| Accurate, detailed, parametric ForgeCAD model | `forgecad-make-a-model` |
|
|
16
|
-
|
|
17
|
-
## Method
|
|
18
|
-
|
|
19
|
-
- Load the `forgecad` skill first; read its Core API and CLI docs. Load nothing else unless the concept demands it.
|
|
20
|
-
- Translate the idea into 3-7 conceptual parts BEFORE writing geometry: masses and zones (base, arm, payload, sweep volume, keep-out, hand access).
|
|
21
|
-
- One primitive stands in for many eventual details. A bounding box beats a fake detailed part. Never add detail as a substitute for clarity — simplify or reposition instead.
|
|
22
|
-
- Use round-number dimensions; "believably shaped" beats numerically correct. Name uncertainty honestly: `armLengthGuess`, `baseWidthApprox`, `clearanceEnvelope`.
|
|
23
|
-
- At most a handful of `param()` values, for comparing proportions. Do not parameterize every dimension.
|
|
24
|
-
- Color by meaning; keep each conceptual part visually distinct via color or opacity. Return named shape objects so the viewer can inspect the concept part-by-part.
|
|
25
|
-
- Ghost geometry: transparent volumes only for REAL physical envelopes — sweep arcs, keep-out volumes, approximate payloads, reach/access zones. Never decorative teaching overlays. Exaggerate tiny clearances when needed for readability.
|
|
26
|
-
- Even a blockout represents the object in its normal assembled state: no labels, legends, cutaways, or permanently exploded layouts (full rule in the `forgecad` skill).
|
|
27
|
-
- Leave out: fasteners and screw holes, wall thicknesses, fillets and blend radii, polished materials, hidden internal structure that does not affect the concept.
|
|
28
|
-
|
|
29
|
-
## Verify
|
|
30
|
-
|
|
31
|
-
Rendering is mandatory even for rough models. Run the script, then render from 2-3 angles with the installed `forgecad` CLI (syntax: the `forgecad` skill's CLI docs). Judge by the reader test:
|
|
32
|
-
|
|
33
|
-
- Can someone unfamiliar with the idea tell what each mass represents?
|
|
34
|
-
- Are proportions believable enough to discuss?
|
|
35
|
-
- Is motion or interference visible where it matters?
|
|
36
|
-
- Are unknowns shown honestly, not hidden behind fake detail?
|
|
37
|
-
|
|
38
|
-
If any answer is no, simplify or add clearer ghost volumes.
|
|
39
|
-
|
|
40
|
-
## Handoff
|
|
41
|
-
|
|
42
|
-
File placement and naming follow `forgecad-make-a-model` conventions; suffix the filename `-blockout` or `-concept` unless the user supplied a clearer name. Once the high-level questions are answered, stop. If the next question is real fit, tunable dimensions, part details, or manufacturing logic, switch to `forgecad-make-a-model` instead of refining the blockout indefinitely.
|