@mylasertools/kerfscript 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/CLA.md +8 -5
  3. package/COMMERCIAL-LICENSE.md +23 -28
  4. package/LANGUAGE.md +194 -60
  5. package/README.md +89 -39
  6. package/THIRD-PARTY-NOTICES.md +68 -4
  7. package/TRADEMARKS.md +16 -10
  8. package/dist/cli.js +773 -297
  9. package/dist/evaluator.d.cts +26 -0
  10. package/dist/evaluator.d.ts +26 -0
  11. package/dist/export/dxf.d.cts +1 -1
  12. package/dist/export/dxf.d.ts +1 -1
  13. package/dist/export/svg.d.cts +2 -0
  14. package/dist/export/svg.d.ts +2 -0
  15. package/dist/font.d.cts +3 -0
  16. package/dist/font.d.ts +3 -0
  17. package/dist/functions.d.cts +21 -2
  18. package/dist/functions.d.ts +21 -2
  19. package/dist/index.cjs +14 -14
  20. package/dist/index.d.cts +2 -2
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +14 -14
  23. package/dist/lexer.d.cts +4 -1
  24. package/dist/lexer.d.ts +4 -1
  25. package/dist/parser.d.cts +1 -1
  26. package/dist/parser.d.ts +1 -1
  27. package/dist/render.d.cts +3 -2
  28. package/dist/render.d.ts +3 -2
  29. package/dist/values.d.cts +10 -2
  30. package/dist/values.d.ts +10 -2
  31. package/dist/wasm.d.cts +1 -0
  32. package/dist/wasm.d.ts +1 -0
  33. package/docs/cli.md +69 -28
  34. package/docs/compatibility.md +40 -65
  35. package/docs/customizer.md +40 -28
  36. package/docs/desktop.md +96 -0
  37. package/docs/getting-started.md +48 -31
  38. package/docs/javascript.md +108 -51
  39. package/docs/kerf.md +36 -39
  40. package/docs/layers.md +71 -37
  41. package/docs/openscad.md +52 -48
  42. package/docs/parts-and-sheets.md +45 -31
  43. package/docs/versions.md +20 -30
  44. package/docs/webassembly.md +131 -0
  45. package/fonts/OFL.txt +92 -0
  46. package/package.json +10 -6
@@ -1,24 +1,24 @@
1
1
  # Getting started
2
2
 
3
- This guide takes you from an empty file to a cut file in about ten minutes.
4
- You need nothing installed: the [playground](https://kerfscript.mylasertools.com/playground/)
5
- runs KerfScript in your browser.
3
+ Make a small sign with two hanging holes and engraved text. You can follow
4
+ this guide in the [playground](https://kerfscript.mylasertools.com/playground/)
5
+ without installing anything.
6
6
 
7
7
  ## Your first part
8
8
 
9
- Open the playground, clear the editor and type:
9
+ Clear the playground editor and enter:
10
10
 
11
11
  ```kerf
12
12
  rect([80, 50], r = 4);
13
13
  ```
14
14
 
15
- That is a rounded rectangle, 80 by 50 mm. Units are always millimetres. A
16
- shape that is not on any other layer is **cut**, so this is a part you could
17
- cut out now.
15
+ This draws an 80 × 50 mm rectangle with 4 mm corner radii. KerfScript uses
16
+ millimetres for lengths and degrees for angles. Shapes go on the cut layer
17
+ unless you assign another layer.
18
18
 
19
19
  ## Holes
20
20
 
21
- `difference()` keeps the first shape and removes the rest from it:
21
+ Replace the rectangle with this code:
22
22
 
23
23
  ```kerf
24
24
  difference() {
@@ -28,13 +28,16 @@ difference() {
28
28
  }
29
29
  ```
30
30
 
31
- `translate([x, y])` moves what follows it. The y axis points up, and
32
- `rect()` starts at `[0, 0]` unless you pass `center = true`.
31
+ `difference()` subtracts the two circles from the rectangle. Each hole is
32
+ 5 mm across, centred 10 mm from a side and halfway up the sign.
33
+
34
+ `translate([x, y])` moves a shape. The x axis points right and the y axis
35
+ points up. A rectangle starts at `[0, 0]`; use `center = true` to centre it
36
+ on that point.
33
37
 
34
38
  ## Engraving and scoring
35
39
 
36
- Wrap shapes in `engrave()` to engrave their area, or `score()` to mark their
37
- outline without cutting through:
40
+ Add a scored border and engraved text:
38
41
 
39
42
  ```kerf
40
43
  difference() {
@@ -43,15 +46,19 @@ difference() {
43
46
  translate([70, 25]) circle(d = 5);
44
47
  }
45
48
  score() translate([20, 10]) rect([40, 30], r = 2);
46
- engrave() translate([40, 25]) text("Oak", size = 10, halign = "center", valign = "center");
49
+ engrave() translate([40, 25])
50
+ text("Oak", size = 10, halign = "center", valign = "center");
47
51
  ```
48
52
 
49
- Engraving and scoring are clipped to the material, so nothing is marked over
50
- a hole or off the edge. [Layers](layers.md) explains the rest.
53
+ `score()` marks the border's outline. `engrave()` fills the letters. Both
54
+ stay within the sign's cut outline, excluding the holes. The playground
55
+ supplies a font for this example.
56
+
57
+ See [Layers](layers.md) for custom layers and guide lines.
51
58
 
52
59
  ## Settings
53
60
 
54
- A top-level variable with a comment in square brackets becomes a slider:
61
+ Give dimensions names so you can change them without editing every shape:
55
62
 
56
63
  ```kerf
57
64
  width = 80; // [40:5:200]
@@ -67,32 +74,42 @@ engrave() translate([width / 2, height / 2])
67
74
  text(label, size = 10, halign = "center", valign = "center");
68
75
  ```
69
76
 
70
- Move a slider in the playground and the number in the code changes with it.
71
- [Settings](customizer.md) covers groups, captions and menus.
77
+ The comments create sliders for width and height. `[40:5:200]` means a
78
+ minimum of 40, a step of 5 and a maximum of 200. `label` becomes a text
79
+ field. The text and holes stay centred as the dimensions change.
80
+
81
+ In the playground, changing a control also updates its value in the code.
82
+ See [Settings](customizer.md) for menus, groups and captions.
72
83
 
73
84
  ## Kerf
74
85
 
75
- The laser burns away a thin line of material, the **kerf**. Tell KerfScript
76
- how wide it is and parts come out the size you typed:
86
+ The beam removes some material along each cut. To account for that width,
87
+ add your measured kerf near the top of the program:
77
88
 
78
89
  ```kerf
79
90
  $kerf = 0.15;
80
91
  ```
81
92
 
82
- Cut the [calibration square](../examples/calibration-square.kerf) once to
83
- measure yours. [Kerf](kerf.md) explains how.
93
+ Here, `0.15` is an example in millimetres. Use the
94
+ [calibration square](../examples/calibration-square.kerf) to measure a value
95
+ for your machine and material. [Kerf](kerf.md) explains the measurement and
96
+ how it affects the exported outlines.
84
97
 
85
98
  ## Getting the file
86
99
 
87
- In the playground, press **SVG** or **DXF**. Open the SVG in your laser
88
- software: cut lines are red, score lines blue and engraving black, each on its
89
- own layer. Set power, speed and the order of operations there, as you would
90
- for any other file. [Laser software](compatibility.md) has notes for the
91
- programs we have checked.
100
+ Choose **SVG** or **DXF** in the playground, then open the file in your laser
101
+ software. Assign the cut, score and engrave layers their power, speed and
102
+ operation order. In DXF, set engraved areas to fill.
103
+
104
+ If you used `$kerf`, leave kerf compensation off in the laser software to
105
+ avoid applying it twice. The [import guide](compatibility.md) covers sizes,
106
+ layers and tested programs.
107
+
108
+ Download **.kerf** to keep an editable copy of the design.
92
109
 
93
110
  ## Next
94
111
 
95
- - [Layers](layers.md): cut, score, engrave and your own layers
96
- - [Parts and sheets](parts-and-sheets.md): several pieces, quantities, materials
97
- - [Coming from OpenSCAD](openscad.md): what is the same and what is not
98
- - [Language reference](../LANGUAGE.md): everything in one page
112
+ - [Layers](layers.md): separate cutting, scoring and engraving.
113
+ - [Parts and sheets](parts-and-sheets.md): arrange several parts by material.
114
+ - [Coming from OpenSCAD](openscad.md): adapt an existing 2D design.
115
+ - [Language reference](../LANGUAGE.md): look up syntax and functions.
@@ -1,8 +1,8 @@
1
1
  # JavaScript API
2
2
 
3
- The same interpreter that runs the command line and the playground is a
4
- TypeScript library with no native code. It runs in browsers, web workers,
5
- Node.js and edge runtimes.
3
+ Add KerfScript rendering to a JavaScript application with the
4
+ `@mylasertools/kerfscript` package. The library runs in browsers and Node.js
5
+ and includes TypeScript definitions.
6
6
 
7
7
  ```sh
8
8
  npm install @mylasertools/kerfscript
@@ -10,89 +10,146 @@ npm install @mylasertools/kerfscript
10
10
 
11
11
  ## render()
12
12
 
13
+ Pass a program as a string. The result contains its geometry, files, parts
14
+ and warnings:
15
+
13
16
  ```js
14
17
  import { render } from '@mylasertools/kerfscript';
15
18
 
19
+ const source = 'width = 80; rect([width, 50]);';
16
20
  const result = render(source, {
17
- params: { width: 200 },
18
- fonts: { Inter: await (await fetch('/Inter.ttf')).arrayBuffer() },
19
- defaultFont: 'Inter',
21
+ params: { width: 120 },
20
22
  sheet: [600, 400],
23
+ margin: 0,
21
24
  });
25
+
26
+ console.log(result.svg);
22
27
  ```
23
28
 
29
+ This exports a 120 × 50 mm rectangle. `params` overrides `width` without
30
+ changing the source string.
31
+
24
32
  ### Options
25
33
 
26
- | Option | Meaning |
27
- | ------------- | ------------------------------------------------------------------------------------ |
28
- | `params` | setting values by name, overriding the top-level assignments |
29
- | `fonts` | font files by name, as `ArrayBuffer` or `Uint8Array` |
30
- | `defaultFont` | font used when `text()` names none |
31
- | `resolve` | `(path) => source` for `use <path>` and `include <path>` beyond the standard library |
32
- | `kerf` | override kerf compensation for every part |
33
- | `layout` | `"pack"` or `"none"` |
34
- | `sheet` | `[width, height]` in mm |
35
- | `gap` | space between packed parts |
36
- | `margin` | space around the sheet edge (default: `gap`) |
37
- | `guides` | include guide layer geometry |
38
- | `maxSteps` | step limit before the program is stopped |
34
+ | Option | Value |
35
+ | ------------- | ---------------------------------------------------------------------- |
36
+ | `params` | Parameter values keyed by name |
37
+ | `fonts` | Font files keyed by name, as `ArrayBuffer` or `Uint8Array` |
38
+ | `defaultFont` | Name to use when `text()` does not specify a font |
39
+ | `resolve` | `(path) => source` callback for libraries outside the standard library |
40
+ | `kerf` | Kerf override for every part, in millimetres |
41
+ | `layout` | `"pack"` or `"none"` |
42
+ | `sheet` | `[width, height]` in millimetres |
43
+ | `gap` | Space between parts, in millimetres |
44
+ | `margin` | Space at sheet edges, in millimetres; defaults to `gap` |
45
+ | `guides` | Whether to include guide geometry |
46
+ | `maxSteps` | Evaluation step limit |
47
+ | `limits` | Memory limits, see below; any you leave out keep their default |
48
+
49
+ Supply a font for programs that use text:
39
50
 
40
- ### Result
51
+ ```js
52
+ const response = await fetch('/fonts/Inter.ttf');
53
+ const font = await response.arrayBuffer();
54
+ const result = render(source, {
55
+ fonts: { Inter: font },
56
+ defaultFont: 'Inter',
57
+ });
58
+ ```
59
+
60
+ The URL should point to a font file provided by your application.
41
61
 
42
- | Field | Contents |
43
- | ----------------- | ---------------------------------------------------------------------------------------------------------- |
44
- | `svg` | every sheet in one SVG, for previews |
45
- | `width`, `height` | size of that combined SVG in mm |
46
- | `sheets` | one entry per sheet: `width`, `height`, `material`, `thickness`, `svg`, `dxf`, `paths`, `items` |
47
- | `parts` | `name`, `qty`, `width`, `height`, `pieces`, `cutLength`, `thickness`, `kerf`, `material` |
48
- | `paths` | per layer: `name`, `mode`, `color`, `d` (all path data), `regions` (closed outlines), `lines` (open lines) |
49
- | `layers` | the layer definitions in output order |
50
- | `warnings` | problems that did not stop the render |
51
- | `echoes` | `echo()` output |
62
+ ### Result
52
63
 
53
- `paths` is for apps that draw the result themselves. Fill `regions` with the
54
- even-odd rule on `fill` layers, and stroke `lines`.
64
+ | Field | Contents |
65
+ | ----------------- | ------------------------------------------------------------------------------------- |
66
+ | `svg` | Combined SVG containing all sheets |
67
+ | `width`, `height` | Combined SVG dimensions in millimetres |
68
+ | `sheets` | Each sheet's dimensions, material, thickness, SVG, DXF, paths and placed items |
69
+ | `parts` | Part names, quantities, dimensions, piece counts, cut lengths and material properties |
70
+ | `paths` | Layer paths with `name`, `mode`, `color`, `d`, `regions` and `lines` |
71
+ | `layers` | Layer definitions in output order |
72
+ | `warnings` | Problems that did not stop the render |
73
+ | `echoes` | Output from the program's `echo()` calls |
74
+
75
+ Use `result.svg` for a combined preview. Use `result.sheets[i].svg` or
76
+ `.dxf` to export individual sheets, particularly when they use different
77
+ materials.
78
+
79
+ For a custom preview, `d` contains all paths in a layer. `regions` contains
80
+ closed outlines and `lines` contains open paths. On a `fill` layer, draw
81
+ regions with the even-odd fill rule and draw open paths as strokes.
55
82
 
56
83
  ## parameters()
57
84
 
85
+ Read a program's editable settings without rendering its geometry:
86
+
58
87
  ```js
59
88
  import { parameters } from '@mylasertools/kerfscript';
60
89
 
61
- parameters(source).parameters;
62
- // [{ name, caption, group, type, initial, min, max, step, options, span }]
90
+ const source = 'width = 80; // [40:5:200]\nrect([width, 50]);';
91
+ const settings = parameters(source).parameters;
63
92
  ```
64
93
 
65
- `type` is `"number"`, `"string"` or `"boolean"`; a list of numbers is
66
- `"number"` with a list `initial`, as in OpenSCAD's parameter JSON. `span` is
67
- `[start, end]`, the offsets of the value in the source, so an editor can change
68
- a value without touching anything else:
94
+ Each setting includes `name`, `caption`, `group`, `type`, `initial` and
95
+ `span`. Ranges add `min`, `max` and `step`; menus add `options`.
96
+
97
+ `type` is `"number"`, `"string"` or `"boolean"`. A numeric vector has
98
+ type `"number"` and an array as its `initial` value.
99
+
100
+ `span` gives the source offsets `[start, end]` of the value. An editor can
101
+ use them to update one setting:
69
102
 
70
103
  ```js
71
- const p = parameters(source).parameters.find((p) => p.name === 'width');
72
- const next = source.slice(0, p.span[0]) + '200' + source.slice(p.span[1]);
104
+ const width = settings.find((p) => p.name === 'width');
105
+ if (width) {
106
+ const next = source.slice(0, width.span[0]) + '120' + source.slice(width.span[1]);
107
+ }
73
108
  ```
74
109
 
110
+ Read the settings again after editing the source; the offsets refer to the
111
+ string passed to `parameters()`.
112
+
75
113
  ## Errors
76
114
 
77
- Errors are thrown as `KerfError` with `line` and `col`. They also carry
78
- `echoes`, the `echo()` output up to the error, and `missingFonts` when a font
79
- was not supplied, so an app can load those fonts and render again.
115
+ Language errors use `KerfError`. Its `line` and `col` identify the source
116
+ location when available, and `echoes` contains output produced before the
117
+ error. `missingFonts` lists fonts the application needs to supply.
80
118
 
81
119
  ```js
82
120
  import { KerfError, render } from '@mylasertools/kerfscript';
83
121
 
84
122
  try {
85
- render(source, { fonts });
86
- } catch (e) {
87
- if (e instanceof KerfError && e.missingFonts) {
88
- // load e.missingFonts, then render again
123
+ render('rect([80, 50]);');
124
+ } catch (error) {
125
+ if (error instanceof KerfError) {
126
+ console.error(error.message, error.line, error.col);
127
+ } else {
128
+ throw error;
89
129
  }
90
130
  }
91
131
  ```
92
132
 
93
133
  ## Running untrusted code
94
134
 
95
- Programs cannot read files or the network: `use` and `include` reach only the
96
- standard library and whatever your `resolve` returns. Step and recursion limits
97
- stop runaway programs. A render is still ordinary synchronous work, so run
98
- it in a web worker, and stop the worker if it takes too long.
135
+ Run browser renders in a web worker so they do not block the page. Set a
136
+ timeout and terminate the worker if it takes too long. Evaluation limits
137
+ bound program steps and recursion, but geometry work also takes time.
138
+
139
+ Size limits stop a program before it builds more than a browser tab can
140
+ usually hold: part copies, circle segments, list sizes, text length, shape
141
+ points, exported points and sheets. They bound what a program makes, not
142
+ every byte the renderer uses, so keep the worker's timeout as well, and run
143
+ programs you did not write where the page can end them. The defaults are in
144
+ [Limits](../LANGUAGE.md#limits) and in `DEFAULT_LIMITS`. Lower them with the
145
+ `limits` option if your worker has little memory:
146
+
147
+ ```js
148
+ import { render, DEFAULT_LIMITS } from '@mylasertools/kerfscript';
149
+
150
+ render(source, { limits: { maxPoints: DEFAULT_LIMITS.maxPoints / 2 } });
151
+ ```
152
+
153
+ KerfScript programs have no direct file or network access. `use` and
154
+ `include` can load the standard library and sources returned by your
155
+ `resolve` callback. Restrict that callback to libraries you intend to expose.
package/docs/kerf.md CHANGED
@@ -1,27 +1,25 @@
1
1
  # Kerf
2
2
 
3
- The laser beam burns away a thin line of material as it cuts. That width is
4
- the **kerf**, usually somewhere between 0.05 and 0.3 mm depending on the
5
- machine, the lens, the material and its thickness. Without compensation every
6
- part comes out smaller than drawn by about one kerf, and every hole bigger.
7
- For a sign that hardly matters. For finger joints and press fits it decides
8
- whether the box holds together.
3
+ Kerf is the width of material removed by a laser cut. Allowing for it helps
4
+ parts finish at the intended size and joints fit together. Measure it for
5
+ the material and cutting settings you use.
9
6
 
10
7
  ## Setting it
11
8
 
9
+ Set `$kerf` in millimetres:
10
+
12
11
  ```kerf
13
12
  $kerf = 0.15;
14
13
  ```
15
14
 
16
- KerfScript offsets every cut outline outward by half the kerf when it writes
17
- the file. The beam then runs half a kerf outside the part and burns away
18
- material that was never part of it, so the part ends up the size you typed.
19
- Holes are offset inward the same way, so they end up the size you typed too.
15
+ With this value, KerfScript moves an outer cut path outward by 0.075 mm and
16
+ a hole's path inward by the same amount. The offset compensates for material
17
+ removed on either side of the beam.
20
18
 
21
- Only closed outlines on cut layers are offset. Engraving, scoring and open cut
22
- lines (like a living hinge) are left as drawn.
19
+ Only closed cut outlines receive the offset. Engraving, scoring and open
20
+ cuts such as living hinges keep their original paths.
23
21
 
24
- You can set the kerf for one material only, together with its thickness:
22
+ To use a value for a particular material, set it inside `material()`:
25
23
 
26
24
  ```kerf
27
25
  material(thickness = 4, kerf = 0.12, name = "birch") {
@@ -29,42 +27,41 @@ material(thickness = 4, kerf = 0.12, name = "birch") {
29
27
  }
30
28
  ```
31
29
 
32
- On the command line, `--kerf 0.1` overrides the value for every part.
30
+ On the command line, `--kerf 0.1` overrides the value for every part. Apply
31
+ kerf compensation in either KerfScript or your laser software, once per job.
33
32
 
34
33
  ## Measuring your kerf
35
34
 
36
35
  1. Cut the [calibration square](../examples/calibration-square.kerf) with
37
- `$kerf = 0`.
38
- 2. Measure the outside of the square with calipers, across the middle.
39
- 3. Your kerf is `100 - measured`. If the square measures 99.82 mm, use
40
- `$kerf = 0.18`.
41
- 4. Cut it again with that value. The square should now be 100 mm and the hole
42
- 20 mm. If the square is still small and the hole big, raise `$kerf` a
43
- little; if the square is big, lower it.
36
+ `$kerf = 0`. Leave any offset in your laser software at zero too.
37
+ 2. Measure the square across the middle with calipers.
38
+ 3. Subtract that measurement from 100 mm. A square measuring 99.82 mm gives
39
+ a kerf of 0.18 mm.
40
+ 4. Set `$kerf = 0.18` and cut another sample. Check the outside against
41
+ 100 mm and the hole against 20 mm, then adjust the value if needed.
44
42
 
45
- Measure each material you use. Thicker material usually has a wider kerf, and
46
- the kerf can differ between the top and bottom face.
43
+ Repeat the measurement when the material or cutting settings change. Thick
44
+ material can have a different cut width at its top and bottom faces.
47
45
 
48
46
  ## Checking a fit
49
47
 
50
- For joints, the [kerf and fit test](../examples/kerf-test.kerf) cuts a row of
51
- slots from slightly narrower to slightly wider than `$thickness`. Push a scrap
52
- of the same sheet into each slot:
48
+ After calibrating kerf, measure your sheet's thickness and set `$thickness`
49
+ to that measurement. The nominal thickness on a material label can differ
50
+ from the sheet itself.
53
51
 
54
- - if the slot marked `0` is snug, your kerf and thickness are right;
55
- - if a `+` slot fits best, the sheet is thicker than `$thickness` says
56
- (plywood often is);
57
- - if a `-` slot fits best, it is thinner.
52
+ The [kerf and fit test](../examples/kerf-test.kerf) cuts slots slightly
53
+ narrower and wider than `$thickness`. Try a scrap from the same sheet in each
54
+ slot. A positive label means a wider slot; a negative label means a narrower
55
+ one. Choose the fit you need and use the result to refine your dimensions.
58
56
 
59
- Set `$thickness` to the thickness you measured rather than the one on the
60
- label, and joints follow.
57
+ Both kerf and sheet thickness affect the fit. A tight or loose slot alone
58
+ does not tell you which value needs adjusting.
61
59
 
62
60
  ## Limits
63
61
 
64
- - A hole, slot or notch narrower than the kerf cannot be cut: the beam is
65
- wider than the gap, so compensation closes it. KerfScript warns when that
66
- happens to a hole.
67
- - Thin bridges and fingers come out the right size but are fragile, and can
68
- char in thin material. Keep them several times wider than the kerf.
69
- - The beam is not perfectly straight in thick material, so cut faces taper a
70
- little. Compensation works on the average width.
62
+ - Compensation can close a hole or notch narrower than the kerf. KerfScript
63
+ warns when a hole disappears.
64
+ - Narrow bridges and fingers may be fragile or char during cutting even
65
+ when their dimensions are correct. Test small features in your material.
66
+ - Compensation adjusts the 2D outline. It does not remove taper from the
67
+ cut face in thick material.
package/docs/layers.md CHANGED
@@ -1,17 +1,19 @@
1
1
  # Layers
2
2
 
3
- Every shape in KerfScript is on a layer, and the layer decides what the
4
- laser does with it. Shapes that are not wrapped in a layer module are on
5
- `cut`.
3
+ Layers separate cutting, scoring and engraving in the exported file. Use
4
+ them to give different parts of a design different operations in your laser
5
+ software. Unassigned shapes go on the `cut` layer.
6
6
 
7
7
  ## The built-in layers
8
8
 
9
- | Layer | Wrap shapes in | Colour in the file | What the laser does |
10
- | --------- | -------------- | ------------------ | ------------------------------------------------- |
11
- | `engrave` | `engrave()` | black `#000000` | engraves the area of the shapes |
12
- | `score` | `score()` | blue `#0000ff` | marks lines and outlines without cutting through |
13
- | `cut` | `cut()` | red `#ff0000` | cuts the outlines through, with kerf compensation |
14
- | `guide` | `guide()` | light blue | nothing; shown only when you ask for guide lines |
9
+ | Layer | Wrap shapes in | Colour in the file | Intended operation |
10
+ | --------- | -------------- | ------------------ | -------------------------- |
11
+ | `engrave` | `engrave()` | black `#000000` | Fill an area |
12
+ | `score` | `score()` | blue `#0000ff` | Mark a line or outline |
13
+ | `cut` | `cut()` | red `#ff0000` | Cut through the material |
14
+ | `guide` | `guide()` | light blue | Show construction geometry |
15
+
16
+ This coaster has a cut outline and hole, an engraved ring and a scored border:
15
17
 
16
18
  ```kerf
17
19
  difference() {
@@ -22,53 +24,85 @@ engrave() difference() { circle(d = 74); circle(d = 68); }
22
24
  score() circle(d = 84);
23
25
  ```
24
26
 
25
- `#` and `%` in front of a module call move it to the guide layer, which is
26
- handy for checking where something is without cutting it.
27
+ The layer describes the operation. Set its power, speed and passes in your
28
+ laser software.
29
+
30
+ Place `#` or `%` before a module call to move it to the guide layer.
31
+ Guides are excluded from the output unless you request them with
32
+ `--guides` on the command line or `guides: true` in the API.
27
33
 
28
34
  ## Areas and lines
29
35
 
30
- Layers hold two kinds of geometry:
36
+ Shapes such as `rect()`, `circle()` and `text()` define areas. On an
37
+ engrave layer, those areas are filled. On cut and score layers, their
38
+ outlines become paths.
39
+
40
+ `line()`, `polyline()`, `arc()` and `living_hinge()` produce lines. They
41
+ remain lines on every layer. A living hinge, for example, is a pattern of
42
+ open cuts within a part.
43
+
44
+ Use `stroke(width)` to turn a line into an area of a specified width. Use
45
+ `outline()` to turn an area's boundary into lines.
46
+
47
+ ### Overlapping shapes merge
48
+
49
+ The areas inside one `score()` are merged first, like a `union()`, and
50
+ only the outline of the result is scored. Two nested circles become one
51
+ disc, so this scores a single ring:
52
+
53
+ ```kerf
54
+ circle(d = 60);
55
+ score() {
56
+ circle(d = 50);
57
+ circle(d = 40); // inside the first circle: merged away
58
+ }
59
+ ```
60
+
61
+ To score every shape's own outline, turn each one into lines first with
62
+ `outline()`. Lines are never merged, so this scores both rings:
31
63
 
32
- - **Areas** come from shapes like `rect()`, `circle()` and `text()`. On
33
- `engrave` the area is filled; on `cut` and `score` its outline is followed.
34
- - **Lines** come from `line()`, `polyline()`, `arc()` and `living_hinge()`.
35
- They are followed as they are, on any layer. On `cut` they are cut through,
36
- which is how a living hinge is made.
64
+ ```kerf
65
+ circle(d = 60);
66
+ score() {
67
+ outline() circle(d = 50);
68
+ outline() circle(d = 40);
69
+ }
70
+ ```
37
71
 
38
- `stroke(width)` turns lines into areas, for engraving a line of a given
39
- width. `outline()` turns areas into their outline lines.
72
+ One `outline()` around both circles would not help: it merges them before
73
+ it traces them. The same rule holds on the cut layer and on custom
74
+ layers. Use `difference()` when you want the area between two outlines,
75
+ such as a ring to engrave.
40
76
 
41
77
  ## The material
42
78
 
43
- When a part is finished, KerfScript takes the union of everything on the cut
44
- layer as the **material**. Then:
79
+ Closed shapes on a part's cut layer define its material. KerfScript clips
80
+ engraving, scoring and open cut lines to that area, excluding holes. Start
81
+ with a cut shape when adding marks to a part.
45
82
 
46
- - engraving and scoring are clipped to the material, so nothing is marked
47
- inside a hole or off the part;
48
- - cut lines are clipped to the material too;
49
- - if the material falls apart into separate pieces, you get a warning,
50
- because that usually means a missing bridge.
83
+ If the cut shapes form separate pieces, KerfScript reports a warning. Check
84
+ for a missing connection or define the pieces as separate parts.
51
85
 
52
86
  ## Your own layers
53
87
 
54
- `layer(name, mode, color)` makes a layer of your own, for example a second
55
- engraving pass at a different power:
88
+ Use `layer(name, mode, color)` when you need another operation, such as a
89
+ second engraving depth:
56
90
 
57
91
  ```kerf
58
92
  rect([60, 40]);
59
- layer("deep", mode = "fill", color = "#00aa00") translate([10, 10]) rect([20, 20]);
93
+ layer("deep", mode = "fill", color = "#00aa00")
94
+ translate([10, 10]) rect([20, 20]);
60
95
  engrave() translate([40, 20]) circle(d = 12);
61
96
  ```
62
97
 
63
- Modes are `"cut"`, `"line"` (score), `"fill"` (engrave) and `"guide"`.
98
+ The rectangle and circle use separate engraving layers. You can assign each
99
+ a different power in your laser software. Supported modes are `"cut"`,
100
+ `"line"`, `"fill"` and `"guide"`.
64
101
 
65
102
  ## Order in the file
66
103
 
67
- Layers are written in this order: engrave, your own layers, score, cut,
68
- guide. Within the cut layer, holes and inner cuts come before outer outlines,
69
- so a part does not drop out before its holes are cut.
104
+ KerfScript writes layers in this order: engrave, custom layers, score, cut,
105
+ then guide. Within a cut layer, inner paths precede outer outlines.
70
106
 
71
- That order is a good default, but it does not control your machine. Most
72
- laser software sorts and orders operations itself. Check the cut order, and
73
- set power, speed and passes for each layer, in your laser software before you
74
- run the job.
107
+ Laser software may reorder these paths. Check its job preview so engraving
108
+ runs before cutting and holes are cut before the surrounding part.