@inweb/viewer-three 27.2.2 → 27.2.3

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.
@@ -33644,41 +33644,6 @@ void main() {
33644
33644
  }
33645
33645
  }
33646
33646
 
33647
- class PlaneHelper2 extends Line$1 {
33648
- constructor(size = 1, color = 0xc0c0c0) {
33649
- const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
33650
- const geometry = new BufferGeometry();
33651
- geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
33652
- geometry.computeBoundingSphere();
33653
- super(geometry, new LineBasicMaterial({ color, toneMapped: false }));
33654
- this.type = "PlaneHelper2";
33655
- this.size = size;
33656
- const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
33657
- const geometry2 = new BufferGeometry();
33658
- geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
33659
- geometry2.computeBoundingSphere();
33660
- this.helper = new Mesh(geometry2, new MeshBasicMaterial({
33661
- color,
33662
- opacity: 0.2,
33663
- transparent: true,
33664
- depthWrite: false,
33665
- toneMapped: false,
33666
- side: DoubleSide,
33667
- }));
33668
- this.add(this.helper);
33669
- }
33670
- dispose() {
33671
- this.geometry.dispose();
33672
- this.material.dispose();
33673
- this.helper.geometry.dispose();
33674
- this.helper.material.dispose();
33675
- }
33676
- updateMatrixWorld(force) {
33677
- this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
33678
- super.updateMatrixWorld(force);
33679
- }
33680
- }
33681
-
33682
33647
  const _changeEvent = { type: "change" };
33683
33648
  const _startEvent = { type: "start" };
33684
33649
  const _endEvent = { type: "end" };
@@ -34337,6 +34302,166 @@ void main() {
34337
34302
  }
34338
34303
  }
34339
34304
 
34305
+ class PlaneHelper2 extends Object3D {
34306
+ constructor(size = 1, color = 0xf0f0f0, opacity = 0.15) {
34307
+ super();
34308
+ this.type = "PlaneHelper2";
34309
+ this.size = size;
34310
+ const positions = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0];
34311
+ const geometry = new BufferGeometry();
34312
+ geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
34313
+ geometry.computeBoundingSphere();
34314
+ this.outline = new Line$1(geometry, new LineBasicMaterial({ color, toneMapped: false }));
34315
+ const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
34316
+ const geometry2 = new BufferGeometry();
34317
+ geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
34318
+ geometry2.computeBoundingSphere();
34319
+ this.mesh = new Mesh(geometry2, new MeshBasicMaterial({
34320
+ color,
34321
+ opacity,
34322
+ transparent: true,
34323
+ depthWrite: false,
34324
+ toneMapped: false,
34325
+ side: DoubleSide,
34326
+ }));
34327
+ this.add(this.outline);
34328
+ this.add(this.mesh);
34329
+ }
34330
+ dispose() {
34331
+ this.outline.geometry.dispose();
34332
+ this.outline.material.dispose();
34333
+ this.mesh.geometry.dispose();
34334
+ this.mesh.material.dispose();
34335
+ }
34336
+ updateMatrixWorld(force) {
34337
+ this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
34338
+ super.updateMatrixWorld(force);
34339
+ }
34340
+ getLineMaterial() {
34341
+ return this.outline.material;
34342
+ }
34343
+ getMeshMaterial() {
34344
+ return this.mesh.material;
34345
+ }
34346
+ }
34347
+
34348
+ const DESKTOP_SNAP_DISTANCE = 10;
34349
+ const MOBILE_SNAP_DISTANCE = 50;
34350
+ const _vertex = new Vector3();
34351
+ const _start$1 = new Vector3();
34352
+ const _end$1 = new Vector3();
34353
+ const _line = new Line3();
34354
+ const _center = new Vector3();
34355
+ const _projection = new Vector3();
34356
+ class Snapper {
34357
+ constructor(camera, renderer, canvas) {
34358
+ this.camera = camera;
34359
+ this.renderer = renderer;
34360
+ this.canvas = canvas;
34361
+ this.threshold = 0.0001;
34362
+ this.raycaster = new Raycaster();
34363
+ this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
34364
+ this.edgesCache = new WeakMap();
34365
+ }
34366
+ isMobile() {
34367
+ if (typeof navigator === "undefined")
34368
+ return false;
34369
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
34370
+ }
34371
+ getMousePosition(event, target) {
34372
+ return target.set(event.clientX, event.clientY);
34373
+ }
34374
+ getPointerIntersects(mouse, objects, recursive = false, clip = true) {
34375
+ const rect = this.canvas.getBoundingClientRect();
34376
+ const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
34377
+ const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
34378
+ const coords = new Vector2(x, y);
34379
+ this.raycaster.setFromCamera(coords, this.camera);
34380
+ this.raycaster.params = {
34381
+ Mesh: {},
34382
+ Line: { threshold: this.threshold },
34383
+ Line2: { threshold: this.threshold },
34384
+ LOD: {},
34385
+ Points: { threshold: this.threshold },
34386
+ Sprite: {},
34387
+ };
34388
+ let intersects = this.raycaster.intersectObjects(objects, recursive);
34389
+ if (clip) {
34390
+ const clippingPlanes = this.renderer.clippingPlanes || [];
34391
+ clippingPlanes.forEach((plane) => {
34392
+ intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
34393
+ });
34394
+ }
34395
+ return intersects;
34396
+ }
34397
+ getDetectRadius(point) {
34398
+ const camera = this.camera;
34399
+ if (camera.isOrthographicCamera) {
34400
+ const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
34401
+ const canvasHeight = this.canvas.height;
34402
+ const worldUnitsPerPixel = fieldHeight / canvasHeight;
34403
+ return this.detectRadiusInPixels * worldUnitsPerPixel;
34404
+ }
34405
+ if (camera.isPerspectiveCamera) {
34406
+ const distance = camera.position.distanceTo(point);
34407
+ const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
34408
+ const canvasHeight = this.canvas.height;
34409
+ const worldUnitsPerPixel = fieldHeight / canvasHeight;
34410
+ return this.detectRadiusInPixels * worldUnitsPerPixel;
34411
+ }
34412
+ return 0.1;
34413
+ }
34414
+ getSnapPoint(mouse, objects) {
34415
+ const intersections = this.getPointerIntersects(mouse, objects);
34416
+ if (intersections.length === 0)
34417
+ return undefined;
34418
+ const object = intersections[0].object;
34419
+ const intersectionPoint = intersections[0].point;
34420
+ const localPoint = object.worldToLocal(intersectionPoint.clone());
34421
+ let snapPoint;
34422
+ let snapDistance = this.getDetectRadius(intersectionPoint);
34423
+ const geometry = object.geometry;
34424
+ const positions = geometry.attributes.position.array;
34425
+ for (let i = 0; i < positions.length; i += 3) {
34426
+ _vertex.set(positions[i], positions[i + 1], positions[i + 2]);
34427
+ const distance = _vertex.distanceTo(localPoint);
34428
+ if (distance < snapDistance) {
34429
+ snapDistance = distance;
34430
+ snapPoint = _vertex.clone();
34431
+ }
34432
+ }
34433
+ if (snapPoint)
34434
+ return object.localToWorld(snapPoint);
34435
+ let edges = this.edgesCache.get(geometry);
34436
+ if (!edges) {
34437
+ edges = new EdgesGeometry(geometry);
34438
+ this.edgesCache.set(geometry, edges);
34439
+ }
34440
+ const edgePositions = edges.attributes.position.array;
34441
+ for (let i = 0; i < edgePositions.length; i += 6) {
34442
+ _start$1.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
34443
+ _end$1.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
34444
+ _line.set(_start$1, _end$1);
34445
+ _line.getCenter(_center);
34446
+ const centerDistance = _center.distanceTo(localPoint);
34447
+ if (centerDistance < snapDistance) {
34448
+ snapDistance = centerDistance;
34449
+ snapPoint = _center.clone();
34450
+ continue;
34451
+ }
34452
+ _line.closestPointToPoint(localPoint, true, _projection);
34453
+ const lineDistance = _projection.distanceTo(localPoint);
34454
+ if (lineDistance < snapDistance) {
34455
+ snapDistance = lineDistance;
34456
+ snapPoint = _projection.clone();
34457
+ }
34458
+ }
34459
+ if (snapPoint)
34460
+ return object.localToWorld(snapPoint);
34461
+ return intersectionPoint.clone();
34462
+ }
34463
+ }
34464
+
34340
34465
  class OrbitDragger {
34341
34466
  constructor(viewer) {
34342
34467
  this.updateControls = () => {
@@ -34429,12 +34554,18 @@ void main() {
34429
34554
  }
34430
34555
 
34431
34556
  class CuttingPlaneDragger extends OrbitDragger {
34432
- constructor(viewer, normal) {
34557
+ constructor(viewer) {
34433
34558
  super(viewer);
34559
+ this.helpers = [];
34560
+ this.activeHelper = null;
34434
34561
  this.transformChange = () => {
34435
- this.plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.planeCenter.quaternion);
34436
- this.plane.constant = -this.planeCenter.position.dot(this.plane.normal);
34562
+ if (!this.activeHelper)
34563
+ return;
34564
+ const plane = this.activeHelper.plane;
34565
+ plane.normal.copy(new Vector3(0, 0, -1)).applyQuaternion(this.activeHelper.quaternion);
34566
+ plane.constant = -this.activeHelper.position.dot(plane.normal);
34437
34567
  this.viewer.update();
34568
+ this.changed = true;
34438
34569
  };
34439
34570
  this.translateDrag = (event) => {
34440
34571
  this.orbit.enabled = !event.value;
@@ -34445,45 +34576,83 @@ void main() {
34445
34576
  this.translate.enabled = !event.value;
34446
34577
  };
34447
34578
  this.updatePlaneSize = () => {
34448
- this.planeHelper.size = this.viewer.extents.getSize(new Vector3()).length() || 1;
34579
+ const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
34580
+ this.helpers.forEach((planeHelper) => (planeHelper.size = extentsSize));
34449
34581
  this.viewer.update();
34450
34582
  };
34451
34583
  this.updateTransformCamera = () => {
34452
34584
  this.translate.camera = this.viewer.camera;
34453
34585
  this.rotate.camera = this.viewer.camera;
34586
+ this.snapper.camera = this.viewer.camera;
34587
+ };
34588
+ this.clearHelpers = () => {
34589
+ this.setActiveHelper();
34590
+ this.helpers.forEach((helper) => {
34591
+ helper.removeFromParent();
34592
+ helper.dispose();
34593
+ });
34594
+ this.helpers = [];
34595
+ this.viewer.update();
34454
34596
  };
34455
34597
  this.onKeyDown = (event) => {
34456
34598
  if (event.key === "Shift")
34457
34599
  this.rotate.setRotationSnap(Math.PI / 4);
34600
+ if (event.key === "Delete" || event.key === "Backspace")
34601
+ this.deleteActivePlane();
34602
+ if (event.key === "Escape" && (this.translate.dragging || this.rotate.dragging))
34603
+ this.reset();
34458
34604
  };
34459
34605
  this.onKeyUp = (event) => {
34460
34606
  if (event.key === "Shift")
34461
34607
  this.rotate.setRotationSnap(null);
34462
34608
  };
34609
+ this.onPointerDown = (event) => {
34610
+ if (event.button !== 0 || !event.isPrimary)
34611
+ return;
34612
+ this.snapper.getMousePosition(event, this.downPosition);
34613
+ this.saveState();
34614
+ };
34615
+ this.onPointerUp = (event) => {
34616
+ if (event.button !== 0)
34617
+ return;
34618
+ const upPosition = this.snapper.getMousePosition(event, new Vector2());
34619
+ if (upPosition.distanceTo(this.downPosition) !== 0)
34620
+ return;
34621
+ const intersects = this.snapper.getPointerIntersects(upPosition, this.helpers, true, false);
34622
+ if (intersects.length === 0)
34623
+ return;
34624
+ this.setActiveHelper(intersects[0].object.parent);
34625
+ };
34626
+ this.onPointerCancel = (event) => {
34627
+ this.viewer.canvas.dispatchEvent(new PointerEvent("pointerup", event));
34628
+ };
34463
34629
  this.onDoubleClick = (event) => {
34464
- event.stopPropagation();
34465
- this.planeCenter.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
34630
+ if (!this.activeHelper)
34631
+ return;
34632
+ const mousePosition = this.snapper.getMousePosition(event, new Vector2());
34633
+ const intersects = this.snapper.getPointerIntersects(mousePosition, [this.activeHelper], true, false);
34634
+ if (intersects.length === 0)
34635
+ return;
34636
+ this.activeHelper.rotateOnAxis(new Vector3(0, 1, 0), Math.PI);
34466
34637
  this.transformChange();
34638
+ event.stopPropagation();
34467
34639
  };
34468
- const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
34469
- const extentsCenter = viewer.extents.getCenter(new Vector3());
34470
- const constant = -extentsCenter.dot(normal);
34471
- this.plane = new Plane(normal, constant);
34472
34640
  if (!viewer.renderer.clippingPlanes)
34473
34641
  viewer.renderer.clippingPlanes = [];
34474
- viewer.renderer.clippingPlanes.push(this.plane);
34475
- this.planeCenter = new Object3D();
34476
- this.planeCenter.position.copy(extentsCenter);
34477
- this.planeCenter.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), normal);
34478
- this.viewer.helpers.add(this.planeCenter);
34479
- this.planeHelper = new PlaneHelper2(extentsSize);
34480
- this.planeCenter.add(this.planeHelper);
34642
+ this.clippingPlanes = viewer.renderer.clippingPlanes;
34643
+ this.clippingPlanes.forEach((plane) => this.addHelper(plane));
34644
+ const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
34645
+ this.snapper = new Snapper(viewer.camera, viewer.renderer, viewer.canvas);
34646
+ this.snapper.threshold = extentsSize / 10000;
34647
+ this.downPosition = new Vector2();
34648
+ this.position0 = new Vector3();
34649
+ this.quaternion0 = new Quaternion();
34481
34650
  this.translate = new TransformControls(viewer.camera, viewer.canvas);
34651
+ this.translate.setMode("translate");
34482
34652
  this.translate.setSpace("local");
34483
34653
  this.translate.showX = false;
34484
34654
  this.translate.showY = false;
34485
34655
  this.translate.showZ = true;
34486
- this.translate.attach(this.planeCenter);
34487
34656
  this.translate.addEventListener("change", this.transformChange);
34488
34657
  this.translate.addEventListener("dragging-changed", this.translateDrag);
34489
34658
  this.viewer.helpers.add(this.translate.getHelper());
@@ -34493,14 +34662,18 @@ void main() {
34493
34662
  this.rotate.showX = true;
34494
34663
  this.rotate.showY = true;
34495
34664
  this.rotate.showZ = false;
34496
- this.rotate.attach(this.planeCenter);
34497
34665
  this.rotate.addEventListener("change", this.transformChange);
34498
34666
  this.rotate.addEventListener("dragging-changed", this.rotateDrag);
34499
34667
  this.viewer.helpers.add(this.rotate.getHelper());
34668
+ this.setActiveHelper(this.helpers[this.helpers.length - 1]);
34500
34669
  this.viewer.addEventListener("explode", this.updatePlaneSize);
34501
34670
  this.viewer.addEventListener("show", this.updatePlaneSize);
34502
34671
  this.viewer.addEventListener("showall", this.updatePlaneSize);
34503
34672
  this.viewer.addEventListener("changecameramode", this.updateTransformCamera);
34673
+ this.viewer.addEventListener("clearslices", this.clearHelpers);
34674
+ this.viewer.canvas.addEventListener("pointerdown", this.onPointerDown, true);
34675
+ this.viewer.canvas.addEventListener("pointerup", this.onPointerUp, true);
34676
+ this.viewer.canvas.addEventListener("pointercancel", this.onPointerCancel, true);
34504
34677
  this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
34505
34678
  window.addEventListener("keydown", this.onKeyDown);
34506
34679
  window.addEventListener("keyup", this.onKeyUp);
@@ -34511,6 +34684,10 @@ void main() {
34511
34684
  this.viewer.removeEventListener("show", this.updatePlaneSize);
34512
34685
  this.viewer.removeEventListener("showall", this.updatePlaneSize);
34513
34686
  this.viewer.removeEventListener("changecameramode", this.updateTransformCamera);
34687
+ this.viewer.removeEventListener("clearslices", this.clearHelpers);
34688
+ this.viewer.canvas.removeEventListener("pointerdown", this.onPointerDown, true);
34689
+ this.viewer.canvas.removeEventListener("pointerup", this.onPointerUp, true);
34690
+ this.viewer.canvas.removeEventListener("pointercancel", this.onPointerCancel, true);
34514
34691
  this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
34515
34692
  window.removeEventListener("keydown", this.onKeyDown);
34516
34693
  window.removeEventListener("keyup", this.onKeyUp);
@@ -34524,28 +34701,108 @@ void main() {
34524
34701
  this.rotate.getHelper().removeFromParent();
34525
34702
  this.rotate.detach();
34526
34703
  this.rotate.dispose();
34527
- this.planeHelper.removeFromParent();
34528
- this.planeHelper.dispose();
34529
- this.planeCenter.removeFromParent();
34704
+ this.helpers.forEach((helper) => {
34705
+ helper.removeFromParent();
34706
+ helper.dispose();
34707
+ });
34708
+ this.helpers = [];
34709
+ this.activeHelper = null;
34530
34710
  super.dispose();
34531
34711
  }
34712
+ addHelper(plane) {
34713
+ const extentsSize = this.viewer.extents.getSize(new Vector3()).length() || 1;
34714
+ const extentsCenter = this.viewer.extents.getCenter(new Vector3());
34715
+ const helper = new PlaneHelper2(extentsSize);
34716
+ helper.plane = plane;
34717
+ helper.position.copy(plane.projectPoint(extentsCenter, new Vector3()));
34718
+ helper.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), plane.normal);
34719
+ this.helpers.push(helper);
34720
+ this.viewer.helpers.add(helper);
34721
+ return helper;
34722
+ }
34723
+ setActiveHelper(helper) {
34724
+ if (helper === this.activeHelper)
34725
+ return;
34726
+ if (this.activeHelper) {
34727
+ this.activeHelper.getLineMaterial().color.setHex(0xf0f0f0);
34728
+ this.activeHelper.getMeshMaterial().opacity = 0.15;
34729
+ this.translate.detach();
34730
+ this.rotate.detach();
34731
+ }
34732
+ this.activeHelper = helper;
34733
+ if (this.activeHelper) {
34734
+ this.activeHelper.getLineMaterial().color.setHex(0xd0d0d0);
34735
+ this.activeHelper.getMeshMaterial().opacity = 0.3;
34736
+ this.translate.attach(this.activeHelper);
34737
+ this.rotate.attach(this.activeHelper);
34738
+ }
34739
+ this.viewer.update();
34740
+ }
34741
+ saveState() {
34742
+ if (!this.activeHelper)
34743
+ return;
34744
+ this.position0.copy(this.activeHelper.position);
34745
+ this.quaternion0.copy(this.activeHelper.quaternion);
34746
+ }
34747
+ reset() {
34748
+ if (!this.activeHelper)
34749
+ return;
34750
+ this.translate.dragging = false;
34751
+ this.rotate.dragging = false;
34752
+ this.orbit.state = STATE.NONE;
34753
+ this.activeHelper.position.copy(this.position0);
34754
+ this.activeHelper.quaternion.copy(this.quaternion0);
34755
+ this.transformChange();
34756
+ }
34757
+ addPlane(normal) {
34758
+ const extentsCenter = this.viewer.extents.getCenter(new Vector3());
34759
+ const constant = -extentsCenter.dot(normal);
34760
+ const plane = new Plane(normal, constant);
34761
+ this.clippingPlanes.push(plane);
34762
+ const helper = this.addHelper(plane);
34763
+ this.setActiveHelper(helper);
34764
+ }
34765
+ addPlaneX() {
34766
+ this.addPlane(new Vector3(-1, 0, 0));
34767
+ }
34768
+ addPlaneY() {
34769
+ this.addPlane(new Vector3(0, -1, 0));
34770
+ }
34771
+ addPlaneZ() {
34772
+ this.addPlane(new Vector3(0, 0, -1));
34773
+ }
34774
+ deleteActivePlane() {
34775
+ if (!this.activeHelper)
34776
+ return;
34777
+ const helper = this.activeHelper;
34778
+ const index = this.clippingPlanes.indexOf(helper.plane);
34779
+ if (index !== -1)
34780
+ this.clippingPlanes.splice(index, 1);
34781
+ this.helpers = this.helpers.filter((x) => x !== helper);
34782
+ helper.removeFromParent();
34783
+ helper.dispose();
34784
+ this.setActiveHelper(this.helpers[this.helpers.length - 1]);
34785
+ }
34532
34786
  }
34533
34787
 
34534
34788
  class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
34535
34789
  constructor(viewer) {
34536
- super(viewer, new Vector3(-1, 0, 0));
34790
+ super(viewer);
34791
+ this.addPlaneX();
34537
34792
  }
34538
34793
  }
34539
34794
 
34540
34795
  class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
34541
34796
  constructor(viewer) {
34542
- super(viewer, new Vector3(0, -1, 0));
34797
+ super(viewer);
34798
+ this.addPlaneY();
34543
34799
  }
34544
34800
  }
34545
34801
 
34546
34802
  class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
34547
34803
  constructor(viewer) {
34548
- super(viewer, new Vector3(0, 0, -1));
34804
+ super(viewer);
34805
+ this.addPlaneZ();
34549
34806
  }
34550
34807
  }
34551
34808
 
@@ -34622,120 +34879,6 @@ void main() {
34622
34879
  }
34623
34880
  }
34624
34881
 
34625
- const DESKTOP_SNAP_DISTANCE = 10;
34626
- const MOBILE_SNAP_DISTANCE = 50;
34627
- const _vertex = new Vector3();
34628
- const _start$1 = new Vector3();
34629
- const _end$1 = new Vector3();
34630
- const _line = new Line3();
34631
- const _center = new Vector3();
34632
- const _projection = new Vector3();
34633
- class Snapper {
34634
- constructor(camera, renderer, canvas) {
34635
- this.camera = camera;
34636
- this.renderer = renderer;
34637
- this.canvas = canvas;
34638
- this.threshold = 0.0001;
34639
- this.raycaster = new Raycaster();
34640
- this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
34641
- this.edgesCache = new WeakMap();
34642
- }
34643
- isMobile() {
34644
- if (typeof navigator === "undefined")
34645
- return false;
34646
- return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
34647
- }
34648
- getMousePosition(event, target) {
34649
- return target.set(event.clientX, event.clientY);
34650
- }
34651
- getPointerIntersects(mouse, objects) {
34652
- const rect = this.canvas.getBoundingClientRect();
34653
- const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
34654
- const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
34655
- const coords = new Vector2(x, y);
34656
- this.raycaster.setFromCamera(coords, this.camera);
34657
- this.raycaster.params = {
34658
- Mesh: {},
34659
- Line: { threshold: this.threshold },
34660
- Line2: { threshold: this.threshold },
34661
- LOD: {},
34662
- Points: { threshold: this.threshold },
34663
- Sprite: {},
34664
- };
34665
- let intersects = this.raycaster.intersectObjects(objects, false);
34666
- (this.renderer.clippingPlanes || []).forEach((plane) => {
34667
- intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
34668
- });
34669
- return intersects;
34670
- }
34671
- getDetectRadius(point) {
34672
- const camera = this.camera;
34673
- if (camera.isOrthographicCamera) {
34674
- const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
34675
- const canvasHeight = this.canvas.height;
34676
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
34677
- return this.detectRadiusInPixels * worldUnitsPerPixel;
34678
- }
34679
- if (camera.isPerspectiveCamera) {
34680
- const distance = camera.position.distanceTo(point);
34681
- const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
34682
- const canvasHeight = this.canvas.height;
34683
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
34684
- return this.detectRadiusInPixels * worldUnitsPerPixel;
34685
- }
34686
- return 0.1;
34687
- }
34688
- getSnapPoint(mouse, objects) {
34689
- const intersections = this.getPointerIntersects(mouse, objects);
34690
- if (intersections.length === 0)
34691
- return undefined;
34692
- const object = intersections[0].object;
34693
- const intersectionPoint = intersections[0].point;
34694
- const localPoint = object.worldToLocal(intersectionPoint.clone());
34695
- let snapPoint;
34696
- let snapDistance = this.getDetectRadius(intersectionPoint);
34697
- const geometry = object.geometry;
34698
- const positions = geometry.attributes.position.array;
34699
- for (let i = 0; i < positions.length; i += 3) {
34700
- _vertex.set(positions[i], positions[i + 1], positions[i + 2]);
34701
- const distance = _vertex.distanceTo(localPoint);
34702
- if (distance < snapDistance) {
34703
- snapDistance = distance;
34704
- snapPoint = _vertex.clone();
34705
- }
34706
- }
34707
- if (snapPoint)
34708
- return object.localToWorld(snapPoint);
34709
- let edges = this.edgesCache.get(geometry);
34710
- if (!edges) {
34711
- edges = new EdgesGeometry(geometry);
34712
- this.edgesCache.set(geometry, edges);
34713
- }
34714
- const edgePositions = edges.attributes.position.array;
34715
- for (let i = 0; i < edgePositions.length; i += 6) {
34716
- _start$1.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
34717
- _end$1.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
34718
- _line.set(_start$1, _end$1);
34719
- _line.getCenter(_center);
34720
- const centerDistance = _center.distanceTo(localPoint);
34721
- if (centerDistance < snapDistance) {
34722
- snapDistance = centerDistance;
34723
- snapPoint = _center.clone();
34724
- continue;
34725
- }
34726
- _line.closestPointToPoint(localPoint, true, _projection);
34727
- const lineDistance = _projection.distanceTo(localPoint);
34728
- if (lineDistance < snapDistance) {
34729
- snapDistance = lineDistance;
34730
- snapPoint = _projection.clone();
34731
- }
34732
- }
34733
- if (snapPoint)
34734
- return object.localToWorld(snapPoint);
34735
- return intersectionPoint.clone();
34736
- }
34737
- }
34738
-
34739
34882
  const _downPoint = new Vector2();
34740
34883
  class MeasureLineDragger extends OrbitDragger {
34741
34884
  constructor(viewer) {
@@ -35219,14 +35362,20 @@ void main() {
35219
35362
  }
35220
35363
  };
35221
35364
  this.onKeyUp = (event) => {
35222
- if (this.moveKeys.delete(event.code))
35365
+ if (this.moveKeys.delete(event.code)) {
35366
+ if (this.moveKeys.size === 0) {
35367
+ this._rebuildGroundBox(this.object.position);
35368
+ }
35223
35369
  this.update();
35370
+ }
35224
35371
  };
35225
35372
  this.camera = camera;
35226
35373
  this.groundObjects = groundObjects;
35227
35374
  for (const obj of groundObjects) {
35228
35375
  this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
35229
35376
  }
35377
+ const pos = this.object.position;
35378
+ this._rebuildGroundBox(pos);
35230
35379
  this.raycaster = new Raycaster();
35231
35380
  this.raycaster.near = 0;
35232
35381
  this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
@@ -35835,6 +35984,7 @@ void main() {
35835
35984
  draggers.registerDragger("Orbit", (viewer) => new OrbitDragger(viewer));
35836
35985
  draggers.registerDragger("Zoom", (viewer) => new ZoomDragger(viewer));
35837
35986
  draggers.registerDragger("MeasureLine", (viewer) => new MeasureLineDragger(viewer));
35987
+ draggers.registerDragger("CuttingPlane", (viewer) => new CuttingPlaneDragger(viewer));
35838
35988
  draggers.registerDragger("CuttingPlaneXAxis", (viewer) => new CuttingPlaneXAxisDragger(viewer));
35839
35989
  draggers.registerDragger("CuttingPlaneYAxis", (viewer) => new CuttingPlaneYAxisDragger(viewer));
35840
35990
  draggers.registerDragger("CuttingPlaneZAxis", (viewer) => new CuttingPlaneZAxisDragger(viewer));
@@ -37369,7 +37519,7 @@ void main() {
37369
37519
  this.getMousePosition(event, this.downPosition);
37370
37520
  };
37371
37521
  this.onPointerUp = (event) => {
37372
- if (!event.isPrimary)
37522
+ if (!event.isPrimary || event.button !== 0)
37373
37523
  return;
37374
37524
  const upPosition = this.getMousePosition(event, new Vector2());
37375
37525
  if (upPosition.distanceTo(this.downPosition) !== 0)
@@ -38832,6 +38982,81 @@ void main() {
38832
38982
  this.mergedObjectMap = new Map();
38833
38983
  this.mergedGeometryVisibility = new Map();
38834
38984
  this._webglInfoCache = null;
38985
+ this.transformTextureSize = 1024;
38986
+ this.transformTexture = this.createDummyTexture();
38987
+ this.transformData = null;
38988
+ this.identityTransformData = null;
38989
+ this.visibilityMaterials = new Set();
38990
+ }
38991
+ createDummyTexture() {
38992
+ const data = new Float32Array(16);
38993
+ const identity = new Matrix4();
38994
+ identity.toArray(data);
38995
+ const dummyData = new Float32Array(16);
38996
+ identity.toArray(dummyData);
38997
+ const dummyTexture = new DataTexture(dummyData, 4, 1, RGBAFormat, FloatType);
38998
+ dummyTexture.minFilter = NearestFilter;
38999
+ dummyTexture.magFilter = NearestFilter;
39000
+ dummyTexture.needsUpdate = true;
39001
+ return dummyTexture;
39002
+ }
39003
+ initTransformTexture() {
39004
+ if (this.transformTexture) {
39005
+ this.transformTexture.dispose();
39006
+ }
39007
+ const maxInstanceCount = this.maxObjectId + 1;
39008
+ let size = Math.sqrt(maxInstanceCount * 4);
39009
+ size = Math.ceil(size / 4) * 4;
39010
+ size = Math.max(size, 4);
39011
+ this.transformTextureSize = size;
39012
+ const arraySize = size * size * 4;
39013
+ this.transformData = new Float32Array(arraySize);
39014
+ this.identityTransformData = new Float32Array(arraySize);
39015
+ for (let i = 0; i <= this.maxObjectId; i++) {
39016
+ const base = i * 16;
39017
+ if (base + 15 < arraySize) {
39018
+ this.identityTransformData[base + 0] = 1;
39019
+ this.identityTransformData[base + 5] = 1;
39020
+ this.identityTransformData[base + 10] = 1;
39021
+ this.identityTransformData[base + 15] = 1;
39022
+ }
39023
+ }
39024
+ this._resetTransformData(false);
39025
+ this.transformTexture = new DataTexture(this.transformData, size, size, RGBAFormat, FloatType);
39026
+ this.transformTexture.needsUpdate = true;
39027
+ this.transformTexture.generateMipmaps = false;
39028
+ console.log(`Initialized transform texture: ${size}x${size} for ${maxInstanceCount} objects`);
39029
+ this.updateMaterialUniforms();
39030
+ this.visibilityMaterials.forEach((material) => {
39031
+ material.needsUpdate = true;
39032
+ });
39033
+ }
39034
+ _resetTransformData(updateTexture = true) {
39035
+ if (!this.transformData || !this.identityTransformData) return;
39036
+ this.transformData.set(this.identityTransformData);
39037
+ if (updateTexture) {
39038
+ this.updateTransformTexture();
39039
+ }
39040
+ }
39041
+ updateMaterialUniforms() {
39042
+ if (
39043
+ this._lastTransformTexture === this.transformTexture &&
39044
+ this._lastTransformTextureSize === this.transformTextureSize
39045
+ ) {
39046
+ return;
39047
+ }
39048
+ this._lastTransformTexture = this.transformTexture;
39049
+ this._lastTransformTextureSize = this.transformTextureSize;
39050
+ this.visibilityMaterials.forEach((material) => {
39051
+ if (material.userData && material.userData.visibilityUniforms) {
39052
+ material.userData.visibilityUniforms.transformTexture.value = this.transformTexture;
39053
+ material.userData.visibilityUniforms.transformTextureSize.value = this.transformTextureSize;
39054
+ }
39055
+ });
39056
+ }
39057
+ updateTransformTexture() {
39058
+ if (!this.transformTexture) return;
39059
+ this.transformTexture.needsUpdate = true;
38835
39060
  }
38836
39061
  setVisibleEdges(visible) {
38837
39062
  this.visibleEdges = visible;
@@ -39721,36 +39946,82 @@ void main() {
39721
39946
  }
39722
39947
  }
39723
39948
  createVisibilityMaterial(material) {
39949
+ this.visibilityMaterials.add(material);
39950
+ const uniforms = {
39951
+ transformTexture: { value: this.transformTexture },
39952
+ transformTextureSize: { value: this.transformTextureSize },
39953
+ };
39954
+ material.userData.visibilityUniforms = uniforms;
39724
39955
  material.onBeforeCompile = (shader) => {
39956
+ shader.uniforms.transformTexture = uniforms.transformTexture;
39957
+ shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
39725
39958
  shader.vertexShader = shader.vertexShader.replace(
39726
39959
  "#include <common>",
39727
39960
  `
39728
39961
  #include <common>
39962
+
39729
39963
  attribute float visibility;
39964
+ attribute float objectId;
39730
39965
  varying float vVisibility;
39731
- `
39732
- );
39733
- shader.fragmentShader = shader.fragmentShader.replace(
39734
- "#include <common>",
39735
- `
39736
- #include <common>
39737
- varying float vVisibility;
39966
+ uniform highp sampler2D transformTexture;
39967
+ uniform float transformTextureSize;
39968
+
39969
+ mat4 getTransformMatrix(float instanceId) {
39970
+ int size = int(transformTextureSize);
39971
+ int index = int(instanceId) * 4;
39972
+
39973
+ int x0 = index % size;
39974
+ int y0 = index / size;
39975
+
39976
+ vec4 row0 = texelFetch(transformTexture, ivec2(x0, y0), 0);
39977
+ vec4 row1 = texelFetch(transformTexture, ivec2(x0 + 1, y0), 0);
39978
+ vec4 row2 = texelFetch(transformTexture, ivec2(x0 + 2, y0), 0);
39979
+ vec4 row3 = texelFetch(transformTexture, ivec2(x0 + 3, y0), 0);
39980
+
39981
+ return mat4(row0, row1, row2, row3);
39982
+ }
39738
39983
  `
39739
39984
  );
39740
39985
  shader.vertexShader = shader.vertexShader.replace(
39741
39986
  "void main() {",
39742
39987
  `
39743
39988
  void main() {
39744
- vVisibility = visibility;
39989
+ mat4 batchingMatrix = getTransformMatrix(objectId);
39990
+ vVisibility = visibility;
39745
39991
  `
39746
39992
  );
39747
- shader.fragmentShader = shader.fragmentShader.replace(
39748
- "void main() {",
39993
+ if (shader.vertexShader.includes("#include <beginnormal_vertex>")) {
39994
+ shader.vertexShader = shader.vertexShader.replace(
39995
+ "#include <beginnormal_vertex>",
39996
+ `
39997
+ vec3 objectNormal = vec3( normal );
39998
+ mat3 bm = mat3( batchingMatrix );
39999
+ objectNormal = bm * objectNormal;
40000
+ `
40001
+ );
40002
+ }
40003
+ shader.vertexShader = shader.vertexShader.replace(
40004
+ "#include <begin_vertex>",
39749
40005
  `
40006
+ vec3 transformed = vec3( position );
40007
+ transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz;
40008
+ `
40009
+ );
40010
+ shader.fragmentShader = shader.fragmentShader
40011
+ .replace(
40012
+ "#include <common>",
40013
+ `
40014
+ #include <common>
40015
+ varying float vVisibility;
40016
+ `
40017
+ )
40018
+ .replace(
40019
+ "void main() {",
40020
+ `
39750
40021
  void main() {
39751
40022
  if (vVisibility < 0.5) discard;
39752
40023
  `
39753
- );
40024
+ );
39754
40025
  };
39755
40026
  material.needsUpdate = true;
39756
40027
  return material;
@@ -39865,6 +40136,8 @@ void main() {
39865
40136
  this.objectIdToIndex.clear();
39866
40137
  this.maxObjectId = 0;
39867
40138
  this.objectVisibility = new Float32Array();
40139
+ this.meshToNodeMap = null;
40140
+ this.visibilityMaterials.clear();
39868
40141
  }
39869
40142
  setStructureTransform(structureId, matrix) {
39870
40143
  const rootGroup = this.structureRoots.get(structureId);
@@ -39980,12 +40253,15 @@ void main() {
39980
40253
  });
39981
40254
  this.originalObjects.clear();
39982
40255
  this.originalObjectsToSelection.clear();
40256
+ this.objectIdToIndex.clear();
40257
+ this.maxObjectId = 0;
39983
40258
  const structureGroups = new Map();
39984
40259
  this.dispatchEvent("optimizationprogress", {
39985
40260
  phase: "collecting",
39986
40261
  progress: 5,
39987
40262
  message: "Collecting scene objects...",
39988
40263
  });
40264
+ let totalObjectsToMerge = 0;
39989
40265
  this.scene.traverse((object) => {
39990
40266
  if (object.userData.structureId) {
39991
40267
  const structureId = object.userData.structureId;
@@ -40003,17 +40279,32 @@ void main() {
40003
40279
  });
40004
40280
  }
40005
40281
  const group = structureGroups.get(structureId);
40282
+ let added = false;
40006
40283
  if (object instanceof Mesh) {
40007
40284
  this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
40285
+ added = true;
40008
40286
  } else if (object instanceof LineSegments) {
40009
40287
  this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
40288
+ added = true;
40010
40289
  } else if (object instanceof Line$1) {
40011
40290
  this.addToMaterialGroup(object, group.mapLines, group.lines);
40291
+ added = true;
40012
40292
  } else if (object instanceof Points) {
40013
40293
  this.addToMaterialGroup(object, group.mapPoints, group.points);
40294
+ added = true;
40295
+ }
40296
+ if (added) {
40297
+ totalObjectsToMerge++;
40014
40298
  }
40015
40299
  }
40016
40300
  });
40301
+ if (totalObjectsToMerge > 0) {
40302
+ console.log(`Pre-allocating transform texture for ${totalObjectsToMerge} objects`);
40303
+ this.maxObjectId = totalObjectsToMerge;
40304
+ this.initTransformTexture();
40305
+ this.initializeObjectVisibility();
40306
+ this.maxObjectId = 0;
40307
+ }
40017
40308
  let processedGroups = 0;
40018
40309
  const totalGroups = structureGroups.size;
40019
40310
  this.dispatchEvent("optimizationprogress", {
@@ -40058,7 +40349,6 @@ void main() {
40058
40349
  this.originalObjectsToSelection.add(obj);
40059
40350
  }
40060
40351
  });
40061
- this.initializeObjectVisibility();
40062
40352
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
40063
40353
  this.dispatchEvent("optimizationprogress", {
40064
40354
  phase: "complete",
@@ -40127,6 +40417,7 @@ void main() {
40127
40417
  }
40128
40418
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40129
40419
  const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
40420
+ mergedMesh.frustumCulled = false;
40130
40421
  mergedMesh.userData.isOptimized = true;
40131
40422
  rootGroup.add(mergedMesh);
40132
40423
  this.mergedMesh.add(mergedMesh);
@@ -40243,6 +40534,7 @@ void main() {
40243
40534
  geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40244
40535
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40245
40536
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
40537
+ mergedLine.frustumCulled = false;
40246
40538
  mergedLine.userData.isEdge = isEdge;
40247
40539
  mergedLine.userData.isOptimized = true;
40248
40540
  const mergedObjects = [mergedLine];
@@ -40331,6 +40623,7 @@ void main() {
40331
40623
  mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40332
40624
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40333
40625
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
40626
+ mergedLine.frustumCulled = false;
40334
40627
  mergedLine.userData.isEdge = isEdge;
40335
40628
  mergedLine.userData.isOptimized = true;
40336
40629
  if (this.useVAO) {
@@ -40400,7 +40693,27 @@ void main() {
40400
40693
  const mergedObjects = [];
40401
40694
  if (geometries.length > 0) {
40402
40695
  const mergedGeometry = mergeGeometries(geometries, false);
40403
- const mergedPoints = new Points(mergedGeometry, group.material);
40696
+ const totalVertices = mergedGeometry.attributes.position.count;
40697
+ const objectIds = new Float32Array(totalVertices);
40698
+ let vertexOffset = 0;
40699
+ group.objects.forEach((points) => {
40700
+ const handle = points.userData.handle;
40701
+ if (!this.objectIdToIndex.has(handle)) {
40702
+ this.objectIdToIndex.set(handle, this.maxObjectId++);
40703
+ }
40704
+ const objectId = this.objectIdToIndex.get(handle);
40705
+ const count = points.geometry.attributes.position.count;
40706
+ for (let i = 0; i < count; i++) {
40707
+ objectIds[vertexOffset++] = objectId;
40708
+ }
40709
+ });
40710
+ mergedGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
40711
+ const visibilityArray = new Float32Array(totalVertices);
40712
+ visibilityArray.fill(1.0);
40713
+ mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40714
+ const visibilityMaterial = this.createVisibilityMaterial(group.material);
40715
+ const mergedPoints = new Points(mergedGeometry, visibilityMaterial);
40716
+ mergedPoints.frustumCulled = false;
40404
40717
  mergedPoints.userData.isOptimized = true;
40405
40718
  if (this.useVAO) {
40406
40719
  this.createVAO(mergedPoints);
@@ -40469,13 +40782,33 @@ void main() {
40469
40782
  geometriesWithIndex.push(clonedGeometry);
40470
40783
  });
40471
40784
  const finalGeometry = mergeGeometries(geometriesWithIndex, false);
40785
+ const totalVertices = finalGeometry.attributes.position.count;
40786
+ const objectIds = new Float32Array(totalVertices);
40787
+ let vertexOffset = 0;
40788
+ lineSegmentsArray.forEach((segment) => {
40789
+ const handle = segment.userData.handle;
40790
+ if (!this.objectIdToIndex.has(handle)) {
40791
+ this.objectIdToIndex.set(handle, this.maxObjectId++);
40792
+ }
40793
+ const objectId = this.objectIdToIndex.get(handle);
40794
+ const count = segment.geometry.attributes.position.count;
40795
+ for (let i = 0; i < count; i++) {
40796
+ objectIds[vertexOffset++] = objectId;
40797
+ }
40798
+ });
40799
+ finalGeometry.setAttribute("objectId", new BufferAttribute(objectIds, 1));
40800
+ const visibilityArray = new Float32Array(totalVertices);
40801
+ visibilityArray.fill(1.0);
40802
+ finalGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40472
40803
  const material = new LineBasicMaterial({
40473
40804
  vertexColors: true,
40474
40805
  });
40806
+ const visibilityMaterial = this.createVisibilityMaterial(material);
40475
40807
  if (this.useVAO) {
40476
40808
  this.createVAO(finalGeometry);
40477
40809
  }
40478
- const mergedLine = new LineSegments(finalGeometry, material);
40810
+ const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
40811
+ mergedLine.frustumCulled = false;
40479
40812
  mergedLine.userData.structureId = structureId;
40480
40813
  mergedLine.userData.isOptimized = true;
40481
40814
  rootGroup.add(mergedLine);
@@ -40594,18 +40927,50 @@ void main() {
40594
40927
  console.warn("No merged objects to transform");
40595
40928
  return;
40596
40929
  }
40597
- this.objectTransforms = new Map(objectTransformMap);
40598
- for (const mesh of this.mergedMesh) {
40599
- this._applyTransformToMergedObject(mesh);
40600
- }
40601
- for (const line of this.mergedLines) {
40602
- this._applyTransformToMergedObject(line);
40930
+ if (!this.transformData) {
40931
+ console.warn("Transform texture not initialized");
40932
+ return;
40603
40933
  }
40604
- for (const lineSegment of this.mergedLineSegments) {
40605
- this._applyTransformToMergedObject(lineSegment);
40934
+ this.objectTransforms = objectTransformMap;
40935
+ this._resetTransformData(false);
40936
+ const transformData = this.transformData;
40937
+ const objectIdToIndex = this.objectIdToIndex;
40938
+ let textureNeedsUpdate = false;
40939
+ if (objectTransformMap instanceof Map) {
40940
+ for (const [object, matrix] of objectTransformMap.entries()) {
40941
+ const userData = object.userData;
40942
+ if (!userData) continue;
40943
+ const handle = userData.handle;
40944
+ if (handle === undefined) continue;
40945
+ const objectId = objectIdToIndex.get(handle);
40946
+ if (objectId !== undefined) {
40947
+ transformData.set(matrix.elements, objectId * 16);
40948
+ textureNeedsUpdate = true;
40949
+ }
40950
+ }
40951
+ } else {
40952
+ const len = objectTransformMap.length;
40953
+ for (let i = 0; i < len; i++) {
40954
+ const pair = objectTransformMap[i];
40955
+ const userData = pair[0].userData;
40956
+ if (!userData) continue;
40957
+ const handle = userData.handle;
40958
+ if (handle === undefined) continue;
40959
+ const objectId = objectIdToIndex.get(handle);
40960
+ if (objectId !== undefined) {
40961
+ transformData.set(pair[1].elements, objectId * 16);
40962
+ textureNeedsUpdate = true;
40963
+ }
40964
+ }
40606
40965
  }
40607
- for (const point of this.mergedPoints) {
40608
- this._applyTransformToMergedObject(point);
40966
+ if (textureNeedsUpdate) {
40967
+ this.updateTransformTexture();
40968
+ if (
40969
+ this._lastTransformTexture !== this.transformTexture ||
40970
+ this._lastTransformTextureSize !== this.transformTextureSize
40971
+ ) {
40972
+ this.updateMaterialUniforms();
40973
+ }
40609
40974
  }
40610
40975
  }
40611
40976
  createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
@@ -40622,21 +40987,66 @@ void main() {
40622
40987
  ? objects
40623
40988
  : Array.from(objects)
40624
40989
  : Array.from(this.originalObjects);
40990
+ const structureInverseMatrices = new Map();
40991
+ if (!this.meshToNodeMap) {
40992
+ this.meshToNodeMap = new Map();
40993
+ for (const node of this.nodes.values()) {
40994
+ if (node.object) {
40995
+ this.meshToNodeMap.set(node.object, node);
40996
+ }
40997
+ }
40998
+ }
40625
40999
  for (const obj of objectsArray) {
40626
41000
  if (!obj.geometry || !obj.geometry.attributes.position) continue;
40627
- const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
40628
- if (obj.matrixWorld) {
40629
- boundingBox.applyMatrix4(obj.matrixWorld);
41001
+ if (!obj.userData.explodeVector) {
41002
+ let center = null;
41003
+ const node = this.meshToNodeMap.get(obj);
41004
+ if (node && node.geometryExtents) {
41005
+ const box = node.geometryExtents.clone();
41006
+ box.applyMatrix4(obj.matrixWorld);
41007
+ center = new Vector3();
41008
+ box.getCenter(center);
41009
+ }
41010
+ if (!center) {
41011
+ if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();
41012
+ const box = obj.geometry.boundingBox.clone();
41013
+ box.applyMatrix4(obj.matrixWorld);
41014
+ center = new Vector3();
41015
+ box.getCenter(center);
41016
+ }
41017
+ const explodeVector = center.sub(explodeCenter);
41018
+ obj.userData.explodeVector = explodeVector;
40630
41019
  }
40631
- if (boundingBox.isEmpty()) continue;
40632
- const objectCenter = new Vector3();
40633
- boundingBox.getCenter(objectCenter);
40634
- const direction = objectCenter.clone().sub(explodeCenter);
40635
- const distance = direction.length();
41020
+ const explodeVector = obj.userData.explodeVector;
41021
+ const distance = explodeVector.length();
40636
41022
  if (distance > 0) {
40637
- direction.normalize();
40638
- const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
40639
- const matrix = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
41023
+ const offset = explodeVector.clone().multiplyScalar(explodeFactor - 1.0);
41024
+ const localOffset = offset.clone();
41025
+ if (obj.userData.structureId) {
41026
+ const structureId = obj.userData.structureId;
41027
+ let inverseMatrix = structureInverseMatrices.get(structureId);
41028
+ if (!inverseMatrix) {
41029
+ const rootGroup = this.structureRoots.get(structureId);
41030
+ if (rootGroup) {
41031
+ if (!rootGroup.userData.inverseWorldMatrix) {
41032
+ rootGroup.userData.inverseWorldMatrix = new Matrix4().copy(rootGroup.matrixWorld).invert();
41033
+ }
41034
+ inverseMatrix = rootGroup.userData.inverseWorldMatrix;
41035
+ structureInverseMatrices.set(structureId, inverseMatrix);
41036
+ }
41037
+ }
41038
+ if (inverseMatrix) {
41039
+ const zero = new Vector3(0, 0, 0).applyMatrix4(inverseMatrix);
41040
+ const vec = offset.clone().applyMatrix4(inverseMatrix).sub(zero);
41041
+ localOffset.copy(vec);
41042
+ }
41043
+ }
41044
+ let matrix = obj.userData.explodeMatrix;
41045
+ if (!matrix) {
41046
+ matrix = new Matrix4();
41047
+ obj.userData.explodeMatrix = matrix;
41048
+ }
41049
+ matrix.makeTranslation(localOffset.x, localOffset.y, localOffset.z);
40640
41050
  transformMap.set(obj, matrix);
40641
41051
  }
40642
41052
  }
@@ -40644,116 +41054,11 @@ void main() {
40644
41054
  }
40645
41055
  clearTransforms() {
40646
41056
  this.objectTransforms.clear();
40647
- for (const mesh of this.mergedMesh) {
40648
- this._restoreOriginalGeometry(mesh);
40649
- }
40650
- for (const line of this.mergedLines) {
40651
- this._restoreOriginalGeometry(line);
40652
- }
40653
- for (const lineSegment of this.mergedLineSegments) {
40654
- this._restoreOriginalGeometry(lineSegment);
40655
- }
40656
- for (const point of this.mergedPoints) {
40657
- this._restoreOriginalGeometry(point);
40658
- }
41057
+ this._resetTransformData(true);
40659
41058
  }
40660
41059
  clearHandleTransforms() {
40661
41060
  this.clearTransforms();
40662
41061
  }
40663
- _applyTransformToMergedObject(mergedObject) {
40664
- const objectData = this.mergedObjectMap.get(mergedObject.uuid);
40665
- if (!objectData || !objectData.objectMapping) return;
40666
- const geometry = mergedObject.geometry;
40667
- if (!geometry || !geometry.attributes.position) return;
40668
- const positionAttr = geometry.attributes.position;
40669
- const positions = positionAttr.array;
40670
- if (!this.transformedGeometries.has(mergedObject.uuid)) {
40671
- this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
40672
- }
40673
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40674
- const tempVector = new Vector3();
40675
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40676
- const transform = this.objectTransforms.get(originalMesh);
40677
- if (!transform) {
40678
- const startIdx = mappingData.startVertexIndex * 3;
40679
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40680
- for (let i = startIdx; i < endIdx; i++) {
40681
- positions[i] = originalPositions[i];
40682
- }
40683
- continue;
40684
- }
40685
- const startVertex = mappingData.startVertexIndex;
40686
- const vertexCount = mappingData.vertexCount;
40687
- for (let i = 0; i < vertexCount; i++) {
40688
- const idx = (startVertex + i) * 3;
40689
- tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
40690
- tempVector.applyMatrix4(transform);
40691
- positions[idx] = tempVector.x;
40692
- positions[idx + 1] = tempVector.y;
40693
- positions[idx + 2] = tempVector.z;
40694
- }
40695
- }
40696
- if (geometry.attributes.normal) {
40697
- this._updateNormalsForTransform(geometry, objectData, originalPositions);
40698
- }
40699
- positionAttr.needsUpdate = true;
40700
- geometry.computeBoundingSphere();
40701
- geometry.computeBoundingBox();
40702
- }
40703
- _updateNormalsForTransform(geometry, objectData, originalPositions) {
40704
- const normalAttr = geometry.attributes.normal;
40705
- if (!normalAttr) return;
40706
- const normals = normalAttr.array;
40707
- const tempVector = new Vector3();
40708
- const normalMatrix = new Matrix4();
40709
- const normalsKey = `${geometry.uuid}_normals`;
40710
- if (!this.transformedGeometries.has(normalsKey)) {
40711
- this.transformedGeometries.set(normalsKey, new Float32Array(normals));
40712
- }
40713
- const originalNormals = this.transformedGeometries.get(normalsKey);
40714
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40715
- const transform = this.objectTransforms.get(originalMesh);
40716
- if (!transform) {
40717
- const startIdx = mappingData.startVertexIndex * 3;
40718
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40719
- for (let i = startIdx; i < endIdx; i++) {
40720
- normals[i] = originalNormals[i];
40721
- }
40722
- continue;
40723
- }
40724
- normalMatrix.copy(transform).invert().transpose();
40725
- const startVertex = mappingData.startVertexIndex;
40726
- const vertexCount = mappingData.vertexCount;
40727
- for (let i = 0; i < vertexCount; i++) {
40728
- const idx = (startVertex + i) * 3;
40729
- tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
40730
- tempVector.applyMatrix4(normalMatrix).normalize();
40731
- normals[idx] = tempVector.x;
40732
- normals[idx + 1] = tempVector.y;
40733
- normals[idx + 2] = tempVector.z;
40734
- }
40735
- }
40736
- normalAttr.needsUpdate = true;
40737
- }
40738
- _restoreOriginalGeometry(mergedObject) {
40739
- const geometry = mergedObject.geometry;
40740
- if (!geometry || !geometry.attributes.position) return;
40741
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40742
- if (originalPositions) {
40743
- const positions = geometry.attributes.position.array;
40744
- positions.set(originalPositions);
40745
- geometry.attributes.position.needsUpdate = true;
40746
- }
40747
- const normalsKey = `${geometry.uuid}_normals`;
40748
- const originalNormals = this.transformedGeometries.get(normalsKey);
40749
- if (originalNormals && geometry.attributes.normal) {
40750
- const normals = geometry.attributes.normal.array;
40751
- normals.set(originalNormals);
40752
- geometry.attributes.normal.needsUpdate = true;
40753
- }
40754
- geometry.computeBoundingSphere();
40755
- geometry.computeBoundingBox();
40756
- }
40757
41062
  syncHiddenObjects() {
40758
41063
  if (this.mergedObjectMap.size === 0) {
40759
41064
  console.log("No merged objects to sync");