import * as THREE from "../../libs/three.js/build/three.module.js"; import {Measure} from "./Measure.js"; import {Utils} from "../utils.js"; import {CameraMode} from "../defines.js"; import { EventDispatcher } from "../EventDispatcher.js"; //old function updateAzimuth(viewer, measure){ const azimuth = measure.azimuth; const isOkay = measure.points.length === 2; azimuth.node.visible = isOkay && measure.showAzimuth; if(!azimuth.node.visible){ return; } const camera = viewer.scene.getActiveCamera(); const renderAreaSize = viewer.renderer.getSize(new THREE.Vector2()); const width = renderAreaSize.width; const height = renderAreaSize.height; const [p0, p1] = measure.points; const r = p0.position.distanceTo(p1.position); const northVec = Utils.getNorthVec(p0.position, r, viewer.getProjection()); const northPos = p0.position.clone().add(northVec); azimuth.center.position.copy(p0.position); azimuth.center.scale.set(2, 2, 2); azimuth.center.visible = false; // azimuth.target.visible = false; { // north azimuth.north.position.copy(northPos); azimuth.north.scale.set(2, 2, 2); let distance = azimuth.north.position.distanceTo(camera.position); let pr = Utils.projectedRadius(1, camera, distance, width, height); let scale = (5 / pr); azimuth.north.scale.set(scale, scale, scale); } { // target azimuth.target.position.copy(p1.position); azimuth.target.position.z = azimuth.north.position.z; let distance = azimuth.target.position.distanceTo(camera.position); let pr = Utils.projectedRadius(1, camera, distance, width, height); let scale = (5 / pr); azimuth.target.scale.set(scale, scale, scale); } azimuth.circle.position.copy(p0.position); azimuth.circle.scale.set(r, r, r); azimuth.circle.material.resolution.set(width, height); // to target azimuth.centerToTarget.geometry.setPositions([ 0, 0, 0, ...p1.position.clone().sub(p0.position).toArray(), ]); azimuth.centerToTarget.position.copy(p0.position); azimuth.centerToTarget.geometry.verticesNeedUpdate = true; azimuth.centerToTarget.geometry.computeBoundingSphere(); azimuth.centerToTarget.computeLineDistances(); azimuth.centerToTarget.material.resolution.set(width, height); // to target ground azimuth.centerToTargetground.geometry.setPositions([ 0, 0, 0, p1.position.x - p0.position.x, p1.position.y - p0.position.y, 0, ]); azimuth.centerToTargetground.position.copy(p0.position); azimuth.centerToTargetground.geometry.verticesNeedUpdate = true; azimuth.centerToTargetground.geometry.computeBoundingSphere(); azimuth.centerToTargetground.computeLineDistances(); azimuth.centerToTargetground.material.resolution.set(width, height); // to north azimuth.centerToNorth.geometry.setPositions([ 0, 0, 0, northPos.x - p0.position.x, northPos.y - p0.position.y, 0, ]); azimuth.centerToNorth.position.copy(p0.position); azimuth.centerToNorth.geometry.verticesNeedUpdate = true; azimuth.centerToNorth.geometry.computeBoundingSphere(); azimuth.centerToNorth.computeLineDistances(); azimuth.centerToNorth.material.resolution.set(width, height); // label const radians = Utils.computeAzimuth(p0.position, p1.position, viewer.getProjection()); let degrees = THREE.Math.radToDeg(radians); if(degrees < 0){ degrees = 360 + degrees; } const txtDegrees = `${degrees.toFixed(2)}°`; const labelDir = northPos.clone().add(p1.position).multiplyScalar(0.5).sub(p0.position); if(labelDir.length() > 0){ labelDir.z = 0; labelDir.normalize(); const labelVec = labelDir.clone().multiplyScalar(r); const labelPos = p0.position.clone().add(labelVec); azimuth.label.position.copy(labelPos); } azimuth.label.setText(txtDegrees); let distance = azimuth.label.position.distanceTo(camera.position); let pr = Utils.projectedRadius(1, camera, distance, width, height); let scale = (70 / pr); azimuth.label.scale.set(scale, scale, scale); } export class MeasuringTool extends EventDispatcher{ constructor (viewer) { super(); this.viewer = viewer; this.renderer = viewer.renderer; this.addEventListener('start_inserting_measurement', e => { this.viewer.dispatchEvent({ type: 'cancel_insertions' }); }); this.showLabels = true; this.scene = new THREE.Scene(); this.scene.name = 'scene_measurement'; this.light = new THREE.PointLight(0xffffff, 1.0); this.scene.add(this.light); this.viewer.inputHandler.registerInteractiveScene(this.scene); this.onRemove = (e) => { this.scene.remove(e.measurement);}; this.onAdd = e => {this.scene.add(e.measurement);}; for(let measurement of viewer.scene.measurements){ this.onAdd({measurement: measurement}); } viewer.addEventListener("update", this.update.bind(this)); viewer.addEventListener("render.pass.perspective_overlay", this.render.bind(this)); viewer.addEventListener("scene_changed", this.onSceneChange.bind(this)); viewer.scene.addEventListener('measurement_added', this.onAdd); viewer.scene.addEventListener('measurement_removed', this.onRemove); } onSceneChange(e){ if(e.oldScene){ e.oldScene.removeEventListener('measurement_added', this.onAdd); e.oldScene.removeEventListener('measurement_removed', this.onRemove); } e.scene.addEventListener('measurement_added', this.onAdd); e.scene.addEventListener('measurement_removed', this.onRemove); } startInsertion (args = {}) { let domElement = this.viewer.renderer.domElement; let measure = new Measure(); this.dispatchEvent({ type: 'start_inserting_measurement', measure: measure }); const pick = (defaul, alternative) => { if(defaul != null){ return defaul; }else{ return alternative; } }; measure.showDistances = (args.showDistances === null) ? true : args.showDistances; measure.showArea = pick(args.showArea, false); measure.showAngles = pick(args.showAngles, false); measure.showCoordinates = pick(args.showCoordinates, false); measure.showHeight = pick(args.showHeight, false); measure.showCircle = pick(args.showCircle, false); measure.showAzimuth = pick(args.showAzimuth, false); measure.showEdges = pick(args.showEdges, true); measure.closed = pick(args.closed, false); measure.maxMarkers = pick(args.maxMarkers, Infinity); measure.name = args.name || 'Measurement'; this.scene.add(measure); let cancel = { removeLastMarker: measure.maxMarkers > 3, callback: null }; let insertionCallback = (e) => { if (e.button === THREE.MOUSE.LEFT) { measure.addMarker(measure.points[measure.points.length - 1].position.clone()); if (measure.points.length >= measure.maxMarkers) { cancel.callback(); } this.viewer.inputHandler.startDragging( measure.spheres[measure.spheres.length - 1]); } else if (e.button === THREE.MOUSE.RIGHT) { cancel.callback(); } }; cancel.callback = e => { if (cancel.removeLastMarker) { measure.removeMarker(measure.points.length - 1); } domElement.removeEventListener('mouseup', insertionCallback, false); this.viewer.removeEventListener('cancel_insertions', cancel.callback); }; if (measure.maxMarkers > 1) { this.viewer.addEventListener('cancel_insertions', cancel.callback); domElement.addEventListener('mouseup', insertionCallback, false); } measure.addMarker(new THREE.Vector3(0, 0, 0)); this.viewer.inputHandler.startDragging( measure.spheres[measure.spheres.length - 1]); this.viewer.scene.addMeasurement(measure); return measure; } update(){ let camera = this.viewer.scene.getActiveCamera(); let domElement = this.renderer.domElement; let measurements = this.viewer.scene.measurements; const renderAreaSize = this.renderer.getSize(new THREE.Vector2()); let clientWidth = renderAreaSize.width; let clientHeight = renderAreaSize.height; this.light.position.copy(camera.position); // make size independant of distance for (let measure of measurements) { measure.lengthUnit = this.viewer.lengthUnit; measure.lengthUnitDisplay = this.viewer.lengthUnitDisplay; measure.update(); updateAzimuth(this.viewer, measure); // spheres for(let sphere of measure.spheres){ let distance = camera.position.distanceTo(sphere.getWorldPosition(new THREE.Vector3())); let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight); let scale = (15 / pr); sphere.scale.set(scale, scale, scale); } // labels let labels = measure.edgeLabels.concat(measure.angleLabels); for(let label of labels){ let distance = camera.position.distanceTo(label.getWorldPosition(new THREE.Vector3())); let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight); let scale = (70 / pr); if(Potree.debug.scale){ scale = (Potree.debug.scale / pr); } label.scale.set(scale, scale, scale); } // coordinate labels for (let j = 0; j < measure.coordinateLabels.length; j++) { let label = measure.coordinateLabels[j]; let sphere = measure.spheres[j]; let distance = camera.position.distanceTo(sphere.getWorldPosition(new THREE.Vector3())); let screenPos = sphere.getWorldPosition(new THREE.Vector3()).clone().project(camera); screenPos.x = Math.round((screenPos.x + 1) * clientWidth / 2); screenPos.y = Math.round((-screenPos.y + 1) * clientHeight / 2); screenPos.z = 0; screenPos.y -= 30; let labelPos = new THREE.Vector3( (screenPos.x / clientWidth) * 2 - 1, -(screenPos.y / clientHeight) * 2 + 1, 0.5 ); labelPos.unproject(camera); if(this.viewer.scene.cameraMode == CameraMode.PERSPECTIVE) { let direction = labelPos.sub(camera.position).normalize(); labelPos = new THREE.Vector3().addVectors( camera.position, direction.multiplyScalar(distance)); } label.position.copy(labelPos); let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight); let scale = (70 / pr); label.scale.set(scale, scale, scale); } // height label if (measure.showHeight) { let label = measure.heightLabel; { let distance = label.position.distanceTo(camera.position); let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight); let scale = (70 / pr); label.scale.set(scale, scale, scale); } { // height edge let edge = measure.heightEdge; let sorted = measure.points.slice().sort((a, b) => a.position.z - b.position.z); let lowPoint = sorted[0].position.clone(); let highPoint = sorted[sorted.length - 1].position.clone(); let min = lowPoint.z; let max = highPoint.z; let start = new THREE.Vector3(highPoint.x, highPoint.y, min); let end = new THREE.Vector3(highPoint.x, highPoint.y, max); let lowScreen = lowPoint.clone().project(camera); let startScreen = start.clone().project(camera); let endScreen = end.clone().project(camera); let toPixelCoordinates = v => { let r = v.clone().addScalar(1).divideScalar(2); r.x = r.x * clientWidth; r.y = r.y * clientHeight; r.z = 0; return r; }; let lowEL = toPixelCoordinates(lowScreen); let startEL = toPixelCoordinates(startScreen); let endEL = toPixelCoordinates(endScreen); let lToS = lowEL.distanceTo(startEL); let sToE = startEL.distanceTo(endEL); edge.geometry.lineDistances = [0, lToS, lToS, lToS + sToE]; edge.geometry.lineDistancesNeedUpdate = true; edge.material.dashSize = 10; edge.material.gapSize = 10; } } { // area label let label = measure.areaLabel; let distance = label.position.distanceTo(camera.position); let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight); let scale = (70 / pr); label.scale.set(scale, scale, scale); } { // radius label let label = measure.circleRadiusLabel; let distance = label.position.distanceTo(camera.position); let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight); let scale = (70 / pr); label.scale.set(scale, scale, scale); } { // edges const materials = [ measure.circleRadiusLine.material, ...measure.edges.map( (e) => e.material), measure.heightEdge.material, measure.circleLine.material, ]; for(const material of materials){ material.resolution.set(clientWidth, clientHeight); } } if(!this.showLabels){ const labels = [ ...measure.sphereLabels, ...measure.edgeLabels, ...measure.angleLabels, ...measure.coordinateLabels, measure.heightLabel, measure.areaLabel, measure.circleRadiusLabel, ]; for(const label of labels){ label.visible = false; } } } } render(){ this.viewer.renderer.render(this.scene, this.viewer.scene.getActiveCamera()); } };