three-slicer 0.2.3 → 0.2.5
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/components/dist/SettingsPanel.js +138 -116
- package/components/styles.css +4 -0
- package/engine/src/client.js +12 -2
- package/engine/src/slicer.worker.js +51 -1
- package/engine/src/slicer_core.js +0 -0
- package/engine/src/slicer_core.mt.js +0 -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/client.d.ts +2 -2
- package/types/viewer.d.ts +41 -2
- package/types/worker.d.ts +30 -1
- package/viewer/README.md +69 -1
- package/viewer/dist/Viewport.js +5091 -3644
- package/viewer/dist/sl1_encode.worker.js +8 -8
- package/viewer/dist/sl1_write-CtX8cVL7.js +302 -0
- package/viewer/dist/sla_slice.worker.js +1 -1
- package/viewer/styles.css +45 -0
- package/viewer/dist/sl1_write-UHmQ_itC.js +0 -170
package/types/viewer.d.ts
CHANGED
|
@@ -3,12 +3,42 @@ import type * as React from 'react'
|
|
|
3
3
|
import type { SlicerSettings } from './settings-keys.d.ts'
|
|
4
4
|
|
|
5
5
|
export interface ViewportProps {
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Sparse settings map. Defaults to `{}`. Two viewer knobs ride in it beside the schema keys: `sla_antialias`
|
|
8
|
+
* (SL1 mask MSAA, 1|2|4) and `slice_workers` — how many plates an all-plates run slices at once (0/absent =
|
|
9
|
+
* Auto: half the cores on the threaded kernel, one per core on the single-threaded one; the in-app select
|
|
10
|
+
* writes it). Neither reaches a 3mf.
|
|
11
|
+
*/
|
|
7
12
|
settings?: SlicerSettings
|
|
8
13
|
/** React setState shape. Defaults to a no-op. */
|
|
9
14
|
setSettings?: React.Dispatch<React.SetStateAction<SlicerSettings>>
|
|
10
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Per-plate overrides: `{[plateIndex]: sparse map}`, merged over `settings` for that plate's slice only —
|
|
17
|
+
* a key's absence means "follow the global value" (delete the key to clear an override). Plate identity is
|
|
18
|
+
* the plate index; deleting the last plate truncates its entry. Editing one plate's override invalidates only
|
|
19
|
+
* that plate's cached result. Any key can be overridden: `printer_technology` routes that plate to the
|
|
20
|
+
* FFF or SLA slicer (an SLA plate's grid cell is sized to its resin display), and a bed override
|
|
21
|
+
* (`printable_area`/`printable_height`) resizes that plate's own grid cell. What a 3mf cannot represent —
|
|
22
|
+
* a mixed-technology or mixed-bed project — is refused at project export with a typed error
|
|
23
|
+
* (`error.code` `'UNSUPPORTED_MIXED_TECH_3MF'` / `'UNSUPPORTED_MIXED_BED_3MF'`); per-plate G-code/.sl1
|
|
24
|
+
* exports always work. Defaults to `{}`.
|
|
25
|
+
*/
|
|
26
|
+
plateSettings?: Record<number, SlicerSettings>
|
|
27
|
+
/** React setState shape for {@link ViewportProps.plateSettings}. Defaults to a no-op. */
|
|
28
|
+
setPlateSettings?: React.Dispatch<React.SetStateAction<Record<number, SlicerSettings>>>
|
|
29
|
+
/**
|
|
30
|
+
* Left process-panel slot — usually `<SettingsPanel embedded/>`. Defaults to null.
|
|
31
|
+
*
|
|
32
|
+
* Pass a FUNCTION to opt into the per-plate scope toggle (shown with more than one plate): in plate scope
|
|
33
|
+
* the panel receives the selected plate's EFFECTIVE map and a setter that writes only the changed keys into
|
|
34
|
+
* {@link ViewportProps.plateSettings}; `meta.overriddenKeys`/`meta.onRevertKey` drive the override badges
|
|
35
|
+
* (`<SettingsPanel overriddenKeys={...} onRevertKey={...}/>`). In global scope the pair is simply
|
|
36
|
+
* `settings`/`setSettings`. A plain node keeps the global-only binding.
|
|
37
|
+
*/
|
|
11
38
|
processPanel?: React.ReactNode
|
|
39
|
+
| ((settings: SlicerSettings, setSettings: React.Dispatch<React.SetStateAction<SlicerSettings>>,
|
|
40
|
+
meta: { scope: 'global' | 'plate', selectedPlate: number, overriddenKeys: string[],
|
|
41
|
+
onRevertKey: ((key: string) => void) | null }) => React.ReactNode)
|
|
12
42
|
/** Motion-limits editor, folded into the printer card — usually `<SettingsPanel only={{builder:'TabPrinter::build_kinematics_page'}}/>`. */
|
|
13
43
|
motionPanel?: React.ReactNode
|
|
14
44
|
/**
|
|
@@ -99,6 +129,15 @@ export interface ViewportProps {
|
|
|
99
129
|
plate: number; stats: Record<string, unknown>; gcode: string
|
|
100
130
|
throughput?: { ms: number; kernelMs: number | null; layersPerSecond: number; msPerMsegment: number | null }
|
|
101
131
|
}) => void
|
|
132
|
+
/**
|
|
133
|
+
* An all-plates run as it moves: the run map on every change, `null` when none is on. `workers.pool` is how
|
|
134
|
+
* many slice at once, `workers.kernel` which kernel loaded ('mt' threads / 'st'); each plate carries its state
|
|
135
|
+
* and its own progress (0..1) and layers/second. Fired several times a second while a run is on.
|
|
136
|
+
*/
|
|
137
|
+
onSliceRun?: (run: {
|
|
138
|
+
workers: { pool: number; active: number; kernel: 'mt' | 'st' | null }
|
|
139
|
+
plates: Record<number, { state: 'queued' | 'busy' | 'done' | 'failed'; progress: number; rate: number }>
|
|
140
|
+
} | null) => void
|
|
102
141
|
}
|
|
103
142
|
|
|
104
143
|
/**
|
package/types/worker.d.ts
CHANGED
|
@@ -13,6 +13,14 @@ export interface BrushArgs {
|
|
|
13
13
|
hx: number; hy: number; hz: number
|
|
14
14
|
cx: number; cy: number; cz: number
|
|
15
15
|
radius: number
|
|
16
|
+
/**
|
|
17
|
+
* The PREVIOUS sample of this stroke. Present, the kernel paints the capsule swept from there to `(hx,hy,hz)`
|
|
18
|
+
* instead of a ball at the hit — which is what upstream does between two consecutive mouse positions
|
|
19
|
+
* (`DoublePointCursor`, GLGizmoPainterBase.cpp:878). A pointer stream is sampled, not continuous, so without it
|
|
20
|
+
* a fast drag lands as a row of separate blobs. Omit on the first sample of a stroke; a kernel built before the
|
|
21
|
+
* capsule existed ignores it and paints the ball, so it is safe to always send.
|
|
22
|
+
*/
|
|
23
|
+
px?: number; py?: number; pz?: number
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
/**
|
|
@@ -56,6 +64,23 @@ export type SlicerRequest =
|
|
|
56
64
|
| { cmd: 'clear'; states?: PaintState[] }
|
|
57
65
|
/** The painted overlay triangles. `enf`/`blk` always; `states` adds those states. Reply: `overlay`. */
|
|
58
66
|
| { cmd: 'overlay'; states?: PaintState[] }
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* What a fill tool WOULD select under the cursor, without marking anything — upstream draws this on every mouse
|
|
70
|
+
* move while a fill is active (GLGizmoPainterBase.cpp:929) so a click is aimed rather than tried. No state
|
|
71
|
+
* argument at all: a preview marks nothing, so there is no NONE for a stray boolean to become.
|
|
72
|
+
* `{clear:true}` drops the standing selection. Reply: `fillPreview`.
|
|
73
|
+
*/
|
|
74
|
+
| { cmd: 'fillPreview'; facet?: number; hx?: number; hy?: number; hz?: number
|
|
75
|
+
tool?: PaintTool; angle?: number; clear?: boolean }
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Brush-wide options, set once and read by every stroke and fill — upstream keeps both on the gizmo.
|
|
79
|
+
* `overhangDeg` restricts painting to facets overhanging by more than that angle (`0` = off, the default);
|
|
80
|
+
* `clipPlane` is the section plane `[nx, ny, nz, offset]` in KERNEL coords that every cursor clips against
|
|
81
|
+
* (`null` turns it off), which is the only way a brush reaches an interior surface. Reply: `paintMode`.
|
|
82
|
+
*/
|
|
83
|
+
| { cmd: 'paintMode'; overhangDeg?: number; clipPlane?: [number, number, number, number] | null }
|
|
59
84
|
/**
|
|
60
85
|
* Legacy single-object SLA (resin) slice. `params` accepts a JSON string or an object (this path parses either). Same reply stream as
|
|
61
86
|
* a slice: `progress` and `layer` while it runs, then `done` (stats only; layers arrived as `layer`) — or `error`.
|
|
@@ -73,7 +98,8 @@ export type SlicerRequest =
|
|
|
73
98
|
| { stl: ArrayBuffer; params: string | object }
|
|
74
99
|
|
|
75
100
|
export type SlicerResponse =
|
|
76
|
-
|
|
101
|
+
/** `kernel` says which variant loaded: 'mt' under crossOriginIsolated, 'st' otherwise (or after an mt load failure). */
|
|
102
|
+
| { type: 'warm'; kernel: 'mt' | 'st' | null }
|
|
77
103
|
| { type: 'prepared'; facets: number; kept: boolean }
|
|
78
104
|
/**
|
|
79
105
|
* `enf`/`blk` are the counts for states 1 and 2 and are always present, so a listener predating the
|
|
@@ -83,6 +109,9 @@ export type SlicerResponse =
|
|
|
83
109
|
/** `supported: false` from a kernel built before the export binding existed — it does not throw. */
|
|
84
110
|
| { type: 'paintExport'; supported: boolean; facets: number[]; hex: string }
|
|
85
111
|
| { type: 'overlay'; enf: Float32Array; blk: Float32Array; overlays?: Record<number, Float32Array> }
|
|
112
|
+
/** `supported` false on a kernel built before the preview existed; `tris` is flat x,y,z, 3 vertices per triangle. */
|
|
113
|
+
| { type: 'fillPreview'; supported: boolean; tris: Float32Array }
|
|
114
|
+
| { type: 'paintMode'; ok: boolean }
|
|
86
115
|
/**
|
|
87
116
|
* Sent once, unprompted, by the multithreaded kernel only: the address of the support-progress counter and of
|
|
88
117
|
* the cancel flag, inside a SharedArrayBuffer. Writing 1 to the cancel flag stops a slice the worker is
|
package/viewer/README.md
CHANGED
|
@@ -130,6 +130,8 @@ Every panel can be switched off, and the values the component owns can be seeded
|
|
|
130
130
|
- **`onSliced`** — `{plate, stats, gcode, throughput}` when a slice is cached. Switching plate tabs does not
|
|
131
131
|
re-fire it. `throughput` sits beside `stats` rather than inside it, because that is where the engine puts it:
|
|
132
132
|
it is measured around the call, not reported by the kernel.
|
|
133
|
+
- **`onSliceRun`** — an all-plates run as it moves: `{workers: {pool, active, kernel}, plates: {[i]: {state,
|
|
134
|
+
progress, rate}}}` on every change, `null` between runs. For a host drawing its own per-plate progress.
|
|
133
135
|
|
|
134
136
|
`sliceRate` is live throughput — layers finished per second over a 250ms window, `0` between slices, and shown in
|
|
135
137
|
the slice bar as well. When it appears depends on the technology, because the two publish per-layer news at
|
|
@@ -180,6 +182,26 @@ arrows and Delete working.
|
|
|
180
182
|
| `Z` · `B` | Zoom to all · zoom to bed |
|
|
181
183
|
| `Esc` | Back to Prepare |
|
|
182
184
|
|
|
185
|
+
While a brush is open these take over the same letters — the object shortcuts come back the moment it closes.
|
|
186
|
+
|
|
187
|
+
| Painting | |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| `C` · `S` | Brush shape: circle (the visible surface) · sphere (reaches through a thin wall) |
|
|
190
|
+
| `F` · `B` · `T` | Smart fill · bucket fill · single triangle |
|
|
191
|
+
| `V` · `H` | Lock the stroke to a vertical · horizontal line — press again to release |
|
|
192
|
+
| `1`…`9` | Paint with that filament (material brush only) |
|
|
193
|
+
| `Shift + drag` | Erase instead of paint |
|
|
194
|
+
| Drag off the model | Rotate the camera — a press that misses the model is not the brush's, so orbiting never means closing it |
|
|
195
|
+
| `Ctrl/⌘ + wheel` | Brush radius, or the fill angle for a fill tool — the bare wheel stays the camera zoom |
|
|
196
|
+
| `Alt + wheel` | Scrub the section plane, which is what lets a brush reach a surface inside the model |
|
|
197
|
+
| `Esc` | Close the brush, and put back whatever the section plane cut away |
|
|
198
|
+
|
|
199
|
+
The brush draws itself while it is open — a ring for the circle cursor, a translucent ball for the sphere — and a
|
|
200
|
+
fill tool shades what a click would flood before you click it. A drag paints the **capsule** swept between samples
|
|
201
|
+
rather than one ball per sample, so a fast stroke is continuous instead of a row of blobs. A press that misses the
|
|
202
|
+
model falls through to the camera, so the view can be turned mid-paint without closing the brush. The support brush also
|
|
203
|
+
offers *on overhangs only*, which restricts every stroke to facets steeper than the support threshold.
|
|
204
|
+
|
|
183
205
|
Mouse selection follows upstream's rules: a plain click **replaces** the selection, `Ctrl/⌘ + click` adds or
|
|
184
206
|
removes, a plain click on something already selected **keeps** the set (which is what makes dragging several
|
|
185
207
|
objects work), `Shift + drag` is a box select, and clicking empty space clears. Alt is not a de-select, in
|
|
@@ -321,7 +343,53 @@ Cross-Origin-Opener-Policy: same-origin
|
|
|
321
343
|
Cross-Origin-Embedder-Policy: require-corp
|
|
322
344
|
```
|
|
323
345
|
|
|
324
|
-
No headers → falls back to the single-threaded kernel. Nothing else to change.
|
|
346
|
+
No headers → falls back to the single-threaded kernel. Nothing else to change. Which one loaded is shown as a
|
|
347
|
+
badge (`mt`/`st`) beside the Workers select in the slice menu, and the `warmup()` client call resolves with it —
|
|
348
|
+
the two are a measured 9.8x apart on a 3M-facet model, which is too much to leave to the console.
|
|
349
|
+
|
|
350
|
+
### Slicing several plates at once
|
|
351
|
+
|
|
352
|
+
**All plates** in the slice menu drains the plates through a pool of workers instead of one after another. The
|
|
353
|
+
**Workers** select beside it sets the pool: *Auto* is half the cores on the threaded kernel and one per core on the
|
|
354
|
+
single-threaded one, capped at the plate count and by model size (below); the select offers every value up to
|
|
355
|
+
that memory cap and nothing past it, because past it the tab itself dies (measured: five workers on a 143MB
|
|
356
|
+
model), which nothing in the page can catch. The plate tabs show each plate's state (queued, slicing with its own percentage, done, failed)
|
|
357
|
+
independently of which one is selected, and the slice bar lists them on one row. Cancel stops every worker. A worker that dies mid-run (memory, most
|
|
358
|
+
often) is dropped and its plate is re-run alone once the others finish, so a count that was too high for the
|
|
359
|
+
model costs time rather than results; the notice says which plates that happened to.
|
|
360
|
+
|
|
361
|
+
The numbers behind Auto (node harness, 3M-facet model, 15 cores, 30 plates, wall time against one worker):
|
|
362
|
+
|
|
363
|
+
| kernel | preset | 3 workers | 5 | 10 | 15 |
|
|
364
|
+
|---|---|---|---|---|---|
|
|
365
|
+
| mt | classic walls, no support | 1.10x | 1.15x | 1.15x | 1.14x |
|
|
366
|
+
| mt | arachne + tree support | 2.08x | 2.60x | 3.16x | 3.31x |
|
|
367
|
+
| st | classic walls, no support | — | — | — | 7.2x (14 workers) |
|
|
368
|
+
|
|
369
|
+
The gain is the kernel's serial share: with classic walls one worker already keeps ~77% of the cores busy, with
|
|
370
|
+
arachne (whose emission pass runs serial) and tree support far less, and the extra workers fill that. More workers
|
|
371
|
+
than cores gained nothing (17 = 15), and no count ever made a run slower — what a worker costs is memory: each
|
|
372
|
+
holds its own copy of the model and its own wasm heap, measured 2.5GB for one, 8.8GB for five and 11.6GB for
|
|
373
|
+
fifteen on that model. That is why Auto stops at half the cores and why the select goes higher only by hand.
|
|
374
|
+
Three things were measured NOT to matter and are deliberately absent: giving each worker a thread budget, slicing
|
|
375
|
+
the largest plate first, and whether the count divides the plate count. The value is `slice_workers` in the
|
|
376
|
+
settings map (0 = Auto), a viewer knob like `sla_antialias`, and it does not reach a 3mf.
|
|
377
|
+
|
|
378
|
+
The browser is tighter than that harness, because every worker's wasm heap lives in the one renderer process that
|
|
379
|
+
already holds each plate's STL buffer and three.js geometry. Measured in Chromium on the same 143MB-STL model
|
|
380
|
+
over nine plates (classic preset, 15 cores):
|
|
381
|
+
|
|
382
|
+
| workers | wall | Chromium RSS peak |
|
|
383
|
+
|---|---|---|
|
|
384
|
+
| 1 | 17.8s | 10.4GB |
|
|
385
|
+
| 2 | 15.7s (1.13x) | 13.7GB |
|
|
386
|
+
| 3 | crashed the tab once; 18.7s once | 10.2GB |
|
|
387
|
+
| 8 (Auto by cores) | crashed the tab | 11.6GB |
|
|
388
|
+
|
|
389
|
+
So Auto is also capped by model size — a worker's heap is taken as 12x the STL it slices (node measured ~1.7GB
|
|
390
|
+
for 143MB) against a 4GB pool budget, which gives 2 for that model and does not bind below ~40MB per plate. A
|
|
391
|
+
manual count is capped the same way. What the browser has NOT measured yet is
|
|
392
|
+
the arachne + tree-support preset, where the harness gains were largest.
|
|
325
393
|
|
|
326
394
|
## Bundler notes
|
|
327
395
|
|