three-slicer 0.1.4 → 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/engine/src/settings.js +81 -0
- package/engine/src/slicer.worker.js +12 -0
- package/engine/src/slicer_core.js +0 -0
- package/engine/src/slicer_core.mt.js +0 -0
- package/package.json +1 -1
- package/types/settings.d.ts +12 -0
- package/viewer/dist/Viewport.js +2210 -1992
- 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/styles.css +6 -0
|
@@ -7,3 +7,10 @@
|
|
|
7
7
|
export function makeSlicerWorker() {
|
|
8
8
|
return new Worker(new URL('../../engine/src/slicer.worker.js', import.meta.url), { type: 'module' })
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
// 3MF parsing worker. Unlike the slicer worker this one points INTO dist/ — the viewer's `src/` is not published
|
|
12
|
+
// (packages/package.json `files` ships `viewer/dist` only), so the worker is a lib entry of the viewer's own build
|
|
13
|
+
// and lands beside this file. The static `new Worker(new URL(…))` pattern is what matters either way.
|
|
14
|
+
export function makeParse3mfWorker() {
|
|
15
|
+
return new Worker(new URL('./parse_3mf.worker.js', import.meta.url), { type: 'module' })
|
|
16
|
+
}
|
|
@@ -1,256 +1,184 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { OBJLoader as
|
|
3
|
-
import { PLYLoader as
|
|
4
|
-
import { AMFLoader as
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
const z = /<vertex\s+x="([^"]*)"\s+y="([^"]*)"\s+z="([^"]*)"/g, I = /<triangle\s+v1="([^"]*)"\s+v2="([^"]*)"\s+v3="([^"]*)"/g;
|
|
25
|
-
function J(t) {
|
|
26
|
-
const e = [];
|
|
27
|
-
z.lastIndex = 0;
|
|
28
|
-
let s;
|
|
29
|
-
for (; s = z.exec(t); ) e.push(+s[1], +s[2], +s[3]);
|
|
30
|
-
if (!e.length)
|
|
31
|
-
for (const o of t.match(/<vertex\b[^>]*>/g) || []) e.push(+w(o, "x"), +w(o, "y"), +w(o, "z"));
|
|
32
|
-
if (e.length < 9) return null;
|
|
33
|
-
const n = [];
|
|
34
|
-
for (I.lastIndex = 0; s = I.exec(t); ) n.push(+s[1], +s[2], +s[3]);
|
|
35
|
-
if (!n.length)
|
|
36
|
-
for (const o of t.match(/<triangle\b[^>]*>/g) || []) n.push(+w(o, "v1"), +w(o, "v2"), +w(o, "v3"));
|
|
37
|
-
return n.length ? { verts: e, tris: n } : null;
|
|
38
|
-
}
|
|
39
|
-
function Y(t) {
|
|
40
|
-
const e = /* @__PURE__ */ new Map(), s = /<object\b([^>]*)>([\s\S]*?)<\/object>/g;
|
|
41
|
-
let n;
|
|
42
|
-
for (; n = s.exec(t); ) {
|
|
43
|
-
const i = w(n[1], "id");
|
|
44
|
-
if (!i) continue;
|
|
45
|
-
const c = n[2], p = [];
|
|
46
|
-
for (const d of c.match(/<component\b[^>]*>/g) || []) {
|
|
47
|
-
const h = w(d, "objectid");
|
|
48
|
-
h && p.push({ objectid: h, path: w(d, "p:path") || w(d, "path"), transform: R(w(d, "transform")) });
|
|
49
|
-
}
|
|
50
|
-
e.set(i, p.length ? { components: p } : { mesh: J(c) });
|
|
1
|
+
import * as L from "three";
|
|
2
|
+
import { OBJLoader as T } from "three/examples/jsm/loaders/OBJLoader.js";
|
|
3
|
+
import { PLYLoader as v } from "three/examples/jsm/loaders/PLYLoader.js";
|
|
4
|
+
import { AMFLoader as P } from "three/examples/jsm/loaders/AMFLoader.js";
|
|
5
|
+
import { p as E } from "./parse_3mf-8oCnCJNK.js";
|
|
6
|
+
import { makeParse3mfWorker as W } from "./make_worker.js";
|
|
7
|
+
let h = null, M = !1, $ = 0;
|
|
8
|
+
const y = /* @__PURE__ */ new Map();
|
|
9
|
+
function B() {
|
|
10
|
+
if (M || typeof Worker > "u") return null;
|
|
11
|
+
if (h) return h;
|
|
12
|
+
try {
|
|
13
|
+
h = W(), h.onmessage = (t) => {
|
|
14
|
+
const { id: r, objects: s, project: o, error: n } = t.data || {}, i = y.get(r);
|
|
15
|
+
i && (y.delete(r), n ? i.reject(new Error(n)) : i.resolve({ objects: s, project: o }));
|
|
16
|
+
}, h.onerror = () => {
|
|
17
|
+
M = !0;
|
|
18
|
+
for (const [, t] of y) t.reject(new Error("3MF parse worker failed"));
|
|
19
|
+
y.clear(), h = null;
|
|
20
|
+
};
|
|
21
|
+
} catch {
|
|
22
|
+
M = !0, h = null;
|
|
51
23
|
}
|
|
52
|
-
|
|
53
|
-
if (u)
|
|
54
|
-
for (const i of u[1].match(/<item\b[^>]*>/g) || []) {
|
|
55
|
-
const c = w(i, "objectid");
|
|
56
|
-
c && o.push({ objectid: c, path: w(i, "p:path") || w(i, "path"), transform: R(w(i, "transform")) });
|
|
57
|
-
}
|
|
58
|
-
return { objects: e, items: o };
|
|
24
|
+
return h;
|
|
59
25
|
}
|
|
60
|
-
function
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const m = L(a);
|
|
69
|
-
if (i.has(m)) return i.get(m);
|
|
70
|
-
const l = o.get(m), M = l ? Y(u.decode(l)) : null;
|
|
71
|
-
return i.set(m, M), M;
|
|
72
|
-
};
|
|
73
|
-
let p = "3D/3dmodel.model";
|
|
74
|
-
const d = o.get("_rels/.rels");
|
|
75
|
-
if (d) {
|
|
76
|
-
for (const a of u.decode(d).match(/<Relationship\b[^>]*>/g) || [])
|
|
77
|
-
if ((w(a, "Type") || "").endsWith("/3dmodel")) {
|
|
78
|
-
p = L(w(a, "Target"));
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
26
|
+
async function C(t, r) {
|
|
27
|
+
const s = B();
|
|
28
|
+
if (!s) return E(t, r);
|
|
29
|
+
try {
|
|
30
|
+
const o = ++$, n = new Promise((i, c) => y.set(o, { resolve: i, reject: c }));
|
|
31
|
+
return s.postMessage({ id: o, buffer: t, baseName: r }), await n;
|
|
32
|
+
} catch {
|
|
33
|
+
return E(t, r);
|
|
81
34
|
}
|
|
82
|
-
const h = c(p);
|
|
83
|
-
if (!h) throw new Error(`3MF root model not found: ${p}`);
|
|
84
|
-
const r = [], f = (a, m, l, M, $) => {
|
|
85
|
-
if ($ > 16) return;
|
|
86
|
-
const T = c(m), E = T == null ? void 0 : T.objects.get(a);
|
|
87
|
-
if (!E) return;
|
|
88
|
-
if (E.components) {
|
|
89
|
-
for (const y of E.components) f(y.objectid, y.path || m, X(y.transform, l), M, $ + 1);
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (!E.mesh) return;
|
|
93
|
-
const { verts: v, tris: S } = E.mesh;
|
|
94
|
-
for (let y = 0; y < S.length; y++) {
|
|
95
|
-
const A = S[y] * 3, b = v[A], j = v[A + 1], F = v[A + 2];
|
|
96
|
-
M.push(
|
|
97
|
-
b * l[0] + j * l[3] + F * l[6] + l[9],
|
|
98
|
-
b * l[1] + j * l[4] + F * l[7] + l[10],
|
|
99
|
-
b * l[2] + j * l[5] + F * l[8] + l[11]
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}, g = h.items.length ? h.items : [...h.objects.keys()].filter((a) => h.objects.get(a).mesh).map((a) => ({ objectid: a, path: p, transform: x }));
|
|
103
|
-
return g.forEach((a, m) => {
|
|
104
|
-
const l = [];
|
|
105
|
-
f(a.objectid, a.path || p, a.transform, l, 0), l.length >= 9 && r.push({ name: g.length > 1 ? `${e}#${m + 1}` : e, tris: new Float32Array(l) });
|
|
106
|
-
}), r;
|
|
107
35
|
}
|
|
108
|
-
const
|
|
109
|
-
function
|
|
110
|
-
for (const s of (Array.isArray(t) ? t : String(t).split(",")).map((
|
|
111
|
-
|
|
36
|
+
const F = ["stl", "obj", "3mf", "amf", "ply"], x = /* @__PURE__ */ new Map();
|
|
37
|
+
function R(t, r) {
|
|
38
|
+
for (const s of (Array.isArray(t) ? t : String(t).split(",")).map((o) => o.trim().toLowerCase()))
|
|
39
|
+
x.set(s, r), F.includes(s) || F.push(s);
|
|
112
40
|
}
|
|
113
|
-
function
|
|
114
|
-
const
|
|
115
|
-
return
|
|
41
|
+
function D(t) {
|
|
42
|
+
const r = t.lastIndexOf(".");
|
|
43
|
+
return r < 0 ? "" : t.slice(r + 1).toLowerCase();
|
|
116
44
|
}
|
|
117
|
-
function
|
|
118
|
-
const
|
|
45
|
+
function I(t) {
|
|
46
|
+
const r = new DataView(t);
|
|
119
47
|
if (t.byteLength >= 84) {
|
|
120
|
-
const
|
|
121
|
-
if (t.byteLength === 84 +
|
|
122
|
-
const
|
|
123
|
-
let
|
|
124
|
-
for (let
|
|
125
|
-
|
|
126
|
-
for (let
|
|
127
|
-
|
|
128
|
-
|
|
48
|
+
const c = r.getUint32(80, !0);
|
|
49
|
+
if (t.byteLength === 84 + c * 50) {
|
|
50
|
+
const a = new Float32Array(c * 9);
|
|
51
|
+
let u = 84, d = 0;
|
|
52
|
+
for (let p = 0; p < c; p++) {
|
|
53
|
+
u += 12;
|
|
54
|
+
for (let e = 0; e < 3; e++)
|
|
55
|
+
a[d++] = r.getFloat32(u, !0), a[d++] = r.getFloat32(u + 4, !0), a[d++] = r.getFloat32(u + 8, !0), u += 12;
|
|
56
|
+
u += 2;
|
|
129
57
|
}
|
|
130
|
-
return
|
|
58
|
+
return a;
|
|
131
59
|
}
|
|
132
60
|
}
|
|
133
|
-
const s = new TextDecoder().decode(t),
|
|
134
|
-
let
|
|
135
|
-
for (;
|
|
136
|
-
return
|
|
61
|
+
const s = new TextDecoder().decode(t), o = /vertex\s+([-\d.eE+]+)\s+([-\d.eE+]+)\s+([-\d.eE+]+)/g, n = [];
|
|
62
|
+
let i;
|
|
63
|
+
for (; i = o.exec(s); ) n.push(+i[1], +i[2], +i[3]);
|
|
64
|
+
return n.length < 9 ? null : new Float32Array(n);
|
|
137
65
|
}
|
|
138
|
-
function
|
|
66
|
+
function A(t, r) {
|
|
139
67
|
var d;
|
|
140
68
|
const s = (d = t == null ? void 0 : t.attributes) == null ? void 0 : d.position;
|
|
141
69
|
if (!s || s.count < 3) return null;
|
|
142
|
-
const
|
|
143
|
-
let
|
|
144
|
-
const
|
|
145
|
-
|
|
70
|
+
const o = t.index, n = new L.Vector3(), i = Math.floor((o ? o.count : s.count) / 3), c = new Float32Array(i * 9);
|
|
71
|
+
let a = 0;
|
|
72
|
+
const u = (p) => {
|
|
73
|
+
n.fromBufferAttribute(s, p), r && n.applyMatrix4(r), c[a++] = n.x, c[a++] = n.y, c[a++] = n.z;
|
|
146
74
|
};
|
|
147
|
-
if (
|
|
148
|
-
for (let
|
|
75
|
+
if (o)
|
|
76
|
+
for (let p = 0; p < i * 3; p++) u(o.getX(p));
|
|
149
77
|
else
|
|
150
|
-
for (let
|
|
151
|
-
return
|
|
78
|
+
for (let p = 0; p < i * 3; p++) u(p);
|
|
79
|
+
return c;
|
|
152
80
|
}
|
|
153
|
-
function
|
|
81
|
+
function j(t) {
|
|
154
82
|
t.updateMatrixWorld(!0);
|
|
155
|
-
const
|
|
83
|
+
const r = [];
|
|
156
84
|
return t.traverse((s) => {
|
|
157
|
-
s.isMesh && s.geometry &&
|
|
158
|
-
}),
|
|
85
|
+
s.isMesh && s.geometry && r.push({ geometry: s.geometry, matrix: s.matrixWorld });
|
|
86
|
+
}), r;
|
|
159
87
|
}
|
|
160
|
-
function
|
|
161
|
-
const
|
|
162
|
-
if (!
|
|
163
|
-
if (s &&
|
|
164
|
-
return
|
|
165
|
-
if (
|
|
166
|
-
let
|
|
167
|
-
for (const
|
|
168
|
-
const
|
|
169
|
-
let
|
|
170
|
-
for (const
|
|
171
|
-
|
|
172
|
-
return [{ name: t, modelPos:
|
|
88
|
+
function k(t, r, s) {
|
|
89
|
+
const o = r.map((a) => A(a.geometry, a.matrix)).filter(Boolean);
|
|
90
|
+
if (!o.length) return [];
|
|
91
|
+
if (s && o.length > 1)
|
|
92
|
+
return o.map((a, u) => ({ name: `${t}#${u + 1}`, modelPos: a }));
|
|
93
|
+
if (o.length === 1) return [{ name: t, modelPos: o[0] }];
|
|
94
|
+
let n = 0;
|
|
95
|
+
for (const a of o) n += a.length;
|
|
96
|
+
const i = new Float32Array(n);
|
|
97
|
+
let c = 0;
|
|
98
|
+
for (const a of o)
|
|
99
|
+
i.set(a, c), c += a.length;
|
|
100
|
+
return [{ name: t, modelPos: i }];
|
|
173
101
|
}
|
|
174
|
-
async function
|
|
175
|
-
const s =
|
|
102
|
+
async function U(t, r) {
|
|
103
|
+
const s = D(t);
|
|
176
104
|
switch (s) {
|
|
177
105
|
case "stl": {
|
|
178
|
-
const
|
|
179
|
-
if (!
|
|
180
|
-
return [{ name: t, modelPos:
|
|
106
|
+
const o = I(r);
|
|
107
|
+
if (!o) throw new Error("Failed to parse STL");
|
|
108
|
+
return [{ name: t, modelPos: o }];
|
|
181
109
|
}
|
|
182
110
|
case "obj": {
|
|
183
|
-
const
|
|
184
|
-
return
|
|
111
|
+
const o = new T().parse(new TextDecoder().decode(r));
|
|
112
|
+
return k(t, j(o), !1);
|
|
185
113
|
}
|
|
186
114
|
case "ply": {
|
|
187
|
-
const
|
|
188
|
-
if (!
|
|
189
|
-
return [{ name: t, modelPos:
|
|
115
|
+
const o = new v().parse(r), n = A(o, null);
|
|
116
|
+
if (!n) throw new Error("No PLY geometry");
|
|
117
|
+
return [{ name: t, modelPos: n }];
|
|
190
118
|
}
|
|
191
119
|
case "3mf": {
|
|
192
|
-
const n =
|
|
193
|
-
if (!
|
|
194
|
-
return
|
|
120
|
+
const { objects: o, project: n } = await C(r, t), i = o.map((c) => ({ name: c.name, modelPos: c.tris, objectid: c.objectid, paint: c.paint, bbox: c.bbox, project: n }));
|
|
121
|
+
if (!i.length) throw new Error("No mesh in 3MF");
|
|
122
|
+
return i;
|
|
195
123
|
}
|
|
196
124
|
case "amf": {
|
|
197
|
-
const
|
|
198
|
-
if (!
|
|
199
|
-
return
|
|
125
|
+
const o = new P().parse(r), n = k(t, j(o), !0);
|
|
126
|
+
if (!n.length) throw new Error("No mesh in AMF");
|
|
127
|
+
return n;
|
|
200
128
|
}
|
|
201
129
|
default: {
|
|
202
|
-
const
|
|
203
|
-
if (!
|
|
204
|
-
const
|
|
205
|
-
if (!(
|
|
206
|
-
return
|
|
130
|
+
const o = x.get(s);
|
|
131
|
+
if (!o) throw new Error(`Unsupported format: .${s}`);
|
|
132
|
+
const n = await o(r, t);
|
|
133
|
+
if (!(n != null && n.length)) throw new Error(`No mesh in .${s}`);
|
|
134
|
+
return n;
|
|
207
135
|
}
|
|
208
136
|
}
|
|
209
137
|
}
|
|
210
|
-
function
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
213
|
-
const s = 1e4,
|
|
214
|
-
for (let
|
|
215
|
-
for (let
|
|
216
|
-
const
|
|
217
|
-
let
|
|
218
|
-
|
|
138
|
+
function V(t) {
|
|
139
|
+
const r = Math.floor(t.length / 9);
|
|
140
|
+
if (r < 2) return null;
|
|
141
|
+
const s = 1e4, o = /* @__PURE__ */ new Map(), n = new Int32Array(r * 3);
|
|
142
|
+
for (let e = 0; e < r; e++)
|
|
143
|
+
for (let l = 0; l < 3; l++) {
|
|
144
|
+
const f = e * 9 + l * 3, m = `${Math.round(t[f] * s)},${Math.round(t[f + 1] * s)},${Math.round(t[f + 2] * s)}`;
|
|
145
|
+
let w = o.get(m);
|
|
146
|
+
w === void 0 && (w = o.size, o.set(m, w)), n[e * 3 + l] = w;
|
|
219
147
|
}
|
|
220
|
-
const
|
|
221
|
-
for (let
|
|
222
|
-
const
|
|
223
|
-
for (;
|
|
224
|
-
|
|
225
|
-
return
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
},
|
|
229
|
-
for (let
|
|
230
|
-
for (let
|
|
231
|
-
const
|
|
232
|
-
|
|
148
|
+
const i = new Int32Array(r);
|
|
149
|
+
for (let e = 0; e < r; e++) i[e] = e;
|
|
150
|
+
const c = (e) => {
|
|
151
|
+
for (; i[e] !== e; )
|
|
152
|
+
i[e] = i[i[e]], e = i[e];
|
|
153
|
+
return e;
|
|
154
|
+
}, a = (e, l) => {
|
|
155
|
+
e = c(e), l = c(l), e !== l && (i[l] = e);
|
|
156
|
+
}, u = /* @__PURE__ */ new Map();
|
|
157
|
+
for (let e = 0; e < r; e++)
|
|
158
|
+
for (let l = 0; l < 3; l++) {
|
|
159
|
+
const f = n[e * 3 + l], m = n[e * 3 + (l + 1) % 3], w = f < m ? f * 4294967296 + m : m * 4294967296 + f, g = u.get(w);
|
|
160
|
+
g === void 0 ? u.set(w, e) : a(e, g);
|
|
233
161
|
}
|
|
234
162
|
const d = /* @__PURE__ */ new Map();
|
|
235
|
-
for (let
|
|
236
|
-
const
|
|
237
|
-
let
|
|
238
|
-
|
|
163
|
+
for (let e = 0; e < r; e++) {
|
|
164
|
+
const l = c(e);
|
|
165
|
+
let f = d.get(l);
|
|
166
|
+
f || (f = [], d.set(l, f)), f.push(e);
|
|
239
167
|
}
|
|
240
|
-
return d.size < 2 ? null : [...d.values()].sort((
|
|
241
|
-
const
|
|
242
|
-
let
|
|
243
|
-
for (const
|
|
244
|
-
const
|
|
245
|
-
for (let
|
|
168
|
+
return d.size < 2 ? null : [...d.values()].sort((e, l) => l.length - e.length).map((e) => {
|
|
169
|
+
const l = new Float32Array(e.length * 9);
|
|
170
|
+
let f = 0;
|
|
171
|
+
for (const m of e) {
|
|
172
|
+
const w = m * 9;
|
|
173
|
+
for (let g = 0; g < 9; g++) l[f++] = t[w + g];
|
|
246
174
|
}
|
|
247
|
-
return
|
|
175
|
+
return l;
|
|
248
176
|
});
|
|
249
177
|
}
|
|
250
178
|
export {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
179
|
+
F as SUPPORTED_EXT,
|
|
180
|
+
D as fileExt,
|
|
181
|
+
U as loadModel,
|
|
182
|
+
R as registerLoader,
|
|
183
|
+
V as splitConnectedComponents
|
|
256
184
|
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { unzip as U, unzipSync as N } from "three/examples/jsm/libs/fflate.module.js";
|
|
2
|
+
function X(n) {
|
|
3
|
+
return new Promise((e) => {
|
|
4
|
+
let o = !1;
|
|
5
|
+
const t = (s) => {
|
|
6
|
+
o || (o = !0, e(s));
|
|
7
|
+
};
|
|
8
|
+
try {
|
|
9
|
+
U(n, (s, r) => t(s ? N(n) : r));
|
|
10
|
+
} catch {
|
|
11
|
+
t(N(n));
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const E = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0];
|
|
16
|
+
function G(n, e) {
|
|
17
|
+
const o = new Array(12);
|
|
18
|
+
for (let t = 0; t < 4; t++)
|
|
19
|
+
for (let s = 0; s < 3; s++)
|
|
20
|
+
o[t * 3 + s] = n[t * 3] * e[s] + n[t * 3 + 1] * e[3 + s] + n[t * 3 + 2] * e[6 + s] + (t === 3 ? e[9 + s] : 0);
|
|
21
|
+
return o;
|
|
22
|
+
}
|
|
23
|
+
function O(n) {
|
|
24
|
+
if (!n) return E;
|
|
25
|
+
const e = n.trim().split(/\s+/).map(Number);
|
|
26
|
+
return e.length === 12 && e.every(Number.isFinite) ? e : E;
|
|
27
|
+
}
|
|
28
|
+
const B = {};
|
|
29
|
+
function i(n, e) {
|
|
30
|
+
const t = (B[e] || (B[e] = new RegExp(`\\b${e}\\s*=\\s*"([^"]*)"`))).exec(n);
|
|
31
|
+
return t ? t[1] : null;
|
|
32
|
+
}
|
|
33
|
+
const $ = /<vertex\s+x="([^"]*)"\s+y="([^"]*)"\s+z="([^"]*)"/g, C = /<triangle\s+v1="([^"]*)"\s+v2="([^"]*)"\s+v3="([^"]*)"([^>]*)/g, R = [
|
|
34
|
+
["paint_color", "color"],
|
|
35
|
+
// multi-material: state 1..16 == Extruder1..16
|
|
36
|
+
["paint_supports", "supports"],
|
|
37
|
+
// support enforcer/blocker: states 1 and 2
|
|
38
|
+
["paint_seam", "seam"],
|
|
39
|
+
// unsupported by the kernel — collected for the warning only
|
|
40
|
+
["paint_fuzzy_skin", "fuzzy"]
|
|
41
|
+
// ditto
|
|
42
|
+
];
|
|
43
|
+
function J() {
|
|
44
|
+
return { color: /* @__PURE__ */ new Map(), supports: /* @__PURE__ */ new Map(), seam: /* @__PURE__ */ new Map(), fuzzy: /* @__PURE__ */ new Map() };
|
|
45
|
+
}
|
|
46
|
+
function H(n) {
|
|
47
|
+
return !n || R.every(([, e]) => n[e].size === 0);
|
|
48
|
+
}
|
|
49
|
+
function D(n, e, o) {
|
|
50
|
+
for (const [t, s] of R) {
|
|
51
|
+
if (!n.includes(t)) continue;
|
|
52
|
+
const r = i(n, t);
|
|
53
|
+
r && o[s].set(e, r);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function V(n) {
|
|
57
|
+
const e = [];
|
|
58
|
+
$.lastIndex = 0;
|
|
59
|
+
let o;
|
|
60
|
+
for (; o = $.exec(n); ) e.push(+o[1], +o[2], +o[3]);
|
|
61
|
+
if (!e.length)
|
|
62
|
+
for (const r of n.match(/<vertex\b[^>]*>/g) || []) e.push(+i(r, "x"), +i(r, "y"), +i(r, "z"));
|
|
63
|
+
if (e.length < 9) return null;
|
|
64
|
+
const t = [], s = J();
|
|
65
|
+
for (C.lastIndex = 0; o = C.exec(n); ) {
|
|
66
|
+
const r = t.length / 3;
|
|
67
|
+
t.push(+o[1], +o[2], +o[3]), o[4].length > 2 && D(o[4], r, s);
|
|
68
|
+
}
|
|
69
|
+
if (!t.length)
|
|
70
|
+
for (const r of n.match(/<triangle\b[^>]*>/g) || []) {
|
|
71
|
+
const c = t.length / 3;
|
|
72
|
+
t.push(+i(r, "v1"), +i(r, "v2"), +i(r, "v3")), D(r, c, s);
|
|
73
|
+
}
|
|
74
|
+
return t.length ? { verts: e, tris: t, paint: s } : null;
|
|
75
|
+
}
|
|
76
|
+
function Y(n) {
|
|
77
|
+
const e = /* @__PURE__ */ new Map(), o = /<object\b([^>]*)>([\s\S]*?)<\/object>/g;
|
|
78
|
+
let t;
|
|
79
|
+
for (; t = o.exec(n); ) {
|
|
80
|
+
const c = i(t[1], "id");
|
|
81
|
+
if (!c) continue;
|
|
82
|
+
const p = t[2], f = [];
|
|
83
|
+
for (const u of p.match(/<component\b[^>]*>/g) || []) {
|
|
84
|
+
const m = i(u, "objectid");
|
|
85
|
+
m && f.push({ objectid: m, path: i(u, "p:path") || i(u, "path"), transform: O(i(u, "transform")) });
|
|
86
|
+
}
|
|
87
|
+
e.set(c, f.length ? { components: f } : { mesh: V(p) });
|
|
88
|
+
}
|
|
89
|
+
const s = [], r = /<build\b[^>]*>([\s\S]*?)<\/build>/.exec(n);
|
|
90
|
+
if (r)
|
|
91
|
+
for (const c of r[1].match(/<item\b[^>]*>/g) || []) {
|
|
92
|
+
const p = i(c, "objectid");
|
|
93
|
+
p && s.push({ objectid: p, path: i(c, "p:path") || i(c, "path"), transform: O(i(c, "transform")) });
|
|
94
|
+
}
|
|
95
|
+
return { objects: e, items: s };
|
|
96
|
+
}
|
|
97
|
+
function P(n) {
|
|
98
|
+
const e = String(n).replace(/^\/+/, "");
|
|
99
|
+
return e.includes("%") ? decodeURIComponent(e) : e;
|
|
100
|
+
}
|
|
101
|
+
function A(n) {
|
|
102
|
+
const e = {};
|
|
103
|
+
for (const o of n.match(/<metadata\b[^>]*>/g) || []) {
|
|
104
|
+
const t = i(o, "key");
|
|
105
|
+
t && (e[t] = i(o, "value") ?? "");
|
|
106
|
+
}
|
|
107
|
+
return e;
|
|
108
|
+
}
|
|
109
|
+
function W(n) {
|
|
110
|
+
const e = /* @__PURE__ */ new Map(), o = /<object\b([^>]*)>([\s\S]*?)<\/object>/g;
|
|
111
|
+
let t;
|
|
112
|
+
for (; t = o.exec(n); ) {
|
|
113
|
+
const c = i(t[1], "id");
|
|
114
|
+
c && e.set(c, A(t[2].replace(/<part\b[\s\S]*?<\/part>/g, "")));
|
|
115
|
+
}
|
|
116
|
+
const s = [], r = /<plate\b[^>]*>([\s\S]*?)<\/plate>/g;
|
|
117
|
+
for (; t = r.exec(n); ) {
|
|
118
|
+
const c = t[1], p = A(c.replace(/<model_instance\b[\s\S]*?<\/model_instance>/g, "")), f = [];
|
|
119
|
+
for (const u of c.match(/<model_instance\b[\s\S]*?<\/model_instance>/g) || []) {
|
|
120
|
+
const m = A(u).object_id;
|
|
121
|
+
m && f.push(m);
|
|
122
|
+
}
|
|
123
|
+
s.push({ index: Math.max(0, (Number(p.plater_id) || s.length + 1) - 1), objectIds: f });
|
|
124
|
+
}
|
|
125
|
+
return { objects: e, plates: s };
|
|
126
|
+
}
|
|
127
|
+
function q(n, e) {
|
|
128
|
+
var c, p;
|
|
129
|
+
const o = (f) => {
|
|
130
|
+
const u = n.get(f);
|
|
131
|
+
return u ? e.decode(u) : null;
|
|
132
|
+
}, t = {
|
|
133
|
+
settings: null,
|
|
134
|
+
// Metadata/project_settings.config — the flattened preset (raw upstream strings)
|
|
135
|
+
objectMeta: /* @__PURE__ */ new Map(),
|
|
136
|
+
// 3mf object id -> per-object metadata (name, extruder, per-object overrides)
|
|
137
|
+
plates: [],
|
|
138
|
+
// [{index, objectIds}]
|
|
139
|
+
hasLayerHeightProfile: !1,
|
|
140
|
+
hasCustomGcodePerLayer: !1
|
|
141
|
+
}, s = o("Metadata/project_settings.config");
|
|
142
|
+
if (s)
|
|
143
|
+
try {
|
|
144
|
+
t.settings = JSON.parse(s);
|
|
145
|
+
} catch {
|
|
146
|
+
t.settings = null;
|
|
147
|
+
}
|
|
148
|
+
const r = o("Metadata/model_settings.config");
|
|
149
|
+
if (r) {
|
|
150
|
+
const f = W(r);
|
|
151
|
+
t.objectMeta = f.objects, t.plates = f.plates;
|
|
152
|
+
}
|
|
153
|
+
return t.hasLayerHeightProfile = !!((c = o("Metadata/layer_heights_profile.txt")) != null && c.trim()), t.hasCustomGcodePerLayer = !!((p = o("Metadata/custom_gcode_per_layer.xml")) != null && p.trim()), t;
|
|
154
|
+
}
|
|
155
|
+
async function Q(n, e = "model") {
|
|
156
|
+
const o = ArrayBuffer.isView(n) ? new Uint8Array(n.buffer, n.byteOffset, n.byteLength) : new Uint8Array(n), t = await X(o), s = /* @__PURE__ */ new Map();
|
|
157
|
+
for (const l of Object.keys(t)) s.set(P(l), t[l]);
|
|
158
|
+
const r = new TextDecoder(), c = /* @__PURE__ */ new Map(), p = (l) => {
|
|
159
|
+
const g = P(l);
|
|
160
|
+
if (c.has(g)) return c.get(g);
|
|
161
|
+
const a = s.get(g), d = a ? Y(r.decode(a)) : null;
|
|
162
|
+
return c.set(g, d), d;
|
|
163
|
+
};
|
|
164
|
+
let f = "3D/3dmodel.model";
|
|
165
|
+
const u = s.get("_rels/.rels");
|
|
166
|
+
if (u) {
|
|
167
|
+
for (const l of r.decode(u).match(/<Relationship\b[^>]*>/g) || [])
|
|
168
|
+
if ((i(l, "Type") || "").endsWith("/3dmodel")) {
|
|
169
|
+
f = P(i(l, "Target"));
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const m = p(f);
|
|
174
|
+
if (!m) throw new Error(`3MF root model not found: ${f}`);
|
|
175
|
+
const k = [], F = (l, g, a, d, y, x) => {
|
|
176
|
+
if (x > 16) return;
|
|
177
|
+
const j = p(g), b = j == null ? void 0 : j.objects.get(l);
|
|
178
|
+
if (!b) return;
|
|
179
|
+
if (b.components) {
|
|
180
|
+
for (const h of b.components) F(h.objectid, h.path || g, G(h.transform, a), d, y, x + 1);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (!b.mesh) return;
|
|
184
|
+
const { verts: w, tris: _, paint: M } = b.mesh, v = d.length / 9;
|
|
185
|
+
for (let h = 0; h < _.length; h++) {
|
|
186
|
+
const I = _[h] * 3, S = w[I], T = w[I + 1], z = w[I + 2];
|
|
187
|
+
d.push(
|
|
188
|
+
S * a[0] + T * a[3] + z * a[6] + a[9],
|
|
189
|
+
S * a[1] + T * a[4] + z * a[7] + a[10],
|
|
190
|
+
S * a[2] + T * a[5] + z * a[8] + a[11]
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (M) for (const [, h] of R)
|
|
194
|
+
for (const [I, S] of M[h]) y[h].set(v + I, S);
|
|
195
|
+
}, L = m.items.length ? m.items : [...m.objects.keys()].filter((l) => m.objects.get(l).mesh).map((l) => ({ objectid: l, path: f, transform: E }));
|
|
196
|
+
return L.forEach((l, g) => {
|
|
197
|
+
const a = [], d = J();
|
|
198
|
+
if (F(l.objectid, l.path || f, l.transform, a, d, 0), a.length < 9) return;
|
|
199
|
+
const y = new Float32Array(a);
|
|
200
|
+
let x = 1 / 0, j = 1 / 0, b = -1 / 0, w = -1 / 0;
|
|
201
|
+
for (let _ = 0; _ < y.length; _ += 3) {
|
|
202
|
+
const M = y[_], v = y[_ + 1];
|
|
203
|
+
M < x && (x = M), M > b && (b = M), v < j && (j = v), v > w && (w = v);
|
|
204
|
+
}
|
|
205
|
+
k.push({
|
|
206
|
+
name: L.length > 1 ? `${e}#${g + 1}` : e,
|
|
207
|
+
tris: y,
|
|
208
|
+
objectid: l.objectid,
|
|
209
|
+
paint: H(d) ? null : d,
|
|
210
|
+
bbox: { minX: x, minY: j, maxX: b, maxY: w }
|
|
211
|
+
});
|
|
212
|
+
}), { objects: k, project: q(s, r) };
|
|
213
|
+
}
|
|
214
|
+
export {
|
|
215
|
+
Q as p
|
|
216
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { p as f } from "./parse_3mf-8oCnCJNK.js";
|
|
2
|
+
self.onmessage = async (a) => {
|
|
3
|
+
const { id: s, buffer: r, baseName: t } = a.data || {};
|
|
4
|
+
try {
|
|
5
|
+
const { objects: e, project: o } = await f(r, t);
|
|
6
|
+
self.postMessage({ id: s, objects: e, project: o }, e.map((c) => c.tris.buffer));
|
|
7
|
+
} catch (e) {
|
|
8
|
+
self.postMessage({ id: s, error: e && e.message || String(e) });
|
|
9
|
+
}
|
|
10
|
+
};
|
package/viewer/styles.css
CHANGED
|
@@ -92,6 +92,12 @@
|
|
|
92
92
|
.bp-actions { margin-top: 6px; }
|
|
93
93
|
.bp-note { margin-top: 6px; font-size: 10.5px; line-height: 1.35; color: #8b98a2; }
|
|
94
94
|
|
|
95
|
+
/* Over-bed warning while arranging. Sits where the stats card does in Preview — the two never show at once, so the
|
|
96
|
+
same corner carries "here is the problem" in both modes. Amber rather than red: the model is still slicable. */
|
|
97
|
+
.bed-warn { position: absolute; left: 14px; bottom: 32px; z-index: 2;
|
|
98
|
+
background: rgba(58,42,16,.94); border: 1px solid #7a5a1e; border-radius: 10px;
|
|
99
|
+
padding: 8px 13px; font-size: 12.5px; color: #f0c060; }
|
|
100
|
+
|
|
95
101
|
/* Preview stats card, bottom left */
|
|
96
102
|
.stats-card { position: absolute; left: 14px; bottom: 32px; background: rgba(22,27,33,.93); border: 1px solid #2c353d;
|
|
97
103
|
border-radius: 10px; padding: 10px 14px; font-size: 13px; line-height: 1.7; z-index: 2; color: #cdd6dd; }
|