Przeglądaj źródła

antialias \ clip \ polygonDraw

xzw 16 godzin temu
rodzic
commit
93d86b9613
38 zmienionych plików z 1654 dodań i 770 usunięć
  1. 1 1
      examples/4dkk.html
  2. 5 5
      libs/three.js/build/three.module.js
  3. 3 1
      src/ExtendPointCloudOctree.js
  4. 14 9
      src/Potree.js
  5. 14 6
      src/custom/materials/postprocessing/FXAAShader.js
  6. 3 0
      src/custom/materials/postprocessing/SMAAPass.js
  7. 11 0
      src/custom/materials/postprocessing/SMAAShader.js
  8. 4 8
      src/custom/materials/postprocessing/SSAARenderPass.js
  9. 7 1
      src/custom/materials/postprocessing/TAARenderPass.js
  10. 297 148
      src/custom/modules/clipModel/Clip.js
  11. 5 5
      src/custom/modules/clipping/Clipping.js
  12. 115 69
      src/custom/modules/datasetAlignment/Alignment.js
  13. 6 6
      src/custom/modules/panos/Images360.js
  14. 25 37
      src/custom/modules/panos/Panorama.js
  15. 5 3
      src/custom/modules/panos/tile/PanoRenderer.js
  16. 7 10
      src/custom/objects/Magnifier.js
  17. 1 1
      src/custom/objects/Sprite.js
  18. 1 1
      src/custom/objects/tool/Measure.js
  19. 240 92
      src/custom/objects/tool/Path.js
  20. 1 1
      src/custom/objects/tool/TransformControls.js
  21. 1 1
      src/custom/objects/tool/ctrlPolygon.js
  22. 2 3
      src/custom/settings.js
  23. 229 7
      src/custom/start.js
  24. 3 2
      src/custom/three.shim.js
  25. 48 6
      src/custom/utils/Common.js
  26. 4 0
      src/custom/utils/CursorDeal.js
  27. 103 0
      src/custom/utils/PanoScreenshot.js
  28. 26 14
      src/custom/utils/SplitScreen.js
  29. 1 1
      src/custom/utils/math.js
  30. 98 79
      src/custom/viewer/ViewerNew.js
  31. 6 7
      src/custom/viewer/map/MapViewer.js
  32. 3 3
      src/custom/viewer/viewerBase.js
  33. 5 5
      src/navigation/InputHandlerNew.js
  34. 251 207
      src/utils/TransformationToolNew.js
  35. 7 5
      src/viewer/EDLRendererNew.js
  36. 64 14
      src/viewer/ExtendView.js
  37. 32 12
      src/viewer/View.js
  38. 6 0
      src/viewer/potree.css

+ 1 - 1
examples/4dkk.html

@@ -51,7 +51,7 @@
         if (number.indexOf("#") != -1) {
             number = number.substring(0, number.indexOf("#"));
         }*/ 
-        
+        Potree.settings.noAA = browser.urlHasValue('noAA');
         var number = browser.urlHasValue('m',true);
         console.log(number)
         Potree.start(document.getElementById("potree_render_area"),null, number);

+ 5 - 5
libs/three.js/build/three.module.js

@@ -17430,14 +17430,14 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 
 			const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' );
 			const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' );
-
-			console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors );
+            //xzw add name
+			console.error( 'THREE.WebGLProgram: shader error: ', parameters.name||'',  gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors );
             //xzw add:
-            if(fragmentErrors){
+            /* if(fragmentErrors){
                 console.log(fragmentGlsl.split("\n").map((a, i) => `${i + 1}`.padEnd(5) + a).join("\n") )
             }else{
                 console.log(vertexGlsl.split("\n").map((a, i) => `${i + 1}`.padEnd(5) + a).join("\n") )
-            }
+            } */
             
 		} else if ( programLog !== '' ) {
 
@@ -17693,7 +17693,7 @@ function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingSta
 		const currentRenderTarget = renderer.getRenderTarget();
 
 		const parameters = {
-
+            name: material.name, //xzw add
 			isWebGL2: isWebGL2,
 
 			shaderID: shaderID,

+ 3 - 1
src/ExtendPointCloudOctree.js

@@ -780,7 +780,9 @@ export class ExtendPointCloudOctree extends PointCloudOctree{
                 Potree.settings.showClass && attributes.classification ? 'classification' :  
                 Potree.settings.showHotTemp && attributes.temp ? 'temp' : 
                 Potree.settings.showHotIr && !Potree.settings.isOfficial && attributes.ir ? 'ir' : 
-                Potree.settings.cloudAttributeName || 'rgba'
+                this.material.attributeName || Potree.settings.cloudAttributeName || 'rgba'
+                
+                
     }
     
     

+ 14 - 9
src/Potree.js

@@ -116,10 +116,7 @@ export * from "./modules/loader/2.0/OctreeLoader.js";
 export {OrbitControls} from "./navigation/OrbitControls.js";
 export {FirstPersonControls} from "./navigation/FirstPersonControls.js"; 
 	 
-     
- 
-     
-     
+      
 import "./extensions/OrthographicCamera.js";
 import "./extensions/PerspectiveCamera.js";
 import "./extensions/Ray.js";
@@ -131,7 +128,7 @@ import {EptLoader} from "./loader/EptLoader.js";
 import {ExtendPointCloudOctree} from "./ExtendPointCloudOctree.js"; 
 import {WorkerPool} from "./WorkerPool.js";
 
-
+export * from "./custom/utils/PanoScreenshot.js";                                              
 
 
 
@@ -197,7 +194,17 @@ export async function loadFile(path, params , callback, onError){
         }).catch(onError) 
     }else{
         try{
-            if(Object.keys(params).length > 0){
+            let info={}
+            if(fetchMethod == 'post')info.method = 'POST'
+            if(params.sendInfo){
+                info.headers = {
+                    'Content-Type': 'application/json' // 告诉后端发送的是 JSON  
+                }
+                info.body = JSON.stringify(params.sendInfo) 
+                delete params.sendInfo//清空 
+            } 
+            
+            if(Object.keys(params).length > 0){//似乎是旧的post?
                 path+='?'
                 let index = 0
                 for(let i in params){ 
@@ -206,9 +213,7 @@ export async function loadFile(path, params , callback, onError){
                     index ++ 
                 }
             }
-            let info={}
-            if(fetchMethod == 'post')info.method = 'POST'
-            
+             
             
             if(Potree.fileStorage){//本地直接获取
                 Potree.fileStorage.get(path).then(data=>{ 

+ 14 - 6
src/custom/materials/postprocessing/FXAAShader.js

@@ -1,6 +1,14 @@
 import * as THREE from "../../../../libs/three.js/build/three.module.js";
 
 /**
+
+这是一种后处理技术。
+它先将渲染好的图像进行分析,找出有锯齿的边缘,然后对这些边缘像素进行模糊和混合处理,从而在视觉上消除锯齿。
+
+- 速度极快,性能开销很小
+- 不占用额外显存
+- 实现简单,适用于所有硬件
+
  * NVIDIA FXAA by Timothy Lottes
  * https://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf
  * - WebGL port by @supereggbert
@@ -13,8 +21,8 @@ const FXAAShader = {
 	uniforms: {
 
 		'tDiffuse': { value: null },
-		'resolution': { value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
-
+		'resolution': { value: new THREE.Vector2( 1 / 1024, 1 / 512 ) },
+        edgeDetectionQuality: { value:0.1}
 	},
 
 	vertexShader: /* glsl */`
@@ -34,9 +42,9 @@ const FXAAShader = {
 	uniform sampler2D tDiffuse;
 
 	uniform vec2 resolution;
-
+    uniform float edgeDetectionQuality;
 	varying vec2 vUv;
-
+    
 	// FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro (biro@archilogic.com)
 
 	//----------------------------------------------------------------------------------
@@ -266,8 +274,8 @@ const FXAAShader = {
 	}
 
 	void main() {
-			const float edgeDetectionQuality = 0.1 ;//.05 ;  //越高,越保留细节;越低,越平滑 但模糊 
-			const float invEdgeDetectionQuality = 1. / edgeDetectionQuality;
+			//const float edgeDetectionQuality = 0.1 ;//.05 ;  //越高,越保留细节;越低,越平滑 但模糊 
+            float invEdgeDetectionQuality = 1. / edgeDetectionQuality;
 
 			gl_FragColor = FxaaPixelShader(
 					vUv,

+ 3 - 0
src/custom/materials/postprocessing/SMAAPass.js

@@ -1,4 +1,7 @@
 /**
+
+
+
  * @author mpk / http://polko.me/
  */
 

+ 11 - 0
src/custom/materials/postprocessing/SMAAShader.js

@@ -1,6 +1,17 @@
 /**
  * @author mpk / http://polko.me/
  *
+ 
+  SMAA(增强型亚像素形态抗锯齿)	作为FXAA的增强版,它也是一种后处理技术。
+  它通过更智能的边缘检测和形态学分析来定位锯齿,再进行混合,效果比FXAA更清晰
+ 
+ - 画质优于FXAA,比FXAA更清晰
+- 性能开销适中(有数据称在2000个物体场景下比FXAA多消耗约15帧)
+ 
+ 
+ 不同于 MSAA (Multisample Anti-Aliasing)一种硬件级别的技术,webgl自带的 。
+ 
+ 
  * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
  * Preset: SMAA 1x Medium (with color edge detection)
  * https://github.com/iryoku/smaa/releases/tag/v2.8

+ 4 - 8
src/custom/materials/postprocessing/SSAARenderPass.js

@@ -1,5 +1,6 @@
-/**
-*
+/** (超级采样抗锯齿)
+*这是一种“暴力”但效果最好的方法。(性能最差)
+它将场景渲染到一个比屏幕大得多的分辨率上,然后再降采样到屏幕大小,从而获得极其平滑的边缘
 * Supersample Anti-Aliasing Render Pass
 *
 * @author bhouston / http://clara.io/
@@ -10,12 +11,7 @@
 *
 */
 
-
-
-
-
-
-
+ 
 
 import * as THREE from "../../../../libs/three.js/build/three.module.js";
 import {Pass, ShaderPass} from './ShaderPass'

+ 7 - 1
src/custom/materials/postprocessing/TAARenderPass.js

@@ -1,5 +1,11 @@
 /**
- *
+ * (时间性抗锯齿)
+ 这是一种比较现代的技术。它利用前后几帧的信息进行混合来消除锯齿和闪烁。它会把当前帧的采样点位置稍微偏移,然后与上一帧混合
+ 
+  效果可以媲美SSAA
+- 能有效解决动态场景中的闪烁 
+ 
+ 
  * Temporal Anti-Aliasing Render Pass
  *
  * @author bhouston / http://clara.io/

+ 297 - 148
src/custom/modules/clipModel/Clip.js

@@ -15,38 +15,62 @@ import AxisViewer from "../../objects/tool/AxisViewer.js";
 
 const defaultBoxWidth = 16;  //navvis:  10
                             //navvis position: si {x: 0, y: 0, z: 0}
+                            
+const orthoReverseQua = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI,0,0))//翻转
 const cameraProps = [ 
     { 
         name : 'top',    
         axis:["x","y"],  
         direction : new THREE.Vector3(0,0,-1), //镜头朝向
+        pitch: Math.PI/2, 
+        yaw:0,
         openCount:0,
     },
-    { 
+    /* { 
         name : 'front', 
         axis:["x","z"], 
         direction : new THREE.Vector3(0,1,0), 
         openCount:0,
+    }, */
+    { 
+        name : 'side', 
+        axis:["y","z"], 
+        direction : new THREE.Vector3(0,1,0), //front  为了使初始yaw为0所以不选right
+        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI/2,0,0)),
+        pitch: 0, 
+        yaw:0,
+        openCount:0,
     },
+    
     {
+        name : 'local-top',  
+        openCount:0,
+    },
+    {
+        name : 'local-side',  
+        direction : new THREE.Vector3(1,0,0),//right   产品选的默认朝x
+        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI/2,Math.PI/2,0)),  //local时转到右边这个视角
+        openCount:0,
+    },
+    
+    /* {
         name : 'mainView',
         openCount:0,
-    }
+    }, */ 
 ] 
 
 
   
-var Clip = {
+var Clip = {//裁剪 剖面 测绘
     bus : new THREE.EventDispatcher,
     selectedDatasets : [],
-    
+    orthoViewType:'world',
+    angleRoundAxis: 0, //侧转角度
     changeCallback(force){ 
         
         if(this.activeViewName != 'mainView'){
             this.splitScreenTool.updateCameraOutOfModel() //更新相机位置
         }
-        
-        
          
         if(Potree.settings.isOfficial && this.showMap){
             let fun = ()=>{ 
@@ -63,9 +87,10 @@ var Clip = {
         }  
     },
 
-    enter:function(){
+    enter(){
         this.initViews()
-         
+        viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect  left等
+        
         this.previousView = { 
 			position: viewer.scene.view.position.clone(),
 			target: viewer.scene.view.getPivot(),
@@ -140,10 +165,20 @@ var Clip = {
                     this.mapBox.rotateBar.rotation.z = this.mapBox.angle
                     this.mapBox.updatePoints()
                 }
+                if(this.orthoViewType == 'local'){//同步box的旋转 
+                    /* let endQua = this.getViewQua(this.activeViewName) 
+                    this.splitScreenTool.rotateCamera(this.orthoViewport, null, null, endQua) //重置旋转轴,转到curAngle为0的状态
+                    let angleRoundAxis_ = this.angleRoundAxis
+                    this.angleRoundAxis = 0 //reset
+                    this.rotateSideCamera( angleRoundAxis_ ) //keep angle */
+                    this.updateRot()
+                }
+                
                 this.changeCallback()
+                  
             })
             viewer.scene.addVolume(this.box);
-            
+            this.box.updateMatrixWorld()
         }
         
         if(this.showMap){//map
@@ -176,8 +211,8 @@ var Clip = {
             })
         }
         
+        this.switchView('top')
         
-        this.switchView('mainView')
         
         {
             //viewer.setClipTask(ClipTask["SHOW_INSIDE"])
@@ -186,7 +221,7 @@ var Clip = {
         
         Potree.settings.unableNavigate = true
         Potree.settings.ifShowMarker = false
-        Potree.Utils.updateVisible(viewer.scene.overlayScene/* viewer.measuringTool.scene */, 'clipModel', false)   
+        Potree.Utils.updateVisible(viewer.scene.overlayScene, 'clipModel', false)   
         //Potree.Utils.updateVisible(viewer.mapViewer.cursor, 'clipModel', false)//隐藏地图游标
         viewer.inputHandler.toggleSelection(this.box);
         viewer.inputHandler.fixSelection = true
@@ -235,7 +270,7 @@ var Clip = {
                                     bound.expandByPoint(viewer.scene.view.position)
                                 let size = bound.getSize(new THREE.Vector3)
                                 let center = bound.getCenter(new THREE.Vector3)
-                                let margin = viewer.mainViewport.resolution.clone().multiplyScalar(0.3) 
+                                let margin = this.orthoViewport.resolution.clone().multiplyScalar(0.3) 
                                 viewer.mapViewer.moveTo(center, size, 100, margin)   
                             }
                             Clip.switchMapCount++
@@ -258,13 +293,18 @@ var Clip = {
         setTimeout(()=>{this.changeCallback(true)},1)
     },
     
-    leave:function(){
+    leave(){
+        
+        this.splitScreenTool.unfocusViewport()
+        viewer.viewports = [viewer.mainViewport]
+        viewer.mainViewport.width = 1, viewer.mainViewport.left = 0
+        viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect  left等
         viewer.inputHandler.fixSelection = false 
         viewer.scene.removeVolume(this.box);
         
         this.showMap && this.mapBox.dispose()
         //viewer.setControls(viewer.fpControls);
-        this.switchView('mainView')
+       
         Potree.settings.unableNavigate = false
         Potree.settings.ifShowMarker = this.previousView.ifShowMarker
         Potree.Utils.updateVisible(viewer.scene.overlayScene/* viewer.measuringTool.scene */, 'clipModel', true)  
@@ -287,125 +327,207 @@ var Clip = {
             this.events = null 
         }
         this.editing = false
+        
+        
+        
     },
     
+    
+    
     initViews(){
-        if(this.views){
+        if(!this.views){
+            this.views = {} 
+             
+            this.orthoCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
+            this.orthoCamera.up.set(0,0,1)
+             
+            for(let i=0;i<cameraProps.length;i++){
+                let prop = cameraProps[i];
+                let view = new ExtendView()  
+                this.views[prop.name] = view   
+                view.name = prop.name
+                prop.direction && (view.direction = prop.direction)
+                /* prop.name.includes('local') &&  */view.setRollFree(true) //即使不随box转也设置为free,否则因pitch有限制用quaternion转会出问题
+            }
             
-            this.views.top.pitch = -Math.PI
+            this.orthoViewport = new Viewport(this.views.top, this.orthoCamera, {
+                left:0, bottom:0, width:0.5, height: 1, name:'ClipOrtho'  
+            }) 
+              
+            this.splitScreenTool = new SplitScreen
+            this.targetPlane = this.orthoViewport.targetPlane = new THREE.Plane()
+            this.shiftTarget = this.orthoViewport.shiftTarget = new THREE.Vector3 //project在targetPlane上的位置
+            
+            {//local模式隐藏ortho的旋转手柄
+                let rotationEnable 
+                viewer.addEventListener("render.begin",(e)=>{ 
+                    if(e.viewport == this.orthoViewport && this.orthoViewType == 'local'){
+                        rotationEnable = viewer.transformationTool.modesEnabled.rotation
+                        
+                        /* if(!rotationEnable){
+                            debugger
+                        } */
+                        rotationEnable && this.setModeEnable('rotation',false)
+                        //console.log('hide')
+                    }
+                })
+                viewer.addEventListener("render.end",(e)=>{ 
+                    if(e.viewport == this.orthoViewport && this.orthoViewType == 'local'){
+                        rotationEnable && this.setModeEnable('rotation',true)
+                        //console.log('show')
+                    }
+                })
+            }
+        }else{
+            for(let i in this.views){
+                this.views[i].angleRoundAxis = 0  
+            }
+            /* this.views.top.pitch = -Math.PI / 2
             this.views.top.yaw = 0
-            this.views.front.pitch = 0
-            this.views.front.yaw = 0        //--还原for bugID 44757
+            this.views.side.pitch = 0
+            this.views.side.yaw = 0   */      //--还原for bugID 44757
             
-            return
-        }
-        
-        this.views = {}
-        this.cameras = {}
-        this.orthoCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
-        this.orthoCamera.up.set(0,0,1)
+            this.angleRoundAxis = 0
+            
+        } 
         
+        viewer.mainViewport.width = 0.5, viewer.mainViewport.left = 0.5    
+        viewer.viewports = [this.orthoViewport, viewer.mainViewport]
         
-        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]
+    
+    setOrthoReverse(reverse, force){//翻转到背面。即从下翻转180度到另一边去看。检查:先把框缩小改薄。表现为上下翻转
+        if(this.orthoReverse == reverse)return
+        this.orthoReverse = reverse
+        /* if(this.orthoViewType == 'local'){
+            this.updateLocalRot()
+        }else{ 
+            this.orthoViewport.view.quaternion = this.orthoViewport.view.quaternion.premultiply(orthoReverseQua)
+            let angleRoundAxis_ = this.angleRoundAxis
+            this.angleRoundAxis = 0 //reset
+            this.rotateSideCamera( angleRoundAxis_ ) 
+           
+            
+            //this.splitScreenTool.updateCameraOutOfModel() //更新相机位置
+        } */
+        this.updateRot()
+    },
+    
+    
+    getViewQua(name){ 
         let prop = cameraProps.find(e=>e.name == name)
+       
+        let qua = new THREE.Quaternion()
+        this.tempRotAxis_ = new THREE.Vector3(0,0,1) 
+        if(this.orthoViewType == 'local'){ 
+            this.tempRotAxis_.applyQuaternion(this.box.quaternion) //record
+            qua.copy(this.box.quaternion)//获取box某个面的quaternion
+        }  
+        if(prop.quaternion){
+            qua.multiplyQuaternions(qua, prop.quaternion)
+        } 
+        if(this.orthoReverse){ 
+            qua.multiplyQuaternions(qua, orthoReverseQua)
+            //this.tempRotAxis_.negate() //不反旋转轴了,太复杂
+        } 
+        return  qua
+        
+    },
+    
+    /* updateLocalRot(){
+        let endQua = this.getViewQua(this.activeViewName2) 
+        this.splitScreenTool.rotateCamera(this.orthoViewport, null, null, endQua) //重置旋转轴,转到curAngle为0的状态
+        let angleRoundAxis_ = this.angleRoundAxis
+        this.angleRoundAxis = 0 //reset
+        this.rotateSideCamera( angleRoundAxis_ ) //keep angle
+    }, */
+    updateRot(){
+        let endQua = this.getViewQua(this.activeViewName2) 
+        this.splitScreenTool.rotateCamera(this.orthoViewport, null, null, endQua) //重置旋转轴,转到curAngle为0的状态
+        let angleRoundAxis_ = this.angleRoundAxis
+        this.angleRoundAxis = 0 //reset
+        this.rotateSideCamera( angleRoundAxis_ ) //keep angle
+    },
+    
+    
+    switchViewType(type, viewName = this.activeViewName){//左侧正交视图类型切换 世界的side & top -> box的side & top
+        this.orthoViewType = type
+        this.switchView(viewName) 
+    },
+    
+    //如果旋转跟随box的话,用同一个view,因为方向不定, 而且还要实时跟随box
+    switchView( name ){//替换view和camera到mainViewport  左侧的正交视图
          
-        let {boundSize, center, boundingBox} = viewer.bound
-        this.lastViewName = this.activeViewName
+        this.lastViewName = this.activeViewName // top side
+        this.lastViewName2 = this.activeViewName2 // top side rotWithBox 
+        let name2 = this.orthoViewType == 'local' ? 'local-'+name : name  
+        //if(this.activeViewName2 == name2 )return
+          
         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)
+        this.activeViewName2 = name2
         
+        let view = this.views[name2]
+        let prop = cameraProps.find(e=>e.name == name2)
         
-        viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect  left等
+        let {boundSize, center, boundingBox} = viewer.bound
+         
+        let lastView = this.views[this.lastViewName2]  
+        lastView && (lastView.zoom = this.orthoCamera.zoom)
+        view.zoom && (this.orthoCamera.zoom = view.zoom)
+        this.orthoViewport.view = view
+        /* this.lastViewName2?.includes('local') &&  */lastView && (lastView.angleRoundAxis = this.angleRoundAxis)
+        this.angleRoundAxis = view.angleRoundAxis || 0
+        /* if(this.orthoViewType == 'local'){     
+            this.updateLocalRot(); //恢复上次的侧转角度
+        }else{
+            //view.direction = prop.direction.multiplyScalar(this.orthoReverse ? -1 : 1) 
+            this.setRev
+        } */
+        this.updateRot(); //恢复上次的侧转角度 更新reverse
+        
+        //viewer.updateScreenSize({forceUpdateSize:true})//更新camera aspect  left等
         
-        view.applyToCamera(camera)
+        
+        view.applyToCamera(this.orthoCamera)
+        this.orthoCamera.updateProjectionMatrix()
         let ifFocus
         let boxBound = Clip.box.boundingBox.clone().applyMatrix4(Clip.box.matrixWorld)
-        if(!Potree.Utils.isInsideFrustum(boxBound, camera)){//屏幕上没有box的话
+        if(!Potree.Utils.isInsideFrustum(boxBound, this.orthoCamera)){//屏幕上没有box的话
             ifFocus = true  
         }
+     
+        this.targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), center )  
+        this.targetPlane.projectPoint(view.position, this.shiftTarget )  //target转换到过模型中心的平面,以保证镜头一定在模型外
+        //view.position.copy(this.splitScreenTool.getPosOutOfModel(viewer.mainViewport))
          
-        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
+        }
+          
+        ifFocus || this.splitScreenTool.updateCameraOutOfModel() //更新相机位置
          
-            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
-             
-        } 
+         
+        if(name == 'front'){
+            //viewer.mainViewport.alignment = {translate:true, rotateSide:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动
+            this.orthoViewport.rotateSide = true
+        }else{
+            this.orthoViewport.rotateSide = false
+        }  
+   
                         
          
         ifFocus && this.focusOnObject(this.box) 
-            
-            
-            
-        
+             
         //首次到front最好能自动对准box的长边的角度上  
           
         prop.openCount ++; 
     },
     
-    focusOnObject(box, duration=0){
-        if(this.activeViewName == 'mainView'){
+    
+    focusOnObject(box, duration=0, viewName){
+        if(viewName == '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) 
@@ -424,26 +546,36 @@ var Clip = {
                 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)
+    rotateSideCamera(rad){//侧视图绕轴旋转到的角度  angle: -180  ~ 180 
+   
+        /* if(this.orthoViewType == 'local'){//local模式时旋转轴为box的z轴
+            //prop.rotStartQua 
+            let diff = rad - this.angleRoundAxis
+            diff != 0 && this.splitScreenTool.rotateSideCamera(this.orthoViewport, diff, this.tempRotAxis_)
+            this.angleRoundAxis = rad
+        }else{//world模式时旋转轴为z轴
+            let diff = rad - this.angleRoundAxis//this.orthoViewport.view.yaw
+            console.log(diff,rad,this.orthoViewport.view.yaw)
+            diff != 0 && this.splitScreenTool.rotateSideCamera(this.orthoViewport, diff, this.orthoReverse ? new THREE.Vector3(0,0,-1) : new THREE.Vector3(0,0,1))
+            this.angleRoundAxis = rad
+        }   */
+        let diff = rad - this.angleRoundAxis
+        diff != 0 && this.splitScreenTool.rotateSideCamera(this.orthoViewport, diff, this.tempRotAxis_)
+        this.angleRoundAxis = rad
   
         
     },    
 
     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 ) 
+        var margin = this.orthoViewport.resolution.clone().multiplyScalar(0.4)// {x: this.orthoViewport.resolution.x*0.4, y: this.orthoViewport.y*0.4}      
+        this.splitScreenTool.viewportFitBound(this.orthoViewport,  info.bound,  pos, duration, margin ) 
     },
 
 
@@ -553,8 +685,8 @@ var Clip = {
     
     
     getRulerBound(){//坐标尺边界 米
-        let camera = viewer.mainViewport.camera
-        if(!camera.isOrthographicCamera)return
+        let camera = this.orthoCamera
+         
         let w = camera.right / camera.zoom //half  视图宽度的一半
         let h = camera.top / camera.zoom
         
@@ -597,7 +729,7 @@ var Clip = {
     
     
     
-    screenshot: async (ifShowRuler, rulerToolFactory /*unitText = '像素 : 米' */ )=>{ //测绘图下载。顶视图|侧视图
+    screenshot: async (ifShowRuler, rulerToolFactory, scaleUnit/*unitText = '像素 : 米' */ )=>{ //测绘图下载。顶视图|侧视图
         if(!rulerToolFactory){
             ifShowRuler = false
         }
@@ -614,15 +746,18 @@ var Clip = {
             )  
             return str.trim()
         }
-     
-    
-        return new Promise((resolve,reject)=>{ 
+        let oldFullViewport = Clip.splitScreenTool.focusInfo
+        
+        let promise = new Promise((resolve,reject)=>{ 
             if(Clip.screenshoting )return reject()
+                
+            Clip.splitScreenTool.focusOnViewport(Clip.orthoViewport.name)
+            
             let rulerBound = {}//因为margin 扩大bound  
             let meterPerPixel
          
             let visiPointclouds = viewer.scene.pointclouds.filter(e=> Potree.Utils.getObjVisiByReason(e, 'datasetSelection'))
-            let camera = viewer.mainViewport.camera
+            
             //if(Clip.activeViewName == 'mainView')return reject()
             
             const maxWidth = 8192, minWidth = Potree.browser.urlHasValue('clipMinWidth',true) || 512  //图片尺寸最大最小值。
@@ -631,30 +766,26 @@ var Clip = {
             viewer.inputHandler.deselectAll() 
             Clip.box.visible = false
             
-            Clip.screenshoting = true
-            
-            
-            let material = new ExtendPointCloudMaterial 
-            material.pointSizeType = Potree.browser.urlHasValue('clipFixed') ? Potree.PointSizeType.FIXED : Potree.PointSizeType.ATTENUATED  //Potree.PointSizeType
-            material.size = Potree.browser.urlHasValue('clipPointSize',true) || visiPointclouds[0].material.size  //1  
-            material.uniforms.minSize.value = 0.1 
-            material.uniforms.orthoMaxSize.value = 6
-            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
-            
-            
+            Clip.screenshoting = true 
             let materials = visiPointclouds.map(e=>{
                 let mat = e.material
+                let material = new ExtendPointCloudMaterial 
+                material.pointSizeType = Potree.browser.urlHasValue('clipFixed') ? Potree.PointSizeType.FIXED : Potree.PointSizeType.ATTENUATED  //Potree.PointSizeType
+                material.size = Potree.browser.urlHasValue('clipPointSize',true) || mat.size  //1  点云大小由用户设置
+                material.uniforms.minSize.value = 0.1 
+                material.uniforms.orthoMaxSize.value = 20 //因为滑动条可以调很大所以我写大点,如果要保持和左侧预览一致那还是和原本一致
+                material.activeAttributeName = mat.activeAttributeName//'rgba'
+                material.color = mat.color
+                //material.classification = pointcloud.material.classification;
+                
+                material.clipBoxBig_in = visiPointclouds[0].material.clipBoxBig_in
+                material.shaderNeedsUpdate = true
+                material.bigClipInBox = visiPointclouds[0].material.bigClipInBox
                 e.material = material
                 return mat
             })
             
-             
+            let camera = Clip.orthoCamera 
             //根据boundingBox尺寸来定图片尺寸
             let boundingBox = Clip.box.boundingBox.clone().applyMatrix4(Clip.box.matrixWorld)
             let points = []
@@ -694,7 +825,7 @@ var Clip = {
             w = Math.round(w)     
             h = Math.round(h)
              
-            let focusObjectInfo = [{boundingBox, points}, 'boundingBox', 0, { dontChangeCamDir:true,  dontMoveMap:true, boundScale:1, minBound : 0.1 }]
+            let focusObjectInfo = [{boundingBox, points}, 'boundingBox', 0, {viewport: Clip.orthoViewport, dontChangeCamDir:true,  dontMoveMap:true, boundScale:1, minBound : 0.1 }]
             
             
             //分块渲染截图,最后拼合图片。需要使每一块的点数不超过pointBudget, 且尺寸不超过4096(会崩溃),过小的话一般不会超过pointBudget,除非是深度较大后排点云多。
@@ -723,7 +854,8 @@ var Clip = {
                 //}
             } 
             
-            let {getImagePromise, finishPromise} = viewer.startScreenshot({ type: 'default', bgOpacity:0,   focusObjectInfo, maxTimeForPointLoad : 3e5, pointDensity:'screenshot2', splitRenderInfo, beforeScreenshot }, w, h, 1 )
+            let {getImagePromise, finishPromise} = viewer.startScreenshot({ type: 'default', bgOpacity:0,   focusObjectInfo, maxTimeForPointLoad : 3e5, pointDensity:'screenshot2', 
+                                                 splitRenderInfo, beforeScreenshot,  mainViewport: Clip.orthoViewport  }, w, h, 1 )
             finishPromise.done(({dataUrl}) => {
                  
                 viewer.inputHandler.toggleSelection(Clip.box);
@@ -774,7 +906,7 @@ var Clip = {
                     //console.log('topRatioToImg',topRatioToImg,'leftRatioToImg',leftRatioToImg,'fontsize', labelInfo.fontsize)
                     let rulerImg, drawRuler, labelGot, rulerGot,  canvas  , unitText, resultSrc, unitLen
                     
-                    if(rulerToolFactory){
+                    if(ifShowRuler || rulerToolFactory && !scaleUnit){
                         
                         rulerBound.left -= marginSelf.left * meterPerPixel  //标尺范围内的宽度(米)
                         rulerBound.right += marginSelf.right * meterPerPixel 
@@ -812,11 +944,11 @@ var Clip = {
                             }  
                         }
                         
-                        console.log('xUnit', result.xUnit, 'yUnit',  result.yUnit  )
+                        //console.log('xUnit', result.xUnit, 'yUnit',  result.yUnit  )
                         
-                        unitLen = result.xUnit //为了得到这个,不管需不需要绘制标尺都要rulerToolFactory
+                        unitLen = scaleUnit || result.xUnit //之前为了得到这个,不绘制标尺也要rulerToolFactory
                     }else{//本地没有
-                        unitLen = boundSize.x / 10  //假定一个
+                        unitLen = scaleUnit || boundSize.x / 10  //假定一个
                        
                     }
                     unitText = '1:' + getRulerUnit(unitLen)
@@ -854,11 +986,14 @@ var Clip = {
                     labelGot = true
                     
                    
-                    //Common.downloadFile(finalDataUrl, 'screenshot11.png') 
+                    
                      
-                    visiPointclouds.forEach((e,i)=>e.material = materials[i])
+                    visiPointclouds.forEach((e,i)=>{
+                        e.material.dispose()
+                        e.material = materials[i]
+                    })
                     Clip.screenshoting = false
-                    viewer.viewports = [viewer.mainViewport]
+                    //viewer.viewports = [viewer.mainViewport]
                      
                     if(ifShowRuler){
                         drawRuler()
@@ -866,18 +1001,32 @@ var Clip = {
                         resultSrc = canvas.toDataURL('image/png' )
                         resolve( resultSrc ) 
                     } 
+                   
+                    if(oldFullViewport?.name != Clip.orthoViewport.name){
+                        Clip.splitScreenTool.unfocusViewport()
+                        oldFullViewport && Clip.splitScreenTool.focusOnViewport(oldFullViewport.name)
+                    }
+                    
+                    
                 } 
             })
             
        
              
         }) 
+        Potree.settings.isOfficial || promise.then(src=>{
+             Common.downloadFile(src, 'clipScreenshot.png') 
+        })
+
+        
+        return promise 
+        
         /* 
             隐患:无法获知最大可加载的点的数量,也未知需要加载的点的数量。需要掌控好分块数量,避免因pointBudget限制造成周围的点稀疏。
             加载点云时间过久
             
          */
-        return finishPromise
+        //return finishPromise
         
          
     } 

+ 5 - 5
src/custom/modules/clipping/Clipping.js

@@ -218,7 +218,7 @@ export class Clipping extends THREE.EventDispatcher{ //实时剪裁
          
         this.setPointLevelAuto()
         var initialPointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == Potree.settings.originDatasetId)
-        //隐藏 初始数据集以外的数据集 
+        /* //隐藏 初始数据集以外的数据集 
         viewer.scene.pointclouds.forEach(e=>{
             if(e.dataset_id!=Potree.settings.originDatasetId){
                 Potree.Utils.updateVisible(e,'enterClipping',false) 
@@ -230,7 +230,7 @@ export class Clipping extends THREE.EventDispatcher{ //实时剪裁
                 //Potree.settings.floorplanEnables[e.dataset_id] = true 
             }
         })
-        
+         */
         viewer.flyToDataset({ pointcloud : initialPointcloud, duration:0})
          
     }
@@ -263,7 +263,7 @@ export class Clipping extends THREE.EventDispatcher{ //实时剪裁
         viewer.transformObject(null)
         viewer.transformationTool.history.clear()
         
-        //恢复 初始数据集以外的数据集 
+        /* //恢复 初始数据集以外的数据集 
         viewer.scene.pointclouds.forEach(e=>{
             if(e.dataset_id!=Potree.settings.originDatasetId){
                 Potree.Utils.updateVisible(e,'enterClipping',true) 
@@ -272,7 +272,7 @@ export class Clipping extends THREE.EventDispatcher{ //实时剪裁
                 Potree.Utils.updateVisible(e,'enterClipping',false, 0, 'cancel')
             }
         }) 
-        
+         */
     }
     
     setTranMode(mode){//rotate or translate 
@@ -316,7 +316,7 @@ export class Clipping extends THREE.EventDispatcher{ //实时剪裁
         let Clip = viewer.modules.Clip //裁剪下载模块 
         
         let data = {   
-            transformation_matrix: viewer.scene.pointclouds.filter(p=>p.dataset_id == Potree.settings.originDatasetId).map((cloud)=>{
+            transformation_matrix: viewer.scene.pointclouds.map((cloud)=>{
                 let data = {
                     id: cloud.dataset_id, 
                     matrix : new THREE.Matrix4().elements,  //参照downloadNoCrop,给默认值,表示没有最外层裁剪

+ 115 - 69
src/custom/modules/datasetAlignment/Alignment.js

@@ -12,8 +12,8 @@ var Alignment = {
     handleState:null,  //操作状态 'translate'|'rotate'
     bus: new THREE.EventDispatcher(), 
     selectedClouds:[],
-    
-
+    listeners:[],
+    rotCenter:{center:null, pointcloud:null},//旋转中心
     /* prepareRecord : true, 
     
       writeToHistory(pointclouds){ 
@@ -72,7 +72,7 @@ var Alignment = {
     
     
     init:function(){ 
-        let transfromInfo, sideRotHovered
+        let transfromInfo 
         
         
         
@@ -96,38 +96,38 @@ var Alignment = {
                 rotFullCircle:true
             }) 
             this.transformControls.name = 'Alignment-transformControls'
-            this.transformControls.setSize(0.5)
+            this.transformControls.setSize(0.6)
             viewer.scene.scene.add(this.transformControls) 
             this.transformControls.setMode('rotate')
             this.transformControls.setRotateMethod(2)
             this.transformControls._gizmo.hideAxis =  {rotate:['x','z','y'] } 
-            this.fakeMarkerForTran = new THREE.Mesh(new THREE.BoxBufferGeometry(0.3,0.3,0.3) , new THREE.MeshBasicMaterial({
-                color:"#F00",  opacity:0.9,  transparent:true, visible:false
+            this.fakeMarkerForRot = new THREE.Mesh(new THREE.BoxBufferGeometry(5,5,5) , new THREE.MeshBasicMaterial({
+                color:"#F00",  opacity:0.5,  transparent:true , visible:false
             }));//一个看不见的mesh,只是为了让transformControls移动点云
-            this.fakeMarkerForTran.name = 'fakeMarkerForTran'
-            viewer.scene.scene.add(this.fakeMarkerForTran)
-            
+            this.fakeMarkerForRot.name = 'fakeMarkerForRot'
+            viewer.scene.scene.add(this.fakeMarkerForRot)
+            Potree.Utils.setObjectLayers(this.fakeMarkerForRot, 'bothMapAndScene' ) 
             let afterMoveCtl = (type)=>{
                 if(Alignment.afterMoveCtl){
                     Alignment.afterMoveCtl(type)
                 }else{
                     if(type == 'position'){
-                        let moveVec = new THREE.Vector3().subVectors(this.fakeMarkerForTran.position, this.fakeMarkerForTran.oldState.position)
+                        let moveVec = new THREE.Vector3().subVectors(this.fakeMarkerForRot.position, this.fakeMarkerForRot.oldState.position)
                         Alignment.translate(this.selectedClouds[0], moveVec)
                     }else{
-                        let center = this.selectedClouds[0].translateUser;
-                        
-                        let diffQua = new THREE.Quaternion().multiplyQuaternions(this.fakeMarkerForTran.quaternion, this.fakeMarkerForTran.oldState.quaternion.clone().invert())
+                        //let center = this.getRotCenter(this.selectedClouds[0]) //this.rotCenter.pointcloud == this.selectedClouds[0] ? this.rotCenter.center : this.selectedClouds[0].bound.getCenter(new THREE.Vector3)//.translateUser;
+                         
+                        let diffQua = new THREE.Quaternion().multiplyQuaternions(this.fakeMarkerForRot.quaternion, this.fakeMarkerForRot.oldState.quaternion.clone().invert())
                         CursorDeal.add('rotatePointcloud') 
-                        //Alignment.rotateAround(center, this.selectedClouds, diffQua)
-                        Alignment.rotate(this.selectedClouds[0], null, diffQua)
+                        Alignment.rotateAround(null, this.selectedClouds[0], diffQua)
+                        //Alignment.rotate(this.selectedClouds[0], null, diffQua)
                         
                     }
                 }
                 
-                this.fakeMarkerForTran.oldState = {
-                    position: this.fakeMarkerForTran.position.clone(),
-                    quaternion: this.fakeMarkerForTran.quaternion.clone(),
+                this.fakeMarkerForRot.oldState = {
+                    position: this.fakeMarkerForRot.position.clone(),
+                    quaternion: this.fakeMarkerForRot.quaternion.clone(),
                 }
              
                 Alignment.history.beforeChange(this.selectedClouds) 
@@ -135,19 +135,17 @@ var Alignment = {
             }
         
          
-            this.fakeMarkerForTran.addEventListener('position_changed', afterMoveCtl.bind(this,'position')) 
-            this.fakeMarkerForTran.addEventListener("rotation_changed", afterMoveCtl.bind(this,'rotation') )
+            this.fakeMarkerForRot.addEventListener('position_changed', afterMoveCtl.bind(this,'position')) 
+            this.fakeMarkerForRot.addEventListener("rotation_changed", afterMoveCtl.bind(this,'rotation') )
           
             this.transformControls.addEventListener('mouseUp',()=>{
                  Alignment.history.afterChange(this.selectedClouds)
             }) 
             this.transformControls.addEventListener('axisHoveredChange',(e)=>{
                 if(e.axis && e.mode == 'rotate'){
-                    CursorDeal.add('rotatePointcloud'),
-                    sideRotHovered = true
+                    CursorDeal.add('rotatePointcloud')  
                 }else{
                     CursorDeal.remove('rotatePointcloud') 
-                    sideRotHovered = false
                 } 
             })
             this.history.addEventListener('undo',()=>{
@@ -197,8 +195,8 @@ var Alignment = {
                             transfromInfo.vecStart = vec
                         } */ 
                    
-                     
-                    let center = Potree.settings.editType == 'pano' ? e.pointclouds[0].translateUser : e.pointclouds[0].bound.getCenter(new THREE.Vector3) //点云编辑:旋转中心是第一个点云的位置 ; 数据集校准:饶当前数据集bound中心,因有的上传的数据集偏离原点很远
+                 
+                    let center = this.getRotCenter(e.pointclouds[0] ) //this.rotCenter.pointcloud == e.pointclouds[0] ? this.rotCenter.center : e.pointclouds[0].bound.getCenter(new THREE.Vector3) //Potree.settings.editType == 'pano' ? e.pointclouds[0].translateUser : this.rotCenter 
                     if(e.intersect.equals(center))return
                     if(!transfromInfo.vecStart){  
                         //transfromInfo.orientationUser = e.pointclouds[0].orientationUser  
@@ -225,8 +223,7 @@ var Alignment = {
                             //let centerNow1 = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:transfromInfo.pointcloud, position: centerNoTranfrom }) //中心点的现在位置
                                  */ 
                                  
-                            Alignment.rotateAround(center, cloud, diffQua)    
-                                
+                            Alignment.rotateAround(center, cloud, diffQua)  
                                  
                         }) 
                         
@@ -247,7 +244,11 @@ var Alignment = {
                         Alignment.rotate(transfromInfo.pointclouds[0], null, diffAngle)
                     }
                 }  */   
+                
+                this.fakeMarkerForRot.material.visible && this.updateCtlDisplay()
+                
             } 
+            
         })
         
         let drop = ()=>{   
@@ -259,9 +260,8 @@ var Alignment = {
          
 		 
         
-        // cursor:
         
-        let updateCursor = (e)=>{ 
+        this.listeners.updateCursor = (e)=>{ 
             if(e.drag || !this.editing)return  //仅在鼠标不按下时更新:
             
             let handleState = Alignment.handleState
@@ -275,34 +275,50 @@ var Alignment = {
                     }
                 }else if(handleState == 'rotate'){ 
                     if( e.intersect && e.intersect.location ){  
-                        CursorDeal.add('rotatePointcloud')  
+                        CursorDeal.add('rotatePointcloud2')  
                     }else{ 
-                        CursorDeal.remove('rotatePointcloud')  
+                        CursorDeal.remove('rotatePointcloud2')  
                     }
                 }  
             }else{  
                 //清空:
-                CursorDeal.remove('movePointcloud')  
-                if(!sideRotHovered) CursorDeal.remove('rotatePointcloud') 
+                CursorDeal.remove('movePointcloud')   
             }                
         }
         
-         
-        viewer.addEventListener('global_mousemove',updateCursor)  
-        viewer.addEventListener('global_drop',updateCursor)//拖拽结束  
-       
-            
-        
-        
-        viewer.addEventListener('updateModelBound', (e)=>{
+        this.listeners.updateModelBound = ()=>{
             if(this.editing){
                 this.SplitScreen.updateCameraOutOfModel() 
-            } 
-        })
-        
+            }
+        }
         
+        this.listeners.updateRotCenter = (e)=>{//2026.3(2.14.0) 鼠标点哪中心点在哪
+            //从bound.center平移到鼠标所在位置 
+            if(e.hoverViewport.name == 'MainView' || this.handleState != 'rotate')return
+            let center = this.selectedClouds[0]?.bound.getCenter(new THREE.Vector3)
+            
+            if(!center)return
+            
+            const projected = center.clone().project(e.hoverViewport.camera); 
+                projected.x = e.pointer.x
+                projected.y = e.pointer.y
+
+            const unprojected = projected.clone().unproject(e.hoverViewport.camera); //平移
+            
+            this.rotCenter = {
+                center: unprojected,
+                pointcloud: this.selectedClouds[0]
+            }
+            this.updateCtlDisplay()
+            
+            return {stopContinue: true}
+        }
     },
     
+    getRotCenter(pointcloud){
+        return this.rotCenter.pointcloud == pointcloud ? this.rotCenter.center : pointcloud.bound.getCenter(new THREE.Vector3)//.translateUser;
+                         
+    },
     
     setMatrix :  function(pointcloud){
         var vec1 = pointcloud.position     //position为数据集内部的偏移,在navvis中对应的是dataset.pointCloudSceneNode的children[0].position
@@ -354,6 +370,10 @@ var Alignment = {
     
     
     rotateAround(center, pointcloud, angle, axis=new THREE.Vector3(0,0,1)){//绕center点水平转动
+        if(!center){
+            center = this.getRotCenter(pointcloud) 
+        }
+        
         
         let vec1 = new THREE.Vector3().subVectors(pointcloud.translateUser, center);
         let rotMatrix = typeof(angle) == 'number' ? 
@@ -407,24 +427,18 @@ var Alignment = {
         this.originData = this.getTemp() 
         
         this.SplitScreen.split({alignment:true})
-         
-        /* viewer.images360.panos.forEach(pano=>{
-            Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', false)
-        }) */  
+       
         
         viewer.viewports.find(e=>e.name == 'mapViewport').alignment = {rotate:true,translate:true};
         viewer.viewports.find(e=>e.name == 'right').alignment = {translate:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动
         viewer.viewports.find(e=>e.name == 'back').alignment = {translate:true, translateVec:new THREE.Vector3(0,0,1)}; //只能上下移动
         viewer.viewports.forEach(e=>{
-            if(e.name == 'right' || e.name == 'back' ){
-                e.layersAdd('sideVisi') 
-                /* if(e.name == 'right') e.layersAdd('layer1') 
-                else if(e.name == 'back') e.layersAdd('layer2')  */
+            if(e.name != 'MainView' ){
+                e.layersAdd('sideVisi')  
             }  
+                       
         })
-        Potree.Utils.setObjectLayers(this.transformControls, 'sideVisi' )
-        /* Potree.Utils.setObjectLayers(this.transformControls, 'layer1' )
-        Potree.Utils.setObjectLayers(this.transformControls, 'layer2' ) */
+        Potree.Utils.setObjectLayers(this.transformControls, 'sideVisi' ) 
          
         { 
             /* viewer.viewports.forEach(e=>{
@@ -454,7 +468,17 @@ var Alignment = {
         
         viewer.updateFpVisiDatasets()
         
+        //隐藏点位和游标 2026.3(2.14.0) 
+        Potree.Utils.updateVisible(viewer.mapViewer.cursor, 'atAlignment', false)//隐藏地图游标
+        viewer.images360.panos.forEach(pano=>{
+            Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', false)
+        }) 
+        
         
+        viewer.addEventListener('global_mousemove', this.listeners.updateCursor)  
+        viewer.addEventListener('global_drop',  this.listeners.updateCursor)//拖拽结束
+        viewer.addEventListener('updateModelBound', this.listeners.updateModelBound)
+        viewer.addEventListener('global_click', this.listeners.updateRotCenter,{importance:5})  
         
     },
     leave:function(){
@@ -471,15 +495,24 @@ var Alignment = {
         
         
         this.SplitScreen.recover()
-        /* viewer.images360.panos.forEach(pano=>{
-            Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', true)
-        })  */
+         
         this.editing = false
         this.history.clear() 
         viewer.updateFpVisiDatasets()
         CursorDeal.remove('movePointcloud')  
         CursorDeal.remove('rotatePointcloud') 
-
+        CursorDeal.remove('rotatePointcloud2') 
+        Potree.Utils.updateVisible(viewer.mapViewer.cursor, 'atAlignment', true) 
+        viewer.images360.panos.forEach(pano=>{
+            Potree.Utils.updateVisible(pano.mapMarker, 'split4Screens', true)
+        }) 
+        
+        viewer.removeEventListener('global_mousemove', this.listeners.updateCursor)  
+        viewer.removeEventListener('global_drop',  this.listeners.updateCursor)
+        viewer.removeEventListener('updateModelBound', this.listeners.updateModelBound)
+        viewer.removeEventListener('global_click', this.listeners.updateRotCenter)  
+        
+        
     } 
     
     ,
@@ -491,26 +524,35 @@ var Alignment = {
         CursorDeal.remove('rotatePointcloud') 
         this.bus.dispatchEvent({type:'switchHandle' , state })
         
-        this.updateCtlDisplay() 
+        this.updateCtlDisplay({resetCenter:true}) 
 
     },
     
-    updateCtlDisplay(){//仅数据集校准执行
+    updateCtlDisplay(o={}){//仅数据集校准执行
         if(!this.editing)return  
+        if(o.resetCenter){//清空旋转中心
+            this.rotCenter = {
+                center: null,
+                pointcloud: null
+            }
+        }
         let selected = this.handleState == 'rotate' && this.selectedClouds.length > 0
-        if(selected){
-            this.transformControls.attach(this.fakeMarkerForTran) 
-            let position = this.selectedClouds[0].translateUser
+        
+        if(selected){ 
+            this.transformControls.attach(this.fakeMarkerForRot) 
+            
+            let position = this.getRotCenter(this.selectedClouds[0])//this.selectedClouds[0].bound.getCenter(new THREE.Vector3)  //.translateUser
             let quaternion = this.getDatasetQuaternion(this.selectedClouds[0])
-            this.fakeMarkerForTran.position.copy(position)
-            this.fakeMarkerForTran.quaternion.copy(quaternion)
-            this.fakeMarkerForTran.oldState = {
+            this.fakeMarkerForRot.position.copy(position)
+            this.fakeMarkerForRot.quaternion.copy(quaternion)
+            this.fakeMarkerForRot.oldState = {
                 position: position.clone(),
                 quaternion: quaternion.clone() 
             }
         }else{
             this.transformControls.detach()
         }
+        
     },
     
     
@@ -545,8 +587,12 @@ var Alignment = {
         }
         
         return {data, callback}
-    }
-        
+    },
+    selectDatasets(datasets) {
+        //selectedDatasets = datasets
+        this.selectedClouds = datasets.map(e=>viewer.scene.pointclouds.find(p => p.dataset_id == e.id))
+        this.updateCtlDisplay({resetCenter:true})
+    }, 
     
 }
 

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

@@ -936,13 +936,13 @@ export class Images360 extends THREE.EventDispatcher{
         
         let done = (makeIt, disturb)=>{
             //console.log('flyToPano done ', toPano.pano.id, makeIt, disturb ) 
-            if(makeIt || disturb) { // disturb已经开始飞行但中途取消
-                toPano.callback && toPano.callback(makeIt) 
+            if(makeIt || disturb) { // disturb已经开始飞行但中途取消 
                 //this.flying = false
-                this.cancelFlyToPano(toPano)
+                
+                this.cancelFlyToPano(toPano) 
                 this.updateClosestPano(this.closestPano,false) //飞行结束后取消点击漫游点时得到的closestPano
-            }else{ 
-            }
+                toPano.callback && toPano.callback(makeIt) 
+            } 
             
             this.dispatchEvent({type:'flyToPanoDone', makeIt, disturb})
             this.fastTranMaskPass.stop() 
@@ -2310,7 +2310,7 @@ export class Images360 extends THREE.EventDispatcher{
                 
                 let levelThreshold1 = Potree.settings.navTileClass == '1k' ? 1.3 : 1.7 , levelThreshold2 = 2
                  
-                var t = this.zoomLevel > levelThreshold1,
+                var t = Potree.settings.highestQualityTile || this.zoomLevel > levelThreshold1,
                     n = !(this.flying && this.nextPano && this.nextPano !== this.currentPano),
                     r = t && n;
                 this.tileDownloader.tilePrioritizer.setZoomingActive(r);

+ 25 - 37
src/custom/modules/panos/Panorama.js

@@ -793,7 +793,7 @@ Panorama.prototype.loadTiledPano = function() {
         null !== o && void 0 !== o || (o = !0),
         null !== a && void 0 !== a || (a = !0);
         var l = this.getWaitDeferred(size)
-          , c = l.deferred
+          , deferred = l.deferred //c
           , h = null
           , u = null; 
         fov && ("number" == typeof fov ? h = fov : (h = fov.hFov, u = fov.vFov))  
@@ -824,9 +824,9 @@ Panorama.prototype.loadTiledPano = function() {
                     if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的全部加载成功
                         //let total = TileUtils.getTileCountForSize(size)
                         //this.onPanoRendered(this.id, size, total, !0);
-                        c.resolve(size/* , total */);
+                        deferred.resolve(size/* , total */);
                         this.resetWaitDeferred(size)
-                        //console.log('该部分早已经加载好了'+size, this.id)
+                        //console.log('该部分早已经加载好了0'+size, this.id)
                         latestPartialRequest[name] = null
                     }
                      
@@ -839,16 +839,19 @@ Panorama.prototype.loadTiledPano = function() {
                         
                         //console.warn('点位(可能部分)下载完成 ', 'id:'+this.id,  'size:'+ev.size ) 
                         
-                        var i = this.getWaitDeferred(ev.size).deferred;//"pending"为还未完成
-                        i && "pending" === i.state() && this.highestPartialTileRenderOpCompleted >= ev.size && (i.resolve(ev.size, ev.count),
-                        this.resetWaitDeferred(ev.size))//恢复active为false
-                         
+                        var deferred_ = this.getWaitDeferred(ev.size).deferred;//"pending"为还未完成
+                        if(deferred_ && "pending" === deferred_.state() && this.highestPartialTileRenderOpCompleted >= ev.size ){
+                            deferred_.resolve(ev.size, ev.count), 
+                            this.resetWaitDeferred(ev.size) //恢复active为false
+                        }
                     }.bind(this)) 
                     
-                    this.addEventListener(PanoramaEvents.LoadFailed, function(ev) {
-                        var t = this.getWaitDeferred(e).deferred;
-                        t && "pending" === t.state() && this.highestPartialTileRenderOpCompleted >= ev.t && (t.reject(ev.t),
-                        this.resetWaitDeferred(ev.t))//恢复active为false
+                    this.addEventListener(PanoramaEvents.LoadFailed, function(ev) {//不会触发
+                        var deferred_ = this.getWaitDeferred(e).deferred;
+                        if(deferred_ && "pending" === deferred_.state() && this.highestPartialTileRenderOpCompleted >= ev.size ){
+                            deferred_.reject(ev.size),
+                            this.resetWaitDeferred(ev.size) //恢复active为false
+                        }
                     }.bind(this)) 
                     
                     this.addEventListener(PanoramaEvents.TileLoaded, function(ev/* t, i, n */) {//每张加载完时
@@ -861,43 +864,28 @@ Panorama.prototype.loadTiledPano = function() {
                         downloaded[name] = downloaded[name] || [] //不是所有的加载都是从loadTiledPano获取的所以会有未定义的情况
                         
                         let {faceTileIndex,face} = TileUtils.getTileLocation(size, tileIndex, {}) 
-                        downloaded[name].push({faceTileIndex,face})    
-                        var r = this.getWaitDeferred(size).deferred;
-                        if (r && "pending" === r.state()) { 
-                            r.notify(size, tileIndex, total);
+                        downloaded[name].push({faceTileIndex,face})  
+                        
+                        var deferred_ = this.getWaitDeferred(size).deferred;
+                        
+                        if (deferred_ && "pending" === deferred_.state()) { 
+                            deferred_.notify(size, tileIndex, total);
                             if(latestPartialRequest[name]){
                                 let item = latestPartialRequest[name].find(e=>e.faceTileIndex == faceTileIndex && e.face == face)    
                                 item && (item.loaded = true ) 
                                 
                                 if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的局部tiles全部加载成功
                                     this.onPanoRendered(this.id, size, total, !0); //onPanoRendered还会触发 PanoramaEvents.LoadComplete   
-                                    r.resolve(size, total);
+                                    deferred_.resolve(size, total);
                                     this.resetWaitDeferred(size)
                                     //console.log('该部分加载好了'+size, this.id)
                                     latestPartialRequest[name] = null
-                                }
-                                
+                                } 
                             } 
                         } 
 
                         viewer.dispatchEvent('content_changed') 
-    
-                        
-                        /* var r = this.getWaitDeferred(ev.size).deferred;
-                        if (r && "pending" === r.state()) {
-                            r.notify(ev.size, ev.index, ev.count);
-                             
-                            var o = downloads[this.id + ":" + ev.size];
-                            if(o){//如果有规定下载哪些tile,只需要下载这些tile则LoadComplete
-                                o.tileCount++ 
-                                
-                                if(o.tileCount === o.targetTileCount){//达到下载目标数
-                                    this.onPanoRendered(this.id, ev.size, ev.count, !0);
-                                    r.resolve(ev.size, ev.count);
-                                    this.resetWaitDeferred(ev.size)
-                                }
-                            }
-                        } */
+     
                     }.bind(this))
                 }
             }
@@ -908,9 +896,9 @@ Panorama.prototype.loadTiledPano = function() {
             
         }else{
             //console.log('早已经全加载好了' +size, this.id)
-            c.resolve(size)
+            deferred.resolve(size)
         }
-        return c.promise()
+        return deferred.promise()
     }
 }()
 

+ 5 - 3
src/custom/modules/panos/tile/PanoRenderer.js

@@ -317,7 +317,7 @@ class PanoRenderer extends THREE.EventDispatcher{
             && (!(e.panoSize > this.qualityManager.getMaxNavPanoSize()) || this.zoomingActive &&  viewer.images360.getPano(e.panoId).tileRes != '2k')) {
  
             
-            var r = this.getUploadQueueForPano(e.panoId);
+            var uploadQueues = this.getUploadQueueForPano(e.panoId);
             //console.log(window.sceneName, 'queueTileUpload: ', e.panoId, e.tileIndex,   i)
             if(i){
                 //console.log('512下载好了直接uploadTile')
@@ -327,8 +327,10 @@ class PanoRenderer extends THREE.EventDispatcher{
                     this.forceQueue.push(e)
                 }else{
                     if(t && this.direction){
-                        TilePrioritizer.insertSortedPanoTile(r, e, n.pano, this.direction) 
-                    }else r.push(e) 
+                        TilePrioritizer.insertSortedPanoTile(uploadQueues, e, n.pano, this.direction) 
+                    }else{ 
+                         uploadQueues.push(e) 
+                    }
                 }
                 e.uploadQueued = !0 
                 this.uploadInterval || this.uploadIntervalCancelled || this.refreshUploadInterval(0)

+ 7 - 10
src/custom/objects/Magnifier.js

@@ -87,25 +87,22 @@ export default class Magnifier extends THREE.Object3D {//放大镜or望远镜
             } 
         }
         
-        
-        
-        
-        this.renderTarget = new THREE.WebGLRenderTarget(this.width,this.height, { 
+        const RenderTarget = Potree.settings.isWebgl2 ? 'WebGLMultisampleRenderTarget' : 'WebGLRenderTarget'  
+        this.renderTarget = new THREE[RenderTarget](this.width,this.height, { 
             minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter,
-            format: THREE.RGBAFormat ,
+            format: THREE.RGBAFormat 
             /* type: THREE.FloatType,
             minFilter: THREE.NearestFilter,
 			magFilter: THREE.NearestFilter, 
 			  */ 
-        } )
-        
-		this.rtEDL = new THREE.WebGLRenderTarget(this.width, this.height, {  //好像没用到? 因为这里不绘制测量线
+        } ) 
+		/* this.rtEDL = new THREE.WebGLRenderTarget(this.width, this.height, {  //好像没用到? 因为这里不绘制测量线
 			minFilter: THREE.NearestFilter,
 			magFilter: THREE.NearestFilter,
 			format: THREE.RGBAFormat,
 			type: THREE.FloatType,
 			depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
-		});
+		}); */
         
         
         
@@ -376,7 +373,7 @@ export default class Magnifier extends THREE.Object3D {//放大镜or望远镜
             viewports : [this.viewport],
             camera : this.camera,
             magnifier : true,
-            rtEDL: this.rtEDL 
+            dontRenderRtEDL:true, //rtEDL: this.rtEDL  //不记得为啥传这个了
             /* width :this.renderTarget.width,
             height: this.renderTarget.height, */
         })

+ 1 - 1
src/custom/objects/Sprite.js

@@ -300,7 +300,7 @@ export default class Sprite extends THREE.Mesh{
         if(!e)e = {viewport:viewer.mainViewport}//随便写一个viewport
         
         let visi = this.visiMap.get(e.viewport) //还原可见性
-        Potree.Utils.updateVisible(this, 'unableCompute', visi == false ? false : true );  
+        this.root.lineDir && Potree.Utils.updateVisible(this, 'unableCompute', visi == false ? false : true );  
         /* if(e.viewport.name == 'mapViewport' && visi && this.visiMap.get(viewer.mainViewport) == false){
             console.log(1)
         } */

+ 1 - 1
src/custom/objects/tool/Measure.js

@@ -1236,7 +1236,7 @@ export class Measure extends ctrlPolygon{
                 multiColors[this.color] = colorObject 
                  
                 {//清理不用的colorObject
-                    let measures = viewer.scene.measurements.slice()
+                    let measures = viewer.scene.measurements.filter(e=>e.Type == 'measure' )
                     measures.includes(this) || measures.push(this)
                     for(let co in multiColors){
                         multiColors[co].useCount = 0

+ 240 - 92
src/custom/objects/tool/Path.js

@@ -17,7 +17,7 @@ import CursorDeal from "../../utils/CursorDeal.js";
 let texLoader = new THREE.TextureLoader()   
  
 const labelSizeInfo = {width2d:180} //稍微小点防止字体模糊
- 
+const quaBase = new THREE.Quaternion().setFromEuler(new THREE.Euler(-Math.PI/2,0, Math.PI/2))
 const titleLineHeight = 2 
  
  
@@ -27,18 +27,18 @@ const arrowCountMax = 1000; //箭头总数不能超过这个值。 The count val
 let lastArrowCamPos,  lastArrowCount = 0
  
 const depthProps = {
-    useDepth : true , 
+    useDepth : true ,  
     //startClipDis  : 0.5,
-    clipDistance : 2,//消失距离      
+    clipDistance : 0.5,//消失距离      
     //startOcclusDis: 0.5,
-    occlusionDistance: 0.7,//变为backColor距离 
+    occlusionDistance: 0.3,//变为backColor距离 
     maxOcclusionFactor:0.9,
     maxClipFactor:1
 }
 const planeGeo = new THREE.PlaneBufferGeometry(1,1)
 const voidGeometry = new THREE.BufferGeometry() 
 
-let markerMats  
+let markerMats, dragPointMat  
 const getMarkerMat = function (name) { 
      if(!markerMats){
          markerMats = {
@@ -68,10 +68,11 @@ const getMarkerMat = function (name) {
 
 
 
-const getMeshQuaInPath = (lineDir)=>{
-    const quaBase = new THREE.Quaternion().setFromEuler(new THREE.Euler(-Math.PI/2,0, Math.PI/2))
-    return math.getQuaFromPosAim( new THREE.Vector3, lineDir ).multiply(quaBase) 
-}
+                                     
+                                                                                                 
+                                                                                 
+ 
+
 
 const getEndCaps = (function () { 
     let endCap, map
@@ -148,6 +149,9 @@ export class Path extends ctrlPolygon{
         this.selectStates = {}
         this.setFadeFar(null)
         this.geoPoints = []
+        this.lineHeight = prop.lineHeight == void 0 ? titleLineHeight : prop.lineHeight
+        
+        
         {
             let group = new THREE.Object3D;     group.name = 'titleGroup'
             this.titleLabel = new TextSprite(Object.assign({}, depthProps,{
@@ -165,42 +169,90 @@ export class Path extends ctrlPolygon{
                 textAlign: Potree.settings.isOfficial && 'left'
             }))
             this.titleLabel.sprite.material.depthTest = false
-            let line = LineDraw.createFatLine([new THREE.Vector3(0,0,0), new THREE.Vector3(0,0,titleLineHeight)],  Object.assign({},depthProps,{color: '#ffffff', lineWidth: 1, transparent:true, fadeFar: this.fadeFar}))
+            let line = LineDraw.createFatLine([new THREE.Vector3(0,0,0), new THREE.Vector3(0,0, this.lineHeight)],  Object.assign({},depthProps,{color: '#ffffff', lineWidth: 1, transparent:true, fadeFar: this.fadeFar}))
             line.renderOrder = Potree.config.renderOrders.line
+            line.name = 'line' 
+            this.titleLine = line
             group.add(line)
             group.add(this.titleLabel)
-            this.titleLabel.position.z = titleLineHeight
+            this.titleLabel.position.z = this.lineHeight
             this.add(group);
             this.setTitleVisi(this.titleLabel.parent, false,  'noPoint')            
             this.setTitle(Potree.settings.isOfficial ? '' : 'title' )
-       
             
-            line.addEventListener('mouseover',(e)=>{ 
-                this.editEnable && CursorDeal.add('hoverGrab') 
-            });
-            line.addEventListener('startDragging',(e)=>{  
-                this.editEnable && CursorDeal.add('grabbing') 
-            });
-            line.addEventListener('drop',(e)=>{  
-                this.editEnable && CursorDeal.remove('grabbing')
-            });
-            line.addEventListener('mouseleave',(e)=>{  
-                this.editEnable && CursorDeal.remove('hoverGrab') 
-                
-            });
-            line.addEventListener('drag',(e)=>{    
+            if(!dragPointMat){ 
+                let map = texLoader.load(Potree.resourcePath+'/textures/whiteCircle.png',()=>{})
+                dragPointMat = {
+                    default: new THREE.MeshBasicMaterial({
+                        map, transparent:true, color:'#0fe', opacity:0, depthTest:false, 
+                    }),   
+                    hover: new THREE.MeshBasicMaterial({
+                        map, transparent:true, color:'#0fe', opacity:0.4, depthTest:false, 
+                    })
+                }
+            }
+            this.lineDragPoint = new THREE.Mesh(planeGeo, dragPointMat.default) //修改线高度时出现的小圆点
+            this.lineDragPoint.scale.set(0.15,0.15,0.15); 
+            this.lineDragPoint.name = 'lineDragPoint' 
+            this.lineDragPoint.renderOrder = this.lineDragPoint.pickOrder = 10;
+            this.titleLabel.add(this.lineDragPoint) 
+            
+            let hoverState = {}, grabbingObject  
+            let setDragPointState = (state)=>{
+                this.lineDragPoint.material = state ? dragPointMat.hover : dragPointMat.default 
+                this.titleLabel.sprite.material.opacity = state ? 0.5 : 1 
+            }
+            let autoPointState = ()=>{
+                setDragPointState(this.editEnable && (hoverState.lineDragPoint || grabbingObject == 'lineDragPoint'))  
+            }
+            let autoCursor = ()=>{ 
+                if(this.editEnable && Object.values(hoverState).some(e=>e)){     
+                    CursorDeal.add('hoverGrab')
+                }else{
+                    CursorDeal.remove('hoverGrab')
+                }
+                autoPointState()
+            }
+            [line, this.lineDragPoint].forEach(e=>e.addEventListener('mouseover',(e)=>{ 
+                hoverState[e.target.name] = 1 
+                autoCursor() 
+            }));
+            [line, this.lineDragPoint].forEach(e=>e.addEventListener('mouseleave',(e)=>{ 
+                hoverState[e.target.name] = 0
+                autoCursor()
+            }));
+            
+            [line, this.lineDragPoint].forEach(e=>e.addEventListener('startDragging',(e)=>{ 
+                grabbingObject = e.target.name
+                this.editEnable && (CursorDeal.add('grabbing'), autoPointState())  
+            }));
+            [line, this.lineDragPoint].forEach(e=>e.addEventListener('drop',(e)=>{ 
+                grabbingObject = null
+                this.editEnable && (CursorDeal.remove('grabbing'), autoPointState())  
+            }));
+            
+            [line, this.lineDragPoint].forEach(e=>e.addEventListener('drag',(e)=>{ 
                 if(this.editEnable){//一旦用户拖动了title,title就固定了,不再随着path居中
-                    let position
-                    if(e.intersect?.location){
-                        position = e.intersect?.location
-                    }else{
-                        let {x,y} = Potree.Utils.getPointerPosAtHeight(0,e.pointer)
-                        position = new THREE.Vector3(x,y,0)
+                    if(e.target.name == 'line'){
+                        let position
+                        if(e.intersect?.location){
+                            position = e.intersect?.location
+                        }else{
+                            let {x,y} = Potree.Utils.getPointerPosAtHeight(0,e.pointer)
+                            position = new THREE.Vector3(x,y,0)
+                        }
+                        this.updateTitlePos(position)
+                        this.dispatchEvent({type:'titlePosChanged',  position ,  root:e.intersect?.pointcloud || e.intersect?.object})
+                    }else if(e.target.name == 'lineDragPoint'){  
+                        this.dragLineLen(e)  
                     }
-                    this.updateTitlePos(position)
-                    this.dispatchEvent({type:'titlePosChanged',  position ,  root:e.intersect?.pointcloud || e.intersect?.object})
+               
                 }
-            });
+            }));
+             
+             
+            
+            
         }
         
         {//和measure不同的是它的边是连在一起的一整条
@@ -255,6 +307,7 @@ export class Path extends ctrlPolygon{
                         viewer.measuringTool.history.afterChange(this) 
                         this.updateEdge()      
                         this.hideArrowUntilUpdate()
+                        this.dispatchEvent({type:'addMarkerByUser',index})
                         this.dispatchEvent('changed')
                     }else{ 
                         this.isNew || viewer.measuringTool.isAdding || this.setSelected('click')  //viewer.focusOnObject(this, 'measure') //正在添加测量线时不要focus其他线(容易误触)
@@ -323,24 +376,31 @@ export class Path extends ctrlPolygon{
    
    
    
-    getPosByIntersect(e, type){//intersect落在线上的位置,以及在哪两个点之间
-     
+    getPosByIntersect(e, type, points=this.points){//intersect落在线上的位置,以及在哪两个点之间
+        let modelMatrixInvert = this.edge.matrixWorld.clone().invert()
+        
         if( !Potree.settings.pathSmooth ){
             let prevIndex = Math.floor(e.hoveredElement.faceIndex / 2) //端点1(可能是最后一个)
             let nextIndex = this.getIndex(prevIndex, 1) //端点2(可能是第一个)
             let index = prevIndex + 1 //新点在端点1后
-            let point = math.getFootPoint(e.hoveredElement.point, this.points[prevIndex], this.points[nextIndex] );
+            let point = math.getFootPoint(e.hoveredElement.point, points[prevIndex], points[nextIndex] );
+             
             return {index, prevIndex, point }  
         }else{
-        
+            let hoverAtGeo = e.hoveredElement.point.clone().applyMatrix4(modelMatrixInvert)
             let prevIndex0 = Math.floor(e.hoveredElement.faceIndex / 2)  //所在的mesh片段的端点1
             let nextIndex0 = prevIndex0 + 1   //所在的mesh片段的端点2
+             
+            let point = math.getFootPoint(hoverAtGeo, this.geoPoints[prevIndex0], this.geoPoints[nextIndex0] ); //新点位置
+            //console.log('point',point)
             
-            let point = math.getFootPoint(e.hoveredElement.point, this.geoPoints[prevIndex0], this.geoPoints[nextIndex0] ); //新点位置
-            if(type == 'onlyPoint')return {point}
+            if(type == 'onlyPoint'){
+                point.applyMatrix4(this.edge.matrixWorld)
+                return {point}
+            }
             
             let prevIndex,nextIndex
-            let count = this.points.length - 1
+            let count = points.length - 1
             for(let i=0;i<count;i++){ 
                 if(prevIndex == void 0 && i / count <= this.UtoTMapArr[prevIndex0] && (i+1) / count > this.UtoTMapArr[prevIndex0]){
                     prevIndex = i //该片段端点1在原先points中哪个点之后(可包含)
@@ -365,7 +425,7 @@ export class Path extends ctrlPolygon{
                 let AB = new THREE.Vector3().subVectors(B,A) 
                 let searchIndex 
                 while(j <= nextIndex){ //根据APlen长度算区间
-                    let len =  AB.clone().normalize().dot(new THREE.Vector3().subVectors(this.points[j+1], A))  //在AB的投影长度
+                    let len =  AB.clone().normalize().dot(new THREE.Vector3().subVectors(points[j+1], A))  //在AB的投影长度
                     if(len > APlen){
                         searchIndex = j
                         break
@@ -378,12 +438,14 @@ export class Path extends ctrlPolygon{
             nextIndex = prevIndex + 1
             //console.log(prevIndex, nextIndex)
             let index = prevIndex + 1 //新点在端点1后
+            
+            point.applyMatrix4(this.edge.matrixWorld)
             return {index, prevIndex, point }      
         }            
     }//如果后续还出现index错误的问题,可以改为绘制时用折线,完成后用曲线。
 
 
-
+ 
 
 
     createMarkerLabel(text, hasHoverEvent){
@@ -453,20 +515,61 @@ export class Path extends ctrlPolygon{
         if(this.color == color)return
         this.color = color  
         let c = new THREE.Color().set(this.color) 
-        this.titleLabel.setTextColor({r: c.r*255, g:c.g*255, b:c.b*255, a:0.5})
+        
+        
+        let hsb = new THREE.Color().set(this.color).getHSV() 
+        let textColor = new THREE.Color().setHSV(hsb.h, hsb.s, 100) //make bright full
+                
+        this.titleLabel.setTextColor({r: textColor.r*255, g:textColor.g*255, b:textColor.b*255, a:0.95})
         this.updateSelectStyle()  //apply color
         
         viewer.dispatchEvent('content_changed')
     }
     
-    updateEdge(){ 
-        if(this.lastUpdatePoints_ && Potree.Common.ifSame(this.lastUpdatePoints_,this.points) && this.halfPathWidth == this.lastHalfPathWidth) return //没变 不更新
+    setLineHeight(len){
+        if(len == this.lineHeight)return
+        this.lineHeight = parseFloat(len)
+        this.titleLabel.position.z = this.lineHeight 
+        this.titleLabel.updatePose() 
+        LineDraw.updateLine(this.titleLine, [new THREE.Vector3(0,0,0), new THREE.Vector3(0,0,this.lineHeight )])
+    }
+    
+     
+    dragLineLen(e){ //拖拽线的顶端修改线长度
+        let endPos = this.titleLabel.getWorldPosition(new THREE.Vector3)
+        let normal = new THREE.Vector3(0,0,1)
+        const projected = endPos.clone().project(e.drag.dragViewport.camera);
+        projected.x = e.pointer.x
+        projected.y = e.pointer.y
+         
+        const unprojected = projected.clone().unproject(e.drag.dragViewport.camera);
+        let moveVec = new THREE.Vector3().subVectors(unprojected, endPos);
+            moveVec = moveVec.projectOnVector(normal) 
+         
+        
+        let newLength = Math.max(0, this.lineHeight + moveVec.dot(normal) )
+        //console.log(moveVec,newLength)
+        this.setLineHeight(newLength)  
+        this.dispatchEvent('dragLineLen')
+            
+    }
+    
+    reDraw(){
+        this.pauseUpdateEdge = true
+        super.reDraw()
+        this.pauseUpdateEdge = false
+        this.updateEdge()
+    } 
+    
+    updateEdge(o={}){ 
+        if(!o.force && (this.pauseUpdateEdge || this.lastUpdatePoints_ && Potree.Common.ifSame(this.lastUpdatePoints_,this.points) && this.halfPathWidth == this.lastHalfPathWidth)) return //没变 不更新
         //this.edge.geometry = MeshDraw.getExtrudeGeo(edgeExtrudePoints, null,  {extrudePath: this.points, openEnded:true, shapeDontClose:true/* , dontSmooth:true, steps: this.points.length-1 */})
         //getExtrudeGeo是平滑过的曲线,和设计不一样,且容易翻转,转角有时候细分过少
         
         //只允许path水平放置
         let geo
-        let points = this.getDifferentPoint(this.points)
+        
+        let points = this.getDifferentPoint(this.points)  
         let count = points.length
         this.edge.geometry.dispose()
         
@@ -474,13 +577,20 @@ export class Path extends ctrlPolygon{
             geo = voidGeometry
             this.geoPoints = []
             this.curve = null
-        }else{
+            this.edge.position.set(0,0,0)
+        }else{ 
+            { //avoidBigNumber 
+                let boundCenter = this.getCenter('bound')
+                points = points.map(e=>e.clone().sub(boundCenter))//防止大数  avoidBigNumber  不过prism的点云效果不对,传入了大数,不好改,等提bug了再说吧
+                this.edge.position.copy(boundCenter)  
+            }
             if(Potree.settings.pathSmooth){//使用曲线
                 let curve = this.curve = new THREE.CatmullRomCurve3(points, false    )  
                 curve.UtoTMapArr = [] //用于存储 getSpacedPoints得到的点对应points的百分比对应
                 let oldCount = count
-                count = Math.max(2, Math.round(this.getTotalDistance() * 200)),  points = curve.getSpacedPoints(count-1) //拆分为更密集的点
-        
+                count = THREE.Math.clamp(Math.round(this.getTotalDistance() * 200), 2, 100000 ),  points = curve.getSpacedPoints(count-1) //拆分为更密集的点
+                //count太多很卡住
+                
                 /* //window.arcLengthDivisions  && (curve.arcLengthDivisions = arcLengthDivisions) //默认200,但改为1也没变化呀
                 let oldCount = count 
                 //减少点数(拐弯的部分紧凑些,直线部分宽松些):
@@ -539,7 +649,8 @@ export class Path extends ctrlPolygon{
                 let sideVec     //该点两边对应的向量
                 let uvX  
                 if(i==0 || i==count-1){ 
-                    sideVec = new THREE.Vector3().copy(math.getNormal2d({p1: i==0 ? B : O, p2: i==0 ? O : A})).setZ(0).multiplyScalar(this.halfPathWidth)  //垂线
+                    let nor = math.getNormal2d({p1: i==0 ? B : O, p2: i==0 ? O : A}) || new THREE.Vector3(1,0,0)
+                    sideVec = new THREE.Vector3().copy(nor).setZ(0).multiplyScalar(this.halfPathWidth)  //垂线
                     uvX = i==0 ? 0 : 1 //percent of length 
                 }else{ 
                     let OA = new THREE.Vector3().subVectors(A,O).setZ(0).normalize()    //只保证俯视角度正确。(如果两点有高度差,该段四个点不会在同一平面,看起来有扭转,有的地方会肥大,但从俯视角度看是正确的。)
@@ -547,7 +658,8 @@ export class Path extends ctrlPolygon{
                     let angle = math.getAngle(OA, OB, 'z') 
                   
                     if(math.closeTo(angle,0,1e-4) || math.closeTo(angle,Math.PI,1e-4) || math.closeTo(angle, -Math.PI,1e-4)){ //这时候直接加两个向量算出的平分线不准,故直接找垂线
-                        sideVec = new THREE.Vector3().copy(math.getNormal2d({p1: O, p2: A})).setZ(0).multiplyScalar(this.halfPathWidth)  //垂线
+                        let nor = math.getNormal2d({p1: O, p2: A}) || new THREE.Vector3(1,0,0)
+                        sideVec = new THREE.Vector3().copy(nor).setZ(0).multiplyScalar(this.halfPathWidth)  //垂线
                         //console.log('接近0或180',angle, sideVec)
                     }else{
                         let midVecLength = this.halfPathWidth / Math.sin(angle/2)   
@@ -626,13 +738,16 @@ export class Path extends ctrlPolygon{
                     this.removePoint(this.markers.indexOf(marker)) 
                     
                 }else{//点击选中点
-                    this.dispatchEvent({type:'markerSelect', marker})
+                    this.markers.forEach(marker_=>{
+                        marker_ != marker && this.setMarkerSelected(marker_, 'unclick' );
+                    })
                     this.setMarkerSelected(marker, 'click' );
+                    
                        
                     setTimeout(()=>{
                         viewer.addEventListener('global_click', (e)=>{ //点击空白处取消全部
-                            if(e.clickElement?.oriObject == marker)return
-                            this.dispatchEvent({type:'markerSelect', marker, cancel:true})
+                            //if(e.clickElement?.oriObject == marker)return
+                                                                                          
                             this.setMarkerSelected(marker, 'unclick' ); 
                         }, {once:true} )
                     },10) 
@@ -641,7 +756,7 @@ export class Path extends ctrlPolygon{
             })
             
             marker.addEventListener('startDragging',(e)=>{ 
-                this.isNew || this.setMarkerSelected(marker, 'click' ); //选中
+                this.isNew || this.setMarkerSelected(marker, 'drag' ); //选中
                 this.isNew || viewer.measuringTool.history.beforeChange(this)
                 this.arrows && Potree.Utils.updateVisible(this.arrows, 'dragging', false)
                 //Potree.Common.waitTool.cancel('pathUpdateArrowDelay')
@@ -661,7 +776,7 @@ export class Path extends ctrlPolygon{
                 
                 this.lastDropTime = Date.now() 
                 this.isNew || viewer.measuringTool.history.afterChange(this)
-                this.setMarkerSelected(marker, 'unclick' );   
+                this.setMarkerSelected(marker, 'undrag' );   
             }) 
             
             
@@ -680,9 +795,9 @@ export class Path extends ctrlPolygon{
 
  
 
-    updateEndCaps(){
-        let len = this.points.length
-        let pts = this.geoPoints.length>0 ? this.geoPoints : this.points
+    updateEndCaps(points=this.points){//更新端点  (这把points提到参数里是因为有个隐藏点的项目)
+        let len = points.length
+        let pts = this.geoPoints.length>0 ? this.geoPoints : points
         let len2 = pts.length
         
         
@@ -692,18 +807,19 @@ export class Path extends ctrlPolygon{
                 if(len>1){
                     let dir = i==0 ? new THREE.Vector3().subVectors(pts[1], pts[0]) 
                               : new THREE.Vector3().subVectors(pts[len2-2], pts[len2-1]) 
-                    e.quaternion.copy(getMeshQuaInPath(dir))
+                    e.quaternion.copy(this.getMeshQuaInPath(dir))
                 }else{
                     i==0 ? e.quaternion.set(0,0,0,1) : e.quaternion.set(0,0,1,0)  //两个半圆拼成一个圆点
                 }
-                e.position.copy(this.points[i==0 ? 0 : len-1])
+                e.position.copy(points[i==0 ? 0 : len-1]) 
                 let s = this.halfPathWidth * 2.15
                 e.scale.set(s,s,s)
             } 
        })
-    }
+    } 
     
     setEditEnable(state){//是否显示可修改控件
+        if(!this.endCaps)return                     
         this.editEnable = !!state
         this.markers.forEach(e=>Potree.Utils.updateVisible(e,'editEnable', this.editEnable)) 
         this.endCaps.forEach((e,i)=>Potree.Utils.updateVisible(e,'editEnable', !this.editEnable))
@@ -773,13 +889,13 @@ export class Path extends ctrlPolygon{
     } 
     
 
-    removePoint(index){
+    removePoint(index){//外部调用
         
         if(index == -1){ 
             return //双击会这样,加了迅速删除, 可能因为没来得及删
         }
         
-        viewer.measuringTool.history.beforeChange(this) 
+        viewer.measuringTool.history.beforeChange(this)  //不同于通过点击marker来删除的已在别处执行了,外部调用的话要补
         this.removeMarker(index)
         this.hideArrowUntilUpdate()
         viewer.measuringTool.history.afterChange(this)
@@ -791,18 +907,37 @@ export class Path extends ctrlPolygon{
         }
     }
 
-
+	removeMarker(index ){ //通过pen点击marker来删除点
+     
+        super.removeMarker(index)
+        
+        this.points_datasets.splice(index, 1);
+        this.dataset_points && this.dataset_points.splice(index, 1)
+        
+        
+        let labelIndex = index 
+        if(this.markerLabels[labelIndex]){ 
+            this.markerLabels[labelIndex].dispose() 
+            this.markerLabels.splice(labelIndex, 1);
+        }
+         
+        this.update({index: this.getIndex(index, -1)}); 
+        this.dispatchEvent({type: 'marker_removed', measurement: this});
+         
+        
+    }
 
     
     setMarkerSelected(marker, state ){
        
         state == 'hover' && (marker.markerSelectStates.hover = true )
         state == 'unhover' && (marker.markerSelectStates.hover = false )
-        state == 'click' && (marker.markerSelectStates.click = true ) //click or drag
-        state == 'unclick' && (marker.markerSelectStates.click = false )
-        
+        state == 'click' && (marker.markerSelectStates.click = true, this.dispatchEvent({type:'markerSelect', marker})) //click or drag
+        state == 'unclick' && (marker.markerSelectStates.click = false, this.dispatchEvent({type:'markerSelect', marker, cancel:true})) 
+        state == 'drag' && (marker.markerSelectStates.drag = true ) //click or drag
+        state == 'undrag' && (marker.markerSelectStates.drag = false ) 
         
-        if(marker.markerSelectStates.click){
+        if(marker.markerSelectStates.click || marker.markerSelectStates.drag){
             marker.material = getMarkerMat('drag')
             marker.renderOrder = marker.pickOrder = Potree.config.renderOrders.path.marker +1
         }else if(marker.markerSelectStates.hover){
@@ -815,6 +950,7 @@ export class Path extends ctrlPolygon{
          
         //marker.selected = absoluteState
         
+        
         viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed') 
         viewer.dispatchEvent('content_changed')
     }
@@ -854,19 +990,26 @@ export class Path extends ctrlPolygon{
     updateSelectStyle(){
         
         
-        let c = new THREE.Color().set(this.color).getHSL({ h: 0, s: 0, l: 0 })
+                                                                              
         
+        //let c = new THREE.Color().set(this.color).getHSL({ h: 0, s: 0, l: 0 })
+        let hsv = new THREE.Color().set(this.color).getHSV()
         let color, arrowColor  
         if(this.selectStates.click){
             color = '#00C8AF'
             arrowColor = '#ffffff'
+             
         }else if(this.selectStates.hover){  
-            color = new THREE.Color().setHSL(c.h, c.s, c.l - 0.1 )   
-            arrowColor = new THREE.Color().setHSL(c.h, c.s, c.l >= 0.4 ? c.l - 0.3 : c.l + 0.3 )  
-        }else{
-            arrowColor = new THREE.Color().setHSL(c.h, c.s, c.l >= 0.4 ? c.l - 0.3 : c.l + 0.3 )  
+            /* color = new THREE.Color().setHSL(c.h, c.s, c.l - 0.1 )   
+            arrowColor = new THREE.Color().setHSL(c.h, c.s, c.l >= 0.4 ? c.l - 0.3 : c.l + 0.3 )  */ 
+            color = new THREE.Color().setHSV(hsv.h, hsv.s, hsv.v - 10 )   
+            arrowColor = new THREE.Color().setHSV(hsv.h, hsv.s, hsv.v >= 40 ? hsv.v - 30 : hsv.v + 30 )
             
+        }else{
+            //arrowColor = new THREE.Color().setHSL(c.h, c.s, c.l >= 0.4 ? c.l - 0.3 : c.l + 0.3 )  
+            arrowColor = new THREE.Color().setHSV(hsv.h, hsv.s, hsv.v >= 40 ? hsv.v - 30 : hsv.v + 30 )  
             color = this.color
+            
         }
         
         if(this.arrows){
@@ -876,28 +1019,30 @@ export class Path extends ctrlPolygon{
         }
         ([this.edge, this.endCaps[0].children[0]].forEach(e=>{ 
             e.material.color.set(color)
+            e.material.uniforms.maxClipFactor.value = this.selectStates.click ? 0.4 : this.selectStates.hover ? 0.6 : depthProps.maxClipFactor
+            e.material.uniforms.occlusionDistance.value = this.selectStates.click ? 0.6 : this.selectStates.hover ? 0.7 : depthProps.occlusionDistance
         }))
     }
     
-	removeMarker(index ){ 
+                       
      
-        super.removeMarker(index)
-        
-        this.points_datasets.splice(index, 1);
-        this.dataset_points && this.dataset_points.splice(index, 1)
+                                 
+    
+                                              
+                                                                   
         
         
-        let labelIndex = index 
-        if(this.markerLabels[labelIndex]){ 
-            this.markerLabels[labelIndex].dispose() 
-            this.markerLabels.splice(labelIndex, 1);
-        }
+                               
+                                           
+                                                    
+                                                    
          
-        this.update({index: this.getIndex(index, -1)}); 
-        this.dispatchEvent({type: 'marker_removed', measurement: this});
+         
+                                                        
+                                                                        
          
         
-    }
+     
     
     setPosition(index, position) {
         super.setPosition(index, position)
@@ -1149,7 +1294,7 @@ export class Path extends ctrlPolygon{
                     scaleMap.set(e.lineInfo.path, scaleMatrix)
                     
                 } 
-                let qua = getMeshQuaInPath(e.lineInfo.dir)//math.getQuaFromPosAim( new THREE.Vector3, e.lineInfo.dir ).multiply(quaBase) 
+                let qua = this.getMeshQuaInPath(e.lineInfo.dir)//math.getQuaFromPosAim( new THREE.Vector3, e.lineInfo.dir ).multiply(quaBase) 
                 let quaMatrix = new THREE.Matrix4().makeRotationFromQuaternion(qua)
                 scaleQuaMatrix = e.lineInfo.scaleQuaMatrix = new THREE.Matrix4().multiplyMatrices(quaMatrix, scaleMatrix) 
             }
@@ -1168,13 +1313,16 @@ export class Path extends ctrlPolygon{
     }
     
    
- 
+    getMeshQuaInPath(lineDir){ 
+
+        return math.getQuaFromPosAim( new THREE.Vector3, lineDir ).multiply(quaBase) 
+    }
 
 }
 
 Path.prototype.cloneMarker = Measure.prototype.cloneMarker
 Path.prototype.updateDatasetBelong = Measure.prototype.updateDatasetBelong 
-Path.prototype.reDraw = Measure.prototype.reDraw
+//Path.prototype.reDraw = Measure.prototype.reDraw
 /* 
 没有intersect的点的dataset_point怎么赋值
 

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

@@ -1045,7 +1045,7 @@ var TransformControlsGizmo = function (options) {
 
 
 	var matLineYellowTransparent = matLineYellow.clone();
-	matLineYellowTransparent.opacity = 0.25;
+	matLineYellowTransparent.opacity = 0.5//0.25;
 
 	// reusable geometry
 

+ 1 - 1
src/custom/objects/tool/ctrlPolygon.js

@@ -151,7 +151,7 @@ export class ctrlPolygon extends THREE.Object3D {
             o.marker.createTime = Date.now()
             
             let addHoverEvent = (e)=>{
-                if(o.marker._listeners?.mouseover || !this.editEnable)return //already has
+                if(o.marker._listeners?.mouseover?.length || !this.editEnable)return //already has
                 
                 let mouseover = (e) => {  
                     this.setMarkerSelected(e.object, 'hover', 'single'); 

+ 2 - 3
src/custom/settings.js

@@ -308,15 +308,14 @@ const config = {//配置参数   不可修改
         measureLabel: 8,
         sorptionSign:10,
         model:10,
-        
-        
+         
         path: {
             label: labelorder, 
             edge: labelorder,  
             line: labelorder ,  //会被edge遮住一些,不过无碍
             marker: labelorder+1, //cover edge
         }, 
-         
+        polyDraw: labelorder, 
         tag:{ 
             label: labelorder,
             spot: labelorder,  

+ 229 - 7
src/custom/start.js

@@ -7,6 +7,8 @@ import math from './utils/math.js'
 import browser from './utils/browser.js' 
 import './three.shim.js'  
 import "./potree.shim.js"
+import ReportInfoStream from './utils/reportInfoStream.js' 
+import {lerp} from './utils/transitions.js' 
  
 window.THREE = THREE
 
@@ -289,7 +291,9 @@ export function start(dom, mapDom, number, info={} ){ //t-Zvd3w0m
             
             viewer.dispatchEvent('allLoaded')
             
-             
+            if(!Potree.settings.isOfficial && Potree.settings.number == 'SG-t-rQ14yS9VjVp' ){
+                 setTimeout(listenLocation, 1000)
+            }
             
             
         }
@@ -600,6 +604,10 @@ export function panoEditStart(dom, number, EditCloudsArgs){
         Potree.Log('loadPointCloudDone  点云加载完毕',{font:[null, 10]})  
         
         viewer.dispatchEvent('allLoaded');
+        
+        
+        
+       
     }
     
     
@@ -1339,12 +1347,7 @@ export function mergeEditStart(dom, mapDom){
     return {THREE}
 }
  
- 
- 
- 
- 
- 
- 
+  
  
 var changeLog = ()=>{  
          
@@ -1391,7 +1394,226 @@ var changeLog = ()=>{
  
  
  
+
+
  
+function listenLocation(){//室内定位测试
+
+    let fakeMeasure = {
+        type : 'Path', "unit": "metric", points:[], width:0.1
+    }
+    let path = viewer.measuringTool.createMeasureFromData(fakeMeasure) 
+    path.setEditEnable(false)
+    path.setAddOrRemPoint(false) 
+    path.edge.material.useDepth = false 
+    path.endCaps[0].children[0].material.useDepth = false 
+    path.endCaps[1].children[0].material.useDepth = false 
+    path.setColor('#FF4399')
+    path.setPathWidth(0.5)
+    viewer.mainViewport.view.applyJson({"yaw":-3.1401257265634337,"pitch":-1.4463945277050603,"position":{"x":-3.6298548644429034,"y":1.631125389514918,"z":25.881133155122455},"radius":2.5485311249338363})
+    let sphere = new THREE.Mesh(new THREE.SphereBufferGeometry(0.1,4,4),new THREE.MeshBasicMaterial({
+        color:'#f11',
+        depthTest:false
+    }))
+    //sphere.rotation.set()
+    viewer.scene.overlayScene.add(sphere)
+     
+    let label = new Potree.TextSprite({ sizeInfo:{width2d:160}, renderOrder:10, transform2Dpercent:{x:0,y:0.5}})
+    
+    label.name = 'locationlabel'
+    sphere.add(label)
+    label.setPos(new THREE.Vector3(0,0,0.3))
+    sphere.visible = false
+    let setLabel = (e)=>{
+        let texts = [e.name]
+        if(e.locationDesc) texts.push(e.locationDesc)
+        label.setText(texts)
+    }
+ 
+    
+    viewer.inputHandler.addEventListener('keydown',e=>{ 
+        if(e.event.key.toLowerCase() == 'f' && sphere.visible){
+            let distance = e.event.shiftKey ? 4 : Math.min(30, viewer.mainViewport.view.position.distanceTo(sphere.position))
+            viewer.focusOnObject({  position: sphere.position}, 'point', 400, {distance} )
+        }            
+    })
+    
+    let wholeId = browser.urlHasValue('livePath',true)   
+    if(wholeId != ''){//显示该用户完整路径 
+        let points, curPercent=0, data,interval, from = new THREE.Vector3, to = new THREE.Vector3//每次走两个点间的十分之一. 显示长度为1个单位
+            
+        let walk = ()=>{
+            if(curPercent < points.length){ 
+             
+                let index = Math.floor(curPercent)
+                let percent_ = curPercent % 1 //小数部分
+                let point1 = points[index-1] ? (from.copy(points[index-1]), to.copy(points[index]),
+                            lerp.vector(from, to)(percent_), from.clone() ) : points[0]
+                let point2 = points[index+1] ? (from.copy(points[index]), to.copy(points[index+1]),
+                            lerp.vector(from, to)(percent_), from.clone() ) : points[index] 
+                path.points = [point1, point2]
+                index > 0 && index < points.length - 1 && path.points.splice(1,0,points[index]) //跨的话 中间加一个拐弯的
+                curPercent += 0.1 
+                path.update()
+                sphere.position.copy(points[index])
+                setLabel(data[index]) 
+                sphere.visible = true
+                viewer.dispatchEvent('content_changed')  
+            }else{
+                clearInterval(interval)
+            } 
+        }
+        
+         
+    
+        Potree.loadFile('https://test.4dkankan.com/openDevice/record/'+wholeId,{
+                fetchMethod:'post',
+            },
+            (data_)=>{
+                if(!(data_ instanceof Array)){
+                    return console.error('获取完整路径为空',data)
+                }
+                data = data_
+                console.log('获取到的路径',data)
+                if(Potree.settings.isTest ){
+                    data = [{
+                        name:'1',
+                        pos: new THREE.Vector3(10,4,0),
+                    },
+                    {
+                        name:'2',
+                        pos: new THREE.Vector3(4,4,2),
+                    },
+                    {
+                        name:'3',
+                        pos: new THREE.Vector3(6,3,2),
+                    },
+                    {
+                        name:'4',
+                        pos: new THREE.Vector3(8,4,0),
+                    }]    
+                }                
+                points = data.map(e=>new THREE.Vector3().copy(e.pos)) 
+                 
+                interval = setInterval(walk, 100)
+                 
+                 
+            }
+        )
+        
+        return
+    } 
+    //以下实时显示正在走的路径
+    
+    let locationStream = new ReportInfoStream
+    
+    let lastPoint ,  requestPoints = {startPos: new THREE.Vector3, endPos: new THREE.Vector3}, waitRoute
+    
+     
+    /* let getZOnGround = (pos_)=>{//要近地面才能搜出来 
+        let pos = new THREE.Vector3().copy(pos_)
+            pos.z -= 1 //假设标签都在离屋顶更靠近的位置,向下寻找临近漫游点
+        let disMap = new Map
+        let result = Potree.Common.sortByScore(viewer.images360.panos, [(pano)=>{
+            return Math.abs(pos.z - pano.position.z) < 2 
+        }],[pano=>{
+            let disSq = pano.position.distanceToSquared(pos) 
+            disMap.set(pano, disSq)
+            return -disSq
+        }])
+        let zArr = result.slice(0,10).map(e=>e.item.position.z)// floorPosition  
+        zArr.sort()
+        pos.z = zArr[Math.round(zArr.length/2)] 
+        if(pos.z == void 0){
+            pos.z = pos_.z - 2
+            console.log('getZOnGround没找到')
+        }
+        //pos.z-=0.2
+        return pos 
+    } */
+    let walk = ()=>{
+        if(path.points.length>1){
+            path.points.splice(0,1)
+            path.update()
+            viewer.dispatchEvent('content_changed') 
+        }
+        
+    }
+    setInterval(walk, 100)
+    
+    
+    let history = []
+    locationStream.subscribe(e=>{
+        //console.log('locationStream',e.pos)
+        setLabel(e)
+        //label.setPos(e.pos)
+        sphere.position.copy(e.pos)
+        sphere.visible = true
+        
+        
+        let last = history[history.length - 1]
+        let isDifferent = Potree.Common.ifSame( last?.pos, e.pos )
+        
+        
+        
+        if(last && !waitRoute && !isDifferent){ 
+            requestPoints.startPos.copy(lastPoint)
+            requestPoints.endPos.copy(e.pos) 
+            /* requestPoints.startPos = getZOnGround(lastPoint)
+            requestPoints.endPos = getZOnGround(e.pos) */ 
+            console.log('startPos',requestPoints.startPos,'endPos',requestPoints.endPos) 
+            
+            Potree.loadFile(`https://test.4dkankan.com/openDevice/query?from=${last.uuid}&to=${e.uuid}`,{},(data)=>{
+                data = 'data' in data ? data.data : data 
+                data = data || []
+                data.length>0 && console.log('getRoute', data.map(e=>e))
+                path.points = path.points.concat(data.map(e=>new THREE.Vector3().copy(e))) 
+                path.update() 
+                waitRoute = false
+            },()=>{
+                waitRoute = false
+            })  
+            /* Potree.loadFile('https://test.4dkankan.com/openDevice/SG-t-rQ14yS9VjVp/getRoute',{
+                fetchMethod:'post',
+                sendInfo : { 
+                  "sceneCode": "SG-t-rQ14yS9VjVp",
+                  "startPos": requestPoints.startPos,
+                  "endPos": requestPoints.endPos
+                }
+            },(data)=>{ 
+                data = 'data' in data ? data.data : data 
+                data = data || []
+                data.length>0 && console.log('getRoute', data.map(e=>e.location))
+                path.points = path.points.concat(data.map(e=>new THREE.Vector3().fromArray(e.location)) ) 
+                path.update() 
+                waitRoute = false
+            },()=>{
+                waitRoute = false
+            })  */
+            waitRoute = true
+        }
+        
+        
+        isDifferent || history.push(e)
+        lastPoint = new THREE.Vector3().copy(e.pos)
+        viewer.dispatchEvent('content_changed') 
+    })
+    locationStream.start()
+    
+    window.liveLocation = {
+        sphere, label, path, history
+    }
+    
+    
+    
+    /* for(let i=0;i<points.length;i++){
+        path.setMarkerTitle(i,'')
+    } */
+    
+     
+    
+    
+}
  
  
 

+ 3 - 2
src/custom/three.shim.js

@@ -176,9 +176,10 @@ THREE.EventDispatcher.prototype.removeAllListeners = function(){  //add
 	this._listeners = {};
 }
 
-THREE.EventDispatcher.prototype.dispatchEvent = function(event){ 
+THREE.EventDispatcher.prototype.dispatchEvent = function(event ){ 
 	if(typeof event == 'string'){//add
-        event = {type:event}
+        let o = arguments[1] || {}
+        event = Object.assign(o, {type:event})
     }
     if ( this._listeners === undefined ) return;
 

+ 48 - 6
src/custom/utils/Common.js

@@ -81,10 +81,29 @@ var Common = {
         }
         return i / n
     },
-    
-    
-    //---------------------------
-    
+    randomWord(randomFlag, min, max) {
+        //随机字符串
+        var str = '',
+            range = min,
+            arr = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',]
+
+        if (randomFlag) {
+            // 随机长度
+            range = Math.round(Math.random() * (max - min)) + min
+        }
+        for (var i = 0; i < range; i++) {
+            var pos = Math.round(Math.random() * (arr.length - 1))
+            str += arr[pos]
+        }
+        return str
+    },
+    getRandomSid() {
+        //随机字符串 + 时间   
+        var pre = this.randomWord(true, 5, 7)
+        var post = new Date().getTime() + '' 
+        return pre + post
+    },//or THREE.Math.generateUUID
+     
     
     getMixedSet : function(arr1, arr2){//交集
         return arr1.filter(item=>arr2.includes(item));
@@ -380,11 +399,34 @@ var Common = {
             index>-1 && this.list.splice(index, 1)
         }
         
-    }
-    
+    },
     
     
     
+    logAreaTool : {
+        area: null,
+        init(area){
+           this.area = area 
+        }, 
+        list:{},
+        change(name, text, level=0){
+            this.list[name] = {text, level}
+            this.update()
+        },
+        remove(name){
+            delete this.list[name]
+            this.update()
+        },
+        update(){
+            let text = ''
+            let objects = Object.values(this.list)
+            objects.sort((a,b)=>{return b.level - a.level})
+            for(let i=0, len=objects.length; i<len; i++){
+                text += objects[i].text + '<br>'
+            }
+            this.area.innerHTML = text
+        }
+    }
     
     
     ,

+ 4 - 0
src/custom/utils/CursorDeal.js

@@ -15,6 +15,8 @@ var CursorDeal = {
         {'grabbing':'grabbing'},//通用
         {'hoverGrab':'grab'},//通用
         {'pointer':'pointer'},//通用
+        {polygonMark_move:'move'},
+        {polygonMark_hover: 'pointer'},
         
         
         
@@ -40,6 +42,8 @@ var CursorDeal = {
         {'mapClipMove':'move'},
         {'mapClipRotate':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
         {'rotatePointcloud':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
+        {'rotatePointcloud2':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
+        
         {'siteModelFloorDrag':'row-resize'},
         {'addSth':'cell'},//or  crosshair
     ], 

+ 103 - 0
src/custom/utils/PanoScreenshot.js

@@ -0,0 +1,103 @@
+//全景截图
+
+import cameraLight from './cameraLight.js' 
+
+
+const whPercent = 1.78 //固定输出的图长宽比
+
+
+export const PanoScreenshot = {
+
+    getMaxHeight() {
+        //全景图高度
+        return 4096 * 2 //this.app.core.get('QualityManager').tileClass == '2k' ? 4096 : this.app.core.get('QualityManager').tileClass == '4k' ? 4096 * 2 : 1024
+    },
+
+    getTileSize() {
+        // 获取单个面的大小
+        return 4096//this.app.core.get('QualityManager').tileClass == '2k' ? 2048 : this.app.core.get('QualityManager').tileClass == '4k' ? 4096 : 512
+    },
+
+
+    getPicSize() { 
+        const maxHeight = this.getMaxHeight()
+        let height = (viewer.mainViewport.camera.fov / 180) * maxHeight
+        let width = height * whPercent
+        let size = {
+            width: Math.round(width),
+            height: Math.round(height),
+        }
+        return size
+        //  matterport:                                       4dkk: (4k)
+
+        //0.7:                                                8101*4551  3.35M
+        //1 :  6068*3413    h 8778.29218106  不是 8192??    5671*3186
+        //3:   2023*1138                                      1890*1062
+
+        //(2k减半)  如1890*1062  --> 945*531
+    },
+    waitTexLoaded(){
+        let tileSize = this.getTileSize()
+        
+        let camera = viewer.mainViewport.camera
+        let vFov = camera.fov
+        let hFov = cameraLight.getHFOVForCamera(camera)
+
+        let promise = viewer.images360.currentPano.loadTiledPano( tileSize, 
+            viewer.images360.getTileDirection(), { hFov, vFov, }, !1, !1, !0 )
+        
+        /* promise.done(() => {
+            console.log('加载完成') 
+        })  */ 
+        return promise
+    },
+    
+    getOrientation() {
+        //"cameraOrientation": "01-东;02-南;03-西;04-北;05-东北;06-东南;07-西北;08-西南" // 拍摄镜头朝向代码 FXLBDM,方位照片和概貌照片需要传;01-东;02-南;03-西;04-北;05-东北;06-东南;07-西北;08-西南
+        let compassAngle = THREE.MathUtils.degToRad(this.app.core.get('Player').compass.angle || 0)
+        let yaw = this.app.core.get('Player').yaw || 0
+        let angle = -yaw + (5 * Math.PI) / 2 - compassAngle + Math.PI / 8
+        let text = ['北', '东北', '东', '东南', '南', '西南', '西', '西北']
+        let num = ['04', '05', '01', '06', '02', '08', '03', '07']
+        let index = Math.floor((angle % (2 * Math.PI)) / (Math.PI / 4))
+        console.log(text[index])
+        let r = num[index]
+        return r
+    },
+    
+    getCamAngle(){//相对指南针的北方向的角度
+        
+        let angle = -THREE.Math.radToDeg(viewer.mainViewport.view.yaw)-(viewer.floorCompass?.angle || 0)
+        
+        return (angle + 360)%360    //限制在0-360 
+    },
+    
+    toggleDisplay(show){//只显示全景图
+         
+    },
+    screenshot(){
+        this.toggleDisplay(false)
+        let size = this.getPicSize()
+        Potree.settings.highestQualityTile = true  //不放大也加载4k
+        let promise = new Promise((resolve, reject) => {
+            this.waitTexLoaded().then(e=>{
+                if( viewer.images360.panoRenderer.zoomRenderTarget.width < 4096){
+                    console.error('zoomRenderTarget size < 4096 !!!!!')
+                    debugger
+                }
+                
+                var {getImagePromise, finishPromise} = viewer.startScreenshot({ type: 'default', noOverlay:true }, size.width, size.height, 1)
+                finishPromise.done((e) => {
+                    //Potree.Common.downloadFile(e.dataUrl, 'screenshot.png') 
+                    Potree.settings.highestQualityTile = false
+                    this.toggleDisplay(true)
+                    resolve(e.dataUrl) 
+                })
+            }) 
+        });  
+        return promise
+    }
+    
+    
+}
+  

+ 26 - 14
src/custom/utils/SplitScreen.js

@@ -14,11 +14,11 @@ class SplitScreen extends THREE.EventDispatcher{
         viewport.shiftTarget  // camera的位置project在targetPlane上的位置
         这两个参数的主要目的是为了getPosOutOfModel,以及rotateSideCamera时保持相对位置
     */
-    splitStart(cameraProps){ 
+    splitStart(cameraProps){//创建viewport 
         this.splited = true
         let viewports = []
       
-        let subViewports = [viewer.mainViewport]
+        let subViewports = [viewer.mainViewport] //已有项
         if(viewer.mapViewer){
             subViewports.push(viewer.mapViewer.viewports[0])
         }
@@ -159,29 +159,38 @@ class SplitScreen extends THREE.EventDispatcher{
         
     }
      
-    rotateSideCamera(viewport, angle){//侧视图或俯视图绕模型中心水平旋转
-         
+    rotateSideCamera(viewport, angle, axis=new THREE.Vector3(0,0,1)){//侧视图或俯视图绕模型中心水平旋转
+         this.rotateCamera(viewport, angle, axis) 
+    }
+    
+    
+    rotateCamera(viewport, angle, axis, endQua){//旋转前后保持shiftTarget在镜头的相对位置不变
         //let {boundSize, center} = viewer.bound
         let {boundSize, boundCenter } = this.getViewBound(viewport)  
         let center = this.focusCenter || boundCenter //旋转中心,一般是所有模型的中心,除非想指定中心点
         this.setShiftTarget(viewport, center)
         //找到平移向量
         let vec = new THREE.Vector3().subVectors(center, viewport.shiftTarget)//相对于中心的偏移值,旋转后偏移值也旋转
-        
-        //旋转
-        var rotMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), angle) 
-        
-        viewport.view.direction = viewport.view.direction.applyMatrix4(rotMatrix)
-         
-         
+        var rotMatrix
+        if(endQua){ 
+            let qua = new THREE.Quaternion().multiplyQuaternions(endQua, viewport.view.quaternion.invert()) 
+            rotMatrix = new THREE.Matrix4().makeRotationFromQuaternion(qua)
+            viewport.view.quaternion = endQua
+        }else{
+            rotMatrix = new THREE.Matrix4().makeRotationAxis(axis, angle) 
+            //viewport.view.direction = viewport.view.direction.applyMatrix4(rotMatrix)
+            let qua = new THREE.Quaternion().setFromRotationMatrix(rotMatrix)
+            viewport.view.quaternion =  viewport.view.quaternion.premultiply(qua)
+                  
+        }
+          
         vec.applyMatrix4(rotMatrix)
         viewport.shiftTarget.subVectors(center,vec) //新的
-        
-        
+         
         viewport.view.position = this.getPosOutOfModel(viewport, boundSize)
-        
     }
     
+    
     getOrthoCamera(){
         let camera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
         camera.up.set(0,0,1)
@@ -189,6 +198,9 @@ class SplitScreen extends THREE.EventDispatcher{
     } 
     
     focusOnViewport(name){//全屏
+        if(this.focusInfo){
+            this.unfocusViewport()
+        }
         viewer.viewports.forEach((viewport, i )=>{
             if(viewport.name == name){
                 this.focusInfo = {

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

@@ -633,7 +633,7 @@ var math = {
     getQuaFromPosAim( position, target ){ 
         /* let matrix = (new THREE.Matrix4).lookAt(position, target, new THREE.Vector3(0,0,1)) //这里垂直的话会默认给一个右向所以不这么写
         return (new THREE.Quaternion).setFromRotationMatrix(matrix) */
-        
+        view.yaw = 0 //reset     direction.z = 1
         view.direction = new THREE.Vector3().subVectors(target,position) 
         return view.quaternion
     },

+ 98 - 79
src/custom/viewer/ViewerNew.js

@@ -60,7 +60,7 @@ import {SiteModel} from "../modules/siteModel/SiteModel.js";
 import MergeEditor from "../modules/mergeModel/MergeEditor.js";
 import {RouteGuider}  from '../modules/route/RouteGuider.js'
 import {Clipping}  from '../modules/clipping/Clipping.js'
-
+import PolygonMarkManager from '../modules/panoPolygon/PolygonMarkManager.js'
 
 
 import ParticleEditor from '../modules/Particles/ParticleEditor.js'
@@ -80,9 +80,9 @@ import {ViewerBase} from "../viewer/viewerBase.js"
     
 import {Gradients} from "../../materials/Gradients.js";   
 import SSAARenderPass from "../materials/postprocessing/SSAARenderPass.js"
-import EffectComposer from '../materials/postprocessing/EffectComposer.js'
-import {ShaderPass} from '../materials/postprocessing/ShaderPass.js'
-import RenderPass from '../materials/postprocessing/RenderPass.js'
+import EffectComposer from '../materials/postprocessing/myVer/EffectComposer.js'
+import {ShaderPass} from '../materials/postprocessing/myVer/ShaderPass.js'
+import RenderPass from '../materials/postprocessing/myVer/RenderPass.js'
 import FXAAShader from "../materials/postprocessing/FXAAShader.js"
 import OutlinePass from "../materials/postprocessing/OutlinePass.js"
 import BasicMaterial from '../materials/BasicMaterial.js'  
@@ -189,7 +189,8 @@ export class Viewer extends ViewerBase{
                 ParticleEditor,
                 CamAniEditor, 
                 volumeComputer: new VolumeComputer,
-                MergeEditor 
+                MergeEditor,
+                PolygonDraw: new PolygonMarkManager(this)
             }
         }
         {
@@ -526,10 +527,21 @@ export class Viewer extends ViewerBase{
               
                 //-------------------------- 
                 
-                
-                this.composer = new EffectComposer( this.renderer );  
+               /*  
+                this.composer = new EffectComposer( this  );  
                 this.composer.scaleRatio = 4 //将底图和测量线绘制在一张高倍贴图上,for测量线不模糊
-                this.composer.readTarget = true  //把底图和测量线一起fxaa 
+                //this.composer.readTarget = true  //把底图和测量线一起fxaa  */
+                
+                if(Potree.settings.isWebgl2 && Potree.settings.noAA){
+                    this.aaComposer = new EffectComposer( this, {antialias:true} );   
+                    const renderPass = new RenderPass(); 
+                          renderPass.clear = false
+                    this.aaComposer.addPass( renderPass );   
+                    this.aaComposer.copyPass.renderToScreen = true
+                    this.aaComposer.copyPass.needsSwap = false
+                    this.aaComposer.addPass(this.aaComposer.copyPass)
+                    
+                }
                 /* const renderPass = new RenderPass();
                 
 				//renderPass.clearColor = new THREE.Color( 0,0,0 );      
@@ -539,32 +551,19 @@ export class Viewer extends ViewerBase{
                  */
                  
                  
-                //for 融合页面 
+               /*  //for 融合页面 
                 let outlinePass = this.outlinePass = new OutlinePass( );
                 outlinePass.renderToScreen = true  //这样更流畅,不用ssaa了,缺点是outline有锯齿
                 outlinePass.enabled = false
                 this.composer.addPass( outlinePass );
                 outlinePass.edgeStrength = 4
                 outlinePass.edgeGlow = 0 
-                outlinePass.visibleEdgeColor = new THREE.Color("#09a1b3")   
+                outlinePass.visibleEdgeColor = new THREE.Color("#09a1b3")    */
                  
                  
                  //--------------
-                 
-                /* const renderPass = new RenderPass(); 
-				//renderPass.clearColor = new THREE.Color( 0,0,0 );      
-				//renderPass.clearAlpha = 0; 
-                renderPass.clear = !this.composer.readTarget 
-                this.composer.addPass( renderPass );   
-                 
-                this.fxaaPass = new ShaderPass( FXAAShader );  
-                this.fxaaPass.readTarget = true //add 
-                this.fxaaPass.setSize = function(width, height){
-                    this.material.uniforms[ 'resolution' ].value.x = 1 / ( width )  ;
-                    this.material.uniforms[ 'resolution' ].value.y = 1 / ( height )  ;  
-                } 
-                this.fxaaPass.renderToScreen = true;
-                this.composer.addPass( this.fxaaPass ); */
+                /*  
+                //用fxaa只能看起来模糊一点点,但转动镜头点云还是闪烁 */
                 //-----------------------
                 
                 
@@ -697,20 +696,16 @@ export class Viewer extends ViewerBase{
              
             //-----------
             CursorDeal.init(this, this.mapViewer ? [this, this.mapViewer] : [this])//ADD
+            this.images360 = new Images360(this);
             
             
+            this.scene.scene.add(this.objs);
             this.modules.PanoEditor?.init()
             this.modules.SiteModel?.init()  //add
             this.modules.Alignment?.init()
             Potree.settings.editType == "merge" && this.modules.MergeEditor?.init()
-            this.modules.ParticleEditor?.init()
-            
-          
-            
-            this.images360 = new Images360(this);
-            
-            this.scene.scene.add(this.objs);
-             
+            this.modules.ParticleEditor?.init() 
+            this.modules.PolygonDraw?.init()
              
             //add test
             /* const environment = new RoomEnvironment();
@@ -1505,11 +1500,18 @@ export class Viewer extends ViewerBase{
             if(pointclouds.length == 0){
                 if(this.focusDatasets){
                     pointclouds = this.focusDatasets  
-                }  
-            }
-            if(pointclouds.length == 0){//如果当前不在任何楼层或楼层中无数据集,就用当前所在数据集
-                pointclouds = this.atDatasets
+                } 
+                if(pointclouds.length == 0){//如果当前不在任何楼层或楼层中无数据集,就用当前所在数据集
+                    pointclouds = this.atDatasets 
+                    
+                    if(pointclouds.length == 0){
+                        let p = this.findClosestDataset(this.mainViewport.view.position)
+                        if(p) pointclouds = [p]
+                    }  
+                }
+                               
             }
+                           
              
             this.updateCadVisibles(pointclouds)
             //this.updatePanosVisibles(currentFloor/* , pointclouds */)
@@ -3421,7 +3423,7 @@ export class Viewer extends ViewerBase{
 			renderer.setClearColor(background, backgroundOpacity);
 		}
 		
-		params.target || renderer.clear();
+		/* params.target ||  */renderer.clear();
     
 	}
 
@@ -3576,19 +3578,20 @@ export class Viewer extends ViewerBase{
                 }else{
                     this.updateViewPointcloud(params.camera, viewport.resolution, true)  
                 } 
-                 
+                params.noOverlay && (params.dontRenderRtEDL = true)
                 pRenderer.render(params); //渲染点云 skybox。 
                 
-            } 
-                 
-            if(Potree.settings.notAdditiveBlending){ // 融合页面才用到
-                params.renderBeforeCloud = false
-                this.renderOverlay1(params) 
-                this.renderOverlay2(params)  
-            }else{ 
-               this.renderOverlay(params)                 
             }
-               
+            
+            if(!params.noOverlay){
+                if(Potree.settings.notAdditiveBlending){ // 融合页面才用到
+                    params.renderBeforeCloud = false
+                    this.renderOverlay1(params) 
+                    this.renderOverlay2(params)  
+                }else{ 
+                   this.renderOverlay(params)                 
+                }
+            }
             
                
                
@@ -3850,16 +3853,30 @@ export class Viewer extends ViewerBase{
             if(viewports.length > 0){   
                 
                 params.viewports = viewports  
-                if(this.outlinePass.selectedObjects.length && this.outlinePass.edgeStrength > 0 && !params.screenshot 
+                /*  if(this.outlinePass.selectedObjects.length && this.outlinePass.edgeStrength > 0 && !params.screenshot 
                    // || this.images360.fastTranMaskPass.enabled
                 ){  
-                    let scenes = this.inputHandler.interactiveScenes.concat(this.scene.scene).concat(viewer.scene.scenePointCloud) 
+                     let scenes = this.inputHandler.interactiveScenes.concat(this.scene.scene).concat(viewer.scene.scenePointCloud) 
                     this.composer.render(scenes, null, this.viewports, this.renderDefault.bind(this));  
-                    //this.composer.render(this.scene, this.mainViewport.camera, this.viewports, this.renderDefault.bind(this));  
-              
+                    
                 }else{  
                     await this.renderDefault(params);
-                }  
+                }   */
+                
+                
+                 if(this.aaComposer && !this.splatter?.visible && !params.target){
+                     this.aaComposer.render({ //补充抗锯齿,当splatter不可见时
+                        camera: this.mainViewport.camera, 
+                        //this.viewports, 
+                        renderParams: params,
+                        renderer: this.renderer,
+                        renderFun: this.renderDefault.bind(this)
+                     });
+                 }else{
+                    await this.renderDefault(params);
+                 }
+                 
+                
                
                 {
                     let marks = performance.getEntriesByName("loop-start")
@@ -3939,7 +3956,7 @@ export class Viewer extends ViewerBase{
                     //直接渲染 会改变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
+                    await viewerMaster.render({  screenshot : true,   width , height,   resize :true,   noOverlay: info.noOverlay, 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
@@ -3954,7 +3971,7 @@ export class Viewer extends ViewerBase{
             let dataUrl
             if(info.splitRenderInfo){//为了防崩溃,分区域批次渲染
                 let {wc , hc} = info.splitRenderInfo
-                let camera = viewer.mainViewport.camera
+                let camera = 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在此窗口大小下
@@ -3972,18 +3989,12 @@ export class Viewer extends ViewerBase{
                 camera.clearViewOffset()
                 
                 dataUrl = await Utils.combineImgs(dataUrls, compressRatio, width, height) 
-                 
+                //viewer.viewports = [ mainViewport]
             }else{
                 dataUrl = await render() 
-            }
-            
-             
-            
+            } 
             
             
-            if(info.splitRenderInfo){
-                viewer.viewports = [viewer.mainViewport]
-            }
             
             if(!Potree.settings.isOfficial){
                 Common.downloadFile(dataUrl, 'screenshot.png') 
@@ -4119,7 +4130,7 @@ export class Viewer extends ViewerBase{
         
         
         let mapViewport 
-        let mainViewport = this.mainViewport
+        let mainViewport = info.mainViewport || this.mainViewport
         let viewports = [],  oldStates = {
             viewports:[],
             pano: Potree.settings.displayMode == 'showPanos' ? viewer.images360.currentPano : null,
@@ -4202,9 +4213,9 @@ export class Viewer extends ViewerBase{
                         console.log('waitMap: '+startTime, Date.now(), this.mapViewer.mapLayer.loadingInProgress )  
                         this.mapViewer.waitLoadDone(screenshot.bind(this))//等待地图所有加载完 
                     }   
-                    this.waitPointLoad(waitMap,info.maxTimeForPointLoad) 
+                    this.waitPointLoad(waitMap, info.maxTimeForPointLoad) 
                 }else{  
-                    this.waitPointLoad(screenshot,info.maxTimeForPointLoad)
+                    this.waitPointLoad(screenshot, info.maxTimeForPointLoad)
                 } 
             }
             
@@ -4294,14 +4305,14 @@ export class Viewer extends ViewerBase{
             })
          
              
-        }else{//default
+        }else{//default 目前只支持单个viewport
         
             let done = ()=>{
                 updateCamera()
                 if(info.splitRenderInfo){ 
                     screenshot()
                 }else{
-                    this.waitPointLoad(screenshot, info.maxTimeForPointLoad)
+                    this.waitPointLoad(screenshot, info.maxTimeForPointLoad, mainViewport)
                 }
             }
             viewer.updateScreenSize({forceUpdateSize:true, width, height, forTarget:info.useRenderTarget}) //更新viewports相机透视 使focusOnObject在此窗口大小下
@@ -4412,15 +4423,16 @@ export class Viewer extends ViewerBase{
         let target  = new THREE.Vector3,  //相机focus的位置
             position = new THREE.Vector3, //相机最终位置
             dis;                          //相机距离目标
-        duration = duration == void 0 ? 1200 : duration;      
-        let camera = o.endCamera || viewer.scene.getActiveCamera()
+        duration = duration == void 0 ? 1200 : duration; 
+        let viewport = o.viewport || this.mainViewport         
+        let camera = o.endCamera || viewport.camera  
         let cameraPos = camera.position.clone()
         let boundSize   
         
         
         if(o.dontChangeCamDir && (o.endYaw == void 0 || o.endPitch == void 0)){ //在俯视时仅靠dir来算不准
-            o.endYaw = viewer.mainViewport.view.yaw
-            o.endPitch = viewer.mainViewport.view.pitch
+            o.endYaw = viewport.view.yaw
+            o.endPitch = viewport.view.pitch
         }
         
         
@@ -4530,7 +4542,7 @@ export class Viewer extends ViewerBase{
             //获得相机最佳位置
             let dir 
             if(o.dontChangeCamDir){
-                dir = viewer.mainViewport.view.direction.negate()
+                dir = viewport.view.direction.negate()
             }else{
                 dir = new THREE.Vector3().subVectors(cameraPos, target).normalize()  
             }
@@ -4773,7 +4785,7 @@ export class Viewer extends ViewerBase{
                         dis = bestDistance
                     }
                     
-                    let dir_ = o.direction ? o.direction.clone().negate() :  this.mainViewport.view.direction.negate()// */new THREE.Vector3().subVectors(camera.position, target).normalize() 
+                    let dir_ = o.direction ? o.direction.clone().negate() : viewport.view.direction.negate()// */new THREE.Vector3().subVectors(camera.position, target).normalize() 
                     if(o.dontLookUp && dir_.z<0)  dir_.z *= -1
                     position.copy(target).add(dir_.multiplyScalar(dis))
                 } 
@@ -4815,7 +4827,7 @@ export class Viewer extends ViewerBase{
             }else{
                 let pano = viewer.images360.fitPanoTowardPoint({
                     point : target,    
-                    dir :  this.mainViewport.view.direction, //尽量不改相机方向,避免镜头晃动
+                    dir :  viewport.view.direction, //尽量不改相机方向,避免镜头晃动
                     checkIntersect: o.checkIntersect, sameFloor:o.sameFloor,
                     bestDistance,   maxDis:  o.maxDis   //越近越好,但不要太近,bestDistance左右差不多
                 })
@@ -4866,7 +4878,7 @@ export class Viewer extends ViewerBase{
 
  
         if(o.startCamera && o.endCamera){
-            viewer.mainViewport.view.tranCamera(this.mainViewport,  { endPosition:position, target ,
+            viewport.view.tranCamera(viewport,  { endPosition:position, target ,
                 boundSize, 
                 callback:()=>{
                     //console.log('focusOnObjectSuccess: '+object.name,  type)
@@ -4875,7 +4887,7 @@ export class Viewer extends ViewerBase{
                 endYaw:o.endYaw,  endPitch:o.endPitch
             }, duration) 
         }else if(camera.type == "OrthographicCamera"){   
-            viewer.mainViewport.view.moveOrthoCamera(this.mainViewport,  { endPosition:position, target ,
+            viewport.view.moveOrthoCamera(viewport,  { endPosition:position, target ,
                 boundSize, 
                 endYaw:o.endYaw,  endPitch:o.endPitch,
                 callback:()=>{
@@ -4884,7 +4896,7 @@ export class Viewer extends ViewerBase{
                 }, 
             }, duration)
         }else{ 
-            viewer.mainViewport.view.setView({position, target, duration,
+            viewport.view.setView({position, target, duration,
                 endYaw:o.endYaw,  endPitch:o.endPitch,
                 callback:()=>{
                     //console.log('focusOnObjectSuccess: '+object.name,  type)
@@ -5001,7 +5013,14 @@ export class Viewer extends ViewerBase{
     }
     
 
-
+    findClosestDataset(position){
+        let pointclouds = viewer.scene.pointclouds.filter(e=>e.visible);
+   
+        let r = Potree.Common.sortByScore(pointclouds, [], [(e)=>{//pos2d和bound2d距离排序
+            return - (e.panosBound ? e.panosBound.bounding : e.bound).distanceToPoint(position)
+        }]);
+        return r[0] && r[0].item
+    }
 
  
     addTimeMark(name, type){

+ 6 - 7
src/custom/viewer/map/MapViewer.js

@@ -768,22 +768,21 @@ export class MapViewer extends ViewerBase{
         renderer.clearDepth(); //防止地图遮挡其他物体  
          
        
-        
-         //绘制其他物体
+ 
+        //绘制其他物体
         let layers = ['mapObjects'  , 'bothMapAndScene',    'light'  ]
         Potree.settings.showObjectsOnMap && layers.push('model')
-        Potree.Utils.setCameraLayers(this.camera, layers)
-        
-        
-        
+        Potree.Utils.setCameraLayers(this.camera, layers,  params.extraEnableLayers || this.viewports[0].extraEnableLayers)
+     
         viewer.dispatchEvent({type: "render.begin",  viewer: this, viewport:this.viewports[0], params }); 
         
         this.attachedToViewer || this.renderCloud || renderer.render(viewer.scene.scene, this.camera); //类同renderOverlay
         renderer.render(this.scene, this.camera);
+        
         if(!this.attachedToViewer && this.renderMeasure){//在未attach到主页面时也要渲染测量线
             viewer.dispatchEvent({type: "render.pass.perspective_overlay", camera:this.camera, viewport:this.viewports[0], renderer});
         }
-               
+            
            
         renderer.setRenderTarget(null)
     

+ 3 - 3
src/custom/viewer/viewerBase.js

@@ -208,7 +208,7 @@ console.log('antialias',args.antialias)
         
         
         if(!onlyForTarget){//因为onlyForTarget不传递devicePixelRatio所以不发送了 
-            this.dispatchEvent('viewerResize')
+            this.dispatchEvent( 'viewerResize', {width, height, devicePixelRatio})
             this.viewports.forEach(e=>{
                 this.ifEmitResize({viewport:e,  deviceRatio:devicePixelRatio})
             })
@@ -283,8 +283,8 @@ console.log('antialias',args.antialias)
             resolution: new THREE.Vector2(width,height), 
         });*/
        
-		let target = new THREE.WebGLRenderTarget(width, height, {
-			format: THREE.RGBAFormat,
+		let target = new THREE.WebGLMultisampleRenderTarget(width, height, {
+			format: THREE.RGBAFormat 
 		});
  
 	 

+ 5 - 5
src/navigation/InputHandlerNew.js

@@ -1301,7 +1301,7 @@ export class InputHandler extends THREE.EventDispatcher {
 
 
 	startDragging (object, args = null) {
-
+        
 		let name = object ? object.name : "no name";
 		if (this.logMessages) console.log(`${this.constructor.name}: startDragging: '${name}'`);
 
@@ -1329,7 +1329,7 @@ export class InputHandler extends THREE.EventDispatcher {
                 }
             )); 
         }
-		 
+		//object && console.log('startDragging', object.name) 
 	}
 
 	/* getMousePointCloudIntersection (mouse) {
@@ -1447,7 +1447,7 @@ export class InputHandler extends THREE.EventDispatcher {
      
  
 	getHoveredElements (interactables, dontCheckDis, raycaster) { 
-		
+        viewer.dispatchEvent( {type:'raycaster1',  viewport: this.hoverViewport})//add
         if(!interactables){ 
             let scenes = this.hoverViewport.interactiveScenes || this.interactiveScenes.concat(this.scene);
 
@@ -1536,8 +1536,8 @@ export class InputHandler extends THREE.EventDispatcher {
          
         //intersections = intersections.sort(function(a,b){return b.object.renderOrder-a.object.renderOrder}) // 降序
         intersections = intersections.sort(function(a,b){
-            let order2 = b.object.pickOrder || 0
-            let order1 = a.object.pickOrder || 0
+            let order2 = b.oriObject.pickOrder || 0         //由object改为oriObject,因为可能同一个object检测到多个oriObject
+            let order1 = a.oriObject.pickOrder || 0
             return order2-order1
         }) // 降序
         

+ 251 - 207
src/utils/TransformationToolNew.js

@@ -6,13 +6,13 @@ import {LineDraw/* , MeshDraw */} from "../custom/utils/DrawUtil.js";
 import History from "../custom/utils/History.js"
 //问题:如何转换到世界坐标?(缩放方向有bug。)
 
-//add-------------------------------------
+ 
 const OpaWhenNotSelect = 0.6
 const ScaleRatio =  3
 const OutlineColor = 0x666666
-//----------------------------------------
-const hideFocusHandles = true//add
-
+const showPickVolumes = false  
+const hideFocusHandles = true 
+let lastUseCamera
 
 export class TransformationTool extends THREE.EventDispatcher{
 	constructor(viewer  ) {
@@ -24,8 +24,8 @@ export class TransformationTool extends THREE.EventDispatcher{
 		this.scene = new THREE.Scene(); 
 		this.selection = [];
 		this.pivot = new THREE.Vector3();
-		this.dragging = false;
-		this.showPickVolumes = false;
+		this.draggingAt = false;
+		this.showPickVolumes = showPickVolumes;
 
 		this.viewer.inputHandler.registerInteractiveScene(this.scene);
 		this.viewer.inputHandler.addEventListener('selection_changed', (e) => {
@@ -40,15 +40,25 @@ export class TransformationTool extends THREE.EventDispatcher{
 			} */
 
 		}); 
-
-
+        
+        /* this.viewer.addEventListener('global_mousedown',(e)=>{ //add 为了支持多viewport自己触发drag,否则要在getHoverelement时onBeforeRender下
+             if(this.activeHandle){ 
+                 viewer.inputHandler.startDragging(this.intersect.object,  {location: this.intersect.point}) 
+             }
+        },{importance:10})   */
+        
+        this.viewer.addEventListener("raycaster1", (e)=>{
+            this.restoreDisplay(e.viewport.camera)
+        }) 
+        
         this.viewer.addEventListener('global_touchstart',(e)=>{ //add
             this.update()
         })
         this.viewer.addEventListener('global_mousemove',(e)=>{ //add
-            this.onPointerMove()
+            this.onPointerMove(e)
         })
-
+        
+        
 
 		let red = Potree.config.axis.x.color
 		let green = Potree.config.axis.y.color
@@ -137,6 +147,171 @@ export class TransformationTool extends THREE.EventDispatcher{
         
         
         //------------------add-----------------------
+       
+        this.scene.autoUpdate = false //执行onBeforeRender后再计算
+        this.scene.onBeforeRender = (camera_)=>{
+        
+            if(!this.scene.visible || !this.selection[0])return
+            //let camera = viewer.modules.Clip.orthoCamera || viewer.mainViewport.camera
+            let camera = camera_?.isCamera ? camera_ : Potree.currentRender?.camera
+            //console.log('onBeforeRender', camera_.isCamera ? '_byPick':'' , camera.type == 'OrthographicCamera')
+            let viewSize = viewer.renderer.getViewport(new THREE.Vector4) 
+            let domWidth = viewSize.z, domHeight = viewSize.w 
+            let center = this.scene.position
+            
+            lastUseCamera = camera
+            
+         
+            // adjust rotation handles
+            if(!this.draggingAt){               //如果要实现拖拽的那个viewport不变可见性的话,不能只这么写this.draggingAt?.camera != camera,这样会被不在拖拽的覆盖,可能需要在updateVisible的reason中指明viewport
+                if(this.modesEnabled.rotation || this.modesEnabled.translation){
+                    let tWorld = this.scene.matrixWorld;
+                    let tObject = tWorld.clone().invert();
+                    let camObjectPos = camera.getWorldPosition(new THREE.Vector3()).applyMatrix4(tObject);
+
+                    
+                    if(this.modesEnabled.translation){//add
+                        ['xy','yz','xz'].forEach(axis=>{
+                            let handle = this.translationHandles["translation.plane."+axis] 
+                            let pos = handle.node.children[0].position  
+                            camObjectPos.x && (pos.x = Math.sign(camObjectPos.x) * Math.abs(pos.x))
+                            camObjectPos.y && (pos.y = Math.sign(camObjectPos.y) * Math.abs(pos.y))
+                            camObjectPos.z && (pos.z = Math.sign(camObjectPos.z) * Math.abs(pos.z))
+                             
+                        })
+                    }
+                    
+                    if(this.modesEnabled.rotation){
+                        let above = camObjectPos.z > 0;
+                        let below = !above;
+                        let PI_HALF = Math.PI / 2;
+                        
+                        let x = this.rotationHandles["rotation.x"].node.rotation;
+                        let y = this.rotationHandles["rotation.y"].node.rotation;
+                        let z = this.rotationHandles["rotation.z"].node.rotation; 
+                        x.order = "ZYX";
+                        y.order = "ZYX";
+
+                      
+                        if(above){
+                            if(camObjectPos.x > 0 && camObjectPos.y > 0){
+                                x.x = 1 * PI_HALF;
+                                y.y = 3 * PI_HALF;
+                                z.z = 0 * PI_HALF; 
+                            }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
+                                x.x = 1 * PI_HALF;
+                                y.y = 2 * PI_HALF;
+                                z.z = 1 * PI_HALF;
+                            }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
+                                x.x = 2 * PI_HALF;
+                                y.y = 2 * PI_HALF;
+                                z.z = 2 * PI_HALF;
+                            }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
+                                x.x = 2 * PI_HALF;
+                                y.y = 3 * PI_HALF;
+                                z.z = 3 * PI_HALF;
+                            } 
+                        }else if(below){
+                            if(camObjectPos.x > 0 && camObjectPos.y > 0){
+                                x.x = 0 * PI_HALF;
+                                y.y = 0 * PI_HALF;
+                                z.z = 0 * PI_HALF;
+                            }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
+                                x.x = 0 * PI_HALF;
+                                y.y = 1 * PI_HALF;
+                                z.z = 1 * PI_HALF;
+                            }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
+                                x.x = 3 * PI_HALF;
+                                y.y = 1 * PI_HALF;
+                                z.z = 2 * PI_HALF;
+                            }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
+                                x.x = 3 * PI_HALF;
+                                y.y = 0 * PI_HALF;
+                                z.z = 3 * PI_HALF;
+                            } 
+                            
+                        }  
+                    } 
+                }
+            }
+
+
+            // adjust scale of components
+            for(let handleName of Object.keys(this.handles)){
+                let handle = this.handles[handleName];
+                let node = handle.node; 
+                
+                
+                //xzw add:---- -当该轴正对相机时隐藏。(主要针对ortho类型camera。 
+                if(!Potree.Utils.getObjVisiByReason(node,'modeForce')  )continue;   
+                
+                let alignment = handle.alignment;
+                if(alignment && (!handleName.includes('rotation') || camera.type == 'OrthographicCamera')){//旋转的话正常都应该显示
+                    
+                    let normal
+                    let dir = new THREE.Vector3(...alignment).applyQuaternion(this.scene.quaternion)
+                    if(camera.type == 'OrthographicCamera'){ 
+                        normal = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion)
+                    }else{ 
+                        normal = new THREE.Vector3().subVectors(center, camera.position).normalize()
+                    } 
+                    let ifOnLine
+                    
+                    if(handleName.includes('rotation') || handleName.includes('plane')){ // 旋转轴和视线垂直时隐藏
+                        ifOnLine = Math.abs(dir.dot(normal)) < 0.1
+                    }else{
+                        ifOnLine = Math.abs(dir.dot(normal)) > 0.995
+                    }
+                    
+                    Potree.Utils.updateVisible(node, 'faceToCamHide', !ifOnLine)
+                }else{
+                    Potree.Utils.updateVisible(node, 'faceToCamHide', true)
+                }      
+                 
+                if(!node.visible)continue;   
+                
+                //------------------------------------------------------------------------
+                if(handle.dontScale)continue; //add
+                let handlePos = node.getWorldPosition(new THREE.Vector3());
+                let distance = handlePos.distanceTo(camera.position);
+                let pr = Utils.projectedRadius(1, camera, distance, domWidth, domHeight/* domElement.clientWidth, domElement.clientHeight */);
+
+                let ws = node.parent.getWorldScale(new THREE.Vector3());
+
+                let s = (ScaleRatio / pr);
+                let scale = new THREE.Vector3(s, s, s).divide(ws);
+            
+                let rot = new THREE.Matrix4().makeRotationFromEuler(node.rotation);     //需要使用到旋转,所以我把设置scale的移到旋转后了,否则在视图上下旋转的分界线处rotateHandel会被拉长从而闪烁。
+                let rotInv = rot.clone().invert();
+            
+                scale.applyMatrix4(rotInv);
+                scale.x = Math.abs(scale.x);
+                scale.y = Math.abs(scale.y);
+                scale.z = Math.abs(scale.z);
+
+                node.scale.copy(scale);  
+            }   
+          
+
+            this.scene.updateMatrixWorld() //add    更新全部  
+
+
+            /* for(let handleName of Object.keys(this.handles)){
+                let handle = this.handles[handleName];
+                let node = handle.node; 
+                
+                if(!handle.displayMap)handle.displayMap = new Map
+                handle.displayMap.set(camera, {matrix: node.matrixWorld.clone(), visible:node.visible})
+                
+            } */
+            this.scene.traverse(object=>{
+                if(!object.displayMap)object.displayMap = new Map
+                object.displayMap.set(camera, {matrix: object.matrixWorld.clone(), visible:object.visible})
+            })
+            
+        }//end-----
+        
+        
         
         this.setModeEnable(['scale','translation','rotation'])
         Potree.Utils.setObjectLayers(this.scene, 'transformationTool' )
@@ -189,7 +364,7 @@ export class TransformationTool extends THREE.EventDispatcher{
         }
 	}
   
-  
+    
     setModeEnable(enableModes=[] ){//xzw add
        
         let length=0;
@@ -247,7 +422,25 @@ export class TransformationTool extends THREE.EventDispatcher{
         this.style = style
     } 
     
-    
+    restoreDisplay(camera){
+        //camera!=lastUseCamera && this.scene.onBeforeRender(camera)
+        /* for(let handleName of Object.keys(this.handles)){
+            let handle = this.handles[handleName];
+            let lastState = handle.displayMap?.get(camera)
+            if(lastState){
+                handle.node.matrixWorld.copy(lastState.matrix)
+                handle.node.visible = lastState.visible
+            }
+        }   */
+        if(!this.scene.visible)return
+        this.scene.traverse(object=>{
+            let lastState = object.displayMap?.get(camera)
+            if(lastState){ 
+                object.matrixWorld.copy(lastState.matrix)
+                object.visible = lastState.visible
+            }
+        })
+    }
     
     
 	initializeTranslationHandles(){//大改
@@ -695,12 +888,12 @@ export class TransformationTool extends THREE.EventDispatcher{
 	dragRotationHandle(e){
 		let drag = e.drag;
 		let handle = this.activeHandle;
-		let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
+        let camera = e.dragViewport.camera
 
 		if(!handle || !handle.name.includes('rotation') ){
 			return
 		};
-
+        
 		let localNormal = new THREE.Vector3(...handle.alignment);
 		let n = new THREE.Vector3();
 		n.copy(new THREE.Vector4(...localNormal.toArray(), 0).applyMatrix4(handle.node.matrixWorld));
@@ -716,6 +909,7 @@ export class TransformationTool extends THREE.EventDispatcher{
 			//Utils.debugLine(this.debug, drag.location, debugEnd, 0xff0000);
 
 			drag.intersectionStart = drag.location;
+            console.log('intersectionStart',drag.location.toArray())
 			drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3());
 			drag.handle = handle;
 
@@ -727,11 +921,11 @@ export class TransformationTool extends THREE.EventDispatcher{
 			handle = drag.handle;
 		}
         if(!drag.dragPlane)return//xzw add 因有时候没有
-		this.dragging = true;
+		this.draggingAt = e.dragViewport;
 
 		let pointer = this.viewer.inputHandler.pointer
-		let domElement = this.viewer.renderer.domElement;
-		let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
+		//let domElement = this.viewer.renderer.domElement;
+		let ray = Utils.mouseToRay(pointer, camera, e.dragViewport.resolution.x,  e.dragViewport.resolution.y/* domElement.clientWidth, domElement.clientHeight */);
 		
 		let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3());
 
@@ -750,6 +944,7 @@ export class TransformationTool extends THREE.EventDispatcher{
 			if (Number.isNaN(angle)) {
 				return;
 			}
+            
             let matrixBefore = this.selection[0].matrix.clone()
 			let normal = new THREE.Vector3(...handle.alignment);
 			for (let selection of this.selection) { 
@@ -784,7 +979,7 @@ export class TransformationTool extends THREE.EventDispatcher{
 	}
 
 	dropRotationHandle(e){
-		this.dragging = false;
+		this.draggingAt = false;
 		this.setActiveHandle(null);
         this.dispatchEvent({type:'stopDrag', handle:'rotation'})//add
          
@@ -793,7 +988,8 @@ export class TransformationTool extends THREE.EventDispatcher{
 	dragTranslationHandle(e){//---大改,参考transformControls,为了加上xyz xy yz xz 这四个方向的变换。 (但感觉好像plane上有丢丢延迟?是因为drag延迟还是worldmatrix没更新)
 		let drag = e.drag;
 		let handle = this.activeHandle;
-		let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
+        let viewport = e.dragViewport
+		let camera = viewport.camera
 		
         if(handle && handle.name.includes('translation') && this.selection[0]){
         
@@ -806,7 +1002,7 @@ export class TransformationTool extends THREE.EventDispatcher{
                  
                 drag.objectQua = this.selection[0].quaternion.clone()//不考虑父级
                 drag.objectQuaInv = drag.objectQua.clone().invert()
-                this.dragging = true
+                this.draggingAt = viewport
             }
 
             if(drag.intersectionStart){ 
@@ -817,7 +1013,7 @@ export class TransformationTool extends THREE.EventDispatcher{
                 let axisName = handle.name.split('.').pop()
                 
                 if(axisName == 'xyz'){ // 平行于屏幕滑动
-                    normal = viewer.mainViewport.view.direction
+                    normal = viewport.view.direction
                 }else{
                     var alignVector = new THREE.Vector3();
                     normal = new THREE.Vector3
@@ -828,15 +1024,15 @@ export class TransformationTool extends THREE.EventDispatcher{
                     
                     switch (axisName) {  
                         case 'x':
-                            alignVector.copy( viewer.mainViewport.view.direction ).cross( unitX );
+                            alignVector.copy( viewport.view.direction ).cross( unitX );
                             normal.copy( unitX ).cross( alignVector );
                             break;
                         case 'y':
-                            alignVector.copy( viewer.mainViewport.view.direction ).cross( unitY );
+                            alignVector.copy( viewport.view.direction ).cross( unitY );
                             normal.copy( unitY ).cross( alignVector );
                             break;
                         case 'z':
-                            alignVector.copy( viewer.mainViewport.view.direction ).cross( unitZ );
+                            alignVector.copy( viewport.view.direction ).cross( unitZ );
                             normal.copy( unitZ ).cross( alignVector );
                             break;
                         case 'xy':
@@ -899,14 +1095,14 @@ export class TransformationTool extends THREE.EventDispatcher{
 
     
 	dropTranslationHandle(e){
-		this.dragging = false;
+		this.draggingAt = false;
 		this.setActiveHandle(null);
         this.dispatchEvent({type:'stopDrag', handle:'translation'})//add
          
 	}
 
 	dropScaleHandle(e){
-		this.dragging = false;
+		this.draggingAt = false;
 		this.setActiveHandle(null);
         this.dispatchEvent({type:'stopDrag', handle:'scale', object: e.drag.object})//add
         
@@ -916,7 +1112,7 @@ export class TransformationTool extends THREE.EventDispatcher{
 		let drag = e.drag;
 		let handle = this.activeHandle;
         if(!handle || !handle.name.includes('scale'))return
-		let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
+		let camera = e.dragViewport.camera 
 
 		if(!drag.intersectionStart){
 			drag.intersectionStart = drag.location;
@@ -944,12 +1140,12 @@ export class TransformationTool extends THREE.EventDispatcher{
 			handle = drag.handle;
 		}
 
-		this.dragging = true;
+		this.draggingAt = e.dragViewport;
 
 		if(drag.dragPlane){//xzw add 因有时候没有
 			let pointer = this.viewer.inputHandler.pointer
-			let domElement = this.viewer.renderer.domElement;
-			let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
+			//let domElement = this.viewer.renderer.domElement;
+			let ray = Utils.mouseToRay(pointer, camera,  e.dragViewport.resolution.x, e.dragViewport.resolution.y/* domElement.clientWidth, domElement.clientHeight */);
 			let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3());
 
 			if (I) {
@@ -1019,17 +1215,18 @@ export class TransformationTool extends THREE.EventDispatcher{
         
 	}
  
-	setActiveHandle(handle){
-		if(this.dragging){
+	setActiveHandle(handle, intersect){
+        
+		if(this.draggingAt){
 			return;
 		}
-
+        this.intersect = intersect;//xzw add
 		if(this.activeHandle === handle){
 			return;
 		}
 
 		this.activeHandle = handle;
-
+        
 		if(handle === null){
 			for(let handleName of Object.keys(this.handles)){
 				let handle = this.handles[handleName];
@@ -1110,32 +1307,19 @@ export class TransformationTool extends THREE.EventDispatcher{
 		viewer.dispatchEvent('content_changed') 
 	}
 
-	update () {
-
-		if(this.selection.length === 1){ 
-			this.scene.visible = true;
-
-			this.scene.updateMatrix();
-			this.scene.updateMatrixWorld();
-
-                 
-			let selected = this.selection[0]; 
-            //selected.updateMatrixWorld();//add 否则scale的sphere抖动
+    update(){ 
+        this.scene.visible = this.selection.length === 1
+        if(this.scene.visible){
+            let selected = this.selection[0];  
+            let world = selected.matrixWorld;
             
-			let world = selected.matrixWorld;
-			let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
-			let domElement = this.viewer.renderer.domElement;
-
-            
-        
-			let center = selected.boundingBox.getCenter(new THREE.Vector3()).clone().applyMatrix4(selected.matrixWorld);
+            let center = selected.boundingBox.getCenter(new THREE.Vector3()).clone().applyMatrix4(selected.matrixWorld);
             let scale = selected.boundingBox.getSize(new THREE.Vector3()).multiply(selected.scale)
             scale.max(new THREE.Vector3(.1,.1,.1)) //xzw add 如果是plane,没有厚度,会导致该tool消失
-            
-			this.scene.scale.copy(scale);
-			this.scene.position.copy(center);
-			this.scene.rotation.copy(selected.rotation);    //这里只考虑当前子级的scale rotation
-            
+
+            this.scene.scale.copy(scale);
+            this.scene.position.copy(center);
+            this.scene.rotation.copy(selected.rotation);    
             //如果是世界坐标 (缩放方向有bug。)
             /* 
             let boundingBox = selected.boundingBox.clone().applyMatrix4(selected.matrixWorld);
@@ -1145,161 +1329,21 @@ export class TransformationTool extends THREE.EventDispatcher{
              
              */
             
-
-			this.scene.updateMatrixWorld();
-
-			{ 
-				// adjust rotation handles
-				if(!this.dragging){
-                    if(this.modesEnabled.rotation || this.modesEnabled.translation){
-                        let tWorld = this.scene.matrixWorld;
-                        let tObject = tWorld.clone().invert();
-                        let camObjectPos = camera.getWorldPosition(new THREE.Vector3()).applyMatrix4(tObject);
-
-                        
-                        
-                        if(this.modesEnabled.translation){//add
-                            ['xy','yz','xz'].forEach(axis=>{
-                                let handle = this.translationHandles["translation.plane."+axis] 
-                                let pos = handle.node.children[0].position  
-                                camObjectPos.x && (pos.x = Math.sign(camObjectPos.x) * Math.abs(pos.x))
-                                camObjectPos.y && (pos.y = Math.sign(camObjectPos.y) * Math.abs(pos.y))
-                                camObjectPos.z && (pos.z = Math.sign(camObjectPos.z) * Math.abs(pos.z))
-                                 
-                            })
-                        }
-                        
-                        if(this.modesEnabled.rotation){
-                            let above = camObjectPos.z > 0;
-                            let below = !above;
-                            let PI_HALF = Math.PI / 2;
-                            
-                            let x = this.rotationHandles["rotation.x"].node.rotation;
-                            let y = this.rotationHandles["rotation.y"].node.rotation;
-                            let z = this.rotationHandles["rotation.z"].node.rotation; 
-                            x.order = "ZYX";
-                            y.order = "ZYX";
-
-                          
-                            if(above){
-                                if(camObjectPos.x > 0 && camObjectPos.y > 0){
-                                    x.x = 1 * PI_HALF;
-                                    y.y = 3 * PI_HALF;
-                                    z.z = 0 * PI_HALF; 
-                                }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
-                                    x.x = 1 * PI_HALF;
-                                    y.y = 2 * PI_HALF;
-                                    z.z = 1 * PI_HALF;
-                                }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
-                                    x.x = 2 * PI_HALF;
-                                    y.y = 2 * PI_HALF;
-                                    z.z = 2 * PI_HALF;
-                                }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
-                                    x.x = 2 * PI_HALF;
-                                    y.y = 3 * PI_HALF;
-                                    z.z = 3 * PI_HALF;
-                                } 
-                            }else if(below){
-                                if(camObjectPos.x > 0 && camObjectPos.y > 0){
-                                    x.x = 0 * PI_HALF;
-                                    y.y = 0 * PI_HALF;
-                                    z.z = 0 * PI_HALF;
-                                }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
-                                    x.x = 0 * PI_HALF;
-                                    y.y = 1 * PI_HALF;
-                                    z.z = 1 * PI_HALF;
-                                }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
-                                    x.x = 3 * PI_HALF;
-                                    y.y = 1 * PI_HALF;
-                                    z.z = 2 * PI_HALF;
-                                }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
-                                    x.x = 3 * PI_HALF;
-                                    y.y = 0 * PI_HALF;
-                                    z.z = 3 * PI_HALF;
-                                } 
-                                
-                            }  
-                        } 
-                    }
-				}
-
-
-                // adjust scale of components
-				for(let handleName of Object.keys(this.handles)){
-					let handle = this.handles[handleName];
-					let node = handle.node; 
-                    
-                    
-                    //xzw add:---- -当该轴正对相机时隐藏。(主要针对ortho类型camera。 
-                    if(!Potree.Utils.getObjVisiByReason(node,'modeForce')  )continue;   
-                    
-                    let alignment = handle.alignment;
-                    if(alignment && (!handleName.includes('rotation') || camera.type == 'OrthographicCamera')){//旋转的话正常都应该显示
-                        
-                        let normal
-                        let dir = new THREE.Vector3(...alignment).applyQuaternion(this.scene.quaternion)
-                        if(camera.type == 'OrthographicCamera'){ 
-                            normal = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion)
-                        }else{ 
-                            normal = new THREE.Vector3().subVectors(center, camera.position).normalize()
-                        } 
-                        let ifOnLine
-                        
-                        if(handleName.includes('rotation') || handleName.includes('plane')){ // 旋转轴和视线垂直时隐藏
-                            ifOnLine = Math.abs(dir.dot(normal)) < 0.1
-                        }else{
-                            ifOnLine = Math.abs(dir.dot(normal)) > 0.995
-                        }
-                        
-                        Potree.Utils.updateVisible(node, 'faceToCamHide', !ifOnLine)
-                    }else{
-                        Potree.Utils.updateVisible(node, 'faceToCamHide', true)
-                    }      
-                     
-                    if(!node.visible)continue;   
-                    
-                    //------------------------------------------------------------------------
-                    if(handle.dontScale)continue; //add
-					let handlePos = node.getWorldPosition(new THREE.Vector3());
-					let distance = handlePos.distanceTo(camera.position);
-					let pr = Utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight);
-
-					let ws = node.parent.getWorldScale(new THREE.Vector3());
-
-					let s = (ScaleRatio / pr);
-					let scale = new THREE.Vector3(s, s, s).divide(ws);
-                
-					let rot = new THREE.Matrix4().makeRotationFromEuler(node.rotation);     //需要使用到旋转,所以我把设置scale的移到旋转后了,否则在视图上下旋转的分界线处rotateHandel会被拉长从而闪烁。
-					let rotInv = rot.clone().invert();
-                
-					scale.applyMatrix4(rotInv);
-					scale.x = Math.abs(scale.x);
-					scale.y = Math.abs(scale.y);
-					scale.z = Math.abs(scale.z);
-
-					node.scale.copy(scale);
-                     
-                    
-				}
- 
-				 
-			}
-
-		}else{
-			this.scene.visible = false;
-		}
-		
-	}
+        }
+    }
+    
 
 
-    onPointerMove(){
-        let pointer = this.viewer.inputHandler.pointer;
-        let camera = this.viewer.mainViewport.camera 
-        if( this.selection.length === 1 && !this.dragging ){ //xzw 添加dragging条件
+    onPointerMove(e){
+        let pointer = e.pointer;
+        let camera = e.hoverViewport.camera
+        if( this.selection.length === 1 && !this.draggingAt ){ //xzw 添加dragging条件
             let ray = Utils.mouseToRay(pointer, camera );
             let raycaster = new THREE.Raycaster(ray.origin, ray.direction);
             raycaster.layers.enableAll()//add
                   
+            //camera!=lastUseCamera && this.scene.onBeforeRender(camera)  //add 多viewport时重新根据hover的camera更新下
+            this.restoreDisplay(camera)//add
             
             let pickVolumes = this.pickVolumes.filter(v=>{
                 let mode = v.handle.split('.')[0]; 
@@ -1318,7 +1362,7 @@ export class TransformationTool extends THREE.EventDispatcher{
                 let handleName = I.object.handle;
                 //console.log(handleName)
               
-                this.setActiveHandle(this.handles[handleName]);
+                this.setActiveHandle(this.handles[handleName], I );
             }else{
                 this.setActiveHandle(null);
             }

+ 7 - 5
src/viewer/EDLRendererNew.js

@@ -121,24 +121,26 @@ export class EDLRenderer{//Eye-Dome Lighting 眼罩照明
 	}
  
  
-    getRtEDL(viewport){//根据不同viewport返回rtEDL的texture
+    getRtEDL(viewport, antialias=true){//根据不同viewport返回rtEDL的texture
         if(!viewport){
             console.warn('getRtEDL没传viewport!!!! !!!!!!!!!!')
             viewport = viewer.mainViewport
         }
         var rtEDL = this.rtEDLs.get(viewport)
         if(!rtEDL){
-            if(Features.EXT_DEPTH.isSupported()){ 
-                rtEDL = new THREE.WebGLRenderTarget(viewport.resolution.x, viewport.resolution.y, {
+            if(Features.EXT_DEPTH.isSupported()){
+                const RenderTarget = (antialias && Potree.settings.isWebgl2) ? 'WebGLMultisampleRenderTarget' : 'WebGLRenderTarget'  
+                rtEDL = new THREE[RenderTarget](viewport.resolution.x, viewport.resolution.y, {
                     minFilter: THREE.NearestFilter,
                     magFilter: THREE.NearestFilter,
                     format: THREE.RGBAFormat,
                     type: THREE.FloatType,
-                    depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType) 
+                    depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType),
+                    
                 }); 
                 //注: 部分手机在resize时会崩溃,经检验去掉rtEDL的resize可以解决,所以更应该注释掉这个
             
-                
+              
                 this.rtEDLs.set(viewport, rtEDL)
             }  
         } 

+ 64 - 14
src/viewer/ExtendView.js

@@ -21,44 +21,94 @@ class ExtendView extends View {
         this.sid = sid++
         this.LookTransition = 'LookTransition'+this.sid
         this.FlyTransition = 'FlyTransition'+this.sid
-
+        //this.freeQuaternion = new THREE.Quaternion
 	}
     //add------
     applyToCamera(camera){
         camera.position.copy(this.position);
-        camera.rotation.copy(this.rotation) 
-       
+         if(this.rollFree){ //不受相机旋转模式限定,可以歪着,任意角度
+            camera.quaternion.copy(this.quaternion) 
+        }else{ 
+            camera.rotation.copy(this.rotation) 
+        }
+         
          
         camera.updateMatrix();
         camera.updateMatrixWorld();
         //camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); 
+        
+        /* if(this.zoom != void 0 && this.zoom != camera.zoom && camera.type == 'OrthographicCamera'){ 
+            camera.updateProjectionMatrix()     //危险 这将不能直接在camera上更改zoom
+        } */
     }
     
     get rotation(){
         var rotation = new THREE.Euler;
         rotation.order = "ZXY";
         rotation.x = Math.PI / 2 + this.pitch;
-        rotation.z = this.yaw;
+        rotation.z = this.yaw
+        rotation.y = this.roll 
         return rotation
     }
      
     set rotation(rotation){ 
-        if(rotation.y != 0){//因为 rotation的y不一定是0 , 所以不能直接逆着get rotation写。 
-            //console.error('set rotation y不为0!!!!?', rotation ) //过渡时因为quaternion lerp所以不为0。没办法了orz
-            this.direction = new THREE.Vector3(0,0,-1).applyEuler(rotation)  //转回direction有损耗,在俯视时的(dir.x==dir.y==0), 丢失yaw信息从而 yaw无法获取(希望不要遇到这种情况,如果有的话,考虑先计算yaw,似乎好像可以算)
-        }else{//尽量不用direction
-            this.pitch = rotation.x - Math.PI / 2
+        //this.direction = new THREE.Vector3(0,0,-1).applyEuler(rotation)  
+        
+        if(this.rollFree){
+            if(rotation.order != 'ZXY'){
+                return this.quaternion = new THREE.Quaternion().setFromEuler(rotation)
+            } 
             this.yaw = rotation.z
-        } 
+            this.rollFree && (this.roll = rotation.y)  //add  一般是极小的数字
+            this.pitch = rotation.x - Math.PI / 2 
+        }else{
+            if(rotation.y != 0){//因为 rotation的y不一定是0 , 所以不能直接逆着get rotation写。 
+                //console.error('set rotation y不为0!!!!?', rotation ) //过渡时因为quaternion lerp所以不为0。没办法了orz
+                this.direction = new THREE.Vector3(0,0,-1).applyEuler(rotation)  //转回direction有损耗,在俯视时的(dir.x==dir.y==0), 丢失yaw信息从而 yaw无法获取(希望不要遇到这种情况,如果有的话,考虑先计算yaw,似乎好像可以算)
+            }else{
+                this.pitch = rotation.x - Math.PI / 2
+                this.yaw = rotation.z
+            }
+        }
+        /* let pitch = rotation.x - Math.PI / 2 
+        if (Math.abs(pitch) > Math.PI/2) { 
+            if(pitch > this.maxPitch){
+                pitch = Math.PI - pitch 
+            }else if(pitch < this.minPitch){
+                pitch = -Math.PI - pitch 
+            } 
+            //这两行不对,算了……只能去掉角度限制
+            this.yaw += Math.PI;  
+            this.roll = -this.roll;  
+        }
+        this.pitch = pitch */
+           
     }
     
-   
+    /* setRotMode(mode){
+        if(mode == 'free'){
+            this.freeQuaternion.copy(this.quaternion)
+        }
+        this.rotMode = mode
+    } */
+    setRollFree(state){
+        this.rollFree = state
+        //if(!state)this.roll = 0 下次生效
+    }
     get quaternion(){
-        return new THREE.Quaternion().setFromEuler(this.rotation)
+        /* if(this.rotMode == 'free'){
+            return this.freeQuaternion
+        }else{ */
+            return new THREE.Quaternion().setFromEuler(this.rotation)
+        //} 
     }
     
     set quaternion(q){ 
-        this.rotation = new THREE.Euler().setFromQuaternion(q)
+        /* if(this.rotMode == 'free'){
+            this.freeQuaternion.copy(q)
+        } */ 
+        this.rotation = new THREE.Euler().setFromQuaternion(q, this.rollFree && 'ZXY')
+         //不知为何非rollFree时不能用ZXY,会突然转向
     }
     
     copy(a){
@@ -244,7 +294,7 @@ class ExtendView extends View {
                 endQuaternion = info.quaternion.clone()
             }   
             
-            if(endQuaternion && math.closeTo(Math.abs(this.direction.z), 1, 1e-4)){ //在垂直的视角下的quaternion刚开始突变的厉害,这时候可能渐变yaw比较好(如俯视时点击测量线)
+            if(this.rotMode != 'free' && endQuaternion && math.closeTo(Math.abs(this.direction.z), 1, 1e-4)){ //在垂直的视角下的quaternion刚开始突变的厉害,这时候可能渐变yaw比较好(如俯视时点击测量线)
                 let a = this.clone();
                 a.quaternion = endQuaternion;
                 info.endYaw = a.yaw; info.endPitch = a.pitch;

+ 32 - 12
src/viewer/View.js

@@ -4,13 +4,17 @@ export class View{//base
 	constructor () {
 		this.position = new THREE.Vector3(0, 0, 0);
 
-		this.yaw = Math.PI / 4;
-		this._pitch = -Math.PI / 4;
+		this.yaw = Math.PI / 4; //偏航角
+		this._pitch = -Math.PI / 4;//俯仰角
+        this.roll = 0 //滚转角 歪头  xzw add
+        
 		this.radius = 1;
 
 		this.maxPitch = Math.PI / 2;
 		this.minPitch = -Math.PI / 2;
 	}
+    
+    
 
 	clone () {
 		let c = new View();
@@ -22,22 +26,38 @@ export class View{//base
 
 		return c;
 	}
-
+    
+    
+    /* get yaw(){
+        return this._yaw
+    }
+    set yaw(v){  
+        this._yaw = v 
+    } */
+    
 	get pitch () {
 		return this._pitch;
 	}
 
 	set pitch (angle) {
-		this._pitch = Math.max(Math.min(angle, this.maxPitch), this.minPitch);
+        if(!this.rollFree){
+            this._pitch = Math.max(Math.min(angle, this.maxPitch), this.minPitch);
+        }else{
+            this._pitch = angle
+        }
 	}
 
 	get direction () {
-		let dir = new THREE.Vector3(0, 1, 0);
-
-		dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch);
-		dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
-
-		return dir;
+        if(this.rollFree){//xzw add 不知为啥当roll不为0时必须这么算才对,好复杂。可能roll也影响了另外两个
+            return new THREE.Vector3(0,0,-1).applyQuaternion(this.quaternion)
+        }else{
+            let dir = new THREE.Vector3(0, 1, 0);
+
+            dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch);
+            dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
+            //不考虑roll,因滚转角不影响视线方向向量。 所以根据direction也无法获知roll信息。甚至当xy都为0时无法获知yaw
+            return dir;
+        }
 	}
 
 	set direction (dir) {
@@ -53,10 +73,10 @@ export class View{//base
 
 			this.yaw = yaw;
 			this.pitch = pitch;
-		}
-		
+		} 
 	}
 
+
 	lookAt(t){//setPivot 
 		let V;
 		if(arguments.length === 1){

+ 6 - 0
src/viewer/potree.css

@@ -176,6 +176,7 @@
 	transition: left .35s;
 }
 
+    
 .potree-panel {
 	border: 		1px solid black;
 	border-radius: 	0.4em;
@@ -875,4 +876,9 @@ body{
 .hide{
     display:none !important; 
 }
+
+
+
+
+