forgecad 0.10.0 → 0.10.2

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 (75) hide show
  1. package/dist/assets/{AdminPage-DwYHz72L.js → AdminPage-CHY6ZN-p.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-a9_f-1US.js → BenchmarkPage-BcRT5iGN.js} +1 -1
  3. package/dist/assets/{BlogPage-DodHpvmf.js → BlogPage-BssBbnb-.js} +1 -1
  4. package/dist/assets/{DocsPage-B5LePEuj.js → DocsPage-DsvdiRNK.js} +33 -2
  5. package/dist/assets/{EditorApp-QXsAISLR.js → EditorApp-Bfd3jbtC.js} +185 -44
  6. package/dist/assets/{EmbedViewer-DdEHGUMU.js → EmbedViewer-D5t8WamV.js} +3 -3
  7. package/dist/assets/{LandingPageProofDriven-yhhOodbf.js → LandingPageProofDriven-DbN7o-Be.js} +1 -1
  8. package/dist/assets/{LegalPage-5RbKRGYK.js → LegalPage-DNGrrY0p.js} +1 -1
  9. package/dist/assets/{PricingPage-E3Rma7aV.js → PricingPage-Nczr3pRz.js} +1 -1
  10. package/dist/assets/{SettingsPage-BJZcM97j.js → SettingsPage-DZlyu4d4.js} +1 -1
  11. package/dist/assets/{app-DSYrDg0V.js → app-C9ct2hRD.js} +1752 -474
  12. package/dist/assets/{app-CE3sYcV7.css → app-CjsbDlb7.css} +143 -0
  13. package/dist/assets/{scalar-sampling-budget-o90NSNmF.js → backendInit-ymjonyQp.js} +85756 -78750
  14. package/dist/assets/cli/{render-ZMHR9HkV.js → render-B_0lQwKU.js} +71 -193
  15. package/dist/assets/{constructionHistoryWorker-AwMMWSxg.js → constructionHistoryWorker-CZ42Dksy.js} +8058 -1225
  16. package/dist/assets/{evalWorker-DbNs7Dkp.js → evalWorker-C2pm8LHP.js} +23037 -15821
  17. package/dist/assets/{forgecad_geometry-Dgceylq9.js → forgecad_geometry-BlMtqluF.js} +120 -1
  18. package/dist/assets/{forgecad_geometry_bg-dD4RNQF1.wasm → forgecad_geometry_bg-BllP_WiL.wasm} +0 -0
  19. package/dist/assets/{inspectWorker-CZsCFtQT.js → inspectWorker-D5T5VbfK.js} +31375 -32603
  20. package/dist/assets/{jointPose-DO6mnXn_.js → jointPose-4r8ed8_5.js} +1 -1
  21. package/dist/assets/{manifold-BU-tJwQh.js → manifold-5PP1eGLN.js} +1 -1
  22. package/dist/assets/{manifold-fy2MV7K1.js → manifold-C4r6B-XY.js} +2 -2
  23. package/dist/assets/{manifold-BGlQBBH9.js → manifold-DjBkyIc8.js} +1 -1
  24. package/dist/assets/{reportWorker-DO6hcQbh.js → reportWorker-CwenM7wB.js} +46620 -44936
  25. package/dist/cli/render.html +1 -1
  26. package/dist/docs/index.html +2 -2
  27. package/dist/docs-raw/CLI.md +43 -16
  28. package/dist/docs-raw/generated/assembly.md +71 -6
  29. package/dist/docs-raw/generated/concepts.md +17 -3
  30. package/dist/docs-raw/generated/core.md +10 -3
  31. package/dist/docs-raw/generated/output.md +14 -43
  32. package/dist/docs-raw/generated/runtime-names.md +4 -4
  33. package/dist/docs-raw/generated/sdf.md +2 -2
  34. package/dist/docs-raw/guides/simready-quickstart.md +173 -0
  35. package/dist/docs-raw/simulation-workflow.md +273 -0
  36. package/dist/index.html +2 -2
  37. package/dist/sitemap.xml +25 -13
  38. package/dist-cli/{check-compiler-JTVBITCR.js → check-compiler-SP7FAL7R.js} +1 -1
  39. package/dist-cli/{check-query-propagation-3FFLSMVN.js → check-query-propagation-BRLSHP22.js} +1 -1
  40. package/dist-cli/{chunk-OAN5T4XD.js → chunk-RQQ42YCP.js} +51209 -43456
  41. package/dist-cli/forgecad.js +5783 -1691
  42. package/dist-cli/{forgecad_geometry-QOQIIP53.js → forgecad_geometry-7TVSNVUB.js} +119 -0
  43. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  44. package/dist-skill/CONTEXT.md +107 -68
  45. package/dist-skill/docs/API/core/concepts.md +2 -2
  46. package/dist-skill/docs/CLI.md +43 -16
  47. package/dist-skill/docs/generated/assembly.md +67 -6
  48. package/dist-skill/docs/generated/core.md +10 -3
  49. package/dist-skill/docs/generated/output.md +14 -43
  50. package/dist-skill/docs/generated/runtime-names.md +4 -4
  51. package/dist-skill/docs/generated/sdf.md +2 -2
  52. package/examples/api/gyroid-voronoi-blend.forge.js +1 -1
  53. package/examples/api/organic-noise-sculpture.forge.js +1 -1
  54. package/examples/api/sdf-circular-array-knurling.forge.js +1 -1
  55. package/examples/api/{sdf-custom-raymarch.forge.js → sdf-custom-field-mesh-preview.forge.js} +3 -4
  56. package/examples/api/sdf-materialize-tree.forge.js +2 -2
  57. package/examples/api/sdf-plain-return.forge.js +3 -2
  58. package/examples/api/sdf-shapes.forge.js +2 -2
  59. package/examples/api/sdf-surface-basket-weave.forge.js +2 -2
  60. package/examples/generative/twisted-lattice-tower.forge.js +1 -1
  61. package/examples/generative/voronoi-lampshade.forge.js +1 -1
  62. package/examples/robotics/README.md +46 -0
  63. package/examples/robotics/scout-cam-rover-simready/README.md +119 -0
  64. package/examples/robotics/scout-cam-rover-simready/lib/dims.js +140 -0
  65. package/examples/robotics/scout-cam-rover-simready/main.forge.js +343 -0
  66. package/examples/robotics/scout-cam-rover-simready/parts/body.forge.js +304 -0
  67. package/examples/robotics/scout-cam-rover-simready/parts/chassis.forge.js +320 -0
  68. package/examples/robotics/scout-cam-rover-simready/parts/hardware.forge.js +21 -0
  69. package/examples/robotics/scout-cam-rover-simready/parts/turret.forge.js +70 -0
  70. package/examples/robotics/scout-cam-rover-simready/parts/wheel.forge.js +116 -0
  71. package/examples/robotics/simready-asset-crate.forge.js +79 -0
  72. package/examples/robotics/simready-diff-drive-rover.forge.js +141 -0
  73. package/examples/robotics/simready-parallel-gripper.forge.js +102 -0
  74. package/package.json +2 -2
  75. package/dist/assets/manifold-CzYf_iub.js +0 -3023
@@ -46,7 +46,9 @@ ForgeCAD includes a local editor. Open it around a dedicated project folder, edi
46
46
  | `dev <project-path> [project-path ...]` | Start the Vite dev server for ForgeCAD source development. |
47
47
  | `web` | Start a local dev server in web/playground mode (no filesystem, localStorage only). |
48
48
 
49
- `forgecad studio <project-path>` is the normal installed-CLI command; `forgecad dev` starts the Vite dev server and is mainly for ForgeCAD source development. Keep one long-running `forgecad studio <project-path> [project-path ...]` process open with every active project folder as an argument — the user opens the single printed localhost port once, and AI agents only create or edit files under those folders so the browser updates live without starting more servers.
49
+ `forgecad studio <project-path>` is the normal installed-CLI command for users. `forgecad dev <project-path>` starts the Vite dev server and is mainly for ForgeCAD source development.
50
+
51
+ Keep one long-running `forgecad studio <project-path> [project-path ...]` process open with every active project folder listed in its arguments; the user opens the single printed localhost port once, and AI agents should only create or edit files under those folders so the browser updates live without starting more servers.
50
52
 
51
53
  <details>
52
54
  <summary>Common flags for studio / dev</summary>
@@ -76,7 +78,7 @@ Direct `.stl`/`.obj`/`.3mf`/`.step`/`.stp` inputs are imported automatically; ST
76
78
 
77
79
  ```bash
78
80
  forgecad run model.forge.js
79
- forgecad run model.forge.js --details --history
81
+ forgecad run model.forge.js other-model.forge.js --quality live
80
82
  ```
81
83
 
82
84
  ### `forgecad ls`
@@ -102,7 +104,7 @@ Without a target, `show` renders the whole scene and behaves like a shorter, int
102
104
 
103
105
  ```bash
104
106
  forgecad show model.forge.js
105
- forgecad show model.forge.js Bench
107
+ forgecad show model.forge.js --target Bench
106
108
  ```
107
109
 
108
110
  ### Object Filtering: `--focus` and `--hide`
@@ -110,7 +112,7 @@ forgecad show model.forge.js Bench
110
112
  `render 3d`, `render wireframe`, `inspect <family> <mode>`, `capture`, and `check print` can filter the visible object set without changing model code. Use `forgecad ls model.forge.js` to list exact object paths, then pass names or globs:
111
113
 
112
114
  ```bash
113
- forgecad render 3d model.forge.js bench.png --focus "Bench.*"
115
+ forgecad render 3d model.forge.js --output bench.png --focus "Bench.*"
114
116
  forgecad inspect visual objects model.forge.js --hide "Bench.Slat0,Bench.Slat1" --camera iso
115
117
  ```
116
118
 
@@ -198,7 +200,7 @@ Runs the script headlessly and prints every named render view with an exact came
198
200
 
199
201
  ```bash
200
202
  forgecad render views model.forge.js
201
- forgecad render views model.forge.js --json
203
+ forgecad render views model.forge.js reference.step
202
204
  ```
203
205
 
204
206
  ### `forgecad render wireframe`
@@ -222,7 +224,7 @@ Exports the scene to Blender and renders with Cycles (path tracer); requires Ble
222
224
 
223
225
  ```bash
224
226
  forgecad render hq model.forge.js
225
- forgecad render hq model.forge.js hero.png --preset dramatic --samples 1024
227
+ forgecad render hq model.forge.js --output hero.png --preset dramatic --samples 1024
226
228
  ```
227
229
 
228
230
  ### `forgecad capture gif|mp4` **\[Pro\]**
@@ -233,18 +235,22 @@ Renders an animated sequence: `--capture orbit` (default) for a turntable, `--ca
233
235
 
234
236
  ```bash
235
237
  forgecad capture gif model.forge.js
236
- forgecad capture gif model.forge.js out/sweep.gif --capture section-sweep --sweep-plane YZ
238
+ forgecad capture gif model.forge.js other-model.forge.js
237
239
  ```
238
240
 
239
241
  ### `forgecad render section`
240
242
 
241
243
  Render a 2D cross-section of a 3D model (cut by a plane) to SVG or PNG.
242
244
 
243
- Cuts all shapes with an axis-aligned plane (default XY at Z=0; `--plane XZ|YZ` reorients, `--offset` shifts the cut) and writes a 2D cross-section drawing. The file extension picks the format: `.svg` (default, vector) or `.png` (rasterized at `--size` pixels); `--edges=<off|thin|bold>` sets the outline stroke. Useful for verifying internal geometry, wall thicknesses, and fits that aren't visible in 3D renders.
245
+ Cuts all shapes in the scene with an axis-aligned plane and produces a 2D cross-section drawing. The default plane is XY at Z=0. Use `--plane XZ` or `--plane YZ` for other orientations, and `--offset` to shift the cut position.
246
+
247
+ Output defaults to `.svg`; pass `--format png` or a single-input `--output *.png` path for a rasterized PNG at `--size` pixels. Use `--edges=<off|thin|bold>` to control the outline stroke on cut shapes.
248
+
249
+ Useful for verifying internal geometry, wall thicknesses, and fit checks that aren't visible in 3D renders.
244
250
 
245
251
  ```bash
246
252
  forgecad render section model.forge.js
247
- forgecad render section model.forge.js out/section.svg --plane XZ --offset 10
253
+ forgecad render section model.forge.js --output out/section.svg --plane XZ --offset 10
248
254
  ```
249
255
 
250
256
  | Command | Description |
@@ -270,8 +276,8 @@ These flags work across run / render / capture / inspect commands:
270
276
  | `--camera <front\|back\|side\|right\|top\|iso\|az:el\|az:el:dist\|spec>` | Camera preset, spherical (az:el), or full spec such as `proj=perspective;pos=x,y,z;target=x,y,z;up=x,y,z;fov=45`. Repeatable. |
271
277
  | `--view <name>` | Named camera view declared by the model with scene({ views }) |
272
278
  | `--size <px>` | Image size in pixels |
279
+ | `--backend <manifold\|occt\|truck\|sdf>` | Geometry backend (default: manifold; STEP inputs default to OCCT) |
273
280
  | `--quality <default\|live\|high>` | Mesh quality preset |
274
- | `--backend <manifold\|occt\|truck>` | Geometry backend (default: manifold; STEP inputs default to OCCT) |
275
281
  | `--json` | Print machine-readable JSON |
276
282
 
277
283
  Every command has more — Blender-only `render hq` flags (`--preset`, `--samples`, `--engine`, `--hdri`, `--transparent`, `--video`) and capture-only flags (`--capture`, `--animation`, `--cut-plane`, `--sweep-*`, `--fps`) among them. Run `forgecad <command> --help` for the full list.
@@ -291,7 +297,9 @@ Export to every format you need.
291
297
  | `export stl` | STL | 3D printing |
292
298
  | `export gcode` **\[Production\]** | G-code | Toolpath (scripted, not sliced) |
293
299
  | `export sdf` **\[Production\]** | SDF package | Gazebo robot simulation |
300
+ | `export mjcf` **\[Production\]** | MJCF package | MuJoCo / MJX robot simulation |
294
301
  | `export urdf` **\[Production\]** | URDF package | ROS / PyBullet / MuJoCo |
302
+ | `export usd` **\[Production\]** | USD package | Isaac Sim / OpenUSD simulation |
295
303
  | `export report` **\[Production\]** | PDF report | Multi-view report with BOM and dimensions |
296
304
  | `export cutting-layout` **\[Production\]** | PDF/DXF | Sheet cutting layout with cut sequence |
297
305
  | `link` | URL | Share link from a GitHub Gist URL or ID (copied to clipboard) |
@@ -300,6 +308,7 @@ Export to every format you need.
300
308
  # Sheet material
301
309
  forgecad cut-list shelf.forge.js
302
310
  forgecad export cutting-layout shelf.forge.js --sheet-width 420 --sheet-height 594 --kerf 3
311
+ forgecad export cutting-layout shelf.forge.js --output out/layout.dxf
303
312
 
304
313
  # 3D printing
305
314
  forgecad check print bracket.forge.js
@@ -310,10 +319,12 @@ forgecad export 3mf bracket.forge.js --quality high
310
319
  forgecad export step bracket.forge.js
311
320
 
312
321
  # Technical drawings
313
- forgecad export report bracket.forge.js out/report.pdf
322
+ forgecad export report bracket.forge.js --output out/report.pdf
314
323
 
315
324
  # Robot simulation
316
325
  forgecad export sdf rover.forge.js --output out/forge_scout
326
+ forgecad export mjcf rover.forge.js --output out/forge_scout_mjcf
327
+ forgecad export usd rover.forge.js --output out/forge_scout_usd
317
328
  ```
318
329
 
319
330
  <details>
@@ -322,12 +333,17 @@ forgecad export sdf rover.forge.js --output out/forge_scout
322
333
  | Option | Description |
323
334
  |--------|-------------|
324
335
  | `--joint <JointName=Value>` | Override a Motion tab joint value (JointName=Value). Repeatable. |
325
- | `--output <path>` | Output STEP path |
336
+ | `--output <path>` | Output SVG path for a single input |
326
337
  | `--backend <occt\|truck>` | Exact BREP exporter: occt (default) or truck (native analytic kernel) |
327
338
  | `--quality <default\|live\|high>` | Forge quality preset |
328
339
  | `-o <path>` | Shorthand for --output |
340
+ | `--param <Key=Value>` | Override a parameter value (Key=Value). Repeatable. |
341
+ | `-p <Key=Value>` | Shorthand for --param |
342
+ | `--format <json\|webgpu-brick>` | Implicit artifact format |
343
+ | `-q <live\|default\|high>` | Shorthand for --quality |
344
+ | `--workgroup-size <x>x<y>x<z>` | WebGPU compute workgroup size |
329
345
  | `--dim-angle-tol <deg>` | Dimension routing tolerance in degrees |
330
- | `--format <pdf\|dxf>` | Output format |
346
+ | `--out <path>` | Alias for --output |
331
347
  | `--sheet-width <mm>` | Stock sheet width in mm |
332
348
  | `--sheet-height <mm>` | Stock sheet height in mm |
333
349
  | `--kerf <mm>` | Cutting clearance (saw blade width) in mm |
@@ -451,7 +467,7 @@ forgecad skill flattened-files ~/Desktop/forgecad-skills
451
467
 
452
468
  ## Validation
453
469
 
454
- Check printability and run focused model integrity reviews.
470
+ Check printability, simulation readiness, and focused model integrity.
455
471
 
456
472
  ### `forgecad compare 3d`
457
473
 
@@ -474,7 +490,18 @@ Checks include script `verify.*` results, exact positive-volume object collision
474
490
 
475
491
  ```bash
476
492
  forgecad check print model.forge.js
477
- forgecad check print model.forge.js --json
493
+ forgecad check print model.forge.js other-model.forge.js
494
+ ```
495
+
496
+ ### `forgecad check simready`
497
+
498
+ Validate source-authored robot and physics metadata before simulation export.
499
+
500
+ Runs a Forge script and checks the returned `assembly(...).withSimulation(...)` contract without Isaac Sim, OpenUSD, or NVIDIA validators. The gate validates Sim.body metadata, explicit colliders, contact connectors, controller joints, numeric physics values, and the robot joint graph.
501
+
502
+ ```bash
503
+ forgecad check simready model.forge.js
504
+ forgecad check simready model.forge.js other-model.forge.js
478
505
  ```
479
506
 
480
507
  ### `forgecad inspect mechanical-integrity`
@@ -514,7 +541,7 @@ The CLI is free for personal non-commercial use. Pro covers human-operated comme
514
541
 
515
542
  | Free | Production outputs | Pro | Enterprise |
516
543
  |------|--------------------|-----|------------|
517
- | `run`, `dev`, `studio`, `render 3d`, `export stl`, `export 3mf`, `export svg`, `compare 3d`, `check print`, `inspect fit interference`, `inspect mechanical-integrity` for personal non-commercial use | `cut-list`, `export sketch-pdf`, `export step`, `export brep`, `export gcode`, `export sdf`, `export urdf`, `export report`, `export cutting-layout` are free to run for personal non-commercial use; Pro covers human-operated commercial CAD work | `render hq`, `capture gif`, `capture mp4` plus commercial coverage for client/customer work | Backend, hosted, embedded, or application workflows that call ForgeCAD automatically |
544
+ | `run`, `dev`, `studio`, `render 3d`, `export stl`, `export 3mf`, `export svg`, `compare 3d`, `check print`, `inspect fit interference`, `inspect mechanical-integrity` for personal non-commercial use | `cut-list`, `export sketch-pdf`, `export step`, `export brep`, `export gcode`, `export implicit`, `export sdf`, `export mjcf`, `export urdf`, `export usd`, `export report`, `export cutting-layout` are free to run for personal non-commercial use; Pro covers human-operated commercial CAD work | `render hq`, `capture gif`, `capture mp4` plus commercial coverage for client/customer work | Backend, hosted, embedded, or application workflows that call ForgeCAD automatically |
518
545
 
519
546
  ```bash
520
547
  forgecad license # Check local license status
@@ -5,7 +5,7 @@ skill-order: 100
5
5
 
6
6
  # Assembly API
7
7
 
8
- Assembly-owned links, constraints, connectors, solved poses, and robot export.
8
+ Assembly-owned links, constraints, connectors, solved poses, and source-level simulation metadata.
9
9
 
10
10
  ## Contents
11
11
 
@@ -18,6 +18,59 @@ Assembly-owned links, constraints, connectors, solved poses, and robot export.
18
18
 
19
19
  ### Assembly & Joints
20
20
 
21
+ #### `Sim.material(name: string, options?: SimMaterialOptions): SimMaterialDef` — Create a named physical material with density and contact coefficients for simulation export and checks.
22
+
23
+ `SimMaterialOptions`: `{ densityKgM3?: number, staticFriction?: number, dynamicFriction?: number, restitution?: number }`
24
+
25
+ `SimMaterialDef`: `{ kind: "material", name: string }`
26
+
27
+ #### `Sim.body(options: SimBodyOptions): SimBodyDef` — Describe one assembly part as a physical body with mass/density, material, collider intent, and optional contact surfaces.
28
+
29
+ **`SimBodyOptions`**: `massKg?: number`, `densityKgM3?: number`, `material?: SimMaterialDef`, `collider?: SimColliderDef`, `contacts?: Record<string, SimContactDef>`
30
+
31
+ `SimColliderDef`: `{ kind: "collider", mode: SimColliderMode, reason?: string }`
32
+
33
+ `SimContactDef`: `{ kind: "wheelSurface" | "gripperSurface", connectorName: string }`
34
+
35
+ `SimBodyDef`: `{ kind: "body" }`
36
+
37
+ #### `Sim.collider` — Collision-geometry intent constructors for physical parts.
38
+
39
+ - `Sim.collider.convexHull(): SimColliderDef` — Use a generated collision mesh for the part. This is the default fast rigid-body collider for irregular parts.
40
+ - `Sim.collider.boundingBox(): SimColliderDef` — Use the part bounding box as the collision geometry. This is fastest and works well for chassis and simple blocks.
41
+ - `Sim.collider.visualMesh(): SimColliderDef` — Use the visual mesh as collision geometry. This is exact but usually slower in physics engines.
42
+ - `Sim.collider.none(reason: string): SimColliderDef` — Disable collision for a part with an explicit reason, such as a sensor-only or decorative object.
43
+
44
+ #### `Sim.drive` — Joint-drive intent constructors for passive or powered assembly joints.
45
+
46
+ - `Sim.drive.passive(options?: SimPassiveDriveOptions): SimDriveDef` — Mark a joint as passive while preserving damping and friction metadata for simulation export.
47
+ - `Sim.drive.velocity(options: SimVelocityDriveOptions): SimDriveDef` — Mark a revolute joint as velocity-driven with torque and speed limits. Speed is authored in rpm and exported as deg/s or rad/s as needed.
48
+
49
+ `SimPassiveDriveOptions`: `{ damping?: number, friction?: number }`
50
+
51
+ `SimVelocityDriveOptions`: `{ maxTorqueNm: number, maxSpeedRpm: number }`
52
+
53
+ #### `Sim.contact` — Contact-surface metadata over existing part connectors.
54
+
55
+ - `Sim.contact.wheelSurface(connectorName: string): SimContactDef` — Mark a connector as the wheel tread contact surface for offline checks and downstream simulation metadata.
56
+ - `Sim.contact.gripperSurface(connectorName: string): SimContactDef` — Mark a connector as a gripper pad/contact surface for offline checks and downstream grasp-readiness metadata.
57
+
58
+ #### `Sim.profile` — Named validation/export profile constructors.
59
+
60
+ - `Sim.profile.robotBodyRunnable(): SimProfileDef` — SimReady-style profile for a robot body that should be runnable in a physics simulator.
61
+ - `Sim.profile.robotBodyIsaac(): SimProfileDef` — SimReady-style profile for robot bodies targeting Isaac Sim readiness.
62
+ - `Sim.profile.roboticsAssetPhysx(): SimProfileDef` — SimReady-style profile for robotics assets with PhysX-ready rigid bodies and colliders.
63
+
64
+ `SimProfileDef`: `{ kind: "profile", name: SimProfileName }`
65
+
66
+ #### `Sim.controller` — Standard controller metadata constructors for simulator package generation.
67
+
68
+ - `Sim.controller.diffDrive(options: SimDiffDriveControllerOptions): SimDiffDriveControllerDef` — Describe a differential-drive controller from left/right wheel joints and wheel dimensions.
69
+
70
+ **`SimDiffDriveControllerOptions`**: `leftJoints: string[]`, `rightJoints: string[]`, `wheelSeparationMm: number`, `wheelRadiusMm: number`, `topic?: string`, `odomTopic?: string`, `tfTopic?: string`, `frameId?: string`, `odomFrameId?: string`, `maxLinearVelocity?: number`, `maxAngularVelocity?: number`, `linearAcceleration?: number`, `angularAcceleration?: number`
71
+
72
+ `SimDiffDriveControllerDef`: `{ kind: "diffDrive" }`
73
+
21
74
  #### `assembly(name?: string): Assembly` — Create an assembly container with named parts, connectors, and kinematic links.
22
75
 
23
76
  **Use this from iteration 1 for any model with moving parts.** Do not build one static pose and retrofit motion later.
@@ -151,7 +204,7 @@ const housing = group(
151
204
  assembly.addPart("Base Assembly", housing);
152
205
  ```
153
206
 
154
- **`PartOptions`**: `transform?: TransformInput`, `metadata?: PartMetadata`, `mate?: AssemblyPartMateInput | AssemblyPartMateInput[]`, `bindToFrame?: string`
207
+ **`PartOptions`**: `transform?: TransformInput`, `metadata?: PartMetadata`, `sim?: SimBodyDef`, `mate?: AssemblyPartMateInput | AssemblyPartMateInput[]`, `bindToFrame?: string`
155
208
 
156
209
  **`PartMetadata`**
157
210
 
@@ -212,7 +265,9 @@ Frame semantics: `origin` is the pivot/contact point, `axis` the hinge or slide
212
265
 
213
266
  **Face-to-face:** each connector's axis points outward from its part; mating makes the axes anti-parallel, like a plug meeting a socket (same convention as `matchTo()`).
214
267
 
215
- **Mirrored revolute axes:** revolute values follow the right-hand rule, so a mirrored hinge axis (`[1, 0, 0]` vs `[-1, 0, 0]`) rotates oppositely for the same `+theta`: negate the mirrored side's value and mirror limits as `[min, max] -> [-max, -min]`. Prismatic joints have no handedness flip. Use an explicit per-side sign mapping (or side-neutral link controls) for bilateral mechanisms.
268
+ **Revolute sign:** a positive joint value follows the right-hand rule about the **child** connector's placed axis. Because face-to-face mating makes the axes anti-parallel, that is the *left*-hand rule about the parent connector's outward axis if `+30` swings the opposite way you expected, you predicted from the parent's axis. `forgecad debug assembly` prints each joint's resolved world axis.
269
+
270
+ **Mirrored revolute axes:** because of the right-hand rule, a mirrored hinge axis (`[1, 0, 0]` vs `[-1, 0, 0]`) rotates oppositely for the same `+theta`: negate the mirrored side's value and mirror limits as `[min, max] -> [-max, -min]`. Prismatic joints have no handedness flip. Use an explicit per-side sign mapping (or side-neutral link controls) for bilateral mechanisms.
216
271
 
217
272
  Joint type defaults to the connector's `kind`. For `start`/`end` connectors, `align` / `parentAlign` / `childAlign` (`'start' | 'middle' | 'end'`) choose which point meets.
218
273
 
@@ -239,7 +294,7 @@ assembly("Door").addPart("Frame", frame).addPart("Door", door)
239
294
  | `align?` | `PortAlign` | Shorthand: set both parentAlign and childAlign at once. |
240
295
  | `follows?` | `JointFollowOptions` | Slave this joint to another joint: `value = ratio × source + offset` (e.g. a mirrored jaw with `ratio: -1`). |
241
296
 
242
- Also: `as?: string`, `type?: JointType`, `default?: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`.
297
+ Also: `as?: string`, `type?: JointType`, `default?: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `drive?: SimDriveDef`.
243
298
 
244
299
  **`JointFollowOptions`**
245
300
  - `joint: string` — Name of the source joint that drives this one.
@@ -299,6 +354,12 @@ return mech.solve({ theta: 45 });
299
354
 
300
355
  **Other**
301
356
 
357
+ #### `withSimulation(options: SimAssemblySimulationOptions): Assembly` — Attach the root simulation contract for this assembly.
358
+
359
+ Use this after adding physical parts and joints. Robot-body profiles require `rootPart`; asset profiles can describe one-part or multi-part physical assets. URDF/SDF exporters and `forgecad check simready` read this contract directly, so model files no longer need a separate `robotExport(...)` side effect.
360
+
361
+ `SimAssemblySimulationOptions`: `{ profile: SimProfileDef, rootPart?: string, controllers?: SimControllerDef[] }`
362
+
302
363
  #### `edgeBetweenFrames(a: string, b: string, options?: AssemblyFrameEdgeOptions): Assembly` — Add a visual skeleton edge between two rig frame origins.
303
364
 
304
365
  Frame edges follow the solved frame poses produced by `fixedJoint()`, `revoluteJoint()`, and `prismaticJoint()`. They do not add constraints, degrees of freedom, parts, or geometry; use them to make a frame-only rig readable in the Motion/rig inspection overlay.
@@ -434,7 +495,7 @@ The sub-assembly must have exactly one root part before it can be merged (collap
434
495
  | `connectorRefs?` | `JointConnectorRefs` | Connector refs that define this joint contract. Usually set by `connect()` / `match()`. |
435
496
  | `follows?` | `JointFollowOptions` | Slave this joint to another joint: `value = ratio × source + offset`. Use for mechanisms with one physical DOF expressed through several joints — a mirrored gripper jaw (`ratio: -1`), a gear pair, a drive crank turning with its servo. A followed joint stops being an independent control: the Motion view drives it from its source, `solve()` derives its value (a direct state override is ignored with a warning), and limits still clamp the derived value. |
436
497
 
437
- Also: `frame?: TransformInput`, `origin?: Vec3`, `axis?: Vec3`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`.
498
+ Also: `frame?: TransformInput`, `origin?: Vec3`, `axis?: Vec3`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `drive?: SimDriveDef`.
438
499
 
439
500
  `JointConnectorRefs`: `{ parent: string, child: string, parentAlign?: PortAlign, childAlign?: PortAlign }`
440
501
 
@@ -506,4 +567,4 @@ console.log("Collisions", solved.collisionReport());
506
567
 
507
568
  #### `minClearance(partA: string, partB: string, searchLength?: number): number` — Compute the minimum gap (clearance) between two parts in this solved pose.
508
569
 
509
- Returns `0` if the parts are touching or overlapping. Requires the Manifold backend. `searchLength` bounds the search radius in mm — increase it for widely separated parts.
570
+ Returns `0` if the parts are touching or overlapping. Manifold-backed parts use the exact Manifold gap query. SDF-backed parts use a mesh-derived sampled gap. `searchLength` bounds the Manifold search radius in mm — increase it for widely separated Manifold parts.
@@ -111,6 +111,8 @@ The `edges` parameter is flexible:
111
111
 
112
112
  Throws if no edges match the selection, or if `radius` is not a positive finite number.
113
113
 
114
+ Selectorless (all-edges) calls draw from a per-run broad edge-feature budget. Exceeding it throws — except in live preview, which skips the finish with a warning for responsiveness. Explicit edge selectors are never budgeted; `FORGECAD_BROAD_EDGE_FEATURE_BUDGET` / `FORGECAD_ALLOW_BROAD_EDGE_FEATURES=1` raise or lift the budget.
115
+
114
116
  ```ts
115
117
  // Fillet all edges
116
118
  fillet(myShape, 2)
@@ -133,6 +135,8 @@ fillet(base, 5, base.edge('vert-br'))
133
135
 
134
136
  Produces a 45° bevel at the specified `size` (distance from edge). Edge selections compile into backend operations; unsupported selections fail as explicit kernel gaps instead of using TypeScript geometry fallbacks.
135
137
 
138
+ Selectorless (all-edges) calls draw from a per-run broad edge-feature budget. Exceeding it throws — except in live preview, which skips the finish with a warning for responsiveness. Explicit edge selectors are never budgeted; `FORGECAD_BROAD_EDGE_FEATURE_BUDGET` / `FORGECAD_ALLOW_BROAD_EDGE_FEATURES=1` raise or lift the budget.
139
+
136
140
  The `edges` parameter accepts the same options as `fillet()`: inline `EdgeQuery`, pre-selected `EdgeSegment`/`EdgeSegment[]`, a tracked `EdgeRef` from `shape.edge('vert-br')` (exact compiler-owned path), or `undefined` (all sharp edges).
137
141
 
138
142
  ```ts
@@ -151,7 +155,7 @@ chamfer(base, 3, base.edge('vert-br'))
151
155
 
152
156
  Adds a taper angle to the vertical faces of a solid so that it can be extracted from a mold. The neutral plane is the Z position where the draft angle is zero — faces above and below are tapered symmetrically. Typical values for injection molding are 1–5°.
153
157
 
154
- Truck supports vertical-prism solids with Z-axis pull directions. OCCT uses its native draft operation when available. Manifold throws.
158
+ SDF, Manifold, and Truck lower supported vertical-prism solids with Z-axis pull directions to a tapered loft. OCCT uses its native draft operation when available.
155
159
 
156
160
  ```ts
157
161
  // Add 3° draft to a box for injection molding
@@ -1406,15 +1410,18 @@ Namespaced file-format import helpers — the single vocabulary for bringing ext
1406
1410
 
1407
1411
  - `dxfSketch(fileName: string, options?: DxfImportOptions): Sketch` — Parse a DXF file and return closed 2D profile geometry as a Sketch. The result can be extruded directly.
1408
1412
  - `svgSketch(fileName: string, options?: SvgImportOptions): Sketch` — Parse an SVG file and return it as a Sketch with options for region filtering, scaling, and simplification.
1409
- - `mesh(fileName: string, options?: { scale?: number; center?: boolean; object?: string; separateObjects?: boolean; }): Shape | ShapeGroup` — Import an external mesh file (STL, OBJ, 3MF).
1413
+ - `mesh(fileName: string, options?: MeshImportOptions): Shape | ShapeGroup` — Import an external mesh file (STL, OBJ, 3MF).
1410
1414
 
1411
1415
  By default, 3MF build items are flattened into one Shape for compatibility. Use `separateObjects: true` to import 3MF build items/resource objects as a named ShapeGroup whose children are targetable by `forgecad ls`. Use `object` to import one item by the stable ref/name reported by `forgecad run`.
1412
1416
 
1413
1417
  For 3MF sources, `forgecad run` prints a source-structure table with one line per build item: `[3mf:build:NNN:object:N] name type=... verts=... tris=... bbox=[min] → [max]`. Build items are numbered from `001`; files with no build items list resource objects as `3mf:object:N` instead. Per-item bboxes reveal multi-part structure — account for every substantial item before flattening. Pass any listed stable ref or name as `object` to import that item alone.
1414
1418
 
1419
+ Use `sourceFrame: { up: "+Y" }` when the file was authored in a non-Z-up coordinate system. ForgeCAD remains Z-up; the import is rotated so the named source axis becomes ForgeCAD +Z. Supported values: `"+X"`, `"-X"`, `"+Y"`, `"-Y"`, `"+Z"`, `"-Z"`.
1420
+
1415
1421
  ```js
1416
1422
  const all = Import.mesh("./assembly.3mf", { separateObjects: true });
1417
1423
  const pin = all.child("Pin #001");
1418
1424
  const plate = Import.mesh("./assembly.3mf", { object: "3mf:build:001:object:7" });
1425
+ const yUpPart = Import.mesh("./part.obj", { sourceFrame: { up: "+Y" } });
1419
1426
  ```
1420
- - `step(fileName: string): Shape` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend.
1427
+ - `step(fileName: string, options?: StepImportOptions): Shape` — Import a STEP file (.step, .stp) as an exact OCCT-backed Shape. Preserves NURBS curves, B-spline surfaces, and exact topology. Requires running with the OCCT backend. Use `sourceFrame: { up: "+Y" }` to rotate Y-up source files into ForgeCAD's Z-up world.
@@ -58,12 +58,14 @@ bom(tubeLen, "rectangular steel tube", {
58
58
  | `notes?` | `string` | Free-form notes |
59
59
  | `grain?` | `string` | Wood grain direction, e.g. "long", "cross" |
60
60
 
61
- #### `robotExport(options: RobotExportOptions): CollectedRobotExport` — Declare that this script should export the assembly as a SDF/URDF robot package.
61
+ #### `robotExport(options: RobotExportOptions): CollectedRobotExport` — Compatibility shim for SDF/URDF robot package metadata.
62
62
 
63
- Call `robotExport()` alongside your assembly definition. `forgecad export sdf` / `forgecad export urdf` pick up the declaration (see the CLI docs for flags) and produce a robot package with:
63
+ Prefer returning `assembly(...).withSimulation(...)` with `Sim.body(...)`, `Sim.drive.*(...)`, and `Sim.controller.*(...)` metadata. The CLI commands `forgecad export sdf` and `forgecad export urdf` now read that assembly simulation contract directly.
64
+
65
+ `robotExport()` remains available for one compatibility window. It converts the legacy descriptor into the same internal simulation model used by source-authored `Sim` metadata and produces a robot package with:
64
66
 
65
67
  - Mesh-based inertia tensors (full 6-component, not bounding-box approximations)
66
- - Separate collision meshes (convex hull by default — ~50–80% smaller)
68
+ - Separate collision meshes
67
69
  - Joint limits, effort/velocity/damping/friction metadata from assembly joints
68
70
 
69
71
  **Collision mesh modes** (set per-link via `links["PartName"].collision`):
@@ -82,6 +84,8 @@ Call `robotExport()` alongside your assembly definition. `forgecad export sdf` /
82
84
  - `massKg` is preferred; `densityKgM3` is used when mass is unknown.
83
85
  - Compatibility coupling metadata, when present, maps only the primary term (largest ratio) to `<mimic>` — SDF/URDF support single-leader mimic only. Dropped terms emit a warning.
84
86
 
87
+ **Legacy example**
88
+
85
89
  ```ts
86
90
  robotExport({
87
91
  assembly: rover, // assembly() with parts + revolute wheel joints
@@ -97,6 +101,13 @@ robotExport({
97
101
  });
98
102
  ```
99
103
 
104
+ **Preferred CLI usage**
105
+
106
+ ```bash
107
+ forgecad export sdf model.forge.js # SDF package (Gazebo/Ignition)
108
+ forgecad export urdf model.forge.js # URDF package (ROS/PyBullet/MuJoCo)
109
+ ```
110
+
100
111
  **`RobotExportOptions`**: `assembly: Assembly`, `modelName?: string`, `state?: JointState`, `static?: boolean`, `selfCollide?: boolean`, `allowAutoDisable?: boolean`, `links?: Record<string, RobotLinkExportOptions>`, `joints?: Record<string, RobotJointExportOptions>`, `plugins?: { diffDrive?: RobotDiffDrivePluginOptions; jointStatePublisher?: RobotJointStatePublisherOptions; }`, `world?: RobotWorldOptions`
101
112
 
102
113
  `RobotLinkExportOptions`: `{ massKg?: number, densityKgM3?: number, collision?: "visual" | "convex" | "box" | "none" }`
@@ -111,46 +122,6 @@ robotExport({
111
122
 
112
123
  `RobotWorldKeyboardTeleopOptions`: `{ enabled?: boolean, linearStep?: number, angularStep?: number }`
113
124
 
114
- **`CollectedRobotExport`**: `modelName: string`, `assembly: AssemblyDefinition`, `state: JointState`, `static: boolean`, `selfCollide: boolean`, `allowAutoDisable: boolean`, `links: Record<string, RobotLinkExportOptions>`, `joints: Record<string, RobotJointExportOptions>`, `plugins: { diffDrive?: RobotDiffDrivePluginOptions; jointStatePublisher?: RobotJointStatePublisherOptions; }`, `world: RobotWorldOptions | null`
115
-
116
- **`AssemblyDefinition`**: `name: string`, `parts: AssemblyPartDef[]`, `joints: AssemblyJointDef[]`, `jointCouplings: AssemblyJointCouplingDef[]`, `kinematics: AssemblyKinematicGraphDef`, `frames: AssemblyFrameDef[]`, `frameJoints: AssemblyFrameJointDef[]`, `frameEdges: AssemblyFrameEdgeDef[]`
117
-
118
- **`AssemblyPartDef`**: `name: string`, `part: AssemblyPart`, `base: Transform`, `metadata?: PartMetadata`, `mates: AssemblyPartMateInput[]`, `bindToFrame?: string`
119
-
120
- `PartMetadata` — defined in [assembly](/docs/assembly).
121
-
122
- `AssemblyPartMateInput` — defined in [assembly](/docs/assembly).
123
-
124
- **`AssemblyJointDef`**: `name: string`, `type: JointType`, `parent: string`, `child: string`, `frame: Transform`, `axis: Vec3`, `min?: number`, `max?: number`, `defaultValue: number`, `unit?: string`, `effort?: number`, `velocity?: number`, `damping?: number`, `friction?: number`, `connectorRefs?: JointConnectorRefs`
125
-
126
- `JointConnectorRefs` — defined in [assembly](/docs/assembly).
127
-
128
- `AssemblyJointCouplingDef`: `{ joint: string, terms: JointCouplingTermRecord[], offset: number }`
129
-
130
- `JointCouplingTermRecord`: `{ joint: string, ratio: number }`
131
-
132
- **`AssemblyKinematicGraphDef`**: `links: AssemblyLinkDef[]`, `edges: AssemblyEdgeBetweenLinksDef[]`, `angles: AssemblyAngleBetweenLinksDef[]`, `derivedLinks: AssemblyDerivedLinkDef[]`
133
-
134
- `AssemblyLinkDef`: `{ name: string, at: Vec3, fixed: boolean, metadata?: Record<string, unknown> }`
135
-
136
- **`AssemblyEdgeBetweenLinksDef`**: `name: string`, `a: string`, `b: string`, `length: number | null`, `min?: number`, `max?: number`, `visualOnly: boolean`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
137
-
138
- `AssemblyKinematicControlOptions` — defined in [assembly](/docs/assembly).
139
-
140
- **`AssemblyAngleBetweenLinksDef`**: `name: string`, `a?: string`, `b: string`, `c: string`, `reference?: AssemblyAngleReferenceDef`, `target?: number`, `min?: number`, `max?: number`, `control?: AssemblyKinematicControlOptions`, `metadata?: Record<string, unknown>`
141
-
142
- `AssemblyAngleReferenceDef`: `{ kind: "worldDirection", direction: Vec3 }`
143
-
144
- **`AssemblyDerivedLinkDef`**
145
- - `distance: number` — Signed: positive moves from `fromLink` toward `towardLink`, negative moves away.
146
- - Also: `name: string`, `fromLink: string`, `towardLink: string`.
147
-
148
- `AssemblyFrameDef`: `{ name: string, origin: Vec3, axis: Vec3, up: Vec3, fixed: boolean, metadata?: Record<string, unknown> }`
149
-
150
- **`AssemblyFrameJointDef`**: `name: string`, `type: AssemblyFrameJointType`, `parent: string`, `child: string`, `rest: Transform`, `min?: number`, `max?: number`, `defaultValue: number`, `unit?: string`, `control: boolean`, `metadata?: Record<string, unknown>`
151
-
152
- `AssemblyFrameEdgeDef`: `{ name: string, a: string, b: string, metadata?: Record<string, unknown> }`
153
-
154
125
  #### `dim()` — Add a dimension annotation between two points, or along an entity.
155
126
 
156
127
  Overloads:
@@ -26,10 +26,10 @@ Point2D, Points, polygon, polygonVertices, port, Product, ProductPanelBuilder, P
26
26
  ProductSkin, ProductSkinBuilder, ProductStationBuilder, ProductSurfaceBuilder, ProductSurfaceRef, projectToPlane, queueMicrotask, rect
27
27
  Rectangle2D, robotExport, roundedRect, Route3D, scene, Sculpt, sdf, SdfShape
28
28
  selectEdge, selectEdges, self, setActiveBackend, setImmediate, setInterval, setTimeout, Shape
29
- ShapeGroup, sheetMetal, SheetMetalPart, sheetStock, Sketch, sketchToDxf, sketchToSvg, slot
30
- SolvedAssembly, spec, sphere, spline2d, stroke, Surface, SurfaceBody, SurfaceMembers
31
- sweep, text2d, textWidth, torus, toShape, Transform, union, union2d
32
- variableSweep, verify, Viewport, window, Wood
29
+ ShapeGroup, sheetMetal, SheetMetalPart, sheetStock, Sim, Sketch, sketchToDxf, sketchToSvg
30
+ slot, SolvedAssembly, spec, sphere, spline2d, stroke, Surface, SurfaceBody
31
+ SurfaceMembers, sweep, text2d, textWidth, torus, toShape, Transform, union
32
+ union2d, variableSweep, verify, Viewport, window, Wood
33
33
  ```
34
34
 
35
35
  `showLabels` is also a runtime global, but it is not part of the top-level collision check. Avoid reusing it unless you intentionally want a local value with that name.
@@ -53,7 +53,7 @@ An immutable SDF expression. Supports SDF-specific operations (smooth booleans,
53
53
 
54
54
  #### `clone(): SdfShape` — Clone this SDF expression and its visual metadata.
55
55
 
56
- #### `toShape(options?: SdfToShapeOptions): Shape` — Mesh this SDF into a ForgeCAD Shape through ForgeCAD's Surface Nets pipeline. Once converted, the result is a regular Shape — booleans, transforms, export all work.
56
+ #### `toShape(options?: SdfToShapeOptions): Shape` — Mesh this SDF into a ForgeCAD Shape. Typed SDF trees materialize through Rust Manifold Dual Contouring; dynamic trees (custom/noise/voronoi/displace/blend callbacks) mesh through the Surface Nets pipeline. Once converted, the result is a regular Shape — booleans, transforms, export all work.
57
57
 
58
58
  #### `color(value: string | undefined): SdfShape` — Set the display color for this implicit leaf.
59
59
 
@@ -230,7 +230,7 @@ return {
230
230
  - `basketWeave(options?: BasketWeaveOptions): SurfacePattern` — Basket weave surface pattern — threads with over-under crossings in UV space. Returns a SurfacePattern for use with `.surfaceDisplace()`.
231
231
  - `pattern2d(): Pattern2DBuilder` — Create typed, composable 2D surface patterns for `.surfaceDisplace()`.
232
232
  - `SurfacePattern: typeof SurfacePattern` — A 2D surface pattern — a heightmap function for use with `.surfaceDisplace()`.
233
- - `fromFunction(fn: SdfFunctionSource, options: SdfFunctionOptions): SdfShape` — Create a custom SDF from one expression; shader-safe expressions raymarch directly.
233
+ - `fromFunction(fn: SdfFunctionSource, options: SdfFunctionOptions): SdfShape` — Create a custom SDF from one expression; shader-safe expressions keep shader metadata for tooling.
234
234
  - `combine(value: unknown, options?: CombineOptions): SdfShape` — Collapse a plain object/array tree of SDF leaves into one continuous implicit field. Per-leaf color/material identity is intentionally discarded — the result is one scalar field. Use plain object returns for multi-material SDF preview, and `sdf.combine(...)` only when you want one implicit body. Explicit shape lists are covered by `a.union(b, c)`; pass `{ op: 'intersection' }` to intersect instead.
235
235
 
236
236
  ```js
@@ -1,5 +1,5 @@
1
1
  // Gyroid-diamond blend — shader-compatible TPMS fields combined in one form.
2
- // Return the raw SDF for fast native preview; use toShape(...) when exporting.
2
+ // Return the SDF directly for implicit preview data; use toShape(...) for export tuning.
3
3
 
4
4
  const bounds = sdf.sphere(22);
5
5
  const gyroid = sdf.gyroid({ cellSize: 10, thickness: 1.0 });
@@ -1,5 +1,5 @@
1
1
  // Organic noise sculpture — natural, coral-like form
2
- // Noise is still materialized because native preview does not raymarch it yet.
2
+ // Explicit meshing keeps the noisy field on the ordinary mesh preview/export path.
3
3
 
4
4
  const shell = sdf.sphere(20).shell(4);
5
5
  const texture = sdf.noise({ scale: 0.15, amplitude: 3, octaves: 3 });
@@ -16,4 +16,4 @@ const leftHandGroove = sdf.box(2.4, 1.2, 48)
16
16
  return body
17
17
  .subtract(rightHandGroove, leftHandGroove)
18
18
  .color('#8da2ad')
19
- .material({ roughness: 0.38, metalness: 0.18 });
19
+ .material({ roughness: 0.38, metalness: 0.18 }).toShape({edgeLength: 0.2});
@@ -1,8 +1,7 @@
1
- // Custom SDF native preview — one user-authored expression drives CPU sampling
2
- // and, when shader-safe, native raymarch preview.
1
+ // Custom SDF implicit preview — one user-authored expression drives the field.
3
2
  //
4
- // This returns a raw SdfShape. Do not call .toShape() here; keeping it implicit
5
- // proves the custom function can preview through the raymarch shader.
3
+ // Returning SdfShape directly keeps the field implicit. Call .toShape(...) when
4
+ // you need explicit mesh quality for CAD operations or export.
6
5
 
7
6
  const rippledSphere = sdf.fromFunction(
8
7
  (x, y, z, radius, frequency, amplitude) =>
@@ -1,4 +1,4 @@
1
- // Materialize a raw SDF tree when you need mesh-backed CAD/export behavior.
1
+ // Return a plain SDF tree; scene mapping keeps SDF leaves implicit.
2
2
 
3
3
  const insert = {
4
4
  shell: sdf.sphere(18)
@@ -9,4 +9,4 @@ const insert = {
9
9
  .color('#ffcf5a'),
10
10
  };
11
11
 
12
- return insert;
12
+ return insert;
@@ -1,6 +1,7 @@
1
- // Plain raw SDF return: no Shape, no .toShape(), no Sculpt facade.
1
+ // Plain SDF return: no explicit Shape, no .toShape(), no Sculpt facade.
2
2
  //
3
- // This should render directly in Studio and the CLI.
3
+ // Direct returns stay as implicit SDF preview data; call .toShape() when you
4
+ // need a mesh-backed Shape for CAD operations or export.
4
5
 
5
6
  scene(Sculpt.look('soft-studio'));
6
7
 
@@ -1,6 +1,6 @@
1
- // SDF native preview — raw SDF leaves render directly by raymarching.
1
+ // SDF implicit preview — SDF leaves stay lazy until .toShape(...).
2
2
  //
3
- // Use .toShape() only when you need mesh-backed CAD operations or export.
3
+ // Use .toShape(...) when you need explicit meshing controls, CAD operations, or export.
4
4
 
5
5
  const smoothBlob = sdf.smoothUnion(
6
6
  sdf.sphere(12),
@@ -1,5 +1,5 @@
1
- // Native SDF surface relief — user-composed Pattern2D IR lowers to shader + CPU.
2
- // These raw SDF leaves raymarch directly; call .toShape() only for export.
1
+ // Native SDF surface relief — user-composed Pattern2D IR lowers to CPU sampling.
2
+ // Direct SDF returns stay implicit; call .toShape(...) for mesh-backed export.
3
3
 
4
4
  scene({
5
5
  background: { top: '#f6fbff', bottom: '#dfe9f3' },
@@ -1,4 +1,4 @@
1
- // Twisted lattice tower — gyroid field raymarched directly in the viewport.
1
+ // Twisted lattice tower — direct SDF return materializes as a mesh-backed preview.
2
2
 
3
3
  const column = sdf.cylinder(54, 13);
4
4
 
@@ -1,5 +1,5 @@
1
1
  // Voronoi lampshade — organic cell-wall dome, open at the bottom.
2
- // Voronoi is still materialized because native preview does not raymarch it yet.
2
+ // Explicit meshing keeps this dense field on the ordinary mesh preview/export path.
3
3
 
4
4
  const dome = sdf.sphere(30)
5
5
  .subtract(sdf.sphere(27))
@@ -0,0 +1,46 @@
1
+ # Robotics And SimReady Examples
2
+
3
+ These examples show the current ForgeCAD simulation contract: physical parts use `Sim.body(...)`, joints carry drive intent, and the returned assembly finishes with `withSimulation(...)`.
4
+
5
+ Run commands from the ForgeCAD repo root with the local CLI:
6
+
7
+ ```bash
8
+ node dist-cli/forgecad.js check simready examples/robotics/simready-diff-drive-rover.forge.js
9
+ node dist-cli/forgecad.js export mjcf examples/robotics/simready-diff-drive-rover.forge.js --output /tmp/forgecad-rover-mjcf
10
+ ```
11
+
12
+ ## Examples
13
+
14
+ | Example | File | What It Proves |
15
+ |---------|------|----------------|
16
+ | Diff-drive rover | `simready-diff-drive-rover.forge.js` | Robot-body profile, wheel contact surfaces, velocity drives, and diff-drive controller metadata |
17
+ | Parallel gripper | `simready-parallel-gripper.forge.js` | Prismatic joints, passive dynamics, and gripper contact-surface metadata |
18
+ | Asset crate | `simready-asset-crate.forge.js` | One-part asset assembly with physical material, collider, and grasp-ready contact metadata |
19
+ | Scout cam rover | `scout-cam-rover-simready/main.forge.js` | Larger copied robot with MJCF, SDF, URDF, MuJoCo smoke motion, starter Gym env, balance training, and push playback |
20
+
21
+ ## Minimum Workflow
22
+
23
+ ```bash
24
+ node dist-cli/forgecad.js run examples/robotics/simready-diff-drive-rover.forge.js
25
+ node dist-cli/forgecad.js check simready examples/robotics/simready-diff-drive-rover.forge.js
26
+ node dist-cli/forgecad.js export mjcf examples/robotics/simready-diff-drive-rover.forge.js --output /tmp/forgecad-rover-mjcf
27
+ ```
28
+
29
+ The MJCF export writes a runnable MuJoCo package with `scene.xml`, `manifest.json`, `simready-manifest.json`, and starter Python scripts.
30
+
31
+ ```bash
32
+ cd /tmp/forgecad-rover-mjcf
33
+ uv run --python 3.11 --with mujoco --with pillow python scripts/smoke_control.py --render-gif drive.gif
34
+ ```
35
+
36
+ The smaller examples are meant to be readable API references. The Scout example is the end-to-end simulator workflow.
37
+
38
+ ## Picking The Right Example
39
+
40
+ Use `simready-diff-drive-rover.forge.js` when you want the smallest robot that proves the full controller/export path.
41
+
42
+ Use `simready-parallel-gripper.forge.js` when you care about manipulators, fingertips, or grasp-readiness metadata.
43
+
44
+ Use `simready-asset-crate.forge.js` when the object is not a robot but still needs physical material, collision, and contact metadata.
45
+
46
+ Use `scout-cam-rover-simready/` when you want MuJoCo scripts, generated package structure, GIF output, and a realistic source-authored robot example.