forgecad 0.9.16 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/dist/assets/{AdminPage-CXvls4-J.js → AdminPage-DcCnj0qo.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-B27zk8xL.js → BenchmarkPage-BVEpJSVk.js} +1 -1
  3. package/dist/assets/{BlogPage-CMAVvgQL.js → BlogPage-DHaGP50_.js} +1 -1
  4. package/dist/assets/{DocsPage-knf4I4h7.js → DocsPage-CDoxHkz8.js} +40 -859
  5. package/dist/assets/EditorApp-BJ0Dloyh.js +16446 -0
  6. package/dist/assets/{EmbedViewer-D7ZGlFjx.js → EmbedViewer-CRKZbY0y.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-CnevhTE8.js → LandingPageProofDriven-BxHkYRE7.js} +1 -1
  8. package/dist/assets/{LegalPage-BPTUmqeg.js → LegalPage-B-u6FrVv.js} +1 -1
  9. package/dist/assets/{PricingPage-B0D4goG_.js → PricingPage-CzpZ6-Ce.js} +1 -1
  10. package/dist/assets/{SettingsPage-CFF-UgjI.js → SettingsPage-CIZSSAd0.js} +1 -1
  11. package/dist/assets/{app-CE3sYcV7.css → app-CjsbDlb7.css} +143 -0
  12. package/dist/assets/{app-T0pDcSX4.js → app-DaTMg3nH.js} +1310 -290
  13. package/dist/assets/cli/{render-C5pcIISc.js → render-DPf4AYJK.js} +55 -60
  14. package/dist/assets/{constructionHistoryWorker-Ba2Hm58b.js → constructionHistoryWorker-AwMMWSxg.js} +1103 -349
  15. package/dist/assets/{evalWorker-vkx310U2.js → evalWorker-CjZZWRWW.js} +5209 -2643
  16. package/dist/assets/{inspectWorker-BuTJDVX6.js → inspectWorker-CZsCFtQT.js} +1163 -409
  17. package/dist/assets/{jointPose-B_Cgedn9.js → jointPose-DzQOViQH.js} +1 -1
  18. package/dist/assets/{manifold-BWgsjmAM.js → manifold-BYlzU521.js} +1 -1
  19. package/dist/assets/{manifold-D6IFSkhH.js → manifold-DgXo0T5P.js} +2 -2
  20. package/dist/assets/{manifold-rZexZI0G.js → manifold-K1SkarlQ.js} +1 -1
  21. package/dist/assets/{reportWorker-0AGij1Ru.js → reportWorker-B9nWwSrB.js} +8501 -3393
  22. package/dist/assets/{scalar-sampling-budget-J5cuzxT1.js → scalar-sampling-budget-prBw_s8t.js} +6067 -3479
  23. package/dist/assets/{scanProxyWorker-Vl4Wxa1y.js → scanProxyWorker-2GtDLk-R.js} +1 -1
  24. package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
  25. package/dist/cli/render.html +1 -1
  26. package/dist/docs/index.html +3 -3
  27. package/dist/docs-raw/AI/usage.md +1 -1
  28. package/dist/docs-raw/CLI.md +77 -240
  29. package/dist/docs-raw/README.md +6 -0
  30. package/dist/docs-raw/component-model.md +17 -150
  31. package/dist/docs-raw/generated/assembly.md +188 -582
  32. package/dist/docs-raw/generated/concepts.md +259 -3501
  33. package/dist/docs-raw/generated/core.md +283 -1250
  34. package/dist/docs-raw/generated/curves.md +387 -1608
  35. package/dist/docs-raw/generated/legacy.md +162 -0
  36. package/dist/docs-raw/generated/lib.md +227 -85
  37. package/dist/docs-raw/generated/output.md +35 -99
  38. package/dist/docs-raw/generated/runtime-names.md +23 -23
  39. package/dist/docs-raw/generated/sdf.md +68 -284
  40. package/dist/docs-raw/generated/sheet-metal.md +68 -335
  41. package/dist/docs-raw/generated/sketch.md +240 -1161
  42. package/dist/docs-raw/generated/viewport.md +75 -316
  43. package/dist/docs-raw/generated/wood.md +21 -49
  44. package/dist/docs-raw/guides/coordinate-system.md +4 -42
  45. package/dist/docs-raw/guides/inspection-bundles.md +44 -442
  46. package/dist/docs-raw/guides/joint-design.md +18 -79
  47. package/dist/docs-raw/guides/positioning.md +21 -143
  48. package/dist/docs-raw/guides/scene-presentation.md +89 -0
  49. package/dist/docs-raw/guides/simready-quickstart.md +171 -0
  50. package/dist/docs-raw/simulation-workflow.md +273 -0
  51. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
  52. package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
  53. package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
  54. package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
  55. package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
  56. package/dist/docs-raw/skills/forgecad-lld.md +19 -113
  57. package/dist/docs-raw/skills/forgecad-make-a-model.md +112 -532
  58. package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
  59. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
  60. package/dist/docs-raw/skills/forgecad-project.md +13 -131
  61. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
  62. package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
  63. package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
  64. package/dist/docs-raw/skills/forgecad.md +19 -18
  65. package/dist/docs-raw/skills/index.md +2 -0
  66. package/dist/docs-raw/welcome.md +2 -2
  67. package/dist/index.html +2 -2
  68. package/dist/llms.txt +1 -2
  69. package/dist/sitemap.xml +25 -13
  70. package/dist-cli/{check-compiler-SYQ2PWOB.js → check-compiler-II7NLPAB.js} +1 -1
  71. package/dist-cli/{check-query-propagation-HIAGV62W.js → check-query-propagation-7462TR3R.js} +1 -1
  72. package/dist-cli/{chunk-SPZE3DUY.js → chunk-UWTJCGXF.js} +5848 -2915
  73. package/dist-cli/forgecad.js +3496 -703
  74. package/dist-skill/CONTEXT.md +1797 -7963
  75. package/dist-skill/SKILL.md +15 -15
  76. package/dist-skill/docs/API/core/concepts.md +27 -157
  77. package/dist-skill/docs/CLI.md +77 -240
  78. package/dist-skill/docs/generated/assembly.md +182 -532
  79. package/dist-skill/docs/generated/core.md +283 -1250
  80. package/dist-skill/docs/generated/curves.md +387 -1609
  81. package/dist-skill/docs/generated/lib.md +227 -85
  82. package/dist-skill/docs/generated/output.md +35 -99
  83. package/dist-skill/docs/generated/runtime-names.md +16 -21
  84. package/dist-skill/docs/generated/sdf.md +68 -284
  85. package/dist-skill/docs/generated/sheet-metal.md +68 -335
  86. package/dist-skill/docs/generated/sketch.md +240 -1160
  87. package/dist-skill/docs/generated/viewport.md +75 -223
  88. package/dist-skill/docs/generated/wood.md +21 -49
  89. package/dist-skill/docs/guides/coordinate-system.md +4 -42
  90. package/dist-skill/docs/guides/inspection-bundles.md +44 -442
  91. package/dist-skill/docs/guides/joint-design.md +18 -79
  92. package/dist-skill/docs/guides/positioning.md +21 -143
  93. package/dist-skill/docs/guides/scene-presentation.md +89 -0
  94. package/dist-skill/docs/guides/surface-members.md +26 -0
  95. package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
  96. package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
  97. package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
  98. package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
  99. package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
  100. package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
  101. package/dist-skill/library/forgecad-make-a-model/SKILL.md +110 -532
  102. package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
  103. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
  104. package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
  105. package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
  106. package/dist-skill/library/forgecad-project/SKILL.md +13 -133
  107. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
  108. package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
  109. package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
  110. package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
  111. package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
  112. package/dist-skill/website/skills/forgecad-component-model.md +53 -0
  113. package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
  114. package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
  115. package/dist-skill/website/skills/forgecad-lld.md +41 -0
  116. package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
  117. package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
  118. package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
  119. package/dist-skill/website/skills/forgecad-project.md +26 -0
  120. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
  121. package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
  122. package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
  123. package/dist-skill/website/skills/forgecad.md +122 -0
  124. package/dist-skill/website/skills/index.md +26 -0
  125. package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
  126. package/examples/api/conformal-product-ribbon.forge.js +1 -1
  127. package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
  128. package/examples/api/extrude-options.forge.js +4 -2
  129. package/examples/api/field-loft-drive-tip.forge.js +40 -0
  130. package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
  131. package/examples/api/highlight-debug.forge.js +10 -10
  132. package/examples/api/mesh-import-slats.forge.js +1 -1
  133. package/examples/api/real-product-curves.forge.js +1 -1
  134. package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
  135. package/examples/api/sdf-shapes.forge.js +2 -5
  136. package/examples/api/sketch-rounding-strategies.forge.js +6 -6
  137. package/examples/api/surface-member-bottle-cage.forge.js +3 -3
  138. package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
  139. package/examples/api/surface-member-razor-inlay.forge.js +1 -1
  140. package/examples/api/variable-sweep-test.forge.js +3 -3
  141. package/examples/mechanical/airplane-propeller.forge.js +74 -39
  142. package/examples/nurbs-surface.forge.js +1 -1
  143. package/examples/products/iphone.forge.js +1 -1
  144. package/examples/robotics/README.md +46 -0
  145. package/examples/robotics/scout-cam-rover-simready/README.md +119 -0
  146. package/examples/robotics/scout-cam-rover-simready/lib/dims.js +140 -0
  147. package/examples/robotics/scout-cam-rover-simready/main.forge.js +343 -0
  148. package/examples/robotics/scout-cam-rover-simready/parts/body.forge.js +304 -0
  149. package/examples/robotics/scout-cam-rover-simready/parts/chassis.forge.js +320 -0
  150. package/examples/robotics/scout-cam-rover-simready/parts/hardware.forge.js +21 -0
  151. package/examples/robotics/scout-cam-rover-simready/parts/turret.forge.js +70 -0
  152. package/examples/robotics/scout-cam-rover-simready/parts/wheel.forge.js +116 -0
  153. package/examples/robotics/simready-asset-crate.forge.js +79 -0
  154. package/examples/robotics/simready-diff-drive-rover.forge.js +141 -0
  155. package/examples/robotics/simready-parallel-gripper.forge.js +102 -0
  156. package/package.json +1 -1
  157. package/dist/assets/EditorApp-BHMQlJ-D.js +0 -14686
  158. package/dist/docs-raw/guides/geometry-conventions.md +0 -52
  159. package/dist/docs-raw/guides/modeling-recipes.md +0 -78
  160. package/dist-skill/docs/guides/geometry-conventions.md +0 -52
  161. package/dist-skill/docs/guides/modeling-recipes.md +0 -78
  162. package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
@@ -0,0 +1,70 @@
1
+ // Pan turret of the scout cam rover (printed in black PLA).
2
+ // Rides the SG90 round horn through the neck bore: the central column reaches
3
+ // down through the neck, its socket ceiling rests on the horn top (M2 screw up
4
+ // through the horn into the spline retains it — purchased hardware, see BOM).
5
+ // Turret-local frame: base underside at z=0, +Z up, spline axis = Z.
6
+
7
+ const wallP = param('wall_thickness', 2.4, { min: 2.0, max: 3.0, step: 0.1, unit: 'mm' });
8
+ const wheelODP = param('wheel_od', 110, { min: 96, max: 130, step: 1, unit: 'mm' });
9
+
10
+ const dimsMod = require('../lib/dims.js');
11
+
12
+ const COL_BLACK = '#1f2023';
13
+
14
+ function buildTurret(D) {
15
+ const T = D.turret;
16
+
17
+ // base disc with 4 cosmetic lugs
18
+ let tr = cylinder(T.baseH, T.baseD / 2);
19
+ for (const pt of circularLayout(T.lugN, T.baseD / 2 + T.lugT / 2 - 0.3, { startDeg: 45 })) {
20
+ tr = tr.add(
21
+ box(T.lugW, T.lugT, T.lugH).translate(pt.x, pt.y, 0)
22
+ .rotate([0, 0, 1], (Math.atan2(pt.y, pt.x) * 180) / Math.PI, { pivot: [pt.x, pt.y, 0] })
23
+ );
24
+ }
25
+
26
+ // barrel + cap
27
+ tr = tr.add(cylinder(T.barrelH, T.barrelD / 2).translate(0, 0, T.baseH));
28
+ tr = tr.add(cylinder(T.capH, T.capD / 2).translate(0, 0, T.baseH + T.barrelH));
29
+
30
+ // emblem on top: two engraved rings + a small dome
31
+ const capTop = T.baseH + T.barrelH + T.capH;
32
+ const ringCut = (rOut, rIn) =>
33
+ cylinder(1.0, rOut).subtract(cylinder(1.4, rIn).translate(0, 0, -0.2)).translate(0, 0, capTop - 0.8);
34
+ tr = tr.subtract(ringCut(10, 8), ringCut(5, 3.4));
35
+ tr = tr.add(sphere(2.2).scale([1, 1, 0.55]).translate(0, 0, capTop - 0.4));
36
+
37
+ // central column down through the neck to the servo horn
38
+ const colTop = 0.5; // weld into the base disc
39
+ let col = cylinder(T.colLen + colTop, T.colD / 2).translate(0, 0, -T.colLen);
40
+ col = col.add(cylinder(T.colHubH, T.colHubD / 2).translate(0, 0, -T.colLen));
41
+ // horn socket: open downward, ceiling rests on the horn top face
42
+ col = col.subtract(cylinder(T.socketDepth + 0.1, T.socketD / 2).translate(0, 0, -T.colLen - 0.1));
43
+ // clearance bore above the socket for the M2 retention screw head
44
+ col = col.subtract(cylinder(6, 2.6).translate(0, 0, -T.colLen + T.socketDepth - 0.1));
45
+ tr = tr.add(col);
46
+
47
+ return tr.color(COL_BLACK).material({ metalness: 0.15, roughness: 0.5, clearcoat: 0.3 })
48
+ .withConnectors({
49
+ hub: connector('axle', {
50
+ origin: [0, 0, -T.colLen + T.socketDepth], axis: [0, 0, -1], up: [0, 1, 0], kind: 'revolute',
51
+ }),
52
+ });
53
+ }
54
+
55
+ const D = dimsMod.compute({ wall: wallP, wheelOD: wheelODP });
56
+
57
+ if (require.main === module) {
58
+ scene({
59
+ camera: { position: [90, -100, 80], target: [0, 0, 8], fov: 40 },
60
+ environment: { preset: 'studio', intensity: 0.3, background: false },
61
+ lights: [
62
+ { type: 'ambient', color: '#efe7dc', intensity: 0.25 },
63
+ { type: 'directional', position: [80, -90, 130], color: '#ffe2bf', intensity: 2.4, castShadow: true },
64
+ { type: 'directional', position: [-70, 60, 60], color: '#d4e6fb', intensity: 0.9 },
65
+ ],
66
+ });
67
+ return { preview: [{ name: 'Turret', shape: buildTurret(D) }], make: { buildTurret } };
68
+ }
69
+
70
+ return { make: { buildTurret } };
@@ -0,0 +1,116 @@
1
+ // Wheel for the scout cam rover: printed rim (PLA) + printed tire (TPU).
2
+ // Wheel-local frame: z=0 is the hub bore mouth (faces the robot), +Z points outboard.
3
+ // The hub boss runs inside the motor-pod bore and grips the N20 D-shaft (press fit).
4
+
5
+ const wallP = param('wall_thickness', 2.4, { min: 2.0, max: 3.0, step: 0.1, unit: 'mm' });
6
+ const wheelODP = param('wheel_od', 110, { min: 96, max: 130, step: 1, unit: 'mm' });
7
+
8
+ const dimsMod = require('../lib/dims.js');
9
+
10
+ const COL_TEAL = '#2fb3b8';
11
+ const COL_TIRE = '#cfc8bc';
12
+
13
+ function buildRim(D) {
14
+ const hub = D.hub, web = D.web;
15
+ const rimOR = D.rimOD / 2, rimIR = D.rimID / 2;
16
+
17
+ // hub boss with D-bore for the N20 shaft
18
+ let boss = cylinder(hub.bossL, hub.bossD / 2);
19
+ const flatY = hub.boreFlat - hub.boreD / 2; // flat plane offset from center
20
+ const dProfile = circle2d(hub.boreD / 2)
21
+ .intersect(rect(hub.boreD + 0.4, hub.boreFlat).translate(0, (hub.boreFlat / 2) - hub.boreD / 2));
22
+ const dBore = dProfile.extrude(hub.boreDepth + 0.1).translate(0, 0, -0.1);
23
+ boss = boss.subtract(dBore);
24
+ verify.greaterThan('D-bore flat offset is positive', flatY + hub.boreD / 2, 0);
25
+
26
+ // spoke web: hub disc + crosshair spokes + mid ring, welded 1mm into the rim band
27
+ const webReach = rimIR + 1;
28
+ const webSk = circle2d(11)
29
+ .add(rect(webReach * 2, 8))
30
+ .add(rect(8, webReach * 2))
31
+ .add(circle2d(28).subtract(circle2d(24)));
32
+ let webSolid = webSk.extrude(web.t).translate(0, 0, web.z0);
33
+
34
+ // rim band
35
+ const rimBand = cylinder(D.rimW, rimOR).subtract(cylinder(D.rimW + 2, rimIR).translate(0, 0, -1))
36
+ .translate(0, 0, D.rim.z0);
37
+
38
+ let rimPart = union(boss, webSolid, rimBand);
39
+
40
+ // cosmetic hub-face dimples (visible on the outboard web face)
41
+ for (const pt of circularLayout(hub.faceHoleN, hub.faceHoleR, { startDeg: 90 })) {
42
+ rimPart = rimPart.subtract(
43
+ cylinder(1.6, hub.faceHoleD / 2).translate(pt.x, pt.y, web.z0 + web.t - 1.5)
44
+ );
45
+ }
46
+ return rimPart.color(COL_TEAL).material({ metalness: 0.05, roughness: 0.45 });
47
+ }
48
+
49
+ function buildTire(D) {
50
+ const rimOR = D.rimOD / 2, tireOR = D.tireOD / 2;
51
+ let prof = rect(tireOR - rimOR, D.tireW).translate(rimOR + (tireOR - rimOR) / 2, D.tireW / 2);
52
+ prof = prof.filletCorner([tireOR, 0], 2.5).filletCorner([tireOR, D.tireW], 2.5);
53
+ return prof.revolve()
54
+ .translate(0, 0, D.rim.z0)
55
+ .color(COL_TIRE).material({ metalness: 0.0, roughness: 0.85 });
56
+ }
57
+
58
+ function buildShaft(D) {
59
+ // The N20 D-shaft rides in the wheel's D-bore and turns WITH the wheel, so it
60
+ // is modeled as a child of the wheel part (the motor keeps a short stub).
61
+ const M = D.motor;
62
+ const prof = circle2d(M.shaftD / 2)
63
+ .intersect(rect(M.shaftD + 0.4, M.shaftFlat).translate(0, M.shaftFlat / 2 - M.shaftD / 2));
64
+ return prof.extrude(8.5).translate(0, 0, -0.1)
65
+ .color('#d8dbe0').material({ metalness: 0.9, roughness: 0.25 });
66
+ }
67
+
68
+ // Returns the wheel as three separate single-body parts that share the same
69
+ // local frame: the rim carries the joint bore plus identity seats for the tire
70
+ // (stretch fit on the rim band) and the N20 D-shaft (turns with the wheel).
71
+ function buildWheel(D) {
72
+ const idSeat = () => connector('mount', { origin: [0, 0, 0], axis: [0, 0, 1], up: [0, 1, 0], kind: 'fixed' });
73
+ const idChild = () => connector('mount', { origin: [0, 0, 0], axis: [0, 0, -1], up: [0, 1, 0], kind: 'fixed' });
74
+ const rimPart = buildRim(D).withConnectors({
75
+ bore: connector('axle', { origin: [0, 0, 0], axis: [0, 0, -1], up: [0, 1, 0], kind: 'revolute' }),
76
+ tire_seat: idSeat(),
77
+ shaft_seat: idSeat(),
78
+ });
79
+ const tirePart = buildTire(D).withConnectors({
80
+ seat: idChild(),
81
+ tread: connector('contact', {
82
+ origin: [0, -D.tireOD / 2, D.rim.z0 + D.tireW / 2],
83
+ axis: [0, -1, 0],
84
+ up: [0, 0, 1],
85
+ }),
86
+ });
87
+ const shaftPart = buildShaft(D).withConnectors({ seat: idChild() });
88
+ verify.clearanceBetween('tire seats on rim band', tirePart, rimPart, -0.01, 0.05);
89
+ verify.clearanceBetween('D-shaft rides in the D-bore', shaftPart, rimPart, 0.02, 0.15);
90
+ return { rim: rimPart, tire: tirePart, shaft: shaftPart };
91
+ }
92
+
93
+ const D = dimsMod.compute({ wall: wallP, wheelOD: wheelODP });
94
+
95
+ if (require.main === module) {
96
+ scene({
97
+ camera: { position: [160, -150, 120], target: [0, 0, 17], fov: 40 },
98
+ environment: { preset: 'studio', intensity: 0.25, background: false },
99
+ lights: [
100
+ { type: 'ambient', color: '#efe7dc', intensity: 0.18 },
101
+ { type: 'directional', position: [140, -160, 220], color: '#ffe2bf', intensity: 2.6, castShadow: true },
102
+ { type: 'directional', position: [-130, 110, 110], color: '#d4e6fb', intensity: 0.8 },
103
+ ],
104
+ });
105
+ const wp = buildWheel(D);
106
+ return {
107
+ preview: [
108
+ { name: 'Rim', shape: wp.rim },
109
+ { name: 'Tire', shape: wp.tire },
110
+ { name: 'Drive Shaft', shape: wp.shaft },
111
+ ],
112
+ make: { buildWheel },
113
+ };
114
+ }
115
+
116
+ return { make: { buildWheel } };
@@ -0,0 +1,79 @@
1
+ const pine = Sim.material("pine crate", {
2
+ densityKgM3: 520,
3
+ staticFriction: 0.55,
4
+ dynamicFriction: 0.42,
5
+ restitution: 0.08,
6
+ });
7
+
8
+ const woodMaterial = { roughness: 0.78, metalness: 0.02 };
9
+ const darkEndgrain = { roughness: 0.86, metalness: 0.01 };
10
+
11
+ const rim = group(
12
+ { name: "Front Top Rail", shape: box(132, 8, 8).translate(0, -50, 37).color("#6f4a25").material(darkEndgrain) },
13
+ { name: "Back Top Rail", shape: box(132, 8, 8).translate(0, 50, 37).color("#6f4a25").material(darkEndgrain) },
14
+ { name: "Left Top Rail", shape: box(8, 92, 8).translate(-66, 0, 37).color("#6f4a25").material(darkEndgrain) },
15
+ { name: "Right Top Rail", shape: box(8, 92, 8).translate(66, 0, 37).color("#6f4a25").material(darkEndgrain) },
16
+ );
17
+
18
+ const cornerPosts = group(
19
+ { name: "Front Left Post", shape: box(10, 10, 78).translate(-64, -48, 0).color("#714d27").material(darkEndgrain) },
20
+ { name: "Front Right Post", shape: box(10, 10, 78).translate(64, -48, 0).color("#714d27").material(darkEndgrain) },
21
+ { name: "Back Left Post", shape: box(10, 10, 78).translate(-64, 48, 0).color("#714d27").material(darkEndgrain) },
22
+ { name: "Back Right Post", shape: box(10, 10, 78).translate(64, 48, 0).color("#714d27").material(darkEndgrain) },
23
+ );
24
+
25
+ const sideSlats = group(
26
+ { name: "Front Upper Slat", shape: box(118, 7, 12).translate(0, -52, 18).color("#81582c").material(woodMaterial) },
27
+ { name: "Front Lower Slat", shape: box(118, 7, 12).translate(0, -52, -12).color("#9a6a38").material(woodMaterial) },
28
+ { name: "Back Upper Slat", shape: box(118, 7, 12).translate(0, 52, 18).color("#81582c").material(woodMaterial) },
29
+ { name: "Back Lower Slat", shape: box(118, 7, 12).translate(0, 52, -12).color("#9a6a38").material(woodMaterial) },
30
+ { name: "Left Upper Slat", shape: box(7, 78, 12).translate(-70, 0, 18).color("#81582c").material(woodMaterial) },
31
+ { name: "Left Lower Slat", shape: box(7, 78, 12).translate(-70, 0, -12).color("#9a6a38").material(woodMaterial) },
32
+ { name: "Right Upper Slat", shape: box(7, 78, 12).translate(70, 0, 18).color("#81582c").material(woodMaterial) },
33
+ { name: "Right Lower Slat", shape: box(7, 78, 12).translate(70, 0, -12).color("#9a6a38").material(woodMaterial) },
34
+ );
35
+
36
+ const floorPlanks = group(
37
+ { name: "Floor Left", shape: box(36, 86, 8).translate(-40, 0, -36).color("#a8733d").material(woodMaterial) },
38
+ { name: "Floor Center", shape: box(36, 86, 8).translate(0, 0, -36).color("#b47b42").material(woodMaterial) },
39
+ { name: "Floor Right", shape: box(36, 86, 8).translate(40, 0, -36).color("#a8733d").material(woodMaterial) },
40
+ );
41
+
42
+ const forkliftSkids = group(
43
+ { name: "Left Fork Pocket", shape: box(112, 10, 8).translate(0, -28, -50).color("#6f4a25").material(darkEndgrain) },
44
+ { name: "Right Fork Pocket", shape: box(112, 10, 8).translate(0, 28, -50).color("#6f4a25").material(darkEndgrain) },
45
+ { name: "Left Spacer", shape: box(14, 10, 18).translate(-46, -28, -42).color("#714d27").material(darkEndgrain) },
46
+ { name: "Right Spacer", shape: box(14, 10, 18).translate(46, 28, -42).color("#714d27").material(darkEndgrain) },
47
+ );
48
+
49
+ const handles = group(
50
+ { name: "Front Handhold", shape: box(48, 5, 14).translate(0, -56, 3).color("#3f2a18").material({ roughness: 0.9 }) },
51
+ { name: "Back Handhold", shape: box(48, 5, 14).translate(0, 56, 3).color("#3f2a18").material({ roughness: 0.9 }) },
52
+ );
53
+
54
+ const crate = group(
55
+ { name: "Rim", group: rim },
56
+ { name: "Posts", group: cornerPosts },
57
+ { name: "Side Slats", group: sideSlats },
58
+ { name: "Floor Planks", group: floorPlanks },
59
+ { name: "Forklift Skids", group: forkliftSkids },
60
+ { name: "Handles", group: handles },
61
+ ).withConnectors({
62
+ grip: connector({ origin: [0, -56, 3], axis: [0, -1, 0], up: [0, 0, 1] }),
63
+ floor_contact: connector({ origin: [0, 0, -54], axis: [0, 0, -1], up: [1, 0, 0] }),
64
+ });
65
+
66
+ return assembly("SimReady Asset Crate")
67
+ .addPart("Body", crate, {
68
+ sim: Sim.body({
69
+ massKg: 2.4,
70
+ material: pine,
71
+ collider: Sim.collider.convexHull(),
72
+ contacts: {
73
+ grip: Sim.contact.gripperSurface("grip"),
74
+ },
75
+ }),
76
+ })
77
+ .withSimulation({
78
+ profile: Sim.profile.roboticsAssetPhysx(),
79
+ });
@@ -0,0 +1,141 @@
1
+ const aluminum = Sim.material("6061 aluminum", {
2
+ densityKgM3: 2700,
3
+ staticFriction: 0.45,
4
+ dynamicFriction: 0.35,
5
+ restitution: 0.05,
6
+ });
7
+
8
+ const rubber = Sim.material("rubber tire", {
9
+ densityKgM3: 1100,
10
+ staticFriction: 0.9,
11
+ dynamicFriction: 0.75,
12
+ restitution: 0.12,
13
+ });
14
+
15
+ const darkPolymer = Sim.material("filled nylon", {
16
+ densityKgM3: 1250,
17
+ staticFriction: 0.42,
18
+ dynamicFriction: 0.32,
19
+ restitution: 0.04,
20
+ });
21
+
22
+ function wheel(axisY) {
23
+ const tireCore = cylinder(26, 34, undefined, 64).color("#171a1d").material({ roughness: 0.9 });
24
+ const treadBlocks = circularPattern(box(9, 5, 30).translate(0, 35, 0), 18).color("#24282c").material({ roughness: 0.95 });
25
+ const outerHub = cylinder(30, 14, undefined, 48).color("#a9b5bc").material({ metalness: 0.35, roughness: 0.42 });
26
+ const hubCap = cylinder(34, 7, undefined, 32).color("#3e4a52").material({ metalness: 0.25, roughness: 0.5 });
27
+
28
+ return group(
29
+ { name: "Tire", shape: tireCore },
30
+ { name: "Tread", shape: treadBlocks },
31
+ { name: "Hub", shape: outerHub },
32
+ { name: "Cap", shape: hubCap },
33
+ )
34
+ .rotateX(90)
35
+ .withConnectors({
36
+ bore: connector({ origin: [0, 0, 0], axis: [0, axisY, 0], up: [0, 0, 1], kind: "revolute" }),
37
+ tread: connector({ origin: [0, 0, -36], axis: [0, 0, -1], up: [1, 0, 0] }),
38
+ });
39
+ }
40
+
41
+ const chassis = group(
42
+ { name: "Lower Pan", shape: box(228, 118, 26).translate(0, 0, 22).color("#46545d").material({ metalness: 0.18, roughness: 0.48 }) },
43
+ { name: "Upper Deck", shape: box(164, 88, 16).translate(8, 0, 46).color("#62727b").material({ metalness: 0.22, roughness: 0.44 }) },
44
+ { name: "Front Bumper", shape: box(18, 124, 20).translate(122, 0, 26).color("#262d33").material({ roughness: 0.72 }) },
45
+ { name: "Rear Bumper", shape: box(14, 116, 16).translate(-122, 0, 24).color("#2d353b").material({ roughness: 0.7 }) },
46
+ { name: "Left Rocker Guard", shape: box(194, 9, 16).translate(0, 68, 24).color("#252c31").material({ roughness: 0.72 }) },
47
+ { name: "Right Rocker Guard", shape: box(194, 9, 16).translate(0, -68, 24).color("#252c31").material({ roughness: 0.72 }) },
48
+ { name: "Sensor Mast", shape: cylinder(34, 5, undefined, 24).translate(58, 0, 72).color("#1c242b").material({ roughness: 0.58 }) },
49
+ { name: "Lidar", shape: cylinder(16, 22, undefined, 48).translate(58, 0, 98).color("#c7d0d6").material({ metalness: 0.1, roughness: 0.35 }) },
50
+ { name: "Camera Bar", shape: box(8, 52, 12).translate(129, 0, 44).color("#161b20").material({ roughness: 0.6 }) },
51
+ )
52
+ .withConnectors({
53
+ left_front_axle: connector({ origin: [72, 82, 30], axis: [0, 1, 0], up: [0, 0, 1], kind: "revolute" }),
54
+ left_rear_axle: connector({ origin: [-72, 82, 30], axis: [0, 1, 0], up: [0, 0, 1], kind: "revolute" }),
55
+ right_front_axle: connector({ origin: [72, -82, 30], axis: [0, -1, 0], up: [0, 0, 1], kind: "revolute" }),
56
+ right_rear_axle: connector({ origin: [-72, -82, 30], axis: [0, -1, 0], up: [0, 0, 1], kind: "revolute" }),
57
+ });
58
+
59
+ const leftWheel = wheel(-1);
60
+ const rightWheel = wheel(1);
61
+
62
+ const rover = assembly("SimReady Diff Drive Rover")
63
+ .addPart("Chassis", chassis, {
64
+ sim: Sim.body({
65
+ massKg: 4.2,
66
+ material: aluminum,
67
+ collider: Sim.collider.convexHull(),
68
+ }),
69
+ })
70
+ .addPart("Left Front Wheel", leftWheel, {
71
+ sim: Sim.body({
72
+ massKg: 0.32,
73
+ material: rubber,
74
+ collider: Sim.collider.convexHull(),
75
+ contacts: { tread: Sim.contact.wheelSurface("tread") },
76
+ }),
77
+ })
78
+ .addPart("Left Rear Wheel", leftWheel, {
79
+ sim: Sim.body({
80
+ massKg: 0.32,
81
+ material: rubber,
82
+ collider: Sim.collider.convexHull(),
83
+ contacts: { tread: Sim.contact.wheelSurface("tread") },
84
+ }),
85
+ })
86
+ .addPart("Right Front Wheel", rightWheel, {
87
+ sim: Sim.body({
88
+ massKg: 0.32,
89
+ material: rubber,
90
+ collider: Sim.collider.convexHull(),
91
+ contacts: { tread: Sim.contact.wheelSurface("tread") },
92
+ }),
93
+ })
94
+ .addPart("Right Rear Wheel", rightWheel, {
95
+ sim: Sim.body({
96
+ massKg: 0.32,
97
+ material: rubber,
98
+ collider: Sim.collider.convexHull(),
99
+ contacts: { tread: Sim.contact.wheelSurface("tread") },
100
+ }),
101
+ })
102
+ .connect("Chassis.left_front_axle", "Left Front Wheel.bore", {
103
+ as: "leftFrontWheel",
104
+ type: "revolute",
105
+ drive: Sim.drive.velocity({ maxTorqueNm: 1.8, maxSpeedRpm: 220, damping: 0.02, friction: 0.01 }),
106
+ })
107
+ .connect("Chassis.left_rear_axle", "Left Rear Wheel.bore", {
108
+ as: "leftRearWheel",
109
+ type: "revolute",
110
+ drive: Sim.drive.velocity({ maxTorqueNm: 1.8, maxSpeedRpm: 220, damping: 0.02, friction: 0.01 }),
111
+ })
112
+ .connect("Chassis.right_front_axle", "Right Front Wheel.bore", {
113
+ as: "rightFrontWheel",
114
+ type: "revolute",
115
+ drive: Sim.drive.velocity({ maxTorqueNm: 1.8, maxSpeedRpm: 220, damping: 0.02, friction: 0.01 }),
116
+ })
117
+ .connect("Chassis.right_rear_axle", "Right Rear Wheel.bore", {
118
+ as: "rightRearWheel",
119
+ type: "revolute",
120
+ drive: Sim.drive.velocity({ maxTorqueNm: 1.8, maxSpeedRpm: 220, damping: 0.02, friction: 0.01 }),
121
+ })
122
+ .withSimulation({
123
+ rootPart: "Chassis",
124
+ profile: Sim.profile.robotBodyRunnable(),
125
+ controllers: [
126
+ Sim.controller.diffDrive({
127
+ leftJoints: ["leftFrontWheel", "leftRearWheel"],
128
+ rightJoints: ["rightFrontWheel", "rightRearWheel"],
129
+ wheelRadiusMm: 34,
130
+ wheelSeparationMm: 164,
131
+ }),
132
+ ],
133
+ });
134
+
135
+ verify.that("rover has five physical bodies", () => rover.describe().parts.length === 5);
136
+ verify.that("rover has four driven wheel joints", () => rover.describe().joints.length === 4);
137
+ verify.that("rover keeps wheel contact metadata on every wheel", () => {
138
+ return rover.describe().parts.filter((part) => part.sim?.contacts?.tread).length === 4;
139
+ });
140
+
141
+ return rover;
@@ -0,0 +1,102 @@
1
+ const aluminum = Sim.material("anodized aluminum", {
2
+ densityKgM3: 2700,
3
+ staticFriction: 0.44,
4
+ dynamicFriction: 0.32,
5
+ restitution: 0.04,
6
+ });
7
+
8
+ const urethane = Sim.material("urethane pad", {
9
+ densityKgM3: 1250,
10
+ staticFriction: 0.95,
11
+ dynamicFriction: 0.8,
12
+ restitution: 0.08,
13
+ });
14
+
15
+ const palm = group(
16
+ { name: "Palm Body", shape: box(36, 72, 24).translate(-28, 0, 8).color("#596872").material({ metalness: 0.2, roughness: 0.42 }) },
17
+ { name: "Top Cover", shape: box(44, 64, 5).translate(-28, 0, 23).color("#75858e").material({ metalness: 0.18, roughness: 0.38 }) },
18
+ { name: "Front Slide Rail", shape: cylinder(74, 3.2, undefined, 24).pointAlong([0, 1, 0]).translate(0, 0, 13).color("#c2c9cc").material({ metalness: 0.55, roughness: 0.32 }) },
19
+ { name: "Rear Slide Rail", shape: cylinder(74, 3.2, undefined, 24).pointAlong([0, 1, 0]).translate(-38, 0, 13).color("#c2c9cc").material({ metalness: 0.55, roughness: 0.32 }) },
20
+ { name: "Lead Screw", shape: cylinder(80, 2.2, undefined, 20).pointAlong([0, 1, 0]).translate(-19, 0, 5).color("#2f363b").material({ metalness: 0.4, roughness: 0.38 }) },
21
+ { name: "Motor Cap", shape: cylinder(16, 11, undefined, 36).pointAlong([0, 1, 0]).translate(-28, -46, 8).color("#222a2f").material({ roughness: 0.56 }) },
22
+ )
23
+ .withConnectors({
24
+ left_slide: connector({ origin: [0, 27, 13], axis: [0, 1, 0], up: [0, 0, 1], kind: "prismatic", min: 0, max: 18 }),
25
+ right_slide: connector({ origin: [0, -27, 13], axis: [0, -1, 0], up: [0, 0, 1], kind: "prismatic", min: 0, max: 18 }),
26
+ });
27
+
28
+ function finger(axisY, color) {
29
+ const carriage = box(28, 15, 16).translate(-14, 0, 0).color("#39464e").material({ metalness: 0.12, roughness: 0.48 });
30
+ const upperFinger = box(78, 8, 9).translate(38, 0, 5).color(color).material({ metalness: 0.08, roughness: 0.44 });
31
+ const lowerFinger = box(62, 7, 8).translate(48, 0, -13).color(color).material({ metalness: 0.08, roughness: 0.48 });
32
+ const frontBridge = box(9, 8, 24).translate(76, 0, -4).color(color).material({ metalness: 0.08, roughness: 0.46 });
33
+ const pad = box(30, 3, 16).translate(60, -axisY * 6, -8).color("#20252a").material({ roughness: 0.9 });
34
+ const padRibA = box(26, 1, 2).translate(60, -axisY * 8, -13).color("#121619").material({ roughness: 0.95 });
35
+ const padRibB = box(26, 1, 2).translate(60, -axisY * 8, -8).color("#121619").material({ roughness: 0.95 });
36
+ const padRibC = box(26, 1, 2).translate(60, -axisY * 8, -3).color("#121619").material({ roughness: 0.95 });
37
+
38
+ return group(
39
+ { name: "Carriage", shape: carriage },
40
+ { name: "Upper Finger", shape: upperFinger },
41
+ { name: "Lower Finger", shape: lowerFinger },
42
+ { name: "Front Bridge", shape: frontBridge },
43
+ { name: "Pad", shape: pad },
44
+ { name: "Pad Rib A", shape: padRibA },
45
+ { name: "Pad Rib B", shape: padRibB },
46
+ { name: "Pad Rib C", shape: padRibC },
47
+ ).withConnectors({
48
+ slide: connector({ origin: [0, 0, 0], axis: [0, -axisY, 0], up: [0, 0, 1], kind: "prismatic", min: 0, max: 18 }),
49
+ pad: connector({ origin: [60, -axisY * 8, -8], axis: [0, -axisY, 0], up: [0, 0, 1] }),
50
+ });
51
+ }
52
+
53
+ const gripper = assembly("SimReady Parallel Gripper")
54
+ .addPart("Palm", palm, {
55
+ sim: Sim.body({
56
+ massKg: 0.42,
57
+ material: aluminum,
58
+ collider: Sim.collider.boundingBox(),
59
+ }),
60
+ })
61
+ .addPart("Left Finger", finger(1, "#d66b4f"), {
62
+ sim: Sim.body({
63
+ massKg: 0.11,
64
+ material: urethane,
65
+ collider: Sim.collider.convexHull(),
66
+ contacts: { pad: Sim.contact.gripperSurface("pad") },
67
+ }),
68
+ })
69
+ .addPart("Right Finger", finger(-1, "#d66b4f"), {
70
+ sim: Sim.body({
71
+ massKg: 0.11,
72
+ material: urethane,
73
+ collider: Sim.collider.convexHull(),
74
+ contacts: { pad: Sim.contact.gripperSurface("pad") },
75
+ }),
76
+ })
77
+ .connect("Palm.left_slide", "Left Finger.slide", {
78
+ as: "leftFingerOpen",
79
+ type: "prismatic",
80
+ min: 0,
81
+ max: 18,
82
+ drive: Sim.drive.passive({ damping: 0.04, friction: 0.02 }),
83
+ })
84
+ .connect("Palm.right_slide", "Right Finger.slide", {
85
+ as: "rightFingerOpen",
86
+ type: "prismatic",
87
+ min: 0,
88
+ max: 18,
89
+ drive: Sim.drive.passive({ damping: 0.04, friction: 0.02 }),
90
+ })
91
+ .withSimulation({
92
+ rootPart: "Palm",
93
+ profile: Sim.profile.robotBodyRunnable(),
94
+ });
95
+
96
+ verify.that("gripper exposes grasp surfaces", () => {
97
+ const parts = gripper.describe().parts;
98
+ return parts.filter((part) => part.sim?.contacts?.pad).length === 2;
99
+ });
100
+ verify.that("gripper has a compact three-body simulation contract", () => gripper.describe().parts.length === 3);
101
+
102
+ return gripper;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgecad",
3
- "version": "0.9.16",
3
+ "version": "0.10.1",
4
4
  "description": "Code-first parametric CAD for JavaScript/TypeScript, in the browser and CLI.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://forgecad.io",