forgecad 0.9.16 → 0.10.1

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 (162) hide show
  1. package/dist/assets/{AdminPage-CXvls4-J.js → AdminPage-DcCnj0qo.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-B27zk8xL.js → BenchmarkPage-BVEpJSVk.js} +1 -1
  3. package/dist/assets/{BlogPage-CMAVvgQL.js → BlogPage-DHaGP50_.js} +1 -1
  4. package/dist/assets/{DocsPage-knf4I4h7.js → DocsPage-CDoxHkz8.js} +40 -859
  5. package/dist/assets/EditorApp-BJ0Dloyh.js +16446 -0
  6. package/dist/assets/{EmbedViewer-D7ZGlFjx.js → EmbedViewer-CRKZbY0y.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-CnevhTE8.js → LandingPageProofDriven-BxHkYRE7.js} +1 -1
  8. package/dist/assets/{LegalPage-BPTUmqeg.js → LegalPage-B-u6FrVv.js} +1 -1
  9. package/dist/assets/{PricingPage-B0D4goG_.js → PricingPage-CzpZ6-Ce.js} +1 -1
  10. package/dist/assets/{SettingsPage-CFF-UgjI.js → SettingsPage-CIZSSAd0.js} +1 -1
  11. package/dist/assets/{app-CE3sYcV7.css → app-CjsbDlb7.css} +143 -0
  12. package/dist/assets/{app-T0pDcSX4.js → app-DaTMg3nH.js} +1310 -290
  13. package/dist/assets/cli/{render-C5pcIISc.js → render-DPf4AYJK.js} +55 -60
  14. package/dist/assets/{constructionHistoryWorker-Ba2Hm58b.js → constructionHistoryWorker-AwMMWSxg.js} +1103 -349
  15. package/dist/assets/{evalWorker-vkx310U2.js → evalWorker-CjZZWRWW.js} +5209 -2643
  16. package/dist/assets/{inspectWorker-BuTJDVX6.js → inspectWorker-CZsCFtQT.js} +1163 -409
  17. package/dist/assets/{jointPose-B_Cgedn9.js → jointPose-DzQOViQH.js} +1 -1
  18. package/dist/assets/{manifold-BWgsjmAM.js → manifold-BYlzU521.js} +1 -1
  19. package/dist/assets/{manifold-D6IFSkhH.js → manifold-DgXo0T5P.js} +2 -2
  20. package/dist/assets/{manifold-rZexZI0G.js → manifold-K1SkarlQ.js} +1 -1
  21. package/dist/assets/{reportWorker-0AGij1Ru.js → reportWorker-B9nWwSrB.js} +8501 -3393
  22. package/dist/assets/{scalar-sampling-budget-J5cuzxT1.js → scalar-sampling-budget-prBw_s8t.js} +6067 -3479
  23. package/dist/assets/{scanProxyWorker-Vl4Wxa1y.js → scanProxyWorker-2GtDLk-R.js} +1 -1
  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 +1 -1
  28. package/dist/docs-raw/CLI.md +77 -240
  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 +188 -582
  32. package/dist/docs-raw/generated/concepts.md +259 -3501
  33. package/dist/docs-raw/generated/core.md +283 -1250
  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 +227 -85
  37. package/dist/docs-raw/generated/output.md +35 -99
  38. package/dist/docs-raw/generated/runtime-names.md +23 -23
  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/guides/simready-quickstart.md +171 -0
  50. package/dist/docs-raw/simulation-workflow.md +273 -0
  51. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
  52. package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
  53. package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
  54. package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
  55. package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
  56. package/dist/docs-raw/skills/forgecad-lld.md +19 -113
  57. package/dist/docs-raw/skills/forgecad-make-a-model.md +112 -532
  58. package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
  59. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
  60. package/dist/docs-raw/skills/forgecad-project.md +13 -131
  61. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
  62. package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
  63. package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
  64. package/dist/docs-raw/skills/forgecad.md +19 -18
  65. package/dist/docs-raw/skills/index.md +2 -0
  66. package/dist/docs-raw/welcome.md +2 -2
  67. package/dist/index.html +2 -2
  68. package/dist/llms.txt +1 -2
  69. package/dist/sitemap.xml +25 -13
  70. package/dist-cli/{check-compiler-SYQ2PWOB.js → check-compiler-II7NLPAB.js} +1 -1
  71. package/dist-cli/{check-query-propagation-HIAGV62W.js → check-query-propagation-7462TR3R.js} +1 -1
  72. package/dist-cli/{chunk-SPZE3DUY.js → chunk-UWTJCGXF.js} +5848 -2915
  73. package/dist-cli/forgecad.js +3496 -703
  74. package/dist-skill/CONTEXT.md +1797 -7963
  75. package/dist-skill/SKILL.md +15 -15
  76. package/dist-skill/docs/API/core/concepts.md +27 -157
  77. package/dist-skill/docs/CLI.md +77 -240
  78. package/dist-skill/docs/generated/assembly.md +182 -532
  79. package/dist-skill/docs/generated/core.md +283 -1250
  80. package/dist-skill/docs/generated/curves.md +387 -1609
  81. package/dist-skill/docs/generated/lib.md +227 -85
  82. package/dist-skill/docs/generated/output.md +35 -99
  83. package/dist-skill/docs/generated/runtime-names.md +16 -21
  84. package/dist-skill/docs/generated/sdf.md +68 -284
  85. package/dist-skill/docs/generated/sheet-metal.md +68 -335
  86. package/dist-skill/docs/generated/sketch.md +240 -1160
  87. package/dist-skill/docs/generated/viewport.md +75 -223
  88. package/dist-skill/docs/generated/wood.md +21 -49
  89. package/dist-skill/docs/guides/coordinate-system.md +4 -42
  90. package/dist-skill/docs/guides/inspection-bundles.md +44 -442
  91. package/dist-skill/docs/guides/joint-design.md +18 -79
  92. package/dist-skill/docs/guides/positioning.md +21 -143
  93. package/dist-skill/docs/guides/scene-presentation.md +89 -0
  94. package/dist-skill/docs/guides/surface-members.md +26 -0
  95. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
  96. package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
  97. package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
  98. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
  99. package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
  100. package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
  101. package/dist-skill/library/forgecad-make-a-model/SKILL.md +110 -532
  102. package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
  103. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
  104. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
  105. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
  106. package/dist-skill/library/forgecad-project/SKILL.md +13 -133
  107. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
  108. package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
  109. package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
  110. package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
  111. package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
  112. package/dist-skill/website/skills/forgecad-component-model.md +53 -0
  113. package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
  114. package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
  115. package/dist-skill/website/skills/forgecad-lld.md +41 -0
  116. package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
  117. package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
  118. package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
  119. package/dist-skill/website/skills/forgecad-project.md +26 -0
  120. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
  121. package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
  122. package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
  123. package/dist-skill/website/skills/forgecad.md +122 -0
  124. package/dist-skill/website/skills/index.md +26 -0
  125. package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
  126. package/examples/api/conformal-product-ribbon.forge.js +1 -1
  127. package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
  128. package/examples/api/extrude-options.forge.js +4 -2
  129. package/examples/api/field-loft-drive-tip.forge.js +40 -0
  130. package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
  131. package/examples/api/highlight-debug.forge.js +10 -10
  132. package/examples/api/mesh-import-slats.forge.js +1 -1
  133. package/examples/api/real-product-curves.forge.js +1 -1
  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 +3 -3
  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/examples/robotics/README.md +46 -0
  145. package/examples/robotics/scout-cam-rover-simready/README.md +119 -0
  146. package/examples/robotics/scout-cam-rover-simready/lib/dims.js +140 -0
  147. package/examples/robotics/scout-cam-rover-simready/main.forge.js +343 -0
  148. package/examples/robotics/scout-cam-rover-simready/parts/body.forge.js +304 -0
  149. package/examples/robotics/scout-cam-rover-simready/parts/chassis.forge.js +320 -0
  150. package/examples/robotics/scout-cam-rover-simready/parts/hardware.forge.js +21 -0
  151. package/examples/robotics/scout-cam-rover-simready/parts/turret.forge.js +70 -0
  152. package/examples/robotics/scout-cam-rover-simready/parts/wheel.forge.js +116 -0
  153. package/examples/robotics/simready-asset-crate.forge.js +79 -0
  154. package/examples/robotics/simready-diff-drive-rover.forge.js +141 -0
  155. package/examples/robotics/simready-parallel-gripper.forge.js +102 -0
  156. package/package.json +1 -1
  157. package/dist/assets/EditorApp-BHMQlJ-D.js +0 -14686
  158. package/dist/docs-raw/guides/geometry-conventions.md +0 -52
  159. package/dist/docs-raw/guides/modeling-recipes.md +0 -78
  160. package/dist-skill/docs/guides/geometry-conventions.md +0 -52
  161. package/dist-skill/docs/guides/modeling-recipes.md +0 -78
  162. package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
@@ -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`.
@@ -0,0 +1,26 @@
1
+ ---
2
+ skill-group: curves
3
+ skill-order: 1
4
+ ---
5
+
6
+ # Surface Members: When To Route Through Carrier + SurfaceBody
7
+
8
+ Surface members model physical material that follows a carrier surface: bottle-cage arms, grip inlays, brace ribs, prop guards, helmet vents. Full API, parameter rules, and worked examples: [../generated/curves.md](../generated/curves.md).
9
+
10
+ ## When to use
11
+
12
+ Route through this layer when the model has: a carrier surface (cylinder, plane, or `ProductSkin`); paths or bands in carrier-local coordinates; member-local features (slots, cutouts, lips, cups, ribs, section thickness/edge radius); or explicit joins between named members. Not for plain boxes, simple extrusions, sheet-metal bends, exact machined faces, or free-floating connector-positioned assemblies.
13
+
14
+ ## Mental model
15
+
16
+ - `Carrier` owns surface coordinates and frames. `SurfaceBody(name)` owns named members — `band()` or `plate()` — and the joins between them.
17
+ - Features attach to a member's local coordinate system before lowering. This is not a global boolean recipe.
18
+ - Cylinder paths take degrees and handle seam wrapping — never compute angles with trig.
19
+ - A ProductSkin path stays on one side. For multi-side detail, split into one member per side and join them at named transition anchors; `sideTransition` / `sideTransitionChain` / `sideRoute` generate the matching side-local endpoints.
20
+ - `Product.ribbon()` stays the simple path for a one-side conformal ribbon. Upgrade to `Carrier.productSkin(skin)` + `SurfaceBody` when the detail needs member-local features, repeated ribs, explicit joins, or mirrored members.
21
+
22
+ ## Verification loop
23
+
24
+ - `build()` returns the member geometry. `buildWithDiagnostics()` adds a serializable member graph + IR; `buildDebug()` adds visible debug markers (anchors, join radii, centerlines, frame axes) alongside the normal shapes.
25
+ - Every diagnostic carries a stable `code` field (e.g. `region.centerOutOfBounds`). Repair loops must match on `code`, never on English prose. Clipped or crossing regions and invalid joins are reported as diagnostics, never silently accepted as valid geometry.
26
+ - Only a limited join set lowers to real geometry: close endpoint pairs, selected named-anchor pairs, sampled landing pads, and unambiguous shared endpoints via `autoJoinAtSharedAnchors()`. Farther, missing-anchor, or ambiguous joins remain diagnostic-only intent — decompose the design into supported joins instead of expecting a fallback.
@@ -6,129 +6,41 @@ forgecad-public: true
6
6
 
7
7
  # ForgeCAD 3D Reconstruction
8
8
 
9
- Use this skill when the user provides an existing 3D file and wants a ForgeCAD model that recreates it as parametric CAD.
9
+ 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.
10
10
 
11
- 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.
11
+ 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.
12
12
 
13
- 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.
13
+ ## Workflow
14
14
 
15
- ## Companion Skills
16
-
17
- - Use `forgecad` for API syntax, direct CAD CLI behavior, and validation commands.
18
- - Use `forgecad-reconstruction-benchmark` instead when the task is a benchmark
19
- or RL episode with a fixed `submission/main.forge.js` output path.
20
- - Use `forgecad-make-a-model` when creating a normal user-facing `.forge.js`
21
- project structure outside the benchmark harness.
22
- - Use `forgecad-render-inspect` when inspection bundles need interpretation.
23
- - Use `forgecad-model-grader` only after reconstruction, when the user asks for an independent grade.
24
-
25
- ## Required Workflow
26
-
27
- 1. Stage the reference.
28
- 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.
29
-
30
- 2. Inspect the source directly.
31
- No wrapper script is needed. Use the local checkout CLI:
32
-
33
- ```bash
34
- node dist-cli/forgecad.js ls path/to/source.stl --quality live --long
35
- node dist-cli/forgecad.js render 3d path/to/source.stl /tmp/<slug>-source.png --camera iso --edges thin --size 900
36
- node dist-cli/forgecad.js inspect visual objects path/to/source.stl /tmp/<slug>-source-objects --camera iso --size 700 --force
37
- node dist-cli/forgecad.js inspect sections sample path/to/source.stl /tmp/<slug>-source-sections --count 5 --size 700 --force
38
- ```
39
-
40
- For 3MF sources, `forgecad run` prints the source archive's build
41
- items/resource objects with stable refs such as
42
- `3mf:build:001:object:2`, automatic names, per-item bounding boxes, and
43
- triangle counts. Use that item table to avoid missing hidden multi-part
44
- structure before scoring the whole shape.
45
-
46
- For STEP/STP files, the CLI auto-selects OCCT unless `--backend` is passed.
47
- Run render commands sequentially; starting multiple browser renders at once can make the shared Vite renderer race and time out.
48
-
49
- 3. Write a Reconstruction Brief.
50
- Before modeling, record:
51
- - source path and file type
52
- - bounding box, volume, triangle count, and apparent units
53
- - object identity and likely manufacturing process
54
- - major primitive families: boxes, cylinders, plates, revolutions, lofts, sweeps, holes, fillets, ribs, threads, text, freeform surfaces
55
- - symmetry, coordinate origin, and key reference planes
56
- - what must be exact, what may be approximate, and what should remain parametric
57
- - scoring tolerance and alignment policy
58
-
59
- 4. Build the ForgeCAD candidate.
60
- 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.
61
- 5. Compare the candidate numerically.
62
- Use the raw geometry comparison command:
15
+ 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).
16
+ 2. **3MF**: enumerate every build item before scoring — hidden multi-part structure is a common miss. Item ref syntax: `Import.mesh` docs.
17
+ 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.
18
+ 4. **Blockout first.** Match bbox and main masses, then add features. Model real geometry with blueprint-first APIs never vertex-chase a faceted copy.
19
+ 5. **Compare numerically** the core loop:
63
20
 
64
21
  ```bash
65
- node dist-cli/forgecad.js compare 3d path/to/source.stl path/to/candidate.forge.js \
22
+ forgecad compare 3d path/to/source.stl path/to/candidate.forge.js \
66
23
  --samples 3000 --json --output /tmp/<slug>-score.json
67
24
  ```
68
25
 
69
- 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.
70
-
71
- 6. Iterate from coarse to fine.
72
- Improve in this order:
73
- - bbox size and coordinate placement
74
- - main volumes and silhouette
75
- - major holes, bosses, ribs, shells, and cutouts
76
- - edge treatments and transitions
77
- - small details, labels, textures, and decorative features
78
-
79
- Use score metrics as evidence:
80
- - low coverage means missing or extra surface area
81
- - high RMS means broad proportional mismatch
82
- - high p95 or max means localized protrusions, holes, or outliers
83
- - bounds delta means size, origin, or scale mismatch
84
- - volume delta means mass, shell, cutout, or scale mismatch
85
-
86
- 7. Validate the final model.
87
- Minimum final checks:
88
-
89
- ```bash
90
- node dist-cli/forgecad.js run path/to/candidate.forge.js
91
- node dist-cli/forgecad.js render 3d path/to/candidate.forge.js /tmp/<slug>-candidate.png --camera iso --edges thin --size 900
92
- 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
93
- node dist-cli/forgecad.js inspect compare overlay path/to/candidate.forge.js /tmp/<slug>-compare --compare-samples 5000 --force --size 700
94
- ```
95
-
96
- 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.
97
-
98
- ## Scoring Guidance
99
-
100
- `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.
101
-
102
- Suggested targets:
103
-
104
- - `95+`: excellent reconstruction for simple prismatic or revolved parts
105
- - `90+`: good target for ordinary mechanical parts with fillets and cutouts
106
- - `80+`: acceptable rough reconstruction when the source is organic, faceted, or underdetermined
107
-
108
- 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.
109
-
110
- 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.
26
+ 6. **Iterate coarse to fine**: bbox/placement main volumes/silhouette holes/bosses/ribs/shells edge treatments small details.
111
27
 
112
- ## Subagent Validation
28
+ ## Reading the Score
113
29
 
114
- When validating this skill with a subagent, give the subagent only:
30
+ 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.
115
31
 
116
- - the reference file path
117
- - the desired output folder
118
- - this skill name
119
- - the command to use the local CLI: `node dist-cli/forgecad.js`
32
+ | Signal | Diagnosis |
33
+ |---|---|
34
+ | Low coverage | Missing or extra surface |
35
+ | High RMS | Broad proportional mismatch |
36
+ | High p95/max | Localized outlier feature (protrusion, hole) |
37
+ | Bounds delta | Size, origin, or scale mismatch |
38
+ | Volume delta | Mass, shell, cutout, or scale mismatch |
120
39
 
121
- 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.
40
+ 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.
122
41
 
123
- ## Output Contract
42
+ 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.
124
43
 
125
- When finished, report:
44
+ ## Done Criteria
126
45
 
127
- - source file path
128
- - candidate `.forge.js` path
129
- - Reconstruction Brief summary
130
- - source inspection bundle and renders
131
- - candidate renders and inspection bundle, if used
132
- - final `forgecad compare 3d` command and score JSON path
133
- - final score, RMS, p95, max distance, coverage, bounds delta, and volume delta
134
- - known mismatches and whether they are intentional simplifications or remaining work
46
+ 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.