three-slicer 0.1.6 → 0.2.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 (43) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +310 -22
  3. package/THIRD-PARTY-NOTICES.md +37 -0
  4. package/components/README.md +2 -0
  5. package/components/dist/SettingsPanel.js +92 -92
  6. package/data/config-schema.json +698 -0
  7. package/data/filaments.js +1 -1
  8. package/data/preset-keys.json +1 -0
  9. package/data/printers.json +1 -1
  10. package/data/processes.js +1 -1
  11. package/data/ui-tree.json +342 -0
  12. package/engine/README.md +279 -3
  13. package/engine/examples/sla_headless.mjs +51 -0
  14. package/engine/index.d.ts +77 -5
  15. package/engine/index.js +15 -4
  16. package/engine/src/client.js +165 -0
  17. package/engine/src/data.js +3 -0
  18. package/engine/src/preset_file.js +99 -0
  19. package/engine/src/settings.js +232 -0
  20. package/engine/src/sla_core.js +188 -0
  21. package/engine/src/sla_request.js +185 -0
  22. package/engine/src/slicer.worker.js +76 -4
  23. package/engine/src/slicer_core.js +0 -0
  24. package/engine/src/slicer_core.mt.js +0 -0
  25. package/package.json +26 -11
  26. package/types/client.d.ts +88 -0
  27. package/types/components.d.ts +10 -0
  28. package/types/data/preset-keys.json.d.ts +20 -0
  29. package/types/data/printers.json.d.ts +2 -2
  30. package/types/data.d.ts +4 -0
  31. package/types/settings-keys.d.ts +107 -1
  32. package/types/settings.d.ts +116 -1
  33. package/types/sla.d.ts +167 -0
  34. package/types/viewer.d.ts +59 -1
  35. package/types/worker.d.ts +95 -0
  36. package/viewer/README.md +163 -3
  37. package/viewer/dist/Viewport.js +3633 -3137
  38. package/viewer/dist/model_loaders.js +1 -1
  39. package/viewer/dist/parse_3mf-C2X_W442.js +300 -0
  40. package/viewer/dist/parse_3mf.worker.js +1 -1
  41. package/viewer/styles.css +5 -0
  42. package/types/gen_settings_types.mjs +0 -70
  43. package/viewer/dist/parse_3mf-8oCnCJNK.js +0 -216
package/viewer/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ![The Prepare tab — drag-and-drop viewport with the printer, filament and process cards in the right sidebar](../../slice-sidebar.png)
6
6
 
7
7
  ```bash
8
- npm i three-slicer # react, react-dom and three are peer dependencies — npm installs them for you
8
+ npm i three-slicer react react-dom three # the three are optional peers; the viewer needs all of them
9
9
  ```
10
10
 
11
11
  ```jsx
@@ -14,12 +14,26 @@ import Viewport from 'three-slicer/viewer'
14
14
 
15
15
  function App() {
16
16
  const [settings, setSettings] = useState({}) // OrcaSlicer schema keys; sparse — defaults fill the rest
17
- return <Viewport settings={settings} setSettings={setSettings} />
17
+ // The container matters — see Layout below.
18
+ return (
19
+ <div style={{ position: 'relative', width: '100%', height: '100vh' }}>
20
+ <Viewport settings={settings} setSettings={setSettings} />
21
+ </div>
22
+ )
18
23
  }
19
24
  ```
20
25
 
21
26
  Styles are injected into the component's Shadow DOM root — nothing to import, and host CSS cannot collide.
22
27
 
28
+ ## Layout
29
+
30
+ **Give it a positioned, sized container.** The shadow host is `display: contents`, so it contributes no box of its
31
+ own, and the shell inside is `position: absolute; inset: 0`. It therefore fills the nearest *positioned* ancestor —
32
+ `position: relative` (or absolute/fixed) with a real height. Drop it into a plain static `<div>` and it escapes to
33
+ the initial containing block instead, which usually looks like the viewer covering the whole page.
34
+
35
+ There is no width or height prop: the component always fills that container, and resizes with it.
36
+
23
37
  Slice parameters are derived from `settings` via `three-slicer`'s schema mapping — pair it with `three-slicer/components`' `<SettingsPanel/>` sharing the same state for a full slicer UI.
24
38
 
25
39
  A `.3mf` written by a slicer (OrcaSlicer/BambuStudio save, MakerWorld download) imports as a **project**: plate layout, project settings, and support/material painting are restored, not just the meshes. Where a facet carries both paint kinds, material paint wins and the dropped support paint is reported.
@@ -49,7 +63,13 @@ Every panel can be switched off, and the values the component owns can be seeded
49
63
  - **`panels`** — `{name: false}` hides one. Everything is visible by default, so a host only ever opts out and a
50
64
  panel added in a later version does not vanish for hosts that listed the ones they wanted. `sidebar: false` drops
51
65
  the whole right column; the rest are `topBar`, `gizmoRail`, `objectToolbar`, `paintPanel`, `statsCard`, `plateBar`,
52
- `emptyHint`, `status`, `printerCard`, `filamentCard`, `objectList`, `previewControls`, `processCard`, `sliceBar`.
66
+ `emptyHint`, `status`, `printerCard`, `filamentCard`, `resinCard`, `objectList`, `previewControls`, `processCard`,
67
+ `sliceBar`. Which of `filamentCard`/`resinCard` renders follows the printer profile's technology: a profile whose
68
+ `printer_technology` says SLA swaps the filament card (and the prime tower, and the painting brushes) for the
69
+ resin card, and slicing routes to the kernel's `slice_sla` — PrusaSlicer's ported support/pad chain — with an
70
+ `.sl1` export instead of G-code (portrait masks, like every SL1-family machine). The preview lifts the model by
71
+ `stats.lift_layers` (pad + elevation), and a request the port does not cover (hollowing, organic trees,
72
+ `pad_around_object`) surfaces as a typed error instead of a wrong print.
53
73
  - **`gcode`** — G-code text drawn on the selected plate instead of a slice result. `parseGcode` recovers roles from
54
74
  `;TYPE:` (OrcaSlicer/PrusaSlicer/Cura), from `;_EXTRUSION_ROLE:` tags and from this kernel's own feature comments;
55
75
  bead width comes from `;WIDTH:` or, absent that, from E. What the file never states cannot be recovered: an
@@ -63,6 +83,55 @@ Every panel can be switched off, and the values the component owns can be seeded
63
83
  second while slicing.
64
84
  - **`onSliced`** — `{plate, stats, gcode}` when a slice is cached. Switching plate tabs does not re-fire it.
65
85
 
86
+ ## Keyboard and mouse
87
+
88
+ Which keys are live depends on the tab, and every one is ignored while an input has focus. Letter shortcuts match
89
+ the **physical** key, so they keep working under a non-Latin keyboard layout — with an IME on, the Korean layout
90
+ reports `'ㅁ'` for M, and matching on the character would silently kill every letter shortcut while leaving the
91
+ arrows and Delete working.
92
+
93
+ | Both tabs | |
94
+ | --- | --- |
95
+ | `Ctrl/⌘ + R` | Slice the current plate |
96
+ | `Ctrl/⌘ + C` / `V` / `X` | Copy · paste · cut the selection |
97
+ | `Ctrl/⌘ + K` or `Ctrl/⌘ + D` | Duplicate |
98
+ | `Ctrl/⌘ + A` | Select all |
99
+ | `Ctrl/⌘ + Z` | Undo — bound to the component root, so it never reaches your app's own Ctrl+Z |
100
+ | `Ctrl/⌘ + Shift + Z` or `Ctrl/⌘ + Y` | Redo |
101
+
102
+ | Prepare | |
103
+ | --- | --- |
104
+ | `M` or `G` · `R` · `S` | Move · rotate · scale gizmo |
105
+ | `Arrow keys` | Nudge 10mm — `Shift` for 1mm |
106
+ | `PageUp` / `PageDown` | Rotate ±45° |
107
+ | `Delete` / `Backspace` | Remove the selection |
108
+ | `Z` · `B` | Zoom to all · zoom to bed |
109
+ | `Esc` | Cancel the current tool |
110
+ | `?` | Shortcut help overlay |
111
+
112
+ | Preview | |
113
+ | --- | --- |
114
+ | `↑` / `↓` | Step one layer — `Shift` for ten |
115
+ | `L` · `T` | Single-layer view · travel moves |
116
+ | `Z` · `B` | Zoom to all · zoom to bed |
117
+ | `Esc` | Back to Prepare |
118
+
119
+ Mouse selection follows upstream's rules: a plain click **replaces** the selection, `Ctrl/⌘ + click` adds or
120
+ removes, a plain click on something already selected **keeps** the set (which is what makes dragging several
121
+ objects work), `Shift + drag` is a box select, and clicking empty space clears. Alt is not a de-select, in
122
+ upstream or here.
123
+
124
+ ## Undo and redo
125
+
126
+ The history covers the viewport's **own** state: object transforms, additions, removals, per-object extruder and
127
+ visibility. Fifty steps, with repeats of the same action inside half a second folded into one — so a held arrow key
128
+ undoes as a single move rather than forty.
129
+
130
+ What it deliberately does not cover, and why the boundary sits there: `settings` is **yours**, passed in as a prop,
131
+ so nothing inside the component can put it back. Painting is outside it too (restoring a step would need a kernel
132
+ re-`prepare` for every entry), as are the prime tower and the plate count, which write host settings. If you want
133
+ those undoable, undo them on your side of the prop.
134
+
66
135
  ## Materials and painting
67
136
 
68
137
  The filament card holds **one material per extruder**: add an extruder, pick its colour, and pick its filament preset from the vendor catalog (`filamentPresets()` in `three-slicer/settings` — the shortlist is the printer model's own recommendation, the full list everything compatible). Selecting a row aims the material picker and the settings form at that extruder; the colours feed the object meshes, the prime tower and the preview's **Filament** view type, which colours every segment by the extruder that printed it.
@@ -76,6 +145,97 @@ Two limits to expect while using it:
76
145
 
77
146
  If you consume `three-slicer/viewer/toolpath` directly, note that the stride-8 role field `paths[k+3]` now carries the printing extruder alongside the role as `role + tool * 16`. Mask it — `& 15` for the role, `>>> 4` for the tool. Streams produced before this encoding are entirely below 16 and decode to their own role with tool 0, so older data still reads correctly.
78
147
 
148
+ ## Turning behaviour off
149
+
150
+ `panels` controls what is drawn; `features` controls what the component *does* outside its own box. Same rule —
151
+ opt-out only, everything on unless you say `false`, an unknown key stays on.
152
+
153
+ ```jsx
154
+ <Viewport
155
+ gcode={text}
156
+ panels={{ sidebar: false, topBar: false }}
157
+ features={{ warmup: false, shortcuts: false, logs: false }}
158
+ />
159
+ ```
160
+
161
+ | Key | Off means |
162
+ | --- | --- |
163
+ | `shortcuts` | No keyboard bindings. They are installed on `window`, so while the viewer is mounted they fire wherever focus is (inputs excepted) — and `Ctrl+C` preventDefaults, which takes a host page's own copy. Turn them off when your app has its own. |
164
+ | `warmup` | The WASM kernel is not loaded on mount. Nothing is downloaded or compiled until something actually slices — which for a `gcode`-only viewer is never, so this is several megabytes and a thread pool you stop paying for. |
165
+ | `drop` | No drag and drop onto the canvas. |
166
+ | `filePicker` | The file dialog never opens, from any of the buttons that open it. |
167
+ | `contextMenu` | No right-click menu. The browser's own menu stays suppressed over the canvas either way — OrbitControls preventDefaults `contextmenu` itself. |
168
+ | `logs` | No console output, from the component or from the slice worker. |
169
+
170
+ ### Read-only panels
171
+
172
+ A sidebar panel can also be shown but not editable — the shape you want when the host picks the printer, process
173
+ and filament itself and does not want the user changing them. It is a third value on the same `panels` key:
174
+
175
+ ```jsx
176
+ <Viewport
177
+ settings={presetSettings}
178
+ panels={{ printerCard: 'readonly', filamentCard: 'readonly', processCard: 'readonly' }}
179
+ />
180
+ ```
181
+
182
+ Lockable: `sidebar`, `printerCard`, `filamentCard`, `objectList`, `towerCard`, `previewControls`, `processCard`,
183
+ `sliceBar`. `sidebar: 'readonly'` locks the whole column at once; the per-card keys are for mixing, such as a
184
+ locked printer card above a live object list. Every other panel takes `true`/`false` only — a `'readonly'` that
185
+ silently did nothing on half the keys would be worse than not offering it.
186
+
187
+ The panel is drawn normally and keeps showing real values; it just cannot be clicked, typed into or tabbed to, and
188
+ that includes any node you passed into it (`motionPanel`, `filamentPanel`, `processPanel`). Implemented with the
189
+ `inert` attribute, so it needs Chrome 102+, Safari 15.5+ or Firefox 112+.
190
+
191
+ **It locks the UI path, not the value.** `settings` is still your prop, and other paths inside the viewer still
192
+ write it — importing a 3mf project applies that project's settings whether or not the cards are locked.
193
+
194
+ ### Printer preset files
195
+
196
+ The printer card carries **Load** and **Save**. Save writes an OrcaSlicer preset: a machine `.json` on its own, or
197
+ an `.orca_printer` bundle when the settings map also names a process or filament preset (the cards record those as
198
+ `printer_settings_id` / `print_settings_id` / `filament_settings_id`, so nothing extra has to be tracked). Load
199
+ accepts the same set upstream's dialog does — `.json`, `.orca_printer`, `.orca_bundle`, `.orca_filament`, `.zip` —
200
+ and applies every preset it finds, clearing that type's keys first.
201
+
202
+ `inherits` is followed through the shipped printer catalog, so a vendor file loads with its bed intact; when the
203
+ parent is unknown the file's own values are still applied and the notice says so. Codec details and the reason
204
+ that matters are in the [package README](../README.md#preset-files).
205
+
206
+ Load opens a file dialog, so it respects `features.filePicker`; Save goes through `onExport` like every other
207
+ download.
208
+
209
+ ### Taking the saves
210
+
211
+ The save buttons are inside the component, so `onExport` is the only way to send a file somewhere other than the
212
+ browser's download folder. It sees the 3mf project, the STL and each plate's G-code. Return truthy to say you
213
+ handled it:
214
+
215
+ ```jsx
216
+ <Viewport onExport={(file, filename) => { uploadToServer(file, filename); return true }} />
217
+ ```
218
+
219
+ Return nothing and the download happens as well, which is useful for logging what was saved without changing it.
220
+
221
+ ## What the host cannot drive
222
+
223
+ The component owns its scene, and the props are the whole interface — there is no ref, no imperative handle, and no
224
+ `models` prop. Worth knowing before you design around it:
225
+
226
+ - **Models enter through the UI only** — the file dialog or drag and drop. A host cannot hand a mesh in, and cannot
227
+ remove or transform one. What it *can* do is watch: the `objects` event reports `{id, name, extruder, visible}`
228
+ for every object as the set changes.
229
+ - **Slicing is triggered from the UI or by `defaultAutoSlice`**, which unlike the in-app toggle also performs the
230
+ first slice — that is what makes a panel-less embed able to slice at all. The result arrives on `onSliced`.
231
+ - **`gcode` is one-way**: pass G-code text and it is drawn on the selected plate instead of a slice result, and
232
+ auto re-slice leaves that plate alone while it is set.
233
+ - Everything else the component owns — camera, selection, plate count, paint state — is reported through `onEvent`
234
+ and settable only by the user.
235
+
236
+ If you need programmatic control over the scene, build on the subpath exports (`three-slicer/viewer/toolpath`,
237
+ `/loaders`, `/gcode`) and `three-slicer/client` rather than on `<Viewport/>`.
238
+
79
239
  ## Multithreaded slicing (automatic)
80
240
 
81
241
  The engine ships two WASM kernels: single-threaded (default) and `-pthread` multithreaded (~2× wall-clock on layer-parallel wall generation, measured 12× on the wall pass itself). The worker picks the mt kernel automatically when the page is [cross-origin isolated](https://developer.mozilla.org/en-US/docs/Web/API/Window/crossOriginIsolated) — i.e. served with: