@toolpath/viewer 1.1.1 → 1.2.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 +866 -94
- package/dist/{chunk-4BU4QZCL.js → chunk-HWAEUJ4H.js} +636 -277
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +615 -198
- package/dist/index.js +1000 -1
- package/dist/{normalize-OULo7e0F.d.ts → normalize-DHOSNiuO.d.ts} +221 -139
- package/package.json +1 -1
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
// src/engine/engine-part.tsx
|
|
2
|
-
import { useEffect as
|
|
2
|
+
import { useEffect as useEffect7, useMemo as useMemo7 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/part-mesh.tsx
|
|
5
5
|
import { useThree as useThree7 } from "@react-three/fiber";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
useCallback as useCallback3,
|
|
8
|
+
useEffect as useEffect6,
|
|
9
|
+
useLayoutEffect as useLayoutEffect2,
|
|
10
|
+
useMemo as useMemo6,
|
|
11
|
+
useRef as useRef6,
|
|
12
|
+
useSyncExternalStore
|
|
13
|
+
} from "react";
|
|
7
14
|
import { Vector3 as Vector310 } from "three";
|
|
8
15
|
|
|
9
16
|
// src/model/directions.ts
|
|
@@ -120,10 +127,19 @@ var DEFAULT_THEME = {
|
|
|
120
127
|
picked: 16347926,
|
|
121
128
|
edge: 0,
|
|
122
129
|
edgeOpacity: 0.5,
|
|
123
|
-
sectionCap:
|
|
130
|
+
sectionCap: 14672618,
|
|
131
|
+
sectionHatch: 9080998,
|
|
124
132
|
sectionOutline: 7057587,
|
|
125
133
|
sectionHandle: 15922167,
|
|
126
134
|
sectionHandleOutline: 3948625,
|
|
135
|
+
/*
|
|
136
|
+
* A cool blue, and the snap a lighter one. Warm is spoken for — hover,
|
|
137
|
+
* picked and the selection are all on that side of the wheel — and the
|
|
138
|
+
* section tool's teal is the nearest neighbour, which a measurement line
|
|
139
|
+
* crossing a cutting plane has to stay legible against.
|
|
140
|
+
*/
|
|
141
|
+
measure: 5213943,
|
|
142
|
+
measureSnap: 12572671,
|
|
127
143
|
cube: 14080482,
|
|
128
144
|
cubeEdge: 7435917,
|
|
129
145
|
cubeLabel: 3948625
|
|
@@ -132,7 +148,7 @@ function resolveTheme(overrides) {
|
|
|
132
148
|
return { ...DEFAULT_THEME, ...overrides };
|
|
133
149
|
}
|
|
134
150
|
function themesEqual(a, b) {
|
|
135
|
-
return a.background === b.background && a.hemisphereSky === b.hemisphereSky && a.hemisphereGround === b.hemisphereGround && a.hemisphereIntensity === b.hemisphereIntensity && a.ambient === b.ambient && a.ambientIntensity === b.ambientIntensity && a.part === b.part && a.partEmissive === b.partEmissive && a.hover === b.hover && a.highlight === b.highlight && a.picked === b.picked && a.edge === b.edge && a.edgeOpacity === b.edgeOpacity && a.sectionCap === b.sectionCap && a.sectionOutline === b.sectionOutline && a.sectionHandle === b.sectionHandle && a.sectionHandleOutline === b.sectionHandleOutline && a.cube === b.cube && a.cubeEdge === b.cubeEdge && a.cubeLabel === b.cubeLabel;
|
|
151
|
+
return a.background === b.background && a.hemisphereSky === b.hemisphereSky && a.hemisphereGround === b.hemisphereGround && a.hemisphereIntensity === b.hemisphereIntensity && a.ambient === b.ambient && a.ambientIntensity === b.ambientIntensity && a.part === b.part && a.partEmissive === b.partEmissive && a.hover === b.hover && a.highlight === b.highlight && a.picked === b.picked && a.edge === b.edge && a.edgeOpacity === b.edgeOpacity && a.sectionCap === b.sectionCap && a.sectionHatch === b.sectionHatch && a.sectionOutline === b.sectionOutline && a.sectionHandle === b.sectionHandle && a.sectionHandleOutline === b.sectionHandleOutline && a.measure === b.measure && a.measureSnap === b.measureSnap && a.cube === b.cube && a.cubeEdge === b.cubeEdge && a.cubeLabel === b.cubeLabel;
|
|
136
152
|
}
|
|
137
153
|
function directionColor(index) {
|
|
138
154
|
const wrapped = index % DIRECTION_COLORS.length;
|
|
@@ -178,19 +194,266 @@ function applyHighlightLayers(part, layers, theme) {
|
|
|
178
194
|
}
|
|
179
195
|
|
|
180
196
|
// src/render/section.ts
|
|
181
|
-
import { Plane, Vector3 } from "three";
|
|
197
|
+
import { Plane, Vector3 as Vector32 } from "three";
|
|
198
|
+
|
|
199
|
+
// src/render/camera.ts
|
|
200
|
+
import { OrthographicCamera, Vector3 } from "three";
|
|
201
|
+
var PERSPECTIVE_FOV = 30;
|
|
202
|
+
var DEFAULT_FIT_MARGIN = 1.2;
|
|
203
|
+
var EXCLUDE_FROM_FRAME = "viewerExcludeFromFrame";
|
|
204
|
+
function defaultBounds() {
|
|
205
|
+
return { center: new Vector3(0, 0, 0), radius: 1 };
|
|
206
|
+
}
|
|
207
|
+
function aspectRatio(size) {
|
|
208
|
+
return size.width > 0 && size.height > 0 ? size.width / size.height : 1;
|
|
209
|
+
}
|
|
210
|
+
var SCREEN_LENGTH_EPSILON = 1e-9;
|
|
211
|
+
function screenLength(camera, point, viewport, pixels) {
|
|
212
|
+
const height = viewport.height > 0 ? viewport.height : 1;
|
|
213
|
+
const zoom = camera.zoom || 1;
|
|
214
|
+
const visible = camera instanceof OrthographicCamera ? (camera.top - camera.bottom) / zoom : 2 * Math.tan(camera.fov / 2 * Math.PI / 180) * Math.max(camera.position.distanceTo(point), SCREEN_LENGTH_EPSILON) / zoom;
|
|
215
|
+
return visible / height * pixels;
|
|
216
|
+
}
|
|
217
|
+
function boundsFromBox(box) {
|
|
218
|
+
if (box.isEmpty()) return defaultBounds();
|
|
219
|
+
const center = box.getCenter(new Vector3());
|
|
220
|
+
const radius = box.getSize(new Vector3()).length() / 2;
|
|
221
|
+
return { center, radius: radius > 0 ? radius : 1 };
|
|
222
|
+
}
|
|
223
|
+
function contentBounds(root, into) {
|
|
224
|
+
into.makeEmpty();
|
|
225
|
+
root.updateWorldMatrix(true, true);
|
|
226
|
+
root.traverse((object) => {
|
|
227
|
+
if (excludedFromFrame(object, root)) return;
|
|
228
|
+
if ("isMesh" in object || "isLine" in object || "isPoints" in object) {
|
|
229
|
+
into.expandByObject(object);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
return boundsFromBox(into);
|
|
233
|
+
}
|
|
234
|
+
function excludedFromFrame(object, root) {
|
|
235
|
+
let current = object;
|
|
236
|
+
while (current && current !== root) {
|
|
237
|
+
if (current.userData[EXCLUDE_FROM_FRAME]) return true;
|
|
238
|
+
current = current.parent;
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
function perspectiveFitDistance(fovDegrees, aspect, radius) {
|
|
243
|
+
const verticalFov = fovDegrees * Math.PI / 180;
|
|
244
|
+
const horizontalFov = 2 * Math.atan(Math.tan(verticalFov / 2) * aspect);
|
|
245
|
+
return radius / Math.sin(Math.min(verticalFov, horizontalFov) / 2);
|
|
246
|
+
}
|
|
247
|
+
function orthographicHalfHeight(aspect, radius) {
|
|
248
|
+
return aspect >= 1 ? radius : radius / aspect;
|
|
249
|
+
}
|
|
250
|
+
function nearFar(projection, radius) {
|
|
251
|
+
if (projection === "orthographic") return { near: -radius * 100, far: radius * 100 };
|
|
252
|
+
return { near: radius / 100, far: radius * 100 };
|
|
253
|
+
}
|
|
254
|
+
var START_DIRECTION = {
|
|
255
|
+
orthographic: [1.2, -2.5, 3],
|
|
256
|
+
perspective: [2, 1.2, -2.5]
|
|
257
|
+
};
|
|
258
|
+
function fitDistance(projection, size, bounds, margin = DEFAULT_FIT_MARGIN) {
|
|
259
|
+
if (projection === "perspective") {
|
|
260
|
+
return perspectiveFitDistance(PERSPECTIVE_FOV, aspectRatio(size), bounds.radius * margin);
|
|
261
|
+
}
|
|
262
|
+
return bounds.radius * margin * 4;
|
|
263
|
+
}
|
|
264
|
+
function startPosition(projection, size, bounds, margin = DEFAULT_FIT_MARGIN) {
|
|
265
|
+
const [x, y, z] = START_DIRECTION[projection];
|
|
266
|
+
return new Vector3(x, y, z).normalize().multiplyScalar(fitDistance(projection, size, bounds, margin)).add(bounds.center);
|
|
267
|
+
}
|
|
268
|
+
var MIN_FRAME_RATIO = 0.25;
|
|
269
|
+
var MAX_FRAME_RATIO = 10;
|
|
270
|
+
var TARGET_BOUNDARY_RATIO = 4;
|
|
271
|
+
function cameraLimits(projection, size, bounds, margin = DEFAULT_FIT_MARGIN, framed) {
|
|
272
|
+
const fit = fitDistance(projection, size, bounds, margin);
|
|
273
|
+
const framedRadius = framed && framed.radius > 0 ? framed.radius : bounds.radius;
|
|
274
|
+
if (projection === "orthographic") {
|
|
275
|
+
const reach = bounds.radius > 0 ? bounds.radius / framedRadius : 1;
|
|
276
|
+
return {
|
|
277
|
+
minZoom: Math.min(MIN_FRAME_RATIO, MIN_FRAME_RATIO * reach),
|
|
278
|
+
maxZoom: Math.max(MAX_FRAME_RATIO, MAX_FRAME_RATIO * reach),
|
|
279
|
+
// Distance does not change apparent size under an orthographic camera, so
|
|
280
|
+
// these are not the zoom clamps in another form — they only keep the
|
|
281
|
+
// camera clear of the geometry it is looking at, and bounded.
|
|
282
|
+
minDistance: bounds.radius * margin,
|
|
283
|
+
maxDistance: fit / MIN_FRAME_RATIO
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
const framedFit = fitDistance(
|
|
287
|
+
projection,
|
|
288
|
+
size,
|
|
289
|
+
framedRadius === bounds.radius ? bounds : { ...bounds, radius: framedRadius },
|
|
290
|
+
margin
|
|
291
|
+
);
|
|
292
|
+
return {
|
|
293
|
+
// The wheel is `ACTION.DOLLY` under a perspective camera, so nothing here
|
|
294
|
+
// moves `camera.zoom`. They are set to the same pair anyway: a consumer
|
|
295
|
+
// that does zoom should not find the two cameras disagreeing about how far
|
|
296
|
+
// in is too far.
|
|
297
|
+
minZoom: MIN_FRAME_RATIO,
|
|
298
|
+
maxZoom: MAX_FRAME_RATIO,
|
|
299
|
+
minDistance: Math.min(fit, framedFit) / MAX_FRAME_RATIO,
|
|
300
|
+
maxDistance: Math.max(fit, framedFit) / MIN_FRAME_RATIO
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
var scratchBoundarySize = new Vector3();
|
|
304
|
+
function targetBoundary(bounds, into, margin = DEFAULT_FIT_MARGIN) {
|
|
305
|
+
const reach = bounds.radius * margin * TARGET_BOUNDARY_RATIO;
|
|
306
|
+
return into.setFromCenterAndSize(
|
|
307
|
+
bounds.center,
|
|
308
|
+
scratchBoundarySize.set(reach * 2, reach * 2, reach * 2)
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
function applyProjection(camera, size, bounds, margin = DEFAULT_FIT_MARGIN) {
|
|
312
|
+
const aspect = aspectRatio(size);
|
|
313
|
+
const radius = bounds.radius * margin;
|
|
314
|
+
if (camera instanceof OrthographicCamera) {
|
|
315
|
+
const halfHeight = orthographicHalfHeight(aspect, radius);
|
|
316
|
+
const halfWidth = halfHeight * aspect;
|
|
317
|
+
camera.left = -halfWidth;
|
|
318
|
+
camera.right = halfWidth;
|
|
319
|
+
camera.top = halfHeight;
|
|
320
|
+
camera.bottom = -halfHeight;
|
|
321
|
+
const { near, far } = nearFar("orthographic", radius);
|
|
322
|
+
camera.near = near;
|
|
323
|
+
camera.far = far;
|
|
324
|
+
} else {
|
|
325
|
+
camera.fov = PERSPECTIVE_FOV;
|
|
326
|
+
camera.aspect = aspect;
|
|
327
|
+
const { near, far } = nearFar("perspective", radius);
|
|
328
|
+
camera.near = near;
|
|
329
|
+
camera.far = far;
|
|
330
|
+
}
|
|
331
|
+
camera.updateProjectionMatrix();
|
|
332
|
+
}
|
|
333
|
+
var CAD_CAMERA_UP = new Vector3(0, 0, 1);
|
|
334
|
+
var scratchSide = new Vector3();
|
|
335
|
+
function adaptedUp(view, up, into) {
|
|
336
|
+
const side = scratchSide.crossVectors(view, up).normalize();
|
|
337
|
+
return into.crossVectors(side, view).normalize();
|
|
338
|
+
}
|
|
339
|
+
var cadViewDirections = {
|
|
340
|
+
front: new Vector3(0, -1, 0),
|
|
341
|
+
back: new Vector3(0, 1, 0),
|
|
342
|
+
left: new Vector3(-1, 0, 0),
|
|
343
|
+
right: new Vector3(1, 0, 0),
|
|
344
|
+
top: new Vector3(0, 0, 1),
|
|
345
|
+
bottom: new Vector3(0, 0, -1),
|
|
346
|
+
isometric: new Vector3(1, -1, 1).normalize()
|
|
347
|
+
};
|
|
348
|
+
function currentViewDirection(camera, target, into) {
|
|
349
|
+
into.subVectors(camera.position, target);
|
|
350
|
+
if (into.lengthSq() <= Number.EPSILON) return into.copy(cadViewDirections.isometric);
|
|
351
|
+
return into.normalize();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/render/section.ts
|
|
182
355
|
var SECTION_RENDER_ORDER = {
|
|
183
356
|
stencil: 1,
|
|
184
357
|
cap: 2,
|
|
185
|
-
handle: 6
|
|
358
|
+
handle: 6,
|
|
359
|
+
/** The tool's translucent plane and its outline, over everything but the handle. */
|
|
360
|
+
outline: 5
|
|
186
361
|
};
|
|
187
362
|
var START_DEPTH = 5e-3;
|
|
188
363
|
var HANDLE_PIXELS = 78;
|
|
364
|
+
var CAP_HATCH = {
|
|
365
|
+
pitch: 9,
|
|
366
|
+
line: 1.25,
|
|
367
|
+
outline: 1.5
|
|
368
|
+
};
|
|
369
|
+
var AXES_PLANE_OFFSET = 0.55;
|
|
370
|
+
var AXES_PLANE_SCALE = 1.2;
|
|
371
|
+
var PREVIEW_SCALE = 0.125;
|
|
372
|
+
var OUTLINE_SCALE = 1.2;
|
|
189
373
|
var EPSILON2 = 1e-9;
|
|
190
|
-
var ARROW_AXIS = new
|
|
374
|
+
var ARROW_AXIS = new Vector32(0, 1, 0);
|
|
375
|
+
var DEFAULT_SECTION_NORMAL = { x: 0, y: 0, z: 1 };
|
|
376
|
+
var DISABLED_SECTION = {
|
|
377
|
+
enabled: false,
|
|
378
|
+
normal: DEFAULT_SECTION_NORMAL,
|
|
379
|
+
offset: 0,
|
|
380
|
+
constant: 0,
|
|
381
|
+
plane: null,
|
|
382
|
+
depth: null,
|
|
383
|
+
depthRange: null
|
|
384
|
+
};
|
|
385
|
+
function sectionOptionsFromState(state) {
|
|
386
|
+
if (state.plane) {
|
|
387
|
+
return { enabled: state.enabled, plane: state.plane, depth: state.depth ?? 0 };
|
|
388
|
+
}
|
|
389
|
+
return { enabled: state.enabled, normal: state.normal, offset: state.offset };
|
|
390
|
+
}
|
|
391
|
+
function axesPlanes(box, eye) {
|
|
392
|
+
const centre = box.getCenter(new Vector32());
|
|
393
|
+
const extent = box.getSize(new Vector32());
|
|
394
|
+
const largest = Math.max(extent.x, extent.y, extent.z);
|
|
395
|
+
const size = largest * AXES_PLANE_SCALE;
|
|
396
|
+
const offset = largest * AXES_PLANE_OFFSET;
|
|
397
|
+
const side = (delta) => delta < 0 ? -1 : 1;
|
|
398
|
+
const sx = side(eye.x - centre.x);
|
|
399
|
+
const sy = side(eye.y - centre.y);
|
|
400
|
+
const sz = side(eye.z - centre.z);
|
|
401
|
+
return [
|
|
402
|
+
{
|
|
403
|
+
axis: "x",
|
|
404
|
+
sign: sx,
|
|
405
|
+
position: { x: centre.x - sx * offset, y: centre.y, z: centre.z },
|
|
406
|
+
size,
|
|
407
|
+
options: { enabled: true, normal: { x: -sx, y: 0, z: 0 }, offset: 0.5 }
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
axis: "y",
|
|
411
|
+
sign: sy,
|
|
412
|
+
position: { x: centre.x, y: centre.y - sy * offset, z: centre.z },
|
|
413
|
+
size,
|
|
414
|
+
options: { enabled: true, normal: { x: 0, y: -sy, z: 0 }, offset: 0.5 }
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
axis: "z",
|
|
418
|
+
sign: sz,
|
|
419
|
+
position: { x: centre.x, y: centre.y, z: centre.z - sz * offset },
|
|
420
|
+
size,
|
|
421
|
+
options: { enabled: true, normal: { x: 0, y: 0, z: -sz }, offset: 0.5 }
|
|
422
|
+
}
|
|
423
|
+
];
|
|
424
|
+
}
|
|
425
|
+
function hitUnderRay(raycaster, root) {
|
|
426
|
+
for (const hit of raycaster.intersectObjects(root.children, true)) {
|
|
427
|
+
if (!("isMesh" in hit.object) || !hit.face) continue;
|
|
428
|
+
if (!hit.object.visible || excludedFromFrame(hit.object, root)) continue;
|
|
429
|
+
if (clippedAway(hit)) continue;
|
|
430
|
+
return hit;
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
function clippedAway(hit) {
|
|
435
|
+
const material = hit.object.material;
|
|
436
|
+
const materials = Array.isArray(material) ? material : [material];
|
|
437
|
+
for (const entry of materials) {
|
|
438
|
+
const planes = entry?.clippingPlanes;
|
|
439
|
+
if (!planes) continue;
|
|
440
|
+
for (const plane of planes) if (plane.distanceToPoint(hit.point) < 0) return true;
|
|
441
|
+
}
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
function surfaceUnderRay(raycaster, root) {
|
|
445
|
+
const hit = hitUnderRay(raycaster, root);
|
|
446
|
+
if (!hit?.face) return null;
|
|
447
|
+
const normal = hit.face.normal.clone().transformDirection(hit.object.matrixWorld);
|
|
448
|
+
return { point: hit.point, normal };
|
|
449
|
+
}
|
|
450
|
+
function sectionAnchor(box, plane, into = new Vector32()) {
|
|
451
|
+
const centre = box.getCenter(into);
|
|
452
|
+
return centre.addScaledVector(plane.normal, -(plane.constant + plane.normal.dot(centre)));
|
|
453
|
+
}
|
|
191
454
|
var PICKED_SURFACE_LABEL = "Part surface";
|
|
192
455
|
function unit(v) {
|
|
193
|
-
const vector = new
|
|
456
|
+
const vector = new Vector32(v.x, v.y, v.z);
|
|
194
457
|
return vector.lengthSq() === 0 ? vector.set(0, 0, 1) : vector.normalize();
|
|
195
458
|
}
|
|
196
459
|
function clamp01(value) {
|
|
@@ -198,7 +461,7 @@ function clamp01(value) {
|
|
|
198
461
|
}
|
|
199
462
|
function sectionBounds(box, normal) {
|
|
200
463
|
const axis = unit(normal);
|
|
201
|
-
const corner = new
|
|
464
|
+
const corner = new Vector32();
|
|
202
465
|
let low = Number.POSITIVE_INFINITY;
|
|
203
466
|
let high = Number.NEGATIVE_INFINITY;
|
|
204
467
|
for (let i = 0; i < 8; i += 1) {
|
|
@@ -222,7 +485,7 @@ function sectionOffset(bounds, constant) {
|
|
|
222
485
|
return span === 0 ? 0 : clamp01((constant - bounds.max) / span);
|
|
223
486
|
}
|
|
224
487
|
function sectionDepth(normal, anchor, constant) {
|
|
225
|
-
return -unit(normal).dot(new
|
|
488
|
+
return -unit(normal).dot(new Vector32(anchor.x, anchor.y, anchor.z)) - constant;
|
|
226
489
|
}
|
|
227
490
|
function sectionDepthConstant(normal, anchor, depth) {
|
|
228
491
|
return sectionDepth(normal, anchor, depth);
|
|
@@ -241,7 +504,7 @@ function sectionFromPick(surface, label = PICKED_SURFACE_LABEL) {
|
|
|
241
504
|
};
|
|
242
505
|
}
|
|
243
506
|
function pickedStartDepth(box) {
|
|
244
|
-
return box.getSize(new
|
|
507
|
+
return box.getSize(new Vector32()).length() * START_DEPTH;
|
|
245
508
|
}
|
|
246
509
|
function sectionPlane(box, normal, offset, into = new Plane()) {
|
|
247
510
|
const axis = unit(normal);
|
|
@@ -249,12 +512,12 @@ function sectionPlane(box, normal, offset, into = new Plane()) {
|
|
|
249
512
|
}
|
|
250
513
|
function dragPlane(axis, view, point, into = new Plane()) {
|
|
251
514
|
const along = axis.lengthSq() === 0 ? ARROW_AXIS.clone() : axis.clone().normalize();
|
|
252
|
-
const side = new
|
|
515
|
+
const side = new Vector32().crossVectors(view, along);
|
|
253
516
|
if (side.lengthSq() < EPSILON2) side.copy(perpendicular(along));
|
|
254
517
|
return into.setFromNormalAndCoplanarPoint(side.cross(along).normalize(), point);
|
|
255
518
|
}
|
|
256
519
|
function perpendicular(axis) {
|
|
257
|
-
const candidate = Math.abs(axis.x) < 0.9 ? new
|
|
520
|
+
const candidate = Math.abs(axis.x) < 0.9 ? new Vector32(1, 0, 0) : new Vector32(0, 1, 0);
|
|
258
521
|
return candidate.cross(axis).normalize();
|
|
259
522
|
}
|
|
260
523
|
|
|
@@ -338,7 +601,7 @@ import {
|
|
|
338
601
|
MeshLambertMaterial,
|
|
339
602
|
RGBAFormat,
|
|
340
603
|
UnsignedByteType,
|
|
341
|
-
Vector3 as
|
|
604
|
+
Vector3 as Vector33
|
|
342
605
|
} from "three";
|
|
343
606
|
|
|
344
607
|
// src/render/edges.ts
|
|
@@ -495,7 +758,7 @@ function createPart(model, geometry, theme) {
|
|
|
495
758
|
const object = new Group();
|
|
496
759
|
object.add(mesh, edges);
|
|
497
760
|
const scratchColor = new Color();
|
|
498
|
-
const scratchVector = new
|
|
761
|
+
const scratchVector = new Vector33();
|
|
499
762
|
const paintRegion = (region, color, weight) => {
|
|
500
763
|
const column = texels.get(region);
|
|
501
764
|
if (column === void 0) return;
|
|
@@ -728,158 +991,6 @@ import { Box3 as Box33, Vector3 as Vector37 } from "three";
|
|
|
728
991
|
import { useFrame, useThree as useThree2 } from "@react-three/fiber";
|
|
729
992
|
import { useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
730
993
|
|
|
731
|
-
// src/render/camera.ts
|
|
732
|
-
import { OrthographicCamera, Vector3 as Vector33 } from "three";
|
|
733
|
-
var PERSPECTIVE_FOV = 30;
|
|
734
|
-
var DEFAULT_FIT_MARGIN = 1.2;
|
|
735
|
-
var EXCLUDE_FROM_FRAME = "viewerExcludeFromFrame";
|
|
736
|
-
function defaultBounds() {
|
|
737
|
-
return { center: new Vector33(0, 0, 0), radius: 1 };
|
|
738
|
-
}
|
|
739
|
-
function aspectRatio(size) {
|
|
740
|
-
return size.width > 0 && size.height > 0 ? size.width / size.height : 1;
|
|
741
|
-
}
|
|
742
|
-
var SCREEN_LENGTH_EPSILON = 1e-9;
|
|
743
|
-
function screenLength(camera, point, viewport, pixels) {
|
|
744
|
-
const height = viewport.height > 0 ? viewport.height : 1;
|
|
745
|
-
const zoom = camera.zoom || 1;
|
|
746
|
-
const visible = camera instanceof OrthographicCamera ? (camera.top - camera.bottom) / zoom : 2 * Math.tan(camera.fov / 2 * Math.PI / 180) * Math.max(camera.position.distanceTo(point), SCREEN_LENGTH_EPSILON) / zoom;
|
|
747
|
-
return visible / height * pixels;
|
|
748
|
-
}
|
|
749
|
-
function boundsFromBox(box) {
|
|
750
|
-
if (box.isEmpty()) return defaultBounds();
|
|
751
|
-
const center = box.getCenter(new Vector33());
|
|
752
|
-
const radius = box.getSize(new Vector33()).length() / 2;
|
|
753
|
-
return { center, radius: radius > 0 ? radius : 1 };
|
|
754
|
-
}
|
|
755
|
-
function contentBounds(root, into) {
|
|
756
|
-
into.makeEmpty();
|
|
757
|
-
root.updateWorldMatrix(true, true);
|
|
758
|
-
root.traverse((object) => {
|
|
759
|
-
if (object.userData[EXCLUDE_FROM_FRAME]) return;
|
|
760
|
-
let ancestor = object.parent;
|
|
761
|
-
while (ancestor && ancestor !== root) {
|
|
762
|
-
if (ancestor.userData[EXCLUDE_FROM_FRAME]) return;
|
|
763
|
-
ancestor = ancestor.parent;
|
|
764
|
-
}
|
|
765
|
-
if ("isMesh" in object || "isLine" in object || "isPoints" in object) {
|
|
766
|
-
into.expandByObject(object);
|
|
767
|
-
}
|
|
768
|
-
});
|
|
769
|
-
return boundsFromBox(into);
|
|
770
|
-
}
|
|
771
|
-
function perspectiveFitDistance(fovDegrees, aspect, radius) {
|
|
772
|
-
const verticalFov = fovDegrees * Math.PI / 180;
|
|
773
|
-
const horizontalFov = 2 * Math.atan(Math.tan(verticalFov / 2) * aspect);
|
|
774
|
-
return radius / Math.sin(Math.min(verticalFov, horizontalFov) / 2);
|
|
775
|
-
}
|
|
776
|
-
function orthographicHalfHeight(aspect, radius) {
|
|
777
|
-
return aspect >= 1 ? radius : radius / aspect;
|
|
778
|
-
}
|
|
779
|
-
function nearFar(projection, radius) {
|
|
780
|
-
if (projection === "orthographic") return { near: -radius * 100, far: radius * 100 };
|
|
781
|
-
return { near: radius / 100, far: radius * 100 };
|
|
782
|
-
}
|
|
783
|
-
var START_DIRECTION = {
|
|
784
|
-
orthographic: [1.2, -2.5, 3],
|
|
785
|
-
perspective: [2, 1.2, -2.5]
|
|
786
|
-
};
|
|
787
|
-
function fitDistance(projection, size, bounds, margin = DEFAULT_FIT_MARGIN) {
|
|
788
|
-
if (projection === "perspective") {
|
|
789
|
-
return perspectiveFitDistance(PERSPECTIVE_FOV, aspectRatio(size), bounds.radius * margin);
|
|
790
|
-
}
|
|
791
|
-
return bounds.radius * margin * 4;
|
|
792
|
-
}
|
|
793
|
-
function startPosition(projection, size, bounds, margin = DEFAULT_FIT_MARGIN) {
|
|
794
|
-
const [x, y, z] = START_DIRECTION[projection];
|
|
795
|
-
return new Vector33(x, y, z).normalize().multiplyScalar(fitDistance(projection, size, bounds, margin)).add(bounds.center);
|
|
796
|
-
}
|
|
797
|
-
var MIN_FRAME_RATIO = 0.25;
|
|
798
|
-
var MAX_FRAME_RATIO = 10;
|
|
799
|
-
var TARGET_BOUNDARY_RATIO = 4;
|
|
800
|
-
function cameraLimits(projection, size, bounds, margin = DEFAULT_FIT_MARGIN, framed) {
|
|
801
|
-
const fit = fitDistance(projection, size, bounds, margin);
|
|
802
|
-
const framedRadius = framed && framed.radius > 0 ? framed.radius : bounds.radius;
|
|
803
|
-
if (projection === "orthographic") {
|
|
804
|
-
const reach = bounds.radius > 0 ? bounds.radius / framedRadius : 1;
|
|
805
|
-
return {
|
|
806
|
-
minZoom: Math.min(MIN_FRAME_RATIO, MIN_FRAME_RATIO * reach),
|
|
807
|
-
maxZoom: Math.max(MAX_FRAME_RATIO, MAX_FRAME_RATIO * reach),
|
|
808
|
-
// Distance does not change apparent size under an orthographic camera, so
|
|
809
|
-
// these are not the zoom clamps in another form — they only keep the
|
|
810
|
-
// camera clear of the geometry it is looking at, and bounded.
|
|
811
|
-
minDistance: bounds.radius * margin,
|
|
812
|
-
maxDistance: fit / MIN_FRAME_RATIO
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
const framedFit = fitDistance(
|
|
816
|
-
projection,
|
|
817
|
-
size,
|
|
818
|
-
framedRadius === bounds.radius ? bounds : { ...bounds, radius: framedRadius },
|
|
819
|
-
margin
|
|
820
|
-
);
|
|
821
|
-
return {
|
|
822
|
-
// The wheel is `ACTION.DOLLY` under a perspective camera, so nothing here
|
|
823
|
-
// moves `camera.zoom`. They are set to the same pair anyway: a consumer
|
|
824
|
-
// that does zoom should not find the two cameras disagreeing about how far
|
|
825
|
-
// in is too far.
|
|
826
|
-
minZoom: MIN_FRAME_RATIO,
|
|
827
|
-
maxZoom: MAX_FRAME_RATIO,
|
|
828
|
-
minDistance: Math.min(fit, framedFit) / MAX_FRAME_RATIO,
|
|
829
|
-
maxDistance: Math.max(fit, framedFit) / MIN_FRAME_RATIO
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
var scratchBoundarySize = new Vector33();
|
|
833
|
-
function targetBoundary(bounds, into, margin = DEFAULT_FIT_MARGIN) {
|
|
834
|
-
const reach = bounds.radius * margin * TARGET_BOUNDARY_RATIO;
|
|
835
|
-
return into.setFromCenterAndSize(
|
|
836
|
-
bounds.center,
|
|
837
|
-
scratchBoundarySize.set(reach * 2, reach * 2, reach * 2)
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
function applyProjection(camera, size, bounds, margin = DEFAULT_FIT_MARGIN) {
|
|
841
|
-
const aspect = aspectRatio(size);
|
|
842
|
-
const radius = bounds.radius * margin;
|
|
843
|
-
if (camera instanceof OrthographicCamera) {
|
|
844
|
-
const halfHeight = orthographicHalfHeight(aspect, radius);
|
|
845
|
-
const halfWidth = halfHeight * aspect;
|
|
846
|
-
camera.left = -halfWidth;
|
|
847
|
-
camera.right = halfWidth;
|
|
848
|
-
camera.top = halfHeight;
|
|
849
|
-
camera.bottom = -halfHeight;
|
|
850
|
-
const { near, far } = nearFar("orthographic", radius);
|
|
851
|
-
camera.near = near;
|
|
852
|
-
camera.far = far;
|
|
853
|
-
} else {
|
|
854
|
-
camera.fov = PERSPECTIVE_FOV;
|
|
855
|
-
camera.aspect = aspect;
|
|
856
|
-
const { near, far } = nearFar("perspective", radius);
|
|
857
|
-
camera.near = near;
|
|
858
|
-
camera.far = far;
|
|
859
|
-
}
|
|
860
|
-
camera.updateProjectionMatrix();
|
|
861
|
-
}
|
|
862
|
-
var CAD_CAMERA_UP = new Vector33(0, 0, 1);
|
|
863
|
-
var scratchSide = new Vector33();
|
|
864
|
-
function adaptedUp(view, up, into) {
|
|
865
|
-
const side = scratchSide.crossVectors(view, up).normalize();
|
|
866
|
-
return into.crossVectors(side, view).normalize();
|
|
867
|
-
}
|
|
868
|
-
var cadViewDirections = {
|
|
869
|
-
front: new Vector33(0, -1, 0),
|
|
870
|
-
back: new Vector33(0, 1, 0),
|
|
871
|
-
left: new Vector33(-1, 0, 0),
|
|
872
|
-
right: new Vector33(1, 0, 0),
|
|
873
|
-
top: new Vector33(0, 0, 1),
|
|
874
|
-
bottom: new Vector33(0, 0, -1),
|
|
875
|
-
isometric: new Vector33(1, -1, 1).normalize()
|
|
876
|
-
};
|
|
877
|
-
function currentViewDirection(camera, target, into) {
|
|
878
|
-
into.subVectors(camera.position, target);
|
|
879
|
-
if (into.lengthSq() <= Number.EPSILON) return into.copy(cadViewDirections.isometric);
|
|
880
|
-
return into.normalize();
|
|
881
|
-
}
|
|
882
|
-
|
|
883
994
|
// src/render/controls.ts
|
|
884
995
|
import CameraControls from "camera-controls";
|
|
885
996
|
import {
|
|
@@ -1214,6 +1325,36 @@ function retargetPose(cameraPosition, currentTarget, hitPoint) {
|
|
|
1214
1325
|
};
|
|
1215
1326
|
}
|
|
1216
1327
|
|
|
1328
|
+
// src/render/section-store.ts
|
|
1329
|
+
function createSectionStore() {
|
|
1330
|
+
let current = null;
|
|
1331
|
+
let engaged = 0;
|
|
1332
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1333
|
+
const notify = () => {
|
|
1334
|
+
for (const listener of listeners) listener();
|
|
1335
|
+
};
|
|
1336
|
+
return {
|
|
1337
|
+
get: () => current,
|
|
1338
|
+
set: (next) => {
|
|
1339
|
+
if (next === current) return;
|
|
1340
|
+
current = next;
|
|
1341
|
+
notify();
|
|
1342
|
+
},
|
|
1343
|
+
isEngaged: () => engaged > 0,
|
|
1344
|
+
setEngaged: (next) => {
|
|
1345
|
+
const was = engaged > 0;
|
|
1346
|
+
engaged = Math.max(0, engaged + (next ? 1 : -1));
|
|
1347
|
+
if (engaged > 0 !== was) notify();
|
|
1348
|
+
},
|
|
1349
|
+
subscribe: (listener) => {
|
|
1350
|
+
listeners.add(listener);
|
|
1351
|
+
return () => {
|
|
1352
|
+
listeners.delete(listener);
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1217
1358
|
// src/target-marker.tsx
|
|
1218
1359
|
import { useFrame as useFrame2, useThree as useThree3 } from "@react-three/fiber";
|
|
1219
1360
|
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
|
|
@@ -1607,6 +1748,12 @@ var useViewerControls = () => {
|
|
|
1607
1748
|
if (!controls) throw new Error("useViewerControls must be used inside <Viewer>");
|
|
1608
1749
|
return controls;
|
|
1609
1750
|
};
|
|
1751
|
+
var SectionStoreContext = createContext2(null);
|
|
1752
|
+
var useSectionStore = () => {
|
|
1753
|
+
const store = useContext2(SectionStoreContext);
|
|
1754
|
+
if (!store) throw new Error("useSectionStore must be used inside <Viewer>");
|
|
1755
|
+
return store;
|
|
1756
|
+
};
|
|
1610
1757
|
var ViewerRetargetContext = createContext2(null);
|
|
1611
1758
|
var useRetarget = () => useContext2(ViewerRetargetContext);
|
|
1612
1759
|
var ViewerScene = ({
|
|
@@ -1759,6 +1906,9 @@ var ViewerScene = ({
|
|
|
1759
1906
|
}, [domElement, fitContent, isTap, recentreOnDoubleClick]);
|
|
1760
1907
|
useEffect4(() => {
|
|
1761
1908
|
setControls({
|
|
1909
|
+
// Answered by the outer proxy, which owns the store; see `Viewer`.
|
|
1910
|
+
setSection: () => {
|
|
1911
|
+
},
|
|
1762
1912
|
fit: () => {
|
|
1763
1913
|
fitContent();
|
|
1764
1914
|
},
|
|
@@ -1839,15 +1989,19 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
1839
1989
|
}, ref) {
|
|
1840
1990
|
const actionsRef = useRef3(null);
|
|
1841
1991
|
const resolved = useMemo4(() => resolveTheme(theme), [theme]);
|
|
1992
|
+
const sectionStore = useMemo4(() => createSectionStore(), []);
|
|
1842
1993
|
const proxy = useMemo4(
|
|
1843
1994
|
() => ({
|
|
1844
1995
|
fit: () => actionsRef.current?.fit(),
|
|
1845
1996
|
reset: () => actionsRef.current?.reset(),
|
|
1846
1997
|
setView: (view) => actionsRef.current?.setView(view),
|
|
1847
1998
|
setViewDirection: (direction) => actionsRef.current?.setViewDirection(direction),
|
|
1848
|
-
frameBox: (box) => actionsRef.current?.frameBox(box)
|
|
1999
|
+
frameBox: (box) => actionsRef.current?.frameBox(box),
|
|
2000
|
+
// Straight to the store rather than through the scene: the store lives
|
|
2001
|
+
// here, so this works before the scene has mounted and after it is gone.
|
|
2002
|
+
setSection: (options) => sectionStore.set(options)
|
|
1849
2003
|
}),
|
|
1850
|
-
[]
|
|
2004
|
+
[sectionStore]
|
|
1851
2005
|
);
|
|
1852
2006
|
useImperativeHandle(ref, () => proxy, [proxy]);
|
|
1853
2007
|
const setControls = useCallback((next) => {
|
|
@@ -1858,7 +2012,7 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
1858
2012
|
tracker.current?.dispose();
|
|
1859
2013
|
tracker.current = element ? trackTaps(element) : null;
|
|
1860
2014
|
}, []);
|
|
1861
|
-
return /* @__PURE__ */ jsx3(ViewerControlsContext.Provider, { value: proxy, children: /* @__PURE__ */ jsx3(
|
|
2015
|
+
return /* @__PURE__ */ jsx3(ViewerControlsContext.Provider, { value: proxy, children: /* @__PURE__ */ jsx3(SectionStoreContext.Provider, { value: sectionStore, children: /* @__PURE__ */ jsx3(
|
|
1862
2016
|
"div",
|
|
1863
2017
|
{
|
|
1864
2018
|
className,
|
|
@@ -1898,25 +2052,21 @@ var Viewer = forwardRef(function Viewer2({
|
|
|
1898
2052
|
projection
|
|
1899
2053
|
)
|
|
1900
2054
|
}
|
|
1901
|
-
) });
|
|
2055
|
+
) }) });
|
|
1902
2056
|
});
|
|
1903
2057
|
|
|
1904
2058
|
// src/section-view.tsx
|
|
1905
2059
|
import { useFrame as useFrame4, useThree as useThree5 } from "@react-three/fiber";
|
|
1906
|
-
import { useCallback as useCallback2, useMemo as useMemo5, useRef as useRef4, useState } from "react";
|
|
2060
|
+
import { useCallback as useCallback2, useEffect as useEffect5, useLayoutEffect, useMemo as useMemo5, useRef as useRef4, useState } from "react";
|
|
1907
2061
|
import {
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
DecrementWrapStencilOp,
|
|
1911
|
-
DoubleSide as DoubleSide2,
|
|
1912
|
-
FrontSide,
|
|
1913
|
-
IncrementWrapStencilOp,
|
|
1914
|
-
NotEqualStencilFunc,
|
|
2062
|
+
Color as Color3,
|
|
2063
|
+
Mesh as Mesh3,
|
|
1915
2064
|
Plane as Plane2,
|
|
2065
|
+
PlaneGeometry,
|
|
1916
2066
|
Quaternion as Quaternion2,
|
|
1917
2067
|
Raycaster as Raycaster2,
|
|
1918
|
-
|
|
1919
|
-
Vector2 as
|
|
2068
|
+
Scene,
|
|
2069
|
+
Vector2 as Vector23,
|
|
1920
2070
|
Vector3 as Vector39
|
|
1921
2071
|
} from "three";
|
|
1922
2072
|
|
|
@@ -1947,13 +2097,153 @@ function arrowPlacement(direction, box) {
|
|
|
1947
2097
|
return { tip: center.addScaledVector(axis, exit + radius * GAP), length: radius * LENGTH };
|
|
1948
2098
|
}
|
|
1949
2099
|
|
|
2100
|
+
// src/render/section-cap.ts
|
|
2101
|
+
import {
|
|
2102
|
+
AlwaysStencilFunc,
|
|
2103
|
+
BackSide,
|
|
2104
|
+
Color as Color2,
|
|
2105
|
+
DecrementWrapStencilOp,
|
|
2106
|
+
DoubleSide as DoubleSide2,
|
|
2107
|
+
FrontSide,
|
|
2108
|
+
IncrementWrapStencilOp,
|
|
2109
|
+
LinearFilter,
|
|
2110
|
+
MeshBasicMaterial as MeshBasicMaterial2,
|
|
2111
|
+
NotEqualStencilFunc,
|
|
2112
|
+
ReplaceStencilOp,
|
|
2113
|
+
ShaderMaterial,
|
|
2114
|
+
Vector2 as Vector22,
|
|
2115
|
+
WebGLRenderTarget
|
|
2116
|
+
} from "three";
|
|
2117
|
+
var VERTEX = (
|
|
2118
|
+
/* glsl */
|
|
2119
|
+
`
|
|
2120
|
+
void main() {
|
|
2121
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
2122
|
+
}
|
|
2123
|
+
`
|
|
2124
|
+
);
|
|
2125
|
+
var FRAGMENT = (
|
|
2126
|
+
/* glsl */
|
|
2127
|
+
`
|
|
2128
|
+
uniform sampler2D uMask;
|
|
2129
|
+
uniform vec2 uResolution;
|
|
2130
|
+
uniform vec3 uFill;
|
|
2131
|
+
uniform vec3 uHatch;
|
|
2132
|
+
uniform vec3 uOutline;
|
|
2133
|
+
uniform float uPitch;
|
|
2134
|
+
uniform float uLine;
|
|
2135
|
+
uniform float uThickness;
|
|
2136
|
+
|
|
2137
|
+
void main() {
|
|
2138
|
+
vec2 uv = gl_FragCoord.xy / uResolution;
|
|
2139
|
+
vec2 step = vec2(uThickness) / uResolution;
|
|
2140
|
+
|
|
2141
|
+
// The mask is 1 inside the cap and 0 outside; a change across a texel in any
|
|
2142
|
+
// direction is the cap's edge.
|
|
2143
|
+
float here = texture2D(uMask, uv).r;
|
|
2144
|
+
float edge =
|
|
2145
|
+
abs(here - texture2D(uMask, uv + vec2(step.x, 0.0)).r) +
|
|
2146
|
+
abs(here - texture2D(uMask, uv - vec2(step.x, 0.0)).r) +
|
|
2147
|
+
abs(here - texture2D(uMask, uv + vec2(0.0, step.y)).r) +
|
|
2148
|
+
abs(here - texture2D(uMask, uv - vec2(0.0, step.y)).r);
|
|
2149
|
+
edge = clamp(edge, 0.0, 1.0);
|
|
2150
|
+
|
|
2151
|
+
// Diagonal stripes at a fixed pitch on screen, anti-aliased over one pixel.
|
|
2152
|
+
float along = mod(gl_FragCoord.x + gl_FragCoord.y, uPitch);
|
|
2153
|
+
float stripe = 1.0 - smoothstep(uLine, uLine + 1.0, along);
|
|
2154
|
+
|
|
2155
|
+
vec3 color = mix(uFill, uHatch, stripe);
|
|
2156
|
+
color = mix(color, uOutline, edge);
|
|
2157
|
+
gl_FragColor = vec4(color, 1.0);
|
|
2158
|
+
#include <colorspace_fragment>
|
|
2159
|
+
}
|
|
2160
|
+
`
|
|
2161
|
+
);
|
|
2162
|
+
function createMaskTarget() {
|
|
2163
|
+
return new WebGLRenderTarget(1, 1, {
|
|
2164
|
+
minFilter: LinearFilter,
|
|
2165
|
+
magFilter: LinearFilter,
|
|
2166
|
+
depthBuffer: true,
|
|
2167
|
+
stencilBuffer: true
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
function createCapMaterial(mask) {
|
|
2171
|
+
const uniforms = {
|
|
2172
|
+
uMask: { value: mask },
|
|
2173
|
+
uResolution: { value: new Vector22(1, 1) },
|
|
2174
|
+
uFill: { value: new Color2() },
|
|
2175
|
+
uHatch: { value: new Color2() },
|
|
2176
|
+
uOutline: { value: new Color2() },
|
|
2177
|
+
uPitch: { value: CAP_HATCH.pitch },
|
|
2178
|
+
uLine: { value: CAP_HATCH.line },
|
|
2179
|
+
uThickness: { value: CAP_HATCH.outline }
|
|
2180
|
+
};
|
|
2181
|
+
return new ShaderMaterial({
|
|
2182
|
+
vertexShader: VERTEX,
|
|
2183
|
+
fragmentShader: FRAGMENT,
|
|
2184
|
+
uniforms,
|
|
2185
|
+
side: DoubleSide2,
|
|
2186
|
+
stencilWrite: true,
|
|
2187
|
+
stencilRef: 0,
|
|
2188
|
+
stencilFunc: NotEqualStencilFunc,
|
|
2189
|
+
stencilFail: ReplaceStencilOp,
|
|
2190
|
+
stencilZFail: ReplaceStencilOp,
|
|
2191
|
+
stencilZPass: ReplaceStencilOp
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
function applyCapTheme(material, theme, dpr) {
|
|
2195
|
+
material.uniforms.uFill.value.set(theme.sectionCap);
|
|
2196
|
+
material.uniforms.uHatch.value.set(theme.sectionHatch);
|
|
2197
|
+
material.uniforms.uOutline.value.set(theme.sectionHandleOutline);
|
|
2198
|
+
material.uniforms.uPitch.value = CAP_HATCH.pitch * dpr;
|
|
2199
|
+
material.uniforms.uLine.value = CAP_HATCH.line * dpr;
|
|
2200
|
+
material.uniforms.uThickness.value = CAP_HATCH.outline * dpr;
|
|
2201
|
+
}
|
|
2202
|
+
function createStencilMaterials(clip) {
|
|
2203
|
+
const shared = {
|
|
2204
|
+
depthWrite: false,
|
|
2205
|
+
depthTest: false,
|
|
2206
|
+
colorWrite: false,
|
|
2207
|
+
stencilWrite: true,
|
|
2208
|
+
stencilFunc: AlwaysStencilFunc,
|
|
2209
|
+
clippingPlanes: clip
|
|
2210
|
+
};
|
|
2211
|
+
return [
|
|
2212
|
+
new MeshBasicMaterial2({
|
|
2213
|
+
...shared,
|
|
2214
|
+
side: BackSide,
|
|
2215
|
+
stencilFail: IncrementWrapStencilOp,
|
|
2216
|
+
stencilZFail: IncrementWrapStencilOp,
|
|
2217
|
+
stencilZPass: IncrementWrapStencilOp
|
|
2218
|
+
}),
|
|
2219
|
+
new MeshBasicMaterial2({
|
|
2220
|
+
...shared,
|
|
2221
|
+
side: FrontSide,
|
|
2222
|
+
stencilFail: DecrementWrapStencilOp,
|
|
2223
|
+
stencilZFail: DecrementWrapStencilOp,
|
|
2224
|
+
stencilZPass: DecrementWrapStencilOp
|
|
2225
|
+
})
|
|
2226
|
+
];
|
|
2227
|
+
}
|
|
2228
|
+
function createMaskMaterial() {
|
|
2229
|
+
return new MeshBasicMaterial2({
|
|
2230
|
+
color: 16777215,
|
|
2231
|
+
side: DoubleSide2,
|
|
2232
|
+
stencilWrite: true,
|
|
2233
|
+
stencilRef: 0,
|
|
2234
|
+
stencilFunc: NotEqualStencilFunc,
|
|
2235
|
+
stencilFail: ReplaceStencilOp,
|
|
2236
|
+
stencilZFail: ReplaceStencilOp,
|
|
2237
|
+
stencilZPass: ReplaceStencilOp
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
|
|
1950
2241
|
// src/section-view.tsx
|
|
1951
2242
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1952
|
-
var DEFAULT_NORMAL = { x: 0, y: 0, z: 1 };
|
|
1953
2243
|
var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1954
2244
|
function resolveSectionPlane(options, box) {
|
|
1955
2245
|
if (!options?.enabled || box.isEmpty()) return null;
|
|
1956
|
-
const normal = options.plane?.normal ?? options.normal ??
|
|
2246
|
+
const normal = options.plane?.normal ?? options.normal ?? DEFAULT_SECTION_NORMAL;
|
|
1957
2247
|
const axis = new Vector39(normal.x, normal.y, normal.z);
|
|
1958
2248
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
1959
2249
|
axis.normalize();
|
|
@@ -1974,6 +2264,7 @@ function resolveSectionPlane(options, box) {
|
|
|
1974
2264
|
}
|
|
1975
2265
|
};
|
|
1976
2266
|
}
|
|
2267
|
+
var Z = new Vector39(0, 0, 1);
|
|
1977
2268
|
var SectionView = ({
|
|
1978
2269
|
geometry,
|
|
1979
2270
|
box,
|
|
@@ -1984,28 +2275,68 @@ var SectionView = ({
|
|
|
1984
2275
|
}) => {
|
|
1985
2276
|
const camera = useThree5((state) => state.camera);
|
|
1986
2277
|
const size = useThree5((state) => state.size);
|
|
2278
|
+
const dpr = useThree5((state) => state.viewport.dpr);
|
|
2279
|
+
const gl = useThree5((state) => state.gl);
|
|
1987
2280
|
const invalidate = useThree5((state) => state.invalidate);
|
|
1988
2281
|
const controls = useThree5((state) => state.controls);
|
|
1989
2282
|
const domElement = useThree5((state) => state.gl.domElement);
|
|
1990
|
-
const capRef = useRef4(null);
|
|
1991
2283
|
const handleRef = useRef4(null);
|
|
1992
2284
|
const [hovered, setHovered] = useState(false);
|
|
1993
2285
|
const dragging = useRef4(null);
|
|
1994
|
-
const centre = useMemo5(() => box.getCenter(new Vector39()), [box]);
|
|
1995
2286
|
const span = useMemo5(() => box.getSize(new Vector39()).length(), [box]);
|
|
1996
2287
|
const clip = useMemo5(() => [plane], [plane]);
|
|
1997
|
-
const capPosition = useMemo5(() =>
|
|
1998
|
-
|
|
1999
|
-
return point.addScaledVector(plane.normal, -(plane.constant + plane.normal.dot(centre)));
|
|
2000
|
-
}, [centre, plane]);
|
|
2001
|
-
const capQuaternion = useMemo5(
|
|
2002
|
-
() => new Quaternion2().setFromUnitVectors(new Vector39(0, 0, 1), plane.normal),
|
|
2003
|
-
[plane]
|
|
2004
|
-
);
|
|
2288
|
+
const capPosition = useMemo5(() => sectionAnchor(box, plane), [box, plane]);
|
|
2289
|
+
const capQuaternion = useMemo5(() => new Quaternion2().setFromUnitVectors(Z, plane.normal), [plane]);
|
|
2005
2290
|
const handleQuaternion = useMemo5(
|
|
2006
2291
|
() => new Quaternion2().setFromUnitVectors(CONE_AXIS, plane.normal.clone().negate()),
|
|
2007
2292
|
[plane]
|
|
2008
2293
|
);
|
|
2294
|
+
const stencils = useMemo5(() => createStencilMaterials([]), []);
|
|
2295
|
+
useEffect5(() => () => stencils.forEach((material) => material.dispose()), [stencils]);
|
|
2296
|
+
useLayoutEffect(() => {
|
|
2297
|
+
for (const material of stencils) material.clippingPlanes = clip;
|
|
2298
|
+
}, [clip, stencils]);
|
|
2299
|
+
const mask = useMemo5(() => createMaskTarget(), []);
|
|
2300
|
+
useEffect5(() => () => mask.dispose(), [mask]);
|
|
2301
|
+
const cap = useMemo5(() => createCapMaterial(mask.texture), [mask]);
|
|
2302
|
+
useEffect5(() => () => cap.dispose(), [cap]);
|
|
2303
|
+
const quad = useMemo5(() => new PlaneGeometry(span * 1.5, span * 1.5), [span]);
|
|
2304
|
+
useEffect5(() => () => quad.dispose(), [quad]);
|
|
2305
|
+
const maskScene = useMemo5(() => {
|
|
2306
|
+
const scene = new Scene();
|
|
2307
|
+
const [back, front] = stencils;
|
|
2308
|
+
const backMesh = new Mesh3(geometry, back);
|
|
2309
|
+
backMesh.renderOrder = SECTION_RENDER_ORDER.stencil;
|
|
2310
|
+
const frontMesh = new Mesh3(geometry, front);
|
|
2311
|
+
frontMesh.renderOrder = SECTION_RENDER_ORDER.stencil;
|
|
2312
|
+
const maskMesh = new Mesh3(quad, createMaskMaterial());
|
|
2313
|
+
maskMesh.renderOrder = SECTION_RENDER_ORDER.cap;
|
|
2314
|
+
scene.add(backMesh, frontMesh, maskMesh);
|
|
2315
|
+
return { scene, maskMesh };
|
|
2316
|
+
}, [geometry, quad, stencils]);
|
|
2317
|
+
useEffect5(() => () => maskScene.maskMesh.material.dispose(), [maskScene]);
|
|
2318
|
+
useEffect5(() => {
|
|
2319
|
+
maskScene.maskMesh.position.copy(capPosition);
|
|
2320
|
+
maskScene.maskMesh.quaternion.copy(capQuaternion);
|
|
2321
|
+
}, [capPosition, capQuaternion, maskScene]);
|
|
2322
|
+
useLayoutEffect(() => {
|
|
2323
|
+
const buffer = gl.getDrawingBufferSize(new Vector23());
|
|
2324
|
+
mask.setSize(buffer.x, buffer.y);
|
|
2325
|
+
cap.uniforms.uResolution.value.copy(buffer);
|
|
2326
|
+
applyCapTheme(cap, theme, dpr);
|
|
2327
|
+
invalidate();
|
|
2328
|
+
}, [cap, dpr, gl, invalidate, mask, size, theme]);
|
|
2329
|
+
const clearColor = useMemo5(() => new Color3(), []);
|
|
2330
|
+
useFrame4(({ gl: renderer, camera: eye }) => {
|
|
2331
|
+
const previousAlpha = renderer.getClearAlpha();
|
|
2332
|
+
renderer.getClearColor(clearColor);
|
|
2333
|
+
renderer.setRenderTarget(mask);
|
|
2334
|
+
renderer.setClearColor(0, 1);
|
|
2335
|
+
renderer.clear();
|
|
2336
|
+
renderer.render(maskScene.scene, eye);
|
|
2337
|
+
renderer.setRenderTarget(null);
|
|
2338
|
+
renderer.setClearColor(clearColor, previousAlpha);
|
|
2339
|
+
}, -1);
|
|
2009
2340
|
useFrame4(() => {
|
|
2010
2341
|
const handle = handleRef.current;
|
|
2011
2342
|
if (!handle) return;
|
|
@@ -2036,7 +2367,7 @@ var SectionView = ({
|
|
|
2036
2367
|
dragging.current = start;
|
|
2037
2368
|
setControlsEnabled(false);
|
|
2038
2369
|
const raycaster = new Raycaster2();
|
|
2039
|
-
const pointer = new
|
|
2370
|
+
const pointer = new Vector23();
|
|
2040
2371
|
const move = (native) => {
|
|
2041
2372
|
const rect = domElement.getBoundingClientRect();
|
|
2042
2373
|
pointer.set(
|
|
@@ -2077,52 +2408,35 @@ var SectionView = ({
|
|
|
2077
2408
|
// the part's diagonal, so a Fit that measured it would frame the cut rather
|
|
2078
2409
|
// than the part and leave the part a speck in the middle of it.
|
|
2079
2410
|
/* @__PURE__ */ jsxs3("group", { userData: FURNITURE2, children: [
|
|
2080
|
-
/* @__PURE__ */ jsx4(
|
|
2081
|
-
"
|
|
2411
|
+
/* @__PURE__ */ jsx4(
|
|
2412
|
+
"mesh",
|
|
2082
2413
|
{
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
stencilWrite: true,
|
|
2088
|
-
stencilFunc: AlwaysStencilFunc,
|
|
2089
|
-
stencilFail: IncrementWrapStencilOp,
|
|
2090
|
-
stencilZFail: IncrementWrapStencilOp,
|
|
2091
|
-
stencilZPass: IncrementWrapStencilOp,
|
|
2092
|
-
clippingPlanes: clip
|
|
2414
|
+
geometry,
|
|
2415
|
+
material: stencils[0],
|
|
2416
|
+
renderOrder: SECTION_RENDER_ORDER.stencil,
|
|
2417
|
+
raycast: () => null
|
|
2093
2418
|
}
|
|
2094
|
-
)
|
|
2095
|
-
/* @__PURE__ */ jsx4(
|
|
2096
|
-
"
|
|
2419
|
+
),
|
|
2420
|
+
/* @__PURE__ */ jsx4(
|
|
2421
|
+
"mesh",
|
|
2097
2422
|
{
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
stencilWrite: true,
|
|
2103
|
-
stencilFunc: AlwaysStencilFunc,
|
|
2104
|
-
stencilFail: DecrementWrapStencilOp,
|
|
2105
|
-
stencilZFail: DecrementWrapStencilOp,
|
|
2106
|
-
stencilZPass: DecrementWrapStencilOp,
|
|
2107
|
-
clippingPlanes: clip
|
|
2423
|
+
geometry,
|
|
2424
|
+
material: stencils[1],
|
|
2425
|
+
renderOrder: SECTION_RENDER_ORDER.stencil,
|
|
2426
|
+
raycast: () => null
|
|
2108
2427
|
}
|
|
2109
|
-
)
|
|
2110
|
-
/* @__PURE__ */ jsx4(
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
stencilZFail: ReplaceStencilOp,
|
|
2122
|
-
stencilZPass: ReplaceStencilOp
|
|
2123
|
-
}
|
|
2124
|
-
)
|
|
2125
|
-
] }) }),
|
|
2428
|
+
),
|
|
2429
|
+
/* @__PURE__ */ jsx4(
|
|
2430
|
+
"mesh",
|
|
2431
|
+
{
|
|
2432
|
+
geometry: quad,
|
|
2433
|
+
material: cap,
|
|
2434
|
+
position: capPosition,
|
|
2435
|
+
quaternion: capQuaternion,
|
|
2436
|
+
renderOrder: SECTION_RENDER_ORDER.cap,
|
|
2437
|
+
raycast: () => null
|
|
2438
|
+
}
|
|
2439
|
+
),
|
|
2126
2440
|
showHandle && onDrag ? /* @__PURE__ */ jsxs3(
|
|
2127
2441
|
"group",
|
|
2128
2442
|
{
|
|
@@ -2206,7 +2520,13 @@ var PartMesh = ({
|
|
|
2206
2520
|
const part = useMemo6(() => createPart(model, geometry, currentTheme.current), [geometry, model]);
|
|
2207
2521
|
const hoverRegion = useRef6(null);
|
|
2208
2522
|
const box = useContentBox();
|
|
2209
|
-
const
|
|
2523
|
+
const store = useSectionStore();
|
|
2524
|
+
const shared = useSyncExternalStore(store.subscribe, store.get, store.get);
|
|
2525
|
+
const controlled = section !== void 0;
|
|
2526
|
+
const options = controlled ? section : shared ?? void 0;
|
|
2527
|
+
const cut = useMemo6(() => resolveSectionPlane(options, box), [box, options]);
|
|
2528
|
+
const engaged = useSyncExternalStore(store.subscribe, store.isEngaged, store.isEngaged);
|
|
2529
|
+
const pressedWhileEngaged = useRef6(false);
|
|
2210
2530
|
const layers = useRef6({
|
|
2211
2531
|
selection,
|
|
2212
2532
|
candidates,
|
|
@@ -2232,37 +2552,39 @@ var PartMesh = ({
|
|
|
2232
2552
|
);
|
|
2233
2553
|
invalidate();
|
|
2234
2554
|
}, [invalidate, part]);
|
|
2235
|
-
|
|
2555
|
+
useEffect6(() => () => part.dispose(), [part]);
|
|
2236
2556
|
const adjacency = useMemo6(() => regionAdjacency(model, geometry), [model, geometry]);
|
|
2237
|
-
|
|
2557
|
+
useEffect6(() => {
|
|
2238
2558
|
onAdjacency?.(adjacency);
|
|
2239
2559
|
}, [adjacency, onAdjacency]);
|
|
2240
2560
|
const framed = useRef6(null);
|
|
2241
|
-
|
|
2561
|
+
useEffect6(() => {
|
|
2242
2562
|
if (focusFeature === null || focusFeature === framed.current) return;
|
|
2243
2563
|
framed.current = focusFeature;
|
|
2244
2564
|
const box2 = part.boxForFeature(focusFeature);
|
|
2245
2565
|
if (box2) viewerControls.frameBox(box2);
|
|
2246
2566
|
}, [focusFeature, part, viewerControls]);
|
|
2247
|
-
|
|
2567
|
+
useLayoutEffect2(() => {
|
|
2248
2568
|
part.setTheme(resolved);
|
|
2249
2569
|
repaint();
|
|
2250
2570
|
}, [part, repaint, resolved]);
|
|
2251
|
-
|
|
2571
|
+
useLayoutEffect2(() => {
|
|
2252
2572
|
part.edges.visible = showEdges;
|
|
2253
2573
|
invalidate();
|
|
2254
2574
|
}, [invalidate, part, showEdges]);
|
|
2255
|
-
|
|
2575
|
+
useLayoutEffect2(() => {
|
|
2256
2576
|
part.setClippingPlanes(cut ? [cut.plane] : null);
|
|
2257
2577
|
invalidate();
|
|
2258
2578
|
}, [cut, invalidate, part]);
|
|
2259
2579
|
const reportedSection = useRef6("");
|
|
2260
|
-
|
|
2580
|
+
useLayoutEffect2(() => {
|
|
2261
2581
|
const state = cut?.state ?? null;
|
|
2262
2582
|
const key = state ? `${state.constant}|${state.normal.x},${state.normal.y},${state.normal.z}` : "";
|
|
2263
2583
|
if (key === reportedSection.current) return;
|
|
2584
|
+
const was = reportedSection.current;
|
|
2264
2585
|
reportedSection.current = key;
|
|
2265
2586
|
if (state) onSectionChange?.(state);
|
|
2587
|
+
else if (was !== "") onSectionChange?.(DISABLED_SECTION);
|
|
2266
2588
|
}, [cut, onSectionChange]);
|
|
2267
2589
|
const layerKey = [
|
|
2268
2590
|
selection.join(" "),
|
|
@@ -2272,7 +2594,7 @@ var PartMesh = ({
|
|
|
2272
2594
|
highlights.map((entry) => `${entry.tag}:${entry.color}:${entry.weight ?? ""}`).join(" "),
|
|
2273
2595
|
regionHighlights.map((entry) => `${entry.region}:${entry.color}:${entry.weight ?? ""}`).join(" ")
|
|
2274
2596
|
].join("|");
|
|
2275
|
-
|
|
2597
|
+
useLayoutEffect2(() => {
|
|
2276
2598
|
repaint();
|
|
2277
2599
|
}, [layerKey, repaint]);
|
|
2278
2600
|
const isTap = useTapGuard();
|
|
@@ -2311,30 +2633,46 @@ var PartMesh = ({
|
|
|
2311
2633
|
repaint();
|
|
2312
2634
|
onHover?.(next);
|
|
2313
2635
|
};
|
|
2636
|
+
const onHoverRef = useRef6(onHover);
|
|
2637
|
+
onHoverRef.current = onHover;
|
|
2638
|
+
useLayoutEffect2(() => {
|
|
2639
|
+
if (!engaged || hoverRegion.current === null) return;
|
|
2640
|
+
hoverRegion.current = null;
|
|
2641
|
+
repaint();
|
|
2642
|
+
onHoverRef.current?.(null);
|
|
2643
|
+
}, [engaged, repaint]);
|
|
2314
2644
|
const dragSection = useCallback3(
|
|
2315
2645
|
(constant) => {
|
|
2316
2646
|
if (!cut) return;
|
|
2317
|
-
const anchor =
|
|
2318
|
-
|
|
2647
|
+
const anchor = options?.plane?.point;
|
|
2648
|
+
const state = {
|
|
2319
2649
|
...cut.state,
|
|
2320
2650
|
constant,
|
|
2321
2651
|
offset: sectionOffset(sectionBounds(box, cut.state.normal), constant),
|
|
2322
2652
|
depth: anchor ? sectionDepth(cut.state.normal, anchor, constant) : null
|
|
2323
|
-
}
|
|
2653
|
+
};
|
|
2654
|
+
if (!controlled) store.set(sectionOptionsFromState(state));
|
|
2655
|
+
onSectionChange?.(state);
|
|
2324
2656
|
},
|
|
2325
|
-
[box, cut, onSectionChange,
|
|
2657
|
+
[box, controlled, cut, onSectionChange, options, store]
|
|
2326
2658
|
);
|
|
2327
2659
|
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
2328
2660
|
/* @__PURE__ */ jsx5(
|
|
2329
2661
|
"primitive",
|
|
2330
2662
|
{
|
|
2331
2663
|
object: part.object,
|
|
2332
|
-
|
|
2664
|
+
onPointerDown: () => {
|
|
2665
|
+
pressedWhileEngaged.current = engaged;
|
|
2666
|
+
},
|
|
2667
|
+
onPointerMove: (event) => {
|
|
2668
|
+
if (!engaged) emitHover(pickFor(event));
|
|
2669
|
+
},
|
|
2333
2670
|
onPointerOut: () => {
|
|
2334
2671
|
emitHover(null);
|
|
2335
2672
|
doubles.reset();
|
|
2336
2673
|
},
|
|
2337
2674
|
onClick: (event) => {
|
|
2675
|
+
if (engaged || pressedWhileEngaged.current) return;
|
|
2338
2676
|
if (!isTap(event.nativeEvent)) {
|
|
2339
2677
|
doubles.reset();
|
|
2340
2678
|
return;
|
|
@@ -2345,6 +2683,7 @@ var PartMesh = ({
|
|
|
2345
2683
|
if (pick) onPick?.(pick);
|
|
2346
2684
|
},
|
|
2347
2685
|
onPointerUp: (event) => {
|
|
2686
|
+
if (engaged || pressedWhileEngaged.current) return;
|
|
2348
2687
|
if (event.nativeEvent.button !== 2) return;
|
|
2349
2688
|
if (!isTap(event.nativeEvent)) return;
|
|
2350
2689
|
const pick = pickFor(event);
|
|
@@ -2362,8 +2701,8 @@ var PartMesh = ({
|
|
|
2362
2701
|
box,
|
|
2363
2702
|
plane: cut.plane,
|
|
2364
2703
|
theme: resolved,
|
|
2365
|
-
showHandle: onSectionChange !== void 0,
|
|
2366
|
-
onDrag: onSectionChange ? dragSection : void 0
|
|
2704
|
+
showHandle: !controlled || onSectionChange !== void 0,
|
|
2705
|
+
onDrag: !controlled || onSectionChange ? dragSection : void 0
|
|
2367
2706
|
}
|
|
2368
2707
|
) : null
|
|
2369
2708
|
] });
|
|
@@ -2386,7 +2725,7 @@ function useStableTheme(theme) {
|
|
|
2386
2725
|
}
|
|
2387
2726
|
|
|
2388
2727
|
// src/engine/geometry.ts
|
|
2389
|
-
import { Matrix4 as Matrix42, Mesh as
|
|
2728
|
+
import { Matrix4 as Matrix42, Mesh as Mesh4 } from "three";
|
|
2390
2729
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
2391
2730
|
import { STLLoader } from "three/examples/jsm/loaders/STLLoader.js";
|
|
2392
2731
|
var PartMeshError = class extends Error {
|
|
@@ -2461,7 +2800,7 @@ function parseStl(data, mesh) {
|
|
|
2461
2800
|
function singleMesh(scene) {
|
|
2462
2801
|
const meshes = [];
|
|
2463
2802
|
scene.traverse((object) => {
|
|
2464
|
-
if (object instanceof
|
|
2803
|
+
if (object instanceof Mesh4) meshes.push(object);
|
|
2465
2804
|
});
|
|
2466
2805
|
const [mesh] = meshes;
|
|
2467
2806
|
if (mesh === void 0 || meshes.length !== 1) {
|
|
@@ -2497,7 +2836,7 @@ function assertCounts(geometry, expected) {
|
|
|
2497
2836
|
}
|
|
2498
2837
|
function disposeMaterials(scene) {
|
|
2499
2838
|
scene.traverse((object) => {
|
|
2500
|
-
if (!(object instanceof
|
|
2839
|
+
if (!(object instanceof Mesh4)) return;
|
|
2501
2840
|
for (const material of Array.isArray(object.material) ? object.material : [object.material]) {
|
|
2502
2841
|
material.dispose();
|
|
2503
2842
|
}
|
|
@@ -2998,7 +3337,7 @@ import { jsx as jsx6 } from "react/jsx-runtime";
|
|
|
2998
3337
|
var EnginePart = ({ report, ...props }) => {
|
|
2999
3338
|
const model = useMemo7(() => normalizePartReport(report), [report]);
|
|
3000
3339
|
const resource = useGeometryResource(model);
|
|
3001
|
-
|
|
3340
|
+
useEffect7(() => {
|
|
3002
3341
|
engineGeometryCache.retain(resource);
|
|
3003
3342
|
return () => engineGeometryCache.release(resource);
|
|
3004
3343
|
}, [resource]);
|
|
@@ -3026,8 +3365,42 @@ export {
|
|
|
3026
3365
|
CANDIDATE_WEIGHT,
|
|
3027
3366
|
HOVER_WEIGHT,
|
|
3028
3367
|
applyHighlightLayers,
|
|
3368
|
+
PERSPECTIVE_FOV,
|
|
3369
|
+
DEFAULT_FIT_MARGIN,
|
|
3370
|
+
EXCLUDE_FROM_FRAME,
|
|
3371
|
+
defaultBounds,
|
|
3372
|
+
aspectRatio,
|
|
3373
|
+
screenLength,
|
|
3374
|
+
boundsFromBox,
|
|
3375
|
+
contentBounds,
|
|
3376
|
+
excludedFromFrame,
|
|
3377
|
+
perspectiveFitDistance,
|
|
3378
|
+
orthographicHalfHeight,
|
|
3379
|
+
fitDistance,
|
|
3380
|
+
startPosition,
|
|
3381
|
+
MIN_FRAME_RATIO,
|
|
3382
|
+
MAX_FRAME_RATIO,
|
|
3383
|
+
cameraLimits,
|
|
3384
|
+
targetBoundary,
|
|
3385
|
+
applyProjection,
|
|
3386
|
+
CAD_CAMERA_UP,
|
|
3387
|
+
adaptedUp,
|
|
3388
|
+
cadViewDirections,
|
|
3389
|
+
currentViewDirection,
|
|
3029
3390
|
SECTION_RENDER_ORDER,
|
|
3030
3391
|
HANDLE_PIXELS,
|
|
3392
|
+
CAP_HATCH,
|
|
3393
|
+
AXES_PLANE_OFFSET,
|
|
3394
|
+
AXES_PLANE_SCALE,
|
|
3395
|
+
PREVIEW_SCALE,
|
|
3396
|
+
OUTLINE_SCALE,
|
|
3397
|
+
DEFAULT_SECTION_NORMAL,
|
|
3398
|
+
DISABLED_SECTION,
|
|
3399
|
+
sectionOptionsFromState,
|
|
3400
|
+
axesPlanes,
|
|
3401
|
+
hitUnderRay,
|
|
3402
|
+
surfaceUnderRay,
|
|
3403
|
+
sectionAnchor,
|
|
3031
3404
|
PICKED_SURFACE_LABEL,
|
|
3032
3405
|
sectionBounds,
|
|
3033
3406
|
sectionConstant,
|
|
@@ -3061,30 +3434,10 @@ export {
|
|
|
3061
3434
|
viewDirection,
|
|
3062
3435
|
buildPick,
|
|
3063
3436
|
focusForPick,
|
|
3064
|
-
PERSPECTIVE_FOV,
|
|
3065
|
-
DEFAULT_FIT_MARGIN,
|
|
3066
|
-
EXCLUDE_FROM_FRAME,
|
|
3067
|
-
defaultBounds,
|
|
3068
|
-
aspectRatio,
|
|
3069
|
-
screenLength,
|
|
3070
|
-
boundsFromBox,
|
|
3071
|
-
contentBounds,
|
|
3072
|
-
perspectiveFitDistance,
|
|
3073
|
-
orthographicHalfHeight,
|
|
3074
|
-
fitDistance,
|
|
3075
|
-
startPosition,
|
|
3076
|
-
MIN_FRAME_RATIO,
|
|
3077
|
-
MAX_FRAME_RATIO,
|
|
3078
|
-
cameraLimits,
|
|
3079
|
-
targetBoundary,
|
|
3080
|
-
applyProjection,
|
|
3081
|
-
CAD_CAMERA_UP,
|
|
3082
|
-
adaptedUp,
|
|
3083
|
-
cadViewDirections,
|
|
3084
|
-
currentViewDirection,
|
|
3085
3437
|
ExtendedCameraControls,
|
|
3086
3438
|
CadCameraControls,
|
|
3087
3439
|
retargetPose,
|
|
3440
|
+
createSectionStore,
|
|
3088
3441
|
ORBIT_TARGET_PIXELS,
|
|
3089
3442
|
ORBIT_TARGET_RING_PIXELS,
|
|
3090
3443
|
ORBIT_TARGET_RING_WIDTH,
|
|
@@ -3107,6 +3460,7 @@ export {
|
|
|
3107
3460
|
labelGeometry,
|
|
3108
3461
|
labelTexture,
|
|
3109
3462
|
useViewerControls,
|
|
3463
|
+
useSectionStore,
|
|
3110
3464
|
useRetarget,
|
|
3111
3465
|
Viewer,
|
|
3112
3466
|
CONE_AXIS,
|
|
@@ -3114,6 +3468,11 @@ export {
|
|
|
3114
3468
|
HEAD_RADIUS,
|
|
3115
3469
|
SHAFT_RADIUS,
|
|
3116
3470
|
arrowPlacement,
|
|
3471
|
+
createMaskTarget,
|
|
3472
|
+
createCapMaterial,
|
|
3473
|
+
applyCapTheme,
|
|
3474
|
+
createStencilMaterials,
|
|
3475
|
+
createMaskMaterial,
|
|
3117
3476
|
resolveSectionPlane,
|
|
3118
3477
|
SectionView,
|
|
3119
3478
|
useContentBox,
|