import * as THREE from "../../../libs/three.js/build/three.module.js"; import {Measure} from "./Measure.js"; import {Utils} from "../../utils.js"; import math from "../../utils/math.js"; import {CameraMode} from "../../defines.js"; import {TextSprite} from '../TextSprite.js' import {ExtendEventDispatcher} from "../../custom/ExtendEventDispatcher.js"; function updateAzimuth(viewer, measure){ if(!measure.showAzimuth)return const azimuth = measure.azimuth; const isOkay = measure.points.length === 2; azimuth.node.visible = isOkay 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 ExtendEventDispatcher{ constructor (viewer) { super(); this.viewer = viewer; this.renderer = viewer.renderer; this.viewer.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.scene = viewer.overlay// this.onRemove = (e) => { e.measurement.dispose()/* 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('camera_changed',(e)=>{ if(e.viewport == viewer.mainViewport ) this.update() }) //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); viewer.addEventListener('resize',this.setSize.bind(this)) } 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); } createMeasureFromData(data){//add const measure = new Measure(data); if(measure.failBuilded){ return } viewer.scene.addMeasurement(measure); if(measure.guideLine)measure.guideLine.visible = false return measure } update(){ return; let camera = this.viewer.scene.getActiveCamera(); let domElement = this.renderer.domElement; let measurements = this.viewer.scene.measurements; // make size independant of distance let mainLabels = [], subLabels = []; for (let measure of measurements) { measure.lengthUnit = this.viewer.lengthUnit; measure.lengthUnitDisplay = this.viewer.lengthUnitDisplay; //measure.update(); updateAzimuth(this.viewer, measure); /* [...measure.markers, ...measure.edgeLabels, measure.areaLabel].forEach(e=>{ e && e.update() }); */ // labels /* let labels = measure.edgeLabels.concat(measure.angleLabels); for(let label of labels){ label.update() if(label.elem.hasClass('sub')){ subLabels.push(label) }else{ mainLabels.push(label) } } // coordinate labels for (let j = 0; j < measure.coordinateLabels.length; j++) { let label = measure.coordinateLabels[j]; label.update() mainLabels.push(label) } if(measure.showArea){ // area label let label = measure.areaLabel; label.update() mainLabels.push(label) } */ /* if(measure.showCircle){ // 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); } */ if(!this.showLabels){ const labels = [ ...measure.sphereLabels, ...measure.angleLabels, measure.circleRadiusLabel, ]; for(const label of labels){ label.visible = false; } } } //this.updateLabelZIndex([{labels:subLabels},{labels:mainLabels}]) } setSize(e){ //e.resolution /* if(Measure.lineMats){ for(var m in Measure.lineMats){ Measure.lineMats[m].resolution.set(e.canvasWidth, e.canvasHeight); } } if(Measure.sphereMats){ for(var s in Measure.sphereMats){ Measure.sphereMats[s].uniforms.resolution.value.set(e.canvasWidth, e.canvasHeight); } } for (let measure of this.viewer.scene.measurements) { measure.edgeLabels.concat(measure.areaLabel).forEach(label=>{ label.sprite.material.uniforms.resolution.value.set(e.canvasWidth, e.canvasHeight); }) } */ } updateLabelZIndex(group){//[{labels:[]},{}] 顺序按照z-index低到高 group.forEach((e,i)=>{ e.base = group[i-1] ? group[i-1].base + group[i-1].labels.length : 0 var labels = e.labels.sort((a,b)=>{ return b.pos2d.z - a.pos2d.z }) labels.forEach((label,index)=>{ $(label.elem).css('z-index', e.base+index) }) }) } editStateChange(e){ //console.log("editStateChange" , e.state) let state = e.state if(!state){ state = viewer.scene.measurements.some(e=>e.isEditing) } if(state){ viewer.dispatchEvent({type:"measureMovePoint"}) }else{ viewer.dispatchEvent({type:"endMeasureMove"}) } //this.editing = } startInsertion (args = {}, callback, cancelFun) { let domElement = this.viewer.renderer.domElement; const pick = (defaul, alternative) => { if(defaul != null){ return defaul; }else{ return alternative; } }; args.showDistances = (args.showDistances === null) ? true : args.showDistances; args.showArea = pick(args.showArea, false); args.showAngles = pick(args.showAngles, false); args.showCoordinates = pick(args.showCoordinates, false); args.showHeight = pick(args.showHeight, false); args.showCircle = pick(args.showCircle, false); args.showAzimuth = pick(args.showAzimuth, false); args.showEdges = pick(args.showEdges, true); args.closed = pick(args.closed, false); args.maxMarkers = pick(args.maxMarkers, Infinity); args.direction = args.direction//add args.type = args.type /* || 'Measurement'; */ args.showGuideLine = pick(args.showGuideLine, false); args.isRect = pick(args.isRect, false); let measure = new Measure(args); this.scene.add(measure); measure.isNew = true this.viewer.dispatchEvent({ type: 'start_inserting_measurement', measure: measure }); measure.addEventListener('editStateChange', this.editStateChange.bind(this)) measure.editStateChange(true) let timer; let endDragFun = (e) => { let length = measure.points.length if (e.button == THREE.MOUSE.LEFT || e.isTouch) { if (length >= measure.maxMarkers) { end({finish:true}); }else{ var marker = measure.addMarker({point:measure.points[length - 1].clone()}) if(args.isRect && measure.markers.length == 3){//marker全可见 measure.addMarker({point:measure.points[0].clone()}) }else{ measure.markers[length].visible = false measure.edges[length].visible = false } measure.edges[length-1].visible = true measure.markers[length-1].visible = true; measure.editStateChange(true) //重新激活reticule状态 marker.isDragging = true measure.continueDrag(marker, e) } } else if (e.button === THREE.MOUSE.RIGHT ) { //触屏怎么取消? if(e.pressDistance < Potree.config.clickMaxDragDis )end(e);//非拖拽的话 else measure.continueDrag(null, e) } }; let end = (e={}) => {//确定、结束 if(!measure.isNew)return if(args.minMarkers != void 0){ if(!e.finish && measure.markers.length<=args.minMarkers){//右键 当个数不够时取消 //this.viewer.scene.removeMeasurement(measure) //cancelFun && cancelFun() //重新开始画 measure.markers[0].removeEventListener('mousedown',end) measure.reDraw() this.viewer.addEventListener('global_click', click, 10) measure.editStateChange(true) return /* if(!Potree.settings.isOfficial) this.viewer.scene.removeMeasurement(measure) else if(e.drag){ //正式版本不允许右键退出, 继续 continueDrag(e.drag.object) measure.editStateChange(true) return } */ } } if (/* !e.finish && */measure.markers.length > 3) { measure.removeMarker(measure.points.length - 1); measure.markers[0].removeEventListener('mouseover', mouseover); measure.markers[0].removeEventListener('mouseleave', mouseleave); measure.markers[0].removeEventListener('click'/* 'mousedown' */,Exit) if(e.byClickMarker && measure.markers.length > 3){//通过点击第一个marker而结束的话,会多一个marker measure.removeMarker(measure.points.length - 1); } } measure.isNew = false let length = measure.points.length if(length){ measure.markers[length-1].visible = true; measure.edges[length-1].visible = true measure.markers.forEach(marker=>{marker.dispatchEvent('addHoverEvent') }) measure.edges.forEach(edge=>{edge.dispatchEvent('addHoverEvent') }) } clearTimeout(timer) this.viewer.removeEventListener('cancel_insertions', Exit); //pressExit && this.viewer.inputHandler.removeEventListener('keydown', pressExit); this.viewer.removeEventListener('global_click', click) this.viewer.removeEventListener('global_mousemove', ifAtWrongPlace) viewer.dispatchEvent({ type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace" }); viewer.inputHandler.dispatchEvent({type:'isMeasuring', v:false, cause:'stopInsertion'} ) e.remove || callback && callback() /* this.viewer.dispatchEvent({ type: 'finish_inserting_measurement', measure: measure }); */ }; let Exit = (e)=>{//强制退出 if(e.measure && e.measure != measure){ return;//若指定了退出的measure但和该measure不一致,就返回 } console.log('Exit: ' + measure.id) if(e.remove){ viewer.scene.removeMeasurement(measure) } measure.editStateChange(false) measure.cannotConfirmNormal = false //一些dropMarker中的句子 measure.guideLine &&(measure.guideLine.visible = false) /* if(this.viewer.inputHandler.drag && !e.remove ){//还未触发drop的话 this.viewer.inputHandler.drag.object.dispatchEvent({ //这句会导致又增一个marker type: 'drop', drag: this.viewer.inputHandler.drag, viewer: this.viewer, pressDistance:0, button : THREE.MOUSE.RIGHT }); } else {*///未结束时添加新的measure时会触发 end({finish:true, remove:e.remove, byClickMarker: e.type == 'click'}) //} this.viewer.inputHandler.drag && (this.viewer.inputHandler.drag.object = null) } this.viewer.addEventListener('cancel_insertions', Exit); /*let pressExit if(!Potree.settings.isOfficial){ pressExit = (e)=>{ if(e.keyCode == 27){//Esc //Exit() //怎么模拟右键???//现由前端发出 } } this.viewer.inputHandler.addEventListener('keydown', pressExit) } */ let mouseover = (e) => { measure.setMarkerSelected(e.object, 'hover', 'single'); }; let mouseleave = (e) => { measure.setMarkerSelected(e.object, 'unhover', 'single'); } let click = (e)=>{//一旦点击就立刻增加两marker if(ifAtWrongPlace(e))return if(e.clickElement)return //如点击label时focusOnObject if(e.button === THREE.MOUSE.RIGHT)return //console.log('measure clicked33', !!e.intersectPoint) //var I = e.intersectPoint && (e.intersectPoint.orthoIntersect || e.intersectPoint.location) var I = e.intersect && (e.intersect.orthoIntersect || e.intersect.location) if(!I){ return measure.dispatchEvent('intersectNoPointcloud') } var atMap = e.drag.dragViewport.name == 'mapViewport' //在地图上测量的首个点按楼层高度(暂时先只按mainViewport相机高度吧,但navvis是按楼层,画在楼层的地面上,可能因为平面图显示的是楼层近地面), if(atMap){ I = I.clone().setZ(viewer.mainViewport.camera.position.z ) } var marker = measure.addMarker({point:I}) marker.isDragging = true this.viewer.inputHandler.startDragging(marker , {endDragFun, notPressMouse:true} ); //notPressMouse代表不是通过按下鼠标来拖拽 e.drag = this.viewer.inputHandler.drag e.drag.endDragFun = endDragFun e.drag.notPressMouse = true //if(!measure.dragMarker(e) || !measure.dropMarker(e))return measure.dragMarker(e) measure.dropMarker(e) if(measure.maxMarkers > 1 ){ measure.markers[1].visible = false measure.edges[1].visible = false } if(measure.maxMarkers>2 && !measure.isRect){ measure.markers[0].addEventListener('mouseover', mouseover); measure.markers[0].addEventListener('mouseleave', mouseleave); measure.markers[0].addEventListener('click'/* 'mousedown' */,Exit) //点击到第一个marker就结束 } this.viewer.removeEventListener('global_click', click)///* global_drop */ //console.log('measure clicked') e.consume && e.consume() return {stopContinue:true}//防止继续执行别的侦听,如flytopano } //点击第n下拥有n+1个marker, n>0 viewer.inputHandler.dispatchEvent({type: 'isMeasuring', v: true, cause:'startInsertion'}) this.viewer.addEventListener('global_click', click, 10)//add importance:10 let ifAtWrongPlace = (e)=>{ if(measure.unableDragAtMap && e.hoverViewport.name == 'mapViewport' ){ if(e.isTouch){ viewer.dispatchEvent({type:'reticule_forbit', v:true}) }else{ viewer.dispatchEvent({ type : "CursorChange", action : "add", name:"polygon_AtWrongPlace" }); } return true }else{ if(e.isTouch){ viewer.dispatchEvent({type:'reticule_forbit',v:false}) }else{ viewer.dispatchEvent({ type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace" }); } } } if(measure.unableDragAtMap){ this.viewer.addEventListener('global_mousemove', ifAtWrongPlace) } this.viewer.scene.addMeasurement(measure); return measure; } render(o={}){ if(this.scene.children.length == 0)return viewer.setCameraLayers(o.camera, ['measure']) if(o.screenshot && this.viewer.ssaaRenderPass.enabled){ //抗锯齿 this.viewer.ssaaRenderPass.sampleLevel = 4 this.viewer.composer.render(this.scene, o.camera ); /* viewer.scene.measurements.forEach(e=>{ //隐藏除了label以外的 e.children.forEach((c)=>{ if(!(c instanceof TextSprite)){ c.visible = false } }) }) */ }else{ this.viewer.renderer.render(this.scene, o.camera ); } } };