@solid-labs/fab-one-widget 0.1.3 → 0.1.4

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.
@@ -1,4126 +0,0 @@
1
- import { jsxs as V, jsx as d, Fragment as He } from "react/jsx-runtime";
2
- import { createContext as tn, useCallback as Fe, useMemo as de, useState as q, memo as nn, useEffect as Ce, useRef as Ie } from "react";
3
- import { useFrame as Dt, useThree as on, Canvas as sn } from "@react-three/fiber";
4
- import { Html as Ke, Line as ge, OrbitControls as Tt } from "@react-three/drei";
5
- import * as J from "three";
6
- import { Plane as lt, Vector3 as h, Box3 as kt, Line3 as gt, Quaternion as Oe, Matrix4 as Bt } from "three";
7
- import { create as rn } from "zustand";
8
- import { OBJLoader as ln } from "three/examples/jsm/loaders/OBJLoader.js";
9
- import { STLLoader as an } from "three/examples/jsm/loaders/STLLoader.js";
10
- import { MeshBVH as $e } from "three-mesh-bvh";
11
- let bo, ut, ao, jt;
12
- let __tla = (async () => {
13
- const cn = {
14
- showDragDrop: true,
15
- showStartOver: true,
16
- showInsertMeasurement: true,
17
- showDebug: true,
18
- showSpacingToggle: true,
19
- showAmputationModal: true,
20
- showNavigation: true,
21
- showToolbar: true
22
- }, fn = {
23
- showDragDrop: false,
24
- showStartOver: false,
25
- showInsertMeasurement: false,
26
- showDebug: false,
27
- showSpacingToggle: false,
28
- showAmputationModal: false,
29
- showNavigation: false,
30
- showToolbar: false
31
- }, dn = tn(cn), mt = rn((e, s) => ({
32
- landmarkPoints: [],
33
- isAligned: false,
34
- isCut: false,
35
- addLandmarkPoint: (n) => e((t) => t.landmarkPoints.length >= 3 ? t : {
36
- landmarkPoints: [
37
- ...t.landmarkPoints,
38
- n
39
- ]
40
- }),
41
- removeLandmarkPoint: (n) => e((t) => ({
42
- landmarkPoints: t.landmarkPoints.filter((i, a) => a !== n)
43
- })),
44
- clearLandmarkPoints: () => e({
45
- landmarkPoints: [],
46
- isAligned: false,
47
- isCut: false
48
- }),
49
- updateLandmarkPositions: (n) => e((t) => ({
50
- landmarkPoints: t.landmarkPoints.map((i, a) => ({
51
- ...i,
52
- position: n[a] ?? i.position
53
- }))
54
- })),
55
- setAligned: (n) => e({
56
- isAligned: n
57
- }),
58
- setCut: (n) => e({
59
- isCut: n
60
- }),
61
- isSelectionComplete: () => s().landmarkPoints.length === 3
62
- })), un = 0.45, Ye = 3, pt = 1e-3, We = 25.4, pn = 5, hn = [
63
- 0.25,
64
- -0.25,
65
- 0.5,
66
- -0.5
67
- ];
68
- function gn(e) {
69
- const s = e.split(`
70
- `), n = [], t = [];
71
- let i = false;
72
- for (const a of s) if (a.startsWith("v ")) {
73
- const c = a.trim().split(/\s+/);
74
- if (c.length >= 7) {
75
- n.push(parseFloat(c[1]), parseFloat(c[2]), parseFloat(c[3]));
76
- let u = parseFloat(c[4]), o = parseFloat(c[5]), l = parseFloat(c[6]);
77
- (u > 1 || o > 1 || l > 1) && (u /= 255, o /= 255, l /= 255), t.push(u, o, l), i = true;
78
- } else c.length >= 4 && (n.push(parseFloat(c[1]), parseFloat(c[2]), parseFloat(c[3])), t.push(0, 0, 0));
79
- }
80
- return i ? {
81
- positions: new Float32Array(n),
82
- colors: new Float32Array(t)
83
- } : null;
84
- }
85
- function mn(e, s, n, t) {
86
- const i = e.getAttribute("position"), a = i.count, c = new Float32Array(a * 3), u = s.length / 3;
87
- let o = 1 / 0, l = 1 / 0, r = 1 / 0, f = -1 / 0, y = -1 / 0, p = -1 / 0;
88
- for (let b = 0; b < u; b++) {
89
- const w = s[b * 3] * t, C = s[b * 3 + 1] * t, M = s[b * 3 + 2] * t;
90
- w < o && (o = w), w > f && (f = w), C < l && (l = C), C > y && (y = C), M < r && (r = M), M > p && (p = M);
91
- }
92
- const m = Math.min(128, Math.max(16, Math.round(Math.cbrt(u)))), g = (f - o + 1e-6) / m, z = (y - l + 1e-6) / m, v = (p - r + 1e-6) / m, x = /* @__PURE__ */ new Map();
93
- for (let b = 0; b < u; b++) {
94
- const w = Math.min(m - 1, Math.floor((s[b * 3] * t - o) / g)), C = Math.min(m - 1, Math.floor((s[b * 3 + 1] * t - l) / z)), M = Math.min(m - 1, Math.floor((s[b * 3 + 2] * t - r) / v)), L = w * m * m + C * m + M;
95
- let P = x.get(L);
96
- P || (P = [], x.set(L, P)), P.push(b);
97
- }
98
- for (let b = 0; b < a; b++) {
99
- const w = i.getX(b), C = i.getY(b), M = i.getZ(b), L = Math.min(m - 1, Math.max(0, Math.floor((w - o) / g))), P = Math.min(m - 1, Math.max(0, Math.floor((C - l) / z))), H = Math.min(m - 1, Math.max(0, Math.floor((M - r) / v)));
100
- let O = 1 / 0, $ = 0;
101
- for (let R = 0; R <= m && O > 0; R++) {
102
- for (let Y = -R; Y <= R; Y++) for (let N = -R; N <= R; N++) for (let ne = -R; ne <= R; ne++) {
103
- if (R > 0 && Math.abs(Y) < R && Math.abs(N) < R && Math.abs(ne) < R) continue;
104
- const oe = L + Y, le = P + N, ee = H + ne;
105
- if (oe < 0 || oe >= m || le < 0 || le >= m || ee < 0 || ee >= m) continue;
106
- const D = x.get(oe * m * m + le * m + ee);
107
- if (D) for (const S of D) {
108
- const F = s[S * 3] * t, _ = s[S * 3 + 1] * t, B = s[S * 3 + 2] * t, T = (w - F) ** 2 + (C - _) ** 2 + (M - B) ** 2;
109
- T < O && (O = T, $ = S);
110
- }
111
- }
112
- if (O < 1 / 0) break;
113
- }
114
- c[b * 3] = n[$ * 3], c[b * 3 + 1] = n[$ * 3 + 1], c[b * 3 + 2] = n[$ * 3 + 2];
115
- }
116
- e.setAttribute("color", new J.Float32BufferAttribute(c, 3));
117
- }
118
- const xn = 30, Ct = [
119
- "#4488ff",
120
- "#ff4444",
121
- "#44cc44",
122
- "#ffaa00",
123
- "#cc44ff",
124
- "#44ffcc"
125
- ];
126
- function yn(e, s) {
127
- const n = e.getVertices(), t = e.getFaces(), i = e.getComponentLabels(), a = /* @__PURE__ */ new Map();
128
- for (let r = 0; r < i.length; r++) {
129
- const f = i[r];
130
- a.has(f) || a.set(f, []), a.get(f).push(r);
131
- }
132
- const c = [
133
- ...a.keys()
134
- ].sort((r, f) => r - f), u = [], o = [], l = [];
135
- for (const r of c) {
136
- const f = a.get(r), y = /* @__PURE__ */ new Map(), p = [], m = [];
137
- for (const x of f) for (let b = 0; b < 3; b++) {
138
- const w = t[x * 3 + b];
139
- if (!y.has(w)) {
140
- const C = p.length / 3;
141
- p.push(n[w * 3], n[w * 3 + 1], n[w * 3 + 2]), y.set(w, C);
142
- }
143
- m.push(y.get(w));
144
- }
145
- const g = new J.BufferGeometry();
146
- g.setAttribute("position", new J.Float32BufferAttribute(p, 3)), g.setIndex(m), g.computeVertexNormals(), u.push(g);
147
- const v = r === s ? `#${r} inner` : r === 0 ? `#${r} outer` : `#${r} (${f.length} faces)`;
148
- o.push(v), l.push(Ct[r % Ct.length]);
149
- }
150
- return {
151
- geometries: u,
152
- labels: o,
153
- colors: l,
154
- innerIdx: s
155
- };
156
- }
157
- async function bn(e, s, n) {
158
- let t = null;
159
- try {
160
- const i = gn(e);
161
- n == null ? void 0 : n("Loading mesh..."), t = new s.WasmMeshSet(), t.loadObjString(e), n == null ? void 0 : n("Detecting units...");
162
- const a = t.autoScaleToMm(pn);
163
- n == null ? void 0 : n("Merging close vertices..."), t.applyMergeCloseVertices(1e-4), n == null ? void 0 : n("Analyzing mesh components...");
164
- let c = false, u = null;
165
- if (t.getComponentCount() >= 2) {
166
- n == null ? void 0 : n("Building component debug info...");
167
- const y = yn(t, null);
168
- n == null ? void 0 : n("Extracting inner shell..."), c = t.extractInnerShell(xn), c ? (console.log("Inner shell extracted from double-shell mesh"), y.innerIdx = 1, y.labels[1] = "#1 inner (extracted)", y.labels[0] && (y.labels[0] = "#0 outer (discarded)"), u = y) : (n == null ? void 0 : n("Removing floating artifacts..."), t.splitAndKeepLargest(), u = y);
169
- } else n == null ? void 0 : n("Removing floating artifacts..."), t.splitAndKeepLargest();
170
- n == null ? void 0 : n("Closing holes..."), t.applyCloseHoles(30), t.applyMergeCloseVertices(1e-4), n == null ? void 0 : n("Extracting geometry...");
171
- const l = t.getVertices(), r = t.getFaces();
172
- if (l.length === 0) return null;
173
- const f = new J.BufferGeometry();
174
- if (f.setAttribute("position", new J.Float32BufferAttribute(l, 3)), f.setIndex(Array.from(r)), f.computeVertexNormals(), i) {
175
- const y = a ? 1e3 : 1;
176
- mn(f, i.positions, i.colors, y);
177
- }
178
- return {
179
- geometry: f,
180
- wasScaled: a,
181
- innerShellExtracted: c,
182
- componentDebug: u
183
- };
184
- } catch (i) {
185
- return console.error("WASM processing failed:", i), null;
186
- } finally {
187
- if (t) try {
188
- t.free();
189
- } catch {
190
- }
191
- }
192
- }
193
- function wn(e) {
194
- const n = new ln().parse(e);
195
- let t = null;
196
- return n.traverse((i) => {
197
- i.isMesh && !t && (t = i.geometry);
198
- }), t;
199
- }
200
- async function dt(e) {
201
- const s = await e.arrayBuffer(), i = new an().parse(s).getAttribute("position");
202
- if (!i || i.count === 0) throw new Error("Empty STL geometry");
203
- const a = [];
204
- for (let c = 0; c < i.count; c++) a.push(`v ${i.getX(c)} ${i.getY(c)} ${i.getZ(c)}`);
205
- for (let c = 0; c < i.count; c += 3) a.push(`f ${c + 1} ${c + 2} ${c + 3}`);
206
- return a.join(`
207
- `);
208
- }
209
- function Rt(e, s) {
210
- const n = 1 / s;
211
- return `${Math.round(e.x * n)}_${Math.round(e.y * n)}_${Math.round(e.z * n)}`;
212
- }
213
- function Et(e, s) {
214
- if (!e.length) return [];
215
- const n = [];
216
- for (const t of e) (n.length === 0 || n[n.length - 1].distanceTo(t) > s) && n.push(t.clone());
217
- return n.length > 2 && n[0].distanceTo(n[n.length - 1]) > s && n.push(n[0].clone()), n;
218
- }
219
- function je(e) {
220
- let s = 0;
221
- for (let n = 0; n < e.length - 1; n++) s += e[n].distanceTo(e[n + 1]);
222
- return s;
223
- }
224
- function Wt(e, s = 1e-3, n = false) {
225
- if (!e.length) return [];
226
- const t = /* @__PURE__ */ new Map(), i = (l) => {
227
- const r = Rt(l, s);
228
- let f = t.get(r);
229
- return f || (f = l.clone(), t.set(r, f)), f;
230
- }, a = e.map((l) => ({
231
- a: i(l.a),
232
- b: i(l.b)
233
- })), c = [];
234
- for (; a.length; ) {
235
- const l = a.pop(), r = [
236
- l.a,
237
- l.b
238
- ];
239
- let f = true;
240
- for (; f; ) {
241
- f = false;
242
- for (let p = a.length - 1; p >= 0; p--) {
243
- const { a: m, b: g } = a[p];
244
- if (m.equals(r[r.length - 1])) r.push(g);
245
- else if (g.equals(r[r.length - 1])) r.push(m);
246
- else if (m.equals(r[0])) r.unshift(g);
247
- else if (g.equals(r[0])) r.unshift(m);
248
- else continue;
249
- a.splice(p, 1), f = true;
250
- }
251
- }
252
- const y = Et(r, s);
253
- y.length > 1 && c.push(y);
254
- }
255
- if (!c.length) return [];
256
- if (c.sort((l, r) => je(r) - je(l)), !n) return c[0] ?? [];
257
- const u = je(c[0]), o = c.filter((l) => je(l) >= u * 0.3);
258
- return o[o.length - 1] ?? c[0];
259
- }
260
- function vt(e, s, n, t = false) {
261
- const i = new lt(new h(0, 1, 0), -n), a = [], c = new kt();
262
- c.setFromBufferAttribute(s.getAttribute("position"));
263
- const u = {
264
- linePoints: [],
265
- lineLength: 0,
266
- rightmostPoint: new h(0, n, 0)
267
- };
268
- if (!i.intersectsBox(c)) return u;
269
- const o = new gt(), l = new h();
270
- e.shapecast({
271
- intersectsBounds: (g) => i.intersectsBox(g),
272
- intersectsTriangle: (g) => {
273
- const z = [];
274
- o.set(g.a, g.b), i.intersectLine(o, l) && z.push(l.clone()), o.set(g.b, g.c), i.intersectLine(o, l) && z.push(l.clone()), o.set(g.c, g.a), i.intersectLine(o, l) && z.push(l.clone()), z.length === 2 && a.push({
275
- a: z[0],
276
- b: z[1]
277
- });
278
- }
279
- });
280
- const r = Wt(a, pt, t), f = je(r);
281
- if (r.length < 2) return u;
282
- let y = -1 / 0, p = new h(0, n, 0);
283
- for (const g of r) g.x > y && (y = g.x, p = g.clone());
284
- const m = r.length > 2 && r[0].distanceTo(r[r.length - 1]) < pt * 10;
285
- return {
286
- linePoints: r,
287
- lineLength: f,
288
- rightmostPoint: p,
289
- isClosed: m
290
- };
291
- }
292
- function Ze(e, s, n, t = false) {
293
- const i = vt(e, s, n, t);
294
- if (i.isClosed && i.linePoints.length >= 3) return i;
295
- console.warn(`[slice] y=${n.toFixed(2)} failed (pts=${i.linePoints.length}, closed=${i.isClosed}), retrying...`);
296
- let a = i;
297
- for (const c of hn) {
298
- const u = vt(e, s, n + c, t);
299
- if (u.isClosed && u.linePoints.length >= 3) return console.log(`[slice] y=${n.toFixed(2)} recovered with offset ${c > 0 ? "+" : ""}${c}mm (pts=${u.linePoints.length}, len=${u.lineLength.toFixed(1)}mm)`), u;
300
- u.linePoints.length > a.linePoints.length && (a = u);
301
- }
302
- return console.warn(`[slice] y=${n.toFixed(2)} all retries exhausted (pts=${a.linePoints.length}, closed=${a.isClosed})`), a;
303
- }
304
- function ht(e, s, n, t) {
305
- const i = new lt().setFromNormalAndCoplanarPoint(t.clone().normalize(), n), a = new kt();
306
- if (a.setFromBufferAttribute(s.getAttribute("position")), !i.intersectsBox(a)) return 0;
307
- const c = [], u = new gt(), o = new h();
308
- e.shapecast({
309
- intersectsBounds: (r) => i.intersectsBox(r),
310
- intersectsTriangle: (r) => {
311
- const f = [];
312
- u.set(r.a, r.b), i.intersectLine(u, o) && f.push(o.clone()), u.set(r.b, r.c), i.intersectLine(u, o) && f.push(o.clone()), u.set(r.c, r.a), i.intersectLine(u, o) && f.push(o.clone()), f.length === 2 && c.push({
313
- a: f[0],
314
- b: f[1]
315
- });
316
- }
317
- });
318
- const l = Wt(c, pt);
319
- return je(l);
320
- }
321
- function st(e) {
322
- return new $e(e, {
323
- maxLeafTris: Ye
324
- });
325
- }
326
- function Mt(e, s, n) {
327
- const t = e instanceof $e ? e : new $e(e, {
328
- maxLeafTris: Ye
329
- }), i = new lt().setFromNormalAndCoplanarPoint(n.clone().normalize(), s), a = [], c = new gt(), u = new h();
330
- return t.shapecast({
331
- intersectsBounds: (o) => i.intersectsBox(o),
332
- intersectsTriangle: (o) => {
333
- const l = [];
334
- c.set(o.a, o.b), i.intersectLine(c, u) && l.push(u.clone()), c.set(o.b, o.c), i.intersectLine(c, u) && l.push(u.clone()), c.set(o.c, o.a), i.intersectLine(c, u) && l.push(u.clone()), l.length === 2 && a.push({
335
- a: l[0],
336
- b: l[1]
337
- });
338
- }
339
- }), a;
340
- }
341
- function At(e, s, n = 1e-3) {
342
- if (!e.length) return [];
343
- const t = /* @__PURE__ */ new Map(), i = (l) => {
344
- const r = Rt(l, n);
345
- let f = t.get(r);
346
- return f || (f = l.clone(), t.set(r, f)), f;
347
- }, a = e.map((l) => ({
348
- a: i(l.a),
349
- b: i(l.b)
350
- })), c = [];
351
- for (; a.length; ) {
352
- const l = a.pop(), r = [
353
- l.a,
354
- l.b
355
- ];
356
- let f = true;
357
- for (; f; ) {
358
- f = false;
359
- for (let p = a.length - 1; p >= 0; p--) {
360
- const { a: m, b: g } = a[p];
361
- if (m.equals(r[r.length - 1])) r.push(g);
362
- else if (g.equals(r[r.length - 1])) r.push(m);
363
- else if (m.equals(r[0])) r.unshift(g);
364
- else if (g.equals(r[0])) r.unshift(m);
365
- else continue;
366
- a.splice(p, 1), f = true;
367
- }
368
- }
369
- const y = Et(r, n);
370
- y.length > 1 && c.push(y);
371
- }
372
- if (!c.length) return [];
373
- let u = [], o = 1 / 0;
374
- for (const l of c) {
375
- if (l.length < 3 || !(l[0].distanceTo(l[l.length - 1]) < 2)) continue;
376
- const f = new h();
377
- for (const p of l) f.add(p);
378
- f.divideScalar(l.length);
379
- const y = f.distanceTo(s);
380
- y < o && (o = y, u = l);
381
- }
382
- if (u.length === 0) for (const l of c) {
383
- if (l.length < 3) continue;
384
- const r = new h();
385
- for (const y of l) r.add(y);
386
- r.divideScalar(l.length);
387
- const f = r.distanceTo(s);
388
- f < o && (o = f, u = l);
389
- }
390
- return u;
391
- }
392
- function Sn(e) {
393
- const s = new h();
394
- for (const n of e) s.add(n);
395
- return s.divideScalar(e.length);
396
- }
397
- function zn(e, s, n) {
398
- const t = new $e(e, {
399
- maxLeafTris: Ye
400
- }), i = new h().subVectors(s, n).normalize(), c = s.distanceTo(n) * 0.05;
401
- let u = i.clone();
402
- const o = (v) => {
403
- const x = [];
404
- for (let $ = -10; $ <= 0; $++) {
405
- const R = s.clone().addScaledVector(v, $ * c), Y = Mt(t, R, v), N = At(Y, R, 1);
406
- N.length >= 3 && N[0].distanceTo(N[N.length - 1]) < 1 && x.push(Sn(N));
407
- }
408
- if (x.length < 3) return null;
409
- const b = new h();
410
- for (const $ of x) b.add($);
411
- b.divideScalar(x.length);
412
- let w = 0, C = 0, M = 0, L = 0, P = 0, H = 0;
413
- for (const $ of x) {
414
- const R = $.x - b.x, Y = $.y - b.y, N = $.z - b.z;
415
- w += R * R, C += R * Y, M += R * N, L += Y * Y, P += Y * N, H += N * N;
416
- }
417
- let O = v.clone();
418
- for (let $ = 0; $ < 30; $++) {
419
- const R = w * O.x + C * O.y + M * O.z, Y = C * O.x + L * O.y + P * O.z, N = M * O.x + P * O.y + H * O.z, ne = new h(R, Y, N), oe = ne.length();
420
- if (oe < 1e-10 || (ne.divideScalar(oe), O.distanceTo(ne) < 1e-8)) break;
421
- O = ne;
422
- }
423
- return O.dot(i) < 0 && O.negate(), O;
424
- };
425
- for (let v = 0; v < 3; v++) {
426
- const x = o(u);
427
- if (!x) break;
428
- const b = u.angleTo(x);
429
- if (u = x, b < 1e-3) break;
430
- }
431
- const l = new h();
432
- Math.abs(u.x) < 0.9 ? l.set(1, 0, 0) : l.set(0, 1, 0), l.crossVectors(u, l).normalize();
433
- const r = new h().crossVectors(u, l).normalize(), f = (v) => {
434
- const x = Mt(t, s, v), b = At(x, s, 1);
435
- if (b.length < 3) return {
436
- circumference: 1 / 0,
437
- closed: false
438
- };
439
- const w = b[0].distanceTo(b[b.length - 1]) < 1;
440
- return {
441
- circumference: je(b),
442
- closed: w
443
- };
444
- };
445
- let y = u.clone(), p = 1 / 0;
446
- for (let v = -3; v <= 3; v += 1) for (let x = -3; x <= 3; x += 1) {
447
- const b = v * Math.PI / 180, w = x * Math.PI / 180, C = u.clone().applyQuaternion(new Oe().setFromAxisAngle(l, b)).applyQuaternion(new Oe().setFromAxisAngle(r, w)).normalize(), M = f(C);
448
- M.closed && M.circumference < p && (p = M.circumference, y = C);
449
- }
450
- const m = y.clone(), g = new h();
451
- Math.abs(m.x) < 0.9 ? g.set(1, 0, 0) : g.set(0, 1, 0), g.crossVectors(m, g).normalize();
452
- const z = new h().crossVectors(m, g).normalize();
453
- for (let v = -5; v <= 5; v += 1) for (let x = -5; x <= 5; x += 1) {
454
- const b = v * 0.1 * Math.PI / 180, w = x * 0.1 * Math.PI / 180, C = m.clone().applyQuaternion(new Oe().setFromAxisAngle(g, b)).applyQuaternion(new Oe().setFromAxisAngle(z, w)).normalize(), M = f(C);
455
- M.closed && M.circumference < p && (p = M.circumference, y = C);
456
- }
457
- return p === 1 / 0 && (p = f(u).circumference, y = u), {
458
- normal: y,
459
- circumference: p
460
- };
461
- }
462
- function Cn(e, s, n) {
463
- const t = e.getAttribute("position"), i = e.getIndex();
464
- if (!i) return {
465
- isDoubleShell: false,
466
- sampledFaces: 0,
467
- opposingPairs: 0,
468
- confidence: 0
469
- };
470
- const a = i.array, c = t.array, u = new h().subVectors(n, s).normalize(), o = n.distanceTo(s), l = Math.min(o * 0.06, 15), r = new $e(e, {
471
- maxLeafTris: Ye
472
- });
473
- let f = new h(1, 0, 0);
474
- Math.abs(u.dot(f)) > 0.9 && (f = new h(0, 1, 0)), f.sub(u.clone().multiplyScalar(u.dot(f))).normalize();
475
- const y = new h().crossVectors(u, f).normalize(), p = 12, m = new h();
476
- let g = 0, z = 0, v = 0;
477
- for (const w of [
478
- 0.65,
479
- 0.78,
480
- 0.9
481
- ]) {
482
- const C = new h().lerpVectors(s, n, w);
483
- let M = 0, L = 0, P = 0, H = 0;
484
- for (let Y = 0; Y < a.length; Y += 3) {
485
- const N = a[Y], ne = a[Y + 1], oe = a[Y + 2], le = (c[N * 3] + c[ne * 3] + c[oe * 3]) / 3, ee = (c[N * 3 + 1] + c[ne * 3 + 1] + c[oe * 3 + 1]) / 3, D = (c[N * 3 + 2] + c[ne * 3 + 2] + c[oe * 3 + 2]) / 3, S = le - C.x, F = ee - C.y, _ = D - C.z;
486
- Math.abs(S * u.x + F * u.y + _ * u.z) > l || (M += le, L += ee, P += D, H++);
487
- }
488
- if (H < 8) continue;
489
- const O = new h(M / H, L / H, P / H);
490
- let $ = 0;
491
- for (let Y = 0; Y < p; Y++) {
492
- const N = Y / p * Math.PI * 2, ne = new h().addScaledVector(f, Math.cos(N)).addScaledVector(y, Math.sin(N)), oe = new J.Ray(O, ne);
493
- let le = 0;
494
- r.shapecast({
495
- intersectsBounds: (ee) => oe.intersectsBox(ee),
496
- intersectsTriangle: (ee) => (oe.intersectTriangle(ee.a, ee.b, ee.c, false, m) && le++, false)
497
- }), $ += le, z++;
498
- }
499
- g += $, $ / p > 1.5 && v++;
500
- }
501
- if (z === 0) return {
502
- isDoubleShell: false,
503
- sampledFaces: 0,
504
- opposingPairs: 0,
505
- confidence: 0
506
- };
507
- const x = g / z;
508
- return {
509
- isDoubleShell: v >= 2,
510
- sampledFaces: z,
511
- opposingPairs: g,
512
- confidence: x
513
- };
514
- }
515
- function vn(e, s, n, t) {
516
- const i = new h().subVectors(n, s), a = new h().subVectors(t, s), c = new h().subVectors(e, s), u = i.dot(i), o = i.dot(a), l = i.dot(c), r = a.dot(a), f = a.dot(c), y = 1 / (u * r - o * o), p = (r * l - o * f) * y, m = (u * f - o * l) * y;
517
- return {
518
- u: 1 - p - m,
519
- v: p,
520
- w: m
521
- };
522
- }
523
- function rt(e, s, n, t, i = false) {
524
- const a = n.clone().normalize(), c = e.getAttribute("position"), u = e.getIndex();
525
- if (!u) throw new Error("No index buffer");
526
- const o = new h().subVectors(t, s).dot(a), l = Math.abs(o) < 1e-6 ? 1 : Math.sign(o), r = c.array, f = u.array, y = e.getAttribute("color"), p = y ? y.array : null, m = [], g = [], z = [], v = (D, S, F) => {
527
- const _ = m.length / 3;
528
- return m.push(D, S, F), _;
529
- }, x = (D) => new h(r[D * 3], r[D * 3 + 1], r[D * 3 + 2]), b = (D, S) => {
530
- const F = new h().subVectors(D, s).dot(a), _ = new h().subVectors(S, s).dot(a), B = F / (F - _);
531
- return new h().lerpVectors(D, S, B);
532
- }, w = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map(), M = /* @__PURE__ */ new Map(), L = (D) => {
533
- let S = M.get(D);
534
- return S === void 0 && (S = new h().subVectors(x(D), s).dot(a), M.set(D, S)), S;
535
- }, P = (D) => {
536
- if (w.has(D)) return w.get(D);
537
- const S = x(D), F = v(S.x, S.y, S.z);
538
- return p && g.push(p[D * 3], p[D * 3 + 1], p[D * 3 + 2]), w.set(D, F), F;
539
- }, H = (D, S) => {
540
- const F = D < S ? `${D}_${S}` : `${S}_${D}`;
541
- if (C.has(F)) return C.get(F);
542
- const _ = b(x(D), x(S)), B = v(_.x, _.y, _.z);
543
- if (p) {
544
- const T = L(D), E = L(S), j = T / (T - E);
545
- g.push(p[D * 3] + j * (p[S * 3] - p[D * 3]), p[D * 3 + 1] + j * (p[S * 3 + 1] - p[D * 3 + 1]), p[D * 3 + 2] + j * (p[S * 3 + 2] - p[D * 3 + 2]));
546
- }
547
- return C.set(F, B), B;
548
- }, O = (D) => {
549
- const S = L(D);
550
- return Math.sign(S) === l || Math.abs(S) < 1e-6;
551
- };
552
- for (let D = 0; D < f.length; D += 3) {
553
- const S = f[D], F = f[D + 1], _ = f[D + 2], B = O(S), T = O(F), E = O(_), j = (B ? 1 : 0) + (T ? 1 : 0) + (E ? 1 : 0);
554
- if (j !== 0) if (j === 3) z.push(P(S), P(F), P(_));
555
- else if (j === 1) {
556
- let X, Q, ie;
557
- B ? (X = S, Q = F, ie = _) : T ? (X = F, Q = _, ie = S) : (X = _, Q = S, ie = F), z.push(P(X), H(X, Q), H(X, ie));
558
- } else {
559
- let X, Q, ie;
560
- B ? T ? (X = _, Q = S, ie = F) : (X = F, Q = _, ie = S) : (X = S, Q = F, ie = _);
561
- const se = P(Q), A = P(ie), I = H(Q, X), k = H(ie, X);
562
- z.push(se, I, A), z.push(A, I, k);
563
- }
564
- }
565
- const $ = z.length / 3;
566
- let R;
567
- if (i) R = Array.from({
568
- length: $
569
- }, (D, S) => S);
570
- else {
571
- const D = /* @__PURE__ */ new Map();
572
- for (let T = 0; T < $; T++) for (let E = 0; E < 3; E++) {
573
- const j = z[T * 3 + E];
574
- D.has(j) || D.set(j, []), D.get(j).push(T);
575
- }
576
- const S = /* @__PURE__ */ new Set(), F = [];
577
- for (let T = 0; T < $; T++) {
578
- if (S.has(T)) continue;
579
- const E = [], j = [
580
- T
581
- ];
582
- for (S.add(T); j.length > 0; ) {
583
- const X = j.shift();
584
- E.push(X);
585
- for (let Q = 0; Q < 3; Q++) {
586
- const ie = z[X * 3 + Q];
587
- for (const se of D.get(ie)) S.has(se) || (S.add(se), j.push(se));
588
- }
589
- }
590
- F.push(E);
591
- }
592
- let _ = F[0] || [], B = 1 / 0;
593
- for (const T of F) {
594
- let E = 1 / 0;
595
- for (const j of T) {
596
- for (let X = 0; X < 3; X++) {
597
- const Q = z[j * 3 + X], ie = m[Q * 3] - t.x, se = m[Q * 3 + 1] - t.y, A = m[Q * 3 + 2] - t.z, I = ie * ie + se * se + A * A;
598
- I < E && (E = I);
599
- }
600
- if (E < B) break;
601
- }
602
- E < B && (B = E, _ = T);
603
- }
604
- R = _;
605
- }
606
- const Y = /* @__PURE__ */ new Map(), N = [], ne = [], oe = [];
607
- let le = 0;
608
- for (const D of R) for (let S = 0; S < 3; S++) {
609
- const F = z[D * 3 + S];
610
- Y.has(F) || (N.push(m[F * 3], m[F * 3 + 1], m[F * 3 + 2]), p && ne.push(g[F * 3], g[F * 3 + 1], g[F * 3 + 2]), Y.set(F, le++)), oe.push(Y.get(F));
611
- }
612
- const ee = new J.BufferGeometry();
613
- return ee.setAttribute("position", new J.Float32BufferAttribute(N, 3)), p && ne.length > 0 && ee.setAttribute("color", new J.Float32BufferAttribute(ne, 3)), ee.setIndex(oe), ee.computeVertexNormals(), ee.computeBoundingBox(), ee;
614
- }
615
- function it(e, s, n) {
616
- const t = e.getAttribute("position"), i = n - s;
617
- if (i < 1) return null;
618
- const a = 30, c = i / a, u = [];
619
- for (let z = 0; z < a; z++) {
620
- const v = s + z * c, x = s + (z + 1) * c;
621
- let b = 0, w = 0, C = 0, M = 0;
622
- for (let L = 0; L < t.count; L++) {
623
- const P = t.getY(L);
624
- P >= v && P < x && (b += t.getX(L), w += P, C += t.getZ(L), M++);
625
- }
626
- M > 20 && u.push(new h(b / M, w / M, C / M));
627
- }
628
- if (u.length < 5) return null;
629
- const o = new h();
630
- for (const z of u) o.add(z);
631
- o.divideScalar(u.length);
632
- let l = 0, r = 0, f = 0, y = 0, p = 0, m = 0;
633
- for (const z of u) {
634
- const v = z.x - o.x, x = z.y - o.y, b = z.z - o.z;
635
- l += v * v, r += v * x, f += v * b, y += x * x, p += x * b, m += b * b;
636
- }
637
- let g = new h(0.01, 1, 0.01).normalize();
638
- for (let z = 0; z < 30; z++) {
639
- const v = l * g.x + r * g.y + f * g.z, x = r * g.x + y * g.y + p * g.z, b = f * g.x + p * g.y + m * g.z, w = new h(v, x, b), C = w.length();
640
- if (C < 1e-10 || (w.divideScalar(C), g.distanceTo(w) < 1e-8)) break;
641
- g = w;
642
- }
643
- return g.y < 0 && g.negate(), g;
644
- }
645
- function Vt(e, s, n, t) {
646
- const i = e.getIndex(), a = i ? i.count / 3 : 0;
647
- if (a < 10) return {
648
- valid: false,
649
- reason: `Geometry is empty or degenerate (${a} faces)`
650
- };
651
- const c = s - n;
652
- if (c < 4) return {
653
- valid: false,
654
- reason: `Height too small (${c.toFixed(1)}mm < 4mm)`
655
- };
656
- if (c > 1e3) return {
657
- valid: false,
658
- reason: `Height too large (${c.toFixed(1)}mm > 1000mm)`
659
- };
660
- const u = new $e(e, {
661
- maxLeafTris: Ye
662
- }), o = Ze(u, e, s);
663
- if (o.lineLength === 0) return {
664
- valid: false,
665
- reason: "No circumference at green point \u2014 mesh may be empty at that height"
666
- };
667
- const l = s - t;
668
- if (l <= n) return {
669
- valid: true,
670
- reason: ""
671
- };
672
- const r = Ze(u, e, l);
673
- if (o.lineLength > 0 && r.lineLength > 0) {
674
- const y = o.lineLength / r.lineLength;
675
- if (y < 0.5) return {
676
- valid: false,
677
- reason: `First circumference is too small relative to second (ratio ${y.toFixed(2)} < 0.5) \u2014 possible trimmed angle`
678
- };
679
- }
680
- const f = s - t * 2;
681
- if (f > n) {
682
- const y = Ze(u, e, f);
683
- if (r.lineLength > 0 && y.lineLength > 0) {
684
- const p = r.lineLength / y.lineLength;
685
- if (p < 0.5) return {
686
- valid: false,
687
- reason: `Second circumference is too small relative to third (ratio ${p.toFixed(2)} < 0.5) \u2014 possible trimmed angle`
688
- };
689
- }
690
- }
691
- return {
692
- valid: true,
693
- reason: ""
694
- };
695
- }
696
- const Mn = [
697
- {
698
- nudge: 3,
699
- angle: 0,
700
- cutOffset: 5
701
- },
702
- {
703
- nudge: 5,
704
- angle: Math.PI * 2 / 3,
705
- cutOffset: 3
706
- },
707
- {
708
- nudge: 8,
709
- angle: Math.PI * 4 / 3,
710
- cutOffset: 4
711
- },
712
- {
713
- nudge: -5,
714
- angle: 0,
715
- cutOffset: 6
716
- }
717
- ], ot = 5;
718
- function _t(e, s, n, t) {
719
- const i = new Oe().setFromAxisAngle(n, t);
720
- return e.applyMatrix4(new Bt().makeRotationFromQuaternion(i)), s.map((a) => a.clone().applyQuaternion(i));
721
- }
722
- function Ot(e, s, n) {
723
- const t = new Oe().setFromAxisAngle(s, n);
724
- e.applyMatrix4(new Bt().makeRotationFromQuaternion(t));
725
- }
726
- function An(e, s, n, t, i, a) {
727
- const c = n.getAttribute("position"), u = n.getIndex();
728
- if (!u) return s;
729
- const l = s[1].y + t * We, r = c.array, f = u.array, y = n.getAttribute("color"), p = y ? y.array : null, m = [], g = [], z = [], v = (A, I, k) => {
730
- const G = m.length / 3;
731
- return m.push(A, I, k), G;
732
- }, x = (A) => [
733
- r[A * 3],
734
- r[A * 3 + 1],
735
- r[A * 3 + 2]
736
- ], b = (A, I) => {
737
- const k = (l - A[1]) / (I[1] - A[1]);
738
- return [
739
- A[0] + k * (I[0] - A[0]),
740
- l,
741
- A[2] + k * (I[2] - A[2])
742
- ];
743
- }, w = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map(), M = (A) => {
744
- if (w.has(A)) return w.get(A);
745
- const [I, k, G] = x(A), U = v(I, k, G);
746
- return p && g.push(p[A * 3], p[A * 3 + 1], p[A * 3 + 2]), w.set(A, U), U;
747
- }, L = (A, I) => {
748
- const k = A < I ? `${A}_${I}` : `${I}_${A}`;
749
- if (C.has(k)) return C.get(k);
750
- const G = x(A), U = x(I), [re, Pe, me] = b(G, U), ae = v(re, Pe, me);
751
- if (p) {
752
- const xe = (l - G[1]) / (U[1] - G[1]);
753
- g.push(p[A * 3] + xe * (p[I * 3] - p[A * 3]), p[A * 3 + 1] + xe * (p[I * 3 + 1] - p[A * 3 + 1]), p[A * 3 + 2] + xe * (p[I * 3 + 2] - p[A * 3 + 2]));
754
- }
755
- return C.set(k, ae), ae;
756
- };
757
- for (let A = 0; A < f.length; A += 3) {
758
- const I = f[A], k = f[A + 1], G = f[A + 2], U = x(I), re = x(k), Pe = x(G), me = U[1] < l, ae = re[1] < l, xe = Pe[1] < l, Re = (me ? 1 : 0) + (ae ? 1 : 0) + (xe ? 1 : 0);
759
- if (Re !== 0) if (Re === 3) z.push(M(I), M(k), M(G));
760
- else if (Re === 1) {
761
- let pe, we, he;
762
- me ? (pe = I, we = k, he = G) : ae ? (pe = k, we = G, he = I) : (pe = G, we = I, he = k), z.push(M(pe), L(pe, we), L(pe, he));
763
- } else {
764
- let pe, we, he;
765
- me ? ae ? (pe = G, we = I, he = k) : (pe = k, we = G, he = I) : (pe = I, we = k, he = G);
766
- const Ge = M(we), fe = M(he), Ne = L(we, pe), Le = L(he, pe);
767
- z.push(Ge, Ne, fe), z.push(fe, Ne, Le);
768
- }
769
- }
770
- const P = z.length / 3, H = /* @__PURE__ */ new Map();
771
- for (let A = 0; A < P; A++) for (let I = 0; I < 3; I++) {
772
- const k = z[A * 3 + I];
773
- H.has(k) || H.set(k, []), H.get(k).push(A);
774
- }
775
- const O = /* @__PURE__ */ new Map();
776
- for (let A = 0; A < P; A++) O.set(A, /* @__PURE__ */ new Set());
777
- for (const [, A] of H) for (const I of A) for (const k of A) I !== k && O.get(I).add(k);
778
- const $ = /* @__PURE__ */ new Set(), R = [];
779
- for (let A = 0; A < P; A++) {
780
- if ($.has(A)) continue;
781
- const I = [], k = [
782
- A
783
- ];
784
- for ($.add(A); k.length > 0; ) {
785
- const G = k.shift();
786
- I.push(G);
787
- for (const U of O.get(G)) $.has(U) || ($.add(U), k.push(U));
788
- }
789
- R.push(I);
790
- }
791
- const Y = s[0];
792
- let N = R[0] || [], ne = 1 / 0;
793
- for (const A of R) {
794
- let I = 1 / 0;
795
- for (const k of A) {
796
- for (let G = 0; G < 3; G++) {
797
- const U = z[k * 3 + G], re = m[U * 3] - Y.x, Pe = m[U * 3 + 1] - Y.y, me = m[U * 3 + 2] - Y.z, ae = re * re + Pe * Pe + me * me;
798
- ae < I && (I = ae);
799
- }
800
- if (I < ne) break;
801
- }
802
- I < ne && (ne = I, N = A);
803
- }
804
- const oe = /* @__PURE__ */ new Map(), le = [], ee = [], D = [];
805
- let S = 0;
806
- for (const A of N) for (let I = 0; I < 3; I++) {
807
- const k = z[A * 3 + I];
808
- oe.has(k) || (le.push(m[k * 3], m[k * 3 + 1], m[k * 3 + 2]), p && ee.push(g[k * 3], g[k * 3 + 1], g[k * 3 + 2]), oe.set(k, S++)), D.push(oe.get(k));
809
- }
810
- const F = new J.BufferGeometry();
811
- F.setAttribute("position", new J.Float32BufferAttribute(le, 3)), p && ee.length > 0 && F.setAttribute("color", new J.Float32BufferAttribute(ee, 3)), F.setIndex(D), F.computeVertexNormals(), F.computeBoundingBox(), e.geometry.dispose(), e.geometry = F, i.onStatus("Refining alignment...");
812
- const _ = new h(0, 1, 0);
813
- let B = [
814
- ...s
815
- ];
816
- const T = (A, I) => {
817
- B = _t(F, B, A, I), a && Ot(a, A, I);
818
- };
819
- for (let A = 0; A < 5; A++) {
820
- const I = F.getAttribute("position"), k = Math.min(B[0].y, B[1].y), U = Math.max(B[0].y, B[1].y) - k;
821
- if (U < 1) break;
822
- const re = 30, Pe = U / re, me = [];
823
- for (let ye = 0; ye < re; ye++) {
824
- const Te = k + ye * Pe, Me = k + (ye + 1) * Pe;
825
- let Ae = 0, De = 0, Se = 0, Ee = 0;
826
- for (let ke = 0; ke < I.count; ke++) {
827
- const Ue = I.getY(ke);
828
- Ue >= Te && Ue < Me && (Ae += I.getX(ke), De += Ue, Se += I.getZ(ke), Ee++);
829
- }
830
- Ee > 20 && me.push(new h(Ae / Ee, De / Ee, Se / Ee));
831
- }
832
- if (me.length < 5) break;
833
- const ae = new h();
834
- for (const ye of me) ae.add(ye);
835
- ae.divideScalar(me.length);
836
- let xe = 0, Re = 0, pe = 0, we = 0, he = 0, Ge = 0;
837
- for (const ye of me) {
838
- const Te = ye.x - ae.x, Me = ye.y - ae.y, Ae = ye.z - ae.z;
839
- xe += Te * Te, Re += Te * Me, pe += Te * Ae, we += Me * Me, he += Me * Ae, Ge += Ae * Ae;
840
- }
841
- let fe = new h(0.01, 1, 0.01).normalize();
842
- for (let ye = 0; ye < 30; ye++) {
843
- const Te = xe * fe.x + Re * fe.y + pe * fe.z, Me = Re * fe.x + we * fe.y + he * fe.z, Ae = pe * fe.x + he * fe.y + Ge * fe.z, De = new h(Te, Me, Ae), Se = De.length();
844
- if (Se < 1e-10 || (De.divideScalar(Se), fe.distanceTo(De) < 1e-8)) break;
845
- fe = De;
846
- }
847
- fe.y < 0 && fe.negate();
848
- const Ne = fe.dot(_);
849
- if (Math.acos(Math.min(1, Math.abs(Ne))) * 180 / Math.PI < 0.1) break;
850
- const Le = new h().crossVectors(fe, _);
851
- Le.length() > 1e-4 && (Le.normalize(), T(Le, Math.acos(Math.min(1, Math.max(-1, Ne)))));
852
- }
853
- B[0].y > B[1].y && T(new h(1, 0, 0), Math.PI);
854
- const E = B[0];
855
- F.translate(-E.x, -E.y, -E.z), a && a.translate(-E.x, -E.y, -E.z), B = B.map((A) => new h(A.x - E.x, A.y - E.y, A.z - E.z));
856
- const X = B[1].y + 5 * We, Q = rt(F, new h(0, X, 0), new h(0, 1, 0), B[0]);
857
- e.geometry.dispose(), e.geometry = Q, Q.computeVertexNormals(), Q.computeBoundingBox(), i.updateLandmarkPositions(B.map((A) => ({
858
- x: A.x,
859
- y: A.y,
860
- z: A.z
861
- })));
862
- const ie = Q.boundingBox, se = new h();
863
- return ie.getSize(se), i.setModelSize(Math.max(se.x, se.y, se.z)), i.setCut(true), i.setAdjustedStartY(null), i.setAdjustedEndY(null), i.setOriginalEndY(B[1].y + 2 * We), B;
864
- }
865
- function Fn(e, s, n, t) {
866
- const i = s.map((m) => new h(m.position.x, m.position.y, m.position.z)), a = i[0], c = i[1];
867
- t.onStatus("Cutting double shell above green point...");
868
- const u = c.y + 2 * We, o = new h(c.x, u, c.z), l = e.geometry, r = rt(l, o, new h(0, 1, 0), a, true);
869
- l.dispose(), e.geometry = r, r.computeVertexNormals(), r.computeBoundingBox(), t.onStatus("Setting blue point..."), t.addLandmarkPoint({
870
- faceIndex: -1,
871
- vertexIndices: [
872
- 0,
873
- 1,
874
- 2
875
- ],
876
- position: {
877
- x: a.x,
878
- y: a.y,
879
- z: a.z
880
- },
881
- barycentricCoords: {
882
- u: 0.33,
883
- v: 0.33,
884
- w: 0.34
885
- }
886
- }), i.push(a.clone()), t.updateLandmarkPositions(i.map((m) => ({
887
- x: m.x,
888
- y: m.y,
889
- z: m.z
890
- }))), t.setAligned(true), t.setCut(true), t.setAdjustedStartY(null), t.setAdjustedEndY(null), t.setOriginalEndY(c.y);
891
- const f = r.boundingBox, y = new h();
892
- f.getSize(y), t.setModelSize(Math.max(y.x, y.y, y.z)), t.onStatus("Validating results...");
893
- const p = Vt(r, c.y, a.y, n);
894
- p.valid || console.warn(`Double shell validation warning: ${p.reason}`);
895
- }
896
- function In(e, s, n, t) {
897
- var _a, _b;
898
- const i = e.geometry.clone(), a = s.map((o) => ({
899
- ...o
900
- })), c = new h(0, 1, 0);
901
- if (t.skipDoubleShellDetection) console.log("Skipping double-shell detection: inner shell already extracted");
902
- else {
903
- const o = new h(s[0].position.x, s[0].position.y, s[0].position.z), l = new h(s[1].position.x, s[1].position.y, s[1].position.z);
904
- t.onStatus("Detecting shell type...");
905
- const r = Cn(e.geometry, o, l);
906
- if (console.log(`Shell detection: ${r.isDoubleShell ? "DOUBLE" : "SINGLE"} shell (${r.opposingPairs} inward/${r.sampledFaces} total faces, confidence ${(r.confidence * 100).toFixed(0)}%)`), r.isDoubleShell) {
907
- (_a = t.setDoubleShell) == null ? void 0 : _a.call(t, true), Fn(e, a, n, t), t.setClippedReferenceGeometry ? (i.computeVertexNormals(), t.setClippedReferenceGeometry(i)) : i.dispose();
908
- return;
909
- }
910
- (_b = t.setDoubleShell) == null ? void 0 : _b.call(t, false);
911
- }
912
- let u = "";
913
- for (let o = 0; o < ot; o++) try {
914
- o > 0 && (t.onStatus(`Retry ${o}/${ot - 1} \u2014 ${u}`), e.geometry.dispose(), e.geometry = i.clone(), t.removeLandmarkPoint(2), t.setAligned(false), t.setCut(false));
915
- let l = e.geometry, r = a.map((S) => new h(S.position.x, S.position.y, S.position.z));
916
- const f = t.setClippedReferenceGeometry ? i.clone() : null;
917
- let y = 5;
918
- if (o > 0) {
919
- const S = Mn[o - 1];
920
- y = S.cutOffset;
921
- const F = new h().subVectors(r[1], r[0]).normalize();
922
- if (S.nudge < 0) r[1].addScaledVector(F, S.nudge);
923
- else {
924
- const E = new h();
925
- Math.abs(F.x) < 0.9 ? E.crossVectors(F, new h(1, 0, 0)).normalize() : E.crossVectors(F, new h(0, 0, 1)).normalize(), E.applyAxisAngle(F, S.angle), r[1].addScaledVector(E, S.nudge);
926
- }
927
- const _ = l.getAttribute("position");
928
- let B = 1 / 0, T = 0;
929
- for (let E = 0; E < _.count; E++) {
930
- const j = _.getX(E) - r[1].x, X = _.getY(E) - r[1].y, Q = _.getZ(E) - r[1].z, ie = j * j + X * X + Q * Q;
931
- ie < B && (B = ie, T = E);
932
- }
933
- r[1].set(_.getX(T), _.getY(T), _.getZ(T)), console.log(`Retry ${o}: nudge=${S.nudge}mm angle=${(S.angle * 180 / Math.PI).toFixed(0)}\xB0 cutOffset=${S.cutOffset}" \u2192 green=(${r[1].x.toFixed(1)}, ${r[1].y.toFixed(1)}, ${r[1].z.toFixed(1)})`);
934
- }
935
- const p = (S, F) => {
936
- r = _t(l, r, S, F), f && Ot(f, S, F);
937
- }, m = r[0], g = r[1], { normal: z } = zn(l, g, m);
938
- t.onStatus(o > 0 ? `Retry ${o}: Slicing mesh...` : "Slicing mesh at cut plane...");
939
- const v = new h().subVectors(g, m).normalize(), x = Math.sign(v.dot(z)), b = g.clone().addScaledVector(z, x * y * We), w = rt(l, b, z, m);
940
- l.dispose(), l = w, e.geometry = l, t.onStatus(o > 0 ? `Retry ${o}: Rough alignment...` : "Rough alignment...");
941
- const C = new h().subVectors(r[1], r[0]).normalize();
942
- let M = C.dot(c), L = new h().crossVectors(C, c);
943
- L.length() > 1e-4 && (L.normalize(), p(L, Math.acos(Math.min(1, Math.max(-1, M))))), r[0].y > r[1].y && p(new h(1, 0, 0), Math.PI), l.computeVertexNormals(), t.onStatus(o > 0 ? `Retry ${o}: Translating...` : "Translating to origin...");
944
- const P = r[0].clone();
945
- l.translate(-P.x, -P.y, -P.z), f && f.translate(-P.x, -P.y, -P.z), r = r.map((S) => new h(S.x - P.x, S.y - P.y, S.z - P.z)), t.onStatus(o > 0 ? `Retry ${o}: Isolating limb...` : "Isolating limb region...");
946
- {
947
- const S = st(l), F = r[0].y, _ = r[1].y, B = new h(0, 1, 0), T = 5;
948
- l.computeBoundingBox();
949
- const E = l.boundingBox.max.y, j = [];
950
- for (let X = F + T; X < E; X += T) {
951
- const Q = ht(S, l, new h(0, X, 0), B);
952
- Q > 0 && j.push({
953
- y: X,
954
- circ: Q
955
- });
956
- }
957
- if (j.length > 5) {
958
- const X = _ - F, Q = F + X * 0.3, ie = F + X * 0.7, se = j.filter((A) => A.y >= Q && A.y <= ie);
959
- if (se.length >= 3) {
960
- const A = se.map((U) => U.circ).sort((U, re) => U - re), I = A[Math.floor(A.length / 2)], k = 1.6;
961
- let G = null;
962
- for (const U of j) if (!(U.y < _ - T * 2) && U.circ > I * k) {
963
- G = U.y - T;
964
- break;
965
- }
966
- if (G === null) {
967
- const U = j.find((re) => Math.abs(re.y - _) < T);
968
- if (U && U.circ > I * k) {
969
- for (let re = j.length - 1; re >= 0; re--) if (!(j[re].y > _ + T) && j[re].circ <= I * k) {
970
- G = j[re].y + T;
971
- break;
972
- }
973
- }
974
- }
975
- if (G !== null && G > F + X * 0.5) {
976
- console.log(`Limb isolation: cutting at Y=${G.toFixed(1)}mm (baseline circ=${I.toFixed(1)}mm, threshold=${(I * k).toFixed(1)}mm)`);
977
- const U = rt(l, new h(0, G, 0), B, r[0]);
978
- l.dispose(), l = U, e.geometry = l, l.computeVertexNormals(), l.computeBoundingBox();
979
- } else console.log(`Limb isolation: no anomaly detected (baseline=${I.toFixed(1)}mm)`);
980
- }
981
- }
982
- }
983
- t.onStatus(o > 0 ? `Retry ${o}: PCA alignment...` : "Iterative PCA alignment...");
984
- const H = 10, O = 80, $ = r[0].y, R = r[1].y;
985
- let Y = R - H;
986
- for (let S = 0; S < O; S++) {
987
- const F = Math.min(Y, R), _ = Math.max(Y, R), B = it(l, F, _);
988
- if (B && (M = B.dot(c), Math.acos(Math.min(1, Math.abs(M))) * 180 / Math.PI > 0.1 && (L = new h().crossVectors(B, c), L.length() > 1e-4))) {
989
- L.normalize(), p(L, Math.acos(Math.min(1, Math.max(-1, M))));
990
- const E = r[0].clone();
991
- l.translate(-E.x, -E.y, -E.z), f && f.translate(-E.x, -E.y, -E.z), r = r.map((j) => new h(j.x - E.x, j.y - E.y, j.z - E.z));
992
- }
993
- if (Y -= H, Y <= $) break;
994
- }
995
- l.computeVertexNormals(), t.onStatus(o > 0 ? `Retry ${o}: Setting blue point...` : "Setting blue point...");
996
- const N = new h(r[0].x, r[0].y, r[0].z);
997
- r.push(N), t.addLandmarkPoint({
998
- faceIndex: -1,
999
- vertexIndices: [
1000
- 0,
1001
- 1,
1002
- 2
1003
- ],
1004
- position: {
1005
- x: N.x,
1006
- y: N.y,
1007
- z: N.z
1008
- },
1009
- barycentricCoords: {
1010
- u: 0.33,
1011
- v: 0.33,
1012
- w: 0.34
1013
- }
1014
- }), t.onStatus(o > 0 ? `Retry ${o}: Final PCA...` : "Final PCA refinement...");
1015
- for (let S = 0; S < 3; S++) {
1016
- const F = it(l, 0, r[1].y);
1017
- if (F) {
1018
- if (M = F.dot(c), Math.acos(Math.min(1, Math.abs(M))) * 180 / Math.PI < 0.1) break;
1019
- if (L = new h().crossVectors(F, c), L.length() > 1e-4) {
1020
- L.normalize(), p(L, Math.acos(Math.min(1, Math.max(-1, M))));
1021
- const B = r[0].clone();
1022
- l.translate(-B.x, -B.y, -B.z), f && f.translate(-B.x, -B.y, -B.z), r = r.map((T) => new h(T.x - B.x, T.y - B.y, T.z - B.z));
1023
- }
1024
- }
1025
- }
1026
- if (r[1].y < r[0].y) {
1027
- p(new h(1, 0, 0), Math.PI);
1028
- const S = r[0].clone();
1029
- l.translate(-S.x, -S.y, -S.z), f && f.translate(-S.x, -S.y, -S.z), r = r.map((F) => new h(F.x - S.x, F.y - S.y, F.z - S.z));
1030
- }
1031
- l.computeVertexNormals(), l.computeBoundingBox(), t.onStatus(o > 0 ? `Retry ${o}: Circumference refinement...` : "Circumference refinement...");
1032
- {
1033
- const S = st(l), F = r[1].y, _ = r[0].y, B = [
1034
- F - We,
1035
- F - 2 * We,
1036
- F - 3 * We
1037
- ].filter((T) => T > _ + 5);
1038
- if (B.length >= 2) {
1039
- const T = Math.PI / 180, E = (I, k) => {
1040
- const G = new h(0, 1, 0);
1041
- Math.abs(I) > 1e-10 && G.applyQuaternion(new Oe().setFromAxisAngle(new h(1, 0, 0), I)), Math.abs(k) > 1e-10 && G.applyQuaternion(new Oe().setFromAxisAngle(new h(0, 0, 1), k));
1042
- let U = 0;
1043
- for (const re of B) U += ht(S, l, new h(0, re, 0), G);
1044
- return U;
1045
- };
1046
- let j = 0, X = 0, Q = E(0, 0);
1047
- for (let I = -3; I <= 3; I += 0.5) for (let k = -3; k <= 3; k += 0.5) {
1048
- const G = E(I * T, k * T);
1049
- G > 0 && G < Q && (Q = G, j = I * T, X = k * T);
1050
- }
1051
- const ie = j, se = X;
1052
- for (let I = -5; I <= 5; I++) for (let k = -5; k <= 5; k++) {
1053
- const G = ie + I * 0.1 * T, U = se + k * 0.1 * T, re = E(G, U);
1054
- re > 0 && re < Q && (Q = re, j = G, X = U);
1055
- }
1056
- const A = Math.sqrt(j * j + X * X);
1057
- if (A > 0.05 * T) {
1058
- console.log(`Circumference refinement: rotating ${(-j / T).toFixed(2)}\xB0X ${(-X / T).toFixed(2)}\xB0Z`), Math.abs(j) > 0.01 * T && p(new h(1, 0, 0), -j), Math.abs(X) > 0.01 * T && p(new h(0, 0, 1), -X);
1059
- const I = r[0].clone();
1060
- l.translate(-I.x, -I.y, -I.z), f && f.translate(-I.x, -I.y, -I.z), r = r.map((k) => new h(k.x - I.x, k.y - I.y, k.z - I.z)), l.computeVertexNormals();
1061
- } else console.log(`Circumference refinement: already optimal (${(A / T).toFixed(3)}\xB0)`);
1062
- }
1063
- }
1064
- const ne = r.map((S) => ({
1065
- x: S.x,
1066
- y: S.y,
1067
- z: S.z
1068
- }));
1069
- t.updateLandmarkPositions(ne), t.setAligned(true), t.onStatus(o > 0 ? `Retry ${o}: Cutting mesh...` : 'Cutting mesh 2" above green...'), r = An(e, r, l, 5, t, f), t.onStatus("Validating results...");
1070
- const oe = e.geometry, le = r[1].y, ee = r[0].y, D = Vt(oe, le, ee, n);
1071
- if (D.valid) {
1072
- console.log(`Processing succeeded on attempt ${o + 1}`), f && t.setClippedReferenceGeometry && (f.computeVertexNormals(), t.setClippedReferenceGeometry(f));
1073
- break;
1074
- }
1075
- console.warn(`Attempt ${o + 1} failed validation: ${D.reason}`), u = D.reason, f && f.dispose(), o === ot - 1 && (t.setError(`Processing produced unusual results after ${ot} attempts: ${D.reason}`), e.geometry.dispose(), e.geometry = i.clone(), t.removeLandmarkPoint(2), t.setAligned(false), t.setCut(false));
1076
- } catch (l) {
1077
- console.error("Processing failed:", l), t.setError(l instanceof Error ? l.message : "Failed to process mesh.");
1078
- break;
1079
- }
1080
- i.dispose();
1081
- }
1082
- const Pn = ({ message: e, onDismiss: s }) => V("div", {
1083
- style: {
1084
- position: "absolute",
1085
- top: 16,
1086
- right: 16,
1087
- padding: "12px 16px",
1088
- backgroundColor: "rgba(220, 53, 69, 0.95)",
1089
- borderRadius: 8,
1090
- color: "#fff",
1091
- fontSize: 14,
1092
- maxWidth: 300,
1093
- zIndex: 100,
1094
- display: "flex",
1095
- alignItems: "flex-start",
1096
- gap: 12
1097
- },
1098
- children: [
1099
- V("div", {
1100
- style: {
1101
- flex: 1
1102
- },
1103
- children: [
1104
- d("div", {
1105
- style: {
1106
- fontWeight: "bold",
1107
- marginBottom: 4
1108
- },
1109
- children: "Error"
1110
- }),
1111
- d("div", {
1112
- style: {
1113
- fontSize: 12,
1114
- opacity: 0.9
1115
- },
1116
- children: e
1117
- })
1118
- ]
1119
- }),
1120
- d("button", {
1121
- onClick: s,
1122
- style: {
1123
- background: "none",
1124
- border: "none",
1125
- color: "#fff",
1126
- cursor: "pointer",
1127
- fontSize: 18,
1128
- padding: 0,
1129
- lineHeight: 1,
1130
- opacity: 0.7
1131
- },
1132
- children: "x"
1133
- })
1134
- ]
1135
- }), Ft = ({ message: e }) => V("div", {
1136
- style: {
1137
- position: "absolute",
1138
- top: 0,
1139
- left: 0,
1140
- right: 0,
1141
- bottom: 0,
1142
- backgroundColor: "rgba(0, 0, 0, 0.7)",
1143
- display: "flex",
1144
- flexDirection: "column",
1145
- alignItems: "center",
1146
- justifyContent: "center",
1147
- zIndex: 100
1148
- },
1149
- children: [
1150
- d("div", {
1151
- style: {
1152
- width: 48,
1153
- height: 48,
1154
- border: "4px solid rgba(255, 255, 255, 0.2)",
1155
- borderTopColor: "#4a90d9",
1156
- borderRadius: "50%",
1157
- animation: "spin 1s linear infinite"
1158
- }
1159
- }),
1160
- e && d("div", {
1161
- style: {
1162
- marginTop: 16,
1163
- color: "#fff",
1164
- fontSize: 14
1165
- },
1166
- children: e
1167
- }),
1168
- d("style", {
1169
- children: "@keyframes spin { to { transform: rotate(360deg); } }"
1170
- })
1171
- ]
1172
- }), Ln = ({ mesh: e, maxPoints: s = 2, meshColor: n = "#c8c8c8", meshOpacity: t = 1 }) => {
1173
- const { addLandmarkPoint: i, landmarkPoints: a } = mt(), c = Fe((l) => {
1174
- if (a.length >= s) return;
1175
- l.stopPropagation();
1176
- const r = l.intersections[0], f = r == null ? void 0 : r.faceIndex;
1177
- if (!r || f == null) return;
1178
- const y = e.geometry, p = y.index;
1179
- let m;
1180
- p ? m = [
1181
- p.getX(f * 3),
1182
- p.getX(f * 3 + 1),
1183
- p.getX(f * 3 + 2)
1184
- ] : m = [
1185
- f * 3,
1186
- f * 3 + 1,
1187
- f * 3 + 2
1188
- ];
1189
- const g = y.getAttribute("position"), z = new h().fromBufferAttribute(g, m[0]), v = new h().fromBufferAttribute(g, m[1]), x = new h().fromBufferAttribute(g, m[2]);
1190
- z.applyMatrix4(e.matrixWorld), v.applyMatrix4(e.matrixWorld), x.applyMatrix4(e.matrixWorld);
1191
- const b = r.point, w = vn(b, z, v, x), C = {
1192
- faceIndex: f,
1193
- vertexIndices: m,
1194
- position: {
1195
- x: b.x,
1196
- y: b.y,
1197
- z: b.z
1198
- },
1199
- barycentricCoords: w
1200
- };
1201
- i(C);
1202
- }, [
1203
- e,
1204
- i,
1205
- a.length,
1206
- s
1207
- ]), u = de(() => !!e.geometry.getAttribute("color"), [
1208
- e
1209
- ]), o = de(() => new J.MeshStandardMaterial({
1210
- color: u ? "#ffffff" : n,
1211
- side: J.DoubleSide,
1212
- roughness: 0.6,
1213
- metalness: 0.1,
1214
- transparent: t < 1,
1215
- opacity: t,
1216
- vertexColors: u
1217
- }), [
1218
- n,
1219
- t,
1220
- u
1221
- ]);
1222
- return d("primitive", {
1223
- object: e,
1224
- onClick: c,
1225
- material: o
1226
- });
1227
- }, Dn = ({ point: e, index: s, markerSize: n, color: t, label: i }) => {
1228
- const [a, c] = q(false);
1229
- return V("mesh", {
1230
- position: [
1231
- e.position.x,
1232
- e.position.y,
1233
- e.position.z
1234
- ],
1235
- onPointerOver: () => c(true),
1236
- onPointerOut: () => c(false),
1237
- children: [
1238
- d("sphereGeometry", {
1239
- args: [
1240
- n,
1241
- 16,
1242
- 16
1243
- ]
1244
- }),
1245
- d("meshBasicMaterial", {
1246
- color: t
1247
- }),
1248
- a && d(Ke, {
1249
- center: true,
1250
- style: {
1251
- pointerEvents: "none"
1252
- },
1253
- children: d("div", {
1254
- style: {
1255
- padding: "3px 8px",
1256
- backgroundColor: "rgba(0, 0, 0, 0.75)",
1257
- borderRadius: 4,
1258
- color: "#fff",
1259
- fontSize: 12,
1260
- fontFamily: "system-ui, sans-serif",
1261
- whiteSpace: "nowrap",
1262
- transform: "translateY(-24px)"
1263
- },
1264
- children: i
1265
- })
1266
- })
1267
- ]
1268
- }, s);
1269
- }, Tn = ({ modelSize: e, labels: s }) => {
1270
- const { landmarkPoints: n } = mt(), t = e * 0.02, i = [
1271
- "#ff4444",
1272
- "#44ff44",
1273
- "#4444ff"
1274
- ], c = s ?? [
1275
- "Origin",
1276
- "MPT",
1277
- "Cut Plane"
1278
- ];
1279
- return d(He, {
1280
- children: n.map((u, o) => d(Dn, {
1281
- point: u,
1282
- index: o,
1283
- markerSize: t,
1284
- color: i[o],
1285
- label: c[o]
1286
- }, o))
1287
- });
1288
- };
1289
- function kn(e) {
1290
- return de(() => e ? new $e(e, {
1291
- maxLeafTris: Ye
1292
- }) : null, [
1293
- e
1294
- ]);
1295
- }
1296
- const It = (e, s) => {
1297
- const n = Math.abs(e - s);
1298
- return n < 1 ? "#8BC34A" : n < 5 ? "#FFC107" : "#FF5722";
1299
- }, Pt = new J.Color("#8BC34A"), Lt = new J.Color("#FFC107"), Bn = new J.Color("#FF5722"), Rn = (e) => {
1300
- if (e < 1) return Pt.clone();
1301
- if (e < 5) {
1302
- const n = (e - 1) / 4;
1303
- return Pt.clone().lerp(Lt, n);
1304
- }
1305
- const s = Math.min((e - 5) / 5, 1);
1306
- return Lt.clone().lerp(Bn, s);
1307
- }, En = (e, s) => {
1308
- const n = e.length, t = new Float32Array(n * 2 * 3), i = new Float32Array(n * 2 * 3), a = [];
1309
- for (let o = 0; o < n; o++) {
1310
- const l = e[o], r = s[o], f = l.distanceTo(r), y = Rn(f);
1311
- if (t[o * 6] = l.x, t[o * 6 + 1] = l.y, t[o * 6 + 2] = l.z, i[o * 6] = y.r, i[o * 6 + 1] = y.g, i[o * 6 + 2] = y.b, t[o * 6 + 3] = r.x, t[o * 6 + 4] = r.y, t[o * 6 + 5] = r.z, i[o * 6 + 3] = y.r, i[o * 6 + 4] = y.g, i[o * 6 + 5] = y.b, o < n - 1) {
1312
- const p = o * 2, m = p + 1, g = (o + 1) * 2, z = g + 1;
1313
- a.push(p, m, g, m, z, g);
1314
- }
1315
- }
1316
- const c = new J.BufferGeometry();
1317
- c.setAttribute("position", new J.Float32BufferAttribute(t, 3)), c.setAttribute("color", new J.Float32BufferAttribute(i, 3)), c.setIndex(a);
1318
- const u = new J.MeshBasicMaterial({
1319
- vertexColors: true,
1320
- transparent: true,
1321
- opacity: 0.25,
1322
- side: J.DoubleSide,
1323
- depthTest: false,
1324
- depthWrite: false
1325
- });
1326
- return new J.Mesh(c, u);
1327
- }, Wn = ({ bvh: e, geometry: s, yPosition: n, color: t = "#00ff00", labelX: i, onDataChange: a, displayUnit: c = "mm", useInnerSurface: u = false, formValue: o, lineWidth: l = 1.5 }) => {
1328
- const r = de(() => Ze(e, s, n, u), [
1329
- e,
1330
- s,
1331
- n,
1332
- u
1333
- ]), { linePoints: f, lineLength: y } = r, p = de(() => {
1334
- if (o == null || y <= 0 || f.length < 2) return null;
1335
- const v = o / y, x = f.reduce((w, C) => w + C.x, 0) / f.length, b = f.reduce((w, C) => w + C.z, 0) / f.length;
1336
- return f.map((w) => new h(x + (w.x - x) * v, w.y, b + (w.z - b) * v));
1337
- }, [
1338
- f,
1339
- y,
1340
- o
1341
- ]), m = de(() => !p || f.length < 2 ? null : En(f, p), [
1342
- f,
1343
- p
1344
- ]);
1345
- Ce(() => () => {
1346
- m && (m.geometry.dispose(), m.material.dispose());
1347
- }, [
1348
- m
1349
- ]);
1350
- const g = Ie(null), z = de(() => {
1351
- const v = new J.BufferGeometry();
1352
- v.setAttribute("position", new J.Float32BufferAttribute(new Float32Array(6), 3));
1353
- const x = new J.LineBasicMaterial({
1354
- color: 6710886,
1355
- depthTest: false,
1356
- depthWrite: false,
1357
- transparent: true
1358
- });
1359
- return new J.Line(v, x);
1360
- }, []);
1361
- return Ce(() => () => {
1362
- z.geometry.dispose(), z.material.dispose();
1363
- }, [
1364
- z
1365
- ]), Ce(() => {
1366
- y > 0 && (a == null ? void 0 : a({
1367
- yPosition: n,
1368
- originalValue: y,
1369
- modifiedValue: null
1370
- }));
1371
- }, [
1372
- y,
1373
- n,
1374
- a
1375
- ]), Dt(({ camera: v }) => {
1376
- if (!g.current || f.length < 2) return;
1377
- const x = new h();
1378
- v.getWorldDirection(x);
1379
- const b = new h(x.x, 0, x.z);
1380
- if (b.lengthSq() < 1e-8) return;
1381
- b.normalize();
1382
- const w = new h().crossVectors(b, new h(0, 1, 0)).normalize();
1383
- let C = -1 / 0, M = f[0];
1384
- for (const O of f) {
1385
- const $ = w.x * O.x + w.z * O.z;
1386
- $ > C && (C = $, M = O);
1387
- }
1388
- const L = i * 0.35, P = new h(M.x + w.x * L, n, M.z + w.z * L);
1389
- g.current.position.copy(P);
1390
- const H = z.geometry.getAttribute("position");
1391
- H.setXYZ(0, M.x, M.y, M.z), H.setXYZ(1, P.x, P.y, P.z), H.needsUpdate = true;
1392
- }), f.length < 2 ? null : V("group", {
1393
- children: [
1394
- d(ge, {
1395
- points: f,
1396
- color: t,
1397
- lineWidth: l,
1398
- depthTest: false,
1399
- depthWrite: false,
1400
- transparent: true
1401
- }),
1402
- m && d("primitive", {
1403
- object: m
1404
- }),
1405
- p && o != null && d(ge, {
1406
- points: p,
1407
- color: It(y, o),
1408
- lineWidth: 2.5,
1409
- dashed: true,
1410
- dashSize: 2,
1411
- gapSize: 1.5,
1412
- depthTest: false,
1413
- depthWrite: false,
1414
- transparent: true,
1415
- opacity: 0.8
1416
- }),
1417
- d("primitive", {
1418
- object: z
1419
- }),
1420
- d("group", {
1421
- ref: g,
1422
- children: d(Ke, {
1423
- zIndexRange: [
1424
- 100,
1425
- 0
1426
- ],
1427
- style: {
1428
- pointerEvents: "none",
1429
- transform: "translateY(-50%)"
1430
- },
1431
- children: V("div", {
1432
- style: {
1433
- display: "flex",
1434
- alignItems: "stretch",
1435
- gap: 0,
1436
- marginLeft: 10,
1437
- pointerEvents: "none",
1438
- whiteSpace: "nowrap"
1439
- },
1440
- children: [
1441
- V("div", {
1442
- style: {
1443
- display: "flex",
1444
- alignItems: "center",
1445
- gap: 4,
1446
- padding: "5px 10px",
1447
- backgroundColor: "rgba(0, 0, 0, 0.75)",
1448
- borderRadius: o != null ? "4px 0 0 4px" : 4
1449
- },
1450
- children: [
1451
- d("span", {
1452
- style: {
1453
- fontSize: 14,
1454
- color: "#fff",
1455
- fontFamily: "monospace",
1456
- minWidth: 52,
1457
- textAlign: "right"
1458
- },
1459
- children: c === "inch" ? (y / 25.4).toFixed(2) : y.toFixed(1)
1460
- }),
1461
- d("span", {
1462
- style: {
1463
- fontSize: 11,
1464
- color: "rgba(255,255,255,0.6)",
1465
- fontFamily: "monospace"
1466
- },
1467
- children: c === "inch" ? "in" : "mm"
1468
- })
1469
- ]
1470
- }),
1471
- o != null && y > 0 && (() => {
1472
- const v = y - o, x = v > 0.5 ? "\u25B2" : v < -0.5 ? "\u25BC" : "", b = It(y, o);
1473
- return V("div", {
1474
- style: {
1475
- display: "flex",
1476
- alignItems: "center",
1477
- gap: 5,
1478
- padding: "5px 10px",
1479
- backgroundColor: "rgba(0, 0, 0, 0.55)",
1480
- borderRadius: "0 4px 4px 0",
1481
- borderLeft: "1px solid rgba(255,255,255,0.12)"
1482
- },
1483
- children: [
1484
- x && d("span", {
1485
- style: {
1486
- fontSize: 10,
1487
- color: b,
1488
- lineHeight: 1
1489
- },
1490
- children: x
1491
- }),
1492
- V("span", {
1493
- style: {
1494
- fontSize: 13,
1495
- color: b,
1496
- fontFamily: "monospace",
1497
- fontWeight: 600
1498
- },
1499
- children: [
1500
- v > 0 ? "+" : "",
1501
- c === "inch" ? (v / 25.4).toFixed(2) : v.toFixed(1)
1502
- ]
1503
- }),
1504
- V("span", {
1505
- style: {
1506
- fontSize: 11,
1507
- color: "rgba(255,255,255,0.4)",
1508
- fontFamily: "monospace"
1509
- },
1510
- children: [
1511
- "form ",
1512
- c === "inch" ? (o / 25.4).toFixed(2) : o.toFixed(0)
1513
- ]
1514
- })
1515
- ]
1516
- });
1517
- })()
1518
- ]
1519
- })
1520
- })
1521
- })
1522
- ]
1523
- });
1524
- }, Vn = nn(Wn), _n = ({ mesh: e, startY: s, endY: n, spacing: t, modelSize: i, onMeasurementsChange: a, reverseOrder: c = false, displayUnit: u = "mm", useInnerSurface: o = false, formMeasurements: l, originY: r }) => {
1525
- const f = Ie(/* @__PURE__ */ new Map()), y = e.geometry, p = kn(y), m = de(() => {
1526
- const x = [];
1527
- if (c) for (let b = n; b >= s; b -= t) x.push(b);
1528
- else for (let b = s; b <= n; b += t) x.push(b);
1529
- return x;
1530
- }, [
1531
- s,
1532
- n,
1533
- t,
1534
- c
1535
- ]);
1536
- Ce(() => {
1537
- f.current.clear();
1538
- }, [
1539
- m
1540
- ]);
1541
- const g = [
1542
- "#5B9BD5"
1543
- ], z = i * un, v = Fe((x) => {
1544
- f.current.set(x.yPosition, x);
1545
- const b = Array.from(f.current.values()).sort((w, C) => c ? C.yPosition - w.yPosition : w.yPosition - C.yPosition);
1546
- a == null ? void 0 : a(b);
1547
- }, [
1548
- a,
1549
- c
1550
- ]);
1551
- return p ? d(He, {
1552
- children: m.map((x, b) => d(Vn, {
1553
- bvh: p,
1554
- geometry: y,
1555
- yPosition: x,
1556
- color: r != null && Math.abs(x - r) < t * 0.5 ? "#44ff44" : g[b % g.length],
1557
- labelX: z,
1558
- onDataChange: v,
1559
- displayUnit: u,
1560
- useInnerSurface: o,
1561
- formValue: l == null ? void 0 : l[b],
1562
- lineWidth: r != null && Math.abs(x - r) < t * 0.5 ? 4 : 1.5
1563
- }, x))
1564
- }) : null;
1565
- }, On = ({ mesh: e, greenY: s, modelSize: n, displayUnit: t = "mm" }) => {
1566
- var _a;
1567
- const i = e.geometry;
1568
- i.computeBoundingBox();
1569
- const a = ((_a = i.boundingBox) == null ? void 0 : _a.min.y) ?? 0, c = s - a, u = n * 0.4, o = n * 0.03, l = Ie(null);
1570
- Dt(({ camera: v }) => {
1571
- if (!l.current) return;
1572
- const x = new h();
1573
- v.getWorldDirection(x);
1574
- const b = new h(x.x, 0, x.z);
1575
- if (b.lengthSq() < 1e-8) return;
1576
- b.normalize();
1577
- const w = new h().crossVectors(new h(0, 1, 0), b).normalize();
1578
- l.current.position.set(w.x * u, 0, w.z * u);
1579
- const C = v.position.x - l.current.position.x, M = v.position.z - l.current.position.z;
1580
- l.current.rotation.y = Math.atan2(C, M);
1581
- });
1582
- const r = new h(0, s, 0), f = new h(0, a, 0), y = new h(0, (s + a) / 2, 0), p = new h(-o, s, 0), m = new h(o, s, 0), g = new h(-o, a, 0), z = new h(o, a, 0);
1583
- return V("group", {
1584
- ref: l,
1585
- children: [
1586
- d(ge, {
1587
- points: [
1588
- r,
1589
- f
1590
- ],
1591
- color: "#888888",
1592
- lineWidth: 1.5,
1593
- depthTest: false
1594
- }),
1595
- d(ge, {
1596
- points: [
1597
- p,
1598
- m
1599
- ],
1600
- color: "#888888",
1601
- lineWidth: 1.5,
1602
- depthTest: false
1603
- }),
1604
- d(ge, {
1605
- points: [
1606
- g,
1607
- z
1608
- ],
1609
- color: "#888888",
1610
- lineWidth: 1.5,
1611
- depthTest: false
1612
- }),
1613
- d("mesh", {
1614
- position: y,
1615
- children: d(Ke, {
1616
- center: true,
1617
- style: {
1618
- pointerEvents: "none"
1619
- },
1620
- children: V("div", {
1621
- style: {
1622
- padding: "4px 8px",
1623
- backgroundColor: "rgba(0, 0, 0, 0.7)",
1624
- borderRadius: 4,
1625
- color: "#fff",
1626
- fontSize: 16,
1627
- fontFamily: "monospace",
1628
- whiteSpace: "nowrap",
1629
- transform: "rotate(-90deg)",
1630
- transformOrigin: "center center",
1631
- pointerEvents: "none"
1632
- },
1633
- children: [
1634
- t === "inch" ? (c / 25.4).toFixed(2) : c.toFixed(1),
1635
- " ",
1636
- t === "inch" ? "in" : "mm"
1637
- ]
1638
- })
1639
- })
1640
- })
1641
- ]
1642
- });
1643
- }, $n = ({ modelSize: e, isAligned: s, isCut: n, mesh: t, viewMode: i, sliceY: a, landmarkCount: c = 0 }) => {
1644
- const { set: u, size: o, camera: l, invalidate: r } = on(), f = Ie(false), y = Ie(s), p = Ie(n), m = Ie(i), g = Ie(new h()), z = Ie(null), v = Ie(c), x = Fe(() => {
1645
- if (!t || e <= 0) return;
1646
- const w = t.geometry;
1647
- w.computeBoundingBox();
1648
- const C = w.boundingBox, M = new h();
1649
- C.getCenter(M);
1650
- const L = new h();
1651
- C.getSize(L), g.current.copy(L);
1652
- const P = o.width / o.height, H = Math.max(L.y, L.x / P) * 1.2, O = H * P, $ = new J.OrthographicCamera(-O / 2, O / 2, H / 2, -H / 2, 0.1, e * 10);
1653
- $.position.set(0, M.y, e * 2), $.lookAt(0, M.y, 0), u({
1654
- camera: $
1655
- });
1656
- }, [
1657
- t,
1658
- e,
1659
- o,
1660
- u
1661
- ]), b = Fe((w) => {
1662
- const C = w.position.clone(), M = C.length(), L = Math.atan2(C.x, C.z), P = Math.acos(C.y / M), O = L + 0.02;
1663
- w.position.set(M * Math.sin(P) * Math.sin(O), M * Math.cos(P), M * Math.sin(P) * Math.cos(O)), w.lookAt(0, 0, 0), w.updateMatrixWorld(true), r();
1664
- }, [
1665
- r
1666
- ]);
1667
- return Ce(() => {
1668
- if (e > 0 && !f.current && !s) {
1669
- f.current = true;
1670
- const w = new J.PerspectiveCamera(50, o.width / o.height, 0.1, e * 10);
1671
- w.position.set(e * 0.3, e * 0.2, e * 1.5), w.lookAt(0, 0, 0), u({
1672
- camera: w
1673
- }), requestAnimationFrame(() => b(w));
1674
- }
1675
- }, [
1676
- e,
1677
- o,
1678
- u,
1679
- s,
1680
- b
1681
- ]), Ce(() => {
1682
- const w = v.current;
1683
- v.current = c, w === 0 && c === 1 && !s && requestAnimationFrame(() => b(l));
1684
- }, [
1685
- c,
1686
- s,
1687
- l,
1688
- b
1689
- ]), Ce(() => {
1690
- y.current === s && p.current === n || (y.current = s, p.current = n, s && i === "3D" && x());
1691
- }, [
1692
- s,
1693
- n,
1694
- i,
1695
- x
1696
- ]), Ce(() => {
1697
- if (m.current === i) return;
1698
- const w = m.current;
1699
- if (m.current = i, !(!s || !t || e <= 0)) if (i === "2D" && a != null) {
1700
- z.current = l;
1701
- const C = t.geometry, M = C.getAttribute("position"), L = M.array, P = e * 0.15;
1702
- let H = 1 / 0, O = -1 / 0, $ = 1 / 0, R = -1 / 0, Y = false;
1703
- for (let B = 0; B < M.count; B++) if (Math.abs(L[B * 3 + 1] - a) < P) {
1704
- const T = L[B * 3], E = L[B * 3 + 2];
1705
- T < H && (H = T), T > O && (O = T), E < $ && ($ = E), E > R && (R = E), Y = true;
1706
- }
1707
- if (!Y) {
1708
- C.computeBoundingBox();
1709
- const B = C.boundingBox;
1710
- H = B.min.x, O = B.max.x, $ = B.min.z, R = B.max.z;
1711
- }
1712
- const N = (H + O) / 2, ne = ($ + R) / 2, oe = o.width / o.height, le = 1.4, ee = (O - H) * le, D = (R - $) * le;
1713
- let S, F;
1714
- ee / D > oe ? (S = ee, F = ee / oe) : (F = D, S = D * oe);
1715
- const _ = new J.OrthographicCamera(-S / 2, S / 2, F / 2, -F / 2, 0.1, e * 10);
1716
- _.position.set(N, a + e * 2, ne), _.up.set(0, 0, -1), _.lookAt(N, a, ne), u({
1717
- camera: _
1718
- });
1719
- } else w === "2D" && (z.current ? (u({
1720
- camera: z.current
1721
- }), z.current = null) : x());
1722
- }, [
1723
- i,
1724
- a,
1725
- s,
1726
- t,
1727
- e,
1728
- o,
1729
- u,
1730
- l,
1731
- x
1732
- ]), Ce(() => {
1733
- if (!s || !l || !l.isOrthographicCamera) return;
1734
- const w = l;
1735
- if (i === "2D") {
1736
- if (!t || a == null) return;
1737
- const C = t.geometry, M = C.getAttribute("position"), L = M.array, P = e * 0.15;
1738
- let H = 1 / 0, O = -1 / 0, $ = 1 / 0, R = -1 / 0, Y = false;
1739
- for (let S = 0; S < M.count; S++) if (Math.abs(L[S * 3 + 1] - a) < P) {
1740
- const F = L[S * 3], _ = L[S * 3 + 2];
1741
- F < H && (H = F), F > O && (O = F), _ < $ && ($ = _), _ > R && (R = _), Y = true;
1742
- }
1743
- if (!Y) {
1744
- C.computeBoundingBox();
1745
- const S = C.boundingBox;
1746
- H = S.min.x, O = S.max.x, $ = S.min.z, R = S.max.z;
1747
- }
1748
- const N = o.width / o.height, ne = 1.4, oe = (O - H) * ne, le = (R - $) * ne;
1749
- let ee, D;
1750
- oe / le > N ? (ee = oe, D = oe / N) : (D = le, ee = le * N), w.left = -ee / 2, w.right = ee / 2, w.top = D / 2, w.bottom = -D / 2;
1751
- } else {
1752
- const C = g.current, M = o.width / o.height, L = Math.max(C.y, C.x / M) * 1.2, P = L * M;
1753
- w.left = -P / 2, w.right = P / 2, w.top = L / 2, w.bottom = -L / 2;
1754
- }
1755
- w.updateProjectionMatrix();
1756
- }, [
1757
- o.width,
1758
- o.height,
1759
- s,
1760
- l,
1761
- i,
1762
- t
1763
- ]), null;
1764
- }, Nn = ({ mesh: e, isDragging: s }) => {
1765
- var _a;
1766
- const n = e.geometry;
1767
- n.computeBoundingBox();
1768
- const t = new h();
1769
- return (_a = n.boundingBox) == null ? void 0 : _a.getCenter(t), d(Tt, {
1770
- enableDamping: false,
1771
- enablePan: false,
1772
- minPolarAngle: Math.PI * 0.15,
1773
- maxPolarAngle: Math.PI * 0.85,
1774
- minZoom: 0.5,
1775
- maxZoom: 3,
1776
- enabled: !s,
1777
- target: [
1778
- t.x,
1779
- t.y,
1780
- t.z
1781
- ]
1782
- });
1783
- }, jn = ({ wasAutoScaled: e, onDismiss: s }) => V("div", {
1784
- style: {
1785
- position: "absolute",
1786
- bottom: 16,
1787
- left: 16,
1788
- zIndex: 10,
1789
- display: "flex",
1790
- alignItems: "flex-start",
1791
- gap: 10,
1792
- padding: "10px 14px",
1793
- maxWidth: 320,
1794
- backgroundColor: e ? "rgba(30, 70, 160, 0.92)" : "rgba(40, 40, 40, 0.88)",
1795
- borderRadius: 8,
1796
- boxShadow: "0 2px 12px rgba(0,0,0,0.15)",
1797
- fontFamily: "system-ui, sans-serif"
1798
- },
1799
- children: [
1800
- d("span", {
1801
- style: {
1802
- fontSize: 13,
1803
- color: "#fff",
1804
- lineHeight: "20px"
1805
- },
1806
- children: e ? "Units detected as meters - converted to mm" : "Units detected as mm"
1807
- }),
1808
- d("button", {
1809
- onClick: s,
1810
- style: {
1811
- background: "none",
1812
- border: "none",
1813
- color: "rgba(255,255,255,0.7)",
1814
- cursor: "pointer",
1815
- fontSize: 16,
1816
- lineHeight: "20px",
1817
- padding: 0,
1818
- marginLeft: "auto",
1819
- flexShrink: 0
1820
- },
1821
- children: "X"
1822
- })
1823
- ]
1824
- }), Hn = ({ isDoubleShell: e, onDismiss: s }) => V("div", {
1825
- style: {
1826
- position: "absolute",
1827
- bottom: 68,
1828
- left: 16,
1829
- zIndex: 10,
1830
- display: "flex",
1831
- alignItems: "flex-start",
1832
- gap: 10,
1833
- padding: "10px 14px",
1834
- maxWidth: 320,
1835
- backgroundColor: e ? "rgba(130, 80, 20, 0.92)" : "rgba(40, 40, 40, 0.88)",
1836
- borderRadius: 8,
1837
- boxShadow: "0 2px 12px rgba(0,0,0,0.15)",
1838
- fontFamily: "system-ui, sans-serif"
1839
- },
1840
- children: [
1841
- d("span", {
1842
- style: {
1843
- fontSize: 13,
1844
- color: "#fff",
1845
- lineHeight: "20px"
1846
- },
1847
- children: e ? "Double shell scan detected" : "Single shell scan detected"
1848
- }),
1849
- d("button", {
1850
- onClick: s,
1851
- style: {
1852
- background: "none",
1853
- border: "none",
1854
- color: "rgba(255,255,255,0.7)",
1855
- cursor: "pointer",
1856
- fontSize: 16,
1857
- lineHeight: "20px",
1858
- padding: 0,
1859
- marginLeft: "auto",
1860
- flexShrink: 0
1861
- },
1862
- children: "X"
1863
- })
1864
- ]
1865
- }), Yn = ({ steps: e, currentStep: s, accentColor: n = "rgb(12, 67, 173)" }) => d("div", {
1866
- style: {
1867
- backgroundColor: "#fff",
1868
- borderBottom: "1px solid #e0e0e0",
1869
- display: "flex",
1870
- alignItems: "center",
1871
- padding: "24px 24px",
1872
- flexShrink: 0
1873
- },
1874
- children: e.map((t, i) => {
1875
- const a = t.number < s, c = t.number === s;
1876
- return V("div", {
1877
- style: {
1878
- display: "contents"
1879
- },
1880
- children: [
1881
- V("div", {
1882
- style: {
1883
- display: "flex",
1884
- alignItems: "center",
1885
- gap: 8,
1886
- padding: "0 8px",
1887
- flexShrink: 0,
1888
- cursor: "default"
1889
- },
1890
- children: [
1891
- d("div", {
1892
- style: {
1893
- width: 24,
1894
- height: 24,
1895
- borderRadius: "50%",
1896
- backgroundColor: a || c ? n : "rgba(0, 0, 0, 0.38)",
1897
- color: "#fff",
1898
- display: "flex",
1899
- alignItems: "center",
1900
- justifyContent: "center",
1901
- fontSize: 12,
1902
- fontFamily: "system-ui, sans-serif",
1903
- flexShrink: 0
1904
- },
1905
- children: a ? "\u2713" : t.number
1906
- }),
1907
- d("div", {
1908
- style: {
1909
- fontSize: 14,
1910
- fontWeight: c ? 600 : 400,
1911
- color: c ? "rgba(0, 0, 0, 0.87)" : "rgba(0, 0, 0, 0.54)",
1912
- fontFamily: "system-ui, sans-serif",
1913
- whiteSpace: "nowrap"
1914
- },
1915
- children: t.label
1916
- })
1917
- ]
1918
- }),
1919
- i < e.length - 1 && d("div", {
1920
- style: {
1921
- flex: "auto",
1922
- borderTop: "1px solid #bdbdbd",
1923
- margin: "0 8px"
1924
- }
1925
- })
1926
- ]
1927
- }, t.number);
1928
- })
1929
- }), Gn = ({ mesh: e, upperY: s, originY: n, modelSize: t, meshColor: i = "#c8c8c8", displayUnit: a = "mm" }) => {
1930
- const c = e.geometry, u = de(() => new $e(c, {
1931
- maxLeafTris: Ye
1932
- }), [
1933
- c
1934
- ]), o = de(() => Ze(u, c, n), [
1935
- u,
1936
- c,
1937
- n
1938
- ]), l = de(() => new lt(new h(0, -1, 0), s), [
1939
- s
1940
- ]), { mlLine: r, apLine: f, mlWidth: y, apWidth: p } = de(() => {
1941
- let z = null, v = null, x = 0, b = 0;
1942
- if (o.linePoints.length >= 2) {
1943
- let w = o.linePoints[0], C = o.linePoints[0], M = o.linePoints[0], L = o.linePoints[0];
1944
- for (const P of o.linePoints) P.x < w.x && (w = P), P.x > C.x && (C = P), P.z < M.z && (M = P), P.z > L.z && (L = P);
1945
- z = [
1946
- new h(w.x, n, w.z),
1947
- new h(C.x, n, C.z)
1948
- ], v = [
1949
- new h(M.x, n, M.z),
1950
- new h(L.x, n, L.z)
1951
- ], x = z[0].distanceTo(z[1]), b = v[0].distanceTo(v[1]);
1952
- }
1953
- return {
1954
- mlLine: z,
1955
- apLine: v,
1956
- mlWidth: x,
1957
- apWidth: b
1958
- };
1959
- }, [
1960
- o,
1961
- n
1962
- ]), m = (z) => a === "inch" ? (z / 25.4).toFixed(2) : z.toFixed(1), g = a === "inch" ? "in" : "mm";
1963
- return V(He, {
1964
- children: [
1965
- d("mesh", {
1966
- geometry: e.geometry,
1967
- children: d("meshStandardMaterial", {
1968
- color: i,
1969
- side: J.DoubleSide,
1970
- transparent: true,
1971
- opacity: 0.15,
1972
- depthWrite: false,
1973
- clippingPlanes: [
1974
- l
1975
- ]
1976
- })
1977
- }),
1978
- o.linePoints.length >= 2 && d(ge, {
1979
- points: o.linePoints,
1980
- color: "#00ff00",
1981
- lineWidth: 3,
1982
- depthTest: false,
1983
- depthWrite: false,
1984
- transparent: true
1985
- }),
1986
- r && V(He, {
1987
- children: [
1988
- d(ge, {
1989
- points: r,
1990
- color: "#ff8800",
1991
- lineWidth: 2,
1992
- depthTest: false,
1993
- depthWrite: false,
1994
- transparent: true
1995
- }),
1996
- d(Ke, {
1997
- position: [
1998
- r[0].x,
1999
- n,
2000
- r[0].z - t * 0.02
2001
- ],
2002
- center: true,
2003
- style: {
2004
- pointerEvents: "none"
2005
- },
2006
- children: d("div", {
2007
- style: {
2008
- whiteSpace: "nowrap",
2009
- padding: "2px 6px",
2010
- backgroundColor: "rgba(0,0,0,0.75)",
2011
- borderRadius: 3
2012
- },
2013
- children: V("span", {
2014
- style: {
2015
- fontSize: 12,
2016
- color: "#ff8800",
2017
- fontFamily: "monospace"
2018
- },
2019
- children: [
2020
- "ML ",
2021
- m(y),
2022
- " ",
2023
- g
2024
- ]
2025
- })
2026
- })
2027
- })
2028
- ]
2029
- }),
2030
- f && V(He, {
2031
- children: [
2032
- d(ge, {
2033
- points: f,
2034
- color: "#ff00ff",
2035
- lineWidth: 2,
2036
- depthTest: false,
2037
- depthWrite: false,
2038
- transparent: true
2039
- }),
2040
- d(Ke, {
2041
- position: [
2042
- Math.max(f[0].x, f[1].x) + t * 0.02,
2043
- n,
2044
- f[0].z > f[1].z ? f[0].z : f[1].z
2045
- ],
2046
- center: true,
2047
- style: {
2048
- pointerEvents: "none"
2049
- },
2050
- children: d("div", {
2051
- style: {
2052
- whiteSpace: "nowrap",
2053
- padding: "2px 6px",
2054
- backgroundColor: "rgba(0,0,0,0.75)",
2055
- borderRadius: 3
2056
- },
2057
- children: V("span", {
2058
- style: {
2059
- fontSize: 12,
2060
- color: "#ff00ff",
2061
- fontFamily: "monospace"
2062
- },
2063
- children: [
2064
- "AP ",
2065
- m(p),
2066
- " ",
2067
- g
2068
- ]
2069
- })
2070
- })
2071
- })
2072
- ]
2073
- })
2074
- ]
2075
- });
2076
- }, Xn = {
2077
- pcaAxes: true,
2078
- obb: true,
2079
- obbAxis: true,
2080
- shellComponents: true,
2081
- circumferenceScan: false,
2082
- landmarkAxis: true,
2083
- iterativePCA: false,
2084
- fullRegionPCA: true
2085
- }, Zn = [
2086
- "#ff4444",
2087
- "#44cc44",
2088
- "#4488ff"
2089
- ];
2090
- function $t(e) {
2091
- const s = e.getAttribute("position"), n = s.count, t = new h();
2092
- for (let g = 0; g < n; g++) t.x += s.getX(g), t.y += s.getY(g), t.z += s.getZ(g);
2093
- t.divideScalar(n);
2094
- let i = 0, a = 0, c = 0, u = 0, o = 0, l = 0;
2095
- for (let g = 0; g < n; g++) {
2096
- const z = s.getX(g) - t.x, v = s.getY(g) - t.y, x = s.getZ(g) - t.z;
2097
- i += z * z, a += z * v, c += z * x, u += v * v, o += v * x, l += x * x;
2098
- }
2099
- i /= n, a /= n, c /= n, u /= n, o /= n, l /= n;
2100
- const r = [], f = [], y = [
2101
- [
2102
- i,
2103
- a,
2104
- c
2105
- ],
2106
- [
2107
- a,
2108
- u,
2109
- o
2110
- ],
2111
- [
2112
- c,
2113
- o,
2114
- l
2115
- ]
2116
- ];
2117
- for (let g = 0; g < 3; g++) {
2118
- let z = new h(1 + g * 0.1, 1 - g * 0.1, 0.5 + g * 0.3).normalize(), v = 0;
2119
- for (let x = 0; x < 100; x++) {
2120
- const b = y[0][0] * z.x + y[0][1] * z.y + y[0][2] * z.z, w = y[1][0] * z.x + y[1][1] * z.y + y[1][2] * z.z, C = y[2][0] * z.x + y[2][1] * z.y + y[2][2] * z.z, M = new h(b, w, C);
2121
- if (v = M.length(), v < 1e-12) break;
2122
- if (M.divideScalar(v), z.distanceTo(M) < 1e-10) {
2123
- z = M;
2124
- break;
2125
- }
2126
- z = M;
2127
- }
2128
- r.push(z.clone()), f.push(v);
2129
- for (let x = 0; x < 3; x++) for (let b = 0; b < 3; b++) {
2130
- const w = [
2131
- z.x,
2132
- z.y,
2133
- z.z
2134
- ][x], C = [
2135
- z.x,
2136
- z.y,
2137
- z.z
2138
- ][b];
2139
- y[x][b] -= v * w * C;
2140
- }
2141
- }
2142
- const p = new h();
2143
- for (let g = 0; g < n; g++) p.x += s.getX(g), p.y += s.getY(g), p.z += s.getZ(g);
2144
- p.divideScalar(n);
2145
- const m = [
2146
- 0,
2147
- 0,
2148
- 0
2149
- ];
2150
- for (let g = 0; g < 3; g++) {
2151
- let z = 1 / 0, v = -1 / 0;
2152
- const x = r[g];
2153
- for (let b = 0; b < n; b++) {
2154
- const w = s.getX(b) - p.x, C = s.getY(b) - p.y, M = s.getZ(b) - p.z, L = w * x.x + C * x.y + M * x.z;
2155
- L < z && (z = L), L > v && (v = L);
2156
- }
2157
- m[g] = (v - z) / 2;
2158
- }
2159
- return {
2160
- axes: r,
2161
- eigenvalues: f,
2162
- center: p,
2163
- halfExtents: m
2164
- };
2165
- }
2166
- function Kn({ pca: e }) {
2167
- return d("group", {
2168
- children: e.axes.map((s, n) => {
2169
- const t = e.center.clone().addScaledVector(s, e.halfExtents[n]), i = e.center.clone().addScaledVector(s, -e.halfExtents[n]);
2170
- return d(ge, {
2171
- points: [
2172
- i,
2173
- t
2174
- ],
2175
- color: Zn[n],
2176
- lineWidth: 2
2177
- }, n);
2178
- })
2179
- });
2180
- }
2181
- function Un({ pca: e }) {
2182
- const s = de(() => {
2183
- const { center: n, axes: t, halfExtents: i } = e, a = [];
2184
- for (let u = -1; u <= 1; u += 2) for (let o = -1; o <= 1; o += 2) for (let l = -1; l <= 1; l += 2) a.push(n.clone().addScaledVector(t[0], u * i[0]).addScaledVector(t[1], o * i[1]).addScaledVector(t[2], l * i[2]));
2185
- return [
2186
- [
2187
- 0,
2188
- 1
2189
- ],
2190
- [
2191
- 2,
2192
- 3
2193
- ],
2194
- [
2195
- 4,
2196
- 5
2197
- ],
2198
- [
2199
- 6,
2200
- 7
2201
- ],
2202
- [
2203
- 0,
2204
- 2
2205
- ],
2206
- [
2207
- 1,
2208
- 3
2209
- ],
2210
- [
2211
- 4,
2212
- 6
2213
- ],
2214
- [
2215
- 5,
2216
- 7
2217
- ],
2218
- [
2219
- 0,
2220
- 4
2221
- ],
2222
- [
2223
- 1,
2224
- 5
2225
- ],
2226
- [
2227
- 2,
2228
- 6
2229
- ],
2230
- [
2231
- 3,
2232
- 7
2233
- ]
2234
- ].map(([u, o]) => [
2235
- a[u],
2236
- a[o]
2237
- ]);
2238
- }, [
2239
- e
2240
- ]);
2241
- return d("group", {
2242
- children: s.map((n, t) => d(ge, {
2243
- points: n,
2244
- color: "#ffaa00",
2245
- lineWidth: 1,
2246
- transparent: true,
2247
- opacity: 0.5
2248
- }, t))
2249
- });
2250
- }
2251
- function Qn({ redPoint: e, greenPoint: s }) {
2252
- const n = de(() => new h().subVectors(s, e).normalize(), [
2253
- e,
2254
- s
2255
- ]), t = de(() => {
2256
- const a = n.dot(new h(0, 1, 0));
2257
- return Math.acos(Math.min(1, Math.abs(a))) * 180 / Math.PI;
2258
- }, [
2259
- n
2260
- ]), i = t < 1 ? "#44ff44" : t < 5 ? "#ffcc00" : "#ff4444";
2261
- return V("group", {
2262
- children: [
2263
- d(ge, {
2264
- points: [
2265
- e,
2266
- s
2267
- ],
2268
- color: i,
2269
- lineWidth: 3
2270
- }),
2271
- V("mesh", {
2272
- position: e,
2273
- children: [
2274
- d("sphereGeometry", {
2275
- args: [
2276
- 1.5,
2277
- 8,
2278
- 8
2279
- ]
2280
- }),
2281
- d("meshBasicMaterial", {
2282
- color: "#ff4444"
2283
- })
2284
- ]
2285
- }),
2286
- V("mesh", {
2287
- position: s,
2288
- children: [
2289
- d("sphereGeometry", {
2290
- args: [
2291
- 1.5,
2292
- 8,
2293
- 8
2294
- ]
2295
- }),
2296
- d("meshBasicMaterial", {
2297
- color: "#44ff44"
2298
- })
2299
- ]
2300
- })
2301
- ]
2302
- });
2303
- }
2304
- function qn({ geometry: e, redY: s, greenY: n, modelSize: t }) {
2305
- const a = t * 0.15, c = de(() => {
2306
- const u = [];
2307
- let o = n - 10;
2308
- const l = new h(0, 1, 0);
2309
- for (; o > s; ) {
2310
- const r = Math.min(o, n), f = Math.max(o, n), y = it(e, r, f);
2311
- if (y) {
2312
- const p = y.dot(l), m = Math.acos(Math.min(1, Math.abs(p))) * 180 / Math.PI;
2313
- u.push({
2314
- axis: y,
2315
- regionMin: r,
2316
- regionMax: f,
2317
- angleDeg: m
2318
- });
2319
- }
2320
- o -= 10;
2321
- }
2322
- return u;
2323
- }, [
2324
- e,
2325
- s,
2326
- n
2327
- ]);
2328
- return d("group", {
2329
- children: c.map((u, o) => {
2330
- const l = (u.regionMin + u.regionMax) / 2, r = new h(0, l, 0), f = r.clone().addScaledVector(u.axis, a), y = r.clone().addScaledVector(u.axis, -a), p = o / Math.max(1, c.length - 1), m = u.angleDeg < 0.5 ? `hsl(${120 - p * 120}, 80%, 60%)` : `hsl(${40 - u.angleDeg * 2}, 90%, 55%)`;
2331
- return V("group", {
2332
- children: [
2333
- d(ge, {
2334
- points: [
2335
- y,
2336
- f
2337
- ],
2338
- color: m,
2339
- lineWidth: 1.5,
2340
- transparent: true,
2341
- opacity: 0.7
2342
- }),
2343
- d(ge, {
2344
- points: [
2345
- new h(-a * 0.3, u.regionMin, 0),
2346
- new h(a * 0.3, u.regionMin, 0)
2347
- ],
2348
- color: m,
2349
- lineWidth: 0.5,
2350
- transparent: true,
2351
- opacity: 0.3
2352
- })
2353
- ]
2354
- }, o);
2355
- })
2356
- });
2357
- }
2358
- function Jn({ geometry: e, redY: s, greenY: n, modelSize: t }) {
2359
- const i = de(() => {
2360
- const f = it(e, s, n);
2361
- if (!f) return null;
2362
- const y = f.dot(new h(0, 1, 0)), p = Math.acos(Math.min(1, Math.abs(y))) * 180 / Math.PI;
2363
- return {
2364
- axis: f,
2365
- angleDeg: p
2366
- };
2367
- }, [
2368
- e,
2369
- s,
2370
- n
2371
- ]);
2372
- if (!i) return null;
2373
- const a = (s + n) / 2, c = new h(0, a, 0), u = t * 0.4, o = c.clone().addScaledVector(i.axis, u), l = c.clone().addScaledVector(i.axis, -u), r = i.angleDeg < 0.5 ? "#00ffff" : i.angleDeg < 2 ? "#ffcc00" : "#ff6600";
2374
- return d("group", {
2375
- children: d(ge, {
2376
- points: [
2377
- l,
2378
- o
2379
- ],
2380
- color: r,
2381
- lineWidth: 3,
2382
- dashed: true,
2383
- dashSize: 3,
2384
- gapSize: 2
2385
- })
2386
- });
2387
- }
2388
- function eo({ pca: e, modelSize: s }) {
2389
- const n = e.axes[0], t = s * 0.6, i = e.center.clone().addScaledVector(n, t), a = e.center.clone().addScaledVector(n, -t);
2390
- return d(ge, {
2391
- points: [
2392
- a,
2393
- i
2394
- ],
2395
- color: "#ff8800",
2396
- lineWidth: 2,
2397
- dashed: true,
2398
- dashSize: 3,
2399
- gapSize: 2
2400
- });
2401
- }
2402
- function to({ geometry: e, redY: s, greenY: n, modelSize: t }) {
2403
- const i = de(() => {
2404
- const l = st(e), r = new h(0, 1, 0), f = 5;
2405
- e.computeBoundingBox();
2406
- const y = e.boundingBox.max.y, p = [];
2407
- for (let C = s + f; C < y; C += f) {
2408
- const M = ht(l, e, new h(0, C, 0), r);
2409
- M > 0 && p.push({
2410
- y: C,
2411
- circ: M
2412
- });
2413
- }
2414
- if (p.length < 5) return null;
2415
- const m = n - s, g = s + m * 0.3, z = s + m * 0.7, v = p.filter((C) => C.y >= g && C.y <= z);
2416
- if (v.length < 3) return null;
2417
- const x = v.map((C) => C.circ).sort((C, M) => C - M), b = x[Math.floor(x.length / 2)], w = Math.max(...p.map((C) => C.circ));
2418
- return {
2419
- circumferences: p,
2420
- baseline: b,
2421
- maxCirc: w
2422
- };
2423
- }, [
2424
- e,
2425
- s,
2426
- n
2427
- ]);
2428
- if (!i) return null;
2429
- const { circumferences: a, baseline: c, maxCirc: u } = i, o = t * 0.3 / u;
2430
- return V("group", {
2431
- children: [
2432
- a.map(({ y: l, circ: r }, f) => {
2433
- const y = r / c, p = y > 1.6 ? "#ff4444" : y > 1.3 ? "#ffcc00" : "#22cc66", m = r * o;
2434
- return d(ge, {
2435
- points: [
2436
- new h(-m, l, 0),
2437
- new h(m, l, 0)
2438
- ],
2439
- color: p,
2440
- lineWidth: 1.5,
2441
- transparent: true,
2442
- opacity: 0.6
2443
- }, f);
2444
- }),
2445
- (() => {
2446
- const l = c * 1.6 * o, r = a[0].y, f = a[a.length - 1].y;
2447
- return V(He, {
2448
- children: [
2449
- d(ge, {
2450
- points: [
2451
- new h(-l, r, 0),
2452
- new h(-l, f, 0)
2453
- ],
2454
- color: "#ff4444",
2455
- lineWidth: 1,
2456
- dashed: true,
2457
- dashSize: 3,
2458
- gapSize: 2,
2459
- transparent: true,
2460
- opacity: 0.4
2461
- }),
2462
- d(ge, {
2463
- points: [
2464
- new h(l, r, 0),
2465
- new h(l, f, 0)
2466
- ],
2467
- color: "#ff4444",
2468
- lineWidth: 1,
2469
- dashed: true,
2470
- dashSize: 3,
2471
- gapSize: 2,
2472
- transparent: true,
2473
- opacity: 0.4
2474
- })
2475
- ]
2476
- });
2477
- })()
2478
- ]
2479
- });
2480
- }
2481
- function no({ componentDebug: e }) {
2482
- return d("group", {
2483
- children: e.geometries.map((s, n) => {
2484
- const t = e.colors[n] ?? "#888888", i = n === e.innerIdx;
2485
- s.computeBoundingBox();
2486
- const a = new h();
2487
- return s.boundingBox.getCenter(a), V("group", {
2488
- children: [
2489
- d("mesh", {
2490
- geometry: s,
2491
- renderOrder: i ? 2 : 1,
2492
- children: d("meshStandardMaterial", {
2493
- color: t,
2494
- transparent: true,
2495
- opacity: i ? 0.5 : 0.2,
2496
- side: J.DoubleSide,
2497
- depthWrite: false,
2498
- polygonOffset: true,
2499
- polygonOffsetFactor: 1,
2500
- polygonOffsetUnits: 1
2501
- })
2502
- }),
2503
- d("mesh", {
2504
- geometry: s,
2505
- renderOrder: i ? 2 : 1,
2506
- children: d("meshBasicMaterial", {
2507
- color: t,
2508
- wireframe: true,
2509
- transparent: true,
2510
- opacity: i ? 0.4 : 0.15
2511
- })
2512
- }),
2513
- d("group", {
2514
- position: a,
2515
- children: d(Ke, {
2516
- center: true,
2517
- style: {
2518
- pointerEvents: "none"
2519
- },
2520
- children: d("div", {
2521
- style: {
2522
- padding: "2px 8px",
2523
- backgroundColor: "rgba(0,0,0,0.8)",
2524
- borderRadius: 4,
2525
- border: `1px solid ${t}`,
2526
- whiteSpace: "nowrap"
2527
- },
2528
- children: d("span", {
2529
- style: {
2530
- fontSize: 11,
2531
- color: t,
2532
- fontFamily: "monospace",
2533
- fontWeight: i ? 700 : 400
2534
- },
2535
- children: e.labels[n]
2536
- })
2537
- })
2538
- })
2539
- })
2540
- ]
2541
- }, n);
2542
- })
2543
- });
2544
- }
2545
- function oo({ mesh: e, layers: s, landmarkPoints: n, componentDebug: t }) {
2546
- const i = e.geometry, a = de(() => i.getAttribute("position") ? $t(i) : null, [
2547
- i
2548
- ]), c = de(() => !n || n.length < 2 ? null : {
2549
- red: new h(n[0].position.x, n[0].position.y, n[0].position.z),
2550
- green: new h(n[1].position.x, n[1].position.y, n[1].position.z)
2551
- }, [
2552
- n
2553
- ]);
2554
- return V("group", {
2555
- children: [
2556
- s.pcaAxes && a && d(Kn, {
2557
- pca: a
2558
- }),
2559
- s.obb && a && d(Un, {
2560
- pca: a
2561
- }),
2562
- s.obbAxis && a && d(eo, {
2563
- pca: a,
2564
- modelSize: a.halfExtents[0] ? Math.max(...a.halfExtents) * 2 : 100
2565
- }),
2566
- s.shellComponents && t && d(no, {
2567
- componentDebug: t
2568
- }),
2569
- s.circumferenceScan && c && d(to, {
2570
- geometry: i,
2571
- redY: c.red.y,
2572
- greenY: c.green.y,
2573
- modelSize: (a == null ? void 0 : a.halfExtents[0]) ? Math.max(...a.halfExtents) * 2 : 100
2574
- }),
2575
- s.landmarkAxis && c && d(Qn, {
2576
- redPoint: c.red,
2577
- greenPoint: c.green
2578
- }),
2579
- s.iterativePCA && c && d(qn, {
2580
- geometry: i,
2581
- redY: c.red.y,
2582
- greenY: c.green.y,
2583
- modelSize: (a == null ? void 0 : a.halfExtents[0]) ? Math.max(...a.halfExtents) * 2 : 100
2584
- }),
2585
- s.fullRegionPCA && c && d(Jn, {
2586
- geometry: i,
2587
- redY: c.red.y,
2588
- greenY: c.green.y,
2589
- modelSize: (a == null ? void 0 : a.halfExtents[0]) ? Math.max(...a.halfExtents) * 2 : 100
2590
- })
2591
- ]
2592
- });
2593
- }
2594
- function so({ mesh: e }) {
2595
- const s = e.geometry, n = de(() => {
2596
- if (!s.getAttribute("position")) return null;
2597
- const a = $t(s), c = a.axes[0], u = a.halfExtents[0] * 1.3;
2598
- return {
2599
- axis: c,
2600
- center: a.center,
2601
- halfLen: u
2602
- };
2603
- }, [
2604
- s
2605
- ]);
2606
- if (!n) return null;
2607
- const t = n.center.clone().addScaledVector(n.axis, n.halfLen), i = n.center.clone().addScaledVector(n.axis, -n.halfLen);
2608
- return d(ge, {
2609
- points: [
2610
- i,
2611
- t
2612
- ],
2613
- color: "#666",
2614
- lineWidth: 1,
2615
- dashed: true,
2616
- dashSize: 4,
2617
- gapSize: 3,
2618
- transparent: true,
2619
- opacity: 0.4,
2620
- depthTest: false,
2621
- renderOrder: 999
2622
- });
2623
- }
2624
- const Nt = [
2625
- {
2626
- key: "pcaAxes",
2627
- label: "PCA Axes (full mesh)",
2628
- color: "#ff4444",
2629
- group: "Geometry"
2630
- },
2631
- {
2632
- key: "obb",
2633
- label: "OBB Wireframe",
2634
- color: "#ffaa00",
2635
- group: "Geometry"
2636
- },
2637
- {
2638
- key: "obbAxis",
2639
- label: "OBB Primary Axis",
2640
- color: "#ff8800",
2641
- group: "Geometry"
2642
- },
2643
- {
2644
- key: "shellComponents",
2645
- label: "Shell Components",
2646
- color: "#4488ff",
2647
- group: "Shell Detection"
2648
- },
2649
- {
2650
- key: "circumferenceScan",
2651
- label: "Circumference Scan (step 4.5)",
2652
- color: "#22cc66",
2653
- group: "Isolation"
2654
- },
2655
- {
2656
- key: "landmarkAxis",
2657
- label: "Landmark Axis (rough align)",
2658
- color: "#44ff44",
2659
- group: "Alignment"
2660
- },
2661
- {
2662
- key: "iterativePCA",
2663
- label: "Iterative PCA (step 5)",
2664
- color: "#cc66ff",
2665
- group: "Alignment"
2666
- },
2667
- {
2668
- key: "fullRegionPCA",
2669
- label: "Full Region PCA (step 7)",
2670
- color: "#00ffff",
2671
- group: "Alignment"
2672
- }
2673
- ], ro = [
2674
- ...new Set(Nt.map((e) => e.group))
2675
- ];
2676
- function io({ layers: e, onToggleLayer: s }) {
2677
- return V("div", {
2678
- style: {
2679
- position: "absolute",
2680
- top: 16,
2681
- left: 16,
2682
- zIndex: 20,
2683
- backgroundColor: "rgba(7, 6, 17, 0.9)",
2684
- border: "1px solid rgba(255,255,255,0.1)",
2685
- borderRadius: 6,
2686
- padding: "10px 12px",
2687
- fontFamily: "system-ui, sans-serif",
2688
- fontSize: 12,
2689
- color: "#ccc",
2690
- minWidth: 180,
2691
- backdropFilter: "blur(8px)"
2692
- },
2693
- children: [
2694
- d("div", {
2695
- style: {
2696
- fontSize: 10,
2697
- fontWeight: 600,
2698
- textTransform: "uppercase",
2699
- letterSpacing: "0.5px",
2700
- color: "#888",
2701
- marginBottom: 8
2702
- },
2703
- children: "Debug Layers"
2704
- }),
2705
- ro.map((n) => V("div", {
2706
- children: [
2707
- d("div", {
2708
- style: {
2709
- fontSize: 9,
2710
- fontWeight: 600,
2711
- textTransform: "uppercase",
2712
- letterSpacing: "0.5px",
2713
- color: "#555",
2714
- marginTop: 6,
2715
- marginBottom: 4
2716
- },
2717
- children: n
2718
- }),
2719
- Nt.filter((t) => t.group === n).map(({ key: t, label: i, color: a }) => V("label", {
2720
- style: {
2721
- display: "flex",
2722
- alignItems: "center",
2723
- gap: 8,
2724
- padding: "3px 0",
2725
- cursor: "pointer",
2726
- userSelect: "none"
2727
- },
2728
- children: [
2729
- d("input", {
2730
- type: "checkbox",
2731
- checked: e[t],
2732
- onChange: () => s(t),
2733
- style: {
2734
- accentColor: a,
2735
- width: 14,
2736
- height: 14,
2737
- cursor: "pointer"
2738
- }
2739
- }),
2740
- d("span", {
2741
- style: {
2742
- width: 8,
2743
- height: 8,
2744
- borderRadius: "50%",
2745
- backgroundColor: a,
2746
- opacity: e[t] ? 1 : 0.3,
2747
- flexShrink: 0
2748
- }
2749
- }),
2750
- d("span", {
2751
- style: {
2752
- opacity: e[t] ? 1 : 0.5
2753
- },
2754
- children: i
2755
- })
2756
- ]
2757
- }, t))
2758
- ]
2759
- }, n))
2760
- ]
2761
- });
2762
- }
2763
- const lo = ({ config: e, spacingType: s, scanUrl: n, formMeasurements: t, onComplete: i, isDebugUser: a = false, onAnalyticsEvent: c, wasmModule: u }) => {
2764
- const [o, l] = q(null), [r, f] = q(0), [y, p] = q(false), [m, g] = q(false), [z, v] = q(""), [x, b] = q("3D"), [w, C] = q(s === "AK" ? 2 : 1), [M, L] = q(false), [P, H] = q(null), [O, $] = q(null), [R, Y] = q(s ?? null), [N, ne] = q("mm"), [oe, le] = q(false), [ee, D] = q(""), [S, F] = q(false), [_, B] = q(false), [T, E] = q(false), [j, X] = q(false), [Q, ie] = q(false), [se, A] = q([]), [I, k] = q(null), [G, U] = q(null), [re, Pe] = q(null), [me, ae] = q(null), [xe, Re] = q(false), [pe, we] = q(Xn), [he, Ge] = q(false), [fe, Ne] = q(null), [Le, ye] = q("obj"), [Te, Me] = q(false), [Ae, De] = q({}), [Se, Ee] = q(t), [ke, Ue] = q(true), [xt] = q("#c8c8c8"), [Ht] = q(1), [Xe, at] = q(false), Je = Ie(null), { landmarkPoints: ce, clearLandmarkPoints: Yt, addLandmarkPoint: yt, removeLandmarkPoint: bt, updateLandmarkPositions: wt, setAligned: St, isAligned: be, setCut: zt, isCut: Gt } = mt(), ct = w * We;
2765
- Ce(() => {
2766
- t && Ee(t);
2767
- }, [
2768
- t
2769
- ]), Ce(() => {
2770
- t || (Ee(void 0), De({}));
2771
- }, [
2772
- w
2773
- ]);
2774
- const ft = Ie(false);
2775
- Ce(() => {
2776
- if (!be || se.length === 0 || ft.current || !c) return;
2777
- ft.current = true;
2778
- const W = ce.length >= 3 ? Math.abs(ce[2].position.y - ce[1].position.y) : null;
2779
- c("dimensions_calculated", {
2780
- spacing_type: R,
2781
- source_unit: "mm",
2782
- file_format: Le,
2783
- measurement_source: "scan_derived",
2784
- is_double_wall: T,
2785
- is_unit_converted: false,
2786
- form_measurements: (Se == null ? void 0 : Se.filter((Z) => Z != null)) ?? null,
2787
- scan_measurements: se.map((Z) => +(Z.modifiedValue ?? Z.originalValue).toFixed(1)),
2788
- measurement_variance: Se ? se.map((Z, ue) => {
2789
- const K = Se[ue];
2790
- return K == null ? null : +((Z.modifiedValue ?? Z.originalValue) - K).toFixed(1);
2791
- }) : null,
2792
- frontal_height: W !== null ? +W.toFixed(1) : null
2793
- });
2794
- }, [
2795
- be,
2796
- se
2797
- ]), Ce(() => {
2798
- if (u !== void 0) {
2799
- Je.current = u, at(true);
2800
- return;
2801
- }
2802
- let W = false;
2803
- return (async () => {
2804
- try {
2805
- const Z = await import("galileo-core-geo").then(async (m2) => {
2806
- await m2.__tla;
2807
- return m2;
2808
- });
2809
- await Z.default(), W || (Je.current = Z, at(true));
2810
- } catch {
2811
- W || at(true);
2812
- }
2813
- })(), () => {
2814
- W = true;
2815
- };
2816
- }, [
2817
- u
2818
- ]);
2819
- const Qe = Fe((W, Z) => {
2820
- W.computeBoundingBox();
2821
- const ue = W.boundingBox, K = new h();
2822
- ue.getCenter(K), W.translate(-K.x, -K.y, -K.z), W.computeBoundingBox();
2823
- const Be = W.boundingBox, te = new h();
2824
- Be.getSize(te), f(Math.max(te.x, te.y, te.z));
2825
- const ve = new J.Mesh(W, new J.MeshStandardMaterial({
2826
- color: 8947848,
2827
- side: J.DoubleSide
2828
- }));
2829
- l(ve), F(Z), B(true);
2830
- }, []), qe = Fe(async (W, Z) => {
2831
- ae(null);
2832
- const ue = Z.toLowerCase(), K = ue.endsWith(".stl");
2833
- if (!ue.endsWith(".obj") && !K) {
2834
- ae("Unsupported file format. Please use OBJ or STL.");
2835
- return;
2836
- }
2837
- ye(K ? "stl" : "obj"), g(true), v("Processing file...");
2838
- try {
2839
- let te;
2840
- if (K) if (v("Converting STL..."), W instanceof ArrayBuffer) {
2841
- const ze = new Blob([
2842
- W
2843
- ]), Ve = new File([
2844
- ze
2845
- ], Z);
2846
- te = await dt(Ve);
2847
- } else {
2848
- const ze = new Blob([
2849
- W
2850
- ]), Ve = new File([
2851
- ze
2852
- ], Z);
2853
- te = await dt(Ve);
2854
- }
2855
- else te = typeof W == "string" ? W : new TextDecoder().decode(W);
2856
- const ve = Je.current ? await bn(te, Je.current, v) : null;
2857
- if (ve) e.showAmputationModal && !s ? (H(ve), L(true)) : (Qe(ve.geometry, ve.wasScaled), ve.innerShellExtracted && (X(true), E(true)), ve.componentDebug && $(ve.componentDebug));
2858
- else {
2859
- v("Using fallback loader...");
2860
- const ze = wn(te);
2861
- ze ? e.showAmputationModal && !s ? (H({
2862
- geometry: ze,
2863
- wasScaled: false,
2864
- innerShellExtracted: false
2865
- }), L(true)) : Qe(ze, false) : ae("Failed to parse the mesh.");
2866
- }
2867
- } catch (te) {
2868
- ae(te instanceof Error ? te.message : "Failed to process the mesh file.");
2869
- } finally {
2870
- g(false), v("");
2871
- }
2872
- }, [
2873
- Xe,
2874
- e.showAmputationModal,
2875
- s,
2876
- Qe
2877
- ]);
2878
- Ce(() => {
2879
- if (!n || !Xe) return;
2880
- (async () => {
2881
- g(true), v("Downloading scan...");
2882
- try {
2883
- const Z = await fetch(n);
2884
- if (!Z.ok) throw new Error(`Failed to download scan: ${Z.status}`);
2885
- const K = new URL(n).pathname.split("/").pop() || "scan.obj";
2886
- if (K.toLowerCase().endsWith(".stl")) {
2887
- const te = await Z.arrayBuffer();
2888
- await qe(te, K);
2889
- } else {
2890
- const te = await Z.text();
2891
- await qe(te, K);
2892
- }
2893
- } catch (Z) {
2894
- ae(Z instanceof Error ? Z.message : "Failed to load scan from URL."), g(false), v("");
2895
- }
2896
- })();
2897
- }, [
2898
- n,
2899
- Xe
2900
- ]);
2901
- const Xt = Fe((W) => {
2902
- W.preventDefault(), p(true);
2903
- }, []), Zt = Fe((W) => {
2904
- W.preventDefault(), p(false);
2905
- }, []), Kt = Fe(async (W) => {
2906
- if (W.preventDefault(), p(false), !Xe) {
2907
- ae("WASM module is still loading. Please wait.");
2908
- return;
2909
- }
2910
- const Z = W.dataTransfer.files[0];
2911
- if (!Z) return;
2912
- const ue = Z.name.toLowerCase();
2913
- if (!ue.endsWith(".obj") && !ue.endsWith(".stl")) {
2914
- ae("Please drop an OBJ or STL file.");
2915
- return;
2916
- }
2917
- if (ue.endsWith(".stl")) {
2918
- ye("stl"), g(true), v("Converting STL...");
2919
- try {
2920
- const K = await dt(Z);
2921
- await qe(K, Z.name);
2922
- } catch (K) {
2923
- ae(K instanceof Error ? K.message : "Failed to process STL file."), g(false), v("");
2924
- }
2925
- } else {
2926
- const K = await Z.text();
2927
- await qe(K, Z.name);
2928
- }
2929
- }, [
2930
- Xe,
2931
- qe
2932
- ]), Ut = Fe((W) => {
2933
- Y(W), C(W === "AK" ? 2 : 1), L(false), P && (Qe(P.geometry, P.wasScaled), P.innerShellExtracted && (X(true), E(true)), P.componentDebug && $(P.componentDebug), H(null)), c == null ? void 0 : c("file_loaded", {
2934
- spacing_type: W,
2935
- file_format: Le,
2936
- is_double_wall: (P == null ? void 0 : P.innerShellExtracted) ?? false
2937
- });
2938
- }, [
2939
- P,
2940
- Qe,
2941
- c,
2942
- Le
2943
- ]), Qt = Fe(() => {
2944
- !o || ce.length < 2 || (le(true), D("Please wait..."), setTimeout(() => {
2945
- In(o, ce, ct, {
2946
- onStatus: D,
2947
- addLandmarkPoint: yt,
2948
- removeLandmarkPoint: bt,
2949
- updateLandmarkPositions: wt,
2950
- setAligned: St,
2951
- setCut: zt,
2952
- setModelSize: f,
2953
- setOriginalEndY: Pe,
2954
- setAdjustedStartY: k,
2955
- setAdjustedEndY: U,
2956
- setError: ae,
2957
- setDoubleShell: (W) => {
2958
- E(W), ie(true);
2959
- },
2960
- setClippedReferenceGeometry: Ne,
2961
- skipDoubleShellDetection: j
2962
- }), ft.current = false, le(false);
2963
- }, 50));
2964
- }, [
2965
- o,
2966
- ce,
2967
- wt,
2968
- St,
2969
- yt,
2970
- bt,
2971
- zt,
2972
- ct,
2973
- j
2974
- ]), qt = Fe(() => {
2975
- if (!i || !o || se.length === 0 || !R) return;
2976
- let W = 0, Z = 0;
2977
- const ue = ce.length >= 3 ? Math.abs(ce[2].position.y - ce[1].position.y) : 0;
2978
- if (ce.length >= 2) {
2979
- const K = o.geometry, te = ce[1].position.y;
2980
- try {
2981
- const ve = st(K), ze = Ze(ve, K, te);
2982
- if (ze.linePoints.length >= 2) {
2983
- let Ve = ze.linePoints[0], et = ze.linePoints[0], tt = ze.linePoints[0], nt = ze.linePoints[0];
2984
- for (const _e of ze.linePoints) _e.x < Ve.x && (Ve = _e), _e.x > et.x && (et = _e), _e.z < tt.z && (tt = _e), _e.z > nt.z && (nt = _e);
2985
- W = new h(Ve.x, te, Ve.z).distanceTo(new h(et.x, te, et.z)), Z = new h(tt.x, te, tt.z).distanceTo(new h(nt.x, te, nt.z));
2986
- }
2987
- } catch {
2988
- }
2989
- }
2990
- i({
2991
- spacingType: R,
2992
- sourceUnit: "mm",
2993
- fileFormat: Le,
2994
- measurementSource: Se ? "form_provided" : "scan_derived",
2995
- isDoubleWall: T,
2996
- isUnitConverted: false,
2997
- formMeasurements: Se,
2998
- scanMeasurements: se,
2999
- frontalHeight: ue,
3000
- transverseML: W,
3001
- transverseAP: Z,
3002
- scanUrl: n
3003
- });
3004
- }, [
3005
- i,
3006
- o,
3007
- se,
3008
- R,
3009
- ce,
3010
- Le,
3011
- T,
3012
- Se,
3013
- n
3014
- ]), Jt = be ? 4 : o ? ce.length === 0 ? 2 : 3 : 1, en = [
3015
- {
3016
- label: "Load File",
3017
- number: 1
3018
- },
3019
- {
3020
- label: "Set Origin",
3021
- number: 2
3022
- },
3023
- {
3024
- label: R === "AK" ? "Set IT/Perineum" : "Set MPT",
3025
- number: 3
3026
- },
3027
- {
3028
- label: "Results",
3029
- number: 4
3030
- }
3031
- ];
3032
- return V(dn.Provider, {
3033
- value: e,
3034
- children: [
3035
- V("div", {
3036
- style: {
3037
- flex: 1,
3038
- display: "flex",
3039
- flexDirection: "column",
3040
- backgroundColor: "#F9F9FA",
3041
- minWidth: 0,
3042
- position: "relative",
3043
- height: "100%"
3044
- },
3045
- children: [
3046
- e.showToolbar && d("div", {
3047
- style: {
3048
- height: 83,
3049
- backgroundColor: "#9e9e9e",
3050
- flexShrink: 0,
3051
- position: "relative",
3052
- overflow: "hidden"
3053
- },
3054
- children: d("div", {
3055
- style: {
3056
- position: "absolute",
3057
- inset: 0,
3058
- opacity: 0.1,
3059
- backgroundImage: "repeating-linear-gradient(45deg, transparent, transparent 10px, #fff 10px, #fff 12px)",
3060
- pointerEvents: "none"
3061
- }
3062
- })
3063
- }),
3064
- d(Yn, {
3065
- steps: en,
3066
- currentStep: Jt
3067
- }),
3068
- V("div", {
3069
- style: {
3070
- flex: 1,
3071
- display: "flex",
3072
- flexDirection: "column",
3073
- minHeight: 0
3074
- },
3075
- children: [
3076
- V("div", {
3077
- style: {
3078
- flex: 1,
3079
- position: "relative",
3080
- minHeight: 0,
3081
- overflow: "hidden"
3082
- },
3083
- onDragOver: e.showDragDrop ? Xt : void 0,
3084
- onDragLeave: e.showDragDrop ? Zt : void 0,
3085
- onDrop: e.showDragDrop ? Kt : void 0,
3086
- children: [
3087
- e.showDragDrop && !o && !m && d("div", {
3088
- style: {
3089
- position: "absolute",
3090
- inset: 16,
3091
- border: "3px dashed #ccc",
3092
- borderRadius: 4,
3093
- display: "flex",
3094
- alignItems: "center",
3095
- justifyContent: "center",
3096
- pointerEvents: "none"
3097
- },
3098
- children: Xe ? d("div", {
3099
- style: {
3100
- fontSize: 18,
3101
- color: "#aaa",
3102
- fontWeight: 400,
3103
- fontFamily: "system-ui, sans-serif"
3104
- },
3105
- children: "Drag & Drop Files Here"
3106
- }) : V("div", {
3107
- style: {
3108
- textAlign: "center"
3109
- },
3110
- children: [
3111
- d("div", {
3112
- style: {
3113
- width: 32,
3114
- height: 32,
3115
- border: "3px solid rgba(0,0,0,0.1)",
3116
- borderTopColor: "#4a90d9",
3117
- borderRadius: "50%",
3118
- animation: "spin 1s linear infinite",
3119
- margin: "0 auto 12px"
3120
- }
3121
- }),
3122
- d("div", {
3123
- style: {
3124
- fontSize: 16,
3125
- color: "#999",
3126
- fontFamily: "system-ui, sans-serif"
3127
- },
3128
- children: "Loading WASM module..."
3129
- }),
3130
- d("style", {
3131
- children: "@keyframes spin { to { transform: rotate(360deg); } }"
3132
- })
3133
- ]
3134
- })
3135
- }),
3136
- !e.showDragDrop && !o && !m && !me && d("div", {
3137
- style: {
3138
- position: "absolute",
3139
- inset: 0,
3140
- display: "flex",
3141
- alignItems: "center",
3142
- justifyContent: "center"
3143
- },
3144
- children: V("div", {
3145
- style: {
3146
- textAlign: "center"
3147
- },
3148
- children: [
3149
- d("div", {
3150
- style: {
3151
- width: 32,
3152
- height: 32,
3153
- border: "3px solid rgba(0,0,0,0.1)",
3154
- borderTopColor: "#4a90d9",
3155
- borderRadius: "50%",
3156
- animation: "spin 1s linear infinite",
3157
- margin: "0 auto 12px"
3158
- }
3159
- }),
3160
- d("div", {
3161
- style: {
3162
- fontSize: 16,
3163
- color: "#999",
3164
- fontFamily: "system-ui, sans-serif"
3165
- },
3166
- children: "Loading scan..."
3167
- }),
3168
- d("style", {
3169
- children: "@keyframes spin { to { transform: rotate(360deg); } }"
3170
- })
3171
- ]
3172
- })
3173
- }),
3174
- o && !be && ce.length === 0 && V("div", {
3175
- style: {
3176
- position: "absolute",
3177
- top: 16,
3178
- left: "50%",
3179
- transform: "translateX(-50%)",
3180
- display: "flex",
3181
- alignItems: "center",
3182
- gap: 8,
3183
- padding: "8px 16px",
3184
- backgroundColor: "rgba(0, 0, 0, 0.6)",
3185
- borderRadius: 8,
3186
- color: "#fff",
3187
- fontSize: 13,
3188
- pointerEvents: "none",
3189
- zIndex: 5,
3190
- fontFamily: "system-ui, sans-serif"
3191
- },
3192
- children: [
3193
- d("div", {
3194
- style: {
3195
- width: 10,
3196
- height: 10,
3197
- borderRadius: "50%",
3198
- backgroundColor: "#ff4444",
3199
- flexShrink: 0
3200
- }
3201
- }),
3202
- "Click mesh to set Origin"
3203
- ]
3204
- }),
3205
- o && !be && ce.length === 1 && V("div", {
3206
- style: {
3207
- position: "absolute",
3208
- top: 16,
3209
- left: "50%",
3210
- transform: "translateX(-50%)",
3211
- display: "flex",
3212
- alignItems: "center",
3213
- gap: 8,
3214
- padding: "8px 16px",
3215
- backgroundColor: "rgba(0, 0, 0, 0.6)",
3216
- borderRadius: 8,
3217
- color: "#fff",
3218
- fontSize: 13,
3219
- pointerEvents: "none",
3220
- zIndex: 5,
3221
- fontFamily: "system-ui, sans-serif"
3222
- },
3223
- children: [
3224
- d("div", {
3225
- style: {
3226
- width: 10,
3227
- height: 10,
3228
- borderRadius: "50%",
3229
- backgroundColor: "#44ff44",
3230
- flexShrink: 0
3231
- }
3232
- }),
3233
- "Click mesh to set ",
3234
- R === "AK" ? "IT/Perineum" : "MPT"
3235
- ]
3236
- }),
3237
- m && d(Ft, {
3238
- message: z || "Processing mesh..."
3239
- }),
3240
- oe && d(Ft, {
3241
- message: ee
3242
- }),
3243
- me && d(Pn, {
3244
- message: me,
3245
- onDismiss: () => ae(null)
3246
- }),
3247
- e.showAmputationModal && M && d("div", {
3248
- style: {
3249
- position: "absolute",
3250
- inset: 0,
3251
- backgroundColor: "rgba(0,0,0,0.32)",
3252
- display: "flex",
3253
- alignItems: "center",
3254
- justifyContent: "center",
3255
- zIndex: 20
3256
- },
3257
- children: V("div", {
3258
- style: {
3259
- backgroundColor: "#fff",
3260
- borderRadius: 4,
3261
- width: 560,
3262
- boxShadow: "0 11px 15px -7px rgba(0,0,0,0.2), 0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12)",
3263
- fontFamily: "system-ui, sans-serif"
3264
- },
3265
- children: [
3266
- V("div", {
3267
- style: {
3268
- padding: "24px 24px 20px"
3269
- },
3270
- children: [
3271
- d("div", {
3272
- style: {
3273
- fontSize: 20,
3274
- fontWeight: 500,
3275
- color: "rgba(0,0,0,0.87)",
3276
- marginBottom: 8
3277
- },
3278
- children: "Select Spacing Type"
3279
- }),
3280
- d("div", {
3281
- style: {
3282
- fontSize: 14,
3283
- color: "rgba(0,0,0,0.54)",
3284
- marginBottom: 24
3285
- },
3286
- children: "Choose the measurement spacing for this scan"
3287
- }),
3288
- d("div", {
3289
- style: {
3290
- display: "flex",
3291
- gap: 16
3292
- },
3293
- children: [
3294
- "AK",
3295
- "BK"
3296
- ].map((W) => V("label", {
3297
- onClick: () => Y(W),
3298
- style: {
3299
- flex: 1,
3300
- display: "flex",
3301
- flexDirection: "column",
3302
- padding: "20px 24px",
3303
- border: "2px solid",
3304
- borderRadius: 4,
3305
- cursor: "pointer",
3306
- borderColor: R === W ? "rgb(12, 67, 173)" : "#e0e0e0",
3307
- backgroundColor: R === W ? "rgba(12, 67, 173, 0.04)" : "#fff",
3308
- transition: "border-color 0.15s, background-color 0.15s"
3309
- },
3310
- children: [
3311
- d("input", {
3312
- type: "radio",
3313
- name: "ampType",
3314
- checked: R === W,
3315
- onChange: () => Y(W),
3316
- style: {
3317
- accentColor: "rgb(12, 67, 173)",
3318
- marginBottom: 12,
3319
- width: 18,
3320
- height: 18
3321
- }
3322
- }),
3323
- d("span", {
3324
- style: {
3325
- fontSize: 18,
3326
- fontWeight: 600,
3327
- color: "rgba(0,0,0,0.87)",
3328
- marginBottom: 4
3329
- },
3330
- children: W
3331
- }),
3332
- V("span", {
3333
- style: {
3334
- fontSize: 13,
3335
- color: "rgba(0,0,0,0.54)"
3336
- },
3337
- children: [
3338
- W === "AK" ? "2" : "1",
3339
- "-inch measurement spacing"
3340
- ]
3341
- })
3342
- ]
3343
- }, W))
3344
- })
3345
- ]
3346
- }),
3347
- V("div", {
3348
- style: {
3349
- display: "flex",
3350
- justifyContent: "flex-end",
3351
- gap: 8,
3352
- padding: "12px 24px 20px",
3353
- borderTop: "1px solid #e0e0e0"
3354
- },
3355
- children: [
3356
- d("button", {
3357
- onClick: () => {
3358
- L(false), H(null);
3359
- },
3360
- style: {
3361
- padding: "6px 16px",
3362
- borderRadius: 4,
3363
- fontSize: 14,
3364
- fontWeight: 500,
3365
- backgroundColor: "#fff",
3366
- border: "1px solid #bdbdbd",
3367
- color: "rgba(0,0,0,0.87)",
3368
- cursor: "pointer",
3369
- fontFamily: "system-ui, sans-serif",
3370
- lineHeight: "36px",
3371
- letterSpacing: "0.4px"
3372
- },
3373
- children: "Cancel"
3374
- }),
3375
- d("button", {
3376
- onClick: () => R && Ut(R),
3377
- disabled: !R,
3378
- style: {
3379
- padding: "6px 16px",
3380
- borderRadius: 4,
3381
- fontSize: 14,
3382
- fontWeight: 500,
3383
- backgroundColor: R ? "rgb(12, 67, 173)" : "#e0e0e0",
3384
- border: "none",
3385
- color: R ? "#fff" : "#9e9e9e",
3386
- cursor: R ? "pointer" : "not-allowed",
3387
- fontFamily: "system-ui, sans-serif",
3388
- lineHeight: "36px",
3389
- letterSpacing: "0.4px"
3390
- },
3391
- children: "Next \xBB"
3392
- })
3393
- ]
3394
- })
3395
- ]
3396
- })
3397
- }),
3398
- e.showDragDrop && y && d("div", {
3399
- style: {
3400
- position: "absolute",
3401
- inset: 0,
3402
- backgroundColor: "rgba(12, 67, 173, 0.1)",
3403
- border: "2px dashed rgb(12, 67, 173)",
3404
- pointerEvents: "none",
3405
- zIndex: 10
3406
- }
3407
- }),
3408
- V(sn, {
3409
- camera: {
3410
- position: [
3411
- 0,
3412
- 0,
3413
- 5
3414
- ]
3415
- },
3416
- style: {
3417
- display: o ? "block" : "none",
3418
- backgroundColor: xe ? "#070611" : void 0
3419
- },
3420
- gl: {
3421
- localClippingEnabled: true
3422
- },
3423
- scene: {
3424
- background: xe ? new J.Color("#070611") : null
3425
- },
3426
- children: [
3427
- d("ambientLight", {
3428
- intensity: 0.4
3429
- }),
3430
- d("directionalLight", {
3431
- position: [
3432
- 10,
3433
- 10,
3434
- 5
3435
- ],
3436
- intensity: 1.2
3437
- }),
3438
- d("directionalLight", {
3439
- position: [
3440
- -5,
3441
- 5,
3442
- -5
3443
- ],
3444
- intensity: 0.4
3445
- }),
3446
- d("directionalLight", {
3447
- position: [
3448
- 0,
3449
- -10,
3450
- 0
3451
- ],
3452
- intensity: 0.2
3453
- }),
3454
- o && x === "3D" && d(Ln, {
3455
- mesh: o,
3456
- maxPoints: 2,
3457
- meshColor: xt,
3458
- meshOpacity: xe ? 0.3 : Ht
3459
- }),
3460
- he && fe && x === "3D" && d("mesh", {
3461
- geometry: fe,
3462
- renderOrder: 1,
3463
- children: d("meshStandardMaterial", {
3464
- color: "#c8c8c8",
3465
- transparent: true,
3466
- opacity: 0.35,
3467
- side: J.DoubleSide,
3468
- depthWrite: false,
3469
- polygonOffset: true,
3470
- polygonOffsetFactor: 1,
3471
- polygonOffsetUnits: 1
3472
- })
3473
- }),
3474
- o && x === "3D" && d(Tn, {
3475
- modelSize: r,
3476
- labels: [
3477
- "Origin",
3478
- R === "AK" ? "IT/Perineum" : "MPT",
3479
- "Cut Plane"
3480
- ]
3481
- }),
3482
- d($n, {
3483
- modelSize: r,
3484
- isAligned: be,
3485
- isCut: Gt,
3486
- mesh: o,
3487
- viewMode: x,
3488
- sliceY: be && ce.length >= 2 ? G ?? re ?? ce[1].position.y : void 0,
3489
- landmarkCount: ce.length
3490
- }),
3491
- !be && d(Tt, {
3492
- enableDamping: false
3493
- }),
3494
- be && se.length > 0 && o && x === "3D" && d(Nn, {
3495
- mesh: o,
3496
- isDragging: false
3497
- }),
3498
- o && be && ce.length >= 3 && (() => {
3499
- const Z = o.geometry.getIndex();
3500
- if (!Z || Z.count < 30) return null;
3501
- const ue = ce[2], K = ce[1], Be = I ?? ue.position.y, te = G ?? re ?? K.position.y;
3502
- return x === "2D" ? d(Gn, {
3503
- mesh: o,
3504
- upperY: te,
3505
- originY: K.position.y,
3506
- modelSize: r,
3507
- meshColor: xt,
3508
- displayUnit: N
3509
- }) : V(He, {
3510
- children: [
3511
- d(_n, {
3512
- mesh: o,
3513
- startY: Be,
3514
- endY: te,
3515
- spacing: ct,
3516
- modelSize: r,
3517
- onMeasurementsChange: A,
3518
- reverseOrder: true,
3519
- displayUnit: N,
3520
- useInnerSurface: T && !j,
3521
- formMeasurements: ke ? Se : void 0,
3522
- originY: K.position.y
3523
- }),
3524
- (!T || j) && d(On, {
3525
- mesh: o,
3526
- greenY: K.position.y,
3527
- modelSize: r,
3528
- displayUnit: N
3529
- })
3530
- ]
3531
- });
3532
- })(),
3533
- o && be && x === "3D" && d(so, {
3534
- mesh: o
3535
- }),
3536
- o && e.showDebug && xe && x === "3D" && d(oo, {
3537
- mesh: o,
3538
- modelSize: r,
3539
- layers: pe,
3540
- landmarkPoints: ce,
3541
- componentDebug: O
3542
- })
3543
- ]
3544
- }),
3545
- o && (e.showStartOver || e.showInsertMeasurement) && V("div", {
3546
- style: {
3547
- position: "absolute",
3548
- top: 16,
3549
- left: 16,
3550
- zIndex: 10,
3551
- display: "flex",
3552
- gap: 8
3553
- },
3554
- children: [
3555
- e.showStartOver && d("button", {
3556
- onClick: () => window.location.reload(),
3557
- style: {
3558
- padding: "6px 16px",
3559
- borderRadius: 4,
3560
- fontSize: 13,
3561
- fontWeight: 500,
3562
- backgroundColor: "#fff",
3563
- border: "1px solid #bdbdbd",
3564
- color: "#333",
3565
- cursor: "pointer",
3566
- fontFamily: "system-ui, sans-serif",
3567
- letterSpacing: "0.4px",
3568
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3569
- },
3570
- children: "Start Over"
3571
- }),
3572
- e.showInsertMeasurement && d("button", {
3573
- onClick: () => Me(true),
3574
- style: {
3575
- padding: "6px 16px",
3576
- borderRadius: 4,
3577
- fontSize: 13,
3578
- fontWeight: 500,
3579
- backgroundColor: "rgb(12, 67, 173)",
3580
- border: "none",
3581
- color: "#fff",
3582
- cursor: "pointer",
3583
- fontFamily: "system-ui, sans-serif",
3584
- letterSpacing: "0.4px",
3585
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3586
- },
3587
- children: "Insert Measurement"
3588
- })
3589
- ]
3590
- }),
3591
- e.showDebug && xe && o && x === "3D" && d(io, {
3592
- layers: pe,
3593
- onToggleLayer: (W) => we((Z) => ({
3594
- ...Z,
3595
- [W]: !Z[W]
3596
- }))
3597
- }),
3598
- be && se.length > 0 && V("div", {
3599
- style: {
3600
- position: "absolute",
3601
- top: 16,
3602
- right: 16,
3603
- display: "flex",
3604
- gap: 8,
3605
- zIndex: 10
3606
- },
3607
- children: [
3608
- V("div", {
3609
- style: {
3610
- display: "flex",
3611
- borderRadius: 6,
3612
- overflow: "hidden",
3613
- border: "1px solid #ccc",
3614
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3615
- },
3616
- children: [
3617
- d("button", {
3618
- onClick: () => b("3D"),
3619
- style: {
3620
- padding: "6px 14px",
3621
- fontSize: 13,
3622
- fontWeight: x === "3D" ? 600 : 400,
3623
- backgroundColor: x === "3D" ? "rgb(12, 67, 173)" : "#fff",
3624
- color: x === "3D" ? "#fff" : "#666",
3625
- border: "none",
3626
- cursor: "pointer",
3627
- fontFamily: "system-ui, sans-serif"
3628
- },
3629
- children: "Frontal"
3630
- }),
3631
- d("button", {
3632
- onClick: () => b("2D"),
3633
- style: {
3634
- padding: "6px 14px",
3635
- fontSize: 13,
3636
- fontWeight: x === "2D" ? 600 : 400,
3637
- backgroundColor: x === "2D" ? "rgb(12, 67, 173)" : "#fff",
3638
- color: x === "2D" ? "#fff" : "#666",
3639
- border: "none",
3640
- borderLeft: "1px solid #ccc",
3641
- cursor: "pointer",
3642
- fontFamily: "system-ui, sans-serif"
3643
- },
3644
- children: "Transverse"
3645
- })
3646
- ]
3647
- }),
3648
- V("div", {
3649
- style: {
3650
- display: "flex",
3651
- borderRadius: 6,
3652
- overflow: "hidden",
3653
- border: "1px solid #ccc",
3654
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3655
- },
3656
- children: [
3657
- d("button", {
3658
- onClick: () => ne("mm"),
3659
- style: {
3660
- padding: "6px 14px",
3661
- fontSize: 13,
3662
- fontWeight: N === "mm" ? 600 : 400,
3663
- backgroundColor: N === "mm" ? "rgb(12, 67, 173)" : "#fff",
3664
- color: N === "mm" ? "#fff" : "#666",
3665
- border: "none",
3666
- cursor: "pointer",
3667
- fontFamily: "system-ui, sans-serif"
3668
- },
3669
- children: "mm"
3670
- }),
3671
- d("button", {
3672
- onClick: () => ne("inch"),
3673
- style: {
3674
- padding: "6px 14px",
3675
- fontSize: 13,
3676
- fontWeight: N === "inch" ? 600 : 400,
3677
- backgroundColor: N === "inch" ? "rgb(12, 67, 173)" : "#fff",
3678
- color: N === "inch" ? "#fff" : "#666",
3679
- border: "none",
3680
- borderLeft: "1px solid #ccc",
3681
- cursor: "pointer",
3682
- fontFamily: "system-ui, sans-serif"
3683
- },
3684
- children: "in"
3685
- })
3686
- ]
3687
- }),
3688
- e.showSpacingToggle && x === "3D" && V("div", {
3689
- style: {
3690
- display: "flex",
3691
- borderRadius: 6,
3692
- overflow: "hidden",
3693
- border: "1px solid #ccc",
3694
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3695
- },
3696
- children: [
3697
- d("button", {
3698
- onClick: () => C(1),
3699
- style: {
3700
- padding: "6px 14px",
3701
- fontSize: 13,
3702
- fontWeight: w === 1 ? 600 : 400,
3703
- backgroundColor: w === 1 ? "rgb(12, 67, 173)" : "#fff",
3704
- color: w === 1 ? "#fff" : "#666",
3705
- border: "none",
3706
- cursor: "pointer",
3707
- fontFamily: "system-ui, sans-serif"
3708
- },
3709
- children: '1"'
3710
- }),
3711
- d("button", {
3712
- onClick: () => C(2),
3713
- style: {
3714
- padding: "6px 14px",
3715
- fontSize: 13,
3716
- fontWeight: w === 2 ? 600 : 400,
3717
- backgroundColor: w === 2 ? "rgb(12, 67, 173)" : "#fff",
3718
- color: w === 2 ? "#fff" : "#666",
3719
- border: "none",
3720
- borderLeft: "1px solid #ccc",
3721
- cursor: "pointer",
3722
- fontFamily: "system-ui, sans-serif"
3723
- },
3724
- children: '2"'
3725
- })
3726
- ]
3727
- }),
3728
- x === "3D" && fe && d("div", {
3729
- style: {
3730
- display: "flex",
3731
- borderRadius: 6,
3732
- overflow: "hidden",
3733
- border: "1px solid #ccc",
3734
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3735
- },
3736
- children: d("button", {
3737
- onClick: () => Ge((W) => !W),
3738
- style: {
3739
- padding: "6px 14px",
3740
- fontSize: 13,
3741
- fontWeight: he ? 600 : 400,
3742
- backgroundColor: he ? "rgb(12, 67, 173)" : "#fff",
3743
- color: he ? "#fff" : "#666",
3744
- border: "none",
3745
- cursor: "pointer",
3746
- fontFamily: "system-ui, sans-serif"
3747
- },
3748
- children: "Show Full Scan"
3749
- })
3750
- }),
3751
- x === "3D" && Se && d("div", {
3752
- style: {
3753
- display: "flex",
3754
- borderRadius: 6,
3755
- overflow: "hidden",
3756
- border: "1px solid #ccc",
3757
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3758
- },
3759
- children: d("button", {
3760
- onClick: () => Ue((W) => !W),
3761
- style: {
3762
- padding: "6px 14px",
3763
- fontSize: 13,
3764
- fontWeight: ke ? 600 : 400,
3765
- backgroundColor: ke ? "rgb(12, 67, 173)" : "#fff",
3766
- color: ke ? "#fff" : "#666",
3767
- border: "none",
3768
- cursor: "pointer",
3769
- fontFamily: "system-ui, sans-serif"
3770
- },
3771
- children: "Form Overlay"
3772
- })
3773
- }),
3774
- e.showDebug && a && d("div", {
3775
- style: {
3776
- display: "flex",
3777
- borderRadius: 6,
3778
- overflow: "hidden",
3779
- border: "1px solid #ccc",
3780
- boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
3781
- },
3782
- children: d("button", {
3783
- onClick: () => Re((W) => !W),
3784
- style: {
3785
- padding: "6px 14px",
3786
- fontSize: 13,
3787
- fontWeight: xe ? 600 : 400,
3788
- backgroundColor: xe ? "#e65100" : "#fff",
3789
- color: xe ? "#fff" : "#666",
3790
- border: "none",
3791
- cursor: "pointer",
3792
- fontFamily: "system-ui, sans-serif"
3793
- },
3794
- children: "Debug"
3795
- })
3796
- })
3797
- ]
3798
- }),
3799
- o && _ && d(jn, {
3800
- wasAutoScaled: S,
3801
- onDismiss: () => B(false)
3802
- }),
3803
- o && Q && be && d(Hn, {
3804
- isDoubleShell: T,
3805
- onDismiss: () => ie(false)
3806
- })
3807
- ]
3808
- }),
3809
- V("div", {
3810
- style: {
3811
- padding: "12px 24px",
3812
- backgroundColor: "#fff",
3813
- borderTop: "1px solid #e0e0e0",
3814
- display: "flex",
3815
- alignItems: "center",
3816
- justifyContent: "flex-end",
3817
- gap: 8,
3818
- flexShrink: 0
3819
- },
3820
- children: [
3821
- o && !be && ce.length >= 2 && d("button", {
3822
- onClick: Qt,
3823
- style: {
3824
- padding: "6px 16px",
3825
- borderRadius: 4,
3826
- fontSize: 14,
3827
- fontWeight: 500,
3828
- backgroundColor: "rgb(12, 67, 173)",
3829
- border: "none",
3830
- color: "#fff",
3831
- cursor: "pointer",
3832
- fontFamily: "system-ui, sans-serif",
3833
- letterSpacing: "0.4px",
3834
- lineHeight: "36px"
3835
- },
3836
- children: "Next \xBB"
3837
- }),
3838
- o && !be && ce.length >= 1 && d("button", {
3839
- onClick: Yt,
3840
- style: {
3841
- padding: "6px 16px",
3842
- borderRadius: 4,
3843
- fontSize: 14,
3844
- fontWeight: 500,
3845
- backgroundColor: "#fff",
3846
- border: "1px solid #bdbdbd",
3847
- color: "#333",
3848
- cursor: "pointer",
3849
- fontFamily: "system-ui, sans-serif",
3850
- letterSpacing: "0.4px",
3851
- lineHeight: "36px"
3852
- },
3853
- children: "Reset Points"
3854
- }),
3855
- be && d("button", {
3856
- onClick: qt,
3857
- disabled: se.length === 0,
3858
- style: {
3859
- padding: "6px 16px",
3860
- borderRadius: 4,
3861
- fontSize: 14,
3862
- fontWeight: 500,
3863
- backgroundColor: se.length > 0 ? "rgb(12, 67, 173)" : "#e0e0e0",
3864
- border: "none",
3865
- color: se.length > 0 ? "#fff" : "#9e9e9e",
3866
- cursor: se.length > 0 ? "pointer" : "not-allowed",
3867
- fontFamily: "system-ui, sans-serif",
3868
- letterSpacing: "0.4px",
3869
- lineHeight: "36px"
3870
- },
3871
- children: "Continue to Next Step"
3872
- })
3873
- ]
3874
- })
3875
- ]
3876
- })
3877
- ]
3878
- }),
3879
- e.showInsertMeasurement && Te && (() => {
3880
- const W = R === "AK" ? "IT/Perineum" : "MPT", Z = w, ue = [];
3881
- for (let K = 2; K >= 1; K -= Z) ue.push(`${K}\u2033 above ${W}`);
3882
- ue.push(`At ${W}`);
3883
- for (let K = Z; K <= 9; K += Z) ue.push(`${K}\u2033 below ${W}`);
3884
- return d("div", {
3885
- style: {
3886
- position: "fixed",
3887
- inset: 0,
3888
- backgroundColor: "rgba(0,0,0,0.32)",
3889
- display: "flex",
3890
- alignItems: "center",
3891
- justifyContent: "flex-start",
3892
- paddingLeft: 40,
3893
- zIndex: 9999
3894
- },
3895
- children: V("div", {
3896
- style: {
3897
- backgroundColor: "#fff",
3898
- borderRadius: 4,
3899
- width: 620,
3900
- maxHeight: "80vh",
3901
- display: "flex",
3902
- flexDirection: "column",
3903
- boxShadow: "0 11px 15px -7px rgba(0,0,0,0.2), 0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12)",
3904
- fontFamily: "system-ui, sans-serif"
3905
- },
3906
- children: [
3907
- V("div", {
3908
- style: {
3909
- padding: "24px 24px 0"
3910
- },
3911
- children: [
3912
- V("div", {
3913
- style: {
3914
- fontSize: 20,
3915
- fontWeight: 500,
3916
- color: "rgba(0,0,0,0.87)",
3917
- marginBottom: 4
3918
- },
3919
- children: [
3920
- R,
3921
- " Circumferences"
3922
- ]
3923
- }),
3924
- V("div", {
3925
- style: {
3926
- fontSize: 13,
3927
- color: "rgba(0,0,0,0.54)",
3928
- marginBottom: 20
3929
- },
3930
- children: [
3931
- "Enter form measurements (mm). ",
3932
- Z,
3933
- "\u2033",
3934
- " spacing."
3935
- ]
3936
- })
3937
- ]
3938
- }),
3939
- d("div", {
3940
- style: {
3941
- padding: "0 24px",
3942
- overflowY: "auto",
3943
- flex: 1
3944
- },
3945
- children: d("div", {
3946
- style: {
3947
- display: "grid",
3948
- gridTemplateColumns: "1fr 1fr",
3949
- gap: "16px 24px"
3950
- },
3951
- children: ue.map((K, Be) => V("div", {
3952
- children: [
3953
- V("label", {
3954
- style: {
3955
- display: "block",
3956
- fontSize: 13,
3957
- fontWeight: 500,
3958
- color: "rgb(12, 67, 173)",
3959
- marginBottom: 6
3960
- },
3961
- children: [
3962
- K,
3963
- " (mm):"
3964
- ]
3965
- }),
3966
- d("input", {
3967
- type: "number",
3968
- step: "0.1",
3969
- value: Ae[K] ?? "",
3970
- onChange: (te) => De((ve) => ({
3971
- ...ve,
3972
- [K]: te.target.value
3973
- })),
3974
- style: {
3975
- width: "100%",
3976
- padding: "10px 12px",
3977
- fontSize: 15,
3978
- border: "2px solid rgb(12, 67, 173)",
3979
- borderRadius: 4,
3980
- outline: "none",
3981
- boxSizing: "border-box",
3982
- fontFamily: "system-ui, sans-serif"
3983
- }
3984
- })
3985
- ]
3986
- }, Be))
3987
- })
3988
- }),
3989
- V("div", {
3990
- style: {
3991
- display: "flex",
3992
- justifyContent: "flex-end",
3993
- gap: 8,
3994
- padding: "16px 24px",
3995
- borderTop: "1px solid #e0e0e0",
3996
- marginTop: 16
3997
- },
3998
- children: [
3999
- d("button", {
4000
- onClick: () => {
4001
- Me(false), De({});
4002
- },
4003
- style: {
4004
- padding: "6px 16px",
4005
- borderRadius: 4,
4006
- fontSize: 14,
4007
- fontWeight: 500,
4008
- backgroundColor: "#fff",
4009
- border: "1px solid #bdbdbd",
4010
- color: "rgba(0,0,0,0.87)",
4011
- cursor: "pointer",
4012
- fontFamily: "system-ui, sans-serif",
4013
- lineHeight: "36px",
4014
- letterSpacing: "0.4px"
4015
- },
4016
- children: "Cancel"
4017
- }),
4018
- d("button", {
4019
- onClick: () => {
4020
- const K = ue.map((te) => parseFloat(Ae[te] || "")), Be = K.filter((te) => !isNaN(te));
4021
- Ee(Be.length > 0 ? K.map((te) => isNaN(te) ? void 0 : te) : void 0), Me(false);
4022
- },
4023
- style: {
4024
- padding: "6px 16px",
4025
- borderRadius: 4,
4026
- fontSize: 14,
4027
- fontWeight: 500,
4028
- backgroundColor: "rgb(12, 67, 173)",
4029
- border: "none",
4030
- color: "#fff",
4031
- cursor: "pointer",
4032
- fontFamily: "system-ui, sans-serif",
4033
- lineHeight: "36px",
4034
- letterSpacing: "0.4px"
4035
- },
4036
- children: "Save"
4037
- })
4038
- ]
4039
- })
4040
- ]
4041
- })
4042
- });
4043
- })()
4044
- ]
4045
- });
4046
- };
4047
- jt = function(e) {
4048
- const s = e === "AK" ? 2 : 1, n = [];
4049
- for (let t = 2; t >= 1; t -= s) n.push(`${t}_above`);
4050
- n.push("at_ref");
4051
- for (let t = s; t <= 9; t += s) n.push(`${t}_below`);
4052
- return n;
4053
- };
4054
- ut = function(e, s) {
4055
- const n = jt(s), t = {};
4056
- for (let i = 0; i < Math.min(e.length, n.length); i++) {
4057
- const a = e[i];
4058
- a != null && !isNaN(a) && (t[n[i]] = a);
4059
- }
4060
- return t;
4061
- };
4062
- ao = function(e, s) {
4063
- if (!e) return;
4064
- const t = jt(s).map((i) => {
4065
- const a = e[i];
4066
- return a ?? void 0;
4067
- });
4068
- if (!t.every((i) => i == null)) return t;
4069
- };
4070
- bo = ({ request: e, onComplete: s, wasmModule: n }) => {
4071
- const t = de(() => ao(e.form_measurements, e.spacing_type), [
4072
- e.form_measurements,
4073
- e.spacing_type
4074
- ]), i = (a) => {
4075
- const c = a.scanMeasurements.map((f) => +(f.modifiedValue ?? f.originalValue).toFixed(1)), u = ut(c, e.spacing_type);
4076
- let o, l;
4077
- if (a.formMeasurements) {
4078
- o = ut(a.formMeasurements, e.spacing_type);
4079
- const f = a.scanMeasurements.map((y, p) => {
4080
- var _a;
4081
- const m = (_a = a.formMeasurements) == null ? void 0 : _a[p];
4082
- return m == null || isNaN(m) ? null : +((y.modifiedValue ?? y.originalValue) - m).toFixed(1);
4083
- });
4084
- l = ut(f, e.spacing_type);
4085
- }
4086
- const r = {
4087
- spacing_type: e.spacing_type,
4088
- source_unit: "mm",
4089
- file_format: a.fileFormat,
4090
- measurement_source: a.formMeasurements ? "form_provided" : "scan_derived",
4091
- is_double_wall: a.isDoubleWall,
4092
- is_unit_converted: false,
4093
- form_measurements: o,
4094
- scan_measurements: u,
4095
- measurement_variance: l,
4096
- scan_url: e.scan_url,
4097
- frontal_height: +a.frontalHeight.toFixed(1),
4098
- transverse_ml: +a.transverseML.toFixed(1),
4099
- transverse_ap: +a.transverseAP.toFixed(1)
4100
- };
4101
- console.log("[GirthManagerWidget] WidgetResponse:", JSON.stringify(r, null, 2)), s == null ? void 0 : s(r);
4102
- };
4103
- return d("div", {
4104
- style: {
4105
- width: "100%",
4106
- height: "100%",
4107
- display: "flex"
4108
- },
4109
- children: d(lo, {
4110
- config: fn,
4111
- spacingType: e.spacing_type,
4112
- scanUrl: e.scan_url,
4113
- formMeasurements: t,
4114
- onComplete: i,
4115
- wasmModule: n
4116
- })
4117
- });
4118
- };
4119
- })();
4120
- export {
4121
- bo as G,
4122
- __tla,
4123
- ut as a,
4124
- ao as c,
4125
- jt as g
4126
- };