@toolpath/viewer 2.5.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -106,6 +106,7 @@ import {
106
106
  formatMillimetres,
107
107
  groupByDirection,
108
108
  hitUnderRay,
109
+ ignoreRay,
109
110
  labelGeometry,
110
111
  labelTexture,
111
112
  lockToAxis,
@@ -163,7 +164,7 @@ import {
163
164
  viewUp,
164
165
  viewVector,
165
166
  visualSurfaces
166
- } from "./chunk-LBG6GVGE.js";
167
+ } from "./chunk-EYNPC2YB.js";
167
168
 
168
169
  // src/render/feature-triangles.ts
169
170
  var EMPTY = { positions: new Float32Array(), normals: new Float32Array() };
@@ -208,12 +209,771 @@ var winding = ([a, b, c]) => {
208
209
  return [u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]];
209
210
  };
210
211
 
212
+ // src/model/explode-amount.ts
213
+ var readExplodeAmount = (amount) => {
214
+ const value = typeof amount === "number" ? amount : amount.value;
215
+ return Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : 0;
216
+ };
217
+ var createExplodeAmount = (initial = 0) => {
218
+ let value = readExplodeAmount(initial);
219
+ const listeners = /* @__PURE__ */ new Set();
220
+ return {
221
+ get value() {
222
+ return value;
223
+ },
224
+ set: (next) => {
225
+ next = readExplodeAmount(next);
226
+ if (next === value) {
227
+ return;
228
+ }
229
+ value = next;
230
+ for (const listener of listeners) {
231
+ listener();
232
+ }
233
+ },
234
+ subscribe: (listener) => {
235
+ const subscription = () => listener();
236
+ listeners.add(subscription);
237
+ return () => {
238
+ listeners.delete(subscription);
239
+ };
240
+ }
241
+ };
242
+ };
243
+
244
+ // src/exploded-part.tsx
245
+ import { useEffect, useLayoutEffect, useMemo, useRef } from "react";
246
+ import { useFrame, useThree } from "@react-three/fiber";
247
+ import {
248
+ DoubleSide as DoubleSide2,
249
+ Vector4
250
+ } from "three";
251
+
252
+ // src/render/explode-theme.ts
253
+ var EXPLODE_DEFAULTS = {
254
+ ghost: 11913180,
255
+ ghostEdge: 10860497,
256
+ ghostEmissive: 0,
257
+ edge: 1582389,
258
+ edgeOpacity: 0.9,
259
+ selected: 16777215,
260
+ hovered: 16777215,
261
+ source: 16767619
262
+ };
263
+ var resolveExplodeTheme = (theme) => ({
264
+ ghost: theme?.part ?? EXPLODE_DEFAULTS.ghost,
265
+ ghostEdge: theme?.edge ?? EXPLODE_DEFAULTS.ghostEdge,
266
+ ghostEmissive: theme?.partEmissive ?? EXPLODE_DEFAULTS.ghostEmissive,
267
+ emissive: theme?.partEmissive ?? DEFAULT_THEME.partEmissive,
268
+ edge: theme?.edge ?? EXPLODE_DEFAULTS.edge,
269
+ edgeOpacity: theme?.edgeOpacity ?? EXPLODE_DEFAULTS.edgeOpacity,
270
+ selected: theme?.highlight ?? EXPLODE_DEFAULTS.selected,
271
+ hovered: theme?.hover ?? EXPLODE_DEFAULTS.hovered,
272
+ // The selected piece's source surfaces are what was picked, apart from its selection tint.
273
+ source: theme?.picked ?? EXPLODE_DEFAULTS.source
274
+ });
275
+ var DEFAULT_EXPLODE_THEME = resolveExplodeTheme();
276
+ var edgeOpacityScale = (theme) => theme.edgeOpacity / EXPLODE_DEFAULTS.edgeOpacity;
277
+
278
+ // src/render/explode.ts
279
+ import {
280
+ BatchedMesh,
281
+ Box3,
282
+ BufferGeometry as BufferGeometry2,
283
+ Color,
284
+ DoubleSide,
285
+ Float32BufferAttribute as Float32BufferAttribute2,
286
+ Group,
287
+ LineBasicMaterial,
288
+ LineSegments,
289
+ Matrix3,
290
+ Matrix4,
291
+ Mesh,
292
+ MeshLambertMaterial,
293
+ Vector3,
294
+ Uint32BufferAttribute
295
+ } from "three";
296
+
297
+ // src/render/explode-geometry.ts
298
+ import { BufferGeometry, Float32BufferAttribute } from "three";
299
+ var explodeTriangleCount = (model, geometry, groups) => {
300
+ if (geometry.index) {
301
+ throw new Error("ExplodedPart requires non-indexed source geometry.");
302
+ }
303
+ const position = geometry.getAttribute("position");
304
+ if (!position || position.itemSize !== 3 || position.count !== model.mesh.triangleCount * 3) {
305
+ throw new Error("ExplodedPart source geometry must match the model triangle ranges.");
306
+ }
307
+ const ids = /* @__PURE__ */ new Set();
308
+ let triangles = 0;
309
+ for (const group of groups) {
310
+ if (!group.id || ids.has(group.id)) {
311
+ throw new Error("ExplodedPart group IDs must be nonempty and unique.");
312
+ }
313
+ ids.add(group.id);
314
+ if (![group.offset.x, group.offset.y, group.offset.z].every(Number.isFinite)) {
315
+ throw new Error(`ExplodedPart group ${group.id} has a nonfinite offset.`);
316
+ }
317
+ if (group.regionIdxs.length === 0) {
318
+ throw new Error(`ExplodedPart group ${group.id} has no regions.`);
319
+ }
320
+ for (const region of new Set(group.regionIdxs)) {
321
+ const range = model.regionIndex.rangeForRegion(region);
322
+ if (!range) {
323
+ throw new Error(`Unknown source region ${region}`);
324
+ }
325
+ triangles += range.end - range.start;
326
+ }
327
+ }
328
+ return triangles;
329
+ };
330
+ var extractExplodeGroup = (source, geometry, group) => {
331
+ const regions = [...new Set(group.regionIdxs)].map((idx) => {
332
+ const region = source.regions.find((region2) => region2.idx === idx);
333
+ if (!region) {
334
+ throw new Error(`Unknown source region ${idx}`);
335
+ }
336
+ return region;
337
+ });
338
+ const triangles = regions.reduce(
339
+ (count, region) => count + region.triangles.end - region.triangles.start,
340
+ 0
341
+ );
342
+ const positions = new Float32Array(triangles * 9);
343
+ const normals = new Float32Array(triangles * 9);
344
+ const sourceTriangles = new Uint32Array(triangles);
345
+ const position = geometry.getAttribute("position");
346
+ const normal = geometry.getAttribute("normal");
347
+ if (geometry.index) {
348
+ throw new Error("Explode requires the viewer\u2019s non-indexed input geometry.");
349
+ }
350
+ let cursor = 0;
351
+ const compact = regions.map((region) => {
352
+ const start = cursor;
353
+ for (let t = region.triangles.start; t < region.triangles.end; t += 1) {
354
+ sourceTriangles[cursor] = t;
355
+ for (let corner = 0; corner < 3; corner += 1) {
356
+ const v = t * 3 + corner;
357
+ const at = cursor * 9 + corner * 3;
358
+ positions.set([position.getX(v), position.getY(v), position.getZ(v)], at);
359
+ if (normal) {
360
+ normals.set([normal.getX(v), normal.getY(v), normal.getZ(v)], at);
361
+ }
362
+ }
363
+ cursor += 1;
364
+ }
365
+ return { ...region, triangles: { start, end: cursor } };
366
+ });
367
+ const copy = new BufferGeometry();
368
+ copy.setAttribute("position", new Float32BufferAttribute(positions, 3));
369
+ if (normal) {
370
+ copy.setAttribute("normal", new Float32BufferAttribute(normals, 3));
371
+ } else {
372
+ copy.computeVertexNormals();
373
+ }
374
+ return { regions: compact, geometry: copy, sourceTriangles };
375
+ };
376
+
377
+ // src/render/explode.ts
378
+ var ASSEMBLED = 1e-4;
379
+ var FADE_IN = 0.1;
380
+ var SELECTED_TINT = 0.45;
381
+ var HOVERED_TINT = 0.25;
382
+ var SOURCE_LIGHTEN = 0.45;
383
+ var WHITE = new Color(16777215);
384
+ var SURFACE_MATERIAL = {
385
+ emissive: DEFAULT_EXPLODE_THEME.emissive,
386
+ side: DoubleSide,
387
+ forceSinglePass: true,
388
+ transparent: true,
389
+ polygonOffset: true,
390
+ polygonOffsetFactor: 1,
391
+ polygonOffsetUnits: 1
392
+ };
393
+ var idsOf = (value) => value === null ? [] : typeof value === "string" ? [value] : value;
394
+ var overlay = (object, renderOrder = 0) => {
395
+ object.visible = false;
396
+ object.renderOrder = renderOrder;
397
+ object.frustumCulled = false;
398
+ object.raycast = ignoreRay;
399
+ object.userData[EXCLUDE_FROM_FRAME] = true;
400
+ return object;
401
+ };
402
+ var buildSurfaces = (model, geometry, groups) => {
403
+ const triangles = explodeTriangleCount(model, geometry, groups);
404
+ if (groups.length === 0) {
405
+ throw new Error("An explode batch needs at least one group.");
406
+ }
407
+ const material = new MeshLambertMaterial({
408
+ ...SURFACE_MATERIAL,
409
+ color: 16777215,
410
+ vertexColors: true
411
+ });
412
+ const mesh = new BatchedMesh(groups.length, triangles * 3, 0, material);
413
+ mesh.name = "explode-surfaces";
414
+ mesh.renderOrder = 3;
415
+ mesh.frustumCulled = false;
416
+ const instanceColors = typeof mesh.setColorAt === "function";
417
+ const edgePoints = [];
418
+ let vertexStart = 0;
419
+ let edgeStart = 0;
420
+ const pieces = groups.map((group) => {
421
+ const extracted = extractExplodeGroup(model, geometry, group);
422
+ const edges = regionEdgesGeometry(extracted.geometry, extracted);
423
+ const points = edges.getAttribute("position").array;
424
+ const vertexCount = extracted.geometry.getAttribute("position").count;
425
+ extracted.geometry.setAttribute(
426
+ "color",
427
+ new Float32BufferAttribute2(new Float32Array(vertexCount * 3).fill(1), 3)
428
+ );
429
+ const geometryId = mesh.addGeometry(extracted.geometry);
430
+ const instanceId = typeof mesh.addInstance === "function" ? mesh.addInstance(geometryId) : geometryId;
431
+ const range = { start: vertexStart, count: vertexCount };
432
+ const regions = extracted.regions.map((region) => ({
433
+ idx: region.idx,
434
+ start: vertexStart + region.triangles.start * 3,
435
+ count: (region.triangles.end - region.triangles.start) * 3
436
+ }));
437
+ vertexStart += vertexCount;
438
+ edgePoints.push(points);
439
+ extracted.geometry.computeBoundingBox();
440
+ const piece = {
441
+ group,
442
+ instanceId,
443
+ range,
444
+ regions,
445
+ sourceTriangles: extracted.sourceTriangles,
446
+ centre: extracted.geometry.boundingBox.getCenter(new Vector3()),
447
+ bounds: extracted.geometry.boundingBox.clone(),
448
+ edgeStart,
449
+ edgeCount: points.length / 3
450
+ };
451
+ edgeStart += piece.edgeCount;
452
+ extracted.geometry.dispose();
453
+ edges.dispose();
454
+ return piece;
455
+ });
456
+ return { mesh, material, instanceColors, pieces, edgePoints, triangles, edgeVertices: edgeStart };
457
+ };
458
+ var buildOutlines = (pieces, edgePoints, edgeVertices) => {
459
+ const positions = new Float32Array(edgeVertices * 3);
460
+ const offsets = new Float32Array(edgeVertices * 3);
461
+ for (const [index, piece] of pieces.entries()) {
462
+ positions.set(edgePoints[index], piece.edgeStart * 3);
463
+ for (let vertex = piece.edgeStart; vertex < piece.edgeStart + piece.edgeCount; vertex += 1) {
464
+ offsets[vertex * 3] = piece.group.offset.x;
465
+ offsets[vertex * 3 + 1] = piece.group.offset.y;
466
+ offsets[vertex * 3 + 2] = piece.group.offset.z;
467
+ }
468
+ }
469
+ const geometry = new BufferGeometry2();
470
+ geometry.setAttribute("position", new Float32BufferAttribute2(positions, 3));
471
+ geometry.setAttribute("aExplodeOffset", new Float32BufferAttribute2(offsets, 3));
472
+ geometry.boundingBox = new Box3();
473
+ const amount = { value: 0 };
474
+ const material = new LineBasicMaterial({
475
+ color: DEFAULT_EXPLODE_THEME.edge,
476
+ transparent: true,
477
+ depthWrite: false
478
+ });
479
+ material.onBeforeCompile = (shader) => {
480
+ shader.uniforms.uExplodeAmount = amount;
481
+ shader.vertexShader = shader.vertexShader.replace(
482
+ "#include <common>",
483
+ "#include <common>\nattribute vec3 aExplodeOffset;\nuniform float uExplodeAmount;"
484
+ ).replace(
485
+ "#include <begin_vertex>",
486
+ "#include <begin_vertex>\ntransformed += aExplodeOffset * uExplodeAmount;"
487
+ );
488
+ };
489
+ const outlines = new LineSegments(geometry, material);
490
+ outlines.name = "explode-edges";
491
+ outlines.renderOrder = 4;
492
+ outlines.frustumCulled = false;
493
+ outlines.raycast = ignoreRay;
494
+ return { outlines, amount };
495
+ };
496
+ var buildSourceSurfaces = (surfaces, triangles) => {
497
+ const geometry = new BufferGeometry2();
498
+ geometry.setAttribute("position", surfaces.geometry.getAttribute("position"));
499
+ geometry.setAttribute("normal", surfaces.geometry.getAttribute("normal"));
500
+ geometry.setIndex(new Uint32BufferAttribute(new Uint32Array(triangles * 3), 1));
501
+ geometry.setDrawRange(0, 0);
502
+ const material = new MeshLambertMaterial({
503
+ ...SURFACE_MATERIAL,
504
+ color: new Color(DEFAULT_EXPLODE_THEME.source).lerp(WHITE, SOURCE_LIGHTEN)
505
+ });
506
+ const source = overlay(new Mesh(geometry, material), 3);
507
+ source.name = "explode-origin";
508
+ return source;
509
+ };
510
+ var buildSourceOutlines = (outlines, edgeVertices) => {
511
+ const geometry = new BufferGeometry2();
512
+ geometry.setAttribute("position", outlines.getAttribute("position"));
513
+ geometry.setIndex(new Uint32BufferAttribute(new Uint32Array(edgeVertices), 1));
514
+ geometry.setDrawRange(0, 0);
515
+ const material = new LineBasicMaterial({
516
+ color: DEFAULT_EXPLODE_THEME.edge,
517
+ transparent: true,
518
+ opacity: DEFAULT_EXPLODE_THEME.edgeOpacity
519
+ });
520
+ return overlay(new LineSegments(geometry, material), 4);
521
+ };
522
+ var buildConnector = () => {
523
+ const geometry = new BufferGeometry2();
524
+ geometry.setAttribute("position", new Float32BufferAttribute2(new Float32Array(6), 3));
525
+ const material = new LineBasicMaterial({
526
+ transparent: true,
527
+ opacity: 0.85,
528
+ depthWrite: false
529
+ });
530
+ return overlay(new LineSegments(geometry, material));
531
+ };
532
+ var createExplodeBatch = (model, geometry, groups) => {
533
+ const { mesh, material, instanceColors, pieces, edgePoints, triangles, edgeVertices } = buildSurfaces(model, geometry, groups);
534
+ const { outlines, amount: amountUniform } = buildOutlines(pieces, edgePoints, edgeVertices);
535
+ const source = buildSourceSurfaces(mesh, triangles);
536
+ const sourceOutlines = buildSourceOutlines(outlines.geometry, edgeVertices);
537
+ const connector = buildConnector();
538
+ const object = new Group();
539
+ object.add(mesh, outlines, source, sourceOutlines, connector);
540
+ const byId = new Map(pieces.map((piece) => [piece.group.id, piece]));
541
+ const matrix = new Matrix4();
542
+ const inverse = new Matrix4();
543
+ const normalMatrix = new Matrix3();
544
+ const color = new Color();
545
+ const highlightColor = new Color();
546
+ const sourceBounds = new Box3();
547
+ const placedBounds = new Box3();
548
+ const movedBounds = new Box3();
549
+ const point = new Vector3();
550
+ const modelPoint = new Vector3();
551
+ const normal = new Vector3();
552
+ let theme = DEFAULT_EXPLODE_THEME;
553
+ let selectedPieces = [];
554
+ let amount = -1;
555
+ let previousColors;
556
+ let previousRegionColors;
557
+ let previousSelected;
558
+ let previousHovered;
559
+ let previousSelectedIds = /* @__PURE__ */ new Set();
560
+ let previousHoveredIds = /* @__PURE__ */ new Set();
561
+ let appearanceSet = false;
562
+ const syncConnector = () => {
563
+ const visible = selectedPieces.length > 0 && amount > ASSEMBLED;
564
+ source.visible = visible;
565
+ sourceOutlines.visible = visible;
566
+ connector.visible = visible;
567
+ if (selectedPieces.length === 0) {
568
+ return;
569
+ }
570
+ const points = connector.geometry.getAttribute("position");
571
+ sourceBounds.getCenter(point);
572
+ points.setXYZ(0, point.x, point.y, point.z);
573
+ placedBounds.makeEmpty();
574
+ for (const piece of selectedPieces) {
575
+ const offset = piece.group.offset;
576
+ point.set(offset.x * amount, offset.y * amount, offset.z * amount);
577
+ placedBounds.union(movedBounds.copy(piece.bounds).translate(point));
578
+ }
579
+ placedBounds.getCenter(point);
580
+ points.setXYZ(1, point.x, point.y, point.z);
581
+ points.needsUpdate = true;
582
+ };
583
+ const hit = (batchId, faceIndex) => {
584
+ if (batchId === void 0 || faceIndex === void 0) {
585
+ return null;
586
+ }
587
+ const piece = pieces[batchId];
588
+ const local = piece ? faceIndex - piece.range.start / 3 : -1;
589
+ return piece && local >= 0 && local < piece.sourceTriangles.length ? { group: piece.group, triangleIndex: piece.sourceTriangles[local] } : null;
590
+ };
591
+ return {
592
+ object,
593
+ mesh,
594
+ pieces,
595
+ hit,
596
+ /** A ray hit on the batch as a pick of its source surface, with the hit's unexploded point. */
597
+ pick: (intersection, context = {}) => {
598
+ const { face } = intersection;
599
+ if (amount <= ASSEMBLED || !face) {
600
+ return null;
601
+ }
602
+ const found = hit(intersection.batchId, intersection.faceIndex ?? void 0);
603
+ if (!found) {
604
+ return null;
605
+ }
606
+ const region = model.regionIndex.regionForTriangle(found.triangleIndex);
607
+ if (region === null) {
608
+ return null;
609
+ }
610
+ normal.copy(face.normal).applyNormalMatrix(normalMatrix.getNormalMatrix(mesh.matrixWorld));
611
+ const { offset } = found.group;
612
+ modelPoint.copy(intersection.point).applyMatrix4(inverse.copy(mesh.matrixWorld).invert());
613
+ modelPoint.x -= offset.x * amount;
614
+ modelPoint.y -= offset.y * amount;
615
+ modelPoint.z -= offset.z * amount;
616
+ return {
617
+ ...buildPick({
618
+ ...context,
619
+ model,
620
+ region,
621
+ triangleIndex: found.triangleIndex,
622
+ point: intersection.point.toArray(),
623
+ normal: normal.toArray()
624
+ }),
625
+ explodeGroupId: found.group.id,
626
+ modelPoint: modelPoint.toArray()
627
+ };
628
+ },
629
+ setAppearance: ({
630
+ colors,
631
+ regionColors,
632
+ selected = null,
633
+ hovered = null,
634
+ theme: nextTheme = DEFAULT_EXPLODE_THEME
635
+ }) => {
636
+ const themeChanged = theme !== nextTheme;
637
+ if (appearanceSet && !themeChanged && previousColors === colors && previousRegionColors === regionColors && previousSelected === selected && previousHovered === hovered) {
638
+ return;
639
+ }
640
+ if (themeChanged) {
641
+ theme = nextTheme;
642
+ material.emissive.setHex(theme.emissive);
643
+ source.material.emissive.setHex(theme.emissive);
644
+ source.material.color.setHex(theme.source).lerp(WHITE, SOURCE_LIGHTEN);
645
+ outlines.material.color.setHex(theme.edge);
646
+ outlines.material.opacity = theme.edgeOpacity * Math.min(1, Math.max(0, amount) / FADE_IN);
647
+ sourceOutlines.material.color.setHex(theme.edge);
648
+ sourceOutlines.material.opacity = theme.edgeOpacity;
649
+ }
650
+ const selectedIds = new Set(idsOf(selected));
651
+ const hoveredIds = new Set(idsOf(hovered));
652
+ for (const piece of pieces) {
653
+ const id = piece.group.id;
654
+ if (appearanceSet && !themeChanged && previousColors === colors && previousRegionColors === regionColors && !selectedIds.has(id) && !previousSelectedIds.has(id) && !hoveredIds.has(id) && !previousHoveredIds.has(id)) {
655
+ continue;
656
+ }
657
+ const base = colors?.get(id) ?? piece.group.color;
658
+ const regions = regionColors?.get(id);
659
+ const perVertex = !instanceColors || Boolean(regions?.size);
660
+ highlightColor.setHex(selectedIds.has(id) ? theme.selected : theme.hovered);
661
+ const tint = selectedIds.has(id) ? SELECTED_TINT : hoveredIds.has(id) ? HOVERED_TINT : 0;
662
+ const vertices = mesh.geometry.getAttribute("color");
663
+ if (perVertex || previousRegionColors?.get(id)?.size) {
664
+ for (const region of piece.regions) {
665
+ color.setHex(regions?.get(region.idx) ?? base).lerp(highlightColor, tint);
666
+ for (let vertex = region.start; vertex < region.start + region.count; vertex += 1) {
667
+ vertices.setXYZ(
668
+ vertex,
669
+ perVertex ? color.r : 1,
670
+ perVertex ? color.g : 1,
671
+ perVertex ? color.b : 1
672
+ );
673
+ }
674
+ }
675
+ vertices.needsUpdate = true;
676
+ }
677
+ if (instanceColors) {
678
+ color.setHex(base).lerp(highlightColor, tint);
679
+ mesh.setColorAt(piece.instanceId, perVertex ? WHITE : color);
680
+ }
681
+ }
682
+ previousColors = colors;
683
+ previousRegionColors = regionColors;
684
+ const selectionChanged = previousSelected !== selected || !appearanceSet;
685
+ previousSelected = selected;
686
+ previousHovered = hovered;
687
+ appearanceSet = true;
688
+ previousSelectedIds = selectedIds;
689
+ previousHoveredIds = hoveredIds;
690
+ if (selectionChanged) {
691
+ selectedPieces = [...selectedIds].flatMap((id) => {
692
+ const piece = byId.get(id);
693
+ return piece ? [piece] : [];
694
+ });
695
+ sourceBounds.makeEmpty();
696
+ let vertices = 0;
697
+ let edgeVertices2 = 0;
698
+ const indices = source.geometry.index;
699
+ const edgeIndices = sourceOutlines.geometry.index;
700
+ for (const piece of selectedPieces) {
701
+ sourceBounds.union(piece.bounds);
702
+ for (let i = 0; i < piece.range.count; i += 1) {
703
+ indices.setX(vertices++, piece.range.start + i);
704
+ }
705
+ for (let i = 0; i < piece.edgeCount; i += 1) {
706
+ edgeIndices.setX(edgeVertices2++, piece.edgeStart + i);
707
+ }
708
+ }
709
+ indices.needsUpdate = true;
710
+ edgeIndices.needsUpdate = true;
711
+ source.geometry.setDrawRange(0, vertices);
712
+ sourceOutlines.geometry.setDrawRange(0, edgeVertices2);
713
+ }
714
+ const first = selectedPieces[0];
715
+ if (first) {
716
+ connector.material.color.setHex(colors?.get(first.group.id) ?? first.group.color);
717
+ }
718
+ syncConnector();
719
+ },
720
+ setAmount: (next) => {
721
+ next = readExplodeAmount(next);
722
+ if (next === amount) {
723
+ return;
724
+ }
725
+ amount = next;
726
+ const fade = Math.min(1, amount / FADE_IN);
727
+ material.depthWrite = fade === 1;
728
+ material.opacity = fade;
729
+ mesh.visible = amount > ASSEMBLED;
730
+ outlines.visible = mesh.visible;
731
+ outlines.material.opacity = theme.edgeOpacity * fade;
732
+ amountUniform.value = amount;
733
+ for (const piece of pieces) {
734
+ const offset = piece.group.offset;
735
+ matrix.makeTranslation(offset.x * amount, offset.y * amount, offset.z * amount);
736
+ mesh.setMatrixAt(piece.instanceId, matrix);
737
+ }
738
+ mesh.computeBoundingBox();
739
+ outlines.geometry.boundingBox.copy(mesh.boundingBox);
740
+ syncConnector();
741
+ },
742
+ dispose: () => {
743
+ object.clear();
744
+ mesh.dispose();
745
+ material.dispose();
746
+ for (const drawn of [outlines, source, sourceOutlines, connector]) {
747
+ drawn.geometry.dispose();
748
+ drawn.material.dispose();
749
+ }
750
+ }
751
+ };
752
+ };
753
+
754
+ // src/exploded-part.tsx
755
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
756
+ var GHOST_FADE = 0.18;
757
+ var Ghost = ({
758
+ model,
759
+ geometry,
760
+ explode,
761
+ appearance,
762
+ visible = true
763
+ }) => {
764
+ const material = useRef(null);
765
+ const outline = useRef(null);
766
+ const edges = useMemo(() => regionEdgesGeometry(geometry, model), [geometry, model]);
767
+ useEffect(() => () => edges.dispose(), [edges]);
768
+ useFrame(() => {
769
+ if (!visible) {
770
+ return;
771
+ }
772
+ const fade = Math.min(1, readExplodeAmount(explode.amount) / GHOST_FADE);
773
+ if (material.current) {
774
+ material.current.opacity = 1 - 0.88 * fade;
775
+ material.current.depthWrite = fade === 0;
776
+ }
777
+ if (outline.current) {
778
+ outline.current.opacity = (0.7 - 0.46 * fade) * edgeOpacityScale(appearance);
779
+ }
780
+ }, -1);
781
+ return /* @__PURE__ */ jsxs("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: !visible }, children: [
782
+ /* @__PURE__ */ jsx("mesh", { geometry, raycast: ignoreRay, children: /* @__PURE__ */ jsx(
783
+ "meshLambertMaterial",
784
+ {
785
+ ref: material,
786
+ color: appearance.ghost,
787
+ emissive: appearance.ghostEmissive,
788
+ transparent: true,
789
+ opacity: 0.12,
790
+ depthWrite: false,
791
+ side: DoubleSide2,
792
+ forceSinglePass: true
793
+ }
794
+ ) }),
795
+ /* @__PURE__ */ jsx("lineSegments", { geometry: edges, raycast: ignoreRay, children: /* @__PURE__ */ jsx(
796
+ "lineBasicMaterial",
797
+ {
798
+ ref: outline,
799
+ color: appearance.ghostEdge,
800
+ transparent: true,
801
+ opacity: 0.24 * edgeOpacityScale(appearance),
802
+ depthWrite: false
803
+ }
804
+ ) })
805
+ ] });
806
+ };
807
+ var Pieces = ({
808
+ model,
809
+ geometry,
810
+ explode,
811
+ selected = null,
812
+ hovered = null,
813
+ activeDirection = null,
814
+ visible = true,
815
+ appearance,
816
+ onReady,
817
+ onPick,
818
+ onHover
819
+ }) => {
820
+ const { groups, colors, regionColors } = explode;
821
+ const invalidate = useThree((state) => state.invalidate);
822
+ const camera = useThree((state) => state.camera);
823
+ const gl = useThree((state) => state.gl);
824
+ const scene = useThree((state) => state.scene);
825
+ const isTap = useTapGuard();
826
+ const ready = useRef(onReady);
827
+ useLayoutEffect(() => {
828
+ ready.current = onReady;
829
+ }, [onReady]);
830
+ const root = useRef(null);
831
+ const resources = useRef(null);
832
+ useLayoutEffect(() => {
833
+ const parent = root.current;
834
+ const batch = createExplodeBatch(model, geometry, groups);
835
+ resources.current = batch;
836
+ parent.add(batch.object);
837
+ invalidate();
838
+ return () => {
839
+ resources.current = null;
840
+ parent.remove(batch.object);
841
+ batch.dispose();
842
+ };
843
+ }, [geometry, groups, invalidate, model]);
844
+ useLayoutEffect(() => {
845
+ resources.current?.setAppearance({
846
+ colors,
847
+ regionColors,
848
+ selected,
849
+ hovered,
850
+ theme: appearance
851
+ });
852
+ resources.current?.setAmount(readExplodeAmount(explode.amount));
853
+ invalidate();
854
+ }, [
855
+ appearance,
856
+ colors,
857
+ explode,
858
+ geometry,
859
+ groups,
860
+ hovered,
861
+ invalidate,
862
+ model,
863
+ regionColors,
864
+ selected
865
+ ]);
866
+ useEffect(() => {
867
+ const batch = resources.current;
868
+ const scheduled = setTimeout(() => {
869
+ const scissor = gl.getScissor(new Vector4());
870
+ const scissorTest = gl.getScissorTest();
871
+ const parent = root.current;
872
+ const parentVisible = parent.visible;
873
+ const visibility = /* @__PURE__ */ new Map();
874
+ batch.object.traverse((object) => {
875
+ visibility.set(object, object.visible);
876
+ object.visible = true;
877
+ });
878
+ parent.visible = true;
879
+ try {
880
+ gl.setScissor(0, 0, 0, 0);
881
+ gl.setScissorTest(true);
882
+ gl.render(scene, camera);
883
+ } finally {
884
+ gl.setScissor(scissor);
885
+ gl.setScissorTest(scissorTest);
886
+ parent.visible = parentVisible;
887
+ for (const [object, visible2] of visibility) {
888
+ object.visible = visible2;
889
+ }
890
+ invalidate();
891
+ }
892
+ ready.current?.();
893
+ }, 0);
894
+ return () => clearTimeout(scheduled);
895
+ }, [camera, geometry, gl, groups, invalidate, model, scene]);
896
+ useFrame(() => {
897
+ if (visible) {
898
+ resources.current?.setAmount(readExplodeAmount(explode.amount));
899
+ }
900
+ }, -1);
901
+ const pick = (event) => {
902
+ const source = event.nativeEvent;
903
+ return resources.current?.pick(event, {
904
+ activeDirection,
905
+ viewDirection: viewDirection(camera, event.point),
906
+ pointer: { clientX: source.clientX, clientY: source.clientY },
907
+ modifiers: {
908
+ alt: source.altKey,
909
+ ctrl: source.ctrlKey,
910
+ meta: source.metaKey,
911
+ shift: source.shiftKey,
912
+ secondary: source.button === 2
913
+ },
914
+ doubled: source.detail === 2
915
+ }) ?? null;
916
+ };
917
+ return /* @__PURE__ */ jsx(
918
+ "group",
919
+ {
920
+ ref: root,
921
+ name: "exploded-part",
922
+ visible,
923
+ userData: { [EXCLUDE_FROM_FRAME]: !visible },
924
+ onPointerMove: visible && onHover ? (event) => {
925
+ event.stopPropagation();
926
+ onHover?.(pick(event));
927
+ } : void 0,
928
+ onPointerOut: visible && onHover ? () => onHover(null) : void 0,
929
+ onClick: visible && onPick ? (event) => {
930
+ event.stopPropagation();
931
+ if (isTap(event.nativeEvent)) {
932
+ const hit = pick(event);
933
+ if (hit) {
934
+ onPick?.(hit);
935
+ }
936
+ }
937
+ } : void 0
938
+ }
939
+ );
940
+ };
941
+ var ExplodedPart = (props) => {
942
+ const { part, partEmissive, edge, edgeOpacity, highlight, hover, picked } = props.theme ?? {};
943
+ const appearance = useMemo(
944
+ () => resolveExplodeTheme({ part, partEmissive, edge, edgeOpacity, highlight, hover, picked }),
945
+ [part, partEmissive, edge, edgeOpacity, highlight, hover, picked]
946
+ );
947
+ const invalidate = useThree((state) => state.invalidate);
948
+ const amount = props.explode.amount;
949
+ useLayoutEffect(() => {
950
+ if (typeof amount !== "number") {
951
+ const unsubscribe = amount.subscribe(invalidate);
952
+ invalidate();
953
+ return unsubscribe;
954
+ }
955
+ }, [amount, invalidate]);
956
+ return props.explode.groups.length === 0 ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
957
+ /* @__PURE__ */ jsx(
958
+ Ghost,
959
+ {
960
+ model: props.model,
961
+ geometry: props.geometry,
962
+ explode: props.explode,
963
+ appearance,
964
+ visible: props.visible
965
+ }
966
+ ),
967
+ /* @__PURE__ */ jsx(Pieces, { ...props, appearance })
968
+ ] });
969
+ };
970
+
211
971
  // src/hover-card.tsx
212
- import { useEffect, useState } from "react";
213
- import { jsx } from "react/jsx-runtime";
972
+ import { useEffect as useEffect2, useState } from "react";
973
+ import { jsx as jsx2 } from "react/jsx-runtime";
214
974
  var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
215
975
  const [pointer, setPointer] = useState(pick?.pointer);
216
- useEffect(() => {
976
+ useEffect2(() => {
217
977
  setPointer(pick?.pointer);
218
978
  if (pick === null) return;
219
979
  const follow = (event) => setPointer({ clientX: event.clientX, clientY: event.clientY });
@@ -228,7 +988,7 @@ var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
228
988
  pointerEvents: "none",
229
989
  zIndex: 4
230
990
  };
231
- return /* @__PURE__ */ jsx(
991
+ return /* @__PURE__ */ jsx2(
232
992
  "div",
233
993
  {
234
994
  className: ["viewer-hover-card", className2].filter(Boolean).join(" "),
@@ -244,8 +1004,8 @@ var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
244
1004
  import { createContext, useContext } from "react";
245
1005
 
246
1006
  // src/toolbar-icons.tsx
247
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
248
- var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE__ */ jsx2(
1007
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1008
+ var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE__ */ jsx3(
249
1009
  "svg",
250
1010
  {
251
1011
  className: "viewer-toolbar-icon",
@@ -260,26 +1020,26 @@ var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE
260
1020
  children
261
1021
  }
262
1022
  );
263
- var FitIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M8 8h8v8H8z" }) });
264
- var ResetIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 10a9 9 0 1 1 2 8M3 4v6h6" }) });
265
- var TopIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 7l8-4 8 4-8 4zM4 7v10l8 4 8-4V7M12 11v10" }) });
266
- var StockIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 6l9-4 9 4v12l-9 4-9-4zM3 6l9 4 9-4M12 10v12M7 8v8l5 2 5-2V8" }) });
267
- var AxesIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M5 19V3m0 16h16M5 19l10-10M2 6l3-3 3 3m10 10 3 3-3 3M11 9h4v4" }) });
268
- var GridIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 8l9-5 9 5-9 5zM3 8v8l9 5 9-5V8M12 13v8M7.5 5.5v8M16.5 5.5v8" }) });
269
- var BananaIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { viewBox: "0 0 576 512", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M284.2 245.6c12.99 6.929 25.35 15.14 36.08 25.8L334.5 285.6l65.75-23.47c14.75-5.265 30.18-7.849 45.81-8.389c1.154-10.73 1.764-21.38 1.764-31.87c0-118.5-81.33-221.9-119.7-221.9c-21.01 0-40.91 17.04-40.91 39.25c0 16.18 16.74 41.9 16.74 103C303.1 170.1 300.6 203.1 284.2 245.6zM575.1 389.6c0-3.687-.8637-7.429-2.687-10.93l-15.12-29.11c-21.05-40.53-63.08-64.51-106.9-64.51c-13.43 0-27.02 2.252-40.22 6.969l-84.84 30.27l-28.59-28.41C274.4 270.9 243.7 258.1 212.8 258.1c-23.72 0-47.57 6.97-68.29 21.2L106.3 306.4c-6.732 4.631-10.35 12.07-10.35 19.63c0 14.93 12.7 23.87 24.04 23.87c4.695 0 9.443-1.376 13.61-4.26l38.13-26.23c12.43-8.525 26.71-12.69 40.91-12.69c10.64 0 21.24 2.339 30.97 6.934c-50.62 62.23-128.3 99.85-211.4 99.85C14.42 413.5 0 427.8 0 445.5v31.38c0 17.68 14.66 32.02 32.46 32.02l28.98.0009c14.15 0 34.69 1.098 59.07 1.098c93.51 0 243.4-16.15 304.8-172.4c9.021-3.22 17.95-4.712 26.53-4.712c27.65 0 51.7 15.49 63.7 38.55l15.12 29.11c3.484 6.723 11.74 12.98 21.41 12.98C564.1 413.6 575.1 403.8 575.1 389.6z" }) });
270
- var ToolsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M9 2h6v7H9zM9.5 9v9.5l2.5 3.5 2.5-3.5V9M9.5 12l5 2.5M9.5 15.5l5 2.5" }) });
271
- var DirectionsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M7 3v18m-4-4 4 4 4-4M17 21V3m-4 4 4-4 4 4" }) });
272
- var DfmIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M8.3 3h7.4L21 8.3v7.4L15.7 21H8.3L3 15.7V8.3zM12 7.5v5.5m0 3.5v.01" }) });
273
- var SharpCornersIcon = () => /* @__PURE__ */ jsxs(SvgIcon, { children: [
274
- /* @__PURE__ */ jsx2("path", { d: "M4 3v17h17" }),
275
- /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "15", r: "4" })
1023
+ var FitIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M8 8h8v8H8z" }) });
1024
+ var ResetIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 10a9 9 0 1 1 2 8M3 4v6h6" }) });
1025
+ var TopIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 7l8-4 8 4-8 4zM4 7v10l8 4 8-4V7M12 11v10" }) });
1026
+ var StockIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 6l9-4 9 4v12l-9 4-9-4zM3 6l9 4 9-4M12 10v12M7 8v8l5 2 5-2V8" }) });
1027
+ var AxesIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M5 19V3m0 16h16M5 19l10-10M2 6l3-3 3 3m10 10 3 3-3 3M11 9h4v4" }) });
1028
+ var GridIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 8l9-5 9 5-9 5zM3 8v8l9 5 9-5V8M12 13v8M7.5 5.5v8M16.5 5.5v8" }) });
1029
+ var BananaIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { viewBox: "0 0 576 512", fill: "currentColor", children: /* @__PURE__ */ jsx3("path", { d: "M284.2 245.6c12.99 6.929 25.35 15.14 36.08 25.8L334.5 285.6l65.75-23.47c14.75-5.265 30.18-7.849 45.81-8.389c1.154-10.73 1.764-21.38 1.764-31.87c0-118.5-81.33-221.9-119.7-221.9c-21.01 0-40.91 17.04-40.91 39.25c0 16.18 16.74 41.9 16.74 103C303.1 170.1 300.6 203.1 284.2 245.6zM575.1 389.6c0-3.687-.8637-7.429-2.687-10.93l-15.12-29.11c-21.05-40.53-63.08-64.51-106.9-64.51c-13.43 0-27.02 2.252-40.22 6.969l-84.84 30.27l-28.59-28.41C274.4 270.9 243.7 258.1 212.8 258.1c-23.72 0-47.57 6.97-68.29 21.2L106.3 306.4c-6.732 4.631-10.35 12.07-10.35 19.63c0 14.93 12.7 23.87 24.04 23.87c4.695 0 9.443-1.376 13.61-4.26l38.13-26.23c12.43-8.525 26.71-12.69 40.91-12.69c10.64 0 21.24 2.339 30.97 6.934c-50.62 62.23-128.3 99.85-211.4 99.85C14.42 413.5 0 427.8 0 445.5v31.38c0 17.68 14.66 32.02 32.46 32.02l28.98.0009c14.15 0 34.69 1.098 59.07 1.098c93.51 0 243.4-16.15 304.8-172.4c9.021-3.22 17.95-4.712 26.53-4.712c27.65 0 51.7 15.49 63.7 38.55l15.12 29.11c3.484 6.723 11.74 12.98 21.41 12.98C564.1 413.6 575.1 403.8 575.1 389.6z" }) });
1030
+ var ToolsIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M9 2h6v7H9zM9.5 9v9.5l2.5 3.5 2.5-3.5V9M9.5 12l5 2.5M9.5 15.5l5 2.5" }) });
1031
+ var DirectionsIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M7 3v18m-4-4 4 4 4-4M17 21V3m-4 4 4-4 4 4" }) });
1032
+ var DfmIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M8.3 3h7.4L21 8.3v7.4L15.7 21H8.3L3 15.7V8.3zM12 7.5v5.5m0 3.5v.01" }) });
1033
+ var SharpCornersIcon = () => /* @__PURE__ */ jsxs2(SvgIcon, { children: [
1034
+ /* @__PURE__ */ jsx3("path", { d: "M4 3v17h17" }),
1035
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "15", r: "4" })
276
1036
  ] });
277
- var ThreadsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M8 3h8v18H8zM8 8l8-2M8 12l8-2M8 16l8-2M8 20l8-2" }) });
278
- var HoverIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v12H9l-5 4zm4 5h8m-8 3h5" }) });
279
- var FocusIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v16H4zM8 8h8v8H8zM2 12h4m12 0h4M12 2v4m0 12v4" }) });
280
- var WireframeIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 7l8-4 8 4v10l-8 4-8-4zM4 7l8 4 8-4M12 11v10M4 17l8-4 8 4M12 3v10" }) });
281
- var SectionIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 6h9v12H3zM12 3v18M16 6h2m3 3v2m0 3v2m-5 3h-2" }) });
282
- var MeasureIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 16L16 3l5 5L8 21zM8 11l3 3m1-7 3 3m-11 5 3 3" }) });
1037
+ var ThreadsIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M8 3h8v18H8zM8 8l8-2M8 12l8-2M8 16l8-2M8 20l8-2" }) });
1038
+ var HoverIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 4h16v12H9l-5 4zm4 5h8m-8 3h5" }) });
1039
+ var FocusIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 4h16v16H4zM8 8h8v8H8zM2 12h4m12 0h4M12 2v4m0 12v4" }) });
1040
+ var WireframeIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 7l8-4 8 4v10l-8 4-8-4zM4 7l8 4 8-4M12 11v10M4 17l8-4 8 4M12 3v10" }) });
1041
+ var SectionIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 6h9v12H3zM12 3v18M16 6h2m3 3v2m0 3v2m-5 3h-2" }) });
1042
+ var MeasureIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 16L16 3l5 5L8 21zM8 11l3 3m1-7 3 3m-11 5 3 3" }) });
283
1043
  var ICONS = {
284
1044
  fit: FitIcon,
285
1045
  reset: ResetIcon,
@@ -301,16 +1061,16 @@ var ICONS = {
301
1061
  };
302
1062
  var ToolbarIcon = ({ name }) => {
303
1063
  const Icon = ICONS[name];
304
- return /* @__PURE__ */ jsx2(Icon, {});
1064
+ return /* @__PURE__ */ jsx3(Icon, {});
305
1065
  };
306
1066
 
307
1067
  // src/viewer-toolbar.tsx
308
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1068
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
309
1069
  var ViewerToolbarContext = createContext(null);
310
1070
  var ViewerToolbarProvider = ({
311
1071
  controls,
312
1072
  children
313
- }) => /* @__PURE__ */ jsx3(ViewerToolbarContext.Provider, { value: controls, children });
1073
+ }) => /* @__PURE__ */ jsx4(ViewerToolbarContext.Provider, { value: controls, children });
314
1074
  var useViewerToolbar = () => {
315
1075
  const controls = useContext(ViewerToolbarContext);
316
1076
  if (!controls) throw new Error("useViewerToolbar must be used inside <ViewerToolbarProvider>");
@@ -323,7 +1083,7 @@ var ToolbarButton = ({ action, icon, label, toggle = true }) => {
323
1083
  throw new Error(`<ViewerToolbar.${buttonName(action)}> requires a '${action}' toolbar control`);
324
1084
  }
325
1085
  const pressed = control.pressed ?? false;
326
- return /* @__PURE__ */ jsxs2(
1086
+ return /* @__PURE__ */ jsxs3(
327
1087
  "button",
328
1088
  {
329
1089
  className: "viewer-toolbar-button",
@@ -333,13 +1093,13 @@ var ToolbarButton = ({ action, icon, label, toggle = true }) => {
333
1093
  "aria-pressed": toggle ? pressed : void 0,
334
1094
  onClick: control.onClick,
335
1095
  children: [
336
- /* @__PURE__ */ jsx3(ToolbarIcon, { name: icon }),
337
- /* @__PURE__ */ jsx3("span", { className: "viewer-toolbar-tooltip", "data-viewer-toolbar-tooltip": "true", role: "tooltip", children: label(pressed) })
1096
+ /* @__PURE__ */ jsx4(ToolbarIcon, { name: icon }),
1097
+ /* @__PURE__ */ jsx4("span", { className: "viewer-toolbar-tooltip", "data-viewer-toolbar-tooltip": "true", role: "tooltip", children: label(pressed) })
338
1098
  ]
339
1099
  }
340
1100
  );
341
1101
  };
342
- var StockButton = () => /* @__PURE__ */ jsx3(
1102
+ var StockButton = () => /* @__PURE__ */ jsx4(
343
1103
  ToolbarButton,
344
1104
  {
345
1105
  action: "stock",
@@ -347,7 +1107,7 @@ var StockButton = () => /* @__PURE__ */ jsx3(
347
1107
  label: (pressed) => pressed ? "Hide stock" : "Show stock"
348
1108
  }
349
1109
  );
350
- var AxesButton = () => /* @__PURE__ */ jsx3(
1110
+ var AxesButton = () => /* @__PURE__ */ jsx4(
351
1111
  ToolbarButton,
352
1112
  {
353
1113
  action: "axes",
@@ -355,7 +1115,7 @@ var AxesButton = () => /* @__PURE__ */ jsx3(
355
1115
  label: (pressed) => pressed ? "Hide axis" : "Show axis"
356
1116
  }
357
1117
  );
358
- var GridButton = () => /* @__PURE__ */ jsx3(
1118
+ var GridButton = () => /* @__PURE__ */ jsx4(
359
1119
  ToolbarButton,
360
1120
  {
361
1121
  action: "grid",
@@ -363,7 +1123,7 @@ var GridButton = () => /* @__PURE__ */ jsx3(
363
1123
  label: (pressed) => pressed ? "Hide grid" : "Show grid"
364
1124
  }
365
1125
  );
366
- var BananaButton = () => /* @__PURE__ */ jsx3(
1126
+ var BananaButton = () => /* @__PURE__ */ jsx4(
367
1127
  ToolbarButton,
368
1128
  {
369
1129
  action: "banana",
@@ -371,7 +1131,7 @@ var BananaButton = () => /* @__PURE__ */ jsx3(
371
1131
  label: (pressed) => pressed ? "Banana for scale (on)" : "Banana for scale"
372
1132
  }
373
1133
  );
374
- var ToolsButton = () => /* @__PURE__ */ jsx3(
1134
+ var ToolsButton = () => /* @__PURE__ */ jsx4(
375
1135
  ToolbarButton,
376
1136
  {
377
1137
  action: "tools",
@@ -379,7 +1139,7 @@ var ToolsButton = () => /* @__PURE__ */ jsx3(
379
1139
  label: (pressed) => pressed ? "Hide tools on the part" : "Show tools on the part"
380
1140
  }
381
1141
  );
382
- var DirectionsButton = () => /* @__PURE__ */ jsx3(
1142
+ var DirectionsButton = () => /* @__PURE__ */ jsx4(
383
1143
  ToolbarButton,
384
1144
  {
385
1145
  action: "directions",
@@ -387,7 +1147,7 @@ var DirectionsButton = () => /* @__PURE__ */ jsx3(
387
1147
  label: () => "Highlight faces by direction"
388
1148
  }
389
1149
  );
390
- var DfmButton = () => /* @__PURE__ */ jsx3(
1150
+ var DfmButton = () => /* @__PURE__ */ jsx4(
391
1151
  ToolbarButton,
392
1152
  {
393
1153
  action: "dfm",
@@ -395,7 +1155,7 @@ var DfmButton = () => /* @__PURE__ */ jsx3(
395
1155
  label: (pressed) => pressed ? "Hide DFM flags" : "Show DFM flags"
396
1156
  }
397
1157
  );
398
- var SharpCornersButton = () => /* @__PURE__ */ jsx3(
1158
+ var SharpCornersButton = () => /* @__PURE__ */ jsx4(
399
1159
  ToolbarButton,
400
1160
  {
401
1161
  action: "sharpCorners",
@@ -403,7 +1163,7 @@ var SharpCornersButton = () => /* @__PURE__ */ jsx3(
403
1163
  label: (pressed) => pressed ? "Hide sharp corners" : "Show sharp corners"
404
1164
  }
405
1165
  );
406
- var ThreadsButton = () => /* @__PURE__ */ jsx3(
1166
+ var ThreadsButton = () => /* @__PURE__ */ jsx4(
407
1167
  ToolbarButton,
408
1168
  {
409
1169
  action: "threads",
@@ -411,7 +1171,7 @@ var ThreadsButton = () => /* @__PURE__ */ jsx3(
411
1171
  label: (pressed) => pressed ? "Hide threads" : "Show threads"
412
1172
  }
413
1173
  );
414
- var HoverButton = () => /* @__PURE__ */ jsx3(
1174
+ var HoverButton = () => /* @__PURE__ */ jsx4(
415
1175
  ToolbarButton,
416
1176
  {
417
1177
  action: "hover",
@@ -419,7 +1179,7 @@ var HoverButton = () => /* @__PURE__ */ jsx3(
419
1179
  label: (pressed) => pressed ? "Disable feature hover" : "Enable feature hover"
420
1180
  }
421
1181
  );
422
- var FocusButton = () => /* @__PURE__ */ jsx3(
1182
+ var FocusButton = () => /* @__PURE__ */ jsx4(
423
1183
  ToolbarButton,
424
1184
  {
425
1185
  action: "focus",
@@ -427,8 +1187,8 @@ var FocusButton = () => /* @__PURE__ */ jsx3(
427
1187
  label: (pressed) => pressed ? "Show full part" : "Focus selection"
428
1188
  }
429
1189
  );
430
- var WireframeButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "wireframe", icon: "wireframe", label: () => "Wireframe" });
431
- var SectionButton = () => /* @__PURE__ */ jsx3(
1190
+ var WireframeButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "wireframe", icon: "wireframe", label: () => "Wireframe" });
1191
+ var SectionButton = () => /* @__PURE__ */ jsx4(
432
1192
  ToolbarButton,
433
1193
  {
434
1194
  action: "section",
@@ -436,7 +1196,7 @@ var SectionButton = () => /* @__PURE__ */ jsx3(
436
1196
  label: (pressed) => pressed ? "Exit section" : "Section"
437
1197
  }
438
1198
  );
439
- var MeasureButton = () => /* @__PURE__ */ jsx3(
1199
+ var MeasureButton = () => /* @__PURE__ */ jsx4(
440
1200
  ToolbarButton,
441
1201
  {
442
1202
  action: "measure",
@@ -444,11 +1204,11 @@ var MeasureButton = () => /* @__PURE__ */ jsx3(
444
1204
  label: (pressed) => pressed ? "Exit measure" : "Measure"
445
1205
  }
446
1206
  );
447
- var FitButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "fit", icon: "fit", label: () => "Fit", toggle: false });
448
- var ResetButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "reset", icon: "reset", label: () => "Reset", toggle: false });
449
- var TopButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "top", icon: "top", label: () => "Top view", toggle: false });
450
- var Divider = () => /* @__PURE__ */ jsx3("span", { className: "viewer-toolbar-divider", "data-viewer-toolbar-divider": "true" });
451
- var Controls = ({ children, className: className2 }) => /* @__PURE__ */ jsx3(
1207
+ var FitButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "fit", icon: "fit", label: () => "Fit", toggle: false });
1208
+ var ResetButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "reset", icon: "reset", label: () => "Reset", toggle: false });
1209
+ var TopButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "top", icon: "top", label: () => "Top view", toggle: false });
1210
+ var Divider = () => /* @__PURE__ */ jsx4("span", { className: "viewer-toolbar-divider", "data-viewer-toolbar-divider": "true" });
1211
+ var Controls = ({ children, className: className2 }) => /* @__PURE__ */ jsx4(
452
1212
  "div",
453
1213
  {
454
1214
  className: ["viewer-toolbar", className2].filter(Boolean).join(" "),
@@ -461,44 +1221,44 @@ var Controls = ({ children, className: className2 }) => /* @__PURE__ */ jsx3(
461
1221
  var DefaultControls = () => {
462
1222
  const controls = useViewerToolbar();
463
1223
  const display = [
464
- controls.stock ? /* @__PURE__ */ jsx3(StockButton, {}, "stock") : null,
465
- controls.axes ? /* @__PURE__ */ jsx3(AxesButton, {}, "axes") : null,
466
- controls.grid ? /* @__PURE__ */ jsx3(GridButton, {}, "grid") : null,
467
- controls.banana ? /* @__PURE__ */ jsx3(BananaButton, {}, "banana") : null,
468
- controls.tools ? /* @__PURE__ */ jsx3(ToolsButton, {}, "tools") : null
1224
+ controls.stock ? /* @__PURE__ */ jsx4(StockButton, {}, "stock") : null,
1225
+ controls.axes ? /* @__PURE__ */ jsx4(AxesButton, {}, "axes") : null,
1226
+ controls.grid ? /* @__PURE__ */ jsx4(GridButton, {}, "grid") : null,
1227
+ controls.banana ? /* @__PURE__ */ jsx4(BananaButton, {}, "banana") : null,
1228
+ controls.tools ? /* @__PURE__ */ jsx4(ToolsButton, {}, "tools") : null
469
1229
  ].filter(Boolean);
470
1230
  const analysis = [
471
- controls.directions ? /* @__PURE__ */ jsx3(DirectionsButton, {}, "directions") : null,
472
- controls.dfm ? /* @__PURE__ */ jsx3(DfmButton, {}, "dfm") : null,
473
- controls.sharpCorners ? /* @__PURE__ */ jsx3(SharpCornersButton, {}, "sharpCorners") : null,
474
- controls.threads ? /* @__PURE__ */ jsx3(ThreadsButton, {}, "threads") : null,
475
- controls.hover ? /* @__PURE__ */ jsx3(HoverButton, {}, "hover") : null,
476
- controls.focus ? /* @__PURE__ */ jsx3(FocusButton, {}, "focus") : null,
477
- controls.wireframe ? /* @__PURE__ */ jsx3(WireframeButton, {}, "wireframe") : null,
478
- controls.section ? /* @__PURE__ */ jsx3(SectionButton, {}, "section") : null,
479
- controls.measure ? /* @__PURE__ */ jsx3(MeasureButton, {}, "measure") : null
1231
+ controls.directions ? /* @__PURE__ */ jsx4(DirectionsButton, {}, "directions") : null,
1232
+ controls.dfm ? /* @__PURE__ */ jsx4(DfmButton, {}, "dfm") : null,
1233
+ controls.sharpCorners ? /* @__PURE__ */ jsx4(SharpCornersButton, {}, "sharpCorners") : null,
1234
+ controls.threads ? /* @__PURE__ */ jsx4(ThreadsButton, {}, "threads") : null,
1235
+ controls.hover ? /* @__PURE__ */ jsx4(HoverButton, {}, "hover") : null,
1236
+ controls.focus ? /* @__PURE__ */ jsx4(FocusButton, {}, "focus") : null,
1237
+ controls.wireframe ? /* @__PURE__ */ jsx4(WireframeButton, {}, "wireframe") : null,
1238
+ controls.section ? /* @__PURE__ */ jsx4(SectionButton, {}, "section") : null,
1239
+ controls.measure ? /* @__PURE__ */ jsx4(MeasureButton, {}, "measure") : null
480
1240
  ].filter(Boolean);
481
1241
  const camera = [
482
- controls.fit ? /* @__PURE__ */ jsx3(FitButton, {}, "fit") : null,
483
- controls.reset ? /* @__PURE__ */ jsx3(ResetButton, {}, "reset") : null,
484
- controls.top ? /* @__PURE__ */ jsx3(TopButton, {}, "top") : null
1242
+ controls.fit ? /* @__PURE__ */ jsx4(FitButton, {}, "fit") : null,
1243
+ controls.reset ? /* @__PURE__ */ jsx4(ResetButton, {}, "reset") : null,
1244
+ controls.top ? /* @__PURE__ */ jsx4(TopButton, {}, "top") : null
485
1245
  ].filter(Boolean);
486
- return /* @__PURE__ */ jsxs2(Controls, { children: [
1246
+ return /* @__PURE__ */ jsxs3(Controls, { children: [
487
1247
  display,
488
- display.length && analysis.length ? /* @__PURE__ */ jsx3(Divider, {}) : null,
1248
+ display.length && analysis.length ? /* @__PURE__ */ jsx4(Divider, {}) : null,
489
1249
  analysis,
490
- (display.length || analysis.length) && camera.length ? /* @__PURE__ */ jsx3(Divider, {}) : null,
1250
+ (display.length || analysis.length) && camera.length ? /* @__PURE__ */ jsx4(Divider, {}) : null,
491
1251
  camera
492
1252
  ] });
493
1253
  };
494
1254
  var ViewerToolbarRoot = ({ className: className2, children }) => {
495
1255
  useViewerToolbar();
496
- return /* @__PURE__ */ jsx3(
1256
+ return /* @__PURE__ */ jsx4(
497
1257
  "div",
498
1258
  {
499
1259
  className: ["viewer-toolbar-stack", className2].filter(Boolean).join(" "),
500
1260
  "data-viewer-toolbar": "true",
501
- children: children ?? /* @__PURE__ */ jsx3(DefaultControls, {})
1261
+ children: children ?? /* @__PURE__ */ jsx4(DefaultControls, {})
502
1262
  }
503
1263
  );
504
1264
  };
@@ -525,11 +1285,11 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
525
1285
  });
526
1286
 
527
1287
  // src/stock.tsx
528
- import { useThree } from "@react-three/fiber";
529
- import { useEffect as useEffect2, useLayoutEffect, useMemo, useState as useState2 } from "react";
1288
+ import { useThree as useThree2 } from "@react-three/fiber";
1289
+ import { useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useState as useState2 } from "react";
530
1290
 
531
1291
  // src/render/stock-mesh.ts
532
- import { Mesh } from "three";
1292
+ import { Mesh as Mesh2 } from "three";
533
1293
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
534
1294
  async function parseStockGlb(data) {
535
1295
  const { scene } = await new GLTFLoader().parseAsync(data, "");
@@ -537,7 +1297,7 @@ async function parseStockGlb(data) {
537
1297
  return stockMeshGeometries(scene);
538
1298
  } finally {
539
1299
  scene.traverse((object) => {
540
- if (!(object instanceof Mesh)) return;
1300
+ if (!(object instanceof Mesh2)) return;
541
1301
  object.geometry.dispose();
542
1302
  for (const material of Array.isArray(object.material) ? object.material : [object.material]) {
543
1303
  material.dispose();
@@ -549,7 +1309,7 @@ function stockMeshGeometries(scene) {
549
1309
  const geometries = [];
550
1310
  scene.updateWorldMatrix(true, true);
551
1311
  scene.traverse((object) => {
552
- if (!(object instanceof Mesh)) return;
1312
+ if (!(object instanceof Mesh2)) return;
553
1313
  const geometry = object.geometry.clone().applyMatrix4(object.matrixWorld);
554
1314
  if (!geometry.hasAttribute("normal")) geometry.computeVertexNormals();
555
1315
  geometries.push(geometry);
@@ -604,30 +1364,30 @@ function createStockMeshBuffer() {
604
1364
 
605
1365
  // src/render/stock.ts
606
1366
  import {
607
- Box3,
1367
+ Box3 as Box32,
608
1368
  BoxGeometry,
609
- BufferGeometry,
1369
+ BufferGeometry as BufferGeometry3,
610
1370
  CylinderGeometry,
611
- Float32BufferAttribute,
612
- Group,
613
- LineBasicMaterial,
614
- LineSegments,
615
- Matrix4,
616
- Mesh as Mesh2,
617
- MeshLambertMaterial,
1371
+ Float32BufferAttribute as Float32BufferAttribute3,
1372
+ Group as Group2,
1373
+ LineBasicMaterial as LineBasicMaterial2,
1374
+ LineSegments as LineSegments2,
1375
+ Matrix4 as Matrix42,
1376
+ Mesh as Mesh3,
1377
+ MeshLambertMaterial as MeshLambertMaterial2,
618
1378
  Quaternion,
619
- Vector3 as Vector32
1379
+ Vector3 as Vector33
620
1380
  } from "three";
621
1381
 
622
1382
  // src/render/stock-edges.ts
623
- import { MathUtils, Triangle, Vector3 } from "three";
1383
+ import { MathUtils, Triangle, Vector3 as Vector32 } from "three";
624
1384
  var STOCK_EDGE_ANGLE = 15;
625
1385
  var SYNC_EDGE_TRIANGLES = 2e4;
626
1386
  var SLICE_MS = 4;
627
1387
  var CHECK_EVERY = 256;
628
- var _v0 = new Vector3();
629
- var _v1 = new Vector3();
630
- var _normal = new Vector3();
1388
+ var _v0 = new Vector32();
1389
+ var _v1 = new Vector32();
1390
+ var _normal = new Vector32();
631
1391
  var _triangle = new Triangle();
632
1392
  function* edgeWork(geometry, thresholdAngle) {
633
1393
  const precision = Math.pow(10, 4);
@@ -751,7 +1511,7 @@ function boxFigure(bounds) {
751
1511
  };
752
1512
  }
753
1513
  var CYLINDER_SEGMENTS = 64;
754
- var CYLINDER_AXIS = new Vector32(0, 1, 0);
1514
+ var CYLINDER_AXIS = new Vector33(0, 1, 0);
755
1515
  function fixedBoxStockBounds(geometry, dimensions, position = "model_centered", positionOffset = 0, offset = { x: 0, y: 0, z: 0 }) {
756
1516
  if (![dimensions.x, dimensions.y, dimensions.z].every(Number.isFinite) || [dimensions.x, dimensions.y, dimensions.z].some((value) => value <= 0)) {
757
1517
  throw new RangeError("Fixed stock dimensions must be finite and positive.");
@@ -763,12 +1523,12 @@ function fixedBoxStockBounds(geometry, dimensions, position = "model_centered",
763
1523
  throw new RangeError("Fixed stock offset must be finite.");
764
1524
  }
765
1525
  const part = partBounds(geometry);
766
- const center = part.getCenter(new Vector32());
1526
+ const center = part.getCenter(new Vector33());
767
1527
  const z = fixedStockLowerZ(part, dimensions.z, position, positionOffset);
768
- return new Box3(
769
- new Vector32(center.x - dimensions.x / 2, center.y - dimensions.y / 2, z),
770
- new Vector32(center.x + dimensions.x / 2, center.y + dimensions.y / 2, z + dimensions.z)
771
- ).translate(new Vector32(offset.x, offset.y, offset.z));
1528
+ return new Box32(
1529
+ new Vector33(center.x - dimensions.x / 2, center.y - dimensions.y / 2, z),
1530
+ new Vector33(center.x + dimensions.x / 2, center.y + dimensions.y / 2, z + dimensions.z)
1531
+ ).translate(new Vector33(offset.x, offset.y, offset.z));
772
1532
  }
773
1533
  function fixedCylinderStock(geometry, size, position = "model_centered", positionOffset = 0) {
774
1534
  if (![size.diameter, size.length].every((value) => Number.isFinite(value) && value > 0)) {
@@ -778,7 +1538,7 @@ function fixedCylinderStock(geometry, size, position = "model_centered", positio
778
1538
  throw new RangeError("Fixed stock position offset must be finite.");
779
1539
  }
780
1540
  const part = partBounds(geometry);
781
- const center = part.getCenter(new Vector32());
1541
+ const center = part.getCenter(new Vector33());
782
1542
  return {
783
1543
  origin: {
784
1544
  x: center.x,
@@ -804,13 +1564,13 @@ function cylinderStockGeometry(input) {
804
1564
  if (![diameter, length].every((value) => Number.isFinite(value) && value > 0)) {
805
1565
  throw new RangeError("Cylinder stock diameter and length must be finite and positive.");
806
1566
  }
807
- const axis = new Vector32(figure.axis.x, figure.axis.y, figure.axis.z);
1567
+ const axis = new Vector33(figure.axis.x, figure.axis.y, figure.axis.z);
808
1568
  const norm = axis.length();
809
1569
  if (!Number.isFinite(norm) || norm === 0) {
810
1570
  throw new RangeError("Cylinder stock axis must be finite and non-zero.");
811
1571
  }
812
1572
  axis.divideScalar(norm);
813
- const center = axis.clone().multiplyScalar(length / 2).add(new Vector32(origin.x, origin.y, origin.z));
1573
+ const center = axis.clone().multiplyScalar(length / 2).add(new Vector33(origin.x, origin.y, origin.z));
814
1574
  return new CylinderGeometry(diameter / 2, diameter / 2, length, CYLINDER_SEGMENTS).applyQuaternion(new Quaternion().setFromUnitVectors(CYLINDER_AXIS, axis)).translate(center.x, center.y, center.z);
815
1575
  }
816
1576
  function boxStockGeometry(input) {
@@ -838,12 +1598,12 @@ function orientedBoxStockGeometry(figure) {
838
1598
  )) {
839
1599
  throw new RangeError("Box stock frame and corners must be finite.");
840
1600
  }
841
- const size = new Vector32(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
1601
+ const size = new Vector33(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
842
1602
  if (size.toArray().some((value) => !(value > 0))) {
843
1603
  throw new RangeError("Box stock upper corner must be above its lower corner on every axis.");
844
1604
  }
845
- const z = new Vector32(axis.x, axis.y, axis.z).normalize();
846
- const x = new Vector32(refDirection.x, refDirection.y, refDirection.z);
1605
+ const z = new Vector33(axis.x, axis.y, axis.z).normalize();
1606
+ const x = new Vector33(refDirection.x, refDirection.y, refDirection.z);
847
1607
  const reference = x.length();
848
1608
  x.addScaledVector(z, -x.dot(z));
849
1609
  if (!(z.lengthSq() > 0 && x.length() > 1e-9 * reference)) {
@@ -852,11 +1612,11 @@ function orientedBoxStockGeometry(figure) {
852
1612
  );
853
1613
  }
854
1614
  x.normalize();
855
- const y = new Vector32().crossVectors(z, x);
856
- return new BoxGeometry(size.x, size.y, size.z).translate((lower.x + upper.x) / 2, (lower.y + upper.y) / 2, (lower.z + upper.z) / 2).applyMatrix4(new Matrix4().makeBasis(x, y, z).setPosition(location.x, location.y, location.z));
1615
+ const y = new Vector33().crossVectors(z, x);
1616
+ return new BoxGeometry(size.x, size.y, size.z).translate((lower.x + upper.x) / 2, (lower.y + upper.y) / 2, (lower.z + upper.z) / 2).applyMatrix4(new Matrix42().makeBasis(x, y, z).setPosition(location.x, location.y, location.z));
857
1617
  }
858
1618
  function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 }) {
859
- const padding = typeof allowance === "number" ? new Vector32(allowance, allowance, allowance) : "wall" in allowance ? new Vector32(allowance.wall, allowance.wall, allowance.floor) : new Vector32(allowance.x, allowance.y, allowance.z);
1619
+ const padding = typeof allowance === "number" ? new Vector33(allowance, allowance, allowance) : "wall" in allowance ? new Vector33(allowance.wall, allowance.wall, allowance.floor) : new Vector33(allowance.x, allowance.y, allowance.z);
860
1620
  if (padding.toArray().some((value) => !Number.isFinite(value) || value < 0)) {
861
1621
  throw new RangeError("Stock allowance must be finite and non-negative.");
862
1622
  }
@@ -864,8 +1624,8 @@ function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 })
864
1624
  throw new RangeError("Stock offset must be finite.");
865
1625
  }
866
1626
  const box = partBounds(geometry);
867
- box.expandByVector(padding).translate(new Vector32(offset.x, offset.y, offset.z));
868
- const size = box.getSize(new Vector32());
1627
+ box.expandByVector(padding).translate(new Vector33(offset.x, offset.y, offset.z));
1628
+ const size = box.getSize(new Vector33());
869
1629
  if (size.toArray().some((value) => !Number.isFinite(value) || value <= 0)) {
870
1630
  throw new RangeError("Stock dimensions must be finite and positive.");
871
1631
  }
@@ -875,8 +1635,8 @@ function partBounds(geometry) {
875
1635
  const position = geometry.getAttribute("position");
876
1636
  if (!position || position.count === 0)
877
1637
  throw new RangeError("Stock needs non-empty part geometry.");
878
- const box = new Box3();
879
- const point = new Vector32();
1638
+ const box = new Box32();
1639
+ const point = new Vector33();
880
1640
  for (let index = 0; index < position.count; index += 1) {
881
1641
  point.fromBufferAttribute(position, index);
882
1642
  if (!Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
@@ -887,10 +1647,10 @@ function partBounds(geometry) {
887
1647
  return box;
888
1648
  }
889
1649
  function createStock(geometry, legacyWorkpiece = false, onEdges) {
890
- const material = new MeshLambertMaterial({ transparent: true, depthWrite: false });
891
- const edgeMaterial = new LineBasicMaterial({ transparent: true, depthWrite: false });
892
- const mesh = new Mesh2(geometry, material);
893
- const object = new Group();
1650
+ const material = new MeshLambertMaterial2({ transparent: true, depthWrite: false });
1651
+ const edgeMaterial = new LineBasicMaterial2({ transparent: true, depthWrite: false });
1652
+ const mesh = new Mesh3(geometry, material);
1653
+ const object = new Group2();
894
1654
  object.userData[STOCK_OBJECT] = true;
895
1655
  mesh.renderOrder = legacyWorkpiece ? 2 : 5;
896
1656
  mesh.raycast = () => {
@@ -907,9 +1667,9 @@ function createStock(geometry, legacyWorkpiece = false, onEdges) {
907
1667
  let building = null;
908
1668
  let shown = false;
909
1669
  const attach = (positions) => {
910
- const outline = new BufferGeometry();
911
- outline.setAttribute("position", new Float32BufferAttribute(positions, 3));
912
- edges = new LineSegments(outline, edgeMaterial);
1670
+ const outline = new BufferGeometry3();
1671
+ outline.setAttribute("position", new Float32BufferAttribute3(positions, 3));
1672
+ edges = new LineSegments2(outline, edgeMaterial);
913
1673
  edges.renderOrder = legacyWorkpiece ? 1 : 6;
914
1674
  edges.raycast = () => {
915
1675
  };
@@ -953,8 +1713,8 @@ function createStock(geometry, legacyWorkpiece = false, onEdges) {
953
1713
  }
954
1714
 
955
1715
  // src/stock.tsx
956
- import { jsx as jsx4 } from "react/jsx-runtime";
957
- var Stock = ({ geometry, glb, box, cylinder, ...appearance }) => geometry ? /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance }) : glb ? /* @__PURE__ */ jsx4(GlbStock, { glb, ...appearance }) : /* @__PURE__ */ jsx4(ShapeStock, { box, cylinder, ...appearance });
1716
+ import { jsx as jsx5 } from "react/jsx-runtime";
1717
+ var Stock = ({ geometry, glb, box, cylinder, ...appearance }) => geometry ? /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance }) : glb ? /* @__PURE__ */ jsx5(GlbStock, { glb, ...appearance }) : /* @__PURE__ */ jsx5(ShapeStock, { box, cylinder, ...appearance });
958
1718
  var GeometryStock = ({
959
1719
  geometry,
960
1720
  renderStyle = "overlay",
@@ -966,13 +1726,13 @@ var GeometryStock = ({
966
1726
  edgeOpacity = renderStyle === "legacy-workpiece" ? 0.5 : 0.75,
967
1727
  showEdges = true
968
1728
  }) => {
969
- const invalidate = useThree((state) => state.invalidate);
970
- const stock = useMemo(
1729
+ const invalidate = useThree2((state) => state.invalidate);
1730
+ const stock = useMemo2(
971
1731
  () => createStock(geometry, renderStyle === "legacy-workpiece", invalidate),
972
1732
  [geometry, renderStyle, invalidate]
973
1733
  );
974
- useEffect2(() => () => stock.dispose(), [stock]);
975
- useLayoutEffect(() => {
1734
+ useEffect3(() => () => stock.dispose(), [stock]);
1735
+ useLayoutEffect2(() => {
976
1736
  stock.material.color.setHex(color);
977
1737
  stock.material.emissive.setHex(emissive);
978
1738
  if (stock.material.flatShading !== flatShading) {
@@ -1000,20 +1760,20 @@ var GeometryStock = ({
1000
1760
  showEdges,
1001
1761
  stock
1002
1762
  ]);
1003
- return /* @__PURE__ */ jsx4("primitive", { object: stock.object, dispose: null });
1763
+ return /* @__PURE__ */ jsx5("primitive", { object: stock.object, dispose: null });
1004
1764
  };
1005
1765
  var GlbStock = ({
1006
1766
  glb,
1007
1767
  retainPrevious = false,
1008
1768
  ...appearance
1009
1769
  }) => {
1010
- const buffer = useMemo(() => createStockMeshBuffer(), []);
1770
+ const buffer = useMemo2(() => createStockMeshBuffer(), []);
1011
1771
  const [decoded, setDecoded] = useState2(null);
1012
- useEffect2(() => () => buffer.dispose(), [buffer]);
1013
- useLayoutEffect(() => {
1772
+ useEffect3(() => () => buffer.dispose(), [buffer]);
1773
+ useLayoutEffect2(() => {
1014
1774
  buffer.commit(decoded?.geometries ?? []);
1015
1775
  }, [buffer, decoded]);
1016
- useEffect2(
1776
+ useEffect3(
1017
1777
  () => buffer.request(
1018
1778
  () => parseStockGlb(glb),
1019
1779
  (geometries) => {
@@ -1030,7 +1790,7 @@ var GlbStock = ({
1030
1790
  );
1031
1791
  if (!decoded || !retainPrevious && decoded.source !== glb) return null;
1032
1792
  if (decoded.error) throw decoded.error;
1033
- return decoded.geometries?.map((geometry) => /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance }, geometry.uuid));
1793
+ return decoded.geometries?.map((geometry) => /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance }, geometry.uuid));
1034
1794
  };
1035
1795
  var keepNonFinite = (_key, value) => typeof value === "number" && !Number.isFinite(value) ? String(value) : value;
1036
1796
  var restoreNonFinite = (_key, value) => value === "NaN" || value === "Infinity" || value === "-Infinity" ? Number(value) : value;
@@ -1061,28 +1821,28 @@ var ShapeStock = ({
1061
1821
  },
1062
1822
  keepNonFinite
1063
1823
  );
1064
- const geometry = useMemo(() => {
1824
+ const geometry = useMemo2(() => {
1065
1825
  const options = JSON.parse(figure, restoreNonFinite);
1066
1826
  const input = partGeometry ? { ...options, partGeometry } : options;
1067
1827
  return isBox ? boxStockGeometry(input) : cylinderStockGeometry(input);
1068
1828
  }, [figure, isBox, partGeometry]);
1069
- useEffect2(() => () => geometry.dispose(), [geometry]);
1070
- return /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance });
1829
+ useEffect3(() => () => geometry.dispose(), [geometry]);
1830
+ return /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance });
1071
1831
  };
1072
1832
 
1073
1833
  // src/banana.tsx
1074
1834
  import { useGLTF } from "@react-three/drei";
1075
- import { useThree as useThree2 } from "@react-three/fiber";
1076
- import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useRef } from "react";
1835
+ import { useThree as useThree3 } from "@react-three/fiber";
1836
+ import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
1077
1837
 
1078
1838
  // src/render/banana.ts
1079
- import { Vector3 as Vector33 } from "three";
1839
+ import { Vector3 as Vector34 } from "three";
1080
1840
  var BANANA_GAP = 0.1;
1081
1841
  function bananaPosition(part, banana) {
1082
- const size = part.getSize(new Vector33());
1842
+ const size = part.getSize(new Vector34());
1083
1843
  const reach = Math.max(size.x, size.y, size.z);
1084
- const centre = part.getCenter(new Vector33());
1085
- return new Vector33(
1844
+ const centre = part.getCenter(new Vector34());
1845
+ return new Vector34(
1086
1846
  part.max.x + reach * BANANA_GAP - banana.min.x,
1087
1847
  centre.y - (banana.min.y + banana.max.y) / 2,
1088
1848
  part.min.z - banana.min.z
@@ -1093,16 +1853,16 @@ function bananaFrameBounds(part, banana, position) {
1093
1853
  }
1094
1854
 
1095
1855
  // src/banana.tsx
1096
- import { jsx as jsx5 } from "react/jsx-runtime";
1856
+ import { jsx as jsx6 } from "react/jsx-runtime";
1097
1857
  var BANANA_MODEL_URL = new URL("./assets/banana.glb", import.meta.url).href;
1098
1858
  var SKIN = "#e8b530";
1099
1859
  var FURNITURE = { [EXCLUDE_FROM_FRAME]: true };
1100
1860
  var Banana = ({ onPlaced }) => {
1101
1861
  const part = useContentBox();
1102
- const invalidate = useThree2((state) => state.invalidate);
1862
+ const invalidate = useThree3((state) => state.invalidate);
1103
1863
  const gltf = useGLTF(BANANA_MODEL_URL);
1104
- const group = useRef(null);
1105
- const geometry = useMemo2(() => {
1864
+ const group = useRef2(null);
1865
+ const geometry = useMemo3(() => {
1106
1866
  let found = null;
1107
1867
  gltf.scene.traverse((object) => {
1108
1868
  const mesh = object;
@@ -1117,7 +1877,7 @@ var Banana = ({ onPlaced }) => {
1117
1877
  own.computeBoundingBox();
1118
1878
  return own;
1119
1879
  }, [gltf]);
1120
- useLayoutEffect2(() => {
1880
+ useLayoutEffect3(() => {
1121
1881
  const node = group.current;
1122
1882
  const own = geometry?.boundingBox;
1123
1883
  if (!node || !own || part.isEmpty()) return;
@@ -1129,7 +1889,7 @@ var Banana = ({ onPlaced }) => {
1129
1889
  invalidate();
1130
1890
  }, [geometry, invalidate, onPlaced, part]);
1131
1891
  if (!geometry) return null;
1132
- return /* @__PURE__ */ jsx5("group", { ref: group, userData: FURNITURE, visible: false, children: /* @__PURE__ */ jsx5("mesh", { geometry, children: /* @__PURE__ */ jsx5("meshStandardMaterial", { color: SKIN, roughness: 0.85, metalness: 0 }) }) });
1892
+ return /* @__PURE__ */ jsx6("group", { ref: group, userData: FURNITURE, visible: false, children: /* @__PURE__ */ jsx6("mesh", { geometry, children: /* @__PURE__ */ jsx6("meshStandardMaterial", { color: SKIN, roughness: 0.85, metalness: 0 }) }) });
1133
1893
  };
1134
1894
 
1135
1895
  // src/render/direction-highlights.ts
@@ -1148,17 +1908,17 @@ function directionHighlights(model, activeDirection = null) {
1148
1908
 
1149
1909
  // src/primitives.tsx
1150
1910
  import { GizmoHelper } from "@react-three/drei";
1151
- import { useThree as useThree3 } from "@react-three/fiber";
1152
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
1911
+ import { useThree as useThree4 } from "@react-three/fiber";
1912
+ import { useEffect as useEffect4, useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
1153
1913
 
1154
1914
  // src/render/grid.ts
1155
- import { BufferGeometry as BufferGeometry2, Float32BufferAttribute as Float32BufferAttribute2, Vector3 as Vector34 } from "three";
1915
+ import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute4, Vector3 as Vector35 } from "three";
1156
1916
  var STEPS = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1e3];
1157
1917
  var TARGET_CELLS = 10;
1158
1918
  var OVERHANG = 1.6;
1159
1919
  function gridSpec(box) {
1160
- const size = box.getSize(new Vector34());
1161
- const center = box.getCenter(new Vector34());
1920
+ const size = box.getSize(new Vector35());
1921
+ const center = box.getCenter(new Vector35());
1162
1922
  const largest = Math.max(size.x, size.y, 1e-6);
1163
1923
  const ideal = largest / TARGET_CELLS;
1164
1924
  const step = [...STEPS].reverse().find((candidate) => candidate <= ideal) ?? STEPS[0];
@@ -1184,8 +1944,8 @@ function gridGeometry(spec) {
1184
1944
  z
1185
1945
  );
1186
1946
  }
1187
- const geometry = new BufferGeometry2();
1188
- geometry.setAttribute("position", new Float32BufferAttribute2(positions, 3));
1947
+ const geometry = new BufferGeometry4();
1948
+ geometry.setAttribute("position", new Float32BufferAttribute4(positions, 3));
1189
1949
  return geometry;
1190
1950
  }
1191
1951
  function gridFor(box, options = {}) {
@@ -1202,26 +1962,26 @@ function gridFor(box, options = {}) {
1202
1962
  }
1203
1963
 
1204
1964
  // src/primitives.tsx
1205
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1965
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1206
1966
  var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
1207
1967
  var Grid = ({ step, extent, color, opacity = 0.35 }) => {
1208
1968
  const theme = resolveTheme();
1209
1969
  const box = useContentBox();
1210
- const geometry = useMemo3(() => gridFor(box, { step, extent }), [box, extent, step]);
1211
- useEffect3(() => () => geometry?.dispose(), [geometry]);
1970
+ const geometry = useMemo4(() => gridFor(box, { step, extent }), [box, extent, step]);
1971
+ useEffect4(() => () => geometry?.dispose(), [geometry]);
1212
1972
  if (!geometry) return null;
1213
- return /* @__PURE__ */ jsx6("lineSegments", { geometry, renderOrder: -1, raycast: () => null, userData: FURNITURE2, children: /* @__PURE__ */ jsx6("lineBasicMaterial", { color: color ?? theme.cubeEdge, transparent: true, opacity }) });
1973
+ return /* @__PURE__ */ jsx7("lineSegments", { geometry, renderOrder: -1, raycast: () => null, userData: FURNITURE2, children: /* @__PURE__ */ jsx7("lineBasicMaterial", { color: color ?? theme.cubeEdge, transparent: true, opacity }) });
1214
1974
  };
1215
- var Axes = ({ size = 25 }) => /* @__PURE__ */ jsx6("axesHelper", { args: [size], userData: FURNITURE2, raycast: () => null });
1975
+ var Axes = ({ size = 25 }) => /* @__PURE__ */ jsx7("axesHelper", { args: [size], userData: FURNITURE2, raycast: () => null });
1216
1976
  var ViewCube = ({
1217
1977
  alignment = "top-right",
1218
1978
  margin = [80, 80],
1219
1979
  theme,
1220
1980
  onViewChange
1221
1981
  }) => {
1222
- const resolved = useMemo3(() => resolveTheme(theme), [theme]);
1223
- const zones = useMemo3(() => cubeZones(), []);
1224
- return /* @__PURE__ */ jsx6(GizmoHelper, { alignment, margin, children: /* @__PURE__ */ jsx6(CubePanels, { zones, theme: resolved, onViewChange }) });
1982
+ const resolved = useMemo4(() => resolveTheme(theme), [theme]);
1983
+ const zones = useMemo4(() => cubeZones(), []);
1984
+ return /* @__PURE__ */ jsx7(GizmoHelper, { alignment, margin, children: /* @__PURE__ */ jsx7(CubePanels, { zones, theme: resolved, onViewChange }) });
1225
1985
  };
1226
1986
  var CubePanels = ({
1227
1987
  zones,
@@ -1229,15 +1989,15 @@ var CubePanels = ({
1229
1989
  onViewChange
1230
1990
  }) => {
1231
1991
  const controls = useViewerControls();
1232
- const invalidate = useThree3((state) => state.invalidate);
1233
- const groupRef = useRef2(null);
1992
+ const invalidate = useThree4((state) => state.invalidate);
1993
+ const groupRef = useRef3(null);
1234
1994
  const [hovered, setHovered] = useState3(null);
1235
- const panels = useMemo3(
1995
+ const panels = useMemo4(
1236
1996
  () => zones.map((zone) => ({ zone, geometry: panelGeometry(zone) })),
1237
1997
  [zones]
1238
1998
  );
1239
- const outline = useMemo3(() => cubeOutlineGeometry(zones), [zones]);
1240
- const labels = useMemo3(
1999
+ const outline = useMemo4(() => cubeOutlineGeometry(zones), [zones]);
2000
+ const labels = useMemo4(
1241
2001
  () => zones.filter((zone) => zone.kind === "face").map((zone) => ({
1242
2002
  zone,
1243
2003
  geometry: labelGeometry(zone),
@@ -1245,7 +2005,7 @@ var CubePanels = ({
1245
2005
  })),
1246
2006
  [theme.cubeLabel, zones]
1247
2007
  );
1248
- useEffect3(
2008
+ useEffect4(
1249
2009
  () => () => {
1250
2010
  for (const panel of panels) panel.geometry.dispose();
1251
2011
  for (const label of labels) {
@@ -1264,8 +2024,8 @@ var CubePanels = ({
1264
2024
  setHovered(name);
1265
2025
  invalidate();
1266
2026
  };
1267
- return /* @__PURE__ */ jsxs3("group", { ref: groupRef, scale: 38, children: [
1268
- panels.map(({ zone, geometry }) => /* @__PURE__ */ jsx6(
2027
+ return /* @__PURE__ */ jsxs4("group", { ref: groupRef, scale: 38, children: [
2028
+ panels.map(({ zone, geometry }) => /* @__PURE__ */ jsx7(
1269
2029
  "mesh",
1270
2030
  {
1271
2031
  geometry,
@@ -1278,7 +2038,7 @@ var CubePanels = ({
1278
2038
  event.stopPropagation();
1279
2039
  choose(zone);
1280
2040
  },
1281
- children: /* @__PURE__ */ jsx6(
2041
+ children: /* @__PURE__ */ jsx7(
1282
2042
  "meshLambertMaterial",
1283
2043
  {
1284
2044
  color: hovered === zone.name ? theme.hover : theme.cube,
@@ -1290,27 +2050,27 @@ var CubePanels = ({
1290
2050
  },
1291
2051
  zone.name
1292
2052
  )),
1293
- labels.map(({ zone, geometry, texture }) => /* @__PURE__ */ jsx6("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsx6("meshBasicMaterial", { map: texture, transparent: true }) }, `${zone.name}-label`)),
1294
- /* @__PURE__ */ jsx6("lineSegments", { geometry: outline, raycast: () => null, children: /* @__PURE__ */ jsx6("lineBasicMaterial", { color: theme.cubeEdge }) }),
1295
- /* @__PURE__ */ jsx6("ambientLight", { intensity: 1.4 }),
1296
- /* @__PURE__ */ jsx6("directionalLight", { position: [viewVector("top-front-right").x, 1, 2], intensity: 1.1 })
2053
+ labels.map(({ zone, geometry, texture }) => /* @__PURE__ */ jsx7("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsx7("meshBasicMaterial", { map: texture, transparent: true }) }, `${zone.name}-label`)),
2054
+ /* @__PURE__ */ jsx7("lineSegments", { geometry: outline, raycast: () => null, children: /* @__PURE__ */ jsx7("lineBasicMaterial", { color: theme.cubeEdge }) }),
2055
+ /* @__PURE__ */ jsx7("ambientLight", { intensity: 1.4 }),
2056
+ /* @__PURE__ */ jsx7("directionalLight", { position: [viewVector("top-front-right").x, 1, 2], intensity: 1.1 })
1297
2057
  ] });
1298
2058
  };
1299
2059
 
1300
2060
  // src/direction-arrows.tsx
1301
- import { useThree as useThree4 } from "@react-three/fiber";
1302
- import { useEffect as useEffect4, useMemo as useMemo4 } from "react";
1303
- import { Quaternion as Quaternion2, Vector3 as Vector36 } from "three";
2061
+ import { useThree as useThree5 } from "@react-three/fiber";
2062
+ import { useEffect as useEffect5, useMemo as useMemo5 } from "react";
2063
+ import { Quaternion as Quaternion2, Vector3 as Vector37 } from "three";
1304
2064
 
1305
2065
  // src/render/directions.ts
1306
- import { Vector3 as Vector35 } from "three";
2066
+ import { Vector3 as Vector36 } from "three";
1307
2067
  var LENGTH = 0.33;
1308
2068
  var GAP = 0.2;
1309
2069
  function arrowPlacement(direction, box) {
1310
- const center = box.getCenter(new Vector35());
1311
- const half = box.getSize(new Vector35()).multiplyScalar(0.5);
2070
+ const center = box.getCenter(new Vector36());
2071
+ const half = box.getSize(new Vector36()).multiplyScalar(0.5);
1312
2072
  const radius = half.length() || 1;
1313
- const axis = new Vector35(direction.x, direction.y, direction.z);
2073
+ const axis = new Vector36(direction.x, direction.y, direction.z);
1314
2074
  if (axis.lengthSq() === 0) axis.set(0, 0, 1);
1315
2075
  axis.normalize();
1316
2076
  let exit = Number.POSITIVE_INFINITY;
@@ -1326,7 +2086,7 @@ function arrowPlacement(direction, box) {
1326
2086
  }
1327
2087
 
1328
2088
  // src/direction-arrows.tsx
1329
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
2089
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1330
2090
  var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
1331
2091
  var DirectionArrows = ({
1332
2092
  directions,
@@ -1339,9 +2099,9 @@ var DirectionArrows = ({
1339
2099
  visible = true
1340
2100
  }) => {
1341
2101
  const box = useContentBox();
1342
- const resolved = useMemo4(() => resolveTheme(theme), [theme]);
1343
- const geometry = useMemo4(() => arrowGeometry(), []);
1344
- useEffect4(() => () => geometry.dispose(), [geometry]);
2102
+ const resolved = useMemo5(() => resolveTheme(theme), [theme]);
2103
+ const geometry = useMemo5(() => arrowGeometry(), []);
2104
+ useEffect5(() => () => geometry.dispose(), [geometry]);
1345
2105
  const shown = shownDirection === void 0 ? activeDirection : shownDirection;
1346
2106
  const drawn = (index) => {
1347
2107
  if (shown === null) return true;
@@ -1353,10 +2113,10 @@ var DirectionArrows = ({
1353
2113
  // Excluded from framing: the arrows are deliberately placed outside the
1354
2114
  // part, so a Fit that measured them would frame the arrows and leave the
1355
2115
  // part small in the middle of them.
1356
- /* @__PURE__ */ jsxs4("group", { userData: FURNITURE3, children: [
2116
+ /* @__PURE__ */ jsxs5("group", { userData: FURNITURE3, children: [
1357
2117
  directions.map((direction, index) => {
1358
2118
  if (!drawn(index)) return null;
1359
- return /* @__PURE__ */ jsx7(
2119
+ return /* @__PURE__ */ jsx8(
1360
2120
  Arrow,
1361
2121
  {
1362
2122
  direction,
@@ -1369,7 +2129,7 @@ var DirectionArrows = ({
1369
2129
  `candidate-${index}`
1370
2130
  );
1371
2131
  }),
1372
- namedDirections.map((named) => /* @__PURE__ */ jsx7(
2132
+ namedDirections.map((named) => /* @__PURE__ */ jsx8(
1373
2133
  Arrow,
1374
2134
  {
1375
2135
  direction: named.direction,
@@ -1381,7 +2141,7 @@ var DirectionArrows = ({
1381
2141
  },
1382
2142
  `named-${named.index}`
1383
2143
  )),
1384
- previewDirection ? /* @__PURE__ */ jsx7(
2144
+ previewDirection ? /* @__PURE__ */ jsx8(
1385
2145
  Arrow,
1386
2146
  {
1387
2147
  direction: previewDirection,
@@ -1396,12 +2156,12 @@ var DirectionArrows = ({
1396
2156
  );
1397
2157
  };
1398
2158
  var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }) => {
1399
- const invalidate = useThree4((state) => state.invalidate);
1400
- const { tip, length, quaternion } = useMemo4(() => {
2159
+ const invalidate = useThree5((state) => state.invalidate);
2160
+ const { tip, length, quaternion } = useMemo5(() => {
1401
2161
  const placement = arrowPlacement(direction, box);
1402
2162
  const aim = new Quaternion2().setFromUnitVectors(
1403
2163
  ARROW_AXIS,
1404
- new Vector36(direction.x, direction.y, direction.z).normalize()
2164
+ new Vector37(direction.x, direction.y, direction.z).normalize()
1405
2165
  );
1406
2166
  return { tip: placement.tip, length: placement.length, quaternion: aim };
1407
2167
  }, [box, direction]);
@@ -1412,23 +2172,23 @@ var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }
1412
2172
  invalidate();
1413
2173
  }
1414
2174
  } : {};
1415
- return /* @__PURE__ */ jsx7("group", { position: tip, quaternion, scale: length, renderOrder: onTop ? 10 : 3, children: /* @__PURE__ */ jsx7(ArrowMesh, { geometry, color, opacity, onTop, events: press }) });
2175
+ return /* @__PURE__ */ jsx8("group", { position: tip, quaternion, scale: length, renderOrder: onTop ? 10 : 3, children: /* @__PURE__ */ jsx8(ArrowMesh, { geometry, color, opacity, onTop, events: press }) });
1416
2176
  };
1417
2177
 
1418
2178
  // src/section-tool.tsx
1419
- import { useFrame, useThree as useThree5 } from "@react-three/fiber";
1420
- import { useEffect as useEffect5, useMemo as useMemo5, useRef as useRef3, useState as useState4, useSyncExternalStore } from "react";
2179
+ import { useFrame as useFrame2, useThree as useThree6 } from "@react-three/fiber";
2180
+ import { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef4, useState as useState4, useSyncExternalStore } from "react";
1421
2181
  import {
1422
- BufferGeometry as BufferGeometry3,
1423
- DoubleSide,
2182
+ BufferGeometry as BufferGeometry5,
2183
+ DoubleSide as DoubleSide3,
1424
2184
  Quaternion as Quaternion3,
1425
2185
  Raycaster,
1426
2186
  Vector2,
1427
- Vector3 as Vector37
2187
+ Vector3 as Vector38
1428
2188
  } from "three";
1429
- import { Fragment, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
2189
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1430
2190
  var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
1431
- var Z = new Vector37(0, 0, 1);
2191
+ var Z = new Vector38(0, 0, 1);
1432
2192
  var AXIS_ROTATIONS = {
1433
2193
  x: [0, Math.PI / 2, 0],
1434
2194
  y: [Math.PI / 2, 0, 0],
@@ -1440,12 +2200,12 @@ var SectionTool = ({ theme }) => {
1440
2200
  const store = useSectionStore();
1441
2201
  const section = useSyncExternalStore(store.subscribe, store.get, store.get);
1442
2202
  const box = useContentBox();
1443
- const resolved = useMemo5(() => resolveTheme(theme), [theme]);
1444
- useEffect5(() => {
2203
+ const resolved = useMemo6(() => resolveTheme(theme), [theme]);
2204
+ useEffect6(() => {
1445
2205
  store.setEngaged(true);
1446
2206
  return () => store.setEngaged(false);
1447
2207
  }, [store]);
1448
- useEffect5(() => {
2208
+ useEffect6(() => {
1449
2209
  if (!section) return void 0;
1450
2210
  const clear = (event) => {
1451
2211
  if (event.key === "Escape") store.set(null);
@@ -1454,43 +2214,43 @@ var SectionTool = ({ theme }) => {
1454
2214
  return () => window.removeEventListener("keydown", clear);
1455
2215
  }, [section, store]);
1456
2216
  if (box.isEmpty()) return null;
1457
- return /* @__PURE__ */ jsx8("group", { userData: FURNITURE4, children: section ? /* @__PURE__ */ jsx8(CutSheet, { box, section, theme: resolved }) : /* @__PURE__ */ jsx8(Picker, { box, theme: resolved }) });
2217
+ return /* @__PURE__ */ jsx9("group", { userData: FURNITURE4, children: section ? /* @__PURE__ */ jsx9(CutSheet, { box, section, theme: resolved }) : /* @__PURE__ */ jsx9(Picker, { box, theme: resolved }) });
1458
2218
  };
1459
2219
  var useSquare = () => {
1460
- const square = useMemo5(
1461
- () => new BufferGeometry3().setFromPoints([
1462
- new Vector37(-1, 1, 0),
1463
- new Vector37(1, 1, 0),
1464
- new Vector37(1, -1, 0),
1465
- new Vector37(-1, -1, 0)
2220
+ const square = useMemo6(
2221
+ () => new BufferGeometry5().setFromPoints([
2222
+ new Vector38(-1, 1, 0),
2223
+ new Vector38(1, 1, 0),
2224
+ new Vector38(1, -1, 0),
2225
+ new Vector38(-1, -1, 0)
1466
2226
  ]),
1467
2227
  []
1468
2228
  );
1469
- useEffect5(() => () => square.dispose(), [square]);
2229
+ useEffect6(() => () => square.dispose(), [square]);
1470
2230
  return square;
1471
2231
  };
1472
2232
  var Picker = ({ box, theme }) => {
1473
2233
  const store = useSectionStore();
1474
- const camera = useThree5((state) => state.camera);
1475
- const scene = useThree5((state) => state.scene);
1476
- const domElement = useThree5((state) => state.gl.domElement);
1477
- const invalidate = useThree5((state) => state.invalidate);
2234
+ const camera = useThree6((state) => state.camera);
2235
+ const scene = useThree6((state) => state.scene);
2236
+ const domElement = useThree6((state) => state.gl.domElement);
2237
+ const invalidate = useThree6((state) => state.invalidate);
1478
2238
  const isTap = useTapGuard();
1479
2239
  const square = useSquare();
1480
- const previewRef = useRef3(null);
1481
- const hover = useRef3(null);
2240
+ const previewRef = useRef4(null);
2241
+ const hover = useRef4(null);
1482
2242
  const [hoveredPlane, setHoveredPlane] = useState4(null);
1483
- const span = useMemo5(() => box.getSize(new Vector37()).length(), [box]);
1484
- const centre = useMemo5(() => box.getCenter(new Vector37()), [box]);
2243
+ const span = useMemo6(() => box.getSize(new Vector38()).length(), [box]);
2244
+ const centre = useMemo6(() => box.getCenter(new Vector38()), [box]);
1485
2245
  const previewSize = span * PREVIEW_SCALE;
1486
- useEffect5(() => {
2246
+ useEffect6(() => {
1487
2247
  store.setPicking(true);
1488
2248
  return () => store.setPicking(false);
1489
2249
  }, [store]);
1490
- useEffect5(() => {
2250
+ useEffect6(() => {
1491
2251
  const raycaster = new Raycaster();
1492
2252
  const pointer = new Vector2();
1493
- const aim = new Vector37();
2253
+ const aim = new Vector38();
1494
2254
  const hide = () => {
1495
2255
  if (!hover.current) return;
1496
2256
  hover.current = null;
@@ -1537,7 +2297,7 @@ var Picker = ({ box, theme }) => {
1537
2297
  }, [box, camera, domElement, invalidate, isTap, scene, span, store]);
1538
2298
  const [planes, setPlanes] = useState4(() => axesPlanes(box, camera.position));
1539
2299
  const key = planes.map((plane) => plane.sign).join();
1540
- useFrame(() => {
2300
+ useFrame2(() => {
1541
2301
  const next = [camera.position.x, camera.position.y, camera.position.z].map((eye, axis) => eye - [centre.x, centre.y, centre.z][axis] < 0 ? -1 : 1).join();
1542
2302
  if (next !== key) setPlanes(axesPlanes(box, camera.position));
1543
2303
  });
@@ -1561,29 +2321,29 @@ var Picker = ({ box, theme }) => {
1561
2321
  store.set(plane.options);
1562
2322
  }
1563
2323
  });
1564
- return /* @__PURE__ */ jsxs5(Fragment, { children: [
1565
- /* @__PURE__ */ jsxs5("group", { ref: previewRef, visible: false, children: [
1566
- /* @__PURE__ */ jsxs5("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
1567
- /* @__PURE__ */ jsx8("planeGeometry", { args: [previewSize, previewSize] }),
1568
- /* @__PURE__ */ jsx8(
2324
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2325
+ /* @__PURE__ */ jsxs6("group", { ref: previewRef, visible: false, children: [
2326
+ /* @__PURE__ */ jsxs6("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
2327
+ /* @__PURE__ */ jsx9("planeGeometry", { args: [previewSize, previewSize] }),
2328
+ /* @__PURE__ */ jsx9(
1569
2329
  "meshBasicMaterial",
1570
2330
  {
1571
2331
  color: theme.sectionOutline,
1572
2332
  transparent: true,
1573
2333
  opacity: 0.2,
1574
- side: DoubleSide,
2334
+ side: DoubleSide3,
1575
2335
  depthWrite: false
1576
2336
  }
1577
2337
  )
1578
2338
  ] }),
1579
- /* @__PURE__ */ jsx8(
2339
+ /* @__PURE__ */ jsx9(
1580
2340
  "lineLoop",
1581
2341
  {
1582
2342
  geometry: square,
1583
2343
  scale: previewSize / 2,
1584
2344
  renderOrder: SECTION_RENDER_ORDER.outline,
1585
2345
  raycast: () => null,
1586
- children: /* @__PURE__ */ jsx8(
2346
+ children: /* @__PURE__ */ jsx9(
1587
2347
  "lineBasicMaterial",
1588
2348
  {
1589
2349
  color: theme.sectionOutline,
@@ -1596,33 +2356,33 @@ var Picker = ({ box, theme }) => {
1596
2356
  }
1597
2357
  )
1598
2358
  ] }),
1599
- planes.map((plane) => /* @__PURE__ */ jsxs5(
2359
+ planes.map((plane) => /* @__PURE__ */ jsxs6(
1600
2360
  "group",
1601
2361
  {
1602
2362
  position: [plane.position.x, plane.position.y, plane.position.z],
1603
2363
  rotation: AXIS_ROTATIONS[plane.axis],
1604
2364
  children: [
1605
- /* @__PURE__ */ jsxs5("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, ...planeHandlers(plane), children: [
1606
- /* @__PURE__ */ jsx8("planeGeometry", { args: [plane.size, plane.size] }),
1607
- /* @__PURE__ */ jsx8(
2365
+ /* @__PURE__ */ jsxs6("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, ...planeHandlers(plane), children: [
2366
+ /* @__PURE__ */ jsx9("planeGeometry", { args: [plane.size, plane.size] }),
2367
+ /* @__PURE__ */ jsx9(
1608
2368
  "meshBasicMaterial",
1609
2369
  {
1610
2370
  color: AXIS_COLORS[plane.axis],
1611
2371
  transparent: true,
1612
2372
  opacity: hoveredPlane === plane.axis ? 0.45 : 0.3,
1613
- side: DoubleSide,
2373
+ side: DoubleSide3,
1614
2374
  depthWrite: false
1615
2375
  }
1616
2376
  )
1617
2377
  ] }),
1618
- /* @__PURE__ */ jsx8(
2378
+ /* @__PURE__ */ jsx9(
1619
2379
  "lineLoop",
1620
2380
  {
1621
2381
  geometry: square,
1622
2382
  scale: plane.size / 2,
1623
2383
  renderOrder: SECTION_RENDER_ORDER.outline,
1624
2384
  raycast: () => null,
1625
- children: /* @__PURE__ */ jsx8("lineBasicMaterial", { color: AXIS_COLORS[plane.axis], transparent: true, opacity: 0.7 })
2385
+ children: /* @__PURE__ */ jsx9("lineBasicMaterial", { color: AXIS_COLORS[plane.axis], transparent: true, opacity: 0.7 })
1626
2386
  }
1627
2387
  )
1628
2388
  ]
@@ -1633,9 +2393,9 @@ var Picker = ({ box, theme }) => {
1633
2393
  };
1634
2394
  var CutSheet = ({ box, section, theme }) => {
1635
2395
  const square = useSquare();
1636
- const cut = useMemo5(() => resolveSectionPlane(section, box), [box, section]);
1637
- const span = useMemo5(() => box.getSize(new Vector37()).length(), [box]);
1638
- const placement = useMemo5(() => {
2396
+ const cut = useMemo6(() => resolveSectionPlane(section, box), [box, section]);
2397
+ const span = useMemo6(() => box.getSize(new Vector38()).length(), [box]);
2398
+ const placement = useMemo6(() => {
1639
2399
  if (!cut) return null;
1640
2400
  const position = sectionAnchor(box, cut.plane).addScaledVector(
1641
2401
  cut.plane.normal,
@@ -1645,28 +2405,28 @@ var CutSheet = ({ box, section, theme }) => {
1645
2405
  }, [box, cut, span]);
1646
2406
  if (!placement) return null;
1647
2407
  const size = span * OUTLINE_SCALE;
1648
- return /* @__PURE__ */ jsxs5("group", { position: placement.position, quaternion: placement.quaternion, children: [
1649
- /* @__PURE__ */ jsxs5("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
1650
- /* @__PURE__ */ jsx8("planeGeometry", { args: [size, size] }),
1651
- /* @__PURE__ */ jsx8(
2408
+ return /* @__PURE__ */ jsxs6("group", { position: placement.position, quaternion: placement.quaternion, children: [
2409
+ /* @__PURE__ */ jsxs6("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
2410
+ /* @__PURE__ */ jsx9("planeGeometry", { args: [size, size] }),
2411
+ /* @__PURE__ */ jsx9(
1652
2412
  "meshBasicMaterial",
1653
2413
  {
1654
2414
  color: theme.sectionOutline,
1655
2415
  transparent: true,
1656
2416
  opacity: 0.1,
1657
- side: DoubleSide,
2417
+ side: DoubleSide3,
1658
2418
  depthWrite: false
1659
2419
  }
1660
2420
  )
1661
2421
  ] }),
1662
- /* @__PURE__ */ jsx8(
2422
+ /* @__PURE__ */ jsx9(
1663
2423
  "lineLoop",
1664
2424
  {
1665
2425
  geometry: square,
1666
2426
  scale: size / 2,
1667
2427
  renderOrder: SECTION_RENDER_ORDER.outline,
1668
2428
  raycast: () => null,
1669
- children: /* @__PURE__ */ jsx8(
2429
+ children: /* @__PURE__ */ jsx9(
1670
2430
  "lineBasicMaterial",
1671
2431
  {
1672
2432
  color: theme.sectionOutline,
@@ -1683,32 +2443,32 @@ var CutSheet = ({ box, section, theme }) => {
1683
2443
 
1684
2444
  // src/measure-tool.tsx
1685
2445
  import { Html, Line } from "@react-three/drei";
1686
- import { useFrame as useFrame2, useThree as useThree6 } from "@react-three/fiber";
2446
+ import { useFrame as useFrame3, useThree as useThree7 } from "@react-three/fiber";
1687
2447
  import {
1688
- useEffect as useEffect6,
1689
- useLayoutEffect as useLayoutEffect3,
1690
- useMemo as useMemo6,
1691
- useRef as useRef4,
2448
+ useEffect as useEffect7,
2449
+ useLayoutEffect as useLayoutEffect4,
2450
+ useMemo as useMemo7,
2451
+ useRef as useRef5,
1692
2452
  useState as useState5,
1693
2453
  useSyncExternalStore as useSyncExternalStore2
1694
2454
  } from "react";
1695
2455
  import {
1696
- Matrix4 as Matrix42,
2456
+ Matrix4 as Matrix43,
1697
2457
  Raycaster as Raycaster2,
1698
2458
  SphereGeometry,
1699
2459
  Vector2 as Vector22,
1700
- Vector3 as Vector39
2460
+ Vector3 as Vector310
1701
2461
  } from "three";
1702
2462
 
1703
2463
  // src/render/resample.ts
1704
- import { Vector3 as Vector38 } from "three";
2464
+ import { Vector3 as Vector39 } from "three";
1705
2465
  var ON_PLANE = 1e-7;
1706
2466
  var STITCH = 1e-5;
1707
2467
  var COLLINEAR = 1e-6;
1708
2468
  function clipSegments(positions, plane) {
1709
2469
  const out = [];
1710
- const a = new Vector38();
1711
- const b = new Vector38();
2470
+ const a = new Vector39();
2471
+ const b = new Vector39();
1712
2472
  const count = Math.floor(positions.length / 6);
1713
2473
  for (let i = 0; i < count; i += 1) {
1714
2474
  a.set(positions[i * 6], positions[i * 6 + 1], positions[i * 6 + 2]);
@@ -1723,11 +2483,11 @@ function clipSegments(positions, plane) {
1723
2483
  return Float32Array.from(out);
1724
2484
  }
1725
2485
  function sectionContour(positions, regionOf, plane) {
1726
- const a = new Vector38();
1727
- const b = new Vector38();
1728
- const c = new Vector38();
1729
- const p = new Vector38();
1730
- const q = new Vector38();
2486
+ const a = new Vector39();
2487
+ const b = new Vector39();
2488
+ const c = new Vector39();
2489
+ const p = new Vector39();
2490
+ const q = new Vector39();
1731
2491
  const byRegion = /* @__PURE__ */ new Map();
1732
2492
  const triangleCount2 = Math.floor(positions.length / 9);
1733
2493
  for (let t = 0; t < triangleCount2; t += 1) {
@@ -1785,17 +2545,17 @@ function stitch(segments, out) {
1785
2545
  return into.set(segments[o], segments[o + 1], segments[o + 2]);
1786
2546
  };
1787
2547
  const direction = (i, into) => {
1788
- const s = new Vector38();
1789
- const e = new Vector38();
2548
+ const s = new Vector39();
2549
+ const e = new Vector39();
1790
2550
  point(i, 0, s);
1791
2551
  point(i, 1, e);
1792
2552
  return into.subVectors(e, s).normalize();
1793
2553
  };
1794
2554
  const consumed = new Uint8Array(count);
1795
- const d = new Vector38();
1796
- const n = new Vector38();
2555
+ const d = new Vector39();
2556
+ const n = new Vector39();
1797
2557
  const next = (i, end) => {
1798
- const at = point(i, end, new Vector38());
2558
+ const at = point(i, end, new Vector39());
1799
2559
  const list = ends.get(key(at.x, at.y, at.z)) ?? [];
1800
2560
  if (list.length !== 2) return null;
1801
2561
  const other = list.find((entry) => entry >> 1 !== i);
@@ -1824,8 +2584,8 @@ function stitch(segments, out) {
1824
2584
  };
1825
2585
  const back = walk(0);
1826
2586
  const forward = walk(1);
1827
- const s = point(back.index, back.end, new Vector38());
1828
- const e = point(forward.index, forward.end, new Vector38());
2587
+ const s = point(back.index, back.end, new Vector39());
2588
+ const e = point(forward.index, forward.end, new Vector39());
1829
2589
  out.push(s.x, s.y, s.z, e.x, e.y, e.z);
1830
2590
  }
1831
2591
  }
@@ -1833,8 +2593,8 @@ function pointInContour(point, plane, contour) {
1833
2593
  const [u, v] = planeAxes(plane);
1834
2594
  const px = point.dot(u);
1835
2595
  const py = point.dot(v);
1836
- const a = new Vector38();
1837
- const b = new Vector38();
2596
+ const a = new Vector39();
2597
+ const b = new Vector39();
1838
2598
  let inside = false;
1839
2599
  const count = Math.floor(contour.length / 6);
1840
2600
  for (let i = 0; i < count; i += 1) {
@@ -1851,15 +2611,15 @@ function pointInContour(point, plane, contour) {
1851
2611
  return inside;
1852
2612
  }
1853
2613
  function capHit(ray, plane, contour) {
1854
- const point = ray.intersectPlane(plane, new Vector38());
2614
+ const point = ray.intersectPlane(plane, new Vector39());
1855
2615
  if (!point) return null;
1856
2616
  return pointInContour(point, plane, contour) ? point : null;
1857
2617
  }
1858
2618
  function planeAxes(plane) {
1859
2619
  const n = plane.normal;
1860
- const seed = Math.abs(n.x) < 0.9 ? new Vector38(1, 0, 0) : new Vector38(0, 1, 0);
1861
- const u = new Vector38().crossVectors(n, seed).normalize();
1862
- const v = new Vector38().crossVectors(n, u).normalize();
2620
+ const seed = Math.abs(n.x) < 0.9 ? new Vector39(1, 0, 0) : new Vector39(0, 1, 0);
2621
+ const u = new Vector39().crossVectors(n, seed).normalize();
2622
+ const v = new Vector39().crossVectors(n, u).normalize();
1863
2623
  return [u, v];
1864
2624
  }
1865
2625
  function sampleSection(meshPositions, regionOf, edgePositions, plane) {
@@ -1872,7 +2632,7 @@ function sampleSection(meshPositions, regionOf, edgePositions, plane) {
1872
2632
  }
1873
2633
 
1874
2634
  // src/measure-tool.tsx
1875
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2635
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1876
2636
  var FURNITURE5 = { [EXCLUDE_FROM_FRAME]: true };
1877
2637
  var MEASURE_LABEL_CLASS = "toolpath-measure-label";
1878
2638
  var MeasureTool = ({
@@ -1886,26 +2646,26 @@ var MeasureTool = ({
1886
2646
  }) => {
1887
2647
  const store = useSectionStore();
1888
2648
  const box = useContentBox();
1889
- const resolved = useMemo6(() => resolveTheme(theme), [theme]);
2649
+ const resolved = useMemo7(() => resolveTheme(theme), [theme]);
1890
2650
  const picking = useSyncExternalStore2(store.subscribe, store.isPicking, store.isPicking);
1891
2651
  const [held, setHeld] = useState5([]);
1892
2652
  const controlled = measurements !== void 0;
1893
2653
  const list = controlled ? measurements : held;
1894
2654
  const [draft, setDraft] = useState5([]);
1895
- useEffect6(() => {
2655
+ useEffect7(() => {
1896
2656
  store.setEngaged(true);
1897
2657
  return () => store.setEngaged(false);
1898
2658
  }, [store]);
1899
- useEffect6(() => {
2659
+ useEffect7(() => {
1900
2660
  setDraft([]);
1901
2661
  }, [mode]);
1902
2662
  const commit = (next) => {
1903
2663
  if (!controlled) setHeld(next);
1904
2664
  onChange?.(next);
1905
2665
  };
1906
- const latest = useRef4({ list, commit });
2666
+ const latest = useRef5({ list, commit });
1907
2667
  latest.current = { list, commit };
1908
- useEffect6(() => {
2668
+ useEffect7(() => {
1909
2669
  const onKey = (event) => {
1910
2670
  if (event.key === "Escape") {
1911
2671
  setDraft([]);
@@ -1933,8 +2693,8 @@ var MeasureTool = ({
1933
2693
  };
1934
2694
  if (box.isEmpty()) return null;
1935
2695
  const label = className(labelClassName);
1936
- return /* @__PURE__ */ jsxs6("group", { userData: FURNITURE5, children: [
1937
- picking ? null : /* @__PURE__ */ jsx9(
2696
+ return /* @__PURE__ */ jsxs7("group", { userData: FURNITURE5, children: [
2697
+ picking ? null : /* @__PURE__ */ jsx10(
1938
2698
  Snapper,
1939
2699
  {
1940
2700
  mode,
@@ -1945,7 +2705,7 @@ var MeasureTool = ({
1945
2705
  label
1946
2706
  }
1947
2707
  ),
1948
- draft.map((point, index) => /* @__PURE__ */ jsx9(
2708
+ draft.map((point, index) => /* @__PURE__ */ jsx10(
1949
2709
  ScreenDot,
1950
2710
  {
1951
2711
  point,
@@ -1954,8 +2714,8 @@ var MeasureTool = ({
1954
2714
  },
1955
2715
  index
1956
2716
  )),
1957
- draft.length === 2 ? /* @__PURE__ */ jsx9(Segment, { points: [draft[0], draft[1]], color: resolved.measure }) : null,
1958
- list.map((measurement) => /* @__PURE__ */ jsx9(
2717
+ draft.length === 2 ? /* @__PURE__ */ jsx10(Segment, { points: [draft[0], draft[1]], color: resolved.measure }) : null,
2718
+ list.map((measurement) => /* @__PURE__ */ jsx10(
1959
2719
  MeasurementView,
1960
2720
  {
1961
2721
  measurement,
@@ -1969,28 +2729,28 @@ var MeasureTool = ({
1969
2729
  ] });
1970
2730
  };
1971
2731
  var NO_POINTER = { pointerEvents: "none" };
1972
- var NOWHERE = [new Vector39(), new Vector39()];
2732
+ var NOWHERE = [new Vector310(), new Vector310()];
1973
2733
  var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
1974
- const camera = useThree6((state) => state.camera);
1975
- const scene = useThree6((state) => state.scene);
1976
- const size = useThree6((state) => state.size);
1977
- const domElement = useThree6((state) => state.gl.domElement);
1978
- const invalidate = useThree6((state) => state.invalidate);
2734
+ const camera = useThree7((state) => state.camera);
2735
+ const scene = useThree7((state) => state.scene);
2736
+ const size = useThree7((state) => state.size);
2737
+ const domElement = useThree7((state) => state.gl.domElement);
2738
+ const invalidate = useThree7((state) => state.invalidate);
1979
2739
  const isTap = useTapGuard();
1980
- const marker = useRef4(null);
1981
- const edgeLine = useRef4(null);
1982
- const band = useRef4(null);
1983
- const bandLabel = useRef4(null);
1984
- const bandText = useRef4(null);
1985
- const snap = useRef4(null);
2740
+ const marker = useRef5(null);
2741
+ const edgeLine = useRef5(null);
2742
+ const band = useRef5(null);
2743
+ const bandLabel = useRef5(null);
2744
+ const bandText = useRef5(null);
2745
+ const snap = useRef5(null);
1986
2746
  const dot = useDot();
1987
- useLayoutEffect3(() => {
2747
+ useLayoutEffect4(() => {
1988
2748
  if (edgeLine.current) edgeLine.current.visible = false;
1989
2749
  if (band.current) band.current.visible = false;
1990
2750
  }, []);
1991
- const current = useRef4({ mode, draft, onPlace, format, size });
2751
+ const current = useRef5({ mode, draft, onPlace, format, size });
1992
2752
  current.current = { mode, draft, onPlace, format, size };
1993
- useEffect6(() => {
2753
+ useEffect7(() => {
1994
2754
  const raycaster = new Raycaster2();
1995
2755
  const pointer = new Vector22();
1996
2756
  const samples = new SampleCache();
@@ -2022,7 +2782,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
2022
2782
  const snapped = snapAt(target, target.edges, camera, viewport);
2023
2783
  const from = placed[placed.length - 1];
2024
2784
  const lock = shift && from ? lockToAxis(from, snapped.point) : null;
2025
- const found = lock ? { ...snapped, point: new Vector39(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
2785
+ const found = lock ? { ...snapped, point: new Vector310(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
2026
2786
  snap.current = found;
2027
2787
  const node = marker.current;
2028
2788
  if (node) {
@@ -2096,7 +2856,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
2096
2856
  window.removeEventListener("keyup", key);
2097
2857
  };
2098
2858
  }, [camera, domElement, invalidate, isTap, scene, theme.measure]);
2099
- useEffect6(() => {
2859
+ useEffect7(() => {
2100
2860
  if (draft.length === 0 && band.current) {
2101
2861
  band.current.visible = false;
2102
2862
  if (bandLabel.current) bandLabel.current.visible = false;
@@ -2107,9 +2867,9 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
2107
2867
  invalidate();
2108
2868
  }
2109
2869
  }, [draft, invalidate]);
2110
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2111
- /* @__PURE__ */ jsx9("group", { ref: marker, visible: false, children: /* @__PURE__ */ jsx9("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx9("meshBasicMaterial", { color: theme.measureSnap, depthTest: false, depthWrite: false }) }) }),
2112
- /* @__PURE__ */ jsx9(
2870
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
2871
+ /* @__PURE__ */ jsx10("group", { ref: marker, visible: false, children: /* @__PURE__ */ jsx10("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx10("meshBasicMaterial", { color: theme.measureSnap, depthTest: false, depthWrite: false }) }) }),
2872
+ /* @__PURE__ */ jsx10(
2113
2873
  Line,
2114
2874
  {
2115
2875
  ref: edgeLine,
@@ -2123,7 +2883,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
2123
2883
  raycast: () => null
2124
2884
  }
2125
2885
  ),
2126
- /* @__PURE__ */ jsx9(
2886
+ /* @__PURE__ */ jsx10(
2127
2887
  Line,
2128
2888
  {
2129
2889
  ref: band,
@@ -2137,7 +2897,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
2137
2897
  raycast: () => null
2138
2898
  }
2139
2899
  ),
2140
- /* @__PURE__ */ jsx9("group", { ref: bandLabel, visible: false, children: /* @__PURE__ */ jsx9(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx9(
2900
+ /* @__PURE__ */ jsx10("group", { ref: bandLabel, visible: false, children: /* @__PURE__ */ jsx10(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx10(
2141
2901
  "div",
2142
2902
  {
2143
2903
  ref: bandText,
@@ -2184,7 +2944,7 @@ function targetUnderRay(raycaster, scene, samples) {
2184
2944
  edges: samples.edgesFor(hit.object)
2185
2945
  };
2186
2946
  }
2187
- const local = new Matrix42();
2947
+ const local = new Matrix43();
2188
2948
  for (const mesh of cutMeshes(scene)) {
2189
2949
  const planes = clippingPlanesOf(mesh);
2190
2950
  const sample = samples.for(mesh, planes);
@@ -2247,7 +3007,7 @@ var SampleCache = class {
2247
3007
  const key = planes.map((plane) => `${plane.normal.x},${plane.normal.y},${plane.normal.z},${plane.constant}`).join("|");
2248
3008
  const cached = this.held.get(mesh);
2249
3009
  if (cached && cached.key === key) return cached;
2250
- const local = new Matrix42().copy(mesh.matrixWorld).invert();
3010
+ const local = new Matrix43().copy(mesh.matrixWorld).invert();
2251
3011
  const localPlanes = planes.map((plane) => plane.clone().applyMatrix4(local));
2252
3012
  const positions = mesh.geometry.getAttribute("position")?.array ?? [];
2253
3013
  const regionOf = this.regionsOf(mesh.geometry);
@@ -2302,37 +3062,37 @@ var MeasurementView = ({
2302
3062
  const anchor = measurementLabelAnchor(measurement);
2303
3063
  if (measurement.kind === "distance") {
2304
3064
  const [a2, b2] = measurement.points;
2305
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2306
- /* @__PURE__ */ jsx9(Segment, { points: [a2, b2], color: theme.measure }),
2307
- /* @__PURE__ */ jsx9(ScreenDot, { point: a2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
2308
- /* @__PURE__ */ jsx9(ScreenDot, { point: b2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
2309
- showDeltas ? /* @__PURE__ */ jsx9(DeltaLegs, { a: a2, b: b2, id: measurement.id, format, label }) : null,
2310
- /* @__PURE__ */ jsx9(Label, { at: anchor, text, kind: "distance", id: measurement.id, className: label })
3065
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
3066
+ /* @__PURE__ */ jsx10(Segment, { points: [a2, b2], color: theme.measure }),
3067
+ /* @__PURE__ */ jsx10(ScreenDot, { point: a2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
3068
+ /* @__PURE__ */ jsx10(ScreenDot, { point: b2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
3069
+ showDeltas ? /* @__PURE__ */ jsx10(DeltaLegs, { a: a2, b: b2, id: measurement.id, format, label }) : null,
3070
+ /* @__PURE__ */ jsx10(Label, { at: anchor, text, kind: "distance", id: measurement.id, className: label })
2311
3071
  ] });
2312
3072
  }
2313
3073
  const [a, vertex, b] = measurement.points;
2314
3074
  const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b));
2315
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2316
- /* @__PURE__ */ jsx9(Segment, { points: [vertex, a], color: theme.measure }),
2317
- /* @__PURE__ */ jsx9(Segment, { points: [vertex, b], color: theme.measure }),
2318
- /* @__PURE__ */ jsx9(Polyline, { points: arc, color: theme.measure }),
2319
- /* @__PURE__ */ jsx9(ScreenDot, { point: a, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
2320
- /* @__PURE__ */ jsx9(ScreenDot, { point: vertex, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
2321
- /* @__PURE__ */ jsx9(ScreenDot, { point: b, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
2322
- /* @__PURE__ */ jsx9(Label, { at: anchor, text, kind: "angle", id: measurement.id, className: label })
3075
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
3076
+ /* @__PURE__ */ jsx10(Segment, { points: [vertex, a], color: theme.measure }),
3077
+ /* @__PURE__ */ jsx10(Segment, { points: [vertex, b], color: theme.measure }),
3078
+ /* @__PURE__ */ jsx10(Polyline, { points: arc, color: theme.measure }),
3079
+ /* @__PURE__ */ jsx10(ScreenDot, { point: a, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
3080
+ /* @__PURE__ */ jsx10(ScreenDot, { point: vertex, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
3081
+ /* @__PURE__ */ jsx10(ScreenDot, { point: b, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
3082
+ /* @__PURE__ */ jsx10(Label, { at: anchor, text, kind: "angle", id: measurement.id, className: label })
2323
3083
  ] });
2324
3084
  };
2325
3085
  var DeltaLegs = ({ a, b, id, format, label }) => {
2326
- const camera = useThree6((state) => state.camera);
2327
- const size = useThree6((state) => state.size);
2328
- const invalidate = useThree6((state) => state.invalidate);
2329
- const legs = useMemo6(() => deltaLegs(a, b), [a, b]);
2330
- const lines = useRef4([]);
2331
- const centre = useMemo6(() => {
3086
+ const camera = useThree7((state) => state.camera);
3087
+ const size = useThree7((state) => state.size);
3088
+ const invalidate = useThree7((state) => state.invalidate);
3089
+ const legs = useMemo7(() => deltaLegs(a, b), [a, b]);
3090
+ const lines = useRef5([]);
3091
+ const centre = useMemo7(() => {
2332
3092
  const at = midpoint(a, b);
2333
- return new Vector39(at.x, at.y, at.z);
3093
+ return new Vector310(at.x, at.y, at.z);
2334
3094
  }, [a, b]);
2335
- useFrame2(() => {
3095
+ useFrame3(() => {
2336
3096
  const dash = screenLength(camera, centre, size, DELTA_DASH_PIXELS);
2337
3097
  let changed = false;
2338
3098
  for (const line of lines.current) {
@@ -2344,16 +3104,16 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
2344
3104
  }
2345
3105
  if (changed) invalidate();
2346
3106
  });
2347
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2348
- legs.map((leg, index) => /* @__PURE__ */ jsx9(
3107
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
3108
+ legs.map((leg, index) => /* @__PURE__ */ jsx10(
2349
3109
  Line,
2350
3110
  {
2351
3111
  ref: (line) => {
2352
3112
  lines.current[index] = line;
2353
3113
  },
2354
3114
  points: [
2355
- new Vector39(leg.from.x, leg.from.y, leg.from.z),
2356
- new Vector39(leg.to.x, leg.to.y, leg.to.z)
3115
+ new Vector310(leg.from.x, leg.from.y, leg.from.z),
3116
+ new Vector310(leg.to.x, leg.to.y, leg.to.z)
2357
3117
  ],
2358
3118
  color: AXIS_COLORS[leg.axis],
2359
3119
  lineWidth: MEASURE_LINE_PIXELS.leg,
@@ -2368,7 +3128,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
2368
3128
  },
2369
3129
  leg.axis
2370
3130
  )),
2371
- legs.map((leg) => /* @__PURE__ */ jsx9(
3131
+ legs.map((leg) => /* @__PURE__ */ jsx10(
2372
3132
  Label,
2373
3133
  {
2374
3134
  at: midpoint(leg.from, leg.to),
@@ -2382,15 +3142,15 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
2382
3142
  ))
2383
3143
  ] });
2384
3144
  };
2385
- var Label = ({ at, text, kind, id, axis, className: name }) => /* @__PURE__ */ jsx9("group", { position: [at.x, at.y, at.z], children: /* @__PURE__ */ jsx9(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx9("div", { className: name, "data-measure-label": kind, "data-measurement-id": id, "data-axis": axis, children: text }) }) });
3145
+ var Label = ({ at, text, kind, id, axis, className: name }) => /* @__PURE__ */ jsx10("group", { position: [at.x, at.y, at.z], children: /* @__PURE__ */ jsx10(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx10("div", { className: name, "data-measure-label": kind, "data-measurement-id": id, "data-axis": axis, children: text }) }) });
2386
3146
  var ScreenDot = ({ point, pixels, color }) => {
2387
- const camera = useThree6((state) => state.camera);
2388
- const size = useThree6((state) => state.size);
2389
- const invalidate = useThree6((state) => state.invalidate);
2390
- const node = useRef4(null);
3147
+ const camera = useThree7((state) => state.camera);
3148
+ const size = useThree7((state) => state.size);
3149
+ const invalidate = useThree7((state) => state.invalidate);
3150
+ const node = useRef5(null);
2391
3151
  const dot = useDot();
2392
- const position = useMemo6(() => new Vector39(point.x, point.y, point.z), [point]);
2393
- useFrame2(() => {
3152
+ const position = useMemo7(() => new Vector310(point.x, point.y, point.z), [point]);
3153
+ useFrame3(() => {
2394
3154
  const group = node.current;
2395
3155
  if (!group) return;
2396
3156
  const length = screenLength(camera, position, size, pixels);
@@ -2399,21 +3159,21 @@ var ScreenDot = ({ point, pixels, color }) => {
2399
3159
  invalidate();
2400
3160
  }
2401
3161
  });
2402
- return /* @__PURE__ */ jsx9("group", { ref: node, position, children: /* @__PURE__ */ jsx9("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx9("meshBasicMaterial", { color, depthTest: false, depthWrite: false }) }) });
3162
+ return /* @__PURE__ */ jsx10("group", { ref: node, position, children: /* @__PURE__ */ jsx10("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx10("meshBasicMaterial", { color, depthTest: false, depthWrite: false }) }) });
2403
3163
  };
2404
3164
  var Segment = ({ points, color }) => {
2405
3165
  const [a, b] = points;
2406
- const ends = useMemo6(
2407
- () => [new Vector39(a.x, a.y, a.z), new Vector39(b.x, b.y, b.z)],
3166
+ const ends = useMemo7(
3167
+ () => [new Vector310(a.x, a.y, a.z), new Vector310(b.x, b.y, b.z)],
2408
3168
  // Keyed on the numbers: a draft's tuple is a fresh array on every render,
2409
3169
  // and a rebuilt array would rebuild the line's geometry with it.
2410
3170
  [a.x, a.y, a.z, b.x, b.y, b.z]
2411
3171
  );
2412
- return /* @__PURE__ */ jsx9(Polyline, { points: ends, color });
3172
+ return /* @__PURE__ */ jsx10(Polyline, { points: ends, color });
2413
3173
  };
2414
3174
  var Polyline = ({ points, color }) => {
2415
3175
  if (points.length < 2) return null;
2416
- return /* @__PURE__ */ jsx9(
3176
+ return /* @__PURE__ */ jsx10(
2417
3177
  Line,
2418
3178
  {
2419
3179
  points,
@@ -2428,8 +3188,8 @@ var Polyline = ({ points, color }) => {
2428
3188
  );
2429
3189
  };
2430
3190
  function useDot() {
2431
- const geometry = useMemo6(() => new SphereGeometry(1, 16, 12), []);
2432
- useEffect6(() => () => geometry.dispose(), [geometry]);
3191
+ const geometry = useMemo7(() => new SphereGeometry(1, 16, 12), []);
3192
+ useEffect7(() => () => geometry.dispose(), [geometry]);
2433
3193
  return geometry;
2434
3194
  }
2435
3195
  function isEditing(target) {
@@ -2442,11 +3202,11 @@ function className(extra) {
2442
3202
 
2443
3203
  // src/tool-marks.tsx
2444
3204
  import { Html as Html2, Line as Line2 } from "@react-three/drei";
2445
- import { useMemo as useMemo7 } from "react";
2446
- import { DoubleSide as DoubleSide2 } from "three";
3205
+ import { useMemo as useMemo8 } from "react";
3206
+ import { DoubleSide as DoubleSide4 } from "three";
2447
3207
 
2448
3208
  // src/render/tool-marks.ts
2449
- import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as Vector310 } from "three";
3209
+ import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as Vector311 } from "three";
2450
3210
  var TOOL_CORNER_STEPS = 12;
2451
3211
  var TOOL_ROUND = 64;
2452
3212
  function toolProfile(radius, corner, height) {
@@ -2466,9 +3226,9 @@ function toolProfile(radius, corner, height) {
2466
3226
  points.push(new Vector23(radius, Math.max(height, rounded)));
2467
3227
  return points;
2468
3228
  }
2469
- var UP = new Vector310(0, 1, 0);
2470
- var AXES = [new Vector310(1, 0, 0), new Vector310(0, 1, 0), new Vector310(0, 0, 1)];
2471
- var toVector = ({ x, y, z }) => new Vector310(x, y, z);
3229
+ var UP = new Vector311(0, 1, 0);
3230
+ var AXES = [new Vector311(1, 0, 0), new Vector311(0, 1, 0), new Vector311(0, 0, 1)];
3231
+ var toVector = ({ x, y, z }) => new Vector311(x, y, z);
2472
3232
  var squareTo = (w, along) => {
2473
3233
  const wanted = along ? toVector(along) : null;
2474
3234
  const flat = wanted?.clone().addScaledVector(w, -wanted.dot(w));
@@ -2488,7 +3248,7 @@ function toolMarkShape(mark) {
2488
3248
  if (!(mark.diameter > 0) || axis.lengthSq() === 0) return null;
2489
3249
  const w = axis.normalize();
2490
3250
  const u = squareTo(w, mark.across);
2491
- const v = new Vector310().crossVectors(w, u);
3251
+ const v = new Vector311().crossVectors(w, u);
2492
3252
  const radius = mark.diameter / 2;
2493
3253
  const corner = Math.min(Math.max(mark.cornerRadius ?? 0, 0), radius);
2494
3254
  const height = Math.max(mark.height, corner);
@@ -2518,13 +3278,12 @@ function toolMarkShape(mark) {
2518
3278
  }
2519
3279
 
2520
3280
  // src/tool-marks.tsx
2521
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
3281
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2522
3282
  var BODY = { color: 16317180, opacity: 0.32 };
2523
3283
  var NO_POINTER2 = { pointerEvents: "none" };
2524
- var ignoreRay = () => void 0;
2525
3284
  var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
2526
3285
  const color = resolveTheme(theme).measure;
2527
- const shapes = useMemo7(
3286
+ const shapes = useMemo8(
2528
3287
  () => marks.flatMap((mark) => {
2529
3288
  const shape = toolMarkShape(mark);
2530
3289
  return shape ? [{ mark, shape }] : [];
@@ -2532,9 +3291,9 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
2532
3291
  [marks]
2533
3292
  );
2534
3293
  const label = labelClassName ? `${MEASURE_LABEL_CLASS} ${labelClassName}` : MEASURE_LABEL_CLASS;
2535
- return /* @__PURE__ */ jsx10("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: true }, children: shapes.map(({ mark, shape }, index) => /* @__PURE__ */ jsxs7("group", { children: [
2536
- /* @__PURE__ */ jsx10(ToolBody, { shape }),
2537
- [...shape.rims, shape.dimension, ...shape.ticks].map((points, line) => /* @__PURE__ */ jsx10(
3294
+ return /* @__PURE__ */ jsx11("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: true }, children: shapes.map(({ mark, shape }, index) => /* @__PURE__ */ jsxs8("group", { children: [
3295
+ /* @__PURE__ */ jsx11(ToolBody, { shape }),
3296
+ [...shape.rims, shape.dimension, ...shape.ticks].map((points, line) => /* @__PURE__ */ jsx11(
2538
3297
  Line2,
2539
3298
  {
2540
3299
  points,
@@ -2547,16 +3306,16 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
2547
3306
  },
2548
3307
  line
2549
3308
  )),
2550
- mark.label && visible ? /* @__PURE__ */ jsx10(Html2, { position: shape.labelAt, center: true, zIndexRange: [0, 0], style: NO_POINTER2, children: /* @__PURE__ */ jsxs7("div", { className: label, "data-measure-label": "tool", children: [
2551
- /* @__PURE__ */ jsx10("span", { "data-measure-value": true, children: mark.label }),
2552
- mark.note ? /* @__PURE__ */ jsxs7("span", { "data-measure-note": true, children: [
3309
+ mark.label && visible ? /* @__PURE__ */ jsx11(Html2, { position: shape.labelAt, center: true, zIndexRange: [0, 0], style: NO_POINTER2, children: /* @__PURE__ */ jsxs8("div", { className: label, "data-measure-label": "tool", children: [
3310
+ /* @__PURE__ */ jsx11("span", { "data-measure-value": true, children: mark.label }),
3311
+ mark.note ? /* @__PURE__ */ jsxs8("span", { "data-measure-note": true, children: [
2553
3312
  " ",
2554
3313
  mark.note
2555
3314
  ] }) : null
2556
3315
  ] }) }) : null
2557
3316
  ] }, index)) });
2558
3317
  };
2559
- var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs7(
3318
+ var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs8(
2560
3319
  "mesh",
2561
3320
  {
2562
3321
  position: shape.base,
@@ -2564,15 +3323,15 @@ var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs7(
2564
3323
  raycast: ignoreRay,
2565
3324
  renderOrder: MEASURE_RENDER_ORDER - 1,
2566
3325
  children: [
2567
- /* @__PURE__ */ jsx10("latheGeometry", { args: [shape.profile, 64] }),
2568
- /* @__PURE__ */ jsx10(
3326
+ /* @__PURE__ */ jsx11("latheGeometry", { args: [shape.profile, 64] }),
3327
+ /* @__PURE__ */ jsx11(
2569
3328
  "meshBasicMaterial",
2570
3329
  {
2571
3330
  color: BODY.color,
2572
3331
  transparent: true,
2573
3332
  opacity: BODY.opacity,
2574
3333
  depthWrite: false,
2575
- side: DoubleSide2
3334
+ side: DoubleSide4
2576
3335
  }
2577
3336
  )
2578
3337
  ]
@@ -2638,6 +3397,7 @@ export {
2638
3397
  DirectionArrows,
2639
3398
  EXCLUDE_FROM_FRAME,
2640
3399
  EnginePart,
3400
+ ExplodedPart,
2641
3401
  ExtendedCameraControls,
2642
3402
  FEATURE_TYPE_RANKS,
2643
3403
  Grid,
@@ -2711,6 +3471,7 @@ export {
2711
3471
  clipSegments,
2712
3472
  contentBounds,
2713
3473
  createCapMaterial,
3474
+ createExplodeAmount,
2714
3475
  createMaskMaterial,
2715
3476
  createMaskTarget,
2716
3477
  createPart,