three-slicer 0.1.0 → 0.1.1
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/README.md +342 -24
- package/components/README.md +1 -1
- package/components/dist/SettingsPanel.js +3 -4
- package/engine/index.d.ts +3 -1
- package/engine/src/data.js +12 -0
- package/engine/src/settings.js +1 -1
- package/engine/src/slicer.worker.js +2 -1
- package/engine/src/slicer_core.js +0 -0
- package/engine/src/slicer_core.mt.js +0 -0
- package/engine/src/toggle_eval.js +1 -2
- package/package.json +54 -26
- package/types/components.d.ts +19 -0
- package/types/data/config-schema.json.d.ts +55 -0
- package/types/data/invalidation-map.json.d.ts +15 -0
- package/types/data/toggle-rules.json.d.ts +20 -0
- package/types/data/ui-tree.json.d.ts +19 -0
- package/types/data.d.ts +17 -0
- package/types/gen_settings_types.mjs +70 -0
- package/types/settings-keys.d.ts +1704 -0
- package/types/settings.d.ts +19 -0
- package/types/toggle.d.ts +22 -0
- package/types/viewer-loaders.d.ts +33 -0
- package/types/viewer-toolpath.d.ts +105 -0
- package/types/viewer.d.ts +15 -0
- package/types/wasm.d.ts +5 -0
- package/types/worker.d.ts +3 -0
- package/viewer/README.md +1 -1
- package/viewer/dist/Viewport.js +674 -617
- package/viewer/dist/model_loaders.js +220 -108
- package/viewer/styles.css +2 -0
- package/data/config-schema-builddump.json +0 -12921
- package/viewer/src/Viewport.jsx +0 -1353
- package/viewer/src/assets/add.svg +0 -1
- package/viewer/src/assets/arrange.svg +0 -1
- package/viewer/src/assets/boolean.svg +0 -1
- package/viewer/src/assets/cut.svg +0 -1
- package/viewer/src/assets/delete.svg +0 -1
- package/viewer/src/assets/deleteall.svg +0 -1
- package/viewer/src/assets/duplicate.svg +0 -1
- package/viewer/src/assets/measure.svg +0 -1
- package/viewer/src/assets/mmu.svg +0 -1
- package/viewer/src/assets/move.svg +0 -1
- package/viewer/src/assets/negative.svg +0 -1
- package/viewer/src/assets/onbed.svg +0 -1
- package/viewer/src/assets/open.svg +0 -1
- package/viewer/src/assets/orient.svg +0 -1
- package/viewer/src/assets/paint.svg +0 -1
- package/viewer/src/assets/rotate.svg +0 -1
- package/viewer/src/assets/scale.svg +0 -1
- package/viewer/src/assets/seam.svg +0 -1
- package/viewer/src/assets/split.svg +0 -1
- package/viewer/src/assets/textmark.svg +0 -1
- package/viewer/src/assets/varlayer.svg +0 -1
- package/viewer/src/icons.js +0 -23
- package/viewer/src/make_worker.js +0 -9
- package/viewer/src/model_loaders.js +0 -178
- package/viewer/src/shadow_host.jsx +0 -30
- package/viewer/src/toolpath_gpu.js +0 -430
package/README.md
CHANGED
|
@@ -1,44 +1,362 @@
|
|
|
1
1
|
# three-slicer
|
|
2
2
|
|
|
3
|
-
Browser/WASM 3D-printing slicer
|
|
4
|
-
Slices STL to G-code entirely in the browser (or Node) — no server.
|
|
3
|
+
Browser/WASM 3D-printing slicer package derived from [OrcaSlicer](https://github.com/OrcaSlicer/OrcaSlicer). It can slice binary STL files to G-code in Node or the browser, and it also ships React UI pieces for building a browser slicer: a three.js viewport, GPU toolpath preview, and a schema-driven settings panel.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
tree/grid supports, support painting, raft/brim/skirt, ironing, arc fitting, multi-material,
|
|
8
|
-
layer streaming (OOM-tolerant), multithreaded kernel auto-selected on cross-origin-isolated sites.
|
|
9
|
-
- **Viewer** — React `<Viewport/>`: model import (STL/OBJ/3MF/AMF/PLY), transform gizmos,
|
|
10
|
-
multi-plate, GPU toolpath preview (libvgcode port). Shadow-DOM isolated — no CSS collisions.
|
|
11
|
-
- **Settings UI** — React `<SettingsPanel/>`: 907 options driven by the extracted OrcaSlicer
|
|
12
|
-
config schema, with search, mode filters, and custom widget registry.
|
|
5
|
+
The package is published as a single npm package, `three-slicer`, with subpath exports for the engine, viewer, components, worker, and extracted OrcaSlicer data.
|
|
13
6
|
|
|
14
|
-
##
|
|
7
|
+
## Links
|
|
8
|
+
|
|
9
|
+
- Package: [npmjs.com/package/three-slicer](https://www.npmjs.com/package/three-slicer)
|
|
10
|
+
- Source: [kimgh06/Web_Three_Slicer](https://github.com/kimgh06/Web_Three_Slicer)
|
|
11
|
+
- Demo: [slicer.kimgh06.com](https://slicer.kimgh06.com/)
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- WASM slicing engine with Arachne walls, infill patterns, supports, raft/brim/skirt, ironing, arc fitting, multi-material paths, and layer streaming.
|
|
16
|
+
- Headless SDK for Node or browser use with no `three.js` dependency.
|
|
17
|
+
- Browser worker entry for off-main-thread slicing.
|
|
18
|
+
- React `<Viewport/>` for model import, plate layout, transform controls, slicing, G-code export, and GPU toolpath preview.
|
|
19
|
+
- React `<SettingsPanel/>` generated from extracted OrcaSlicer metadata.
|
|
20
|
+
- Extracted data files for custom UIs: config schema, UI tree, toggle rules, and invalidation map.
|
|
21
|
+
- Automatic multithreaded WASM selection on cross-origin-isolated browser pages.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
15
24
|
|
|
16
25
|
```bash
|
|
17
|
-
npm i three-slicer
|
|
18
|
-
npm i three-slicer react react-dom three # + viewer/components
|
|
26
|
+
npm i three-slicer
|
|
19
27
|
```
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
`react`, `react-dom`, and `three` are peer dependencies and npm installs them for you. They stay peers rather than dependencies because React and three must be single instances — a duplicate copy breaks hooks and `instanceof`.
|
|
30
|
+
|
|
31
|
+
Only the viewer and components import them. If you use the headless engine, they are installed into `node_modules` but never enter your bundle:
|
|
32
|
+
|
|
33
|
+
| Import path | Needs react/three at runtime |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `three-slicer`, `/settings`, `/toggle`, `/worker`, `/wasm`, `/data` | no |
|
|
36
|
+
| `three-slicer/viewer`, `/viewer/toolpath`, `/viewer/loaders` | yes |
|
|
37
|
+
| `three-slicer/components` | react only |
|
|
38
|
+
|
|
39
|
+
## Quick Start: Headless Slicing
|
|
40
|
+
|
|
41
|
+
Use the root import when you only need STL-to-G-code slicing.
|
|
22
42
|
|
|
23
43
|
```js
|
|
24
|
-
// Headless slicing (Node or browser main thread)
|
|
25
44
|
import { createSlicer } from 'three-slicer'
|
|
26
|
-
const s = await createSlicer()
|
|
27
|
-
const { gcode, stats } = s.slice(stlArrayBuffer, params)
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
const slicer = await createSlicer()
|
|
47
|
+
|
|
48
|
+
const result = slicer.slice(stlArrayBuffer, {
|
|
49
|
+
layer_height: 0.2,
|
|
50
|
+
first_layer_height: 0.2,
|
|
51
|
+
line_width: 0.42,
|
|
52
|
+
wall_loops: 2,
|
|
53
|
+
infill_density: 0.15,
|
|
54
|
+
nozzle_diameter: 0.4,
|
|
55
|
+
filament_diameter: 1.75,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
console.log(result.stats.layers)
|
|
59
|
+
console.log(result.gcode)
|
|
60
|
+
|
|
61
|
+
slicer.dispose()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`slice()` accepts a binary STL as an `ArrayBuffer` or `Uint8Array`. Parameters can be either a kernel params object or a JSON string.
|
|
65
|
+
|
|
66
|
+
## Streaming Layers
|
|
67
|
+
|
|
68
|
+
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.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const chunks = []
|
|
72
|
+
|
|
73
|
+
const result = slicer.slice(stlArrayBuffer, params, {
|
|
74
|
+
onProgress(done, total) {
|
|
75
|
+
console.log(`${done}/${total}`)
|
|
76
|
+
},
|
|
77
|
+
onLayer(layer) {
|
|
78
|
+
chunks.push(layer.gcode)
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const gcode = chunks.join('')
|
|
83
|
+
console.log(result.stats)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
When `onLayer` is set, the returned result carries stats; assemble G-code from the layer callback if you need the full file.
|
|
87
|
+
|
|
88
|
+
## Quick Start: React Viewer
|
|
89
|
+
|
|
90
|
+
Use `three-slicer/viewer` when you want a ready-made browser slicer viewport.
|
|
91
|
+
|
|
92
|
+
```jsx
|
|
93
|
+
import { useState } from 'react'
|
|
94
|
+
import Viewport from 'three-slicer/viewer'
|
|
95
|
+
|
|
96
|
+
export default function App() {
|
|
97
|
+
const [settings, setSettings] = useState({})
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Viewport
|
|
101
|
+
settings={settings}
|
|
102
|
+
setSettings={setSettings}
|
|
103
|
+
/>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The viewer handles model loading, drag and drop, transform controls, multi-plate layout, worker slicing, GPU toolpath preview, and G-code export. It supports STL, OBJ, 3MF (including the production extension used by Orca/Bambu/Prusa), AMF, and PLY out of the box; other formats such as STEP can be added with `registerLoader()`. Viewer and component styles are bundled into their Shadow DOM roots, so host app CSS does not need to import package CSS.
|
|
109
|
+
|
|
110
|
+
## Quick Start: Settings Panel
|
|
111
|
+
|
|
112
|
+
Use `three-slicer/components` when you want the OrcaSlicer-style settings UI without building the form yourself.
|
|
113
|
+
|
|
114
|
+
```jsx
|
|
115
|
+
import { useState } from 'react'
|
|
116
|
+
import SettingsPanel from 'three-slicer/components'
|
|
117
|
+
|
|
118
|
+
export default function App() {
|
|
119
|
+
const [settings, setSettings] = useState({})
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<SettingsPanel
|
|
123
|
+
settings={settings}
|
|
124
|
+
setSettings={setSettings}
|
|
125
|
+
/>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`settings` is a sparse object keyed by OrcaSlicer setting names. Store only edited values; missing keys fall back to schema defaults.
|
|
131
|
+
|
|
132
|
+
## Full React Example
|
|
133
|
+
|
|
134
|
+
The viewer and settings panel are designed to share the same `settings` state.
|
|
135
|
+
|
|
136
|
+
```jsx
|
|
137
|
+
import { useState } from 'react'
|
|
30
138
|
import Viewport from 'three-slicer/viewer'
|
|
31
139
|
import SettingsPanel from 'three-slicer/components'
|
|
32
|
-
// <Viewport settings={settings} setSettings={setSettings} />
|
|
33
140
|
|
|
34
|
-
|
|
35
|
-
|
|
141
|
+
export default function App() {
|
|
142
|
+
const [settings, setSettings] = useState({})
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<main style={{ display: 'flex', height: '100vh' }}>
|
|
146
|
+
<Viewport
|
|
147
|
+
settings={settings}
|
|
148
|
+
setSettings={setSettings}
|
|
149
|
+
/>
|
|
150
|
+
<SettingsPanel
|
|
151
|
+
settings={settings}
|
|
152
|
+
setSettings={setSettings}
|
|
153
|
+
/>
|
|
154
|
+
</main>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
36
157
|
```
|
|
37
158
|
|
|
38
|
-
|
|
39
|
-
|
|
159
|
+
The same sparse settings map can feed the settings panel, the viewer, and `deriveKernelParams()`.
|
|
160
|
+
|
|
161
|
+
## Import Paths
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
import { createSlicer, engineWorkerURL } from 'three-slicer'
|
|
165
|
+
import { deriveKernelParams } from 'three-slicer/settings'
|
|
166
|
+
import { disabledKeys, makeCfg } from 'three-slicer/toggle'
|
|
167
|
+
import Viewport from 'three-slicer/viewer'
|
|
168
|
+
import SettingsPanel from 'three-slicer/components'
|
|
169
|
+
import { schema, uiTree } from 'three-slicer/data'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Available subpaths:
|
|
173
|
+
|
|
174
|
+
| Import path | Use |
|
|
175
|
+
| --- | --- |
|
|
176
|
+
| `three-slicer` | Headless WASM engine SDK |
|
|
177
|
+
| `three-slicer/settings` | Schema defaults and settings-to-kernel mapping |
|
|
178
|
+
| `three-slicer/toggle` | Enable/disable rule evaluation for settings UIs |
|
|
179
|
+
| `three-slicer/worker` | Browser worker entry |
|
|
180
|
+
| `three-slicer/wasm` | Single-threaded Emscripten WASM glue |
|
|
181
|
+
| `three-slicer/wasm-mt` | Multithreaded Emscripten WASM glue |
|
|
182
|
+
| `three-slicer/viewer` | React `<Viewport/>` |
|
|
183
|
+
| `three-slicer/viewer/toolpath` | GPU toolpath renderer utilities |
|
|
184
|
+
| `three-slicer/viewer/loaders` | Model loaders |
|
|
185
|
+
| `three-slicer/components` | React `<SettingsPanel/>` |
|
|
186
|
+
| `three-slicer/data` | Named exports for extracted metadata |
|
|
187
|
+
| `three-slicer/data/*.json` | Extracted OrcaSlicer metadata |
|
|
188
|
+
|
|
189
|
+
## TypeScript
|
|
190
|
+
|
|
191
|
+
Type declarations ship with the package — no `@types/*` needed. All 907 setting keys are typed from the config schema, enum values included:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { createSlicer, type SlicerSettings } from 'three-slicer'
|
|
195
|
+
import { deriveKernelParams } from 'three-slicer/settings'
|
|
196
|
+
|
|
197
|
+
const settings: SlicerSettings = {
|
|
198
|
+
layer_height: 0.2,
|
|
199
|
+
sparse_infill_pattern: 'gyroid', // autocompletes; 'nope' is a type error
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const slicer = await createSlicer()
|
|
203
|
+
slicer.slice(stlArrayBuffer, deriveKernelParams(settings))
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Requires `"moduleResolution": "bundler"`, `"node16"`, or `"nodenext"`. The declarations are wired through the `exports` field, which the legacy `"node"` resolution ignores.
|
|
207
|
+
|
|
208
|
+
## Settings Model
|
|
209
|
+
|
|
210
|
+
There are two related settings shapes:
|
|
211
|
+
|
|
212
|
+
| Shape | Used by | Description |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| Sparse OrcaSlicer settings map | `<SettingsPanel/>`, `<Viewport/>` | Object keyed by original OrcaSlicer schema keys. Missing keys use schema defaults. |
|
|
215
|
+
| Kernel params | `slicer.slice()` | Flat object consumed directly by the WASM kernel. |
|
|
216
|
+
|
|
217
|
+
To convert UI settings to kernel params:
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
import { deriveKernelParams } from 'three-slicer/settings'
|
|
221
|
+
|
|
222
|
+
const settings = {
|
|
223
|
+
layer_height: 0.25,
|
|
224
|
+
sparse_infill_density: 15,
|
|
225
|
+
wall_loops: 3,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const params = deriveKernelParams(settings)
|
|
229
|
+
const result = slicer.slice(stlArrayBuffer, params)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`deriveKernelParams()` maps the curated set of schema keys currently supported by the kernel. Other schema keys can still be displayed by the UI, but they may not affect slicing output yet. Vector options are simplified to their first element.
|
|
233
|
+
|
|
234
|
+
## Worker Usage
|
|
235
|
+
|
|
236
|
+
Use a module worker to run slicing off the browser main thread.
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
import { engineWorkerURL } from 'three-slicer'
|
|
240
|
+
|
|
241
|
+
const worker = new Worker(engineWorkerURL(), { type: 'module' })
|
|
242
|
+
|
|
243
|
+
worker.onmessage = (event) => {
|
|
244
|
+
const message = event.data
|
|
245
|
+
|
|
246
|
+
if (message.type === 'progress') {
|
|
247
|
+
console.log(message.done, message.total)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (message.type === 'layer') {
|
|
251
|
+
console.log(message.idx, message.z, message.gcode)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (message.type === 'done') {
|
|
255
|
+
console.log(message.stats)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (message.type === 'error') {
|
|
259
|
+
console.error(message.error)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The worker protocol emits `progress`, `layer`, `done`, and `error` messages. For bundler-specific setups, `three-slicer/worker` exposes the worker entry directly.
|
|
265
|
+
|
|
266
|
+
## Bundler Notes
|
|
267
|
+
|
|
268
|
+
Vite consumers should enable ES module workers and an ES2022 build target:
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
import { defineConfig } from 'vite'
|
|
272
|
+
|
|
273
|
+
export default defineConfig({
|
|
274
|
+
worker: { format: 'es' },
|
|
275
|
+
build: { target: 'es2022' },
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
For Next.js or webpack apps, render the viewer on the client side and disable Node-only module aliases used by Emscripten guards.
|
|
280
|
+
|
|
281
|
+
```js
|
|
282
|
+
module.exports = {
|
|
283
|
+
webpack: (config) => {
|
|
284
|
+
for (const moduleName of [
|
|
285
|
+
'node:module',
|
|
286
|
+
'node:fs',
|
|
287
|
+
'node:path',
|
|
288
|
+
'node:url',
|
|
289
|
+
'node:crypto',
|
|
290
|
+
'node:worker_threads',
|
|
291
|
+
]) {
|
|
292
|
+
config.resolve.alias[moduleName] = false
|
|
293
|
+
}
|
|
294
|
+
return config
|
|
295
|
+
},
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Multithreaded WASM
|
|
300
|
+
|
|
301
|
+
The engine ships single-threaded and multithreaded WASM builds. The browser worker automatically uses the multithreaded kernel when the page is cross-origin isolated.
|
|
302
|
+
|
|
303
|
+
Serve these headers to enable it:
|
|
304
|
+
|
|
305
|
+
```txt
|
|
306
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
307
|
+
Cross-Origin-Embedder-Policy: require-corp
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Without those headers, the engine falls back to the single-threaded kernel.
|
|
311
|
+
|
|
312
|
+
## Data Files
|
|
313
|
+
|
|
314
|
+
The package includes extracted OrcaSlicer metadata for custom interfaces:
|
|
315
|
+
|
|
316
|
+
| File | Use |
|
|
317
|
+
| --- | --- |
|
|
318
|
+
| `three-slicer/data/config-schema.json` | Setting definitions, defaults, labels, units, and option metadata |
|
|
319
|
+
| `three-slicer/data/ui-tree.json` | Tab, page, group, and option layout |
|
|
320
|
+
| `three-slicer/data/toggle-rules.json` | Original enable/disable rule metadata |
|
|
321
|
+
| `three-slicer/data/invalidation-map.json` | Setting invalidation/dependency metadata |
|
|
322
|
+
|
|
323
|
+
## API Reference
|
|
324
|
+
|
|
325
|
+
| API | Description |
|
|
326
|
+
| --- | --- |
|
|
327
|
+
| `createSlicer()` | Loads the WASM kernel and returns a slicer handle |
|
|
328
|
+
| `slicer.slice(stl, params, callbacks?)` | Slices binary STL input to G-code or streamed layers |
|
|
329
|
+
| `slicer.paintPrepare(stl)` | Prepares support painting against a model |
|
|
330
|
+
| `slicer.paint(args)` | Paints support enforcer/blocker data |
|
|
331
|
+
| `slicer.paintClear()` | Clears support painting data |
|
|
332
|
+
| `slicer.overlay(enforcer)` | Returns support painting overlay data |
|
|
333
|
+
| `slicer.heapSize()` | Returns current WASM heap size |
|
|
334
|
+
| `slicer.dispose()` | Releases the slicer handle for garbage collection |
|
|
335
|
+
| `engineWorkerURL()` | Returns a browser worker URL |
|
|
336
|
+
| `deriveKernelParams(settings)` | Converts sparse OrcaSlicer settings to kernel params |
|
|
337
|
+
| `<Viewport/>` | React slicer viewport |
|
|
338
|
+
| `<SettingsPanel/>` | React settings form |
|
|
339
|
+
|
|
340
|
+
## Runtime Support
|
|
341
|
+
|
|
342
|
+
| Environment | Supported use |
|
|
343
|
+
| --- | --- |
|
|
344
|
+
| Node ESM | Headless slicing |
|
|
345
|
+
| Browser main thread | Headless slicing |
|
|
346
|
+
| Browser worker | Off-main-thread slicing and streaming |
|
|
347
|
+
| React | Viewer and settings panel |
|
|
348
|
+
| three.js | Required by `three-slicer/viewer` |
|
|
349
|
+
|
|
350
|
+
## Known Limits
|
|
351
|
+
|
|
352
|
+
- `slice()` currently takes binary STL input at the engine API boundary.
|
|
353
|
+
- The viewer can import STL, OBJ, 3MF, AMF, and PLY, then converts them for slicing.
|
|
354
|
+
- `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).
|
|
355
|
+
- Not every OrcaSlicer schema key is wired into the WASM kernel yet.
|
|
356
|
+
- Some vector settings are simplified to their first element.
|
|
357
|
+
- Multithreaded WASM requires cross-origin isolation.
|
|
358
|
+
- `three` is pinned as a peer dependency because viewer internals depend on the `TransformControls` API shape.
|
|
40
359
|
|
|
41
360
|
## License
|
|
42
361
|
|
|
43
|
-
AGPL-3.0-or-later
|
|
44
|
-
Source: https://github.com/kimgh06/Web_Three_Slicer
|
|
362
|
+
AGPL-3.0-or-later. This package is derived from OrcaSlicer. Source is available at [kimgh06/Web_Three_Slicer](https://github.com/kimgh06/Web_Three_Slicer). If you embed it in a web app, make sure your app complies with the AGPL network-use requirements, including offering source code to users where required.
|
package/components/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# three-slicer/components
|
|
2
2
|
|
|
3
|
-
Reusable React components for the browser slicer. Props-driven — no global state, no React context, no router coupling (proven standalone by the tarball consumer gate `
|
|
3
|
+
Reusable React components for the browser slicer. Props-driven — no global state, no React context, no router coupling (proven standalone by the tarball consumer gate `packages/pack_check.sh`).
|
|
4
4
|
|
|
5
5
|
## `<SettingsPanel/>`
|
|
6
6
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import e, { useRef as R, useState as f, useLayoutEffect as F, useEffect as J, useMemo as z } from "react";
|
|
2
2
|
import { createPortal as O } from "react-dom";
|
|
3
|
-
import C from "three-slicer/data
|
|
4
|
-
import M from "three-slicer/data/ui-tree.json";
|
|
3
|
+
import { uiTree as M, schema as C } from "three-slicer/data";
|
|
5
4
|
import { settingRaw as P } from "three-slicer/settings";
|
|
6
5
|
import { disabledKeys as I, makeCfg as B } from "three-slicer/toggle";
|
|
7
6
|
function D({ css: t, className: r, children: n }) {
|
|
@@ -163,7 +162,7 @@ function V({ optKey: t, settings: r, setSettings: n, disabled: s, onOptionOpen:
|
|
|
163
162
|
}), c = (o == null ? void 0 : o.label) || (o == null ? void 0 : o.full_label) || t;
|
|
164
163
|
return /* @__PURE__ */ e.createElement("div", { className: "row" + (m ? " disabled" : ""), title: m ? `비활성 조건: ${i}` : (o == null ? void 0 : o.tooltip) ?? "", "data-testid": `row-${t}` }, /* @__PURE__ */ e.createElement("span", { className: "lbl-cell" }, h && /* @__PURE__ */ e.createElement("span", { className: "dirty-dot", title: "기본값에서 변경됨", "data-testid": `dirty-${t}` }), l ? /* @__PURE__ */ e.createElement("button", { type: "button", className: "lbl lbl-link", onClick: () => l(t), title: "상세" }, c) : /* @__PURE__ */ e.createElement("span", { className: "lbl" }, c)), /* @__PURE__ */ e.createElement("code", { className: "key" }, t), /* @__PURE__ */ e.createElement(X, { mode: o == null ? void 0 : o.mode }), /* @__PURE__ */ e.createElement("span", { className: "widget-cell" }, /* @__PURE__ */ e.createElement(Y, { def: o, optKey: t, settings: r, setSettings: n, disabled: m, customWidgets: d }), h && /* @__PURE__ */ e.createElement("button", { className: "reset-btn", onClick: u, title: "기본값으로 리셋", "data-testid": `reset-${t}` }, "↺")));
|
|
165
164
|
}
|
|
166
|
-
function
|
|
165
|
+
function ne({ settings: t, setSettings: r, onOptionOpen: n, embedded: s = !1, customWidgets: l }) {
|
|
167
166
|
const [d, o] = f(j[0] ?? ""), [i, m] = f(0), [h, u] = f("all"), [c, x] = f(""), g = M[d] ?? [], y = g[Math.min(i, g.length - 1)], v = z(() => I(B(t)), [t]), w = c.trim().toLowerCase(), N = z(() => w ? Object.entries(C).filter(([a, p]) => {
|
|
168
167
|
var b, k;
|
|
169
168
|
return a.includes(w) || ((b = p.label) == null ? void 0 : b.toLowerCase().includes(w)) || ((k = p.full_label) == null ? void 0 : k.toLowerCase().includes(w));
|
|
@@ -176,5 +175,5 @@ function re({ settings: t, setSettings: r, onOptionOpen: n, embedded: s = !1, cu
|
|
|
176
175
|
})) : /* @__PURE__ */ e.createElement("p", { className: "muted" }, "빌더 없음"))));
|
|
177
176
|
}
|
|
178
177
|
export {
|
|
179
|
-
|
|
178
|
+
ne as default
|
|
180
179
|
};
|
package/engine/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Type definitions for
|
|
1
|
+
// Type definitions for three-slicer (engine entry point)
|
|
2
|
+
export type { SlicerSettings, SettingKey, Point } from '../types/settings-keys.d.ts'
|
|
3
|
+
|
|
2
4
|
export interface SliceLayer { z: number; idx: number; gcode: string; paths: Float32Array; widths: Float32Array }
|
|
3
5
|
export interface SliceCallbacks {
|
|
4
6
|
onProgress?: (done: number, total: number) => void
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// 추출 JSON 4종의 유일한 import 지점.
|
|
2
|
+
//
|
|
3
|
+
// 왜 한 군데로 모으는가: JSON 은 Node 22+ ESM 에서 `with { type: 'json' }` 이 필수인데,
|
|
4
|
+
// Vite/esbuild 는 번들 출력에서 이 attribute 를 떼어낸다(build.target·importAttributesKey 로
|
|
5
|
+
// 막을 수 없음 — 실측). 그래서 같은 JSON 을 "attribute 있는 원본(engine/src/*.js)" 과
|
|
6
|
+
// "attribute 없는 번들(components/dist)" 이 동시에 가리키면 소비자 번들러가
|
|
7
|
+
// "inconsistent import attributes" 로 경고한다. 여기 한 곳만 JSON 을 잡고 나머지는
|
|
8
|
+
// 이 모듈을 external 로 import → 번들에 JSON import 자체가 남지 않아 불일치가 성립하지 않는다.
|
|
9
|
+
export { default as schema } from 'three-slicer/data/config-schema.json' with { type: 'json' }
|
|
10
|
+
export { default as uiTree } from 'three-slicer/data/ui-tree.json' with { type: 'json' }
|
|
11
|
+
export { default as toggleRules } from 'three-slicer/data/toggle-rules.json' with { type: 'json' }
|
|
12
|
+
export { default as invalidationMap } from 'three-slicer/data/invalidation-map.json' with { type: 'json' }
|
package/engine/src/settings.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 우측 설정 패널(편집 가능)의 실제 값 → 커널 슬라이스 파라미터 매핑.
|
|
2
2
|
// - 설정 상태는 sparse 맵(key→value): 편집한 키만 저장, 없으면 config-schema default.
|
|
3
3
|
// - 벡터형(coFloats/coInts 등)은 첫 원소만 사용/편집(단순화).
|
|
4
|
-
import schema from '
|
|
4
|
+
import { schema } from './data.js'
|
|
5
5
|
|
|
6
6
|
export function schemaDefault(key) { return schema[key]?.default }
|
|
7
7
|
export function settingRaw(settings, key) { return (settings && key in settings) ? settings[key] : schemaDefault(key) }
|
|
@@ -47,8 +47,9 @@ self.onmessage = async (e) => {
|
|
|
47
47
|
try {
|
|
48
48
|
// Module.HEAPU8 은 emscripten 6.x 글루가 노출하지 않음 — embind typed_memory_view 로 버퍼 획득
|
|
49
49
|
const v = Module.sup_progress_view && Module.sup_progress_view()
|
|
50
|
+
const c = Module.cancel_flag_view && Module.cancel_flag_view()
|
|
50
51
|
if (v && v.buffer instanceof SharedArrayBuffer)
|
|
51
|
-
self.postMessage({ type: 'supsab', buf: v.buffer, ptr: v.byteOffset })
|
|
52
|
+
self.postMessage({ type: 'supsab', buf: v.buffer, ptr: v.byteOffset, cancelPtr: c ? c.byteOffset : 0 })
|
|
52
53
|
} catch {}
|
|
53
54
|
}
|
|
54
55
|
// 기본: 슬라이스. 레이어 싱크 등록 → 커널이 레이어마다 콜백(z, idx, gcodeChunk, pathsF32, widthsF32).
|
|
Binary file
|
|
Binary file
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// 범위: 뷰어가 다루는 키에 관련된 규칙 중, 아래 하드코딩 로컬로 "완전히" 번역 가능한 것만 적용.
|
|
3
3
|
// 번역 불가(모르는 로컬/enum 비교 등)는 fail-open(활성 유지) — 잘못된 비활성 방지(정직한 부분 구현).
|
|
4
4
|
// 전체 907키·231규칙 완역은 범위 외(README 기록). 참조: toggle-rules.json 의 toggle_print_fff_options.locals.
|
|
5
|
-
import toggles from '
|
|
6
|
-
import schema from 'three-slicer/data/config-schema.json'
|
|
5
|
+
import { toggleRules as toggles, schema } from './data.js'
|
|
7
6
|
|
|
8
7
|
// 설정값 접근기 (settings 맵 우선, 없으면 스키마 default). 퍼센트("15%")는 숫자로.
|
|
9
8
|
export function makeCfg(settings) {
|
package/package.json
CHANGED
|
@@ -1,55 +1,83 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "three-slicer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Browser/WASM 3D-printing slicer reverse-engineered from OrcaSlicer: slicing engine (Arachne walls, tree/grid supports, multi-material), React viewer with GPU toolpath preview, and schema-driven settings UI.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./engine/index.d.ts",
|
|
10
|
-
"
|
|
10
|
+
"default": "./engine/index.js"
|
|
11
11
|
},
|
|
12
|
-
"./settings":
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"./
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"./
|
|
21
|
-
|
|
12
|
+
"./settings": {
|
|
13
|
+
"types": "./types/settings.d.ts",
|
|
14
|
+
"default": "./engine/src/settings.js"
|
|
15
|
+
},
|
|
16
|
+
"./toggle": {
|
|
17
|
+
"types": "./types/toggle.d.ts",
|
|
18
|
+
"default": "./engine/src/toggle_eval.js"
|
|
19
|
+
},
|
|
20
|
+
"./worker": {
|
|
21
|
+
"types": "./types/worker.d.ts",
|
|
22
|
+
"default": "./engine/src/slicer.worker.js"
|
|
23
|
+
},
|
|
24
|
+
"./wasm": {
|
|
25
|
+
"types": "./types/wasm.d.ts",
|
|
26
|
+
"default": "./engine/src/slicer_core.js"
|
|
27
|
+
},
|
|
28
|
+
"./wasm-mt": {
|
|
29
|
+
"types": "./types/wasm.d.ts",
|
|
30
|
+
"default": "./engine/src/slicer_core.mt.js"
|
|
31
|
+
},
|
|
32
|
+
"./viewer": {
|
|
33
|
+
"types": "./types/viewer.d.ts",
|
|
34
|
+
"default": "./viewer/dist/Viewport.js"
|
|
35
|
+
},
|
|
36
|
+
"./viewer/toolpath": {
|
|
37
|
+
"types": "./types/viewer-toolpath.d.ts",
|
|
38
|
+
"default": "./viewer/dist/toolpath_gpu.js"
|
|
39
|
+
},
|
|
40
|
+
"./viewer/loaders": {
|
|
41
|
+
"types": "./types/viewer-loaders.d.ts",
|
|
42
|
+
"default": "./viewer/dist/model_loaders.js"
|
|
43
|
+
},
|
|
44
|
+
"./components": {
|
|
45
|
+
"types": "./types/components.d.ts",
|
|
46
|
+
"default": "./components/dist/SettingsPanel.js"
|
|
47
|
+
},
|
|
48
|
+
"./data": {
|
|
49
|
+
"types": "./types/data.d.ts",
|
|
50
|
+
"default": "./engine/src/data.js"
|
|
51
|
+
},
|
|
52
|
+
"./data/*": {
|
|
53
|
+
"types": "./types/data/*.d.ts",
|
|
54
|
+
"default": "./data/*"
|
|
55
|
+
}
|
|
22
56
|
},
|
|
23
57
|
"scripts": {
|
|
24
|
-
"build": "vite build --config components/vite.config.js && vite build --config viewer/vite.config.js && cp viewer/src/make_worker.js viewer/dist/make_worker.js"
|
|
58
|
+
"build": "node types/gen_settings_types.mjs && vite build --config components/vite.config.js && vite build --config viewer/vite.config.js && cp viewer/src/make_worker.js viewer/dist/make_worker.js",
|
|
59
|
+
"check": "bash pack_check.sh"
|
|
25
60
|
},
|
|
26
61
|
"peerDependencies": {
|
|
27
62
|
"react": ">=18",
|
|
28
63
|
"react-dom": ">=18",
|
|
29
64
|
"three": "^0.160.0"
|
|
30
65
|
},
|
|
31
|
-
"peerDependenciesMeta": {
|
|
32
|
-
"react": {
|
|
33
|
-
"optional": true
|
|
34
|
-
},
|
|
35
|
-
"react-dom": {
|
|
36
|
-
"optional": true
|
|
37
|
-
},
|
|
38
|
-
"three": {
|
|
39
|
-
"optional": true
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
66
|
"devDependencies": {
|
|
67
|
+
"typescript": "^5.6.0",
|
|
43
68
|
"vite": "^5.4.11"
|
|
44
69
|
},
|
|
45
70
|
"files": [
|
|
46
71
|
"engine",
|
|
72
|
+
"types",
|
|
47
73
|
"components/dist",
|
|
48
74
|
"viewer/dist",
|
|
49
|
-
"viewer/src",
|
|
50
75
|
"viewer/styles.css",
|
|
51
76
|
"components/styles.css",
|
|
52
|
-
"data
|
|
77
|
+
"data/config-schema.json",
|
|
78
|
+
"data/ui-tree.json",
|
|
79
|
+
"data/toggle-rules.json",
|
|
80
|
+
"data/invalidation-map.json",
|
|
53
81
|
"README.md",
|
|
54
82
|
"LICENSE.txt"
|
|
55
83
|
],
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// three-slicer/components
|
|
2
|
+
import type * as React from 'react'
|
|
3
|
+
import type { SlicerSettings } from './settings-keys.d.ts'
|
|
4
|
+
|
|
5
|
+
export interface SettingsPanelProps {
|
|
6
|
+
/** sparse 맵 — 편집한 키만. 없는 키는 config-schema default 가 쓰인다. */
|
|
7
|
+
settings: SlicerSettings
|
|
8
|
+
/** React setState 형태. 상태가 컴포넌트 밖으로 나가는 유일한 통로. */
|
|
9
|
+
setSettings: React.Dispatch<React.SetStateAction<SlicerSettings>>
|
|
10
|
+
/** 라벨 클릭 — 딥링크/상세용. 생략하면 평범한 라벨(라우터 결합 0). */
|
|
11
|
+
onOptionOpen?: (optKey: string) => void
|
|
12
|
+
/** 다른 패널 안에 끼워 넣을 때 */
|
|
13
|
+
embedded?: boolean
|
|
14
|
+
/** 스키마 구동 위젯을 키 단위로 교체 (예: `{ printable_area: MyBedEditor }`) */
|
|
15
|
+
customWidgets?: Record<string, React.ComponentType<any>>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare const SettingsPanel: React.FC<SettingsPanelProps>
|
|
19
|
+
export default SettingsPanel
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// three-slicer/data/config-schema.json — OrcaSlicer 원본에서 추출한 907개 옵션 정의.
|
|
2
|
+
// ponytail: 값을 리터럴로 좁히지 않는다. 907키 × 33필드를 리터럴로 추론시키면 소비자 tsc 가 기어간다.
|
|
3
|
+
// 키 단위 정확도가 필요하면 SlicerSettings(three-slicer/settings) 쪽을 쓸 것.
|
|
4
|
+
|
|
5
|
+
/** 원본 ConfigOptionDef 의 coXxx 타입 태그 */
|
|
6
|
+
export type ConfigOptionType =
|
|
7
|
+
| 'coBool' | 'coBools'
|
|
8
|
+
| 'coInt' | 'coInts'
|
|
9
|
+
| 'coFloat' | 'coFloats'
|
|
10
|
+
| 'coPercent' | 'coPercents'
|
|
11
|
+
| 'coFloatOrPercent' | 'coFloatsOrPercents'
|
|
12
|
+
| 'coString' | 'coStrings'
|
|
13
|
+
| 'coEnum' | 'coEnums'
|
|
14
|
+
| 'coPoint' | 'coPoints' | 'coPointsGroups'
|
|
15
|
+
|
|
16
|
+
/** 907키 중 `type` 과 `defined_in` 만 전부 존재한다. 나머지는 전부 optional. */
|
|
17
|
+
export interface ConfigOption {
|
|
18
|
+
type: ConfigOptionType
|
|
19
|
+
/** 추출된 원본 함수명 (예: 'init_common_params') */
|
|
20
|
+
defined_in: string
|
|
21
|
+
line?: number
|
|
22
|
+
mode?: string
|
|
23
|
+
label?: string
|
|
24
|
+
full_label?: string
|
|
25
|
+
tooltip?: string
|
|
26
|
+
sidetext?: string
|
|
27
|
+
category?: string
|
|
28
|
+
default?: unknown
|
|
29
|
+
default_type?: string
|
|
30
|
+
default_raw?: string
|
|
31
|
+
enum_type?: string
|
|
32
|
+
enum_values?: string[]
|
|
33
|
+
enum_labels?: string[]
|
|
34
|
+
enum_values_copied_from?: string
|
|
35
|
+
enum_labels_copied_from?: string
|
|
36
|
+
min?: number
|
|
37
|
+
max?: number
|
|
38
|
+
max_literal?: unknown
|
|
39
|
+
gui_type?: string
|
|
40
|
+
gui_flags?: string
|
|
41
|
+
ratio_over?: string
|
|
42
|
+
nullable?: boolean
|
|
43
|
+
readonly?: boolean
|
|
44
|
+
multiline?: boolean
|
|
45
|
+
full_width?: boolean
|
|
46
|
+
height?: number
|
|
47
|
+
cli?: string
|
|
48
|
+
cli_params?: string
|
|
49
|
+
aliases?: string[]
|
|
50
|
+
plugin_type?: string
|
|
51
|
+
generated_by_loop?: unknown
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare const schema: Record<string, ConfigOption>
|
|
55
|
+
export default schema
|