import * as THREE from "../../../../libs/three.js/build/three.module.js"; import {BoxVolume} from '../../../utils/VolumeNew.js' import { ClipTask, ClipMethod} from "../../../defines.js" import {mapClipBox} from '../../objects/tool/mapClipBox.js' import Common from '../../utils/Common.js' import math from '../../utils/math.js' import {Images360} from '../panos/Images360.js' const defaultBoxWidth = 16; //navvis: 10 //navvis position: si {x: 0, y: 0, z: 0} var Clip = { bus : new THREE.EventDispatcher, selectedDatasets : [], changeCallback(force){ viewer.controls.setTarget(this.box.position)//绕其旋转 if(Potree.settings.isOfficial){ Common.intervalTool.isWaiting('clipSelectedDatasets', ()=>{ //延时update,防止卡顿 let pointclouds = this.getIntersectPointcloud() if(force || Common.getDifferenceSet(pointclouds,this.selectedDatasets).length){ this.selectedDatasets = pointclouds //console.error('clipSelectedDatasets',selectedDatasets) this.bus.dispatchEvent({type:'updateSelectedDatasets', selectedDatasets:pointclouds.map(e=>e.dataset_id) }) force = false return true } }, 300) } }, enter:function(){ this.previousView = { position: viewer.images360.position, target: viewer.scene.view.getPivot(), displayMode : Potree.settings.displayMode, //--- ifShowMarker : Potree.settings.ifShowMarker, } let pointcloud = this.getPointcloud() let bound = pointcloud.bound //只选取其中一个数据集的bound,而非整体,是因为担心两个数据集中间有空隙,于是刚好落在没有点云的地方。 let boundSize = bound.getSize(new THREE.Vector3()) let target = this.getTarget(bound.getCenter(new THREE.Vector3())); //navvis的位置xy是用相机位置 this.ViewService.mainView.getCamera().position 我觉得也可以用第一个漫游点的,或者最接近bound中心的漫游点 let scale = new THREE.Vector3(defaultBoxWidth,defaultBoxWidth, boundSize.z)//z和navvis一样 let eyeDir = viewer.scene.view.direction.clone().setZ(0/* -boundSize.z/3 */).multiplyScalar(-defaultBoxWidth) //为了使所在楼层不变,不修改z //let eyeDir = scale.clone().setZ(boundSize.z/3).multiplyScalar(1.3) let position = new THREE.Vector3().addVectors(target, eyeDir) Potree.settings.displayMode = 'showPointCloud' viewer.setView({ position , target, duration:300, callback:function(){ } }) //viewer.setControls(viewer.orbitControls); viewer.setLimitFar(false) //viewer.setClipState(false) //暂时关闭旧的clipping { this.box = new BoxVolume({ clip:true }) this.box.clipTask = ClipTask['SHOW_INSIDE_Big' /* "SHOW_INSIDE" */] this.box.showBox = false this.box.name = "ClipBox"; this.box.position.copy(target) this.box.scale.copy(scale) //带动mapBox this.box.addEventListener('position_changed',e=>{ this.mapBox.center.setX(this.box.position.x) this.mapBox.center.setY(this.box.position.y) this.mapBox.updatePoints() this.changeCallback() }) this.box.addEventListener('scale_changed',e=>{ var scale = this.box.scale this.mapBox.updatePoints(scale) this.changeCallback() }) this.box.addEventListener('orientation_changed',e=>{ this.mapBox.angle = this.box.rotation.z this.mapBox.rotateBar.rotation.z = this.mapBox.angle this.mapBox.updatePoints() this.changeCallback() }) viewer.scene.addVolume(this.box); } {//map let boxRotateBack = ()=>{//不知道是不是这么写。 因为可能z的旋转不一定都在z this.box.rotation.x = 0; this.box.rotation.y = 0; } this.mapBox = new mapClipBox(target, scale) viewer.mapViewer.scene.add(this.mapBox) //带动box this.mapBox.addEventListener('repos',e=>{ this.box.position.setX(this.mapBox.center.x) this.box.position.setY(this.mapBox.center.y) boxRotateBack() this.changeCallback() }) this.mapBox.addEventListener('dragChange',e=>{ var scale = this.mapBox.getScale() this.box.scale.setX(scale.x) this.box.scale.setY(scale.y) this.box.position.setX(this.mapBox.center.x) this.box.position.setY(this.mapBox.center.y) boxRotateBack() this.changeCallback() }) this.mapBox.addEventListener('rotate',e=>{ this.box.rotation.z = this.mapBox.angle boxRotateBack() this.changeCallback() }) } { //viewer.setClipTask(ClipTask["SHOW_INSIDE"]) } Potree.settings.unableNavigate = true Potree.settings.ifShowMarker = false viewer.updateVisible(viewer.measuringTool.scene, 'clipModel', false) //viewer.updateVisible(viewer.mapViewer.cursor, 'clipModel', false)//隐藏地图游标 viewer.inputHandler.toggleSelection(this.box); viewer.inputHandler.fixSelection = true viewer.transformationTool.frame.material.color.set(Potree.config.clip.color)//navvis 15899953 viewer.setPointStandardMat(true) { let mapVisi = false this.events = { flyToPos : (e)=>{ let dis = 2 let target = e.position //position = new THREE.Vector3().subVectors(target, this.scene.view.direction) //永远朝向框的中心 /* let dir = new THREE.Vector3().subVectors(this.box.position, e.position).normalize() position = new THREE.Vector3().subVectors(target, dir) */ target = this.box.position position = e.position //为了方便缩放操作,直接使用box中心作为target let duration = 1000 viewer.scene.view.setView({position, duration, target}) }, mapVisiChange(e){ mapVisi = e.visible let delay = 100 //因resize了camera需要时间更新projectionMatrix setTimeout(()=>{ let boundingBox = Clip.box.boundingBox.clone().applyMatrix4(Clip.box.matrixWorld) if(mapVisi){//切换地图 if(Clip.switchMapCount == 0 || !Potree.Utils.isInsideFrustum(boundingBox, viewer.mapViewer.camera)){ let size = boundingBox.getSize(new THREE.Vector3) let margin = viewer.mainViewport.resolution.clone().multiplyScalar(0.3) viewer.mapViewer.moveTo(Clip.box.position, size, 100, margin) } Clip.switchMapCount++ //关于究竟是focus box还是dataset有点纠结,又或是两个的union。box和数据集可能离得很远,且无法确定当前想选择的数据集,且数据集可能无floorplan, 即使有可能也不展示…… }else{//切换3d if(!Potree.Utils.isInsideFrustum(boundingBox, viewer.scene.getActiveCamera())){//屏幕上没有box的话 viewer.focusOnObject({boundingBox}, 'boundingBox', 100 ) } } },delay) } } this.switchMapCount = 0 this.bus.addEventListener('flyToPos',this.events.flyToPos) viewer.mapViewer.addEventListener('forceVisible',this.events.mapVisiChange) } this.editing = true setTimeout(()=>{this.changeCallback(true)},1) }, leave:function(){ viewer.inputHandler.fixSelection = false viewer.scene.removeVolume(this.box); this.mapBox.dispose() //viewer.setControls(viewer.fpControls); Potree.settings.unableNavigate = false Potree.settings.ifShowMarker = this.previousView.ifShowMarker viewer.updateVisible(viewer.measuringTool.scene, 'clipModel', true) //viewer.updateVisible(viewer.mapViewer.cursor, 'clipModel', true) viewer.setView(this.previousView) viewer.setLimitFar(true) viewer.setPointStandardMat(false) //viewer.setClipState(true) viewer.controls.setTarget(null) { this.bus.removeEventListener('flyToPos',this.events.flyToPos) viewer.mapViewer.removeEventListener('forceVisible',this.events.mapVisiChange) this.events = null } this.editing = false }, getPointcloud:function(){ //找一个离当前最近的点云,且最好有漫游点 let pointclouds = viewer.scene.pointclouds.filter(e=>e.panos.length>0) if(pointclouds.length == 0)pointclouds = viewer.scene.pointclouds; let result = Common.sortByScore(pointclouds,[],[e=>{ let center = e.bound.getCenter(new THREE.Vector3) let size = e.bound.getSize(new THREE.Vector3).length() / 2 let posToCenter = viewer.images360.position.distanceTo(center) return size / posToCenter }]) return result[0].item }, getTarget:function(boundCenter){//box位置。要找一个有点云的地方。方案1相机位置, 方案2接近相机的漫游点, 方案3接近中心的漫游点。选择方案2,因最大概率有点云 var target = new THREE.Vector3() var cameraPos = viewer.images360.position; var pano = Common.find(viewer.images360.panos , [], [Images360.sortFunctions.floorDisSquaredToPoint(cameraPos)]); if(pano){ target.copy(pano.position) target.setZ(boundCenter.z) }else{ target.copy(boundCenter) } return target }, /* switchMap:function(state){ }, */ download:function( ){ if(this.getIntersectPointcloud().length == 0){ return null } var visiPointclouds = viewer.scene.pointclouds.filter(e=> viewer.getObjVisiByReason(e, 'datasetSelection')) let data = { transformation_matrix: visiPointclouds.map((cloud)=>{ let data = { id: cloud.dataset_id, matrix : this.getTransformationMatrix(cloud).elements, //剪裁大框 VisiMatrixes: cloud.material.clipBoxes_in.map(e=>this.getTransformationMatrix(cloud, e.inverse).elements), //若干个可见型小框(虽然现在用不到了,因为普通界面不展示这些剪裁区域) UnVisiMatrixes: cloud.material.clipBoxes_out.map(e=>this.getTransformationMatrix(cloud, e.inverse).elements), //若干个不可见型小框 modelMatrix:(new THREE.Matrix4).copy(cloud.transformMatrix).transpose().elements } return data }) , aabb: "b-0.5 -0.5 -0.5 0.5 0.5 0.5" //剪裁空间( 所有点在乘上这个矩阵后, 还能落在 1 * 1 * 1的box内的点就是所裁剪的 } return data //https://testlaser.4dkankan.com/indoor/t-ia44BhY/api/pointcloud/crop }, downloadNoCrop(){//不剪裁 下载整个点云 var visiPointclouds = viewer.scene.pointclouds.filter(e=> viewer.getObjVisiByReason(e, 'datasetSelection')) let data = { transformation_matrix: visiPointclouds.map((cloud)=>{ let data = { id: cloud.dataset_id, matrix : new THREE.Matrix4().elements, //固定值 modelMatrix:(new THREE.Matrix4).copy(cloud.transformMatrix).transpose().elements } return data }) , aabb: "b-12742000 -12742000 -12742000 12742000 12742000 12742000" //固定剪裁空间 } console.log(data) return data }, getTransformationMatrix:function(pointcloud, invMatrix) {//剪裁矩阵 var invMatrix = invMatrix || this.box.matrixWorld.clone().invert() return (new THREE.Matrix4).multiplyMatrices(invMatrix, pointcloud.transformMatrix).transpose() }, /* getIntersectPointcloud(){ var boxBound = new THREE.Box3( new THREE.Vector3(-0.5,-0.5,-0.5), new THREE.Vector3(0.5,0.5,0.5), ).applyMatrix4(this.box.matrixWorld) //large boundingbox let boxMatrixInverse = new THREE.Matrix4().copy(this.box.matrixWorld).invert(); let boxPoints = [ new THREE.Vector3(boxBound.min.x, boxBound.min.y,0), new THREE.Vector3(boxBound.max.x, boxBound.min.y,0), new THREE.Vector3(boxBound.max.x, boxBound.max.y,0), new THREE.Vector3(boxBound.min.x, boxBound.max.y,0) ] var intersect = (pointcloud)=>{ if(!pointcloud.bound.intersectsBox(boxBound))return false //判断box和点云的tight bound是否相交(因为box可以任意旋转,且实在找不到三维中的立方体相交的函数,所以直接用boxBound) var points = pointcloud.getUnrotBoundPoint('all') let rings = math.getPolygonsMixedRings([points.slice(0,4), boxPoints] , true) //console.log(pointcloud.dataset_id, pointcloud.name, rings.length) if(rings.length > 1 )return false {//再用frustum和数据集的sphere相交试试,能排除一些错误 let a = Potree.Utils.isIntersectBox(points, this.box.matrixWorld) if(!a){ console.log('没能经过isInsideBox测试') } return a } return true } return viewer.scene.pointclouds.filter(e=>intersect(e)) } */ getIntersectPointcloud(){ var intersect = (pointcloud)=>{ if(pointcloud.intersectBox(this.box.matrixWorld))return true } return viewer.scene.pointclouds.filter(e=>intersect(e)) } } export {Clip} /* 裁剪点云时,2D界面显示全部平面图,按楼层切换显示。 */