@toolpath/viewer 2.3.3 → 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 +139 -4
- package/dist/{chunk-M2B3CQPQ.js → chunk-ESPHWO5M.js} +367 -84
- package/dist/engine/index.d.ts +14 -3
- package/dist/engine/index.js +5 -3
- package/dist/index.d.ts +81 -4
- package/dist/index.js +1141 -368
- package/dist/{normalize-C4Wj4IGK.d.ts → normalize-BBADLCmd.d.ts} +47 -2
- 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,25 +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
|
|
278
|
-
var
|
|
279
|
-
var
|
|
280
|
-
var
|
|
281
|
-
var
|
|
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" }) });
|
|
282
1043
|
var ICONS = {
|
|
283
1044
|
fit: FitIcon,
|
|
284
1045
|
reset: ResetIcon,
|
|
@@ -291,6 +1052,7 @@ var ICONS = {
|
|
|
291
1052
|
directions: DirectionsIcon,
|
|
292
1053
|
dfm: DfmIcon,
|
|
293
1054
|
sharpCorners: SharpCornersIcon,
|
|
1055
|
+
threads: ThreadsIcon,
|
|
294
1056
|
hover: HoverIcon,
|
|
295
1057
|
focus: FocusIcon,
|
|
296
1058
|
wireframe: WireframeIcon,
|
|
@@ -299,16 +1061,16 @@ var ICONS = {
|
|
|
299
1061
|
};
|
|
300
1062
|
var ToolbarIcon = ({ name }) => {
|
|
301
1063
|
const Icon = ICONS[name];
|
|
302
|
-
return /* @__PURE__ */
|
|
1064
|
+
return /* @__PURE__ */ jsx3(Icon, {});
|
|
303
1065
|
};
|
|
304
1066
|
|
|
305
1067
|
// src/viewer-toolbar.tsx
|
|
306
|
-
import { jsx as
|
|
1068
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
307
1069
|
var ViewerToolbarContext = createContext(null);
|
|
308
1070
|
var ViewerToolbarProvider = ({
|
|
309
1071
|
controls,
|
|
310
1072
|
children
|
|
311
|
-
}) => /* @__PURE__ */
|
|
1073
|
+
}) => /* @__PURE__ */ jsx4(ViewerToolbarContext.Provider, { value: controls, children });
|
|
312
1074
|
var useViewerToolbar = () => {
|
|
313
1075
|
const controls = useContext(ViewerToolbarContext);
|
|
314
1076
|
if (!controls) throw new Error("useViewerToolbar must be used inside <ViewerToolbarProvider>");
|
|
@@ -321,7 +1083,7 @@ var ToolbarButton = ({ action, icon, label, toggle = true }) => {
|
|
|
321
1083
|
throw new Error(`<ViewerToolbar.${buttonName(action)}> requires a '${action}' toolbar control`);
|
|
322
1084
|
}
|
|
323
1085
|
const pressed = control.pressed ?? false;
|
|
324
|
-
return /* @__PURE__ */
|
|
1086
|
+
return /* @__PURE__ */ jsxs3(
|
|
325
1087
|
"button",
|
|
326
1088
|
{
|
|
327
1089
|
className: "viewer-toolbar-button",
|
|
@@ -331,13 +1093,13 @@ var ToolbarButton = ({ action, icon, label, toggle = true }) => {
|
|
|
331
1093
|
"aria-pressed": toggle ? pressed : void 0,
|
|
332
1094
|
onClick: control.onClick,
|
|
333
1095
|
children: [
|
|
334
|
-
/* @__PURE__ */
|
|
335
|
-
/* @__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) })
|
|
336
1098
|
]
|
|
337
1099
|
}
|
|
338
1100
|
);
|
|
339
1101
|
};
|
|
340
|
-
var StockButton = () => /* @__PURE__ */
|
|
1102
|
+
var StockButton = () => /* @__PURE__ */ jsx4(
|
|
341
1103
|
ToolbarButton,
|
|
342
1104
|
{
|
|
343
1105
|
action: "stock",
|
|
@@ -345,7 +1107,7 @@ var StockButton = () => /* @__PURE__ */ jsx3(
|
|
|
345
1107
|
label: (pressed) => pressed ? "Hide stock" : "Show stock"
|
|
346
1108
|
}
|
|
347
1109
|
);
|
|
348
|
-
var AxesButton = () => /* @__PURE__ */
|
|
1110
|
+
var AxesButton = () => /* @__PURE__ */ jsx4(
|
|
349
1111
|
ToolbarButton,
|
|
350
1112
|
{
|
|
351
1113
|
action: "axes",
|
|
@@ -353,7 +1115,7 @@ var AxesButton = () => /* @__PURE__ */ jsx3(
|
|
|
353
1115
|
label: (pressed) => pressed ? "Hide axis" : "Show axis"
|
|
354
1116
|
}
|
|
355
1117
|
);
|
|
356
|
-
var GridButton = () => /* @__PURE__ */
|
|
1118
|
+
var GridButton = () => /* @__PURE__ */ jsx4(
|
|
357
1119
|
ToolbarButton,
|
|
358
1120
|
{
|
|
359
1121
|
action: "grid",
|
|
@@ -361,7 +1123,7 @@ var GridButton = () => /* @__PURE__ */ jsx3(
|
|
|
361
1123
|
label: (pressed) => pressed ? "Hide grid" : "Show grid"
|
|
362
1124
|
}
|
|
363
1125
|
);
|
|
364
|
-
var BananaButton = () => /* @__PURE__ */
|
|
1126
|
+
var BananaButton = () => /* @__PURE__ */ jsx4(
|
|
365
1127
|
ToolbarButton,
|
|
366
1128
|
{
|
|
367
1129
|
action: "banana",
|
|
@@ -369,7 +1131,7 @@ var BananaButton = () => /* @__PURE__ */ jsx3(
|
|
|
369
1131
|
label: (pressed) => pressed ? "Banana for scale (on)" : "Banana for scale"
|
|
370
1132
|
}
|
|
371
1133
|
);
|
|
372
|
-
var ToolsButton = () => /* @__PURE__ */
|
|
1134
|
+
var ToolsButton = () => /* @__PURE__ */ jsx4(
|
|
373
1135
|
ToolbarButton,
|
|
374
1136
|
{
|
|
375
1137
|
action: "tools",
|
|
@@ -377,7 +1139,7 @@ var ToolsButton = () => /* @__PURE__ */ jsx3(
|
|
|
377
1139
|
label: (pressed) => pressed ? "Hide tools on the part" : "Show tools on the part"
|
|
378
1140
|
}
|
|
379
1141
|
);
|
|
380
|
-
var DirectionsButton = () => /* @__PURE__ */
|
|
1142
|
+
var DirectionsButton = () => /* @__PURE__ */ jsx4(
|
|
381
1143
|
ToolbarButton,
|
|
382
1144
|
{
|
|
383
1145
|
action: "directions",
|
|
@@ -385,7 +1147,7 @@ var DirectionsButton = () => /* @__PURE__ */ jsx3(
|
|
|
385
1147
|
label: () => "Highlight faces by direction"
|
|
386
1148
|
}
|
|
387
1149
|
);
|
|
388
|
-
var DfmButton = () => /* @__PURE__ */
|
|
1150
|
+
var DfmButton = () => /* @__PURE__ */ jsx4(
|
|
389
1151
|
ToolbarButton,
|
|
390
1152
|
{
|
|
391
1153
|
action: "dfm",
|
|
@@ -393,7 +1155,7 @@ var DfmButton = () => /* @__PURE__ */ jsx3(
|
|
|
393
1155
|
label: (pressed) => pressed ? "Hide DFM flags" : "Show DFM flags"
|
|
394
1156
|
}
|
|
395
1157
|
);
|
|
396
|
-
var SharpCornersButton = () => /* @__PURE__ */
|
|
1158
|
+
var SharpCornersButton = () => /* @__PURE__ */ jsx4(
|
|
397
1159
|
ToolbarButton,
|
|
398
1160
|
{
|
|
399
1161
|
action: "sharpCorners",
|
|
@@ -401,7 +1163,15 @@ var SharpCornersButton = () => /* @__PURE__ */ jsx3(
|
|
|
401
1163
|
label: (pressed) => pressed ? "Hide sharp corners" : "Show sharp corners"
|
|
402
1164
|
}
|
|
403
1165
|
);
|
|
404
|
-
var
|
|
1166
|
+
var ThreadsButton = () => /* @__PURE__ */ jsx4(
|
|
1167
|
+
ToolbarButton,
|
|
1168
|
+
{
|
|
1169
|
+
action: "threads",
|
|
1170
|
+
icon: "threads",
|
|
1171
|
+
label: (pressed) => pressed ? "Hide threads" : "Show threads"
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
var HoverButton = () => /* @__PURE__ */ jsx4(
|
|
405
1175
|
ToolbarButton,
|
|
406
1176
|
{
|
|
407
1177
|
action: "hover",
|
|
@@ -409,7 +1179,7 @@ var HoverButton = () => /* @__PURE__ */ jsx3(
|
|
|
409
1179
|
label: (pressed) => pressed ? "Disable feature hover" : "Enable feature hover"
|
|
410
1180
|
}
|
|
411
1181
|
);
|
|
412
|
-
var FocusButton = () => /* @__PURE__ */
|
|
1182
|
+
var FocusButton = () => /* @__PURE__ */ jsx4(
|
|
413
1183
|
ToolbarButton,
|
|
414
1184
|
{
|
|
415
1185
|
action: "focus",
|
|
@@ -417,8 +1187,8 @@ var FocusButton = () => /* @__PURE__ */ jsx3(
|
|
|
417
1187
|
label: (pressed) => pressed ? "Show full part" : "Focus selection"
|
|
418
1188
|
}
|
|
419
1189
|
);
|
|
420
|
-
var WireframeButton = () => /* @__PURE__ */
|
|
421
|
-
var SectionButton = () => /* @__PURE__ */
|
|
1190
|
+
var WireframeButton = () => /* @__PURE__ */ jsx4(ToolbarButton, { action: "wireframe", icon: "wireframe", label: () => "Wireframe" });
|
|
1191
|
+
var SectionButton = () => /* @__PURE__ */ jsx4(
|
|
422
1192
|
ToolbarButton,
|
|
423
1193
|
{
|
|
424
1194
|
action: "section",
|
|
@@ -426,7 +1196,7 @@ var SectionButton = () => /* @__PURE__ */ jsx3(
|
|
|
426
1196
|
label: (pressed) => pressed ? "Exit section" : "Section"
|
|
427
1197
|
}
|
|
428
1198
|
);
|
|
429
|
-
var MeasureButton = () => /* @__PURE__ */
|
|
1199
|
+
var MeasureButton = () => /* @__PURE__ */ jsx4(
|
|
430
1200
|
ToolbarButton,
|
|
431
1201
|
{
|
|
432
1202
|
action: "measure",
|
|
@@ -434,11 +1204,11 @@ var MeasureButton = () => /* @__PURE__ */ jsx3(
|
|
|
434
1204
|
label: (pressed) => pressed ? "Exit measure" : "Measure"
|
|
435
1205
|
}
|
|
436
1206
|
);
|
|
437
|
-
var FitButton = () => /* @__PURE__ */
|
|
438
|
-
var ResetButton = () => /* @__PURE__ */
|
|
439
|
-
var TopButton = () => /* @__PURE__ */
|
|
440
|
-
var Divider = () => /* @__PURE__ */
|
|
441
|
-
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(
|
|
442
1212
|
"div",
|
|
443
1213
|
{
|
|
444
1214
|
className: ["viewer-toolbar", className2].filter(Boolean).join(" "),
|
|
@@ -451,43 +1221,44 @@ var Controls = ({ children, className: className2 }) => /* @__PURE__ */ jsx3(
|
|
|
451
1221
|
var DefaultControls = () => {
|
|
452
1222
|
const controls = useViewerToolbar();
|
|
453
1223
|
const display = [
|
|
454
|
-
controls.stock ? /* @__PURE__ */
|
|
455
|
-
controls.axes ? /* @__PURE__ */
|
|
456
|
-
controls.grid ? /* @__PURE__ */
|
|
457
|
-
controls.banana ? /* @__PURE__ */
|
|
458
|
-
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
|
|
459
1229
|
].filter(Boolean);
|
|
460
1230
|
const analysis = [
|
|
461
|
-
controls.directions ? /* @__PURE__ */
|
|
462
|
-
controls.dfm ? /* @__PURE__ */
|
|
463
|
-
controls.sharpCorners ? /* @__PURE__ */
|
|
464
|
-
controls.
|
|
465
|
-
controls.
|
|
466
|
-
controls.
|
|
467
|
-
controls.
|
|
468
|
-
controls.
|
|
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
|
|
469
1240
|
].filter(Boolean);
|
|
470
1241
|
const camera = [
|
|
471
|
-
controls.fit ? /* @__PURE__ */
|
|
472
|
-
controls.reset ? /* @__PURE__ */
|
|
473
|
-
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
|
|
474
1245
|
].filter(Boolean);
|
|
475
|
-
return /* @__PURE__ */
|
|
1246
|
+
return /* @__PURE__ */ jsxs3(Controls, { children: [
|
|
476
1247
|
display,
|
|
477
|
-
display.length && analysis.length ? /* @__PURE__ */
|
|
1248
|
+
display.length && analysis.length ? /* @__PURE__ */ jsx4(Divider, {}) : null,
|
|
478
1249
|
analysis,
|
|
479
|
-
(display.length || analysis.length) && camera.length ? /* @__PURE__ */
|
|
1250
|
+
(display.length || analysis.length) && camera.length ? /* @__PURE__ */ jsx4(Divider, {}) : null,
|
|
480
1251
|
camera
|
|
481
1252
|
] });
|
|
482
1253
|
};
|
|
483
1254
|
var ViewerToolbarRoot = ({ className: className2, children }) => {
|
|
484
1255
|
useViewerToolbar();
|
|
485
|
-
return /* @__PURE__ */
|
|
1256
|
+
return /* @__PURE__ */ jsx4(
|
|
486
1257
|
"div",
|
|
487
1258
|
{
|
|
488
1259
|
className: ["viewer-toolbar-stack", className2].filter(Boolean).join(" "),
|
|
489
1260
|
"data-viewer-toolbar": "true",
|
|
490
|
-
children: children ?? /* @__PURE__ */
|
|
1261
|
+
children: children ?? /* @__PURE__ */ jsx4(DefaultControls, {})
|
|
491
1262
|
}
|
|
492
1263
|
);
|
|
493
1264
|
};
|
|
@@ -502,6 +1273,7 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
|
|
|
502
1273
|
DirectionsButton,
|
|
503
1274
|
DfmButton,
|
|
504
1275
|
SharpCornersButton,
|
|
1276
|
+
ThreadsButton,
|
|
505
1277
|
HoverButton,
|
|
506
1278
|
FocusButton,
|
|
507
1279
|
WireframeButton,
|
|
@@ -513,11 +1285,11 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
|
|
|
513
1285
|
});
|
|
514
1286
|
|
|
515
1287
|
// src/stock.tsx
|
|
516
|
-
import { useThree } from "@react-three/fiber";
|
|
517
|
-
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";
|
|
518
1290
|
|
|
519
1291
|
// src/render/stock-mesh.ts
|
|
520
|
-
import { Mesh } from "three";
|
|
1292
|
+
import { Mesh as Mesh2 } from "three";
|
|
521
1293
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
522
1294
|
async function parseStockGlb(data) {
|
|
523
1295
|
const { scene } = await new GLTFLoader().parseAsync(data, "");
|
|
@@ -525,7 +1297,7 @@ async function parseStockGlb(data) {
|
|
|
525
1297
|
return stockMeshGeometries(scene);
|
|
526
1298
|
} finally {
|
|
527
1299
|
scene.traverse((object) => {
|
|
528
|
-
if (!(object instanceof
|
|
1300
|
+
if (!(object instanceof Mesh2)) return;
|
|
529
1301
|
object.geometry.dispose();
|
|
530
1302
|
for (const material of Array.isArray(object.material) ? object.material : [object.material]) {
|
|
531
1303
|
material.dispose();
|
|
@@ -537,7 +1309,7 @@ function stockMeshGeometries(scene) {
|
|
|
537
1309
|
const geometries = [];
|
|
538
1310
|
scene.updateWorldMatrix(true, true);
|
|
539
1311
|
scene.traverse((object) => {
|
|
540
|
-
if (!(object instanceof
|
|
1312
|
+
if (!(object instanceof Mesh2)) return;
|
|
541
1313
|
const geometry = object.geometry.clone().applyMatrix4(object.matrixWorld);
|
|
542
1314
|
if (!geometry.hasAttribute("normal")) geometry.computeVertexNormals();
|
|
543
1315
|
geometries.push(geometry);
|
|
@@ -592,30 +1364,30 @@ function createStockMeshBuffer() {
|
|
|
592
1364
|
|
|
593
1365
|
// src/render/stock.ts
|
|
594
1366
|
import {
|
|
595
|
-
Box3,
|
|
1367
|
+
Box3 as Box32,
|
|
596
1368
|
BoxGeometry,
|
|
597
|
-
BufferGeometry,
|
|
1369
|
+
BufferGeometry as BufferGeometry3,
|
|
598
1370
|
CylinderGeometry,
|
|
599
|
-
Float32BufferAttribute,
|
|
600
|
-
Group,
|
|
601
|
-
LineBasicMaterial,
|
|
602
|
-
LineSegments,
|
|
603
|
-
Matrix4,
|
|
604
|
-
Mesh as
|
|
605
|
-
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,
|
|
606
1378
|
Quaternion,
|
|
607
|
-
Vector3 as
|
|
1379
|
+
Vector3 as Vector33
|
|
608
1380
|
} from "three";
|
|
609
1381
|
|
|
610
1382
|
// src/render/stock-edges.ts
|
|
611
|
-
import { MathUtils, Triangle, Vector3 } from "three";
|
|
1383
|
+
import { MathUtils, Triangle, Vector3 as Vector32 } from "three";
|
|
612
1384
|
var STOCK_EDGE_ANGLE = 15;
|
|
613
1385
|
var SYNC_EDGE_TRIANGLES = 2e4;
|
|
614
1386
|
var SLICE_MS = 4;
|
|
615
1387
|
var CHECK_EVERY = 256;
|
|
616
|
-
var _v0 = new
|
|
617
|
-
var _v1 = new
|
|
618
|
-
var _normal = new
|
|
1388
|
+
var _v0 = new Vector32();
|
|
1389
|
+
var _v1 = new Vector32();
|
|
1390
|
+
var _normal = new Vector32();
|
|
619
1391
|
var _triangle = new Triangle();
|
|
620
1392
|
function* edgeWork(geometry, thresholdAngle) {
|
|
621
1393
|
const precision = Math.pow(10, 4);
|
|
@@ -739,7 +1511,7 @@ function boxFigure(bounds) {
|
|
|
739
1511
|
};
|
|
740
1512
|
}
|
|
741
1513
|
var CYLINDER_SEGMENTS = 64;
|
|
742
|
-
var CYLINDER_AXIS = new
|
|
1514
|
+
var CYLINDER_AXIS = new Vector33(0, 1, 0);
|
|
743
1515
|
function fixedBoxStockBounds(geometry, dimensions, position = "model_centered", positionOffset = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
744
1516
|
if (![dimensions.x, dimensions.y, dimensions.z].every(Number.isFinite) || [dimensions.x, dimensions.y, dimensions.z].some((value) => value <= 0)) {
|
|
745
1517
|
throw new RangeError("Fixed stock dimensions must be finite and positive.");
|
|
@@ -751,12 +1523,12 @@ function fixedBoxStockBounds(geometry, dimensions, position = "model_centered",
|
|
|
751
1523
|
throw new RangeError("Fixed stock offset must be finite.");
|
|
752
1524
|
}
|
|
753
1525
|
const part = partBounds(geometry);
|
|
754
|
-
const center = part.getCenter(new
|
|
1526
|
+
const center = part.getCenter(new Vector33());
|
|
755
1527
|
const z = fixedStockLowerZ(part, dimensions.z, position, positionOffset);
|
|
756
|
-
return new
|
|
757
|
-
new
|
|
758
|
-
new
|
|
759
|
-
).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));
|
|
760
1532
|
}
|
|
761
1533
|
function fixedCylinderStock(geometry, size, position = "model_centered", positionOffset = 0) {
|
|
762
1534
|
if (![size.diameter, size.length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
@@ -766,7 +1538,7 @@ function fixedCylinderStock(geometry, size, position = "model_centered", positio
|
|
|
766
1538
|
throw new RangeError("Fixed stock position offset must be finite.");
|
|
767
1539
|
}
|
|
768
1540
|
const part = partBounds(geometry);
|
|
769
|
-
const center = part.getCenter(new
|
|
1541
|
+
const center = part.getCenter(new Vector33());
|
|
770
1542
|
return {
|
|
771
1543
|
origin: {
|
|
772
1544
|
x: center.x,
|
|
@@ -792,13 +1564,13 @@ function cylinderStockGeometry(input) {
|
|
|
792
1564
|
if (![diameter, length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
793
1565
|
throw new RangeError("Cylinder stock diameter and length must be finite and positive.");
|
|
794
1566
|
}
|
|
795
|
-
const axis = new
|
|
1567
|
+
const axis = new Vector33(figure.axis.x, figure.axis.y, figure.axis.z);
|
|
796
1568
|
const norm = axis.length();
|
|
797
1569
|
if (!Number.isFinite(norm) || norm === 0) {
|
|
798
1570
|
throw new RangeError("Cylinder stock axis must be finite and non-zero.");
|
|
799
1571
|
}
|
|
800
1572
|
axis.divideScalar(norm);
|
|
801
|
-
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));
|
|
802
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);
|
|
803
1575
|
}
|
|
804
1576
|
function boxStockGeometry(input) {
|
|
@@ -826,12 +1598,12 @@ function orientedBoxStockGeometry(figure) {
|
|
|
826
1598
|
)) {
|
|
827
1599
|
throw new RangeError("Box stock frame and corners must be finite.");
|
|
828
1600
|
}
|
|
829
|
-
const size = new
|
|
1601
|
+
const size = new Vector33(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
|
|
830
1602
|
if (size.toArray().some((value) => !(value > 0))) {
|
|
831
1603
|
throw new RangeError("Box stock upper corner must be above its lower corner on every axis.");
|
|
832
1604
|
}
|
|
833
|
-
const z = new
|
|
834
|
-
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);
|
|
835
1607
|
const reference = x.length();
|
|
836
1608
|
x.addScaledVector(z, -x.dot(z));
|
|
837
1609
|
if (!(z.lengthSq() > 0 && x.length() > 1e-9 * reference)) {
|
|
@@ -840,11 +1612,11 @@ function orientedBoxStockGeometry(figure) {
|
|
|
840
1612
|
);
|
|
841
1613
|
}
|
|
842
1614
|
x.normalize();
|
|
843
|
-
const y = new
|
|
844
|
-
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));
|
|
845
1617
|
}
|
|
846
1618
|
function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
847
|
-
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);
|
|
848
1620
|
if (padding.toArray().some((value) => !Number.isFinite(value) || value < 0)) {
|
|
849
1621
|
throw new RangeError("Stock allowance must be finite and non-negative.");
|
|
850
1622
|
}
|
|
@@ -852,8 +1624,8 @@ function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 })
|
|
|
852
1624
|
throw new RangeError("Stock offset must be finite.");
|
|
853
1625
|
}
|
|
854
1626
|
const box = partBounds(geometry);
|
|
855
|
-
box.expandByVector(padding).translate(new
|
|
856
|
-
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());
|
|
857
1629
|
if (size.toArray().some((value) => !Number.isFinite(value) || value <= 0)) {
|
|
858
1630
|
throw new RangeError("Stock dimensions must be finite and positive.");
|
|
859
1631
|
}
|
|
@@ -863,8 +1635,8 @@ function partBounds(geometry) {
|
|
|
863
1635
|
const position = geometry.getAttribute("position");
|
|
864
1636
|
if (!position || position.count === 0)
|
|
865
1637
|
throw new RangeError("Stock needs non-empty part geometry.");
|
|
866
|
-
const box = new
|
|
867
|
-
const point = new
|
|
1638
|
+
const box = new Box32();
|
|
1639
|
+
const point = new Vector33();
|
|
868
1640
|
for (let index = 0; index < position.count; index += 1) {
|
|
869
1641
|
point.fromBufferAttribute(position, index);
|
|
870
1642
|
if (!Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
|
|
@@ -875,10 +1647,10 @@ function partBounds(geometry) {
|
|
|
875
1647
|
return box;
|
|
876
1648
|
}
|
|
877
1649
|
function createStock(geometry, legacyWorkpiece = false, onEdges) {
|
|
878
|
-
const material = new
|
|
879
|
-
const edgeMaterial = new
|
|
880
|
-
const mesh = new
|
|
881
|
-
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();
|
|
882
1654
|
object.userData[STOCK_OBJECT] = true;
|
|
883
1655
|
mesh.renderOrder = legacyWorkpiece ? 2 : 5;
|
|
884
1656
|
mesh.raycast = () => {
|
|
@@ -895,9 +1667,9 @@ function createStock(geometry, legacyWorkpiece = false, onEdges) {
|
|
|
895
1667
|
let building = null;
|
|
896
1668
|
let shown = false;
|
|
897
1669
|
const attach = (positions) => {
|
|
898
|
-
const outline = new
|
|
899
|
-
outline.setAttribute("position", new
|
|
900
|
-
edges = new
|
|
1670
|
+
const outline = new BufferGeometry3();
|
|
1671
|
+
outline.setAttribute("position", new Float32BufferAttribute3(positions, 3));
|
|
1672
|
+
edges = new LineSegments2(outline, edgeMaterial);
|
|
901
1673
|
edges.renderOrder = legacyWorkpiece ? 1 : 6;
|
|
902
1674
|
edges.raycast = () => {
|
|
903
1675
|
};
|
|
@@ -941,8 +1713,8 @@ function createStock(geometry, legacyWorkpiece = false, onEdges) {
|
|
|
941
1713
|
}
|
|
942
1714
|
|
|
943
1715
|
// src/stock.tsx
|
|
944
|
-
import { jsx as
|
|
945
|
-
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 });
|
|
946
1718
|
var GeometryStock = ({
|
|
947
1719
|
geometry,
|
|
948
1720
|
renderStyle = "overlay",
|
|
@@ -954,13 +1726,13 @@ var GeometryStock = ({
|
|
|
954
1726
|
edgeOpacity = renderStyle === "legacy-workpiece" ? 0.5 : 0.75,
|
|
955
1727
|
showEdges = true
|
|
956
1728
|
}) => {
|
|
957
|
-
const invalidate =
|
|
958
|
-
const stock =
|
|
1729
|
+
const invalidate = useThree2((state) => state.invalidate);
|
|
1730
|
+
const stock = useMemo2(
|
|
959
1731
|
() => createStock(geometry, renderStyle === "legacy-workpiece", invalidate),
|
|
960
1732
|
[geometry, renderStyle, invalidate]
|
|
961
1733
|
);
|
|
962
|
-
|
|
963
|
-
|
|
1734
|
+
useEffect3(() => () => stock.dispose(), [stock]);
|
|
1735
|
+
useLayoutEffect2(() => {
|
|
964
1736
|
stock.material.color.setHex(color);
|
|
965
1737
|
stock.material.emissive.setHex(emissive);
|
|
966
1738
|
if (stock.material.flatShading !== flatShading) {
|
|
@@ -988,20 +1760,20 @@ var GeometryStock = ({
|
|
|
988
1760
|
showEdges,
|
|
989
1761
|
stock
|
|
990
1762
|
]);
|
|
991
|
-
return /* @__PURE__ */
|
|
1763
|
+
return /* @__PURE__ */ jsx5("primitive", { object: stock.object, dispose: null });
|
|
992
1764
|
};
|
|
993
1765
|
var GlbStock = ({
|
|
994
1766
|
glb,
|
|
995
1767
|
retainPrevious = false,
|
|
996
1768
|
...appearance
|
|
997
1769
|
}) => {
|
|
998
|
-
const buffer =
|
|
1770
|
+
const buffer = useMemo2(() => createStockMeshBuffer(), []);
|
|
999
1771
|
const [decoded, setDecoded] = useState2(null);
|
|
1000
|
-
|
|
1001
|
-
|
|
1772
|
+
useEffect3(() => () => buffer.dispose(), [buffer]);
|
|
1773
|
+
useLayoutEffect2(() => {
|
|
1002
1774
|
buffer.commit(decoded?.geometries ?? []);
|
|
1003
1775
|
}, [buffer, decoded]);
|
|
1004
|
-
|
|
1776
|
+
useEffect3(
|
|
1005
1777
|
() => buffer.request(
|
|
1006
1778
|
() => parseStockGlb(glb),
|
|
1007
1779
|
(geometries) => {
|
|
@@ -1018,7 +1790,7 @@ var GlbStock = ({
|
|
|
1018
1790
|
);
|
|
1019
1791
|
if (!decoded || !retainPrevious && decoded.source !== glb) return null;
|
|
1020
1792
|
if (decoded.error) throw decoded.error;
|
|
1021
|
-
return decoded.geometries?.map((geometry) => /* @__PURE__ */
|
|
1793
|
+
return decoded.geometries?.map((geometry) => /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance }, geometry.uuid));
|
|
1022
1794
|
};
|
|
1023
1795
|
var keepNonFinite = (_key, value) => typeof value === "number" && !Number.isFinite(value) ? String(value) : value;
|
|
1024
1796
|
var restoreNonFinite = (_key, value) => value === "NaN" || value === "Infinity" || value === "-Infinity" ? Number(value) : value;
|
|
@@ -1049,28 +1821,28 @@ var ShapeStock = ({
|
|
|
1049
1821
|
},
|
|
1050
1822
|
keepNonFinite
|
|
1051
1823
|
);
|
|
1052
|
-
const geometry =
|
|
1824
|
+
const geometry = useMemo2(() => {
|
|
1053
1825
|
const options = JSON.parse(figure, restoreNonFinite);
|
|
1054
1826
|
const input = partGeometry ? { ...options, partGeometry } : options;
|
|
1055
1827
|
return isBox ? boxStockGeometry(input) : cylinderStockGeometry(input);
|
|
1056
1828
|
}, [figure, isBox, partGeometry]);
|
|
1057
|
-
|
|
1058
|
-
return /* @__PURE__ */
|
|
1829
|
+
useEffect3(() => () => geometry.dispose(), [geometry]);
|
|
1830
|
+
return /* @__PURE__ */ jsx5(GeometryStock, { geometry, ...appearance });
|
|
1059
1831
|
};
|
|
1060
1832
|
|
|
1061
1833
|
// src/banana.tsx
|
|
1062
1834
|
import { useGLTF } from "@react-three/drei";
|
|
1063
|
-
import { useThree as
|
|
1064
|
-
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";
|
|
1065
1837
|
|
|
1066
1838
|
// src/render/banana.ts
|
|
1067
|
-
import { Vector3 as
|
|
1839
|
+
import { Vector3 as Vector34 } from "three";
|
|
1068
1840
|
var BANANA_GAP = 0.1;
|
|
1069
1841
|
function bananaPosition(part, banana) {
|
|
1070
|
-
const size = part.getSize(new
|
|
1842
|
+
const size = part.getSize(new Vector34());
|
|
1071
1843
|
const reach = Math.max(size.x, size.y, size.z);
|
|
1072
|
-
const centre = part.getCenter(new
|
|
1073
|
-
return new
|
|
1844
|
+
const centre = part.getCenter(new Vector34());
|
|
1845
|
+
return new Vector34(
|
|
1074
1846
|
part.max.x + reach * BANANA_GAP - banana.min.x,
|
|
1075
1847
|
centre.y - (banana.min.y + banana.max.y) / 2,
|
|
1076
1848
|
part.min.z - banana.min.z
|
|
@@ -1081,16 +1853,16 @@ function bananaFrameBounds(part, banana, position) {
|
|
|
1081
1853
|
}
|
|
1082
1854
|
|
|
1083
1855
|
// src/banana.tsx
|
|
1084
|
-
import { jsx as
|
|
1856
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1085
1857
|
var BANANA_MODEL_URL = new URL("./assets/banana.glb", import.meta.url).href;
|
|
1086
1858
|
var SKIN = "#e8b530";
|
|
1087
1859
|
var FURNITURE = { [EXCLUDE_FROM_FRAME]: true };
|
|
1088
1860
|
var Banana = ({ onPlaced }) => {
|
|
1089
1861
|
const part = useContentBox();
|
|
1090
|
-
const invalidate =
|
|
1862
|
+
const invalidate = useThree3((state) => state.invalidate);
|
|
1091
1863
|
const gltf = useGLTF(BANANA_MODEL_URL);
|
|
1092
|
-
const group =
|
|
1093
|
-
const geometry =
|
|
1864
|
+
const group = useRef2(null);
|
|
1865
|
+
const geometry = useMemo3(() => {
|
|
1094
1866
|
let found = null;
|
|
1095
1867
|
gltf.scene.traverse((object) => {
|
|
1096
1868
|
const mesh = object;
|
|
@@ -1105,7 +1877,7 @@ var Banana = ({ onPlaced }) => {
|
|
|
1105
1877
|
own.computeBoundingBox();
|
|
1106
1878
|
return own;
|
|
1107
1879
|
}, [gltf]);
|
|
1108
|
-
|
|
1880
|
+
useLayoutEffect3(() => {
|
|
1109
1881
|
const node = group.current;
|
|
1110
1882
|
const own = geometry?.boundingBox;
|
|
1111
1883
|
if (!node || !own || part.isEmpty()) return;
|
|
@@ -1117,7 +1889,7 @@ var Banana = ({ onPlaced }) => {
|
|
|
1117
1889
|
invalidate();
|
|
1118
1890
|
}, [geometry, invalidate, onPlaced, part]);
|
|
1119
1891
|
if (!geometry) return null;
|
|
1120
|
-
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 }) }) });
|
|
1121
1893
|
};
|
|
1122
1894
|
|
|
1123
1895
|
// src/render/direction-highlights.ts
|
|
@@ -1136,17 +1908,17 @@ function directionHighlights(model, activeDirection = null) {
|
|
|
1136
1908
|
|
|
1137
1909
|
// src/primitives.tsx
|
|
1138
1910
|
import { GizmoHelper } from "@react-three/drei";
|
|
1139
|
-
import { useThree as
|
|
1140
|
-
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";
|
|
1141
1913
|
|
|
1142
1914
|
// src/render/grid.ts
|
|
1143
|
-
import { BufferGeometry as
|
|
1915
|
+
import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute4, Vector3 as Vector35 } from "three";
|
|
1144
1916
|
var STEPS = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1e3];
|
|
1145
1917
|
var TARGET_CELLS = 10;
|
|
1146
1918
|
var OVERHANG = 1.6;
|
|
1147
1919
|
function gridSpec(box) {
|
|
1148
|
-
const size = box.getSize(new
|
|
1149
|
-
const center = box.getCenter(new
|
|
1920
|
+
const size = box.getSize(new Vector35());
|
|
1921
|
+
const center = box.getCenter(new Vector35());
|
|
1150
1922
|
const largest = Math.max(size.x, size.y, 1e-6);
|
|
1151
1923
|
const ideal = largest / TARGET_CELLS;
|
|
1152
1924
|
const step = [...STEPS].reverse().find((candidate) => candidate <= ideal) ?? STEPS[0];
|
|
@@ -1172,8 +1944,8 @@ function gridGeometry(spec) {
|
|
|
1172
1944
|
z
|
|
1173
1945
|
);
|
|
1174
1946
|
}
|
|
1175
|
-
const geometry = new
|
|
1176
|
-
geometry.setAttribute("position", new
|
|
1947
|
+
const geometry = new BufferGeometry4();
|
|
1948
|
+
geometry.setAttribute("position", new Float32BufferAttribute4(positions, 3));
|
|
1177
1949
|
return geometry;
|
|
1178
1950
|
}
|
|
1179
1951
|
function gridFor(box, options = {}) {
|
|
@@ -1190,26 +1962,26 @@ function gridFor(box, options = {}) {
|
|
|
1190
1962
|
}
|
|
1191
1963
|
|
|
1192
1964
|
// src/primitives.tsx
|
|
1193
|
-
import { jsx as
|
|
1965
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1194
1966
|
var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1195
1967
|
var Grid = ({ step, extent, color, opacity = 0.35 }) => {
|
|
1196
1968
|
const theme = resolveTheme();
|
|
1197
1969
|
const box = useContentBox();
|
|
1198
|
-
const geometry =
|
|
1199
|
-
|
|
1970
|
+
const geometry = useMemo4(() => gridFor(box, { step, extent }), [box, extent, step]);
|
|
1971
|
+
useEffect4(() => () => geometry?.dispose(), [geometry]);
|
|
1200
1972
|
if (!geometry) return null;
|
|
1201
|
-
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 }) });
|
|
1202
1974
|
};
|
|
1203
|
-
var Axes = ({ size = 25 }) => /* @__PURE__ */
|
|
1975
|
+
var Axes = ({ size = 25 }) => /* @__PURE__ */ jsx7("axesHelper", { args: [size], userData: FURNITURE2, raycast: () => null });
|
|
1204
1976
|
var ViewCube = ({
|
|
1205
1977
|
alignment = "top-right",
|
|
1206
1978
|
margin = [80, 80],
|
|
1207
1979
|
theme,
|
|
1208
1980
|
onViewChange
|
|
1209
1981
|
}) => {
|
|
1210
|
-
const resolved =
|
|
1211
|
-
const zones =
|
|
1212
|
-
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 }) });
|
|
1213
1985
|
};
|
|
1214
1986
|
var CubePanels = ({
|
|
1215
1987
|
zones,
|
|
@@ -1217,15 +1989,15 @@ var CubePanels = ({
|
|
|
1217
1989
|
onViewChange
|
|
1218
1990
|
}) => {
|
|
1219
1991
|
const controls = useViewerControls();
|
|
1220
|
-
const invalidate =
|
|
1221
|
-
const groupRef =
|
|
1992
|
+
const invalidate = useThree4((state) => state.invalidate);
|
|
1993
|
+
const groupRef = useRef3(null);
|
|
1222
1994
|
const [hovered, setHovered] = useState3(null);
|
|
1223
|
-
const panels =
|
|
1995
|
+
const panels = useMemo4(
|
|
1224
1996
|
() => zones.map((zone) => ({ zone, geometry: panelGeometry(zone) })),
|
|
1225
1997
|
[zones]
|
|
1226
1998
|
);
|
|
1227
|
-
const outline =
|
|
1228
|
-
const labels =
|
|
1999
|
+
const outline = useMemo4(() => cubeOutlineGeometry(zones), [zones]);
|
|
2000
|
+
const labels = useMemo4(
|
|
1229
2001
|
() => zones.filter((zone) => zone.kind === "face").map((zone) => ({
|
|
1230
2002
|
zone,
|
|
1231
2003
|
geometry: labelGeometry(zone),
|
|
@@ -1233,7 +2005,7 @@ var CubePanels = ({
|
|
|
1233
2005
|
})),
|
|
1234
2006
|
[theme.cubeLabel, zones]
|
|
1235
2007
|
);
|
|
1236
|
-
|
|
2008
|
+
useEffect4(
|
|
1237
2009
|
() => () => {
|
|
1238
2010
|
for (const panel of panels) panel.geometry.dispose();
|
|
1239
2011
|
for (const label of labels) {
|
|
@@ -1252,8 +2024,8 @@ var CubePanels = ({
|
|
|
1252
2024
|
setHovered(name);
|
|
1253
2025
|
invalidate();
|
|
1254
2026
|
};
|
|
1255
|
-
return /* @__PURE__ */
|
|
1256
|
-
panels.map(({ zone, geometry }) => /* @__PURE__ */
|
|
2027
|
+
return /* @__PURE__ */ jsxs4("group", { ref: groupRef, scale: 38, children: [
|
|
2028
|
+
panels.map(({ zone, geometry }) => /* @__PURE__ */ jsx7(
|
|
1257
2029
|
"mesh",
|
|
1258
2030
|
{
|
|
1259
2031
|
geometry,
|
|
@@ -1266,7 +2038,7 @@ var CubePanels = ({
|
|
|
1266
2038
|
event.stopPropagation();
|
|
1267
2039
|
choose(zone);
|
|
1268
2040
|
},
|
|
1269
|
-
children: /* @__PURE__ */
|
|
2041
|
+
children: /* @__PURE__ */ jsx7(
|
|
1270
2042
|
"meshLambertMaterial",
|
|
1271
2043
|
{
|
|
1272
2044
|
color: hovered === zone.name ? theme.hover : theme.cube,
|
|
@@ -1278,27 +2050,27 @@ var CubePanels = ({
|
|
|
1278
2050
|
},
|
|
1279
2051
|
zone.name
|
|
1280
2052
|
)),
|
|
1281
|
-
labels.map(({ zone, geometry, texture }) => /* @__PURE__ */
|
|
1282
|
-
/* @__PURE__ */
|
|
1283
|
-
/* @__PURE__ */
|
|
1284
|
-
/* @__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 })
|
|
1285
2057
|
] });
|
|
1286
2058
|
};
|
|
1287
2059
|
|
|
1288
2060
|
// src/direction-arrows.tsx
|
|
1289
|
-
import { useThree as
|
|
1290
|
-
import { useEffect as
|
|
1291
|
-
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";
|
|
1292
2064
|
|
|
1293
2065
|
// src/render/directions.ts
|
|
1294
|
-
import { Vector3 as
|
|
2066
|
+
import { Vector3 as Vector36 } from "three";
|
|
1295
2067
|
var LENGTH = 0.33;
|
|
1296
2068
|
var GAP = 0.2;
|
|
1297
2069
|
function arrowPlacement(direction, box) {
|
|
1298
|
-
const center = box.getCenter(new
|
|
1299
|
-
const half = box.getSize(new
|
|
2070
|
+
const center = box.getCenter(new Vector36());
|
|
2071
|
+
const half = box.getSize(new Vector36()).multiplyScalar(0.5);
|
|
1300
2072
|
const radius = half.length() || 1;
|
|
1301
|
-
const axis = new
|
|
2073
|
+
const axis = new Vector36(direction.x, direction.y, direction.z);
|
|
1302
2074
|
if (axis.lengthSq() === 0) axis.set(0, 0, 1);
|
|
1303
2075
|
axis.normalize();
|
|
1304
2076
|
let exit = Number.POSITIVE_INFINITY;
|
|
@@ -1314,7 +2086,7 @@ function arrowPlacement(direction, box) {
|
|
|
1314
2086
|
}
|
|
1315
2087
|
|
|
1316
2088
|
// src/direction-arrows.tsx
|
|
1317
|
-
import { jsx as
|
|
2089
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1318
2090
|
var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1319
2091
|
var DirectionArrows = ({
|
|
1320
2092
|
directions,
|
|
@@ -1327,9 +2099,9 @@ var DirectionArrows = ({
|
|
|
1327
2099
|
visible = true
|
|
1328
2100
|
}) => {
|
|
1329
2101
|
const box = useContentBox();
|
|
1330
|
-
const resolved =
|
|
1331
|
-
const geometry =
|
|
1332
|
-
|
|
2102
|
+
const resolved = useMemo5(() => resolveTheme(theme), [theme]);
|
|
2103
|
+
const geometry = useMemo5(() => arrowGeometry(), []);
|
|
2104
|
+
useEffect5(() => () => geometry.dispose(), [geometry]);
|
|
1333
2105
|
const shown = shownDirection === void 0 ? activeDirection : shownDirection;
|
|
1334
2106
|
const drawn = (index) => {
|
|
1335
2107
|
if (shown === null) return true;
|
|
@@ -1341,10 +2113,10 @@ var DirectionArrows = ({
|
|
|
1341
2113
|
// Excluded from framing: the arrows are deliberately placed outside the
|
|
1342
2114
|
// part, so a Fit that measured them would frame the arrows and leave the
|
|
1343
2115
|
// part small in the middle of them.
|
|
1344
|
-
/* @__PURE__ */
|
|
2116
|
+
/* @__PURE__ */ jsxs5("group", { userData: FURNITURE3, children: [
|
|
1345
2117
|
directions.map((direction, index) => {
|
|
1346
2118
|
if (!drawn(index)) return null;
|
|
1347
|
-
return /* @__PURE__ */
|
|
2119
|
+
return /* @__PURE__ */ jsx8(
|
|
1348
2120
|
Arrow,
|
|
1349
2121
|
{
|
|
1350
2122
|
direction,
|
|
@@ -1357,7 +2129,7 @@ var DirectionArrows = ({
|
|
|
1357
2129
|
`candidate-${index}`
|
|
1358
2130
|
);
|
|
1359
2131
|
}),
|
|
1360
|
-
namedDirections.map((named) => /* @__PURE__ */
|
|
2132
|
+
namedDirections.map((named) => /* @__PURE__ */ jsx8(
|
|
1361
2133
|
Arrow,
|
|
1362
2134
|
{
|
|
1363
2135
|
direction: named.direction,
|
|
@@ -1369,7 +2141,7 @@ var DirectionArrows = ({
|
|
|
1369
2141
|
},
|
|
1370
2142
|
`named-${named.index}`
|
|
1371
2143
|
)),
|
|
1372
|
-
previewDirection ? /* @__PURE__ */
|
|
2144
|
+
previewDirection ? /* @__PURE__ */ jsx8(
|
|
1373
2145
|
Arrow,
|
|
1374
2146
|
{
|
|
1375
2147
|
direction: previewDirection,
|
|
@@ -1384,12 +2156,12 @@ var DirectionArrows = ({
|
|
|
1384
2156
|
);
|
|
1385
2157
|
};
|
|
1386
2158
|
var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }) => {
|
|
1387
|
-
const invalidate =
|
|
1388
|
-
const { tip, length, quaternion } =
|
|
2159
|
+
const invalidate = useThree5((state) => state.invalidate);
|
|
2160
|
+
const { tip, length, quaternion } = useMemo5(() => {
|
|
1389
2161
|
const placement = arrowPlacement(direction, box);
|
|
1390
2162
|
const aim = new Quaternion2().setFromUnitVectors(
|
|
1391
2163
|
ARROW_AXIS,
|
|
1392
|
-
new
|
|
2164
|
+
new Vector37(direction.x, direction.y, direction.z).normalize()
|
|
1393
2165
|
);
|
|
1394
2166
|
return { tip: placement.tip, length: placement.length, quaternion: aim };
|
|
1395
2167
|
}, [box, direction]);
|
|
@@ -1400,23 +2172,23 @@ var Arrow = ({ direction, box, geometry, color, opacity, onPick, onTop = false }
|
|
|
1400
2172
|
invalidate();
|
|
1401
2173
|
}
|
|
1402
2174
|
} : {};
|
|
1403
|
-
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 }) });
|
|
1404
2176
|
};
|
|
1405
2177
|
|
|
1406
2178
|
// src/section-tool.tsx
|
|
1407
|
-
import { useFrame, useThree as
|
|
1408
|
-
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";
|
|
1409
2181
|
import {
|
|
1410
|
-
BufferGeometry as
|
|
1411
|
-
DoubleSide,
|
|
2182
|
+
BufferGeometry as BufferGeometry5,
|
|
2183
|
+
DoubleSide as DoubleSide3,
|
|
1412
2184
|
Quaternion as Quaternion3,
|
|
1413
2185
|
Raycaster,
|
|
1414
2186
|
Vector2,
|
|
1415
|
-
Vector3 as
|
|
2187
|
+
Vector3 as Vector38
|
|
1416
2188
|
} from "three";
|
|
1417
|
-
import { Fragment, jsx as
|
|
2189
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1418
2190
|
var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1419
|
-
var Z = new
|
|
2191
|
+
var Z = new Vector38(0, 0, 1);
|
|
1420
2192
|
var AXIS_ROTATIONS = {
|
|
1421
2193
|
x: [0, Math.PI / 2, 0],
|
|
1422
2194
|
y: [Math.PI / 2, 0, 0],
|
|
@@ -1428,12 +2200,12 @@ var SectionTool = ({ theme }) => {
|
|
|
1428
2200
|
const store = useSectionStore();
|
|
1429
2201
|
const section = useSyncExternalStore(store.subscribe, store.get, store.get);
|
|
1430
2202
|
const box = useContentBox();
|
|
1431
|
-
const resolved =
|
|
1432
|
-
|
|
2203
|
+
const resolved = useMemo6(() => resolveTheme(theme), [theme]);
|
|
2204
|
+
useEffect6(() => {
|
|
1433
2205
|
store.setEngaged(true);
|
|
1434
2206
|
return () => store.setEngaged(false);
|
|
1435
2207
|
}, [store]);
|
|
1436
|
-
|
|
2208
|
+
useEffect6(() => {
|
|
1437
2209
|
if (!section) return void 0;
|
|
1438
2210
|
const clear = (event) => {
|
|
1439
2211
|
if (event.key === "Escape") store.set(null);
|
|
@@ -1442,43 +2214,43 @@ var SectionTool = ({ theme }) => {
|
|
|
1442
2214
|
return () => window.removeEventListener("keydown", clear);
|
|
1443
2215
|
}, [section, store]);
|
|
1444
2216
|
if (box.isEmpty()) return null;
|
|
1445
|
-
return /* @__PURE__ */
|
|
2217
|
+
return /* @__PURE__ */ jsx9("group", { userData: FURNITURE4, children: section ? /* @__PURE__ */ jsx9(CutSheet, { box, section, theme: resolved }) : /* @__PURE__ */ jsx9(Picker, { box, theme: resolved }) });
|
|
1446
2218
|
};
|
|
1447
2219
|
var useSquare = () => {
|
|
1448
|
-
const square =
|
|
1449
|
-
() => new
|
|
1450
|
-
new
|
|
1451
|
-
new
|
|
1452
|
-
new
|
|
1453
|
-
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)
|
|
1454
2226
|
]),
|
|
1455
2227
|
[]
|
|
1456
2228
|
);
|
|
1457
|
-
|
|
2229
|
+
useEffect6(() => () => square.dispose(), [square]);
|
|
1458
2230
|
return square;
|
|
1459
2231
|
};
|
|
1460
2232
|
var Picker = ({ box, theme }) => {
|
|
1461
2233
|
const store = useSectionStore();
|
|
1462
|
-
const camera =
|
|
1463
|
-
const scene =
|
|
1464
|
-
const domElement =
|
|
1465
|
-
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);
|
|
1466
2238
|
const isTap = useTapGuard();
|
|
1467
2239
|
const square = useSquare();
|
|
1468
|
-
const previewRef =
|
|
1469
|
-
const hover =
|
|
2240
|
+
const previewRef = useRef4(null);
|
|
2241
|
+
const hover = useRef4(null);
|
|
1470
2242
|
const [hoveredPlane, setHoveredPlane] = useState4(null);
|
|
1471
|
-
const span =
|
|
1472
|
-
const centre =
|
|
2243
|
+
const span = useMemo6(() => box.getSize(new Vector38()).length(), [box]);
|
|
2244
|
+
const centre = useMemo6(() => box.getCenter(new Vector38()), [box]);
|
|
1473
2245
|
const previewSize = span * PREVIEW_SCALE;
|
|
1474
|
-
|
|
2246
|
+
useEffect6(() => {
|
|
1475
2247
|
store.setPicking(true);
|
|
1476
2248
|
return () => store.setPicking(false);
|
|
1477
2249
|
}, [store]);
|
|
1478
|
-
|
|
2250
|
+
useEffect6(() => {
|
|
1479
2251
|
const raycaster = new Raycaster();
|
|
1480
2252
|
const pointer = new Vector2();
|
|
1481
|
-
const aim = new
|
|
2253
|
+
const aim = new Vector38();
|
|
1482
2254
|
const hide = () => {
|
|
1483
2255
|
if (!hover.current) return;
|
|
1484
2256
|
hover.current = null;
|
|
@@ -1525,7 +2297,7 @@ var Picker = ({ box, theme }) => {
|
|
|
1525
2297
|
}, [box, camera, domElement, invalidate, isTap, scene, span, store]);
|
|
1526
2298
|
const [planes, setPlanes] = useState4(() => axesPlanes(box, camera.position));
|
|
1527
2299
|
const key = planes.map((plane) => plane.sign).join();
|
|
1528
|
-
|
|
2300
|
+
useFrame2(() => {
|
|
1529
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();
|
|
1530
2302
|
if (next !== key) setPlanes(axesPlanes(box, camera.position));
|
|
1531
2303
|
});
|
|
@@ -1549,29 +2321,29 @@ var Picker = ({ box, theme }) => {
|
|
|
1549
2321
|
store.set(plane.options);
|
|
1550
2322
|
}
|
|
1551
2323
|
});
|
|
1552
|
-
return /* @__PURE__ */
|
|
1553
|
-
/* @__PURE__ */
|
|
1554
|
-
/* @__PURE__ */
|
|
1555
|
-
/* @__PURE__ */
|
|
1556
|
-
/* @__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(
|
|
1557
2329
|
"meshBasicMaterial",
|
|
1558
2330
|
{
|
|
1559
2331
|
color: theme.sectionOutline,
|
|
1560
2332
|
transparent: true,
|
|
1561
2333
|
opacity: 0.2,
|
|
1562
|
-
side:
|
|
2334
|
+
side: DoubleSide3,
|
|
1563
2335
|
depthWrite: false
|
|
1564
2336
|
}
|
|
1565
2337
|
)
|
|
1566
2338
|
] }),
|
|
1567
|
-
/* @__PURE__ */
|
|
2339
|
+
/* @__PURE__ */ jsx9(
|
|
1568
2340
|
"lineLoop",
|
|
1569
2341
|
{
|
|
1570
2342
|
geometry: square,
|
|
1571
2343
|
scale: previewSize / 2,
|
|
1572
2344
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
1573
2345
|
raycast: () => null,
|
|
1574
|
-
children: /* @__PURE__ */
|
|
2346
|
+
children: /* @__PURE__ */ jsx9(
|
|
1575
2347
|
"lineBasicMaterial",
|
|
1576
2348
|
{
|
|
1577
2349
|
color: theme.sectionOutline,
|
|
@@ -1584,33 +2356,33 @@ var Picker = ({ box, theme }) => {
|
|
|
1584
2356
|
}
|
|
1585
2357
|
)
|
|
1586
2358
|
] }),
|
|
1587
|
-
planes.map((plane) => /* @__PURE__ */
|
|
2359
|
+
planes.map((plane) => /* @__PURE__ */ jsxs6(
|
|
1588
2360
|
"group",
|
|
1589
2361
|
{
|
|
1590
2362
|
position: [plane.position.x, plane.position.y, plane.position.z],
|
|
1591
2363
|
rotation: AXIS_ROTATIONS[plane.axis],
|
|
1592
2364
|
children: [
|
|
1593
|
-
/* @__PURE__ */
|
|
1594
|
-
/* @__PURE__ */
|
|
1595
|
-
/* @__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(
|
|
1596
2368
|
"meshBasicMaterial",
|
|
1597
2369
|
{
|
|
1598
2370
|
color: AXIS_COLORS[plane.axis],
|
|
1599
2371
|
transparent: true,
|
|
1600
2372
|
opacity: hoveredPlane === plane.axis ? 0.45 : 0.3,
|
|
1601
|
-
side:
|
|
2373
|
+
side: DoubleSide3,
|
|
1602
2374
|
depthWrite: false
|
|
1603
2375
|
}
|
|
1604
2376
|
)
|
|
1605
2377
|
] }),
|
|
1606
|
-
/* @__PURE__ */
|
|
2378
|
+
/* @__PURE__ */ jsx9(
|
|
1607
2379
|
"lineLoop",
|
|
1608
2380
|
{
|
|
1609
2381
|
geometry: square,
|
|
1610
2382
|
scale: plane.size / 2,
|
|
1611
2383
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
1612
2384
|
raycast: () => null,
|
|
1613
|
-
children: /* @__PURE__ */
|
|
2385
|
+
children: /* @__PURE__ */ jsx9("lineBasicMaterial", { color: AXIS_COLORS[plane.axis], transparent: true, opacity: 0.7 })
|
|
1614
2386
|
}
|
|
1615
2387
|
)
|
|
1616
2388
|
]
|
|
@@ -1621,9 +2393,9 @@ var Picker = ({ box, theme }) => {
|
|
|
1621
2393
|
};
|
|
1622
2394
|
var CutSheet = ({ box, section, theme }) => {
|
|
1623
2395
|
const square = useSquare();
|
|
1624
|
-
const cut =
|
|
1625
|
-
const span =
|
|
1626
|
-
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(() => {
|
|
1627
2399
|
if (!cut) return null;
|
|
1628
2400
|
const position = sectionAnchor(box, cut.plane).addScaledVector(
|
|
1629
2401
|
cut.plane.normal,
|
|
@@ -1633,28 +2405,28 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
1633
2405
|
}, [box, cut, span]);
|
|
1634
2406
|
if (!placement) return null;
|
|
1635
2407
|
const size = span * OUTLINE_SCALE;
|
|
1636
|
-
return /* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
/* @__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(
|
|
1640
2412
|
"meshBasicMaterial",
|
|
1641
2413
|
{
|
|
1642
2414
|
color: theme.sectionOutline,
|
|
1643
2415
|
transparent: true,
|
|
1644
2416
|
opacity: 0.1,
|
|
1645
|
-
side:
|
|
2417
|
+
side: DoubleSide3,
|
|
1646
2418
|
depthWrite: false
|
|
1647
2419
|
}
|
|
1648
2420
|
)
|
|
1649
2421
|
] }),
|
|
1650
|
-
/* @__PURE__ */
|
|
2422
|
+
/* @__PURE__ */ jsx9(
|
|
1651
2423
|
"lineLoop",
|
|
1652
2424
|
{
|
|
1653
2425
|
geometry: square,
|
|
1654
2426
|
scale: size / 2,
|
|
1655
2427
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
1656
2428
|
raycast: () => null,
|
|
1657
|
-
children: /* @__PURE__ */
|
|
2429
|
+
children: /* @__PURE__ */ jsx9(
|
|
1658
2430
|
"lineBasicMaterial",
|
|
1659
2431
|
{
|
|
1660
2432
|
color: theme.sectionOutline,
|
|
@@ -1671,32 +2443,32 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
1671
2443
|
|
|
1672
2444
|
// src/measure-tool.tsx
|
|
1673
2445
|
import { Html, Line } from "@react-three/drei";
|
|
1674
|
-
import { useFrame as
|
|
2446
|
+
import { useFrame as useFrame3, useThree as useThree7 } from "@react-three/fiber";
|
|
1675
2447
|
import {
|
|
1676
|
-
useEffect as
|
|
1677
|
-
useLayoutEffect as
|
|
1678
|
-
useMemo as
|
|
1679
|
-
useRef as
|
|
2448
|
+
useEffect as useEffect7,
|
|
2449
|
+
useLayoutEffect as useLayoutEffect4,
|
|
2450
|
+
useMemo as useMemo7,
|
|
2451
|
+
useRef as useRef5,
|
|
1680
2452
|
useState as useState5,
|
|
1681
2453
|
useSyncExternalStore as useSyncExternalStore2
|
|
1682
2454
|
} from "react";
|
|
1683
2455
|
import {
|
|
1684
|
-
Matrix4 as
|
|
2456
|
+
Matrix4 as Matrix43,
|
|
1685
2457
|
Raycaster as Raycaster2,
|
|
1686
2458
|
SphereGeometry,
|
|
1687
2459
|
Vector2 as Vector22,
|
|
1688
|
-
Vector3 as
|
|
2460
|
+
Vector3 as Vector310
|
|
1689
2461
|
} from "three";
|
|
1690
2462
|
|
|
1691
2463
|
// src/render/resample.ts
|
|
1692
|
-
import { Vector3 as
|
|
2464
|
+
import { Vector3 as Vector39 } from "three";
|
|
1693
2465
|
var ON_PLANE = 1e-7;
|
|
1694
2466
|
var STITCH = 1e-5;
|
|
1695
2467
|
var COLLINEAR = 1e-6;
|
|
1696
2468
|
function clipSegments(positions, plane) {
|
|
1697
2469
|
const out = [];
|
|
1698
|
-
const a = new
|
|
1699
|
-
const b = new
|
|
2470
|
+
const a = new Vector39();
|
|
2471
|
+
const b = new Vector39();
|
|
1700
2472
|
const count = Math.floor(positions.length / 6);
|
|
1701
2473
|
for (let i = 0; i < count; i += 1) {
|
|
1702
2474
|
a.set(positions[i * 6], positions[i * 6 + 1], positions[i * 6 + 2]);
|
|
@@ -1711,11 +2483,11 @@ function clipSegments(positions, plane) {
|
|
|
1711
2483
|
return Float32Array.from(out);
|
|
1712
2484
|
}
|
|
1713
2485
|
function sectionContour(positions, regionOf, plane) {
|
|
1714
|
-
const a = new
|
|
1715
|
-
const b = new
|
|
1716
|
-
const c = new
|
|
1717
|
-
const p = new
|
|
1718
|
-
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();
|
|
1719
2491
|
const byRegion = /* @__PURE__ */ new Map();
|
|
1720
2492
|
const triangleCount2 = Math.floor(positions.length / 9);
|
|
1721
2493
|
for (let t = 0; t < triangleCount2; t += 1) {
|
|
@@ -1773,17 +2545,17 @@ function stitch(segments, out) {
|
|
|
1773
2545
|
return into.set(segments[o], segments[o + 1], segments[o + 2]);
|
|
1774
2546
|
};
|
|
1775
2547
|
const direction = (i, into) => {
|
|
1776
|
-
const s = new
|
|
1777
|
-
const e = new
|
|
2548
|
+
const s = new Vector39();
|
|
2549
|
+
const e = new Vector39();
|
|
1778
2550
|
point(i, 0, s);
|
|
1779
2551
|
point(i, 1, e);
|
|
1780
2552
|
return into.subVectors(e, s).normalize();
|
|
1781
2553
|
};
|
|
1782
2554
|
const consumed = new Uint8Array(count);
|
|
1783
|
-
const d = new
|
|
1784
|
-
const n = new
|
|
2555
|
+
const d = new Vector39();
|
|
2556
|
+
const n = new Vector39();
|
|
1785
2557
|
const next = (i, end) => {
|
|
1786
|
-
const at = point(i, end, new
|
|
2558
|
+
const at = point(i, end, new Vector39());
|
|
1787
2559
|
const list = ends.get(key(at.x, at.y, at.z)) ?? [];
|
|
1788
2560
|
if (list.length !== 2) return null;
|
|
1789
2561
|
const other = list.find((entry) => entry >> 1 !== i);
|
|
@@ -1812,8 +2584,8 @@ function stitch(segments, out) {
|
|
|
1812
2584
|
};
|
|
1813
2585
|
const back = walk(0);
|
|
1814
2586
|
const forward = walk(1);
|
|
1815
|
-
const s = point(back.index, back.end, new
|
|
1816
|
-
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());
|
|
1817
2589
|
out.push(s.x, s.y, s.z, e.x, e.y, e.z);
|
|
1818
2590
|
}
|
|
1819
2591
|
}
|
|
@@ -1821,8 +2593,8 @@ function pointInContour(point, plane, contour) {
|
|
|
1821
2593
|
const [u, v] = planeAxes(plane);
|
|
1822
2594
|
const px = point.dot(u);
|
|
1823
2595
|
const py = point.dot(v);
|
|
1824
|
-
const a = new
|
|
1825
|
-
const b = new
|
|
2596
|
+
const a = new Vector39();
|
|
2597
|
+
const b = new Vector39();
|
|
1826
2598
|
let inside = false;
|
|
1827
2599
|
const count = Math.floor(contour.length / 6);
|
|
1828
2600
|
for (let i = 0; i < count; i += 1) {
|
|
@@ -1839,15 +2611,15 @@ function pointInContour(point, plane, contour) {
|
|
|
1839
2611
|
return inside;
|
|
1840
2612
|
}
|
|
1841
2613
|
function capHit(ray, plane, contour) {
|
|
1842
|
-
const point = ray.intersectPlane(plane, new
|
|
2614
|
+
const point = ray.intersectPlane(plane, new Vector39());
|
|
1843
2615
|
if (!point) return null;
|
|
1844
2616
|
return pointInContour(point, plane, contour) ? point : null;
|
|
1845
2617
|
}
|
|
1846
2618
|
function planeAxes(plane) {
|
|
1847
2619
|
const n = plane.normal;
|
|
1848
|
-
const seed = Math.abs(n.x) < 0.9 ? new
|
|
1849
|
-
const u = new
|
|
1850
|
-
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();
|
|
1851
2623
|
return [u, v];
|
|
1852
2624
|
}
|
|
1853
2625
|
function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
@@ -1860,7 +2632,7 @@ function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
|
1860
2632
|
}
|
|
1861
2633
|
|
|
1862
2634
|
// src/measure-tool.tsx
|
|
1863
|
-
import { Fragment as
|
|
2635
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1864
2636
|
var FURNITURE5 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1865
2637
|
var MEASURE_LABEL_CLASS = "toolpath-measure-label";
|
|
1866
2638
|
var MeasureTool = ({
|
|
@@ -1874,26 +2646,26 @@ var MeasureTool = ({
|
|
|
1874
2646
|
}) => {
|
|
1875
2647
|
const store = useSectionStore();
|
|
1876
2648
|
const box = useContentBox();
|
|
1877
|
-
const resolved =
|
|
2649
|
+
const resolved = useMemo7(() => resolveTheme(theme), [theme]);
|
|
1878
2650
|
const picking = useSyncExternalStore2(store.subscribe, store.isPicking, store.isPicking);
|
|
1879
2651
|
const [held, setHeld] = useState5([]);
|
|
1880
2652
|
const controlled = measurements !== void 0;
|
|
1881
2653
|
const list = controlled ? measurements : held;
|
|
1882
2654
|
const [draft, setDraft] = useState5([]);
|
|
1883
|
-
|
|
2655
|
+
useEffect7(() => {
|
|
1884
2656
|
store.setEngaged(true);
|
|
1885
2657
|
return () => store.setEngaged(false);
|
|
1886
2658
|
}, [store]);
|
|
1887
|
-
|
|
2659
|
+
useEffect7(() => {
|
|
1888
2660
|
setDraft([]);
|
|
1889
2661
|
}, [mode]);
|
|
1890
2662
|
const commit = (next) => {
|
|
1891
2663
|
if (!controlled) setHeld(next);
|
|
1892
2664
|
onChange?.(next);
|
|
1893
2665
|
};
|
|
1894
|
-
const latest =
|
|
2666
|
+
const latest = useRef5({ list, commit });
|
|
1895
2667
|
latest.current = { list, commit };
|
|
1896
|
-
|
|
2668
|
+
useEffect7(() => {
|
|
1897
2669
|
const onKey = (event) => {
|
|
1898
2670
|
if (event.key === "Escape") {
|
|
1899
2671
|
setDraft([]);
|
|
@@ -1921,8 +2693,8 @@ var MeasureTool = ({
|
|
|
1921
2693
|
};
|
|
1922
2694
|
if (box.isEmpty()) return null;
|
|
1923
2695
|
const label = className(labelClassName);
|
|
1924
|
-
return /* @__PURE__ */
|
|
1925
|
-
picking ? null : /* @__PURE__ */
|
|
2696
|
+
return /* @__PURE__ */ jsxs7("group", { userData: FURNITURE5, children: [
|
|
2697
|
+
picking ? null : /* @__PURE__ */ jsx10(
|
|
1926
2698
|
Snapper,
|
|
1927
2699
|
{
|
|
1928
2700
|
mode,
|
|
@@ -1933,7 +2705,7 @@ var MeasureTool = ({
|
|
|
1933
2705
|
label
|
|
1934
2706
|
}
|
|
1935
2707
|
),
|
|
1936
|
-
draft.map((point, index) => /* @__PURE__ */
|
|
2708
|
+
draft.map((point, index) => /* @__PURE__ */ jsx10(
|
|
1937
2709
|
ScreenDot,
|
|
1938
2710
|
{
|
|
1939
2711
|
point,
|
|
@@ -1942,8 +2714,8 @@ var MeasureTool = ({
|
|
|
1942
2714
|
},
|
|
1943
2715
|
index
|
|
1944
2716
|
)),
|
|
1945
|
-
draft.length === 2 ? /* @__PURE__ */
|
|
1946
|
-
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(
|
|
1947
2719
|
MeasurementView,
|
|
1948
2720
|
{
|
|
1949
2721
|
measurement,
|
|
@@ -1957,28 +2729,28 @@ var MeasureTool = ({
|
|
|
1957
2729
|
] });
|
|
1958
2730
|
};
|
|
1959
2731
|
var NO_POINTER = { pointerEvents: "none" };
|
|
1960
|
-
var NOWHERE = [new
|
|
2732
|
+
var NOWHERE = [new Vector310(), new Vector310()];
|
|
1961
2733
|
var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
1962
|
-
const camera =
|
|
1963
|
-
const scene =
|
|
1964
|
-
const size =
|
|
1965
|
-
const domElement =
|
|
1966
|
-
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);
|
|
1967
2739
|
const isTap = useTapGuard();
|
|
1968
|
-
const marker =
|
|
1969
|
-
const edgeLine =
|
|
1970
|
-
const band =
|
|
1971
|
-
const bandLabel =
|
|
1972
|
-
const bandText =
|
|
1973
|
-
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);
|
|
1974
2746
|
const dot = useDot();
|
|
1975
|
-
|
|
2747
|
+
useLayoutEffect4(() => {
|
|
1976
2748
|
if (edgeLine.current) edgeLine.current.visible = false;
|
|
1977
2749
|
if (band.current) band.current.visible = false;
|
|
1978
2750
|
}, []);
|
|
1979
|
-
const current =
|
|
2751
|
+
const current = useRef5({ mode, draft, onPlace, format, size });
|
|
1980
2752
|
current.current = { mode, draft, onPlace, format, size };
|
|
1981
|
-
|
|
2753
|
+
useEffect7(() => {
|
|
1982
2754
|
const raycaster = new Raycaster2();
|
|
1983
2755
|
const pointer = new Vector22();
|
|
1984
2756
|
const samples = new SampleCache();
|
|
@@ -2010,7 +2782,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2010
2782
|
const snapped = snapAt(target, target.edges, camera, viewport);
|
|
2011
2783
|
const from = placed[placed.length - 1];
|
|
2012
2784
|
const lock = shift && from ? lockToAxis(from, snapped.point) : null;
|
|
2013
|
-
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;
|
|
2014
2786
|
snap.current = found;
|
|
2015
2787
|
const node = marker.current;
|
|
2016
2788
|
if (node) {
|
|
@@ -2084,7 +2856,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2084
2856
|
window.removeEventListener("keyup", key);
|
|
2085
2857
|
};
|
|
2086
2858
|
}, [camera, domElement, invalidate, isTap, scene, theme.measure]);
|
|
2087
|
-
|
|
2859
|
+
useEffect7(() => {
|
|
2088
2860
|
if (draft.length === 0 && band.current) {
|
|
2089
2861
|
band.current.visible = false;
|
|
2090
2862
|
if (bandLabel.current) bandLabel.current.visible = false;
|
|
@@ -2095,9 +2867,9 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2095
2867
|
invalidate();
|
|
2096
2868
|
}
|
|
2097
2869
|
}, [draft, invalidate]);
|
|
2098
|
-
return /* @__PURE__ */
|
|
2099
|
-
/* @__PURE__ */
|
|
2100
|
-
/* @__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(
|
|
2101
2873
|
Line,
|
|
2102
2874
|
{
|
|
2103
2875
|
ref: edgeLine,
|
|
@@ -2111,7 +2883,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2111
2883
|
raycast: () => null
|
|
2112
2884
|
}
|
|
2113
2885
|
),
|
|
2114
|
-
/* @__PURE__ */
|
|
2886
|
+
/* @__PURE__ */ jsx10(
|
|
2115
2887
|
Line,
|
|
2116
2888
|
{
|
|
2117
2889
|
ref: band,
|
|
@@ -2125,7 +2897,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
2125
2897
|
raycast: () => null
|
|
2126
2898
|
}
|
|
2127
2899
|
),
|
|
2128
|
-
/* @__PURE__ */
|
|
2900
|
+
/* @__PURE__ */ jsx10("group", { ref: bandLabel, visible: false, children: /* @__PURE__ */ jsx10(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx10(
|
|
2129
2901
|
"div",
|
|
2130
2902
|
{
|
|
2131
2903
|
ref: bandText,
|
|
@@ -2172,7 +2944,7 @@ function targetUnderRay(raycaster, scene, samples) {
|
|
|
2172
2944
|
edges: samples.edgesFor(hit.object)
|
|
2173
2945
|
};
|
|
2174
2946
|
}
|
|
2175
|
-
const local = new
|
|
2947
|
+
const local = new Matrix43();
|
|
2176
2948
|
for (const mesh of cutMeshes(scene)) {
|
|
2177
2949
|
const planes = clippingPlanesOf(mesh);
|
|
2178
2950
|
const sample = samples.for(mesh, planes);
|
|
@@ -2235,7 +3007,7 @@ var SampleCache = class {
|
|
|
2235
3007
|
const key = planes.map((plane) => `${plane.normal.x},${plane.normal.y},${plane.normal.z},${plane.constant}`).join("|");
|
|
2236
3008
|
const cached = this.held.get(mesh);
|
|
2237
3009
|
if (cached && cached.key === key) return cached;
|
|
2238
|
-
const local = new
|
|
3010
|
+
const local = new Matrix43().copy(mesh.matrixWorld).invert();
|
|
2239
3011
|
const localPlanes = planes.map((plane) => plane.clone().applyMatrix4(local));
|
|
2240
3012
|
const positions = mesh.geometry.getAttribute("position")?.array ?? [];
|
|
2241
3013
|
const regionOf = this.regionsOf(mesh.geometry);
|
|
@@ -2290,37 +3062,37 @@ var MeasurementView = ({
|
|
|
2290
3062
|
const anchor = measurementLabelAnchor(measurement);
|
|
2291
3063
|
if (measurement.kind === "distance") {
|
|
2292
3064
|
const [a2, b2] = measurement.points;
|
|
2293
|
-
return /* @__PURE__ */
|
|
2294
|
-
/* @__PURE__ */
|
|
2295
|
-
/* @__PURE__ */
|
|
2296
|
-
/* @__PURE__ */
|
|
2297
|
-
showDeltas ? /* @__PURE__ */
|
|
2298
|
-
/* @__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 })
|
|
2299
3071
|
] });
|
|
2300
3072
|
}
|
|
2301
3073
|
const [a, vertex, b] = measurement.points;
|
|
2302
3074
|
const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b));
|
|
2303
|
-
return /* @__PURE__ */
|
|
2304
|
-
/* @__PURE__ */
|
|
2305
|
-
/* @__PURE__ */
|
|
2306
|
-
/* @__PURE__ */
|
|
2307
|
-
/* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
2309
|
-
/* @__PURE__ */
|
|
2310
|
-
/* @__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 })
|
|
2311
3083
|
] });
|
|
2312
3084
|
};
|
|
2313
3085
|
var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
2314
|
-
const camera =
|
|
2315
|
-
const size =
|
|
2316
|
-
const invalidate =
|
|
2317
|
-
const legs =
|
|
2318
|
-
const lines =
|
|
2319
|
-
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(() => {
|
|
2320
3092
|
const at = midpoint(a, b);
|
|
2321
|
-
return new
|
|
3093
|
+
return new Vector310(at.x, at.y, at.z);
|
|
2322
3094
|
}, [a, b]);
|
|
2323
|
-
|
|
3095
|
+
useFrame3(() => {
|
|
2324
3096
|
const dash = screenLength(camera, centre, size, DELTA_DASH_PIXELS);
|
|
2325
3097
|
let changed = false;
|
|
2326
3098
|
for (const line of lines.current) {
|
|
@@ -2332,16 +3104,16 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
2332
3104
|
}
|
|
2333
3105
|
if (changed) invalidate();
|
|
2334
3106
|
});
|
|
2335
|
-
return /* @__PURE__ */
|
|
2336
|
-
legs.map((leg, index) => /* @__PURE__ */
|
|
3107
|
+
return /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
3108
|
+
legs.map((leg, index) => /* @__PURE__ */ jsx10(
|
|
2337
3109
|
Line,
|
|
2338
3110
|
{
|
|
2339
3111
|
ref: (line) => {
|
|
2340
3112
|
lines.current[index] = line;
|
|
2341
3113
|
},
|
|
2342
3114
|
points: [
|
|
2343
|
-
new
|
|
2344
|
-
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)
|
|
2345
3117
|
],
|
|
2346
3118
|
color: AXIS_COLORS[leg.axis],
|
|
2347
3119
|
lineWidth: MEASURE_LINE_PIXELS.leg,
|
|
@@ -2356,7 +3128,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
2356
3128
|
},
|
|
2357
3129
|
leg.axis
|
|
2358
3130
|
)),
|
|
2359
|
-
legs.map((leg) => /* @__PURE__ */
|
|
3131
|
+
legs.map((leg) => /* @__PURE__ */ jsx10(
|
|
2360
3132
|
Label,
|
|
2361
3133
|
{
|
|
2362
3134
|
at: midpoint(leg.from, leg.to),
|
|
@@ -2370,15 +3142,15 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
2370
3142
|
))
|
|
2371
3143
|
] });
|
|
2372
3144
|
};
|
|
2373
|
-
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 }) }) });
|
|
2374
3146
|
var ScreenDot = ({ point, pixels, color }) => {
|
|
2375
|
-
const camera =
|
|
2376
|
-
const size =
|
|
2377
|
-
const invalidate =
|
|
2378
|
-
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);
|
|
2379
3151
|
const dot = useDot();
|
|
2380
|
-
const position =
|
|
2381
|
-
|
|
3152
|
+
const position = useMemo7(() => new Vector310(point.x, point.y, point.z), [point]);
|
|
3153
|
+
useFrame3(() => {
|
|
2382
3154
|
const group = node.current;
|
|
2383
3155
|
if (!group) return;
|
|
2384
3156
|
const length = screenLength(camera, position, size, pixels);
|
|
@@ -2387,21 +3159,21 @@ var ScreenDot = ({ point, pixels, color }) => {
|
|
|
2387
3159
|
invalidate();
|
|
2388
3160
|
}
|
|
2389
3161
|
});
|
|
2390
|
-
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 }) }) });
|
|
2391
3163
|
};
|
|
2392
3164
|
var Segment = ({ points, color }) => {
|
|
2393
3165
|
const [a, b] = points;
|
|
2394
|
-
const ends =
|
|
2395
|
-
() => [new
|
|
3166
|
+
const ends = useMemo7(
|
|
3167
|
+
() => [new Vector310(a.x, a.y, a.z), new Vector310(b.x, b.y, b.z)],
|
|
2396
3168
|
// Keyed on the numbers: a draft's tuple is a fresh array on every render,
|
|
2397
3169
|
// and a rebuilt array would rebuild the line's geometry with it.
|
|
2398
3170
|
[a.x, a.y, a.z, b.x, b.y, b.z]
|
|
2399
3171
|
);
|
|
2400
|
-
return /* @__PURE__ */
|
|
3172
|
+
return /* @__PURE__ */ jsx10(Polyline, { points: ends, color });
|
|
2401
3173
|
};
|
|
2402
3174
|
var Polyline = ({ points, color }) => {
|
|
2403
3175
|
if (points.length < 2) return null;
|
|
2404
|
-
return /* @__PURE__ */
|
|
3176
|
+
return /* @__PURE__ */ jsx10(
|
|
2405
3177
|
Line,
|
|
2406
3178
|
{
|
|
2407
3179
|
points,
|
|
@@ -2416,8 +3188,8 @@ var Polyline = ({ points, color }) => {
|
|
|
2416
3188
|
);
|
|
2417
3189
|
};
|
|
2418
3190
|
function useDot() {
|
|
2419
|
-
const geometry =
|
|
2420
|
-
|
|
3191
|
+
const geometry = useMemo7(() => new SphereGeometry(1, 16, 12), []);
|
|
3192
|
+
useEffect7(() => () => geometry.dispose(), [geometry]);
|
|
2421
3193
|
return geometry;
|
|
2422
3194
|
}
|
|
2423
3195
|
function isEditing(target) {
|
|
@@ -2430,11 +3202,11 @@ function className(extra) {
|
|
|
2430
3202
|
|
|
2431
3203
|
// src/tool-marks.tsx
|
|
2432
3204
|
import { Html as Html2, Line as Line2 } from "@react-three/drei";
|
|
2433
|
-
import { useMemo as
|
|
2434
|
-
import { DoubleSide as
|
|
3205
|
+
import { useMemo as useMemo8 } from "react";
|
|
3206
|
+
import { DoubleSide as DoubleSide4 } from "three";
|
|
2435
3207
|
|
|
2436
3208
|
// src/render/tool-marks.ts
|
|
2437
|
-
import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as
|
|
3209
|
+
import { Quaternion as Quaternion4, Vector2 as Vector23, Vector3 as Vector311 } from "three";
|
|
2438
3210
|
var TOOL_CORNER_STEPS = 12;
|
|
2439
3211
|
var TOOL_ROUND = 64;
|
|
2440
3212
|
function toolProfile(radius, corner, height) {
|
|
@@ -2454,9 +3226,9 @@ function toolProfile(radius, corner, height) {
|
|
|
2454
3226
|
points.push(new Vector23(radius, Math.max(height, rounded)));
|
|
2455
3227
|
return points;
|
|
2456
3228
|
}
|
|
2457
|
-
var UP = new
|
|
2458
|
-
var AXES = [new
|
|
2459
|
-
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);
|
|
2460
3232
|
var squareTo = (w, along) => {
|
|
2461
3233
|
const wanted = along ? toVector(along) : null;
|
|
2462
3234
|
const flat = wanted?.clone().addScaledVector(w, -wanted.dot(w));
|
|
@@ -2476,7 +3248,7 @@ function toolMarkShape(mark) {
|
|
|
2476
3248
|
if (!(mark.diameter > 0) || axis.lengthSq() === 0) return null;
|
|
2477
3249
|
const w = axis.normalize();
|
|
2478
3250
|
const u = squareTo(w, mark.across);
|
|
2479
|
-
const v = new
|
|
3251
|
+
const v = new Vector311().crossVectors(w, u);
|
|
2480
3252
|
const radius = mark.diameter / 2;
|
|
2481
3253
|
const corner = Math.min(Math.max(mark.cornerRadius ?? 0, 0), radius);
|
|
2482
3254
|
const height = Math.max(mark.height, corner);
|
|
@@ -2506,13 +3278,12 @@ function toolMarkShape(mark) {
|
|
|
2506
3278
|
}
|
|
2507
3279
|
|
|
2508
3280
|
// src/tool-marks.tsx
|
|
2509
|
-
import { jsx as
|
|
3281
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2510
3282
|
var BODY = { color: 16317180, opacity: 0.32 };
|
|
2511
3283
|
var NO_POINTER2 = { pointerEvents: "none" };
|
|
2512
|
-
var ignoreRay = () => void 0;
|
|
2513
3284
|
var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
|
|
2514
3285
|
const color = resolveTheme(theme).measure;
|
|
2515
|
-
const shapes =
|
|
3286
|
+
const shapes = useMemo8(
|
|
2516
3287
|
() => marks.flatMap((mark) => {
|
|
2517
3288
|
const shape = toolMarkShape(mark);
|
|
2518
3289
|
return shape ? [{ mark, shape }] : [];
|
|
@@ -2520,9 +3291,9 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
|
|
|
2520
3291
|
[marks]
|
|
2521
3292
|
);
|
|
2522
3293
|
const label = labelClassName ? `${MEASURE_LABEL_CLASS} ${labelClassName}` : MEASURE_LABEL_CLASS;
|
|
2523
|
-
return /* @__PURE__ */
|
|
2524
|
-
/* @__PURE__ */
|
|
2525
|
-
[...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(
|
|
2526
3297
|
Line2,
|
|
2527
3298
|
{
|
|
2528
3299
|
points,
|
|
@@ -2535,16 +3306,16 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
|
|
|
2535
3306
|
},
|
|
2536
3307
|
line
|
|
2537
3308
|
)),
|
|
2538
|
-
mark.label && visible ? /* @__PURE__ */
|
|
2539
|
-
/* @__PURE__ */
|
|
2540
|
-
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: [
|
|
2541
3312
|
" ",
|
|
2542
3313
|
mark.note
|
|
2543
3314
|
] }) : null
|
|
2544
3315
|
] }) }) : null
|
|
2545
3316
|
] }, index)) });
|
|
2546
3317
|
};
|
|
2547
|
-
var ToolBody = ({ shape }) => /* @__PURE__ */
|
|
3318
|
+
var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs8(
|
|
2548
3319
|
"mesh",
|
|
2549
3320
|
{
|
|
2550
3321
|
position: shape.base,
|
|
@@ -2552,15 +3323,15 @@ var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs7(
|
|
|
2552
3323
|
raycast: ignoreRay,
|
|
2553
3324
|
renderOrder: MEASURE_RENDER_ORDER - 1,
|
|
2554
3325
|
children: [
|
|
2555
|
-
/* @__PURE__ */
|
|
2556
|
-
/* @__PURE__ */
|
|
3326
|
+
/* @__PURE__ */ jsx11("latheGeometry", { args: [shape.profile, 64] }),
|
|
3327
|
+
/* @__PURE__ */ jsx11(
|
|
2557
3328
|
"meshBasicMaterial",
|
|
2558
3329
|
{
|
|
2559
3330
|
color: BODY.color,
|
|
2560
3331
|
transparent: true,
|
|
2561
3332
|
opacity: BODY.opacity,
|
|
2562
3333
|
depthWrite: false,
|
|
2563
|
-
side:
|
|
3334
|
+
side: DoubleSide4
|
|
2564
3335
|
}
|
|
2565
3336
|
)
|
|
2566
3337
|
]
|
|
@@ -2626,6 +3397,7 @@ export {
|
|
|
2626
3397
|
DirectionArrows,
|
|
2627
3398
|
EXCLUDE_FROM_FRAME,
|
|
2628
3399
|
EnginePart,
|
|
3400
|
+
ExplodedPart,
|
|
2629
3401
|
ExtendedCameraControls,
|
|
2630
3402
|
FEATURE_TYPE_RANKS,
|
|
2631
3403
|
Grid,
|
|
@@ -2699,6 +3471,7 @@ export {
|
|
|
2699
3471
|
clipSegments,
|
|
2700
3472
|
contentBounds,
|
|
2701
3473
|
createCapMaterial,
|
|
3474
|
+
createExplodeAmount,
|
|
2702
3475
|
createMaskMaterial,
|
|
2703
3476
|
createMaskTarget,
|
|
2704
3477
|
createPart,
|