three-slicer 0.1.3 → 0.1.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/README.md +67 -8
- package/components/README.md +1 -1
- package/components/dist/SettingsPanel.js +12 -12
- package/data/README.md +39 -2
- package/data/config-schema.json +164 -0
- package/data/filaments.js +2 -0
- package/data/processes.js +1 -1
- package/data/ui-tree.json +21 -2
- package/engine/README.md +56 -4
- package/engine/src/data.js +2 -0
- package/engine/src/settings.js +214 -6
- package/engine/src/slicer.worker.js +154 -5
- package/engine/src/slicer_core.js +0 -0
- package/engine/src/slicer_core.mt.js +0 -0
- package/package.json +15 -2
- package/types/data/filaments.js.d.ts +27 -0
- package/types/data.d.ts +7 -0
- package/types/settings-keys.d.ts +35 -1
- package/types/settings.d.ts +42 -0
- package/types/viewer-gcode.d.ts +29 -0
- package/types/viewer-toolpath.d.ts +9 -1
- package/types/viewer.d.ts +53 -0
- package/viewer/README.md +48 -2
- package/viewer/dist/Viewport.js +2706 -1525
- package/viewer/dist/gcode_parse.js +190 -0
- package/viewer/dist/make_worker.js +7 -0
- package/viewer/dist/model_loaders.js +139 -211
- package/viewer/dist/parse_3mf-8oCnCJNK.js +216 -0
- package/viewer/dist/parse_3mf.worker.js +10 -0
- package/viewer/dist/toolpath_gpu.js +162 -137
- package/viewer/styles.css +56 -2
package/README.md
CHANGED
|
@@ -17,7 +17,9 @@ The package is published as a single npm package, `three-slicer`, with subpath e
|
|
|
17
17
|
- Browser worker entry for off-main-thread slicing.
|
|
18
18
|
- React `<Viewport/>` for model import, plate layout, transform controls, slicing, G-code export, and GPU toolpath preview.
|
|
19
19
|
- React `<SettingsPanel/>` generated from extracted OrcaSlicer metadata.
|
|
20
|
-
-
|
|
20
|
+
- Vendor printer, print-process and filament preset catalogs, with one material assignable per extruder.
|
|
21
|
+
- Support painting and material painting, plus per-tool filament and purge statistics.
|
|
22
|
+
- Extracted data files for custom UIs: config schema, UI tree, toggle rules, invalidation map, and the printer/process/filament catalogs.
|
|
21
23
|
- Automatic multithreaded WASM selection on cross-origin-isolated browser pages.
|
|
22
24
|
|
|
23
25
|
## Installation
|
|
@@ -188,7 +190,7 @@ Available subpaths:
|
|
|
188
190
|
|
|
189
191
|
## TypeScript
|
|
190
192
|
|
|
191
|
-
Type declarations ship with the package — no `@types/*` needed. All
|
|
193
|
+
Type declarations ship with the package — no `@types/*` needed. All 923 setting keys are typed from the config schema, enum values included:
|
|
192
194
|
|
|
193
195
|
```ts
|
|
194
196
|
import { createSlicer, type SlicerSettings } from 'three-slicer'
|
|
@@ -229,7 +231,52 @@ const params = deriveKernelParams(settings)
|
|
|
229
231
|
const result = slicer.slice(stlArrayBuffer, params)
|
|
230
232
|
```
|
|
231
233
|
|
|
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.
|
|
234
|
+
`deriveKernelParams()` maps the curated set of schema keys currently supported by the kernel (92 today). 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, except the filament options described below, which keep every extruder's entry.
|
|
235
|
+
|
|
236
|
+
## Materials and Multi-Material
|
|
237
|
+
|
|
238
|
+
A material is a filament preset — temperatures, flow, diameter, cooling, and the retraction/z-hop overrides a material may apply on top of the machine's. Read the catalog through the facade rather than decoding the data file:
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
import { filamentPresets } from 'three-slicer/settings'
|
|
242
|
+
|
|
243
|
+
const filaments = await filamentPresets()
|
|
244
|
+
filaments.listFor('Bambu Lab X1 Carbon 0.4 nozzle') // [{name, type, vendor}, …]
|
|
245
|
+
filaments.recommendedFor('Bambu Lab X1 Carbon 0.4 nozzle') // the vendor's shortlist for that machine model
|
|
246
|
+
const material = filaments.settingsFor('Bambu PLA Basic @BBL X1C')
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The filament key set is disjoint from the process key set, so applying a material never clears a process pick and vice versa.
|
|
250
|
+
|
|
251
|
+
**One material per extruder.** OrcaSlicer stores every filament option as one entry per extruder, so a multi-material settings map writes each extruder's material at its own index:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
const params = {
|
|
255
|
+
...deriveKernelParams({
|
|
256
|
+
nozzle_temperature: [255, 220], // T0 ABS, T1 PLA
|
|
257
|
+
filament_flow_ratio: [0.95, 1.0],
|
|
258
|
+
}),
|
|
259
|
+
extruder_count: 2, // set on the params directly; not a derived schema key
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The kernel reloads the loaded-filament settings at every tool change and reports the split back as `stats.filament_mm_by_tool` (indexed by tool number, sums to `filament_mm`) plus `stats.filament_mm_purge` for the prime/wipe tower share. `support_filament` and `support_interface_filament` are 1-based filament indices — `0` means "keep the loaded tool" — choosing which extruder prints the support base/raft and the support interface.
|
|
264
|
+
|
|
265
|
+
A single-material slice sends none of these keys and produces exactly the G-code it produced before multi-material existed.
|
|
266
|
+
|
|
267
|
+
### Painting a region onto another extruder
|
|
268
|
+
|
|
269
|
+
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.
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
worker.postMessage({ cmd: 'prepare', stl })
|
|
273
|
+
worker.postMessage({ cmd: 'paint', state: 3, facet, hx, hy, hz, cx, cy, cz, radius, states: [1, 2, 3] })
|
|
274
|
+
worker.postMessage({ cmd: 'erase', facet, hx, hy, hz, cx, cy, cz, radius })
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`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.
|
|
278
|
+
|
|
279
|
+
Because a single enum serves both jobs, a support blocker paint and an Extruder 2 paint are the same mark on a facet, and painted materials only take effect with support turned off (the painted multi-material path emits no support). Support painting is unaffected: a support-enabled slice stays on the single-material path.
|
|
233
280
|
|
|
234
281
|
## Worker Usage
|
|
235
282
|
|
|
@@ -315,10 +362,17 @@ The package includes extracted OrcaSlicer metadata for custom interfaces:
|
|
|
315
362
|
|
|
316
363
|
| File | Use |
|
|
317
364
|
| --- | --- |
|
|
318
|
-
| `three-slicer/data/config-schema.json` | Setting definitions, defaults, labels, units, and option metadata |
|
|
365
|
+
| `three-slicer/data/config-schema.json` | Setting definitions, defaults, labels, units, and option metadata (923 options) |
|
|
319
366
|
| `three-slicer/data/ui-tree.json` | Tab, page, group, and option layout |
|
|
320
367
|
| `three-slicer/data/toggle-rules.json` | Original enable/disable rule metadata |
|
|
321
368
|
| `three-slicer/data/invalidation-map.json` | Setting invalidation/dependency metadata |
|
|
369
|
+
| `three-slicer/data/printers.json` | 1,035 vendor machine profiles across 64 vendors: motion limits, bed, nozzle |
|
|
370
|
+
| `three-slicer/data/processes.js` | 2,243 print presets (speeds, accelerations) |
|
|
371
|
+
| `three-slicer/data/filaments.js` | 5,999 material presets over 81 filament types, plus each printer model's recommended list |
|
|
372
|
+
|
|
373
|
+
The last three are large, column-oriented and deduplicated, and they load on demand. Read them through
|
|
374
|
+
`printerSettings()`, `processPresets()` and `filamentPresets()` from `three-slicer/settings` rather than decoding
|
|
375
|
+
the layout by hand.
|
|
322
376
|
|
|
323
377
|
## API Reference
|
|
324
378
|
|
|
@@ -326,14 +380,17 @@ The package includes extracted OrcaSlicer metadata for custom interfaces:
|
|
|
326
380
|
| --- | --- |
|
|
327
381
|
| `createSlicer()` | Loads the WASM kernel and returns a slicer handle |
|
|
328
382
|
| `slicer.slice(stl, params, callbacks?)` | Slices binary STL input to G-code or streamed layers |
|
|
329
|
-
| `slicer.paintPrepare(stl)` | Prepares
|
|
330
|
-
| `slicer.paint(args)` | Paints
|
|
331
|
-
| `slicer.paintClear()` | Clears
|
|
332
|
-
| `slicer.overlay(enforcer)` | Returns
|
|
383
|
+
| `slicer.paintPrepare(stl)` | Prepares painting against a model |
|
|
384
|
+
| `slicer.paint(args)` | Paints enforcer/blocker data (boolean pair; the state-addressed form is worker-only) |
|
|
385
|
+
| `slicer.paintClear()` | Clears painting data for every state |
|
|
386
|
+
| `slicer.overlay(enforcer)` | Returns painting overlay data |
|
|
333
387
|
| `slicer.heapSize()` | Returns current WASM heap size |
|
|
334
388
|
| `slicer.dispose()` | Releases the slicer handle for garbage collection |
|
|
335
389
|
| `engineWorkerURL()` | Returns a browser worker URL |
|
|
336
390
|
| `deriveKernelParams(settings)` | Converts sparse OrcaSlicer settings to kernel params |
|
|
391
|
+
| `printerSettings(name)` | Settings a vendor machine profile applies |
|
|
392
|
+
| `processPresets()` | Lazy facade over the print (process) preset catalog |
|
|
393
|
+
| `filamentPresets()` | Lazy facade over the material preset catalog |
|
|
337
394
|
| `<Viewport/>` | React slicer viewport |
|
|
338
395
|
| `<SettingsPanel/>` | React settings form |
|
|
339
396
|
|
|
@@ -354,6 +411,8 @@ The package includes extracted OrcaSlicer metadata for custom interfaces:
|
|
|
354
411
|
- `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
412
|
- Not every OrcaSlicer schema key is wired into the WASM kernel yet.
|
|
356
413
|
- Some vector settings are simplified to their first element.
|
|
414
|
+
- Material painting and support cannot currently produce two materials on the same slice: the painted multi-material path emits no support, and one triangle selector serves both brushes.
|
|
415
|
+
- The multi-material prime tower is a real ported wipe tower, but the fallback square ring used when it fails is not.
|
|
357
416
|
- Multithreaded WASM requires cross-origin isolation.
|
|
358
417
|
- `three` is pinned as a peer dependency because viewer internals depend on the `TransformControls` API shape.
|
|
359
418
|
|
package/components/README.md
CHANGED
|
@@ -4,7 +4,7 @@ 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 (simple/advanced/expert), search across 923 options, dirty markers with per-option reset, and enable/disable rules evaluation.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npm i three-slicer/components react
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import e, { useRef as
|
|
1
|
+
import e, { useRef as _, useState as h, useLayoutEffect as D, useEffect as P, useMemo as A } from "react";
|
|
2
2
|
import { createPortal as F } from "react-dom";
|
|
3
3
|
import { uiTree as S, schema as C } from "three-slicer/data";
|
|
4
4
|
import { settingRaw as J } from "three-slicer/settings";
|
|
5
5
|
import { disabledKeys as I, makeCfg as B } from "three-slicer/toggle";
|
|
6
6
|
function L({ css: t, className: s, children: r }) {
|
|
7
|
-
const i =
|
|
7
|
+
const i = _(null), [c] = h(() => {
|
|
8
8
|
const l = document.createElement("div");
|
|
9
9
|
return l.style.display = "contents", l;
|
|
10
10
|
});
|
|
11
|
-
return
|
|
11
|
+
return D(() => {
|
|
12
12
|
const l = i.current, o = l.shadowRoot || l.attachShadow({ mode: "open" });
|
|
13
13
|
let p = o.querySelector("style[data-ts-css]");
|
|
14
14
|
p || (p = document.createElement("style"), p.setAttribute("data-ts-css", ""), o.append(p)), p.textContent = t, o.contains(c) || o.append(c);
|
|
15
15
|
}, [t, c]), /* @__PURE__ */ e.createElement("div", { ref: i, className: s }, F(r, c));
|
|
16
16
|
}
|
|
17
|
-
const
|
|
17
|
+
const $ = ':host{display:contents}*{box-sizing:border-box}.row{display:grid;grid-template-columns:260px 220px 70px 1fr;gap:8px;align-items:center;padding:3px 0;border-bottom:1px dashed #f0f0f0}.row .lbl{color:#222;text-decoration:none}.row .lbl:hover{color:#0a7d4f;text-decoration:underline}.row .key{font-size:11px;color:#999;overflow:hidden;text-overflow:ellipsis}.row input[type=text],.row input:not([type]),.row select,.row textarea{width:100%;max-width:260px;padding:2px 6px}.inputwrap{display:inline-flex;align-items:center;gap:4px}.unit{color:#888;font-size:12px;white-space:nowrap}.pts{font-size:11px;color:#666}.badge{font-size:10px;padding:1px 6px;border-radius:8px;color:#fff;text-align:center}.mode-simple{background:#2e9e5b}.mode-advanced{background:#3673c3}.mode-expert{background:#d9822b}.mode-develop{background:#c0392b}.mode-none,.mode-undefined{background:#aaa}.muted{color:#999;font-weight:400;font-size:12px}.row.custom{grid-template-columns:1fr}.row.disabled{opacity:.42}.row.disabled input,.row.disabled select,.row.disabled textarea{cursor:not-allowed;background:#f2f2f2}.lbl-cell{display:inline-flex;align-items:center;gap:5px;min-width:0}.lbl-cell .lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dirty-dot{flex:0 0 auto;width:7px;height:7px;border-radius:50%;background:#e8873a;box-shadow:0 0 0 2px #fff3e6}.widget-cell{display:inline-flex;align-items:center;gap:6px;min-width:0}.reset-btn{flex:0 0 auto;border:1px solid #d8b48a;background:#fbf1e4;color:#b5701f;border-radius:5px;cursor:pointer;font-size:13px;line-height:1;padding:1px 5px}.reset-btn:hover{background:#f3e2cb}.settings-panel{flex:0 0 400px;width:400px;border-left:1px solid #e2e2e2;display:flex;flex-direction:column;min-height:0;background:#fff;color:#222;font:14px/1.5 -apple-system,Segoe UI,sans-serif}.settings-panel .sp-top{padding:10px 12px 8px;border-bottom:1px solid #eee}.sp-builder{display:flex;align-items:center;gap:8px;font-size:12px;color:#666}.sp-builder select{flex:1;padding:4px 6px}.sp-pages{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}.sp-pages button{border:1px solid #e0e0e0;background:#fafafa;border-radius:6px;padding:4px 8px;font-size:12px;cursor:pointer}.sp-pages button.on{background:#e6f5ee;color:#0a7d4f;border-color:#b7e2cd;font-weight:700}.sp-modes{display:flex;gap:4px;margin-top:8px}.sp-modes button{border:0;background:none;font-size:11px;color:#888;padding:2px 6px;border-radius:6px;cursor:pointer}.sp-modes button.on{background:#eef2f0;color:#0a7d4f;font-weight:700}.settings-panel .sp-body{flex:1 1 auto;overflow-y:auto;padding:8px 14px 24px}.settings-panel .sp-body h2{font-size:16px}.settings-panel .row{grid-template-columns:1fr auto;grid-template-areas:"lbl badge" "key widget";row-gap:2px;padding:6px 0}.settings-panel .row .lbl{grid-area:lbl;font-weight:600}.settings-panel .row .badge{grid-area:badge;justify-self:end}.settings-panel .row .key{grid-area:key}.settings-panel .row .inputwrap,.settings-panel .row>select,.settings-panel .row>input,.settings-panel .row>textarea,.settings-panel .row>.pts,.settings-panel .row>.muted{grid-area:widget;justify-self:end}.settings-panel .row input:not([type=checkbox]):not([type=color]),.settings-panel .row select,.settings-panel .row textarea{max-width:200px}.settings-panel .row.custom{grid-template-columns:1fr;grid-template-areas:"lbl"}.sp-search{width:100%;box-sizing:border-box;padding:6px 9px;border:1px solid #d6d6d6;border-radius:6px;font-size:13px;margin-bottom:8px}:host(.sp-embedded) .settings-panel{position:static;width:auto;flex:none;border:0;height:auto}:host(.sp-embedded) .sp-body{padding:4px 2px 8px;max-height:460px}.bse{display:flex;flex-direction:column;gap:6px;min-width:0;max-width:220px}.bse-modes{display:flex;gap:4px}.bse-modes button{border:1px solid #e0e0e0;background:#fafafa;border-radius:6px;padding:2px 8px;font-size:11px;cursor:pointer}.bse-modes button.on{background:#e6f5ee;color:#0a7d4f;border-color:#b7e2cd;font-weight:700}.bse-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px 8px}.bse-grid label{display:flex;align-items:center;gap:4px;font-size:11px;color:#666;white-space:nowrap}.bse-grid input{width:56px;padding:2px 4px;font-size:12px}.bse-json{width:100%;font:11px ui-monospace,monospace;padding:4px 6px;border:1px solid #d6d6d6;border-radius:6px;box-sizing:border-box}.bse-json.err{border-color:#d0435a;background:#fdf3f4}', j = (t, s, r, i) => [[r, i], [t + r, i], [t + r, s + i], [r, s + i]], T = (t) => {
|
|
18
18
|
const s = t / 2, r = 32;
|
|
19
19
|
return Array.from({ length: r }, (i, c) => {
|
|
20
20
|
const l = 2 * Math.PI * c / r;
|
|
21
21
|
return [+(s + s * Math.cos(l)).toFixed(2), +(s + s * Math.sin(l)).toFixed(2)];
|
|
22
22
|
});
|
|
23
|
-
},
|
|
23
|
+
}, O = (t) => {
|
|
24
24
|
const s = t.map((i) => i[0]), r = t.map((i) => i[1]);
|
|
25
25
|
return { x0: Math.min(...s), y0: Math.min(...r), w: Math.max(...s) - Math.min(...s), d: Math.max(...r) - Math.min(...r) };
|
|
26
26
|
};
|
|
27
27
|
function U({ value: t, onChange: s, disabled: r }) {
|
|
28
|
-
const i = Array.isArray(t) && t.length >= 3 ? t : j(200, 200, 0, 0), c =
|
|
28
|
+
const i = Array.isArray(t) && t.length >= 3 ? t : j(200, 200, 0, 0), c = O(i), [l, o] = h(i.length === 4 ? "rect" : i.length >= 12 ? "circle" : "custom"), [p, x] = h({ w: c.w, d: c.d, ox: c.x0, oy: c.y0 }), [w, u] = h(Math.round(c.w)), [d, f] = h(JSON.stringify(i)), [E, N] = h(!1);
|
|
29
29
|
P(() => {
|
|
30
|
-
const a =
|
|
30
|
+
const a = O(i);
|
|
31
31
|
x({ w: a.w, d: a.d, ox: a.x0, oy: a.y0 }), u(Math.round(a.w)), f(JSON.stringify(i)), N(!1);
|
|
32
32
|
}, [JSON.stringify(t)]);
|
|
33
33
|
const y = (a) => {
|
|
@@ -35,7 +35,7 @@ function U({ value: t, onChange: s, disabled: r }) {
|
|
|
35
35
|
const { w: m, d: n, ox: b, oy: v } = a;
|
|
36
36
|
m > 0 && n > 0 && s(j(m, n, b, v));
|
|
37
37
|
}, k = (a) => {
|
|
38
|
-
u(a), a > 0 && s(
|
|
38
|
+
u(a), a > 0 && s(T(a));
|
|
39
39
|
}, M = (a) => {
|
|
40
40
|
f(a);
|
|
41
41
|
try {
|
|
@@ -110,7 +110,7 @@ function U({ value: t, onChange: s, disabled: r }) {
|
|
|
110
110
|
}
|
|
111
111
|
const W = {
|
|
112
112
|
printable_area: U
|
|
113
|
-
},
|
|
113
|
+
}, R = Object.keys(S).filter((t) => S[t].length > 0), q = ["all", "simple", "advanced", "expert", "develop"], G = (t) => t.replace("::build", "").replace("Tab", "");
|
|
114
114
|
function H(t, s) {
|
|
115
115
|
var c;
|
|
116
116
|
if (!t || !(s in t)) return !1;
|
|
@@ -163,13 +163,13 @@ function Z({ optKey: t, settings: s, setSettings: r, disabled: i, onOptionOpen:
|
|
|
163
163
|
return /* @__PURE__ */ e.createElement("div", { className: "row" + (x ? " disabled" : ""), title: x ? `Disabled when: ${p}` : (o == null ? void 0 : o.tooltip) ?? "", "data-testid": `row-${t}` }, /* @__PURE__ */ e.createElement("span", { className: "lbl-cell" }, w && /* @__PURE__ */ e.createElement("span", { className: "dirty-dot", title: "Changed from default", "data-testid": `dirty-${t}` }), c ? /* @__PURE__ */ e.createElement("button", { type: "button", className: "lbl lbl-link", onClick: () => c(t), title: "Details" }, d) : /* @__PURE__ */ e.createElement("span", { className: "lbl" }, d)), /* @__PURE__ */ e.createElement("code", { className: "key" }, t), /* @__PURE__ */ e.createElement(Y, { mode: o == null ? void 0 : o.mode }), /* @__PURE__ */ e.createElement("span", { className: "widget-cell" }, /* @__PURE__ */ e.createElement(V, { def: o, optKey: t, settings: s, setSettings: r, disabled: x, customWidgets: l }), w && /* @__PURE__ */ e.createElement("button", { className: "reset-btn", onClick: u, title: "Reset to default", "data-testid": `reset-${t}` }, "↺")));
|
|
164
164
|
}
|
|
165
165
|
function re({ settings: t, setSettings: s, onOptionOpen: r, embedded: i = !1, customWidgets: c, only: l }) {
|
|
166
|
-
const [o, p] = h((l == null ? void 0 : l.builder) ??
|
|
166
|
+
const [o, p] = h((l == null ? void 0 : l.builder) ?? R[0] ?? ""), [x, w] = h(0), [u, d] = h("all"), [f, E] = h(""), N = S[(l == null ? void 0 : l.builder) ?? o] ?? [], y = l != null && l.page ? N.filter((n) => n.page === l.page) : N, k = y[Math.min(x, y.length - 1)], M = A(() => I(B(t)), [t]), g = f.trim().toLowerCase(), a = A(() => g ? Object.entries(C).filter(([n, b]) => {
|
|
167
167
|
var v, z;
|
|
168
168
|
return n.includes(g) || ((v = b.label) == null ? void 0 : v.toLowerCase().includes(g)) || ((z = b.full_label) == null ? void 0 : z.toLowerCase().includes(g));
|
|
169
169
|
}).slice(0, 60).map(([n]) => n) : [], [g]), m = (n, b) => /* @__PURE__ */ e.createElement(Z, { key: n + b, optKey: n, settings: t, setSettings: s, disabled: M, onOptionOpen: r, customWidgets: c });
|
|
170
|
-
return /* @__PURE__ */ e.createElement(L, { css:
|
|
170
|
+
return /* @__PURE__ */ e.createElement(L, { css: $, className: i ? "sp-embedded" : void 0 }, /* @__PURE__ */ e.createElement("div", { className: "settings-panel" }, !l && /* @__PURE__ */ e.createElement("div", { className: "sp-top" }, /* @__PURE__ */ e.createElement("input", { className: "sp-search", placeholder: `Search options (${Object.keys(C).length})…`, value: f, onChange: (n) => E(n.target.value), "data-testid": "opt-search" }), !g && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("label", { className: "sp-builder" }, /* @__PURE__ */ e.createElement("span", null, "Settings group"), /* @__PURE__ */ e.createElement("select", { value: o, onChange: (n) => {
|
|
171
171
|
p(n.target.value), w(0);
|
|
172
|
-
} },
|
|
172
|
+
} }, R.map((n) => /* @__PURE__ */ e.createElement("option", { key: n, value: n }, G(n))))), /* @__PURE__ */ e.createElement("div", { className: "sp-pages" }, y.map((n, b) => /* @__PURE__ */ e.createElement("button", { key: n.page + b, className: b === Math.min(x, y.length - 1) ? "on" : "", onClick: () => w(b) }, n.page))), /* @__PURE__ */ e.createElement("div", { className: "sp-modes" }, q.map((n) => /* @__PURE__ */ e.createElement("button", { key: n, className: u === n ? "on" : "", onClick: () => d(n) }, n))))), /* @__PURE__ */ e.createElement("div", { className: "sp-body" }, g ? /* @__PURE__ */ e.createElement("section", { "data-testid": "search-results" }, /* @__PURE__ */ e.createElement("h3", null, "Search: “", f, "” — ", a.length, " hit", a.length === 1 ? "" : "s", a.length === 60 ? "+" : ""), a.map(m), !a.length && /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No matches")) : k ? /* @__PURE__ */ e.createElement(e.Fragment, null, !l && /* @__PURE__ */ e.createElement("h2", null, k.page, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "Tab.cpp:", k.line)), k.groups.map((n) => {
|
|
173
173
|
const b = n.options.filter((v) => Q(v, u) && !(l && v.startsWith("<")));
|
|
174
174
|
return b.length ? /* @__PURE__ */ e.createElement("section", { key: n.group + n.line }, /* @__PURE__ */ e.createElement("h3", null, n.group || "(untitled group)", " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, ":", n.line)), b.map(m)) : null;
|
|
175
175
|
})) : /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No builder"))));
|
package/data/README.md
CHANGED
|
@@ -4,11 +4,48 @@ OrcaSlicer configuration metadata extracted from the original C++ sources, as pl
|
|
|
4
4
|
|
|
5
5
|
| File | Contents |
|
|
6
6
|
|---|---|
|
|
7
|
-
| `config-schema.json` |
|
|
7
|
+
| `config-schema.json` | 923 print options: type, label, category, tooltip, min/max, mode, enum values/labels, defaults, source line numbers |
|
|
8
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` (
|
|
9
|
+
| `ui-tree.json` | Tab → page → group → option tree from `Tab.cpp` (20 pages, 542 option references) |
|
|
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
|
+
| `printers.json` | 1,035 vendor machine profiles across 64 vendors: motion limits, bed and nozzle |
|
|
13
|
+
| `processes.js` | 2,243 print presets (speeds, accelerations), joined to printers by `compatible_printers` |
|
|
14
|
+
| `filaments.js` | 5,999 material presets over 81 filament types, plus each printer model's recommended list |
|
|
15
|
+
|
|
16
|
+
The last two are column-oriented and deduplicated, and load on demand. Read them through `processPresets()` /
|
|
17
|
+
`filamentPresets()` in `three-slicer/settings` rather than decoding the layout by hand.
|
|
18
|
+
|
|
19
|
+
## Material presets
|
|
20
|
+
|
|
21
|
+
`filaments.js` carries only the keys the kernel actually reads (14 today: temperatures, flow, diameter, cooling,
|
|
22
|
+
and the retraction/z-hop overrides a material is allowed to apply on top of the machine's). That column set is
|
|
23
|
+
disjoint from `processes.js`' 59, which is what lets a material pick and a process pick be applied independently —
|
|
24
|
+
neither clears the other's values.
|
|
25
|
+
|
|
26
|
+
`filamentPresets()` is the facade:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
import { filamentPresets } from 'three-slicer/settings'
|
|
30
|
+
|
|
31
|
+
const filaments = await filamentPresets()
|
|
32
|
+
filaments.listFor('Bambu Lab X1 Carbon 0.4 nozzle') // [{name, type, vendor}, …] compatible materials
|
|
33
|
+
filaments.recommendedFor('Bambu Lab X1 Carbon 0.4 nozzle') // the vendor's shortlist, filtered to the above
|
|
34
|
+
filaments.settingsFor('Bambu PLA Basic @BBL X1C') // {schemaKey: value} ready to merge
|
|
35
|
+
filaments.keys // clear these before applying a different material
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`recommendedFor()` intersects with the compatible list rather than returning the raw recommendation, and the
|
|
39
|
+
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,035 printer profiles, 1,749 of 4,259 raw
|
|
41
|
+
recommendation entries (41%) name a material whose own compatible list excludes that profile.
|
|
42
|
+
|
|
43
|
+
`type` and `vendor` are empty strings when the profile chain declares neither — 265 of the 5,999 presets. That is
|
|
44
|
+
why the type count above is 81 and not 82: the empty bucket is "no type declared", not a type.
|
|
45
|
+
|
|
46
|
+
Filament options are per-extruder vectors upstream (one entry per extruder), so a preset — which describes a single
|
|
47
|
+
material — always contributes the single first entry. Building a multi-material settings map means writing each
|
|
48
|
+
extruder's material at its own index in those vectors; see `deriveKernelParams` in `three-slicer/settings`.
|
|
12
49
|
|
|
13
50
|
```js
|
|
14
51
|
import schema from 'three-slicer/data/config-schema.json'
|
package/data/config-schema.json
CHANGED
|
@@ -12780,5 +12780,169 @@
|
|
|
12780
12780
|
2.5,
|
|
12781
12781
|
2.5
|
|
12782
12782
|
]
|
|
12783
|
+
},
|
|
12784
|
+
"filament_retraction_length": {
|
|
12785
|
+
"type": "coFloats",
|
|
12786
|
+
"label": "Length",
|
|
12787
|
+
"full_label": "Retraction Length",
|
|
12788
|
+
"tooltip": "Some amount of material in extruder is pulled back to avoid ooze during long travel. Set zero to disable retraction.",
|
|
12789
|
+
"sidetext": "mm",
|
|
12790
|
+
"mode": "simple",
|
|
12791
|
+
"nullable": true
|
|
12792
|
+
},
|
|
12793
|
+
"filament_z_hop": {
|
|
12794
|
+
"type": "coFloats",
|
|
12795
|
+
"label": "Z-hop height",
|
|
12796
|
+
"tooltip": "Whenever there is a retraction, the nozzle is lifted a little to create clearance between the nozzle and the print. This prevents the nozzle from hitting the print when traveling more. Using spiral lines to lift z can prevent stringing.",
|
|
12797
|
+
"sidetext": "mm",
|
|
12798
|
+
"min": 0,
|
|
12799
|
+
"max": 5,
|
|
12800
|
+
"mode": "simple",
|
|
12801
|
+
"nullable": true
|
|
12802
|
+
},
|
|
12803
|
+
"filament_z_hop_types": {
|
|
12804
|
+
"type": "coEnums",
|
|
12805
|
+
"label": "Z-hop type",
|
|
12806
|
+
"tooltip": "Type of Z-hop.",
|
|
12807
|
+
"enum_type": "ZHopType",
|
|
12808
|
+
"enum_values": [
|
|
12809
|
+
"Auto Lift",
|
|
12810
|
+
"Normal Lift",
|
|
12811
|
+
"Slope Lift",
|
|
12812
|
+
"Spiral Lift"
|
|
12813
|
+
],
|
|
12814
|
+
"enum_labels": [
|
|
12815
|
+
"Auto",
|
|
12816
|
+
"Normal",
|
|
12817
|
+
"Slope",
|
|
12818
|
+
"Spiral"
|
|
12819
|
+
],
|
|
12820
|
+
"mode": "advanced",
|
|
12821
|
+
"nullable": true
|
|
12822
|
+
},
|
|
12823
|
+
"filament_retract_lift_above": {
|
|
12824
|
+
"type": "coFloats",
|
|
12825
|
+
"label": "Only lift Z above",
|
|
12826
|
+
"tooltip": "If you set this to a positive value, Z lift will only take place above the specified absolute Z.",
|
|
12827
|
+
"sidetext": "mm",
|
|
12828
|
+
"mode": "advanced",
|
|
12829
|
+
"nullable": true
|
|
12830
|
+
},
|
|
12831
|
+
"filament_retract_lift_below": {
|
|
12832
|
+
"type": "coFloats",
|
|
12833
|
+
"label": "Only lift Z below",
|
|
12834
|
+
"tooltip": "If you set this to a positive value, Z lift will only take place below the specified absolute Z.",
|
|
12835
|
+
"sidetext": "mm",
|
|
12836
|
+
"mode": "advanced",
|
|
12837
|
+
"nullable": true
|
|
12838
|
+
},
|
|
12839
|
+
"filament_retract_lift_enforce": {
|
|
12840
|
+
"type": "coEnums",
|
|
12841
|
+
"label": "On surfaces",
|
|
12842
|
+
"tooltip": "Enforce Z-Hop behavior. This setting is impacted by the above settings (Only lift Z above/below).",
|
|
12843
|
+
"enum_type": "RetractLiftEnforceType",
|
|
12844
|
+
"enum_values": [
|
|
12845
|
+
"All Surfaces",
|
|
12846
|
+
"Top Only",
|
|
12847
|
+
"Bottom Only",
|
|
12848
|
+
"Top and Bottom"
|
|
12849
|
+
],
|
|
12850
|
+
"enum_labels": [
|
|
12851
|
+
"All Surfaces",
|
|
12852
|
+
"Top Only",
|
|
12853
|
+
"Bottom Only",
|
|
12854
|
+
"Top and Bottom"
|
|
12855
|
+
],
|
|
12856
|
+
"mode": "advanced",
|
|
12857
|
+
"nullable": true
|
|
12858
|
+
},
|
|
12859
|
+
"filament_retraction_speed": {
|
|
12860
|
+
"type": "coFloats",
|
|
12861
|
+
"label": "Retraction speed",
|
|
12862
|
+
"full_label": "Retraction speed",
|
|
12863
|
+
"tooltip": "Speed for retracting filament from the nozzle.",
|
|
12864
|
+
"sidetext": "mm/s",
|
|
12865
|
+
"mode": "advanced",
|
|
12866
|
+
"nullable": true
|
|
12867
|
+
},
|
|
12868
|
+
"filament_deretraction_speed": {
|
|
12869
|
+
"type": "coFloats",
|
|
12870
|
+
"label": "Deretraction speed",
|
|
12871
|
+
"full_label": "Deretraction speed",
|
|
12872
|
+
"tooltip": "Speed for reloading filament into the nozzle. Zero means same speed of retraction.",
|
|
12873
|
+
"sidetext": "mm/s",
|
|
12874
|
+
"mode": "advanced",
|
|
12875
|
+
"nullable": true
|
|
12876
|
+
},
|
|
12877
|
+
"filament_retract_restart_extra": {
|
|
12878
|
+
"type": "coFloats",
|
|
12879
|
+
"label": "Extra length on restart",
|
|
12880
|
+
"tooltip": "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed.",
|
|
12881
|
+
"sidetext": "mm",
|
|
12882
|
+
"mode": "advanced",
|
|
12883
|
+
"nullable": true
|
|
12884
|
+
},
|
|
12885
|
+
"filament_retraction_minimum_travel": {
|
|
12886
|
+
"type": "coFloats",
|
|
12887
|
+
"label": "Travel distance threshold",
|
|
12888
|
+
"tooltip": "Only trigger retraction when the travel distance is longer than this threshold.",
|
|
12889
|
+
"sidetext": "mm",
|
|
12890
|
+
"mode": "advanced",
|
|
12891
|
+
"nullable": true
|
|
12892
|
+
},
|
|
12893
|
+
"filament_wipe_distance": {
|
|
12894
|
+
"type": "coFloats",
|
|
12895
|
+
"label": "Wipe distance",
|
|
12896
|
+
"tooltip": "Describe how long the nozzle will move along the last path when retracting.\n\nDepending on how long the wipe operation lasts, how fast and long the extruder/filament retraction settings are, a retraction move may be needed to retract the remaining filament.\n\nSetting a value in the retract amount before wipe setting below will perform any excess retraction before the wipe, else it will be performed after.",
|
|
12897
|
+
"sidetext": "mm",
|
|
12898
|
+
"min": 0,
|
|
12899
|
+
"mode": "advanced",
|
|
12900
|
+
"nullable": true
|
|
12901
|
+
},
|
|
12902
|
+
"filament_retract_when_changing_layer": {
|
|
12903
|
+
"type": "coBools",
|
|
12904
|
+
"label": "Retract on layer change",
|
|
12905
|
+
"tooltip": "This forces a retraction on layer changes.",
|
|
12906
|
+
"mode": "advanced",
|
|
12907
|
+
"nullable": true
|
|
12908
|
+
},
|
|
12909
|
+
"filament_wipe": {
|
|
12910
|
+
"type": "coBools",
|
|
12911
|
+
"label": "Wipe while retracting",
|
|
12912
|
+
"tooltip": "This moves the nozzle along the last extrusion path when retracting to clean any leaked material on the nozzle. This can minimize blobs when printing a new part after traveling.",
|
|
12913
|
+
"mode": "advanced",
|
|
12914
|
+
"nullable": true
|
|
12915
|
+
},
|
|
12916
|
+
"filament_retract_before_wipe": {
|
|
12917
|
+
"type": "coPercents",
|
|
12918
|
+
"label": "Retract amount before wipe",
|
|
12919
|
+
"tooltip": "This is the length of fast retraction before a wipe, relative to retraction length.",
|
|
12920
|
+
"sidetext": "%",
|
|
12921
|
+
"mode": "advanced",
|
|
12922
|
+
"nullable": true
|
|
12923
|
+
},
|
|
12924
|
+
"filament_retract_after_wipe": {
|
|
12925
|
+
"type": "coPercents",
|
|
12926
|
+
"label": "Retract amount after wipe",
|
|
12927
|
+
"tooltip": "The length of fast retraction after wipe, relative to retraction length.\nThe value will be clamped by 100% minus the retract amount before the wipe value.",
|
|
12928
|
+
"sidetext": "%",
|
|
12929
|
+
"mode": "advanced",
|
|
12930
|
+
"nullable": true
|
|
12931
|
+
},
|
|
12932
|
+
"filament_long_retractions_when_cut": {
|
|
12933
|
+
"type": "coBools",
|
|
12934
|
+
"label": "Long retraction when cut (beta)",
|
|
12935
|
+
"tooltip": "Experimental feature: Retracting and cutting off the filament at a longer distance during changes to minimize purge. While this reduces flush significantly, it may also raise the risk of nozzle clogs or other printing problems.",
|
|
12936
|
+
"mode": "simple",
|
|
12937
|
+
"nullable": true
|
|
12938
|
+
},
|
|
12939
|
+
"filament_retraction_distances_when_cut": {
|
|
12940
|
+
"type": "coFloats",
|
|
12941
|
+
"label": "Retraction distance when cut",
|
|
12942
|
+
"tooltip": "Experimental feature: Retraction length before cutting off during filament change.",
|
|
12943
|
+
"min": 10,
|
|
12944
|
+
"max": 18,
|
|
12945
|
+
"mode": "simple",
|
|
12946
|
+
"nullable": true
|
|
12783
12947
|
}
|
|
12784
12948
|
}
|