@lingbi/studio 0.3.3 → 0.5.0

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.
Files changed (80) hide show
  1. package/Application.d.ts +74 -12
  2. package/DownloadSaveSink.d.ts +21 -0
  3. package/EditedArchiveResourceContext.d.ts +32 -0
  4. package/ImportPerformanceMode.d.ts +5 -0
  5. package/ModelDownloadController.d.ts +21 -0
  6. package/ModelExportContext.d.ts +20 -0
  7. package/ModelExportContextFactory.d.ts +21 -0
  8. package/ModelResourceListSource.d.ts +2 -0
  9. package/ModelResourceSceneSource.d.ts +12 -3
  10. package/ModelUpAxis.d.ts +4 -0
  11. package/StoneBasePoseMode.d.ts +4 -0
  12. package/StudioToolbarState.d.ts +20 -0
  13. package/StudioWorkspace.d.ts +45 -0
  14. package/archive/ArchiveImportCentralDirectoryValidator.d.ts +21 -0
  15. package/archive/ArchiveImportChunkAcknowledgement.d.ts +12 -0
  16. package/archive/ArchiveImportCrc32.d.ts +8 -0
  17. package/archive/ArchiveImportError.d.ts +9 -0
  18. package/archive/ArchiveImportFileResult.d.ts +7 -0
  19. package/archive/ArchiveImportJobRequest.d.ts +4 -0
  20. package/archive/ArchiveImportJobValidator.d.ts +8 -0
  21. package/archive/ArchiveImportModelFormat.d.ts +1 -0
  22. package/archive/ArchiveImportPathValidator.d.ts +22 -0
  23. package/archive/ArchiveImportResult.d.ts +6 -0
  24. package/archive/ArchiveImportResultAccumulator.d.ts +17 -0
  25. package/archive/ArchiveImportRole.d.ts +1 -0
  26. package/archive/ArchiveImportRoleResult.d.ts +6 -0
  27. package/archive/ArchiveImportSignatureValidator.d.ts +7 -0
  28. package/archive/ArchiveImportWorkerClient.d.ts +23 -0
  29. package/archive/ArchiveImportWorkerClientTask.d.ts +17 -0
  30. package/archive/ArchiveImportWorkerEndpoint.d.ts +13 -0
  31. package/archive/ArchiveImportWorkerEntry.d.ts +1 -0
  32. package/archive/ArchiveImportWorkerFactory.d.ts +4 -0
  33. package/archive/ArchiveImportWorkerMessage.d.ts +48 -0
  34. package/archive/ArchiveImportWorkerMessageParser.d.ts +16 -0
  35. package/archive/ArchiveImportWorkerPort.d.ts +5 -0
  36. package/archive/ArchiveImportWorkerRuntime.d.ts +47 -0
  37. package/archive/ArchiveImportWorkerTask.d.ts +38 -0
  38. package/archive/BrowserArchiveImportWorkerEndpoint.d.ts +15 -0
  39. package/archive/BrowserArchiveImportWorkerFactory.d.ts +5 -0
  40. package/archive/BrowserArchiveImportWorkerPort.d.ts +7 -0
  41. package/assets/ArchiveImportWorkerEntry-Dh4RZo22.js +1548 -0
  42. package/assets/ManifoldWorkerEntry-BulSBVFW.js +3028 -0
  43. package/assets/ModelExportWorkerEntry-DwhcdDU3.js +3457 -0
  44. package/export/BrowserExportWorkerPort.d.ts +7 -0
  45. package/export/BrowserModelExportWorkerEndpoint.d.ts +15 -0
  46. package/export/BrowserModelExportWorkerFactory.d.ts +6 -0
  47. package/export/ByteChunkWriter.d.ts +11 -0
  48. package/export/ExportChunkAcknowledgement.d.ts +13 -0
  49. package/export/ExportJobRequest.d.ts +6 -0
  50. package/export/ExportJobValidator.d.ts +17 -0
  51. package/export/ExportModelFormat.d.ts +1 -0
  52. package/export/ExportModelRole.d.ts +1 -0
  53. package/export/ExportResourceFile.d.ts +6 -0
  54. package/export/ExportRoleRequest.d.ts +25 -0
  55. package/export/ExportWorkerMessage.d.ts +41 -0
  56. package/export/ExportWorkerMessageParser.d.ts +29 -0
  57. package/export/ExportWorkerPort.d.ts +6 -0
  58. package/export/ExportWorkerTask.d.ts +15 -0
  59. package/export/GeneratedMtlWriter.d.ts +10 -0
  60. package/export/GeneratedObjModelBaker.d.ts +12 -0
  61. package/export/GeneratedTextChunkWriter.d.ts +10 -0
  62. package/export/MatrixTransformer.d.ts +21 -0
  63. package/export/ModelExportError.d.ts +14 -0
  64. package/export/ModelExportWorkerClient.d.ts +24 -0
  65. package/export/ModelExportWorkerClientListener.d.ts +5 -0
  66. package/export/ModelExportWorkerClientTask.d.ts +14 -0
  67. package/export/ModelExportWorkerEndpoint.d.ts +14 -0
  68. package/export/ModelExportWorkerEntry.d.ts +1 -0
  69. package/export/ModelExportWorkerFactory.d.ts +4 -0
  70. package/export/ModelExportWorkerRuntime.d.ts +47 -0
  71. package/export/ObjModelBaker.d.ts +27 -0
  72. package/export/PlyHeaderParser.d.ts +64 -0
  73. package/export/PlyModelBaker.d.ts +52 -0
  74. package/index.d.ts +3 -4
  75. package/index.js +11880 -4665
  76. package/package.json +1 -1
  77. package/ObjImportSource.d.ts +0 -5
  78. package/ObjResourceListSource.d.ts +0 -3
  79. package/ObjResourceSceneSource.d.ts +0 -5
  80. package/ObjSceneSource.d.ts +0 -5
@@ -0,0 +1,3457 @@
1
+ (function() {
2
+ "use strict";
3
+ class BrowserExportWorkerPort {
4
+ addEventListener(type, listener) {
5
+ globalThis.addEventListener(type, listener);
6
+ }
7
+ postMessage(message, transfer) {
8
+ Reflect.apply(globalThis.postMessage, globalThis, [message, transfer]);
9
+ }
10
+ }
11
+ var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;
12
+ var fleb = new u8([
13
+ 0,
14
+ 0,
15
+ 0,
16
+ 0,
17
+ 0,
18
+ 0,
19
+ 0,
20
+ 0,
21
+ 1,
22
+ 1,
23
+ 1,
24
+ 1,
25
+ 2,
26
+ 2,
27
+ 2,
28
+ 2,
29
+ 3,
30
+ 3,
31
+ 3,
32
+ 3,
33
+ 4,
34
+ 4,
35
+ 4,
36
+ 4,
37
+ 5,
38
+ 5,
39
+ 5,
40
+ 5,
41
+ 0,
42
+ /* unused */
43
+ 0,
44
+ 0,
45
+ /* impossible */
46
+ 0
47
+ ]);
48
+ var fdeb = new u8([
49
+ 0,
50
+ 0,
51
+ 0,
52
+ 0,
53
+ 1,
54
+ 1,
55
+ 2,
56
+ 2,
57
+ 3,
58
+ 3,
59
+ 4,
60
+ 4,
61
+ 5,
62
+ 5,
63
+ 6,
64
+ 6,
65
+ 7,
66
+ 7,
67
+ 8,
68
+ 8,
69
+ 9,
70
+ 9,
71
+ 10,
72
+ 10,
73
+ 11,
74
+ 11,
75
+ 12,
76
+ 12,
77
+ 13,
78
+ 13,
79
+ /* unused */
80
+ 0,
81
+ 0
82
+ ]);
83
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
84
+ var freb = function(eb, start) {
85
+ var b = new u16(31);
86
+ for (var i2 = 0; i2 < 31; ++i2) {
87
+ b[i2] = start += 1 << eb[i2 - 1];
88
+ }
89
+ var r = new i32(b[30]);
90
+ for (var i2 = 1; i2 < 30; ++i2) {
91
+ for (var j = b[i2]; j < b[i2 + 1]; ++j) {
92
+ r[j] = j - b[i2] << 5 | i2;
93
+ }
94
+ }
95
+ return { b, r };
96
+ };
97
+ var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;
98
+ fl[28] = 258, revfl[258] = 28;
99
+ var _b = freb(fdeb, 0), revfd = _b.r;
100
+ var rev = new u16(32768);
101
+ for (var i = 0; i < 32768; ++i) {
102
+ var x = (i & 43690) >> 1 | (i & 21845) << 1;
103
+ x = (x & 52428) >> 2 | (x & 13107) << 2;
104
+ x = (x & 61680) >> 4 | (x & 3855) << 4;
105
+ rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
106
+ }
107
+ var hMap = (function(cd, mb, r) {
108
+ var s = cd.length;
109
+ var i2 = 0;
110
+ var l = new u16(mb);
111
+ for (; i2 < s; ++i2) {
112
+ if (cd[i2])
113
+ ++l[cd[i2] - 1];
114
+ }
115
+ var le = new u16(mb);
116
+ for (i2 = 1; i2 < mb; ++i2) {
117
+ le[i2] = le[i2 - 1] + l[i2 - 1] << 1;
118
+ }
119
+ var co;
120
+ if (r) {
121
+ co = new u16(1 << mb);
122
+ var rvb = 15 - mb;
123
+ for (i2 = 0; i2 < s; ++i2) {
124
+ if (cd[i2]) {
125
+ var sv = i2 << 4 | cd[i2];
126
+ var r_1 = mb - cd[i2];
127
+ var v = le[cd[i2] - 1]++ << r_1;
128
+ for (var m = v | (1 << r_1) - 1; v <= m; ++v) {
129
+ co[rev[v] >> rvb] = sv;
130
+ }
131
+ }
132
+ }
133
+ } else {
134
+ co = new u16(s);
135
+ for (i2 = 0; i2 < s; ++i2) {
136
+ if (cd[i2]) {
137
+ co[i2] = rev[le[cd[i2] - 1]++] >> 15 - cd[i2];
138
+ }
139
+ }
140
+ }
141
+ return co;
142
+ });
143
+ var flt = new u8(288);
144
+ for (var i = 0; i < 144; ++i)
145
+ flt[i] = 8;
146
+ for (var i = 144; i < 256; ++i)
147
+ flt[i] = 9;
148
+ for (var i = 256; i < 280; ++i)
149
+ flt[i] = 7;
150
+ for (var i = 280; i < 288; ++i)
151
+ flt[i] = 8;
152
+ var fdt = new u8(32);
153
+ for (var i = 0; i < 32; ++i)
154
+ fdt[i] = 5;
155
+ var flm = /* @__PURE__ */ hMap(flt, 9, 0);
156
+ var fdm = /* @__PURE__ */ hMap(fdt, 5, 0);
157
+ var shft = function(p) {
158
+ return (p + 7) / 8 | 0;
159
+ };
160
+ var slc = function(v, s, e) {
161
+ if (e == null || e > v.length)
162
+ e = v.length;
163
+ return new u8(v.subarray(s, e));
164
+ };
165
+ var ec = [
166
+ "unexpected EOF",
167
+ "invalid block type",
168
+ "invalid length/literal",
169
+ "invalid distance",
170
+ "stream finished",
171
+ "no stream handler",
172
+ ,
173
+ // determined by compression function
174
+ "no callback",
175
+ "invalid UTF-8 data",
176
+ "extra field too long",
177
+ "date not in range 1980-2099",
178
+ "filename too long",
179
+ "stream finishing",
180
+ "invalid zip data"
181
+ // determined by unknown compression method
182
+ ];
183
+ var err = function(ind, msg, nt) {
184
+ var e = new Error(msg || ec[ind]);
185
+ e.code = ind;
186
+ if (Error.captureStackTrace)
187
+ Error.captureStackTrace(e, err);
188
+ if (!nt)
189
+ throw e;
190
+ return e;
191
+ };
192
+ var wbits = function(d, p, v) {
193
+ v <<= p & 7;
194
+ var o = p / 8 | 0;
195
+ d[o] |= v;
196
+ d[o + 1] |= v >> 8;
197
+ };
198
+ var wbits16 = function(d, p, v) {
199
+ v <<= p & 7;
200
+ var o = p / 8 | 0;
201
+ d[o] |= v;
202
+ d[o + 1] |= v >> 8;
203
+ d[o + 2] |= v >> 16;
204
+ };
205
+ var hTree = function(d, mb) {
206
+ var t = [];
207
+ for (var i2 = 0; i2 < d.length; ++i2) {
208
+ if (d[i2])
209
+ t.push({ s: i2, f: d[i2] });
210
+ }
211
+ var s = t.length;
212
+ var t2 = t.slice();
213
+ if (!s)
214
+ return { t: et, l: 0 };
215
+ if (s == 1) {
216
+ var v = new u8(t[0].s + 1);
217
+ v[t[0].s] = 1;
218
+ return { t: v, l: 1 };
219
+ }
220
+ t.sort(function(a, b) {
221
+ return a.f - b.f;
222
+ });
223
+ t.push({ s: -1, f: 25001 });
224
+ var l = t[0], r = t[1], i0 = 0, i1 = 1, i22 = 2;
225
+ t[0] = { s: -1, f: l.f + r.f, l, r };
226
+ while (i1 != s - 1) {
227
+ l = t[t[i0].f < t[i22].f ? i0++ : i22++];
228
+ r = t[i0 != i1 && t[i0].f < t[i22].f ? i0++ : i22++];
229
+ t[i1++] = { s: -1, f: l.f + r.f, l, r };
230
+ }
231
+ var maxSym = t2[0].s;
232
+ for (var i2 = 1; i2 < s; ++i2) {
233
+ if (t2[i2].s > maxSym)
234
+ maxSym = t2[i2].s;
235
+ }
236
+ var tr = new u16(maxSym + 1);
237
+ var mbt = ln(t[i1 - 1], tr, 0);
238
+ if (mbt > mb) {
239
+ var i2 = 0, dt = 0;
240
+ var lft = mbt - mb, cst = 1 << lft;
241
+ t2.sort(function(a, b) {
242
+ return tr[b.s] - tr[a.s] || a.f - b.f;
243
+ });
244
+ for (; i2 < s; ++i2) {
245
+ var i2_1 = t2[i2].s;
246
+ if (tr[i2_1] > mb) {
247
+ dt += cst - (1 << mbt - tr[i2_1]);
248
+ tr[i2_1] = mb;
249
+ } else
250
+ break;
251
+ }
252
+ dt >>= lft;
253
+ while (dt > 0) {
254
+ var i2_2 = t2[i2].s;
255
+ if (tr[i2_2] < mb)
256
+ dt -= 1 << mb - tr[i2_2]++ - 1;
257
+ else
258
+ ++i2;
259
+ }
260
+ for (; i2 >= 0 && dt; --i2) {
261
+ var i2_3 = t2[i2].s;
262
+ if (tr[i2_3] == mb) {
263
+ --tr[i2_3];
264
+ ++dt;
265
+ }
266
+ }
267
+ mbt = mb;
268
+ }
269
+ return { t: new u8(tr), l: mbt };
270
+ };
271
+ var ln = function(n, l, d) {
272
+ return n.s == -1 ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1)) : l[n.s] = d;
273
+ };
274
+ var lc = function(c) {
275
+ var s = c.length;
276
+ while (s && !c[--s])
277
+ ;
278
+ var cl = new u16(++s);
279
+ var cli = 0, cln = c[0], cls = 1;
280
+ var w = function(v) {
281
+ cl[cli++] = v;
282
+ };
283
+ for (var i2 = 1; i2 <= s; ++i2) {
284
+ if (c[i2] == cln && i2 != s)
285
+ ++cls;
286
+ else {
287
+ if (!cln && cls > 2) {
288
+ for (; cls > 138; cls -= 138)
289
+ w(32754);
290
+ if (cls > 2) {
291
+ w(cls > 10 ? cls - 11 << 5 | 28690 : cls - 3 << 5 | 12305);
292
+ cls = 0;
293
+ }
294
+ } else if (cls > 3) {
295
+ w(cln), --cls;
296
+ for (; cls > 6; cls -= 6)
297
+ w(8304);
298
+ if (cls > 2)
299
+ w(cls - 3 << 5 | 8208), cls = 0;
300
+ }
301
+ while (cls--)
302
+ w(cln);
303
+ cls = 1;
304
+ cln = c[i2];
305
+ }
306
+ }
307
+ return { c: cl.subarray(0, cli), n: s };
308
+ };
309
+ var clen = function(cf, cl) {
310
+ var l = 0;
311
+ for (var i2 = 0; i2 < cl.length; ++i2)
312
+ l += cf[i2] * cl[i2];
313
+ return l;
314
+ };
315
+ var wfblk = function(out, pos, dat) {
316
+ var s = dat.length;
317
+ var o = shft(pos + 2);
318
+ out[o] = s & 255;
319
+ out[o + 1] = s >> 8;
320
+ out[o + 2] = out[o] ^ 255;
321
+ out[o + 3] = out[o + 1] ^ 255;
322
+ for (var i2 = 0; i2 < s; ++i2)
323
+ out[o + i2 + 4] = dat[i2];
324
+ return (o + 4 + s) * 8;
325
+ };
326
+ var wblk = function(dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
327
+ wbits(out, p++, final);
328
+ ++lf[256];
329
+ var _a2 = hTree(lf, 15), dlt = _a2.t, mlb = _a2.l;
330
+ var _b2 = hTree(df, 15), ddt = _b2.t, mdb = _b2.l;
331
+ var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
332
+ var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
333
+ var lcfreq = new u16(19);
334
+ for (var i2 = 0; i2 < lclt.length; ++i2)
335
+ ++lcfreq[lclt[i2] & 31];
336
+ for (var i2 = 0; i2 < lcdt.length; ++i2)
337
+ ++lcfreq[lcdt[i2] & 31];
338
+ var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
339
+ var nlcc = 19;
340
+ for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
341
+ ;
342
+ var flen = bl + 5 << 3;
343
+ var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
344
+ var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
345
+ if (bs >= 0 && flen <= ftlen && flen <= dtlen)
346
+ return wfblk(out, p, dat.subarray(bs, bs + bl));
347
+ var lm, ll, dm, dl;
348
+ wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
349
+ if (dtlen < ftlen) {
350
+ lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
351
+ var llm = hMap(lct, mlcb, 0);
352
+ wbits(out, p, nlc - 257);
353
+ wbits(out, p + 5, ndc - 1);
354
+ wbits(out, p + 10, nlcc - 4);
355
+ p += 14;
356
+ for (var i2 = 0; i2 < nlcc; ++i2)
357
+ wbits(out, p + 3 * i2, lct[clim[i2]]);
358
+ p += 3 * nlcc;
359
+ var lcts = [lclt, lcdt];
360
+ for (var it = 0; it < 2; ++it) {
361
+ var clct = lcts[it];
362
+ for (var i2 = 0; i2 < clct.length; ++i2) {
363
+ var len = clct[i2] & 31;
364
+ wbits(out, p, llm[len]), p += lct[len];
365
+ if (len > 15)
366
+ wbits(out, p, clct[i2] >> 5 & 127), p += clct[i2] >> 12;
367
+ }
368
+ }
369
+ } else {
370
+ lm = flm, ll = flt, dm = fdm, dl = fdt;
371
+ }
372
+ for (var i2 = 0; i2 < li; ++i2) {
373
+ var sym = syms[i2];
374
+ if (sym > 255) {
375
+ var len = sym >> 18 & 31;
376
+ wbits16(out, p, lm[len + 257]), p += ll[len + 257];
377
+ if (len > 7)
378
+ wbits(out, p, sym >> 23 & 31), p += fleb[len];
379
+ var dst = sym & 31;
380
+ wbits16(out, p, dm[dst]), p += dl[dst];
381
+ if (dst > 3)
382
+ wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];
383
+ } else {
384
+ wbits16(out, p, lm[sym]), p += ll[sym];
385
+ }
386
+ }
387
+ wbits16(out, p, lm[256]);
388
+ return p + ll[256];
389
+ };
390
+ var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
391
+ var et = /* @__PURE__ */ new u8(0);
392
+ var dflt = function(dat, lvl, plvl, pre, post, st) {
393
+ var s = st.z || dat.length;
394
+ var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post);
395
+ var w = o.subarray(pre, o.length - post);
396
+ var lst = st.l;
397
+ var pos = (st.r || 0) & 7;
398
+ if (lvl) {
399
+ if (pos)
400
+ w[0] = st.r >> 3;
401
+ var opt = deo[lvl - 1];
402
+ var n = opt >> 13, c = opt & 8191;
403
+ var msk_1 = (1 << plvl) - 1;
404
+ var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
405
+ var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
406
+ var hsh = function(i3) {
407
+ return (dat[i3] ^ dat[i3 + 1] << bs1_1 ^ dat[i3 + 2] << bs2_1) & msk_1;
408
+ };
409
+ var syms = new i32(25e3);
410
+ var lf = new u16(288), df = new u16(32);
411
+ var lc_1 = 0, eb = 0, i2 = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
412
+ for (; i2 + 2 < s; ++i2) {
413
+ var hv = hsh(i2);
414
+ var imod = i2 & 32767, pimod = head[hv];
415
+ prev[imod] = pimod;
416
+ head[hv] = imod;
417
+ if (wi <= i2) {
418
+ var rem = s - i2;
419
+ if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {
420
+ pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i2 - bs, pos);
421
+ li = lc_1 = eb = 0, bs = i2;
422
+ for (var j = 0; j < 286; ++j)
423
+ lf[j] = 0;
424
+ for (var j = 0; j < 30; ++j)
425
+ df[j] = 0;
426
+ }
427
+ var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
428
+ if (rem > 2 && hv == hsh(i2 - dif)) {
429
+ var maxn = Math.min(n, rem) - 1;
430
+ var maxd = Math.min(32767, i2);
431
+ var ml = Math.min(258, rem);
432
+ while (dif <= maxd && --ch_1 && imod != pimod) {
433
+ if (dat[i2 + l] == dat[i2 + l - dif]) {
434
+ var nl = 0;
435
+ for (; nl < ml && dat[i2 + nl] == dat[i2 + nl - dif]; ++nl)
436
+ ;
437
+ if (nl > l) {
438
+ l = nl, d = dif;
439
+ if (nl > maxn)
440
+ break;
441
+ var mmd = Math.min(dif, nl - 2);
442
+ var md = 0;
443
+ for (var j = 0; j < mmd; ++j) {
444
+ var ti = i2 - dif + j & 32767;
445
+ var pti = prev[ti];
446
+ var cd = ti - pti & 32767;
447
+ if (cd > md)
448
+ md = cd, pimod = ti;
449
+ }
450
+ }
451
+ }
452
+ imod = pimod, pimod = prev[imod];
453
+ dif += imod - pimod & 32767;
454
+ }
455
+ }
456
+ if (d) {
457
+ syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];
458
+ var lin = revfl[l] & 31, din = revfd[d] & 31;
459
+ eb += fleb[lin] + fdeb[din];
460
+ ++lf[257 + lin];
461
+ ++df[din];
462
+ wi = i2 + l;
463
+ ++lc_1;
464
+ } else {
465
+ syms[li++] = dat[i2];
466
+ ++lf[dat[i2]];
467
+ }
468
+ }
469
+ }
470
+ for (i2 = Math.max(i2, wi); i2 < s; ++i2) {
471
+ syms[li++] = dat[i2];
472
+ ++lf[dat[i2]];
473
+ }
474
+ pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i2 - bs, pos);
475
+ if (!lst) {
476
+ st.r = pos & 7 | w[pos / 8 | 0] << 3;
477
+ pos -= 7;
478
+ st.h = head, st.p = prev, st.i = i2, st.w = wi;
479
+ }
480
+ } else {
481
+ for (var i2 = st.w || 0; i2 < s + lst; i2 += 65535) {
482
+ var e = i2 + 65535;
483
+ if (e >= s) {
484
+ w[pos / 8 | 0] = lst;
485
+ e = s;
486
+ }
487
+ pos = wfblk(w, pos + 1, dat.subarray(i2, e));
488
+ }
489
+ st.i = s;
490
+ }
491
+ return slc(o, 0, pre + shft(pos) + post);
492
+ };
493
+ var crct = /* @__PURE__ */ (function() {
494
+ var t = new Int32Array(256);
495
+ for (var i2 = 0; i2 < 256; ++i2) {
496
+ var c = i2, k = 9;
497
+ while (--k)
498
+ c = (c & 1 && -306674912) ^ c >>> 1;
499
+ t[i2] = c;
500
+ }
501
+ return t;
502
+ })();
503
+ var crc = function() {
504
+ var c = -1;
505
+ return {
506
+ p: function(d) {
507
+ var cr = c;
508
+ for (var i2 = 0; i2 < d.length; ++i2)
509
+ cr = crct[cr & 255 ^ d[i2]] ^ cr >>> 8;
510
+ c = cr;
511
+ },
512
+ d: function() {
513
+ return ~c;
514
+ }
515
+ };
516
+ };
517
+ var dopt = function(dat, opt, pre, post, st) {
518
+ if (!st) {
519
+ st = { l: 1 };
520
+ if (opt.dictionary) {
521
+ var dict = opt.dictionary.subarray(-32768);
522
+ var newDat = new u8(dict.length + dat.length);
523
+ newDat.set(dict);
524
+ newDat.set(dat, dict.length);
525
+ dat = newDat;
526
+ st.w = dict.length;
527
+ }
528
+ }
529
+ return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post, st);
530
+ };
531
+ var mrg = function(a, b) {
532
+ var o = {};
533
+ for (var k in a)
534
+ o[k] = a[k];
535
+ for (var k in b)
536
+ o[k] = b[k];
537
+ return o;
538
+ };
539
+ var wbytes = function(d, b, v) {
540
+ for (; v; ++b)
541
+ d[b] = v, v >>>= 8;
542
+ };
543
+ var Deflate = /* @__PURE__ */ (function() {
544
+ function Deflate2(opts, cb) {
545
+ if (typeof opts == "function")
546
+ cb = opts, opts = {};
547
+ this.ondata = cb;
548
+ this.o = opts || {};
549
+ this.s = { l: 0, i: 32768, w: 32768, z: 32768 };
550
+ this.b = new u8(98304);
551
+ if (this.o.dictionary) {
552
+ var dict = this.o.dictionary.subarray(-32768);
553
+ this.b.set(dict, 32768 - dict.length);
554
+ this.s.i = 32768 - dict.length;
555
+ }
556
+ }
557
+ Deflate2.prototype.p = function(c, f) {
558
+ this.ondata(dopt(c, this.o, 0, 0, this.s), f);
559
+ };
560
+ Deflate2.prototype.push = function(chunk, final) {
561
+ if (!this.ondata)
562
+ err(5);
563
+ if (this.s.l)
564
+ err(4);
565
+ var endLen = chunk.length + this.s.z;
566
+ if (endLen > this.b.length) {
567
+ if (endLen > 2 * this.b.length - 32768) {
568
+ var newBuf = new u8(endLen & -32768);
569
+ newBuf.set(this.b.subarray(0, this.s.z));
570
+ this.b = newBuf;
571
+ }
572
+ var split = this.b.length - this.s.z;
573
+ this.b.set(chunk.subarray(0, split), this.s.z);
574
+ this.s.z = this.b.length;
575
+ this.p(this.b, false);
576
+ this.b.set(this.b.subarray(-32768));
577
+ this.b.set(chunk.subarray(split), 32768);
578
+ this.s.z = chunk.length - split + 32768;
579
+ this.s.i = 32766, this.s.w = 32768;
580
+ } else {
581
+ this.b.set(chunk, this.s.z);
582
+ this.s.z += chunk.length;
583
+ }
584
+ this.s.l = final & 1;
585
+ if (this.s.z > this.s.w + 8191 || final) {
586
+ this.p(this.b, final || false);
587
+ this.s.w = this.s.i, this.s.i -= 2;
588
+ }
589
+ if (final) {
590
+ this.s = this.o = {};
591
+ this.b = et;
592
+ }
593
+ };
594
+ Deflate2.prototype.flush = function(sync) {
595
+ if (!this.ondata)
596
+ err(5);
597
+ if (this.s.l)
598
+ err(4);
599
+ this.p(this.b, false);
600
+ this.s.w = this.s.i, this.s.i -= 2;
601
+ if (sync) {
602
+ var c = new u8(6);
603
+ c[0] = this.s.r >> 3;
604
+ var ep = wfblk(c, this.s.r, et);
605
+ this.s.r = 0;
606
+ this.ondata(c.subarray(0, ep >> 3), false);
607
+ }
608
+ };
609
+ return Deflate2;
610
+ })();
611
+ var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();
612
+ var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
613
+ var tds = 0;
614
+ try {
615
+ td.decode(et, { stream: true });
616
+ tds = 1;
617
+ } catch (e) {
618
+ }
619
+ function strToU8(str, latin1) {
620
+ var i2;
621
+ if (te)
622
+ return te.encode(str);
623
+ var l = str.length;
624
+ var ar = new u8(str.length + (str.length >> 1));
625
+ var ai = 0;
626
+ var w = function(v) {
627
+ ar[ai++] = v;
628
+ };
629
+ for (var i2 = 0; i2 < l; ++i2) {
630
+ if (ai + 5 > ar.length) {
631
+ var n = new u8(ai + 8 + (l - i2 << 1));
632
+ n.set(ar);
633
+ ar = n;
634
+ }
635
+ var c = str.charCodeAt(i2);
636
+ if (c < 128 || latin1)
637
+ w(c);
638
+ else if (c < 2048)
639
+ w(192 | c >> 6), w(128 | c & 63);
640
+ else if (c > 55295 && c < 57344)
641
+ c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i2) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);
642
+ else
643
+ w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);
644
+ }
645
+ return slc(ar, 0, ai);
646
+ }
647
+ var dbf = function(l) {
648
+ return l == 1 ? 3 : l < 6 ? 2 : l == 9 ? 1 : 0;
649
+ };
650
+ var exfl = function(ex) {
651
+ var le = 0;
652
+ if (ex) {
653
+ for (var k in ex) {
654
+ var l = ex[k].length;
655
+ if (l > 65535)
656
+ err(9);
657
+ le += l + 4;
658
+ }
659
+ }
660
+ return le;
661
+ };
662
+ var wzh = function(d, b, f, fn, u, c, ce, co) {
663
+ var fl2 = fn.length, ex = f.extra, col = co && co.length;
664
+ var exl = exfl(ex);
665
+ wbytes(d, b, ce != null ? 33639248 : 67324752), b += 4;
666
+ if (ce != null)
667
+ d[b++] = 20, d[b++] = f.os;
668
+ d[b] = 20, b += 2;
669
+ d[b++] = f.flag << 1 | (c < 0 && 8), d[b++] = u && 8;
670
+ d[b++] = f.compression & 255, d[b++] = f.compression >> 8;
671
+ var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;
672
+ if (y < 0 || y > 119)
673
+ err(10);
674
+ wbytes(d, b, y << 25 | dt.getMonth() + 1 << 21 | dt.getDate() << 16 | dt.getHours() << 11 | dt.getMinutes() << 5 | dt.getSeconds() >> 1), b += 4;
675
+ if (c != -1) {
676
+ wbytes(d, b, f.crc);
677
+ wbytes(d, b + 4, c < 0 ? -c - 2 : c);
678
+ wbytes(d, b + 8, f.size);
679
+ }
680
+ wbytes(d, b + 12, fl2);
681
+ wbytes(d, b + 14, exl), b += 16;
682
+ if (ce != null) {
683
+ wbytes(d, b, col);
684
+ wbytes(d, b + 6, f.attrs);
685
+ wbytes(d, b + 10, ce), b += 14;
686
+ }
687
+ d.set(fn, b);
688
+ b += fl2;
689
+ if (exl) {
690
+ for (var k in ex) {
691
+ var exf = ex[k], l = exf.length;
692
+ wbytes(d, b, +k);
693
+ wbytes(d, b + 2, l);
694
+ d.set(exf, b + 4), b += 4 + l;
695
+ }
696
+ }
697
+ if (col)
698
+ d.set(co, b), b += col;
699
+ return b;
700
+ };
701
+ var wzf = function(o, b, c, d, e) {
702
+ wbytes(o, b, 101010256);
703
+ wbytes(o, b + 8, c);
704
+ wbytes(o, b + 10, c);
705
+ wbytes(o, b + 12, d);
706
+ wbytes(o, b + 16, e);
707
+ };
708
+ var ZipPassThrough = /* @__PURE__ */ (function() {
709
+ function ZipPassThrough2(filename) {
710
+ this.filename = filename;
711
+ this.c = crc();
712
+ this.size = 0;
713
+ this.compression = 0;
714
+ }
715
+ ZipPassThrough2.prototype.process = function(chunk, final) {
716
+ this.ondata(null, chunk, final);
717
+ };
718
+ ZipPassThrough2.prototype.push = function(chunk, final) {
719
+ if (!this.ondata)
720
+ err(5);
721
+ this.c.p(chunk);
722
+ this.size += chunk.length;
723
+ if (final)
724
+ this.crc = this.c.d();
725
+ this.process(chunk, final || false);
726
+ };
727
+ return ZipPassThrough2;
728
+ })();
729
+ var ZipDeflate = /* @__PURE__ */ (function() {
730
+ function ZipDeflate2(filename, opts) {
731
+ var _this = this;
732
+ if (!opts)
733
+ opts = {};
734
+ ZipPassThrough.call(this, filename);
735
+ this.d = new Deflate(opts, function(dat, final) {
736
+ _this.ondata(null, dat, final);
737
+ });
738
+ this.compression = 8;
739
+ this.flag = dbf(opts.level);
740
+ }
741
+ ZipDeflate2.prototype.process = function(chunk, final) {
742
+ try {
743
+ this.d.push(chunk, final);
744
+ } catch (e) {
745
+ this.ondata(e, null, final);
746
+ }
747
+ };
748
+ ZipDeflate2.prototype.push = function(chunk, final) {
749
+ ZipPassThrough.prototype.push.call(this, chunk, final);
750
+ };
751
+ return ZipDeflate2;
752
+ })();
753
+ var Zip = /* @__PURE__ */ (function() {
754
+ function Zip2(cb) {
755
+ this.ondata = cb;
756
+ this.u = [];
757
+ this.d = 1;
758
+ }
759
+ Zip2.prototype.add = function(file) {
760
+ var _this = this;
761
+ if (!this.ondata)
762
+ err(5);
763
+ if (this.d & 2)
764
+ this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, false);
765
+ else {
766
+ var f = strToU8(file.filename), fl_1 = f.length;
767
+ var com = file.comment, o = com && strToU8(com);
768
+ var u = fl_1 != file.filename.length || o && com.length != o.length;
769
+ var hl_1 = fl_1 + exfl(file.extra) + 30;
770
+ if (fl_1 > 65535)
771
+ this.ondata(err(11, 0, 1), null, false);
772
+ var header = new u8(hl_1);
773
+ wzh(header, 0, file, f, u, -1);
774
+ var chks_1 = [header];
775
+ var pAll_1 = function() {
776
+ for (var _i = 0, chks_2 = chks_1; _i < chks_2.length; _i++) {
777
+ var chk = chks_2[_i];
778
+ _this.ondata(null, chk, false);
779
+ }
780
+ chks_1 = [];
781
+ };
782
+ var tr_1 = this.d;
783
+ this.d = 0;
784
+ var ind_1 = this.u.length;
785
+ var uf_1 = mrg(file, {
786
+ f,
787
+ u,
788
+ o,
789
+ t: function() {
790
+ if (file.terminate)
791
+ file.terminate();
792
+ },
793
+ r: function() {
794
+ pAll_1();
795
+ if (tr_1) {
796
+ var nxt = _this.u[ind_1 + 1];
797
+ if (nxt)
798
+ nxt.r();
799
+ else
800
+ _this.d = 1;
801
+ }
802
+ tr_1 = 1;
803
+ }
804
+ });
805
+ var cl_1 = 0;
806
+ file.ondata = function(err2, dat, final) {
807
+ if (err2) {
808
+ _this.ondata(err2, dat, final);
809
+ _this.terminate();
810
+ } else {
811
+ cl_1 += dat.length;
812
+ chks_1.push(dat);
813
+ if (final) {
814
+ var dd = new u8(16);
815
+ wbytes(dd, 0, 134695760);
816
+ wbytes(dd, 4, file.crc);
817
+ wbytes(dd, 8, cl_1);
818
+ wbytes(dd, 12, file.size);
819
+ chks_1.push(dd);
820
+ uf_1.c = cl_1, uf_1.b = hl_1 + cl_1 + 16, uf_1.crc = file.crc, uf_1.size = file.size;
821
+ if (tr_1)
822
+ uf_1.r();
823
+ tr_1 = 1;
824
+ } else if (tr_1)
825
+ pAll_1();
826
+ }
827
+ };
828
+ this.u.push(uf_1);
829
+ }
830
+ };
831
+ Zip2.prototype.end = function() {
832
+ var _this = this;
833
+ if (this.d & 2) {
834
+ this.ondata(err(4 + (this.d & 1) * 8, 0, 1), null, true);
835
+ return;
836
+ }
837
+ if (this.d)
838
+ this.e();
839
+ else
840
+ this.u.push({
841
+ r: function() {
842
+ if (!(_this.d & 1))
843
+ return;
844
+ _this.u.splice(-1, 1);
845
+ _this.e();
846
+ },
847
+ t: function() {
848
+ }
849
+ });
850
+ this.d = 3;
851
+ };
852
+ Zip2.prototype.e = function() {
853
+ var bt = 0, l = 0, tl = 0;
854
+ for (var _i = 0, _a2 = this.u; _i < _a2.length; _i++) {
855
+ var f = _a2[_i];
856
+ tl += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0);
857
+ }
858
+ var out = new u8(tl + 22);
859
+ for (var _b2 = 0, _c = this.u; _b2 < _c.length; _b2++) {
860
+ var f = _c[_b2];
861
+ wzh(out, bt, f, f.f, f.u, -f.c - 2, l, f.o);
862
+ bt += 46 + f.f.length + exfl(f.extra) + (f.o ? f.o.length : 0), l += f.b;
863
+ }
864
+ wzf(out, bt, this.u.length, tl, l);
865
+ this.ondata(null, out, true);
866
+ this.d = 2;
867
+ };
868
+ Zip2.prototype.terminate = function() {
869
+ for (var _i = 0, _a2 = this.u; _i < _a2.length; _i++) {
870
+ var f = _a2[_i];
871
+ f.t();
872
+ }
873
+ this.d = 2;
874
+ };
875
+ return Zip2;
876
+ })();
877
+ var ModelExportErrorCode = /* @__PURE__ */ ((ModelExportErrorCode2) => {
878
+ ModelExportErrorCode2["Cancelled"] = "cancelled";
879
+ ModelExportErrorCode2["InvalidJob"] = "invalid-job";
880
+ ModelExportErrorCode2["InvalidModel"] = "invalid-model";
881
+ ModelExportErrorCode2["InvalidSource"] = "invalid-source";
882
+ ModelExportErrorCode2["Protocol"] = "protocol";
883
+ ModelExportErrorCode2["UnsupportedModel"] = "unsupported-model";
884
+ ModelExportErrorCode2["WriteFailed"] = "write-failed";
885
+ return ModelExportErrorCode2;
886
+ })(ModelExportErrorCode || {});
887
+ class ModelExportError extends Error {
888
+ code;
889
+ constructor(code, message) {
890
+ super(message);
891
+ this.code = code;
892
+ this.name = "ModelExportError";
893
+ }
894
+ }
895
+ class ExportChunkAcknowledgement {
896
+ chunkId;
897
+ promise;
898
+ taskId;
899
+ rejectPromise;
900
+ resolvePromise;
901
+ settled = false;
902
+ constructor(taskId, chunkId) {
903
+ this.taskId = taskId;
904
+ this.chunkId = chunkId;
905
+ this.promise = new Promise(this.initializePromise.bind(this));
906
+ }
907
+ acknowledge() {
908
+ if (this.settled) {
909
+ return;
910
+ }
911
+ this.settled = true;
912
+ this.resolvePromise?.();
913
+ }
914
+ cancel() {
915
+ if (this.settled) {
916
+ return;
917
+ }
918
+ this.settled = true;
919
+ this.rejectPromise?.(
920
+ new ModelExportError(ModelExportErrorCode.Cancelled, "The model export was cancelled.")
921
+ );
922
+ }
923
+ initializePromise(resolve, reject) {
924
+ this.resolvePromise = resolve;
925
+ this.rejectPromise = reject;
926
+ }
927
+ }
928
+ class ExportJobValidator {
929
+ validate(job) {
930
+ this.assertTaskId(job.taskId);
931
+ this.assertRoles(job.roles);
932
+ }
933
+ assertTaskId(taskId) {
934
+ if (!Number.isSafeInteger(taskId) || taskId <= 0) {
935
+ throw new ModelExportError(
936
+ ModelExportErrorCode.InvalidJob,
937
+ "The export task id must be a positive safe integer."
938
+ );
939
+ }
940
+ }
941
+ assertRoles(roles) {
942
+ if (roles.length === 0 || roles.length > 2) {
943
+ throw new ModelExportError(
944
+ ModelExportErrorCode.InvalidJob,
945
+ "An export must contain one or two model roles."
946
+ );
947
+ }
948
+ for (let index = 0; index < roles.length; index += 1) {
949
+ const role = roles[index];
950
+ if (role === void 0) {
951
+ throw new ModelExportError(
952
+ ModelExportErrorCode.InvalidJob,
953
+ "The export contains an empty role."
954
+ );
955
+ }
956
+ this.assertRole(role, index, roles.length);
957
+ }
958
+ }
959
+ assertRole(role, index, roleCount) {
960
+ const expectedRole = index === 0 ? "stone" : "base";
961
+ if (roleCount > 1 && role.role !== expectedRole) {
962
+ throw new ModelExportError(
963
+ ModelExportErrorCode.InvalidJob,
964
+ "Export roles must be ordered as stone followed by base."
965
+ );
966
+ }
967
+ if (!this.isModelFormat(role.format)) {
968
+ throw new ModelExportError(
969
+ ModelExportErrorCode.InvalidJob,
970
+ "The export role has an unsupported model format."
971
+ );
972
+ }
973
+ this.assertMatrix(role.matrix);
974
+ if (role.kind === "generated-obj") {
975
+ this.assertGeneratedRole(role);
976
+ return;
977
+ }
978
+ this.assertFiles(role.files, role.format);
979
+ }
980
+ assertGeneratedRole(role) {
981
+ const roleValue = role.role;
982
+ if (roleValue !== "base" || !this.isModelFormat(role.sourceFormat)) {
983
+ throw new ModelExportError(
984
+ ModelExportErrorCode.InvalidJob,
985
+ "Only the base role may contain a generated Boolean mesh."
986
+ );
987
+ }
988
+ if (!this.isSafeFileName(role.modelFileName) || !this.isSafeFileName(role.materialFileName)) {
989
+ throw new ModelExportError(
990
+ ModelExportErrorCode.InvalidJob,
991
+ "Generated model file names must be single safe file names."
992
+ );
993
+ }
994
+ this.assertFiles(role.files, role.sourceFormat);
995
+ this.assertGeneratedSnapshot(role.generated);
996
+ }
997
+ assertGeneratedSnapshot(snapshot) {
998
+ if (!Number.isSafeInteger(snapshot.sourceRevision) || snapshot.sourceRevision < 1 || !Number.isSafeInteger(snapshot.revision) || snapshot.revision < 1 || snapshot.positions.length === 0 || snapshot.positions.length % 3 !== 0 || snapshot.normals.length !== snapshot.positions.length || snapshot.uvs.length !== snapshot.positions.length / 3 * 2 || snapshot.indices.length === 0 || snapshot.indices.length % 3 !== 0 || snapshot.groups.length === 0 || snapshot.materials.length === 0) {
999
+ throw new ModelExportError(
1000
+ ModelExportErrorCode.InvalidJob,
1001
+ "The generated Boolean base mesh is incomplete."
1002
+ );
1003
+ }
1004
+ if (!this.hasFiniteValues(snapshot.positions) || !this.hasFiniteValues(snapshot.normals) || !this.hasFiniteValues(snapshot.uvs)) {
1005
+ throw new ModelExportError(
1006
+ ModelExportErrorCode.InvalidJob,
1007
+ "The generated Boolean base mesh contains non-finite values."
1008
+ );
1009
+ }
1010
+ for (const index of snapshot.indices) {
1011
+ if (index >= snapshot.positions.length / 3) {
1012
+ throw new ModelExportError(
1013
+ ModelExportErrorCode.InvalidJob,
1014
+ "The generated Boolean base mesh contains an invalid index."
1015
+ );
1016
+ }
1017
+ }
1018
+ for (const group of snapshot.groups) {
1019
+ if (!Number.isSafeInteger(group.start) || !Number.isSafeInteger(group.count) || !Number.isSafeInteger(group.materialIndex) || group.start < 0 || group.count <= 0 || group.start + group.count > snapshot.indices.length || group.start % 3 !== 0 || group.count % 3 !== 0 || group.materialIndex < 0 || group.materialIndex >= snapshot.materials.length) {
1020
+ throw new ModelExportError(
1021
+ ModelExportErrorCode.InvalidJob,
1022
+ "The generated Boolean base mesh has invalid material groups."
1023
+ );
1024
+ }
1025
+ }
1026
+ }
1027
+ hasFiniteValues(values) {
1028
+ return values.every(Number.isFinite);
1029
+ }
1030
+ assertMatrix(matrix) {
1031
+ if (matrix.length !== 16 || !matrix.every(Number.isFinite)) {
1032
+ throw new ModelExportError(
1033
+ ModelExportErrorCode.InvalidJob,
1034
+ "The export role must include sixteen finite world matrix values."
1035
+ );
1036
+ }
1037
+ }
1038
+ assertFiles(files, format) {
1039
+ if (files.length === 0) {
1040
+ throw new ModelExportError(
1041
+ ModelExportErrorCode.InvalidJob,
1042
+ "The export role must contain at least one source file."
1043
+ );
1044
+ }
1045
+ const fileNames = /* @__PURE__ */ new Set();
1046
+ let modelCount = 0;
1047
+ for (const file of files) {
1048
+ this.assertFile(file, format, fileNames);
1049
+ if (file.isModel) {
1050
+ modelCount += 1;
1051
+ }
1052
+ }
1053
+ if (modelCount !== 1) {
1054
+ throw new ModelExportError(
1055
+ ModelExportErrorCode.InvalidJob,
1056
+ "Each export role must contain exactly one model file."
1057
+ );
1058
+ }
1059
+ }
1060
+ assertFile(file, format, fileNames) {
1061
+ if (!this.isSafeFileName(file.fileName)) {
1062
+ throw new ModelExportError(
1063
+ ModelExportErrorCode.InvalidJob,
1064
+ "Each export file name must be a single non-empty file name."
1065
+ );
1066
+ }
1067
+ if (fileNames.has(file.fileName)) {
1068
+ throw new ModelExportError(
1069
+ ModelExportErrorCode.InvalidJob,
1070
+ "File names must be unique within an export role."
1071
+ );
1072
+ }
1073
+ fileNames.add(file.fileName);
1074
+ if (file.url.length === 0) {
1075
+ throw new ModelExportError(
1076
+ ModelExportErrorCode.InvalidJob,
1077
+ "Each export file must include a source URL."
1078
+ );
1079
+ }
1080
+ if (file.isModel && !this.hasExpectedModelExtension(file.fileName, format)) {
1081
+ throw new ModelExportError(
1082
+ ModelExportErrorCode.InvalidJob,
1083
+ "The marked model file does not match the role model format."
1084
+ );
1085
+ }
1086
+ }
1087
+ hasExpectedModelExtension(fileName, format) {
1088
+ return fileName.toLowerCase().endsWith(`.${format}`);
1089
+ }
1090
+ isModelFormat(value) {
1091
+ return value === "obj" || value === "ply";
1092
+ }
1093
+ isSafeFileName(fileName) {
1094
+ return fileName.length > 0 && !fileName.includes("/") && !fileName.includes("\\") && !fileName.includes("\0") && fileName !== "." && fileName !== "..";
1095
+ }
1096
+ }
1097
+ class ExportWorkerMessageParser {
1098
+ parseInbound(value) {
1099
+ const record = this.getRecord(value);
1100
+ if (record === void 0) {
1101
+ return void 0;
1102
+ }
1103
+ switch (record["type"]) {
1104
+ case "acknowledge-chunk":
1105
+ return this.parseAcknowledgement(record);
1106
+ case "cancel":
1107
+ return this.parseCancellation(record);
1108
+ case "start":
1109
+ return this.parseStart(record);
1110
+ default:
1111
+ return void 0;
1112
+ }
1113
+ }
1114
+ parseOutbound(value) {
1115
+ const record = this.getRecord(value);
1116
+ if (record === void 0) {
1117
+ return void 0;
1118
+ }
1119
+ const taskId = record["taskId"];
1120
+ if (!this.isPositiveSafeInteger(taskId)) {
1121
+ return void 0;
1122
+ }
1123
+ switch (record["type"]) {
1124
+ case "cancelled":
1125
+ return {
1126
+ taskId,
1127
+ type: "cancelled"
1128
+ };
1129
+ case "complete":
1130
+ return {
1131
+ taskId,
1132
+ type: "complete"
1133
+ };
1134
+ case "error":
1135
+ return typeof record["message"] === "string" ? {
1136
+ message: record["message"],
1137
+ taskId,
1138
+ type: "error"
1139
+ } : void 0;
1140
+ case "progress":
1141
+ return this.parseProgress(record, taskId);
1142
+ case "zip-chunk":
1143
+ return this.parseZipChunk(record, taskId);
1144
+ default:
1145
+ return void 0;
1146
+ }
1147
+ }
1148
+ getRecord(value) {
1149
+ return this.isRecord(value) ? value : void 0;
1150
+ }
1151
+ isPositiveSafeInteger(value) {
1152
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
1153
+ }
1154
+ parseAcknowledgement(record) {
1155
+ const chunkId = record["chunkId"];
1156
+ const taskId = record["taskId"];
1157
+ if (!this.isPositiveSafeInteger(chunkId) || !this.isPositiveSafeInteger(taskId)) {
1158
+ return void 0;
1159
+ }
1160
+ return {
1161
+ chunkId,
1162
+ taskId,
1163
+ type: "acknowledge-chunk"
1164
+ };
1165
+ }
1166
+ parseCancellation(record) {
1167
+ const taskId = record["taskId"];
1168
+ if (!this.isPositiveSafeInteger(taskId)) {
1169
+ return void 0;
1170
+ }
1171
+ return {
1172
+ taskId,
1173
+ type: "cancel"
1174
+ };
1175
+ }
1176
+ parseProgress(record, taskId) {
1177
+ const completedFileCount = record["completedFileCount"];
1178
+ const totalFileCount = record["totalFileCount"];
1179
+ if (!this.isNonNegativeSafeInteger(completedFileCount) || !this.isPositiveSafeInteger(totalFileCount) || completedFileCount > totalFileCount) {
1180
+ return void 0;
1181
+ }
1182
+ return {
1183
+ completedFileCount,
1184
+ taskId,
1185
+ totalFileCount,
1186
+ type: "progress"
1187
+ };
1188
+ }
1189
+ parseStart(record) {
1190
+ const job = this.parseJob(record["job"]);
1191
+ if (job === void 0) {
1192
+ return void 0;
1193
+ }
1194
+ return {
1195
+ job,
1196
+ type: "start"
1197
+ };
1198
+ }
1199
+ parseZipChunk(record, taskId) {
1200
+ const chunk = record["chunk"];
1201
+ const chunkId = record["chunkId"];
1202
+ if (!(chunk instanceof ArrayBuffer) || !this.isPositiveSafeInteger(chunkId)) {
1203
+ return void 0;
1204
+ }
1205
+ return {
1206
+ chunk,
1207
+ chunkId,
1208
+ taskId,
1209
+ type: "zip-chunk"
1210
+ };
1211
+ }
1212
+ isModelFormat(value) {
1213
+ return value === "obj" || value === "ply";
1214
+ }
1215
+ isModelRole(value) {
1216
+ return value === "stone" || value === "base";
1217
+ }
1218
+ isRecord(value) {
1219
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1220
+ }
1221
+ isUnknownArray(value) {
1222
+ return Array.isArray(value);
1223
+ }
1224
+ parseFile(value) {
1225
+ const record = this.getRecord(value);
1226
+ if (record === void 0 || typeof record["fileName"] !== "string" || typeof record["isModel"] !== "boolean" || typeof record["url"] !== "string") {
1227
+ return void 0;
1228
+ }
1229
+ return {
1230
+ fileName: record["fileName"],
1231
+ isModel: record["isModel"],
1232
+ url: record["url"]
1233
+ };
1234
+ }
1235
+ parseFiles(value) {
1236
+ if (!this.isUnknownArray(value)) {
1237
+ return void 0;
1238
+ }
1239
+ const files = [];
1240
+ for (const item of value) {
1241
+ const file = this.parseFile(item);
1242
+ if (file === void 0) {
1243
+ return void 0;
1244
+ }
1245
+ files.push(file);
1246
+ }
1247
+ return files;
1248
+ }
1249
+ parseJob(value) {
1250
+ const record = this.getRecord(value);
1251
+ const taskId = record?.["taskId"];
1252
+ const roles = this.parseRoles(record?.["roles"]);
1253
+ if (!this.isPositiveSafeInteger(taskId) || roles === void 0) {
1254
+ return void 0;
1255
+ }
1256
+ return {
1257
+ roles,
1258
+ taskId
1259
+ };
1260
+ }
1261
+ parseMatrix(value) {
1262
+ if (!this.isUnknownArray(value)) {
1263
+ return void 0;
1264
+ }
1265
+ const matrix = [];
1266
+ for (const item of value) {
1267
+ if (typeof item !== "number") {
1268
+ return void 0;
1269
+ }
1270
+ matrix.push(item);
1271
+ }
1272
+ return matrix;
1273
+ }
1274
+ parseRole(value) {
1275
+ const record = this.getRecord(value);
1276
+ if (record === void 0) {
1277
+ return void 0;
1278
+ }
1279
+ const files = this.parseFiles(record["files"]);
1280
+ const format = record["format"];
1281
+ const matrix = this.parseMatrix(record["matrix"]);
1282
+ const role = record["role"];
1283
+ if (files === void 0 || !this.isModelFormat(format) || matrix === void 0 || !this.isModelRole(role)) {
1284
+ return void 0;
1285
+ }
1286
+ if (record["kind"] === "generated-obj") {
1287
+ return this.parseGeneratedRole(record, files, matrix);
1288
+ }
1289
+ if (record["kind"] !== void 0 && record["kind"] !== "original") {
1290
+ return void 0;
1291
+ }
1292
+ return {
1293
+ files,
1294
+ format,
1295
+ matrix,
1296
+ role
1297
+ };
1298
+ }
1299
+ parseGeneratedRole(record, files, matrix) {
1300
+ const generated = this.parseGeneratedSnapshot(record["generated"]);
1301
+ const materialFileName = record["materialFileName"];
1302
+ const modelFileName = record["modelFileName"];
1303
+ const role = record["role"];
1304
+ const sourceFormat = record["sourceFormat"];
1305
+ if (generated === void 0 || typeof materialFileName !== "string" || typeof modelFileName !== "string" || role !== "base" || sourceFormat !== "obj" && sourceFormat !== "ply") {
1306
+ return void 0;
1307
+ }
1308
+ return {
1309
+ files,
1310
+ format: "obj",
1311
+ generated,
1312
+ kind: "generated-obj",
1313
+ materialFileName,
1314
+ matrix,
1315
+ modelFileName,
1316
+ role: "base",
1317
+ sourceFormat
1318
+ };
1319
+ }
1320
+ parseGeneratedSnapshot(value) {
1321
+ const record = this.getRecord(value);
1322
+ const groups = this.parseGeneratedGroups(record?.["groups"]);
1323
+ const indices = record?.["indices"];
1324
+ const materials = this.parseGeneratedMaterials(record?.["materials"]);
1325
+ const normals = record?.["normals"];
1326
+ const positions = record?.["positions"];
1327
+ const revision = record?.["revision"];
1328
+ const sourceRevision = record?.["sourceRevision"];
1329
+ const uvs = record?.["uvs"];
1330
+ if (groups === void 0 || !(indices instanceof Uint32Array) || materials === void 0 || !(normals instanceof Float32Array) || !(positions instanceof Float32Array) || !this.isPositiveSafeInteger(revision) || !this.isPositiveSafeInteger(sourceRevision) || !(uvs instanceof Float32Array)) {
1331
+ return void 0;
1332
+ }
1333
+ return {
1334
+ groups,
1335
+ indices,
1336
+ materials,
1337
+ normals,
1338
+ positions,
1339
+ revision,
1340
+ sourceRevision,
1341
+ uvs
1342
+ };
1343
+ }
1344
+ parseGeneratedGroups(value) {
1345
+ if (!this.isUnknownArray(value)) {
1346
+ return void 0;
1347
+ }
1348
+ const groups = [];
1349
+ for (const item of value) {
1350
+ const record = this.getRecord(item);
1351
+ const count = record?.["count"];
1352
+ const materialIndex = record?.["materialIndex"];
1353
+ const start = record?.["start"];
1354
+ if (!this.isNonNegativeSafeInteger(count) || !this.isNonNegativeSafeInteger(materialIndex) || !this.isNonNegativeSafeInteger(start)) {
1355
+ return void 0;
1356
+ }
1357
+ groups.push({ count, materialIndex, start });
1358
+ }
1359
+ return groups;
1360
+ }
1361
+ parseGeneratedMaterials(value) {
1362
+ if (!this.isUnknownArray(value)) {
1363
+ return void 0;
1364
+ }
1365
+ const materials = [];
1366
+ for (const item of value) {
1367
+ const record = this.getRecord(item);
1368
+ const color = this.parseColor(record?.["color"]);
1369
+ const kind = record?.["kind"];
1370
+ const name = record?.["name"];
1371
+ const opacity = record?.["opacity"];
1372
+ if (color === void 0 || typeof name !== "string" || typeof opacity !== "number") {
1373
+ return void 0;
1374
+ }
1375
+ if (kind === "boolean-cut" && name === "lingbi_boolean_cut") {
1376
+ materials.push({ color, kind, name, opacity });
1377
+ continue;
1378
+ }
1379
+ const sourceName = record?.["sourceName"];
1380
+ const textured = record?.["textured"];
1381
+ if (kind !== "source" || typeof sourceName !== "string" || typeof textured !== "boolean") {
1382
+ return void 0;
1383
+ }
1384
+ materials.push({ color, kind, name, opacity, sourceName, textured });
1385
+ }
1386
+ return materials;
1387
+ }
1388
+ parseColor(value) {
1389
+ if (!this.isUnknownArray(value) || value.length !== 3) {
1390
+ return void 0;
1391
+ }
1392
+ const [red, green, blue] = value;
1393
+ return typeof red === "number" && typeof green === "number" && typeof blue === "number" && Number.isFinite(red) && Number.isFinite(green) && Number.isFinite(blue) ? [red, green, blue] : void 0;
1394
+ }
1395
+ parseRoles(value) {
1396
+ if (!this.isUnknownArray(value)) {
1397
+ return void 0;
1398
+ }
1399
+ const roles = [];
1400
+ for (const item of value) {
1401
+ const role = this.parseRole(item);
1402
+ if (role === void 0) {
1403
+ return void 0;
1404
+ }
1405
+ roles.push(role);
1406
+ }
1407
+ return roles;
1408
+ }
1409
+ isNonNegativeSafeInteger(value) {
1410
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1411
+ }
1412
+ }
1413
+ class ExportWorkerTask {
1414
+ abortController = new AbortController();
1415
+ awaitingAcknowledgement;
1416
+ cancelled = false;
1417
+ nextChunkId = 1;
1418
+ queuedZipChunks = [];
1419
+ request;
1420
+ zip;
1421
+ constructor(request) {
1422
+ this.request = request;
1423
+ }
1424
+ cancel() {
1425
+ if (this.cancelled) {
1426
+ return;
1427
+ }
1428
+ this.cancelled = true;
1429
+ this.abortController.abort();
1430
+ this.awaitingAcknowledgement?.cancel();
1431
+ this.zip?.terminate();
1432
+ this.queuedZipChunks.length = 0;
1433
+ }
1434
+ }
1435
+ const GENERATED_TEXT_CHUNK_SIZE = 65536;
1436
+ class GeneratedTextChunkWriter {
1437
+ chunks = [];
1438
+ current = new Uint8Array(GENERATED_TEXT_CHUNK_SIZE);
1439
+ currentLength = 0;
1440
+ encoder = new TextEncoder();
1441
+ appendLine(value) {
1442
+ this.append(this.encoder.encode(`${value}
1443
+ `));
1444
+ }
1445
+ toChunks() {
1446
+ const chunks = [...this.chunks];
1447
+ if (this.currentLength > 0) {
1448
+ chunks.push(this.current.slice(0, this.currentLength));
1449
+ }
1450
+ return chunks;
1451
+ }
1452
+ append(value) {
1453
+ let offset = 0;
1454
+ while (offset < value.byteLength) {
1455
+ const available = this.current.byteLength - this.currentLength;
1456
+ const length = Math.min(available, value.byteLength - offset);
1457
+ this.current.set(value.subarray(offset, offset + length), this.currentLength);
1458
+ this.currentLength += length;
1459
+ offset += length;
1460
+ if (this.currentLength === this.current.byteLength) {
1461
+ this.chunks.push(this.current);
1462
+ this.current = new Uint8Array(GENERATED_TEXT_CHUNK_SIZE);
1463
+ this.currentLength = 0;
1464
+ }
1465
+ }
1466
+ }
1467
+ }
1468
+ class GeneratedMtlWriter {
1469
+ createChunks(snapshot, sourceTexts) {
1470
+ const definitions = this.collectDefinitions(sourceTexts);
1471
+ const writer = new GeneratedTextChunkWriter();
1472
+ writer.appendLine("# Lingbi Boolean base materials");
1473
+ for (const material of snapshot.materials) {
1474
+ this.writeMaterial(writer, material, definitions);
1475
+ }
1476
+ return writer.toChunks();
1477
+ }
1478
+ collectDefinitions(sourceTexts) {
1479
+ const definitions = /* @__PURE__ */ new Map();
1480
+ for (const sourceText of sourceTexts) {
1481
+ let currentName;
1482
+ let currentLines = [];
1483
+ for (const line of sourceText.split(/\r?\n/u)) {
1484
+ const match = /^\s*newmtl\s+(.+?)\s*$/iu.exec(line);
1485
+ if (match !== null) {
1486
+ this.addDefinition(definitions, currentName, currentLines);
1487
+ currentName = match[1];
1488
+ currentLines = [line];
1489
+ } else if (currentName !== void 0) {
1490
+ currentLines.push(line);
1491
+ }
1492
+ }
1493
+ this.addDefinition(definitions, currentName, currentLines);
1494
+ }
1495
+ return definitions;
1496
+ }
1497
+ addDefinition(definitions, name, lines) {
1498
+ if (name === void 0 || lines.length === 0) {
1499
+ return;
1500
+ }
1501
+ const existing = definitions.get(name) ?? [];
1502
+ existing.push({ lines });
1503
+ definitions.set(name, existing);
1504
+ }
1505
+ writeMaterial(writer, material, definitions) {
1506
+ if (material.kind === "boolean-cut") {
1507
+ this.writeMinimalMaterial(writer, material);
1508
+ return;
1509
+ }
1510
+ const matches = definitions.get(material.sourceName) ?? [];
1511
+ if (matches.length > 1) {
1512
+ throw new ModelExportError(
1513
+ ModelExportErrorCode.UnsupportedModel,
1514
+ `The source material ${material.sourceName} is defined by multiple MTL files.`
1515
+ );
1516
+ }
1517
+ const definition = matches[0];
1518
+ if (definition === void 0) {
1519
+ if (material.textured) {
1520
+ throw new ModelExportError(
1521
+ ModelExportErrorCode.UnsupportedModel,
1522
+ `The textured source material ${material.sourceName} has no unique MTL definition.`
1523
+ );
1524
+ }
1525
+ this.writeMinimalMaterial(writer, material);
1526
+ return;
1527
+ }
1528
+ for (let index = 0; index < definition.lines.length; index += 1) {
1529
+ const line = definition.lines[index];
1530
+ if (line === void 0) {
1531
+ continue;
1532
+ }
1533
+ writer.appendLine(index === 0 ? `newmtl ${material.name}` : line);
1534
+ }
1535
+ }
1536
+ writeMinimalMaterial(writer, material) {
1537
+ writer.appendLine(`newmtl ${material.name}`);
1538
+ writer.appendLine(
1539
+ `Kd ${this.formatNumber(material.color[0])} ${this.formatNumber(material.color[1])} ${this.formatNumber(material.color[2])}`
1540
+ );
1541
+ writer.appendLine(`d ${this.formatNumber(material.opacity)}`);
1542
+ writer.appendLine("Ns 20");
1543
+ writer.appendLine("illum 2");
1544
+ }
1545
+ formatNumber(value) {
1546
+ return String(value);
1547
+ }
1548
+ }
1549
+ class MatrixTransformer {
1550
+ inverseTranspose00;
1551
+ inverseTranspose01;
1552
+ inverseTranspose02;
1553
+ inverseTranspose10;
1554
+ inverseTranspose11;
1555
+ inverseTranspose12;
1556
+ inverseTranspose20;
1557
+ inverseTranspose21;
1558
+ inverseTranspose22;
1559
+ matrix;
1560
+ constructor(matrix) {
1561
+ this.assertAffineMatrix(matrix);
1562
+ this.matrix = matrix;
1563
+ const inverseTranspose = this.createInverseTranspose(matrix);
1564
+ this.inverseTranspose00 = this.getMatrixValue(inverseTranspose, 0);
1565
+ this.inverseTranspose01 = this.getMatrixValue(inverseTranspose, 1);
1566
+ this.inverseTranspose02 = this.getMatrixValue(inverseTranspose, 2);
1567
+ this.inverseTranspose10 = this.getMatrixValue(inverseTranspose, 3);
1568
+ this.inverseTranspose11 = this.getMatrixValue(inverseTranspose, 4);
1569
+ this.inverseTranspose12 = this.getMatrixValue(inverseTranspose, 5);
1570
+ this.inverseTranspose20 = this.getMatrixValue(inverseTranspose, 6);
1571
+ this.inverseTranspose21 = this.getMatrixValue(inverseTranspose, 7);
1572
+ this.inverseTranspose22 = this.getMatrixValue(inverseTranspose, 8);
1573
+ }
1574
+ transformNormal(x2, y, z) {
1575
+ this.assertFiniteVector(x2, y, z, "normal");
1576
+ const transformedX = this.inverseTranspose00 * x2 + this.inverseTranspose01 * y + this.inverseTranspose02 * z;
1577
+ const transformedY = this.inverseTranspose10 * x2 + this.inverseTranspose11 * y + this.inverseTranspose12 * z;
1578
+ const transformedZ = this.inverseTranspose20 * x2 + this.inverseTranspose21 * y + this.inverseTranspose22 * z;
1579
+ const length = Math.hypot(transformedX, transformedY, transformedZ);
1580
+ if (!Number.isFinite(length) || length === 0) {
1581
+ throw new ModelExportError(
1582
+ ModelExportErrorCode.InvalidModel,
1583
+ "A model normal cannot be normalized after its world transform."
1584
+ );
1585
+ }
1586
+ return [
1587
+ this.assertFiniteResult(transformedX / length, "normal"),
1588
+ this.assertFiniteResult(transformedY / length, "normal"),
1589
+ this.assertFiniteResult(transformedZ / length, "normal")
1590
+ ];
1591
+ }
1592
+ transformPosition(x2, y, z) {
1593
+ this.assertFiniteVector(x2, y, z, "position");
1594
+ const matrix = this.matrix;
1595
+ return [
1596
+ this.assertFiniteResult(
1597
+ this.getMatrixValue(matrix, 0) * x2 + this.getMatrixValue(matrix, 4) * y + this.getMatrixValue(matrix, 8) * z + this.getMatrixValue(matrix, 12),
1598
+ "position"
1599
+ ),
1600
+ this.assertFiniteResult(
1601
+ this.getMatrixValue(matrix, 1) * x2 + this.getMatrixValue(matrix, 5) * y + this.getMatrixValue(matrix, 9) * z + this.getMatrixValue(matrix, 13),
1602
+ "position"
1603
+ ),
1604
+ this.assertFiniteResult(
1605
+ this.getMatrixValue(matrix, 2) * x2 + this.getMatrixValue(matrix, 6) * y + this.getMatrixValue(matrix, 10) * z + this.getMatrixValue(matrix, 14),
1606
+ "position"
1607
+ )
1608
+ ];
1609
+ }
1610
+ assertAffineMatrix(matrix) {
1611
+ if (matrix.length !== 16 || !matrix.every(Number.isFinite)) {
1612
+ throw new ModelExportError(
1613
+ ModelExportErrorCode.InvalidJob,
1614
+ "The world matrix must contain sixteen finite values."
1615
+ );
1616
+ }
1617
+ if (this.getMatrixValue(matrix, 3) !== 0 || this.getMatrixValue(matrix, 7) !== 0 || this.getMatrixValue(matrix, 11) !== 0 || this.getMatrixValue(matrix, 15) !== 1) {
1618
+ throw new ModelExportError(
1619
+ ModelExportErrorCode.InvalidJob,
1620
+ "The world matrix must be affine."
1621
+ );
1622
+ }
1623
+ }
1624
+ assertFiniteResult(value, kind) {
1625
+ if (!Number.isFinite(value)) {
1626
+ throw new ModelExportError(
1627
+ ModelExportErrorCode.InvalidModel,
1628
+ `The transformed ${kind} is not finite.`
1629
+ );
1630
+ }
1631
+ return value;
1632
+ }
1633
+ assertFiniteVector(x2, y, z, kind) {
1634
+ if (!Number.isFinite(x2) || !Number.isFinite(y) || !Number.isFinite(z)) {
1635
+ throw new ModelExportError(
1636
+ ModelExportErrorCode.InvalidModel,
1637
+ `The model ${kind} contains a non-finite value.`
1638
+ );
1639
+ }
1640
+ }
1641
+ createInverseTranspose(matrix) {
1642
+ const a00 = this.getMatrixValue(matrix, 0);
1643
+ const a01 = this.getMatrixValue(matrix, 4);
1644
+ const a02 = this.getMatrixValue(matrix, 8);
1645
+ const a10 = this.getMatrixValue(matrix, 1);
1646
+ const a11 = this.getMatrixValue(matrix, 5);
1647
+ const a12 = this.getMatrixValue(matrix, 9);
1648
+ const a20 = this.getMatrixValue(matrix, 2);
1649
+ const a21 = this.getMatrixValue(matrix, 6);
1650
+ const a22 = this.getMatrixValue(matrix, 10);
1651
+ const cofactor00 = a11 * a22 - a12 * a21;
1652
+ const cofactor01 = a12 * a20 - a10 * a22;
1653
+ const cofactor02 = a10 * a21 - a11 * a20;
1654
+ const cofactor10 = a02 * a21 - a01 * a22;
1655
+ const cofactor11 = a00 * a22 - a02 * a20;
1656
+ const cofactor12 = a01 * a20 - a00 * a21;
1657
+ const cofactor20 = a01 * a12 - a02 * a11;
1658
+ const cofactor21 = a02 * a10 - a00 * a12;
1659
+ const cofactor22 = a00 * a11 - a01 * a10;
1660
+ const determinant = a00 * cofactor00 + a01 * cofactor01 + a02 * cofactor02;
1661
+ if (!Number.isFinite(determinant) || determinant === 0) {
1662
+ throw new ModelExportError(
1663
+ ModelExportErrorCode.InvalidJob,
1664
+ "The world matrix cannot transform normals because its scale is singular."
1665
+ );
1666
+ }
1667
+ const reciprocalDeterminant = 1 / determinant;
1668
+ return [
1669
+ cofactor00 * reciprocalDeterminant,
1670
+ cofactor01 * reciprocalDeterminant,
1671
+ cofactor02 * reciprocalDeterminant,
1672
+ cofactor10 * reciprocalDeterminant,
1673
+ cofactor11 * reciprocalDeterminant,
1674
+ cofactor12 * reciprocalDeterminant,
1675
+ cofactor20 * reciprocalDeterminant,
1676
+ cofactor21 * reciprocalDeterminant,
1677
+ cofactor22 * reciprocalDeterminant
1678
+ ];
1679
+ }
1680
+ getMatrixValue(matrix, index) {
1681
+ const value = matrix[index];
1682
+ if (value === void 0) {
1683
+ throw new ModelExportError(
1684
+ ModelExportErrorCode.InvalidJob,
1685
+ "The world matrix does not contain all sixteen values."
1686
+ );
1687
+ }
1688
+ return value;
1689
+ }
1690
+ }
1691
+ class GeneratedObjModelBaker {
1692
+ createChunks(snapshot, matrix, materialFileName) {
1693
+ const transformer = new MatrixTransformer(matrix);
1694
+ const writer = new GeneratedTextChunkWriter();
1695
+ writer.appendLine(`# Lingbi Boolean base export`);
1696
+ writer.appendLine(`mtllib ${materialFileName}`);
1697
+ writer.appendLine("o base");
1698
+ this.writePositions(writer, snapshot, transformer);
1699
+ this.writeUvs(writer, snapshot);
1700
+ this.writeNormals(writer, snapshot, transformer);
1701
+ this.writeFaces(writer, snapshot);
1702
+ return writer.toChunks();
1703
+ }
1704
+ writeFaces(writer, snapshot) {
1705
+ for (const group of snapshot.groups) {
1706
+ const material = snapshot.materials[group.materialIndex];
1707
+ if (material === void 0) {
1708
+ throw new Error("The generated Boolean material group is incomplete.");
1709
+ }
1710
+ writer.appendLine(`usemtl ${material.name}`);
1711
+ for (let offset = group.start; offset < group.start + group.count; offset += 3) {
1712
+ const first = snapshot.indices[offset];
1713
+ const second = snapshot.indices[offset + 1];
1714
+ const third = snapshot.indices[offset + 2];
1715
+ if (first === void 0 || second === void 0 || third === void 0) {
1716
+ throw new Error("The generated Boolean face index is incomplete.");
1717
+ }
1718
+ writer.appendLine(
1719
+ `f ${this.formatIndex(first)}/${this.formatIndex(first)}/${this.formatIndex(first)} ${this.formatIndex(second)}/${this.formatIndex(second)}/${this.formatIndex(second)} ${this.formatIndex(third)}/${this.formatIndex(third)}/${this.formatIndex(third)}`
1720
+ );
1721
+ }
1722
+ }
1723
+ }
1724
+ writeNormals(writer, snapshot, transformer) {
1725
+ for (let index = 0; index < snapshot.normals.length; index += 3) {
1726
+ const normal = transformer.transformNormal(
1727
+ this.getValue(snapshot.normals, index),
1728
+ this.getValue(snapshot.normals, index + 1),
1729
+ this.getValue(snapshot.normals, index + 2)
1730
+ );
1731
+ writer.appendLine(
1732
+ `vn ${this.formatNumber(normal[0])} ${this.formatNumber(normal[1])} ${this.formatNumber(normal[2])}`
1733
+ );
1734
+ }
1735
+ }
1736
+ writePositions(writer, snapshot, transformer) {
1737
+ for (let index = 0; index < snapshot.positions.length; index += 3) {
1738
+ const position = transformer.transformPosition(
1739
+ this.getValue(snapshot.positions, index),
1740
+ this.getValue(snapshot.positions, index + 1),
1741
+ this.getValue(snapshot.positions, index + 2)
1742
+ );
1743
+ writer.appendLine(
1744
+ `v ${this.formatNumber(position[0])} ${this.formatNumber(position[1])} ${this.formatNumber(position[2])}`
1745
+ );
1746
+ }
1747
+ }
1748
+ writeUvs(writer, snapshot) {
1749
+ for (let index = 0; index < snapshot.uvs.length; index += 2) {
1750
+ writer.appendLine(
1751
+ `vt ${this.formatNumber(this.getValue(snapshot.uvs, index))} ${this.formatNumber(this.getValue(snapshot.uvs, index + 1))}`
1752
+ );
1753
+ }
1754
+ }
1755
+ getValue(values, index) {
1756
+ const value = values[index];
1757
+ if (value === void 0 || !Number.isFinite(value)) {
1758
+ throw new Error("The generated Boolean mesh contains an invalid value.");
1759
+ }
1760
+ return value;
1761
+ }
1762
+ formatIndex(value) {
1763
+ return String(value + 1);
1764
+ }
1765
+ formatNumber(value) {
1766
+ return String(value);
1767
+ }
1768
+ }
1769
+ class ByteChunkWriter {
1770
+ buffer;
1771
+ length = 0;
1772
+ constructor(initialCapacity) {
1773
+ this.buffer = new Uint8Array(Math.max(initialCapacity, 1));
1774
+ }
1775
+ append(bytes) {
1776
+ this.ensureCapacity(bytes.byteLength);
1777
+ this.buffer.set(bytes, this.length);
1778
+ this.length += bytes.byteLength;
1779
+ }
1780
+ appendRange(bytes, start, end) {
1781
+ this.append(bytes.subarray(start, end));
1782
+ }
1783
+ hasData() {
1784
+ return this.length > 0;
1785
+ }
1786
+ toBytes() {
1787
+ return this.buffer.slice(0, this.length);
1788
+ }
1789
+ ensureCapacity(additionalLength) {
1790
+ const requiredLength = this.length + additionalLength;
1791
+ if (requiredLength <= this.buffer.byteLength) {
1792
+ return;
1793
+ }
1794
+ const replacement = new Uint8Array(Math.max(requiredLength, this.buffer.byteLength * 2));
1795
+ replacement.set(this.buffer.subarray(0, this.length));
1796
+ this.buffer = replacement;
1797
+ }
1798
+ }
1799
+ class ObjModelBaker {
1800
+ static MAX_LINE_BYTES = 1048576;
1801
+ encoder = new TextEncoder();
1802
+ transformer;
1803
+ pending = new Uint8Array(0);
1804
+ constructor(matrix) {
1805
+ this.transformer = new MatrixTransformer(matrix);
1806
+ }
1807
+ push(chunk, final) {
1808
+ const source = this.combinePending(chunk);
1809
+ const writer = new ByteChunkWriter(source.byteLength);
1810
+ let lineStart = 0;
1811
+ let cursor = 0;
1812
+ while (cursor < source.byteLength) {
1813
+ const lineEnd = this.findLineEnd(source, cursor, final);
1814
+ if (lineEnd === void 0) {
1815
+ break;
1816
+ }
1817
+ this.appendProcessedLine(writer, source.subarray(lineStart, lineEnd));
1818
+ lineStart = lineEnd;
1819
+ cursor = lineEnd;
1820
+ }
1821
+ const trailing = source.subarray(lineStart);
1822
+ if (final && trailing.byteLength > 0) {
1823
+ this.appendProcessedLine(writer, trailing);
1824
+ this.pending = new Uint8Array(0);
1825
+ } else {
1826
+ this.assertLineLength(trailing.byteLength);
1827
+ this.pending = trailing.slice();
1828
+ }
1829
+ if (final && this.pending.byteLength > 0) {
1830
+ throw new ModelExportError(
1831
+ ModelExportErrorCode.InvalidModel,
1832
+ "The OBJ source ended before its final line could be processed."
1833
+ );
1834
+ }
1835
+ return writer.toBytes();
1836
+ }
1837
+ appendProcessedLine(writer, line) {
1838
+ const command = this.findLineCommand(line);
1839
+ if (command === void 0) {
1840
+ writer.append(line);
1841
+ return;
1842
+ }
1843
+ const text = this.decodeLine(line);
1844
+ const fields = this.collectFields(text, command.fieldsStart);
1845
+ if (command.name === "v") {
1846
+ this.appendVertexLine(writer, text, fields);
1847
+ return;
1848
+ }
1849
+ this.appendNormalLine(writer, text, fields);
1850
+ }
1851
+ appendNormalLine(writer, text, fields) {
1852
+ if (fields.length !== 3) {
1853
+ throw new ModelExportError(
1854
+ ModelExportErrorCode.InvalidModel,
1855
+ "An OBJ normal must contain exactly three numeric values."
1856
+ );
1857
+ }
1858
+ const transformed = this.transformer.transformNormal(
1859
+ this.parseFiniteNumber(fields[0], "normal"),
1860
+ this.parseFiniteNumber(fields[1], "normal"),
1861
+ this.parseFiniteNumber(fields[2], "normal")
1862
+ );
1863
+ writer.append(this.replaceFieldValues(text, fields, transformed));
1864
+ }
1865
+ appendVertexLine(writer, text, fields) {
1866
+ if (!this.hasSupportedVertexFieldCount(fields.length)) {
1867
+ throw new ModelExportError(
1868
+ ModelExportErrorCode.UnsupportedModel,
1869
+ "The OBJ vertex uses an ambiguous or unsupported field layout."
1870
+ );
1871
+ }
1872
+ const x2 = this.parseFiniteNumber(fields[0], "position");
1873
+ const y = this.parseFiniteNumber(fields[1], "position");
1874
+ const z = this.parseFiniteNumber(fields[2], "position");
1875
+ if (fields.length === 4) {
1876
+ const w = this.parseFiniteNumber(fields[3], "vertex w");
1877
+ if (w !== 1) {
1878
+ throw new ModelExportError(
1879
+ ModelExportErrorCode.UnsupportedModel,
1880
+ "OBJ vertices with a w value other than 1 cannot be exported safely."
1881
+ );
1882
+ }
1883
+ } else if (fields.length === 6 || fields.length === 7) {
1884
+ this.assertFiniteColorValues(fields);
1885
+ }
1886
+ const transformed = this.transformer.transformPosition(x2, y, z);
1887
+ writer.append(this.replaceFieldValues(text, fields, transformed));
1888
+ }
1889
+ assertFiniteColorValues(fields) {
1890
+ for (let index = 3; index < fields.length; index += 1) {
1891
+ const field = fields[index];
1892
+ if (field === void 0) {
1893
+ throw new ModelExportError(
1894
+ ModelExportErrorCode.InvalidModel,
1895
+ "The OBJ vertex color layout is incomplete."
1896
+ );
1897
+ }
1898
+ this.parseFiniteNumber(field, "vertex color");
1899
+ }
1900
+ }
1901
+ assertLineLength(length) {
1902
+ if (length > ObjModelBaker.MAX_LINE_BYTES) {
1903
+ throw new ModelExportError(
1904
+ ModelExportErrorCode.UnsupportedModel,
1905
+ "The OBJ contains a line too large to process safely."
1906
+ );
1907
+ }
1908
+ }
1909
+ collectFields(text, fieldsStart) {
1910
+ const fields = [];
1911
+ let cursor = fieldsStart;
1912
+ while (cursor < text.length) {
1913
+ cursor = this.skipWhitespace(text, cursor);
1914
+ if (cursor >= text.length || text[cursor] === "#") {
1915
+ break;
1916
+ }
1917
+ const start = cursor;
1918
+ while (cursor < text.length && !this.isWhitespace(text[cursor])) {
1919
+ cursor += 1;
1920
+ }
1921
+ fields.push({
1922
+ end: cursor,
1923
+ start,
1924
+ value: text.slice(start, cursor)
1925
+ });
1926
+ }
1927
+ return fields;
1928
+ }
1929
+ combinePending(chunk) {
1930
+ if (this.pending.byteLength === 0) {
1931
+ return chunk;
1932
+ }
1933
+ const combined = new Uint8Array(this.pending.byteLength + chunk.byteLength);
1934
+ combined.set(this.pending);
1935
+ combined.set(chunk, this.pending.byteLength);
1936
+ this.pending = new Uint8Array(0);
1937
+ return combined;
1938
+ }
1939
+ decodeLine(line) {
1940
+ try {
1941
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(line);
1942
+ } catch {
1943
+ throw new ModelExportError(
1944
+ ModelExportErrorCode.InvalidModel,
1945
+ "The OBJ contains an invalid UTF-8 vertex or normal line."
1946
+ );
1947
+ }
1948
+ }
1949
+ findLineCommand(line) {
1950
+ let cursor = 0;
1951
+ while (cursor < line.byteLength && this.isAsciiWhitespace(line[cursor])) {
1952
+ cursor += 1;
1953
+ }
1954
+ if (line[cursor] !== 118) {
1955
+ return void 0;
1956
+ }
1957
+ const next = line[cursor + 1];
1958
+ if (next === 110 && (line[cursor + 2] === void 0 || this.isAsciiWhitespace(line[cursor + 2]))) {
1959
+ return {
1960
+ fieldsStart: cursor + 2,
1961
+ name: "vn"
1962
+ };
1963
+ }
1964
+ if (next === void 0 || this.isAsciiWhitespace(next)) {
1965
+ return {
1966
+ fieldsStart: cursor + 1,
1967
+ name: "v"
1968
+ };
1969
+ }
1970
+ return void 0;
1971
+ }
1972
+ findLineEnd(source, start, final) {
1973
+ for (let cursor = start; cursor < source.byteLength; cursor += 1) {
1974
+ const byte = source[cursor];
1975
+ if (byte === 10) {
1976
+ return cursor + 1;
1977
+ }
1978
+ if (byte !== 13) {
1979
+ continue;
1980
+ }
1981
+ const next = source[cursor + 1];
1982
+ if (next === void 0 && !final) {
1983
+ return void 0;
1984
+ }
1985
+ return next === 10 ? cursor + 2 : cursor + 1;
1986
+ }
1987
+ return void 0;
1988
+ }
1989
+ hasSupportedVertexFieldCount(length) {
1990
+ return length === 3 || length === 4 || length === 6 || length === 7;
1991
+ }
1992
+ isAsciiWhitespace(byte) {
1993
+ return byte === 9 || byte === 11 || byte === 12 || byte === 13 || byte === 32;
1994
+ }
1995
+ isNumericLiteral(value) {
1996
+ return /^[+-]?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+))(?:[eE][+-]?\d+)?$/u.test(value);
1997
+ }
1998
+ isWhitespace(character) {
1999
+ return character === " " || character === " " || character === "\r" || character === "\n";
2000
+ }
2001
+ parseFiniteNumber(field, kind) {
2002
+ if (field === void 0 || !this.isNumericLiteral(field.value)) {
2003
+ throw new ModelExportError(
2004
+ ModelExportErrorCode.InvalidModel,
2005
+ `The OBJ ${kind} is not a valid finite number.`
2006
+ );
2007
+ }
2008
+ const value = Number(field.value);
2009
+ if (!Number.isFinite(value)) {
2010
+ throw new ModelExportError(
2011
+ ModelExportErrorCode.InvalidModel,
2012
+ `The OBJ ${kind} is not finite.`
2013
+ );
2014
+ }
2015
+ return value;
2016
+ }
2017
+ replaceFieldValues(text, fields, values) {
2018
+ let cursor = 0;
2019
+ let output = "";
2020
+ for (let index = 0; index < values.length; index += 1) {
2021
+ const field = fields[index];
2022
+ const value = values[index];
2023
+ if (field === void 0 || value === void 0) {
2024
+ throw new ModelExportError(
2025
+ ModelExportErrorCode.InvalidModel,
2026
+ "The OBJ model line ended before its position data."
2027
+ );
2028
+ }
2029
+ output += text.slice(cursor, field.start);
2030
+ output += this.formatNumber(value);
2031
+ cursor = field.end;
2032
+ }
2033
+ output += text.slice(cursor);
2034
+ return this.encoder.encode(output);
2035
+ }
2036
+ skipWhitespace(text, cursor) {
2037
+ let next = cursor;
2038
+ while (next < text.length && this.isWhitespace(text[next])) {
2039
+ next += 1;
2040
+ }
2041
+ return next;
2042
+ }
2043
+ formatNumber(value) {
2044
+ return Object.is(value, -0) ? "0" : value.toString();
2045
+ }
2046
+ }
2047
+ class PlyHeaderParser {
2048
+ getScalarDefinition(type) {
2049
+ switch (type) {
2050
+ case "char":
2051
+ case "int8":
2052
+ case "uchar":
2053
+ case "uint8":
2054
+ return {
2055
+ byteLength: 1,
2056
+ floatingPoint: false,
2057
+ integer: true
2058
+ };
2059
+ case "int16":
2060
+ case "short":
2061
+ case "uint16":
2062
+ case "ushort":
2063
+ return {
2064
+ byteLength: 2,
2065
+ floatingPoint: false,
2066
+ integer: true
2067
+ };
2068
+ case "float":
2069
+ case "float32":
2070
+ return {
2071
+ byteLength: 4,
2072
+ floatingPoint: true,
2073
+ integer: false
2074
+ };
2075
+ case "double":
2076
+ case "float64":
2077
+ return {
2078
+ byteLength: 8,
2079
+ floatingPoint: true,
2080
+ integer: false
2081
+ };
2082
+ case "int":
2083
+ case "int32":
2084
+ case "uint":
2085
+ case "uint32":
2086
+ return {
2087
+ byteLength: 4,
2088
+ floatingPoint: false,
2089
+ integer: true
2090
+ };
2091
+ }
2092
+ }
2093
+ parse(headerBytes) {
2094
+ const header = this.decodeHeader(headerBytes);
2095
+ const lines = header.split(/\r\n|\n|\r/u);
2096
+ this.assertMagic(lines[0]);
2097
+ const elements = [];
2098
+ let format;
2099
+ let currentElement;
2100
+ let endHeaderSeen = false;
2101
+ for (let index = 1; index < lines.length; index += 1) {
2102
+ const line = lines[index];
2103
+ if (line === void 0) {
2104
+ throw new ModelExportError(
2105
+ ModelExportErrorCode.InvalidModel,
2106
+ "The PLY header contains an empty directive."
2107
+ );
2108
+ }
2109
+ const trimmed = line.trim();
2110
+ if (trimmed.length === 0 || trimmed.startsWith("comment ") || trimmed.startsWith("obj_info ")) {
2111
+ continue;
2112
+ }
2113
+ if (trimmed === "end_header") {
2114
+ endHeaderSeen = true;
2115
+ break;
2116
+ }
2117
+ if (trimmed.startsWith("format ")) {
2118
+ if (format !== void 0) {
2119
+ throw new ModelExportError(
2120
+ ModelExportErrorCode.InvalidModel,
2121
+ "The PLY header declares its format more than once."
2122
+ );
2123
+ }
2124
+ format = this.parseFormat(trimmed);
2125
+ continue;
2126
+ }
2127
+ if (trimmed.startsWith("element ")) {
2128
+ currentElement = this.parseElement(trimmed);
2129
+ elements.push(currentElement);
2130
+ continue;
2131
+ }
2132
+ if (trimmed.startsWith("property ")) {
2133
+ if (currentElement === void 0) {
2134
+ throw new ModelExportError(
2135
+ ModelExportErrorCode.InvalidModel,
2136
+ "A PLY property appears before its element declaration."
2137
+ );
2138
+ }
2139
+ this.addProperty(currentElement, trimmed);
2140
+ continue;
2141
+ }
2142
+ throw new ModelExportError(
2143
+ ModelExportErrorCode.UnsupportedModel,
2144
+ "The PLY header contains an unsupported directive."
2145
+ );
2146
+ }
2147
+ if (!endHeaderSeen || format === void 0) {
2148
+ throw new ModelExportError(
2149
+ ModelExportErrorCode.InvalidModel,
2150
+ "The PLY header is missing a format or end_header directive."
2151
+ );
2152
+ }
2153
+ const vertexElementIndex = this.findVertexElementIndex(elements);
2154
+ const vertexElement = elements[vertexElementIndex];
2155
+ if (vertexElement === void 0) {
2156
+ throw new ModelExportError(
2157
+ ModelExportErrorCode.InvalidModel,
2158
+ "The PLY header is missing its vertex element."
2159
+ );
2160
+ }
2161
+ return {
2162
+ elements,
2163
+ format,
2164
+ vertexElementIndex,
2165
+ vertexLayout: this.createVertexLayout(vertexElement)
2166
+ };
2167
+ }
2168
+ addProperty(element, directive) {
2169
+ const parts = directive.split(/\s+/u);
2170
+ const kind = parts[1];
2171
+ if (kind === "list") {
2172
+ const countType = this.parseScalarType(parts[2]);
2173
+ const itemType = this.parseScalarType(parts[3]);
2174
+ const name2 = this.requirePart(parts, 4, "list property name");
2175
+ if (parts.length !== 5) {
2176
+ throw new ModelExportError(
2177
+ ModelExportErrorCode.InvalidModel,
2178
+ "A PLY list property must contain exactly five header fields."
2179
+ );
2180
+ }
2181
+ if (!this.getScalarDefinition(countType).integer) {
2182
+ throw new ModelExportError(
2183
+ ModelExportErrorCode.UnsupportedModel,
2184
+ "A PLY list property must use an integer count type."
2185
+ );
2186
+ }
2187
+ element.properties.push({
2188
+ countType,
2189
+ itemType,
2190
+ kind: "list",
2191
+ name: name2
2192
+ });
2193
+ return;
2194
+ }
2195
+ const type = this.parseScalarType(kind);
2196
+ const name = this.requirePart(parts, 2, "scalar property name");
2197
+ if (parts.length !== 3) {
2198
+ throw new ModelExportError(
2199
+ ModelExportErrorCode.InvalidModel,
2200
+ "A PLY scalar property must contain exactly three header fields."
2201
+ );
2202
+ }
2203
+ element.properties.push({
2204
+ kind: "scalar",
2205
+ name,
2206
+ type
2207
+ });
2208
+ }
2209
+ assertMagic(value) {
2210
+ const magic = value?.replace(/^\uFEFF/u, "");
2211
+ if (magic !== "ply") {
2212
+ throw new ModelExportError(
2213
+ ModelExportErrorCode.InvalidModel,
2214
+ "The model is not a valid PLY file."
2215
+ );
2216
+ }
2217
+ }
2218
+ createVertexLayout(element) {
2219
+ if (element.properties.some(this.isListProperty.bind(this))) {
2220
+ throw new ModelExportError(
2221
+ ModelExportErrorCode.UnsupportedModel,
2222
+ "PLY vertex records with list properties cannot be exported safely."
2223
+ );
2224
+ }
2225
+ const fields = this.createVertexFields(element.properties);
2226
+ const position = [
2227
+ this.requireFloatingPointField(fields, ["x", "px", "posx"], "x position"),
2228
+ this.requireFloatingPointField(fields, ["y", "py", "posy"], "y position"),
2229
+ this.requireFloatingPointField(fields, ["z", "pz", "posz"], "z position")
2230
+ ];
2231
+ const normal = this.createNormalFields(fields);
2232
+ return normal === void 0 ? {
2233
+ position,
2234
+ recordByteLength: this.getRecordByteLength(element.properties)
2235
+ } : {
2236
+ normal,
2237
+ position,
2238
+ recordByteLength: this.getRecordByteLength(element.properties)
2239
+ };
2240
+ }
2241
+ createNormalFields(fields) {
2242
+ const x2 = this.findSingleField(fields, ["nx", "normalx"], "x normal");
2243
+ const y = this.findSingleField(fields, ["ny", "normaly"], "y normal");
2244
+ const z = this.findSingleField(fields, ["nz", "normalz"], "z normal");
2245
+ const count = Number(x2 !== void 0) + Number(y !== void 0) + Number(z !== void 0);
2246
+ if (count === 0) {
2247
+ return void 0;
2248
+ }
2249
+ if (count !== 3 || x2 === void 0 || y === void 0 || z === void 0) {
2250
+ throw new ModelExportError(
2251
+ ModelExportErrorCode.UnsupportedModel,
2252
+ "PLY normals must include complete x, y and z fields."
2253
+ );
2254
+ }
2255
+ this.assertFloatingPointField(x2, "x normal");
2256
+ this.assertFloatingPointField(y, "y normal");
2257
+ this.assertFloatingPointField(z, "z normal");
2258
+ return [x2, y, z];
2259
+ }
2260
+ createVertexFields(properties) {
2261
+ const fields = [];
2262
+ let byteOffset = 0;
2263
+ for (let index = 0; index < properties.length; index += 1) {
2264
+ const property = properties[index];
2265
+ if (property?.kind !== "scalar") {
2266
+ throw new ModelExportError(
2267
+ ModelExportErrorCode.UnsupportedModel,
2268
+ "PLY vertex properties must be scalar values."
2269
+ );
2270
+ }
2271
+ fields.push({
2272
+ asciiIndex: index,
2273
+ byteOffset,
2274
+ name: property.name.toLowerCase(),
2275
+ type: property.type
2276
+ });
2277
+ byteOffset += this.getScalarDefinition(property.type).byteLength;
2278
+ }
2279
+ return fields;
2280
+ }
2281
+ decodeHeader(headerBytes) {
2282
+ try {
2283
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(headerBytes);
2284
+ } catch {
2285
+ throw new ModelExportError(
2286
+ ModelExportErrorCode.InvalidModel,
2287
+ "The PLY header is not valid UTF-8 text."
2288
+ );
2289
+ }
2290
+ }
2291
+ findSingleField(fields, aliases, label) {
2292
+ let matching;
2293
+ for (const field of fields) {
2294
+ if (!aliases.includes(field.name)) {
2295
+ continue;
2296
+ }
2297
+ if (matching !== void 0) {
2298
+ throw new ModelExportError(
2299
+ ModelExportErrorCode.UnsupportedModel,
2300
+ `PLY contains ambiguous ${label} fields.`
2301
+ );
2302
+ }
2303
+ matching = field;
2304
+ }
2305
+ return matching;
2306
+ }
2307
+ findVertexElementIndex(elements) {
2308
+ let index = -1;
2309
+ for (let elementIndex = 0; elementIndex < elements.length; elementIndex += 1) {
2310
+ const element = elements[elementIndex];
2311
+ if (element?.name.toLowerCase() === "vertex") {
2312
+ if (index !== -1) {
2313
+ throw new ModelExportError(
2314
+ ModelExportErrorCode.UnsupportedModel,
2315
+ "PLY contains more than one vertex element."
2316
+ );
2317
+ }
2318
+ index = elementIndex;
2319
+ }
2320
+ }
2321
+ if (index === -1) {
2322
+ throw new ModelExportError(
2323
+ ModelExportErrorCode.InvalidModel,
2324
+ "PLY is missing a vertex element."
2325
+ );
2326
+ }
2327
+ return index;
2328
+ }
2329
+ getRecordByteLength(properties) {
2330
+ let byteLength = 0;
2331
+ for (const property of properties) {
2332
+ if (property.kind !== "scalar") {
2333
+ throw new ModelExportError(
2334
+ ModelExportErrorCode.UnsupportedModel,
2335
+ "PLY vertex records with list properties cannot be exported safely."
2336
+ );
2337
+ }
2338
+ byteLength += this.getScalarDefinition(property.type).byteLength;
2339
+ }
2340
+ return byteLength;
2341
+ }
2342
+ isListProperty(property) {
2343
+ return property.kind === "list";
2344
+ }
2345
+ parseElement(directive) {
2346
+ const parts = directive.split(/\s+/u);
2347
+ const name = this.requirePart(parts, 1, "element name");
2348
+ const count = this.parseCount(this.requirePart(parts, 2, "element count"));
2349
+ if (parts.length !== 3) {
2350
+ throw new ModelExportError(
2351
+ ModelExportErrorCode.InvalidModel,
2352
+ "A PLY element must contain exactly three header fields."
2353
+ );
2354
+ }
2355
+ return {
2356
+ count,
2357
+ name,
2358
+ properties: []
2359
+ };
2360
+ }
2361
+ parseCount(value) {
2362
+ if (!/^\d+$/u.test(value)) {
2363
+ throw new ModelExportError(
2364
+ ModelExportErrorCode.InvalidModel,
2365
+ "A PLY element count must be a non-negative integer."
2366
+ );
2367
+ }
2368
+ const count = Number(value);
2369
+ if (!Number.isSafeInteger(count)) {
2370
+ throw new ModelExportError(
2371
+ ModelExportErrorCode.UnsupportedModel,
2372
+ "A PLY element count is too large to process safely."
2373
+ );
2374
+ }
2375
+ return count;
2376
+ }
2377
+ parseFormat(directive) {
2378
+ const parts = directive.split(/\s+/u);
2379
+ const format = this.requirePart(parts, 1, "format");
2380
+ const version = this.requirePart(parts, 2, "format version");
2381
+ if (parts.length !== 3 || version !== "1.0") {
2382
+ throw new ModelExportError(
2383
+ ModelExportErrorCode.UnsupportedModel,
2384
+ "Only PLY format version 1.0 is supported for export."
2385
+ );
2386
+ }
2387
+ if (format === "ascii" || format === "binary_little_endian" || format === "binary_big_endian") {
2388
+ return format;
2389
+ }
2390
+ throw new ModelExportError(
2391
+ ModelExportErrorCode.UnsupportedModel,
2392
+ "The PLY encoding is not supported for export."
2393
+ );
2394
+ }
2395
+ parseScalarType(value) {
2396
+ const normalized = value?.toLowerCase();
2397
+ if (this.isScalarType(normalized)) {
2398
+ return normalized;
2399
+ }
2400
+ throw new ModelExportError(
2401
+ ModelExportErrorCode.UnsupportedModel,
2402
+ "The PLY header uses an unsupported scalar type."
2403
+ );
2404
+ }
2405
+ isScalarType(value) {
2406
+ switch (value) {
2407
+ case "char":
2408
+ case "int8":
2409
+ case "uchar":
2410
+ case "uint8":
2411
+ case "short":
2412
+ case "int16":
2413
+ case "ushort":
2414
+ case "uint16":
2415
+ case "int":
2416
+ case "int32":
2417
+ case "uint":
2418
+ case "uint32":
2419
+ case "float":
2420
+ case "float32":
2421
+ case "double":
2422
+ case "float64":
2423
+ return true;
2424
+ default:
2425
+ return false;
2426
+ }
2427
+ }
2428
+ requireFloatingPointField(fields, aliases, label) {
2429
+ const field = this.findSingleField(fields, aliases, label);
2430
+ if (field === void 0) {
2431
+ throw new ModelExportError(
2432
+ ModelExportErrorCode.UnsupportedModel,
2433
+ `PLY is missing a writable ${label} field.`
2434
+ );
2435
+ }
2436
+ this.assertFloatingPointField(field, label);
2437
+ return field;
2438
+ }
2439
+ requirePart(parts, index, label) {
2440
+ const value = parts[index];
2441
+ if (value === void 0 || value.length === 0) {
2442
+ throw new ModelExportError(
2443
+ ModelExportErrorCode.InvalidModel,
2444
+ `The PLY header is missing its ${label}.`
2445
+ );
2446
+ }
2447
+ return value;
2448
+ }
2449
+ assertFloatingPointField(field, label) {
2450
+ if (!this.getScalarDefinition(field.type).floatingPoint) {
2451
+ throw new ModelExportError(
2452
+ ModelExportErrorCode.UnsupportedModel,
2453
+ `PLY ${label} must be a floating-point scalar.`
2454
+ );
2455
+ }
2456
+ }
2457
+ }
2458
+ class PlyModelBaker {
2459
+ static MAX_BINARY_RECORD_BYTES = 33554432;
2460
+ static MAX_HEADER_BYTES = 65536;
2461
+ static MAX_LINE_BYTES = 1048576;
2462
+ completed = false;
2463
+ elementIndex = 0;
2464
+ encoder = new TextEncoder();
2465
+ headerParser = new PlyHeaderParser();
2466
+ pending = new Uint8Array(0);
2467
+ remainingElementRecords = 0;
2468
+ schema;
2469
+ transformer;
2470
+ constructor(matrix) {
2471
+ this.transformer = new MatrixTransformer(matrix);
2472
+ }
2473
+ push(chunk, final) {
2474
+ const writer = new ByteChunkWriter(chunk.byteLength + this.pending.byteLength);
2475
+ if (this.schema === void 0) {
2476
+ this.pushHeaderAndInitialBody(writer, chunk, final);
2477
+ } else {
2478
+ writer.append(this.pushBody(chunk, final));
2479
+ }
2480
+ if (final) {
2481
+ this.assertCompleteSource();
2482
+ }
2483
+ return writer.toBytes();
2484
+ }
2485
+ appendAsciiRecord(writer, line) {
2486
+ const element = this.getCurrentElement();
2487
+ if (element === void 0) {
2488
+ writer.append(line);
2489
+ return;
2490
+ }
2491
+ if (this.elementIndex === this.getSchema().vertexElementIndex) {
2492
+ writer.append(this.transformAsciiVertex(line));
2493
+ } else {
2494
+ writer.append(line);
2495
+ }
2496
+ this.consumeRecord();
2497
+ }
2498
+ appendBinaryRecords(source, final) {
2499
+ const data = this.combinePending(source);
2500
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
2501
+ let cursor = 0;
2502
+ while (cursor < data.byteLength && !this.completed) {
2503
+ const element = this.getCurrentElement();
2504
+ if (element === void 0) {
2505
+ break;
2506
+ }
2507
+ const recordByteLength = this.getBinaryRecordByteLength(
2508
+ view,
2509
+ data.byteLength,
2510
+ cursor,
2511
+ element
2512
+ );
2513
+ if (recordByteLength === void 0) {
2514
+ break;
2515
+ }
2516
+ if (this.elementIndex === this.getSchema().vertexElementIndex) {
2517
+ this.transformBinaryVertex(view, cursor, this.getSchema().vertexLayout);
2518
+ }
2519
+ cursor += recordByteLength;
2520
+ this.consumeRecord();
2521
+ }
2522
+ if (this.completed) {
2523
+ this.pending = new Uint8Array(0);
2524
+ return data;
2525
+ }
2526
+ const trailing = data.subarray(cursor);
2527
+ if (final && trailing.byteLength > 0) {
2528
+ throw new ModelExportError(
2529
+ ModelExportErrorCode.InvalidModel,
2530
+ "The PLY binary data ends during a record."
2531
+ );
2532
+ }
2533
+ this.assertPendingByteLength(trailing.byteLength);
2534
+ this.pending = trailing.slice();
2535
+ return data.subarray(0, cursor);
2536
+ }
2537
+ appendProcessedAsciiLines(source, final) {
2538
+ const data = this.combinePending(source);
2539
+ const writer = new ByteChunkWriter(data.byteLength);
2540
+ let lineStart = 0;
2541
+ let cursor = 0;
2542
+ while (cursor < data.byteLength) {
2543
+ const lineEnd = this.findLineEnd(data, cursor, final);
2544
+ if (lineEnd === void 0) {
2545
+ break;
2546
+ }
2547
+ this.appendAsciiRecord(writer, data.subarray(lineStart, lineEnd));
2548
+ lineStart = lineEnd;
2549
+ cursor = lineEnd;
2550
+ }
2551
+ const trailing = data.subarray(lineStart);
2552
+ if (final && trailing.byteLength > 0) {
2553
+ this.appendAsciiRecord(writer, trailing);
2554
+ this.pending = new Uint8Array(0);
2555
+ } else {
2556
+ this.assertPendingByteLength(trailing.byteLength);
2557
+ this.pending = trailing.slice();
2558
+ }
2559
+ return writer.toBytes();
2560
+ }
2561
+ assertCompleteSource() {
2562
+ if (this.schema === void 0) {
2563
+ throw new ModelExportError(
2564
+ ModelExportErrorCode.InvalidModel,
2565
+ "The PLY source ended before its header was complete."
2566
+ );
2567
+ }
2568
+ if (!this.completed) {
2569
+ throw new ModelExportError(
2570
+ ModelExportErrorCode.InvalidModel,
2571
+ "The PLY source ended before all declared records were available."
2572
+ );
2573
+ }
2574
+ }
2575
+ assertPendingByteLength(length) {
2576
+ const maximum = this.getSchemaOrUndefined()?.format === "ascii" ? PlyModelBaker.MAX_LINE_BYTES : PlyModelBaker.MAX_BINARY_RECORD_BYTES;
2577
+ if (length > maximum) {
2578
+ throw new ModelExportError(
2579
+ ModelExportErrorCode.UnsupportedModel,
2580
+ "The PLY contains a record too large to process safely."
2581
+ );
2582
+ }
2583
+ }
2584
+ combinePending(chunk) {
2585
+ if (this.pending.byteLength === 0) {
2586
+ return chunk;
2587
+ }
2588
+ const combined = new Uint8Array(this.pending.byteLength + chunk.byteLength);
2589
+ combined.set(this.pending);
2590
+ combined.set(chunk, this.pending.byteLength);
2591
+ this.pending = new Uint8Array(0);
2592
+ return combined;
2593
+ }
2594
+ consumeRecord() {
2595
+ this.remainingElementRecords -= 1;
2596
+ if (this.remainingElementRecords < 0) {
2597
+ throw new ModelExportError(
2598
+ ModelExportErrorCode.InvalidModel,
2599
+ "The PLY record stream exceeds its declared element count."
2600
+ );
2601
+ }
2602
+ if (this.remainingElementRecords === 0) {
2603
+ this.elementIndex += 1;
2604
+ this.advanceElement();
2605
+ }
2606
+ }
2607
+ createAsciiFields(text) {
2608
+ const fields = [];
2609
+ let cursor = 0;
2610
+ while (cursor < text.length) {
2611
+ cursor = this.skipWhitespace(text, cursor);
2612
+ if (cursor >= text.length) {
2613
+ break;
2614
+ }
2615
+ const start = cursor;
2616
+ while (cursor < text.length && !this.isWhitespace(text[cursor])) {
2617
+ cursor += 1;
2618
+ }
2619
+ fields.push({
2620
+ end: cursor,
2621
+ start,
2622
+ value: text.slice(start, cursor)
2623
+ });
2624
+ }
2625
+ return fields;
2626
+ }
2627
+ createReplacement(fields, vertexField, value) {
2628
+ const field = fields[vertexField.asciiIndex];
2629
+ if (field === void 0) {
2630
+ throw new ModelExportError(
2631
+ ModelExportErrorCode.InvalidModel,
2632
+ "The PLY vertex record is missing a declared property value."
2633
+ );
2634
+ }
2635
+ return {
2636
+ field,
2637
+ value
2638
+ };
2639
+ }
2640
+ decodeAsciiLine(line) {
2641
+ try {
2642
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(line);
2643
+ } catch {
2644
+ throw new ModelExportError(
2645
+ ModelExportErrorCode.InvalidModel,
2646
+ "The PLY ASCII vertex data is not valid UTF-8 text."
2647
+ );
2648
+ }
2649
+ }
2650
+ findHeaderEnd(source, final) {
2651
+ let lineStart = 0;
2652
+ let cursor = 0;
2653
+ while (cursor < source.byteLength) {
2654
+ const lineEnd = this.findLineEnd(source, cursor, final);
2655
+ if (lineEnd === void 0) {
2656
+ return void 0;
2657
+ }
2658
+ if (this.isEndHeaderLine(source.subarray(lineStart, lineEnd))) {
2659
+ return lineEnd;
2660
+ }
2661
+ lineStart = lineEnd;
2662
+ cursor = lineEnd;
2663
+ }
2664
+ return void 0;
2665
+ }
2666
+ findLineEnd(source, start, final) {
2667
+ for (let cursor = start; cursor < source.byteLength; cursor += 1) {
2668
+ const byte = source[cursor];
2669
+ if (byte === 10) {
2670
+ return cursor + 1;
2671
+ }
2672
+ if (byte !== 13) {
2673
+ continue;
2674
+ }
2675
+ const next = source[cursor + 1];
2676
+ if (next === void 0 && !final) {
2677
+ return void 0;
2678
+ }
2679
+ return next === 10 ? cursor + 2 : cursor + 1;
2680
+ }
2681
+ return void 0;
2682
+ }
2683
+ getBinaryRecordByteLength(view, availableByteLength, start, element) {
2684
+ let cursor = start;
2685
+ for (const property of element.properties) {
2686
+ if (property.kind === "scalar") {
2687
+ const byteLength = this.headerParser.getScalarDefinition(property.type).byteLength;
2688
+ if (cursor + byteLength > availableByteLength) {
2689
+ return void 0;
2690
+ }
2691
+ cursor += byteLength;
2692
+ continue;
2693
+ }
2694
+ const countDefinition = this.headerParser.getScalarDefinition(property.countType);
2695
+ if (cursor + countDefinition.byteLength > availableByteLength) {
2696
+ return void 0;
2697
+ }
2698
+ const count = this.readScalar(view, cursor, property.countType);
2699
+ if (!Number.isSafeInteger(count) || count < 0) {
2700
+ throw new ModelExportError(
2701
+ ModelExportErrorCode.InvalidModel,
2702
+ "A PLY list count must be a non-negative safe integer."
2703
+ );
2704
+ }
2705
+ cursor += countDefinition.byteLength;
2706
+ const itemByteLength = this.headerParser.getScalarDefinition(
2707
+ property.itemType
2708
+ ).byteLength;
2709
+ const listByteLength = count * itemByteLength;
2710
+ if (!Number.isSafeInteger(listByteLength) || listByteLength > PlyModelBaker.MAX_BINARY_RECORD_BYTES) {
2711
+ throw new ModelExportError(
2712
+ ModelExportErrorCode.UnsupportedModel,
2713
+ "A PLY list record is too large to process safely."
2714
+ );
2715
+ }
2716
+ if (cursor + listByteLength > availableByteLength) {
2717
+ return void 0;
2718
+ }
2719
+ cursor += listByteLength;
2720
+ }
2721
+ const recordByteLength = cursor - start;
2722
+ if (recordByteLength > PlyModelBaker.MAX_BINARY_RECORD_BYTES) {
2723
+ throw new ModelExportError(
2724
+ ModelExportErrorCode.UnsupportedModel,
2725
+ "A PLY binary record is too large to process safely."
2726
+ );
2727
+ }
2728
+ return recordByteLength;
2729
+ }
2730
+ getCurrentElement() {
2731
+ const schema = this.getSchema();
2732
+ if (this.completed) {
2733
+ return void 0;
2734
+ }
2735
+ const element = schema.elements[this.elementIndex];
2736
+ if (element === void 0) {
2737
+ throw new ModelExportError(
2738
+ ModelExportErrorCode.InvalidModel,
2739
+ "The PLY element stream does not match its header."
2740
+ );
2741
+ }
2742
+ return element;
2743
+ }
2744
+ getSchema() {
2745
+ const schema = this.schema;
2746
+ if (schema === void 0) {
2747
+ throw new ModelExportError(
2748
+ ModelExportErrorCode.InvalidModel,
2749
+ "The PLY header has not been parsed yet."
2750
+ );
2751
+ }
2752
+ return schema;
2753
+ }
2754
+ getSchemaOrUndefined() {
2755
+ return this.schema;
2756
+ }
2757
+ initializeElements() {
2758
+ this.elementIndex = 0;
2759
+ this.remainingElementRecords = 0;
2760
+ this.completed = false;
2761
+ this.advanceElement();
2762
+ }
2763
+ isEndHeaderLine(line) {
2764
+ try {
2765
+ const text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(line);
2766
+ return text.trim() === "end_header";
2767
+ } catch {
2768
+ throw new ModelExportError(
2769
+ ModelExportErrorCode.InvalidModel,
2770
+ "The PLY header is not valid UTF-8 text."
2771
+ );
2772
+ }
2773
+ }
2774
+ isNumericLiteral(value) {
2775
+ return /^[+-]?(?:(?:\d+(?:\.\d*)?)|(?:\.\d+))(?:[eE][+-]?\d+)?$/u.test(value);
2776
+ }
2777
+ isWhitespace(character) {
2778
+ return character === " " || character === " " || character === "\r" || character === "\n";
2779
+ }
2780
+ parseAsciiFloat(field, label) {
2781
+ if (!this.isNumericLiteral(field.value)) {
2782
+ throw new ModelExportError(
2783
+ ModelExportErrorCode.InvalidModel,
2784
+ `PLY ${label} is not a valid finite number.`
2785
+ );
2786
+ }
2787
+ const value = Number(field.value);
2788
+ if (!Number.isFinite(value)) {
2789
+ throw new ModelExportError(
2790
+ ModelExportErrorCode.InvalidModel,
2791
+ `PLY ${label} is not finite.`
2792
+ );
2793
+ }
2794
+ return value;
2795
+ }
2796
+ pushBody(chunk, final) {
2797
+ return this.getSchema().format === "ascii" ? this.appendProcessedAsciiLines(chunk, final) : this.appendBinaryRecords(chunk, final);
2798
+ }
2799
+ pushHeaderAndInitialBody(writer, chunk, final) {
2800
+ const source = this.combinePending(chunk);
2801
+ const headerEnd = this.findHeaderEnd(source, final);
2802
+ if (headerEnd === void 0) {
2803
+ if (source.byteLength > PlyModelBaker.MAX_HEADER_BYTES) {
2804
+ throw new ModelExportError(
2805
+ ModelExportErrorCode.UnsupportedModel,
2806
+ "The PLY header is too large to process safely."
2807
+ );
2808
+ }
2809
+ this.pending = source.slice();
2810
+ return;
2811
+ }
2812
+ const header = source.subarray(0, headerEnd);
2813
+ this.schema = this.headerParser.parse(header);
2814
+ this.initializeElements();
2815
+ writer.append(header);
2816
+ writer.append(this.pushBody(source.subarray(headerEnd), final));
2817
+ }
2818
+ readScalar(view, offset, type) {
2819
+ const littleEndian = this.getSchema().format === "binary_little_endian";
2820
+ switch (type) {
2821
+ case "char":
2822
+ case "int8":
2823
+ return view.getInt8(offset);
2824
+ case "uchar":
2825
+ case "uint8":
2826
+ return view.getUint8(offset);
2827
+ case "short":
2828
+ case "int16":
2829
+ return view.getInt16(offset, littleEndian);
2830
+ case "ushort":
2831
+ case "uint16":
2832
+ return view.getUint16(offset, littleEndian);
2833
+ case "int":
2834
+ case "int32":
2835
+ return view.getInt32(offset, littleEndian);
2836
+ case "uint":
2837
+ case "uint32":
2838
+ return view.getUint32(offset, littleEndian);
2839
+ case "float":
2840
+ case "float32":
2841
+ return view.getFloat32(offset, littleEndian);
2842
+ case "double":
2843
+ case "float64":
2844
+ return view.getFloat64(offset, littleEndian);
2845
+ }
2846
+ }
2847
+ replaceAsciiValues(text, replacements) {
2848
+ const sortedReplacements = [...replacements].sort(this.compareReplacements.bind(this));
2849
+ let cursor = 0;
2850
+ let output = "";
2851
+ for (const replacement of sortedReplacements) {
2852
+ output += text.slice(cursor, replacement.field.start);
2853
+ output += this.formatNumber(replacement.value);
2854
+ cursor = replacement.field.end;
2855
+ }
2856
+ output += text.slice(cursor);
2857
+ return this.encoder.encode(output);
2858
+ }
2859
+ compareReplacements(left, right) {
2860
+ return left.field.start - right.field.start;
2861
+ }
2862
+ formatNumber(value) {
2863
+ return Object.is(value, -0) ? "0" : value.toString();
2864
+ }
2865
+ skipWhitespace(text, cursor) {
2866
+ let next = cursor;
2867
+ while (next < text.length && this.isWhitespace(text[next])) {
2868
+ next += 1;
2869
+ }
2870
+ return next;
2871
+ }
2872
+ transformAsciiVertex(line) {
2873
+ const text = this.decodeAsciiLine(line);
2874
+ const fields = this.createAsciiFields(text);
2875
+ const layout = this.getSchema().vertexLayout;
2876
+ if (fields.length !== this.getCurrentElementPropertyCount()) {
2877
+ throw new ModelExportError(
2878
+ ModelExportErrorCode.InvalidModel,
2879
+ "A PLY ASCII vertex record does not match its declared properties."
2880
+ );
2881
+ }
2882
+ const replacements = this.createVertexReplacements(fields, layout);
2883
+ return this.replaceAsciiValues(text, replacements);
2884
+ }
2885
+ createVertexReplacements(fields, layout) {
2886
+ const position = layout.position;
2887
+ const transformedPosition = this.transformer.transformPosition(
2888
+ this.parseAsciiFloat(
2889
+ this.requireAsciiField(fields, position[0], "x position"),
2890
+ "x position"
2891
+ ),
2892
+ this.parseAsciiFloat(
2893
+ this.requireAsciiField(fields, position[1], "y position"),
2894
+ "y position"
2895
+ ),
2896
+ this.parseAsciiFloat(
2897
+ this.requireAsciiField(fields, position[2], "z position"),
2898
+ "z position"
2899
+ )
2900
+ );
2901
+ const replacements = [
2902
+ this.createReplacement(fields, position[0], transformedPosition[0]),
2903
+ this.createReplacement(fields, position[1], transformedPosition[1]),
2904
+ this.createReplacement(fields, position[2], transformedPosition[2])
2905
+ ];
2906
+ const normal = layout.normal;
2907
+ if (normal !== void 0) {
2908
+ const transformedNormal = this.transformer.transformNormal(
2909
+ this.parseAsciiFloat(
2910
+ this.requireAsciiField(fields, normal[0], "x normal"),
2911
+ "x normal"
2912
+ ),
2913
+ this.parseAsciiFloat(
2914
+ this.requireAsciiField(fields, normal[1], "y normal"),
2915
+ "y normal"
2916
+ ),
2917
+ this.parseAsciiFloat(
2918
+ this.requireAsciiField(fields, normal[2], "z normal"),
2919
+ "z normal"
2920
+ )
2921
+ );
2922
+ replacements.push(
2923
+ this.createReplacement(fields, normal[0], transformedNormal[0]),
2924
+ this.createReplacement(fields, normal[1], transformedNormal[1]),
2925
+ this.createReplacement(fields, normal[2], transformedNormal[2])
2926
+ );
2927
+ }
2928
+ return replacements;
2929
+ }
2930
+ getCurrentElementPropertyCount() {
2931
+ const element = this.getCurrentElement();
2932
+ if (element === void 0) {
2933
+ throw new ModelExportError(
2934
+ ModelExportErrorCode.InvalidModel,
2935
+ "The PLY vertex element is unavailable."
2936
+ );
2937
+ }
2938
+ return element.properties.length;
2939
+ }
2940
+ requireAsciiField(fields, vertexField, label) {
2941
+ if (vertexField === void 0) {
2942
+ throw new ModelExportError(
2943
+ ModelExportErrorCode.InvalidModel,
2944
+ `The PLY vertex is missing its ${label} field.`
2945
+ );
2946
+ }
2947
+ const field = fields[vertexField.asciiIndex];
2948
+ if (field === void 0) {
2949
+ throw new ModelExportError(
2950
+ ModelExportErrorCode.InvalidModel,
2951
+ `The PLY vertex is missing its ${label} value.`
2952
+ );
2953
+ }
2954
+ return field;
2955
+ }
2956
+ transformBinaryVertex(view, recordOffset, layout) {
2957
+ const position = layout.position;
2958
+ const transformedPosition = this.transformer.transformPosition(
2959
+ this.readRequiredFloat(view, recordOffset, position[0], "x position"),
2960
+ this.readRequiredFloat(view, recordOffset, position[1], "y position"),
2961
+ this.readRequiredFloat(view, recordOffset, position[2], "z position")
2962
+ );
2963
+ this.writeRequiredFloat(
2964
+ view,
2965
+ recordOffset,
2966
+ position[0],
2967
+ transformedPosition[0],
2968
+ "x position"
2969
+ );
2970
+ this.writeRequiredFloat(
2971
+ view,
2972
+ recordOffset,
2973
+ position[1],
2974
+ transformedPosition[1],
2975
+ "y position"
2976
+ );
2977
+ this.writeRequiredFloat(
2978
+ view,
2979
+ recordOffset,
2980
+ position[2],
2981
+ transformedPosition[2],
2982
+ "z position"
2983
+ );
2984
+ const normal = layout.normal;
2985
+ if (normal !== void 0) {
2986
+ const transformedNormal = this.transformer.transformNormal(
2987
+ this.readRequiredFloat(view, recordOffset, normal[0], "x normal"),
2988
+ this.readRequiredFloat(view, recordOffset, normal[1], "y normal"),
2989
+ this.readRequiredFloat(view, recordOffset, normal[2], "z normal")
2990
+ );
2991
+ this.writeRequiredFloat(
2992
+ view,
2993
+ recordOffset,
2994
+ normal[0],
2995
+ transformedNormal[0],
2996
+ "x normal"
2997
+ );
2998
+ this.writeRequiredFloat(
2999
+ view,
3000
+ recordOffset,
3001
+ normal[1],
3002
+ transformedNormal[1],
3003
+ "y normal"
3004
+ );
3005
+ this.writeRequiredFloat(
3006
+ view,
3007
+ recordOffset,
3008
+ normal[2],
3009
+ transformedNormal[2],
3010
+ "z normal"
3011
+ );
3012
+ }
3013
+ }
3014
+ readRequiredFloat(view, recordOffset, field, label) {
3015
+ if (field === void 0) {
3016
+ throw new ModelExportError(
3017
+ ModelExportErrorCode.InvalidModel,
3018
+ `The PLY vertex is missing its ${label} field.`
3019
+ );
3020
+ }
3021
+ const value = this.readScalar(view, recordOffset + field.byteOffset, field.type);
3022
+ if (!Number.isFinite(value)) {
3023
+ throw new ModelExportError(
3024
+ ModelExportErrorCode.InvalidModel,
3025
+ `PLY ${label} is not finite.`
3026
+ );
3027
+ }
3028
+ return value;
3029
+ }
3030
+ writeRequiredFloat(view, recordOffset, field, value, label) {
3031
+ if (field === void 0 || !Number.isFinite(value)) {
3032
+ throw new ModelExportError(
3033
+ ModelExportErrorCode.InvalidModel,
3034
+ `PLY ${label} cannot be written as a finite value.`
3035
+ );
3036
+ }
3037
+ const littleEndian = this.getSchema().format === "binary_little_endian";
3038
+ const offset = recordOffset + field.byteOffset;
3039
+ if (field.type === "float" || field.type === "float32") {
3040
+ view.setFloat32(offset, value, littleEndian);
3041
+ } else if (field.type === "double" || field.type === "float64") {
3042
+ view.setFloat64(offset, value, littleEndian);
3043
+ } else {
3044
+ throw new ModelExportError(
3045
+ ModelExportErrorCode.UnsupportedModel,
3046
+ `PLY ${label} must be a floating-point scalar.`
3047
+ );
3048
+ }
3049
+ if (!Number.isFinite(this.readScalar(view, offset, field.type))) {
3050
+ throw new ModelExportError(
3051
+ ModelExportErrorCode.InvalidModel,
3052
+ `PLY ${label} overflows its scalar type after transformation.`
3053
+ );
3054
+ }
3055
+ }
3056
+ advanceElement() {
3057
+ const elements = this.getSchema().elements;
3058
+ while (this.elementIndex < elements.length) {
3059
+ const element = elements[this.elementIndex];
3060
+ if (element === void 0) {
3061
+ throw new ModelExportError(
3062
+ ModelExportErrorCode.InvalidModel,
3063
+ "The PLY header has an empty element declaration."
3064
+ );
3065
+ }
3066
+ this.remainingElementRecords = element.count;
3067
+ if (this.remainingElementRecords > 0) {
3068
+ return;
3069
+ }
3070
+ this.elementIndex += 1;
3071
+ }
3072
+ this.completed = true;
3073
+ }
3074
+ }
3075
+ class ModelExportWorkerRuntime {
3076
+ activeTask;
3077
+ jobValidator = new ExportJobValidator();
3078
+ latestStartedTaskId = 0;
3079
+ messageParser = new ExportWorkerMessageParser();
3080
+ port;
3081
+ generatedMtlWriter = new GeneratedMtlWriter();
3082
+ generatedObjModelBaker = new GeneratedObjModelBaker();
3083
+ constructor(port) {
3084
+ this.port = port;
3085
+ }
3086
+ attach() {
3087
+ this.port.addEventListener("message", this.handleMessage.bind(this));
3088
+ }
3089
+ acknowledgeChunk(task, chunkId) {
3090
+ const acknowledgement = task.awaitingAcknowledgement;
3091
+ if (acknowledgement?.chunkId === chunkId) {
3092
+ acknowledgement.acknowledge();
3093
+ }
3094
+ }
3095
+ cancelTask(task) {
3096
+ task.cancel();
3097
+ }
3098
+ copyZipChunk(data) {
3099
+ const copy = new Uint8Array(data.byteLength);
3100
+ copy.set(data);
3101
+ return copy.buffer;
3102
+ }
3103
+ createBaker(format, matrix) {
3104
+ return format === "obj" ? new ObjModelBaker(matrix) : new PlyModelBaker(matrix);
3105
+ }
3106
+ createZip(task) {
3107
+ return new Zip(this.handleZipData.bind(this, task));
3108
+ }
3109
+ async drainZipChunks(task) {
3110
+ while (task.queuedZipChunks.length > 0) {
3111
+ this.throwIfCancelled(task);
3112
+ const chunk = task.queuedZipChunks.shift();
3113
+ if (chunk === void 0) {
3114
+ throw new ModelExportError(
3115
+ ModelExportErrorCode.Protocol,
3116
+ "The export ZIP output queue was unexpectedly empty."
3117
+ );
3118
+ }
3119
+ const acknowledgement = new ExportChunkAcknowledgement(
3120
+ task.request.taskId,
3121
+ task.nextChunkId
3122
+ );
3123
+ task.nextChunkId += 1;
3124
+ task.awaitingAcknowledgement = acknowledgement;
3125
+ this.port.postMessage(
3126
+ {
3127
+ chunk,
3128
+ chunkId: acknowledgement.chunkId,
3129
+ taskId: task.request.taskId,
3130
+ type: "zip-chunk"
3131
+ },
3132
+ [chunk]
3133
+ );
3134
+ await acknowledgement.promise;
3135
+ if (task.awaitingAcknowledgement === acknowledgement) {
3136
+ task.awaitingAcknowledgement = void 0;
3137
+ }
3138
+ }
3139
+ }
3140
+ async exportFile(task, format, roleDirectory, file) {
3141
+ this.throwIfCancelled(task);
3142
+ let response;
3143
+ try {
3144
+ response = await fetch(file.url, { signal: task.abortController.signal });
3145
+ } catch {
3146
+ this.throwIfCancelled(task);
3147
+ throw new ModelExportError(
3148
+ ModelExportErrorCode.InvalidSource,
3149
+ `The export source ${file.fileName} could not be fetched.`
3150
+ );
3151
+ }
3152
+ if (!response.ok || response.body === null) {
3153
+ throw new ModelExportError(
3154
+ ModelExportErrorCode.InvalidSource,
3155
+ `The export source ${file.fileName} could not be read as a stream.`
3156
+ );
3157
+ }
3158
+ const entry = this.createZipEntry(`${roleDirectory}/${file.fileName}`, file.isModel);
3159
+ const baker = file.isModel ? this.createBaker(format, this.getRoleMatrix(task, roleDirectory)) : void 0;
3160
+ task.zip?.add(entry);
3161
+ await this.drainZipChunks(task);
3162
+ await this.streamResponseIntoEntry(task, response.body, entry, baker);
3163
+ }
3164
+ async exportGeneratedRole(task, role, completedFileCount) {
3165
+ const sourceMaterialTexts = await this.fetchSourceMaterialTexts(task, role);
3166
+ const objChunks = this.generatedObjModelBaker.createChunks(
3167
+ role.generated,
3168
+ role.matrix,
3169
+ role.materialFileName
3170
+ );
3171
+ const mtlChunks = this.generatedMtlWriter.createChunks(role.generated, sourceMaterialTexts);
3172
+ let completed = completedFileCount;
3173
+ await this.writeGeneratedEntry(task, role.role, role.modelFileName, objChunks);
3174
+ completed += 1;
3175
+ this.postProgress(task, completed);
3176
+ await this.writeGeneratedEntry(task, role.role, role.materialFileName, mtlChunks);
3177
+ completed += 1;
3178
+ this.postProgress(task, completed);
3179
+ for (const file of role.files) {
3180
+ if (file.isModel || this.isMtlFile(file.fileName)) {
3181
+ continue;
3182
+ }
3183
+ await this.exportFile(task, role.sourceFormat, role.role, file);
3184
+ completed += 1;
3185
+ this.postProgress(task, completed);
3186
+ }
3187
+ return completed - completedFileCount;
3188
+ }
3189
+ async fetchSourceMaterialTexts(task, role) {
3190
+ const texts = [];
3191
+ for (const file of role.files) {
3192
+ if (file.isModel || !this.isMtlFile(file.fileName)) {
3193
+ continue;
3194
+ }
3195
+ this.throwIfCancelled(task);
3196
+ let response;
3197
+ try {
3198
+ response = await fetch(file.url, { signal: task.abortController.signal });
3199
+ } catch {
3200
+ this.throwIfCancelled(task);
3201
+ throw new ModelExportError(
3202
+ ModelExportErrorCode.InvalidSource,
3203
+ `The source material ${file.fileName} could not be fetched.`
3204
+ );
3205
+ }
3206
+ if (!response.ok) {
3207
+ throw new ModelExportError(
3208
+ ModelExportErrorCode.InvalidSource,
3209
+ `The source material ${file.fileName} could not be read.`
3210
+ );
3211
+ }
3212
+ try {
3213
+ texts.push(await response.text());
3214
+ } catch {
3215
+ throw new ModelExportError(
3216
+ ModelExportErrorCode.InvalidSource,
3217
+ `The source material ${file.fileName} could not be decoded.`
3218
+ );
3219
+ }
3220
+ }
3221
+ return texts;
3222
+ }
3223
+ async writeGeneratedEntry(task, roleDirectory, fileName, chunks) {
3224
+ const entry = new ZipDeflate(`${roleDirectory}/${fileName}`);
3225
+ task.zip?.add(entry);
3226
+ await this.drainZipChunks(task);
3227
+ for (const chunk of chunks) {
3228
+ this.throwIfCancelled(task);
3229
+ entry.push(chunk, false);
3230
+ await this.drainZipChunks(task);
3231
+ }
3232
+ entry.push(new Uint8Array(0), true);
3233
+ await this.drainZipChunks(task);
3234
+ }
3235
+ createZipEntry(fileName, isModel) {
3236
+ return isModel ? new ZipDeflate(fileName) : new ZipPassThrough(fileName);
3237
+ }
3238
+ finishCurrentTask(task) {
3239
+ if (this.activeTask === task) {
3240
+ this.activeTask = void 0;
3241
+ }
3242
+ }
3243
+ getErrorMessage(error) {
3244
+ return error instanceof ModelExportError ? error.message : "The model export failed. Please retry.";
3245
+ }
3246
+ getFileCount(request) {
3247
+ let count = 0;
3248
+ for (const role of request.roles) {
3249
+ count += role.kind === "generated-obj" ? 2 + role.files.filter((file) => !file.isModel && !this.isMtlFile(file.fileName)).length : role.files.length;
3250
+ }
3251
+ return count;
3252
+ }
3253
+ getPotentialTaskId(job) {
3254
+ return Number.isSafeInteger(job.taskId) && job.taskId > 0 ? job.taskId : void 0;
3255
+ }
3256
+ getRoleMatrix(task, roleDirectory) {
3257
+ const role = task.request.roles.find(this.matchesRoleDirectory.bind(this, roleDirectory));
3258
+ if (role === void 0) {
3259
+ throw new ModelExportError(
3260
+ ModelExportErrorCode.Protocol,
3261
+ "The export model role is missing its world matrix."
3262
+ );
3263
+ }
3264
+ return role.matrix;
3265
+ }
3266
+ handleCancellation(taskId) {
3267
+ const task = this.activeTask;
3268
+ if (task?.request.taskId === taskId) {
3269
+ this.cancelTask(task);
3270
+ }
3271
+ }
3272
+ handleChunkAcknowledgement(taskId, chunkId) {
3273
+ const task = this.activeTask;
3274
+ if (task?.request.taskId === taskId) {
3275
+ this.acknowledgeChunk(task, chunkId);
3276
+ }
3277
+ }
3278
+ handleMessage(event) {
3279
+ const message = this.messageParser.parseInbound(event.data);
3280
+ if (message === void 0) {
3281
+ return;
3282
+ }
3283
+ this.handleInboundMessage(message);
3284
+ }
3285
+ handleInboundMessage(message) {
3286
+ switch (message.type) {
3287
+ case "acknowledge-chunk":
3288
+ this.handleChunkAcknowledgement(message.taskId, message.chunkId);
3289
+ return;
3290
+ case "cancel":
3291
+ this.handleCancellation(message.taskId);
3292
+ return;
3293
+ case "start":
3294
+ this.handleStart(message);
3295
+ }
3296
+ }
3297
+ handleStart(message) {
3298
+ try {
3299
+ this.jobValidator.validate(message.job);
3300
+ } catch (error) {
3301
+ const taskId = this.getPotentialTaskId(message.job);
3302
+ if (taskId !== void 0) {
3303
+ this.port.postMessage({
3304
+ message: this.getErrorMessage(error),
3305
+ taskId,
3306
+ type: "error"
3307
+ });
3308
+ }
3309
+ return;
3310
+ }
3311
+ if (message.job.taskId <= this.latestStartedTaskId) {
3312
+ this.port.postMessage({
3313
+ message: "Each model export must use a newer task id.",
3314
+ taskId: message.job.taskId,
3315
+ type: "error"
3316
+ });
3317
+ return;
3318
+ }
3319
+ this.activeTask?.cancel();
3320
+ const task = new ExportWorkerTask(message.job);
3321
+ task.zip = this.createZip(task);
3322
+ this.activeTask = task;
3323
+ this.latestStartedTaskId = task.request.taskId;
3324
+ void this.runTask(task);
3325
+ }
3326
+ handleZipData(task, error, data, _final) {
3327
+ if (!this.isCurrentTask(task) || task.cancelled) {
3328
+ return;
3329
+ }
3330
+ if (error !== null) {
3331
+ throw new ModelExportError(ModelExportErrorCode.InvalidModel, error.message);
3332
+ }
3333
+ if (data.byteLength > 0) {
3334
+ task.queuedZipChunks.push(this.copyZipChunk(data));
3335
+ }
3336
+ }
3337
+ isCurrentTask(task) {
3338
+ return this.activeTask === task;
3339
+ }
3340
+ matchesRoleDirectory(roleDirectory, role) {
3341
+ return role.role === roleDirectory;
3342
+ }
3343
+ isMtlFile(fileName) {
3344
+ return fileName.toLowerCase().endsWith(".mtl");
3345
+ }
3346
+ postCancelled(task) {
3347
+ this.port.postMessage({
3348
+ taskId: task.request.taskId,
3349
+ type: "cancelled"
3350
+ });
3351
+ }
3352
+ postComplete(task) {
3353
+ this.port.postMessage({
3354
+ taskId: task.request.taskId,
3355
+ type: "complete"
3356
+ });
3357
+ }
3358
+ postError(task, error) {
3359
+ this.port.postMessage({
3360
+ message: this.getErrorMessage(error),
3361
+ taskId: task.request.taskId,
3362
+ type: "error"
3363
+ });
3364
+ }
3365
+ postProgress(task, completedFileCount) {
3366
+ this.port.postMessage({
3367
+ completedFileCount,
3368
+ taskId: task.request.taskId,
3369
+ totalFileCount: this.getFileCount(task.request),
3370
+ type: "progress"
3371
+ });
3372
+ }
3373
+ async runTask(task) {
3374
+ let completedFileCount = 0;
3375
+ try {
3376
+ for (const role of task.request.roles) {
3377
+ if (role.kind === "generated-obj") {
3378
+ completedFileCount += await this.exportGeneratedRole(
3379
+ task,
3380
+ role,
3381
+ completedFileCount
3382
+ );
3383
+ } else {
3384
+ for (const file of role.files) {
3385
+ await this.exportFile(task, role.format, role.role, file);
3386
+ completedFileCount += 1;
3387
+ this.postProgress(task, completedFileCount);
3388
+ }
3389
+ }
3390
+ }
3391
+ this.throwIfCancelled(task);
3392
+ task.zip?.end();
3393
+ await this.drainZipChunks(task);
3394
+ this.throwIfCancelled(task);
3395
+ if (this.isCurrentTask(task)) {
3396
+ this.postComplete(task);
3397
+ }
3398
+ } catch (error) {
3399
+ if (this.isCurrentTask(task)) {
3400
+ if (task.cancelled || this.isCancelledError(error)) {
3401
+ this.postCancelled(task);
3402
+ } else {
3403
+ task.zip?.terminate();
3404
+ this.postError(task, error);
3405
+ }
3406
+ }
3407
+ } finally {
3408
+ this.finishCurrentTask(task);
3409
+ }
3410
+ }
3411
+ async streamResponseIntoEntry(task, body, entry, baker) {
3412
+ const reader = body.getReader();
3413
+ try {
3414
+ for (; ; ) {
3415
+ this.throwIfCancelled(task);
3416
+ const result = await reader.read();
3417
+ if (result.done) {
3418
+ break;
3419
+ }
3420
+ const source = result.value;
3421
+ this.pushEntryChunk(
3422
+ entry,
3423
+ baker === void 0 ? source : baker.push(source, false),
3424
+ false
3425
+ );
3426
+ await this.drainZipChunks(task);
3427
+ }
3428
+ const finalChunk = baker?.push(new Uint8Array(0), true);
3429
+ if (finalChunk !== void 0) {
3430
+ this.pushEntryChunk(entry, finalChunk, false);
3431
+ }
3432
+ this.pushEntryChunk(entry, new Uint8Array(0), true);
3433
+ await this.drainZipChunks(task);
3434
+ } finally {
3435
+ reader.releaseLock();
3436
+ }
3437
+ }
3438
+ pushEntryChunk(entry, chunk, final) {
3439
+ if (chunk.byteLength > 0 || final) {
3440
+ entry.push(chunk, final);
3441
+ }
3442
+ }
3443
+ throwIfCancelled(task) {
3444
+ if (task.cancelled || task.abortController.signal.aborted) {
3445
+ throw new ModelExportError(
3446
+ ModelExportErrorCode.Cancelled,
3447
+ "The model export was cancelled."
3448
+ );
3449
+ }
3450
+ }
3451
+ isCancelledError(error) {
3452
+ return error instanceof ModelExportError && error.code === ModelExportErrorCode.Cancelled;
3453
+ }
3454
+ }
3455
+ const runtime = new ModelExportWorkerRuntime(new BrowserExportWorkerPort());
3456
+ runtime.attach();
3457
+ })();