@mylasertools/kerfscript 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/CLA.md +76 -0
  3. package/COMMERCIAL-LICENSE.md +63 -0
  4. package/LANGUAGE.md +398 -0
  5. package/LICENSE +661 -0
  6. package/README.md +187 -2
  7. package/THIRD-PARTY-NOTICES.md +124 -0
  8. package/TRADEMARKS.md +45 -0
  9. package/dist/ast.d.cts +160 -0
  10. package/dist/ast.d.ts +160 -0
  11. package/dist/cli.d.cts +1 -0
  12. package/dist/cli.d.ts +1 -0
  13. package/dist/cli.js +19416 -0
  14. package/dist/customizer.d.cts +19 -0
  15. package/dist/customizer.d.ts +19 -0
  16. package/dist/evaluator.d.cts +129 -0
  17. package/dist/evaluator.d.ts +129 -0
  18. package/dist/export/dxf.d.cts +3 -0
  19. package/dist/export/dxf.d.ts +3 -0
  20. package/dist/export/svg.d.cts +15 -0
  21. package/dist/export/svg.d.ts +15 -0
  22. package/dist/functions.d.cts +4 -0
  23. package/dist/functions.d.ts +4 -0
  24. package/dist/geometry.d.cts +44 -0
  25. package/dist/geometry.d.ts +44 -0
  26. package/dist/index.cjs +44 -0
  27. package/dist/index.d.cts +10 -0
  28. package/dist/index.d.ts +10 -0
  29. package/dist/index.js +44 -0
  30. package/dist/joinery.d.cts +7 -0
  31. package/dist/joinery.d.ts +7 -0
  32. package/dist/lexer.d.cts +28 -0
  33. package/dist/lexer.d.ts +28 -0
  34. package/dist/parser.d.cts +6 -0
  35. package/dist/parser.d.ts +6 -0
  36. package/dist/paths.d.cts +28 -0
  37. package/dist/paths.d.ts +28 -0
  38. package/dist/render.d.cts +58 -0
  39. package/dist/render.d.ts +58 -0
  40. package/dist/stdlib.d.cts +1 -0
  41. package/dist/stdlib.d.ts +1 -0
  42. package/dist/text.d.cts +36 -0
  43. package/dist/text.d.ts +36 -0
  44. package/dist/values.d.cts +25 -0
  45. package/dist/values.d.ts +25 -0
  46. package/docs/cli.md +76 -0
  47. package/docs/compatibility.md +64 -0
  48. package/docs/customizer.md +74 -0
  49. package/docs/getting-started.md +115 -0
  50. package/docs/javascript.md +153 -0
  51. package/docs/kerf.md +67 -0
  52. package/docs/layers.md +108 -0
  53. package/docs/openscad.md +71 -0
  54. package/docs/parts-and-sheets.md +82 -0
  55. package/docs/versions.md +33 -0
  56. package/package.json +86 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ ## 0.2.1
4
+
5
+ - Memory limits, so a program cannot make a browser tab run out of memory:
6
+ part copies, circle segments and polygon corners, list and string sizes,
7
+ all list items together, `text()` length, shape points, exported points
8
+ and sheets. Each stops the program with an error that says what to change.
9
+ Defaults are in `DEFAULT_LIMITS` and the `limits` render option changes
10
+ them. Every example stays well under them.
11
+ - A list or string that grows past what JavaScript can hold now gives a
12
+ KerfScript error instead of a `RangeError`.
13
+ - Lists are checked while they are built, so `each` in a long loop, list
14
+ arithmetic and matrix products stop at the limit instead of first filling
15
+ memory. Strings joined with `+`, strings from functions and `echo()` text
16
+ count toward `maxListItems` too, and a repeated unknown argument warning
17
+ is shown once.
18
+ - The commercial licence for closed-source apps and services is free: email
19
+ hi@mylasertools.com.
20
+
21
+ ## 0.2.0 (2026-10-28)
22
+
23
+ First public release.
24
+
25
+ - `render()` returns `paths` (path data per layer), `width` and `height`, and
26
+ every sheet has `paths`, so host apps can draw layers without parsing SVG.
27
+ Each layer path has `d` (everything), `regions` (closed outlines, filled
28
+ even-odd on fill layers) and `lines` (open lines, always stroked).
29
+ - `margin` render option and `--margin` flag: space between the parts and
30
+ the sheet edge, separate from `gap` between parts (defaults to `gap`).
31
+ - Errors from a missing font carry `missingFonts`, so a host can load the
32
+ fonts and render again.
33
+ - `parameters()` gives list settings `"type": "number"` with a list
34
+ `initial`, the same as OpenSCAD's parameter JSON (was `"vector"`).
35
+ - Every setting has `span`, the source offsets of its value, so an editor
36
+ can change the value without touching anything else.
37
+ - A setting's caption comes only from a comment on its own line above it,
38
+ not from the end-of-line comment of the setting before.
39
+ - `text_width()` and `text_metrics()` errors report their line and column.
40
+ - A warning when kerf compensation closes a hole narrower than the kerf.
41
+ - `kerf check` lists every sheet with its material, thickness and pieces;
42
+ `kerf render` warns when several sheets are stacked in one SVG, and
43
+ `kerf --version` shows the language version too.
44
+ - Guides in `docs/`, calibration square and kerf test examples, and
45
+ `npm run compat`, which checks SVG and DXF output in Inkscape.
46
+
47
+ ## 0.1.0 (not published)
48
+
49
+ First version of the KerfScript language (language version 0.1).
50
+
51
+ - Language: numbers, strings, lists, ranges, functions and closures, list
52
+ comprehensions, modules with children, dynamic `$` variables, `use` and
53
+ `include`, customizer comments.
54
+ - Shapes: rect, circle, ellipse, regular_polygon, star, polygon, SVG path,
55
+ text, line, polyline, arc.
56
+ - Booleans, hull, offset, stroke, outline and transforms.
57
+ - Laser layers (cut, score, engrave, guide, custom), parts, materials, kerf
58
+ compensation, inner cuts first, sheet packing.
59
+ - Joinery: finger_rect, finger_holes, tslot, living_hinge; `kerf/box`.
60
+ - SVG and DXF output, `kerf` command line tool.
package/CLA.md ADDED
@@ -0,0 +1,76 @@
1
+ # KerfScript Contributor License Agreement
2
+
3
+ Version 1.0
4
+
5
+ Thank you for contributing to KerfScript. This agreement lets MyLaserTools
6
+ ("we", "us") keep offering KerfScript as open source under the AGPL and under
7
+ commercial licences, including licences we may sell. You keep the copyright in
8
+ your contribution.
9
+
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.
16
+
17
+ ## 1. Copyright licence
18
+
19
+ You grant us and recipients of software we distribute a perpetual, worldwide,
20
+ non-exclusive, no-charge, royalty-free, irrevocable licence to reproduce,
21
+ prepare derivative works of, publicly display, publicly perform, sublicense
22
+ and distribute Your Contributions and derivative works.
23
+
24
+ This licence includes the right for us to license Your Contribution, alone or
25
+ as part of KerfScript, under **any terms we choose**, including open source
26
+ licences other than the AGPL and proprietary or commercial licences, and to
27
+ sell such licences.
28
+
29
+ ## 2. Patent licence
30
+
31
+ You grant us and recipients of software we distribute a perpetual, worldwide,
32
+ non-exclusive, no-charge, royalty-free, irrevocable patent licence to make,
33
+ have made, use, offer to sell, sell, import and otherwise transfer Your
34
+ Contribution, where the licence covers patent claims You can license that are
35
+ necessarily infringed by Your Contribution alone or combined with KerfScript.
36
+ If anyone starts patent litigation claiming KerfScript or a Contribution
37
+ infringes a patent, the patent licences granted to them under this agreement
38
+ end on the date the litigation is filed.
39
+
40
+ ## 3. Your promises
41
+
42
+ - Each Contribution is Your original work, or You have the right to submit it
43
+ under this agreement and You identify any third-party parts and their
44
+ licences.
45
+ - If Your employer has rights in what You create, You have permission to make
46
+ the Contribution on its behalf, or Your employer has waived those rights.
47
+ - You will tell us if any of these promises stops being true.
48
+
49
+ ## 4. What we promise
50
+
51
+ We will keep publishing KerfScript under an OSI-approved open source licence
52
+ alongside any commercial licence, for as long as we publish it at all. The
53
+ version that includes Your Contribution stays available under the licence it
54
+ was released with.
55
+
56
+ ## 5. No other obligations
57
+
58
+ You provide Contributions "as is", without warranties or conditions of any
59
+ kind. We are not required to use Your Contribution.
60
+
61
+ ## 6. Trademarks
62
+
63
+ This agreement does not grant either party rights in the other's names or
64
+ trademarks.
65
+
66
+ ## How to sign
67
+
68
+ Add this statement to each pull request description, using your legal name:
69
+
70
+ ```text
71
+ I have read the KerfScript CLA 1.0 and agree to it for my contributions.
72
+ Name: <your name> GitHub: <your handle> Date: <YYYY-MM-DD>
73
+ ```
74
+
75
+ If You contribute on behalf of a company, contact hi@mylasertools.com for
76
+ the company version first.
@@ -0,0 +1,63 @@
1
+ # Licensing
2
+
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.
8
+
9
+ KerfScript is dual licensed. You can use it under **either**:
10
+
11
+ 1. **The GNU Affero General Public License, version 3 only** (`AGPL-3.0-only`,
12
+ full text in [LICENSE](LICENSE)), together with the additional permission
13
+ below, or
14
+ 2. **A commercial licence** from MyLaserTools, which is **free**.
15
+
16
+ ## Free commercial licence
17
+
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.
22
+
23
+ ## Additional permission: your designs are yours
24
+
25
+ As an additional permission under section 7 of the AGPL, MyLaserTools grants
26
+ that:
27
+
28
+ 1. KerfScript programs you write (`.kerf` files), including programs that
29
+ `use` or `include` the KerfScript standard library in `lib/`, are not
30
+ considered a work based on KerfScript, and you may license and sell them on
31
+ any terms you choose.
32
+ 2. Files that KerfScript produces from your programs (SVG, DXF, JSON and
33
+ others) belong to you and are not covered by the AGPL.
34
+
35
+ This permission covers your programs and their output only. It does not cover
36
+ the KerfScript interpreter, its tools, or modified copies of the standard
37
+ library, which remain under the AGPL unless you have the commercial licence.
38
+
39
+ ## What the AGPL means for you
40
+
41
+ Under the AGPL you can:
42
+
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.
52
+
53
+ ## Trademarks
54
+
55
+ "KerfScript" and the KerfScript logo are trademarks of MyLaserTools and are not
56
+ licensed by the AGPL or by the commercial licence unless that licence says so.
57
+ See [TRADEMARKS.md](TRADEMARKS.md).
58
+
59
+ ## Contributions
60
+
61
+ MyLaserTools accepts contributions under the
62
+ [Contributor License Agreement](CLA.md), which lets MyLaserTools keep offering
63
+ KerfScript under both licences.
package/LANGUAGE.md ADDED
@@ -0,0 +1,398 @@
1
+ # KerfScript language reference
2
+
3
+ Language version 0.1. Copyright (c) 2026 MyLaserTools. This document is
4
+ licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
5
+ "KerfScript" is a trademark of MyLaserTools, see [TRADEMARKS.md](TRADEMARKS.md).
6
+
7
+ This reference covers KerfScript syntax, built-in modules and functions.
8
+ For a first project, follow [Getting started](docs/getting-started.md).
9
+
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.
16
+
17
+ ```kerf
18
+ // A coaster with a hole, an engraved ring and a scored border.
19
+
20
+ size = 90; // [60:5:120]
21
+
22
+ difference() {
23
+ circle(d = size);
24
+ circle(d = 6);
25
+ }
26
+ engrave() difference() { circle(d = size - 16); circle(d = size - 22); }
27
+ score() circle(d = size - 6);
28
+ ```
29
+
30
+ ## Lexical structure
31
+
32
+ - Comments: `// line` and `/* block */`.
33
+ - Numbers: `12`, `0.5`, `.5`, `1e3`.
34
+ - Strings: `"text"` with `\n`, `\t`, `\"`, `\\` and `\uXXXX` escapes.
35
+ - Names: letters, digits and `_`, not starting with a digit. Names starting
36
+ with `$` are **special variables** (see Scope).
37
+ - Keywords: `module function if else for let each use include true false undef`.
38
+
39
+ ## Values
40
+
41
+ | Type | Examples |
42
+ | -------- | ------------------------------------------------------------ |
43
+ | number | `3`, `-0.25`, `PI` |
44
+ | string | `"Oak"` |
45
+ | boolean | `true`, `false` |
46
+ | undef | `undef`, a missing argument, an index past the end of a list |
47
+ | list | `[1, 2]`, `[[0, 0], [10, 0], [5, 8]]` |
48
+ | range | `[0:5]` (0 to 5 step 1), `[0:2:10]` (0 to 10 step 2) |
49
+ | function | `function(x) x * 2` |
50
+
51
+ Points are lists `[x, y]`. `p.x` and `p.y` read the first two items.
52
+
53
+ ## Expressions
54
+
55
+ Operators are listed from lowest to highest precedence. Use parentheses
56
+ to change the order of evaluation.
57
+
58
+ | Operator | Meaning |
59
+ | ------------------- | -------------------------------------------------------------------------------------------------------------- |
60
+ | `c ? a : b` | conditional |
61
+ | `\|\|` | or |
62
+ | `&&` | and |
63
+ | `==` `!=` | equality, lists compare item by item |
64
+ | `<` `<=` `>` `>=` | compare numbers or strings |
65
+ | `+` `-` | add or subtract numbers or equal length lists; `+` joins strings |
66
+ | `*` `/` `%` | multiply, divide, remainder; list times number scales; list times list is the dot product; matrix times vector |
67
+ | `-x` `!x` | negate, not |
68
+ | `a ^ b` | power, right associative and tighter than unary minus |
69
+ | `f(x)` `a[i]` `a.x` | call, index, member |
70
+
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.
75
+
76
+ Use `let (a = 1, b = a + 1) expr` to define names for one expression.
77
+ Each binding can use the ones before it.
78
+
79
+ ### List comprehensions
80
+
81
+ Build a list with `for`, filter items with `if`, or insert the contents of
82
+ another list with `each`:
83
+
84
+ ```kerf
85
+ squares = [for (i = [1:5]) i * i];
86
+ odd = [for (i = [1:9]) if (i % 2) i];
87
+ grid = [for (x = [0:2], y = [0:2]) [x * 10, y * 10]];
88
+ flat = [each [1, 2], 3];
89
+ ```
90
+
91
+ ## Statements
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
+
97
+ ```kerf
98
+ name = expr; // assignment
99
+ function name(a, b = 2) = expr; // function
100
+ module name(a, b = 2) { ... } // module (makes shapes)
101
+ name(args) child; // module call with one child
102
+ name(args) { child; child; } // module call with a block of children
103
+ for (i = range_or_list, j = ...) ... // repeats, combined as a union
104
+ if (cond) ... else ...
105
+ let (a = 1) ...
106
+ use <path> // import modules and functions
107
+ include <path> // run another file in place
108
+ ```
109
+
110
+ Arguments can be positional or named: `rect([20, 10], r = 2)`.
111
+
112
+ ### Scope
113
+
114
+ - Assignments run **in order**. A later assignment to the same name replaces
115
+ the earlier one from that point on.
116
+ - `module` and `function` definitions are visible in their whole block, so you
117
+ can call them before they are defined.
118
+ - Blocks, loops and module bodies have their own scope.
119
+ - **Special variables** (`$name`) are dynamically scoped: a value set by a
120
+ caller is seen by everything it calls. Pass them as arguments too:
121
+ `circle(5, $fn = 6)`.
122
+ - Top-level assignments are the program's **parameters**. Renderers override
123
+ them by name (`kerf render -D width=200`).
124
+
125
+ ### Modifiers
126
+
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.
130
+
131
+ ### children()
132
+
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.
136
+
137
+ ```kerf
138
+ module hang_hole(d = 5) {
139
+ difference() { children(); translate([0, -d * 1.5]) circle(d = d); }
140
+ }
141
+ ```
142
+
143
+ ## Special variables
144
+
145
+ | Name | Default | Meaning |
146
+ | ----------------- | ------------- | -------------------------------------------------------------------------- |
147
+ | `$thickness` | `3` | material thickness, used by joinery and stored on parts |
148
+ | `$kerf` | `0` | beam width; cut outlines are offset by half of it |
149
+ | `$material` | `"material"` | material name; parts are packed per material and thickness |
150
+ | `$sheet` | `[600, 400]` | sheet size for packing |
151
+ | `$gap` | `3` | space between packed parts and around the sheet edge |
152
+ | `$layout` | `"pack"` | `"pack"` packs parts onto sheets, `"none"` keeps your placement |
153
+ | `$fn` `$fa` `$fs` | `0` `2` `0.2` | curve resolution: fixed segment count, or max angle and max segment length |
154
+ | `$font` | | default font name for `text()` |
155
+
156
+ ## Layers
157
+
158
+ Every shape is on a layer. Shapes not wrapped in a layer module are on `cut`.
159
+
160
+ | Layer | Mode | Colour | Intended operation |
161
+ | --------- | ----- | --------- | ---------------------------------------------------- |
162
+ | `engrave` | fill | `#000000` | the area is engraved |
163
+ | `score` | line | `#0000ff` | lines and shape outlines are marked, not cut through |
164
+ | `cut` | cut | `#ff0000` | outlines are cut through, with kerf compensation |
165
+ | `guide` | guide | `#00a0ff` | shown only when guides are requested |
166
+
167
+ ```kerf
168
+ cut() ... engrave() ... score() ... guide() ...
169
+ layer("pocket", mode = "fill", color = "#00aa00") ...
170
+ ```
171
+
172
+ Use `layer(name, mode, color)` to define a custom layer. Its mode is
173
+ `"cut"`, `"line"`, `"fill"` or `"guide"`.
174
+
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:
180
+
181
+ - The part's **material** is the union of its cut layer shapes.
182
+ - Engrave, score and custom layers are clipped to the material, so nothing is
183
+ marked inside holes or off the part. Score outlines become lines first.
184
+ - Cut lines (from `line()`, `living_hinge()` and similar) are clipped to the
185
+ material.
186
+ - If the material breaks into more than one piece, the renderer warns.
187
+ - In the output, holes and inner cuts come before outer outlines.
188
+
189
+ ## Parts and sheets
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
+
194
+ ```kerf
195
+ material(thickness = 4, kerf = 0.12, name = "birch") {
196
+ part("side", qty = 2) finger_rect([100, 60], "out");
197
+ part("base") rect([100, 100]);
198
+ }
199
+ ```
200
+
201
+ - `part(name, qty = 1) { ... }` makes a named part. It records `$thickness`,
202
+ `$kerf` and `$material` where it is called.
203
+ - Shapes outside any part form one part named `part`.
204
+ - `material(thickness, kerf, name) { ... }` sets those special variables for
205
+ its children.
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.
212
+
213
+ ## Built-in modules
214
+
215
+ ### Shapes
216
+
217
+ | Module | Notes |
218
+ | ---------------------------------------------------------- | ---------------------------------------------------------------------------- |
219
+ | `rect(size, center = false, r = 0)` | `size` is a number or `[w, h]`; `r` rounds the corners. `square` is an alias |
220
+ | `circle(r)` or `circle(d = ...)` | |
221
+ | `ellipse([rx, ry])` | |
222
+ | `regular_polygon(n, r)` | corner radius `r` |
223
+ | `star(n, r, inner)` | |
224
+ | `polygon(points, paths)` | even-odd fill, `paths` lists point indexes per outline |
225
+ | `path(d, fill = "evenodd")` | SVG path data; closed subpaths become shapes, open ones lines |
226
+ | `text(text, size = 10, font, halign, valign, spacing = 1)` | `halign`: left, center, right; `valign`: baseline, top, center, bottom |
227
+ | `line(from, to)` | a line, not an area |
228
+ | `polyline(points, closed = false)` | |
229
+ | `arc(r, start = 0, end = 90)` | arc line, angles in degrees |
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).
234
+
235
+ ### Transforms
236
+
237
+ | Module | Effect |
238
+ | --- | --- |
239
+ | `translate([x, y])` | Move geometry by x and y |
240
+ | `rotate(a, center = [x, y])` | Rotate by `a` degrees around a point; the default centre is the origin |
241
+ | `scale(s)` | Scale both axes by the same factor |
242
+ | `scale([sx, sy])` | Scale each axis separately |
243
+ | `mirror([nx, ny])` | Reflect across a line through the origin, perpendicular to this vector |
244
+ | `multmatrix(m)` | Apply a 2 × 3 transformation matrix |
245
+
246
+ ### Combining
247
+
248
+ | Module | Notes |
249
+ | ---------------------------------------------- | -------------------------------------------------------------------------- |
250
+ | `union()` / `group()` | joins children |
251
+ | `difference()` | removes every later child's area from every layer of the first child |
252
+ | `intersection()` | keeps only the area common to all children, layers of the first child kept |
253
+ | `hull()` | convex hull |
254
+ | `offset(r)` / `offset(delta, chamfer = false)` | grow (positive) or shrink areas; `r` rounds corners |
255
+ | `stroke(width)` | turns lines into areas of that width, for cutting single line art |
256
+ | `outline()` | turns areas into their outline lines |
257
+
258
+ ### Joinery
259
+
260
+ | Module | Notes |
261
+ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
262
+ | `finger_rect(size, edges, finger)` | rectangle with finger joints. `edges` is one value or `[bottom, right, top, left]`, each `"out"`, `"in"` or `"flat"`. An `"out"` edge keeps its corners, an `"in"` edge notches them; join an `"out"` edge to an `"in"` edge of the same length. Notch depth is `$thickness`; `finger` defaults to `max(3 * $thickness, 6)` and the count is always odd |
263
+ | `finger_holes(length, edge = "out", finger)` | row of slots along x, centred on y = 0, that an `edge` finger edge of the same length plugs into (for shelves and dividers) |
264
+ | `tslot(d = 3, length = 12, nut = [w, h], nut_at)` | T-slot for a screw and nut, opening at y = 0 and running up; subtract it from a panel edge |
265
+ | `living_hinge(size, spacing = 1.5, bridge = 3, cut)` | straight-line flex pattern as cut lines filling `[w, h]` |
266
+
267
+ ### Other
268
+
269
+ | Module | Effect |
270
+ | --- | --- |
271
+ | `echo(...)` | Report values without creating geometry |
272
+ | `assert(cond, message)` | Stop with `message` when `cond` is false |
273
+ | `children(i)` | Render a child passed to the current module; omit `i` to render all children |
274
+
275
+ ## Built-in functions
276
+
277
+ Functions return values. The examples below use positional arguments.
278
+
279
+ ### Numbers and vectors
280
+
281
+ | Function | Result |
282
+ | --- | --- |
283
+ | `abs(x)`, `sign(x)` | Absolute value, or sign as -1, 0 or 1 |
284
+ | `floor(x)`, `ceil(x)`, `round(x)` | Round down, up or to the nearest integer |
285
+ | `sqrt(x)`, `pow(x, y)` | Square root, or x raised to y |
286
+ | `exp(x)`, `ln(x)` | Exponential with base e, or natural logarithm |
287
+ | `log(x)`, `log(base, x)` | Base-10 logarithm, or logarithm with a chosen base |
288
+ | `sin(a)`, `cos(a)`, `tan(a)` | Trigonometric functions with angles in degrees |
289
+ | `asin(x)`, `acos(x)`, `atan(x)`, `atan2(y, x)` | Inverse trigonometric functions, returning degrees |
290
+ | `min(values)`, `max(values)` | Minimum or maximum of a list; also accept separate values |
291
+ | `clamp(x, low, high)` | Limit x to the given interval |
292
+ | `lerp(a, b, t)` | Interpolate between numbers or vectors; t = 0 gives a, t = 1 gives b |
293
+ | `norm(v)` | Length of a vector |
294
+ | `cross(a, b)` | Cross product; a scalar for 2D vectors or a vector for 3D vectors |
295
+
296
+ ### Lists and strings
297
+
298
+ | Function | Result |
299
+ | --- | --- |
300
+ | `len(value)` | Number of items in a list or characters in a string |
301
+ | `concat(...)` | Join lists and individual values into one list |
302
+ | `reverse(value)` | Reverse a list or string |
303
+ | `sum(values)` | Sum a list of numbers |
304
+ | `slice(value, start = 0, end)` | Extract part of a list or string; `end` is exclusive and defaults to its length |
305
+ | `str(...)` | Convert values to text and join them |
306
+ | `chr(...)`, `ord(text)` | Convert Unicode code points to text, or read the first character's code point |
307
+ | `upper(text)`, `lower(text)` | Change letter case |
308
+ | `split(text, separator = " ")` | Split text into a list of strings |
309
+ | `join(values, separator = "")` | Join values into a string |
310
+ | `search(value, list)` | Index of the first equal item, or `undef` if absent; also searches characters in strings |
311
+ | `contains(value, item)` | Whether a list contains an item or a string contains a substring |
312
+ | `lookup(x, table)` | Interpolate in a table of `[x, y]` pairs; use the nearest endpoint outside the table's range |
313
+
314
+ ### Types
315
+
316
+ `is_num(value)`, `is_string(value)`, `is_bool(value)`, `is_list(value)`,
317
+ `is_undef(value)` and `is_function(value)` return a boolean.
318
+ `type_of(value)` returns the type name as a string.
319
+
320
+ ### Random values
321
+
322
+ `rands(min, max, count, seed = 1)` returns a list of numbers between the
323
+ bounds. The same seed gives the same sequence.
324
+
325
+ ### Text measurements
326
+
327
+ `text_width(text, size, font)` returns the advance width, including glyph
328
+ spacing. `text_metrics(text, size, font)` returns
329
+ `[advance, [[x0, y0], [x1, y1]], ascent, descent]`.
330
+
331
+ Both require the font to be available to the renderer, just like `text()`.
332
+
333
+ ### Joint dimensions
334
+
335
+ `finger_count(length, finger)` returns the odd finger count used by
336
+ `finger_rect()` for an edge of that length.
337
+
338
+ ## Customizer comments
339
+
340
+ Top-level assignments with a literal number, string, boolean or numeric
341
+ list can appear as editable settings. Comments supply captions, ranges,
342
+ choices and groups:
343
+
344
+ ```kerf
345
+ /* [Size] */ // group for the following parameters
346
+ // Outer width <- caption: the comment line above
347
+ width = 120; // [40:5:400] min:step:max
348
+ depth = 80; // [200] max
349
+ shape = "round"; // [round:Round, square:Square] choices with labels
350
+ count = 3; // [2, 3, 4] choices
351
+ /* [Hidden] */ // not listed
352
+ ```
353
+
354
+ These comments follow OpenSCAD's customizer format. Read
355
+ [Settings](docs/customizer.md) for supported controls, or use `kerf params`
356
+ to inspect the settings in a file.
357
+
358
+ ## Standard library
359
+
360
+ Load the box modules with `use <kerf/box>`:
361
+
362
+ - `box([w, d, h], lid = false, finger)` makes the six (or five) panels of a
363
+ finger-jointed box as parts `bottom`, `lid`, `front`, `back`, `left`,
364
+ `right`. Sizes are outer sizes.
365
+ - `divider([w, h], finger)` makes a divider with finger tabs on both sides.
366
+
367
+ ## Output
368
+
369
+ - **SVG**: millimetre units, one Inkscape layer group per KerfScript layer,
370
+ colours as above, 0.1 mm hairline strokes, `data-part` on every path.
371
+ - **DXF**: R12 (AC1009) polylines on layers named after KerfScript layers,
372
+ with matching AutoCAD colours.
373
+
374
+ ## Limits
375
+
376
+ The interpreter limits call depth to 1,000 for modules and 2,000 for
377
+ functions. Expanded ranges are limited to 1,000,000 items. The default
378
+ evaluation limit is 5,000,000 steps; applications can change it with
379
+ `maxSteps`. Exceeding a limit stops evaluation with an error.
380
+
381
+ Programs are also limited in how much memory they can ask for:
382
+
383
+ | Limit | Default | What it counts |
384
+ | --- | --- | --- |
385
+ | `maxParts` | 10,000 | copies of all parts, adding up every `qty` |
386
+ | `maxSegments` | 10,000 | segments in one circle, ellipse or arc, and corners in one polygon |
387
+ | `maxListLength` | 1,000,000 | items in one list, or characters in one string |
388
+ | `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 |
389
+ | `maxTextLength` | 2,000 | characters in one `text()` |
390
+ | `maxPoints` | 2,000,000 | points in all shapes the modules make, plus 100 for each shape |
391
+ | `maxOutputPoints` | 1,000,000 | points in the exported files, counting every copy |
392
+ | `maxSheets` | 200 | sheets in the layout |
393
+
394
+ Applications can change them with the `limits` render option.
395
+
396
+ Geometry operations also take time. Applications that render user programs
397
+ should run them in a worker with a timeout; see the
398
+ [JavaScript API](docs/javascript.md#running-untrusted-code).