|
@@ -0,0 +1,376 @@
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+import * as THREE from "../../../../libs/three.js/build/three.module.js";
|
|
|
+
|
|
|
+import math from "../../utils/math.js"
|
|
|
+import Common from '../../utils/Common.js'
|
|
|
+import {LineDraw, MeshDraw} from "../../utils/DrawUtil.js";
|
|
|
+import {ExtendView} from "../../../viewer/ExtendView.js";
|
|
|
+import Viewport from "../../viewer/Viewport.js";
|
|
|
+import Sprite from "../../objects/Sprite.js";
|
|
|
+import {transitions, easing, lerp} from '../../utils/transitions.js'
|
|
|
+import {TransformControls} from "../../objects/tool/TransformControls.js";
|
|
|
+import SplitScreen from "../../utils/SplitScreen.js"
|
|
|
+//import History from "../../utils/History.js"
|
|
|
+
|
|
|
+const cameraProps = [
|
|
|
+ {
|
|
|
+ name : 'top',
|
|
|
+ axis:["x","y"],
|
|
|
+ direction : new THREE.Vector3(0,0,-1), //镜头朝向
|
|
|
+ openCount:0,
|
|
|
+ }
|
|
|
+]
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+export class Clipping extends THREE.EventDispatcher{ //实时剪裁
|
|
|
+
|
|
|
+ constructor(){
|
|
|
+ super()
|
|
|
+ this.views = {}
|
|
|
+ this.cameras = {}
|
|
|
+ this.orthoCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
|
|
|
+ this.orthoCamera.up.set(0,0,1)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ init(){
|
|
|
+ if(this.inited)return
|
|
|
+ this.initViews()
|
|
|
+ this.inited = true
|
|
|
+ this.prepareRecord = true
|
|
|
+ this.activeViewName = 'mainView'
|
|
|
+
|
|
|
+ this.events = {
|
|
|
+ transfromCallback:(e)=>{//拖拽变化时
|
|
|
+ this.adjustCamHeight()
|
|
|
+ //检测漫游点、回退等
|
|
|
+ /* if(this.prepareRecord){
|
|
|
+ let box = viewer.transformationTool.selection[0]
|
|
|
+ this.history.writeIn({box, matrix:box.matrix.clone()})
|
|
|
+ this.prepareRecord = false
|
|
|
+ } */
|
|
|
+ },
|
|
|
+ /* onTransfromEnd:(e)=>{//拖拽结束、松开
|
|
|
+ this.prepareRecord = true
|
|
|
+ }, */
|
|
|
+ selectCallback:(e)=>{
|
|
|
+ this.adjustCamHeight()
|
|
|
+
|
|
|
+ let unableNavigate = this.activeViewName != 'mainView' || e.selection.length > 0
|
|
|
+ if(Potree.settings.unableNavigate && !unableNavigate){
|
|
|
+ setTimeout(()=>{
|
|
|
+ Potree.settings.unableNavigate = this.activeViewName != 'mainView' || e.selection.length > 0
|
|
|
+ },300)//延迟是因为点击时取消选择后可能立即就会触发flyToPano。 而且有的人喜欢点两下
|
|
|
+ }else Potree.settings.unableNavigate = unableNavigate
|
|
|
+
|
|
|
+
|
|
|
+ },
|
|
|
+ onkeydown:(e)=>{
|
|
|
+ if(e.keyCode == 8 || e.keyCode == 46){// Backspace or Delete
|
|
|
+ viewer.inputHandler.selection[0] && viewer.scene.removeVolume(viewer.inputHandler.selection[0]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /* this.history = new History({ //也可以写到全局,但需要加个判断物品是否存在的函数
|
|
|
+ applyData: (data)=>{
|
|
|
+ if(viewer.scene.volumes.includes(data.box)){
|
|
|
+ data.matrix.decompose( data.box.position, data.box.quaternion, data.box.scale );
|
|
|
+ }else{
|
|
|
+ this.history.undo()//找不到就回退下一个。(直接写这?)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }) */
|
|
|
+ }
|
|
|
+ initViews(){
|
|
|
+
|
|
|
+ this.splitScreenTool = new SplitScreen
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ for(let i=0;i<1;i++){
|
|
|
+ let prop = cameraProps[i];
|
|
|
+ let view = new ExtendView()
|
|
|
+ this.views[prop.name] = view
|
|
|
+ this.cameras[prop.name] = this.orthoCamera
|
|
|
+
|
|
|
+ view.direction = prop.direction
|
|
|
+ }
|
|
|
+ this.views.mainView = viewer.mainViewport.view
|
|
|
+ this.cameras.mainView = viewer.mainViewport.camera
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ switchView(name){//替换view和camera到mainViewport
|
|
|
+ if(this.activeViewName == name)return
|
|
|
+
|
|
|
+ let view = this.views[name]
|
|
|
+ let camera = this.cameras[name]
|
|
|
+ let prop = cameraProps.find(e=>e.name == name)
|
|
|
+
|
|
|
+ let {boundSize, center, boundingBox} = viewer.bound
|
|
|
+ this.lastViewName = this.activeViewName
|
|
|
+ this.activeViewName = name
|
|
|
+ let lastView = this.views[this.lastViewName]
|
|
|
+ let lastCamera = this.cameras[this.lastViewName]
|
|
|
+ viewer.mainViewport.view = view
|
|
|
+ viewer.mainViewport.camera = camera
|
|
|
+ if(lastCamera)lastView.zoom = lastCamera.zoom
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /* if(lastView){//2d->3d
|
|
|
+ view.copy(lastView)
|
|
|
+ } */
|
|
|
+ if(name == 'mainView'){
|
|
|
+ Potree.settings.unableNavigate = false
|
|
|
+ /* viewer.transformationTool.handles['scale.z+'].node.visible = true
|
|
|
+ viewer.transformationTool.handles['scale.z-'].node.visible = true */
|
|
|
+ }else{
|
|
|
+ Potree.settings.unableNavigate = true
|
|
|
+ /* viewer.transformationTool.handles['scale.z+'].node.visible = false
|
|
|
+ viewer.transformationTool.handles['scale.z-'].node.visible = false */
|
|
|
+ if(prop.openCount == 0){//至多执行一次
|
|
|
+ //this.viewportFitBound(name, boundSize, center)
|
|
|
+ this.orthoMoveFit(center, {bound:boundingBox}, 0)
|
|
|
+ this.camHeightOutOfModel = view.position.z //记录下此刻相机高度。
|
|
|
+ }
|
|
|
+ prop.openCount++
|
|
|
+
|
|
|
+ this.adjustCamHeight()
|
|
|
+
|
|
|
+ /* this.targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), center )
|
|
|
+ this.targetPlane.projectPoint(view.position, this.shiftTarget ) //target转换到过模型中心的平面,以保证镜头一定在模型外
|
|
|
+ view.position.copy(this.splitScreenTool.getPosOutOfModel(viewer.mainViewport)) */
|
|
|
+
|
|
|
+ if(view.zoom)camera.zoom = view.zoom//恢复上次的zoom
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect left等
|
|
|
+ if(viewer.inputHandler.selection.length){
|
|
|
+ this.focusOnObject(viewer.inputHandler.selection[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ focusOnObject(box, duration=0){
|
|
|
+ if(this.activeViewName == 'mainView'){
|
|
|
+ viewer.focusOnObject({boundingBox:box.boundingBox.clone().applyMatrix4(box.matrixWorld)}, 'boundingBox', duration)
|
|
|
+ }else{
|
|
|
+ this.orthoMoveFit(box.position, {bound:box.boundingBox.clone().applyMatrix4(box.matrixWorld)}, duration)
|
|
|
+ }
|
|
|
+ this.adjustCamHeight()
|
|
|
+ }
|
|
|
+
|
|
|
+ orthoMoveFit(pos, info, duration){
|
|
|
+ var margin = {x:viewer.mainViewport.resolution.x*0.4, y:viewer.mainViewport.resolution.y*0.4}
|
|
|
+ this.splitScreenTool.viewportFitBound(viewer.mainViewport, info.bound, pos, duration, margin )
|
|
|
+ }
|
|
|
+
|
|
|
+ adjustCamHeight(){
|
|
|
+ if(this.activeViewName != 'top')return
|
|
|
+ let view = this.views.top
|
|
|
+ let height
|
|
|
+
|
|
|
+ if(viewer.inputHandler.selection.length){ //相机高度位于选中的box的顶部
|
|
|
+ let box = viewer.inputHandler.selection[0]
|
|
|
+ height = box.boundingBox.clone().applyMatrix4(box.matrixWorld).max.z;
|
|
|
+
|
|
|
+ }else{
|
|
|
+ height = this.camHeightOutOfModel //显示全部点云
|
|
|
+ }
|
|
|
+ view.position.z = height
|
|
|
+ //console.log('adjustCamHeight',height)
|
|
|
+
|
|
|
+ //缺点:1 会导致缩放很小的时候,transformationTool的轴因放大到了相机背面。(只有scale轴做了处理)
|
|
|
+ //2 无法直接切换 看不到的box,但可以先取消选择
|
|
|
+ //3 但是俯视图中无法切换到被上层盖住的box(不过把俯视图作为辅助,只针对单个box调动的话,问题不大)
|
|
|
+ }
|
|
|
+
|
|
|
+ enter(){
|
|
|
+ this.init()
|
|
|
+ viewer.transformationTool.setModeEnable(['translation'])
|
|
|
+ //viewer.transformationTool.handles['rotation.x'].node.visible = false
|
|
|
+ viewer.transformationTool.frame.material.visible = false //不盖住boxVolume的frame
|
|
|
+ this.targetPlane = viewer.mainViewport.targetPlane = new THREE.Plane()
|
|
|
+ this.shiftTarget = viewer.mainViewport.shiftTarget = new THREE.Vector3 //project在targetPlane上的位置
|
|
|
+
|
|
|
+ this.getAllBoxes().forEach(box=>{
|
|
|
+ viewer.updateVisible(box,'hidden',true) //显现
|
|
|
+ })
|
|
|
+
|
|
|
+ viewer.transformationTool.history.clear()
|
|
|
+
|
|
|
+ viewer.transformationTool.addEventListener('transformed', this.events.transfromCallback)
|
|
|
+ //viewer.transformationTool.addEventListener('stopDrag', this.events.onTransfromEnd)
|
|
|
+ viewer.inputHandler.addEventListener('selection_changed', this.events.selectCallback)
|
|
|
+ viewer.inputHandler.addEventListener('keydown', this.events.onkeydown)
|
|
|
+
|
|
|
+
|
|
|
+ this.setPointLevelAuto()
|
|
|
+ var initialPointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == Potree.settings.originDatasetId)
|
|
|
+ //隐藏 初始数据集以外的数据集
|
|
|
+ viewer.scene.pointclouds.forEach(e=>{
|
|
|
+ if(e.dataset_id!=Potree.settings.originDatasetId){
|
|
|
+ viewer.updateVisible(e,'enterClipping',false)
|
|
|
+ //Potree.settings.floorplanEnables[e.dataset_id] = false
|
|
|
+ e.panos.forEach(pano=>pano.setEnable(false)) //禁止漫游
|
|
|
+
|
|
|
+ }else{
|
|
|
+ viewer.updateVisible(e,'enterClipping',true, 1, 'add')
|
|
|
+ //Potree.settings.floorplanEnables[e.dataset_id] = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ viewer.flyToDataset({ pointcloud : initialPointcloud, duration:0})
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ leave(){
|
|
|
+
|
|
|
+ viewer.transformationTool.setModeEnable(['scale', 'translation', 'rotation'] )
|
|
|
+
|
|
|
+ viewer.transformationTool.frame.material.visible = true //恢复
|
|
|
+ this.switchView( 'mainView' )
|
|
|
+
|
|
|
+
|
|
|
+ this.getAllBoxes().forEach(box=>{
|
|
|
+ viewer.updateVisible(box,'hidden',false)//隐身
|
|
|
+ })
|
|
|
+ viewer.transformationTool.removeEventListener('transformed', this.events.transfromCallback)
|
|
|
+ //viewer.transformationTool.removeEventListener('stopDrag', this.events.onTransfromEnd)
|
|
|
+ viewer.inputHandler.removeEventListener('selection_changed', this.events.selectCallback)
|
|
|
+ //viewer.inputHandler.removeEventListener('keydown', this.events.onkeydown)
|
|
|
+ viewer.transformObject(null)
|
|
|
+ viewer.transformationTool.history.clear()
|
|
|
+
|
|
|
+ //恢复 初始数据集以外的数据集
|
|
|
+ viewer.scene.pointclouds.forEach(e=>{
|
|
|
+ if(e.dataset_id!=Potree.settings.originDatasetId){
|
|
|
+ viewer.updateVisible(e,'enterClipping',true)
|
|
|
+ e.panos.forEach(pano=>pano.setEnable(true))
|
|
|
+ }else{
|
|
|
+ viewer.updateVisible(e,'enterClipping',false, 0, 'cancel')
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ setTranMode(mode){//rotate or translate
|
|
|
+ this.tranMode = mode
|
|
|
+
|
|
|
+ viewer.transformationTool.setModeEnable([mode])
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //问:是否要显示其他数据集
|
|
|
+ setPointLevelAuto(){
|
|
|
+ /*
|
|
|
+
|
|
|
+ let visiCount = viewer.images360.panos.length
|
|
|
+ let maxCount = 200, minCount = 20, minPer = 0.7, maxPer = 1
|
|
|
+ let percent = maxPer - ( maxPer - minPer) * THREE.Math.clamp((visiCount - minCount) / (maxCount - minCount),0,1)
|
|
|
+
|
|
|
+ Potree.settings.UserDensityPercent = percent ---还是不限制了,尤其是平面图希望更细致点,毕竟剪裁主要要看清剪裁的部位。
|
|
|
+
|
|
|
+ */
|
|
|
+
|
|
|
+ viewer.setPointBudget(5*1000*1000); //给个中等到高等之间的质量
|
|
|
+ Potree.settings.sizeFitToLevel = true
|
|
|
+ viewer.setPointLevels()
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ getAllBoxes(){
|
|
|
+ return viewer.scene.volumes.filter(v=>v.clip && v instanceof Potree.BoxVolume )
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ getCalcData(){//给后台矩阵数据,以裁剪点云。
|
|
|
+ let Clip = viewer.modules.Clip //裁剪下载模块
|
|
|
+
|
|
|
+ let data = {
|
|
|
+ transformation_matrix: viewer.scene.pointclouds.filter(p=>p.dataset_id == Potree.settings.originDatasetId).map((cloud)=>{
|
|
|
+ let data = {
|
|
|
+ id: cloud.dataset_id,
|
|
|
+ matrix : new THREE.Matrix4().elements, //参照downloadNoCrop,给默认值,表示没有最外层裁剪
|
|
|
+ VisiMatrixes: cloud.material.clipBoxes_in.filter(e=>!e.box.isNew).map(e=>Clip.getTransformationMatrix(cloud, e.inverse).elements),
|
|
|
+ UnVisiMatrixes: cloud.material.clipBoxes_out.filter(e=>!e.box.isNew).map(e=>Clip.getTransformationMatrix(cloud, e.inverse).elements),
|
|
|
+ modelMatrix:(new THREE.Matrix4).copy(cloud.transformMatrix).transpose().elements
|
|
|
+ }
|
|
|
+ return data
|
|
|
+ }) ,
|
|
|
+ aabb: "b-12742000 -12742000 -12742000 12742000 12742000 12742000" //剪裁空间
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return data
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ saveClipData(){//输出所有的clip volumeBox
|
|
|
+ let oldState = !viewer.clipUnabled;
|
|
|
+ viewer.setClipState(true)
|
|
|
+ let data = this.getAllBoxes().filter(e=>!e.isNew).map(volume=>{
|
|
|
+ return {
|
|
|
+ clipTask: volume.clipTask,
|
|
|
+ position: Potree.Utils.datasetPosTransform({position:volume.position, toDataset: true, datasetId: Potree.settings.originDatasetId}).toArray(),
|
|
|
+ rotation: Potree.Utils.datasetRotTransform({rotation:volume.rotation, toDataset: true, datasetId: Potree.settings.originDatasetId, getRotation:true}).toArray().slice(0,3),
|
|
|
+ scale: volume.scale.toArray(),
|
|
|
+ }
|
|
|
+ })
|
|
|
+ console.log(data)
|
|
|
+ console.log(JSON.stringify(data))
|
|
|
+ viewer.setClipState(oldState)
|
|
|
+ return data
|
|
|
+ }
|
|
|
+
|
|
|
+ loadFromData(data=[]){
|
|
|
+ data.forEach(v=>{
|
|
|
+ let volume = new Potree.BoxVolume({clip:true, clipTask:v.clipTask});
|
|
|
+ volume.scale.fromArray(v.scale);
|
|
|
+ volume.position.fromArray(v.position);
|
|
|
+ volume.rotation.fromArray(v.rotation);
|
|
|
+
|
|
|
+ volume.position.copy(Potree.Utils.datasetPosTransform({position:volume.position, fromDataset: true, datasetId:Potree.settings.originDatasetId}))
|
|
|
+ volume.rotation.copy(Potree.Utils.datasetRotTransform({rotation:volume.rotation, fromDataset: true, datasetId:Potree.settings.originDatasetId, getRotation:true}))
|
|
|
+
|
|
|
+ viewer.scene.addVolume(volume);
|
|
|
+ viewer.volumeTool.scene.add(volume);
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+//注意:实时裁剪只对初始数据集有效
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|