@xeokit/xeokit-sdk 2.4.2-beta-14 → 2.4.2-beta-15

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.
@@ -6653,6 +6653,8 @@ const kdTreeDimLength$1 = new Float32Array(3);
6653
6653
  * Automatically indexes a {@link Viewer}'s {@link Entity}s in a 3D k-d tree
6654
6654
  * to support fast collision detection with 3D World-space axis-aligned boundaries (AABBs) and frustums.
6655
6655
  *
6656
+ * See {@link MarqueePicker} for usage example.
6657
+ *
6656
6658
  * An ObjectsKdTree3 is configured with a Viewer, and will then automatically
6657
6659
  * keep itself populated with k-d nodes that contain the Viewer's Entitys.
6658
6660
  *
@@ -8581,6 +8583,87 @@ function frustumIntersectsAABB3(frustum, aabb) {
8581
8583
 
8582
8584
  /**
8583
8585
  * Picks a {@link Viewer}'s {@link Entity}s with a canvas-space 2D marquee box.
8586
+ *
8587
+ * [<img src="https://xeokit.github.io/xeokit-sdk/assets/images/MarqueeSelect.gif">](https://xeokit.github.io/xeokit-sdk/examples/picking/#marqueePick_select)
8588
+ *
8589
+ * * [[Example 1: Select Objects with Marquee](https://xeokit.github.io/xeokit-sdk/examples/picking/#marqueePick_select)]
8590
+ * * [[Example 2: View-Fit Objects with Marquee](https://xeokit.github.io/xeokit-sdk/examples/picking/#marqueePick_viewFit)]
8591
+ *
8592
+ * # Usage
8593
+ *
8594
+ * In the example below, we
8595
+ *
8596
+ * 1. Create a {@link Viewer}, arrange the {@link Camera}
8597
+ * 2. Use an {@link XKTLoaderPlugin} to load a BIM model,
8598
+ * 3. Create a {@link ObjectsKdTree3} to automatically index the `Viewer's` {@link Entity}s for fast spatial lookup,
8599
+ * 4. Create a `MarqueePicker` to pick {@link Entity}s in the {@link Viewer}, using the {@link ObjectsKdTree3} to accelerate picking
8600
+ * 5. Create a {@link MarqueePickerMouseControl} to perform the marquee-picking with the `MarqueePicker`, using mouse input to draw the marquee box on the `Viewer's` canvas.
8601
+ *
8602
+ * When the {@link MarqueePickerMouseControl} is active:
8603
+ *
8604
+ * * Long-click, drag and release on the canvas to define a marque box that picks {@link Entity}s.
8605
+ * * Drag left-to-right to pick {@link Entity}s that intersect the box.
8606
+ * * Drag right-to-left to pick {@link Entity}s that are fully inside the box.
8607
+ * * On release, the `MarqueePicker` will fire a "picked" event with IDs of the picked {@link Entity}s, if any.
8608
+ * * Handling that event, we mark the {@link Entity}s as selected.
8609
+ * * Hold down CTRL to multi-pick.
8610
+ *
8611
+ * ````javascript
8612
+ * import {
8613
+ * Viewer,
8614
+ * XKTLoaderPlugin,
8615
+ * ObjectsKdTree3,
8616
+ * MarqueePicker,
8617
+ * MarqueePickerMouseControl
8618
+ * } from "xeokit-sdk.es.js";
8619
+ *
8620
+ * // 1
8621
+ *
8622
+ * const viewer = new Viewer({
8623
+ * canvasId: "myCanvas"
8624
+ * });
8625
+ *
8626
+ * viewer.scene.camera.eye = [14.9, 14.3, 5.4];
8627
+ * viewer.scene.camera.look = [6.5, 8.3, -4.1];
8628
+ * viewer.scene.camera.up = [-0.28, 0.9, -0.3];
8629
+ *
8630
+ * // 2
8631
+ *
8632
+ * const xktLoader = new XKTLoaderPlugin(viewer);
8633
+ *
8634
+ * const sceneModel = xktLoader.load({
8635
+ * id: "myModel",
8636
+ * src: "../../assets/models/xkt/v8/ifc/HolterTower.ifc.xkt"
8637
+ * });
8638
+ *
8639
+ * // 3
8640
+ *
8641
+ * const objectsKdTree3 = new ObjectsKdTree3({viewer});
8642
+ *
8643
+ * // 4
8644
+ *
8645
+ * const marqueePicker = new MarqueePicker({viewer, objectsKdTree3});
8646
+ *
8647
+ * // 5
8648
+ *
8649
+ * const marqueePickerMouseControl = new MarqueePickerMouseControl({marqueePicker});
8650
+ *
8651
+ * marqueePicker.on("clear", () => {
8652
+ * viewer.scene.setObjectsSelected(viewer.scene.selectedObjectIds, false);
8653
+ * });
8654
+ *
8655
+ * marqueePicker.on("picked", (objectIds) => {
8656
+ * viewer.scene.setObjectsSelected(objectIds, true);
8657
+ * });
8658
+ *
8659
+ * marqueePickerMouseControl.setActive(true);
8660
+ * ````
8661
+ *
8662
+ * # Design Notes
8663
+ *
8664
+ * * The {@link ObjectsKdTree3} can be shared with any other components that want to use it to spatially search for {@link Entity}s.
8665
+ * * The {@link MarqueePickerMouseControl} can be replaced with other types of controllers (i.e. touch), or used alongside them.
8666
+ * * The `MarqueePicker` has no input handlers of its own, and provides an API through which to programmatically control marquee picking. By firing the "picked" events, `MarqueePicker` implements the *Blackboard Pattern*.
8584
8667
  */
8585
8668
  class MarqueePicker extends Component {
8586
8669
 
@@ -8850,6 +8933,8 @@ MarqueePicker.PICK_MODE_INSIDE = 1;
8850
8933
  /**
8851
8934
  * Controls a {@link MarqueePicker} with mouse input.
8852
8935
  *
8936
+ * See {@link MarqueePicker} for usage example.
8937
+ *
8853
8938
  * When the MarqueePickerMouseControl is active:
8854
8939
  *
8855
8940
  * * Long-click, drag and release on the canvas to define a marque box that picks {@link Entity}s.
@@ -20018,23 +20103,31 @@ class Input extends Component {
20018
20103
  }
20019
20104
  });
20020
20105
 
20106
+ const tickifedMouseMoveFn = this.scene.tickify(
20107
+ () => this.fire("mousemove", this.mouseCanvasPos, true)
20108
+ );
20109
+
20021
20110
  this.element.addEventListener("mousemove", this._mouseMoveListener = (e) => {
20022
20111
  if (!this.enabled) {
20023
20112
  return;
20024
20113
  }
20025
20114
  this._getMouseCanvasPos(e);
20026
- this.fire("mousemove", this.mouseCanvasPos, true);
20115
+ tickifedMouseMoveFn();
20027
20116
  if (this.mouseover) {
20028
20117
  e.preventDefault();
20029
20118
  }
20030
20119
  });
20031
20120
 
20121
+ const tickifiedMouseWheelFn = this.scene.tickify(
20122
+ (delta) => { this.fire("mousewheel", delta, true); }
20123
+ );
20124
+
20032
20125
  this.element.addEventListener("wheel", this._mouseWheelListener = (e, d) => {
20033
20126
  if (!this.enabled) {
20034
20127
  return;
20035
20128
  }
20036
20129
  const delta = Math.max(-1, Math.min(1, -e.deltaY * 40));
20037
- this.fire("mousewheel", delta, true);
20130
+ tickifiedMouseWheelFn(delta);
20038
20131
  }, {passive: true});
20039
20132
 
20040
20133
  // mouseclicked
@@ -27858,6 +27951,11 @@ class Scene extends Component {
27858
27951
  throw "Mandatory config expected: valid canvasId or canvasElement";
27859
27952
  }
27860
27953
 
27954
+ /**
27955
+ * @type {{[key: string]: {wrapperFunc: Function, tickSubId: string}}}
27956
+ */
27957
+ this._tickifiedFunctions = {};
27958
+
27861
27959
  const transparent = (!!cfg.transparent);
27862
27960
  const alphaDepthMask = (!!cfg.alphaDepthMask);
27863
27961
 
@@ -30020,6 +30118,63 @@ class Scene extends Component {
30020
30118
  return changed;
30021
30119
  }
30022
30120
 
30121
+ /**
30122
+ * This method will "tickify" the provided `cb` function.
30123
+ *
30124
+ * This means, the function will be wrapped so:
30125
+ *
30126
+ * - it runs time-aligned to scene ticks
30127
+ * - it runs maximum once per scene-tick
30128
+ *
30129
+ * @param {Function} cb The function to tickify
30130
+ * @returns {Function)}
30131
+ */
30132
+ tickify(cb) {
30133
+ const cbString = cb.toString();
30134
+
30135
+ /**
30136
+ * Check if the function is already tickified, and if so return the cached one.
30137
+ */
30138
+ if (cbString in this._tickifiedFunctions) {
30139
+ return this._tickifiedFunctions[cbString].wrapperFunc;
30140
+ }
30141
+
30142
+ let alreadyRun = 0;
30143
+ let needToRun = 0;
30144
+
30145
+ let lastArgs;
30146
+
30147
+ /**
30148
+ * The provided `cb` function is replaced with a "set-dirty" function
30149
+ *
30150
+ * @type {Function}
30151
+ */
30152
+ const wrapperFunc = function (...args) {
30153
+ lastArgs = args;
30154
+ needToRun++;
30155
+ };
30156
+
30157
+ /**
30158
+ * An each scene tick, if the "dirty-flag" is set, run the `cb` function.
30159
+ *
30160
+ * This will make it run time-aligned to the scene tick.
30161
+ */
30162
+ const tickSubId = this.on("tick", () => {
30163
+ const tmp = needToRun;
30164
+ if (tmp > alreadyRun) {
30165
+ alreadyRun = tmp;
30166
+ cb(...lastArgs);
30167
+ }
30168
+ });
30169
+
30170
+ /**
30171
+ * And, store the list of subscribers.
30172
+ */
30173
+ this._tickifiedFunctions[cbString] = { tickSubId, wrapperFunc };
30174
+
30175
+ return wrapperFunc;
30176
+ }
30177
+
30023
30178
  /**
30024
30179
  * Destroys this Scene.
30025
30180
  */
@@ -87287,29 +87442,12 @@ class PickController {
87287
87442
  this._lastPickedEntityId = null;
87288
87443
 
87289
87444
  this._needFireEvents = 0;
87290
-
87291
- this._needToUpdate = false;
87292
-
87293
- this._tickSub = this._scene.on("tick", () => {
87294
- this.runUpdate();
87295
- });
87296
- }
87297
-
87298
- update() {
87299
- this._needToUpdate = true;
87300
87445
  }
87301
87446
 
87302
87447
  /**
87303
87448
  * Immediately attempts a pick, if scheduled.
87304
87449
  */
87305
- runUpdate() {
87306
-
87307
- if (!this._needToUpdate) {
87308
- return;
87309
- }
87310
-
87311
- this._needToUpdate = false;
87312
-
87450
+ update() {
87313
87451
  if (!this._configs.pointerEnabled) {
87314
87452
  return;
87315
87453
  }
@@ -87484,10 +87622,6 @@ class PickController {
87484
87622
 
87485
87623
  this._needFireEvents = 0;
87486
87624
  }
87487
-
87488
- destroy() {
87489
- this._scene.off(this._tickSub);
87490
- }
87491
87625
  }
87492
87626
 
87493
87627
  /**
@@ -87982,75 +88116,78 @@ class MousePickHandler {
87982
88116
  }
87983
88117
  };
87984
88118
 
87985
- canvas.addEventListener("mousemove", this._canvasMouseMoveHandler = (e) => {
88119
+ const tickifiedMouseMoveFn = scene.tickify (
88120
+ this._canvasMouseMoveHandler = (e) => {
88121
+ if (!(configs.active && configs.pointerEnabled)) {
88122
+ return;
88123
+ }
87986
88124
 
87987
- if (!(configs.active && configs.pointerEnabled)) {
87988
- return;
87989
- }
88125
+ if (leftDown || rightDown) {
88126
+ return;
88127
+ }
87990
88128
 
87991
- if (leftDown || rightDown) {
87992
- return;
87993
- }
88129
+ const hoverSubs = cameraControl.hasSubs("hover");
88130
+ const hoverEnterSubs = cameraControl.hasSubs("hoverEnter");
88131
+ const hoverOutSubs = cameraControl.hasSubs("hoverOut");
88132
+ const hoverOffSubs = cameraControl.hasSubs("hoverOff");
88133
+ const hoverSurfaceSubs = cameraControl.hasSubs("hoverSurface");
88134
+ const hoverSnapOrSurfaceSubs = cameraControl.hasSubs("hoverSnapOrSurface");
87994
88135
 
87995
- const hoverSubs = cameraControl.hasSubs("hover");
87996
- const hoverEnterSubs = cameraControl.hasSubs("hoverEnter");
87997
- const hoverOutSubs = cameraControl.hasSubs("hoverOut");
87998
- const hoverOffSubs = cameraControl.hasSubs("hoverOff");
87999
- const hoverSurfaceSubs = cameraControl.hasSubs("hoverSurface");
88000
- const hoverSnapOrSurfaceSubs = cameraControl.hasSubs("hoverSnapOrSurface");
88136
+ if (hoverSubs || hoverEnterSubs || hoverOutSubs || hoverOffSubs || hoverSurfaceSubs || hoverSnapOrSurfaceSubs) {
88001
88137
 
88002
- if (hoverSubs || hoverEnterSubs || hoverOutSubs || hoverOffSubs || hoverSurfaceSubs || hoverSnapOrSurfaceSubs) {
88138
+ pickController.pickCursorPos = states.pointerCanvasPos;
88139
+ pickController.schedulePickEntity = true;
88140
+ pickController.schedulePickSurface = hoverSurfaceSubs;
88141
+ pickController.scheduleSnapOrPick = hoverSnapOrSurfaceSubs;
88003
88142
 
88004
- pickController.pickCursorPos = states.pointerCanvasPos;
88005
- pickController.schedulePickEntity = true;
88006
- pickController.schedulePickSurface = hoverSurfaceSubs;
88007
- pickController.scheduleSnapOrPick = hoverSnapOrSurfaceSubs;
88143
+ pickController.update();
88008
88144
 
88009
- pickController.update();
88145
+ if (pickController.pickResult) {
88010
88146
 
88011
- if (pickController.pickResult) {
88147
+ if (pickController.pickResult.entity) {
88148
+ const pickedEntityId = pickController.pickResult.entity.id;
88149
+
88150
+ if (this._lastPickedEntityId !== pickedEntityId) {
88012
88151
 
88013
- if (pickController.pickResult.entity) {
88014
- const pickedEntityId = pickController.pickResult.entity.id;
88152
+ if (this._lastPickedEntityId !== undefined) {
88015
88153
 
88016
- if (this._lastPickedEntityId !== pickedEntityId) {
88154
+ cameraControl.fire("hoverOut", { // Hovered off an entity
88155
+ entity: scene.objects[this._lastPickedEntityId]
88156
+ }, true);
88157
+ }
88017
88158
 
88018
- if (this._lastPickedEntityId !== undefined) {
88159
+ cameraControl.fire("hoverEnter", pickController.pickResult, true); // Hovering over a new entity
88019
88160
 
88020
- cameraControl.fire("hoverOut", { // Hovered off an entity
88021
- entity: scene.objects[this._lastPickedEntityId]
88022
- }, true);
88161
+ this._lastPickedEntityId = pickedEntityId;
88023
88162
  }
88163
+ }
88024
88164
 
88025
- cameraControl.fire("hoverEnter", pickController.pickResult, true); // Hovering over a new entity
88165
+ cameraControl.fire("hover", pickController.pickResult, true);
88026
88166
 
88027
- this._lastPickedEntityId = pickedEntityId;
88167
+ if (pickController.pickResult.worldPos || pickController.pickResult.snappedWorldPos) { // Hovering the surface of an entity
88168
+ cameraControl.fire("hoverSurface", pickController.pickResult, true);
88028
88169
  }
88029
- }
88030
88170
 
88031
- cameraControl.fire("hover", pickController.pickResult, true);
88171
+ } else {
88032
88172
 
88033
- if (pickController.pickResult.worldPos || pickController.pickResult.snappedWorldPos) { // Hovering the surface of an entity
88034
- cameraControl.fire("hoverSurface", pickController.pickResult, true);
88035
- }
88173
+ if (this._lastPickedEntityId !== undefined) {
88036
88174
 
88037
- } else {
88175
+ cameraControl.fire("hoverOut", { // Hovered off an entity
88176
+ entity: scene.objects[this._lastPickedEntityId]
88177
+ }, true);
88038
88178
 
88039
- if (this._lastPickedEntityId !== undefined) {
88179
+ this._lastPickedEntityId = undefined;
88180
+ }
88040
88181
 
88041
- cameraControl.fire("hoverOut", { // Hovered off an entity
88042
- entity: scene.objects[this._lastPickedEntityId]
88182
+ cameraControl.fire("hoverOff", { // Not hovering on any entity
88183
+ canvasPos: pickController.pickCursorPos
88043
88184
  }, true);
88044
-
88045
- this._lastPickedEntityId = undefined;
88046
88185
  }
88047
-
88048
- cameraControl.fire("hoverOff", { // Not hovering on any entity
88049
- canvasPos: pickController.pickCursorPos
88050
- }, true);
88051
88186
  }
88052
88187
  }
88053
- });
88188
+ );
88189
+
88190
+ canvas.addEventListener("mousemove", tickifiedMouseMoveFn);
88054
88191
 
88055
88192
  canvas.addEventListener('mousedown', this._canvasMouseDownHandler = (e) => {
88056
88193