three-slicer 0.2.2 → 0.2.4
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 +97 -0
- package/README.md +143 -19
- package/components/README.md +6 -3
- package/data/README.md +10 -9
- package/engine/PARAMS.md +248 -0
- package/engine/README.md +18 -264
- package/engine/examples/headless.mjs +4 -0
- package/engine/index.d.ts +26 -0
- package/engine/index.js +24 -4
- package/engine/src/client.js +17 -2
- package/engine/src/kernel_setting_keys.js +132 -0
- package/engine/src/settings.js +36 -2
- package/engine/src/slicer.worker.js +70 -6
- package/engine/src/slicer_core.js +0 -0
- package/engine/src/slicer_core.mt.js +0 -0
- package/engine/src/throughput.js +53 -0
- package/engine/src/warnings.js +23 -0
- package/package.json +1 -1
- package/types/.omc/state/sessions/02e5ddf5-4378-46e1-8fca-e90412c30804/pre-tool-advisory-throttle.json +10 -0
- package/types/settings.d.ts +20 -0
- package/types/viewer.d.ts +50 -1
- package/types/worker.d.ts +32 -3
- package/viewer/README.md +112 -16
- package/viewer/dist/Viewport.js +3772 -3295
- package/viewer/styles.css +18 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,102 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.3 — 2026-08-29
|
|
4
|
+
|
|
5
|
+
A developer-experience release: everything here comes from a log of what integrating the package actually
|
|
6
|
+
cost while building the four `examples/` demos and the live landing embed (`packages/FRICTION.md`). The
|
|
7
|
+
theme is the one failure mode they shared — wrong input returning something plausible instead of an error.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `sliceRequest` on `<Viewport/>`: a token whose identity change requests one slice. The component's
|
|
12
|
+
contract stays props-only, but a host that hides the built-in chrome no longer has to remount the whole
|
|
13
|
+
viewer to start a slice (which blanked the scene and re-parsed the model — measured as a visible
|
|
14
|
+
double-flip on the landing embed).
|
|
15
|
+
- `result.warnings`, a `string[]` naming what a **successful** slice got away with. Today's one entry is
|
|
16
|
+
`'over_bed_model'`: a bed-centred model sliced outside the printable area with plausible time and
|
|
17
|
+
material and no error. Present on the direct handle, through `createSlicerClient()`, and on the raw
|
|
18
|
+
worker's `done` reply. The `stats` flags are unchanged.
|
|
19
|
+
- `result.throughput` — how fast the slice ran: `ms` (wall time around the call), `kernelMs` (the kernel's own
|
|
20
|
+
phase total, so `ms - kernelMs` is the WASM boundary cost), `layersPerSecond`, and `msPerMsegment`. The last
|
|
21
|
+
one is the field to compare runs with: the kernel is not deterministic in segment count, so raw milliseconds
|
|
22
|
+
compare two different amounts of work. Same three paths as `warnings`, and on the SLA routes as well —
|
|
23
|
+
`kernelMs` there sums the resin passes (contours, sample, tree, raster, emit), since the two technologies
|
|
24
|
+
share only the name `t_emit_ms`.
|
|
25
|
+
- Throughput in the viewer, in three places: the slice bar shows layers/second **while** a slice runs, the stats
|
|
26
|
+
card shows the finished figure ("Sliced in 4.0s · 166 layers/s", with the kernel share and ms/Msegment on the
|
|
27
|
+
tooltip), and hosts receive both — `sliceRate` on `onEvent` and `throughput` beside `stats` on `onSliced`.
|
|
28
|
+
An SLA slice reports the live figure throughout; an FFF slice reports it once the emission pass starts
|
|
29
|
+
streaming layers, since its earlier passes publish no per-layer progress and none is invented. It therefore
|
|
30
|
+
reads higher than the whole-slice average, which counts the passes that emit nothing (measured on a 38MB
|
|
31
|
+
model: 306–707 layers/s live, 166 over the whole 4.0s slice).
|
|
32
|
+
- `applyPreset(settings, preset, keys)` in `three-slicer/settings` — clear-then-merge, so a preset switch
|
|
33
|
+
cannot leave the previous material's keys underneath. All four demos had written this by hand.
|
|
34
|
+
- `kernelSettingKeys` and `ignoredKernelSettings(settings)`: the 128 schema keys the FFF derivation reads,
|
|
35
|
+
and which of yours it does not. A key it does not read is accepted and does nothing.
|
|
36
|
+
- The raw worker's slice request accepts an object `params`, not only a JSON string — the direct handle
|
|
37
|
+
and the client already did, and the worker's own SLA branch always had.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- The prime tower stand-in is drawn only when the plate actually changes tools — objects on more than one
|
|
42
|
+
extruder, or material paint reaching extruder 2 or higher — instead of whenever a second filament happened to
|
|
43
|
+
be loaded. With everything on T1 the slice emits no tool change and generates no tower, so the box promised
|
|
44
|
+
one that would not be printed.
|
|
45
|
+
- The viewer no longer shows a slice result that the current settings would not produce. A change to `settings`
|
|
46
|
+
drops the cached results and returns to Prepare, instead of leaving a preview, a print estimate and an enabled
|
|
47
|
+
G-code export describing the settings the slice ran with. An injected (`gcode` / `sl1`) plate and an opened
|
|
48
|
+
`.sl1` archive are left alone — neither claims to be a slice of these settings.
|
|
49
|
+
- **`createSlicerClient()` with no argument now works after `vite build`.** Its default worker is written
|
|
50
|
+
as the literal `new Worker(new URL(…), { type: 'module' })` that bundlers recognize as a worker entry;
|
|
51
|
+
routed through `engineWorkerURL()` it matched Vite's plain-asset rule instead and was copied verbatim,
|
|
52
|
+
still importing an unhashed `./slicer_core.js` that the build never emitted — a 404 on the first message,
|
|
53
|
+
invisible until a production build. Measured on a Vite 5 consumer: a 21KB copy in a 40KB dist with no
|
|
54
|
+
kernel at all, versus a 12KB worker chunk importing the emitted kernels. `pack_check.sh` now fails when
|
|
55
|
+
a worker chunk imports a file the build did not produce.
|
|
56
|
+
- `support_type: 'tree(auto)'` selects tree supports. Tree vs grid was routed from `support_style` alone,
|
|
57
|
+
so an imported OrcaSlicer project asking for tree supports sliced grid, silently.
|
|
58
|
+
- A `files` prop changed after mount warns once instead of being ignored in silence.
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- `engineWorkerURL()` is documented as the **no-bundler** path (native ESM, an import map, a CDN). Under
|
|
63
|
+
Vite or webpack use `createSlicerClient()` or `three-slicer/worker?worker`. The function is unchanged.
|
|
64
|
+
- Since tree vs grid now reads `support_type`, a settings map carrying `support_type: 'tree(auto)'` with
|
|
65
|
+
`support_style: 'default'` produces different G-code than it did in 0.2.2 — tree, as upstream does.
|
|
66
|
+
|
|
67
|
+
## 0.2.2 — 2026-08-22
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
|
|
71
|
+
- SL1 import in the viewer: an `.sl1` archive opens through the file picker, drag & drop, or the new `files`
|
|
72
|
+
prop — rendered as a raster preview, with the model shape reconstructed from the masks in the background
|
|
73
|
+
(two passes: a coarse mesh in under a second, then full resolution). This viewer's own exports carry scene
|
|
74
|
+
and role sidecars, so reopening one shows the exported surface itself, in the sliced preview's colours, and
|
|
75
|
+
re-exports byte-identical. Opening an `.sl1` applies the archive's job description to the settings
|
|
76
|
+
(`printer_technology` first), so a fresh FFF session switches to the SLA route.
|
|
77
|
+
- SL1 export: role/scene sidecar members, off-thread mask encoding, and a save window.
|
|
78
|
+
- Move scrub in Preview: a horizontal slider that walks the top layer move by move with a screen-fixed nozzle
|
|
79
|
+
marker — upstream's sequential view. Position changes reach the host as the `moveScrub` event.
|
|
80
|
+
- `files` and `sl1` Viewport props: models, `.3mf` projects, `.sl1` archives and preset files handed in once
|
|
81
|
+
at mount.
|
|
82
|
+
|
|
83
|
+
### Fixed
|
|
84
|
+
|
|
85
|
+
- The SLA print area is the resin display size, not `printable_area`.
|
|
86
|
+
|
|
87
|
+
## 0.2.1 — 2026-08-21
|
|
88
|
+
|
|
89
|
+
### Added
|
|
90
|
+
|
|
91
|
+
- `pad_around_object` (embed) is now supported in the SLA path — the `SLA_PAD_AROUND_OBJECT_UNSUPPORTED`
|
|
92
|
+
capability gate is gone. It forces zero elevation (upstream `is_zero_elevation`), and an empty embed pad is
|
|
93
|
+
legal: the ring survives only where supports stand.
|
|
94
|
+
|
|
95
|
+
### Performance
|
|
96
|
+
|
|
97
|
+
- SLA slicing: the raster fallback was rewritten, and slicing/prepare run layer-parallel on the multithreaded
|
|
98
|
+
kernel — byte-identical output, with progress bands matched to measured time.
|
|
99
|
+
|
|
3
100
|
## 0.2.0 — 2026-08-19
|
|
4
101
|
|
|
5
102
|
SLA (resin) printing lands as a second technology in the same kernel, routed by `printer_technology`.
|
package/README.md
CHANGED
|
@@ -12,6 +12,20 @@ The package is published as a single npm package, `three-slicer`, with subpath e
|
|
|
12
12
|
- Source: [kimgh06/Web_Three_Slicer](https://github.com/kimgh06/Web_Three_Slicer)
|
|
13
13
|
- Demo: [slicer.kimgh06.com](https://slicer.kimgh06.com/)
|
|
14
14
|
|
|
15
|
+
## Start Here
|
|
16
|
+
|
|
17
|
+
Three ways in — pick the row that is your case. Each path is a working code block plus the one doc worth
|
|
18
|
+
reading whole; everything else is lookup.
|
|
19
|
+
|
|
20
|
+
| Your case | Path | Copy this | Then read |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| "Just the numbers" — quotes, farms, automation. No UI, no React | **Headless** | [Quick Start: Headless Slicing](#quick-start-headless-slicing) | [engine/README](engine/README.md) (134 lines), params via [PARAMS.md](engine/PARAMS.md) as lookup |
|
|
23
|
+
| A slicer inside your existing page | **Embed** | [Quick Start: React Viewer](#quick-start-react-viewer) | [viewer/README](viewer/README.md) (330 lines — the props contracts live there) |
|
|
24
|
+
| The full slicer UI — viewport + settings panel | **Full UI** | [Full React Example](#full-react-example) | viewer/README + [components/README](components/README.md) |
|
|
25
|
+
|
|
26
|
+
Whichever path: read [Before You Ship](#before-you-ship) before deploying — those traps return plausible
|
|
27
|
+
results instead of errors, and the first two never show up in `npm run dev`.
|
|
28
|
+
|
|
15
29
|
## Features
|
|
16
30
|
|
|
17
31
|
- WASM slicing engine with Arachne walls, infill patterns, supports, raft/brim/skirt, ironing, arc fitting, multi-material paths, and layer streaming.
|
|
@@ -23,7 +37,9 @@ The package is published as a single npm package, `three-slicer`, with subpath e
|
|
|
23
37
|
- Support painting and material painting, plus per-tool filament and purge statistics.
|
|
24
38
|
- 3MF **project** import in the viewer: a slicer-written `.3mf` (OrcaSlicer/BambuStudio save, MakerWorld download) restores its plate layout, project settings, and painted facets — not just the meshes.
|
|
25
39
|
- Extracted data files for custom UIs: config schema, UI tree, toggle rules, invalidation map, and the printer/process/filament catalogs.
|
|
26
|
-
- SLA (resin) slicing: PrusaSlicer 2.9.6's ported support-point generator, support tree and pad, a resin material catalog, and `.sl1` mask export — routed by `printer_technology`, with typed capability errors for what the port does not cover.
|
|
40
|
+
- SLA (resin) slicing: PrusaSlicer 2.9.6's ported support-point generator, support tree and pad, a resin material catalog, and `.sl1` mask export *and import* — routed by `printer_technology`, with typed capability errors for what the port does not cover.
|
|
41
|
+
- Preview move scrub: a position slider with a nozzle marker that walks the toolpath inside a layer (upstream's sequential view).
|
|
42
|
+
- Host injection props on the viewer: `files` hands models, `.3mf` projects, `.sl1` archives and preset files in at mount; `gcode` injects a sliced result for preview-only use.
|
|
27
43
|
- Automatic multithreaded WASM selection on cross-origin-isolated browser pages.
|
|
28
44
|
|
|
29
45
|
## Installation
|
|
@@ -42,8 +58,8 @@ npm i three-slicer react react-dom three
|
|
|
42
58
|
|
|
43
59
|
| Import path | Needs react/three |
|
|
44
60
|
| --- | --- |
|
|
45
|
-
| `three-slicer`, `/settings`, `/toggle`, `/client`, `/worker`, `/wasm`, `/data` | no |
|
|
46
|
-
| `three-slicer/viewer`, `/viewer/
|
|
61
|
+
| `three-slicer`, `/settings`, `/toggle`, `/client`, `/worker`, `/wasm`, `/data`, `/viewer/gcode`, `/viewer/toolpath` | no — `makeToolpath(THREE, data)` takes `three` as an argument |
|
|
62
|
+
| `three-slicer/viewer`, `/viewer/loaders` | yes |
|
|
47
63
|
| `three-slicer/components` | react only |
|
|
48
64
|
|
|
49
65
|
## Quick Start: Headless Slicing
|
|
@@ -71,12 +87,37 @@ console.log(result.gcode)
|
|
|
71
87
|
slicer.dispose()
|
|
72
88
|
```
|
|
73
89
|
|
|
74
|
-
`slice()` accepts a binary STL as an `ArrayBuffer` or `Uint8Array`. Parameters can be either a kernel params object or a JSON string — every accepted parameter is listed in the [kernel parameter reference](engine/
|
|
90
|
+
`slice()` accepts a binary STL as an `ArrayBuffer` or `Uint8Array`. Parameters can be either a kernel params object or a JSON string — every accepted parameter is listed in the [kernel parameter reference](engine/PARAMS.md), which is generated from the kernel's own reader.
|
|
75
91
|
|
|
76
92
|
A runnable version of the above ships with the package: `node node_modules/three-slicer/engine/examples/headless.mjs` writes a cube, slices it batch and streamed, and prints the stats.
|
|
77
93
|
|
|
78
94
|
`result.error` is set instead of a result when a slice fails or is cancelled — check it before reading `gcode`.
|
|
79
95
|
|
|
96
|
+
`result.warnings` is the other half of that: a `string[]`, empty when nothing, naming what a **successful** slice
|
|
97
|
+
got away with. Today the one entry is `'over_bed_model'` — the model sliced outside the printable area, which
|
|
98
|
+
otherwise reports plausible time and material and no error (see [Before You Ship](#before-you-ship)). Every path
|
|
99
|
+
carries it: the direct handle, `createSlicerClient()`, and the raw worker's `done` reply.
|
|
100
|
+
|
|
101
|
+
`result.throughput` says how fast it ran, measured around the call on the same three paths:
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
result.throughput.ms // wall time, incl. the STL crossing into WASM and the layer stream out
|
|
105
|
+
result.throughput.kernelMs // the kernel's own phase total; ms − kernelMs is the boundary cost
|
|
106
|
+
result.throughput.layersPerSecond // the figure to show a person
|
|
107
|
+
result.throughput.msPerMsegment // the figure to COMPARE runs with
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Compare runs on `msPerMsegment`, not on `ms`. The kernel is not deterministic in segment count — the same input
|
|
111
|
+
has been measured 15% apart between runs — so two raw millisecond figures describe two different amounts of work,
|
|
112
|
+
and the difference reads as a regression that is not there. In the viewer the same throughput is live: the slice
|
|
113
|
+
bar shows layers/second while slicing, and the host receives it as the `sliceRate` event.
|
|
114
|
+
|
|
115
|
+
SLA slices carry it too (`sliceSla`, the object-aware job, and the JS contour fallback). `kernelMs` sums that
|
|
116
|
+
technology's own passes — contours, sample, tree, raster, emit — because the two share only the name `t_emit_ms`,
|
|
117
|
+
and reading an SLA result through the FFF phase list would report its emit pass as the entire kernel time
|
|
118
|
+
(measured on a 10mm cube: 2.99ms summed correctly, 1.42ms if mis-read). Compare SLA runs against SLA runs: its
|
|
119
|
+
`path_segments` counts mask segments, not toolpath segments.
|
|
120
|
+
|
|
80
121
|
## Streaming Layers
|
|
81
122
|
|
|
82
123
|
For large models, pass callbacks to receive each sliced layer as it is produced. This avoids keeping the full G-code and layer payload in memory at once.
|
|
@@ -209,9 +250,36 @@ Available subpaths:
|
|
|
209
250
|
| `three-slicer/data` | Named exports for extracted metadata |
|
|
210
251
|
| `three-slicer/data/*.json` | Extracted OrcaSlicer metadata |
|
|
211
252
|
|
|
253
|
+
## Architecture: How the Pieces Connect
|
|
254
|
+
|
|
255
|
+
One npm package, five modules. The dependency direction is one-way: `wasm-core` →(build)→ `engine`
|
|
256
|
+
←(data)← `data`, with `components` and `viewer` consuming different surfaces of the engine, `types`
|
|
257
|
+
generating and gating the contract from the side — and the glue at the top is always a single sparse
|
|
258
|
+
settings map held by the host app.
|
|
259
|
+
|
|
260
|
+
| Module | Role | Published as | Depends on | Consumed by |
|
|
261
|
+
| --- | --- | --- | --- | --- |
|
|
262
|
+
| `wasm-core` | C++ kernel sources + vendored third_party | not published | (ported upstream sources) | `engine` — as build output only |
|
|
263
|
+
| `engine` | kernel SDK: direct handle, worker, client, settings derivation | `three-slicer` root + `/settings` `/toggle` `/client` `/worker` `/wasm(-mt)` `/data` | committed wasm-core output, `data`, the generated `kernel_setting_keys.js` | `components`, `viewer`, every consumer |
|
|
264
|
+
| `data` | extracted schema + catalogs (config-schema, printers, processes, filaments…) | `/data` | `extract_all.py` ← upstream | `engine`'s settings, `components`, `viewer` |
|
|
265
|
+
| `types` | every `.d.ts` + two generators | attached per subpath via the exports map's `types` fields | runs `engine` at generation time (probing), reads `data`'s schema | consumers' IDE/TS — no runtime dependency |
|
|
266
|
+
| `components` | `<SettingsPanel/>` (Shadow DOM) | `/components` | `data` (schema + UI tree), `engine`'s `/toggle` | host apps |
|
|
267
|
+
| `viewer` | `<Viewport/>` (three.js, Shadow DOM) | `/viewer` + `/viewer/toolpath` `/loaders` `/gcode` | `engine`'s `/settings`, the worker, `/data`; `three` (peer) | host apps |
|
|
268
|
+
|
|
269
|
+
Some of the connections are not imports, and knowing which is which explains most of the layout:
|
|
270
|
+
|
|
271
|
+
| Connection | Mechanism | Notes |
|
|
272
|
+
| --- | --- | --- |
|
|
273
|
+
| `wasm-core` → `engine` | `build.sh` (emscripten) commits its output into `engine/src/` | not an import — the golden byte-identical gate and the invariant suite verify kernel changes against the engine's output |
|
|
274
|
+
| `data` → `engine`/`components`/`viewer` | `/data` subpath imports | the source of truth is `extract_all.py` over the upstream checkout |
|
|
275
|
+
| `types` → `engine` | the generator **executes** `deriveKernelParams` (probing) and writes `engine/PARAMS.md` + `engine/src/kernel_setting_keys.js` back into the engine | `test_kernel_params.mjs` is the staleness gate |
|
|
276
|
+
| `types` → `data` | `gen_settings_types.mjs` generates `settings-keys.d.ts` (976 keys) from the schema | run automatically by the build |
|
|
277
|
+
| `viewer` ↔ `components` | **no direct link** | the host shares one settings map with both as props, and mounts `<SettingsPanel embedded/>` into the viewer's panel slots |
|
|
278
|
+
| inside `viewer` | `core` (pure, node-runnable) ← `scene` (three/DOM) ← `actions` ← `ui` | `test_layers.mjs` enforces the boundary |
|
|
279
|
+
|
|
212
280
|
## TypeScript
|
|
213
281
|
|
|
214
|
-
Type declarations ship with the package — no `@types/*` needed. All
|
|
282
|
+
Type declarations ship with the package — no `@types/*` needed. All 976 setting keys are typed from the config schema, enum values included:
|
|
215
283
|
|
|
216
284
|
```ts
|
|
217
285
|
import { createSlicer, type SlicerSettings } from 'three-slicer'
|
|
@@ -252,7 +320,7 @@ const params = deriveKernelParams(settings)
|
|
|
252
320
|
const result = slicer.slice(stlArrayBuffer, params)
|
|
253
321
|
```
|
|
254
322
|
|
|
255
|
-
`deriveKernelParams()` maps the curated set of schema keys currently supported by the kernel
|
|
323
|
+
`deriveKernelParams()` maps the curated set of schema keys currently supported by the kernel — 131 of its 159 parameters are reachable from 128 schema keys today; the [generated reference](engine/PARAMS.md) is the authoritative list. Other schema keys can still be displayed by the UI, but they may not affect slicing output yet — `ignoredKernelSettings(settings)` names the keys of your map in that category, so a "why did this setting do nothing" question has a one-call answer. Vector options are simplified to their first element, except the filament options described below, which keep every extruder's entry.
|
|
256
324
|
|
|
257
325
|
## 3MF Projects
|
|
258
326
|
|
|
@@ -358,13 +426,10 @@ pick's leftovers behind — an ABS material followed by a PLA one keeps ABS's ch
|
|
|
358
426
|
exposes the exact key set to remove first:
|
|
359
427
|
|
|
360
428
|
```js
|
|
361
|
-
|
|
362
|
-
const next = { ...settings }
|
|
363
|
-
for (const key of keys) delete next[key]
|
|
364
|
-
return Object.assign(next, preset)
|
|
365
|
-
}
|
|
429
|
+
import { applyPreset } from 'three-slicer/settings'
|
|
366
430
|
|
|
367
431
|
setSettings(s => applyPreset(s, filaments.settingsFor(name), filaments.keys))
|
|
432
|
+
// clears filaments.keys first, then merges; a null preset (failed lookup) applies nothing
|
|
368
433
|
```
|
|
369
434
|
|
|
370
435
|
The same applies to `processPresets().keys` and `printerKeys`. The filament key set is disjoint from the process key
|
|
@@ -386,6 +451,8 @@ The kernel reloads the loaded-filament settings at every tool change and reports
|
|
|
386
451
|
|
|
387
452
|
A single-material slice sends none of these keys and produces exactly the G-code it produced before multi-material existed.
|
|
388
453
|
|
|
454
|
+
Besides per-extruder arrays and painting, a per-feature filament assignment is the third way a slice becomes multi-tool: `outer_wall_filament_id`, `inner_wall_filament_id`, `sparse_infill_filament_id`, `top_surface_filament_id`, `bottom_surface_filament_id` and `internal_solid_filament_id` are 1-based filament indices ("print the outer wall with filament 2"). Values of 0 (default) and 1 (the default extruder) add no second tool, so existing single-material slices are unaffected.
|
|
455
|
+
|
|
389
456
|
### Painting a region onto another extruder
|
|
390
457
|
|
|
391
458
|
Painting is the only way a single object can print in two materials. Painting states follow OrcaSlicer's own enum, in which state `1` is both the support enforcer and Extruder 1, state `2` is both the support blocker and Extruder 2, and `3..16` are Extruders 3 to 16. The state-addressed protocol is on the worker; `slicer.paint()` on the direct handle is the original enforcer/blocker boolean pair.
|
|
@@ -398,12 +465,12 @@ worker.postMessage({ cmd: 'erase', facet, hx, hy, hz, cx, cy, cz, radius })
|
|
|
398
465
|
|
|
399
466
|
`erase` is a separate command rather than `state: 0`, because a JS `false` coerces to `0` at the WASM boundary and the legacy blocker brush sends exactly that — so the state path refuses `0` outright.
|
|
400
467
|
|
|
401
|
-
|
|
468
|
+
Painting and support now coexist: the painted multi-material path runs the same support pass the single-material path does, so enabling support no longer costs the material paint (or the other way round). What remains is the ambiguity a single enum creates — a support **blocker** paint and an Extruder 2 paint are the same mark on a facet, so on a model that uses both brushes the blocker reads as "print with T1".
|
|
402
469
|
|
|
403
470
|
## SLA (Resin) Slicing
|
|
404
471
|
|
|
405
472
|
SLA is a second printer technology, not an FFF mode. `printer_technology` in the settings map routes it —
|
|
406
|
-
apply an SLA machine profile from the catalog (`
|
|
473
|
+
apply an SLA machine profile from the catalog (`printerTechByVendor` marks the technology) or set the key
|
|
407
474
|
yourself, then derive SLA params and call the SLA entry:
|
|
408
475
|
|
|
409
476
|
```js
|
|
@@ -481,7 +548,11 @@ and its out-of-memory retry ladder, both of which are UI policy rather than prot
|
|
|
481
548
|
### Driving the worker directly
|
|
482
549
|
|
|
483
550
|
`createSlicerClient` is a thin wrapper; the protocol underneath is a plain message contract, fully typed in
|
|
484
|
-
`three-slicer/worker`.
|
|
551
|
+
`three-slicer/worker`. A slice request is the message with **no `cmd`**, and its `params` takes an object or a
|
|
552
|
+
JSON string (object accepted since 0.2.3 — the kernel parses JSON text, so it is stringified for you).
|
|
553
|
+
|
|
554
|
+
The worker below is created with `engineWorkerURL()`, which suits a no-bundler page; under Vite or webpack
|
|
555
|
+
create it the way [Bundler Notes](#bundler-notes) describes and drive that instance instead.
|
|
485
556
|
|
|
486
557
|
```js
|
|
487
558
|
import { engineWorkerURL } from 'three-slicer'
|
|
@@ -546,6 +617,19 @@ progress cannot be interrupted — terminate the worker instead.
|
|
|
546
617
|
|
|
547
618
|
## Bundler Notes
|
|
548
619
|
|
|
620
|
+
**Creating the slice worker.** `createSlicerClient()` with no argument is the supported path under Vite and
|
|
621
|
+
webpack: the default is written as the literal `new Worker(new URL('./slicer.worker.js', import.meta.url),
|
|
622
|
+
{ type: 'module' })` that both bundlers recognize as a worker entry, so the worker reaches `dist` together
|
|
623
|
+
with the kernel chunks it dynamically imports. Verified in `pack_check.sh`, which builds a Vite consumer and
|
|
624
|
+
asserts every relative import the emitted worker chunk makes names a file the build actually produced.
|
|
625
|
+
|
|
626
|
+
Through 0.2.2 that default went through `engineWorkerURL()`, and a function call does not match the pattern —
|
|
627
|
+
Vite fell back to its plain-asset rule and copied the worker source in verbatim, still importing an unhashed
|
|
628
|
+
`./slicer_core.js`. Measured on a Vite 5 app importing `three-slicer/client` and nothing else: a 21KB copy in
|
|
629
|
+
a 40KB `dist` — no kernel emitted at all, a 404 on the worker's first message, and only after `vite build`,
|
|
630
|
+
since the dev server serves the sources. `engineWorkerURL()` remains for no-bundler setups (native ESM, an
|
|
631
|
+
import map, a CDN); under a bundler use the no-argument client or `three-slicer/worker?worker`.
|
|
632
|
+
|
|
549
633
|
Vite consumers should enable ES module workers and an ES2022 build target:
|
|
550
634
|
|
|
551
635
|
```js
|
|
@@ -596,11 +680,12 @@ The package includes extracted OrcaSlicer metadata for custom interfaces:
|
|
|
596
680
|
|
|
597
681
|
| File | Use |
|
|
598
682
|
| --- | --- |
|
|
599
|
-
| `three-slicer/data/config-schema.json` | Setting definitions, defaults, labels, units, and option metadata (
|
|
683
|
+
| `three-slicer/data/config-schema.json` | Setting definitions, defaults, labels, units, and option metadata (976 options) |
|
|
600
684
|
| `three-slicer/data/ui-tree.json` | Tab, page, group, and option layout |
|
|
601
685
|
| `three-slicer/data/toggle-rules.json` | Original enable/disable rule metadata |
|
|
602
686
|
| `three-slicer/data/invalidation-map.json` | Setting invalidation/dependency metadata |
|
|
603
|
-
| `three-slicer/data/
|
|
687
|
+
| `three-slicer/data/preset-keys.json` | The option keys belonging to each preset type — what `presetOptionKeys(type)` reads |
|
|
688
|
+
| `three-slicer/data/printers.json` | 1,041 vendor machine profiles across 66 vendors: motion limits, bed, nozzle — plus the resin catalog and each vendor's printer technology |
|
|
604
689
|
| `three-slicer/data/processes.js` | 2,243 print presets (speeds, accelerations) |
|
|
605
690
|
| `three-slicer/data/filaments.js` | 5,999 material presets over 81 filament types, plus each printer model's recommended list |
|
|
606
691
|
|
|
@@ -623,13 +708,16 @@ the layout by hand.
|
|
|
623
708
|
| `slicer.overlay(enforcer)` | The painted overlay triangles for one state, as a `Float32Array` |
|
|
624
709
|
| `slicer.heapSize()` | Current WASM heap size, bytes (peak, monotonic) |
|
|
625
710
|
| `slicer.dispose()` | Releases the slicer handle for garbage collection |
|
|
626
|
-
| `engineWorkerURL()` |
|
|
711
|
+
| `engineWorkerURL()` | Worker URL for a **no-bundler** browser setup. Under Vite/webpack use `createSlicerClient()` or `three-slicer/worker?worker` — see [Bundler Notes](#bundler-notes) |
|
|
627
712
|
|
|
628
713
|
### `three-slicer/settings`
|
|
629
714
|
|
|
630
715
|
| API | Description |
|
|
631
716
|
| --- | --- |
|
|
632
717
|
| `deriveKernelParams(settings, opts?)` | Converts sparse OrcaSlicer settings to kernel params. `opts.plate` picks a plate's entry from per-plate options |
|
|
718
|
+
| `applyPreset(settings, preset, keys)` | Clear-then-merge: deletes the catalog's `keys`, merges the preset; a null preset applies nothing |
|
|
719
|
+
| `kernelSettingKeys` | The schema keys `deriveKernelParams` consumes — generated with the [reference table](engine/PARAMS.md) |
|
|
720
|
+
| `ignoredKernelSettings(settings)` | The map's keys the FFF derivation does **not** read — accepted but without slicing effect |
|
|
633
721
|
| `schemaDefault(key)` | The config-schema default for a key |
|
|
634
722
|
| `settingRaw(settings, key)` | The map's value, or the schema default |
|
|
635
723
|
| `settingScalar(settings, key)` | The same, reduced to a scalar — the first set entry of a per-extruder column |
|
|
@@ -640,6 +728,7 @@ the layout by hand.
|
|
|
640
728
|
| `readPresetFile(raw, opts)` | A preset `.json` → a settings map; follows `inherits` via `resolveParent` |
|
|
641
729
|
| `presetOptionKeys(type)` | The option keys belonging to `machine` / `process` / `filament` |
|
|
642
730
|
| `printersByVendor` | Vendor → profile name → entry, for building a picker |
|
|
731
|
+
| `printerTechByVendor` | Vendor → printer technology (`'FFF'` / `'SLA'`) |
|
|
643
732
|
| `printerSettings(name)` | Settings a vendor machine profile applies |
|
|
644
733
|
| `printerKeys` | Every key a printer profile can set — clear these before applying another |
|
|
645
734
|
| `printerDefaultPreset(name)` | The vendor's recommended process preset for that printer |
|
|
@@ -678,7 +767,7 @@ the layout by hand.
|
|
|
678
767
|
|
|
679
768
|
| API | Description |
|
|
680
769
|
| --- | --- |
|
|
681
|
-
| `schema`, `uiTree`, `toggleRules`, `invalidationMap`, `printers` | The extracted metadata, import attributes already applied |
|
|
770
|
+
| `schema`, `uiTree`, `toggleRules`, `invalidationMap`, `printers`, `presetKeys` | The extracted metadata, import attributes already applied |
|
|
682
771
|
| `loadProcesses()` / `loadFilaments()` | The two large catalogs, on demand — prefer the `settings` facades |
|
|
683
772
|
|
|
684
773
|
## Runtime Support
|
|
@@ -691,6 +780,41 @@ the layout by hand.
|
|
|
691
780
|
| React | Viewer and settings panel |
|
|
692
781
|
| three.js | Required by `three-slicer/viewer` |
|
|
693
782
|
|
|
783
|
+
## Before You Ship
|
|
784
|
+
|
|
785
|
+
The traps below were all hit while building the [integration demos](https://github.com/kimgh06/Web_Three_Slicer/tree/main/examples)
|
|
786
|
+
and the live landing embed — each one returns something plausible instead of an error, which is why they
|
|
787
|
+
are listed **in the order they bite**, not by topic. The later a trap fires, the more it costs — and none of
|
|
788
|
+
them fails a build or throws, so nothing surfaces them for you.
|
|
789
|
+
|
|
790
|
+
1. **Your first real model: hand it over origin-centered, not bed-centered.** The kernel takes plate-local
|
|
791
|
+
coordinates and seats the model on the bed itself; pre-centering on the bed adds the offset twice and the
|
|
792
|
+
part slices *off the bed* — with plausible time and material and no error. Since 0.2.3 the result says so:
|
|
793
|
+
`result.warnings` carries `'over_bed_model'` (the `stats.over_bed_model` flag is still there too). Check
|
|
794
|
+
`result.warnings.length` after every slice; it is the channel every future condition of this kind uses.
|
|
795
|
+
2. **Reading the results: `filament_mm` is a length, and the time depends on the kernel.** Grams are your
|
|
796
|
+
conversion (`filament_mm × π × (diameter/2)² × density / 1000`, from the filament preset's
|
|
797
|
+
`filament_diameter`/`filament_density`). `time_estimate` differs ~25% between the single- and
|
|
798
|
+
multi-threaded kernels on identical geometry, so your COOP/COEP headers change the number — pin one as
|
|
799
|
+
the reference if you price on it.
|
|
800
|
+
3. **Drawing toolpaths: the role field is encoded, and G-code round-trips lose roles.** `paths[k+3]` holds
|
|
801
|
+
`role + tool*16` — mask with `& 15` / `>>> 4`. `SegmentData.position` is stride 4 (x, y, z, w). The
|
|
802
|
+
kernel's G-code carries no `;TYPE:` comments, so `parseGcode` collapses roles into wall — draw
|
|
803
|
+
`result.layers` directly when the slicing side is yours.
|
|
804
|
+
4. **Settings: presets are sparse, and unread keys are silent.** Apply presets with
|
|
805
|
+
[`applyPreset`](#materials-and-multi-material) (clear-then-merge — a plain merge keeps the previous
|
|
806
|
+
material's leftovers). A key the FFF derivation does not read is accepted and does nothing:
|
|
807
|
+
`ignoredKernelSettings(settings)` names them.
|
|
808
|
+
5. **Cancel is conditional.** `client.cancel()` works only on a cross-origin-isolated page (the mt kernel);
|
|
809
|
+
otherwise it returns `false` — terminate and recreate the client instead.
|
|
810
|
+
Details: [Cancelling a slice](#cancelling-a-slice).
|
|
811
|
+
|
|
812
|
+
Gone as of 0.2.3, and listed here only because it bit every consumer before then: `createSlicerClient()` with
|
|
813
|
+
no argument used to build a worker Vite copied as a plain asset, which 404'd on its own kernel import in
|
|
814
|
+
production while `npm run dev` passed. The no-argument form now emits a real worker chunk — see
|
|
815
|
+
[Bundler Notes](#bundler-notes). On 0.2.2 or earlier, pass the worker in yourself
|
|
816
|
+
(`import SlicerWorker from 'three-slicer/worker?worker'`).
|
|
817
|
+
|
|
694
818
|
## Known Limits
|
|
695
819
|
|
|
696
820
|
- `slice()` currently takes binary STL input at the engine API boundary.
|
|
@@ -698,7 +822,7 @@ the layout by hand.
|
|
|
698
822
|
- `registerLoader(exts, fn)` from `three-slicer/viewer/loaders` adds any other format. Formats needing a heavy dependency are kept out of the package so it stays runtime-dependency-free — see `web/viewer/src/step_loader.js` for a STEP loader built on `occt-import-js` (OCCT WASM).
|
|
699
823
|
- Not every OrcaSlicer schema key is wired into the WASM kernel yet.
|
|
700
824
|
- Some vector settings are simplified to their first element.
|
|
701
|
-
-
|
|
825
|
+
- One triangle selector serves both the support brush and the material brush (upstream's shared enum), so a support **blocker** paint and an Extruder 2 paint are the same mark — the two brushes cannot mark the same model independently.
|
|
702
826
|
- The multi-material prime tower is a real ported wipe tower, but the fallback square ring used when it fails is not.
|
|
703
827
|
- SLA hollowing and drain-hole geometry are refused with `SLA_UNSUPPORTED_HOLLOWING` (the OpenVDB chain is not ported); the records still round-trip through `.3mf`.
|
|
704
828
|
- The `.sl1` export lives in the viewer (its masks need a canvas); there is no headless `.sl1` writer export yet.
|
package/components/README.md
CHANGED
|
@@ -4,12 +4,12 @@ Reusable React components for the browser slicer. Props-driven — no global sta
|
|
|
4
4
|
|
|
5
5
|
## `<SettingsPanel/>`
|
|
6
6
|
|
|
7
|
-
An OrcaSlicer-style settings form generated from `three-slicer/data` (schema + UI tree + toggle rules): tab/page/group navigation, mode filter (simple/advanced/expert), search across
|
|
7
|
+
An OrcaSlicer-style settings form generated from `three-slicer/data` (schema + UI tree + toggle rules): tab/page/group navigation, mode filter (all/simple/advanced/expert/develop), search across 976 options, dirty markers with per-option reset, and enable/disable rules evaluation.
|
|
8
8
|
|
|
9
9
|
The tab set follows the printer profile's technology: a settings map whose `printer_technology` says SLA swaps the FFF tabs for the SLA ones (print/material/printer builders carry the technology in their upstream builder name, which is what the filter reads). No prop needed — change the profile in the shared settings map and the panel follows.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm i three-slicer # react is
|
|
12
|
+
npm i three-slicer react react-dom # react is an OPTIONAL peer — npm does not install it for you
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
```jsx
|
|
@@ -29,10 +29,13 @@ Props:
|
|
|
29
29
|
| `settings` | `{ [schemaKey]: value }` | Sparse settings map — the consumer's state is the single source of truth |
|
|
30
30
|
| `setSettings` | `(updater) => void` | React setState-style updater; the only way state leaves the component |
|
|
31
31
|
| `onOptionOpen` | `(key) => void` (optional) | Label click hook for deep links/detail views. Omit → plain labels |
|
|
32
|
+
| `embedded` | `boolean` (optional) | Compact layout for embedding inside another panel |
|
|
33
|
+
| `customWidgets` | `{ [schemaKey]: Component }` (optional) | Replace schema-driven widgets per key (e.g. `{ printable_area: MyBedEditor }`) |
|
|
34
|
+
| `only` | `{ builder, page? }` (optional) | Pins the panel to one builder/page and drops the search/page/mode chrome — how `<Viewport/>`'s `motionPanel` slot is meant to be filled |
|
|
32
35
|
|
|
33
36
|
The same `settings` object feeds `deriveKernelParams()` in `three-slicer` and the `<Viewport/>` in `three-slicer/viewer` — one state connects all three.
|
|
34
37
|
|
|
35
|
-
Ships transpiled ESM (`dist/`); react is
|
|
38
|
+
Ships transpiled ESM (`dist/`); react is an optional peer dependency. The panel arrives fully styled: its stylesheet is inlined into the bundle and injected into a Shadow DOM root, so host CSS neither styles it nor collides with it.
|
|
36
39
|
|
|
37
40
|
The 3D viewport lives in its own package: `three-slicer/viewer`.
|
|
38
41
|
|
package/data/README.md
CHANGED
|
@@ -4,12 +4,13 @@ OrcaSlicer configuration metadata extracted from the original C++ sources, shipp
|
|
|
4
4
|
|
|
5
5
|
| File | Contents |
|
|
6
6
|
|---|---|
|
|
7
|
-
| `config-schema.json` |
|
|
8
|
-
| `config-schema-builddump.json` | Build-verified cross-check: the real `print_config_def` (817 options) dumped from a WASM compile of the original `PrintConfig.cpp` |
|
|
9
|
-
| `ui-tree.json` | Tab → page → group → option tree from `Tab.cpp` (
|
|
7
|
+
| `config-schema.json` | 976 print options: type, label, category, tooltip, min/max, mode, enum values/labels, defaults, source line numbers |
|
|
8
|
+
| `config-schema-builddump.json` | Build-verified cross-check: the real `print_config_def` (817 options) dumped from a WASM compile of the original `PrintConfig.cpp` — repo-only, excluded from the npm tarball |
|
|
9
|
+
| `ui-tree.json` | Tab → page → group → option tree from `Tab.cpp` (34 pages, 618 option references — the SLA tabs included) |
|
|
10
10
|
| `toggle-rules.json` | 231 enable/disable rules with original C++ `enable_if` condition source |
|
|
11
11
|
| `invalidation-map.json` | Option-change → re-slice step mapping (Print/PrintObject invalidation branches) |
|
|
12
|
-
| `
|
|
12
|
+
| `preset-keys.json` | The option keys belonging to each preset type (`machine`/`process`/`filament`) — what `presetOptionKeys(type)` reads |
|
|
13
|
+
| `printers.json` | 1,041 vendor machine profiles across 66 vendors: motion limits, bed and nozzle — plus the resin catalog and each vendor's printer technology |
|
|
13
14
|
| `processes.js` | 2,243 print presets (speeds, accelerations), joined to printers by `compatible_printers` |
|
|
14
15
|
| `filaments.js` | 5,999 material presets over 81 filament types, plus each printer model's recommended list |
|
|
15
16
|
|
|
@@ -18,10 +19,10 @@ The last two are column-oriented and deduplicated, and load on demand. Read them
|
|
|
18
19
|
|
|
19
20
|
## Material presets
|
|
20
21
|
|
|
21
|
-
`filaments.js` carries only the keys the kernel actually reads (
|
|
22
|
-
and the retraction/z-hop overrides a material is allowed to apply on top of the
|
|
23
|
-
disjoint from `processes.js`'
|
|
24
|
-
neither clears the other's values.
|
|
22
|
+
`filaments.js` carries only the keys the kernel actually reads (18 today: temperatures, flow, diameter, cooling,
|
|
23
|
+
pressure advance, cost/density, and the retraction/z-hop overrides a material is allowed to apply on top of the
|
|
24
|
+
machine's). That column set is disjoint from `processes.js`' 85, which is what lets a material pick and a process
|
|
25
|
+
pick be applied independently — neither clears the other's values.
|
|
25
26
|
|
|
26
27
|
`filamentPresets()` is the facade:
|
|
27
28
|
|
|
@@ -37,7 +38,7 @@ filaments.keys // clear these before appl
|
|
|
37
38
|
|
|
38
39
|
`recommendedFor()` intersects with the compatible list rather than returning the raw recommendation, and the
|
|
39
40
|
filtering is not cosmetic: the recommendation is declared on the machine **model** and so is nozzle-agnostic,
|
|
40
|
-
while its entries name nozzle-specific presets. Across all 1,
|
|
41
|
+
while its entries name nozzle-specific presets. Across all 1,041 printer profiles, 1,749 of 4,259 raw
|
|
41
42
|
recommendation entries (41%) name a material whose own compatible list excludes that profile.
|
|
42
43
|
|
|
43
44
|
`type` and `vendor` are empty strings when the profile chain declares neither — 265 of the 5,999 presets. That is
|