@mylasertools/kerfscript 0.2.0 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
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
+
3
21
  ## 0.2.0 (2026-10-28)
4
22
 
5
23
  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,20 @@ 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).
234
+
207
235
  ### Transforms
208
236
 
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.
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 |
212
245
 
213
246
  ### Combining
214
247
 
@@ -233,31 +266,80 @@ origin with that normal), `multmatrix(m)` with a 2x3 matrix.
233
266
 
234
267
  ### Other
235
268
 
236
- `echo(...)` prints values, `assert(cond, message)` stops with the message when
237
- `cond` is false, `children(i)`.
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 |
238
274
 
239
275
  ## Built-in functions
240
276
 
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`.
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.
243
324
 
244
- Lists and strings: `len concat reverse sum slice str chr ord upper lower split
245
- join search contains lookup`.
325
+ ### Text measurements
246
326
 
247
- Types: `is_num is_string is_bool is_list is_undef is_function type_of`.
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]`.
248
330
 
249
- Random: `rands(min, max, count, seed)` is repeatable for the same seed.
331
+ Both require the font to be available to the renderer, just like `text()`.
250
332
 
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]`.
333
+ ### Joint dimensions
253
334
 
254
- Joinery: `finger_count(length, finger)` returns the finger count
255
- `finger_rect` uses for that edge.
335
+ `finger_count(length, finger)` returns the odd finger count used by
336
+ `finger_rect()` for an edge of that length.
256
337
 
257
338
  ## Customizer comments
258
339
 
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:
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:
261
343
 
262
344
  ```kerf
263
345
  /* [Size] */ // group for the following parameters
@@ -269,12 +351,13 @@ count = 3; // [2, 3, 4] choices
269
351
  /* [Hidden] */ // not listed
270
352
  ```
271
353
 
272
- The format matches OpenSCAD customizer comments, so existing editors can show
273
- KerfScript parameters.
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.
274
357
 
275
358
  ## Standard library
276
359
 
277
- `use <kerf/box>`:
360
+ Load the box modules with `use <kerf/box>`:
278
361
 
279
362
  - `box([w, d, h], lid = false, finger)` makes the six (or five) panels of a
280
363
  finger-jointed box as parts `bottom`, `lid`, `front`, `back`, `left`,
@@ -290,6 +373,26 @@ KerfScript parameters.
290
373
 
291
374
  ## Limits
292
375
 
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.
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).
package/README.md CHANGED
@@ -1,17 +1,38 @@
1
- # KerfScript
2
-
3
- **A programming language for laser cutting.**
4
-
5
- [Website](https://kerfscript.mylasertools.com) ·
6
- [Playground](https://kerfscript.mylasertools.com/playground/) ·
7
- [Models](https://kerfscript.mylasertools.com/models/) ·
8
- [Docs](https://kerfscript.mylasertools.com/docs/)
1
+ <p align="center">
2
+ <a href="https://kerfscript.mylasertools.com"><img src=".github/readme/banner.png" alt="KerfScript, a programming language for laser cutting" width="840"></a>
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@mylasertools/kerfscript"><img src="https://img.shields.io/npm/v/@mylasertools/kerfscript?color=d8361f&label=npm" alt="npm version"></a>
7
+ <a href="COMMERCIAL-LICENSE.md"><img src="https://img.shields.io/badge/licence-AGPL--3.0%20or%20free%20commercial-1f1a17" alt="Licence: AGPL-3.0 or free commercial"></a>
8
+ <a href="LANGUAGE.md"><img src="https://img.shields.io/badge/language-0.1-e3c69a" alt="Language version 0.1"></a>
9
+ <a href="https://kerfscript.mylasertools.com/playground/"><img src="https://img.shields.io/badge/try%20it-playground-5a3519" alt="Try it in the playground"></a>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://kerfscript.mylasertools.com">Website</a> ·
14
+ <a href="https://kerfscript.mylasertools.com/playground/">Playground</a> ·
15
+ <a href="https://kerfscript.mylasertools.com/models/">Models</a> ·
16
+ <a href="https://kerfscript.mylasertools.com/docs/">Docs</a> ·
17
+ <a href="https://www.npmjs.com/package/@mylasertools/kerfscript">npm</a>
18
+ </p>
9
19
 
10
20
  Write a part once as code, change a number, and get a new cut file. KerfScript
11
21
  is to laser cutters what OpenSCAD is to 3D printers, except it was designed for
12
22
  flat parts from the start: cut, score and engrave layers, kerf compensation,
13
23
  finger joints, living hinges, parts and sheets are part of the language.
14
24
 
25
+ <p align="center">
26
+ <a href="https://kerfscript.mylasertools.com/playground/?model=fractal-tree"><img src=".github/readme/playground.gif" alt="Moving a slider in the playground changes the number in the code and redraws the fractal tree" width="840"></a>
27
+ <br><sub>The <a href="https://kerfscript.mylasertools.com/playground/">playground</a> runs the interpreter in your browser. A comment after a number turns it into a slider.</sub>
28
+ </p>
29
+
30
+ ## Example
31
+
32
+ <table>
33
+ <tr>
34
+ <td width="50%" valign="top">
35
+
15
36
  ```kerf
16
37
  use <kerf/box>
17
38
 
@@ -28,6 +49,13 @@ $kerf = 0.15; // [0:0.01:0.5]
28
49
  box([width, depth, height], lid = lid);
29
50
  ```
30
51
 
52
+ </td>
53
+ <td width="50%" valign="top">
54
+ <img src=".github/readme/model-box.png" alt="Six finger-jointed panels of a box, packed on a sheet">
55
+ </td>
56
+ </tr>
57
+ </table>
58
+
31
59
  ```sh
32
60
  npx @mylasertools/kerfscript render box.kerf -o box.svg -D width=200
33
61
  ```
@@ -37,6 +65,35 @@ millimetre SVG with one layer per operation. Output is checked automatically
37
65
  in Inkscape; see [docs/compatibility.md](docs/compatibility.md) for what has
38
66
  and has not been tested in other laser software.
39
67
 
68
+ ## Made with KerfScript
69
+
70
+ Every model is a file in [examples/](examples). Open one on the website to
71
+ turn it around in 3D, change its settings and download the SVG or DXF, or copy
72
+ it as the start of your own design.
73
+
74
+ <table>
75
+ <tr>
76
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/hadal/"><img src="site/public/3d/hadal.webp" alt="Hadal sculpture, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/hadal/">Hadal sculpture</a></b><br><sub>13 slotted parts, ribs computed from lists</sub></td>
77
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/tilt-maze/"><img src="site/public/3d/tilt-maze.webp" alt="Tilt maze, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/tilt-maze/">Tilt maze</a></b><br><sub>a new maze for every seed, by recursion</sub></td>
78
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/topo-island/"><img src="site/public/3d/topo-island.webp" alt="Layered island, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/topo-island/">Layered island</a></b><br><sub>contours traced from a height formula</sub></td>
79
+ </tr>
80
+ <tr>
81
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/fractal-tree/"><img src="site/public/3d/fractal-tree.webp" alt="Fractal tree, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/fractal-tree/">Fractal tree</a></b><br><sub>a module that calls itself</sub></td>
82
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/round-badge/"><img src="site/public/3d/round-badge.webp" alt="Round badge, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/round-badge/">Round badge</a></b><br><sub>text set letter by letter around a circle</sub></td>
83
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/winterlight/"><img src="site/public/3d/winterlight.webp" alt="Winterlight ornaments, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/winterlight/">Winterlight ornaments</a></b><br><sub>one-piece ornaments with scored detail</sub></td>
84
+ </tr>
85
+ <tr>
86
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/xenospora/"><img src="site/public/3d/xenospora.webp" alt="Xenospora seed pod, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/xenospora/">Xenospora seed pod</a></b><br><sub>16 parts on 3 sheets, one file per sheet</sub></td>
87
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/solstice/"><img src="site/public/3d/solstice.webp" alt="Solstice lantern, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/solstice/">Solstice lantern</a></b><br><sub>tabs, slots and a fit test piece</sub></td>
88
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/box/"><img src="site/public/3d/box.webp" alt="Finger-jointed box, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/box/">Finger-jointed box</a></b><br><sub>six panels from one library call</sub></td>
89
+ </tr>
90
+ <tr>
91
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/wave-cathedral/"><img src="site/public/3d/wave-cathedral.webp" alt="Wave Cathedral, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/wave-cathedral/">Wave Cathedral</a></b><br><sub>17 different ribs from one formula</sub></td>
92
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/gift-tags/"><img src="site/public/3d/gift-tags.webp" alt="Gift tags from a list, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/gift-tags/">Gift tags from a list</a></b><br><sub>one tag per name, each name fitted</sub></td>
93
+ <td width="33%" valign="top"><a href="https://kerfscript.mylasertools.com/models/umbra/"><img src="site/public/3d/umbra.webp" alt="Umbra shadow machine, assembled"></a><br><b><a href="https://kerfscript.mylasertools.com/models/umbra/">Umbra shadow machine</a></b><br><sub>six fins whose shadows make one dragon</sub></td>
94
+ </tr>
95
+ </table>
96
+
40
97
  ## Why a new language
41
98
 
42
99
  - **Laser concepts are built in.** `engrave()`, `score()` and `cut()` put
@@ -107,25 +164,6 @@ Errors are thrown as `KerfError` with `line` and `col`.
107
164
  - [examples/](examples): every model on the website, including a
108
165
  calibration square and a kerf test
109
166
 
110
- ## Website
111
-
112
- kerfscript.mylasertools.com is an Astro site in [site/](site), deployed to
113
- Cloudflare as a static assets Worker. Every model picture and the guides are
114
- built from [examples/](examples), [docs/](docs) and LANGUAGE.md by this
115
- version of KerfScript.
116
-
117
- ```sh
118
- npm --prefix site install
119
- npm run site # build KerfScript, then the site into site/dist
120
- npm run site:serve # preview on http://localhost:4321
121
- npm --prefix site run deploy
122
- ```
123
-
124
- ## Checking output
125
-
126
- `npm run compat` renders a reference set of models and opens every SVG and
127
- DXF in Inkscape to check sizes, layers and fills.
128
-
129
167
  ## Community
130
168
 
131
169
  Bug reports, cut reports and ideas are welcome; see
@@ -141,8 +179,9 @@ KerfScript is free software under the
141
179
  that makes **your `.kerf` designs and the files KerfScript generates yours to
142
180
  license and sell however you like**.
143
181
 
144
- Using KerfScript in a closed source product or hosted service without
145
- publishing your source requires a [commercial licence](COMMERCIAL-LICENSE.md).
182
+ Building KerfScript into a closed-source app, machine or service? The
183
+ [commercial licence](COMMERCIAL-LICENSE.md#free-commercial-licence) is free:
184
+ just email hi@mylasertools.com.
146
185
 
147
186
  "KerfScript" is a trademark of MyLaserTools; see [TRADEMARKS.md](TRADEMARKS.md).
148
187
  Contributions need the [CLA](CLA.md); see [CONTRIBUTING.md](CONTRIBUTING.md).
@@ -62,6 +62,61 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
62
62
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
63
  ```
64
64
 
65
+ ## tiny-inflate 1.0.3 (bundled, part of opentype.js)
66
+
67
+ Source: https://github.com/foliojs/tiny-inflate
68
+
69
+ ```text
70
+ MIT License
71
+
72
+ Copyright (c) 2015-present Devon Govett
73
+
74
+ Permission is hereby granted, free of charge, to any person obtaining a copy
75
+ of this software and associated documentation files (the "Software"), to deal
76
+ in the Software without restriction, including without limitation the rights
77
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
78
+ copies of the Software, and to permit persons to whom the Software is
79
+ furnished to do so, subject to the following conditions:
80
+
81
+ The above copyright notice and this permission notice shall be included in all
82
+ copies or substantial portions of the Software.
83
+
84
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
85
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
86
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
87
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
88
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
89
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
90
+ SOFTWARE.
91
+ ```
92
+
93
+ ## string.prototype.codepointat 0.2.1 (bundled, part of opentype.js)
94
+
95
+ Source: https://github.com/mathiasbynens/String.prototype.codePointAt
96
+
97
+ ```text
98
+ Copyright Mathias Bynens <https://mathiasbynens.be/>
99
+
100
+ Permission is hereby granted, free of charge, to any person obtaining
101
+ a copy of this software and associated documentation files (the
102
+ "Software"), to deal in the Software without restriction, including
103
+ without limitation the rights to use, copy, modify, merge, publish,
104
+ distribute, sublicense, and/or sell copies of the Software, and to
105
+ permit persons to whom the Software is furnished to do so, subject to
106
+ the following conditions:
107
+
108
+ The above copyright notice and this permission notice shall be
109
+ included in all copies or substantial portions of the Software.
110
+
111
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
112
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
113
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
114
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
115
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
116
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
117
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
118
+ ```
119
+
65
120
  ## Atkinson Hyperlegible (test fixture only, not published to npm)
66
121
 
67
122
  `test/fixtures/AtkinsonHyperlegible-Regular.ttf`, Copyright 2020 Braille