From 53c896e91e081be40067271a83d35169746a72eb Mon Sep 17 00:00:00 2001 From: leonopulos Date: Tue, 22 Jun 2021 22:12:53 +0200 Subject: [PATCH] Make Z up/down axis and adjust Lonov Also slimming down of ViewerPanoAPI --- src/css/main.css | 8 +- src/js/viewer/ViewerAPI.js | 17 ++-- src/js/viewer/ViewerImage.js | 7 +- src/js/viewer/ViewerMapAPI.js | 2 +- src/js/viewer/ViewerPanoAPI.js | 180 +++++++++++---------------------- 5 files changed, 71 insertions(+), 143 deletions(-) diff --git a/src/css/main.css b/src/css/main.css index 2e7d34e..5aae541 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -25,9 +25,9 @@ body { #map{ position: fixed; bottom: 1%; - right: 3%; + right: 1%; width: 20%; - height:20%; + height: 20%; color: #fff; } @@ -37,8 +37,8 @@ body { .control-OL{ position: fixed; - bottom:20.5%; - right: 3%; + bottom: 22%; + right: 1%; color: #fff; } diff --git a/src/js/viewer/ViewerAPI.js b/src/js/viewer/ViewerAPI.js index 8e60e1d..2fd7d31 100644 --- a/src/js/viewer/ViewerAPI.js +++ b/src/js/viewer/ViewerAPI.js @@ -81,8 +81,8 @@ export class ViewerAPI { this.floor.currentFloor.viewerImages.forEach(element => { const currLocalPos = this.toLocal(element.pos); - const [dx, dz] = [localPos.x - currLocalPos.x, localPos.z - currLocalPos.z]; - const currDistance = Math.sqrt(dx * dx + dz * dz); + const [dx, dy, dz] = [localPos.x - currLocalPos.x, localPos.y - currLocalPos.y, localPos.z - currLocalPos.z]; + const currDistance = Math.sqrt(dx * dx + dy * dy + dz * dz); if (currDistance < minDistance) { minDistance = currDistance; @@ -142,10 +142,9 @@ export class ViewerAPI { toGlobal(localCoords) { // localCoords : THREE.Vector3 // Local coordinates used by the viewer const globalX = this.floor.origin[0] - ((localCoords.x / 1000) / LON_SCALAR); - const globalY = this.floor.origin[1] - ((-localCoords.z / 1000) / LAN_SCALAR); - const globalZ = localCoords.y - this.floor.currentFloor.z; + const globalY = this.floor.origin[1] - ((localCoords.y / 1000) / LAN_SCALAR); + const globalZ = localCoords.z - this.floor.currentFloor.z; - // the three js scene sees the y axis as the up-down axis so we have to swap with z return [globalX, globalY, globalZ]; // Returns: [Number] : WGS 84 coordinates [longitude, latitude, z] (z value is floorZ + panoZ, where localCoords is just the panoZ) } @@ -156,15 +155,15 @@ export class ViewerAPI { // Distance calculation math taken from here https://www.mkompf.com/gps/distcalc.html // The more accurate calculation breaks the pixel offset on the pre-created maps const dx = LON_SCALAR * (this.floor.origin[0] - globalCoords[0]); - const dz = LAN_SCALAR * (this.floor.origin[1] - globalCoords[1]); + const dy = LAN_SCALAR * (this.floor.origin[1] - globalCoords[1]); return new this.THREE.Vector3( dx * 1000, - globalCoords[2] + this.floor.currentFloor.z, - -dz * 1000); + dy * 1000, + globalCoords[2] + this.floor.currentFloor.z); } - eventMeshTest(x = 0, y = -2, z = 0) { + eventMeshTest(x = 0, y = 0, z = -2) { // visual test, spawn in white sphere at first image position in scene (offset specified by parameters) const sphere = new THREE.SphereGeometry(1 / 5, 10, 10); const testMesh = new THREE.Mesh(sphere, new THREE.MeshBasicMaterial()); diff --git a/src/js/viewer/ViewerImage.js b/src/js/viewer/ViewerImage.js index a30fe78..1323122 100644 --- a/src/js/viewer/ViewerImage.js +++ b/src/js/viewer/ViewerImage.js @@ -16,12 +16,7 @@ export class ViewerImage { this.pos = [panoLon, panoLat, panoZ]; // : [Number] // WGS 84 coordinates [longitude, latitude, z] of this image - // The quaternion data available in the json is not quite compatible with the translation we need in our scene - const threeX = y; - const threeY = z; - const threeZ = x; - - this.orientation = new THREE.Quaternion(threeX, threeY, threeZ, w); + this.orientation = new THREE.Quaternion(x, y, z, w); this.mapOffset; // : [offsetX, offsetY] // in pixels, offset from map png diff --git a/src/js/viewer/ViewerMapAPI.js b/src/js/viewer/ViewerMapAPI.js index 64512aa..16ed698 100644 --- a/src/js/viewer/ViewerMapAPI.js +++ b/src/js/viewer/ViewerMapAPI.js @@ -207,7 +207,7 @@ export class ViewerMapAPI { var lonov = this.viewerViewState.lonov; // temporary using 170 degree for correcting the starting zero degree of 2D map - var direction = -(lonov + 180) * (Math.PI / 180) % 360; + var direction = lonov * (Math.PI / 180) % 360; // remove prvious vector layers diff --git a/src/js/viewer/ViewerPanoAPI.js b/src/js/viewer/ViewerPanoAPI.js index e3e163c..97e9a3b 100644 --- a/src/js/viewer/ViewerPanoAPI.js +++ b/src/js/viewer/ViewerPanoAPI.js @@ -8,35 +8,35 @@ import { EventPosition } from "./EventPosition.js"; export class ViewerPanoAPI { constructor(viewerAPI) { - this.scene = new THREE.Scene(); // three.js scene used by the panorama (3D) viewer - this.camera = new THREE.PerspectiveCamera(DEFAULT_FOV, window.innerWidth / window.innerHeight, 1, 1100); this.viewerImageAPI = viewerAPI.image; this.viewerAPI = viewerAPI; - this.sphereRadius = 10; this.addedLayers = new Set(); // EventMesh and EventLayer objects added via addLayer(); - this.preMeshes = new Set(); // meshes that the mouse pointer is currently over - this.raycaster = new THREE.Raycaster(); - - this.viewerViewState = new ViewerViewState(DEFAULT_FOV, 0, 0); - this.lastViewState; - this.lastMousePos; - //initialize the eventLayer - this.eventLayer = new EventLayer(); + this.scene = new THREE.Scene(); // three.js scene used by the panorama (3D) viewer + this.camera = new THREE.PerspectiveCamera(DEFAULT_FOV, window.innerWidth / window.innerHeight, 1, 1100); + this.camera.up = new THREE.Vector3(0, 0, 1); + this.sphereRadius = 10; + this.preMeshes = new Set(); // meshes that the mouse pointer is currently over - // properties needed for display and depthAtPointer method + // property needed for display method this.loadedMesh = null; - this.depthCanvas = document.createElement("canvas"); - // Two new event listeneres are called to handle *how far* the user drags - this.oPM = (event) => this.onPointerMove(event); - this.oPU = () => this.onPointerUp(); + // property needed for depthAtPointer method + this.depthCanvas = document.createElement("canvas"); + // handeling zooming / panning / moving const panoViewer = document.getElementById('pano-viewer'); + this.viewerViewState = new ViewerViewState(DEFAULT_FOV, 0, 0); + this.lastViewState; + this.lastMousePos; panoViewer.addEventListener('wheel', (event) => this.onDocumentMouseWheel(event)); panoViewer.addEventListener('pointerdown', (event) => this.onPointerDown(event)); panoViewer.addEventListener('dblclick', (event) => this.onDoubleClick(event)); - + // Two new event listeneres are called to handle *how far* the user drags + this.oPM = (event) => this.onPointerMove(event); + this.oPU = () => this.onPointerUp(); + + // handeling EventMesh / EventLayer API integration panoViewer.addEventListener('click', (event) => this.meshCheckClick(event)); panoViewer.addEventListener('contextmenu', (event) => { event.preventDefault(); @@ -56,6 +56,7 @@ export class ViewerPanoAPI { const sphere = new THREE.SphereGeometry(this.sphereRadius, 60, 40); // invert the geometry on the x-axis so that we look out from the middle of the sphere sphere.scale(-1, 1, 1); + sphere.rotateX(Math.PI / 2); // load the 360-panorama image data (highest resolution hardcoded for now) const texturePano = this.viewerAPI.textureLoader.load( @@ -149,8 +150,8 @@ export class ViewerPanoAPI { // handles continues update of the distance mouse moved onPointerMove(event) { const scalingFactor = this.camera.fov / MAX_FOV; - - this.viewerViewState.setLonov((this.lastMousePos[0] - event.clientX) * PAN_SPEED * scalingFactor + this.lastViewState[0]); + + this.viewerViewState.setLonov((event.clientX - this.lastMousePos[0]) * PAN_SPEED * scalingFactor + this.lastViewState[0]); this.viewerViewState.setLatov((event.clientY - this.lastMousePos[1]) * PAN_SPEED * scalingFactor + this.lastViewState[1]); this.viewerAPI.map.show_direction(); @@ -175,17 +176,10 @@ export class ViewerPanoAPI { } onDoubleClick(event) { - const [adjustedLonov, adjustedLatov] = this.getAdjustedViewstate(event); - const MEDIAN_WALKING_DISTANCE = 5; // in meter - // distance to be walked along adjustedHorizontalAngle from current location - const distance = MEDIAN_WALKING_DISTANCE + ((adjustedLatov / 85) * MEDIAN_WALKING_DISTANCE); - - // convertedAngle converted to represent directions like specified in newLocationFromPointAngle - const convertedAngle = (adjustedLonov < 180) ? -adjustedLonov : 360 - adjustedLonov; - const currentPos = this.viewerImageAPI.currentImage.pos; + const newLocalPos = this.getCursorLocation(event); + const newPos = this.viewerAPI.toGlobal(newLocalPos); - const newPos = newLocationFromPointAngle(currentPos[0], currentPos[1], THREE.Math.degToRad(convertedAngle), distance); this.viewerAPI.move(newPos[0], newPos[1], currentPos[2]); this.viewerAPI.propagateEvent("moved", this.viewerImageAPI.currentImage.id, true); @@ -193,17 +187,10 @@ export class ViewerPanoAPI { // ---- event handeling functions for EventMesh / EventLayer API interaction ---- getIntersectingMeshes(event) { - // calculate mouse position in normalized device coordinates - // (-1 to +1) for both components - const mouse = new THREE.Vector2(); - mouse.x = (event.clientX / window.innerWidth) * 2 - 1; - mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; - - // update the picking ray with the camera and mouse position - this.raycaster.setFromCamera(mouse, this.camera); + const raycaster = this.getRaycaster(event); // calculate objects intersecting the picking ray - const intersects = this.raycaster.intersectObjects(this.scene.children); + const intersects = raycaster.intersectObjects(this.scene.children); // include only objects that are added meshes const meshes = []; @@ -286,29 +273,21 @@ export class ViewerPanoAPI { // returns: the depth information (in meter) of the panorama at the current curser position (event.clientX, event.clientY) depthAtPointer(event) { - const [adjustedLonov, adjustedLatov] = this.getAdjustedViewstate(event); - + const raycaster = this.getRaycaster(event); // because depth map is not rotated by quaternion like panorama mesh, the quaternion adjustment need to happen first - const localPos = lonLatToLocal(adjustedLonov, adjustedLatov); - const adjustedQuaternion = localPos.applyQuaternion(this.viewerImageAPI.currentImage.orientation); - const [realLonov, realLatov] = localToLonLat(adjustedQuaternion); + const mappedCursorDirection = raycaster.ray.direction.applyQuaternion(this.viewerImageAPI.currentImage.orientation); + const [cursorLon, cursorLat] = localToLonLat(mappedCursorDirection); + // adjust to calculate pixel offset on image, values in [0;360, -90;90] + const [adjustedLonov, adjustedLatov] = [((180 - cursorLon) + 360) % 360, cursorLat]; + // pixel offsets in depth map at current curser position - const pixelX = Math.trunc((realLonov / 360) * this.depthCanvas.width); - const pixelY = Math.trunc((realLatov + 90) / 180 * this.depthCanvas.height); - - const offsetX = (pixelX >= 2) ? pixelX - 2 : 0; - const offsetY = (pixelY >= 2) ? pixelY - 2 : 0; + const pixelX = Math.trunc((adjustedLonov / 360) * this.depthCanvas.width); + const pixelY = Math.trunc((adjustedLatov + 90) / 180 * this.depthCanvas.height); // convert pixel value to depth information - const use5pixelAvg = false; - let imgData; - if (use5pixelAvg) { - imgData = this.depthCanvas.getContext("2d").getImageData(offsetX, offsetY, 5, 5); - } else { - imgData = this.depthCanvas.getContext("2d").getImageData(pixelX, pixelY, 1, 1); - } - const [red, green, blue, alpha] = averagePixelValues(imgData.data); + const imgData = this.depthCanvas.getContext("2d").getImageData(pixelX, pixelY, 1, 1); + const [red, green, blue, alpha] = imgData.data; // LSB red -> green -> blue MSB (ignore alpha) const distanceMM = red | (green << 8) | (blue << 16); @@ -319,73 +298,28 @@ export class ViewerPanoAPI { // returns the current location of the cursor in the three js scene (Vector3) getCursorLocation(event) { - // param: event.x event.y current cursor position on screen - const [adjustedLonov, adjustedLatov] = this.getAdjustedViewstate(event); - const normalizedLocalViewingDir = lonLatToLocal(adjustedLonov, adjustedLatov); - - // adjust looking direction for offset of current mesh in scene - const localCoord = this.viewerAPI.toLocal(this.viewerImageAPI.currentImage.pos); - - // get distance und extend viewing direction vector by distance - const dist = this.depthAtPointer(event); - - localCoord.addScaledVector(normalizedLocalViewingDir, dist) - - return localCoord; + const raycaster = this.getRaycaster(event); + // formula for position is currentLoc + direction*distance (where the direction is normalized) + const distance = this.depthAtPointer(event); + const cursorLocation = raycaster.ray.origin.addScaledVector(raycaster.ray.direction, distance); + + return cursorLocation; } - // returns [lonov, latov] at the current cursor position - getAdjustedViewstate(event) { - // find correct pixel position on equilateral projected depth map - const halfWidth = window.innerWidth / 2; - const halfHeight = window.innerHeight / 2; - - // horizontal (lonov) : image left -> 0, image right -> 360 - // vertical (latov) : image top -> 85, image bottom -> -85 - const horizontalOffset = (event.clientX - halfWidth) / halfWidth; // scaled between [-1,1] depending how left-right the mouse click is on the screen - const verticalOffset = (halfHeight - event.clientY) / halfHeight; // scaled between [-1,1] depending how up-down the mouse click is on the screen - - const adjustedLonov = ((this.viewerViewState.lonov + (horizontalOffset * this.viewerViewState.fov / 2)) + 360) % 360; - const adjustedLatov = Math.max(-85, Math.min(85, this.viewerViewState.latov + (verticalOffset * this.viewerViewState.fov / 2))); - - return [adjustedLonov, adjustedLatov]; - } - -} - - -// takes in a location (in lot/lat), a direction (as a *angle*[rad, in birds eye view), and a distance (in meters) to move in the direction -const newLocationFromPointAngle = (lon1, lat1, angle, distance) => { - // angle: +-0 -> west, +pi/2 -> south, +-pi -> east, -pi/2 -> north - let lon2, lat2; - - const dx = (distance / 1000) * Math.cos(angle); - const dy = (distance / 1000) * Math.sin(angle); - - lon2 = lon1 - (dx / LON_SCALAR); - lat2 = lat1 - (dy / LAN_SCALAR); - - return [lon2, lat2]; -} - -const averagePixelValues = (data) => { - const pixels = data.length / 4; - let [red, green, blue, alpha] = [0, 0, 0, 0]; // sum of all pixel values + getRaycaster(event) { + // calculate mouse position in normalized device coordinates + // (-1 to +1) for both components + const mouse = new THREE.Vector2(); + const raycaster = new THREE.Raycaster(); - for (let i = 0; i < data.length; i = i + 4) { - red = red + data[i]; - green = green + data[i + 1]; - blue = blue + data[i + 2]; - alpha = alpha + data[i + 3]; + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; + mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(mouse, this.camera); + + return raycaster; } - - // get average by dividing - red = red / pixels; - green = green / pixels; - blue = blue / pixels; - alpha = alpha / pixels; - - return [red, green, blue, alpha]; + } // returns a normalized Vector3 pointing in the direction specified by lonov latov @@ -394,16 +328,16 @@ const lonLatToLocal = (lonov, latov) => { const theta = THREE.MathUtils.degToRad(lonov); const x = Math.sin(phi) * Math.cos(theta); - const y = Math.cos(phi); - const z = Math.sin(phi) * Math.sin(theta); + const y = Math.sin(phi) * Math.sin(theta); + const z = Math.cos(phi); - return new THREE.Vector3(x, y, z); + return new THREE.Vector3(-x, -y, z); } // inverse operation to above const localToLonLat = (vec) => { - const phi = Math.acos(vec.y); - const theta = Math.atan2(vec.z, vec.x); + const phi = Math.acos(vec.z); + const theta = Math.atan2(-vec.y, -vec.x); let latov = THREE.MathUtils.radToDeg(phi); const lonov = (THREE.MathUtils.radToDeg(theta) + 360) % 360;