@inweb/viewer-three 25.9.8 → 25.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +26 -18
  2. package/dist/viewer-three.js +10315 -6179
  3. package/dist/viewer-three.js.map +1 -1
  4. package/dist/viewer-three.min.js +3 -2
  5. package/dist/viewer-three.module.js +1881 -322
  6. package/dist/viewer-three.module.js.map +1 -1
  7. package/lib/Viewer/IDisposable.d.ts +6 -0
  8. package/lib/Viewer/Viewer.d.ts +113 -14
  9. package/lib/Viewer/commands/ApplyModelTransform.d.ts +1 -0
  10. package/lib/Viewer/commands/ClearMarkup.d.ts +1 -0
  11. package/lib/Viewer/commands/ClearSlices.d.ts +1 -0
  12. package/lib/Viewer/commands/CreatePreview.d.ts +1 -0
  13. package/lib/Viewer/commands/Explode.d.ts +1 -0
  14. package/lib/Viewer/commands/GetDefaultViewPositions.d.ts +1 -0
  15. package/lib/Viewer/commands/GetModels.d.ts +1 -0
  16. package/lib/Viewer/commands/GetSelected.d.ts +1 -0
  17. package/lib/Viewer/commands/HideSelected.d.ts +1 -0
  18. package/lib/Viewer/commands/IsolateSelected.d.ts +1 -0
  19. package/lib/Viewer/commands/RegenerateAll.d.ts +1 -0
  20. package/lib/Viewer/commands/ResetView.d.ts +1 -0
  21. package/lib/Viewer/commands/SelectModel.d.ts +1 -0
  22. package/lib/Viewer/commands/SetActiveDragger.d.ts +1 -0
  23. package/lib/Viewer/commands/SetDefaultViewPosition.d.ts +13 -0
  24. package/lib/Viewer/commands/SetMarkupColor.d.ts +1 -0
  25. package/lib/Viewer/commands/SetSelected.d.ts +1 -0
  26. package/lib/Viewer/commands/ShowAll.d.ts +1 -0
  27. package/lib/Viewer/commands/Unselect.d.ts +1 -0
  28. package/lib/Viewer/commands/ZoomToExtents.d.ts +1 -0
  29. package/lib/Viewer/commands/ZoomToObjects.d.ts +1 -0
  30. package/lib/Viewer/commands/ZoomToSelected.d.ts +1 -0
  31. package/lib/Viewer/commands/index.d.ts +22 -0
  32. package/lib/Viewer/components/AxesHelperComponent.d.ts +10 -0
  33. package/lib/Viewer/components/BackgroundComponent.d.ts +4 -4
  34. package/lib/Viewer/components/{DefaultCameraPositionComponent.d.ts → DefaultPositionComponent.d.ts} +3 -2
  35. package/lib/Viewer/components/ExtentsComponent.d.ts +8 -0
  36. package/lib/Viewer/components/ExtentsHelperComponent.d.ts +9 -0
  37. package/lib/Viewer/components/LightComponent.d.ts +5 -5
  38. package/lib/Viewer/components/RenderLoopComponent.d.ts +3 -3
  39. package/lib/Viewer/components/ResizeCanvasComponent.d.ts +2 -2
  40. package/lib/Viewer/components/SelectionComponent.d.ts +19 -0
  41. package/lib/Viewer/components/ViewPositionComponent.d.ts +31 -0
  42. package/lib/Viewer/components/WCSHelperComponent.d.ts +9 -0
  43. package/lib/Viewer/controls/WalkControls.d.ts +26 -0
  44. package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +17 -0
  45. package/lib/Viewer/draggers/CuttingPlaneXAxis.d.ts +5 -0
  46. package/lib/Viewer/draggers/CuttingPlaneYAxis.d.ts +5 -0
  47. package/lib/Viewer/draggers/CuttingPlaneZAxis.d.ts +5 -0
  48. package/lib/Viewer/draggers/OrbitDragger.d.ts +9 -5
  49. package/lib/Viewer/draggers/WalkDragger.d.ts +7 -33
  50. package/lib/Viewer/helpers/PlaneHelper.d.ts +11 -0
  51. package/lib/Viewer/helpers/WCSHelper.d.ts +10 -0
  52. package/lib/Viewer/loaders/GLTFLoadingManager.d.ts +3 -3
  53. package/lib/index.d.ts +1 -0
  54. package/package.json +7 -6
  55. package/src/Viewer/IDisposable.ts +29 -0
  56. package/src/Viewer/Viewer.ts +218 -49
  57. package/src/Viewer/commands/ApplyModelTransform.ts +33 -0
  58. package/src/Viewer/commands/ClearMarkup.ts +29 -0
  59. package/src/Viewer/commands/ClearSlices.ts +32 -0
  60. package/src/Viewer/commands/CreatePreview.ts +32 -0
  61. package/src/Viewer/commands/Explode.ts +83 -0
  62. package/src/Viewer/commands/GetDefaultViewPositions.ts +31 -0
  63. package/src/Viewer/commands/GetModels.ts +32 -0
  64. package/src/Viewer/commands/GetSelected.ts +31 -0
  65. package/src/Viewer/commands/HideSelected.ts +40 -0
  66. package/src/Viewer/commands/IsolateSelected.ts +50 -0
  67. package/src/Viewer/commands/RegenerateAll.ts +32 -0
  68. package/src/Viewer/commands/ResetView.ts +41 -0
  69. package/src/Viewer/commands/SelectModel.ts +32 -0
  70. package/src/Viewer/commands/SetActiveDragger.ts +29 -0
  71. package/src/Viewer/commands/SetDefaultViewPosition.ts +83 -0
  72. package/src/Viewer/commands/SetMarkupColor.ts +30 -0
  73. package/src/Viewer/commands/SetSelected.ts +44 -0
  74. package/src/Viewer/commands/ShowAll.ts +34 -0
  75. package/src/Viewer/commands/Unselect.ts +37 -0
  76. package/src/Viewer/commands/ZoomToExtents.ts +47 -0
  77. package/src/Viewer/commands/ZoomToObjects.ts +55 -0
  78. package/src/Viewer/commands/ZoomToSelected.ts +51 -0
  79. package/src/Viewer/commands/index.ts +45 -0
  80. package/src/Viewer/components/AxesHelperComponent.ts +70 -0
  81. package/src/Viewer/components/BackgroundComponent.ts +9 -7
  82. package/src/Viewer/components/{DefaultCameraPositionComponent.ts → DefaultPositionComponent.ts} +11 -22
  83. package/src/Viewer/components/ExtentsComponent.ts +54 -0
  84. package/src/Viewer/components/ExtentsHelperComponent.ts +58 -0
  85. package/src/Viewer/components/LightComponent.ts +14 -10
  86. package/src/Viewer/components/RenderLoopComponent.ts +6 -6
  87. package/src/Viewer/components/ResizeCanvasComponent.ts +2 -2
  88. package/src/Viewer/components/SelectionComponent.ts +132 -0
  89. package/src/Viewer/components/ViewPositionComponent.ts +165 -0
  90. package/src/Viewer/components/WCSHelperComponent.ts +46 -0
  91. package/src/Viewer/controls/WalkControls.ts +221 -0
  92. package/src/Viewer/draggers/CuttingPlaneDragger.ts +110 -0
  93. package/src/Viewer/draggers/CuttingPlaneXAxis.ts +33 -0
  94. package/src/Viewer/draggers/CuttingPlaneYAxis.ts +33 -0
  95. package/src/Viewer/draggers/CuttingPlaneZAxis.ts +33 -0
  96. package/src/Viewer/draggers/OrbitDragger.ts +47 -22
  97. package/src/Viewer/draggers/PanDragger.ts +4 -3
  98. package/src/Viewer/draggers/WalkDragger.ts +27 -216
  99. package/src/Viewer/draggers/ZoomDragger.ts +4 -3
  100. package/src/Viewer/helpers/PlaneHelper.ts +99 -0
  101. package/src/Viewer/helpers/WCSHelper.ts +119 -0
  102. package/src/Viewer/loaders/GLTFLoadingManager.ts +6 -6
  103. package/src/index.ts +2 -0
  104. package/lib/Viewer/IComponent.d.ts +0 -3
  105. package/lib/Viewer/components/ObjectSelectionComponent.d.ts +0 -16
  106. package/lib/Viewer/draggers/ClippingPlaneDragger.d.ts +0 -17
  107. package/src/Viewer/IComponent.ts +0 -3
  108. package/src/Viewer/components/ObjectSelectionComponent.ts +0 -105
  109. package/src/Viewer/draggers/ClippingPlaneDragger.ts +0 -120
@@ -1,4 +1,4 @@
1
- import * as THREE from "three";
1
+ import { Box3, Vector3, Sphere, Vector2, Raycaster, MeshBasicMaterial, LoadingManager, LoaderUtils, MOUSE, TOUCH, EventDispatcher, Clock, Quaternion, Object3D, Euler, Matrix4, LineBasicMaterial, CylinderGeometry, BoxGeometry, BufferGeometry, Float32BufferAttribute, Mesh, OctahedronGeometry, Line, SphereGeometry, TorusGeometry, PlaneGeometry, DoubleSide, Plane, AmbientLight, DirectionalLight, Color, PMREMGenerator, OrthographicCamera, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, Vector4, Scene, PerspectiveCamera, WebGLRenderer, LinearToneMapping } from "three";
2
2
 
3
3
  import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
4
4
 
@@ -380,6 +380,478 @@ class Dragger {
380
380
  updatePreview() {}
381
381
  }
382
382
 
383
+ function applyModelTransform(viewer, model) {
384
+ console.warn("applyModelTransform not implemented");
385
+ }
386
+
387
+ commands("ThreeJS").registerCommand("applyModelTransform", applyModelTransform);
388
+
389
+ commands("ThreeJS").registerCommand("clearMarkup", (viewer => console.warn("clearMarkup not implemented")));
390
+
391
+ commands("ThreeJS").registerCommandAlias("clearMarkup", "clearOverlay");
392
+
393
+ function clearSlices(viewer) {
394
+ viewer.renderer.clippingPlanes = [];
395
+ viewer.update();
396
+ }
397
+
398
+ commands("ThreeJS").registerCommand("clearSlices", clearSlices);
399
+
400
+ function createPreview(viewer, type = "image/jpeg", encoderOptions = .25) {
401
+ viewer.update(true);
402
+ return viewer.canvas.toDataURL(type, encoderOptions);
403
+ }
404
+
405
+ commands("ThreeJS").registerCommand("createPreview", createPreview);
406
+
407
+ function calcObjectDepth(object, depth) {
408
+ let res = depth;
409
+ object.children.forEach((x => {
410
+ const objectDepth = calcObjectDepth(x, depth + 1);
411
+ if (res < objectDepth) res = objectDepth;
412
+ }));
413
+ object.originalPosition = object.position.clone();
414
+ return res;
415
+ }
416
+
417
+ function explodeScene(scene, scale = 0) {
418
+ scale /= 100;
419
+ if (!scene.maxDepth) scene.maxDepth = calcObjectDepth(scene, 1);
420
+ const maxDepth = scene.maxDepth;
421
+ let explodeDepth = scale * (maxDepth - 1) + 1;
422
+ if (maxDepth === 1) explodeDepth = 1;
423
+ function explodeObject(object, depth, parentCenter, parentOffset) {
424
+ const objectBox = (new Box3).setFromObject(object);
425
+ const objectCenter = objectBox.getCenter(new Vector3);
426
+ const objectOffset = parentOffset.clone();
427
+ if (depth > 0 && depth <= explodeDepth) {
428
+ const offset = objectCenter.clone().sub(parentCenter).multiplyScalar(scale);
429
+ objectOffset.add(offset);
430
+ }
431
+ object.children.forEach((object => explodeObject(object, depth + 1, objectCenter, objectOffset)));
432
+ const originalPosition = object.originalPosition;
433
+ object.position.copy(originalPosition);
434
+ if (scale > 0) {
435
+ const direction = objectCenter.sub(parentCenter).normalize();
436
+ object.position.add(direction.add(objectOffset));
437
+ }
438
+ }
439
+ const sceneExtents = (new Box3).setFromObject(scene);
440
+ const sceneCenter = sceneExtents.getCenter(new Vector3);
441
+ explodeObject(scene, 0, sceneCenter, new Vector3(0, 0, 0));
442
+ }
443
+
444
+ function explode(viewer, index = 0) {
445
+ viewer.models.forEach((gltf => explodeScene(gltf.scene, index)));
446
+ viewer.update();
447
+ viewer.emit({
448
+ type: "explode",
449
+ data: index
450
+ });
451
+ }
452
+
453
+ commands("ThreeJS").registerCommand("explode", explode);
454
+
455
+ commands("ThreeJS").registerCommand("collect", (viewer => explode(viewer, 0)));
456
+
457
+ const defaultViewPositions = {
458
+ top: new Vector3(0, 0, 1),
459
+ bottom: new Vector3(0, 0, -1),
460
+ left: new Vector3(-1, 0, 0),
461
+ right: new Vector3(1, 0, 0),
462
+ front: new Vector3(0, 1, 0),
463
+ back: new Vector3(0, -1, 0),
464
+ sw: new Vector3(-.5, -.5, 1).normalize(),
465
+ se: new Vector3(.5, -.5, 1).normalize(),
466
+ ne: new Vector3(.5, .5, 1).normalize(),
467
+ nw: new Vector3(-.5, .5, 1).normalize()
468
+ };
469
+
470
+ function setDefaultViewPosition(viewer, position) {
471
+ const direction = defaultViewPositions[position] || defaultViewPositions["sw"];
472
+ const camera = viewer.camera;
473
+ const center = viewer.extents.getCenter(new Vector3);
474
+ const sphere = viewer.extents.getBoundingSphere(new Sphere);
475
+ const offset = (new Vector3).copy(direction).multiplyScalar(sphere.radius);
476
+ camera.position.copy(center);
477
+ camera.position.add(offset);
478
+ camera.rotation.set(0, 0, 0);
479
+ camera.lookAt(center);
480
+ camera.updateProjectionMatrix();
481
+ viewer.update();
482
+ viewer.emit({
483
+ type: "viewposition",
484
+ data: position
485
+ });
486
+ viewer.executeCommand("zoomToExtents");
487
+ }
488
+
489
+ commands("ThreeJS").registerCommand("setDefaultViewPosition", setDefaultViewPosition);
490
+
491
+ commands("ThreeJS").registerCommand("top", (viewer => setDefaultViewPosition(viewer, "top")));
492
+
493
+ commands("ThreeJS").registerCommand("bottom", (viewer => setDefaultViewPosition(viewer, "bottom")));
494
+
495
+ commands("ThreeJS").registerCommand("left", (viewer => setDefaultViewPosition(viewer, "left")));
496
+
497
+ commands("ThreeJS").registerCommand("right", (viewer => setDefaultViewPosition(viewer, "right")));
498
+
499
+ commands("ThreeJS").registerCommand("front", (viewer => setDefaultViewPosition(viewer, "front")));
500
+
501
+ commands("ThreeJS").registerCommand("back", (viewer => setDefaultViewPosition(viewer, "back")));
502
+
503
+ commands("ThreeJS").registerCommand("sw", (viewer => setDefaultViewPosition(viewer, "sw")));
504
+
505
+ commands("ThreeJS").registerCommand("se", (viewer => setDefaultViewPosition(viewer, "se")));
506
+
507
+ commands("ThreeJS").registerCommand("ne", (viewer => setDefaultViewPosition(viewer, "ne")));
508
+
509
+ commands("ThreeJS").registerCommand("nw", (viewer => setDefaultViewPosition(viewer, "nw")));
510
+
511
+ commands("ThreeJS").registerCommandAlias("top", "k3DViewTop");
512
+
513
+ commands("ThreeJS").registerCommandAlias("bottom", "k3DViewBottom");
514
+
515
+ commands("ThreeJS").registerCommandAlias("left", "k3DViewLeft");
516
+
517
+ commands("ThreeJS").registerCommandAlias("right", "k3DViewRight");
518
+
519
+ commands("ThreeJS").registerCommandAlias("front", "k3DViewFront");
520
+
521
+ commands("ThreeJS").registerCommandAlias("back", "k3DViewBack");
522
+
523
+ commands("ThreeJS").registerCommandAlias("se", "k3DViewSE");
524
+
525
+ commands("ThreeJS").registerCommandAlias("sw", "k3DViewSW");
526
+
527
+ commands("ThreeJS").registerCommandAlias("ne", "k3DViewNE");
528
+
529
+ commands("ThreeJS").registerCommandAlias("nw", "k3DViewNW");
530
+
531
+ function getDefaultViewPositions() {
532
+ return Object.keys(defaultViewPositions);
533
+ }
534
+
535
+ commands("ThreeJS").registerCommand("getDefaultViewPositions", getDefaultViewPositions);
536
+
537
+ function getModels(viewer) {
538
+ const handles = viewer.models.map((model => model.userData.handle || "")).filter((handle => handle));
539
+ return handles;
540
+ }
541
+
542
+ commands("ThreeJS").registerCommand("getModels", getModels);
543
+
544
+ function getSelected(viewer) {
545
+ return viewer.selected.map((object => {
546
+ var _a;
547
+ return (_a = object.userData) === null || _a === void 0 ? void 0 : _a.handle;
548
+ })).filter((handle => handle));
549
+ }
550
+
551
+ commands("ThreeJS").registerCommand("getSelected", getSelected);
552
+
553
+ class SelectionComponent {
554
+ constructor(viewer) {
555
+ this.onPointerDown = event => {
556
+ if (!event.isPrimary || event.button !== 0) return;
557
+ this.getMousePosition(event, this.downPosition);
558
+ };
559
+ this.onPointerUp = event => {
560
+ if (!event.isPrimary) return;
561
+ const upPosition = this.getMousePosition(event, new Vector2);
562
+ if (this.downPosition.distanceTo(upPosition) !== 0) return;
563
+ const intersects = this.getPointerIntersects(upPosition);
564
+ this.clearSelection();
565
+ if (intersects.length > 0) this.select(intersects[0].object);
566
+ this.viewer.update();
567
+ this.viewer.emitEvent({
568
+ type: "select",
569
+ data: undefined,
570
+ handles: this.viewer.getSelected()
571
+ });
572
+ };
573
+ this.onDoubleClick = event => {
574
+ if (event.button !== 0) return;
575
+ this.viewer.executeCommand("zoomToSelected");
576
+ };
577
+ this.optionsChange = () => {
578
+ const {facesColor: facesColor, facesTransparancy: facesTransparancy} = this.viewer.options;
579
+ this.facesMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
580
+ this.facesMaterial.opacity = (255 - facesTransparancy) / 255;
581
+ this.viewer.update();
582
+ };
583
+ this.viewer = viewer;
584
+ this.raycaster = new Raycaster;
585
+ this.downPosition = new Vector2;
586
+ const {facesColor: facesColor, facesTransparancy: facesTransparancy} = this.viewer.options;
587
+ this.facesMaterial = new MeshBasicMaterial;
588
+ this.facesMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255);
589
+ this.facesMaterial.opacity = (255 - facesTransparancy) / 255;
590
+ this.facesMaterial.transparent = true;
591
+ this.viewer.addEventListener("pointerdown", this.onPointerDown);
592
+ this.viewer.addEventListener("pointerup", this.onPointerUp);
593
+ this.viewer.addEventListener("dblclick", this.onDoubleClick);
594
+ this.viewer.addEventListener("optionschange", this.optionsChange);
595
+ }
596
+ dispose() {
597
+ this.facesMaterial.dispose();
598
+ this.viewer.removeEventListener("pointerdown", this.onPointerDown);
599
+ this.viewer.removeEventListener("pointerup", this.onPointerUp);
600
+ this.viewer.removeEventListener("dblclick", this.onDoubleClick);
601
+ this.viewer.removeEventListener("optionschange", this.optionsChange);
602
+ }
603
+ getMousePosition(event, position) {
604
+ const rect = this.viewer.canvas.getBoundingClientRect();
605
+ position.setX((event.clientX - rect.left) / rect.width);
606
+ position.setY((event.clientY - rect.top) / rect.height);
607
+ return position;
608
+ }
609
+ getPointerIntersects(position) {
610
+ const mouse = new Vector2(position.x * 2 - 1, -(position.y * 2) + 1);
611
+ this.raycaster.setFromCamera(mouse, this.viewer.camera);
612
+ const objects = [];
613
+ this.viewer.scene.traverseVisible((child => objects.push(child)));
614
+ return this.raycaster.intersectObjects(objects, false);
615
+ }
616
+ select(object) {
617
+ if (object.isSelected) return;
618
+ object.isSelected = true;
619
+ object.originalMaterial = object.material;
620
+ object.material = this.facesMaterial;
621
+ this.viewer.selected.push(object);
622
+ }
623
+ clearSelection() {
624
+ this.viewer.selected.forEach((object => {
625
+ object.isSelected = false;
626
+ object.material = object.originalMaterial;
627
+ }));
628
+ this.viewer.selected.length = 0;
629
+ }
630
+ }
631
+
632
+ function hideSelected(viewer) {
633
+ viewer.selected.forEach((object => object.visible = false));
634
+ const selection = new SelectionComponent(viewer);
635
+ selection.clearSelection();
636
+ selection.dispose();
637
+ viewer.update();
638
+ viewer.emit({
639
+ type: "hide"
640
+ });
641
+ viewer.emit({
642
+ type: "select",
643
+ data: undefined,
644
+ handles: []
645
+ });
646
+ }
647
+
648
+ commands("ThreeJS").registerCommand("hideSelected", hideSelected);
649
+
650
+ function isolateSelected(viewer) {
651
+ const selectedSet = new Set(viewer.selected);
652
+ function isolateObject(object, depth) {
653
+ let canBeIsolated = true;
654
+ object.children.forEach((object => {
655
+ if (selectedSet.has(object)) canBeIsolated = false; else isolateObject(object, depth + 1);
656
+ }));
657
+ if (canBeIsolated && depth > 0) object.visible = false;
658
+ return canBeIsolated;
659
+ }
660
+ isolateObject(viewer.scene, 0);
661
+ viewer.update();
662
+ viewer.emit({
663
+ type: "isolate"
664
+ });
665
+ }
666
+
667
+ commands("ThreeJS").registerCommand("isolateSelected", isolateSelected);
668
+
669
+ function regenerateAll(viewer) {
670
+ console.warn("regenerateAll not implemented");
671
+ viewer.emit({
672
+ type: "regenerateall"
673
+ });
674
+ }
675
+
676
+ commands("ThreeJS").registerCommand("regenerateAll", regenerateAll);
677
+
678
+ function resetView(viewer) {
679
+ viewer.executeCommand("setActiveDragger", "");
680
+ viewer.executeCommand("clearSlices");
681
+ viewer.executeCommand("clearOverlay");
682
+ viewer.executeCommand("setMarkupColor");
683
+ viewer.executeCommand("unselect");
684
+ viewer.executeCommand("showAll");
685
+ viewer.executeCommand("explode", 0);
686
+ viewer.executeCommand("zoomToExtents", true);
687
+ viewer.executeCommand("k3DViewSW");
688
+ viewer.emit({
689
+ type: "resetview"
690
+ });
691
+ }
692
+
693
+ commands("ThreeJS").registerCommand("resetView", resetView);
694
+
695
+ function selectModel(viewer, handle) {
696
+ console.warn("selectModel not implemented");
697
+ viewer.emit({
698
+ type: "select",
699
+ data: []
700
+ });
701
+ }
702
+
703
+ commands("ThreeJS").registerCommand("selectModel", selectModel);
704
+
705
+ commands("ThreeJS").registerCommand("setActiveDragger", ((viewer, dragger = "") => {
706
+ viewer.setActiveDragger(dragger);
707
+ }));
708
+
709
+ commands("ThreeJS").registerCommand("setMarkupColor", ((viewer, r = 255, g = 0, b = 0) => {
710
+ console.warn("setMarkupColor not implemented");
711
+ }));
712
+
713
+ function setSelected(viewer, handles = []) {
714
+ const handleSet = new Set(handles);
715
+ const objects = [];
716
+ viewer.scene.traverseVisible((child => {
717
+ var _a;
718
+ if (handleSet.has((_a = child.userData) === null || _a === void 0 ? void 0 : _a.handle)) objects.push(child);
719
+ }));
720
+ const selection = new SelectionComponent(viewer);
721
+ selection.clearSelection();
722
+ objects.forEach((object => selection.select(object)));
723
+ selection.dispose();
724
+ viewer.update();
725
+ viewer.emit({
726
+ type: "select",
727
+ data: undefined,
728
+ handles: handles
729
+ });
730
+ }
731
+
732
+ commands("ThreeJS").registerCommand("setSelected", setSelected);
733
+
734
+ function showAll(viewer) {
735
+ viewer.scene.traverse((object => object.visible = true));
736
+ viewer.update();
737
+ viewer.emit({
738
+ type: "showall"
739
+ });
740
+ }
741
+
742
+ commands("ThreeJS").registerCommand("showAll", showAll);
743
+
744
+ function unselect(viewer) {
745
+ const selection = new SelectionComponent(viewer);
746
+ selection.clearSelection();
747
+ selection.dispose();
748
+ viewer.update();
749
+ viewer.emit({
750
+ type: "select",
751
+ data: undefined,
752
+ handles: []
753
+ });
754
+ }
755
+
756
+ commands("ThreeJS").registerCommand("unselect", unselect);
757
+
758
+ function zoomToExtents(viewer) {
759
+ if (viewer.extents.isEmpty()) return;
760
+ const center = viewer.extents.getCenter(new Vector3);
761
+ const distance = viewer.extents.getBoundingSphere(new Sphere).radius;
762
+ const delta = new Vector3(0, 0, 1);
763
+ delta.applyQuaternion(viewer.camera.quaternion);
764
+ delta.multiplyScalar(distance * 3);
765
+ viewer.camera.position.copy(center).add(delta);
766
+ viewer.target.copy(center);
767
+ viewer.update();
768
+ viewer.emitEvent({
769
+ type: "zoom"
770
+ });
771
+ }
772
+
773
+ commands("ThreeJS").registerCommand("zoomToExtents", zoomToExtents);
774
+
775
+ commands("ThreeJS").registerCommandAlias("zoomToExtents", "zoomExtents");
776
+
777
+ function zoomToObjects(viewer, handles = []) {
778
+ const handleSet = new Set(handles);
779
+ const objects = [];
780
+ viewer.scene.traverseVisible((child => {
781
+ var _a;
782
+ if (handleSet.has((_a = child.userData) === null || _a === void 0 ? void 0 : _a.handle)) objects.push(child);
783
+ }));
784
+ const extents = objects.reduce(((result, object) => {
785
+ const objectExtents = (new Box3).setFromObject(object);
786
+ return result.isEmpty() ? result.copy(objectExtents) : result.union(objectExtents);
787
+ }), new Box3);
788
+ const center = extents.getCenter(new Vector3);
789
+ const distance = extents.getBoundingSphere(new Sphere).radius;
790
+ const delta = new Vector3(0, 0, 1);
791
+ delta.applyQuaternion(viewer.camera.quaternion);
792
+ delta.multiplyScalar(distance * 3);
793
+ viewer.camera.position.copy(center).add(delta);
794
+ viewer.target.copy(center);
795
+ viewer.update();
796
+ viewer.emitEvent({
797
+ type: "zoom"
798
+ });
799
+ }
800
+
801
+ commands("ThreeJS").registerCommand("zoomToObjects", zoomToObjects);
802
+
803
+ function zoomToSelected(viewer) {
804
+ const extents = viewer.selected.reduce(((result, object) => {
805
+ const objectExtents = (new Box3).setFromObject(object);
806
+ return result.isEmpty() ? result.copy(objectExtents) : result.union(objectExtents);
807
+ }), new Box3);
808
+ if (extents.isEmpty()) extents.copy(viewer.extents);
809
+ const center = extents.getCenter(new Vector3);
810
+ const distance = extents.getBoundingSphere(new Sphere).radius;
811
+ const delta = new Vector3(0, 0, 1);
812
+ delta.applyQuaternion(viewer.camera.quaternion);
813
+ delta.multiplyScalar(distance * 3);
814
+ viewer.camera.position.copy(center).add(delta);
815
+ viewer.target.copy(center);
816
+ viewer.update();
817
+ viewer.emitEvent({
818
+ type: "zoom"
819
+ });
820
+ }
821
+
822
+ commands("ThreeJS").registerCommand("zoomToSelected", zoomToSelected);
823
+
824
+ class GLTFLoadingManager extends LoadingManager {
825
+ constructor(file, externalData = new Map, params = {}) {
826
+ super();
827
+ this.path = "";
828
+ this.resourcePath = "";
829
+ this.fileURL = "";
830
+ this.dataURLs = new Map;
831
+ this.path = params.path || "";
832
+ if (typeof file === "string") {
833
+ this.fileURL = file;
834
+ this.resourcePath = LoaderUtils.extractUrlBase(file);
835
+ } else {
836
+ externalData.forEach(((value, key) => this.fileURL = value === file ? key : this.fileURL));
837
+ externalData.set(this.fileURL, file);
838
+ }
839
+ externalData.forEach(((value, key) => {
840
+ let dataURL;
841
+ if (typeof value === "string") dataURL = value; else dataURL = URL.createObjectURL(new Blob([ value ]));
842
+ this.dataURLs.set(key, dataURL);
843
+ }));
844
+ this.setURLModifier((url => {
845
+ const key = decodeURI(url).replace(this.path, "").replace(this.resourcePath, "").replace(/^(\.?\/)/, "");
846
+ const dataURL = this.dataURLs.get(key);
847
+ return dataURL !== null && dataURL !== void 0 ? dataURL : url;
848
+ }));
849
+ }
850
+ dispose() {
851
+ this.dataURLs.forEach(URL.revokeObjectURL);
852
+ }
853
+ }
854
+
383
855
  class EventEmitter2 {
384
856
  constructor() {
385
857
  this._listeners = {};
@@ -419,83 +891,67 @@ class EventEmitter2 {
419
891
  }
420
892
  }
421
893
 
422
- class GLTFLoadingManager extends THREE.LoadingManager {
423
- constructor(file, externalData = new Map, params = {}) {
424
- super();
425
- this.path = "";
426
- this.resourcePath = "";
427
- this.fileURL = "";
428
- this.dataURLs = new Map;
429
- this.path = params.path || "";
430
- if (typeof file === "string") {
431
- this.fileURL = file;
432
- this.resourcePath = THREE.LoaderUtils.extractUrlBase(file);
433
- } else {
434
- externalData.forEach(((value, key) => this.fileURL = value === file ? key : this.fileURL));
435
- externalData.set(this.fileURL, file);
436
- }
437
- externalData.forEach(((value, key) => {
438
- let dataURL;
439
- if (typeof value === "string") dataURL = value; else dataURL = URL.createObjectURL(new Blob([ value ]));
440
- this.dataURLs.set(key, dataURL);
441
- }));
442
- this.setURLModifier((url => {
443
- const key = decodeURI(url).replace(this.path, "").replace(this.resourcePath, "").replace(/^(\.?\/)/, "");
444
- const dataURL = this.dataURLs.get(key);
445
- return dataURL !== null && dataURL !== void 0 ? dataURL : url;
446
- }));
447
- }
448
- dispose() {
449
- this.dataURLs.forEach(URL.revokeObjectURL);
450
- }
451
- }
452
-
453
- class OrbitDragger extends OrbitControls {
894
+ class OrbitDragger {
454
895
  constructor(viewer) {
455
- super(viewer.camera, viewer.canvas);
456
- this.geometryEnd = event => {
457
- const {data: scene} = event;
458
- const box = (new THREE.Box3).setFromObject(scene);
459
- const size = box.getSize(new THREE.Vector3).length();
460
- this.maxDistance = size * 10;
461
- this.update();
896
+ this.updateControls = () => {
897
+ this.orbit.maxDistance = this.viewer.camera.far;
898
+ this.orbit.minDistance = this.viewer.camera.near;
899
+ this.orbit.target.copy(this.viewer.target);
900
+ this.orbit.update();
462
901
  };
463
- this.updateViewer = () => {
902
+ this.controlsStart = () => {
903
+ this.changed = false;
904
+ };
905
+ this.controlsChange = () => {
906
+ this.viewer.target.copy(this.orbit.target);
464
907
  this.viewer.update();
908
+ this.changed = true;
909
+ };
910
+ this.stopContextMenu = event => {
911
+ if (this.changed) {
912
+ event.preventDefault();
913
+ event.stopPropagation();
914
+ }
465
915
  };
466
- this.mouseButtons = {
467
- LEFT: THREE.MOUSE.ROTATE,
468
- MIDDLE: THREE.MOUSE.PAN,
469
- RIGHT: THREE.MOUSE.PAN
916
+ this.orbit = new OrbitControls(viewer.camera, viewer.canvas);
917
+ this.orbit.mouseButtons = {
918
+ LEFT: MOUSE.ROTATE,
919
+ MIDDLE: MOUSE.PAN,
920
+ RIGHT: MOUSE.PAN
470
921
  };
471
- this.touches = {
472
- ONE: THREE.TOUCH.ROTATE,
473
- TWO: THREE.TOUCH.DOLLY_PAN
922
+ this.orbit.touches = {
923
+ ONE: TOUCH.ROTATE,
924
+ TWO: TOUCH.DOLLY_PAN
474
925
  };
475
- this.screenSpacePanning = true;
476
- this.rotateSpeed = .33;
926
+ this.orbit.screenSpacePanning = true;
927
+ this.orbit.rotateSpeed = .33;
928
+ this.orbit.addEventListener("start", this.controlsStart);
929
+ this.orbit.addEventListener("change", this.controlsChange);
930
+ this.changed = false;
477
931
  this.viewer = viewer;
478
- this.viewer.addEventListener("geometryend", this.geometryEnd);
479
- this.addEventListener("change", this.updateViewer);
932
+ this.viewer.on("geometryend", this.updateControls);
933
+ this.viewer.on("viewposition", this.updateControls);
934
+ this.viewer.on("zoom", this.updateControls);
935
+ this.viewer.on("contextmenu", this.stopContextMenu);
936
+ this.updateControls();
480
937
  }
481
938
  dispose() {
482
- this.removeEventListener("change", this.updateViewer);
483
- this.viewer.removeEventListener("geometryend", this.geometryEnd);
484
- super.dispose();
939
+ this.viewer.off("contextmenu", this.stopContextMenu);
940
+ this.viewer.off("zoom", this.updateControls);
941
+ this.viewer.off("viewposition", this.updateControls);
942
+ this.viewer.off("geometryend", this.updateControls);
943
+ this.orbit.removeEventListener("change", this.controlsChange);
944
+ this.orbit.dispose();
485
945
  }
486
946
  }
487
947
 
488
948
  class PanDragger extends OrbitDragger {
489
949
  constructor(viewer) {
490
950
  super(viewer);
491
- this.mouseButtons = {
492
- LEFT: THREE.MOUSE.PAN,
493
- MIDDLE: THREE.MOUSE.PAN,
494
- RIGHT: THREE.MOUSE.PAN
495
- };
496
- this.touches = {
497
- ONE: THREE.TOUCH.PAN,
498
- TWO: THREE.TOUCH.DOLLY_ROTATE
951
+ this.orbit.mouseButtons = {
952
+ LEFT: MOUSE.PAN,
953
+ MIDDLE: MOUSE.PAN,
954
+ RIGHT: MOUSE.PAN
499
955
  };
500
956
  }
501
957
  }
@@ -503,283 +959,1253 @@ class PanDragger extends OrbitDragger {
503
959
  class ZoomDragger extends OrbitDragger {
504
960
  constructor(viewer) {
505
961
  super(viewer);
506
- this.mouseButtons = {
507
- LEFT: THREE.MOUSE.DOLLY,
508
- MIDDLE: THREE.MOUSE.PAN,
509
- RIGHT: THREE.MOUSE.PAN
510
- };
511
- this.touches = {
512
- ONE: THREE.TOUCH.DOLLY_PAN,
513
- TWO: THREE.TOUCH.DOLLY_PAN
962
+ this.orbit.mouseButtons = {
963
+ LEFT: MOUSE.DOLLY,
964
+ MIDDLE: MOUSE.PAN,
965
+ RIGHT: MOUSE.PAN
514
966
  };
515
967
  }
516
968
  }
517
969
 
518
- class WalkDragger {
519
- constructor(viewer) {
970
+ const _changeEvent$1 = {
971
+ type: "change"
972
+ };
973
+
974
+ class WalkControls extends EventDispatcher {
975
+ constructor(camera, canvas) {
976
+ super();
977
+ this.movementSpeed = .2;
978
+ this.lookSpeed = 5;
979
+ this.multiplier = 5;
980
+ this.moveWheel = 0;
981
+ this.mouseDragOn = false;
982
+ this.onPointerDown = event => {
983
+ if (!event.isPrimary || event.button !== 0) return;
984
+ this.canvas.setPointerCapture(event.pointerId);
985
+ this.downPosition.set(event.clientX, event.clientY);
986
+ this.quaternion.copy(this.camera.quaternion);
987
+ this.mouseDragOn = true;
988
+ };
989
+ this.onPointerMove = event => {
990
+ if (!event.isPrimary || !this.mouseDragOn) return;
991
+ const movePosition = new Vector2(event.clientX, event.clientY);
992
+ if (this.downPosition.distanceTo(movePosition) === 0) return;
993
+ this.rotateDelta.copy(this.downPosition).sub(movePosition);
994
+ this.rotateCamera(this.rotateDelta);
995
+ this.dispatchEvent(_changeEvent$1);
996
+ };
997
+ this.onPointerUp = event => {
998
+ if (!event.isPrimary || event.button !== 0) return;
999
+ this.canvas.releasePointerCapture(event.pointerId);
1000
+ this.mouseDragOn = false;
1001
+ };
1002
+ this.onPointerCancel = event => {
1003
+ this.canvas.dispatchEvent(new PointerEvent("pointerup", event));
1004
+ };
1005
+ this.onWheel = event => {
1006
+ this.moveWheel = event.deltaY;
1007
+ this.update();
1008
+ };
520
1009
  this.onKeyDown = event => {
521
1010
  switch (event.code) {
522
- case "KeyW":
523
- if (event.shiftKey) {
524
- this.speed.z = this.walkSpeed * this.boostSpeed;
525
- } else {
526
- this.speed.z = this.walkSpeed;
1011
+ case "NumpadSubtract":
1012
+ case "Minus":
1013
+ if (this.multiplier > 1) {
1014
+ this.multiplier = this.multiplier - 1;
1015
+ this.dispatchEvent({
1016
+ type: "walkspeedchange",
1017
+ data: this.multiplier
1018
+ });
527
1019
  }
528
1020
  break;
529
1021
 
530
- case "KeyS":
531
- if (event.shiftKey) {
532
- this.speed.z = -this.walkSpeed * this.boostSpeed;
533
- } else {
534
- this.speed.z = -this.walkSpeed;
1022
+ case "NumpadAdd":
1023
+ case "Equal":
1024
+ if (this.multiplier < 10) {
1025
+ this.multiplier = this.multiplier + 1;
1026
+ this.dispatchEvent({
1027
+ type: "walkspeedchange",
1028
+ data: this.multiplier
1029
+ });
535
1030
  }
536
1031
  break;
537
1032
 
1033
+ case "ArrowLeft":
1034
+ case "ArrowRight":
1035
+ case "ArrowUp":
1036
+ case "ArrowDown":
1037
+ case "KeyW":
1038
+ case "KeyS":
538
1039
  case "KeyA":
539
- if (event.shiftKey) {
540
- this.speed.x = this.walkSpeed * this.boostSpeed;
541
- } else {
542
- this.speed.x = this.walkSpeed;
543
- }
544
- break;
545
-
546
1040
  case "KeyD":
547
- if (event.shiftKey) {
548
- this.speed.x = -this.walkSpeed * this.boostSpeed;
549
- } else {
550
- this.speed.x = -this.walkSpeed;
551
- }
1041
+ case "KeyQ":
1042
+ case "KeyE":
1043
+ this.moveKeys.add(event.code);
1044
+ this.update();
552
1045
  break;
553
1046
  }
554
1047
  };
555
1048
  this.onKeyUp = event => {
556
1049
  switch (event.code) {
1050
+ case "ArrowLeft":
1051
+ case "ArrowRight":
1052
+ case "ArrowUp":
1053
+ case "ArrowDown":
557
1054
  case "KeyW":
558
- this.speed.z = 0;
559
- break;
560
-
561
1055
  case "KeyS":
562
- this.speed.z = 0;
563
- break;
564
-
565
1056
  case "KeyA":
566
- this.speed.x = 0;
567
- break;
568
-
569
1057
  case "KeyD":
570
- this.speed.x = 0;
1058
+ case "KeyQ":
1059
+ case "KeyE":
1060
+ this.moveKeys.delete(event.code);
1061
+ this.update();
571
1062
  break;
572
1063
  }
573
1064
  };
574
- this.onMouseDown = event => {
575
- const {clientX: clientX, clientY: clientY} = event;
576
- this.mouseStart.set(clientX, clientY);
577
- this.mouseDelta.set(0, 0);
578
- this.quaternion.copy(this.viewer.camera.quaternion);
579
- this.viewer.addEventListener("mousemove", this.onMouseMove);
1065
+ this.camera = camera;
1066
+ this.canvas = canvas;
1067
+ this.moveKeys = new Set;
1068
+ this.moveClock = new Clock;
1069
+ this.quaternion = camera.quaternion.clone();
1070
+ this.downPosition = new Vector2(0, 0);
1071
+ this.rotateDelta = new Vector2(0, 0);
1072
+ this.canvas.addEventListener("pointerdown", this.onPointerDown);
1073
+ this.canvas.addEventListener("pointermove", this.onPointerMove);
1074
+ this.canvas.addEventListener("pointerup", this.onPointerUp);
1075
+ this.canvas.addEventListener("pointercancel", this.onPointerCancel);
1076
+ this.canvas.addEventListener("wheel", this.onWheel);
1077
+ window.addEventListener("keydown", this.onKeyDown);
1078
+ window.addEventListener("keyup", this.onKeyUp);
1079
+ }
1080
+ dispose() {
1081
+ this.canvas.removeEventListener("pointerdown", this.onPointerDown);
1082
+ this.canvas.removeEventListener("pointermove", this.onPointerMove);
1083
+ this.canvas.removeEventListener("pointerup", this.onPointerUp);
1084
+ this.canvas.removeEventListener("pointercancel", this.onPointerCancel);
1085
+ this.canvas.removeEventListener("wheel", this.onWheel);
1086
+ window.removeEventListener("keydown", this.onKeyDown);
1087
+ window.removeEventListener("keyup", this.onKeyUp);
1088
+ }
1089
+ update() {
1090
+ if (this.moveKeys.size > 0) {
1091
+ const timeDelta = this.moveClock.getDelta();
1092
+ const moveDelta = timeDelta * this.movementSpeed * this.multiplier;
1093
+ if (this.moveKeys.has("KeyW")) this.camera.translateZ(-moveDelta);
1094
+ if (this.moveKeys.has("KeyS")) this.camera.translateZ(moveDelta);
1095
+ if (this.moveKeys.has("KeyA")) this.camera.translateX(-moveDelta);
1096
+ if (this.moveKeys.has("KeyD")) this.camera.translateX(moveDelta);
1097
+ if (this.moveKeys.has("KeyQ")) this.camera.translateY(moveDelta);
1098
+ if (this.moveKeys.has("KeyE")) this.camera.translateY(-moveDelta);
1099
+ const lookDelta = this.lookSpeed + (this.multiplier - 1);
1100
+ if (this.moveKeys.has("ArrowUp")) this.rotateCamera(this.rotateDelta.add(new Vector2(0, -lookDelta / 2)));
1101
+ if (this.moveKeys.has("ArrowDown")) this.rotateCamera(this.rotateDelta.add(new Vector2(0, lookDelta / 2)));
1102
+ if (this.moveKeys.has("ArrowLeft")) this.rotateCamera(this.rotateDelta.add(new Vector2(lookDelta, 0)));
1103
+ if (this.moveKeys.has("ArrowRight")) this.rotateCamera(this.rotateDelta.add(new Vector2(-lookDelta, 0)));
1104
+ this.moveWheel = 0;
1105
+ this.dispatchEvent(_changeEvent$1);
1106
+ }
1107
+ if (this.moveWheel !== 0) {
1108
+ const moveDelta = this.moveWheel * 1e-4 * this.movementSpeed * this.multiplier;
1109
+ this.camera.translateZ(-moveDelta);
1110
+ this.moveWheel += -1 * Math.sign(this.moveWheel);
1111
+ this.dispatchEvent(_changeEvent$1);
1112
+ }
1113
+ if (this.moveKeys.size === 0 && this.moveWheel === 0) {
1114
+ this.moveClock.stop();
1115
+ this.moveClock.autoStart = true;
1116
+ }
1117
+ }
1118
+ rotateCamera(delta) {
1119
+ const rotateX = Math.PI * delta.x / this.canvas.clientWidth;
1120
+ const rotateY = Math.PI * delta.y / this.canvas.clientHeight;
1121
+ const xRotation = new Quaternion;
1122
+ xRotation.setFromAxisAngle(this.camera.up, rotateX);
1123
+ const yRotation = new Quaternion;
1124
+ yRotation.setFromAxisAngle(new Vector3(1, 0, 0), rotateY);
1125
+ const quaternion = this.quaternion.clone();
1126
+ quaternion.premultiply(xRotation).multiply(yRotation).normalize();
1127
+ this.camera.setRotationFromQuaternion(quaternion);
1128
+ }
1129
+ }
1130
+
1131
+ class WalkDragger {
1132
+ constructor(viewer) {
1133
+ this.updateControls = () => {
1134
+ const size = this.viewer.extents.getSize(new Vector3);
1135
+ this.controls.movementSpeed = Math.min(size.x, size.y, size.z) / 2;
580
1136
  };
581
- this.onMouseMove = event => {
582
- const {clientX: clientX, clientY: clientY} = event;
583
- this.mouseDelta.set(this.mouseStart.x - clientX, this.mouseStart.y - clientY);
584
- this.rotateCamera(this.mouseDelta);
1137
+ this.controlsChange = () => {
1138
+ this.viewer.update();
585
1139
  };
586
- this.onMouseUp = event => {
587
- this.speed.set(0, 0, 0);
588
- this.mouseStart.set(0, 0);
589
- this.mouseDelta.set(0, 0);
590
- this.quaternion.copy(this.viewer.camera.quaternion);
591
- this.viewer.removeEventListener("mousemove", this.onMouseMove);
1140
+ this.walkspeedChange = event => {
1141
+ this.viewer.emitEvent(event);
592
1142
  };
593
- this.onMouseWheel = event => {
594
- event.preventDefault();
595
- if (-event.deltaY < 0) {
596
- this.walkSpeed = Math.max(.001, this.walkSpeed - 1);
597
- } else if (-event.deltaY > 0) {
598
- this.walkSpeed++;
599
- }
1143
+ this.viewerRender = () => {
1144
+ this.controls.update();
600
1145
  };
601
- this.onContextMenu = event => {
602
- console.log(event);
603
- event.preventDefault();
604
- event.stopImmediatePropagation();
1146
+ this.viewerZoom = () => {
1147
+ this.controls.rotateDelta.set(0, 0);
605
1148
  };
606
- this.onTouchStart = event => {
607
- if (event.touches.length > 1) {
608
- this.touchStartDistance = this.getTouchsDistance(event.touches);
1149
+ this.controls = new WalkControls(viewer.camera, viewer.canvas);
1150
+ this.controls.addEventListener("change", this.controlsChange);
1151
+ this.controls.addEventListener("walkspeedchange", this.walkspeedChange);
1152
+ this.viewer = viewer;
1153
+ this.viewer.on("render", this.viewerRender);
1154
+ this.viewer.on("zoom", this.viewerZoom);
1155
+ this.updateControls();
1156
+ }
1157
+ dispose() {
1158
+ this.viewer.off("render", this.viewerRender);
1159
+ this.viewer.off("zoom", this.viewerZoom);
1160
+ this.controls.removeEventListener("walkspeedchange", this.walkspeedChange);
1161
+ this.controls.removeEventListener("change", this.controlsChange);
1162
+ this.controls.dispose();
1163
+ }
1164
+ }
1165
+
1166
+ const _raycaster = new Raycaster;
1167
+
1168
+ const _tempVector = new Vector3;
1169
+
1170
+ const _tempVector2 = new Vector3;
1171
+
1172
+ const _tempQuaternion = new Quaternion;
1173
+
1174
+ const _unit = {
1175
+ X: new Vector3(1, 0, 0),
1176
+ Y: new Vector3(0, 1, 0),
1177
+ Z: new Vector3(0, 0, 1)
1178
+ };
1179
+
1180
+ const _changeEvent = {
1181
+ type: "change"
1182
+ };
1183
+
1184
+ const _mouseDownEvent = {
1185
+ type: "mouseDown"
1186
+ };
1187
+
1188
+ const _mouseUpEvent = {
1189
+ type: "mouseUp",
1190
+ mode: null
1191
+ };
1192
+
1193
+ const _objectChangeEvent = {
1194
+ type: "objectChange"
1195
+ };
1196
+
1197
+ class TransformControls extends Object3D {
1198
+ constructor(camera, domElement) {
1199
+ super();
1200
+ if (domElement === undefined) {
1201
+ console.warn('THREE.TransformControls: The second parameter "domElement" is now mandatory.');
1202
+ domElement = document;
1203
+ }
1204
+ this.isTransformControls = true;
1205
+ this.visible = false;
1206
+ this.domElement = domElement;
1207
+ this.domElement.style.touchAction = "none";
1208
+ const _gizmo = new TransformControlsGizmo;
1209
+ this._gizmo = _gizmo;
1210
+ this.add(_gizmo);
1211
+ const _plane = new TransformControlsPlane;
1212
+ this._plane = _plane;
1213
+ this.add(_plane);
1214
+ const scope = this;
1215
+ function defineProperty(propName, defaultValue) {
1216
+ let propValue = defaultValue;
1217
+ Object.defineProperty(scope, propName, {
1218
+ get: function() {
1219
+ return propValue !== undefined ? propValue : defaultValue;
1220
+ },
1221
+ set: function(value) {
1222
+ if (propValue !== value) {
1223
+ propValue = value;
1224
+ _plane[propName] = value;
1225
+ _gizmo[propName] = value;
1226
+ scope.dispatchEvent({
1227
+ type: propName + "-changed",
1228
+ value: value
1229
+ });
1230
+ scope.dispatchEvent(_changeEvent);
1231
+ }
1232
+ }
1233
+ });
1234
+ scope[propName] = defaultValue;
1235
+ _plane[propName] = defaultValue;
1236
+ _gizmo[propName] = defaultValue;
1237
+ }
1238
+ defineProperty("camera", camera);
1239
+ defineProperty("object", undefined);
1240
+ defineProperty("enabled", true);
1241
+ defineProperty("axis", null);
1242
+ defineProperty("mode", "translate");
1243
+ defineProperty("translationSnap", null);
1244
+ defineProperty("rotationSnap", null);
1245
+ defineProperty("scaleSnap", null);
1246
+ defineProperty("space", "world");
1247
+ defineProperty("size", 1);
1248
+ defineProperty("dragging", false);
1249
+ defineProperty("showX", true);
1250
+ defineProperty("showY", true);
1251
+ defineProperty("showZ", true);
1252
+ const worldPosition = new Vector3;
1253
+ const worldPositionStart = new Vector3;
1254
+ const worldQuaternion = new Quaternion;
1255
+ const worldQuaternionStart = new Quaternion;
1256
+ const cameraPosition = new Vector3;
1257
+ const cameraQuaternion = new Quaternion;
1258
+ const pointStart = new Vector3;
1259
+ const pointEnd = new Vector3;
1260
+ const rotationAxis = new Vector3;
1261
+ const rotationAngle = 0;
1262
+ const eye = new Vector3;
1263
+ defineProperty("worldPosition", worldPosition);
1264
+ defineProperty("worldPositionStart", worldPositionStart);
1265
+ defineProperty("worldQuaternion", worldQuaternion);
1266
+ defineProperty("worldQuaternionStart", worldQuaternionStart);
1267
+ defineProperty("cameraPosition", cameraPosition);
1268
+ defineProperty("cameraQuaternion", cameraQuaternion);
1269
+ defineProperty("pointStart", pointStart);
1270
+ defineProperty("pointEnd", pointEnd);
1271
+ defineProperty("rotationAxis", rotationAxis);
1272
+ defineProperty("rotationAngle", rotationAngle);
1273
+ defineProperty("eye", eye);
1274
+ this._offset = new Vector3;
1275
+ this._startNorm = new Vector3;
1276
+ this._endNorm = new Vector3;
1277
+ this._cameraScale = new Vector3;
1278
+ this._parentPosition = new Vector3;
1279
+ this._parentQuaternion = new Quaternion;
1280
+ this._parentQuaternionInv = new Quaternion;
1281
+ this._parentScale = new Vector3;
1282
+ this._worldScaleStart = new Vector3;
1283
+ this._worldQuaternionInv = new Quaternion;
1284
+ this._worldScale = new Vector3;
1285
+ this._positionStart = new Vector3;
1286
+ this._quaternionStart = new Quaternion;
1287
+ this._scaleStart = new Vector3;
1288
+ this._getPointer = getPointer.bind(this);
1289
+ this._onPointerDown = onPointerDown.bind(this);
1290
+ this._onPointerHover = onPointerHover.bind(this);
1291
+ this._onPointerMove = onPointerMove.bind(this);
1292
+ this._onPointerUp = onPointerUp.bind(this);
1293
+ this.domElement.addEventListener("pointerdown", this._onPointerDown);
1294
+ this.domElement.addEventListener("pointermove", this._onPointerHover);
1295
+ this.domElement.addEventListener("pointerup", this._onPointerUp);
1296
+ }
1297
+ updateMatrixWorld() {
1298
+ if (this.object !== undefined) {
1299
+ this.object.updateMatrixWorld();
1300
+ if (this.object.parent === null) {
1301
+ console.error("TransformControls: The attached 3D object must be a part of the scene graph.");
609
1302
  } else {
610
- const {clientX: clientX, clientY: clientY} = event.touches[0];
611
- this.mouseStart.set(clientX, clientY);
612
- this.mouseDelta.set(0, 0);
613
- this.quaternion.copy(this.viewer.camera.quaternion);
1303
+ this.object.parent.matrixWorld.decompose(this._parentPosition, this._parentQuaternion, this._parentScale);
614
1304
  }
615
- };
616
- this.onTouchEnd = event => {
617
- if (event.touches.length === 0) {
618
- this.touchStartDistance = 0;
1305
+ this.object.matrixWorld.decompose(this.worldPosition, this.worldQuaternion, this._worldScale);
1306
+ this._parentQuaternionInv.copy(this._parentQuaternion).invert();
1307
+ this._worldQuaternionInv.copy(this.worldQuaternion).invert();
1308
+ }
1309
+ this.camera.updateMatrixWorld();
1310
+ this.camera.matrixWorld.decompose(this.cameraPosition, this.cameraQuaternion, this._cameraScale);
1311
+ if (this.camera.isOrthographicCamera) {
1312
+ this.camera.getWorldDirection(this.eye).negate();
1313
+ } else {
1314
+ this.eye.copy(this.cameraPosition).sub(this.worldPosition).normalize();
1315
+ }
1316
+ super.updateMatrixWorld(this);
1317
+ }
1318
+ pointerHover(pointer) {
1319
+ if (this.object === undefined || this.dragging === true) return;
1320
+ _raycaster.setFromCamera(pointer, this.camera);
1321
+ const intersect = intersectObjectWithRay(this._gizmo.picker[this.mode], _raycaster);
1322
+ if (intersect) {
1323
+ this.axis = intersect.object.name;
1324
+ } else {
1325
+ this.axis = null;
1326
+ }
1327
+ }
1328
+ pointerDown(pointer) {
1329
+ if (this.object === undefined || this.dragging === true || pointer.button !== 0) return;
1330
+ if (this.axis !== null) {
1331
+ _raycaster.setFromCamera(pointer, this.camera);
1332
+ const planeIntersect = intersectObjectWithRay(this._plane, _raycaster, true);
1333
+ if (planeIntersect) {
1334
+ this.object.updateMatrixWorld();
1335
+ this.object.parent.updateMatrixWorld();
1336
+ this._positionStart.copy(this.object.position);
1337
+ this._quaternionStart.copy(this.object.quaternion);
1338
+ this._scaleStart.copy(this.object.scale);
1339
+ this.object.matrixWorld.decompose(this.worldPositionStart, this.worldQuaternionStart, this._worldScaleStart);
1340
+ this.pointStart.copy(planeIntersect.point).sub(this.worldPositionStart);
619
1341
  }
620
- this.speed.set(0, 0, 0);
621
- this.mouseStart.set(0, 0);
622
- this.mouseDelta.set(0, 0);
623
- this.quaternion.copy(this.viewer.camera.quaternion);
624
- };
625
- this.onTouchMove = event => {
626
- if (event.touches.length === 1 && this.touchStartDistance === 0) {
627
- const {clientX: clientX, clientY: clientY} = event.touches[0];
628
- this.mouseDelta.set(this.mouseStart.x - clientX, this.mouseStart.y - clientY);
629
- this.rotateCamera(this.mouseDelta);
1342
+ this.dragging = true;
1343
+ _mouseDownEvent.mode = this.mode;
1344
+ this.dispatchEvent(_mouseDownEvent);
1345
+ }
1346
+ }
1347
+ pointerMove(pointer) {
1348
+ const axis = this.axis;
1349
+ const mode = this.mode;
1350
+ const object = this.object;
1351
+ let space = this.space;
1352
+ if (mode === "scale") {
1353
+ space = "local";
1354
+ } else if (axis === "E" || axis === "XYZE" || axis === "XYZ") {
1355
+ space = "world";
1356
+ }
1357
+ if (object === undefined || axis === null || this.dragging === false || pointer.button !== -1) return;
1358
+ _raycaster.setFromCamera(pointer, this.camera);
1359
+ const planeIntersect = intersectObjectWithRay(this._plane, _raycaster, true);
1360
+ if (!planeIntersect) return;
1361
+ this.pointEnd.copy(planeIntersect.point).sub(this.worldPositionStart);
1362
+ if (mode === "translate") {
1363
+ this._offset.copy(this.pointEnd).sub(this.pointStart);
1364
+ if (space === "local" && axis !== "XYZ") {
1365
+ this._offset.applyQuaternion(this._worldQuaternionInv);
630
1366
  }
631
- if (event.touches.length === 2) {
632
- const distance = this.getTouchsDistance(event.touches);
633
- this.speed.z = (distance - this.touchStartDistance) / 2;
1367
+ if (axis.indexOf("X") === -1) this._offset.x = 0;
1368
+ if (axis.indexOf("Y") === -1) this._offset.y = 0;
1369
+ if (axis.indexOf("Z") === -1) this._offset.z = 0;
1370
+ if (space === "local" && axis !== "XYZ") {
1371
+ this._offset.applyQuaternion(this._quaternionStart).divide(this._parentScale);
1372
+ } else {
1373
+ this._offset.applyQuaternion(this._parentQuaternionInv).divide(this._parentScale);
634
1374
  }
1375
+ object.position.copy(this._offset).add(this._positionStart);
1376
+ if (this.translationSnap) {
1377
+ if (space === "local") {
1378
+ object.position.applyQuaternion(_tempQuaternion.copy(this._quaternionStart).invert());
1379
+ if (axis.search("X") !== -1) {
1380
+ object.position.x = Math.round(object.position.x / this.translationSnap) * this.translationSnap;
1381
+ }
1382
+ if (axis.search("Y") !== -1) {
1383
+ object.position.y = Math.round(object.position.y / this.translationSnap) * this.translationSnap;
1384
+ }
1385
+ if (axis.search("Z") !== -1) {
1386
+ object.position.z = Math.round(object.position.z / this.translationSnap) * this.translationSnap;
1387
+ }
1388
+ object.position.applyQuaternion(this._quaternionStart);
1389
+ }
1390
+ if (space === "world") {
1391
+ if (object.parent) {
1392
+ object.position.add(_tempVector.setFromMatrixPosition(object.parent.matrixWorld));
1393
+ }
1394
+ if (axis.search("X") !== -1) {
1395
+ object.position.x = Math.round(object.position.x / this.translationSnap) * this.translationSnap;
1396
+ }
1397
+ if (axis.search("Y") !== -1) {
1398
+ object.position.y = Math.round(object.position.y / this.translationSnap) * this.translationSnap;
1399
+ }
1400
+ if (axis.search("Z") !== -1) {
1401
+ object.position.z = Math.round(object.position.z / this.translationSnap) * this.translationSnap;
1402
+ }
1403
+ if (object.parent) {
1404
+ object.position.sub(_tempVector.setFromMatrixPosition(object.parent.matrixWorld));
1405
+ }
1406
+ }
1407
+ }
1408
+ } else if (mode === "scale") {
1409
+ if (axis.search("XYZ") !== -1) {
1410
+ let d = this.pointEnd.length() / this.pointStart.length();
1411
+ if (this.pointEnd.dot(this.pointStart) < 0) d *= -1;
1412
+ _tempVector2.set(d, d, d);
1413
+ } else {
1414
+ _tempVector.copy(this.pointStart);
1415
+ _tempVector2.copy(this.pointEnd);
1416
+ _tempVector.applyQuaternion(this._worldQuaternionInv);
1417
+ _tempVector2.applyQuaternion(this._worldQuaternionInv);
1418
+ _tempVector2.divide(_tempVector);
1419
+ if (axis.search("X") === -1) {
1420
+ _tempVector2.x = 1;
1421
+ }
1422
+ if (axis.search("Y") === -1) {
1423
+ _tempVector2.y = 1;
1424
+ }
1425
+ if (axis.search("Z") === -1) {
1426
+ _tempVector2.z = 1;
1427
+ }
1428
+ }
1429
+ object.scale.copy(this._scaleStart).multiply(_tempVector2);
1430
+ if (this.scaleSnap) {
1431
+ if (axis.search("X") !== -1) {
1432
+ object.scale.x = Math.round(object.scale.x / this.scaleSnap) * this.scaleSnap || this.scaleSnap;
1433
+ }
1434
+ if (axis.search("Y") !== -1) {
1435
+ object.scale.y = Math.round(object.scale.y / this.scaleSnap) * this.scaleSnap || this.scaleSnap;
1436
+ }
1437
+ if (axis.search("Z") !== -1) {
1438
+ object.scale.z = Math.round(object.scale.z / this.scaleSnap) * this.scaleSnap || this.scaleSnap;
1439
+ }
1440
+ }
1441
+ } else if (mode === "rotate") {
1442
+ this._offset.copy(this.pointEnd).sub(this.pointStart);
1443
+ const ROTATION_SPEED = 20 / this.worldPosition.distanceTo(_tempVector.setFromMatrixPosition(this.camera.matrixWorld));
1444
+ if (axis === "E") {
1445
+ this.rotationAxis.copy(this.eye);
1446
+ this.rotationAngle = this.pointEnd.angleTo(this.pointStart);
1447
+ this._startNorm.copy(this.pointStart).normalize();
1448
+ this._endNorm.copy(this.pointEnd).normalize();
1449
+ this.rotationAngle *= this._endNorm.cross(this._startNorm).dot(this.eye) < 0 ? 1 : -1;
1450
+ } else if (axis === "XYZE") {
1451
+ this.rotationAxis.copy(this._offset).cross(this.eye).normalize();
1452
+ this.rotationAngle = this._offset.dot(_tempVector.copy(this.rotationAxis).cross(this.eye)) * ROTATION_SPEED;
1453
+ } else if (axis === "X" || axis === "Y" || axis === "Z") {
1454
+ this.rotationAxis.copy(_unit[axis]);
1455
+ _tempVector.copy(_unit[axis]);
1456
+ if (space === "local") {
1457
+ _tempVector.applyQuaternion(this.worldQuaternion);
1458
+ }
1459
+ this.rotationAngle = this._offset.dot(_tempVector.cross(this.eye).normalize()) * ROTATION_SPEED;
1460
+ }
1461
+ if (this.rotationSnap) this.rotationAngle = Math.round(this.rotationAngle / this.rotationSnap) * this.rotationSnap;
1462
+ if (space === "local" && axis !== "E" && axis !== "XYZE") {
1463
+ object.quaternion.copy(this._quaternionStart);
1464
+ object.quaternion.multiply(_tempQuaternion.setFromAxisAngle(this.rotationAxis, this.rotationAngle)).normalize();
1465
+ } else {
1466
+ this.rotationAxis.applyQuaternion(this._parentQuaternionInv);
1467
+ object.quaternion.copy(_tempQuaternion.setFromAxisAngle(this.rotationAxis, this.rotationAngle));
1468
+ object.quaternion.multiply(this._quaternionStart).normalize();
1469
+ }
1470
+ }
1471
+ this.dispatchEvent(_changeEvent);
1472
+ this.dispatchEvent(_objectChangeEvent);
1473
+ }
1474
+ pointerUp(pointer) {
1475
+ if (pointer.button !== 0) return;
1476
+ if (this.dragging && this.axis !== null) {
1477
+ _mouseUpEvent.mode = this.mode;
1478
+ this.dispatchEvent(_mouseUpEvent);
1479
+ }
1480
+ this.dragging = false;
1481
+ this.axis = null;
1482
+ }
1483
+ dispose() {
1484
+ this.domElement.removeEventListener("pointerdown", this._onPointerDown);
1485
+ this.domElement.removeEventListener("pointermove", this._onPointerHover);
1486
+ this.domElement.removeEventListener("pointermove", this._onPointerMove);
1487
+ this.domElement.removeEventListener("pointerup", this._onPointerUp);
1488
+ this.traverse((function(child) {
1489
+ if (child.geometry) child.geometry.dispose();
1490
+ if (child.material) child.material.dispose();
1491
+ }));
1492
+ }
1493
+ attach(object) {
1494
+ this.object = object;
1495
+ this.visible = true;
1496
+ return this;
1497
+ }
1498
+ detach() {
1499
+ this.object = undefined;
1500
+ this.visible = false;
1501
+ this.axis = null;
1502
+ return this;
1503
+ }
1504
+ reset() {
1505
+ if (!this.enabled) return;
1506
+ if (this.dragging) {
1507
+ this.object.position.copy(this._positionStart);
1508
+ this.object.quaternion.copy(this._quaternionStart);
1509
+ this.object.scale.copy(this._scaleStart);
1510
+ this.dispatchEvent(_changeEvent);
1511
+ this.dispatchEvent(_objectChangeEvent);
1512
+ this.pointStart.copy(this.pointEnd);
1513
+ }
1514
+ }
1515
+ getRaycaster() {
1516
+ return _raycaster;
1517
+ }
1518
+ getMode() {
1519
+ return this.mode;
1520
+ }
1521
+ setMode(mode) {
1522
+ this.mode = mode;
1523
+ }
1524
+ setTranslationSnap(translationSnap) {
1525
+ this.translationSnap = translationSnap;
1526
+ }
1527
+ setRotationSnap(rotationSnap) {
1528
+ this.rotationSnap = rotationSnap;
1529
+ }
1530
+ setScaleSnap(scaleSnap) {
1531
+ this.scaleSnap = scaleSnap;
1532
+ }
1533
+ setSize(size) {
1534
+ this.size = size;
1535
+ }
1536
+ setSpace(space) {
1537
+ this.space = space;
1538
+ }
1539
+ }
1540
+
1541
+ function getPointer(event) {
1542
+ if (this.domElement.ownerDocument.pointerLockElement) {
1543
+ return {
1544
+ x: 0,
1545
+ y: 0,
1546
+ button: event.button
635
1547
  };
636
- this.onRender = event => {
637
- this.viewer.camera.matrix.extractBasis(this.xAxis, this.yAxis, this.zAxis);
638
- this.viewer.camera.position.add(this.zAxis.multiplyScalar(-event.deltaTime * this.speed.z));
639
- this.viewer.camera.position.add(this.xAxis.multiplyScalar(-event.deltaTime * this.speed.x));
1548
+ } else {
1549
+ const rect = this.domElement.getBoundingClientRect();
1550
+ return {
1551
+ x: (event.clientX - rect.left) / rect.width * 2 - 1,
1552
+ y: -(event.clientY - rect.top) / rect.height * 2 + 1,
1553
+ button: event.button
640
1554
  };
641
- this.viewer = viewer;
642
- this._target = new THREE.Vector3;
643
- this.quaternion = this.viewer.camera.quaternion.clone();
644
- this.xRotation = new THREE.Quaternion;
645
- this.yRotation = new THREE.Quaternion;
646
- this.mouseStart = new THREE.Vector2;
647
- this.mouseDelta = new THREE.Vector2;
648
- this.xAxis = new THREE.Vector3;
649
- this.yAxis = new THREE.Vector3;
650
- this.zAxis = new THREE.Vector3;
651
- this.yRotationAxis = new THREE.Vector3(1, 0, 0);
652
- this.walkSpeed = 1;
653
- this.boostSpeed = 5;
654
- this.speed = new THREE.Vector3;
655
- this._maxDistance = 1;
656
- this.touchStartDistance = 0;
657
- this.viewer.addEventListener("render", this.onRender);
658
- this.viewer.addEventListener("contextmenu", this.onContextMenu);
659
- this.viewer.addEventListener("mousedown", this.onMouseDown);
660
- this.viewer.addEventListener("mouseup", this.onMouseUp);
661
- this.viewer.addEventListener("touchstart", this.onTouchStart);
662
- this.viewer.addEventListener("touchmove", this.onTouchMove);
663
- this.viewer.addEventListener("touchend", this.onTouchEnd);
664
- this.viewer.addEventListener("wheel", this.onMouseWheel);
665
- document.addEventListener("keydown", this.onKeyDown);
666
- document.addEventListener("keyup", this.onKeyUp);
667
1555
  }
668
- dispose() {
669
- this.viewer.removeEventListener("render", this.onRender);
670
- this.viewer.removeEventListener("contextmenu", this.onContextMenu);
671
- this.viewer.removeEventListener("mousedown", this.onMouseDown);
672
- this.viewer.removeEventListener("mouseup", this.onMouseUp);
673
- this.viewer.removeEventListener("mousemove", this.onMouseMove);
674
- this.viewer.removeEventListener("touchstart", this.onTouchStart);
675
- this.viewer.removeEventListener("touchmove", this.onTouchMove);
676
- this.viewer.removeEventListener("touchend", this.onTouchEnd);
677
- this.viewer.removeEventListener("wheel", this.onMouseWheel);
678
- document.removeEventListener("keydown", this.onKeyDown);
679
- document.removeEventListener("keyup", this.onKeyUp);
680
- }
681
- getTouchsDistance(touches) {
682
- const [start, end] = touches;
683
- const dx = start.clientX - end.clientX;
684
- const dy = start.clientY - end.clientY;
685
- const distance = Math.sqrt(dx * dx + dy * dy);
686
- return distance;
687
- }
688
- update() {}
689
- rotateCamera(delta) {
690
- const rotateX = Math.PI * delta.x / this.viewer.canvas.clientWidth;
691
- const rotateY = Math.PI * delta.y / this.viewer.canvas.clientHeight;
692
- const quaternion = this.quaternion.clone();
693
- this.xRotation.setFromAxisAngle(this.viewer.camera.up, rotateX);
694
- this.yRotation.setFromAxisAngle(this.yRotationAxis, rotateY);
695
- quaternion.premultiply(this.xRotation).multiply(this.yRotation).normalize();
696
- this.viewer.camera.setRotationFromQuaternion(quaternion);
1556
+ }
1557
+
1558
+ function onPointerHover(event) {
1559
+ if (!this.enabled) return;
1560
+ switch (event.pointerType) {
1561
+ case "mouse":
1562
+ case "pen":
1563
+ this.pointerHover(this._getPointer(event));
1564
+ break;
697
1565
  }
698
1566
  }
699
1567
 
700
- class ClippingPlaneDragger {
701
- constructor(viewer) {
702
- this.onPointerDown = event => {
703
- this.viewer.addEventListener("pointermove", this.onPointerMove);
704
- const {offsetX: offsetX, offsetY: offsetY} = event;
705
- this.start.set(offsetX, offsetY, .5);
706
- this.start = this.screenToPlane(this.start);
707
- this.delta.set(0, 0, 0);
708
- this.end.set(0, 0, 0);
1568
+ function onPointerDown(event) {
1569
+ if (!this.enabled) return;
1570
+ if (!document.pointerLockElement) {
1571
+ this.domElement.setPointerCapture(event.pointerId);
1572
+ }
1573
+ this.domElement.addEventListener("pointermove", this._onPointerMove);
1574
+ this.pointerHover(this._getPointer(event));
1575
+ this.pointerDown(this._getPointer(event));
1576
+ }
1577
+
1578
+ function onPointerMove(event) {
1579
+ if (!this.enabled) return;
1580
+ this.pointerMove(this._getPointer(event));
1581
+ }
1582
+
1583
+ function onPointerUp(event) {
1584
+ if (!this.enabled) return;
1585
+ this.domElement.releasePointerCapture(event.pointerId);
1586
+ this.domElement.removeEventListener("pointermove", this._onPointerMove);
1587
+ this.pointerUp(this._getPointer(event));
1588
+ }
1589
+
1590
+ function intersectObjectWithRay(object, raycaster, includeInvisible) {
1591
+ const allIntersections = raycaster.intersectObject(object, true);
1592
+ for (let i = 0; i < allIntersections.length; i++) {
1593
+ if (allIntersections[i].object.visible || includeInvisible) {
1594
+ return allIntersections[i];
1595
+ }
1596
+ }
1597
+ return false;
1598
+ }
1599
+
1600
+ const _tempEuler = new Euler;
1601
+
1602
+ const _alignVector = new Vector3(0, 1, 0);
1603
+
1604
+ const _zeroVector = new Vector3(0, 0, 0);
1605
+
1606
+ const _lookAtMatrix = new Matrix4;
1607
+
1608
+ const _tempQuaternion2 = new Quaternion;
1609
+
1610
+ const _identityQuaternion = new Quaternion;
1611
+
1612
+ const _dirVector = new Vector3;
1613
+
1614
+ const _tempMatrix = new Matrix4;
1615
+
1616
+ const _unitX = new Vector3(1, 0, 0);
1617
+
1618
+ const _unitY = new Vector3(0, 1, 0);
1619
+
1620
+ const _unitZ = new Vector3(0, 0, 1);
1621
+
1622
+ const _v1 = new Vector3;
1623
+
1624
+ const _v2 = new Vector3;
1625
+
1626
+ const _v3 = new Vector3;
1627
+
1628
+ class TransformControlsGizmo extends Object3D {
1629
+ constructor() {
1630
+ super();
1631
+ this.isTransformControlsGizmo = true;
1632
+ this.type = "TransformControlsGizmo";
1633
+ const gizmoMaterial = new MeshBasicMaterial({
1634
+ depthTest: false,
1635
+ depthWrite: false,
1636
+ fog: false,
1637
+ toneMapped: false,
1638
+ transparent: true
1639
+ });
1640
+ const gizmoLineMaterial = new LineBasicMaterial({
1641
+ depthTest: false,
1642
+ depthWrite: false,
1643
+ fog: false,
1644
+ toneMapped: false,
1645
+ transparent: true
1646
+ });
1647
+ const matInvisible = gizmoMaterial.clone();
1648
+ matInvisible.opacity = .15;
1649
+ const matHelper = gizmoLineMaterial.clone();
1650
+ matHelper.opacity = .5;
1651
+ const matRed = gizmoMaterial.clone();
1652
+ matRed.color.setHex(16711680);
1653
+ const matGreen = gizmoMaterial.clone();
1654
+ matGreen.color.setHex(65280);
1655
+ const matBlue = gizmoMaterial.clone();
1656
+ matBlue.color.setHex(255);
1657
+ const matRedTransparent = gizmoMaterial.clone();
1658
+ matRedTransparent.color.setHex(16711680);
1659
+ matRedTransparent.opacity = .5;
1660
+ const matGreenTransparent = gizmoMaterial.clone();
1661
+ matGreenTransparent.color.setHex(65280);
1662
+ matGreenTransparent.opacity = .5;
1663
+ const matBlueTransparent = gizmoMaterial.clone();
1664
+ matBlueTransparent.color.setHex(255);
1665
+ matBlueTransparent.opacity = .5;
1666
+ const matWhiteTransparent = gizmoMaterial.clone();
1667
+ matWhiteTransparent.opacity = .25;
1668
+ const matYellowTransparent = gizmoMaterial.clone();
1669
+ matYellowTransparent.color.setHex(16776960);
1670
+ matYellowTransparent.opacity = .25;
1671
+ const matYellow = gizmoMaterial.clone();
1672
+ matYellow.color.setHex(16776960);
1673
+ const matGray = gizmoMaterial.clone();
1674
+ matGray.color.setHex(7895160);
1675
+ const arrowGeometry = new CylinderGeometry(0, .04, .1, 12);
1676
+ arrowGeometry.translate(0, .05, 0);
1677
+ const scaleHandleGeometry = new BoxGeometry(.08, .08, .08);
1678
+ scaleHandleGeometry.translate(0, .04, 0);
1679
+ const lineGeometry = new BufferGeometry;
1680
+ lineGeometry.setAttribute("position", new Float32BufferAttribute([ 0, 0, 0, 1, 0, 0 ], 3));
1681
+ const lineGeometry2 = new CylinderGeometry(.0075, .0075, .5, 3);
1682
+ lineGeometry2.translate(0, .25, 0);
1683
+ function CircleGeometry(radius, arc) {
1684
+ const geometry = new TorusGeometry(radius, .0075, 3, 64, arc * Math.PI * 2);
1685
+ geometry.rotateY(Math.PI / 2);
1686
+ geometry.rotateX(Math.PI / 2);
1687
+ return geometry;
1688
+ }
1689
+ function TranslateHelperGeometry() {
1690
+ const geometry = new BufferGeometry;
1691
+ geometry.setAttribute("position", new Float32BufferAttribute([ 0, 0, 0, 1, 1, 1 ], 3));
1692
+ return geometry;
1693
+ }
1694
+ const gizmoTranslate = {
1695
+ X: [ [ new Mesh(arrowGeometry, matRed), [ .5, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], [ new Mesh(arrowGeometry, matRed), [ -.5, 0, 0 ], [ 0, 0, Math.PI / 2 ] ], [ new Mesh(lineGeometry2, matRed), [ 0, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ] ],
1696
+ Y: [ [ new Mesh(arrowGeometry, matGreen), [ 0, .5, 0 ] ], [ new Mesh(arrowGeometry, matGreen), [ 0, -.5, 0 ], [ Math.PI, 0, 0 ] ], [ new Mesh(lineGeometry2, matGreen) ] ],
1697
+ Z: [ [ new Mesh(arrowGeometry, matBlue), [ 0, 0, .5 ], [ Math.PI / 2, 0, 0 ] ], [ new Mesh(arrowGeometry, matBlue), [ 0, 0, -.5 ], [ -Math.PI / 2, 0, 0 ] ], [ new Mesh(lineGeometry2, matBlue), null, [ Math.PI / 2, 0, 0 ] ] ],
1698
+ XYZ: [ [ new Mesh(new OctahedronGeometry(.1, 0), matWhiteTransparent.clone()), [ 0, 0, 0 ] ] ],
1699
+ XY: [ [ new Mesh(new BoxGeometry(.15, .15, .01), matBlueTransparent.clone()), [ .15, .15, 0 ] ] ],
1700
+ YZ: [ [ new Mesh(new BoxGeometry(.15, .15, .01), matRedTransparent.clone()), [ 0, .15, .15 ], [ 0, Math.PI / 2, 0 ] ] ],
1701
+ XZ: [ [ new Mesh(new BoxGeometry(.15, .15, .01), matGreenTransparent.clone()), [ .15, 0, .15 ], [ -Math.PI / 2, 0, 0 ] ] ]
709
1702
  };
710
- this.onPointerUp = event => {
711
- this.viewer.removeEventListener("pointermove", this.onPointerMove);
712
- if (this.end.length() === 0) {
713
- const plane = this.plane;
714
- plane.normal.multiplyScalar(-1);
715
- plane.constant *= -1;
1703
+ const pickerTranslate = {
1704
+ X: [ [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ .3, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ -.3, 0, 0 ], [ 0, 0, Math.PI / 2 ] ] ],
1705
+ Y: [ [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, .3, 0 ] ], [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, -.3, 0 ], [ 0, 0, Math.PI ] ] ],
1706
+ Z: [ [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, 0, .3 ], [ Math.PI / 2, 0, 0 ] ], [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, 0, -.3 ], [ -Math.PI / 2, 0, 0 ] ] ],
1707
+ XYZ: [ [ new Mesh(new OctahedronGeometry(.2, 0), matInvisible) ] ],
1708
+ XY: [ [ new Mesh(new BoxGeometry(.2, .2, .01), matInvisible), [ .15, .15, 0 ] ] ],
1709
+ YZ: [ [ new Mesh(new BoxGeometry(.2, .2, .01), matInvisible), [ 0, .15, .15 ], [ 0, Math.PI / 2, 0 ] ] ],
1710
+ XZ: [ [ new Mesh(new BoxGeometry(.2, .2, .01), matInvisible), [ .15, 0, .15 ], [ -Math.PI / 2, 0, 0 ] ] ]
1711
+ };
1712
+ const helperTranslate = {
1713
+ START: [ [ new Mesh(new OctahedronGeometry(.01, 2), matHelper), null, null, null, "helper" ] ],
1714
+ END: [ [ new Mesh(new OctahedronGeometry(.01, 2), matHelper), null, null, null, "helper" ] ],
1715
+ DELTA: [ [ new Line(TranslateHelperGeometry(), matHelper), null, null, null, "helper" ] ],
1716
+ X: [ [ new Line(lineGeometry, matHelper.clone()), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], "helper" ] ],
1717
+ Y: [ [ new Line(lineGeometry, matHelper.clone()), [ 0, -1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], "helper" ] ],
1718
+ Z: [ [ new Line(lineGeometry, matHelper.clone()), [ 0, 0, -1e3 ], [ 0, -Math.PI / 2, 0 ], [ 1e6, 1, 1 ], "helper" ] ]
1719
+ };
1720
+ const gizmoRotate = {
1721
+ XYZE: [ [ new Mesh(CircleGeometry(.5, 1), matGray), null, [ 0, Math.PI / 2, 0 ] ] ],
1722
+ X: [ [ new Mesh(CircleGeometry(.5, .5), matRed) ] ],
1723
+ Y: [ [ new Mesh(CircleGeometry(.5, .5), matGreen), null, [ 0, 0, -Math.PI / 2 ] ] ],
1724
+ Z: [ [ new Mesh(CircleGeometry(.5, .5), matBlue), null, [ 0, Math.PI / 2, 0 ] ] ],
1725
+ E: [ [ new Mesh(CircleGeometry(.75, 1), matYellowTransparent), null, [ 0, Math.PI / 2, 0 ] ] ]
1726
+ };
1727
+ const helperRotate = {
1728
+ AXIS: [ [ new Line(lineGeometry, matHelper.clone()), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], "helper" ] ]
1729
+ };
1730
+ const pickerRotate = {
1731
+ XYZE: [ [ new Mesh(new SphereGeometry(.25, 10, 8), matInvisible) ] ],
1732
+ X: [ [ new Mesh(new TorusGeometry(.5, .1, 4, 24), matInvisible), [ 0, 0, 0 ], [ 0, -Math.PI / 2, -Math.PI / 2 ] ] ],
1733
+ Y: [ [ new Mesh(new TorusGeometry(.5, .1, 4, 24), matInvisible), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ] ],
1734
+ Z: [ [ new Mesh(new TorusGeometry(.5, .1, 4, 24), matInvisible), [ 0, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ] ],
1735
+ E: [ [ new Mesh(new TorusGeometry(.75, .1, 2, 24), matInvisible) ] ]
1736
+ };
1737
+ const gizmoScale = {
1738
+ X: [ [ new Mesh(scaleHandleGeometry, matRed), [ .5, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], [ new Mesh(lineGeometry2, matRed), [ 0, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], [ new Mesh(scaleHandleGeometry, matRed), [ -.5, 0, 0 ], [ 0, 0, Math.PI / 2 ] ] ],
1739
+ Y: [ [ new Mesh(scaleHandleGeometry, matGreen), [ 0, .5, 0 ] ], [ new Mesh(lineGeometry2, matGreen) ], [ new Mesh(scaleHandleGeometry, matGreen), [ 0, -.5, 0 ], [ 0, 0, Math.PI ] ] ],
1740
+ Z: [ [ new Mesh(scaleHandleGeometry, matBlue), [ 0, 0, .5 ], [ Math.PI / 2, 0, 0 ] ], [ new Mesh(lineGeometry2, matBlue), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ] ], [ new Mesh(scaleHandleGeometry, matBlue), [ 0, 0, -.5 ], [ -Math.PI / 2, 0, 0 ] ] ],
1741
+ XY: [ [ new Mesh(new BoxGeometry(.15, .15, .01), matBlueTransparent), [ .15, .15, 0 ] ] ],
1742
+ YZ: [ [ new Mesh(new BoxGeometry(.15, .15, .01), matRedTransparent), [ 0, .15, .15 ], [ 0, Math.PI / 2, 0 ] ] ],
1743
+ XZ: [ [ new Mesh(new BoxGeometry(.15, .15, .01), matGreenTransparent), [ .15, 0, .15 ], [ -Math.PI / 2, 0, 0 ] ] ],
1744
+ XYZ: [ [ new Mesh(new BoxGeometry(.1, .1, .1), matWhiteTransparent.clone()) ] ]
1745
+ };
1746
+ const pickerScale = {
1747
+ X: [ [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ .3, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ -.3, 0, 0 ], [ 0, 0, Math.PI / 2 ] ] ],
1748
+ Y: [ [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, .3, 0 ] ], [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, -.3, 0 ], [ 0, 0, Math.PI ] ] ],
1749
+ Z: [ [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, 0, .3 ], [ Math.PI / 2, 0, 0 ] ], [ new Mesh(new CylinderGeometry(.2, 0, .6, 4), matInvisible), [ 0, 0, -.3 ], [ -Math.PI / 2, 0, 0 ] ] ],
1750
+ XY: [ [ new Mesh(new BoxGeometry(.2, .2, .01), matInvisible), [ .15, .15, 0 ] ] ],
1751
+ YZ: [ [ new Mesh(new BoxGeometry(.2, .2, .01), matInvisible), [ 0, .15, .15 ], [ 0, Math.PI / 2, 0 ] ] ],
1752
+ XZ: [ [ new Mesh(new BoxGeometry(.2, .2, .01), matInvisible), [ .15, 0, .15 ], [ -Math.PI / 2, 0, 0 ] ] ],
1753
+ XYZ: [ [ new Mesh(new BoxGeometry(.2, .2, .2), matInvisible), [ 0, 0, 0 ] ] ]
1754
+ };
1755
+ const helperScale = {
1756
+ X: [ [ new Line(lineGeometry, matHelper.clone()), [ -1e3, 0, 0 ], null, [ 1e6, 1, 1 ], "helper" ] ],
1757
+ Y: [ [ new Line(lineGeometry, matHelper.clone()), [ 0, -1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], "helper" ] ],
1758
+ Z: [ [ new Line(lineGeometry, matHelper.clone()), [ 0, 0, -1e3 ], [ 0, -Math.PI / 2, 0 ], [ 1e6, 1, 1 ], "helper" ] ]
1759
+ };
1760
+ function setupGizmo(gizmoMap) {
1761
+ const gizmo = new Object3D;
1762
+ for (const name in gizmoMap) {
1763
+ for (let i = gizmoMap[name].length; i--; ) {
1764
+ const object = gizmoMap[name][i][0].clone();
1765
+ const position = gizmoMap[name][i][1];
1766
+ const rotation = gizmoMap[name][i][2];
1767
+ const scale = gizmoMap[name][i][3];
1768
+ const tag = gizmoMap[name][i][4];
1769
+ object.name = name;
1770
+ object.tag = tag;
1771
+ if (position) {
1772
+ object.position.set(position[0], position[1], position[2]);
1773
+ }
1774
+ if (rotation) {
1775
+ object.rotation.set(rotation[0], rotation[1], rotation[2]);
1776
+ }
1777
+ if (scale) {
1778
+ object.scale.set(scale[0], scale[1], scale[2]);
1779
+ }
1780
+ object.updateMatrix();
1781
+ const tempGeometry = object.geometry.clone();
1782
+ tempGeometry.applyMatrix4(object.matrix);
1783
+ object.geometry = tempGeometry;
1784
+ object.renderOrder = Infinity;
1785
+ object.position.set(0, 0, 0);
1786
+ object.rotation.set(0, 0, 0);
1787
+ object.scale.set(1, 1, 1);
1788
+ gizmo.add(object);
1789
+ }
1790
+ }
1791
+ return gizmo;
1792
+ }
1793
+ this.gizmo = {};
1794
+ this.picker = {};
1795
+ this.helper = {};
1796
+ this.add(this.gizmo["translate"] = setupGizmo(gizmoTranslate));
1797
+ this.add(this.gizmo["rotate"] = setupGizmo(gizmoRotate));
1798
+ this.add(this.gizmo["scale"] = setupGizmo(gizmoScale));
1799
+ this.add(this.picker["translate"] = setupGizmo(pickerTranslate));
1800
+ this.add(this.picker["rotate"] = setupGizmo(pickerRotate));
1801
+ this.add(this.picker["scale"] = setupGizmo(pickerScale));
1802
+ this.add(this.helper["translate"] = setupGizmo(helperTranslate));
1803
+ this.add(this.helper["rotate"] = setupGizmo(helperRotate));
1804
+ this.add(this.helper["scale"] = setupGizmo(helperScale));
1805
+ this.picker["translate"].visible = false;
1806
+ this.picker["rotate"].visible = false;
1807
+ this.picker["scale"].visible = false;
1808
+ }
1809
+ updateMatrixWorld(force) {
1810
+ const space = this.mode === "scale" ? "local" : this.space;
1811
+ const quaternion = space === "local" ? this.worldQuaternion : _identityQuaternion;
1812
+ this.gizmo["translate"].visible = this.mode === "translate";
1813
+ this.gizmo["rotate"].visible = this.mode === "rotate";
1814
+ this.gizmo["scale"].visible = this.mode === "scale";
1815
+ this.helper["translate"].visible = this.mode === "translate";
1816
+ this.helper["rotate"].visible = this.mode === "rotate";
1817
+ this.helper["scale"].visible = this.mode === "scale";
1818
+ let handles = [];
1819
+ handles = handles.concat(this.picker[this.mode].children);
1820
+ handles = handles.concat(this.gizmo[this.mode].children);
1821
+ handles = handles.concat(this.helper[this.mode].children);
1822
+ for (let i = 0; i < handles.length; i++) {
1823
+ const handle = handles[i];
1824
+ handle.visible = true;
1825
+ handle.rotation.set(0, 0, 0);
1826
+ handle.position.copy(this.worldPosition);
1827
+ let factor;
1828
+ if (this.camera.isOrthographicCamera) {
1829
+ factor = (this.camera.top - this.camera.bottom) / this.camera.zoom;
716
1830
  } else {
717
- const {offsetX: offsetX, offsetY: offsetY} = event;
718
- this.end.set(offsetX, offsetY, .5);
719
- this.end = this.screenToPlane(this.end);
1831
+ factor = this.worldPosition.distanceTo(this.cameraPosition) * Math.min(1.9 * Math.tan(Math.PI * this.camera.fov / 360) / this.camera.zoom, 7);
1832
+ }
1833
+ handle.scale.set(1, 1, 1).multiplyScalar(factor * this.size / 4);
1834
+ if (handle.tag === "helper") {
1835
+ handle.visible = false;
1836
+ if (handle.name === "AXIS") {
1837
+ handle.visible = !!this.axis;
1838
+ if (this.axis === "X") {
1839
+ _tempQuaternion.setFromEuler(_tempEuler.set(0, 0, 0));
1840
+ handle.quaternion.copy(quaternion).multiply(_tempQuaternion);
1841
+ if (Math.abs(_alignVector.copy(_unitX).applyQuaternion(quaternion).dot(this.eye)) > .9) {
1842
+ handle.visible = false;
1843
+ }
1844
+ }
1845
+ if (this.axis === "Y") {
1846
+ _tempQuaternion.setFromEuler(_tempEuler.set(0, 0, Math.PI / 2));
1847
+ handle.quaternion.copy(quaternion).multiply(_tempQuaternion);
1848
+ if (Math.abs(_alignVector.copy(_unitY).applyQuaternion(quaternion).dot(this.eye)) > .9) {
1849
+ handle.visible = false;
1850
+ }
1851
+ }
1852
+ if (this.axis === "Z") {
1853
+ _tempQuaternion.setFromEuler(_tempEuler.set(0, Math.PI / 2, 0));
1854
+ handle.quaternion.copy(quaternion).multiply(_tempQuaternion);
1855
+ if (Math.abs(_alignVector.copy(_unitZ).applyQuaternion(quaternion).dot(this.eye)) > .9) {
1856
+ handle.visible = false;
1857
+ }
1858
+ }
1859
+ if (this.axis === "XYZE") {
1860
+ _tempQuaternion.setFromEuler(_tempEuler.set(0, Math.PI / 2, 0));
1861
+ _alignVector.copy(this.rotationAxis);
1862
+ handle.quaternion.setFromRotationMatrix(_lookAtMatrix.lookAt(_zeroVector, _alignVector, _unitY));
1863
+ handle.quaternion.multiply(_tempQuaternion);
1864
+ handle.visible = this.dragging;
1865
+ }
1866
+ if (this.axis === "E") {
1867
+ handle.visible = false;
1868
+ }
1869
+ } else if (handle.name === "START") {
1870
+ handle.position.copy(this.worldPositionStart);
1871
+ handle.visible = this.dragging;
1872
+ } else if (handle.name === "END") {
1873
+ handle.position.copy(this.worldPosition);
1874
+ handle.visible = this.dragging;
1875
+ } else if (handle.name === "DELTA") {
1876
+ handle.position.copy(this.worldPositionStart);
1877
+ handle.quaternion.copy(this.worldQuaternionStart);
1878
+ _tempVector.set(1e-10, 1e-10, 1e-10).add(this.worldPositionStart).sub(this.worldPosition).multiplyScalar(-1);
1879
+ _tempVector.applyQuaternion(this.worldQuaternionStart.clone().invert());
1880
+ handle.scale.copy(_tempVector);
1881
+ handle.visible = this.dragging;
1882
+ } else {
1883
+ handle.quaternion.copy(quaternion);
1884
+ if (this.dragging) {
1885
+ handle.position.copy(this.worldPositionStart);
1886
+ } else {
1887
+ handle.position.copy(this.worldPosition);
1888
+ }
1889
+ if (this.axis) {
1890
+ handle.visible = this.axis.search(handle.name) !== -1;
1891
+ }
1892
+ }
1893
+ continue;
1894
+ }
1895
+ handle.quaternion.copy(quaternion);
1896
+ if (this.mode === "translate" || this.mode === "scale") {
1897
+ const AXIS_HIDE_THRESHOLD = .99;
1898
+ const PLANE_HIDE_THRESHOLD = .2;
1899
+ if (handle.name === "X") {
1900
+ if (Math.abs(_alignVector.copy(_unitX).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_THRESHOLD) {
1901
+ handle.scale.set(1e-10, 1e-10, 1e-10);
1902
+ handle.visible = false;
1903
+ }
1904
+ }
1905
+ if (handle.name === "Y") {
1906
+ if (Math.abs(_alignVector.copy(_unitY).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_THRESHOLD) {
1907
+ handle.scale.set(1e-10, 1e-10, 1e-10);
1908
+ handle.visible = false;
1909
+ }
1910
+ }
1911
+ if (handle.name === "Z") {
1912
+ if (Math.abs(_alignVector.copy(_unitZ).applyQuaternion(quaternion).dot(this.eye)) > AXIS_HIDE_THRESHOLD) {
1913
+ handle.scale.set(1e-10, 1e-10, 1e-10);
1914
+ handle.visible = false;
1915
+ }
1916
+ }
1917
+ if (handle.name === "XY") {
1918
+ if (Math.abs(_alignVector.copy(_unitZ).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_THRESHOLD) {
1919
+ handle.scale.set(1e-10, 1e-10, 1e-10);
1920
+ handle.visible = false;
1921
+ }
1922
+ }
1923
+ if (handle.name === "YZ") {
1924
+ if (Math.abs(_alignVector.copy(_unitX).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_THRESHOLD) {
1925
+ handle.scale.set(1e-10, 1e-10, 1e-10);
1926
+ handle.visible = false;
1927
+ }
1928
+ }
1929
+ if (handle.name === "XZ") {
1930
+ if (Math.abs(_alignVector.copy(_unitY).applyQuaternion(quaternion).dot(this.eye)) < PLANE_HIDE_THRESHOLD) {
1931
+ handle.scale.set(1e-10, 1e-10, 1e-10);
1932
+ handle.visible = false;
1933
+ }
1934
+ }
1935
+ } else if (this.mode === "rotate") {
1936
+ _tempQuaternion2.copy(quaternion);
1937
+ _alignVector.copy(this.eye).applyQuaternion(_tempQuaternion.copy(quaternion).invert());
1938
+ if (handle.name.search("E") !== -1) {
1939
+ handle.quaternion.setFromRotationMatrix(_lookAtMatrix.lookAt(this.eye, _zeroVector, _unitY));
1940
+ }
1941
+ if (handle.name === "X") {
1942
+ _tempQuaternion.setFromAxisAngle(_unitX, Math.atan2(-_alignVector.y, _alignVector.z));
1943
+ _tempQuaternion.multiplyQuaternions(_tempQuaternion2, _tempQuaternion);
1944
+ handle.quaternion.copy(_tempQuaternion);
1945
+ }
1946
+ if (handle.name === "Y") {
1947
+ _tempQuaternion.setFromAxisAngle(_unitY, Math.atan2(_alignVector.x, _alignVector.z));
1948
+ _tempQuaternion.multiplyQuaternions(_tempQuaternion2, _tempQuaternion);
1949
+ handle.quaternion.copy(_tempQuaternion);
1950
+ }
1951
+ if (handle.name === "Z") {
1952
+ _tempQuaternion.setFromAxisAngle(_unitZ, Math.atan2(_alignVector.y, _alignVector.x));
1953
+ _tempQuaternion.multiplyQuaternions(_tempQuaternion2, _tempQuaternion);
1954
+ handle.quaternion.copy(_tempQuaternion);
1955
+ }
1956
+ }
1957
+ handle.visible = handle.visible && (handle.name.indexOf("X") === -1 || this.showX);
1958
+ handle.visible = handle.visible && (handle.name.indexOf("Y") === -1 || this.showY);
1959
+ handle.visible = handle.visible && (handle.name.indexOf("Z") === -1 || this.showZ);
1960
+ handle.visible = handle.visible && (handle.name.indexOf("E") === -1 || this.showX && this.showY && this.showZ);
1961
+ handle.material._color = handle.material._color || handle.material.color.clone();
1962
+ handle.material._opacity = handle.material._opacity || handle.material.opacity;
1963
+ handle.material.color.copy(handle.material._color);
1964
+ handle.material.opacity = handle.material._opacity;
1965
+ if (this.enabled && this.axis) {
1966
+ if (handle.name === this.axis) {
1967
+ handle.material.color.setHex(16776960);
1968
+ handle.material.opacity = 1;
1969
+ } else if (this.axis.split("").some((function(a) {
1970
+ return handle.name === a;
1971
+ }))) {
1972
+ handle.material.color.setHex(16776960);
1973
+ handle.material.opacity = 1;
1974
+ }
720
1975
  }
1976
+ }
1977
+ super.updateMatrixWorld(force);
1978
+ }
1979
+ }
1980
+
1981
+ class TransformControlsPlane extends Mesh {
1982
+ constructor() {
1983
+ super(new PlaneGeometry(1e5, 1e5, 2, 2), new MeshBasicMaterial({
1984
+ visible: false,
1985
+ wireframe: true,
1986
+ side: DoubleSide,
1987
+ transparent: true,
1988
+ opacity: .1,
1989
+ toneMapped: false
1990
+ }));
1991
+ this.isTransformControlsPlane = true;
1992
+ this.type = "TransformControlsPlane";
1993
+ }
1994
+ updateMatrixWorld(force) {
1995
+ let space = this.space;
1996
+ this.position.copy(this.worldPosition);
1997
+ if (this.mode === "scale") space = "local";
1998
+ _v1.copy(_unitX).applyQuaternion(space === "local" ? this.worldQuaternion : _identityQuaternion);
1999
+ _v2.copy(_unitY).applyQuaternion(space === "local" ? this.worldQuaternion : _identityQuaternion);
2000
+ _v3.copy(_unitZ).applyQuaternion(space === "local" ? this.worldQuaternion : _identityQuaternion);
2001
+ _alignVector.copy(_v2);
2002
+ switch (this.mode) {
2003
+ case "translate":
2004
+ case "scale":
2005
+ switch (this.axis) {
2006
+ case "X":
2007
+ _alignVector.copy(this.eye).cross(_v1);
2008
+ _dirVector.copy(_v1).cross(_alignVector);
2009
+ break;
2010
+
2011
+ case "Y":
2012
+ _alignVector.copy(this.eye).cross(_v2);
2013
+ _dirVector.copy(_v2).cross(_alignVector);
2014
+ break;
2015
+
2016
+ case "Z":
2017
+ _alignVector.copy(this.eye).cross(_v3);
2018
+ _dirVector.copy(_v3).cross(_alignVector);
2019
+ break;
2020
+
2021
+ case "XY":
2022
+ _dirVector.copy(_v3);
2023
+ break;
2024
+
2025
+ case "YZ":
2026
+ _dirVector.copy(_v1);
2027
+ break;
2028
+
2029
+ case "XZ":
2030
+ _alignVector.copy(_v3);
2031
+ _dirVector.copy(_v2);
2032
+ break;
2033
+
2034
+ case "XYZ":
2035
+ case "E":
2036
+ _dirVector.set(0, 0, 0);
2037
+ break;
2038
+ }
2039
+ break;
2040
+
2041
+ case "rotate":
2042
+ default:
2043
+ _dirVector.set(0, 0, 0);
2044
+ }
2045
+ if (_dirVector.length() === 0) {
2046
+ this.quaternion.copy(this.cameraQuaternion);
2047
+ } else {
2048
+ _tempMatrix.lookAt(_tempVector.set(0, 0, 0), _dirVector, _alignVector);
2049
+ this.quaternion.setFromRotationMatrix(_tempMatrix);
2050
+ }
2051
+ super.updateMatrixWorld(force);
2052
+ }
2053
+ }
2054
+
2055
+ class PlaneHelper extends Line {
2056
+ constructor(plane, size = 1, color = 16776960, offset = new Vector3) {
2057
+ const positions = [ 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0 ];
2058
+ const geometry = new BufferGeometry;
2059
+ geometry.setAttribute("position", new Float32BufferAttribute(positions, 3));
2060
+ geometry.computeBoundingSphere();
2061
+ super(geometry, new LineBasicMaterial({
2062
+ color: color,
2063
+ toneMapped: false
2064
+ }));
2065
+ this.type = "PlaneHelper";
2066
+ this.plane = plane;
2067
+ this.size = size;
2068
+ this.offset = offset;
2069
+ const positions2 = [ 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0 ];
2070
+ const geometry2 = new BufferGeometry;
2071
+ geometry2.setAttribute("position", new Float32BufferAttribute(positions2, 3));
2072
+ geometry2.computeBoundingSphere();
2073
+ this.helper = new Mesh(geometry2, new MeshBasicMaterial({
2074
+ color: color,
2075
+ opacity: .2,
2076
+ transparent: true,
2077
+ depthWrite: false,
2078
+ toneMapped: false,
2079
+ side: DoubleSide
2080
+ }));
2081
+ this.add(this.helper);
2082
+ }
2083
+ dispose() {
2084
+ this.geometry.dispose();
2085
+ this.material.dispose();
2086
+ this.children[0].geometry.dispose();
2087
+ this.children[0].material.dispose();
2088
+ }
2089
+ updateMatrixWorld(force) {
2090
+ this.position.set(0, 0, 0);
2091
+ this.lookAt(this.plane.normal);
2092
+ this.position.copy(this.offset);
2093
+ this.translateZ(-(this.offset.dot(this.plane.normal) + this.plane.constant));
2094
+ this.scale.set(.5 * this.size, .5 * this.size, 1);
2095
+ super.updateMatrixWorld(force);
2096
+ }
2097
+ }
2098
+
2099
+ class CuttingPlaneDragger extends OrbitDragger {
2100
+ constructor(viewer, normal, color) {
2101
+ super(viewer);
2102
+ this.transformChange = () => {
2103
+ this.plane.constant = -this.planeCenter.position.dot(this.plane.normal);
2104
+ this.viewer.update();
721
2105
  };
722
- this.onPointerMove = event => {
723
- const {offsetX: offsetX, offsetY: offsetY} = event;
724
- this.end.set(offsetX, offsetY, .5);
725
- this.end = this.screenToPlane(this.end);
726
- this.delta.copy(this.end).sub(this.start);
727
- this.start.copy(this.end);
728
- const plane = this.plane;
729
- plane.translate(this.delta);
2106
+ this.transformDrag = event => {
2107
+ this.orbit.enabled = !event.value;
730
2108
  };
731
- this.viewer = viewer;
732
- this.viewer.addEventListener("pointerdown", this.onPointerDown);
733
- this.viewer.addEventListener("pointerup", this.onPointerUp);
734
- this.plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
735
- this.start = new THREE.Vector3;
736
- this.end = new THREE.Vector3;
737
- this.delta = new THREE.Vector3;
738
- if (!this.viewer.renderer.clippingPlanes) this.viewer.renderer.clippingPlanes = [];
739
- this.viewer.renderer.clippingPlanes.push(this.plane);
740
- this.planeHelper = new THREE.PlaneHelper(this.plane, 150, 16776960);
741
- this.viewer.scene.add(this.planeHelper);
2109
+ this.viewerExplode = () => {
2110
+ this.planeHelper.size = this.viewer.extents.getSize(new Vector3).length();
2111
+ this.viewer.update();
2112
+ };
2113
+ this.onDoubleClick = event => {
2114
+ event.stopPropagation();
2115
+ this.plane.negate();
2116
+ this.viewer.update();
2117
+ };
2118
+ const size = viewer.extents.getSize(new Vector3).length();
2119
+ const center = viewer.extents.getCenter(new Vector3);
2120
+ const constant = -center.dot(normal);
2121
+ this.plane = new Plane(normal, constant);
2122
+ if (!viewer.renderer.clippingPlanes) viewer.renderer.clippingPlanes = [];
2123
+ viewer.renderer.clippingPlanes.push(this.plane);
2124
+ this.planeHelper = new PlaneHelper(this.plane, size, color, center);
2125
+ this.viewer.helpers.add(this.planeHelper);
2126
+ this.planeCenter = new Object3D;
2127
+ this.planeCenter.position.copy(viewer.extents.getCenter(new Vector3));
2128
+ this.viewer.helpers.add(this.planeCenter);
2129
+ this.transform = new TransformControls(viewer.camera, viewer.canvas);
2130
+ this.transform.showX = !!normal.x;
2131
+ this.transform.showY = !!normal.y;
2132
+ this.transform.showZ = !!normal.z;
2133
+ this.transform.attach(this.planeCenter);
2134
+ this.transform.addEventListener("change", this.transformChange);
2135
+ this.transform.addEventListener("dragging-changed", this.transformDrag);
2136
+ this.viewer.helpers.add(this.transform);
2137
+ this.viewer.on("explode", this.viewerExplode);
2138
+ this.viewer.canvas.addEventListener("dblclick", this.onDoubleClick, true);
2139
+ this.viewer.update();
742
2140
  }
743
2141
  dispose() {
744
- this.viewer.removeEventListener("pointerdown", this.onPointerDown);
745
- this.viewer.removeEventListener("pointerup", this.onPointerUp);
746
- this.viewer.removeEventListener("pointermove", this.onPointerMove);
747
- this.viewer.renderer.clippingPlanes = this.viewer.renderer.clippingPlanes.filter((plane => this.plane !== plane));
2142
+ this.viewer.off("explode", this.viewerExplode);
2143
+ this.viewer.canvas.removeEventListener("dblclick", this.onDoubleClick, true);
2144
+ this.transform.removeEventListener("change", this.transformChange);
2145
+ this.transform.removeEventListener("dragging-changed", this.transformDrag);
2146
+ this.transform.removeFromParent();
2147
+ this.transform.dispose();
748
2148
  this.planeHelper.removeFromParent();
2149
+ this.planeHelper.dispose();
2150
+ this.planeCenter.removeFromParent();
2151
+ super.dispose();
749
2152
  }
750
- update() {}
751
- screenToWorld(v) {
752
- v.x = 2 * v.x / this.viewer.canvas.clientWidth - 1;
753
- v.y = 1 - 2 * v.y / this.viewer.canvas.clientHeight;
754
- return v.unproject(this.viewer.camera);
755
- }
756
- screenToPlane(v) {
757
- v = this.screenToWorld(v);
758
- const direction = v.sub(this.viewer.camera.position).normalize();
759
- const ray = new THREE.Ray(this.viewer.camera.position, direction);
760
- let plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
761
- const dot = plane.normal.dot(this.plane.normal);
762
- if (Math.abs(dot) === 1) {
763
- plane = new THREE.Plane(new THREE.Vector3(1, 0, 0), 0);
764
- }
765
- const result = new THREE.Vector3;
766
- ray.intersectPlane(plane, result);
767
- return result;
2153
+ }
2154
+
2155
+ class CuttingPlaneXAxisDragger extends CuttingPlaneDragger {
2156
+ constructor(viewer) {
2157
+ super(viewer, new Vector3(1, 0, 0), 16711680);
2158
+ }
2159
+ }
2160
+
2161
+ class CuttingPlaneYAxisDragger extends CuttingPlaneDragger {
2162
+ constructor(viewer) {
2163
+ super(viewer, new Vector3(0, 1, 0), 65280);
2164
+ }
2165
+ }
2166
+
2167
+ class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
2168
+ constructor(viewer) {
2169
+ super(viewer, new Vector3(0, 0, 1), 255);
2170
+ }
2171
+ }
2172
+
2173
+ class ExtentsComponent {
2174
+ constructor(viewer) {
2175
+ this.syncExtents = () => {
2176
+ const extents = this.viewer.models.reduce(((result, gltf) => {
2177
+ const modelExtents = (new Box3).setFromObject(gltf.scene);
2178
+ return result.isEmpty() ? result.copy(modelExtents) : result.union(modelExtents);
2179
+ }), new Box3);
2180
+ this.viewer.extents.copy(extents);
2181
+ this.viewer.target.copy(extents.getCenter(new Vector3));
2182
+ };
2183
+ this.viewer = viewer;
2184
+ this.viewer.addEventListener("geometryend", this.syncExtents);
2185
+ this.viewer.addEventListener("clear", this.syncExtents);
2186
+ this.viewer.on("explode", this.syncExtents);
2187
+ }
2188
+ dispose() {
2189
+ this.viewer.removeEventListener("geometryend", this.syncExtents);
2190
+ this.viewer.removeEventListener("clear", this.syncExtents);
2191
+ this.viewer.off("explode", this.syncExtents);
768
2192
  }
769
2193
  }
770
2194
 
771
2195
  class LightComponent {
772
2196
  constructor(viewer) {
773
2197
  this.viewer = viewer;
774
- this.ambientLight = new THREE.AmbientLight(16777215, 0);
2198
+ this.ambientLight = new AmbientLight(16777215, 0);
775
2199
  this.viewer.camera.add(this.ambientLight);
776
- this.directLight = new THREE.DirectionalLight(16777215, 1);
777
- this.directLight.position.set(.5, 0, .866);
778
- this.viewer.camera.add(this.directLight);
2200
+ this.directionalLight = new DirectionalLight(16777215, 1);
2201
+ this.directionalLight.position.set(.5, 0, .866);
2202
+ this.viewer.camera.add(this.directionalLight);
779
2203
  }
780
2204
  dispose() {
781
2205
  this.ambientLight.removeFromParent();
782
- this.directLight.removeFromParent();
2206
+ this.ambientLight = undefined;
2207
+ this.directionalLight.removeFromParent();
2208
+ this.directionalLight = undefined;
783
2209
  }
784
2210
  }
785
2211
 
@@ -789,9 +2215,10 @@ class BackgroundComponent {
789
2215
  this.backgroundColor.setHex(16777215);
790
2216
  };
791
2217
  this.viewer = viewer;
792
- this.backgroundColor = new THREE.Color(16777215);
2218
+ this.backgroundColor = new Color(16777215);
793
2219
  const environment = new RoomEnvironment;
794
- const pmremGenerator = new THREE.PMREMGenerator(this.viewer.renderer);
2220
+ const pmremGenerator = new PMREMGenerator(this.viewer.renderer);
2221
+ this.viewer.renderer.setClearColor(this.backgroundColor);
795
2222
  this.viewer.scene.background = this.backgroundColor;
796
2223
  this.viewer.scene.environment = pmremGenerator.fromScene(environment).texture;
797
2224
  this.viewer.addEventListener("optionschange", this.syncOptions);
@@ -804,26 +2231,16 @@ class BackgroundComponent {
804
2231
  }
805
2232
  }
806
2233
 
807
- class DefaultCameraPositionComponent {
2234
+ class DefaultPositionComponent {
808
2235
  constructor(viewer) {
809
2236
  this.geometryEnd = event => {
810
- const {data: scene} = event;
811
- scene.updateMatrixWorld();
812
- const box = (new THREE.Box3).setFromObject(scene);
813
- const size = box.getSize(new THREE.Vector3).length();
814
- const center = box.getCenter(new THREE.Vector3);
815
- scene.position.x += scene.position.x - center.x;
816
- scene.position.y += scene.position.y - center.y;
817
- scene.position.z += scene.position.z - center.z;
2237
+ const box = this.viewer.extents;
2238
+ const size = box.getSize(new Vector3).length();
818
2239
  this.viewer.camera.near = size / 100;
819
2240
  this.viewer.camera.far = size * 100;
820
- this.viewer.camera.position.copy(center);
821
- this.viewer.camera.position.x += size / 2;
822
- this.viewer.camera.position.y += size / 5;
823
- this.viewer.camera.position.z += size / 2;
824
2241
  this.viewer.camera.updateMatrixWorld();
825
2242
  this.viewer.camera.updateProjectionMatrix();
826
- this.viewer.camera.lookAt(center);
2243
+ this.viewer.executeCommand("zoomToExtents");
827
2244
  };
828
2245
  this.viewer = viewer;
829
2246
  this.viewer.addEventListener("geometryend", this.geometryEnd);
@@ -859,38 +2276,136 @@ class ResizeCanvasComponent {
859
2276
 
860
2277
  class RenderLoopComponent {
861
2278
  constructor(viewer) {
862
- this.requestID = 0;
863
2279
  this.animate = (time = 0) => {
864
- this.requestID = requestAnimationFrame(this.animate);
2280
+ this.requestId = requestAnimationFrame(this.animate);
865
2281
  this.viewer.render(time);
866
2282
  };
867
2283
  this.viewer = viewer;
868
2284
  this.animate();
869
2285
  }
870
2286
  dispose() {
871
- cancelAnimationFrame(this.requestID);
2287
+ cancelAnimationFrame(this.requestId);
2288
+ }
2289
+ }
2290
+
2291
+ class WCSHelper extends Object3D {
2292
+ constructor(camera) {
2293
+ super();
2294
+ this.camera = camera;
2295
+ this.size = 160;
2296
+ this.orthoCamera = new OrthographicCamera(-2, 2, 2, -2, 0, 4);
2297
+ this.orthoCamera.position.set(0, 0, 2);
2298
+ const matRed = new MeshBasicMaterial({
2299
+ toneMapped: false,
2300
+ color: "#aa0000"
2301
+ });
2302
+ const matGreen = new MeshBasicMaterial({
2303
+ toneMapped: false,
2304
+ color: "#00aa00"
2305
+ });
2306
+ const matBlue = new MeshBasicMaterial({
2307
+ toneMapped: false,
2308
+ color: "#0000aa"
2309
+ });
2310
+ const spriteRed = this.getSpriteMaterial(matRed.color, "X");
2311
+ const spriteGreen = this.getSpriteMaterial(matGreen.color, "Y");
2312
+ const spriteBlue = this.getSpriteMaterial(matBlue.color, "Z");
2313
+ const lineGeometry = new CylinderGeometry(.01, .01, 1, 3);
2314
+ lineGeometry.translate(0, .5, 0);
2315
+ const arrowGeometry = new CylinderGeometry(0, .1, .25, 12);
2316
+ arrowGeometry.translate(0, .625, 0);
2317
+ const axesMap = {
2318
+ X: [ [ new Mesh(arrowGeometry, matRed), [ .5, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], [ new Mesh(lineGeometry, matRed), [ 0, 0, 0 ], [ 0, 0, -Math.PI / 2 ] ], [ new Sprite(spriteRed), [ 1.55, 0, 0 ] ] ],
2319
+ Y: [ [ new Mesh(arrowGeometry, matGreen), [ 0, .5, 0 ], null ], [ new Mesh(lineGeometry, matGreen), null, null ], [ new Sprite(spriteGreen), [ 0, 1.55, 0 ] ] ],
2320
+ Z: [ [ new Mesh(arrowGeometry, matBlue), [ 0, 0, .5 ], [ Math.PI / 2, 0, 0 ] ], [ new Mesh(lineGeometry, matBlue), null, [ Math.PI / 2, 0, 0 ] ], [ new Sprite(spriteBlue), [ 0, 0, 1.55 ] ] ]
2321
+ };
2322
+ Object.keys(axesMap).forEach((key => {
2323
+ axesMap[key].forEach((objects => {
2324
+ const object = objects[0];
2325
+ const position = objects[1];
2326
+ const rotation = objects[2];
2327
+ object.name = key;
2328
+ if (position) object.position.set(position[0], position[1], position[2]);
2329
+ if (rotation) object.rotation.set(rotation[0], rotation[1], rotation[2]);
2330
+ object.updateMatrixWorld();
2331
+ this.add(object);
2332
+ }));
2333
+ }));
2334
+ }
2335
+ dispose() {
2336
+ this.traverse((object => {
2337
+ if (object.geometry) object.geometry.dispose();
2338
+ if (object.material) object.material.dispose();
2339
+ }));
2340
+ }
2341
+ getSpriteMaterial(color, text) {
2342
+ const canvas = document.createElement("canvas");
2343
+ canvas.width = 64;
2344
+ canvas.height = 64;
2345
+ const context = canvas.getContext("2d");
2346
+ context.clearRect(0, 0, 64, 64);
2347
+ context.font = "24px Arial";
2348
+ context.textAlign = "center";
2349
+ context.fillStyle = color.getStyle();
2350
+ context.fillText(text, 32, 41);
2351
+ const texture = new CanvasTexture(canvas);
2352
+ texture.colorSpace = SRGBColorSpace;
2353
+ return new SpriteMaterial({
2354
+ map: texture,
2355
+ toneMapped: false
2356
+ });
2357
+ }
2358
+ render(renderer) {
2359
+ this.quaternion.copy(this.camera.quaternion).invert();
2360
+ this.updateMatrixWorld();
2361
+ const clippingPlanes = renderer.clippingPlanes;
2362
+ const viewport = renderer.getViewport(new Vector4);
2363
+ renderer.setViewport(this.position.x, this.position.y, this.size, this.size);
2364
+ renderer.clippingPlanes = [];
2365
+ renderer.clearDepth();
2366
+ renderer.render(this, this.orthoCamera);
2367
+ renderer.setViewport(viewport);
2368
+ renderer.clippingPlanes = clippingPlanes;
2369
+ }
2370
+ }
2371
+
2372
+ class WCSHelperComponent {
2373
+ constructor(viewer) {
2374
+ this.viewerRender = () => {
2375
+ if (!this.viewer.extents.isEmpty()) this.wcsHelper.render(this.viewer.renderer);
2376
+ };
2377
+ this.wcsHelper = new WCSHelper(viewer.camera);
2378
+ this.viewer = viewer;
2379
+ this.viewer.addEventListener("render", this.viewerRender);
2380
+ }
2381
+ dispose() {
2382
+ this.viewer.removeEventListener("render", this.viewerRender);
2383
+ this.wcsHelper.dispose();
872
2384
  }
873
2385
  }
874
2386
 
875
2387
  class Viewer extends EventEmitter2 {
876
2388
  constructor(client) {
877
2389
  super();
878
- this.renderNeeded = false;
879
2390
  this._options = new Options(this);
880
2391
  this.client = client;
881
- this.canvasEvents = CANVAS_EVENTS.slice();
2392
+ this.canvasEvents = CANVAS_EVENTS;
882
2393
  this.canvaseventlistener = event => this.emit(event);
2394
+ this.extents = new Box3;
2395
+ this.target = new Vector3;
883
2396
  this.draggerFactory = {
884
2397
  Pan: PanDragger,
885
2398
  Zoom: ZoomDragger,
886
2399
  Orbit: OrbitDragger,
887
2400
  Walk: WalkDragger,
888
- Clipping: ClippingPlaneDragger
2401
+ CuttingPlaneXAxis: CuttingPlaneXAxisDragger,
2402
+ CuttingPlaneYAxis: CuttingPlaneYAxisDragger,
2403
+ CuttingPlaneZAxis: CuttingPlaneZAxisDragger
889
2404
  };
890
2405
  this._activeDragger = null;
891
2406
  this.models = [];
892
2407
  this.components = [];
893
- this.selectedObjects = [];
2408
+ this.selected = [];
894
2409
  this.renderTime = 0;
895
2410
  this.render = this.render.bind(this);
896
2411
  this.update = this.update.bind(this);
@@ -903,30 +2418,35 @@ class Viewer extends EventEmitter2 {
903
2418
  }
904
2419
  initialize(canvas, onProgress) {
905
2420
  this.addEventListener("optionschange", (event => this.syncOptions(event.data)));
906
- this.scene = new THREE.Scene;
2421
+ this.scene = new Scene;
2422
+ this.helpers = new Scene;
907
2423
  const rect = canvas.parentElement.getBoundingClientRect();
908
2424
  const width = rect.width || 1;
909
2425
  const height = rect.height || 1;
910
- this.camera = new THREE.PerspectiveCamera(45, width / height, .01, 1e3);
2426
+ this.camera = new PerspectiveCamera(45, width / height, .01, 1e3);
911
2427
  this.camera.up.set(0, 0, 1);
912
- this.renderer = new THREE.WebGLRenderer({
2428
+ this.renderer = new WebGLRenderer({
913
2429
  canvas: canvas,
914
- antialias: true
2430
+ antialias: true,
2431
+ preserveDrawingBuffer: true
915
2432
  });
916
2433
  this.renderer.setPixelRatio(window.devicePixelRatio);
917
2434
  this.renderer.setSize(width, height);
918
- this.renderer.toneMapping = THREE.LinearToneMapping;
2435
+ this.renderer.toneMapping = LinearToneMapping;
919
2436
  this.canvas = canvas;
920
2437
  this.canvasEvents.forEach((x => canvas.addEventListener(x, this.canvaseventlistener)));
2438
+ this.components.push(new ExtentsComponent(this));
921
2439
  this.components.push(new LightComponent(this));
922
2440
  this.components.push(new BackgroundComponent(this));
923
- this.components.push(new DefaultCameraPositionComponent(this));
2441
+ this.components.push(new DefaultPositionComponent(this));
924
2442
  this.components.push(new ResizeCanvasComponent(this));
925
2443
  this.components.push(new RenderLoopComponent(this));
2444
+ this.components.push(new SelectionComponent(this));
2445
+ this.components.push(new WCSHelperComponent(this));
926
2446
  this.syncOptions();
927
2447
  this.renderTime = performance.now();
928
2448
  this.render(this.renderTime);
929
- if (onProgress) onProgress(new ProgressEvent("progress", {
2449
+ if (typeof onProgress === "function") onProgress(new ProgressEvent("progress", {
930
2450
  lengthComputable: true,
931
2451
  loaded: 1,
932
2452
  total: 1
@@ -951,6 +2471,7 @@ class Viewer extends EventEmitter2 {
951
2471
  this.components = [];
952
2472
  this.setActiveDragger("");
953
2473
  this.removeAllListeners();
2474
+ this.clear();
954
2475
  if (this.canvas) {
955
2476
  this.canvasEvents.forEach((x => this.canvas.removeEventListener(x, this.canvaseventlistener)));
956
2477
  this.canvas = undefined;
@@ -959,6 +2480,7 @@ class Viewer extends EventEmitter2 {
959
2480
  this.renderer = undefined;
960
2481
  this.camera = undefined;
961
2482
  this.scene = undefined;
2483
+ this.helpers = undefined;
962
2484
  return this;
963
2485
  }
964
2486
  isInitialized() {
@@ -967,10 +2489,12 @@ class Viewer extends EventEmitter2 {
967
2489
  render(time) {
968
2490
  if (!this.renderNeeded) return;
969
2491
  if (!this.renderer) return;
970
- if (!this.scene) return;
971
- if (!this.camera) return;
2492
+ const clippingPlanes = this.renderer.clippingPlanes;
2493
+ this.renderer.setViewport(0, 0, this.canvas.offsetWidth, this.canvas.offsetHeight);
972
2494
  this.renderer.render(this.scene, this.camera);
973
- this.renderNeeded = false;
2495
+ this.renderer.clippingPlanes = [];
2496
+ this.renderer.autoClear = false;
2497
+ this.renderer.render(this.helpers, this.camera);
974
2498
  const deltaTime = (time - this.renderTime) / 1e3;
975
2499
  this.renderTime = time;
976
2500
  this.emitEvent({
@@ -978,6 +2502,9 @@ class Viewer extends EventEmitter2 {
978
2502
  time: time,
979
2503
  deltaTime: deltaTime
980
2504
  });
2505
+ this.renderer.autoClear = true;
2506
+ this.renderer.clippingPlanes = clippingPlanes;
2507
+ this.renderNeeded = false;
981
2508
  }
982
2509
  update(force = false) {
983
2510
  this.renderNeeded = true;
@@ -1055,6 +2582,7 @@ class Viewer extends EventEmitter2 {
1055
2582
  this.models.push(gltf);
1056
2583
  this.scene.add(gltf.scene);
1057
2584
  this.update();
2585
+ this.resetActiveDragger();
1058
2586
  this.emitEvent({
1059
2587
  type: "databasechunk"
1060
2588
  });
@@ -1074,6 +2602,7 @@ class Viewer extends EventEmitter2 {
1074
2602
  return this;
1075
2603
  }
1076
2604
  clear() {
2605
+ if (!this.renderer) return this;
1077
2606
  function disposeMaterial(material) {
1078
2607
  const materials = Array.isArray(material) ? material : [ material ];
1079
2608
  materials.forEach((material => {
@@ -1084,49 +2613,79 @@ class Viewer extends EventEmitter2 {
1084
2613
  if (object.geometry) object.geometry.dispose();
1085
2614
  if (object.material) disposeMaterial(object.material);
1086
2615
  }
1087
- this.selectedObjects = [];
2616
+ this.setActiveDragger();
2617
+ this.selected = [];
2618
+ this.renderer.clippingPlanes = [];
2619
+ this.helpers.traverse(disposeObject);
2620
+ this.helpers.clear();
1088
2621
  this.models.forEach((gltf => gltf.scene.traverse(disposeObject)));
1089
2622
  this.models.forEach((gltf => gltf.scene.removeFromParent()));
1090
2623
  this.models = [];
1091
- this.update();
2624
+ this.scene.clear();
1092
2625
  this.emitEvent({
1093
2626
  type: "clear"
1094
2627
  });
2628
+ this.update(true);
1095
2629
  return this;
1096
2630
  }
1097
2631
  activeDragger() {
1098
2632
  return this._activeDragger;
1099
2633
  }
1100
- setActiveDragger(name) {
2634
+ setActiveDragger(name = "") {
1101
2635
  if (!this._activeDragger || this._activeDragger.name !== name) {
1102
2636
  if (this._activeDragger) {
1103
2637
  this._activeDragger.dispose();
1104
2638
  this._activeDragger = null;
1105
2639
  }
1106
- const Constructor = this.draggerFactory[name];
1107
- if (Constructor) {
1108
- this._activeDragger = new Constructor(this);
1109
- this._activeDragger.name = name;
2640
+ if (this.isInitialized()) {
2641
+ const Constructor = this.draggerFactory[name];
2642
+ if (Constructor) {
2643
+ this._activeDragger = new Constructor(this);
2644
+ this._activeDragger.name = name;
2645
+ }
2646
+ }
2647
+ const canvas = this.canvas;
2648
+ if (canvas) {
2649
+ canvas.className = canvas.className.split(" ").filter((x => !x.startsWith("oda-cursor-"))).filter((x => x)).concat(`oda-cursor-${name.toLowerCase()}`).join(" ");
1110
2650
  }
2651
+ this.emitEvent({
2652
+ type: "changeactivedragger",
2653
+ data: name
2654
+ });
1111
2655
  }
1112
2656
  return this._activeDragger;
1113
2657
  }
1114
2658
  resetActiveDragger() {
1115
2659
  const dragger = this._activeDragger;
1116
2660
  if (dragger) {
1117
- this.setActiveDragger("");
2661
+ this.setActiveDragger();
1118
2662
  this.setActiveDragger(dragger.name);
1119
2663
  }
1120
2664
  }
1121
2665
  is3D() {
1122
- return false;
2666
+ return true;
2667
+ }
2668
+ getSelected() {
2669
+ return this.executeCommand("getSelected");
2670
+ }
2671
+ setSelected(handles) {
2672
+ this.executeCommand("setSelected", handles);
1123
2673
  }
1124
2674
  executeCommand(id, ...args) {
1125
2675
  return commands("ThreeJS").executeCommand(id, this, ...args);
1126
2676
  }
2677
+ getComponent(type) {
2678
+ return this.components.find((component => component instanceof type));
2679
+ }
1127
2680
  drawViewpoint(viewpoint) {}
1128
2681
  createViewpoint() {
1129
- return {};
2682
+ var _a;
2683
+ const viewpoint = {};
2684
+ viewpoint.snapshot = {
2685
+ data: (_a = this.canvas) === null || _a === void 0 ? void 0 : _a.toDataURL("image/jpeg", .25)
2686
+ };
2687
+ viewpoint.description = (new Date).toDateString();
2688
+ return viewpoint;
1130
2689
  }
1131
2690
  }
1132
2691