forgecad 0.10.0 → 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 (58) hide show
  1. package/dist/assets/{AdminPage-DwYHz72L.js → AdminPage-DcCnj0qo.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-a9_f-1US.js → BenchmarkPage-BVEpJSVk.js} +1 -1
  3. package/dist/assets/{BlogPage-DodHpvmf.js → BlogPage-DHaGP50_.js} +1 -1
  4. package/dist/assets/{DocsPage-B5LePEuj.js → DocsPage-CDoxHkz8.js} +33 -2
  5. package/dist/assets/{EditorApp-QXsAISLR.js → EditorApp-BJ0Dloyh.js} +174 -35
  6. package/dist/assets/{EmbedViewer-DdEHGUMU.js → EmbedViewer-CRKZbY0y.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-yhhOodbf.js → LandingPageProofDriven-BxHkYRE7.js} +1 -1
  8. package/dist/assets/{LegalPage-5RbKRGYK.js → LegalPage-B-u6FrVv.js} +1 -1
  9. package/dist/assets/{PricingPage-E3Rma7aV.js → PricingPage-CzpZ6-Ce.js} +1 -1
  10. package/dist/assets/{SettingsPage-BJZcM97j.js → SettingsPage-CIZSSAd0.js} +1 -1
  11. package/dist/assets/{app-CE3sYcV7.css → app-CjsbDlb7.css} +143 -0
  12. package/dist/assets/{app-DSYrDg0V.js → app-DaTMg3nH.js} +612 -120
  13. package/dist/assets/cli/{render-ZMHR9HkV.js → render-DPf4AYJK.js} +38 -16
  14. package/dist/assets/{evalWorker-DbNs7Dkp.js → evalWorker-CjZZWRWW.js} +1428 -1038
  15. package/dist/assets/{jointPose-DO6mnXn_.js → jointPose-DzQOViQH.js} +1 -1
  16. package/dist/assets/{manifold-BGlQBBH9.js → manifold-BYlzU521.js} +1 -1
  17. package/dist/assets/{manifold-fy2MV7K1.js → manifold-DgXo0T5P.js} +2 -2
  18. package/dist/assets/{manifold-BU-tJwQh.js → manifold-K1SkarlQ.js} +1 -1
  19. package/dist/assets/{reportWorker-DO6hcQbh.js → reportWorker-B9nWwSrB.js} +1402 -1012
  20. package/dist/assets/{scalar-sampling-budget-o90NSNmF.js → scalar-sampling-budget-prBw_s8t.js} +2139 -1749
  21. package/dist/cli/render.html +1 -1
  22. package/dist/docs/index.html +2 -2
  23. package/dist/docs-raw/CLI.md +18 -3
  24. package/dist/docs-raw/generated/assembly.md +70 -5
  25. package/dist/docs-raw/generated/concepts.md +16 -2
  26. package/dist/docs-raw/generated/core.md +9 -2
  27. package/dist/docs-raw/generated/lib.md +1 -1
  28. package/dist/docs-raw/generated/output.md +14 -43
  29. package/dist/docs-raw/generated/runtime-names.md +4 -4
  30. package/dist/docs-raw/guides/simready-quickstart.md +171 -0
  31. package/dist/docs-raw/simulation-workflow.md +273 -0
  32. package/dist/index.html +2 -2
  33. package/dist/sitemap.xml +25 -13
  34. package/dist-cli/{check-compiler-JTVBITCR.js → check-compiler-II7NLPAB.js} +1 -1
  35. package/dist-cli/{check-query-propagation-3FFLSMVN.js → check-query-propagation-7462TR3R.js} +1 -1
  36. package/dist-cli/{chunk-OAN5T4XD.js → chunk-UWTJCGXF.js} +1455 -722
  37. package/dist-cli/forgecad.js +2994 -529
  38. package/dist-skill/CONTEXT.md +94 -55
  39. package/dist-skill/docs/API/core/concepts.md +1 -1
  40. package/dist-skill/docs/CLI.md +18 -3
  41. package/dist-skill/docs/generated/assembly.md +66 -5
  42. package/dist-skill/docs/generated/core.md +9 -2
  43. package/dist-skill/docs/generated/lib.md +1 -1
  44. package/dist-skill/docs/generated/output.md +14 -43
  45. package/dist-skill/docs/generated/runtime-names.md +4 -4
  46. package/examples/robotics/README.md +46 -0
  47. package/examples/robotics/scout-cam-rover-simready/README.md +119 -0
  48. package/examples/robotics/scout-cam-rover-simready/lib/dims.js +140 -0
  49. package/examples/robotics/scout-cam-rover-simready/main.forge.js +343 -0
  50. package/examples/robotics/scout-cam-rover-simready/parts/body.forge.js +304 -0
  51. package/examples/robotics/scout-cam-rover-simready/parts/chassis.forge.js +320 -0
  52. package/examples/robotics/scout-cam-rover-simready/parts/hardware.forge.js +21 -0
  53. package/examples/robotics/scout-cam-rover-simready/parts/turret.forge.js +70 -0
  54. package/examples/robotics/scout-cam-rover-simready/parts/wheel.forge.js +116 -0
  55. package/examples/robotics/simready-asset-crate.forge.js +79 -0
  56. package/examples/robotics/simready-diff-drive-rover.forge.js +141 -0
  57. package/examples/robotics/simready-parallel-gripper.forge.js +102 -0
  58. package/package.json +1 -1
@@ -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.
@@ -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.
@@ -0,0 +1,119 @@
1
+ # Scout Cam Rover SimReady Example
2
+
3
+ This is a copied, source-authored simulation version of the personal Scout Cam Rover model. The `.forge.js` file returns the normal assembly, and the same assembly carries the simulation contract through `withSimulation(...)`.
4
+
5
+ ## What Is Sim-Ready Here
6
+
7
+ - Every assembly part has `Sim.body(...)` metadata with mass, physical material, and collision intent.
8
+ - The TPU tires expose connector-based wheel contact surfaces through `Sim.contact.wheelSurface("tread")`.
9
+ - The two wheel joints have velocity drive intent through `Sim.drive.velocity(...)`.
10
+ - The root assembly uses `withSimulation(...)` with a `Sim.controller.diffDrive(...)` controller.
11
+ - SDF export emits a Gazebo Sim DiffDrive plugin that listens on `/model/scout_cam_rover/cmd_vel`.
12
+
13
+ ## Local Checks
14
+
15
+ Run from the ForgeCAD repo root:
16
+
17
+ ```bash
18
+ node dist-cli/forgecad.js run examples/robotics/scout-cam-rover-simready/main.forge.js
19
+ node dist-cli/forgecad.js check simready examples/robotics/scout-cam-rover-simready/main.forge.js
20
+ node dist-cli/forgecad.js export mjcf examples/robotics/scout-cam-rover-simready/main.forge.js --output /tmp/scout-cam-rover-mjcf
21
+ node dist-cli/forgecad.js export sdf examples/robotics/scout-cam-rover-simready/main.forge.js --output /tmp/scout-cam-rover-sdf
22
+ node dist-cli/forgecad.js export urdf examples/robotics/scout-cam-rover-simready/main.forge.js --output /tmp/scout-cam-rover-urdf
23
+ ```
24
+
25
+ The MJCF package is written to:
26
+
27
+ ```text
28
+ /tmp/scout-cam-rover-mjcf/scout_cam_rover.xml
29
+ /tmp/scout-cam-rover-mjcf/scene.xml
30
+ /tmp/scout-cam-rover-mjcf/scripts/gym_env.py
31
+ /tmp/scout-cam-rover-mjcf/scripts/smoke_control.py
32
+ /tmp/scout-cam-rover-mjcf/scripts/train_balance.py
33
+ /tmp/scout-cam-rover-mjcf/scripts/play_balance.py
34
+ /tmp/scout-cam-rover-mjcf/simready-manifest.json
35
+ ```
36
+
37
+ The SDF package is written to:
38
+
39
+ ```text
40
+ /tmp/scout-cam-rover-sdf/models/scout_cam_rover/model.sdf
41
+ /tmp/scout-cam-rover-sdf/simready-manifest.json
42
+ ```
43
+
44
+ The URDF package is written to:
45
+
46
+ ```text
47
+ /tmp/scout-cam-rover-urdf/scout_cam_rover.urdf
48
+ /tmp/scout-cam-rover-urdf/simready-manifest.json
49
+ ```
50
+
51
+ ## Moving It In A Simulator
52
+
53
+ The fastest local path is MuJoCo. The MJCF export includes a runnable scene and a package-local smoke controller that discovers the exported actuator names from `manifest.json`.
54
+
55
+ ```bash
56
+ cd /tmp/scout-cam-rover-mjcf
57
+ uv run --python 3.11 --with mujoco --with pillow python scripts/smoke_control.py --render-gif drive.gif
58
+ ```
59
+
60
+ That command compiles the generated `scene.xml`, commands the wheel velocity actuators, prints a motion summary, and writes `/tmp/scout-cam-rover-mjcf/drive.gif`.
61
+ The generated smoke controller calibrates the diff-drive motor signs inside MuJoCo before it commands semantic forward/turn/reverse motion, so mirrored wheel axes do not require hand-flipped script edits.
62
+
63
+ The MJCF package also includes a small Gymnasium control lab and starter balance trainer:
64
+
65
+ ```bash
66
+ cd /tmp/scout-cam-rover-mjcf
67
+ uv run --python 3.11 --with mujoco --with numpy --with gymnasium --with pillow python scripts/train_balance.py --render-gif balance.gif --fps 10
68
+ ```
69
+
70
+ That writes `policies/balance_linear.json` and `balance.gif`. The trainer is intentionally editable Python boilerplate: ForgeCAD wires the model, actuators, calibrated diff-drive semantics, and observation plumbing; the reward, policy class, curriculum, and RL library choice stay outside the CAD API.
71
+ The GIF duration is controlled by both the simulated `--seconds` value and the rendered `--fps`; 10 FPS maps to a clean 100 ms GIF frame delay in common viewers.
72
+
73
+ After training, push the policy with an external force pulse:
74
+
75
+ ```bash
76
+ cd /tmp/scout-cam-rover-mjcf
77
+ uv run --python 3.11 --with mujoco --with numpy --with gymnasium --with pillow python scripts/play_balance.py \
78
+ --policy policies/balance_linear.json \
79
+ --push-y 1 \
80
+ --push-at 2 \
81
+ --push-duration 0.15 \
82
+ --render-gif balance_push.gif \
83
+ --fps 10
84
+ ```
85
+
86
+ `play_balance.py` applies the pulse through MuJoCo's root-body external force array and prints the final tilt, survival time, frame count, and approximate GIF duration.
87
+
88
+ If your desktop supports MuJoCo's GLFW viewer, you can also open the generated scene:
89
+
90
+ ```bash
91
+ uv run --python 3.11 --with mujoco python -m mujoco.viewer --mjcf=/tmp/scout-cam-rover-mjcf/scene.xml
92
+ ```
93
+
94
+ Gazebo Sim is the SDF path, because ForgeCAD's SDF exporter already emits a DiffDrive plugin for the `Sim.controller.diffDrive(...)` metadata.
95
+
96
+ ```bash
97
+ export GZ_SIM_RESOURCE_PATH=/tmp/scout-cam-rover-sdf/models:${GZ_SIM_RESOURCE_PATH}
98
+ gz sim empty.sdf
99
+ gz service -s /world/empty/create \
100
+ --reqtype gz.msgs.EntityFactory \
101
+ --reptype gz.msgs.Boolean \
102
+ --timeout 300 \
103
+ --req 'sdf_filename: "/tmp/scout-cam-rover-sdf/models/scout_cam_rover/model.sdf", name: "scout_cam_rover"'
104
+ gz topic -t /model/scout_cam_rover/cmd_vel \
105
+ -m gz.msgs.Twist \
106
+ -p 'linear: {x: 0.15}, angular: {z: 0.0}'
107
+ ```
108
+
109
+ Those commands require Gazebo Sim to be installed on the host. This repo check does not execute Gazebo, Isaac Sim, MuJoCo, or any physics engine.
110
+
111
+ ## Important Physics Caveat
112
+
113
+ This robot is a two-wheel self-balancing rover. The exported DiffDrive controller can command the wheel joints, and the generated MuJoCo package includes a starter balance trainer, but the policy itself is simulator-side code. In a real physics project, the model usually needs one of these next:
114
+
115
+ - A quick demo stabilizer: add a caster, skid, or temporary roll/pitch constraint so basic drive motion is inspectable.
116
+ - A faithful robot controller: add IMU feedback and a balance PID/LQR loop that commands the wheel velocity or torque.
117
+ - A simulator integration layer: connect the exported model to ROS 2 control, Gazebo systems, Isaac controllers, PufferLib, MJX, or another MuJoCo actuator/controller stack.
118
+
119
+ Without that controller or stabilizer, the wheels can move but the body may fall over.
@@ -0,0 +1,140 @@
1
+ // Scout cam rover — shared dimension tree (pure data/math, no geometry).
2
+ // World frame: Z up, ground z=0, axle along X at z=AXLE_Z, front of robot faces -Y.
3
+ // All printed-part interfaces derive from these numbers; change here, not in parts.
4
+
5
+ function compute(p) {
6
+ const wall = p.wall; // printed shell wall thickness
7
+
8
+ // ---- drive line / wheels --------------------------------------------------
9
+ const tireOD = p.wheelOD; // outer diameter incl. TPU tire
10
+ const tireThk = 5; // radial
11
+ const tireW = 14;
12
+ const rimOD = tireOD - 2 * tireThk;
13
+ const rimID = rimOD - 12; // rim band 6 thick radially
14
+ const rimW = 14;
15
+ const axleZ = tireOD / 2;
16
+
17
+ // N20 micro gearmotor (purchased): body 12 wide x 10 tall, 24 long incl gearbox,
18
+ // D-shaft d3 x 9.5 with 2.5 flat.
19
+ const motor = {
20
+ w: 12, h: 10, len: 24, // gearbox front face = mating plane
21
+ shaftD: 3, shaftFlat: 2.5, shaftLen: 9.5,
22
+ noseBossD: 4, noseBossL: 1.2, // small ring around shaft exit
23
+ };
24
+
25
+ // ---- chassis tub ----------------------------------------------------------
26
+ const tub = {
27
+ w: 70, d: 72, h: 52, z0: axleZ - 26, // base z (world) — axle centered in tub height
28
+ cornerR: 6,
29
+ wall: wall,
30
+ floorT: 2.8,
31
+ postSq: 12, postInset: 6, // corner posts receiving the shell plugs
32
+ sideScrewZ: 47, // tub-local height of the 4 horizontal shell screws
33
+ };
34
+ tub.axleZl = axleZ - tub.z0; // axle height in tub-local Z
35
+
36
+ // motor pod (printed, black): hides shaft exit, wheel hub runs inside its bore
37
+ const pod = {
38
+ od: 34, len: 14, boreD: 24,
39
+ screwR: 14.0, screwN: 3, // M2 from inside tub through the wall into pod bosses
40
+ pilotD: 1.7, wallClearD: 1.9, pilotDepth: 6,
41
+ };
42
+
43
+ // wheel hub geometry (wheel-local: z=0 bore mouth face, +Z outward)
44
+ const hub = {
45
+ bossD: 20,
46
+ bossL: 14, // reaches from inside pod bore out to the spoke web
47
+ boreD: 3.2, boreFlat: 2.65, boreDepth: 9,
48
+ faceHoleN: 5, faceHoleD: 2.6, faceHoleR: 6,
49
+ };
50
+ const web = { t: 6, z0: hub.bossL }; // spoke web axial band (wheel-local)
51
+ const rim = { z0: web.z0 - (rimW - web.t) / 2 }; // rim band centered on web
52
+ // wheel-local Z of bore mouth maps to world |x| = tub.w/2 + 1.5 (mouth sits 1.5 outside the wall,
53
+ // inside the pod bore). Pod outer face at tub.w/2 + pod.len.
54
+ const wheelMouthX = tub.w / 2 + 1.5;
55
+
56
+ // battery: 2x18650 holder (purchased) mounted vertically on the body shell rear
57
+ // inner wall — high mass is correct for an inverted-pendulum self-balancer.
58
+ const batt = { w: 70, h: 42, d: 21, mountHoleSpan: 50, zc: 38 }; // zc = shell-local center
59
+
60
+ // service door in the tub front wall (exposes driver / IMU / wiring bay)
61
+ const door = {
62
+ openW: 44, openH: 22, zc: 19, // tub-local opening, center height
63
+ panelW: 54, panelH: 34, panelT: 2.0,
64
+ plugT: 2.2, slop: 0.3, // plug per-side clearance into opening
65
+ recess: 1.0, recessSlop: 0.4,
66
+ screwDX: 23.5, screwDZ: 13.5, // screw offsets from door center
67
+ pilotD: 1.7, screwHeadD: 3.8, bossD: 6, bossLen: 7,
68
+ };
69
+
70
+ // electronics in the tub bay (purchased modules on printed floor posts)
71
+ const boards = {
72
+ standoffH: 4,
73
+ drv: { w: 16, d: 18, t: 1.6, x: -12, y: -22.5 }, // DRV8833 dual H-bridge
74
+ buck: { w: 22, d: 17, t: 1.6, x: 7.6, y: -22.5 }, // MP1584 5V buck
75
+ mpu: { w: 16.5, d: 21.5, t: 1.6, x: 0, y: 3 }, // MPU6050 IMU (mid-bay)
76
+ };
77
+ const sw = { w: 13, h: 9, t: 9, plateW: 15, plateH: 11, plateT: 1.8, zc: 14 }; // rocker, rear wall
78
+
79
+ // ---- body shell (upper cube) ---------------------------------------------
80
+ const body = {
81
+ w: 84, d: 92, h: 88, cornerR: 13,
82
+ yOff: -4, // shell center sits 4mm toward the front of the tub center
83
+ topT: 3.0,
84
+ plugSq: 8, plugL: 9.7, socketSq: 8.3, socketDepth: 10, // 4 corner plugs into tub posts
85
+ plugInsetX: 10, plugInsetY: 10, // plug centers from tub inner corners
86
+ };
87
+ // neck + ring on top of shell (printed as part of shell)
88
+ const neck = { od: 44, h: 14, boreD: 36, ringOD: 50, ringH: 4 };
89
+
90
+ // camera (front face of shell, shell-local: centered XY, base z=0)
91
+ const cam = {
92
+ zc: 52, // lens center height above shell base
93
+ recessD: 44, recessDepth: 1.0,
94
+ holeD: 26,
95
+ screwR: 18.5, screwN: 4, pilotD: 1.7, screwStartDeg: 0,
96
+ bezelOD: 43.4, bezelID: 28, bezelT: 3.4, // proud of face by bezelT - recessDepth
97
+ cowlD: 27.8, cowlT: 3.0, apertureD: 9.5,
98
+ };
99
+
100
+ // ESP32-CAM (purchased): PCB 27 x 40.5 x 1.6, lens barrel d8 x 6.5 on front
101
+ const esp = { pcbW: 27, pcbH: 40.5, pcbT: 1.6, barrelD: 8, barrelL: 6.5, compT: 3.0 };
102
+
103
+ // side vents: skewed recessed panel + 5 vertical through slots, both side faces
104
+ const vent = {
105
+ n: 5, slotW: 4.5, slotL: 36, pitch: 11,
106
+ recessDepth: 0.8, skewDZ: 14, // rear edge of band this much higher than front
107
+ yc: 3, zc: 56, bandW: 58, bandH: 52, // band center (shell-local y/z) and size
108
+ };
109
+
110
+ // SG90 servo (purchased) hung under a bridge bar across the neck-bore opening.
111
+ // Servo-local frame: z=0 at the tab TOP plane (mates upward against the bosses);
112
+ // spline axis at local x=0.
113
+ const servo = {
114
+ bodyW: 23, bodyD: 12.4, bodyDown: 19, bodyUp: 4.5, // body extent below/above tab plane
115
+ bodyXc: -5.6, // body center offset so the spline sits at x=0
116
+ tabSpan: 32.4, tabT: 2.5, tabHoleXa: -13.75, tabHoleXb: 13.75, // relative to body center
117
+ towerH: 4.6, towerD: 11.8, splineD: 4.8, splineH: 3.2,
118
+ hornD: 20.6, hornT: 1.8, hornHubD: 7.4, // round horn pressed on the spline
119
+ tabPlaneZ: 78.2, // shell-local height of the tab plane
120
+ bossSq: 8, // printed bosses hanging from the bridge bar
121
+ };
122
+ const bridge = { w: 10, holeD: 13 }; // bar left across the neck-bore wall opening
123
+
124
+ // turret (printed black) rides the servo horn through the neck bore
125
+ const turret = {
126
+ baseD: 40, baseH: 5, lugN: 4, lugW: 5, lugT: 2, lugH: 2.4,
127
+ barrelD: 34, barrelH: 15, capD: 37, capH: 3,
128
+ colD: 14, colHubD: 25, colHubH: 5, colLen: 12.5, // column reaches the horn through the neck
129
+ socketD: 21.2, socketDepth: 2.0,
130
+ gapAboveNeck: 1.2, // running gap between turret base underside and neck ring top
131
+ };
132
+
133
+ return {
134
+ wall, axleZ, tireOD, tireThk, tireW, rimOD, rimID, rimW,
135
+ motor, tub, pod, hub, web, rim, wheelMouthX,
136
+ batt, door, boards, sw, body, neck, cam, esp, vent, servo, bridge, turret,
137
+ };
138
+ }
139
+
140
+ module.exports = { compute };