@toolpath/viewer 2.3.0 → 2.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -163,7 +163,7 @@ import {
163
163
  viewUp,
164
164
  viewVector,
165
165
  visualSurfaces
166
- } from "./chunk-N54FHMTG.js";
166
+ } from "./chunk-M2B3CQPQ.js";
167
167
 
168
168
  // src/render/feature-triangles.ts
169
169
  var EMPTY = { positions: new Float32Array(), normals: new Float32Array() };
@@ -173,13 +173,13 @@ function featureTriangles(model, geometry, tag) {
173
173
  const normal = geometry.getAttribute("normal");
174
174
  const index = geometry.getIndex();
175
175
  const vertex = (corner) => index ? index.getX(corner) : corner;
176
- const triangleCount = Math.floor((index ? index.count : position.count) / 3);
176
+ const triangleCount2 = Math.floor((index ? index.count : position.count) / 3);
177
177
  const positions = [];
178
178
  const normals = [];
179
179
  for (const region of new Set(model.regionIndex.regionsForFeature(tag))) {
180
180
  const range = model.regionIndex.rangeForRegion(region);
181
181
  if (!range) continue;
182
- for (let triangle = range.start; triangle < Math.min(range.end, triangleCount); triangle++) {
182
+ for (let triangle = range.start; triangle < Math.min(range.end, triangleCount2); triangle++) {
183
183
  const corners = [vertex(triangle * 3), vertex(triangle * 3 + 1), vertex(triangle * 3 + 2)];
184
184
  const at = corners.map((corner) => [
185
185
  position.getX(corner),
@@ -594,8 +594,9 @@ function createStockMeshBuffer() {
594
594
  import {
595
595
  Box3,
596
596
  BoxGeometry,
597
+ BufferGeometry,
597
598
  CylinderGeometry,
598
- EdgesGeometry,
599
+ Float32BufferAttribute,
599
600
  Group,
600
601
  LineBasicMaterial,
601
602
  LineSegments,
@@ -603,8 +604,134 @@ import {
603
604
  Mesh as Mesh2,
604
605
  MeshLambertMaterial,
605
606
  Quaternion,
606
- Vector3
607
+ Vector3 as Vector32
607
608
  } from "three";
609
+
610
+ // src/render/stock-edges.ts
611
+ import { MathUtils, Triangle, Vector3 } from "three";
612
+ var STOCK_EDGE_ANGLE = 15;
613
+ var SYNC_EDGE_TRIANGLES = 2e4;
614
+ var SLICE_MS = 4;
615
+ var CHECK_EVERY = 256;
616
+ var _v0 = new Vector3();
617
+ var _v1 = new Vector3();
618
+ var _normal = new Vector3();
619
+ var _triangle = new Triangle();
620
+ function* edgeWork(geometry, thresholdAngle) {
621
+ const precision = Math.pow(10, 4);
622
+ const thresholdDot = Math.cos(MathUtils.DEG2RAD * thresholdAngle);
623
+ const indexAttr = geometry.getIndex();
624
+ const positionAttr = geometry.getAttribute("position");
625
+ const indexCount = indexAttr ? indexAttr.count : positionAttr.count;
626
+ const indexArr = [0, 0, 0];
627
+ const corners = [_triangle.a, _triangle.b, _triangle.c];
628
+ const hashes = ["", "", ""];
629
+ const edgeData = /* @__PURE__ */ new Map();
630
+ const vertices = [];
631
+ const key = (v) => `${Math.round(v.x * precision)},${Math.round(v.y * precision)},${Math.round(v.z * precision)}`;
632
+ for (let i = 0, step2 = 0; i < indexCount; i += 3, step2 += 1) {
633
+ if (step2 % CHECK_EVERY === 0 && step2 > 0) yield;
634
+ if (indexAttr) {
635
+ indexArr[0] = indexAttr.getX(i);
636
+ indexArr[1] = indexAttr.getX(i + 1);
637
+ indexArr[2] = indexAttr.getX(i + 2);
638
+ } else {
639
+ indexArr[0] = i;
640
+ indexArr[1] = i + 1;
641
+ indexArr[2] = i + 2;
642
+ }
643
+ _triangle.a.fromBufferAttribute(positionAttr, indexArr[0]);
644
+ _triangle.b.fromBufferAttribute(positionAttr, indexArr[1]);
645
+ _triangle.c.fromBufferAttribute(positionAttr, indexArr[2]);
646
+ _triangle.getNormal(_normal);
647
+ hashes[0] = key(_triangle.a);
648
+ hashes[1] = key(_triangle.b);
649
+ hashes[2] = key(_triangle.c);
650
+ if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) continue;
651
+ for (let j = 0; j < 3; j += 1) {
652
+ const jNext = (j + 1) % 3;
653
+ const v0 = corners[j];
654
+ const v1 = corners[jNext];
655
+ const hash = `${hashes[j]}_${hashes[jNext]}`;
656
+ const reverse = edgeData.get(`${hashes[jNext]}_${hashes[j]}`);
657
+ if (reverse) {
658
+ if (_normal.dot(reverse.normal) <= thresholdDot) {
659
+ vertices.push(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z);
660
+ }
661
+ edgeData.set(`${hashes[jNext]}_${hashes[j]}`, null);
662
+ } else if (!edgeData.has(hash)) {
663
+ edgeData.set(hash, {
664
+ index0: indexArr[j],
665
+ index1: indexArr[jNext],
666
+ normal: _normal.clone()
667
+ });
668
+ }
669
+ }
670
+ }
671
+ let step = 0;
672
+ for (const edge of edgeData.values()) {
673
+ step += 1;
674
+ if (step % CHECK_EVERY === 0) yield;
675
+ if (!edge) continue;
676
+ _v0.fromBufferAttribute(positionAttr, edge.index0);
677
+ _v1.fromBufferAttribute(positionAttr, edge.index1);
678
+ vertices.push(_v0.x, _v0.y, _v0.z, _v1.x, _v1.y, _v1.z);
679
+ }
680
+ return new Float32Array(vertices);
681
+ }
682
+ function stockEdgePositions(geometry, thresholdAngle = STOCK_EDGE_ANGLE) {
683
+ const work = edgeWork(geometry, thresholdAngle);
684
+ for (; ; ) {
685
+ const next = work.next();
686
+ if (next.done) return next.value;
687
+ }
688
+ }
689
+ function triangleCount(geometry) {
690
+ const index = geometry.getIndex();
691
+ return Math.floor((index ? index.count : geometry.getAttribute("position")?.count ?? 0) / 3);
692
+ }
693
+ var nextTurn = (run) => {
694
+ if (typeof MessageChannel === "undefined") {
695
+ const timer = setTimeout(run, 0);
696
+ return () => clearTimeout(timer);
697
+ }
698
+ const channel = new MessageChannel();
699
+ let live = true;
700
+ channel.port1.onmessage = () => {
701
+ channel.port1.close();
702
+ if (live) run();
703
+ };
704
+ channel.port2.postMessage(null);
705
+ return () => {
706
+ live = false;
707
+ channel.port1.close();
708
+ };
709
+ };
710
+ function buildStockEdges(geometry, onDone, thresholdAngle = STOCK_EDGE_ANGLE) {
711
+ const work = edgeWork(geometry, thresholdAngle);
712
+ let cancelTurn = () => {
713
+ };
714
+ let cancelled = false;
715
+ const slice = () => {
716
+ const until = performance.now() + SLICE_MS;
717
+ for (; ; ) {
718
+ const next = work.next();
719
+ if (next.done) {
720
+ onDone(next.value);
721
+ return;
722
+ }
723
+ if (performance.now() >= until) break;
724
+ }
725
+ if (!cancelled) cancelTurn = nextTurn(slice);
726
+ };
727
+ cancelTurn = nextTurn(slice);
728
+ return () => {
729
+ cancelled = true;
730
+ cancelTurn();
731
+ };
732
+ }
733
+
734
+ // src/render/stock.ts
608
735
  function boxFigure(bounds) {
609
736
  return {
610
737
  lower: { x: bounds.min.x, y: bounds.min.y, z: bounds.min.z },
@@ -612,7 +739,7 @@ function boxFigure(bounds) {
612
739
  };
613
740
  }
614
741
  var CYLINDER_SEGMENTS = 64;
615
- var CYLINDER_AXIS = new Vector3(0, 1, 0);
742
+ var CYLINDER_AXIS = new Vector32(0, 1, 0);
616
743
  function fixedBoxStockBounds(geometry, dimensions, position = "model_centered", positionOffset = 0, offset = { x: 0, y: 0, z: 0 }) {
617
744
  if (![dimensions.x, dimensions.y, dimensions.z].every(Number.isFinite) || [dimensions.x, dimensions.y, dimensions.z].some((value) => value <= 0)) {
618
745
  throw new RangeError("Fixed stock dimensions must be finite and positive.");
@@ -624,12 +751,12 @@ function fixedBoxStockBounds(geometry, dimensions, position = "model_centered",
624
751
  throw new RangeError("Fixed stock offset must be finite.");
625
752
  }
626
753
  const part = partBounds(geometry);
627
- const center = part.getCenter(new Vector3());
754
+ const center = part.getCenter(new Vector32());
628
755
  const z = fixedStockLowerZ(part, dimensions.z, position, positionOffset);
629
756
  return new Box3(
630
- new Vector3(center.x - dimensions.x / 2, center.y - dimensions.y / 2, z),
631
- new Vector3(center.x + dimensions.x / 2, center.y + dimensions.y / 2, z + dimensions.z)
632
- ).translate(new Vector3(offset.x, offset.y, offset.z));
757
+ new Vector32(center.x - dimensions.x / 2, center.y - dimensions.y / 2, z),
758
+ new Vector32(center.x + dimensions.x / 2, center.y + dimensions.y / 2, z + dimensions.z)
759
+ ).translate(new Vector32(offset.x, offset.y, offset.z));
633
760
  }
634
761
  function fixedCylinderStock(geometry, size, position = "model_centered", positionOffset = 0) {
635
762
  if (![size.diameter, size.length].every((value) => Number.isFinite(value) && value > 0)) {
@@ -639,7 +766,7 @@ function fixedCylinderStock(geometry, size, position = "model_centered", positio
639
766
  throw new RangeError("Fixed stock position offset must be finite.");
640
767
  }
641
768
  const part = partBounds(geometry);
642
- const center = part.getCenter(new Vector3());
769
+ const center = part.getCenter(new Vector32());
643
770
  return {
644
771
  origin: {
645
772
  x: center.x,
@@ -665,13 +792,13 @@ function cylinderStockGeometry(input) {
665
792
  if (![diameter, length].every((value) => Number.isFinite(value) && value > 0)) {
666
793
  throw new RangeError("Cylinder stock diameter and length must be finite and positive.");
667
794
  }
668
- const axis = new Vector3(figure.axis.x, figure.axis.y, figure.axis.z);
795
+ const axis = new Vector32(figure.axis.x, figure.axis.y, figure.axis.z);
669
796
  const norm = axis.length();
670
797
  if (!Number.isFinite(norm) || norm === 0) {
671
798
  throw new RangeError("Cylinder stock axis must be finite and non-zero.");
672
799
  }
673
800
  axis.divideScalar(norm);
674
- const center = axis.clone().multiplyScalar(length / 2).add(new Vector3(origin.x, origin.y, origin.z));
801
+ const center = axis.clone().multiplyScalar(length / 2).add(new Vector32(origin.x, origin.y, origin.z));
675
802
  return new CylinderGeometry(diameter / 2, diameter / 2, length, CYLINDER_SEGMENTS).applyQuaternion(new Quaternion().setFromUnitVectors(CYLINDER_AXIS, axis)).translate(center.x, center.y, center.z);
676
803
  }
677
804
  function boxStockGeometry(input) {
@@ -699,12 +826,12 @@ function orientedBoxStockGeometry(figure) {
699
826
  )) {
700
827
  throw new RangeError("Box stock frame and corners must be finite.");
701
828
  }
702
- const size = new Vector3(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
829
+ const size = new Vector32(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
703
830
  if (size.toArray().some((value) => !(value > 0))) {
704
831
  throw new RangeError("Box stock upper corner must be above its lower corner on every axis.");
705
832
  }
706
- const z = new Vector3(axis.x, axis.y, axis.z).normalize();
707
- const x = new Vector3(refDirection.x, refDirection.y, refDirection.z);
833
+ const z = new Vector32(axis.x, axis.y, axis.z).normalize();
834
+ const x = new Vector32(refDirection.x, refDirection.y, refDirection.z);
708
835
  const reference = x.length();
709
836
  x.addScaledVector(z, -x.dot(z));
710
837
  if (!(z.lengthSq() > 0 && x.length() > 1e-9 * reference)) {
@@ -713,11 +840,11 @@ function orientedBoxStockGeometry(figure) {
713
840
  );
714
841
  }
715
842
  x.normalize();
716
- const y = new Vector3().crossVectors(z, x);
843
+ const y = new Vector32().crossVectors(z, x);
717
844
  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));
718
845
  }
719
846
  function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 }) {
720
- const padding = typeof allowance === "number" ? new Vector3(allowance, allowance, allowance) : "wall" in allowance ? new Vector3(allowance.wall, allowance.wall, allowance.floor) : new Vector3(allowance.x, allowance.y, allowance.z);
847
+ 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);
721
848
  if (padding.toArray().some((value) => !Number.isFinite(value) || value < 0)) {
722
849
  throw new RangeError("Stock allowance must be finite and non-negative.");
723
850
  }
@@ -725,8 +852,8 @@ function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 })
725
852
  throw new RangeError("Stock offset must be finite.");
726
853
  }
727
854
  const box = partBounds(geometry);
728
- box.expandByVector(padding).translate(new Vector3(offset.x, offset.y, offset.z));
729
- const size = box.getSize(new Vector3());
855
+ box.expandByVector(padding).translate(new Vector32(offset.x, offset.y, offset.z));
856
+ const size = box.getSize(new Vector32());
730
857
  if (size.toArray().some((value) => !Number.isFinite(value) || value <= 0)) {
731
858
  throw new RangeError("Stock dimensions must be finite and positive.");
732
859
  }
@@ -737,7 +864,7 @@ function partBounds(geometry) {
737
864
  if (!position || position.count === 0)
738
865
  throw new RangeError("Stock needs non-empty part geometry.");
739
866
  const box = new Box3();
740
- const point = new Vector3();
867
+ const point = new Vector32();
741
868
  for (let index = 0; index < position.count; index += 1) {
742
869
  point.fromBufferAttribute(position, index);
743
870
  if (!Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
@@ -747,7 +874,7 @@ function partBounds(geometry) {
747
874
  }
748
875
  return box;
749
876
  }
750
- function createStock(geometry, legacyWorkpiece = false) {
877
+ function createStock(geometry, legacyWorkpiece = false, onEdges) {
751
878
  const material = new MeshLambertMaterial({ transparent: true, depthWrite: false });
752
879
  const edgeMaterial = new LineBasicMaterial({ transparent: true, depthWrite: false });
753
880
  const mesh = new Mesh2(geometry, material);
@@ -765,6 +892,18 @@ function createStock(geometry, legacyWorkpiece = false) {
765
892
  edgeMaterial.depthWrite = true;
766
893
  }
767
894
  let edges = null;
895
+ let building = null;
896
+ let shown = false;
897
+ const attach = (positions) => {
898
+ const outline = new BufferGeometry();
899
+ outline.setAttribute("position", new Float32BufferAttribute(positions, 3));
900
+ edges = new LineSegments(outline, edgeMaterial);
901
+ edges.renderOrder = legacyWorkpiece ? 1 : 6;
902
+ edges.raycast = () => {
903
+ };
904
+ edges.visible = shown;
905
+ object.add(edges);
906
+ };
768
907
  return {
769
908
  object,
770
909
  material,
@@ -772,19 +911,28 @@ function createStock(geometry, legacyWorkpiece = false) {
772
911
  /**
773
912
  * Shows or hides the outline, building it the first time it is shown: the
774
913
  * edge pass reads every triangle, and stock cut by a toolpath has a great
775
- * many of them.
914
+ * many of them. A small mesh's outline is built at once; a large one's in
915
+ * slices between frames, so the stock shows first and its outline joins it
916
+ * a moment later, the same lines `EdgesGeometry` would have drawn.
776
917
  */
777
918
  showEdges(show) {
778
- if (show && !edges) {
779
- edges = new LineSegments(new EdgesGeometry(geometry, 15), edgeMaterial);
780
- edges.renderOrder = legacyWorkpiece ? 1 : 6;
781
- edges.raycast = () => {
782
- };
783
- object.add(edges);
919
+ shown = show;
920
+ if (show && !edges && !building) {
921
+ if (triangleCount(geometry) <= SYNC_EDGE_TRIANGLES) {
922
+ attach(stockEdgePositions(geometry));
923
+ } else {
924
+ building = buildStockEdges(geometry, (positions) => {
925
+ building = null;
926
+ attach(positions);
927
+ onEdges?.();
928
+ });
929
+ }
784
930
  }
785
931
  if (edges) edges.visible = show;
786
932
  },
787
933
  dispose() {
934
+ building?.();
935
+ building = null;
788
936
  material.dispose();
789
937
  edgeMaterial.dispose();
790
938
  edges?.geometry.dispose();
@@ -808,8 +956,8 @@ var GeometryStock = ({
808
956
  }) => {
809
957
  const invalidate = useThree((state) => state.invalidate);
810
958
  const stock = useMemo(
811
- () => createStock(geometry, renderStyle === "legacy-workpiece"),
812
- [geometry, renderStyle]
959
+ () => createStock(geometry, renderStyle === "legacy-workpiece", invalidate),
960
+ [geometry, renderStyle, invalidate]
813
961
  );
814
962
  useEffect2(() => () => stock.dispose(), [stock]);
815
963
  useLayoutEffect(() => {
@@ -916,13 +1064,13 @@ import { useThree as useThree2 } from "@react-three/fiber";
916
1064
  import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useRef } from "react";
917
1065
 
918
1066
  // src/render/banana.ts
919
- import { Vector3 as Vector32 } from "three";
1067
+ import { Vector3 as Vector33 } from "three";
920
1068
  var BANANA_GAP = 0.1;
921
1069
  function bananaPosition(part, banana) {
922
- const size = part.getSize(new Vector32());
1070
+ const size = part.getSize(new Vector33());
923
1071
  const reach = Math.max(size.x, size.y, size.z);
924
- const centre = part.getCenter(new Vector32());
925
- return new Vector32(
1072
+ const centre = part.getCenter(new Vector33());
1073
+ return new Vector33(
926
1074
  part.max.x + reach * BANANA_GAP - banana.min.x,
927
1075
  centre.y - (banana.min.y + banana.max.y) / 2,
928
1076
  part.min.z - banana.min.z
@@ -992,13 +1140,13 @@ import { useThree as useThree3 } from "@react-three/fiber";
992
1140
  import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
993
1141
 
994
1142
  // src/render/grid.ts
995
- import { BufferGeometry, Float32BufferAttribute, Vector3 as Vector33 } from "three";
1143
+ import { BufferGeometry as BufferGeometry2, Float32BufferAttribute as Float32BufferAttribute2, Vector3 as Vector34 } from "three";
996
1144
  var STEPS = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1e3];
997
1145
  var TARGET_CELLS = 10;
998
1146
  var OVERHANG = 1.6;
999
1147
  function gridSpec(box) {
1000
- const size = box.getSize(new Vector33());
1001
- const center = box.getCenter(new Vector33());
1148
+ const size = box.getSize(new Vector34());
1149
+ const center = box.getCenter(new Vector34());
1002
1150
  const largest = Math.max(size.x, size.y, 1e-6);
1003
1151
  const ideal = largest / TARGET_CELLS;
1004
1152
  const step = [...STEPS].reverse().find((candidate) => candidate <= ideal) ?? STEPS[0];
@@ -1024,8 +1172,8 @@ function gridGeometry(spec) {
1024
1172
  z
1025
1173
  );
1026
1174
  }
1027
- const geometry = new BufferGeometry();
1028
- geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
1175
+ const geometry = new BufferGeometry2();
1176
+ geometry.setAttribute("position", new Float32BufferAttribute2(positions, 3));
1029
1177
  return geometry;
1030
1178
  }
1031
1179
  function gridFor(box, options = {}) {
@@ -1140,17 +1288,17 @@ var CubePanels = ({
1140
1288
  // src/direction-arrows.tsx
1141
1289
  import { useThree as useThree4 } from "@react-three/fiber";
1142
1290
  import { useEffect as useEffect4, useMemo as useMemo4 } from "react";
1143
- import { Quaternion as Quaternion2, Vector3 as Vector35 } from "three";
1291
+ import { Quaternion as Quaternion2, Vector3 as Vector36 } from "three";
1144
1292
 
1145
1293
  // src/render/directions.ts
1146
- import { Vector3 as Vector34 } from "three";
1294
+ import { Vector3 as Vector35 } from "three";
1147
1295
  var LENGTH = 0.33;
1148
1296
  var GAP = 0.2;
1149
1297
  function arrowPlacement(direction, box) {
1150
- const center = box.getCenter(new Vector34());
1151
- const half = box.getSize(new Vector34()).multiplyScalar(0.5);
1298
+ const center = box.getCenter(new Vector35());
1299
+ const half = box.getSize(new Vector35()).multiplyScalar(0.5);
1152
1300
  const radius = half.length() || 1;
1153
- const axis = new Vector34(direction.x, direction.y, direction.z);
1301
+ const axis = new Vector35(direction.x, direction.y, direction.z);
1154
1302
  if (axis.lengthSq() === 0) axis.set(0, 0, 1);
1155
1303
  axis.normalize();
1156
1304
  let exit = Number.POSITIVE_INFINITY;
@@ -1241,7 +1389,7 @@ var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }
1241
1389
  const placement = arrowPlacement(direction, box);
1242
1390
  const aim = new Quaternion2().setFromUnitVectors(
1243
1391
  ARROW_AXIS,
1244
- new Vector35(direction.x, direction.y, direction.z).normalize()
1392
+ new Vector36(direction.x, direction.y, direction.z).normalize()
1245
1393
  );
1246
1394
  return { tip: placement.tip, length: placement.length, quaternion: aim };
1247
1395
  }, [box, direction]);
@@ -1259,16 +1407,16 @@ var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }
1259
1407
  import { useFrame, useThree as useThree5 } from "@react-three/fiber";
1260
1408
  import { useEffect as useEffect5, useMemo as useMemo5, useRef as useRef3, useState as useState4, useSyncExternalStore } from "react";
1261
1409
  import {
1262
- BufferGeometry as BufferGeometry2,
1410
+ BufferGeometry as BufferGeometry3,
1263
1411
  DoubleSide,
1264
1412
  Quaternion as Quaternion3,
1265
1413
  Raycaster,
1266
1414
  Vector2,
1267
- Vector3 as Vector36
1415
+ Vector3 as Vector37
1268
1416
  } from "three";
1269
1417
  import { Fragment, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1270
1418
  var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
1271
- var Z = new Vector36(0, 0, 1);
1419
+ var Z = new Vector37(0, 0, 1);
1272
1420
  var AXIS_ROTATIONS = {
1273
1421
  x: [0, Math.PI / 2, 0],
1274
1422
  y: [Math.PI / 2, 0, 0],
@@ -1298,11 +1446,11 @@ var SectionTool = ({ theme }) => {
1298
1446
  };
1299
1447
  var useSquare = () => {
1300
1448
  const square = useMemo5(
1301
- () => new BufferGeometry2().setFromPoints([
1302
- new Vector36(-1, 1, 0),
1303
- new Vector36(1, 1, 0),
1304
- new Vector36(1, -1, 0),
1305
- new Vector36(-1, -1, 0)
1449
+ () => new BufferGeometry3().setFromPoints([
1450
+ new Vector37(-1, 1, 0),
1451
+ new Vector37(1, 1, 0),
1452
+ new Vector37(1, -1, 0),
1453
+ new Vector37(-1, -1, 0)
1306
1454
  ]),
1307
1455
  []
1308
1456
  );
@@ -1320,8 +1468,8 @@ var Picker = ({ box, theme }) => {
1320
1468
  const previewRef = useRef3(null);
1321
1469
  const hover = useRef3(null);
1322
1470
  const [hoveredPlane, setHoveredPlane] = useState4(null);
1323
- const span = useMemo5(() => box.getSize(new Vector36()).length(), [box]);
1324
- const centre = useMemo5(() => box.getCenter(new Vector36()), [box]);
1471
+ const span = useMemo5(() => box.getSize(new Vector37()).length(), [box]);
1472
+ const centre = useMemo5(() => box.getCenter(new Vector37()), [box]);
1325
1473
  const previewSize = span * PREVIEW_SCALE;
1326
1474
  useEffect5(() => {
1327
1475
  store.setPicking(true);
@@ -1330,7 +1478,7 @@ var Picker = ({ box, theme }) => {
1330
1478
  useEffect5(() => {
1331
1479
  const raycaster = new Raycaster();
1332
1480
  const pointer = new Vector2();
1333
- const aim = new Vector36();
1481
+ const aim = new Vector37();
1334
1482
  const hide = () => {
1335
1483
  if (!hover.current) return;
1336
1484
  hover.current = null;
@@ -1474,7 +1622,7 @@ var Picker = ({ box, theme }) => {
1474
1622
  var CutSheet = ({ box, section, theme }) => {
1475
1623
  const square = useSquare();
1476
1624
  const cut = useMemo5(() => resolveSectionPlane(section, box), [box, section]);
1477
- const span = useMemo5(() => box.getSize(new Vector36()).length(), [box]);
1625
+ const span = useMemo5(() => box.getSize(new Vector37()).length(), [box]);
1478
1626
  const placement = useMemo5(() => {
1479
1627
  if (!cut) return null;
1480
1628
  const position = sectionAnchor(box, cut.plane).addScaledVector(
@@ -1537,18 +1685,18 @@ import {
1537
1685
  Raycaster as Raycaster2,
1538
1686
  SphereGeometry,
1539
1687
  Vector2 as Vector22,
1540
- Vector3 as Vector38
1688
+ Vector3 as Vector39
1541
1689
  } from "three";
1542
1690
 
1543
1691
  // src/render/resample.ts
1544
- import { Vector3 as Vector37 } from "three";
1692
+ import { Vector3 as Vector38 } from "three";
1545
1693
  var ON_PLANE = 1e-7;
1546
1694
  var STITCH = 1e-5;
1547
1695
  var COLLINEAR = 1e-6;
1548
1696
  function clipSegments(positions, plane) {
1549
1697
  const out = [];
1550
- const a = new Vector37();
1551
- const b = new Vector37();
1698
+ const a = new Vector38();
1699
+ const b = new Vector38();
1552
1700
  const count = Math.floor(positions.length / 6);
1553
1701
  for (let i = 0; i < count; i += 1) {
1554
1702
  a.set(positions[i * 6], positions[i * 6 + 1], positions[i * 6 + 2]);
@@ -1563,14 +1711,14 @@ function clipSegments(positions, plane) {
1563
1711
  return Float32Array.from(out);
1564
1712
  }
1565
1713
  function sectionContour(positions, regionOf, plane) {
1566
- const a = new Vector37();
1567
- const b = new Vector37();
1568
- const c = new Vector37();
1569
- const p = new Vector37();
1570
- const q = new Vector37();
1714
+ const a = new Vector38();
1715
+ const b = new Vector38();
1716
+ const c = new Vector38();
1717
+ const p = new Vector38();
1718
+ const q = new Vector38();
1571
1719
  const byRegion = /* @__PURE__ */ new Map();
1572
- const triangleCount = Math.floor(positions.length / 9);
1573
- for (let t = 0; t < triangleCount; t += 1) {
1720
+ const triangleCount2 = Math.floor(positions.length / 9);
1721
+ for (let t = 0; t < triangleCount2; t += 1) {
1574
1722
  a.set(positions[t * 9], positions[t * 9 + 1], positions[t * 9 + 2]);
1575
1723
  b.set(positions[t * 9 + 3], positions[t * 9 + 4], positions[t * 9 + 5]);
1576
1724
  c.set(positions[t * 9 + 6], positions[t * 9 + 7], positions[t * 9 + 8]);
@@ -1625,17 +1773,17 @@ function stitch(segments, out) {
1625
1773
  return into.set(segments[o], segments[o + 1], segments[o + 2]);
1626
1774
  };
1627
1775
  const direction = (i, into) => {
1628
- const s = new Vector37();
1629
- const e = new Vector37();
1776
+ const s = new Vector38();
1777
+ const e = new Vector38();
1630
1778
  point(i, 0, s);
1631
1779
  point(i, 1, e);
1632
1780
  return into.subVectors(e, s).normalize();
1633
1781
  };
1634
1782
  const consumed = new Uint8Array(count);
1635
- const d = new Vector37();
1636
- const n = new Vector37();
1783
+ const d = new Vector38();
1784
+ const n = new Vector38();
1637
1785
  const next = (i, end) => {
1638
- const at = point(i, end, new Vector37());
1786
+ const at = point(i, end, new Vector38());
1639
1787
  const list = ends.get(key(at.x, at.y, at.z)) ?? [];
1640
1788
  if (list.length !== 2) return null;
1641
1789
  const other = list.find((entry) => entry >> 1 !== i);
@@ -1664,8 +1812,8 @@ function stitch(segments, out) {
1664
1812
  };
1665
1813
  const back = walk(0);
1666
1814
  const forward = walk(1);
1667
- const s = point(back.index, back.end, new Vector37());
1668
- const e = point(forward.index, forward.end, new Vector37());
1815
+ const s = point(back.index, back.end, new Vector38());
1816
+ const e = point(forward.index, forward.end, new Vector38());
1669
1817
  out.push(s.x, s.y, s.z, e.x, e.y, e.z);
1670
1818
  }
1671
1819
  }
@@ -1673,8 +1821,8 @@ function pointInContour(point, plane, contour) {
1673
1821
  const [u, v] = planeAxes(plane);
1674
1822
  const px = point.dot(u);
1675
1823
  const py = point.dot(v);
1676
- const a = new Vector37();
1677
- const b = new Vector37();
1824
+ const a = new Vector38();
1825
+ const b = new Vector38();
1678
1826
  let inside = false;
1679
1827
  const count = Math.floor(contour.length / 6);
1680
1828
  for (let i = 0; i < count; i += 1) {
@@ -1691,15 +1839,15 @@ function pointInContour(point, plane, contour) {
1691
1839
  return inside;
1692
1840
  }
1693
1841
  function capHit(ray, plane, contour) {
1694
- const point = ray.intersectPlane(plane, new Vector37());
1842
+ const point = ray.intersectPlane(plane, new Vector38());
1695
1843
  if (!point) return null;
1696
1844
  return pointInContour(point, plane, contour) ? point : null;
1697
1845
  }
1698
1846
  function planeAxes(plane) {
1699
1847
  const n = plane.normal;
1700
- const seed = Math.abs(n.x) < 0.9 ? new Vector37(1, 0, 0) : new Vector37(0, 1, 0);
1701
- const u = new Vector37().crossVectors(n, seed).normalize();
1702
- const v = new Vector37().crossVectors(n, u).normalize();
1848
+ const seed = Math.abs(n.x) < 0.9 ? new Vector38(1, 0, 0) : new Vector38(0, 1, 0);
1849
+ const u = new Vector38().crossVectors(n, seed).normalize();
1850
+ const v = new Vector38().crossVectors(n, u).normalize();
1703
1851
  return [u, v];
1704
1852
  }
1705
1853
  function sampleSection(meshPositions, regionOf, edgePositions, plane) {
@@ -1809,7 +1957,7 @@ var MeasureTool = ({
1809
1957
  ] });
1810
1958
  };
1811
1959
  var NO_POINTER = { pointerEvents: "none" };
1812
- var NOWHERE = [new Vector38(), new Vector38()];
1960
+ var NOWHERE = [new Vector39(), new Vector39()];
1813
1961
  var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
1814
1962
  const camera = useThree6((state) => state.camera);
1815
1963
  const scene = useThree6((state) => state.scene);
@@ -1862,7 +2010,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
1862
2010
  const snapped = snapAt(target, target.edges, camera, viewport);
1863
2011
  const from = placed[placed.length - 1];
1864
2012
  const lock = shift && from ? lockToAxis(from, snapped.point) : null;
1865
- const found = lock ? { ...snapped, point: new Vector38(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
2013
+ const found = lock ? { ...snapped, point: new Vector39(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
1866
2014
  snap.current = found;
1867
2015
  const node = marker.current;
1868
2016
  if (node) {
@@ -2170,7 +2318,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
2170
2318
  const lines = useRef4([]);
2171
2319
  const centre = useMemo6(() => {
2172
2320
  const at = midpoint(a, b);
2173
- return new Vector38(at.x, at.y, at.z);
2321
+ return new Vector39(at.x, at.y, at.z);
2174
2322
  }, [a, b]);
2175
2323
  useFrame2(() => {
2176
2324
  const dash = screenLength(camera, centre, size, DELTA_DASH_PIXELS);
@@ -2192,8 +2340,8 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
2192
2340
  lines.current[index] = line;
2193
2341
  },
2194
2342
  points: [
2195
- new Vector38(leg.from.x, leg.from.y, leg.from.z),
2196
- new Vector38(leg.to.x, leg.to.y, leg.to.z)
2343
+ new Vector39(leg.from.x, leg.from.y, leg.from.z),
2344
+ new Vector39(leg.to.x, leg.to.y, leg.to.z)
2197
2345
  ],
2198
2346
  color: AXIS_COLORS[leg.axis],
2199
2347
  lineWidth: MEASURE_LINE_PIXELS.leg,
@@ -2229,7 +2377,7 @@ var ScreenDot = ({ point, pixels, color }) => {
2229
2377
  const invalidate = useThree6((state) => state.invalidate);
2230
2378
  const node = useRef4(null);
2231
2379
  const dot = useDot();
2232
- const position = useMemo6(() => new Vector38(point.x, point.y, point.z), [point]);
2380
+ const position = useMemo6(() => new Vector39(point.x, point.y, point.z), [point]);
2233
2381
  useFrame2(() => {
2234
2382
  const group = node.current;
2235
2383
  if (!group) return;
@@ -2244,7 +2392,7 @@ var ScreenDot = ({ point, pixels, color }) => {
2244
2392
  var Segment = ({ points, color }) => {
2245
2393
  const [a, b] = points;
2246
2394
  const ends = useMemo6(
2247
- () => [new Vector38(a.x, a.y, a.z), new Vector38(b.x, b.y, b.z)],
2395
+ () => [new Vector39(a.x, a.y, a.z), new Vector39(b.x, b.y, b.z)],
2248
2396
  // Keyed on the numbers: a draft's tuple is a fresh array on every render,
2249
2397
  // and a rebuilt array would rebuild the line's geometry with it.
2250
2398
  [a.x, a.y, a.z, b.x, b.y, b.z]
@@ -2286,7 +2434,7 @@ import { useMemo as useMemo7 } from "react";
2286
2434
  import { DoubleSide as DoubleSide2 } from "three";
2287
2435
 
2288
2436
  // src/render/tool-marks.ts
2289
- import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as Vector39 } from "three";
2437
+ import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as Vector310 } from "three";
2290
2438
  var TOOL_CORNER_STEPS = 12;
2291
2439
  var TOOL_ROUND = 64;
2292
2440
  function toolProfile(radius, corner, height) {
@@ -2306,9 +2454,9 @@ function toolProfile(radius, corner, height) {
2306
2454
  points.push(new Vector23(radius, Math.max(height, rounded)));
2307
2455
  return points;
2308
2456
  }
2309
- var UP = new Vector39(0, 1, 0);
2310
- var AXES = [new Vector39(1, 0, 0), new Vector39(0, 1, 0), new Vector39(0, 0, 1)];
2311
- var toVector = ({ x, y, z }) => new Vector39(x, y, z);
2457
+ var UP = new Vector310(0, 1, 0);
2458
+ var AXES = [new Vector310(1, 0, 0), new Vector310(0, 1, 0), new Vector310(0, 0, 1)];
2459
+ var toVector = ({ x, y, z }) => new Vector310(x, y, z);
2312
2460
  var squareTo = (w, along) => {
2313
2461
  const wanted = along ? toVector(along) : null;
2314
2462
  const flat = wanted?.clone().addScaledVector(w, -wanted.dot(w));
@@ -2328,7 +2476,7 @@ function toolMarkShape(mark) {
2328
2476
  if (!(mark.diameter > 0) || axis.lengthSq() === 0) return null;
2329
2477
  const w = axis.normalize();
2330
2478
  const u = squareTo(w, mark.across);
2331
- const v = new Vector39().crossVectors(w, u);
2479
+ const v = new Vector310().crossVectors(w, u);
2332
2480
  const radius = mark.diameter / 2;
2333
2481
  const corner = Math.min(Math.max(mark.cornerRadius ?? 0, 0), radius);
2334
2482
  const height = Math.max(mark.height, corner);