forgecad 0.6.3 → 0.8.0

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 (234) hide show
  1. package/README.md +3 -12
  2. package/dist/assets/{AdminPage-CeqCUUgu.js → AdminPage-D4bocK4E.js} +250 -151
  3. package/dist/assets/{BlogPage-P_AJP0v9.js → BlogPage-CJEXL_zJ.js} +94 -70
  4. package/dist/assets/{DocsPage-CKRV2iq2.js → DocsPage-D3A_g8V3.js} +329 -163
  5. package/dist/assets/{EditorApp-CnC2k4cW.css → EditorApp-BWYUSpUN.css} +590 -136
  6. package/dist/assets/EditorApp-Cihhqcsq.js +11692 -0
  7. package/dist/assets/{EmbedViewer-DBlzmQ5i.js → EmbedViewer-kWjKaC_t.js} +2 -4
  8. package/dist/assets/LandingPageProofDriven-Bg2IUc3l.css +856 -0
  9. package/dist/assets/LandingPageProofDriven-DXkKlyhI.js +601 -0
  10. package/dist/assets/PricingPage-BsU5vzEx.js +232 -0
  11. package/dist/assets/{SettingsPage-BqCh9JcC.js → SettingsPage-PqvpAKIs.js} +129 -5
  12. package/dist/assets/{evalWorker-Ql-aKwLA.js → evalWorker-C-hzNUMy.js} +8949 -3161
  13. package/dist/assets/{Viewport-CoB46f5R.js → index-Pz321YAt.js} +38382 -7501
  14. package/dist/assets/{index-2hfs_ub0.css → index-ay13WNfa.css} +726 -53
  15. package/dist/assets/{javascript-DCxGoE5Y.js → javascript-DAl8Gmyo.js} +1 -1
  16. package/dist/assets/{manifold-CqNMHHKO.js → manifold-BcbjWLIo.js} +4 -3
  17. package/dist/assets/{manifold-Cce9wRFz.js → manifold-DBckbFgx.js} +1 -1
  18. package/dist/assets/{manifold-D6BeHIOo.js → manifold-O2AAGXyj.js} +1 -1
  19. package/dist/assets/{reportWorker-sFEFonXf.js → reportWorker-Dxr-5A7w.js} +8760 -3559
  20. package/dist/assets/{vendor-react-Dt7-aaJH.js → vendor-react-CG3i_wp0.js} +65 -8
  21. package/dist/docs/index.html +2 -2
  22. package/dist/docs-raw/CLI.md +341 -718
  23. package/dist/docs-raw/generated/assembly.md +699 -112
  24. package/dist/docs-raw/generated/concepts.md +1834 -1346
  25. package/dist/docs-raw/generated/core.md +1012 -1059
  26. package/dist/docs-raw/generated/curves.md +759 -116
  27. package/dist/docs-raw/generated/lib.md +43 -748
  28. package/dist/docs-raw/generated/output.md +139 -245
  29. package/dist/docs-raw/generated/sdf.md +208 -0
  30. package/dist/docs-raw/generated/sheet-metal.md +473 -21
  31. package/dist/docs-raw/generated/sketch.md +1518 -362
  32. package/dist/docs-raw/generated/viewport.md +368 -299
  33. package/dist/docs-raw/generated/wood.md +104 -0
  34. package/dist/index.html +2 -2
  35. package/dist/landing/proof-ams-adapter.png +0 -0
  36. package/dist/landing/proof-bolt-and-nut.png +0 -0
  37. package/dist/landing/proof-fillet-enclosure.png +0 -0
  38. package/dist/landing/proof-glasses.png +0 -0
  39. package/dist/landing/proof-gyroid.png +0 -0
  40. package/dist/sitemap.xml +6 -6
  41. package/dist-cli/forgecad.js +12321 -5700
  42. package/dist-cli/forgecad.js.map +1 -0
  43. package/dist-cli/solver-46FFSK2U.js +363 -0
  44. package/dist-cli/solver-46FFSK2U.js.map +1 -0
  45. package/dist-skill/CONTEXT.md +4890 -6302
  46. package/dist-skill/SKILL-dev.md +22 -66
  47. package/dist-skill/SKILL.md +20 -59
  48. package/dist-skill/docs/API/core/concepts.md +37 -92
  49. package/dist-skill/docs/CLI.md +341 -718
  50. package/dist-skill/docs/generated/assembly.md +699 -112
  51. package/dist-skill/docs/generated/core.md +1012 -1059
  52. package/dist-skill/docs/generated/curves.md +759 -116
  53. package/dist-skill/docs/generated/lib.md +43 -748
  54. package/dist-skill/docs/generated/output.md +139 -245
  55. package/dist-skill/docs/generated/sdf.md +208 -0
  56. package/dist-skill/docs/generated/sheet-metal.md +473 -21
  57. package/dist-skill/docs/generated/sketch.md +1518 -362
  58. package/dist-skill/docs/generated/viewport.md +368 -299
  59. package/dist-skill/docs/generated/wood.md +104 -0
  60. package/dist-skill/docs/guides/coordinate-system.md +11 -17
  61. package/dist-skill/docs/guides/geometry-conventions.md +13 -70
  62. package/dist-skill/docs/guides/joint-design.md +78 -0
  63. package/dist-skill/docs/guides/modeling-recipes.md +22 -195
  64. package/dist-skill/docs/guides/positioning.md +88 -147
  65. package/dist-skill/docs-dev/API/core/concepts.md +78 -0
  66. package/dist-skill/docs-dev/CLI.md +488 -0
  67. package/dist-skill/{docs → docs-dev}/blueprint-first.md +5 -0
  68. package/dist-skill/{docs → docs-dev}/coding-best-practices.md +6 -8
  69. package/dist-skill/{docs → docs-dev}/coding.md +2 -4
  70. package/dist-skill/docs-dev/component-model.md +164 -0
  71. package/dist-skill/docs-dev/generated/assembly.md +779 -0
  72. package/dist-skill/docs-dev/generated/core.md +1676 -0
  73. package/dist-skill/docs-dev/generated/curves.md +855 -0
  74. package/dist-skill/docs-dev/generated/lib.md +55 -0
  75. package/dist-skill/docs-dev/generated/output.md +234 -0
  76. package/dist-skill/docs-dev/generated/sdf.md +208 -0
  77. package/dist-skill/docs-dev/generated/sheet-metal.md +506 -0
  78. package/dist-skill/docs-dev/generated/sketch.md +1753 -0
  79. package/dist-skill/docs-dev/generated/viewport.md +513 -0
  80. package/dist-skill/docs-dev/generated/wood.md +104 -0
  81. package/dist-skill/docs-dev/guides/coordinate-system.md +46 -0
  82. package/dist-skill/docs-dev/guides/geometry-conventions.md +52 -0
  83. package/dist-skill/docs-dev/guides/joint-design.md +78 -0
  84. package/dist-skill/docs-dev/guides/modeling-recipes.md +77 -0
  85. package/dist-skill/docs-dev/guides/positioning.md +151 -0
  86. package/dist-skill/{docs → docs-dev}/guides/skill-maintenance.md +21 -10
  87. package/dist-skill/{docs → docs-dev}/internals/compiler.md +5 -6
  88. package/dist-skill/{docs → docs-dev}/internals/constraint-solver-quality.md +0 -1
  89. package/dist-skill/{docs → docs-dev}/internals/constraint-solver.md +0 -1
  90. package/dist-skill/{docs → docs-dev}/internals/sketch-2d-pipeline.md +2 -3
  91. package/examples/api/attachTo-basics.forge.js +8 -8
  92. package/examples/api/bill-of-materials.forge.js +9 -9
  93. package/examples/api/bolt-pattern.forge.js +5 -5
  94. package/examples/api/boolean-operations.forge.js +5 -5
  95. package/examples/api/bounding-box-visualizer.forge.js +3 -3
  96. package/examples/api/clone-duplicate.forge.js +2 -2
  97. package/examples/api/colors-union-vs-array.forge.js +6 -6
  98. package/examples/api/connector-assembly.forge.js +8 -6
  99. package/examples/api/connector-basics.forge.js +7 -7
  100. package/examples/api/constrained-sketch-mechanical.forge.js +4 -4
  101. package/examples/api/elbow-test.forge.js +3 -3
  102. package/examples/api/extrude-options.forge.js +8 -14
  103. package/examples/api/feature-created-faces.forge.js +6 -10
  104. package/examples/api/fillet-showcase.forge.js +2 -2
  105. package/examples/api/folded-service-panel-cover.forge.js +2 -2
  106. package/examples/api/gears-tier1.forge.js +5 -5
  107. package/examples/api/group-test.forge.js +3 -3
  108. package/examples/api/group-vs-union.forge.js +1 -1
  109. package/examples/api/highlight-debug.forge.js +4 -0
  110. package/examples/api/js-module-pillars.js +1 -1
  111. package/examples/api/js-module-scene.js +2 -2
  112. package/examples/api/mesh-import-slats.forge.js +4 -4
  113. package/examples/api/patterns.forge.js +3 -3
  114. package/examples/api/pointAlong-orientation.forge.js +3 -3
  115. package/examples/api/profile-2020-b-slot6.forge.js +4 -5
  116. package/examples/api/route-perimeter-flange.forge.js +1 -1
  117. package/examples/api/sdf-rover-demo.forge.js +10 -10
  118. package/examples/api/sketch-on-face-demo.forge.js +2 -2
  119. package/examples/api/sketch-regions.forge.js +4 -4
  120. package/examples/api/sketch-rounding-strategies.forge.js +1 -1
  121. package/examples/api/smooth-curve-connections.forge.js +1 -1
  122. package/examples/api/transition-curves.forge.js +4 -4
  123. package/examples/api/variable-sweep-pure-sdf-test.forge.js +162 -0
  124. package/examples/api/variable-sweep-test.forge.js +2 -2
  125. package/examples/api/wood-joinery.forge.js +60 -0
  126. package/examples/compiler-corpus/enclosure-shell-cuts.forge.js +3 -3
  127. package/examples/compiler-corpus/fastener-plate-variants.forge.js +2 -2
  128. package/examples/constraints/01-fully-constrained-rect.forge.js +2 -2
  129. package/examples/constraints/02-underconstrained-triangle.forge.js +1 -1
  130. package/examples/constraints/03-redundant-constraints.forge.js +2 -2
  131. package/examples/constraints/05-parallel-with-linedistance.forge.js +2 -2
  132. package/examples/constraints/06-complex-spectrogram.forge.js +1 -1
  133. package/examples/constraints/07-perpendicular-chain.forge.js +4 -4
  134. package/examples/constraints/08-symmetric-bracket.forge.js +4 -4
  135. package/examples/constraints/09-stress-spiral.forge.js +1 -1
  136. package/examples/constraints/10-stress-honeycomb.forge.js +1 -1
  137. package/examples/constraints/11-surface-grid.forge.js +2 -2
  138. package/examples/constraints/12-surface-nested.forge.js +4 -4
  139. package/examples/constraints/13-surface-complex.forge.js +1 -1
  140. package/examples/exact-arc-housing.forge.js +12 -0
  141. package/examples/experiments/drone-arm.forge.js +53 -0
  142. package/examples/furniture/adjustable-table.forge.js +15 -15
  143. package/examples/furniture/bathroom.forge.js +26 -26
  144. package/examples/furniture/chair.forge.js +13 -13
  145. package/examples/furniture/picture-frame.forge.js +6 -6
  146. package/examples/furniture/shoe-rack-doors.forge.js +8 -8
  147. package/examples/furniture/shoe-rack.forge.js +7 -7
  148. package/examples/furniture/table-lamp.forge.js +8 -8
  149. package/examples/gcode/lissajous-vase.forge.js +4 -4
  150. package/examples/gcode/math-surface.forge.js +3 -3
  151. package/examples/gcode/parametric-vase.forge.js +4 -4
  152. package/examples/gcode/spiral-tower.forge.js +4 -4
  153. package/examples/generative/crystal-growth.forge.js +9 -9
  154. package/examples/generative/frost-spires.forge.js +9 -9
  155. package/examples/generative/golden-spiral-tower.forge.js +11 -11
  156. package/examples/generative/molten-forge.forge.js +6 -6
  157. package/examples/generative/neon-coral.forge.js +7 -7
  158. package/examples/mechanical/3d-printer.forge.js +37 -37
  159. package/examples/mechanical/5-finger-robot-hand.forge.js +19 -19
  160. package/examples/mechanical/airplane-propeller.forge.js +9 -9
  161. package/examples/mechanical/bolt-and-nut.forge.js +10 -10
  162. package/examples/mechanical/door-with-hinges.forge.js +7 -7
  163. package/examples/mechanical/fillet-enclosure.forge.js +15 -11
  164. package/examples/mechanical/headphone-hanger-v2.forge.js +11 -11
  165. package/examples/mechanical/robot_hand.forge.js +24 -24
  166. package/examples/mechanical/robot_hand_2.forge.js +26 -26
  167. package/examples/nurbs-surface.forge.js +8 -0
  168. package/examples/nurbs-tube.forge.js +7 -0
  169. package/examples/products/bottle.forge.js +8 -8
  170. package/examples/products/chess-set.forge.js +25 -25
  171. package/examples/products/classical-piano.forge.js +20 -20
  172. package/examples/products/clock.forge.js +33 -33
  173. package/examples/products/cup.forge.js +5 -5
  174. package/examples/products/iphone.forge.js +20 -20
  175. package/examples/products/laptop.forge.js +24 -24
  176. package/examples/products/laser-cut-box.forge.js +6 -6
  177. package/examples/products/laser-cut-tray.forge.js +6 -6
  178. package/examples/products/liquid-soap-dispenser.forge.js +23 -23
  179. package/examples/products/origami-fish.forge.js +14 -12
  180. package/examples/products/spiderman-cake.forge.js +6 -6
  181. package/examples/shelf/container.forge.js +5 -5
  182. package/examples/shelf/shelf-unit.forge.js +6 -6
  183. package/examples/toolbox/bolted-joint.forge.js +7 -7
  184. package/package.json +9 -4
  185. package/dist/assets/EditorApp-B-vQvgam.js +0 -9888
  186. package/dist/assets/LandingPage-C5n9hDXI.js +0 -322
  187. package/dist/assets/PublishedModelPage-Dt7PCVBj.js +0 -146
  188. package/dist/assets/__vite-browser-external-CURh0WXD.js +0 -8
  189. package/dist/assets/deserializeRunResult-BLAFoiE0.js +0 -19365
  190. package/dist/assets/index-1CYp3zUp.js +0 -1455
  191. package/dist-skill/docs/API/API.md +0 -1666
  192. package/dist-skill/docs/API/README.md +0 -37
  193. package/dist-skill/docs/API/assembly/assembly.md +0 -617
  194. package/dist-skill/docs/API/core/edge-queries.md +0 -130
  195. package/dist-skill/docs/API/core/parameters.md +0 -122
  196. package/dist-skill/docs/API/core/reserved-terms.md +0 -137
  197. package/dist-skill/docs/API/core/sdf.md +0 -326
  198. package/dist-skill/docs/API/core/skill-cli.md +0 -194
  199. package/dist-skill/docs/API/core/skill-guide.md +0 -205
  200. package/dist-skill/docs/API/core/specs.md +0 -186
  201. package/dist-skill/docs/API/core/topology.md +0 -372
  202. package/dist-skill/docs/API/entities.md +0 -268
  203. package/dist-skill/docs/API/output/bom.md +0 -58
  204. package/dist-skill/docs/API/output/brep-export.md +0 -87
  205. package/dist-skill/docs/API/output/dimensions.md +0 -67
  206. package/dist-skill/docs/API/output/export.md +0 -110
  207. package/dist-skill/docs/API/output/gcode.md +0 -195
  208. package/dist-skill/docs/API/runtime/viewport.md +0 -420
  209. package/dist-skill/docs/API/sheet-metal/sheet-metal.md +0 -185
  210. package/dist-skill/docs/API/sketch/anchor.md +0 -37
  211. package/dist-skill/docs/API/sketch/booleans.md +0 -91
  212. package/dist-skill/docs/API/sketch/core.md +0 -73
  213. package/dist-skill/docs/API/sketch/extrude.md +0 -62
  214. package/dist-skill/docs/API/sketch/on-face.md +0 -104
  215. package/dist-skill/docs/API/sketch/operations.md +0 -78
  216. package/dist-skill/docs/API/sketch/path.md +0 -75
  217. package/dist-skill/docs/API/sketch/primitives.md +0 -146
  218. package/dist-skill/docs/API/sketch/regions.md +0 -80
  219. package/dist-skill/docs/API/sketch/text.md +0 -108
  220. package/dist-skill/docs/API/sketch/transforms.md +0 -65
  221. package/dist-skill/docs/API/toolbox/fasteners.md +0 -129
  222. package/dist-skill/docs/INDEX.md +0 -94
  223. package/dist-skill/docs/RELEASING.md +0 -55
  224. package/dist-skill/docs/cli-monetization.md +0 -111
  225. package/dist-skill/docs/deployment.md +0 -281
  226. package/dist-skill/docs/generated/concepts.md +0 -2112
  227. package/dist-skill/docs/internals/shape-from-slices.md +0 -152
  228. package/dist-skill/docs/platform/admin.md +0 -45
  229. package/dist-skill/docs/platform/architecture.md +0 -79
  230. package/dist-skill/docs/platform/auth.md +0 -110
  231. package/dist-skill/docs/platform/email.md +0 -67
  232. package/dist-skill/docs/platform/projects.md +0 -111
  233. package/dist-skill/docs/platform/sharing.md +0 -90
  234. package/dist-skill/docs/runbook.md +0 -345
@@ -5,440 +5,509 @@ skill-order: 100
5
5
 
6
6
  # Viewport & Runtime
7
7
 
8
- > **Auto-generated** from `src/forge/forge-public-api.ts`. Do not edit by hand — run `npm run gen:docs` to regenerate.
9
-
10
8
  Cut planes, exploded views, joint animations, and scene configuration.
11
9
 
10
+ ## Contents
11
+
12
+ - [Viewport & Runtime](#viewport-runtime) — `jointsView`, `explodeView`, `cutPlane`, `cutPlane`, `mock`, `scene`, `viewConfig`, `showLabels`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`, `highlight`
13
+ - [RouteBuilder](#routebuilder)
14
+ - [route](#route)
15
+
12
16
  ## Functions
13
17
 
14
18
  ### Viewport & Runtime
15
19
 
16
- Configure viewport behavior: cut planes, exploded views, joint controls.
20
+ #### `jointsView()` — Register viewport-only mechanism controls that animate returned objects without re-running the script.
17
21
 
18
- #### `jointsView()`
22
+ Defines joints (revolute or prismatic), optional gear/rack couplings, and named animations. The viewport resolves transforms through the joint chain at display time — the script geometry is computed only once at rest pose.
19
23
 
20
- ```ts
21
- jointsView(options?: JointsViewOptions): void
24
+ **Critical:** Solve the assembly at **rest pose** (all animated joints = 0). The viewport applies `jointsView` transforms on top of the returned scene. If geometry is already solved at non-zero angles, animation will double-rotate everything.
25
+
26
+ ```js
27
+ // BAD — double rotation
28
+ const solved = mech.solve({ shoulder: 45, elbow: 30 });
29
+ jointsView({ joints: [{ name: 'shoulder', ... }] });
30
+ return solved;
31
+
32
+ // GOOD — rest pose, jointsView controls all posing
33
+ const solved = mech.solve({ shoulder: 0, elbow: 0 });
34
+ jointsView({
35
+ joints: [
36
+ { name: 'shoulder', child: 'Upper Arm', default: 45, ... },
37
+ { name: 'elbow', child: 'Forearm', parent: 'Upper Arm', default: 30, ... },
38
+ ],
39
+ });
40
+ return solved;
22
41
  ```
23
42
 
24
- Configure runtime joint controls that animate object transforms in the viewport without re-running the script.
43
+ **Pivot coordinates** are world-space positions of each joint origin at rest pose. For `addRevolute('shoulder', 'Base', 'Link', { frame: Transform.identity().translate(0, 0, 20) })` where "Base" is at world origin, the pivot is `[0, 0, 20]`.
25
44
 
26
- <details><summary><code>JointsViewOptions</code></summary>
45
+ **Fixed attachments** that must follow a parent during animation need a zero-angle revolute joint in the chain:
27
46
 
28
- ```ts
29
- interface JointsViewOptions {
30
- enabled?: boolean;
31
- joints?: JointViewInput[];
32
- couplings?: JointViewCouplingInput[];
33
- animations?: JointViewAnimationInput[];
34
- defaultAnimation?: string;
35
- }
47
+ ```js
48
+ { name: 'EE_Follow', child: 'End Effector', parent: 'Last Link',
49
+ type: 'revolute', axis: [0, 0, 1], pivot: [linkLength, 0, 0],
50
+ min: 0, max: 0, default: 0 }
36
51
  ```
37
52
 
38
- </details>
53
+ Animation values are interpolated linearly between keyframes. ForgeCAD does **not** auto-wrap revolute values across `-180/180`. Keep keyframe values continuous — a `-180 -> 171` jump spins the part the long way around. Use `-180 -> -189` instead. Author high-speed multi-turn joints as accumulating angles (`0, 360, 720, ...`) with `continuous: true`.
39
54
 
40
- <details><summary><code>JointViewInput</code></summary>
55
+ **Tick-based keyframes:** Omit `at` from all keyframes to auto-distribute by tick weight:
41
56
 
42
- ```ts
43
- interface JointViewInput {
44
- name: string;
45
- child: string;
46
- parent?: string;
47
- type?: JointViewType;
48
- axis?: JointViewAxis;
49
- min?: number;
50
- max?: number;
51
- default?: number;
52
- unit?: string;
53
- hidden?: boolean;
54
- }
57
+ ```js
58
+ keyframes: [
59
+ { ticks: 3, values: { Shoulder: 20 } }, // slow segment (3x weight)
60
+ { ticks: 1, values: { Shoulder: -10 } }, // fast segment (1x weight)
61
+ { values: { Shoulder: 20 } }, // last keyframe; ticks ignored
62
+ ]
63
+ // positions: 0, 0.75, 1.0
55
64
  ```
56
65
 
57
- </details>
58
-
59
- <details><summary><code>JointViewCouplingInput</code></summary>
66
+ Mixing explicit `at` and omitted `at` in the same animation is not allowed.
67
+
68
+ ```js
69
+ jointsView({
70
+ joints: [{
71
+ name: 'Shoulder', child: 'Upper Arm', parent: 'Base',
72
+ type: 'revolute', axis: [0, -1, 0], pivot: [0, 0, 46],
73
+ min: -30, max: 110, default: 15,
74
+ }],
75
+ animations: [{
76
+ name: 'Walk Cycle', duration: 1.6, loop: true,
77
+ keyframes: [
78
+ { values: { Shoulder: 20 } },
79
+ { values: { Shoulder: -10 } },
80
+ { values: { Shoulder: 20 } },
81
+ ],
82
+ }],
83
+ });
84
+ ```
60
85
 
61
86
  ```ts
62
- interface JointViewCouplingInput {
63
- joint: string;
64
- terms: JointViewCouplingTermInput[];
65
- offset?: number;
66
- }
87
+ jointsView(options?: JointsViewOptions): void
67
88
  ```
68
89
 
69
- </details>
90
+ **`JointsViewOptions`**: `enabled?: boolean`, `joints?: JointViewInput[]`, `couplings?: JointViewCouplingInput[]`, `animations?: JointViewAnimationInput[]`, `defaultAnimation?: string`
70
91
 
71
- <details><summary><code>JointViewCouplingTermInput</code></summary>
92
+ **`JointViewInput`**: `name: string`, `child: string`, `parent?: string`, `type?: JointViewType`, `axis?: JointViewAxis`, `min?: number`, `max?: number`, `default?: number`, `unit?: string`, `hidden?: boolean`
72
93
 
73
- ```ts
74
- interface JointViewCouplingTermInput {
75
- joint: string;
76
- ratio?: number;
77
- }
78
- ```
94
+ `JointViewCouplingInput`: `{ joint: string, terms: JointViewCouplingTermInput[], offset?: number }`
95
+
96
+ `JointViewCouplingTermInput`: `{ joint: string, ratio?: number }`
97
+
98
+ `JointViewAnimationInput`: `{ name: string, duration?: number, loop?: boolean, continuous?: boolean, keyframes: JointViewAnimationKeyframeInput[] }`
99
+
100
+ **`JointViewAnimationKeyframeInput`**
101
+ - `at?: number` — Timeline position [0, 1]. If omitted from ALL keyframes, positions are auto-computed from tick weights.
102
+ - `ticks?: number` — Relative weight of the segment from this keyframe to the next (default 1). Only used in tick-based mode (when `at` is omitted). Last keyframe's ticks value is ignored.
103
+ - Also: `values: Record<string, number>`
79
104
 
80
- </details>
105
+ #### `explodeView()` — Configure how the viewport explode slider offsets returned objects.
81
106
 
82
- <details><summary><code>JointViewAnimationInput</code></summary>
107
+ Offsets are resolved from the returned object tree, not a flat list. In `radial` mode each node follows its parent branch direction, then fans locally from the immediate parent center — nested assemblies peel apart level by level. In fixed-axis or fixed-vector modes, the branch follows that axis/vector but nested descendants fan out perpendicular by default.
108
+
109
+ Multiple calls merge — later values override earlier ones on a per-key basis. `byName` and `byPath` maps are merged entry-by-entry.
110
+
111
+ For programmatic explode applied before returning (without the slider), use `lib.explode()` instead.
112
+
113
+ ```js
114
+ explodeView({
115
+ amountScale: 1.2,
116
+ stages: [0.35, 0.8],
117
+ mode: 'radial',
118
+ byPath: { 'Drive/Shaft': { direction: [1, 0, 0], stage: 1.6 } },
119
+ });
120
+ ```
83
121
 
84
122
  ```ts
85
- interface JointViewAnimationInput {
86
- name: string;
87
- duration?: number;
88
- loop?: boolean;
89
- continuous?: boolean;
90
- keyframes: JointViewAnimationKeyframeInput[];
91
- }
123
+ explodeView(options?: ExplodeViewOptions): void
92
124
  ```
93
125
 
94
- </details>
126
+ **`ExplodeViewOptions`**
127
+
128
+ | Option | Type | Description |
129
+ |--------|------|-------------|
130
+ | `enabled?` | `boolean` | Set false to disable viewport explode offsets for this script output. |
131
+ | `amountScale?` | `number` | Scales the UI explode amount. Default: 1 |
132
+ | `stages?` | `number[]` | Per-depth stage multipliers (depth 1 = first level). If depth exceeds this array, the last value is reused. Default when omitted: reciprocal depth (1, 1/2, 1/3, ...) |
133
+ | `mode?` | `ExplodeViewDirection` | Global direction mode fallback. Default: 'radial' |
134
+ | `axisLock?` | `ExplodeAxis` | Global axis lock fallback. |
135
+ | `byName?` | `Record<string, ExplodeViewDirective>` | Per-object overrides by final object name. |
136
+ | `byPath?` | `Record<string, ExplodeViewDirective>` | Per-tree-path overrides using slash-separated object tree segments. |
137
+
138
+ **`ExplodeDirective`**
139
+ - `stage?: number` — Multiplier applied to `amount` for this node
140
+ - `direction?: ExplodeDirection` — Direction mode for this node
141
+ - `axisLock?: ExplodeAxis` — Optional axis lock after direction is resolved
142
+
143
+ #### `cutPlane()` — Define a named section plane for inspecting internal geometry.
144
+
145
+ Registers a cut plane that appears as a toggle in the viewport View Panel. When enabled, geometry on the positive side of the plane (the side the normal points toward) is clipped away, revealing the internal cross-section. The newly exposed section faces render with a hatched overlay; pre-existing coplanar boundary faces are left unhatched.
95
146
 
96
- <details><summary><code>JointViewAnimationKeyframeInput</code></summary>
147
+ Planes are registered once per script run. The viewport toggle state (on/off) persists across parameter changes without re-running the script. The `exclude` option only works correctly when the excluded object names are stable across parameter changes.
148
+
149
+ Accepts two overloads: `cutPlane(name, normal, offset?, options?)` or `cutPlane(name, normal, options?)` where options may include `offset`.
150
+
151
+ ```js
152
+ const cutZ = param('Cut Height', 10, { min: -50, max: 50, unit: 'mm' });
153
+ cutPlane('Inspection', [0, 0, 1], cutZ, { exclude: ['Probe', 'Fasteners'] });
154
+ ```
97
155
 
98
156
  ```ts
99
- interface JointViewAnimationKeyframeInput {
100
- /** Timeline position [0, 1]. If omitted from ALL keyframes, positions are auto-computed from tick weights. */
101
- at?: number;
102
- /** Relative weight of the segment from this keyframe to the next (default 1). Only used in tick-based mode (when `at` is omitted). Last keyframe's ticks value is ignored. */
103
- ticks?: number;
104
- values: Record<string, number>;
105
- }
157
+ cutPlane(name: string, normal: [ number, number, number ], offset?: number, options?: CutPlaneOptions): void
106
158
  ```
107
159
 
108
- </details>
160
+ **`CutPlaneOptions`**
161
+ - `offset?: number` — Optional offset along the plane normal (primarily for object-form overload).
162
+ - `exclude?: CutPlaneExcludeInput` — Object names to keep uncut for this plane.
109
163
 
110
- #### `explodeView()`
164
+ #### `cutPlane()`
111
165
 
112
166
  ```ts
113
- explodeView(options?: ExplodeViewOptions): void
167
+ cutPlane(name: string, normal: [ number, number, number ], options?: CutPlaneOptions): void
114
168
  ```
115
169
 
116
- Configure viewport exploded-view behavior for the current script execution. Multiple calls merge; later values override earlier ones.
170
+ #### `mock()` Register a mock (context) object for visualization and collision checking.
117
171
 
118
- <details><summary><code>ExplodeViewOptions</code></summary>
172
+ Mock objects appear in the viewport and spatial analysis when you run a file directly, but are excluded when the file is imported via [`require()`](/docs/core#require). This lets you model the surrounding context — walls, bolts, mating parts — without polluting the module's exports.
173
+
174
+ The shape is returned unchanged, so you can reference it for alignment, dimensioning, and `verify` checks.
175
+
176
+ Mock objects participate in `forgecad run` collision detection and spatial analysis. Their names appear with a `(mock)` suffix in reports.
177
+
178
+ In the viewport, mock objects render at reduced opacity so they are visually distinct from real geometry.
119
179
 
120
180
  ```ts
121
- interface ExplodeViewOptions {
122
- /** Set false to disable viewport explode offsets for this script output. */
123
- enabled?: boolean;
124
- /** Scales the UI explode amount. Default: 1 */
125
- amountScale?: number;
126
- /** Per-depth stage multipliers (depth 1 = first level). If depth exceeds this array, the last value is reused. Default when omitted: reciprocal depth (1, 1/2, 1/3, ...) */
127
- stages?: number[];
128
- /** Global direction mode fallback. Default: 'radial' */
129
- mode?: ExplodeViewDirection;
130
- /** Global axis lock fallback. */
131
- axisLock?: ExplodeAxis;
132
- /** Per-object overrides by final object name. */
133
- byName?: Record<string, ExplodeViewDirective>;
134
- /** Per-tree-path overrides using slash-separated object tree segments. */
135
- byPath?: Record<string, ExplodeViewDirective>;
136
- }
181
+ // bracket.forge.js
182
+ const wall = mock(box(100, 200, 10).translate(0, 0, -5), "wall");
183
+ const bolt = mock(cylinder(3, 15).translate(10, 15, 0), "bolt");
184
+
185
+ const bracket = box(20, 30, 5);
186
+ verify.notColliding("bracket vs wall", bracket, wall);
187
+
188
+ return bracket;
189
+ // When imported: only bracket is exported
190
+ // When run directly: bracket + wall + bolt all visible
137
191
  ```
138
192
 
139
- </details>
193
+ ```ts
194
+ mock<T extends Shape>(shape: T, name?: string): T
195
+ ```
140
196
 
141
- <details><summary><code>ExplodeDirective</code></summary>
197
+ #### `scene()` — Configure the scene environment for the current script execution.
198
+
199
+ Controls camera position, lighting rig, background color or gradient, atmospheric fog, environment maps, post-processing effects, and capture parameters for the `forgecad capture` command. Multiple calls merge — later values override earlier ones on a per-key basis, so you can split configuration across multiple `scene()` calls.
200
+
201
+ When `lights` is specified, **all** default lights are removed. You must include your own ambient light or the scene will be fully dark.
202
+
203
+ Setting `camera.position` overrides auto-framing — the viewport will no longer auto-fit the geometry on script reload.
204
+
205
+ Post-processing effects (`bloom`, `vignette`, `grain`) work in the browser viewport only. The CLI applies camera, lights, background, fog, and `toneMappingExposure` but skips shader effects.
206
+
207
+ All numeric values accept `param()` expressions.
208
+
209
+ ```js
210
+ scene({
211
+ background: { top: '#000814', bottom: '#001d3d' },
212
+ camera: { position: [160, -120, 100], target: [0, 0, 50], fov: 52 },
213
+ lights: [
214
+ { type: 'ambient', color: '#001233', intensity: 0.08 },
215
+ { type: 'point', position: [120, -80, 130], color: '#00f5d4', intensity: 4, distance: 400, decay: 1 },
216
+ { type: 'point', position: [-100, 60, 20], color: '#f72585', intensity: 3, distance: 350 },
217
+ { type: 'directional', position: [50, -30, 200], color: '#ffd60a', intensity: 1.2 },
218
+ { type: 'hemisphere', skyColor: '#003566', groundColor: '#000814', intensity: 0.2 },
219
+ ],
220
+ fog: { color: '#000814', near: 100, far: 450 },
221
+ postProcessing: {
222
+ bloom: { intensity: param('bloom', 1.5, 0, 4), threshold: 0.5, radius: 0.7 },
223
+ vignette: { darkness: 0.8, offset: 0.25 },
224
+ grain: { intensity: 0.08 },
225
+ toneMappingExposure: param('exposure', 1.5, 0.5, 4),
226
+ },
227
+ });
228
+ ```
142
229
 
143
230
  ```ts
144
- interface ExplodeDirective {
145
- /** Multiplier applied to `amount` for this node */
146
- stage?: number;
147
- /** Direction mode for this node */
148
- direction?: ExplodeDirection;
149
- /** Optional axis lock after direction is resolved */
150
- axisLock?: ExplodeAxis;
151
- }
231
+ scene(options: SceneOptions): void
152
232
  ```
153
233
 
154
- </details>
234
+ **`SceneOptions`**
235
+
236
+ | Option | Type | Description |
237
+ |--------|------|-------------|
238
+ | `capture?` | `SceneCaptureConfig` | Default capture parameters for `forgecad capture` — CLI flags override these. |
239
+ | `background?`, `camera?`, `lights?`, `environment?`, `fog?`, `postProcessing?`, `ground?` | | — |
240
+
241
+ `SceneBackgroundGradient`: `{ top: string, bottom: string }`
242
+
243
+ `SceneCameraConfig`: `{ fov?: number, type?: "perspective" | "orthographic" }`
244
+
245
+ **`SceneLightConfig`**
246
+
247
+ | Option | Type | Description |
248
+ |--------|------|-------------|
249
+ | `groundColor?` | `string` | Ground color for hemisphere lights |
250
+ | `skyColor?` | `string` | Sky color alias for hemisphere lights (same as color) |
251
+ | `angle?` | `number` | Spot light cone angle in radians |
252
+ | `penumbra?` | `number` | Spot light penumbra (0–1) |
253
+ | `decay?` | `number` | Point/spot light decay |
254
+ | `distance?` | `number` | Point/spot light distance (0 = infinite) |
255
+ | `castShadow?` | `boolean` | Whether this light casts shadows |
256
+ | `type`, `color?`, `intensity?` | | — |
257
+
258
+ **`SceneEnvironmentConfig`**
259
+ - `preset?: "studio" | "sunset" | "dawn" | "warehouse" | "forest" | "apartment" | "lobby" | "city" | "park" | "night" | "none"` — Built-in preset name or 'none' to disable
260
+ - `intensity?: number` — Environment map intensity
261
+ - `background?: boolean` — Use environment map as scene background
262
+
263
+ **`SceneFogConfig`**
264
+ - `near?: number` — Linear fog near distance
265
+ - `far?: number` — Linear fog far distance
266
+ - `density?: number` — Exponential fog density (if set, uses FogExp2 instead of linear Fog)
267
+ - Also: `color?: string`
268
+
269
+ `ScenePostProcessingConfig`: `{ bloom?: SceneBloomConfig, vignette?: SceneVignetteConfig, grain?: SceneGrainConfig, toneMappingExposure?: number }`
270
+
271
+ `SceneBloomConfig`: `{ intensity?: number, threshold?: number, radius?: number }`
155
272
 
156
- <details><summary><code>ExplodeViewDirective</code> extends ExplodeDirective</summary>
273
+ `SceneVignetteConfig`: `{ darkness?: number, offset?: number }`
274
+
275
+ `SceneGrainConfig`: `{ intensity?: number }`
276
+
277
+ **`SceneGroundConfig`**
278
+
279
+ | Option | Type | Description |
280
+ |--------|------|-------------|
281
+ | `visible?` | `boolean` | Show a ground plane |
282
+ | `color?` | `string` | Ground color |
283
+ | `offset?` | `number` | Offset below the model's bounding box minimum Z. Default 0 (flush with model bottom). |
284
+ | `receiveShadow?` | `boolean` | Receive shadows on the ground |
285
+
286
+ **`SceneCaptureConfig`**
287
+
288
+ | Option | Type | Description |
289
+ |--------|------|-------------|
290
+ | `framesPerTurn?` | `number` | Frames for one full orbit rotation (default: 72) |
291
+ | `holdFrames?` | `number` | Frozen frames before motion starts (default: 6) |
292
+ | `pitchDeg?` | `number` | Orbit pitch angle in degrees (default: auto from camera) |
293
+ | `fps?` | `number` | Output frame rate (default: 24) |
294
+ | `size?` | `number` | Output frame size in pixels (default: 960) |
295
+ | `background?` | `string` | Canvas background color for capture (default: '#252526') |
296
+
297
+ #### `viewConfig()` — Configure viewport helper visuals for the current script execution.
298
+
299
+ Controls renderer-only overlays that appear in the viewport but are not part of the geometry. Currently supports the joint overlay that renders axis arrows and arc indicators when `jointsView` is active. Multiple calls merge — later values override earlier ones per key.
300
+
301
+ This does **not** trigger a geometry recompute; it only affects the visual helpers drawn on top of the 3D scene.
302
+
303
+ ```js
304
+ viewConfig({
305
+ jointOverlay: {
306
+ axisColor: '#13dfff',
307
+ arcColor: '#ff7a1a',
308
+ axisLineRadiusScale: 0.03,
309
+ arcLineRadiusScale: 0.022,
310
+ },
311
+ });
312
+ ```
157
313
 
158
314
  ```ts
159
- interface ExplodeViewDirective extends ExplodeDirective {
160
- }
315
+ viewConfig(options?: ViewConfigOptions): void
161
316
  ```
162
317
 
163
- </details>
318
+ `ViewConfigOptions`: `{ jointOverlay?: JointOverlayViewConfigOptions }`
164
319
 
165
- #### `cutPlane()`
320
+ **`JointOverlayViewConfigOptions`**: `enabled?: boolean`, `axisColor?: string`, `axisCoreColor?: string`, `arcColor?: string`, `zeroColor?: string`, `arcVisualLimitDeg?: number`, `axisLengthScale?: number`, `axisLengthMin?: number`, `axisLineRadiusScale?: number`, `axisLineRadiusMin?: number`, `axisLineRadiusMax?: number`, `spokeLineRadiusScale?: number`, `spokeLineRadiusMin?: number`, `spokeLineRadiusMax?: number`, `arcLineRadiusScale?: number`, `arcLineRadiusMin?: number`, `arcLineRadiusMax?: number`, `axisDotRadiusScale?: number`, `axisDotRadiusMin?: number`, `axisArrowRadiusScale?: number`, `axisArrowRadiusMin?: number`, `axisArrowLengthScale?: number`, `axisArrowLengthMin?: number`, `axisArrowOffsetFactor?: number`, `arcRadiusScale?: number`, `arcRadiusMin?: number`, `arcDotRadiusScale?: number`, `arcDotRadiusMin?: number`, `arcArrowRadiusScale?: number`, `arcArrowRadiusMin?: number`, `arcArrowLengthScale?: number`, `arcArrowLengthMin?: number`, `arcArrowOffsetFactor?: number`, `arcStepDeg?: number`, `arcMinSteps?: number`, `arcTubeSegmentsMin?: number`, `arcTubeSegmentsFactor?: number`, `arcTubeRadialSegments?: number`
321
+
322
+ #### `showLabels()` — Highlight all user-labeled faces on a shape for visual debugging.
323
+
324
+ Shows each user-authored label name in the viewport for visual debugging. Returns the shape unchanged for chaining: `return showLabels(myShape)`.
166
325
 
167
326
  ```ts
168
- cutPlane(name: string, normal: [ number, number, number ], offset?: number, options?: CutPlaneOptions): void
327
+ showLabels(shape: Shape): Shape
169
328
  ```
170
329
 
171
- Define a named section/cut plane. Appears as a toggle in the View Panel. When enabled, geometry on the positive side of the plane is clipped away.
330
+ #### `highlight()` Highlight any geometry for visual debugging in the viewport.
172
331
 
173
- <details><summary><code>CutPlaneOptions</code></summary>
332
+ Supported inputs:
333
+
334
+ - `string` — sketch entity ID (e.g. `'L0'`, `'P0'`, `'C0'`)
335
+ - `[x, y, z]` — 3D point
336
+ - `[[x1,y1,z1], [x2,y2,z2]]` — edge (line segment)
337
+ - `{ normal: [x,y,z], offset: number }` — plane by normal + distance from origin
338
+ - `{ normal: [x,y,z], point: [x,y,z] }` — plane by normal + point on plane
339
+ - [`Shape`](/docs/core#shape) — highlight entire 3D shape
340
+ - `FaceRef` (from `shape.face('top')`) — highlight as plane at face center
341
+ - `EdgeRef` (from `shape.edge('left')`) — highlight as edge segment
174
342
 
175
343
  ```ts
176
- interface CutPlaneOptions {
177
- /** Optional offset along the plane normal (primarily for object-form overload). */
178
- offset?: number;
179
- /** Object names to keep uncut for this plane. */
180
- exclude?: CutPlaneExcludeInput;
181
- }
344
+ highlight(entityId: string, opts?: HighlightOptions): void
182
345
  ```
183
346
 
184
- </details>
347
+ **`HighlightOptions`**
348
+ - `size?: number` — Size hint for points (radius in mm) or planes (disc radius in mm).
349
+ - Also: `color?: string, label?: string, pulse?: boolean`
185
350
 
186
- #### `cutPlane()`
351
+ #### `highlight()`
187
352
 
188
353
  ```ts
189
- cutPlane(name: string, normal: [ number, number, number ], options?: CutPlaneOptions): void
354
+ highlight(point: [ number, number, number ], opts?: HighlightOptions): void
190
355
  ```
191
356
 
192
- #### `scene()`
357
+ #### `highlight()`
193
358
 
194
359
  ```ts
195
- scene(options: SceneOptions): void
360
+ highlight(edge: [ [ number, number, number ], [ number, number, number ] ], opts?: HighlightOptions): void
196
361
  ```
197
362
 
198
- Configure the scene environment for the current script execution. Controls camera, lighting, background, fog, and post-processing. Multiple calls merge; later values override earlier ones. ```js scene({ background: '#0a0a0a', camera: { position: [200, 100, 150], target: [0, 0, 30], fov: 60 }, lights: [ { type: 'ambient', color: '#1a1a2e', intensity: 0.2 }, { type: 'point', position: [0, 0, 100], color: '#ff6b35', intensity: 2 }, ], fog: { color: '#0a0a0a', near: 100, far: 500 }, postProcessing: { bloom: { intensity: 1.5, threshold: 0.8, radius: 0.4 }, }, }); ```
363
+ #### `highlight()`
199
364
 
200
- <details><summary><code>SceneOptions</code></summary>
365
+ ```ts
366
+ highlight(plane: { normal: [ number, number, number ]; offset: number; }, opts?: HighlightOptions): void
367
+ ```
368
+
369
+ #### `highlight()`
201
370
 
202
371
  ```ts
203
- interface SceneOptions {
204
- background?: string | SceneBackgroundGradient;
205
- camera?: SceneCameraConfig;
206
- lights?: SceneLightConfig[];
207
- environment?: SceneEnvironmentConfig;
208
- fog?: SceneFogConfig;
209
- postProcessing?: ScenePostProcessingConfig;
210
- ground?: SceneGroundConfig;
211
- /** Default capture parameters for `forgecad capture` — CLI flags override these. */
212
- capture?: SceneCaptureConfig;
213
- }
372
+ highlight(plane: { normal: [ number, number, number ]; point: [ number, number, number ]; }, opts?: HighlightOptions): void
214
373
  ```
215
374
 
216
- </details>
375
+ #### `highlight()`
376
+
377
+ ```ts
378
+ highlight(shape: Shape, opts?: HighlightOptions): void
379
+ ```
217
380
 
218
- <details><summary><code>SceneBackgroundGradient</code></summary>
381
+ #### `highlight()`
219
382
 
220
383
  ```ts
221
- interface SceneBackgroundGradient {
222
- top: string;
223
- bottom: string;
224
- }
384
+ highlight(face: FaceRef, opts?: HighlightOptions): void
225
385
  ```
226
386
 
227
- </details>
387
+ **`FaceRef`**
388
+ - `query?: FaceQueryRef` — Compiler-owned face query when available.
389
+ - `planar?: boolean` — True when the face can host a 2D sketch placement frame
390
+ - `descendant?: FaceDescendantMetadata` — Shared descendant-resolution metadata when this face is a semantic region/set.
391
+ - Also: `name: FaceName`
392
+
393
+ **`FaceDescendantMetadata`**: `kind: "single" | "face-set"`, `semantic: FaceDescendantSemantic`, `memberCount: number`, `memberNames: string[]`, `coplanar: boolean`
228
394
 
229
- <details><summary><code>SceneCameraConfig</code></summary>
395
+ #### `highlight()`
230
396
 
231
397
  ```ts
232
- interface SceneCameraConfig {
233
- fov?: number;
234
- type?: "perspective" | "orthographic";
235
- }
398
+ highlight(edge: EdgeRef, opts?: HighlightOptions): void
236
399
  ```
237
400
 
238
- </details>
401
+ **`EdgeRef`**
402
+ - `query?: EdgeQueryRef` — Compiler-owned edge query when available.
403
+ - Also: `name: EdgeName`
239
404
 
240
- <details><summary><code>SceneLightConfig</code></summary>
405
+ ---
406
+
407
+ ## Classes
408
+
409
+ ### `RouteBuilder`
410
+
411
+ #### `up()` — Vertical line going +Y. Length is optional (solver determines it from constraints).
241
412
 
242
413
  ```ts
243
- interface SceneLightConfig {
244
- type: SceneLightType;
245
- color?: string;
246
- intensity?: number;
247
- /** Ground color for hemisphere lights */
248
- groundColor?: string;
249
- /** Sky color alias for hemisphere lights (same as color) */
250
- skyColor?: string;
251
- /** Spot light cone angle in radians */
252
- angle?: number;
253
- /** Spot light penumbra (0–1) */
254
- penumbra?: number;
255
- /** Point/spot light decay */
256
- decay?: number;
257
- /** Point/spot light distance (0 = infinite) */
258
- distance?: number;
259
- /** Whether this light casts shadows */
260
- castShadow?: boolean;
261
- }
414
+ up(length?: number): LineId
262
415
  ```
263
416
 
264
- </details>
417
+ #### `down()` — Vertical line going -Y. Length is optional.
418
+
419
+ ```ts
420
+ down(length?: number): LineId
421
+ ```
265
422
 
266
- <details><summary><code>SceneEnvironmentConfig</code></summary>
423
+ #### `right()` — Horizontal line going +X. Length is optional.
267
424
 
268
425
  ```ts
269
- interface SceneEnvironmentConfig {
270
- /** Built-in preset name or 'none' to disable */
271
- preset?: "studio" | "sunset" | "dawn" | "warehouse" | "forest" | "apartment" | "lobby" | "city" | "park" | "night" | "none";
272
- /** Environment map intensity */
273
- intensity?: number;
274
- /** Use environment map as scene background */
275
- background?: boolean;
276
- }
426
+ right(length?: number): LineId
277
427
  ```
278
428
 
279
- </details>
429
+ #### `left()` — Horizontal line going -X. Length is optional.
280
430
 
281
- <details><summary><code>SceneFogConfig</code></summary>
431
+ ```ts
432
+ left(length?: number): LineId
433
+ ```
434
+
435
+ #### `lineAt()` — Line at an arbitrary angle (degrees from +X). Length is optional.
282
436
 
283
437
  ```ts
284
- interface SceneFogConfig {
285
- color?: string;
286
- /** Linear fog near distance */
287
- near?: number;
288
- /** Linear fog far distance */
289
- far?: number;
290
- /** Exponential fog density (if set, uses FogExp2 instead of linear Fog) */
291
- density?: number;
292
- }
438
+ lineAt(angleDeg: number, length?: number): LineId
293
439
  ```
294
440
 
295
- </details>
441
+ #### [`line()`](/docs/sketch#line) — Line with solver-determined direction. Length is optional. Direction comes from tangency to previous arc or from constraints.
442
+
443
+ ```ts
444
+ line(length?: number): LineId
445
+ ```
296
446
 
297
- <details><summary><code>ScenePostProcessingConfig</code></summary>
447
+ #### `toward()` — Line toward a specific point. Length defaults to the distance to that point.
298
448
 
299
449
  ```ts
300
- interface ScenePostProcessingConfig {
301
- bloom?: SceneBloomConfig;
302
- vignette?: SceneVignetteConfig;
303
- grain?: SceneGrainConfig;
304
- toneMappingExposure?: number;
305
- }
450
+ toward(x: number, y: number): LineId
306
451
  ```
307
452
 
308
- </details>
453
+ #### `arcLeft()` — Tangent arc turning left relative to travel direction.
309
454
 
310
- <details><summary><code>SceneBloomConfig</code></summary>
455
+ or `{ minSweep: degrees }` to seed the geometry without constraining. `minSweep` guides the solver to the correct branch for arcs that sweep more than the default 90° seed.
311
456
 
312
457
  ```ts
313
- interface SceneBloomConfig {
314
- intensity?: number;
315
- threshold?: number;
316
- radius?: number;
317
- }
458
+ arcLeft(radius?: number, sweepDegOrOpts?: number | { minSweep: number; }): ArcId
318
459
  ```
319
460
 
320
- </details>
461
+ #### `arcRight()` — Tangent arc turning right relative to travel direction.
321
462
 
322
- <details><summary><code>SceneVignetteConfig</code></summary>
463
+ or `{ minSweep: degrees }` to seed without constraining.
323
464
 
324
465
  ```ts
325
- interface SceneVignetteConfig {
326
- darkness?: number;
327
- offset?: number;
328
- }
466
+ arcRight(radius?: number, sweepDegOrOpts?: number | { minSweep: number; }): ArcId
329
467
  ```
330
468
 
331
- </details>
332
-
333
- <details><summary><code>SceneGrainConfig</code></summary>
469
+ #### `close()` — Close the route with a straight line back to the start point.
334
470
 
335
471
  ```ts
336
- interface SceneGrainConfig {
337
- intensity?: number;
338
- }
472
+ close(): void
339
473
  ```
340
474
 
341
- </details>
475
+ #### `done()` — Close the route back to its start point and register as a profile loop.
342
476
 
343
- <details><summary><code>SceneGroundConfig</code></summary>
477
+ No extra line segment is added. A coincident constraint connects the last point to the start, and tangency is added for G1 smoothness when arcs are at the junction. The session's incremental solver processes these constraints, keeping seed positions accurate for the final solve.
344
478
 
345
479
  ```ts
346
- interface SceneGroundConfig {
347
- /** Show a ground plane */
348
- visible?: boolean;
349
- /** Ground color */
350
- color?: string;
351
- /** Offset below the model's bounding box minimum Z. Default 0 (flush with model bottom). */
352
- offset?: number;
353
- /** Receive shadows on the ground */
354
- receiveShadow?: boolean;
355
- }
480
+ done(): void
356
481
  ```
357
482
 
358
- </details>
483
+ #### `start()` — PointId of the route's start point.
484
+
485
+ ```ts
486
+ get start(): PointId
487
+ ```
359
488
 
360
- <details><summary><code>SceneCaptureConfig</code></summary>
489
+ #### `end()` — PointId of the current cursor (route's end).
361
490
 
362
491
  ```ts
363
- interface SceneCaptureConfig {
364
- /** Frames for one full orbit rotation (default: 72) */
365
- framesPerTurn?: number;
366
- /** Frozen frames before motion starts (default: 6) */
367
- holdFrames?: number;
368
- /** Orbit pitch angle in degrees (default: auto from camera) */
369
- pitchDeg?: number;
370
- /** Output frame rate (default: 24) */
371
- fps?: number;
372
- /** Output frame size in pixels (default: 960) */
373
- size?: number;
374
- /** Canvas background color for capture (default: '#252526') */
375
- background?: string;
376
- }
492
+ get end(): PointId
377
493
  ```
378
494
 
379
- </details>
495
+ #### `startOf()` — Get the start point of a segment.
496
+
497
+ ```ts
498
+ startOf(segId: LineId | ArcId): PointId
499
+ ```
380
500
 
381
- #### `viewConfig()`
501
+ #### `endOf()` — Get the end point of a segment.
382
502
 
383
503
  ```ts
384
- viewConfig(options?: ViewConfigOptions): void
504
+ endOf(segId: LineId | ArcId): PointId
385
505
  ```
386
506
 
387
- Configure runtime viewport visuals for the current script execution. Multiple calls merge; later values override earlier ones.
388
-
389
- <details><summary><code>ViewConfigOptions</code></summary>
390
-
391
- ```ts
392
- interface ViewConfigOptions {
393
- jointOverlay?: JointOverlayViewConfigOptions;
394
- }
395
- ```
396
-
397
- </details>
398
-
399
- <details><summary><code>JointOverlayViewConfigOptions</code></summary>
400
-
401
- ```ts
402
- interface JointOverlayViewConfigOptions {
403
- enabled?: boolean;
404
- axisColor?: string;
405
- axisCoreColor?: string;
406
- arcColor?: string;
407
- zeroColor?: string;
408
- arcVisualLimitDeg?: number;
409
- axisLengthScale?: number;
410
- axisLengthMin?: number;
411
- axisLineRadiusScale?: number;
412
- axisLineRadiusMin?: number;
413
- axisLineRadiusMax?: number;
414
- spokeLineRadiusScale?: number;
415
- spokeLineRadiusMin?: number;
416
- spokeLineRadiusMax?: number;
417
- arcLineRadiusScale?: number;
418
- arcLineRadiusMin?: number;
419
- arcLineRadiusMax?: number;
420
- axisDotRadiusScale?: number;
421
- axisDotRadiusMin?: number;
422
- axisArrowRadiusScale?: number;
423
- axisArrowRadiusMin?: number;
424
- axisArrowLengthScale?: number;
425
- axisArrowLengthMin?: number;
426
- axisArrowOffsetFactor?: number;
427
- arcRadiusScale?: number;
428
- arcRadiusMin?: number;
429
- arcDotRadiusScale?: number;
430
- arcDotRadiusMin?: number;
431
- arcArrowRadiusScale?: number;
432
- arcArrowRadiusMin?: number;
433
- arcArrowLengthScale?: number;
434
- arcArrowLengthMin?: number;
435
- arcArrowOffsetFactor?: number;
436
- arcStepDeg?: number;
437
- arcMinSteps?: number;
438
- arcTubeSegmentsMin?: number;
439
- arcTubeSegmentsFactor?: number;
440
- arcTubeRadialSegments?: number;
441
- }
442
- ```
443
-
444
- </details>
507
+ ---
508
+
509
+ ## Constants
510
+
511
+ ### `route`
512
+
513
+ Route step factories. Access via `route.line()`, `route.fillet()`, etc.