forma-dsl-mcp 0.1.4

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 (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/package.json +52 -0
  4. package/src/bin.js +30 -0
  5. package/src/catalogue.js +703 -0
  6. package/src/document.js +285 -0
  7. package/src/examples.js +212 -0
  8. package/src/guide.js +313 -0
  9. package/src/reference/README.md +153 -0
  10. package/src/reference/api/describe-parameters.md +167 -0
  11. package/src/reference/api/evaluation-context.md +221 -0
  12. package/src/reference/api/export.md +185 -0
  13. package/src/reference/api/geometry-node.md +215 -0
  14. package/src/reference/api/kernel.md +182 -0
  15. package/src/reference/api/low-level.md +239 -0
  16. package/src/reference/api/render.md +217 -0
  17. package/src/reference/api/values.md +223 -0
  18. package/src/reference/errors.md +253 -0
  19. package/src/reference/language/control-flow.md +160 -0
  20. package/src/reference/language/expressions.md +206 -0
  21. package/src/reference/language/overview.md +156 -0
  22. package/src/reference/language/scope.md +184 -0
  23. package/src/reference/language/syntax.md +210 -0
  24. package/src/reference/reference/align.md +189 -0
  25. package/src/reference/reference/booleans.md +231 -0
  26. package/src/reference/reference/component.md +248 -0
  27. package/src/reference/reference/conversions.md +269 -0
  28. package/src/reference/reference/functions.md +314 -0
  29. package/src/reference/reference/local.md +197 -0
  30. package/src/reference/reference/model.md +213 -0
  31. package/src/reference/reference/param.md +230 -0
  32. package/src/reference/reference/part.md +229 -0
  33. package/src/reference/reference/refinement.md +284 -0
  34. package/src/reference/reference/shapes-2d.md +309 -0
  35. package/src/reference/reference/shapes-3d.md +279 -0
  36. package/src/reference/reference/transforms.md +246 -0
  37. package/src/reference.js +160 -0
  38. package/src/server.js +219 -0
  39. package/src/tools.js +523 -0
  40. package/src/workspace.js +204 -0
@@ -0,0 +1,284 @@
1
+ # Refinement
2
+
3
+ Six operations that change how geometry is built rather than where it is. Most are about
4
+ mesh quality or a single cheap cut.
5
+
6
+ | Block | Takes | Description |
7
+ | --- | --- | --- |
8
+ | [`offset`](#offset) | 2D | Grows or shrinks an outline |
9
+ | [`hull`](#hull) | 2D or 3D | The convex hull of everything inside |
10
+ | [`refine`](#refine) | 3D | Subdivides edges longer than a limit |
11
+ | [`simplify`](#simplify) | 2D or 3D | Removes vertices within a tolerance |
12
+ | [`smooth`](#smooth) | 3D | Rounds edges below a sharpness threshold |
13
+ | [`trim`](#trim) | 3D | Cuts a solid with a half-space |
14
+
15
+ ---
16
+
17
+ ## `offset`
18
+
19
+ Grows or shrinks a 2D outline, keeping true arcs at convex corners.
20
+
21
+ ```hcl
22
+ offset { amount = 2 project { bracket { } } }
23
+ offset { amount = -1.5 join = "Miter" rect { size = [40, 20] } }
24
+ ```
25
+
26
+ #### Attributes
27
+
28
+ | Attribute | Type | Default | Description |
29
+ | --- | --- | --- | --- |
30
+ | `amount` | number | **required** | Positive grows, negative shrinks |
31
+ | `join` | `"Square"` `"Round"` `"Miter"` | `"Round"` | How convex corners are treated |
32
+ | `miter_limit` | number | `2` | How far a mitred corner may extend before it is squared off |
33
+ | `segments` | integer | `0` | Segments per rounded corner |
34
+
35
+ #### Caveats
36
+
37
+ - **2D only.** `"offset" takes 2D geometry, got 3D`. There is no 3D shell operation; build
38
+ one as a `difference` of two profiles, or offset the profile before extruding.
39
+ - A negative amount larger than the shape's half-width erases it, leaving empty geometry.
40
+ - This is the operation [`rounded_rect`](shapes-2d.md#rounded_rect) is built from — a
41
+ shrunken rectangle offset back out is what makes its corners true arcs.
42
+
43
+ ---
44
+
45
+ ## `hull`
46
+
47
+ The convex hull of everything inside — the shape a rubber sheet would take around it.
48
+
49
+ ```hcl
50
+ hull {
51
+ translate { offset = [0, 0, 0] sphere { radius = 4 } }
52
+ translate { offset = [40, 0, 0] sphere { radius = 4 } }
53
+ translate { offset = [20, 30, 0] sphere { radius = 8 } }
54
+ }
55
+ ```
56
+
57
+ #### Attributes
58
+
59
+ None.
60
+
61
+ #### Caveats
62
+
63
+ - Works in 2D and 3D, passing the dimensionality through.
64
+ - Children are unioned first, so the hull is of everything together — which is the point.
65
+ - Any concavity is filled in. A hull of a C-shape is a solid blob.
66
+
67
+ ---
68
+
69
+ ## `refine`
70
+
71
+ Subdivides every edge longer than a limit, producing more, smaller triangles.
72
+
73
+ ```hcl
74
+ refine { edge_length = 1 sphere { radius = 20 } }
75
+ ```
76
+
77
+ #### Attributes
78
+
79
+ | Attribute | Type | Default | Description |
80
+ | --- | --- | --- | --- |
81
+ | `edge_length` | number | **required** | No edge longer than this survives |
82
+
83
+ #### Caveats
84
+
85
+ - 3D only.
86
+ - Triangle count grows quadratically as the limit falls. `edge_length = 0.1` on anything
87
+ large will cost seconds and hundreds of megabytes.
88
+ - Refining does not change the shape — it only adds vertices, usually so that
89
+ [`smooth`](#smooth) or a downstream deformation has something to work with.
90
+
91
+ ---
92
+
93
+ ## `simplify`
94
+
95
+ Removes vertices that lie within a tolerance of the surface they are on.
96
+
97
+ ```hcl
98
+ simplify { tolerance = 0.01 imported_thing { } }
99
+ ```
100
+
101
+ #### Attributes
102
+
103
+ | Attribute | Type | Default | Description |
104
+ | --- | --- | --- | --- |
105
+ | `tolerance` | number | `1e-6` | How far a vertex may move |
106
+
107
+ #### Caveats
108
+
109
+ - Works in 2D and 3D.
110
+ - The default is small enough to be a no-op on most geometry; it is there so the block can be
111
+ written without arguments while a value is chosen.
112
+ - A tolerance near the size of a feature will delete that feature.
113
+
114
+ ---
115
+
116
+ ## `smooth`
117
+
118
+ Rounds edges whose dihedral angle is below a threshold.
119
+
120
+ ```hcl
121
+ smooth { min_sharp_angle = 40 refine { edge_length = 1 blocky_thing { } } }
122
+ ```
123
+
124
+ #### Attributes
125
+
126
+ | Attribute | Type | Default | Description |
127
+ | --- | --- | --- | --- |
128
+ | `min_sharp_angle` | number | `60` | Degrees. Edges sharper than this stay sharp |
129
+ | `min_smoothness` | number | `0` | How strongly to smooth, from `0` to `1` |
130
+
131
+ #### Caveats
132
+
133
+ - 3D only.
134
+ - **Smoothing needs vertices to work with.** On a cube there is nothing between the corners
135
+ to move, so the result is barely different. Pair it with [`refine`](#refine).
136
+ - This is a mesh operation, not a fillet. For a controlled radius, model the fillet — round
137
+ the profile, or union in a [`torus`](shapes-3d.md#torus).
138
+
139
+ ---
140
+
141
+ ## `trim`
142
+
143
+ Cuts a solid with a half-space, keeping what is behind the plane.
144
+
145
+ ```hcl
146
+ trim { normal = [0, 0, 1] offset = 20 tall_thing { } }
147
+ ```
148
+
149
+ #### Attributes
150
+
151
+ | Attribute | Type | Default | Description |
152
+ | --- | --- | --- | --- |
153
+ | `normal` | 3-vector | **required** | The plane's normal. Everything on the normal's side is removed |
154
+ | `offset` | number | `0` | How far the plane sits along the normal from the origin |
155
+
156
+ #### Caveats
157
+
158
+ - 3D only.
159
+ - Much cheaper than an `intersection` with a big box, because it is a plane clip rather than
160
+ a boolean between two solids. Use it for flat cuts.
161
+ - The cut face is capped, so the result stays a closed solid.
162
+
163
+ ---
164
+
165
+ ## Usage
166
+
167
+ ### Growing a footprint
168
+
169
+ `offset` after `project` is the standard way to derive a base, a gasket or a clearance
170
+ outline from a finished part:
171
+
172
+ ```hcl
173
+ extrude {
174
+ height = 3
175
+ offset { amount = 2 project { bracket { } } }
176
+ }
177
+ ```
178
+
179
+ ### Shelling a profile
180
+
181
+ There is no 3D shell, but offsetting the profile inwards gives the same result for anything
182
+ prismatic:
183
+
184
+ ```hcl
185
+ difference {
186
+ extrude { height = 20 rounded_rect { size = [40, 30] radius = 4 center = true } }
187
+ translate {
188
+ offset = [0, 0, 2]
189
+ extrude {
190
+ height = 20
191
+ offset { amount = -2 rounded_rect { size = [40, 30] radius = 4 center = true } }
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### Organic shapes from a hull
198
+
199
+ A hull of a few spheres is the quickest route to a smooth, blobby form — a handle, a fillet
200
+ between two bosses, a rounded enclosure:
201
+
202
+ ```hcl
203
+ component "rounded_enclosure" {
204
+ param size = [60, 40, 20]
205
+ param radius = 6
206
+
207
+ hull {
208
+ for x in [radius, size.x - radius] {
209
+ for y in [radius, size.y - radius] {
210
+ for z in [radius, size.z - radius] {
211
+ translate { offset = [x, y, z] sphere { radius = radius segments = 32 } }
212
+ }
213
+ }
214
+ }
215
+ }
216
+ }
217
+ ```
218
+
219
+ Eight spheres, but one sphere node — content identity means the kernel builds the sphere
220
+ once and the hull sees eight transforms of it.
221
+
222
+ ### Flat cuts
223
+
224
+ `trim` is the cheap way to lop something off:
225
+
226
+ ```hcl
227
+ trim { normal = [0, 0, 1] offset = 15 bottle { } } // keep everything below z = 15
228
+ trim { normal = [-1, 0, 0] bracket { } } // keep everything with x ≥ 0
229
+ ```
230
+
231
+ For a half-model view, that is one operation instead of an intersection with a box.
232
+
233
+ ### Refine, then smooth
234
+
235
+ The two go together — smoothing has nothing to move until the mesh is dense enough:
236
+
237
+ ```hcl
238
+ smooth {
239
+ min_sharp_angle = 35
240
+ refine { edge_length = 0.8 blocky_thing { } }
241
+ }
242
+ ```
243
+
244
+ Watch the triangle count; this is the most expensive pair of operations in the language.
245
+
246
+ ---
247
+
248
+ ## Troubleshooting
249
+
250
+ ### `"offset" takes 2D geometry, got 3D`
251
+
252
+ `offset` works on outlines. To grow a solid, project it, offset the outline and re-extrude —
253
+ or offset the profile before it was extruded in the first place.
254
+
255
+ ### `offset: "amount" must be a finite number`
256
+
257
+ `amount` is required and has no default. A shrink is a negative number, not a missing one.
258
+
259
+ ### My `smooth` did nothing
260
+
261
+ The mesh has no vertices to move. Refine first, and check that `min_sharp_angle` is above the
262
+ angle of the edges you want rounded — the default of `60` leaves every edge on a cube alone.
263
+
264
+ ### `refine` hung the browser
265
+
266
+ `edge_length` is an absolute length, and triangle count grows quadratically as it falls.
267
+ Start an order of magnitude coarser than you think you need.
268
+
269
+ ### My negative offset erased the shape
270
+
271
+ A negative `amount` larger than half the narrowest part of the outline consumes it entirely.
272
+ The result is empty geometry, not an error.
273
+
274
+ ### `hull` swallowed the hole in my part
275
+
276
+ Hulls are convex; every concavity and every hole is filled. Hull the pieces that need it, and
277
+ subtract the holes afterwards:
278
+
279
+ ```hcl
280
+ difference {
281
+ hull { ... }
282
+ the_holes { }
283
+ }
284
+ ```
@@ -0,0 +1,309 @@
1
+ # 2D shapes
2
+
3
+ Seven leaf blocks producing cross-sections. They take attributes and never contain other
4
+ blocks.
5
+
6
+ **2D shapes cannot be rendered on their own.** Give them thickness with
7
+ [`extrude`](conversions.md#extrude) or [`revolve`](conversions.md#revolve) first, or a part
8
+ holding one fails with `part "body" is 2D — extrude or revolve it before rendering`.
9
+
10
+ | Block | Sized by | Centred by default |
11
+ | --- | --- | --- |
12
+ | [`rect`](#rect) | `size` | no |
13
+ | [`rounded_rect`](#rounded_rect) | `size`, `radius` | no |
14
+ | [`circle`](#circle) | `radius` or `diameter` | always |
15
+ | [`ellipse`](#ellipse) | `radii` | always |
16
+ | [`regular_polygon`](#regular_polygon) | across flats, across corners, or radius | always |
17
+ | [`stadium`](#stadium) | `size` | no |
18
+ | [`polygon`](#polygon) | `points` | as drawn |
19
+
20
+ ### A note on `segments`
21
+
22
+ Several shapes take `segments`, the number of straight edges used to approximate a curve.
23
+ `0` — the default — means *let the kernel decide* from its global quality settings, which
24
+ [`setQuality`](../api/kernel.md#setquality) controls. Set it explicitly when a specific facet
25
+ count matters: a hole that must clear a printed bolt, or a preview that must stay cheap.
26
+
27
+ ---
28
+
29
+ ## `rect`
30
+
31
+ An axis-aligned rectangle.
32
+
33
+ ```hcl
34
+ rect { size = [40, 20] }
35
+ rect { size = [40, 20] center = true }
36
+ ```
37
+
38
+ #### Attributes
39
+
40
+ | Attribute | Type | Default | Description |
41
+ | --- | --- | --- | --- |
42
+ | `size` | 2-vector | **required** | Width and height |
43
+ | `center` | bool | `false` | `true` centres it on the origin; otherwise its lower-left corner sits there |
44
+
45
+ ---
46
+
47
+ ## `rounded_rect`
48
+
49
+ A rectangle with true arcs at the corners.
50
+
51
+ ```hcl
52
+ rounded_rect { size = [62, 13] radius = 6.5 center = true }
53
+ ```
54
+
55
+ #### Attributes
56
+
57
+ | Attribute | Type | Default | Description |
58
+ | --- | --- | --- | --- |
59
+ | `size` | 2-vector | **required** | Width and height, over the rounded corners |
60
+ | `radius` | number | `1` | Corner radius. Clamped to half the shorter side, so it cannot invert the shape |
61
+ | `segments` | integer | `0` | Segments per corner arc; `0` lets the kernel choose |
62
+ | `center` | bool | `false` | As for [`rect`](#rect) |
63
+
64
+ #### Caveats
65
+
66
+ - Built by shrinking the rectangle and offsetting it back out, which is what makes each
67
+ corner a true arc rather than a chamfer. A `radius` of `0` or less degenerates to a plain
68
+ `rect`.
69
+ - `size` is the outer size. `rounded_rect { size = [10, 10] radius = 5 }` is a circle of
70
+ diameter 10.
71
+
72
+ ---
73
+
74
+ ## `circle`
75
+
76
+ ```hcl
77
+ circle { radius = 12 }
78
+ circle { diameter = 24 segments = 64 }
79
+ ```
80
+
81
+ #### Attributes
82
+
83
+ | Attribute | Type | Default | Description |
84
+ | --- | --- | --- | --- |
85
+ | `radius` | number | `1` | |
86
+ | `diameter` | number | — | An alternative spelling of the same thing |
87
+ | `segments` | integer | `0` | Segments around the circle; `0` lets the kernel choose |
88
+
89
+ #### Caveats
90
+
91
+ - Give `radius` **or** `diameter`, never both: `circle: give either "radius" or "diameter",
92
+ not both`. Both spellings exist because a drilled hole is specified by diameter and a
93
+ fillet by radius, and converting at the call site is where mistakes happen.
94
+ - Always centred on the origin. There is no `center` attribute.
95
+
96
+ ---
97
+
98
+ ## `ellipse`
99
+
100
+ A circle scaled to two exact radii.
101
+
102
+ ```hcl
103
+ ellipse { radii = [30, 18] }
104
+ ```
105
+
106
+ #### Attributes
107
+
108
+ | Attribute | Type | Default | Description |
109
+ | --- | --- | --- | --- |
110
+ | `radii` | 2-vector | **required** | Semi-axes along X and Y |
111
+ | `segments` | integer | `0` | Segments around the unit circle before scaling |
112
+
113
+ #### Caveats
114
+
115
+ - Implemented as a scaled unit circle, so both radii stay exact rather than being
116
+ approximated by a polygon fitted to an ellipse.
117
+ - Always centred. There is no `center` attribute — passing one is
118
+ `ellipse: unknown attribute "center"`.
119
+
120
+ ---
121
+
122
+ ## `regular_polygon`
123
+
124
+ An *n*-sided polygon, sized the way the part it represents is actually specified.
125
+
126
+ ```hcl
127
+ regular_polygon { sides = 6 width_across_flats = 5.5 } // an M3 nut
128
+ regular_polygon { sides = 8 radius = 20 rotation = 22.5 }
129
+ ```
130
+
131
+ #### Attributes
132
+
133
+ | Attribute | Type | Default | Description |
134
+ | --- | --- | --- | --- |
135
+ | `sides` | integer | **required** | At least 3 |
136
+ | `width_across_flats` | number | — | Distance between opposite flats — how a hex key or a nut is specified |
137
+ | `width_across_corners` | number | — | Distance between opposite corners |
138
+ | `radius` | number | `1` | Centre to corner. Same as half the across-corners width |
139
+ | `diameter` | number | — | Twice the radius |
140
+ | `rotation` | angle | `0` | Where the first corner sits, in degrees |
141
+
142
+ #### Caveats
143
+
144
+ - Give **one** sizing attribute. They are tried in the order above, so
145
+ `width_across_flats` wins if several are present — do not rely on that; pass one.
146
+ - `radius` and `diameter` together are an error, as on [`circle`](#circle).
147
+ - Fewer than 3 sides is an error: `regular_polygon: "sides" must be at least 3`.
148
+ - Always centred on the origin.
149
+ - With `rotation = 0` the first corner is on +X. For a hexagon with **flats** on the X axis,
150
+ use `rotation = 30`.
151
+
152
+ ---
153
+
154
+ ## `stadium`
155
+
156
+ A rectangle fully rounded on its short axis — the shape of a slot.
157
+
158
+ ```hcl
159
+ stadium { size = [40, 8] center = true }
160
+ ```
161
+
162
+ #### Attributes
163
+
164
+ | Attribute | Type | Default | Description |
165
+ | --- | --- | --- | --- |
166
+ | `size` | 2-vector | **required** | Width and height, over the rounded ends |
167
+ | `segments` | integer | `0` | Segments per end arc |
168
+ | `center` | bool | `false` | As for [`rect`](#rect) |
169
+
170
+ #### Caveats
171
+
172
+ - The corner radius is always half the **shorter** side. That is what makes it a stadium
173
+ rather than a [`rounded_rect`](#rounded_rect) that happens to look like one, and it means
174
+ the ends are always semicircular however long it is.
175
+
176
+ ---
177
+
178
+ ## `polygon`
179
+
180
+ An explicit outline, or several contours.
181
+
182
+ ```hcl
183
+ polygon {
184
+ points = [
185
+ [0, 0], [26, 0], [24, 6], [16, 26],
186
+ [14, 48], [18, 66], [22, 74], [0, 74],
187
+ ]
188
+ }
189
+ ```
190
+
191
+ #### Attributes
192
+
193
+ | Attribute | Type | Default | Description |
194
+ | --- | --- | --- | --- |
195
+ | `points` | list | **required** | A list of `[x, y]` points, or a list of such lists for several contours |
196
+ | `fill_rule` | `"EvenOdd"` `"NonZero"` `"Positive"` `"Negative"` | `"NonZero"` | How overlapping contours are resolved |
197
+
198
+ #### Caveats
199
+
200
+ - At least three points: `polygon: "points" must be an array of at least 3 points`.
201
+ - **A nested list is read as several contours.** `[[0,0],[1,0],[1,1]]` is one triangle;
202
+ `[[[0,0],…],[[2,2],…]]` is two contours, which is how a shape with a hole is written
203
+ without a separate `difference`.
204
+ - The outline should not self-intersect. The fill rule decides what happens when it does,
205
+ rather than the shape being rejected.
206
+ - Points are not closed for you — the last point connects back to the first automatically, so
207
+ do not repeat it.
208
+
209
+ ---
210
+
211
+ ## Usage
212
+
213
+ ### Profiles for revolving
214
+
215
+ `polygon` is the workhorse for anything turned on a lathe. The profile is written in the XY
216
+ plane with X as the radius and Y as the height, then revolved:
217
+
218
+ ```hcl
219
+ local profile = [
220
+ [0, 0], [26, 0], [24, 6], [16, 26],
221
+ [14, 48], [18, 66], [22, 74], [0, 74],
222
+ ]
223
+
224
+ model "vase" {
225
+ revolve { segments = 96 polygon { points = profile } }
226
+ }
227
+ ```
228
+
229
+ Every X must be ≥ 0 — a profile that crosses the axis revolves through itself.
230
+
231
+ ### Slots and keyways
232
+
233
+ `stadium` extruded is a slot; subtracted, it is a keyway:
234
+
235
+ ```hcl
236
+ difference {
237
+ extrude { height = 6 rect { size = [60, 20] center = true } }
238
+ translate {
239
+ offset = [0, 0, -1]
240
+ extrude { height = 8 stadium { size = [40, 6] center = true } }
241
+ }
242
+ }
243
+ ```
244
+
245
+ ### Hex sockets sized from the tool
246
+
247
+ `width_across_flats` is the number printed on the key, so the model says what it means:
248
+
249
+ ```hcl
250
+ for i, size in [1.5, 2, 2.5, 3, 4, 5, 6] {
251
+ translate {
252
+ offset = [i * 8, 0, 0]
253
+ extrude { height = 20 regular_polygon { sides = 6 width_across_flats = size + 0.3 } }
254
+ }
255
+ }
256
+ ```
257
+
258
+ The `+ 0.3` is print clearance — the language has no opinion about fits, so the model states
259
+ them.
260
+
261
+ ### Holes without a boolean
262
+
263
+ A second contour inside the first is a hole, which keeps a two-part profile as one shape:
264
+
265
+ ```hcl
266
+ polygon {
267
+ fill_rule = "EvenOdd"
268
+ points = [
269
+ [[0, 0], [40, 0], [40, 30], [0, 30]], // outline
270
+ [[10, 10], [30, 10], [30, 20], [10, 20]], // hole
271
+ ]
272
+ }
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Troubleshooting
278
+
279
+ ### `part "body" is 2D — extrude or revolve it before rendering`
280
+
281
+ A 2D shape reached the renderer. Wrap it:
282
+
283
+ ```hcl
284
+ extrude { height = 4 circle { radius = 10 } }
285
+ ```
286
+
287
+ ### `"circle" is a shape and cannot contain other blocks`
288
+
289
+ A shape is a leaf. Something was nested inside its braces — usually a missing `}` on the
290
+ line before, so the next block was swallowed.
291
+
292
+ ### `circle: give either "radius" or "diameter", not both`
293
+
294
+ Pass one. They mean the same thing and accepting both would let a model disagree with itself.
295
+
296
+ ### `circle: unknown attribute "segements"`
297
+
298
+ A typo. Every block rejects attributes it did not read, precisely so a misspelling fails
299
+ loudly instead of silently rendering the default.
300
+
301
+ ### My rounded rectangle came out as a plain rectangle
302
+
303
+ `radius` defaults to `1`, which is invisible at most sizes, and it is clamped to half the
304
+ shorter side. Check that it is set and that the shape is big enough to show it.
305
+
306
+ ### My hexagon is rotated 30° from what I wanted
307
+
308
+ `rotation = 0` puts a **corner** on +X. For a flat on +X, use `rotation = 30` — or in general
309
+ `180 / sides`.