@inweb/viewer-three 25.11.0 → 25.11.1

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.
@@ -3,10 +3,11 @@ import { GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import { EventEmitter2 } from "@inweb/eventemitter2";
4
4
  import { Assembly, Client, Model, File } from "@inweb/client";
5
5
  import { CanvasEventMap, IDragger, IOptions, IViewer, IViewpoint, Options, OptionsEventMap, ViewerEventMap } from "@inweb/viewer-core";
6
+ import { IMarkup, IWorldTransform } from "@inweb/markup";
6
7
  /**
7
8
  * 3D viewer powered by {@link https://threejs.org/ | Three.js}.
8
9
  */
9
- export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & OptionsEventMap> implements IViewer {
10
+ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & OptionsEventMap> implements IViewer, IWorldTransform {
10
11
  client: Client | undefined;
11
12
  protected _options: Options;
12
13
  private canvaseventlistener;
@@ -25,6 +26,7 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
25
26
  private components;
26
27
  private renderNeeded;
27
28
  private renderTime;
29
+ private _markup;
28
30
  /**
29
31
  * @param client - The `Client` instance that is used to load model reference files from the
30
32
  * Open Cloud Server. Do not specify `Client` if you need a standalone viewer instance to
@@ -33,6 +35,12 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
33
35
  constructor(client?: Client);
34
36
  get options(): IOptions;
35
37
  get draggers(): string[];
38
+ /**
39
+ * 2D markup core instance used to create markups.
40
+ *
41
+ * @readonly
42
+ */
43
+ get markup(): IMarkup;
36
44
  initialize(canvas: HTMLCanvasElement, onProgress?: (event: ProgressEvent<EventTarget>) => void): Promise<this>;
37
45
  dispose(): this;
38
46
  isInitialized(): boolean;
@@ -140,6 +148,9 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
140
148
  withCredentials?: boolean;
141
149
  }): Promise<this>;
142
150
  clear(): this;
151
+ syncOverlay(): void;
152
+ clearOverlay(): void;
153
+ clearSlices(): void;
143
154
  getSelected(): string[];
144
155
  setSelected(handles?: string[]): void;
145
156
  clearSelected(): void;
@@ -152,6 +163,27 @@ export declare class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMa
152
163
  setActiveDragger(name?: string): IDragger | null;
153
164
  resetActiveDragger(): void;
154
165
  is3D(): boolean;
166
+ screenToWorld(position: {
167
+ x: number;
168
+ y: number;
169
+ }): {
170
+ x: number;
171
+ y: number;
172
+ z: number;
173
+ };
174
+ worldToScreen(position: {
175
+ x: number;
176
+ y: number;
177
+ z: number;
178
+ }): {
179
+ x: number;
180
+ y: number;
181
+ };
182
+ getScale(): {
183
+ x: number;
184
+ y: number;
185
+ z: number;
186
+ };
155
187
  executeCommand(id: string, ...args: any[]): any;
156
188
  getComponent(type: any): any;
157
189
  drawViewpoint(viewpoint: IViewpoint): void;
@@ -11,8 +11,8 @@ export declare class SelectionComponent implements IDisposable {
11
11
  onPointerDown: (event: PointerEvent) => void;
12
12
  onPointerUp: (event: PointerEvent) => void;
13
13
  onDoubleClick: (event: MouseEvent) => void;
14
- getMousePosition(event: MouseEvent, position: Vector2): Vector2;
15
- getPointerIntersects(position: Vector2): Array<Intersection<Object3D>>;
14
+ getMousePosition(event: MouseEvent, target: Vector2): Vector2;
15
+ getPointerIntersects(mouse: Vector2): Array<Intersection<Object3D>>;
16
16
  select(object: any): void;
17
17
  clearSelection(): void;
18
18
  optionsChange: () => void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@inweb/viewer-three",
3
- "version": "25.11.0",
4
- "description": "3D viewer powered by Three.js",
3
+ "version": "25.11.1",
4
+ "description": "JavaScript library for rendering CAD and BIM files in a browser using Three.js",
5
5
  "homepage": "https://cloud.opendesign.com/docs/index.html",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "author": "Open Design Alliance",
@@ -28,9 +28,10 @@
28
28
  "docs": "typedoc"
29
29
  },
30
30
  "dependencies": {
31
- "@inweb/client": "~25.11.0",
32
- "@inweb/eventemitter2": "~25.11.0",
33
- "@inweb/viewer-core": "~25.11.0"
31
+ "@inweb/client": "~25.11.1",
32
+ "@inweb/eventemitter2": "~25.11.1",
33
+ "@inweb/markup": "~25.11.1",
34
+ "@inweb/viewer-core": "~25.11.1"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/three": "^0.152.1",
@@ -21,7 +21,7 @@
21
21
  // acknowledge and accept the above terms.
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
- import { Box3, LinearToneMapping, Object3D, PerspectiveCamera, Scene, Vector3, WebGLRenderer } from "three";
24
+ import { Box3, LinearToneMapping, Object3D, PerspectiveCamera, Plane, Scene, Vector3, WebGLRenderer } from "three";
25
25
  import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
26
26
  import { GLTFLoadingManager } from "./loaders/GLTFLoadingManager";
27
27
 
@@ -31,14 +31,19 @@ import {
31
31
  CANVAS_EVENTS,
32
32
  CanvasEventMap,
33
33
  commands,
34
+ IClippingPlane,
35
+ IComponent,
34
36
  IDragger,
35
37
  IOptions,
38
+ IPerspectiveCamera,
39
+ IPoint,
36
40
  IViewer,
37
41
  IViewpoint,
38
42
  Options,
39
43
  OptionsEventMap,
40
44
  ViewerEventMap,
41
45
  } from "@inweb/viewer-core";
46
+ import { IMarkup, IWorldTransform, Markup } from "@inweb/markup";
42
47
 
43
48
  import { PanDragger } from "./draggers/PanDragger";
44
49
  import { OrbitDragger } from "./draggers/OrbitDragger";
@@ -64,7 +69,10 @@ import { WCSHelperComponent } from "./components/WCSHelperComponent";
64
69
  /**
65
70
  * 3D viewer powered by {@link https://threejs.org/ | Three.js}.
66
71
  */
67
- export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & OptionsEventMap> implements IViewer {
72
+ export class Viewer
73
+ extends EventEmitter2<ViewerEventMap & CanvasEventMap & OptionsEventMap>
74
+ implements IViewer, IWorldTransform
75
+ {
68
76
  public client: Client | undefined;
69
77
  protected _options: Options;
70
78
 
@@ -90,6 +98,8 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
90
98
  private renderNeeded: boolean;
91
99
  private renderTime: DOMHighResTimeStamp;
92
100
 
101
+ private _markup: IMarkup;
102
+
93
103
  /**
94
104
  * @param client - The `Client` instance that is used to load model reference files from the
95
105
  * Open Cloud Server. Do not specify `Client` if you need a standalone viewer instance to
@@ -126,6 +136,8 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
126
136
 
127
137
  this.render = this.render.bind(this);
128
138
  this.update = this.update.bind(this);
139
+
140
+ this._markup = new Markup();
129
141
  }
130
142
 
131
143
  get options(): IOptions {
@@ -136,6 +148,15 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
136
148
  return Object.keys(this.draggerFactory);
137
149
  }
138
150
 
151
+ /**
152
+ * 2D markup core instance used to create markups.
153
+ *
154
+ * @readonly
155
+ */
156
+ get markup(): IMarkup {
157
+ return this._markup;
158
+ }
159
+
139
160
  initialize(canvas: HTMLCanvasElement, onProgress?: (event: ProgressEvent<EventTarget>) => void): Promise<this> {
140
161
  this.addEventListener("optionschange", (event) => this.syncOptions(event.data));
141
162
 
@@ -156,6 +177,8 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
156
177
  this.canvas = canvas;
157
178
  this.canvasEvents.forEach((x) => canvas.addEventListener(x, this.canvaseventlistener));
158
179
 
180
+ this._markup.initialize(this.canvas, this.canvasEvents, this, this);
181
+
159
182
  this.components.push(new ExtentsComponent(this));
160
183
  this.components.push(new LightComponent(this));
161
184
  this.components.push(new BackgroundComponent(this));
@@ -169,6 +192,7 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
169
192
  // this.components.push(new ExtentsHelperComponent(this));
170
193
 
171
194
  this.syncOptions();
195
+ this.syncOverlay();
172
196
 
173
197
  this.renderTime = performance.now();
174
198
  this.render(this.renderTime);
@@ -189,11 +213,13 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
189
213
  this.components.forEach((component: IDisposable) => component.dispose());
190
214
  this.components = [];
191
215
 
192
- this.setActiveDragger("");
216
+ this.setActiveDragger();
193
217
  this.removeAllListeners();
194
218
 
195
219
  this.clear();
196
220
 
221
+ this._markup.dispose();
222
+
197
223
  if (this.canvas) {
198
224
  this.canvasEvents.forEach((x) => this.canvas.removeEventListener(x, this.canvaseventlistener));
199
225
  this.canvas = undefined;
@@ -418,8 +444,9 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
418
444
  this.models.push(gltf);
419
445
  this.scene.add(gltf.scene);
420
446
 
447
+ this.syncOptions();
448
+ this.syncOverlay();
421
449
  this.update();
422
- this.resetActiveDragger();
423
450
 
424
451
  this.emitEvent({ type: "databasechunk" });
425
452
  this.emitEvent({ type: "geometryend", data: gltf.scene });
@@ -450,9 +477,8 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
450
477
  }
451
478
 
452
479
  this.setActiveDragger();
453
-
454
- this.selected = [];
455
- this.renderer.clippingPlanes = [];
480
+ this.clearSlices();
481
+ this.clearOverlay();
456
482
 
457
483
  this.helpers.traverse(disposeObject);
458
484
  this.helpers.clear();
@@ -463,12 +489,36 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
463
489
 
464
490
  this.scene.clear();
465
491
 
466
- this.emitEvent({ type: "clear" });
492
+ this.syncOptions();
493
+ this.syncOverlay();
467
494
  this.update(true);
468
495
 
496
+ this.emitEvent({ type: "clear" });
497
+
469
498
  return this;
470
499
  }
471
500
 
501
+ syncOverlay(): void {
502
+ if (!this.renderer) return;
503
+
504
+ this._markup.syncOverlay();
505
+ this.update();
506
+ }
507
+
508
+ clearOverlay(): void {
509
+ if (!this.renderer) return;
510
+
511
+ this._markup.clearOverlay();
512
+ this.update();
513
+ }
514
+
515
+ clearSlices(): void {
516
+ if (!this.renderer) return;
517
+
518
+ this.renderer.clippingPlanes = [];
519
+ this.update();
520
+ }
521
+
472
522
  getSelected(): string[] {
473
523
  return this.executeCommand("getSelected");
474
524
  }
@@ -528,6 +578,7 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
528
578
  .join(" ");
529
579
  }
530
580
  this.emitEvent({ type: "changeactivedragger", data: name });
581
+ this.update();
531
582
  }
532
583
  return this._activeDragger;
533
584
  }
@@ -544,6 +595,36 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
544
595
  return true;
545
596
  }
546
597
 
598
+ screenToWorld(position: { x: number; y: number }): { x: number; y: number; z: number } {
599
+ if (!this.renderer) return { x: position.x, y: position.y, z: 0 };
600
+
601
+ const rect = this.canvas.getBoundingClientRect();
602
+ const x = position.x / (rect.width / 2) - 1;
603
+ const y = -position.y / (rect.height / 2) + 1;
604
+
605
+ const point = new Vector3(x, y, -1);
606
+ point.unproject(this.camera);
607
+
608
+ return { x: point.x, y: point.y, z: point.z };
609
+ }
610
+
611
+ worldToScreen(position: { x: number; y: number; z: number }): { x: number; y: number } {
612
+ if (!this.renderer) return { x: position.x, y: position.y };
613
+
614
+ const point = new Vector3(position.x, position.y, position.z);
615
+ point.project(this.camera);
616
+
617
+ const rect = this.canvas.getBoundingClientRect();
618
+ const x = (point.x + 1) * (rect.width / 2);
619
+ const y = (-point.y + 1) * (rect.height / 2);
620
+
621
+ return { x, y };
622
+ }
623
+
624
+ getScale(): { x: number; y: number; z: number } {
625
+ return { x: 1, y: 1, z: 1 };
626
+ }
627
+
547
628
  executeCommand(id: string, ...args: any[]): any {
548
629
  return commands("ThreeJS").executeCommand(id, this, ...args);
549
630
  }
@@ -552,13 +633,101 @@ export class Viewer extends EventEmitter2<ViewerEventMap & CanvasEventMap & Opti
552
633
  return this.components.find((component) => component instanceof type);
553
634
  }
554
635
 
555
- drawViewpoint(viewpoint: IViewpoint): void {}
636
+ drawViewpoint(viewpoint: IViewpoint): void {
637
+ if (!this.renderer) return;
638
+
639
+ const getVector3FromPoint3d = ({ x, y, z }): Vector3 => new Vector3(x, y, z);
640
+
641
+ const setPerspectiveCamera = (camera: IPerspectiveCamera) => {
642
+ if (camera) {
643
+ this.camera.up.copy(getVector3FromPoint3d(camera.up_vector));
644
+ this.camera.fov = camera.field_of_view;
645
+ this.camera.position.copy(getVector3FromPoint3d(camera.view_point));
646
+ this.camera.lookAt(getVector3FromPoint3d(camera.direction).add(this.camera.position));
647
+ this.camera.updateMatrixWorld();
648
+ this.camera.updateProjectionMatrix();
649
+ }
650
+ };
651
+
652
+ const setClippingPlanes = (clipping_planes: IClippingPlane[]) => {
653
+ clipping_planes?.forEach((clipping_plane) => {
654
+ const plane = new Plane();
655
+ plane.setFromNormalAndCoplanarPoint(
656
+ getVector3FromPoint3d(clipping_plane.direction),
657
+ getVector3FromPoint3d(clipping_plane.location)
658
+ );
659
+
660
+ this.renderer.clippingPlanes.push(plane);
661
+ });
662
+ };
663
+
664
+ const setSelection = (selection: IComponent[]) => {
665
+ this.setSelected(selection?.map((component) => component.handle));
666
+ };
667
+
668
+ const draggerName = this._activeDragger?.name;
669
+
670
+ this.setActiveDragger();
671
+ this.clearSlices();
672
+ this.clearOverlay();
673
+
674
+ this.clearSelected();
675
+ this.showAll();
676
+ this.explode();
677
+
678
+ setPerspectiveCamera(viewpoint.perspective_camera);
679
+ setClippingPlanes(viewpoint.clipping_planes);
680
+ setSelection(viewpoint.selection);
681
+ this._markup.setViewpoint(viewpoint);
682
+
683
+ this.target = getVector3FromPoint3d(viewpoint.custom_fields?.camera_target ?? this.target);
684
+
685
+ this.setActiveDragger(draggerName);
686
+ this.emitEvent({ type: "drawviewpoint", data: viewpoint });
687
+ this.update();
688
+ }
556
689
 
557
690
  createViewpoint(): IViewpoint {
558
- const viewpoint: IViewpoint = {};
691
+ if (!this.renderer) return {};
692
+
693
+ const getPoint3dFromVector3 = ({ x, y, z }): IPoint => ({ x, y, z });
694
+
695
+ const getPerspectiveCamera = (): IPerspectiveCamera => {
696
+ return {
697
+ view_point: getPoint3dFromVector3(this.camera.position),
698
+ direction: getPoint3dFromVector3(this.camera.getWorldDirection(new Vector3())),
699
+ up_vector: getPoint3dFromVector3(this.camera.up),
700
+ field_of_view: this.camera.fov,
701
+ };
702
+ };
559
703
 
560
- viewpoint.snapshot = { data: this.canvas?.toDataURL("image/jpeg", 0.25) };
704
+ const getClippingPlanes = (): IClippingPlane[] => {
705
+ const clipping_planes = [];
706
+ this.renderer.clippingPlanes.forEach((plane: Plane) => {
707
+ const clipping_plane = {
708
+ location: getPoint3dFromVector3(plane.coplanarPoint(new Vector3())),
709
+ direction: getPoint3dFromVector3(plane.normal),
710
+ };
711
+ clipping_planes.push(clipping_plane);
712
+ });
713
+ return clipping_planes;
714
+ };
715
+
716
+ const getSelection = (): IComponent[] => {
717
+ return this.getSelected().map((handle) => ({ handle }));
718
+ };
719
+
720
+ const viewpoint: IViewpoint = { custom_fields: {} };
721
+
722
+ viewpoint.perspective_camera = getPerspectiveCamera();
723
+ viewpoint.clipping_planes = getClippingPlanes();
724
+ viewpoint.selection = getSelection();
561
725
  viewpoint.description = new Date().toDateString();
726
+ this._markup.getViewpoint(viewpoint);
727
+
728
+ viewpoint.custom_fields.camera_target = getPoint3dFromVector3(this.target);
729
+
730
+ this.emitEvent({ type: "createviewpoint", data: viewpoint });
562
731
 
563
732
  return viewpoint;
564
733
  }
@@ -24,6 +24,5 @@
24
24
  import { commands } from "@inweb/viewer-core";
25
25
  import type { Viewer } from "../Viewer";
26
26
 
27
- // markup should be as a separate library
28
- commands("ThreeJS").registerCommand("clearMarkup", (viewer: Viewer) => console.warn("clearMarkup not implemented"));
27
+ commands("ThreeJS").registerCommand("clearMarkup", (viewer: Viewer) => viewer.clearOverlay());
29
28
  commands("ThreeJS").registerCommandAlias("clearMarkup", "clearOverlay");
@@ -24,9 +24,4 @@
24
24
  import { commands } from "@inweb/viewer-core";
25
25
  import type { Viewer } from "../Viewer";
26
26
 
27
- function clearSlices(viewer: Viewer) {
28
- viewer.renderer.clippingPlanes = [];
29
- viewer.update();
30
- }
31
-
32
- commands("ThreeJS").registerCommand("clearSlices", clearSlices);
27
+ commands("ThreeJS").registerCommand("clearSlices", (viewer: Viewer) => viewer.clearSlices());
@@ -25,7 +25,7 @@ import { commands } from "@inweb/viewer-core";
25
25
  import type { Viewer } from "../Viewer";
26
26
 
27
27
  function resetView(viewer: Viewer): void {
28
- viewer.executeCommand("setActiveDragger", "");
28
+ viewer.executeCommand("setActiveDragger");
29
29
  viewer.executeCommand("clearSlices");
30
30
  viewer.executeCommand("clearOverlay");
31
31
  viewer.executeCommand("setMarkupColor");
@@ -31,8 +31,8 @@ export const defaultViewPositions = {
31
31
  bottom: new Vector3(0, 0, -1),
32
32
  left: new Vector3(-1, 0, 0),
33
33
  right: new Vector3(1, 0, 0),
34
- front: new Vector3(0, 1, 0),
35
- back: new Vector3(0, -1, 0),
34
+ front: new Vector3(0, -1, 0),
35
+ back: new Vector3(0, 1, 0),
36
36
  sw: new Vector3(-0.5, -0.5, 1.0).normalize(),
37
37
  se: new Vector3(0.5, -0.5, 1.0).normalize(),
38
38
  ne: new Vector3(0.5, 0.5, 1.0).normalize(),
@@ -42,16 +42,17 @@ export const defaultViewPositions = {
42
42
  function setDefaultViewPosition(viewer: Viewer, position: string): void {
43
43
  const direction = defaultViewPositions[position] || defaultViewPositions["sw"];
44
44
 
45
- const camera = viewer.camera;
46
45
  const center = viewer.extents.getCenter(new Vector3());
47
46
  const sphere = viewer.extents.getBoundingSphere(new Sphere());
48
- const offset = new Vector3().copy(direction).multiplyScalar(sphere.radius);
47
+ const offet = direction.clone().multiplyScalar(sphere.radius);
49
48
 
50
- camera.position.copy(center);
51
- camera.position.add(offset);
52
- camera.rotation.set(0, 0, 0);
49
+ const camera = viewer.camera;
50
+ camera.position.copy(center).add(offet);
53
51
  camera.lookAt(center);
54
52
  camera.updateProjectionMatrix();
53
+ camera.updateMatrixWorld();
54
+
55
+ viewer.target.copy(center);
55
56
 
56
57
  viewer.update();
57
58
  viewer.emit({ type: "viewposition", data: position });
@@ -49,7 +49,7 @@ export class DefaultPositionComponent implements IDisposable {
49
49
  this.viewer.camera.updateMatrixWorld();
50
50
  this.viewer.camera.updateProjectionMatrix();
51
51
 
52
- // this.viewer.executeCommand("setDefaultViewPosition", "sw");
52
+ this.viewer.executeCommand("setDefaultViewPosition", "sw");
53
53
  this.viewer.executeCommand("zoomToExtents");
54
54
  };
55
55
  }
@@ -85,18 +85,17 @@ export class SelectionComponent implements IDisposable {
85
85
  this.viewer.executeCommand("zoomToSelected");
86
86
  };
87
87
 
88
- getMousePosition(event: MouseEvent, position: Vector2): Vector2 {
89
- const rect = this.viewer.canvas.getBoundingClientRect();
90
-
91
- position.setX((event.clientX - rect.left) / rect.width);
92
- position.setY((event.clientY - rect.top) / rect.height);
93
-
94
- return position;
88
+ getMousePosition(event: MouseEvent, target: Vector2): Vector2 {
89
+ return target.set(event.clientX, event.clientY);
95
90
  }
96
91
 
97
- getPointerIntersects(position: Vector2): Array<Intersection<Object3D>> {
98
- const mouse = new Vector2(position.x * 2 - 1, -(position.y * 2) + 1);
99
- this.raycaster.setFromCamera(mouse, this.viewer.camera);
92
+ getPointerIntersects(mouse: Vector2): Array<Intersection<Object3D>> {
93
+ const rect = this.viewer.canvas.getBoundingClientRect();
94
+ const x = ((mouse.x - rect.left) / rect.width) * 2 - 1;
95
+ const y = (-(mouse.y - rect.top) / rect.height) * 2 + 1;
96
+
97
+ const coords = new Vector2(x, y);
98
+ this.raycaster.setFromCamera(coords, this.viewer.camera);
100
99
 
101
100
  const objects = [];
102
101
  this.viewer.scene.traverseVisible((child) => objects.push(child));
@@ -45,6 +45,7 @@ export class OrbitDragger implements IDisposable {
45
45
  this.viewer.on("geometryend", this.updateControls);
46
46
  this.viewer.on("viewposition", this.updateControls);
47
47
  this.viewer.on("zoom", this.updateControls);
48
+ this.viewer.on("drawviewpoint", this.updateControls);
48
49
  this.viewer.on("contextmenu", this.stopContextMenu);
49
50
  this.updateControls();
50
51
  }
@@ -53,6 +54,7 @@ export class OrbitDragger implements IDisposable {
53
54
  this.viewer.off("geometryend", this.updateControls);
54
55
  this.viewer.off("viewposition", this.updateControls);
55
56
  this.viewer.off("zoom", this.updateControls);
57
+ this.viewer.off("drawviewpoint", this.updateControls);
56
58
  this.viewer.off("contextmenu", this.stopContextMenu);
57
59
 
58
60
  this.orbit.removeEventListener("change", this.controlsChange);