diff --git a/.gitignore b/.gitignore index a72949aa..98b6bfa6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ # .gitignore -node_modules \ No newline at end of file +.vscode +node_modules +dist \ No newline at end of file diff --git a/src/toolbar/MarqueeSelectionTool.js b/src/toolbar/MarqueeSelectionTool.js index 39cc0532..ec530854 100644 --- a/src/toolbar/MarqueeSelectionTool.js +++ b/src/toolbar/MarqueeSelectionTool.js @@ -179,6 +179,95 @@ export class MarqueeSelectionTool extends Controller { this._marquee[2] = Math.max(canvasMarqueeStartX, canvasMarqueeEndX); this._marquee[3] = Math.max(canvasMarqueeStartY, canvasMarqueeEndY); }); + + // ***** below referenced and modded from above (mouse-event-driven) and xeokit-sdk\src\extras\MarqueePicker\MarqueePickerMouseControl.js + canvas.addEventListener("touchstart", (e) => { // modded + if (!this.getActive() || !this.getEnabled()) { + return; + } + + const input = this.bimViewer.viewer.scene.input; + if (!input.keyDown[input.KEY_CTRL]) { // Clear selection unless CTRL down + scene.setObjectsSelected(scene.selectedObjectIds, false); + } + canvasDragStartX = e.touches[0].pageX; // modded + canvasDragStartY = e.touches[0].pageY; // modded + marqueeStyle.visibility = "visible"; + marqueeStyle.left = `${canvasDragStartX}px`; + marqueeStyle.top = `${canvasDragStartY}px`; + marqueeStyle.width = "0px"; + marqueeStyle.height = "0px"; + marqueeStyle.display = "block"; + canvasMarqueeStartX = e.touches[0].pageX; // modded + canvasMarqueeStartY = e.touches[0].pageY; // modded + isMouseDragging = true; + this.viewer.cameraControl.pointerEnabled = false; // Disable camera rotation + }); + + canvas.addEventListener("touchend", (e) => { // modded + if (!this.getActive() || !this.getEnabled()) { + return; + } + if (!isMouseDragging && !mouseWasUpOffCanvas) { + return + } + + canvasDragEndX = e.changedTouches[0].pageX; // modded + canvasDragEndY = e.changedTouches[0].pageY; // modded + const width = Math.abs(canvasDragEndX - canvasDragStartX); + const height = Math.abs(canvasDragEndY - canvasDragStartY); + marqueeStyle.width = `${width}px`; + marqueeStyle.height = `${height}px`; + marqueeStyle.visibility = "hidden"; + isMouseDragging = false; + this.viewer.cameraControl.pointerEnabled = true; // Enable camera rotation + if (mouseWasUpOffCanvas) { + mouseWasUpOffCanvas = false; + } + if (width > 3 || height > 3) { // Marquee pick if rectangle big enough + this._marqueePick(); + } + }); // Bubbling + + document.addEventListener("touchend", (e) => { // modded + if (!this.getActive() || !this.getEnabled()) { + return; + } + + if (!isMouseDragging) { + return + } + marqueeStyle.visibility = "hidden"; + isMouseDragging = false; + mouseWasUpOffCanvas = true; + this.viewer.cameraControl.pointerEnabled = true; + }, true); // Capturing + + canvas.addEventListener("touchmove", (e) => { // modded + if (!this.getActive() || !this.getEnabled()) { + return; + } + + if (!isMouseDragging) { + return + } + const x = e.changedTouches[0].pageX; // modded + const y = e.changedTouches[0].pageY; // modded + const width = x - canvasDragStartX; + const height = y - canvasDragStartY; + marqueeStyle.width = `${Math.abs(width)}px`; + marqueeStyle.height = `${Math.abs(height)}px`; + marqueeStyle.left = `${Math.min(canvasDragStartX, x)}px`; + marqueeStyle.top = `${Math.min(canvasDragStartY, y)}px`; + canvasMarqueeEndX = e.changedTouches[0].pageX; // modded + canvasMarqueeEndY = e.changedTouches[0].pageY; // modded + const marqueeDir = (canvasMarqueeStartX < canvasMarqueeEndX) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT; + this._setMarqueeDir(marqueeDir); + this._marquee[0] = Math.min(canvasMarqueeStartX, canvasMarqueeEndX) - canvas.parentElement.offsetLeft; // modded : need to manually add parent canvas offset, differet from mouse input + this._marquee[1] = Math.min(canvasMarqueeStartY, canvasMarqueeEndY) - canvas.parentElement.offsetTop; // modded : need to manually add parent canvas offset, differet from mouse input + this._marquee[2] = Math.max(canvasMarqueeStartX, canvasMarqueeEndX) - canvas.parentElement.offsetLeft; // modded : need to manually add parent canvas offset, differet from mouse input + this._marquee[3] = Math.max(canvasMarqueeStartY, canvasMarqueeEndY) - canvas.parentElement.offsetTop; // modded : need to manually add parent canvas offset, differet from mouse input + }); } _setMarqueeDir(marqueeDir) { @@ -254,15 +343,46 @@ export class MarqueeSelectionTool extends Controller { const top = -this._marquee[1] * yCanvasToClip + 1; const near = this.viewer.scene.camera.frustum.near * (NEAR_SCALING * ratio); const far = FAR_PLANE; - math.frustumMat4( - left, - right, - bottom * ratio, - top * ratio, - near, - far, - this._marqueeFrustumProjMat, - ); + + function ratioToMultipler(x) { // smooth curve function for adjusting frustum angle according to canvas proportion + const a = 1.2; + const b = -1.6; + const c = 1.4; + return a * x * x + b * x + c; // AI generated quadratic that pass 3 points : f(1)=1, f(1.5)=1.7 , f(2)=3 + } + + if (ratio < 1) { // use ratio, ratioToMultipler and 3 to adjust selection frustum aperture according to canvas proportion + math.frustumMat4( + left, + right, + bottom * ratio, + top * ratio, + near, + far, + this._marqueeFrustumProjMat, + ); + } else if (ratio < 2) { + math.frustumMat4( + left * ratioToMultipler(ratio), + right * ratioToMultipler(ratio), + bottom * ratio * ratioToMultipler(ratio), + top * ratio * ratioToMultipler(ratio), + near, + far, + this._marqueeFrustumProjMat, + ); + } else { + math.frustumMat4( + left * 3, + right * 3, + bottom * 3 * ratio, + top * 3 * ratio, + near, + far, + this._marqueeFrustumProjMat, + ); + } + setFrustum(this._marqueeFrustum, this.viewer.scene.camera.viewMatrix, this._marqueeFrustumProjMat); } } diff --git a/src/toolbar/OrthoMode.js b/src/toolbar/OrthoMode.js index f34939b4..2c0911ab 100644 --- a/src/toolbar/OrthoMode.js +++ b/src/toolbar/OrthoMode.js @@ -57,6 +57,7 @@ class OrthoMode extends Controller { this._active = active; if (active) { this._buttonElement.classList.add("active"); + this.bimViewer._marqueeSelectionTool.setEnabled(false); // disable marquee selection during ortho mode if (done) { this._enterOrthoMode(() => { this.fire("active", this._active); @@ -68,6 +69,7 @@ class OrthoMode extends Controller { } } else { this._buttonElement.classList.remove("active"); + this.bimViewer._marqueeSelectionTool.setEnabled(true); // enable marquee selection when exit ortho mode if (done) { this._exitOrthoMode(() => { this.fire("active", this._active); diff --git a/src/toolbar/SectionTool.js b/src/toolbar/SectionTool.js index 74a7c003..e7a96116 100644 --- a/src/toolbar/SectionTool.js +++ b/src/toolbar/SectionTool.js @@ -1,6 +1,6 @@ import {Controller} from "../Controller.js"; import {SectionToolContextMenu} from "./../contextMenus/SectionToolContextMenu.js"; -import {math, SectionPlanesPlugin} from "@xeokit/xeokit-sdk/dist/xeokit-sdk.es.js"; +import {PointerCircle, touchPointSelector, math, SectionPlanesPlugin} from "@xeokit/xeokit-sdk/dist/xeokit-sdk.es.js"; /** @private */ class SectionTool extends Controller { // XX @@ -129,15 +129,14 @@ class SectionTool extends Controller { // XX _initSectionMode() { - this._containerElement.addEventListener('mouseup', (e) => { + this.viewer.scene.input.on("mouseclicked", (coor) => { // fix: not create section panes when button up after spin - if (e.which === 1) { - - const coords = getMouseCanvasPos(e); if (!this.getActive() || !this.getEnabled()) { return; } + const coords = coor; // fix: not create section panes when button up after spin + const pickResult = this.viewer.scene.pick({ canvasPos: coords, pickSurface: true // <<------ This causes picking to find the intersection point on the entity @@ -152,9 +151,56 @@ class SectionTool extends Controller { // XX this._sectionPlanesPlugin.showControl(sectionPlane.id); } - } }); + // migrated and modfied from xeokit-sdk : example : SectionPlanesPlugin_createWithTouch + const setupTouchSelector = touchPointSelector( + this.viewer, + new PointerCircle(this.viewer), // needed to indicate tap duration to user + (orig, dir) => { + // find an intersection of a ray from the camera through the scene entities + const pickResult = this.viewer.scene.pick({ + origin: orig, + direction: dir, + pickSurface: true + }); + return pickResult; + }); + + const createPlane = (pickResult) => { // modded + if (pickResult) { + const sectionPlane = this._sectionPlanesPlugin.createSectionPlane({ // modded : use BIMViewer's version + // id: "mySectionPlane" + id, // modded + pos: pickResult.worldPos, + dir: math.mulVec3Scalar(pickResult.worldNormal, -1) + }); + this._sectionPlanesPlugin.showControl(sectionPlane.id); // modded : use BIMViewer's version + } + }; + + const ifSectionToolActive = () => { // modded : added checker function and pass into IIFE + return (!this.getActive() || !this.getEnabled()); + } + + (function setupCreateSectionPlane() { + setupTouchSelector( + () => null, // onCancel do nothing + () => null, // onChange do nothing + (canvasPos, worldPos) => { // onCommit + if (ifSectionToolActive()) { // modded: add condition if section tool is active + setupCreateSectionPlane(); + } else { + if (worldPos) { // if the point selected by user was on top + createPlane(worldPos); // of an attachable surface, then create section plane + setupCreateSectionPlane(); // and move on to the next one + } + } + }); + } + )() + // migrate and modfied from xeokit-sdk : example : SectionPlanesPlugin_createWithTouch + + this._updateSectionPlanesCount(); }