three-slicer-viewer 0.3.2 → 0.3.3
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/VIEWER.md +24 -0
- package/dist/components.js +47 -43
- package/dist/gcode_parse.js +99 -95
- package/dist/index.js +3855 -3758
- package/dist/{model_loaders-DxlFNFOu.js → model_loaders-Dk9rqXFb.js} +27 -27
- package/dist/model_loaders.js +2 -2
- package/dist/parse_3mf-CpbA-6Iq.js +666 -0
- package/dist/parse_3mf.worker.js +67 -5
- package/dist/toolpath.js +248 -176
- package/package.json +6 -1
- package/src/core/paint_tools.js +8 -0
- package/src/settings/kernel_setting_keys.js +28 -0
- package/src/settings/settings_core.js +55 -5
- package/types/paint.d.ts +7 -0
- package/types/viewer.d.ts +1 -1
- package/dist/parse_3mf-BZISF3eW.js +0 -327
package/VIEWER.md
CHANGED
|
@@ -256,6 +256,7 @@ opt-out only, everything on unless you say `false`, an unknown key stays on.
|
|
|
256
256
|
| `filePicker` | The file dialog never opens, from any of the buttons that open it. |
|
|
257
257
|
| `contextMenu` | No right-click menu. The browser's own menu stays suppressed over the canvas either way — OrbitControls preventDefaults `contextmenu` itself. |
|
|
258
258
|
| `logs` | No console output, from the component or from the slice worker. |
|
|
259
|
+
| `versionBadge` | The build number at the right end of the status bar is not drawn. It is there so a bug report can name a build; turn it off when the viewer is embedded as part of your own product. `panels.status: false` hides it too, since it rides that bar. |
|
|
259
260
|
|
|
260
261
|
### Read-only panels
|
|
261
262
|
|
|
@@ -395,6 +396,29 @@ for 143MB) against a 4GB pool budget, which gives 2 for that model and does not
|
|
|
395
396
|
manual count is capped the same way. What the browser has NOT measured yet is
|
|
396
397
|
the arachne + tree-support preset, where the harness gains were largest.
|
|
397
398
|
|
|
399
|
+
### The contour union on the GPU
|
|
400
|
+
|
|
401
|
+
The slice bar's **GPU** select (`gpu_acceleration` in the settings map: `auto`, `on`, `off`; absent is `auto`; a viewer
|
|
402
|
+
knob like `slice_workers`, not a kernel parameter) lets the slicer worker compute every layer's contour union with
|
|
403
|
+
WebGPU instead of Clipper. `auto` asks for it only when the single-threaded kernel loaded, `on` on either kernel,
|
|
404
|
+
`off` never. It applies to the selected plate's filament slice; pool workers, multi-material and resin slices compute
|
|
405
|
+
their contours themselves.
|
|
406
|
+
|
|
407
|
+
Measured in node (Dawn, Metal, 15 cores) with the kernel consuming the GPU's contours:
|
|
408
|
+
|
|
409
|
+
| model | kernel | CPU | GPU |
|
|
410
|
+
|---|---|---|---|
|
|
411
|
+
| 3M-facet scan, tree slim support, 420 layers | st | 9.6-9.8 s | 7.6-7.7 s |
|
|
412
|
+
| 1.13M-facet plate, tree hybrid support, 849 layers | st | 105 s | 102-104 s |
|
|
413
|
+
| either | mt | | within run-to-run spread of the CPU slice |
|
|
414
|
+
|
|
415
|
+
The union is about 2.5 s of the single-threaded slice and 50-100 ms on the GPU; on the threaded kernel the same union is
|
|
416
|
+
already spread over the threads, which is why `auto` leaves it alone. What it costs: the GPU's contours have the CPU
|
|
417
|
+
union's area (within 2e-10 relative) and other vertices, so the G-code is not the CPU slice's byte for byte (filament
|
|
418
|
+
within 0.3 % in those two models). The slice never depends on the GPU: without a device, or when the GPU refuses an
|
|
419
|
+
input, the kernel slices by itself, a layer whose loops the GPU leaves open gets the kernel's own union, and the stats
|
|
420
|
+
card says which happened (`Contours on the GPU`, with the count of layers that went back to the CPU).
|
|
421
|
+
|
|
398
422
|
## Theming
|
|
399
423
|
|
|
400
424
|
Both shadow roots (the viewer and `<SettingsPanel/>`) paint their chrome from one palette, `UI_TOKENS` in
|
package/dist/components.js
CHANGED
|
@@ -8,39 +8,39 @@ import { S as X } from "./shadow_host-CQ3LirzD.js";
|
|
|
8
8
|
const Y = ':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 var(--_light-divider)}.row .lbl{color:var(--_light-text);text-decoration:none}.row .lbl:hover{color:var(--_accent-ink);text-decoration:underline}.row .key{font-size:11px;color:var(--_light-text-faint);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:var(--_light-text-faint);font-size:12px;white-space:nowrap}.pts{font-size:11px;color:var(--_light-text-muted)}.badge{font-size:10px;padding:1px 6px;border-radius:8px;color:var(--_on-accent);text-align:center}.mode-simple{background:var(--_accent-ink)}.mode-advanced{background:var(--_info-ink)}.mode-expert{background:var(--_warn)}.mode-develop{background:var(--_danger)}.mode-none,.mode-undefined{background:var(--_light-text-faint)}.muted{color:var(--_light-text-faint);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:var(--_light-subtle)}.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:var(--_warn);box-shadow:0 0 0 2px var(--_warn-soft)}.override-dot{flex:0 0 auto;width:9px;height:9px;border-radius:50%;background:var(--_warn);border:0;padding:0;cursor:pointer;box-shadow:0 0 0 2px var(--_warn-soft)}.override-dot:hover{background:var(--_warn-ink)}.widget-cell{display:inline-flex;align-items:center;gap:6px;min-width:0}.reset-btn{flex:0 0 auto;border:1px solid var(--_warn-border);background:var(--_warn-soft);color:var(--_warn-ink);border-radius:5px;cursor:pointer;font-size:13px;line-height:1;padding:1px 5px}.reset-btn:hover{background:var(--_warn-soft-hover)}.settings-panel{flex:0 0 400px;width:400px;border-left:1px solid var(--_light-divider);display:flex;flex-direction:column;min-height:0;background:var(--_light-surface);color:var(--_light-text);font:14px/1.5 -apple-system,Segoe UI,sans-serif}.settings-panel .sp-top{padding:10px 12px 8px;border-bottom:1px solid var(--_light-divider)}.sp-builder{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--_light-text-muted)}.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 var(--_light-border);background:var(--_light-subtle);border-radius:6px;padding:4px 8px;font-size:12px;cursor:pointer}.sp-pages button.on{background:var(--_accent-soft);color:var(--_accent-ink);border-color:var(--_accent-soft-border);font-weight:700}.sp-modes{display:flex;gap:4px;margin-top:8px}.sp-modes button{border:0;background:none;font-size:11px;color:var(--_light-text-faint);padding:2px 6px;border-radius:6px;cursor:pointer}.sp-modes button.on{background:var(--_accent-soft);color:var(--_accent-ink);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 var(--_light-border);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 var(--_light-border);background:var(--_light-subtle);border-radius:6px;padding:2px 8px;font-size:11px;cursor:pointer}.bse-modes button.on{background:var(--_accent-soft);color:var(--_accent-ink);border-color:var(--_accent-soft-border);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:var(--_light-text-muted);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 var(--_light-border);border-radius:6px;box-sizing:border-box}.bse-json.err{border-color:var(--_danger);background:var(--_danger-soft)}', q = (t, a, r, o) => [[r, o], [t + r, o], [t + r, a + o], [r, a + o]], L = R.printable_area.default, V = (t) => {
|
|
9
9
|
const a = t / 2, r = 32;
|
|
10
10
|
return Array.from({ length: r }, (o, i) => {
|
|
11
|
-
const
|
|
12
|
-
return [+(a + a * Math.cos(
|
|
11
|
+
const m = 2 * Math.PI * i / r;
|
|
12
|
+
return [+(a + a * Math.cos(m)).toFixed(2), +(a + a * Math.sin(m)).toFixed(2)];
|
|
13
13
|
});
|
|
14
14
|
}, T = (t) => {
|
|
15
15
|
const a = t.map((o) => o[0]), r = t.map((o) => o[1]);
|
|
16
16
|
return { x0: Math.min(...a), y0: Math.min(...r), w: Math.max(...a) - Math.min(...a), d: Math.max(...r) - Math.min(...r) };
|
|
17
17
|
};
|
|
18
18
|
function Z({ value: t, onChange: a, disabled: r }) {
|
|
19
|
-
const o = Array.isArray(t) && t.length >= 3 ? t : L, i = T(o), [
|
|
19
|
+
const o = Array.isArray(t) && t.length >= 3 ? t : L, i = T(o), [m, d] = y(o.length === 4 ? "rect" : o.length >= 12 ? "circle" : "custom"), [c, p] = y({ w: i.w, d: i.d, ox: i.x0, oy: i.y0 }), [x, b] = y(Math.round(i.w)), [l, u] = y(JSON.stringify(o)), [_, w] = y(!1);
|
|
20
20
|
P(() => {
|
|
21
21
|
const n = T(o);
|
|
22
|
-
p({ w: n.w, d: n.d, ox: n.x0, oy: n.y0 }),
|
|
22
|
+
p({ w: n.w, d: n.d, ox: n.x0, oy: n.y0 }), b(Math.round(n.w)), u(JSON.stringify(o)), w(!1);
|
|
23
23
|
}, [JSON.stringify(t)]);
|
|
24
24
|
const v = (n) => {
|
|
25
25
|
p(n);
|
|
26
|
-
const { w:
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
|
|
26
|
+
const { w: g, d: f, ox: S, oy: M } = n;
|
|
27
|
+
g > 0 && f > 0 && a(q(g, f, S, M));
|
|
28
|
+
}, N = (n) => {
|
|
29
|
+
b(n), n > 0 && a(V(n));
|
|
30
30
|
}, z = (n) => {
|
|
31
|
-
|
|
31
|
+
u(n);
|
|
32
32
|
try {
|
|
33
|
-
const
|
|
34
|
-
if (!Array.isArray(
|
|
35
|
-
w(!1), a(
|
|
33
|
+
const g = JSON.parse(n);
|
|
34
|
+
if (!Array.isArray(g) || g.length < 3 || !g.every((f) => Array.isArray(f) && f.length === 2 && f.every(Number.isFinite))) throw 0;
|
|
35
|
+
w(!1), a(g);
|
|
36
36
|
} catch {
|
|
37
37
|
w(!0);
|
|
38
38
|
}
|
|
39
39
|
}, E = (n) => {
|
|
40
|
-
const
|
|
41
|
-
return Number.isFinite(
|
|
40
|
+
const g = parseFloat(n);
|
|
41
|
+
return Number.isFinite(g) ? g : 0;
|
|
42
42
|
};
|
|
43
|
-
return /* @__PURE__ */ e.createElement("div", { className: "bse", "data-testid": "bed-shape-editor" }, /* @__PURE__ */ e.createElement("div", { className: "bse-modes" }, [["rect", "Rectangular"], ["circle", "Circular"], ["custom", "Custom"]].map(([n,
|
|
43
|
+
return /* @__PURE__ */ e.createElement("div", { className: "bse", "data-testid": "bed-shape-editor" }, /* @__PURE__ */ e.createElement("div", { className: "bse-modes" }, [["rect", "Rectangular"], ["circle", "Circular"], ["custom", "Custom"]].map(([n, g]) => /* @__PURE__ */ e.createElement("button", { key: n, type: "button", className: m === n ? "on" : "", disabled: r, onClick: () => d(n) }, g))), m === "rect" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Width ", /* @__PURE__ */ e.createElement(
|
|
44
44
|
"input",
|
|
45
45
|
{
|
|
46
46
|
type: "number",
|
|
@@ -76,7 +76,7 @@ function Z({ value: t, onChange: a, disabled: r }) {
|
|
|
76
76
|
disabled: r,
|
|
77
77
|
onChange: (n) => v({ ...c, oy: E(n.target.value) })
|
|
78
78
|
}
|
|
79
|
-
))),
|
|
79
|
+
))), m === "circle" && /* @__PURE__ */ e.createElement("div", { className: "bse-grid" }, /* @__PURE__ */ e.createElement("label", null, "Diameter ", /* @__PURE__ */ e.createElement(
|
|
80
80
|
"input",
|
|
81
81
|
{
|
|
82
82
|
type: "number",
|
|
@@ -84,12 +84,12 @@ function Z({ value: t, onChange: a, disabled: r }) {
|
|
|
84
84
|
value: x,
|
|
85
85
|
disabled: r,
|
|
86
86
|
"data-testid": "bed-dia",
|
|
87
|
-
onChange: (n) =>
|
|
87
|
+
onChange: (n) => N(E(n.target.value))
|
|
88
88
|
}
|
|
89
|
-
), " mm")),
|
|
89
|
+
), " mm")), m === "custom" && /* @__PURE__ */ e.createElement(
|
|
90
90
|
"textarea",
|
|
91
91
|
{
|
|
92
|
-
className: "bse-json" + (
|
|
92
|
+
className: "bse-json" + (_ ? " err" : ""),
|
|
93
93
|
rows: 3,
|
|
94
94
|
value: l,
|
|
95
95
|
disabled: r,
|
|
@@ -118,35 +118,39 @@ function le(t) {
|
|
|
118
118
|
function ie({ mode: t }) {
|
|
119
119
|
return /* @__PURE__ */ e.createElement("span", { className: `badge mode-${t ?? "none"}` }, t ?? "—");
|
|
120
120
|
}
|
|
121
|
-
function ce({ def: t, optKey: a, settings: r, setSettings: o, disabled: i, customWidgets:
|
|
122
|
-
const d = I(r, a), c = (l) => o((
|
|
121
|
+
function ce({ def: t, optKey: a, settings: r, setSettings: o, disabled: i, customWidgets: m }) {
|
|
122
|
+
const d = I(r, a), c = (l) => o((u) => ({ ...u, [a]: l })), p = m?.[a] ?? K[a];
|
|
123
123
|
if (p) return /* @__PURE__ */ e.createElement(p, { def: t, value: d, onChange: c, disabled: i });
|
|
124
|
-
const x = le(t),
|
|
124
|
+
const x = le(t), b = Array.isArray(d) ? d[0] : d;
|
|
125
125
|
switch (x) {
|
|
126
126
|
case "select":
|
|
127
|
-
return /* @__PURE__ */ e.createElement("select", { value:
|
|
127
|
+
return /* @__PURE__ */ e.createElement("select", { value: b ?? "", onChange: (l) => c(l.target.value), disabled: i }, t.enum_values.map((l, u) => /* @__PURE__ */ e.createElement("option", { key: l, value: l }, t.enum_labels?.[u] ?? l)));
|
|
128
128
|
case "checkbox":
|
|
129
129
|
return /* @__PURE__ */ e.createElement("input", { type: "checkbox", checked: Array.isArray(d) ? !!d[0] : !!d, onChange: (l) => c(l.target.checked), disabled: i });
|
|
130
130
|
case "color":
|
|
131
|
-
return /* @__PURE__ */ e.createElement("input", { type: "color", value: typeof
|
|
132
|
-
case "textarea":
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
return /* @__PURE__ */ e.createElement("input", { type: "color", value: typeof b == "string" && b.startsWith("#") ? b : H, onChange: (l) => c(l.target.value), disabled: i });
|
|
132
|
+
case "textarea": {
|
|
133
|
+
let l = 2;
|
|
134
|
+
t.full_width && (l = 12);
|
|
135
|
+
let u = d ?? "";
|
|
136
|
+
return Array.isArray(d) && (u = d.join(`
|
|
137
|
+
`)), /* @__PURE__ */ e.createElement("textarea", { rows: l, value: u, onChange: (_) => c(_.target.value), disabled: i });
|
|
138
|
+
}
|
|
135
139
|
case "points":
|
|
136
140
|
return /* @__PURE__ */ e.createElement("code", { className: "pts" }, JSON.stringify(d ?? []));
|
|
137
141
|
case "unknown":
|
|
138
142
|
return /* @__PURE__ */ e.createElement("span", { className: "muted" }, "No schema");
|
|
139
143
|
default:
|
|
140
|
-
return /* @__PURE__ */ e.createElement("span", { className: "inputwrap" }, /* @__PURE__ */ e.createElement("input", { value:
|
|
144
|
+
return /* @__PURE__ */ e.createElement("span", { className: "inputwrap" }, /* @__PURE__ */ e.createElement("input", { value: b ?? "", onChange: (l) => c(l.target.value), disabled: i }), t.sidetext && /* @__PURE__ */ e.createElement("span", { className: "unit" }, t.sidetext));
|
|
141
145
|
}
|
|
142
146
|
}
|
|
143
|
-
function de({ schema: t, optKey: a, settings: r, setSettings: o, disabled: i, onOptionOpen:
|
|
147
|
+
function de({ schema: t, optKey: a, settings: r, setSettings: o, disabled: i, onOptionOpen: m, customWidgets: d, overridden: c, onRevertKey: p }) {
|
|
144
148
|
if (a.startsWith("<")) return /* @__PURE__ */ e.createElement("div", { className: "row custom" }, /* @__PURE__ */ e.createElement("span", { className: "muted" }, "⚙ Custom widget ", a));
|
|
145
|
-
const x = t[a],
|
|
146
|
-
const
|
|
147
|
-
return delete
|
|
149
|
+
const x = t[a], b = i ? i[a] : void 0, l = b != null, u = oe(t, r, a), _ = () => o((v) => {
|
|
150
|
+
const N = { ...v };
|
|
151
|
+
return delete N[a], N;
|
|
148
152
|
}), w = x?.label || x?.full_label || a;
|
|
149
|
-
return /* @__PURE__ */ e.createElement("div", { className: "row" + (l ? " disabled" : ""), title: l ? `Disabled when: ${
|
|
153
|
+
return /* @__PURE__ */ e.createElement("div", { className: "row" + (l ? " disabled" : ""), title: l ? `Disabled when: ${b}` : x?.tooltip ?? "", "data-testid": `row-${a}` }, /* @__PURE__ */ e.createElement("span", { className: "lbl-cell" }, c && /* @__PURE__ */ e.createElement(
|
|
150
154
|
"button",
|
|
151
155
|
{
|
|
152
156
|
type: "button",
|
|
@@ -155,7 +159,7 @@ function de({ schema: t, optKey: a, settings: r, setSettings: o, disabled: i, on
|
|
|
155
159
|
title: "This plate's override — click to follow the global value again",
|
|
156
160
|
"data-testid": `override-${a}`
|
|
157
161
|
}
|
|
158
|
-
),
|
|
162
|
+
), u && /* @__PURE__ */ e.createElement("span", { className: "dirty-dot", title: "Changed from default", "data-testid": `dirty-${a}` }), m ? /* @__PURE__ */ e.createElement("button", { type: "button", className: "lbl lbl-link", onClick: () => m(a), title: "Details" }, w) : /* @__PURE__ */ e.createElement("span", { className: "lbl" }, w)), /* @__PURE__ */ e.createElement("code", { className: "key" }, a), /* @__PURE__ */ e.createElement(ie, { mode: x?.mode }), /* @__PURE__ */ e.createElement("span", { className: "widget-cell" }, /* @__PURE__ */ e.createElement(ce, { def: x, optKey: a, settings: r, setSettings: o, disabled: l, customWidgets: d }), u && /* @__PURE__ */ e.createElement("button", { className: "reset-btn", onClick: _, title: "Reset to default", "data-testid": `reset-${a}` }, "↺")));
|
|
159
163
|
}
|
|
160
164
|
function fe({
|
|
161
165
|
schema: t = R,
|
|
@@ -163,14 +167,14 @@ function fe({
|
|
|
163
167
|
toggle: r = re,
|
|
164
168
|
settings: o,
|
|
165
169
|
setSettings: i,
|
|
166
|
-
onOptionOpen:
|
|
170
|
+
onOptionOpen: m,
|
|
167
171
|
embedded: d = !1,
|
|
168
172
|
customWidgets: c,
|
|
169
173
|
only: p,
|
|
170
174
|
overriddenKeys: x,
|
|
171
|
-
onRevertKey:
|
|
175
|
+
onRevertKey: b
|
|
172
176
|
}) {
|
|
173
|
-
const l = j(() => ne(a), [a]), [
|
|
177
|
+
const l = j(() => ne(a), [a]), [u, _] = y(p?.builder ?? l[0] ?? ""), [w, v] = y(0), [N, z] = y("all"), [E, n] = y(""), g = W(o), f = l.filter((s) => te(s) === g), S = p?.builder ?? (f.includes(u) ? u : f[0] ?? ""), M = a[p?.builder ?? S] ?? [], A = p?.page ? M.filter((s) => s.page === p.page) : M, O = A[Math.min(w, A.length - 1)], J = j(() => r.disabledKeys(r.makeCfg(o)), [o, r]), k = E.trim().toLowerCase(), C = j(() => k ? Object.entries(t).filter(([s, h]) => s.includes(k) || h.label?.toLowerCase().includes(k) || h.full_label?.toLowerCase().includes(k)).slice(0, 60).map(([s]) => s) : [], [k]), D = (s, h) => /* @__PURE__ */ e.createElement(
|
|
174
178
|
de,
|
|
175
179
|
{
|
|
176
180
|
schema: t,
|
|
@@ -179,16 +183,16 @@ function fe({
|
|
|
179
183
|
settings: o,
|
|
180
184
|
setSettings: i,
|
|
181
185
|
disabled: J,
|
|
182
|
-
onOptionOpen:
|
|
186
|
+
onOptionOpen: m,
|
|
183
187
|
customWidgets: c,
|
|
184
188
|
overridden: x?.includes(s),
|
|
185
|
-
onRevertKey:
|
|
189
|
+
onRevertKey: b
|
|
186
190
|
}
|
|
187
191
|
);
|
|
188
|
-
return /* @__PURE__ */ e.createElement(X, { css: Q + Y, className: d ? "sp-embedded" : void 0 }, /* @__PURE__ */ e.createElement("div", { className: "settings-panel" }, !p && /* @__PURE__ */ e.createElement("div", { className: "sp-top" }, /* @__PURE__ */ e.createElement("input", { className: "sp-search", placeholder: `Search options (${Object.keys(t).length})…`, value: E, onChange: (s) => n(s.target.value), "data-testid": "opt-search" }), !
|
|
189
|
-
|
|
190
|
-
} }, f.map((s) => /* @__PURE__ */ e.createElement("option", { key: s, value: s }, B(s))))), /* @__PURE__ */ e.createElement("div", { className: "sp-pages" }, A.map((s, h) => /* @__PURE__ */ e.createElement("button", { key: s.page + h, className: h === Math.min(w, A.length - 1) ? "on" : "", onClick: () => v(h) }, s.page))), /* @__PURE__ */ e.createElement("div", { className: "sp-modes" }, ee.map((s) => /* @__PURE__ */ e.createElement("button", { key: s, className:
|
|
191
|
-
const h = s.options.filter((F) => se(t, F,
|
|
192
|
+
return /* @__PURE__ */ e.createElement(X, { css: Q + Y, className: d ? "sp-embedded" : void 0 }, /* @__PURE__ */ e.createElement("div", { className: "settings-panel" }, !p && /* @__PURE__ */ e.createElement("div", { className: "sp-top" }, /* @__PURE__ */ e.createElement("input", { className: "sp-search", placeholder: `Search options (${Object.keys(t).length})…`, value: E, onChange: (s) => n(s.target.value), "data-testid": "opt-search" }), !k && /* @__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: S, onChange: (s) => {
|
|
193
|
+
_(s.target.value), v(0);
|
|
194
|
+
} }, f.map((s) => /* @__PURE__ */ e.createElement("option", { key: s, value: s }, B(s))))), /* @__PURE__ */ e.createElement("div", { className: "sp-pages" }, A.map((s, h) => /* @__PURE__ */ e.createElement("button", { key: s.page + h, className: h === Math.min(w, A.length - 1) ? "on" : "", onClick: () => v(h) }, s.page))), /* @__PURE__ */ e.createElement("div", { className: "sp-modes" }, ee.map((s) => /* @__PURE__ */ e.createElement("button", { key: s, className: N === s ? "on" : "", onClick: () => z(s) }, s))))), /* @__PURE__ */ e.createElement("div", { className: "sp-body" }, k ? /* @__PURE__ */ e.createElement("section", { "data-testid": "search-results" }, /* @__PURE__ */ e.createElement("h3", null, "Search: “", E, "” — ", C.length, " hit", C.length === 1 ? "" : "s", C.length === 60 ? "+" : ""), C.map(D), !C.length && /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No matches")) : O ? /* @__PURE__ */ e.createElement(e.Fragment, null, !p && /* @__PURE__ */ e.createElement("h2", null, O.page, " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, "Tab.cpp:", O.line)), O.groups.map((s) => {
|
|
195
|
+
const h = s.options.filter((F) => se(t, F, N) && !(p && F.startsWith("<")));
|
|
192
196
|
return h.length ? /* @__PURE__ */ e.createElement("section", { key: s.group + s.line }, /* @__PURE__ */ e.createElement("h3", null, s.group || "(untitled group)", " ", /* @__PURE__ */ e.createElement("span", { className: "muted" }, ":", s.line)), h.map(D)) : null;
|
|
193
197
|
})) : /* @__PURE__ */ e.createElement("p", { className: "muted" }, "No builder"))));
|
|
194
198
|
}
|
package/dist/gcode_parse.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { D as
|
|
1
|
+
import { D as Nt, a as Rt } from "./viewer_defaults-B7UUZ582.js";
|
|
2
2
|
import { R as t, e as Z } from "./toolpath_encoding-BuYs95qZ.js";
|
|
3
3
|
import { T as ft } from "./toolpath_palette-B8xEl-qa.js";
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
return
|
|
7
|
-
}),
|
|
8
|
-
function gt(
|
|
9
|
-
let
|
|
10
|
-
Array.isArray(
|
|
11
|
-
const
|
|
12
|
-
return Array.from({ length: u }, (
|
|
4
|
+
const K = 1e-6, It = 254, At = /^(filament|extruder)_colour\s*=\s*(.*)$/i, ut = (n) => n.split(/[;,]/).map((r) => {
|
|
5
|
+
const o = /^"?#?([0-9a-f]{6})(?:[0-9a-f]{2})?"?$/i.exec(r.trim());
|
|
6
|
+
return o ? "#" + o[1].toUpperCase() : null;
|
|
7
|
+
}), dt = (n) => "#" + ft[n % ft.length].map((r) => Math.round(r * 255).toString(16).padStart(2, "0")).join("");
|
|
8
|
+
function gt(n, r) {
|
|
9
|
+
let o = [];
|
|
10
|
+
Array.isArray(n?.colors) && (o = n.colors);
|
|
11
|
+
const d = r ?? [], u = Math.max(o.length, d.length, n?.filament_mm_by_tool?.length ?? 0);
|
|
12
|
+
return Array.from({ length: u }, (h, c) => o[c] || d[c] || dt(c));
|
|
13
13
|
}
|
|
14
|
-
function wt(
|
|
15
|
-
return
|
|
14
|
+
function wt(n, r) {
|
|
15
|
+
return St(n.gcode, gt(n.stats, r));
|
|
16
16
|
}
|
|
17
|
-
function
|
|
18
|
-
const
|
|
19
|
-
if (!
|
|
20
|
-
const u = "; filament_colour = " +
|
|
21
|
-
`,
|
|
17
|
+
function St(n, r) {
|
|
18
|
+
const o = String(n ?? ""), d = [r].flat().filter(Boolean);
|
|
19
|
+
if (!d.length || /^;\s*filament_colour\s*=/mi.test(o)) return o;
|
|
20
|
+
const u = "; filament_colour = " + d.join(";") + `
|
|
21
|
+
`, h = o.lastIndexOf(`
|
|
22
22
|
; CONFIG_BLOCK_END`);
|
|
23
|
-
if (
|
|
23
|
+
if (h >= 0) return o.slice(0, h + 1) + u + o.slice(h + 1);
|
|
24
24
|
let c = `
|
|
25
25
|
`;
|
|
26
|
-
return (!
|
|
27
|
-
`)) && (c = ""),
|
|
26
|
+
return (!o || o.endsWith(`
|
|
27
|
+
`)) && (c = ""), o + c + u;
|
|
28
28
|
}
|
|
29
|
-
const
|
|
29
|
+
const ht = {
|
|
30
30
|
// OrcaSlicer / PrusaSlicer ;TYPE: names
|
|
31
31
|
"outer wall": t.WALL,
|
|
32
32
|
"external perimeter": t.WALL,
|
|
@@ -80,7 +80,7 @@ const St = {
|
|
|
80
80
|
["support", t.SUPPORT],
|
|
81
81
|
["prime tower", t.PRIME],
|
|
82
82
|
["wipe_tower_real", t.PRIME]
|
|
83
|
-
],
|
|
83
|
+
], Ot = (n, r) => n === r || r.length < n.length && " (:/".includes(n[r.length]) && n.startsWith(r), Et = {
|
|
84
84
|
1: t.WALL,
|
|
85
85
|
2: t.WALL,
|
|
86
86
|
3: t.WALL,
|
|
@@ -99,136 +99,140 @@ const St = {
|
|
|
99
99
|
16: t.SUPPORT,
|
|
100
100
|
17: t.PRIME
|
|
101
101
|
};
|
|
102
|
-
function
|
|
103
|
-
const
|
|
104
|
-
let u = 0,
|
|
102
|
+
function Wt(n, r = {}) {
|
|
103
|
+
const o = Math.PI / 4 * (r.filamentDiameter > 0 ? r.filamentDiameter : Nt) ** 2, d = r.defaultLayerHeight > 0 ? r.defaultLayerHeight : Rt;
|
|
104
|
+
let u = 0, h = 0, c = 0, b = 0, w = !0, y = !0, g = 0, O = t.WALL, W = 0, R = null;
|
|
105
105
|
const E = [];
|
|
106
|
-
let D = !1, x = !0,
|
|
107
|
-
const
|
|
108
|
-
let
|
|
106
|
+
let D = !1, x = !0, v = NaN, q = 0, X = 0, $ = 0;
|
|
107
|
+
const z = /* @__PURE__ */ new Set([0]), H = [];
|
|
108
|
+
let k = [], j = [], U = !1;
|
|
109
109
|
const V = () => {
|
|
110
|
-
|
|
111
|
-
},
|
|
112
|
-
R = { z:
|
|
113
|
-
}, mt = () => E.length > 1 ? E[E.length - 2].z : 0, J = (
|
|
114
|
-
const
|
|
115
|
-
if (!(
|
|
116
|
-
if (e >
|
|
117
|
-
$ += e, H[
|
|
118
|
-
const
|
|
119
|
-
R.p.push(
|
|
120
|
-
const f = Math.max(0.02, R.z - mt() ||
|
|
110
|
+
O = t.WALL, W = 0;
|
|
111
|
+
}, C = (s, a = !1) => {
|
|
112
|
+
R = { z: s, p: [], w: [] }, E.push(R), x = !1, v = NaN, (a || !U) && V();
|
|
113
|
+
}, mt = () => E.length > 1 ? E[E.length - 2].z : 0, J = (s, a, S, P, A, i, e) => {
|
|
114
|
+
const m = Math.hypot(P - s, A - a, i - S);
|
|
115
|
+
if (!(m < K))
|
|
116
|
+
if (e > K) {
|
|
117
|
+
$ += e, H[g] = (H[g] ?? 0) + e, x ? C(Number.isNaN(v) ? i : v) : !D && i > R.z + 1e-3 && C(i);
|
|
118
|
+
const p = Z(O, g);
|
|
119
|
+
R.p.push(s, a, S, p, P, A, i, p);
|
|
120
|
+
const f = Math.max(0.02, R.z - mt() || d), L = W > 0 ? W : e * o / (m * f) + f * (1 - Math.PI / 4);
|
|
121
121
|
R.w.push(Math.min(3, Math.max(0.05, L))), q++;
|
|
122
|
-
} else R && (R.p.push(
|
|
122
|
+
} else R && (R.p.push(s, a, S, Z(t.TRAVEL, g), P, A, i, Z(t.TRAVEL, g)), R.w.push(0), X++);
|
|
123
123
|
};
|
|
124
|
-
for (const
|
|
124
|
+
for (const s of n.split(`
|
|
125
125
|
`)) {
|
|
126
|
-
const a =
|
|
126
|
+
const a = s.indexOf(";"), S = (a < 0 ? s : s.slice(0, a)).trim(), P = a < 0 ? "" : s.slice(a + 1).trim();
|
|
127
127
|
if (P && !S) {
|
|
128
|
-
const e = P.toLowerCase(),
|
|
129
|
-
if (
|
|
130
|
-
|
|
128
|
+
const e = P.toLowerCase(), m = At.exec(P);
|
|
129
|
+
if (m) {
|
|
130
|
+
m[1].toLowerCase() === "filament" ? k = ut(m[2]) : j = ut(m[2]);
|
|
131
131
|
continue;
|
|
132
132
|
}
|
|
133
133
|
if (e.startsWith("type:")) {
|
|
134
|
-
|
|
134
|
+
O = ht[e.slice(5).trim()] ?? t.WALL, U = !0;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (e.startsWith("feature:")) {
|
|
138
|
+
O = ht[e.slice(8).trim()] ?? t.WALL, U = !0;
|
|
135
139
|
continue;
|
|
136
140
|
}
|
|
137
141
|
if (e.startsWith("_extrusion_role:")) {
|
|
138
|
-
|
|
142
|
+
O = Et[parseInt(e.slice(16), 10)] ?? t.WALL, U = !0;
|
|
139
143
|
continue;
|
|
140
144
|
}
|
|
141
|
-
if (e.startsWith("width:")) {
|
|
142
|
-
const L = parseFloat(e.slice(
|
|
143
|
-
|
|
145
|
+
if (e.startsWith("width:") || e.startsWith("line_width:")) {
|
|
146
|
+
const L = parseFloat(e.slice(e.indexOf(":") + 1));
|
|
147
|
+
W = 0, L > 0 && (W = L);
|
|
144
148
|
continue;
|
|
145
149
|
}
|
|
146
|
-
if (e.startsWith("layer_change") || e.startsWith("layer:")) {
|
|
150
|
+
if (e.startsWith("layer_change") || e.startsWith("change_layer") || e.startsWith("layer:")) {
|
|
147
151
|
D = !0, x = !0, V();
|
|
148
152
|
continue;
|
|
149
153
|
}
|
|
150
|
-
if (e.startsWith("z:")) {
|
|
151
|
-
const L = parseFloat(e.slice(
|
|
152
|
-
x && L > 0 && (D = !0,
|
|
154
|
+
if (e.startsWith("z:") || e.startsWith("z_height:")) {
|
|
155
|
+
const L = parseFloat(e.slice(e.indexOf(":") + 1));
|
|
156
|
+
x && L > 0 && (D = !0, C(L));
|
|
153
157
|
continue;
|
|
154
158
|
}
|
|
155
|
-
const
|
|
156
|
-
if (
|
|
157
|
-
D = !0,
|
|
159
|
+
const p = /^layer \d+ z([-\d.]+)/.exec(e);
|
|
160
|
+
if (p) {
|
|
161
|
+
D = !0, C(parseFloat(p[1]), !0);
|
|
158
162
|
continue;
|
|
159
163
|
}
|
|
160
|
-
const f = Pt.find(([L]) =>
|
|
161
|
-
f && (
|
|
164
|
+
const f = Pt.find(([L]) => Ot(e, L));
|
|
165
|
+
f && (O = f[1]);
|
|
162
166
|
continue;
|
|
163
167
|
}
|
|
164
168
|
if (!S) continue;
|
|
165
|
-
const A = S.split(/\s+/),
|
|
166
|
-
if (
|
|
167
|
-
|
|
169
|
+
const A = S.split(/\s+/), i = A[0].toUpperCase();
|
|
170
|
+
if (i === "G90") {
|
|
171
|
+
w = !0;
|
|
168
172
|
continue;
|
|
169
173
|
}
|
|
170
|
-
if (
|
|
171
|
-
|
|
174
|
+
if (i === "G91") {
|
|
175
|
+
w = !1;
|
|
172
176
|
continue;
|
|
173
177
|
}
|
|
174
|
-
if (
|
|
175
|
-
|
|
178
|
+
if (i === "M82") {
|
|
179
|
+
y = !0;
|
|
176
180
|
continue;
|
|
177
181
|
}
|
|
178
|
-
if (
|
|
179
|
-
|
|
182
|
+
if (i === "M83") {
|
|
183
|
+
y = !1;
|
|
180
184
|
continue;
|
|
181
185
|
}
|
|
182
|
-
if (/^T\d+$/.test(
|
|
183
|
-
const e = parseInt(
|
|
184
|
-
e <=
|
|
186
|
+
if (/^T\d+$/.test(i)) {
|
|
187
|
+
const e = parseInt(i.slice(1), 10);
|
|
188
|
+
e <= It && (g = e, z.add(g));
|
|
185
189
|
continue;
|
|
186
190
|
}
|
|
187
|
-
if (
|
|
188
|
-
let e = NaN,
|
|
191
|
+
if (i === "G92" || i === "G0" || i === "G1" || i === "G2" || i === "G3") {
|
|
192
|
+
let e = NaN, m = NaN, p = NaN, f = NaN, L = 0, tt = 0;
|
|
189
193
|
for (let I = 1; I < A.length; I++) {
|
|
190
194
|
const l = A[I][0], N = parseFloat(A[I].slice(1));
|
|
191
|
-
Number.isNaN(N) || (l === "X" || l === "x" ? e = N : l === "Y" || l === "y" ?
|
|
195
|
+
Number.isNaN(N) || (l === "X" || l === "x" ? e = N : l === "Y" || l === "y" ? m = N : l === "Z" || l === "z" ? p = N : l === "E" || l === "e" ? f = N : l === "I" || l === "i" ? L = N : (l === "J" || l === "j") && (tt = N));
|
|
192
196
|
}
|
|
193
|
-
if (
|
|
194
|
-
Number.isNaN(e) || (u = e), Number.isNaN(
|
|
197
|
+
if (i === "G92") {
|
|
198
|
+
Number.isNaN(e) || (u = e), Number.isNaN(m) || (h = m), Number.isNaN(p) || (c = p), Number.isNaN(f) || (b = f);
|
|
195
199
|
continue;
|
|
196
200
|
}
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
199
|
-
const I = u + L, l =
|
|
200
|
-
let
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
let
|
|
205
|
-
for (let T = 1; T <=
|
|
206
|
-
const
|
|
207
|
-
J(
|
|
201
|
+
const B = Number.isNaN(e) ? u : w ? e : u + e, F = Number.isNaN(m) ? h : w ? m : h + m, Y = Number.isNaN(p) ? c : w ? p : c + p, et = Number.isNaN(f) ? 0 : y ? f - b : f;
|
|
202
|
+
if (i === "G2" || i === "G3") {
|
|
203
|
+
const I = u + L, l = h + tt, N = Math.hypot(u - I, h - l), G = Math.atan2(h - l, u - I);
|
|
204
|
+
let M = Math.atan2(F - l, B - I);
|
|
205
|
+
const it = i === "G3";
|
|
206
|
+
it && M <= G + K && (M += 2 * Math.PI), !it && M >= G - K && (M -= 2 * Math.PI);
|
|
207
|
+
const _ = Math.max(1, Math.min(360, Math.ceil(Math.abs(M - G) * N / 0.3)));
|
|
208
|
+
let nt = u, rt = h, st = c;
|
|
209
|
+
for (let T = 1; T <= _; T++) {
|
|
210
|
+
const ot = G + (M - G) * T / _, at = T === _ ? B : I + N * Math.cos(ot), lt = T === _ ? F : l + N * Math.sin(ot), ct = c + (Y - c) * T / _;
|
|
211
|
+
J(nt, rt, st, at, lt, ct, et / _), nt = at, rt = lt, st = ct;
|
|
208
212
|
}
|
|
209
213
|
} else
|
|
210
|
-
J(u,
|
|
211
|
-
u =
|
|
214
|
+
J(u, h, c, B, F, Y, et);
|
|
215
|
+
u = B, h = F, c = Y, Number.isNaN(f) || (b = y ? f : b + f);
|
|
212
216
|
}
|
|
213
217
|
}
|
|
214
|
-
const pt = Math.max(
|
|
218
|
+
const pt = Math.max(k.length, j.length), Q = Array.from({ length: pt }, (s, a) => j[a] || k[a] || null), Lt = Array.from({ length: Math.max(...z) + 1 }, (s, a) => H[a] || 0);
|
|
215
219
|
return {
|
|
216
|
-
layers: E.map((
|
|
220
|
+
layers: E.map((s) => ({ z: s.z, paths: Float32Array.from(s.p), widths: Float32Array.from(s.w) })),
|
|
217
221
|
stats: {
|
|
218
222
|
layers: E.length,
|
|
219
223
|
path_segments: q,
|
|
220
224
|
travel_segments: X,
|
|
221
225
|
filament_mm: $,
|
|
222
|
-
tools: [...
|
|
226
|
+
tools: [...z].sort((s, a) => s - a),
|
|
223
227
|
// The kernel's own stat name, so the Filament-view switch and the stats card's per-tool split work unchanged.
|
|
224
|
-
filament_mm_by_tool:
|
|
228
|
+
filament_mm_by_tool: Lt,
|
|
225
229
|
...Q.some(Boolean) && { colors: Q }
|
|
226
230
|
}
|
|
227
231
|
};
|
|
228
232
|
}
|
|
229
233
|
export {
|
|
230
234
|
wt as exportedGcode,
|
|
231
|
-
|
|
235
|
+
Wt as parseGcode,
|
|
232
236
|
gt as resultToolColors,
|
|
233
|
-
|
|
237
|
+
St as withFilamentColours
|
|
234
238
|
};
|