@mylasertools/kerfscript 0.2.0 → 0.3.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 (46) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/CLA.md +8 -5
  3. package/COMMERCIAL-LICENSE.md +23 -28
  4. package/LANGUAGE.md +194 -60
  5. package/README.md +89 -39
  6. package/THIRD-PARTY-NOTICES.md +68 -4
  7. package/TRADEMARKS.md +16 -10
  8. package/dist/cli.js +773 -297
  9. package/dist/evaluator.d.cts +26 -0
  10. package/dist/evaluator.d.ts +26 -0
  11. package/dist/export/dxf.d.cts +1 -1
  12. package/dist/export/dxf.d.ts +1 -1
  13. package/dist/export/svg.d.cts +2 -0
  14. package/dist/export/svg.d.ts +2 -0
  15. package/dist/font.d.cts +3 -0
  16. package/dist/font.d.ts +3 -0
  17. package/dist/functions.d.cts +21 -2
  18. package/dist/functions.d.ts +21 -2
  19. package/dist/index.cjs +14 -14
  20. package/dist/index.d.cts +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +14 -14
  23. package/dist/lexer.d.cts +4 -1
  24. package/dist/lexer.d.ts +4 -1
  25. package/dist/parser.d.cts +1 -1
  26. package/dist/parser.d.ts +1 -1
  27. package/dist/render.d.cts +3 -2
  28. package/dist/render.d.ts +3 -2
  29. package/dist/values.d.cts +10 -2
  30. package/dist/values.d.ts +10 -2
  31. package/dist/wasm.d.cts +1 -0
  32. package/dist/wasm.d.ts +1 -0
  33. package/docs/cli.md +69 -28
  34. package/docs/compatibility.md +40 -65
  35. package/docs/customizer.md +40 -28
  36. package/docs/desktop.md +96 -0
  37. package/docs/getting-started.md +48 -31
  38. package/docs/javascript.md +108 -51
  39. package/docs/kerf.md +36 -39
  40. package/docs/layers.md +71 -37
  41. package/docs/openscad.md +52 -48
  42. package/docs/parts-and-sheets.md +45 -31
  43. package/docs/versions.md +20 -30
  44. package/docs/webassembly.md +131 -0
  45. package/fonts/OFL.txt +92 -0
  46. package/package.json +10 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,102 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2026-10-30)
4
+
5
+ - `kerfscript.wasm`: a WASI module that runs KerfScript from Python, Rust,
6
+ Go, C# and other languages. It takes a JSON request on standard input and
7
+ writes SVG, DXF, parts and warnings as JSON. `"command": "licenses"`
8
+ returns the licence texts of the module and of the JavaScript engine built
9
+ into it. Build it with `npm run wasm`; see
10
+ [docs/webassembly.md](docs/webassembly.md).
11
+ - A desktop app for Windows, macOS and Linux: the playground with `.kerf`
12
+ files on disk (Open, Save, Save as, double-click to open; Save as adds
13
+ `.kerf` to a name without it and asks before replacing a file of that
14
+ name), exports through a save dialog, `use`/`include` libraries read from
15
+ the design's folder and the folders below it (`.kerf` files only), and an
16
+ About window with the source link and the licences of everything in the
17
+ app. Saving writes the whole file before it replaces the old one, and the
18
+ app asks about unsaved changes before it closes, also on ⌘Q.
19
+ Installers: Windows setup and MSI, a universal macOS dmg, and an AppImage,
20
+ `.deb` and `.rpm` for Linux x64 and arm64; Flathub, winget, Chocolatey and
21
+ AUR manifests. See [docs/desktop.md](docs/desktop.md).
22
+ - Open in LightBurn in the desktop app: sends the cut file (or one sheet) to
23
+ LightBurn 2.2 or later through its REST API. KerfScript asks LightBurn only
24
+ for permission to send files, and talks to it directly, past any proxy
25
+ set on the computer.
26
+ - Chocolatey and MacPorts manifests for the command line.
27
+ - The playground does not load a shared link, draft or file longer than
28
+ 1,000,000 characters, and stops unpacking a compressed link at that size.
29
+ Any code the editor runs fits in a link, whatever its script, and a link
30
+ that cannot be opened says why in the console. Code over the limit is not
31
+ coloured or read for settings, and Copy link refuses it. A link to an
32
+ empty design opens it, instead of the draft.
33
+ - Settings for a list, such as `size = [10, 20]`, keep numbers changed in the
34
+ code since the settings were drawn, instead of putting the old ones back.
35
+ An empty or half-typed number box leaves the code as it is.
36
+ - The playground renders the newest code after fast edits to a slow design,
37
+ instead of stopping with "Stopped after 15 seconds", and keeps working
38
+ when the number of sheets drops to one in the laser file view. Edits after
39
+ opening a shared link or moving a slider on an example are kept on reload.
40
+ - The website's licence page, and `assets/THIRD-PARTY-NOTICES.md` next to
41
+ `kerfscript.js`, have the notices of the libraries bundled in its
42
+ JavaScript.
43
+ - Standalone `kerf` binaries for Windows, macOS and Linux that need no
44
+ Node.js, with the licences of the Deno runtime built into them,
45
+ `kerfscript-cli` `.deb` and `.rpm` packages (which install next to the
46
+ desktop app), a Docker image, a snap, and Homebrew,
47
+ Scoop, winget and AUR manifests, all built by the release workflow with
48
+ SHA-256, SHA-512 and GPG signatures. Nightly builds go to a rolling
49
+ `nightly` prerelease. See RELEASING.md.
50
+ - A `kerf(1)` man page and a `text/x-kerfscript` MIME type for `*.kerf` files
51
+ in the Linux packages.
52
+ - The command line and `kerfscript.wasm` have a font built in, Atkinson
53
+ Hyperlegible as `Atkinson` (the playground's font), so `text()` works
54
+ without `--font`. The JavaScript library still takes fonts from the host.
55
+ - Memory limits, so a program cannot make a browser tab run out of memory:
56
+ part copies, circle segments and polygon corners, list and string sizes,
57
+ all list items together, `text()` length, shape points, exported points
58
+ and sheets. Each stops the program with an error that says what to change.
59
+ Defaults are in `DEFAULT_LIMITS` and the `limits` render option changes
60
+ them. Every example stays well under them. Ranges in loops and built-in
61
+ functions stop at `maxListLength` items, not at a fixed million.
62
+ - A list or string that grows past what JavaScript can hold now gives a
63
+ KerfScript error instead of a `RangeError`.
64
+ - Lists are checked while they are built, so `each` in a long loop, list
65
+ arithmetic and matrix products stop at the limit instead of first filling
66
+ memory. Strings joined with `+`, strings from functions and `echo()` text
67
+ count toward `maxListItems` too, and a repeated unknown argument warning
68
+ is shown once.
69
+ - `echo()`, `str()`, `join()` and `==` on lists that hold the same list many
70
+ times stop at the limits instead of running out of memory: text stops at
71
+ `maxListLength` characters while it is written, and comparing lists does
72
+ not compare the same pair of lists twice. Error messages, part names and
73
+ other settings show only the start of a long list.
74
+ - Comparing lists, dot products and built-in functions that go through a list
75
+ or string, such as `search()`, `contains()`, `max()` and `sum()`, take a
76
+ step for each item, so a loop over a long list stops at `maxSteps` instead
77
+ of running for minutes.
78
+ - `hull()` around named parts makes the hull of each part, the same as
79
+ `hull()` inside the part. It left the parts unchanged before.
80
+ - DXF layers whose names come out the same in DXF, such as `a b` and `a_b`,
81
+ stay separate layers: the later ones get `_2`, `_3` and so on. The
82
+ built-in layers keep `CUT`, `SCORE`, `ENGRAVE` and `GUIDE`.
83
+ - Built-in functions take named arguments, such as `sin(x = 30)` and
84
+ `pow(x = 2, y = 10)`; an unknown name gives a warning, and so does an
85
+ argument given both by position and by name. They were ignored before.
86
+ - Errors and warnings in a library name it, as in
87
+ `(line 3, column 5 in <lib/tabs>)`. The playground marks the line only for
88
+ errors in the program itself.
89
+ - `toString()`, `constructor()` and other names every JavaScript object has
90
+ are unknown functions and libraries, instead of a crash or a wrong value.
91
+ - `text_width()` and `text_metrics()` take a step per character, have the
92
+ same length limit as `text()`, and are much faster for long text.
93
+ - A list times a matrix of the wrong size, and `params` values that are not
94
+ numbers, strings, booleans or lists, are errors instead of a crash.
95
+ - A list times a list whose items multiply to lists is an error instead of
96
+ text.
97
+ - The commercial licence for closed-source apps and services is free: email
98
+ hi@mylasertools.com.
99
+
3
100
  ## 0.2.0 (2026-10-28)
4
101
 
5
102
  First public release.
package/CLA.md CHANGED
@@ -7,9 +7,12 @@ Thank you for contributing to KerfScript. This agreement lets MyLaserTools
7
7
  commercial licences, including licences we may sell. You keep the copyright in
8
8
  your contribution.
9
9
 
10
- By submitting a Contribution (a pull request, patch, issue attachment or any
11
- other material you intentionally send us for inclusion in KerfScript) you
12
- ("You") agree to the following for that Contribution and every later one.
10
+ This agreement applies to a Contribution when you add the signature
11
+ statement below to the pull request that contains it. A Contribution is
12
+ material you intentionally submit in that signed pull request for inclusion
13
+ in KerfScript. Issue reports, ideas, unsigned pull requests and other
14
+ communications do not grant rights under this agreement. Sign each pull
15
+ request you want us to consider for inclusion.
13
16
 
14
17
  ## 1. Copyright licence
15
18
 
@@ -62,12 +65,12 @@ trademarks.
62
65
 
63
66
  ## How to sign
64
67
 
65
- Add this line to your first pull request description, using your legal name:
68
+ Add this statement to each pull request description, using your legal name:
66
69
 
67
70
  ```text
68
71
  I have read the KerfScript CLA 1.0 and agree to it for my contributions.
69
72
  Name: <your name> GitHub: <your handle> Date: <YYYY-MM-DD>
70
73
  ```
71
74
 
72
- If You contribute on behalf of a company, contact julia@mylasertools.com for
75
+ If You contribute on behalf of a company, contact hi@mylasertools.com for
73
76
  the company version first.
@@ -1,35 +1,24 @@
1
1
  # Licensing
2
2
 
3
- Copyright (c) 2026 MyLaserTools. All rights reserved except as granted below.
3
+ Copyright (c) 2026 MyLaserTools.
4
+
5
+ **KerfScript is free to use, including for commercial work.** Write programs,
6
+ cut and sell what you make, and sell your `.kerf` designs and the files they
7
+ produce. You don't need to ask us for any of that.
4
8
 
5
9
  KerfScript is dual licensed. You can use it under **either**:
6
10
 
7
11
  1. **The GNU Affero General Public License, version 3 only** (`AGPL-3.0-only`,
8
12
  full text in [LICENSE](LICENSE)), together with the additional permission
9
13
  below, or
10
- 2. **A commercial licence** from MyLaserTools.
11
-
12
- ## What the AGPL means for you
14
+ 2. **A commercial licence** from MyLaserTools, which is **free**.
13
15
 
14
- Free, under the AGPL, when you:
15
-
16
- - write KerfScript programs and cut, sell or share what you make with them
17
- - use the `kerf` command line tool, or the library inside software you only run
18
- for yourself
19
- - build open source software on KerfScript and publish its complete source code
20
- under the AGPL
16
+ ## Free commercial licence
21
17
 
22
- You need a commercial licence when you:
23
-
24
- - ship KerfScript inside a closed source app, plugin, machine or firmware
25
- - run KerfScript (or a modified copy) behind a website or online service and do
26
- not want to publish that service's complete source code under the AGPL
27
- - want to change KerfScript without sharing your changes
28
-
29
- The AGPL treats letting people use the software over a network like
30
- distributing it: if you modify KerfScript or build it into a service that
31
- others use, you must offer those users the complete corresponding source code
32
- of the whole program under the AGPL.
18
+ Want to build KerfScript into a closed-source app, plugin, machine, firmware
19
+ or online service? The commercial licence costs nothing. Just email
20
+ hi@mylasertools.com with your name or company and what you are building, and
21
+ we will send it to you.
33
22
 
34
23
  ## Additional permission: your designs are yours
35
24
 
@@ -45,15 +34,21 @@ that:
45
34
 
46
35
  This permission covers your programs and their output only. It does not cover
47
36
  the KerfScript interpreter, its tools, or modified copies of the standard
48
- library, which remain under the AGPL.
37
+ library, which remain under the AGPL unless you have the commercial licence.
49
38
 
50
- ## Commercial licences
39
+ ## What the AGPL means for you
51
40
 
52
- A commercial licence replaces the AGPL obligations with ordinary commercial
53
- terms: closed source use, use in hosted services, private modifications and
54
- support options. Terms and pricing are set per agreement.
41
+ Under the AGPL you can:
55
42
 
56
- Contact: julia@mylasertools.com
43
+ - write KerfScript programs and cut, sell or share what you make with them
44
+ - run the interpreter, including a modified copy, privately or in internal
45
+ tools without publishing those private changes
46
+ - use an unmodified copy on a server
47
+ - distribute software that includes KerfScript, as long as you share its
48
+ source code under the AGPL
49
+
50
+ If you would rather not share your source code, ask for the free commercial
51
+ licence.
57
52
 
58
53
  ## Trademarks
59
54
 
package/LANGUAGE.md CHANGED
@@ -1,15 +1,18 @@
1
1
  # KerfScript language reference
2
2
 
3
3
  Language version 0.1. Copyright (c) 2026 MyLaserTools. This document is
4
- licensed under [CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0/).
4
+ licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
5
5
  "KerfScript" is a trademark of MyLaserTools, see [TRADEMARKS.md](TRADEMARKS.md).
6
6
 
7
- KerfScript describes flat parts for laser cutting. A program is a list of
8
- statements that build 2D shapes, put them on **layers** (cut, score, engrave),
9
- group them into **parts**, and the renderer compensates for the **kerf**, packs
10
- parts onto **sheets** and writes SVG or DXF.
7
+ This reference covers KerfScript syntax, built-in modules and functions.
8
+ For a first project, follow [Getting started](docs/getting-started.md).
11
9
 
12
- Units are millimetres and degrees. The y axis points up.
10
+ Programs build 2D shapes, assign them to cut, score or engrave layers, and
11
+ group them into parts. Rendering applies kerf compensation, arranges the
12
+ parts on sheets and produces SVG or DXF files.
13
+
14
+ Lengths are in millimetres and angles are in degrees. Positive x points
15
+ right; positive y points up.
13
16
 
14
17
  ```kerf
15
18
  // A coaster with a hole, an engraved ring and a scored border.
@@ -49,7 +52,8 @@ Points are lists `[x, y]`. `p.x` and `p.y` read the first two items.
49
52
 
50
53
  ## Expressions
51
54
 
52
- From lowest to highest precedence:
55
+ Operators are listed from lowest to highest precedence. Use parentheses
56
+ to change the order of evaluation.
53
57
 
54
58
  | Operator | Meaning |
55
59
  | ------------------- | -------------------------------------------------------------------------------------------------------------- |
@@ -64,14 +68,19 @@ From lowest to highest precedence:
64
68
  | `a ^ b` | power, right associative and tighter than unary minus |
65
69
  | `f(x)` `a[i]` `a.x` | call, index, member |
66
70
 
67
- Mixing types an operator can't handle (a list plus a number, `undef + 1`)
68
- stops the program with an error naming the line. Truthiness: `false`, `0`,
69
- `""`, `[]` and `undef` are false.
71
+ Unsupported combinations, such as a list plus a number or `undef + 1`,
72
+ produce an error at the source line.
73
+
74
+ In conditions, `false`, `0`, `""`, `[]` and `undef` count as false.
70
75
 
71
- `let (a = 1, b = a + 1) expr` binds names for one expression.
76
+ Use `let (a = 1, b = a + 1) expr` to define names for one expression.
77
+ Each binding can use the ones before it.
72
78
 
73
79
  ### List comprehensions
74
80
 
81
+ Build a list with `for`, filter items with `if`, or insert the contents of
82
+ another list with `each`:
83
+
75
84
  ```kerf
76
85
  squares = [for (i = [1:5]) i * i];
77
86
  odd = [for (i = [1:9]) if (i % 2) i];
@@ -81,6 +90,10 @@ flat = [each [1, 2], 3];
81
90
 
82
91
  ## Statements
83
92
 
93
+ Assignments and function definitions end with a semicolon. Modules create
94
+ geometry and can receive child statements. In the syntax below, `...`
95
+ stands for the body of a statement:
96
+
84
97
  ```kerf
85
98
  name = expr; // assignment
86
99
  function name(a, b = 2) = expr; // function
@@ -111,13 +124,15 @@ Arguments can be positional or named: `rect([20, 10], r = 2)`.
111
124
 
112
125
  ### Modifiers
113
126
 
114
- Put before a module call: `*` disables it, `#` and `%` move it to the guide
115
- layer so you can see it without cutting it.
127
+ Place `*` before a module call to disable it. Use `#` or `%` to move its
128
+ geometry to the guide layer. Guides are included only when requested in
129
+ the render options.
116
130
 
117
131
  ### children()
118
132
 
119
- Inside a module, `children()` renders the statements the module was called
120
- with, `children(i)` renders only the i-th one, and `$children` is their count.
133
+ Inside a module, `children()` renders all child statements passed by the
134
+ caller. `children(i)` selects one child, using a zero-based index.
135
+ `$children` is the number of children.
121
136
 
122
137
  ```kerf
123
138
  module hang_hole(d = 5) {
@@ -142,7 +157,7 @@ module hang_hole(d = 5) {
142
157
 
143
158
  Every shape is on a layer. Shapes not wrapped in a layer module are on `cut`.
144
159
 
145
- | Layer | Mode | Colour | What happens |
160
+ | Layer | Mode | Colour | Intended operation |
146
161
  | --------- | ----- | --------- | ---------------------------------------------------- |
147
162
  | `engrave` | fill | `#000000` | the area is engraved |
148
163
  | `score` | line | `#0000ff` | lines and shape outlines are marked, not cut through |
@@ -154,11 +169,14 @@ cut() ... engrave() ... score() ... guide() ...
154
169
  layer("pocket", mode = "fill", color = "#00aa00") ...
155
170
  ```
156
171
 
157
- `layer(name, mode, color)` defines a layer. Modes are `"cut"`, `"line"`,
158
- `"fill"` and `"guide"`. Output lists layers as engrave, custom layers, score,
159
- cut, guide, so machines engrave before cutting.
172
+ Use `layer(name, mode, color)` to define a custom layer. Its mode is
173
+ `"cut"`, `"line"`, `"fill"` or `"guide"`.
160
174
 
161
- Rules applied when a part is finished:
175
+ Files list layers in this order: engrave, custom layers, score, cut, guide.
176
+ Set the actual operation order, power and speed in your laser software.
177
+ See [Laser software](docs/compatibility.md) for import details.
178
+
179
+ When a part is rendered:
162
180
 
163
181
  - The part's **material** is the union of its cut layer shapes.
164
182
  - Engrave, score and custom layers are clipped to the material, so nothing is
@@ -170,6 +188,9 @@ Rules applied when a part is finished:
170
188
 
171
189
  ## Parts and sheets
172
190
 
191
+ Use `part()` to name a component and set its quantity. Wrap parts in
192
+ `material()` when they share a thickness, kerf and material name:
193
+
173
194
  ```kerf
174
195
  material(thickness = 4, kerf = 0.12, name = "birch") {
175
196
  part("side", qty = 2) finger_rect([100, 60], "out");
@@ -182,9 +203,12 @@ material(thickness = 4, kerf = 0.12, name = "birch") {
182
203
  - Shapes outside any part form one part named `part`.
183
204
  - `material(thickness, kerf, name) { ... }` sets those special variables for
184
205
  its children.
185
- - With `$layout = "pack"` parts are grouped by material and thickness, copied
186
- `qty` times, rotated 90° if that makes them fit, and shelf-packed onto
187
- sheets of `$sheet`. Parts larger than the sheet are reported.
206
+ - With `$layout = "pack"`, parts are grouped by material and thickness and
207
+ repeated `qty` times. They are arranged in rows on sheets of `$sheet` and
208
+ may be rotated 90° to fit. Parts larger than the sheet produce a warning.
209
+
210
+ See [Parts and sheets](docs/parts-and-sheets.md) for spacing, layout and
211
+ export examples.
188
212
 
189
213
  ## Built-in modules
190
214
 
@@ -204,11 +228,22 @@ material(thickness = 4, kerf = 0.12, name = "birch") {
204
228
  | `polyline(points, closed = false)` | |
205
229
  | `arc(r, start = 0, end = 90)` | arc line, angles in degrees |
206
230
 
231
+ Supply geometry as children of a shape-processing module, for example
232
+ `translate([10, 0]) circle(d = 5);`. Fonts used by `text()` must be supplied
233
+ by the application or through the [command line](docs/cli.md). The
234
+ playground, the command line and `kerfscript.wasm` have `Atkinson`
235
+ (Atkinson Hyperlegible) built in.
236
+
207
237
  ### Transforms
208
238
 
209
- `translate([x, y])`, `rotate(a, center = [x, y])`, `scale(s)` or
210
- `scale([sx, sy])`, `mirror([nx, ny])` (mirror across the line through the
211
- origin with that normal), `multmatrix(m)` with a 2x3 matrix.
239
+ | Module | Effect |
240
+ | ---------------------------- | ---------------------------------------------------------------------- |
241
+ | `translate([x, y])` | Move geometry by x and y |
242
+ | `rotate(a, center = [x, y])` | Rotate by `a` degrees around a point; the default centre is the origin |
243
+ | `scale(v)` | Scale both axes by the number `v` |
244
+ | `scale([sx, sy])` | Scale each axis separately |
245
+ | `mirror([nx, ny])` | Reflect across a line through the origin, perpendicular to this vector |
246
+ | `multmatrix(m)` | Apply a 2 × 3 transformation matrix |
212
247
 
213
248
  ### Combining
214
249
 
@@ -217,7 +252,7 @@ origin with that normal), `multmatrix(m)` with a 2x3 matrix.
217
252
  | `union()` / `group()` | joins children |
218
253
  | `difference()` | removes every later child's area from every layer of the first child |
219
254
  | `intersection()` | keeps only the area common to all children, layers of the first child kept |
220
- | `hull()` | convex hull |
255
+ | `hull()` | convex hull; each named part inside gets its own hull |
221
256
  | `offset(r)` / `offset(delta, chamfer = false)` | grow (positive) or shrink areas; `r` rounds corners |
222
257
  | `stroke(width)` | turns lines into areas of that width, for cutting single line art |
223
258
  | `outline()` | turns areas into their outline lines |
@@ -233,48 +268,115 @@ origin with that normal), `multmatrix(m)` with a 2x3 matrix.
233
268
 
234
269
  ### Other
235
270
 
236
- `echo(...)` prints values, `assert(cond, message)` stops with the message when
237
- `cond` is false, `children(i)`.
271
+ | Module | Effect |
272
+ | ---------------------------- | -------------------------------------------------------------------------------- |
273
+ | `echo(...)` | Report values without creating geometry |
274
+ | `assert(condition, message)` | Stop with `message` when `condition` is false |
275
+ | `children(index)` | Render a child passed to the current module; omit `index` to render all children |
238
276
 
239
277
  ## Built-in functions
240
278
 
241
- Math (degrees): `abs sign floor ceil round sqrt pow exp ln log sin cos tan
242
- asin acos atan atan2 min max clamp lerp norm cross`.
243
-
244
- Lists and strings: `len concat reverse sum slice str chr ord upper lower split
245
- join search contains lookup`.
246
-
247
- Types: `is_num is_string is_bool is_list is_undef is_function type_of`.
248
-
249
- Random: `rands(min, max, count, seed)` is repeatable for the same seed.
250
-
251
- Text: `text_width(text, size, font)` returns the advance width;
252
- `text_metrics(text, size, font)` returns `[advance, [[x0, y0], [x1, y1]], ascent, descent]`.
253
-
254
- Joinery: `finger_count(length, finger)` returns the finger count
255
- `finger_rect` uses for that edge.
279
+ Functions return values. Arguments can be positional or named with the
280
+ parameter names below, as in `pow(x = 2, y = 10)` or `log(8, base = 2)`. An
281
+ unknown name gives a warning. `concat`, `str` and `chr` take only positional
282
+ arguments.
283
+
284
+ ### Numbers and vectors
285
+
286
+ | Function | Result |
287
+ | ---------------------------------------------- | -------------------------------------------------------------------- |
288
+ | `abs(x)`, `sign(x)` | Absolute value, or sign as -1, 0 or 1 |
289
+ | `floor(x)`, `ceil(x)`, `round(x)` | Round down, up or to the nearest integer |
290
+ | `sqrt(x)`, `pow(x, y)` | Square root, or x raised to y |
291
+ | `exp(x)`, `ln(x)` | Exponential with base e, or natural logarithm |
292
+ | `log(x)`, `log(base, x)` | Base-10 logarithm, or logarithm with a chosen base |
293
+ | `sin(x)`, `cos(x)`, `tan(x)` | Trigonometric functions with angles in degrees |
294
+ | `asin(x)`, `acos(x)`, `atan(x)`, `atan2(y, x)` | Inverse trigonometric functions, returning degrees |
295
+ | `min(values)`, `max(values)` | Minimum or maximum of a list; also accept separate values |
296
+ | `clamp(x, low, high)` | Limit x to the given interval |
297
+ | `lerp(a, b, t)` | Interpolate between numbers or vectors; t = 0 gives a, t = 1 gives b |
298
+ | `norm(v)` | Length of a vector |
299
+ | `cross(a, b)` | Cross product; a scalar for 2D vectors or a vector for 3D vectors |
300
+
301
+ ### Lists and strings
302
+
303
+ | Function | Result |
304
+ | ------------------------------ | -------------------------------------------------------------------------------------------- |
305
+ | `len(value)` | Number of items in a list or characters in a string |
306
+ | `concat(...)` | Join lists and individual values into one list |
307
+ | `reverse(value)` | Reverse a list or string |
308
+ | `sum(values)` | Sum a list of numbers |
309
+ | `slice(value, start = 0, end)` | Extract part of a list or string; `end` is exclusive and defaults to its length |
310
+ | `str(...)` | Convert values to text and join them |
311
+ | `chr(...)`, `ord(text)` | Convert Unicode code points to text, or read the first character's code point |
312
+ | `upper(text)`, `lower(text)` | Change letter case |
313
+ | `split(text, separator = " ")` | Split text into a list of strings |
314
+ | `join(values, separator = "")` | Join values into a string |
315
+ | `search(value, list)` | Index of the first equal item, or `undef` if absent; also searches characters in strings |
316
+ | `contains(value, item)` | Whether a list contains an item or a string contains a substring |
317
+ | `lookup(x, table)` | Interpolate in a table of `[x, y]` pairs; use the nearest endpoint outside the table's range |
318
+
319
+ ### Types
320
+
321
+ `is_num(value)`, `is_string(value)`, `is_bool(value)`, `is_list(value)`,
322
+ `is_undef(value)` and `is_function(value)` return a boolean.
323
+ `type_of(value)` returns the type name as a string.
324
+
325
+ ### Random values
326
+
327
+ `rands(min, max, count, seed = 1)` returns a list of numbers between the
328
+ bounds. The same seed gives the same sequence.
329
+
330
+ ### Text measurements
331
+
332
+ `text_width(text, size, font)` returns the advance width, including glyph
333
+ spacing. `text_metrics(text, size, font)` returns
334
+ `[advance, [[x0, y0], [x1, y1]], ascent, descent]`.
335
+
336
+ Both require the font to be available to the renderer, just like `text()`.
337
+
338
+ ### Joint dimensions
339
+
340
+ `finger_count(length, finger)` returns the odd finger count used by
341
+ `finger_rect()` for an edge of that length.
256
342
 
257
343
  ## Customizer comments
258
344
 
259
- Top-level assignments with a plain value (number, string, boolean or list of
260
- numbers) are listed as parameters by `kerf params` and by editors:
345
+ Top-level assignments with a literal number, string, boolean or numeric
346
+ list can appear as editable settings. Comments supply captions, ranges,
347
+ choices and groups:
261
348
 
262
349
  ```kerf
263
- /* [Size] */ // group for the following parameters
264
- // Outer width <- caption: the comment line above
265
- width = 120; // [40:5:400] min:step:max
266
- depth = 80; // [200] max
267
- shape = "round"; // [round:Round, square:Square] choices with labels
268
- count = 3; // [2, 3, 4] choices
269
- /* [Hidden] */ // not listed
350
+ /* [Size] */
351
+ // Outer width
352
+ width = 120; // [40:5:400]
353
+ depth = 80; // [200]
354
+ shape = "round"; // [round:Round, square:Square]
355
+ count = 3; // [2, 3, 4]
356
+ /* [Hidden] */
357
+ seed = 7;
270
358
  ```
271
359
 
272
- The format matches OpenSCAD customizer comments, so existing editors can show
273
- KerfScript parameters.
360
+ - `/* [Size] */` starts a group; the settings below it are listed under
361
+ _Size_.
362
+ - `// Outer width`, the comment line just above an assignment, is its caption.
363
+ - `[40:5:400]` is a slider from 40 to 400 in steps of 5; `[200]` is a slider
364
+ up to 200.
365
+ - `[round:Round, square:Square]` is a menu of values with labels; `[2, 3, 4]`
366
+ is a menu of values.
367
+ - Settings after `/* [Hidden] */` are not listed.
368
+
369
+ Nothing else may follow the brackets on the same line: with
370
+ `// [40:5:400] min:step:max` the setting is a plain number field, not a
371
+ slider.
372
+
373
+ These comments follow OpenSCAD's customizer format. Read
374
+ [Settings](docs/customizer.md) for supported controls, or use `kerf params`
375
+ to inspect the settings in a file.
274
376
 
275
377
  ## Standard library
276
378
 
277
- `use <kerf/box>`:
379
+ Load the box modules with `use <kerf/box>`:
278
380
 
279
381
  - `box([w, d, h], lid = false, finger)` makes the six (or five) panels of a
280
382
  finger-jointed box as parts `bottom`, `lid`, `front`, `back`, `left`,
@@ -286,10 +388,42 @@ KerfScript parameters.
286
388
  - **SVG**: millimetre units, one Inkscape layer group per KerfScript layer,
287
389
  colours as above, 0.1 mm hairline strokes, `data-part` on every path.
288
390
  - **DXF**: R12 (AC1009) polylines on layers named after KerfScript layers,
289
- with matching AutoCAD colours.
391
+ with matching AutoCAD colours. Names are upper case with characters other
392
+ than letters, digits, `_` and `-` as `_`; when two layers come out the
393
+ same, the later one gets `_2`, `_3` and so on. The built-in layers keep
394
+ `CUT`, `SCORE`, `ENGRAVE` and `GUIDE`, so a layer of your own named `Cut`
395
+ becomes `CUT_2`.
290
396
 
291
397
  ## Limits
292
398
 
293
- Renderers stop programs that recurse deeper than about a thousand calls, loop
294
- over more than a million items, or run more than five million steps, with an
295
- error instead of hanging.
399
+ The interpreter limits call depth to 1,000 for modules and 2,000 for
400
+ functions. A range a loop or built-in function goes through is limited to
401
+ `maxListLength` items (below). The default
402
+ evaluation limit is 5,000,000 steps; applications can change it with
403
+ `maxSteps`. Comparing lists with `==`, multiplying two lists, and built-in
404
+ functions that go through a list or string take a step for each item;
405
+ `len()` of a list, `ord()`, `type_of()` and the `is_` functions take one.
406
+ Exceeding a limit stops evaluation with an error.
407
+
408
+ Programs are also limited in how much memory they can ask for:
409
+
410
+ | Limit | Default | What it counts |
411
+ | ----------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
412
+ | `maxParts` | 10,000 | copies of all parts, adding up every `qty` |
413
+ | `maxSegments` | 10,000 | segments in one circle, ellipse or arc, and corners in one polygon |
414
+ | `maxListLength` | 1,000,000 | items in one list or range, or characters in one string, including the text `echo()` writes in one call |
415
+ | `maxListItems` | 3,000,000 | items in all lists the program builds, plus 4 for each list, one for every 8 characters of the strings it builds or echoes |
416
+ | `maxTextLength` | 2,000 | characters in one `text()` |
417
+ | `maxPoints` | 2,000,000 | points in all shapes the modules make, plus 100 for each shape |
418
+ | `maxOutputPoints` | 1,000,000 | points in the exported files, counting every copy |
419
+ | `maxSheets` | 200 | sheets in the layout |
420
+
421
+ Applications can change them with the `limits` render option.
422
+
423
+ A list can hold the same list many times, as in `b = [a, a]`. `echo()`,
424
+ `str()` and `join()` write every copy out, so their text can be far longer
425
+ than the list; they stop with an error once it passes `maxListLength`.
426
+
427
+ Geometry operations also take time. Applications that render user programs
428
+ should run them in a worker with a timeout; see the
429
+ [JavaScript API](docs/javascript.md#running-untrusted-code).