import * as THREE from "../../../../libs/three.js/build/three.module.js"; import SplitScreen4Views from "../../utils/SplitScreen4Views.js" import math from "../../utils/math.js" import History from "../../utils/History.js" import {TransformControls} from "../../objects/tool/TransformControls.js"; import CursorDeal from "../../utils/CursorDeal.js"; var Alignment = { SplitScreen: SplitScreen4Views, handleState:null, //操作状态 'translate'|'rotate' bus: new THREE.EventDispatcher(), selectedClouds:[], /* prepareRecord : true, writeToHistory(pointclouds){ if(!this.prepareRecord)return; this.prepareRecord = false let content = this.getTemp(pointclouds) this.history.writeIn(content) }, undo(){//撤销一步 let last = this.history.pop(); last && last.forEach(item=>{ this.applyTemp(item) }) }, */ applyTemp(item){ var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id+p.name == item.sid) pointcloud.orientationUser = item.orientationUser pointcloud.translateUser = item.translateUser this.setMatrix( pointcloud ) }, getTemp(pointclouds){//记录最近一次保存后的状态,便于恢复 pointclouds = pointclouds || viewer.scene.pointclouds return pointclouds.map(e=>{ return { sid : e.dataset_id+e.name, orientationUser : typeof(e.orientationUser) == 'number' ? e.orientationUser : e.orientationUser.clone() , translateUser : e.translateUser.clone(), } } ) }, getDatasetQuaternion(pointcloud){ let quaternion if(typeof(pointcloud.orientationUser) == 'number' ){ quaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), pointcloud.orientationUser) }else{ quaternion = pointcloud.orientationUser } return quaternion }, getDatasetOrient(pointcloud){ let orientation if(typeof(pointcloud.orientationUser) == 'number' ){ orientation = pointcloud.orientationUser }else{ orientation = math.getYawPitchByQua(pointcloud.orientationUser) } return orientation }, init:function(){ let transfromInfo, sideRotHovered this.history = new History({ applyData: (data)=>{ data.forEach(item=>{ Alignment.applyTemp(item) }) return true }, getData:(pointclouds)=>{ return Alignment.getTemp(pointclouds) } }) {//侧视图旋转 this.transformControls = new TransformControls(viewer.mainViewport.camera, viewer.renderArea,{ //dontHideWhenFaceCamera: true, rotFullCircle:true }) this.transformControls.name = 'Alignment-transformControls' this.transformControls.setSize(0.5) viewer.scene.scene.add(this.transformControls) this.transformControls.setMode('rotate') this.transformControls.setRotateMethod(2) this.transformControls._gizmo.hideAxis = {rotate:['x','z','y'] } this.fakeMarkerForTran = new THREE.Mesh(new THREE.BoxBufferGeometry(0.3,0.3,0.3) , new THREE.MeshBasicMaterial({ color:"#F00", opacity:0.9, transparent:true, visible:false }));//一个看不见的mesh,只是为了让transformControls移动点云 this.fakeMarkerForTran.name = 'fakeMarkerForTran' viewer.scene.scene.add(this.fakeMarkerForTran) let afterMoveCtl = (type)=>{ if(Alignment.afterMoveCtl){ Alignment.afterMoveCtl(type) }else{ if(type == 'position'){ let moveVec = new THREE.Vector3().subVectors(this.fakeMarkerForTran.position, this.fakeMarkerForTran.oldState.position) Alignment.translate(this.selectedClouds[0], moveVec) }else{ let center = this.selectedClouds[0].translateUser; let diffQua = new THREE.Quaternion().multiplyQuaternions(this.fakeMarkerForTran.quaternion, this.fakeMarkerForTran.oldState.quaternion.clone().invert()) CursorDeal.add('rotatePointcloud') //Alignment.rotateAround(center, this.selectedClouds, diffQua) Alignment.rotate(this.selectedClouds[0], null, diffQua) } } this.fakeMarkerForTran.oldState = { position: this.fakeMarkerForTran.position.clone(), quaternion: this.fakeMarkerForTran.quaternion.clone(), } Alignment.history.beforeChange(this.selectedClouds) } this.fakeMarkerForTran.addEventListener('position_changed', afterMoveCtl.bind(this,'position')) this.fakeMarkerForTran.addEventListener("rotation_changed", afterMoveCtl.bind(this,'rotation') ) this.transformControls.addEventListener('mouseUp',()=>{ Alignment.history.afterChange(this.selectedClouds) }) this.transformControls.addEventListener('axisHoveredChange',(e)=>{ if(e.axis && e.mode == 'rotate'){ CursorDeal.add('rotatePointcloud'), sideRotHovered = true }else{ CursorDeal.remove('rotatePointcloud') sideRotHovered = false } }) this.history.addEventListener('undo',()=>{ this.updateFakeMarker() }) } viewer.fpControls.addEventListener("transformPointcloud",(e)=>{ if(e.pointclouds[0].dataset_id == Potree.settings.originDatasetId && Potree.settings.editType != 'pano'){//禁止手动移动初始数据集 return this.bus.dispatchEvent('forbitMoveOriginDataset') } this.history.beforeChange(e.pointclouds) //this.writeToHistory( e.pointclouds ) if(!transfromInfo){ transfromInfo = {pointclouds:e.pointclouds} } if(this.handleState == 'translate'){ e.pointclouds.forEach(cloud=>Alignment.translate(cloud, e.moveVec)) //this.updateFakeMarker() }else if(this.handleState == 'rotate'){ //if(Potree.settings.editType == 'pano'){ //旋转中心是intersectStart的版本 /* let center = e.intersectStart //旋转中心是mousedown的位置 if(e.intersect.equals(center))return if(!transfromInfo){ transfromInfo = { orientationUser : e.pointclouds[0].orientationUser, //vecStart : e.moveVec, // 首次移动向量 只有八个方向,精度太小,所以延迟 pointclouds: e.pointclouds } this.bus.dispatchEvent({type:'rotateStart', startPoint:center}) return }else if(!transfromInfo.vecStart){ let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0) if(vec.length() * e.camera.zoom > 30){ //在屏幕上距离初始点有一定距离后开始算 //console.log('moveVec',vec) transfromInfo.vecStart = vec } */ let center = e.pointclouds[0].translateUser //旋转中心是第一个点云的位置 if(e.intersect.equals(center))return if(!transfromInfo.vecStart){ //transfromInfo.orientationUser = e.pointclouds[0].orientationUser transfromInfo.vecStart = new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0) transfromInfo.lastQua = new THREE.Quaternion() }else{ let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0) let angle = math.getAngle(transfromInfo.vecStart,vec,'z') //let diffAngle = transfromInfo.orientationUser + angle - transfromInfo.pointclouds[0].orientationUser let diffQuaFromStart = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), angle) let diffQua = new THREE.Quaternion().multiplyQuaternions(diffQuaFromStart, transfromInfo.lastQua.clone().invert()) transfromInfo.lastQua.copy(diffQuaFromStart) transfromInfo.pointclouds.forEach(cloud=>{ /* let centerNoTranfrom = Potree.Utils.datasetPosTransform({ toDataset: true, pointcloud:cloud, position: center }) //中心点在数据集中的位置 Alignment.rotate(cloud, null, diffAngle) let centerNow = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:cloud, position: centerNoTranfrom }) //中心点的现在位置 let shift = new THREE.Vector3().subVectors( center, centerNow); //偏移量 Alignment.translate(cloud,shift) //使center还保留在原位 //let centerNow1 = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:transfromInfo.pointcloud, position: centerNoTranfrom }) //中心点的现在位置 */ Alignment.rotateAround(center, cloud, diffQua) }) } //this.bus.dispatchEvent({type:'rotate', endPoint: e.intersect}) /* }else{ //旧版数据集校准只转z轴 let center = e.pointclouds[0].translateUser //移动到的位置就是中心 if(e.intersect.equals(center))return if(!transfromInfo.vecStart){ transfromInfo.orientationUser = e.pointclouds[0].orientationUser transfromInfo.vecStart = new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0) }else{ let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0) let angle = math.getAngle(transfromInfo.vecStart,vec,'z') let diffAngle = transfromInfo.orientationUser + angle - transfromInfo.pointclouds[0].orientationUser Alignment.rotate(transfromInfo.pointclouds[0], null, diffAngle) } } */ } }) viewer.fpControls.addEventListener("end",(e)=>{ transfromInfo && this.history.afterChange(transfromInfo.pointclouds ) transfromInfo = null }) // cursor: let updateCursor = (e)=>{ if(e.drag || !this.editing)return //仅在鼠标不按下时更新: let handleState = Alignment.handleState if(e.hoverViewport.alignment && handleState && e.hoverViewport.alignment[handleState]){ if(handleState == 'translate'){ if( e.intersect && e.intersect.location ){ CursorDeal.add('movePointcloud') }else{ CursorDeal.remove('movePointcloud') } }else if(handleState == 'rotate'){ if( e.intersect && e.intersect.location ){ CursorDeal.add('rotatePointcloud') }else{ CursorDeal.remove('rotatePointcloud') } } }else{ //清空: CursorDeal.remove('movePointcloud') if(!sideRotHovered) CursorDeal.remove('rotatePointcloud') } } viewer.addEventListener('global_mousemove',updateCursor) viewer.addEventListener('global_drop',updateCursor)//拖拽结束 viewer.addEventListener('updateModelBound', (e)=>{ if(this.editing){ this.SplitScreen.updateCameraOutOfModel() } }) }, setMatrix : function(pointcloud){ var vec1 = pointcloud.position //position为数据集内部的偏移,在navvis中对应的是dataset.pointCloudSceneNode的children[0].position var vec2 = pointcloud.translateUser var pos1Matrix = new THREE.Matrix4().setPosition(vec1);//先移动到点云本身应该在的初始位置(在4dkk里和其他应用中都是在这个位置的,也能和漫游点对应上) var rotMatrix = typeof(pointcloud.orientationUser ) == 'number' ? //再旋转 new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), pointcloud.orientationUser ) : new THREE.Matrix4().makeRotationFromQuaternion(pointcloud.orientationUser) var pos2Matrix = new THREE.Matrix4().setPosition(vec2);//最后是平移 var matrix = new THREE.Matrix4().multiplyMatrices(pos2Matrix, rotMatrix); pointcloud.transformMatrix = matrix.clone();//为该数据集的变化矩阵。 对应navvis的m2w_ pointcloud.transformInvMatrix.copy(pointcloud.transformMatrix).invert() pointcloud.rotateMatrix = rotMatrix pointcloud.rotateInvMatrix.copy(rotMatrix).invert() pointcloud.panos.forEach(e=>e.transformByPointcloud()) matrix = new THREE.Matrix4().multiplyMatrices(matrix, pos1Matrix); //matrix.premultiply(viewer.scene.scene.matrix);////////////////////////add pointcloud.matrix = matrix; //pointcloud.matrixWorldNeedsUpdate = true //更新matrixWorld (非计算,直接赋值) pointcloud.updateMatrixWorld(true) if(this.editing){ Alignment.changeCallBack && Alignment.changeCallBack(); } if(pointcloud.spriteNodeRoot){ pointcloud.spriteNodeRoot.matrixWorld.copy(pointcloud.matrixWorld)//.multiplyMatrices(pointcloud.matrixWorld, pointcloud.matrixWorld); } viewer.boundNeedUpdate = true //pointcloud.updateBound() pointcloud.getPanosBound() viewer.dispatchEvent('content_changed') }, rotateAround(center, pointcloud, angle, axis){//绕center点水平转动 let vec1 = new THREE.Vector3().subVectors(pointcloud.translateUser, center); let rotMatrix = typeof(angle) == 'number' ? new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), angle) : new THREE.Matrix4().makeRotationFromQuaternion(angle) let vec2 = vec1.clone().applyMatrix4(rotMatrix) //将到旋转中心的偏差也转动 let vec3 = new THREE.Vector3().subVectors(vec2,vec1); //这个就是多出来的一步translateUser this.rotate(pointcloud, null, angle) this.translate(pointcloud, vec3) //绕点转动就是比普通转动多一步移动到相对center的某个位置。 1 初始点云移动到自己的position; 2 移动一个vec1 3绕原点旋转 4再移动一个原本的translateUser。 绘制出来后发现移动量就是第二步vec旋转后的偏移 }, rotate:function(pointcloud, deg, angle, axis){//绕各自中心水平转动(各自的position) 假设点云位移position后0,0,0就是它的中心了(根据navvis观察这样做是绕同一个点旋转的) let qua if(deg || typeof(angle) == 'number'){ angle = angle != void 0 ? angle : THREE.Math.degToRad(deg) //正逆负顺 }else if(angle instanceof Array){//when init qua = new THREE.Quaternion().fromArray(angle) }else{ qua = angle } if(typeof(pointcloud.orientationUser) == 'number' ){ //pointcloud.orientationUser += angle pointcloud.orientationUser = this.getDatasetQuaternion(pointcloud) } if(!qua){ qua = new THREE.Quaternion().setFromAxisAngle(axis || new THREE.Vector3(0,0,1), angle) } pointcloud.orientationUser.premultiply(qua) //新版全部使用qua Alignment.setMatrix(pointcloud) }, translate:function(pointcloud, vec){ pointcloud.translateUser.add(vec) Alignment.setMatrix(pointcloud) }, enter:function(){ //this.saveTemp() this.originData = this.getTemp() this.SplitScreen.split({alignment:true}) /* viewer.images360.panos.forEach(pano=>{ Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', false) }) */ viewer.viewports.find(e=>e.name == 'mapViewport').alignment = {rotate:true,translate:true}; viewer.viewports.find(e=>e.name == 'right').alignment = {translate:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动 viewer.viewports.find(e=>e.name == 'back').alignment = {translate:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动 viewer.viewports.forEach(e=>{ if(e.name == 'right' || e.name == 'back' ){ e.layersAdd('sideVisi') /* if(e.name == 'right') e.layersAdd('layer1') else if(e.name == 'back') e.layersAdd('layer2') */ } }) Potree.Utils.setObjectLayers(this.transformControls, 'sideVisi' ) /* Potree.Utils.setObjectLayers(this.transformControls, 'layer1' ) Potree.Utils.setObjectLayers(this.transformControls, 'layer2' ) */ { /* viewer.viewports.forEach(e=>{ let oldFun = e.beforeRender e.beforeRender = function(){ oldFun.bind(this)() if(e.name == 'right'){ Alignment.transformControls._gizmo.hideAxis = {rotate:['e','z','y'] } }else if(e.name == 'back'){ Alignment.transformControls._gizmo.hideAxis = {rotate:['e','z','x'] } } } }) */ /* viewer.viewports.find(e=>e.name == 'right').beforeRender = function(){ oldFun.bind(this)() if(this.name == right) } viewer.viewports.find(e=>e.name == 'back').beforeRender = (e)=>{ old2() this.transformControls._gizmo.hideAxis = {rotate:['e','z','x'] } } */ } this.editing = true viewer.updateFpVisiDatasets() }, leave:function(){ this.switchHandle(null) /* this.originData.forEach(e=>{//恢复 var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id == e.id) this.translate(pointcloud, new THREE.Vector3().subVectors(e.translateUser , pointcloud.translateUser)) this.rotate(pointcloud, null, e.orientationUser - pointcloud.orientationUser) }) */ this.originData.forEach(e=>{//恢复 this.applyTemp(e) }) this.SplitScreen.recover() /* viewer.images360.panos.forEach(pano=>{ Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', true) }) */ this.editing = false this.history.clear() viewer.updateFpVisiDatasets() CursorDeal.remove('movePointcloud') CursorDeal.remove('rotatePointcloud') } , switchHandle:function(state){ console.log('switchHandle',state) this.handleState = state //清空: CursorDeal.remove('movePointcloud') CursorDeal.remove('rotatePointcloud') this.bus.dispatchEvent({type:'switchHandle' , state }) this.updateCtlDisplay() }, updateCtlDisplay(){//仅数据集校准执行 if(!this.editing)return let selected = this.handleState == 'rotate' && this.selectedClouds.length > 0 if(selected){ this.updateFakeMarker() }else{ this.transformControls.detach() } }, updateFakeMarker(){ this.transformControls.attach(this.fakeMarkerForTran) let position = this.selectedClouds[0].translateUser let quaternion = this.getDatasetQuaternion(this.selectedClouds[0]) this.fakeMarkerForTran.position.copy(position) this.fakeMarkerForTran.quaternion.copy(quaternion) this.fakeMarkerForTran.oldState = { position: position.clone(), quaternion: quaternion.clone() } } , save: function(){//保存所有数据集的位置和旋转 let callback = ()=>{//保存成功后 this.originData = this.getTemp() //this.saveTemp(); //需要修改 测量线的position。漫游点已经实时修改了 viewer.scene.measurements.forEach(e=>e.transformByPointcloud()) viewer.images360.updateCube(viewer.bound) } let initialPointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == Potree.settings.originDatasetId) var data = viewer.scene.pointclouds.map(e=>{ let pos = viewer.transform.lonlatToLocal.inverse(e.translateUser.clone()) let data = { id: e.dataset_id, quaternion: this.getDatasetQuaternion(e).toArray(), orientation : this.getDatasetOrient(e).yaw, location:[pos.x, pos.y, pos.z + initialPointcloud.datasetData.location[2]], //transformMatrix: e.transformMatrix.elements, } return data }) //data = JSON.stringify(data) //test: 退出后保留结果 if(!Potree.settings.isOfficial){ callback() } return {data, callback} } } /* 关于控制点: 两个控制点只能打在同一个数据集上。传输这两个点的4dkk中的本地坐标和经纬度,后台算出该数据集的旋转平移, 然后其他数据集绕该数据集旋转,并保持相对位置不变。 关于cloud.js tightBoundingBox规定了整个点云应该放置后的边界范围。 boundingBox则是在其基础上变为正方体。 为了达成这个目标,需要将点云进行位移。在没有位移前,点云boundingBox的min是0,0,0(如果没出错的话) 所以要令min变为tightBoundingBox.min, 需要将点云position设置为 tightBoundingBox.min 。 设置完以后整体点云的旋转中心为0,0,0。如果不出错的话0,0,0大概在点云中央附近。 在添加经纬度信息后,非初始数据集又会有一个位置偏移,其旋转中心也跟着一起偏移,如偏移了 1,0,0,则旋转中心就是 1,0,0 但是,有的cloud.js的tightBoundingBox使用的是大地坐标,需要转成普通值 */ export {Alignment}