123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- import * as THREE from "../../../../libs/three.js/build/three.module.js";
- import {Utils} from "../../../utils.js";
- import Sprite from '../../objects/Sprite.js'
- import Common from "../../utils/Common.js";
- import browser from '../../utils/browser.js'
- const texLoader = new THREE.TextureLoader()
- const arrowSpacing = 1 //间隔
- const arrowSize = arrowSpacing * 0.5
- const planeGeo = new THREE.PlaneBufferGeometry(1,1);
- const sphereSizeInfo = {
- nearBound : 0.1, farBound:25, minSize : 50, maxSize : 200 //scale:arrowSize, restricMeshScale : true,
- }
- //const arrowsShowingCount = 25; //场景里最多展示多少个箭头
- const arrowShowMinDis = 10
- export class RouteGuider extends THREE.EventDispatcher{
- constructor () {
- super();
-
- this.route = [];
- this.curve = []
- this.scenePoints = []
- this.sceneMeshGroup = new THREE.Object3D;
- this.mapMeshGroup = new THREE.Object3D;
- this.generateDeferred;
- viewer.addEventListener('loadPointCloudDone',this.init.bind(this))
-
- this.lastResult;//保存上一个的结果,以便于反向
- this.datasetIds = [];//起始和终点的datasetId
- }
- init(){
- if(this.inited) return;
-
- let zoom, resolution=new THREE.Vector2;
- viewer.mapViewer.addEventListener('camera_changed', e => {
- if(!this.routeStart || !this.routeEnd) return
- var camera = e.viewport.camera
-
- Common.intervalTool.isWaiting('routeCameraInterval', ()=>{ //延时update,防止卡顿
- if(camera.zoom != zoom || !resolution.equals(e.viewport.resolution)){
- //console.log('updateMapArrows')
- this.updateMapArrows(true)
- zoom = camera.zoom; resolution.copy(e.viewport.resolution)
- return true
- }
- }, browser.isMobile()?500:200)
- })
-
- let lastPos = new THREE.Vector3
- viewer.addEventListener('camera_changed', e => {
- if(!this.routeStart || !this.routeEnd || !e.changeInfo.positionChanged) return
- Common.intervalTool.isWaiting('routeCameraInterval2', ()=>{ //延时update,防止卡顿
- let currPos = viewer.scene.getActiveCamera().position
-
- if(!currPos.equals(lastPos)){
- lastPos.copy(currPos)
- this.updateArrowDisplay()
-
- return true
- }
- }, 1000)
- })
-
-
-
- var polesMats = {
- shadowMat: new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/pano_instruction_bottomMarker.png' )
- }),
- sphereMat : new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/whiteCircle.png' )
- }),
- hatMats:{
- start: new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/pano_instruction_start_route.png' )
- }),
- end: new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/pano_instruction_target_reached.png' )
- })
- }
- }
- polesMats.shadowMat.map.anisotropy = 4
-
- this.poleStart = this.createPole(polesMats, 'start')
- this.poleEnd = this.createPole(polesMats, 'end')
-
- this.sceneMeshGroup.add(this.poleStart)
- this.sceneMeshGroup.add(this.poleEnd)
-
-
- let map = texLoader.load(Potree.resourcePath+'/textures/routePoint_panorama.png' )
- map.anisotropy = 4 // 各向异性过滤 .防止倾斜模糊
- this.arrow = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
- transparent:true,
- depthTest:false,
- map
- }))
- this.arrow.scale.set(arrowSize,arrowSize,arrowSize)
- Potree.Utils.setObjectLayers(this.arrow, 'sceneObjects' )
-
-
- /* this.testArrow = this.arrow.clone();
- this.testArrow.material = this.arrow.material.clone()
- this.testArrow.material.color = 'red' */
-
- this.arrows = new THREE.Object3D;
- this.sceneMeshGroup.add(this.arrows)
-
- Potree.Utils.setObjectLayers(this.sceneMeshGroup, 'sceneObjects' )
- //this.sceneMeshGroup.traverse(e=>e.renderOrder = 90)
-
-
- viewer.scene.scene.add(this.sceneMeshGroup);
- this.sceneMeshGroup.visible = /* this.poleStart.visibile = this.poleEnd.visibile = */ false
-
- //-------------map---------------------
-
- /* this.mapMarkStart = new THREE.Mesh( planeGeo, new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/map_instruction_start_route.png' )
- }))
- this.mapMarkEnd = new THREE.Mesh( planeGeo, new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/map_instruction_target_reached.png' )
- }))
- this.mapMarkStart.renderOrder = this.mapMarkEnd.renderOrder = 2//在箭头之上 */
-
- let map2 = texLoader.load(Potree.resourcePath+'/textures/routePoint_map_fsna.png' )
- this.mapArrowMats = {
- default: new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map:map2,
- }),
-
- fade: new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map:map2,
- opacity:0.4
- }),
- }
-
-
-
- this.mapArrow = new THREE.Mesh( planeGeo, this.mapArrowMats.default)
- this.mapArrow.scale.set(arrowSize,arrowSize,arrowSize)
- this.mapArrows = new THREE.Object3D;
- this.mapArrows.name = 'mapArrows'
-
-
-
- this.mapMeshGroup.add(this.mapArrows)
- this.mapMeshGroup.name = 'mapRouteLayer'
- this.mapMeshGroup.visible = false
-
- viewer.mapViewer.dispatchEvent({type:'add', object:this.mapMeshGroup, name:'route'})
- this.mapArrow.layers.mask = this.mapArrows.layers.mask // 修改成和map中的layer一样的
-
-
-
- viewer.modules.SiteModel.bus.addEventListener('FloorChange',()=>{
- if(this.routeStart && this.routeEnd){
- this.updateOpacityAtMap()
- }
- })
- this.inited = true
- }
-
- updateOpacityAtMap(){//只有当前楼层的透明度为1
- var currentFloor = viewer.modules.SiteModel.currentFloor
- //console.log('updateOpacityAtMap', currentFloor && currentFloor.name)
- const lift = 0.3 // 因为发送请求时用的是floorPosition的高度,而它可能会到画好的floor之下,所以有误差
- this.mapArrows.children.forEach((arrow,index)=>{
- let pos = this.mapPoints[index].clone()
- pos.z += lift
- let inSide = currentFloor && currentFloor.ifContainsPoint(pos)
- arrow.material = inSide ? this.mapArrowMats.default : this.mapArrowMats.fade
- //console.log('arrow',index, arrow.material.opacity)
- })
-
- viewer.mapViewer.dispatchEvent('content_changed')
- }//但是如果楼层刚好只框柱相机位置而没框住地面位置就不好了……
-
-
-
-
-
- createPole(polesMats, name){
- const height = 1.5, sphereCount = 6, shadowSize = 0.5 /* sphereSizeInfo.scale */, sphereSize = 0.05
-
- var group = new THREE.Object3D;
- group.name = 'pole_'+name
- var shadow = new THREE.Mesh(planeGeo, polesMats.shadowMat)
- shadow.scale.set(shadowSize,shadowSize,shadowSize)
- var sliceDis = height / (sphereCount+1);
- group.add(shadow)
-
- for(let i=0;i<sphereCount;i++){
- var sphere = new Sprite({mat: polesMats.sphereMat, renderOrder:3})
- sphere.position.set(0,0,sliceDis*(i+1))
- sphere.scale.set(sphereSize,sphereSize,sphereSize);
- sphere.visible = false
- group.add(sphere)
- }
-
- var hatSphere = new Sprite({mat: polesMats.hatMats[name], sizeInfo:sphereSizeInfo, renderOrder:4})
- sphere.visible = false
- hatSphere.position.set(0,0,height)
- hatSphere.scale.copy(shadow.scale)
- group.add(hatSphere)
- return group
- }
-
-
- addTestArrow(){
-
- }
-
- addArrow(position){
- var arrow = this.arrow.clone()
- arrow.position.copy(position);
- this.arrows.add(arrow);
- }
- addMapArrow(position){
- var mapArrow = this.mapArrow.clone()
- mapArrow.position.copy(position).setZ(0)
- this.mapArrows.add(mapArrow);
- }
-
-
- setArrowDir(arrows,index){
- let arrow = arrows[index]
- var nextOne = arrows[index+1];
- var nextPos = nextOne ? nextOne.position : this.endPolePos //routeEnd
- var direction = new THREE.Vector3().subVectors(arrow.position, nextPos).setZ(0);
- //direction.normalize();
- //console.log(direction.toArray())
- var angle = Math.atan2(direction.y, direction.x ) + Math.PI/2 //Math.PI/2是因为贴图本身箭头方向不朝x
- arrow.rotation.z = angle
- //console.log(angle)
- }
-
-
-
-
-
- setRouteStart(pos, dealZ , datasetId ){
- if(this.routeStart && pos && this.routeStart.equals(pos)) return //可能重复设置
- this.routeStart = pos && new THREE.Vector3().copy(pos)
- if(dealZ && this.routeStart){
- this.routeStart.setZ(this.getZAtMap())
- this.bus && this.bus.emit('reposStartMarker', this.routeStart)
- }
- console.log('setRouteStart',this.routeStart&&this.routeStart.toArray())
-
- this.datasetIds[0] = datasetId
-
- //this.setStartPole(pos)
-
- this.generateRoute()
-
-
- }
-
- setStartPole(pos){
- this.startPolePos = pos
- this.bus && this.bus.emit('reposStartMarker', pos)
- }
-
-
- setRouteEnd(pos, dealZ , datasetId ){
- if(this.routeEnd && pos && this.routeEnd.equals(pos)) return
- this.routeEnd = pos && new THREE.Vector3().copy(pos)
- if(dealZ && this.routeEnd){
- this.routeEnd.setZ(this.getZAtMap())
- this.bus && this.bus.emit('reposEndMarker', this.routeEnd)
- }
- console.log('setRouteEnd',this.routeEnd&&this.routeEnd.toArray())
- this.datasetIds[1] = datasetId
- //this.setEndPole(pos)
- this.generateRoute()
-
- }
-
-
- getZAtMap(){
-
- //找到position.z与当前高度最接近的漫游点
- let result = Common.sortByScore(viewer.images360.panos,[],[e=> -(Math.abs(e.position.z - viewer.images360.position.z)) ])
- let pano = result && result[0] && result[0].item
-
- return pano ? pano.floorPosition.z : viewer.bound.boundingBox.min.z + 1
- //若在平面图上画实在得不到当前楼层的,大概率是楼层画得不好,那就只能去获取当前楼层的了
-
- //navvis的高度取的是主视图所在楼层的中心高度(可能再高些)
-
- }
-
- setEndPole(pos){
- this.endPolePos = pos
- this.bus && this.bus.emit('reposEndMarker', pos)
- }
-
- getSourceProjectionIndex(route) {//真正的起始
- var e = route.findIndex(function(t) {
- return t.instruction && t.instruction.type === 'source_projection_to_navgraph'
- });
- return e < 0 ? 0 : e
- }
- getDestinationProjectionIndex(route) {//真正的终点
- var e = route.findIndex(function(t) {
- return t.instruction && t.instruction.type === "destination_projection_to_navgraph"
- });
- return e < 0 ? route.length - 1 : e
- }
-
- generateRoute(){
- if(!this.routeStart || !this.routeEnd){
-
- return
- }
-
-
- //array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
-
-
- let create = ()=>{
- this.routeLength = this.route.reduce((total, currentValue, currentIndex, arr)=>{
- if(currentIndex == 0)return 0
- return total + currentValue.distanceTo(arr[currentIndex-1]);
- },0)
- let count = Math.max(2,Math.round(this.routeLength / arrowSpacing))//点数
-
- const curve = new THREE.CatmullRomCurve3( this.route );
- curve.curveType = 'chordal'//'centripetal' 'catmullrom'这个可能会超出路径外
- this.curve = curve
-
- const scenePoints = curve.getSpacedPoints( count );//更平均
- //const scenePoints = curve.getPoints( count );
- scenePoints.splice(0,1);//去掉首尾
- scenePoints.pop()
- this.scenePoints = scenePoints
-
- this.updateMapArrows()
- this.displayRoute()
-
- {//map focus on this area
-
- const minBound = new THREE.Vector2(1,1)//针对垂直线,在地图上只有一个点
- let bound = new THREE.Box2;
- this.route.forEach(e=>{
- bound.expandByPoint(e)
- })
- let size = bound.getSize(new THREE.Vector2)
- let markerSize = new THREE.Vector2(115,40) //起始和终点的标识呈长方形
- let areaSize = viewer.mapViewer.viewports[0].resolution2
- let areaArea = areaSize.x * areaSize.y
- if(areaArea> 800 * 400){//是放大的
- markerSize.multiplyScalar(areaArea / (800 * 400) /* / (size.x * size.y) */)
- }
- let margin = size.clone().divide(viewer.mapViewer.viewports[0].resolution2).multiply(markerSize) ///边距 重点是起始和终点的标识占据较大
- size.add(margin)
- let center = bound.getCenter(new THREE.Vector2)
-
- size.x = Math.max(size.x, minBound.x )
- size.y = Math.max(size.y, minBound.y )
- let duration = 1000
- viewer.mapViewer.moveTo(center, size, duration)
- }
-
- this.bus.emit('gotResult', {dis:this.routeLength})
- /* this.generateDeferred && this.generateDeferred.resolve({dis:this.routeLength})
- this.generateDeferred = null */
- }
-
-
- if(Potree.fileServer){
- let dealData = (data)=>{
-
- if(!data.data){
- console.log('没有数据')
- let result
- if(data && data.code == 4002){
- result = data;//正被修改数据集
- }else if(this.routeStart.distanceTo(this.routeEnd) < 1){
- result = { code: 500, msg: '距离太短,无法规划路线' }
- }else{
- result = { code: 500, msg: '超出数据集范围,无法规划路线' }
- }
- this.clearRoute()
- this.setStartPole(this.routeStart)
- this.setEndPole(this.routeEnd)
-
- this.displayRoute() //还是要显示一下起始
- this.bus && this.bus.emit('gotResult', result )
-
- return //this.generateDeferred && this.generateDeferred.resolve()
- }
-
-
- data = data.data
-
- this.clearRoute()
- let length = data.length
-
- if(length < 2){//可能距离太短
- console.log('路径点数为'+length+',直接取起点和终点连线')
- this.route = [this.routeStart, this.routeEnd];
- }else{
- let startIndex = this.getSourceProjectionIndex(data)
- let endIndex = this.getDestinationProjectionIndex(data)
-
-
- let effectiveItems = data.slice(startIndex, endIndex + 1 );//只要点云范围内的点
- effectiveItems.forEach((item,i)=>{
- let pos = viewer.transform.lonlatToLocal.forward(item.location.slice(0))
- pos = new THREE.Vector3().fromArray(pos)//.setZ(item.z)
- this.route.push(pos)
- })
-
- console.log(this.route)
-
-
- }
- this.setStartPole(this.route[0])
- this.setEndPole(this.route[this.route.length-1])
-
- create()
- /*
- distance: 0.17581000000000116
- distance_to_previous: 0.17581000000000116
- id: 567
- instruction: {type: 'source_projection_to_navgraph'}
- latitude: 22.366605927999238
- location: (3) [113.5957510575092, 22.366605927999238, -1.12419]
- longitude: 113.5957510575092
- z: -1.12419
- */
- }
-
-
-
-
- if(this.lastResult && (this.lastResult.data || this.lastResult.data.code != 4002)){//正被修改数据集的话要重新计算
- let data = Common.CloneObject(this.lastResult.data) , use; //直接用上次的结果
- if(this.lastResult.routeStart.equals(this.routeStart) && this.lastResult.routeEnd.equals(this.routeEnd)){//和上次请求相同
- use = true
- }else if(this.lastResult.routeStart.equals(this.routeEnd) && this.lastResult.routeEnd.equals(this.routeStart)){//..反向
- use = true
- if(data.data){
- data.data = this.lastResult.data.data.slice(0).reverse()
- }
- }
- if(use){
- console.log('直接用上次的结果')
- return setTimeout(()=>{dealData(data)}, 1)//延迟是为了等待获得 RouteGuider.generateDeferred
-
- }
-
- }
-
-
-
-
- let start = this.routeStart.clone();
- let end = this.routeEnd.clone();
- let startLonlat = viewer.transform.lonlatToLocal.inverse(start)
- let endLonlat = viewer.transform.lonlatToLocal.inverse(end)
-
- var query = {
- source_longitude: startLonlat.x,
- source_latitude: startLonlat.y,
- source_z: start.z,
- destination_longitude: endLonlat.x,
- destination_latitude: endLonlat.y,
- destination_z: end.z
- };
-
-
- //let url = `/laser/route/${Potree.settings.number}/getRoute/${this.datasetIds[0]}/${this.datasetIds[1]}?`
- let url = `/laser/route/${Potree.settings.number}/getRoute/${Potree.settings.originDatasetId}?`
- for(let i in query){
- url+= (i + '='+ query[i] +'&')
- }
-
- Potree.fileServer.get(url).then((data)=>{
- console.log(data.data)
- if(!this.routeStart || !this.routeEnd)return
-
- this.lastResult = {//保存数据
- routeStart : this.routeStart.clone(),
- routeEnd: this.routeEnd.clone(),
- data,
-
- }
-
- dealData(data)
-
- })
-
-
- }else{
- //创个直线
- /* const sliceDis = 1
- let dis = this.routeStart.distanceTo(this.routeEnd);
- let count = Math.max(2,Math.round(dis / sliceDis))//点数
- let realSlideDis = dis / (count-1);
- let dir = new THREE.Vector3().subVectors(this.routeEnd, this.routeStart).normalize().multiplyScalar(realSlideDis);
- this.route = [this.routeStart];
- for(let i=0;i<count-1;i++){
- let lastOne = this.route[i];
- this.route.push(new THREE.Vector3().addVectors(lastOne,dir))
- }
- this.route.splice(0,1) //route不用包含收尾 */
- this.clearRoute()
- this.route = [this.routeStart, this.routeEnd]
- create()
-
- }
-
- }
-
- updateMapArrows(ifReset){
- if(this.route.length == 0)return
- var zoom = viewer.mapViewer.camera.zoom
- let isBig = viewer.mapViewer.viewports[0].resolution.y > 300
- let count = Math.max(2,Math.round(this.routeLength * zoom / arrowSpacing / (isBig?35:30)))//点数
-
- if(count == this.mapPoints.length+1)return//没变
- const mapPoints = this.curve.getSpacedPoints( count );
- mapPoints.splice(0,1);//去掉首尾
- mapPoints.pop()
- this.mapPoints = mapPoints
-
-
- var scale = (isBig ? 26 : 22)/zoom
- this.mapArrow.scale.set(scale,scale,scale)
- /* this.mapMarkStart.scale.set(scale,scale,scale)
- this.mapMarkEnd.scale.set(scale,scale,scale) */
-
-
- if(ifReset){//因为缩放而重新排布箭头
- this.clearRoute({resetMap:true})
- this.displayRoute({resetMap:true})
- }
- this.updateOpacityAtMap()
- }
-
-
- updateArrowDisplay(){//根据当前位置更新显示一定范围内的箭头
-
- if(this.scenePoints.length == 0)return
-
- /* var a = Common.sortByScore(this.scenePoints , null, [(point)=>{ //是否还要再requires里限制最远距离?
- var playerPos = viewer.scene.getActiveCamera().position.clone().setZ(0)
-
- var pos = point.clone().setZ(0)
-
- return -pos.distanceTo(playerPos);
-
- }]);
- //获得展示的起始点
- let start = a[0].item
- let startIndex = this.scenePoints.indexOf(start)
- this.arrows.children.forEach((e,i)=>{
- if(i<startIndex || i>startIndex+arrowsShowingCount)e.visible = false
- else e.visible = true
- }) */
-
- let cameraPos = viewer.scene.getActiveCamera().position
- this.arrows.children.forEach((e,i)=>{
- if(e.position.distanceTo(cameraPos) < arrowShowMinDis) e.visible = true
- else e.visible = false
- })
-
- viewer.dispatchEvent('content_changed')
- }
-
-
- displayRoute(o={}){
- if(!o.resetMap){
-
- this.poleStart.position.copy(this.startPolePos || this.routeStart)
- this.poleEnd.position.copy(this.endPolePos || this.routeEnd)
- /* this.mapMarkStart.position.copy(this.routeStart).setZ(0)
- this.mapMarkEnd.position.copy(this.routeEnd).setZ(0) */
- this.scenePoints.forEach(e=>this.addArrow(e))
- this.arrows.children.forEach((e,i)=>this.setArrowDir(this.arrows.children,i));
- }
- this.sceneMeshGroup.traverse(e=>e.visible = true)
- this.mapMeshGroup.visible = true
- this.mapPoints.forEach(e=>this.addMapArrow(e))
- this.mapArrows.children.forEach((e,i)=>this.setArrowDir(this.mapArrows.children,i));
- viewer.mapViewer.dispatchEvent({'type':'content_changed'})
- this.updateArrowDisplay()
- }
-
- clearRoute(o={}){
- if(!o.resetMap){
- this.routeLength = 0
- this.route = []
- this.scenePoints = []
- this.mapPoints = []
- let arrows = this.arrows.children.slice(0)
- arrows.forEach(e=>{
- this.arrows.remove(e)
- })
- }
-
- let mapArrows = this.mapArrows.children.slice(0)
- mapArrows.forEach(e=>{
- this.mapArrows.remove(e)
- })
-
- this.sceneMeshGroup.traverse(e=>e.visible = false) //包括sprite也要设置,防止update
- this.mapMeshGroup.visible = false
- viewer.mapViewer.dispatchEvent({'type':'content_changed'})
- viewer.dispatchEvent('content_changed')
- }
-
- clear(){//退出
- console.log('导航clear')
- this.routeStart = null
- this.routeEnd = null
- this.clearRoute()
-
- }
- }
- //大概每十米要花一秒
- /*
- 存在的问题:
- 路径不准确。起始点和终点偏移。
- https://uat-laser.4dkankan.com/routeDebug/ 可查整个map的通路点位图
-
-
- */
|