123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818 |
- 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'
- import SplitScreen from "../../utils/SplitScreen.js"
- import {ExtendView} from "../../../viewer/ExtendView.js";
- import Viewport from "../../viewer/Viewport.js";
- import {TextSprite} from "../../objects/TextSprite.js";
- import {ExtendPointCloudMaterial} from "../../../materials/ExtendPointCloudMaterial.js";
- import AxisViewer from "../../objects/tool/AxisViewer.js";
-
- const defaultBoxWidth = 16; //navvis: 10
- //navvis position: si {x: 0, y: 0, z: 0}
- const cameraProps = [
- {
- name : 'top',
- axis:["x","y"],
- direction : new THREE.Vector3(0,0,-1), //镜头朝向
- openCount:0,
- },
- {
- name : 'front',
- axis:["x","z"],
- direction : new THREE.Vector3(0,1,0),
- openCount:0,
- },
- {
- name : 'mainView',
- openCount:0,
- }
- ]
-
- var Clip = {
- bus : new THREE.EventDispatcher,
- selectedDatasets : [],
-
- changeCallback(force){
-
- if(this.activeViewName != 'mainView'){
- this.splitScreenTool.updateCameraOutOfModel() //更新相机位置
- }
-
-
-
- if(Potree.settings.isOfficial && this.showMap){
- let fun = ()=>{
- let pointclouds = this.getIntersectPointcloud()
- if( 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) })
-
- }
- }
- if(force)fun()
- else Common.intervalTool.isWaiting('clipSelectedDatasets', fun , 300)
- }
- },
- enter:function(){
- this.initViews()
-
- 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
- /* new AxisViewer(viewer.mainViewport, viewer.renderArea,{domStyle:{
- bottom: '2px',right: '20px', width:'80px',height:'80px'}
- }) */
-
- {
- 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)
- viewer.controls.setTarget(this.box.position)//绕其旋转
- this.splitScreenTool.focusCenter = this.box.position
-
- //带动mapBox
- this.box.addEventListener('position_changed',e=>{
- if(this.showMap){
- 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=>{
- if(this.showMap){
- var scale = this.box.scale
- this.mapBox.updatePoints(scale)
- }
- this.changeCallback()
- })
- this.box.addEventListener('orientation_changed',e=>{
- if(this.showMap){
- 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);
-
- }
-
- if(this.showMap){//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()
- })
- }
-
-
- this.switchView('mainView')
-
- {
- //viewer.setClipTask(ClipTask["SHOW_INSIDE"])
-
- }
-
- Potree.settings.unableNavigate = true
- Potree.settings.ifShowMarker = false
- Potree.Utils.updateVisible(viewer.measuringTool.scene, 'clipModel', false)
- //Potree.Utils.updateVisible(viewer.mapViewer.cursor, 'clipModel', false)//隐藏地图游标
- viewer.inputHandler.toggleSelection(this.box);
- viewer.inputHandler.fixSelection = true
- viewer.transformationTool.setModeEnable(['scale', 'translation', 'rotation'] )
- viewer.transformationTool.frame.material.color.set(Potree.config.clip.color)//navvis 15899953
- viewer.setPointStandardMat(true)
-
- if(this.showMap){
- let mapVisi = false
- this.events = {
- flyToPos : (e)=>{
- let dis = 2
- let target = e.position
-
- target = this.box.position
- position = e.position
- //为了方便缩放操作,直接使用box中心作为target
-
-
- let duration = e.duration == void 0 ? 1000 : e.duration
- 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(viewer.fpVisiDatasets.length == 0){//不会显示任何一个floorplan图,就要就近移动到一个可见数据集里
- let clouds = viewer.scene.pointclouds.filter(e=>e.visible)
- if(clouds.length == 0)clouds = viewer.scene.pointclouds
- let scores = clouds.map((e,i)=>{
- return [ -viewer.scene.view.position.distanceToSquared(e.bound2) , i]
- })
- scores = scores.sort((a,b)=> a[0] - b[0])
- let neareast = clouds[scores[0][1]]
- viewer.flyToDataset({ pointcloud : neareast, duration:0})
- }
-
- if(Clip.switchMapCount == 0 || !Potree.Utils.getPos2d(viewer.scene.view.position, viewer.mapViewer.viewports[0], viewer.mapViewer.renderArea).inSight
- || !Potree.Utils.isInsideFrustum(boundingBox, viewer.mapViewer.camera)){ //要使box框和游标都在屏幕内。因为游标是3d的当前位置,很可能是准备去框住的位置
- let bound = boundingBox.clone()
- bound.expandByPoint(viewer.scene.view.position)
- let size = bound.getSize(new THREE.Vector3)
- let center = bound.getCenter(new THREE.Vector3)
- let margin = viewer.mainViewport.resolution.clone().multiplyScalar(0.3)
- viewer.mapViewer.moveTo(center, 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.showMap && this.mapBox.dispose()
- //viewer.setControls(viewer.fpControls);
- this.switchView('mainView')
- Potree.settings.unableNavigate = false
- Potree.settings.ifShowMarker = this.previousView.ifShowMarker
- Potree.Utils.updateVisible(viewer.measuringTool.scene, 'clipModel', true)
- //Potree.Utils.updateVisible(viewer.mapViewer.cursor, 'clipModel', true)
- viewer.setView(this.previousView)
- viewer.setLimitFar(true)
- viewer.setPointStandardMat(false)
- //viewer.setClipState(true)
- viewer.controls.setTarget(null)
- this.splitScreenTool.focusCenter = null
-
- //viewer.mainViewport.axis.dispose()
-
- cameraProps.forEach(e=>e.openCount = 0)
-
- if(this.showMap){
- this.bus.removeEventListener('flyToPos',this.events.flyToPos)
- viewer.mapViewer.removeEventListener('forceVisible',this.events.mapVisiChange)
-
- this.events = null
- }
- this.editing = false
- },
-
- initViews(){
- if(this.views){
-
- this.views.top.pitch = -Math.PI
- this.views.top.yaw = 0
- this.views.front.pitch = 0
- this.views.front.yaw = 0 //--还原for bugID 44757
-
- return
- }
-
- this.views = {}
- this.cameras = {}
- this.orthoCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
- this.orthoCamera.up.set(0,0,1)
-
-
- this.splitScreenTool = new SplitScreen
- this.targetPlane = viewer.mainViewport.targetPlane = new THREE.Plane()
- this.shiftTarget = viewer.mainViewport.shiftTarget = new THREE.Vector3 //project在targetPlane上的位置
-
-
- for(let i=0;i<2;i++){
- let prop = cameraProps[i];
- let view = new ExtendView()
- this.views[prop.name] = view
- this.cameras[prop.name] = this.orthoCamera
- view.name = prop.name
- 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
- view.zoom && (camera.zoom = view.zoom)
-
-
- viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect left等
-
- view.applyToCamera(camera)
- let ifFocus
- let boxBound = Clip.box.boundingBox.clone().applyMatrix4(Clip.box.matrixWorld)
- if(!Potree.Utils.isInsideFrustum(boxBound, camera)){//屏幕上没有box的话
- ifFocus = true
- }
-
- if(name == 'mainView'){
-
- if(lastView){//2d->3d
-
- }
- if(prop.openCount == 0) ifFocus = false
- //viewer.fpControls.lockKey = false
-
- }else{
-
- 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
-
- if(prop.openCount == 0){//至多执行一次
- ifFocus = true
- }
-
- if(this.lastViewName == 'mainView'){//3d->2d
-
- }else{
-
- }
-
-
-
- ifFocus || this.splitScreenTool.updateCameraOutOfModel() //更新相机位置
-
- //if(name == 'top') viewer.mainViewport.alignment = {rotate:true,translate:true};
- if(name == 'front'){
- //viewer.mainViewport.alignment = {translate:true, rotateSide:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动
- viewer.mainViewport.rotateSide = true
- }else{
- viewer.mainViewport.rotateSide = false
- }
- //viewer.fpControls.lockKey = true
-
- }
-
-
- ifFocus && this.focusOnObject(this.box)
-
-
-
-
- //首次到front最好能自动对准box的长边的角度上
-
- prop.openCount ++;
- },
-
- 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)
- }
- },
-
- transform(type, axis, value ){//使用按键微调
- this.box[type][axis] += value;
- },
-
- setModeEnable(type, enable){
-
- let modes = []
- ;['scale', 'translation', 'rotation'].forEach(e=>{
- if(e == type){
- enable && modes.push(e)
- }else{
- viewer.transformationTool.modesEnabled[e] && modes.push(e)
- }
-
- })
-
- viewer.transformationTool.setModeEnable(modes)
- viewer.dispatchEvent('content_changed')
- },
-
-
- rotateSideCamera(angle){//侧视图绕模型中心水平旋转到的角度 angle: -180 ~ 180
- let diff = THREE.Math.degToRad(angle) - viewer.mainViewport.view.yaw
-
- this.splitScreenTool.rotateSideCamera(viewer.mainViewport, diff)
-
-
- },
- 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 )
- },
- 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=> Potree.Utils.getObjVisiByReason(e, 'datasetSelection'))
- visiPointclouds.sort((a,b)=>{return a.dataset_id-b.dataset_id})//缓存需要固定排序好比较
-
- 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
- },
-
-
-
- downloadNoCrop(){//不剪裁 下载整个点云
-
- var visiPointclouds = viewer.scene.pointclouds.filter(e=> Potree.Utils.getObjVisiByReason(e, 'datasetSelection'))
- visiPointclouds.sort((a,b)=>{return a.dataset_id-b.dataset_id})//缓存需要固定排序好比较
-
- 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 intersect = (pointcloud)=>{
- if(pointcloud.intersectBox(this.box.matrixWorld))return true
- }
- return viewer.scene.pointclouds.filter(e=>intersect(e))
- },
-
-
-
-
- getRulerBound(){//坐标尺边界
- let camera = viewer.mainViewport.camera
- if(!camera.isOrthographicCamera)return
- let w = camera.right / camera.zoom //half
- let h = camera.top / camera.zoom
-
-
- let points = []
- var boundAtCamera = new THREE.Box3()
- Clip.box.children[0].geometry.vertices.forEach(e=>{ //模仿getPosWithFullBound
- let p = e.clone().applyMatrix4(Clip.box.matrixWorld)
- points.push(p)
- let p1 = p.clone().applyMatrix4(camera.matrixWorldInverse);
- boundAtCamera.expandByPoint(p1)
- })
- //需要找出clipbox的bound的左上角,它在标尺中是原点
- let ClipBoxLeftTop = new THREE.Vector2(boundAtCamera.min.x, boundAtCamera.max.y) //相对于相机的位置
- let camPos = new THREE.Vector2(-ClipBoxLeftTop.x, ClipBoxLeftTop.y)//由于ClipBoxLeftTop要变换到原点,所以相机位置就成了ClipBoxLeftTop的相反数, 但因是从上到下所以y再乘-1
-
-
-
- let bound_ = {
- left: camPos.x - w,
- right: camPos.x + w,
- bottom: camPos.y + h, //注意从上到下增大
- top: camPos.y - h,
- }
- //console.log(bound)
- return bound_
- },
-
-
-
- screenshot: async (rulerBound, rulerMargin, unitText='像素 : 米' )=>{ //测绘图下载。顶视图|侧视图
- return new Promise((resolve,reject)=>{
- if(Clip.screenshoting )return reject()
-
-
-
- let visiPointclouds = viewer.scene.pointclouds.filter(e=> Potree.Utils.getObjVisiByReason(e, 'datasetSelection'))
- let camera = viewer.mainViewport.camera
- //if(Clip.activeViewName == 'mainView')return reject()
-
- const maxWidth = 8192, minWidth = Potree.browser.urlHasValue('clipMinWidth',true) || 1024 //图片尺寸最大最小值。
- //Potree.settings.pointDensity = 'ultraHigh'
-
- viewer.inputHandler.deselectAll()
- Clip.box.visible = false
-
- Clip.screenshoting = true
-
-
- let material = new ExtendPointCloudMaterial
- material.pointSizeType = Potree.PointSizeType.FIXED //Potree.PointSizeType
- material.size = Potree.browser.urlHasValue('clipPointSize',true) || 1
- material.uniforms.minSize.value = 0.1
- material.activeAttributeName = 'rgba'
- //material.classification = pointcloud.material.classification;
-
- material.clipBoxBig_in = visiPointclouds[0].material.clipBoxBig_in
- material.shaderNeedsUpdate = true
- material.bigClipInBox = visiPointclouds[0].material.bigClipInBox
-
-
- let materials = visiPointclouds.map(e=>{
- let mat = e.material
- e.material = material
- return mat
- })
-
-
- //根据boundingBox尺寸来定图片尺寸
- let boundingBox = Clip.box.boundingBox.clone().applyMatrix4(Clip.box.matrixWorld)
- let points = []
- var bound = new THREE.Box3()
- Clip.box.children[0].geometry.vertices.forEach(e=>{ //模仿getPosWithFullBound
- let p = e.clone().applyMatrix4(Clip.box.matrixWorld)
- points.push(p)
- let p1 = p.clone().applyMatrix4(camera.matrixWorldInverse);
- bound.expandByPoint(p1)
- })
- let boundSize = bound.getSize(new THREE.Vector3)
- let minZoom = Potree.browser.urlHasValue('clipZoom',true) || 80 //1px = 1个点 = 1cm , 如果一个点都是1cm的话
- //zoom不宜过大或过小,过小点云重叠、画面模糊;过大点云有间隙,纹理就看不清
- //中间部分75-80为佳,文字100,边缘50. 最好支持调节 . 且当下载尺寸变大往往点云也多,可能冲破pointbudget限制
- //let text = `1 : ${(1 / camera.zoom).toFixed(4)}(像素 : 米)`
-
-
-
- let w = boundSize.x * minZoom
- let h = boundSize.y * minZoom
-
- let max = Math.max(w,h)
- let s = 1
- if(max > maxWidth){
- s = maxWidth/max
- }else if(max < minWidth){
- s = minWidth/max
- }
- w *= s, h *= s
- material.size *= s
- s != 1 && console.log('宽度缩放倍数',s)
-
-
- //虽然size>1就是浪费像素,但是太小了视觉效果有点差……可能因为fov不真实。 点云大就像是老式电视机虽然显示像素量少但感觉不出画面模糊,且边缘锐利。
-
-
- w = Math.round(w)
- h = Math.round(h)
-
- let focusObjectInfo = [{boundingBox, points}, 'boundingBox', 0, { dontChangeCamDir:true, dontMoveMap:true, boundScale:1 }]
-
-
- //分块渲染截图,最后拼合图片。需要使每一块的点数不超过pointBudget, 且尺寸不超过4096(会崩溃),过小的话一般不会超过pointBudget,除非是深度较大后排点云多。
- let r = THREE.Math.clamp(Math.sqrt(3.5e7 / boundSize.z) * s, 1024, 4096); //每多少米分一块。 推理 wc*hc = w / r * h / r = zoom * w * s * zoom * h * s / r^2 = xyz / V(常数,每多少立方米会超出pointBudget,就是括号里的数字,给大概值)
- //let r = THREE.Math.clamp(1024 / boundSize.z, 256, 4096);
-
- let wc = w / r //横向块数
- let hc = h / r //纵向块数
- let splitRenderInfo = (wc > 1 || hc > 1) && {wc: Math.ceil(wc), hc:Math.ceil(hc)} //因为结果需要化为整数所以可能不太理想, 分割数过多。 另外如果面积小但是因z过长而超出pointBudget没关系,因后排点云无需绘制
-
- console.log({wc,hc,w,h})
-
- let meterPerPixel = boundSize.x / w
-
- let text = `1 : ${(meterPerPixel).toFixed(4)}(${unitText})`
-
- let beforeScreenshot = ()=>{
- if(rulerBound){
- let ruler = Clip.getRulerBound()
- Object.assign(rulerBound,ruler)
- }
- }
-
- let {getImagePromise, finishPromise} = viewer.startScreenshot({ type: 'default', bgOpacity:0, focusObjectInfo, maxTimeForPointLoad : 3e5, pointDensity:'screenshot2', splitRenderInfo, beforeScreenshot }, w, h, 1 )
- finishPromise.done(({dataUrl}) => {
-
- viewer.inputHandler.toggleSelection(Clip.box);
- Clip.box.visible = true
-
- let img = new Image
- img.src = dataUrl
- img.onload = async ()=>{//加上标尺比例水印
-
- //固定文字大小和边距像素 //如果有一边过小,可能超出画面外,暂时不管这种可能
- const fontsize = math.linearClamp(w, [100, 700, 8000], [12, 20, 30])
-
- const marginSelf = {//img外的margin(标尺内)
- left : rulerBound ? 76 : 40,
- right : rulerBound ? 30 : 40,
- bottom : rulerBound ? 20 + fontsize*2 : 40 + fontsize*2,
- top : rulerBound ? 60 : 40,
- }
- //const Margin = rulerMargin + marginSelf
-
-
- //文字的边距
- let bottomRatioToImg = - Math.min(fontsize*2.5, rulerBound ? marginSelf.bottom : marginSelf.bottom*0.9 ) / h//- (marginSelf.bottom - fontsize)/ h //在img之下. text底部到img底部的距离
- //let leftRatioToImg = Margin / w
-
-
- let labelInfo = {
- bottomRatioToImg, horizonCenter:true,//leftRatioToImg,
- textColor: rulerBound ? {r: 0, g: 0, b: 0, a: 1} : { r: 255, g: 255, b: 255, a: 1 },
- textBorderColor : { r: 30, g: 30, b: 30, a: 1 },
- textBorderThick : rulerBound ? 0 : 1 ,
- fontsize,
- fontWeight: rulerBound ? 'normal':'Bold',
- outputCanvas : !!rulerBound,
- bgColor: rulerBound ? {r:255,g:255,b:255,a:255}:null
- }
-
- labelInfo.bgMargin = {//img外需要多少margin
- left: marginSelf.left + rulerMargin, //rulerMargin是在标尺外的margin
- bottom : marginSelf.bottom + rulerMargin,
- right: marginSelf.right + rulerMargin,
- top: marginSelf.top + rulerMargin
- }
-
- if(rulerBound){//因为margin 扩大bound
- rulerBound.left -= marginSelf.left * meterPerPixel
- rulerBound.right += marginSelf.right * meterPerPixel
- rulerBound.top -= marginSelf.top * meterPerPixel
- rulerBound.bottom += marginSelf.bottom * meterPerPixel
- }
-
- //console.log('topRatioToImg',topRatioToImg,'leftRatioToImg',leftRatioToImg,'fontsize', labelInfo.fontsize)
-
-
-
- let result = await Potree.Utils.imgAddText(img, text , labelInfo )
-
-
- //Common.downloadFile(finalDataUrl, 'screenshot11.png')
-
- visiPointclouds.forEach((e,i)=>e.material = materials[i])
- Clip.screenshoting = false
- viewer.viewports = [viewer.mainViewport]
-
- resolve(result)
- }
-
-
- })
-
-
-
- })
- /*
- 隐患:无法获知最大可加载的点的数量,也未知需要加载的点的数量。需要掌控好分块数量,避免因pointBudget限制造成周围的点稀疏。
- 加载点云时间过久
-
- */
- return finishPromise
-
-
- }
-
-
-
- }
- export {Clip}
- /*
- 裁剪点云时,2D界面显示全部平面图,按楼层切换显示。
- 所有点云下载后点云所带的本地坐标都是场景显示的local坐标,这样才能看起来一样。
- 且为了使之和坐标页面对应,坐标页面的本地坐标也是local,而非mesh_local.
- //保存box
- viewer.modules.Clip.box.matrix
- //恢复box
- let a = new THREE.Matrix4().fromArray([
- 14.189120116830964,
- 0,
- 0,
- 0,
- 0,
- 9.924790069368392,
- 0,
- 0,
- 0,
- 0,
- 0.8751009568437951,
- 0,
- -7.433819981633984,
- 1.528696446650445,
- 2.9412375879215977,
- 1
- ])
- a.decompose(viewer.modules.Clip.box.position,viewer.modules.Clip.box.quaternion,viewer.modules.Clip.box.scale)
-
- viewer.modules.Clip.rotateSideCamera(-93)
- */
|