@xeokit/xeokit-sdk 2.6.52 → 2.6.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xeokit/xeokit-sdk",
3
- "version": "2.6.52",
3
+ "version": "2.6.54",
4
4
  "description": "Web Programming Toolkit for 3D/2D BIM and AEC Graphics",
5
5
  "module": "./dist/xeokit-sdk.es.js",
6
6
  "main": "./dist/xeokit-sdk.cjs.js",
@@ -680,9 +680,10 @@ class StoreyViewsPlugin extends Plugin {
680
680
  * @param {Number[]} worldPos 3D World-space position.
681
681
  * @returns {String} ID of the storey containing the position, or null if the position falls outside all the storeys.
682
682
  */
683
- getStoreyContainingWorldPos(worldPos) {
684
- for (var storeyId in this.storeys) {
685
- const storey = this.storeys[storeyId];
683
+ getStoreyContainingWorldPos(worldPos, modelId = null) {
684
+ const storeys = modelId ? this._filterStoreys(modelId) : this.storeys;
685
+ for (let storeyId in storeys) {
686
+ const storey = storeys[storeyId];
686
687
  if (math.point3AABB3AbsoluteIntersect(storey.storeyAABB, worldPos)) {
687
688
  return storeyId;
688
689
  }
@@ -696,9 +697,10 @@ class StoreyViewsPlugin extends Plugin {
696
697
  * @param {Number[]} worldPos 3D World-space position.
697
698
  * @returns {String} ID of the storey containing the position, or null if the position falls outside all the storeys.
698
699
  */
699
- getStoreyInVerticalRange(worldPos) {
700
- for (let storeyId in this.storeys) {
701
- const storey = this.storeys[storeyId];
700
+ getStoreyInVerticalRange(worldPos, modelId = null) {
701
+ const storeys = modelId ? this._filterStoreys(modelId) : this.storeys;
702
+ for (let storeyId in storeys) {
703
+ const storey = storeys[storeyId];
702
704
  const aabb = [0, 0, 0, 0, 0, 0], pos = [0, 0, 0];
703
705
  aabb[1] = storey.storeyAABB[1];
704
706
  aabb[4] = storey.storeyAABB[4];
@@ -716,12 +718,14 @@ class StoreyViewsPlugin extends Plugin {
716
718
  * @param {Number[]} worldPos 3D World-space position.
717
719
  * @returns {String} ID of the lowest/highest story or null.
718
720
  */
719
- isPositionAboveOrBelowBuilding(worldPos) {
720
- const keys = Object.keys(this.storeys);
721
+ isPositionAboveOrBelowBuilding(worldPos, modelId = null) {
722
+ const storeys = modelId ? this._filterStoreys(modelId) : this.storeys;
723
+ const keys = Object.keys(storeys);
724
+ if(keys.length <= 0) return null;
721
725
  const ids = [keys[0], keys[keys.length - 1]];
722
- if (worldPos[1] < this.storeys[ids[0]].storeyAABB[1])
726
+ if (worldPos[1] < storeys[ids[0]].storeyAABB[1])
723
727
  return ids[0];
724
- else if (worldPos[1] > this.storeys[ids[1]].storeyAABB[4])
728
+ else if (worldPos[1] > storeys[ids[1]].storeyAABB[4])
725
729
  return ids[1];
726
730
  return null;
727
731
  }
@@ -845,8 +849,8 @@ class StoreyViewsPlugin extends Plugin {
845
849
 
846
850
  if (storey1MetaObject && (storey1MetaObject.attributes && storey1MetaObject.attributes.elevation !== undefined) &&
847
851
  storey2MetaObject && (storey2MetaObject.attributes && storey2MetaObject.attributes.elevation !== undefined)) {
848
- const elevation1 = storey1MetaObject.attributes.elevation;
849
- const elevation2 = storey2MetaObject.attributes.elevation;
852
+ const elevation1 = Number.parseFloat(storey1MetaObject.attributes.elevation);
853
+ const elevation2 = Number.parseFloat(storey2MetaObject.attributes.elevation);
850
854
  if (elevation1 > elevation2) {
851
855
  return -1;
852
856
  }
@@ -865,6 +869,12 @@ class StoreyViewsPlugin extends Plugin {
865
869
  }
866
870
  });
867
871
  }
872
+
873
+ _filterStoreys(modelId) {
874
+ return Object.fromEntries(
875
+ Object.entries(this.storeys).filter(([_, value]) => value.modelId === modelId)
876
+ );
877
+ }
868
878
  }
869
879
 
870
880
  export {StoreyViewsPlugin}
@@ -1205,7 +1205,7 @@ export class TreeViewPlugin extends Plugin {
1205
1205
  return;
1206
1206
  }
1207
1207
  const firstChild = children[0];
1208
- if (this._hierarchy === "storeys" && firstChild.type === "IfcBuildingStorey") {
1208
+ if ((this._hierarchy === "storeys" || this._hierarchy === "containment") && firstChild.type === "IfcBuildingStorey") {
1209
1209
  children.sort(this._getSpatialSortFunc());
1210
1210
  } else {
1211
1211
  children.sort(this._alphaSortFunc);
@@ -1216,21 +1216,11 @@ export class TreeViewPlugin extends Plugin {
1216
1216
  }
1217
1217
  }
1218
1218
 
1219
- _getSpatialSortFunc() { // Creates cached sort func with Viewer in scope
1219
+ _getSpatialSortFunc() {
1220
1220
  const viewer = this.viewer;
1221
1221
  const scene = viewer.scene;
1222
1222
  const camera = scene.camera;
1223
- const metaScene = viewer.metaScene;
1224
- return this._spatialSortFunc || (this._spatialSortFunc = (node1, node2) => {
1225
- if (!node1.aabb || !node2.aabb) {
1226
- // Sorting on lowest point of the AABB is likely more more robust when objects could overlap storeys
1227
- if (!node1.aabb) {
1228
- node1.aabb = scene.getAABB(metaScene.getObjectIDsInSubtree(node1.objectId));
1229
- }
1230
- if (!node2.aabb) {
1231
- node2.aabb = scene.getAABB(metaScene.getObjectIDsInSubtree(node2.objectId));
1232
- }
1233
- }
1223
+ return this._spatialSortFunc || (this._spatialSortFunc = (storey1, storey2) => {
1234
1224
  let idx = 0;
1235
1225
  if (camera.xUp) {
1236
1226
  idx = 0;
@@ -1239,13 +1229,24 @@ export class TreeViewPlugin extends Plugin {
1239
1229
  } else {
1240
1230
  idx = 2;
1241
1231
  }
1242
- if (node1.aabb[idx] > node2.aabb[idx]) {
1243
- return -1;
1244
- }
1245
- if (node1.aabb[idx] < node2.aabb[idx]) {
1246
- return 1;
1232
+ const metaScene = this.viewer.metaScene;
1233
+ const storey1MetaObject = metaScene.metaObjects[storey1.objectId];
1234
+ const storey2MetaObject = metaScene.metaObjects[storey2.objectId];
1235
+
1236
+ if (storey1MetaObject && (storey1MetaObject.attributes && storey1MetaObject.attributes.elevation !== undefined) &&
1237
+ storey2MetaObject && (storey2MetaObject.attributes && storey2MetaObject.attributes.elevation !== undefined)) {
1238
+ const elevation1 = Number.parseFloat(storey1MetaObject.attributes.elevation);
1239
+ const elevation2 = Number.parseFloat(storey2MetaObject.attributes.elevation);
1240
+ if (elevation1 > elevation2) {
1241
+ return -1;
1242
+ }
1243
+ if (elevation1 < elevation2) {
1244
+ return 1;
1245
+ }
1246
+ return 0;
1247
+ } else {
1248
+ return 0;
1247
1249
  }
1248
- return 0;
1249
1250
  });
1250
1251
  }
1251
1252
 
@@ -493,19 +493,15 @@ class Viewer {
493
493
 
494
494
  for (let i = 0, len = pluginContainerElements.length; i < len; i++) {
495
495
  const containerElement = pluginContainerElements[i];
496
- //only calculate the scale for first plugin
497
- //for all others keep the scale 1 otherwise it will keep multiplying the scale with the base scale of canvas
498
- //resulting in increase/decreased size for the the canvas that is being overlapped
499
- const scale = i == 0 ? snapshotCanvas.width / containerElement.clientWidth : 1;
500
- const off = math.vec2([ 0, 0 ]);
501
- transformToNode(canvas, containerElement, off);
502
496
  await html2canvas(containerElement, {
503
497
  canvas: snapshotCanvas,
504
498
  backgroundColor: null,
505
- x: off[0],
506
- y: off[1],
507
- scale
499
+ scale: snapshotCanvas.width / containerElement.clientWidth
508
500
  });
501
+ // Reverts translation and scaling applied to the snapshotCanvas's context
502
+ // by the html2canvas call (inside the ForeignObjectRenderer's constructor)
503
+ // (implemented to compensate XCD-153 issue)
504
+ snapshotCanvas.getContext("2d").resetTransform();
509
505
  }
510
506
  if (!params.includeGizmos) {
511
507
  this.sendToPlugins("snapshotFinished");
@@ -459,6 +459,12 @@ const DEFAULT_SNAP_EDGE = true;
459
459
  * keyMap[cameraControl.AXIS_VIEW_FRONT] = [input.KEY_NUM_4];
460
460
  * keyMap[cameraControl.AXIS_VIEW_TOP] = [input.KEY_NUM_5];
461
461
  * keyMap[cameraControl.AXIS_VIEW_BOTTOM] = [input.KEY_NUM_6];
462
+ * keyMap[cameraControl.MOUSE_PAN] = [[input.KEY_SHIFT, input.MOUSE_LEFT_BUTTON]];
463
+ * keyMap[cameraControl.MOUSE_ROTATE] = [
464
+ * [input.KEY_SHIFT, input.MOUSE_MIDDLE_BUTTON],
465
+ * [input.KEY_SHIFT, input.MOUSE_RIGHT_BUTTON]
466
+ * ]
467
+ * keyMap[cameraControl.MOUSE_DOLLY] = [[input.KEY_CTRL, input.MOUSE_RIGHT_BUTTON]];
462
468
  *
463
469
  * cameraControl.keyMap = keyMap;
464
470
  * ````
@@ -477,6 +483,70 @@ const DEFAULT_SNAP_EDGE = true;
477
483
  *
478
484
  * * ````"qwerty"````
479
485
  * * ````"azerty"````
486
+ *
487
+ * ## Basic Keyboard Mapping
488
+ * * ````"OR" Relation````
489
+ * Set multiple keys to trigger an action if any one is pressed:
490
+ *
491
+ * ````javascript
492
+ * keyMap[cameraControl.DOLLY_BACKWARDS] = [input.KEY_S, input.KEY_SUBTRACT];
493
+ * ````
494
+ *
495
+ * If either ````KEY_S```` or ````KEY_SUBTRACT```` is pressed, the camera will dolly backward.
496
+ *
497
+ * * ````"AND" Relation````
498
+ * To require all keys in a combination to be pressed:
499
+ *
500
+ * ````javascript
501
+ * keyMap[cameraControl.DOLLY_BACKWARDS] = [[input.KEY_S, input.KEY_SUBTRACT]];
502
+ * ````
503
+ *
504
+ * The camera will dolly backward if ````both KEY_S```` and ````KEY_SUBTRACT```` are pressed.
505
+ *
506
+ * * ````Mix "AND" and "OR" Relation````
507
+ * Use a combination of keys and groups for flexibility:
508
+ *
509
+ * ````javascript
510
+ * keyMap[cameraControl.DOLLY_BACKWARDS] = [
511
+ * [input.KEY_S, input.KEY_SUBTRACT], // 'And' group
512
+ * input.KEY_SHIFT // 'Or' with previous
513
+ * ];
514
+ * ````
515
+ *
516
+ * The camera will dolly backward if ````KEY_S```` + ````KEY_SUBTRACT```` are pressed together, or if ````KEY_SHIFT```` is pressed.
517
+ *
518
+ * ## Special Mouse Actions
519
+ * Certain actions support combinations with specific mouse buttons or events:
520
+ *
521
+ * * ````MOUSE_PAN````
522
+ * For panning with a key and mouse movement:
523
+ *
524
+ * ````javascript
525
+ * keyMap[cameraControl.MOUSE_PAN] = [[input.KEY_SHIFT, input.MOUSE_LEFT_BUTTON]];
526
+ * ````
527
+ *
528
+ * Panning is triggered by pressing ````KEY_SHIFT```` + ````MOUSE_LEFT_BUTTON```` while moving the mouse.
529
+ *
530
+ * * ````MOUSE_ROTATE````
531
+ * Similar to panning, rotation can be configured with key and mouse button combinations:
532
+ *
533
+ * ````javascript
534
+ * keyMap[cameraControl.MOUSE_ROTATE] = [[input.KEY_CTRL, input.MOUSE_LEFT_BUTTON]];
535
+ * ````
536
+ *
537
+ * Rotation is triggered by pressing ````KEY_CTRL```` + ````MOUSE_LEFT_BUTTON```` during mouse movement.
538
+ *
539
+ * * ````MOUSE_DOLLY````
540
+ * Dolly with mouse wheel scrolling and optional key combinations:
541
+ *
542
+ * ````javascript
543
+ * keyMap[cameraControl.MOUSE_DOLLY] = [[input.KEY_ALT]];
544
+ * ````
545
+ *
546
+ * Dolly action occurs when scrolling the mouse wheel with ````KEY_ALT```` held (no need to specify MOUSE_WHEEL).
547
+ *
548
+ * ## Special Mouse Actions
549
+ * Use ````input.MOUSE_LEFT_BUTTON````, ````input.MOUSE_MIDDLE_BUTTON````, and ````input.MOUSE_RIGHT_BUTTON```` in combinations only for camera movements involving the mouse.
480
550
  */
481
551
  class CameraControl extends Component {
482
552
 
@@ -614,6 +684,27 @@ class CameraControl extends Component {
614
684
  */
615
685
  this.AXIS_VIEW_BOTTOM = 17;
616
686
 
687
+ /**
688
+ * Identifies the XX action.
689
+ * @final
690
+ * @type {Number}
691
+ */
692
+ this.MOUSE_PAN = 18;
693
+
694
+ /**
695
+ * Identifies the XX action.
696
+ * @final
697
+ * @type {Number}
698
+ */
699
+ this.MOUSE_ROTATE = 19;
700
+
701
+ /**
702
+ * Identifies the XX action.
703
+ * @final
704
+ * @type {Number}
705
+ */
706
+ this.MOUSE_DOLLY = 20;
707
+
617
708
  this._keyMap = {}; // Maps key codes to the above actions
618
709
 
619
710
  this.scene.canvas.canvas.oncontextmenu = (e) => {
@@ -802,6 +893,12 @@ class CameraControl extends Component {
802
893
  keyMap[this.AXIS_VIEW_FRONT] = [input.KEY_NUM_4];
803
894
  keyMap[this.AXIS_VIEW_TOP] = [input.KEY_NUM_5];
804
895
  keyMap[this.AXIS_VIEW_BOTTOM] = [input.KEY_NUM_6];
896
+ keyMap[this.MOUSE_PAN] = [
897
+ [input.MOUSE_LEFT_BUTTON, input.KEY_SHIFT],
898
+ this._configs.panRightClick ? input.MOUSE_RIGHT_BUTTON : input.MOUSE_MIDDLE_BUTTON
899
+ ]
900
+ keyMap[this.MOUSE_ROTATE] = [input.MOUSE_LEFT_BUTTON];
901
+ keyMap[this.MOUSE_DOLLY] = [];
805
902
  break;
806
903
 
807
904
  case "azerty":
@@ -823,6 +920,12 @@ class CameraControl extends Component {
823
920
  keyMap[this.AXIS_VIEW_FRONT] = [input.KEY_NUM_4];
824
921
  keyMap[this.AXIS_VIEW_TOP] = [input.KEY_NUM_5];
825
922
  keyMap[this.AXIS_VIEW_BOTTOM] = [input.KEY_NUM_6];
923
+ keyMap[this.MOUSE_PAN] = [
924
+ [input.MOUSE_LEFT_BUTTON, input.KEY_SHIFT],
925
+ this._configs.panRightClick ? input.MOUSE_RIGHT_BUTTON : input.MOUSE_MIDDLE_BUTTON
926
+ ]
927
+ keyMap[this.MOUSE_ROTATE] = [input.MOUSE_LEFT_BUTTON];
928
+ keyMap[this.MOUSE_DOLLY] = [];
826
929
  break;
827
930
  }
828
931
 
@@ -842,6 +945,44 @@ class CameraControl extends Component {
842
945
  return this._keyMap;
843
946
  }
844
947
 
948
+ _areAllKeysDown(keyDownMap, keys) {
949
+ if (!keys || keys.length <= 0) {
950
+ return true;
951
+ }
952
+ if (!keyDownMap) {
953
+ return false;
954
+ }
955
+ for (let i = 0, len = keys.length; i < len; i++) {
956
+ const key = keys[i];
957
+ if (!keyDownMap[key])
958
+ return false;
959
+ }
960
+ return true;
961
+ }
962
+
963
+ _isAnyOtherKeyDown(keyDownMap, keyMap) {
964
+ for (let i = 0, len = keyDownMap.length; i < len; i++) {
965
+ if (keyDownMap[i]) {
966
+ if (Array.isArray(keyMap)) {
967
+ if (keyMap.indexOf(i) < 0) return true;
968
+ }
969
+ else if (i !== keyMap) return true;
970
+ }
971
+ }
972
+ return false;
973
+ }
974
+
975
+ _isMouseAction(action) {
976
+ switch (action) {
977
+ case this.MOUSE_ROTATE:
978
+ case this.MOUSE_DOLLY:
979
+ case this.MOUSE_PAN:
980
+ return true;
981
+ default:
982
+ return false;
983
+ }
984
+ }
985
+
845
986
  /**
846
987
  * Returns true if any keys configured for the given action are down.
847
988
  * @param action
@@ -853,14 +994,21 @@ class CameraControl extends Component {
853
994
  if (!keys) {
854
995
  return false;
855
996
  }
997
+ if (keys.length === 0 && this._isMouseAction(action)) return true;
856
998
  if (!keyDownMap) {
857
999
  keyDownMap = this.scene.input.keyDown;
858
1000
  }
859
1001
  for (let i = 0, len = keys.length; i < len; i++) {
860
1002
  const key = keys[i];
861
- if (keyDownMap[key]) {
862
- return true;
1003
+ if (!Array.isArray(key)) {
1004
+ if (keyDownMap[key] && !this._isAnyOtherKeyDown(keyDownMap, key))
1005
+ return true;
1006
+ }
1007
+ else {
1008
+ if (this._areAllKeysDown(keyDownMap, key) && !this._isAnyOtherKeyDown(keyDownMap, key))
1009
+ return true;
863
1010
  }
1011
+
864
1012
  }
865
1013
  return false;
866
1014
  }
@@ -1296,6 +1444,16 @@ class CameraControl extends Component {
1296
1444
  */
1297
1445
  set panRightClick(value) {
1298
1446
  this._configs.panRightClick = value !== false;
1447
+ const panKeyMap = this._keyMap[this.MOUSE_PAN];
1448
+ if (panKeyMap && panKeyMap.length > 0) {
1449
+ const input = this.scene.input;
1450
+ for (let i = 0, len = panKeyMap.length; i < len; i++) {
1451
+ if (this._configs.panRightClick && panKeyMap[i] === input.MOUSE_MIDDLE_BUTTON)
1452
+ this._keyMap[this.MOUSE_PAN][i] = input.MOUSE_RIGHT_BUTTON;
1453
+ else if (!this._configs.panRightClick && panKeyMap[i] === input.MOUSE_RIGHT_BUTTON)
1454
+ this._keyMap[this.MOUSE_PAN][i] = input.MOUSE_MIDDLE_BUTTON;
1455
+ }
1456
+ }
1299
1457
  }
1300
1458
 
1301
1459
  /**
@@ -39,6 +39,7 @@ class MousePanRotateDollyHandler {
39
39
  this._scene = scene;
40
40
 
41
41
  const pickController = controllers.pickController;
42
+ const cameraControl = controllers.cameraControl;
42
43
 
43
44
  let lastX = 0;
44
45
  let lastY = 0;
@@ -107,6 +108,15 @@ class MousePanRotateDollyHandler {
107
108
  }
108
109
  }
109
110
 
111
+ function isPanning() {
112
+ return cameraControl._isKeyDownForAction(cameraControl.MOUSE_PAN, keyDown);
113
+ }
114
+
115
+ function isRotating() {
116
+ return cameraControl._isKeyDownForAction(cameraControl.MOUSE_ROTATE, keyDown);
117
+ }
118
+
119
+
110
120
  canvas.addEventListener("mousedown", this._mouseDownHandler = (e) => {
111
121
 
112
122
  if (!(configs.active && configs.pointerEnabled)) {
@@ -120,12 +130,14 @@ class MousePanRotateDollyHandler {
120
130
  if (keyDown[scene.input.KEY_SHIFT] || configs.planView) {
121
131
 
122
132
  mouseDownLeft = true;
133
+ keyDown[scene.input.MOUSE_LEFT_BUTTON] = true;
123
134
 
124
135
  setMousedownState();
125
136
 
126
137
  } else {
127
138
 
128
139
  mouseDownLeft = true;
140
+ keyDown[scene.input.MOUSE_LEFT_BUTTON] = true;
129
141
 
130
142
  setMousedownState(false);
131
143
  }
@@ -135,6 +147,7 @@ class MousePanRotateDollyHandler {
135
147
  case 2: // Middle/both buttons
136
148
 
137
149
  mouseDownMiddle = true;
150
+ keyDown[scene.input.MOUSE_MIDDLE_BUTTON] = true;
138
151
 
139
152
  setMousedownState();
140
153
 
@@ -143,6 +156,7 @@ class MousePanRotateDollyHandler {
143
156
  case 3: // Right button
144
157
 
145
158
  mouseDownRight = true;
159
+ keyDown[scene.input.MOUSE_RIGHT_BUTTON] = true;
146
160
 
147
161
  if (configs.panRightClick) {
148
162
 
@@ -175,7 +189,8 @@ class MousePanRotateDollyHandler {
175
189
  const x = states.pointerCanvasPos[0];
176
190
  const y = states.pointerCanvasPos[1];
177
191
 
178
- const panning = keyDown[scene.input.KEY_SHIFT] || configs.planView || (!configs.panRightClick && mouseDownMiddle) || (configs.panRightClick && mouseDownRight);
192
+ const panning = isPanning();
193
+ const rotating = isRotating();
179
194
 
180
195
  const xDelta = document.pointerLockElement ? e.movementX : (x - lastX);
181
196
  const yDelta = document.pointerLockElement ? e.movementY : (y - lastY);
@@ -200,7 +215,7 @@ class MousePanRotateDollyHandler {
200
215
  updates.panDeltaY += 0.5 * camera.ortho.scale * (yDelta / canvasHeight);
201
216
  }
202
217
 
203
- } else if (mouseDownLeft && !mouseDownMiddle && !mouseDownRight) {
218
+ } else if (rotating) {
204
219
 
205
220
  if (!configs.planView) { // No rotating in plan-view mode
206
221
 
@@ -241,16 +256,25 @@ class MousePanRotateDollyHandler {
241
256
  mouseDownLeft = false;
242
257
  mouseDownMiddle = false;
243
258
  mouseDownRight = false;
259
+ keyDown[scene.input.MOUSE_LEFT_BUTTON] = false;
260
+ keyDown[scene.input.MOUSE_MIDDLE_BUTTON] = false;
261
+ keyDown[scene.input.MOUSE_RIGHT_BUTTON] = false;
244
262
  break;
245
263
  case 2: // Middle/both buttons
246
264
  mouseDownLeft = false;
247
265
  mouseDownMiddle = false;
248
266
  mouseDownRight = false;
267
+ keyDown[scene.input.MOUSE_LEFT_BUTTON] = false;
268
+ keyDown[scene.input.MOUSE_MIDDLE_BUTTON] = false;
269
+ keyDown[scene.input.MOUSE_RIGHT_BUTTON] = false;
249
270
  break;
250
271
  case 3: // Right button
251
272
  mouseDownLeft = false;
252
273
  mouseDownMiddle = false;
253
274
  mouseDownRight = false;
275
+ keyDown[scene.input.MOUSE_LEFT_BUTTON] = false;
276
+ keyDown[scene.input.MOUSE_MIDDLE_BUTTON] = false;
277
+ keyDown[scene.input.MOUSE_RIGHT_BUTTON] = false;
254
278
  break;
255
279
  default:
256
280
  break;
@@ -296,7 +320,7 @@ class MousePanRotateDollyHandler {
296
320
  let secsNowLast = null;
297
321
 
298
322
  canvas.addEventListener("wheel", this._mouseWheelHandler = (e) => {
299
- if (!(configs.active && configs.pointerEnabled && configs.zoomOnMouseWheel)) {
323
+ if (!(configs.active && configs.pointerEnabled && configs.zoomOnMouseWheel && cameraControl._isKeyDownForAction(cameraControl.MOUSE_DOLLY, keyDown))) {
300
324
  return;
301
325
  }
302
326
  const secsNow = performance.now() / 1000.0;
@@ -16,6 +16,7 @@ class MousePickHandler {
16
16
  this._clicks = 0;
17
17
  this._timeout = null;
18
18
  this._lastPickedEntityId = null;
19
+ this._lastClickedWorldPos = null;
19
20
 
20
21
  let leftDown = false;
21
22
  let rightDown = false;
@@ -165,11 +166,16 @@ class MousePickHandler {
165
166
  if (pickResult && pickResult.worldPos) {
166
167
  pivotController.setPivotPos(pickResult.worldPos);
167
168
  pivotController.startPivot();
169
+ this._lastClickedWorldPos = pickResult.worldPos.slice();
168
170
  } else {
169
171
  if (configs.smartPivot) {
170
172
  pivotController.setCanvasPivotPos(states.pointerCanvasPos);
171
173
  } else {
172
- pivotController.setPivotPos(scene.camera.look);
174
+ if (this._lastClickedWorldPos) {
175
+ pivotController.setPivotPos(this._lastClickedWorldPos);
176
+ } else {
177
+ pivotController.setPivotPos(scene.camera.look);
178
+ }
173
179
  }
174
180
  pivotController.startPivot();
175
181
  }
@@ -937,6 +937,30 @@ class Input extends Component {
937
937
  */
938
938
  this.KEY_SPACE = 32;
939
939
 
940
+ /**
941
+ * Code for the left mouse button.
942
+ * @property MOUSE_LEFT_BUTTON
943
+ * @final
944
+ * @type {Number}
945
+ */
946
+ this.MOUSE_LEFT_BUTTON = 260;
947
+
948
+ /**
949
+ * Code for the middle mouse button.
950
+ * @property MOUSE_MIDDLE_BUTTON
951
+ * @final
952
+ * @type {Number}
953
+ */
954
+ this.MOUSE_MIDDLE_BUTTON = 261;
955
+
956
+ /**
957
+ * Code for the right mouse button.
958
+ * @property MOUSE_RIGHT_BUTTON
959
+ * @final
960
+ * @type {Number}
961
+ */
962
+ this.MOUSE_RIGHT_BUTTON = 262;
963
+
940
964
  /**
941
965
  * The canvas element that mouse and keyboards are bound to.
942
966
  *
@@ -116,7 +116,7 @@ export declare class StoreyViewsPlugin extends Plugin {
116
116
  * @param {Number[]} worldPos 3D World-space position.
117
117
  * @returns {String} ID of the storey containing the position, or null if the position falls outside all the storeys.
118
118
  */
119
- getStoreyContainingWorldPos(worldPos: number[]): string;
119
+ getStoreyContainingWorldPos(worldPos: number[], modelId: null | string): string;
120
120
 
121
121
  /**
122
122
  * Gets the ID of the storey which's bounding box contains the y point of the world position
@@ -124,7 +124,7 @@ export declare class StoreyViewsPlugin extends Plugin {
124
124
  * @param {Number[]} worldPos 3D World-space position.
125
125
  * @returns {String} ID of the storey containing the position, or null if the position falls outside all the storeys.
126
126
  */
127
- getStoreyInVerticalRange(worldPos: number[]): string;
127
+ getStoreyInVerticalRange(worldPos: number[], modelId: null | string): string;
128
128
 
129
129
 
130
130
  /**
@@ -133,7 +133,7 @@ export declare class StoreyViewsPlugin extends Plugin {
133
133
  * @param {Number[]} worldPos 3D World-space position.
134
134
  * @returns {String} ID of the lowest/highest story or null.
135
135
  */
136
- isPositionAboveOrBelowBuilding(worldPos: number[]): string;
136
+ isPositionAboveOrBelowBuilding(worldPos: number[], modelId: null | string): string;
137
137
 
138
138
  /**
139
139
  * Converts a 3D World-space position to a 2D position within a StoreyMap image.