@toolpath/viewer 2.3.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +801 -1169
- package/dist/{chunk-N54FHMTG.js → chunk-LBG6GVGE.js} +405 -86
- package/dist/engine/index.d.ts +14 -3
- package/dist/engine/index.js +5 -3
- package/dist/index.d.ts +12 -2
- package/dist/index.js +13 -1
- package/dist/{normalize-C4Wj4IGK.d.ts → normalize-BBADLCmd.d.ts} +47 -2
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/engine/engine-part.tsx
|
|
2
|
-
import { useEffect as useEffect8
|
|
2
|
+
import { useEffect as useEffect8 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/part-mesh.tsx
|
|
5
5
|
import { useThree as useThree8 } from "@react-three/fiber";
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
useLayoutEffect as useLayoutEffect3,
|
|
10
10
|
useMemo as useMemo7,
|
|
11
11
|
useRef as useRef6,
|
|
12
|
-
useSyncExternalStore
|
|
12
|
+
useSyncExternalStore as useSyncExternalStore2
|
|
13
13
|
} from "react";
|
|
14
|
-
import { Vector3 as
|
|
14
|
+
import { Vector3 as Vector313 } from "three";
|
|
15
15
|
|
|
16
16
|
// src/part-context.tsx
|
|
17
17
|
import { createContext, useContext } from "react";
|
|
@@ -863,6 +863,7 @@ import {
|
|
|
863
863
|
DataTexture,
|
|
864
864
|
DoubleSide as DoubleSide2,
|
|
865
865
|
Float32BufferAttribute as Float32BufferAttribute3,
|
|
866
|
+
FloatType,
|
|
866
867
|
Group as Group2,
|
|
867
868
|
LineBasicMaterial,
|
|
868
869
|
LineSegments,
|
|
@@ -871,7 +872,7 @@ import {
|
|
|
871
872
|
RedFormat,
|
|
872
873
|
RGBAFormat,
|
|
873
874
|
UnsignedByteType,
|
|
874
|
-
Vector3 as
|
|
875
|
+
Vector3 as Vector36
|
|
875
876
|
} from "three";
|
|
876
877
|
|
|
877
878
|
// src/render/edges.ts
|
|
@@ -1488,9 +1489,147 @@ function emptyCreases() {
|
|
|
1488
1489
|
};
|
|
1489
1490
|
}
|
|
1490
1491
|
|
|
1492
|
+
// src/render/threads.ts
|
|
1493
|
+
import {
|
|
1494
|
+
Vector3 as Vector35
|
|
1495
|
+
} from "three";
|
|
1496
|
+
var THREAD_TEXTURE_ROWS = 2;
|
|
1497
|
+
var MAX_AXIAL_NORMAL = 0.2;
|
|
1498
|
+
var MIN_AXIS_CROSS = 0.1;
|
|
1499
|
+
var DIAMETER_SLACK = 1.1;
|
|
1500
|
+
function facetsOf(position, ranges) {
|
|
1501
|
+
const facets = [];
|
|
1502
|
+
const a = new Vector35();
|
|
1503
|
+
const b = new Vector35();
|
|
1504
|
+
const c = new Vector35();
|
|
1505
|
+
for (const range of ranges) {
|
|
1506
|
+
for (let triangle = range.start; triangle < range.end; triangle += 1) {
|
|
1507
|
+
const first = triangle * 3;
|
|
1508
|
+
if (first + 2 >= position.count) break;
|
|
1509
|
+
a.fromBufferAttribute(position, first);
|
|
1510
|
+
b.fromBufferAttribute(position, first + 1);
|
|
1511
|
+
c.fromBufferAttribute(position, first + 2);
|
|
1512
|
+
const normal = new Vector35().subVectors(b, a).cross(new Vector35().subVectors(c, a));
|
|
1513
|
+
const doubleArea = normal.length();
|
|
1514
|
+
if (doubleArea === 0) continue;
|
|
1515
|
+
facets.push({
|
|
1516
|
+
centre: new Vector35().add(a).add(b).add(c).divideScalar(3),
|
|
1517
|
+
normal: normal.divideScalar(doubleArea),
|
|
1518
|
+
area: doubleArea / 2
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return facets;
|
|
1523
|
+
}
|
|
1524
|
+
function axisOf(facets) {
|
|
1525
|
+
const first = facets[0];
|
|
1526
|
+
if (!first) return null;
|
|
1527
|
+
let furthest = first;
|
|
1528
|
+
for (const facet of facets) {
|
|
1529
|
+
if (Math.abs(facet.normal.dot(first.normal)) < Math.abs(furthest.normal.dot(first.normal))) {
|
|
1530
|
+
furthest = facet;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
const axis = new Vector35().crossVectors(first.normal, furthest.normal);
|
|
1534
|
+
if (axis.length() < MIN_AXIS_CROSS) return null;
|
|
1535
|
+
axis.normalize();
|
|
1536
|
+
const allSquare = facets.every((facet) => Math.abs(facet.normal.dot(axis)) <= MAX_AXIAL_NORMAL);
|
|
1537
|
+
return allSquare ? axis : null;
|
|
1538
|
+
}
|
|
1539
|
+
function fitBore(position, ranges) {
|
|
1540
|
+
const facets = facetsOf(position, ranges);
|
|
1541
|
+
const axis = axisOf(facets);
|
|
1542
|
+
if (!axis) return null;
|
|
1543
|
+
const u = new Vector35().crossVectors(axis, Math.abs(axis.x) < 0.9 ? new Vector35(1, 0, 0) : new Vector35(0, 1, 0)).normalize();
|
|
1544
|
+
const v = new Vector35().crossVectors(axis, u);
|
|
1545
|
+
let xx = 0;
|
|
1546
|
+
let xy = 0;
|
|
1547
|
+
let yy = 0;
|
|
1548
|
+
let rx = 0;
|
|
1549
|
+
let ry = 0;
|
|
1550
|
+
let along = 0;
|
|
1551
|
+
let total = 0;
|
|
1552
|
+
for (const { centre, normal, area } of facets) {
|
|
1553
|
+
const mx = normal.dot(u);
|
|
1554
|
+
const my = normal.dot(v);
|
|
1555
|
+
const length = Math.hypot(mx, my);
|
|
1556
|
+
const nx = mx / length;
|
|
1557
|
+
const ny = my / length;
|
|
1558
|
+
const qx = centre.dot(u);
|
|
1559
|
+
const qy = centre.dot(v);
|
|
1560
|
+
const pxx = 1 - nx * nx;
|
|
1561
|
+
const pxy = -nx * ny;
|
|
1562
|
+
const pyy = 1 - ny * ny;
|
|
1563
|
+
xx += area * pxx;
|
|
1564
|
+
xy += area * pxy;
|
|
1565
|
+
yy += area * pyy;
|
|
1566
|
+
rx += area * (pxx * qx + pxy * qy);
|
|
1567
|
+
ry += area * (pxy * qx + pyy * qy);
|
|
1568
|
+
along += area * centre.dot(axis);
|
|
1569
|
+
total += area;
|
|
1570
|
+
}
|
|
1571
|
+
const determinant = xx * yy - xy * xy;
|
|
1572
|
+
if (Math.abs(determinant) < 1e-9 * total * total) return null;
|
|
1573
|
+
const cx = (yy * rx - xy * ry) / determinant;
|
|
1574
|
+
const cy = (xx * ry - xy * rx) / determinant;
|
|
1575
|
+
let radius = 0;
|
|
1576
|
+
for (const { centre, area } of facets) {
|
|
1577
|
+
radius += area * Math.hypot(centre.dot(u) - cx, centre.dot(v) - cy);
|
|
1578
|
+
}
|
|
1579
|
+
return {
|
|
1580
|
+
origin: new Vector35().addScaledVector(u, cx).addScaledVector(v, cy).addScaledVector(axis, along / total),
|
|
1581
|
+
axis,
|
|
1582
|
+
radius: radius / total
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
function threadedRanges(position, model, mark) {
|
|
1586
|
+
const regions = [];
|
|
1587
|
+
const ranges = [];
|
|
1588
|
+
for (const region of mark.regions) {
|
|
1589
|
+
const range = model.regionIndex.rangeForRegion(region);
|
|
1590
|
+
if (!range) continue;
|
|
1591
|
+
const bore = fitBore(position, [range]);
|
|
1592
|
+
if (!bore || bore.radius * 2 > mark.majorDiameter * DIAMETER_SLACK) continue;
|
|
1593
|
+
regions.push(region);
|
|
1594
|
+
ranges.push(range);
|
|
1595
|
+
}
|
|
1596
|
+
return { regions, ranges };
|
|
1597
|
+
}
|
|
1598
|
+
function writeThreadTexels(data, width, geometry, model, texels, options) {
|
|
1599
|
+
data.fill(0);
|
|
1600
|
+
if (!options) return;
|
|
1601
|
+
const position = geometry.getAttribute("position");
|
|
1602
|
+
for (const mark of options.threads) {
|
|
1603
|
+
if (!Number.isFinite(mark.pitch) || mark.pitch === 0) continue;
|
|
1604
|
+
const { regions, ranges } = threadedRanges(position, model, mark);
|
|
1605
|
+
const bore = fitBore(position, ranges);
|
|
1606
|
+
if (!bore) continue;
|
|
1607
|
+
for (const region of regions) {
|
|
1608
|
+
const column = texels.get(region);
|
|
1609
|
+
if (column === void 0) continue;
|
|
1610
|
+
const first = column * 4;
|
|
1611
|
+
const second = (width + column) * 4;
|
|
1612
|
+
data[first] = bore.origin.x;
|
|
1613
|
+
data[first + 1] = bore.origin.y;
|
|
1614
|
+
data[first + 2] = bore.origin.z;
|
|
1615
|
+
data[first + 3] = mark.pitch;
|
|
1616
|
+
data[second] = bore.axis.x;
|
|
1617
|
+
data[second + 1] = bore.axis.y;
|
|
1618
|
+
data[second + 2] = bore.axis.z;
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
function threadKey(options) {
|
|
1623
|
+
if (!options) return "";
|
|
1624
|
+
return options.threads.map((mark) => `${mark.regions.join(",")}:${mark.pitch}:${mark.majorDiameter}`).join("|");
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1491
1627
|
// src/render/part.ts
|
|
1492
1628
|
var REGION_ATTRIBUTE = "aRegion";
|
|
1493
1629
|
var EMISSIVE_MIX = 0.4;
|
|
1630
|
+
var THREAD_TILT = Math.tan(Math.PI / 4);
|
|
1631
|
+
var THREAD_ROOT_SHADE = 0.45;
|
|
1632
|
+
var THREAD_CREST_LIGHT = 1.15;
|
|
1494
1633
|
function buildRegionTexels(model) {
|
|
1495
1634
|
return new Map(model.regions.map((region, column) => [region.idx, column]));
|
|
1496
1635
|
}
|
|
@@ -1522,6 +1661,10 @@ function createPart(model, geometry, theme) {
|
|
|
1522
1661
|
const opacity = new Uint8Array(width).fill(255);
|
|
1523
1662
|
const opacityTexture = new DataTexture(opacity, width, 1, RedFormat, UnsignedByteType);
|
|
1524
1663
|
opacityTexture.needsUpdate = true;
|
|
1664
|
+
const threads = new Float32Array(width * THREAD_TEXTURE_ROWS * 4);
|
|
1665
|
+
const threadTexture = new DataTexture(threads, width, THREAD_TEXTURE_ROWS, RGBAFormat, FloatType);
|
|
1666
|
+
threadTexture.needsUpdate = true;
|
|
1667
|
+
let threadsKey = "";
|
|
1525
1668
|
const focusState = { value: false };
|
|
1526
1669
|
const wireframe = { value: false };
|
|
1527
1670
|
let focused = false;
|
|
@@ -1547,15 +1690,26 @@ function createPart(model, geometry, theme) {
|
|
|
1547
1690
|
shader.uniforms["uWireframe"] = wireframe;
|
|
1548
1691
|
shader.uniforms["uFocus"] = focusState;
|
|
1549
1692
|
shader.uniforms["uOpaqueFocusPass"] = { value: opaqueFocusPass };
|
|
1693
|
+
shader.uniforms["uThreads"] = { value: threadTexture };
|
|
1550
1694
|
shader.vertexShader = shader.vertexShader.replace(
|
|
1551
1695
|
"#include <common>",
|
|
1552
1696
|
`#include <common>
|
|
1553
1697
|
attribute float ${REGION_ATTRIBUTE};
|
|
1554
|
-
|
|
1698
|
+
uniform sampler2D uThreads;
|
|
1699
|
+
varying float vRegion;
|
|
1700
|
+
varying vec3 vThreadPosition;
|
|
1701
|
+
varying vec4 vThreadOrigin;
|
|
1702
|
+
varying vec3 vThreadAxis;
|
|
1703
|
+
varying vec3 vThreadAxisView;`
|
|
1555
1704
|
).replace(
|
|
1556
1705
|
"#include <begin_vertex>",
|
|
1557
1706
|
`#include <begin_vertex>
|
|
1558
|
-
vRegion = ${REGION_ATTRIBUTE}
|
|
1707
|
+
vRegion = ${REGION_ATTRIBUTE};
|
|
1708
|
+
// Every vertex of a triangle is in one region, so these are flat across it.
|
|
1709
|
+
vThreadOrigin = texelFetch(uThreads, ivec2(int(vRegion + 0.5), 0), 0);
|
|
1710
|
+
vThreadAxis = texelFetch(uThreads, ivec2(int(vRegion + 0.5), 1), 0).xyz;
|
|
1711
|
+
vThreadAxisView = normalMatrix * vThreadAxis;
|
|
1712
|
+
vThreadPosition = position;`
|
|
1559
1713
|
);
|
|
1560
1714
|
shader.fragmentShader = shader.fragmentShader.replace(
|
|
1561
1715
|
"#include <common>",
|
|
@@ -1566,6 +1720,10 @@ function createPart(model, geometry, theme) {
|
|
|
1566
1720
|
uniform bool uFocus;
|
|
1567
1721
|
uniform bool uOpaqueFocusPass;
|
|
1568
1722
|
varying float vRegion;
|
|
1723
|
+
varying vec3 vThreadPosition;
|
|
1724
|
+
varying vec4 vThreadOrigin;
|
|
1725
|
+
varying vec3 vThreadAxis;
|
|
1726
|
+
varying vec3 vThreadAxisView;
|
|
1569
1727
|
vec4 regionState;`
|
|
1570
1728
|
).replace(
|
|
1571
1729
|
"#include <color_fragment>",
|
|
@@ -1578,6 +1736,46 @@ function createPart(model, geometry, theme) {
|
|
|
1578
1736
|
diffuseColor.a *= regionOpacity;
|
|
1579
1737
|
if (uWireframe) diffuseColor.a *= regionState.a;
|
|
1580
1738
|
`
|
|
1739
|
+
).replace(
|
|
1740
|
+
"#include <normal_fragment_maps>",
|
|
1741
|
+
`#include <normal_fragment_maps>
|
|
1742
|
+
// Worked out for every pixel, threaded or not, and applied only where
|
|
1743
|
+
// there is a thread: derivatives are undefined inside a branch that
|
|
1744
|
+
// differs between neighbouring pixels, as it does along a region's edge.
|
|
1745
|
+
bool threaded = vThreadOrigin.w != 0.0;
|
|
1746
|
+
float threadPitch = threaded ? vThreadOrigin.w : 1.0;
|
|
1747
|
+
vec3 threadAxis = threaded ? normalize(vThreadAxis) : vec3(0.0, 0.0, 1.0);
|
|
1748
|
+
vec3 threadOffset = vThreadPosition - vThreadOrigin.xyz;
|
|
1749
|
+
float threadAlong = dot(threadOffset, threadAxis);
|
|
1750
|
+
vec3 threadU = normalize(cross(
|
|
1751
|
+
threadAxis,
|
|
1752
|
+
abs(threadAxis.x) < 0.9 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0)
|
|
1753
|
+
));
|
|
1754
|
+
vec3 threadV = cross(threadAxis, threadU);
|
|
1755
|
+
float threadX = dot(threadOffset, threadU);
|
|
1756
|
+
float threadY = dot(threadOffset, threadV);
|
|
1757
|
+
float threadTurn = atan(threadY, threadX) / PI2;
|
|
1758
|
+
float threadPhase = threadAlong / threadPitch - threadTurn;
|
|
1759
|
+
// Two angle seams on opposite sides, so the blur estimate has none.
|
|
1760
|
+
float threadBlur = fwidth(threadAlong / threadPitch) + min(
|
|
1761
|
+
fwidth(threadTurn),
|
|
1762
|
+
fwidth(atan(-threadY, -threadX) / PI2)
|
|
1763
|
+
);
|
|
1764
|
+
if (threaded) {
|
|
1765
|
+
float threadStrength = 1.0 - smoothstep(0.2, 0.45, threadBlur);
|
|
1766
|
+
// The V of the thread: 1 at its root (phase 0), 0 at its crest (phase \xBD).
|
|
1767
|
+
float threadDepth = abs(fract(threadPhase) - 0.5) * 2.0;
|
|
1768
|
+
float threadSlope = clamp(sin(PI2 * threadPhase) * 3.0, -1.0, 1.0) * sign(threadPitch);
|
|
1769
|
+
normal = normalize(
|
|
1770
|
+
normal - ${THREAD_TILT.toFixed(4)} * threadStrength * threadSlope * normalize(vThreadAxisView)
|
|
1771
|
+
);
|
|
1772
|
+
float threadShade = mix(
|
|
1773
|
+
${THREAD_CREST_LIGHT.toFixed(2)},
|
|
1774
|
+
${THREAD_ROOT_SHADE.toFixed(2)},
|
|
1775
|
+
smoothstep(0.0, 1.0, threadDepth)
|
|
1776
|
+
);
|
|
1777
|
+
diffuseColor.rgb *= mix(1.0, threadShade, threadStrength);
|
|
1778
|
+
}`
|
|
1581
1779
|
).replace(
|
|
1582
1780
|
"#include <emissivemap_fragment>",
|
|
1583
1781
|
`#include <emissivemap_fragment>
|
|
@@ -1629,7 +1827,7 @@ function createPart(model, geometry, theme) {
|
|
|
1629
1827
|
const object = new Group2();
|
|
1630
1828
|
object.add(focusMesh, mesh, edges);
|
|
1631
1829
|
const scratchColor = new Color2();
|
|
1632
|
-
const scratchVector = new
|
|
1830
|
+
const scratchVector = new Vector36();
|
|
1633
1831
|
const paintRegion = (region, color, weight) => {
|
|
1634
1832
|
const column = texels.get(region);
|
|
1635
1833
|
if (column === void 0) return;
|
|
@@ -1758,6 +1956,13 @@ function createPart(model, geometry, theme) {
|
|
|
1758
1956
|
setHoveredSharpCorner(index) {
|
|
1759
1957
|
sharpLines?.setHovered(index);
|
|
1760
1958
|
},
|
|
1959
|
+
setThreads(options) {
|
|
1960
|
+
const key = threadKey(options);
|
|
1961
|
+
if (key === threadsKey) return;
|
|
1962
|
+
threadsKey = key;
|
|
1963
|
+
writeThreadTexels(threads, width, geometry, model, texels, options);
|
|
1964
|
+
threadTexture.needsUpdate = true;
|
|
1965
|
+
},
|
|
1761
1966
|
dispose() {
|
|
1762
1967
|
object.clear();
|
|
1763
1968
|
if (geometry.getAttribute(REGION_ATTRIBUTE) === regionAttribute) {
|
|
@@ -1770,6 +1975,7 @@ function createPart(model, geometry, theme) {
|
|
|
1770
1975
|
sharpLines?.dispose();
|
|
1771
1976
|
stateTexture.dispose();
|
|
1772
1977
|
opacityTexture.dispose();
|
|
1978
|
+
threadTexture.dispose();
|
|
1773
1979
|
}
|
|
1774
1980
|
};
|
|
1775
1981
|
}
|
|
@@ -1883,9 +2089,10 @@ import {
|
|
|
1883
2089
|
useEffect as useEffect4,
|
|
1884
2090
|
useImperativeHandle,
|
|
1885
2091
|
useMemo as useMemo4,
|
|
1886
|
-
useRef as useRef3
|
|
2092
|
+
useRef as useRef3,
|
|
2093
|
+
useSyncExternalStore
|
|
1887
2094
|
} from "react";
|
|
1888
|
-
import { Box3 as Box33, Vector3 as
|
|
2095
|
+
import { Box3 as Box33, Vector3 as Vector310 } from "three";
|
|
1889
2096
|
|
|
1890
2097
|
// src/camera.tsx
|
|
1891
2098
|
import { useFrame, useThree as useThree2 } from "@react-three/fiber";
|
|
@@ -1902,7 +2109,7 @@ import {
|
|
|
1902
2109
|
Sphere,
|
|
1903
2110
|
Spherical,
|
|
1904
2111
|
Vector2 as Vector23,
|
|
1905
|
-
Vector3 as
|
|
2112
|
+
Vector3 as Vector37,
|
|
1906
2113
|
Vector4 as Vector42
|
|
1907
2114
|
} from "three";
|
|
1908
2115
|
|
|
@@ -2000,7 +2207,7 @@ CameraControls.install({
|
|
|
2000
2207
|
Sphere,
|
|
2001
2208
|
Spherical,
|
|
2002
2209
|
Vector2: Vector23,
|
|
2003
|
-
Vector3:
|
|
2210
|
+
Vector3: Vector37,
|
|
2004
2211
|
Vector4: Vector42
|
|
2005
2212
|
}
|
|
2006
2213
|
});
|
|
@@ -2023,9 +2230,9 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
2023
2230
|
// Scratch objects — `#onPointerMove` and `#adaptUpVector` run at pointer and
|
|
2024
2231
|
// frame rate respectively, so neither may allocate.
|
|
2025
2232
|
#spherical = new Spherical();
|
|
2026
|
-
#scratchA = new
|
|
2027
|
-
#scratchB = new
|
|
2028
|
-
#scratchC = new
|
|
2233
|
+
#scratchA = new Vector37();
|
|
2234
|
+
#scratchB = new Vector37();
|
|
2235
|
+
#scratchC = new Vector37();
|
|
2029
2236
|
constructor(camera, domElement, options = {}) {
|
|
2030
2237
|
super(camera, domElement);
|
|
2031
2238
|
this.#domElement = domElement;
|
|
@@ -2363,6 +2570,22 @@ function counted(notify) {
|
|
|
2363
2570
|
};
|
|
2364
2571
|
}
|
|
2365
2572
|
|
|
2573
|
+
// src/render/webgl.ts
|
|
2574
|
+
var isWebGLAvailable = () => {
|
|
2575
|
+
if (typeof document === "undefined") {
|
|
2576
|
+
return false;
|
|
2577
|
+
}
|
|
2578
|
+
try {
|
|
2579
|
+
const context = document.createElement("canvas").getContext("webgl2");
|
|
2580
|
+
context?.getExtension("WEBGL_lose_context")?.loseContext();
|
|
2581
|
+
return context !== null;
|
|
2582
|
+
} catch {
|
|
2583
|
+
return false;
|
|
2584
|
+
}
|
|
2585
|
+
};
|
|
2586
|
+
var probed;
|
|
2587
|
+
var webGLAvailableSnapshot = () => probed ??= isWebGLAvailable();
|
|
2588
|
+
|
|
2366
2589
|
// src/target-marker.tsx
|
|
2367
2590
|
import { useFrame as useFrame2, useThree as useThree3 } from "@react-three/fiber";
|
|
2368
2591
|
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
|
|
@@ -2370,7 +2593,7 @@ import {
|
|
|
2370
2593
|
DoubleSide as DoubleSide3,
|
|
2371
2594
|
RingGeometry,
|
|
2372
2595
|
SphereGeometry,
|
|
2373
|
-
Vector3 as
|
|
2596
|
+
Vector3 as Vector38
|
|
2374
2597
|
} from "three";
|
|
2375
2598
|
|
|
2376
2599
|
// src/render/target.ts
|
|
@@ -2413,8 +2636,8 @@ var TargetMarker = ({ controlsRef }) => {
|
|
|
2413
2636
|
const heldUntil = useRef2(0);
|
|
2414
2637
|
const painted = useRef2(-1);
|
|
2415
2638
|
const placed = useRef2(false);
|
|
2416
|
-
const lastTarget = useMemo3(() => new
|
|
2417
|
-
const scratch = useMemo3(() => new
|
|
2639
|
+
const lastTarget = useMemo3(() => new Vector38(), []);
|
|
2640
|
+
const scratch = useMemo3(() => new Vector38(), []);
|
|
2418
2641
|
useEffect3(() => {
|
|
2419
2642
|
const controls = controlsRef.current;
|
|
2420
2643
|
if (!controls) return void 0;
|
|
@@ -2508,13 +2731,41 @@ var TargetMarker = ({ controlsRef }) => {
|
|
|
2508
2731
|
] });
|
|
2509
2732
|
};
|
|
2510
2733
|
|
|
2734
|
+
// src/webgl-unavailable.tsx
|
|
2735
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2736
|
+
var STYLE = {
|
|
2737
|
+
display: "flex",
|
|
2738
|
+
flexDirection: "column",
|
|
2739
|
+
alignItems: "center",
|
|
2740
|
+
justifyContent: "center",
|
|
2741
|
+
gap: "0.5rem",
|
|
2742
|
+
height: "100%",
|
|
2743
|
+
padding: "1rem",
|
|
2744
|
+
boxSizing: "border-box",
|
|
2745
|
+
textAlign: "center"
|
|
2746
|
+
};
|
|
2747
|
+
var DEFAULT_MESSAGE = /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
2748
|
+
/* @__PURE__ */ jsx3("strong", { children: "3D view unavailable" }),
|
|
2749
|
+
/* @__PURE__ */ jsx3("span", { children: "WebGL is blocked in this browser. Turn on graphics (hardware) acceleration in your browser settings, then restart the browser." })
|
|
2750
|
+
] });
|
|
2751
|
+
var WebGLUnavailable = ({ children = DEFAULT_MESSAGE }) => /* @__PURE__ */ jsx3(
|
|
2752
|
+
"div",
|
|
2753
|
+
{
|
|
2754
|
+
className: "viewer-webgl-unavailable",
|
|
2755
|
+
"data-viewer-webgl-unavailable": "true",
|
|
2756
|
+
role: "alert",
|
|
2757
|
+
style: STYLE,
|
|
2758
|
+
children
|
|
2759
|
+
}
|
|
2760
|
+
);
|
|
2761
|
+
|
|
2511
2762
|
// src/render/view-cube.ts
|
|
2512
2763
|
import {
|
|
2513
2764
|
BufferGeometry as BufferGeometry2,
|
|
2514
2765
|
CanvasTexture,
|
|
2515
2766
|
Float32BufferAttribute as Float32BufferAttribute4,
|
|
2516
2767
|
SRGBColorSpace,
|
|
2517
|
-
Vector3 as
|
|
2768
|
+
Vector3 as Vector39
|
|
2518
2769
|
} from "three";
|
|
2519
2770
|
var VIEW_NAMES = [
|
|
2520
2771
|
// The six faces.
|
|
@@ -2598,12 +2849,12 @@ function viewUp(direction) {
|
|
|
2598
2849
|
return { x: 0, y: 0, z: 1 };
|
|
2599
2850
|
}
|
|
2600
2851
|
function squaredUp(direction, currentUp) {
|
|
2601
|
-
const forward = new
|
|
2852
|
+
const forward = new Vector39(direction.x, direction.y, direction.z).normalize();
|
|
2602
2853
|
const canonical = viewUp(direction);
|
|
2603
|
-
const zero = new
|
|
2854
|
+
const zero = new Vector39(canonical.x, canonical.y, canonical.z);
|
|
2604
2855
|
zero.addScaledVector(forward, -forward.dot(zero)).normalize();
|
|
2605
|
-
const quarter = new
|
|
2606
|
-
const current = new
|
|
2856
|
+
const quarter = new Vector39().crossVectors(forward, zero);
|
|
2857
|
+
const current = new Vector39(currentUp.x, currentUp.y, currentUp.z);
|
|
2607
2858
|
const rolls = [zero, quarter, zero.clone().negate(), quarter.clone().negate()];
|
|
2608
2859
|
const nearest = rolls.reduce(
|
|
2609
2860
|
(best, roll) => roll.dot(current) > best.dot(current) ? roll : best
|
|
@@ -2621,11 +2872,11 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
2621
2872
|
const direction = viewVector(name);
|
|
2622
2873
|
const [sx, sy, sz] = VIEW_SIGNS[name];
|
|
2623
2874
|
const sign = (axis) => VIEW_SIGNS[name][axis] ?? 0;
|
|
2624
|
-
const normal = new
|
|
2875
|
+
const normal = new Vector39(direction.x, direction.y, direction.z);
|
|
2625
2876
|
let polygon;
|
|
2626
2877
|
if (kind === "face") {
|
|
2627
2878
|
const up = viewUp(direction);
|
|
2628
|
-
const upVector = new
|
|
2879
|
+
const upVector = new Vector39(up.x, up.y, up.z);
|
|
2629
2880
|
const right = normal.clone().negate().cross(upVector).normalize();
|
|
2630
2881
|
const square = [
|
|
2631
2882
|
[-1, -1],
|
|
@@ -2647,7 +2898,7 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
2647
2898
|
[chamfer, 1, -chamfer]
|
|
2648
2899
|
];
|
|
2649
2900
|
polygon = rectangle.map(
|
|
2650
|
-
([onA, onB, onC]) => new
|
|
2901
|
+
([onA, onB, onC]) => new Vector39().setComponent(a, sign(a) * onA).setComponent(b, sign(b) * onB).setComponent(c, onC)
|
|
2651
2902
|
);
|
|
2652
2903
|
} else {
|
|
2653
2904
|
const triangle = [
|
|
@@ -2655,11 +2906,11 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
2655
2906
|
[chamfer, 1, chamfer],
|
|
2656
2907
|
[chamfer, chamfer, 1]
|
|
2657
2908
|
];
|
|
2658
|
-
polygon = triangle.map(([onX, onY, onZ]) => new
|
|
2909
|
+
polygon = triangle.map(([onX, onY, onZ]) => new Vector39(sx * onX, sy * onY, sz * onZ));
|
|
2659
2910
|
}
|
|
2660
2911
|
const [first, second, third] = polygon;
|
|
2661
2912
|
if (first && second && third) {
|
|
2662
|
-
const facing = new
|
|
2913
|
+
const facing = new Vector39().subVectors(second, first).cross(new Vector39().subVectors(third, first));
|
|
2663
2914
|
if (facing.dot(normal) < 0) polygon.reverse();
|
|
2664
2915
|
}
|
|
2665
2916
|
return { name, kind, direction, polygon: polygon.map(vec) };
|
|
@@ -2749,7 +3000,7 @@ function labelTexture(label, color) {
|
|
|
2749
3000
|
}
|
|
2750
3001
|
|
|
2751
3002
|
// src/viewer.tsx
|
|
2752
|
-
import { jsx as
|
|
3003
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2753
3004
|
var ViewerControlsContext = createContext3(null);
|
|
2754
3005
|
var useViewerControls = () => {
|
|
2755
3006
|
const controls = useContext3(ViewerControlsContext);
|
|
@@ -2789,10 +3040,10 @@ var ViewerScene = ({
|
|
|
2789
3040
|
const framedRef = useRef3(null);
|
|
2790
3041
|
const scratchBox = useMemo4(() => new Box33(), []);
|
|
2791
3042
|
const scratchBoundary = useMemo4(() => new Box33(), []);
|
|
2792
|
-
const scratchDirection = useMemo4(() => new
|
|
2793
|
-
const scratchView = useMemo4(() => new
|
|
2794
|
-
const scratchTarget = useMemo4(() => new
|
|
2795
|
-
const scratchPosition = useMemo4(() => new
|
|
3043
|
+
const scratchDirection = useMemo4(() => new Vector310(), []);
|
|
3044
|
+
const scratchView = useMemo4(() => new Vector310(), []);
|
|
3045
|
+
const scratchTarget = useMemo4(() => new Vector310(), []);
|
|
3046
|
+
const scratchPosition = useMemo4(() => new Vector310(), []);
|
|
2796
3047
|
const applyLimits = useCallback(
|
|
2797
3048
|
(bounds, framed) => {
|
|
2798
3049
|
controlsRef.current?.applyLimits(
|
|
@@ -2841,8 +3092,8 @@ var ViewerScene = ({
|
|
|
2841
3092
|
(bounds) => {
|
|
2842
3093
|
const controls = controlsRef.current;
|
|
2843
3094
|
if (!controls) return false;
|
|
2844
|
-
const target = controls.getTarget(new
|
|
2845
|
-
const direction = currentViewDirection(camera, target, new
|
|
3095
|
+
const target = controls.getTarget(new Vector310());
|
|
3096
|
+
const direction = currentViewDirection(camera, target, new Vector310());
|
|
2846
3097
|
const distance = fitDistance(projection, size, bounds, DEFAULT_FIT_MARGIN);
|
|
2847
3098
|
const position = direction.multiplyScalar(distance).add(bounds.center);
|
|
2848
3099
|
void controls.setLookAt(
|
|
@@ -2867,8 +3118,8 @@ var ViewerScene = ({
|
|
|
2867
3118
|
const fitContent = useCallback(() => {
|
|
2868
3119
|
const controls = controlsRef.current;
|
|
2869
3120
|
if (!controls) return false;
|
|
2870
|
-
const target = controls.getTarget(new
|
|
2871
|
-
return frame(currentViewDirection(camera, target, new
|
|
3121
|
+
const target = controls.getTarget(new Vector310());
|
|
3122
|
+
return frame(currentViewDirection(camera, target, new Vector310()), true);
|
|
2872
3123
|
}, [camera, frame]);
|
|
2873
3124
|
const resetContent = useCallback(() => {
|
|
2874
3125
|
const bounds = measure();
|
|
@@ -2958,18 +3209,18 @@ var ViewerScene = ({
|
|
|
2958
3209
|
initialFrameComplete.current = resetContent();
|
|
2959
3210
|
}
|
|
2960
3211
|
});
|
|
2961
|
-
return /* @__PURE__ */
|
|
2962
|
-
/* @__PURE__ */
|
|
2963
|
-
/* @__PURE__ */
|
|
2964
|
-
/* @__PURE__ */
|
|
3212
|
+
return /* @__PURE__ */ jsx4(ViewerTapProvider, { value: isTap, children: /* @__PURE__ */ jsxs3(ViewerRetargetContext.Provider, { value: retargetOn, children: [
|
|
3213
|
+
/* @__PURE__ */ jsxs3("group", { ref: lightsRef, children: [
|
|
3214
|
+
/* @__PURE__ */ jsx4("ambientLight", { color: theme.ambient, intensity: theme.ambientIntensity }),
|
|
3215
|
+
/* @__PURE__ */ jsx4(
|
|
2965
3216
|
"hemisphereLight",
|
|
2966
3217
|
{
|
|
2967
3218
|
args: [theme.hemisphereSky, theme.hemisphereGround, theme.hemisphereIntensity]
|
|
2968
3219
|
}
|
|
2969
3220
|
)
|
|
2970
3221
|
] }),
|
|
2971
|
-
/* @__PURE__ */
|
|
2972
|
-
/* @__PURE__ */
|
|
3222
|
+
/* @__PURE__ */ jsx4("group", { ref: contentRef, children }),
|
|
3223
|
+
/* @__PURE__ */ jsx4(
|
|
2973
3224
|
CadCameraControls,
|
|
2974
3225
|
{
|
|
2975
3226
|
controlsRef,
|
|
@@ -2978,9 +3229,11 @@ var ViewerScene = ({
|
|
|
2978
3229
|
zoomTo
|
|
2979
3230
|
}
|
|
2980
3231
|
),
|
|
2981
|
-
showOrbitTarget ? /* @__PURE__ */
|
|
3232
|
+
showOrbitTarget ? /* @__PURE__ */ jsx4(TargetMarker, { controlsRef }) : null
|
|
2982
3233
|
] }) });
|
|
2983
3234
|
};
|
|
3235
|
+
var subscribeNever = () => () => {
|
|
3236
|
+
};
|
|
2984
3237
|
var Viewer = forwardRef(function Viewer2({
|
|
2985
3238
|
children,
|
|
2986
3239
|
className,
|
|
@@ -2993,8 +3246,10 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
2993
3246
|
retargetOnDoubleClick = true,
|
|
2994
3247
|
showOrbitTarget = false,
|
|
2995
3248
|
theme,
|
|
2996
|
-
onPointerMissed
|
|
3249
|
+
onPointerMissed,
|
|
3250
|
+
webglUnavailable
|
|
2997
3251
|
}, ref) {
|
|
3252
|
+
const webglAvailable = useSyncExternalStore(subscribeNever, webGLAvailableSnapshot, () => true);
|
|
2998
3253
|
const actionsRef = useRef3(null);
|
|
2999
3254
|
const resolved = useMemo4(() => resolveTheme(theme), [theme]);
|
|
3000
3255
|
const sectionStore = useMemo4(() => createSectionStore(), []);
|
|
@@ -3020,7 +3275,7 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
3020
3275
|
tracker.current?.dispose();
|
|
3021
3276
|
tracker.current = element ? trackTaps(element) : null;
|
|
3022
3277
|
}, []);
|
|
3023
|
-
return /* @__PURE__ */
|
|
3278
|
+
return /* @__PURE__ */ jsx4(ViewerControlsContext.Provider, { value: proxy, children: /* @__PURE__ */ jsx4(SectionStoreContext.Provider, { value: sectionStore, children: /* @__PURE__ */ jsx4(
|
|
3024
3279
|
"div",
|
|
3025
3280
|
{
|
|
3026
3281
|
className: ["viewer-root", className].filter(Boolean).join(" "),
|
|
@@ -3030,7 +3285,7 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
3030
3285
|
if (event.target instanceof HTMLCanvasElement) event.preventDefault();
|
|
3031
3286
|
},
|
|
3032
3287
|
style: { height: "100%", width: "100%", ...style },
|
|
3033
|
-
children: /* @__PURE__ */
|
|
3288
|
+
children: webglAvailable ? /* @__PURE__ */ jsx4(
|
|
3034
3289
|
Canvas,
|
|
3035
3290
|
{
|
|
3036
3291
|
className: "viewer-canvas-container",
|
|
@@ -3051,7 +3306,7 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
3051
3306
|
if (event.button !== 0) return;
|
|
3052
3307
|
if (tracker.current?.isTap(event) ?? true) onPointerMissed?.();
|
|
3053
3308
|
},
|
|
3054
|
-
children: /* @__PURE__ */
|
|
3309
|
+
children: /* @__PURE__ */ jsx4(
|
|
3055
3310
|
ViewerScene,
|
|
3056
3311
|
{
|
|
3057
3312
|
setControls,
|
|
@@ -3068,7 +3323,7 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
3068
3323
|
)
|
|
3069
3324
|
},
|
|
3070
3325
|
projection
|
|
3071
|
-
)
|
|
3326
|
+
) : /* @__PURE__ */ jsx4(WebGLUnavailable, { children: webglUnavailable })
|
|
3072
3327
|
}
|
|
3073
3328
|
) }) });
|
|
3074
3329
|
});
|
|
@@ -3087,7 +3342,7 @@ import {
|
|
|
3087
3342
|
Raycaster as Raycaster2,
|
|
3088
3343
|
Scene,
|
|
3089
3344
|
Vector2 as Vector26,
|
|
3090
|
-
Vector3 as
|
|
3345
|
+
Vector3 as Vector312
|
|
3091
3346
|
} from "three";
|
|
3092
3347
|
|
|
3093
3348
|
// src/arrow-mesh.tsx
|
|
@@ -3178,7 +3433,7 @@ function createDepthResetMaterial() {
|
|
|
3178
3433
|
}
|
|
3179
3434
|
|
|
3180
3435
|
// src/arrow-mesh.tsx
|
|
3181
|
-
import { Fragment, jsx as
|
|
3436
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3182
3437
|
var ArrowMesh = ({ geometry, color, opacity, onTop = false, events }) => {
|
|
3183
3438
|
const invalidate = useThree5((state) => state.invalidate);
|
|
3184
3439
|
const material = useMemo5(() => createArrowMaterial(), []);
|
|
@@ -3191,16 +3446,16 @@ var ArrowMesh = ({ geometry, color, opacity, onTop = false, events }) => {
|
|
|
3191
3446
|
applyArrowColor(material, color, opacity);
|
|
3192
3447
|
invalidate();
|
|
3193
3448
|
}, [color, invalidate, material, opacity]);
|
|
3194
|
-
return /* @__PURE__ */
|
|
3195
|
-
reset ? /* @__PURE__ */
|
|
3196
|
-
/* @__PURE__ */
|
|
3197
|
-
/* @__PURE__ */
|
|
3449
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
3450
|
+
reset ? /* @__PURE__ */ jsx5("mesh", { geometry, material: reset, renderOrder: 0, raycast: () => null }) : null,
|
|
3451
|
+
/* @__PURE__ */ jsx5("mesh", { geometry, material: depth, renderOrder: 1, raycast: () => null }),
|
|
3452
|
+
/* @__PURE__ */ jsx5("mesh", { geometry, material, renderOrder: 2, ...events })
|
|
3198
3453
|
] });
|
|
3199
3454
|
};
|
|
3200
3455
|
|
|
3201
3456
|
// src/render/arrow.ts
|
|
3202
|
-
import { LatheGeometry, Vector2 as Vector24, Vector3 as
|
|
3203
|
-
var ARROW_AXIS2 = new
|
|
3457
|
+
import { LatheGeometry, Vector2 as Vector24, Vector3 as Vector311 } from "three";
|
|
3458
|
+
var ARROW_AXIS2 = new Vector311(0, 1, 0);
|
|
3204
3459
|
var ARROW_ROUND = 32;
|
|
3205
3460
|
var HEAD_LENGTH = 0.415;
|
|
3206
3461
|
var HEAD_RADIUS = 0.153;
|
|
@@ -3426,12 +3681,12 @@ function createMaskMaterial() {
|
|
|
3426
3681
|
}
|
|
3427
3682
|
|
|
3428
3683
|
// src/section-view.tsx
|
|
3429
|
-
import { Fragment as
|
|
3684
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3430
3685
|
var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
3431
3686
|
function resolveSectionPlane(options, box) {
|
|
3432
3687
|
if (!options?.enabled || box.isEmpty()) return null;
|
|
3433
3688
|
const normal = options.plane?.normal ?? options.normal ?? DEFAULT_SECTION_NORMAL;
|
|
3434
|
-
const axis = new
|
|
3689
|
+
const axis = new Vector312(normal.x, normal.y, normal.z);
|
|
3435
3690
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
3436
3691
|
axis.normalize();
|
|
3437
3692
|
const bounds = sectionBounds(box, normal);
|
|
@@ -3452,7 +3707,7 @@ function resolveSectionPlane(options, box) {
|
|
|
3452
3707
|
}
|
|
3453
3708
|
};
|
|
3454
3709
|
}
|
|
3455
|
-
var Z = new
|
|
3710
|
+
var Z = new Vector312(0, 0, 1);
|
|
3456
3711
|
var SectionView = ({
|
|
3457
3712
|
geometry,
|
|
3458
3713
|
box,
|
|
@@ -3472,7 +3727,7 @@ var SectionView = ({
|
|
|
3472
3727
|
const handleRef = useRef4(null);
|
|
3473
3728
|
const [hovered, setHovered] = useState(false);
|
|
3474
3729
|
const dragging = useRef4(null);
|
|
3475
|
-
const span = useMemo6(() => box.getSize(new
|
|
3730
|
+
const span = useMemo6(() => box.getSize(new Vector312()).length(), [box]);
|
|
3476
3731
|
const frameSize = span * SECTION_GIZMO_FRAME_SCALE;
|
|
3477
3732
|
const directionColor2 = useMemo6(() => sectionDirectionColor(plane.normal), [plane]);
|
|
3478
3733
|
const resolvedHandleColor = sectionHandleColor(handleColor, directionColor2);
|
|
@@ -3556,7 +3811,7 @@ var SectionView = ({
|
|
|
3556
3811
|
event.stopPropagation();
|
|
3557
3812
|
const view = camera.position.clone().sub(capPosition).normalize();
|
|
3558
3813
|
const surface = dragPlane(plane.normal.clone(), view, capPosition);
|
|
3559
|
-
const hit = event.ray.intersectPlane(surface, new
|
|
3814
|
+
const hit = event.ray.intersectPlane(surface, new Vector312());
|
|
3560
3815
|
const start = {
|
|
3561
3816
|
plane: surface,
|
|
3562
3817
|
from: hit ? hit.dot(plane.normal) : 0,
|
|
@@ -3573,7 +3828,7 @@ var SectionView = ({
|
|
|
3573
3828
|
-((native.clientY - rect.top) / rect.height) * 2 + 1
|
|
3574
3829
|
);
|
|
3575
3830
|
raycaster.setFromCamera(pointer, camera);
|
|
3576
|
-
const point = raycaster.ray.intersectPlane(start.plane, new
|
|
3831
|
+
const point = raycaster.ray.intersectPlane(start.plane, new Vector312());
|
|
3577
3832
|
if (!point) return;
|
|
3578
3833
|
onDrag(start.constant - (point.dot(plane.normal) - start.from));
|
|
3579
3834
|
invalidate();
|
|
@@ -3605,8 +3860,8 @@ var SectionView = ({
|
|
|
3605
3860
|
// Excluded from framing, all of it. The cap is a quad half again as wide as
|
|
3606
3861
|
// the part's diagonal, so a Fit that measured it would frame the cut rather
|
|
3607
3862
|
// than the part and leave the part a speck in the middle of it.
|
|
3608
|
-
/* @__PURE__ */
|
|
3609
|
-
/* @__PURE__ */
|
|
3863
|
+
/* @__PURE__ */ jsxs5("group", { userData: FURNITURE2, children: [
|
|
3864
|
+
/* @__PURE__ */ jsx6(
|
|
3610
3865
|
"mesh",
|
|
3611
3866
|
{
|
|
3612
3867
|
geometry,
|
|
@@ -3615,7 +3870,7 @@ var SectionView = ({
|
|
|
3615
3870
|
raycast: () => null
|
|
3616
3871
|
}
|
|
3617
3872
|
),
|
|
3618
|
-
/* @__PURE__ */
|
|
3873
|
+
/* @__PURE__ */ jsx6(
|
|
3619
3874
|
"mesh",
|
|
3620
3875
|
{
|
|
3621
3876
|
geometry,
|
|
@@ -3624,7 +3879,7 @@ var SectionView = ({
|
|
|
3624
3879
|
raycast: () => null
|
|
3625
3880
|
}
|
|
3626
3881
|
),
|
|
3627
|
-
/* @__PURE__ */
|
|
3882
|
+
/* @__PURE__ */ jsx6(
|
|
3628
3883
|
"mesh",
|
|
3629
3884
|
{
|
|
3630
3885
|
geometry: quad,
|
|
@@ -3635,15 +3890,15 @@ var SectionView = ({
|
|
|
3635
3890
|
raycast: () => null
|
|
3636
3891
|
}
|
|
3637
3892
|
),
|
|
3638
|
-
showHandle && onDrag ? /* @__PURE__ */
|
|
3639
|
-
/* @__PURE__ */
|
|
3893
|
+
showHandle && onDrag ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3894
|
+
/* @__PURE__ */ jsxs5(
|
|
3640
3895
|
"group",
|
|
3641
3896
|
{
|
|
3642
3897
|
position: capPosition,
|
|
3643
3898
|
quaternion: capQuaternion,
|
|
3644
3899
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
3645
3900
|
children: [
|
|
3646
|
-
/* @__PURE__ */
|
|
3901
|
+
/* @__PURE__ */ jsx6("mesh", { geometry: frameSurface, raycast: () => null, children: /* @__PURE__ */ jsx6(
|
|
3647
3902
|
"meshBasicMaterial",
|
|
3648
3903
|
{
|
|
3649
3904
|
color: directionColor2,
|
|
@@ -3654,18 +3909,18 @@ var SectionView = ({
|
|
|
3654
3909
|
depthWrite: false
|
|
3655
3910
|
}
|
|
3656
3911
|
) }),
|
|
3657
|
-
/* @__PURE__ */
|
|
3912
|
+
/* @__PURE__ */ jsx6("lineSegments", { geometry: frameEdges, raycast: () => null, children: /* @__PURE__ */ jsx6("lineBasicMaterial", { color: directionColor2, depthTest: false, depthWrite: false }) })
|
|
3658
3913
|
]
|
|
3659
3914
|
}
|
|
3660
3915
|
),
|
|
3661
|
-
/* @__PURE__ */
|
|
3916
|
+
/* @__PURE__ */ jsx6(
|
|
3662
3917
|
"group",
|
|
3663
3918
|
{
|
|
3664
3919
|
ref: handleRef,
|
|
3665
3920
|
position: capPosition,
|
|
3666
3921
|
quaternion: handleQuaternion,
|
|
3667
3922
|
renderOrder: SECTION_RENDER_ORDER.handle,
|
|
3668
|
-
children: /* @__PURE__ */
|
|
3923
|
+
children: /* @__PURE__ */ jsx6(
|
|
3669
3924
|
ArrowMesh,
|
|
3670
3925
|
{
|
|
3671
3926
|
geometry: handleShape,
|
|
@@ -3702,7 +3957,7 @@ function useContentBox() {
|
|
|
3702
3957
|
}
|
|
3703
3958
|
|
|
3704
3959
|
// src/part-mesh.tsx
|
|
3705
|
-
import { Fragment as
|
|
3960
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3706
3961
|
var PartMesh = ({
|
|
3707
3962
|
model,
|
|
3708
3963
|
geometry,
|
|
@@ -3724,6 +3979,7 @@ var PartMesh = ({
|
|
|
3724
3979
|
theme,
|
|
3725
3980
|
showEdges = true,
|
|
3726
3981
|
sharpCorners = null,
|
|
3982
|
+
threads = null,
|
|
3727
3983
|
display = "solid",
|
|
3728
3984
|
children
|
|
3729
3985
|
}) => {
|
|
@@ -3740,11 +3996,11 @@ var PartMesh = ({
|
|
|
3740
3996
|
onHoverRef.current = onHover;
|
|
3741
3997
|
const box = useContentBox();
|
|
3742
3998
|
const store = useSectionStore();
|
|
3743
|
-
const shared =
|
|
3999
|
+
const shared = useSyncExternalStore2(store.subscribe, store.get, store.get);
|
|
3744
4000
|
const controlled = section !== void 0;
|
|
3745
4001
|
const options = controlled ? section : shared ?? void 0;
|
|
3746
4002
|
const cut = useMemo7(() => resolveSectionPlane(options, box), [box, options]);
|
|
3747
|
-
const engaged =
|
|
4003
|
+
const engaged = useSyncExternalStore2(store.subscribe, store.isEngaged, store.isEngaged);
|
|
3748
4004
|
const pressedWhileEngaged = useRef6(false);
|
|
3749
4005
|
const layers = useRef6({
|
|
3750
4006
|
selection,
|
|
@@ -3825,6 +4081,13 @@ var PartMesh = ({
|
|
|
3825
4081
|
if (hoverCorner.current !== null) dropHover();
|
|
3826
4082
|
invalidate();
|
|
3827
4083
|
}, [dropHover, invalidate, part, sharpKey]);
|
|
4084
|
+
const threadsRef = useRef6(threads);
|
|
4085
|
+
threadsRef.current = threads;
|
|
4086
|
+
const threadsKey = threadKey(threads);
|
|
4087
|
+
useLayoutEffect3(() => {
|
|
4088
|
+
part.setThreads(threadsRef.current);
|
|
4089
|
+
invalidate();
|
|
4090
|
+
}, [invalidate, part, threadsKey]);
|
|
3828
4091
|
const reportedSection = useRef6("");
|
|
3829
4092
|
useLayoutEffect3(() => {
|
|
3830
4093
|
const state = cut?.state ?? null;
|
|
@@ -3914,8 +4177,8 @@ var PartMesh = ({
|
|
|
3914
4177
|
},
|
|
3915
4178
|
[box, controlled, cut, onSectionChange, options, store]
|
|
3916
4179
|
);
|
|
3917
|
-
return /* @__PURE__ */
|
|
3918
|
-
/* @__PURE__ */
|
|
4180
|
+
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
4181
|
+
/* @__PURE__ */ jsx7(
|
|
3919
4182
|
"primitive",
|
|
3920
4183
|
{
|
|
3921
4184
|
object: part.object,
|
|
@@ -3952,7 +4215,7 @@ var PartMesh = ({
|
|
|
3952
4215
|
}
|
|
3953
4216
|
}
|
|
3954
4217
|
),
|
|
3955
|
-
cut ? /* @__PURE__ */
|
|
4218
|
+
cut ? /* @__PURE__ */ jsx7(
|
|
3956
4219
|
SectionView,
|
|
3957
4220
|
{
|
|
3958
4221
|
geometry,
|
|
@@ -3964,12 +4227,12 @@ var PartMesh = ({
|
|
|
3964
4227
|
onDrag: !controlled || onSectionChange ? dragSection : void 0
|
|
3965
4228
|
}
|
|
3966
4229
|
) : null,
|
|
3967
|
-
children ? /* @__PURE__ */
|
|
4230
|
+
children ? /* @__PURE__ */ jsx7(PartContext.Provider, { value: context, children }) : null
|
|
3968
4231
|
] });
|
|
3969
4232
|
};
|
|
3970
|
-
var ORIGIN = new
|
|
3971
|
-
var UP = new
|
|
3972
|
-
var TARGET = new
|
|
4233
|
+
var ORIGIN = new Vector313();
|
|
4234
|
+
var UP = new Vector313(0, 0, 1);
|
|
4235
|
+
var TARGET = new Vector313();
|
|
3973
4236
|
function readTarget(controls) {
|
|
3974
4237
|
if (controls && typeof controls.getTarget === "function") {
|
|
3975
4238
|
return controls.getTarget(TARGET);
|
|
@@ -4183,6 +4446,8 @@ function createEngineGeometryCache(loadGeometry = async (part) => {
|
|
|
4183
4446
|
return geometry;
|
|
4184
4447
|
}, maximumEntries = 8) {
|
|
4185
4448
|
const resources = /* @__PURE__ */ new Map();
|
|
4449
|
+
const requests = /* @__PURE__ */ new WeakMap();
|
|
4450
|
+
let failures = /* @__PURE__ */ new WeakMap();
|
|
4186
4451
|
let accessSequence = 0;
|
|
4187
4452
|
const touch = (resource) => {
|
|
4188
4453
|
resource.lastAccess = ++accessSequence;
|
|
@@ -4202,11 +4467,31 @@ function createEngineGeometryCache(loadGeometry = async (part) => {
|
|
|
4202
4467
|
oldest[1].geometry?.dispose();
|
|
4203
4468
|
}
|
|
4204
4469
|
};
|
|
4470
|
+
const rememberFailure = (part, requestKey, resource) => {
|
|
4471
|
+
const attempts = failures;
|
|
4472
|
+
void resource.promise.then(() => {
|
|
4473
|
+
if (resource.status === "rejected" && requests.get(resource) === requestKey) {
|
|
4474
|
+
attempts.set(part, resource);
|
|
4475
|
+
}
|
|
4476
|
+
});
|
|
4477
|
+
};
|
|
4205
4478
|
return {
|
|
4206
4479
|
get(part) {
|
|
4207
4480
|
const key = engineGeometryResourceKey(part.mesh);
|
|
4208
4481
|
const existing = resources.get(key);
|
|
4482
|
+
if (existing?.status === "fulfilled") {
|
|
4483
|
+
failures.delete(part);
|
|
4484
|
+
touch(existing);
|
|
4485
|
+
return existing;
|
|
4486
|
+
}
|
|
4487
|
+
const requestKey = JSON.stringify([part.mesh.glbUrl, part.mesh.stlUrl]);
|
|
4488
|
+
const failed = failures.get(part);
|
|
4489
|
+
if (failed && requests.get(failed) === requestKey) {
|
|
4490
|
+
touch(failed);
|
|
4491
|
+
return failed;
|
|
4492
|
+
}
|
|
4209
4493
|
if (existing) {
|
|
4494
|
+
rememberFailure(part, requestKey, existing);
|
|
4210
4495
|
touch(existing);
|
|
4211
4496
|
return existing;
|
|
4212
4497
|
}
|
|
@@ -4216,6 +4501,7 @@ function createEngineGeometryCache(loadGeometry = async (part) => {
|
|
|
4216
4501
|
references: 0,
|
|
4217
4502
|
lastAccess: ++accessSequence
|
|
4218
4503
|
};
|
|
4504
|
+
requests.set(resource, requestKey);
|
|
4219
4505
|
resource.promise = loadGeometry(part).then(
|
|
4220
4506
|
(geometry) => {
|
|
4221
4507
|
resource.status = "fulfilled";
|
|
@@ -4228,6 +4514,7 @@ function createEngineGeometryCache(loadGeometry = async (part) => {
|
|
|
4228
4514
|
}
|
|
4229
4515
|
);
|
|
4230
4516
|
resources.set(key, resource);
|
|
4517
|
+
rememberFailure(part, requestKey, resource);
|
|
4231
4518
|
return resource;
|
|
4232
4519
|
},
|
|
4233
4520
|
retain(resource) {
|
|
@@ -4242,6 +4529,7 @@ function createEngineGeometryCache(loadGeometry = async (part) => {
|
|
|
4242
4529
|
clear() {
|
|
4243
4530
|
for (const resource of resources.values()) resource.geometry?.dispose();
|
|
4244
4531
|
resources.clear();
|
|
4532
|
+
failures = /* @__PURE__ */ new WeakMap();
|
|
4245
4533
|
}
|
|
4246
4534
|
};
|
|
4247
4535
|
}
|
|
@@ -4593,15 +4881,28 @@ function compareVersions(a, b) {
|
|
|
4593
4881
|
}
|
|
4594
4882
|
|
|
4595
4883
|
// src/engine/engine-part.tsx
|
|
4596
|
-
import { jsx as
|
|
4884
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
4885
|
+
var models = /* @__PURE__ */ new WeakMap();
|
|
4886
|
+
var modelForReport = (report) => {
|
|
4887
|
+
if (typeof report !== "object" || report === null) {
|
|
4888
|
+
return normalizePartReport(report);
|
|
4889
|
+
}
|
|
4890
|
+
const existing = models.get(report);
|
|
4891
|
+
if (existing) {
|
|
4892
|
+
return existing;
|
|
4893
|
+
}
|
|
4894
|
+
const model = normalizePartReport(report);
|
|
4895
|
+
models.set(report, model);
|
|
4896
|
+
return model;
|
|
4897
|
+
};
|
|
4597
4898
|
var EnginePart = ({ report, ...props }) => {
|
|
4598
|
-
const model =
|
|
4899
|
+
const model = modelForReport(report);
|
|
4599
4900
|
const resource = useGeometryResource(model);
|
|
4600
4901
|
useEffect8(() => {
|
|
4601
4902
|
engineGeometryCache.retain(resource);
|
|
4602
4903
|
return () => engineGeometryCache.release(resource);
|
|
4603
4904
|
}, [resource]);
|
|
4604
|
-
return /* @__PURE__ */
|
|
4905
|
+
return /* @__PURE__ */ jsx8(PartMesh, { model, geometry: resource.geometry, ...props });
|
|
4605
4906
|
};
|
|
4606
4907
|
function useGeometryResource(part) {
|
|
4607
4908
|
const resource = engineGeometryCache.get(part);
|
|
@@ -4610,6 +4911,23 @@ function useGeometryResource(part) {
|
|
|
4610
4911
|
return resource;
|
|
4611
4912
|
}
|
|
4612
4913
|
|
|
4914
|
+
// src/engine/threads.ts
|
|
4915
|
+
function threadMark(feature, datasheet) {
|
|
4916
|
+
if (!isRecord(datasheet) || !isRecord(datasheet["facts"])) return null;
|
|
4917
|
+
const facts = datasheet["facts"];
|
|
4918
|
+
if (facts["kind"] !== "Hole" || !isRecord(facts["threading"])) return null;
|
|
4919
|
+
const spec = facts["threading"]["spec"];
|
|
4920
|
+
if (!isRecord(spec)) return null;
|
|
4921
|
+
const pitch = spec["threadPitch"];
|
|
4922
|
+
const majorDiameter = spec["basicDiameter"];
|
|
4923
|
+
if (!isFiniteNumber(pitch) || pitch <= 0 || !isFiniteNumber(majorDiameter)) return null;
|
|
4924
|
+
return {
|
|
4925
|
+
regions: feature.regionIdxs,
|
|
4926
|
+
pitch: spec["handedness"] === "Left" ? -pitch : pitch,
|
|
4927
|
+
majorDiameter
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4613
4931
|
export {
|
|
4614
4932
|
usePartContext,
|
|
4615
4933
|
sameDirection,
|
|
@@ -4784,5 +5102,6 @@ export {
|
|
|
4784
5102
|
MIN_KERNEL_VERSION,
|
|
4785
5103
|
normalizePartReport,
|
|
4786
5104
|
assertSupportedKernelVersion,
|
|
4787
|
-
EnginePart
|
|
5105
|
+
EnginePart,
|
|
5106
|
+
threadMark
|
|
4788
5107
|
};
|