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.
Files changed (166) hide show
  1. package/dist/assets/{AdminPage-CDyGUinA.js → AdminPage-DwYHz72L.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-DfPMY_-d.js → BenchmarkPage-a9_f-1US.js} +1 -1
  3. package/dist/assets/{BlogPage-kF0fkdJT.js → BlogPage-DodHpvmf.js} +1 -1
  4. package/dist/assets/{DocsPage-B954L3YN.js → DocsPage-B5LePEuj.js} +8 -858
  5. package/dist/assets/{EditorApp-CuDLxKqL.css → EditorApp-BpjZgzk0.css} +148 -0
  6. package/dist/assets/EditorApp-QXsAISLR.js +16307 -0
  7. package/dist/assets/{EmbedViewer-C77B-TrF.js → EmbedViewer-DdEHGUMU.js} +2 -2
  8. package/dist/assets/{LandingPageProofDriven-Cr6fXMDj.js → LandingPageProofDriven-yhhOodbf.js} +2 -2
  9. package/dist/assets/{LegalPage-Dzklqmmg.js → LegalPage-5RbKRGYK.js} +1 -1
  10. package/dist/assets/{PricingPage-zWXkvlwl.js → PricingPage-E3Rma7aV.js} +1 -1
  11. package/dist/assets/{SettingsPage-Bz0of4KQ.js → SettingsPage-BJZcM97j.js} +1 -1
  12. package/dist/assets/{app-D3kDkggg.js → app-DSYrDg0V.js} +1846 -352
  13. package/dist/assets/cli/{render-DSY3mMQa.js → render-ZMHR9HkV.js} +161 -70
  14. package/dist/assets/{constructionHistoryWorker-gpDo-uH2.js → constructionHistoryWorker-AwMMWSxg.js} +1104 -349
  15. package/dist/assets/{evalWorker-CU0Ke6DP.js → evalWorker-DbNs7Dkp.js} +5155 -3772
  16. package/dist/assets/{inspectWorker-COyp8XXA.js → inspectWorker-CZsCFtQT.js} +1415 -439
  17. package/dist/assets/{targets-B9sGB5nB.js → jointPose-DO6mnXn_.js} +71 -3
  18. package/dist/assets/{manifold-DNkrUWpA.js → manifold-BGlQBBH9.js} +1 -1
  19. package/dist/assets/{manifold-BRI5prcH.js → manifold-BU-tJwQh.js} +1 -1
  20. package/dist/assets/{manifold-C-3h2M7p.js → manifold-fy2MV7K1.js} +2 -2
  21. package/dist/assets/{reportWorker-CdBz5bNg.js → reportWorker-DO6hcQbh.js} +8474 -4549
  22. package/dist/assets/{scalar-sampling-budget-wJF98aY9.js → scalar-sampling-budget-o90NSNmF.js} +5347 -3906
  23. package/dist/assets/{scanProxyWorker-B-9VbLIs.js → scanProxyWorker-2GtDLk-R.js} +19 -6
  24. package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
  25. package/dist/cli/render.html +1 -1
  26. package/dist/docs/index.html +3 -3
  27. package/dist/docs-raw/AI/usage.md +3 -1
  28. package/dist/docs-raw/CLI.md +65 -239
  29. package/dist/docs-raw/README.md +6 -0
  30. package/dist/docs-raw/component-model.md +17 -150
  31. package/dist/docs-raw/generated/assembly.md +159 -520
  32. package/dist/docs-raw/generated/concepts.md +245 -3491
  33. package/dist/docs-raw/generated/core.md +277 -1251
  34. package/dist/docs-raw/generated/curves.md +387 -1608
  35. package/dist/docs-raw/generated/legacy.md +162 -0
  36. package/dist/docs-raw/generated/lib.md +238 -112
  37. package/dist/docs-raw/generated/output.md +51 -76
  38. package/dist/docs-raw/generated/runtime-names.md +30 -22
  39. package/dist/docs-raw/generated/sdf.md +68 -284
  40. package/dist/docs-raw/generated/sheet-metal.md +68 -335
  41. package/dist/docs-raw/generated/sketch.md +240 -1161
  42. package/dist/docs-raw/generated/viewport.md +75 -316
  43. package/dist/docs-raw/generated/wood.md +21 -49
  44. package/dist/docs-raw/guides/coordinate-system.md +4 -42
  45. package/dist/docs-raw/guides/inspection-bundles.md +44 -442
  46. package/dist/docs-raw/guides/joint-design.md +18 -79
  47. package/dist/docs-raw/guides/positioning.md +21 -143
  48. package/dist/docs-raw/guides/scene-presentation.md +89 -0
  49. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
  50. package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
  51. package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
  52. package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
  53. package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
  54. package/dist/docs-raw/skills/forgecad-lld.md +19 -113
  55. package/dist/docs-raw/skills/forgecad-make-a-model.md +113 -532
  56. package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
  57. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
  58. package/dist/docs-raw/skills/forgecad-project.md +13 -129
  59. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
  60. package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
  61. package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
  62. package/dist/docs-raw/skills/forgecad.md +19 -18
  63. package/dist/docs-raw/skills/index.md +2 -0
  64. package/dist/docs-raw/welcome.md +4 -2
  65. package/dist/index.html +1 -1
  66. package/dist/llms.txt +1 -2
  67. package/dist/sitemap.xml +13 -13
  68. package/dist-cli/{check-compiler-SDX5QIXI.js → check-compiler-JTVBITCR.js} +1 -1
  69. package/dist-cli/{check-query-propagation-EAYEFT77.js → check-query-propagation-3FFLSMVN.js} +1 -1
  70. package/dist-cli/{chunk-N4O47JLF.js → chunk-OAN5T4XD.js} +5722 -4287
  71. package/dist-cli/forgecad.js +2195 -656
  72. package/dist-skill/CONTEXT.md +1778 -7912
  73. package/dist-skill/SKILL.md +15 -15
  74. package/dist-skill/docs/API/core/concepts.md +27 -157
  75. package/dist-skill/docs/CLI.md +65 -239
  76. package/dist-skill/docs/generated/assembly.md +160 -493
  77. package/dist-skill/docs/generated/core.md +277 -1251
  78. package/dist-skill/docs/generated/curves.md +387 -1609
  79. package/dist-skill/docs/generated/lib.md +238 -112
  80. package/dist-skill/docs/generated/output.md +51 -76
  81. package/dist-skill/docs/generated/runtime-names.md +16 -22
  82. package/dist-skill/docs/generated/sdf.md +68 -284
  83. package/dist-skill/docs/generated/sheet-metal.md +68 -335
  84. package/dist-skill/docs/generated/sketch.md +240 -1160
  85. package/dist-skill/docs/generated/viewport.md +75 -223
  86. package/dist-skill/docs/generated/wood.md +21 -49
  87. package/dist-skill/docs/guides/coordinate-system.md +4 -42
  88. package/dist-skill/docs/guides/inspection-bundles.md +44 -442
  89. package/dist-skill/docs/guides/joint-design.md +18 -79
  90. package/dist-skill/docs/guides/positioning.md +21 -143
  91. package/dist-skill/docs/guides/scene-presentation.md +89 -0
  92. package/dist-skill/docs/guides/surface-members.md +26 -0
  93. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
  94. package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
  95. package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
  96. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
  97. package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
  98. package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
  99. package/dist-skill/library/forgecad-make-a-model/SKILL.md +111 -532
  100. package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
  101. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
  102. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
  103. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
  104. package/dist-skill/library/forgecad-project/SKILL.md +13 -131
  105. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
  106. package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
  107. package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
  108. package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
  109. package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
  110. package/dist-skill/website/skills/forgecad-component-model.md +53 -0
  111. package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
  112. package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
  113. package/dist-skill/website/skills/forgecad-lld.md +41 -0
  114. package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
  115. package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
  116. package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
  117. package/dist-skill/website/skills/forgecad-project.md +26 -0
  118. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
  119. package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
  120. package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
  121. package/dist-skill/website/skills/forgecad.md +122 -0
  122. package/dist-skill/website/skills/index.md +26 -0
  123. package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
  124. package/examples/api/conformal-product-ribbon.forge.js +1 -1
  125. package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
  126. package/examples/api/extrude-options.forge.js +4 -2
  127. package/examples/api/field-loft-drive-tip.forge.js +40 -0
  128. package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
  129. package/examples/api/helix-basics.forge.js +2 -2
  130. package/examples/api/highlight-debug.forge.js +10 -10
  131. package/examples/api/mesh-import-slats.forge.js +1 -1
  132. package/examples/api/real-product-curves.forge.js +1 -1
  133. package/examples/api/route3d-elbow.forge.js +3 -0
  134. package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
  135. package/examples/api/sdf-shapes.forge.js +2 -5
  136. package/examples/api/sketch-rounding-strategies.forge.js +6 -6
  137. package/examples/api/surface-member-bottle-cage.forge.js +3 -3
  138. package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
  139. package/examples/api/surface-member-razor-inlay.forge.js +1 -1
  140. package/examples/api/variable-sweep-test.forge.js +4 -2
  141. package/examples/mechanical/airplane-propeller.forge.js +74 -39
  142. package/examples/nurbs-surface.forge.js +1 -1
  143. package/examples/products/iphone.forge.js +1 -1
  144. package/package.json +4 -1
  145. package/dist/assets/EditorApp-Beb-IZ0y.js +0 -14014
  146. package/dist/docs-raw/guides/geometry-conventions.md +0 -52
  147. package/dist/docs-raw/guides/modeling-recipes.md +0 -78
  148. package/dist-skill/docs/guides/geometry-conventions.md +0 -52
  149. package/dist-skill/docs/guides/modeling-recipes.md +0 -78
  150. package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
  151. package/examples/api/bolted-service-cover.forge.js +0 -17
  152. package/examples/api/cable-gland-anchor.forge.js +0 -14
  153. package/examples/api/captured-cartridge-guide.forge.js +0 -14
  154. package/examples/api/captured-linear-slide.forge.js +0 -13
  155. package/examples/api/clevis-pin-joint.forge.js +0 -13
  156. package/examples/api/datum-enclosure.forge.js +0 -16
  157. package/examples/api/hose-barb-port.forge.js +0 -14
  158. package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
  159. package/examples/api/living-hinge-cover.forge.js +0 -14
  160. package/examples/api/pcb-terminal-block.forge.js +0 -22
  161. package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
  162. package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
  163. package/examples/api/routed-tube-clip.forge.js +0 -15
  164. package/examples/api/seated-bearing-stack.forge.js +0 -30
  165. package/examples/api/snap-latch-cover.forge.js +0 -14
  166. 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
- How to build mechanical joints — clevis-tongue hinges, ball-and-socket, dovetails — that actually rotate without binding and stop where they should.
8
+ Geometry recipes for joints that actually rotate without binding clevis-tongue hinges, hinge chains, hard stops.
9
9
 
10
- ## Frame-Aware Connectors First
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 mechanical joint has a **cavity** in one part and a **tenon** in the other. The cavity must be a real empty volume — not a gap implied by the absence of two separate solids.
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
- ```ts
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
- // GOOD — body ends FLAT before the joint. Tines extend forward to the pivot.
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
- After applying the cavity rule, `forgecad run` collision volume between adjacent parts in a clevis-tongue chain should drop to **zero** (or a few mm³ of clearance overlap). If it doesn't, there's still solid material where there should be a cavity.
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
- ## Connecting Cantilevers
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
- `addRevolute({ min: 0, max: 90 })` sets **slider limits** the viewport won't let the user drag past them, but the geometry permits any rotation. There is no physical stop.
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 geometrythey only constrain the viewport slider; the geometry still permits any rotation. A physical stop requires an interfering protrusion:
81
27
 
82
- Verify with `forgecad run` at the limit poses the contact pair should show ~0 mm³ collision (just touching), and rotation past the limit should report a non-zero collision volume.
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
- ## Knuckle Sizing
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
- 1. Build the joint at rest pose. Run `forgecad run`. Check collision volumes.
99
- 2. If adjacent parts in the joint show > clearance-volume of overlap → missing cavity (apply the cavity rule).
100
- 3. Render with `--focus PartName` to inspect each part in isolation. The clevis end should clearly show a gap between the tines (the cavity).
101
- 4. Render at curl angles (set joint debug params) at 30°, 60°, 90°. No new collisions should appear from rotation.
102
- 5. Render at -10° (backbend test). Either no rotation possible (geometric stop in place) or rotation occurs and you need to add a stop.
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 Strategy
6
+ # Positioning Decision Ladder
7
7
 
8
- ## Rule 0: if parts should touch, use connectors first
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
- For any fixed assembly where parts are meant to stay in contact in the final model, start with connectors + `matchTo()`. This applies to furniture, fixtures, toys, enclosures, sleds, and any other static multi-part object, not only mechanisms.
10
+ ## 1. Connectors + `matchTo()` every real part-to-part interface
11
11
 
12
- Use raw `translate()` and `rotate()` when parts are intentionally free-floating or when you are doing quick exploratory layout. Use `attachTo()` for rough bounding-box placement. But if the relationship is a real interface, make it explicit with connectors.
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).translate(0, 0, -5).withConnectors({
51
- left_tab: connector.male("dovetail", { origin: [-100, 0, 0], axis: [-1, 0, 0], up: [0, 0, 1] }),
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 panel = box(12, 120, 200).translate(0, 0, -100).withConnectors({
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
- **Why connectors first:** stable (don't shift on fillet/chamfer/boolean), semantic (carry type/gender), oriented (full frame), queryable (`shape.connectorDistance('a','b')`), explode-aware.
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
- The most common positioning bug: manually adding a parent's global offset to every sub-part. One wrong sign or forgotten variable and parts float into space. **Use `group()` to build parts in local coordinates (at the origin), then position the group once.**
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
- ```javascript
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
- **Groups nest.** Build sub-assemblies as groups, then group those into larger assemblies each level has its own local origin.
29
+ Always call `pointAlong()` **before** `matchTo()`/`translate()`it reorients around the origin.
94
30
 
95
- ```javascript
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
- **When to use something else:** `group()` preserves individual shapes — you can't boolean (subtract/intersect) a group. If a sub-part needs a boolean with the parent body, do that boolean first in local coordinates, then group the result.
33
+ Anchor points shift after fillet/chamfer/boolean: fine for quick prototyping, fragile for assembly interfaces promote real interfaces to connectors.
105
34
 
106
- ## 3. `pointAlong()` — orient cylinders before positioning
35
+ ## 5. `placeReference()` — land a named anchor on a world coordinate
107
36
 
108
- ```javascript
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
- **Always call `pointAlong()` BEFORE `matchTo()` or `translate()`** — it reorients around the origin.
39
+ ## 6. Last resort: `rotateAroundTo()`, `moveToLocal()`, `translate()`
116
40
 
117
- ## 4. `attachTo()` quick bounding-box positioning
41
+ For computed offsets and free-floating or exploratory layout. Raw `translate()`/`rotate()` is correct only when parts are intentionally unrelated.
118
42
 
119
- ```javascript
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
- // Center at the world origin
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
- // Align left edge to X = 10
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
- For cross-file parts needing proper alignment, prefer connectors over placement references.
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
- Use this skill when the user provides an existing 3D file and wants a ForgeCAD model that recreates it as parametric CAD.
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
- ### Companion Skills
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
- - Use `forgecad` for API syntax, direct CAD CLI behavior, and validation commands.
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
- ### Required Workflow
31
-
32
- 1. Stage the reference.
33
- Put the source 3D file in a scratch folder such as `/tmp/<slug>-reconstruct/ref/` or keep the user's original path if it is already stable. Preserve the original file.
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
- node dist-cli/forgecad.js ls path/to/source.stl --quality live --long
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
- Start with `--align none`. Use `--align center` only when the source and candidate clearly use different origins but the same scale and orientation. Use `--align center-scale` only for exploratory diagnosis because it can hide dimensional errors.
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
- ### Subagent Validation
35
+ ### Reading the Score
118
36
 
119
- When validating this skill with a subagent, give the subagent only:
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
- - the reference file path
122
- - the desired output folder
123
- - this skill name
124
- - the command to use the local CLI: `node dist-cli/forgecad.js`
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
- Do not provide your intended modeling strategy, hidden measurements, or expected score. The point is to test whether the skill guides a fresh agent through evidence, modeling, scoring, and iteration.
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
- ### Output Contract
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
- When finished, report:
51
+ ### Done Criteria
131
52
 
132
- - source file path
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