@xeokit/xeokit-sdk 2.6.72 → 2.6.73

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.
@@ -1,7 +1,7 @@
1
1
  /**
2
- * xeokit-sdk v2.6.71
3
- * Commit: 7443626ab66b3eab0018b1ed0ee05d3bfa9f8eae
4
- * Built: 2025-03-21T09:02:29.217Z
2
+ * xeokit-sdk v2.6.73
3
+ * Commit: 3b3bb2ad8abfdf189cf4ba7f67d6798375772b7b
4
+ * Built: 2025-04-10T16:46:49.684Z
5
5
  */
6
6
 
7
7
  'use strict';
@@ -334,6 +334,7 @@ class ContextMenu {
334
334
  * @param {Boolean} [cfg.enabled=true] Whether this ````ContextMenu```` is initially enabled. {@link ContextMenu#show} does nothing while this is ````false````.
335
335
  * @param {Boolean} [cfg.hideOnMouseDown=true] Whether this ````ContextMenu```` automatically hides whenever we mouse-down or tap anywhere in the page.
336
336
  * @param {Boolean} [cfg.hideOnAction=true] Whether this ````ContextMenu```` automatically hides after we select a menu item. Se false if we want the menu to remain shown and show any updates to its item titles, after we've selected an item.
337
+ * @param {Node | undefined} [cfg.parentNode] Optional reference to an existing DOM Node (e.g. ShadowRoot), to which the menu's HTML element will be appended, defaults to ````document.body````.
337
338
  */
338
339
  constructor(cfg = {}) {
339
340
 
@@ -348,6 +349,7 @@ class ContextMenu {
348
349
  this._itemMap = {}; // Items mapped to their IDs
349
350
  this._shown = false; // True when the ContextMenu is visible
350
351
  this._nextId = 0;
352
+ this._parentNode = cfg.parentNode || document.body;
351
353
 
352
354
  /**
353
355
  * Subscriptions to events fired at this ContextMenu.
@@ -356,12 +358,12 @@ class ContextMenu {
356
358
  this._eventSubs = {};
357
359
 
358
360
  if (cfg.hideOnMouseDown !== false) {
359
- document.addEventListener("mousedown", (event) => {
361
+ this._parentNode.addEventListener("mousedown", (event) => {
360
362
  if (!event.target.classList.contains("xeokit-context-menu-item")) {
361
363
  this.hide();
362
364
  }
363
365
  });
364
- document.addEventListener("touchstart", this._canvasTouchStartHandler = (event) => {
366
+ this._parentNode.addEventListener("touchstart", this._canvasTouchStartHandler = (event) => {
365
367
  if (!event.target.classList.contains("xeokit-context-menu-item")) {
366
368
  this.hide();
367
369
  }
@@ -665,7 +667,10 @@ class ContextMenu {
665
667
  const groups = menu.groups;
666
668
  const html = [];
667
669
 
668
- html.push('<div class="xeokit-context-menu ' + menu.id + '" style="z-index:300000; position: absolute;">');
670
+ const menuElement= document.createElement("div");
671
+ menuElement.classList.add("xeokit-context-menu", menu.id);
672
+ menuElement.style.zIndex = 300000;
673
+ menuElement.style.position = "absolute";
669
674
 
670
675
  html.push('<ul>');
671
676
 
@@ -716,13 +721,10 @@ class ContextMenu {
716
721
  }
717
722
 
718
723
  html.push('</ul>');
719
- html.push('</div>');
720
724
 
721
725
  const htmlString = html.join("");
722
-
723
- document.body.insertAdjacentHTML('beforeend', htmlString);
724
-
725
- const menuElement = document.querySelector("." + menu.id);
726
+ menuElement.innerHTML = htmlString;
727
+ this._parentNode.appendChild(menuElement);
726
728
 
727
729
  menu.menuElement = menuElement;
728
730
 
@@ -756,7 +758,7 @@ class ContextMenu {
756
758
  const item = groupItems[j];
757
759
  const itemSubMenu = item.subMenu;
758
760
 
759
- item.itemElement = document.getElementById(item.id);
761
+ item.itemElement = this._parentNode.querySelector(`#${item.id}`);
760
762
 
761
763
  if (!item.itemElement) {
762
764
  console.error("ContextMenu item element not found: " + item.id);
@@ -11659,9 +11661,28 @@ const tmpVec4b = math.vec4();
11659
11661
  const tmpVec4c = math.vec4();
11660
11662
  const tmpVec4d = math.vec4();
11661
11663
  const tmpMat4 = math.mat4();
11664
+ const tmpRay = {
11665
+ origin: math.vec3(),
11666
+ direction: math.vec3()
11667
+ };
11662
11668
 
11663
11669
  const nop = () => { };
11664
11670
 
11671
+ const planeIntersect$1 = function(p0, n, origin, direction) {
11672
+ const t = (p0 - math.dotVec3(origin, n)) / math.dotVec3(direction, n);
11673
+ if (t < 0)
11674
+ {
11675
+ return null;
11676
+ }
11677
+ else
11678
+ {
11679
+ const worldPos = math.vec3();
11680
+ math.mulVec3Scalar(direction, t, worldPos);
11681
+ math.addVec3(origin, worldPos, worldPos);
11682
+ return worldPos;
11683
+ }
11684
+ };
11685
+
11665
11686
  function transformToNode(from, to, vec) {
11666
11687
  const fromRec = from.getBoundingClientRect();
11667
11688
  const toRec = to.getBoundingClientRect();
@@ -11923,9 +11944,15 @@ class Label3D {
11923
11944
  this._mid = math.vec3();
11924
11945
  this._end = math.vec3();
11925
11946
  this._yOff = 0;
11926
- this._betweenWires = false;
11927
11947
  this.__visible = true;
11928
11948
 
11949
+ this._updatePos = () => {
11950
+ toClipSpace(camera, this._start, tmpVec4a);
11951
+ math.mulVec4Scalar(tmpVec4a, 1.0 / tmpVec4a[3]);
11952
+ toCanvasSpace(scene.canvas.canvas, parentElement, tmpVec4a, tmpVec2a);
11953
+ this._label.setPos(tmpVec2a[0], tmpVec2a[1]);
11954
+ };
11955
+
11929
11956
  const setPosOnWire = (p0, p1, yOff) => {
11930
11957
  p0[0] += p1[0];
11931
11958
  p0[1] += p1[1];
@@ -11933,36 +11960,44 @@ class Label3D {
11933
11960
  this._label.setPos(p0[0], p0[1] + yOff);
11934
11961
  };
11935
11962
 
11936
- this._updatePositions = () => {
11937
- if (! this.__visible) {
11938
- return;
11963
+ this._updatePosBetween = () => {
11964
+ const visibleA = clipSegment(scene, parentElement, this._start, this._mid, tmpVec2a, tmpVec2b);
11965
+ const visibleB = clipSegment(scene, parentElement, this._end, this._mid, tmpVec2c, tmpVec2d);
11966
+ this._label.setCulled(! (visibleA || visibleB));
11967
+ if (visibleA && visibleB) {
11968
+ tmpVec2b[0] += tmpVec2d[0];
11969
+ tmpVec2b[1] += tmpVec2d[1];
11970
+ math.mulVec2Scalar(tmpVec2b, .5);
11971
+
11972
+ tmpVec2b[0] += tmpVec2a[0] + tmpVec2c[0];
11973
+ tmpVec2b[1] += tmpVec2a[1] + tmpVec2c[1];
11974
+ math.mulVec2Scalar(tmpVec2b, 1/3);
11975
+ this._label.setPos(tmpVec2b[0], tmpVec2b[1]);
11976
+ } else if (visibleA) {
11977
+ setPosOnWire(tmpVec2a, tmpVec2b, 0);
11978
+ } else if (visibleB) {
11979
+ setPosOnWire(tmpVec2c, tmpVec2d, 0);
11939
11980
  }
11940
- if (this._betweenWires) {
11941
- const visibleA = clipSegment(scene, parentElement, this._start, this._mid, tmpVec2a, tmpVec2b);
11942
- const visibleB = clipSegment(scene, parentElement, this._end, this._mid, tmpVec2c, tmpVec2d);
11943
- this._label.setCulled(! (visibleA || visibleB));
11944
- if (visibleA && visibleB) {
11945
- tmpVec2b[0] += tmpVec2d[0];
11946
- tmpVec2b[1] += tmpVec2d[1];
11947
- math.mulVec2Scalar(tmpVec2b, .5);
11948
-
11949
- tmpVec2b[0] += tmpVec2a[0] + tmpVec2c[0];
11950
- tmpVec2b[1] += tmpVec2a[1] + tmpVec2c[1];
11951
- math.mulVec2Scalar(tmpVec2b, 1/3);
11952
- this._label.setPos(tmpVec2b[0], tmpVec2b[1]);
11953
- } else if (visibleA) {
11954
- setPosOnWire(tmpVec2a, tmpVec2b, 0);
11955
- } else if (visibleB) {
11956
- setPosOnWire(tmpVec2c, tmpVec2d, 0);
11957
- }
11958
- } else {
11959
- const visible = (clipSegment(scene, parentElement, this._start, this._end, tmpVec2a, tmpVec2b)
11960
- &&
11961
- (math.distVec2(tmpVec2a, tmpVec2b) >= this._labelMinAxisLength));
11962
- this._label.setCulled(!visible);
11963
- if (visible) {
11964
- setPosOnWire(tmpVec2a, tmpVec2b, this._yOff);
11965
- }
11981
+ };
11982
+
11983
+ this._updatePosOnWire = () => {
11984
+ const visible = (clipSegment(scene, parentElement, this._start, this._end, tmpVec2a, tmpVec2b)
11985
+ &&
11986
+ (math.distVec2(tmpVec2a, tmpVec2b) >= this._labelMinAxisLength));
11987
+ this._label.setCulled(!visible);
11988
+ if (visible) {
11989
+ setPosOnWire(tmpVec2a, tmpVec2b, this._yOff);
11990
+ }
11991
+ };
11992
+
11993
+ let posUpdate = () => { };
11994
+ this._setUpdatePositions = (_posUpdate) => {
11995
+ posUpdate = _posUpdate;
11996
+ this._updatePositions();
11997
+ };
11998
+ this._updatePositions = () => {
11999
+ if (this.__visible) {
12000
+ posUpdate();
11966
12001
  }
11967
12002
  };
11968
12003
 
@@ -11980,21 +12015,24 @@ class Label3D {
11980
12015
  };
11981
12016
  }
11982
12017
 
12018
+ setPos(p0) {
12019
+ this._start.set(p0);
12020
+ this._setUpdatePositions(this._updatePos);
12021
+ }
12022
+
11983
12023
  setPosOnWire(p0, p1, yOff, labelMinAxisLength) {
11984
12024
  this._start.set(p0);
11985
12025
  this._end.set(p1);
11986
12026
  this._yOff = yOff;
11987
12027
  this._labelMinAxisLength = labelMinAxisLength;
11988
- this._betweenWires = false;
11989
- this._updatePositions();
12028
+ this._setUpdatePositions(this._updatePosOnWire);
11990
12029
  }
11991
12030
 
11992
12031
  setPosBetween(p0, p1, p2) {
11993
12032
  this._start.set(p0);
11994
12033
  this._mid.set(p1);
11995
12034
  this._end.set(p2);
11996
- this._betweenWires = true;
11997
- this._updatePositions();
12035
+ this._setUpdatePositions(this._updatePosBetween);
11998
12036
  }
11999
12037
 
12000
12038
  setFillColor(value) {
@@ -12097,6 +12135,51 @@ class Wire3D {
12097
12135
 
12098
12136
  }
12099
12137
 
12138
+ const marker3D = function(scene, color) {
12139
+ const marker = new Dot3D(scene, {}, scene.canvas.canvas.parentNode, {
12140
+ borderColor: "white",
12141
+ fillColor: color,
12142
+ zIndex: 100
12143
+ });
12144
+
12145
+ return {
12146
+ update: function(worldPos) {
12147
+ if (worldPos)
12148
+ {
12149
+ marker.worldPos = worldPos;
12150
+ }
12151
+ marker.setVisible(!!worldPos);
12152
+ },
12153
+
12154
+ setFillColor: c => marker.setFillColor(c),
12155
+ setHighlighted: h => marker.setHighlighted(h),
12156
+ getCanvasPos: () => marker.canvasPos,
12157
+ getWorldPos: () => marker.worldPos,
12158
+ destroy: () => marker.destroy()
12159
+ };
12160
+ };
12161
+
12162
+ const wire3D = function(scene, color) {
12163
+ const wire = new Wire3D(scene, scene.canvas.canvas.ownerDocument.body, {
12164
+ color: color,
12165
+ thickness: 1,
12166
+ thicknessClickable: 6 // TODO: Remove to make not clickable?
12167
+ });
12168
+ wire.setVisible(false);
12169
+ return {
12170
+ update: (startWorldPos, endWorldPos) => {
12171
+ if (endWorldPos)
12172
+ {
12173
+ wire.setEnds(startWorldPos, endWorldPos);
12174
+ }
12175
+ wire.setVisible(!!endWorldPos);
12176
+ },
12177
+
12178
+ setColor: c => wire.setColor(c),
12179
+ destroy: () => wire.destroy()
12180
+ };
12181
+ };
12182
+
12100
12183
  function activateDraggableDot(dot, cfg) {
12101
12184
  const extractCFG = function(propName, defaultValue) {
12102
12185
  if (propName in cfg) {
@@ -12411,6 +12494,482 @@ const touchPointSelector = function(viewer, pointerCircle, ray2WorldPos) {
12411
12494
  };
12412
12495
  };
12413
12496
 
12497
+ const triangulateEarClipping = function(planeCoords) {
12498
+ const polygonVertices = [ ];
12499
+ for (let i = 0; i < planeCoords.length; ++i)
12500
+ polygonVertices.push(i);
12501
+
12502
+ const isCCW = (function() {
12503
+ const ba = math.vec2();
12504
+ const bc = math.vec2();
12505
+
12506
+ let anglesSum = 0;
12507
+
12508
+ for (let i = 0; i < polygonVertices.length; ++i)
12509
+ {
12510
+ const a = planeCoords[polygonVertices[i]];
12511
+ const b = planeCoords[polygonVertices[(i + 1) % polygonVertices.length]];
12512
+ const c = planeCoords[polygonVertices[(i + 2) % polygonVertices.length]];
12513
+
12514
+ math.subVec2(a, b, ba);
12515
+ math.subVec2(c, b, bc);
12516
+
12517
+ const theta = math.dotVec2(ba, bc) / Math.sqrt(math.sqLenVec2(ba) * math.sqLenVec2(bc));
12518
+ const angle = Math.acos(Math.max(-1, Math.min(theta, 1)));
12519
+ const convex = (ba[0] * bc[1] - ba[1] * bc[0]) >= 0;
12520
+ anglesSum += convex ? angle : (2 * Math.PI - angle);
12521
+ }
12522
+
12523
+ return anglesSum < (polygonVertices.length * Math.PI);
12524
+ })();
12525
+
12526
+ const pointInTriangle = (function() {
12527
+ const sign = (p1, p2, p3) => {
12528
+ return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
12529
+ };
12530
+
12531
+ return (pt, v1, v2, v3) => {
12532
+ const d1 = sign(pt, v1, v2);
12533
+ const d2 = sign(pt, v2, v3);
12534
+ const d3 = sign(pt, v3, v1);
12535
+
12536
+ const has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
12537
+ const has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
12538
+
12539
+ return !(has_neg && has_pos);
12540
+ };
12541
+ })();
12542
+
12543
+ const baseTriangles = [ ];
12544
+
12545
+ const vertices = (isCCW ? polygonVertices : polygonVertices.slice(0).reverse()).map(i => ({ idx: i }));
12546
+ vertices.forEach((v, i) => {
12547
+ v.prev = vertices[(i - 1 + vertices.length) % vertices.length];
12548
+ v.next = vertices[(i + 1) % vertices.length];
12549
+ });
12550
+
12551
+ const ba = math.vec2();
12552
+ const bc = math.vec2();
12553
+
12554
+ while (vertices.length > 2) {
12555
+ let earIdx = 0;
12556
+ while (true) {
12557
+ if (earIdx >= vertices.length)
12558
+ {
12559
+ throw `isCCW = ${isCCW}; earIdx = ${earIdx}; len = ${vertices.length}`;
12560
+ }
12561
+ const v = vertices[earIdx];
12562
+
12563
+ const a = planeCoords[v.prev.idx];
12564
+ const b = planeCoords[v.idx];
12565
+ const c = planeCoords[v.next.idx];
12566
+
12567
+ math.subVec2(a, b, ba);
12568
+ math.subVec2(c, b, bc);
12569
+
12570
+ if (((ba[0] * bc[1] - ba[1] * bc[0]) >= 0) // a convex vertex
12571
+ &&
12572
+ vertices.every( // no other vertices inside
12573
+ vv => ((vv === v)
12574
+ ||
12575
+ (vv === v.prev)
12576
+ ||
12577
+ (vv === v.next)
12578
+ ||
12579
+ !pointInTriangle(planeCoords[vv.idx], a, b, c))))
12580
+ break;
12581
+ ++earIdx;
12582
+ }
12583
+
12584
+ const ear = vertices[earIdx];
12585
+ vertices.splice(earIdx, 1);
12586
+
12587
+ baseTriangles.push([ ear.idx, ear.next.idx, ear.prev.idx ]);
12588
+
12589
+ const prev = ear.prev;
12590
+ prev.next = ear.next;
12591
+ const next = ear.next;
12592
+ next.prev = ear.prev;
12593
+ }
12594
+
12595
+ return [ planeCoords, baseTriangles, isCCW ];
12596
+ };
12597
+
12598
+ const addMousePressListener = function(element, onChange) {
12599
+ const moveTolerance = 4;
12600
+
12601
+ const copyElementPos = (event, out) => {
12602
+ out[0] = event.clientX;
12603
+ out[1] = event.clientY;
12604
+ transformToNode(element.ownerDocument.documentElement, element, out);
12605
+ return out;
12606
+ };
12607
+
12608
+ let buttonDown = false;
12609
+
12610
+ const cleanups = [ ];
12611
+ const cleanup = () => cleanups.forEach(c => c());
12612
+
12613
+ const addElementEventListener = (type, listener) => {
12614
+ element.addEventListener(type, listener);
12615
+ cleanups.push(() => element.removeEventListener(type, listener));
12616
+ };
12617
+
12618
+ const downPos = math.vec2();
12619
+ addElementEventListener("mousedown", function(event) {
12620
+ if (event.which === 1) {
12621
+ buttonDown = true;
12622
+ onChange(copyElementPos(event, downPos));
12623
+ }
12624
+ });
12625
+
12626
+ addElementEventListener("mousemove", function(event) {
12627
+ copyElementPos(event, tmpVec2a);
12628
+ if (buttonDown && (math.distVec2(downPos, tmpVec2a) > moveTolerance)) {
12629
+ buttonDown = false;
12630
+ }
12631
+ if (buttonDown || (! event.buttons & 1)) {
12632
+ onChange(tmpVec2a);
12633
+ }
12634
+ });
12635
+
12636
+ addElementEventListener("mouseup", function(event) {
12637
+ if ((event.which === 1) && buttonDown) {
12638
+ const commit = onChange(copyElementPos(event, tmpVec2a));
12639
+ if (commit) {
12640
+ cleanup();
12641
+ commit();
12642
+ }
12643
+ }
12644
+ });
12645
+
12646
+ return cleanup;
12647
+ };
12648
+
12649
+ const addTouchPressListener = function(element, cameraControl, pointerCircle, onChange) {
12650
+ const longTouchTimeoutMs = 300;
12651
+ const moveTolerance = 20;
12652
+ const startPos = math.vec2();
12653
+
12654
+ const copyElementPos = (event, out) => {
12655
+ out[0] = event.clientX;
12656
+ out[1] = event.clientY;
12657
+ transformToNode(element.ownerDocument.documentElement, element, out);
12658
+ return out;
12659
+ };
12660
+
12661
+ let longTouchTimeout = null;
12662
+ let onSingleTouchMove = nop;
12663
+ let startTouchIdentifier;
12664
+
12665
+ const resetAction = function() {
12666
+ clearTimeout(longTouchTimeout);
12667
+ pointerCircle.stop();
12668
+ cameraControl.active = true;
12669
+ onSingleTouchMove = nop;
12670
+ startTouchIdentifier = null;
12671
+ };
12672
+
12673
+ const cleanups = [ ];
12674
+ const cleanup = () => cleanups.forEach(c => c());
12675
+
12676
+ const addElementEventListener = (type, listener) => {
12677
+ element.addEventListener(type, listener, {passive: true});
12678
+ cleanups.push(() => element.removeEventListener(type, listener));
12679
+ };
12680
+
12681
+ addElementEventListener("touchstart", function(event) {
12682
+ const touches = event.touches;
12683
+
12684
+ if (touches.length !== 1)
12685
+ {
12686
+ resetAction();
12687
+ onChange(null);
12688
+ }
12689
+ else
12690
+ {
12691
+ const touch = touches[0];
12692
+ copyElementPos(touch, startPos);
12693
+
12694
+ startTouchIdentifier = touch.identifier;
12695
+
12696
+ onSingleTouchMove = elementPos => {
12697
+ if (math.distVec2(startPos, elementPos) > moveTolerance)
12698
+ {
12699
+ resetAction();
12700
+ }
12701
+ };
12702
+
12703
+ longTouchTimeout = setTimeout(
12704
+ function() {
12705
+ pointerCircle.start(startPos);
12706
+
12707
+ longTouchTimeout = setTimeout(
12708
+ function() {
12709
+ pointerCircle.stop();
12710
+ cameraControl.active = false;
12711
+ onSingleTouchMove = onChange;
12712
+ onSingleTouchMove(startPos);
12713
+ },
12714
+ longTouchTimeoutMs);
12715
+ },
12716
+ 250);
12717
+ }
12718
+ });
12719
+
12720
+ // element.addEventListener("touchcancel", e => console.log("touchcancel", e), {passive: true});
12721
+
12722
+ addElementEventListener("touchmove", function(event) {
12723
+ const touch = [...event.changedTouches].find(e => e.identifier === startTouchIdentifier);
12724
+ if (touch)
12725
+ {
12726
+ onSingleTouchMove(copyElementPos(touch, tmpVec2a));
12727
+ }
12728
+ });
12729
+
12730
+ addElementEventListener("touchend", function(event) {
12731
+ const touch = [...event.changedTouches].find(e => e.identifier === startTouchIdentifier);
12732
+ if (touch)
12733
+ {
12734
+ const commit = onChange(copyElementPos(touch, tmpVec2a));
12735
+ resetAction();
12736
+ if (commit) {
12737
+ cleanup();
12738
+ commit();
12739
+ } else {
12740
+ onChange(null);
12741
+ }
12742
+ }
12743
+ });
12744
+
12745
+ return () => {
12746
+ resetAction();
12747
+ cleanup();
12748
+ };
12749
+ };
12750
+
12751
+ const startPolygonCreate = function(scene, pointerLens, addPressListener, pickRayResult, onChange, onConclude) {
12752
+ const canvas = scene.canvas.canvas;
12753
+
12754
+ const updatePointerLens = (pointerLens
12755
+ ? function(canvasPos, isSnapped) {
12756
+ pointerLens.visible = !! canvasPos;
12757
+ if (canvasPos)
12758
+ {
12759
+ pointerLens.canvasPos = canvasPos;
12760
+ pointerLens.snapped = !! isSnapped;
12761
+ }
12762
+ }
12763
+ : () => { });
12764
+
12765
+ const testLastSegmentIntersects = (function() {
12766
+ const onSegment = (p, q, r) => ((q[0] <= Math.max(p[0], r[0])) &&
12767
+ (q[0] >= Math.min(p[0], r[0])) &&
12768
+ (q[1] <= Math.max(p[1], r[1])) &&
12769
+ (q[1] >= Math.min(p[1], r[1])));
12770
+
12771
+ const orient = (p, q, r) => {
12772
+ const val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
12773
+ // collinear
12774
+ // clockwise
12775
+ // counterclockwise
12776
+ return ((val === 0) ? 0 : ((val > 0) ? 1 : 2));
12777
+ };
12778
+
12779
+ return function(pos2D, lastSegmentClosesLoop) {
12780
+ const s = lastSegmentClosesLoop ? 1 : 0;
12781
+ const a = pos2D[(pos2D.length - 2 + s) % pos2D.length];
12782
+ const b = pos2D[(pos2D.length - 1 + s) % pos2D.length];
12783
+
12784
+ for (let i = s; i < pos2D.length - 2 - 1 + s; ++i) {
12785
+ const c = pos2D[i];
12786
+ const d = pos2D[i + 1];
12787
+
12788
+ const o1 = orient(a, b, c);
12789
+ const o2 = orient(a, b, d);
12790
+ const o3 = orient(c, d, a);
12791
+ const o4 = orient(c, d, b);
12792
+
12793
+ if (((o1 !== o2) && (o3 !== o4)) || // General case
12794
+ ((o1 === 0) && onSegment(a, c, b)) || // a, b and c are collinear and c lies on segment ab
12795
+ ((o2 === 0) && onSegment(a, d, b)) || // a, b and d are collinear and d lies on segment ab
12796
+ ((o3 === 0) && onSegment(c, a, d)) || // c, d and a are collinear and a lies on segment cd
12797
+ ((o4 === 0) && onSegment(c, b, d))) // c, d and b are collinear and b lies on segment cd
12798
+ {
12799
+ return true;
12800
+ }
12801
+ }
12802
+
12803
+ return false;
12804
+ };
12805
+ })();
12806
+
12807
+ const getPlane = (points) => (points.length >= 3) && (function() {
12808
+ const u = math.normalizeVec3(math.subVec3(points[1], points[0], math.vec3()));
12809
+ const v20 = math.normalizeVec3(math.subVec3(points[2], points[0], tmpVec3a$2));
12810
+ const normal = math.normalizeVec3(math.cross3Vec3(u, v20, math.vec3()));
12811
+ const v = math.normalizeVec3(math.cross3Vec3(normal, u, math.vec3()));
12812
+ return {
12813
+ normal: normal,
12814
+ origin: math.vec3(points[0]),
12815
+ u: u,
12816
+ v: v
12817
+ };
12818
+ })();
12819
+
12820
+ const vertices = [ ];
12821
+ let currentInteraction;
12822
+
12823
+ (function selectNextPoint() {
12824
+ const plane = getPlane(vertices);
12825
+
12826
+ const canvasPos2Ray = (canvasPos, dst) => (math.canvasPosToWorldRay(canvas, scene.camera.viewMatrix, scene.camera.projMatrix, scene.camera.projection, canvasPos, dst.origin, dst.direction), dst);
12827
+
12828
+ const pickRay = (ray) => {
12829
+ const pickResult = pickRayResult(ray);
12830
+ const snapWorldPos = pickResult && pickResult.entity && ((! plane) || pickResult.snapped) && pickResult.worldPos;
12831
+ if (plane) {
12832
+ const originDistance = math.dotVec3(plane.normal, plane.origin);
12833
+ const snapPlaneDist = (snapWorldPos
12834
+ ? ((originDistance - math.dotVec3(snapWorldPos, plane.normal)))
12835
+ : window.Infinity);
12836
+ if (Math.abs(snapPlaneDist) < 0.0001) {
12837
+ const ret = math.vec3();
12838
+ return {
12839
+ worldPos: math.addVec3(snapWorldPos, math.mulVec3Scalar(plane.normal, snapPlaneDist, ret), ret),
12840
+ snapped: true
12841
+ };
12842
+ } else {
12843
+ return {
12844
+ worldPos: planeIntersect$1(originDistance, plane.normal, ray.origin, ray.direction),
12845
+ snapped: false
12846
+ };
12847
+ }
12848
+ } else {
12849
+ return {
12850
+ worldPos: snapWorldPos,
12851
+ snapped: pickResult && pickResult.snapped
12852
+ };
12853
+ }
12854
+ };
12855
+
12856
+ let placeVertex = false;
12857
+ const removePressListener = addPressListener(
12858
+ (inputCanvasPos) => {
12859
+ const rayPick = inputCanvasPos && pickRay(canvasPos2Ray(inputCanvasPos, tmpRay));
12860
+ const worldPos = rayPick && rayPick.worldPos;
12861
+ return onWorldPos(worldPos, inputCanvasPos, rayPick && rayPick.snapped);
12862
+ });
12863
+
12864
+ const onWorldPos = (worldPos, inputCanvasPos, rayPickSnapped) => {
12865
+ const canvasPos = worldPos ? scene.camera.projectWorldPos(worldPos) : inputCanvasPos;
12866
+ const firstMarker = (vertices.length > 0) && (function() {
12867
+ const v = vertices[0];
12868
+ return {
12869
+ canvasPos: scene.camera.projectWorldPos(v),
12870
+ worldPos: v
12871
+ };
12872
+ })();
12873
+ const snapToFirst = (vertices.length >= 3) && ((canvasPos && (math.distVec2(canvasPos, firstMarker.canvasPos) < 10))
12874
+ ||
12875
+ (worldPos && math.compareVec3(worldPos, firstMarker.worldPos)));
12876
+
12877
+ updatePointerLens(snapToFirst ? firstMarker.canvasPos : canvasPos, snapToFirst || rayPickSnapped);
12878
+
12879
+ const lastPointOverlaps = (inputCanvasPos
12880
+ &&
12881
+ ((! worldPos)
12882
+ ||
12883
+ ((vertices.length < 3)
12884
+ ? vertices.some(v => math.compareVec3(v, worldPos))
12885
+ : math.compareVec3(vertices[vertices.length - 1], worldPos))));
12886
+
12887
+ const points = vertices.concat(((! snapToFirst) && worldPos && (! lastPointOverlaps)) ? [worldPos] : []);
12888
+
12889
+ const curPlane = (! lastPointOverlaps) && (plane || getPlane(points));
12890
+
12891
+ const uvs = curPlane && points.map(p => {
12892
+ math.subVec3(p, curPlane.origin, tmpVec3a$2);
12893
+ return math.vec2([ math.dotVec3(tmpVec3a$2, curPlane.u), math.dotVec3(tmpVec3a$2, curPlane.v) ]);
12894
+ });
12895
+
12896
+ const isValid = (! lastPointOverlaps) && (! (uvs && testLastSegmentIntersects(uvs, snapToFirst)));
12897
+ const geometry = isValid && uvs && (snapToFirst || (! testLastSegmentIntersects(uvs, true))) && (function() {
12898
+ try {
12899
+ const [ baseVertices, baseTriangles, isCCW ] = triangulateEarClipping(uvs);
12900
+ return {
12901
+ faces: baseTriangles,
12902
+ vertices: baseVertices.map(uv => math.addVec3(
12903
+ curPlane.origin,
12904
+ math.addVec3(
12905
+ math.mulVec3Scalar(curPlane.u, uv[0], tmpVec3a$2),
12906
+ math.mulVec3Scalar(curPlane.v, uv[1], tmpVec3b$2),
12907
+ tmpVec3a$2),
12908
+ math.vec3()))
12909
+ };
12910
+ } catch (e) {
12911
+ console.warn("e", e);
12912
+ return false;
12913
+ }
12914
+ })();
12915
+
12916
+ onChange(points, snapToFirst, isValid, geometry);
12917
+
12918
+ return placeVertex = (isValid && (snapToFirst
12919
+ ? () => {
12920
+ updatePointerLens(null);
12921
+ onConclude();
12922
+ }
12923
+ : () => {
12924
+ vertices.push(math.vec3(worldPos));
12925
+ updatePointerLens(null);
12926
+ selectNextPoint();
12927
+ }));
12928
+ };
12929
+
12930
+ currentInteraction = {
12931
+ closePolygon: (() => {
12932
+ const commit = (vertices.length >= 3) && onWorldPos(vertices[0]);
12933
+ if (commit) {
12934
+ removePressListener();
12935
+ commit();
12936
+ }
12937
+ return !! commit;
12938
+ }),
12939
+ placeVertex: () => {
12940
+ if (placeVertex) {
12941
+ removePressListener();
12942
+ placeVertex();
12943
+ }
12944
+ return !!placeVertex;
12945
+ },
12946
+ popVertex: () => {
12947
+ if (vertices.length > 0) {
12948
+ removePressListener();
12949
+ vertices.pop();
12950
+ selectNextPoint();
12951
+ return true;
12952
+ } else {
12953
+ return false;
12954
+ }
12955
+ },
12956
+ removePressListener: removePressListener,
12957
+ updateOnChange: () => onWorldPos()
12958
+ };
12959
+ })();
12960
+
12961
+ return {
12962
+ cancel: () => {
12963
+ currentInteraction.removePressListener();
12964
+ updatePointerLens(null);
12965
+ },
12966
+ closePolygon: () => currentInteraction.closePolygon(),
12967
+ placeVertex: () => currentInteraction.placeVertex(),
12968
+ popVertex: () => currentInteraction.popVertex(),
12969
+ updateOnChange: () => currentInteraction.updateOnChange()
12970
+ };
12971
+ };
12972
+
12414
12973
  const tmpVec3a$1 = math.vec3();
12415
12974
  const tmpVec3b$1 = math.vec3();
12416
12975
 
@@ -14830,6 +15389,8 @@ const tempVec3a$M = math.vec3();
14830
15389
  const tempVec3b$B = math.vec3();
14831
15390
  const tempVec3c$w = math.vec3();
14832
15391
 
15392
+ const px = x => x + "px";
15393
+
14833
15394
  /**
14834
15395
  * A {@link Marker} with an HTML label attached to it, managed by an {@link AnnotationsPlugin}.
14835
15396
  *
@@ -15036,22 +15597,29 @@ class Annotation extends Marker {
15036
15597
  * @private
15037
15598
  */
15038
15599
  _updateWithCurWidths() {
15039
- const px = x => x + "px";
15040
15600
  const boundary = this.scene.canvas.boundary;
15041
15601
  const left = boundary[0] + this.canvasPos[0];
15042
15602
  const top = boundary[1] + this.canvasPos[1];
15043
- const markerWidth = this._curMarkerWidth;
15044
- const markerDir = (this._markerAlign === "right") ? -1 : ((this._markerAlign === "center") ? 0 : 1);
15045
- const markerCenter = left + markerDir * (markerWidth / 2 - 12);
15046
- this._marker.style.left = px(markerCenter - markerWidth / 2);
15047
- this._marker.style.top = px(top - 12);
15048
- this._marker.style["z-index"] = 90005 + Math.floor(this._viewPos[2]) + 1;
15049
-
15050
- const labelWidth = this._curLabelWidth;
15051
- const labelDir = Math.sign(this._labelPosition);
15052
- this._label.style.left = px(markerCenter + labelDir * (markerWidth / 2 + Math.abs(this._labelPosition) + labelWidth / 2) - labelWidth / 2);
15603
+ this._marker.style.top = px(top - 12);
15053
15604
  this._label.style.top = px(top - 17);
15054
- this._label.style["z-index"] = 90005 + Math.floor(this._viewPos[2]) + 1;
15605
+
15606
+ if (this._markerAlign === "legacy") {
15607
+ this._marker.style.left = px(left - 12);
15608
+ this._label.style.left = px(left + 40);
15609
+ } else {
15610
+ const markerWidth = this._curMarkerWidth;
15611
+ const markerDir = (this._markerAlign === "right") ? -1 : ((this._markerAlign === "center") ? 0 : 1);
15612
+ const markerCenter = left + markerDir * (markerWidth / 2 - 12);
15613
+ this._marker.style.left = px(markerCenter - markerWidth / 2);
15614
+
15615
+ const labelWidth = this._curLabelWidth;
15616
+ const labelDir = Math.sign(this._labelPosition);
15617
+ this._label.style.left = px(markerCenter + labelDir * (markerWidth / 2 + Math.abs(this._labelPosition) + labelWidth / 2) - labelWidth / 2);
15618
+ }
15619
+
15620
+ const zIndex = 90005 + Math.floor(this._viewPos[2]) + 1;
15621
+ this._marker.style["z-index"] = zIndex;
15622
+ this._label.style["z-index"] = zIndex;
15055
15623
  }
15056
15624
 
15057
15625
  /**
@@ -15082,7 +15650,8 @@ class Annotation extends Marker {
15082
15650
  * @private
15083
15651
  */
15084
15652
  _updatePosition() {
15085
- if (this._curMarkerWidth === undefined) {
15653
+ const isLegacy = this._markerAlign === "legacy";
15654
+ if ((! isLegacy) && (this._curMarkerWidth === undefined)) {
15086
15655
  this._updateIfWidthsChanged();
15087
15656
  } else {
15088
15657
  // Update position with cached width values
@@ -15090,7 +15659,9 @@ class Annotation extends Marker {
15090
15659
  // so they don't interfere with e.g. interactive scene manipulation
15091
15660
  this._updateWithCurWidths();
15092
15661
  window.clearTimeout(this._widthTimeout);
15093
- this._widthTimeout = window.setTimeout(() => this._updateIfWidthsChanged(), 500);
15662
+ if (! isLegacy) {
15663
+ this._widthTimeout = window.setTimeout(() => this._updateIfWidthsChanged(), 500);
15664
+ }
15094
15665
  }
15095
15666
  }
15096
15667
 
@@ -15128,10 +15699,10 @@ class Annotation extends Marker {
15128
15699
  /**
15129
15700
  * Sets the horizontal alignment of the Annotation's marker HTML.
15130
15701
  *
15131
- * @param {String} align Either "left", "center", "right" (default "left")
15702
+ * @param {String} align Either "left", "center", "right", "legacy" (default "left")
15132
15703
  */
15133
15704
  setMarkerAlign(align) {
15134
- const valid = [ "left", "center", "right" ];
15705
+ const valid = [ "left", "center", "right", "legacy" ];
15135
15706
  if (! valid.includes(align)) {
15136
15707
  this.error("Param 'align' should be one of: " + JSON.stringify(valid));
15137
15708
  } else {
@@ -26514,6 +27085,65 @@ class ReadableGeometry extends Geometry {
26514
27085
  return this._obb;
26515
27086
  }
26516
27087
 
27088
+ _getMetrics() {
27089
+ if (! ("_metrics" in this)) {
27090
+ switch (this._state.primitiveName) {
27091
+ case "solid":
27092
+ case "surface":
27093
+ case "triangles": {
27094
+ const indices = this._state.indices;
27095
+ const positions = this._state.positions;
27096
+ const getPos = (i, out) => {
27097
+ const idx = indices[i] * 3;
27098
+ for (let j = 0; j < 3; ++j) {
27099
+ out[j] = positions[idx + j];
27100
+ } return out;
27101
+ };
27102
+ const tmp = [ math.vec3(), math.vec3(), math.vec3(), math.vec3() ];
27103
+ let totalArea = 0;
27104
+ const centroid = math.vec3([ 0, 0, 0 ]);
27105
+ for (let i = 0; i < indices.length; i += 3) {
27106
+ const v0 = getPos(i, tmp[0]);
27107
+ const v1 = getPos(i+1, tmp[1]);
27108
+ const v2 = getPos(i+2, tmp[2]);
27109
+ math.addVec3(v0, v1, tmp[3]);
27110
+ math.addVec3(v2, tmp[3], tmp[3]);
27111
+ const faceArea = math.lenVec3(
27112
+ math.cross3Vec3(
27113
+ math.subVec3(v1, v0, tmp[1]),
27114
+ math.subVec3(v2, v0, tmp[2]),
27115
+ tmp[0])) / 2;
27116
+ totalArea += faceArea;
27117
+ math.mulVec3Scalar(tmp[3], faceArea, tmp[3]);
27118
+ math.addVec3(centroid, tmp[3], centroid);
27119
+ }
27120
+ this._metrics = { surfaceArea: totalArea, centroid: math.mulVec3Scalar(centroid, 1 / totalArea / 3, centroid) };
27121
+ break;
27122
+ }
27123
+ default:
27124
+ this._metrics = { surfaceArea: 0 };
27125
+ break;
27126
+ }
27127
+ }
27128
+ return this._metrics;
27129
+ }
27130
+
27131
+ /**
27132
+ * Returns the surface area of this Mesh.
27133
+ * @returns {number}
27134
+ */
27135
+ get surfaceArea() {
27136
+ return this._getMetrics().surfaceArea;
27137
+ }
27138
+
27139
+ /**
27140
+ * Returns the centroid of this Mesh.
27141
+ * @returns {number}
27142
+ */
27143
+ get centroid() {
27144
+ return this._getMetrics().centroid;
27145
+ }
27146
+
26517
27147
  /**
26518
27148
  * Approximate number of triangles in this ReadableGeometry.
26519
27149
  *
@@ -40062,7 +40692,7 @@ function buildPlaneGeometry(cfg = {}) {
40062
40692
  positions[offset + 1] = centerY;
40063
40693
  positions[offset + 2] = -z + centerZ;
40064
40694
 
40065
- normals[offset + 2] = -1;
40695
+ normals[offset + 1] = 1;
40066
40696
 
40067
40697
  uvs[offset2] = (ix) / planeX;
40068
40698
  uvs[offset2 + 1] = ((planeZ - iz) / planeZ);
@@ -43360,8 +43990,7 @@ class SectionCaps {
43360
43990
  if(!this._resourcesAllocated) {
43361
43991
  this._resourcesAllocated = true;
43362
43992
  this._sectionPlanes = [];
43363
- this._verticesMap = {};
43364
- this._indicesMap = {};
43993
+ this._sceneModelsData = {};
43365
43994
  this._dirtyMap = {};
43366
43995
  this._prevIntersectionModelsMap = {};
43367
43996
  this._sectionPlaneTimeout = null;
@@ -43376,7 +44005,8 @@ class SectionCaps {
43376
44005
  this._sectionPlanes.push(sectionPlane);
43377
44006
  sectionPlane.on('pos', onSectionPlaneUpdated);
43378
44007
  sectionPlane.on('dir', onSectionPlaneUpdated);
43379
- sectionPlane.once('destroyed', ((sectionPlane) => {
44008
+ sectionPlane.on('active', onSectionPlaneUpdated);
44009
+ sectionPlane.once('destroyed', (() => {
43380
44010
  const sectionPlaneId = sectionPlane.id;
43381
44011
  if (sectionPlaneId) {
43382
44012
  this._sectionPlanes = this._sectionPlanes.filter((sectionPlane) => sectionPlane.id !== sectionPlaneId);
@@ -43393,13 +44023,19 @@ class SectionCaps {
43393
44023
 
43394
44024
  this._onTick = this.scene.on("tick", () => {
43395
44025
  //on ticks we only check if there is a model that we have saved vertices for,
43396
- //but it's no more available on the scene
43397
- for(const key in this._verticesMap) {
43398
- if(!this.scene.models[key]){
43399
- delete this._verticesMap[key];
43400
- delete this._indicesMap[key];
43401
- this._update();
43402
- }
44026
+ //but it's no more available on the scene, or if its visibility changed
44027
+ let dirty = false;
44028
+ for(const sceneModelId in this._sceneModelsData) {
44029
+ if(!this.scene.models[sceneModelId]){
44030
+ delete this._sceneModelsData[sceneModelId];
44031
+ dirty = true;
44032
+ } else if (this._sceneModelsData[sceneModelId].visible !== (!!this.scene.models[sceneModelId].visible)) {
44033
+ this._sceneModelsData[sceneModelId].visible = !!this.scene.models[sceneModelId].visible;
44034
+ dirty = true;
44035
+ }
44036
+ }
44037
+ if (dirty) {
44038
+ this._update();
43403
44039
  }
43404
44040
  });
43405
44041
  }
@@ -43416,8 +44052,8 @@ class SectionCaps {
43416
44052
  this._deletePreviousModels();
43417
44053
  this._updateTimeout = setTimeout(() => {
43418
44054
  clearTimeout(this._updateTimeout);
43419
- const sceneModels = Object.keys(this.scene.models).map((key) => this.scene.models[key]);
43420
- this._addHatches(sceneModels, this._sectionPlanes);
44055
+ const sceneModels = Object.values(this.scene.models).filter(sceneModel => sceneModel.visible);
44056
+ this._addHatches(sceneModels, this._sectionPlanes.filter(sectionPlane => sectionPlane.active));
43421
44057
  this._setAllDirty(false);
43422
44058
  }, 100);
43423
44059
  }
@@ -43430,21 +44066,9 @@ class SectionCaps {
43430
44066
 
43431
44067
  _addHatches(sceneModels, planes) {
43432
44068
 
43433
- if (planes.length <= 0) return;
43434
-
43435
44069
  planes.forEach((plane) => {
43436
44070
  sceneModels.forEach((sceneModel) => {
43437
- //#region creating a plane equation
43438
- //we create a plane equation that will be used to slice through each triangle
43439
- const planeEquation = {
43440
- A: plane.dir[0],
43441
- B: plane.dir[1],
43442
- C: plane.dir[2],
43443
- D: -(plane.dir[0] * plane.pos[0] + plane.dir[1] * plane.pos[1] + plane.dir[2] * plane.pos[2])
43444
- };
43445
- //#endregion
43446
-
43447
- if(!this._doesPlaneIntersectBoundingBox(sceneModel.aabb, planeEquation)) return;
44071
+ if(!this._doesPlaneIntersectBoundingBox(sceneModel.aabb, plane)) return;
43448
44072
 
43449
44073
  if(!this._dirtyMap[sceneModel.id]) return;
43450
44074
 
@@ -43466,39 +44090,35 @@ class SectionCaps {
43466
44090
 
43467
44091
  const object = objects[objectId];
43468
44092
 
43469
- if(!this._doesPlaneIntersectBoundingBox(object.aabb, planeEquation)) return;
44093
+ if(!this._doesPlaneIntersectBoundingBox(object.aabb, plane)) return;
43470
44094
 
43471
- if(!this._verticesMap[sceneModel.id]) {
43472
- this._verticesMap[sceneModel.id] = new Map();
43473
- this._indicesMap[sceneModel.id] = new Map();
44095
+ if(!this._sceneModelsData[sceneModel.id]) {
44096
+ this._sceneModelsData[sceneModel.id] = {
44097
+ verticesMap: new Map(),
44098
+ indicesMap: new Map()
44099
+ };
43474
44100
  }
43475
44101
 
43476
- let vertices = [], indices = [];
44102
+ const sceneModelData = this._sceneModelsData[sceneModel.id];
43477
44103
 
43478
- if(!this._verticesMap[sceneModel.id].has(objectId)) {
44104
+ if(!sceneModelData.verticesMap.has(objectId)) {
43479
44105
  const isSolid = object.meshes[0].isSolid();
44106
+ const vertices = [ ];
44107
+ const indices = [ ];
43480
44108
  if(isSolid && object.capMaterial) {
43481
- object.getEachVertex((_vertices) => {
43482
- vertices.push(_vertices[0], _vertices[1], _vertices[2]);
43483
- });
43484
- object.getEachIndex((_indices) => {
43485
- indices.push(_indices);
43486
- });
44109
+ object.getEachVertex(v => vertices.push(v[0], v[1], v[2]));
44110
+ object.getEachIndex(i => indices.push(i));
43487
44111
  }
43488
- this._verticesMap[sceneModel.id].set(objectId, vertices);
43489
- this._indicesMap[sceneModel.id].set(objectId, indices);
44112
+ sceneModelData.verticesMap.set(objectId, vertices);
44113
+ sceneModelData.indicesMap.set(objectId, indices);
43490
44114
  }
43491
- else {
43492
- vertices = this._verticesMap[sceneModel.id].get(objectId);
43493
- indices = this._indicesMap[sceneModel.id].get(objectId);
43494
- }
43495
-
44115
+
44116
+ const vertices = sceneModelData.verticesMap.get(objectId);
44117
+ const indices = sceneModelData.indicesMap.get(objectId);
44118
+
43496
44119
  const capSegments = [];
43497
44120
  const vertCount = indices.length;
43498
-
43499
- // Preallocate intersection result array
43500
- const intersectionBuffer = new Float32Array(3);
43501
-
44121
+
43502
44122
  for (let i = 0; i < vertCount; i += 3) {
43503
44123
  // Reuse triangle buffer instead of creating new arrays
43504
44124
  for (let j = 0; j < 3; j++) {
@@ -43515,21 +44135,15 @@ class SectionCaps {
43515
44135
  for (let i = 0; i < 3; i++) {
43516
44136
  const p1 = triangle[i];
43517
44137
  const p2 = triangle[(i + 1) % 3];
43518
-
43519
- // Inline the distance calculations to avoid function calls
43520
- const d1 = planeEquation.A * p1[0] + planeEquation.B * p1[1] + planeEquation.C * p1[2] + planeEquation.D;
43521
- const d2 = planeEquation.A * p2[0] + planeEquation.B * p2[1] + planeEquation.C * p2[2] + planeEquation.D;
43522
-
44138
+
44139
+ const d1 = plane.dist + math.dotVec3(plane.dir, p1);
44140
+ const d2 = plane.dist + math.dotVec3(plane.dir, p2);
44141
+
43523
44142
  if (d1 * d2 > 0) continue;
43524
-
44143
+
43525
44144
  const t = -d1 / (d2 - d1);
43526
- // Reuse intersection buffer
43527
- intersectionBuffer[0] = p1[0] + t * (p2[0] - p1[0]);
43528
- intersectionBuffer[1] = p1[1] + t * (p2[1] - p1[1]);
43529
- intersectionBuffer[2] = p1[2] + t * (p2[2] - p1[2]);
43530
-
43531
- // Clone the buffer for storage
43532
- intersections.push(new Float32Array(intersectionBuffer));
44145
+
44146
+ intersections.push(math.lerpVec3(t, 0, 1, p1, p2, math.vec3()));
43533
44147
  }
43534
44148
 
43535
44149
  if(intersections.length === 2) capSegments.push(intersections);
@@ -43809,7 +44423,7 @@ class SectionCaps {
43809
44423
 
43810
44424
  }
43811
44425
 
43812
- _doesPlaneIntersectBoundingBox(bb, planeEquation) {
44426
+ _doesPlaneIntersectBoundingBox(bb, plane) {
43813
44427
  const min = [bb[0], bb[1], bb[2]];
43814
44428
  const max = [bb[3], bb[4], bb[5]];
43815
44429
 
@@ -43829,10 +44443,7 @@ class SectionCaps {
43829
44443
  let hasNegative = false;
43830
44444
 
43831
44445
  for (const corner of corners) {
43832
- const distance = planeEquation.A * corner[0] +
43833
- planeEquation.B * corner[1] +
43834
- planeEquation.C * corner[2] +
43835
- planeEquation.D;
44446
+ const distance = plane.dist + math.dotVec3(plane.dir, corner);
43836
44447
 
43837
44448
  if (distance > 0) hasPositive = true;
43838
44449
  if (distance < 0) hasNegative = true;
@@ -44327,13 +44938,12 @@ class Scene extends Component {
44327
44938
  * configures renderer logic for the specified number of SectionPlanes, eliminating the need for setting up logic with each SectionPlane creation and thereby enhancing
44328
44939
  * responsiveness. It is important to consider that each SectionPlane imposes rendering performance, so it is recommended to set this value to a quantity that aligns with
44329
44940
  * your expected usage.
44330
- * @throws {String} Throws an exception when both canvasId or canvasElement are missing or they aren't pointing to a valid HTMLCanvasElement.
44941
+ * @throws {String} Throws an exception when canvasId or canvasElement are missing or they aren't pointing to a valid HTMLCanvasElement.
44331
44942
  */
44332
44943
  constructor(viewer, cfg = {}) {
44333
44944
 
44334
44945
  super(null, cfg);
44335
-
44336
- const canvas = cfg.canvasElement || document.getElementById(cfg.canvasId);
44946
+ const canvas = cfg.canvasElement || document.querySelector(`#${cfg.canvasId}`);
44337
44947
 
44338
44948
  if (!(canvas instanceof HTMLCanvasElement)) {
44339
44949
  throw "Mandatory config expected: valid canvasId or canvasElement";
@@ -123581,6 +124191,7 @@ function CubeTextureCanvas(viewer, navCubeScene, cfg = {}) {
123581
124191
  const cubeColor = "lightgrey";
123582
124192
  const cubeHighlightColor = cfg.hoverColor || "rgba(0,0,0,0.4)";
123583
124193
  const textColor = cfg.textColor || "black";
124194
+ const parentNode = cfg.canvasElement || document.body;
123584
124195
 
123585
124196
  const height = 500;
123586
124197
  const width = height + (height / 3);
@@ -123690,8 +124301,7 @@ function CubeTextureCanvas(viewer, navCubeScene, cfg = {}) {
123690
124301
  this._textureCanvas.style.visibility = "hidden";
123691
124302
  this._textureCanvas.style["z-index"] = 2000000;
123692
124303
 
123693
- const body = document.getElementsByTagName("body")[0];
123694
- body.appendChild(this._textureCanvas);
124304
+ parentNode.appendChild(this._textureCanvas);
123695
124305
 
123696
124306
  const context = this._textureCanvas.getContext("2d");
123697
124307
 
@@ -123961,7 +124571,7 @@ class NavCubePlugin extends Plugin {
123961
124571
  this._navCubeScene = new Scene(viewer, {
123962
124572
  canvasId: cfg.canvasId,
123963
124573
  canvasElement: cfg.canvasElement,
123964
- transparent: true
124574
+ transparent: true,
123965
124575
  });
123966
124576
 
123967
124577
  this._navCubeCanvas = this._navCubeScene.canvas.canvas;
@@ -124194,7 +124804,7 @@ class NavCubePlugin extends Plugin {
124194
124804
  }
124195
124805
  });
124196
124806
 
124197
- document.addEventListener("mouseup", self._onMouseUp = function (e) {
124807
+ self._navCubeCanvas.addEventListener("mouseup", self._onMouseUp = function (e) {
124198
124808
  if (e.which !== 1) {// Left button
124199
124809
  return;
124200
124810
  }
@@ -124211,7 +124821,7 @@ class NavCubePlugin extends Plugin {
124211
124821
  if (hit.uv) {
124212
124822
  var areaId = self._cubeTextureCanvas.getArea(hit.uv);
124213
124823
  if (areaId >= 0) {
124214
- document.body.style.cursor = "pointer";
124824
+ self._navCubeCanvas.style.cursor = "pointer";
124215
124825
  if (lastAreaId >= 0) {
124216
124826
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
124217
124827
  self._repaint();
@@ -124237,7 +124847,7 @@ class NavCubePlugin extends Plugin {
124237
124847
  self._repaint();
124238
124848
  lastAreaId = -1;
124239
124849
  }
124240
- document.body.style.cursor = "pointer";
124850
+ self._navCubeCanvas.style.cursor = "pointer";
124241
124851
  if (lastAreaId >= 0) {
124242
124852
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
124243
124853
  self._repaint();
@@ -124256,7 +124866,7 @@ class NavCubePlugin extends Plugin {
124256
124866
  }
124257
124867
  });
124258
124868
 
124259
- document.addEventListener("mousemove", self._onMouseMove = function (e) {
124869
+ self._navCubeCanvas.addEventListener("mousemove", self._onMouseMove = function (e) {
124260
124870
  if (lastAreaId >= 0) {
124261
124871
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
124262
124872
  self._repaint();
@@ -124268,7 +124878,7 @@ class NavCubePlugin extends Plugin {
124268
124878
  if (down) {
124269
124879
  var posX = e.clientX;
124270
124880
  var posY = e.clientY;
124271
- document.body.style.cursor = "move";
124881
+ self._navCubeCanvas.style.cursor = "move";
124272
124882
  actionMove(posX, posY);
124273
124883
  return;
124274
124884
  }
@@ -124282,7 +124892,7 @@ class NavCubePlugin extends Plugin {
124282
124892
  });
124283
124893
  if (hit) {
124284
124894
  if (hit.uv) {
124285
- document.body.style.cursor = "pointer";
124895
+ self._navCubeCanvas.style.cursor = "pointer";
124286
124896
  var areaId = self._cubeTextureCanvas.getArea(hit.uv);
124287
124897
  if (areaId === lastAreaId) {
124288
124898
  return;
@@ -124297,7 +124907,7 @@ class NavCubePlugin extends Plugin {
124297
124907
  }
124298
124908
  }
124299
124909
  } else {
124300
- document.body.style.cursor = "default";
124910
+ self._navCubeCanvas.style.cursor = "default";
124301
124911
  if (lastAreaId >= 0) {
124302
124912
  self._cubeTextureCanvas.setAreaHighlighted(lastAreaId, false);
124303
124913
  self._repaint();
@@ -124560,8 +125170,8 @@ class NavCubePlugin extends Plugin {
124560
125170
  this._navCubeCanvas.removeEventListener("mouseleave", this._onMouseLeave);
124561
125171
  this._navCubeCanvas.removeEventListener("mousedown", this._onMouseDown);
124562
125172
 
124563
- document.removeEventListener("mousemove", this._onMouseMove);
124564
- document.removeEventListener("mouseup", this._onMouseUp);
125173
+ this._navCubeCanvas.removeEventListener("mousemove", this._onMouseMove);
125174
+ this._navCubeCanvas.removeEventListener("mouseup", this._onMouseUp);
124565
125175
 
124566
125176
  this._navCubeCanvas = null;
124567
125177
  this._cubeTextureCanvas.destroy();
@@ -125955,8 +126565,8 @@ class Control {
125955
126565
  const canvasPos = math.vec2();
125956
126566
 
125957
126567
  const copyCanvasPos = (event, vec2) => {
125958
- vec2[0] = event.clientX;
125959
- vec2[1] = event.clientY;
126568
+ vec2[0] = event.pageX;
126569
+ vec2[1] = event.pageY;
125960
126570
  transformToNode(canvas.ownerDocument.documentElement, canvas, vec2);
125961
126571
  };
125962
126572
 
@@ -168436,151 +169046,6 @@ const hex2rgb = function(color) {
168436
169046
  return [ rgb(0), rgb(2), rgb(4) ];
168437
169047
  };
168438
169048
 
168439
- const triangulateEarClipping = function(planeCoords) {
168440
-
168441
- const polygonVertices = [ ];
168442
- for (let i = 0; i < planeCoords.length; ++i)
168443
- polygonVertices.push(i);
168444
-
168445
- const isCCW = (function() {
168446
- const ba = math.vec2();
168447
- const bc = math.vec2();
168448
-
168449
- let anglesSum = 0;
168450
-
168451
- for (let i = 0; i < polygonVertices.length; ++i)
168452
- {
168453
- const a = planeCoords[polygonVertices[i]];
168454
- const b = planeCoords[polygonVertices[(i + 1) % polygonVertices.length]];
168455
- const c = planeCoords[polygonVertices[(i + 2) % polygonVertices.length]];
168456
-
168457
- math.subVec2(a, b, ba);
168458
- math.subVec2(c, b, bc);
168459
-
168460
- const theta = math.dotVec2(ba, bc) / Math.sqrt(math.sqLenVec2(ba) * math.sqLenVec2(bc));
168461
- const angle = Math.acos(Math.max(-1, Math.min(theta, 1)));
168462
- const convex = (ba[0] * bc[1] - ba[1] * bc[0]) >= 0;
168463
- anglesSum += convex ? angle : (2 * Math.PI - angle);
168464
- }
168465
-
168466
- return anglesSum < (polygonVertices.length * Math.PI);
168467
- })();
168468
-
168469
- const pointInTriangle = (function() {
168470
- const sign = (p1, p2, p3) => {
168471
- return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
168472
- };
168473
-
168474
- return (pt, v1, v2, v3) => {
168475
- const d1 = sign(pt, v1, v2);
168476
- const d2 = sign(pt, v2, v3);
168477
- const d3 = sign(pt, v3, v1);
168478
-
168479
- const has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
168480
- const has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
168481
-
168482
- return !(has_neg && has_pos);
168483
- };
168484
- })();
168485
-
168486
- const baseTriangles = [ ];
168487
-
168488
- const vertices = (isCCW ? polygonVertices : polygonVertices.slice(0).reverse()).map(i => ({ idx: i }));
168489
- vertices.forEach((v, i) => {
168490
- v.prev = vertices[(i - 1 + vertices.length) % vertices.length];
168491
- v.next = vertices[(i + 1) % vertices.length];
168492
- });
168493
-
168494
- const ba = math.vec2();
168495
- const bc = math.vec2();
168496
-
168497
- while (vertices.length > 2) {
168498
- let earIdx = 0;
168499
- while (true) {
168500
- if (earIdx >= vertices.length)
168501
- {
168502
- throw `isCCW = ${isCCW}; earIdx = ${earIdx}; len = ${vertices.length}`;
168503
- }
168504
- const v = vertices[earIdx];
168505
-
168506
- const a = planeCoords[v.prev.idx];
168507
- const b = planeCoords[v.idx];
168508
- const c = planeCoords[v.next.idx];
168509
-
168510
- math.subVec2(a, b, ba);
168511
- math.subVec2(c, b, bc);
168512
-
168513
- if (((ba[0] * bc[1] - ba[1] * bc[0]) >= 0) // a convex vertex
168514
- &&
168515
- vertices.every( // no other vertices inside
168516
- vv => ((vv === v)
168517
- ||
168518
- (vv === v.prev)
168519
- ||
168520
- (vv === v.next)
168521
- ||
168522
- !pointInTriangle(planeCoords[vv.idx], a, b, c))))
168523
- break;
168524
- ++earIdx;
168525
- }
168526
-
168527
- const ear = vertices[earIdx];
168528
- vertices.splice(earIdx, 1);
168529
-
168530
- baseTriangles.push([ ear.idx, ear.next.idx, ear.prev.idx ]);
168531
-
168532
- const prev = ear.prev;
168533
- prev.next = ear.next;
168534
- const next = ear.next;
168535
- next.prev = ear.prev;
168536
- }
168537
-
168538
- return [ planeCoords, baseTriangles, isCCW ];
168539
- };
168540
-
168541
- const marker3D = function(scene, color) {
168542
- const marker = new Dot3D(scene, {}, scene.canvas.canvas.parentNode, {
168543
- borderColor: "white",
168544
- fillColor: color,
168545
- zIndex: 100
168546
- });
168547
-
168548
- return {
168549
- update: function(worldPos) {
168550
- if (worldPos)
168551
- {
168552
- marker.worldPos = worldPos;
168553
- }
168554
- marker.setVisible(!!worldPos);
168555
- },
168556
-
168557
- setHighlighted: h => marker.setHighlighted(h),
168558
- getCanvasPos: () => marker.canvasPos,
168559
- getWorldPos: () => marker.worldPos,
168560
- destroy: () => marker.destroy()
168561
- };
168562
- };
168563
-
168564
- const wire3D = function(scene, color, startWorldPos) {
168565
- const wire = new Wire3D(scene, scene.canvas.canvas.ownerDocument.body, {
168566
- color: color,
168567
- thickness: 1,
168568
- thicknessClickable: 6
168569
- });
168570
- wire.setVisible(false);
168571
- return {
168572
- update: endWorldPos => {
168573
- if (endWorldPos)
168574
- {
168575
- wire.setEnds(startWorldPos, endWorldPos);
168576
- }
168577
- wire.setVisible(!!endWorldPos);
168578
- },
168579
-
168580
- destroy: () => wire.destroy()
168581
- };
168582
- };
168583
-
168584
169049
  const basePolygon3D = function(scene, color, alpha) {
168585
169050
  let mesh = null;
168586
169051
 
@@ -169560,7 +170025,8 @@ const startPolysurfaceZoneCreateUI = function(scene, zoneAltitude, zoneHeight, z
169560
170025
 
169561
170026
  (function selectNextPoint(markers) {
169562
170027
  const marker = marker3D(scene, zoneColor);
169563
- const wire = (markers.length > 0) && wire3D(scene, zoneColor, markers[markers.length - 1].getWorldPos());
170028
+ const wire = (markers.length > 0) && wire3D(scene, zoneColor);
170029
+ const wireStart = wire && markers[markers.length - 1].getWorldPos();
169564
170030
 
169565
170031
  cleanups.push(() => {
169566
170032
  marker.destroy();
@@ -169620,7 +170086,7 @@ const startPolysurfaceZoneCreateUI = function(scene, zoneAltitude, zoneHeight, z
169620
170086
  () => {
169621
170087
  updatePointerLens(null);
169622
170088
  marker.update(null);
169623
- wire && wire.update(null);
170089
+ wire && wire.update(wireStart, null);
169624
170090
  basePolygon.updateBase((markers.length > 2) ? markers.map(m => m.getWorldPos()) : null);
169625
170091
  },
169626
170092
  (canvasPos, worldPos) => {
@@ -169628,7 +170094,7 @@ const startPolysurfaceZoneCreateUI = function(scene, zoneAltitude, zoneHeight, z
169628
170094
  firstMarker && firstMarker.setHighlighted(!! snappedFirst);
169629
170095
  updatePointerLens(snappedFirst ? snappedFirst.canvasPos : canvasPos);
169630
170096
  marker.update((! snappedFirst) && worldPos);
169631
- wire && wire.update(snappedFirst ? snappedFirst.worldPos : worldPos);
170097
+ wire && wire.update(wireStart, snappedFirst ? snappedFirst.worldPos : worldPos);
169632
170098
  if ((markers.length >= 2))
169633
170099
  {
169634
170100
  const pos = markers.map(m => m.getWorldPos()).concat(snappedFirst ? [] : [worldPos]);
@@ -169672,7 +170138,7 @@ const startPolysurfaceZoneCreateUI = function(scene, zoneAltitude, zoneHeight, z
169672
170138
  else
169673
170139
  {
169674
170140
  marker.update(worldPos);
169675
- wire && wire.update(worldPos);
170141
+ wire && wire.update(wireStart, worldPos);
169676
170142
  selectNextPoint(markers.concat(marker));
169677
170143
  }
169678
170144
  });
@@ -170290,6 +170756,8 @@ exports.ZonesPolysurfaceTouchControl = ZonesPolysurfaceTouchControl;
170290
170756
  exports.ZonesTouchControl = ZonesTouchControl;
170291
170757
  exports.activateDraggableDot = activateDraggableDot;
170292
170758
  exports.activateDraggableDots = activateDraggableDots;
170759
+ exports.addMousePressListener = addMousePressListener;
170760
+ exports.addTouchPressListener = addTouchPressListener;
170293
170761
  exports.buildBoxGeometry = buildBoxGeometry;
170294
170762
  exports.buildBoxLinesGeometry = buildBoxLinesGeometry;
170295
170763
  exports.buildBoxLinesGeometryFromAABB = buildBoxLinesGeometryFromAABB;
@@ -170309,6 +170777,7 @@ exports.getPlaneRTCPos = getPlaneRTCPos;
170309
170777
  exports.isTriangleMeshSolid = isTriangleMeshSolid;
170310
170778
  exports.load3DSGeometry = load3DSGeometry;
170311
170779
  exports.loadOBJGeometry = loadOBJGeometry;
170780
+ exports.marker3D = marker3D;
170312
170781
  exports.math = math;
170313
170782
  exports.meshSurfaceArea = meshSurfaceArea;
170314
170783
  exports.meshVolume = meshVolume;
@@ -170316,9 +170785,12 @@ exports.os = os;
170316
170785
  exports.rtcToWorldPos = rtcToWorldPos;
170317
170786
  exports.sRGBEncoding = sRGBEncoding;
170318
170787
  exports.setFrustum = setFrustum;
170788
+ exports.startPolygonCreate = startPolygonCreate;
170319
170789
  exports.stats = stats;
170320
170790
  exports.touchPointSelector = touchPointSelector;
170321
170791
  exports.transformToNode = transformToNode;
170792
+ exports.triangulateEarClipping = triangulateEarClipping;
170322
170793
  exports.utils = utils;
170794
+ exports.wire3D = wire3D;
170323
170795
  exports.worldToRTCPos = worldToRTCPos;
170324
170796
  exports.worldToRTCPositions = worldToRTCPositions;