@toolpath/viewer 2.3.3 → 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 +26 -4
- package/dist/{chunk-M2B3CQPQ.js → chunk-LBG6GVGE.js} +365 -84
- 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
package/README.md
CHANGED
|
@@ -331,6 +331,7 @@ the part opaque. For multiselection, update your own tag array and avoid duplica
|
|
|
331
331
|
| `showOrbitTarget` | `false` | Show the pivot marker. |
|
|
332
332
|
| `theme` | Default lighting | Override lighting; part/cube/tool colors have their own theme props. |
|
|
333
333
|
| `onPointerMissed` | — | Empty-space primary click, with drag guarding; use it to clear selection. |
|
|
334
|
+
| `webglUnavailable` | Default message | Content of the alert shown instead of the canvas when WebGL is unavailable. |
|
|
334
335
|
| `style`, `className` | Full-size wrapper | Set dimensions and background on the wrapper or parent. |
|
|
335
336
|
| `ref` | — | `ViewerHandle` for controls outside the canvas. |
|
|
336
337
|
|
|
@@ -340,6 +341,13 @@ the part opaque. For multiselection, update your own tag array and avoid duplica
|
|
|
340
341
|
Named views are `top`, `bottom`, `front`, `back`, `left`, `right`, and `isometric`.
|
|
341
342
|
Direction vectors point from the part toward the camera.
|
|
342
343
|
|
|
344
|
+
`Viewer` checks for WebGL2 once per page, on the client. If the browser will not create a context
|
|
345
|
+
(hardware acceleration off or the GPU blocklisted), it renders a `role="alert"`
|
|
346
|
+
element (`viewer-webgl-unavailable`, `data-viewer-webgl-unavailable="true"`) in
|
|
347
|
+
place of the canvas, asking the user to turn on graphics acceleration; children are
|
|
348
|
+
not rendered. Pass `webglUnavailable` to replace that message. Server rendering assumes
|
|
349
|
+
WebGL is available; the check runs on the client after hydration.
|
|
350
|
+
|
|
343
351
|
#### CAD controls
|
|
344
352
|
|
|
345
353
|
| Preset | Rotate | Pan | Zoom |
|
|
@@ -375,6 +383,7 @@ fails or is absent. It does not fetch a report or start an Engine job.
|
|
|
375
383
|
| `activeDirection` | Candidate-direction index; `null`/omitted means all directions. |
|
|
376
384
|
| `display`, `showEdges` | `solid` or `wireframe`; outlines default on. Wireframe always shows semantic face boundaries, not triangle diagonals. |
|
|
377
385
|
| `sharpCorners` | `{ features: tags }` for externally flagged features, or `null`. |
|
|
386
|
+
| `threads` | `{ threads: marks }` to draw threads in their holes, or `null`. See "Showing threads". |
|
|
378
387
|
| `section`, `onSectionChange` | Controlled section options and change reporting. Omit `section` to follow the viewer's section store. |
|
|
379
388
|
| `onAdjacency` | Receive touching-region sets for the prepared mesh. |
|
|
380
389
|
| `theme` | Partial `ViewerTheme` for the part. |
|
|
@@ -468,7 +477,7 @@ With no children, `ViewerToolbar` renders configured controls in standard order.
|
|
|
468
477
|
For custom composition use `ViewerToolbar.Controls`, `ViewerToolbar.Divider`, and
|
|
469
478
|
its `*Button` components. A button without a matching provider control throws.
|
|
470
479
|
The supported action names are `stock`, `axes`, `grid`, `banana`, `tools`,
|
|
471
|
-
`directions`, `dfm`, `sharpCorners`, `hover`, `focus`, `wireframe`, `section`,
|
|
480
|
+
`directions`, `dfm`, `sharpCorners`, `threads`, `hover`, `focus`, `wireframe`, `section`,
|
|
472
481
|
`measure`, `fit`, `reset`, and `top`; button names capitalize the action, such as
|
|
473
482
|
`ViewerToolbar.SharpCornersButton`. `useViewerToolbar()` reads those controls.
|
|
474
483
|
|
|
@@ -670,6 +679,19 @@ axis legs also have `data-axis`, and finished labels have `data-measurement-id`.
|
|
|
670
679
|
millimetres; the `format(mm)` callback changes their displayed string, not geometry
|
|
671
680
|
units or angles (which remain degrees).
|
|
672
681
|
|
|
682
|
+
### Showing threads
|
|
683
|
+
|
|
684
|
+
The Engine cuts a modeled internal thread out of the mesh, so a tapped hole arrives as a
|
|
685
|
+
plain bore. Pass `threads={{ threads: marks }}` to draw each thread back in as shading
|
|
686
|
+
over its hole's cylinders, at its pitch and hand. Build a mark from a hole's Engine
|
|
687
|
+
datasheet with `threadMark(feature, datasheet)` from `@toolpath/viewer/engine`; it is
|
|
688
|
+
`null` unless the datasheet carries a thread, so only threads the Engine reports are
|
|
689
|
+
drawn. A `ThreadMark` is `{ regions, pitch, majorDiameter }`: `pitch` is in mm and
|
|
690
|
+
negative for a left-hand thread, and cylinders wider than `majorDiameter` (a counterbore)
|
|
691
|
+
stay plain. The axis is fitted from the mesh, so marks need no coordinates. The datasheet
|
|
692
|
+
gives no thread length, so a tap-drill section of the same bore below the thread is
|
|
693
|
+
drawn threaded too.
|
|
694
|
+
|
|
673
695
|
### Showing sharp corners and tool marks
|
|
674
696
|
|
|
675
697
|
DFM decisions belong to the application. Pass flagged feature tags as
|
|
@@ -719,9 +741,9 @@ Part keys include `part`, `partEmissive`, `hover`, `highlight`, `picked`, `edge`
|
|
|
719
741
|
use `measure`/`measureSnap`; cube keys are `cube`, `cubeEdge`, and `cubeLabel`.
|
|
720
742
|
The canvas background comes from the wrapper/parent, independently of those colors.
|
|
721
743
|
|
|
722
|
-
Stable DOM classes include `viewer-root`, `viewer-
|
|
723
|
-
`viewer-canvas-
|
|
724
|
-
`viewer-toolbar`, `viewer-toolbar-button`, `viewer-toolbar-icon`,
|
|
744
|
+
Stable DOM classes include `viewer-root`, `viewer-webgl-unavailable`,
|
|
745
|
+
`viewer-canvas-container`, `viewer-canvas-frame`, `viewer-canvas`, `viewer-hover-card`,
|
|
746
|
+
`viewer-toolbar-stack`, `viewer-toolbar`, `viewer-toolbar-button`, `viewer-toolbar-icon`,
|
|
725
747
|
`viewer-toolbar-tooltip`, and `viewer-toolbar-divider`. Caller `className` values
|
|
726
748
|
append to the component's hook. Corresponding `data-viewer-*` attributes identify
|
|
727
749
|
the DOM boundaries; `data-viewer-toolbar-action` contains the control's action name.
|
|
@@ -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);
|
|
@@ -4618,7 +4881,7 @@ function compareVersions(a, b) {
|
|
|
4618
4881
|
}
|
|
4619
4882
|
|
|
4620
4883
|
// src/engine/engine-part.tsx
|
|
4621
|
-
import { jsx as
|
|
4884
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
4622
4885
|
var models = /* @__PURE__ */ new WeakMap();
|
|
4623
4886
|
var modelForReport = (report) => {
|
|
4624
4887
|
if (typeof report !== "object" || report === null) {
|
|
@@ -4639,7 +4902,7 @@ var EnginePart = ({ report, ...props }) => {
|
|
|
4639
4902
|
engineGeometryCache.retain(resource);
|
|
4640
4903
|
return () => engineGeometryCache.release(resource);
|
|
4641
4904
|
}, [resource]);
|
|
4642
|
-
return /* @__PURE__ */
|
|
4905
|
+
return /* @__PURE__ */ jsx8(PartMesh, { model, geometry: resource.geometry, ...props });
|
|
4643
4906
|
};
|
|
4644
4907
|
function useGeometryResource(part) {
|
|
4645
4908
|
const resource = engineGeometryCache.get(part);
|
|
@@ -4648,6 +4911,23 @@ function useGeometryResource(part) {
|
|
|
4648
4911
|
return resource;
|
|
4649
4912
|
}
|
|
4650
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
|
+
|
|
4651
4931
|
export {
|
|
4652
4932
|
usePartContext,
|
|
4653
4933
|
sameDirection,
|
|
@@ -4822,5 +5102,6 @@ export {
|
|
|
4822
5102
|
MIN_KERNEL_VERSION,
|
|
4823
5103
|
normalizePartReport,
|
|
4824
5104
|
assertSupportedKernelVersion,
|
|
4825
|
-
EnginePart
|
|
5105
|
+
EnginePart,
|
|
5106
|
+
threadMark
|
|
4826
5107
|
};
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PartModel, a as PartMeshRefs } from '../normalize-
|
|
2
|
-
export { E as EnginePart,
|
|
1
|
+
import { P as PartModel, a as PartMeshRefs, b as PartModelFeature, T as ThreadMark } from '../normalize-BBADLCmd.js';
|
|
2
|
+
export { E as EnginePart, c as EnginePartProps, M as MIN_KERNEL_VERSION, d as assertSupportedKernelVersion, n as normalizePartReport, s as smoothRegionNormals } from '../normalize-BBADLCmd.js';
|
|
3
3
|
import { BufferGeometry } from 'three';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
@@ -115,4 +115,15 @@ declare function loadPartMesh(mesh: PartMeshRefs, options?: LoadPartGeometryOpti
|
|
|
115
115
|
*/
|
|
116
116
|
declare function parsePartGeometry(data: ArrayBuffer, mesh: PartMeshExpectation, format?: MeshFormat): Promise<BufferGeometry>;
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
/**
|
|
119
|
+
* The thread to draw in a hole, read from its Engine datasheet: a hole's
|
|
120
|
+
* `facts.threading.spec`, which the Engine fills for a modeled thread it
|
|
121
|
+
* took for a catalog thread (ISO 6H, ANSI 2B, straight pipe). `null` for a
|
|
122
|
+
* plain hole, any other feature, and a datasheet of an unexpected shape.
|
|
123
|
+
*
|
|
124
|
+
* Only what the Engine sends is drawn: a thread it did not report is not
|
|
125
|
+
* guessed at from the mesh.
|
|
126
|
+
*/
|
|
127
|
+
declare function threadMark(feature: PartModelFeature, datasheet: unknown): ThreadMark | null;
|
|
128
|
+
|
|
129
|
+
export { type CacheablePart, type EngineGeometryCache, type EngineGeometryResource, type FetchLike, type LoadPartGeometryOptions, type MeshAsset, type MeshFormat, PartMeshError, type PartMeshExpectation, createEngineGeometryCache, engineGeometryCache, engineGeometryResourceKey, loadPartGeometry, loadPartMesh, parsePartGeometry, partMeshAssets, threadMark };
|
package/dist/engine/index.js
CHANGED
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
normalizePartReport,
|
|
12
12
|
parsePartGeometry,
|
|
13
13
|
partMeshAssets,
|
|
14
|
-
smoothRegionNormals
|
|
15
|
-
|
|
14
|
+
smoothRegionNormals,
|
|
15
|
+
threadMark
|
|
16
|
+
} from "../chunk-LBG6GVGE.js";
|
|
16
17
|
export {
|
|
17
18
|
EnginePart,
|
|
18
19
|
MIN_KERNEL_VERSION,
|
|
@@ -26,5 +27,6 @@ export {
|
|
|
26
27
|
normalizePartReport,
|
|
27
28
|
parsePartGeometry,
|
|
28
29
|
partMeshAssets,
|
|
29
|
-
smoothRegionNormals
|
|
30
|
+
smoothRegionNormals,
|
|
31
|
+
threadMark
|
|
30
32
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PartModel, F as FeatureTag,
|
|
2
|
-
export { A as AXES_PLANE_OFFSET,
|
|
1
|
+
import { P as PartModel, F as FeatureTag, e as PartPick, V as Vec3, R as RegionHighlight, f as ViewerTheme, S as SectionOptions, g as SectionState, h as ViewerCamera, i as ViewportSize, j as PartModelRegion, C as CameraLimits, k as ViewerView, l as Projection, m as TriangleRange, o as RegionIndex, b as PartModelFeature, p as FeatureType } from './normalize-BBADLCmd.js';
|
|
2
|
+
export { A as AXES_PLANE_OFFSET, q as AXES_PLANE_SCALE, r as AxesPlane, B as BuildPickInput, t as CAD_CAMERA_UP, u as CANDIDATE_WEIGHT, v as CAP_HATCH, D as DEFAULT_FIT_MARGIN, w as DEFAULT_SECTION_NORMAL, x as DEFAULT_THEME, y as DIRECTION_COLORS, z as DISABLED_SECTION, G as EXCLUDE_FROM_FRAME, E as EnginePart, H as FeatureHighlight, I as FocusOptions, J as HANDLE_PIXELS, K as HIGHLIGHT_COLORS, L as HIGHLIGHT_WEIGHT, N as HOVER_WEIGHT, O as HighlightLayers, Q as KnownFeatureType, U as KnownShapeKind, W as MAX_FRAME_RATIO, X as MIN_FRAME_RATIO, Y as NO_MODIFIERS, Z as OUTLINE_SCALE, _ as PERSPECTIVE_FOV, $ as PICKED_SURFACE_LABEL, a0 as PREVIEW_SCALE, a1 as PartDisplay, a2 as PartMesh, a3 as PartMeshProps, a as PartMeshRefs, a4 as PartObject, a5 as PickModifiers, a6 as PointerLocation, a7 as REGION_ATTRIBUTE, a8 as RegionPaint, a9 as SECTION_GIZMO_FRAME_SCALE, aa as SECTION_RENDER_ORDER, ab as SceneBounds, ac as SectionAnchor, ad as SectionBounds, ae as SectionPlacement, af as ShapeKind, ag as SharpCorner, ah as SharpCornerOptions, ai as SurfaceCreases, aj as SurfaceHit, T as ThreadMark, ak as ThreadOptions, al as adaptedUp, am as applyHighlightLayers, an as applyProjection, ao as aspectRatio, ap as axesPlanes, aq as boundsFromBox, ar as buildPick, as as buildRegionAttribute, at as buildRegionTexels, au as cadViewDirections, av as cameraLimits, aw as contentBounds, ax as createPart, ay as currentViewDirection, az as defaultBounds, aA as directionColor, aB as dragPlane, aC as excludedFromFrame, aD as findSharpCorners, aE as fitDistance, aF as focusForPick, aG as hitUnderRay, n as normalizePartReport, aH as orthographicHalfHeight, aI as perspectiveFitDistance, aJ as pickedStartDepth, aK as resolveTheme, aL as screenLength, aM as sectionAnchor, aN as sectionBounds, aO as sectionConstant, aP as sectionDepth, aQ as sectionDepthConstant, aR as sectionDepthRange, aS as sectionFromPick, aT as sectionMeasurement, aU as sectionOffset, aV as sectionOptionsFromState, aW as sectionPlane, aX as sharpCornerPositions, s as smoothRegionNormals, aY as startPosition, aZ as surfaceCreases, a_ as surfaceUnderRay, a$ as targetBoundary, b0 as themesEqual, b1 as viewDirection } from './normalize-BBADLCmd.js';
|
|
3
3
|
import { BufferGeometry, Box3, CanvasTexture, Plane, Vector3, Matrix4, Vector2, Ray, ShaderMaterial, Texture, Color, MeshBasicMaterial, WebGLRenderTarget } from 'three';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { ReactNode, PropsWithChildren, RefObject, CSSProperties } from 'react';
|
|
@@ -93,6 +93,8 @@ interface ViewerToolbarControls {
|
|
|
93
93
|
dfm?: ViewerToolbarControl;
|
|
94
94
|
/** The DFM rules' sharp corners, drawn on the part: see `sharpCorners` on `<PartMesh>`. */
|
|
95
95
|
sharpCorners?: ViewerToolbarControl;
|
|
96
|
+
/** Threads drawn in their holes: see `threads` on `<PartMesh>`. */
|
|
97
|
+
threads?: ViewerToolbarControl;
|
|
96
98
|
hover?: ViewerToolbarControl;
|
|
97
99
|
focus?: ViewerToolbarControl;
|
|
98
100
|
wireframe?: ViewerToolbarControl;
|
|
@@ -136,6 +138,7 @@ declare const ViewerToolbar: (({ className, children }: ViewerToolbarProps) => r
|
|
|
136
138
|
DirectionsButton: () => react.JSX.Element;
|
|
137
139
|
DfmButton: () => react.JSX.Element;
|
|
138
140
|
SharpCornersButton: () => react.JSX.Element;
|
|
141
|
+
ThreadsButton: () => react.JSX.Element;
|
|
139
142
|
HoverButton: () => react.JSX.Element;
|
|
140
143
|
FocusButton: () => react.JSX.Element;
|
|
141
144
|
WireframeButton: () => react.JSX.Element;
|
|
@@ -1501,6 +1504,13 @@ interface ViewerProps {
|
|
|
1501
1504
|
* section handle. The usual meaning is "put the selection down".
|
|
1502
1505
|
*/
|
|
1503
1506
|
onPointerMissed?: () => void;
|
|
1507
|
+
/**
|
|
1508
|
+
* What to show in place of the canvas when the browser will not create a
|
|
1509
|
+
* WebGL context — hardware acceleration off, or the GPU blocklisted. It goes
|
|
1510
|
+
* inside a `role="alert"` element (`viewer-webgl-unavailable`), and replaces
|
|
1511
|
+
* the default message asking the user to turn acceleration on.
|
|
1512
|
+
*/
|
|
1513
|
+
webglUnavailable?: ReactNode;
|
|
1504
1514
|
}
|
|
1505
1515
|
declare const Viewer: react.ForwardRefExoticComponent<ViewerProps & react.RefAttributes<ViewerHandle>>;
|
|
1506
1516
|
|
package/dist/index.js
CHANGED
|
@@ -163,7 +163,7 @@ import {
|
|
|
163
163
|
viewUp,
|
|
164
164
|
viewVector,
|
|
165
165
|
visualSurfaces
|
|
166
|
-
} from "./chunk-
|
|
166
|
+
} from "./chunk-LBG6GVGE.js";
|
|
167
167
|
|
|
168
168
|
// src/render/feature-triangles.ts
|
|
169
169
|
var EMPTY = { positions: new Float32Array(), normals: new Float32Array() };
|
|
@@ -274,6 +274,7 @@ var SharpCornersIcon = () => /* @__PURE__ */ jsxs(SvgIcon, { children: [
|
|
|
274
274
|
/* @__PURE__ */ jsx2("path", { d: "M4 3v17h17" }),
|
|
275
275
|
/* @__PURE__ */ jsx2("circle", { cx: "9", cy: "15", r: "4" })
|
|
276
276
|
] });
|
|
277
|
+
var ThreadsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M8 3h8v18H8zM8 8l8-2M8 12l8-2M8 16l8-2M8 20l8-2" }) });
|
|
277
278
|
var HoverIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v12H9l-5 4zm4 5h8m-8 3h5" }) });
|
|
278
279
|
var FocusIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v16H4zM8 8h8v8H8zM2 12h4m12 0h4M12 2v4m0 12v4" }) });
|
|
279
280
|
var WireframeIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 7l8-4 8 4v10l-8 4-8-4zM4 7l8 4 8-4M12 11v10M4 17l8-4 8 4M12 3v10" }) });
|
|
@@ -291,6 +292,7 @@ var ICONS = {
|
|
|
291
292
|
directions: DirectionsIcon,
|
|
292
293
|
dfm: DfmIcon,
|
|
293
294
|
sharpCorners: SharpCornersIcon,
|
|
295
|
+
threads: ThreadsIcon,
|
|
294
296
|
hover: HoverIcon,
|
|
295
297
|
focus: FocusIcon,
|
|
296
298
|
wireframe: WireframeIcon,
|
|
@@ -401,6 +403,14 @@ var SharpCornersButton = () => /* @__PURE__ */ jsx3(
|
|
|
401
403
|
label: (pressed) => pressed ? "Hide sharp corners" : "Show sharp corners"
|
|
402
404
|
}
|
|
403
405
|
);
|
|
406
|
+
var ThreadsButton = () => /* @__PURE__ */ jsx3(
|
|
407
|
+
ToolbarButton,
|
|
408
|
+
{
|
|
409
|
+
action: "threads",
|
|
410
|
+
icon: "threads",
|
|
411
|
+
label: (pressed) => pressed ? "Hide threads" : "Show threads"
|
|
412
|
+
}
|
|
413
|
+
);
|
|
404
414
|
var HoverButton = () => /* @__PURE__ */ jsx3(
|
|
405
415
|
ToolbarButton,
|
|
406
416
|
{
|
|
@@ -461,6 +471,7 @@ var DefaultControls = () => {
|
|
|
461
471
|
controls.directions ? /* @__PURE__ */ jsx3(DirectionsButton, {}, "directions") : null,
|
|
462
472
|
controls.dfm ? /* @__PURE__ */ jsx3(DfmButton, {}, "dfm") : null,
|
|
463
473
|
controls.sharpCorners ? /* @__PURE__ */ jsx3(SharpCornersButton, {}, "sharpCorners") : null,
|
|
474
|
+
controls.threads ? /* @__PURE__ */ jsx3(ThreadsButton, {}, "threads") : null,
|
|
464
475
|
controls.hover ? /* @__PURE__ */ jsx3(HoverButton, {}, "hover") : null,
|
|
465
476
|
controls.focus ? /* @__PURE__ */ jsx3(FocusButton, {}, "focus") : null,
|
|
466
477
|
controls.wireframe ? /* @__PURE__ */ jsx3(WireframeButton, {}, "wireframe") : null,
|
|
@@ -502,6 +513,7 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
|
|
|
502
513
|
DirectionsButton,
|
|
503
514
|
DfmButton,
|
|
504
515
|
SharpCornersButton,
|
|
516
|
+
ThreadsButton,
|
|
505
517
|
HoverButton,
|
|
506
518
|
FocusButton,
|
|
507
519
|
WireframeButton,
|
|
@@ -546,6 +546,39 @@ declare function findSharpCorners(creases: SurfaceCreases, model: Pick<PartModel
|
|
|
546
546
|
/** The flagged features' corners, as `x y z x y z` endpoint pairs. See {@link findSharpCorners}. */
|
|
547
547
|
declare function sharpCornerPositions(creases: SurfaceCreases, model: Pick<PartModel, 'features' | 'regionIndex'>, options: SharpCornerOptions): Float32Array;
|
|
548
548
|
|
|
549
|
+
/**
|
|
550
|
+
* Threads drawn into the bores they are cut in.
|
|
551
|
+
*
|
|
552
|
+
* The kernel cuts a modeled internal thread out of the body before it meshes
|
|
553
|
+
* it, so a tapped hole arrives as a plain bore. A displacement map cannot put
|
|
554
|
+
* the thread back: it moves vertices, and wants UVs and a dense mesh, where
|
|
555
|
+
* the Engine's has neither — a small bore is a handful of 30° facets. So the
|
|
556
|
+
* thread is drawn the way a bump map draws: the part's shader tilts each
|
|
557
|
+
* pixel's normal along the bore's axis as a helix of the thread's pitch runs
|
|
558
|
+
* past it, and the light falls as it would on the flanks of a real thread.
|
|
559
|
+
* The silhouette stays round, which inside a bore nobody sees.
|
|
560
|
+
*
|
|
561
|
+
* What the shader needs per region is the bore's axis, a point on it, and the
|
|
562
|
+
* pitch. The pitch comes from the caller; the axis and the point are fitted
|
|
563
|
+
* here from the region's own triangles, so the drawing sits on the mesh
|
|
564
|
+
* exactly and needs nothing from the report it does not already carry.
|
|
565
|
+
*/
|
|
566
|
+
/** One thread to draw, on the faces of the hole it is cut in. */
|
|
567
|
+
interface ThreadMark {
|
|
568
|
+
/** The regions of the threaded hole, as the report numbers them. */
|
|
569
|
+
readonly regions: readonly number[];
|
|
570
|
+
/** From one crest to the next, in mm. Negative for a left-hand thread. */
|
|
571
|
+
readonly pitch: number;
|
|
572
|
+
/**
|
|
573
|
+
* The thread's major diameter, in mm. A cylinder of the hole wider than
|
|
574
|
+
* this — a counterbore — is not threaded, and is left plain.
|
|
575
|
+
*/
|
|
576
|
+
readonly majorDiameter: number;
|
|
577
|
+
}
|
|
578
|
+
interface ThreadOptions {
|
|
579
|
+
readonly threads: readonly ThreadMark[];
|
|
580
|
+
}
|
|
581
|
+
|
|
549
582
|
/** The vertex attribute carrying each vertex's column in the state texture. */
|
|
550
583
|
declare const REGION_ATTRIBUTE = "aRegion";
|
|
551
584
|
type PartDisplay = 'solid' | 'wireframe';
|
|
@@ -610,6 +643,11 @@ interface PartObject {
|
|
|
610
643
|
}, camera: ViewerCamera, viewport: ViewportSize): SharpCorner | null;
|
|
611
644
|
/** Draws one corner up as the one under the pointer; `null` for none. */
|
|
612
645
|
setHoveredSharpCorner(index: number | null): void;
|
|
646
|
+
/**
|
|
647
|
+
* Draws threads into the bores they are cut in — see `render/threads.ts`.
|
|
648
|
+
* `null` draws none. Keyed by content, like `setSharpCorners`.
|
|
649
|
+
*/
|
|
650
|
+
setThreads(options: ThreadOptions | null): void;
|
|
613
651
|
dispose(): void;
|
|
614
652
|
}
|
|
615
653
|
interface RegionPaint {
|
|
@@ -1180,6 +1218,13 @@ interface PartMeshProps {
|
|
|
1180
1218
|
* Coloured by the theme's `sharpCorner`.
|
|
1181
1219
|
*/
|
|
1182
1220
|
sharpCorners?: SharpCornerOptions | null;
|
|
1221
|
+
/**
|
|
1222
|
+
* Draws threads into the bores they are cut in: each mark's pitch, as a
|
|
1223
|
+
* helix of light and shade over the hole's cylinders. The Engine cuts a
|
|
1224
|
+
* modeled internal thread out of the mesh, so without this a tapped hole is
|
|
1225
|
+
* a plain bore. Off when omitted or `null`. See `threadMark` in `/engine`.
|
|
1226
|
+
*/
|
|
1227
|
+
threads?: ThreadOptions | null;
|
|
1183
1228
|
/** Wireframe shows region boundaries and any hovered/painted faces, with no triangle diagonals. */
|
|
1184
1229
|
display?: PartDisplay;
|
|
1185
1230
|
/**
|
|
@@ -1198,7 +1243,7 @@ interface PartMeshProps {
|
|
|
1198
1243
|
* React render at all: the hovered face lives in a ref, and routing it through
|
|
1199
1244
|
* state is what this rewrite exists to stop doing.
|
|
1200
1245
|
*/
|
|
1201
|
-
declare const PartMesh: ({ model, geometry, selection, focus, candidates, highlights, regionHighlights, pickedRegions, hoveredFeatureIds, hover, activeDirection, section, onSectionChange, onAdjacency, focusFeature, onHover, onPick, theme, showEdges, sharpCorners, display, children, }: PartMeshProps) => react.JSX.Element;
|
|
1246
|
+
declare const PartMesh: ({ model, geometry, selection, focus, candidates, highlights, regionHighlights, pickedRegions, hoveredFeatureIds, hover, activeDirection, section, onSectionChange, onAdjacency, focusFeature, onHover, onPick, theme, showEdges, sharpCorners, threads, display, children, }: PartMeshProps) => react.JSX.Element;
|
|
1202
1247
|
|
|
1203
1248
|
interface EnginePartProps extends Omit<PartMeshProps, 'model' | 'geometry'> {
|
|
1204
1249
|
/**
|
|
@@ -1273,4 +1318,4 @@ declare function normalizePartReport(report: unknown): PartModel;
|
|
|
1273
1318
|
*/
|
|
1274
1319
|
declare function assertSupportedKernelVersion(kernelVersion: string): void;
|
|
1275
1320
|
|
|
1276
|
-
export {
|
|
1321
|
+
export { PICKED_SURFACE_LABEL as $, AXES_PLANE_OFFSET as A, type BuildPickInput as B, type CameraLimits as C, DEFAULT_FIT_MARGIN as D, EnginePart as E, type FeatureTag as F, EXCLUDE_FROM_FRAME as G, type FeatureHighlight as H, type FocusOptions as I, HANDLE_PIXELS as J, HIGHLIGHT_COLORS as K, HIGHLIGHT_WEIGHT as L, MIN_KERNEL_VERSION as M, HOVER_WEIGHT as N, type HighlightLayers as O, type PartModel as P, type KnownFeatureType as Q, type RegionHighlight as R, type SectionOptions as S, type ThreadMark as T, type KnownShapeKind as U, type Vec3 as V, MAX_FRAME_RATIO as W, MIN_FRAME_RATIO as X, NO_MODIFIERS as Y, OUTLINE_SCALE as Z, PERSPECTIVE_FOV as _, type PartMeshRefs as a, targetBoundary as a$, PREVIEW_SCALE as a0, type PartDisplay as a1, PartMesh as a2, type PartMeshProps as a3, type PartObject as a4, type PickModifiers as a5, type PointerLocation as a6, REGION_ATTRIBUTE as a7, type RegionPaint as a8, SECTION_GIZMO_FRAME_SCALE as a9, directionColor as aA, dragPlane as aB, excludedFromFrame as aC, findSharpCorners as aD, fitDistance as aE, focusForPick as aF, hitUnderRay as aG, orthographicHalfHeight as aH, perspectiveFitDistance as aI, pickedStartDepth as aJ, resolveTheme as aK, screenLength as aL, sectionAnchor as aM, sectionBounds as aN, sectionConstant as aO, sectionDepth as aP, sectionDepthConstant as aQ, sectionDepthRange as aR, sectionFromPick as aS, sectionMeasurement as aT, sectionOffset as aU, sectionOptionsFromState as aV, sectionPlane as aW, sharpCornerPositions as aX, startPosition as aY, surfaceCreases as aZ, surfaceUnderRay as a_, SECTION_RENDER_ORDER as aa, type SceneBounds as ab, type SectionAnchor as ac, type SectionBounds as ad, type SectionPlacement as ae, type ShapeKind as af, type SharpCorner as ag, type SharpCornerOptions as ah, type SurfaceCreases as ai, type SurfaceHit as aj, type ThreadOptions as ak, adaptedUp as al, applyHighlightLayers as am, applyProjection as an, aspectRatio as ao, axesPlanes as ap, boundsFromBox as aq, buildPick as ar, buildRegionAttribute as as, buildRegionTexels as at, cadViewDirections as au, cameraLimits as av, contentBounds as aw, createPart as ax, currentViewDirection as ay, defaultBounds as az, type PartModelFeature as b, themesEqual as b0, viewDirection as b1, type EnginePartProps as c, assertSupportedKernelVersion as d, type PartPick as e, type ViewerTheme as f, type SectionState as g, type ViewerCamera as h, type ViewportSize as i, type PartModelRegion as j, type ViewerView as k, type Projection as l, type TriangleRange as m, normalizePartReport as n, type RegionIndex as o, type FeatureType as p, AXES_PLANE_SCALE as q, type AxesPlane as r, smoothRegionNormals as s, CAD_CAMERA_UP as t, CANDIDATE_WEIGHT as u, CAP_HATCH as v, DEFAULT_SECTION_NORMAL as w, DEFAULT_THEME as x, DIRECTION_COLORS as y, DISABLED_SECTION as z };
|