@mylasertools/kerfscript 0.2.1 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,13 +1,82 @@
1
1
  # Changelog
2
2
 
3
- ## 0.2.1
3
+ ## 0.4.0 (2026-10-30)
4
4
 
5
+ - Assembly: `part()` takes `at` and `rotate` to place a part in 3D, rotated
6
+ first and then moved, like OpenSCAD. `render()` returns the placed parts as
7
+ `assembly` (outline, thickness and a 4 × 4 matrix per copy) so apps can show
8
+ the design put together, and warns when two placed parts collide, which is
9
+ how a wrong finger edge shows up. Overlaps under about 1 mm³ are ignored.
10
+ Parts without a placement lie flat beside the assembly, marked
11
+ `placed: false`, so a divider added to a `box()` still shows.
12
+ `box()` from `kerf/box` places its own panels.
13
+ - Copies of a part that share one placement are drawn in the same place and
14
+ get one warning that says so, instead of a collision warning for every pair.
15
+ - The collision check stops after `maxCollisionPairs` pairs of touching parts
16
+ (20,000 by default) with a warning, so a program that places thousands of
17
+ parts cannot hold up the render. It also skips parts far apart on x before
18
+ comparing them, so large assemblies check faster.
19
+ - `kerfscript.wasm` returns `assembly` in its render response.
20
+
21
+ ## 0.3.0 (2026-10-30)
22
+
23
+ - `kerfscript.wasm`: a WASI module that runs KerfScript from Python, Rust,
24
+ Go, C# and other languages. It takes a JSON request on standard input and
25
+ writes SVG, DXF, parts and warnings as JSON. `"command": "licenses"`
26
+ returns the licence texts of the module and of the JavaScript engine built
27
+ into it. Build it with `npm run wasm`; see
28
+ [docs/webassembly.md](docs/webassembly.md).
29
+ - A desktop app for Windows, macOS and Linux: the playground with `.kerf`
30
+ files on disk (Open, Save, Save as, double-click to open; Save as adds
31
+ `.kerf` to a name without it and asks before replacing a file of that
32
+ name), exports through a save dialog, `use`/`include` libraries read from
33
+ the design's folder and the folders below it (`.kerf` files only), and an
34
+ About window with the source link and the licences of everything in the
35
+ app. Saving writes the whole file before it replaces the old one, and the
36
+ app asks about unsaved changes before it closes, also on ⌘Q.
37
+ Installers: Windows setup and MSI, a universal macOS dmg, and an AppImage,
38
+ `.deb` and `.rpm` for Linux x64 and arm64; Flathub, winget, Chocolatey and
39
+ AUR manifests. See [docs/desktop.md](docs/desktop.md).
40
+ - Open in LightBurn in the desktop app: sends the cut file (or one sheet) to
41
+ LightBurn 2.2 or later through its REST API. KerfScript asks LightBurn only
42
+ for permission to send files, and talks to it directly, past any proxy
43
+ set on the computer.
44
+ - Chocolatey and MacPorts manifests for the command line.
45
+ - The playground does not load a shared link, draft or file longer than
46
+ 1,000,000 characters, and stops unpacking a compressed link at that size.
47
+ Any code the editor runs fits in a link, whatever its script, and a link
48
+ that cannot be opened says why in the console. Code over the limit is not
49
+ coloured or read for settings, and Copy link refuses it. A link to an
50
+ empty design opens it, instead of the draft.
51
+ - Settings for a list, such as `size = [10, 20]`, keep numbers changed in the
52
+ code since the settings were drawn, instead of putting the old ones back.
53
+ An empty or half-typed number box leaves the code as it is.
54
+ - The playground renders the newest code after fast edits to a slow design,
55
+ instead of stopping with "Stopped after 15 seconds", and keeps working
56
+ when the number of sheets drops to one in the laser file view. Edits after
57
+ opening a shared link or moving a slider on an example are kept on reload.
58
+ - The website's licence page, and `assets/THIRD-PARTY-NOTICES.md` next to
59
+ `kerfscript.js`, have the notices of the libraries bundled in its
60
+ JavaScript.
61
+ - Standalone `kerf` binaries for Windows, macOS and Linux that need no
62
+ Node.js, with the licences of the Deno runtime built into them,
63
+ `kerfscript-cli` `.deb` and `.rpm` packages (which install next to the
64
+ desktop app), a Docker image, a snap, and Homebrew,
65
+ Scoop, winget and AUR manifests, all built by the release workflow with
66
+ SHA-256, SHA-512 and GPG signatures. Nightly builds go to a rolling
67
+ `nightly` prerelease. See RELEASING.md.
68
+ - A `kerf(1)` man page and a `text/x-kerfscript` MIME type for `*.kerf` files
69
+ in the Linux packages.
70
+ - The command line and `kerfscript.wasm` have a font built in, Atkinson
71
+ Hyperlegible as `Atkinson` (the playground's font), so `text()` works
72
+ without `--font`. The JavaScript library still takes fonts from the host.
5
73
  - Memory limits, so a program cannot make a browser tab run out of memory:
6
74
  part copies, circle segments and polygon corners, list and string sizes,
7
75
  all list items together, `text()` length, shape points, exported points
8
76
  and sheets. Each stops the program with an error that says what to change.
9
77
  Defaults are in `DEFAULT_LIMITS` and the `limits` render option changes
10
- them. Every example stays well under them.
78
+ them. Every example stays well under them. Ranges in loops and built-in
79
+ functions stop at `maxListLength` items, not at a fixed million.
11
80
  - A list or string that grows past what JavaScript can hold now gives a
12
81
  KerfScript error instead of a `RangeError`.
13
82
  - Lists are checked while they are built, so `each` in a long loop, list
@@ -15,6 +84,34 @@
15
84
  memory. Strings joined with `+`, strings from functions and `echo()` text
16
85
  count toward `maxListItems` too, and a repeated unknown argument warning
17
86
  is shown once.
87
+ - `echo()`, `str()`, `join()` and `==` on lists that hold the same list many
88
+ times stop at the limits instead of running out of memory: text stops at
89
+ `maxListLength` characters while it is written, and comparing lists does
90
+ not compare the same pair of lists twice. Error messages, part names and
91
+ other settings show only the start of a long list.
92
+ - Comparing lists, dot products and built-in functions that go through a list
93
+ or string, such as `search()`, `contains()`, `max()` and `sum()`, take a
94
+ step for each item, so a loop over a long list stops at `maxSteps` instead
95
+ of running for minutes.
96
+ - `hull()` around named parts makes the hull of each part, the same as
97
+ `hull()` inside the part. It left the parts unchanged before.
98
+ - DXF layers whose names come out the same in DXF, such as `a b` and `a_b`,
99
+ stay separate layers: the later ones get `_2`, `_3` and so on. The
100
+ built-in layers keep `CUT`, `SCORE`, `ENGRAVE` and `GUIDE`.
101
+ - Built-in functions take named arguments, such as `sin(x = 30)` and
102
+ `pow(x = 2, y = 10)`; an unknown name gives a warning, and so does an
103
+ argument given both by position and by name. They were ignored before.
104
+ - Errors and warnings in a library name it, as in
105
+ `(line 3, column 5 in <lib/tabs>)`. The playground marks the line only for
106
+ errors in the program itself.
107
+ - `toString()`, `constructor()` and other names every JavaScript object has
108
+ are unknown functions and libraries, instead of a crash or a wrong value.
109
+ - `text_width()` and `text_metrics()` take a step per character, have the
110
+ same length limit as `text()`, and are much faster for long text.
111
+ - A list times a matrix of the wrong size, and `params` values that are not
112
+ numbers, strings, booleans or lists, are errors instead of a crash.
113
+ - A list times a list whose items multiply to lists is an error instead of
114
+ text.
18
115
  - The commercial licence for closed-source apps and services is free: email
19
116
  hi@mylasertools.com.
20
117
 
package/LANGUAGE.md CHANGED
@@ -198,8 +198,12 @@ material(thickness = 4, kerf = 0.12, name = "birch") {
198
198
  }
199
199
  ```
200
200
 
201
- - `part(name, qty = 1) { ... }` makes a named part. It records `$thickness`,
202
- `$kerf` and `$material` where it is called.
201
+ - `part(name, qty = 1, at, rotate) { ... }` makes a named part. It records
202
+ `$thickness`, `$kerf` and `$material` where it is called. `at = [x, y, z]`
203
+ and `rotate = [x, y, z]` (degrees) place the part in the assembled view:
204
+ rotated first, then moved, with its thickness along +Z. A part with copies
205
+ takes one placement or a list with one per copy. `render()` returns the
206
+ placed parts as `assembly` and warns when two of them collide.
203
207
  - Shapes outside any part form one part named `part`.
204
208
  - `material(thickness, kerf, name) { ... }` sets those special variables for
205
209
  its children.
@@ -230,18 +234,20 @@ export examples.
230
234
 
231
235
  Supply geometry as children of a shape-processing module, for example
232
236
  `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).
237
+ by the application or through the [command line](docs/cli.md). The
238
+ playground, the command line and `kerfscript.wasm` have `Atkinson`
239
+ (Atkinson Hyperlegible) built in.
234
240
 
235
241
  ### Transforms
236
242
 
237
- | Module | Effect |
238
- | --- | --- |
239
- | `translate([x, y])` | Move geometry by x and y |
243
+ | Module | Effect |
244
+ | ---------------------------- | ---------------------------------------------------------------------- |
245
+ | `translate([x, y])` | Move geometry by x and y |
240
246
  | `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 |
247
+ | `scale(v)` | Scale both axes by the number `v` |
248
+ | `scale([sx, sy])` | Scale each axis separately |
249
+ | `mirror([nx, ny])` | Reflect across a line through the origin, perpendicular to this vector |
250
+ | `multmatrix(m)` | Apply a 2 × 3 transformation matrix |
245
251
 
246
252
  ### Combining
247
253
 
@@ -250,7 +256,7 @@ by the application or through the [command line](docs/cli.md).
250
256
  | `union()` / `group()` | joins children |
251
257
  | `difference()` | removes every later child's area from every layer of the first child |
252
258
  | `intersection()` | keeps only the area common to all children, layers of the first child kept |
253
- | `hull()` | convex hull |
259
+ | `hull()` | convex hull; each named part inside gets its own hull |
254
260
  | `offset(r)` / `offset(delta, chamfer = false)` | grow (positive) or shrink areas; `r` rounds corners |
255
261
  | `stroke(width)` | turns lines into areas of that width, for cutting single line art |
256
262
  | `outline()` | turns areas into their outline lines |
@@ -266,50 +272,53 @@ by the application or through the [command line](docs/cli.md).
266
272
 
267
273
  ### Other
268
274
 
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 |
275
+ | Module | Effect |
276
+ | ---------------------------- | -------------------------------------------------------------------------------- |
277
+ | `echo(...)` | Report values without creating geometry |
278
+ | `assert(condition, message)` | Stop with `message` when `condition` is false |
279
+ | `children(index)` | Render a child passed to the current module; omit `index` to render all children |
274
280
 
275
281
  ## Built-in functions
276
282
 
277
- Functions return values. The examples below use positional arguments.
283
+ Functions return values. Arguments can be positional or named with the
284
+ parameter names below, as in `pow(x = 2, y = 10)` or `log(8, base = 2)`. An
285
+ unknown name gives a warning. `concat`, `str` and `chr` take only positional
286
+ arguments.
278
287
 
279
288
  ### Numbers and vectors
280
289
 
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 |
290
+ | Function | Result |
291
+ | ---------------------------------------------- | -------------------------------------------------------------------- |
292
+ | `abs(x)`, `sign(x)` | Absolute value, or sign as -1, 0 or 1 |
293
+ | `floor(x)`, `ceil(x)`, `round(x)` | Round down, up or to the nearest integer |
294
+ | `sqrt(x)`, `pow(x, y)` | Square root, or x raised to y |
295
+ | `exp(x)`, `ln(x)` | Exponential with base e, or natural logarithm |
296
+ | `log(x)`, `log(base, x)` | Base-10 logarithm, or logarithm with a chosen base |
297
+ | `sin(x)`, `cos(x)`, `tan(x)` | Trigonometric functions with angles in degrees |
298
+ | `asin(x)`, `acos(x)`, `atan(x)`, `atan2(y, x)` | Inverse trigonometric functions, returning degrees |
299
+ | `min(values)`, `max(values)` | Minimum or maximum of a list; also accept separate values |
300
+ | `clamp(x, low, high)` | Limit x to the given interval |
301
+ | `lerp(a, b, t)` | Interpolate between numbers or vectors; t = 0 gives a, t = 1 gives b |
302
+ | `norm(v)` | Length of a vector |
303
+ | `cross(a, b)` | Cross product; a scalar for 2D vectors or a vector for 3D vectors |
295
304
 
296
305
  ### Lists and strings
297
306
 
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 |
307
+ | Function | Result |
308
+ | ------------------------------ | -------------------------------------------------------------------------------------------- |
309
+ | `len(value)` | Number of items in a list or characters in a string |
310
+ | `concat(...)` | Join lists and individual values into one list |
311
+ | `reverse(value)` | Reverse a list or string |
312
+ | `sum(values)` | Sum a list of numbers |
313
+ | `slice(value, start = 0, end)` | Extract part of a list or string; `end` is exclusive and defaults to its length |
314
+ | `str(...)` | Convert values to text and join them |
315
+ | `chr(...)`, `ord(text)` | Convert Unicode code points to text, or read the first character's code point |
316
+ | `upper(text)`, `lower(text)` | Change letter case |
317
+ | `split(text, separator = " ")` | Split text into a list of strings |
318
+ | `join(values, separator = "")` | Join values into a string |
319
+ | `search(value, list)` | Index of the first equal item, or `undef` if absent; also searches characters in strings |
320
+ | `contains(value, item)` | Whether a list contains an item or a string contains a substring |
321
+ | `lookup(x, table)` | Interpolate in a table of `[x, y]` pairs; use the nearest endpoint outside the table's range |
313
322
 
314
323
  ### Types
315
324
 
@@ -342,15 +351,29 @@ list can appear as editable settings. Comments supply captions, ranges,
342
351
  choices and groups:
343
352
 
344
353
  ```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
354
+ /* [Size] */
355
+ // Outer width
356
+ width = 120; // [40:5:400]
357
+ depth = 80; // [200]
358
+ shape = "round"; // [round:Round, square:Square]
359
+ count = 3; // [2, 3, 4]
360
+ /* [Hidden] */
361
+ seed = 7;
352
362
  ```
353
363
 
364
+ - `/* [Size] */` starts a group; the settings below it are listed under
365
+ _Size_.
366
+ - `// Outer width`, the comment line just above an assignment, is its caption.
367
+ - `[40:5:400]` is a slider from 40 to 400 in steps of 5; `[200]` is a slider
368
+ up to 200.
369
+ - `[round:Round, square:Square]` is a menu of values with labels; `[2, 3, 4]`
370
+ is a menu of values.
371
+ - Settings after `/* [Hidden] */` are not listed.
372
+
373
+ Nothing else may follow the brackets on the same line: with
374
+ `// [40:5:400] min:step:max` the setting is a plain number field, not a
375
+ slider.
376
+
354
377
  These comments follow OpenSCAD's customizer format. Read
355
378
  [Settings](docs/customizer.md) for supported controls, or use `kerf params`
356
379
  to inspect the settings in a file.
@@ -369,30 +392,43 @@ Load the box modules with `use <kerf/box>`:
369
392
  - **SVG**: millimetre units, one Inkscape layer group per KerfScript layer,
370
393
  colours as above, 0.1 mm hairline strokes, `data-part` on every path.
371
394
  - **DXF**: R12 (AC1009) polylines on layers named after KerfScript layers,
372
- with matching AutoCAD colours.
395
+ with matching AutoCAD colours. Names are upper case with characters other
396
+ than letters, digits, `_` and `-` as `_`; when two layers come out the
397
+ same, the later one gets `_2`, `_3` and so on. The built-in layers keep
398
+ `CUT`, `SCORE`, `ENGRAVE` and `GUIDE`, so a layer of your own named `Cut`
399
+ becomes `CUT_2`.
373
400
 
374
401
  ## Limits
375
402
 
376
403
  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
404
+ functions. A range a loop or built-in function goes through is limited to
405
+ `maxListLength` items (below). The default
378
406
  evaluation limit is 5,000,000 steps; applications can change it with
379
- `maxSteps`. Exceeding a limit stops evaluation with an error.
407
+ `maxSteps`. Comparing lists with `==`, multiplying two lists, and built-in
408
+ functions that go through a list or string take a step for each item;
409
+ `len()` of a list, `ord()`, `type_of()` and the `is_` functions take one.
410
+ Exceeding a limit stops evaluation with an error.
380
411
 
381
412
  Programs are also limited in how much memory they can ask for:
382
413
 
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 |
414
+ | Limit | Default | What it counts |
415
+ | ------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
416
+ | `maxParts` | 10,000 | copies of all parts, adding up every `qty` |
417
+ | `maxSegments` | 10,000 | segments in one circle, ellipse or arc, and corners in one polygon |
418
+ | `maxListLength` | 1,000,000 | items in one list or range, or characters in one string, including the text `echo()` writes in one call |
419
+ | `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 |
420
+ | `maxTextLength` | 2,000 | characters in one `text()` |
421
+ | `maxPoints` | 2,000,000 | points in all shapes the modules make, plus 100 for each shape |
422
+ | `maxOutputPoints` | 1,000,000 | points in the exported files, counting every copy |
423
+ | `maxSheets` | 200 | sheets in the layout |
424
+ | `maxCollisionPairs` | 20,000 | pairs of touching placed parts checked for collisions; past it the check stops with a warning |
393
425
 
394
426
  Applications can change them with the `limits` render option.
395
427
 
428
+ A list can hold the same list many times, as in `b = [a, a]`. `echo()`,
429
+ `str()` and `join()` write every copy out, so their text can be far longer
430
+ than the list; they stop with an error once it passes `maxListLength`.
431
+
396
432
  Geometry operations also take time. Applications that render user programs
397
433
  should run them in a worker with a timeout; see the
398
434
  [JavaScript API](docs/javascript.md#running-untrusted-code).
package/README.md CHANGED
@@ -101,17 +101,20 @@ it as the start of your own design.
101
101
  cuts come before outer ones, and the whole part is offset by half the kerf.
102
102
  - **Parts, not just shapes.** `part("lid", qty = 2)` names a piece; the
103
103
  renderer packs parts by material and thickness onto sheets.
104
- - **Joinery that fits.** `finger_rect`, `finger_holes`, `tslot` and
105
- `living_hinge` use `$thickness`, so changing the material keeps joints
106
- tight.
107
- - **Familiar syntax.** If you know OpenSCAD, you can read KerfScript. The
108
- customizer comments are the same, so editors that understand OpenSCAD
109
- parameters understand KerfScript ones.
104
+ - **Joinery that follows the material.** `finger_rect`, `finger_holes`,
105
+ `tslot` and `living_hinge` use `$thickness`, so the joints resize when the
106
+ material changes. Measure your kerf and cut a test piece for a snug fit.
107
+ - **Readable code.** Variables, modules, functions, loops and transforms like
108
+ `translate()` and `rotate()`. Settings come from comments such as
109
+ `// [10:1:100]`, which the playground turns into sliders. If you have used
110
+ OpenSCAD, [Coming from OpenSCAD](docs/openscad.md) lists what differs.
110
111
  - **Runs anywhere.** Pure TypeScript with no native code: the same interpreter
111
112
  runs in browsers, Node, workers and the command line.
112
- - **Safe to run.** Programs read libraries only through the resolver you
113
- provide. Step and recursion limits stop runaway programs, and errors name
114
- the line.
113
+ - **Contained.** Programs read libraries only through the resolver you
114
+ provide and have no other access to files or the network. Step, recursion
115
+ and size limits stop runaway programs, and errors name the line. Run
116
+ programs you did not write in a worker or process you can stop; see
117
+ [Running untrusted code](docs/javascript.md#running-untrusted-code).
115
118
 
116
119
  ## Install
117
120
 
@@ -119,6 +122,12 @@ it as the start of your own design.
119
122
  npm install @mylasertools/kerfscript
120
123
  ```
121
124
 
125
+ The `kerf` command line also comes as a standalone binary that needs no
126
+ Node.js, and through Homebrew, winget, Chocolatey, Scoop, Snap, the AUR,
127
+ MacPorts and Docker: see [Command line](docs/cli.md#without-nodejs). The
128
+ desktop app is in [Desktop app](docs/desktop.md), and the WebAssembly module
129
+ in [WebAssembly](docs/webassembly.md).
130
+
122
131
  ## Command line
123
132
 
124
133
  ```sh
@@ -156,7 +165,9 @@ Errors are thrown as `KerfError` with `line` and `col`.
156
165
  - [Getting started](docs/getting-started.md), then the guides on
157
166
  [layers](docs/layers.md), [kerf](docs/kerf.md),
158
167
  [parts and sheets](docs/parts-and-sheets.md), [settings](docs/customizer.md),
159
- the [command line](docs/cli.md) and the [JavaScript API](docs/javascript.md)
168
+ the [desktop app](docs/desktop.md), the [command line](docs/cli.md), the
169
+ [JavaScript API](docs/javascript.md)
170
+ and [WebAssembly](docs/webassembly.md) for other languages
160
171
  - [Coming from OpenSCAD](docs/openscad.md): what is the same and what is not
161
172
  - [LANGUAGE.md](LANGUAGE.md): the full language reference
162
173
  - [Versions](docs/versions.md): language and package versions, and what stays
@@ -117,8 +117,17 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
117
117
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
118
118
  ```
119
119
 
120
- ## Atkinson Hyperlegible (test fixture only, not published to npm)
120
+ ## Desktop app
121
121
 
122
- `test/fixtures/AtkinsonHyperlegible-Regular.ttf`, Copyright 2020 Braille
123
- Institute of America, Inc., licensed under the SIL Open Font License 1.1. See
124
- `test/fixtures/OFL.txt`.
122
+ The desktop app is also built from Rust crates (Tauri and its dependencies)
123
+ under MIT, Apache-2.0, MPL-2.0, BSD, ISC, Zlib, Unicode and similar licences.
124
+ `desktop/THIRD-PARTY-LICENSES.md` lists each one with its licence text, and
125
+ the installers place it next to the app.
126
+
127
+ ## Atkinson Hyperlegible (built into the command line and kerfscript.wasm)
128
+
129
+ `fonts/AtkinsonHyperlegible-Regular.ttf`, Copyright 2020 Braille Institute
130
+ of America, Inc., licensed under the SIL Open Font License 1.1. See
131
+ `fonts/OFL.txt`. It is embedded unmodified in `dist/cli.js` and
132
+ `kerfscript.wasm`, and the playground serves it; `dist/index.js` does not
133
+ include it.
@@ -0,0 +1,20 @@
1
+ import type { Poly } from './geometry.cjs';
2
+ export type Vec3 = [number, number, number];
3
+ export interface Placement {
4
+ at: Vec3;
5
+ rotate: Vec3;
6
+ }
7
+ export interface AssemblyPart {
8
+ name: string;
9
+ copy: number;
10
+ thickness: number;
11
+ matrix: number[];
12
+ outline: Poly[];
13
+ placed: boolean;
14
+ }
15
+ export declare function placementMatrix(p: Placement): number[];
16
+ export declare function besideAssembly(placed: AssemblyPart[], loose: AssemblyPart[], gap?: number): AssemblyPart[];
17
+ export declare function collisions(parts: AssemblyPart[], maxPairs?: number, eps?: number, pitch?: number, volume?: number): {
18
+ found: [AssemblyPart, AssemblyPart][];
19
+ complete: boolean;
20
+ };
@@ -0,0 +1,20 @@
1
+ import type { Poly } from './geometry.js';
2
+ export type Vec3 = [number, number, number];
3
+ export interface Placement {
4
+ at: Vec3;
5
+ rotate: Vec3;
6
+ }
7
+ export interface AssemblyPart {
8
+ name: string;
9
+ copy: number;
10
+ thickness: number;
11
+ matrix: number[];
12
+ outline: Poly[];
13
+ placed: boolean;
14
+ }
15
+ export declare function placementMatrix(p: Placement): number[];
16
+ export declare function besideAssembly(placed: AssemblyPart[], loose: AssemblyPart[], gap?: number): AssemblyPart[];
17
+ export declare function collisions(parts: AssemblyPart[], maxPairs?: number, eps?: number, pitch?: number, volume?: number): {
18
+ found: [AssemblyPart, AssemblyPart][];
19
+ complete: boolean;
20
+ };