forgecad 0.9.15 → 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-CDyGUinA.js → AdminPage-DwYHz72L.js} +1 -1
- package/dist/assets/{BenchmarkPage-DfPMY_-d.js → BenchmarkPage-a9_f-1US.js} +1 -1
- package/dist/assets/{BlogPage-kF0fkdJT.js → BlogPage-DodHpvmf.js} +1 -1
- package/dist/assets/{DocsPage-B954L3YN.js → DocsPage-B5LePEuj.js} +8 -858
- package/dist/assets/{EditorApp-CuDLxKqL.css → EditorApp-BpjZgzk0.css} +148 -0
- package/dist/assets/EditorApp-QXsAISLR.js +16307 -0
- package/dist/assets/{EmbedViewer-C77B-TrF.js → EmbedViewer-DdEHGUMU.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-Cr6fXMDj.js → LandingPageProofDriven-yhhOodbf.js} +2 -2
- package/dist/assets/{LegalPage-Dzklqmmg.js → LegalPage-5RbKRGYK.js} +1 -1
- package/dist/assets/{PricingPage-zWXkvlwl.js → PricingPage-E3Rma7aV.js} +1 -1
- package/dist/assets/{SettingsPage-Bz0of4KQ.js → SettingsPage-BJZcM97j.js} +1 -1
- package/dist/assets/{app-D3kDkggg.js → app-DSYrDg0V.js} +1846 -352
- package/dist/assets/cli/{render-DSY3mMQa.js → render-ZMHR9HkV.js} +161 -70
- package/dist/assets/{constructionHistoryWorker-gpDo-uH2.js → constructionHistoryWorker-AwMMWSxg.js} +1104 -349
- package/dist/assets/{evalWorker-CU0Ke6DP.js → evalWorker-DbNs7Dkp.js} +5155 -3772
- package/dist/assets/{inspectWorker-COyp8XXA.js → inspectWorker-CZsCFtQT.js} +1415 -439
- package/dist/assets/{targets-B9sGB5nB.js → jointPose-DO6mnXn_.js} +71 -3
- package/dist/assets/{manifold-DNkrUWpA.js → manifold-BGlQBBH9.js} +1 -1
- package/dist/assets/{manifold-BRI5prcH.js → manifold-BU-tJwQh.js} +1 -1
- package/dist/assets/{manifold-C-3h2M7p.js → manifold-fy2MV7K1.js} +2 -2
- package/dist/assets/{reportWorker-CdBz5bNg.js → reportWorker-DO6hcQbh.js} +8474 -4549
- package/dist/assets/{scalar-sampling-budget-wJF98aY9.js → scalar-sampling-budget-o90NSNmF.js} +5347 -3906
- package/dist/assets/{scanProxyWorker-B-9VbLIs.js → scanProxyWorker-2GtDLk-R.js} +19 -6
- 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 +3 -1
- package/dist/docs-raw/CLI.md +65 -239
- package/dist/docs-raw/README.md +6 -0
- package/dist/docs-raw/component-model.md +17 -150
- package/dist/docs-raw/generated/assembly.md +159 -520
- package/dist/docs-raw/generated/concepts.md +245 -3491
- 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 +238 -112
- package/dist/docs-raw/generated/output.md +51 -76
- package/dist/docs-raw/generated/runtime-names.md +30 -22
- 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 +113 -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 -129
- 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 +4 -2
- package/dist/index.html +1 -1
- package/dist/llms.txt +1 -2
- package/dist/sitemap.xml +13 -13
- package/dist-cli/{check-compiler-SDX5QIXI.js → check-compiler-JTVBITCR.js} +1 -1
- package/dist-cli/{check-query-propagation-EAYEFT77.js → check-query-propagation-3FFLSMVN.js} +1 -1
- package/dist-cli/{chunk-N4O47JLF.js → chunk-OAN5T4XD.js} +5722 -4287
- package/dist-cli/forgecad.js +2195 -656
- package/dist-skill/CONTEXT.md +1778 -7912
- package/dist-skill/SKILL.md +15 -15
- package/dist-skill/docs/API/core/concepts.md +27 -157
- package/dist-skill/docs/CLI.md +65 -239
- package/dist-skill/docs/generated/assembly.md +160 -493
- 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 +238 -112
- package/dist-skill/docs/generated/output.md +51 -76
- package/dist-skill/docs/generated/runtime-names.md +16 -22
- 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 +111 -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 -131
- 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/helix-basics.forge.js +2 -2
- 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/route3d-elbow.forge.js +3 -0
- 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 +4 -2
- 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 +4 -1
- package/dist/assets/EditorApp-Beb-IZ0y.js +0 -14014
- 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/examples/api/bolted-service-cover.forge.js +0 -17
- package/examples/api/cable-gland-anchor.forge.js +0 -14
- package/examples/api/captured-cartridge-guide.forge.js +0 -14
- package/examples/api/captured-linear-slide.forge.js +0 -13
- package/examples/api/clevis-pin-joint.forge.js +0 -13
- package/examples/api/datum-enclosure.forge.js +0 -16
- package/examples/api/hose-barb-port.forge.js +0 -14
- package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
- package/examples/api/living-hinge-cover.forge.js +0 -14
- package/examples/api/pcb-terminal-block.forge.js +0 -22
- package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
- package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
- package/examples/api/routed-tube-clip.forge.js +0 -15
- package/examples/api/seated-bearing-stack.forge.js +0 -30
- package/examples/api/snap-latch-cover.forge.js +0 -14
- package/examples/api/thumb-screw-clamp.forge.js +0 -15
|
@@ -5,98 +5,37 @@ skill-order: 5
|
|
|
5
5
|
|
|
6
6
|
# Joint Design Recipes
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Geometry recipes for joints that actually rotate without binding — clevis-tongue hinges, hinge chains, hard stops.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
If a part must rotate, slide, or point in a specific physical direction, define connectors and use `assembly().connect()`. A connector is a small coordinate frame on the part:
|
|
13
|
-
|
|
14
|
-
- `origin` is the pivot, pin center, socket center, or contact point.
|
|
15
|
-
- `axis` is the hinge line or slide direction.
|
|
16
|
-
- `up` is the secondary direction that fixes the part's zero-angle twist.
|
|
17
|
-
|
|
18
|
-
For a hip -> knee -> wheel chain, the upper leg should be a real part with a hip connector and a knee connector. The hip connector frame defines how the upper leg sits on the hip drum at rest; the knee connector frame defines where the next part attaches. `connect()` aligns those frames and derives the joint axis.
|
|
19
|
-
|
|
20
|
-
Do not treat `up` as optional on hinges, wheels, levers, or keyed sliders. If `up` is omitted, ForgeCAD chooses a deterministic perpendicular vector, which keeps the model stable but may not match the intended mechanical rest pose.
|
|
21
|
-
|
|
22
|
-
Use `link()`, `edgeBetweenLinks()`, and `addAngleBetweenLinks()` for solved point skeletons and closed-loop relationships. A link is a point, not a bone frame. `addPart(..., { mate: { connector, toLink } })` only moves a connector origin onto a solved link point; it does not rotate the part to aim along an edge.
|
|
23
|
-
|
|
24
|
-
## Mirrored Revolute Axes
|
|
25
|
-
|
|
26
|
-
Revolute joint values are physical values signed by the right-hand rule around the joint axis. If a bilateral mechanism mirrors a hinge axis, the same numeric joint value does not mean "same pose" on both sides.
|
|
27
|
-
|
|
28
|
-
Example: a right ankle hinge with `axis: [1, 0, 0]` and a left ankle hinge with `axis: [-1, 0, 0]` are exact geometric mirrors at rest. But `+20` degrees around those two axes rotates the feet in opposite fore/aft senses. The mirrored pose uses `RightAnkle: 20` and `LeftAnkle: -20`.
|
|
29
|
-
|
|
30
|
-
When you expose physical joint limits directly, mirror revolute limits as `[min, max] -> [-max, -min]`. Prismatic joints do not have this angle-handedness flip because their scalar value translates along the mirrored axis.
|
|
31
|
-
|
|
32
|
-
For bilateral robots, legs, grippers, and suspension mechanisms, prefer a side-neutral control layer when possible: solve mirrored link positions with `link()`, `edgeBetweenLinks()`, and `addAngleBetweenLinks()`, then attach real parts to the solved skeleton. If you use connector-frame joints directly, make the sign mapping explicit in defaults, keyframes, and verification.
|
|
10
|
+
Anything that must rotate or slide gets connector frames: `origin` = pivot, `axis` = hinge line, `up` = rest twist. Always set `up` on hinges, wheels, and levers. Connector/link/mate semantics and mirrored-axis sign rules: see the assembly API reference.
|
|
33
11
|
|
|
34
12
|
## The Cavity Rule
|
|
35
13
|
|
|
36
|
-
Every
|
|
37
|
-
|
|
38
|
-
If two adjacent parts in an assembly show a collision volume larger than the expected clearance volume in `forgecad run`, one part is missing its cavity. Both parts have solid material at the same joint position. This will look fine at rest pose but will block rotation and produce confusing joint behavior.
|
|
14
|
+
Every joint is a **cavity** in one part plus a **tenon** in the other, and the cavity must be a real empty volume — not a gap implied by separate solids. A body that runs solid through the joint zone (e.g. a stadium cap under the clevis slot) blocks rotation even though the rest pose looks fine. End the body FLAT before the joint; extend the tines forward to the pivot; the inter-tine volume must be genuinely empty.
|
|
39
15
|
|
|
40
|
-
|
|
41
|
-
// BAD — body has a stadium cap at both ends; the "slot" between two clevis tines
|
|
42
|
-
// is just empty space next to a solid body cap. The next phalanx's tongue knuckle
|
|
43
|
-
// has nowhere to go (it intersects the previous body's cap).
|
|
44
|
-
const body = stadiumBar(L); // cap at X=0 AND X=L
|
|
45
|
-
const tine1 = box(...).translate(L, Y_OFF, 0);
|
|
46
|
-
const tine2 = box(...).translate(L, -Y_OFF, 0);
|
|
47
|
-
let phalanx = union(body, tine1, tine2);
|
|
16
|
+
Diagnostic: adjacent-part collision volume > expected clearance in `forgecad run` = missing cavity (both parts have solid material at the joint position). After fixing, the collision volume should drop to ~0 (or a few mm³ of clearance overlap).
|
|
48
17
|
|
|
49
|
-
|
|
50
|
-
// The X = L-KNUCK_R..L+KNUCK_R volume between the tines is genuinely empty.
|
|
51
|
-
const body = box(L - KNUCK_R, TONG_T, H).translate((L - KNUCK_R) / 2, 0, -H / 2);
|
|
52
|
-
const tongueKnuckle = knuckleDisc(0, 0, TONG_T); // proximal cap only
|
|
53
|
-
let phalanx = union(tongueKnuckle, body, tine1, tine2, ...tineCaps);
|
|
54
|
-
```
|
|
18
|
+
## Structural Sizing
|
|
55
19
|
|
|
56
|
-
|
|
20
|
+
**Yoke (connecting cantilevers).** Clevis tines at Y = ±Y_OFF are physically disconnected from a body of thickness TONG_T when Y_OFF > TONG_T/2 + clearance — the tines float and would snap under load. Always bridge with a yoke slab spanning the full clevis width, `(Y_OFF + TINE_T/2) * 2`, with a few mm of structural overlap along the joint axis, so material runs continuously from body to each tine.
|
|
57
21
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
A clevis tine arm at Y=±Y_OFF is geometrically separate from a body at Y=±TONG_T/2. With Y_OFF > TONG_T/2 + clearance, there is a **physical gap** between them. The tines float — they would snap off as soon as load is applied.
|
|
61
|
-
|
|
62
|
-
Always add a **yoke**: a short slab spanning the full clevis width, sitting between the body's flat distal end and the tines' attachment point. The yoke fills the Y gap so material is continuous from the body through to each tine.
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
const yokeLen = 3; // a few mm of structural overlap
|
|
66
|
-
const yokeStart = L - KNUCK_R - yokeLen;
|
|
67
|
-
const totalY = (Y_OFF + TINE_T / 2) * 2; // full clevis width
|
|
68
|
-
const yoke = box(yokeLen, totalY, H)
|
|
69
|
-
.translate(yokeStart + yokeLen / 2, 0, -H / 2);
|
|
70
|
-
phalanx = union(phalanx, yoke);
|
|
71
|
-
```
|
|
22
|
+
**Knuckle radius.** For body height H, require `KNUCK_R >= H/2`. Smaller, and the body corners protrude past the knuckle's cylindrical envelope and sweep into the adjacent part during rotation. `KNUCK_R = H/2` makes the body cross-section a stadium that exactly fits the envelope.
|
|
72
23
|
|
|
73
24
|
## Hard Stops vs Slider Limits
|
|
74
25
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
For a **geometric** hard stop (parts can't backbend past extension, or can't curl past full closure), add a small protrusion on one part that interferes with the other at the limit angle:
|
|
78
|
-
|
|
79
|
-
- **Extension stop at 0°** (typical for fingers, knees, elbows): add a small "lip" on the dorsal side of the proximal end of the child phalanx, sized so it just touches the parent's distal dorsal corner at 0°. Negative rotation (backbending) is then blocked by part-on-part contact.
|
|
80
|
-
- **Flexion stop at θmax**: add a similar lip on the palmar side, or rely on the body-to-body collision when bodies meet.
|
|
26
|
+
Declared joint min/max are not geometry — they only constrain the viewport slider; the geometry still permits any rotation. A physical stop requires an interfering protrusion:
|
|
81
27
|
|
|
82
|
-
|
|
28
|
+
- **Extension stop at 0°**: a small lip on the dorsal side of the child's proximal end, sized to just touch the parent's distal dorsal corner at 0°; backbending is then blocked by contact.
|
|
29
|
+
- **Flexion stop at θmax**: a palmar lip, or body-on-body contact when bodies meet.
|
|
83
30
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
For a clevis-tongue joint with body height H, the tongue knuckle radius and clevis tine knuckle radius must satisfy:
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
KNUCK_R >= H / 2
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
If the knuckle radius is smaller than the body's half-height, the body's corners protrude beyond the knuckle envelope. When the joint rotates, those corners sweep through space outside the cylindrical envelope and collide with the adjacent part.
|
|
93
|
-
|
|
94
|
-
Setting `KNUCK_R = H / 2` exactly makes the body cross-section a stadium that perfectly fits the knuckle envelope.
|
|
31
|
+
Verify: ~0 mm³ collision exactly at the limit pose (just touching), non-zero past it.
|
|
95
32
|
|
|
96
33
|
## Verification Workflow
|
|
97
34
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
35
|
+
Check the loop, not just the rest pose:
|
|
36
|
+
|
|
37
|
+
1. Build at rest; `forgecad run`; check collision volumes.
|
|
38
|
+
2. Overlap > clearance volume between joint neighbors → apply the cavity rule.
|
|
39
|
+
3. Render each part with `--focus PartName`; the clevis end must show a visible gap between tines.
|
|
40
|
+
4. Re-check at swept angles (30°/60°/90°) — rotation reveals collisions the rest pose hides.
|
|
41
|
+
5. Backbend test at -10°: blocked = hard stop exists; rotates = add a stop.
|
|
@@ -3,169 +3,47 @@ skill-group: geometry
|
|
|
3
3
|
skill-order: 3
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Positioning
|
|
6
|
+
# Positioning Decision Ladder
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Most positioning bugs come from manual coordinate arithmetic. Pick the **highest applicable rung**; drop down only when the rung above doesn't fit.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## 1. Connectors + `matchTo()` — every real part-to-part interface
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Mechanisms: connector frames vs link points
|
|
15
|
-
|
|
16
|
-
For serial articulated parts (hinges, hips, knees, levers, wheels), use `assembly().connect()` with connectors. `connect()` aligns the full connector frame: `origin` is the pivot/contact point, `axis` is the hinge line or slide direction, and `up` locks the zero-angle twist of the child part. `up` is a local roll reference, not world up; author it explicitly whenever the rest pose matters.
|
|
17
|
-
|
|
18
|
-
Do not use `addPart(..., { mate: { connector, toLink } })` when the part must point along a bone or inherit orientation from a link edge. Link mates are point attachments only: they translate the connector origin onto the solved link position and preserve the part's existing rotation. That is good for markers, sensors, labels, and debug handles. It is not a bone-frame API.
|
|
19
|
-
|
|
20
|
-
Use link graphs (`link()`, `edgeBetweenLinks()`, `addAngleBetweenLinks()`) when the hard part is solving point positions, especially closed loops. Use connector-frame joints when the hard part is orienting real physical parts.
|
|
21
|
-
|
|
22
|
-
For bilateral mechanisms, remember that connector-frame revolute values are physical axis-local values. Mirrored hinge axes need negated physical revolute values for the same mirrored pose, and physical limits mirror as `[min, max] -> [-max, -min]`. If you want `HipR: 10` and `HipL: 10` to mean the same semantic pose, drive a mirrored link graph or write an explicit state mapping instead of assuming equal connector-joint values are symmetric.
|
|
23
|
-
|
|
24
|
-
## Primitive origin convention
|
|
25
|
-
|
|
26
|
-
All 3D primitives are **centered on XY, base at Z=0**:
|
|
27
|
-
|
|
28
|
-
| Primitive | X range | Y range | Z range |
|
|
29
|
-
|-----------|---------|---------|---------|
|
|
30
|
-
| `box(60, 40, 20)` | [-30, 30] | [-20, 20] | [0, 20] |
|
|
31
|
-
| `cylinder(50, 10)` | [-10, 10] | [-10, 10] | [0, 50] |
|
|
32
|
-
| `sphere(15)` | [-15, 15] | [-15, 15] | [-15, 15] |
|
|
33
|
-
| `torus(20, 5)` | [-25, 25] | [-25, 25] | [-5, 5] |
|
|
34
|
-
|
|
35
|
-
Sphere and torus are fully centered (symmetric in Z). Box and cylinder sit on the XY ground plane — **Z goes up from zero, never negative**.
|
|
36
|
-
|
|
37
|
-
This means `box(w, d, h).translate(0, 0, -h / 2)` is the manual way to "center on Z" — it moves the box from `[0, h]` to `[-h / 2, h / 2]`. Prefer `box(w, d, h).placeReference('center', [0, 0, 0])` when you want full XYZ centering.
|
|
38
|
-
|
|
39
|
-
Do not assume `center: true` or a positional `true` gives OpenSCAD-style full XYZ centering. Primitive placement is fixed unless the primitive docs explicitly say otherwise.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
Most positioning bugs come from manual coordinate arithmetic. Use these methods in priority order.
|
|
44
|
-
|
|
45
|
-
## 1. Connectors + `matchTo()` — default for mating interfaces
|
|
46
|
-
|
|
47
|
-
Define connectors on parts; `matchTo()` provides automatic alignment. With one connector pair, the child translates and rotates so its connector aligns with the target's — origins coincide, axes oppose (plug-in model), and `up` pins the roll reference. With multiple connector pairs, the connector origins define the rigid transform; still author meaningful `axis` and `up` values so the same connectors remain useful for `connect()`, audits, and future matching.
|
|
12
|
+
**Rule 0:** if parts are meant to stay in contact, define connectors and `matchTo()` — including *static* assemblies (furniture, enclosures, fixtures, toys), not just mechanisms. Connectors win because they are **stable** (don't shift on fillet/chamfer/boolean), **semantic** (type/gender), **oriented** (full frame), **queryable** (`verify.connectorDistance`), and **explode-aware**.
|
|
48
13
|
|
|
49
14
|
```javascript
|
|
50
|
-
const shelf = box(200, 120, 10).
|
|
51
|
-
|
|
15
|
+
const shelf = box(200, 120, 10).withConnectors({
|
|
16
|
+
tab: connector.male("dovetail", { origin: [-100, 0, 5], axis: [-1, 0, 0], up: [0, 0, 1] }),
|
|
52
17
|
});
|
|
53
|
-
const
|
|
54
|
-
shelf_0: connector.female("dovetail", { origin: [6, 0, -50], axis: [1, 0, 0], up: [0, 0, 1] }),
|
|
55
|
-
});
|
|
56
|
-
const placed = shelf.matchTo(panel, "left_tab", "shelf_0");
|
|
57
|
-
// Dictionary form for multiple pairs on same target:
|
|
58
|
-
const placed2 = shelf.matchTo(panel, { left_tab: "shelf_0" });
|
|
59
|
-
// Named group children bubble connectors via dotted paths:
|
|
60
|
-
const cabinet = group({ name: "Left", shape: panel });
|
|
61
|
-
shelf.matchTo(cabinet, "left_tab", "Left.shelf_0");
|
|
18
|
+
const placed = shelf.matchTo(panel, "tab", "shelf_slot");
|
|
62
19
|
```
|
|
63
20
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
For a non-mechanism fixed-assembly example, see `examples/api/static-assembly-connectors.forge.js`.
|
|
21
|
+
Alignment semantics, dictionary form, and dotted group paths: see `matchTo()` JSDoc. For cross-file part alignment, prefer connectors over `withReferences()` placement points.
|
|
67
22
|
|
|
68
23
|
## 2. `group()` — local coordinates for multi-part assemblies
|
|
69
24
|
|
|
70
|
-
|
|
25
|
+
Build sub-parts at the **local origin**, group them, then translate the group **once** — never add a parent's global offset to every sub-part. Groups nest; each level has its own local origin. Groups cannot be booleaned: do subtract/intersect first in local coordinates, then group the result. Worked example: `group()` JSDoc.
|
|
71
26
|
|
|
72
|
-
|
|
73
|
-
// BAD — every sub-part repeats the parent's global position
|
|
74
|
-
const unitY = -18, unitZ = 70;
|
|
75
|
-
const body = lib.roundedBox(100, 20, 32, 4).translate(0, unitY, unitZ);
|
|
76
|
-
const panel = box(98, 2, 18).translate(0, unitY - 12, unitZ + 4);
|
|
77
|
-
const louver = box(88, 2, 6).translate(0, unitY - 14, unitZ - 11);
|
|
78
|
-
const led = sphere(1.2).translate(35, unitY - 12, unitZ + 9);
|
|
79
|
-
|
|
80
|
-
// GOOD — build at local origin, group, translate once
|
|
81
|
-
const body = lib.roundedBox(100, 20, 32, 4);
|
|
82
|
-
const panel = box(98, 2, 18).translate(0, -12, 4); // relative to local origin
|
|
83
|
-
const louver = box(88, 2, 6).translate(0, -14, -11); // relative to local origin
|
|
84
|
-
const led = sphere(1.2).translate(35, -12, 9); // relative to local origin
|
|
85
|
-
const indoorUnit = group(
|
|
86
|
-
{ name: 'Body', shape: body },
|
|
87
|
-
{ name: 'Panel', shape: panel },
|
|
88
|
-
{ name: 'Louver', shape: louver },
|
|
89
|
-
{ name: 'LED', shape: led },
|
|
90
|
-
).translate(0, -18, 70); // ONE translate for the whole assembly
|
|
91
|
-
```
|
|
27
|
+
## 3. `pointAlong()` — orient before positioning
|
|
92
28
|
|
|
93
|
-
|
|
29
|
+
Always call `pointAlong()` **before** `matchTo()`/`translate()` — it reorients around the origin.
|
|
94
30
|
|
|
95
|
-
|
|
96
|
-
const fan = group(hub, ...blades).translate(0, 25, 0); // fan assembly
|
|
97
|
-
const outdoorUnit = group(
|
|
98
|
-
{ name: 'Body', shape: casing },
|
|
99
|
-
{ name: 'Fan', shape: fan }, // already a group
|
|
100
|
-
{ name: 'Grille', shape: grille },
|
|
101
|
-
).translate(0, 23, -42); // position the whole outdoor unit
|
|
102
|
-
```
|
|
31
|
+
## 4. `attachTo()` — rough bounding-box placement only
|
|
103
32
|
|
|
104
|
-
|
|
33
|
+
Anchor points shift after fillet/chamfer/boolean: fine for quick prototyping, fragile for assembly interfaces — promote real interfaces to connectors.
|
|
105
34
|
|
|
106
|
-
##
|
|
35
|
+
## 5. `placeReference()` — land a named anchor on a world coordinate
|
|
107
36
|
|
|
108
|
-
|
|
109
|
-
// BAD
|
|
110
|
-
const pipe = cylinder(100, 5).rotateX(90).translate(x, y, z);
|
|
111
|
-
// GOOD — reads as "pipe pointing along Y"
|
|
112
|
-
const pipe = cylinder(100, 5).pointAlong([0, 1, 0]).translate(x, y, z);
|
|
113
|
-
```
|
|
37
|
+
Grounding, centering, edge alignment, custom reference points: see `placeReference()` JSDoc.
|
|
114
38
|
|
|
115
|
-
|
|
39
|
+
## 6. Last resort: `rotateAroundTo()`, `moveToLocal()`, `translate()`
|
|
116
40
|
|
|
117
|
-
|
|
41
|
+
For computed offsets and free-floating or exploratory layout. Raw `translate()`/`rotate()` is correct only when parts are intentionally unrelated.
|
|
118
42
|
|
|
119
|
-
|
|
120
|
-
const column = cylinder(50, 8).attachTo(base, 'top', 'bottom');
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
`child.attachTo(parent, parentAnchor, selfAnchor, offset)`. Anchor points shift on fillet/chamfer/boolean — fragile for assembly interfaces, fine for quick prototyping.
|
|
124
|
-
|
|
125
|
-
## 5. `rotateAroundTo()` — aim a point around a hinge/axis
|
|
126
|
-
|
|
127
|
-
```javascript
|
|
128
|
-
const aimed = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 20]);
|
|
129
|
-
// Exact line solve:
|
|
130
|
-
const lineHit = arm.rotateAroundTo([0, 0, 1], [0, 0, 0], "tip", [30, 30, 0], { mode: 'line' });
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## 6. `moveToLocal()` — offset from another shape's min corner
|
|
134
|
-
|
|
135
|
-
```javascript
|
|
136
|
-
const part = box(20, 20, 30).moveToLocal(base, 10, 10, 10);
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## 7. `translate()` — for simple offsets or bridging computed locations
|
|
140
|
-
|
|
141
|
-
```javascript
|
|
142
|
-
const pipeLen = bb2.min[1] - bb1.max[1];
|
|
143
|
-
const pipe = cylinder(pipeLen, 5).pointAlong([0, 1, 0]).translate(40, (bb1.max[1] + bb2.min[1]) / 2, bb1.min[2] + 15);
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## 8. `placeReference()` — align any anchor to a world coordinate
|
|
147
|
-
|
|
148
|
-
Place a shape so a named anchor point lands exactly where you want it. Accepts all built-in anchors (`'bottom'`, `'center'`, `'top-front-left'`, etc.) plus custom references from `withReferences()`.
|
|
149
|
-
|
|
150
|
-
```javascript
|
|
151
|
-
// Ground a shape — bottom face center at Z = 0
|
|
152
|
-
const grounded = shape.placeReference('bottom', [0, 0, 0])
|
|
43
|
+
## Mechanisms
|
|
153
44
|
|
|
154
|
-
|
|
155
|
-
const centered = shape.placeReference('center', [0, 0, 0])
|
|
45
|
+
Link graphs (`link()`, `edgeBetweenLinks()`) solve **point positions** (closed loops); connector-frame joints (`assembly().connect()`) **orient physical parts**. Frame semantics and mirrored-revolute rules: assembly docs; joint geometry: `guides/joint-design.md`.
|
|
156
46
|
|
|
157
|
-
|
|
158
|
-
const aligned = shape.placeReference('left', [10, 0, 0])
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Also works with custom placement references for cross-file parts:
|
|
162
|
-
|
|
163
|
-
```javascript
|
|
164
|
-
// widget.forge.js — define once
|
|
165
|
-
return union(base, post).withReferences({ points: { mount: [0, -16, -4] } });
|
|
166
|
-
|
|
167
|
-
// importer — consume
|
|
168
|
-
const widget = require("./widget.forge.js").placeReference("mount", [120, 40, 0]);
|
|
169
|
-
```
|
|
47
|
+
## Primitive placement
|
|
170
48
|
|
|
171
|
-
|
|
49
|
+
Box and cylinder sit base-at-Z=0, centered on XY; sphere and torus are fully centered. There is no OpenSCAD-style `center: true` — placement is fixed; use `placeReference('center', [0, 0, 0])` to fully center. Exact per-axis extents: primitive JSDoc.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill-group: recipes
|
|
3
|
+
skill-order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Scene Presentation Recipes
|
|
7
|
+
|
|
8
|
+
Worked `scene()` setups. The option schema and behavioral cliffs (e.g. setting `lights` replaces all defaults) live in the `scene()` API docs (viewport group) — this file is copyable recipes only.
|
|
9
|
+
|
|
10
|
+
## Baseline studio setup
|
|
11
|
+
|
|
12
|
+
Every model deserves at least this; default lighting looks flat.
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
scene({
|
|
16
|
+
background: { top: '#1a1a2e', bottom: '#0a0a14' },
|
|
17
|
+
camera: { position: [x, y, z], target: [0, 0, 0], fov: 42 },
|
|
18
|
+
environment: { preset: 'studio', intensity: 0.6 },
|
|
19
|
+
lights: [
|
|
20
|
+
{ type: 'ambient', color: '#c8cdd4', intensity: 0.15 },
|
|
21
|
+
{ type: 'directional', position: [80, -60, 120], target: [0, 0, 0], color: '#fff4e0', intensity: 1.8, castShadow: true },
|
|
22
|
+
{ type: 'directional', position: [-60, 40, 80], target: [0, 0, 0], color: '#b0c4de', intensity: 0.7 },
|
|
23
|
+
],
|
|
24
|
+
ground: { visible: true, color: '#111118', height: -10, receiveShadow: true },
|
|
25
|
+
postProcessing: {
|
|
26
|
+
bloom: { intensity: 0.3, threshold: 0.85, radius: 0.3 },
|
|
27
|
+
vignette: { darkness: 0.5, offset: 0.4 },
|
|
28
|
+
toneMappingExposure: 1.3,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Adapt to the material family: metallic/jewelry → `studio`, exposure 1.2–1.5, subtle bloom; organic/wood/matte → `warehouse`/`apartment`, warmer ambient, lower bloom; dark/dramatic → `night`, bloom + vignette. Ground with `receiveShadow: true` only for objects that stand on something.
|
|
34
|
+
|
|
35
|
+
Camera: 3/4 angle, `fov` 35–50 (lower = flatter/telephoto), `target` at the visual center of mass — not necessarily `[0,0,0]`.
|
|
36
|
+
|
|
37
|
+
## Matte industrial hero shot
|
|
38
|
+
|
|
39
|
+
For mechanisms, tools, product prototypes, and vehicles, prefer a matte studio look over gloss or atmosphere (ranges = tuning room, not options syntax):
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
scene({
|
|
43
|
+
background: { top: '#c3ccd7', bottom: '#566474' },
|
|
44
|
+
camera: { position: [430, -540, 340], target: [0, 30, 125], fov: 38 },
|
|
45
|
+
environment: { preset: 'studio', intensity: 0.2, background: false }, // 0.15–0.25
|
|
46
|
+
lights: [
|
|
47
|
+
{ type: 'ambient', color: '#efe7dc', intensity: 0.15 }, // 0.12–0.2
|
|
48
|
+
{ type: 'directional', position: [260, -320, 420], color: '#ffe2bf', intensity: 2.8, castShadow: true }, // 2.6–3.2
|
|
49
|
+
{ type: 'directional', position: [-260, 210, 220], color: '#d4e6fb', intensity: 0.85 }, // 0.7–1.0
|
|
50
|
+
{ type: 'hemisphere', skyColor: '#c7d3df', groundColor: '#495463', intensity: 0.15 }, // 0.1–0.2
|
|
51
|
+
],
|
|
52
|
+
postProcessing: {
|
|
53
|
+
bloom: { intensity: 0.04, threshold: 0.94, radius: 0.28 },
|
|
54
|
+
vignette: { darkness: 0.4, offset: 0.32 },
|
|
55
|
+
toneMappingExposure: 1.1, // 1.05–1.18
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Stage the model on an intentionally matte plinth:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
const stage = cylinder(16, 226).translate(0, 0, -26)
|
|
64
|
+
.color('#8b97a4').material({ metalness: 0.04, roughness: 0.78 });
|
|
65
|
+
mock(stage, 'StudioPlinth');
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Iteration rules that held up in practice:
|
|
69
|
+
|
|
70
|
+
- Prefer roughness over fog for softness — fog flattens form; matte materials keep shadow definition.
|
|
71
|
+
- Keep bloom near zero for mechanical scenes; too much reads toy-like.
|
|
72
|
+
- If the render is close but not right, nudge `toneMappingExposure` by ~0.05 before touching the light rig; avoid large ambient jumps — they kill contrast fastest.
|
|
73
|
+
- Add accent point lights near focal features, localized with `distance` and `decay`.
|
|
74
|
+
|
|
75
|
+
## Named render views
|
|
76
|
+
|
|
77
|
+
For repeatable review or hero renders, declare views in `scene({ views })` — wrap each camera in `{ camera: ... }`:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
scene({
|
|
81
|
+
camera: { position: [430, -540, 340], target: [0, 30, 125], fov: 38 },
|
|
82
|
+
views: {
|
|
83
|
+
hero: { camera: { position: [430, -540, 340], target: [0, 30, 125], up: [0, 0, 1], fov: 38 } },
|
|
84
|
+
side: { camera: { position: [700, 0, 180], target: [0, 30, 100], up: [0, 0, 1], fov: 32 } },
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Render one with `forgecad render 3d model.forge.js --view hero`.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<!-- Generated by scripts/build-forgecad-skill.mjs — do not edit. Edit agent-skill-library/forgecad-3d-reconstruction/SKILL.md instead. -->
|
|
2
|
+
|
|
1
3
|
# forgecad-3d-reconstruction
|
|
2
4
|
|
|
3
5
|
Reconstruct a parametric ForgeCAD model from an existing 3D CAD or mesh file such as STL, OBJ, 3MF, STEP, or STP; inspect the source asset directly, author real ForgeCAD geometry, and iteratively compare the candidate with `forgecad compare 3d`.
|
|
@@ -11,132 +13,44 @@ Reconstruct a parametric ForgeCAD model from an existing 3D CAD or mesh file suc
|
|
|
11
13
|
|
|
12
14
|
## ForgeCAD 3D Reconstruction
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
The reference asset is evidence, not the deliverable. The deliverable is a readable `.forge.js` model that runs, renders, inspects, and scores well against the source geometry.
|
|
17
|
-
|
|
18
|
-
Do not solve reconstruction by returning `importMesh("reference.stl")` or `importStep("reference.step")` as the final model unless the user explicitly asks for an import wrapper. Imported source assets are for measurement, rendering, inspection, and scoring.
|
|
16
|
+
The reference asset is evidence, not the deliverable. The deliverable is a readable, parametric `.forge.js` model that runs, renders, and scores well against the source. Never return `Import.mesh()`/`Import.step()` of the source as the final model unless the user explicitly asks for an import wrapper — imports are for measurement, rendering, and scoring only.
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
Routing: user wants to KEEP the file as a live component and design around it (bracket, enclosure, mating assembly) → `forgecad-make-a-model` (Imported Parts section), not this skill; benchmark/RL episodes → `forgecad-reconstruction-benchmark`; inspection-bundle interpretation → `forgecad-render-inspect`; independent grading after reconstruction → `forgecad-model-grader`; API and command reference → `forgecad` skill + CLI.md.
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
- Use `forgecad-reconstruction-benchmark` instead when the task is a benchmark
|
|
24
|
-
or RL episode with a fixed `submission/main.forge.js` output path.
|
|
25
|
-
- Use `forgecad-make-a-model` when creating a normal user-facing `.forge.js`
|
|
26
|
-
project structure outside the benchmark harness.
|
|
27
|
-
- Use `forgecad-render-inspect` when inspection bundles need interpretation.
|
|
28
|
-
- Use `forgecad-model-grader` only after reconstruction, when the user asks for an independent grade.
|
|
20
|
+
### Workflow
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
2. Inspect the source directly.
|
|
36
|
-
No wrapper script is needed. Use the local checkout CLI:
|
|
22
|
+
1. **Inspect the source directly** — the CLI reads CAD/mesh files as inputs, no wrapper script. Gather four evidence types: `ls --long` stats, an iso render, per-object views, sampled sections (invocations in CLI.md; when sharing a renderer server via `--port`, run renders sequentially).
|
|
23
|
+
2. **3MF**: enumerate every build item before scoring — hidden multi-part structure is a common miss. Item ref syntax: `Import.mesh` docs.
|
|
24
|
+
3. **Reconstruction Brief** before modeling: what must be exact vs. approximate vs. parametric; symmetry, origin, key reference planes; likely manufacturing process; scoring tolerance and alignment policy.
|
|
25
|
+
4. **Blockout first.** Match bbox and main masses, then add features. Model real geometry with blueprint-first APIs — never vertex-chase a faceted copy.
|
|
26
|
+
5. **Compare numerically** — the core loop:
|
|
37
27
|
|
|
38
28
|
```bash
|
|
39
|
-
|
|
40
|
-
node dist-cli/forgecad.js render 3d path/to/source.stl /tmp/<slug>-source.png --camera iso --edges thin --size 900
|
|
41
|
-
node dist-cli/forgecad.js inspect visual objects path/to/source.stl /tmp/<slug>-source-objects --camera iso --size 700 --force
|
|
42
|
-
node dist-cli/forgecad.js inspect sections sample path/to/source.stl /tmp/<slug>-source-sections --count 5 --size 700 --force
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
For 3MF sources, `forgecad run` prints the source archive's build
|
|
46
|
-
items/resource objects with stable refs such as
|
|
47
|
-
`3mf:build:001:object:2`, automatic names, per-item bounding boxes, and
|
|
48
|
-
triangle counts. Use that item table to avoid missing hidden multi-part
|
|
49
|
-
structure before scoring the whole shape.
|
|
50
|
-
|
|
51
|
-
For STEP/STP files, the CLI auto-selects OCCT unless `--backend` is passed.
|
|
52
|
-
Run render commands sequentially; starting multiple browser renders at once can make the shared Vite renderer race and time out.
|
|
53
|
-
|
|
54
|
-
3. Write a Reconstruction Brief.
|
|
55
|
-
Before modeling, record:
|
|
56
|
-
- source path and file type
|
|
57
|
-
- bounding box, volume, triangle count, and apparent units
|
|
58
|
-
- object identity and likely manufacturing process
|
|
59
|
-
- major primitive families: boxes, cylinders, plates, revolutions, lofts, sweeps, holes, fillets, ribs, threads, text, freeform surfaces
|
|
60
|
-
- symmetry, coordinate origin, and key reference planes
|
|
61
|
-
- what must be exact, what may be approximate, and what should remain parametric
|
|
62
|
-
- scoring tolerance and alignment policy
|
|
63
|
-
|
|
64
|
-
4. Build the ForgeCAD candidate.
|
|
65
|
-
Model the real geometry, not a faceted copy. Start with a blockout that matches bbox and main masses, then add holes, cutouts, transitions, and details. Prefer high-level ForgeCAD APIs and blueprint-first intent over vertex chasing.
|
|
66
|
-
5. Compare the candidate numerically.
|
|
67
|
-
Use the raw geometry comparison command:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
node dist-cli/forgecad.js compare 3d path/to/source.stl path/to/candidate.forge.js \
|
|
29
|
+
forgecad compare 3d path/to/source.stl path/to/candidate.forge.js \
|
|
71
30
|
--samples 3000 --json --output /tmp/<slug>-score.json
|
|
72
31
|
```
|
|
73
32
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
6. Iterate from coarse to fine.
|
|
77
|
-
Improve in this order:
|
|
78
|
-
- bbox size and coordinate placement
|
|
79
|
-
- main volumes and silhouette
|
|
80
|
-
- major holes, bosses, ribs, shells, and cutouts
|
|
81
|
-
- edge treatments and transitions
|
|
82
|
-
- small details, labels, textures, and decorative features
|
|
83
|
-
|
|
84
|
-
Use score metrics as evidence:
|
|
85
|
-
- low coverage means missing or extra surface area
|
|
86
|
-
- high RMS means broad proportional mismatch
|
|
87
|
-
- high p95 or max means localized protrusions, holes, or outliers
|
|
88
|
-
- bounds delta means size, origin, or scale mismatch
|
|
89
|
-
- volume delta means mass, shell, cutout, or scale mismatch
|
|
90
|
-
|
|
91
|
-
7. Validate the final model.
|
|
92
|
-
Minimum final checks:
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
node dist-cli/forgecad.js run path/to/candidate.forge.js
|
|
96
|
-
node dist-cli/forgecad.js render 3d path/to/candidate.forge.js /tmp/<slug>-candidate.png --camera iso --edges thin --size 900
|
|
97
|
-
node dist-cli/forgecad.js compare 3d path/to/source.stl path/to/candidate.forge.js --samples 5000 --json --output /tmp/<slug>-final-score.json
|
|
98
|
-
node dist-cli/forgecad.js inspect compare overlay path/to/candidate.forge.js /tmp/<slug>-compare --compare-samples 5000 --force --size 700
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Add targeted evidence commands such as `inspect fit interference`, `inspect manufacture thickness`, `inspect physical components`, `inspect physical floating`, or `inspect surface zebra` when the object is multi-part, hollow, mechanical, thin-walled, or surface-sensitive.
|
|
102
|
-
|
|
103
|
-
### Scoring Guidance
|
|
104
|
-
|
|
105
|
-
`forgecad compare 3d` returns an overall 0-100 raw geometry score plus distance, feature, bounds, and volume metrics. Treat the score as a guide, not a license to make unmaintainable code.
|
|
106
|
-
|
|
107
|
-
Suggested targets:
|
|
108
|
-
|
|
109
|
-
- `95+`: excellent reconstruction for simple prismatic or revolved parts
|
|
110
|
-
- `90+`: good target for ordinary mechanical parts with fillets and cutouts
|
|
111
|
-
- `80+`: acceptable rough reconstruction when the source is organic, faceted, or underdetermined
|
|
112
|
-
|
|
113
|
-
Always report the raw `rms`, `p95`, `max`, coverage, bounds delta, and volume delta. A high score with a large `max` distance can still hide a missing local feature.
|
|
114
|
-
|
|
115
|
-
For faceted source meshes, decide whether tessellation itself is evidence. If the source is an exported primitive or low-poly mesh, exact triangle/segment matching can raise the numeric score but may reduce parametric clarity. Prefer analytic intent when the user wants a clean CAD reconstruction; match tessellation only when the faceting is part of the artifact or required by the acceptance criteria.
|
|
33
|
+
6. **Iterate coarse to fine**: bbox/placement → main volumes/silhouette → holes/bosses/ribs/shells → edge treatments → small details.
|
|
116
34
|
|
|
117
|
-
###
|
|
35
|
+
### Reading the Score
|
|
118
36
|
|
|
119
|
-
|
|
37
|
+
Alignment: start `--align none`. Use `--align center` only when origins clearly differ but scale and orientation match. Use `--align center-scale` only for exploratory diagnosis — it hides dimensional errors.
|
|
120
38
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
39
|
+
| Signal | Diagnosis |
|
|
40
|
+
|---|---|
|
|
41
|
+
| Low coverage | Missing or extra surface |
|
|
42
|
+
| High RMS | Broad proportional mismatch |
|
|
43
|
+
| High p95/max | Localized outlier feature (protrusion, hole) |
|
|
44
|
+
| Bounds delta | Size, origin, or scale mismatch |
|
|
45
|
+
| Volume delta | Mass, shell, cutout, or scale mismatch |
|
|
125
46
|
|
|
126
|
-
|
|
47
|
+
Calibration: 95+ simple prismatic/revolved parts, 90+ ordinary mechanical parts with fillets/cutouts, 80+ acceptable for organic, faceted, or underdetermined sources. Always report raw rms, p95, max, coverage, bounds delta, and volume delta — a high overall score with a large max can hide a missing local feature.
|
|
127
48
|
|
|
128
|
-
|
|
49
|
+
Faceted sources: decide whether tessellation itself is evidence. Matching low-poly faceting raises the score but reduces parametric clarity — prefer analytic intent unless faceting is part of the artifact or required by the acceptance criteria.
|
|
129
50
|
|
|
130
|
-
|
|
51
|
+
### Done Criteria
|
|
131
52
|
|
|
132
|
-
- source
|
|
133
|
-
- candidate `.forge.js` path
|
|
134
|
-
- Reconstruction Brief summary
|
|
135
|
-
- source inspection bundle and renders
|
|
136
|
-
- candidate renders and inspection bundle, if used
|
|
137
|
-
- final `forgecad compare 3d` command and score JSON path
|
|
138
|
-
- final score, RMS, p95, max distance, coverage, bounds delta, and volume delta
|
|
139
|
-
- known mismatches and whether they are intentional simplifications or remaining work
|
|
53
|
+
The final model must run, render, re-compare at `--samples 5000`, and pass an `inspect compare overlay`. Add targeted inspects (`forgecad-render-inspect`) when the object is multi-part, hollow, thin-walled, or surface-sensitive. Report: source and candidate paths, score JSON path, final metrics, and every known mismatch classified as intentional simplification or remaining work.
|
|
140
54
|
|
|
141
55
|
|
|
142
56
|
## Bundled Files
|