@toolpath/viewer 1.3.0 → 1.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 +290 -55
- package/dist/assets/banana.glb +0 -0
- package/dist/{chunk-HWAEUJ4H.js → chunk-4TBLMR4Z.js} +744 -175
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +225 -22
- package/dist/index.js +755 -405
- package/dist/{normalize-DHOSNiuO.d.ts → normalize-CvnV9Ggh.d.ts} +55 -18
- package/dist/toolbar.css +108 -0
- package/package.json +5 -4
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useRef as useRef6,
|
|
12
12
|
useSyncExternalStore
|
|
13
13
|
} from "react";
|
|
14
|
-
import { Vector3 as
|
|
14
|
+
import { Vector3 as Vector311 } from "three";
|
|
15
15
|
|
|
16
16
|
// src/model/directions.ts
|
|
17
17
|
var EPSILON = 1e-6;
|
|
@@ -193,14 +193,31 @@ function applyHighlightLayers(part, layers, theme) {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
// src/render/focus.ts
|
|
197
|
+
var DEFAULT_FOCUS_OPACITY = 0.15;
|
|
198
|
+
function focusOpacity(options) {
|
|
199
|
+
return Math.min(Math.max(options?.opacity ?? DEFAULT_FOCUS_OPACITY, 0), 1);
|
|
200
|
+
}
|
|
201
|
+
function focusStateKey(options, selection) {
|
|
202
|
+
return `${options === void 0 ? "off" : `on:${options.opacity ?? ""}`}|${selection.join(" ")}`;
|
|
203
|
+
}
|
|
204
|
+
function focusedRegions(model, selection) {
|
|
205
|
+
const regions = /* @__PURE__ */ new Set();
|
|
206
|
+
for (const tag of selection) {
|
|
207
|
+
for (const region of model.regionIndex.regionsForFeature(tag)) regions.add(region);
|
|
208
|
+
}
|
|
209
|
+
return regions;
|
|
210
|
+
}
|
|
211
|
+
|
|
196
212
|
// src/render/section.ts
|
|
197
|
-
import { Plane, Vector3 as
|
|
213
|
+
import { Plane, Vector3 as Vector33 } from "three";
|
|
198
214
|
|
|
199
215
|
// src/render/camera.ts
|
|
200
216
|
import { OrthographicCamera, Vector3 } from "three";
|
|
201
217
|
var PERSPECTIVE_FOV = 30;
|
|
202
218
|
var DEFAULT_FIT_MARGIN = 1.2;
|
|
203
219
|
var EXCLUDE_FROM_FRAME = "viewerExcludeFromFrame";
|
|
220
|
+
var STOCK_OBJECT = "viewerStock";
|
|
204
221
|
function defaultBounds() {
|
|
205
222
|
return { center: new Vector3(0, 0, 0), radius: 1 };
|
|
206
223
|
}
|
|
@@ -221,16 +238,30 @@ function boundsFromBox(box) {
|
|
|
221
238
|
return { center, radius: radius > 0 ? radius : 1 };
|
|
222
239
|
}
|
|
223
240
|
function contentBounds(root, into) {
|
|
241
|
+
return measureBounds(root, into, false);
|
|
242
|
+
}
|
|
243
|
+
function partBounds(root, into) {
|
|
244
|
+
return measureBounds(root, into, true);
|
|
245
|
+
}
|
|
246
|
+
function measureBounds(root, into, partOnly) {
|
|
224
247
|
into.makeEmpty();
|
|
225
248
|
root.updateWorldMatrix(true, true);
|
|
226
249
|
root.traverse((object) => {
|
|
227
|
-
if (excludedFromFrame(object, root)) return;
|
|
250
|
+
if (partOnly ? excludedFromPart(object, root) : excludedFromFrame(object, root)) return;
|
|
228
251
|
if ("isMesh" in object || "isLine" in object || "isPoints" in object) {
|
|
229
252
|
into.expandByObject(object);
|
|
230
253
|
}
|
|
231
254
|
});
|
|
232
255
|
return boundsFromBox(into);
|
|
233
256
|
}
|
|
257
|
+
function excludedFromPart(object, root) {
|
|
258
|
+
let current = object;
|
|
259
|
+
while (current && current !== root) {
|
|
260
|
+
if (current.userData[STOCK_OBJECT]) return true;
|
|
261
|
+
current = current.parent;
|
|
262
|
+
}
|
|
263
|
+
return excludedFromFrame(object, root);
|
|
264
|
+
}
|
|
234
265
|
function excludedFromFrame(object, root) {
|
|
235
266
|
let current = object;
|
|
236
267
|
while (current && current !== root) {
|
|
@@ -351,6 +382,206 @@ function currentViewDirection(camera, target, into) {
|
|
|
351
382
|
return into.normalize();
|
|
352
383
|
}
|
|
353
384
|
|
|
385
|
+
// src/render/measure.ts
|
|
386
|
+
import { Vector2, Vector3 as Vector32 } from "three";
|
|
387
|
+
var MEASURE_RENDER_ORDER = 7;
|
|
388
|
+
var AXIS_COLORS = { x: 16739179, y: 7331978, z: 7314431 };
|
|
389
|
+
var SNAP_PIXELS = {
|
|
390
|
+
vertex: 14,
|
|
391
|
+
midpoint: 12,
|
|
392
|
+
edge: 9
|
|
393
|
+
};
|
|
394
|
+
var SNAP_MARKER_PIXELS = {
|
|
395
|
+
vertex: 6,
|
|
396
|
+
midpoint: 5,
|
|
397
|
+
edge: 4,
|
|
398
|
+
face: 3.5
|
|
399
|
+
};
|
|
400
|
+
var MEASURE_MARKER_PIXELS = 3.5;
|
|
401
|
+
var MEASURE_LINE_PIXELS = {
|
|
402
|
+
line: 2,
|
|
403
|
+
leg: 1.5,
|
|
404
|
+
edge: 3
|
|
405
|
+
};
|
|
406
|
+
var DELTA_DASH_PIXELS = 6;
|
|
407
|
+
var ANGLE_ARC_FRACTION = 0.3;
|
|
408
|
+
var ANGLE_ARC_SEGMENTS = 24;
|
|
409
|
+
function snapAt(hit, edges, camera, viewport) {
|
|
410
|
+
const face = {
|
|
411
|
+
kind: "face",
|
|
412
|
+
point: hit.point.clone(),
|
|
413
|
+
normal: hit.normal.clone(),
|
|
414
|
+
edge: null
|
|
415
|
+
};
|
|
416
|
+
if (!edges) return face;
|
|
417
|
+
const positions = edges.positions;
|
|
418
|
+
const segments = Math.floor(positions.length / 6);
|
|
419
|
+
if (segments === 0) return face;
|
|
420
|
+
const inverse = edges.matrixWorld.clone().invert();
|
|
421
|
+
const p = hit.point.clone().applyMatrix4(inverse);
|
|
422
|
+
const n = hit.normal.clone().transformDirection(inverse);
|
|
423
|
+
const reach = screenLength(camera, hit.point, viewport, SNAP_PIXELS.vertex) * 1.5;
|
|
424
|
+
const reachSq = reach * reach;
|
|
425
|
+
const pointer = screenPoint(camera, hit.point, viewport);
|
|
426
|
+
const best = {
|
|
427
|
+
vertex: { distance: Number.POSITIVE_INFINITY, index: -1, t: 0 },
|
|
428
|
+
midpoint: { distance: Number.POSITIVE_INFINITY, index: -1, t: 0 },
|
|
429
|
+
edge: { distance: Number.POSITIVE_INFINITY, index: -1, t: 0 }
|
|
430
|
+
};
|
|
431
|
+
const a = new Vector32();
|
|
432
|
+
const b = new Vector32();
|
|
433
|
+
const ab = new Vector32();
|
|
434
|
+
const ap = new Vector32();
|
|
435
|
+
const q = new Vector32();
|
|
436
|
+
const pixel = new Vector2();
|
|
437
|
+
const consider = (kind, point, index, t) => {
|
|
438
|
+
const distance = screenPoint(
|
|
439
|
+
camera,
|
|
440
|
+
q.copy(point).applyMatrix4(edges.matrixWorld),
|
|
441
|
+
viewport,
|
|
442
|
+
pixel
|
|
443
|
+
).distanceTo(pointer);
|
|
444
|
+
if (distance <= SNAP_PIXELS[kind] && distance < best[kind].distance) {
|
|
445
|
+
best[kind] = { distance, index, t };
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
for (let index = 0; index < segments; index += 1) {
|
|
449
|
+
const offset = index * 6;
|
|
450
|
+
a.set(positions[offset], positions[offset + 1], positions[offset + 2]);
|
|
451
|
+
b.set(positions[offset + 3], positions[offset + 4], positions[offset + 5]);
|
|
452
|
+
if (Math.abs(ap.subVectors(a, p).dot(n)) > reach) continue;
|
|
453
|
+
if (Math.abs(ap.subVectors(b, p).dot(n)) > reach) continue;
|
|
454
|
+
ab.subVectors(b, a);
|
|
455
|
+
ap.subVectors(p, a);
|
|
456
|
+
const lengthSq = ab.lengthSq();
|
|
457
|
+
const t = lengthSq === 0 ? 0 : Math.min(1, Math.max(0, ap.dot(ab) / lengthSq));
|
|
458
|
+
q.copy(a).addScaledVector(ab, t);
|
|
459
|
+
if (q.distanceToSquared(p) > reachSq) continue;
|
|
460
|
+
consider("vertex", a, index, 0);
|
|
461
|
+
consider("vertex", b, index, 1);
|
|
462
|
+
consider("midpoint", q.copy(a).addScaledVector(ab, 0.5), index, 0.5);
|
|
463
|
+
consider("edge", q.copy(a).addScaledVector(ab, t), index, t);
|
|
464
|
+
}
|
|
465
|
+
for (const kind of ["vertex", "midpoint", "edge"]) {
|
|
466
|
+
const found = best[kind];
|
|
467
|
+
if (found.index < 0) continue;
|
|
468
|
+
const offset = found.index * 6;
|
|
469
|
+
const start = new Vector32(positions[offset], positions[offset + 1], positions[offset + 2]);
|
|
470
|
+
const end = new Vector32(positions[offset + 3], positions[offset + 4], positions[offset + 5]);
|
|
471
|
+
const point = start.clone().lerp(end, found.t);
|
|
472
|
+
start.applyMatrix4(edges.matrixWorld);
|
|
473
|
+
end.applyMatrix4(edges.matrixWorld);
|
|
474
|
+
point.applyMatrix4(edges.matrixWorld);
|
|
475
|
+
return {
|
|
476
|
+
kind,
|
|
477
|
+
point,
|
|
478
|
+
normal: hit.normal.clone(),
|
|
479
|
+
edge: kind === "vertex" ? null : [start, end]
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return face;
|
|
483
|
+
}
|
|
484
|
+
function screenPoint(camera, point, viewport, into = new Vector2()) {
|
|
485
|
+
const ndc = point.clone().project(camera);
|
|
486
|
+
return into.set((ndc.x + 1) / 2 * viewport.width, (1 - ndc.y) / 2 * viewport.height);
|
|
487
|
+
}
|
|
488
|
+
var POINTS_PER_MEASUREMENT = {
|
|
489
|
+
distance: 2,
|
|
490
|
+
angle: 3
|
|
491
|
+
};
|
|
492
|
+
function measurementFromPoints(kind, points, id) {
|
|
493
|
+
if (points.length < POINTS_PER_MEASUREMENT[kind]) return null;
|
|
494
|
+
const [a, b, c] = points;
|
|
495
|
+
if (kind === "distance") return { id, kind, points: [copy(a), copy(b)] };
|
|
496
|
+
return { id, kind, points: [copy(a), copy(b), copy(c)] };
|
|
497
|
+
}
|
|
498
|
+
function nextMeasurementId(held) {
|
|
499
|
+
return held.reduce((top, entry) => Math.max(top, entry.id), 0) + 1;
|
|
500
|
+
}
|
|
501
|
+
function distanceBetween(a, b) {
|
|
502
|
+
return Math.hypot(b.x - a.x, b.y - a.y, b.z - a.z);
|
|
503
|
+
}
|
|
504
|
+
function deltaBetween(a, b) {
|
|
505
|
+
return { x: b.x - a.x, y: b.y - a.y, z: b.z - a.z };
|
|
506
|
+
}
|
|
507
|
+
function midpoint(a, b) {
|
|
508
|
+
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 };
|
|
509
|
+
}
|
|
510
|
+
function angleAt(vertex, a, b) {
|
|
511
|
+
const u = new Vector32(a.x - vertex.x, a.y - vertex.y, a.z - vertex.z);
|
|
512
|
+
const w = new Vector32(b.x - vertex.x, b.y - vertex.y, b.z - vertex.z);
|
|
513
|
+
if (u.lengthSq() === 0 || w.lengthSq() === 0) return 0;
|
|
514
|
+
return u.angleTo(w) * 180 / Math.PI;
|
|
515
|
+
}
|
|
516
|
+
function lockToAxis(from, to) {
|
|
517
|
+
const delta = deltaBetween(from, to);
|
|
518
|
+
let axis = "x";
|
|
519
|
+
for (const candidate of ["y", "z"]) {
|
|
520
|
+
if (Math.abs(delta[candidate]) > Math.abs(delta[axis])) axis = candidate;
|
|
521
|
+
}
|
|
522
|
+
return { axis, point: { ...from, [axis]: to[axis] } };
|
|
523
|
+
}
|
|
524
|
+
function deltaLegs(a, b) {
|
|
525
|
+
const delta = deltaBetween(a, b);
|
|
526
|
+
const threshold = Math.max(1e-9, distanceBetween(a, b) * 1e-6);
|
|
527
|
+
const legs = [];
|
|
528
|
+
let cursor = copy(a);
|
|
529
|
+
for (const axis of ["x", "y", "z"]) {
|
|
530
|
+
if (Math.abs(delta[axis]) <= threshold) continue;
|
|
531
|
+
const to = { ...cursor, [axis]: b[axis] };
|
|
532
|
+
legs.push({ axis, from: cursor, to, length: Math.abs(delta[axis]) });
|
|
533
|
+
cursor = to;
|
|
534
|
+
}
|
|
535
|
+
return legs.length > 1 ? legs : [];
|
|
536
|
+
}
|
|
537
|
+
function angleArc(vertex, a, b, radius, segments = ANGLE_ARC_SEGMENTS) {
|
|
538
|
+
const v = new Vector32(vertex.x, vertex.y, vertex.z);
|
|
539
|
+
const u = new Vector32(a.x, a.y, a.z).sub(v);
|
|
540
|
+
const w = new Vector32(b.x, b.y, b.z).sub(v);
|
|
541
|
+
if (u.lengthSq() === 0 || w.lengthSq() === 0) return [];
|
|
542
|
+
u.normalize();
|
|
543
|
+
w.normalize();
|
|
544
|
+
const theta = u.angleTo(w);
|
|
545
|
+
const sine = Math.sin(theta);
|
|
546
|
+
if (sine < 1e-6) {
|
|
547
|
+
return [v.clone().addScaledVector(u, radius), v.clone().addScaledVector(w, radius)];
|
|
548
|
+
}
|
|
549
|
+
const points = [];
|
|
550
|
+
for (let i = 0; i <= segments; i += 1) {
|
|
551
|
+
const t = i / segments;
|
|
552
|
+
const direction = u.clone().multiplyScalar(Math.sin((1 - t) * theta) / sine).addScaledVector(w, Math.sin(t * theta) / sine);
|
|
553
|
+
points.push(v.clone().addScaledVector(direction, radius));
|
|
554
|
+
}
|
|
555
|
+
return points;
|
|
556
|
+
}
|
|
557
|
+
function angleArcRadius(vertex, a, b) {
|
|
558
|
+
return Math.min(distanceBetween(vertex, a), distanceBetween(vertex, b)) * ANGLE_ARC_FRACTION;
|
|
559
|
+
}
|
|
560
|
+
function formatMillimetres(mm, digits = 2) {
|
|
561
|
+
return `${mm.toFixed(digits)} mm`;
|
|
562
|
+
}
|
|
563
|
+
function formatDegrees(degrees, digits = 1) {
|
|
564
|
+
return `${degrees.toFixed(digits)}\xB0`;
|
|
565
|
+
}
|
|
566
|
+
function measurementLabel(measurement, format = formatMillimetres) {
|
|
567
|
+
if (measurement.kind === "distance") {
|
|
568
|
+
const [a2, b2] = measurement.points;
|
|
569
|
+
return format(distanceBetween(a2, b2));
|
|
570
|
+
}
|
|
571
|
+
const [a, vertex, b] = measurement.points;
|
|
572
|
+
return formatDegrees(angleAt(vertex, a, b));
|
|
573
|
+
}
|
|
574
|
+
function measurementLabelAnchor(measurement) {
|
|
575
|
+
if (measurement.kind === "distance") return midpoint(measurement.points[0], measurement.points[1]);
|
|
576
|
+
const [a, vertex, b] = measurement.points;
|
|
577
|
+
const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b) * 1.6, 2);
|
|
578
|
+
const middle = arc[1] ?? arc[0];
|
|
579
|
+
return middle ? { x: middle.x, y: middle.y, z: middle.z } : copy(vertex);
|
|
580
|
+
}
|
|
581
|
+
function copy(v) {
|
|
582
|
+
return { x: v.x, y: v.y, z: v.z };
|
|
583
|
+
}
|
|
584
|
+
|
|
354
585
|
// src/render/section.ts
|
|
355
586
|
var SECTION_RENDER_ORDER = {
|
|
356
587
|
stencil: 1,
|
|
@@ -361,6 +592,7 @@ var SECTION_RENDER_ORDER = {
|
|
|
361
592
|
};
|
|
362
593
|
var START_DEPTH = 5e-3;
|
|
363
594
|
var HANDLE_PIXELS = 78;
|
|
595
|
+
var SECTION_GIZMO_FRAME_SCALE = 1.1;
|
|
364
596
|
var CAP_HATCH = {
|
|
365
597
|
pitch: 9,
|
|
366
598
|
line: 1.25,
|
|
@@ -371,7 +603,7 @@ var AXES_PLANE_SCALE = 1.2;
|
|
|
371
603
|
var PREVIEW_SCALE = 0.125;
|
|
372
604
|
var OUTLINE_SCALE = 1.2;
|
|
373
605
|
var EPSILON2 = 1e-9;
|
|
374
|
-
var ARROW_AXIS = new
|
|
606
|
+
var ARROW_AXIS = new Vector33(0, 1, 0);
|
|
375
607
|
var DEFAULT_SECTION_NORMAL = { x: 0, y: 0, z: 1 };
|
|
376
608
|
var DISABLED_SECTION = {
|
|
377
609
|
enabled: false,
|
|
@@ -380,8 +612,25 @@ var DISABLED_SECTION = {
|
|
|
380
612
|
constant: 0,
|
|
381
613
|
plane: null,
|
|
382
614
|
depth: null,
|
|
615
|
+
cutDistance: 0,
|
|
383
616
|
depthRange: null
|
|
384
617
|
};
|
|
618
|
+
function sectionMeasurement(state) {
|
|
619
|
+
return `${(state.depth ?? state.cutDistance ?? 0).toFixed(2)} mm`;
|
|
620
|
+
}
|
|
621
|
+
function sectionDirectionColor(normal) {
|
|
622
|
+
const x = Math.abs(normal.x);
|
|
623
|
+
const y = Math.abs(normal.y);
|
|
624
|
+
const z = Math.abs(normal.z);
|
|
625
|
+
const total = x + y + z || 1;
|
|
626
|
+
const channel = (shift) => Math.round(
|
|
627
|
+
((AXIS_COLORS.x >> shift & 255) * x + (AXIS_COLORS.y >> shift & 255) * y + (AXIS_COLORS.z >> shift & 255) * z) / total
|
|
628
|
+
);
|
|
629
|
+
return channel(16) << 16 | channel(8) << 8 | channel(0);
|
|
630
|
+
}
|
|
631
|
+
function sectionHandleColor(themeColor, directionColor2) {
|
|
632
|
+
return themeColor ?? directionColor2;
|
|
633
|
+
}
|
|
385
634
|
function sectionOptionsFromState(state) {
|
|
386
635
|
if (state.plane) {
|
|
387
636
|
return { enabled: state.enabled, plane: state.plane, depth: state.depth ?? 0 };
|
|
@@ -389,8 +638,8 @@ function sectionOptionsFromState(state) {
|
|
|
389
638
|
return { enabled: state.enabled, normal: state.normal, offset: state.offset };
|
|
390
639
|
}
|
|
391
640
|
function axesPlanes(box, eye) {
|
|
392
|
-
const centre = box.getCenter(new
|
|
393
|
-
const extent = box.getSize(new
|
|
641
|
+
const centre = box.getCenter(new Vector33());
|
|
642
|
+
const extent = box.getSize(new Vector33());
|
|
394
643
|
const largest = Math.max(extent.x, extent.y, extent.z);
|
|
395
644
|
const size = largest * AXES_PLANE_SCALE;
|
|
396
645
|
const offset = largest * AXES_PLANE_OFFSET;
|
|
@@ -425,7 +674,7 @@ function axesPlanes(box, eye) {
|
|
|
425
674
|
function hitUnderRay(raycaster, root) {
|
|
426
675
|
for (const hit of raycaster.intersectObjects(root.children, true)) {
|
|
427
676
|
if (!("isMesh" in hit.object) || !hit.face) continue;
|
|
428
|
-
if (!hit.object.visible ||
|
|
677
|
+
if (!hit.object.visible || excludedFromPart(hit.object, root)) continue;
|
|
429
678
|
if (clippedAway(hit)) continue;
|
|
430
679
|
return hit;
|
|
431
680
|
}
|
|
@@ -447,21 +696,21 @@ function surfaceUnderRay(raycaster, root) {
|
|
|
447
696
|
const normal = hit.face.normal.clone().transformDirection(hit.object.matrixWorld);
|
|
448
697
|
return { point: hit.point, normal };
|
|
449
698
|
}
|
|
450
|
-
function sectionAnchor(box, plane, into = new
|
|
699
|
+
function sectionAnchor(box, plane, into = new Vector33()) {
|
|
451
700
|
const centre = box.getCenter(into);
|
|
452
701
|
return centre.addScaledVector(plane.normal, -(plane.constant + plane.normal.dot(centre)));
|
|
453
702
|
}
|
|
454
703
|
var PICKED_SURFACE_LABEL = "Part surface";
|
|
455
704
|
function unit(v) {
|
|
456
|
-
const vector = new
|
|
705
|
+
const vector = new Vector33(v.x, v.y, v.z);
|
|
457
706
|
return vector.lengthSq() === 0 ? vector.set(0, 0, 1) : vector.normalize();
|
|
458
707
|
}
|
|
459
708
|
function clamp01(value) {
|
|
460
709
|
return Math.min(1, Math.max(0, value));
|
|
461
710
|
}
|
|
462
|
-
function
|
|
711
|
+
function projectedBounds(box, normal) {
|
|
463
712
|
const axis = unit(normal);
|
|
464
|
-
const corner = new
|
|
713
|
+
const corner = new Vector33();
|
|
465
714
|
let low = Number.POSITIVE_INFINITY;
|
|
466
715
|
let high = Number.NEGATIVE_INFINITY;
|
|
467
716
|
for (let i = 0; i < 8; i += 1) {
|
|
@@ -474,9 +723,17 @@ function sectionBounds(box, normal) {
|
|
|
474
723
|
low = Math.min(low, distance);
|
|
475
724
|
high = Math.max(high, distance);
|
|
476
725
|
}
|
|
726
|
+
return { low, high };
|
|
727
|
+
}
|
|
728
|
+
function sectionBounds(box, normal) {
|
|
729
|
+
const { low, high } = projectedBounds(box, normal);
|
|
477
730
|
const margin = Math.max((high - low) * 5e-3, 1e-6);
|
|
478
731
|
return { min: -(high + margin), max: -(low - margin) };
|
|
479
732
|
}
|
|
733
|
+
function sectionCutDistance(box, normal, constant) {
|
|
734
|
+
const { low, high } = projectedBounds(box, normal);
|
|
735
|
+
return Math.max(0, Math.min(high - low, -constant - low));
|
|
736
|
+
}
|
|
480
737
|
function sectionConstant(bounds, t) {
|
|
481
738
|
return bounds.max + clamp01(t) * (bounds.min - bounds.max);
|
|
482
739
|
}
|
|
@@ -485,7 +742,7 @@ function sectionOffset(bounds, constant) {
|
|
|
485
742
|
return span === 0 ? 0 : clamp01((constant - bounds.max) / span);
|
|
486
743
|
}
|
|
487
744
|
function sectionDepth(normal, anchor, constant) {
|
|
488
|
-
return -unit(normal).dot(new
|
|
745
|
+
return -unit(normal).dot(new Vector33(anchor.x, anchor.y, anchor.z)) - constant;
|
|
489
746
|
}
|
|
490
747
|
function sectionDepthConstant(normal, anchor, depth) {
|
|
491
748
|
return sectionDepth(normal, anchor, depth);
|
|
@@ -504,7 +761,7 @@ function sectionFromPick(surface, label = PICKED_SURFACE_LABEL) {
|
|
|
504
761
|
};
|
|
505
762
|
}
|
|
506
763
|
function pickedStartDepth(box) {
|
|
507
|
-
return box.getSize(new
|
|
764
|
+
return box.getSize(new Vector33()).length() * START_DEPTH;
|
|
508
765
|
}
|
|
509
766
|
function sectionPlane(box, normal, offset, into = new Plane()) {
|
|
510
767
|
const axis = unit(normal);
|
|
@@ -512,12 +769,12 @@ function sectionPlane(box, normal, offset, into = new Plane()) {
|
|
|
512
769
|
}
|
|
513
770
|
function dragPlane(axis, view, point, into = new Plane()) {
|
|
514
771
|
const along = axis.lengthSq() === 0 ? ARROW_AXIS.clone() : axis.clone().normalize();
|
|
515
|
-
const side = new
|
|
772
|
+
const side = new Vector33().crossVectors(view, along);
|
|
516
773
|
if (side.lengthSq() < EPSILON2) side.copy(perpendicular(along));
|
|
517
774
|
return into.setFromNormalAndCoplanarPoint(side.cross(along).normalize(), point);
|
|
518
775
|
}
|
|
519
776
|
function perpendicular(axis) {
|
|
520
|
-
const candidate = Math.abs(axis.x) < 0.9 ? new
|
|
777
|
+
const candidate = Math.abs(axis.x) < 0.9 ? new Vector33(1, 0, 0) : new Vector33(0, 1, 0);
|
|
521
778
|
return candidate.cross(axis).normalize();
|
|
522
779
|
}
|
|
523
780
|
|
|
@@ -593,15 +850,17 @@ import {
|
|
|
593
850
|
Box3,
|
|
594
851
|
Color,
|
|
595
852
|
DataTexture,
|
|
853
|
+
DoubleSide,
|
|
596
854
|
Float32BufferAttribute as Float32BufferAttribute2,
|
|
597
855
|
Group,
|
|
598
856
|
LineBasicMaterial,
|
|
599
857
|
LineSegments,
|
|
600
858
|
Mesh,
|
|
601
859
|
MeshLambertMaterial,
|
|
860
|
+
RedFormat,
|
|
602
861
|
RGBAFormat,
|
|
603
862
|
UnsignedByteType,
|
|
604
|
-
Vector3 as
|
|
863
|
+
Vector3 as Vector34
|
|
605
864
|
} from "three";
|
|
606
865
|
|
|
607
866
|
// src/render/edges.ts
|
|
@@ -700,6 +959,13 @@ function createPart(model, geometry, theme) {
|
|
|
700
959
|
const state = new Uint8Array(width * 4);
|
|
701
960
|
const stateTexture = new DataTexture(state, width, 1, RGBAFormat, UnsignedByteType);
|
|
702
961
|
stateTexture.needsUpdate = true;
|
|
962
|
+
const opacity = new Uint8Array(width).fill(255);
|
|
963
|
+
const opacityTexture = new DataTexture(opacity, width, 1, RedFormat, UnsignedByteType);
|
|
964
|
+
opacityTexture.needsUpdate = true;
|
|
965
|
+
const focusState = { value: false };
|
|
966
|
+
const wireframe = { value: false };
|
|
967
|
+
let focused = false;
|
|
968
|
+
let currentTheme = theme;
|
|
703
969
|
const material = new MeshLambertMaterial({
|
|
704
970
|
color: theme.part,
|
|
705
971
|
emissive: theme.partEmissive,
|
|
@@ -709,40 +975,64 @@ function createPart(model, geometry, theme) {
|
|
|
709
975
|
polygonOffsetFactor: 1,
|
|
710
976
|
polygonOffsetUnits: 1
|
|
711
977
|
});
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
"
|
|
716
|
-
|
|
978
|
+
const configureShader = (target, opaqueFocusPass) => {
|
|
979
|
+
target.onBeforeCompile = (shader) => {
|
|
980
|
+
shader.uniforms["uRegionState"] = { value: stateTexture };
|
|
981
|
+
shader.uniforms["uRegionOpacity"] = { value: opacityTexture };
|
|
982
|
+
shader.uniforms["uWireframe"] = wireframe;
|
|
983
|
+
shader.uniforms["uFocus"] = focusState;
|
|
984
|
+
shader.uniforms["uOpaqueFocusPass"] = { value: opaqueFocusPass };
|
|
985
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
986
|
+
"#include <common>",
|
|
987
|
+
`#include <common>
|
|
717
988
|
attribute float ${REGION_ATTRIBUTE};
|
|
718
989
|
varying float vRegion;`
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
990
|
+
).replace(
|
|
991
|
+
"#include <begin_vertex>",
|
|
992
|
+
`#include <begin_vertex>
|
|
722
993
|
vRegion = ${REGION_ATTRIBUTE};`
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
994
|
+
);
|
|
995
|
+
shader.fragmentShader = shader.fragmentShader.replace(
|
|
996
|
+
"#include <common>",
|
|
997
|
+
`#include <common>
|
|
727
998
|
uniform sampler2D uRegionState;
|
|
999
|
+
uniform sampler2D uRegionOpacity;
|
|
1000
|
+
uniform bool uWireframe;
|
|
1001
|
+
uniform bool uFocus;
|
|
1002
|
+
uniform bool uOpaqueFocusPass;
|
|
728
1003
|
varying float vRegion;
|
|
729
1004
|
vec4 regionState;`
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
1005
|
+
).replace(
|
|
1006
|
+
"#include <color_fragment>",
|
|
1007
|
+
`#include <color_fragment>
|
|
733
1008
|
regionState = texelFetch(uRegionState, ivec2(int(vRegion + 0.5), 0), 0);
|
|
734
1009
|
diffuseColor.rgb = mix(diffuseColor.rgb, regionState.rgb, regionState.a);
|
|
1010
|
+
float regionOpacity = texelFetch(uRegionOpacity, ivec2(int(vRegion + 0.5), 0), 0).r;
|
|
1011
|
+
if (uFocus && uOpaqueFocusPass && regionOpacity < 0.999) discard;
|
|
1012
|
+
if (uFocus && !uOpaqueFocusPass && regionOpacity >= 0.999) discard;
|
|
1013
|
+
diffuseColor.a *= regionOpacity;
|
|
1014
|
+
if (uWireframe) diffuseColor.a *= regionState.a;
|
|
735
1015
|
`
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1016
|
+
).replace(
|
|
1017
|
+
"#include <emissivemap_fragment>",
|
|
1018
|
+
`#include <emissivemap_fragment>
|
|
739
1019
|
totalEmissiveRadiance = mix(
|
|
740
1020
|
totalEmissiveRadiance,
|
|
741
1021
|
regionState.rgb * ${EMISSIVE_MIX.toFixed(2)},
|
|
742
1022
|
regionState.a
|
|
743
1023
|
);`
|
|
744
|
-
|
|
1024
|
+
);
|
|
1025
|
+
};
|
|
745
1026
|
};
|
|
1027
|
+
const focusMaterial = new MeshLambertMaterial({
|
|
1028
|
+
color: theme.part,
|
|
1029
|
+
emissive: theme.partEmissive,
|
|
1030
|
+
// A selected face can be viewed through the part from the side its normal
|
|
1031
|
+
// faces away from.
|
|
1032
|
+
side: DoubleSide
|
|
1033
|
+
});
|
|
1034
|
+
configureShader(focusMaterial, true);
|
|
1035
|
+
configureShader(material, false);
|
|
746
1036
|
const mesh = new Mesh(geometry, material);
|
|
747
1037
|
mesh.renderOrder = 3;
|
|
748
1038
|
const edgeGeometry = regionEdgesGeometry(geometry, model);
|
|
@@ -755,10 +1045,25 @@ function createPart(model, geometry, theme) {
|
|
|
755
1045
|
edges.renderOrder = 4;
|
|
756
1046
|
edges.raycast = () => {
|
|
757
1047
|
};
|
|
1048
|
+
const focusMesh = new Mesh(geometry, focusMaterial);
|
|
1049
|
+
focusMesh.renderOrder = 3;
|
|
1050
|
+
focusMesh.visible = false;
|
|
1051
|
+
focusMesh.raycast = () => {
|
|
1052
|
+
};
|
|
1053
|
+
const syncTransparency = () => {
|
|
1054
|
+
const transparent = wireframe.value || focused;
|
|
1055
|
+
if (material.transparent !== transparent || material.depthWrite !== !transparent) {
|
|
1056
|
+
material.transparent = transparent;
|
|
1057
|
+
material.depthWrite = !transparent;
|
|
1058
|
+
material.needsUpdate = true;
|
|
1059
|
+
}
|
|
1060
|
+
focusState.value = focused;
|
|
1061
|
+
focusMesh.visible = focused;
|
|
1062
|
+
};
|
|
758
1063
|
const object = new Group();
|
|
759
|
-
object.add(mesh, edges);
|
|
1064
|
+
object.add(focusMesh, mesh, edges);
|
|
760
1065
|
const scratchColor = new Color();
|
|
761
|
-
const scratchVector = new
|
|
1066
|
+
const scratchVector = new Vector34();
|
|
762
1067
|
const paintRegion = (region, color, weight) => {
|
|
763
1068
|
const column = texels.get(region);
|
|
764
1069
|
if (column === void 0) return;
|
|
@@ -796,6 +1101,27 @@ function createPart(model, geometry, theme) {
|
|
|
796
1101
|
state.fill(0);
|
|
797
1102
|
stateTexture.needsUpdate = true;
|
|
798
1103
|
},
|
|
1104
|
+
setFocus(selection, focus) {
|
|
1105
|
+
const regions = focus === void 0 ? null : focusedRegions(model, selection);
|
|
1106
|
+
const outside = focusOpacity(focus);
|
|
1107
|
+
let changed = false;
|
|
1108
|
+
let hasTransparency = false;
|
|
1109
|
+
for (const [region, column] of texels) {
|
|
1110
|
+
const value = regions === null || regions.size === 0 || regions.has(region) ? 255 : Math.round(outside * 255);
|
|
1111
|
+
if (opacity[column] !== value) {
|
|
1112
|
+
opacity[column] = value;
|
|
1113
|
+
changed = true;
|
|
1114
|
+
}
|
|
1115
|
+
hasTransparency ||= value < 255;
|
|
1116
|
+
}
|
|
1117
|
+
focused = hasTransparency;
|
|
1118
|
+
syncTransparency();
|
|
1119
|
+
if (changed) opacityTexture.needsUpdate = true;
|
|
1120
|
+
},
|
|
1121
|
+
regionOpacity(region) {
|
|
1122
|
+
const column = texels.get(region);
|
|
1123
|
+
return column === void 0 ? null : (opacity[column] ?? 0) / 255;
|
|
1124
|
+
},
|
|
799
1125
|
boxForFeature(tag) {
|
|
800
1126
|
const regions = model.regionIndex.regionsForFeature(tag);
|
|
801
1127
|
if (regions.length === 0) return null;
|
|
@@ -812,13 +1138,27 @@ function createPart(model, geometry, theme) {
|
|
|
812
1138
|
setClippingPlanes(planes) {
|
|
813
1139
|
const value = planes === null ? null : [...planes];
|
|
814
1140
|
material.clippingPlanes = value;
|
|
1141
|
+
focusMaterial.clippingPlanes = value;
|
|
815
1142
|
edgeMaterial.clippingPlanes = value;
|
|
816
1143
|
},
|
|
817
1144
|
setTheme(next) {
|
|
1145
|
+
currentTheme = next;
|
|
818
1146
|
material.color.setHex(next.part);
|
|
819
1147
|
material.emissive.setHex(next.partEmissive);
|
|
820
|
-
|
|
821
|
-
|
|
1148
|
+
focusMaterial.color.setHex(next.part);
|
|
1149
|
+
focusMaterial.emissive.setHex(next.partEmissive);
|
|
1150
|
+
edgeMaterial.color.setHex(wireframe.value ? next.part : next.edge);
|
|
1151
|
+
edgeMaterial.opacity = wireframe.value ? 1 : next.edgeOpacity;
|
|
1152
|
+
},
|
|
1153
|
+
setDisplay(display, showEdges = true) {
|
|
1154
|
+
const enabled = display === "wireframe";
|
|
1155
|
+
if (wireframe.value !== enabled) {
|
|
1156
|
+
wireframe.value = enabled;
|
|
1157
|
+
syncTransparency();
|
|
1158
|
+
}
|
|
1159
|
+
edges.visible = enabled || showEdges;
|
|
1160
|
+
edgeMaterial.color.setHex(enabled ? currentTheme.part : currentTheme.edge);
|
|
1161
|
+
edgeMaterial.opacity = enabled ? 1 : currentTheme.edgeOpacity;
|
|
822
1162
|
},
|
|
823
1163
|
dispose() {
|
|
824
1164
|
object.clear();
|
|
@@ -826,9 +1166,11 @@ function createPart(model, geometry, theme) {
|
|
|
826
1166
|
geometry.deleteAttribute(REGION_ATTRIBUTE);
|
|
827
1167
|
}
|
|
828
1168
|
material.dispose();
|
|
1169
|
+
focusMaterial.dispose();
|
|
829
1170
|
edgeGeometry.dispose();
|
|
830
1171
|
edgeMaterial.dispose();
|
|
831
1172
|
stateTexture.dispose();
|
|
1173
|
+
opacityTexture.dispose();
|
|
832
1174
|
}
|
|
833
1175
|
};
|
|
834
1176
|
}
|
|
@@ -962,6 +1304,7 @@ function buildPick(input) {
|
|
|
962
1304
|
triangleIndex: input.triangleIndex,
|
|
963
1305
|
point: input.point,
|
|
964
1306
|
normal: input.normal,
|
|
1307
|
+
...input.pointer === void 0 ? {} : { pointer: input.pointer },
|
|
965
1308
|
modifiers: input.modifiers ?? NO_MODIFIERS,
|
|
966
1309
|
doubled: input.doubled ?? false
|
|
967
1310
|
};
|
|
@@ -985,7 +1328,7 @@ import {
|
|
|
985
1328
|
useMemo as useMemo4,
|
|
986
1329
|
useRef as useRef3
|
|
987
1330
|
} from "react";
|
|
988
|
-
import { Box3 as Box33, Vector3 as
|
|
1331
|
+
import { Box3 as Box33, Vector3 as Vector38 } from "three";
|
|
989
1332
|
|
|
990
1333
|
// src/camera.tsx
|
|
991
1334
|
import { useFrame, useThree as useThree2 } from "@react-three/fiber";
|
|
@@ -1001,10 +1344,96 @@ import {
|
|
|
1001
1344
|
Raycaster,
|
|
1002
1345
|
Sphere,
|
|
1003
1346
|
Spherical,
|
|
1004
|
-
Vector2,
|
|
1005
|
-
Vector3 as
|
|
1347
|
+
Vector2 as Vector22,
|
|
1348
|
+
Vector3 as Vector35,
|
|
1006
1349
|
Vector4
|
|
1007
1350
|
} from "three";
|
|
1351
|
+
|
|
1352
|
+
// src/render/control-schemes.ts
|
|
1353
|
+
var CONTROL_SCHEME_OPTIONS = [
|
|
1354
|
+
{ value: "toolpath", label: "Toolpath" },
|
|
1355
|
+
{ value: "fusion", label: "Fusion" },
|
|
1356
|
+
{ value: "alias", label: "Alias" },
|
|
1357
|
+
{ value: "inventor", label: "Inventor" },
|
|
1358
|
+
{ value: "solidworks", label: "SolidWorks" },
|
|
1359
|
+
{ value: "tinkercad", label: "Tinkercad" },
|
|
1360
|
+
{ value: "powermill", label: "PowerMill" },
|
|
1361
|
+
{ value: "onshape", label: "Onshape" }
|
|
1362
|
+
];
|
|
1363
|
+
var DEFAULT_TOUCHES = {
|
|
1364
|
+
one: "rotate",
|
|
1365
|
+
two: "dolly-truck",
|
|
1366
|
+
three: "truck"
|
|
1367
|
+
};
|
|
1368
|
+
var wheelFor = (orthographic) => orthographic ? "zoom" : "dolly";
|
|
1369
|
+
var mapping = (environment, mouse, options, touches = DEFAULT_TOUCHES) => ({
|
|
1370
|
+
mouse: {
|
|
1371
|
+
left: "none",
|
|
1372
|
+
middle: "none",
|
|
1373
|
+
right: "none",
|
|
1374
|
+
wheel: wheelFor(environment.orthographic),
|
|
1375
|
+
...mouse
|
|
1376
|
+
},
|
|
1377
|
+
touches,
|
|
1378
|
+
...options
|
|
1379
|
+
});
|
|
1380
|
+
var resolveControlScheme = (scheme, environment) => {
|
|
1381
|
+
const { shift, ctrl } = environment.modifiers;
|
|
1382
|
+
switch (scheme) {
|
|
1383
|
+
case "fusion":
|
|
1384
|
+
case "inventor": {
|
|
1385
|
+
const rotating = shift;
|
|
1386
|
+
return mapping(
|
|
1387
|
+
environment,
|
|
1388
|
+
{ middle: rotating ? "rotate" : "truck", wheel: "none" },
|
|
1389
|
+
{ usesCadWheel: true, immediate: true },
|
|
1390
|
+
{
|
|
1391
|
+
one: "rotate",
|
|
1392
|
+
two: rotating ? "rotate" : "truck",
|
|
1393
|
+
three: "truck"
|
|
1394
|
+
}
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
case "solidworks":
|
|
1398
|
+
return mapping(
|
|
1399
|
+
environment,
|
|
1400
|
+
{ middle: shift ? "zoom" : ctrl ? "truck" : "rotate" },
|
|
1401
|
+
{ usesCadWheel: false, immediate: false }
|
|
1402
|
+
);
|
|
1403
|
+
case "alias":
|
|
1404
|
+
return mapping(
|
|
1405
|
+
environment,
|
|
1406
|
+
{ left: "rotate", middle: "truck" },
|
|
1407
|
+
{ usesCadWheel: false, immediate: false }
|
|
1408
|
+
);
|
|
1409
|
+
case "tinkercad":
|
|
1410
|
+
return mapping(
|
|
1411
|
+
environment,
|
|
1412
|
+
{ right: shift ? "truck" : "rotate" },
|
|
1413
|
+
{ usesCadWheel: false, immediate: false }
|
|
1414
|
+
);
|
|
1415
|
+
case "powermill":
|
|
1416
|
+
return mapping(
|
|
1417
|
+
environment,
|
|
1418
|
+
{ middle: shift ? "truck" : "rotate" },
|
|
1419
|
+
{ usesCadWheel: false, immediate: false }
|
|
1420
|
+
);
|
|
1421
|
+
case "onshape":
|
|
1422
|
+
return mapping(
|
|
1423
|
+
environment,
|
|
1424
|
+
{ middle: "truck", right: "rotate" },
|
|
1425
|
+
{ usesCadWheel: false, immediate: false }
|
|
1426
|
+
);
|
|
1427
|
+
case "toolpath":
|
|
1428
|
+
return mapping(
|
|
1429
|
+
environment,
|
|
1430
|
+
{ left: "rotate", right: "truck" },
|
|
1431
|
+
{ usesCadWheel: false, immediate: false }
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
// src/render/controls.ts
|
|
1008
1437
|
CameraControls.install({
|
|
1009
1438
|
THREE: {
|
|
1010
1439
|
Box3: Box32,
|
|
@@ -1013,8 +1442,8 @@ CameraControls.install({
|
|
|
1013
1442
|
Raycaster,
|
|
1014
1443
|
Sphere,
|
|
1015
1444
|
Spherical,
|
|
1016
|
-
Vector2,
|
|
1017
|
-
Vector3:
|
|
1445
|
+
Vector2: Vector22,
|
|
1446
|
+
Vector3: Vector35,
|
|
1018
1447
|
Vector4
|
|
1019
1448
|
}
|
|
1020
1449
|
});
|
|
@@ -1032,13 +1461,14 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
1032
1461
|
#attached = false;
|
|
1033
1462
|
#autoUpEnabled = false;
|
|
1034
1463
|
#shiftPressed = false;
|
|
1464
|
+
#ctrlPressed = false;
|
|
1035
1465
|
#wheelHandler = null;
|
|
1036
1466
|
// Scratch objects — `#onPointerMove` and `#adaptUpVector` run at pointer and
|
|
1037
1467
|
// frame rate respectively, so neither may allocate.
|
|
1038
1468
|
#spherical = new Spherical();
|
|
1039
|
-
#scratchA = new
|
|
1040
|
-
#scratchB = new
|
|
1041
|
-
#scratchC = new
|
|
1469
|
+
#scratchA = new Vector35();
|
|
1470
|
+
#scratchB = new Vector35();
|
|
1471
|
+
#scratchC = new Vector35();
|
|
1042
1472
|
constructor(camera, domElement, options = {}) {
|
|
1043
1473
|
super(camera, domElement);
|
|
1044
1474
|
this.#domElement = domElement;
|
|
@@ -1101,7 +1531,7 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
1101
1531
|
view?.removeEventListener("keyup", this.#onModifierChange);
|
|
1102
1532
|
view?.removeEventListener("blur", this.#onWindowBlur);
|
|
1103
1533
|
this.#disableAutoUp();
|
|
1104
|
-
this.#
|
|
1534
|
+
this.#disableCadWheel();
|
|
1105
1535
|
}
|
|
1106
1536
|
dispose() {
|
|
1107
1537
|
this.detach();
|
|
@@ -1114,29 +1544,38 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
1114
1544
|
*/
|
|
1115
1545
|
applyScheme(scheme) {
|
|
1116
1546
|
this.#scheme = scheme;
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
this
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1547
|
+
const mapping2 = resolveControlScheme(scheme, {
|
|
1548
|
+
orthographic: this.camera instanceof OrthographicCamera2,
|
|
1549
|
+
modifiers: { shift: this.#shiftPressed, ctrl: this.#ctrlPressed }
|
|
1550
|
+
});
|
|
1551
|
+
this.#applyMapping(mapping2);
|
|
1552
|
+
}
|
|
1553
|
+
#applyMapping(mapping2) {
|
|
1554
|
+
const action = {
|
|
1555
|
+
none: CameraControls.ACTION.NONE,
|
|
1556
|
+
rotate: CameraControls.ACTION.ROTATE,
|
|
1557
|
+
truck: CameraControls.ACTION.TRUCK,
|
|
1558
|
+
zoom: CameraControls.ACTION.ZOOM,
|
|
1559
|
+
dolly: CameraControls.ACTION.DOLLY
|
|
1560
|
+
};
|
|
1561
|
+
const touchAction = {
|
|
1562
|
+
rotate: CameraControls.ACTION.TOUCH_ROTATE,
|
|
1563
|
+
truck: CameraControls.ACTION.TOUCH_TRUCK,
|
|
1564
|
+
"dolly-truck": CameraControls.ACTION.TOUCH_DOLLY_TRUCK
|
|
1565
|
+
};
|
|
1566
|
+
this.#disableCadWheel();
|
|
1567
|
+
this.mouseButtons.left = action[mapping2.mouse.left];
|
|
1568
|
+
this.mouseButtons.middle = action[mapping2.mouse.middle];
|
|
1569
|
+
this.mouseButtons.right = action[mapping2.mouse.right];
|
|
1570
|
+
this.mouseButtons.wheel = action[mapping2.mouse.wheel];
|
|
1571
|
+
this.touches.one = touchAction[mapping2.touches.one];
|
|
1572
|
+
this.touches.two = touchAction[mapping2.touches.two];
|
|
1573
|
+
this.touches.three = touchAction[mapping2.touches.three];
|
|
1574
|
+
this.smoothTime = mapping2.immediate ? 0 : DEFAULT_SMOOTH_TIME;
|
|
1575
|
+
this.draggingSmoothTime = mapping2.immediate ? 0 : DEFAULT_SMOOTH_TIME;
|
|
1576
|
+
if (mapping2.usesCadWheel) {
|
|
1577
|
+
this.#enableCadWheel();
|
|
1135
1578
|
}
|
|
1136
|
-
this.mouseButtons.left = CameraControls.ACTION.ROTATE;
|
|
1137
|
-
this.mouseButtons.right = CameraControls.ACTION.TRUCK;
|
|
1138
|
-
this.mouseButtons.middle = CameraControls.ACTION.TRUCK;
|
|
1139
|
-
this.mouseButtons.wheel = this.camera instanceof OrthographicCamera2 ? CameraControls.ACTION.ZOOM : CameraControls.ACTION.DOLLY;
|
|
1140
1579
|
}
|
|
1141
1580
|
setFreeOrbit(freeOrbit) {
|
|
1142
1581
|
if (this.#freeOrbit === freeOrbit) {
|
|
@@ -1174,13 +1613,16 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
1174
1613
|
this.#autoUpEnabled = false;
|
|
1175
1614
|
this.removeEventListener("update", this.#adaptUpVector);
|
|
1176
1615
|
}
|
|
1177
|
-
#
|
|
1178
|
-
this.#
|
|
1616
|
+
#enableCadWheel() {
|
|
1617
|
+
if (!this.#attached || this.#wheelHandler) {
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
this.#wheelHandler = (event) => this.#onCadWheel(event);
|
|
1179
1621
|
this.#domElement.addEventListener("wheel", this.#wheelHandler, {
|
|
1180
1622
|
passive: false
|
|
1181
1623
|
});
|
|
1182
1624
|
}
|
|
1183
|
-
#
|
|
1625
|
+
#disableCadWheel() {
|
|
1184
1626
|
if (!this.#wheelHandler) {
|
|
1185
1627
|
return;
|
|
1186
1628
|
}
|
|
@@ -1223,7 +1665,7 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
1223
1665
|
void this.rotateTo(azimuth, polar, false);
|
|
1224
1666
|
this.update(0);
|
|
1225
1667
|
};
|
|
1226
|
-
#
|
|
1668
|
+
#onCadWheel = (event) => {
|
|
1227
1669
|
event.preventDefault();
|
|
1228
1670
|
if (event.ctrlKey) {
|
|
1229
1671
|
void this.zoom(-event.deltaY * FUSION_PINCH_ZOOM_SCALE, false);
|
|
@@ -1241,19 +1683,18 @@ var ExtendedCameraControls = class extends CameraControls {
|
|
|
1241
1683
|
this.dispatchEvent({ type: "control" });
|
|
1242
1684
|
};
|
|
1243
1685
|
#onModifierChange = (event) => {
|
|
1244
|
-
this.#
|
|
1686
|
+
this.#setModifiers(event.shiftKey, event.ctrlKey);
|
|
1245
1687
|
};
|
|
1246
1688
|
#onWindowBlur = () => {
|
|
1247
|
-
this.#
|
|
1689
|
+
this.#setModifiers(false, false);
|
|
1248
1690
|
};
|
|
1249
|
-
#
|
|
1250
|
-
if (this.#shiftPressed ===
|
|
1691
|
+
#setModifiers(shift, ctrl) {
|
|
1692
|
+
if (this.#shiftPressed === shift && this.#ctrlPressed === ctrl) {
|
|
1251
1693
|
return;
|
|
1252
1694
|
}
|
|
1253
|
-
this.#shiftPressed =
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
}
|
|
1695
|
+
this.#shiftPressed = shift;
|
|
1696
|
+
this.#ctrlPressed = ctrl;
|
|
1697
|
+
this.applyScheme(this.#scheme);
|
|
1257
1698
|
}
|
|
1258
1699
|
};
|
|
1259
1700
|
|
|
@@ -1328,11 +1769,12 @@ function retargetPose(cameraPosition, currentTarget, hitPoint) {
|
|
|
1328
1769
|
// src/render/section-store.ts
|
|
1329
1770
|
function createSectionStore() {
|
|
1330
1771
|
let current = null;
|
|
1331
|
-
let engaged = 0;
|
|
1332
1772
|
const listeners = /* @__PURE__ */ new Set();
|
|
1333
1773
|
const notify = () => {
|
|
1334
1774
|
for (const listener of listeners) listener();
|
|
1335
1775
|
};
|
|
1776
|
+
const engaged = counted(notify);
|
|
1777
|
+
const picking = counted(notify);
|
|
1336
1778
|
return {
|
|
1337
1779
|
get: () => current,
|
|
1338
1780
|
set: (next) => {
|
|
@@ -1340,12 +1782,10 @@ function createSectionStore() {
|
|
|
1340
1782
|
current = next;
|
|
1341
1783
|
notify();
|
|
1342
1784
|
},
|
|
1343
|
-
isEngaged:
|
|
1344
|
-
setEngaged:
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
if (engaged > 0 !== was) notify();
|
|
1348
|
-
},
|
|
1785
|
+
isEngaged: engaged.is,
|
|
1786
|
+
setEngaged: engaged.set,
|
|
1787
|
+
isPicking: picking.is,
|
|
1788
|
+
setPicking: picking.set,
|
|
1349
1789
|
subscribe: (listener) => {
|
|
1350
1790
|
listeners.add(listener);
|
|
1351
1791
|
return () => {
|
|
@@ -1354,15 +1794,26 @@ function createSectionStore() {
|
|
|
1354
1794
|
}
|
|
1355
1795
|
};
|
|
1356
1796
|
}
|
|
1797
|
+
function counted(notify) {
|
|
1798
|
+
let held = 0;
|
|
1799
|
+
return {
|
|
1800
|
+
is: () => held > 0,
|
|
1801
|
+
set: (next) => {
|
|
1802
|
+
const was = held > 0;
|
|
1803
|
+
held = Math.max(0, held + (next ? 1 : -1));
|
|
1804
|
+
if (held > 0 !== was) notify();
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1357
1808
|
|
|
1358
1809
|
// src/target-marker.tsx
|
|
1359
1810
|
import { useFrame as useFrame2, useThree as useThree3 } from "@react-three/fiber";
|
|
1360
1811
|
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
|
|
1361
1812
|
import {
|
|
1362
|
-
DoubleSide,
|
|
1813
|
+
DoubleSide as DoubleSide2,
|
|
1363
1814
|
RingGeometry,
|
|
1364
1815
|
SphereGeometry,
|
|
1365
|
-
Vector3 as
|
|
1816
|
+
Vector3 as Vector36
|
|
1366
1817
|
} from "three";
|
|
1367
1818
|
|
|
1368
1819
|
// src/render/target.ts
|
|
@@ -1405,8 +1856,8 @@ var TargetMarker = ({ controlsRef }) => {
|
|
|
1405
1856
|
const heldUntil = useRef2(0);
|
|
1406
1857
|
const painted = useRef2(-1);
|
|
1407
1858
|
const placed = useRef2(false);
|
|
1408
|
-
const lastTarget = useMemo3(() => new
|
|
1409
|
-
const scratch = useMemo3(() => new
|
|
1859
|
+
const lastTarget = useMemo3(() => new Vector36(), []);
|
|
1860
|
+
const scratch = useMemo3(() => new Vector36(), []);
|
|
1410
1861
|
useEffect3(() => {
|
|
1411
1862
|
const controls = controlsRef.current;
|
|
1412
1863
|
if (!controls) return void 0;
|
|
@@ -1472,7 +1923,7 @@ var TargetMarker = ({ controlsRef }) => {
|
|
|
1472
1923
|
opacity: 0,
|
|
1473
1924
|
depthTest: false,
|
|
1474
1925
|
depthWrite: false,
|
|
1475
|
-
side:
|
|
1926
|
+
side: DoubleSide2
|
|
1476
1927
|
}
|
|
1477
1928
|
)
|
|
1478
1929
|
}
|
|
@@ -1506,7 +1957,7 @@ import {
|
|
|
1506
1957
|
CanvasTexture,
|
|
1507
1958
|
Float32BufferAttribute as Float32BufferAttribute3,
|
|
1508
1959
|
SRGBColorSpace,
|
|
1509
|
-
Vector3 as
|
|
1960
|
+
Vector3 as Vector37
|
|
1510
1961
|
} from "three";
|
|
1511
1962
|
var VIEW_NAMES = [
|
|
1512
1963
|
// The six faces.
|
|
@@ -1590,12 +2041,12 @@ function viewUp(direction) {
|
|
|
1590
2041
|
return { x: 0, y: 0, z: 1 };
|
|
1591
2042
|
}
|
|
1592
2043
|
function squaredUp(direction, currentUp) {
|
|
1593
|
-
const forward = new
|
|
2044
|
+
const forward = new Vector37(direction.x, direction.y, direction.z).normalize();
|
|
1594
2045
|
const canonical = viewUp(direction);
|
|
1595
|
-
const zero = new
|
|
2046
|
+
const zero = new Vector37(canonical.x, canonical.y, canonical.z);
|
|
1596
2047
|
zero.addScaledVector(forward, -forward.dot(zero)).normalize();
|
|
1597
|
-
const quarter = new
|
|
1598
|
-
const current = new
|
|
2048
|
+
const quarter = new Vector37().crossVectors(forward, zero);
|
|
2049
|
+
const current = new Vector37(currentUp.x, currentUp.y, currentUp.z);
|
|
1599
2050
|
const rolls = [zero, quarter, zero.clone().negate(), quarter.clone().negate()];
|
|
1600
2051
|
const nearest = rolls.reduce(
|
|
1601
2052
|
(best, roll) => roll.dot(current) > best.dot(current) ? roll : best
|
|
@@ -1613,11 +2064,11 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
1613
2064
|
const direction = viewVector(name);
|
|
1614
2065
|
const [sx, sy, sz] = VIEW_SIGNS[name];
|
|
1615
2066
|
const sign = (axis) => VIEW_SIGNS[name][axis] ?? 0;
|
|
1616
|
-
const normal = new
|
|
2067
|
+
const normal = new Vector37(direction.x, direction.y, direction.z);
|
|
1617
2068
|
let polygon;
|
|
1618
2069
|
if (kind === "face") {
|
|
1619
2070
|
const up = viewUp(direction);
|
|
1620
|
-
const upVector = new
|
|
2071
|
+
const upVector = new Vector37(up.x, up.y, up.z);
|
|
1621
2072
|
const right = normal.clone().negate().cross(upVector).normalize();
|
|
1622
2073
|
const square = [
|
|
1623
2074
|
[-1, -1],
|
|
@@ -1639,7 +2090,7 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
1639
2090
|
[chamfer, 1, -chamfer]
|
|
1640
2091
|
];
|
|
1641
2092
|
polygon = rectangle.map(
|
|
1642
|
-
([onA, onB, onC]) => new
|
|
2093
|
+
([onA, onB, onC]) => new Vector37().setComponent(a, sign(a) * onA).setComponent(b, sign(b) * onB).setComponent(c, onC)
|
|
1643
2094
|
);
|
|
1644
2095
|
} else {
|
|
1645
2096
|
const triangle = [
|
|
@@ -1647,11 +2098,11 @@ function cubeZones(chamfer = CHAMFER) {
|
|
|
1647
2098
|
[chamfer, 1, chamfer],
|
|
1648
2099
|
[chamfer, chamfer, 1]
|
|
1649
2100
|
];
|
|
1650
|
-
polygon = triangle.map(([onX, onY, onZ]) => new
|
|
2101
|
+
polygon = triangle.map(([onX, onY, onZ]) => new Vector37(sx * onX, sy * onY, sz * onZ));
|
|
1651
2102
|
}
|
|
1652
2103
|
const [first, second, third] = polygon;
|
|
1653
2104
|
if (first && second && third) {
|
|
1654
|
-
const facing = new
|
|
2105
|
+
const facing = new Vector37().subVectors(second, first).cross(new Vector37().subVectors(third, first));
|
|
1655
2106
|
if (facing.dot(normal) < 0) polygon.reverse();
|
|
1656
2107
|
}
|
|
1657
2108
|
return { name, kind, direction, polygon: polygon.map(vec) };
|
|
@@ -1781,10 +2232,10 @@ var ViewerScene = ({
|
|
|
1781
2232
|
const framedRef = useRef3(null);
|
|
1782
2233
|
const scratchBox = useMemo4(() => new Box33(), []);
|
|
1783
2234
|
const scratchBoundary = useMemo4(() => new Box33(), []);
|
|
1784
|
-
const scratchDirection = useMemo4(() => new
|
|
1785
|
-
const scratchView = useMemo4(() => new
|
|
1786
|
-
const scratchTarget = useMemo4(() => new
|
|
1787
|
-
const scratchPosition = useMemo4(() => new
|
|
2235
|
+
const scratchDirection = useMemo4(() => new Vector38(), []);
|
|
2236
|
+
const scratchView = useMemo4(() => new Vector38(), []);
|
|
2237
|
+
const scratchTarget = useMemo4(() => new Vector38(), []);
|
|
2238
|
+
const scratchPosition = useMemo4(() => new Vector38(), []);
|
|
1788
2239
|
const applyLimits = useCallback(
|
|
1789
2240
|
(bounds, framed) => {
|
|
1790
2241
|
controlsRef.current?.applyLimits(
|
|
@@ -1833,8 +2284,8 @@ var ViewerScene = ({
|
|
|
1833
2284
|
(bounds) => {
|
|
1834
2285
|
const controls = controlsRef.current;
|
|
1835
2286
|
if (!controls) return false;
|
|
1836
|
-
const target = controls.getTarget(new
|
|
1837
|
-
const direction = currentViewDirection(camera, target, new
|
|
2287
|
+
const target = controls.getTarget(new Vector38());
|
|
2288
|
+
const direction = currentViewDirection(camera, target, new Vector38());
|
|
1838
2289
|
const distance = fitDistance(projection, size, bounds, DEFAULT_FIT_MARGIN);
|
|
1839
2290
|
const position = direction.multiplyScalar(distance).add(bounds.center);
|
|
1840
2291
|
void controls.setLookAt(
|
|
@@ -1859,8 +2310,8 @@ var ViewerScene = ({
|
|
|
1859
2310
|
const fitContent = useCallback(() => {
|
|
1860
2311
|
const controls = controlsRef.current;
|
|
1861
2312
|
if (!controls) return false;
|
|
1862
|
-
const target = controls.getTarget(new
|
|
1863
|
-
return frame(currentViewDirection(camera, target, new
|
|
2313
|
+
const target = controls.getTarget(new Vector38());
|
|
2314
|
+
return frame(currentViewDirection(camera, target, new Vector38()), true);
|
|
1864
2315
|
}, [camera, frame]);
|
|
1865
2316
|
const resetContent = useCallback(() => {
|
|
1866
2317
|
const bounds = measure();
|
|
@@ -2015,7 +2466,8 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
2015
2466
|
return /* @__PURE__ */ jsx3(ViewerControlsContext.Provider, { value: proxy, children: /* @__PURE__ */ jsx3(SectionStoreContext.Provider, { value: sectionStore, children: /* @__PURE__ */ jsx3(
|
|
2016
2467
|
"div",
|
|
2017
2468
|
{
|
|
2018
|
-
className,
|
|
2469
|
+
className: ["viewer-root", className].filter(Boolean).join(" "),
|
|
2470
|
+
"data-viewer-root": "true",
|
|
2019
2471
|
ref: hold,
|
|
2020
2472
|
onMouseDown: (event) => {
|
|
2021
2473
|
if (event.target instanceof HTMLCanvasElement) event.preventDefault();
|
|
@@ -2024,11 +2476,20 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
2024
2476
|
children: /* @__PURE__ */ jsx3(
|
|
2025
2477
|
Canvas,
|
|
2026
2478
|
{
|
|
2479
|
+
className: "viewer-canvas-container",
|
|
2480
|
+
"data-viewer-canvas-container": "true",
|
|
2027
2481
|
orthographic: projection === "orthographic",
|
|
2028
2482
|
camera: { fov: PERSPECTIVE_FOV, up: [0, 0, 1], position: [1, -1, 1] },
|
|
2029
2483
|
dpr: [1, 2],
|
|
2030
2484
|
frameloop: "demand",
|
|
2031
2485
|
gl: { antialias: true, alpha: true, stencil: true, localClippingEnabled: true },
|
|
2486
|
+
onCreated: ({ gl }) => {
|
|
2487
|
+
const canvas = gl.domElement;
|
|
2488
|
+
canvas.classList.add("viewer-canvas");
|
|
2489
|
+
canvas.dataset.viewerCanvas = "true";
|
|
2490
|
+
canvas.parentElement?.classList.add("viewer-canvas-frame");
|
|
2491
|
+
canvas.parentElement?.setAttribute("data-viewer-canvas-frame", "true");
|
|
2492
|
+
},
|
|
2032
2493
|
onPointerMissed: (event) => {
|
|
2033
2494
|
if (event.button !== 0) return;
|
|
2034
2495
|
if (tracker.current?.isTap(event) ?? true) onPointerMissed?.();
|
|
@@ -2060,29 +2521,31 @@ import { useFrame as useFrame4, useThree as useThree5 } from "@react-three/fiber
|
|
|
2060
2521
|
import { useCallback as useCallback2, useEffect as useEffect5, useLayoutEffect, useMemo as useMemo5, useRef as useRef4, useState } from "react";
|
|
2061
2522
|
import {
|
|
2062
2523
|
Color as Color3,
|
|
2524
|
+
DoubleSide as DoubleSide4,
|
|
2525
|
+
EdgesGeometry,
|
|
2063
2526
|
Mesh as Mesh3,
|
|
2064
2527
|
Plane as Plane2,
|
|
2065
2528
|
PlaneGeometry,
|
|
2066
2529
|
Quaternion as Quaternion2,
|
|
2067
2530
|
Raycaster as Raycaster2,
|
|
2068
2531
|
Scene,
|
|
2069
|
-
Vector2 as
|
|
2070
|
-
Vector3 as
|
|
2532
|
+
Vector2 as Vector24,
|
|
2533
|
+
Vector3 as Vector310
|
|
2071
2534
|
} from "three";
|
|
2072
2535
|
|
|
2073
2536
|
// src/render/directions.ts
|
|
2074
|
-
import { Vector3 as
|
|
2075
|
-
var CONE_AXIS = new
|
|
2537
|
+
import { Vector3 as Vector39 } from "three";
|
|
2538
|
+
var CONE_AXIS = new Vector39(0, 1, 0);
|
|
2076
2539
|
var LENGTH = 0.45;
|
|
2077
2540
|
var HEAD = 0.45;
|
|
2078
2541
|
var HEAD_RADIUS = 0.3;
|
|
2079
2542
|
var SHAFT_RADIUS = 0.09;
|
|
2080
2543
|
var GAP = 0.2;
|
|
2081
2544
|
function arrowPlacement(direction, box) {
|
|
2082
|
-
const center = box.getCenter(new
|
|
2083
|
-
const half = box.getSize(new
|
|
2545
|
+
const center = box.getCenter(new Vector39());
|
|
2546
|
+
const half = box.getSize(new Vector39()).multiplyScalar(0.5);
|
|
2084
2547
|
const radius = half.length() || 1;
|
|
2085
|
-
const axis = new
|
|
2548
|
+
const axis = new Vector39(direction.x, direction.y, direction.z);
|
|
2086
2549
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
2087
2550
|
axis.normalize();
|
|
2088
2551
|
let exit = Number.POSITIVE_INFINITY;
|
|
@@ -2103,7 +2566,7 @@ import {
|
|
|
2103
2566
|
BackSide,
|
|
2104
2567
|
Color as Color2,
|
|
2105
2568
|
DecrementWrapStencilOp,
|
|
2106
|
-
DoubleSide as
|
|
2569
|
+
DoubleSide as DoubleSide3,
|
|
2107
2570
|
FrontSide,
|
|
2108
2571
|
IncrementWrapStencilOp,
|
|
2109
2572
|
LinearFilter,
|
|
@@ -2111,7 +2574,7 @@ import {
|
|
|
2111
2574
|
NotEqualStencilFunc,
|
|
2112
2575
|
ReplaceStencilOp,
|
|
2113
2576
|
ShaderMaterial,
|
|
2114
|
-
Vector2 as
|
|
2577
|
+
Vector2 as Vector23,
|
|
2115
2578
|
WebGLRenderTarget
|
|
2116
2579
|
} from "three";
|
|
2117
2580
|
var VERTEX = (
|
|
@@ -2170,7 +2633,7 @@ function createMaskTarget() {
|
|
|
2170
2633
|
function createCapMaterial(mask) {
|
|
2171
2634
|
const uniforms = {
|
|
2172
2635
|
uMask: { value: mask },
|
|
2173
|
-
uResolution: { value: new
|
|
2636
|
+
uResolution: { value: new Vector23(1, 1) },
|
|
2174
2637
|
uFill: { value: new Color2() },
|
|
2175
2638
|
uHatch: { value: new Color2() },
|
|
2176
2639
|
uOutline: { value: new Color2() },
|
|
@@ -2182,7 +2645,7 @@ function createCapMaterial(mask) {
|
|
|
2182
2645
|
vertexShader: VERTEX,
|
|
2183
2646
|
fragmentShader: FRAGMENT,
|
|
2184
2647
|
uniforms,
|
|
2185
|
-
side:
|
|
2648
|
+
side: DoubleSide3,
|
|
2186
2649
|
stencilWrite: true,
|
|
2187
2650
|
stencilRef: 0,
|
|
2188
2651
|
stencilFunc: NotEqualStencilFunc,
|
|
@@ -2228,7 +2691,7 @@ function createStencilMaterials(clip) {
|
|
|
2228
2691
|
function createMaskMaterial() {
|
|
2229
2692
|
return new MeshBasicMaterial2({
|
|
2230
2693
|
color: 16777215,
|
|
2231
|
-
side:
|
|
2694
|
+
side: DoubleSide3,
|
|
2232
2695
|
stencilWrite: true,
|
|
2233
2696
|
stencilRef: 0,
|
|
2234
2697
|
stencilFunc: NotEqualStencilFunc,
|
|
@@ -2239,12 +2702,12 @@ function createMaskMaterial() {
|
|
|
2239
2702
|
}
|
|
2240
2703
|
|
|
2241
2704
|
// src/section-view.tsx
|
|
2242
|
-
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2705
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2243
2706
|
var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
2244
2707
|
function resolveSectionPlane(options, box) {
|
|
2245
2708
|
if (!options?.enabled || box.isEmpty()) return null;
|
|
2246
2709
|
const normal = options.plane?.normal ?? options.normal ?? DEFAULT_SECTION_NORMAL;
|
|
2247
|
-
const axis = new
|
|
2710
|
+
const axis = new Vector310(normal.x, normal.y, normal.z);
|
|
2248
2711
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
2249
2712
|
axis.normalize();
|
|
2250
2713
|
const bounds = sectionBounds(box, normal);
|
|
@@ -2260,16 +2723,18 @@ function resolveSectionPlane(options, box) {
|
|
|
2260
2723
|
constant,
|
|
2261
2724
|
plane: options.plane ?? null,
|
|
2262
2725
|
depth: anchor === null ? null : sectionDepth(normal, anchor, constant),
|
|
2726
|
+
cutDistance: sectionCutDistance(box, normal, constant),
|
|
2263
2727
|
depthRange: anchor === null ? null : sectionDepthRange(bounds, normal, anchor)
|
|
2264
2728
|
}
|
|
2265
2729
|
};
|
|
2266
2730
|
}
|
|
2267
|
-
var Z = new
|
|
2731
|
+
var Z = new Vector310(0, 0, 1);
|
|
2268
2732
|
var SectionView = ({
|
|
2269
2733
|
geometry,
|
|
2270
2734
|
box,
|
|
2271
2735
|
plane,
|
|
2272
2736
|
theme,
|
|
2737
|
+
handleColor,
|
|
2273
2738
|
showHandle,
|
|
2274
2739
|
onDrag
|
|
2275
2740
|
}) => {
|
|
@@ -2283,7 +2748,10 @@ var SectionView = ({
|
|
|
2283
2748
|
const handleRef = useRef4(null);
|
|
2284
2749
|
const [hovered, setHovered] = useState(false);
|
|
2285
2750
|
const dragging = useRef4(null);
|
|
2286
|
-
const span = useMemo5(() => box.getSize(new
|
|
2751
|
+
const span = useMemo5(() => box.getSize(new Vector310()).length(), [box]);
|
|
2752
|
+
const frameSize = span * SECTION_GIZMO_FRAME_SCALE;
|
|
2753
|
+
const directionColor2 = useMemo5(() => sectionDirectionColor(plane.normal), [plane]);
|
|
2754
|
+
const resolvedHandleColor = sectionHandleColor(handleColor, directionColor2);
|
|
2287
2755
|
const clip = useMemo5(() => [plane], [plane]);
|
|
2288
2756
|
const capPosition = useMemo5(() => sectionAnchor(box, plane), [box, plane]);
|
|
2289
2757
|
const capQuaternion = useMemo5(() => new Quaternion2().setFromUnitVectors(Z, plane.normal), [plane]);
|
|
@@ -2291,6 +2759,10 @@ var SectionView = ({
|
|
|
2291
2759
|
() => new Quaternion2().setFromUnitVectors(CONE_AXIS, plane.normal.clone().negate()),
|
|
2292
2760
|
[plane]
|
|
2293
2761
|
);
|
|
2762
|
+
const frameSurface = useMemo5(() => new PlaneGeometry(frameSize, frameSize), [frameSize]);
|
|
2763
|
+
useEffect5(() => () => frameSurface.dispose(), [frameSurface]);
|
|
2764
|
+
const frameEdges = useMemo5(() => new EdgesGeometry(frameSurface), [frameSurface]);
|
|
2765
|
+
useEffect5(() => () => frameEdges.dispose(), [frameEdges]);
|
|
2294
2766
|
const stencils = useMemo5(() => createStencilMaterials([]), []);
|
|
2295
2767
|
useEffect5(() => () => stencils.forEach((material) => material.dispose()), [stencils]);
|
|
2296
2768
|
useLayoutEffect(() => {
|
|
@@ -2320,7 +2792,7 @@ var SectionView = ({
|
|
|
2320
2792
|
maskScene.maskMesh.quaternion.copy(capQuaternion);
|
|
2321
2793
|
}, [capPosition, capQuaternion, maskScene]);
|
|
2322
2794
|
useLayoutEffect(() => {
|
|
2323
|
-
const buffer = gl.getDrawingBufferSize(new
|
|
2795
|
+
const buffer = gl.getDrawingBufferSize(new Vector24());
|
|
2324
2796
|
mask.setSize(buffer.x, buffer.y);
|
|
2325
2797
|
cap.uniforms.uResolution.value.copy(buffer);
|
|
2326
2798
|
applyCapTheme(cap, theme, dpr);
|
|
@@ -2358,7 +2830,7 @@ var SectionView = ({
|
|
|
2358
2830
|
event.stopPropagation();
|
|
2359
2831
|
const view = camera.position.clone().sub(capPosition).normalize();
|
|
2360
2832
|
const surface = dragPlane(plane.normal.clone(), view, capPosition);
|
|
2361
|
-
const hit = event.ray.intersectPlane(surface, new
|
|
2833
|
+
const hit = event.ray.intersectPlane(surface, new Vector310());
|
|
2362
2834
|
const start = {
|
|
2363
2835
|
plane: surface,
|
|
2364
2836
|
from: hit ? hit.dot(plane.normal) : 0,
|
|
@@ -2367,7 +2839,7 @@ var SectionView = ({
|
|
|
2367
2839
|
dragging.current = start;
|
|
2368
2840
|
setControlsEnabled(false);
|
|
2369
2841
|
const raycaster = new Raycaster2();
|
|
2370
|
-
const pointer = new
|
|
2842
|
+
const pointer = new Vector24();
|
|
2371
2843
|
const move = (native) => {
|
|
2372
2844
|
const rect = domElement.getBoundingClientRect();
|
|
2373
2845
|
pointer.set(
|
|
@@ -2375,7 +2847,7 @@ var SectionView = ({
|
|
|
2375
2847
|
-((native.clientY - rect.top) / rect.height) * 2 + 1
|
|
2376
2848
|
);
|
|
2377
2849
|
raycaster.setFromCamera(pointer, camera);
|
|
2378
|
-
const point = raycaster.ray.intersectPlane(start.plane, new
|
|
2850
|
+
const point = raycaster.ray.intersectPlane(start.plane, new Vector310());
|
|
2379
2851
|
if (!point) return;
|
|
2380
2852
|
onDrag(start.constant - (point.dot(plane.normal) - start.from));
|
|
2381
2853
|
invalidate();
|
|
@@ -2437,37 +2909,81 @@ var SectionView = ({
|
|
|
2437
2909
|
raycast: () => null
|
|
2438
2910
|
}
|
|
2439
2911
|
),
|
|
2440
|
-
showHandle && onDrag ? /* @__PURE__ */ jsxs3(
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
/* @__PURE__ */ jsx4("coneGeometry", { args: [0.16, 0.34, 20] }),
|
|
2450
|
-
/* @__PURE__ */ jsx4(
|
|
2451
|
-
"meshBasicMaterial",
|
|
2452
|
-
{
|
|
2453
|
-
color: hovered ? theme.hover : theme.sectionHandle,
|
|
2454
|
-
depthTest: false
|
|
2455
|
-
}
|
|
2456
|
-
)
|
|
2457
|
-
] }),
|
|
2458
|
-
/* @__PURE__ */ jsxs3("mesh", { position: [0, 0.08, 0], ...grab, children: [
|
|
2459
|
-
/* @__PURE__ */ jsx4("cylinderGeometry", { args: [0.045, 0.045, 0.4, 12] }),
|
|
2460
|
-
/* @__PURE__ */ jsx4(
|
|
2912
|
+
showHandle && onDrag ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2913
|
+
/* @__PURE__ */ jsxs3(
|
|
2914
|
+
"group",
|
|
2915
|
+
{
|
|
2916
|
+
position: capPosition,
|
|
2917
|
+
quaternion: capQuaternion,
|
|
2918
|
+
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
2919
|
+
children: [
|
|
2920
|
+
/* @__PURE__ */ jsx4("mesh", { geometry: frameSurface, raycast: () => null, children: /* @__PURE__ */ jsx4(
|
|
2461
2921
|
"meshBasicMaterial",
|
|
2462
2922
|
{
|
|
2463
|
-
color:
|
|
2464
|
-
|
|
2923
|
+
color: directionColor2,
|
|
2924
|
+
transparent: true,
|
|
2925
|
+
opacity: 0.18,
|
|
2926
|
+
side: DoubleSide4,
|
|
2927
|
+
depthTest: false,
|
|
2928
|
+
depthWrite: false
|
|
2465
2929
|
}
|
|
2466
|
-
)
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2930
|
+
) }),
|
|
2931
|
+
/* @__PURE__ */ jsx4("lineSegments", { geometry: frameEdges, raycast: () => null, children: /* @__PURE__ */ jsx4("lineBasicMaterial", { color: directionColor2, depthTest: false, depthWrite: false }) })
|
|
2932
|
+
]
|
|
2933
|
+
}
|
|
2934
|
+
),
|
|
2935
|
+
/* @__PURE__ */ jsxs3(
|
|
2936
|
+
"group",
|
|
2937
|
+
{
|
|
2938
|
+
ref: handleRef,
|
|
2939
|
+
position: capPosition,
|
|
2940
|
+
quaternion: handleQuaternion,
|
|
2941
|
+
renderOrder: SECTION_RENDER_ORDER.handle,
|
|
2942
|
+
children: [
|
|
2943
|
+
/* @__PURE__ */ jsxs3("mesh", { ...grab, children: [
|
|
2944
|
+
/* @__PURE__ */ jsx4("sphereGeometry", { args: [0.1, 16, 12] }),
|
|
2945
|
+
/* @__PURE__ */ jsx4(
|
|
2946
|
+
"meshBasicMaterial",
|
|
2947
|
+
{
|
|
2948
|
+
color: hovered ? theme.hover : resolvedHandleColor,
|
|
2949
|
+
depthTest: false
|
|
2950
|
+
}
|
|
2951
|
+
)
|
|
2952
|
+
] }),
|
|
2953
|
+
/* @__PURE__ */ jsxs3("mesh", { ...grab, children: [
|
|
2954
|
+
/* @__PURE__ */ jsx4("cylinderGeometry", { args: [0.035, 0.035, 0.9, 12] }),
|
|
2955
|
+
/* @__PURE__ */ jsx4(
|
|
2956
|
+
"meshBasicMaterial",
|
|
2957
|
+
{
|
|
2958
|
+
color: hovered ? theme.hover : resolvedHandleColor,
|
|
2959
|
+
depthTest: false
|
|
2960
|
+
}
|
|
2961
|
+
)
|
|
2962
|
+
] }),
|
|
2963
|
+
/* @__PURE__ */ jsxs3("mesh", { position: [0, 0.61, 0], ...grab, children: [
|
|
2964
|
+
/* @__PURE__ */ jsx4("coneGeometry", { args: [0.15, 0.3, 20] }),
|
|
2965
|
+
/* @__PURE__ */ jsx4(
|
|
2966
|
+
"meshBasicMaterial",
|
|
2967
|
+
{
|
|
2968
|
+
color: hovered ? theme.hover : resolvedHandleColor,
|
|
2969
|
+
depthTest: false
|
|
2970
|
+
}
|
|
2971
|
+
)
|
|
2972
|
+
] }),
|
|
2973
|
+
/* @__PURE__ */ jsxs3("mesh", { position: [0, -0.61, 0], rotation: [Math.PI, 0, 0], ...grab, children: [
|
|
2974
|
+
/* @__PURE__ */ jsx4("coneGeometry", { args: [0.15, 0.3, 20] }),
|
|
2975
|
+
/* @__PURE__ */ jsx4(
|
|
2976
|
+
"meshBasicMaterial",
|
|
2977
|
+
{
|
|
2978
|
+
color: hovered ? theme.hover : resolvedHandleColor,
|
|
2979
|
+
depthTest: false
|
|
2980
|
+
}
|
|
2981
|
+
)
|
|
2982
|
+
] })
|
|
2983
|
+
]
|
|
2984
|
+
}
|
|
2985
|
+
)
|
|
2986
|
+
] }) : null
|
|
2471
2987
|
] })
|
|
2472
2988
|
);
|
|
2473
2989
|
};
|
|
@@ -2483,7 +2999,7 @@ function useContentBox() {
|
|
|
2483
2999
|
useFrame5(() => {
|
|
2484
3000
|
if (measured.current) return;
|
|
2485
3001
|
const next = new Box34();
|
|
2486
|
-
|
|
3002
|
+
partBounds(scene, next);
|
|
2487
3003
|
if (next.isEmpty()) return;
|
|
2488
3004
|
measured.current = true;
|
|
2489
3005
|
setBox(next);
|
|
@@ -2492,16 +3008,18 @@ function useContentBox() {
|
|
|
2492
3008
|
}
|
|
2493
3009
|
|
|
2494
3010
|
// src/part-mesh.tsx
|
|
2495
|
-
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3011
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2496
3012
|
var PartMesh = ({
|
|
2497
3013
|
model,
|
|
2498
3014
|
geometry,
|
|
2499
3015
|
selection = [],
|
|
3016
|
+
focus,
|
|
2500
3017
|
candidates = [],
|
|
2501
3018
|
highlights = [],
|
|
2502
3019
|
regionHighlights = [],
|
|
2503
3020
|
pickedRegions = [],
|
|
2504
3021
|
hoveredFeatureIds = [],
|
|
3022
|
+
hover = true,
|
|
2505
3023
|
activeDirection = null,
|
|
2506
3024
|
section,
|
|
2507
3025
|
onSectionChange,
|
|
@@ -2510,7 +3028,8 @@ var PartMesh = ({
|
|
|
2510
3028
|
onHover,
|
|
2511
3029
|
onPick,
|
|
2512
3030
|
theme,
|
|
2513
|
-
showEdges = true
|
|
3031
|
+
showEdges = true,
|
|
3032
|
+
display = "solid"
|
|
2514
3033
|
}) => {
|
|
2515
3034
|
const { camera, controls, invalidate } = useThree7();
|
|
2516
3035
|
const viewerControls = useViewerControls();
|
|
@@ -2519,6 +3038,8 @@ var PartMesh = ({
|
|
|
2519
3038
|
currentTheme.current = resolved;
|
|
2520
3039
|
const part = useMemo6(() => createPart(model, geometry, currentTheme.current), [geometry, model]);
|
|
2521
3040
|
const hoverRegion = useRef6(null);
|
|
3041
|
+
const onHoverRef = useRef6(onHover);
|
|
3042
|
+
onHoverRef.current = onHover;
|
|
2522
3043
|
const box = useContentBox();
|
|
2523
3044
|
const store = useSectionStore();
|
|
2524
3045
|
const shared = useSyncExternalStore(store.subscribe, store.get, store.get);
|
|
@@ -2568,10 +3089,19 @@ var PartMesh = ({
|
|
|
2568
3089
|
part.setTheme(resolved);
|
|
2569
3090
|
repaint();
|
|
2570
3091
|
}, [part, repaint, resolved]);
|
|
3092
|
+
const focusRef = useRef6(focus);
|
|
3093
|
+
focusRef.current = focus;
|
|
3094
|
+
const selectionRef = useRef6(selection);
|
|
3095
|
+
selectionRef.current = selection;
|
|
3096
|
+
const focusKey = focusStateKey(focus, selection);
|
|
2571
3097
|
useLayoutEffect2(() => {
|
|
2572
|
-
part.
|
|
3098
|
+
part.setFocus(selectionRef.current, focusRef.current);
|
|
2573
3099
|
invalidate();
|
|
2574
|
-
}, [invalidate, part
|
|
3100
|
+
}, [focusKey, invalidate, part]);
|
|
3101
|
+
useLayoutEffect2(() => {
|
|
3102
|
+
part.setDisplay(display, showEdges);
|
|
3103
|
+
invalidate();
|
|
3104
|
+
}, [display, invalidate, part, showEdges]);
|
|
2575
3105
|
useLayoutEffect2(() => {
|
|
2576
3106
|
part.setClippingPlanes(cut ? [cut.plane] : null);
|
|
2577
3107
|
invalidate();
|
|
@@ -2614,6 +3144,7 @@ var PartMesh = ({
|
|
|
2614
3144
|
triangleIndex,
|
|
2615
3145
|
point: [event.point.x, event.point.y, event.point.z],
|
|
2616
3146
|
normal: [normal.x, normal.y, normal.z],
|
|
3147
|
+
pointer: { clientX: source.clientX, clientY: source.clientY },
|
|
2617
3148
|
activeDirection,
|
|
2618
3149
|
doubled,
|
|
2619
3150
|
viewDirection: viewDirection(camera, target),
|
|
@@ -2633,8 +3164,12 @@ var PartMesh = ({
|
|
|
2633
3164
|
repaint();
|
|
2634
3165
|
onHover?.(next);
|
|
2635
3166
|
};
|
|
2636
|
-
|
|
2637
|
-
|
|
3167
|
+
useLayoutEffect2(() => {
|
|
3168
|
+
if (hover || hoverRegion.current === null) return;
|
|
3169
|
+
hoverRegion.current = null;
|
|
3170
|
+
repaint();
|
|
3171
|
+
onHoverRef.current?.(null);
|
|
3172
|
+
}, [hover, repaint]);
|
|
2638
3173
|
useLayoutEffect2(() => {
|
|
2639
3174
|
if (!engaged || hoverRegion.current === null) return;
|
|
2640
3175
|
hoverRegion.current = null;
|
|
@@ -2645,18 +3180,21 @@ var PartMesh = ({
|
|
|
2645
3180
|
(constant) => {
|
|
2646
3181
|
if (!cut) return;
|
|
2647
3182
|
const anchor = options?.plane?.point;
|
|
3183
|
+
const bounds = sectionBounds(box, cut.state.normal);
|
|
3184
|
+
const offset = sectionOffset(bounds, constant);
|
|
2648
3185
|
const state = {
|
|
2649
3186
|
...cut.state,
|
|
2650
3187
|
constant,
|
|
2651
|
-
offset
|
|
2652
|
-
depth: anchor ? sectionDepth(cut.state.normal, anchor, constant) : null
|
|
3188
|
+
offset,
|
|
3189
|
+
depth: anchor ? sectionDepth(cut.state.normal, anchor, constant) : null,
|
|
3190
|
+
cutDistance: sectionCutDistance(box, cut.state.normal, constant)
|
|
2653
3191
|
};
|
|
2654
3192
|
if (!controlled) store.set(sectionOptionsFromState(state));
|
|
2655
3193
|
onSectionChange?.(state);
|
|
2656
3194
|
},
|
|
2657
3195
|
[box, controlled, cut, onSectionChange, options, store]
|
|
2658
3196
|
);
|
|
2659
|
-
return /* @__PURE__ */ jsxs4(
|
|
3197
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2660
3198
|
/* @__PURE__ */ jsx5(
|
|
2661
3199
|
"primitive",
|
|
2662
3200
|
{
|
|
@@ -2665,7 +3203,7 @@ var PartMesh = ({
|
|
|
2665
3203
|
pressedWhileEngaged.current = engaged;
|
|
2666
3204
|
},
|
|
2667
3205
|
onPointerMove: (event) => {
|
|
2668
|
-
if (!engaged) emitHover(pickFor(event));
|
|
3206
|
+
if (!engaged && hover) emitHover(pickFor(event));
|
|
2669
3207
|
},
|
|
2670
3208
|
onPointerOut: () => {
|
|
2671
3209
|
emitHover(null);
|
|
@@ -2701,15 +3239,16 @@ var PartMesh = ({
|
|
|
2701
3239
|
box,
|
|
2702
3240
|
plane: cut.plane,
|
|
2703
3241
|
theme: resolved,
|
|
3242
|
+
handleColor: theme?.sectionHandle,
|
|
2704
3243
|
showHandle: !controlled || onSectionChange !== void 0,
|
|
2705
3244
|
onDrag: !controlled || onSectionChange ? dragSection : void 0
|
|
2706
3245
|
}
|
|
2707
3246
|
) : null
|
|
2708
3247
|
] });
|
|
2709
3248
|
};
|
|
2710
|
-
var ORIGIN = new
|
|
2711
|
-
var UP = new
|
|
2712
|
-
var TARGET = new
|
|
3249
|
+
var ORIGIN = new Vector311();
|
|
3250
|
+
var UP = new Vector311(0, 0, 1);
|
|
3251
|
+
var TARGET = new Vector311();
|
|
2713
3252
|
function readTarget(controls) {
|
|
2714
3253
|
if (controls && typeof controls.getTarget === "function") {
|
|
2715
3254
|
return controls.getTarget(TARGET);
|
|
@@ -3368,6 +3907,7 @@ export {
|
|
|
3368
3907
|
PERSPECTIVE_FOV,
|
|
3369
3908
|
DEFAULT_FIT_MARGIN,
|
|
3370
3909
|
EXCLUDE_FROM_FRAME,
|
|
3910
|
+
STOCK_OBJECT,
|
|
3371
3911
|
defaultBounds,
|
|
3372
3912
|
aspectRatio,
|
|
3373
3913
|
screenLength,
|
|
@@ -3387,8 +3927,35 @@ export {
|
|
|
3387
3927
|
adaptedUp,
|
|
3388
3928
|
cadViewDirections,
|
|
3389
3929
|
currentViewDirection,
|
|
3930
|
+
MEASURE_RENDER_ORDER,
|
|
3931
|
+
AXIS_COLORS,
|
|
3932
|
+
SNAP_PIXELS,
|
|
3933
|
+
SNAP_MARKER_PIXELS,
|
|
3934
|
+
MEASURE_MARKER_PIXELS,
|
|
3935
|
+
MEASURE_LINE_PIXELS,
|
|
3936
|
+
DELTA_DASH_PIXELS,
|
|
3937
|
+
ANGLE_ARC_FRACTION,
|
|
3938
|
+
ANGLE_ARC_SEGMENTS,
|
|
3939
|
+
snapAt,
|
|
3940
|
+
screenPoint,
|
|
3941
|
+
POINTS_PER_MEASUREMENT,
|
|
3942
|
+
measurementFromPoints,
|
|
3943
|
+
nextMeasurementId,
|
|
3944
|
+
distanceBetween,
|
|
3945
|
+
deltaBetween,
|
|
3946
|
+
midpoint,
|
|
3947
|
+
angleAt,
|
|
3948
|
+
lockToAxis,
|
|
3949
|
+
deltaLegs,
|
|
3950
|
+
angleArc,
|
|
3951
|
+
angleArcRadius,
|
|
3952
|
+
formatMillimetres,
|
|
3953
|
+
formatDegrees,
|
|
3954
|
+
measurementLabel,
|
|
3955
|
+
measurementLabelAnchor,
|
|
3390
3956
|
SECTION_RENDER_ORDER,
|
|
3391
3957
|
HANDLE_PIXELS,
|
|
3958
|
+
SECTION_GIZMO_FRAME_SCALE,
|
|
3392
3959
|
CAP_HATCH,
|
|
3393
3960
|
AXES_PLANE_OFFSET,
|
|
3394
3961
|
AXES_PLANE_SCALE,
|
|
@@ -3396,6 +3963,7 @@ export {
|
|
|
3396
3963
|
OUTLINE_SCALE,
|
|
3397
3964
|
DEFAULT_SECTION_NORMAL,
|
|
3398
3965
|
DISABLED_SECTION,
|
|
3966
|
+
sectionMeasurement,
|
|
3399
3967
|
sectionOptionsFromState,
|
|
3400
3968
|
axesPlanes,
|
|
3401
3969
|
hitUnderRay,
|
|
@@ -3434,6 +4002,7 @@ export {
|
|
|
3434
4002
|
viewDirection,
|
|
3435
4003
|
buildPick,
|
|
3436
4004
|
focusForPick,
|
|
4005
|
+
CONTROL_SCHEME_OPTIONS,
|
|
3437
4006
|
ExtendedCameraControls,
|
|
3438
4007
|
CadCameraControls,
|
|
3439
4008
|
retargetPose,
|