xzw 1 year ago
parent
commit
57a2804eb5

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

@@ -31226,9 +31226,9 @@ class ExtrudeBufferGeometry extends BufferGeometry {
 			/* Faces */
 
 			// Top and bottom faces
-
-			buildLidFaces();
-
+            if(!options.openEnded){//xzw add 可以选择开口,不创建Top and bottom faces
+                buildLidFaces();
+            }
 			// Sides faces
 
 			buildSideFaces();

+ 4 - 1
package.json

@@ -37,5 +37,8 @@
     "through": "~2.3.4"
   },
   "author": "Markus Schütz",
-  "license": "BSD-2-CLAUSE"
+  "license": "BSD-2-CLAUSE",
+  "devDependencies": {
+    "@babel/plugin-proposal-optional-chaining": "^7.21.0"
+  }
 }

+ 2 - 2
src/Potree.js

@@ -62,7 +62,7 @@ export * from "./utils/PointCloudSM.js";
 												// export * from "./objects/tool/VolumeTool.js";
 												// export * from "./objects/tool/Compass.js";
                                                 
-                                                
+export * from "./custom/utils/DrawUtil.js";                                              
 export * from "./custom/objects/tool/Measure.js";
 export * from "./custom/objects/tool/MeasuringTool.js";
 export * from "./utils/PolygonClipVolume.js";
@@ -175,7 +175,7 @@ export async function loadFile(path, params , callback, onError){
             
             let response = await fetch(path, info); 
             let text = await response.text();
-            var data = JSON.parse(text)
+            var data = params.returnText ? text : JSON.parse(text)
             if(data.data) data = data.data
             callback && callback(data)    
             return data 

+ 50 - 16
src/PotreeRendererNew.js

@@ -467,11 +467,20 @@ class Shader {
 
 class WebGLTexture {
 
-	constructor(gl, texture) {
+	constructor(gl, texture, threeRenderer) {
 		this.gl = gl;
 
 		this.texture = texture;
-		this.id = gl.createTexture(); 
+        
+        if(texture.image && !(texture.image instanceof Image) && !texture.isCanvasTexture && !texture.isDataTexture){
+            //renderTarget的texture在创建renderTarget时已经初始化过 见setupRenderTarget
+            this.id = threeRenderer.properties.get(texture).__webglTexture || gl.createTexture(); 
+            this.isFromRenderTarget = true 
+        }else{
+            this.id = gl.createTexture(); 
+        }
+        
+		
 
 		this.target = gl.TEXTURE_2D;
 		this.version = -1;
@@ -486,6 +495,7 @@ class WebGLTexture {
 
 			return;
 		}
+        //if(this.isFromRenderTarget)return  //没找到怎么update。 在three.js里的uploadTexture没找到。 这里会报错,可能是 state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ),类似depthTex的写法
 
 		let gl = this.gl;
 		let texture = this.texture;
@@ -531,10 +541,14 @@ class WebGLTexture {
 
 			gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL(gl, texture.magFilter));
 			gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL(gl, texture.minFilter));
-
-			gl.texImage2D(this.target, level, internalFormat,
-				internalFormat, srcType, data);
-
+            
+            if(this.isFromRenderTarget){
+                 //咋写?
+                //gl.texImage2D( 3553, 0, internalFormat, width, height, 0, srcFormat, srcType, null ) 
+                //console.log('isFromRenderTarget') 
+            }else{
+                gl.texImage2D(this.target, level, internalFormat,   internalFormat, srcType, data); //这个参数怎么跟我查的不一样呢
+            }
 			if (texture instanceof THREE.Texture) {gl.generateMipmap(gl.TEXTURE_2D);}
 		}
 
@@ -1263,7 +1277,7 @@ export class Renderer {
                     `#define num_prism ${material.prisms.length}`, //土方量数
                     `#define prismPointCountSum ${material.prisms.pointsCount}`,//点总个数
                     `#define prism_maxPointsCount ${material.prisms.maxPointsCount}`, //单个prism最大点个数 (如果define也能传递个数数组,就不用再uniform里传了,呜 )      
-                    
+                     
                 ];
                     
                 //add:-----------
@@ -1281,7 +1295,9 @@ export class Renderer {
                     
                 }
 
-                
+                if(material.uniforms.baseHeightAreaMap.value){//根据模型高亮土方
+                    defines.push('#define showBaseHeight')
+                }
                 //---------------
                 
                 
@@ -1344,12 +1360,18 @@ export class Renderer {
                     if(uniformName == 'pano0Map' || uniformName == 'pano1Map' ){ //属于cubeTex,另外设置 
                         continue
                     } 
+                    /* if(texture.image && !(texture.image instanceof Image) && !(texture instanceof THREE.CanvasTexture)){
+                        //renderTarget的texture在创建renderTarget时已经初始化过 见setupRenderTarget
+                        continue
+                    } */
                     
-					if (!this.textures.has(texture)) {
-						let webglTexture = new WebGLTexture(gl, texture);
+					if (!this.textures.has(texture) || texture.needsRebuild) {
+						let webglTexture = new WebGLTexture(gl, texture, this.threeRenderer);
 
-						this.textures.set(texture, webglTexture);
-					}
+						this.textures.set(texture, webglTexture); 
+                        delete texture.needsRebuild //renderTarget在resize后会触发dispose, 然后 _gl.deleteTexture( textureProperties.__webglTexture )所以需要重新建立
+					}  
+                    
 
 					let webGLTexture = this.textures.get(texture);
 					webGLTexture.update();
@@ -1462,7 +1484,12 @@ export class Renderer {
             if (material.bigClipInBox ) {//add 
 				shader.setUniformMatrix4("clipBoxBig_in", material.uniforms.clipBoxBig_in.value); 
             }
-            
+            if(material.uniforms.baseHeightAreaMap.value ){//根据模型高亮土方
+                const baseHeightBoundZ = shader.uniformLocations["baseHeightBoundZ"];
+                gl.uniform2f(baseHeightBoundZ, ...material.uniforms.baseHeightBoundZ.value.toArray());  
+                const baseHeightBoundXY = shader.uniformLocations["baseHeightBoundXY"];
+                gl.uniform4f(baseHeightBoundXY, ...material.uniforms.baseHeightBoundXY.value.toArray());   
+            }
             
             if(material.prisms.length){ 
                 const prismList = shader.uniformLocations["prismList[0]"];
@@ -1472,7 +1499,6 @@ export class Renderer {
                 gl.uniform2fv(prismPoints,  material.uniforms.prismPoints.value);   
             }
             
-
 			// TODO CLIPSPHERES
 			if(params.clipSpheres && params.clipSpheres.length > 0){
 
@@ -1586,10 +1612,19 @@ export class Renderer {
 			gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint);
 			gl.bindTexture(matcapTexture.target, matcapTexture.id);
 			currentTextureBindingPoint++; */
-
             
             
             
+            let baseHeightAreaMap = material.uniforms.baseHeightAreaMap.value
+            if(baseHeightAreaMap){//根据模型高亮土方 
+                let map = this.textures.get(baseHeightAreaMap);
+                shader.setUniform1i("baseHeightAreaMap", currentTextureBindingPoint);
+                gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint);
+                gl.bindTexture(map.target, map.id);
+                currentTextureBindingPoint++;  
+            } 
+            
+            
             
 			if (material.snapEnabled === true) {
 
@@ -1658,7 +1693,6 @@ export class Renderer {
             //=============add===========
             
             
-            
             if(material.usePanoMap){//为什么pointsize失效 
                 shader.setUniform1f("progress", material.uniforms.progress.value);
                 shader.setUniform1f("easeInOutRatio", material.uniforms.easeInOutRatio.value);

+ 135 - 12
src/custom/mergeStartTest.js

@@ -3,7 +3,8 @@ import {settings, config} from './settings.js'
 import math from './utils/math.js' 
 import browser from './utils/browser.js' 
 import {Utils} from "./../utils.js"
-import cameraLight from './utils/cameraLight.js' 
+import cameraLight from './utils/cameraLight.js'  
+import {MeshDraw} from './utils/DrawUtil.js' 
 import './three.shim.js'  
 import "./potree.shim.js"
 
@@ -307,10 +308,13 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
     }
     viewer.setControls(viewer.orbitControls) 
      
-     
+      
           
       
     let tilesetUrls = [ 
+    
+        'https://4dkk.4dage.com/scene_view_data/SG-t-hFdFM1eCYs6/images/3dtiles/tileset.json?_=1716879888240',
+        //(`scene_view_data/{num}/images/3dtiles/tileset.json?_=${Date.now()}`),
         'https://4dkk.4dage.com/fusion/test/b3dm/tileset.json',  //高层小区
         'https://testgis.4dage.com/LVBADUI_qp/tileset.json', //村庄
         'https://4dkk.4dage.com/fusion/testb3dm/modelId_613/tileset.json',//"952.16MB"  港一
@@ -326,7 +330,7 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
       
     
     let modelType,  modelEditing, MergeEditor = viewer.modules.MergeEditor
-    Potree.addModel = function(name, done){ 
+    Potree.addModel = function(name, done, url){ 
         
         let isFirstLoad = true
         cancelMove()
@@ -373,10 +377,10 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                 })
             
             }
-            model.updateMatrixWorld()
+            //model.updateMatrixWorld()
             viewer.updateModelBound()
             model.lastMatrixWorld = model.matrixWorld.clone();
-            MergeEditor.getBoundCenter(model) //初始化
+            model.boundCenter || MergeEditor.getBoundCenter(model) //初始化
             
             
             
@@ -389,7 +393,7 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
             
             
             
-            done(model)
+            done && done(model)
         }
         
         
@@ -484,11 +488,12 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                     }  
                 },callback,onprogress)  */  
                      
-                var path = `${Potree.resourcePath}/models/obj/28M/`
+                //var path = `${Potree.resourcePath}/models/obj/28M/`
+                var path = `${Potree.resourcePath}/models/obj/`
                 viewer.loadModel({
                     name, 
-                    objurl: path+'GW1H.obj',  //解析时间4.392s
-                    mtlurl: path+'GW1H.mtl',    
+                    objurl: path+ 'sphere.obj' /* 'GW1H.obj' */,  //解析时间4.392s
+                    //mtlurl: path+'GW1H.mtl',    
                     transform : { 
                         rotation : [0,  0,   0],
                         position : [0,0,0]  
@@ -554,10 +559,12 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                         
                  
             }else if(name == '3dTiles'){ 
+                  
+                 //------------ 
                  
                 viewer.loadModel({ 
                     fileType:'3dTiles',
-                    url: tilesetUrls[tileIndex++],
+                    url: url || tilesetUrls[tileIndex++],
                      
                     transform : { 
                         rotation : [Math.PI/2,  0,   0],
@@ -565,11 +572,98 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                     }  
                 },callback,onprogress)
                 
-                 
+                  
                 
                  
                 
                 
+            }else if(name == 'dxf'){
+                 let fileName1 = 'tunnel.dxf', fileName2 = 'tunnel-line.dxf' 
+                 //let angle = Math.PI
+                 let path = `${Potree.resourcePath}/models/dxf/`
+                  
+                 
+                 let extrude = (shapeModel, pathModel)=>{//创建隧道,将剖面沿着轨迹挤出 
+                     
+                    //let shape = shapeModel.children[0].geometry.vertices.map(e=>e.clone())   //该模型中的只有第一个child是外围的shape,其他多余。取它的点
+                    // let shape = [new THREE.Vector3(10,10,0),new THREE.Vector3(10,0,0),new THREE.Vector3(0,0,0),new THREE.Vector3(0,10,0)]
+                    let shapeBound = shapeModel.boundingBox
+                    let boundCenter = shapeBound.getCenter(new THREE.Vector3)
+                    let shape = shapeModel.children[0].geometry.vertices.map(e=>{
+                        let p = e.clone().sub(boundCenter)
+                        return new THREE.Vector3(-p.y,p.x,0) //转90度
+                    })
+
+
+                    let boundCenter2 = pathModel.boundingBox.getCenter(new THREE.Vector3)
+                    //let extrudePath = pathModel.children[0].geometry.vertices.map(e=>e.clone().sub(boundCenter2))
+                     
+                    let vertices = pathModel.children[0].geometry.vertices.map(e=>e.clone().sub(boundCenter2) ) //不减去bound,tension为0的话也没事
+                    let extrudePath = []
+                    const minDis = 1
+                    vertices.forEach((p,i)=>{
+                        if(i==0 || i== vertices.length-1)return extrudePath.push(p) //首尾直接加入
+                        let last = extrudePath[extrudePath.length-1]
+                        let dis = last.distanceTo(p)
+                        if(dis < minDis){
+                            console.log(`第${i}个点(${p.toArray()})因为和上一个数据(${last.toArray()})距离太短(${dis})所以删除`)
+                        }else if(i == vertices.length - 2){//因为最后一个必定加入,所以倒数第二个还也不能太靠近最后一个
+                            last = extrudePath[vertices.length-1]
+                            if(dis < minDis){
+                                console.log(`第${i}个点(${p.toArray()})因为和下一个数据(${last.toArray()})距离太短(${dis})所以删除`)
+                            }else{
+                                extrudePath.push(p)
+                            }
+                        }else{
+                            extrudePath.push(p)
+                        }
+                        
+                    }) 
+                     
+                       
+                    let geo = MeshDraw.getExtrudeGeo(shape, null, {extrudePath, steps:  extrudePath.length-1  , dontSmooth:true  , tension:0, openEnded:true}) //dontSmooth可以为false
+                    let tunnel = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({
+                        color:'#777', side : 2, metalness:0.5, //wireframe:true  ,
+                        transparent:true, opacity:1, depthTest:false,  
+                    })) 
+                    
+                    
+                    viewer.modelLoaded(tunnel,{fileType:'dxf',name:'tunnel-extrude'}, callback.bind(tunnel))
+                    
+
+                    MergeEditor.moveBoundCenterTo(shapeModel,new THREE.Vector3(0,0, shapeModel.boundCenter.z))  //使模型中心的xy在鼠标所在位置
+                    MergeEditor.moveBoundCenterTo(pathModel,new THREE.Vector3(5,5, pathModel.boundCenter.z))  //使模型中心的xy在鼠标所在位置
+                    MergeEditor.moveBoundCenterTo(tunnel,new THREE.Vector3(5,5, tunnel.boundCenter.z))  //使模型中心的xy在鼠标所在位置
+                 
+                 
+                    
+                 }
+                 
+                 
+                 viewer.loadModel({ 
+                        fileType:'dxf',
+                        name, 
+                        url: path+fileName1,
+                        transform : { 
+                            rotation : [0, 0, 0],
+                            position : [0,0,0]  
+                        } 
+                    },(model1)=>{
+                        callback(model1)
+                        viewer.loadModel({ 
+                            fileType:'dxf',
+                            name, 
+                            url: path+fileName2,
+                            transform : { 
+                                rotation : [0, 0, 0],
+                                position : [0,0,0]  
+                            } 
+                        }, (model2)=>{
+                            callback(model2)
+                            extrude(model1, model2)
+                        },onprogress)
+                 },onprogress)
+                   
             }
              
         }  
@@ -620,6 +714,35 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
         viewer.scene.pointclouds.forEach(e=>e.predictNodeMaxLevel())
               
     }
+    
+    
+    
+   /* setTimeout(()=>{
+    
+    
+        Potree.addModel('dxf')
+        
+        viewer.addEventListener('modelLoaded',(e)=>{
+            if(e.model.name == 'tunnel-extrude'){
+                confirmPos()
+            }
+        }) 
+        
+    },2000)  */  
+    
+    setTimeout(()=>{
+    
+    
+        Potree.addModel('laser')
+        /* setTimeout(()=>{ 
+            Potree.addModel('glb')
+        },100) */
+        
+        /* viewer.addEventListener('modelLoaded',(e)=>{ 
+            confirmPos() 
+        }) */ 
+        
+    },2000)
 }
  
  
@@ -684,7 +807,7 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
 changeLog() */
  //可以直接用edlShader来渲染obj的outline,但不能渲染被遮挡的部分 
  
- 
+
  
 export {start}
 

+ 4 - 4
src/custom/modules/clipModel/Clip.js

@@ -11,8 +11,7 @@ import Viewport from "../../viewer/Viewport.js";
 import {TextSprite} from "../../objects/TextSprite.js";
 import {ExtendPointCloudMaterial} from "../../../materials/ExtendPointCloudMaterial.js";
 import AxisViewer from "../../objects/tool/AxisViewer.js";
-
-
+ 
 
 const defaultBoxWidth = 16;  //navvis:  10
                             //navvis position: si {x: 0, y: 0, z: 0}
@@ -583,7 +582,7 @@ var Clip = {
     
     
     
-    screenshot: async (rulerBound, rulerMargin )=>{ //测绘图下载。顶视图|侧视图
+    screenshot: async (rulerBound, rulerMargin, unitText='像素 : 米' )=>{ //测绘图下载。顶视图|侧视图
         return new Promise((resolve,reject)=>{ 
             if(Clip.screenshoting )return reject()
              
@@ -674,7 +673,8 @@ var Clip = {
             console.log({wc,hc,w,h})
             
             let meterPerPixel = boundSize.x / w
-            let text = `1 : ${(meterPerPixel).toFixed(4)}(像素 : 米)`
+            
+            let text = `1 : ${(meterPerPixel).toFixed(4)}(${unitText})`
             
             let beforeScreenshot = ()=>{
                 if(rulerBound){

+ 9 - 0
src/custom/modules/clipping/Clipping.js

@@ -236,6 +236,15 @@ export class Clipping extends THREE.EventDispatcher{ //实时剪裁
     }
     
     
+    enter2(){//在土方量界面的
+        
+        viewer.transformationTool.setModeEnable(['translation'])  
+        viewer.transformationTool.frame.material.visible = false //不盖住boxVolume的frame
+        viewer.transformationTool.history.clear()
+          
+        //viewer.inputHandler.addEventListener('keydown', this.events.onkeydown)
+    }
+    
     leave(){
          
         viewer.transformationTool.setModeEnable(['scale', 'translation', 'rotation'] )

+ 1 - 0
src/custom/modules/volumeCompute/Prism.js

@@ -34,6 +34,7 @@ export class Prism extends Measure{
             this.setHorizonHeight(this.horizonType, true)//更新 
             this.needsCompute = true
             this.getBound()
+            this.dispatchEvent('updated')
         }
         this.addEventListener('marker_moved',update)
         this.addEventListener('changeByHistory',update)

File diff suppressed because it is too large
+ 611 - 119
src/custom/modules/volumeCompute/VolumeComputer.js


+ 35 - 2
src/custom/objects/tool/Measure.js

@@ -161,7 +161,7 @@ export class Measure extends ctrlPolygon{
      
     updateDatasetBelong(changeIndex){//更新所属数据集
      
-        if(Potree.settings.editType == "merge" || this.measureType == 'MulDistance Ring'){//无地图
+        if(Potree.settings.editType == "merge" || this.measureType == 'MulDistance Ring'){//点直接跟着数据集走,不用找整体的datasetId
           
             this.dataset_points[changeIndex] = Potree.Utils.datasetPosTransform({toDataset:true, datasetId:this.points_datasets[changeIndex], position:this.points[changeIndex].clone()})
              
@@ -617,6 +617,19 @@ export class Measure extends ctrlPolygon{
             
         })
         
+        marker.addEventListener('click',()=>{
+            
+            if(viewer.measuringTool.editMode == 'delPoint' ){
+                if(this.points.length == this.minMarkers){
+                    viewer.scene.removeMeasurement(this)
+                }else{
+                    let index = this.markers.indexOf(marker)
+                    this.removeMarker(index)
+                }
+                
+            }
+        })
+        
         //marker.measure = this 
         let edge
 		{ // edges 
@@ -644,11 +657,31 @@ export class Measure extends ctrlPolygon{
                 edge.addEventListener('click',(e)=>{
                     let now = Date.now()
                     if(now - this.lastDropTime<100)return ;//防止拖拽marker时误触导致focus, 以及点到marker不focus
-                    this.isNew || viewer.measuringTool.isAdding || viewer.focusOnObject(this, 'measure') //正在添加测量线时不要focus其他线(容易误触)
+                    
+                    
+                    if(viewer.measuringTool.editMode == 'addPoint' && this.points.length < this.maxMarkers){ 
+                        let index = this.edges.indexOf(edge) + 1
+                        let nextIndex = index % this.edges.length
+                        let point = math.getFootPoint(e.hoveredElement.point, this.points[index-1], this.points[nextIndex] );
+                        
+                        this.addMarker({
+                            index, 
+                            point,
+                            dataset_point: this.dataset_points && new THREE.Vector3 , //初始化
+                            points_dataset : this.points_datasets[index] //使用前一个的
+                        })  
+                        this.updateDatasetBelong(index) //获取dataset_point
+                        //this.update({})       
+                    }else{ 
+                        this.isNew || viewer.measuringTool.isAdding || viewer.focusOnObject(this, 'measure') //正在添加测量线时不要focus其他线(容易误触)
+                    }
                 })
                  
             }
             edge.addEventListener('addHoverEvent', addHoverEvent);
+            if(!this.isNew){
+                edge.dispatchEvent('addHoverEvent')
+            }
 		}
         
         super.addMarker(Object.assign(o, {index, marker,  edge}))

+ 13 - 1
src/custom/objects/tool/MeasuringTool.js

@@ -318,7 +318,19 @@ export class MeasuringTool extends THREE.EventDispatcher{
     }
     
     
-    
+    changeEditMode(mode){
+        viewer.dispatchEvent({type : "CursorChange", action : "remove",  name:"addPoint"})
+        viewer.dispatchEvent({type : "CursorChange", action : "remove",  name:"delPoint"})
+        if(mode){
+            if(mode == 'addPoint'){
+                viewer.dispatchEvent({type : "CursorChange", action : "add",  name:"addPoint"}) 
+            }else{
+                viewer.dispatchEvent({type : "CursorChange", action : "add",  name:"delPoint"})
+            }
+            
+        } 
+        this.editMode = mode
+    }
     
     editStateChange(e){
         //console.log("editStateChange" , e.state)

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

@@ -29,7 +29,7 @@ export class ctrlPolygon extends THREE.Object3D {
             this[i] = prop[i]
         }
         
-        if((this.atPlane || this.showArea) && this.closed && this.dimension == '2d'){
+        if((this.atPlane || this.showArea ) && this.closed && this.dimension == '2d'){
             this.areaPlane = this.createAreaPlane(); 
             this.add(this.areaPlane)
         }

+ 7 - 5
src/custom/potree.shim.js

@@ -406,7 +406,7 @@ Utils.getMousePointCloudIntersection = function(viewport, mouse, pointer, camera
             distance: closestDistance,
             pointcloud: selectedPointcloud,
             point: closestPoint,
-            pointclouds: allPointclouds, //add 
+            pointclouds: allPointclouds, //add  
             normal:new THREE.Vector3().fromArray(closestPoint.normal ).applyMatrix4(selectedPointcloud.rotateMatrix)//add
         };
     } else {
@@ -900,7 +900,7 @@ Utils.setCameraLayers = function(camera, enableLayers, extraEnableLayers=[]){//a
 Utils.setObjectLayers = function(object, layerName){//add
     let layer = Potree.config.renderLayers[layerName]
     if(layer == void 0){
-        console.error('setCameraLayer没找到layer!');
+        console.error('setObjectLayers没找到layer!',layerName);
         return 
     }
     object.traverse(e=>{ 
@@ -1296,11 +1296,13 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
         
 		let insideFrustum = frustum.intersectsBox(box);
 		let maxLevel = pointcloud.maxLevel == void 0 ? Infinity : pointcloud.maxLevel;
+		let minLevel = pointcloud.minLevel == void 0 ? 0 : pointcloud.minLevel; //add
+        
 		let level = node.getLevel();
 		let visible = insideFrustum;
 		visible = visible && !(numVisiblePoints + node.getNumPoints() > Potree.pointBudget);
 		visible = visible && !(numVisiblePointsInPointclouds.get(pointcloud) + node.getNumPoints() > pointcloud.pointBudget); // pointcloud.pointBudget一直是Infinity
-		visible = visible && level <= maxLevel; //< 改为 <=
+		visible = visible && level <= maxLevel /* && level >= minLevel */ ;  //< 改为 <=
 		//visible = visible || node.getLevel() <= 2;
         let pcWorldInverse = pointcloud.matrixWorld.clone().invert();
         /* let m = pcWorldInverse.elements 
@@ -1419,8 +1421,8 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 			node.sceneNode.visible = true;
 			node.sceneNode.material = pointcloud.material;
 
-			visibleNodes.push(node);
-			pointcloud.visibleNodes.push(node);
+			/* level >= minLevel && */visibleNodes.push(node);
+			/* level >= minLevel && */pointcloud.visibleNodes.push(node);
 
 			if(node._transformVersion === undefined){
 				node._transformVersion = -1;

+ 2 - 1
src/custom/utils/CursorDeal.js

@@ -20,13 +20,14 @@ var CursorDeal = {
         {"movePointcloud":'move'}, 
         {"polygon_isIntersectSelf":'not-allowed'},
         {"polygon_AtWrongPlace":'not-allowed'},
+        {'delPoint':'url("https://4dkk.4dage.com/v4-test/www/sdk/images/polygon_mark/pic_pen_sub.png"), auto'},
         {"markerMove":'grab'},
+        {'addPoint':'url("https://4dkk.4dage.com/v4-test/www/sdk/images/polygon_mark/pic_pen_add.png"), auto'},
         {'mapClipMove':'move'},
         {'mapClipRotate':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
         {'rotatePointcloud':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
         {'siteModelFloorDrag':'row-resize'},
         {'addSth':'cell'},//or  crosshair
-         
     ], 
     list:[], //当前存在的cursor状态
     currentCursorIndex:null,

+ 38 - 3
src/custom/utils/DrawUtil.js

@@ -277,21 +277,56 @@ var MeshDraw = {
                 if(currentIndex == 0)return 0
                 return total + currentValue.distanceTo(arr[currentIndex-1]);
             },0)
-            options.extrudePath = new THREE.CatmullRomCurve3(options.extrudePath, options.closed ,  'catmullrom'  /* 'centripetal' */  , options.tension)//tension:拐弯剧烈程度,但随着长度增长,该值需要减小,否则会扭曲
             //options.extrudePath = new THREE.CatmullRomCurve3(options.extrudePath)
             if(options.extrudePath.length == 2){
                 options.tension = 0 ;//否则一端扭曲
                 options.steps = 1
             }
+            
+            if(options.dontSmooth){
+                let curvePath = new THREE.CurvePath()//通用的曲线路径对象,它可以包含直线段和曲线段。在这里只做折线
+                for (let i = 0; i < options.extrudePath.length - 1; i++){ 
+                    let curve3 = new THREE.LineCurve3(options.extrudePath[i], options.extrudePath[i + 1]);//添加线段
+                    curvePath.add(curve3);
+                } 
+                options.extrudePath = curvePath
+            }else{
+                
+                /* CatmullRomCurve3 经常扭曲,如果两个点靠得很近可能会扭曲,这里去除靠的太近的点。但去除后依旧会出现一定扭曲
+                let vertices =  pathModel.children[0].geometry.vertices.slice(10,80)
+                let extrudePath = []
+                const minDis = 1
+                vertices.forEach((p,i)=>{
+                    if(i==0 || i== vertices.length-1)return extrudePath.push(p) //首尾直接加入
+                    let last = extrudePath[extrudePath.length-1]
+                    let dis = last.distanceTo(p)
+                    if(dis < minDis){
+                        console.log(`第${i}个点(${p.toArray()})因为和上一个数据(${last.toArray()})距离太短(${dis})所以删除`)
+                    }else if(i == vertices.length - 2){//因为最后一个必定加入,所以倒数第二个还也不能太靠近最后一个
+                        last = extrudePath[vertices.length-1]
+                        if(dis < minDis){
+                            console.log(`第${i}个点(${p.toArray()})因为和下一个数据(${last.toArray()})距离太短(${dis})所以删除`)
+                        }else{
+                            extrudePath.push(p)
+                        }
+                    }else{
+                        extrudePath.push(p)
+                    }
+                    
+                }) */
+                //平滑连续的曲线(但经常会有扭曲的问题,tension:0能缓解, 另外shape和path都最好在原点附近,也就是点需减去bound.min )
+                options.extrudePath = new THREE.CatmullRomCurve3(options.extrudePath, options.closed ,  'catmullrom'  /* 'centripetal' */  , options.tension)//tension:拐弯剧烈程度,但随着长度增长,该值需要减小,否则会扭曲
+                                     
+            } 
         }
         
  
         var extrudeSettings = $.extend(options,{
             steps: options.steps != void 0 ? options.steps : ( options.extrudePath ? Math.round(length/(options.spaceDis || 0.3)) : 1), //分成几段    spaceDis每段长度
             bevelEnabled: false, //不加的话,height为0时会有圆弧高度
-            //depth
+            //openEnded默认false 
         }) 
-        var geometry = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings );
+        var geometry = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings ); //修改了three.js文件,  buildLidFaces处,创建顶底面加了选项,可以选择开口。 
         return geometry;
     },
     

+ 204 - 177
src/custom/viewer/ViewerNew.js

@@ -82,6 +82,7 @@ import {OrbitControls} from "../../navigation/OrbitControlsNew.js";
 
 import { ClassificationScheme } from "../../materials/ClassificationScheme.js";
 import { VRButton } from '../../../libs/three.js/extra/VRButton.js';
+import DxfLoader from '../../loader/DxfLoader.js'
  
  
  
@@ -118,7 +119,8 @@ export class Viewer extends ViewerBase{
         if(Potree.settings.editType == "pano" || Potree.settings.editType == "merge"){
             this.modules = { 
                 Alignment,  
-                SiteModel
+                SiteModel,
+                volumeComputer: new VolumeComputer //暂时
             }
             Potree.settings.useDepthTex = false
             
@@ -138,7 +140,8 @@ export class Viewer extends ViewerBase{
                 clipping: new Clipping,
                 ParticleEditor,
                 CamAniEditor, 
-                volumeComputer: new VolumeComputer
+                volumeComputer: new VolumeComputer,
+                MergeEditor 
             }
         }
         {
@@ -582,6 +585,7 @@ export class Viewer extends ViewerBase{
                 mtlLoader : new MTLLoader( manager ),
                 glbLoader : new GLTFLoader(undefined, this.renderer, Potree.settings.libsUrl ),
                 plyLoader : new PLYLoader( manager ),
+                dxfLoader : new DxfLoader() 
             } 
             //add test
             /* const environment = new RoomEnvironment();
@@ -712,7 +716,7 @@ export class Viewer extends ViewerBase{
             this.scene.pointclouds.forEach(pointcloud=>{
                 pointcloud.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
                     //console.log('pointcloud  isVisible', this.id, e.visible)  
-                    if(e.reason != 'displayMode' ){
+                    if(e.reason != 'displayMode' && e.reason != 'overlinePass'){
                          this.updateModelBound('visibleChanged')
                     }
                     this.dispatchEvent('pointcloud_changed') 
@@ -3786,8 +3790,8 @@ export class Viewer extends ViewerBase{
                     }
                     
                     
-                    
-                    
+                    mapViewport.noPointcloud = oldStates.noPointcloud
+                     
                     
                     Potree.Utils.updateVisible(viewer.mapViewer.mapLayer.sceneGroup, 'screenshot-prism', true) 
                 
@@ -3841,7 +3845,7 @@ export class Viewer extends ViewerBase{
             viewports.push(mapViewport)
             oldStates.viewports.push(mapViewport.clone())  
             oldStates.attachedToViewer = this.mapViewer.attachedToViewer
-         
+            oldStates.noPointcloud = mapViewport.noPointcloud
              
              
             Potree.Utils.updateVisible(this.mapViewer.cursor, 'screenshot', false)//令mapCursor不可见
@@ -3960,32 +3964,28 @@ export class Viewer extends ViewerBase{
             }) 
             this.scene.measurements.forEach(e=>Potree.Utils.updateVisible(e,'screenshot', prisms.includes(e))  )
             
-            
+            this.backgroundOpacity = 0
             
             let {promise} = this.focusOnObject({points}, 'prisms2d', 0, { 
                 minMapWidth: 0.5  , onlyMap:true,  focusBoundCenter:true, boundExpandRatio:1.4
-            })  
-             
-            this.backgroundOpacity = 0
-                 
-            promise.done(()=>{
+            })
+               
+            promise.done(()=>{  
                 updateCamera() 
-                focusDatasets(prisms) //更新平面图  可能有漏洞,当不在任何一个数据集内的话?
-                this.mapViewer.waitLoadDone(screenshot.bind(this))//等待地图所有加载完
+                if(info.type == 'prism2d-all'){//全部 带平面图    
+                    focusDatasets(prisms) //更新平面图  可能有漏洞,当不在任何一个数据集内的话?
+                    this.mapViewer.waitLoadDone(screenshot.bind(this))//等待地图所有加载完
                 
-                if(info.type == 'prism2d-all'){//全部 带平面图   
+                }else{//single
+                    mapViewport.noPointcloud = false
+                    Potree.Utils.updateVisible(viewer.mapViewer.mapLayer.sceneGroup, 'screenshot-prism', false)
                     
-                }else{//单个
+                    this.waitPointLoad( screenshot)
+                }    
+             
                  
-                    
-                    //info.prism.setSelected(true, 'screenshot')
-                    //viewer.mapViewer.transparentBG = true
-                
-                    /* Potree.Utils.updateVisible(viewer.mapViewer.mapLayer.sceneGroup, 'screenshot-prism', false)
-
-                    screenshot() */
-                }
             })
+         
              
         }else{//default
         
@@ -4967,7 +4967,7 @@ export class Viewer extends ViewerBase{
         this.bound = Utils.computePointcloudsBound(this.scene.pointclouds.filter(pointcloud=> //只求可见
             pointcloud.visible || pointcloud.unvisibleReasons && pointcloud.unvisibleReasons.length == 1 && pointcloud.unvisibleReasons[0].reason == 'displayMode'
         )) 
-        if(Potree.settings.boundAddObjs){//加上obj的bound 
+        if(Potree.settings.boundAddObjs){//加上obj的bound   需要确保都updateMatrixWorld过
             this.objs.children.forEach(e=>{ 
                 this.bound.boundingBox.union(e.boundingBox.clone().applyMatrix4(e.matrixWorld))
             })
@@ -5146,6 +5146,158 @@ export class Viewer extends ViewerBase{
     } 
      */
 
+    modelLoaded(object, fileInfo_={}, done){//普通模型加载完以后
+        object.isModel = true
+        let boundingBox = new THREE.Box3()
+        if(fileInfo_.parentInfo){
+            object.name = fileInfo_.name   
+            fileInfo_.parentInfo.loadedCount ++
+            fileInfo_.parentInfo.modelGroup.add(object) 
+            if(fileInfo_.parentInfo.loadedCount == fileInfo_.parentInfo.url.length){ 
+                return loadDone(fileInfo_.parentInfo.modelGroup, fileInfo_.parentInfo)
+            }else{ 
+                return
+            }   
+        }
+        
+        object.name = fileInfo_.name != void 0 ? fileInfo_.name : fileInfo_.type
+        object.fileType = fileInfo_.fileType
+        object.boundingBox = boundingBox  //未乘上matrixWorld的本地boundingBox
+        //object.scale.set(1,1,1);//先获取原始的大小时的boundingBox
+        object.opacity = 1 //初始化 记录
+        object.updateMatrixWorld()
+        
+        if(fileInfo_.id != void 0)object.dataset_id = fileInfo_.id
+        
+        
+        fileInfo_.loadCostTime = Date.now() - fileInfo_.loadStartTime
+        /* let weight = Math.round((total / 1024 / 1024) * 100) / 100;*/
+        console.log( '加载完毕:', Common.getNameFromURL(fileInfo_.url),  '耗时(ms)', fileInfo_.loadCostTime, /* 模型数据量:' + weight + 'M' */)
+          
+           
+        if(fileInfo_.fileType == '3dTiles'){
+            let tileset = object.runtime.getTileset()
+           
+            //TileHeader: tileset.root 
+            //参见另一个工程 TileRenderer.js  preprocessNode //这个坐标位置几万…… let data = boundingVolume.halfAxes  //但这个似乎是premultiply( transform );过后的,可能需还原下
+            
+            let json = tileset.tileset   
+            let box = json.root.boundingVolume.box
+             
+            if(box){
+                let center = new THREE.Vector3(box[0],box[1],box[2])
+                let boundSize = new THREE.Vector3( )  
+                 
+                // get the extents of the bounds in each axis
+                let vecX = new THREE.Vector3( box[ 3 ], box[ 4 ], box[ 5 ] )
+                let vecY = new THREE.Vector3( box[ 6 ], box[ 7 ], box[ 8 ] );
+                let vecZ = new THREE.Vector3( box[ 9 ], box[ 10 ], box[ 11 ] );
+
+                const scaleX = vecX.length();
+                const scaleY = vecY.length();
+                const scaleZ = vecZ.length();
+                /* boundingBox.expandByPoint(center);
+                boundingBox.expandByVector(new THREE.Vector3(scaleX,scaleY,scaleZ)) */
+                 
+                
+                boundingBox.min.set( - scaleX, - scaleY, - scaleZ );
+                boundingBox.max.set( scaleX, scaleY, scaleZ );
+                
+            }else if(json.root.boundingVolume.sphere){
+                let sphereData = json.root.boundingVolume.sphere
+                let center = new THREE.Vector3(...sphereData)
+                let radius = sphereData[3] / 2
+                /* let sphere = new THREE.Sphere(center, radius) 
+                let box = sphere.getBoundingBox()
+                boundingBox.copy(box) */
+                 
+                boundingBox.min.set( - radius, - radius, - radius );
+                boundingBox.max.set( radius, radius, radius );
+                
+                
+                
+            }else{
+                return console.error('json boundingVolume 缺少信息') 
+            }
+            
+            //中心点居然没用。可能是漏用了什么信息,也许这和LVBADUI_qp是散的有关。
+            console.log('3d tiles json',json)
+            
+            json.root.refine = 'ADD';
+            json.refine = 'ADD';
+        }else { 
+            Potree.Utils.setObjectLayers(object,'model')  
+            
+            
+            object.traverse( ( child )=>{ 
+                let is = child.isMesh || child instanceof THREE.Points || child.isLine
+                
+                if (is){
+                    
+                    child.renderOrder = Potree.config.renderOrders.model;
+                    //if(Potree.settings.boundAddObjs){
+                        child.geometry.computeBoundingBox()
+                        //console.log(child.matrixWorld.clone())
+                        boundingBox.union(child.geometry.boundingBox.clone().applyMatrix4(child.matrixWorld)) //但感觉如果最外层object大小不为1,要还原下scale再乘
+                    //}//获取在scale为1时,表现出的大小
+                    //Potree.Utils.makeTexDontResize(child.material.map) 
+                    //console.log(child.name, 'roughness',child.material.roughness,'metalness',child.material.metalness)
+                       
+
+                     //暂时用这种材质:
+                    if(fileInfo_.unlit && (!(child.material instanceof THREE.MeshBasicMaterial) || object.fileType == 'glb')){
+                        //let material = new THREE.MeshBasicMaterial({map:child.material.map})
+                        let material = new BasicMaterial({map : child.material.map})  //很奇怪glb的图会使原本的MeshBasicMaterial 会偏暗,所以自己重新写
+                        
+                        //child.material.dispose()
+                        child.material = material 
+                    } 
+                    if(fileInfo_.useStandandMat && !(child.material instanceof THREE.MeshStandardMaterial)){
+                        child.material = new THREE.MeshStandardMaterial()
+                    } 
+                    if(child.material instanceof THREE.MeshStandardMaterial){
+                        child.material.roughness = 0.7
+                        child.material.metalness = 0.3 
+                    }
+                } 
+            } );
+        }
+        this.objs.add(object) 
+        
+        
+        if(fileInfo_.transform){
+            let setTransfrom = (name)=>{
+                let value = fileInfo_.transform[name]
+                if(!value)return
+                if(value instanceof Array){
+                    object[name].fromArray(value)
+                }else{ 
+                    object[name].copy(value)
+                }
+            } 
+            setTransfrom('position')
+            setTransfrom('rotation')
+            setTransfrom('scale')
+              
+        }
+        
+        
+        if(fileInfo_.moveWithPointcloud){
+            object.updateMatrix();
+            object.matrixAutoUpdate = false
+            object.matrix.premultiply(viewer.scene.pointclouds[0].transformMatrix) //默认跟随第一个数据集
+            object.matrixWorldNeedsUpdate = true 
+        }
+        object.updateMatrixWorld()
+        MergeEditor.getBoundCenter(object) //初始化       
+        done && done(object, fileInfo_)
+            
+        this.dispatchEvent({type:'modelLoaded',model:object})
+    }
+    
+    
+    
+    
     async loadModel(fileInfo, done, onProgress_, onError){ 
         console.log('开始加载',  Common.getNameFromURL(fileInfo.url) )
     
@@ -5177,144 +5329,8 @@ export class Viewer extends ViewerBase{
         //let fileType =  fileInfo.tilesUrl ? '3dTiles' :  fileInfo.objurl ? 'obj' : 'glb'
          
         
-        let loadDone = (object,   fileInfo_    /* , total, url */)=>{ 
-            
-            fileInfo_ = fileInfo_ || fileInfo
-            if(fileInfo_.parentInfo){
-                object.name = fileInfo_.name   
-                fileInfo_.parentInfo.loadedCount ++
-                fileInfo_.parentInfo.modelGroup.add(object) 
-                if(fileInfo_.parentInfo.loadedCount == fileInfo_.parentInfo.url.length){ 
-                    return loadDone(fileInfo_.parentInfo.modelGroup, fileInfo_.parentInfo)
-                }else{ 
-                    return
-                }   
-            }
-            
-            object.name = fileInfo_.name != void 0 ? fileInfo_.name : fileInfo_.type
-            object.fileType = fileInfo_.fileType
-            object.boundingBox = boundingBox  //未乘上matrixWorld的本地boundingBox
-            //object.scale.set(1,1,1);//先获取原始的大小时的boundingBox
-            object.opacity = 1 //初始化 记录
-            object.updateMatrixWorld()
-            
-            if(fileInfo_.id != void 0)object.dataset_id = fileInfo_.id
-            
-            
-            fileInfo_.loadCostTime = Date.now() - fileInfo_.loadStartTime
-            /* let weight = Math.round((total / 1024 / 1024) * 100) / 100;*/
-            console.log( '加载完毕:', Common.getNameFromURL(fileInfo_.url),  '耗时(ms)', fileInfo_.loadCostTime, /* 模型数据量:' + weight + 'M' */)
-             
-             
-            if(fileInfo_.fileType == '3dTiles'){
-                let tileset = object.runtime.getTileset()
-               
-                //TileHeader: tileset.root 
-                //参见另一个工程 TileRenderer.js  preprocessNode //这个坐标位置几万…… let data = boundingVolume.halfAxes  //但这个似乎是premultiply( transform );过后的,可能需还原下
-                
-                let json = tileset.tileset   
-                let box = json.root.boundingVolume.box
-                
-                if(box){
-                    let center = new THREE.Vector3(box[0],box[1],box[2])
-                    let boundSize = new THREE.Vector3( )  
-                     
-                    // get the extents of the bounds in each axis
-                    let vecX = new THREE.Vector3( box[ 3 ], box[ 4 ], box[ 5 ] )
-                    let vecY = new THREE.Vector3( box[ 6 ], box[ 7 ], box[ 8 ] );
-                    let vecZ = new THREE.Vector3( box[ 9 ], box[ 10 ], box[ 11 ] );
-
-                    const scaleX = vecX.length();
-                    const scaleY = vecY.length();
-                    const scaleZ = vecZ.length();
-                    /* boundingBox.expandByPoint(center);
-                    boundingBox.expandByVector(new THREE.Vector3(scaleX,scaleY,scaleZ)) */
-                     
-                    
-                    boundingBox.min.set( - scaleX, - scaleY, - scaleZ );
-                    boundingBox.max.set( scaleX, scaleY, scaleZ );
-                    
-                }else if(json.root.boundingVolume.sphere){
-                    let sphereData = json.root.boundingVolume.sphere
-                    let center = new THREE.Vector3(...sphereData)
-                    let radius = sphereData[3] / 2
-                    /* let sphere = new THREE.Sphere(center, radius) 
-                    let box = sphere.getBoundingBox()
-                    boundingBox.copy(box) */
-                     
-                    boundingBox.min.set( - radius, - radius, - radius );
-                    boundingBox.max.set( radius, radius, radius );
-                    
-                    
-                    
-                }else{
-                    return console.error('json boundingVolume 缺少信息') 
-                }
-                
-                //中心点居然没用。可能是漏用了什么信息,也许这和LVBADUI_qp是散的有关。
-                console.log('3d tiles json',json)
-                
-                json.root.refine = 'ADD';
-                json.refine = 'ADD';
-            }else {
-                Potree.Utils.setObjectLayers(object,'model')  
-                
-                
-                object.traverse( ( child )=>{ 
-                    if ( child instanceof THREE.Mesh || child instanceof THREE.Points ) { 
-                        child.renderOrder = Potree.config.renderOrders.model;
-                        if(Potree.settings.boundAddObjs){
-                            child.geometry.computeBoundingBox()
-                            //console.log(child.matrixWorld.clone())
-                            boundingBox.union(child.geometry.boundingBox.clone().applyMatrix4(child.matrixWorld)) //但感觉如果最外层object大小不为1,要还原下scale再乘
-                        }//获取在scale为1时,表现出的大小
-                        //Potree.Utils.makeTexDontResize(child.material.map) 
-                        //console.log(child.name, 'roughness',child.material.roughness,'metalness',child.material.metalness)
-                        
-     
-                        /* //暂时用这种材质:
-                        if(fileInfo.unlit && (!(child.material instanceof THREE.MeshBasicMaterial) || fileType == 'glb')){
-                            //let material = new THREE.MeshBasicMaterial({map:child.material.map})
-                            let material = new BasicMaterial({map : child.material.map})  //很奇怪glb的图会使原本的MeshBasicMaterial 会偏暗,所以自己重新写
-                            
-                            //child.material.dispose()
-                            child.material = material 
-                        } */
-                        if(child.material instanceof THREE.MeshStandardMaterial){
-                            child.material.roughness = 0.6 
-                            child.material.metalness = 0.3 
-                        }
-                    }
-                } );
-            } 
-            this.objs.add(object) 
-            
-            
-            if(fileInfo_.transform){
-                let setTransfrom = (name)=>{
-                    let value = fileInfo_.transform[name]
-                    if(!value)return
-                    if(value instanceof Array){
-                        object[name].fromArray(value)
-                    }else{ 
-                        object[name].copy(value)
-                    }
-                } 
-                setTransfrom('position')
-                setTransfrom('rotation')
-                setTransfrom('scale')
-            }
-            
-            
-            if(fileInfo_.moveWithPointcloud){
-                object.updateMatrix();
-                object.matrixAutoUpdate = false
-                object.matrix.premultiply(viewer.scene.pointclouds[0].transformMatrix) //默认跟随第一个数据集
-                object.matrixWorldNeedsUpdate = true 
-            }
-            done && done(object)
-        
-            
+        let loadDone = (object,   fileInfo_   )=>{ 
+            this.modelLoaded(object,   fileInfo_ || fileInfo  , done)
         }
         
         
@@ -5327,14 +5343,19 @@ export class Viewer extends ViewerBase{
         };
     
         if(fileInfo.fileType == 'obj'){ //暂时不支持数组
-            loaders.mtlLoader.load( fileInfo.mtlurl , (materials)=>{ 
-                materials.preload(); 
-        
-                loaders.objLoader.setMaterials( materials ).load(fileInfo.objurl, (object, total)=>{  
-                    loadDone(object/* , total, fileInfo.objurl */)
-                })
-            } , onProgress,  onError  );  
+            if(fileInfo.mtlurl){ 
+                loaders.mtlLoader.load( fileInfo.mtlurl , (materials)=>{ 
+                    materials.preload(); 
             
+                    loaders.objLoader.setMaterials( materials ).load(fileInfo.objurl, (object, total)=>{  
+                        loadDone(object/* , total, fileInfo.objurl */)
+                    })
+                } , onProgress,  onError  );  
+            }else{
+                loaders.objLoader.load(fileInfo.objurl, (object, total)=>{  
+                    loadDone(object)
+                })
+            }
         }else if(fileInfo.fileType == 'glb'){
             loaders.glbLoader.unlitMat = true//!!fileInfo.unlit
             loaders.glbLoader.load(fileInfo.url,  ( gltf, total )=>{    
@@ -5365,15 +5386,16 @@ export class Viewer extends ViewerBase{
                     //dracoDecoderPath: '../utils/loaders/DRACOLoader/draco',
                     //basisTranscoderPath: '../utils/loaders/KTX2Loader/basis',
                     maximumScreenSpaceError: 30,  //如果本身tiles很密很小这个值就不能很大。
-                    maxDepth: 100, 
+                    //maxDepth: 100, 
                     maximumMemoryUsage: 200, //缓存大小。单位M(但实际结果是 2.5*maximumMemoryUsage + 750  。超过2G会崩, 所以应该小于540) 若太小,密集的tile反复加载很卡. (任务管理器刷新网页后若内存不掉就要结束进程否则虚高)
                     //debug:true,
-                    parent: this.scene.scene
+                    parent: this.scene.scene,
+                    is4dkk: fileInfo.is4dkk,//是否是4dkk中的模型
+                    updateTime: fileInfo.updateTime, //加后缀防止缓存
                 },
             })
             console.log(result)
             result.model.runtime = result.runtime
-            loadDone(result.model/* , null, fileInfo.url */) 
  
                
             let loaded = false
@@ -5386,10 +5408,11 @@ export class Viewer extends ViewerBase{
             });
             tileset.addEventListener('tileLoaded',(e)=>{ //每一个tile加载完要更改透明度
                 let opacity = result.model.opacity
-                MergeEditor.changeOpacity(e.tileContent,opacity) 
+                MergeEditor.changeOpacity(e.tileContent,opacity)
+                //set Layers ?
             })
             
-            {
+            { 
                 let vi = true
                 Object.defineProperty( result.model, "visible", {
                     get: function() {
@@ -5401,10 +5424,14 @@ export class Viewer extends ViewerBase{
                         tileset.nextForceUpdate = true
                     }
                 })  
-            }
-             
+            } 
             
+            loadDone(result.model/* , null, fileInfo.url */) 
             
+        }else if(fileInfo.fileType == 'dxf'){
+            loaders.dxfLoader.load(fileInfo.url,(object)=>{
+                loadDone(object)
+            }) 
         }
         
          

+ 699 - 0
src/loader/DxfLoader.js

@@ -0,0 +1,699 @@
+import * as THREE from "../../libs/three.js/build/three.module.js"; 
+import  '../../libs/three.js/loaders/dxf/dxf-parser.js'  
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+export default class DxfLoader extends THREE.EventDispatcher{
+ 
+    constructor(){
+        super()
+        
+        this.parser = new Potree.DxfParser
+    }
+    
+    
+    load(url,done){
+        Potree.loadFile(url, {returnText:true} , (data)=>{
+             
+            var dxfJson = this.parser.parseSync(data);
+            
+            console.log(dxfJson)
+            let model = this.createModel(dxfJson)
+            
+            done(model)
+        })
+    }
+    
+    
+    
+    createModel(data){
+        let root = new THREE.Object3D;
+
+        
+        var i, entity, obj, min_x, min_y, min_z, max_x, max_y, max_z;
+       /*  var dims = {
+            min: { x: false, y: false, z: false},
+            max: { x: false, y: false, z: false}
+        }; */
+        
+        //let bound = THREE.Box3
+        
+        for(i = 0; i < data.entities.length; i++) {
+            entity = data.entities[i];
+            obj = this.drawEntity(entity, data);
+ 
+            if (obj) {
+                var bbox = new THREE.Box3().setFromObject(obj);
+                /* if (bbox.min.x && ((dims.min.x === false) || (dims.min.x > bbox.min.x))) dims.min.x = bbox.min.x;
+                if (bbox.min.y && ((dims.min.y === false) || (dims.min.y > bbox.min.y))) dims.min.y = bbox.min.y;
+                if (bbox.min.z && ((dims.min.z === false) || (dims.min.z > bbox.min.z))) dims.min.z = bbox.min.z;
+                if (bbox.max.x && ((dims.max.x === false) || (dims.max.x < bbox.max.x))) dims.max.x = bbox.max.x;
+                if (bbox.max.y && ((dims.max.y === false) || (dims.max.y < bbox.max.y))) dims.max.y = bbox.max.y;
+                if (bbox.max.z && ((dims.max.z === false) || (dims.max.z < bbox.max.z))) dims.max.z = bbox.max.z; */
+                root.add(obj);
+            }
+            obj = null;
+        }
+        return root
+     
+    }
+    
+    
+    drawEntity(entity, data) {
+        var mesh;
+        if(entity.type === 'CIRCLE' || entity.type === 'ARC') {
+            mesh = drawArc(entity, data);
+        } else if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE' || entity.type === 'POLYLINE') {
+            mesh = drawLine(entity, data);
+        } else if(entity.type === 'TEXT') {
+            mesh = drawText(entity, data);
+        } else if(entity.type === 'SOLID') {
+            mesh = drawSolid(entity, data);
+        } else if(entity.type === 'POINT') {
+            mesh = drawPoint(entity, data);
+        } else if(entity.type === 'INSERT') {
+            mesh = drawBlock(entity, data);
+        }  else if(entity.type === 'SPLINE') {
+            mesh = drawSpline(entity, data);
+        } else if(entity.type === 'MTEXT') {
+            mesh = drawMtext(entity, data);
+        } else if(entity.type === 'ELLIPSE') {
+            mesh = drawEllipse(entity, data);
+        } else if(entity.type === 'DIMENSION') {
+            var dimTypeEnum = entity.dimensionType & 7;
+            if(dimTypeEnum === 0) {
+                mesh = drawDimension(entity, data);
+            } else {
+                console.log("Unsupported Dimension type: " + dimTypeEnum);
+            }
+        }
+        else {
+            console.log("Unsupported Entity Type: " + entity.type);
+        }   
+         
+        if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE' || entity.type === 'POLYLINE') {
+            mesh = drawLine(entity, data);
+        } 
+        
+        /* if(entity.type === 'INSERT') {
+            mesh = drawBlock(entity, data);     //group
+        }  */
+        /* 
+        
+            门都是arc
+            似乎墙壁都是不是insert类型,但窗户是
+         */
+        return mesh;
+    }
+    
+    
+    
+    
+    
+    
+    
+    
+}
+
+
+
+function drawEllipse(entity, data) {
+        var color = getColor(entity, data);
+
+        var xrad = Math.sqrt(Math.pow(entity.majorAxisEndPoint.x,2) + Math.pow(entity.majorAxisEndPoint.y,2));
+        var yrad = xrad*entity.axisRatio;
+        var rotation = Math.atan2(entity.majorAxisEndPoint.y, entity.majorAxisEndPoint.x);
+
+        var curve = new THREE.EllipseCurve(
+            entity.center.x,  entity.center.y,
+            xrad, yrad,
+            entity.startAngle, entity.endAngle,
+            false, // Always counterclockwise
+            rotation
+        );
+
+        var points = curve.getPoints( 50 );
+        var geometry = new THREE.BufferGeometry().setFromPoints( points );
+        var material = new THREE.LineBasicMaterial( {  linewidth: 1, color : color } );
+
+        // Create the final object to add to the scene
+        var ellipse = new THREE.Line( geometry, material );
+        return ellipse;
+    }
+
+    function drawMtext(entity, data) {
+        var color = getColor(entity, data);
+
+        if (!font) { return console.log('font parameter not set. Ignoring text entity.')}
+
+        var geometry = new THREE.TextGeometry( entity.text, {
+            font: font,
+            size: entity.height * (4/5),
+            height: 1
+        });
+        var material = new THREE.MeshBasicMaterial( {color: color} );
+        var text = new THREE.Mesh( geometry, material );
+
+        // Measure what we rendered.
+        var measure = new THREE.Box3();
+        measure.setFromObject( text );
+
+        var textWidth  = measure.max.x - measure.min.x;
+
+        // If the text ends up being wider than the box, it's supposed
+        // to be multiline. Doing that in threeJS is overkill.
+        if (textWidth > entity.width) {
+            console.log("Can't render this multipline MTEXT entity, sorry.", entity);
+            return undefined;
+        }
+
+        text.position.z = 0;
+        switch (entity.attachmentPoint) {
+            case 1:
+                // Top Left
+                text.position.x = entity.position.x;
+                text.position.y = entity.position.y - entity.height;
+            break;
+            case 2:
+                // Top Center
+                text.position.x = entity.position.x - textWidth/2;
+                text.position.y = entity.position.y - entity.height;
+            break;
+            case 3:
+                // Top Right
+                text.position.x = entity.position.x - textWidth;
+                text.position.y = entity.position.y - entity.height;
+            break;
+
+            case 4:
+                // Middle Left
+                text.position.x = entity.position.x;
+                text.position.y = entity.position.y - entity.height/2;
+            break;
+            case 5:
+                // Middle Center
+                text.position.x = entity.position.x - textWidth/2;
+                text.position.y = entity.position.y - entity.height/2;
+            break;
+            case 6:
+                // Middle Right
+                text.position.x = entity.position.x - textWidth;
+                text.position.y = entity.position.y - entity.height/2;
+            break;
+
+            case 7:
+                // Bottom Left
+                text.position.x = entity.position.x;
+                text.position.y = entity.position.y;
+            break;
+            case 8:
+                // Bottom Center
+                text.position.x = entity.position.x - textWidth/2;
+                text.position.y = entity.position.y;
+            break;
+            case 9:
+                // Bottom Right
+                text.position.x = entity.position.x - textWidth;
+                text.position.y = entity.position.y;
+            break;
+
+            default:
+                return undefined;
+        };
+
+        return text;
+    }
+
+    function drawSpline(entity, data) {
+        var color = getColor(entity, data);
+
+        var points = entity.controlPoints.map(function(vec) {
+            return new THREE.Vector2(vec.x, vec.y);
+        });
+
+        var interpolatedPoints = [];
+        var curve;
+        if (entity.degreeOfSplineCurve === 2 || entity.degreeOfSplineCurve === 3) {
+            for(var i = 0; i + 2 < points.length; i = i + 2) {
+        if (entity.degreeOfSplineCurve === 2) {
+                        curve = new THREE.QuadraticBezierCurve(points[i], points[i + 1], points[i + 2]);
+        } else {
+            curve = new THREE.QuadraticBezierCurve3(points[i], points[i + 1], points[i + 2]);
+        }
+                interpolatedPoints.push.apply(interpolatedPoints, curve.getPoints(50));
+            }
+        } else {
+            curve = new THREE.SplineCurve(points);
+            interpolatedPoints = curve.getPoints( 100 );
+        }
+
+        var geometry = new THREE.BufferGeometry().setFromPoints( interpolatedPoints );
+        var material = new THREE.LineBasicMaterial( { linewidth: 1, color : color } );
+        var splineObject = new THREE.Line( geometry, material );
+
+        return splineObject;
+    }
+
+    function drawLine(entity, data) {
+        var geometry = new THREE.Geometry(),
+            color = getColor(entity, data),
+            material, lineType, vertex, startPoint, endPoint, bulgeGeometry,
+            bulge, i, line;
+
+        if (!entity.vertices) return console.log('entity missing vertices.');
+
+        // create geometry
+        for(i = 0; i < entity.vertices.length; i++) {
+
+            if(entity.vertices[i].bulge) { 
+                bulge = entity.vertices[i].bulge;
+                startPoint = entity.vertices[i];
+                endPoint = i + 1 < entity.vertices.length ? entity.vertices[i + 1] : geometry.vertices[0];
+
+                bulgeGeometry = new THREEx.BulgeGeometry(startPoint, endPoint, bulge);
+
+                geometry.vertices.push.apply(geometry.vertices, bulgeGeometry.vertices);
+            } else {
+                vertex = entity.vertices[i];
+                geometry.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0));
+            }
+
+        }
+        if(entity.shape) geometry.vertices.push(geometry.vertices[0]);
+         
+
+        // set material
+        if(entity.lineType) {
+            lineType = data.tables.lineType.lineTypes[entity.lineType];
+        }
+
+        if(lineType && lineType.pattern && lineType.pattern.length !== 0) {
+            material = new THREE.LineDashedMaterial({ color: color, gapSize: 4, dashSize: 4});
+        } else {
+            material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
+        }
+
+        // if(lineType && lineType.pattern && lineType.pattern.length !== 0) {
+
+        //           geometry.computeLineDistances();
+
+        //           // Ugly hack to add diffuse to this. Maybe copy the uniforms object so we
+        //           // don't add diffuse to a material.
+        //           lineType.material.uniforms.diffuse = { type: 'c', value: new THREE.Color(color) };
+
+        // 	material = new THREE.ShaderMaterial({
+        // 		uniforms: lineType.material.uniforms,
+        // 		vertexShader: lineType.material.vertexShader,
+        // 		fragmentShader: lineType.material.fragmentShader
+        // 	});
+        // }else {
+        // 	material = new THREE.LineBasicMaterial({ linewidth: 1, color: color });
+        // }
+
+        line = new THREE.Line(geometry, material);
+        return line;
+    }
+    
+    function drawArc(entity, data) {
+        var startAngle, endAngle;
+        if (entity.type === 'CIRCLE') {
+            startAngle = entity.startAngle || 0;
+            endAngle = startAngle + 2 * Math.PI;
+        } else {
+            startAngle = entity.startAngle;
+            endAngle = entity.endAngle;
+        }
+
+        var curve = new THREE.ArcCurve(
+            0, 0,
+            entity.radius,
+            startAngle,
+            endAngle);
+
+        var points = curve.getPoints( 32 );
+        var geometry = new THREE.BufferGeometry().setFromPoints( points );
+
+        var material = new THREE.LineBasicMaterial({ color: getColor(entity, data) });
+
+        var arc = new THREE.Line(geometry, material);
+        arc.position.x = entity.center.x;
+        arc.position.y = entity.center.y;
+        arc.position.z = entity.center.z;
+
+        return arc;
+    }
+
+    function drawSolid(entity, data) {
+        var material, mesh, verts,
+            geometry = new THREE.Geometry();
+
+        verts = geometry.vertices;
+        verts.push(new THREE.Vector3(entity.points[0].x, entity.points[0].y, entity.points[0].z));
+        verts.push(new THREE.Vector3(entity.points[1].x, entity.points[1].y, entity.points[1].z));
+        verts.push(new THREE.Vector3(entity.points[2].x, entity.points[2].y, entity.points[2].z));
+        verts.push(new THREE.Vector3(entity.points[3].x, entity.points[3].y, entity.points[3].z));
+
+        // Calculate which direction the points are facing (clockwise or counter-clockwise)
+        var vector1 = new THREE.Vector3();
+        var vector2 = new THREE.Vector3();
+        vector1.subVectors(verts[1], verts[0]);
+        vector2.subVectors(verts[2], verts[0]);
+        vector1.cross(vector2);
+
+        // If z < 0 then we must draw these in reverse order
+        if(vector1.z < 0) {
+            geometry.faces.push(new THREE.Face3(2, 1, 0));
+            geometry.faces.push(new THREE.Face3(2, 3, 1));
+        } else {
+            geometry.faces.push(new THREE.Face3(0, 1, 2));
+            geometry.faces.push(new THREE.Face3(1, 3, 2));
+        }
+
+
+        material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) });
+
+        return new THREE.Mesh(geometry, material);
+        
+    }
+
+    function drawText(entity, data) {
+        var geometry, material, text;
+
+        if(!font)
+            return console.warn('Text is not supported without a Three.js font loaded with THREE.FontLoader! Load a font of your choice and pass this into the constructor. See the sample for this repository or Three.js examples at http://threejs.org/examples/?q=text#webgl_geometry_text for more details.');
+        
+        geometry = new THREE.TextGeometry(entity.text, { font: font, height: 0, size: entity.textHeight || 12 });
+
+        if (entity.rotation) {
+            var zRotation = entity.rotation * Math.PI / 180;
+            geometry.rotateZ(zRotation);
+        }
+
+        material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) });
+
+        text = new THREE.Mesh(geometry, material);
+        text.position.x = entity.startPoint.x;
+        text.position.y = entity.startPoint.y;
+        text.position.z = entity.startPoint.z;
+
+        return text;
+    }
+
+    function drawPoint(entity, data) {
+        var geometry, material, point;
+
+        geometry = new THREE.Geometry();
+
+        geometry.vertices.push(new THREE.Vector3(entity.position.x, entity.position.y, entity.position.z));
+
+        // TODO: could be more efficient. PointCloud per layer?
+
+        var numPoints = 1;
+
+        var color = getColor(entity, data);
+        var colors = new Float32Array( numPoints*3 );
+        colors[0] = color.r;
+        colors[1] = color.g;
+        colors[2] = color.b;
+
+        geometry.colors = colors;
+        geometry.computeBoundingBox();
+
+        material = new THREE.PointsMaterial( { size: 0.05, vertexColors: THREE.VertexColors } );
+        point = new THREE.Points(geometry, material);
+        scene.add(point);
+    }
+
+    function drawDimension(entity, data) {
+        var block = data.blocks[entity.block];
+
+        if (!block || !block.entities) return null;
+
+        var group = new THREE.Object3D();
+        // if(entity.anchorPoint) {
+        //     group.position.x = entity.anchorPoint.x;
+        //     group.position.y = entity.anchorPoint.y;
+        //     group.position.z = entity.anchorPoint.z;
+        // }
+
+        for(var i = 0; i < block.entities.length; i++) {
+            var childEntity = drawEntity(block.entities[i], data, group);
+            if(childEntity) group.add(childEntity);
+        }
+
+        return group;
+    }
+
+    function drawBlock(entity, data) {
+        var block = data.blocks[entity.name];
+        
+        if (!block.entities) return null;
+
+        var group = new THREE.Object3D()
+        
+        if(entity.xScale) group.scale.x = entity.xScale;
+        if(entity.yScale) group.scale.y = entity.yScale;
+
+        if(entity.rotation) {
+            group.rotation.z = entity.rotation * Math.PI / 180;
+        }
+
+        if(entity.position) {
+            group.position.x = entity.position.x;
+            group.position.y = entity.position.y;
+            group.position.z = entity.position.z;
+        }
+        
+        for(var i = 0; i < block.entities.length; i++) {
+            var childEntity = drawEntity(block.entities[i], data, group);
+            if(childEntity) group.add(childEntity);
+        }
+
+        return group;
+    }
+
+    function getColor(entity, data) {
+        var color = 0x000000; //default
+        if(entity.color) color = entity.color;
+        else if(data.tables && data.tables.layer && data.tables.layer.layers[entity.layer])
+            color = data.tables.layer.layers[entity.layer].color;
+            
+        if(color == null || color === 0xffffff) {
+            color = 0x000000;
+        }
+        return color;
+    }
+
+    function createLineTypeShaders(data) {
+        var ltype, type;
+        if(!data.tables || !data.tables.lineType) return;
+        var ltypes = data.tables.lineType.lineTypes;
+
+        for(type in ltypes) {
+            ltype = ltypes[type];
+            if(!ltype.pattern) continue;
+            ltype.material = createDashedLineShader(ltype.pattern);
+        }
+    }
+
+    function createDashedLineShader(pattern) {
+        var i,
+            dashedLineShader = {},
+            totalLength = 0.0;
+
+        for(i = 0; i < pattern.length; i++) {
+            totalLength += Math.abs(pattern[i]);
+        }
+
+        dashedLineShader.uniforms = THREE.UniformsUtils.merge([
+
+            THREE.UniformsLib[ 'common' ],
+            THREE.UniformsLib[ 'fog' ],
+
+            {
+                'pattern': { type: 'fv1', value: pattern },
+                'patternLength': { type: 'f', value: totalLength }
+            }
+
+        ]);
+
+        dashedLineShader.vertexShader = [
+            'attribute float lineDistance;',
+
+            'varying float vLineDistance;',
+
+            THREE.ShaderChunk[ 'color_pars_vertex' ],
+
+            'void main() {',
+
+            THREE.ShaderChunk[ 'color_vertex' ],
+
+            'vLineDistance = lineDistance;',
+
+            'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+
+            '}'
+        ].join('\n');
+
+        dashedLineShader.fragmentShader = [
+            'uniform vec3 diffuse;',
+            'uniform float opacity;',
+
+            'uniform float pattern[' + pattern.length + '];',
+            'uniform float patternLength;',
+
+            'varying float vLineDistance;',
+
+            THREE.ShaderChunk[ 'color_pars_fragment' ],
+            THREE.ShaderChunk[ 'fog_pars_fragment' ],
+
+            'void main() {',
+
+            'float pos = mod(vLineDistance, patternLength);',
+
+            'for ( int i = 0; i < ' + pattern.length + '; i++ ) {',
+            'pos = pos - abs(pattern[i]);',
+            'if( pos < 0.0 ) {',
+            'if( pattern[i] > 0.0 ) {',
+            'gl_FragColor = vec4(1.0, 0.0, 0.0, opacity );',
+            'break;',
+            '}',
+            'discard;',
+            '}',
+
+            '}',
+
+            THREE.ShaderChunk[ 'color_fragment' ],
+            THREE.ShaderChunk[ 'fog_fragment' ],
+
+            '}'
+        ].join('\n');
+
+        return dashedLineShader;
+    }
+
+    function findExtents(scene) { 
+        for(var child of scene.children) {
+            var minX, maxX, minY, maxY;
+            if(child.position) {
+                minX = Math.min(child.position.x, minX);
+                minY = Math.min(child.position.y, minY);
+                maxX = Math.max(child.position.x, maxX);
+                maxY = Math.max(child.position.y, maxY);
+            }
+        }
+
+        return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY }};
+    }
+
+ 
+
+ 
+ 
+
+// Three.js extension functions. Webpack doesn't seem to like it if we modify the THREE object directly.
+var THREEx = { Math: {} };
+/**
+ * Returns the angle in radians of the vector (p1,p2). In other words, imagine
+ * putting the base of the vector at coordinates (0,0) and finding the angle
+ * from vector (1,0) to (p1,p2).
+ * @param  {Object} p1 start point of the vector
+ * @param  {Object} p2 end point of the vector
+ * @return {Number} the angle
+ */
+THREEx.Math.angle2 = function(p1, p2) {
+	var v1 = new THREE.Vector2(p1.x, p1.y);
+	var v2 = new THREE.Vector2(p2.x, p2.y);
+	v2.sub(v1); // sets v2 to be our chord
+	v2.normalize();
+	if(v2.y < 0) return -Math.acos(v2.x);
+	return Math.acos(v2.x);
+};
+
+
+THREEx.Math.polar = function(point, distance, angle) {
+	var result = {};
+	result.x = point.x + distance * Math.cos(angle);
+	result.y = point.y + distance * Math.sin(angle);
+	return result;
+};
+
+/**
+ * Calculates points for a curve between two points
+ * @param startPoint - the starting point of the curve
+ * @param endPoint - the ending point of the curve
+ * @param bulge - a value indicating how much to curve
+ * @param segments - number of segments between the two given points
+ */
+THREEx.BulgeGeometry = function ( startPoint, endPoint, bulge, segments ) {
+
+	var vertex, i,
+		center, p0, p1, angle,
+		radius, startAngle,
+		thetaAngle;
+
+	THREE.Geometry.call( this );
+
+	this.startPoint = p0 = startPoint ? new THREE.Vector2(startPoint.x, startPoint.y) : new THREE.Vector2(0,0);
+	this.endPoint = p1 = endPoint ? new THREE.Vector2(endPoint.x, endPoint.y) : new THREE.Vector2(1,0);
+	this.bulge = bulge = bulge || 1;
+
+	angle = 4 * Math.atan(bulge);
+	radius = p0.distanceTo(p1) / 2 / Math.sin(angle/2);
+	center = THREEx.Math.polar(startPoint, radius, THREEx.Math.angle2(p0,p1) + (Math.PI / 2 - angle/2));
+
+	this.segments = segments = segments || Math.max( Math.abs(Math.ceil(angle/(Math.PI/18))), 6); // By default want a segment roughly every 10 degrees
+	startAngle = THREEx.Math.angle2(center, p0);
+	thetaAngle = angle / segments;
+
+
+	this.vertices.push(new THREE.Vector3(p0.x, p0.y, 0));
+
+	for(i = 1; i <= segments - 1; i++) {
+
+		vertex = THREEx.Math.polar(center, Math.abs(radius), startAngle + thetaAngle * i);
+
+		this.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0));
+
+	}
+
+};
+
+THREEx.BulgeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+    
+
+
+
+ 
+
+/* -----note--------------
+
+ 
+
+
+
+
+
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 20 - 3
src/materials/ExtendPointCloudMaterial.js

@@ -96,6 +96,23 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
 				type: "vec2fv",
 				value: null
 			},
+            
+            
+            baseHeightAreaMap:{
+                type:'t',
+                value:null
+            },
+            
+            baseHeightBoundZ:{
+                type:'vec2',
+                value:new THREE.Vector2
+            },
+            
+            baseHeightBoundXY:{
+                type:'vec4',
+                value:new THREE.Vector4
+            },
+            
         })
         
         delete this.clipBoxes;
@@ -308,7 +325,7 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
 
 	 
     
-	setClipBoxes (bigClipInBox, clipBoxes_in,clipBoxes_out, highlightBoxes, prismPolygons=[]) {
+	setClipBoxes (bigClipInBox, clipBoxes_in, clipBoxes_out, highlightBoxes, prismPolygons=[]) {
 		if (!clipBoxes_in || !clipBoxes_out) {
 			return;
 		}
@@ -340,7 +357,7 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
 				this.uniforms.clipBoxes.value[i] = Infinity;
 			}
 		} */
-        
+         
   
         if(prismPolygons.length){  
             let pointsCount = prismPolygons.pointsCount = prismPolygons.reduce((w,c)=>{return w+c.points.length},0) 
@@ -370,7 +387,7 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
             
             
             
-        } 
+        }  
          
         let doUpdate = (this.clipBoxes_in.length !== clipBoxes_in.length) || (this.clipBoxes_out.length != clipBoxes_out.length)
                     || this.highlightBoxes.length !== highlightBoxes.length 

+ 158 - 46
src/materials/shaders/pointcloud_new.vs

@@ -1,8 +1,7 @@
 
 precision highp float;
 precision highp int;
-
-#define max_clip_polygons 8
+ 
 #define PI 3.141592653589793
 
  
@@ -112,8 +111,11 @@ uniform float uOrthoHeight;
     uniform vec2 prismPoints[prismPointCountSum];  
 #endif        
 
-
-
+#if defined(showBaseHeight)  
+    uniform sampler2D baseHeightAreaMap ;
+    uniform vec2 baseHeightBoundZ;
+    uniform vec4 baseHeightBoundXY; 
+#endif    
 
 uniform float size;
 uniform float minSize;
@@ -717,7 +719,9 @@ vec3 getColor(vec4 world){
 		color = getCompositeColor(world);
 	#elif defined color_type_matcap
 		color = getMatcap();
-	#else 
+	#elif defined color_type_heightCpt      //add
+        color = vec3(0.0,0.0,0.0);
+    #else
 		color = getExtra();
 	#endif
 	
@@ -794,30 +798,36 @@ bool insideBox(mat4 clipBox, vec4 worldPos){//add
     return inside;
 }
 
-#if defined(num_prism) && num_prism > 0
-    #if defined(color_type_prismHeight)
-        vec3 percentToByte(float num){
-                                        //输出是0-1, shader精度不够,只够存3个数
-            float a = 1.0;
-            float result[4];
-            for(int i=0;i<3;i++){
-                a = i == 2 ? a/255.0 : a / 256.0 ; 
-                                           //分成256份,其中255份给当前位置,最后一份给后一个位置,而到了最后一个位置时没有下一个位置了所以只分成255份
-                if(num > a){
-                    float c = num / a;
-                    float r = floor(c);
-                    r = min(255.0, r);
-                    result[i] = r;
-                    num -= r * a;
-                }else{
-                    result[i] = 0.0;
-                }
-            } 
-            return vec3(result[0]/255.0, result[1]/255.0,result[2]/255.0);  
-            
-            //除以多少255还是256?  参考uPCIndex / 255.0 应该是255
+
+
+#if defined(color_type_heightCpt)
+    vec3 percentToByte(float num){
+                                    //输出是0-1, shader精度不够,只够存3个数
+        float a = 1.0;
+        float result[4];
+        for(int i=0;i<3;i++){
+            a = i == 2 ? a/255.0 : a / 256.0 ; 
+                                       //分成256份,其中255份给当前位置,最后一份给后一个位置,而到了最后一个位置时没有下一个位置了所以只分成255份
+            if(num > a){
+                float c = num / a;
+                float r = floor(c);
+                r = min(255.0, r);
+                result[i] = r;
+                num -= r * a;
+            }else{
+                result[i] = 0.0;
+            }
         } 
-    #endif
+        return vec3(result[0]/255.0, result[1]/255.0,result[2]/255.0);  
+        
+        //除以多少255还是256?  参考uPCIndex / 255.0 应该是255
+    } 
+#endif
+
+ 
+
+#if defined(num_prism) && num_prism > 0
+    
     
     int insidePrism(mat3 prismInfo, int pointIndexStart, vec4 worldPos){//是否在棱柱里 
      
@@ -831,8 +841,12 @@ bool insideBox(mat4 clipBox, vec4 worldPos){//add
 
         int pointCount = int(round(prismInfo[2][1]));
          
-        if(worldPos.z < zMin || worldPos.z > zMax || worldPos.x < xMin || worldPos.x > xMax || worldPos.y < yMin || worldPos.y > yMax)return 0;
-             
+        if( worldPos.x < xMin || worldPos.x > xMax || worldPos.y < yMin || worldPos.y > yMax)return 0;
+        #ifndef showBaseHeight
+            if( worldPos.z < zMin || worldPos.z > zMax) return 0;
+        #endif
+         
+         
         bool inside = false;
          
         int j=pointCount-1;
@@ -848,19 +862,37 @@ bool insideBox(mat4 clipBox, vec4 worldPos){//add
         }
             
         if(inside){
-            #if defined(color_type_prismHeight)
-                vColor = percentToByte((worldPos.z - zMin) / (zMax - zMin)); 
+            #ifdef showBaseHeight
+                return 1;
+            #else 
+                #ifdef color_type_heightCpt 
+                    vColor = percentToByte((worldPos.z - zMin) / (zMax - zMin)); 
+                #endif    
             #endif
             
             return worldPos.z < zMid ? 1 : 2;
         }else return 0;    
     } 
-    
-    
+     
+#endif
+
+#ifdef showBaseHeight
+    float byteToFloat(vec3 color){ 
+        float percent = 0.0;
+        float a = 1.0; 
+        for(int i=0;i<3;i++){
+            a = i == 2 ? a/255.0 : a/256.0; 
+            percent += a * color[i] ;
+        }
+        return  percent * 255.0 ; 
+    } 
     
 #endif
+
+
  
 
+
 void doClipping(vec4 world){
 
 	{
@@ -978,22 +1010,24 @@ void doClipping(vec4 world){
  
  
  
-   
+    int highlight_ = 0; 
  
     #if defined(num_prism) && num_prism > 0 
-        int highlight = 0; 
+    //基于prism棱柱的土方量显示或计算
+        
         int pointIndexStart = 0;
 		for(int i = 0; i < num_prism; i++){  
-            highlight = insidePrism(prismList[i], pointIndexStart, world);
+            highlight_ = insidePrism(prismList[i], pointIndexStart, world);
               
-            if(highlight>0){
-                #ifndef color_type_prismHeight 
-                    if(highlight == 1){
+            if(highlight_>0){ 
+                #if !defined(showBaseHeight) && !defined(color_type_heightCpt)
+                    if(highlight_ == 1){
                         vColor.r += 0.5; 
-                    }else if(highlight == 2){
+                    }else if(highlight_ == 2){
                         vColor.g += 0.5;
                     }
                 #endif
+                  
                 break;
             }
         
@@ -1001,14 +1035,92 @@ void doClipping(vec4 world){
             pointIndexStart += int(round(prismList[i][2][1])); 
 		} 
         
-        #ifdef color_type_prismHeight 
-            if(highlight==0){
-                gl_Position = vec4(100.0, 100.0, 100.0, 1.0);
+        #ifdef color_type_heightCpt  
+            if(highlight_==0){ 
+                gl_Position = vec4(100.0, 100.0, 100.0, 1.0);   //unvisible
+                return;
             }
         #endif 
         
 	#endif
- 
+    
+    
+    
+    
+    #ifdef showBaseHeight
+    //基于模型的土方量显示或计算
+        bool outsidePrism = false;
+        #if defined(num_prism) && num_prism > 0//高亮交集
+            if( highlight_ == 0 ){
+                outsidePrism = true;
+            }
+        #endif 
+        
+        if(!outsidePrism){
+        
+            float zMin = baseHeightBoundZ.x; 
+            float zMax = baseHeightBoundZ.y;  
+            float xMin = baseHeightBoundXY.x;
+            float xMax = baseHeightBoundXY.y;
+            float yMin = baseHeightBoundXY.z;
+            float yMax = baseHeightBoundXY.w; 
+     
+            bool inside = false;
+            if(world.z < zMin || world.z > zMax || world.x < xMin || world.x > xMax || world.y < yMin || world.y > yMax){
+                inside = false;
+            }else{
+                vec2 uv = vec2((world.x - xMin) / (xMax - xMin),  (world.y - yMin) / (yMax - yMin) );
+                
+                vec4 color = texture2D(baseHeightAreaMap,uv);
+                //抗锯齿待写
+                 
+                
+                if(color.a==1.0){ 
+                    
+                    float currentHeight = (world.z - zMin) / (zMax - zMin) ; 
+                    
+                    #ifdef color_type_heightCpt
+                        /*float diff = currentHeight - baseHeight;
+                        if(diff<0.0){
+                            diff = -diff;
+                            vOpacity = 0.5;//需要透明通道,容易出错
+                        }else{
+                            vOpacity = 1.0;
+                        }
+                        vColor = percentToByte(diff); 
+                        */ 
+                        vColor = percentToByte(currentHeight);
+                         
+                        vOpacity = 1.0;
+                        
+                    #else
+                        vColor = color.rgb;
+                        //float baseHeight = byteToFloat(color.rgb);
+                        //if(currentHeight > baseHeight){
+                        //    vColor.g += 0.5; 
+                        //}else{
+                        //    vColor.r += 0.5; 
+                        //}
+                    #endif
+                    
+                    inside = true;
+                }else{
+                    inside = false;
+                }
+                
+                
+                
+            } 
+            
+            if(inside == false){
+                #ifdef color_type_heightCpt
+                    gl_Position = vec4(100.0, 100.0, 100.0, 1.0);  //unvisible
+                    return;
+                #endif
+            }
+            
+        }
+	#endif
 }
 
 
@@ -1061,7 +1173,7 @@ void main() {
     
     
     vec4 worldPos = modelMatrix * vec4(position, 1.0); 
-    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
+    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );  //vec4 mvPosition = viewMatrix * worldPos;
     vViewPosition = mvPosition.xyz;
     gl_Position = projectionMatrix * mvPosition;
     vLogDepth = log2(-mvPosition.z);

+ 19 - 10
src/navigation/InputHandlerNew.js

@@ -667,7 +667,7 @@ export class InputHandler extends THREE.EventDispatcher {
                 let selectable
                 if(/* !consumed && */ !this.fixSelection){
                     if (e.button === THREE.MOUSE.LEFT) {
-                        if (noMovement) {
+                        //if (noMovement) {
                             selectable = this.hoveredElements.find(el => el.object._listeners && el.object._listeners['select']);
 
                             if (selectable) {
@@ -684,8 +684,8 @@ export class InputHandler extends THREE.EventDispatcher {
                                 if(this.selection.length>0)consumed = true //add 取消选择后,阻断后续 
                                 this.deselectAll();
                             }
-                        }
-                    } else if ((e.button === THREE.MOUSE.RIGHT) && noMovement) {
+                        //}
+                    } else if ((e.button === THREE.MOUSE.RIGHT) /* && noMovement */) {
                         this.deselectAll();
                     }
                 }
@@ -944,12 +944,18 @@ export class InputHandler extends THREE.EventDispatcher {
                 allElements = this.getHoveredElements(viewer.objs.children, true, raycaster)
                 
                 
+                
+                
                 if(allElements[0]){
+                    let quaternion = new THREE.Quaternion
+                    let nor = allElements[0].face && allElements[0].face.normal
+                    nor && allElements[0].oriObject.matrixWorld.decompose( new THREE.Vector3, quaternion, new THREE.Vector3 ) 
+            
                     intersectOnModel = {//模拟点云的intersectPoint的结构写法
                         hoveredElement : allElements[0] ,
                         location: allElements[0].point,
                         //point: {normal: allElements[0].face.normal },
-                        normal: allElements[0].face && allElements[0].face.normal,
+                        normal: nor && nor.applyQuaternion(quaternion),
                         distance: allElements[0].distance,
                         object: allElements[0].object
                     } 
@@ -1123,8 +1129,8 @@ export class InputHandler extends THREE.EventDispatcher {
                     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 cur = hoveredElements.find(a => a.object)
+                let curr = cur && cur.object//hoveredElements.map(a => a.object).find(a => true);//只取第一个
                 let prev = this.lastMouseoverElement //this.hoveredElements.map(a => a.object).find(a => true);
 
                 if(curr !== prev){
@@ -1133,13 +1139,14 @@ export class InputHandler extends THREE.EventDispatcher {
                         curr.dispatchEvent({
                             type: 'mouseover',
                             object: curr,
+                            hoveredElement:cur
                         });
                     }
                     if(prev){
                         if (this.logMessages) console.log(`${this.constructor.name}: mouseleave: ${prev.name}`);
                         prev.dispatchEvent({
                             type: 'mouseleave',
-                            object: prev,
+                            object: prev, 
                         });
                     }
                     
@@ -1432,18 +1439,20 @@ export class InputHandler extends THREE.EventDispatcher {
     
         let intersectionsCopy = intersections.slice()
         
-        
+         
         
         if(this.intersect && this.intersect.distance != void 0 && !dontCheckDis){//add
             intersections = intersections.filter(e=>{
+                if( this.intersect.hoveredElement && this.intersect.hoveredElement.oriObject == e.object)return true
+                
                 let material = e.object.material
                 
                 return e.object.pickDontCheckDis || ( material.depthTest == false || material.depthWrite == false) && !material.realUseDepth  //!material.depthTestWhenPick
-                 || ( material.useDepth ? e.distance < this.intersect.distance + material.uniforms.clipDistance.value : e.distance < this.intersect.distance )
+                 || ( material.useDepth ? e.distance <= this.intersect.distance + material.uniforms.clipDistance.value : e.distance <= this.intersect.distance )
                 //maxClipFactor是否需要考虑?
             }) 
         }
-     
+      
        
         intersections = intersections.map(e=>{//add 转化为interactables
             var object = e.object; 

+ 4 - 1
src/utils/TransformationToolNew.js

@@ -1074,7 +1074,10 @@ export class TransformationTool extends THREE.EventDispatcher{
 			this.scene.updateMatrix();
 			this.scene.updateMatrixWorld();
 
-			let selected = this.selection[0];
+                 
+			let selected = this.selection[0]; 
+            selected.updateMatrixWorld();//add 否则scale的sphere抖动
+            
 			let world = selected.matrixWorld;
 			let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
 			let domElement = this.viewer.renderer.domElement;

+ 1 - 1
src/viewer/EDLRendererNew.js

@@ -335,7 +335,7 @@ export class EDLRenderer{//Eye-Dome Lighting 眼罩照明
                   
                 Utils.screenPass.render(viewer.renderer, this.recoverToScreenMat, target/* , Potree.settings.useFxaa && viewer.composer2 */);
                 
-                params.drawedModelOnRT = true
+                params.drawedModelOnRT = Potree.settings.intersectOnObjs
             }else{
                 //渲染点云 (直接用rtEDL上的会失去抗锯齿, 导致频闪、密集时出现条纹,  自己写抗锯齿也要渲染好几次。另外透明度也要处理下) 
                    

+ 7 - 2
src/viewer/ExtendScene.js

@@ -153,14 +153,19 @@ class ExtendScene extends Scene{
          
         
         //add:------给空间模型的box 或其他obj------
-        let light2 = new THREE.AmbientLight( 16777215, 1 );
+        let light2 = new THREE.AmbientLight( 16777215, 0.5 );
         Potree.Utils.setObjectLayers(light2, 'light'/* 'bothMapAndScene' */)
         this.scene.add(light2)
-        let light3 = new THREE.DirectionalLight( 16777215, 1);  
+        let light3 = new THREE.DirectionalLight( 16777215, 0.7);  
         light3.position.set( 10, 10, 10 );
 		light3.lookAt( new THREE.Vector3(0, 0, 0));
         Potree.Utils.setObjectLayers(light3, 'light')
         this.scene.add(light3)  
+        let light4 = new THREE.DirectionalLight( 16777215, 0.3);      //补光
+        light4.position.set( -10, -5, -7 );
+		light4.lookAt( new THREE.Vector3(0, 0, 0));
+        Potree.Utils.setObjectLayers(light4, 'light')
+        this.scene.add(light4)  
         //--------------------------------------------
 
 		{ // background

+ 163 - 1
yarn.lock

@@ -17,6 +17,14 @@
   dependencies:
     "@babel/highlight" "^7.18.6"
 
+"@babel/code-frame@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
+  integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==
+  dependencies:
+    "@babel/highlight" "^7.24.7"
+    picocolors "^1.0.0"
+
 "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1":
   version "7.20.5"
   resolved "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.20.5.tgz"
@@ -52,6 +60,16 @@
     "@jridgewell/gen-mapping" "^0.3.2"
     jsesc "^2.5.1"
 
+"@babel/generator@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d"
+  integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==
+  dependencies:
+    "@babel/types" "^7.24.7"
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.25"
+    jsesc "^2.5.1"
+
 "@babel/helper-annotate-as-pure@^7.18.6":
   version "7.18.6"
   resolved "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
@@ -115,6 +133,13 @@
   resolved "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
   integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
 
+"@babel/helper-environment-visitor@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz#4b31ba9551d1f90781ba83491dd59cf9b269f7d9"
+  integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==
+  dependencies:
+    "@babel/types" "^7.24.7"
+
 "@babel/helper-explode-assignable-expression@^7.18.6":
   version "7.18.6"
   resolved "https://registry.npmmirror.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096"
@@ -130,6 +155,14 @@
     "@babel/template" "^7.18.10"
     "@babel/types" "^7.19.0"
 
+"@babel/helper-function-name@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2"
+  integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==
+  dependencies:
+    "@babel/template" "^7.24.7"
+    "@babel/types" "^7.24.7"
+
 "@babel/helper-hoist-variables@^7.18.6":
   version "7.18.6"
   resolved "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
@@ -137,6 +170,13 @@
   dependencies:
     "@babel/types" "^7.18.6"
 
+"@babel/helper-hoist-variables@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee"
+  integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==
+  dependencies:
+    "@babel/types" "^7.24.7"
+
 "@babel/helper-member-expression-to-functions@^7.18.9":
   version "7.18.9"
   resolved "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815"
@@ -212,6 +252,14 @@
   dependencies:
     "@babel/types" "^7.20.0"
 
+"@babel/helper-skip-transparent-expression-wrappers@^7.20.0":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9"
+  integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==
+  dependencies:
+    "@babel/traverse" "^7.24.7"
+    "@babel/types" "^7.24.7"
+
 "@babel/helper-split-export-declaration@^7.18.6":
   version "7.18.6"
   resolved "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
@@ -219,16 +267,33 @@
   dependencies:
     "@babel/types" "^7.18.6"
 
+"@babel/helper-split-export-declaration@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856"
+  integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==
+  dependencies:
+    "@babel/types" "^7.24.7"
+
 "@babel/helper-string-parser@^7.19.4":
   version "7.19.4"
   resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz"
   integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
 
+"@babel/helper-string-parser@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz#4d2d0f14820ede3b9807ea5fc36dfc8cd7da07f2"
+  integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==
+
 "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
   version "7.19.1"
   resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz"
   integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
 
+"@babel/helper-validator-identifier@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db"
+  integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
+
 "@babel/helper-validator-option@^7.18.6":
   version "7.18.6"
   resolved "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz"
@@ -262,11 +327,26 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
+"@babel/highlight@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d"
+  integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.24.7"
+    chalk "^2.4.2"
+    js-tokens "^4.0.0"
+    picocolors "^1.0.0"
+
 "@babel/parser@^7.18.10", "@babel/parser@^7.20.5":
   version "7.20.5"
   resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8"
   integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==
 
+"@babel/parser@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85"
+  integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==
+
 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
   version "7.18.6"
   resolved "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2"
@@ -386,6 +466,15 @@
     "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9"
     "@babel/plugin-syntax-optional-chaining" "^7.8.3"
 
+"@babel/plugin-proposal-optional-chaining@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.npmmirror.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea"
+  integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
 "@babel/plugin-proposal-private-methods@^7.18.6":
   version "7.18.6"
   resolved "https://registry.npmmirror.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea"
@@ -887,6 +976,15 @@
     "@babel/parser" "^7.18.10"
     "@babel/types" "^7.18.10"
 
+"@babel/template@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315"
+  integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==
+  dependencies:
+    "@babel/code-frame" "^7.24.7"
+    "@babel/parser" "^7.24.7"
+    "@babel/types" "^7.24.7"
+
 "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5":
   version "7.20.5"
   resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133"
@@ -903,6 +1001,22 @@
     debug "^4.1.0"
     globals "^11.1.0"
 
+"@babel/traverse@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.24.7.tgz#de2b900163fa741721ba382163fe46a936c40cf5"
+  integrity sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==
+  dependencies:
+    "@babel/code-frame" "^7.24.7"
+    "@babel/generator" "^7.24.7"
+    "@babel/helper-environment-visitor" "^7.24.7"
+    "@babel/helper-function-name" "^7.24.7"
+    "@babel/helper-hoist-variables" "^7.24.7"
+    "@babel/helper-split-export-declaration" "^7.24.7"
+    "@babel/parser" "^7.24.7"
+    "@babel/types" "^7.24.7"
+    debug "^4.3.1"
+    globals "^11.1.0"
+
 "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.4.4":
   version "7.20.5"
   resolved "https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz"
@@ -912,6 +1026,15 @@
     "@babel/helper-validator-identifier" "^7.19.1"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.24.7":
+  version "7.24.7"
+  resolved "https://registry.npmmirror.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2"
+  integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==
+  dependencies:
+    "@babel/helper-string-parser" "^7.24.7"
+    "@babel/helper-validator-identifier" "^7.24.7"
+    to-fast-properties "^2.0.0"
+
 "@jridgewell/gen-mapping@^0.1.0":
   version "0.1.1"
   resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
@@ -929,21 +1052,53 @@
     "@jridgewell/sourcemap-codec" "^1.4.10"
     "@jridgewell/trace-mapping" "^0.3.9"
 
+"@jridgewell/gen-mapping@^0.3.5":
+  version "0.3.5"
+  resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
+  integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
+  dependencies:
+    "@jridgewell/set-array" "^1.2.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
 "@jridgewell/resolve-uri@3.1.0":
   version "3.1.0"
   resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
   integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
 
+"@jridgewell/resolve-uri@^3.1.0":
+  version "3.1.2"
+  resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
+  integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+
 "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
   version "1.1.2"
   resolved "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
   integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
 
+"@jridgewell/set-array@^1.2.1":
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
+  integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
+
 "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
   version "1.4.14"
   resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
   integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
 
+"@jridgewell/sourcemap-codec@^1.4.14":
+  version "1.4.15"
+  resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
+  integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+  version "0.3.25"
+  resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
+  integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.1.0"
+    "@jridgewell/sourcemap-codec" "^1.4.14"
+
 "@jridgewell/trace-mapping@^0.3.9":
   version "0.3.17"
   resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
@@ -1289,7 +1444,7 @@ caniuse-lite@^1.0.30001400:
   resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz"
   integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==
 
-chalk@^2.0.0:
+chalk@^2.0.0, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -1507,6 +1662,13 @@ debug@^4.1.0, debug@^4.1.1:
   dependencies:
     ms "2.1.2"
 
+debug@^4.3.1:
+  version "4.3.5"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
+  integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
+  dependencies:
+    ms "2.1.2"
+
 decamelize@^1.1.1:
   version "1.2.0"
   resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"