Sfoglia il codice sorgente

fix: Clip截图写完了

xzw 1 anno fa
parent
commit
ebe8696481

+ 1 - 1
examples/4dkk.html

@@ -37,7 +37,7 @@
 		</div>
 		<div id="potree_sidebar_container"> </div>
 	</div>
-	
+    <!-- <script src="https://webapi.amap.com/maps?v=2.0&key=4578048f6e03386759d5609401e738d3"></script> -->
 	<script type="module">
 
 	import * as THREE from "../libs/three.js/build/three.module.js";

+ 4 - 6
src/ExtendPointCloudOctree.js

@@ -271,7 +271,7 @@ export class ExtendPointCloudOctree extends PointCloudOctree{
 			let scene = new THREE.Scene();
 
 			let material = new ExtendPointCloudMaterial();
-			material.activeAttributeName = "indices";
+			material.activeAttributeName = "indices";//indices
 
 			let renderTarget = new THREE.WebGLRenderTarget(
 				1, 1,
@@ -299,9 +299,7 @@ export class ExtendPointCloudOctree extends PointCloudOctree{
 			pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value;
 			pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value;
 			pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value;
-
-			pickMaterial.activeAttributeName = "indices";//indices
-
+ 
 			pickMaterial.size = pointSize;
 			pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value;
 			pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value;
@@ -329,7 +327,7 @@ export class ExtendPointCloudOctree extends PointCloudOctree{
 			parseInt(pickWindowSize), parseInt(pickWindowSize));
              */ //---这段没用 
         pickState.renderTarget.scissor.set(parseInt(pixelPos.x - (pickWindowSize - 1) / 2),  parseInt(pixelPos.y - (pickWindowSize - 1) / 2),parseInt(pickWindowSize), parseInt(pickWindowSize)); 
-        pickState.renderTarget.scissorTest = true
+        pickState.renderTarget.scissorTest = true 
 
 		renderer.state.buffers.depth.setTest(pickMaterial.depthTest);
 		renderer.state.buffers.depth.setMask(pickMaterial.depthWrite);
@@ -344,7 +342,7 @@ export class ExtendPointCloudOctree extends PointCloudOctree{
 			let tmp = this.material;
 			this.material = pickMaterial;
             
-			pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget);
+			pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget); //只绘制ray pick到的部分nodes
             screenshot();
             
 

+ 13 - 4
src/custom/modules/CameraAnimation/CameraAnimation.js

@@ -624,12 +624,21 @@ export class CameraAnimation extends THREE.EventDispatcher{
         frustum.position.copy(frame.position);
         //frustum.lookAt(...frame.target.toArray());
         frustum.rotation.copy(frame.rotation)
+        viewer.dispatchEvent('content_changed')
     }
 
-    changeCallback(){
-        this.posCurve.update()
-        //this.targetCurve.update()
-        this.targets.forEach(e=>e.update()) 
+    changeCallback(i){
+        this.posCurve.update() 
+        //this.targets.forEach(e=>e.update()) 
+        this.updatePathCallback()
+        if(i==void 0){
+            let length = this.posCurve.points.length;
+            for(let i=0; i<length; i++){
+                this.dragPointCallback({index:i})
+            }
+        }else this.dragPointCallback({index:i})
+        
+        
         this.updateFrustum()
     }
 

+ 371 - 76
src/custom/modules/clipModel/Clip.js

@@ -5,17 +5,51 @@ 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";
+
+
+
 
 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 : [],    
+    selectedDatasets : [],
+    
     changeCallback(force){ 
-        viewer.controls.setTarget(this.box.position)//绕其旋转
         
-        if(Potree.settings.isOfficial){
+        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){  
@@ -31,6 +65,8 @@ var Clip = {
     },
 
     enter:function(){
+        this.initViews()
+         
         this.previousView = { 
 			position: viewer.images360.position,
 			target: viewer.scene.view.getPivot(),
@@ -73,29 +109,38 @@ var Clip = {
             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=>{
-                this.mapBox.center.setX(this.box.position.x)
-                this.mapBox.center.setY(this.box.position.y)
-                this.mapBox.updatePoints() 
+                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=>{
-                var scale = this.box.scale 
-                this.mapBox.updatePoints(scale)
+                if(this.showMap){
+                    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()
+                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);
             
         }
         
-        {//map
+        if(this.showMap){//map
             let boxRotateBack = ()=>{//不知道是不是这么写。 因为可能z的旋转不一定都在z 
                 this.box.rotation.x = 0;
                 this.box.rotation.y = 0;
@@ -126,7 +171,7 @@ var Clip = {
         }
         
         
-        
+        this.switchView('mainView')
         
         {
             //viewer.setClipTask(ClipTask["SHOW_INSIDE"])
@@ -139,21 +184,16 @@ var Clip = {
         //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
-                    //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
@@ -213,12 +253,12 @@ var Clip = {
     },
     
     leave:function(){
-        viewer.inputHandler.fixSelection = false
+        viewer.inputHandler.fixSelection = false 
         viewer.scene.removeVolume(this.box);
         
-        this.mapBox.dispose()
+        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)  
@@ -228,7 +268,11 @@ var Clip = {
         viewer.setPointStandardMat(false) 
         //viewer.setClipState(true)
         viewer.controls.setTarget(null)
-        {
+        this.splitScreenTool.focusCenter = null
+        
+        cameraProps.forEach(e=>e.openCount = 0)
+        
+        if(this.showMap){
             this.bus.removeEventListener('flyToPos',this.events.flyToPos) 
             viewer.mapViewer.removeEventListener('forceVisible',this.events.mapVisiChange) 
             
@@ -237,6 +281,156 @@ var Clip = {
         this.editing = false
     },
     
+    initViews(){
+        if(this.views)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(){ //找一个离当前最近的点云,且最好有漫游点
@@ -281,6 +475,8 @@ var Clip = {
         
         
         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 = {
@@ -296,8 +492,7 @@ var Clip = {
             
         }
         
-        return data
-        //https://testlaser.4dkankan.com/indoor/t-ia44BhY/api/pointcloud/crop
+        return data 
     },
     
     
@@ -305,6 +500,8 @@ var Clip = {
     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 = {
@@ -330,55 +527,150 @@ var Clip = {
         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)
-        ]
-
+ 
+    getIntersectPointcloud(){  
         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
+            if(pointcloud.intersectBox(this.box.matrixWorld))return true   
+        }  
+        return viewer.scene.pointclouds.filter(e=>intersect(e))  
+    },
+    
+    
+    screenshot: async ()=>{ 
+        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 = 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
+            })
+            
+            
+            let minZoom = Potree.browser.urlHasValue('clipZoom',true) || 80 //1px = 1个点 = 1cm , 如果一个点都是1cm的话
+                    //中间部分75-80为佳,文字100,边缘50. 最好支持调节 . 且当下载尺寸变大往往点云也多,可能冲破pointbudget限制
+            
+            
+            /* let minPointSize = Infinity
+            visiPointclouds.forEach(e=>minPointSize = Math.min(e.material.size,minPointSize))
+             
+            let minZoom = 2 / minPointSize //这是相信当前size是恰好的前提,每个点云1px时不会重叠也不会有缝隙
+            */
+            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 w = boundSize.x * minZoom
+            let h = boundSize.y * minZoom
+            
+            let max = Math.max(w,h)//, min = Math.min(w,h)
+            let s = 1
+            if(max > maxWidth){
+                s = maxWidth/max 
+            }else if(max < minWidth){
+                s = minWidth/max 
             }
-            return true 
+            w *= s, h *= s
+            material.size *= s  
+            s != 1 && console.log('宽度缩放倍数',s)  
             
-        } 
-
-        return viewer.scene.pointclouds.filter(e=>intersect(e)) 
+            
+            //虽然size>1就是浪费像素,但是太小了视觉效果有点差……可能因为fov不真实。 点云大就像是老式电视机虽然显示像素量少但感觉不出画面模糊,且边缘锐利。
+             
+            
+            w = Math.round(w)
+            h = Math.round(h)
+             
+            let focusObjectInfo = [{boundingBox, points}, 'boundingBox', 0, { dontChangeCamDir:true,  dontMoveMap:true, boundScale:1 }]
+            
+            
+            //分块渲染
+            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 {getImagePromise, finishPromise} = viewer.startScreenshot({ type: 'default', bgOpacity:0, focusObjectInfo, maxTimeForPointLoad : 3e5, pointDensity:'screenshot2', splitRenderInfo }, 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 ()=>{
+                    let labelInfo = {
+                        bottomRatioToImg : 0.03, rightRatioToImg : 0.03, /* widthRatioToImg : 0.2, //不固定字大小 */
+                        textColor: { r: 255, g: 255, b: 255, a: 1 }, 
+                        textBorderColor : { r: 30, g: 30, b: 30, a: 1 },
+                        textBorderThick :  1,
+                        fontsize: 24
+                    }
+                    
+                    let text = `1 : ${(1 / camera.zoom).toFixed(4)}(像素 : 米)`
+                 
+                    let finalDataUrl = 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(finalDataUrl) 
+                }
+                
+                
+            })
+            
+       
+             
+        }) 
+        /* 
+            隐患:无法获知最大可加在的点的数量,也未知需要加载的点的数量。所以会因pointBudget限制造成周围的点稀疏。
+            
+            如果需要加载的点云还是超出,是否可以改为分批渲染,类似多viewport但共用一个camera,在beforeRender时分viewport
+            
+         */
+        return finishPromise
+        
          
-
-    }  */ 
-    
-    
-    getIntersectPointcloud(){ 
-          
-        var intersect = (pointcloud)=>{
-            if(pointcloud.intersectBox(this.box.matrixWorld))return true   
-        } 
-
-        return viewer.scene.pointclouds.filter(e=>intersect(e))  
-    } 
+    }
     
    
     
@@ -402,10 +694,13 @@ export {Clip}
 
 
 
+//保存box
+viewer.modules.Clip.box.matrix
 
-
-
-
-
+//恢复box
+let a = new THREE.Matrix4().fromArray([....])
+a.decompose(viewer.modules.Clip.box.position,viewer.modules.Clip.box.quaternion,viewer.modules.Clip.box.scale)
+ 
+viewer.modules.Clip.rotateSideCamera(-93)
 
  */

+ 4 - 4
src/custom/modules/panos/Images360.js

@@ -2835,7 +2835,7 @@ Images360.prototype.updateCube = (function(){//增加细分的版本,且垂直
             let getFar = (dir, pano, origin, height)=>{//获取在这个方向上和墙体intersect的距离  
                 //在垂直方向上分出多个方向,取一个最可能的接近真实的距离
                 let maxH = 40, minH = 2, minR = 0.5, maxR = 2  
-                height = height == void 0 ? (Math.min(skyHeight,pano.ceilZ) - pano.floorPosition.z) : height
+                height = height == void 0 ? Math.min(skyHeight,  pano.ceilZ - pano.floorPosition.z) : height
                 //let r = height (maxH - minH)* 0.14  // 高度越小,角度越小 
                 //let r = minR + ( maxR - minR) * THREE.Math.clamp((height - minH)  / (maxH - minH),0,1)   //THREE.Math.smoothstep(currentDis,  op.nearBound,  op.farBound);
                 let r = math.linearClamp(height, [minH,maxH],   [minR,  maxR]) 
@@ -2874,7 +2874,7 @@ Images360.prototype.updateCube = (function(){//增加细分的版本,且垂直
                 
                 maxZ = pano.getCeilHeight()
                 
-                if(maxZ == Infinity) maxZ = skyHeight;   //maxZ = Math.max(skyHeight, maxZ)
+                if(maxZ == Infinity) maxZ = skyHeight + pano.position.z;   //maxZ = Math.max(skyHeight, maxZ)
                   
                 [maxZ, minZ ].forEach(z=>{  
                     posArr.push(pano.position.clone().setZ(z))
@@ -2973,8 +2973,8 @@ Images360.prototype.updateCube = (function(){//增加细分的版本,且垂直
             
             let addSide = ()=>{//两个漫游点间两边各加一些侧线 
                 //中点处的
-                let top0 = pano0.ceilZ == Infinity ?  skyHeight : pano0.ceilZ 
-                let top1 = pano1.ceilZ == Infinity ?  skyHeight : pano1.ceilZ 
+                let top0 = pano0.ceilZ == Infinity ?  pano0.position.z+skyHeight : pano0.ceilZ 
+                let top1 = pano1.ceilZ == Infinity ?  pano1.position.z+skyHeight : pano1.ceilZ 
                 let midMaxZ = (top0 + top1)/2
                 let midMinZ = (pano0.floorPosition.z+pano1.floorPosition.z)/2;
                 let mid = new THREE.Vector3().addVectors(pano0.position, pano1.position).multiplyScalar(0.5)

+ 8 - 6
src/custom/modules/volumeCompute/VolumeComputer.js

@@ -196,6 +196,7 @@ export default class VolumeComputer extends THREE.EventDispatcher{
             viewer.mainViewport.active = true  
             viewer.mapViewer.viewports[0].active = true 
             viewer.magnifier.viewport.active = true 
+            viewer.pauseTestMaxLevel = false
             console.log('pxPerMetric',pxPerMetric, 'boundSize',boundSize)
         }
         
@@ -225,7 +226,7 @@ export default class VolumeComputer extends THREE.EventDispatcher{
         
         viewer.screenshoting = true
         
-        
+        viewer.pauseTestMaxLevel = true
         
         
         let pxPerMetric = math.linearClamp(Math.max(boundSize.x,boundSize.y), [4, 10, 20, 80, 500, 2000], [300, 200, 100, 30, 6, 3])
@@ -626,7 +627,7 @@ export default class VolumeComputer extends THREE.EventDispatcher{
             let notifyArea_ = [...notifyArea, [0, progressEnd1]] 
             
             if(Potree.pointsLoading ){  //如果有要加载的node
-
+                
 
                 let curProgress = 0,  beginTime = Date.now(), unloadBefore,
              
@@ -662,10 +663,9 @@ export default class VolumeComputer extends THREE.EventDispatcher{
                 }
                 let update = ()=>{  
                     Potree.updatePointClouds(viewer.scene.pointclouds, camera, viewport.resolution );
-                    if( Potree.numVisiblePoints > maxPointBudge * 0.9){ 
-                        decreaseLevel()
-                    }
-                    
+                    /* if( Potree.numVisiblePoints > maxPointBudge * 0.9){ 
+                        decreaseLevel() 
+                     */
                     notify(notifyArea_,  ()=>{ 
                        /* console.log('unloadedGeometry',Potree.unloadedGeometry.length)
                        
@@ -767,6 +767,8 @@ export default class VolumeComputer extends THREE.EventDispatcher{
         
         
         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 = {

+ 2 - 2
src/custom/objects/TextSprite.js

@@ -83,9 +83,9 @@ export class TextSprite extends THREE.Object3D{
     update(){
         this.sprite.waitUpdate()
     }
-    setVisible(v){ 
+    /* setVisible(v){ 
         Potree.Utils.updateVisible(this, 'setVisible', v)
-    }
+    } */
     setUniforms(name,value){
         this.sprite.setUniforms(name,value)
     }

+ 49 - 36
src/custom/objects/tool/Measure.js

@@ -118,13 +118,12 @@ export class Measure extends ctrlPolygon{
         Potree.Utils.setObjectLayers(this, 'measure' )
         
         
-        /* if(this.measureType == 'MulDistance'){
-            this.showTotalDis = true
-            
+        if(this.measureType == 'MulDistance'){
+            //this.showTotalDis = true 
             this.totalDisLabel = this.createTotalDisLabel()
             this.add(this.totalDisLabel) 
         }
-         */
+         
         
         //addMarkers:
          
@@ -286,9 +285,10 @@ export class Measure extends ctrlPolygon{
             if(this.showDistances){ // edge labels
                 let edgeLabel = this.edgeLabels[index];
                 let distance = point.distanceTo(nextPoint)
+                this.edges[index].distance_ = distance
                 edgeLabel.shouldVisi = (index < lastIndex || this.isRect || this.closed && !this.isNew ) && distance>0 
-                this.closed || edgeLabel.setVisible(edgeLabel.shouldVisi)  //closed的在setEdgesDisplay中设置
-                
+                //this.closed || edgeLabel.setVisible(edgeLabel.shouldVisi)  //closed的在setEdgesDisplay中设置
+                Utils.updateVisible(edgeLabel, 'shouldVisi', edgeLabel.shouldVisi)
                 if(edgeLabel.shouldVisi){
                     edgeLabel.lineDir = new THREE.Vector3().subVectors(point,nextPoint).normalize() //[point,nextPoint]
                     setEdgeLabel(edgeLabel,point,nextPoint,distance)
@@ -348,23 +348,26 @@ export class Measure extends ctrlPolygon{
             
             this.areaLabel.setPos(this.getCenter('areaPlaneCenter'))
             this.areaLabel.setText(msg);
-            this.areaLabel.setVisible(true)
+            Utils.updateVisible(this.areaLabel, 'setVisible', true) //this.areaLabel.setVisible(true)
              
         }  
          
-        
-        
-        
-        if(this.showTotalDis && this.points.length > 2){  
-            //this.ifShowTotalDis()
-            let dis = this.getTotalDistance()
-            let msg = viewer.unitConvert.convert(dis, 'distance',  Potree.settings.precision , this.unitSystem, 0.01 , true  ) 
-            this.center = this.getCenter()
-            this.totalDisLabel.setPos(this.center);
-            this.totalDisLabel.setText(msg);
-            this.totalDisLabel.setVisible(true)
-        
-        }  
+        if(this.totalDisLabel){
+            this.ifShowTotalDis()
+  
+            Utils.updateVisible(this.totalDisLabel,'setVisible', this.showTotalDis)  
+            this.edgeLabels.forEach(e=> Utils.updateVisible(e, 'showTotalDis', !this.showTotalDis))
+  
+            if(this.showTotalDis){   
+                let dis = this.getTotalDistance()
+                let msg = this.getConvertString(dis, 'distance')   
+                this.center = null
+                this.center = this.getCenter()
+                this.totalDisLabel.setPos(this.center);
+                this.totalDisLabel.setText(msg); 
+            }  
+        }
+          
         
 	};
     
@@ -397,11 +400,15 @@ export class Measure extends ctrlPolygon{
             let show = this.points.length > 2
             
             if(show){
-                
-                
-                
-                
-                
+                let maxDis = 0.15 
+                let lastIndex = this.points.length - 1; 
+                for(let i=0;i<lastIndex;i++){
+                    let len = this.edges[i].distance_
+                    
+                    if(len > maxDis){
+                        show = false; break;
+                    }
+                } 
             }
             
             
@@ -623,8 +630,14 @@ export class Measure extends ctrlPolygon{
                         
             let addHoverEvent = ()=>{ //当非isNew时才添加事件 
             
-                let mouseover = (e) => {this.setSelected(true, 'edge')};
-                let mouseleave = (e) => {this.setSelected(false, 'edge')};
+                let mouseover = (e) => {
+                    /* if(this.measureType == 'MulDistance'){ 
+                    } */
+                    this.setSelected(true, 'edge')
+                };
+                let mouseleave = (e) => { 
+                    this.setSelected(false, 'edge')
+                };
  
                 edge.addEventListener('mouseover', mouseover);
                 edge.addEventListener('mouseleave', mouseleave); 
@@ -741,9 +754,9 @@ export class Measure extends ctrlPolygon{
     
     
     
-    setEdgesDisplay(state, ignoreGuideLine){
-        this.closed && this.edgeLabels.forEach(e=>e.setVisible(!!(state && e.shouldVisi))  )
-         
+    setEdgesDisplay(state, ignoreGuideLine){ 
+        this.closed && this.edgeLabels.forEach(e=>Utils.updateVisible(e,'hover',state)) 
+        
         if(!ignoreGuideLine && this.measureType == 'Distance'){
             this.horEdgeLabel.visible = this.verEdgeLabel.visible = this.horGuideEdge.visible = this.verGuideEdge.visible = !!(state && this.shouldShowHorVerGuide)
         }
@@ -1002,17 +1015,17 @@ export class Measure extends ctrlPolygon{
         )
         
         centerLabel.addEventListener('mouseover',()=>{
-            this.setSelected(true, 'centerLabel')
+            this.isNew || this.setSelected(true, 'centerLabel')
         })
         centerLabel.addEventListener('mouseleave',()=>{
-            this.setSelected(false, 'centerLabel')
+            this.isNew || this.setSelected(false, 'centerLabel')
         }) 
         centerLabel.addEventListener('click',()=>{
             this.isNew || viewer.measuringTool.isAdding || viewer.focusOnObject(this, 'measure')
         })
         Potree.Utils.setObjectLayers(centerLabel, 'measure' )
-        centerLabel.setVisible(false) 
-        
+       
+        Utils.updateVisible(centerLabel, 'setVisible', false)
         return centerLabel; 
     }
     
@@ -1275,10 +1288,10 @@ export class Measure extends ctrlPolygon{
         }
         if(this.showArea){ 
             this.area = {value:0};
-            this.areaLabel && this.areaLabel.setVisible(false)
+            this.areaLabel && Utils.updateVisible(this.areaLabel, 'setVisible', false )
         }
         if(this.showTotalDis){
-            this.totalDisLabel && this.totalDisLabel.setVisible(false) 
+            this.totalDisLabel && Utils.updateVisible(this.totalDisLabel, 'setVisible', false )
         }
         viewer.inputHandler.dispatchEvent( {type:'measuring', v:true, cause:'reDraw',object:this, situation:'dragging'}  )
 	            

+ 11 - 0
src/custom/objects/tool/MeasuringTool.js

@@ -151,6 +151,17 @@ export class MeasuringTool extends THREE.EventDispatcher{
     }
     
 	update(e){ 
+    
+    
+    
+        /* viewer.scene.measurements.forEach(measure=>{
+            if(measure.measureType != 'MulDistance' || (measure.isNew ? measure.points.length<4 : measure.points.length<3)) return
+           
+               
+        }) */
+    
+    
+    
         if(viewer.inputHandler.measuring && Potree.settings.adsorption){
             viewer.scene.measurements.forEach(measure=>{
                 measure.getPointsPos2d(e.viewport, true) 

+ 2 - 2
src/custom/objects/tool/TransformControls.js

@@ -1931,12 +1931,12 @@ var TransformControlsPlane = function (options) {
 
 		}
 
-		if ( dirVector.length() === 0 ) {
+		if ( dirVector.length() === 0 ) {//使物体在视线中平移
 
 			// If in rotate mode, make the plane parallel to camera
 			this.quaternion.copy( this.cameraQuaternion );
 
-		} else {
+		} else {//方向滑动所在面
 
 			tempMatrix.lookAt( tempVector.set( 0, 0, 0 ), dirVector, alignVector );
 

+ 82 - 65
src/custom/potree.shim.js

@@ -331,10 +331,10 @@ Utils.getMousePointCloudIntersection = function(viewport, mouse, pointer, camera
         if(window.notViewOffset){ 
             Potree.updatePointClouds(pointclouds,  camera, viewport.resolution );  
         }else{
-            //尽量减少点云加载的范围,集中在pick的空间。但似乎感觉这部分空间没加载到最密
-            let viewWidth = 100//viewer.magnifier ? viewer.magnifier.viewport.resolution.x : 200
-            let camera_ = camera.clone()
-            camera_.setViewOffset( viewport.resolution.x, viewport.resolution.y,   pickParams.x-viewWidth/2, pickParams.y-viewWidth/2,    viewWidth, viewWidth );
+            //尽量减少点云加载的范围,集中在pick的空间(这部分以外还是会加载一些散点的)
+            let viewWidth = 80 //viewer.magnifier ? viewer.magnifier.viewport.resolution.x : 200
+            let camera_ = camera.clone() 
+            camera_.setViewOffset( viewport.resolution.x, viewport.resolution.y,   pickParams.x-viewWidth/2, (viewport.resolution.y - pickParams.y)-viewWidth/2,      viewWidth, viewWidth ); //注意offsetY是从上到下,和一般的不同
             Potree.updatePointClouds(pointclouds,  camera_, viewport.resolution );   
         }  
     }
@@ -908,12 +908,66 @@ Utils.setObjectLayers = function(object, layerName){//add
     })
 }
 
+ 
 
+ 
+ 
+Utils.imgAddText = async (img, text, labelInfo)=>{
+    
+    let label = new Potree.TextSprite(Object.assign({//如果直接在canvas里写字,要另外写很多和canvas.drawText有关的,所以还是借助textSprite吧
+        backgroundColor: { r: 0, g: 0, b: 0, a: 0 },
+        textColor: { r: 255, g: 255, b: 255, a: 1 }, 
+        renderOrder: 50,  fontsize : 20,
+        text, 
+    },labelInfo))
+         
+    let labelImg = new Image
+    labelImg.src = label.sprite.material.map.image.toDataURL('image/png')
+    
+    return new Promise((resolve,reject)=>{ 
+        labelImg.onload = ()=>{
+            let src = Common.imgAddLabel(img,labelImg,labelInfo)
+            label.dispose()
+            resolve(src) 
+        }  
+    }) 
+     
+}
 
 
 
-
-
+Utils.combineImgs = async (imgs, compressRatio, width, height)=>{//拼合图片,顺序从上到下从左到右, 每张图大小不一定一致,但同列的宽一致,同行宽一致
+    return new Promise((resolve,reject)=>{ 
+        let item = imgs[0][0]
+        
+        let wc=imgs.length, hc=imgs[0].length, loadCount = 0, amount = wc * hc
+        
+        width = width || imgs.reduce((w,c)=>w + c[0].width, 0), //相加得到的可能比想要得到的大几个像素,之后会重叠
+        height = height || imgs[0].reduce((w,c)=>w + c.height, 0) 
+        let canvas = document.createElement('canvas') 
+            canvas.width = width
+            canvas.height = height
+        let context = canvas.getContext('2d')
+        
+        for(let i=0;i<wc;i++){
+            for(let j=0;j<hc;j++){
+                let img = new Image
+                img.src = imgs[i][j].dataUrl
+                img.index = {i,j}
+                img.onload = ()=>{ 
+                    loadCount++
+                    context.drawImage(img, img.index.i * img.width,  img.index.j * img.height, img.width, img.height)
+                    if(loadCount == amount){ 
+                        var dataUrl = canvas.toDataURL('image/png',compressRatio)
+                        context.clearRect(0,0,width,height)
+                        resolve(dataUrl)
+                    }
+                }
+            }                
+        }
+    }) 
+    
+}
 
 
 
@@ -1047,7 +1101,7 @@ Potree.updateVisibilityStructures = function(pointclouds, camera, areaSize) {
 	let frustums = {};
 	let camObjPositions = {}
     let camObjDirs = {} //add
-	let priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; });//二叉堆。
+	let priorityQueue = new BinaryHeap(function (x) { return  -x.weight  /*  1 / x.weight; */  });//二叉堆。 改,之前的weight不支持负数
 
                 
     viewer.addTimeMark('visiStructure','start')
@@ -1245,65 +1299,16 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 		visible = visible && level <= maxLevel; //< 改为 <=
 		//visible = visible || node.getLevel() <= 2;
         let pcWorldInverse = pointcloud.matrixWorld.clone().invert();
-        let m = pcWorldInverse.elements 
+        /* let m = pcWorldInverse.elements 
         let pcWorldInvM3 =  new THREE.Matrix3().set(m[0],m[4],m[12],m[1],m[5],m[13],m[3],m[7],m[15]) //去掉z的
-
+        */
         //pointcloud.pcMatrix3 = new THREE.Matrix3().set(m[0],m[4],m[12],m[1],m[5],m[13],m[3],m[7],m[15]) //去掉z的
     
     
 
-        let intersectBox = (clipBox)=>{
-
-            
-            let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld);  //box乘上点云逆矩阵
-
-            /* let px = new THREE.Vector3(+0.5, 0, 0).applyMatrix4(pcWorldInverse);
-            let nx = new THREE.Vector3(-0.5, 0, 0).applyMatrix4(pcWorldInverse);
-            let py = new THREE.Vector3(0, +0.5, 0).applyMatrix4(pcWorldInverse);
-            let ny = new THREE.Vector3(0, -0.5, 0).applyMatrix4(pcWorldInverse);
-            let pz = new THREE.Vector3(0, 0, +0.5).applyMatrix4(pcWorldInverse);
-            let nz = new THREE.Vector3(0, 0, -0.5).applyMatrix4(pcWorldInverse);
-
-            let pxN = new THREE.Vector3().subVectors(nx, px).normalize();
-            let nxN = pxN.clone().multiplyScalar(-1);
-            let pyN = new THREE.Vector3().subVectors(ny, py).normalize();
-            let nyN = pyN.clone().multiplyScalar(-1);
-            let pzN = new THREE.Vector3().subVectors(nz, pz).normalize();
-            let nzN = pzN.clone().multiplyScalar(-1);
-
-            let pxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pxN, px);
-            let nxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nxN, nx);
-            let pyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pyN, py);
-            let nyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nyN, ny);
-            let pzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pzN, pz);
-            let nzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nzN, nz);
-
-            //if(window.debugdraw !== undefined && window.debugdraw === true && node.name === "r60"){
-
-            //	Potree.utils.debugPlane(viewer.scene.scene, pxPlane, 1, 0xFF0000);
-            //	Potree.utils.debugPlane(viewer.scene.scene, nxPlane, 1, 0x990000);
-            //	Potree.utils.debugPlane(viewer.scene.scene, pyPlane, 1, 0x00FF00);
-            //	Potree.utils.debugPlane(viewer.scene.scene, nyPlane, 1, 0x009900);
-            //	Potree.utils.debugPlane(viewer.scene.scene, pzPlane, 1, 0x0000FF);
-            //	Potree.utils.debugPlane(viewer.scene.scene, nzPlane, 1, 0x000099);
-
-            //	Potree.utils.debugBox(viewer.scene.scene, box, new THREE.Matrix4(), 0x00FF00);
-            //	Potree.utils.debugBox(viewer.scene.scene, box, pointcloud.matrixWorld, 0xFF0000);
-            //	Potree.utils.debugBox(viewer.scene.scene, clipBox.box.boundingBox, clipBox.box.matrixWorld, 0xFF0000);
-
-            //	window.debugdraw = false;
-            //}
-
-            let frustum = new THREE.Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane);
-            let intersects = frustum.intersectsBox(box); //node的bounding
-
-             
-            return !!intersects */
-            
-             
-            return Potree.Utils.isIntersectBox(box, toPCObject) 
-            
-            
+        let intersectBox = (clipBox)=>{ 
+            let toPCObject = pcWorldInverse.clone().multiply(clipBox.box.matrixWorld);  //box乘上点云逆矩阵 
+            return Potree.Utils.isIntersectBox(box, toPCObject)  
         }
 
         //改 总共两种box : 可见和不可见(都是并集)
@@ -1377,6 +1382,7 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 		}
 
 		if (numVisiblePoints + node.getNumPoints() > Potree.pointBudget) {
+            viewer.dispatchEvent({type:'overPointBudget', restQueueSize: priorityQueue.size(), numVisiblePoints}  )
 			break;
 		}
 
@@ -1502,9 +1508,20 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
                 if( sphere.radius *  /* Math.pow( */camera.zoom/* ,1-reduce) */   < pointcloud.minimumNodePixelSize   ){
 					continue;
 				}
-                		
-				let distance = sphere.center.distanceToSquared(camObjPos); //先加载中间然后四周 
-				weight = sphere.radius / distance;
+                 		
+                let distance = sphere.center.distanceToSquared(camObjPos); //先加载中间然后四周 
+                weight = sphere.radius / distance   
+                 
+                
+               /* let vec = new THREE.Vector3().subVectors(sphere.center, camObjPos)  
+                let disOnCamDir = vec.dot(camObjDir)
+                let vecOnCamDir = camObjDir.clone().multiplyScalar(disOnCamDir) 
+                let vecSide = new THREE.Vector3().subVectors(vec, vecOnCamDir) //在屏幕上从中心到该node的向量
+                let disSide = vecSide.length()   
+				//weight = sphere.radius / disSide * camera.zoom - disOnCamDir * 2;  //如果用除的,ortho的camera离远了的话dis的影响就小了
+                
+                weight = sphere.radius /  ( disSide * 0.1 + disOnCamDir  * 14 )
+                      */
                 
 				//weight = diagonal;
 			}
@@ -2000,7 +2017,7 @@ LRU.prototype.freeMemory = function(){
     } 
     let memoryRatio = browser.isMobile() ? 2 : 5;
     //改成navvis的,使用pointBudget,否则四屏点云闪烁。 (似乎要比updateVisiblede的node时限制要宽些,作为缓存继续存着。否则会闪烁)
-    let max = THREE.Math.clamp( viewer.viewports.length * memoryRatio * Potree.pointBudget, 0, 40e6)
+    let max = THREE.Math.clamp( viewer.viewports.length * memoryRatio * Potree.pointBudget, 0, 35e6)
        
     for (; this.numPoints > max;  ) { 
         var node = this.getLRUItem();

+ 7 - 2
src/custom/settings.js

@@ -146,8 +146,13 @@ const config = {//配置参数   不可修改
         },
         screenshot:{
             maxLevelPercent: 1,  
-            pointBudget: browser.isMobile() ? 3.5*1000*1000 :  8*1000*1000,  //一般只有电脑需要截图,手机的加载多会崩
+            pointBudget: browser.isMobile() ? 4*1000*1000 :  10*1000*1000,  //一般只有电脑需要截图,手机的加载多会崩
             minNodeSize :   40 / window.devicePixelRatio  ,  
+        }, 
+        screenshot2:{
+            maxLevelPercent: 1,  
+            pointBudget: browser.isMobile() ? 8*1000*1000 :  15*1000*1000,   
+            minNodeSize :   20 / window.devicePixelRatio  ,  
         },
         ultraHigh:{
             maxLevelPercent: 1,  
@@ -461,7 +466,7 @@ let settings = {//设置   可修改
     
     showCompass : false,
     showAxis : isTest,
-    // testCube : true,
+    //testCube : true,
     // moveToCenter:true, //针对数据集间隔很远的场景  dis>5000 容易抖动
     tiles3DMaxMemory: config.tiles3DMaxMemory,
     adsorption:false,//测量时吸附点 

+ 4 - 4
src/custom/start.js

@@ -254,7 +254,7 @@ export function start(dom, mapDom, number ){ //t-Zvd3w0m
         }
         
         var transformPointcloud = (pointcloud, dataset)=>{
-            var locationLonLat = dataset.location.slice(0,2)
+            var locationLonLat = dataset.location.slice(0,3)
              
            
             //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。
@@ -283,7 +283,7 @@ export function start(dom, mapDom, number ){ //t-Zvd3w0m
             pointcloud.updateMatrixWorld()
             
             
-            Potree.Log(`点云${pointcloud.dataset_id}(${pointcloud.name})旋转值:${pointcloud.orientationUser}, 位置${math.toPrecision(pointcloud.translateUser.toArray(),3)}, 经纬度 ${locationLonLat}, spacing ${pointcloud.material.spacing}`,  {font:[null, 13]} )
+            Potree.Log(`点云${pointcloud.dataset_id}(${pointcloud.name})旋转值:${pointcloud.orientationUser}, 位置${math.toPrecision(pointcloud.translateUser.toArray(),3)}, 经纬度 ${dataset.location}, spacing ${pointcloud.material.spacing}`,  {font:[null, 13]} )
             
             
             //-------------------
@@ -297,13 +297,13 @@ export function start(dom, mapDom, number ){ //t-Zvd3w0m
         {//拿初始数据集作为基准。它的位置是000
             var locationLonLat = originDataset.location.slice(0,2)
             
-            if(window.AMapWith84){//需要转换为高德的
+            if(window.AMapWith84){//需要转换为高德的,但该函数不准确,转入后再转出,和原来的有偏差.    navvis的我看data中存的globalLocation直接输入到高德地图后的定位和其要展示的定位一致,而我们要转为高德后才一致,猜测是navvis后台转为了高德可用的经纬度。 若不转的话,其他看起来没问题,仅高德地图定位不准确,因其为被加密后的火星坐标系。
                 locationLonLat = AMapWith84.wgs84ToAMap({x:locationLonLat[0], y:locationLonLat[1]})
                 locationLonLat = [locationLonLat.x,locationLonLat.y] 
             } 
             
             proj4.defs("LOCAL", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); //高德坐标系
-            proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15));
+            proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); //地图和本地一样
             proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
              
             let transform1 = proj4("WGS84", "LOCAL"); //这个ok 是展开的平面投影  LOCAL即NAVVIS:TMERC

+ 33 - 1
src/custom/utils/Common.js

@@ -478,7 +478,39 @@ var Common = {
         }) 
     },
       
-    
+    imgAddLabel : function (img, labelImg, labelInfo = {}) {
+        //图上加另一张小图,用于添加水印
+
+        let canvas = document.createElement('canvas')
+        let context = canvas.getContext('2d')
+        context.canvas.width = img.width
+        context.canvas.height = img.height
+        context.drawImage(img, 0, 0, img.width, img.height)
+
+        let labelWidth = labelInfo.widthRatioToImg ? img.width * labelInfo.widthRatioToImg : labelImg.width //widthRatioToImg:label的width占img的width的比例
+        let labelHeight = (labelWidth * labelImg.height) / labelImg.width
+
+        if (!labelInfo.leftRatioToImg && labelInfo.rightRatioToImg) {
+            labelInfo.leftRatioToImg = 1 - labelInfo.rightRatioToImg - (labelInfo.widthRatioToImg || labelImg.width / img.width)
+        }
+        if (!labelInfo.topRatioToImg && labelInfo.bottomRatioToImg) {
+            labelInfo.topRatioToImg = 1 - labelInfo.bottomRatioToImg - labelHeight / img.height
+        }
+
+        let labelLeft = img.width * labelInfo.leftRatioToImg //leftRatioToImg:label的left占img的width的比例
+        let labelTop = img.height * labelInfo.topRatioToImg //topRatioToImg:label的top占img的height的比例
+
+        context.globalAlpha = labelInfo.opacity != void 0 ? labelInfo.opacity : 1
+        context.drawImage(labelImg, labelLeft, labelTop, labelWidth, labelHeight)
+
+
+        var dataUrl = canvas.toDataURL('image/png', labelInfo.compressRatio)
+        //Common.downloadFile(dataUrl, 'screenshot.png')
+        context.clearRect(0,0,img.width,img.height)
+        return dataUrl
+
+ 
+    }
 }  
 
 Potree.Common = Common

+ 9 - 5
src/custom/utils/SplitScreen.js

@@ -102,9 +102,13 @@ class SplitScreen extends THREE.EventDispatcher{
         }
         
         
-        if(viewport.viewContainsPoints){//视野范围内必须要包含的点,直接算入模型区域。这时候得到的boundCenter和模型中心不重合
+        let containsPoints = []
+        this.focusCenter && containsPoints.push(this.focusCenter)
+        viewport.viewContainsPoints && containsPoints.push(...viewport.viewContainsPoints)   
+        
+        if(containsPoints.length){//视野范围内必须要包含的点,直接算入模型区域。这时候得到的boundCenter和模型中心不重合
             boundingBox = boundingBox.clone()
-            viewport.viewContainsPoints.forEach(point=>{
+            containsPoints.forEach(point=>{
                 boundingBox.expandByPoint(point)
             })
             boundSize = boundingBox.getSize(new THREE.Vector3)
@@ -118,7 +122,7 @@ class SplitScreen extends THREE.EventDispatcher{
         //let {boundSize, center} = viewer.bound
         boundSize = boundSize || this.getViewBound(viewport).boundSize
         let expand = 10; 
-        let radius = boundSize.length() //    / 2  
+        let radius = boundSize.length() * 2 
         let position = viewport.shiftTarget.clone().sub(viewport.view.direction.clone().multiplyScalar(radius + expand))  
          
         return position 
@@ -126,7 +130,7 @@ class SplitScreen extends THREE.EventDispatcher{
     
     updateCameraOutOfModel(){//因为移动模型导致模型超出相机外,所以更新位置
         viewer.viewports.forEach((viewport, i )=>{
-            if(viewport != viewer.mainViewport){
+            if(viewport.camera.isOrthographicCamera){ //or if(viewport.targetPlane) 
                 let {boundSize, boundCenter} = this.getViewBound(viewport)
                 /* viewport.targetPlane.setFromNormalAndCoplanarPoint( viewport.view.direction.clone(), boundCenter) 
                 viewport.targetPlane.projectPoint(viewport.view.position, viewport.shiftTarget)  //target转换到过模型中心的平面,以保证镜头一定在模型外 this.shiftTarget是得到的
@@ -153,7 +157,7 @@ class SplitScreen extends THREE.EventDispatcher{
         
     }
      
-    rotateSideCamera(viewport, angle){//侧视图绕模型中心水平旋转
+    rotateSideCamera(viewport, angle){//侧视图或俯视图绕模型中心水平旋转
          
         //let {boundSize, center} = viewer.bound
         let {boundSize, boundCenter } = this.getViewBound(viewport)  

+ 1 - 1
src/custom/utils/SplitScreen4Views.js

@@ -195,7 +195,7 @@ SplitScreen4Views.split = function(o={}){
 
 SplitScreen4Views.recover = function(){
     
-    viewer.viewports.forEach(e=>e.axis.dispose())
+    viewer.viewports.forEach(e=>{e.axis.dispose(),e.axis = null})
     
     this.unSplit()
     viewer.mapViewer.viewports[0].beforeRender = null; 

+ 281 - 152
src/custom/viewer/ViewerNew.js

@@ -1427,7 +1427,7 @@ export class Viewer extends ViewerBase{
             let camera = e.camera || this.scene.getActiveCamera()
             
             Common.intervalTool.isWaiting('testPointcloudsMaxLevel', ()=>{  
-                if(!camera_changed && count>50 || Potree.settings.displayMode == 'showPanos'  )return //只有当camera_changed后才继续循环, 除了最开始几次需要连续加载下
+                if(!camera_changed && count>50 || Potree.settings.displayMode == 'showPanos' || this.pauseTestMaxLevel )return //只有当camera_changed后才继续循环, 除了最开始几次需要连续加载下
                 camera_changed = false
                 count ++;
                 //console.log('testPointcloudsMaxLevel中', camera.type /* count */)
@@ -1915,7 +1915,7 @@ export class Viewer extends ViewerBase{
 	};
 
 	zoomTo(node, factor, animationDuration = 0){
-		let view = this.scene.view;
+		let view = this.mainViewport.view;
 
 		let camera = this.scene.cameraP.clone();
 		camera.rotation.copy(this.scene.cameraP.rotation);
@@ -3172,9 +3172,12 @@ export class Viewer extends ViewerBase{
         return buffer
     }
 
-	renderDefault(params_={}){
+ 
+
+
+	async renderDefault(params_={}){
         
-        if(!this.visible  )return
+        if(!this.visible  )  return
         
          
 		let pRenderer = this.getPRenderer();
@@ -3198,34 +3201,36 @@ export class Viewer extends ViewerBase{
         
         ///let needsResize = viewports.length > 1 || params_.resize   //去掉原因:因为不需要渲染的viewport不在此中所以无法判断几个viewport
         
-        
-        
-        viewports.forEach(view=>{ 
+        for(let i=0; i<viewports.length; i++){
+            let viewport = viewports[i]
             
             let params = $.extend({},params_);
-            params.viewport = view
+            params.viewport = viewport
             //if(!params.target){
-                params.camera = params.camera || view.camera;
-                params.extraEnableLayers = view.extraEnableLayers
-                params.cameraLayers = view.cameraLayers
+                params.camera = params.camera || viewport.camera;
+                params.extraEnableLayers = viewport.extraEnableLayers
+                params.cameraLayers = viewport.cameraLayers
             //}
             
             
             var left,bottom,width,height
             { 
-                left = Math.ceil(renderSize.x * view.left)
-                bottom = Math.ceil(renderSize.y * view.bottom)
+                left = Math.ceil(renderSize.x * viewport.left)
+                bottom = Math.ceil(renderSize.y * viewport.bottom)
                 
                 if(params_.target){//有target时最好viewport是专门建出来的
-                    width = Math.ceil(renderSize.x * view.width)  //target的大小可能和viewport不同,比如截图,这时会更改viewport大小
-                    height = Math.ceil(renderSize.y * view.height)
+                    width = renderSize.x * viewport.width  //target的大小可能和viewport不同,比如截图,这时会更改viewport大小
+                    height = renderSize.y * viewport.height
                 }else{ 
-                    width = view.resolution.x // 用的是client的width和height
-                    height = view.resolution.y
-                }
-                if(width == 0 || height == 0)return
+                    width = viewport.resolution.x // 用的是client的width和height
+                    height = viewport.resolution.y
+                }  
+                if(width == 0 || height == 0)continue;//return
+                
+                width = Math.ceil(width)        //使用ceil,当原本有小数时,会重叠一个像素,无所谓
+                height = Math.ceil(height)
                 
-                let scissorTest =  view.width<1 || view.height<1  
+                let scissorTest =  viewport.width<1 || viewport.height<1  
                 if(params_.target){
                     params_.target.viewport.set(left, bottom, width, height);  
                     scissorTest && params_.target.scissor.set(left, bottom, width, height); 
@@ -3233,13 +3238,21 @@ export class Viewer extends ViewerBase{
                     this.renderer.setRenderTarget(params_.target)
                 }else{    
                     /* if(viewports.length == 1 && left == 0 && bottom == 0){
-                        left = 1 , width-=1   //这种情况下渲染也会有问题,只有鼠标的地方刷新
+                        left = 1 , width-=1   //这种情况下渲染也会有问题,只有鼠标的地方刷新 估计是pick那影响的
                     } */
-                    if(scissorTest && left == 0 && bottom == 0){
+                    
+                    /* if(scissorTest && left == 0 && bottom == 0){
                         left = 1 , width-=1 
+                    } */   
+                    if(viewport.forViewOffset){ //分块渲染,渲染时的camera还是原来的,但加载点云时的camera是分块中的
+                        viewport.forViewOffset.camera.setViewOffset(renderSize.x, renderSize.y, left, renderSize.y - bottom - height, width, height)
+                        this.renderer.setViewport(0,0,renderSize.x, renderSize.y)
+                        viewport.resolution.copy(viewport.forViewOffset.originViewport.resolution)
+                        viewport.resolution2.copy(viewport.forViewOffset.originViewport.resolution2) //后续渲染都当做原先的viewport
+                    }else{
+                        this.renderer.setViewport(left, bottom, width, height) //规定视口,影响图形变换(画布的使用范围) 
                     }
-                    
-                    this.renderer.setViewport(left, bottom, width, height) //规定视口,影响图形变换(画布的使用范围) 
+                    scissorTest && console.log('setScissor', left, bottom, width, height)
                     scissorTest && this.renderer.setScissor( left, bottom, width, height );//规定渲染范围
                     this.renderer.setScissorTest( scissorTest );//开启WebGL剪裁测试功能,如果不开启,.setScissor方法设置的范围不起作用 | width==1且height==1时开启会只有鼠标的地方刷新,很奇怪
                     
@@ -3247,25 +3260,25 @@ export class Viewer extends ViewerBase{
             } 
             
             //if(needsResize){
-                this.ifEmitResize( { viewport:view} )
+                this.ifEmitResize( { viewport} )
             //} 
               
-            viewer.dispatchEvent({type: "render.begin",  viewer: viewer, viewport:view, params });
+            viewer.dispatchEvent({type: "render.begin",  viewer: viewer, viewport, params });
              
           
-            view.beforeRender && view.beforeRender()  
+            viewport.beforeRender && viewport.beforeRender()  
             
-            if(view.render){ 
-                if(!view.render($.extend({}, params, {
+            if(viewport.render){ 
+                if(!viewport.render($.extend({}, params, {
                     renderer:this.renderer,   clear:this.clear.bind(this), resize:null,
-                    renderBG:this.renderBG.bind(this),  force:true  //viewer content_change时map也直接渲染吧     //!view.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
-                })))return
+                    renderBG:this.renderBG.bind(this),  force:true  //viewer content_change时map也直接渲染吧     //!viewport.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
+                }))) continue//return
             }else{ 
                 this.clear(params)  
                 pRenderer.clearTargets(params);
                  
                  
-                this.renderBG(view)
+                this.renderBG(viewport)
                  
                 if(Potree.settings.notAdditiveBlending){
                     params.renderBeforeCloud = true
@@ -3274,8 +3287,16 @@ export class Viewer extends ViewerBase{
             }
            
             
-            if(!view.noPointcloud ){  
-                this.updateViewPointcloud(params.camera, view.resolution, true)  
+            if(!viewport.noPointcloud ){ 
+
+                if(params_.needWaitLoadPoint){
+                    await this.waitPointLoad(null, params_.maxTimeForPointLoad,  viewport ) //resolution得用split前整个的viewport的
+                }else{
+                    this.updateViewPointcloud(params.camera, viewport.resolution, true)  
+                }
+                
+            
+                
                  
                 pRenderer.render(params);  //渲染点云 skybox
                 
@@ -3289,13 +3310,14 @@ export class Viewer extends ViewerBase{
                this.renderOverlay(params)                 
             }
                
-            view.afterRender && view.afterRender() 
+            viewport.afterRender && viewport.afterRender() 
 
-            this.dispatchEvent({type: "render.end",  viewer: this, viewport:view  }); 
-            view.needRender = false
-            view.lastRenderTime = Date.now()
-        })
-        
+            this.dispatchEvent({type: "render.end",  viewer: this, viewport  }); 
+            viewport.needRender = false
+            viewport.lastRenderTime = Date.now()
+         
+        }
+ 
         
        
         
@@ -3313,17 +3335,17 @@ export class Viewer extends ViewerBase{
         viewer.addTimeMark('renderDefault','end')
 	}
     
-    renderBG(view){ 
-        let background = view.background || viewer.background;
-        let backgroundOpacity = view.backgroundOpacity == void 0 ? viewer.backgroundOpacity : view.backgroundOpacity//如果想完全透明,只需要backgroundOpacity为0
+    renderBG(viewport){ 
+        let background = viewport.background || viewer.background;
+        let backgroundOpacity = viewport.backgroundOpacity == void 0 ? viewer.backgroundOpacity : viewport.backgroundOpacity//如果想完全透明,只需要backgroundOpacity为0
         if(backgroundOpacity != 0){//绘制背景
             if(background === "skybox"){
                 //限制相机到原点的距离。 
-                let skyCamera = view.camera.type == "OrthographicCamera" ? viewer.skybox.cameraOrtho : viewer.skybox.camera
+                let skyCamera = viewport.camera.type == "OrthographicCamera" ? viewer.skybox.cameraOrtho : viewer.skybox.camera
                 let safeRatio = 0.02;
                 let safeWidth = Potree.config.skyboxBgWidth * safeRatio / 2;  //相机只能在这个范围内移动
-                if(!view.skyboxFixPos){ //允许不在全景图中心,允许位移造成一定程度畸变 
-                    let dir = new THREE.Vector3().subVectors(view.camera.position, viewer.bound.center);
+                if(!viewport.skyboxFixPos){ //允许不在全景图中心,允许位移造成一定程度畸变 
+                    let dir = new THREE.Vector3().subVectors(viewport.camera.position, viewer.bound.center);
                     let length = dir.length()
                     const moveMax = 100;
                     let aimRadius = easing.easeOutQuart(Math.min(length, moveMax) , 0, safeWidth, moveMax) //(x, startY, wholeY, maxX) 当自变量为0-moveMax时,相机位移量为0-safeWidth
@@ -3333,29 +3355,29 @@ export class Viewer extends ViewerBase{
                     skyCamera.position.set(0,0,0)
                 }
                 
-                skyCamera.rotation.copy(view.camera.rotation);
-                skyCamera.aspect = view.camera.aspect;
+                skyCamera.rotation.copy(viewport.camera.rotation);
+                skyCamera.aspect = viewport.camera.aspect;
                 
-                if(view.camera.type == "OrthographicCamera"){ //调节zoom
-                    skyCamera.left = view.camera.left; skyCamera.right = view.camera.right; skyCamera.top = view.camera.top; skyCamera.bottom = view.camera.bottom
+                if(viewport.camera.type == "OrthographicCamera"){ //调节zoom
+                    skyCamera.left = viewport.camera.left; skyCamera.right = viewport.camera.right; skyCamera.top = viewport.camera.top; skyCamera.bottom = viewport.camera.bottom
                     
                     let a = Potree.config.skyboxBgWidth / 2 - safeWidth
-                    let minY = Math.max(skyCamera.right / a, skyCamera.top / a,  view.skyboxMinZoom||0); //能够使skybox铺满画布的最小zoom.  提示:越远zoom越小
+                    let minY = Math.max(skyCamera.right / a, skyCamera.top / a,  viewport.skyboxMinZoom||0); //能够使skybox铺满画布的最小zoom.  提示:越远zoom越小
                     let maxY = Math.max(20, minY) ;//自定义一个 不会超过的最大实际zoom
-                    //view.camera.zoom自变量的变化范围:
+                    //viewport.camera.zoom自变量的变化范围:
                     let minX = 1
                     let maxX = 80  
-                    let x = THREE.Math.clamp(view.camera.zoom - minX, minX, maxX) 
+                    let x = THREE.Math.clamp(viewport.camera.zoom - minX, minX, maxX) 
                     skyCamera.zoom = easing.easeOutCubic(x-minX, minY, maxY-minY, maxX-minX) //自变量范围从0开始,所以减去minX
                     
                     //pos的范围先不管了 其实aimRadius是有误的,但效果还行
                 }else{
-                    skyCamera.fov = view.camera.fov;
+                    skyCamera.fov = viewport.camera.fov;
                     skyCamera.zoom = 1
                 }
                   
                 
-                view.skyboxRenderFun && view.skyboxRenderFun()
+                viewport.skyboxRenderFun && viewport.skyboxRenderFun()
                 
                 skyCamera.updateProjectionMatrix();
                 skyCamera.updateMatrixWorld()
@@ -3364,8 +3386,8 @@ export class Viewer extends ViewerBase{
                 viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg);
                 viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
             }else if(background === 'overlayColor'){//在不clear的前提下加一层背景色 
-                viewer.scene.bg2.material.color.copy(view.backgroundColor)
-                viewer.scene.bg2.material.opacity = view.backgroundOpacity
+                viewer.scene.bg2.material.color.copy(viewport.backgroundColor)
+                viewer.scene.bg2.material.opacity = viewport.backgroundOpacity
                 viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg2);
                 viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
                 
@@ -3512,11 +3534,12 @@ export class Viewer extends ViewerBase{
     /* 大规模WebGL应用引发浏览器崩溃的几种情况及解决办法
     https://blog.csdn.net/weixin_30378311/article/details/94846947 */
      
-	render(params={}){//add params 
+	async render(params={}){//add params 
         viewer.addTimeMark('render','start')
         const vrActive = this.renderer.xr.isPresenting;
         let SiteModel = viewer.modules.SiteModel 
         
+        if(this.screenshoting && !params.screenshot)return //正在截图
    
         let s = SiteModel.editing && SiteModel.selected && (SiteModel.selected.buildType == 'room' || SiteModel.selected.buildType == 'floor') //空间模型的房间选中材质是需要depth的,这时候需要绘制两次点云
          
@@ -3539,7 +3562,7 @@ export class Viewer extends ViewerBase{
                     let scenes = this.inputHandler.interactiveScenes.concat(this.scene.scene).concat(viewer.scene.scenePointCloud) 
                     this.composer.render(scenes, null, this.viewports, this.renderDefault.bind(this));  
                 }else{  
-                    this.renderDefault(params);
+                    await this.renderDefault(params);
                 } 
             } 
             if(!specialRender) this.needRender = false            
@@ -3560,25 +3583,28 @@ export class Viewer extends ViewerBase{
         let useMap = info.map || info.type == 'measure' || info.type.includes('prism2d')
         
         
-        if(Potree.settings.displayMode == 'showPanos' && viewer.scene.view.isFlying('pos')){//如果在飞,飞完再截图
+        
+        if(Potree.settings.displayMode == 'showPanos' && viewer.mainViewport.view.isFlying('pos')){//如果在飞,飞完再截图
             info.getImageDeferred = getImageDeferred , info.finishDeferred = finishDeferred
             let f = ()=>{
                 this.startScreenshot(info,  width, height, compressRatio)  
             } 
-            viewer.scene.view.addEventListener('flyingDone', f, {once:true}) 
+            viewer.mainViewport.view.addEventListener('flyingDone', f, {once:true}) 
           
             return {getImagePromise:getImageDeferred.promise(), finishPromise:finishDeferred.promise()}
         }
         
-        var sid = Date.now()
+        var startTime = Date.now()
         //抗锯齿待加 1 post处理 2截图大张再抗锯齿缩小
+        this.pauseTestMaxLevel = true
+    
     
-        console.log('startScreenshot: '+sid)
+        console.log('startScreenshot: '+startTime)
         
         let updateCamera = ()=>{ 
             viewports.forEach(e=>{
                 e.view.applyToCamera(e.camera) //因为fly时只更新了view所以要强制更新下camera
-                 
+                
                 this.dispatchEvent({  //update map  and sprite,  mapChanged
                     type: "camera_changed", 
                     camera: e.camera,
@@ -3588,32 +3614,74 @@ export class Viewer extends ViewerBase{
             })  
         }
         
-        let screenshot = ()=>{ 
+        let screenshot = async ()=>{ 
             let pose
-            //console.log('screenshot',sid, Date.now())
+          
             useMap && (viewer.mapViewer.needRender = true)
             
-            this.needRender = true
             
-            if(info.useRenderTarget){
-                //离屏渲染 有抗锯齿问题、在手机上速度慢   主视图一样会变内容,还是不用这个了
-                var { dataUrl  } = viewerMaster.makeScreenshot( new THREE.Vector2(width,height), null, compressRatio    );
-                
-            }else{ 
-                
-                //直接渲染 会改变canvas大小
-                let canvas = viewerMaster.renderer.domElement
-                //canvas.width = width, canvas.height = height //不需要改canvas大小, 只需要 this.renderer.setSize(width, height ); 前面updateScreenSize已经执行
-                viewerMaster.render({  screenshot : true,   width , height,   resize :true  }); //需要resize
-                
-                /* if(info.type.includes('prism2d')){//o.map要为true
-                    viewer.dispatchEvent({type:'resize', viewport:viewer.mapViewer.viewports[0]}) //借用viewer通知lineMaterial resize
-                    viewerMaster.renderOverlay()
-                } */
+            
+            info.beforeScreenshot && info.beforeScreenshot()
+            
+            let render = async ()=>{ 
+            
+                this.needRender = true
+                viewports.forEach(e=>e.active = true)  
+            
+                if(info.useRenderTarget){
+                    //离屏渲染 有抗锯齿问题、在手机上速度慢   主视图一样会变内容,还是不用这个了
+                    var { dataUrl  } = viewerMaster.makeScreenshot( new THREE.Vector2(width,height), null, compressRatio    );
+                    
+                }else{ 
+                    
+                    //直接渲染 会改变canvas大小
+                    let canvas = viewerMaster.renderer.domElement
+                    //canvas.width = width, canvas.height = height //不需要改canvas大小, 只需要 this.renderer.setSize(width, height ); 前面updateScreenSize已经执行
+                    await viewerMaster.render({  screenshot : true,   width , height,   resize :true,  needWaitLoadPoint:!!info.splitRenderInfo, maxTimeForPointLoad:info.maxTimeForPointLoad  }); //需要resize
+                    
+                    /* if(info.type.includes('prism2d')){//o.map要为true
+                        viewer.dispatchEvent({type:'resize', viewport:viewer.mapViewer.viewports[0]}) //借用viewer通知lineMaterial resize
+                        viewerMaster.renderOverlay()
+                    } */
+                    var dataUrl = canvas.toDataURL('image/png',compressRatio) 
+                }
+                return dataUrl
+            }
+            
+            
+            let dataUrl
+            if(info.splitRenderInfo){//为了防崩溃,分区域批次渲染
+                let {wc , hc} = info.splitRenderInfo
+                let camera = viewer.mainViewport.camera
+                let dataUrls = []  
+                let width_ = Math.ceil(1/wc * width) , height_ = Math.ceil(1/hc * height)  //可能加起来比原图大,没事到时候边界会重合
+                viewer.updateScreenSize({forceUpdateSize:true, width:width_, height:height_, forTarget:info.useRenderTarget}) //更新viewports相机透视 使focusOnObject在此窗口大小下
+                for(let i=0;i<wc;i++){
+                    let col = []
+                    for(let j=0;j<hc;j++){
+                        let left = Math.ceil((-wc/2+0.5+i)*width_), top = Math.ceil((-hc/2+0.5+j)*height_) 
+                        camera.setViewOffset(width_, height_,  left, top , width_, height_)   //顺序从上到下从左到右
+                        let dataUrl = await render() 
+                        //Common.downloadFile(dataUrl, 'screenshot'+ i+':'+j+'.png') 
+                        col.push({dataUrl,  width:width_, height:height_})
+                    }
+                    dataUrls.push(col) 
+                }
+                camera.clearViewOffset()
                 
-                var dataUrl = canvas.toDataURL('image/png',compressRatio) 
+                dataUrl = await Utils.combineImgs(dataUrls, compressRatio, width, height) 
+                 
+            }else{
+                dataUrl = await render()
             }
             
+             
+            
+            
+            
+            if(info.splitRenderInfo){
+                viewer.viewports = [viewer.mainViewport]
+            }
             
             if(!Potree.settings.isOfficial){
                 Common.downloadFile(dataUrl, 'screenshot.png') 
@@ -3629,6 +3697,9 @@ export class Viewer extends ViewerBase{
                     viewport.width = old.width; viewport.height = old.height
                     viewport.view.copy(old.view) 
                     //viewport.view.applyToCamera(viewport.camera);  
+                    if(viewport.camera.isOrthographicCamera){
+                        viewport.camera.zoom = viewport.view.zoom
+                    }
                 }) 
                 
                 viewerMaster.updateScreenSize({forceUpdateSize:true})//更新像素
@@ -3642,7 +3713,7 @@ export class Viewer extends ViewerBase{
                     }) 
                 })  */
                 updateCamera()       
-                 
+                
                 finishDeferred.resolve({dataUrl, pose})
                
                
@@ -3659,12 +3730,14 @@ export class Viewer extends ViewerBase{
                 },500) //延迟:避免连续多次截图时释放点云
                 this.screenshoting = false
                
-                console.log('screenshot done: '+sid)
+                console.log('screenshot done: ' +  startTime,  'costTime', (Date.now() - startTime)  + 'ms')
             }
             
             {//恢复:
                 
                 this.backgroundOpacity = oldStates.bgOpacity;
+                this.pauseTestMaxLevel = false
+                
                 
                 if(info.type == 'measure' || info.type.includes('prism2d')){
                     this.modules.SiteModel.pauseUpdateEntity = false
@@ -3680,7 +3753,7 @@ export class Viewer extends ViewerBase{
                         prism.changeStyleForScreenshot(false)
                     })  
                 }
-                 
+               
                 
                 this.images360.panos.forEach(pano=>{
                     Potree.Utils.updateVisible(pano, 'screenshot', true)
@@ -3703,8 +3776,6 @@ export class Viewer extends ViewerBase{
                     
                     
                     
-                    mapViewport.camera.zoom = oldStates.mapZoom
-                    mapViewport.camera.updateProjectionMatrix()  
                     
                     
                     Potree.Utils.updateVisible(viewer.mapViewer.mapLayer.sceneGroup, 'screenshot-prism', true) 
@@ -3749,19 +3820,22 @@ export class Viewer extends ViewerBase{
         }
         if(!info.type.includes('prism2d')){
             viewports.push(mainViewport)
+            mainViewport.camera.isOrthographicCamera && (mainViewport.view.zoom = mainViewport.camera.zoom)
             oldStates.viewports.push(mainViewport.clone()) 
         }
         if(useMap){
             mapViewport = this.mapViewer.viewports[0]
+            mapViewport.view.zoom = mapViewport.camera.zoom
             viewports.push(mapViewport)
             oldStates.viewports.push(mapViewport.clone())  
             oldStates.attachedToViewer = this.mapViewer.attachedToViewer
-            oldStates.mapZoom = mapViewport.camera.zoom
+         
+             
              
             Potree.Utils.updateVisible(this.mapViewer.cursor, 'screenshot', false)//令mapCursor不可见
         }
          
-        
+        viewports.forEach(e=>e.active = false) //暂停渲染
         
         if(info.hideMarkers){
             this.images360.panos.forEach(pano=>{//令漫游点不可见
@@ -3776,54 +3850,11 @@ export class Viewer extends ViewerBase{
             this.backgroundOpacity = info.bgOpacity
         }
          
-        Potree.settings.pointDensity = 'screenshot'
+        Potree.settings.pointDensity = info.pointDensity || 'screenshot'
+        
         
         
         
-        let waitPointLoad = (done)=>{ 
-            let finish
-                done = done || screenshot 
-            let dealDone = ()=>{
-                //viewer.removeEventListener('overPointBudget',decreaseLevel)      
-                finish || done()
-                finish = true
-            }
-            let decreaseLevel = ()=>{ //降点云level
-                let levels = viewer.scene.pointclouds[0].visibleNodes.map(e=>e.getLevel()) 
-                //console.log(levels)
-                let actMaxLevel = Math.max.apply(null, levels) //实际加载到的最高的node level
-                //console.warn('decreaseLevel, 新maxLevel', actMaxLevel - 1, '原maxlevel', viewer.scene.pointclouds[0].maxLevel, 'numVisiblePoints', Potree.numVisiblePoints) 
-                 
-            }
-            if(Potree.settings.displayMode == 'showPointCloud'){
-                viewer.updateScreenSize({forceUpdateSize:true, width, height, forTarget:info.useRenderTarget})//需要先setSize才能加载范围内的点云
-            }
-            
-            
-            let maxTime = document.hidden ? 6000 : 1000 //离开页面后会变成1秒1帧   
-            
-            setTimeout(()=>{
-                if(Potree.pointsLoading && Potree.settings.displayMode == 'showPointCloud'){//如果还在加载 
-                    
-                    viewer.addEventListener('pointsLoaded',()=>{ //点云加载完时(不一定准确) 
-                         if(!finish)console.warn('加载完毕', ' numVisiblePoints', Potree.numVisiblePoints)                         
-                        dealDone()
-                    },{once:true})
-                     
-                    let overTime = ()=>{//超时不候(其实之后等待地图还会再加载几秒)
-                        if(document.hidden){
-                            return setTimeout(overTime, maxTime)  
-                        }
-                        if(!finish)console.warn('超时, numVisiblePoints', Potree.numVisiblePoints)  
-                        dealDone()
-                    }
-                     
-                    setTimeout(overTime, maxTime)
-                }else{
-                    dealDone()
-                } 
-            },200)//先加载一段时间  
-        }
          
         
         let focusDatasets = (measurements)=>{
@@ -3860,15 +3891,15 @@ export class Viewer extends ViewerBase{
                         console.log('waitMap: '+sid, Date.now(), this.mapViewer.mapLayer.loadingInProgress )  
                         this.mapViewer.waitLoadDone(screenshot.bind(this))//等待地图所有加载完 
                     }   
-                    waitPointLoad(waitMap) 
+                    this.waitPointLoad(waitMap,info.maxTimeForPointLoad) 
                 }else{  
-                    waitPointLoad()
+                    this.waitPointLoad(screenshot,info.maxTimeForPointLoad)
                 } 
             }
             
             let {promise} = this.focusOnObject(info.measurement, 'measure', 0, {
                 basePanoSize:1024,  gotoBestView:true,
-                minMapWidth: this.mapViewer.mapLayer.maps.find(e=>e.name == 'map').disabled ? 10 : 20  //有地图的话为了显示出名字需要更大范围
+                minMapWidth: THREE.Math.clamp(Math.min(viewer.bound.boundSize.x,  viewer.bound.boundSize.y) / (this.mapViewer.mapLayer.maps.find(e=>e.name == 'map').disabled ? 6 : 3),  2, 25)           //有地图的话为了显示出名字需要更大范围
             })//注意:不同角度截图 得到三维的会不一样,因为focusOnObject是根据方向的
             promise.done(()=>{  
                 //console.log('promise.done') 
@@ -3941,9 +3972,28 @@ export class Viewer extends ViewerBase{
                 }
             })
              
-        }else{
+        }else{//default
+        
+            let done = ()=>{
+                updateCamera()
+                if(info.splitRenderInfo){ 
+                    screenshot()
+                }else{
+                    this.waitPointLoad(screenshot, info.maxTimeForPointLoad)
+                }
+            }
+        
+            if(info.focusObjectInfo){ 
+                viewer.updateScreenSize({forceUpdateSize:true, width, height, forTarget:info.useRenderTarget}) //更新viewports相机透视 使focusOnObject在此窗口大小下
              
-            waitPointLoad()
+                let {promise} = this.focusOnObject( ...info.focusObjectInfo)
+                promise.done(()=>{
+                    done()
+                }) 
+            }else{
+                done()
+            } 
+            
         }            
          
          /*
@@ -3956,6 +4006,82 @@ export class Viewer extends ViewerBase{
         
     }
     
+    
+    
+    
+    async waitPointLoad(done, maxTimeForPointLoad, viewport = viewer.mainViewport, resolution ){ 
+        
+        return new Promise((resolve,reject)=>{  
+            let finish 
+            let dealDone = ()=>{
+                viewer.removeEventListener('update',update)     
+                viewer.removeEventListener('overPointBudget',decreaseLevel)     
+                finish || done && done()
+                finish = true
+                resolve() 
+            }
+            let decreaseLevel = (e)=>{ //降点云level 但基本降完也不会再加载低等级的点云了,所以是否直接不降?即使质量不平均
+                if(e.restQueueSize < 10)return ;//差不多完成了
+                let pointclouds = viewer.scene.pointclouds.filter(e=>e.visibleNodes.length)
+                console.log('准备decreaseLevel, numVisiblePoints', e.numVisiblePoints, '最小numVisiblePoints占比',  1/pointclouds.length )
+                 
+                pointclouds.forEach(pointcloud=>{
+                     let percent = pointcloud.numVisiblePoints / e.numVisiblePoints
+                     //console.log('numVisiblePoints占总比', pointcloud.dataset_id, percent )
+                     if(percent < 1/pointclouds.length)return
+                     
+                     let old = pointcloud.maxLevel
+                     let levels = pointcloud.visibleNodes.map(e=>e.getLevel())  
+                     let actMaxLevel = Math.max.apply(null, levels) //实际加载到的最高的node level
+                     pointcloud.maxLevel = actMaxLevel - 1
+                     console.warn(pointcloud.dataset_id,'decreaseLevel,新maxLevel', actMaxLevel - 1, '原maxlevel', old   ) 
+                    
+                 }) 
+            }
+            /* if(Potree.settings.displayMode == 'showPointCloud'){
+                viewer.updateScreenSize({forceUpdateSize:true, width, height, forTarget:info.useRenderTarget})//需要先setSize才能加载范围内的点云
+            } */
+            //updateCamera()  
+            //viewer.addEventListener('overPointBudget',decreaseLevel)
+            
+            let update = ()=>{ //前提:已经禁止渲染,仅加载点云
+                let camera = viewport.forViewOffset ? viewport.forViewOffset.camera : viewport.camera 
+                Potree.updatePointClouds(viewer.scene.pointclouds, camera , resolution || viewport.resolution );
+            }
+            viewer.addEventListener('update',update)            
+            update()
+            
+            let maxTime = maxTimeForPointLoad || 2000
+            document.hidden && (maxTime *= 6) //离开页面后会变成1秒1帧 
+             
+            setTimeout(()=>{
+                if(Potree.pointsLoading && Potree.settings.displayMode == 'showPointCloud'){//如果还在加载 
+                    
+                    viewer.addEventListener('pointsLoaded',()=>{ //点云加载完时(不一定准确) 
+                         if(!finish)console.log('加载完毕', ' numVisiblePoints', Potree.numVisiblePoints)                         
+                        dealDone()
+                    },{once:true})
+                      
+                    let overTime = ()=>{//超时不候(其实之后等待地图还会再加载几秒)
+                        if(document.hidden){
+                            return setTimeout(overTime, maxTime)  
+                        }
+                        if(!finish)console.warn('超时, numVisiblePoints', Potree.numVisiblePoints)  
+                        dealDone()
+                    }
+                     
+                
+                    setTimeout(overTime, maxTime)
+                }else{
+                    console.log('已经加载完,无需再加载点云 numVisiblePoints', Potree.numVisiblePoints)
+                    dealDone()
+                } 
+            },200)//先加载一段时间  
+          
+        }) 
+    }
+    
+    
 
     focusOnObject(object, type, duration, o={} ) {
         //飞向热点、测量线等 。
@@ -3973,7 +4099,7 @@ export class Viewer extends ViewerBase{
         
         
         let moveMap = (done)=>{
-            if(this.mapViewer){ 
+            if(this.mapViewer && !o.dontMoveMap){ 
                 //console.log('mapFocusOn: '+target.toArray())
                 const minMapWidth = o.minMapWidth || 2 //截图的时候要显示的地图范围较大,为了显示出地区名字
                 const minBound = new THREE.Vector2(minMapWidth,minMapWidth)//针对垂直线,在地图上只有一个点
@@ -4034,16 +4160,19 @@ export class Viewer extends ViewerBase{
                     var p = e.clone().applyMatrix4(inv);
                     bound.expandByPoint(p)
                 })
-                scale = 1.3;                
+                scale = 1.2;                
             }else{ 
                 bound = boundingBox.applyMatrix4(inv);
-                scale = 0.9; 
+                scale = 1 //0.9; 
             }
             boundSize = bound.getSize(new THREE.Vector3)
             
+            if(o.boundScale){
+                scale = o.boundScale
+            }
             
             {
-                
+                 
                 boundSize.x *= scale //稍微放大一些,不然会靠到屏幕边缘
                 boundSize.y *= scale  
                 let min = 0.0001
@@ -4137,7 +4266,7 @@ export class Viewer extends ViewerBase{
                 if(object.points.length>2){ 
                     let facePlane = object.getFacePlane(target) 
                     let normal = facePlane.normal.clone()                
-                    let angle = this.scene.view.direction.angleTo(normal)
+                    let angle = this.mainViewport.view.direction.angleTo(normal)
                     let minDiff = THREE.Math.degToRad(60) 
                     //console.log('angle',angle)
                     if(angle>minDiff && angle<Math.PI-minDiff){//当几乎正对时就不执行
@@ -4150,7 +4279,7 @@ export class Viewer extends ViewerBase{
                     }   
                 }else if(object.points.length == 2){ //线段
                     let lineDir = new THREE.Vector3().subVectors(object.points[0],object.points[1]).normalize()
-                    let angle = this.scene.view.direction.angleTo(lineDir)
+                    let angle = this.mainViewport.view.direction.angleTo(lineDir)
                     let maxDiff = Math.PI*0.25// 45度
                     if(angle<maxDiff || angle>Math.PI-maxDiff){//当几乎正对时就不执行
                         if(angle>Math.PI/2){  //令dir和lineDir成钝角
@@ -4342,7 +4471,7 @@ export class Viewer extends ViewerBase{
             }else if(Potree.settings.displayMode == 'showPanos'){
                 let pano = viewer.images360.fitPanoTowardPoint({
                     point : target,    
-                    dir :  this.scene.view.direction, //尽量不改相机方向,避免镜头晃动
+                    dir :  this.mainViewport.view.direction, //尽量不改相机方向,避免镜头晃动
                     checkIntersect: o.checkIntersect, sameFloor:o.sameFloor,
                     bestDistance,   maxDis:  o.maxDis   //越近越好,但不要太近,bestDistance左右差不多
                 })
@@ -4391,7 +4520,7 @@ export class Viewer extends ViewerBase{
 
  
         if(o.startCamera && o.endCamera){
-            viewer.scene.view.tranCamera(this.mainViewport,  { endPosition:position, target ,
+            viewer.mainViewport.view.tranCamera(this.mainViewport,  { endPosition:position, target ,
                 boundSize, 
                 callback:()=>{
                     //console.log('focusOnObjectSuccess: '+object.name,  type)
@@ -4400,7 +4529,7 @@ export class Viewer extends ViewerBase{
                 endYaw:o.endYaw,  endPitch:o.endPitch
             }, duration) 
         }else if(camera.type == "OrthographicCamera"){   
-            viewer.scene.view.moveOrthoCamera(this.mainViewport,  { endPosition:position, target ,
+            viewer.mainViewport.view.moveOrthoCamera(this.mainViewport,  { endPosition:position, target ,
                 boundSize, 
                 endYaw:o.endYaw,  endPitch:o.endPitch,
                 callback:()=>{
@@ -4409,7 +4538,7 @@ export class Viewer extends ViewerBase{
                 }, 
             }, duration)
         }else{ 
-            viewer.scene.view.setView({position, target, duration,
+            viewer.mainViewport.view.setView({position, target, duration,
                 endYaw:o.endYaw,  endPitch:o.endPitch,
                 callback:()=>{
                     //console.log('focusOnObjectSuccess: '+object.name,  type)
@@ -4500,7 +4629,7 @@ export class Viewer extends ViewerBase{
             }else{
                 if(math.closeTo(position, this.images360.position)) return 'posNoChange'
                  
-                viewer.scene.view.setView({position,  target, duration })   
+                viewer.mainViewport.view.setView({position,  target, duration })   
             }
             o.dontMoveMap || viewer.mapViewer.fitToPointcloud(pointcloud, duration)            
         }            
@@ -4878,7 +5007,7 @@ export class Viewer extends ViewerBase{
         if(o.pano != void 0){//pano 权重高于 position
             this.images360.flyToPano(o)
         }else{
-            this.scene.view.setView($.extend({},o, {callback}))
+            this.mainViewport.view.setView($.extend({},o, {callback}))
         }  
     }
     

+ 3 - 19
src/custom/viewer/map/Map.js

@@ -266,25 +266,9 @@ export class TiledMapBase extends THREE.EventDispatcher{
         this.objectGroup.updateMatrixWorld(!0)
     }
     
-    updateProjection(){
-         
+    updateProjection(){ 
         if(!this.transformMapToLocal){
-            if(proj4.defs("LOCAL_MAP")){
-                /* if(this.projection == "EPSG:4550"){
-                    this.transformMapToLocal = {
-                            forward:(e)=>{
-                                var a = viewer.transform.lonlatTo4550.inverse(e)
-                                return viewer.transform.lonlatToLocal.forward(a)
-                            },
-                        } 
-                }else{ */
-                     
-                    this.transformMapToLocal = proj4(this.projection, "LOCAL_MAP") 
-                    
-                    
-                    
-                //} 
-            }
+            this.transformMapToLocal = proj4(this.projection, "LOCAL_MAP")  
         }  
     } 
     
@@ -1005,5 +989,5 @@ export class TiledMapFromEntity extends TiledMapBase{
  目前看的几个场景floorplan原图是1米=33.03个像素  图宽度= 512*2^(max_depth-1) ,   map_size_m 代表整个地图是多少米
  
 
- 
+ 经纬度精度小数点后5位为米级别,6位为分米,7位是厘米
  */

+ 2 - 1
src/custom/viewer/viewerBase.js

@@ -113,6 +113,7 @@ export class ViewerBase extends THREE.EventDispatcher{
     
     
     updateScreenSize(o={}) { //有可能需要让viewport来判断,当窗口大小不变但viewport大小变时 
+        if(this.screenshoting && !o.forceUpdateSize)   return //截图时不允许因窗口改变大小而updateScreenSize
            
         var render = false, ratio, w, h;
         //记录应当render的大小
@@ -161,7 +162,7 @@ export class ViewerBase extends THREE.EventDispatcher{
                 var width_ = width * view.width
                 var height_ = height * view.height
                  
-                view.setResolution(Math.ceil(width_), Math.ceil(height_), width, height ) //本来应该是floor,但是这样奇数时会少一个像素,导致向左移一个像素且宽度少1。现在则多绘制1个像素,超出的1个像素应该不会绘制出来(但不知道其他地方是否有偏差,比如pick时)
+                view.setResolution(Math.ceil(width_), Math.ceil(height_), width, height ) 
                 
                 
                 if(height_ == 0)return  //avoid NAN

+ 2 - 1
src/materials/ExtendPointCloudMaterial.js

@@ -373,7 +373,8 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
         } 
          
         let doUpdate = (this.clipBoxes_in.length !== clipBoxes_in.length) || (this.clipBoxes_out.length != clipBoxes_out.length)
-                    || this.highlightBoxes.length !== highlightBoxes.length || this.bigClipInBox != bigClipInBox 
+                    || this.highlightBoxes.length !== highlightBoxes.length 
+                    || !this.bigClipInBox != !bigClipInBox 
                     || (this.prisms.length != prismPolygons.length) || this.prisms.maxPointsCount != prismPolygons.maxPointsCount                                                                                                                                                                                                                                                                                                                                                                              
 		//this.clipBoxes = clipBoxes; 
 		if (doUpdate){

+ 2 - 4
src/materials/shaders/pointcloud_new.vs

@@ -753,10 +753,8 @@ float getPointSize(){
 	#if defined fixed_point_size
 		pointSize = size;
 	#elif defined attenuated_point_size
-		if(uUseOrthographicCamera){
-			//pointSize = size * 100.0;  //加个乘数
-            
-            pointSize = size / uOrthoWidth  * resolution.x; //改成近似adaptive_point_size根据窗口缩放  size单位转化为米,如size为1代表一个点云在场景中1米宽
+		if(uUseOrthographicCamera){  
+            pointSize = size / uOrthoWidth  * resolution.x; //改成近似adaptive_point_size根据窗口缩放。其实就是size * zoom (size单位转化为米,如size为1代表一个点云在场景中1米宽,zoom代表1px=1/zoom米)
             maxSize_ =  orthoMaxSize;    //for panoEditor, when zoom in, need more details, rather than always same size
 
 		}else{  //近大远小,模拟真实mesh,边缘放大

+ 54 - 14
src/utils/TransformationToolNew.js

@@ -215,23 +215,27 @@ export class TransformationTool extends THREE.EventDispatcher{
     }
      
     changeStyle(style){// 切换单个mode & 多个mode混合 风格(因多个混合比较拥挤,需要做调整)
-        ['x','y','z'].forEach(axis=>{
+        let s1 = style == 'singleMode' ? 30 : 25;
+        let s2 = style == 'singleMode' ? 15 : 10
+        
+        ;['x','y','z'].forEach(axis=>{
             this.translationHandles['translation.'+axis].node.children.forEach(mesh=>{
                 if(mesh.name.includes('arrow')){
                     Potree.Utils.updateVisible(mesh, 'modeStyle', style == 'singleMode') 
                 }else if(mesh.name.includes('handle')){
-                    mesh.material.lineWidth = style == 'singleMode' ? 5 : 8
-                }
+                    mesh.material.lineWidth = style == 'singleMode' ? 5 : 7
+                } 
             })
+            this.rotationHandles['rotation.'+axis].translateNode.scale.set(s1,s1,s1);
             
         });
        
         ['xy','yz','xz'].forEach(axis=>{
             let handle = this.translationHandles['translation.plane.'+axis]
             
-            let s =  style == 'singleMode' ? 15 : 12
-            handle.node.children[0].scale.set(s,s,s);
-            handle.node.children[0].position.fromArray(handle.alignment2).multiplyScalar(s*1.5)        
+            
+            handle.node.children[0].scale.set(s2,s2,s2);
+            handle.node.children[0].position.fromArray(handle.alignment2).multiplyScalar(s2*1.5)        
         })
         
         Potree.Utils.updateVisible(this.scaleHandles['lines'].node, 'modeStyle', style == 'singleMode') 
@@ -300,7 +304,7 @@ export class TransformationTool extends THREE.EventDispatcher{
                 
                 
                 let point = new THREE.Vector3(0,0,length/2)//new THREE.Vector3().copy(alignment).multiplyScalar(length/2)
-                mesh = LineDraw.createFatLine([point, point.clone().negate()],{lineWidth:5, mat : new LineDraw.createFatLineMat(matProp)})
+                mesh = LineDraw.createFatLine([point, point.clone().negate()],{lineWidth:6, mat : new LineDraw.createFatLineMat(matProp)})
                 lookAtPoint = alignment 
                 renderOrder = 10; 
                 
@@ -490,10 +494,10 @@ export class TransformationTool extends THREE.EventDispatcher{
     
     
 	initializeRotationHandles(){
-		let boldAdjust = 2.5;
-		let torusGeometry = new THREE.TorusGeometry(1.4, boldAdjust * 0.015, 8, 64, Math.PI / 2);
+		let boldAdjust = 2.3;
+		let torusGeometry = new THREE.TorusGeometry(1.3, boldAdjust * 0.015, 8, 64, Math.PI / 2);
 		//let outlineGeometry = new THREE.TorusGeometry(1, boldAdjust * 0.018, 8, 64, Math.PI / 2);
-		let pickGeometry = new THREE.TorusGeometry(1.4, boldAdjust * 0.06, 6, 4, Math.PI / 2);
+		let pickGeometry = new THREE.TorusGeometry(1.3, boldAdjust * 0.06, 6, 4, Math.PI / 2);
 
 		for(let handleName of Object.keys(this.rotationHandles)){
 			let handle = this.handles[handleName];
@@ -784,14 +788,50 @@ export class TransformationTool extends THREE.EventDispatcher{
             if(drag.intersectionStart){ 
                 let pointer = this.viewer.inputHandler.pointer
                 let ray = Utils.mouseToRay(pointer, camera);
+                //方向滑动所在面
+                let normal  
+                let axisName = handle.name.split('.').pop()
                 
-                let normal = viewer.mainViewport.view.direction
-                drag.dragPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, posWorld/* drag.worldPositionStart */)//过center的与视线垂直的平面
+                if(axisName == 'xyz'){ // 平行于屏幕滑动
+                    normal = viewer.mainViewport.view.direction
+                }else{
+                    var alignVector = new THREE.Vector3();
+                    normal = new THREE.Vector3
+                    
+                    let unitX = new THREE.Vector3( 1, 0, 0 ).applyQuaternion(drag.objectQua)
+                    let unitY = new THREE.Vector3( 0, 1, 0 ).applyQuaternion(drag.objectQua)
+                    let unitZ = new THREE.Vector3( 0, 0, 1 ).applyQuaternion(drag.objectQua)
+                    
+                    switch (axisName) {  
+                        case 'x':
+                            alignVector.copy( viewer.mainViewport.view.direction ).cross( unitX );
+                            normal.copy( unitX ).cross( alignVector );
+                            break;
+                        case 'y':
+                            alignVector.copy( viewer.mainViewport.view.direction ).cross( unitY );
+                            normal.copy( unitY ).cross( alignVector );
+                            break;
+                        case 'z':
+                            alignVector.copy( viewer.mainViewport.view.direction ).cross( unitZ );
+                            normal.copy( unitZ ).cross( alignVector );
+                            break;
+                        case 'xy':
+                            normal.copy( unitZ );
+                            break;
+                        case 'yz':
+                            normal.copy( unitX );
+                            break;
+                        case 'xz': 
+                            normal.copy( unitY );
+                            break; 
+                    }//参考transformControls.使跟手 
+                }
                  
                 
-                
+                drag.dragPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, posWorld/* drag.worldPositionStart */)//过center的与视线垂直的平面
+                  
                 let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3());
-
+ 
                 if (I) {
                     let offset = new THREE.Vector3().subVectors( I , drag.worldPositionStart );
 

+ 4 - 4
src/viewer/ExtendView.js

@@ -244,7 +244,7 @@ class ExtendView extends View {
                 let a = this.clone();
                 a.quaternion = endQuaternion;
                 info.endYaw = a.yaw; info.endPitch = a.pitch;
-                console.log('turn to yaw')
+                //console.log('turn to yaw')
             }
         }
         
@@ -257,7 +257,7 @@ class ExtendView extends View {
             if(Math.abs(startYaw - endYaw)>Math.PI){//如果差距大于半个圆,就要反个方向转(把大的那个数字减去360度)
                 startYaw > endYaw ? (startYaw -= Math.PI*2) : (endYaw -= Math.PI*2) 
             }
-            console.log('startYaw', startYaw, 'endYaw', endYaw)
+            //console.log('startYaw', startYaw, 'endYaw', endYaw)
 		}  
          
         if(endQuaternion){ 
@@ -388,7 +388,7 @@ class ExtendView extends View {
                         //onUpdate时更新endzoom是因为画布大小可能更改
                     }  
                     
-                    camera.zoom = endZoom * progress + startZoom * (1 - progress)
+                    this.zoom = camera.zoom = endZoom * progress + startZoom * (1 - progress)    //view里也加一下,有些地方需要记录,如截图
                     camera.updateProjectionMatrix() 
                     onUpdate && onUpdate(progress, delta)
                 } 
@@ -413,7 +413,7 @@ class ExtendView extends View {
         transitions.start(( progress)=>{ 
             let oldPos = pointerPos.clone().unproject(camera);
             
-            camera.zoom = endZoom * progress + startZoom * (1 - progress)
+            this.zoom = camera.zoom = endZoom * progress + startZoom * (1 - progress)
             camera.updateProjectionMatrix() 
             
             

+ 17 - 6
src/viewer/PropertyPanels/CameraAnimationPanel.js

@@ -21,6 +21,7 @@ export class CameraAnimationPanel{
 					<span>Time: </span><span id="lblTime"></span> <div id="sldTime"></div>
 
 					<input name="play" type="button" value="play"/>
+					<input name="pause" type="button" value="pause"/>
 				</span>
 			</div>
 		`);
@@ -30,6 +31,13 @@ export class CameraAnimationPanel{
 			animation.play();
 		});
 
+        const elPause = this.elContent.find("input[name=pause]");
+		elPause.click( () => {
+			animation.pause();
+		});
+
+
+
 		const elSlider = this.elContent.find('#sldTime');
 		elSlider.slider({
 			value: 0,
@@ -40,7 +48,7 @@ export class CameraAnimationPanel{
 				animation.set(ui.value);
                 animation.updateFrustum()
 			}
-		});
+		}); 
 
 		let elDuration = this.elContent.find(`input[name=spnDuration]`);
 		elDuration.spinner({
@@ -94,7 +102,10 @@ export class CameraAnimationPanel{
 
 				const elAdd = elNewKeyframe.find("input[name=add]");
 				elAdd.click( () => {
-					animation.createControlPoint(index);
+					animation.createControlPoint(index,
+                    {   position: viewer.scene.view.position,
+                        target: viewer.scene.view.getPivot()
+                    });
                     animation.changeCallback()
 				});
 
@@ -123,16 +134,16 @@ export class CameraAnimationPanel{
 				const elMove = elKeyframe.find("img[name=move]");
 				const elDelete = elKeyframe.find("img[name=delete]");
 
-				elAssign.click( () => { 
+				elAssign.click( () => { //修改
                     animation.posCurve.points[index].copy(viewer.scene.view.position);
-					animation.targetCurve.points[index].copy(viewer.scene.view.getPivot()); 
-                    animation.changeCallback()
+					animation.targets[index].position.copy(viewer.scene.view.getPivot()); 
+                    animation.changeCallback(index)
                      
 				});
 
 				elMove.click( () => {  
 					viewer.scene.view.position.copy(animation.posCurve.points[index]);
-					viewer.scene.view.lookAt(animation.targetCurve.points[index]); 
+					viewer.scene.view.lookAt(animation.targets[index].position); 
 				});
 
 				elDelete.click( () => {