@inweb/viewer-three 27.2.1 → 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.
@@ -21255,7 +21255,7 @@
21255
21255
  this.material.dispose();
21256
21256
  }
21257
21257
  }
21258
- let PlaneHelper$1 = class PlaneHelper extends Line$1 {
21258
+ class PlaneHelper extends Line$1 {
21259
21259
  constructor( plane, size = 1, hex = 0xffff00 ) {
21260
21260
  const color = hex;
21261
21261
  const positions = [ 1, -1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0 ];
@@ -21285,7 +21285,7 @@
21285
21285
  this.children[ 0 ].geometry.dispose();
21286
21286
  this.children[ 0 ].material.dispose();
21287
21287
  }
21288
- };
21288
+ }
21289
21289
  const _axis = new Vector3();
21290
21290
  let _lineGeometry, _coneGeometry;
21291
21291
  class ArrowHelper extends Object3D {
@@ -32455,7 +32455,7 @@ void main() {
32455
32455
  PerspectiveCamera: PerspectiveCamera,
32456
32456
  Plane: Plane,
32457
32457
  PlaneGeometry: PlaneGeometry,
32458
- PlaneHelper: PlaneHelper$1,
32458
+ PlaneHelper: PlaneHelper,
32459
32459
  PointLight: PointLight,
32460
32460
  PointLightHelper: PointLightHelper,
32461
32461
  Points: Points,
@@ -33644,47 +33644,6 @@ void main() {
33644
33644
  }
33645
33645
  }
33646
33646
 
33647
- class PlaneHelper extends Line$1 {
33648
- constructor(plane, size = 1, color = 0xffff00, offset = new Vector3()) {
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 = "PlaneHelper";
33655
- this.plane = plane;
33656
- this.size = size;
33657
- this.offset = offset;
33658
- const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0];
33659
- const geometry2 = new BufferGeometry();
33660
- geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
33661
- geometry2.computeBoundingSphere();
33662
- this.helper = new Mesh(geometry2, new MeshBasicMaterial({
33663
- color,
33664
- opacity: 0.2,
33665
- transparent: true,
33666
- depthWrite: false,
33667
- toneMapped: false,
33668
- side: DoubleSide,
33669
- }));
33670
- this.add(this.helper);
33671
- }
33672
- dispose() {
33673
- this.geometry.dispose();
33674
- this.material.dispose();
33675
- this.children[0].geometry.dispose();
33676
- this.children[0].material.dispose();
33677
- }
33678
- updateMatrixWorld(force) {
33679
- this.position.set(0, 0, 0);
33680
- this.lookAt(this.plane.normal);
33681
- this.position.copy(this.offset);
33682
- this.translateZ(-(this.offset.dot(this.plane.normal) + this.plane.constant));
33683
- this.scale.set(0.5 * this.size, 0.5 * this.size, 1);
33684
- super.updateMatrixWorld(force);
33685
- }
33686
- }
33687
-
33688
33647
  const _changeEvent = { type: "change" };
33689
33648
  const _startEvent = { type: "start" };
33690
33649
  const _endEvent = { type: "end" };
@@ -34343,6 +34302,166 @@ void main() {
34343
34302
  }
34344
34303
  }
34345
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
+
34346
34465
  class OrbitDragger {
34347
34466
  constructor(viewer) {
34348
34467
  this.updateControls = () => {
@@ -34435,52 +34554,129 @@ void main() {
34435
34554
  }
34436
34555
 
34437
34556
  class CuttingPlaneDragger extends OrbitDragger {
34438
- constructor(viewer, normal, color) {
34557
+ constructor(viewer) {
34439
34558
  super(viewer);
34559
+ this.helpers = [];
34560
+ this.activeHelper = null;
34440
34561
  this.transformChange = () => {
34441
- 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);
34442
34567
  this.viewer.update();
34568
+ this.changed = true;
34569
+ };
34570
+ this.translateDrag = (event) => {
34571
+ this.orbit.enabled = !event.value;
34572
+ this.rotate.enabled = !event.value;
34443
34573
  };
34444
- this.transformDrag = (event) => {
34574
+ this.rotateDrag = (event) => {
34445
34575
  this.orbit.enabled = !event.value;
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
- this.transform.camera = this.viewer.camera;
34584
+ this.translate.camera = this.viewer.camera;
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();
34596
+ };
34597
+ this.onKeyDown = (event) => {
34598
+ if (event.key === "Shift")
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();
34604
+ };
34605
+ this.onKeyUp = (event) => {
34606
+ if (event.key === "Shift")
34607
+ this.rotate.setRotationSnap(null);
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));
34453
34628
  };
34454
34629
  this.onDoubleClick = (event) => {
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);
34637
+ this.transformChange();
34455
34638
  event.stopPropagation();
34456
- this.plane.negate();
34457
- this.viewer.update();
34458
34639
  };
34459
- const extentsSize = viewer.extents.getSize(new Vector3()).length() || 1;
34460
- const extentsCenter = viewer.extents.getCenter(new Vector3());
34461
- const constant = -extentsCenter.dot(normal);
34462
- this.plane = new Plane(normal, constant);
34463
34640
  if (!viewer.renderer.clippingPlanes)
34464
34641
  viewer.renderer.clippingPlanes = [];
34465
- viewer.renderer.clippingPlanes.push(this.plane);
34466
- this.planeHelper = new PlaneHelper(this.plane, extentsSize, color, extentsCenter);
34467
- this.viewer.helpers.add(this.planeHelper);
34468
- this.planeCenter = new Object3D();
34469
- this.planeCenter.position.copy(extentsCenter);
34470
- this.viewer.helpers.add(this.planeCenter);
34471
- this.transform = new TransformControls(viewer.camera, viewer.canvas);
34472
- this.transform.showX = !!normal.x;
34473
- this.transform.showY = !!normal.y;
34474
- this.transform.showZ = !!normal.z;
34475
- this.transform.attach(this.planeCenter);
34476
- this.transform.addEventListener("change", this.transformChange);
34477
- this.transform.addEventListener("dragging-changed", this.transformDrag);
34478
- this.viewer.helpers.add(this.transform.getHelper());
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();
34650
+ this.translate = new TransformControls(viewer.camera, viewer.canvas);
34651
+ this.translate.setMode("translate");
34652
+ this.translate.setSpace("local");
34653
+ this.translate.showX = false;
34654
+ this.translate.showY = false;
34655
+ this.translate.showZ = true;
34656
+ this.translate.addEventListener("change", this.transformChange);
34657
+ this.translate.addEventListener("dragging-changed", this.translateDrag);
34658
+ this.viewer.helpers.add(this.translate.getHelper());
34659
+ this.rotate = new TransformControls(viewer.camera, viewer.canvas);
34660
+ this.rotate.setMode("rotate");
34661
+ this.rotate.setSpace("local");
34662
+ this.rotate.showX = true;
34663
+ this.rotate.showY = true;
34664
+ this.rotate.showZ = false;
34665
+ this.rotate.addEventListener("change", this.transformChange);
34666
+ this.rotate.addEventListener("dragging-changed", this.rotateDrag);
34667
+ this.viewer.helpers.add(this.rotate.getHelper());
34668
+ this.setActiveHelper(this.helpers[this.helpers.length - 1]);
34479
34669
  this.viewer.addEventListener("explode", this.updatePlaneSize);
34480
34670
  this.viewer.addEventListener("show", this.updatePlaneSize);
34481
34671
  this.viewer.addEventListener("showall", this.updatePlaneSize);
34482
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);
34483
34677
  this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
34678
+ window.addEventListener("keydown", this.onKeyDown);
34679
+ window.addEventListener("keyup", this.onKeyUp);
34484
34680
  this.viewer.update();
34485
34681
  }
34486
34682
  dispose() {
@@ -34488,34 +34684,125 @@ void main() {
34488
34684
  this.viewer.removeEventListener("show", this.updatePlaneSize);
34489
34685
  this.viewer.removeEventListener("showall", this.updatePlaneSize);
34490
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);
34491
34691
  this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
34492
- this.transform.removeEventListener("change", this.transformChange);
34493
- this.transform.removeEventListener("dragging-changed", this.transformDrag);
34494
- this.transform.getHelper().removeFromParent();
34495
- this.transform.detach();
34496
- this.transform.dispose();
34497
- this.planeHelper.removeFromParent();
34498
- this.planeHelper.dispose();
34499
- this.planeCenter.removeFromParent();
34692
+ window.removeEventListener("keydown", this.onKeyDown);
34693
+ window.removeEventListener("keyup", this.onKeyUp);
34694
+ this.translate.removeEventListener("change", this.transformChange);
34695
+ this.translate.removeEventListener("dragging-changed", this.translateDrag);
34696
+ this.translate.getHelper().removeFromParent();
34697
+ this.translate.detach();
34698
+ this.translate.dispose();
34699
+ this.rotate.removeEventListener("change", this.transformChange);
34700
+ this.rotate.removeEventListener("dragging-changed", this.rotateDrag);
34701
+ this.rotate.getHelper().removeFromParent();
34702
+ this.rotate.detach();
34703
+ this.rotate.dispose();
34704
+ this.helpers.forEach((helper) => {
34705
+ helper.removeFromParent();
34706
+ helper.dispose();
34707
+ });
34708
+ this.helpers = [];
34709
+ this.activeHelper = null;
34500
34710
  super.dispose();
34501
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
+ }
34502
34786
  }
34503
34787
 
34504
34788
  class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
34505
34789
  constructor(viewer) {
34506
- super(viewer, new Vector3(1, 0, 0), 0xff0000);
34790
+ super(viewer);
34791
+ this.addPlaneX();
34507
34792
  }
34508
34793
  }
34509
34794
 
34510
34795
  class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
34511
34796
  constructor(viewer) {
34512
- super(viewer, new Vector3(0, 1, 0), 0x00ff00);
34797
+ super(viewer);
34798
+ this.addPlaneY();
34513
34799
  }
34514
34800
  }
34515
34801
 
34516
34802
  class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
34517
34803
  constructor(viewer) {
34518
- super(viewer, new Vector3(0, 0, 1), 0x0000ff);
34804
+ super(viewer);
34805
+ this.addPlaneZ();
34519
34806
  }
34520
34807
  }
34521
34808
 
@@ -34592,120 +34879,6 @@ void main() {
34592
34879
  }
34593
34880
  }
34594
34881
 
34595
- const DESKTOP_SNAP_DISTANCE = 10;
34596
- const MOBILE_SNAP_DISTANCE = 50;
34597
- const _vertex = new Vector3();
34598
- const _start$1 = new Vector3();
34599
- const _end$1 = new Vector3();
34600
- const _line = new Line3();
34601
- const _center = new Vector3();
34602
- const _projection = new Vector3();
34603
- class Snapper {
34604
- constructor(camera, renderer, canvas) {
34605
- this.camera = camera;
34606
- this.renderer = renderer;
34607
- this.canvas = canvas;
34608
- this.threshold = 0.0001;
34609
- this.raycaster = new Raycaster();
34610
- this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
34611
- this.edgesCache = new WeakMap();
34612
- }
34613
- isMobile() {
34614
- if (typeof navigator === "undefined")
34615
- return false;
34616
- return /Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
34617
- }
34618
- getMousePosition(event, target) {
34619
- return target.set(event.clientX, event.clientY);
34620
- }
34621
- getPointerIntersects(mouse, objects) {
34622
- const rect = this.canvas.getBoundingClientRect();
34623
- const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
34624
- const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
34625
- const coords = new Vector2(x, y);
34626
- this.raycaster.setFromCamera(coords, this.camera);
34627
- this.raycaster.params = {
34628
- Mesh: {},
34629
- Line: { threshold: this.threshold },
34630
- Line2: { threshold: this.threshold },
34631
- LOD: {},
34632
- Points: { threshold: this.threshold },
34633
- Sprite: {},
34634
- };
34635
- let intersects = this.raycaster.intersectObjects(objects, false);
34636
- (this.renderer.clippingPlanes || []).forEach((plane) => {
34637
- intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
34638
- });
34639
- return intersects;
34640
- }
34641
- getDetectRadius(point) {
34642
- const camera = this.camera;
34643
- if (camera.isOrthographicCamera) {
34644
- const fieldHeight = (camera.top - camera.bottom) / camera.zoom;
34645
- const canvasHeight = this.canvas.height;
34646
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
34647
- return this.detectRadiusInPixels * worldUnitsPerPixel;
34648
- }
34649
- if (camera.isPerspectiveCamera) {
34650
- const distance = camera.position.distanceTo(point);
34651
- const fieldHeight = 2 * Math.tan(MathUtils.degToRad(camera.fov * 0.5)) * distance;
34652
- const canvasHeight = this.canvas.height;
34653
- const worldUnitsPerPixel = fieldHeight / canvasHeight;
34654
- return this.detectRadiusInPixels * worldUnitsPerPixel;
34655
- }
34656
- return 0.1;
34657
- }
34658
- getSnapPoint(mouse, objects) {
34659
- const intersections = this.getPointerIntersects(mouse, objects);
34660
- if (intersections.length === 0)
34661
- return undefined;
34662
- const object = intersections[0].object;
34663
- const intersectionPoint = intersections[0].point;
34664
- const localPoint = object.worldToLocal(intersectionPoint.clone());
34665
- let snapPoint;
34666
- let snapDistance = this.getDetectRadius(intersectionPoint);
34667
- const geometry = object.geometry;
34668
- const positions = geometry.attributes.position.array;
34669
- for (let i = 0; i < positions.length; i += 3) {
34670
- _vertex.set(positions[i], positions[i + 1], positions[i + 2]);
34671
- const distance = _vertex.distanceTo(localPoint);
34672
- if (distance < snapDistance) {
34673
- snapDistance = distance;
34674
- snapPoint = _vertex.clone();
34675
- }
34676
- }
34677
- if (snapPoint)
34678
- return object.localToWorld(snapPoint);
34679
- let edges = this.edgesCache.get(geometry);
34680
- if (!edges) {
34681
- edges = new EdgesGeometry(geometry);
34682
- this.edgesCache.set(geometry, edges);
34683
- }
34684
- const edgePositions = edges.attributes.position.array;
34685
- for (let i = 0; i < edgePositions.length; i += 6) {
34686
- _start$1.set(edgePositions[i], edgePositions[i + 1], edgePositions[i + 2]);
34687
- _end$1.set(edgePositions[i + 3], edgePositions[i + 4], edgePositions[i + 5]);
34688
- _line.set(_start$1, _end$1);
34689
- _line.getCenter(_center);
34690
- const centerDistance = _center.distanceTo(localPoint);
34691
- if (centerDistance < snapDistance) {
34692
- snapDistance = centerDistance;
34693
- snapPoint = _center.clone();
34694
- continue;
34695
- }
34696
- _line.closestPointToPoint(localPoint, true, _projection);
34697
- const lineDistance = _projection.distanceTo(localPoint);
34698
- if (lineDistance < snapDistance) {
34699
- snapDistance = lineDistance;
34700
- snapPoint = _projection.clone();
34701
- }
34702
- }
34703
- if (snapPoint)
34704
- return object.localToWorld(snapPoint);
34705
- return intersectionPoint.clone();
34706
- }
34707
- }
34708
-
34709
34882
  const _downPoint = new Vector2();
34710
34883
  class MeasureLineDragger extends OrbitDragger {
34711
34884
  constructor(viewer) {
@@ -35117,6 +35290,12 @@ void main() {
35117
35290
  this.movementSpeed = 0.1;
35118
35291
  this.multiplier = 3;
35119
35292
  this.groundFollowingSkippedFrames = 0;
35293
+ this.GROUND_BOX_HALF_SIZE = 20;
35294
+ this.GROUND_BOX_REFRESH_THRESHOLD = 0.3;
35295
+ this._groundObjectBoxes = new Map();
35296
+ this._activeGroundObjects = [];
35297
+ this._groundBox = new Box3();
35298
+ this._groundBoxCenter = new Vector3();
35120
35299
  this.moveWheel = 0;
35121
35300
  this.mouseDragOn = false;
35122
35301
  this._up = new Vector3();
@@ -35183,11 +35362,20 @@ void main() {
35183
35362
  }
35184
35363
  };
35185
35364
  this.onKeyUp = (event) => {
35186
- 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
+ }
35187
35369
  this.update();
35370
+ }
35188
35371
  };
35189
35372
  this.camera = camera;
35190
35373
  this.groundObjects = groundObjects;
35374
+ for (const obj of groundObjects) {
35375
+ this._groundObjectBoxes.set(obj, new Box3().setFromObject(obj));
35376
+ }
35377
+ const pos = this.object.position;
35378
+ this._rebuildGroundBox(pos);
35191
35379
  this.raycaster = new Raycaster();
35192
35380
  this.raycaster.near = 0;
35193
35381
  this.raycaster.far = this.EYE_HEIGHT + this.FAILING_DISTANCE;
@@ -35222,10 +35410,29 @@ void main() {
35222
35410
  window.removeEventListener("keyup", this.onKeyUp);
35223
35411
  super.dispose();
35224
35412
  }
35413
+ _rebuildGroundBox(center) {
35414
+ const h = this.GROUND_BOX_HALF_SIZE;
35415
+ this._groundBoxCenter.copy(center);
35416
+ this._groundBox.set(new Vector3(center.x - h, center.y - h * 4, center.z - h), new Vector3(center.x + h, center.y + h * 4, center.z + h));
35417
+ this._activeGroundObjects = this.groundObjects.filter((obj) => {
35418
+ const objectBox = this._groundObjectBoxes.get(obj);
35419
+ return objectBox !== undefined && this._groundBox.intersectsBox(objectBox);
35420
+ });
35421
+ }
35422
+ _needsGroundBoxRebuild(pos) {
35423
+ if (this._activeGroundObjects.length === 0 && this.groundObjects.length > 0)
35424
+ return true;
35425
+ const threshold = this.GROUND_BOX_HALF_SIZE * this.GROUND_BOX_REFRESH_THRESHOLD;
35426
+ return (Math.abs(pos.x - this._groundBoxCenter.x) > threshold || Math.abs(pos.z - this._groundBoxCenter.z) > threshold);
35427
+ }
35225
35428
  updateGroundFollowing() {
35429
+ const pos = this.object.position;
35430
+ if (this._needsGroundBoxRebuild(pos)) {
35431
+ this._rebuildGroundBox(pos);
35432
+ }
35226
35433
  this._up.copy(this.camera.up).negate();
35227
- this.raycaster.set(this.object.position, this._up);
35228
- const intersects = this.raycaster.intersectObjects(this.groundObjects, false);
35434
+ this.raycaster.set(pos, this._up);
35435
+ const intersects = this.raycaster.intersectObjects(this._activeGroundObjects, false);
35229
35436
  if (intersects.length > 0) {
35230
35437
  const groundY = intersects[0].point.y;
35231
35438
  const targetY = groundY + this.EYE_HEIGHT;
@@ -35777,6 +35984,7 @@ void main() {
35777
35984
  draggers.registerDragger("Orbit", (viewer) => new OrbitDragger(viewer));
35778
35985
  draggers.registerDragger("Zoom", (viewer) => new ZoomDragger(viewer));
35779
35986
  draggers.registerDragger("MeasureLine", (viewer) => new MeasureLineDragger(viewer));
35987
+ draggers.registerDragger("CuttingPlane", (viewer) => new CuttingPlaneDragger(viewer));
35780
35988
  draggers.registerDragger("CuttingPlaneXAxis", (viewer) => new CuttingPlaneXAxisDragger(viewer));
35781
35989
  draggers.registerDragger("CuttingPlaneYAxis", (viewer) => new CuttingPlaneYAxisDragger(viewer));
35782
35990
  draggers.registerDragger("CuttingPlaneZAxis", (viewer) => new CuttingPlaneZAxisDragger(viewer));
@@ -37311,7 +37519,7 @@ void main() {
37311
37519
  this.getMousePosition(event, this.downPosition);
37312
37520
  };
37313
37521
  this.onPointerUp = (event) => {
37314
- if (!event.isPrimary)
37522
+ if (!event.isPrimary || event.button !== 0)
37315
37523
  return;
37316
37524
  const upPosition = this.getMousePosition(event, new Vector2());
37317
37525
  if (upPosition.distanceTo(this.downPosition) !== 0)
@@ -38774,6 +38982,81 @@ void main() {
38774
38982
  this.mergedObjectMap = new Map();
38775
38983
  this.mergedGeometryVisibility = new Map();
38776
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;
38777
39060
  }
38778
39061
  setVisibleEdges(visible) {
38779
39062
  this.visibleEdges = visible;
@@ -39663,36 +39946,82 @@ void main() {
39663
39946
  }
39664
39947
  }
39665
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;
39666
39955
  material.onBeforeCompile = (shader) => {
39956
+ shader.uniforms.transformTexture = uniforms.transformTexture;
39957
+ shader.uniforms.transformTextureSize = uniforms.transformTextureSize;
39667
39958
  shader.vertexShader = shader.vertexShader.replace(
39668
39959
  "#include <common>",
39669
39960
  `
39670
39961
  #include <common>
39962
+
39671
39963
  attribute float visibility;
39964
+ attribute float objectId;
39672
39965
  varying float vVisibility;
39673
- `
39674
- );
39675
- shader.fragmentShader = shader.fragmentShader.replace(
39676
- "#include <common>",
39677
- `
39678
- #include <common>
39679
- 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
+ }
39680
39983
  `
39681
39984
  );
39682
39985
  shader.vertexShader = shader.vertexShader.replace(
39683
39986
  "void main() {",
39684
39987
  `
39685
39988
  void main() {
39686
- vVisibility = visibility;
39989
+ mat4 batchingMatrix = getTransformMatrix(objectId);
39990
+ vVisibility = visibility;
39687
39991
  `
39688
39992
  );
39689
- shader.fragmentShader = shader.fragmentShader.replace(
39690
- "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;
39691
40000
  `
40001
+ );
40002
+ }
40003
+ shader.vertexShader = shader.vertexShader.replace(
40004
+ "#include <begin_vertex>",
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
+ `
39692
40021
  void main() {
39693
40022
  if (vVisibility < 0.5) discard;
39694
40023
  `
39695
- );
40024
+ );
39696
40025
  };
39697
40026
  material.needsUpdate = true;
39698
40027
  return material;
@@ -39807,6 +40136,8 @@ void main() {
39807
40136
  this.objectIdToIndex.clear();
39808
40137
  this.maxObjectId = 0;
39809
40138
  this.objectVisibility = new Float32Array();
40139
+ this.meshToNodeMap = null;
40140
+ this.visibilityMaterials.clear();
39810
40141
  }
39811
40142
  setStructureTransform(structureId, matrix) {
39812
40143
  const rootGroup = this.structureRoots.get(structureId);
@@ -39922,12 +40253,15 @@ void main() {
39922
40253
  });
39923
40254
  this.originalObjects.clear();
39924
40255
  this.originalObjectsToSelection.clear();
40256
+ this.objectIdToIndex.clear();
40257
+ this.maxObjectId = 0;
39925
40258
  const structureGroups = new Map();
39926
40259
  this.dispatchEvent("optimizationprogress", {
39927
40260
  phase: "collecting",
39928
40261
  progress: 5,
39929
40262
  message: "Collecting scene objects...",
39930
40263
  });
40264
+ let totalObjectsToMerge = 0;
39931
40265
  this.scene.traverse((object) => {
39932
40266
  if (object.userData.structureId) {
39933
40267
  const structureId = object.userData.structureId;
@@ -39945,17 +40279,32 @@ void main() {
39945
40279
  });
39946
40280
  }
39947
40281
  const group = structureGroups.get(structureId);
40282
+ let added = false;
39948
40283
  if (object instanceof Mesh) {
39949
40284
  this.addToMaterialGroup(object, group.mapMeshes, group.meshes);
40285
+ added = true;
39950
40286
  } else if (object instanceof LineSegments) {
39951
40287
  this.addToMaterialGroup(object, group.mapLineSegments, group.lineSegments);
40288
+ added = true;
39952
40289
  } else if (object instanceof Line$1) {
39953
40290
  this.addToMaterialGroup(object, group.mapLines, group.lines);
40291
+ added = true;
39954
40292
  } else if (object instanceof Points) {
39955
40293
  this.addToMaterialGroup(object, group.mapPoints, group.points);
40294
+ added = true;
40295
+ }
40296
+ if (added) {
40297
+ totalObjectsToMerge++;
39956
40298
  }
39957
40299
  }
39958
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
+ }
39959
40308
  let processedGroups = 0;
39960
40309
  const totalGroups = structureGroups.size;
39961
40310
  this.dispatchEvent("optimizationprogress", {
@@ -40000,7 +40349,6 @@ void main() {
40000
40349
  this.originalObjectsToSelection.add(obj);
40001
40350
  }
40002
40351
  });
40003
- this.initializeObjectVisibility();
40004
40352
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
40005
40353
  this.dispatchEvent("optimizationprogress", {
40006
40354
  phase: "complete",
@@ -40069,6 +40417,7 @@ void main() {
40069
40417
  }
40070
40418
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40071
40419
  const mergedMesh = new Mesh(mergedGeometry, visibilityMaterial);
40420
+ mergedMesh.frustumCulled = false;
40072
40421
  mergedMesh.userData.isOptimized = true;
40073
40422
  rootGroup.add(mergedMesh);
40074
40423
  this.mergedMesh.add(mergedMesh);
@@ -40185,6 +40534,7 @@ void main() {
40185
40534
  geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40186
40535
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40187
40536
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
40537
+ mergedLine.frustumCulled = false;
40188
40538
  mergedLine.userData.isEdge = isEdge;
40189
40539
  mergedLine.userData.isOptimized = true;
40190
40540
  const mergedObjects = [mergedLine];
@@ -40273,6 +40623,7 @@ void main() {
40273
40623
  mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
40274
40624
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
40275
40625
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
40626
+ mergedLine.frustumCulled = false;
40276
40627
  mergedLine.userData.isEdge = isEdge;
40277
40628
  mergedLine.userData.isOptimized = true;
40278
40629
  if (this.useVAO) {
@@ -40342,7 +40693,27 @@ void main() {
40342
40693
  const mergedObjects = [];
40343
40694
  if (geometries.length > 0) {
40344
40695
  const mergedGeometry = mergeGeometries(geometries, false);
40345
- 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;
40346
40717
  mergedPoints.userData.isOptimized = true;
40347
40718
  if (this.useVAO) {
40348
40719
  this.createVAO(mergedPoints);
@@ -40411,13 +40782,33 @@ void main() {
40411
40782
  geometriesWithIndex.push(clonedGeometry);
40412
40783
  });
40413
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));
40414
40803
  const material = new LineBasicMaterial({
40415
40804
  vertexColors: true,
40416
40805
  });
40806
+ const visibilityMaterial = this.createVisibilityMaterial(material);
40417
40807
  if (this.useVAO) {
40418
40808
  this.createVAO(finalGeometry);
40419
40809
  }
40420
- const mergedLine = new LineSegments(finalGeometry, material);
40810
+ const mergedLine = new LineSegments(finalGeometry, visibilityMaterial);
40811
+ mergedLine.frustumCulled = false;
40421
40812
  mergedLine.userData.structureId = structureId;
40422
40813
  mergedLine.userData.isOptimized = true;
40423
40814
  rootGroup.add(mergedLine);
@@ -40536,18 +40927,50 @@ void main() {
40536
40927
  console.warn("No merged objects to transform");
40537
40928
  return;
40538
40929
  }
40539
- this.objectTransforms = new Map(objectTransformMap);
40540
- for (const mesh of this.mergedMesh) {
40541
- this._applyTransformToMergedObject(mesh);
40542
- }
40543
- for (const line of this.mergedLines) {
40544
- this._applyTransformToMergedObject(line);
40930
+ if (!this.transformData) {
40931
+ console.warn("Transform texture not initialized");
40932
+ return;
40545
40933
  }
40546
- for (const lineSegment of this.mergedLineSegments) {
40547
- 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
+ }
40548
40965
  }
40549
- for (const point of this.mergedPoints) {
40550
- 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
+ }
40551
40974
  }
40552
40975
  }
40553
40976
  createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
@@ -40564,21 +40987,66 @@ void main() {
40564
40987
  ? objects
40565
40988
  : Array.from(objects)
40566
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
+ }
40567
40999
  for (const obj of objectsArray) {
40568
41000
  if (!obj.geometry || !obj.geometry.attributes.position) continue;
40569
- const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
40570
- if (obj.matrixWorld) {
40571
- 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;
40572
41019
  }
40573
- if (boundingBox.isEmpty()) continue;
40574
- const objectCenter = new Vector3();
40575
- boundingBox.getCenter(objectCenter);
40576
- const direction = objectCenter.clone().sub(explodeCenter);
40577
- const distance = direction.length();
41020
+ const explodeVector = obj.userData.explodeVector;
41021
+ const distance = explodeVector.length();
40578
41022
  if (distance > 0) {
40579
- direction.normalize();
40580
- const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
40581
- 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);
40582
41050
  transformMap.set(obj, matrix);
40583
41051
  }
40584
41052
  }
@@ -40586,116 +41054,11 @@ void main() {
40586
41054
  }
40587
41055
  clearTransforms() {
40588
41056
  this.objectTransforms.clear();
40589
- for (const mesh of this.mergedMesh) {
40590
- this._restoreOriginalGeometry(mesh);
40591
- }
40592
- for (const line of this.mergedLines) {
40593
- this._restoreOriginalGeometry(line);
40594
- }
40595
- for (const lineSegment of this.mergedLineSegments) {
40596
- this._restoreOriginalGeometry(lineSegment);
40597
- }
40598
- for (const point of this.mergedPoints) {
40599
- this._restoreOriginalGeometry(point);
40600
- }
41057
+ this._resetTransformData(true);
40601
41058
  }
40602
41059
  clearHandleTransforms() {
40603
41060
  this.clearTransforms();
40604
41061
  }
40605
- _applyTransformToMergedObject(mergedObject) {
40606
- const objectData = this.mergedObjectMap.get(mergedObject.uuid);
40607
- if (!objectData || !objectData.objectMapping) return;
40608
- const geometry = mergedObject.geometry;
40609
- if (!geometry || !geometry.attributes.position) return;
40610
- const positionAttr = geometry.attributes.position;
40611
- const positions = positionAttr.array;
40612
- if (!this.transformedGeometries.has(mergedObject.uuid)) {
40613
- this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
40614
- }
40615
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40616
- const tempVector = new Vector3();
40617
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40618
- const transform = this.objectTransforms.get(originalMesh);
40619
- if (!transform) {
40620
- const startIdx = mappingData.startVertexIndex * 3;
40621
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40622
- for (let i = startIdx; i < endIdx; i++) {
40623
- positions[i] = originalPositions[i];
40624
- }
40625
- continue;
40626
- }
40627
- const startVertex = mappingData.startVertexIndex;
40628
- const vertexCount = mappingData.vertexCount;
40629
- for (let i = 0; i < vertexCount; i++) {
40630
- const idx = (startVertex + i) * 3;
40631
- tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
40632
- tempVector.applyMatrix4(transform);
40633
- positions[idx] = tempVector.x;
40634
- positions[idx + 1] = tempVector.y;
40635
- positions[idx + 2] = tempVector.z;
40636
- }
40637
- }
40638
- if (geometry.attributes.normal) {
40639
- this._updateNormalsForTransform(geometry, objectData, originalPositions);
40640
- }
40641
- positionAttr.needsUpdate = true;
40642
- geometry.computeBoundingSphere();
40643
- geometry.computeBoundingBox();
40644
- }
40645
- _updateNormalsForTransform(geometry, objectData, originalPositions) {
40646
- const normalAttr = geometry.attributes.normal;
40647
- if (!normalAttr) return;
40648
- const normals = normalAttr.array;
40649
- const tempVector = new Vector3();
40650
- const normalMatrix = new Matrix4();
40651
- const normalsKey = `${geometry.uuid}_normals`;
40652
- if (!this.transformedGeometries.has(normalsKey)) {
40653
- this.transformedGeometries.set(normalsKey, new Float32Array(normals));
40654
- }
40655
- const originalNormals = this.transformedGeometries.get(normalsKey);
40656
- for (const [originalMesh, mappingData] of objectData.objectMapping) {
40657
- const transform = this.objectTransforms.get(originalMesh);
40658
- if (!transform) {
40659
- const startIdx = mappingData.startVertexIndex * 3;
40660
- const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
40661
- for (let i = startIdx; i < endIdx; i++) {
40662
- normals[i] = originalNormals[i];
40663
- }
40664
- continue;
40665
- }
40666
- normalMatrix.copy(transform).invert().transpose();
40667
- const startVertex = mappingData.startVertexIndex;
40668
- const vertexCount = mappingData.vertexCount;
40669
- for (let i = 0; i < vertexCount; i++) {
40670
- const idx = (startVertex + i) * 3;
40671
- tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
40672
- tempVector.applyMatrix4(normalMatrix).normalize();
40673
- normals[idx] = tempVector.x;
40674
- normals[idx + 1] = tempVector.y;
40675
- normals[idx + 2] = tempVector.z;
40676
- }
40677
- }
40678
- normalAttr.needsUpdate = true;
40679
- }
40680
- _restoreOriginalGeometry(mergedObject) {
40681
- const geometry = mergedObject.geometry;
40682
- if (!geometry || !geometry.attributes.position) return;
40683
- const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
40684
- if (originalPositions) {
40685
- const positions = geometry.attributes.position.array;
40686
- positions.set(originalPositions);
40687
- geometry.attributes.position.needsUpdate = true;
40688
- }
40689
- const normalsKey = `${geometry.uuid}_normals`;
40690
- const originalNormals = this.transformedGeometries.get(normalsKey);
40691
- if (originalNormals && geometry.attributes.normal) {
40692
- const normals = geometry.attributes.normal.array;
40693
- normals.set(originalNormals);
40694
- geometry.attributes.normal.needsUpdate = true;
40695
- }
40696
- geometry.computeBoundingSphere();
40697
- geometry.computeBoundingBox();
40698
- }
40699
41062
  syncHiddenObjects() {
40700
41063
  if (this.mergedObjectMap.size === 0) {
40701
41064
  console.log("No merged objects to sync");
@@ -57746,12 +58109,14 @@ js: import "konva/skia-backend";
57746
58109
  if (!this.renderer)
57747
58110
  return;
57748
58111
  this._markup.clearOverlay();
58112
+ this.emitEvent({ type: "clearoverlay" });
57749
58113
  this.update();
57750
58114
  }
57751
58115
  clearSlices() {
57752
58116
  if (!this.renderer)
57753
58117
  return;
57754
58118
  this.renderer.clippingPlanes = [];
58119
+ this.emitEvent({ type: "clearslices" });
57755
58120
  this.update();
57756
58121
  }
57757
58122
  getSelected() {