@toolpath/viewer 1.5.0 → 2.1.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 +155 -25
- package/dist/{chunk-4TBLMR4Z.js → chunk-WTTOUG4P.js} +27 -14
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +224 -27
- package/dist/index.js +563 -111
- package/dist/{normalize-CvnV9Ggh.d.ts → normalize--FiXUhO4.d.ts} +7 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -152,6 +152,7 @@ import {
|
|
|
152
152
|
trackDoubleTaps,
|
|
153
153
|
trackTaps,
|
|
154
154
|
useContentBox,
|
|
155
|
+
usePartContext,
|
|
155
156
|
useRetarget,
|
|
156
157
|
useSectionStore,
|
|
157
158
|
useTapGuard,
|
|
@@ -161,7 +162,50 @@ import {
|
|
|
161
162
|
viewUp,
|
|
162
163
|
viewVector,
|
|
163
164
|
visualSurfaces
|
|
164
|
-
} from "./chunk-
|
|
165
|
+
} from "./chunk-WTTOUG4P.js";
|
|
166
|
+
|
|
167
|
+
// src/render/feature-triangles.ts
|
|
168
|
+
var EMPTY = { positions: new Float32Array(), normals: new Float32Array() };
|
|
169
|
+
function featureTriangles(model, geometry, tag) {
|
|
170
|
+
const position = geometry.getAttribute("position");
|
|
171
|
+
if (!position) return EMPTY;
|
|
172
|
+
const normal = geometry.getAttribute("normal");
|
|
173
|
+
const index = geometry.getIndex();
|
|
174
|
+
const vertex = (corner) => index ? index.getX(corner) : corner;
|
|
175
|
+
const triangleCount = Math.floor((index ? index.count : position.count) / 3);
|
|
176
|
+
const positions = [];
|
|
177
|
+
const normals = [];
|
|
178
|
+
for (const region of new Set(model.regionIndex.regionsForFeature(tag))) {
|
|
179
|
+
const range = model.regionIndex.rangeForRegion(region);
|
|
180
|
+
if (!range) continue;
|
|
181
|
+
for (let triangle = range.start; triangle < Math.min(range.end, triangleCount); triangle++) {
|
|
182
|
+
const corners = [vertex(triangle * 3), vertex(triangle * 3 + 1), vertex(triangle * 3 + 2)];
|
|
183
|
+
const at = corners.map((corner) => [
|
|
184
|
+
position.getX(corner),
|
|
185
|
+
position.getY(corner),
|
|
186
|
+
position.getZ(corner)
|
|
187
|
+
]);
|
|
188
|
+
const out = normal ? corners.reduce(
|
|
189
|
+
(sum, corner) => [
|
|
190
|
+
sum[0] + normal.getX(corner),
|
|
191
|
+
sum[1] + normal.getY(corner),
|
|
192
|
+
sum[2] + normal.getZ(corner)
|
|
193
|
+
],
|
|
194
|
+
[0, 0, 0]
|
|
195
|
+
) : winding(at);
|
|
196
|
+
const length = Math.hypot(out[0], out[1], out[2]);
|
|
197
|
+
if (length === 0) continue;
|
|
198
|
+
positions.push(...at[0], ...at[1], ...at[2]);
|
|
199
|
+
normals.push(out[0] / length, out[1] / length, out[2] / length);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { positions: new Float32Array(positions), normals: new Float32Array(normals) };
|
|
203
|
+
}
|
|
204
|
+
var winding = ([a, b, c]) => {
|
|
205
|
+
const u = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
|
|
206
|
+
const v = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
|
|
207
|
+
return [u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]];
|
|
208
|
+
};
|
|
165
209
|
|
|
166
210
|
// src/hover-card.tsx
|
|
167
211
|
import { useEffect, useState } from "react";
|
|
@@ -222,6 +266,7 @@ var StockIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */
|
|
|
222
266
|
var AxesIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M5 19V3m0 16h16M5 19l10-10M2 6l3-3 3 3m10 10 3 3-3 3M11 9h4v4" }) });
|
|
223
267
|
var GridIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 8l9-5 9 5-9 5zM3 8v8l9 5 9-5V8M12 13v8M7.5 5.5v8M16.5 5.5v8" }) });
|
|
224
268
|
var BananaIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { viewBox: "0 0 576 512", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M284.2 245.6c12.99 6.929 25.35 15.14 36.08 25.8L334.5 285.6l65.75-23.47c14.75-5.265 30.18-7.849 45.81-8.389c1.154-10.73 1.764-21.38 1.764-31.87c0-118.5-81.33-221.9-119.7-221.9c-21.01 0-40.91 17.04-40.91 39.25c0 16.18 16.74 41.9 16.74 103C303.1 170.1 300.6 203.1 284.2 245.6zM575.1 389.6c0-3.687-.8637-7.429-2.687-10.93l-15.12-29.11c-21.05-40.53-63.08-64.51-106.9-64.51c-13.43 0-27.02 2.252-40.22 6.969l-84.84 30.27l-28.59-28.41C274.4 270.9 243.7 258.1 212.8 258.1c-23.72 0-47.57 6.97-68.29 21.2L106.3 306.4c-6.732 4.631-10.35 12.07-10.35 19.63c0 14.93 12.7 23.87 24.04 23.87c4.695 0 9.443-1.376 13.61-4.26l38.13-26.23c12.43-8.525 26.71-12.69 40.91-12.69c10.64 0 21.24 2.339 30.97 6.934c-50.62 62.23-128.3 99.85-211.4 99.85C14.42 413.5 0 427.8 0 445.5v31.38c0 17.68 14.66 32.02 32.46 32.02l28.98.0009c14.15 0 34.69 1.098 59.07 1.098c93.51 0 243.4-16.15 304.8-172.4c9.021-3.22 17.95-4.712 26.53-4.712c27.65 0 51.7 15.49 63.7 38.55l15.12 29.11c3.484 6.723 11.74 12.98 21.41 12.98C564.1 413.6 575.1 403.8 575.1 389.6z" }) });
|
|
269
|
+
var ToolsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M9 2h6v7H9zM9.5 9v9.5l2.5 3.5 2.5-3.5V9M9.5 12l5 2.5M9.5 15.5l5 2.5" }) });
|
|
225
270
|
var DirectionsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M7 3v18m-4-4 4 4 4-4M17 21V3m-4 4 4-4 4 4" }) });
|
|
226
271
|
var HoverIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v12H9l-5 4zm4 5h8m-8 3h5" }) });
|
|
227
272
|
var FocusIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v16H4zM8 8h8v8H8zM2 12h4m12 0h4M12 2v4m0 12v4" }) });
|
|
@@ -236,6 +281,7 @@ var ICONS = {
|
|
|
236
281
|
axes: AxesIcon,
|
|
237
282
|
grid: GridIcon,
|
|
238
283
|
banana: BananaIcon,
|
|
284
|
+
tools: ToolsIcon,
|
|
239
285
|
directions: DirectionsIcon,
|
|
240
286
|
hover: HoverIcon,
|
|
241
287
|
focus: FocusIcon,
|
|
@@ -315,6 +361,14 @@ var BananaButton = () => /* @__PURE__ */ jsx3(
|
|
|
315
361
|
label: (pressed) => pressed ? "Banana for scale (on)" : "Banana for scale"
|
|
316
362
|
}
|
|
317
363
|
);
|
|
364
|
+
var ToolsButton = () => /* @__PURE__ */ jsx3(
|
|
365
|
+
ToolbarButton,
|
|
366
|
+
{
|
|
367
|
+
action: "tools",
|
|
368
|
+
icon: "tools",
|
|
369
|
+
label: (pressed) => pressed ? "Hide tools on the part" : "Show tools on the part"
|
|
370
|
+
}
|
|
371
|
+
);
|
|
318
372
|
var DirectionsButton = () => /* @__PURE__ */ jsx3(
|
|
319
373
|
ToolbarButton,
|
|
320
374
|
{
|
|
@@ -376,7 +430,8 @@ var DefaultControls = () => {
|
|
|
376
430
|
controls.stock ? /* @__PURE__ */ jsx3(StockButton, {}, "stock") : null,
|
|
377
431
|
controls.axes ? /* @__PURE__ */ jsx3(AxesButton, {}, "axes") : null,
|
|
378
432
|
controls.grid ? /* @__PURE__ */ jsx3(GridButton, {}, "grid") : null,
|
|
379
|
-
controls.banana ? /* @__PURE__ */ jsx3(BananaButton, {}, "banana") : null
|
|
433
|
+
controls.banana ? /* @__PURE__ */ jsx3(BananaButton, {}, "banana") : null,
|
|
434
|
+
controls.tools ? /* @__PURE__ */ jsx3(ToolsButton, {}, "tools") : null
|
|
380
435
|
].filter(Boolean);
|
|
381
436
|
const analysis = [
|
|
382
437
|
controls.directions ? /* @__PURE__ */ jsx3(DirectionsButton, {}, "directions") : null,
|
|
@@ -417,6 +472,7 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
|
|
|
417
472
|
AxesButton,
|
|
418
473
|
GridButton,
|
|
419
474
|
BananaButton,
|
|
475
|
+
ToolsButton,
|
|
420
476
|
DirectionsButton,
|
|
421
477
|
HoverButton,
|
|
422
478
|
FocusButton,
|
|
@@ -430,20 +486,105 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
|
|
|
430
486
|
|
|
431
487
|
// src/stock.tsx
|
|
432
488
|
import { useThree } from "@react-three/fiber";
|
|
433
|
-
import { useEffect as useEffect2, useLayoutEffect, useMemo } from "react";
|
|
434
|
-
|
|
489
|
+
import { useEffect as useEffect2, useLayoutEffect, useMemo, useState as useState2 } from "react";
|
|
490
|
+
|
|
491
|
+
// src/render/stock-mesh.ts
|
|
492
|
+
import { Mesh } from "three";
|
|
493
|
+
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
494
|
+
async function parseStockGlb(data) {
|
|
495
|
+
const { scene } = await new GLTFLoader().parseAsync(data, "");
|
|
496
|
+
try {
|
|
497
|
+
return stockMeshGeometries(scene);
|
|
498
|
+
} finally {
|
|
499
|
+
scene.traverse((object) => {
|
|
500
|
+
if (!(object instanceof Mesh)) return;
|
|
501
|
+
object.geometry.dispose();
|
|
502
|
+
for (const material of Array.isArray(object.material) ? object.material : [object.material]) {
|
|
503
|
+
material.dispose();
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
function stockMeshGeometries(scene) {
|
|
509
|
+
const geometries = [];
|
|
510
|
+
scene.updateWorldMatrix(true, true);
|
|
511
|
+
scene.traverse((object) => {
|
|
512
|
+
if (!(object instanceof Mesh)) return;
|
|
513
|
+
const geometry = object.geometry.clone().applyMatrix4(object.matrixWorld);
|
|
514
|
+
if (!geometry.hasAttribute("normal")) geometry.computeVertexNormals();
|
|
515
|
+
geometries.push(geometry);
|
|
516
|
+
});
|
|
517
|
+
if (geometries.length === 0) throw new Error("The stock GLB contains no mesh.");
|
|
518
|
+
return geometries;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/render/stock-mesh-buffer.ts
|
|
522
|
+
function createStockMeshBuffer() {
|
|
523
|
+
const owned = /* @__PURE__ */ new Set();
|
|
524
|
+
let disposed = false;
|
|
525
|
+
return {
|
|
526
|
+
request(decode, ready, failed) {
|
|
527
|
+
disposed = false;
|
|
528
|
+
let active = true;
|
|
529
|
+
void decode().then(
|
|
530
|
+
(geometries) => {
|
|
531
|
+
if (!active || disposed) {
|
|
532
|
+
geometries.forEach((geometry) => geometry.dispose());
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
geometries.forEach((geometry) => owned.add(geometry));
|
|
536
|
+
ready(geometries);
|
|
537
|
+
},
|
|
538
|
+
(cause) => {
|
|
539
|
+
if (active && !disposed) {
|
|
540
|
+
failed(cause);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
);
|
|
544
|
+
return () => {
|
|
545
|
+
active = false;
|
|
546
|
+
};
|
|
547
|
+
},
|
|
548
|
+
commit(geometries) {
|
|
549
|
+
const visible = new Set(geometries);
|
|
550
|
+
for (const geometry of owned) {
|
|
551
|
+
if (!visible.has(geometry)) {
|
|
552
|
+
owned.delete(geometry);
|
|
553
|
+
geometry.dispose();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
dispose() {
|
|
558
|
+
disposed = true;
|
|
559
|
+
owned.forEach((geometry) => geometry.dispose());
|
|
560
|
+
owned.clear();
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
}
|
|
435
564
|
|
|
436
565
|
// src/render/stock.ts
|
|
437
566
|
import {
|
|
438
567
|
Box3,
|
|
568
|
+
BoxGeometry,
|
|
569
|
+
CylinderGeometry,
|
|
439
570
|
EdgesGeometry,
|
|
440
571
|
Group,
|
|
441
572
|
LineBasicMaterial,
|
|
442
573
|
LineSegments,
|
|
443
|
-
|
|
574
|
+
Matrix4,
|
|
575
|
+
Mesh as Mesh2,
|
|
444
576
|
MeshLambertMaterial,
|
|
577
|
+
Quaternion,
|
|
445
578
|
Vector3
|
|
446
579
|
} from "three";
|
|
580
|
+
function boxFigure(bounds) {
|
|
581
|
+
return {
|
|
582
|
+
lower: { x: bounds.min.x, y: bounds.min.y, z: bounds.min.z },
|
|
583
|
+
upper: { x: bounds.max.x, y: bounds.max.y, z: bounds.max.z }
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
var CYLINDER_SEGMENTS = 64;
|
|
587
|
+
var CYLINDER_AXIS = new Vector3(0, 1, 0);
|
|
447
588
|
function fixedBoxStockBounds(geometry, dimensions, position = "model_centered", positionOffset = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
448
589
|
if (![dimensions.x, dimensions.y, dimensions.z].every(Number.isFinite) || [dimensions.x, dimensions.y, dimensions.z].some((value) => value <= 0)) {
|
|
449
590
|
throw new RangeError("Fixed stock dimensions must be finite and positive.");
|
|
@@ -456,13 +597,97 @@ function fixedBoxStockBounds(geometry, dimensions, position = "model_centered",
|
|
|
456
597
|
}
|
|
457
598
|
const part = partBounds(geometry);
|
|
458
599
|
const center = part.getCenter(new Vector3());
|
|
459
|
-
const z =
|
|
460
|
-
const half = new Vector3(dimensions.x / 2, dimensions.y / 2, dimensions.z / 2);
|
|
600
|
+
const z = fixedStockLowerZ(part, dimensions.z, position, positionOffset);
|
|
461
601
|
return new Box3(
|
|
462
|
-
new Vector3(center.x, center.y, z)
|
|
463
|
-
new Vector3(center.x, center.y, z
|
|
602
|
+
new Vector3(center.x - dimensions.x / 2, center.y - dimensions.y / 2, z),
|
|
603
|
+
new Vector3(center.x + dimensions.x / 2, center.y + dimensions.y / 2, z + dimensions.z)
|
|
464
604
|
).translate(new Vector3(offset.x, offset.y, offset.z));
|
|
465
605
|
}
|
|
606
|
+
function fixedCylinderStock(geometry, size, position = "model_centered", positionOffset = 0) {
|
|
607
|
+
if (![size.diameter, size.length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
608
|
+
throw new RangeError("Fixed stock diameter and length must be finite and positive.");
|
|
609
|
+
}
|
|
610
|
+
if (!Number.isFinite(positionOffset)) {
|
|
611
|
+
throw new RangeError("Fixed stock position offset must be finite.");
|
|
612
|
+
}
|
|
613
|
+
const part = partBounds(geometry);
|
|
614
|
+
const center = part.getCenter(new Vector3());
|
|
615
|
+
return {
|
|
616
|
+
origin: {
|
|
617
|
+
x: center.x,
|
|
618
|
+
y: center.y,
|
|
619
|
+
z: fixedStockLowerZ(part, size.length, position, positionOffset)
|
|
620
|
+
},
|
|
621
|
+
axis: { x: 0, y: 0, z: 1 },
|
|
622
|
+
diameter: size.diameter,
|
|
623
|
+
length: size.length
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function fixedStockLowerZ(part, length, position, positionOffset) {
|
|
627
|
+
if (position === "offset_from_top") return part.max.z + positionOffset - length;
|
|
628
|
+
if (position === "offset_from_bottom") return part.min.z - positionOffset;
|
|
629
|
+
return (part.min.z + part.max.z - length) / 2;
|
|
630
|
+
}
|
|
631
|
+
function cylinderStockGeometry(input) {
|
|
632
|
+
const figure = input.partGeometry ? fixedCylinderStock(input.partGeometry, input, input.position, input.positionOffset) : input;
|
|
633
|
+
const { origin, diameter, length } = figure;
|
|
634
|
+
if (![origin.x, origin.y, origin.z].every(Number.isFinite)) {
|
|
635
|
+
throw new RangeError("Cylinder stock origin must be finite.");
|
|
636
|
+
}
|
|
637
|
+
if (![diameter, length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
638
|
+
throw new RangeError("Cylinder stock diameter and length must be finite and positive.");
|
|
639
|
+
}
|
|
640
|
+
const axis = new Vector3(figure.axis.x, figure.axis.y, figure.axis.z);
|
|
641
|
+
const norm = axis.length();
|
|
642
|
+
if (!Number.isFinite(norm) || norm === 0) {
|
|
643
|
+
throw new RangeError("Cylinder stock axis must be finite and non-zero.");
|
|
644
|
+
}
|
|
645
|
+
axis.divideScalar(norm);
|
|
646
|
+
const center = axis.clone().multiplyScalar(length / 2).add(new Vector3(origin.x, origin.y, origin.z));
|
|
647
|
+
return new CylinderGeometry(diameter / 2, diameter / 2, length, CYLINDER_SEGMENTS).applyQuaternion(new Quaternion().setFromUnitVectors(CYLINDER_AXIS, axis)).translate(center.x, center.y, center.z);
|
|
648
|
+
}
|
|
649
|
+
function boxStockGeometry(input) {
|
|
650
|
+
const figure = input.partGeometry ? boxFigure(
|
|
651
|
+
input.dimensions ? fixedBoxStockBounds(
|
|
652
|
+
input.partGeometry,
|
|
653
|
+
input.dimensions,
|
|
654
|
+
input.position,
|
|
655
|
+
input.positionOffset,
|
|
656
|
+
input.offset
|
|
657
|
+
) : boxStockBounds(input.partGeometry, input.allowance, input.offset)
|
|
658
|
+
) : input;
|
|
659
|
+
return orientedBoxStockGeometry(figure);
|
|
660
|
+
}
|
|
661
|
+
function orientedBoxStockGeometry(figure) {
|
|
662
|
+
const { lower, upper } = figure;
|
|
663
|
+
const frame = figure.frame ?? {
|
|
664
|
+
location: { x: 0, y: 0, z: 0 },
|
|
665
|
+
axis: { x: 0, y: 0, z: 1 },
|
|
666
|
+
refDirection: { x: 1, y: 0, z: 0 }
|
|
667
|
+
};
|
|
668
|
+
const { location, axis, refDirection } = frame;
|
|
669
|
+
if (![location, axis, refDirection, lower, upper].every(
|
|
670
|
+
(vector) => [vector.x, vector.y, vector.z].every(Number.isFinite)
|
|
671
|
+
)) {
|
|
672
|
+
throw new RangeError("Box stock frame and corners must be finite.");
|
|
673
|
+
}
|
|
674
|
+
const size = new Vector3(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
|
|
675
|
+
if (size.toArray().some((value) => !(value > 0))) {
|
|
676
|
+
throw new RangeError("Box stock upper corner must be above its lower corner on every axis.");
|
|
677
|
+
}
|
|
678
|
+
const z = new Vector3(axis.x, axis.y, axis.z).normalize();
|
|
679
|
+
const x = new Vector3(refDirection.x, refDirection.y, refDirection.z);
|
|
680
|
+
const reference = x.length();
|
|
681
|
+
x.addScaledVector(z, -x.dot(z));
|
|
682
|
+
if (!(z.lengthSq() > 0 && x.length() > 1e-9 * reference)) {
|
|
683
|
+
throw new RangeError(
|
|
684
|
+
"Box stock frame axis and reference direction must be non-zero and not parallel."
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
x.normalize();
|
|
688
|
+
const y = new Vector3().crossVectors(z, x);
|
|
689
|
+
return new BoxGeometry(size.x, size.y, size.z).translate((lower.x + upper.x) / 2, (lower.y + upper.y) / 2, (lower.z + upper.z) / 2).applyMatrix4(new Matrix4().makeBasis(x, y, z).setPosition(location.x, location.y, location.z));
|
|
690
|
+
}
|
|
466
691
|
function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
467
692
|
const padding = typeof allowance === "number" ? new Vector3(allowance, allowance, allowance) : "wall" in allowance ? new Vector3(allowance.wall, allowance.wall, allowance.floor) : new Vector3(allowance.x, allowance.y, allowance.z);
|
|
468
693
|
if (padding.toArray().some((value) => !Number.isFinite(value) || value < 0)) {
|
|
@@ -494,81 +719,167 @@ function partBounds(geometry) {
|
|
|
494
719
|
}
|
|
495
720
|
return box;
|
|
496
721
|
}
|
|
497
|
-
function createStock(geometry) {
|
|
722
|
+
function createStock(geometry, legacyWorkpiece = false) {
|
|
498
723
|
const material = new MeshLambertMaterial({ transparent: true, depthWrite: false });
|
|
499
724
|
const edgeMaterial = new LineBasicMaterial({ transparent: true, depthWrite: false });
|
|
500
|
-
const mesh = new
|
|
501
|
-
const edgeGeometry = new EdgesGeometry(geometry, 15);
|
|
502
|
-
const edges = new LineSegments(edgeGeometry, edgeMaterial);
|
|
725
|
+
const mesh = new Mesh2(geometry, material);
|
|
503
726
|
const object = new Group();
|
|
504
727
|
object.userData[STOCK_OBJECT] = true;
|
|
505
|
-
mesh.renderOrder = 5;
|
|
506
|
-
edges.renderOrder = 6;
|
|
728
|
+
mesh.renderOrder = legacyWorkpiece ? 2 : 5;
|
|
507
729
|
mesh.raycast = () => {
|
|
508
730
|
};
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
731
|
+
object.add(mesh);
|
|
732
|
+
if (legacyWorkpiece) {
|
|
733
|
+
const shadow = mesh.clone();
|
|
734
|
+
shadow.raycast = () => {
|
|
735
|
+
};
|
|
736
|
+
object.add(shadow);
|
|
737
|
+
edgeMaterial.depthWrite = true;
|
|
738
|
+
}
|
|
739
|
+
let edges = null;
|
|
512
740
|
return {
|
|
513
741
|
object,
|
|
514
742
|
material,
|
|
515
743
|
edgeMaterial,
|
|
516
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Shows or hides the outline, building it the first time it is shown: the
|
|
746
|
+
* edge pass reads every triangle, and stock cut by a toolpath has a great
|
|
747
|
+
* many of them.
|
|
748
|
+
*/
|
|
749
|
+
showEdges(show) {
|
|
750
|
+
if (show && !edges) {
|
|
751
|
+
edges = new LineSegments(new EdgesGeometry(geometry, 15), edgeMaterial);
|
|
752
|
+
edges.renderOrder = legacyWorkpiece ? 1 : 6;
|
|
753
|
+
edges.raycast = () => {
|
|
754
|
+
};
|
|
755
|
+
object.add(edges);
|
|
756
|
+
}
|
|
757
|
+
if (edges) edges.visible = show;
|
|
758
|
+
},
|
|
517
759
|
dispose() {
|
|
518
760
|
material.dispose();
|
|
519
761
|
edgeMaterial.dispose();
|
|
520
|
-
|
|
762
|
+
edges?.geometry.dispose();
|
|
521
763
|
}
|
|
522
764
|
};
|
|
523
765
|
}
|
|
524
766
|
|
|
525
767
|
// src/stock.tsx
|
|
526
768
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
527
|
-
var Stock = ({
|
|
769
|
+
var Stock = ({ geometry, glb, box, cylinder, ...appearance }) => geometry ? /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance }) : glb ? /* @__PURE__ */ jsx4(GlbStock, { glb, ...appearance }) : /* @__PURE__ */ jsx4(ShapeStock, { box, cylinder, ...appearance });
|
|
770
|
+
var GeometryStock = ({
|
|
528
771
|
geometry,
|
|
529
|
-
|
|
772
|
+
renderStyle = "overlay",
|
|
773
|
+
color = renderStyle === "legacy-workpiece" ? 16777215 : 12176354,
|
|
774
|
+
emissive = renderStyle === "legacy-workpiece" ? 3948625 : 0,
|
|
775
|
+
flatShading = renderStyle === "legacy-workpiece",
|
|
530
776
|
opacity = 0.2,
|
|
531
|
-
edgeColor = 11058648,
|
|
532
|
-
edgeOpacity = 0.75,
|
|
777
|
+
edgeColor = renderStyle === "legacy-workpiece" ? 0 : 11058648,
|
|
778
|
+
edgeOpacity = renderStyle === "legacy-workpiece" ? 0.5 : 0.75,
|
|
533
779
|
showEdges = true
|
|
534
780
|
}) => {
|
|
535
781
|
const invalidate = useThree((state) => state.invalidate);
|
|
536
|
-
const stock = useMemo(
|
|
782
|
+
const stock = useMemo(
|
|
783
|
+
() => createStock(geometry, renderStyle === "legacy-workpiece"),
|
|
784
|
+
[geometry, renderStyle]
|
|
785
|
+
);
|
|
537
786
|
useEffect2(() => () => stock.dispose(), [stock]);
|
|
538
787
|
useLayoutEffect(() => {
|
|
539
788
|
stock.material.color.setHex(color);
|
|
789
|
+
stock.material.emissive.setHex(emissive);
|
|
790
|
+
if (stock.material.flatShading !== flatShading) {
|
|
791
|
+
stock.material.flatShading = flatShading;
|
|
792
|
+
stock.material.needsUpdate = true;
|
|
793
|
+
}
|
|
540
794
|
stock.material.opacity = opacity;
|
|
795
|
+
stock.material.depthWrite = renderStyle === "legacy-workpiece" && opacity === 1;
|
|
796
|
+
stock.material.polygonOffset = renderStyle === "legacy-workpiece" && opacity === 1;
|
|
797
|
+
stock.material.polygonOffsetFactor = 1;
|
|
798
|
+
stock.material.polygonOffsetUnits = 1;
|
|
541
799
|
stock.edgeMaterial.color.setHex(edgeColor);
|
|
542
800
|
stock.edgeMaterial.opacity = edgeOpacity;
|
|
543
|
-
stock.
|
|
801
|
+
stock.showEdges(showEdges);
|
|
544
802
|
invalidate();
|
|
545
|
-
}, [
|
|
803
|
+
}, [
|
|
804
|
+
color,
|
|
805
|
+
emissive,
|
|
806
|
+
flatShading,
|
|
807
|
+
edgeColor,
|
|
808
|
+
edgeOpacity,
|
|
809
|
+
invalidate,
|
|
810
|
+
opacity,
|
|
811
|
+
renderStyle,
|
|
812
|
+
showEdges,
|
|
813
|
+
stock
|
|
814
|
+
]);
|
|
546
815
|
return /* @__PURE__ */ jsx4("primitive", { object: stock.object, dispose: null });
|
|
547
816
|
};
|
|
548
|
-
var
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
817
|
+
var GlbStock = ({
|
|
818
|
+
glb,
|
|
819
|
+
retainPrevious = false,
|
|
820
|
+
...appearance
|
|
821
|
+
}) => {
|
|
822
|
+
const buffer = useMemo(() => createStockMeshBuffer(), []);
|
|
823
|
+
const [decoded, setDecoded] = useState2(null);
|
|
824
|
+
useEffect2(() => () => buffer.dispose(), [buffer]);
|
|
825
|
+
useLayoutEffect(() => {
|
|
826
|
+
buffer.commit(decoded?.geometries ?? []);
|
|
827
|
+
}, [buffer, decoded]);
|
|
828
|
+
useEffect2(
|
|
829
|
+
() => buffer.request(
|
|
830
|
+
() => parseStockGlb(glb),
|
|
831
|
+
(geometries) => {
|
|
832
|
+
setDecoded({ source: glb, geometries });
|
|
833
|
+
},
|
|
834
|
+
(cause) => {
|
|
835
|
+
setDecoded({
|
|
836
|
+
source: glb,
|
|
837
|
+
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
),
|
|
841
|
+
[buffer, glb]
|
|
842
|
+
);
|
|
843
|
+
if (!decoded || !retainPrevious && decoded.source !== glb) return null;
|
|
844
|
+
if (decoded.error) throw decoded.error;
|
|
845
|
+
return decoded.geometries?.map((geometry) => /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance }, geometry.uuid));
|
|
846
|
+
};
|
|
847
|
+
var keepNonFinite = (_key, value) => typeof value === "number" && !Number.isFinite(value) ? String(value) : value;
|
|
848
|
+
var restoreNonFinite = (_key, value) => value === "NaN" || value === "Infinity" || value === "-Infinity" ? Number(value) : value;
|
|
849
|
+
var ShapeStock = ({
|
|
850
|
+
box,
|
|
851
|
+
cylinder,
|
|
852
|
+
...appearance
|
|
556
853
|
}) => {
|
|
557
|
-
const
|
|
558
|
-
const
|
|
559
|
-
const
|
|
854
|
+
const isBox = box !== void 0;
|
|
855
|
+
const partGeometry = box?.partGeometry ?? cylinder?.partGeometry;
|
|
856
|
+
const figure = JSON.stringify(
|
|
857
|
+
box ? box.partGeometry ? {
|
|
858
|
+
dimensions: box.dimensions,
|
|
859
|
+
allowance: box.allowance,
|
|
860
|
+
offset: box.offset,
|
|
861
|
+
position: box.position,
|
|
862
|
+
positionOffset: box.positionOffset
|
|
863
|
+
} : { frame: box.frame, lower: box.lower, upper: box.upper } : cylinder.partGeometry ? {
|
|
864
|
+
diameter: cylinder.diameter,
|
|
865
|
+
length: cylinder.length,
|
|
866
|
+
position: cylinder.position,
|
|
867
|
+
positionOffset: cylinder.positionOffset
|
|
868
|
+
} : {
|
|
869
|
+
origin: cylinder.origin,
|
|
870
|
+
axis: cylinder.axis,
|
|
871
|
+
diameter: cylinder.diameter,
|
|
872
|
+
length: cylinder.length
|
|
873
|
+
},
|
|
874
|
+
keepNonFinite
|
|
875
|
+
);
|
|
560
876
|
const geometry = useMemo(() => {
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}) : boxStockBounds(partGeometry, allowance, { x: ox, y: oy, z: oz });
|
|
566
|
-
const size = box.getSize(new Vector32());
|
|
567
|
-
const center = box.getCenter(new Vector32());
|
|
568
|
-
return new BoxGeometry(size.x, size.y, size.z).translate(center.x, center.y, center.z);
|
|
569
|
-
}, [allowance, dimensions, ox, oy, oz, partGeometry, position, positionOffset]);
|
|
877
|
+
const options = JSON.parse(figure, restoreNonFinite);
|
|
878
|
+
const input = partGeometry ? { ...options, partGeometry } : options;
|
|
879
|
+
return isBox ? boxStockGeometry(input) : cylinderStockGeometry(input);
|
|
880
|
+
}, [figure, isBox, partGeometry]);
|
|
570
881
|
useEffect2(() => () => geometry.dispose(), [geometry]);
|
|
571
|
-
return /* @__PURE__ */ jsx4(
|
|
882
|
+
return /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance });
|
|
572
883
|
};
|
|
573
884
|
|
|
574
885
|
// src/banana.tsx
|
|
@@ -577,13 +888,13 @@ import { useThree as useThree2 } from "@react-three/fiber";
|
|
|
577
888
|
import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useRef } from "react";
|
|
578
889
|
|
|
579
890
|
// src/render/banana.ts
|
|
580
|
-
import { Vector3 as
|
|
891
|
+
import { Vector3 as Vector32 } from "three";
|
|
581
892
|
var BANANA_GAP = 0.1;
|
|
582
893
|
function bananaPosition(part, banana) {
|
|
583
|
-
const size = part.getSize(new
|
|
894
|
+
const size = part.getSize(new Vector32());
|
|
584
895
|
const reach = Math.max(size.x, size.y, size.z);
|
|
585
|
-
const centre = part.getCenter(new
|
|
586
|
-
return new
|
|
896
|
+
const centre = part.getCenter(new Vector32());
|
|
897
|
+
return new Vector32(
|
|
587
898
|
part.max.x + reach * BANANA_GAP - banana.min.x,
|
|
588
899
|
centre.y - (banana.min.y + banana.max.y) / 2,
|
|
589
900
|
part.min.z - banana.min.z
|
|
@@ -650,16 +961,16 @@ function directionHighlights(model, activeDirection = null) {
|
|
|
650
961
|
// src/primitives.tsx
|
|
651
962
|
import { GizmoHelper } from "@react-three/drei";
|
|
652
963
|
import { useThree as useThree3 } from "@react-three/fiber";
|
|
653
|
-
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as
|
|
964
|
+
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
|
|
654
965
|
|
|
655
966
|
// src/render/grid.ts
|
|
656
|
-
import { BufferGeometry, Float32BufferAttribute, Vector3 as
|
|
967
|
+
import { BufferGeometry, Float32BufferAttribute, Vector3 as Vector33 } from "three";
|
|
657
968
|
var STEPS = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1e3];
|
|
658
969
|
var TARGET_CELLS = 10;
|
|
659
970
|
var OVERHANG = 1.6;
|
|
660
971
|
function gridSpec(box) {
|
|
661
|
-
const size = box.getSize(new
|
|
662
|
-
const center = box.getCenter(new
|
|
972
|
+
const size = box.getSize(new Vector33());
|
|
973
|
+
const center = box.getCenter(new Vector33());
|
|
663
974
|
const largest = Math.max(size.x, size.y, 1e-6);
|
|
664
975
|
const ideal = largest / TARGET_CELLS;
|
|
665
976
|
const step = [...STEPS].reverse().find((candidate) => candidate <= ideal) ?? STEPS[0];
|
|
@@ -732,7 +1043,7 @@ var CubePanels = ({
|
|
|
732
1043
|
const controls = useViewerControls();
|
|
733
1044
|
const invalidate = useThree3((state) => state.invalidate);
|
|
734
1045
|
const groupRef = useRef2(null);
|
|
735
|
-
const [hovered, setHovered] =
|
|
1046
|
+
const [hovered, setHovered] = useState3(null);
|
|
736
1047
|
const panels = useMemo3(
|
|
737
1048
|
() => zones.map((zone) => ({ zone, geometry: panelGeometry(zone) })),
|
|
738
1049
|
[zones]
|
|
@@ -801,7 +1112,7 @@ var CubePanels = ({
|
|
|
801
1112
|
// src/direction-arrows.tsx
|
|
802
1113
|
import { useThree as useThree4 } from "@react-three/fiber";
|
|
803
1114
|
import { useMemo as useMemo4 } from "react";
|
|
804
|
-
import { Quaternion, Vector3 as
|
|
1115
|
+
import { Quaternion as Quaternion2, Vector3 as Vector34 } from "three";
|
|
805
1116
|
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
806
1117
|
var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
|
|
807
1118
|
var DirectionArrows = ({
|
|
@@ -861,9 +1172,9 @@ var Arrow = ({ direction, box, color, opacity, onPick, onTop = false }) => {
|
|
|
861
1172
|
const invalidate = useThree4((state) => state.invalidate);
|
|
862
1173
|
const { tip, length, quaternion } = useMemo4(() => {
|
|
863
1174
|
const placement = arrowPlacement(direction, box);
|
|
864
|
-
const aim = new
|
|
1175
|
+
const aim = new Quaternion2().setFromUnitVectors(
|
|
865
1176
|
CONE_AXIS,
|
|
866
|
-
new
|
|
1177
|
+
new Vector34(direction.x, direction.y, direction.z).normalize()
|
|
867
1178
|
);
|
|
868
1179
|
return { tip: placement.tip, length: placement.length, quaternion: aim };
|
|
869
1180
|
}, [box, direction]);
|
|
@@ -890,18 +1201,18 @@ var Arrow = ({ direction, box, color, opacity, onPick, onTop = false }) => {
|
|
|
890
1201
|
|
|
891
1202
|
// src/section-tool.tsx
|
|
892
1203
|
import { useFrame, useThree as useThree5 } from "@react-three/fiber";
|
|
893
|
-
import { useEffect as useEffect4, useMemo as useMemo5, useRef as useRef3, useState as
|
|
1204
|
+
import { useEffect as useEffect4, useMemo as useMemo5, useRef as useRef3, useState as useState4, useSyncExternalStore } from "react";
|
|
894
1205
|
import {
|
|
895
1206
|
BufferGeometry as BufferGeometry2,
|
|
896
1207
|
DoubleSide,
|
|
897
|
-
Quaternion as
|
|
1208
|
+
Quaternion as Quaternion3,
|
|
898
1209
|
Raycaster,
|
|
899
1210
|
Vector2,
|
|
900
|
-
Vector3 as
|
|
1211
|
+
Vector3 as Vector35
|
|
901
1212
|
} from "three";
|
|
902
1213
|
import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
903
1214
|
var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
|
|
904
|
-
var Z = new
|
|
1215
|
+
var Z = new Vector35(0, 0, 1);
|
|
905
1216
|
var AXIS_ROTATIONS = {
|
|
906
1217
|
x: [0, Math.PI / 2, 0],
|
|
907
1218
|
y: [Math.PI / 2, 0, 0],
|
|
@@ -932,10 +1243,10 @@ var SectionTool = ({ theme }) => {
|
|
|
932
1243
|
var useSquare = () => {
|
|
933
1244
|
const square = useMemo5(
|
|
934
1245
|
() => new BufferGeometry2().setFromPoints([
|
|
935
|
-
new
|
|
936
|
-
new
|
|
937
|
-
new
|
|
938
|
-
new
|
|
1246
|
+
new Vector35(-1, 1, 0),
|
|
1247
|
+
new Vector35(1, 1, 0),
|
|
1248
|
+
new Vector35(1, -1, 0),
|
|
1249
|
+
new Vector35(-1, -1, 0)
|
|
939
1250
|
]),
|
|
940
1251
|
[]
|
|
941
1252
|
);
|
|
@@ -952,9 +1263,9 @@ var Picker = ({ box, theme }) => {
|
|
|
952
1263
|
const square = useSquare();
|
|
953
1264
|
const previewRef = useRef3(null);
|
|
954
1265
|
const hover = useRef3(null);
|
|
955
|
-
const [hoveredPlane, setHoveredPlane] =
|
|
956
|
-
const span = useMemo5(() => box.getSize(new
|
|
957
|
-
const centre = useMemo5(() => box.getCenter(new
|
|
1266
|
+
const [hoveredPlane, setHoveredPlane] = useState4(null);
|
|
1267
|
+
const span = useMemo5(() => box.getSize(new Vector35()).length(), [box]);
|
|
1268
|
+
const centre = useMemo5(() => box.getCenter(new Vector35()), [box]);
|
|
958
1269
|
const previewSize = span * PREVIEW_SCALE;
|
|
959
1270
|
useEffect4(() => {
|
|
960
1271
|
store.setPicking(true);
|
|
@@ -963,7 +1274,7 @@ var Picker = ({ box, theme }) => {
|
|
|
963
1274
|
useEffect4(() => {
|
|
964
1275
|
const raycaster = new Raycaster();
|
|
965
1276
|
const pointer = new Vector2();
|
|
966
|
-
const aim = new
|
|
1277
|
+
const aim = new Vector35();
|
|
967
1278
|
const hide = () => {
|
|
968
1279
|
if (!hover.current) return;
|
|
969
1280
|
hover.current = null;
|
|
@@ -1008,7 +1319,7 @@ var Picker = ({ box, theme }) => {
|
|
|
1008
1319
|
domElement.removeEventListener("pointerup", up);
|
|
1009
1320
|
};
|
|
1010
1321
|
}, [box, camera, domElement, invalidate, isTap, scene, span, store]);
|
|
1011
|
-
const [planes, setPlanes] =
|
|
1322
|
+
const [planes, setPlanes] = useState4(() => axesPlanes(box, camera.position));
|
|
1012
1323
|
const key = planes.map((plane) => plane.sign).join();
|
|
1013
1324
|
useFrame(() => {
|
|
1014
1325
|
const next = [camera.position.x, camera.position.y, camera.position.z].map((eye, axis) => eye - [centre.x, centre.y, centre.z][axis] < 0 ? -1 : 1).join();
|
|
@@ -1107,14 +1418,14 @@ var Picker = ({ box, theme }) => {
|
|
|
1107
1418
|
var CutSheet = ({ box, section, theme }) => {
|
|
1108
1419
|
const square = useSquare();
|
|
1109
1420
|
const cut = useMemo5(() => resolveSectionPlane(section, box), [box, section]);
|
|
1110
|
-
const span = useMemo5(() => box.getSize(new
|
|
1421
|
+
const span = useMemo5(() => box.getSize(new Vector35()).length(), [box]);
|
|
1111
1422
|
const placement = useMemo5(() => {
|
|
1112
1423
|
if (!cut) return null;
|
|
1113
1424
|
const position = sectionAnchor(box, cut.plane).addScaledVector(
|
|
1114
1425
|
cut.plane.normal,
|
|
1115
1426
|
-span * SHEET_SETBACK
|
|
1116
1427
|
);
|
|
1117
|
-
return { position, quaternion: new
|
|
1428
|
+
return { position, quaternion: new Quaternion3().setFromUnitVectors(Z, cut.plane.normal) };
|
|
1118
1429
|
}, [box, cut, span]);
|
|
1119
1430
|
if (!placement) return null;
|
|
1120
1431
|
const size = span * OUTLINE_SCALE;
|
|
@@ -1162,26 +1473,26 @@ import {
|
|
|
1162
1473
|
useLayoutEffect as useLayoutEffect3,
|
|
1163
1474
|
useMemo as useMemo6,
|
|
1164
1475
|
useRef as useRef4,
|
|
1165
|
-
useState as
|
|
1476
|
+
useState as useState5,
|
|
1166
1477
|
useSyncExternalStore as useSyncExternalStore2
|
|
1167
1478
|
} from "react";
|
|
1168
1479
|
import {
|
|
1169
|
-
Matrix4,
|
|
1480
|
+
Matrix4 as Matrix42,
|
|
1170
1481
|
Raycaster as Raycaster2,
|
|
1171
1482
|
SphereGeometry,
|
|
1172
1483
|
Vector2 as Vector22,
|
|
1173
|
-
Vector3 as
|
|
1484
|
+
Vector3 as Vector37
|
|
1174
1485
|
} from "three";
|
|
1175
1486
|
|
|
1176
1487
|
// src/render/resample.ts
|
|
1177
|
-
import { Vector3 as
|
|
1488
|
+
import { Vector3 as Vector36 } from "three";
|
|
1178
1489
|
var ON_PLANE = 1e-7;
|
|
1179
1490
|
var STITCH = 1e-5;
|
|
1180
1491
|
var COLLINEAR = 1e-6;
|
|
1181
1492
|
function clipSegments(positions, plane) {
|
|
1182
1493
|
const out = [];
|
|
1183
|
-
const a = new
|
|
1184
|
-
const b = new
|
|
1494
|
+
const a = new Vector36();
|
|
1495
|
+
const b = new Vector36();
|
|
1185
1496
|
const count = Math.floor(positions.length / 6);
|
|
1186
1497
|
for (let i = 0; i < count; i += 1) {
|
|
1187
1498
|
a.set(positions[i * 6], positions[i * 6 + 1], positions[i * 6 + 2]);
|
|
@@ -1196,11 +1507,11 @@ function clipSegments(positions, plane) {
|
|
|
1196
1507
|
return Float32Array.from(out);
|
|
1197
1508
|
}
|
|
1198
1509
|
function sectionContour(positions, regionOf, plane) {
|
|
1199
|
-
const a = new
|
|
1200
|
-
const b = new
|
|
1201
|
-
const c = new
|
|
1202
|
-
const p = new
|
|
1203
|
-
const q = new
|
|
1510
|
+
const a = new Vector36();
|
|
1511
|
+
const b = new Vector36();
|
|
1512
|
+
const c = new Vector36();
|
|
1513
|
+
const p = new Vector36();
|
|
1514
|
+
const q = new Vector36();
|
|
1204
1515
|
const byRegion = /* @__PURE__ */ new Map();
|
|
1205
1516
|
const triangleCount = Math.floor(positions.length / 9);
|
|
1206
1517
|
for (let t = 0; t < triangleCount; t += 1) {
|
|
@@ -1258,17 +1569,17 @@ function stitch(segments, out) {
|
|
|
1258
1569
|
return into.set(segments[o], segments[o + 1], segments[o + 2]);
|
|
1259
1570
|
};
|
|
1260
1571
|
const direction = (i, into) => {
|
|
1261
|
-
const s = new
|
|
1262
|
-
const e = new
|
|
1572
|
+
const s = new Vector36();
|
|
1573
|
+
const e = new Vector36();
|
|
1263
1574
|
point(i, 0, s);
|
|
1264
1575
|
point(i, 1, e);
|
|
1265
1576
|
return into.subVectors(e, s).normalize();
|
|
1266
1577
|
};
|
|
1267
1578
|
const consumed = new Uint8Array(count);
|
|
1268
|
-
const d = new
|
|
1269
|
-
const n = new
|
|
1579
|
+
const d = new Vector36();
|
|
1580
|
+
const n = new Vector36();
|
|
1270
1581
|
const next = (i, end) => {
|
|
1271
|
-
const at = point(i, end, new
|
|
1582
|
+
const at = point(i, end, new Vector36());
|
|
1272
1583
|
const list = ends.get(key(at.x, at.y, at.z)) ?? [];
|
|
1273
1584
|
if (list.length !== 2) return null;
|
|
1274
1585
|
const other = list.find((entry) => entry >> 1 !== i);
|
|
@@ -1297,8 +1608,8 @@ function stitch(segments, out) {
|
|
|
1297
1608
|
};
|
|
1298
1609
|
const back = walk(0);
|
|
1299
1610
|
const forward = walk(1);
|
|
1300
|
-
const s = point(back.index, back.end, new
|
|
1301
|
-
const e = point(forward.index, forward.end, new
|
|
1611
|
+
const s = point(back.index, back.end, new Vector36());
|
|
1612
|
+
const e = point(forward.index, forward.end, new Vector36());
|
|
1302
1613
|
out.push(s.x, s.y, s.z, e.x, e.y, e.z);
|
|
1303
1614
|
}
|
|
1304
1615
|
}
|
|
@@ -1306,8 +1617,8 @@ function pointInContour(point, plane, contour) {
|
|
|
1306
1617
|
const [u, v] = planeAxes(plane);
|
|
1307
1618
|
const px = point.dot(u);
|
|
1308
1619
|
const py = point.dot(v);
|
|
1309
|
-
const a = new
|
|
1310
|
-
const b = new
|
|
1620
|
+
const a = new Vector36();
|
|
1621
|
+
const b = new Vector36();
|
|
1311
1622
|
let inside = false;
|
|
1312
1623
|
const count = Math.floor(contour.length / 6);
|
|
1313
1624
|
for (let i = 0; i < count; i += 1) {
|
|
@@ -1324,15 +1635,15 @@ function pointInContour(point, plane, contour) {
|
|
|
1324
1635
|
return inside;
|
|
1325
1636
|
}
|
|
1326
1637
|
function capHit(ray, plane, contour) {
|
|
1327
|
-
const point = ray.intersectPlane(plane, new
|
|
1638
|
+
const point = ray.intersectPlane(plane, new Vector36());
|
|
1328
1639
|
if (!point) return null;
|
|
1329
1640
|
return pointInContour(point, plane, contour) ? point : null;
|
|
1330
1641
|
}
|
|
1331
1642
|
function planeAxes(plane) {
|
|
1332
1643
|
const n = plane.normal;
|
|
1333
|
-
const seed = Math.abs(n.x) < 0.9 ? new
|
|
1334
|
-
const u = new
|
|
1335
|
-
const v = new
|
|
1644
|
+
const seed = Math.abs(n.x) < 0.9 ? new Vector36(1, 0, 0) : new Vector36(0, 1, 0);
|
|
1645
|
+
const u = new Vector36().crossVectors(n, seed).normalize();
|
|
1646
|
+
const v = new Vector36().crossVectors(n, u).normalize();
|
|
1336
1647
|
return [u, v];
|
|
1337
1648
|
}
|
|
1338
1649
|
function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
@@ -1361,10 +1672,10 @@ var MeasureTool = ({
|
|
|
1361
1672
|
const box = useContentBox();
|
|
1362
1673
|
const resolved = useMemo6(() => resolveTheme(theme), [theme]);
|
|
1363
1674
|
const picking = useSyncExternalStore2(store.subscribe, store.isPicking, store.isPicking);
|
|
1364
|
-
const [held, setHeld] =
|
|
1675
|
+
const [held, setHeld] = useState5([]);
|
|
1365
1676
|
const controlled = measurements !== void 0;
|
|
1366
1677
|
const list = controlled ? measurements : held;
|
|
1367
|
-
const [draft, setDraft] =
|
|
1678
|
+
const [draft, setDraft] = useState5([]);
|
|
1368
1679
|
useEffect5(() => {
|
|
1369
1680
|
store.setEngaged(true);
|
|
1370
1681
|
return () => store.setEngaged(false);
|
|
@@ -1442,7 +1753,7 @@ var MeasureTool = ({
|
|
|
1442
1753
|
] });
|
|
1443
1754
|
};
|
|
1444
1755
|
var NO_POINTER = { pointerEvents: "none" };
|
|
1445
|
-
var NOWHERE = [new
|
|
1756
|
+
var NOWHERE = [new Vector37(), new Vector37()];
|
|
1446
1757
|
var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
1447
1758
|
const camera = useThree6((state) => state.camera);
|
|
1448
1759
|
const scene = useThree6((state) => state.scene);
|
|
@@ -1495,7 +1806,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1495
1806
|
const snapped = snapAt(target, target.edges, camera, viewport);
|
|
1496
1807
|
const from = placed[placed.length - 1];
|
|
1497
1808
|
const lock = shift && from ? lockToAxis(from, snapped.point) : null;
|
|
1498
|
-
const found = lock ? { ...snapped, point: new
|
|
1809
|
+
const found = lock ? { ...snapped, point: new Vector37(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
|
|
1499
1810
|
snap.current = found;
|
|
1500
1811
|
const node = marker.current;
|
|
1501
1812
|
if (node) {
|
|
@@ -1657,7 +1968,7 @@ function targetUnderRay(raycaster, scene, samples) {
|
|
|
1657
1968
|
edges: samples.edgesFor(hit.object)
|
|
1658
1969
|
};
|
|
1659
1970
|
}
|
|
1660
|
-
const local = new
|
|
1971
|
+
const local = new Matrix42();
|
|
1661
1972
|
for (const mesh of cutMeshes(scene)) {
|
|
1662
1973
|
const planes = clippingPlanesOf(mesh);
|
|
1663
1974
|
const sample = samples.for(mesh, planes);
|
|
@@ -1720,7 +2031,7 @@ var SampleCache = class {
|
|
|
1720
2031
|
const key = planes.map((plane) => `${plane.normal.x},${plane.normal.y},${plane.normal.z},${plane.constant}`).join("|");
|
|
1721
2032
|
const cached = this.held.get(mesh);
|
|
1722
2033
|
if (cached && cached.key === key) return cached;
|
|
1723
|
-
const local = new
|
|
2034
|
+
const local = new Matrix42().copy(mesh.matrixWorld).invert();
|
|
1724
2035
|
const localPlanes = planes.map((plane) => plane.clone().applyMatrix4(local));
|
|
1725
2036
|
const positions = mesh.geometry.getAttribute("position")?.array ?? [];
|
|
1726
2037
|
const regionOf = this.regionsOf(mesh.geometry);
|
|
@@ -1803,7 +2114,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1803
2114
|
const lines = useRef4([]);
|
|
1804
2115
|
const centre = useMemo6(() => {
|
|
1805
2116
|
const at = midpoint(a, b);
|
|
1806
|
-
return new
|
|
2117
|
+
return new Vector37(at.x, at.y, at.z);
|
|
1807
2118
|
}, [a, b]);
|
|
1808
2119
|
useFrame2(() => {
|
|
1809
2120
|
const dash = screenLength(camera, centre, size, DELTA_DASH_PIXELS);
|
|
@@ -1825,8 +2136,8 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1825
2136
|
lines.current[index] = line;
|
|
1826
2137
|
},
|
|
1827
2138
|
points: [
|
|
1828
|
-
new
|
|
1829
|
-
new
|
|
2139
|
+
new Vector37(leg.from.x, leg.from.y, leg.from.z),
|
|
2140
|
+
new Vector37(leg.to.x, leg.to.y, leg.to.z)
|
|
1830
2141
|
],
|
|
1831
2142
|
color: AXIS_COLORS[leg.axis],
|
|
1832
2143
|
lineWidth: MEASURE_LINE_PIXELS.leg,
|
|
@@ -1862,7 +2173,7 @@ var ScreenDot = ({ point, pixels, color }) => {
|
|
|
1862
2173
|
const invalidate = useThree6((state) => state.invalidate);
|
|
1863
2174
|
const node = useRef4(null);
|
|
1864
2175
|
const dot = useDot();
|
|
1865
|
-
const position = useMemo6(() => new
|
|
2176
|
+
const position = useMemo6(() => new Vector37(point.x, point.y, point.z), [point]);
|
|
1866
2177
|
useFrame2(() => {
|
|
1867
2178
|
const group = node.current;
|
|
1868
2179
|
if (!group) return;
|
|
@@ -1877,7 +2188,7 @@ var ScreenDot = ({ point, pixels, color }) => {
|
|
|
1877
2188
|
var Segment = ({ points, color }) => {
|
|
1878
2189
|
const [a, b] = points;
|
|
1879
2190
|
const ends = useMemo6(
|
|
1880
|
-
() => [new
|
|
2191
|
+
() => [new Vector37(a.x, a.y, a.z), new Vector37(b.x, b.y, b.z)],
|
|
1881
2192
|
// Keyed on the numbers: a draft's tuple is a fresh array on every render,
|
|
1882
2193
|
// and a rebuilt array would rebuild the line's geometry with it.
|
|
1883
2194
|
[a.x, a.y, a.z, b.x, b.y, b.z]
|
|
@@ -1913,6 +2224,145 @@ function className(extra) {
|
|
|
1913
2224
|
return extra ? `${MEASURE_LABEL_CLASS} ${extra}` : MEASURE_LABEL_CLASS;
|
|
1914
2225
|
}
|
|
1915
2226
|
|
|
2227
|
+
// src/tool-marks.tsx
|
|
2228
|
+
import { Html as Html2, Line as Line2 } from "@react-three/drei";
|
|
2229
|
+
import { useMemo as useMemo7 } from "react";
|
|
2230
|
+
import { DoubleSide as DoubleSide2 } from "three";
|
|
2231
|
+
|
|
2232
|
+
// src/render/tool-marks.ts
|
|
2233
|
+
import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as Vector38 } from "three";
|
|
2234
|
+
var TOOL_CORNER_STEPS = 12;
|
|
2235
|
+
var TOOL_ROUND = 64;
|
|
2236
|
+
function toolProfile(radius, corner, height) {
|
|
2237
|
+
const rounded = Math.min(Math.max(corner, 0), radius);
|
|
2238
|
+
const flat = radius - rounded;
|
|
2239
|
+
const points = [new Vector23(0, 0)];
|
|
2240
|
+
if (rounded <= 0) {
|
|
2241
|
+
points.push(new Vector23(radius, 0));
|
|
2242
|
+
} else {
|
|
2243
|
+
for (let step = 0; step <= TOOL_CORNER_STEPS; step++) {
|
|
2244
|
+
const angle = step / TOOL_CORNER_STEPS * (Math.PI / 2);
|
|
2245
|
+
points.push(
|
|
2246
|
+
new Vector23(flat + Math.sin(angle) * rounded, rounded - Math.cos(angle) * rounded)
|
|
2247
|
+
);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
points.push(new Vector23(radius, Math.max(height, rounded)));
|
|
2251
|
+
return points;
|
|
2252
|
+
}
|
|
2253
|
+
var UP = new Vector38(0, 1, 0);
|
|
2254
|
+
var AXES = [new Vector38(1, 0, 0), new Vector38(0, 1, 0), new Vector38(0, 0, 1)];
|
|
2255
|
+
var toVector = ({ x, y, z }) => new Vector38(x, y, z);
|
|
2256
|
+
var squareTo = (w, along) => {
|
|
2257
|
+
const wanted = along ? toVector(along) : null;
|
|
2258
|
+
const flat = wanted?.clone().addScaledVector(w, -wanted.dot(w));
|
|
2259
|
+
if (flat && flat.lengthSq() > 1e-12) return flat.normalize();
|
|
2260
|
+
const least = AXES.reduce(
|
|
2261
|
+
(best, axis) => Math.abs(axis.dot(w)) < Math.abs(best.dot(w)) ? axis : best
|
|
2262
|
+
);
|
|
2263
|
+
return least.clone().addScaledVector(w, -least.dot(w)).normalize();
|
|
2264
|
+
};
|
|
2265
|
+
var circle = (centre, u, v, radius) => Array.from({ length: TOOL_ROUND + 1 }, (_, step) => {
|
|
2266
|
+
const angle = step / TOOL_ROUND * Math.PI * 2;
|
|
2267
|
+
return centre.clone().addScaledVector(u, Math.cos(angle) * radius).addScaledVector(v, Math.sin(angle) * radius);
|
|
2268
|
+
});
|
|
2269
|
+
var tickLength = (diameter) => Math.min(Math.max(diameter * 0.15, 0.3), 3);
|
|
2270
|
+
function toolMarkShape(mark) {
|
|
2271
|
+
const axis = toVector(mark.axis);
|
|
2272
|
+
if (!(mark.diameter > 0) || axis.lengthSq() === 0) return null;
|
|
2273
|
+
const w = axis.normalize();
|
|
2274
|
+
const u = squareTo(w, mark.across);
|
|
2275
|
+
const v = new Vector38().crossVectors(w, u);
|
|
2276
|
+
const radius = mark.diameter / 2;
|
|
2277
|
+
const corner = Math.min(Math.max(mark.cornerRadius ?? 0, 0), radius);
|
|
2278
|
+
const height = Math.max(mark.height, corner);
|
|
2279
|
+
const base = toVector(mark.base);
|
|
2280
|
+
const top = base.clone().addScaledVector(w, height);
|
|
2281
|
+
const dimension = [
|
|
2282
|
+
top.clone().addScaledVector(u, radius),
|
|
2283
|
+
top.clone().addScaledVector(u, -radius)
|
|
2284
|
+
];
|
|
2285
|
+
const half = tickLength(mark.diameter) / 2;
|
|
2286
|
+
const tick = (end) => [
|
|
2287
|
+
end.clone().addScaledVector(w, -half),
|
|
2288
|
+
end.clone().addScaledVector(w, half)
|
|
2289
|
+
];
|
|
2290
|
+
return {
|
|
2291
|
+
base,
|
|
2292
|
+
turn: new Quaternion4().setFromUnitVectors(UP, w),
|
|
2293
|
+
profile: mark.profile ? mark.profile.map(({ r, z }) => new Vector23(r, z)) : toolProfile(radius, corner, height),
|
|
2294
|
+
rims: [
|
|
2295
|
+
...radius - corner > 1e-9 ? [circle(base, u, v, radius - corner)] : [],
|
|
2296
|
+
circle(top, u, v, radius)
|
|
2297
|
+
],
|
|
2298
|
+
dimension,
|
|
2299
|
+
ticks: [tick(dimension[0]), tick(dimension[1])],
|
|
2300
|
+
labelAt: top
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
// src/tool-marks.tsx
|
|
2305
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2306
|
+
var BODY = { color: 16317180, opacity: 0.32 };
|
|
2307
|
+
var NO_POINTER2 = { pointerEvents: "none" };
|
|
2308
|
+
var ignoreRay = () => void 0;
|
|
2309
|
+
var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
|
|
2310
|
+
const color = resolveTheme(theme).measure;
|
|
2311
|
+
const shapes = useMemo7(
|
|
2312
|
+
() => marks.flatMap((mark) => {
|
|
2313
|
+
const shape = toolMarkShape(mark);
|
|
2314
|
+
return shape ? [{ mark, shape }] : [];
|
|
2315
|
+
}),
|
|
2316
|
+
[marks]
|
|
2317
|
+
);
|
|
2318
|
+
const label = labelClassName ? `${MEASURE_LABEL_CLASS} ${labelClassName}` : MEASURE_LABEL_CLASS;
|
|
2319
|
+
return /* @__PURE__ */ jsx10("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: true }, children: shapes.map(({ mark, shape }, index) => /* @__PURE__ */ jsxs6("group", { children: [
|
|
2320
|
+
/* @__PURE__ */ jsx10(ToolBody, { shape }),
|
|
2321
|
+
[...shape.rims, shape.dimension, ...shape.ticks].map((points, line) => /* @__PURE__ */ jsx10(
|
|
2322
|
+
Line2,
|
|
2323
|
+
{
|
|
2324
|
+
points,
|
|
2325
|
+
color,
|
|
2326
|
+
lineWidth: MEASURE_LINE_PIXELS.line,
|
|
2327
|
+
depthTest: false,
|
|
2328
|
+
depthWrite: false,
|
|
2329
|
+
renderOrder: MEASURE_RENDER_ORDER,
|
|
2330
|
+
raycast: ignoreRay
|
|
2331
|
+
},
|
|
2332
|
+
line
|
|
2333
|
+
)),
|
|
2334
|
+
mark.label && visible ? /* @__PURE__ */ jsx10(Html2, { position: shape.labelAt, center: true, zIndexRange: [0, 0], style: NO_POINTER2, children: /* @__PURE__ */ jsxs6("div", { className: label, "data-measure-label": "tool", children: [
|
|
2335
|
+
/* @__PURE__ */ jsx10("span", { "data-measure-value": true, children: mark.label }),
|
|
2336
|
+
mark.note ? /* @__PURE__ */ jsxs6("span", { "data-measure-note": true, children: [
|
|
2337
|
+
" ",
|
|
2338
|
+
mark.note
|
|
2339
|
+
] }) : null
|
|
2340
|
+
] }) }) : null
|
|
2341
|
+
] }, index)) });
|
|
2342
|
+
};
|
|
2343
|
+
var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs6(
|
|
2344
|
+
"mesh",
|
|
2345
|
+
{
|
|
2346
|
+
position: shape.base,
|
|
2347
|
+
quaternion: shape.turn,
|
|
2348
|
+
raycast: ignoreRay,
|
|
2349
|
+
renderOrder: MEASURE_RENDER_ORDER - 1,
|
|
2350
|
+
children: [
|
|
2351
|
+
/* @__PURE__ */ jsx10("latheGeometry", { args: [shape.profile, 64] }),
|
|
2352
|
+
/* @__PURE__ */ jsx10(
|
|
2353
|
+
"meshBasicMaterial",
|
|
2354
|
+
{
|
|
2355
|
+
color: BODY.color,
|
|
2356
|
+
transparent: true,
|
|
2357
|
+
opacity: BODY.opacity,
|
|
2358
|
+
depthWrite: false,
|
|
2359
|
+
side: DoubleSide2
|
|
2360
|
+
}
|
|
2361
|
+
)
|
|
2362
|
+
]
|
|
2363
|
+
}
|
|
2364
|
+
);
|
|
2365
|
+
|
|
1916
2366
|
// src/model/normals.ts
|
|
1917
2367
|
function regionNormals(geometry, regions) {
|
|
1918
2368
|
const position = geometry.getAttribute("position");
|
|
@@ -1956,7 +2406,6 @@ export {
|
|
|
1956
2406
|
BANANA_GAP,
|
|
1957
2407
|
BANANA_MODEL_URL,
|
|
1958
2408
|
Banana,
|
|
1959
|
-
BoxStock,
|
|
1960
2409
|
CAD_CAMERA_UP,
|
|
1961
2410
|
CANDIDATE_WEIGHT,
|
|
1962
2411
|
CAP_HATCH,
|
|
@@ -2013,6 +2462,7 @@ export {
|
|
|
2013
2462
|
SectionView,
|
|
2014
2463
|
Stock,
|
|
2015
2464
|
TAP_SLOP,
|
|
2465
|
+
ToolMarks,
|
|
2016
2466
|
UnsupportedKernelVersionError,
|
|
2017
2467
|
VIEW_NAMES,
|
|
2018
2468
|
VIEW_SIGNS,
|
|
@@ -2064,6 +2514,7 @@ export {
|
|
|
2064
2514
|
distanceBetween,
|
|
2065
2515
|
dragPlane,
|
|
2066
2516
|
excludedFromFrame,
|
|
2517
|
+
featureTriangles,
|
|
2067
2518
|
featureTypeRank,
|
|
2068
2519
|
fitDistance,
|
|
2069
2520
|
fixedBoxStockBounds,
|
|
@@ -2123,6 +2574,7 @@ export {
|
|
|
2123
2574
|
trackDoubleTaps,
|
|
2124
2575
|
trackTaps,
|
|
2125
2576
|
useContentBox,
|
|
2577
|
+
usePartContext,
|
|
2126
2578
|
useRetarget,
|
|
2127
2579
|
useSectionStore,
|
|
2128
2580
|
useTapGuard,
|