Sfoglia il codice sorgente

正在写history的redo,还没写完

xzw 2 anni fa
parent
commit
4e687b02cf
54 ha cambiato i file con 14414 aggiunte e 2682 eliminazioni
  1. 1 1
      gulpfile.js
  2. 37 5
      libs/three.js/3dtiles/three-loader-3dtiles.esm.js
  3. 1 1
      libs/three.js/lines/LineGeometry.js
  4. 6 29
      libs/three.js/loaders/DRACOLoader.js
  5. 2411 1615
      libs/three.js/loaders/GLTFLoader.js
  6. 108 39
      libs/three.js/loaders/KTX2Loader.js
  7. 46 0
      libs/three.js/loaders/basis/README.md
  8. 0 0
      libs/three.js/loaders/basis/basis_transcoder.js
  9. 0 0
      libs/three.js/loaders/basis/basis_transcoder.wasm
  10. 1 0
      libs/three.js/loaders/basis/ver146.txt
  11. 33 0
      libs/three.js/loaders/draco/draco_encoder.js
  12. 1 1
      src/custom/utils/WorkerPool.js
  13. 9 3
      src/ExtendPointCloudOctree.js
  14. 22 10
      src/Potree.js
  15. 30 199
      src/PotreeRenderer.js
  16. 1674 0
      src/PotreeRendererNew.js
  17. 16 9
      src/custom/materials/BasicMaterial.js
  18. 8 0
      src/custom/materials/DepthBasicMaterial.js
  19. 4 2
      src/custom/materials/ModelTextureMaterial.js
  20. 10 8
      src/custom/mergeStartTest.js
  21. 12 9
      src/custom/modules/clipModel/Clip.js
  22. 42 42
      src/custom/modules/datasetAlignment/Alignment.js
  23. 45 0
      src/custom/modules/mergeModel/MergeEditor.js
  24. 13 11
      src/custom/modules/panoEdit/panoEditor.js
  25. 18 17
      src/custom/modules/panos/Images360.js
  26. 6 6
      src/custom/modules/panos/tile/PanoRenderer.js
  27. 34 4
      src/custom/objects/tool/Measure.js
  28. 6 4
      src/custom/objects/tool/MeasuringTool.js
  29. 14 6
      src/custom/objects/tool/TransformControls.js
  30. 8 6
      src/custom/objects/tool/ctrlPolygon.js
  31. 292 76
      src/custom/potree.shim.js
  32. 1 2
      src/custom/settings.js
  33. 37 18
      src/custom/start.js
  34. 8 5
      src/custom/utils/Common.js
  35. 18 23
      src/custom/utils/DrawUtil.js
  36. 81 17
      src/custom/utils/History.js
  37. 5 2
      src/custom/utils/SplitScreen.js
  38. 5074 0
      src/custom/viewer/ViewerNew.js
  39. 53 3
      src/materials/ExtendPointCloudMaterial.js
  40. 4 4
      src/materials/shaders/basicTextured.fs
  41. 96 0
      src/materials/shaders/edl_new.fs
  42. 19 0
      src/materials/shaders/edl_new.vs
  43. 185 0
      src/materials/shaders/pointcloud_new.fs
  44. 1119 0
      src/materials/shaders/pointcloud_new.vs
  45. 10 10
      src/navigation/FirstPersonControlsNew.js
  46. 42 34
      src/navigation/InputHandlerNew.js
  47. 1 1
      src/utils/ClippingTool.js
  48. 216 111
      src/utils/TransformationToolNew.js
  49. 365 0
      src/utils/VolumeNew.js
  50. 3 1
      src/viewer/ExtendScene.js
  51. 1519 336
      src/viewer/sidebar.html
  52. 406 0
      src/viewer/sidebar1.html
  53. 106 0
      src/viewer/sidebar2.html
  54. 138 12
      src/viewer/sidebarNew.js

+ 1 - 1
gulpfile.js

@@ -21,7 +21,7 @@ let paths = {
 	],
 	html: [
 		"src/viewer/potree.css",
-		"src/viewer/sidebar.html","src/viewer/sidebar2.html",
+		"src/viewer/sidebar1.html","src/viewer/sidebar2.html",
 		"src/viewer/profile.html"
 	],
 	resources: [

+ 37 - 5
libs/three.js/3dtiles/three-loader-3dtiles.esm.js

@@ -1,8 +1,11 @@
-import { EventDispatcher, CanvasTexture, LinearFilter, RepeatWrapping, Vector2 as Vector2$1, Frustum, Matrix4 as Matrix4$1, Group, PlaneGeometry, Vector3 as Vector3$1, MeshBasicMaterial, DoubleSide, Mesh, ArrowHelper, Color, BoxGeometry, EdgesGeometry, LineSegments, LineBasicMaterial, ShaderMaterial, Euler, BufferGeometry, Float32BufferAttribute, Uint8BufferAttribute, BufferAttribute, Points } from '../build/three.module.js';
+import { EventDispatcher, CanvasTexture, LinearFilter, RepeatWrapping, Vector2 as Vector2$1, Frustum, Matrix4 as Matrix4$1, Group, PlaneGeometry, Vector3 as Vector3$1/*,  MeshBasicMaterial */, DoubleSide, Mesh, ArrowHelper, Color, BoxGeometry, EdgesGeometry, LineSegments, LineBasicMaterial, ShaderMaterial, Euler, BufferGeometry, Float32BufferAttribute, Uint8BufferAttribute, BufferAttribute, Points } from '../build/three.module.js';
 import { GLTFLoader as GLTFLoader$1 } from '../loaders/GLTFLoader.js';
 import { DRACOLoader } from '../loaders/DRACOLoader.js';
 import { KTX2Loader } from '../loaders/KTX2Loader.js';
 
+
+import MeshBasicMaterial  from '../../../src/custom/materials/BasicMaterial.js'  // xzw改  原先MeshBasicMaterial贴图发黑,可能和贴图有关
+
 /*! *****************************************************************************
 Copyright (c) Microsoft Corporation.
 
@@ -10206,6 +10209,7 @@ class Tileset3D extends EventDispatcher{//xzw add EventDispatcher
               stack.push(childTile);
             }
         }
+        window.maxDepth = Math.max(window.maxDepth||0,tile.depth)
       }
     }
 
@@ -16564,6 +16568,7 @@ function getTileType(tile) {
 }
 
 function getRefine(refine) {
+     
   switch (refine) {
     case 'REPLACE':
     case 'replace':
@@ -16573,7 +16578,7 @@ function getRefine(refine) {
     case 'add':
       return TILE_REFINEMENT.ADD;
 
-    default:
+    default: 
       return refine;
   }
 }
@@ -17335,6 +17340,10 @@ class Loader3DTiles {
                             tileBoxes.add(box);
                             boxMap[tile.id] = box;
                         }
+                        //xzw :
+                        tileset.dispatchEvent({type:'tileLoaded',tileContent}) //每一个tile加载完要更改透明度等
+                        
+                        
                     }
                 }),
                 onTileLoad: (tile) => __awaiter(this, void 0, void 0, function* () {
@@ -17701,9 +17710,12 @@ function createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransform
                 
                  
                 // !zeg改
-                                                                                                                                                 
-                tileContent.position.set(tile.header.boundingVolume.box[0], tile.header.boundingVolume.box[1], tile.header.boundingVolume.box[2])//add
-                //这个链接需要加上这句https://testgis.4dage.com/LVBADUI_qp/tileset.json
+                //未知                                                                                                                                 
+                //tileContent.position.set(tile.header.boundingVolume.box[0], tile.header.boundingVolume.box[1], tile.header.boundingVolume.box[2])//add
+                //这个链接需要加上这句https://testgis.4dage.com/LVBADUI_qp/tileset.json //这和子集是否为json无关,为何在另一个工程正常
+                //console.log('位移tile', tileContent.position)  
+                
+                
                 
                 tileContent.traverse((object) => {
                     if (object.type == "Mesh") {
@@ -17823,3 +17835,23 @@ function disposeNode(node) {
 
 export { GeoTransform, Loader3DTiles, PointCloudColoring, Shading };
 //# sourceMappingURL=three-loader-3dtiles.esm.js.map
+
+
+
+/* 
+
+
+
+关键搜寻:
+
+Loader3DTiles   :  const tileset = new Tileset3D
+tileset.dispatchEvent({type:'tileLoaded',tileContent})
+ 
+createGLTFNodes : gltfLoader.parse
+
+
+
+
+
+
+ */

+ 1 - 1
libs/three.js/lines/LineGeometry.js

@@ -12,7 +12,7 @@ class LineGeometry extends LineSegmentsGeometry {
 
 	}
 
-	setPositions( array ) {
+	setPositions( array ) {//见potree.shim.js
 
 		// converts [ x1, y1, z1,  x2, y2, z2, ... ] to pairs format
 

+ 6 - 29
libs/three.js/loaders/DRACOLoader.js

@@ -1,3 +1,6 @@
+//2022.11.11 copyfrom : https://unpkg.com/three@0.146.0/examples/jsm/loaders/DRACOLoader.js
+
+
 import {
 	BufferAttribute,
 	BufferGeometry,
@@ -73,21 +76,12 @@ class DRACOLoader extends Loader {
 
 		loader.load( url, ( buffer ) => {
 
-			const taskConfig = {
-				attributeIDs: this.defaultAttributeIDs,
-				attributeTypes: this.defaultAttributeTypes,
-				useUniqueIDs: false
-			};
-
-			this.decodeGeometry( buffer, taskConfig )
-				.then( onLoad )
-				.catch( onError );
+			this.decodeDracoFile( buffer, onLoad ).catch( onError );
 
 		}, onProgress, onError );
 
 	}
 
-	/** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
 	decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) {
 
 		const taskConfig = {
@@ -96,29 +90,12 @@ class DRACOLoader extends Loader {
 			useUniqueIDs: !! attributeIDs
 		};
 
-		this.decodeGeometry( buffer, taskConfig ).then( callback );
+		return this.decodeGeometry( buffer, taskConfig ).then( callback );
 
 	}
 
 	decodeGeometry( buffer, taskConfig ) {
 
-		// TODO: For backward-compatibility, support 'attributeTypes' objects containing
-		// references (rather than names) to typed array constructors. These must be
-		// serialized before sending them to the worker.
-		for ( const attribute in taskConfig.attributeTypes ) {
-
-			const type = taskConfig.attributeTypes[ attribute ];
-
-			if ( type.BYTES_PER_ELEMENT !== undefined ) {
-
-				taskConfig.attributeTypes[ attribute ] = type.name;
-
-			}
-
-		}
-
-		//
-
 		const taskKey = JSON.stringify( taskConfig );
 
 		// Check for an existing task using this buffer. A transferred buffer cannot be transferred
@@ -584,4 +561,4 @@ function DRACOWorker() {
 
 }
 
-export { DRACOLoader };
+export { DRACOLoader };

File diff suppressed because it is too large
+ 2411 - 1615
libs/three.js/loaders/GLTFLoader.js


+ 108 - 39
libs/three.js/loaders/KTX2Loader.js

@@ -10,11 +10,14 @@
  * - KTX: http://github.khronos.org/KTX-Specification/
  * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
  */
+//2022.11.11 ver146
 
+ 
 import {
 	CompressedTexture,
+	//CompressedArrayTexture,//暂时注释
 	//Data3DTexture,//暂时注释
-	DataTexture, 
+	DataTexture,
 	FileLoader,
 	FloatType,
 	HalfFloatType,
@@ -35,17 +38,15 @@ import {
 	RGBAFormat,
 	RGFormat,
 	sRGBEncoding,
-	UnsignedByteType
+	UnsignedByteType,
 } from '../build/three.module.js';
-
-
-import { WorkerPool } from '../../../src/custom/utils/WorkerPool.js';
-import * as KTX from '../libs/ktx-parse.module.js';
-
-const {
+import { WorkerPool } from '../utils/WorkerPool.js';
+import {
 	read,
 	KHR_DF_FLAG_ALPHA_PREMULTIPLIED,
 	KHR_DF_TRANSFER_SRGB,
+	KHR_SUPERCOMPRESSION_NONE,
+	KHR_SUPERCOMPRESSION_ZSTD,
 	VK_FORMAT_UNDEFINED,
 	VK_FORMAT_R16_SFLOAT,
 	VK_FORMAT_R16G16_SFLOAT,
@@ -59,12 +60,15 @@ const {
 	VK_FORMAT_R8G8_UNORM,
 	VK_FORMAT_R8G8B8A8_SRGB,
 	VK_FORMAT_R8G8B8A8_UNORM,
-} = KTX; // eslint-disable-line no-undef
+} from '../libs/ktx-parse.module.js'; 
+import { ZSTDDecoder } from '../libs/zstddec.module.js';
 
 const _taskCache = new WeakMap();
 
 let _activeLoaders = 0;
 
+let _zstd;
+
 class KTX2Loader extends Loader {
 
 	constructor( manager ) {
@@ -235,16 +239,21 @@ class KTX2Loader extends Loader {
 
 	}
 
-	_createTextureFrom( transcodeResult ) {
+	_createTextureFrom( transcodeResult, container ) {
 
 		const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;
 
 		if ( type === 'error' ) return Promise.reject( error );
 
-		const texture = new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
+		const texture = container.layerCount > 1
+			? new CompressedArrayTexture( mipmaps, width, height, container.layerCount, format, UnsignedByteType )
+			: new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
+
+
 		texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
 		texture.magFilter = LinearFilter;
 		texture.generateMipmaps = false;
+
 		texture.needsUpdate = true;
 		texture.encoding = dfdTransferFn === KHR_DF_TRANSFER_SRGB ? sRGBEncoding : LinearEncoding;
 		texture.premultiplyAlpha = !! ( dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED );
@@ -256,9 +265,9 @@ class KTX2Loader extends Loader {
 	/**
 	 * @param {ArrayBuffer} buffer
 	 * @param {object?} config
-	 * @return {Promise<CompressedTexture|DataTexture|Data3DTexture>}
+	 * @return {Promise<CompressedTexture|CompressedArrayTexture|DataTexture|Data3DTexture>}
 	 */
-	_createTexture( buffer, config = {} ) {
+	async _createTexture( buffer, config = {} ) {
 
 		const container = read( new Uint8Array( buffer ) );
 
@@ -269,13 +278,12 @@ class KTX2Loader extends Loader {
 		}
 
 		//
-
 		const taskConfig = config;
 		const texturePending = this.init().then( () => {
 
 			return this.workerPool.postMessage( { type: 'transcode', buffer, taskConfig: taskConfig }, [ buffer ] );
 
-		} ).then( ( e ) => this._createTextureFrom( e.data ) );
+		} ).then( ( e ) => this._createTextureFrom( e.data, container ) );
 
 		// Cache the task result.
 		_taskCache.set( buffer, { promise: texturePending } );
@@ -436,6 +444,7 @@ KTX2Loader.BasisWorker = function () {
 		const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
 		const width = ktx2File.getWidth();
 		const height = ktx2File.getHeight();
+		const layers = ktx2File.getLayers() || 1;
 		const levels = ktx2File.getLevels();
 		const hasAlpha = ktx2File.getHasAlpha();
 		const dfdTransferFn = ktx2File.getDFDTransferFunc();
@@ -461,30 +470,39 @@ KTX2Loader.BasisWorker = function () {
 
 		for ( let mip = 0; mip < levels; mip ++ ) {
 
-			const levelInfo = ktx2File.getImageLevelInfo( mip, 0, 0 );
-			const mipWidth = levelInfo.origWidth;
-			const mipHeight = levelInfo.origHeight;
-			const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, 0, 0, transcoderFormat ) );
-
-			const status = ktx2File.transcodeImage(
-				dst,
-				mip,
-				0,
-				0,
-				transcoderFormat,
-				0,
-				- 1,
-				- 1,
-			);
+			const layerMips = [];
+
+			let mipWidth, mipHeight;
+
+			for ( let layer = 0; layer < layers; layer ++ ) {
+
+				const levelInfo = ktx2File.getImageLevelInfo( mip, layer, 0 );
+				mipWidth = levelInfo.origWidth;
+				mipHeight = levelInfo.origHeight;
+				const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, layer, 0, transcoderFormat ) );
+				const status = ktx2File.transcodeImage(
+					dst,
+					mip,
+					layer,
+					0,
+					transcoderFormat,
+					0,
+					- 1,
+					- 1,
+				);
+
+				if ( ! status ) {
 
-			if ( ! status ) {
+					cleanup();
+					throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
 
-				cleanup();
-				throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
+				}
+
+				layerMips.push( dst );
 
 			}
 
-			mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } );
+			mipmaps.push( { data: concat( layerMips ), width: mipWidth, height: mipHeight } );
 
 		}
 
@@ -611,6 +629,33 @@ KTX2Loader.BasisWorker = function () {
 
 	}
 
+	/** Concatenates N byte arrays. */
+	function concat( arrays ) {
+
+		let totalByteLength = 0;
+
+		for ( const array of arrays ) {
+
+			totalByteLength += array.byteLength;
+
+		}
+
+		const result = new Uint8Array( totalByteLength );
+
+		let byteOffset = 0;
+
+		for ( const array of arrays ) {
+
+			result.set( array, byteOffset );
+
+			byteOffset += array.byteLength;
+
+		}
+
+		return result;
+
+	}
+
 };
 
 //
@@ -662,7 +707,7 @@ const ENCODING_MAP = {
 
 };
 
-function createDataTexture( container ) {
+async function createDataTexture( container ) {
 
 	const { vkFormat, pixelWidth, pixelHeight, pixelDepth } = container;
 
@@ -672,11 +717,36 @@ function createDataTexture( container ) {
 
 	}
 
-	//
+	const level = container.levels[ 0 ];
 
+	let levelData;
 	let view;
 
-	const levelData = container.levels[ 0 ].levelData;
+	if ( container.supercompressionScheme === KHR_SUPERCOMPRESSION_NONE ) {
+
+		levelData = level.levelData;
+
+	} else if ( container.supercompressionScheme === KHR_SUPERCOMPRESSION_ZSTD ) {
+
+		if ( ! _zstd ) {
+
+			_zstd = new Promise( async ( resolve ) => {
+
+				const zstd = new ZSTDDecoder();
+				await zstd.init();
+				resolve( zstd );
+
+			} );
+
+		}
+
+		levelData = ( await _zstd ).decode( level.levelData, level.uncompressedByteLength );
+
+	} else {
+
+		throw new Error( 'THREE.KTX2Loader: Unsupported supercompressionScheme.' );
+
+	}
 
 	if ( TYPE_MAP[ vkFormat ] === FloatType ) {
 
@@ -703,7 +773,6 @@ function createDataTexture( container ) {
 		view = levelData;
 
 	}
-
 	//
 
 	const texture = pixelDepth === 0
@@ -722,4 +791,4 @@ function createDataTexture( container ) {
 
 }
 
-export { KTX2Loader };
+export { KTX2Loader };

+ 46 - 0
libs/three.js/loaders/basis/README.md

@@ -0,0 +1,46 @@
+# Basis Universal GPU Texture Compression
+
+Basis Universal is a "[supercompressed](http://gamma.cs.unc.edu/GST/gst.pdf)"
+GPU texture and texture video compression system that outputs a highly
+compressed intermediate file format (.basis) that can be quickly transcoded to
+a wide variety of GPU texture compression formats.
+
+[GitHub](https://github.com/BinomialLLC/basis_universal)
+
+## Transcoders
+
+Basis Universal texture data may be used in two different file formats:
+`.basis` and `.ktx2`, where `ktx2` is a standardized wrapper around basis texture data.
+
+For further documentation about the Basis compressor and transcoder, refer to
+the [Basis GitHub repository](https://github.com/BinomialLLC/basis_universal).
+
+The folder contains two files required for transcoding `.basis` or `.ktx2` textures:
+
+* `basis_transcoder.js` — JavaScript wrapper for the WebAssembly transcoder.
+* `basis_transcoder.wasm` — WebAssembly transcoder.
+
+Both are dependencies of `THREE.KTX2Loader` and `THREE.BasisTextureLoader`:
+
+```js
+var ktx2Loader = new THREE.KTX2Loader();
+ktx2Loader.setTranscoderPath( 'examples/js/libs/basis/' );
+ktx2Loader.detectSupport( renderer );
+ktx2Loader.load( 'diffuse.ktx2', function ( texture ) {
+
+	var material = new THREE.MeshStandardMaterial( { map: texture } );
+
+}, function () {
+
+	console.log( 'onProgress' );
+
+}, function ( e ) {
+
+	console.error( e );
+
+} );
+```
+
+## License
+
+[Apache License 2.0](https://github.com/BinomialLLC/basis_universal/blob/master/LICENSE)

libs/three.js/loaders/ktx/basis_transcoder.js → libs/three.js/loaders/basis/basis_transcoder.js


libs/three.js/loaders/ktx/basis_transcoder.wasm → libs/three.js/loaders/basis/basis_transcoder.wasm


+ 1 - 0
libs/three.js/loaders/basis/ver146.txt

@@ -0,0 +1 @@
+ktx2Loader 使用

File diff suppressed because it is too large
+ 33 - 0
libs/three.js/loaders/draco/draco_encoder.js


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

@@ -3,7 +3,7 @@
  */
 //用于KTX2Loader
 export class WorkerPool {
-
+ 
 	constructor( pool = 4 ) {
 
 		this.pool = pool;

+ 9 - 3
src/ExtendPointCloudOctree.js

@@ -272,7 +272,7 @@ export class ExtendPointCloudOctree extends PointCloudOctree{
 			pickMaterial.classification = this.material.classification;
 			pickMaterial.recomputeClassification();
 
-			if(params.pickClipped){
+			/* if(params.pickClipped){
 				pickMaterial.clipBoxes = this.material.clipBoxes;
 				pickMaterial.uniforms.clipBoxes = this.material.uniforms.clipBoxes;
 				if(this.material.clipTask === Potree.ClipTask.HIGHLIGHT){
@@ -283,8 +283,14 @@ export class ExtendPointCloudOctree extends PointCloudOctree{
 				pickMaterial.clipMethod = this.material.clipMethod;
 			}else{
 				pickMaterial.clipBoxes = [];
-			}
-
+			} */ 
+            //pickClipped判断转移到上一层函数
+            pickMaterial.clipBoxes_in = this.material.clipBoxes_in; 
+            pickMaterial.clipBoxes_out = this.material.clipBoxes_out; 
+            pickMaterial.bigClipInBox = this.material.bigClipInBox; 
+            pickMaterial.uniforms.clipBoxes_in = this.material.uniforms.clipBoxes_in;
+            pickMaterial.uniforms.clipBoxes_out = this.material.uniforms.clipBoxes_out;
+            pickMaterial.uniforms.clipBoxBig_in = this.material.uniforms.clipBoxBig_in;
 			this.updateMaterial(pickMaterial, nodes, camera, renderer, new THREE.Vector2(width, height));
 		}
 

+ 22 - 10
src/Potree.js

@@ -71,7 +71,7 @@ export * from "./utils/ProfileTool.js";
 export * from "./utils/ScreenBoxSelectTool.js";
 export * from "./utils/SpotLightHelper.js";
 export * from "./utils/TransformationToolNew.js";
-export * from "./utils/Volume.js";
+export * from "./utils/VolumeNew.js";
 export * from "./utils/VolumeTool.js"; 
 //export * from "./custom/viewer/ExtendViewer.js"; 
 export * from "./custom/viewer/ViewerNew.js"; 
@@ -147,16 +147,28 @@ export {scriptPath, resourcePath};
 
 
 //add: 
-export async function loadFile(path, callback, onError){
+export async function loadFile(path, params , callback, onError){
+    params = params || {}
     if(Potree.fileServer){
          
-        Potree.fileServer.get(path).then(data=>{ 
+        Potree.fileServer.get(path, { params }).then(data=>{ 
             if(data.data)data = data.data
             if(data.data)data = data.data //融合页面getdataset需要查找两次data
             callback && callback(data)
         }).catch(onError) 
     }else{
         try{
+            if(Object.keys(params).length > 0){
+                path+='?'
+                let index = 0
+                for(let i in params){ 
+                    if(index>0) path += '&'
+                    path+=i; path+='='; path+=params[i] 
+                    index ++ 
+                }
+            }
+             
+            
             let response = await fetch(path); 
             let text = await response.text();
             var data = JSON.parse(text)
@@ -185,7 +197,7 @@ export async function loadDatasets(callback,sceneCode,onError){//之后直接把
         //path = `${Potree.scriptPath}/data/${sceneCode}/getDataSet.json`
         
     }
-    return loadFile(path, callback,onError)
+    return loadFile(path, null, callback,onError)
     
 }
 
@@ -233,7 +245,7 @@ export async function loadMapEntity(datasetId, force){
         path = `${prefix}/laser/tiledMap/${Potree.settings.number}/tiledMap/${floorplanType}/${dataset_id}` 
         
         Potree.settings.floorplanRequests[dataset_id] = true //开始加载了
-        return loadFile(path, callback.bind(this,  dataset_id, floorplanType)  )
+        return loadFile(path, null, callback.bind(this,  dataset_id, floorplanType)  )
     })
     
      
@@ -242,17 +254,17 @@ export async function loadMapEntity(datasetId, force){
  
 export async function loadPanos(datasetId, callback){
     var path 
-    let query = `?datasetId=${datasetId}`                  //`?lat=${center.lat}&lon=${center.lon}&radius=200000`
+    //let query = `?datasetId=${datasetId}`                  //`?lat=${center.lat}&lon=${center.lon}&radius=200000`
     if(Potree.fileServer){
-        path = `/laser/filter/${Potree.settings.number}/query` + query
+        path = `/laser/filter/${Potree.settings.number}/query` 
     }else{
         //path = `${Potree.settings.urls.prefix2}/indoor/${Potree.settings.number}/api/images/filter` + query
         //path = `${Potree.scriptPath}/data/${Potree.settings.number}/panos-${datasetId}.json`
-        path = `${Potree.settings.urls.prefix}/laser/filter/${Potree.settings.number}/query` + query
+        path = `${Potree.settings.urls.prefix}/laser/filter/${Potree.settings.number}/query` 
        
          
     }
-    return loadFile(path, callback) 
+    return loadFile(path, {datasetId:datasetId}, callback) 
     
 }
 
@@ -265,7 +277,7 @@ export async function loadPanosInfo(callback){
         path = `${Potree.scriptPath}/data/panoEdit/vision_edit.txt`
           
     }
-    return loadFile(path, callback)
+    return loadFile(path, null, callback)
     
 }
 

+ 30 - 199
src/PotreeRenderer.js

@@ -1,6 +1,8 @@
 
 import * as THREE from "../libs/three.js/build/three.module.js";
 import {PointCloudTree} from "./PointCloudTree.js";
+import {PointCloudOctreeNode} from "./PointCloudOctree.js";
+import {PointCloudArena4DNode} from "./arena4d/PointCloudArena4D.js";
 import {PointSizeType, ClipTask, ElevationGradientRepeat} from "./defines.js";
 
 // Copied from three.js: WebGLRenderer.js
@@ -154,7 +156,7 @@ let attributeLocations = {
 class Shader {
 
 	constructor(gl, name, vsSource, fsSource) {
-		this.gl = gl; 
+		this.gl = gl;
 		this.name = name;
 		this.vsSource = vsSource;
 		this.fsSource = fsSource;
@@ -223,20 +225,7 @@ class Shader {
 
 			this.vs = gl.createShader(gl.VERTEX_SHADER);
 			this.fs = gl.createShader(gl.FRAGMENT_SHADER);
-            
-            
-            
-            
 			this.program = gl.createProgram();
-            
-            if(  !gl.isProgram(this.program  )){//创建失败  开启多个页面可能会,原因是webglcontextlost
-                //console.error('创建program失败');
-                viewer.dispatchEvent('webglError', {msg: 'potreeRenderer创建program失败'})
-                console.log(this.vs)
-                console.log(this.fs)
-                
-                return;
-            }
 
 			for(let name of Object.keys(attributeLocations)){
 				let location = attributeLocations[name].location;
@@ -256,9 +245,7 @@ class Shader {
 
 			gl.detachShader(program, this.vs);
 			gl.detachShader(program, this.fs);
-            
 
-            // 检测当前程序链接状态
 			let success = gl.getProgramParameter(program, gl.LINK_STATUS);
 			if (!success) {
 				let info = gl.getProgramInfoLog(program);
@@ -294,7 +281,7 @@ class Shader {
 			}
 
 			// uniform blocks
-			if( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext){ //WebGL2RenderingContext在mac的safari14以下是没有定义的
+			if(gl instanceof WebGL2RenderingContext){ 
 				let numBlocks = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS);
 
 				for (let i = 0; i < numBlocks; i++) {
@@ -341,7 +328,7 @@ class Shader {
 		const tEnd = performance.now();
 		const duration = tEnd - tStart;
 
-		//console.log(`shader compile duration: ${duration.toFixed(3)}`);
+		console.log(`shader compile duration: ${duration.toFixed(3)}`);
 
 
 	}
@@ -469,7 +456,8 @@ class WebGLTexture {
 		this.gl = gl;
 
 		this.texture = texture;
-		this.id = gl.createTexture(); 
+		this.id = gl.createTexture();
+
 		this.target = gl.TEXTURE_2D;
 		this.version = -1;
 
@@ -557,7 +545,7 @@ export class Renderer {
 	constructor(threeRenderer) {
 		this.threeRenderer = threeRenderer;
 		this.gl = this.threeRenderer.getContext();
-  
+
 		this.buffers = new Map();
 		this.shaders = new Map();
 		this.textures = new Map();
@@ -688,7 +676,7 @@ export class Renderer {
 
 			let node = stack.pop();
 
-			if (node instanceof PointCloudTree) { 
+			if (node instanceof PointCloudTree) {
 				octrees.push(node);
 				continue;
 			}
@@ -868,10 +856,7 @@ export class Renderer {
 			}
 
 			const geometry = node.geometryNode.geometry;
-/* if(!geometry){
-    console.error('no geometry', node)
-    continue
-} */
+
 			if(geometry.attributes["gps-time"]){
 				const bufferAttribute = geometry.attributes["gps-time"];
 				const attGPS = octree.getAttribute("gps-time");
@@ -1042,11 +1027,8 @@ export class Renderer {
 			}
 
 			let numPoints = webglBuffer.numElements;
-			 
-            gl.drawArrays(gl.POINTS, 0, numPoints); 
-            //gl.drawArrays(gl.TRIANGLES, 0, numPoints);
-            
-            
+			gl.drawArrays(gl.POINTS, 0, numPoints);
+
 			i++;
 		}
 
@@ -1057,10 +1039,6 @@ export class Renderer {
 			performance.measure("render.renderNodes", "renderNodes-start", "renderNodes-end");
 		}
 	}
-    
-    
-    
-    
 
 	renderOctree(octree, nodes, camera, target, params = {}){
 
@@ -1107,7 +1085,7 @@ export class Renderer {
 
 				this.shaders.set(material, shader);
 			}
-            
+
 			shader = this.shaders.get(material);
 
 			//if(material.needsUpdate){
@@ -1126,23 +1104,8 @@ export class Renderer {
 					`#define num_clipspheres ${numClipSpheres}`,
 					`#define num_clippolygons ${numClipPolygons}`,
 				];
-                
-                //add:-----------
-                if(material.usePanoMap){
-                    defines.push("#define usePanoMap");
-                }
-                
-                if(material.useFilterByNormal){
-                    defines.push("#define use_filter_by_normal");
-                    //Potree.settings.editType == 'pano' ? defines.push("#define attenuated_opacity2") : defines.push("#define attenuated_opacity");
-                    
-                }
-
-                
-                //---------------
-                
-                
-                
+
+
 				if(octree.pcoGeometry.root.isLoaded()){
 					let attributes = octree.pcoGeometry.root.geometry.attributes;
 
@@ -1186,7 +1149,7 @@ export class Renderer {
 
 				material.needsUpdate = false;
 			}
-             
+
 			for (let uniformName of Object.keys(material.uniforms)) {
 				let uniform = material.uniforms[uniformName];
 
@@ -1197,11 +1160,7 @@ export class Renderer {
 					if (!texture) {
 						continue;
 					}
-                    //add 
-                    if(uniformName == 'pano0Map' || uniformName == 'pano1Map' ){ //属于cubeTex,另外设置 
-                        continue
-                    } 
-                    
+
 					if (!this.textures.has(texture)) {
 						let webglTexture = new WebGLTexture(gl, texture);
 
@@ -1222,23 +1181,14 @@ export class Renderer {
 		if(params.transparent !== undefined){
 			transparent = params.transparent && material.opacity < 1;
 		}else{
-			transparent = material.usePanoMap ? false : (material.useFilterByNormal || material.opacity < 1); //add useFilterByNormal
+			transparent = material.opacity < 1;
 		}
 
 		if (transparent){
 			gl.enable(gl.BLEND);
-            
-            if(params.notAdditiveBlending){
-                gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); //NormalBlending 
-                gl.enable(gl.DEPTH_TEST);
-                gl.depthMask(true); //如果不开启depthWrite,深度会错乱。 
-            }else{
-                gl.blendFunc(gl.SRC_ALPHA, gl.ONE); //AdditiveBlending   原本
-                gl.disable(gl.DEPTH_TEST);
-                gl.depthMask(false);
-            } 
-			
-			
+			gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
+			gl.depthMask(false);
+			gl.disable(gl.DEPTH_TEST);
 		} else {
 			gl.disable(gl.BLEND);
 			gl.depthMask(true);
@@ -1274,15 +1224,11 @@ export class Renderer {
 			shader.setUniformMatrix4("uViewInv", viewInv);
 			shader.setUniformMatrix4("uProjInv", projInv);
 
-			/* let screenWidth = target ? target.width : material.screenWidth;
+			let screenWidth = target ? target.width : material.screenWidth;
 			let screenHeight = target ? target.height : material.screenHeight;
 
 			shader.setUniform1f("uScreenWidth", screenWidth);
-			shader.setUniform1f("uScreenHeight", screenHeight); */
-            
-            shader.setUniform2f('resolution', material.resolution.toArray())
-            
-            
+			shader.setUniform1f("uScreenHeight", screenHeight);
 			shader.setUniform1f("fov", Math.PI * camera.fov / 180);
 			shader.setUniform1f("near", camera.near);
 			shader.setUniform1f("far", camera.far);
@@ -1343,7 +1289,7 @@ export class Renderer {
 			}
 
 
-			shader.setUniform1f("size", material.usePanoMap ? Potree.config.material.absolutePanoramaSize * Math.min(window.devicePixelRatio,2) : material.size);//usePanoMap时控制在不大不小的范围内感觉较好,考虑到有的点云稀疏,用大一点的点
+			shader.setUniform1f("size", material.size);
 			shader.setUniform1f("maxSize", material.uniforms.maxSize.value);
 			shader.setUniform1f("minSize", material.uniforms.minSize.value);
 
@@ -1356,7 +1302,7 @@ export class Renderer {
 			//uniform vec3 uColor;
 			shader.setUniform3f("uColor", material.color.toArray());
 			//uniform float opacity;
-			shader.setUniform1f("uOpacity", material.usePanoMap ? 1: material.opacity);
+			shader.setUniform1f("uOpacity", material.opacity);
 
 			shader.setUniform2f("elevationRange", material.elevationRange);
 			shader.setUniform2f("intensityRange", material.intensityRange);
@@ -1384,14 +1330,6 @@ export class Renderer {
 
 			shader.setUniform("backfaceCulling", material.uniforms.backfaceCulling.value);
 
-
-
-                
-          
-            //==========================
-            //gl.TEXTURE_CUBE_MAP: 34067
-            //gl.TEXTURE0=33984 , vnWebGLTexture.target=gl.TEXTURE_2D = 3353
-            
 			let vnWebGLTexture = this.textures.get(material.visibleNodesTexture);
 			if(vnWebGLTexture){
 				shader.setUniform1i("visibleNodesTexture", currentTextureBindingPoint);
@@ -1491,47 +1429,6 @@ export class Renderer {
 				}
 
 			}
-            
-            
-            
-
-
-
-            //=============add===========
-            
-            
-            
-            if(material.usePanoMap){//为什么pointsize失效 
-                shader.setUniform1f("progress", material.uniforms.progress.value);
-                shader.setUniform1f("easeInOutRatio", material.uniforms.easeInOutRatio.value);
-                shader.setUniform3f("pano0Position", material.uniforms.pano0Position.value.toArray());
-                shader.setUniform3f("pano1Position", material.uniforms.pano1Position.value.toArray()); 
-                shader.setUniform('pano0Matrix',  material.uniforms.pano0Matrix.value);
-                shader.setUniform('pano1Matrix',  material.uniforms.pano1Matrix.value);
-                
-                let pano0Map = material.uniforms.pano0Map.value
-                if(pano0Map){
-                    this.threeRenderer._textures.safeSetTextureCube( pano0Map, ++currentTextureBindingPoint );
-                          
-                    shader.setUniform1i('pano0Map', currentTextureBindingPoint);
-                }
-                let pano1Map = material.uniforms.pano1Map.value
-                if(pano1Map){
-                    this.threeRenderer._textures.safeSetTextureCube( pano1Map, ++currentTextureBindingPoint );
-                         
-                    shader.setUniform1i('pano1Map', currentTextureBindingPoint);
-                } 
-                
-                //注: three.js我添加了个 _textures,   safeSetTextureCube里主要就是activeTexture和bindTexture
-             
-            }   
-
-
-            
-            
-            
-            
-            
 		}
 
 		this.renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params);
@@ -1539,37 +1436,10 @@ export class Renderer {
 		gl.activeTexture(gl.TEXTURE2);
 		gl.bindTexture(gl.TEXTURE_2D, null);
 		gl.activeTexture(gl.TEXTURE0);
-        
-        
-        
-        //gl.bindTexture(gl.TEXTURE_2D, null); //add
-        
-        
-        
-        
-        //add  恢复为不透明(否则renderToCubeMap时的贴图会被渲染成高亮的颜色)
-        gl.disable(gl.BLEND);
-        gl.depthMask(true);
-        gl.enable(gl.DEPTH_TEST);
-            //DEPTH_TEST等需要恢复吗
-         
-        
 	}
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-
 
 	render(scene, camera, target = null, params = {}) {
- 
+
 		const gl = this.gl;
 
 		// PREPARE 
@@ -1581,42 +1451,11 @@ export class Renderer {
 		// camera.matrixWorldInverse.invert(camera.matrixWorld);
 
 		const traversalResult = this.traverse(scene);
-        
-        //排序
-        if(Potree.settings.notAdditiveBlending){//add 
-            
-            traversalResult.octrees.forEach(tree=>{
-                if(tree.material.opacity==1){
-                    tree._z = Infinity //不透明的先渲染
-                }else{
-                    let center = tree.boundCenter ? tree.boundCenter.clone() : tree.boundingBox.getCenter(tree.boundCenter).applyMatrix4(tree.matrixWorld) 
-                    center.project(camera) 
-                    tree._z = center.z 
-                }
-            })   
-                 
-            traversalResult.octrees.sort((tree1,tree2)=>{ 
-                return tree2._z - tree1._z //降序  (-1 朝外)。 离屏幕近的后渲染
-            })
-        }
-        
-        
-        
-        
+
+
 		// RENDER
 		for (const octree of traversalResult.octrees) {
 			let nodes = octree.visibleNodes;
-            
-            
-            
-            /* nodes.sort((node1,node2)=>{//姑且
-                
-                let center = node.getBoundingSphere().center.clone().applyMatrix4(octree.matrixWorld)
-                return  
-                
-                
-            }) */
-            
 			this.renderOctree(octree, nodes, camera, target, params);
 		}
 
@@ -1634,18 +1473,10 @@ export class Renderer {
 
 };
 
-/* 
-中东的链接http://indoor.popsmart.cn:8094/zdoblh-yz/?vlon=5.14&vlat=-0.13&fov=100.0&pc=true&lon=121.61136592&lat=29.87855579&z=16.577
-geometry: 有的attributes: 属性是:
 
-classification:  
-color: 
-indices:  
-normal: 
-position:  
 
 
-最好有个spacing
 
 
- */
+
+

File diff suppressed because it is too large
+ 1674 - 0
src/PotreeRendererNew.js


+ 16 - 9
src/custom/materials/BasicMaterial.js

@@ -8,30 +8,37 @@ class BasicMaterial  extends THREE.ShaderMaterial{
         
        super( Object.assign({},{ 
             uniforms:{
-                tDiffuse:    { type: 't',  value: o.map },
-                alpha : {type:'f', value : 1 }
+                map:    { type: 't',  value: o.map },
+                opacity : {type:'f', value : o.opacity == void 0 ? 1 : o.opacity }
             },
             vertexShader: Shaders['basicTextured.vs'],   
             fragmentShader: Shaders['basicTextured.fs']  
         },o))
-        
-        
-         
+            
+    } 
+     
+    clone(){
+        let prop = {opacity: this.opacity}
+        if(this.map) prop.map = this.map
+        return new BasicMaterial(prop)
     }
+    
+    
     set opacity(o){
-        this.uniforms && (this.uniforms.alpha.value = o)
+        this.uniforms && (this.uniforms.opacity.value = o)
          
     }
     get opacity(){
-        return this.uniforms.alpha.value  
+        return this.uniforms.opacity.value  
     }
     
     set map(o){
-        this.uniforms.tDiffuse.value = o
+        this.uniforms.map.value = o
          
+          
     }
     get map(){
-        return this.uniforms.tDiffuse.value  
+        return this.uniforms.map.value  
     }
     
   

+ 8 - 0
src/custom/materials/DepthBasicMaterial.js

@@ -131,6 +131,14 @@ export default class DepthBasicMaterial extends THREE.ShaderMaterial{
         this.uniforms && (this.uniforms.opacity.value = o)
     }
     
+    get color(){
+        return this.uniforms.baseColor.value
+    }
+    set color(c){
+        this.uniforms && (this.uniforms.baseColor.value.set(c))
+    }
+    
+    
     /* dispose(){ 
         super.dispose()
         viewer.depthBasic

+ 4 - 2
src/custom/materials/ModelTextureMaterial.js

@@ -300,9 +300,11 @@ export default class ModelTextureMaterial extends THREE.RawShaderMaterial {
             
         let setSize = (e)=>{ 
             let viewport = e.viewport
-            let viewportOffset = viewport.offset || new Vector2()  
+            //let viewportOffset = viewport.offset || new Vector2()  
             let resolution = viewport.resolution2 
-            this.uniforms.viewport.value.set(viewportOffset.x, viewportOffset.y, resolution.x, resolution.y) 
+            //this.uniforms.viewport.value.set(viewportOffset.x, viewportOffset.y, resolution.x, resolution.y) 
+            this.uniforms.viewport.value.set(0,0, resolution.x, resolution.y);// xy是在viewport中的left和bottom,和整个窗口没有关系,所以不是viewportOffset。几乎都是0,0
+        
         }
         let viewport = viewer.mainViewport;
          

+ 10 - 8
src/custom/mergeStartTest.js

@@ -167,8 +167,8 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
             
             viewer.mapViewer && viewer.mapViewer.mapLayer.maps[0].updateProjection()
             
-        }
-         
+        }  
+          
         
         data.forEach((dataset,index)=>{  
             if(!ifReload){
@@ -519,22 +519,24 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                     } 
                 },callback,onprogress)
                   
-                
+                 
             }else if(name == '3dTiles'){
                  
                 viewer.loadModel({ 
                     name, 
-                    /* tilesUrl: 'https://4dkk.4dage.com/scene_view_data/SS-Ds19qsmuFA/images/3dtiles/tileset.json',
+                    /*  tilesUrl: 'https://4dkk.4dage.com/scene_view_data/SS-Ds19qsmuFA/images/3dtiles/tileset.json',
                     transform : { 
                         rotation : [Math.PI/2,  0,   0],
                         position : [0,0,0]  
-                    }  */
+                    }  */   
   
-                      tilesUrl: 'https://testgis.4dage.com/LVBADUI_qp/tileset.json',
+                    tilesUrl: 'https://testgis.4dage.com/LVBADUI_qp/tileset.json',
                     transform : { 
                         rotation : [0,  0,   0],
-                        position : [0,0,0]  
-                    }  
+                        position : [0,0,0]   
+                    }   
+                    
+                    
                 },callback,onprogress)
             }
              

+ 12 - 9
src/custom/modules/clipModel/Clip.js

@@ -1,5 +1,5 @@
 import * as THREE from "../../../../libs/three.js/build/three.module.js";
-import {BoxVolume} from '../../../utils/Volume.js'
+import {BoxVolume} from '../../../utils/VolumeNew.js'
 import {  ClipTask, ClipMethod} from "../../../defines.js"
 import {mapClipBox} from '../../objects/tool/mapClipBox.js'
 import Common from '../../utils/Common.js'
@@ -57,15 +57,16 @@ var Clip = {
             }
         })
         viewer.setControls(viewer.orbitControls);
-        viewer.setLimitFar(false)
-         
+        viewer.setLimitFar(false) 
+        //viewer.setClipState(false) //暂时关闭旧的clipping
         
         
         {
             this.box = new BoxVolume({
-                clip:true
-                
-            })  
+                clip:true 
+            }) 
+            this.box.clipTask = ClipTask['SHOW_INSIDE_Big'  /* "SHOW_INSIDE" */]
+            this.box.showBox = false
             this.box.name = "ClipBox"; 
             this.box.position.copy(target)
             this.box.scale.copy(scale)
@@ -125,8 +126,8 @@ var Clip = {
         
         
         {
-            viewer.setClipTask(ClipTask["SHOW_INSIDE"])
-            //viewer.setClipMethod(ClipMethod["INSIDE_ANY"])  
+            //viewer.setClipTask(ClipTask["SHOW_INSIDE"])
+             
         }
         
         Potree.settings.unableNavigate = true
@@ -181,7 +182,7 @@ var Clip = {
         viewer.setView(this.previousView)
         viewer.setLimitFar(true)
         viewer.setPointStandardMat(false) 
-        
+        //viewer.setClipState(true)
         
         {
             this.bus.removeEventListener('flyToPos',this.events.flyToPos) 
@@ -239,6 +240,8 @@ var Clip = {
                 let data = {
                     id: cloud.dataset_id, 
                     matrix : this.getTransformationMatrix(cloud).elements, 
+                    //VisiMatrixes:[],
+                    //UnVisiMatrixes:[],
                     modelMatrix:(new THREE.Matrix4).copy(cloud.transformMatrix).transpose().elements
                 }  
                 return data

+ 42 - 42
src/custom/modules/datasetAlignment/Alignment.js

@@ -10,16 +10,19 @@ var Alignment = {
     bus: new THREE.EventDispatcher(), 
     
     history : new History({ 
-        callback: (data)=>{ 
+        applyData: (data)=>{    
             data.forEach(item=>{
                 Alignment.applyTemp(item)  
             }) 
+        },
+        getData:(pointclouds)=>{ 
+            return Alignment.getTemp(pointclouds)
         } 
     }),
 
-    prepareRecord : true, 
+    /* prepareRecord : true, 
     
-    writeToHistory(pointclouds){ 
+      writeToHistory(pointclouds){ 
         if(!this.prepareRecord)return;
         this.prepareRecord = false
         
@@ -28,7 +31,7 @@ var Alignment = {
     },
     
     
-    /* undo(){//撤销一步 
+    undo(){//撤销一步 
         let last = this.history.pop();
         last && last.forEach(item=>{
             this.applyTemp(item)  
@@ -56,20 +59,22 @@ var Alignment = {
     
     
     init:function(){ 
-        let rotateInfo   
+        let transfromInfo   
         
         viewer.fpControls.addEventListener("transformPointcloud",(e)=>{ 
             if(e.pointclouds[0].dataset_id == Potree.settings.originDatasetId){//禁止手动移动初始数据集
                 return this.bus.dispatchEvent('forbitMoveOriginDataset') 
             }
             
+                             
+            this.history.beforeChange(e.pointclouds, e.pointclouds, [Potree.ExtendPointCloudOctree])    
+            //this.writeToHistory( e.pointclouds ) 
+              
+            if(!transfromInfo){  
+                transfromInfo = {pointclouds:e.pointclouds}
+            }
             
-                                                              
-             
             
-            this.writeToHistory( e.pointclouds ) 
-              
-        
             if(this.handleState == 'translate'){
                 e.pointclouds.forEach(cloud=>Alignment.translate(cloud, e.moveVec))
                 
@@ -79,37 +84,35 @@ var Alignment = {
                     //旋转中心是intersectStart的版本
                     /* let center = e.intersectStart //旋转中心是mousedown的位置
                     if(e.intersect.equals(center))return
-                    if(!rotateInfo){  
-                        rotateInfo = {
+                    if(!transfromInfo){  
+                        transfromInfo = {
                             orientationUser : e.pointclouds[0].orientationUser,
                             //vecStart : e.moveVec, // 首次移动向量 只有八个方向,精度太小,所以延迟 
                             pointclouds: e.pointclouds
                         } 
                         this.bus.dispatchEvent({type:'rotateStart', startPoint:center})
                         return
-                    }else if(!rotateInfo.vecStart){  
+                    }else if(!transfromInfo.vecStart){  
                         let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0)
                         if(vec.length() * e.camera.zoom >  30){  //在屏幕上距离初始点有一定距离后开始算
                             //console.log('moveVec',vec)
-                            rotateInfo.vecStart = vec
+                            transfromInfo.vecStart = vec
                         } */ 
                    
                      
                     let center = e.pointclouds[0].translateUser //旋转中心是第一个点云的位置  
                     if(e.intersect.equals(center))return
-                    if(!rotateInfo){  
-                        rotateInfo = {
-                            orientationUser : e.pointclouds[0].orientationUser,
-                            vecStart : new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0), 
-                            pointclouds: e.pointclouds
-                        }  
+                    if(!transfromInfo.vecStart){  
+                        transfromInfo.orientationUser = e.pointclouds[0].orientationUser  
+                        transfromInfo.vecStart = new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0)  
+                           
                     }else{ 
                     
                         let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0)
-                        let angle = math.getAngle(rotateInfo.vecStart,vec,'z')   
-                        let diffAngle = rotateInfo.orientationUser + angle - rotateInfo.pointclouds[0].orientationUser
+                        let angle = math.getAngle(transfromInfo.vecStart,vec,'z')   
+                        let diffAngle = transfromInfo.orientationUser + angle - transfromInfo.pointclouds[0].orientationUser
                         
-                        rotateInfo.pointclouds.forEach(cloud=>{ 
+                        transfromInfo.pointclouds.forEach(cloud=>{ 
                             
                            /*  let centerNoTranfrom = Potree.Utils.datasetPosTransform({ toDataset: true, pointcloud:cloud, position: center }) //中心点在数据集中的位置
                             Alignment.rotate(cloud, null, diffAngle)
@@ -117,7 +120,7 @@ var Alignment = {
                             let centerNow = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:cloud, position: centerNoTranfrom }) //中心点的现在位置
                             let shift = new THREE.Vector3().subVectors( center, centerNow); //偏移量
                             Alignment.translate(cloud,shift)   //使center还保留在原位
-                            //let centerNow1 = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:rotateInfo.pointcloud, position: centerNoTranfrom }) //中心点的现在位置
+                            //let centerNow1 = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:transfromInfo.pointcloud, position: centerNoTranfrom }) //中心点的现在位置
                                  */ 
                                  
                             Alignment.rotateAround(center, cloud, null, diffAngle)    
@@ -132,33 +135,32 @@ var Alignment = {
                 }else{ 
                     let center = e.pointclouds[0].translateUser //移动到的位置就是中心
                     if(e.intersect.equals(center))return
-                    if(!rotateInfo){  
-                        rotateInfo = {
-                            orientationUser : e.pointclouds[0].orientationUser,
-                            vecStart : new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0),
-                            //pointclouds: e.pointclouds
-                            pointcloud: e.pointclouds[0]
-                        }  
+                    if(!transfromInfo.vecStart){  
+                        transfromInfo.orientationUser = e.pointclouds[0].orientationUser
+                        transfromInfo.vecStart = new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0)  
                     }else{ 
                         let vec = new THREE.Vector3().subVectors(e.intersect, center).setZ(0)
-                        let angle = math.getAngle(rotateInfo.vecStart,vec,'z')   
-                        let diffAngle = rotateInfo.orientationUser + angle - rotateInfo.pointcloud.orientationUser
-                        Alignment.rotate(rotateInfo.pointcloud, null, diffAngle)
+                        let angle = math.getAngle(transfromInfo.vecStart,vec,'z')   
+                        let diffAngle = transfromInfo.orientationUser + angle - transfromInfo.pointcloud[0].orientationUser
+                        Alignment.rotate(transfromInfo.pointcloud[0], null, diffAngle)
                     }
                 }    
             } 
         })
         
         
-        viewer.fpControls.addEventListener("end",(e)=>{ 
-            rotateInfo = null 
-            this.prepareRecord = true
+        viewer.fpControls.addEventListener("end",(e)=>{  
+            //this.prepareRecord = true
+            transfromInfo && this.history.afterChange(transfromInfo.pointclouds , transfromInfo.pointclouds )
+            transfromInfo = null 
         })
         
         viewer.inputHandler.addEventListener('keydown',e=>{ 
-            if(e.keyCode == 90 && e.event.ctrlKey){//Z
+            /* if(e.keyCode == 90 && e.event.ctrlKey){//Z
                 this.history.undo()
-            } 
+            }else if(e.keyCode == 89 && e.event.ctrlKey){//Y
+                this.history.redo()
+            } */
         })  
 		 
         
@@ -391,9 +393,7 @@ var Alignment = {
                                   
                         
                             
-                                       
-           
-      
+ 
   
  
 /* 

+ 45 - 0
src/custom/modules/mergeModel/MergeEditor.js

@@ -595,7 +595,52 @@ let MergeEditor = {
      
         
         model.lastMatrixWorld = model.matrixWorld.clone()
+    },
+    
+    
+    changeOpacity(model, opacity){
+         
+        let isRoot = model.dataset_id != void 0  //是否是最外层 
+             
+    
+        if(model.isPointcloud){ 
+            model.changePointOpacity(opacity) 
+            //MergeEditor.updateEdgeStrength()
+        }else{
+            //model.traverse(e=>e.material && setOp(e, opacity))
+            model.traverse(mesh=>{
+                if(mesh.material){ 
+                
+                    mesh.material.opacity = opacity
+                    if(opacity<1){
+                        mesh.material.transparent = true 
+                        if(model.isPointcloud){
+                            mesh.changePointOpacity(opacity)  
+                        }else{
+                            mesh.material.opacity = opacity
+                        }
+                        
+                        mesh.renderOrder = Potree.config.renderOrders.model+1 
+                        mesh.material.depthWrite = false
+                    }else{
+                        mesh.material.transparent = false
+                        mesh.renderOrder = Potree.config.renderOrders.model
+                        mesh.material.depthWrite = true
+                    }
+                
+                }
+            })
+        }
+            
+              
+        isRoot && (model.opacity = opacity)//记录在最外层
+
+        
     }
+    
+    
+    
+    
 }   
     
     

+ 13 - 11
src/custom/modules/panoEdit/panoEditor.js

@@ -525,7 +525,7 @@ class PanoEditor extends THREE.EventDispatcher{
     zoomIn(intersect, pointer){ 
         let camera = viewer.mainViewport.camera
         let endZoom = 200
-        //this.moveFit(intersect, {endZoom:viewer.mainViewport.camera.zoom < aimZoom ? aimZoom : null}  , 300) 
+        //this.orthoMoveFit(intersect, {endZoom:viewer.mainViewport.camera.zoom < aimZoom ? aimZoom : null}  , 300) 
         let startZoom = camera.zoom
         if(startZoom >= endZoom){return}
         
@@ -533,7 +533,7 @@ class PanoEditor extends THREE.EventDispatcher{
     
     }
    
-    moveFit(pos, info, duration){  
+    orthoMoveFit(pos, info, duration){  
         var margin = {x:200, y:230}      
         this.splitScreenTool.viewportFitBound(viewer.mainViewport,  info.bound,  pos, duration, margin )
          
@@ -564,14 +564,16 @@ class PanoEditor extends THREE.EventDispatcher{
    
         if(this.currentFloor == floor && !force)return
         
-        //let pointclouds = viewer.findPointcloudsAtFloor(floor)
-        let panos = floor == 'all' ? viewer.images360.panos : floor.panos  
-       
-        viewer.images360.panos.forEach(pano=>{
-            let v = panos.includes(pano)
-            this.switchPanoVisible(pano,v) 
-        })
         
+        if(this.currentFloor != floor){//如果楼层没变,不修改可视
+            //let pointclouds = viewer.findPointcloudsAtFloor(floor)
+            let panos = floor == 'all' ? viewer.images360.panos : floor.panos  
+             
+            viewer.images360.panos.forEach(pano=>{  
+                let v = panos.includes(pano)
+                this.switchPanoVisible(pano,v) 
+            })
+        }
         
         this.updateLinesVisible() 
         
@@ -598,7 +600,7 @@ class PanoEditor extends THREE.EventDispatcher{
         } 
         
         if(this.activeViewName != 'mainView'   ){ 
-            fitBound && this.moveFit(center, {bound},  duration)
+            fitBound && this.orthoMoveFit(center, {bound},  duration)
         }else if(this.activeViewName == 'mainView'){
             if(floor != 'all'){ //切换一下位置,因为原处点云会消失
                  viewer.scene.view.setView({position:center,  duration })
@@ -1112,7 +1114,7 @@ class PanoEditor extends THREE.EventDispatcher{
                     }
                     viewer.focusOnObject({ position:this.selectedPano.position}, 'point', null, {distance })
                 }else{
-                    this.moveFit(this.selectedPano.position, {}, 500)
+                    this.orthoMoveFit(this.selectedPano.position, {}, 500)
                 }
             } 
         }else{

+ 18 - 17
src/custom/modules/panos/Images360.js

@@ -563,7 +563,7 @@ export class Images360 extends THREE.EventDispatcher{
             return viewer.scene.view.direction
         }else{
             return e = e ? e : (new THREE.Vector3).copy(Vectors.FORWARD),
-            e.applyQuaternion(viewer.scene.getActiveCamera().quaternion)
+            e.applyQuaternion(viewer.mainViewport.camera.quaternion)
         }
        
     }
@@ -956,23 +956,24 @@ export class Images360 extends THREE.EventDispatcher{
 	} */
 
 
-    updateCube(pano0, pano1){
-        
-                                                                                                                            
-        
+    updateCube(pano0, pano1){ 
         if(Potree.settings.displayMode != 'showPanos')return
         if(!viewer.scene.pointclouds.some(e=>!e.hasDepthTex))   return this.updateCube2(pano0, pano1) //都hasDepthTex的话
         
-       
-        let f = (bound, size)=>{
+        let useBound = (bound, size)=>{  
             size = size || bound.getSize(new THREE.Vector3) 
             let center = bound.getCenter(new THREE.Vector3)
-            size.max(new THREE.Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth))
+            size.max(new THREE.Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth)) 
             this.cube.scale.copy(size)  
-            this.cube.position.copy(center) 
-            
+            this.cube.position.copy(center)  
         }
         
+        let getPanoBound = (pano)=>{//因漫游点可能在点云外部,如室外平地,所以需要union进漫游点
+            let panoBound = new THREE.Box3
+            panoBound.expandByPoint(pano.position)
+            panoBound.expandByVector(new THREE.Vector3(10,10,10));//give pano a margin
+            return pano.pointcloud.bound.clone().union(panoBound)
+        }
         let getDis = (bound1, bound2)=>{ //获取bound1边界到bound2边界距离
             if(bound1.intersectsBox(bound2))return 0
             let center1 = bound1.getCenter(new THREE.Vector3) 
@@ -985,23 +986,23 @@ export class Images360 extends THREE.EventDispatcher{
         
         if(pano1){//过渡
             if(pano0.pointcloud == pano1.pointcloud){//同一个数据集内的过渡
-                f(pano0.pointcloud.bound)
+                useBound(getPanoBound(pano0))
                 //console.log('updateCube1' )
-            }else{//非同一个数据集内的过渡 
-                let bound = pano0.pointcloud.bound.clone().union(pano1.pointcloud.bound)
-                if(getDis(pano0.pointcloud.bound, pano1.pointcloud.bound) < 100){ 
-                    f(bound)
+            }else{//非同一个数据集内的过渡  
+                let bound = getPanoBound(pano0).union(getPanoBound(pano1))
+                if(getDis(pano0.pointcloud.bound, pano1.pointcloud.bound) < 100){  
+                    useBound(bound)
                 }else{//如果两个数据集boundingbox距离大于这个距离,扩大一下,防止精度问题导致失真//对很远的数据集似乎没有什么用
                     let size = bound.getSize(new THREE.Vector3) 
                     let max = Math.max(size.x, size.y, size.z)
                     size.set(max,max,max)
-                    f(bound, size)
+                    useBound(pano0.pointcloud.bound.clone().union(pano1.pointcloud.bound), size)
                     //console.log('updateCube2', size)
                     //far可能要修改下
                 }
             }
         }else{
-            f(pano0.pointcloud.bound) //假定一个数据集不会太大,否则会造成失真,且bump时移动距离看起来很小。或者可以考虑用固定大小
+            useBound(getPanoBound(pano0)) //假定一个数据集不会太大,否则会造成失真,且bump时移动距离看起来很小。或者可以考虑用固定大小
             //console.log('updateCube3' )
         } 
         

+ 6 - 6
src/custom/modules/panos/tile/PanoRenderer.js

@@ -1008,12 +1008,12 @@ PanoRenderer.prototype.renderToCubeMap = function() {
             scene.add(camera),
             material = new THREE.ShaderMaterial({
                 uniforms: {
-                    tDiffuse: {
-                        type: "scene",
+                    map: {
+                        type: "t",
                         value: null
                     },
-                    alpha: {
-                        type: "startYinTile",
+                    opacity: {
+                        type: "f",
                         value: 1
                     }
                 },
@@ -1072,12 +1072,12 @@ PanoRenderer.prototype.renderToCubeMap = function() {
             posArr[10] = N;
             
         renderer.properties.get(scene); 
-        material.uniforms.tDiffuse.value = texture;
+        material.uniforms.map.value = texture;
         material.blending = E || THREE.NoBlending,
         material.transparent = !!b 
         
         void 0 !== w && null !== w || (w = 1),
-        material.uniforms.alpha.value = w,
+        material.uniforms.opacity.value = w,
         material.needUpdate = !0  
         //renderTarget.activeCubeFace = cubeFace, //0-5 应该是指定渲染h中的面  失效
         /* renderer.setScissorTest(!0)  

+ 34 - 4
src/custom/objects/tool/Measure.js

@@ -244,7 +244,8 @@ export class Measure extends ctrlPolygon{
             
             let nextIndex = (index + 1 > lastIndex) ? 0 : index + 1;
             let previousIndex = (index === 0) ? lastIndex : index - 1;
-
+            //if(!this.closed && nextIndex == 0  )break; //add
+            
             let point = this.points[index];
             let nextPoint = this.points[nextIndex];
             let previousPoint = this.points[previousIndex];
@@ -253,7 +254,7 @@ export class Measure extends ctrlPolygon{
             if(this.showDistances){ // edge labels
                 let edgeLabel = this.edgeLabels[index];
                 let distance = point.distanceTo(nextPoint)
-                edgeLabel.shouldVisi = (index < lastIndex || this.isRect || this.closed && !this.isNew /* && this.points.length > 2 */) && distance>0 
+                edgeLabel.shouldVisi = (index < lastIndex || this.isRect || this.closed && !this.isNew ) && distance>0 
                 /* this.closed || */edgeLabel.setVisible(edgeLabel.shouldVisi)  
                 if(edgeLabel.visible){
                     setEdgeLabel(edgeLabel,point,nextPoint,distance)
@@ -583,11 +584,20 @@ export class Measure extends ctrlPolygon{
 	}
     
     getCenter(/* update */){ 
-        if(this.points.length>=3){
+        if(this.closed){
             return this.center.clone()
+        }else{  
+            let center = this.points.reduce(function(total, currentValue ){
+                return total.add(currentValue)
+            }, new THREE.Vector3 ) 
+            
+            center.multiplyScalar(1/this.points.length)
+            return center
+        }/* else if(this.points.length>=3){
+            
         }else if(this.points.length == 2){
             return this.points[0].clone().add((this.points[1])).multiplyScalar(0.5)
-        }else return this.points[0].clone()
+        }else return this.points[0].clone() */
         
     }
     
@@ -617,9 +627,11 @@ export class Measure extends ctrlPolygon{
         var verGuideEdge = LineDraw.createFatLine([ ],{material:this.getLineMat('guide')} )
         verGuideEdge.visible = false 
         this.verGuideEdge = verGuideEdge
+        verGuideEdge.name = 'verGuideEdge'
         
         var horGuideEdge = LineDraw.createFatLine([ ],{material:this.getLineMat('guide')} )
         horGuideEdge.visible = false 
+        horGuideEdge.name = 'horGuideEdge'
         this.horGuideEdge = horGuideEdge
         
         this.add(this.verGuideEdge);
@@ -806,6 +818,24 @@ export class Measure extends ctrlPolygon{
             prop.showEdges = true,
             prop.maxMarkers = 2,
             prop.minMarkers = 2 
+        }else if(prop.measureType == 'MulDistance'){//new 
+            prop.showDistances = true,  
+            prop.closed = false,
+            prop.showEdges = true, 
+            prop.minMarkers = 2 
+        }else if(prop.measureType == 'Ver MulDistance'){ 
+            prop.showDistances = true,  
+            prop.closed = false,
+            prop.showEdges = true, 
+            prop.minMarkers = 2 
+            prop.faceDirection = "vertical" 
+            prop.unableDragAtMap = true
+        }else if(prop.measureType == 'Hor MulDistance'){
+            prop.showDistances = true,  
+            prop.closed = false,
+            prop.showEdges = true, 
+            prop.minMarkers = 2 
+            prop.faceDirection = "horizontal"  
         }else if(prop.measureType == 'Ver Distance'){
             prop.showDistances = true,  
             prop.closed = false,

+ 6 - 4
src/custom/objects/tool/MeasuringTool.js

@@ -438,6 +438,8 @@ export class MeasuringTool extends THREE.EventDispatcher{
                     } */
                 } 
             }
+             
+            
             if (/* !e.finish&& */ measure.markers.length > args.minMarkers) {
 				measure.removeMarker(measure.points.length - 1); 
                 measure.markers[0].removeEventListener('mouseover', mouseover);
@@ -451,12 +453,12 @@ export class MeasuringTool extends THREE.EventDispatcher{
             measure.isNew = false
             let length = measure.points.length 
             if(length){
-                measure.markers[length-1].visible = true; 
-                measure.edges[length-1].visible = true  
+                measure.markers[length-1].visible = true;  
+                measure.edges[length-1].visible = !!measure.closed  
                 
                 measure.markers.forEach(marker=>{marker.dispatchEvent('addHoverEvent') })
                 measure.edges.forEach(edge=>{edge.dispatchEvent('addHoverEvent') })
-              
+                measure.update();//update last edgeLabel 
             }
             clearTimeout(timer) 
 			this.viewer.removeEventListener('cancel_insertions', Exit);
@@ -568,7 +570,7 @@ export class MeasuringTool extends THREE.EventDispatcher{
                 measure.markers[1].visible = false
                 measure.edges[1].visible = false
             }
-            if(measure.maxMarkers>2 && !measure.isRect){ 
+            if(measure.closed  && !measure.isRect){ 
                 measure.markers[0].addEventListener('mouseover', mouseover);
                 measure.markers[0].addEventListener('mouseleave', mouseleave);
                 measure.markers[0].addEventListener('click'/* 'mousedown' */,Exit) //点击到第一个marker就结束 

+ 14 - 6
src/custom/objects/tool/TransformControls.js

@@ -251,13 +251,21 @@ var TransformControls = function ( camera, domElement, options ) {
 	this.updateMatrixWorld = function () {
         if(!this.visible)return//add
 		if ( this.object !== undefined ) {
-
+ 
 			this.object.updateMatrixWorld();
 			this.object.parent.matrixWorld.decompose( parentPosition, parentQuaternion, parentScale );
 			this.object.matrixWorld.decompose( worldPosition, worldQuaternion, worldScale );
-            //add 使坐标轴在boundingBox中心
-            this.object.boundingBox && this.object.boundingBox.getCenter(worldPosition).applyMatrix4(this.object.matrixWorld);
-
+            
+            
+            //add
+            if(this.object.boundingBox){
+                let boundingBox = this.object.boundingBox.clone().applyMatrix4(this.object.matrixWorld)
+                boundingBox.getCenter(worldPosition) //bound中心
+                if(this.pivotOnBottom){
+                    worldPosition.setZ(boundingBox.min.z) //中心点居于模型bound底部,因固定离地高度,旋转时旋转中心在地面上就不会变位置
+                }
+            }  
+            
 
 			parentQuaternionInv.copy( parentQuaternion ).invert();
 			worldQuaternionInv.copy( worldQuaternion ).invert();
@@ -637,8 +645,8 @@ var TransformControls = function ( camera, domElement, options ) {
                         rotationAxis.copy( _unit[ axis ] );
                     }
                     let center = new THREE.Vector3//坐标轴位置
-                    if(this.object.boundingBox){
-                        this.object.boundingBox.getCenter(center).applyMatrix4(this.object.matrixWorld);
+                    if(this.object.boundingBox){ 
+                        center.copy(worldPosition)
                     }else{
                         center.copy(worldPositionStart)//boundingBox中心可能变化 这里可能要改成直接获取model的position
                     }

+ 8 - 6
src/custom/objects/tool/ctrlPolygon.js

@@ -232,7 +232,7 @@ export class ctrlPolygon extends THREE.Object3D {
             LineDraw.updateLine(this.guideLine, [location, projectPos])
             location = projectPos
             this.guideLine.visible = true
-        }else if( len > 1){ 
+        }else if( len > 1 ){ 
              
             var points = this.points.map(e=>e.clone())
             points[i].copy(location) //算normal需要提前确认point
@@ -286,9 +286,9 @@ export class ctrlPolygon extends THREE.Object3D {
                 }
             } 
 
-            if(len > 2){//area
-            
-                if(!this.faceDirection){ 
+            if(len > 2){ 
+                
+                if(!this.faceDirection && this.closed){ 
                     if(len == 3 || this.isRect) this.cannotConfirmNormal = true //当第三个点固定后(有四个点时)才能固定面
                     if(!this.facePlane || this.cannotConfirmNormal){
                         var points3 = getDifferentPoint(points, 3);//只有找到三个不同的点算拥有面和area
@@ -344,7 +344,7 @@ export class ctrlPolygon extends THREE.Object3D {
                 } */
                 this.getPoint2dInfo(points)
                 
-                var isIntersectSelf = !this.isRect && len > 3 && this.intersectSelf(this.point2dInfo.points2d)//检测相交
+                var isIntersectSelf = this.closed && !this.isRect && len > 3 && this.intersectSelf(this.point2dInfo.points2d)//检测相交
                 if(isIntersectSelf){
                     //not-allowed
                     viewer.dispatchEvent({
@@ -608,7 +608,7 @@ export class ctrlPolygon extends THREE.Object3D {
         
         
         
-        let lastIndex = this.points.length - 1;
+        let lastIndex = this.points.length - 1
             
        
         for (let index = 0; index <= lastIndex; index++) {
@@ -616,6 +616,8 @@ export class ctrlPolygon extends THREE.Object3D {
             let nextIndex = (index + 1 > lastIndex) ? 0 : index + 1;
             let previousIndex = (index === 0) ? lastIndex : index - 1;
 
+            if(!this.closed && nextIndex == 0  )break; //add
+        
             let point = this.points[index];
             let nextPoint = this.points[nextIndex];
             let previousPoint = this.points[previousIndex];

+ 292 - 76
src/custom/potree.shim.js

@@ -13,19 +13,21 @@ import {PointAttribute,PointAttributeTypes} from "../loader/PointAttributes.js";
 import {ProfileWindow} from "../viewer/profile.js";
 import {XHRFactory} from "../XHRFactory.js";
 import {ClipTask, ClipMethod} from "../defines.js";
+
+import {VolumeTool} from "../utils/VolumeTool.js";
 import {Box3Helper} from "../utils/Box3Helper.js";
 import {KeyCodes} from "../KeyCodes.js";
 import {HQSplatRenderer} from "../viewer/HQSplatRenderer.js";
 import {LRU} from "../LRU.js";
-import {PointCloudMaterial} from '../materials/PointCloudMaterial.js'
+import {ExtendPointCloudMaterial} from '../materials/ExtendPointCloudMaterial.js'
  
 import {PointCloudOctreeGeometry, PointCloudOctreeGeometryNode} from '../PointCloudOctreeGeometry.js'
 import {Shaders} from "../../build/shaders/shaders.js";  
- 
-
+  
+import {LineSegmentsGeometry} from '../../libs/three.js/lines/LineSegmentsGeometry.js'  
+import {LineGeometry} from '../../libs/three.js/lines/LineGeometry.js'  
 KeyCodes.BACKSPACE = 8
 
-
 {//defines:
     Potree.defines = {}
     Potree.defines.Buttons = {// MouseEvent.buttons
@@ -121,7 +123,7 @@ KeyCodes.BACKSPACE = 8
         VideoRendered: "panorama.video.rendered"
     };
  
-    
+    ClipTask.SHOW_INSIDE_Big = 4 
 }
 
 
@@ -213,9 +215,7 @@ Utils.getMousePointCloudIntersection = function(viewport, mouse, pointer, camera
     
       
     if(viewport){ //转换到类似整个画面时
-        
-         /*let mouseInViewport = Utils.convertNDCToScreenPosition(pointer, null, viewport.resolution.x, viewport.resolution.y)
-    
+        /*let mouseInViewport = Utils.convertNDCToScreenPosition(pointer, null, viewport.resolution.x, viewport.resolution.y)
         pickParams.x = mouseInViewport.x   //mouse.x / viewport.width;
         pickParams.y = mouseInViewport.y //renderer.domElement.clientHeight - mouse.y / viewport.height;  */
         pickParams.x = mouse.x;
@@ -226,10 +226,7 @@ Utils.getMousePointCloudIntersection = function(viewport, mouse, pointer, camera
     } 
      
     //console.log('getMousePointCloudIntersection')
-    
-
-     
-    
+      
     /* if(!raycaster){
         raycaster = new THREE.Raycaster();
         raycaster.setFromCamera(pointer, camera); 
@@ -245,9 +242,19 @@ Utils.getMousePointCloudIntersection = function(viewport, mouse, pointer, camera
     let closestPoint = null;
     
     
+    
+    //-----------add--------------------
+    let old_clipBoxes_in = new Map()  
+    let old_clipBoxes_out = new Map()  
+    let old_bigClipInBox = new Map()  
+     
+    
+    //bigClipInBox 最好也写下 
     let density
     let sizeType
     let size = new Map()  
+    let needsUpdate = false;
+    
     if(pickParams.isMeasuring || Potree.settings.displayMode == 'showPanos'){ //测量或全景模式提高精准度
         density = Potree.settings.pointDensity 
         Potree.settings.pointDensity = 'magnifier' 
@@ -259,16 +266,31 @@ Utils.getMousePointCloudIntersection = function(viewport, mouse, pointer, camera
              
             e.changePointSize(Potree.config.material.realPointSize*2, true)//更改点云大小到能铺满为止,否则容易识别不到
         }) 
-        Potree.updatePointClouds(pointclouds,  camera, viewport.resolution );
+        needsUpdate = true
     }else{
         if(viewer.viewports.filter(e=>!e.noPointcloud && e.active).length>1 || pickParams.cameraChanged){
             viewport.beforeRender && viewport.beforeRender()
-            Potree.updatePointClouds(pointclouds,  camera, viewport.resolution ); //不加这句的话hover久了会不准 因node是错的
+            needsUpdate = true //不updatePointClouds的话hover久了会不准 因node是错的
             //但依旧需要camera真的移动到那个位置才能加载出点云
+        } 
+    }
+    
+    if(!pickParams.pickClipped){// 无视clipBoxes
+        for(let pointcloud of pointclouds){  
+            old_clipBoxes_in.set(pointcloud, pointcloud.clipBoxes_in)
+            old_clipBoxes_out.set(pointcloud, pointcloud.clipBoxes_in)
+            old_bigClipInBox.set(pointcloud, pointcloud.bigClipInBox)
+            pointcloud.material.setClipBoxes(null, [],[])  
         }
-        
+        needsUpdate = true
     }
-     
+    
+    if(needsUpdate){
+        Potree.updatePointClouds(pointclouds,  camera, viewport.resolution );
+    }
+    //------------------------------------------------
+    
+    
     let allPointclouds = [] 
     for(let pointcloud of pointclouds){ 
         
@@ -306,7 +328,11 @@ Utils.getMousePointCloudIntersection = function(viewport, mouse, pointer, camera
             viewport.afterRender && viewport.afterRender() 
         } */
     }
-
+    if(!pickParams.pickClipped){//add  
+        for(let pointcloud of pointclouds){  
+            pointcloud.material.setClipBoxes(old_bigClipInBox.get(pointcloud), old_clipBoxes_in.get(pointcloud), old_clipBoxes_out.get(pointcloud))  
+        } 
+    }
 
     if (selectedPointcloud) {
         return {
@@ -608,7 +634,7 @@ Utils.isInsideBox = function(object,  boxMatrixInverse){//object可以是点或
     frustum.setFromProjectionMatrix(boxMatrixInverse) 
      
     if(object instanceof THREE.Box3){
-        return frustum.intersectsSphere(object)  
+        return frustum.intersectsBox(object)  
     }else if(object instanceof Array){//点合集,先求Sphere setFromPoints
         let sphere = new THREE.Sphere()
         sphere.setFromPoints(object) 
@@ -618,7 +644,7 @@ Utils.isInsideBox = function(object,  boxMatrixInverse){//object可以是点或
         return frustum.intersectsSphere(object)  
     }else if(object instanceof THREE.Vector3){
         return frustum.containsPoint(object)  
-    }
+    } 
 
     /* containsPoint: ƒ containsPoint( point ) 
     intersectsBox: ƒ intersectsBox( box )
@@ -734,7 +760,7 @@ ProfileWindow.prototype.initTHREE = function(){
 }
 
 //Potree_update_visibility
-Potree.updatePointClouds =  function(pointclouds,camera, areaSize /* renderer */){
+Potree.updatePointClouds =  function(pointclouds,camera, areaSize  ){
  
 	for (let pointcloud of pointclouds) {
 		let start = performance.now();
@@ -916,8 +942,110 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 		visible = visible && level <= maxLevel; //< 改为 <=
 		//visible = visible || node.getLevel() <= 2;
 
-		let clipBoxes = pointcloud.material.clipBoxes;
-		if(true && clipBoxes.length > 0){
+
+        let insideBox = (clipBox)=>{
+
+            let pcWorldInverse = pointcloud.matrixWorld.clone().invert();
+            let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld);
+
+            let px = new THREE.Vector3(+0.5, 0, 0).applyMatrix4(pcWorldInverse);
+            let nx = new THREE.Vector3(-0.5, 0, 0).applyMatrix4(pcWorldInverse);
+            let py = new THREE.Vector3(0, +0.5, 0).applyMatrix4(pcWorldInverse);
+            let ny = new THREE.Vector3(0, -0.5, 0).applyMatrix4(pcWorldInverse);
+            let pz = new THREE.Vector3(0, 0, +0.5).applyMatrix4(pcWorldInverse);
+            let nz = new THREE.Vector3(0, 0, -0.5).applyMatrix4(pcWorldInverse);
+
+            let pxN = new THREE.Vector3().subVectors(nx, px).normalize();
+            let nxN = pxN.clone().multiplyScalar(-1);
+            let pyN = new THREE.Vector3().subVectors(ny, py).normalize();
+            let nyN = pyN.clone().multiplyScalar(-1);
+            let pzN = new THREE.Vector3().subVectors(nz, pz).normalize();
+            let nzN = pzN.clone().multiplyScalar(-1);
+
+            let pxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pxN, px);
+            let nxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nxN, nx);
+            let pyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pyN, py);
+            let nyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nyN, ny);
+            let pzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pzN, pz);
+            let nzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nzN, nz);
+
+            //if(window.debugdraw !== undefined && window.debugdraw === true && node.name === "r60"){
+
+            //	Potree.utils.debugPlane(viewer.scene.scene, pxPlane, 1, 0xFF0000);
+            //	Potree.utils.debugPlane(viewer.scene.scene, nxPlane, 1, 0x990000);
+            //	Potree.utils.debugPlane(viewer.scene.scene, pyPlane, 1, 0x00FF00);
+            //	Potree.utils.debugPlane(viewer.scene.scene, nyPlane, 1, 0x009900);
+            //	Potree.utils.debugPlane(viewer.scene.scene, pzPlane, 1, 0x0000FF);
+            //	Potree.utils.debugPlane(viewer.scene.scene, nzPlane, 1, 0x000099);
+
+            //	Potree.utils.debugBox(viewer.scene.scene, box, new THREE.Matrix4(), 0x00FF00);
+            //	Potree.utils.debugBox(viewer.scene.scene, box, pointcloud.matrixWorld, 0xFF0000);
+            //	Potree.utils.debugBox(viewer.scene.scene, clipBox.box.boundingBox, clipBox.box.matrixWorld, 0xFF0000);
+
+            //	window.debugdraw = false;
+            //}
+
+            let frustum = new THREE.Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane);
+            let intersects = frustum.intersectsBox(box);
+
+            /* if(intersects){
+                    numIntersecting++;
+                }
+                numIntersectionVolumes++;
+            */
+            return !!intersects
+        }
+
+        //改 总共两种box : 可见和不可见(都是并集)
+ 
+    
+        
+		let clipBoxes_in = pointcloud.material.clipBoxes_in;
+		let clipBoxes_out = pointcloud.material.clipBoxes_out;
+        let bigClipInBox = pointcloud.material.bigClipInBox
+        
+        if(visible && bigClipInBox){//不在剪裁下载的框内
+            if(!insideBox(bigClipInBox)){
+                visible = false; 
+            } 
+        }
+        
+        
+		if(visible && clipBoxes_in.length > 0){//当有可见box时,需要在任一可见box内才可见
+            let visi = false;
+            for(let i = 0, length=clipBoxes_in.length; i < length; i++){ 
+                if(insideBox(clipBoxes_in[i])){
+                    visi = true;
+                    break;
+                } 
+            }
+            if(!visi){
+                visible = false  
+            }
+        }
+        
+        
+        //outside不做处理。因为node必须完全在clipBox内才能完全隐藏,而这里的intersect只能识别出部分在clipBox内
+        /* if(visible && clipBoxes_out.length > 0){ //当有不可见box时,不在所有不可见box内才可见
+            let visi = true;
+            for(let i = 0,length=clipBoxes_out.length; i < length; i++){ 
+                if(insideBox(clipBoxes_out[i])){
+                    visi = false;
+                    break;
+                } 
+            }
+            if(!visi){
+                visible = false  
+            }
+        }
+        */
+
+
+
+
+
+            /*let clipBoxes = pointcloud.material.clipBoxes;
+		 if(true && clipBoxes.length > 0){
 
 			//node.debug = false;
 
@@ -929,54 +1057,6 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 			//}
 
 			for(let clipBox of clipBoxes){
-
-				let pcWorldInverse = pointcloud.matrixWorld.clone().invert();
-				let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld);
-
-				let px = new THREE.Vector3(+0.5, 0, 0).applyMatrix4(pcWorldInverse);
-				let nx = new THREE.Vector3(-0.5, 0, 0).applyMatrix4(pcWorldInverse);
-				let py = new THREE.Vector3(0, +0.5, 0).applyMatrix4(pcWorldInverse);
-				let ny = new THREE.Vector3(0, -0.5, 0).applyMatrix4(pcWorldInverse);
-				let pz = new THREE.Vector3(0, 0, +0.5).applyMatrix4(pcWorldInverse);
-				let nz = new THREE.Vector3(0, 0, -0.5).applyMatrix4(pcWorldInverse);
-
-				let pxN = new THREE.Vector3().subVectors(nx, px).normalize();
-				let nxN = pxN.clone().multiplyScalar(-1);
-				let pyN = new THREE.Vector3().subVectors(ny, py).normalize();
-				let nyN = pyN.clone().multiplyScalar(-1);
-				let pzN = new THREE.Vector3().subVectors(nz, pz).normalize();
-				let nzN = pzN.clone().multiplyScalar(-1);
-
-				let pxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pxN, px);
-				let nxPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nxN, nx);
-				let pyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pyN, py);
-				let nyPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nyN, ny);
-				let pzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(pzN, pz);
-				let nzPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(nzN, nz);
-
-				//if(window.debugdraw !== undefined && window.debugdraw === true && node.name === "r60"){
-
-				//	Potree.utils.debugPlane(viewer.scene.scene, pxPlane, 1, 0xFF0000);
-				//	Potree.utils.debugPlane(viewer.scene.scene, nxPlane, 1, 0x990000);
-				//	Potree.utils.debugPlane(viewer.scene.scene, pyPlane, 1, 0x00FF00);
-				//	Potree.utils.debugPlane(viewer.scene.scene, nyPlane, 1, 0x009900);
-				//	Potree.utils.debugPlane(viewer.scene.scene, pzPlane, 1, 0x0000FF);
-				//	Potree.utils.debugPlane(viewer.scene.scene, nzPlane, 1, 0x000099);
-
-				//	Potree.utils.debugBox(viewer.scene.scene, box, new THREE.Matrix4(), 0x00FF00);
-				//	Potree.utils.debugBox(viewer.scene.scene, box, pointcloud.matrixWorld, 0xFF0000);
-				//	Potree.utils.debugBox(viewer.scene.scene, clipBox.box.boundingBox, clipBox.box.matrixWorld, 0xFF0000);
-
-				//	window.debugdraw = false;
-				//}
-
-				let frustum = new THREE.Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane);
-				let intersects = frustum.intersectsBox(box);
-
-				if(intersects){
-					numIntersecting++;
-				}
-				numIntersectionVolumes++;
 			}
 
 			let insideAny = numIntersecting > 0;
@@ -1001,9 +1081,9 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 				//	visible = false;
 				//}
 			}
-			
+			 
 
-		}
+		}*/
 
 		// visible = ["r", "r0", "r06", "r060"].includes(node.name);
 		// visible = ["r"].includes(node.name);
@@ -1061,7 +1141,7 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
                 node._transformVersion = transformVersion.number;
                                
 			}
-
+ 
 			if (pointcloud.showBoundingBox && !node.boundingBoxNode && node.getBoundingBox) {
 				let boxHelper = new Box3Helper(node.getBoundingBox());
 				boxHelper.matrixAutoUpdate = false;
@@ -1073,7 +1153,7 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 				node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld);
 			} else if (!pointcloud.showBoundingBox && node.boundingBoxNode) {
 				node.boundingBoxNode.visible = false;
-			}
+			}   
 
 			// if(node.boundingBoxNode !== undefined && exports.debug.allowedNodes !== undefined){
 			// 	if(!exports.debug.allowedNodes.includes(node.name)){
@@ -1207,12 +1287,12 @@ Potree.updateVisibility = function(pointclouds, camera, areaSize){
 			originalMaterials.set(pointcloud, pointcloud.material);
 
 			if(!this.attributeMaterials.has(pointcloud)){
-				let attributeMaterial = new PointCloudMaterial();
+				let attributeMaterial = new ExtendPointCloudMaterial();
 				this.attributeMaterials.set(pointcloud, attributeMaterial);
 			}
 
 			if(!this.depthMaterials.has(pointcloud)){
-				let depthMaterial = new PointCloudMaterial();
+				let depthMaterial = new ExtendPointCloudMaterial();
 
 				depthMaterial.setDefine("depth_pass", "#define hq_depth_pass");
 				depthMaterial.setDefine("use_edl", "#define use_edl");
@@ -1585,3 +1665,139 @@ LRU.prototype.disposeSubtree = function(t) {//add from navvis 25.js
     }
 }
 
+VolumeTool.prototype.update = function(){}
+
+VolumeTool.prototype.startInsertion = function(args = {}){
+    
+    let volume;
+    if(args.type){
+        volume = new args.type();
+    }else{
+        volume = new Potree.BoxVolume({clip:args.clip, clipTask:ClipTask.SHOW_OUTSIDE});
+    }
+      
+    volume.name = args.name || 'Volume';
+    
+      
+    
+    let camera = this.viewer.scene.getActiveCamera();
+    let updateScale = ()=>{ //保证在视野中的大小一致:
+    
+        let w = math.getScaleForConstantSize({
+            width2d: 300,
+            camera , position:volume.getWorldPosition(new THREE.Vector3()) ,
+            resolution: viewer.mainViewport.resolution//2
+        })
+        /* let wp = volume.getWorldPosition(new THREE.Vector3()).applyMatrix4(camera.matrixWorldInverse);
+        // let pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(camera.projectionMatrix);
+        let w = Math.abs((wp.z / 3));*/
+        volume.scale.set(w, w, w);  
+    }
+
+    this.dispatchEvent({
+        type: 'start_inserting_volume',
+        volume: volume
+    });
+    
+    if(viewer.scene.volumes.length>0){
+        volume.rotation.copy(viewer.scene.volumes[viewer.scene.volumes.length-1].rotation); //使用上一个的旋转值
+    }
+    updateScale()
+    this.viewer.scene.addVolume(volume);
+    this.scene.add(volume);
+    viewer.transformObject(volume)
+    
+    
+    
+    let drag = e => {
+        
+        let I = Utils.getMousePointCloudIntersection(
+            viewer.mainViewport,
+            viewer.inputHandler.mouse,
+            viewer.inputHandler.pointer,
+            this.viewer.scene.getActiveCamera(), 
+            this.viewer, 
+            this.viewer.scene.pointclouds, 
+            {pickClipped: false} //无视clip状态
+        );
+            
+        var worldPos = I && I.location 
+        if(!worldPos){
+            return  
+        }
+             
+        volume.position.copy(worldPos);
+        
+        updateScale() 
+    };
+    
+    let cancel = ()=>{
+        end()
+    }
+    let end = e => {
+        volume.removeEventListener('drag', drag);
+        volume.removeEventListener('drop', end);
+        this.viewer.removeEventListener('cancel_insertions', cancel);
+    };
+    
+    volume.addEventListener('drag', drag);
+    volume.addEventListener('drop', end);
+    
+    this.viewer.addEventListener('cancel_insertions', cancel);
+
+    this.viewer.inputHandler.startDragging(volume);
+    
+    return volume;
+}
+
+VolumeTool.prototype.loadFromData = function(data=[]){
+    data.forEach(v=>{
+        let volume = new Potree.BoxVolume({clip:true, clipTask:v.clipTask});
+        volume.scale.fromArray(v.scale);
+        volume.position.fromArray(v.position);
+        volume.rotation.fromArray(v.rotation);  
+        this.viewer.scene.addVolume(volume);
+        this.scene.add(volume);
+    })
+}
+VolumeTool.prototype.saveClipData = function(){//输出所有的clip volumeBox
+    let oldState = !viewer.clipUnabled;
+    viewer.setClipState(true)
+    let data = viewer.scene.volumes.filter(v=>v.clip && v instanceof Potree.BoxVolume).map(volume=>{
+        return {  
+            clipTask: volume.clipTask,
+            position: volume.position.toArray(),
+            rotation: volume.rotation.toArray(),
+            scale: volume.scale.toArray(),  
+        }
+    })
+    console.log(data)
+    console.log(JSON.stringify(data))
+    viewer.setClipState(oldState)
+    return data
+}
+
+LineGeometry.prototype.setPositions = function( array ) {
+
+    // converts [ x1, y1, z1,  x2, y2, z2, ... ] to pairs format
+      
+    const length = array.length - 3 > 0 ? array.length - 3 : 0; //xzw改 必须>0 
+    const points = new Float32Array( 2 * length );
+
+    for ( let i = 0; i < length; i += 3 ) {
+
+        points[ 2 * i ] = array[ i ];
+        points[ 2 * i + 1 ] = array[ i + 1 ];
+        points[ 2 * i + 2 ] = array[ i + 2 ];
+
+        points[ 2 * i + 3 ] = array[ i + 3 ];
+        points[ 2 * i + 4 ] = array[ i + 4 ];
+        points[ 2 * i + 5 ] = array[ i + 5 ];
+
+    }
+
+    LineSegmentsGeometry.prototype.setPositions.call(this, points );
+
+    return this;
+
+}  

+ 1 - 2
src/custom/settings.js

@@ -370,7 +370,7 @@ let settings = {//设置   可修改
     isLocal:false, //是否本地 局域网版本
     libsUrl:'../libs/',
     displayMode:'',
-    isTest :browser.urlHasValue('test'),
+    isTest : browser.urlHasValue('test'),
     prefix: getPrefix(),
     pointDensity: '',    UserPointDensity:'',//pointDensity会随着进入不同的模块而自动改变,UserPointDensity记录了用户的设置
     UserDensityPercent:null,//点云密度百分比 
@@ -439,7 +439,6 @@ let settings = {//设置   可修改
 
 settings.isLocalhost = settings.prefix.includes('localhost')
 
-
 /* 
     关于maxLevel:
     viewer.scene.pointclouds[0].root.getLevel() 是 0

+ 37 - 18
src/custom/start.js

@@ -47,6 +47,7 @@ export function start(dom, mapDom, number ){ //t-Zvd3w0m
             viewer.toggleSidebar();
         }); 
         Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
+        Potree.settings.rotAroundPoint = false
     }
 
     Potree.loadDatasetsCallback = function(data, ifReload){
@@ -201,7 +202,7 @@ export function start(dom, mapDom, number ){ //t-Zvd3w0m
                     pointcloud.hasDepthTex = Potree.settings.useDepthTex && (!!dataset.has_depth  ||  Potree.settings.isLocalhost && Potree.settings.number == 'SS-t-7DUfWAUZ3V') //test   
                     material.minSize =  config.minSize
                     material.maxSize =  config.maxSize   
-                    material.pointSizeType = Potree.settings.isOfficial ? config.pointSizeType : 'ADAPTIVE'  //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
+                    material.pointSizeType = /* Potree.settings.isOfficial ? */ config.pointSizeType/*  : 'ADAPTIVE' */  //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
                     pointcloud.changePointSize(config.realPointSize)  //material.size =  config.pointSize;
                     pointcloud.changePointOpacity(1)
                     material.shape = Potree.PointShape.SQUARE; 
@@ -726,16 +727,11 @@ export function mergeEditStart(dom){
             
         }
         
+         
         
         
+        if(prop.type == 'glb'){ 
         
-        
-        
-        
-        if(prop.type == 'glb'){
-            
-            
-            
             let callback = (object)=>{
                 //focusOnSelect(object, 1000)  
                 object.isModel = true
@@ -755,27 +751,50 @@ export function mergeEditStart(dom){
                 name: prop.type, 
                 id: prop.id,
                 unlit:true,
+                glburl : prop.url,
                 /* transform : { 
                     position : prop.position,
                     rotation : new THREE.Euler().setFromVector3(prop.rotation), 
                     scale: new THREE.Vector3(prop.scale,prop.scale,prop.scale),        
                 }  */               
-            }
-            
-            if(prop.type == 'glb'){
-                info.glburl = prop.url  
-            }
-                 
+            }    
               
             viewer.loadModel(info , callback, onProgress, onError)
             
             
             
+        }else if(prop.type == 'obsg'){  //3d tiles  
+        
+            let callback = (object)=>{
+                 
+                object.isModel = true 
+                //透明度怎么办
+                //object.traverse(e=>e.material && (e.material.transparent = true))
+                
+                loadDone(object)
+            }
+        
+            viewer.loadModel({ 
+                name: '3dTiles', 
+                id: prop.id,
+                /* tilesUrl: 'https://4dkk.4dage.com/scene_view_data/SS-Ds19qsmuFA/images/3dtiles/tileset.json',
+                transform : { 
+                    rotation : [Math.PI/2,  0,   0],
+                    position : [0,0,0]  
+                }  
+
+                  tilesUrl: 'https://testgis.4dage.com/LVBADUI_qp/tileset.json',
+                transform : { 
+                    rotation : [0,  0,   0],
+                    position : [0,0,0]  
+                }  */
+                
+                tilesUrl:prop.url,
+                
+            },callback,onprogress)
+        
             
-            
-            
-            
-          }else{  
+        }else{  
             
              //else if(prop.type == 'las' || prop.type == 'ply')
  

+ 8 - 5
src/custom/utils/Common.js

@@ -184,17 +184,20 @@ var Common = {
     }
     ,
     
-    ifSame : function(object1, object2){
+    ifSame : function(object1, object2, simpleEqualClass=[]){ //对于复杂的类对象,若能简单判断就直接写进simpleEqualClass 
         if(object1 == object2  )return true // 0 != undefined  , 0 == ''
-        else if(!object1 || !object2) return false
+        else if(!object1 || !object2) return false 
         else if(object1.constructor != object2.constructor){
             return false
+        }else if(simpleEqualClass.some(className => object1 instanceof className)){
+            return object1 == object2
+        
         }else if(object1 instanceof Array ) {
             if(object1.length != object2.length)return false;
             var _object2 = object2.slice(0);
             
             for(let i=0;i<object1.length;i++){ 
-                var u = _object2.find(e=>ifSame(object1[i], e));
+                var u = _object2.find(e=>Common.ifSame(object1[i], e, simpleEqualClass));
                 if(u == void 0 && !_object2.includes(u) && !object1.includes(u))return false;
                 else{
                     let index = _object2.indexOf(u);
@@ -214,10 +217,10 @@ var Common = {
         }else if(typeof object1 == "object"){
             var keys1 = Object.keys(object1)
             var keys2 = Object.keys(object2)
-            if(!ifSame(keys1,keys2))return false;
+            if(!Common.ifSame(keys1,keys2,simpleEqualClass))return false;
             
             for(let i in object1){
-                var same = ifSame(object1[i], object2[i]);
+                var same = Common.ifSame(object1[i], object2[i],simpleEqualClass);
                 if(!same)return false
             }
             return true

+ 18 - 23
src/custom/utils/DrawUtil.js

@@ -117,31 +117,26 @@ var LineDraw = {
 	moveFatLine: function(line, posArr){
 		var geometry = line.geometry;
         var positions = [];
-         
-        posArr.forEach(e=>{positions.push(...e.toArray())})
         
-        
-		if(positions.length > 0){
-            if(!geometry){
-                geometry = line.geometry = new LineGeometry(); 
-            }
-            if(geometry.attributes.instanceEnd && geometry.attributes.instanceEnd.data.array.length != positions.length){//positions个数改变会有部分显示不出来,所以重建
-                geometry.dispose();
-                geometry = new LineGeometry();
-                line.geometry = geometry
-            }
-            geometry.setPositions( positions ) 
-            
-            if(line.material.defines.USE_DASH != void 0){
-                //line.geometry.verticesNeedUpdate = true; //没用
-                line.geometry.computeBoundingSphere(); //for raycaster
-                line.computeLineDistances(); 
-            } 
-        }else{
-            geometry.dispose()
-            line.geometry = new LineGeometry(); 
-             
+        posArr.forEach(e=>{positions.push(...e.toArray())})
+         
+		 
+        if(!geometry){
+            geometry = line.geometry = new LineGeometry(); 
+        }
+        if(geometry.attributes.instanceEnd && geometry.attributes.instanceEnd.data.array.length != positions.length){//positions个数改变会有部分显示不出来,所以重建
+            geometry.dispose();
+            geometry = new LineGeometry();
+            line.geometry = geometry
         }
+        geometry.setPositions( positions ) 
+        
+        if(line.material.defines.USE_DASH != void 0){
+            //line.geometry.verticesNeedUpdate = true; //没用
+            line.geometry.computeBoundingSphere(); //for raycaster
+            line.computeLineDistances(); 
+        } 
+         
         
         
 	},

+ 81 - 17
src/custom/utils/History.js

@@ -1,41 +1,105 @@
  
 import * as THREE from "../../../libs/three.js/build/three.module.js";
 
+
+import Common from './Common.js'
+
 class History extends THREE.EventDispatcher{
     
     constructor(o){ 
         super()
         
-        this.list = []
-        
-        this.callback = o.callback
+        this.undoList = []   
+        this.redoList = []
+        //this.initializedMasters = []// [objects1, objects2,....]
+        this.applyData = o.applyData
+        this.getData = o.getData
+        this.prepareRecord = true
+        this.currentData 
     }
     
-    undo(){
-        let last = this.list.pop();
-        last && this.callback && this.callback(last) 
-        this.dispatchEvent('undo')
-        
+    undo(){ 
+        let length = this.undoList.length
+        /* if(length>1){  
+            let last = this.undoList.pop();
+            this.redoList.push(last);
+            let now = this.undoList[length-2];
+            this.applyData && this.applyData(now.data) 
+            this.dispatchEvent('undo')
+            console.log('undo',now.data)
+        }  */
+        if(length>0){  
+            let last = this.undoList.pop(); 
+            this.applyData && this.applyData(last) 
+            this.redoList.push(this.currentData);
+            this.currentData = last
+            this.dispatchEvent('undo')
+            console.log('undo',last)
+        } 
     }
     
     
-    redo(){//暂时不写
-        
-        
+    redo(){ 
+        let length = this.undoList.length
+        let last = this.redoList.pop();
+        if(last){
+            //注意:每行的顺序不能乱
+            this.undoList.push(this.currentData) 
+            this.currentData = last
+            this.applyData && this.applyData(this.currentData) 
+            this.dispatchEvent('redo')
+            console.log('redo',last)
+        }
+    }
+     
+    
+    beforeChange(o/* , masters, simpleEqualClass */){
+        /* if(!this.initializedMasters.find(e=>Common.ifSame(e, masters, simpleEqualClass))){  
+            let data = this.getData(o)
+            console.log('beforeChange',data )
+            this.writeIn(data, masters) 
+            this.initializedMasters.push(masters)
+            
+        }  */
         
+        if(this.prepareRecord){
+            let data = this.getData(o)
+            this.writeIn(data)
+            this.prepareRecord = false
+        } 
+    } 
+    
+    afterChange(o/* , masters  */){ 
+        //let data = this.getData(o)
+        //console.log('afterChange',data )
+        //this.writeIn(data , masters ) 
+        this.currentData = this.getData(o)
+        this.prepareRecord = true    
     }
     
-    writeIn(item){
+    writeIn(data/*  , masters  */){
         
-        this.list.push(item)
+        /* this.redoList.forEach(e=>{ 
+            if(!this.undoList.some(item=>Common.ifSame(item.masters, e.masters))){//删除该主体
+                let masters = this.initializedMasters.find(masters=>Common.ifSame(e.masters, masters))
+                if(masters){
+                    let index = this.initializedMasters.indexOf(masters)
+                    this.initializedMasters.splice(index,1)
+                    console.log('删除该主体',masters)
+                }
+            }  
+        }) */
         
+        this.redoList.length = 0; //一旦录入新的操作,就不允许恢复了
+         
+        this.undoList.push(data/* {data,masters} */);
+        console.log('新增undo', data)    
     }
     
     
-    clear(){
-        
-        this.list.length = 0
-        
+    clear(){ 
+        this.redoList.length = 0 
+        this.undoList.length = 0 
     }
     
     

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

@@ -9,8 +9,11 @@ class SplitScreen extends THREE.EventDispatcher{
 		super();
         
     }
-    
-    
+    /* 
+        viewport.targetPlane  // bound中心点处的plane,方向和view一致
+        viewport.shiftTarget  // camera的位置project在targetPlane上的位置
+        这两个参数的主要目的是为了getPosOutOfModel,以及rotateSideCamera时保持相对位置
+    */
     splitStart(cameraProps){ 
         let viewports = []
       

File diff suppressed because it is too large
+ 5074 - 0
src/custom/viewer/ViewerNew.js


+ 53 - 3
src/materials/ExtendPointCloudMaterial.js

@@ -33,13 +33,24 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
 		this._gradient = Gradients.RAINBOW//Gradients.SPECTRAL;//海拔贴图种类
 		this.gradientTexture = ExtendPointCloudMaterial.generateGradientTexture(this._gradient); 
 		//this.matcapTexture = ExtendPointCloudMaterial.generateMatcapTexture(this._matcap);
-
+        
         delete this.uniforms.screenWidth
         delete this.uniforms.screenHeight
+        delete this.uniforms.clipBoxes
+        delete this.uniforms.clipPolygons
+        delete this.uniforms.clipPolygonVCount
+        delete this.uniforms.clipPolygonVP
+        delete this.uniforms.clipBoxCount
+        
+        //注意:这里修改了uniforms后,还需要在PotreeRender中手动传递到shader, like: gl.uniformMatrix4fv(....
         Object.assign(this.uniforms,{
             resolution:    { type: 'v2',  value: new THREE.Vector2() },
             maxSize:			{ type: "f", value: maxSize },
             gradient:			{ type: "t", value: this.gradientTexture },
+            
+            clipBoxes_in:  { type: "Matrix4fv", value: [] },
+            clipBoxes_out:  { type: "Matrix4fv", value: [] },
+            clipBoxBig_in:  { type: "Matrix4fv", value: [] },
 			progress: {
 				type: "f",
 				value: 0
@@ -74,7 +85,10 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
             },   
         })
         
-         
+        delete this.clipBoxes;
+        
+        this.clipBoxes_in = [];
+        this.clipBoxes_out = [];
 
 		 
 		this.updateShaderSource();
@@ -292,6 +306,42 @@ export class ExtendPointCloudMaterial extends PointCloudMaterial {
 	// 	this.copyFrom(from);
 	// }
     
-    
+	setClipBoxes (bigClipInBox, clipBoxes_in,clipBoxes_out) {
+		if (!clipBoxes_in || !clipBoxes_out) {
+			return;
+		}
+
+		/* let doUpdate = (this.clipBoxes_in.length !== clipBoxes_in.length) && (clipBoxes_out.length === 0 || this.clipBoxes_out.length === 0);
+		//this.clipBoxes = clipBoxes; 
+		if (doUpdate) {
+			this.updateShaderSource();
+		}  //好像是实时更新的
+         */
+        
+        this.bigClipInBox = bigClipInBox
+        this.clipBoxes_in = clipBoxes_in
+        this.clipBoxes_out = clipBoxes_out
+		this.uniforms.clipBoxBig_in.value = bigClipInBox && bigClipInBox.inverse
+		this.uniforms.clipBoxes_in.value = new Float32Array(this.clipBoxes_in.length * 16);
+		this.uniforms.clipBoxes_out.value = new Float32Array(this.clipBoxes_out.length * 16);
+        
+        
+		for (let i = 0; i < this.clipBoxes_in.length; i++) {
+			let box = clipBoxes_in[i];  
+			this.uniforms.clipBoxes_in.value.set(box.inverse.elements, 16 * i);
+		}
+		for (let i = 0; i < this.clipBoxes_out.length; i++) {
+			let box = clipBoxes_out[i];  
+			this.uniforms.clipBoxes_out.value.set(box.inverse.elements, 16 * i);
+		}
+
+
+
+		/* for (let i = 0; i < this.uniforms.clipBoxes.value.length; i++) {??
+			if (Number.isNaN(this.uniforms.clipBoxes.value[i])) {
+				this.uniforms.clipBoxes.value[i] = Infinity;
+			}
+		} */
+	}
 
 }

+ 4 - 4
src/materials/shaders/basicTextured.fs

@@ -1,9 +1,9 @@
 varying vec2 vUv;
-uniform float alpha;
-uniform sampler2D tDiffuse;
+uniform float opacity;
+uniform sampler2D map;
 
 
 void main() {
-  vec4 texColor = texture2D(tDiffuse, vUv);
-  gl_FragColor = vec4(texColor.rgb, texColor.a * alpha);
+  vec4 texColor = texture2D(map, vUv);
+  gl_FragColor = vec4(texColor.rgb, texColor.a * opacity);
 }

+ 96 - 0
src/materials/shaders/edl_new.fs

@@ -0,0 +1,96 @@
+
+#extension GL_EXT_frag_depth : enable
+
+// 
+// adapted from the EDL shader code from Christian Boucheny in cloud compare:
+// https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL
+//
+
+precision mediump float;
+precision mediump int;
+
+//uniform float screenWidth;
+//uniform float screenHeight;
+uniform vec2 resolution;
+
+
+uniform vec2 neighbours[NEIGHBOUR_COUNT];
+uniform float edlStrength;
+uniform float radius;
+uniform float opacity;
+
+//uniform float uNear;
+//uniform float uFar;
+
+uniform mat4 uProj;
+
+uniform sampler2D uEDLColor;
+uniform sampler2D uEDLDepth;
+
+varying vec2 vUv;
+
+uniform int useEDL;
+
+float response(float depth){
+	vec2 uvRadius = radius / resolution;             //vec2(screenWidth, screenHeight);
+	
+	float sum = 0.0;
+	
+	for(int i = 0; i < NEIGHBOUR_COUNT; i++){
+		vec2 uvNeighbor = vUv + uvRadius * neighbours[i];
+		//获取周围八个格子的值
+		float neighbourDepth = texture2D(uEDLColor, uvNeighbor).a;
+		neighbourDepth = (neighbourDepth == 1.0) ? 0.0 : neighbourDepth;
+
+		if(neighbourDepth != 0.0){
+			//if(depth == 0.0){
+			//	sum += 100.0;
+			//}else{
+				sum += max(0.0, depth - neighbourDepth);  //获取差值
+			//}
+		}
+	}
+	
+	return sum / float(NEIGHBOUR_COUNT);
+}
+
+void main(){
+	vec4 cEDL = texture2D(uEDLColor, vUv);
+	
+	float depth = cEDL.a;
+	depth = (depth == 1.0) ? 0.0 : depth;
+    
+    if(depth == 0.0){ //去掉这句就能在无点云像素的地方渲染outline,但会遮住其他mesh
+		discard;
+	}
+    
+    
+    if(useEDL == 1){
+        float res = response(depth);
+        
+        //if(depth == 0.0 && res == 0.0){   //test
+        //    discard;
+        //}
+         
+        float shade = exp(-res * 300.0 * edlStrength); //自然常数e为底的指数函数
+
+        gl_FragColor = vec4(cEDL.rgb * shade, opacity); 
+        
+        //const vec3 outlineColor = vec3(1.0,0.0,0.0);//test -outline
+        //gl_FragColor = vec4(mix(cEDL.rgb, outlineColor, -res), opacity );
+    }else{//加  不改颜色的情况 
+        gl_FragColor = vec4(cEDL.rgb, opacity);
+    } 
+    
+    
+    { // write regular hyperbolic depth values to depth buffer  修改深度
+        float dl = pow(2.0, depth);
+
+        vec4 dp = uProj * vec4(0.0, 0.0, -dl, 1.0);
+        float pz = dp.z / dp.w;
+        float fragDepth = (pz + 1.0) / 2.0;
+
+        gl_FragDepthEXT = fragDepth;
+    }
+	
+}

+ 19 - 0
src/materials/shaders/edl_new.vs

@@ -0,0 +1,19 @@
+
+precision mediump float;
+precision mediump int;
+
+attribute vec3 position;
+attribute vec2 uv;
+
+uniform mat4 projectionMatrix;
+uniform mat4 modelViewMatrix;
+
+varying vec2 vUv;
+
+void main() {
+	vUv = uv;
+	
+	vec4 mvPosition = modelViewMatrix * vec4(position,1.0);
+
+	gl_Position = projectionMatrix * mvPosition;
+}

+ 185 - 0
src/materials/shaders/pointcloud_new.fs

@@ -0,0 +1,185 @@
+
+#if defined paraboloid_point_shape
+	#extension GL_EXT_frag_depth : enable
+#endif
+#define PI 3.141592653589793
+
+
+
+precision highp float;
+precision highp int;
+
+/*
+#if defined(usePanoMap) 
+    
+    uniform samplerCube pano0Map;   //随便设置一个samplerCube去使用都会让点云消失
+    uniform samplerCube pano1Map;
+     
+    uniform float progress;
+    uniform float easeInOutRatio;
+
+     
+    uniform vec3 pano0Position;
+    uniform mat4 pano0Matrix; 
+    uniform vec3 pano1Position;
+    uniform mat4 pano1Matrix;
+    varying vec3 vWorldPosition0;
+    varying vec3 vWorldPosition1;
+
+#endif 
+*/
+
+
+
+//------------
+
+
+
+
+
+uniform mat4 viewMatrix;
+uniform mat4 uViewInv;
+uniform mat4 uProjInv;
+uniform vec3 cameraPosition;
+
+
+uniform mat4 projectionMatrix;
+//uniform float uOpacity;
+varying float vOpacity; //add
+
+uniform float blendHardness;
+uniform float blendDepthSupplement;
+uniform float fov;
+uniform float uSpacing;
+uniform float near;
+uniform float far;
+uniform float uPCIndex;
+uniform float uScreenWidth;
+uniform float uScreenHeight;
+
+varying vec3	vColor;
+varying float	vLogDepth;
+varying vec3	vViewPosition;
+varying float	vRadius;
+varying float 	vPointSize;
+varying vec3 	vPosition;
+
+
+float specularStrength = 1.0;
+
+
+vec2 getSamplerCoord( vec3 direction ) 
+{
+    direction = normalize(direction);
+    float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;
+    float ty=acos(direction.z)/PI;
+
+    return vec2(tx,ty);
+}  
+
+
+
+
+void main() {
+
+    vec3 color = vColor;  
+	 
+    
+    /*#if defined(usePanoMap) //加     经测试,即使全部写在fragment里也是无论pointsize多大都是一个点一个颜色,所以干脆写在vectex里
+    
+    
+        vec4 colorFromPano0=textureCube(pano0Map,vWorldPosition0.xyz);
+        vec4 colorFromPano1=textureCube(pano1Map,vWorldPosition1.xyz);
+        
+        color = mix(colorFromPano0,colorFromPano1,progress).xyz; 
+        
+        
+        //float easeInOutRatio = 0.0;  //缓冲,渐变点云到贴图的颜色 
+        if(progress < easeInOutRatio){
+            float easeProgress = (easeInOutRatio - progress) / easeInOutRatio; 
+            color = mix(color,vColor,easeProgress); 
+        }else if(progress > 1.0 - easeInOutRatio){ 
+            float easeProgress = (progress - (1.0 - easeInOutRatio) ) / easeInOutRatio; 
+            color = mix(color,vColor,easeProgress); 
+        }
+        
+        
+    #else 
+        color = vColor;
+    #endif*/
+   
+   
+    
+	float depth = gl_FragCoord.z;
+
+	#if defined(circle_point_shape) || defined(paraboloid_point_shape) 
+		float u = 2.0 * gl_PointCoord.x - 1.0;
+		float v = 2.0 * gl_PointCoord.y - 1.0;
+	#endif
+	
+	#if defined(circle_point_shape) 
+		float cc = u*u + v*v;
+		if(cc > 1.0){
+			discard;
+		}
+	#endif
+	
+
+ 
+
+	
+ 
+    #if defined color_type_indices    //pick point recognize
+		gl_FragColor = vec4(color, uPCIndex / 255.0); //uPCIndex : node Index
+	#else
+		gl_FragColor = vec4(color, vOpacity);
+	#endif
+    
+    
+    
+    
+    
+    
+    
+    
+
+	#if defined paraboloid_point_shape
+		float wi = 0.0 - ( u*u + v*v);
+		vec4 pos = vec4(vViewPosition, 1.0);
+		pos.z += wi * vRadius;
+		float linearDepth = -pos.z;
+		pos = projectionMatrix * pos;
+		pos = pos / pos.w;
+		float expDepth = pos.z;
+		depth = (pos.z + 1.0) / 2.0;
+		gl_FragDepthEXT = depth;
+		
+		#if defined(color_type_depth)
+			color.r = linearDepth;
+			color.g = expDepth;
+		#endif
+		
+		#if defined(use_edl)
+			gl_FragColor.a = log2(linearDepth);
+		#endif
+		
+	#else
+		#if defined(use_edl)
+			gl_FragColor.a = vLogDepth;
+		#endif
+	#endif
+
+	#if defined(weighted_splats)
+		float distance = 2.0 * length(gl_PointCoord.xy - 0.5);
+		float weight = max(0.0, 1.0 - distance);
+		weight = pow(weight, 1.5);
+
+		gl_FragColor.a = weight;
+		gl_FragColor.xyz = gl_FragColor.xyz * weight;
+	#endif
+
+	//gl_FragColor = vec4(0.0, 0.7, 0.0, 1.0);
+	
+}
+
+

File diff suppressed because it is too large
+ 1119 - 0
src/materials/shaders/pointcloud_new.vs


+ 10 - 10
src/navigation/FirstPersonControlsNew.js

@@ -120,7 +120,7 @@ export class FirstPersonControls extends THREE.EventDispatcher {
                     if(this.rotateStartInfo.rotCenter2d.z>1)distance *= -1 //在背面  
                       
                     //按照orbitControl的方式旋转:
-                    let rotationSpeed = 1//2.5;   
+                    let rotationSpeed = 2;   
                     this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed;
                     this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed; 
 
@@ -361,7 +361,7 @@ export class FirstPersonControls extends THREE.EventDispatcher {
                     }else if (e.delta < 0) {
                         ratio = 0.9 
                     } else if (e.delta > 0) {
-                        ratio = 1.1
+                        ratio = 1.12    //增加的需要比减少的多一些,否则缩放相同次数下,r0 * ([1+s)(1-s)]^n = r0 * (1-s^2)^n < r0
                     } 
                 }else{
                     ratio = e.scale //触屏缩放
@@ -451,23 +451,23 @@ export class FirstPersonControls extends THREE.EventDispatcher {
         }
         let prepareRotate = (e)=>{ 
             this.pointerDragStart = e.pointer.clone()
-            if(e.viewer.name == 'mapViewer' )return
+            if(e.viewer.name == 'mapViewer' )return 
             let intersect = e.intersect || e.dragViewport.lastIntersect
-            //在数据集外部时绕中心点旋转,在数据集内部时绕intersect点旋转 或者 原地旋转镜头
-            let rotAroundPoint = Potree.settings.rotAroundPoint && (viewer.atDatasets.length == 0 || intersect) && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[17]
+            //在数据集外部时绕中心点旋转,在数据集内部时绕intersect点旋转(其他数据集的点也可以) 或者 原地旋转镜头
+            let rotAroundPoint = Potree.settings.rotAroundPoint && e.dragViewport.camera.type != 'OrthographicCamera' &&  (viewer.atDatasets.length == 0 || intersect) && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[32]
             this.rotateStartInfo = {  
                 rotAroundPoint, //定点旋转 
                 /* intersectPos : e.intersect && e.intersect.location,
                 pointer :  e.intersect && e.intersect.location.clone().project(e.dragViewport.camera) //intersect点在屏幕中的位置 */
             }
             if(rotAroundPoint){
-                let pivotType = viewer.atDatasets.length > 0 ? 'intersect' : 'boundCenter'  
-                this.rotateStartInfo.rotCenter = pivotType == 'intersect' ? intersect.location :  viewer.bound.center
+                let pivotType = viewer.atDatasets.length > 0 ? 'intersect' :  viewer.inputHandler.selection.length ? 'selection' : 'boundCenter'  
+                this.rotateStartInfo.rotCenter = pivotType == 'intersect' ? intersect.location : pivotType == 'selection' ? viewer.inputHandler.selection[0].position : viewer.bound.center
                 this.rotateStartInfo.rotCenter2d = this.rotateStartInfo.rotCenter.clone().project(e.dragViewport.camera) //点在屏幕中的位置
-                console.log('pivotType',pivotType,  this.rotateStartInfo.rotCenter2d)
+                //console.log('pivotType',pivotType,  this.rotateStartInfo.rotCenter2d)
             }
             
-            
+            //缺点:多数据集绕中心点转很难操作,感觉可以也改为绕lastIntersect
             
             //console.log('prepareRotate' )
         }
@@ -549,7 +549,7 @@ export class FirstPersonControls extends THREE.EventDispatcher {
                 let pano = viewer.images360.findNearestPano()
                 if(!pano)return
                 let dis = pano.position.distanceTo(position)
-                let minSpeed = 0.07, minDis = 2, multiplier = 0.005;
+                let minSpeed = 0.05, minDis = 3, multiplier = 0.005;
                 let speed = dis <= minDis ? minSpeed : minSpeed + (dis-minDis) * multiplier
                 //console.log('dis', dis, 'speed', speed,  pano.id )
                      

+ 42 - 34
src/navigation/InputHandlerNew.js

@@ -596,7 +596,7 @@ export class InputHandler extends THREE.EventDispatcher {
             // check for a click  
             
             if(pressDistance < Potree.config.clickMaxDragDis && pressTime<Potree.config.clickMaxPressTime && !e.unableClick){
-                let clickElement
+                let clickElement, consumed = false;
                 if(this.hoveredElements){
                     clickElement = this.hoveredElements.find(e=>e.object._listeners['click']) 
                     if(clickElement){
@@ -609,26 +609,59 @@ export class InputHandler extends THREE.EventDispatcher {
                                 pressDistance     
                             }
                         )); 
-                    }
-                    
-                    
+                    } 
                 }
                 
                 
-                let consumed = false;
+                let selectable
+                if(/* !consumed && */ !this.fixSelection){
+                    if (e.button === THREE.MOUSE.LEFT) {
+                        if (noMovement) {
+                            selectable = this.hoveredElements.find(el => el.object._listeners && el.object._listeners['select']);
+
+                            if (selectable) {
+                                selectable = selectable.object;
+
+                                /* if (this.isSelected(selectable)) {
+                                    this.selection.filter(e => e !== selectable).forEach(e => this.toggleSelection(e)); 
+                                } else {
+                                    this.deselectAll();
+                                    this.toggleSelection(selectable);
+                                } */
+                                 
+                                if (this.isSelected(selectable)) {
+                                    this.deselectAll();
+                                } else {
+                                    this.deselectAll();
+                                    this.toggleSelection(selectable);
+                                } 
+                                consumed = true //add
+                            } else {
+                                if(this.selection.length>0)consumed = true //add 取消选择后,阻断后续 
+                                this.deselectAll();
+                            }
+                        }
+                    } else if ((e.button === THREE.MOUSE.RIGHT) && noMovement) {
+                        this.deselectAll();
+                    }
+                }
+                 
+                 
                 let consume = () => { return consumed = true; };
                 let desc = this.getEventDesc(e,isTouch)
-                //if(!clickElement){
+                 
+                if(!consumed){ 
+                    
                     this.viewer.dispatchEvent($.extend(  
                         desc,
                         {
                             type: 'global_click',  
                             pressDistance,
-                            clickElement,
+                            clickElement:clickElement/*  || selectable */,
                             consume
                         }
                     )); 
-                //}
+                }
                 
                 
                 
@@ -664,31 +697,6 @@ export class InputHandler extends THREE.EventDispatcher {
 
         this.dragViewport = null
 
-		if(!consumed && !this.fixSelection){
-			if (e.button === THREE.MOUSE.LEFT) {
-				if (noMovement) {
-					let selectable = this.hoveredElements
-						.find(el => el.object._listeners && el.object._listeners['select']);
-
-					if (selectable) {
-						selectable = selectable.object;
-
-						if (this.isSelected(selectable)) {
-							this.selection
-								.filter(e => e !== selectable)
-								.forEach(e => this.toggleSelection(e));
-						} else {
-							this.deselectAll();
-							this.toggleSelection(selectable);
-						}
-					} else {
-						this.deselectAll();
-					}
-				}
-			} else if ((e.button === THREE.MOUSE.RIGHT) && noMovement) {
-				this.deselectAll();
-			}
-		}
         
         
     }
@@ -1305,7 +1313,7 @@ export class InputHandler extends THREE.EventDispatcher {
         if(camera.type == "OrthographicCamera"){//使无论多远,threshold区域都是一样宽的
             raycaster.params.Line.threshold = 20/camera.zoom  
         }else{
-            raycaster.params.Line.threshold = 0.2; 
+            raycaster.params.Line.threshold = 0.04; //相对长度 
         } 
         raycaster.params.Line2 = {threshold :20 } //拓宽的lineWidth
         

+ 1 - 1
src/utils/ClippingTool.js

@@ -4,7 +4,7 @@ import * as THREE from "../../libs/three.js/build/three.module.js";
 import {ClipVolume} from "./ClipVolume.js";
 import {PolygonClipVolume} from "./PolygonClipVolume.js";
 import { EventDispatcher } from "../EventDispatcher.js";
-
+//这个文件只是clipping polygon的,没用到
 export class ClippingTool extends EventDispatcher{
 
 	constructor(viewer){

+ 216 - 111
src/utils/TransformationToolNew.js

@@ -1,19 +1,20 @@
 
 import * as THREE from "../../libs/three.js/build/three.module.js";
 import {Utils} from "../utils.js";
-
-
+import History from "../custom/utils/History.js"
+//问题:如何转换到世界坐标?(缩放方向有bug。)
 
 //add-------------------------------------
-const OpaWhenNotSelect = 0.6
-const ScaleRatio = 8
+const OpaWhenNotSelect = 0.4
+const ScaleRatio = 5
 const OutlineColor = 0x666666
 //----------------------------------------
 const hideFocusHandles = true//add
 
 
-export class TransformationTool {
+export class TransformationTool extends THREE.EventDispatcher{
 	constructor(viewer) {
+        super()
 		this.viewer = viewer;
 
 		this.scene = new THREE.Scene();
@@ -25,17 +26,17 @@ export class TransformationTool {
 
 		this.viewer.inputHandler.registerInteractiveScene(this.scene);
 		this.viewer.inputHandler.addEventListener('selection_changed', (e) => {
-			for(let selected of this.selection){
+			/* for(let selected of this.selection){ //若不删除,应该会穿过选中的物体选到别的而不是取消选择
 				this.viewer.inputHandler.blacklist.delete(selected);
-			}
+			} */
 
 			this.selection = e.selection;
 
-			for(let selected of this.selection){
+			/* for(let selected of this.selection){
 				this.viewer.inputHandler.blacklist.add(selected);
-			}
+			} */
 
-		});
+		}); 
 
 
         this.viewer.addEventListener('global_touchstart',(e)=>{ //add
@@ -116,13 +117,59 @@ export class TransformationTool {
 		}
 		this.frame = new THREE.LineSegments(boxFrameGeometry, new THREE.LineBasicMaterial({color: 0xffff00}));
 		this.scene.add(this.frame);
+        //------------------add-----------------------
+        
+        
         viewer.setObjectLayers(this.scene, 'transformationTool' )
 		
         this.scene.traverse(e=>{
             e.pickDontCheckDis = true; //pick时不需要识别是否在点云之上
         })
         
-        
+        {
+            let exist = (object)=>{//是否没被删除(暂时不考虑换了parent) 
+                while(object.parent){
+                    object = object.parent
+                }
+                if(object instanceof THREE.Scene){
+                    return true
+                }
+            }
+            this.history = new History({ //也可以写到全局,但需要加个判断物品是否存在的函数
+                applyData: (data)=>{  
+                    if(exist(data.object)){//viewer.scene.volumes.includes(data.box) //或许还要识别是否matrixAuto
+                        data.matrix.decompose( data.object.position, data.object.quaternion, data.object.scale );
+                    }else{
+                        this.history.undo()//找不到就回退下一个。(直接写这?)
+                    }
+                } ,
+                getData:(data)=>{ 
+                    return data
+                } 
+            })
+            //this.prepareRecord = true
+            this.addEventListener('transformed', (e)=>{
+                /* if(this.prepareRecord){
+                    let object = viewer.transformationTool.selection[0]
+                    this.history.writeIn({object, matrix:e.matrixBefore.clone()})
+                    this.prepareRecord = false
+                } */
+                let object = viewer.transformationTool.selection[0]
+                this.history.beforeChange({object, matrix:e.matrixBefore.clone()},[object],[Potree.BoxVolume])
+            })
+            this.addEventListener('stopDrag', (e)=>{
+                //this.prepareRecord = true
+                let object = viewer.transformationTool.selection[0]
+                object && this.history.afterChange({object, matrix:object.matrix.clone()},[object])
+            })
+            viewer.inputHandler.addEventListener('keydown', (e)=>{
+                if(e.keyCode == 90 && e.event.ctrlKey){//Z
+                    this.history.undo()
+                }else if(e.keyCode == 89 && e.event.ctrlKey){//Y
+                    this.history.redo()
+                }
+            })
+        }
 	}
   
   
@@ -130,7 +177,7 @@ export class TransformationTool {
         let handels = this[mode + 'Handles']
         if(!handels)return
         for(let o in handels){
-            handels[o].node.visible = !!enable
+            viewer.updateVisible(handels[o].node,'modeForce',!!enable) 
         }  
         this.modesEnabled[mode] = !!enable
     }
@@ -148,6 +195,7 @@ export class TransformationTool {
 
 			let material = new THREE.MeshBasicMaterial({
 				color: handle.color,
+                side:THREE.DoubleSide,//xzw add
 				opacity: OpaWhenNotSelect,
 				transparent: true
 				});
@@ -161,10 +209,11 @@ export class TransformationTool {
 			let pickMaterial = new THREE.MeshNormalMaterial({
 				opacity: 0.2,
 				transparent: true,
+                side:THREE.DoubleSide,//xzw add for orthoCam, 缩小画面时因球体放大导致到相机背面去了而看不到球体正面
 				visible: this.showPickVolumes});
 
 			let sphere = new THREE.Mesh(sgSphere, material);
-			sphere.scale.set(2, 2, 2 );
+			sphere.scale.set(3, 3, 3 );
 			sphere.name = `${handleName}.handle`;
 			node.add(sphere);
 			
@@ -182,7 +231,7 @@ export class TransformationTool {
 
 			node.setOpacity = (target) => {
 				let opacity = {x: material.opacity};
-				let t = new TWEEN.Tween(opacity).to({x: target}, 100);
+				let t = new TWEEN.Tween(opacity).to({x: target},  0);  //xzw改 原100毫秒,因为太慢容易选错
 				t.onUpdate(() => {
 					sphere.visible = opacity.x > 0;
 					pickSphere.visible = opacity.x > 0;
@@ -286,7 +335,7 @@ export class TransformationTool {
 
 			node.setOpacity = (target) => {
 				let opacity = {x: material.opacity};
-				let t = new TWEEN.Tween(opacity).to({x: target}, 100);
+				let t = new TWEEN.Tween(opacity).to({x: target},  0);
 				t.onUpdate(() => {
 					pickSphere.visible = opacity.x > 0;
 					box.visible = opacity.x > 0;
@@ -381,7 +430,7 @@ export class TransformationTool {
 
 			node.setOpacity = (target) => {
 				let opacity = {x: material.opacity};
-				let t = new TWEEN.Tween(opacity).to({x: target}, 100);
+				let t = new TWEEN.Tween(opacity).to({x: target},  0);
 				t.onUpdate(() => {
 					box.visible = opacity.x > 0;
 					pickVolume.visible = opacity.x > 0;
@@ -449,7 +498,7 @@ export class TransformationTool {
 
 			node.setOpacity = (target) => {
 				let opacity = {x: material.opacity};
-				let t = new TWEEN.Tween(opacity).to({x: target}, 100);
+				let t = new TWEEN.Tween(opacity).to({x: target}, 0);
 				t.onUpdate(() => {
 					box.visible = opacity.x > 0;
 					pickVolume.visible = opacity.x > 0;
@@ -474,7 +523,7 @@ export class TransformationTool {
 	dragRotationHandle(e){
 		let drag = e.drag;
 		let handle = this.activeHandle;
-		let camera = this.viewer.scene.getActiveCamera();
+		let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
 
 		if(!handle){
 			return
@@ -505,7 +554,7 @@ export class TransformationTool {
 		}else{
 			handle = drag.handle;
 		}
-
+        if(!drag.dragPlane)return//xzw add 因有时候没有
 		this.dragging = true;
 
 		let pointer = this.viewer.inputHandler.pointer
@@ -528,7 +577,7 @@ export class TransformationTool {
 			if (Number.isNaN(angle)) {
 				return;
 			}
-
+            let matrixBefore = this.selection[0].matrix.clone()
 			let normal = new THREE.Vector3(...handle.alignment);
 			for (let selection of this.selection) {
 				selection.rotateOnAxis(normal, angle);
@@ -537,7 +586,7 @@ export class TransformationTool {
 					object: selection
 				});
 			}
-
+            this.dispatchEvent({type:'transformed', changeType: ['orientation'], matrixBefore })//add
 			drag.pivot = I;
 		}
 	}
@@ -545,13 +594,16 @@ export class TransformationTool {
 	dropRotationHandle(e){
 		this.dragging = false;
 		this.setActiveHandle(null);
+        this.dispatchEvent({type:'stopDrag', handle:'rotation'})//add
 	}
 
 	dragTranslationHandle(e){
 		let drag = e.drag;
 		let handle = this.activeHandle;
-		let camera = this.viewer.scene.getActiveCamera();
+		let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
 			
+            
+            
 		if(!drag.intersectionStart && handle){
 			drag.intersectionStart = drag.location;
 			drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3());
@@ -561,10 +613,16 @@ export class TransformationTool {
 			let end = new THREE.Vector3().addVectors(start, dir);
 			let line = new THREE.Line3(start.clone(), end.clone());
 			drag.line = line;
-
-			let camOnLine = line.closestPointToPoint(camera.position, false, new THREE.Vector3());
-			let normal = new THREE.Vector3().subVectors(camera.position, camOnLine);
-			let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart);
+            
+			
+			let normal  
+            if(camera.type == 'OrthographicCamera'){//xzw add
+                normal = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion)
+            }else{
+                let camOnLine = line.closestPointToPoint(camera.position, false, new THREE.Vector3());
+                normal = new THREE.Vector3().subVectors(camera.position, camOnLine);
+            }
+            let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart);
 			drag.dragPlane = plane;
 			drag.pivot = drag.intersectionStart;
 		}else{
@@ -573,7 +631,7 @@ export class TransformationTool {
 
 		this.dragging = true;
 
-		{
+		if(drag.dragPlane){//xzw add 因有时候没有 
 			let pointer = this.viewer.inputHandler.pointer
 			let domElement = this.viewer.renderer.domElement;
 			let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
@@ -583,7 +641,7 @@ export class TransformationTool {
 				let iOnLine = drag.line.closestPointToPoint(I, false, new THREE.Vector3());
 
 				let diff = new THREE.Vector3().subVectors(iOnLine, drag.pivot);
-
+                let matrixBefore = this.selection[0].matrix.clone()
 				for (let selection of this.selection) {
 					selection.position.add(diff);
 					selection.dispatchEvent({
@@ -591,7 +649,7 @@ export class TransformationTool {
 						object: selection
 					});
 				}
-
+                this.dispatchEvent({type:'transformed', changeType: ['position'], matrixBefore})//add
 				drag.pivot = drag.pivot.add(diff);
 			}
 		}
@@ -600,31 +658,37 @@ export class TransformationTool {
 	dropTranslationHandle(e){
 		this.dragging = false;
 		this.setActiveHandle(null);
+        this.dispatchEvent({type:'stopDrag', handle:'translation'})//add
 	}
 
 	dropScaleHandle(e){
 		this.dragging = false;
 		this.setActiveHandle(null);
+        this.dispatchEvent({type:'stopDrag', handle:'scale'})//add
 	}
 
 	dragScaleHandle(e){
 		let drag = e.drag;
 		let handle = this.activeHandle;
-		let camera = this.viewer.scene.getActiveCamera();
+		let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
 
 		if(!drag.intersectionStart){
 			drag.intersectionStart = drag.location;
 			drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3());
 			drag.handle = handle;
-
-			let start = drag.intersectionStart;
-			let dir = new THREE.Vector4(...handle.alignment, 0).applyMatrix4(this.scene.matrixWorld);
-			let end = new THREE.Vector3().addVectors(start, dir);
-			let line = new THREE.Line3(start.clone(), end.clone());
+            let start = drag.intersectionStart;
+            let dir = new THREE.Vector4(...handle.alignment, 0).applyMatrix4(this.scene.matrixWorld);
+            let end = new THREE.Vector3().addVectors(start, dir);
+            let line = new THREE.Line3(start.clone(), end.clone());
 			drag.line = line;
-
-			let camOnLine = line.closestPointToPoint(camera.position, false, new THREE.Vector3());
-			let normal = new THREE.Vector3().subVectors(camera.position, camOnLine);
+ 
+			let normal   
+            if(camera.type == 'OrthographicCamera'){//xzw add
+                normal = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion)
+            }else{ 
+                let camOnLine = line.closestPointToPoint(camera.position, false, new THREE.Vector3());
+                normal = new THREE.Vector3().subVectors(camera.position, camOnLine);
+            }
 			let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart);
 			drag.dragPlane = plane;
 			drag.pivot = drag.intersectionStart;
@@ -636,7 +700,7 @@ export class TransformationTool {
 
 		this.dragging = true;
 
-		{
+		if(drag.dragPlane){//xzw add 因有时候没有
 			let pointer = this.viewer.inputHandler.pointer
 			let domElement = this.viewer.renderer.domElement;
 			let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
@@ -661,7 +725,7 @@ export class TransformationTool {
 				let diffPosition = diff.clone().multiplyScalar(0.5);
    
  
- 
+                let matrixBefore = this.selection[0].matrix.clone()
 				for (let selection of this.selection) {
                     //xzw 改:否则不跟手
                     let diffScale_ = diffScale.clone().divide(selection.boundingBox.getSize(new THREE.Vector3))
@@ -681,6 +745,8 @@ export class TransformationTool {
 						type: "scale_changed",
 						object: selection
 					});
+                    
+                    this.dispatchEvent({type:'transformed', changeType: ['position','scale'], matrixBefore})//add
 				}
 
 				drag.pivot.copy(iOnLine);
@@ -788,83 +854,125 @@ export class TransformationTool {
 
 			let selected = this.selection[0];
 			let world = selected.matrixWorld;
-			let camera = this.viewer.scene.getActiveCamera();
+			let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera();
 			let domElement = this.viewer.renderer.domElement;
 			let pointer = this.viewer.inputHandler.pointer;
 
+            
+        
 			let center = selected.boundingBox.getCenter(new THREE.Vector3()).clone().applyMatrix4(selected.matrixWorld);
 
 			this.scene.scale.copy(selected.boundingBox.getSize(new THREE.Vector3()).multiply(selected.scale));
 			this.scene.position.copy(center);
-			this.scene.rotation.copy(selected.rotation);
+			this.scene.rotation.copy(selected.rotation); 
+            
+            //如果是世界坐标 (缩放方向有bug。)
+            /* 
+            let boundingBox = selected.boundingBox.clone().applyMatrix4(selected.matrixWorld);
+            let center = boundingBox.getCenter(new THREE.Vector3())
+            this.scene.position.copy(center);
+            this.scene.scale.copy(boundingBox.getSize(new THREE.Vector3()));
+             
+             */
+            
 
 			this.scene.updateMatrixWorld();
 
 			{ 
 				// adjust rotation handles
 				if(!this.dragging){
-					let tWorld = this.scene.matrixWorld;
-					let tObject = tWorld.clone().invert();
-					let camObjectPos = camera.getWorldPosition(new THREE.Vector3()).applyMatrix4(tObject);
-
-					let x = this.rotationHandles["rotation.x"].node.rotation;
-					let y = this.rotationHandles["rotation.y"].node.rotation;
-					let z = this.rotationHandles["rotation.z"].node.rotation;
-
-					x.order = "ZYX";
-					y.order = "ZYX";
-
-					let above = camObjectPos.z > 0;
-					let below = !above;
-					let PI_HALF = Math.PI / 2;
-
-					if(above){
-						if(camObjectPos.x > 0 && camObjectPos.y > 0){
-							x.x = 1 * PI_HALF;
-							y.y = 3 * PI_HALF;
-							z.z = 0 * PI_HALF;
-						}else if(camObjectPos.x < 0 && camObjectPos.y > 0){
-							x.x = 1 * PI_HALF;
-							y.y = 2 * PI_HALF;
-							z.z = 1 * PI_HALF;
-						}else if(camObjectPos.x < 0 && camObjectPos.y < 0){
-							x.x = 2 * PI_HALF;
-							y.y = 2 * PI_HALF;
-							z.z = 2 * PI_HALF;
-						}else if(camObjectPos.x > 0 && camObjectPos.y < 0){
-							x.x = 2 * PI_HALF;
-							y.y = 3 * PI_HALF;
-							z.z = 3 * PI_HALF;
-						} 
-					}else if(below){
-						if(camObjectPos.x > 0 && camObjectPos.y > 0){
-							x.x = 0 * PI_HALF;
-							y.y = 0 * PI_HALF;
-							z.z = 0 * PI_HALF;
-						}else if(camObjectPos.x < 0 && camObjectPos.y > 0){
-							x.x = 0 * PI_HALF;
-							y.y = 1 * PI_HALF;
-							z.z = 1 * PI_HALF;
-						}else if(camObjectPos.x < 0 && camObjectPos.y < 0){
-							x.x = 3 * PI_HALF;
-							y.y = 1 * PI_HALF;
-							z.z = 2 * PI_HALF;
-						}else if(camObjectPos.x > 0 && camObjectPos.y < 0){
-							x.x = 3 * PI_HALF;
-							y.y = 0 * PI_HALF;
-							z.z = 3 * PI_HALF;
-						} 
-                        
-                    }  
-                    
+                    if(this.modesEnabled.rotation){
+                        let tWorld = this.scene.matrixWorld;
+                        let tObject = tWorld.clone().invert();
+                        let camObjectPos = camera.getWorldPosition(new THREE.Vector3()).applyMatrix4(tObject);
+
+                        let x = this.rotationHandles["rotation.x"].node.rotation;
+                        let y = this.rotationHandles["rotation.y"].node.rotation;
+                        let z = this.rotationHandles["rotation.z"].node.rotation;
+
+                        x.order = "ZYX";
+                        y.order = "ZYX";
+
+                        let above = camObjectPos.z > 0;
+                        let below = !above;
+                        let PI_HALF = Math.PI / 2;
+
+                        if(above){
+                            if(camObjectPos.x > 0 && camObjectPos.y > 0){
+                                x.x = 1 * PI_HALF;
+                                y.y = 3 * PI_HALF;
+                                z.z = 0 * PI_HALF;
+                            }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
+                                x.x = 1 * PI_HALF;
+                                y.y = 2 * PI_HALF;
+                                z.z = 1 * PI_HALF;
+                            }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
+                                x.x = 2 * PI_HALF;
+                                y.y = 2 * PI_HALF;
+                                z.z = 2 * PI_HALF;
+                            }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
+                                x.x = 2 * PI_HALF;
+                                y.y = 3 * PI_HALF;
+                                z.z = 3 * PI_HALF;
+                            } 
+                        }else if(below){
+                            if(camObjectPos.x > 0 && camObjectPos.y > 0){
+                                x.x = 0 * PI_HALF;
+                                y.y = 0 * PI_HALF;
+                                z.z = 0 * PI_HALF;
+                            }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
+                                x.x = 0 * PI_HALF;
+                                y.y = 1 * PI_HALF;
+                                z.z = 1 * PI_HALF;
+                            }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
+                                x.x = 3 * PI_HALF;
+                                y.y = 1 * PI_HALF;
+                                z.z = 2 * PI_HALF;
+                            }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
+                                x.x = 3 * PI_HALF;
+                                y.y = 0 * PI_HALF;
+                                z.z = 3 * PI_HALF;
+                            } 
+                            
+                        }  
+                    }
 				}
 
 
                 // adjust scale of components
 				for(let handleName of Object.keys(this.handles)){
 					let handle = this.handles[handleName];
-					let node = handle.node;
-
+					let node = handle.node; 
+                    
+                    
+                    //xzw add:---- -当该轴正对相机时隐藏。(主要针对ortho类型camera。 )
+                    if(!viewer.getObjVisiByReason(node,'modeForce')  )continue;   
+                    if(!handleName.includes('rotation') || camera.type == 'OrthographicCamera'){//旋转的话正常都应该显示
+                        let alignment = handle.alignment;
+                        let normal
+                        let dir = new THREE.Vector3(...alignment).applyQuaternion(this.scene.quaternion)
+                        if(camera.type == 'OrthographicCamera'){ 
+                            normal = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion)
+                        }else{ 
+                            normal = new THREE.Vector3().subVectors(center, camera.position).normalize()
+                        } 
+                        let ifOnLine
+                        
+                        if(handleName.includes('rotation')){ //旋转时 旋转轴和视线垂直时隐藏
+                            ifOnLine = Math.abs(dir.dot(normal)) < 0.01
+                        }else{
+                            ifOnLine = Math.abs(dir.dot(normal)) > 0.995
+                        }
+                        
+                        viewer.updateVisible(node, 'faceToCamHide', !ifOnLine)
+                    }else{
+                        viewer.updateVisible(node, 'faceToCamHide', true)
+                    }      
+                     
+                    if(!node.visible)continue;   
+                    
+                    //------------------------------------------------------------------------
+                    
 					let handlePos = node.getWorldPosition(new THREE.Vector3());
 					let distance = handlePos.distanceTo(camera.position);
 					let pr = Utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight);
@@ -883,15 +991,20 @@ export class TransformationTool {
 					scale.z = Math.abs(scale.z);
 
 					node.scale.copy(scale);
+                    
+                    
+                    
+                 
+                    
 				}
 
 
 
-				{
-					let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
+				if(!this.dragging){ //xzw 添加dragging条件
+					let ray = Utils.mouseToRay(pointer, camera/* , domElement.clientWidth, domElement.clientHeight */);
 					let raycaster = new THREE.Raycaster(ray.origin, ray.direction);
 					raycaster.layers.enableAll()//add
-                    
+                          
                     
                     let pickVolumes = this.pickVolumes.filter(v=>{
                         let mode = v.handle.split('.')[0]; 
@@ -903,22 +1016,14 @@ export class TransformationTool {
 					if(intersects.length > 0){
 						let I = intersects[0];
 						let handleName = I.object.handle;
-                        console.log(handleName)
+                        //console.log(handleName)
 						this.setActiveHandle(this.handles[handleName]);
 					}else{
 						this.setActiveHandle(null);
 					}
 				}
 
-				// 
-				for(let handleName of Object.keys(this.scaleHandles)){
-					let handle = this.handles[handleName];
-					let node = handle.node;
-					let alignment = handle.alignment;
-
-					
-
-				}
+				 
 			}
 
 		}else{

+ 365 - 0
src/utils/VolumeNew.js

@@ -0,0 +1,365 @@
+
+import * as THREE from "../../libs/three.js/build/three.module.js";
+import {TextSprite} from "../TextSprite.js";
+import DepthBasicMaterial from "../custom/materials/DepthBasicMaterial.js";
+
+const boxOpacity = {
+    hovered : 0.1,
+    selected : 0.3
+}
+const colors = {
+    2: 0x00ff80, //可见
+    3: 0xff3158, //不可见
+    4: 0xffffff, //
+}
+
+export class Volume extends THREE.Object3D {
+	constructor (args = {}) {
+		super();
+
+		if(this.constructor.name === "Volume"){
+			console.warn("Can't create object of class Volume directly. Use classes BoxVolume or SphereVolume instead.");
+		}
+
+	 
+		this._clip = args.clip || false;
+		this._visible = true;
+		//this.showVolumeLabel = true;
+		this._modifiable = args.modifiable || true;
+
+	 
+		{ // event listeners
+			this.addEventListener('select', e => {
+                //console.log('select') 
+                this.selected = true
+                this.update() 
+            });
+			this.addEventListener('deselect', e => {
+                //console.log('deselect') 
+                this.selected = false 
+                this.update()
+            });
+            
+            this.addEventListener('mouseover', e => {
+                this.hovered = true
+                this.update()
+            })
+			this.addEventListener('mouseleave', e => { 
+                this.hovered = false
+                this.update()
+            });
+            
+            
+		}
+
+	}
+
+	get visible(){
+		return this._visible;
+	}
+
+	set visible(value){
+		if(this._visible !== value){
+			this._visible = value;
+
+			this.dispatchEvent({type: "visibility_changed", object: this});
+		}
+	}
+
+	getVolume () {
+		console.warn("override this in subclass");
+	}
+
+	update () {
+		
+	};
+
+	raycast (raycaster, intersects) {
+
+	}
+
+	get clip () {
+		return this._clip;
+	}
+
+	set clip (value) {
+
+		if(this._clip !== value){
+			this._clip = value;
+
+			this.update();
+
+			this.dispatchEvent({
+				type: "clip_changed",
+				object: this
+			});
+		}
+		
+	}
+
+	get modifieable () {
+		return this._modifiable;
+	}
+
+	set modifieable (value) {
+		this._modifiable = value;
+
+		this.update();
+	}
+};
+
+
+export class BoxVolume extends Volume{
+
+	constructor(args = {}){
+		super(args);
+
+		this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
+		this.name = 'box_' + this.constructor.counter;
+        this.clipTask = args.clipTask || Potree.ClipTask.SHOW_INSIDE //add
+        this.showBox = true
+        
+		let boxGeometry = new THREE.BoxGeometry(1, 1, 1);
+		boxGeometry.computeBoundingBox();
+        
+		let boxFrameGeometry = new THREE.Geometry();
+		{
+			let Vector3 = THREE.Vector3;
+
+			boxFrameGeometry.vertices.push(
+
+				// bottom
+				new Vector3(-0.5, -0.5, 0.5),
+				new Vector3(0.5, -0.5, 0.5),
+				new Vector3(0.5, -0.5, 0.5),
+				new Vector3(0.5, -0.5, -0.5),
+				new Vector3(0.5, -0.5, -0.5),
+				new Vector3(-0.5, -0.5, -0.5),
+				new Vector3(-0.5, -0.5, -0.5),
+				new Vector3(-0.5, -0.5, 0.5),
+				// top
+				new Vector3(-0.5, 0.5, 0.5),
+				new Vector3(0.5, 0.5, 0.5),
+				new Vector3(0.5, 0.5, 0.5),
+				new Vector3(0.5, 0.5, -0.5),
+				new Vector3(0.5, 0.5, -0.5),
+				new Vector3(-0.5, 0.5, -0.5),
+				new Vector3(-0.5, 0.5, -0.5),
+				new Vector3(-0.5, 0.5, 0.5),
+				// sides
+				new Vector3(-0.5, -0.5, 0.5),
+				new Vector3(-0.5, 0.5, 0.5),
+				new Vector3(0.5, -0.5, 0.5),
+				new Vector3(0.5, 0.5, 0.5),
+				new Vector3(0.5, -0.5, -0.5),
+				new Vector3(0.5, 0.5, -0.5),
+				new Vector3(-0.5, -0.5, -0.5),
+				new Vector3(-0.5, 0.5, -0.5),
+
+			);
+
+		}
+
+		this.material = new DepthBasicMaterial({
+			color: colors[this.clipTask],
+            side:THREE.DoubleSide,
+			transparent: true,
+			opacity: boxOpacity.hovered,
+			depthTest: true,
+			depthWrite: false,
+            useDepth:true,
+            clipDistance : 2,//消失距离
+            occlusionDistance: 0.1,//变为backColor距离 
+            maxClipFactor: 0.9,
+        });
+		this.box = new THREE.Mesh(boxGeometry, this.material);
+		this.box.geometry.computeBoundingBox();
+		this.boundingBox = this.box.geometry.boundingBox;
+		this.add(this.box);
+
+		this.frame = new THREE.LineSegments(boxFrameGeometry, new THREE.LineBasicMaterial({color: colors[this.clipTask]/* 0xff2050 */}));
+		// this.frame.mode = THREE.Lines;
+		this.add(this.frame);
+
+		this.update();  
+	}
+
+
+    
+
+
+	update(){
+		this.boundingBox = this.box.geometry.boundingBox;
+		this.boundingSphere = this.boundingBox.getBoundingSphere(new THREE.Sphere());
+
+		/* if (this._clip) {
+			this.box.visible = false;
+			//this.label.visible = false;
+		} else {
+			this.box.visible = true;
+			//this.label.visible = this.showVolumeLabel;
+		} */
+        
+        viewer.updateVisible(this.box, 'selected', (this.selected || this.hovered) && this.showBox) 
+        
+        this.box.material.opacity = this.selected ? boxOpacity.selected : boxOpacity.hovered
+        
+        this.box.material.color.set(colors[this.clipTask])
+        this.frame.material.color.set(colors[this.clipTask])
+	}
+
+	raycast (raycaster, intersects) {
+		let is = [];
+		this.box.raycast(raycaster, is);
+
+		if (is.length > 0) {
+			let I = is[0];
+			intersects.push({
+				distance: I.distance,
+				object: this,
+				point: I.point.clone()
+			});
+		}
+	}
+
+	getVolume(){
+		return Math.abs(this.scale.x * this.scale.y * this.scale.z);
+	}
+
+};
+
+export class SphereVolume extends Volume{
+
+	constructor(args = {}){
+		super(args);
+
+		this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
+		this.name = 'sphere_' + this.constructor.counter;
+
+		let sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
+		sphereGeometry.computeBoundingBox();
+
+		this.material = new THREE.MeshBasicMaterial({
+			color: 0x00ff00,
+			transparent: true,
+			opacity: 0.3,
+			depthTest: true,
+			depthWrite: false});
+		this.sphere = new THREE.Mesh(sphereGeometry, this.material);
+		this.sphere.visible = false;
+		this.sphere.geometry.computeBoundingBox();
+		this.boundingBox = this.sphere.geometry.boundingBox;
+		this.add(this.sphere);
+
+		this.label.visible = false;
+
+
+		let frameGeometry = new THREE.Geometry();
+		{
+			let steps = 64;
+			let uSegments = 8;
+			let vSegments = 5;
+			let r = 1;
+
+			for(let uSegment = 0; uSegment < uSegments; uSegment++){
+
+				let alpha = (uSegment / uSegments) * Math.PI * 2;
+				let dirx = Math.cos(alpha);
+				let diry = Math.sin(alpha);
+
+				for(let i = 0; i <= steps; i++){
+					let v = (i / steps) * Math.PI * 2;
+					let vNext = v + 2 * Math.PI / steps;
+
+					let height = Math.sin(v);
+					let xyAmount = Math.cos(v);
+
+					let heightNext = Math.sin(vNext);
+					let xyAmountNext = Math.cos(vNext);
+
+					let vertex = new THREE.Vector3(dirx * xyAmount, diry * xyAmount, height);
+					frameGeometry.vertices.push(vertex);
+
+					let vertexNext = new THREE.Vector3(dirx * xyAmountNext, diry * xyAmountNext, heightNext);
+					frameGeometry.vertices.push(vertexNext);
+				}
+			}
+
+			// creates rings at poles, just because it's easier to implement
+			for(let vSegment = 0; vSegment <= vSegments + 1; vSegment++){
+
+				//let height = (vSegment / (vSegments + 1)) * 2 - 1; // -1 to 1
+				let uh = (vSegment / (vSegments + 1)); // -1 to 1
+				uh = (1 - uh) * (-Math.PI / 2) + uh *(Math.PI / 2);
+				let height = Math.sin(uh);
+
+				console.log(uh, height);
+
+				for(let i = 0; i <= steps; i++){
+					let u = (i / steps) * Math.PI * 2;
+					let uNext = u + 2 * Math.PI / steps;
+
+					let dirx = Math.cos(u);
+					let diry = Math.sin(u);
+
+					let dirxNext = Math.cos(uNext);
+					let diryNext = Math.sin(uNext);
+
+					let xyAmount = Math.sqrt(1 - height * height);
+
+					let vertex = new THREE.Vector3(dirx * xyAmount, diry * xyAmount, height);
+					frameGeometry.vertices.push(vertex);
+
+					let vertexNext = new THREE.Vector3(dirxNext * xyAmount, diryNext * xyAmount, height);
+					frameGeometry.vertices.push(vertexNext);
+				}
+			}
+		}
+
+		this.frame = new THREE.LineSegments(frameGeometry, new THREE.LineBasicMaterial({color: 0x000000}));
+		this.add(this.frame);
+
+		let frameMaterial = new THREE.MeshBasicMaterial({wireframe: true, color: 0x000000});
+		this.frame = new THREE.Mesh(sphereGeometry, frameMaterial);
+		//this.add(this.frame);
+
+		//this.frame = new THREE.LineSegments(boxFrameGeometry, new THREE.LineBasicMaterial({color: 0x000000}));
+		// this.frame.mode = THREE.Lines;
+		//this.add(this.frame);
+
+		this.update();
+	}
+
+	update(){
+		this.boundingBox = this.sphere.geometry.boundingBox;
+		this.boundingSphere = this.boundingBox.getBoundingSphere(new THREE.Sphere());
+
+		//if (this._clip) {
+		//	this.sphere.visible = false;
+		//	this.label.visible = false;
+		//} else {
+		//	this.sphere.visible = true;
+		//	this.label.visible = this.showVolumeLabel;
+		//}
+	}
+
+	raycast (raycaster, intersects) {
+		let is = [];
+		this.sphere.raycast(raycaster, is);
+
+		if (is.length > 0) {
+			let I = is[0];
+			intersects.push({
+				distance: I.distance,
+				object: this,
+				point: I.point.clone()
+			});
+		}
+	}
+	
+	// see https://en.wikipedia.org/wiki/Ellipsoid#Volume
+	getVolume(){
+		return (4 / 3) * Math.PI * this.scale.x * this.scale.y * this.scale.z;
+	}
+
+};

+ 3 - 1
src/viewer/ExtendScene.js

@@ -114,7 +114,9 @@ class ExtendScene extends Scene{
 		}
 	};
 
-	 
+    getActiveCamera() { 
+		return viewer.mainViewport.camera
+	}
 	initialize(){//不用旧的 因为还没创建完变量
 	}
 

File diff suppressed because it is too large
+ 1519 - 336
src/viewer/sidebar.html


+ 406 - 0
src/viewer/sidebar1.html

@@ -0,0 +1,406 @@
+
+<div id="sidebar_root" class="">
+	<div id="sidebar_header">
+		<span id="potree_branding" class="potree_sidebar_brand">
+			<a href="http://potree.org" target="_blank">potree.org</a> 
+			<span style="margin: 0px 3px; color: #9AA1A4"> - </span> 
+			<a href="https://github.com/potree/potree" target="_blank">github</a>
+			<span style="margin: 0px 3px; color: #9AA1A4"> - </span> 
+			<a href="https://twitter.com/m_schuetz" target="_blank">twitter</a>
+			<span style="flex-grow: 1"></span>
+			<span id="potree_version_number" style="color: #9AA1A4; font-size: 80%; font-weight: 100"></span>
+		</span>
+		<div id="potree_languages" style="font-family:arial;"></div>
+	</div>
+	<div> 
+
+	<div id="potree_menu" class="accordion">
+
+		
+		<!-- APPEARANCE -->
+		<h3 id="menu_appearance"><span data-i18n="tb.rendering_opt"></span></h3>
+		<div>
+			<ul class="pv-menu-list">
+				<li><span data-i18n="appearance.nb_max_pts"></span>: <span id="lblPointBudget"></span> <div id="sldPointBudget"></div></li>
+				<li><span data-i18n="appearance.field_view"></span>: <span id="lblFOV"></span><div id="sldFOV"></div></li>
+
+				<div class="divider"><span>Eye-Dome-Lighting</span></div>
+
+				<li><label><input type="checkbox" id="chkEDLEnabled"/><span data-i18n="appearance.edl_enable"></span></label></li>
+				<li><span data-i18n="appearance.edl_radius"></span>: <span id="lblEDLRadius"></span><div id="sldEDLRadius"></div></li>
+				<li><span data-i18n="appearance.edl_strength"></span>: <span id="lblEDLStrength"></span><div id="sldEDLStrength"></div></li>
+				<li><span data-i18n="appearance.edl_opacity"></span>: <span id="lblEDLOpacity"></span><div id="sldEDLOpacity"></div></li>
+				
+				<div class="divider"><span>Background</span></div>
+
+				<li>
+					<selectgroup id="background_options">
+						<option id="background_options_skybox" value="skybox">Skybox</option>
+						<option id="background_options_gradient" value="gradient">Gradient</option>
+						<option id="background_options_black" value="black">Black</option>
+						<option id="background_options_white" value="white">White</option>
+						<option id="background_options_none" value="null">None</option>
+					</selectgroup>
+				</li>
+
+				<div class="divider"><span>Other</span></div>
+
+				<li>
+					<selectgroup id="splat_quality_options">
+						<option id="splat_quality_options_standard" value="standard">Standard</option>
+						<option id="splat_quality_options_hq" value="hq">High Quality</option>
+					</selectgroup>
+				</li>
+				
+				<li><span data-i18n="appearance.min_node_size"></span>: <span id="lblMinNodeSize"></span><div id="sldMinNodeSize"></div></li>
+				<li><label><input id="show_bounding_box" type="checkbox" /><span data-i18n="appearance.box"></span></label></li>
+				<li><label><input id="set_freeze" type="checkbox" /><span data-i18n="appearance.freeze"></span></label></li>
+			</ul>
+			
+		</div>
+		
+		<!-- TOOLS -->
+		<h3 id="menu_tools"><span data-i18n="tb.tools_opt"></span></h3>
+		<div class="pv-menu-list">
+
+			<div class="divider"><span>Measurement</span></div>
+
+			<li id="tools"></li>
+
+			<li>
+				<selectgroup id="measurement_options_show">
+					<option id="measurement_options_show_yes" value="SHOW">Show</option>
+					<option id="measurement_options_show_no" value="HIDE">Hide</option>
+				</selectgroup>
+			</li>
+
+			<div class="divider"><span>Clipping</span></div>
+
+			<li id="clipping_tools"></li>
+
+			<li>
+				<selectgroup id="cliptask_options">
+					<option id="cliptask_options_none" value="NONE">None</option>
+					<option id="cliptask_options_highlight" value="HIGHLIGHT">Highlight</option>
+					<option id="cliptask_options_show_inside" value="SHOW_INSIDE">Inside</option>
+					<option id="cliptask_options_show_outside" value="SHOW_OUTSIDE">Outside</option>
+				</selectgroup>
+			</li>
+
+			<li>
+				<selectgroup id="clipmethod_options">
+					<option id="clipmethod_options_any" value="INSIDE_ANY">Inside Any</option>
+					<option id="clipmethod_options_all" value="INSIDE_ALL">Inside All</option>
+				</selectgroup>
+			</li>
+			
+			<div class="divider"><span>Navigation</span></div>
+
+			<li id="navigation"></li>
+
+			<li><span data-i18n="appearance.move_speed"></span>: <span id="lblMoveSpeed"></span><div id="sldMoveSpeed"></div></li>
+		</div>
+
+		<!-- SCENE -->
+		<h3 id="menu_scene"><span data-i18n="tb.scene_opt"></span></h3>
+		<div class="pv-menu-list">
+
+			<div id="scene_export"></div>
+
+			<div class="divider"><span>Objects</span></div>
+
+			<div id="scene_objects"></div>
+
+			<div class="divider"><span>Properties</span></div>
+
+			<div id="scene_object_properties"></div>
+		</div>
+
+		<!-- Classification -->
+		<h3 id="menu_filters"><span data-i18n="tb.filters_opt"></span></h3>
+		<div>
+
+			<div class="divider"><span>Classification</span></div>
+
+			<ul id="classificationList" class="pv-menu-list"></ul>
+
+			<div class="divider"><span>Returns</span></div>
+
+			<div id="return_filter_panel">
+				<ul class="pv-menu-list">
+					
+					<li><span data-i18n="filters.return_number"></span>: <span id="lblReturnNumber"></span> <div id="sldReturnNumber"></div></li>
+					<li><span data-i18n="filters.number_of_returns"></span>: <span id="lblNumberOfReturns"></span> <div id="sldNumberOfReturns"></div></li>
+
+				</ul>
+			</div>
+
+			<div class="divider"><span>Point Source ID</span></div>
+
+			<div>
+				<ul class="pv-menu-list" id="pointsourceid_filter_panel">
+
+				</ul>
+			</div>
+
+			<div class="divider"><span>GPS Time</span></div>
+
+			<div id="gpstime_filter_panel">
+
+				<ul class="pv-menu-list">
+					<div id="gpstime_multilevel_range_container">
+
+						<li>
+							<span style="display: flex">
+								<span>Time: </span>
+								<input id="txtGpsTime" type="text" style="margin: auto 10px"/>
+								<input id="btnFindGpsTime" type="button" value="find" />
+							</span>
+						</li>
+
+					</div>
+
+				</ul>
+
+			</div>
+		</div>
+		
+		<!-- ABOUT -->
+		<h3 id="menu_about"><span data-i18n="tb.about_opt"></span></h3>
+		<div>
+			<ul class="pv-menu-list">
+				<li><a href="http://potree.org" target="_blank">Potree</a> is a viewer for large point cloud / LIDAR data sets, developed at the Vienna University of Technology. 
+				<a href="https://github.com/potree/potree" target="_blank">(github)</a>
+				</li>
+				<li><b>Author: </b><a href="mailto:mschuetz@potree.org">Markus Sch&uuml;tz</a></li>
+				<li><b>License: </b><a target="_blank" href="https://github.com/potree/potree/blob/develop/LICENSE/">FreeBSD (2-clause BSD)</a></li>
+				<li><b>Dependency Licenses:</b> <a target="_blank" href="https://github.com/potree/potree/tree/develop/libs/">See github</a></li>
+
+<li>
+<b>Funding:</b>
+Potree is funded by a combination of research projects, companies, institutions and individuals. If you're making good use of Potree, please consider funding its future development <a href="https://github.com/sponsors/potree">via Github Sponsors</a> or by directly inquiring <a href="mailto:mschuetz@potree.org">via e-mail</a>.
+
+<br>
+<br>
+Research projects who's funding contributes to Potree:
+
+<table>
+	<tr>
+		<th>Project Name</th>
+		<th>Funding Agency</th>
+	</tr>
+	<tr>
+		<td><a href="https://projekte.ffg.at/projekt/3851914">LargeClouds2BIM</a></td>
+		<td><a href="https://www.ffg.at/">FFG</a></td>
+	</tr>
+	<tr>
+		<td><a href="https://harvest4d.org/">Harvest4D</a></td>
+		<td><a href="https://ec.europa.eu/transport/themes/research/fp7_en">EU 7th Framework Program 323567</a></td>
+	</tr>
+	<tr>
+		<td><a href="https://gcd.tuwien.ac.at/">GCD Doctoral College</a></td>
+		<td><a href="https://www.tuwien.at/en/">TU Wien</a></td>
+	</tr>
+	<tr>
+		<td><a href="https://www.cg.tuwien.ac.at/research/projects/Superhumans/">Superhumans</a></td>
+		<td><a href="https://www.fwf.ac.at/">FWF</a></td>
+	</tr>
+</table>
+
+<br>
+Thanks to all the companies and institutions funding Potree:
+
+<table style="width: 100%; text-align: center;">
+	<tr>
+		<th style="padding-top: 0.7em;">• Diamond •</th>
+	</tr>
+	
+	<tr><td><a href="http://www.ne.ch/autorites/DDTE/SGRF/SITN/Pages/accueil.aspx">SITN</a> </td></tr>
+	<tr><td><a href="http://synth.earth/">Synth</a></td></tr>
+	<tr><td><a href="http://www.geocue.com">GeoCue Group</a> </td></tr>
+	<tr><td><a href="http://rapidlasso.com">Rapidlasso</a> </td></tr>
+
+	<tr>
+		<th style="padding-top: 0.7em;">• Gold •</th>
+	</tr>
+	<tr><td><a href="https://www.bart.gov">BART</a></td></tr>
+	<tr>
+		<th style="padding-top: 0.7em;">• Silver •</th>
+	</tr>
+	<tr><td><a href="https://biology.anu.edu.au/research/facilities/australian-plant-phenomics-facility-anu">APPF ANU</a></td></tr>
+	<tr><td><a href="https://www.limit-addict.fr/">LimitAddict</a></td></tr>
+	<tr><td><a href="http://georepublic.info">Georepublic</a></td></tr>
+	<tr>
+		<th style="padding-top: 0.7em;">• Bronze •</th>
+	</tr>
+	<tr><td><a href="https://www.eventart.at/">EventArt</a></td></tr>
+	<tr><td><a href="https://www.geodelta.com/">Geodelta</a></td></tr>
+	<tr><td><a href="https://www.e-cassini.fr/">E-Cassini</a></td></tr>
+	<tr><td><a href="https://www.sogelink.fr/">Sogelink</a></td></tr>
+	<tr><td>Data-viewer</td></tr>
+	<tr><td><a href="http://www.helimap.com/">Helimap</a></td></tr>
+	<tr><td><a href="http://www.vevey.ch/">Vevey</a></td></tr>
+	<tr><td><a href="https://www.yverdon-les-bains.ch/">yverdon-les-bains</a></td></tr>
+	<tr><td><a href="http://archpro.lbg.ac.at">LBI ArchPro</a> </td></tr>
+	<tr><td><a href="http://www.kts.co.jp">KTS</a></td></tr>
+	<tr><td><a href="http://veesus.com">Veesus</a></td></tr>
+	<tr><td><a href="http://www.sigeom.ch">Sigeom</a></td></tr>
+</table>
+</li>
+
+				
+
+			
+			</ul>
+		</div>
+
+
+
+        <h3 class="accordion-header ui-widget"><span>数据集校准</span></h3>
+        <div id="alignment"  class="accordion-content ui-widget pv-menu-list">
+            <li name='function'><button id='startAlignment'>开始</button></li>
+            <li name='function'><button id='exitAlignment'>退出</button></li>
+            <li name = 'selectPointCloud'> 
+                <span>选择数据集</span>
+                <div> </div>   
+            </li>
+            <li name='transform'>
+                <span>旋转</span>
+                <div><button>逆10°</button><button>逆1°</button><button>逆0.1°</button> <span class='gap'><button>顺10°</button><button>顺1°</button><button>顺0.1°</button></div>
+                <span>平移</span>
+                <div><button>-x</button><button>+x</button><span class='gap'></span> <button>-y</button><button>+y</button> <span class='gap'> <button>-z</button><button>+z</button></div>
+            </li>
+            <button id='rotTool'>旋转</button> 
+            <button id='moveTool'>平移</button> 
+        </div>
+
+        <h3 class="accordion-header ui-widget"><span>点云下载</span></h3>
+        <div id="clipModel"  class="accordion-content ui-widget pv-menu-list">
+            <button>开始</button>
+            <button>下载</button>
+            <button>退出</button>
+        </div>
+
+
+
+        <h3 class="accordion-header ui-widget"><span>空间模型</span></h3>
+        <div id="siteModel"  class="accordion-content ui-widget pv-menu-list">
+            <button name='start'>开始</button>
+            <button name='exit'>退出</button>
+            <li name='build'>
+                <button name='building'>创建建筑</button>
+                <button name='floor'>添加楼层</button>
+                <button name='room'>创建房间</button>
+                <button name='digHole'>挖洞</button>
+            </li> 
+            <li name='edit'>
+                <button name='removeFirstMarker'>去除第一个点</button>
+                <button name='removeFirstRoom'>去除第一个房间</button>
+                <button name='removeFirstFloor'>去除第一个楼层</button>
+                <button name='removeFirstBuilding'>去除第一个建筑</button>
+                <button name='removeFirstHole'>去除第一个洞</button>
+                <!-- 退出绘图模式 -->
+                
+                
+                <button name='selectFloor'>选中第一个楼层</button>
+                <button name='selectBuilding'>选中第一个建筑</button>
+                <button name='selectRoom'>选中第一个房间</button>
+                
+            </li> 
+        </div>
+
+
+        <h3 class="accordion-header ui-widget"><span>粒子</span></h3>
+        <div id="particle"  class="accordion-content ui-widget pv-menu-list">
+            <button name='addFire'>添加火烟</button> 
+            <button name='addExplode'>添加爆炸</button> 
+        </div>
+
+
+        <h3 class="accordion-header ui-widget"><span>漫游点编辑</span></h3>
+        <div id="panos"  class="accordion-content ui-widget pv-menu-list">
+            <li name="transform">
+                <button name='translate'>平移</button> 
+                <button name='rotate'>旋转</button>
+            </li> 
+            <li name="operation">
+                
+                <div><button name='removeLink'>断开</button>  <span class="gap"><button name='addLink'>连接</button>  </div>
+                 
+                <div><button name='getCloser'> 放大 </button>  </div>
+            </li> 
+            <li name="views">
+                
+                <div><button name='topView'>顶视图</button>  <span class="gap"><button name='sideView'>侧视图</button> <span class="gap"><button name='3DView'>3D</button></span></div>
+                  
+            </li>
+            
+            <button name="save">保存</button> 
+            
+        </div>
+        
+        
+        
+        <h3 class="accordion-header ui-widget"><span>多元融合</span></h3>
+        <div id="mergeModel"  class="accordion-content ui-widget pv-menu-list">
+            <li name="laser">
+                laser<button name='operation'>添加</button>  
+            </li>
+            <li name="4dkk">
+                4dkk<button name='operation'>添加</button>  
+            </li>
+            <li name="obj">
+                obj<button name='operation'>添加</button>  
+            </li>
+        </div>
+        
+        
+        <h3 class="accordion-header ui-widget"><span>实时剪裁</span></h3>
+        <div id="clipping" class="accordion-content ui-widget pv-menu-list" style=""> 
+            <li name='start'>
+                <button name="enter">开始</button> 
+                <button name="exit">退出</button>
+            </li>     
+            <li name="operation" style='display:none'> 
+                <button name="switchView">切换视图</button>  
+                <button name="translation">平移</button>
+                <button name="rotation">旋转</button>
+                <button name="scale">缩放</button>
+                <button name="add">新增</button> 
+                <button name="save">保存</button>
+            </li> 
+            <li name="list" style='display:none'>
+               <ul>
+                <!-- <li><button name='changeTask'>不可见</button><button name='chose'>选择</button><button name='delete'>删除</button></li> -->
+               </ul> 
+            </li>  
+        </div>
+        
+        
+	</div>
+    
+    
+    
+
+	<div>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+			<br>
+		</div>
+	</div>
+	
+    
+    
+</div>
+
+

+ 106 - 0
src/viewer/sidebar2.html

@@ -0,0 +1,106 @@
+
+<div id="sidebar_root" class="">
+	<div>  
+ 
+
+        <div id="potree_menu" class="accordion"> 
+            <h3 class="accordion-header ui-widget"><span>多元融合</span></h3>
+            <div id="mergeModel"  class="accordion-content ui-widget pv-menu-list">
+                <ul name='model'>
+                    <li name="laser">
+                        laser : <button name='operation'>添加</button>  <button name='select'> select </button>
+                    </li>
+                    <li name="4dkk">
+                        4dkk : <button name='operation'>添加</button>  <button name='select'> select </button>
+                    </li>
+                    <li name="obj">
+                        obj : <button name='operation'>添加</button>  <button name='select'> select </button>
+                    </li>
+                    <li name="glb">
+                        glb : <button name='operation'>添加</button>  <button name='select'> select </button>
+                    </li>
+                    <li name="3dTiles">
+                        3dTiles : <button name='operation'>添加</button>  <button name='select'> select </button>
+                    </li>
+                </ul>
+                
+                <li> <button name='splitScreen'>分屏</button> </li>
+                <li> <button name='tag'>加热点</button> </li>
+                
+                
+            </div>
+            
+            
+            
+            <!-- TOOLS -->
+            <h3 id="menu_tools"><span data-i18n="tb.tools_opt"></span></h3>
+            <div class="pv-menu-list">
+
+                <div class="divider"><span>Measurement</span></div>
+
+                <li id="tools"></li>
+
+                <li>
+                    <selectgroup id="measurement_options_show">
+                        <option id="measurement_options_show_yes" value="SHOW">Show</option>
+                        <option id="measurement_options_show_no" value="HIDE">Hide</option>
+                    </selectgroup>
+                </li>
+
+                <div class="divider"><span>Clipping</span></div>
+
+                <li id="clipping_tools"></li>
+
+                <li>
+                    <selectgroup id="cliptask_options">
+                        <option id="cliptask_options_none" value="NONE">None</option>
+                        <option id="cliptask_options_highlight" value="HIGHLIGHT">Highlight</option>
+                        <option id="cliptask_options_show_inside" value="SHOW_INSIDE">Inside</option>
+                        <option id="cliptask_options_show_outside" value="SHOW_OUTSIDE">Outside</option>
+                    </selectgroup>
+                </li>
+
+                <li>
+                    <selectgroup id="clipmethod_options">
+                        <option id="clipmethod_options_any" value="INSIDE_ANY">Inside Any</option>
+                        <option id="clipmethod_options_all" value="INSIDE_ALL">Inside All</option>
+                    </selectgroup>
+                </li>
+                
+                <div class="divider"><span>Navigation</span></div>
+
+                <li id="navigation"></li>
+
+                <li><span data-i18n="appearance.move_speed"></span>: <span id="lblMoveSpeed"></span><div id="sldMoveSpeed"></div></li>
+            </div>
+                
+            <!-- SCENE -->
+            <h3 id="menu_scene"><span data-i18n="tb.scene_opt"></span></h3>
+            <div class="pv-menu-list">
+
+                <div id="scene_export"></div>
+
+                <div class="divider"><span>Objects</span></div>
+
+                <div id="scene_objects"></div>
+
+                <div class="divider"><span>Properties</span></div>
+
+                <div id="scene_object_properties"></div>
+            </div>
+            
+            
+            
+            
+            
+            
+        </div>  
+        
+	</div>  
+    <div id='log' style='position:absolute; bottom:0; height:100px;width:100%; background:rgba(1,1,1,0.3);font-size: 16px;'>
+        
+    </div> 
+	
+</div>
+
+

+ 138 - 12
src/viewer/sidebarNew.js

@@ -2,7 +2,7 @@
 import * as THREE from "../../libs/three.js/build/three.module.js";
 import {GeoJSONExporter} from "../exporter/GeoJSONExporter.js"
 import {DXFExporter} from "../exporter/DXFExporter.js"
-import {Volume, SphereVolume} from "../utils/Volume.js"
+import {Volume, SphereVolume} from "../utils/VolumeNew.js"
 import {PolygonClipVolume} from "../utils/PolygonClipVolume.js"
 import {PropertiesPanel} from "./PropertyPanels/PropertiesPanel.js"
 import {PointCloudTree} from "../PointCloudTree.js"
@@ -60,7 +60,7 @@ export class Sidebar{
             this.initScene();
             this.initNavigation();
             this.initFilters();
-            this.initClippingTool();
+            //this.initClippingTool(); //因为修改了clipping,所以这项有bug不能使用
             this.initSettings();
             
             if(Potree.settings.editType != 'pano'){
@@ -68,7 +68,7 @@ export class Sidebar{
                 this.initClipModel();
                 this.initSiteModel()
                 this.initParitcle()
-                 
+                this.initClippingModel()
             }else{
                 this.initPanosEdit()
             }
@@ -142,19 +142,19 @@ export class Sidebar{
         var buttons = pannel.find('button')
         let MergeEditor = viewer.modules.MergeEditor
         let loading = false;
-        
+         
         pannel.find('ul[name="model"] li button').on('click',(e)=>{
             if(loading)return console.log('还在加载', loading)
             let $elem = $(e.target)
-            
-                
+               
+                  
             let parent = $elem.parent()
             let name = parent.attr('name') 
             
             if($elem.attr('name') == 'select'){ 
                 return Potree.selectModel(name)
             } 
-            
+             
             if($elem.text() == '添加'){
                 let startTime = Date.now()
                 Potree.addModel(name,()=>{
@@ -195,7 +195,78 @@ export class Sidebar{
     }
     
     
+    initClippingModel(){//实时裁剪
     
+        /* 总共两种box : 可见和不可见(都是并集) 
+        当有可见box时,需要在任一可见box内才可见
+        当有不可见box时,不在所有不可见box内才可见 */
+        var clipping = viewer.modules.clipping
+        var pannel = $('#clipping');
+        var addBtn = pannel.find('[name="operation"] button[name="add"] ')   
+        var switchBtn = pannel.find('[name="operation"] button[name="switchView"] ')   
+        var enterBtn = pannel.find('  button[name="enter"] ')
+        var exitBtn = pannel.find(' button[name="exit"] ')  
+        let list = pannel.find('[name="list"]  ul ') 
+        
+        
+        enterBtn.on('click',()=>{ 
+            clipping.enter() 
+            pannel.find('li[name=operation]').css('display','block')
+            pannel.find('li[name=list]').css('display','block')
+        }) 
+        exitBtn.on('click',()=>{ 
+            clipping.leave() 
+            pannel.find('li[name=operation]').css('display','none')
+            pannel.find('li[name=list]').css('display','none')
+        })
+        switchBtn.on('click',()=>{ 
+            clipping.switchView(clipping.activeViewName == 'top' ? 'mainView' : 'top') 
+        })
+        pannel.find('[name="operation"] button[name="translation"] ').on('click',()=>{
+            clipping.setTranMode('translation')
+        })
+        pannel.find('[name="operation"] button[name="rotation"] ').on('click',()=>{
+            clipping.setTranMode('rotation')
+        })
+        pannel.find('[name="operation"] button[name="scale"] ').on('click',()=>{
+            clipping.setTranMode('scale')
+        })
+        
+        addBtn.on('click',()=>{
+            
+            let volumeBox = this.volumeTool.startInsertion({clip: true});
+            
+            
+            let li = $("<li><button name='changeTask'>不可见</button><button name='chose'>选择</button><button name='delete'>删除</button></li>")
+            list.append(li);
+            
+            li.find('button[name=changeTask]').on('click',(e)=>{
+                if(e.target.innerText == '不可见'){
+                    volumeBox.clipTask = Potree.ClipTask.SHOW_INSIDE 
+                    e.target.innerText = '可见' 
+                }else{ 
+                    volumeBox.clipTask = Potree.ClipTask.SHOW_OUTSIDE 
+                    e.target.innerText = '不可见'
+                } 
+                volumeBox.update()
+            })
+            
+            li.find('button[name=chose]').on('click',(e)=>{
+                viewer.transformObject(volumeBox)//viewer.inputHandler.toggleSelection(volumeBox)
+            })
+            
+            li.find('button[name=delete]').on('click',(e)=>{
+                li.remove()
+                viewer.scene.removeVolume(volumeBox);
+            })
+             
+        })
+        
+        pannel.find('button[name=save]').on('click',(e)=>{
+            let data = viewer.volumeTool.saveClipData();   
+        })
+         
+    }
     
     addAlignmentButton(pointcloud){
         var pannel = $('#alignment li[name="selectPointCloud"]>div');
@@ -390,7 +461,62 @@ export class Sidebar{
 				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
 			}
 		));
+        // DISTANCE2
+		elToolbar.append(this.createToolIcon(
+			Potree.resourcePath + '/icons/distance.svg',
+			'[title]MulDistance',
+			() => {
+				$('#menu_measurements').next().slideDown();
+				let measurement = this.measuringTool.startInsertion({
+					showDistances: true,
+					showArea: false,
+					closed: false,
+                    minMarkers:2, 
+					measureType: 'MulDistance'
+                });
+
+				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
+				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
+				$.jstree.reference(jsonNode.id).deselect_all();
+				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
+			}
+		));
+        
+        
+        elToolbar.append(this.createToolIcon(
+			Potree.resourcePath + '/icons/distance.svg',
+			'[title]Ver MulDistance',
+			() => {
+				$('#menu_measurements').next().slideDown();
+				let measurement = this.measuringTool.startInsertion({
+					 
+					measureType: 'Ver MulDistance'
+                });
 
+				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
+				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
+				$.jstree.reference(jsonNode.id).deselect_all();
+				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
+			}
+		));
+        
+        elToolbar.append(this.createToolIcon(
+			Potree.resourcePath + '/icons/distance.svg',
+			'[title]Hor MulDistance',
+			() => {
+				$('#menu_measurements').next().slideDown();
+				let measurement = this.measuringTool.startInsertion({
+					 
+					measureType: 'Hor MulDistance'
+                });
+
+				let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
+				let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
+				$.jstree.reference(jsonNode.id).deselect_all();
+				$.jstree.reference(jsonNode.id).select_node(jsonNode.id);
+			}
+		));
+        
 		// HEIGHT
 		elToolbar.append(this.createToolIcon(
 			Potree.resourcePath + '/icons/height.svg',
@@ -490,7 +616,7 @@ export class Sidebar{
         //Hor AREA
 		elToolbar.append(this.createToolIcon(
 			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.Hor Area',
+			'[title]Hor Area',
 			() => {
 				$('#menu_measurements').next().slideDown();
 				let measurement = this.measuringTool.startInsertion({
@@ -511,7 +637,7 @@ export class Sidebar{
         // Ver Area
 		elToolbar.append(this.createToolIcon(
 			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.Ver Area',
+			'[title]Ver Area',
 			() => {
 				$('#menu_measurements').next().slideDown();
 				let measurement = this.measuringTool.startInsertion({
@@ -531,7 +657,7 @@ export class Sidebar{
         // rect area freedom direction
         elToolbar.append(this.createToolIcon(
 			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.area_freedom_rect',
+			'[title]area_freedom_rect',
 			() => {
 				$('#menu_measurements').next().slideDown();
 				let measurement = this.measuringTool.startInsertion({
@@ -553,7 +679,7 @@ export class Sidebar{
         // rect area horizontal
 		elToolbar.append(this.createToolIcon(
 			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.area_horizontal_rect',
+			'[title]area_horizontal_rect',
 			() => {
 				$('#menu_measurements').next().slideDown();
 				let measurement = this.measuringTool.startInsertion({
@@ -577,7 +703,7 @@ export class Sidebar{
         // rect area vertical
         elToolbar.append(this.createToolIcon(
 			Potree.resourcePath + '/icons/area.svg',
-			'[title]tt.area_vertical_rect',
+			'[title]area_vertical_rect',
 			() => {
 				$('#menu_measurements').next().slideDown();
 				let measurement = this.measuringTool.startInsertion({