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
@@ -0,0 +1,173 @@
1
+ # SimReady Quickstart
2
+
3
+ ForgeCAD simulation readiness starts in the model file. The `.forge.js` script should return the same assembly you want to inspect, export, and simulate; simulation metadata lives on that assembly instead of in a separate export command.
4
+
5
+ Use this pattern when you want a robot, fixture, gripper, crate, or imported asset to become a physics package later:
6
+
7
+ 1. Build real parts with connectors.
8
+ 2. Add each part to an `assembly(...)` with `sim: Sim.body(...)`.
9
+ 3. Connect moving parts with `connect(...)` and put drive intent on the joint.
10
+ 4. Finish the root assembly with `withSimulation(...)`.
11
+ 5. Run `forgecad check simready` before exporting.
12
+
13
+ ## Robot Pattern
14
+
15
+ ```js
16
+ const aluminum = Sim.material("6061 aluminum", {
17
+ densityKgM3: 2700,
18
+ staticFriction: 0.45,
19
+ dynamicFriction: 0.35,
20
+ restitution: 0.05,
21
+ });
22
+
23
+ const rubber = Sim.material("rubber tire", {
24
+ densityKgM3: 1100,
25
+ staticFriction: 0.9,
26
+ dynamicFriction: 0.75,
27
+ restitution: 0.12,
28
+ });
29
+
30
+ const chassis = box(220, 120, 28).withConnectors({
31
+ left_axle: connector({ origin: [0, 82, 0], axis: [0, 1, 0], up: [0, 0, 1], kind: "revolute" }),
32
+ right_axle: connector({ origin: [0, -82, 0], axis: [0, -1, 0], up: [0, 0, 1], kind: "revolute" }),
33
+ });
34
+
35
+ const wheel = cylinder(34, 28, undefined, 48)
36
+ .rotateX(90)
37
+ .withConnectors({
38
+ bore: connector({ origin: [0, 0, 0], axis: [0, 1, 0], up: [0, 0, 1], kind: "revolute" }),
39
+ tread: connector({ origin: [0, 0, -34], axis: [0, 0, -1], up: [1, 0, 0] }),
40
+ });
41
+
42
+ return assembly("Rover")
43
+ .addPart("Chassis", chassis, {
44
+ sim: Sim.body({
45
+ massKg: 4.2,
46
+ material: aluminum,
47
+ collider: Sim.collider.boundingBox(),
48
+ }),
49
+ })
50
+ .addPart("Left Wheel", wheel, {
51
+ sim: Sim.body({
52
+ massKg: 0.32,
53
+ material: rubber,
54
+ collider: Sim.collider.convexHull(),
55
+ contacts: { tread: Sim.contact.wheelSurface("tread") },
56
+ }),
57
+ })
58
+ .addPart("Right Wheel", wheel, {
59
+ sim: Sim.body({
60
+ massKg: 0.32,
61
+ material: rubber,
62
+ collider: Sim.collider.convexHull(),
63
+ contacts: { tread: Sim.contact.wheelSurface("tread") },
64
+ }),
65
+ })
66
+ .connect("Chassis.left_axle", "Left Wheel.bore", {
67
+ as: "leftWheel",
68
+ type: "revolute",
69
+ drive: Sim.drive.velocity({ maxTorqueNm: 1.8, maxSpeedRpm: 220, damping: 0.02, friction: 0.01 }),
70
+ })
71
+ .connect("Chassis.right_axle", "Right Wheel.bore", {
72
+ as: "rightWheel",
73
+ type: "revolute",
74
+ drive: Sim.drive.velocity({ maxTorqueNm: 1.8, maxSpeedRpm: 220, damping: 0.02, friction: 0.01 }),
75
+ })
76
+ .withSimulation({
77
+ rootPart: "Chassis",
78
+ profile: Sim.profile.robotBodyRunnable(),
79
+ controllers: [
80
+ Sim.controller.diffDrive({
81
+ leftJoints: ["leftWheel"],
82
+ rightJoints: ["rightWheel"],
83
+ wheelRadiusMm: 34,
84
+ wheelSeparationMm: 164,
85
+ }),
86
+ ],
87
+ });
88
+ ```
89
+
90
+ The important names are authored once:
91
+
92
+ - `rootPart` names the body that downstream simulators treat as the robot root.
93
+ - `leftWheel` and `rightWheel` are joint names, so controllers and exporters do not need to guess motor wiring.
94
+ - `tread` is an existing connector on the wheel, so contact metadata is checked against geometry-owned names.
95
+
96
+ ## Asset Pattern
97
+
98
+ Assets use the same assembly contract. In V1, a crate, prop, imported STEP, or static environment object is usually a one-part assembly:
99
+
100
+ ```js
101
+ const pine = Sim.material("pine crate", {
102
+ densityKgM3: 520,
103
+ staticFriction: 0.55,
104
+ dynamicFriction: 0.42,
105
+ restitution: 0.08,
106
+ });
107
+
108
+ const crate = box(140, 110, 80).withConnectors({
109
+ grip: connector({ origin: [0, -56, 8], axis: [0, -1, 0], up: [0, 0, 1] }),
110
+ });
111
+
112
+ return assembly("Crate Asset")
113
+ .addPart("Body", crate, {
114
+ sim: Sim.body({
115
+ massKg: 2.4,
116
+ material: pine,
117
+ collider: Sim.collider.convexHull(),
118
+ contacts: { grip: Sim.contact.gripperSurface("grip") },
119
+ }),
120
+ })
121
+ .withSimulation({
122
+ profile: Sim.profile.roboticsAssetPhysx(),
123
+ });
124
+ ```
125
+
126
+ Asset profiles require physical body metadata and explicit collider intent. They do not require `rootPart`, joints, or controllers.
127
+
128
+ ## Check Then Export
129
+
130
+ Run the offline gate first:
131
+
132
+ ```bash
133
+ node dist-cli/forgecad.js check simready examples/robotics/simready-diff-drive-rover.forge.js
134
+ ```
135
+
136
+ Then export the package that matches your simulator:
137
+
138
+ ```bash
139
+ node dist-cli/forgecad.js export mjcf examples/robotics/simready-diff-drive-rover.forge.js --output /tmp/rover-mjcf
140
+ node dist-cli/forgecad.js export sdf examples/robotics/simready-diff-drive-rover.forge.js --output /tmp/rover-sdf
141
+ node dist-cli/forgecad.js export urdf examples/robotics/simready-diff-drive-rover.forge.js --output /tmp/rover-urdf
142
+ node dist-cli/forgecad.js export usd examples/robotics/simready-diff-drive-rover.forge.js --output /tmp/rover-usd
143
+ ```
144
+
145
+ Use MJCF/MuJoCo for the fastest local physics loop. Use SDF for Gazebo Sim. Use URDF when the next tool expects a ROS/PyBullet/Isaac import path.
146
+ Use USD for native Isaac Sim/OpenUSD handoff: the package contains a text `.usda` stage with USD Physics bodies, colliders, mass, joints, velocity drives, a `PhysicsScene`, and package-local validation script.
147
+
148
+ For Isaac workflows, prefer `export usd` when you want to avoid importer heuristics and preserve authored physics metadata directly in USD. Use URDF only when a downstream tool specifically expects URDF or when you need an importer-managed conversion step.
149
+
150
+ Every package includes `simready-manifest.json`. That manifest is the neutral handoff: model name, units, profile, bodies, joints, drives, controllers, contacts, and validation results.
151
+
152
+ ## What The Check Catches
153
+
154
+ `forgecad check simready` is source-level validation. It does not need Isaac Sim, OpenUSD, `pxr`, MuJoCo, or NVIDIA validators installed.
155
+
156
+ It fails when:
157
+
158
+ - the returned model has no `withSimulation(...)` contract
159
+ - a physical part is missing `Sim.body(...)`
160
+ - mass, density, friction, restitution, torque, speed, damping, or world pose values are non-finite or invalid
161
+ - a collider is missing for profiles that require explicit collision intent
162
+ - `Sim.collider.none(...)` has no reason
163
+ - a contact surface names a missing connector
164
+ - a controller names an unknown or wrong-type joint
165
+ - a robot profile has no `rootPart`, an unknown `rootPart`, or a disconnected joint graph
166
+
167
+ It intentionally treats joint-connected bodies as connected. A robot with separated wheels, arms, or gripper fingers should pass when the articulated graph is valid.
168
+
169
+ ## Where Control Code Lives
170
+
171
+ ForgeCAD owns geometry, physical metadata, joints, drives, controllers, manifests, and starter simulator packages. It should not own a single blessed balance controller, reward function, or RL library.
172
+
173
+ For a runnable training loop, export MJCF and use the generated package scripts. The Scout Cam Rover example includes `gym_env.py`, `train_balance.py`, `train_sb3.py`, `play_balance.py`, and `live_policy_viewer.py`; see [Simulation Workflow](../simulation-workflow.md) for the full MuJoCo and Isaac Lab path.
@@ -0,0 +1,273 @@
1
+ # Simulation Workflow
2
+
3
+ ForgeCAD should make a robot or simulated asset runnable without making the CAD API own the control algorithm.
4
+
5
+ The source `.forge.js` model owns geometry, assembly structure, physical metadata, joints, drive intent, contact hints, and the neutral simulation contract. Exported packages then generate an editable simulator lab with all names and wiring connected. Rewards, policies, perturbation tests, curriculum, and RL libraries stay in simulator-side code.
6
+
7
+ ## Current Path: ForgeCAD To MuJoCo
8
+
9
+ 1. Source-author the model as an assembly with `withSimulation(...)`.
10
+ 2. Put physical metadata on every part with `Sim.body(...)`.
11
+ 3. Put motor intent on joints with `Sim.drive.velocity(...)` or passive dynamics with `Sim.drive.passive(...)`.
12
+ 4. Add high-level controller metadata such as `Sim.controller.diffDrive(...)` when the assembly has a standard command interface.
13
+ 5. Run the offline gate:
14
+
15
+ ```bash
16
+ node dist-cli/forgecad.js check simready examples/robotics/scout-cam-rover-simready/main.forge.js
17
+ ```
18
+
19
+ 6. Export the MuJoCo package:
20
+
21
+ ```bash
22
+ node dist-cli/forgecad.js export mjcf examples/robotics/scout-cam-rover-simready/main.forge.js --output /tmp/scout-cam-rover-mjcf
23
+ ```
24
+
25
+ The generated package contains:
26
+
27
+ - `scene.xml`: a runnable MuJoCo scene with floor, light, and render settings.
28
+ - `manifest.json`: ForgeCAD-to-MJCF names for links, joints, actuators, generated scripts, and suggested commands.
29
+ - `simready-manifest.json`: neutral simulation contract and validation results.
30
+ - `scripts/gym_env.py`: Gymnasium-compatible MuJoCo env helpers.
31
+ - `scripts/policy_runtime.py`: shared policy loader for ForgeCAD JSON and Stable-Baselines3 zip policies.
32
+ - `scripts/smoke_control.py`: basic actuator smoke test and GIF renderer.
33
+ - `scripts/train_balance.py`: small editable balance trainer.
34
+ - `scripts/play_balance.py`: policy playback with optional external force and torque pulses.
35
+ - `scripts/train_sb3.py`: Stable-Baselines3 PPO/SAC training starter.
36
+ - `scripts/live_policy_viewer.py`: live MuJoCo passive-viewer policy runner.
37
+
38
+ ## What The Generated Gym Env Does
39
+
40
+ `scripts/gym_env.py` is the bridge between the exported CAD package and control code.
41
+
42
+ It loads the package manifests, compiles `scene.xml`, discovers actuator IDs, identifies the simulation root body, and calibrates diff-drive signs from MuJoCo joint axes. This matters because mirrored wheel axes can make a naive same-sign left/right command spin or drift instead of moving forward.
43
+
44
+ The balance environment exposes a compact observation:
45
+
46
+ - tilt around the wheel axle
47
+ - side tilt
48
+ - angular velocity
49
+ - uprightness
50
+ - root height
51
+ - longitudinal velocity
52
+ - wheel velocities
53
+ - previous forward command
54
+
55
+ The semantic action is:
56
+
57
+ - `semanticForward`
58
+ - `semanticTurn`
59
+
60
+ The env converts those semantic actions into calibrated actuator commands.
61
+
62
+ ## Training The Starter Balance Policy
63
+
64
+ The generated trainer intentionally avoids heavy RL machinery.
65
+
66
+ ```bash
67
+ cd /tmp/scout-cam-rover-mjcf
68
+ uv run --python 3.11 --with mujoco --with numpy --with gymnasium --with pillow python scripts/train_balance.py --render-gif balance.gif --fps 10
69
+ ```
70
+
71
+ It trains a linear policy:
72
+
73
+ ```text
74
+ observation -> weighted sum -> tanh -> motor command
75
+ ```
76
+
77
+ The optimizer is cross-entropy method:
78
+
79
+ 1. Try many random weight sets.
80
+ 2. Simulate each candidate.
81
+ 3. Score survival, uprightness, angular rate, and command cost.
82
+ 4. Keep the best candidates.
83
+ 5. Sample the next generation around those.
84
+
85
+ This is not deep RL. It is a fast, inspectable starter controller. The right next steps for serious RL are PPO/SAC/PufferLib/MJX/Isaac Lab integrations that reuse the generated env or manifests.
86
+
87
+ ## Serious RL Workflow
88
+
89
+ For a real reinforcement-learning run, treat the generated MJCF package as the simulator adapter and replace `train_balance.py` with the training stack you actually want. ForgeCAD's job is to produce a valid robot model, named actuators, controller metadata, and a Gymnasium-style environment scaffold; the RL project owns the policy class, reward, curriculum, vectorization, checkpointing, and evaluation harness.
90
+
91
+ The usual loop is:
92
+
93
+ 1. Load `scene.xml` with MuJoCo.
94
+ 2. Load `manifest.json` and `simready-manifest.json` to map ForgeCAD names to MuJoCo bodies, joints, and actuators.
95
+ 3. Build observations from `mjData`, such as pose, tilt, angular velocity, wheel velocity, sensors, previous command, and task-specific state.
96
+ 4. Let the policy choose an action.
97
+ 5. Convert semantic actions, such as `[forward, turn]`, into actuator controls.
98
+ 6. Write controls into `mjData.ctrl`, then call `mj_step`.
99
+ 7. Compute reward, termination, truncation, and diagnostics.
100
+ 8. Let PPO, SAC, PufferLib, MJX, Isaac Lab, or another trainer update the policy.
101
+ 9. Save checkpoints and evaluate them with deterministic playback, randomized starts, friction/mass perturbations, and force-push tests.
102
+
103
+ For the Scout Cam Rover balance example, the first serious action space should usually stay semantic:
104
+
105
+ ```text
106
+ action = [semanticForward, semanticTurn]
107
+ ```
108
+
109
+ That keeps the RL task independent from left/right motor sign conventions. The generated environment already calibrates mirrored wheel axes and maps semantic commands to actuator controls. If a later task needs raw motor commands, add that deliberately after the semantic baseline works.
110
+
111
+ The reward should encode the actual job, not just survival. For a self-balancing rover, a practical first reward includes uprightness, low tilt rate, target velocity or position tracking, bounded control effort, and clear fall termination. Randomize initial tilt, mass, friction, and push disturbances once the baseline can solve the nominal case.
112
+
113
+ ## Training A Stable-Baselines3 Policy
114
+
115
+ The MJCF package includes an editable SB3 starter that reuses `BalanceEnv` and the generated semantic diff-drive action mapping:
116
+
117
+ ```bash
118
+ cd /tmp/scout-cam-rover-mjcf
119
+ uv run --python 3.11 --with mujoco --with numpy --with gymnasium --with stable-baselines3 --with pillow python scripts/train_sb3.py \
120
+ --timesteps 50000 \
121
+ --output policies/sb3_balance_ppo.zip \
122
+ --render-gif sb3_balance.gif \
123
+ --fps 10
124
+ ```
125
+
126
+ That command writes `policies/sb3_balance_ppo.zip` plus `policies/sb3_balance_ppo.json`. The JSON sidecar records the algorithm, observation/action shape, drive calibration, and short deterministic evaluation summary. Use `--algo sac` when you want the SAC template instead of PPO.
127
+
128
+ ## Pushing The Trained Policy
129
+
130
+ After training, run the policy with a force pulse:
131
+
132
+ ```bash
133
+ cd /tmp/scout-cam-rover-mjcf
134
+ uv run --python 3.11 --with mujoco --with numpy --with gymnasium --with pillow python scripts/play_balance.py \
135
+ --policy policies/balance_linear.json \
136
+ --push-y 1 \
137
+ --push-at 2 \
138
+ --push-duration 0.15 \
139
+ --render-gif balance_push.gif \
140
+ --fps 10
141
+ ```
142
+
143
+ `play_balance.py` applies force or torque through MuJoCo's `xfrc_applied` array on the root body. It reports the frame count, approximate GIF duration, push vector, final tilt, and survival time.
144
+
145
+ Use these options to push from other directions:
146
+
147
+ ```bash
148
+ --push-x 2
149
+ --push-y -4
150
+ --push-z 1
151
+ --torque-x 0.1
152
+ --torque-y -0.1
153
+ --torque-z 0.1
154
+ ```
155
+
156
+ Increase `--push-y` or `--push-x` when you want a failure/stress test instead of a recovery demo.
157
+
158
+ Stable-Baselines3 zip policies use the same playback path:
159
+
160
+ ```bash
161
+ cd /tmp/scout-cam-rover-mjcf
162
+ uv run --python 3.11 --with mujoco --with numpy --with gymnasium --with stable-baselines3 --with pillow python scripts/play_balance.py \
163
+ --policy policies/sb3_balance_ppo.zip \
164
+ --push-y 1 \
165
+ --push-at 2 \
166
+ --push-duration 0.15 \
167
+ --render-gif sb3_balance_push.gif \
168
+ --fps 10
169
+ ```
170
+
171
+ ## MuJoCo App Vs Policy Control
172
+
173
+ MuJoCo's standalone viewer and MuJoCo.app load models, not trained policies. Open `scene.xml` when you want to inspect the exported robot, geometry, joints, contacts, actuators, and passive dynamics:
174
+
175
+ ```bash
176
+ uv run --python 3.11 --with mujoco python -m mujoco.viewer --mjcf=scene.xml
177
+ ```
178
+
179
+ A trained policy is control code. The official MuJoCo control path is to set controls before stepping the simulator, or to install a control callback. In Python, the common interactive pattern is the passive viewer: the user script owns the loop, computes policy actions, writes `mjData.ctrl`, calls `mj_step`, and then calls `viewer.sync()`. On macOS, passive-viewer scripts should run with `mjpython`, which is installed with the `mujoco` Python package.
180
+
181
+ Conceptually:
182
+
183
+ ```python
184
+ model = mujoco.MjModel.from_xml_path("scene.xml")
185
+ data = mujoco.MjData(model)
186
+
187
+ with mujoco.viewer.launch_passive(model, data) as viewer:
188
+ while viewer.is_running():
189
+ action = policy(observation_from(data))
190
+ data.ctrl[:] = action_to_actuator_controls(action)
191
+ mujoco.mj_step(model, data)
192
+ viewer.sync()
193
+ ```
194
+
195
+ `scripts/play_balance.py` is the generated scripted playback path. It loads the policy JSON, applies calibrated actuator commands, optionally applies force or torque pulses through `xfrc_applied`, and can render a diagnostic GIF. It is not the same thing as loading the policy into MuJoCo.app.
196
+
197
+ To run a policy in an interactive window, use the generated passive-viewer script. On macOS, run it through `mjpython`:
198
+
199
+ ```bash
200
+ cd /tmp/scout-cam-rover-mjcf
201
+ uv run --python 3.11 --with mujoco --with numpy --with gymnasium --with stable-baselines3 mjpython scripts/live_policy_viewer.py \
202
+ --policy policies/sb3_balance_ppo.zip
203
+ ```
204
+
205
+ ## GIF Timing
206
+
207
+ The generated scripts treat GIF duration as:
208
+
209
+ ```text
210
+ duration ~= frame_count / fps
211
+ ```
212
+
213
+ For a 6-second run at 10 FPS, expect about 60 frames. If the GIF is much shorter, the renderer is undersampling frames or the policy terminated early. The summary JSON prints both `frames` and `gifDurationSecondsApprox` for playback scripts.
214
+
215
+ Keep FPS modest. A 10 FPS diagnostic GIF is usually easier to review, smaller than a 30 FPS one, and maps to a clean 100 ms GIF frame delay in common viewers.
216
+
217
+ ## What Belongs In ForgeCAD
218
+
219
+ ForgeCAD should own:
220
+
221
+ - physical metadata on parts
222
+ - colliders and contact intent
223
+ - joints and drive limits
224
+ - named controller metadata
225
+ - simulator package manifests
226
+ - export to URDF, SDF, and MJCF; native USD/USDZ export is future work
227
+ - generated starter envs, smoke tests, trainers, playback, and perturbation scripts
228
+ - offline source-level readiness checks
229
+
230
+ ForgeCAD should not own:
231
+
232
+ - a `selfBalancingDiffDrive(...)` API
233
+ - a single blessed RL algorithm
234
+ - task-specific rewards
235
+ - PufferLib-specific or IsaacLab-specific core CAD primitives
236
+ - deployment-specific training infrastructure
237
+
238
+ The durable product promise is: model once, export a working simulation lab, then swap the control stack freely.
239
+
240
+ ## Isaac Lab Fit
241
+
242
+ Isaac Lab integration is plausible, but not turnkey in this slice.
243
+
244
+ The current NVIDIA Isaac Lab docs describe custom robot training as a multi-step flow: import and tune the robot in Isaac Sim, then define the Isaac Lab interface needed to clone the robot, drive joints, and reset state. Their "Adding a New Robot" tutorial names `AssetBaseCfg` as the interface between the USD articulation and learning algorithms. Their asset configuration guide shows `ArticulationCfg` with `UsdFileCfg`, and notes that `UrdfFileCfg` can replace `UsdFileCfg` for URDF-backed articulations.
245
+
246
+ Relevant docs:
247
+
248
+ - https://isaac-sim.github.io/IsaacLab/main/source/tutorials/01_assets/add_new_robot.html
249
+ - https://isaac-sim.github.io/IsaacLab/main/source/how-to/write_articulation_cfg.html
250
+
251
+ ForgeCAD has the information Isaac Lab needs in pieces:
252
+
253
+ - URDF export for an articulation import path
254
+ - SimReady manifest with root part, joints, drives, materials, contacts, and controller metadata
255
+ - SDF/MJCF packages for fast local physics iteration
256
+
257
+ ForgeCAD does not currently have a native USD/USDZ export command. For Isaac workflows today, use URDF as the import artifact and carry the richer ForgeCAD simulation intent through `simready-manifest.json`.
258
+
259
+ The missing ForgeCAD work for a clean Isaac Lab path is:
260
+
261
+ - direct USD authoring or a generated Isaac Sim URDF import/conversion script
262
+ - generated `ArticulationCfg` scaffold from `simready-manifest.json`
263
+ - actuator config generation from `Sim.drive.*(...)`
264
+ - reset/observation/action scaffolds equivalent to the MuJoCo Gym env
265
+ - coordinate/frame audit for Isaac's stage conventions and imported URDF axes
266
+ - Isaac Sim/Isaac Lab validation on a machine with those tools installed
267
+
268
+ Until that exists, the recommended path is:
269
+
270
+ 1. Use MuJoCo export for fast local control-loop iteration.
271
+ 2. Export URDF for Isaac Sim and keep `simready-manifest.json` beside it.
272
+ 3. Generate or hand-write Isaac Lab `ArticulationCfg` and task code from the ForgeCAD manifests.
273
+ 4. Validate imported articulation axes, actuators, masses, colliders, and reset behavior in Isaac Lab.
package/dist/index.html CHANGED
@@ -83,8 +83,8 @@
83
83
  * { margin: 0; padding: 0; box-sizing: border-box; }
84
84
  html, body, #root { width: 100%; min-height: 100%; background: var(--fc-bg); color: var(--fc-text); font-family: system-ui, -apple-system, sans-serif; }
85
85
  </style>
86
- <script type="module" crossorigin src="/assets/app-DSYrDg0V.js"></script>
87
- <link rel="stylesheet" crossorigin href="/assets/app-CE3sYcV7.css">
86
+ <script type="module" crossorigin src="/assets/app-C9ct2hRD.js"></script>
87
+ <link rel="stylesheet" crossorigin href="/assets/app-CjsbDlb7.css">
88
88
  </head>
89
89
  <body>
90
90
  <div id="root"></div>
package/dist/sitemap.xml CHANGED
@@ -2,79 +2,91 @@
2
2
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3
3
  <url>
4
4
  <loc>https://forgecad.io/</loc>
5
- <lastmod>2026-06-10</lastmod>
5
+ <lastmod>2026-06-12</lastmod>
6
6
  <changefreq>weekly</changefreq>
7
7
  <priority>1.0</priority>
8
8
  </url>
9
9
  <url>
10
10
  <loc>https://forgecad.io/docs</loc>
11
- <lastmod>2026-06-10</lastmod>
11
+ <lastmod>2026-06-12</lastmod>
12
12
  <changefreq>weekly</changefreq>
13
13
  <priority>0.8</priority>
14
14
  </url>
15
15
  <url>
16
16
  <loc>https://forgecad.io/docs/ai-native-cad</loc>
17
- <lastmod>2026-06-10</lastmod>
17
+ <lastmod>2026-06-12</lastmod>
18
18
  <changefreq>weekly</changefreq>
19
19
  <priority>0.9</priority>
20
20
  </url>
21
21
  <url>
22
22
  <loc>https://forgecad.io/docs/ai-usage</loc>
23
- <lastmod>2026-06-10</lastmod>
23
+ <lastmod>2026-06-12</lastmod>
24
24
  <changefreq>weekly</changefreq>
25
25
  <priority>0.8</priority>
26
26
  </url>
27
27
  <url>
28
28
  <loc>https://forgecad.io/docs/cli</loc>
29
- <lastmod>2026-06-10</lastmod>
29
+ <lastmod>2026-06-12</lastmod>
30
30
  <changefreq>weekly</changefreq>
31
31
  <priority>0.7</priority>
32
32
  </url>
33
+ <url>
34
+ <loc>https://forgecad.io/docs/simready-quickstart</loc>
35
+ <lastmod>2026-06-12</lastmod>
36
+ <changefreq>weekly</changefreq>
37
+ <priority>0.8</priority>
38
+ </url>
39
+ <url>
40
+ <loc>https://forgecad.io/docs/simulation-workflow</loc>
41
+ <lastmod>2026-06-12</lastmod>
42
+ <changefreq>weekly</changefreq>
43
+ <priority>0.8</priority>
44
+ </url>
33
45
  <url>
34
46
  <loc>https://forgecad.io/docs/skills/forgecad-make-a-model</loc>
35
- <lastmod>2026-06-10</lastmod>
47
+ <lastmod>2026-06-12</lastmod>
36
48
  <changefreq>weekly</changefreq>
37
49
  <priority>0.7</priority>
38
50
  </url>
39
51
  <url>
40
52
  <loc>https://forgecad.io/benchmark</loc>
41
- <lastmod>2026-06-10</lastmod>
53
+ <lastmod>2026-06-12</lastmod>
42
54
  <changefreq>weekly</changefreq>
43
55
  <priority>0.7</priority>
44
56
  </url>
45
57
  <url>
46
58
  <loc>https://forgecad.io/blog</loc>
47
- <lastmod>2026-06-10</lastmod>
59
+ <lastmod>2026-06-12</lastmod>
48
60
  <changefreq>weekly</changefreq>
49
61
  <priority>0.6</priority>
50
62
  </url>
51
63
  <url>
52
64
  <loc>https://forgecad.io/pricing</loc>
53
- <lastmod>2026-06-10</lastmod>
65
+ <lastmod>2026-06-12</lastmod>
54
66
  <changefreq>monthly</changefreq>
55
67
  <priority>0.5</priority>
56
68
  </url>
57
69
  <url>
58
70
  <loc>https://forgecad.io/terms</loc>
59
- <lastmod>2026-06-10</lastmod>
71
+ <lastmod>2026-06-12</lastmod>
60
72
  <changefreq>yearly</changefreq>
61
73
  <priority>0.3</priority>
62
74
  </url>
63
75
  <url>
64
76
  <loc>https://forgecad.io/privacy</loc>
65
- <lastmod>2026-06-10</lastmod>
77
+ <lastmod>2026-06-12</lastmod>
66
78
  <changefreq>yearly</changefreq>
67
79
  <priority>0.3</priority>
68
80
  </url>
69
81
  <url>
70
82
  <loc>https://forgecad.io/license</loc>
71
- <lastmod>2026-06-10</lastmod>
83
+ <lastmod>2026-06-12</lastmod>
72
84
  <changefreq>yearly</changefreq>
73
85
  <priority>0.3</priority>
74
86
  </url>
75
87
  <url>
76
88
  <loc>https://forgecad.io/blog/hello-forgecad-io</loc>
77
- <lastmod>2026-06-10</lastmod>
89
+ <lastmod>2026-06-12</lastmod>
78
90
  <changefreq>monthly</changefreq>
79
91
  <priority>0.5</priority>
80
92
  </url>
@@ -9,7 +9,7 @@ import {
9
9
  resolvePackagePath,
10
10
  runDirectCliMain,
11
11
  setActiveBackend
12
- } from "./chunk-OAN5T4XD.js";
12
+ } from "./chunk-RQQ42YCP.js";
13
13
 
14
14
  // cli/check-compiler.ts
15
15
  import assert from "assert/strict";
@@ -12,7 +12,7 @@ import {
12
12
  resolvePackagePath,
13
13
  runDirectCliMain,
14
14
  setActiveBackend
15
- } from "./chunk-OAN5T4XD.js";
15
+ } from "./chunk-RQQ42YCP.js";
16
16
 
17
17
  // cli/check-query-propagation.ts
18
18
  import assert from "assert/strict";