@xeokit/xeokit-sdk 2.6.17 → 2.6.19

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 (37) hide show
  1. package/dist/xeokit-sdk.cjs.js +2345 -117
  2. package/dist/xeokit-sdk.es.js +2335 -118
  3. package/dist/xeokit-sdk.es5.js +558 -418
  4. package/dist/xeokit-sdk.min.cjs.js +4 -4
  5. package/dist/xeokit-sdk.min.es.js +5 -5
  6. package/dist/xeokit-sdk.min.es5.js +4 -4
  7. package/package.json +2 -2
  8. package/src/extras/PointerLens/PointerLens.js +2 -2
  9. package/src/plugins/AnnotationsPlugin/Annotation.js +1 -0
  10. package/src/plugins/CityJSONLoaderPlugin/CityJSONDefaultDataSource.js +15 -2
  11. package/src/plugins/DistanceMeasurementsPlugin/DistanceMeasurement.js +1 -1
  12. package/src/plugins/DotBIMLoaderPlugin/DotBIMDefaultDataSource.js +15 -2
  13. package/src/plugins/GLTFLoaderPlugin/GLTFDefaultDataSource.js +18 -5
  14. package/src/plugins/LASLoaderPlugin/LASDefaultDataSource.js +15 -1
  15. package/src/plugins/STLLoaderPlugin/STLDefaultDataSource.js +17 -0
  16. package/src/plugins/WebIFCLoaderPlugin/WebIFCDefaultDataSource.js +16 -1
  17. package/src/plugins/XKTLoaderPlugin/XKTDefaultDataSource.js +18 -4
  18. package/src/plugins/ZonesPlugin/index.js +2046 -0
  19. package/src/plugins/index.js +2 -1
  20. package/src/plugins/lib/html/Dot.js +19 -1
  21. package/src/viewer/Viewer.js +3 -1
  22. package/src/viewer/scene/CameraControl/lib/handlers/MousePickHandler.js +9 -0
  23. package/src/viewer/scene/geometry/builders/buildLineGeometry.js +267 -0
  24. package/src/viewer/scene/input/Input.js +41 -0
  25. package/src/viewer/scene/marker/Marker.js +1 -1
  26. package/src/viewer/scene/mesh/Mesh.js +5 -0
  27. package/src/viewer/scene/model/SceneModel.js +7 -1
  28. package/src/viewer/scene/scene/Scene.js +30 -4
  29. package/src/viewer/scene/sectionPlane/SectionPlane.js +3 -7
  30. package/src/viewer/scene/webgl/Renderer.js +84 -79
  31. package/src/viewer/scene/webgl/occlusion/OcclusionLayer.js +1 -1
  32. package/src/viewer/scene/webgl/occlusion/OcclusionTester.js +8 -4
  33. package/types/viewer/Viewer.d.ts +2 -0
  34. package/types/viewer/scene/geometry/builders/buildLineGeometry.d.ts +22 -0
  35. package/types/viewer/scene/geometry/builders/buildPolylineGeometry.d.ts +2 -2
  36. package/types/viewer/scene/scene/Scene.d.ts +11 -0
  37. package/dist/web-ifc.wasm +0 -0
@@ -1122,8 +1122,8 @@ class PointerLens {
1122
1122
  );
1123
1123
 
1124
1124
  const centerLensCanvas = [
1125
- (lensRect.left + lensRect.right) / 2,
1126
- (lensRect.top + lensRect.bottom) / 2
1125
+ (lensRect.left + lensRect.right) / 2 - canvasRect.left,
1126
+ (lensRect.top + lensRect.bottom) / 2 - canvasRect.top
1127
1127
  ];
1128
1128
 
1129
1129
  if (this._snappedCanvasPos) {
@@ -10763,7 +10763,7 @@ class Marker extends Component {
10763
10763
  this.scene.camera.off(this._onCameraProjMatrix);
10764
10764
  if (this._entity) {
10765
10765
  if (this._onEntityDestroyed !== null) {
10766
- this._entity.off(this._onEntityDestroyed);
10766
+ this._entity.model.off(this._onEntityDestroyed);
10767
10767
  }
10768
10768
  if (this._onEntityModelDestroyed !== null) {
10769
10769
  this._entity.model.off(this._onEntityModelDestroyed);
@@ -11163,7 +11163,25 @@ class Dot {
11163
11163
  }
11164
11164
 
11165
11165
  }
11166
-
11166
+
11167
+ if (cfg.onTouchstart) {
11168
+ dotClickable.addEventListener('touchstart', (event) => {
11169
+ cfg.onTouchstart(event, this);
11170
+ });
11171
+ }
11172
+
11173
+ if (cfg.onTouchmove) {
11174
+ dotClickable.addEventListener('touchmove', (event) => {
11175
+ cfg.onTouchmove(event, this);
11176
+ });
11177
+ }
11178
+
11179
+ if (cfg.onTouchend) {
11180
+ dotClickable.addEventListener('touchend', (event) => {
11181
+ cfg.onTouchend(event, this);
11182
+ });
11183
+ }
11184
+
11167
11185
  this.setPos(cfg.x || 0, cfg.y || 0);
11168
11186
  this.setFillColor(cfg.fillColor);
11169
11187
  this.setBorderColor(cfg.borderColor);
@@ -14431,6 +14449,7 @@ class Annotation extends Marker {
14431
14449
  if (this._marker) {
14432
14450
  if (!this._markerExternal) {
14433
14451
  this._marker.parentNode.removeChild(this._marker);
14452
+ this._marker = null;
14434
14453
  } else {
14435
14454
  this._marker.removeEventListener("click", this._onMouseClickedExternalMarker);
14436
14455
  this._marker.removeEventListener("mouseenter", this._onMouseEnterExternalMarker);
@@ -17059,7 +17078,7 @@ class OcclusionLayer {
17059
17078
  _buildVBOs() {
17060
17079
  if (this.positionsBuf) {
17061
17080
  if (this.lenPositionsBuf === this.positions.length) { // Just updating buffer elements, don't need to reallocate
17062
- this.positionsBuf.setData(this.positions); // Indices don't need updating
17081
+ this.positionsBuf.setData(new Float32Array(this.positions)); // Indices don't need updating
17063
17082
  return;
17064
17083
  }
17065
17084
  this.positionsBuf.destroy();
@@ -17148,7 +17167,6 @@ class OcclusionLayer {
17148
17167
 
17149
17168
  const MARKER_COLOR = math.vec3([1.0, 0.0, 0.0]);
17150
17169
  const POINT_SIZE = 20;
17151
- const MARKER_SPRITE_CLIPZ_OFFSET = -0.001; // Amount that we offset sprite clip Z coords to raise them from surfaces
17152
17170
 
17153
17171
  const tempVec3a$J = math.vec3();
17154
17172
 
@@ -17229,7 +17247,7 @@ class OcclusionTester {
17229
17247
  let newOcclusionLayer = this._occlusionLayers[originHash];
17230
17248
  if (!newOcclusionLayer) {
17231
17249
  newOcclusionLayer = new OcclusionLayer(this._scene, marker.origin);
17232
- this._occlusionLayers[originHash] = occlusionLayer;
17250
+ this._occlusionLayers[originHash] = newOcclusionLayer;
17233
17251
  this._occlusionLayersListDirty = true;
17234
17252
  }
17235
17253
  newOcclusionLayer.addMarker(marker);
@@ -17359,7 +17377,9 @@ class OcclusionTester {
17359
17377
  if (scene.logarithmicDepthBufferEnabled) {
17360
17378
  src.push("vFragDepth = 1.0 + clipPos.w;");
17361
17379
  } else {
17362
- src.push("clipPos.z += " + MARKER_SPRITE_CLIPZ_OFFSET + ";");
17380
+ if (scene.markerZOffset < 0.000) {
17381
+ src.push("clipPos.z += " + scene.markerZOffset + ";");
17382
+ }
17363
17383
  }
17364
17384
  src.push(" gl_Position = clipPos;");
17365
17385
  src.push("}");
@@ -17473,7 +17493,10 @@ class OcclusionTester {
17473
17493
  continue;
17474
17494
  }
17475
17495
 
17476
- const origin = occlusionLayer.origin;
17496
+ // The `origin` has been changed from `occlusionLayer.origin` to `[ 0, 0, 0 ]`
17497
+ // because OcclusionLayer markers' transformation is being applied through the
17498
+ // OcclusionLayer::positions array. See XEOK-33
17499
+ const origin = [ 0, 0, 0 ];
17477
17500
 
17478
17501
  gl.uniformMatrix4fv(this._uViewMatrix, false, createRTCViewMat(camera.viewMatrix, origin));
17479
17502
 
@@ -18712,6 +18735,9 @@ const Renderer$1 = function (scene, options) {
18712
18735
  let drawableTypeInfo = {};
18713
18736
  let drawables = {};
18714
18737
 
18738
+ let postSortDrawableList = [];
18739
+ let postCullDrawableList = [];
18740
+
18715
18741
  let drawableListDirty = true;
18716
18742
  let stateSortDirty = true;
18717
18743
  let imageDirty = true;
@@ -18937,28 +18963,38 @@ const Renderer$1 = function (scene, options) {
18937
18963
  const drawableInfo = drawableTypeInfo[type];
18938
18964
  if (drawableInfo.isStateSortable) {
18939
18965
  drawableInfo.drawableListPreCull.sort(drawableInfo.stateSortCompare);
18966
+
18967
+ drawableInfo.drawableList = drawableInfo.drawableListPreCull;
18940
18968
  }
18941
18969
  }
18942
18970
  }
18943
- }
18944
-
18945
- function cullDrawableList() {
18971
+ let lenDrawableList = 0;
18946
18972
  for (let type in drawableTypeInfo) {
18947
18973
  if (drawableTypeInfo.hasOwnProperty(type)) {
18948
18974
  const drawableInfo = drawableTypeInfo[type];
18949
18975
  const drawableListPreCull = drawableInfo.drawableListPreCull;
18950
- const drawableList = drawableInfo.drawableList;
18951
- let lenDrawableList = 0;
18952
18976
  for (let i = 0, len = drawableListPreCull.length; i < len; i++) {
18953
18977
  const drawable = drawableListPreCull[i];
18954
- drawable.rebuildRenderFlags();
18955
- if (!drawable.renderFlags.culled) {
18956
- drawableList[lenDrawableList++] = drawable;
18957
- }
18978
+ postSortDrawableList[lenDrawableList++] = drawable;
18958
18979
  }
18959
- drawableList.length = lenDrawableList;
18960
18980
  }
18961
18981
  }
18982
+ postSortDrawableList.length = lenDrawableList;
18983
+ postSortDrawableList.sort((a, b) => {
18984
+ return a.renderOrder - b.renderOrder;
18985
+ });
18986
+ }
18987
+
18988
+ function cullDrawableList() {
18989
+ let lenDrawableList = 0;
18990
+ for (let i = 0, len = postSortDrawableList.length; i < len; i++) {
18991
+ const drawable = postSortDrawableList[i];
18992
+ drawable.rebuildRenderFlags();
18993
+ if (!drawable.renderFlags.culled) {
18994
+ postCullDrawableList[lenDrawableList++] = drawable;
18995
+ }
18996
+ }
18997
+ postCullDrawableList.length = lenDrawableList;
18962
18998
  }
18963
18999
 
18964
19000
  function draw(params) {
@@ -19191,7 +19227,6 @@ const Renderer$1 = function (scene, options) {
19191
19227
  }
19192
19228
 
19193
19229
  let i;
19194
- let len;
19195
19230
  let drawable;
19196
19231
 
19197
19232
  const startTime = Date.now();
@@ -19224,93 +19259,85 @@ const Renderer$1 = function (scene, options) {
19224
19259
  // Render normal opaque solids, defer others to bins to render after
19225
19260
  //------------------------------------------------------------------------------------------------------
19226
19261
 
19227
- for (let type in drawableTypeInfo) {
19228
- if (drawableTypeInfo.hasOwnProperty(type)) {
19229
-
19230
- const drawableInfo = drawableTypeInfo[type];
19231
- const drawableList = drawableInfo.drawableList;
19232
-
19233
- for (i = 0, len = drawableList.length; i < len; i++) {
19262
+ for (let i = 0, len = postCullDrawableList.length; i < len; i++) {
19234
19263
 
19235
- drawable = drawableList[i];
19264
+ drawable = postCullDrawableList[i];
19236
19265
 
19237
- if (drawable.culled === true || drawable.visible === false) {
19238
- continue;
19239
- }
19266
+ if (drawable.culled === true || drawable.visible === false) {
19267
+ continue;
19268
+ }
19240
19269
 
19241
- const renderFlags = drawable.renderFlags;
19270
+ const renderFlags = drawable.renderFlags;
19242
19271
 
19243
- if (renderFlags.colorOpaque) {
19244
- if (saoEnabled && saoPossible && drawable.saoEnabled) {
19245
- normalDrawSAOBin[normalDrawSAOBinLen++] = drawable;
19246
- } else {
19247
- drawable.drawColorOpaque(frameCtx);
19248
- }
19249
- }
19272
+ if (renderFlags.colorOpaque) {
19273
+ if (saoEnabled && saoPossible && drawable.saoEnabled) {
19274
+ normalDrawSAOBin[normalDrawSAOBinLen++] = drawable;
19275
+ } else {
19276
+ drawable.drawColorOpaque(frameCtx);
19277
+ }
19278
+ }
19250
19279
 
19251
- if (transparentEnabled) {
19252
- if (renderFlags.colorTransparent) {
19253
- normalFillTransparentBin[normalFillTransparentBinLen++] = drawable;
19254
- }
19255
- }
19280
+ if (transparentEnabled) {
19281
+ if (renderFlags.colorTransparent) {
19282
+ normalFillTransparentBin[normalFillTransparentBinLen++] = drawable;
19283
+ }
19284
+ }
19256
19285
 
19257
- if (renderFlags.xrayedSilhouetteTransparent) {
19258
- xrayedFillTransparentBin[xrayedFillTransparentBinLen++] = drawable;
19259
- }
19286
+ if (renderFlags.xrayedSilhouetteTransparent) {
19287
+ xrayedFillTransparentBin[xrayedFillTransparentBinLen++] = drawable;
19288
+ }
19260
19289
 
19261
- if (renderFlags.xrayedSilhouetteOpaque) {
19262
- xrayedFillOpaqueBin[xrayedFillOpaqueBinLen++] = drawable;
19263
- }
19290
+ if (renderFlags.xrayedSilhouetteOpaque) {
19291
+ xrayedFillOpaqueBin[xrayedFillOpaqueBinLen++] = drawable;
19292
+ }
19264
19293
 
19265
- if (renderFlags.highlightedSilhouetteTransparent) {
19266
- highlightedFillTransparentBin[highlightedFillTransparentBinLen++] = drawable;
19267
- }
19294
+ if (renderFlags.highlightedSilhouetteTransparent) {
19295
+ highlightedFillTransparentBin[highlightedFillTransparentBinLen++] = drawable;
19296
+ }
19268
19297
 
19269
- if (renderFlags.highlightedSilhouetteOpaque) {
19270
- highlightedFillOpaqueBin[highlightedFillOpaqueBinLen++] = drawable;
19271
- }
19298
+ if (renderFlags.highlightedSilhouetteOpaque) {
19299
+ highlightedFillOpaqueBin[highlightedFillOpaqueBinLen++] = drawable;
19300
+ }
19272
19301
 
19273
- if (renderFlags.selectedSilhouetteTransparent) {
19274
- selectedFillTransparentBin[selectedFillTransparentBinLen++] = drawable;
19275
- }
19302
+ if (renderFlags.selectedSilhouetteTransparent) {
19303
+ selectedFillTransparentBin[selectedFillTransparentBinLen++] = drawable;
19304
+ }
19276
19305
 
19277
- if (renderFlags.selectedSilhouetteOpaque) {
19278
- selectedFillOpaqueBin[selectedFillOpaqueBinLen++] = drawable;
19279
- }
19306
+ if (renderFlags.selectedSilhouetteOpaque) {
19307
+ selectedFillOpaqueBin[selectedFillOpaqueBinLen++] = drawable;
19308
+ }
19280
19309
 
19281
- if (drawable.edges && edgesEnabled) {
19282
- if (renderFlags.edgesOpaque) {
19283
- normalEdgesOpaqueBin[normalEdgesOpaqueBinLen++] = drawable;
19284
- }
19310
+ if (drawable.edges && edgesEnabled) {
19311
+ if (renderFlags.edgesOpaque) {
19312
+ normalEdgesOpaqueBin[normalEdgesOpaqueBinLen++] = drawable;
19313
+ }
19285
19314
 
19286
- if (renderFlags.edgesTransparent) {
19287
- normalEdgesTransparentBin[normalEdgesTransparentBinLen++] = drawable;
19288
- }
19315
+ if (renderFlags.edgesTransparent) {
19316
+ normalEdgesTransparentBin[normalEdgesTransparentBinLen++] = drawable;
19317
+ }
19289
19318
 
19290
- if (renderFlags.selectedEdgesTransparent) {
19291
- selectedEdgesTransparentBin[selectedEdgesTransparentBinLen++] = drawable;
19292
- }
19319
+ if (renderFlags.selectedEdgesTransparent) {
19320
+ selectedEdgesTransparentBin[selectedEdgesTransparentBinLen++] = drawable;
19321
+ }
19293
19322
 
19294
- if (renderFlags.selectedEdgesOpaque) {
19295
- selectedEdgesOpaqueBin[selectedEdgesOpaqueBinLen++] = drawable;
19296
- }
19323
+ if (renderFlags.selectedEdgesOpaque) {
19324
+ selectedEdgesOpaqueBin[selectedEdgesOpaqueBinLen++] = drawable;
19325
+ }
19297
19326
 
19298
- if (renderFlags.xrayedEdgesTransparent) {
19299
- xrayEdgesTransparentBin[xrayEdgesTransparentBinLen++] = drawable;
19300
- }
19327
+ if (renderFlags.xrayedEdgesTransparent) {
19328
+ xrayEdgesTransparentBin[xrayEdgesTransparentBinLen++] = drawable;
19329
+ }
19301
19330
 
19302
- if (renderFlags.xrayedEdgesOpaque) {
19303
- xrayEdgesOpaqueBin[xrayEdgesOpaqueBinLen++] = drawable;
19304
- }
19331
+ if (renderFlags.xrayedEdgesOpaque) {
19332
+ xrayEdgesOpaqueBin[xrayEdgesOpaqueBinLen++] = drawable;
19333
+ }
19305
19334
 
19306
- if (renderFlags.highlightedEdgesTransparent) {
19307
- highlightedEdgesTransparentBin[highlightedEdgesTransparentBinLen++] = drawable;
19308
- }
19335
+ if (renderFlags.highlightedEdgesTransparent) {
19336
+ highlightedEdgesTransparentBin[highlightedEdgesTransparentBinLen++] = drawable;
19337
+ }
19309
19338
 
19310
- if (renderFlags.highlightedEdgesOpaque) {
19311
- highlightedEdgesOpaqueBin[highlightedEdgesOpaqueBinLen++] = drawable;
19312
- }
19313
- }
19339
+ if (renderFlags.highlightedEdgesOpaque) {
19340
+ highlightedEdgesOpaqueBin[highlightedEdgesOpaqueBinLen++] = drawable;
19314
19341
  }
19315
19342
  }
19316
19343
  }
@@ -19623,7 +19650,7 @@ const Renderer$1 = function (scene, options) {
19623
19650
  math.cross3Vec3(worldRayDir, randomVec3, up);
19624
19651
 
19625
19652
  pickViewMatrix = math.lookAtMat4v(worldRayOrigin, look, up, tempMat4b);
19626
- // pickProjMatrix = scene.camera.projMatrix;
19653
+ // pickProjMatrix = scene.camera.projMatrix;
19627
19654
  pickProjMatrix = scene.camera.ortho.matrix;
19628
19655
 
19629
19656
  pickResult.origin = worldRayOrigin;
@@ -21610,6 +21637,14 @@ class Input extends Component {
21610
21637
  }
21611
21638
  });
21612
21639
 
21640
+ this.element.addEventListener("contextmenu", this._contextmenuListener = (e) => {
21641
+ if (!this.enabled) {
21642
+ return;
21643
+ }
21644
+ this._getMouseCanvasPos(e);
21645
+ this.fire("contextmenu", this.mouseCanvasPos, true);
21646
+ });
21647
+
21613
21648
  const tickifiedMouseWheelFn = this.scene.tickify(
21614
21649
  (delta) => { this.fire("mousewheel", delta, true); }
21615
21650
  );
@@ -21643,6 +21678,24 @@ class Input extends Component {
21643
21678
  });
21644
21679
  }
21645
21680
 
21681
+ this.element.addEventListener("touchstart", this._touchstartListener = (e) => {
21682
+ if (!this.enabled) {
21683
+ return;
21684
+ }
21685
+ [...e.changedTouches].forEach(e => {
21686
+ this.fire("touchstart", [ e.identifier, this._getTouchCanvasPos(e) ], true);
21687
+ });
21688
+ });
21689
+
21690
+ this.element.addEventListener("touchend", this._touchendListener = (e) => {
21691
+ if (!this.enabled) {
21692
+ return;
21693
+ }
21694
+ [...e.changedTouches].forEach(e => {
21695
+ this.fire("touchend", [ e.identifier, this._getTouchCanvasPos(e) ], true);
21696
+ });
21697
+ });
21698
+
21646
21699
  this._eventsBound = true;
21647
21700
  }
21648
21701
 
@@ -21659,7 +21712,10 @@ class Input extends Component {
21659
21712
  document.removeEventListener("click", this._clickListener);
21660
21713
  document.removeEventListener("dblclick", this._dblClickListener);
21661
21714
  this.element.removeEventListener("mousemove", this._mouseMoveListener);
21715
+ this.element.removeEventListener("contextmenu", this._contextmenuListener);
21662
21716
  this.element.removeEventListener("wheel", this._mouseWheelListener);
21717
+ this.element.removeEventListener("touchstart", this._touchstartListener);
21718
+ this.element.removeEventListener("touchend", this._touchendListener);
21663
21719
  if (window.OrientationChangeEvent) {
21664
21720
  window.removeEventListener('orientationchange', this._orientationchangedListener);
21665
21721
  }
@@ -21672,6 +21728,18 @@ class Input extends Component {
21672
21728
  this._eventsBound = false;
21673
21729
  }
21674
21730
 
21731
+ _getTouchCanvasPos(event) {
21732
+ let element = event.target;
21733
+ let totalOffsetLeft = 0;
21734
+ let totalOffsetTop = 0;
21735
+ while (element.offsetParent) {
21736
+ totalOffsetLeft += element.offsetLeft;
21737
+ totalOffsetTop += element.offsetTop;
21738
+ element = element.offsetParent;
21739
+ }
21740
+ return [ event.pageX - totalOffsetLeft, event.pageY - totalOffsetTop ];
21741
+ }
21742
+
21675
21743
  _getMouseCanvasPos(event) {
21676
21744
  if (!event) {
21677
21745
  event = window.event;
@@ -30156,6 +30224,7 @@ class Scene extends Component {
30156
30224
  this._pbrEnabled = !!cfg.pbrEnabled;
30157
30225
  this._colorTextureEnabled = (cfg.colorTextureEnabled !== false);
30158
30226
  this._dtxEnabled = !!cfg.dtxEnabled;
30227
+ this._markerZOffset = cfg.markerZOffset;
30159
30228
 
30160
30229
  // Register Scene on xeokit
30161
30230
  // Do this BEFORE we add components below
@@ -30676,6 +30745,22 @@ class Scene extends Component {
30676
30745
  return this._colorTextureEnabled;
30677
30746
  }
30678
30747
 
30748
+ /**
30749
+ * Gets the Z value of offset for Marker's OcclusionTester.
30750
+ * The closest the value is to 0.000 the more precise OcclusionTester will be, but at the same time the less
30751
+ * precise it will behave for Markers that are located exactly on the Surface.
30752
+ *
30753
+ * Default is ````-0.001````.
30754
+ *
30755
+ * @returns {Number} Z offset for Marker
30756
+ */
30757
+ get markerZOffset() {
30758
+ if (this._markerZOffset == null) {
30759
+ return -0.001;
30760
+ }
30761
+ return this._markerZOffset;
30762
+ }
30763
+
30679
30764
  /**
30680
30765
  * Performs an occlusion test on all {@link Marker}s in this {@link Scene}.
30681
30766
  *
@@ -31520,9 +31605,9 @@ class Scene extends Component {
31520
31605
  * @param {Number[]} [params.matrix] 4x4 transformation matrix to define the World-space ray origin and direction, as an alternative to ````origin```` and ````direction````.
31521
31606
  * @param {String[]} [params.includeEntities] IDs of {@link Entity}s to restrict picking to. When given, ignores {@link Entity}s whose IDs are not in this list.
31522
31607
  * @param {String[]} [params.excludeEntities] IDs of {@link Entity}s to ignore. When given, will pick *through* these {@link Entity}s, as if they were not there.
31523
- * @param {Number} [params.snapRadius=30] The snap radius, in canvas pixels
31524
- * @param {boolean} [params.snapToVertex=true] Whether to snap to vertex.
31525
- * @param {boolean} [params.snapToEdge=true] Whether to snap to edge.
31608
+ * @param {Number} [params.snapRadius=30] The snap radius, in canvas pixels.
31609
+ * @param {boolean} [params.snapToVertex=true] Whether to snap to vertex. Only works when `canvasPos` given.
31610
+ * @param {boolean} [params.snapToEdge=true] Whether to snap to edge. Only works when `canvasPos` given.
31526
31611
  * @param {PickResult} [pickResult] Holds the results of the pick attempt. Will use the Scene's singleton PickResult if you don't supply your own.
31527
31612
  * @returns {PickResult} Holds results of the pick attempt, returned when an {@link Entity} is picked, else null. See method comments for description.
31528
31613
  */
@@ -31533,6 +31618,11 @@ class Scene extends Component {
31533
31618
  return null;
31534
31619
  }
31535
31620
 
31621
+ if ((params.snapToVertex || params.snapToEdge) && !params.canvasPos) {
31622
+ this.error("Scene.snapPick() `canvasPos` parameter expected for `snapToVertex:true` or `snapToEdge:true`");
31623
+ return;
31624
+ }
31625
+
31536
31626
  params = params || {};
31537
31627
 
31538
31628
  params.pickSurface = params.pickSurface || params.rayPick; // Backwards compatibility
@@ -31580,7 +31670,7 @@ class Scene extends Component {
31580
31670
 
31581
31671
  /**
31582
31672
  * @param {Object} params Picking parameters.
31583
- * @param {Number[]} [params.canvasPos] Canvas-space coordinates. When ray-picking, this will override the **origin** and ** direction** parameters and will cause the ray to be fired through the canvas at this position, directly along the negative View-space Z-axis.
31673
+ * @param {Number[]} params.canvasPos Canvas-space coordinates.
31584
31674
  * @param {Number} [params.snapRadius=30] The snap radius, in canvas pixels
31585
31675
  * @param {boolean} [params.snapToVertex=true] Whether to snap to vertex.
31586
31676
  * @param {boolean} [params.snapToEdge=true] Whether to snap to edge.
@@ -31591,6 +31681,10 @@ class Scene extends Component {
31591
31681
  this._warnSnapPickDeprecated = true;
31592
31682
  this.warn("Scene.snapPick() is deprecated since v2.4.2 - use Scene.pick() instead");
31593
31683
  }
31684
+ if (!params.canvasPos) {
31685
+ this.error("Scene.snapPick() canvasPos parameter expected");
31686
+ return;
31687
+ }
31594
31688
  return this._renderer.snapPick(
31595
31689
  params.canvasPos,
31596
31690
  params.snapRadius || 30,
@@ -37653,11 +37747,16 @@ class Mesh extends Component {
37653
37747
  * @param {EmphasisMaterial} [cfg.highlightMaterial] {@link EmphasisMaterial} to define the xrayed appearance for this Mesh. Inherits {@link Scene#highlightMaterial} by default.
37654
37748
  * @param {EmphasisMaterial} [cfg.selectedMaterial] {@link EmphasisMaterial} to define the selected appearance for this Mesh. Inherits {@link Scene#selectedMaterial} by default.
37655
37749
  * @param {EmphasisMaterial} [cfg.edgeMaterial] {@link EdgeMaterial} to define the appearance of enhanced edges for this Mesh. Inherits {@link Scene#edgeMaterial} by default.
37750
+ * @param {Number} [cfg.renderOrder=0] Specifies the rendering order for this mESH. This is used to control the order in which
37751
+ * mESHES are drawn when they have transparent objects, to give control over the order in which those objects are blended within the transparent
37752
+ * render pass.
37656
37753
  */
37657
37754
  constructor(owner, cfg = {}) {
37658
37755
 
37659
37756
  super(owner, cfg);
37660
37757
 
37758
+ this.renderOrder = cfg.renderOrder || 0;
37759
+
37661
37760
  /**
37662
37761
  * ID of the corresponding object within the originating system, if any.
37663
37762
  *
@@ -42182,6 +42281,7 @@ class SectionPlane extends Component {
42182
42281
  this._state.active = value !== false;
42183
42282
  this.glRedraw();
42184
42283
  this.fire("active", this._state.active);
42284
+ this.scene.fire("sectionPlaneUpdated", this);
42185
42285
  }
42186
42286
 
42187
42287
  /**
@@ -42262,13 +42362,8 @@ class SectionPlane extends Component {
42262
42362
  * Inverts the direction of {@link SectionPlane#dir}.
42263
42363
  */
42264
42364
  flipDir() {
42265
- const dir = this._state.dir;
42266
- dir[0] *= -1.0;
42267
- dir[1] *= -1.0;
42268
- dir[2] *= -1.0;
42269
- this._state.dist = (-math.dotVec3(this._state.pos, this._state.dir));
42270
- this.fire("dir", this._state.dir);
42271
- this.glRedraw();
42365
+ math.mulVec3Scalar(this._state.dir, -1.0, this._state.dir);
42366
+ this.dir = this._state.dir;
42272
42367
  }
42273
42368
 
42274
42369
  /**
@@ -82250,11 +82345,16 @@ class SceneModel extends Component {
82250
82345
  * represent the returned model. Set false to always use vertex buffer objects (VBOs). Note that DTX is only applicable
82251
82346
  * to non-textured triangle meshes, and that VBOs are always used for meshes that have textures, line segments, or point
82252
82347
  * primitives. Only works while {@link DTX#enabled} is also ````true````.
82348
+ * @param {Number} [cfg.renderOrder=0] Specifies the rendering order for this SceneModel. This is used to control the order in which
82349
+ * SceneModels are drawn when they have transparent objects, to give control over the order in which those objects are blended within the transparent
82350
+ * render pass.
82253
82351
  */
82254
82352
  constructor(owner, cfg = {}) {
82255
82353
 
82256
82354
  super(owner, cfg);
82257
82355
 
82356
+ this.renderOrder = cfg.renderOrder || 0;
82357
+
82258
82358
  this._dtxEnabled = this.scene.dtxEnabled && (cfg.dtxEnabled !== false);
82259
82359
 
82260
82360
  this._enableVertexWelding = false; // Not needed for most objects, and very expensive, so disabled
@@ -84321,6 +84421,7 @@ class SceneModel extends Component {
84321
84421
  _getVBOBatchingLayer(cfg) {
84322
84422
  const model = this;
84323
84423
  const origin = cfg.origin;
84424
+ cfg.renderLayer || 0;
84324
84425
  const positionsDecodeHash = cfg.positionsDecodeMatrix || cfg.positionsDecodeBoundary ?
84325
84426
  this._createHashStringFromMatrix(cfg.positionsDecodeMatrix || cfg.positionsDecodeBoundary)
84326
84427
  : "-";
@@ -84807,7 +84908,7 @@ class SceneModel extends Component {
84807
84908
  // -------------- RENDERING ---------------------------------------------------------------------------------------
84808
84909
 
84809
84910
  /** @private */
84810
- drawColorOpaque(frameCtx) {
84911
+ drawColorOpaque(frameCtx, layerList) {
84811
84912
  const renderFlags = this.renderFlags;
84812
84913
  for (let i = 0, len = renderFlags.visibleLayers.length; i < len; i++) {
84813
84914
  const layerIndex = renderFlags.visibleLayers[i];
@@ -86601,7 +86702,7 @@ class DistanceMeasurement extends Component {
86601
86702
 
86602
86703
  if (this._wpDirty) {
86603
86704
 
86604
- this._measurementOrientation = determineMeasurementOrientation(this._originWorld, this._targetWorld, 1);
86705
+ this._measurementOrientation = determineMeasurementOrientation(this._originWorld, this._targetWorld, 0);
86605
86706
  if(this._measurementOrientation === 'Vertical' && this.useRotationAdjustment){
86606
86707
  this._wp[0] = this._originWorld[0];
86607
86708
  this._wp[1] = this._originWorld[1];
@@ -89728,7 +89829,20 @@ class FastNavPlugin extends Plugin {
89728
89829
  */
89729
89830
  class GLTFDefaultDataSource {
89730
89831
 
89731
- constructor() {
89832
+ constructor(cfg = {}) {
89833
+ this.cacheBuster = (cfg.cacheBuster !== false);
89834
+ }
89835
+
89836
+ _cacheBusterURL(url) {
89837
+ if (!this.cacheBuster) {
89838
+ return url;
89839
+ }
89840
+ const timestamp = new Date().getTime();
89841
+ if (url.indexOf('?') > -1) {
89842
+ return url + '&_=' + timestamp;
89843
+ } else {
89844
+ return url + '?_=' + timestamp;
89845
+ }
89732
89846
  }
89733
89847
 
89734
89848
  /**
@@ -89739,7 +89853,7 @@ class GLTFDefaultDataSource {
89739
89853
  * @param {Function} error Fired on error while loading the metamodel JSON asset.
89740
89854
  */
89741
89855
  getMetaModel(metaModelSrc, ok, error) {
89742
- utils.loadJSON(metaModelSrc,
89856
+ utils.loadJSON(this._cacheBusterURL(metaModelSrc),
89743
89857
  (json) => {
89744
89858
  ok(json);
89745
89859
  },
@@ -89756,7 +89870,7 @@ class GLTFDefaultDataSource {
89756
89870
  * @param {Function} error Fired on error while loading the glTF JSON asset.
89757
89871
  */
89758
89872
  getGLTF(glTFSrc, ok, error) {
89759
- utils.loadArraybuffer(glTFSrc,
89873
+ utils.loadArraybuffer(this._cacheBusterURL(glTFSrc),
89760
89874
  (gltf) => {
89761
89875
  ok(gltf);
89762
89876
  },
@@ -89773,7 +89887,7 @@ class GLTFDefaultDataSource {
89773
89887
  * @param {Function} error Fired on error while loading the .glb asset.
89774
89888
  */
89775
89889
  getGLB(glbSrc, ok, error) {
89776
- utils.loadArraybuffer(glbSrc,
89890
+ utils.loadArraybuffer(this._cacheBusterURL(glbSrc),
89777
89891
  (arraybuffer) => {
89778
89892
  ok(arraybuffer);
89779
89893
  },
@@ -89794,7 +89908,7 @@ class GLTFDefaultDataSource {
89794
89908
  * @param {Function} error Fired on error while loading the glTF binary asset.
89795
89909
  */
89796
89910
  getArrayBuffer(glTFSrc, binarySrc, ok, error) {
89797
- loadArraybuffer(glTFSrc, binarySrc,
89911
+ loadArraybuffer(this._cacheBusterURL(glTFSrc), binarySrc,
89798
89912
  (arrayBuffer) => {
89799
89913
  ok(arrayBuffer);
89800
89914
  },
@@ -96110,6 +96224,15 @@ class MousePickHandler {
96110
96224
  return;
96111
96225
  }
96112
96226
 
96227
+ if (cameraControl.hasSubs("rayMove"))
96228
+ {
96229
+ const origin = math.vec3();
96230
+ const direction = math.vec3();
96231
+ // The origin from math.canvasPosToWorldRay is incorrect for a perspective camera, should be the same as scene.camera.eye
96232
+ math.canvasPosToWorldRay(scene.canvas.canvas, scene.camera.viewMatrix, scene.camera.projMatrix, states.pointerCanvasPos, origin, direction);
96233
+ cameraControl.fire("rayMove", { canvasPos: states.pointerCanvasPos, ray: { origin: origin, direction: direction, canvasPos: states.pointerCanvasPos } }, true);
96234
+ }
96235
+
96113
96236
  const hoverSubs = cameraControl.hasSubs("hover");
96114
96237
  const hoverEnterSubs = cameraControl.hasSubs("hoverEnter");
96115
96238
  const hoverOutSubs = cameraControl.hasSubs("hoverOut");
@@ -108202,6 +108325,7 @@ class Viewer {
108202
108325
  * store geometry on the GPU for triangle meshes that don't have textures. This gives a much lower memory footprint for these types of model element. This mode may not perform well on low-end GPUs that are optimized
108203
108326
  * to use textures to hold geometry data. Works great on most medium/high-end GPUs found in desktop computers, including the nVIDIA and Intel HD chipsets. Set this false to use the default vertex buffer object (VBO)
108204
108327
  * mode for storing geometry, which is the standard technique used in most graphics engines, and will work adequately on most low-end GPUs.
108328
+ * @param {Number} [cfg.markerZOffset=-0.001] The Z value of offset for Marker's OcclusionTester. The closest the value is to 0.000 the more precise OcclusionTester will be, but at the same time the less precise it will behave for Markers that are located exactly on the Surface.
108205
108329
  * @param {number} [cfg.numCachedSectionPlanes=0] Enhances the efficiency of SectionPlane creation by proactively allocating Viewer resources for a specified quantity
108206
108330
  * of SectionPlanes. Introducing this parameter streamlines the initial creation speed of SectionPlanes, particularly up to the designated quantity. This parameter internally
108207
108331
  * configures renderer logic for the specified number of SectionPlanes, eliminating the need for setting up logic with each SectionPlane creation and thereby enhancing
@@ -108265,6 +108389,7 @@ class Viewer {
108265
108389
  pbrEnabled: (!!cfg.pbrEnabled),
108266
108390
  colorTextureEnabled: (cfg.colorTextureEnabled !== false),
108267
108391
  dtxEnabled: (!!cfg.dtxEnabled),
108392
+ markerZOffset: cfg.markerZOffset,
108268
108393
  numCachedSectionPlanes: cfg.numCachedSectionPlanes
108269
108394
  });
108270
108395
 
@@ -123709,6 +123834,22 @@ class SkyboxesPlugin extends Plugin {
123709
123834
  */
123710
123835
  class STLDefaultDataSource {
123711
123836
 
123837
+ constructor(cfg = {}) {
123838
+ this.cacheBuster = (cfg.cacheBuster !== false);
123839
+ }
123840
+
123841
+ _cacheBusterURL(url) {
123842
+ if (!this.cacheBuster) {
123843
+ return url;
123844
+ }
123845
+ const timestamp = new Date().getTime();
123846
+ if (url.indexOf('?') > -1) {
123847
+ return url + '&_=' + timestamp;
123848
+ } else {
123849
+ return url + '?_=' + timestamp;
123850
+ }
123851
+ }
123852
+
123712
123853
  /**
123713
123854
  * Gets STL data.
123714
123855
  *
@@ -123717,6 +123858,7 @@ class STLDefaultDataSource {
123717
123858
  * @param {Function} error Fired on error while loading the STL file.
123718
123859
  */
123719
123860
  getSTL(src, ok, error) {
123861
+ src = this._cacheBusterURL(src);
123720
123862
  const request = new XMLHttpRequest();
123721
123863
  request.overrideMimeType("application/json");
123722
123864
  request.open('GET', src, true);
@@ -126339,7 +126481,20 @@ class ViewCullPlugin extends Plugin {
126339
126481
  */
126340
126482
  class XKTDefaultDataSource {
126341
126483
 
126342
- constructor() {
126484
+ constructor(cfg = {}) {
126485
+ this.cacheBuster = (cfg.cacheBuster !== false);
126486
+ }
126487
+
126488
+ _cacheBusterURL(url) {
126489
+ if (!this.cacheBuster) {
126490
+ return url;
126491
+ }
126492
+ const timestamp = new Date().getTime();
126493
+ if (url.indexOf('?') > -1) {
126494
+ return url + '&_=' + timestamp;
126495
+ } else {
126496
+ return url + '?_=' + timestamp;
126497
+ }
126343
126498
  }
126344
126499
 
126345
126500
  /**
@@ -126350,7 +126505,7 @@ class XKTDefaultDataSource {
126350
126505
  * @param {Function} error Fired on error while loading the manifest JSON asset.
126351
126506
  */
126352
126507
  getManifest(manifestSrc, ok, error) {
126353
- utils.loadJSON(manifestSrc,
126508
+ utils.loadJSON(this._cacheBusterURL(manifestSrc),
126354
126509
  (json) => {
126355
126510
  ok(json);
126356
126511
  },
@@ -126367,7 +126522,7 @@ class XKTDefaultDataSource {
126367
126522
  * @param {Function} error Fired on error while loading the metamodel JSON asset.
126368
126523
  */
126369
126524
  getMetaModel(metaModelSrc, ok, error) {
126370
- utils.loadJSON(metaModelSrc,
126525
+ utils.loadJSON(this._cacheBusterURL(metaModelSrc),
126371
126526
  (json) => {
126372
126527
  ok(json);
126373
126528
  },
@@ -126409,7 +126564,7 @@ class XKTDefaultDataSource {
126409
126564
  }
126410
126565
  } else {
126411
126566
  const request = new XMLHttpRequest();
126412
- request.open('GET', src, true);
126567
+ request.open('GET', this._cacheBusterURL(src), true);
126413
126568
  request.responseType = 'arraybuffer';
126414
126569
  request.onreadystatechange = function () {
126415
126570
  if (request.readyState === 4) {
@@ -133810,7 +133965,20 @@ class XML3DLoaderPlugin extends Plugin {
133810
133965
  */
133811
133966
  class WebIFCDefaultDataSource {
133812
133967
 
133813
- constructor() {
133968
+ constructor(cfg = {}) {
133969
+ this.cacheBuster = (cfg.cacheBuster !== false);
133970
+ }
133971
+
133972
+ _cacheBusterURL(url) {
133973
+ if (!this.cacheBuster) {
133974
+ return url;
133975
+ }
133976
+ const timestamp = new Date().getTime();
133977
+ if (url.indexOf('?') > -1) {
133978
+ return url + '&_=' + timestamp;
133979
+ } else {
133980
+ return url + '?_=' + timestamp;
133981
+ }
133814
133982
  }
133815
133983
 
133816
133984
  /**
@@ -133821,6 +133989,8 @@ class WebIFCDefaultDataSource {
133821
133989
  * @param {Function} error Callback fired on error.
133822
133990
  */
133823
133991
  getIFC(src, ok, error) {
133992
+ src = this._cacheBusterURL(src);
133993
+
133824
133994
  var defaultCallback = () => {
133825
133995
  };
133826
133996
  ok = ok || defaultCallback;
@@ -134818,7 +134988,20 @@ class WebIFCLoaderPlugin extends Plugin {
134818
134988
  */
134819
134989
  class LASDefaultDataSource {
134820
134990
 
134821
- constructor() {
134991
+ constructor(cfg = {}) {
134992
+ this.cacheBuster = (cfg.cacheBuster !== false);
134993
+ }
134994
+
134995
+ _cacheBusterURL(url) {
134996
+ if (!this.cacheBuster) {
134997
+ return url;
134998
+ }
134999
+ const timestamp = new Date().getTime();
135000
+ if (url.indexOf('?') > -1) {
135001
+ return url + '&_=' + timestamp;
135002
+ } else {
135003
+ return url + '?_=' + timestamp;
135004
+ }
134822
135005
  }
134823
135006
 
134824
135007
  /**
@@ -134829,6 +135012,7 @@ class LASDefaultDataSource {
134829
135012
  * @param {Function} error Callback fired on error.
134830
135013
  */
134831
135014
  getLAS(src, ok, error) {
135015
+ src = this._cacheBusterURL(src);
134832
135016
  var defaultCallback = () => {
134833
135017
  };
134834
135018
  ok = ok || defaultCallback;
@@ -135683,7 +135867,20 @@ function chunkArray(array, chunkSize) {
135683
135867
  */
135684
135868
  class CityJSONDefaultDataSource {
135685
135869
 
135686
- constructor() {
135870
+ constructor(cfg = {}) {
135871
+ this.cacheBuster = (cfg.cacheBuster !== false);
135872
+ }
135873
+
135874
+ _cacheBusterURL(url) {
135875
+ if (!this.cacheBuster) {
135876
+ return url;
135877
+ }
135878
+ const timestamp = new Date().getTime();
135879
+ if (url.indexOf('?') > -1) {
135880
+ return url + '&_=' + timestamp;
135881
+ } else {
135882
+ return url + '?_=' + timestamp;
135883
+ }
135687
135884
  }
135688
135885
 
135689
135886
  /**
@@ -135694,7 +135891,7 @@ class CityJSONDefaultDataSource {
135694
135891
  * @param {Function} error Callback fired on error.
135695
135892
  */
135696
135893
  getCityJSON(src, ok, error) {
135697
- utils.loadJSON(src,
135894
+ utils.loadJSON(this._cacheBusterURL(src),
135698
135895
  (json) => {
135699
135896
  ok(json);
135700
135897
  },
@@ -137126,7 +137323,20 @@ class CityJSONLoaderPlugin extends Plugin {
137126
137323
  */
137127
137324
  class DotBIMDefaultDataSource {
137128
137325
 
137129
- constructor() {
137326
+ constructor(cfg = {}) {
137327
+ this.cacheBuster = (cfg.cacheBuster !== false);
137328
+ }
137329
+
137330
+ _cacheBusterURL(url) {
137331
+ if (!this.cacheBuster) {
137332
+ return url;
137333
+ }
137334
+ const timestamp = new Date().getTime();
137335
+ if (url.indexOf('?') > -1) {
137336
+ return url + '&_=' + timestamp;
137337
+ } else {
137338
+ return url + '?_=' + timestamp;
137339
+ }
137130
137340
  }
137131
137341
 
137132
137342
  /**
@@ -137137,7 +137347,7 @@ class DotBIMDefaultDataSource {
137137
137347
  * @param {Function} error Fired on error while loading the .BIM JSON asset.
137138
137348
  */
137139
137349
  getDotBIM(dotBIMSrc, ok, error) {
137140
- utils.loadJSON(dotBIMSrc,
137350
+ utils.loadJSON(this._cacheBusterURL(dotBIMSrc),
137141
137351
  (json) => {
137142
137352
  ok(json);
137143
137353
  },
@@ -137699,6 +137909,2013 @@ class DotBIMLoaderPlugin extends Plugin {
137699
137909
  }
137700
137910
  }
137701
137911
 
137912
+ const hex2rgb = function(color) {
137913
+ const rgb = idx => parseInt(color.substr(idx + 1, 2), 16) / 255;
137914
+ return [ rgb(0), rgb(2), rgb(4) ];
137915
+ };
137916
+
137917
+ const transformToNode = function(from, to, vec) {
137918
+ const fromRec = from.getBoundingClientRect();
137919
+ const toRec = to.getBoundingClientRect();
137920
+ vec[0] += fromRec.left - toRec.left;
137921
+ vec[1] += fromRec.top - toRec.top;
137922
+ };
137923
+
137924
+ const triangulateEarClipping = function(planeCoords) {
137925
+
137926
+ const polygonVertices = [ ];
137927
+ for (let i = 0; i < planeCoords.length; ++i)
137928
+ polygonVertices.push(i);
137929
+
137930
+ const isCCW = (function() {
137931
+ const ba = math.vec2();
137932
+ const bc = math.vec2();
137933
+
137934
+ let anglesSum = 0;
137935
+
137936
+ for (let i = 0; i < polygonVertices.length; ++i)
137937
+ {
137938
+ const a = planeCoords[polygonVertices[i]];
137939
+ const b = planeCoords[polygonVertices[(i + 1) % polygonVertices.length]];
137940
+ const c = planeCoords[polygonVertices[(i + 2) % polygonVertices.length]];
137941
+
137942
+ math.subVec2(a, b, ba);
137943
+ math.subVec2(c, b, bc);
137944
+
137945
+ const theta = math.dotVec2(ba, bc) / Math.sqrt(math.sqLenVec2(ba) * math.sqLenVec2(bc));
137946
+ const angle = Math.acos(Math.max(-1, Math.min(theta, 1)));
137947
+ const convex = (ba[0] * bc[1] - ba[1] * bc[0]) >= 0;
137948
+ anglesSum += convex ? angle : (2 * Math.PI - angle);
137949
+ }
137950
+
137951
+ return anglesSum < (polygonVertices.length * Math.PI);
137952
+ })();
137953
+
137954
+ const pointInTriangle = (function() {
137955
+ const sign = (p1, p2, p3) => {
137956
+ return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
137957
+ };
137958
+
137959
+ return (pt, v1, v2, v3) => {
137960
+ const d1 = sign(pt, v1, v2);
137961
+ const d2 = sign(pt, v2, v3);
137962
+ const d3 = sign(pt, v3, v1);
137963
+
137964
+ const has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
137965
+ const has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
137966
+
137967
+ return !(has_neg && has_pos);
137968
+ };
137969
+ })();
137970
+
137971
+ const baseTriangles = [ ];
137972
+
137973
+ const vertices = (isCCW ? polygonVertices : polygonVertices.slice(0).reverse()).map(i => ({ idx: i }));
137974
+ vertices.forEach((v, i) => {
137975
+ v.prev = vertices[(i - 1 + vertices.length) % vertices.length];
137976
+ v.next = vertices[(i + 1) % vertices.length];
137977
+ });
137978
+
137979
+ const ba = math.vec2();
137980
+ const bc = math.vec2();
137981
+
137982
+ while (vertices.length > 2) {
137983
+ let earIdx = 0;
137984
+ while (true) {
137985
+ if (earIdx >= vertices.length)
137986
+ {
137987
+ throw `isCCW = ${isCCW}; earIdx = ${earIdx}; len = ${vertices.length}`;
137988
+ }
137989
+ const v = vertices[earIdx];
137990
+
137991
+ const a = planeCoords[v.prev.idx];
137992
+ const b = planeCoords[v.idx];
137993
+ const c = planeCoords[v.next.idx];
137994
+
137995
+ math.subVec2(a, b, ba);
137996
+ math.subVec2(c, b, bc);
137997
+
137998
+ if (((ba[0] * bc[1] - ba[1] * bc[0]) >= 0) // a convex vertex
137999
+ &&
138000
+ vertices.every( // no other vertices inside
138001
+ vv => ((vv === v)
138002
+ ||
138003
+ (vv === v.prev)
138004
+ ||
138005
+ (vv === v.next)
138006
+ ||
138007
+ !pointInTriangle(planeCoords[vv.idx], a, b, c))))
138008
+ break;
138009
+ ++earIdx;
138010
+ }
138011
+
138012
+ const ear = vertices[earIdx];
138013
+ vertices.splice(earIdx, 1);
138014
+
138015
+ baseTriangles.push([ ear.idx, ear.next.idx, ear.prev.idx ]);
138016
+
138017
+ const prev = ear.prev;
138018
+ prev.next = ear.next;
138019
+ const next = ear.next;
138020
+ next.prev = ear.prev;
138021
+ }
138022
+
138023
+ return [ planeCoords, baseTriangles ];
138024
+ };
138025
+
138026
+ const draggableDot3D = function(handleMouseEvents, handleTouchEvents, viewer, worldPos, color, ray2WorldPos, onStart, onMove, onEnd) {
138027
+ const scene = viewer.scene;
138028
+ const canvas = scene.canvas.canvas;
138029
+
138030
+ const marker = new Marker(scene, {});
138031
+
138032
+ const pickWorldPos = canvasPos => {
138033
+ const origin = math.vec3();
138034
+ const direction = math.vec3();
138035
+ math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, canvasPos, origin, direction);
138036
+ return ray2WorldPos(origin, direction);
138037
+ };
138038
+
138039
+ const onChange = event => {
138040
+ const canvasPos = math.vec2([ event.clientX, event.clientY ]);
138041
+ transformToNode(canvas.ownerDocument.body, canvas, canvasPos);
138042
+
138043
+ const worldPos = pickWorldPos(canvasPos);
138044
+ marker.worldPos = worldPos;
138045
+ updateDotPos();
138046
+ onMove(canvasPos, worldPos);
138047
+ };
138048
+
138049
+ let currentDrag = null;
138050
+
138051
+ const onDragMove = function(event) {
138052
+ const e = currentDrag.matchesEvent(event);
138053
+ if (e)
138054
+ {
138055
+ onChange(e);
138056
+ }
138057
+ };
138058
+
138059
+ const onDragEnd = function(event) {
138060
+ const e = currentDrag.matchesEvent(event);
138061
+ if (e)
138062
+ {
138063
+ dot.setOpacity(idleOpacity);
138064
+ currentDrag.cleanup();
138065
+ onChange(e);
138066
+ onEnd();
138067
+ }
138068
+ };
138069
+
138070
+ const startDrag = function(matchesEvent, cleanupHandlers) {
138071
+ if (currentDrag) {
138072
+ currentDrag.cleanup();
138073
+ }
138074
+
138075
+ dot.setOpacity(1.0);
138076
+ dot.setClickable(false);
138077
+ viewer.cameraControl.active = false;
138078
+
138079
+ currentDrag = {
138080
+ matchesEvent: matchesEvent,
138081
+ cleanup: function() {
138082
+ currentDrag = null;
138083
+ dot.setClickable(true);
138084
+ viewer.cameraControl.active = true;
138085
+ cleanupHandlers();
138086
+ }
138087
+ };
138088
+
138089
+ onStart();
138090
+ };
138091
+
138092
+ const dotCfg = { fillColor: color };
138093
+
138094
+ if (handleMouseEvents)
138095
+ {
138096
+ dotCfg.onMouseOver = () => (! currentDrag) && dot.setOpacity(1.0);
138097
+ dotCfg.onMouseLeave = () => (! currentDrag) && dot.setOpacity(idleOpacity);
138098
+ dotCfg.onMouseDown = event => {
138099
+ if (event.which === 1)
138100
+ {
138101
+ canvas.addEventListener("mousemove", onDragMove);
138102
+ canvas.addEventListener("mouseup", onDragEnd);
138103
+ startDrag(
138104
+ event => (event.which === 1) && event,
138105
+ () => {
138106
+ canvas.removeEventListener("mousemove", onDragMove);
138107
+ canvas.removeEventListener("mouseup", onDragEnd);
138108
+ });
138109
+ }
138110
+ };
138111
+ }
138112
+
138113
+ if (handleTouchEvents)
138114
+ {
138115
+ let touchStartId;
138116
+ dotCfg.onTouchstart = event => {
138117
+ event.preventDefault();
138118
+ if (event.touches.length === 1)
138119
+ {
138120
+ touchStartId = event.touches[0].identifier;
138121
+ startDrag(
138122
+ event => [...event.changedTouches].find(e => e.identifier === touchStartId),
138123
+ () => { touchStartId = null; });
138124
+ }
138125
+ };
138126
+ dotCfg.onTouchmove = event => {
138127
+ event.preventDefault();
138128
+ onDragMove(event);
138129
+ };
138130
+ dotCfg.onTouchend = event => {
138131
+ event.preventDefault();
138132
+ onDragEnd(event);
138133
+ };
138134
+ }
138135
+
138136
+ const dotParent = canvas.ownerDocument.body;
138137
+ const dot = new Dot(dotParent, dotCfg);
138138
+
138139
+ const idleOpacity = 0.5;
138140
+ dot.setOpacity(idleOpacity);
138141
+
138142
+ const updateDotPos = function() {
138143
+ const pos = marker.canvasPos.slice();
138144
+ transformToNode(canvas, dotParent, pos);
138145
+ dot.setPos(pos[0], pos[1]);
138146
+ };
138147
+
138148
+ marker.worldPos = worldPos;
138149
+ updateDotPos();
138150
+
138151
+ const onViewMatrix = scene.camera.on("viewMatrix", updateDotPos);
138152
+ const onProjMatrix = scene.camera.on("projMatrix", updateDotPos);
138153
+
138154
+ return {
138155
+ setActive: value => dot.setClickable(value),
138156
+ getWorldPos: () => marker.worldPos,
138157
+ setWorldPos: pos => { marker.worldPos = pos; updateDotPos(); },
138158
+ destroy: function() {
138159
+ currentDrag && currentDrag.cleanup();
138160
+ scene.camera.off(onViewMatrix);
138161
+ scene.camera.off(onProjMatrix);
138162
+ marker.destroy();
138163
+ dot.destroy();
138164
+ }
138165
+ };
138166
+ };
138167
+
138168
+ const marker3D = function(scene, color) {
138169
+ const canvas = scene.canvas.canvas;
138170
+
138171
+ const markerParent = canvas.parentNode;
138172
+ const markerDiv = document.createElement("div");
138173
+ markerParent.insertBefore(markerDiv, canvas);
138174
+
138175
+ let size = 5;
138176
+ markerDiv.style.background = color;
138177
+ markerDiv.style.border = "2px solid white";
138178
+ markerDiv.style.margin = "0 0";
138179
+ markerDiv.style.zIndex = "100";
138180
+ markerDiv.style.position = "absolute";
138181
+ markerDiv.style.pointerEvents = "none";
138182
+ markerDiv.style.display = "none";
138183
+
138184
+ const marker = new Marker(scene, {});
138185
+
138186
+ const px = x => x + "px";
138187
+ const update = function() {
138188
+ const pos = marker.canvasPos.slice();
138189
+ transformToNode(canvas, markerParent, pos);
138190
+ markerDiv.style.left = px(pos[0] - 3 - size / 2);
138191
+ markerDiv.style.top = px(pos[1] - 3 - size / 2);
138192
+ markerDiv.style.borderRadius = px(size * 2);
138193
+ markerDiv.style.width = px(size);
138194
+ markerDiv.style.height = px(size);
138195
+ };
138196
+ const onViewMatrix = scene.camera.on("viewMatrix", update);
138197
+ const onProjMatrix = scene.camera.on("projMatrix", update);
138198
+
138199
+ return {
138200
+ update: function(worldPos) {
138201
+ if (worldPos)
138202
+ {
138203
+ marker.worldPos = worldPos;
138204
+ update();
138205
+ }
138206
+ markerDiv.style.display = worldPos ? "" : "none";
138207
+ },
138208
+
138209
+ setHighlighted: function(h) {
138210
+ size = h ? 10 : 5;
138211
+ update();
138212
+ },
138213
+
138214
+ getCanvasPos: () => marker.canvasPos,
138215
+
138216
+ getWorldPos: () => marker.worldPos,
138217
+
138218
+ destroy: function() {
138219
+ markerDiv.parentNode.removeChild(markerDiv);
138220
+ scene.camera.off(onViewMatrix);
138221
+ scene.camera.off(onProjMatrix);
138222
+ marker.destroy();
138223
+ }
138224
+ };
138225
+ };
138226
+
138227
+ const wire3D = function(scene, color, startWorldPos) {
138228
+ const canvas = scene.canvas.canvas;
138229
+
138230
+ const startMarker = new Marker(scene, {});
138231
+ startMarker.worldPos = startWorldPos;
138232
+ const endMarker = new Marker(scene, {});
138233
+ const wireParent = canvas.ownerDocument.body;
138234
+ const wire = new Wire(wireParent, {
138235
+ color: color,
138236
+ thickness: 1,
138237
+ thicknessClickable: 6
138238
+ });
138239
+ wire.setVisible(false);
138240
+
138241
+ const updatePos = function() {
138242
+ const p0 = startMarker.canvasPos.slice();
138243
+ const p1 = endMarker.canvasPos.slice();
138244
+ transformToNode(canvas, wireParent, p0);
138245
+ transformToNode(canvas, wireParent, p1);
138246
+ wire.setStartAndEnd(p0[0], p0[1], p1[0], p1[1]);
138247
+ };
138248
+ const onViewMatrix = scene.camera.on("viewMatrix", updatePos);
138249
+ const onProjMatrix = scene.camera.on("projMatrix", updatePos);
138250
+
138251
+ return {
138252
+ update: function(endWorldPos) {
138253
+ if (endWorldPos)
138254
+ {
138255
+ endMarker.worldPos = endWorldPos;
138256
+ updatePos();
138257
+ }
138258
+ wire.setVisible(!!endWorldPos);
138259
+ },
138260
+
138261
+ destroy: function() {
138262
+ scene.camera.off(onViewMatrix);
138263
+ scene.camera.off(onProjMatrix);
138264
+ startMarker.destroy();
138265
+ endMarker.destroy();
138266
+ wire.destroy();
138267
+ }
138268
+ };
138269
+ };
138270
+
138271
+ const basePolygon3D = function(scene, color, alpha) {
138272
+ let mesh = null;
138273
+
138274
+ const updateBase = points => {
138275
+ if (points)
138276
+ {
138277
+ if (mesh)
138278
+ {
138279
+ mesh.destroy();
138280
+ }
138281
+
138282
+ try {
138283
+ const [ baseVertices, baseTriangles ] = triangulateEarClipping(points.map(p => [ p[0], p[2] ]));
138284
+
138285
+ const positions = [ ].concat(...baseVertices.map(p => [p[0], points[0][1], p[1]])); // To convert from Float64Array into an Array
138286
+ const ind = [ ].concat(...baseTriangles);
138287
+ mesh = new Mesh(scene, {
138288
+ pickable: false, // otherwise there's a WebGL error inside PickMeshRenderer.prototype.drawMesh
138289
+ geometry: new ReadableGeometry(
138290
+ scene,
138291
+ {
138292
+ positions: positions,
138293
+ indices: ind,
138294
+ normals: math.buildNormals(positions, ind)
138295
+ }),
138296
+ material: new PhongMaterial(scene, {
138297
+ alpha: (alpha !== undefined) ? alpha : 0.5,
138298
+ backfaces: true,
138299
+ diffuse: hex2rgb(color)
138300
+ })
138301
+ });
138302
+ } catch (e) {
138303
+ mesh = null;
138304
+ }
138305
+ }
138306
+
138307
+ if (mesh)
138308
+ {
138309
+ mesh.visible = !!points;
138310
+ }
138311
+ };
138312
+ updateBase(null);
138313
+
138314
+ return {
138315
+ updateBase: updateBase,
138316
+ destroy: () => mesh && mesh.destroy()
138317
+ };
138318
+ };
138319
+
138320
+ const startAAZoneCreateUI = function(scene, zoneAltitude, zoneHeight, zoneColor, zoneAlpha, pointerLens, zonesPlugin, select3dPoint, onZoneCreated) {
138321
+ const marker1 = marker3D(scene, zoneColor);
138322
+ const marker2 = marker3D(scene, zoneColor);
138323
+ const basePolygon = basePolygon3D(scene, zoneColor, zoneAlpha);
138324
+
138325
+ const updatePointerLens = (pointerLens
138326
+ ? function(canvasPos) {
138327
+ pointerLens.visible = !! canvasPos;
138328
+ if (canvasPos)
138329
+ {
138330
+ pointerLens.canvasPos = canvasPos;
138331
+ }
138332
+ }
138333
+ : () => { });
138334
+
138335
+ let deactivatePointSelection = select3dPoint(
138336
+ () => {
138337
+ updatePointerLens(null);
138338
+ marker1.update(null);
138339
+ },
138340
+ (canvasPos, worldPos) => {
138341
+ updatePointerLens(canvasPos);
138342
+ marker1.update(worldPos);
138343
+ },
138344
+ function(point1CanvasPos, point1WorldPos) {
138345
+ marker1.update(point1WorldPos);
138346
+
138347
+ deactivatePointSelection = select3dPoint(
138348
+ function() {
138349
+ updatePointerLens(null);
138350
+ marker2.update(null);
138351
+ basePolygon.updateBase(null);
138352
+ },
138353
+ function(canvasPos, point2WorldPos) {
138354
+ updatePointerLens(canvasPos);
138355
+ marker2.update(point2WorldPos);
138356
+
138357
+ if (math.distVec3(point1WorldPos, point2WorldPos) > 0.01)
138358
+ {
138359
+ const min = (idx) => Math.min(point1WorldPos[idx], point2WorldPos[idx]);
138360
+ const max = (idx) => Math.max(point1WorldPos[idx], point2WorldPos[idx]);
138361
+
138362
+ const xmin = min(0);
138363
+ const ymin = min(1);
138364
+ const zmin = min(2);
138365
+ const xmax = max(0);
138366
+ max(1);
138367
+ const zmax = max(2);
138368
+
138369
+ basePolygon.updateBase([ [ xmin, ymin, zmax ], [ xmax, ymin, zmax ],
138370
+ [ xmax, ymin, zmin ], [ xmin, ymin, zmin ] ]);
138371
+ }
138372
+ else
138373
+ basePolygon.updateBase(null);
138374
+ },
138375
+ function(point2CanvasPos, point2WorldPos) {
138376
+ // `marker2.update' makes sure marker's position has been updated from its default [0,0,0]
138377
+ // This works around an unidentified bug somewhere around OcclusionLayer, that causes error
138378
+ // [.WebGL-0x13400c47e00] GL_INVALID_OPERATION: Vertex buffer is not big enough for the draw call
138379
+ marker2.update(point2WorldPos);
138380
+
138381
+ marker1.destroy();
138382
+ marker2.destroy();
138383
+ basePolygon.destroy();
138384
+ updatePointerLens(null);
138385
+
138386
+ const min = (idx) => Math.min(point1WorldPos[idx], point2WorldPos[idx]);
138387
+ const max = (idx) => Math.max(point1WorldPos[idx], point2WorldPos[idx]);
138388
+
138389
+ const xmin = min(0);
138390
+ const zmin = min(2);
138391
+ const xmax = max(0);
138392
+ const zmax = max(2);
138393
+
138394
+ const zone = zonesPlugin.createZone(
138395
+ {
138396
+ id: math.createUUID(),
138397
+ geometry: {
138398
+ planeCoordinates: [
138399
+ [ xmin, zmax ],
138400
+ [ xmax, zmax ],
138401
+ [ xmax, zmin ],
138402
+ [ xmin, zmin ]
138403
+ ],
138404
+ altitude: zoneAltitude,
138405
+ height: zoneHeight
138406
+ },
138407
+ alpha: zoneAlpha,
138408
+ color: zoneColor
138409
+ });
138410
+
138411
+ onZoneCreated(zone);
138412
+ });
138413
+ });
138414
+
138415
+ return {
138416
+ deactivate: function() {
138417
+ deactivatePointSelection();
138418
+ marker1.destroy();
138419
+ marker2.destroy();
138420
+ basePolygon.destroy();
138421
+ updatePointerLens(null);
138422
+ }
138423
+ };
138424
+ };
138425
+
138426
+ const mousePointSelector = function(viewer, ray2WorldPos) {
138427
+ return function(onCancel, onChange, onCommit) {
138428
+ const scene = viewer.scene;
138429
+ const canvas = scene.canvas.canvas;
138430
+ const moveTolerance = 20;
138431
+
138432
+ const copyCanvasPos = (event, vec2) => {
138433
+ vec2[0] = event.clientX;
138434
+ vec2[1] = event.clientY;
138435
+ transformToNode(canvas.ownerDocument.body, canvas, vec2);
138436
+ return vec2;
138437
+ };
138438
+
138439
+ const pickWorldPos = canvasPos => {
138440
+ const origin = math.vec3();
138441
+ const direction = math.vec3();
138442
+ math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, canvasPos, origin, direction);
138443
+ return ray2WorldPos(origin, direction);
138444
+ };
138445
+
138446
+ let buttonDown = false;
138447
+ const resetAction = function() {
138448
+ buttonDown = false;
138449
+ };
138450
+
138451
+ const cleanup = function() {
138452
+ resetAction();
138453
+ canvas.removeEventListener("mousedown", onMouseDown);
138454
+ canvas.removeEventListener("mousemove", onMouseMove);
138455
+ viewer.cameraControl.off(onCameraControlRayMove);
138456
+ canvas.removeEventListener("mouseup", onMouseUp);
138457
+ };
138458
+
138459
+ const startCanvasPos = math.vec2();
138460
+ const onMouseDown = function(event) {
138461
+ if (event.which === 1)
138462
+ {
138463
+ copyCanvasPos(event, startCanvasPos);
138464
+ buttonDown = true;
138465
+ }
138466
+ };
138467
+ canvas.addEventListener("mousedown", onMouseDown);
138468
+
138469
+ const onMouseMove = function(event) {
138470
+ const canvasPos = copyCanvasPos(event, math.vec2());
138471
+ if (buttonDown && math.distVec2(startCanvasPos, canvasPos) > moveTolerance)
138472
+ {
138473
+ resetAction();
138474
+ onCancel();
138475
+ }
138476
+ };
138477
+ canvas.addEventListener("mousemove", onMouseMove);
138478
+
138479
+ const onCameraControlRayMove = viewer.cameraControl.on(
138480
+ "rayMove",
138481
+ event => {
138482
+ const canvasPos = event.canvasPos;
138483
+ onChange(canvasPos, pickWorldPos(canvasPos));
138484
+ });
138485
+
138486
+ const onMouseUp = function(event) {
138487
+ if ((event.which === 1) && buttonDown)
138488
+ {
138489
+ cleanup();
138490
+ const canvasPos = copyCanvasPos(event, math.vec2());
138491
+ onCommit(canvasPos, pickWorldPos(canvasPos));
138492
+ }
138493
+ };
138494
+ canvas.addEventListener("mouseup", onMouseUp);
138495
+
138496
+ return cleanup;
138497
+ };
138498
+ };
138499
+
138500
+ const touchPointSelector = function(viewer, pointerCircle, ray2WorldPos) {
138501
+ return function(onCancel, onChange, onCommit) {
138502
+ const scene = viewer.scene;
138503
+ const canvas = scene.canvas.canvas;
138504
+ const longTouchTimeoutMs = 300;
138505
+ const moveTolerance = 20;
138506
+
138507
+ const copyCanvasPos = (event, vec2) => {
138508
+ vec2[0] = event.clientX;
138509
+ vec2[1] = event.clientY;
138510
+ transformToNode(canvas.ownerDocument.body, canvas, vec2);
138511
+ return vec2;
138512
+ };
138513
+
138514
+ const pickWorldPos = canvasPos => {
138515
+ const origin = math.vec3();
138516
+ const direction = math.vec3();
138517
+ math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, canvasPos, origin, direction);
138518
+ return ray2WorldPos(origin, direction);
138519
+ };
138520
+
138521
+ let longTouchTimeout = null;
138522
+ const nop = () => { };
138523
+ let onSingleTouchMove = nop;
138524
+ let startTouchIdentifier;
138525
+
138526
+ const resetAction = function() {
138527
+ pointerCircle.stop();
138528
+ clearTimeout(longTouchTimeout);
138529
+ viewer.cameraControl.active = true;
138530
+ onSingleTouchMove = nop;
138531
+ startTouchIdentifier = null;
138532
+ };
138533
+
138534
+ const cleanup = function() {
138535
+ resetAction();
138536
+ canvas.removeEventListener("touchstart", onCanvasTouchStart);
138537
+ canvas.removeEventListener("touchmove", onCanvasTouchMove);
138538
+ canvas.removeEventListener("touchend", onCanvasTouchEnd);
138539
+ };
138540
+
138541
+ const onCanvasTouchStart = function(event) {
138542
+ const touches = event.touches;
138543
+
138544
+ if (touches.length !== 1)
138545
+ {
138546
+ resetAction();
138547
+ onCancel();
138548
+ }
138549
+ else
138550
+ {
138551
+ const startTouch = touches[0];
138552
+ const startCanvasPos = copyCanvasPos(startTouch, math.vec2());
138553
+
138554
+ const startWorldPos = pickWorldPos(startCanvasPos);
138555
+ if (startWorldPos)
138556
+ {
138557
+ startTouchIdentifier = startTouch.identifier;
138558
+
138559
+ onSingleTouchMove = canvasPos => {
138560
+ if (math.distVec2(startCanvasPos, canvasPos) > moveTolerance)
138561
+ {
138562
+ resetAction();
138563
+ }
138564
+ };
138565
+
138566
+ longTouchTimeout = setTimeout(
138567
+ function() {
138568
+ pointerCircle.start(startCanvasPos);
138569
+
138570
+ longTouchTimeout = setTimeout(
138571
+ function() {
138572
+ pointerCircle.stop();
138573
+
138574
+ viewer.cameraControl.active = false;
138575
+
138576
+ onSingleTouchMove = canvasPos => {
138577
+ onChange(canvasPos, pickWorldPos(canvasPos));
138578
+ };
138579
+
138580
+ onSingleTouchMove(startCanvasPos);
138581
+ },
138582
+ longTouchTimeoutMs);
138583
+ },
138584
+ 250);
138585
+ }
138586
+ }
138587
+ };
138588
+ canvas.addEventListener("touchstart", onCanvasTouchStart, {passive: true});
138589
+
138590
+ // canvas.addEventListener("touchcancel", e => console.log("touchcancel", e), {passive: true});
138591
+
138592
+ const onCanvasTouchMove = function(event) {
138593
+ const touch = [...event.changedTouches].find(e => e.identifier === startTouchIdentifier);
138594
+ if (touch)
138595
+ {
138596
+ onSingleTouchMove(copyCanvasPos(touch, math.vec2()));
138597
+ }
138598
+ };
138599
+ canvas.addEventListener("touchmove", onCanvasTouchMove, {passive: true});
138600
+
138601
+ const onCanvasTouchEnd = function(event) {
138602
+ const touch = [...event.changedTouches].find(e => e.identifier === startTouchIdentifier);
138603
+ if (touch)
138604
+ {
138605
+ cleanup();
138606
+ const canvasPos = copyCanvasPos(touch, math.vec2());
138607
+ onCommit(canvasPos, pickWorldPos(canvasPos));
138608
+ }
138609
+ };
138610
+ canvas.addEventListener("touchend", onCanvasTouchEnd, {passive: true});
138611
+
138612
+ return cleanup;
138613
+ };
138614
+ };
138615
+
138616
+ const planeIntersect = function(p0, n, origin, direction) {
138617
+ const t = - (math.dotVec3(origin, n) - p0) / math.dotVec3(direction, n);
138618
+ {
138619
+ const worldPos = math.vec3();
138620
+ math.mulVec3Scalar(direction, t, worldPos);
138621
+ math.addVec3(origin, worldPos, worldPos);
138622
+ return worldPos;
138623
+ }
138624
+ };
138625
+
138626
+ /**
138627
+ * @desc Renders a transparent box between two 3D points.
138628
+ *
138629
+ * See {@link ZonesPlugin} for more info.
138630
+ */
138631
+
138632
+ class Zone extends Component {
138633
+
138634
+ /**
138635
+ * @private
138636
+ */
138637
+ constructor(plugin, cfg = {}) {
138638
+
138639
+ super(plugin.viewer.scene, cfg);
138640
+
138641
+ /**
138642
+ * The {@link ZonesPlugin} that owns this Zone.
138643
+ * @type {ZonesPlugin}
138644
+ */
138645
+ this.plugin = plugin;
138646
+
138647
+ this._container = cfg.container;
138648
+ if (!this._container) {
138649
+ throw "config missing: container";
138650
+ }
138651
+
138652
+ this._eventSubs = {};
138653
+
138654
+ this.plugin.viewer.scene;
138655
+
138656
+ this._geometry = cfg.geometry;
138657
+
138658
+ cfg.onMouseOver ? (event) => {
138659
+ cfg.onMouseOver(event, this);
138660
+ this.plugin.viewer.scene.canvas.canvas.dispatchEvent(new MouseEvent('mouseover', event));
138661
+ } : null;
138662
+
138663
+ cfg.onMouseLeave ? (event) => {
138664
+ cfg.onMouseLeave(event, this);
138665
+ this.plugin.viewer.scene.canvas.canvas.dispatchEvent(new MouseEvent('mouseleave', event));
138666
+ } : null;
138667
+
138668
+ cfg.onContextMenu ? (event) => {
138669
+ cfg.onContextMenu(event, this);
138670
+ } : null;
138671
+
138672
+ this._alpha = (("alpha" in cfg) && (cfg.alpha !== undefined)) ? cfg.alpha : 0.5;
138673
+ this.color = cfg.color;
138674
+
138675
+ this._visible = true;
138676
+
138677
+ this._rebuildMesh();
138678
+ }
138679
+
138680
+ _rebuildMesh() {
138681
+ const scene = this.plugin.viewer.scene;
138682
+ const planeCoords = this._geometry.planeCoordinates.slice();
138683
+ const downward = this._geometry.height < 0;
138684
+ const altitude = this._geometry.altitude + (downward ? this._geometry.height : 0);
138685
+ const height = this._geometry.height * (downward ? -1 : 1);
138686
+
138687
+ const [ baseVertices, baseTriangles ] = triangulateEarClipping(planeCoords); // TODO: prevent crossing edges
138688
+
138689
+ const pos = [ ];
138690
+ const ind = [ ];
138691
+
138692
+
138693
+ const addPlane = (isCeiling) => {
138694
+ const baseIdx = pos.length;
138695
+
138696
+ for (let c of baseVertices) {
138697
+ pos.push([ c[0], altitude + (isCeiling ? height : 0), c[1] ]);
138698
+ }
138699
+
138700
+ for (let t of baseTriangles) {
138701
+ ind.push(...(isCeiling ? t : t.slice(0).reverse()).map(i => i + baseIdx));
138702
+ }
138703
+ };
138704
+ addPlane(false); // floor
138705
+ addPlane(true); // ceiling
138706
+
138707
+
138708
+ // sides
138709
+ for (let i = 0; i < baseVertices.length; ++i) {
138710
+ const a = baseVertices[i];
138711
+ const b = baseVertices[(i+1) % baseVertices.length];
138712
+ const f = altitude;
138713
+ const c = altitude + height;
138714
+
138715
+ const baseIdx = pos.length;
138716
+
138717
+ pos.push(
138718
+ [ a[0], f, a[1] ],
138719
+ [ b[0], f, b[1] ],
138720
+ [ b[0], c, b[1] ],
138721
+ [ a[0], c, a[1] ]
138722
+ );
138723
+
138724
+ ind.push(...[ 0, 1, 2, 0, 2, 3 ].map(i => i + baseIdx));
138725
+ }
138726
+
138727
+
138728
+ if (this._zoneMesh) {
138729
+ this._zoneMesh.destroy();
138730
+ }
138731
+
138732
+
138733
+ const positions = [].concat(...pos);
138734
+ this._zoneMesh = new Mesh(scene, {
138735
+ edges: this._edges,
138736
+ geometry: new ReadableGeometry(
138737
+ scene,
138738
+ {
138739
+ positions: positions,
138740
+ indices: ind,
138741
+ normals: math.buildNormals(positions, ind)
138742
+ }),
138743
+ material: new PhongMaterial(scene, {
138744
+ alpha: this._alpha,
138745
+ backfaces: true,
138746
+ diffuse: hex2rgb(this._color)
138747
+ }),
138748
+ visible: this._visible
138749
+ });
138750
+ this._zoneMesh.highlighted = this._highlighted;
138751
+
138752
+ this._zoneMesh.zone = this;
138753
+
138754
+
138755
+ const min = idx => Math.min(...pos.map(p => p[idx]));
138756
+ const max = idx => Math.max(...pos.map(p => p[idx]));
138757
+
138758
+ const xmin = min(0);
138759
+ const ymin = min(1);
138760
+ const zmin = min(2);
138761
+ const xmax = max(0);
138762
+ const ymax = max(1);
138763
+ const zmax = max(2);
138764
+
138765
+ this._center = math.vec3([ (xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2 ]);
138766
+ }
138767
+
138768
+ sectionedAverage(sectionPlanes) {
138769
+ const planeCoords = this._geometry.planeCoordinates.slice();
138770
+
138771
+ let faces = [ ];
138772
+ {
138773
+ const h = this._geometry.height;
138774
+ const a = this._geometry.altitude;
138775
+ const c = a + Math.max(0, h);
138776
+ const f = a + Math.min(0, h);
138777
+
138778
+ const addPlane = (isCeiling) => {
138779
+ const face = planeCoords.map(p => [ p[0], isCeiling ? c : f, p[1] ]);
138780
+ faces.push(isCeiling ? face : face.slice(0).reverse());
138781
+ };
138782
+ addPlane(true); // ceiling
138783
+ addPlane(false); // floor
138784
+
138785
+ // sides
138786
+ const p = (idx, y) => [ planeCoords[idx][0], y, planeCoords[idx][1] ];
138787
+ for (let i = 0; i < planeCoords.length; ++i)
138788
+ {
138789
+ const j = (i + 1) % planeCoords.length;
138790
+ faces.push([ p(i, f), p(j, f), p(j, c), p(i, c) ]);
138791
+ }
138792
+ }
138793
+
138794
+ for (const s of sectionPlanes)
138795
+ {
138796
+ const dir = s.dir;
138797
+ const dist = s.dist;
138798
+ const newFaces = [ ];
138799
+
138800
+ for (const face of faces)
138801
+ {
138802
+ const EPSILON = 1e-5;
138803
+ const COPLANAR = 0;
138804
+ const FRONT = 1;
138805
+ const BACK = 2;
138806
+ const SPANNING = 3;
138807
+
138808
+ // Classify each point as well as the entire polygon into one of the above four classes.
138809
+ let polygonType = 0;
138810
+ const types = [ ];
138811
+ for (let i = 0; i < face.length; i++) {
138812
+ const t = math.dotVec3(dir, face[i]) + dist;
138813
+ const type = (t < -EPSILON) ? BACK : (t > EPSILON) ? FRONT : COPLANAR;
138814
+ polygonType |= type;
138815
+ types.push(type);
138816
+ }
138817
+
138818
+ // Put the polygon in the correct list, splitting it when necessary.
138819
+ switch (polygonType) {
138820
+ case COPLANAR:
138821
+ newFaces.push(face);
138822
+ break;
138823
+ case FRONT:
138824
+ newFaces.push(face);
138825
+ break;
138826
+ case BACK:
138827
+ break;
138828
+ case SPANNING:
138829
+ const f = [ ];
138830
+ for (let i = 0; i < face.length; i++)
138831
+ {
138832
+ var j = (i + 1) % face.length;
138833
+ const ti = types[i];
138834
+ const tj = types[j];
138835
+ const vi = face[i];
138836
+ const vj = face[j];
138837
+ if (ti !== BACK)
138838
+ {
138839
+ f.push(vi);
138840
+ }
138841
+
138842
+ if ((ti | tj) === SPANNING)
138843
+ {
138844
+ const diff = math.vec3();
138845
+ math.subVec3(vj, vi, diff);
138846
+ const t = - (dist + math.dotVec3(dir, vi)) / math.dotVec3(dir, diff);
138847
+ const v = [0,0,0];
138848
+ math.lerpVec3(t, 0, 1, vi, vj, v);
138849
+ f.push(v);
138850
+ }
138851
+ }
138852
+ if (f.length >= 3)
138853
+ {
138854
+ newFaces.push(f);
138855
+ }
138856
+ break;
138857
+ }
138858
+ }
138859
+
138860
+ faces = newFaces;
138861
+ }
138862
+
138863
+ if (faces.length === 0)
138864
+ {
138865
+ return null;
138866
+ }
138867
+ else
138868
+ {
138869
+ const avg = math.vec3([ 0, 0, 0 ]);
138870
+ const unique = new Set();
138871
+
138872
+ for (const f of faces)
138873
+ {
138874
+ for (const p of f)
138875
+ {
138876
+ const id = p.map(x => x.toFixed(3)).join(":");
138877
+ if (! (unique.has(id)))
138878
+ {
138879
+ unique.add(id);
138880
+ math.addVec3(avg, p, avg);
138881
+ }
138882
+ }
138883
+ }
138884
+
138885
+ math.mulVec3Scalar(avg, 1 / unique.size, avg);
138886
+
138887
+ return avg;
138888
+ }
138889
+ }
138890
+
138891
+ get center() {
138892
+ return this._center;
138893
+ }
138894
+
138895
+ get altitude() {
138896
+ return this._geometry.altitude;
138897
+ }
138898
+
138899
+ set altitude(value) {
138900
+ this._geometry.altitude = value;
138901
+ this._rebuildMesh();
138902
+ }
138903
+
138904
+ get height() {
138905
+ return this._geometry.height;
138906
+ }
138907
+
138908
+ set height(value) {
138909
+ this._geometry.height = value;
138910
+ this._rebuildMesh();
138911
+ }
138912
+
138913
+ get highlighted() {
138914
+ return this._highlighted;
138915
+ }
138916
+
138917
+ set highlighted(value)
138918
+ {
138919
+ this._highlighted = value;
138920
+ if (this._zoneMesh) {
138921
+ this._zoneMesh.highlighted = value;
138922
+ }
138923
+ }
138924
+
138925
+ set color(value) {
138926
+ this._color = value;
138927
+ if (this._zoneMesh) {
138928
+ this._zoneMesh.material.diffuse = hex2rgb(this._color);
138929
+ }
138930
+ }
138931
+
138932
+ get color() {
138933
+ return this._color;
138934
+ }
138935
+
138936
+ set alpha(value) {
138937
+ this._alpha = value;
138938
+ if (this._zoneMesh) {
138939
+ this._zoneMesh.material.alpha = this._alpha;
138940
+ }
138941
+ }
138942
+
138943
+ get alpha() {
138944
+ return this._alpha;
138945
+ }
138946
+
138947
+ get edges() {
138948
+ return this._edges;
138949
+ }
138950
+
138951
+ set edges(edges) {
138952
+ this._edges = edges;
138953
+ if (this._zoneMesh) {
138954
+ this._zoneMesh.edges = this._edges;
138955
+ }
138956
+ }
138957
+
138958
+ /**
138959
+ * Sets whether this Zone is visible or not.
138960
+ *
138961
+ * @type {Boolean}
138962
+ */
138963
+ set visible(value) {
138964
+ this._visible = !!value;
138965
+ this._zoneMesh.visible = this._visible;
138966
+ this._needUpdate();
138967
+ }
138968
+
138969
+ /**
138970
+ * Gets whether this Zone is visible or not.
138971
+ *
138972
+ * @type {Boolean}
138973
+ */
138974
+ get visible() {
138975
+ return this._visible;
138976
+ }
138977
+
138978
+ /**
138979
+ * Gets this Zone as JSON.
138980
+ *
138981
+ * @returns {JSON}
138982
+ */
138983
+
138984
+ getJSON() {
138985
+ return {
138986
+ id: this.id,
138987
+ geometry: this._geometry,
138988
+ alpha: this._alpha,
138989
+ color: this._color
138990
+ };
138991
+ }
138992
+
138993
+ duplicate() {
138994
+ return this.plugin.createZone(
138995
+ {
138996
+ id: math.createUUID(),
138997
+ geometry: {
138998
+ planeCoordinates: this._geometry.planeCoordinates.map(c => c.slice()),
138999
+ altitude: this._geometry.altitude,
139000
+ height: this._geometry.height
139001
+ },
139002
+ alpha: this._alpha,
139003
+ color: this._color
139004
+ });
139005
+ }
139006
+
139007
+ /**
139008
+ * @private
139009
+ */
139010
+ destroy() {
139011
+ this._zoneMesh.destroy();
139012
+ super.destroy();
139013
+ }
139014
+ }
139015
+
139016
+ /**
139017
+ * Creates {@link Zone}s in a {@link ZonesPlugin} from mouse input.
139018
+ *
139019
+ * ## Usage
139020
+ *
139021
+ * [[Run example](/examples/measurement/#distance_createWithMouse_snapping)]
139022
+ *
139023
+ * ````javascript
139024
+ * import {Viewer, XKTLoaderPlugin, ZonesPlugin, ZonesMouseControl} from "xeokit-sdk.es.js";
139025
+ *
139026
+ * const viewer = new Viewer({
139027
+ * canvasId: "myCanvas",
139028
+ * });
139029
+ *
139030
+ * viewer.camera.eye = [-3.93, 2.85, 27.01];
139031
+ * viewer.camera.look = [4.40, 3.72, 8.89];
139032
+ * viewer.camera.up = [-0.01, 0.99, 0.039];
139033
+ *
139034
+ * const xktLoader = new XKTLoaderPlugin(viewer);
139035
+ *
139036
+ * const sceneModel = xktLoader.load({
139037
+ * id: "myModel",
139038
+ * src: "Duplex.xkt"
139039
+ * });
139040
+ *
139041
+ * const zones = new ZonesPlugin(viewer);
139042
+ *
139043
+ * const zonesControl = new ZonesMouseControl(Zones)
139044
+ * ````
139045
+ */
139046
+ class ZonesMouseControl extends Component {
139047
+
139048
+ /**
139049
+ * Creates a ZonesMouseControl bound to the given ZonesPlugin.
139050
+ *
139051
+ * @param {ZonesPlugin} zonesPlugin The ZonesPlugin to control.
139052
+ * @param [cfg] Configuration
139053
+ * @param {PointerLens} [cfg.pointerLens] A PointerLens to use to provide a magnified view of the cursor when snapping is enabled.
139054
+ */
139055
+ constructor(zonesPlugin, cfg = {}) {
139056
+ super(zonesPlugin.viewer.scene);
139057
+
139058
+ this.zonesPlugin = zonesPlugin;
139059
+ this.pointerLens = cfg.pointerLens;
139060
+ this._deactivate = null;
139061
+ }
139062
+
139063
+ get active() {
139064
+ return !! this._deactivate;
139065
+ }
139066
+
139067
+ activate(zoneAltitude, zoneHeight, zoneColor, zoneAlpha) {
139068
+
139069
+ if (this._deactivate) {
139070
+ return;
139071
+ }
139072
+
139073
+ if (typeof(zoneAltitude) === "object" && (zoneAltitude !== null)) {
139074
+ const params = zoneAltitude;
139075
+ const param = (name, defaultValue) => {
139076
+ if (name in params) {
139077
+ return params[name];
139078
+ } else if (defaultValue !== undefined) {
139079
+ return defaultValue;
139080
+ } else {
139081
+ throw "config missing: " + name;
139082
+ }
139083
+ };
139084
+
139085
+ zoneAltitude = param("altitude");
139086
+ zoneHeight = param("height");
139087
+ zoneColor = param("color", "#008000");
139088
+ zoneAlpha = param("alpha", 0.5);
139089
+ }
139090
+
139091
+ const zonesPlugin = this.zonesPlugin;
139092
+ const viewer = zonesPlugin.viewer;
139093
+ const scene = viewer.scene;
139094
+ const self = this;
139095
+
139096
+ const select3dPoint = mousePointSelector(
139097
+ viewer,
139098
+ function(origin, direction) {
139099
+ return planeIntersect(zoneAltitude, math.vec3([ 0, 1, 0 ]), origin, direction);
139100
+ });
139101
+
139102
+ (function rec() {
139103
+ self._deactivate = startAAZoneCreateUI(
139104
+ scene, zoneAltitude, zoneHeight, zoneColor, zoneAlpha, self.pointerLens, zonesPlugin, select3dPoint,
139105
+ zone => {
139106
+ let reactivate = true;
139107
+ self._deactivate = () => { reactivate = false; };
139108
+ self.fire("zoneEnd", zone);
139109
+ if (reactivate)
139110
+ {
139111
+ rec();
139112
+ }
139113
+ }).deactivate;
139114
+ })();
139115
+ }
139116
+
139117
+ deactivate() {
139118
+ if (this._deactivate)
139119
+ {
139120
+ this._deactivate();
139121
+ this._deactivate = null;
139122
+ }
139123
+ }
139124
+
139125
+ /**
139126
+ * Destroys this ZonesMouseControl.
139127
+ *
139128
+ * Destroys any {@link Zone} under construction by this ZonesMouseControl.
139129
+ */
139130
+ destroy() {
139131
+ this.deactivate();
139132
+ super.destroy();
139133
+ }
139134
+ }
139135
+
139136
+ /**
139137
+ * ZonesPlugin documentation to be added, mostly compatible with DistanceMeasurementsPlugin.
139138
+ */
139139
+ class ZonesPlugin extends Plugin {
139140
+
139141
+ /**
139142
+ * @constructor
139143
+ * @param {Viewer} viewer The Viewer.
139144
+ * @param {Object} [cfg] Plugin configuration.
139145
+ * @param {String} [cfg.id="Zones"] Optional ID for this plugin, so that we can find it within {@link Viewer#plugins}.
139146
+ * @param {HTMLElement} [cfg.container] Container DOM element for markers and labels. Defaults to ````document.body````.
139147
+ * @param {string} [cfg.defaultColor=#00BBFF] The default color of the length dots, wire and label.
139148
+ * @param {number} [cfg.zIndex] If set, the wires, dots and labels will have this zIndex (+1 for dots and +2 for labels).
139149
+ * @param {PointerCircle} [cfg.pointerLens] A PointerLens to help the user position the pointer. This can be shared with other plugins.
139150
+ */
139151
+ constructor(viewer, cfg = {}) {
139152
+
139153
+ super("Zones", viewer);
139154
+
139155
+ this._pointerLens = cfg.pointerLens;
139156
+
139157
+ this._container = cfg.container || document.body;
139158
+
139159
+ this._zones = [ ];
139160
+
139161
+ this.defaultColor = cfg.defaultColor !== undefined ? cfg.defaultColor : "#00BBFF";
139162
+ this.zIndex = cfg.zIndex || 10000;
139163
+
139164
+ this._onMouseOver = (event, zone) => {
139165
+ this.fire("mouseOver", {
139166
+ plugin: this,
139167
+ zone,
139168
+ event
139169
+ });
139170
+ };
139171
+
139172
+ this._onMouseLeave = (event, zone) => {
139173
+ this.fire("mouseLeave", {
139174
+ plugin: this,
139175
+ zone,
139176
+ event
139177
+ });
139178
+ };
139179
+
139180
+ this._onContextMenu = (event, zone) => {
139181
+ this.fire("contextMenu", {
139182
+ plugin: this,
139183
+ zone,
139184
+ event
139185
+ });
139186
+ };
139187
+ }
139188
+
139189
+ /**
139190
+ * Creates a {@link Zone}.
139191
+ *
139192
+ * The Zone is then registered by {@link Zone#id} in {@link ZonesPlugin#zones}.
139193
+ *
139194
+ * @param {Object} params {@link Zone} configuration.
139195
+ * @param {String} params.id Unique ID to assign to {@link Zone#id}. The Zone will be registered by this in {@link ZonesPlugin#zones} and {@link Scene.components}. Must be unique among all components in the {@link Viewer}.
139196
+ * @param {Number[]} params.origin.worldPos Origin World-space 3D position.
139197
+ * @param {Entity} params.origin.entity Origin Entity.
139198
+ * @param {Number[]} params.target.worldPos Target World-space 3D position.
139199
+ * @param {Entity} params.target.entity Target Entity.
139200
+ * @param {string} [params.color] The color of the length dot, wire and label.
139201
+ * @returns {Zone} The new {@link Zone}.
139202
+ */
139203
+ createZone(params = {}) {
139204
+ if (this.viewer.scene.components[params.id]) {
139205
+ this.error("Viewer scene component with this ID already exists: " + params.id);
139206
+ delete params.id;
139207
+ }
139208
+
139209
+ const zone = new Zone(this, {
139210
+ id: params.id,
139211
+ plugin: this,
139212
+ container: this._container,
139213
+ geometry: params.geometry,
139214
+ alpha: params.alpha,
139215
+ color: params.color,
139216
+ onMouseOver: this._onMouseOver,
139217
+ onMouseLeave: this._onMouseLeave,
139218
+ onContextMenu: this._onContextMenu
139219
+ });
139220
+ this._zones.push(zone);
139221
+ zone.on("destroyed", () => {
139222
+ const idx = this._zones.indexOf(zone);
139223
+ if (idx >= 0) {
139224
+ this._zones.splice(idx, 1);
139225
+ }
139226
+ });
139227
+ this.fire("zoneCreated", zone);
139228
+ return zone;
139229
+ }
139230
+
139231
+ /**
139232
+ * Gets the existing {@link Zone}s, each mapped to its {@link Zone#id}.
139233
+ *
139234
+ * @type {{String:Zone}}
139235
+ */
139236
+ get zones() {
139237
+ return this._zones;
139238
+ }
139239
+
139240
+ /**
139241
+ * Destroys this ZonesPlugin.
139242
+ *
139243
+ * Destroys all {@link Zone}s first.
139244
+ */
139245
+ destroy() {
139246
+ super.destroy();
139247
+ }
139248
+ }
139249
+
139250
+ class ZonesTouchControl extends Component {
139251
+
139252
+ constructor(zonesPlugin, cfg = {}) {
139253
+ super(zonesPlugin.viewer.scene);
139254
+
139255
+ this.zonesPlugin = zonesPlugin;
139256
+ this.pointerLens = cfg.pointerLens;
139257
+ this.pointerCircle = new PointerCircle(zonesPlugin.viewer);
139258
+ this._deactivate = null;
139259
+ }
139260
+
139261
+ get active() {
139262
+ return !! this._deactivate;
139263
+ }
139264
+
139265
+ activate(zoneAltitude, zoneHeight, zoneColor, zoneAlpha) {
139266
+
139267
+ if (typeof(zoneAltitude) === "object" && (zoneAltitude !== null)) {
139268
+ const params = zoneAltitude;
139269
+ const param = (name, defaultValue) => {
139270
+ if (name in params) {
139271
+ return params[name];
139272
+ } else if (defaultValue !== undefined) {
139273
+ return defaultValue;
139274
+ } else {
139275
+ throw "config missing: " + name;
139276
+ }
139277
+ };
139278
+
139279
+ zoneAltitude = param("altitude");
139280
+ zoneHeight = param("height");
139281
+ zoneColor = param("color", "#008000");
139282
+ zoneAlpha = param("alpha", 0.5);
139283
+ }
139284
+
139285
+ if (this._deactivate) {
139286
+ return;
139287
+ }
139288
+
139289
+ const zonesPlugin = this.zonesPlugin;
139290
+ const viewer = zonesPlugin.viewer;
139291
+ const scene = viewer.scene;
139292
+ const self = this;
139293
+
139294
+ const select3dPoint = touchPointSelector(
139295
+ viewer,
139296
+ this.pointerCircle,
139297
+ function(origin, direction) {
139298
+ return planeIntersect(zoneAltitude, math.vec3([ 0, 1, 0 ]), origin, direction);
139299
+ });
139300
+
139301
+ (function rec() {
139302
+ self._deactivate = startAAZoneCreateUI(
139303
+ scene, zoneAltitude, zoneHeight, zoneColor, zoneAlpha, self.pointerLens, zonesPlugin, select3dPoint,
139304
+ zone => {
139305
+ let reactivate = true;
139306
+ self._deactivate = () => { reactivate = false; };
139307
+ self.fire("zoneEnd", zone);
139308
+ if (reactivate)
139309
+ {
139310
+ rec();
139311
+ }
139312
+ }).deactivate;
139313
+ })();
139314
+ }
139315
+
139316
+ deactivate() {
139317
+ if (this._deactivate)
139318
+ {
139319
+ this._deactivate();
139320
+ this._deactivate = null;
139321
+ }
139322
+ }
139323
+
139324
+ destroy() {
139325
+ this.deactivate();
139326
+ super.destroy();
139327
+ }
139328
+ }
139329
+
139330
+ const startPolysurfaceZoneCreateUI = function(scene, zoneAltitude, zoneHeight, zoneColor, zoneAlpha, pointerLens, zonesPlugin, select3dPoint, onZoneCreated) {
139331
+ const updatePointerLens = (pointerLens
139332
+ ? function(canvasPos) {
139333
+ pointerLens.visible = !! canvasPos;
139334
+ if (canvasPos)
139335
+ {
139336
+ pointerLens.canvasPos = canvasPos;
139337
+ }
139338
+ }
139339
+ : () => { });
139340
+
139341
+ let deactivatePointSelection;
139342
+ const cleanups = [ () => updatePointerLens(null) ];
139343
+
139344
+ const basePolygon = basePolygon3D(scene, zoneColor, zoneAlpha);
139345
+ cleanups.push(() => basePolygon.destroy());
139346
+
139347
+ (function selectNextPoint(markers) {
139348
+ const marker = marker3D(scene, zoneColor);
139349
+ const wire = (markers.length > 0) && wire3D(scene, zoneColor, markers[markers.length - 1].getWorldPos());
139350
+
139351
+ cleanups.push(() => {
139352
+ marker.destroy();
139353
+ wire && wire.destroy();
139354
+ });
139355
+
139356
+ const firstMarker = (markers.length > 0) && markers[0];
139357
+ const getSnappedFirst = function(canvasPos) {
139358
+ const firstCanvasPos = firstMarker && firstMarker.getCanvasPos();
139359
+ const snapToFirst = firstCanvasPos && (math.distVec2(firstCanvasPos, canvasPos) < 10);
139360
+ return snapToFirst && { canvasPos: firstCanvasPos, worldPos: firstMarker.getWorldPos() };
139361
+ };
139362
+
139363
+ const lastSegmentIntersects = (function() {
139364
+ const onSegment = (p, q, r) => ((q[0] <= Math.max(p[0], r[0])) &&
139365
+ (q[0] >= Math.min(p[0], r[0])) &&
139366
+ (q[1] <= Math.max(p[1], r[1])) &&
139367
+ (q[1] >= Math.min(p[1], r[1])));
139368
+
139369
+ const orient = (p, q, r) => {
139370
+ const val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
139371
+ // collinear
139372
+ // clockwise
139373
+ // counterclockwise
139374
+ return ((val === 0) ? 0 : ((val > 0) ? 1 : 2));
139375
+ };
139376
+
139377
+ return function(pos2D, excludeFirstSegment) {
139378
+ const a = pos2D[pos2D.length - 2];
139379
+ const b = pos2D[pos2D.length - 1];
139380
+
139381
+ for (let i = excludeFirstSegment ? 1 : 0; i < pos2D.length - 2 - 1; ++i)
139382
+ {
139383
+ const c = pos2D[i];
139384
+ const d = pos2D[i + 1];
139385
+
139386
+ const o1 = orient(a, b, c);
139387
+ const o2 = orient(a, b, d);
139388
+ const o3 = orient(c, d, a);
139389
+ const o4 = orient(c, d, b);
139390
+
139391
+ if (((o1 !== o2) && (o3 !== o4)) || // General case
139392
+ ((o1 === 0) && onSegment(a, c, b)) || // a, b and c are collinear and c lies on segment ab
139393
+ ((o2 === 0) && onSegment(a, d, b)) || // a, b and d are collinear and d lies on segment ab
139394
+ ((o3 === 0) && onSegment(c, a, d)) || // c, d and a are collinear and a lies on segment cd
139395
+ ((o4 === 0) && onSegment(c, b, d))) // c, d and b are collinear and b lies on segment cd
139396
+ {
139397
+ return true;
139398
+ }
139399
+ }
139400
+
139401
+ return false;
139402
+ };
139403
+ })();
139404
+
139405
+ deactivatePointSelection = select3dPoint(
139406
+ () => {
139407
+ updatePointerLens(null);
139408
+ marker.update(null);
139409
+ wire && wire.update(null);
139410
+ basePolygon.updateBase((markers.length > 2) ? markers.map(m => m.getWorldPos()) : null);
139411
+ },
139412
+ (canvasPos, worldPos) => {
139413
+ const snappedFirst = (markers.length > 2) && getSnappedFirst(canvasPos);
139414
+ firstMarker && firstMarker.setHighlighted(!! snappedFirst);
139415
+ updatePointerLens(snappedFirst ? snappedFirst.canvasPos : canvasPos);
139416
+ marker.update((! snappedFirst) && worldPos);
139417
+ wire && wire.update(snappedFirst ? snappedFirst.worldPos : worldPos);
139418
+ if ((markers.length >= 2))
139419
+ {
139420
+ const pos = markers.map(m => m.getWorldPos()).concat(snappedFirst ? [] : [worldPos]);
139421
+ const inter = lastSegmentIntersects(pos.map(p => [ p[0], p[2] ]), snappedFirst);
139422
+ basePolygon.updateBase(inter ? null : pos);
139423
+ }
139424
+ else
139425
+ basePolygon.updateBase(null);
139426
+ },
139427
+ function(canvasPos, worldPos) {
139428
+ const snappedFirst = (markers.length > 2) && getSnappedFirst(canvasPos);
139429
+ const pos = markers.map(m => m.getWorldPos()).concat(snappedFirst ? [] : [worldPos]);
139430
+ basePolygon.updateBase(pos);
139431
+ const pos2D = pos.map(p => [ p[0], p[2] ]);
139432
+ if ((markers.length > 2) && lastSegmentIntersects(pos2D, snappedFirst))
139433
+ {
139434
+ cleanups.pop()();
139435
+ selectNextPoint(markers);
139436
+ }
139437
+ else if (snappedFirst)
139438
+ {
139439
+ // `marker2.update' makes sure marker's position has been updated from its default [0,0,0]
139440
+ // This works around an unidentified bug somewhere around OcclusionLayer, that causes error
139441
+ // [.WebGL-0x13400c47e00] GL_INVALID_OPERATION: Vertex buffer is not big enough for the draw call
139442
+ marker.update(worldPos);
139443
+
139444
+ cleanups.forEach(c => c());
139445
+ onZoneCreated(
139446
+ zonesPlugin.createZone(
139447
+ {
139448
+ id: math.createUUID(),
139449
+ geometry: {
139450
+ planeCoordinates: pos2D,
139451
+ altitude: zoneAltitude,
139452
+ height: zoneHeight
139453
+ },
139454
+ alpha: zoneAlpha,
139455
+ color: zoneColor
139456
+ }));
139457
+ }
139458
+ else
139459
+ {
139460
+ marker.update(worldPos);
139461
+ wire && wire.update(worldPos);
139462
+ selectNextPoint(markers.concat(marker));
139463
+ }
139464
+ });
139465
+ })([ ]);
139466
+
139467
+ return {
139468
+ closeSurface: function() {
139469
+ throw "TODO";
139470
+ },
139471
+ deactivate: function() {
139472
+ deactivatePointSelection();
139473
+ cleanups.forEach(c => c());
139474
+ }
139475
+ };
139476
+ };
139477
+
139478
+ class ZonesPolysurfaceMouseControl extends Component {
139479
+
139480
+ constructor(zonesPlugin, cfg = {}) {
139481
+ super(zonesPlugin.viewer.scene);
139482
+
139483
+ this.zonesPlugin = zonesPlugin;
139484
+ this.pointerLens = cfg.pointerLens;
139485
+ this._action = null;
139486
+ }
139487
+
139488
+ get active() {
139489
+ return !! this._action;
139490
+ }
139491
+
139492
+ activate(zoneAltitude, zoneHeight, zoneColor, zoneAlpha) {
139493
+
139494
+ if (typeof(zoneAltitude) === "object" && (zoneAltitude !== null)) {
139495
+ const params = zoneAltitude;
139496
+ const param = (name, defaultValue) => {
139497
+ if (name in params) {
139498
+ return params[name];
139499
+ } else if (defaultValue !== undefined) {
139500
+ return defaultValue;
139501
+ } else {
139502
+ throw "config missing: " + name;
139503
+ }
139504
+ };
139505
+
139506
+ zoneAltitude = param("altitude");
139507
+ zoneHeight = param("height");
139508
+ zoneColor = param("color", "#008000");
139509
+ zoneAlpha = param("alpha", 0.5);
139510
+ }
139511
+
139512
+ if (this._action) {
139513
+ return;
139514
+ }
139515
+
139516
+ const zonesPlugin = this.zonesPlugin;
139517
+ const viewer = zonesPlugin.viewer;
139518
+ const scene = viewer.scene;
139519
+ const self = this;
139520
+
139521
+ const select3dPoint = mousePointSelector(
139522
+ viewer,
139523
+ function(origin, direction) {
139524
+ return planeIntersect(zoneAltitude, math.vec3([ 0, 1, 0 ]), origin, direction);
139525
+ });
139526
+
139527
+ (function rec() {
139528
+ self._action = startPolysurfaceZoneCreateUI(
139529
+ scene, zoneAltitude, zoneHeight, zoneColor, zoneAlpha, self.pointerLens, zonesPlugin, select3dPoint,
139530
+ zone => {
139531
+ let reactivate = true;
139532
+ self._action = { deactivate: () => { reactivate = false; } };
139533
+ self.fire("zoneEnd", zone);
139534
+ if (reactivate)
139535
+ {
139536
+ rec();
139537
+ }
139538
+ });
139539
+ })();
139540
+ }
139541
+
139542
+ deactivate() {
139543
+ if (this._action)
139544
+ {
139545
+ this._action.deactivate();
139546
+ this._action = null;
139547
+ }
139548
+ }
139549
+
139550
+ destroy() {
139551
+ this.deactivate();
139552
+ super.destroy();
139553
+ }
139554
+ }
139555
+
139556
+ class ZonesPolysurfaceTouchControl extends Component {
139557
+
139558
+ constructor(zonesPlugin, cfg = {}) {
139559
+ super(zonesPlugin.viewer.scene);
139560
+
139561
+ this.zonesPlugin = zonesPlugin;
139562
+ this.pointerLens = cfg.pointerLens;
139563
+ this.pointerCircle = new PointerCircle(zonesPlugin.viewer);
139564
+ this._action = null;
139565
+ }
139566
+
139567
+ get active() {
139568
+ return !! this._action;
139569
+ }
139570
+
139571
+ activate(zoneAltitude, zoneHeight, zoneColor, zoneAlpha) {
139572
+
139573
+ if (typeof(zoneAltitude) === "object" && (zoneAltitude !== null)) {
139574
+ const params = zoneAltitude;
139575
+ const param = (name, defaultValue) => {
139576
+ if (name in params) {
139577
+ return params[name];
139578
+ } else if (defaultValue !== undefined) {
139579
+ return defaultValue;
139580
+ } else {
139581
+ throw "config missing: " + name;
139582
+ }
139583
+ };
139584
+
139585
+ zoneAltitude = param("altitude");
139586
+ zoneHeight = param("height");
139587
+ zoneColor = param("color", "#008000");
139588
+ zoneAlpha = param("alpha", 0.5);
139589
+ }
139590
+
139591
+ if (this._action) {
139592
+ return;
139593
+ }
139594
+
139595
+ const zonesPlugin = this.zonesPlugin;
139596
+ const viewer = zonesPlugin.viewer;
139597
+ const scene = viewer.scene;
139598
+ const self = this;
139599
+
139600
+ const select3dPoint = touchPointSelector(
139601
+ viewer,
139602
+ this.pointerCircle,
139603
+ function(origin, direction) {
139604
+ return planeIntersect(zoneAltitude, math.vec3([ 0, 1, 0 ]), origin, direction);
139605
+ });
139606
+
139607
+ (function rec() {
139608
+ self._action = startPolysurfaceZoneCreateUI(
139609
+ scene, zoneAltitude, zoneHeight, zoneColor, zoneAlpha, self.pointerLens, zonesPlugin, select3dPoint,
139610
+ zone => {
139611
+ let reactivate = true;
139612
+ self._action = { deactivate: () => { reactivate = false; } };
139613
+ self.fire("zoneEnd", zone);
139614
+ if (reactivate)
139615
+ {
139616
+ rec();
139617
+ }
139618
+ });
139619
+ })();
139620
+ }
139621
+
139622
+ deactivate() {
139623
+ if (this._action)
139624
+ {
139625
+ this._action.deactivate();
139626
+ this._action = null;
139627
+ }
139628
+ }
139629
+
139630
+ destroy() {
139631
+ this.deactivate();
139632
+ super.destroy();
139633
+ }
139634
+ }
139635
+
139636
+ class ZoneEditControl extends Component {
139637
+ constructor(zone, cfg, handleMouseEvents, handleTouchEvents) {
139638
+ super(zone.plugin.viewer.scene);
139639
+ const self = this;
139640
+
139641
+ const altitude = zone._geometry.altitude;
139642
+ const pointerLens = cfg && cfg.pointerLens;
139643
+ const updatePointerLens = (pointerLens
139644
+ ? function(canvasPos) {
139645
+ pointerLens.visible = !! canvasPos;
139646
+ if (canvasPos)
139647
+ {
139648
+ pointerLens.canvasPos = canvasPos;
139649
+ }
139650
+ }
139651
+ : () => { });
139652
+
139653
+ const dots = zone._geometry.planeCoordinates.map(planeCoord => {
139654
+ let initWorldPos, initPlaneCoord;
139655
+ const setPlaneCoord = function(coord) {
139656
+ planeCoord[0] = coord[0];
139657
+ planeCoord[1] = coord[1];
139658
+ try {
139659
+ zone._rebuildMesh();
139660
+ } catch (e) {
139661
+ if (zone._zoneMesh) {
139662
+ zone._zoneMesh.destroy();
139663
+ zone._zoneMesh = null;
139664
+ }
139665
+ }
139666
+ };
139667
+
139668
+ const dot = draggableDot3D(
139669
+ handleMouseEvents,
139670
+ handleTouchEvents,
139671
+ zone.plugin.viewer,
139672
+ math.vec3([ planeCoord[0], altitude, planeCoord[1] ]),
139673
+ zone._color,
139674
+ (orig, dir) => planeIntersect(altitude, math.vec3([ 0, 1, 0 ]), orig, dir),
139675
+ () => {
139676
+ initWorldPos = dot.getWorldPos().slice();
139677
+ initPlaneCoord = planeCoord.slice();
139678
+ set_other_dots_active(false, dot);
139679
+ },
139680
+ (canvasPos, worldPos) => {
139681
+ updatePointerLens(canvasPos);
139682
+ setPlaneCoord([ worldPos[0], worldPos[2] ]);
139683
+ },
139684
+ () => {
139685
+ if (zone._zoneMesh)
139686
+ {
139687
+ self.fire("edited");
139688
+ }
139689
+ else
139690
+ {
139691
+ dot.setWorldPos(initWorldPos);
139692
+ setPlaneCoord(initPlaneCoord);
139693
+ }
139694
+ updatePointerLens(null);
139695
+ set_other_dots_active(true, dot);
139696
+ });
139697
+ return dot;
139698
+ });
139699
+ const set_other_dots_active = (active, dot) => dots.forEach(d => (d !== dot) && d.setActive(active));
139700
+ set_other_dots_active(true);
139701
+
139702
+ const cleanup = function() {
139703
+ dots.forEach(m => m.destroy());
139704
+ updatePointerLens(null);
139705
+ };
139706
+
139707
+ const destroyCb = zone.on("destroyed", cleanup);
139708
+
139709
+ this._deactivate = function() {
139710
+ zone.off("destroyed", destroyCb);
139711
+ cleanup();
139712
+ };
139713
+ }
139714
+
139715
+ deactivate() {
139716
+ this._deactivate();
139717
+ super.destroy();
139718
+ }
139719
+ }
139720
+
139721
+ class ZoneEditMouseControl extends ZoneEditControl {
139722
+ constructor(zone, cfg) {
139723
+ super(zone, cfg, true, false);
139724
+ }
139725
+ }
139726
+
139727
+ class ZoneEditTouchControl extends ZoneEditControl {
139728
+ constructor(zone, cfg) {
139729
+ super(zone, cfg, false, true);
139730
+ }
139731
+ }
139732
+
139733
+
139734
+ class ZoneTranslateControl extends Component {
139735
+ constructor(zone, cfg, handleMouseEvents, handleTouchEvents) {
139736
+ const viewer = zone.plugin.viewer;
139737
+ const scene = viewer.scene;
139738
+ const canvas = scene.canvas.canvas;
139739
+
139740
+ super(scene);
139741
+ const self = this;
139742
+
139743
+ const altitude = zone._geometry.altitude;
139744
+ const pointerLens = cfg && cfg.pointerLens;
139745
+ const updatePointerLens = (pointerLens
139746
+ ? function(canvasPos) {
139747
+ pointerLens.visible = !! canvasPos;
139748
+ if (canvasPos)
139749
+ {
139750
+ pointerLens.canvasPos = canvasPos;
139751
+ }
139752
+ }
139753
+ : () => { });
139754
+
139755
+ const ray2WorldPos = (orig, dir) => planeIntersect(altitude, math.vec3([ 0, 1, 0 ]), orig, dir);
139756
+
139757
+ const pickWorldPos = canvasPos => {
139758
+ const origin = math.vec3();
139759
+ const direction = math.vec3();
139760
+ math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, canvasPos, origin, direction);
139761
+ return ray2WorldPos(origin, direction);
139762
+ };
139763
+
139764
+ const copyCanvasPos = (event, vec2) => {
139765
+ vec2[0] = event.clientX;
139766
+ vec2[1] = event.clientY;
139767
+ transformToNode(canvas.ownerDocument.body, canvas, vec2);
139768
+ return vec2;
139769
+ };
139770
+
139771
+ const canvasHandle = function(type, cb) {
139772
+ const callback = event => {
139773
+ event.preventDefault();
139774
+ cb(event);
139775
+ };
139776
+ canvas.addEventListener(type, callback);
139777
+ return () => canvas.removeEventListener(type, callback);
139778
+ };
139779
+
139780
+ let cleanupCurrentDrag = () => { };
139781
+
139782
+ const startDrag = function(event, onMoveType, onEndType, matchesEvent) {
139783
+ const e = matchesEvent(event);
139784
+ const canvasPos = copyCanvasPos(e, math.vec2());
139785
+ const pickRecord = viewer.scene.pick({ canvasPos: canvasPos, includeEntities: [ zone._zoneMesh.id ] });
139786
+ const pickZone = pickRecord && pickRecord.entity && pickRecord.entity.zone;
139787
+
139788
+ if (pickZone === zone)
139789
+ {
139790
+ cleanupCurrentDrag();
139791
+
139792
+ canvas.style.cursor = "move";
139793
+ viewer.cameraControl.active = false;
139794
+
139795
+ const onChange = (function() {
139796
+ const initCoords = zone._geometry.planeCoordinates.map(c => c.slice());
139797
+ const initWorldPos = pickWorldPos(canvasPos);
139798
+ const initDragCoord = math.vec2([ initWorldPos[0], initWorldPos[2] ]);
139799
+ const dPos = math.vec2();
139800
+
139801
+ return function(canvasPos) {
139802
+ const worldPos = pickWorldPos(canvasPos);
139803
+ dPos[0] = worldPos[0];
139804
+ dPos[1] = worldPos[2];
139805
+ math.subVec2(initDragCoord, dPos, dPos);
139806
+
139807
+ zone._geometry.planeCoordinates.forEach((planeCoord, idx) => {
139808
+ math.subVec2(initCoords[idx], dPos, planeCoord);
139809
+ });
139810
+
139811
+ try {
139812
+ zone._rebuildMesh();
139813
+ } catch (e) {
139814
+ if (zone._zoneMesh) {
139815
+ zone._zoneMesh.destroy();
139816
+ zone._zoneMesh = null;
139817
+ }
139818
+ }
139819
+ };
139820
+ })();
139821
+
139822
+ const cleanupMove = canvasHandle(
139823
+ onMoveType,
139824
+ function(event) {
139825
+ const e = matchesEvent(event);
139826
+ if (e)
139827
+ {
139828
+ const canvasPos = copyCanvasPos(e, math.vec2());
139829
+ onChange(canvasPos);
139830
+ updatePointerLens(canvasPos);
139831
+ }
139832
+ });
139833
+
139834
+ const cleanupEnd = canvasHandle(
139835
+ onEndType,
139836
+ function(event) {
139837
+ const e = matchesEvent(event);
139838
+ if (e)
139839
+ {
139840
+ const canvasPos = copyCanvasPos(e, math.vec2());
139841
+ onChange(canvasPos);
139842
+ updatePointerLens(null);
139843
+ cleanupCurrentDrag();
139844
+ self.fire("translated");
139845
+ }
139846
+ });
139847
+
139848
+ cleanupCurrentDrag = function() {
139849
+ cleanupCurrentDrag = () => { };
139850
+ canvas.style.cursor = "default";
139851
+ viewer.cameraControl.active = true;
139852
+ cleanupMove();
139853
+ cleanupEnd();
139854
+ };
139855
+ }
139856
+ };
139857
+
139858
+ const startDragCbs = [ ];
139859
+
139860
+ if (handleMouseEvents) {
139861
+ startDragCbs.push(
139862
+ canvasHandle("mousedown", event => {
139863
+ if (event.which === 1) {
139864
+ startDrag(
139865
+ event,
139866
+ "mousemove",
139867
+ "mouseup",
139868
+ event => (event.which === 1) && event);
139869
+ }
139870
+ }));
139871
+ }
139872
+
139873
+ if (handleTouchEvents) {
139874
+ startDragCbs.push(
139875
+ canvasHandle("touchstart", event => {
139876
+ if (event.touches.length === 1) {
139877
+ const touchStartId = event.touches[0].identifier;
139878
+ startDrag(
139879
+ event,
139880
+ "touchmove",
139881
+ "touchend",
139882
+ event => [...event.changedTouches].find(e => e.identifier === touchStartId));
139883
+ }
139884
+ }));
139885
+ }
139886
+
139887
+ const cleanup = function() {
139888
+ cleanupCurrentDrag();
139889
+ startDragCbs.forEach(cb => cb());
139890
+ updatePointerLens(null);
139891
+ };
139892
+
139893
+ const destroyCb = zone.on("destroyed", cleanup);
139894
+
139895
+ this._deactivate = function() {
139896
+ zone.off("destroyed", destroyCb);
139897
+ cleanup();
139898
+ };
139899
+ }
139900
+
139901
+ deactivate() {
139902
+ this._deactivate();
139903
+ super.destroy();
139904
+ }
139905
+ }
139906
+
139907
+ class ZoneTranslateMouseControl extends ZoneTranslateControl {
139908
+ constructor(zone, cfg) {
139909
+ super(zone, cfg, true, false);
139910
+ }
139911
+ }
139912
+
139913
+ class ZoneTranslateTouchControl extends ZoneTranslateControl {
139914
+ constructor(zone, cfg) {
139915
+ super(zone, cfg, false, true);
139916
+ }
139917
+ }
139918
+
137702
139919
  exports.AlphaFormat = AlphaFormat;
137703
139920
  exports.AmbientLight = AmbientLight;
137704
139921
  exports.AngleMeasurementsControl = AngleMeasurementsControl;
@@ -137858,6 +140075,17 @@ exports.WorkerPool = WorkerPool$1;
137858
140075
  exports.XKTDefaultDataSource = XKTDefaultDataSource;
137859
140076
  exports.XKTLoaderPlugin = XKTLoaderPlugin;
137860
140077
  exports.XML3DLoaderPlugin = XML3DLoaderPlugin;
140078
+ exports.ZoneEditControl = ZoneEditControl;
140079
+ exports.ZoneEditMouseControl = ZoneEditMouseControl;
140080
+ exports.ZoneEditTouchControl = ZoneEditTouchControl;
140081
+ exports.ZoneTranslateControl = ZoneTranslateControl;
140082
+ exports.ZoneTranslateMouseControl = ZoneTranslateMouseControl;
140083
+ exports.ZoneTranslateTouchControl = ZoneTranslateTouchControl;
140084
+ exports.ZonesMouseControl = ZonesMouseControl;
140085
+ exports.ZonesPlugin = ZonesPlugin;
140086
+ exports.ZonesPolysurfaceMouseControl = ZonesPolysurfaceMouseControl;
140087
+ exports.ZonesPolysurfaceTouchControl = ZonesPolysurfaceTouchControl;
140088
+ exports.ZonesTouchControl = ZonesTouchControl;
137861
140089
  exports.buildBoxGeometry = buildBoxGeometry;
137862
140090
  exports.buildBoxLinesGeometry = buildBoxLinesGeometry;
137863
140091
  exports.buildBoxLinesGeometryFromAABB = buildBoxLinesGeometryFromAABB;