@toolpath/viewer 2.5.0 → 2.6.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 +113 -0
- package/dist/{chunk-LBG6GVGE.js → chunk-ESPHWO5M.js} +2 -0
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +70 -3
- package/dist/index.js +1132 -371
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -106,6 +106,7 @@ import {
|
|
|
106
106
|
formatMillimetres,
|
|
107
107
|
groupByDirection,
|
|
108
108
|
hitUnderRay,
|
|
109
|
+
ignoreRay,
|
|
109
110
|
labelGeometry,
|
|
110
111
|
labelTexture,
|
|
111
112
|
lockToAxis,
|
|
@@ -163,7 +164,7 @@ import {
|
|
|
163
164
|
viewUp,
|
|
164
165
|
viewVector,
|
|
165
166
|
visualSurfaces
|
|
166
|
-
} from "./chunk-
|
|
167
|
+
} from "./chunk-ESPHWO5M.js";
|
|
167
168
|
|
|
168
169
|
// src/render/feature-triangles.ts
|
|
169
170
|
var EMPTY = { positions: new Float32Array(), normals: new Float32Array() };
|
|
@@ -208,12 +209,771 @@ var winding = ([a, b, c]) => {
|
|
|
208
209
|
return [u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]];
|
|
209
210
|
};
|
|
210
211
|
|
|
212
|
+
// src/model/explode-amount.ts
|
|
213
|
+
var readExplodeAmount = (amount) => {
|
|
214
|
+
const value = typeof amount === "number" ? amount : amount.value;
|
|
215
|
+
return Number.isFinite(value) ? Math.max(0, Math.min(1, value)) : 0;
|
|
216
|
+
};
|
|
217
|
+
var createExplodeAmount = (initial = 0) => {
|
|
218
|
+
let value = readExplodeAmount(initial);
|
|
219
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
220
|
+
return {
|
|
221
|
+
get value() {
|
|
222
|
+
return value;
|
|
223
|
+
},
|
|
224
|
+
set: (next) => {
|
|
225
|
+
next = readExplodeAmount(next);
|
|
226
|
+
if (next === value) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
value = next;
|
|
230
|
+
for (const listener of listeners) {
|
|
231
|
+
listener();
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
subscribe: (listener) => {
|
|
235
|
+
const subscription = () => listener();
|
|
236
|
+
listeners.add(subscription);
|
|
237
|
+
return () => {
|
|
238
|
+
listeners.delete(subscription);
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/exploded-part.tsx
|
|
245
|
+
import { useEffect, useLayoutEffect, useMemo, useRef } from "react";
|
|
246
|
+
import { useFrame, useThree } from "@react-three/fiber";
|
|
247
|
+
import {
|
|
248
|
+
DoubleSide as DoubleSide2,
|
|
249
|
+
Vector4
|
|
250
|
+
} from "three";
|
|
251
|
+
|
|
252
|
+
// src/render/explode-theme.ts
|
|
253
|
+
var EXPLODE_DEFAULTS = {
|
|
254
|
+
ghost: 11913180,
|
|
255
|
+
ghostEdge: 10860497,
|
|
256
|
+
ghostEmissive: 0,
|
|
257
|
+
edge: 1582389,
|
|
258
|
+
edgeOpacity: 0.9,
|
|
259
|
+
selected: 16777215,
|
|
260
|
+
hovered: 16777215,
|
|
261
|
+
source: 16767619
|
|
262
|
+
};
|
|
263
|
+
var resolveExplodeTheme = (theme) => ({
|
|
264
|
+
ghost: theme?.part ?? EXPLODE_DEFAULTS.ghost,
|
|
265
|
+
ghostEdge: theme?.edge ?? EXPLODE_DEFAULTS.ghostEdge,
|
|
266
|
+
ghostEmissive: theme?.partEmissive ?? EXPLODE_DEFAULTS.ghostEmissive,
|
|
267
|
+
emissive: theme?.partEmissive ?? DEFAULT_THEME.partEmissive,
|
|
268
|
+
edge: theme?.edge ?? EXPLODE_DEFAULTS.edge,
|
|
269
|
+
edgeOpacity: theme?.edgeOpacity ?? EXPLODE_DEFAULTS.edgeOpacity,
|
|
270
|
+
selected: theme?.highlight ?? EXPLODE_DEFAULTS.selected,
|
|
271
|
+
hovered: theme?.hover ?? EXPLODE_DEFAULTS.hovered,
|
|
272
|
+
// The selected piece's source surfaces are what was picked, apart from its selection tint.
|
|
273
|
+
source: theme?.picked ?? EXPLODE_DEFAULTS.source
|
|
274
|
+
});
|
|
275
|
+
var DEFAULT_EXPLODE_THEME = resolveExplodeTheme();
|
|
276
|
+
var edgeOpacityScale = (theme) => theme.edgeOpacity / EXPLODE_DEFAULTS.edgeOpacity;
|
|
277
|
+
|
|
278
|
+
// src/render/explode.ts
|
|
279
|
+
import {
|
|
280
|
+
BatchedMesh,
|
|
281
|
+
Box3,
|
|
282
|
+
BufferGeometry as BufferGeometry2,
|
|
283
|
+
Color,
|
|
284
|
+
DoubleSide,
|
|
285
|
+
Float32BufferAttribute as Float32BufferAttribute2,
|
|
286
|
+
Group,
|
|
287
|
+
LineBasicMaterial,
|
|
288
|
+
LineSegments,
|
|
289
|
+
Matrix3,
|
|
290
|
+
Matrix4,
|
|
291
|
+
Mesh,
|
|
292
|
+
MeshLambertMaterial,
|
|
293
|
+
Vector3,
|
|
294
|
+
Uint32BufferAttribute
|
|
295
|
+
} from "three";
|
|
296
|
+
|
|
297
|
+
// src/render/explode-geometry.ts
|
|
298
|
+
import { BufferGeometry, Float32BufferAttribute } from "three";
|
|
299
|
+
var explodeTriangleCount = (model, geometry, groups) => {
|
|
300
|
+
if (geometry.index) {
|
|
301
|
+
throw new Error("ExplodedPart requires non-indexed source geometry.");
|
|
302
|
+
}
|
|
303
|
+
const position = geometry.getAttribute("position");
|
|
304
|
+
if (!position || position.itemSize !== 3 || position.count !== model.mesh.triangleCount * 3) {
|
|
305
|
+
throw new Error("ExplodedPart source geometry must match the model triangle ranges.");
|
|
306
|
+
}
|
|
307
|
+
const ids = /* @__PURE__ */ new Set();
|
|
308
|
+
let triangles = 0;
|
|
309
|
+
for (const group of groups) {
|
|
310
|
+
if (!group.id || ids.has(group.id)) {
|
|
311
|
+
throw new Error("ExplodedPart group IDs must be nonempty and unique.");
|
|
312
|
+
}
|
|
313
|
+
ids.add(group.id);
|
|
314
|
+
if (![group.offset.x, group.offset.y, group.offset.z].every(Number.isFinite)) {
|
|
315
|
+
throw new Error(`ExplodedPart group ${group.id} has a nonfinite offset.`);
|
|
316
|
+
}
|
|
317
|
+
if (group.regionIdxs.length === 0) {
|
|
318
|
+
throw new Error(`ExplodedPart group ${group.id} has no regions.`);
|
|
319
|
+
}
|
|
320
|
+
for (const region of new Set(group.regionIdxs)) {
|
|
321
|
+
const range = model.regionIndex.rangeForRegion(region);
|
|
322
|
+
if (!range) {
|
|
323
|
+
throw new Error(`Unknown source region ${region}`);
|
|
324
|
+
}
|
|
325
|
+
triangles += range.end - range.start;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return triangles;
|
|
329
|
+
};
|
|
330
|
+
var extractExplodeGroup = (source, geometry, group) => {
|
|
331
|
+
const regions = [...new Set(group.regionIdxs)].map((idx) => {
|
|
332
|
+
const region = source.regions.find((region2) => region2.idx === idx);
|
|
333
|
+
if (!region) {
|
|
334
|
+
throw new Error(`Unknown source region ${idx}`);
|
|
335
|
+
}
|
|
336
|
+
return region;
|
|
337
|
+
});
|
|
338
|
+
const triangles = regions.reduce(
|
|
339
|
+
(count, region) => count + region.triangles.end - region.triangles.start,
|
|
340
|
+
0
|
|
341
|
+
);
|
|
342
|
+
const positions = new Float32Array(triangles * 9);
|
|
343
|
+
const normals = new Float32Array(triangles * 9);
|
|
344
|
+
const sourceTriangles = new Uint32Array(triangles);
|
|
345
|
+
const position = geometry.getAttribute("position");
|
|
346
|
+
const normal = geometry.getAttribute("normal");
|
|
347
|
+
if (geometry.index) {
|
|
348
|
+
throw new Error("Explode requires the viewer\u2019s non-indexed input geometry.");
|
|
349
|
+
}
|
|
350
|
+
let cursor = 0;
|
|
351
|
+
const compact = regions.map((region) => {
|
|
352
|
+
const start = cursor;
|
|
353
|
+
for (let t = region.triangles.start; t < region.triangles.end; t += 1) {
|
|
354
|
+
sourceTriangles[cursor] = t;
|
|
355
|
+
for (let corner = 0; corner < 3; corner += 1) {
|
|
356
|
+
const v = t * 3 + corner;
|
|
357
|
+
const at = cursor * 9 + corner * 3;
|
|
358
|
+
positions.set([position.getX(v), position.getY(v), position.getZ(v)], at);
|
|
359
|
+
if (normal) {
|
|
360
|
+
normals.set([normal.getX(v), normal.getY(v), normal.getZ(v)], at);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
cursor += 1;
|
|
364
|
+
}
|
|
365
|
+
return { ...region, triangles: { start, end: cursor } };
|
|
366
|
+
});
|
|
367
|
+
const copy = new BufferGeometry();
|
|
368
|
+
copy.setAttribute("position", new Float32BufferAttribute(positions, 3));
|
|
369
|
+
if (normal) {
|
|
370
|
+
copy.setAttribute("normal", new Float32BufferAttribute(normals, 3));
|
|
371
|
+
} else {
|
|
372
|
+
copy.computeVertexNormals();
|
|
373
|
+
}
|
|
374
|
+
return { regions: compact, geometry: copy, sourceTriangles };
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// src/render/explode.ts
|
|
378
|
+
var ASSEMBLED = 1e-4;
|
|
379
|
+
var FADE_IN = 0.1;
|
|
380
|
+
var SELECTED_TINT = 0.45;
|
|
381
|
+
var HOVERED_TINT = 0.25;
|
|
382
|
+
var SOURCE_LIGHTEN = 0.45;
|
|
383
|
+
var WHITE = new Color(16777215);
|
|
384
|
+
var SURFACE_MATERIAL = {
|
|
385
|
+
emissive: DEFAULT_EXPLODE_THEME.emissive,
|
|
386
|
+
side: DoubleSide,
|
|
387
|
+
forceSinglePass: true,
|
|
388
|
+
transparent: true,
|
|
389
|
+
polygonOffset: true,
|
|
390
|
+
polygonOffsetFactor: 1,
|
|
391
|
+
polygonOffsetUnits: 1
|
|
392
|
+
};
|
|
393
|
+
var idsOf = (value) => value === null ? [] : typeof value === "string" ? [value] : value;
|
|
394
|
+
var overlay = (object, renderOrder = 0) => {
|
|
395
|
+
object.visible = false;
|
|
396
|
+
object.renderOrder = renderOrder;
|
|
397
|
+
object.frustumCulled = false;
|
|
398
|
+
object.raycast = ignoreRay;
|
|
399
|
+
object.userData[EXCLUDE_FROM_FRAME] = true;
|
|
400
|
+
return object;
|
|
401
|
+
};
|
|
402
|
+
var buildSurfaces = (model, geometry, groups) => {
|
|
403
|
+
const triangles = explodeTriangleCount(model, geometry, groups);
|
|
404
|
+
if (groups.length === 0) {
|
|
405
|
+
throw new Error("An explode batch needs at least one group.");
|
|
406
|
+
}
|
|
407
|
+
const material = new MeshLambertMaterial({
|
|
408
|
+
...SURFACE_MATERIAL,
|
|
409
|
+
color: 16777215,
|
|
410
|
+
vertexColors: true
|
|
411
|
+
});
|
|
412
|
+
const mesh = new BatchedMesh(groups.length, triangles * 3, 0, material);
|
|
413
|
+
mesh.name = "explode-surfaces";
|
|
414
|
+
mesh.renderOrder = 3;
|
|
415
|
+
mesh.frustumCulled = false;
|
|
416
|
+
const instanceColors = typeof mesh.setColorAt === "function";
|
|
417
|
+
const edgePoints = [];
|
|
418
|
+
let vertexStart = 0;
|
|
419
|
+
let edgeStart = 0;
|
|
420
|
+
const pieces = groups.map((group) => {
|
|
421
|
+
const extracted = extractExplodeGroup(model, geometry, group);
|
|
422
|
+
const edges = regionEdgesGeometry(extracted.geometry, extracted);
|
|
423
|
+
const points = edges.getAttribute("position").array;
|
|
424
|
+
const vertexCount = extracted.geometry.getAttribute("position").count;
|
|
425
|
+
extracted.geometry.setAttribute(
|
|
426
|
+
"color",
|
|
427
|
+
new Float32BufferAttribute2(new Float32Array(vertexCount * 3).fill(1), 3)
|
|
428
|
+
);
|
|
429
|
+
const geometryId = mesh.addGeometry(extracted.geometry);
|
|
430
|
+
const instanceId = typeof mesh.addInstance === "function" ? mesh.addInstance(geometryId) : geometryId;
|
|
431
|
+
const range = { start: vertexStart, count: vertexCount };
|
|
432
|
+
const regions = extracted.regions.map((region) => ({
|
|
433
|
+
idx: region.idx,
|
|
434
|
+
start: vertexStart + region.triangles.start * 3,
|
|
435
|
+
count: (region.triangles.end - region.triangles.start) * 3
|
|
436
|
+
}));
|
|
437
|
+
vertexStart += vertexCount;
|
|
438
|
+
edgePoints.push(points);
|
|
439
|
+
extracted.geometry.computeBoundingBox();
|
|
440
|
+
const piece = {
|
|
441
|
+
group,
|
|
442
|
+
instanceId,
|
|
443
|
+
range,
|
|
444
|
+
regions,
|
|
445
|
+
sourceTriangles: extracted.sourceTriangles,
|
|
446
|
+
centre: extracted.geometry.boundingBox.getCenter(new Vector3()),
|
|
447
|
+
bounds: extracted.geometry.boundingBox.clone(),
|
|
448
|
+
edgeStart,
|
|
449
|
+
edgeCount: points.length / 3
|
|
450
|
+
};
|
|
451
|
+
edgeStart += piece.edgeCount;
|
|
452
|
+
extracted.geometry.dispose();
|
|
453
|
+
edges.dispose();
|
|
454
|
+
return piece;
|
|
455
|
+
});
|
|
456
|
+
return { mesh, material, instanceColors, pieces, edgePoints, triangles, edgeVertices: edgeStart };
|
|
457
|
+
};
|
|
458
|
+
var buildOutlines = (pieces, edgePoints, edgeVertices) => {
|
|
459
|
+
const positions = new Float32Array(edgeVertices * 3);
|
|
460
|
+
const offsets = new Float32Array(edgeVertices * 3);
|
|
461
|
+
for (const [index, piece] of pieces.entries()) {
|
|
462
|
+
positions.set(edgePoints[index], piece.edgeStart * 3);
|
|
463
|
+
for (let vertex = piece.edgeStart; vertex < piece.edgeStart + piece.edgeCount; vertex += 1) {
|
|
464
|
+
offsets[vertex * 3] = piece.group.offset.x;
|
|
465
|
+
offsets[vertex * 3 + 1] = piece.group.offset.y;
|
|
466
|
+
offsets[vertex * 3 + 2] = piece.group.offset.z;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const geometry = new BufferGeometry2();
|
|
470
|
+
geometry.setAttribute("position", new Float32BufferAttribute2(positions, 3));
|
|
471
|
+
geometry.setAttribute("aExplodeOffset", new Float32BufferAttribute2(offsets, 3));
|
|
472
|
+
geometry.boundingBox = new Box3();
|
|
473
|
+
const amount = { value: 0 };
|
|
474
|
+
const material = new LineBasicMaterial({
|
|
475
|
+
color: DEFAULT_EXPLODE_THEME.edge,
|
|
476
|
+
transparent: true,
|
|
477
|
+
depthWrite: false
|
|
478
|
+
});
|
|
479
|
+
material.onBeforeCompile = (shader) => {
|
|
480
|
+
shader.uniforms.uExplodeAmount = amount;
|
|
481
|
+
shader.vertexShader = shader.vertexShader.replace(
|
|
482
|
+
"#include <common>",
|
|
483
|
+
"#include <common>\nattribute vec3 aExplodeOffset;\nuniform float uExplodeAmount;"
|
|
484
|
+
).replace(
|
|
485
|
+
"#include <begin_vertex>",
|
|
486
|
+
"#include <begin_vertex>\ntransformed += aExplodeOffset * uExplodeAmount;"
|
|
487
|
+
);
|
|
488
|
+
};
|
|
489
|
+
const outlines = new LineSegments(geometry, material);
|
|
490
|
+
outlines.name = "explode-edges";
|
|
491
|
+
outlines.renderOrder = 4;
|
|
492
|
+
outlines.frustumCulled = false;
|
|
493
|
+
outlines.raycast = ignoreRay;
|
|
494
|
+
return { outlines, amount };
|
|
495
|
+
};
|
|
496
|
+
var buildSourceSurfaces = (surfaces, triangles) => {
|
|
497
|
+
const geometry = new BufferGeometry2();
|
|
498
|
+
geometry.setAttribute("position", surfaces.geometry.getAttribute("position"));
|
|
499
|
+
geometry.setAttribute("normal", surfaces.geometry.getAttribute("normal"));
|
|
500
|
+
geometry.setIndex(new Uint32BufferAttribute(new Uint32Array(triangles * 3), 1));
|
|
501
|
+
geometry.setDrawRange(0, 0);
|
|
502
|
+
const material = new MeshLambertMaterial({
|
|
503
|
+
...SURFACE_MATERIAL,
|
|
504
|
+
color: new Color(DEFAULT_EXPLODE_THEME.source).lerp(WHITE, SOURCE_LIGHTEN)
|
|
505
|
+
});
|
|
506
|
+
const source = overlay(new Mesh(geometry, material), 3);
|
|
507
|
+
source.name = "explode-origin";
|
|
508
|
+
return source;
|
|
509
|
+
};
|
|
510
|
+
var buildSourceOutlines = (outlines, edgeVertices) => {
|
|
511
|
+
const geometry = new BufferGeometry2();
|
|
512
|
+
geometry.setAttribute("position", outlines.getAttribute("position"));
|
|
513
|
+
geometry.setIndex(new Uint32BufferAttribute(new Uint32Array(edgeVertices), 1));
|
|
514
|
+
geometry.setDrawRange(0, 0);
|
|
515
|
+
const material = new LineBasicMaterial({
|
|
516
|
+
color: DEFAULT_EXPLODE_THEME.edge,
|
|
517
|
+
transparent: true,
|
|
518
|
+
opacity: DEFAULT_EXPLODE_THEME.edgeOpacity
|
|
519
|
+
});
|
|
520
|
+
return overlay(new LineSegments(geometry, material), 4);
|
|
521
|
+
};
|
|
522
|
+
var buildConnector = () => {
|
|
523
|
+
const geometry = new BufferGeometry2();
|
|
524
|
+
geometry.setAttribute("position", new Float32BufferAttribute2(new Float32Array(6), 3));
|
|
525
|
+
const material = new LineBasicMaterial({
|
|
526
|
+
transparent: true,
|
|
527
|
+
opacity: 0.85,
|
|
528
|
+
depthWrite: false
|
|
529
|
+
});
|
|
530
|
+
return overlay(new LineSegments(geometry, material));
|
|
531
|
+
};
|
|
532
|
+
var createExplodeBatch = (model, geometry, groups) => {
|
|
533
|
+
const { mesh, material, instanceColors, pieces, edgePoints, triangles, edgeVertices } = buildSurfaces(model, geometry, groups);
|
|
534
|
+
const { outlines, amount: amountUniform } = buildOutlines(pieces, edgePoints, edgeVertices);
|
|
535
|
+
const source = buildSourceSurfaces(mesh, triangles);
|
|
536
|
+
const sourceOutlines = buildSourceOutlines(outlines.geometry, edgeVertices);
|
|
537
|
+
const connector = buildConnector();
|
|
538
|
+
const object = new Group();
|
|
539
|
+
object.add(mesh, outlines, source, sourceOutlines, connector);
|
|
540
|
+
const byId = new Map(pieces.map((piece) => [piece.group.id, piece]));
|
|
541
|
+
const matrix = new Matrix4();
|
|
542
|
+
const inverse = new Matrix4();
|
|
543
|
+
const normalMatrix = new Matrix3();
|
|
544
|
+
const color = new Color();
|
|
545
|
+
const highlightColor = new Color();
|
|
546
|
+
const sourceBounds = new Box3();
|
|
547
|
+
const placedBounds = new Box3();
|
|
548
|
+
const movedBounds = new Box3();
|
|
549
|
+
const point = new Vector3();
|
|
550
|
+
const modelPoint = new Vector3();
|
|
551
|
+
const normal = new Vector3();
|
|
552
|
+
let theme = DEFAULT_EXPLODE_THEME;
|
|
553
|
+
let selectedPieces = [];
|
|
554
|
+
let amount = -1;
|
|
555
|
+
let previousColors;
|
|
556
|
+
let previousRegionColors;
|
|
557
|
+
let previousSelected;
|
|
558
|
+
let previousHovered;
|
|
559
|
+
let previousSelectedIds = /* @__PURE__ */ new Set();
|
|
560
|
+
let previousHoveredIds = /* @__PURE__ */ new Set();
|
|
561
|
+
let appearanceSet = false;
|
|
562
|
+
const syncConnector = () => {
|
|
563
|
+
const visible = selectedPieces.length > 0 && amount > ASSEMBLED;
|
|
564
|
+
source.visible = visible;
|
|
565
|
+
sourceOutlines.visible = visible;
|
|
566
|
+
connector.visible = visible;
|
|
567
|
+
if (selectedPieces.length === 0) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const points = connector.geometry.getAttribute("position");
|
|
571
|
+
sourceBounds.getCenter(point);
|
|
572
|
+
points.setXYZ(0, point.x, point.y, point.z);
|
|
573
|
+
placedBounds.makeEmpty();
|
|
574
|
+
for (const piece of selectedPieces) {
|
|
575
|
+
const offset = piece.group.offset;
|
|
576
|
+
point.set(offset.x * amount, offset.y * amount, offset.z * amount);
|
|
577
|
+
placedBounds.union(movedBounds.copy(piece.bounds).translate(point));
|
|
578
|
+
}
|
|
579
|
+
placedBounds.getCenter(point);
|
|
580
|
+
points.setXYZ(1, point.x, point.y, point.z);
|
|
581
|
+
points.needsUpdate = true;
|
|
582
|
+
};
|
|
583
|
+
const hit = (batchId, faceIndex) => {
|
|
584
|
+
if (batchId === void 0 || faceIndex === void 0) {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
const piece = pieces[batchId];
|
|
588
|
+
const local = piece ? faceIndex - piece.range.start / 3 : -1;
|
|
589
|
+
return piece && local >= 0 && local < piece.sourceTriangles.length ? { group: piece.group, triangleIndex: piece.sourceTriangles[local] } : null;
|
|
590
|
+
};
|
|
591
|
+
return {
|
|
592
|
+
object,
|
|
593
|
+
mesh,
|
|
594
|
+
pieces,
|
|
595
|
+
hit,
|
|
596
|
+
/** A ray hit on the batch as a pick of its source surface, with the hit's unexploded point. */
|
|
597
|
+
pick: (intersection, context = {}) => {
|
|
598
|
+
const { face } = intersection;
|
|
599
|
+
if (amount <= ASSEMBLED || !face) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const found = hit(intersection.batchId, intersection.faceIndex ?? void 0);
|
|
603
|
+
if (!found) {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
const region = model.regionIndex.regionForTriangle(found.triangleIndex);
|
|
607
|
+
if (region === null) {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
normal.copy(face.normal).applyNormalMatrix(normalMatrix.getNormalMatrix(mesh.matrixWorld));
|
|
611
|
+
const { offset } = found.group;
|
|
612
|
+
modelPoint.copy(intersection.point).applyMatrix4(inverse.copy(mesh.matrixWorld).invert());
|
|
613
|
+
modelPoint.x -= offset.x * amount;
|
|
614
|
+
modelPoint.y -= offset.y * amount;
|
|
615
|
+
modelPoint.z -= offset.z * amount;
|
|
616
|
+
return {
|
|
617
|
+
...buildPick({
|
|
618
|
+
...context,
|
|
619
|
+
model,
|
|
620
|
+
region,
|
|
621
|
+
triangleIndex: found.triangleIndex,
|
|
622
|
+
point: intersection.point.toArray(),
|
|
623
|
+
normal: normal.toArray()
|
|
624
|
+
}),
|
|
625
|
+
explodeGroupId: found.group.id,
|
|
626
|
+
modelPoint: modelPoint.toArray()
|
|
627
|
+
};
|
|
628
|
+
},
|
|
629
|
+
setAppearance: ({
|
|
630
|
+
colors,
|
|
631
|
+
regionColors,
|
|
632
|
+
selected = null,
|
|
633
|
+
hovered = null,
|
|
634
|
+
theme: nextTheme = DEFAULT_EXPLODE_THEME
|
|
635
|
+
}) => {
|
|
636
|
+
const themeChanged = theme !== nextTheme;
|
|
637
|
+
if (appearanceSet && !themeChanged && previousColors === colors && previousRegionColors === regionColors && previousSelected === selected && previousHovered === hovered) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (themeChanged) {
|
|
641
|
+
theme = nextTheme;
|
|
642
|
+
material.emissive.setHex(theme.emissive);
|
|
643
|
+
source.material.emissive.setHex(theme.emissive);
|
|
644
|
+
source.material.color.setHex(theme.source).lerp(WHITE, SOURCE_LIGHTEN);
|
|
645
|
+
outlines.material.color.setHex(theme.edge);
|
|
646
|
+
outlines.material.opacity = theme.edgeOpacity * Math.min(1, Math.max(0, amount) / FADE_IN);
|
|
647
|
+
sourceOutlines.material.color.setHex(theme.edge);
|
|
648
|
+
sourceOutlines.material.opacity = theme.edgeOpacity;
|
|
649
|
+
}
|
|
650
|
+
const selectedIds = new Set(idsOf(selected));
|
|
651
|
+
const hoveredIds = new Set(idsOf(hovered));
|
|
652
|
+
for (const piece of pieces) {
|
|
653
|
+
const id = piece.group.id;
|
|
654
|
+
if (appearanceSet && !themeChanged && previousColors === colors && previousRegionColors === regionColors && !selectedIds.has(id) && !previousSelectedIds.has(id) && !hoveredIds.has(id) && !previousHoveredIds.has(id)) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
const base = colors?.get(id) ?? piece.group.color;
|
|
658
|
+
const regions = regionColors?.get(id);
|
|
659
|
+
const perVertex = !instanceColors || Boolean(regions?.size);
|
|
660
|
+
highlightColor.setHex(selectedIds.has(id) ? theme.selected : theme.hovered);
|
|
661
|
+
const tint = selectedIds.has(id) ? SELECTED_TINT : hoveredIds.has(id) ? HOVERED_TINT : 0;
|
|
662
|
+
const vertices = mesh.geometry.getAttribute("color");
|
|
663
|
+
if (perVertex || previousRegionColors?.get(id)?.size) {
|
|
664
|
+
for (const region of piece.regions) {
|
|
665
|
+
color.setHex(regions?.get(region.idx) ?? base).lerp(highlightColor, tint);
|
|
666
|
+
for (let vertex = region.start; vertex < region.start + region.count; vertex += 1) {
|
|
667
|
+
vertices.setXYZ(
|
|
668
|
+
vertex,
|
|
669
|
+
perVertex ? color.r : 1,
|
|
670
|
+
perVertex ? color.g : 1,
|
|
671
|
+
perVertex ? color.b : 1
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
vertices.needsUpdate = true;
|
|
676
|
+
}
|
|
677
|
+
if (instanceColors) {
|
|
678
|
+
color.setHex(base).lerp(highlightColor, tint);
|
|
679
|
+
mesh.setColorAt(piece.instanceId, perVertex ? WHITE : color);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
previousColors = colors;
|
|
683
|
+
previousRegionColors = regionColors;
|
|
684
|
+
const selectionChanged = previousSelected !== selected || !appearanceSet;
|
|
685
|
+
previousSelected = selected;
|
|
686
|
+
previousHovered = hovered;
|
|
687
|
+
appearanceSet = true;
|
|
688
|
+
previousSelectedIds = selectedIds;
|
|
689
|
+
previousHoveredIds = hoveredIds;
|
|
690
|
+
if (selectionChanged) {
|
|
691
|
+
selectedPieces = [...selectedIds].flatMap((id) => {
|
|
692
|
+
const piece = byId.get(id);
|
|
693
|
+
return piece ? [piece] : [];
|
|
694
|
+
});
|
|
695
|
+
sourceBounds.makeEmpty();
|
|
696
|
+
let vertices = 0;
|
|
697
|
+
let edgeVertices2 = 0;
|
|
698
|
+
const indices = source.geometry.index;
|
|
699
|
+
const edgeIndices = sourceOutlines.geometry.index;
|
|
700
|
+
for (const piece of selectedPieces) {
|
|
701
|
+
sourceBounds.union(piece.bounds);
|
|
702
|
+
for (let i = 0; i < piece.range.count; i += 1) {
|
|
703
|
+
indices.setX(vertices++, piece.range.start + i);
|
|
704
|
+
}
|
|
705
|
+
for (let i = 0; i < piece.edgeCount; i += 1) {
|
|
706
|
+
edgeIndices.setX(edgeVertices2++, piece.edgeStart + i);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
indices.needsUpdate = true;
|
|
710
|
+
edgeIndices.needsUpdate = true;
|
|
711
|
+
source.geometry.setDrawRange(0, vertices);
|
|
712
|
+
sourceOutlines.geometry.setDrawRange(0, edgeVertices2);
|
|
713
|
+
}
|
|
714
|
+
const first = selectedPieces[0];
|
|
715
|
+
if (first) {
|
|
716
|
+
connector.material.color.setHex(colors?.get(first.group.id) ?? first.group.color);
|
|
717
|
+
}
|
|
718
|
+
syncConnector();
|
|
719
|
+
},
|
|
720
|
+
setAmount: (next) => {
|
|
721
|
+
next = readExplodeAmount(next);
|
|
722
|
+
if (next === amount) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
amount = next;
|
|
726
|
+
const fade = Math.min(1, amount / FADE_IN);
|
|
727
|
+
material.depthWrite = fade === 1;
|
|
728
|
+
material.opacity = fade;
|
|
729
|
+
mesh.visible = amount > ASSEMBLED;
|
|
730
|
+
outlines.visible = mesh.visible;
|
|
731
|
+
outlines.material.opacity = theme.edgeOpacity * fade;
|
|
732
|
+
amountUniform.value = amount;
|
|
733
|
+
for (const piece of pieces) {
|
|
734
|
+
const offset = piece.group.offset;
|
|
735
|
+
matrix.makeTranslation(offset.x * amount, offset.y * amount, offset.z * amount);
|
|
736
|
+
mesh.setMatrixAt(piece.instanceId, matrix);
|
|
737
|
+
}
|
|
738
|
+
mesh.computeBoundingBox();
|
|
739
|
+
outlines.geometry.boundingBox.copy(mesh.boundingBox);
|
|
740
|
+
syncConnector();
|
|
741
|
+
},
|
|
742
|
+
dispose: () => {
|
|
743
|
+
object.clear();
|
|
744
|
+
mesh.dispose();
|
|
745
|
+
material.dispose();
|
|
746
|
+
for (const drawn of [outlines, source, sourceOutlines, connector]) {
|
|
747
|
+
drawn.geometry.dispose();
|
|
748
|
+
drawn.material.dispose();
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
// src/exploded-part.tsx
|
|
755
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
756
|
+
var GHOST_FADE = 0.18;
|
|
757
|
+
var Ghost = ({
|
|
758
|
+
model,
|
|
759
|
+
geometry,
|
|
760
|
+
explode,
|
|
761
|
+
appearance,
|
|
762
|
+
visible = true
|
|
763
|
+
}) => {
|
|
764
|
+
const material = useRef(null);
|
|
765
|
+
const outline = useRef(null);
|
|
766
|
+
const edges = useMemo(() => regionEdgesGeometry(geometry, model), [geometry, model]);
|
|
767
|
+
useEffect(() => () => edges.dispose(), [edges]);
|
|
768
|
+
useFrame(() => {
|
|
769
|
+
if (!visible) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const fade = Math.min(1, readExplodeAmount(explode.amount) / GHOST_FADE);
|
|
773
|
+
if (material.current) {
|
|
774
|
+
material.current.opacity = 1 - 0.88 * fade;
|
|
775
|
+
material.current.depthWrite = fade === 0;
|
|
776
|
+
}
|
|
777
|
+
if (outline.current) {
|
|
778
|
+
outline.current.opacity = (0.7 - 0.46 * fade) * edgeOpacityScale(appearance);
|
|
779
|
+
}
|
|
780
|
+
}, -1);
|
|
781
|
+
return /* @__PURE__ */ jsxs("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: !visible }, children: [
|
|
782
|
+
/* @__PURE__ */ jsx("mesh", { geometry, raycast: ignoreRay, children: /* @__PURE__ */ jsx(
|
|
783
|
+
"meshLambertMaterial",
|
|
784
|
+
{
|
|
785
|
+
ref: material,
|
|
786
|
+
color: appearance.ghost,
|
|
787
|
+
emissive: appearance.ghostEmissive,
|
|
788
|
+
transparent: true,
|
|
789
|
+
opacity: 0.12,
|
|
790
|
+
depthWrite: false,
|
|
791
|
+
side: DoubleSide2,
|
|
792
|
+
forceSinglePass: true
|
|
793
|
+
}
|
|
794
|
+
) }),
|
|
795
|
+
/* @__PURE__ */ jsx("lineSegments", { geometry: edges, raycast: ignoreRay, children: /* @__PURE__ */ jsx(
|
|
796
|
+
"lineBasicMaterial",
|
|
797
|
+
{
|
|
798
|
+
ref: outline,
|
|
799
|
+
color: appearance.ghostEdge,
|
|
800
|
+
transparent: true,
|
|
801
|
+
opacity: 0.24 * edgeOpacityScale(appearance),
|
|
802
|
+
depthWrite: false
|
|
803
|
+
}
|
|
804
|
+
) })
|
|
805
|
+
] });
|
|
806
|
+
};
|
|
807
|
+
var Pieces = ({
|
|
808
|
+
model,
|
|
809
|
+
geometry,
|
|
810
|
+
explode,
|
|
811
|
+
selected = null,
|
|
812
|
+
hovered = null,
|
|
813
|
+
activeDirection = null,
|
|
814
|
+
visible = true,
|
|
815
|
+
appearance,
|
|
816
|
+
onReady,
|
|
817
|
+
onPick,
|
|
818
|
+
onHover
|
|
819
|
+
}) => {
|
|
820
|
+
const { groups, colors, regionColors } = explode;
|
|
821
|
+
const invalidate = useThree((state) => state.invalidate);
|
|
822
|
+
const camera = useThree((state) => state.camera);
|
|
823
|
+
const gl = useThree((state) => state.gl);
|
|
824
|
+
const scene = useThree((state) => state.scene);
|
|
825
|
+
const isTap = useTapGuard();
|
|
826
|
+
const ready = useRef(onReady);
|
|
827
|
+
useLayoutEffect(() => {
|
|
828
|
+
ready.current = onReady;
|
|
829
|
+
}, [onReady]);
|
|
830
|
+
const root = useRef(null);
|
|
831
|
+
const resources = useRef(null);
|
|
832
|
+
useLayoutEffect(() => {
|
|
833
|
+
const parent = root.current;
|
|
834
|
+
const batch = createExplodeBatch(model, geometry, groups);
|
|
835
|
+
resources.current = batch;
|
|
836
|
+
parent.add(batch.object);
|
|
837
|
+
invalidate();
|
|
838
|
+
return () => {
|
|
839
|
+
resources.current = null;
|
|
840
|
+
parent.remove(batch.object);
|
|
841
|
+
batch.dispose();
|
|
842
|
+
};
|
|
843
|
+
}, [geometry, groups, invalidate, model]);
|
|
844
|
+
useLayoutEffect(() => {
|
|
845
|
+
resources.current?.setAppearance({
|
|
846
|
+
colors,
|
|
847
|
+
regionColors,
|
|
848
|
+
selected,
|
|
849
|
+
hovered,
|
|
850
|
+
theme: appearance
|
|
851
|
+
});
|
|
852
|
+
resources.current?.setAmount(readExplodeAmount(explode.amount));
|
|
853
|
+
invalidate();
|
|
854
|
+
}, [
|
|
855
|
+
appearance,
|
|
856
|
+
colors,
|
|
857
|
+
explode,
|
|
858
|
+
geometry,
|
|
859
|
+
groups,
|
|
860
|
+
hovered,
|
|
861
|
+
invalidate,
|
|
862
|
+
model,
|
|
863
|
+
regionColors,
|
|
864
|
+
selected
|
|
865
|
+
]);
|
|
866
|
+
useEffect(() => {
|
|
867
|
+
const batch = resources.current;
|
|
868
|
+
const scheduled = setTimeout(() => {
|
|
869
|
+
const scissor = gl.getScissor(new Vector4());
|
|
870
|
+
const scissorTest = gl.getScissorTest();
|
|
871
|
+
const parent = root.current;
|
|
872
|
+
const parentVisible = parent.visible;
|
|
873
|
+
const visibility = /* @__PURE__ */ new Map();
|
|
874
|
+
batch.object.traverse((object) => {
|
|
875
|
+
visibility.set(object, object.visible);
|
|
876
|
+
object.visible = true;
|
|
877
|
+
});
|
|
878
|
+
parent.visible = true;
|
|
879
|
+
try {
|
|
880
|
+
gl.setScissor(0, 0, 0, 0);
|
|
881
|
+
gl.setScissorTest(true);
|
|
882
|
+
gl.render(scene, camera);
|
|
883
|
+
} finally {
|
|
884
|
+
gl.setScissor(scissor);
|
|
885
|
+
gl.setScissorTest(scissorTest);
|
|
886
|
+
parent.visible = parentVisible;
|
|
887
|
+
for (const [object, visible2] of visibility) {
|
|
888
|
+
object.visible = visible2;
|
|
889
|
+
}
|
|
890
|
+
invalidate();
|
|
891
|
+
}
|
|
892
|
+
ready.current?.();
|
|
893
|
+
}, 0);
|
|
894
|
+
return () => clearTimeout(scheduled);
|
|
895
|
+
}, [camera, geometry, gl, groups, invalidate, model, scene]);
|
|
896
|
+
useFrame(() => {
|
|
897
|
+
if (visible) {
|
|
898
|
+
resources.current?.setAmount(readExplodeAmount(explode.amount));
|
|
899
|
+
}
|
|
900
|
+
}, -1);
|
|
901
|
+
const pick = (event) => {
|
|
902
|
+
const source = event.nativeEvent;
|
|
903
|
+
return resources.current?.pick(event, {
|
|
904
|
+
activeDirection,
|
|
905
|
+
viewDirection: viewDirection(camera, event.point),
|
|
906
|
+
pointer: { clientX: source.clientX, clientY: source.clientY },
|
|
907
|
+
modifiers: {
|
|
908
|
+
alt: source.altKey,
|
|
909
|
+
ctrl: source.ctrlKey,
|
|
910
|
+
meta: source.metaKey,
|
|
911
|
+
shift: source.shiftKey,
|
|
912
|
+
secondary: source.button === 2
|
|
913
|
+
},
|
|
914
|
+
doubled: source.detail === 2
|
|
915
|
+
}) ?? null;
|
|
916
|
+
};
|
|
917
|
+
return /* @__PURE__ */ jsx(
|
|
918
|
+
"group",
|
|
919
|
+
{
|
|
920
|
+
ref: root,
|
|
921
|
+
name: "exploded-part",
|
|
922
|
+
visible,
|
|
923
|
+
userData: { [EXCLUDE_FROM_FRAME]: !visible },
|
|
924
|
+
onPointerMove: visible && onHover ? (event) => {
|
|
925
|
+
event.stopPropagation();
|
|
926
|
+
onHover?.(pick(event));
|
|
927
|
+
} : void 0,
|
|
928
|
+
onPointerOut: visible && onHover ? () => onHover(null) : void 0,
|
|
929
|
+
onClick: visible && onPick ? (event) => {
|
|
930
|
+
event.stopPropagation();
|
|
931
|
+
if (isTap(event.nativeEvent)) {
|
|
932
|
+
const hit = pick(event);
|
|
933
|
+
if (hit) {
|
|
934
|
+
onPick?.(hit);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
} : void 0
|
|
938
|
+
}
|
|
939
|
+
);
|
|
940
|
+
};
|
|
941
|
+
var ExplodedPart = (props) => {
|
|
942
|
+
const { part, partEmissive, edge, edgeOpacity, highlight, hover, picked } = props.theme ?? {};
|
|
943
|
+
const appearance = useMemo(
|
|
944
|
+
() => resolveExplodeTheme({ part, partEmissive, edge, edgeOpacity, highlight, hover, picked }),
|
|
945
|
+
[part, partEmissive, edge, edgeOpacity, highlight, hover, picked]
|
|
946
|
+
);
|
|
947
|
+
const invalidate = useThree((state) => state.invalidate);
|
|
948
|
+
const amount = props.explode.amount;
|
|
949
|
+
useLayoutEffect(() => {
|
|
950
|
+
if (typeof amount !== "number") {
|
|
951
|
+
const unsubscribe = amount.subscribe(invalidate);
|
|
952
|
+
invalidate();
|
|
953
|
+
return unsubscribe;
|
|
954
|
+
}
|
|
955
|
+
}, [amount, invalidate]);
|
|
956
|
+
return props.explode.groups.length === 0 ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
957
|
+
/* @__PURE__ */ jsx(
|
|
958
|
+
Ghost,
|
|
959
|
+
{
|
|
960
|
+
model: props.model,
|
|
961
|
+
geometry: props.geometry,
|
|
962
|
+
explode: props.explode,
|
|
963
|
+
appearance,
|
|
964
|
+
visible: props.visible
|
|
965
|
+
}
|
|
966
|
+
),
|
|
967
|
+
/* @__PURE__ */ jsx(Pieces, { ...props, appearance })
|
|
968
|
+
] });
|
|
969
|
+
};
|
|
970
|
+
|
|
211
971
|
// src/hover-card.tsx
|
|
212
|
-
import { useEffect, useState } from "react";
|
|
213
|
-
import { jsx } from "react/jsx-runtime";
|
|
972
|
+
import { useEffect as useEffect2, useState } from "react";
|
|
973
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
214
974
|
var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
|
|
215
975
|
const [pointer, setPointer] = useState(pick?.pointer);
|
|
216
|
-
|
|
976
|
+
useEffect2(() => {
|
|
217
977
|
setPointer(pick?.pointer);
|
|
218
978
|
if (pick === null) return;
|
|
219
979
|
const follow = (event) => setPointer({ clientX: event.clientX, clientY: event.clientY });
|
|
@@ -228,7 +988,7 @@ var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
|
|
|
228
988
|
pointerEvents: "none",
|
|
229
989
|
zIndex: 4
|
|
230
990
|
};
|
|
231
|
-
return /* @__PURE__ */
|
|
991
|
+
return /* @__PURE__ */ jsx2(
|
|
232
992
|
"div",
|
|
233
993
|
{
|
|
234
994
|
className: ["viewer-hover-card", className2].filter(Boolean).join(" "),
|
|
@@ -244,8 +1004,8 @@ var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
|
|
|
244
1004
|
import { createContext, useContext } from "react";
|
|
245
1005
|
|
|
246
1006
|
// src/toolbar-icons.tsx
|
|
247
|
-
import { jsx as
|
|
248
|
-
var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE__ */
|
|
1007
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1008
|
+
var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE__ */ jsx3(
|
|
249
1009
|
"svg",
|
|
250
1010
|
{
|
|
251
1011
|
className: "viewer-toolbar-icon",
|
|
@@ -260,26 +1020,26 @@ var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE
|
|
|
260
1020
|
children
|
|
261
1021
|
}
|
|
262
1022
|
);
|
|
263
|
-
var FitIcon = () => /* @__PURE__ */
|
|
264
|
-
var ResetIcon = () => /* @__PURE__ */
|
|
265
|
-
var TopIcon = () => /* @__PURE__ */
|
|
266
|
-
var StockIcon = () => /* @__PURE__ */
|
|
267
|
-
var AxesIcon = () => /* @__PURE__ */
|
|
268
|
-
var GridIcon = () => /* @__PURE__ */
|
|
269
|
-
var BananaIcon = () => /* @__PURE__ */
|
|
270
|
-
var ToolsIcon = () => /* @__PURE__ */
|
|
271
|
-
var DirectionsIcon = () => /* @__PURE__ */
|
|
272
|
-
var DfmIcon = () => /* @__PURE__ */
|
|
273
|
-
var SharpCornersIcon = () => /* @__PURE__ */
|
|
274
|
-
/* @__PURE__ */
|
|
275
|
-
/* @__PURE__ */
|
|
1023
|
+
var FitIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M8 8h8v8H8z" }) });
|
|
1024
|
+
var ResetIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 10a9 9 0 1 1 2 8M3 4v6h6" }) });
|
|
1025
|
+
var TopIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 7l8-4 8 4-8 4zM4 7v10l8 4 8-4V7M12 11v10" }) });
|
|
1026
|
+
var StockIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 6l9-4 9 4v12l-9 4-9-4zM3 6l9 4 9-4M12 10v12M7 8v8l5 2 5-2V8" }) });
|
|
1027
|
+
var AxesIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M5 19V3m0 16h16M5 19l10-10M2 6l3-3 3 3m10 10 3 3-3 3M11 9h4v4" }) });
|
|
1028
|
+
var GridIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 8l9-5 9 5-9 5zM3 8v8l9 5 9-5V8M12 13v8M7.5 5.5v8M16.5 5.5v8" }) });
|
|
1029
|
+
var BananaIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { viewBox: "0 0 576 512", fill: "currentColor", children: /* @__PURE__ */ jsx3("path", { d: "M284.2 245.6c12.99 6.929 25.35 15.14 36.08 25.8L334.5 285.6l65.75-23.47c14.75-5.265 30.18-7.849 45.81-8.389c1.154-10.73 1.764-21.38 1.764-31.87c0-118.5-81.33-221.9-119.7-221.9c-21.01 0-40.91 17.04-40.91 39.25c0 16.18 16.74 41.9 16.74 103C303.1 170.1 300.6 203.1 284.2 245.6zM575.1 389.6c0-3.687-.8637-7.429-2.687-10.93l-15.12-29.11c-21.05-40.53-63.08-64.51-106.9-64.51c-13.43 0-27.02 2.252-40.22 6.969l-84.84 30.27l-28.59-28.41C274.4 270.9 243.7 258.1 212.8 258.1c-23.72 0-47.57 6.97-68.29 21.2L106.3 306.4c-6.732 4.631-10.35 12.07-10.35 19.63c0 14.93 12.7 23.87 24.04 23.87c4.695 0 9.443-1.376 13.61-4.26l38.13-26.23c12.43-8.525 26.71-12.69 40.91-12.69c10.64 0 21.24 2.339 30.97 6.934c-50.62 62.23-128.3 99.85-211.4 99.85C14.42 413.5 0 427.8 0 445.5v31.38c0 17.68 14.66 32.02 32.46 32.02l28.98.0009c14.15 0 34.69 1.098 59.07 1.098c93.51 0 243.4-16.15 304.8-172.4c9.021-3.22 17.95-4.712 26.53-4.712c27.65 0 51.7 15.49 63.7 38.55l15.12 29.11c3.484 6.723 11.74 12.98 21.41 12.98C564.1 413.6 575.1 403.8 575.1 389.6z" }) });
|
|
1030
|
+
var ToolsIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M9 2h6v7H9zM9.5 9v9.5l2.5 3.5 2.5-3.5V9M9.5 12l5 2.5M9.5 15.5l5 2.5" }) });
|
|
1031
|
+
var DirectionsIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M7 3v18m-4-4 4 4 4-4M17 21V3m-4 4 4-4 4 4" }) });
|
|
1032
|
+
var DfmIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M8.3 3h7.4L21 8.3v7.4L15.7 21H8.3L3 15.7V8.3zM12 7.5v5.5m0 3.5v.01" }) });
|
|
1033
|
+
var SharpCornersIcon = () => /* @__PURE__ */ jsxs2(SvgIcon, { children: [
|
|
1034
|
+
/* @__PURE__ */ jsx3("path", { d: "M4 3v17h17" }),
|
|
1035
|
+
/* @__PURE__ */ jsx3("circle", { cx: "9", cy: "15", r: "4" })
|
|
276
1036
|
] });
|
|
277
|
-
var ThreadsIcon = () => /* @__PURE__ */
|
|
278
|
-
var HoverIcon = () => /* @__PURE__ */
|
|
279
|
-
var FocusIcon = () => /* @__PURE__ */
|
|
280
|
-
var WireframeIcon = () => /* @__PURE__ */
|
|
281
|
-
var SectionIcon = () => /* @__PURE__ */
|
|
282
|
-
var MeasureIcon = () => /* @__PURE__ */
|
|
1037
|
+
var ThreadsIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M8 3h8v18H8zM8 8l8-2M8 12l8-2M8 16l8-2M8 20l8-2" }) });
|
|
1038
|
+
var HoverIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 4h16v12H9l-5 4zm4 5h8m-8 3h5" }) });
|
|
1039
|
+
var FocusIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 4h16v16H4zM8 8h8v8H8zM2 12h4m12 0h4M12 2v4m0 12v4" }) });
|
|
1040
|
+
var WireframeIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M4 7l8-4 8 4v10l-8 4-8-4zM4 7l8 4 8-4M12 11v10M4 17l8-4 8 4M12 3v10" }) });
|
|
1041
|
+
var SectionIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 6h9v12H3zM12 3v18M16 6h2m3 3v2m0 3v2m-5 3h-2" }) });
|
|
1042
|
+
var MeasureIcon = () => /* @__PURE__ */ jsx3(SvgIcon, { children: /* @__PURE__ */ jsx3("path", { d: "M3 16L16 3l5 5L8 21zM8 11l3 3m1-7 3 3m-11 5 3 3" }) });
|
|
283
1043
|
var ICONS = {
|
|
284
1044
|
fit: FitIcon,
|
|
285
1045
|
reset: ResetIcon,
|
|
@@ -301,16 +1061,16 @@ var ICONS = {
|
|
|
301
1061
|
};
|
|
302
1062
|
var ToolbarIcon = ({ name }) => {
|
|
303
1063
|
const Icon = ICONS[name];
|
|
304
|
-
return /* @__PURE__ */
|
|
1064
|
+
return /* @__PURE__ */ jsx3(Icon, {});
|
|
305
1065
|
};
|
|
306
1066
|
|
|
307
1067
|
// src/viewer-toolbar.tsx
|
|
308
|
-
import { jsx as
|
|
1068
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
309
1069
|
var ViewerToolbarContext = createContext(null);
|
|
310
1070
|
var ViewerToolbarProvider = ({
|
|
311
1071
|
controls,
|
|
312
1072
|
children
|
|
313
|
-
}) => /* @__PURE__ */
|
|
1073
|
+
}) => /* @__PURE__ */ jsx4(ViewerToolbarContext.Provider, { value: controls, children });
|
|
314
1074
|
var useViewerToolbar = () => {
|
|
315
1075
|
const controls = useContext(ViewerToolbarContext);
|
|
316
1076
|
if (!controls) throw new Error("useViewerToolbar must be used inside <ViewerToolbarProvider>");
|
|
@@ -323,7 +1083,7 @@ var ToolbarButton = ({ action, icon, label, toggle = true }) => {
|
|
|
323
1083
|
throw new Error(`<ViewerToolbar.${buttonName(action)}> requires a '${action}' toolbar control`);
|
|
324
1084
|
}
|
|
325
1085
|
const pressed = control.pressed ?? false;
|
|
326
|
-
return /* @__PURE__ */
|
|
1086
|
+
return /* @__PURE__ */ jsxs3(
|
|
327
1087
|
"button",
|
|
328
1088
|
{
|
|
329
1089
|
className: "viewer-toolbar-button",
|
|
@@ -333,13 +1093,13 @@ var ToolbarButton = ({ action, icon, label, toggle = true }) => {
|
|
|
333
1093
|
"aria-pressed": toggle ? pressed : void 0,
|
|
334
1094
|
onClick: control.onClick,
|
|
335
1095
|
children: [
|
|
336
|
-
/* @__PURE__ */
|
|
337
|
-
/* @__PURE__ */
|
|
1096
|
+
/* @__PURE__ */ jsx4(ToolbarIcon, { name: icon }),
|
|
1097
|
+
/* @__PURE__ */ jsx4("span", { className: "viewer-toolbar-tooltip", "data-viewer-toolbar-tooltip": "true", role: "tooltip", children: label(pressed) })
|
|
338
1098
|
]
|
|
339
1099
|
}
|
|
340
1100
|
);
|
|
341
1101
|
};
|
|
342
|
-
var StockButton = () => /* @__PURE__ */
|
|
1102
|
+
var StockButton = () => /* @__PURE__ */ jsx4(
|
|
343
1103
|
ToolbarButton,
|
|
344
1104
|
{
|
|
345
1105
|
action: "stock",
|
|
@@ -347,7 +1107,7 @@ var StockButton = () => /* @__PURE__ */ jsx3(
|
|
|
347
1107
|
label: (pressed) => pressed ? "Hide stock" : "Show stock"
|
|
348
1108
|
}
|
|
349
1109
|
);
|
|
350
|
-
var AxesButton = () => /* @__PURE__ */
|
|
1110
|
+
var AxesButton = () => /* @__PURE__ */ jsx4(
|
|
351
1111
|
ToolbarButton,
|
|
352
1112
|
{
|
|
353
1113
|
action: "axes",
|
|
@@ -355,7 +1115,7 @@ var AxesButton = () => /* @__PURE__ */ jsx3(
|
|
|
355
1115
|
label: (pressed) => pressed ? "Hide axis" : "Show axis"
|
|
356
1116
|
}
|
|
357
1117
|
);
|
|
358
|
-
var GridButton = () => /* @__PURE__ */
|
|
1118
|
+
var GridButton = () => /* @__PURE__ */ jsx4(
|
|
359
1119
|
ToolbarButton,
|
|
360
1120
|
{
|
|
361
1121
|
action: "grid",
|
|
@@ -363,7 +1123,7 @@ var GridButton = () => /* @__PURE__ */ jsx3(
|
|
|
363
1123
|
label: (pressed) => pressed ? "Hide grid" : "Show grid"
|
|
364
1124
|
}
|
|
365
1125
|
);
|
|
366
|
-
var BananaButton = () => /* @__PURE__ */
|
|
1126
|
+
var BananaButton = () => /* @__PURE__ */ jsx4(
|
|
367
1127
|
ToolbarButton,
|
|
368
1128
|
{
|
|
369
1129
|
action: "banana",
|
|
@@ -371,7 +1131,7 @@ var BananaButton = () => /* @__PURE__ */ jsx3(
|
|
|
371
1131
|
label: (pressed) => pressed ? "Banana for scale (on)" : "Banana for scale"
|
|
372
1132
|
}
|
|
373
1133
|
);
|
|
374
|
-
var ToolsButton = () => /* @__PURE__ */
|
|
1134
|
+
var ToolsButton = () => /* @__PURE__ */ jsx4(
|
|
375
1135
|
ToolbarButton,
|
|
376
1136
|
{
|
|
377
1137
|
action: "tools",
|
|
@@ -379,7 +1139,7 @@ var ToolsButton = () => /* @__PURE__ */ jsx3(
|
|
|
379
1139
|
label: (pressed) => pressed ? "Hide tools on the part" : "Show tools on the part"
|
|
380
1140
|
}
|
|
381
1141
|
);
|
|
382
|
-
var DirectionsButton = () => /* @__PURE__ */
|
|
1142
|
+
var DirectionsButton = () => /* @__PURE__ */ jsx4(
|
|
383
1143
|
ToolbarButton,
|
|
384
1144
|
{
|
|
385
1145
|
action: "directions",
|
|
@@ -387,7 +1147,7 @@ var DirectionsButton = () => /* @__PURE__ */ jsx3(
|
|
|
387
1147
|
label: () => "Highlight faces by direction"
|
|
388
1148
|
}
|
|
389
1149
|
);
|
|
390
|
-
var DfmButton = () => /* @__PURE__ */
|
|
1150
|
+
var DfmButton = () => /* @__PURE__ */ jsx4(
|
|
391
1151
|
ToolbarButton,
|
|
392
1152
|
{
|
|
393
1153
|
action: "dfm",
|
|
@@ -395,7 +1155,7 @@ var DfmButton = () => /* @__PURE__ */ jsx3(
|
|
|
395
1155
|
label: (pressed) => pressed ? "Hide DFM flags" : "Show DFM flags"
|
|
396
1156
|
}
|
|
397
1157
|
);
|
|
398
|
-
var SharpCornersButton = () => /* @__PURE__ */
|
|
1158
|
+
var SharpCornersButton = () => /* @__PURE__ */ jsx4(
|
|
399
1159
|
ToolbarButton,
|
|
400
1160
|
{
|
|
401
1161
|
action: "sharpCorners",
|
|
@@ -403,7 +1163,7 @@ var SharpCornersButton = () => /* @__PURE__ */ jsx3(
|
|
|
403
1163
|
label: (pressed) => pressed ? "Hide sharp corners" : "Show sharp corners"
|
|
404
1164
|
}
|
|
405
1165
|
);
|
|
406
|
-
var ThreadsButton = () => /* @__PURE__ */
|
|
1166
|
+
var ThreadsButton = () => /* @__PURE__ */ jsx4(
|
|
407
1167
|
ToolbarButton,
|
|
408
1168
|
{
|
|
409
1169
|
action: "threads",
|
|
@@ -411,7 +1171,7 @@ var ThreadsButton = () => /* @__PURE__ */ jsx3(
|
|
|
411
1171
|
label: (pressed) => pressed ? "Hide threads" : "Show threads"
|
|
412
1172
|
}
|
|
413
1173
|
);
|
|
414
|
-
var HoverButton = () => /* @__PURE__ */
|
|
1174
|
+
var HoverButton = () => /* @__PURE__ */ jsx4(
|
|
415
1175
|
ToolbarButton,
|
|
416
1176
|
{
|
|
417
1177
|
action: "hover",
|
|
@@ -419,7 +1179,7 @@ var HoverButton = () => /* @__PURE__ */ jsx3(
|
|
|
419
1179
|
label: (pressed) => pressed ? "Disable feature hover" : "Enable feature hover"
|
|
420
1180
|
}
|
|
421
1181
|
);
|
|
422
|
-
var FocusButton = () => /* @__PURE__ */
|
|
1182
|
+
var FocusButton = () => /* @__PURE__ */ jsx4(
|
|
423
1183
|
ToolbarButton,
|
|
424
1184
|
{
|
|
425
1185
|
action: "focus",
|
|
@@ -427,8 +1187,8 @@ var FocusButton = () => /* @__PURE__ */ jsx3(
|
|
|
427
1187
|
label: (pressed) => pressed ? "Show full part" : "Focus selection"
|
|
428
1188
|
}
|
|
429
1189
|
);
|
|
430
|
-
var WireframeButton = () => /* @__PURE__ */
|
|
431
|
-
var SectionButton = () => /* @__PURE__ */
|
|
1190
|
+
var WireframeButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "wireframe", icon: "wireframe", label: () => "Wireframe" });
|
|
1191
|
+
var SectionButton = () => /* @__PURE__ */ jsx4(
|
|
432
1192
|
ToolbarButton,
|
|
433
1193
|
{
|
|
434
1194
|
action: "section",
|
|
@@ -436,7 +1196,7 @@ var SectionButton = () => /* @__PURE__ */ jsx3(
|
|
|
436
1196
|
label: (pressed) => pressed ? "Exit section" : "Section"
|
|
437
1197
|
}
|
|
438
1198
|
);
|
|
439
|
-
var MeasureButton = () => /* @__PURE__ */
|
|
1199
|
+
var MeasureButton = () => /* @__PURE__ */ jsx4(
|
|
440
1200
|
ToolbarButton,
|
|
441
1201
|
{
|
|
442
1202
|
action: "measure",
|
|
@@ -444,11 +1204,11 @@ var MeasureButton = () => /* @__PURE__ */ jsx3(
|
|
|
444
1204
|
label: (pressed) => pressed ? "Exit measure" : "Measure"
|
|
445
1205
|
}
|
|
446
1206
|
);
|
|
447
|
-
var FitButton = () => /* @__PURE__ */
|
|
448
|
-
var ResetButton = () => /* @__PURE__ */
|
|
449
|
-
var TopButton = () => /* @__PURE__ */
|
|
450
|
-
var Divider = () => /* @__PURE__ */
|
|
451
|
-
var Controls = ({ children, className: className2 }) => /* @__PURE__ */
|
|
1207
|
+
var FitButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "fit", icon: "fit", label: () => "Fit", toggle: false });
|
|
1208
|
+
var ResetButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "reset", icon: "reset", label: () => "Reset", toggle: false });
|
|
1209
|
+
var TopButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "top", icon: "top", label: () => "Top view", toggle: false });
|
|
1210
|
+
var Divider = () => /* @__PURE__ */ jsx4("span", { className: "viewer-toolbar-divider", "data-viewer-toolbar-divider": "true" });
|
|
1211
|
+
var Controls = ({ children, className: className2 }) => /* @__PURE__ */ jsx4(
|
|
452
1212
|
"div",
|
|
453
1213
|
{
|
|
454
1214
|
className: ["viewer-toolbar", className2].filter(Boolean).join(" "),
|
|
@@ -461,44 +1221,44 @@ var Controls = ({ children, className: className2 }) => /* @__PURE__ */ jsx3(
|
|
|
461
1221
|
var DefaultControls = () => {
|
|
462
1222
|
const controls = useViewerToolbar();
|
|
463
1223
|
const display = [
|
|
464
|
-
controls.stock ? /* @__PURE__ */
|
|
465
|
-
controls.axes ? /* @__PURE__ */
|
|
466
|
-
controls.grid ? /* @__PURE__ */
|
|
467
|
-
controls.banana ? /* @__PURE__ */
|
|
468
|
-
controls.tools ? /* @__PURE__ */
|
|
1224
|
+
controls.stock ? /* @__PURE__ */ jsx4(StockButton, {}, "stock") : null,
|
|
1225
|
+
controls.axes ? /* @__PURE__ */ jsx4(AxesButton, {}, "axes") : null,
|
|
1226
|
+
controls.grid ? /* @__PURE__ */ jsx4(GridButton, {}, "grid") : null,
|
|
1227
|
+
controls.banana ? /* @__PURE__ */ jsx4(BananaButton, {}, "banana") : null,
|
|
1228
|
+
controls.tools ? /* @__PURE__ */ jsx4(ToolsButton, {}, "tools") : null
|
|
469
1229
|
].filter(Boolean);
|
|
470
1230
|
const analysis = [
|
|
471
|
-
controls.directions ? /* @__PURE__ */
|
|
472
|
-
controls.dfm ? /* @__PURE__ */
|
|
473
|
-
controls.sharpCorners ? /* @__PURE__ */
|
|
474
|
-
controls.threads ? /* @__PURE__ */
|
|
475
|
-
controls.hover ? /* @__PURE__ */
|
|
476
|
-
controls.focus ? /* @__PURE__ */
|
|
477
|
-
controls.wireframe ? /* @__PURE__ */
|
|
478
|
-
controls.section ? /* @__PURE__ */
|
|
479
|
-
controls.measure ? /* @__PURE__ */
|
|
1231
|
+
controls.directions ? /* @__PURE__ */ jsx4(DirectionsButton, {}, "directions") : null,
|
|
1232
|
+
controls.dfm ? /* @__PURE__ */ jsx4(DfmButton, {}, "dfm") : null,
|
|
1233
|
+
controls.sharpCorners ? /* @__PURE__ */ jsx4(SharpCornersButton, {}, "sharpCorners") : null,
|
|
1234
|
+
controls.threads ? /* @__PURE__ */ jsx4(ThreadsButton, {}, "threads") : null,
|
|
1235
|
+
controls.hover ? /* @__PURE__ */ jsx4(HoverButton, {}, "hover") : null,
|
|
1236
|
+
controls.focus ? /* @__PURE__ */ jsx4(FocusButton, {}, "focus") : null,
|
|
1237
|
+
controls.wireframe ? /* @__PURE__ */ jsx4(WireframeButton, {}, "wireframe") : null,
|
|
1238
|
+
controls.section ? /* @__PURE__ */ jsx4(SectionButton, {}, "section") : null,
|
|
1239
|
+
controls.measure ? /* @__PURE__ */ jsx4(MeasureButton, {}, "measure") : null
|
|
480
1240
|
].filter(Boolean);
|
|
481
1241
|
const camera = [
|
|
482
|
-
controls.fit ? /* @__PURE__ */
|
|
483
|
-
controls.reset ? /* @__PURE__ */
|
|
484
|
-
controls.top ? /* @__PURE__ */
|
|
1242
|
+
controls.fit ? /* @__PURE__ */ jsx4(FitButton, {}, "fit") : null,
|
|
1243
|
+
controls.reset ? /* @__PURE__ */ jsx4(ResetButton, {}, "reset") : null,
|
|
1244
|
+
controls.top ? /* @__PURE__ */ jsx4(TopButton, {}, "top") : null
|
|
485
1245
|
].filter(Boolean);
|
|
486
|
-
return /* @__PURE__ */
|
|
1246
|
+
return /* @__PURE__ */ jsxs3(Controls, { children: [
|
|
487
1247
|
display,
|
|
488
|
-
display.length && analysis.length ? /* @__PURE__ */
|
|
1248
|
+
display.length && analysis.length ? /* @__PURE__ */ jsx4(Divider, {}) : null,
|
|
489
1249
|
analysis,
|
|
490
|
-
(display.length || analysis.length) && camera.length ? /* @__PURE__ */
|
|
1250
|
+
(display.length || analysis.length) && camera.length ? /* @__PURE__ */ jsx4(Divider, {}) : null,
|
|
491
1251
|
camera
|
|
492
1252
|
] });
|
|
493
1253
|
};
|
|
494
1254
|
var ViewerToolbarRoot = ({ className: className2, children }) => {
|
|
495
1255
|
useViewerToolbar();
|
|
496
|
-
return /* @__PURE__ */
|
|
1256
|
+
return /* @__PURE__ */ jsx4(
|
|
497
1257
|
"div",
|
|
498
1258
|
{
|
|
499
1259
|
className: ["viewer-toolbar-stack", className2].filter(Boolean).join(" "),
|
|
500
1260
|
"data-viewer-toolbar": "true",
|
|
501
|
-
children: children ?? /* @__PURE__ */
|
|
1261
|
+
children: children ?? /* @__PURE__ */ jsx4(DefaultControls, {})
|
|
502
1262
|
}
|
|
503
1263
|
);
|
|
504
1264
|
};
|
|
@@ -525,11 +1285,11 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
|
|
|
525
1285
|
});
|
|
526
1286
|
|
|
527
1287
|
// src/stock.tsx
|
|
528
|
-
import { useThree } from "@react-three/fiber";
|
|
529
|
-
import { useEffect as
|
|
1288
|
+
import { useThree as useThree2 } from "@react-three/fiber";
|
|
1289
|
+
import { useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useState as useState2 } from "react";
|
|
530
1290
|
|
|
531
1291
|
// src/render/stock-mesh.ts
|
|
532
|
-
import { Mesh } from "three";
|
|
1292
|
+
import { Mesh as Mesh2 } from "three";
|
|
533
1293
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
534
1294
|
async function parseStockGlb(data) {
|
|
535
1295
|
const { scene } = await new GLTFLoader().parseAsync(data, "");
|
|
@@ -537,7 +1297,7 @@ async function parseStockGlb(data) {
|
|
|
537
1297
|
return stockMeshGeometries(scene);
|
|
538
1298
|
} finally {
|
|
539
1299
|
scene.traverse((object) => {
|
|
540
|
-
if (!(object instanceof
|
|
1300
|
+
if (!(object instanceof Mesh2)) return;
|
|
541
1301
|
object.geometry.dispose();
|
|
542
1302
|
for (const material of Array.isArray(object.material) ? object.material : [object.material]) {
|
|
543
1303
|
material.dispose();
|
|
@@ -549,7 +1309,7 @@ function stockMeshGeometries(scene) {
|
|
|
549
1309
|
const geometries = [];
|
|
550
1310
|
scene.updateWorldMatrix(true, true);
|
|
551
1311
|
scene.traverse((object) => {
|
|
552
|
-
if (!(object instanceof
|
|
1312
|
+
if (!(object instanceof Mesh2)) return;
|
|
553
1313
|
const geometry = object.geometry.clone().applyMatrix4(object.matrixWorld);
|
|
554
1314
|
if (!geometry.hasAttribute("normal")) geometry.computeVertexNormals();
|
|
555
1315
|
geometries.push(geometry);
|
|
@@ -604,30 +1364,30 @@ function createStockMeshBuffer() {
|
|
|
604
1364
|
|
|
605
1365
|
// src/render/stock.ts
|
|
606
1366
|
import {
|
|
607
|
-
Box3,
|
|
1367
|
+
Box3 as Box32,
|
|
608
1368
|
BoxGeometry,
|
|
609
|
-
BufferGeometry,
|
|
1369
|
+
BufferGeometry as BufferGeometry3,
|
|
610
1370
|
CylinderGeometry,
|
|
611
|
-
Float32BufferAttribute,
|
|
612
|
-
Group,
|
|
613
|
-
LineBasicMaterial,
|
|
614
|
-
LineSegments,
|
|
615
|
-
Matrix4,
|
|
616
|
-
Mesh as
|
|
617
|
-
MeshLambertMaterial,
|
|
1371
|
+
Float32BufferAttribute as Float32BufferAttribute3,
|
|
1372
|
+
Group as Group2,
|
|
1373
|
+
LineBasicMaterial as LineBasicMaterial2,
|
|
1374
|
+
LineSegments as LineSegments2,
|
|
1375
|
+
Matrix4 as Matrix42,
|
|
1376
|
+
Mesh as Mesh3,
|
|
1377
|
+
MeshLambertMaterial as MeshLambertMaterial2,
|
|
618
1378
|
Quaternion,
|
|
619
|
-
Vector3 as
|
|
1379
|
+
Vector3 as Vector33
|
|
620
1380
|
} from "three";
|
|
621
1381
|
|
|
622
1382
|
// src/render/stock-edges.ts
|
|
623
|
-
import { MathUtils, Triangle, Vector3 } from "three";
|
|
1383
|
+
import { MathUtils, Triangle, Vector3 as Vector32 } from "three";
|
|
624
1384
|
var STOCK_EDGE_ANGLE = 15;
|
|
625
1385
|
var SYNC_EDGE_TRIANGLES = 2e4;
|
|
626
1386
|
var SLICE_MS = 4;
|
|
627
1387
|
var CHECK_EVERY = 256;
|
|
628
|
-
var _v0 = new
|
|
629
|
-
var _v1 = new
|
|
630
|
-
var _normal = new
|
|
1388
|
+
var _v0 = new Vector32();
|
|
1389
|
+
var _v1 = new Vector32();
|
|
1390
|
+
var _normal = new Vector32();
|
|
631
1391
|
var _triangle = new Triangle();
|
|
632
1392
|
function* edgeWork(geometry, thresholdAngle) {
|
|
633
1393
|
const precision = Math.pow(10, 4);
|
|
@@ -751,7 +1511,7 @@ function boxFigure(bounds) {
|
|
|
751
1511
|
};
|
|
752
1512
|
}
|
|
753
1513
|
var CYLINDER_SEGMENTS = 64;
|
|
754
|
-
var CYLINDER_AXIS = new
|
|
1514
|
+
var CYLINDER_AXIS = new Vector33(0, 1, 0);
|
|
755
1515
|
function fixedBoxStockBounds(geometry, dimensions, position = "model_centered", positionOffset = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
756
1516
|
if (![dimensions.x, dimensions.y, dimensions.z].every(Number.isFinite) || [dimensions.x, dimensions.y, dimensions.z].some((value) => value <= 0)) {
|
|
757
1517
|
throw new RangeError("Fixed stock dimensions must be finite and positive.");
|
|
@@ -763,12 +1523,12 @@ function fixedBoxStockBounds(geometry, dimensions, position = "model_centered",
|
|
|
763
1523
|
throw new RangeError("Fixed stock offset must be finite.");
|
|
764
1524
|
}
|
|
765
1525
|
const part = partBounds(geometry);
|
|
766
|
-
const center = part.getCenter(new
|
|
1526
|
+
const center = part.getCenter(new Vector33());
|
|
767
1527
|
const z = fixedStockLowerZ(part, dimensions.z, position, positionOffset);
|
|
768
|
-
return new
|
|
769
|
-
new
|
|
770
|
-
new
|
|
771
|
-
).translate(new
|
|
1528
|
+
return new Box32(
|
|
1529
|
+
new Vector33(center.x - dimensions.x / 2, center.y - dimensions.y / 2, z),
|
|
1530
|
+
new Vector33(center.x + dimensions.x / 2, center.y + dimensions.y / 2, z + dimensions.z)
|
|
1531
|
+
).translate(new Vector33(offset.x, offset.y, offset.z));
|
|
772
1532
|
}
|
|
773
1533
|
function fixedCylinderStock(geometry, size, position = "model_centered", positionOffset = 0) {
|
|
774
1534
|
if (![size.diameter, size.length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
@@ -778,7 +1538,7 @@ function fixedCylinderStock(geometry, size, position = "model_centered", positio
|
|
|
778
1538
|
throw new RangeError("Fixed stock position offset must be finite.");
|
|
779
1539
|
}
|
|
780
1540
|
const part = partBounds(geometry);
|
|
781
|
-
const center = part.getCenter(new
|
|
1541
|
+
const center = part.getCenter(new Vector33());
|
|
782
1542
|
return {
|
|
783
1543
|
origin: {
|
|
784
1544
|
x: center.x,
|
|
@@ -804,13 +1564,13 @@ function cylinderStockGeometry(input) {
|
|
|
804
1564
|
if (![diameter, length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
805
1565
|
throw new RangeError("Cylinder stock diameter and length must be finite and positive.");
|
|
806
1566
|
}
|
|
807
|
-
const axis = new
|
|
1567
|
+
const axis = new Vector33(figure.axis.x, figure.axis.y, figure.axis.z);
|
|
808
1568
|
const norm = axis.length();
|
|
809
1569
|
if (!Number.isFinite(norm) || norm === 0) {
|
|
810
1570
|
throw new RangeError("Cylinder stock axis must be finite and non-zero.");
|
|
811
1571
|
}
|
|
812
1572
|
axis.divideScalar(norm);
|
|
813
|
-
const center = axis.clone().multiplyScalar(length / 2).add(new
|
|
1573
|
+
const center = axis.clone().multiplyScalar(length / 2).add(new Vector33(origin.x, origin.y, origin.z));
|
|
814
1574
|
return new CylinderGeometry(diameter / 2, diameter / 2, length, CYLINDER_SEGMENTS).applyQuaternion(new Quaternion().setFromUnitVectors(CYLINDER_AXIS, axis)).translate(center.x, center.y, center.z);
|
|
815
1575
|
}
|
|
816
1576
|
function boxStockGeometry(input) {
|
|
@@ -838,12 +1598,12 @@ function orientedBoxStockGeometry(figure) {
|
|
|
838
1598
|
)) {
|
|
839
1599
|
throw new RangeError("Box stock frame and corners must be finite.");
|
|
840
1600
|
}
|
|
841
|
-
const size = new
|
|
1601
|
+
const size = new Vector33(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
|
|
842
1602
|
if (size.toArray().some((value) => !(value > 0))) {
|
|
843
1603
|
throw new RangeError("Box stock upper corner must be above its lower corner on every axis.");
|
|
844
1604
|
}
|
|
845
|
-
const z = new
|
|
846
|
-
const x = new
|
|
1605
|
+
const z = new Vector33(axis.x, axis.y, axis.z).normalize();
|
|
1606
|
+
const x = new Vector33(refDirection.x, refDirection.y, refDirection.z);
|
|
847
1607
|
const reference = x.length();
|
|
848
1608
|
x.addScaledVector(z, -x.dot(z));
|
|
849
1609
|
if (!(z.lengthSq() > 0 && x.length() > 1e-9 * reference)) {
|
|
@@ -852,11 +1612,11 @@ function orientedBoxStockGeometry(figure) {
|
|
|
852
1612
|
);
|
|
853
1613
|
}
|
|
854
1614
|
x.normalize();
|
|
855
|
-
const y = new
|
|
856
|
-
return new BoxGeometry(size.x, size.y, size.z).translate((lower.x + upper.x) / 2, (lower.y + upper.y) / 2, (lower.z + upper.z) / 2).applyMatrix4(new
|
|
1615
|
+
const y = new Vector33().crossVectors(z, x);
|
|
1616
|
+
return new BoxGeometry(size.x, size.y, size.z).translate((lower.x + upper.x) / 2, (lower.y + upper.y) / 2, (lower.z + upper.z) / 2).applyMatrix4(new Matrix42().makeBasis(x, y, z).setPosition(location.x, location.y, location.z));
|
|
857
1617
|
}
|
|
858
1618
|
function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
859
|
-
const padding = typeof allowance === "number" ? new
|
|
1619
|
+
const padding = typeof allowance === "number" ? new Vector33(allowance, allowance, allowance) : "wall" in allowance ? new Vector33(allowance.wall, allowance.wall, allowance.floor) : new Vector33(allowance.x, allowance.y, allowance.z);
|
|
860
1620
|
if (padding.toArray().some((value) => !Number.isFinite(value) || value < 0)) {
|
|
861
1621
|
throw new RangeError("Stock allowance must be finite and non-negative.");
|
|
862
1622
|
}
|
|
@@ -864,8 +1624,8 @@ function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 })
|
|
|
864
1624
|
throw new RangeError("Stock offset must be finite.");
|
|
865
1625
|
}
|
|
866
1626
|
const box = partBounds(geometry);
|
|
867
|
-
box.expandByVector(padding).translate(new
|
|
868
|
-
const size = box.getSize(new
|
|
1627
|
+
box.expandByVector(padding).translate(new Vector33(offset.x, offset.y, offset.z));
|
|
1628
|
+
const size = box.getSize(new Vector33());
|
|
869
1629
|
if (size.toArray().some((value) => !Number.isFinite(value) || value <= 0)) {
|
|
870
1630
|
throw new RangeError("Stock dimensions must be finite and positive.");
|
|
871
1631
|
}
|
|
@@ -875,8 +1635,8 @@ function partBounds(geometry) {
|
|
|
875
1635
|
const position = geometry.getAttribute("position");
|
|
876
1636
|
if (!position || position.count === 0)
|
|
877
1637
|
throw new RangeError("Stock needs non-empty part geometry.");
|
|
878
|
-
const box = new
|
|
879
|
-
const point = new
|
|
1638
|
+
const box = new Box32();
|
|
1639
|
+
const point = new Vector33();
|
|
880
1640
|
for (let index = 0; index < position.count; index += 1) {
|
|
881
1641
|
point.fromBufferAttribute(position, index);
|
|
882
1642
|
if (!Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
|
|
@@ -887,10 +1647,10 @@ function partBounds(geometry) {
|
|
|
887
1647
|
return box;
|
|
888
1648
|
}
|
|
889
1649
|
function createStock(geometry, legacyWorkpiece = false, onEdges) {
|
|
890
|
-
const material = new
|
|
891
|
-
const edgeMaterial = new
|
|
892
|
-
const mesh = new
|
|
893
|
-
const object = new
|
|
1650
|
+
const material = new MeshLambertMaterial2({ transparent: true, depthWrite: false });
|
|
1651
|
+
const edgeMaterial = new LineBasicMaterial2({ transparent: true, depthWrite: false });
|
|
1652
|
+
const mesh = new Mesh3(geometry, material);
|
|
1653
|
+
const object = new Group2();
|
|
894
1654
|
object.userData[STOCK_OBJECT] = true;
|
|
895
1655
|
mesh.renderOrder = legacyWorkpiece ? 2 : 5;
|
|
896
1656
|
mesh.raycast = () => {
|
|
@@ -907,9 +1667,9 @@ function createStock(geometry, legacyWorkpiece = false, onEdges) {
|
|
|
907
1667
|
let building = null;
|
|
908
1668
|
let shown = false;
|
|
909
1669
|
const attach = (positions) => {
|
|
910
|
-
const outline = new
|
|
911
|
-
outline.setAttribute("position", new
|
|
912
|
-
edges = new
|
|
1670
|
+
const outline = new BufferGeometry3();
|
|
1671
|
+
outline.setAttribute("position", new Float32BufferAttribute3(positions, 3));
|
|
1672
|
+
edges = new LineSegments2(outline, edgeMaterial);
|
|
913
1673
|
edges.renderOrder = legacyWorkpiece ? 1 : 6;
|
|
914
1674
|
edges.raycast = () => {
|
|
915
1675
|
};
|
|
@@ -953,8 +1713,8 @@ function createStock(geometry, legacyWorkpiece = false, onEdges) {
|
|
|
953
1713
|
}
|
|
954
1714
|
|
|
955
1715
|
// src/stock.tsx
|
|
956
|
-
import { jsx as
|
|
957
|
-
var Stock = ({ geometry, glb, box, cylinder, ...appearance }) => geometry ? /* @__PURE__ */
|
|
1716
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1717
|
+
var Stock = ({ geometry, glb, box, cylinder, ...appearance }) => geometry ? /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance }) : glb ? /* @__PURE__ */ jsx5(GlbStock, { glb, ...appearance }) : /* @__PURE__ */ jsx5(ShapeStock, { box, cylinder, ...appearance });
|
|
958
1718
|
var GeometryStock = ({
|
|
959
1719
|
geometry,
|
|
960
1720
|
renderStyle = "overlay",
|
|
@@ -966,13 +1726,13 @@ var GeometryStock = ({
|
|
|
966
1726
|
edgeOpacity = renderStyle === "legacy-workpiece" ? 0.5 : 0.75,
|
|
967
1727
|
showEdges = true
|
|
968
1728
|
}) => {
|
|
969
|
-
const invalidate =
|
|
970
|
-
const stock =
|
|
1729
|
+
const invalidate = useThree2((state) => state.invalidate);
|
|
1730
|
+
const stock = useMemo2(
|
|
971
1731
|
() => createStock(geometry, renderStyle === "legacy-workpiece", invalidate),
|
|
972
1732
|
[geometry, renderStyle, invalidate]
|
|
973
1733
|
);
|
|
974
|
-
|
|
975
|
-
|
|
1734
|
+
useEffect3(() => () => stock.dispose(), [stock]);
|
|
1735
|
+
useLayoutEffect2(() => {
|
|
976
1736
|
stock.material.color.setHex(color);
|
|
977
1737
|
stock.material.emissive.setHex(emissive);
|
|
978
1738
|
if (stock.material.flatShading !== flatShading) {
|
|
@@ -1000,20 +1760,20 @@ var GeometryStock = ({
|
|
|
1000
1760
|
showEdges,
|
|
1001
1761
|
stock
|
|
1002
1762
|
]);
|
|
1003
|
-
return /* @__PURE__ */
|
|
1763
|
+
return /* @__PURE__ */ jsx5("primitive", { object: stock.object, dispose: null });
|
|
1004
1764
|
};
|
|
1005
1765
|
var GlbStock = ({
|
|
1006
1766
|
glb,
|
|
1007
1767
|
retainPrevious = false,
|
|
1008
1768
|
...appearance
|
|
1009
1769
|
}) => {
|
|
1010
|
-
const buffer =
|
|
1770
|
+
const buffer = useMemo2(() => createStockMeshBuffer(), []);
|
|
1011
1771
|
const [decoded, setDecoded] = useState2(null);
|
|
1012
|
-
|
|
1013
|
-
|
|
1772
|
+
useEffect3(() => () => buffer.dispose(), [buffer]);
|
|
1773
|
+
useLayoutEffect2(() => {
|
|
1014
1774
|
buffer.commit(decoded?.geometries ?? []);
|
|
1015
1775
|
}, [buffer, decoded]);
|
|
1016
|
-
|
|
1776
|
+
useEffect3(
|
|
1017
1777
|
() => buffer.request(
|
|
1018
1778
|
() => parseStockGlb(glb),
|
|
1019
1779
|
(geometries) => {
|
|
@@ -1030,7 +1790,7 @@ var GlbStock = ({
|
|
|
1030
1790
|
);
|
|
1031
1791
|
if (!decoded || !retainPrevious && decoded.source !== glb) return null;
|
|
1032
1792
|
if (decoded.error) throw decoded.error;
|
|
1033
|
-
return decoded.geometries?.map((geometry) => /* @__PURE__ */
|
|
1793
|
+
return decoded.geometries?.map((geometry) => /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance }, geometry.uuid));
|
|
1034
1794
|
};
|
|
1035
1795
|
var keepNonFinite = (_key, value) => typeof value === "number" && !Number.isFinite(value) ? String(value) : value;
|
|
1036
1796
|
var restoreNonFinite = (_key, value) => value === "NaN" || value === "Infinity" || value === "-Infinity" ? Number(value) : value;
|
|
@@ -1061,28 +1821,28 @@ var ShapeStock = ({
|
|
|
1061
1821
|
},
|
|
1062
1822
|
keepNonFinite
|
|
1063
1823
|
);
|
|
1064
|
-
const geometry =
|
|
1824
|
+
const geometry = useMemo2(() => {
|
|
1065
1825
|
const options = JSON.parse(figure, restoreNonFinite);
|
|
1066
1826
|
const input = partGeometry ? { ...options, partGeometry } : options;
|
|
1067
1827
|
return isBox ? boxStockGeometry(input) : cylinderStockGeometry(input);
|
|
1068
1828
|
}, [figure, isBox, partGeometry]);
|
|
1069
|
-
|
|
1070
|
-
return /* @__PURE__ */
|
|
1829
|
+
useEffect3(() => () => geometry.dispose(), [geometry]);
|
|
1830
|
+
return /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance });
|
|
1071
1831
|
};
|
|
1072
1832
|
|
|
1073
1833
|
// src/banana.tsx
|
|
1074
1834
|
import { useGLTF } from "@react-three/drei";
|
|
1075
|
-
import { useThree as
|
|
1076
|
-
import { useLayoutEffect as
|
|
1835
|
+
import { useThree as useThree3 } from "@react-three/fiber";
|
|
1836
|
+
import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo3, useRef as useRef2 } from "react";
|
|
1077
1837
|
|
|
1078
1838
|
// src/render/banana.ts
|
|
1079
|
-
import { Vector3 as
|
|
1839
|
+
import { Vector3 as Vector34 } from "three";
|
|
1080
1840
|
var BANANA_GAP = 0.1;
|
|
1081
1841
|
function bananaPosition(part, banana) {
|
|
1082
|
-
const size = part.getSize(new
|
|
1842
|
+
const size = part.getSize(new Vector34());
|
|
1083
1843
|
const reach = Math.max(size.x, size.y, size.z);
|
|
1084
|
-
const centre = part.getCenter(new
|
|
1085
|
-
return new
|
|
1844
|
+
const centre = part.getCenter(new Vector34());
|
|
1845
|
+
return new Vector34(
|
|
1086
1846
|
part.max.x + reach * BANANA_GAP - banana.min.x,
|
|
1087
1847
|
centre.y - (banana.min.y + banana.max.y) / 2,
|
|
1088
1848
|
part.min.z - banana.min.z
|
|
@@ -1093,16 +1853,16 @@ function bananaFrameBounds(part, banana, position) {
|
|
|
1093
1853
|
}
|
|
1094
1854
|
|
|
1095
1855
|
// src/banana.tsx
|
|
1096
|
-
import { jsx as
|
|
1856
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1097
1857
|
var BANANA_MODEL_URL = new URL("./assets/banana.glb", import.meta.url).href;
|
|
1098
1858
|
var SKIN = "#e8b530";
|
|
1099
1859
|
var FURNITURE = { [EXCLUDE_FROM_FRAME]: true };
|
|
1100
1860
|
var Banana = ({ onPlaced }) => {
|
|
1101
1861
|
const part = useContentBox();
|
|
1102
|
-
const invalidate =
|
|
1862
|
+
const invalidate = useThree3((state) => state.invalidate);
|
|
1103
1863
|
const gltf = useGLTF(BANANA_MODEL_URL);
|
|
1104
|
-
const group =
|
|
1105
|
-
const geometry =
|
|
1864
|
+
const group = useRef2(null);
|
|
1865
|
+
const geometry = useMemo3(() => {
|
|
1106
1866
|
let found = null;
|
|
1107
1867
|
gltf.scene.traverse((object) => {
|
|
1108
1868
|
const mesh = object;
|
|
@@ -1117,7 +1877,7 @@ var Banana = ({ onPlaced }) => {
|
|
|
1117
1877
|
own.computeBoundingBox();
|
|
1118
1878
|
return own;
|
|
1119
1879
|
}, [gltf]);
|
|
1120
|
-
|
|
1880
|
+
useLayoutEffect3(() => {
|
|
1121
1881
|
const node = group.current;
|
|
1122
1882
|
const own = geometry?.boundingBox;
|
|
1123
1883
|
if (!node || !own || part.isEmpty()) return;
|
|
@@ -1129,7 +1889,7 @@ var Banana = ({ onPlaced }) => {
|
|
|
1129
1889
|
invalidate();
|
|
1130
1890
|
}, [geometry, invalidate, onPlaced, part]);
|
|
1131
1891
|
if (!geometry) return null;
|
|
1132
|
-
return /* @__PURE__ */
|
|
1892
|
+
return /* @__PURE__ */ jsx6("group", { ref: group, userData: FURNITURE, visible: false, children: /* @__PURE__ */ jsx6("mesh", { geometry, children: /* @__PURE__ */ jsx6("meshStandardMaterial", { color: SKIN, roughness: 0.85, metalness: 0 }) }) });
|
|
1133
1893
|
};
|
|
1134
1894
|
|
|
1135
1895
|
// src/render/direction-highlights.ts
|
|
@@ -1148,17 +1908,17 @@ function directionHighlights(model, activeDirection = null) {
|
|
|
1148
1908
|
|
|
1149
1909
|
// src/primitives.tsx
|
|
1150
1910
|
import { GizmoHelper } from "@react-three/drei";
|
|
1151
|
-
import { useThree as
|
|
1152
|
-
import { useEffect as
|
|
1911
|
+
import { useThree as useThree4 } from "@react-three/fiber";
|
|
1912
|
+
import { useEffect as useEffect4, useMemo as useMemo4, useRef as useRef3, useState as useState3 } from "react";
|
|
1153
1913
|
|
|
1154
1914
|
// src/render/grid.ts
|
|
1155
|
-
import { BufferGeometry as
|
|
1915
|
+
import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute4, Vector3 as Vector35 } from "three";
|
|
1156
1916
|
var STEPS = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1e3];
|
|
1157
1917
|
var TARGET_CELLS = 10;
|
|
1158
1918
|
var OVERHANG = 1.6;
|
|
1159
1919
|
function gridSpec(box) {
|
|
1160
|
-
const size = box.getSize(new
|
|
1161
|
-
const center = box.getCenter(new
|
|
1920
|
+
const size = box.getSize(new Vector35());
|
|
1921
|
+
const center = box.getCenter(new Vector35());
|
|
1162
1922
|
const largest = Math.max(size.x, size.y, 1e-6);
|
|
1163
1923
|
const ideal = largest / TARGET_CELLS;
|
|
1164
1924
|
const step = [...STEPS].reverse().find((candidate) => candidate <= ideal) ?? STEPS[0];
|
|
@@ -1184,8 +1944,8 @@ function gridGeometry(spec) {
|
|
|
1184
1944
|
z
|
|
1185
1945
|
);
|
|
1186
1946
|
}
|
|
1187
|
-
const geometry = new
|
|
1188
|
-
geometry.setAttribute("position", new
|
|
1947
|
+
const geometry = new BufferGeometry4();
|
|
1948
|
+
geometry.setAttribute("position", new Float32BufferAttribute4(positions, 3));
|
|
1189
1949
|
return geometry;
|
|
1190
1950
|
}
|
|
1191
1951
|
function gridFor(box, options = {}) {
|
|
@@ -1202,26 +1962,26 @@ function gridFor(box, options = {}) {
|
|
|
1202
1962
|
}
|
|
1203
1963
|
|
|
1204
1964
|
// src/primitives.tsx
|
|
1205
|
-
import { jsx as
|
|
1965
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1206
1966
|
var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1207
1967
|
var Grid = ({ step, extent, color, opacity = 0.35 }) => {
|
|
1208
1968
|
const theme = resolveTheme();
|
|
1209
1969
|
const box = useContentBox();
|
|
1210
|
-
const geometry =
|
|
1211
|
-
|
|
1970
|
+
const geometry = useMemo4(() => gridFor(box, { step, extent }), [box, extent, step]);
|
|
1971
|
+
useEffect4(() => () => geometry?.dispose(), [geometry]);
|
|
1212
1972
|
if (!geometry) return null;
|
|
1213
|
-
return /* @__PURE__ */
|
|
1973
|
+
return /* @__PURE__ */ jsx7("lineSegments", { geometry, renderOrder: -1, raycast: () => null, userData: FURNITURE2, children: /* @__PURE__ */ jsx7("lineBasicMaterial", { color: color ?? theme.cubeEdge, transparent: true, opacity }) });
|
|
1214
1974
|
};
|
|
1215
|
-
var Axes = ({ size = 25 }) => /* @__PURE__ */
|
|
1975
|
+
var Axes = ({ size = 25 }) => /* @__PURE__ */ jsx7("axesHelper", { args: [size], userData: FURNITURE2, raycast: () => null });
|
|
1216
1976
|
var ViewCube = ({
|
|
1217
1977
|
alignment = "top-right",
|
|
1218
1978
|
margin = [80, 80],
|
|
1219
1979
|
theme,
|
|
1220
1980
|
onViewChange
|
|
1221
1981
|
}) => {
|
|
1222
|
-
const resolved =
|
|
1223
|
-
const zones =
|
|
1224
|
-
return /* @__PURE__ */
|
|
1982
|
+
const resolved = useMemo4(() => resolveTheme(theme), [theme]);
|
|
1983
|
+
const zones = useMemo4(() => cubeZones(), []);
|
|
1984
|
+
return /* @__PURE__ */ jsx7(GizmoHelper, { alignment, margin, children: /* @__PURE__ */ jsx7(CubePanels, { zones, theme: resolved, onViewChange }) });
|
|
1225
1985
|
};
|
|
1226
1986
|
var CubePanels = ({
|
|
1227
1987
|
zones,
|
|
@@ -1229,15 +1989,15 @@ var CubePanels = ({
|
|
|
1229
1989
|
onViewChange
|
|
1230
1990
|
}) => {
|
|
1231
1991
|
const controls = useViewerControls();
|
|
1232
|
-
const invalidate =
|
|
1233
|
-
const groupRef =
|
|
1992
|
+
const invalidate = useThree4((state) => state.invalidate);
|
|
1993
|
+
const groupRef = useRef3(null);
|
|
1234
1994
|
const [hovered, setHovered] = useState3(null);
|
|
1235
|
-
const panels =
|
|
1995
|
+
const panels = useMemo4(
|
|
1236
1996
|
() => zones.map((zone) => ({ zone, geometry: panelGeometry(zone) })),
|
|
1237
1997
|
[zones]
|
|
1238
1998
|
);
|
|
1239
|
-
const outline =
|
|
1240
|
-
const labels =
|
|
1999
|
+
const outline = useMemo4(() => cubeOutlineGeometry(zones), [zones]);
|
|
2000
|
+
const labels = useMemo4(
|
|
1241
2001
|
() => zones.filter((zone) => zone.kind === "face").map((zone) => ({
|
|
1242
2002
|
zone,
|
|
1243
2003
|
geometry: labelGeometry(zone),
|
|
@@ -1245,7 +2005,7 @@ var CubePanels = ({
|
|
|
1245
2005
|
})),
|
|
1246
2006
|
[theme.cubeLabel, zones]
|
|
1247
2007
|
);
|
|
1248
|
-
|
|
2008
|
+
useEffect4(
|
|
1249
2009
|
() => () => {
|
|
1250
2010
|
for (const panel of panels) panel.geometry.dispose();
|
|
1251
2011
|
for (const label of labels) {
|
|
@@ -1264,8 +2024,8 @@ var CubePanels = ({
|
|
|
1264
2024
|
setHovered(name);
|
|
1265
2025
|
invalidate();
|
|
1266
2026
|
};
|
|
1267
|
-
return /* @__PURE__ */
|
|
1268
|
-
panels.map(({ zone, geometry }) => /* @__PURE__ */
|
|
2027
|
+
return /* @__PURE__ */ jsxs4("group", { ref: groupRef, scale: 38, children: [
|
|
2028
|
+
panels.map(({ zone, geometry }) => /* @__PURE__ */ jsx7(
|
|
1269
2029
|
"mesh",
|
|
1270
2030
|
{
|
|
1271
2031
|
geometry,
|
|
@@ -1278,7 +2038,7 @@ var CubePanels = ({
|
|
|
1278
2038
|
event.stopPropagation();
|
|
1279
2039
|
choose(zone);
|
|
1280
2040
|
},
|
|
1281
|
-
children: /* @__PURE__ */
|
|
2041
|
+
children: /* @__PURE__ */ jsx7(
|
|
1282
2042
|
"meshLambertMaterial",
|
|
1283
2043
|
{
|
|
1284
2044
|
color: hovered === zone.name ? theme.hover : theme.cube,
|
|
@@ -1290,27 +2050,27 @@ var CubePanels = ({
|
|
|
1290
2050
|
},
|
|
1291
2051
|
zone.name
|
|
1292
2052
|
)),
|
|
1293
|
-
labels.map(({ zone, geometry, texture }) => /* @__PURE__ */
|
|
1294
|
-
/* @__PURE__ */
|
|
1295
|
-
/* @__PURE__ */
|
|
1296
|
-
/* @__PURE__ */
|
|
2053
|
+
labels.map(({ zone, geometry, texture }) => /* @__PURE__ */ jsx7("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsx7("meshBasicMaterial", { map: texture, transparent: true }) }, `${zone.name}-label`)),
|
|
2054
|
+
/* @__PURE__ */ jsx7("lineSegments", { geometry: outline, raycast: () => null, children: /* @__PURE__ */ jsx7("lineBasicMaterial", { color: theme.cubeEdge }) }),
|
|
2055
|
+
/* @__PURE__ */ jsx7("ambientLight", { intensity: 1.4 }),
|
|
2056
|
+
/* @__PURE__ */ jsx7("directionalLight", { position: [viewVector("top-front-right").x, 1, 2], intensity: 1.1 })
|
|
1297
2057
|
] });
|
|
1298
2058
|
};
|
|
1299
2059
|
|
|
1300
2060
|
// src/direction-arrows.tsx
|
|
1301
|
-
import { useThree as
|
|
1302
|
-
import { useEffect as
|
|
1303
|
-
import { Quaternion as Quaternion2, Vector3 as
|
|
2061
|
+
import { useThree as useThree5 } from "@react-three/fiber";
|
|
2062
|
+
import { useEffect as useEffect5, useMemo as useMemo5 } from "react";
|
|
2063
|
+
import { Quaternion as Quaternion2, Vector3 as Vector37 } from "three";
|
|
1304
2064
|
|
|
1305
2065
|
// src/render/directions.ts
|
|
1306
|
-
import { Vector3 as
|
|
2066
|
+
import { Vector3 as Vector36 } from "three";
|
|
1307
2067
|
var LENGTH = 0.33;
|
|
1308
2068
|
var GAP = 0.2;
|
|
1309
2069
|
function arrowPlacement(direction, box) {
|
|
1310
|
-
const center = box.getCenter(new
|
|
1311
|
-
const half = box.getSize(new
|
|
2070
|
+
const center = box.getCenter(new Vector36());
|
|
2071
|
+
const half = box.getSize(new Vector36()).multiplyScalar(0.5);
|
|
1312
2072
|
const radius = half.length() || 1;
|
|
1313
|
-
const axis = new
|
|
2073
|
+
const axis = new Vector36(direction.x, direction.y, direction.z);
|
|
1314
2074
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
1315
2075
|
axis.normalize();
|
|
1316
2076
|
let exit = Number.POSITIVE_INFINITY;
|
|
@@ -1326,7 +2086,7 @@ function arrowPlacement(direction, box) {
|
|
|
1326
2086
|
}
|
|
1327
2087
|
|
|
1328
2088
|
// src/direction-arrows.tsx
|
|
1329
|
-
import { jsx as
|
|
2089
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1330
2090
|
var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1331
2091
|
var DirectionArrows = ({
|
|
1332
2092
|
directions,
|
|
@@ -1339,9 +2099,9 @@ var DirectionArrows = ({
|
|
|
1339
2099
|
visible = true
|
|
1340
2100
|
}) => {
|
|
1341
2101
|
const box = useContentBox();
|
|
1342
|
-
const resolved =
|
|
1343
|
-
const geometry =
|
|
1344
|
-
|
|
2102
|
+
const resolved = useMemo5(() => resolveTheme(theme), [theme]);
|
|
2103
|
+
const geometry = useMemo5(() => arrowGeometry(), []);
|
|
2104
|
+
useEffect5(() => () => geometry.dispose(), [geometry]);
|
|
1345
2105
|
const shown = shownDirection === void 0 ? activeDirection : shownDirection;
|
|
1346
2106
|
const drawn = (index) => {
|
|
1347
2107
|
if (shown === null) return true;
|
|
@@ -1353,10 +2113,10 @@ var DirectionArrows = ({
|
|
|
1353
2113
|
// Excluded from framing: the arrows are deliberately placed outside the
|
|
1354
2114
|
// part, so a Fit that measured them would frame the arrows and leave the
|
|
1355
2115
|
// part small in the middle of them.
|
|
1356
|
-
/* @__PURE__ */
|
|
2116
|
+
/* @__PURE__ */ jsxs5("group", { userData: FURNITURE3, children: [
|
|
1357
2117
|
directions.map((direction, index) => {
|
|
1358
2118
|
if (!drawn(index)) return null;
|
|
1359
|
-
return /* @__PURE__ */
|
|
2119
|
+
return /* @__PURE__ */ jsx8(
|
|
1360
2120
|
Arrow,
|
|
1361
2121
|
{
|
|
1362
2122
|
direction,
|
|
@@ -1369,7 +2129,7 @@ var DirectionArrows = ({
|
|
|
1369
2129
|
`candidate-${index}`
|
|
1370
2130
|
);
|
|
1371
2131
|
}),
|
|
1372
|
-
namedDirections.map((named) => /* @__PURE__ */
|
|
2132
|
+
namedDirections.map((named) => /* @__PURE__ */ jsx8(
|
|
1373
2133
|
Arrow,
|
|
1374
2134
|
{
|
|
1375
2135
|
direction: named.direction,
|
|
@@ -1381,7 +2141,7 @@ var DirectionArrows = ({
|
|
|
1381
2141
|
},
|
|
1382
2142
|
`named-${named.index}`
|
|
1383
2143
|
)),
|
|
1384
|
-
previewDirection ? /* @__PURE__ */
|
|
2144
|
+
previewDirection ? /* @__PURE__ */ jsx8(
|
|
1385
2145
|
Arrow,
|
|
1386
2146
|
{
|
|
1387
2147
|
direction: previewDirection,
|
|
@@ -1396,12 +2156,12 @@ var DirectionArrows = ({
|
|
|
1396
2156
|
);
|
|
1397
2157
|
};
|
|
1398
2158
|
var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }) => {
|
|
1399
|
-
const invalidate =
|
|
1400
|
-
const { tip, length, quaternion } =
|
|
2159
|
+
const invalidate = useThree5((state) => state.invalidate);
|
|
2160
|
+
const { tip, length, quaternion } = useMemo5(() => {
|
|
1401
2161
|
const placement = arrowPlacement(direction, box);
|
|
1402
2162
|
const aim = new Quaternion2().setFromUnitVectors(
|
|
1403
2163
|
ARROW_AXIS,
|
|
1404
|
-
new
|
|
2164
|
+
new Vector37(direction.x, direction.y, direction.z).normalize()
|
|
1405
2165
|
);
|
|
1406
2166
|
return { tip: placement.tip, length: placement.length, quaternion: aim };
|
|
1407
2167
|
}, [box, direction]);
|
|
@@ -1412,23 +2172,23 @@ var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }
|
|
|
1412
2172
|
invalidate();
|
|
1413
2173
|
}
|
|
1414
2174
|
} : {};
|
|
1415
|
-
return /* @__PURE__ */
|
|
2175
|
+
return /* @__PURE__ */ jsx8("group", { position: tip, quaternion, scale: length, renderOrder: onTop ? 10 : 3, children: /* @__PURE__ */ jsx8(ArrowMesh, { geometry, color, opacity, onTop, events: press }) });
|
|
1416
2176
|
};
|
|
1417
2177
|
|
|
1418
2178
|
// src/section-tool.tsx
|
|
1419
|
-
import { useFrame, useThree as
|
|
1420
|
-
import { useEffect as
|
|
2179
|
+
import { useFrame as useFrame2, useThree as useThree6 } from "@react-three/fiber";
|
|
2180
|
+
import { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef4, useState as useState4, useSyncExternalStore } from "react";
|
|
1421
2181
|
import {
|
|
1422
|
-
BufferGeometry as
|
|
1423
|
-
DoubleSide,
|
|
2182
|
+
BufferGeometry as BufferGeometry5,
|
|
2183
|
+
DoubleSide as DoubleSide3,
|
|
1424
2184
|
Quaternion as Quaternion3,
|
|
1425
2185
|
Raycaster,
|
|
1426
2186
|
Vector2,
|
|
1427
|
-
Vector3 as
|
|
2187
|
+
Vector3 as Vector38
|
|
1428
2188
|
} from "three";
|
|
1429
|
-
import { Fragment, jsx as
|
|
2189
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1430
2190
|
var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1431
|
-
var Z = new
|
|
2191
|
+
var Z = new Vector38(0, 0, 1);
|
|
1432
2192
|
var AXIS_ROTATIONS = {
|
|
1433
2193
|
x: [0, Math.PI / 2, 0],
|
|
1434
2194
|
y: [Math.PI / 2, 0, 0],
|
|
@@ -1440,12 +2200,12 @@ var SectionTool = ({ theme }) => {
|
|
|
1440
2200
|
const store = useSectionStore();
|
|
1441
2201
|
const section = useSyncExternalStore(store.subscribe, store.get, store.get);
|
|
1442
2202
|
const box = useContentBox();
|
|
1443
|
-
const resolved =
|
|
1444
|
-
|
|
2203
|
+
const resolved = useMemo6(() => resolveTheme(theme), [theme]);
|
|
2204
|
+
useEffect6(() => {
|
|
1445
2205
|
store.setEngaged(true);
|
|
1446
2206
|
return () => store.setEngaged(false);
|
|
1447
2207
|
}, [store]);
|
|
1448
|
-
|
|
2208
|
+
useEffect6(() => {
|
|
1449
2209
|
if (!section) return void 0;
|
|
1450
2210
|
const clear = (event) => {
|
|
1451
2211
|
if (event.key === "Escape") store.set(null);
|
|
@@ -1454,43 +2214,43 @@ var SectionTool = ({ theme }) => {
|
|
|
1454
2214
|
return () => window.removeEventListener("keydown", clear);
|
|
1455
2215
|
}, [section, store]);
|
|
1456
2216
|
if (box.isEmpty()) return null;
|
|
1457
|
-
return /* @__PURE__ */
|
|
2217
|
+
return /* @__PURE__ */ jsx9("group", { userData: FURNITURE4, children: section ? /* @__PURE__ */ jsx9(CutSheet, { box, section, theme: resolved }) : /* @__PURE__ */ jsx9(Picker, { box, theme: resolved }) });
|
|
1458
2218
|
};
|
|
1459
2219
|
var useSquare = () => {
|
|
1460
|
-
const square =
|
|
1461
|
-
() => new
|
|
1462
|
-
new
|
|
1463
|
-
new
|
|
1464
|
-
new
|
|
1465
|
-
new
|
|
2220
|
+
const square = useMemo6(
|
|
2221
|
+
() => new BufferGeometry5().setFromPoints([
|
|
2222
|
+
new Vector38(-1, 1, 0),
|
|
2223
|
+
new Vector38(1, 1, 0),
|
|
2224
|
+
new Vector38(1, -1, 0),
|
|
2225
|
+
new Vector38(-1, -1, 0)
|
|
1466
2226
|
]),
|
|
1467
2227
|
[]
|
|
1468
2228
|
);
|
|
1469
|
-
|
|
2229
|
+
useEffect6(() => () => square.dispose(), [square]);
|
|
1470
2230
|
return square;
|
|
1471
2231
|
};
|
|
1472
2232
|
var Picker = ({ box, theme }) => {
|
|
1473
2233
|
const store = useSectionStore();
|
|
1474
|
-
const camera =
|
|
1475
|
-
const scene =
|
|
1476
|
-
const domElement =
|
|
1477
|
-
const invalidate =
|
|
2234
|
+
const camera = useThree6((state) => state.camera);
|
|
2235
|
+
const scene = useThree6((state) => state.scene);
|
|
2236
|
+
const domElement = useThree6((state) => state.gl.domElement);
|
|
2237
|
+
const invalidate = useThree6((state) => state.invalidate);
|
|
1478
2238
|
const isTap = useTapGuard();
|
|
1479
2239
|
const square = useSquare();
|
|
1480
|
-
const previewRef =
|
|
1481
|
-
const hover =
|
|
2240
|
+
const previewRef = useRef4(null);
|
|
2241
|
+
const hover = useRef4(null);
|
|
1482
2242
|
const [hoveredPlane, setHoveredPlane] = useState4(null);
|
|
1483
|
-
const span =
|
|
1484
|
-
const centre =
|
|
2243
|
+
const span = useMemo6(() => box.getSize(new Vector38()).length(), [box]);
|
|
2244
|
+
const centre = useMemo6(() => box.getCenter(new Vector38()), [box]);
|
|
1485
2245
|
const previewSize = span * PREVIEW_SCALE;
|
|
1486
|
-
|
|
2246
|
+
useEffect6(() => {
|
|
1487
2247
|
store.setPicking(true);
|
|
1488
2248
|
return () => store.setPicking(false);
|
|
1489
2249
|
}, [store]);
|
|
1490
|
-
|
|
2250
|
+
useEffect6(() => {
|
|
1491
2251
|
const raycaster = new Raycaster();
|
|
1492
2252
|
const pointer = new Vector2();
|
|
1493
|
-
const aim = new
|
|
2253
|
+
const aim = new Vector38();
|
|
1494
2254
|
const hide = () => {
|
|
1495
2255
|
if (!hover.current) return;
|
|
1496
2256
|
hover.current = null;
|
|
@@ -1537,7 +2297,7 @@ var Picker = ({ box, theme }) => {
|
|
|
1537
2297
|
}, [box, camera, domElement, invalidate, isTap, scene, span, store]);
|
|
1538
2298
|
const [planes, setPlanes] = useState4(() => axesPlanes(box, camera.position));
|
|
1539
2299
|
const key = planes.map((plane) => plane.sign).join();
|
|
1540
|
-
|
|
2300
|
+
useFrame2(() => {
|
|
1541
2301
|
const next = [camera.position.x, camera.position.y, camera.position.z].map((eye, axis) => eye - [centre.x, centre.y, centre.z][axis] < 0 ? -1 : 1).join();
|
|
1542
2302
|
if (next !== key) setPlanes(axesPlanes(box, camera.position));
|
|
1543
2303
|
});
|
|
@@ -1561,29 +2321,29 @@ var Picker = ({ box, theme }) => {
|
|
|
1561
2321
|
store.set(plane.options);
|
|
1562
2322
|
}
|
|
1563
2323
|
});
|
|
1564
|
-
return /* @__PURE__ */
|
|
1565
|
-
/* @__PURE__ */
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
/* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
2324
|
+
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
2325
|
+
/* @__PURE__ */ jsxs6("group", { ref: previewRef, visible: false, children: [
|
|
2326
|
+
/* @__PURE__ */ jsxs6("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
|
|
2327
|
+
/* @__PURE__ */ jsx9("planeGeometry", { args: [previewSize, previewSize] }),
|
|
2328
|
+
/* @__PURE__ */ jsx9(
|
|
1569
2329
|
"meshBasicMaterial",
|
|
1570
2330
|
{
|
|
1571
2331
|
color: theme.sectionOutline,
|
|
1572
2332
|
transparent: true,
|
|
1573
2333
|
opacity: 0.2,
|
|
1574
|
-
side:
|
|
2334
|
+
side: DoubleSide3,
|
|
1575
2335
|
depthWrite: false
|
|
1576
2336
|
}
|
|
1577
2337
|
)
|
|
1578
2338
|
] }),
|
|
1579
|
-
/* @__PURE__ */
|
|
2339
|
+
/* @__PURE__ */ jsx9(
|
|
1580
2340
|
"lineLoop",
|
|
1581
2341
|
{
|
|
1582
2342
|
geometry: square,
|
|
1583
2343
|
scale: previewSize / 2,
|
|
1584
2344
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
1585
2345
|
raycast: () => null,
|
|
1586
|
-
children: /* @__PURE__ */
|
|
2346
|
+
children: /* @__PURE__ */ jsx9(
|
|
1587
2347
|
"lineBasicMaterial",
|
|
1588
2348
|
{
|
|
1589
2349
|
color: theme.sectionOutline,
|
|
@@ -1596,33 +2356,33 @@ var Picker = ({ box, theme }) => {
|
|
|
1596
2356
|
}
|
|
1597
2357
|
)
|
|
1598
2358
|
] }),
|
|
1599
|
-
planes.map((plane) => /* @__PURE__ */
|
|
2359
|
+
planes.map((plane) => /* @__PURE__ */ jsxs6(
|
|
1600
2360
|
"group",
|
|
1601
2361
|
{
|
|
1602
2362
|
position: [plane.position.x, plane.position.y, plane.position.z],
|
|
1603
2363
|
rotation: AXIS_ROTATIONS[plane.axis],
|
|
1604
2364
|
children: [
|
|
1605
|
-
/* @__PURE__ */
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
/* @__PURE__ */
|
|
2365
|
+
/* @__PURE__ */ jsxs6("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, ...planeHandlers(plane), children: [
|
|
2366
|
+
/* @__PURE__ */ jsx9("planeGeometry", { args: [plane.size, plane.size] }),
|
|
2367
|
+
/* @__PURE__ */ jsx9(
|
|
1608
2368
|
"meshBasicMaterial",
|
|
1609
2369
|
{
|
|
1610
2370
|
color: AXIS_COLORS[plane.axis],
|
|
1611
2371
|
transparent: true,
|
|
1612
2372
|
opacity: hoveredPlane === plane.axis ? 0.45 : 0.3,
|
|
1613
|
-
side:
|
|
2373
|
+
side: DoubleSide3,
|
|
1614
2374
|
depthWrite: false
|
|
1615
2375
|
}
|
|
1616
2376
|
)
|
|
1617
2377
|
] }),
|
|
1618
|
-
/* @__PURE__ */
|
|
2378
|
+
/* @__PURE__ */ jsx9(
|
|
1619
2379
|
"lineLoop",
|
|
1620
2380
|
{
|
|
1621
2381
|
geometry: square,
|
|
1622
2382
|
scale: plane.size / 2,
|
|
1623
2383
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
1624
2384
|
raycast: () => null,
|
|
1625
|
-
children: /* @__PURE__ */
|
|
2385
|
+
children: /* @__PURE__ */ jsx9("lineBasicMaterial", { color: AXIS_COLORS[plane.axis], transparent: true, opacity: 0.7 })
|
|
1626
2386
|
}
|
|
1627
2387
|
)
|
|
1628
2388
|
]
|
|
@@ -1633,9 +2393,9 @@ var Picker = ({ box, theme }) => {
|
|
|
1633
2393
|
};
|
|
1634
2394
|
var CutSheet = ({ box, section, theme }) => {
|
|
1635
2395
|
const square = useSquare();
|
|
1636
|
-
const cut =
|
|
1637
|
-
const span =
|
|
1638
|
-
const placement =
|
|
2396
|
+
const cut = useMemo6(() => resolveSectionPlane(section, box), [box, section]);
|
|
2397
|
+
const span = useMemo6(() => box.getSize(new Vector38()).length(), [box]);
|
|
2398
|
+
const placement = useMemo6(() => {
|
|
1639
2399
|
if (!cut) return null;
|
|
1640
2400
|
const position = sectionAnchor(box, cut.plane).addScaledVector(
|
|
1641
2401
|
cut.plane.normal,
|
|
@@ -1645,28 +2405,28 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
1645
2405
|
}, [box, cut, span]);
|
|
1646
2406
|
if (!placement) return null;
|
|
1647
2407
|
const size = span * OUTLINE_SCALE;
|
|
1648
|
-
return /* @__PURE__ */
|
|
1649
|
-
/* @__PURE__ */
|
|
1650
|
-
/* @__PURE__ */
|
|
1651
|
-
/* @__PURE__ */
|
|
2408
|
+
return /* @__PURE__ */ jsxs6("group", { position: placement.position, quaternion: placement.quaternion, children: [
|
|
2409
|
+
/* @__PURE__ */ jsxs6("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
|
|
2410
|
+
/* @__PURE__ */ jsx9("planeGeometry", { args: [size, size] }),
|
|
2411
|
+
/* @__PURE__ */ jsx9(
|
|
1652
2412
|
"meshBasicMaterial",
|
|
1653
2413
|
{
|
|
1654
2414
|
color: theme.sectionOutline,
|
|
1655
2415
|
transparent: true,
|
|
1656
2416
|
opacity: 0.1,
|
|
1657
|
-
side:
|
|
2417
|
+
side: DoubleSide3,
|
|
1658
2418
|
depthWrite: false
|
|
1659
2419
|
}
|
|
1660
2420
|
)
|
|
1661
2421
|
] }),
|
|
1662
|
-
/* @__PURE__ */
|
|
2422
|
+
/* @__PURE__ */ jsx9(
|
|
1663
2423
|
"lineLoop",
|
|
1664
2424
|
{
|
|
1665
2425
|
geometry: square,
|
|
1666
2426
|
scale: size / 2,
|
|
1667
2427
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
1668
2428
|
raycast: () => null,
|
|
1669
|
-
children: /* @__PURE__ */
|
|
2429
|
+
children: /* @__PURE__ */ jsx9(
|
|
1670
2430
|
"lineBasicMaterial",
|
|
1671
2431
|
{
|
|
1672
2432
|
color: theme.sectionOutline,
|
|
@@ -1683,32 +2443,32 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
1683
2443
|
|
|
1684
2444
|
// src/measure-tool.tsx
|
|
1685
2445
|
import { Html, Line } from "@react-three/drei";
|
|
1686
|
-
import { useFrame as
|
|
2446
|
+
import { useFrame as useFrame3, useThree as useThree7 } from "@react-three/fiber";
|
|
1687
2447
|
import {
|
|
1688
|
-
useEffect as
|
|
1689
|
-
useLayoutEffect as
|
|
1690
|
-
useMemo as
|
|
1691
|
-
useRef as
|
|
2448
|
+
useEffect as useEffect7,
|
|
2449
|
+
useLayoutEffect as useLayoutEffect4,
|
|
2450
|
+
useMemo as useMemo7,
|
|
2451
|
+
useRef as useRef5,
|
|
1692
2452
|
useState as useState5,
|
|
1693
2453
|
useSyncExternalStore as useSyncExternalStore2
|
|
1694
2454
|
} from "react";
|
|
1695
2455
|
import {
|
|
1696
|
-
Matrix4 as
|
|
2456
|
+
Matrix4 as Matrix43,
|
|
1697
2457
|
Raycaster as Raycaster2,
|
|
1698
2458
|
SphereGeometry,
|
|
1699
2459
|
Vector2 as Vector22,
|
|
1700
|
-
Vector3 as
|
|
2460
|
+
Vector3 as Vector310
|
|
1701
2461
|
} from "three";
|
|
1702
2462
|
|
|
1703
2463
|
// src/render/resample.ts
|
|
1704
|
-
import { Vector3 as
|
|
2464
|
+
import { Vector3 as Vector39 } from "three";
|
|
1705
2465
|
var ON_PLANE = 1e-7;
|
|
1706
2466
|
var STITCH = 1e-5;
|
|
1707
2467
|
var COLLINEAR = 1e-6;
|
|
1708
2468
|
function clipSegments(positions, plane) {
|
|
1709
2469
|
const out = [];
|
|
1710
|
-
const a = new
|
|
1711
|
-
const b = new
|
|
2470
|
+
const a = new Vector39();
|
|
2471
|
+
const b = new Vector39();
|
|
1712
2472
|
const count = Math.floor(positions.length / 6);
|
|
1713
2473
|
for (let i = 0; i < count; i += 1) {
|
|
1714
2474
|
a.set(positions[i * 6], positions[i * 6 + 1], positions[i * 6 + 2]);
|
|
@@ -1723,11 +2483,11 @@ function clipSegments(positions, plane) {
|
|
|
1723
2483
|
return Float32Array.from(out);
|
|
1724
2484
|
}
|
|
1725
2485
|
function sectionContour(positions, regionOf, plane) {
|
|
1726
|
-
const a = new
|
|
1727
|
-
const b = new
|
|
1728
|
-
const c = new
|
|
1729
|
-
const p = new
|
|
1730
|
-
const q = new
|
|
2486
|
+
const a = new Vector39();
|
|
2487
|
+
const b = new Vector39();
|
|
2488
|
+
const c = new Vector39();
|
|
2489
|
+
const p = new Vector39();
|
|
2490
|
+
const q = new Vector39();
|
|
1731
2491
|
const byRegion = /* @__PURE__ */ new Map();
|
|
1732
2492
|
const triangleCount2 = Math.floor(positions.length / 9);
|
|
1733
2493
|
for (let t = 0; t < triangleCount2; t += 1) {
|
|
@@ -1785,17 +2545,17 @@ function stitch(segments, out) {
|
|
|
1785
2545
|
return into.set(segments[o], segments[o + 1], segments[o + 2]);
|
|
1786
2546
|
};
|
|
1787
2547
|
const direction = (i, into) => {
|
|
1788
|
-
const s = new
|
|
1789
|
-
const e = new
|
|
2548
|
+
const s = new Vector39();
|
|
2549
|
+
const e = new Vector39();
|
|
1790
2550
|
point(i, 0, s);
|
|
1791
2551
|
point(i, 1, e);
|
|
1792
2552
|
return into.subVectors(e, s).normalize();
|
|
1793
2553
|
};
|
|
1794
2554
|
const consumed = new Uint8Array(count);
|
|
1795
|
-
const d = new
|
|
1796
|
-
const n = new
|
|
2555
|
+
const d = new Vector39();
|
|
2556
|
+
const n = new Vector39();
|
|
1797
2557
|
const next = (i, end) => {
|
|
1798
|
-
const at = point(i, end, new
|
|
2558
|
+
const at = point(i, end, new Vector39());
|
|
1799
2559
|
const list = ends.get(key(at.x, at.y, at.z)) ?? [];
|
|
1800
2560
|
if (list.length !== 2) return null;
|
|
1801
2561
|
const other = list.find((entry) => entry >> 1 !== i);
|
|
@@ -1824,8 +2584,8 @@ function stitch(segments, out) {
|
|
|
1824
2584
|
};
|
|
1825
2585
|
const back = walk(0);
|
|
1826
2586
|
const forward = walk(1);
|
|
1827
|
-
const s = point(back.index, back.end, new
|
|
1828
|
-
const e = point(forward.index, forward.end, new
|
|
2587
|
+
const s = point(back.index, back.end, new Vector39());
|
|
2588
|
+
const e = point(forward.index, forward.end, new Vector39());
|
|
1829
2589
|
out.push(s.x, s.y, s.z, e.x, e.y, e.z);
|
|
1830
2590
|
}
|
|
1831
2591
|
}
|
|
@@ -1833,8 +2593,8 @@ function pointInContour(point, plane, contour) {
|
|
|
1833
2593
|
const [u, v] = planeAxes(plane);
|
|
1834
2594
|
const px = point.dot(u);
|
|
1835
2595
|
const py = point.dot(v);
|
|
1836
|
-
const a = new
|
|
1837
|
-
const b = new
|
|
2596
|
+
const a = new Vector39();
|
|
2597
|
+
const b = new Vector39();
|
|
1838
2598
|
let inside = false;
|
|
1839
2599
|
const count = Math.floor(contour.length / 6);
|
|
1840
2600
|
for (let i = 0; i < count; i += 1) {
|
|
@@ -1851,15 +2611,15 @@ function pointInContour(point, plane, contour) {
|
|
|
1851
2611
|
return inside;
|
|
1852
2612
|
}
|
|
1853
2613
|
function capHit(ray, plane, contour) {
|
|
1854
|
-
const point = ray.intersectPlane(plane, new
|
|
2614
|
+
const point = ray.intersectPlane(plane, new Vector39());
|
|
1855
2615
|
if (!point) return null;
|
|
1856
2616
|
return pointInContour(point, plane, contour) ? point : null;
|
|
1857
2617
|
}
|
|
1858
2618
|
function planeAxes(plane) {
|
|
1859
2619
|
const n = plane.normal;
|
|
1860
|
-
const seed = Math.abs(n.x) < 0.9 ? new
|
|
1861
|
-
const u = new
|
|
1862
|
-
const v = new
|
|
2620
|
+
const seed = Math.abs(n.x) < 0.9 ? new Vector39(1, 0, 0) : new Vector39(0, 1, 0);
|
|
2621
|
+
const u = new Vector39().crossVectors(n, seed).normalize();
|
|
2622
|
+
const v = new Vector39().crossVectors(n, u).normalize();
|
|
1863
2623
|
return [u, v];
|
|
1864
2624
|
}
|
|
1865
2625
|
function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
@@ -1872,7 +2632,7 @@ function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
|
1872
2632
|
}
|
|
1873
2633
|
|
|
1874
2634
|
// src/measure-tool.tsx
|
|
1875
|
-
import { Fragment as
|
|
2635
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1876
2636
|
var FURNITURE5 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1877
2637
|
var MEASURE_LABEL_CLASS = "toolpath-measure-label";
|
|
1878
2638
|
var MeasureTool = ({
|
|
@@ -1886,26 +2646,26 @@ var MeasureTool = ({
|
|
|
1886
2646
|
}) => {
|
|
1887
2647
|
const store = useSectionStore();
|
|
1888
2648
|
const box = useContentBox();
|
|
1889
|
-
const resolved =
|
|
2649
|
+
const resolved = useMemo7(() => resolveTheme(theme), [theme]);
|
|
1890
2650
|
const picking = useSyncExternalStore2(store.subscribe, store.isPicking, store.isPicking);
|
|
1891
2651
|
const [held, setHeld] = useState5([]);
|
|
1892
2652
|
const controlled = measurements !== void 0;
|
|
1893
2653
|
const list = controlled ? measurements : held;
|
|
1894
2654
|
const [draft, setDraft] = useState5([]);
|
|
1895
|
-
|
|
2655
|
+
useEffect7(() => {
|
|
1896
2656
|
store.setEngaged(true);
|
|
1897
2657
|
return () => store.setEngaged(false);
|
|
1898
2658
|
}, [store]);
|
|
1899
|
-
|
|
2659
|
+
useEffect7(() => {
|
|
1900
2660
|
setDraft([]);
|
|
1901
2661
|
}, [mode]);
|
|
1902
2662
|
const commit = (next) => {
|
|
1903
2663
|
if (!controlled) setHeld(next);
|
|
1904
2664
|
onChange?.(next);
|
|
1905
2665
|
};
|
|
1906
|
-
const latest =
|
|
2666
|
+
const latest = useRef5({ list, commit });
|
|
1907
2667
|
latest.current = { list, commit };
|
|
1908
|
-
|
|
2668
|
+
useEffect7(() => {
|
|
1909
2669
|
const onKey = (event) => {
|
|
1910
2670
|
if (event.key === "Escape") {
|
|
1911
2671
|
setDraft([]);
|
|
@@ -1933,8 +2693,8 @@ var MeasureTool = ({
|
|
|
1933
2693
|
};
|
|
1934
2694
|
if (box.isEmpty()) return null;
|
|
1935
2695
|
const label = className(labelClassName);
|
|
1936
|
-
return /* @__PURE__ */
|
|
1937
|
-
picking ? null : /* @__PURE__ */
|
|
2696
|
+
return /* @__PURE__ */ jsxs7("group", { userData: FURNITURE5, children: [
|
|
2697
|
+
picking ? null : /* @__PURE__ */ jsx10(
|
|
1938
2698
|
Snapper,
|
|
1939
2699
|
{
|
|
1940
2700
|
mode,
|
|
@@ -1945,7 +2705,7 @@ var MeasureTool = ({
|
|
|
1945
2705
|
label
|
|
1946
2706
|
}
|
|
1947
2707
|
),
|
|
1948
|
-
draft.map((point, index) => /* @__PURE__ */
|
|
2708
|
+
draft.map((point, index) => /* @__PURE__ */ jsx10(
|
|
1949
2709
|
ScreenDot,
|
|
1950
2710
|
{
|
|
1951
2711
|
point,
|
|
@@ -1954,8 +2714,8 @@ var MeasureTool = ({
|
|
|
1954
2714
|
},
|
|
1955
2715
|
index
|
|
1956
2716
|
)),
|
|
1957
|
-
draft.length === 2 ? /* @__PURE__ */
|
|
1958
|
-
list.map((measurement) => /* @__PURE__ */
|
|
2717
|
+
draft.length === 2 ? /* @__PURE__ */ jsx10(Segment, { points: [draft[0], draft[1]], color: resolved.measure }) : null,
|
|
2718
|
+
list.map((measurement) => /* @__PURE__ */ jsx10(
|
|
1959
2719
|
MeasurementView,
|
|
1960
2720
|
{
|
|
1961
2721
|
measurement,
|
|
@@ -1969,28 +2729,28 @@ var MeasureTool = ({
|
|
|
1969
2729
|
] });
|
|
1970
2730
|
};
|
|
1971
2731
|
var NO_POINTER = { pointerEvents: "none" };
|
|
1972
|
-
var NOWHERE = [new
|
|
2732
|
+
var NOWHERE = [new Vector310(), new Vector310()];
|
|
1973
2733
|
var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
1974
|
-
const camera =
|
|
1975
|
-
const scene =
|
|
1976
|
-
const size =
|
|
1977
|
-
const domElement =
|
|
1978
|
-
const invalidate =
|
|
2734
|
+
const camera = useThree7((state) => state.camera);
|
|
2735
|
+
const scene = useThree7((state) => state.scene);
|
|
2736
|
+
const size = useThree7((state) => state.size);
|
|
2737
|
+
const domElement = useThree7((state) => state.gl.domElement);
|
|
2738
|
+
const invalidate = useThree7((state) => state.invalidate);
|
|
1979
2739
|
const isTap = useTapGuard();
|
|
1980
|
-
const marker =
|
|
1981
|
-
const edgeLine =
|
|
1982
|
-
const band =
|
|
1983
|
-
const bandLabel =
|
|
1984
|
-
const bandText =
|
|
1985
|
-
const snap =
|
|
2740
|
+
const marker = useRef5(null);
|
|
2741
|
+
const edgeLine = useRef5(null);
|
|
2742
|
+
const band = useRef5(null);
|
|
2743
|
+
const bandLabel = useRef5(null);
|
|
2744
|
+
const bandText = useRef5(null);
|
|
2745
|
+
const snap = useRef5(null);
|
|
1986
2746
|
const dot = useDot();
|
|
1987
|
-
|
|
2747
|
+
useLayoutEffect4(() => {
|
|
1988
2748
|
if (edgeLine.current) edgeLine.current.visible = false;
|
|
1989
2749
|
if (band.current) band.current.visible = false;
|
|
1990
2750
|
}, []);
|
|
1991
|
-
const current =
|
|
2751
|
+
const current = useRef5({ mode, draft, onPlace, format, size });
|
|
1992
2752
|
current.current = { mode, draft, onPlace, format, size };
|
|
1993
|
-
|
|
2753
|
+
useEffect7(() => {
|
|
1994
2754
|
const raycaster = new Raycaster2();
|
|
1995
2755
|
const pointer = new Vector22();
|
|
1996
2756
|
const samples = new SampleCache();
|
|
@@ -2022,7 +2782,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2022
2782
|
const snapped = snapAt(target, target.edges, camera, viewport);
|
|
2023
2783
|
const from = placed[placed.length - 1];
|
|
2024
2784
|
const lock = shift && from ? lockToAxis(from, snapped.point) : null;
|
|
2025
|
-
const found = lock ? { ...snapped, point: new
|
|
2785
|
+
const found = lock ? { ...snapped, point: new Vector310(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
|
|
2026
2786
|
snap.current = found;
|
|
2027
2787
|
const node = marker.current;
|
|
2028
2788
|
if (node) {
|
|
@@ -2096,7 +2856,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2096
2856
|
window.removeEventListener("keyup", key);
|
|
2097
2857
|
};
|
|
2098
2858
|
}, [camera, domElement, invalidate, isTap, scene, theme.measure]);
|
|
2099
|
-
|
|
2859
|
+
useEffect7(() => {
|
|
2100
2860
|
if (draft.length === 0 && band.current) {
|
|
2101
2861
|
band.current.visible = false;
|
|
2102
2862
|
if (bandLabel.current) bandLabel.current.visible = false;
|
|
@@ -2107,9 +2867,9 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2107
2867
|
invalidate();
|
|
2108
2868
|
}
|
|
2109
2869
|
}, [draft, invalidate]);
|
|
2110
|
-
return /* @__PURE__ */
|
|
2111
|
-
/* @__PURE__ */
|
|
2112
|
-
/* @__PURE__ */
|
|
2870
|
+
return /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
2871
|
+
/* @__PURE__ */ jsx10("group", { ref: marker, visible: false, children: /* @__PURE__ */ jsx10("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx10("meshBasicMaterial", { color: theme.measureSnap, depthTest: false, depthWrite: false }) }) }),
|
|
2872
|
+
/* @__PURE__ */ jsx10(
|
|
2113
2873
|
Line,
|
|
2114
2874
|
{
|
|
2115
2875
|
ref: edgeLine,
|
|
@@ -2123,7 +2883,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2123
2883
|
raycast: () => null
|
|
2124
2884
|
}
|
|
2125
2885
|
),
|
|
2126
|
-
/* @__PURE__ */
|
|
2886
|
+
/* @__PURE__ */ jsx10(
|
|
2127
2887
|
Line,
|
|
2128
2888
|
{
|
|
2129
2889
|
ref: band,
|
|
@@ -2137,7 +2897,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2137
2897
|
raycast: () => null
|
|
2138
2898
|
}
|
|
2139
2899
|
),
|
|
2140
|
-
/* @__PURE__ */
|
|
2900
|
+
/* @__PURE__ */ jsx10("group", { ref: bandLabel, visible: false, children: /* @__PURE__ */ jsx10(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx10(
|
|
2141
2901
|
"div",
|
|
2142
2902
|
{
|
|
2143
2903
|
ref: bandText,
|
|
@@ -2184,7 +2944,7 @@ function targetUnderRay(raycaster, scene, samples) {
|
|
|
2184
2944
|
edges: samples.edgesFor(hit.object)
|
|
2185
2945
|
};
|
|
2186
2946
|
}
|
|
2187
|
-
const local = new
|
|
2947
|
+
const local = new Matrix43();
|
|
2188
2948
|
for (const mesh of cutMeshes(scene)) {
|
|
2189
2949
|
const planes = clippingPlanesOf(mesh);
|
|
2190
2950
|
const sample = samples.for(mesh, planes);
|
|
@@ -2247,7 +3007,7 @@ var SampleCache = class {
|
|
|
2247
3007
|
const key = planes.map((plane) => `${plane.normal.x},${plane.normal.y},${plane.normal.z},${plane.constant}`).join("|");
|
|
2248
3008
|
const cached = this.held.get(mesh);
|
|
2249
3009
|
if (cached && cached.key === key) return cached;
|
|
2250
|
-
const local = new
|
|
3010
|
+
const local = new Matrix43().copy(mesh.matrixWorld).invert();
|
|
2251
3011
|
const localPlanes = planes.map((plane) => plane.clone().applyMatrix4(local));
|
|
2252
3012
|
const positions = mesh.geometry.getAttribute("position")?.array ?? [];
|
|
2253
3013
|
const regionOf = this.regionsOf(mesh.geometry);
|
|
@@ -2302,37 +3062,37 @@ var MeasurementView = ({
|
|
|
2302
3062
|
const anchor = measurementLabelAnchor(measurement);
|
|
2303
3063
|
if (measurement.kind === "distance") {
|
|
2304
3064
|
const [a2, b2] = measurement.points;
|
|
2305
|
-
return /* @__PURE__ */
|
|
2306
|
-
/* @__PURE__ */
|
|
2307
|
-
/* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
2309
|
-
showDeltas ? /* @__PURE__ */
|
|
2310
|
-
/* @__PURE__ */
|
|
3065
|
+
return /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
3066
|
+
/* @__PURE__ */ jsx10(Segment, { points: [a2, b2], color: theme.measure }),
|
|
3067
|
+
/* @__PURE__ */ jsx10(ScreenDot, { point: a2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
3068
|
+
/* @__PURE__ */ jsx10(ScreenDot, { point: b2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
3069
|
+
showDeltas ? /* @__PURE__ */ jsx10(DeltaLegs, { a: a2, b: b2, id: measurement.id, format, label }) : null,
|
|
3070
|
+
/* @__PURE__ */ jsx10(Label, { at: anchor, text, kind: "distance", id: measurement.id, className: label })
|
|
2311
3071
|
] });
|
|
2312
3072
|
}
|
|
2313
3073
|
const [a, vertex, b] = measurement.points;
|
|
2314
3074
|
const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b));
|
|
2315
|
-
return /* @__PURE__ */
|
|
2316
|
-
/* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2319
|
-
/* @__PURE__ */
|
|
2320
|
-
/* @__PURE__ */
|
|
2321
|
-
/* @__PURE__ */
|
|
2322
|
-
/* @__PURE__ */
|
|
3075
|
+
return /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
3076
|
+
/* @__PURE__ */ jsx10(Segment, { points: [vertex, a], color: theme.measure }),
|
|
3077
|
+
/* @__PURE__ */ jsx10(Segment, { points: [vertex, b], color: theme.measure }),
|
|
3078
|
+
/* @__PURE__ */ jsx10(Polyline, { points: arc, color: theme.measure }),
|
|
3079
|
+
/* @__PURE__ */ jsx10(ScreenDot, { point: a, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
3080
|
+
/* @__PURE__ */ jsx10(ScreenDot, { point: vertex, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
3081
|
+
/* @__PURE__ */ jsx10(ScreenDot, { point: b, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
3082
|
+
/* @__PURE__ */ jsx10(Label, { at: anchor, text, kind: "angle", id: measurement.id, className: label })
|
|
2323
3083
|
] });
|
|
2324
3084
|
};
|
|
2325
3085
|
var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
2326
|
-
const camera =
|
|
2327
|
-
const size =
|
|
2328
|
-
const invalidate =
|
|
2329
|
-
const legs =
|
|
2330
|
-
const lines =
|
|
2331
|
-
const centre =
|
|
3086
|
+
const camera = useThree7((state) => state.camera);
|
|
3087
|
+
const size = useThree7((state) => state.size);
|
|
3088
|
+
const invalidate = useThree7((state) => state.invalidate);
|
|
3089
|
+
const legs = useMemo7(() => deltaLegs(a, b), [a, b]);
|
|
3090
|
+
const lines = useRef5([]);
|
|
3091
|
+
const centre = useMemo7(() => {
|
|
2332
3092
|
const at = midpoint(a, b);
|
|
2333
|
-
return new
|
|
3093
|
+
return new Vector310(at.x, at.y, at.z);
|
|
2334
3094
|
}, [a, b]);
|
|
2335
|
-
|
|
3095
|
+
useFrame3(() => {
|
|
2336
3096
|
const dash = screenLength(camera, centre, size, DELTA_DASH_PIXELS);
|
|
2337
3097
|
let changed = false;
|
|
2338
3098
|
for (const line of lines.current) {
|
|
@@ -2344,16 +3104,16 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
2344
3104
|
}
|
|
2345
3105
|
if (changed) invalidate();
|
|
2346
3106
|
});
|
|
2347
|
-
return /* @__PURE__ */
|
|
2348
|
-
legs.map((leg, index) => /* @__PURE__ */
|
|
3107
|
+
return /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
3108
|
+
legs.map((leg, index) => /* @__PURE__ */ jsx10(
|
|
2349
3109
|
Line,
|
|
2350
3110
|
{
|
|
2351
3111
|
ref: (line) => {
|
|
2352
3112
|
lines.current[index] = line;
|
|
2353
3113
|
},
|
|
2354
3114
|
points: [
|
|
2355
|
-
new
|
|
2356
|
-
new
|
|
3115
|
+
new Vector310(leg.from.x, leg.from.y, leg.from.z),
|
|
3116
|
+
new Vector310(leg.to.x, leg.to.y, leg.to.z)
|
|
2357
3117
|
],
|
|
2358
3118
|
color: AXIS_COLORS[leg.axis],
|
|
2359
3119
|
lineWidth: MEASURE_LINE_PIXELS.leg,
|
|
@@ -2368,7 +3128,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
2368
3128
|
},
|
|
2369
3129
|
leg.axis
|
|
2370
3130
|
)),
|
|
2371
|
-
legs.map((leg) => /* @__PURE__ */
|
|
3131
|
+
legs.map((leg) => /* @__PURE__ */ jsx10(
|
|
2372
3132
|
Label,
|
|
2373
3133
|
{
|
|
2374
3134
|
at: midpoint(leg.from, leg.to),
|
|
@@ -2382,15 +3142,15 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
2382
3142
|
))
|
|
2383
3143
|
] });
|
|
2384
3144
|
};
|
|
2385
|
-
var Label = ({ at, text, kind, id, axis, className: name }) => /* @__PURE__ */
|
|
3145
|
+
var Label = ({ at, text, kind, id, axis, className: name }) => /* @__PURE__ */ jsx10("group", { position: [at.x, at.y, at.z], children: /* @__PURE__ */ jsx10(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx10("div", { className: name, "data-measure-label": kind, "data-measurement-id": id, "data-axis": axis, children: text }) }) });
|
|
2386
3146
|
var ScreenDot = ({ point, pixels, color }) => {
|
|
2387
|
-
const camera =
|
|
2388
|
-
const size =
|
|
2389
|
-
const invalidate =
|
|
2390
|
-
const node =
|
|
3147
|
+
const camera = useThree7((state) => state.camera);
|
|
3148
|
+
const size = useThree7((state) => state.size);
|
|
3149
|
+
const invalidate = useThree7((state) => state.invalidate);
|
|
3150
|
+
const node = useRef5(null);
|
|
2391
3151
|
const dot = useDot();
|
|
2392
|
-
const position =
|
|
2393
|
-
|
|
3152
|
+
const position = useMemo7(() => new Vector310(point.x, point.y, point.z), [point]);
|
|
3153
|
+
useFrame3(() => {
|
|
2394
3154
|
const group = node.current;
|
|
2395
3155
|
if (!group) return;
|
|
2396
3156
|
const length = screenLength(camera, position, size, pixels);
|
|
@@ -2399,21 +3159,21 @@ var ScreenDot = ({ point, pixels, color }) => {
|
|
|
2399
3159
|
invalidate();
|
|
2400
3160
|
}
|
|
2401
3161
|
});
|
|
2402
|
-
return /* @__PURE__ */
|
|
3162
|
+
return /* @__PURE__ */ jsx10("group", { ref: node, position, children: /* @__PURE__ */ jsx10("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx10("meshBasicMaterial", { color, depthTest: false, depthWrite: false }) }) });
|
|
2403
3163
|
};
|
|
2404
3164
|
var Segment = ({ points, color }) => {
|
|
2405
3165
|
const [a, b] = points;
|
|
2406
|
-
const ends =
|
|
2407
|
-
() => [new
|
|
3166
|
+
const ends = useMemo7(
|
|
3167
|
+
() => [new Vector310(a.x, a.y, a.z), new Vector310(b.x, b.y, b.z)],
|
|
2408
3168
|
// Keyed on the numbers: a draft's tuple is a fresh array on every render,
|
|
2409
3169
|
// and a rebuilt array would rebuild the line's geometry with it.
|
|
2410
3170
|
[a.x, a.y, a.z, b.x, b.y, b.z]
|
|
2411
3171
|
);
|
|
2412
|
-
return /* @__PURE__ */
|
|
3172
|
+
return /* @__PURE__ */ jsx10(Polyline, { points: ends, color });
|
|
2413
3173
|
};
|
|
2414
3174
|
var Polyline = ({ points, color }) => {
|
|
2415
3175
|
if (points.length < 2) return null;
|
|
2416
|
-
return /* @__PURE__ */
|
|
3176
|
+
return /* @__PURE__ */ jsx10(
|
|
2417
3177
|
Line,
|
|
2418
3178
|
{
|
|
2419
3179
|
points,
|
|
@@ -2428,8 +3188,8 @@ var Polyline = ({ points, color }) => {
|
|
|
2428
3188
|
);
|
|
2429
3189
|
};
|
|
2430
3190
|
function useDot() {
|
|
2431
|
-
const geometry =
|
|
2432
|
-
|
|
3191
|
+
const geometry = useMemo7(() => new SphereGeometry(1, 16, 12), []);
|
|
3192
|
+
useEffect7(() => () => geometry.dispose(), [geometry]);
|
|
2433
3193
|
return geometry;
|
|
2434
3194
|
}
|
|
2435
3195
|
function isEditing(target) {
|
|
@@ -2442,11 +3202,11 @@ function className(extra) {
|
|
|
2442
3202
|
|
|
2443
3203
|
// src/tool-marks.tsx
|
|
2444
3204
|
import { Html as Html2, Line as Line2 } from "@react-three/drei";
|
|
2445
|
-
import { useMemo as
|
|
2446
|
-
import { DoubleSide as
|
|
3205
|
+
import { useMemo as useMemo8 } from "react";
|
|
3206
|
+
import { DoubleSide as DoubleSide4 } from "three";
|
|
2447
3207
|
|
|
2448
3208
|
// src/render/tool-marks.ts
|
|
2449
|
-
import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as
|
|
3209
|
+
import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as Vector311 } from "three";
|
|
2450
3210
|
var TOOL_CORNER_STEPS = 12;
|
|
2451
3211
|
var TOOL_ROUND = 64;
|
|
2452
3212
|
function toolProfile(radius, corner, height) {
|
|
@@ -2466,9 +3226,9 @@ function toolProfile(radius, corner, height) {
|
|
|
2466
3226
|
points.push(new Vector23(radius, Math.max(height, rounded)));
|
|
2467
3227
|
return points;
|
|
2468
3228
|
}
|
|
2469
|
-
var UP = new
|
|
2470
|
-
var AXES = [new
|
|
2471
|
-
var toVector = ({ x, y, z }) => new
|
|
3229
|
+
var UP = new Vector311(0, 1, 0);
|
|
3230
|
+
var AXES = [new Vector311(1, 0, 0), new Vector311(0, 1, 0), new Vector311(0, 0, 1)];
|
|
3231
|
+
var toVector = ({ x, y, z }) => new Vector311(x, y, z);
|
|
2472
3232
|
var squareTo = (w, along) => {
|
|
2473
3233
|
const wanted = along ? toVector(along) : null;
|
|
2474
3234
|
const flat = wanted?.clone().addScaledVector(w, -wanted.dot(w));
|
|
@@ -2488,7 +3248,7 @@ function toolMarkShape(mark) {
|
|
|
2488
3248
|
if (!(mark.diameter > 0) || axis.lengthSq() === 0) return null;
|
|
2489
3249
|
const w = axis.normalize();
|
|
2490
3250
|
const u = squareTo(w, mark.across);
|
|
2491
|
-
const v = new
|
|
3251
|
+
const v = new Vector311().crossVectors(w, u);
|
|
2492
3252
|
const radius = mark.diameter / 2;
|
|
2493
3253
|
const corner = Math.min(Math.max(mark.cornerRadius ?? 0, 0), radius);
|
|
2494
3254
|
const height = Math.max(mark.height, corner);
|
|
@@ -2518,13 +3278,12 @@ function toolMarkShape(mark) {
|
|
|
2518
3278
|
}
|
|
2519
3279
|
|
|
2520
3280
|
// src/tool-marks.tsx
|
|
2521
|
-
import { jsx as
|
|
3281
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2522
3282
|
var BODY = { color: 16317180, opacity: 0.32 };
|
|
2523
3283
|
var NO_POINTER2 = { pointerEvents: "none" };
|
|
2524
|
-
var ignoreRay = () => void 0;
|
|
2525
3284
|
var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
|
|
2526
3285
|
const color = resolveTheme(theme).measure;
|
|
2527
|
-
const shapes =
|
|
3286
|
+
const shapes = useMemo8(
|
|
2528
3287
|
() => marks.flatMap((mark) => {
|
|
2529
3288
|
const shape = toolMarkShape(mark);
|
|
2530
3289
|
return shape ? [{ mark, shape }] : [];
|
|
@@ -2532,9 +3291,9 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
|
|
|
2532
3291
|
[marks]
|
|
2533
3292
|
);
|
|
2534
3293
|
const label = labelClassName ? `${MEASURE_LABEL_CLASS} ${labelClassName}` : MEASURE_LABEL_CLASS;
|
|
2535
|
-
return /* @__PURE__ */
|
|
2536
|
-
/* @__PURE__ */
|
|
2537
|
-
[...shape.rims, shape.dimension, ...shape.ticks].map((points, line) => /* @__PURE__ */
|
|
3294
|
+
return /* @__PURE__ */ jsx11("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: true }, children: shapes.map(({ mark, shape }, index) => /* @__PURE__ */ jsxs8("group", { children: [
|
|
3295
|
+
/* @__PURE__ */ jsx11(ToolBody, { shape }),
|
|
3296
|
+
[...shape.rims, shape.dimension, ...shape.ticks].map((points, line) => /* @__PURE__ */ jsx11(
|
|
2538
3297
|
Line2,
|
|
2539
3298
|
{
|
|
2540
3299
|
points,
|
|
@@ -2547,16 +3306,16 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
|
|
|
2547
3306
|
},
|
|
2548
3307
|
line
|
|
2549
3308
|
)),
|
|
2550
|
-
mark.label && visible ? /* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
2552
|
-
mark.note ? /* @__PURE__ */
|
|
3309
|
+
mark.label && visible ? /* @__PURE__ */ jsx11(Html2, { position: shape.labelAt, center: true, zIndexRange: [0, 0], style: NO_POINTER2, children: /* @__PURE__ */ jsxs8("div", { className: label, "data-measure-label": "tool", children: [
|
|
3310
|
+
/* @__PURE__ */ jsx11("span", { "data-measure-value": true, children: mark.label }),
|
|
3311
|
+
mark.note ? /* @__PURE__ */ jsxs8("span", { "data-measure-note": true, children: [
|
|
2553
3312
|
" ",
|
|
2554
3313
|
mark.note
|
|
2555
3314
|
] }) : null
|
|
2556
3315
|
] }) }) : null
|
|
2557
3316
|
] }, index)) });
|
|
2558
3317
|
};
|
|
2559
|
-
var ToolBody = ({ shape }) => /* @__PURE__ */
|
|
3318
|
+
var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs8(
|
|
2560
3319
|
"mesh",
|
|
2561
3320
|
{
|
|
2562
3321
|
position: shape.base,
|
|
@@ -2564,15 +3323,15 @@ var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs7(
|
|
|
2564
3323
|
raycast: ignoreRay,
|
|
2565
3324
|
renderOrder: MEASURE_RENDER_ORDER - 1,
|
|
2566
3325
|
children: [
|
|
2567
|
-
/* @__PURE__ */
|
|
2568
|
-
/* @__PURE__ */
|
|
3326
|
+
/* @__PURE__ */ jsx11("latheGeometry", { args: [shape.profile, 64] }),
|
|
3327
|
+
/* @__PURE__ */ jsx11(
|
|
2569
3328
|
"meshBasicMaterial",
|
|
2570
3329
|
{
|
|
2571
3330
|
color: BODY.color,
|
|
2572
3331
|
transparent: true,
|
|
2573
3332
|
opacity: BODY.opacity,
|
|
2574
3333
|
depthWrite: false,
|
|
2575
|
-
side:
|
|
3334
|
+
side: DoubleSide4
|
|
2576
3335
|
}
|
|
2577
3336
|
)
|
|
2578
3337
|
]
|
|
@@ -2638,6 +3397,7 @@ export {
|
|
|
2638
3397
|
DirectionArrows,
|
|
2639
3398
|
EXCLUDE_FROM_FRAME,
|
|
2640
3399
|
EnginePart,
|
|
3400
|
+
ExplodedPart,
|
|
2641
3401
|
ExtendedCameraControls,
|
|
2642
3402
|
FEATURE_TYPE_RANKS,
|
|
2643
3403
|
Grid,
|
|
@@ -2711,6 +3471,7 @@ export {
|
|
|
2711
3471
|
clipSegments,
|
|
2712
3472
|
contentBounds,
|
|
2713
3473
|
createCapMaterial,
|
|
3474
|
+
createExplodeAmount,
|
|
2714
3475
|
createMaskMaterial,
|
|
2715
3476
|
createMaskTarget,
|
|
2716
3477
|
createPart,
|