فهرست منبع

空间模型差不多完成

xzw 3 سال پیش
والد
کامیت
1cb5cbb1a2

+ 9 - 8
libs/three.js/loaders/MTLLoader.js

@@ -5,12 +5,12 @@ import {
 	FrontSide,
 	Loader,
 	LoaderUtils,
-	MeshPhongMaterial,
+	MeshStandardMaterial,//MeshPhongMaterial, 用MeshStandardMaterial好调些,因为遵循能量守恒
 	RepeatWrapping,
 	TextureLoader,
 	Vector2
-} from '../../../build/three.module.js';
-
+} from '../build/three.module.js';
+ 
 /**
  * Loads a Wavefront .mtl file specifying materials
  */
@@ -388,8 +388,8 @@ MTLLoader.MaterialCreator.prototype = {
 				case 'ks':
 
 					// Specular color (color when light is reflected from shiny surface) using RGB values
-					params.specular = new Color().fromArray( value );
-
+					//params.specular = new Color().fromArray( value );
+                    //console.log('specular',value)
 					break;
 
 				case 'ke':
@@ -452,8 +452,9 @@ MTLLoader.MaterialCreator.prototype = {
 					// The specular exponent (defines the focus of the specular highlight)
 					// A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
 
-					params.shininess = parseFloat( value );
-
+					//params.shininess = parseFloat( value );
+                    //console.log('shininess',value)
+                    
 					break;
 
 				case 'd':
@@ -489,7 +490,7 @@ MTLLoader.MaterialCreator.prototype = {
 
 		}
 
-		this.materials[ materialName ] = new MeshPhongMaterial( params );
+		this.materials[ materialName ] = new MeshStandardMaterial( params )//MeshPhongMaterial( params );
 		return this.materials[ materialName ];
 
 	},

+ 38 - 128
note笔记笔记笔记.txt

@@ -7,6 +7,7 @@ http://localhost:1234/examples/page.html
 https://testlaser.4dkankan.com/maxkk/t-iksBApb/?image=1&vlon=4.91&vlat=-0.13&fov=100.0
 https://hq.iv.navvis.com/?site=1493761991057195&vlon=1.12&vlat=-0.52&fov=100.0&image=16902
 http://139.224.42.18:8080/siemens/?pc=true&vlon=2.59&vlat=-0.03&fov=100.0&lon=116.46694741&lat=39.98387332&z=1.807
+三味书屋可以编辑:
 http://indoor.popsmart.cn:8084/sxswsw-sx/?vlon=5.25&vlat=0.03&fov=100.0&pc=true&lon=120.58634810&lat=29.99135414&z=2.002
 
 用户名:admin
@@ -24,6 +25,13 @@ t-Y22JxCS7sP 小区
 t-8BCqxQAr93 小会议室
 t-AY03FoVZhm 一楼 
 t-GusppsiKEC 字节跳动楼外场景
+t-YLZ5XAALl7 燃气管线街景
+SS-t-P6zBR73Gke 燃气站
+t-QaHxu5Nmn3 一楼门口
+	--多楼层漫游点:
+
+
+
 
 正式服务器:
 3o5II3n9Rs 地下车库
@@ -58,7 +66,8 @@ Aa123456
 setView 旋转改成4dkk那种
 
 当距离很远的过渡可以考虑瞬间过渡
-热点可能需要再次矫正
+
+热点和测量线可能需要再次矫正(尤其截图时尽量居中)
  
  
 测量线截图的抗锯齿
@@ -78,6 +87,10 @@ moveSpeed 、 地图的自适应缩放
 
 刚开始加载高质量点云可以在rendertarget内
 
+地理位置修改了的话,没有绑定到数据集上的测量线怎么办? 要不还是默认放第一个数据集?
+
+
+
 ---------
 Bug	|
 ---------
@@ -90,6 +103,7 @@ Bug	|
 
 ----------------
 截图时map为何闪一下
+为什么pano的label被marker遮住  透明问题无解?
  
 隐藏数据集后再全景
 
@@ -100,8 +114,6 @@ Bug	|
 地图坐标矫正??
  
  
-截图的等待全景图加载的地方写全  现在暂时用的延时
- 
 
 
 
@@ -114,9 +126,7 @@ Bug	|
  
 
 
-23212 (导航)在导航页面停留一段时间不操作,界面会异常向上转 http://192.168.0.21/index.php?m=bug&f=view&bugID=23212
  
-分享的测量线地图上的端点大小有时大有时小 偶现
  
   
 
@@ -125,14 +135,8 @@ Bug	|
 
  
  
+  
  
-
- 
-
-导航距离很短时,只得到一个点时,地图上的箭头好像偏离了起点终点?  
- 
-
-我发现navvis的只要一进测量,细节程度自动变为8.退出后也不还原。(故放大镜里不稀疏) 
  macbook触摸板的也没有检查。 还不知道navvis的表现如何
 
  
@@ -150,148 +154,54 @@ Bug	|
 
 
 
-
-
-
  
-click是否要判断时间,如果按下到抬起时间过长不算click,navvis就是
-
-
-
-为什么有的mesh位置不准确,当前这个场景地面抬高了。
-很多碎片。
-
-
-
-
-
-好像还是会点着点着就不能测量了。  
-另外右侧为何还是禁止的reticule
-
-
-测量截图的无法loading贴图结束了   未加载
+前进时贴图好像是一个tile一个tile变的,尤其手机
 
+触屏放大后测量很卡。手机全景很卡
 
  
+偶现手机里测量线没marker  
 
 
-
-
-关于测量线截图1024时常get不到的情况:(奇怪为什么之前不会这样?)
-原因: uploadTile中,如果某个tile的子集(如1024的子集是四个2048)已经加载好了,那么该tile就不会触发加载,也就不会发送加载成功的消息。
-所以最好还是只请求加载512.除非首次加载就请求高分辨率。(是不是我写漏了啥?因为4dkk 飞进去不会)
-
-
-在map中reticule变为禁止后,再回到场景里点击,恢复成非禁止会延迟。可能因为click完才会恢复??
-另外地图上reticule变化透明度时没有更新
-
-
-
-
-手机测量线的遮挡失效 和 useEdl一样 EXT_frag_depth不可用
-
-
-
-
-
-
-
-
-关于iphone离开网页一段时间后再回来,重启(or显示内存不足)的bug: 
-	·t-6UoYGXbWhi 这个场景(最不容易崩溃 只有一个pano)的无此问题
-	·隧道。。第一次测会,第二次就不会了奇怪。。。(但有时候打开是报网络错误)
-
-	·我的手机是可能白屏(清除缓存后不会了)
-	·微信会(可能是应用问题,因为应用也容易关闭)	
-
-
-
-
-
-	
-
-
-
-
-
-
-
+测量线的白色在地图上不明显
 
  
+手机打开全景图特别慢
 
-
-
-
-
-
-
-为什么pano的label被marker遮住
+  
+在坐标页面加放大镜
+========================
  
+sitemodel 
 
 
-截图的测量线marker为何有大有小
-
-初始画面https://uat-laser.4dkankan.com/uat/index.html?m=t-e2Kb2iU#/coordinate
+暂时不做的功能: 重置or解锁轮廓(没啥用)。  添加子实体
+	移除点、结束绘画、去掉最后一个点
+	吸附点、线、90度
 
  
 
-
-触屏放大后测量很卡。
-
+点击实体后是到哪个漫游点? 如果没有漫游点呢?(找离bounding最近的漫游点吧)  
+	但是为何仿版无论空间模型怎么改都飞到同一个点呢
 
 
+cursor总感觉应该打开
+--------------------
 
 
+出现过marker无法松开的情况(加载的数据),但是再选中房间又可以了
+·是dropMarker时 因为有相同点 return continueDrag了
+·数据里竟然有五个点,后两个相同。应该是添加时没删除
+·是绘制完前删最后一个点后没有发送update
 
 
 
-地理位置修改了的话,没有绑定到数据集上的测量线怎么办? 要不还是默认放第一个数据集?
-
 
+地图上 双击似乎应该 flytopano
 
-map加updateTime
 
 
 
 
-
-全景图时放大镜里的点云稀疏了
-
-手机里测量线没marker
-
-
-
- 
-有时候加载全景图一直loading。 
-
-·六张512的有一张未打印出加载成功(但实际在网络里查看是加载了的,tileDownloader也有值),然后就去加载更高分辨率去了,没发送成功。
-·没通知成功的原因是uploadTile那张512时已经加载好了子集1024,就跳过了。
-·原来是预加载的dir写错了。但是只是预加载的话大概率不会发生此bug而已。
-·但万一先关闭了全景,在一个没有预加载的点位上打开全景,就可能发生bug
-
-
- 试试1024加载 {basePanoSize:1024}
-
-
-
-有时候转为全景模式还是加载不出,明明加载好了
-
-初始画面还是会转一下 t-1aw99jhDgB#/setup  但为何只是有时候?
-
-
-
-
-
-触屏放大镜里的点云为何那么大???
-·只要一转为触屏就这样  pointsize和什么有关,会重新计算吗
-·自适应时的触屏devicePixelRatio是2!!
-·shader中计算pointsize时最关键的参数是uScreenHeight。由于我在渲染屏幕点云时乘了devicePixelRatio,所以无论devicePixelRatio多少看起来都一样大;但是放大镜也被乘了devicePixelRatio,所以放大了。它的devicePixelRatio应该永远为1
-·考虑修改uScreenHeight为resolution2.y , 并取消乘以devicePixelRatio
- 
-
-低质量点云测量怎么点不上?
-·e.intersectPoint 居然没有
-·好像是因为改了shader后resolution没赋值,都是0,而电脑会给一个最小pointsize,手机可能更小吧? 所以pick不出。赋值就ok了
-
-
+出现过mainViewer中的reticule位置改变特别慢
   

+ 34 - 29
src/PointCloudOctree.js

@@ -333,7 +333,7 @@ export class PointCloudOctree extends PointCloudTree {
 			parent.children[childIndex] = geometryNode;
 		};
 		geometryNode.oneTimeDisposeHandlers.push(disposeListener);
-
+  
         
         //this.buildTexMesh(geometryNode,sceneNode)
 
@@ -347,9 +347,8 @@ export class PointCloudOctree extends PointCloudTree {
     
     
     buildTexMesh(geometryNode,sceneNode){
-         
         //  viewer.scene.pointclouds.forEach(a=>a.areaPlanes.forEach(e=>e.material = viewer.images360.cube.material))  
-       
+        //还是无法避免不在同一个平面的点被识别到同一个面上,然后法向都算错了。  另外还有就是破面太多。虽然能算出地板也不错。或者只去算水平面和垂直面。
         let startTime = Date.now()
         
             
@@ -369,6 +368,11 @@ export class PointCloudOctree extends PointCloudTree {
         
         if(this.texMeshUseLevel == void 0 || geometryNode.level == this.texMeshUseLevel){//只需要某一层level的点
             
+            
+            let showPointLabel = true, showCenterLabel = false  
+            
+            
+             
             //let spritesGeo = new THREE.BufferGeometry();
           
             let scaleRatio =  1.4 //稍微放大些,填满缝隙
@@ -380,27 +384,15 @@ export class PointCloudOctree extends PointCloudTree {
                 this.texMeshUseLevel = geometryNode.level
                 console.log('texMeshUseLevel ',geometryNode.level)
             }
-            
-            
-            let splitSprites = new THREE.Object3D
-            splitSprites.name = 'sub_splitSprites_'+geometryNode.name
-            splitSprites.position.copy(sceneNode.position)
-            splitSprites.rotation.copy(sceneNode.rotation)
-            this.splitSprites.add(splitSprites)
-            
-            
              
-            
             let geometry = geometryNode.geometry
             let count = geometry.attributes.position.count
             
-            
-            let end = Math.min(count, 850)
-            let start = Math.min(0, end) 
-            
-             
+            let end = Math.min(count, 6000)
+            let start = Math.min(5000, end)  
             console.warn('check points count:', end-start)
             
+            
             let position = geometry.attributes.position.array
             let normal = geometry.attributes.normal.array
             let i
@@ -410,19 +402,23 @@ export class PointCloudOctree extends PointCloudTree {
             let normals = [];
             let newPositions = [];
             let newIndices = []
-            let groupMaxPointCount = 2000 ;//太多找环会崩
+            let groupMaxPointCount = 1000 ;//太多找环会崩
             
             let minDisSquare =  Math.pow(spriteWidth1 * 1.8 , 2 ) //这个数太小就连不上啦 1.65太小
-            let minDot = Math.cos(THREE.Math.degToRad(10))//括号内是最小偏差角度, 20太大。太大的话会将立方体的相邻两面视为一个面。
+            let minDot = Math.cos(THREE.Math.degToRad(5))//括号内是最小偏差角度, 20太大。太大的话会将立方体的相邻两面视为一个面。
             
             //根据相邻点位置和角度是否相近来分组。有风险:两大区域可能因为一个模棱两可中间点连接在一起。
             
             let closeGroups = []
-            let groupTolerateMaxPoint = 4//组内点<=这个数的最后会被删除
+            let groupTolerateMaxPoint = 6//组内点<=这个数的最后会被删除
             let removedCount = 0
             let useGroupCount = 0
-            let pointDebug = true
-             
+            
+            let splitSprites = new THREE.Object3D
+            splitSprites.name = 'sub_splitSprites_'+geometryNode.name
+            splitSprites.position.copy(sceneNode.position)
+            splitSprites.rotation.copy(sceneNode.rotation)
+            this.splitSprites.add(splitSprites)
             
             for(i=start;i<end;i++){
                 let pos = new THREE.Vector3(position[3*i], position[3*i+1], position[3*i+2] );
@@ -499,7 +495,7 @@ export class PointCloudOctree extends PointCloudTree {
                 let planeMat = getPlaneMat(points, true)
                 
                 
-                if(pointDebug){
+                if(showPointLabel){
                     points.forEach((p,i)=>{
                         var label = new TextSprite({ 
                            text : index+"-"+i+" ("+p.index+")"+geometryNode.name, dontFixOrient:true,
@@ -565,7 +561,7 @@ export class PointCloudOctree extends PointCloudTree {
                                 if(planeNormals[0]){
                                     if(nor.dot(planeNormals[0])<0)nor.negate()  //反向下
                                 }
-                                //console.log('nor',nor)
+                                console.log('nor',nor)
                                 planeNormals.push(nor)
                                 
                             }
@@ -581,8 +577,10 @@ export class PointCloudOctree extends PointCloudTree {
                     return total.add(currentValue)
                 }, new THREE.Vector3).normalize() 
                 
-                console.log('aveNor',aveNor, 'avePos' ,avePos)
-                {
+                console.log('aveNor',aveNor, 'avePos' ,avePos, index)
+                
+                
+                if(showCenterLabel){
                     var label = new TextSprite({ 
                         //index+"-"+i+" ("+p.index+")"+geometryNode.name
                     
@@ -613,7 +611,7 @@ export class PointCloudOctree extends PointCloudTree {
                 points2d.forEach((p,j)=>{
                     p.id = j
                     for(let i=0;i<j;i++){
-                        if(p.distanceToSquared(points2d[i])<minDisSquare){
+                        if(p.distanceToSquared(points2d[i])<minDisSquare*1.5){
                             lines.push({p1:i,p2:j})
                         }
                     }  
@@ -1857,7 +1855,14 @@ export class PointCloudOctree extends PointCloudTree {
     
     
     
-    
+    getUnrotBoundPoint(){//获取没有旋转的tightBounding的水平四个点 
+        let bound = this.pcoGeometry.tightBoundingBox 
+        return [new THREE.Vector3(bound.min.x, bound.min.y,0),
+            new THREE.Vector3(bound.max.x, bound.min.y,0),
+            new THREE.Vector3(bound.max.x, bound.max.y,0),
+            new THREE.Vector3(bound.min.x, bound.max.y,0),
+        ].map(e=>e.applyMatrix4(this.matrixWorld)) 
+    }
     
     
     //数据集的显示影响到其下的:点云、marker  .不会影响地图上的显示

+ 1 - 1
src/Potree.js

@@ -258,7 +258,7 @@ export async function loadPanos(center, callback){
 
 
 
-export function Log(value, color, fontSize){
+export function Log(value, color, fontSize){ 
     color = color || '#13f'
     fontSize = fontSize || 14
     console.warn(`%c${value}`, `color:${color};font-size:${fontSize}px`) 

+ 106 - 0
src/materials/InfiniteGridMaterial.js

@@ -0,0 +1,106 @@
+
+import * as THREE from "../../libs/three.js/build/three.module.js";
+ 
+
+ 
+let vertexShader = `
+           
+   varying vec3 worldPosition;
+   
+   uniform float uDistance;
+   
+   void main() {
+   
+        vec3 pos = position.xzy * uDistance;
+        pos.xz += cameraPosition.xz;
+        
+        worldPosition = pos;
+        
+        gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
+   
+   }
+   ` 
+
+
+let fragmentShader = `
+   
+   varying vec3 worldPosition;
+   
+   uniform float uSize1;
+   uniform float uSize2;
+   uniform vec3 uColor;
+   uniform float uDistance;
+    
+    
+    
+    float getGrid(float size) {
+    
+        vec2 r = worldPosition.xz / size;
+        
+        
+        vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r);
+        float line = min(grid.x, grid.y);
+        
+    
+        return 1.0 - min(line, 1.0);
+    }
+    
+   void main() {
+   
+        
+          float d = 1.0 - min(distance(cameraPosition.xz, worldPosition.xz) / uDistance, 1.0);
+        
+          float g1 = getGrid(uSize1);
+          float g2 = getGrid(uSize2);
+          
+          
+          gl_FragColor = vec4(uColor.rgb, mix(g2, g1, g1) * pow(d, 3.0));
+          gl_FragColor.a = mix(0.5 * gl_FragColor.a, gl_FragColor.a, g2);
+        
+          if ( gl_FragColor.a <= 0.0 ) discard;
+        
+   
+   }
+`
+
+
+
+
+
+
+
+
+export default class InfiniteGridMaterial extends THREE.ShaderMaterial{
+    constructor(o={}){ 
+        let color = o.color instanceof THREE.Color ? o.color : new THREE.Color(o.color || 'white');
+        let size1 = o.size1 || 10;
+        let size2 = o.size2 || 100; 
+        let distance = o.distance || 8000;
+ 
+        let uniforms = { 
+            uSize1: {
+                value: size1
+            },
+            uSize2: {
+                value: size2
+            },
+            uColor: {
+                value: color
+            },
+            uDistance: {
+                value: distance
+            }
+        } 
+        
+        super({
+            uniforms,
+            vertexShader,
+            fragmentShader,
+            transparent: true,
+            side :  THREE.DoubleSide,
+            extensions: {
+                derivatives: true
+            }
+        }) 
+    }
+}

+ 1 - 1
src/materials/PointCloudMaterial.js

@@ -689,7 +689,7 @@ export class PointCloudMaterial extends THREE.RawShaderMaterial {
         if(typeof value == 'string') {
             var colorArr = Potree.config.colors[value]  
             if(!colorArr){ 
-                console.warn('没找到该颜色值'+ value)
+                //console.warn('没找到该颜色值'+ value)
             }else{
                 color = new THREE.Color().fromArray(colorArr).multiplyScalar(1/255)
             }                

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

@@ -205,8 +205,8 @@ export class CameraAnimation extends EventDispatcher{
 		// cp.position.copy(viewer.scene.view.position);
 		// cp.target.copy(viewer.scene.view.getPivot());
 
-		cp.positionHandle = this.createHandle(cp.position);
-		cp.targetHandle = this.createHandle(cp.target);
+		cp.positionHandle = this.createHandle(cp.position, 'red');
+		cp.targetHandle = this.createHandle(cp.target, 'blue');
 
 		this.controlPoints.splice(index, 0, cp);
 
@@ -397,7 +397,7 @@ export class CameraAnimation extends EventDispatcher{
 		this.t = t;
 	}
 
-	createHandle(vector){
+	createHandle(vector, color){
 		
 		const svgns = "http://www.w3.org/2000/svg";
 		const svg = document.createElementNS(svgns, "svg");
@@ -415,7 +415,7 @@ export class CameraAnimation extends EventDispatcher{
 		circle.setAttributeNS(null, 'cx', "1em");
 		circle.setAttributeNS(null, 'cy', "1em");
 		circle.setAttributeNS(null, 'r', "0.5em");
-		circle.setAttributeNS(null, 'style', 'fill: red; stroke: black; stroke-width: 0.2em;' );
+		circle.setAttributeNS(null, 'style', 'fill: '+color+'; stroke: black; stroke-width: 0.2em;' );
 		svg.appendChild(circle);
 
 

+ 13 - 3
src/modules/Images360/Images360.js

@@ -85,7 +85,7 @@ export class Images360 extends EventDispatcher{
         this.ultraHighPanoSize = this.qualityManager.getPanoSize(PanoSizeClass.ULTRAHIGH);
         this.tileDownloader.processPriorityQueue = !1;
         this.tileDownloader.tilePrioritizer = new TilePrioritizer(this.qualityManager, this.basePanoSize, this.standardPanoSize, this.highPanoSize, this.ultraHighPanoSize);    
-       
+        this.flying_ = false
         
         {//高分辨率cube 放大
             this.addHighMapCube()
@@ -314,6 +314,7 @@ export class Images360 extends EventDispatcher{
                             canLeavePano :  config.canLeavePano ,
                             viewport: 
                         }) */
+                        
                         viewer.mainViewport.unableChangePos = !config.canLeavePano
                          
                         
@@ -340,7 +341,7 @@ export class Images360 extends EventDispatcher{
                                 e.material.pointSizeType = Potree.config.material.pointSizeType
                             })
                             
-                        }  
+                        }   
                         camera.updateProjectionMatrix() 
                         
                         
@@ -458,7 +459,16 @@ export class Images360 extends EventDispatcher{
      
     }
 
- 
+    set flying(v){
+        this.flying_ = !!v
+        //this.emit('flying', this.flying_)
+        let config = Potree.config.displayMode[Potree.settings.displayMode]
+        viewer.mainViewport.unableChangePos = !config.canLeavePano || !!v  
+        
+    }
+    get flying(){
+        return this.flying_
+    }
     
     
     

+ 7 - 3
src/modules/Images360/tile/TileDownloader.js

@@ -168,8 +168,9 @@ class TileDownloader extends EventDispatcher{
                     //add 为了防止1024的在512前下载完,这里强行等待512下载完毕再开始下载
                     if(o.panoSize > 512 && !this.isPanoDownloaded(o.pano, 512) ){
                         //console.log('512的还没下载好呢!')
+                        e.push(o)
                         break;//一般512的都是连续下载的,所以后面就都不是512了直接中断 
-                    }
+                    } 
                     
                     this.startDownload(o)
                     n++
@@ -177,8 +178,11 @@ class TileDownloader extends EventDispatcher{
                 
             }
         }
-    }
- 
+    } 
+    
+    
+    
+    
     testDownload(panoSize, tileSize, callback) {
         var n = this.downloadTestResults[panoSize];
         if (n)

+ 10 - 3
src/modules/datasetAlignment/Alignment.js

@@ -95,14 +95,19 @@ var Alignment = {
     },
     enter:function(){
         this.saveTemp()  
-        
-        
+         
         
         SplitScreen.splitScreen4Views({alignment:true})
+         
+        viewer.images360.panos.forEach(pano=>{
+            viewer.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};
         viewer.viewports.find(e=>e.name == 'Back').alignment = {translate:true};
         
+        
         this.editing = true
     },
     leave:function(){
@@ -117,7 +122,9 @@ var Alignment = {
         
         
         SplitScreen.recoverFrom4Views()
-        
+        viewer.images360.panos.forEach(pano=>{
+            viewer.updateVisible(pano.mapMarker, 'split4Screens', true)
+        }) 
         this.editing = false
         
          

+ 547 - 114
src/modules/siteModel/BuildingBox.js

@@ -1,108 +1,331 @@
 
 import * as THREE from "../../../libs/three.js/build/three.module.js";
 import {ctrlPolygon} from '../../utils/ctrlPolygon'
-import {LineDraw, MeshDraw } from "../../utils/DrawUtil";
+import {LineDraw, MeshDraw } from "../../utils/DrawUtil";  
+import math  from "../../utils/math";
 import Sprite from '../../viewer/Sprite'
 /* import {config} from '../settings' */
-
+import searchRings from "../../utils/searchRings.js";
 
 let texLoader = new THREE.TextureLoader() 
 
  
 let markerMats
-let markerSizeInfo = {width2d:30}
+let markerSizeInfo = {width2d:35}
 let color = new THREE.Color('#FFF')
-
-
-let faceMats = {
-    building: new THREE.MeshPhongMaterial({
-        color:"#00bbff",
-        side:THREE.DoubleSide,
-        opacity:0.1,
-        transparent:true,
-        depthTest:false
-    }),
-    floor:  new THREE.MeshPhongMaterial({
-        color:"#eeee00",
-        side:THREE.DoubleSide,//BackSide,
-        opacity:0.06,
-        transparent:true,
-        depthTest:false
-    }),
-    floorSelect: new THREE.MeshPhongMaterial({
-        color:"#eeee00",
-        side:THREE.DoubleSide,
-        opacity:0.15,
-        transparent:true,
-        depthTest:false
-    }),
-    room: new THREE.MeshPhongMaterial({
-        color:"#ff44ee",
-        side:THREE.DoubleSide,//BackSide,
-        opacity:0.06,
-        transparent:true,
-        depthTest:false
-    }),
-    roomSelect: new THREE.MeshPhongMaterial({
-        color:"#ff44ee",
-        side:THREE.DoubleSide,//BackSide,
-        opacity:0.15,
-        transparent:true,
-        depthTest:false
-    }),
-    
+let faceMats
+let getFaceMat = (name)=>{
+    if(!faceMats){ //navvis材质可以搜gridTexture
+        let gridTex = texLoader.load( Potree.resourcePath+'/textures/gridmap.png' ) 
+            gridTex.wrapS = gridTex.wrapT = THREE.RepeatWrapping   
+            gridTex.repeat.set(0.5,0.5)//放大一些
+        faceMats = { 
+            dataset: new THREE.MeshStandardMaterial({
+                color:812922,   
+                side:THREE.DoubleSide, 
+                opacity:0.2,
+                transparent:true,  
+                depthTest:false,
+                wireframe:true
+            }),
+            building: new THREE.MeshStandardMaterial({
+                color:812922,  metalness: 0.2, roughness:0.8,
+                side:THREE.DoubleSide, 
+                opacity:0.1,
+                transparent:true,  
+                depthTest:false
+            }),
+            buildingSelect: new THREE.MeshStandardMaterial({
+                color:36582,  metalness: 0, roughness:1,
+                side:THREE.DoubleSide,
+                opacity:0.1,
+                transparent:true,
+                depthTest:false
+            }),
+            floor:  new THREE.MeshStandardMaterial({
+                color:11708469,  metalness: 0.1, roughness:1,
+                side:THREE.DoubleSide,//BackSide,
+                opacity:0.05,
+                transparent:true,
+                depthTest:false
+            }),
+            /* floorSelect: new THREE.MeshStandardMaterial({
+                color:"#eeee00",  metalness: 0, roughness:1,
+                side:THREE.DoubleSide,
+                opacity:0.12,
+                transparent:true,
+                depthTest:false
+            }), */
+             
+            floorSelect: new THREE.MeshStandardMaterial({
+                color:16707151,  metalness: 0, roughness:1,
+                side:THREE.DoubleSide,
+                opacity:1,
+                transparent:true,
+                depthTest:false,
+                map: gridTex,
+            }), 
+            
+            
+            room: new THREE.MeshStandardMaterial({
+                color:"#ff44ee",  metalness: 0, roughness:1,
+                side:THREE.DoubleSide,//BackSide,
+                opacity:0.08,
+                transparent:true,
+                depthTest:false
+            }),
+            roomSelect: new THREE.MeshStandardMaterial({
+                color:"#ff44ee",  metalness: 0.3, roughness:1,
+                side:THREE.DoubleSide,//BackSide,
+                opacity:1,
+                transparent:true,
+                depthTest:false,
+                map: gridTex,
+            }),
+            
+        }
+    }
+    return faceMats[name]
 }
 
 
 
+ 
+
 export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, floor, room
     constructor(prop) {
         prop.dimension = '3d'
-        super('siteModel_'+prop.type_,  prop);
+        //prop.name = Potree.config.siteModel.names[prop.buildType] + 
+         
+        super('siteModel_'+prop.buildType,  prop);
+        
+         
+        
         this.midMarkers = []
+        this.buildChildren = []//子实体
+        this.holes = [] //在这创建的hole
+        this.parentHoles = [];//floor从building那得到的当层holes
+        this.mats = {} //材质
+        
+        this.panos = this.panos || [];
+        this.center //中心点
         
-        if(this.type_=='floor'){//一旦添加了楼层,建筑原本的faceMesh lineMesh就隐藏
-            /* this.buildParent.box.visible = false 
-            this.buildParent.lineMesh.visible = false  */
+        if(this.buildType=='floor'){
             
             this.points = this.buildParent.points;//完全等于建筑的点
+            this.buildParent.holes.forEach(hole=>{//从building获取holes
+                let floorHole = new BuildingBox({
+                    buildType : 'hole', 
+                    buildParent:this,
+                    originHole : hole, //整栋大楼在当层的hole
+                    ifDraw: this.ifDraw  || Potree.settings.drawEntityData
+                });
+                this.parentHoles.push(floorHole)
+                this.add(floorHole) 
+                
+                floorHole.points = hole.points//完全等于建筑的点
+            })
         } 
+        if(this.buildType == 'room' || this.buildType == 'hole'){
+            this.restrictArea = this.buildParent  //不能超出的区域
+        }
         
-        this.box = this.createBox()
-        this.add(this.box)
+        if(this.ifDraw){ //只存储空间模型信息,不绘制
+            if(this.buildType != 'hole'){
+                this.box = this.createBox()
+                this.add(this.box)
+            }
+            {
+                this.lineMesh = LineDraw.createLine([],{color})
+                this.lineMesh.name = 'buildingLines'
+                this.lineMesh.visible = false            
+                this.add(this.lineMesh) 
+                viewer.setObjectLayers(this.lineMesh, 'sceneObjects' ) 
+            }
+            
+            
+            this.addEventListener('dragChange',(e)=>{ //修改中点
+                this.isNew || this.updateTwoMidMarker(e.index)
+            }) 
+            
+        }
         
-        this.lineMesh = LineDraw.createLine([],{color})
-        this.lineMesh.name = 'buildingLines'        
-        this.add(this.lineMesh) 
-        viewer.setObjectLayers(this.lineMesh, 'sceneObjects' )
+        this.initData(prop)
         
-        this.buildChildren = []//子实体
         
-        this.addEventListener('dragChange',(e)=>{ //修改中点
-            this.isNew || this.updateTwoMidMarker(e.index)
-        })
-   
+    } 
+    
+    
+    initData(prop){
+        if(prop.ifDraw){
+            super.initData(prop) 
+        }else{
+            if(prop.points){
+                this.points = prop.points  
+            }
+            
+            
+        }
         
     } 
     
+    intersectPointcloudVolume(pointcloud){//和pointcloud的重叠体积
+        var bound = this.getBound()
+        let bound2 = pointcloud.bound;
+        if(!bound.intersectsBox(bound2)) return 0;
+        
+        let min = Math.min(this.zMin, bound2.min.z);
+        let max = Math.max(this.zMax, bound2.max.z); 
+        let height1 = this.zMax - this.zMin
+        let height2 = bound2.max.z-bound2.min.z
+        let coverHeight = height1 + height2 - (max-min)//重叠高度
+        
+        let boxPoints = pointcloud.getUnrotBoundPoint() //获取tightBound的四个点。 如果是有旋转角度的点云,这个和pointcloud.bound的四个点是不一致的,覆盖面积小于pointcloud.bound
+       
+        let areaWhole = 0  
+        let area1 = this.getArea()
+        let area2 = Math.abs(math.getArea(boxPoints))
+         
+        {//计算points与点云总面积 (但是把hole也加入了面积)(并集,重叠部分只算一次)  
+            let rings = math.getPolygonsMixedRings([this.points, boxPoints] )
+            
+            rings.forEach(e=>{ 
+                areaWhole+=e.area
+            })
+        }
+        
+        let coverHoleArea = 0 //holes与数据集重叠的部分
+        let holes = this.holes.concat(this.parentHoles)
+        let holesArea = 0    //所有holes面积相加
+        let areaHoleWithPointcloud = 0 //hole和点云的面积并集
+        
+        if(holes.length>0){//还要再扣除holes与数据集重叠的部分。其中holes为mix轮廓
+            let outHoles = []//没有重合的holes的外轮廓
+            if(holes.length>=2){//合并holes。如果能在绘制时直接合并holes就好啦,这步就转移到那去,但是要删除hole好麻烦
+                  
+                let holes_ = holes.map(e=>e.points)
+                
+                outHoles = math.getPolygonsMixedRings(holes_,  true)
+                outHoles.forEach(e=>{ 
+                    holesArea+=e.area
+                }) 
+                outHoles = outHoles.map(e=>e.points)
+                
+                
+            }else{
+                outHoles = holes.map(e=>e.points)
+                outHoles.forEach(e=> holesArea += Math.abs(math.getArea(e)))                
+            }
+            
+            //holes与数据集重叠的部分
+            
+            {    
+                let polygons = outHoles.concat([boxPoints]) 
+                let rings = math.getPolygonsMixedRings(polygons)
+                rings.forEach(e=>{ 
+                    areaHoleWithPointcloud+=e.area
+                })
+                coverHoleArea = holesArea + area2 - areaHoleWithPointcloud//hole和点云的交集
+            }
+             
+        }
+        
+        
+        let coverArea = area1 + area2 - areaWhole - coverHoleArea; //重叠面积
+         
+        return coverArea * coverHeight
+    }
+    
+    
+    addHole(points=[]){  
+        let prop = {
+            buildType : 'hole',
+            zMin : this.zMin,
+            zMax : this.zMax,
+            points, 
+            buildParent:this,
+            ifDraw: this.ifDraw || Potree.settings.drawEntityData
+        }
+        //hole的zMin zMax跟随buildParent
+        var hole = new BuildingBox(prop);
+        this.holes.push(hole)
+        
+        if(this.buildType == 'building'){//为每一层添加对应的hole
+            this.buildChildren.forEach(floor=>{
+                let floorHole = new BuildingBox({
+                    buildType : 'hole', 
+                    zMin : this.zMin,
+                    zMax : this.zMax,
+                    buildParent:floor,
+                    originHole : hole, //整栋大楼在当层的hole
+                    ifDraw: this.ifDraw  || Potree.settings.drawEntityData
+                });
+                floor.parentHoles.push(floorHole)
+                floor.add(floorHole)
+                floorHole.points = hole.points//完全等于建筑的点
+            })
+            
+        }
+        
+        this.add(hole);//直接加在这,不加meshGroup了
+        this.update() //update box mesh
+        return hole
+        
+        //hole不创建box,只有它的buildParent需要更新box。 但有线条和marker.  hole不在buildChildren里,但有buildParent
+        
+    }
+    
+    
+    removeHole(hole){// 这个hole不会是parentHoles里的。
+        hole.dispose()
+        
+        if(this.buildType == 'building'){ //若是整栋大楼的hole,在每层去除它的对应hole
+            this.buildChildren.forEach(floor=>{ 
+                let holeAtFloor = floor.parentHoles.find(e=>e.originHole == this )
+                let index = floor.parentHoles.indexOf(holeAtFloor)  
+                index > -1 && floor.parentHoles.splice(index, 1)                
+                holeAtFloor.dispose() 
+            })
+        } 
+        
+        let index = this.holes.indexOf(hole)
+        if(index>-1){
+            this.holes.splice(index, 1)
+        }
+        this.remove(hole)
+        this.update()
+    }
+    
+    
     
     
     createBox(){ 
         var geometry = new THREE.Geometry();
-        var mesh = new THREE.Mesh(geometry, faceMats[this.type_])
+        
+        
+        
+        this.mats.boxDefault = getFaceMat(this.buildType)  
+        this.mats.boxSelected = getFaceMat(this.buildType+'Select')     
+        
+        
+        var mesh = new THREE.Mesh(geometry, this.mats.boxDefault)
         mesh.name = 'buildingBox';
-        if(this.type_ == 'floor'){
-            viewer.setObjectLayers(mesh, 'siteModelMapUnvisi' )
+        if(this.buildType == 'floor'){
+            viewer.setObjectLayers(mesh, 'siteModelMapUnvisi' ) //楼层默认在地图不显示,为了不会叠加透明度
         }else{
             viewer.setObjectLayers(mesh, 'sceneObjects' )
         }
+         
+        
+        
+        //mesh.frustumCulled = false;
         return mesh
     }
     
     
     
     addMarker(o={} ){
-        if(this.type_=='floor')return; //楼层不需要marker
+        if(this.buildType=='floor')return; //楼层不需要marker
         
         let marker = new Sprite({mat:this.getMarkerMaterial('default'), sizeInfo: markerSizeInfo, dontFixOrient: true, name:"building_marker"} )
         
@@ -113,14 +336,30 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
         o.marker = marker
         super.addMarker(o)
         
+        if(!this.selected)viewer.updateVisible(marker,'select',false) 
          
         return marker
     }
     
-    
+    removeMarker(index){
+        super.removeMarker(index);
+        if(!this.isNew){
+            //重新添加midMarkers 
+            this.midMarkers.forEach(e=>this.remove(e));
+            this.midMarkers = [] 
+            this.addMidMarkers() 
+        }
+        
+        this.update();  
+        if(this.points.length == 2 && this.box){//清除原先length>=3时候的
+            this.box.geometry = new THREE.Geometry();
+        } 
+        
+     
+    }
     
     addMidMarker(index, point){
-        if(this.type_=='floor')return; //楼层不需要marker
+        if(this.buildType=='floor')return; //楼层不需要marker
         let marker = new Sprite({mat:this.getMarkerMaterial('midPrepare'), sizeInfo: markerSizeInfo, dontFixOrient: true, name:"building_midMarker"} )
         this.midMarkers = [...this.midMarkers.slice(0,index), marker, ...this.midMarkers.slice(index,this.midMarkers.length)]
         
@@ -155,11 +394,14 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
         }
         this.add(marker)
         this.updateMarker(marker, point)
+        if(!this.selected)viewer.updateVisible(marker,'select',false) 
         return marker
     }
     
     
     
+    
+    
     addMidMarkers(){//第一次画好所有marker后,一次性为线段增加中点marker
         let length = this.points.length
         this.points.forEach((point,index)=>{
@@ -182,35 +424,78 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
         this.updateMarker(nextMidMarker, nextMid) 
     }
     
-    dispose(){
+     
+    
+    
+    
+    dispose(){//销毁geo、remove from parent
         super.dispose()
-        this.box.geometry.dispose();
-        this.lineMesh.geometry.dispose();
-        
-        
+        this.box && this.box.geometry.dispose();
+        this.lineMesh && this.lineMesh.geometry.dispose();
+        this.holes.forEach(e=>e.dispose()) 
+        this.parentHoles.forEach(e=>e.dispose()) 
         this.buildChildren.forEach(e=>e.dispose())
     }
     
-    update(ifUpdateMarkers, dontUpdateChildren){ 
-        super.update(ifUpdateMarkers)
-        
-        
+    
+    
+    updateBox(){
+        if(!this.box)return
         this.box.geometry.dispose()
-        let length = this.points.length 
-        if(length >= 3){ 
-            this.box.geometry = MeshDraw.getExtrudeGeo(this.points, this.zMax-this.zMin) 
+        
+        if(this.points.length >= 3){ 
+            let holes = this.holes.concat(this.parentHoles)
+            let holesPoints = holes.filter(e=>e.points.length>2).map(e=>e.points)
+            this.box.geometry = MeshDraw.getExtrudeGeo(this.points, holesPoints, {
+                depth:this.zMax-this.zMin,
+                UVGenerator: new MetricUVGenerator()
+            }) 
             this.box.position.z = this.zMin
         }
+    }
+    
+     
+    
+    update(options={}){ 
+        super.update(this.buildType != 'floor' && options.ifUpdateMarkers)
+        let length = this.points.length
         
-       /*  if(!this.isNew){
-            this.midMarkers
+        
+       
+        {//确保一下一样
+            if(this.originHole){ 
+                this.points = this.originHole.points //完全等于building的hole
+            }
+            if(this.buildType == 'hole'){
+                this.zMin = this.buildParent.zMin;
+                this.zMax = this.buildParent.zMax;
+            } 
         }
-         */
+        
+        
+        
+        if(!options.dontUpdateBox){
+            let boxOwner
+            if(this.buildType == 'hole'){
+                if(this.buildParent.buildType == 'building'){ //若是整栋大楼的hole,在每层都要更新下它的对应hole
+                    this.buildParent.buildChildren.forEach(floor=>{
+                        let holeAtFloor = floor.parentHoles.find(e=>e.originHole == this ) 
+                        holeAtFloor && holeAtFloor.update()  //刚开始创建时还没创建对应的 holeAtFloor会为null
+                    })
+                } 
+                boxOwner = this.buildParent 
+            }else{
+                boxOwner = this
+            }
+            boxOwner.updateBox()
+        }
+        
+        
         
         {//update lines
-            //let zMin = 0, zMax = 1
-            let positions = [];
             
+            let positions = [];
+           
             this.points.forEach((point, index)=>{
                 
                 //竖线:
@@ -218,41 +503,105 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
                 
                 //横线
                 let nextPoint = this.points[(index+1)%length]; 
-                if(point == nextPoint)return;//when length==1
+                if(!nextPoint)return;//when length==1
                 
                 positions.push(point.clone().setZ(this.zMax), nextPoint.clone().setZ(this.zMax))//上横线
                 positions.push(point.clone().setZ(this.zMin), nextPoint.clone().setZ(this.zMin))//下横线
-            })
+            }) 
             
             LineDraw.moveLine(this.lineMesh,positions)
              
         }
         
-        
-        if(this.type_ == 'building' && !dontUpdateChildren){
-            this.buildChildren.forEach(e=>{
-                e.points = this.points
-                e.update()
-            })
+        if(!options.dontUpdateChildren){
+            if(this.buildType == 'building'  ){
+                this.buildChildren.forEach(floor=>{
+                    floor.points = this.points 
+                    floor.update()
+                })
+                
+            } 
             
-        }
-        
-        
+            { 
+                let holes = this.holes.concat(this.parentHoles) 
+                holes.forEach(hole=> {  
+                    
+                    hole.update({dontUpdateBox:true})//父级更新了box,hole就不需要更新box了
+                }) 
+                    
+            } 
         
+        } 
     }
     
-    removeMarker(index ){  
-        super.removeMarker(index) 
-        this.update(); 
-        if(this.points.length == 2){//清除原先length>=3时候的
-            this.box.geometry = new THREE.Geometry();
+    
+     
+    
+    
+    
+    
+    
+    getArea(){//面积
+        //不排除hole
+        return Math.abs(math.getArea(this.points))
+    }
+    
+    getVolume(){//体积
+        let {zMin , zMax} = this.getRealZ()
+        let height = zMax - zMin;
+        if(isNaN(height))height = 0
+        return this.getArea() * height
+    
+    }
+    
+    getRealZ(){//求真实高度时用到的
+        let zMin , zMax
+        if (this.buildType == 'building') {
+            //building的zMax和zMin一样的所以要算
+            let top = this.buildChildren[this.buildChildren.length - 1]
+            let btm = this.buildChildren[0] 
+            if(btm) zMin = btm.zMin 
+            if(top)zMax = top.zMax 
+        }else if(this.buildType == 'hole'){
+            return this.buildParent.getRealZ()
+        }else{
+            zMin = this.zMin,  zMax = this.zMax
         }
-        //this.dispatchEvent({type: 'marker_removed', measurement: this});
+        return {zMin,zMax}
     }
     
     
+    /* getDrawZ(){ //画线和box时用到的z
+        let zMin , zMax
+        if(this.buildType == 'hole'){
+             if(this.buildParent.buildType == 'building' && atFloor){ 
+                 zMin = atFloor.zMin,  zMax = atFloor.zMax 
+             }else{
+                 zMin = this.buildParent.zMin,  zMax = this.buildParent.zMax
+             }
+        }else{
+            zMin = this.zMin,  zMax = this.zMax
+        }
+        
+        return {zMin, zMax}
+        
+    } */
+    
     
     
+    getBound(){
+        let bound = new THREE.Box3
+         
+        let {zMin , zMax} = this.getRealZ()
+        
+        this.points.forEach(p=>{
+            bound.expandByPoint(p.clone().setZ(zMin))
+            bound.expandByPoint(p.clone().setZ(zMax))
+        }) 
+        return bound
+    }
+    
+     
     getMarkerMaterial(type) {
         if(!markerMats){
             markerMats = {  
@@ -302,50 +651,134 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
     
     
     select(){
+        //最多再显示一层子级的线,如building不会显示room中的hole的线
+        //box是一直显示的,但会切换材质 
+        
+        /* 
+            选中                  box                                     线
+            
+            building            自己(底盘)选中                 自己, floor不带hole
+        
+            floor               自己选中                         自己, room不带hole
+            
+            room                自己选中                         自己   
+        
+         */  //注:自己的就代表定包括hole,如果有parentHoles的也(building上的hole的对应)
+        
+        
+        
+        
         if(this.selected)return
         
+        if(this.box){
+            this.box.material = this.mats.boxSelected;
+        }
         
-        if(this.type_ == 'building'){
+        if(this.buildType == 'building'|| this.buildType == 'floor'){
             this.buildChildren.forEach(e=>{
                 e.lineMesh.visible = true
             })
-            
-        }else if(this.type_ == 'floor'){
-            
-            this.box.material = faceMats.floorSelect
-            viewer.setObjectLayers(this.box, 'sceneObjects' )
-             
-        }else if(this.type_ == 'room'){
-            this.box.material = faceMats.roomSelect
+              
+            if(this.buildType == 'floor'){
+                viewer.setObjectLayers(this.box, 'sceneObjects' ) 
+                viewer.setObjectLayers(this.buildParent.box, 'siteModelMapUnvisi' ) //当选中floor或room时,building在地图不可见
+            }
+        }else if(this.buildType == 'room'){
+            viewer.setObjectLayers(this.buildParent.box, 'sceneObjects' )
+            viewer.setObjectLayers(this.buildParent.buildParent.box, 'siteModelMapUnvisi' )
         }
+        
+        
+        
+        
+        
         this.lineMesh.visible = true
         this.markers && this.markers.forEach(e=>viewer.updateVisible(e,'select',true) )
         this.midMarkers && this.midMarkers.forEach(e=>e.visible = true)
         
-        this.selected = true
+        let holes = this.holes.concat(this.parentHoles)
+        holes.forEach(e=>e.select()) 
         
+        this.selected = true
+        this.dispatchEvent({type:'select'})
     }
     
     
     unselect(){
+        if(this.box){
+            this.box.material = this.mats.boxDefault;
+        }
         
-        if(this.type_ == 'building'){ 
+        if(this.buildType == 'building' || this.buildType == 'floor'){ 
             this.buildChildren.forEach(e=>{
                 e.lineMesh.visible = false
             })  
-        }else if(this.type_ == 'floor'){ 
-            viewer.setObjectLayers(this.box, 'siteModelMapUnvisi' )
-            this.box.material = faceMats.floor
-        }else if(this.type_ == 'room'){
-            this.box.material = faceMats.room
+            
+            if(this.buildType == 'floor'){
+                viewer.setObjectLayers(this.box, 'siteModelMapUnvisi' ) 
+                viewer.setObjectLayers(this.buildParent.box, 'sceneObjects' ) 
+            }
+            
+        }else if(this.buildType == 'room'){
+            viewer.setObjectLayers(this.buildParent.box, 'siteModelMapUnvisi' )
+            viewer.setObjectLayers(this.buildParent.buildParent.box, 'sceneObjects' )
         }
+        
         this.lineMesh.visible = false
         this.markers && this.markers.forEach(e=>viewer.updateVisible(e,'select',false) )
         this.midMarkers && this.midMarkers.forEach(e=>e.visible = false)
         
+        let holes = this.holes.concat(this.parentHoles)
+        holes.forEach(e=>e.unselect()) 
+        
         this.selected = false
+        this.dispatchEvent({type:'unselect'})
     }
     
+     
+    
+    
+    ifContainsPoint(position){//看它所定义的空间是否包含某个坐标(要排除hole)
     
+        let {zMin , zMax} = this.getRealZ()
+        if(position.z < zMin ||  position.z > zMax   ) return
+    
+        let holes = this.holes.concat(this.parentHoles)
+        let holesPoints = holes.filter(e=>e!=this && e.points.length>2).map(e=>e.points) 
+        let inShape = math.isPointInArea(this.points, holesPoints, position) 
+        
+         
+        return !!inShape 
+    }
      
-}
+}
+
+
+
+
+
+class MetricUVGenerator{
+    constructor(){
+        this.a = new THREE.Vector3,
+        this.b = new THREE.Vector3,
+        this.c = new THREE.Vector3,
+        this.d = new THREE.Vector3
+    }
+    generateTopUV(t, e, n, r, o) {
+        return [new THREE.Vector2(e[3 * n],e[3 * n + 1]), new THREE.Vector2(e[3 * r],e[3 * r + 1]), new THREE.Vector2(e[3 * o],e[3 * o + 1])]
+    }
+    
+    generateSideWallUV(t, e, n, r, o, a) {
+        var s = e;
+        this.a.set(s[3 * n], s[3 * n + 1], s[3 * n + 2]),
+        this.b.set(s[3 * r], s[3 * r + 1], s[3 * r + 2]),
+        this.c.set(s[3 * o], s[3 * o + 1], s[3 * o + 2]),
+        this.d.set(s[3 * a], s[3 * a + 1], s[3 * a + 2]);
+        var c = this.a.x !== this.b.x
+          , l = c ? this.b : this.d
+          , u = this.a.distanceTo(l)
+          , d = l.distanceTo(this.c);
+        return [new THREE.Vector2(this.a.x,0), c ? new THREE.Vector2(this.a.x + u,0) : new THREE.Vector2(this.a.x,d), new THREE.Vector2(this.a.x + u,d), c ? new THREE.Vector2(this.a.x,d) : new THREE.Vector2(this.a.x + u,0)]
+    }
+}
+ 

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 612 - 251
src/modules/siteModel/SiteModel.js


+ 18 - 16
src/navigation/FirstPersonControls.js

@@ -181,6 +181,7 @@ export class FirstPersonControls extends EventDispatcher {
                                 type: 'dragPanBegin', 
                                 projectionMatrixInverse : e.drag.projectionMatrixInverse
                             }); */
+                            //console.log('开始拖拽', e.pointer.clone())
                         }  
                         //拖拽的过程中将projectionMatrixInverse替换成开始拖拽时的,因为near、far一直在变,会导致unproject计算出的3d坐标改变很大而闪烁。
                         var _projectionMatrixInverse = camera.projectionMatrixInverse;
@@ -195,6 +196,9 @@ export class FirstPersonControls extends EventDispatcher {
                         camera.projectionMatrixInverse = _projectionMatrixInverse
                         this.translationWorldDelta.copy(moveVec.negate())  //这里没法用add,原因未知,会跳动
                         //console.log('pan 1', this.translationWorldDelta.clone())   
+                        
+                         
+                        
                         //四指松开剩三指时会偏移一下,暂不知道哪里的问题,或许跟开头防止点云吸附有关?
                         
                         
@@ -340,13 +344,13 @@ export class FirstPersonControls extends EventDispatcher {
 		};
 
 		this.viewer.addEventListener('global_drag', drag);
-        this.viewer.addEventListener('global_touchmove', (e)=>{ 
+        /* this.viewer.addEventListener('global_touchmove', (e)=>{ 
             if(!this.enabled)return
             if(e.touches.length>1){//单指的就触发上一句 
                 //console.log('global_touchmove' )
                 drag(e)
             }
-        });
+        }); */
 		this.viewer.addEventListener('global_drop', drop);
 		this.viewer.addEventListener('global_mousewheel', scroll);
 		this.viewer.addEventListener('global_dblclick', dblclick);
@@ -361,17 +365,10 @@ export class FirstPersonControls extends EventDispatcher {
             //console.log('prepareRotate')
         }
         let preparePan = (e)=>{//触屏的pan点云    还是会偏移
-            
-            var pos2d = {
-                onlyGetIntersect : true,
-                clientX : e.touches[0].touch.pageX,
-                clientY : e.touches[0].touch.pageY,
-            } 
-             
-            var intersect = this.viewer.inputHandler.onMouseMove(pos2d) 
-            e.drag.intersectStart = intersect
-            e.drag.z = void 0  //清空
-            e.drag.end.copy(e.pointer)  
+            this.pointerDragStart = e.pointer.clone() 
+              
+            e.drag.z = void 0  //清空    
+            drag(e) //触屏点击时更新的pointer直接用一次drag
             console.log('preparePan '   )
         }
         
@@ -380,9 +377,16 @@ export class FirstPersonControls extends EventDispatcher {
             this.setCurrentViewport(e)
             prepareRotate(e)
         })
+        
+        
+        //注意,每次增减指头都会修改pointer,需要更新下状态
         this.viewer.addEventListener('global_touchstart', (e)=>{
-            if(this.enabled && e.touches.length==2){//只监听开头两个指头
+            if(!this.enabled)return
+
+            if(e.touches.length==2){//只监听开头两个指头
                 prepareScale(e)
+            }else if(e.touches.length>=3){
+                preparePan(e)
             }
         })
         this.viewer.addEventListener('global_touchend', (e)=>{
@@ -391,8 +395,6 @@ export class FirstPersonControls extends EventDispatcher {
                 prepareScale(e)
             }else if(e.touches.length==1){//停止scale,开始rotate
                 prepareRotate(e)
-                //this.pointerDragStart = null
-                //console.log('只剩一个', e.pointer.toArray())
             }else if(e.touches.length>=3){//重新准备下平移(因为抬起的指头可能包含平移使用的数据),否则抬起时漂移
                 preparePan(e)
             }

+ 202 - 167
src/navigation/InputHandler.js

@@ -9,7 +9,7 @@ import {KeyCodes} from "../KeyCodes.js";
 import {Utils} from "../utils.js";
 import {Buttons} from "../defines.js"; 
 import {EventDispatcher} from "../EventDispatcher.js";
-
+import Common from "../utils/Common.js";
 
 export class InputHandler extends EventDispatcher {
 	constructor (viewer,scene) {
@@ -116,14 +116,32 @@ export class InputHandler extends EventDispatcher {
             Array.from(e.changedTouches).forEach(touch=>{   //修改changedTouches的 
                 let touch_ = this.touches.find(a=>a.touch.identifier == touch.identifier)
                 if(touch_){
-                    var a = this.getPointerInViewport(touch.pageX, touch.pageY, this.dragViewport||viewport, new THREE.Vector2) 
+                    let a = this.getPointerInViewport(touch.pageX, touch.pageY, this.dragViewport||viewport, new THREE.Vector2) 
                     touch_.pointer = a.pointer.clone()
                     viewport = a.viewport;  camera = a.camera
                 }
                 
             })
             
-            this.pointer = this.touches[0].pointer.clone() //更新,使用当前touches中的第一个
+            
+            
+            //使用当前touches的平均
+            if(e.touches.length > 1){
+                let pageX = Common.average(e.touches, "pageX")
+                let pageY = Common.average(e.touches, "pageY")
+                let a = this.getPointerInViewport(pageX, pageY, viewport, new THREE.Vector2) 
+                this.pointer.copy(a.pointer)
+                //console.log('updateTouchesInfo', this.pointer.clone())
+                
+            }else{
+                this.pointer = this.touches[0].pointer.clone() //更新,使用当前touches中的第一个
+                
+                 
+            }
+                
+            
+            
+            
             //console.log('更新pointer1',this.pointer.toArray())
             return  {viewport,  camera/* , pointer:this.pointer  */}
         }
@@ -135,15 +153,18 @@ export class InputHandler extends EventDispatcher {
 
 		e.preventDefault(); 
        
-		if (e.touches.length === 1 || !this.drag) { //!this.drag代表一次性下了两个指头
+		/* if (e.touches.length === 1 || !this.drag) { //!this.drag代表一次性下了两个指头
 			let rect = this.domElement.getBoundingClientRect();
 			let x = e.touches[0].pageX 
 			let y = e.touches[0].pageY   
             this.dealPointerDown(x,y,e,true) 
-		}else{
+		}else{ 
             this.updateTouchesInfo(e)
-        } 
-           
+            this.drag.end.copy(this.pointer)
+        }  */
+        
+        
+        this.dealPointerDown(e,true) 
         
 		
 		this.viewer.dispatchEvent($.extend(  
@@ -168,18 +189,21 @@ export class InputHandler extends EventDispatcher {
 
 		e.preventDefault();
         
-		if (e.touches.length === 1) {
+		/* if (e.touches.length === 1) {
 			let rect = this.domElement.getBoundingClientRect();
 			let x = e.touches[0].pageX;
 			let y = e.touches[0].pageY;
  
-            this.dealPointerMove(x, y, e, true) //究竟多指要不要也执行dealPointerMove
+            
             
 		}else{ 
             this.updateTouchesInfo(e)
             this.drag.pointerDelta.subVectors(this.pointer,  this.drag.end)
             this.drag.end.copy(this.pointer)
         }
+         */
+        
+        this.dealPointerMove(e, true) 
         
         
         
@@ -209,18 +233,19 @@ export class InputHandler extends EventDispatcher {
 
 
         
-        if (e.touches.length === 0) {
+        /* if (e.touches.length === 0) {
 			let rect = this.domElement.getBoundingClientRect();
 			let x = e.changedTouches[0].pageX   //万一一次松开两个指头的怎么办
 			let y = e.changedTouches[0].pageY  
 			
             this.dealPointerUp(x,y,e,true)
              
-		}else if(e.touches.length == 1){ 
-            this.drag.end.copy(this.pointer)
-            
-            
-        }  
+		}else { 
+            this.drag.end.copy(this.pointer) 
+        }  */ 
+        this.dealPointerUp(e,true)
+        
+        
         
         this.viewer.dispatchEvent($.extend(  
             this.getEventDesc(e,true),
@@ -285,7 +310,7 @@ export class InputHandler extends EventDispatcher {
 
 		e.preventDefault();
 	}
-
+ 
 	onDoubleClick (e) {
 		if (this.logMessages) console.log(this.constructor.name + ': onDoubleClick');
 
@@ -324,7 +349,7 @@ export class InputHandler extends EventDispatcher {
 
 
 
-    dealPointerDown(x,y,e,isTouch){
+    dealPointerDown(e,isTouch){
         e.preventDefault(); 
         
         //重新获取一下pointer, 因点击了浏览器的按钮展开列表时 move回来不会触发onmousemove,所以pointer是旧的
@@ -338,7 +363,8 @@ export class InputHandler extends EventDispatcher {
              
              
         }else{
-            var  {  camera, viewport  } = this.getPointerInViewport(x, y ) 
+            
+            var  {  camera, viewport  } = this.getPointerInViewport(e.clientX, e.clientY ) 
         }
         
         
@@ -355,55 +381,49 @@ export class InputHandler extends EventDispatcher {
         
         if(!viewport)return 
     
-		
-         
-        //this.onMouseMove(e)//add 重新获取一下mouse, 因在此前canvas可能失去侦听(不记得是什么了。如果一定要的话再写个加侦听的函数,但是直接调用mousemove的话会发送drag,导致magnifier停止渲染)
-        
-		let consumed = false;
-		let consume = () => { return consumed = true; };
-		//if (this.hoveredElements.length === 0) {
-			this.viewer.dispatchEvent($.extend(  
+		if(!isTouch || e.touches.length == 1){ 
+       
+            let consumed = false;
+            let consume = () => { return consumed = true; };
+            //if (this.hoveredElements.length === 0) {
+            this.viewer.dispatchEvent($.extend(  
                 this.getEventDesc(e,isTouch),
                     {
                         type: 'global_mousedown' 
                     } 
             ));
-			
-		 
-			for(let hovered of this.hoveredElements){
-				let object = hovered.object;
-				object.dispatchEvent({
-					type: 'mousedown',
-					viewer: this.viewer,
-					consume: consume
-				});
-
-				if(consumed){
-					break;
-				}
-			}
-		 
-
-
-        
-        
-
-		if (!this.drag) {
-			let target = (isTouch||e.button == THREE.MOUSE.LEFT) && this.hoveredElements.find(el => (//只有左键能拖拽
-					el.object._listeners &&
-					el.object._listeners['drag'] &&
-					el.object._listeners['drag'].length > 0));
+            
+         
+            for(let hovered of this.hoveredElements){
+                let object = hovered.object;
+                object.dispatchEvent({
+                    type: 'mousedown',
+                    viewer: this.viewer,
+                    consume: consume
+                });
+
+                if(consumed){
+                    break;
+                }
+            }
+          
 
-			if (target) {
-				this.startDragging(target.object, {location: target.point});
-			} else {
-				this.startDragging(null);
-			}
-		}
-        
+            
+        }
+        if (!this.drag) {
+            let target = (isTouch||e.button == THREE.MOUSE.LEFT) && this.hoveredElements.find(el => (//只有左键能拖拽
+                    el.object._listeners &&
+                    el.object._listeners['drag'] &&
+                    el.object._listeners['drag'].length > 0));
+
+            if (target) {
+                this.startDragging(target.object, {location: target.point});
+            } else {
+                this.startDragging(null);
+            }
+        }
          
        
-       
         this.drag.intersectStart = this.intersectPoint;
         this.mouseDownMouse = this.mouse.clone()
         this.dragViewport = this.hoverViewport;
@@ -415,7 +435,7 @@ export class InputHandler extends EventDispatcher {
 	onMouseDown (e) {
          
 		if (this.logMessages) console.log(this.constructor.name + ': onMouseDown');
-        this.dealPointerDown(e.clientX, e.clientY,e)
+        this.dealPointerDown(e)
 		 
 	}
 
@@ -448,11 +468,19 @@ export class InputHandler extends EventDispatcher {
 
 
 
-    dealPointerUp(x,y,e,isTouch){
-        
+    dealPointerUp(e,isTouch){
+         
         if(!this.drag){// 在canvas外mousedown
             return 
         }
+        
+        this.drag.end.copy(this.pointer) 
+        
+        if(isTouch && e.touches.length >= 1){ 
+            return
+        }
+        
+        
 		if (this.logMessages) console.log(this.constructor.name + ': onMouseUp');
 
 		e.preventDefault();
@@ -593,7 +621,7 @@ export class InputHandler extends EventDispatcher {
     }
     
 	onMouseUp (e) {
-        this.dealPointerUp(e.clientX, e.clientY, e )
+        this.dealPointerUp( e )
 	}
 
 
@@ -668,7 +696,7 @@ export class InputHandler extends EventDispatcher {
         
         if(viewport.camera.type == 'OrthographicCamera'/*  == 'mapViewport' */){ 
             let pos3d = new THREE.Vector3(this.pointer.x,this.pointer.y,-1).unproject(viewport.camera); //z:-1朝外   
-            pos3d.setZ(viewer.bound.boundingBox.min.z + 0.2) //大概放地面上
+             
             if(!intersectPoint){
                 intersectPoint = {}
             }   
@@ -712,157 +740,164 @@ export class InputHandler extends EventDispatcher {
     
     
     onMouseMove (e) { 
-        return this.dealPointerMove(e.clientX, e.clientY, e )
+        return this.dealPointerMove( e )
     }
 
-    dealPointerMove(x, y, e, isTouch){ 
+    dealPointerMove(e, isTouch){ 
         if(isTouch){
             var  {  camera, viewport  } = this.updateTouchesInfo(e) 
         }else{
             
-            var  {  camera, viewport  } = this.getPointerInViewport(x, y ,  this.dragViewport) 
+            var  {  camera, viewport  } = this.getPointerInViewport(e.clientX, e.clientY,  this.dragViewport) 
         }
          
 		this.hoverViewport = viewport
         if(!viewport)return//刚变化viewport时会找不到
-        
-        
-		let hoveredElements = this.getHoveredElements(); 
-		if(hoveredElements.length > 0){
-			let names = hoveredElements.map(h => h.object.name).join(", ");
-			if (this.logMessages) console.log(`${this.constructor.name}: onMouseMove; hovered: '${names}'`);
-		}
+         
+		
         let intersectPoint = this.getIntersect(viewport,camera,e.onlyGetIntersect)
         if(e.onlyGetIntersect){ 
             return intersectPoint
         }
         e.preventDefault();
         
- 
-		if (this.drag) {//有拖拽(不一定拖拽了物体, 也不一定按下了鼠标)
-			this.drag.mouse = isTouch ? 1 : e.buttons; 
+        
+        let hoveredElements = []
+        
+            
+        
+        if (this.drag) {//有拖拽(不一定拖拽了物体, 也不一定按下了鼠标)
+            this.drag.mouse = isTouch ? 1 : e.buttons; 
             //add:
             //this.drag.pointer = this.pointer.clone();
             //this.drag.hoverViewport = this.hoverViewport
             this.drag.pointerDelta.subVectors(this.pointer,  this.drag.end)
-			this.drag.end.copy(this.pointer)
+            this.drag.end.copy(this.pointer)
             
             
-			if (this.drag.object && (e.buttons == Buttons.NONE || !this.drag.notPressMouse )){//如果是本不需要按鼠标的拖拽,但按下了鼠标,就不执行这段(改为拖拽场景,如添加测量时突然拖拽画面)
-				if (this.logMessages) console.log(this.constructor.name + ': drag: ' + this.drag.object.name);
-				
+            if (this.drag.object && (e.buttons == Buttons.NONE || !this.drag.notPressMouse )){//如果是本不需要按鼠标的拖拽,但按下了鼠标,就不执行这段(改为拖拽场景,如添加测量时突然拖拽画面)
+                if (this.logMessages) console.log(this.constructor.name + ': drag: ' + this.drag.object.name);
+                
                 this.drag.object.dispatchEvent($.extend(  
-					this.getEventDesc(e,isTouch),
+                    this.getEventDesc(e,isTouch),
                     {
                         type: 'drag',  //拖拽物体
                     }
                 ))
                 
                 
-			} else {
+            } else {
                 
                 
                 
-				if (this.logMessages) console.log(this.constructor.name + ': drag: ');
+                if (this.logMessages) console.log(this.constructor.name + ': drag: ');
 
-				let dragConsumed = false; 
-				this.viewer.dispatchEvent($.extend(  
-					this.getEventDesc(e,isTouch),   
+                let dragConsumed = false; 
+                this.viewer.dispatchEvent($.extend(  
+                    this.getEventDesc(e,isTouch),   
                     {
                         type: 'global_drag',        //拖拽画面
                         consume: () => {dragConsumed = true;}
                     }
                 ))
                  
-				
-			}
-		} 
-        
-        if(!this.drag || this.drag.notPressMouse ){
-         
-			let curr = hoveredElements.map(a => a.object).find(a => true);
-			let prev = this.hoveredElements.map(a => a.object).find(a => true);
-
-			if(curr !== prev){
-				if(curr){
-					if (this.logMessages) console.log(`${this.constructor.name}: mouseover: ${curr.name}`);
-					curr.dispatchEvent({
-						type: 'mouseover',
-						object: curr,
-					});
-				}
-				if(prev){
-					if (this.logMessages) console.log(`${this.constructor.name}: mouseleave: ${prev.name}`);
-					prev.dispatchEvent({
-						type: 'mouseleave',
-						object: prev,
-					});
-				}
-			}
-
-			if(hoveredElements.length > 0){
-				let object = hoveredElements
-					.map(e => e.object)
-					.find(e => (e._listeners && e._listeners['mousemove']));
-				
-				if(object){
-					object.dispatchEvent({
-						type: 'mousemove',
-						object: object
-					});
-				}
-			}
-
-        }
-
-        //仅在鼠标不按下时更新:
-        if(!this.drag){
-            let handleState = viewer.modules.Alignment.handleState
-            if(viewport.alignment && handleState && viewport.alignment[handleState]){
-                if(handleState == 'translate'){
-                    if( intersectPoint && intersectPoint.location ){ 
-                        viewer.dispatchEvent({
-                            type : "CursorChange", action : "add",  name:"movePointcloud"
-                        })
-                    }else{
-                        viewer.dispatchEvent({
-                            type : "CursorChange", action : "remove",  name:"movePointcloud"
-                        })
-                    }
-                }else if(handleState == 'rotate'){ 
-                    if( intersectPoint && intersectPoint.location ){ 
-                        viewer.dispatchEvent({
-                            type : "CursorChange", action : "add",  name:"rotatePointcloud"
-                        })
-                    }else{
-                        viewer.dispatchEvent({
-                            type : "CursorChange", action : "remove",  name:"rotatePointcloud"
-                        })
-                    }
-                }  
+                
             }
         } 
-            
-		 
         
-         
-        
-		
         
+        if(!isTouch || e.touches.length == 1){ 
         
-        this.viewer.dispatchEvent($.extend(  
-            this.getEventDesc(e,isTouch),
-            {
-                type: 'global_mousemove',
-                
+            if(!this.drag || this.drag.notPressMouse ){
+                hoveredElements = this.getHoveredElements(); 
+                if(hoveredElements.length > 0){
+                    let names = hoveredElements.map(h => h.object.name).join(", ");
+                    if (this.logMessages) console.log(`${this.constructor.name}: onMouseMove; hovered: '${names}'`);
+                }
+            
+                let curr = hoveredElements.map(a => a.object).find(a => true);
+                let prev = this.hoveredElements.map(a => a.object).find(a => true);
+
+                if(curr !== prev){
+                    if(curr){
+                        if (this.logMessages) console.log(`${this.constructor.name}: mouseover: ${curr.name}`);
+                        curr.dispatchEvent({
+                            type: 'mouseover',
+                            object: curr,
+                        });
+                    }
+                    if(prev){
+                        if (this.logMessages) console.log(`${this.constructor.name}: mouseleave: ${prev.name}`);
+                        prev.dispatchEvent({
+                            type: 'mouseleave',
+                            object: prev,
+                        });
+                    }
+                }
+
+                if(hoveredElements.length > 0){
+                    let object = hoveredElements
+                        .map(e => e.object)
+                        .find(e => (e._listeners && e._listeners['mousemove']));
+                    
+                    if(object){
+                        object.dispatchEvent({
+                            type: 'mousemove',
+                            object: object
+                        });
+                    }
+                }
+
             }
-        ))
-        
-        
+
+            //仅在鼠标不按下时更新:
+            if(!this.drag){
+                let handleState = viewer.modules.Alignment.handleState
+                if(viewport.alignment && handleState && viewport.alignment[handleState]){
+                    if(handleState == 'translate'){
+                        if( intersectPoint && intersectPoint.location ){ 
+                            viewer.dispatchEvent({
+                                type : "CursorChange", action : "add",  name:"movePointcloud"
+                            })
+                        }else{
+                            viewer.dispatchEvent({
+                                type : "CursorChange", action : "remove",  name:"movePointcloud"
+                            })
+                        }
+                    }else if(handleState == 'rotate'){ 
+                        if( intersectPoint && intersectPoint.location ){ 
+                            viewer.dispatchEvent({
+                                type : "CursorChange", action : "add",  name:"rotatePointcloud"
+                            })
+                        }else{
+                            viewer.dispatchEvent({
+                                type : "CursorChange", action : "remove",  name:"rotatePointcloud"
+                            })
+                        }
+                    }  
+                }
+            } 
+                
+             
+            
+             
+            
+            
+            
+            
+            this.viewer.dispatchEvent($.extend(  
+                this.getEventDesc(e,isTouch),
+                {
+                    type: 'global_mousemove',
+                    
+                }
+            ))
+            this.hoveredElements = hoveredElements
+        }
         
 		
 
-		this.hoveredElements = hoveredElements;
+		
 	}
 	
 	onMouseWheel(e){

+ 48 - 14
src/navigation/Reticule.js

@@ -6,6 +6,9 @@ import math from '../utils/math.js'
 
 let texLoader = new THREE.TextureLoader()
 let defaultOpacity =  0.7
+ 
+
+
 //鼠标指示小圆片 
 export default class Reticule extends THREE.Mesh{
     constructor(viewer){
@@ -102,20 +105,39 @@ export default class Reticule extends THREE.Mesh{
          
     }
 
-    hide(){
-         
-        this.hidden || (this.hidden = !0,
-        transitions.start(lerp.property(this.material , "opacity", 0), 500))
+    hide(duration = 500){
+        if(this.hidden)return
+        
+ 
+        
+        this.hidden = !0 
+        transitions.start(lerp.property(this.material , "opacity", 0), duration)
+             
         this.dispatchEvent({type:'update', visible:false})
+        
+        setTimeout(()=>{
+            this.dispatchEvent({type:'update', visible:false})
+        },duration)
+        
     }
 
-    show(){
-        if(!this.visible)return
+    show(duration = 300){
+        if(!viewer.getObjVisiByReason(this, 'force'))return
         //console.log("show Reticule")
-        this.hidden = !1,
-        this.material.opacity <= 0 && transitions.start(lerp.property(this.material, "opacity", defaultOpacity), 300)
+        this.hidden = !1
+        
+        if(this.material.opacity <= 0){
+            transitions.start(lerp.property(this.material, "opacity", defaultOpacity), duration)
+        
+            this.dispatchEvent({type:'update', visible:true})
+            
+            setTimeout(()=>{
+                this.dispatchEvent({type:'update', visible:false})
+            },duration)
+                
+        }
+        
         
-        this.dispatchEvent({type:'update', visible:true})
         
     }
 
@@ -128,7 +150,10 @@ export default class Reticule extends THREE.Mesh{
     updateScale(viewport){
         let s, camera = viewport.camera
         if(camera.type == "OrthographicCamera"){
-            s = math.getScaleForConstantSize({width2d:400, position:this.position, camera, resolution:viewport.resolution/* 2 */} )
+            
+            var sizeInfo = this.state.cross ? {width2d:400} : {minSize : 100,  maxSize : 400,   nearBound : 100 , farBound :  700}
+            s = math.getScaleForConstantSize($.extend(   sizeInfo ,  
+            {position:this.position, camera, resolution:viewport.resolution/* 2 */} ))
             
         }else{
             
@@ -155,9 +180,11 @@ export default class Reticule extends THREE.Mesh{
         if(!matrix){ 
             this.updateScale(viewport)
             this.updateMatrix(); 
+            //this.updateMatrixWorld()
             this.matrixMap.set(viewport, this.matrix.clone())
         }else{
             this.matrix.copy(matrix) 
+            //this.updateMatrixWorld()
         }
         
     }
@@ -185,12 +212,18 @@ export default class Reticule extends THREE.Mesh{
             }
              
             
-             
-            this.show();
+            //地图上要瞬间变化 , 因为要使needRender为true很麻烦
+            this.show(atMap ? 0 : 300);
+            
+            if(atMap){
+                this.direction = normal.clone()
+            }else{
+                this.direction = this.direction.multiplyScalar(.8); 
+                this.direction.add(normal.clone().multiplyScalar(.2));
+            }
             
-            this.direction = this.direction.multiplyScalar(.8); 
-            this.direction.add(normal.clone().multiplyScalar(.2));
             this.position.copy(location).add(normal.clone().multiplyScalar(.01));
+            this.updateMatrix();  //lookAt之前要保证得到matrix
             this.lookAt(this.position.clone().add(this.direction));
             
             
@@ -200,6 +233,7 @@ export default class Reticule extends THREE.Mesh{
             
             {//存储matrix,节省计算
                 this.updateMatrix(); 
+                //this.updateMatrixWorld()
                 this.matrixMap.clear();//重新计算
                 this.matrixMap.set(viewport, this.matrix.clone())
                 //别处会updateMatrixWorld

+ 16 - 8
src/navigation/RouteGuider.js

@@ -4,7 +4,7 @@ import {Utils} from "../utils.js";
 import { EventDispatcher } from "../EventDispatcher.js";
 import Sprite from '../viewer/Sprite'
 import Common from "../utils/Common.js";
-
+import browser from '../utils/browser'
 const texLoader = new THREE.TextureLoader()
 const arrowSpacing = 1 //间隔
 const arrowSize = arrowSpacing * 0.5
@@ -37,10 +37,15 @@ export class RouteGuider extends EventDispatcher{
         viewer.mapViewer.addEventListener('camera_changed', e => {
             if(!this.routeStart || !this.routeEnd) return   
             var camera = e.viewport.camera
-            if(camera.zoom != zoom){ 
-                this.updateMapArrows(true)
-                zoom = camera.zoom          //这个就不延时了,因为滚轮不怎么连续
-            } 
+           
+            Common.intervalTool.isWaiting('routeCameraInterval', ()=>{ //延时update,防止卡顿
+                if(camera.zoom != zoom){ 
+                    console.log('updateMapArrows')
+                    this.updateMapArrows(true)
+                    zoom = camera.zoom         
+                    return true 
+                } 
+            }, browser.isMobile()?500:200)
         })
         
    
@@ -212,8 +217,8 @@ export class RouteGuider extends EventDispatcher{
     
     setRouteStart(pos, dealMapZ   ){
         if(this.routeStart && pos && this.routeStart.equals(pos)) return //可能重复设置
-        this.routeStart = pos && new THREE.Vector3().copy(pos) 
-        //if(dealMapZ && this.routeStart) this.routeStart.setZ(0)//后端要求设置为0,但设置后反而得不到了。直接用的是接近boundingbox.min.z的值
+        this.routeStart = pos && new THREE.Vector3().copy(pos)  
+        if(dealMapZ && this.routeStart) this.routeStart.setZ(viewer.bound.boundingBox.min.z + 0.2) 
         console.log('setRouteStart',this.routeStart&&this.routeStart.toArray()) 
         
         
@@ -231,7 +236,7 @@ export class RouteGuider extends EventDispatcher{
     setRouteEnd(pos, dealMapZ   ){ 
         if(this.routeEnd && pos && this.routeEnd.equals(pos)) return 
         this.routeEnd = pos && new THREE.Vector3().copy(pos)
-        //if(dealMapZ && this.routeEnd) this.routeEnd.setZ(0)//后端要求设置为0
+        if(dealMapZ && this.routeEnd) this.routeEnd.setZ(viewer.bound.boundingBox.min.z + 0.2) 
         console.log('setRouteEnd',this.routeEnd&&this.routeEnd.toArray())        
         
         
@@ -465,6 +470,9 @@ export class RouteGuider extends EventDispatcher{
         if(this.route.length == 0)return  
         var zoom = viewer.mapViewer.camera.zoom
         let count = Math.max(2,Math.round(this.routeLength * zoom  / arrowSpacing / 25))//点数
+        
+        if(count == this.mapPoints.length+1)return//没变
+
         const mapPoints = this.curve.getSpacedPoints( count ); 
         mapPoints.splice(0,1);//去掉首尾
         mapPoints.pop() 

+ 33 - 40
src/settings.js

@@ -102,59 +102,40 @@ const config = {//配置参数   不可修改
         },
         panorama:{//显示全景时的漫游。因为点只能显示1个像素的大小,所以必须很密集,但又要限制点的数量
             maxLevelPercent: 1,
-            pointBudget : browser.isMobile ?  0.1*1000*1000 :  0.25*1000*1000,  //点云总最大数
+            pointBudget : browser.isMobile() ?  0.1*1000*1000 :  0.25*1000*1000,  //点云总最大数
         }, 
         fourViewports:{//分四屏时防止卡顿
-            maxLevelPercent: 0.5,  
-            pointBudget :0.25*1000*1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000)
-        },
+            maxLevelPercent: 0.4,  
+            pointBudget :1*1000*1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000)
+        }, 
+        fourViewportsMain:{//分四屏时防止卡顿
+            maxLevelPercent: 0.8,  
+            pointBudget :1*1000*1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000)
+        }
+        ,
         low:{//highPerformance
             maxLevelPercent: 0.4, //最小为0
+            percentByUser:true, //如果用户定义了percent,使用用户的
             pointBudget :1*1000*1000,
         }, 
         middle:{//balanced  //不同场景相同级别所产生的numVisibleNodes和numVisiblePoints不同,如果分层比较细,可能要到level8才能看清,那么level5看到的点就很大且很少,如隧道t-e2Kb2iU
             maxLevelPercent: 0.7,
+            percentByUser:true,
             pointBudget:4*1000*1000,
         },
         high:{//highQuality
             maxLevelPercent: 1, 
+            percentByUser:true,
             pointBudget:8*1000*1000,
         }
         //browser.isMobile() 时要不要限制下pointBudget,还是让用户自己调低质量?
         //minNodeSize?
         //数值由testLevelSteps得来,其中nodeMaxLevel为2时,low和middle的都是1,如果真有这么低的点云就单独处理下。
+        //多个viewport尽量保证pointBudget一样,或者pointBudget不能太低于所需,否则会反复加载了又清除
     },  
+     
     
-    
-    
-    /* pointDensity:{
-        panorama:{//显示全景时的漫游。因为点只能显示1个像素的大小,所以必须很密集,但又要限制点的数量
-            maxLevelPercent: 1,
-            pointBudget :   0.1*1000*1000,  //点云总最大数
-        },
-        fourViewports:{//分四屏时防止卡顿
-            maxLevelPercent: 0.1,  
-            pointBudget :0.01*1000*1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000)
-        },
-        low:{//highPerformance
-            maxLevelPercent: 0.2, //最小为0
-            pointBudget :0.01*1000*1000,
-        }, 
-        middle:{//balanced  //不同场景相同级别所产生的numVisibleNodes和numVisiblePoints不同,如果分层比较细,可能要到level8才能看清,那么level5看到的点就很大且很少,如隧道t-e2Kb2iU
-            maxLevelPercent: 0.3,
-            pointBudget:0.01*1000*1000,
-        },
-        high:{//highQuality
-            maxLevelPercent: 0.5, 
-            pointBudget:0.01*1000*1000,
-        }
-        //browser.isMobile() 时要不要限制下pointBudget,还是让用户自己调低质量?
-        //minNodeSize?
-        //数值由testLevelSteps得来,其中nodeMaxLevel为2时,low和middle的都是1,如果真有这么低的点云就单独处理下。
-    },
-     */
-    
-    
+     
     clip:{
         color: '#FFC266', //map
         
@@ -162,12 +143,24 @@ const config = {//配置参数   不可修改
     
     
     measure:{
-        lineColor:"#00c7b2",
-        highLightColor:'#FFFFFF',//'#02e4cc', //hover时线条和mainLabel颜色
+        color:'#00C8AF',
+        default:{
+            color:"#64C8BB",//"#00c7b2",
+            opacity:0.7
+        },
+        highlight:{
+            color:'#00C8AF',//"#00c7b2",
+            opacity:1
+        },
+        guide:{
+            color:'#FFFFFF', 
+            opacity:1
+        }
+        ,   
         backColor:'#333333',
-        guideLineColor:"#FFFFFF",
+         
         lineWidth: 4,
-        labelOpacity:0.6,
+       
         textColor: "#FFFFFF"
         
     },
@@ -347,13 +340,13 @@ let settings = {//设置   可修改
         activationThreshold: 1.1,
     },
     
-    navTileClass:'2k',  //默认加载到
+    navTileClass: browser.isMobile() ? '1k' : '2k',  //默认加载到
     tileClass:'4k',     //最高可达
     /* loadTilesWhenUnfocus:false, //页面unfocus时也仍在加载tiles
     loadPointsWhenUnfocus:true, //页面unfocus时也仍在加载点云 */
    
     //initialShowPano:true
-    
+    drawEntityData: true,
     
     zoomFromPointert:{//定点缩放(包括点云模式、全景模式、地图)
         whenPanos:true,

+ 41 - 13
src/start.js

@@ -40,8 +40,8 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
             //$("#menu_appearance").next().show();
             $("#menu_tools").next().show();
             $("#menu_scene").next().show();
-            //$("#siteModel").show();
-            $("#alignment").show();
+            $("#siteModel").show();
+            //$("#alignment").show();
             viewer.toggleSidebar();
         });
         Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
@@ -87,8 +87,30 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                     }
                     
                     
+                    /* setTimeout(()=>{
+                        if( Potree.settings.number == 't-YLZ5XAALl7'  ||  't-e2Kb2iU' ){
+                             
+                            let transform = {
+                                't-YLZ5XAALl7' : {
+                                    rotation : [0,  0,    0.002326740152215126],
+                                    position : [0.421017820930033,   -0.22730084679456727,   0.0068952417582]
+                                },
+                                't-e2Kb2iU' : {
+                                    rotation : [0.06595546058095993,  -0.026986798620029413,  0.8429662590573239],
+                                    position : [ -502.6216232179794, 1051.5690392495885, 15.48490744053752]
+                                },
+                            }
+                             
+                            var path = `${Potree.resourcePath}/models/${Potree.settings.number}/`
+                            
+                            viewer.loadObj({ 
+                                objurl: path+'pipe.obj',
+                                mtlurl: path+'pipe.mtl',
+                                transform : transform[Potree.settings.number]
+                            })
+                        } 
+                    },1000) */
                     
-                    viewer.addObjectTest()
                     
                     viewer.emit('allLoaded')
                 });
@@ -102,18 +124,21 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                     center
                 )
                 
-                if(!Potree.settings.isOfficial){
+                //if(!Potree.settings.isOfficial){
+                /* if(number == 't-e2Kb2iU') {   
                     setTimeout(//暂时延迟,等focus第一个点之后
                         ()=>{
-                           // viewer.loadProject(Potree.scriptPath + "/data/t-iksBApb/potree.json5") 
+                            viewer.loadProject(Potree.scriptPath + "/data/"+ number +"/potree.json5", ()=>{
+                                viewer.scene.cameraAnimations[0].play()
+                            })  
                         },
-                    500)
-                }   
+                    3000) 
+                }  */  
                 
                 viewer.dispatchEvent({type:'loadPointCloudDone'})
             
                 if(!Potree.settings.UserPointDensity){
-                    Potree.settings.UserPointDensity = 'middle' 
+                    Potree.settings.UserPointDensity = 'high'//'middle' 
                 }
                 
                 
@@ -157,8 +182,11 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
         data.forEach((dataset,index)=>{
             
             //dataset.location = [ 113.60182446595765,22.364155116865753,0]
-            //dataset.orientation = -0.9
-             
+            /* if(number == 't-e2Kb2iU') {    
+                dataset.orientation = 0
+            } */
+            
+            
             if(!viewer.transform){//拿任意一个数据集作为基准。它的位置就会是000  (第一个数据集应该一直就是初始数据集吧?)
                 var locationLonLat = dataset.location.slice(0,2)
                 proj4.defs("NAVVIS:TMERC", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15));
@@ -180,10 +208,10 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
             } 
             
             if(!ifReload){
-                var cloudPath = `https://${Potree.config.urls.prefix}/${Potree.settings.webSite}/${number}/data/${dataset.name}/webcloud/cloud.js` 
-                var timeStamp = dataset.createTime.replace(/[^0-9]/ig,'');  //每重算一次后缀随createTime更新一次 
+                var cloudPath = `https://${Potree.config.urls.prefix}/${Potree.settings.webSite}/${number}/data/${number}/webcloud/cloud.js` 
+                var timeStamp = dataset.createTime ? dataset.createTime.replace(/[^0-9]/ig,'') : '';  //每重算一次后缀随createTime更新一次 
                 
-                Potree.loadPointCloud(cloudPath, dataset.name , timeStamp, e => {
+                Potree.loadPointCloud(cloudPath, dataset.sceneCode || dataset.name , timeStamp, e => {
                     let scene = viewer.scene;
                     let pointcloud = e.pointcloud; 
                     let config = Potree.config.material

+ 2 - 1
src/utils.js

@@ -397,7 +397,8 @@ export class Utils {
 	}
 
 	static getMousePointCloudIntersection (viewport, mouse, pointer, camera, viewer, pointclouds, params = {}) {
-		
+		if(!pointclouds)return
+        
 		let renderer = viewer.renderer;
 		
 		 

+ 13 - 0
src/utils/Common.js

@@ -47,6 +47,19 @@ var Common = {
             return e.value
         })
     },
+    
+    average: function (e, t) {
+        if (0 === e.length)
+            return null;
+        for (var i = 0, n = 0, r = 0; r < e.length; r++) {
+            var o = t ? e[r][t] : e[r];
+            i += o,
+                n++
+        }
+        return i / n
+    },
+    
+    
     //---------------------------
     
     

+ 30 - 25
src/utils/DrawUtil.js

@@ -15,15 +15,26 @@ var LineDraw = {
     
 	createLine: function (posArr, o={}) {
         //多段普通线  (第二个点和第三个点之间是没有线段的, 所以不用在意线段顺序)
-        var mat = o.mat || new THREE[o.deshed ? "LineDashedMaterial" : "LineBasicMaterial"]({
-			lineWidth: o.lineWidth || 1,
-			//windows无效。 似乎mac/ios上粗细有效 ? 
-			color: o.color || defaultColor,
-			transparent: o.dontAlwaysSeen ? false : true,
-			depthTest: o.dontAlwaysSeen ? true : false,
-            dashSize : o.dashSize || 0.1,
-            gapSize: o.gapSize || 0.1
-		})
+        var mat
+        if(o.mat){
+            mat = o.mat
+        }else{
+            let prop = {
+                lineWidth: o.lineWidth || 1,
+                //windows无效。 似乎mac/ios上粗细有效 ? 
+                color: o.color || defaultColor,
+                transparent: o.dontAlwaysSeen ? false : true,
+                depthTest: o.dontAlwaysSeen ? true : false, 
+            }
+            if(o.deshed ){
+                prop.dashSize = o.dashSize || 0.1,
+                prop.gapSize = o.gapSize || 0.1
+            }
+            mat = new THREE[o.deshed ? "LineDashedMaterial" : "LineBasicMaterial"](prop) 
+        }
+         
+        
+        
         var line = new THREE.LineSegments(new THREE.BufferGeometry, mat);
 		line.renderOrder = o.renderOrder || 4
   
@@ -256,31 +267,25 @@ var MeshDraw = {
 	},
     
     
-    getExtrudeGeo:  function(points, height, extrudePath, tension, closed){//获得挤出棱柱,可以选择传递height,或者extrudePath
-        var shape = this.getShape(points) //points是横截面 [vector2,...]
+    getExtrudeGeo:  function(points, holes, options={}){//获得挤出棱柱,可以选择传递height,或者extrudePath
+        var shape = this.getShape(points, holes) //points是横截面 [vector2,...]
         
-        if(extrudePath){// 路径 :[vector3,...]
-            
+        if(options.extrudePath ){// 路径 :[vector3,...]
             
             var length = extrudePath.reduce((total, currentValue, currentIndex, arr)=>{
                 if(currentIndex == 0)return 0
                 return total + currentValue.distanceTo(arr[currentIndex-1]);
             },0)
-            extrudePath = new THREE.CatmullRomCurve3(extrudePath, closed ,  'catmullrom'  /* 'centripetal' */  , tension)
+            options.extrudePath = new THREE.CatmullRomCurve3(extrudePath, options.closed ,  'catmullrom'  /* 'centripetal' */  , options.tension)
             
            
         }
-        
-        
-        
-        
-        
-        var extrudeSettings = {
-            steps: height != void 0 ? 1 : Math.round(length/0.3), 
-            depth: height,
-            extrudePath,
-            
-        };
+         
+        var extrudeSettings = $.extend(options,{
+            steps: options.steps != void 0 ? options.steps : ( options.extrudePath ? Math.round(length/0.3) : 1),
+            bevelEnabled: false, //不加的话,height为0时会有圆弧高度
+            //depth
+        }) 
         var geometry = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings );
         return geometry;
     },

+ 26 - 21
src/utils/Measure.js

@@ -13,9 +13,11 @@ import {ctrlPolygon} from './ctrlPolygon'
 
  
 let texLoader = new THREE.TextureLoader()  
-let color = new THREE.Color(config.measure.lineColor)
-let textColor = new THREE.Color(config.measure.textColor)
-let highLightColor = new THREE.Color(config.measure.highLightColor)
+let defaultColor = new THREE.Color(config.measure.default.color);
+let highlightColor = new THREE.Color(config.measure.highlight.color);
+
+let color = new THREE.Color(config.measure.color)
+let textColor = new THREE.Color(config.measure.textColor) 
 var markerMats;  
 var lineMats;  
 var planeMats 
@@ -25,7 +27,7 @@ const markerSizeInfo = {
 }
 const labelSizeInfo = {width2d:200}
 const mainLabelProp = { 
-    backgroundColor: {r: color.r*255, g: color.g*255, b: color.b*255, a:config.measure.labelOpacity},
+    backgroundColor: {r: defaultColor.r*255, g: defaultColor.g*255, b: defaultColor.b*255, a:config.measure.default.opacity},
     textColor: {r: textColor.r*255, g: textColor.g*255, b: textColor.b*255, a: 1.0},
     fontsize:16, 
     useDepth : true ,
@@ -57,8 +59,7 @@ export class Measure extends ctrlPolygon{
         
         this.name = this.measureType + this.constructor.counter  //'Measure_' + this.constructor.counter;
            
-		this.color = new THREE.Color(config.measure.lineColor)//new THREE.Color(0xff0000);
-    
+	 
 		
 		this.markerLabels = [];
 		this.edgeLabels = [];
@@ -154,13 +155,13 @@ export class Measure extends ctrlPolygon{
         })
          
         this.getPoint2dInfo(this.points)
-        this.update(true)
+        this.update({ifUpdateMarkers:true})
         this.setSelected(false)//隐藏edgelabel
          
     }
  
-	update(ifUpdateMarkers) { 
-        super.update(ifUpdateMarkers)
+	update(options={}) { 
+        super.update(options)
       
         if(this.showCoordinates && this.points.length>0){
             let position = this.points[0];
@@ -666,13 +667,13 @@ export class Measure extends ctrlPolygon{
                 default:    new DepthBasicMaterial({  
                     transparent: !0,
                     opacity: 1,
-                    map: texLoader.load(Potree.resourcePath+'/textures/pic_point32.png' ), 
+                    map: texLoader.load(Potree.resourcePath+'/textures/pic_point_s32.png' ), 
                     useDepth:true 
                 }),
                 select:    new DepthBasicMaterial({   
                     transparent: !0,
                     opacity: 1,
-                    map: texLoader.load(Potree.resourcePath+'/textures/pic_point_s32.png'/* , null, null, { antialias: false } */), 
+                    map: texLoader.load(Potree.resourcePath+'/textures/pic_point32.png'/* , null, null, { antialias: false } */), 
                      
                 }),   
             }
@@ -689,7 +690,7 @@ export class Measure extends ctrlPolygon{
         if(!Measure.lineMats){
             Measure.lineMats = { 
                 edgeDefault:  LineDraw.createFatLineMat({
-                    color: color,
+                    color: config.measure.default.color,
                     dashSize: 0.5, 
                     gapSize: 0.2, 
                     lineWidth: config.measure.lineWidth,
@@ -697,16 +698,20 @@ export class Measure extends ctrlPolygon{
                     dashWithDepth :true,  // 只在被遮住的部分显示虚线,因为实线容易挡住label
                     dashed :true,   
                     dashSize : 0.04,
-                    gapSize: 0.04,                    
+                    gapSize: 0.04,    
+                    transparent: true,
+                    opacity: config.measure.default.opacity
                 }),
                 edgeSelect:  LineDraw.createFatLineMat({
-                    color: highLightColor,//'#f0ff00',
+                    color: config.measure.highlight.color,//'#f0ff00',
                     dashSize: 0.5, 
                     gapSize: 0.2, 
-                    lineWidth: config.measure.lineWidth  
+                    lineWidth: config.measure.lineWidth  ,
+                    transparent: true,
+                    opacity: config.measure.highlight.opacity
                 }),
                 guide:   LineDraw.createFatLineMat({
-                    color:config.measure.guideLineColor, 
+                    color:config.measure.guide.color, 
                     dashSize: 0.1, 
                     gapSize: 0.02,
                     dashed: true,
@@ -961,14 +966,14 @@ export class Measure extends ctrlPolygon{
 
 
 function setLabelHightState(label, state){
-    if(state){
-        //label.backgroundColor =  {r: highLightColor.r*255, g: highLightColor.g*255, b: highLightColor.b*255, a:1},
-        label.backgroundColor.a = 1
+    if(state){ 
+        label.backgroundColor =  {r: highlightColor.r*255, g: highlightColor.g*255, b: highlightColor.b*255, a:config.measure.highlight.opacity},
+        label.backgroundColor.a = config.measure.highlight.opacity
         label.sprite.material.useDepth = false;
         
     }else{
-        //label.backgroundColor = mainLabelProp.backgroundColor
-        label.backgroundColor.a = config.measure.labelOpacity
+        label.backgroundColor = mainLabelProp.backgroundColor
+        label.backgroundColor.a = config.measure.default.opacity
         label.sprite.material.useDepth = true
         
     } 

+ 3 - 10
src/utils/MeasuringTool.js

@@ -373,14 +373,7 @@ export class MeasuringTool extends EventDispatcher{
         
         let timer;
 
-
-        let continueDrag = (marker)=>{
-            timer = setTimeout(()=>{//等 drag=null之后 //右键拖拽结束后需要重新得到drag
-                this.viewer.inputHandler.startDragging(marker,
-                    {endDragFun, notPressMouse:true} 
-                ); 
-            },1)  
-        }
+ 
 
 		let endDragFun = (e) => { 
             let length = measure.points.length
@@ -405,12 +398,12 @@ export class MeasuringTool extends EventDispatcher{
                     measure.markers[length-1].visible = true;
                     
                     measure.editStateChange(true) //重新激活reticule状态
-                    continueDrag(marker) 
+                    measure.continueDrag(marker, e)    
                 } 
 				 
 			} else if (e.button === THREE.MOUSE.RIGHT ) { //触屏怎么取消?
 				if(e.pressDistance < Potree.config.clickMaxDragDis )end(e);//非拖拽的话
-                else continueDrag(e.drag.object)
+                else measure.continueDrag(null, e)     
                  
 			}
 		};

+ 40 - 17
src/utils/SplitScreen.js

@@ -56,7 +56,7 @@ var SplitScreen = {
             var widthPX = viewer.renderArea.clientWidth * widthRatio;
             var heightPX = viewer.renderArea.clientHeight * heightRatio;
             var aspect = widthPX/heightPX
-            console.log(viewer.renderArea.clientWidth,viewer.renderArea.clientHeight,aspect)
+            //console.log(viewer.renderArea.clientWidth,viewer.renderArea.clientHeight,aspect)
             var width = Math.max(boundSize[axis[0]],  boundSize[axis[1]] * aspect)
             var orthographicCamera = new THREE.OrthographicCamera(-widthPX/2, widthPX/2, heightPX/2, -heightPX/2, 0.01, 10000);
             var margin = 50 //px 
@@ -85,8 +85,15 @@ var SplitScreen = {
             },
             pointDensity : Potree.settings.pointDensity,
             displayMode : Potree.settings.displayMode,
+            
+            position: viewer.images360.position,
+			target: viewer.scene.view.getPivot(),
+              
+            //---
+            //ifShowMarker : Potree.settings.ifShowMarker,
+            
         }
-      
+        
         
         let beforeRender = function(viewport){
             viewer.scene.pointclouds.forEach(e=>{ 
@@ -95,7 +102,7 @@ var SplitScreen = {
                     //e.material.color = SplitScreen.statesBefore.mat.color
                     e.material.useFilterByNormal = false
                     //e.material.opacity = SplitScreen.statesBefore.mat.opacity 
-                    Potree.settings.pointDensity = 'fourViewports' //本来想比另外三屏高一点质量,结果发现会闪烁,因为点云加载需要时间 (navvis仿版也是一样,以后看看能否优化)
+                    Potree.settings.pointDensity = 'fourViewportsMain'/* 'fourViewports' */ //本来想比另外三屏高一点质量,结果发现会闪烁,因为点云加载需要时间 (navvis仿版也是一样,以后看看能否优化)
                 }else{ 
                     e.material.activeAttributeName = "color"
                     //e.material.color = e.color
@@ -159,9 +166,9 @@ var SplitScreen = {
         mapViewport.noPointcloud = false
         //隐藏地图游标
         viewer.updateVisible(viewer.mapViewer.cursor, 'split4Screens', false)
-        viewer.images360.panos.forEach(pano=>{
+        /* viewer.images360.panos.forEach(pano=>{
             viewer.updateVisible(pano.mapMarker, 'split4Screens', false) //希望这时候mapMarker已经建好了吧
-        })
+        }) */
             
             
          
@@ -172,7 +179,7 @@ var SplitScreen = {
         //viewer.dispatchEvent({'type': 'beginSplitView' }) 
         viewer.updateScreenSize({forceUpdateSize:true})   
         this.viewportFitBound(mapViewport, boundSize, center)
-        
+        //Potree.settings.ifShowMarker = false
         Potree.settings.displayMode = 'showPointCloud'
     },
     
@@ -182,13 +189,20 @@ var SplitScreen = {
     
     
     recoverFrom4Views: function(){
-       
-        this.unfocusViewport()
-         
+        
+        this.unfocusViewport() 
         const {width, height} = viewer.renderer.getSize(new THREE.Vector2());
         viewer.renderer.setViewport(0,0,width,height)
         viewer.renderer.setScissorTest( false );
         
+        viewer.setView({
+            position: SplitScreen.statesBefore.position,
+            target: SplitScreen.statesBefore.target,
+            duration:300,
+            callback:function(){ 
+            }
+        })
+        
         viewer.scene.pointclouds.forEach(e=>{ 
             e.material.color.set(SplitScreen.statesBefore.mat.color)
             e.material.activeAttributeName = SplitScreen.statesBefore.mat.colorType 
@@ -206,9 +220,9 @@ var SplitScreen = {
         let mapViewport = viewer.mapViewer.viewports[0]
         viewer.mapViewer.attachToMainViewer(false) 
         viewer.updateVisible(viewer.mapViewer.cursor, 'split4Screens', true)
-        viewer.images360.panos.forEach(pano=>{
+        /* viewer.images360.panos.forEach(pano=>{
             viewer.updateVisible(pano.mapMarker, 'split4Screens', true)
-        })
+        }) */
         mapViewport.noPointcloud = true
          
         this.enableMap(true)
@@ -219,7 +233,7 @@ var SplitScreen = {
         
         Potree.settings.pointDensity = SplitScreen.statesBefore.pointDensity
         Potree.settings.displayMode = SplitScreen.statesBefore.displayMode
-        
+        //Potree.settings.ifShowMarker = SplitScreen.statesBefore.ifShowMarker
         //viewer.dispatchEvent({'type': 'finishSplitView' }) 
         viewer.updateScreenSize({forceUpdateSize:true}) 
     },
@@ -324,17 +338,26 @@ var SplitScreen = {
         viewport.camera.updateProjectionMatrix()
     },
     
+     
     focusOnPointCloud:function(pointcloud){//三个屏都聚焦在这个点云 
         var boundSize = pointcloud.bound.getSize(new THREE.Vector3);
 	    var center = pointcloud.bound.getCenter(new THREE.Vector3);
-        viewer.viewports.forEach(e=>{
-            if(e.name == 'MainView')return 
-            this.viewportFitBound(e, boundSize, center)
-        })
+        this.focusOnObject(boundSize,center)
         
         
     }
-    
+    ,
+    focusOnObject:function(boundSize,center, duration=0){
+        viewer.viewports.forEach(e=>{
+            if(e.name == 'MainView'){
+                let len = boundSize.length()
+                let distance = THREE.Math.clamp(e.view.position.distanceTo(center),  len * 0.01,  len*0.3 ) //距离限制
+                viewer.focusOnObject({position:center}, 'point', duration, {distance, direction: e.view.direction} )//平移镜头
+            }else{
+                this.viewportFitBound(e, boundSize, center)
+            }
+        })
+    },
 }
 
 export default SplitScreen

+ 50 - 36
src/utils/ctrlPolygon.js

@@ -70,7 +70,7 @@ export class ctrlPolygon extends THREE.Object3D {
             this.getFacePlane()
             this.getPoint2dInfo(this.points)
             
-            this.update(true)
+            this.update({ifUpdateMarkers:true})
             //this.dragChange(new THREE.Vector3().copy(prop.points[prop.points.length-1]), prop.points.length-1); 
             this.setSelected(false )
             this.addHoverEvent()
@@ -336,23 +336,32 @@ export class ctrlPolygon extends THREE.Object3D {
             //console.log(this.points.map(e=>e.toArray())) 
 
         } 
-        if(this.retrictArea && !math.isPointInArea(this.retrictArea.points, location)){
-            
-            viewer.dispatchEvent({
-                type : "CursorChange", action : "add",  name:"polygon_AtWrongPlace"
-            })
-            this.isAtWrongPlace = true
-            return
-        }else{
-            viewer.dispatchEvent({
-                type : "CursorChange", action : "remove",  name:"polygon_AtWrongPlace"
-            })
-            this.isAtWrongPlace = false
-            this.setPosition(i, location); 
-            this.update()
-            
-            this.dispatchEvent({type:'dragChange', index:i})
-        }
+         
+        
+        if(this.restrictArea){  
+            let holes = this.restrictArea.holes.concat(this.restrictArea.parentHoles)           
+            let holesPoints = holes.filter(e=>e!=this && e.points.length>2).map(e=>e.points) 
+            if(!math.isPointInArea(this.restrictArea.points, holesPoints, location)){
+                viewer.dispatchEvent({
+                    type : "CursorChange", action : "add",  name:"polygon_AtWrongPlace"
+                })
+                this.isAtWrongPlace = true
+                return
+            }
+            //就不处理相交线了。 有个缺点:floor上的hole可以限制room,但hole不受room限制,会导致room的marker被框在hole里而动不了。只能去调整hole了
+        } 
+        
+        
+        
+        viewer.dispatchEvent({
+            type : "CursorChange", action : "remove",  name:"polygon_AtWrongPlace"
+        })
+        this.isAtWrongPlace = false
+        this.setPosition(i, location); 
+        this.update()
+        
+        this.dispatchEvent({type:'dragChange', index:i})
+        
         
         
     }
@@ -360,14 +369,14 @@ export class ctrlPolygon extends THREE.Object3D {
     dropMarker(e){
         console.log('dropMarker')
         if (this.isNew && e.pressDistance>Potree.config.clickMaxDragDis){//拖拽的话返回
-            return continueDrag(e, this)   
+            return this.continueDrag(null,e)   
         } 
         
         if(e.isTouch){ 
             if(e.hoverViewport != viewer.mainViewport && this.unableDragAtMap){
                 viewer.emit('reticule_forbit', true)
                 //console.log('reticule_forbit',true)
-                return continueDrag(e, this) 
+                return this.continueDrag(null,e)    
             }else{
                 viewer.emit('reticule_forbit', false)
                 //console.log('reticule_forbit',false)
@@ -385,7 +394,7 @@ export class ctrlPolygon extends THREE.Object3D {
                 || !getDifferentPoint(this.points, this.points.length) //不允许和之前的点相同 
             ) 
         ){
-            return continueDrag(e, this)   
+            return this.continueDrag(null,e)    
         } 
          
         //console.log('drop marker' )
@@ -538,7 +547,7 @@ export class ctrlPolygon extends THREE.Object3D {
     
      
 
-    update(ifUpdateMarkers){
+    update(options={}){
         if(this.points.length === 0){
 			return;
 		} 
@@ -559,7 +568,7 @@ export class ctrlPolygon extends THREE.Object3D {
             let nextPoint = this.points[nextIndex];
             let previousPoint = this.points[previousIndex];
 
-            if(ifUpdateMarkers){
+            if(options.ifUpdateMarkers){
                 this.updateMarker(this.markers[index], point)
             }   
 
@@ -581,8 +590,8 @@ export class ctrlPolygon extends THREE.Object3D {
             this.updateAreaPlane() 
         }
         
-             
-        viewer.mapViewer.emit('content_changed')
+        //this.dispatchEvent({type:'update'})     
+        viewer.mapViewer.emit('content_changed') //暂时先这么都通知
         
     } 
     
@@ -620,6 +629,21 @@ export class ctrlPolygon extends THREE.Object3D {
     
     transformData(){}
     setSelected(){}
+    
+    
+    
+    continueDrag(marker, e){
+        var timer = setTimeout(()=>{//等 drag=null之后 //右键拖拽结束后需要重新得到drag
+            if(this.parent){//没被删的话
+                viewer.inputHandler.startDragging(  marker || e.drag.object,
+                    {endDragFun: e.drag.endDragFun, notPressMouse:e.drag.notPressMouse, dragViewport:e.drag.dragViewport} 
+                )
+            } 
+        },1)
+        return timer
+    }
+ 
+    
 }
 
 
@@ -639,14 +663,4 @@ function getDifferentPoint(points, count){//for facePlane
  
 
 
-
-function continueDrag(e, this_){
-    setTimeout(()=>{//等 drag=null之后 //右键拖拽结束后需要重新得到drag
-        if(this_.parent){//没被删的话
-            viewer.inputHandler.startDragging(e.drag.object,
-                {endDragFun: e.drag.endDragFun, notPressMouse:e.drag.notPressMouse, dragViewport:e.drag.dragViewport} 
-            )
-        } 
-    },1)
-}
-
+ 

+ 3 - 3
src/utils/mapClipBox.js

@@ -272,11 +272,11 @@ export class mapClipBox extends ctrlPolygon {
     updatePoints(scale){
         this.points = getPoints(this.center, scale || this.getScale(), this.angle)
         this.getPoint2dInfo(this.points)
-        this.update(true) 
+        this.update({ifUpdateMarkers:true}) 
     }
     
-    update(ifUpdateMarkers){ 
-        super.update(ifUpdateMarkers)
+    update(options={}){ 
+        super.update(options)
         
         {//update rotateBar
             let center = new THREE.Vector3().addVectors(this.points[0], this.points[1]).multiplyScalar(0.5); 

+ 49 - 6
src/utils/math.js

@@ -1,5 +1,8 @@
 
 import * as THREE from "../../libs/three.js/build/three.module.js";
+import searchRings from "./searchRings.js";
+
+
 
 var math = {
     convertVector : {
@@ -237,7 +240,7 @@ var math = {
 		return bound;
 	},
 
-	isPointInArea : function(ring, point, ifAtLine){//判断点是否在某个环内
+	isPointInArea : function(ring, holes, point, ifAtLine){//判断点是否在某个环内, 若传递了holes代表还要不能在内环内
 		var bound = this.getBound(ring);
 		if(point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y)return false;
 		
@@ -255,20 +258,26 @@ var math = {
 		  if((xi - x)*(yj - y) == (xi - x)*(yi - y) && x>=Math.min(xi,xj) && x<=Math.max(xi,xj)//xzw add
 			  && y>=Math.min(yi,yj) && y<=Math.max(yi,yj)
 		  ){
-			  return !!ifAtLine;//在线段上,则判断为…… (默认在外)
+			  //return !!ifAtLine;//在线段上,则判断为…… (默认在外)
+              return {atLine:true}
 		  }
 		  
 		   if (((yi > y) != (yj > y)) &&
 			(x < (xj - xi) * (y - yi) / (yj - yi) + xi) 
 			) {
 			inside = !inside;
-		  }
-		  
-		  
+		  } 
 		  
 		}
+        
+        if(inside && holes){
+            return !holes.some(ring=>this.isPointInArea(ring, null, point, ifAtLine) )   //不能存在于任何一个二级内环内
+        }else{
+            return inside;
+        }
+        
 
-		return inside;
+		
 	},
 	
 	getArea : function (ring) { //求面积  顺时针为正  来自three shape
@@ -534,7 +543,41 @@ var math = {
         var plane = new THREE.Plane().setFromCoplanarPoints(...o.facePoints)
         return plane.projectPoint(o.point, new THREE.Vector3() )
     }
+    ,
     
+    getPolygonsMixedRings:function( polygons,  onlyGetOutRing){//{points:[vector2,...],holes:[[],[]]} 
+         
+         
+        let points = [] 
+        let lines = []
+        let i = 0 
+        
+        polygons.forEach(e=> points.push(...e.map(a=>new THREE.Vector2().copy(a) )) )   
+        polygons.forEach((ps,j)=>{
+            let length = ps.length;
+            let index = 0;
+            while(index<length){
+                lines.push({p1:index+i,p2:(index+1)%length+i});
+                index ++;
+            }
+            i+=length
+        })
+    
+             
+        points.forEach((p,j)=>{p.id = j}) 
+            
+        let rings = searchRings({
+            points,
+            lines,
+            onlyGetOutRing    
+        })
+        //console.log(rings)
+        
+        rings = rings.filter(e=>e.closetParent == void 0)// 子环不加,被外环包含了
+        
+        return rings 
+        
+    }  
 };
 
  

+ 4 - 1
src/viewer/LoadProject.js

@@ -297,7 +297,7 @@ function loadClassification(viewer, data){
 	viewer.setClassifications(classifications);
 }
 
-export async function loadProject(viewer, data){
+export async function loadProject(viewer, data, done){
 
 	if(data.type !== "Potree"){
 		console.error("not a valid Potree project");
@@ -340,6 +340,9 @@ export async function loadProject(viewer, data){
 
 	loadClassification(viewer, data.classification);
 
+    done && done()
+
+
 	// need to load at least one point cloud that defines the scene projection,
 	// before we can load stuff in other projections such as geopackages
 	//await Promise.any(pointcloudPromises); // (not yet supported)

+ 5 - 4
src/viewer/Scene.js

@@ -400,17 +400,18 @@ export class Scene extends EventDispatcher{
 		let light = new THREE.AmbientLight( 0x555555 ); // soft white light 
 		this.scenePointCloud.add( light );
         
+         
         
-        
-        //add:
-        let light2 = new THREE.AmbientLight( 16777215, 0.8 ); //给空间模型的box 
+        //add:------给空间模型的box 或其他obj------
+        let light2 = new THREE.AmbientLight( 16777215, 1 );
         viewer.setObjectLayers(light2, 'sceneObjects')
         this.scene.add(light2)
-        let light3 = new THREE.DirectionalLight( 16777215, 0.1); //给空间模型的box 
+        let light3 = new THREE.DirectionalLight( 16777215, 1);  
         light3.position.set( 10, 10, 10 );
 		light3.lookAt( new THREE.Vector3(0, 0, 0));
         viewer.setObjectLayers(light3, 'sceneObjects')
         this.scene.add(light3)
+        //--------------------------------------------
 
 		{ // background
 			let texture = Utils.createBackgroundTexture(512, 512);

+ 6 - 3
src/viewer/map/MapViewer.js

@@ -52,7 +52,7 @@ export class MapViewer extends ViewerBase{
         
         //this.scene.add(centerCross)
         
-        //CursorDeal.attachToViewer(this)
+     
         viewer.addEventListener("camera_changed", (e)=>{
             if(e.viewport == viewer.mainViewport) this.updateCursor()
             else this.updateWhenAtViewer() 
@@ -61,7 +61,7 @@ export class MapViewer extends ViewerBase{
          
         //viewer.addEventListener("global_mousemove", this.updateWhenAtViewer.bind(this))  //鼠标移动时reticule也动,所以直接就needRender
         viewer.reticule.addEventListener('update',(e)=>{
-            this.needRender = true 
+            if(this.attachedToViewer)this.needRender = true 
         })
          
         
@@ -541,7 +541,10 @@ export class MapViewer extends ViewerBase{
         } 
         params.clear ? params.clear() : renderer.clear(); 
         
-         
+        if(!this.attachedToViewer){
+            viewer.dispatchEvent({type: "render.begin",  viewer: this, viewport:this.viewports[0], params }); 
+        }
+        
         viewer.setCameraLayers(this.camera, ['map','mapObjects'  , 'bothMapAndScene'  ])
         
         if(this.mapGradientBG){//渲染背景

+ 8 - 1
src/viewer/sidebar.html

@@ -294,7 +294,14 @@ Thanks to all the companies and institutions funding Potree:
                 <button name='digHole'>挖洞</button>
             </li> 
             <li name='edit'>
-                <button name='removeLastMarker'>去除最后一个点</button>
+                <button name='removeFirstMarker'>去除第一个点</button>
+                <button name='removeFirstRoom'>去除第一个房间</button>
+                <button name='removeFirstFloor'>去除第一个楼层</button>
+                <button name='removeFirstBuilding'>去除第一个建筑</button>
+                <button name='removeFirstHole'>去除第一个洞</button>
+                <!-- 退出绘图模式 -->
+                
+                
                 <button name='selectFloor'>选中第一个楼层</button>
                 <button name='selectBuilding'>选中第一个建筑</button>
                 <button name='selectRoom'>选中第一个房间</button>

+ 31 - 4
src/viewer/sidebar.js

@@ -148,9 +148,9 @@ export class Sidebar{
         pannel.find('button[name="room"] ').on('click', ()=>{
             SiteModel.startInsertion('room', SiteModel.buildings[0].buildChildren[0])
         })
-        /* pannel.find('button[name="digHole"] ').on('click', ()=>{
-            SiteModel.startInsertion('room', SiteModel.buildings[0].buildChildren[0])
-        }) */
+        pannel.find('button[name="digHole"] ').on('click', ()=>{
+            SiteModel.selected && SiteModel.startInsertion('hole', SiteModel.selected)
+        })
         
         
         
@@ -162,7 +162,34 @@ export class Sidebar{
         } )
         pannel.find('button[name="selectRoom"] ').on('click', ()=>{
             SiteModel.selectEntity(SiteModel.buildings[0].buildChildren[0].buildChildren[0])
-        } )
+        })
+        
+        
+        pannel.find('button[name="removeFirstBuilding"] ').on('click', ()=>{
+            SiteModel.removeEntity(SiteModel.buildings[0])
+        })
+        pannel.find('button[name="removeFirstFloor"] ').on('click', ()=>{
+            SiteModel.removeEntity(SiteModel.buildings[0].buildChildren[0])
+        })
+        pannel.find('button[name="removeFirstRoom"] ').on('click', ()=>{
+            SiteModel.removeEntity(SiteModel.buildings[0].buildChildren[0].buildChildren[0])
+        })
+        pannel.find('button[name="removeFirstHole"] ').on('click', ()=>{
+            SiteModel.selected.removeHole(SiteModel.selected.holes[0])   
+        })
+        
+        
+        
+        pannel.find('button[name="removeFirstMarker"] ').on('click', ()=>{
+            //SiteModel.removeMarker(SiteModel.selected.markers[0])
+            SiteModel.selected.removeMarker(0)
+        })
+        
+        
+        
+        
+        
+        
     }
     
 

+ 117 - 25
src/viewer/viewer.js

@@ -41,9 +41,10 @@ import Common from '../utils/Common'
 import {Clip} from '../modules/clipModel/Clip'
 import {Alignment} from "../modules/datasetAlignment/Alignment.js";
 import {SiteModel} from "../modules/siteModel/SiteModel.js";
+import {Images360} from "../modules/Images360/Images360.js";
 
-
-
+  
+ 
 
 import Magnifier from "../utils/Magnifier.js";
 import Reticule from "../navigation/Reticule.js";
@@ -59,9 +60,8 @@ import {RouteGuider}  from '../navigation/RouteGuider'
 import {MeshDraw}  from '../utils/DrawUtil'
 
 
-
-
-
+import {OBJLoader} from "../../libs/three.js/loaders/OBJLoader.js";
+import {MTLLoader} from "../../libs/three.js/loaders/MTLLoader.js";
 
 
 
@@ -518,7 +518,7 @@ export class Viewer extends ViewerBase{
                     },3000) 
                     viewer.beginTestTime = Date.now()
                 }
-                console.log('updateNodeMaxLevel ' +  pointcloud.dataset_id + " : "+ nodeMaxLevel)                
+                //console.log('updateNodeMaxLevel ' +  pointcloud.dataset_id + " : "+ nodeMaxLevel)                
                 if(nodeMaxLevel >= 10 && viewer.testingMaxLevel){//10的时候差不多能加载到11和12了。假设最高只有12的话,就到10就可以。不过大多数场景都到不了10,也不知有没有大于10的,如果没有,这里可以写5.
                     viewer.testingMaxLevel = false
                     console.log('提前结束testingMaxLevel,用时:'+(Date.now()-viewer.beginTestTime))
@@ -574,9 +574,9 @@ export class Viewer extends ViewerBase{
                 e.maxLevel = 12;//先加载到最大的直到测试完毕。由于5个level为一组来加载,所以如果写4最高能加载到5,如果写5最高能加载到下一个级别的最高也就是10
                 //console.log('maxLevel: '+e.maxLevel +  ' testingMaxLevel中 '  )                                
             }else{
-                let percent = (pointDensity == 'panorama' || 'magnifier') ? config.maxLevelPercent :  (Potree.settings.UserDensityPercent == void 0 ? config.maxLevelPercent : Potree.settings.UserDensityPercent)
+                let percent = config.percentByUser && Potree.settings.UserDensityPercent != void 0  ? Potree.settings.UserDensityPercent : config.maxLevelPercent  
                 e.maxLevel = Math.round( percent * e.nodeMaxLevel); 
-                console.log('maxLevel: '+e.maxLevel +  ',   density : '+Potree.settings.pointDensity,  ",  percent :"+percent);
+                //console.log('maxLevel: '+e.maxLevel +  ',   density : '+Potree.settings.pointDensity,  ",  percent :"+percent);
                 
                 if(Potree.settings.sizeFitToLevel){
                     e.changePointSize() 
@@ -1264,7 +1264,7 @@ export class Viewer extends ViewerBase{
 		}
 	}
 
-	async loadProject(url){
+	async loadProject(url,done){
 
 		const response = await fetch(url);
         if(response.ok){
@@ -1273,7 +1273,7 @@ export class Viewer extends ViewerBase{
             // const json = JSON.parse(text);
 
             if(json.type === "Potree"){
-                Potree.loadProject(viewer, json);
+                Potree.loadProject(viewer, json, done);
             }
             
         }else{
@@ -2422,7 +2422,7 @@ export class Viewer extends ViewerBase{
 		} else if (background === 'white') {
 			renderer.setClearColor(0xFFFFFF, 1);
 		} else {
-			renderer.setClearColor(0x000000, 0);
+			renderer.setClearColor(background, backgroundOpacity);
 		}
 		
 		params.target || renderer.clear();
@@ -2937,7 +2937,7 @@ export class Viewer extends ViewerBase{
                     normal = object.facePlane.normal.clone()
                 }               
                 let angle = this.scene.view.direction.angleTo(normal)
-                let minDiff = Math.PI*0.25// 45度
+                let minDiff = THREE.Math.degToRad(60) 
                 if(angle>minDiff && angle<Math.PI-minDiff){//当几乎正对时就不执行
                     if(angle<Math.PI/2){ //在背面
                         normal.negate()
@@ -3056,10 +3056,10 @@ export class Viewer extends ViewerBase{
                 //出现过到达位置后测量线标签闪烁的情况
             }
             
-        } else if (type == 'tag') {
+        } else if (type == 'tag' || type == 'point') {
             //dimension = 1  
             target.copy(object.position)
-            const bestDistance = 2
+            let bestDistance = o.distance || 2 
             
             { 
                 //console.log('mapFocusOn: '+target.toArray()) 
@@ -3068,7 +3068,7 @@ export class Viewer extends ViewerBase{
             
             if(Potree.settings.displayMode == 'showPointCloud'){ 
                 dis = bestDistance
-                let dir = new THREE.Vector3().subVectors(camera.position, target).normalize() 
+                let dir = o.direction ? o.direction.clone().negate() : new THREE.Vector3().subVectors(camera.position, target).normalize() 
                 position.copy(target).add(dir.multiplyScalar(dis))
             
             }else if(Potree.settings.displayMode == 'showPanos'){
@@ -3107,7 +3107,28 @@ export class Viewer extends ViewerBase{
 
 
 
-
+    flyToDataset(o={}){
+        var pointcloud;
+        if(o instanceof THREE.Object3D) pointcloud = o
+        else if(o.pointcloud) pointcloud = o.pointcloud
+        else pointcloud = this.scene.pointclouds.find(p => p.dataset_id == o.id);
+         
+        var center = pointcloud.bound.getCenter(new THREE.Vector3);
+        
+        let request = []
+        let rank = [
+            Images360.scoreFunctions.distanceSquared({position: center}) 
+        ]
+        let r = Common.sortByScore(pointcloud.panos, request, rank);
+        
+        if(r && r.length){
+            this.images360.flyToPano({
+                pano:r[0].item
+            })
+        }
+        
+        
+    }
 
 
 
@@ -3374,8 +3395,9 @@ export class Viewer extends ViewerBase{
         viewer.inputHandler.toggleSelection(object);   
     }
     
+     
     
-    addObjectTest(){//加水管
+    addObjectTest1(){//加水管
         
         if(Potree.settings.number == 't-8KbK1JjubE'){
             
@@ -3396,7 +3418,7 @@ export class Viewer extends ViewerBase{
                 var name = 'cylinder'+count
                 var mat = new THREE.MeshStandardMaterial({color, /* wireframe:true, */ depthTest:false, roughness:0.4,metalness:0.5}) 
                 let linePath = path.map(e=>new THREE.Vector3().copy(e).setZ(e.z+height))
-                let geo = MeshDraw.getExtrudeGeo( circlePts, null, linePath, 0.2)
+                let geo = MeshDraw.getExtrudeGeo( circlePts, null,{ extrudePath:linePath, tension:0.2}   )
                 var mesh = new THREE.Mesh(geo,mat);
                 mesh.name = name
                 window[name] = mesh
@@ -3440,17 +3462,87 @@ export class Viewer extends ViewerBase{
         
         
     }
-     
-};
+    
+    
 
 
+    loadObj(fileInfo){//加水管2
+        let boundingBox = new THREE.Box3()
+            boundingBox.min.set(-1,-1,-1); boundingBox.max.set(1,1,1)
+             
+        let manager = new THREE.LoadingManager();
+        /* manager.onProgress = function ( item, loaded, total ) {
+            console.log( item, loaded, total );
+        }; */
+
+        let onProgress = function ( xhr ) {
+            if ( xhr.lengthComputable ) {
+                let percentComplete = xhr.loaded / xhr.total * 100;
+                console.log( Math.round(percentComplete, 2) + '% downloaded' );
+            }
+        };
+        let onError = function ( xhr ) {};
+
+         
+
+        let Objloader = new OBJLoader( manager );
+        let MtlLoader = new MTLLoader( manager );
+        
+         
+        
+        MtlLoader.load( fileInfo.mtlurl , (materials)=>{ 
+            materials.preload(); 
+        
+            Objloader.setMaterials( materials ).load(fileInfo.objurl, (object)=>{ 
+         
+                object.traverse( function ( child ) {
+                    if ( child instanceof THREE.Mesh ) {
+                        //child.material.map = texture;
+                        child.material.roughness = 0.6 
+                        child.material.metalness = 0.3 
+                    }
+                } );
+ 
+                this.obj = object; 
+                this.scene.scene.add(object);
+                 
+                object.boundingBox = boundingBox
+                object.rotation.fromArray(fileInfo.transform.rotation)
+                object.position.fromArray(fileInfo.transform.position)
+                 
+                 
+                object.updateMatrix();
+                object.matrixAutoUpdate = false
+                object.matrix.premultiply(viewer.scene.pointclouds[0].transformMatrix) //默认跟随第一个数据集
+                object.matrixWorldNeedsUpdate = true 
+                
+                
+
+                /* viewer.onGUILoaded(() => {
+                    // Add entries to object list in sidebar
+                    let tree = $(`#jstree_scene`);
+                    let parentNode = "other";
+
+                    let bunnyID = tree.jstree('create_node', parentNode, { 
+                            text: "Bunny Textured", 
+                            icon: `${Potree.resourcePath}/icons/triangle.svg`,
+                            data: object
+                        }, 
+                        "last", false, false);
+                    tree.jstree(object.visible ? "check_node" : "uncheck_node", bunnyID);
+
+                    //tree.jstree("open_node", parentNode);
+                }); */
+
+            }, onProgress, onError );
+        });
+        
+         
+    }
+};
+
 
 
 
 
 
-/* t.prototype.getTransformationMatrix = function(t) {//点云截取   所有点在乘上这个矩阵后, 还能落在 1 * 1 * 1的box内的点就是所裁剪的
-            var e = this.ViewService.mainView.getVolumeClippingLayer().getBoxFrame()
-              , n = (new o.Matrix4).getInverse(e.matrixWorld)
-              , i = t.m2w_;
-            return (new o.Matrix4).multiplyMatrices(n, i).transpose() */

+ 58 - 0
改bug的历史.txt

@@ -1,8 +1,55 @@
+2022-03-18
+出现过marker无法松开的情况(加载的数据),但是再选中房间又可以了
+·是dropMarker时 因为有相同点 return continueDrag了
+·数据里竟然有五个点,后两个相同。应该是添加时没删除
+·是绘制完前删最后一个点后没有发送update
+
+
+
+2022-03-03
+
+有时候加载全景图一直loading。 
+
+·六张512的有一张未打印出加载成功(但实际在网络里查看是加载了的,tileDownloader也有值),然后就去加载更高分辨率去了,没发送成功。
+·没通知成功的原因是uploadTile那张512时已经加载好了子集1024,就跳过了。
+·原来是预加载的dir写错了。但是只是预加载的话大概率不会发生此bug而已。
+·但万一先关闭了全景,在一个没有预加载的点位上打开全景,就可能发生bug
+·问题的关键在于4dkk一定先uploadTile低分辨率的,而这里乱序了
+·发现失败的原因是forceQueue  512的加载太慢了,为什么不是一开始就有6个呢,不到6个就没了然后就下载1024去了。难道4dkk不会吗
+·是先开始下载的512,但是1024的先下载完毕。 
+·4dkk 也可能因为uploadTile失败,而不发送完成消息,通常都是1024的。
+
+forceQueue在tiledownloader和panorenderer都有。
+forceQueue 的应该就是优先下载的吧,按理说一定要先下载完才能下载后面的在 
+在panoRenderer中 uploadQueues 是候补队列 ,在tileDownloader里priorityQueue是候补队列 
+tiledownloader下载完成会触发tiledownloader开始uploadTile.
+ 
+·试不出来为什么4dkk会先下载完512了。改个代码,确保512的都下载完后才能下载其他的。修改了tileDownloader的processQueueForDownloading
+
+
+
+
+--更早前:
+
+关于测量线截图1024时常get不到的情况:(奇怪为什么之前不会这样?)
+原因: uploadTile中,如果某个tile的子集(如1024的子集是四个2048)已经加载好了,那么该tile就不会触发加载,也就不会发送加载成功的消息。
+所以最好还是只请求加载512.除非首次加载就请求高分辨率。(是不是我写漏了啥?因为4dkk 飞进去不会)
+4dkk中也可能不一定发送1024的完成信息
+
 
 
 
 2022-03-02
 
+
+
+初始画面还是会转一下 t-1aw99jhDgB#/setup  在未设置过初始画面的场景才会 
+·又好像是某些角度会算错。
+·原来是set rotation(rotation)函数有问题,没有考虑到y也有值。 
+
+
+
+
 触屏放大镜里的点云为何那么大???
 ·只要一转为触屏就这样  pointsize和什么有关,会重新计算吗
 ·自适应时的触屏devicePixelRatio是2!!
@@ -55,6 +102,7 @@
 
 
 
+
 2022-02-24
 关于iphone测试机旋转屏幕or点击小地图后mainViewer setSize导致webgl context lost 的bug:
 	·修改canvas.width height就会
@@ -69,11 +117,21 @@
 
 
 
+关于iphone离开网页一段时间后再回来,重启(or显示内存不足)的bug: 
+	
+	·隧道。。第一次测会,第二次就不会了奇怪。。。(但有时候打开是报网络错误)
+
+	·我的手机是可能白屏(清除缓存后不会了)
+	·微信会(可能是应用问题,因为应用也容易关闭)	
+	·无解
+
+
 
 
 
 2022-02-23
 关于iphone测试机创建不了potree中的program:
+	·t-6UoYGXbWhi 这个场景(最不容易崩溃 只有一个pano)的无此问题	
 	·即使把mapViewer禁止渲染,使potree的program是第一个创建的program也不行
 	·有一个场景有时候可以打开 https://uat-laser.4dkankan.com/uat/index.html?m=t-6UoYGXbWhi
 	·和点位无关,删除全部点也不行