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