@toolpath/viewer 2.3.0 → 2.3.1
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 +241 -93
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
754
|
+
const center = part.getCenter(new Vector32());
|
|
628
755
|
const z = fixedStockLowerZ(part, dimensions.z, position, positionOffset);
|
|
629
756
|
return new Box3(
|
|
630
|
-
new
|
|
631
|
-
new
|
|
632
|
-
).translate(new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
707
|
-
const x = new
|
|
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
|
|
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
|
|
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
|
|
729
|
-
const size = box.getSize(new
|
|
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
|
|
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
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
}
|
|
783
|
-
|
|
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
|
|
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
|
|
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
|
|
925
|
-
return new
|
|
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
|
|
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
|
|
1001
|
-
const center = box.getCenter(new
|
|
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
|
|
1028
|
-
geometry.setAttribute("position", new
|
|
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
|
|
1291
|
+
import { Quaternion as Quaternion2, Vector3 as Vector36 } from "three";
|
|
1144
1292
|
|
|
1145
1293
|
// src/render/directions.ts
|
|
1146
|
-
import { Vector3 as
|
|
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
|
|
1151
|
-
const half = box.getSize(new
|
|
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
|
|
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
|
|
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
|
|
1410
|
+
BufferGeometry as BufferGeometry3,
|
|
1263
1411
|
DoubleSide,
|
|
1264
1412
|
Quaternion as Quaternion3,
|
|
1265
1413
|
Raycaster,
|
|
1266
1414
|
Vector2,
|
|
1267
|
-
Vector3 as
|
|
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
|
|
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
|
|
1302
|
-
new
|
|
1303
|
-
new
|
|
1304
|
-
new
|
|
1305
|
-
new
|
|
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
|
|
1324
|
-
const centre = useMemo5(() => box.getCenter(new
|
|
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
|
|
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
|
|
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
|
|
1688
|
+
Vector3 as Vector39
|
|
1541
1689
|
} from "three";
|
|
1542
1690
|
|
|
1543
1691
|
// src/render/resample.ts
|
|
1544
|
-
import { Vector3 as
|
|
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
|
|
1551
|
-
const b = new
|
|
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
|
|
1567
|
-
const b = new
|
|
1568
|
-
const c = new
|
|
1569
|
-
const p = new
|
|
1570
|
-
const q = new
|
|
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
|
|
1573
|
-
for (let t = 0; t <
|
|
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
|
|
1629
|
-
const e = new
|
|
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
|
|
1636
|
-
const n = new
|
|
1783
|
+
const d = new Vector38();
|
|
1784
|
+
const n = new Vector38();
|
|
1637
1785
|
const next = (i, end) => {
|
|
1638
|
-
const at = point(i, end, new
|
|
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
|
|
1668
|
-
const e = point(forward.index, forward.end, new
|
|
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
|
|
1677
|
-
const b = new
|
|
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
|
|
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
|
|
1701
|
-
const u = new
|
|
1702
|
-
const v = new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2196
|
-
new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2310
|
-
var AXES = [new
|
|
2311
|
-
var toVector = ({ x, y, z }) => new
|
|
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
|
|
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);
|