Kaynağa Gözat

更换了line2 因为旧的在某些角度无法cast到

xzw 3 yıl önce
ebeveyn
işleme
025a5df51f
41 değiştirilmiş dosya ile 3630 ekleme ve 1335 silme
  1. 79 0
      examples/pano.html
  2. 7 12
      libs/three.js/lines/Line2.js
  3. 22 40
      libs/three.js/lines/LineGeometry.js
  4. 539 264
      libs/three.js/lines/LineMaterial.js
  5. 274 128
      libs/three.js/lines/LineSegments2.js
  6. 76 95
      libs/three.js/lines/LineSegmentsGeometry.js
  7. 38 12
      src/PointCloudOctree.js
  8. 13 1
      src/Potree.js
  9. 1 1
      src/PotreeRenderer.js
  10. 5 2
      src/Potree_update_visibility.js
  11. 28 16
      src/materials/shaders/pointcloud.vs
  12. 123 83
      src/modules/Images360/Images360.js
  13. 113 67
      src/modules/Images360/Panorama.js
  14. 9 3
      src/modules/clipModel/Clip.js
  15. 134 41
      src/modules/datasetAlignment/Alignment.js
  16. 1048 0
      src/modules/panoEdit/panoEditor.js
  17. 15 14
      src/modules/siteModel/BuildingBox.js
  18. 92 162
      src/modules/siteModel/SiteModel.js
  19. 114 56
      src/navigation/FirstPersonControls.js
  20. 44 25
      src/navigation/InputHandler.js
  21. 2 1
      src/navigation/OrbitControls.js
  22. 1 1
      src/objects/Magnifier.js
  23. 3 6
      src/objects/Reticule.js
  24. 2 2
      src/objects/Sprite.js
  25. 5 6
      src/objects/tool/Measure.js
  26. 3 0
      src/objects/tool/mapClipBox.js
  27. 22 12
      src/settings.js
  28. 158 8
      src/start.js
  29. 6 1
      src/utils.js
  30. 48 6
      src/utils/Common.js
  31. 15 5
      src/utils/CursorDeal.js
  32. 5 1
      src/utils/DrawUtil.js
  33. 37 27
      src/utils/SplitScreen.js
  34. 19 1
      src/utils/math.js
  35. 6 3
      src/viewer/Scene.js
  36. 113 13
      src/viewer/View.js
  37. 13 5
      src/viewer/map/MapViewer.js
  38. 21 1
      src/viewer/sidebar.html
  39. 58 5
      src/viewer/sidebar.js
  40. 292 208
      src/viewer/viewer.js
  41. 27 1
      改bug的历史.txt

+ 79 - 0
examples/pano.html

@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<meta charset="utf-8">
+	<meta name="description" content="">
+	<meta name="author" content="">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+	<title>Potree Viewer</title>
+
+	<link rel="stylesheet" type="text/css" href="../../build/potree/potree.css">
+	<link rel="stylesheet" type="text/css" href="../../libs/jquery-ui/jquery-ui.min.css">
+	<link rel="stylesheet" type="text/css" href="../../libs/openlayers3/ol.css">
+	<link rel="stylesheet" type="text/css" href="../../libs/spectrum/spectrum.css">
+	<link rel="stylesheet" type="text/css" href="../../libs/jstree/themes/mixed/style.css">
+</head>
+
+<body>
+	<script src="../../libs/jquery/jquery-3.1.1.min.js"></script>
+	<script src="../../libs/spectrum/spectrum.js"></script>
+	<script src="../../libs/jquery-ui/jquery-ui.min.js"></script>
+	<script src="../../libs/other/BinaryHeap.js"></script>
+	<script src="../../libs/tween/tween.min.js"></script>
+	<script src="../../libs/d3/d3.js"></script>
+	<script src="../../libs/proj4/proj4.js"></script> 
+	
+    
+	<script src="../../libs/openlayers3/ol.js"></script>
+	<script src="../../libs/i18next/i18next.js"></script>
+	<script src="../../libs/jstree/jstree.js"></script>
+	<script src="../../build/potree/potree.js"></script>
+	<script src="../../libs/plasio/js/laslaz.js"></script>
+	
+	
+	<div class="potree_container" style="position: absolute; width: 100%; height: 100%; left: 0px; top: 0px; ">
+		<div id="potree_render_area" style="background-image: url('../../build/potree/resources/images/background.jpg');">
+            
+		</div>
+		<div id="potree_sidebar_container"> </div>
+	</div>
+	
+	<script type="module">
+
+	import * as THREE from "../libs/three.js/build/three.module.js";
+    import browser from '../src/utils/browser.js' //这里必须加.js
+     
+        /*var number = window.location.href.substring(window.location.href.indexOf("=") + 1);
+        if (number.indexOf("&") != -1) {
+            number = number.substring(0, number.indexOf("&"));
+        }
+        if (number.indexOf("#") != -1) {
+            number = number.substring(0, number.indexOf("#"));
+        }*/
+        
+        var number = browser.urlHasValue('m',true);
+        console.log(number)
+        Potree.panoEditStart(document.getElementById("potree_render_area"),null, number);
+        
+          
+		/*
+        数据集校准 平移后
+        单个数据集:
+        
+        点云的本地位置是一样的 。说明:单个数据集时点云最终平移量为0,同理漫游点也是 
+        多个数据集时,参照为第一个dataset,见GeoTransformationService.setOffsetFromGlobal。所以第一个数据集的位置为000,其他的不是。因此本地坐标是不固定的,只有指定了参考数据集才能确定,如果去掉第一个数据集下一次显示的坐标就不同了,但是不影响相对位置所以看起来一样。
+        (注意:navvis平移后要刷新location才生效。)
+        
+        
+        var view = window.IV.getMainView() 
+        view.ImageService.images.forEach(e=>console.log(e.id + ": "+e.location.toArray()))
+        
+        
+        images360.panos.forEach(e=>console.log(e.id + ": "+e.position.toArray()))
+        */        
+		
+	</script>
+	
+	
+  </body>
+</html>

+ 7 - 12
libs/three.js/lines/Line2.js

@@ -2,23 +2,18 @@ import { LineSegments2 } from '../lines/LineSegments2.js';
 import { LineGeometry } from '../lines/LineGeometry.js';
 import { LineMaterial } from '../lines/LineMaterial.js';
 
-var Line2 = function ( geometry, material ) {
+class Line2 extends LineSegments2 {
 
-	if ( geometry === undefined ) geometry = new LineGeometry();
-	if ( material === undefined ) material = new LineMaterial( { color: Math.random() * 0xffffff } );
+	constructor( geometry = new LineGeometry(), material = new LineMaterial( { color: Math.random() * 0xffffff } ) ) {
 
-	LineSegments2.call( this, geometry, material );
+		super( geometry, material );
 
-	this.type = 'Line2';
+		this.isLine2 = true;
 
-};
+		this.type = 'Line2';
 
-Line2.prototype = Object.assign( Object.create( LineSegments2.prototype ), {
+	}
 
-	constructor: Line2,
-
-	isLine2: true
-
-} );
+}
 
 export { Line2 };

+ 22 - 40
libs/three.js/lines/LineGeometry.js

@@ -1,27 +1,25 @@
 import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry.js';
 
-var LineGeometry = function () {
+class LineGeometry extends LineSegmentsGeometry {
 
-	LineSegmentsGeometry.call( this );
+	constructor() {
 
-	this.type = 'LineGeometry';
+		super();
 
-};
+		this.isLineGeometry = true;
 
-LineGeometry.prototype = Object.assign( Object.create( LineSegmentsGeometry.prototype ), {
+		this.type = 'LineGeometry';
 
-	constructor: LineGeometry,
-
-	isLineGeometry: true,
+	}
 
-	setPositions: function ( array ) {
+	setPositions( array ) {
 
 		// converts [ x1, y1, z1,  x2, y2, z2, ... ] to pairs format
 
-		var length = array.length - 3;
-		var points = new Float32Array( 2 * length );
+		const length = array.length - 3;
+		const points = new Float32Array( 2 * length );
 
-		for ( var i = 0; i < length; i += 3 ) {
+		for ( let i = 0; i < length; i += 3 ) {
 
 			points[ 2 * i ] = array[ i ];
 			points[ 2 * i + 1 ] = array[ i + 1 ];
@@ -33,20 +31,20 @@ LineGeometry.prototype = Object.assign( Object.create( LineSegmentsGeometry.prot
 
 		}
 
-		LineSegmentsGeometry.prototype.setPositions.call( this, points );
+		super.setPositions( points );
 
 		return this;
 
-	},
+	}
 
-	setColors: function ( array ) {
+	setColors( array ) {
 
 		// converts [ r1, g1, b1,  r2, g2, b2, ... ] to pairs format
 
-		var length = array.length - 3;
-		var colors = new Float32Array( 2 * length );
+		const length = array.length - 3;
+		const colors = new Float32Array( 2 * length );
 
-		for ( var i = 0; i < length; i += 3 ) {
+		for ( let i = 0; i < length; i += 3 ) {
 
 			colors[ 2 * i ] = array[ i ];
 			colors[ 2 * i + 1 ] = array[ i + 1 ];
@@ -58,40 +56,24 @@ LineGeometry.prototype = Object.assign( Object.create( LineSegmentsGeometry.prot
 
 		}
 
-		LineSegmentsGeometry.prototype.setColors.call( this, colors );
+		super.setColors( colors );
 
 		return this;
 
-	},
-
-	fromLine: function ( line ) {
-
-		var geometry = line.geometry;
-
-		if ( geometry.isGeometry ) {
-
-			this.setPositions( geometry.vertices );
+	}
 
-		} else if ( geometry.isBufferGeometry ) {
+	fromLine( line ) {
 
-			this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
+		const geometry = line.geometry;
 
-		}
+		this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
 
 		// set colors, maybe
 
 		return this;
 
-	},
-
-	copy: function ( /* source */ ) {
-
-		// todo
-
-		return this;
-
 	}
 
-} );
+}
 
 export { LineGeometry };

Dosya farkı çok büyük olduğundan ihmal edildi
+ 539 - 264
libs/three.js/lines/LineMaterial.js


+ 274 - 128
libs/three.js/lines/LineSegments2.js

@@ -1,209 +1,355 @@
 import {
+	Box3,
 	InstancedInterleavedBuffer,
 	InterleavedBufferAttribute,
 	Line3,
 	MathUtils,
 	Matrix4,
 	Mesh,
+	Sphere,
 	Vector3,
 	Vector4
-} from '../build/three.module.js';
+} from '../build/three.module.js'; 
 import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry.js';
 import { LineMaterial } from '../lines/LineMaterial.js';
 
-var LineSegments2 = function ( geometry, material ) {
+const _start = new Vector3();
+const _end = new Vector3();
 
-	if ( geometry === undefined ) geometry = new LineSegmentsGeometry();
-	if ( material === undefined ) material = new LineMaterial( { color: Math.random() * 0xffffff } );
+const _start4 = new Vector4();
+const _end4 = new Vector4();
 
-	Mesh.call( this, geometry, material );
+const _ssOrigin = new Vector4();
+const _ssOrigin3 = new Vector3();
+const _mvMatrix = new Matrix4();
+const _line = new Line3();
+const _closestPoint = new Vector3();
 
-	this.type = 'LineSegments2';
+const _box = new Box3();
+const _sphere = new Sphere();
+const _clipToWorldVector = new Vector4();
 
-};
+let _ray, _instanceStart, _instanceEnd, _lineWidth;
 
-LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
+// Returns the margin required to expand by in world space given the distance from the camera,
+// line width, resolution, and camera projection
+function getWorldSpaceHalfWidth( camera, distance, resolution ) {
 
-	constructor: LineSegments2,
+	// transform into clip space, adjust the x and y values by the pixel width offset, then
+	// transform back into world space to get world offset. Note clip space is [-1, 1] so full
+	// width does not need to be halved.
+	_clipToWorldVector.set( 0, 0, - distance, 1.0 ).applyMatrix4( camera.projectionMatrix );
+	_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
+	_clipToWorldVector.x = _lineWidth / resolution.width;
+	_clipToWorldVector.y = _lineWidth / resolution.height;
+	_clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
+	_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
 
-	isLineSegments2: true,
+	return Math.abs( Math.max( _clipToWorldVector.x, _clipToWorldVector.y ) );
 
-	computeLineDistances: ( function () { // for backwards-compatability, but could be a method of LineSegmentsGeometry...
+}
 
-		var start = new Vector3();
-		var end = new Vector3();
+function raycastWorldUnits( lineSegments, intersects ) {
 
-		return function computeLineDistances() {
+	for ( let i = 0, l = _instanceStart.count; i < l; i ++ ) {
 
-			var geometry = this.geometry;
+		_line.start.fromBufferAttribute( _instanceStart, i );
+		_line.end.fromBufferAttribute( _instanceEnd, i );
 
-			var instanceStart = geometry.attributes.instanceStart;
-			var instanceEnd = geometry.attributes.instanceEnd;
-			var lineDistances = new Float32Array( 2 * instanceStart.data.count );
+		const pointOnLine = new Vector3();
+		const point = new Vector3();
 
-			for ( var i = 0, j = 0, l = instanceStart.data.count; i < l; i ++, j += 2 ) {
+		_ray.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );
+		const isInside = point.distanceTo( pointOnLine ) < _lineWidth * 0.5;
 
-				start.fromBufferAttribute( instanceStart, i );
-				end.fromBufferAttribute( instanceEnd, i );
+		if ( isInside ) {
 
-				lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
-				lineDistances[ j + 1 ] = lineDistances[ j ] + start.distanceTo( end );
+			intersects.push( {
+				point,
+				pointOnLine,
+				distance: _ray.origin.distanceTo( point ),
+				object: lineSegments,
+				face: null,
+				faceIndex: i,
+				uv: null,
+				uv2: null,
+			} );
 
-			}
+		}
 
-			var instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1
+	}
 
-			geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
-			geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1
+}
 
-			return this;
+function raycastScreenSpace( lineSegments, camera, intersects ) {
 
-		};
+	const projectionMatrix = camera.projectionMatrix;
+	const material = lineSegments.material;
+	const resolution = material.resolution;
+	const matrixWorld = lineSegments.matrixWorld;
 
-	}() ),
+	const geometry = lineSegments.geometry;
+	const instanceStart = geometry.attributes.instanceStart;
+	const instanceEnd = geometry.attributes.instanceEnd;
 
-	raycast: ( function () {
+	const near = - camera.near;
 
-		var start = new Vector4();
-		var end = new Vector4();
+	//
 
-		var ssOrigin = new Vector4();
-		var ssOrigin3 = new Vector3();
-		var mvMatrix = new Matrix4();
-		var line = new Line3();
-		var closestPoint = new Vector3();
+	// pick a point 1 unit out along the ray to avoid the ray origin
+	// sitting at the camera origin which will cause "w" to be 0 when
+	// applying the projection matrix.
+	_ray.at( 1, _ssOrigin );
 
-		return function raycast( raycaster, intersects ) {
+	// ndc space [ - 1.0, 1.0 ]
+	_ssOrigin.w = 1;
+	_ssOrigin.applyMatrix4( camera.matrixWorldInverse );
+	_ssOrigin.applyMatrix4( projectionMatrix );
+	_ssOrigin.multiplyScalar( 1 / _ssOrigin.w );
 
-			if ( raycaster.camera === null ) {
+	// screen space
+	_ssOrigin.x *= resolution.x / 2;
+	_ssOrigin.y *= resolution.y / 2;
+	_ssOrigin.z = 0;
 
-				console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.' );
+	_ssOrigin3.copy( _ssOrigin );
 
-			}
+	_mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
 
-			var threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0;
+	for ( let i = 0, l = instanceStart.count; i < l; i ++ ) {
 
-			var ray = raycaster.ray;
-			var camera = raycaster.camera;
-			var projectionMatrix = camera.projectionMatrix;
+		_start4.fromBufferAttribute( instanceStart, i );
+		_end4.fromBufferAttribute( instanceEnd, i );
 
-			var geometry = this.geometry;
-			var material = this.material;
-			var resolution = material.resolution;
-			var lineWidth = material.lineWidth + threshold;
+		_start4.w = 1;
+		_end4.w = 1;
 
-			var instanceStart = geometry.attributes.instanceStart;
-			var instanceEnd = geometry.attributes.instanceEnd;
+		// camera space
+		_start4.applyMatrix4( _mvMatrix );
+		_end4.applyMatrix4( _mvMatrix );
 
-			// pick a point 1 unit out along the ray to avoid the ray origin
-			// sitting at the camera origin which will cause "w" to be 0 when
-			// applying the projection matrix.
-			ray.at( 1, ssOrigin );
+		// skip the segment if it's entirely behind the camera
+		const isBehindCameraNear = _start4.z > near && _end4.z > near;
+		if ( isBehindCameraNear ) {
 
-			// ndc space [ - 1.0, 1.0 ]
-			ssOrigin.w = 1;
-			ssOrigin.applyMatrix4( camera.matrixWorldInverse );
-			ssOrigin.applyMatrix4( projectionMatrix );
-			ssOrigin.multiplyScalar( 1 / ssOrigin.w );
+			continue;
 
-			// screen space
-			ssOrigin.x *= resolution.x / 2;
-			ssOrigin.y *= resolution.y / 2;
-			ssOrigin.z = 0;
+		}
 
-			ssOrigin3.copy( ssOrigin );
+		// trim the segment if it extends behind camera near
+		if ( _start4.z > near ) {
 
-			var matrixWorld = this.matrixWorld;
-			mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
+			const deltaDist = _start4.z - _end4.z;
+			const t = ( _start4.z - near ) / deltaDist;
+			_start4.lerp( _end4, t );
 
-			for ( var i = 0, l = instanceStart.count; i < l; i ++ ) {
+		} else if ( _end4.z > near ) {
 
-				start.fromBufferAttribute( instanceStart, i );
-				end.fromBufferAttribute( instanceEnd, i );
+			const deltaDist = _end4.z - _start4.z;
+			const t = ( _end4.z - near ) / deltaDist;
+			_end4.lerp( _start4, t );
 
-				start.w = 1;
-				end.w = 1;
+		}
 
-				// camera space
-				start.applyMatrix4( mvMatrix );
-				end.applyMatrix4( mvMatrix );
+		// clip space
+		_start4.applyMatrix4( projectionMatrix );
+		_end4.applyMatrix4( projectionMatrix );
 
-				// clip space
-				start.applyMatrix4( projectionMatrix );
-				end.applyMatrix4( projectionMatrix );
+		// ndc space [ - 1.0, 1.0 ]
+		_start4.multiplyScalar( 1 / _start4.w );
+		_end4.multiplyScalar( 1 / _end4.w );
 
-				// ndc space [ - 1.0, 1.0 ]
-				start.multiplyScalar( 1 / start.w );
-				end.multiplyScalar( 1 / end.w );
+		// screen space
+		_start4.x *= resolution.x / 2;
+		_start4.y *= resolution.y / 2;
 
-				// skip the segment if it's outside the camera near and far planes
-				var isBehindCameraNear = start.z < - 1 && end.z < - 1;
-				var isPastCameraFar = start.z > 1 && end.z > 1;
-				if ( isBehindCameraNear || isPastCameraFar ) {
+		_end4.x *= resolution.x / 2;
+		_end4.y *= resolution.y / 2;
 
-					continue;
+		// create 2d segment
+		_line.start.copy( _start4 );
+		_line.start.z = 0;
 
-				}
+		_line.end.copy( _end4 );
+		_line.end.z = 0;
 
-				// screen space
-				start.x *= resolution.x / 2;
-				start.y *= resolution.y / 2;
+		// get closest point on ray to segment
+		const param = _line.closestPointToPointParameter( _ssOrigin3, true );
+		_line.at( param, _closestPoint );
 
-				end.x *= resolution.x / 2;
-				end.y *= resolution.y / 2;
+		// check if the intersection point is within clip space
+		const zPos = MathUtils.lerp( _start4.z, _end4.z, param );
+		const isInClipSpace = zPos >= - 1 && zPos <= 1;
 
-				// create 2d segment
-				line.start.copy( start );
-				line.start.z = 0;
+		const isInside = _ssOrigin3.distanceTo( _closestPoint ) < _lineWidth * 0.5;
 
-				line.end.copy( end );
-				line.end.z = 0;
+		if ( isInClipSpace && isInside ) {
 
-				// get closest point on ray to segment
-				var param = line.closestPointToPointParameter( ssOrigin3, true );
-				line.at( param, closestPoint );
+			_line.start.fromBufferAttribute( instanceStart, i );
+			_line.end.fromBufferAttribute( instanceEnd, i );
 
-				// check if the intersection point is within clip space
-				var zPos = MathUtils.lerp( start.z, end.z, param );
-				var isInClipSpace = zPos >= - 1 && zPos <= 1;
+			_line.start.applyMatrix4( matrixWorld );
+			_line.end.applyMatrix4( matrixWorld );
 
-				var isInside = ssOrigin3.distanceTo( closestPoint ) < lineWidth * 0.5;
+			const pointOnLine = new Vector3();
+			const point = new Vector3();
 
-				if ( isInClipSpace && isInside ) {
+			_ray.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );
 
-					line.start.fromBufferAttribute( instanceStart, i );
-					line.end.fromBufferAttribute( instanceEnd, i );
+			intersects.push( {
+				point: point,
+				pointOnLine: pointOnLine,
+				distance: _ray.origin.distanceTo( point ),
+				object: lineSegments,
+				face: null,
+				faceIndex: i,
+				uv: null,
+				uv2: null,
+			} );
 
-					line.start.applyMatrix4( matrixWorld );
-					line.end.applyMatrix4( matrixWorld );
+		}
 
-					var pointOnLine = new Vector3();
-					var point = new Vector3();
+	}
 
-					ray.distanceSqToSegment( line.start, line.end, point, pointOnLine );
+}
 
-					intersects.push( {
+class LineSegments2 extends Mesh {
 
-						point: point,
-						pointOnLine: pointOnLine,
-						distance: ray.origin.distanceTo( point ),
+	constructor( geometry = new LineSegmentsGeometry(), material = new LineMaterial( { color: Math.random() * 0xffffff } ) ) {
 
-						object: this,
-						face: null,
-						faceIndex: i,
-						uv: null,
-						uv2: null,
+		super( geometry, material );
 
-					} );
+		this.isLineSegments2 = true;
 
-				}
+		this.type = 'LineSegments2';
 
-			}
+	}
 
-		};
+	// for backwards-compatibility, but could be a method of LineSegmentsGeometry...
 
-	}() )
+	computeLineDistances() {
 
-} );
+		const geometry = this.geometry;
+
+		const instanceStart = geometry.attributes.instanceStart;
+		const instanceEnd = geometry.attributes.instanceEnd;
+		const lineDistances = new Float32Array( 2 * instanceStart.count );
+
+		for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) {
+
+			_start.fromBufferAttribute( instanceStart, i );
+			_end.fromBufferAttribute( instanceEnd, i );
+
+			lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
+			lineDistances[ j + 1 ] = lineDistances[ j ] + _start.distanceTo( _end );
+
+		}
+
+		const instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1
+
+		geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
+		geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1
+
+		return this;
+
+	}
+
+	raycast( raycaster, intersects ) {
+
+		const worldUnits = this.material.worldUnits;
+		const camera = raycaster.camera;
+
+		if ( camera === null && ! worldUnits ) {
+
+			console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.' );
+
+		}
+
+		const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0;
+
+		_ray = raycaster.ray;
+
+		const matrixWorld = this.matrixWorld;
+		const geometry = this.geometry;
+		const material = this.material;
+
+		_lineWidth = material.lineWidth + threshold;
+
+		_instanceStart = geometry.attributes.instanceStart;
+		_instanceEnd = geometry.attributes.instanceEnd;
+
+		// check if we intersect the sphere bounds
+		if ( geometry.boundingSphere === null ) {
+
+			geometry.computeBoundingSphere();
+
+		}
+
+		_sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
+
+		// increase the sphere bounds by the worst case line screen space width
+		let sphereMargin;
+		if ( worldUnits ) {
+
+			sphereMargin = _lineWidth * 0.5;
+
+		} else {
+
+			const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( _ray.origin ) );
+			sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, material.resolution );
+
+		}
+
+		_sphere.radius += sphereMargin;
+
+		if ( _ray.intersectsSphere( _sphere ) === false ) {
+
+			return;
+
+		}
+
+		// check if we intersect the box bounds
+		if ( geometry.boundingBox === null ) {
+
+			geometry.computeBoundingBox();
+
+		}
+
+		_box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
+
+		// increase the box bounds by the worst case line width
+		let boxMargin;
+		if ( worldUnits ) {
+
+			boxMargin = _lineWidth * 0.5;
+
+		} else {
+
+			const distanceToBox = Math.max( camera.near, _box.distanceToPoint( _ray.origin ) );
+			boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, material.resolution );
+
+		}
+
+		_box.expandByScalar( boxMargin );
+
+		if ( _ray.intersectsBox( _box ) === false ) {
+
+			return;
+
+		}
+
+		if ( worldUnits ) {
+
+			raycastWorldUnits( this, intersects );
+
+		} else {
+
+			raycastScreenSpace( this, camera, intersects );
+
+		}
+
+	}
+
+}
 
 export { LineSegments2 };

+ 76 - 95
libs/three.js/lines/LineSegmentsGeometry.js

@@ -9,32 +9,33 @@ import {
 	WireframeGeometry
 } from '../build/three.module.js';
 
-var LineSegmentsGeometry = function () {
+const _box = new Box3();
+const _vector = new Vector3();
 
-	InstancedBufferGeometry.call( this );
+class LineSegmentsGeometry extends InstancedBufferGeometry {
 
-	this.type = 'LineSegmentsGeometry';
+	constructor() {
 
-	var positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ];
-	var uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ];
-	var index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ];
+		super();
 
-	this.setIndex( index );
-	this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
-	this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+		this.isLineSegmentsGeometry = true;
 
-};
+		this.type = 'LineSegmentsGeometry';
 
-LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGeometry.prototype ), {
+		const positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ];
+		const uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ];
+		const index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ];
 
-	constructor: LineSegmentsGeometry,
+		this.setIndex( index );
+		this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
+		this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
 
-	isLineSegmentsGeometry: true,
+	}
 
-	applyMatrix4: function ( matrix ) {
+	applyMatrix4( matrix ) {
 
-		var start = this.attributes.instanceStart;
-		var end = this.attributes.instanceEnd;
+		const start = this.attributes.instanceStart;
+		const end = this.attributes.instanceEnd;
 
 		if ( start !== undefined ) {
 
@@ -60,11 +61,11 @@ LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGe
 
 		return this;
 
-	},
+	}
 
-	setPositions: function ( array ) {
+	setPositions( array ) {
 
-		var lineSegments;
+		let lineSegments;
 
 		if ( array instanceof Float32Array ) {
 
@@ -76,7 +77,7 @@ LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGe
 
 		}
 
-		var instanceBuffer = new InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz
+		const instanceBuffer = new InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz
 
 		this.setAttribute( 'instanceStart', new InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz
 		this.setAttribute( 'instanceEnd', new InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz
@@ -88,11 +89,11 @@ LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGe
 
 		return this;
 
-	},
+	}
 
-	setColors: function ( array ) {
+	setColors( array ) {
 
-		var colors;
+		let colors;
 
 		if ( array instanceof Float32Array ) {
 
@@ -104,32 +105,32 @@ LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGe
 
 		}
 
-		var instanceColorBuffer = new InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb
+		const instanceColorBuffer = new InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb
 
 		this.setAttribute( 'instanceColorStart', new InterleavedBufferAttribute( instanceColorBuffer, 3, 0 ) ); // rgb
 		this.setAttribute( 'instanceColorEnd', new InterleavedBufferAttribute( instanceColorBuffer, 3, 3 ) ); // rgb
 
 		return this;
 
-	},
+	}
 
-	fromWireframeGeometry: function ( geometry ) {
+	fromWireframeGeometry( geometry ) {
 
 		this.setPositions( geometry.attributes.position.array );
 
 		return this;
 
-	},
+	}
 
-	fromEdgesGeometry: function ( geometry ) {
+	fromEdgesGeometry( geometry ) {
 
 		this.setPositions( geometry.attributes.position.array );
 
 		return this;
 
-	},
+	}
 
-	fromMesh: function ( mesh ) {
+	fromMesh( mesh ) {
 
 		this.fromWireframeGeometry( new WireframeGeometry( mesh.geometry ) );
 
@@ -137,117 +138,97 @@ LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGe
 
 		return this;
 
-	},
-
-	fromLineSegments: function ( lineSegments ) {
-
-		var geometry = lineSegments.geometry;
-
-		if ( geometry.isGeometry ) {
-
-			this.setPositions( geometry.vertices );
+	}
 
-		} else if ( geometry.isBufferGeometry ) {
+	fromLineSegments( lineSegments ) {
 
-			this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
+		const geometry = lineSegments.geometry;
 
-		}
+		this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
 
 		// set colors, maybe
 
 		return this;
 
-	},
-
-	computeBoundingBox: function () {
-
-		var box = new Box3();
-
-		return function computeBoundingBox() {
-
-			if ( this.boundingBox === null ) {
-
-				this.boundingBox = new Box3();
-
-			}
+	}
 
-			var start = this.attributes.instanceStart;
-			var end = this.attributes.instanceEnd;
+	computeBoundingBox() {
 
-			if ( start !== undefined && end !== undefined ) {
+		if ( this.boundingBox === null ) {
 
-				this.boundingBox.setFromBufferAttribute( start );
+			this.boundingBox = new Box3();
 
-				box.setFromBufferAttribute( end );
+		}
 
-				this.boundingBox.union( box );
+		const start = this.attributes.instanceStart;
+		const end = this.attributes.instanceEnd;
 
-			}
+		if ( start !== undefined && end !== undefined ) {
 
-		};
+			this.boundingBox.setFromBufferAttribute( start );
 
-	}(),
+			_box.setFromBufferAttribute( end );
 
-	computeBoundingSphere: function () {
+			this.boundingBox.union( _box );
 
-		var vector = new Vector3();
+		}
 
-		return function computeBoundingSphere() {
+	}
 
-			if ( this.boundingSphere === null ) {
+	computeBoundingSphere() {
 
-				this.boundingSphere = new Sphere();
+		if ( this.boundingSphere === null ) {
 
-			}
+			this.boundingSphere = new Sphere();
 
-			if ( this.boundingBox === null ) {
+		}
 
-				this.computeBoundingBox();
+		if ( this.boundingBox === null ) {
 
-			}
+			this.computeBoundingBox();
 
-			var start = this.attributes.instanceStart;
-			var end = this.attributes.instanceEnd;
+		}
 
-			if ( start !== undefined && end !== undefined ) {
+		const start = this.attributes.instanceStart;
+		const end = this.attributes.instanceEnd;
 
-				var center = this.boundingSphere.center;
+		if ( start !== undefined && end !== undefined ) {
 
-				this.boundingBox.getCenter( center );
+			const center = this.boundingSphere.center;
 
-				var maxRadiusSq = 0;
+			this.boundingBox.getCenter( center );
 
-				for ( var i = 0, il = start.count; i < il; i ++ ) {
+			let maxRadiusSq = 0;
 
-					vector.fromBufferAttribute( start, i );
-					maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
+			for ( let i = 0, il = start.count; i < il; i ++ ) {
 
-					vector.fromBufferAttribute( end, i );
-					maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
+				_vector.fromBufferAttribute( start, i );
+				maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
 
-				}
+				_vector.fromBufferAttribute( end, i );
+				maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
 
-				this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
+			}
 
-				if ( isNaN( this.boundingSphere.radius ) ) {
+			this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
 
-					console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this );
+			if ( isNaN( this.boundingSphere.radius ) ) {
 
-				}
+				console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this );
 
 			}
 
-		};
+		}
 
-	}(),
+	}
 
-	toJSON: function () {
+	toJSON() {
 
 		// todo
 
-	},
+	}
 
-	applyMatrix: function ( matrix ) {
+	applyMatrix( matrix ) {
 
 		console.warn( 'THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4().' );
 
@@ -255,6 +236,6 @@ LineSegmentsGeometry.prototype = Object.assign( Object.create( InstancedBufferGe
 
 	}
 
-} );
+}
 
 export { LineSegmentsGeometry };

+ 38 - 12
src/PointCloudOctree.js

@@ -10,8 +10,8 @@ import Common from './utils/Common'
 import {MeshDraw} from "./utils/DrawUtil.js";
 import searchRings from "./utils/searchRings.js";
 import {TextSprite} from './objects/TextSprite'
-
-
+import {PointSizeType } from "./defines.js";
+ 
 
 
 const planeGeo = new THREE.PlaneBufferGeometry(1,1);
@@ -153,14 +153,25 @@ export class PointCloudOctree extends PointCloudTree {
         this.maxLevel = Infinity;
         this.temp = { sizeFitToLevel:{}, opacity:{}}//add
         //add 
+        
+        this.panos = []
+        this.matrixAutoUpdate = false   //最好禁止updateMatrix  直接使用matrixWorld
+        this.orientationUser = 0  
+        this.translateUser = new THREE.Vector3;
+        
         this.rotateMatrix = new THREE.Matrix4;
         this.transformMatrix = new THREE.Matrix4;// 数据集的变化矩阵
         this.transformInvMatrix = new THREE.Matrix4; 
         this.rotateInvMatrix = new THREE.Matrix4; 
-        this.material.spacing = this.pcoGeometry.spacing;//初始化一下 以便于设置pointsize
+        
         
         this.nodeMaxLevelPredict = this.predictNodeMaxLevel()//预测maxNodeLevel  
         this.testMaxNodeCount = this.testMaxNodeCount2 = 0
+        
+        this.material.spacing = this.pcoGeometry.spacing;//初始化一下 以便于设置pointsize
+        
+        
+        
 		{
 
 			let priorityQueue = ["rgba", "rgb", "intensity", "classification"];
@@ -275,7 +286,7 @@ export class PointCloudOctree extends PointCloudTree {
      
      
      
-     
+   //panoEdit时比预测值小很多?
      
    testMaxNodeLevel(){//手动使maxLevel达到最高,从而迫使updateNodeMaxLevel。  因为Potree.settings.pointDensity 不为 'high'时,maxLevel不是所加载的最高,就很容易加载不出下一个层级,导致无法知道nodeMaxLevel
         if(this.testMaxNodeLevelDone ) return
@@ -1535,10 +1546,11 @@ export class PointCloudOctree extends PointCloudTree {
 
 		let nodes = this.nodesOnRay(this.visibleNodes, ray);
 
-		if (nodes.length === 0) {
+		if (nodes.length === 0) { 
+            
 			return null;
 		}
-
+        //console.log('nodes.length != 0', this.name)
 		if (!this.pickState) {
 			let scene = new THREE.Scene();
 
@@ -1780,7 +1792,7 @@ export class PointCloudOctree extends PointCloudTree {
 
 	};
 
-	* getFittedBoxGen(boxNode){
+	* getFittedBoxGen(boxNode){//???
 		let start = performance.now();
 
 		let shrinkedLocalBounds = new THREE.Box3();
@@ -1924,12 +1936,19 @@ export class PointCloudOctree extends PointCloudTree {
     
     // 设置点大小
     changePointSize(num, sizeFitToLevel) {
-        
+ 
+        if(this.material.pointSizeType != PointSizeType.ATTENUATED){
+            return num && (this.material.size = num)
+        }
         if (num == void 0) {
             num = this.temp.pointSize
         } else {
             this.temp.pointSize = num
+            
         }
+        num /= (Potree.config.material.realPointSize / Potree.config.material.pointSize) //兼容 
+         
+        
         
         
         num = Math.pow(num, 1.05) * 6 
@@ -2009,14 +2028,21 @@ export class PointCloudOctree extends PointCloudTree {
         //console.log('changePointOpacity ' + this.dataset_id + ', num : ' + num + ' , opacity : ' + this.material.opacity) //检查是否做到了低质量时num==opacity,中质量opacity稍小于num,高质量更小
          
     } 
-
+ 
 
 
     updateBound(){
         var boundingBox_ = this.pcoGeometry.tightBoundingBox.clone().applyMatrix4(this.matrixWorld)
         this.bound = boundingBox_
     }
-    
+    getPanosBound(){
+        if(this.panos.length > 0){
+            let minSize = new THREE.Vector3(1,1,1)
+            this.panosBound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize)
+        }else{
+            this.panosBound = null
+        } 
+    }
     
     getUnrotBoundPoint(type){//获取没有旋转的tightBounding的水平四个点
         //如果alighment支持任意轴旋转,水平面上看到的可能就是六边形了,失去意义,到时候不能用这个。也可以若只绕z旋转, 使用tightBoundingBox,否则bound
@@ -2105,7 +2131,7 @@ export class PointCloudOctree extends PointCloudTree {
 
 
 
-
+/* 
 window.searchOutRing = function(points){
     var points = [{x:0,y:0},{x:1,y:1},{x:0,y:1},{x:1,y:0},{x:2,y:1},{x:1,y:2},{x:1,y:3},{x:2,y:3},
         {x:3,y:1},{x:3,y:2},{x:0,y:2},{x:0,y:3},{x:2,y:0},{x:-1,y:0},{x:-1,y:1},{x:-2,y:1},{x:3,y:3},
@@ -2131,7 +2157,7 @@ window.searchOutRing = function(points){
         onlyGetOutRing:true
     })
 }
-
+ */
 
 
 

+ 13 - 1
src/Potree.js

@@ -1,5 +1,5 @@
 export {config, settings} from "./settings.js";
-export {start} from "./start.js";
+export {start, panoEditStart} from "./start.js";
 
 
  
@@ -234,6 +234,18 @@ export async function loadPanos(datasetId, callback){
 }
 
 
+export async function loadPanosInfo(callback){ 
+    var path 
+    if(Potree.fileServer){
+        
+    }else{
+        path = `${Potree.scriptPath}/data/panoEdit/vision_edit.txt`
+          
+    }
+    return loadFile(path, callback)
+    
+}
+
 
 
 //site_model

+ 1 - 1
src/PotreeRenderer.js

@@ -1134,7 +1134,7 @@ export class Renderer {
                 
                 if(material.useFilterByNormal){
                     defines.push("#define use_filter_by_normal");
-                    defines.push("#define attenuated_opacity");
+                    //Potree.settings.editType == 'pano' ? defines.push("#define attenuated_opacity2") : defines.push("#define attenuated_opacity");
                     
                 }
 

+ 5 - 2
src/Potree_update_visibility.js

@@ -75,8 +75,11 @@ export function updateVisibilityStructures(pointclouds, camera, areaSize) {
 		let camMatrixObject = new THREE.Matrix4().multiply(worldI).multiply(view);
 		let camObjPos = new THREE.Vector3().setFromMatrixPosition(camMatrixObject);
 		camObjPositions.push(camObjPos);
-
-		if ( viewer.getObjVisiByReason(pointcloud, 'datasetSelection') &&  pointcloud.root !== null) {//改 visible -> 
+        
+        // 因漫游模式而隐藏的话 依旧需要加入visibleNodes,因为pick需要
+        
+                                                    /*  viewer.getObjVisiByReason(pointcloud, 'datasetSelection') */
+		if (pointcloud.visible || pointcloud.unvisibleReasons && pointcloud.unvisibleReasons.length == 1 && pointcloud.unvisibleReasons[0].reason == 'displayMode'   &&  pointcloud.root !== null) {//改 visible -> 
 			priorityQueue.push({pointcloud: i, node: pointcloud.root, weight: Number.MAX_VALUE});
 		}
 

+ 28 - 16
src/materials/shaders/pointcloud.vs

@@ -739,7 +739,7 @@ float getPointSize(){
 		pointSize = size;
 	#elif defined attenuated_point_size
 		if(uUseOrthographicCamera){
-			pointSize = size;
+			pointSize = size * 10.0;  //加个乘数
 		}else{  //近大远小,模拟真实mesh,边缘放大
 			//pointSize = size * spacing * projFactor;  //spacing是attribute  为空  如果有这个值就能更自适应填补
             //pointSize = size * uOctreeSpacing * projFactor / 18.0; //直接用cloud的spacing里,不过因为都一样所以可能没有什么意义
@@ -938,23 +938,29 @@ vec3 transformAxis( vec3 direction ) //navvis->4dkk
 }
 
 void main() {
-    bool filtered_by_normal = false; 
+    //bool filtered_by_normal = false; 
+    float normalZ = 0.0;
+
+
 
     #ifdef use_filter_by_normal
-        if(abs(getNormal().z) > 0.3) { //ufilterByNormalThreshold 暂定0.3
+        /*if(abs(getNormal().z) > 0.4) { //ufilterByNormalThreshold 暂定 3
 			// Move point outside clip space space to discard it.
-			//gl_Position = vec4(0.0, 0.0, 2.0, 1.0);   
+			//gl_Position = vec4(0.0, 0.0, 2.0, 1.0);   //gl_Position的可视区域是 x,y,z都是[-1,1]  
             //return;
-            filtered_by_normal = true; //标记一下。不直接不绘制,因为有的法线都是垂直向上
-		}
+            //filtered_by_normal = true; //标记一下。不直接不绘制,因为有的法线都是垂直向上
+             
+		}*/
+        
+        normalZ = abs(getNormal().z);
     #endif
      
     vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
     vViewPosition = mvPosition.xyz;
     gl_Position = projectionMatrix * mvPosition;
     vLogDepth = log2(-mvPosition.z);
-
-
+    
+    
      
     // COLOR
     //加-------------------
@@ -1007,19 +1013,25 @@ void main() {
    
    
     //-------------------        
-
-    //数据集校准时,相机拉远后随着点云密集需降低透明度 
+ 
     #ifdef attenuated_opacity  
-        //vOpacity = uOpacity * exp(-length(-mvPosition.xyz) / 1000.0);  //opacityAttenuation = 1000
-        vOpacity = uOpacity * exp(-mvPosition.z / 1000.0); 
+        //zoom不会改变z 所以这并不是用在分屏时候的
+        //vOpacity = uOpacity * exp(-length(-mvPosition.xyz) / 1000.0);  // e为底的指数函数  opacityAttenuation = 1000
+        vOpacity = uOpacity  * exp(gl_Position.z/50.0); 
         vOpacity = clamp(vOpacity, 0.001, 1.0);          
-        if(filtered_by_normal){//垂直朝相机时降低透明度
-            vOpacity *= 0.05; 
+        /*if(filtered_by_normal){//垂直朝相机时降低透明度 
+            vOpacity *= 0.2; 
             vOpacity = clamp(vOpacity, 0.0001, 0.1);    
-        }  
-        
+        } */  
     #else
         vOpacity = uOpacity;
+        /*if(filtered_by_normal){//垂直朝相机时降低透明度 
+        /*if(filtered_by_normal){//垂直朝相机时降低透明度 
+            vOpacity *= 0.3; 
+            vOpacity = clamp(vOpacity, 0.0001, 0.1);    
+        }*/ 
+        
+        vOpacity *= max(0.1,  (1.0 - normalZ));
     #endif
 	 
 

+ 123 - 83
src/modules/Images360/Images360.js

@@ -36,7 +36,7 @@ let previousView = {
 const HighMapCubeWidth = 1
  
  
-
+const directionFactor = 600 //原先10,几乎只往距离近的走了
  
  
 export class Images360 extends THREE.EventDispatcher{
@@ -129,13 +129,17 @@ export class Images360 extends THREE.EventDispatcher{
         
         
 
-        let click = (e) => {//不用"mouseup" 是因为 mouseup有drag object时也会触发
-            if(Potree.settings.unableNavigate || this.flying  || !e.isTouch && e.button != THREE.MOUSE.LEFT || e.drag &&  e.drag.object)return //拖拽结束时不算
+        let click = (e) => {//不用"mouseup" 是因为 mouseup有drag object时也会触发 
+            if(Potree.settings.unableNavigate || this.flying  || !e.isTouch && e.button != THREE.MOUSE.LEFT || e.drag &&  e.drag.object //拖拽结束时不算
+               || Potree.settings.editType == 'pano' && viewer.modules.PanoEditor.activeViewName != 'mainView'
+            )  return 
             
-            if( e.hoverViewport == viewer.mapViewer.viewports[0]){
-                return viewer.mapViewer.dispatchEvent(e/* {type:'global_click',e } */) 
-            }
             
+            if(Potree.settings.editType != 'pano'){
+                if( e.hoverViewport == viewer.mapViewer.viewports[0]){
+                    return viewer.mapViewer.dispatchEvent(e/* {type:'global_click',e } */) 
+                }
+            }
              
              
             /* if(currentlyHovered && currentlyHovered.pano){
@@ -239,14 +243,14 @@ export class Images360 extends THREE.EventDispatcher{
                         let config = Potree.config.displayMode[mode]
                         let config2 
                         let camera = viewer.scene.getActiveCamera()
-                        if(mode == 'showPanos' && this.flying){//飞完才能切换全景 
+                        if(mode == 'showPanos' && viewer.mainViewport.view.isFlying()){//飞完才能切换全景 
                             let f = ()=>{
                                 if(latestRequestMode == mode){//如果ui还是停在这个模式的话
                                     Potree.settings.displayMode = mode 
                                 }
-                                this.removeEventListener('cameraMoveDone', f)
+                                viewer.mainViewport.view.removeEventListener('flyingDone', f)
                             } 
-                            this.addEventListener('cameraMoveDone', f) //once
+                            viewer.mainViewport.view.addEventListener('flyingDone', f) //once
                             return
                         }
                         if(this.isAtPano() ){//this.currentPano
@@ -260,11 +264,13 @@ export class Images360 extends THREE.EventDispatcher{
                                 //this.modeChanging = true //主要是因为到全景图不会立刻成功
                                 this.flyToPano({
                                     pano: this.findNearestPano(),   
+                                    dealDoneWhenCancel:true,
                                     callback: ()=>{
-                                        //this.modeChanging = false
-                                        if(latestRequestMode == mode ){
-                                            Potree.settings.displayMode = mode 
-                                        } 
+                                        setTimeout(()=>{ //防止循环,所以延迟
+                                           if(latestRequestMode == mode ){
+                                                Potree.settings.displayMode = mode 
+                                            } 
+                                        },1)  
                                     }
                                 }) 
                                 
@@ -367,9 +373,9 @@ export class Images360 extends THREE.EventDispatcher{
                             this.elDisplayModel.value = mode == 'showPointCloud' ? ">>全景" : '>>点云'
                         }
                          
-                        this.panos.forEach(e=>{
+                        /* this.panos.forEach(e=>{
                             viewer.updateVisible(e, 'modeIsShowPanos', mode == 'showPanos', 1,  mode == 'showPanos' ? 'add':'cancel') // 
-                        })
+                        }) */
                          
                          
                         this.dispatchEvent({type:'endChangeMode',mode})  
@@ -411,7 +417,7 @@ export class Images360 extends THREE.EventDispatcher{
                     show = !!show
                     if(show != ifShowMarker){
                         this.panos.forEach(pano=>{
-                            viewer.updateVisible(pano, 'ifShowMarker', show) 
+                            viewer.updateVisible(pano, 'ifShowMarker', show, 1 ) 
                         })
                         //this.emit('markersDisplayChange', show) 
                         ifShowMarker = show
@@ -523,9 +529,9 @@ export class Images360 extends THREE.EventDispatcher{
 		}
 
 
-		for(const image of this.panos){
+		/* for(const image of this.panos){
 			image.mesh.visible = visible && (this.currentPano == null);
-		}
+		} */
 
 		//this.sphere.visible = visible && (this.currentPano != null);
 		this._visible = visible;
@@ -583,12 +589,14 @@ export class Images360 extends THREE.EventDispatcher{
         } */
         //console.log('flyToPano:', toPano.pano.id)
         let done = (makeIt)=>{
-            //console.log('flyToPano done ', toPano.pano.id, makeIt )
-            toPano.deferred && toPano.deferred.resolve(makeIt)
-            if(makeIt) {
-                toPano.callback && toPano.callback()
-                this.flying = false 
+            //console.log('flyToPano done ', toPano.pano.id, makeIt ) 
+            if(makeIt || toPano.dealDoneWhenCancel) {
+                toPano.callback && toPano.callback()  
             }
+            this.flying = false
+            this.updateClosestPano(this.closestPano,false) //飞行结束后取消点击漫游点时得到的closestPano
+            //this.dispatchEvent('cameraMoveDone')
+            toPano.deferred && toPano.deferred.resolve(makeIt)  //测量线截图时发现,resolve需要写在flying=false 后才行。
         }
         if(this.currentPano == toPano.pano && this.isAtPano() && !toPano.target ){
             this.dispatchEvent({type:'flyToPano', toPano})
@@ -693,7 +701,7 @@ export class Images360 extends THREE.EventDispatcher{
                 }
                 done(true);
                 this.updateCube(this.currentPano)
-                this.dispatchEvent('cameraMoveDone')
+                
                 
             }, onUpdate:(progress)=>{ 
                 this.cube.material.uniforms.progress.value = progress 
@@ -702,7 +710,7 @@ export class Images360 extends THREE.EventDispatcher{
                     e.material.uniforms.progress.value = progress 
                 })
             },
-            cancelFun:()=>{this.flying = false},
+            cancelFun:()=>{ done(false) },
             Easing:easeName  
         })
         
@@ -920,7 +928,7 @@ export class Images360 extends THREE.EventDispatcher{
                     viewer.scene.view.setView({position:currentPos, duration: duration*5, 
                         callback: ()=>{ 
                             this.flying = false
-                            this.dispatchEvent('cameraMoveDone')
+                            //this.dispatchEvent('cameraMoveDone')
                         }, 
                         Easing:'easeInOutSine',
                         cancelFun:()=>{this.flying = false}
@@ -972,9 +980,7 @@ export class Images360 extends THREE.EventDispatcher{
         if (this.closestPano) {
             let pano = this.closestPano 
             return this.flyToPano({
-                pano, callback:()=>{
-                    this.updateClosestPano(this.closestPano,false) 
-                } 
+                pano 
             });
             
         } 
@@ -1010,15 +1016,51 @@ export class Images360 extends THREE.EventDispatcher{
         t || (t = 0);
         option1 = void 0 !== option1 ? option1 : .75;
         var o = option2 ? "angle" : "direction";
+        
+        var floor = viewer.modules.SiteModel.currentFloor;
+        var entity = viewer.modules.SiteModel.inEntity;
+        var getHeightDis = (pano)=>{
+            if(!floor.panos.includes(pano) && pano.position.z < this.position.z){ //若是上方的漫游点,就正常走。因为一般不会点击天花板。
+                return this.position.z - pano.position.z  
+            }else{
+                return 0
+            } 
+            
+        }
+        
+        
+        
         var request = [//必要条件 
             Images360.filters.inPanoDirection( this.position, direction, option1), 
             //Images360.filters.isNeighbourPanoTo(this.currentPano), 
             Images360.filters.not(this.currentPano),
-            Images360.filters.isEnabled()
+            Images360.filters.isEnabled(),
+            
+            (pano)=>{ //防止不小心穿越地板到下一层, 尽量走楼梯,实在没有楼梯或楼梯漫游点稀疏的话就通过楼层按钮。
+                let dis = getHeightDis(pano)
+                //console.log('getHeightDis',pano.id,dis)
+                if(dis < 3){//不能超过最大高度差( 最大高度差暂定为接近一层楼的高度。)(最好的解决方案是设置漫游可行)
+                    return true
+                } 
+            } 
         ] 
+        
+        
         var list = [//决胜项目
             Images360.scoreFunctions.distanceSquared(this.position),
-            Images360.scoreFunctions[o]( this.position, direction)
+            Images360.scoreFunctions[o]( this.position, direction),
+            (pano)=>{//尽量不穿越地板到下一层
+                let dis = getHeightDis(pano)
+                return -dis * directionFactor * 0.1;
+            },
+            
+            (pano)=>{ //尽量在一个建筑内行走,这样不易穿墙
+                if(!entity.panos.includes(pano) ){  
+                    return - directionFactor * 0.2;
+                }else{
+                    return 0
+                } 
+            }
         ]; 
          
         this.findRankedByScore(t,request,list,panoSet);
@@ -1027,10 +1069,7 @@ export class Images360 extends THREE.EventDispatcher{
 		
     }
 
-    updateClosestPano(intersect, state) {//距离reticule最近的点  可以是null
-        
-         
-     
+    updateClosestPano(intersect, state) {//hover到的pano   大多数时候是null 
         /* if(this.isAtPano() ){ 
              filterFuncs.push(Images360.filters.not(this.currentPano));
             
@@ -1041,6 +1080,8 @@ export class Images360 extends THREE.EventDispatcher{
         }else{
 			 
         } */
+        
+        
         var pano
         if(this.isAtPano() ){
             if(intersect instanceof Panorama){
@@ -1053,7 +1094,8 @@ export class Images360 extends THREE.EventDispatcher{
             var filterFuncs = [];
             intersect = intersect && intersect.location
             if(!intersect)return
-            pano = Common.find(this.panos,  filterFuncs, [Images360.sortFunctions.floorDistanceToPoint(intersect)]);
+            let sortFuncs = Potree.settings.editType != 'pano'? [Images360.sortFunctions.floorDisSquaredToPoint(intersect)] : [Images360.sortFunctions.disSquaredToPoint(intersect)] 
+            pano = Common.find(this.panos,  filterFuncs, sortFuncs);
         }
         
         
@@ -1082,33 +1124,7 @@ export class Images360 extends THREE.EventDispatcher{
     }
 
 
-
-	/* handleHovering(){
-		let pointer = viewer.inputHandler.pointer;
-		let camera = viewer.scene.getActiveCamera();
-		let domElement = viewer.renderer.domElement;
-
-		let ray = Potree.Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
-
-		// let tStart = performance.now();
-		raycaster.ray.copy(ray);
-		let intersections = raycaster.intersectObjects(this.node.children);
-
-		if(intersections.length === 0){
-			// label.visible = false;
-
-			return;
-		}
-
-		let intersection = intersections[0];
-		currentlyHovered = intersection.object;
-		currentlyHovered.material = smHovered;
-
-		//label.visible = true;
-		//label.setText(currentlyHovered.pano.file);
-		//currentlyHovered.getWorldPosition(label.position);
-	}
-    */
+ 
     
     getTileDirection(){//根据不同dataset的来存储
         var vectorForward = viewer.scene.view.direction.clone() 
@@ -1640,41 +1656,45 @@ export class Images360 extends THREE.EventDispatcher{
     //缩小后继续显示cube呢还是不显示?  不显示的话,就要把cube上的复制到renderTarget上……会不会又崩溃,or没加载的显示???
     
     
-    addPanoData(data, dataset ){
+    addPanoData(data, datasetId ){
         //data[0].file_id = '00019'
          
         if(data.data) data = data.data 
-        if(data.length == 0)console.error(dataset.name , dataset.id + ' 没有漫游点') 
+        if(data.length == 0)console.error(datasetId + ' 没有漫游点') 
         //data = data.sort(function(a,b){return a.id-b.id})
         
         data.forEach((info)=>{  
-            if(Potree.fileServer){
+            //if(Potree.fileServer){
                 info.id = this.panos.length             //把info的id的一长串数字改简单点
-            } 
-            let pano = new Panorama( info,  viewer.transform, this   );
+            //} 
+            let pano = new Panorama( info, this   );
             
             /* pano.mesh.layers.set(Potree.config.renderLayers.marker)
             pano.marker.layers.set(Potree.config.renderLayers.marker)  */
          
             this.panos.push(pano);
-
+            if(Potree.settings.editType == 'pano'){
+                Potree.settings.datasetsPanos[datasetId].panos.push(pano);
+            }
         })  
-          
+         
+
+         
     }
     
     
     loadDone(){
-        viewer.setObjectLayers(this.node, 'marker'/* 'sceneObjects' */)
+        viewer.setObjectLayers(this.node, 'sceneObjects')
         //this.updateCube(/* viewer.bound */)
         
         this.panos.forEach(e=>{
             e.label && viewer.setObjectLayers(e.label, 'bothMapAndScene') 
-        })
+        }) 
         
         this.tileDownloader.setPanoData(this.panos, [] /* , Potree.settings.number */);
 
         {
-            var panosBound = new THREE.Box3
+            /* var panosBound = new THREE.Box3
             this.panos.forEach(pano=>{
                 panosBound.expandByPoint(pano.position)
             }) 
@@ -1686,11 +1706,20 @@ export class Images360 extends THREE.EventDispatcher{
                 bounding:panosBound,
                 size: panosBound.getSize(new THREE.Vector3),
                 center,
-            }
+            } */
+            
+            let minSize = new THREE.Vector3(1,1,1)
+            this.bound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize)
+            
+            
+            viewer.scene.pointclouds.forEach(pointcloud=>pointcloud.getPanosBound()) 
+            
+             
+            
         }
         
         
-
+            
         if(viewer.scene.pointclouds.some(e=>e.panos.length == 0)){
             //console.warn('存在数据集没有pano');
             viewer.hasNoPanoDataset = true
@@ -1700,7 +1729,9 @@ export class Images360 extends THREE.EventDispatcher{
 
     }
     
-            
+    getPano(value, typeName='id'){ //默认找的是id,也可以是sid、uuid
+        return this.panos.find(p=>p[typeName] == value)
+    }       
 };
 
 
@@ -1818,12 +1849,15 @@ Images360.filters = {
 }
 
 
+
+
+
 Images360.scoreFunctions = {
-   direction: function(pos, dir) {
+   direction: function(curPos, dir) {
         return function(pano) { 
-            var pos = pano.position.clone()
-            var n = pos.clone().sub(pos).normalize();
-            return n.dot(dir) * 10 
+            var pos1 = pano.position 
+            var n = pos1.clone().sub(curPos).normalize();
+            return n.dot(dir) * directionFactor  
         }
     },
      
@@ -1843,11 +1877,17 @@ Images360.scoreFunctions = {
 }
 
 Images360.sortFunctions =  {//排序函数,涉及到两个item相减
-    floorDistanceToPoint: function(e) {
+    floorDisSquaredToPoint: function(e) {
+        return function(t, i) {
+            return t.floorPosition.distanceToSquared(e) - i.floorPosition.distanceToSquared(e)
+        }
+    }, 
+    disSquaredToPoint: function(e) {
         return function(t, i) {
-            return t.floorPosition.distanceTo(e) - i.floorPosition.distanceTo(e)
+            return t.position.distanceToSquared(e) - i.position.distanceToSquared(e)
         }
-    },  
+    }, 
+        
 }    
  
 

+ 113 - 67
src/modules/Images360/Panorama.js

@@ -53,88 +53,123 @@ var a = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), THREE.
 //暂时直接用4dkkconsole输出的数据 
 class Panorama extends THREE.EventDispatcher{
 
-	constructor(o, transform, images360){//file, time, longitude, latitude, altitude, course, pitch, roll
+	constructor(o,  images360){//file, time, longitude, latitude, altitude, course, pitch, roll
         super()
-        this.id = o.id;
+        this.id = o.id; //唯一标识
         this.images360 = images360
-        this.transform =  transform
         this.visible = true  //for viewer updateVisible
         this.enabled = true//是否可以走
-
-        this.originPosition = new THREE.Vector3().fromArray(o.dataset_location) 
-        this.originFloorPosition = new THREE.Vector3().fromArray(o.dataset_floor_location)
-        
-        this.originID = parseInt(o.file_id)//"file_id":"00022"对应是原本的4dkk的id --来自vision.txt
-         
-        this.pointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == o.dataset_id) || viewer.scene.pointclouds[0]
-        this.pointcloud.panos.push(this)
-        
-        this.sid = this.pointcloud.sceneCode + '|' + this.originID  //不会更改的标记
-        
-        /* this.pointcloud.addEventListener('isVisible',(e)=>{ 
-             
-            var visible = viewer.getObjVisiByReason(this.pointcloud, 'datasetSelection')
-            console.log('datasetVisi', visible)
-            viewer.updateVisible(this, 'pointcloudVisi', visible) 
-            
-            //e.reason == 'datasetSelection' && viewer.updateVisible(this, 'pointcloudVisi', e.visible) 
-            
-        }) */
         this.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
-            this.marker.visible = e.visible
+            //console.log('pano isVisible', this.id, e.visible) 
+            viewer.updateVisible(this.marker, 'panoVisi', e.visible)
             Potree.settings.showPanoMesh && (this.mesh.visible = e.visible)
             if(e.reason == 'screenshot' || e.visible){
                 this.label && (this.label.visible = e.visible)//截图时隐藏下
             }
         })
         /* 
-        漫游点可见性:
+        漫游点可见性:旧
             level       reason                           类型
             2(最高)buildingChange(不在此楼层)        unvisible   
-            1       modeIsShowPanos(漫游模式)          visible 
+            1       modeIsShowPanos(漫游模式)          visible    //不记得为什么加这个了,所以重写
             0       pointcloudVisi(隐藏了数据集)       unvisible
-        
          */
          
+         /* 
+        漫游点可见性:新
+            level       reason                           类型
+            2(最高)buildingChange(不在此楼层)        unvisible   
+            1       ifShowMarker(marker显示开关)       unvisible 
+            0       pointcloudVisi(隐藏了数据集)       unvisible
+         */ 
          
-         
-         
-         
-        //全景图和Cube的水平采样起始坐标相差90度 
         
-
-        /* if(from4dkk){
-            var qua = o.dataset_orientation 
+        if(Potree.settings.editType == 'pano'){//漫游点拼合编辑
+            this.uuid = o.uuid  //因为有多个数据集 所以会重复
+            this.index = o.index  //下标, 用于visibles
+            this.panosData = o
             
-            var quaternion = new THREE.Quaternion().fromArray(qua)
-                quaternion = new THREE.Quaternion().multiplyQuaternions(quaternion,  rot901);//整张球幕图要旋转下  因为在4dkk里转过,还原。如果是tiles的不用
-            this.quaternion = new THREE.Quaternion(quaternion.x, -quaternion.z, quaternion.y, quaternion.w) //转化坐标
-                
-        }else{ */
-        
+            //数据中原本的位置朝向
+            this.dataPosition = new THREE.Vector3().copy(o.pose.translation) 
+            this.dataQuaternion = new THREE.Quaternion().copy(o.pose.rotation) 
+            this.dataRotation = new THREE.Euler().setFromQuaternion(this.dataQuaternion) 
             
-            var qua = o.dataset_orientation 
-            qua = [qua[1], qua[2], qua[3], qua[0]] 
-            this.quaternion = new THREE.Quaternion().fromArray(qua)
-            this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion 
             
-            this.quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion,  rot90);//全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度
+            //因为位置朝向随着点云位置改变,所以直接运用到点云上,这里清零
+            this.originPosition = new THREE.Vector3()   //{x: 0, y: 0, z: 0}
+            this.quaternion = new THREE.Quaternion()  //{w: 0, x: 0, y: 0, z: 1}
+            //this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion 
+            this.visibles = o.visibles 
+            this.pointcloud = viewer.scene.pointclouds.find(e=>e.panoUuid == o.uuid) 
+            this.pointcloud.panos.push(this)
             
-            this.rotation4dkk = new THREE.Euler().setFromQuaternion(this.quaternion4dkk)
-            this.rotation = new THREE.Euler().setFromQuaternion(this.quaternion) 
-         //}
-         
+            const height = 1.4; //相机高度
+            this.originFloorPosition = this.originPosition.clone()
+            this.originFloorPosition.z -= height
+            
+            
+            /* this.originPosition = new THREE.Vector3().copy(o.pose.translation)  //{x: 0, y: 0, z: 0}
+            this.quaternion = new THREE.Quaternion().copy(o.pose.rotation) //{w: 0, x: 0, y: 0, z: 1}
+            //this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion 
+            this.visibles = o.visibles 
+            this.pointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == o.uuid) 
+            this.pointcloud.panos.push(this)
+            
+            const height = 1.5; //相机高度
+            this.originFloorPosition = this.originPosition.clone()
+            this.originFloorPosition.z -= height
+             */
+            
+            
+        }else{
+            this.originPosition = new THREE.Vector3().fromArray(o.dataset_location) 
+            this.originFloorPosition = new THREE.Vector3().fromArray(o.dataset_floor_location)
+            
+            this.originID = parseInt(o.file_id)//"file_id":"00022"对应是原本的4dkk的id --来自vision.txt
+             
+            this.pointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == o.dataset_id) || viewer.scene.pointclouds[0]
+            this.pointcloud.panos.push(this)
+            
+            this.sid = this.pointcloud.sceneCode + '|' + this.originID  //不会更改的标记
          
-            //this.quaternion1 = Potree.Utils.QuaternionFactory.fromArray(o.dataset_orientation)
-            //同quaternion
+            //全景图和Cube的水平采样起始坐标相差90度 
+            
+
+            /* if(from4dkk){
+                var qua = o.dataset_orientation 
+                
+                var quaternion = new THREE.Quaternion().fromArray(qua)
+                    quaternion = new THREE.Quaternion().multiplyQuaternions(quaternion,  rot901);//整张球幕图要旋转下  因为在4dkk里转过,还原。如果是tiles的不用
+                this.quaternion = new THREE.Quaternion(quaternion.x, -quaternion.z, quaternion.y, quaternion.w) //转化坐标
+                    
+            }else{ */
+            
+                
+                var qua = o.dataset_orientation 
+                qua = [qua[1], qua[2], qua[3], qua[0]] 
+                this.quaternion = new THREE.Quaternion().fromArray(qua)
+                this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion 
+                
+                this.quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion,  rot90);//全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度
+                
+                this.rotation4dkk = new THREE.Euler().setFromQuaternion(this.quaternion4dkk)
+                
+             //}
+             
+             
+                //this.quaternion1 = Potree.Utils.QuaternionFactory.fromArray(o.dataset_orientation)
+                //同quaternion
 
-      
-        //let xy = this.transform.forward([this.longitude, this.latitude]);  
-        this.file = `https://4dkk.4dage.com/images/images${Potree.settings.number}/pan/high/${this.id}.jpg`
+          
+            //let xy = this.transform.forward([this.longitude, this.latitude]);  
+            this.file = `https://4dkk.4dage.com/images/images${Potree.settings.number}/pan/high/${this.id}.jpg`
+            
+             
+            
+        }
+        this.rotation = new THREE.Euler().setFromQuaternion(this.quaternion) 
         this.build()
         this.transformByPointcloud() //初始化位移
-         
-        
         
         {//tile
             this.minimumTiledPanoLoaded = !1;
@@ -185,7 +220,7 @@ class Panorama extends THREE.EventDispatcher{
     
     build(){
           
-        let mesh = new THREE.Mesh(sg, sm); 
+        /* let mesh = new THREE.Mesh(sg, sm); 
         mesh.scale.set(1, 1, 1);
         mesh.material.transparent = true;
         mesh.material.opacity = 0.75;
@@ -199,7 +234,12 @@ class Panorama extends THREE.EventDispatcher{
         })
         mesh.addEventListener('click',(e)=>{
             this.images360.focusPano(this)
-        })
+        }) 
+        this.mesh = mesh;
+        if(!Potree.settings.showPanoMesh) mesh.visible = false
+        this.images360.node.add(mesh)
+        */
+        
         { // orientation
             //var {course, pitch, roll} = this;
             //mesh.quaternion.copy(this.quaternion) 
@@ -212,8 +252,8 @@ class Panorama extends THREE.EventDispatcher{
             //console.log(this.quaternion)
             //this.quaternion = quaternion
         } 
-        this.mesh = mesh;
-        if(!Potree.settings.showPanoMesh) mesh.visible = false
+        
+        
          
         let marker = new THREE.Mesh(planeGeo, getMarerMat() ) 
             marker.up.set(0,0,1)
@@ -222,8 +262,10 @@ class Panorama extends THREE.EventDispatcher{
             
              
         this.marker = marker 
+        if(Potree.settings.editType == 'pano'){
+            viewer.updateVisible(marker, 'panoEdit', false, 4)
+        }
         
-        this.images360.node.add(mesh)
         this.images360.node.add(marker)
         Potree.settings.isTest && this.createTextLabel()
         
@@ -266,11 +308,17 @@ class Panorama extends THREE.EventDispatcher{
     setPosition(position, floorPosition){
         this.position = position
         this.floorPosition = floorPosition
-        this.mesh.position.copy(this.position)
+        //this.mesh.position.copy(this.position)
         this.marker.position.copy(this.floorPosition) 
         this.marker.position.z+=0.1//会被点云遮住
         if(this.label){
-            this.label.position.copy(this.floorPosition), this.label.position.z+=0.2
+             
+            if(Potree.settings.editType == 'pano'){
+                this.label.position.copy(this.position)
+            }else{
+                this.label.position.copy(this.floorPosition)
+            }
+            this.label.position.z+=0.2
             this.label.update()
         }
           
@@ -448,15 +496,13 @@ class Panorama extends THREE.EventDispatcher{
         this.removeTextLabel()
         this.label = new TextSprite(Object.assign({},
            labelProp, {text: this.id }) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`}
-        );
-    
+        ); 
         this.images360.node.add(this.label);
         this.floorPosition && this.label.position.copy(this.floorPosition)
     }
     
     removeTextLabel(){
-        if(this.label){
-            
+        if(this.label){ 
             this.label.parent.remove(this.label);
         }
     }

+ 9 - 3
src/modules/clipModel/Clip.js

@@ -24,7 +24,7 @@ var Clip = {
                     return true 
                 } 
             },  300)  
-        }
+        } 
     },
 
     enter:function(){
@@ -211,7 +211,7 @@ var Clip = {
     getTarget:function(boundCenter){//box位置。要找一个有点云的地方。方案1相机位置, 方案2接近相机的漫游点, 方案3接近中心的漫游点。选择方案2,因最大概率有点云
         var target = new THREE.Vector3()
         var cameraPos = viewer.images360.position;
-        var pano = Common.find(viewer.images360.panos , [], [Images360.sortFunctions.floorDistanceToPoint(cameraPos)]);
+        var pano = Common.find(viewer.images360.panos , [], [Images360.sortFunctions.floorDisSquaredToPoint(cameraPos)]);
         if(pano){
             target.copy(pano.position) 
             target.setZ(boundCenter.z)
@@ -226,7 +226,13 @@ var Clip = {
         
     }, */
     
-    download:function(notCrop){
+    download:function( ){
+        
+        if(this.getIntersectPointcloud().length == 0){
+            return null
+        }
+        
+        
         var visiPointclouds = viewer.scene.pointclouds.filter(e=> viewer.getObjVisiByReason(e, 'datasetSelection'))
         let data = {   
             transformation_matrix: visiPointclouds.map((cloud)=>{

+ 134 - 41
src/modules/datasetAlignment/Alignment.js

@@ -2,49 +2,137 @@
 import * as THREE from "../../../libs/three.js/build/three.module.js";
 import SplitScreen from "../../utils/SplitScreen"
 import math from "../../utils/math"
-import {EventDispatcher} from "../../EventDispatcher.js";
  
 
 var Alignment = {
     SplitScreen, 
     handleState:null,  //操作状态 'translate'|'rotate'
     bus: new THREE.EventDispatcher(), 
+    history:[], 
+    prepareRecord : true, 
+    
+    writeToHistory(content){ 
+        if(!this.prepareRecord)return;
+        this.prepareRecord = false
+        this.history.push(content)
+    },
+    
+    
+    undo(){//撤销一步 
+        let last = this.history.pop();
+        last && last.forEach(item=>{
+            this.applyTemp(item)  
+        })
+        
+    },
+    
+    applyTemp(item){ 
+        var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id+p.name == item.sid)
+            pointcloud.orientationUser = item.orientationUser
+            pointcloud.translateUser = item.translateUser
+            this.setMatrix( pointcloud )
+    },
+    getTemp(pointclouds){//记录最近一次保存后的状态,便于恢复
+        pointclouds = pointclouds || viewer.scene.pointclouds
+        return pointclouds.map(e=>{
+            return {
+                sid : e.dataset_id+e.name,
+                orientationUser : e.orientationUser,
+                translateUser : e.translateUser.clone(),
+            }
+        } )
+    },
+    
+    
+    
     init:function(){ 
-        let rotateInfo  
+        let rotateInfo   
         
         viewer.fpControls.addEventListener("transformPointcloud",(e)=>{ 
-            if(e.pointcloud.dataset_id == Potree.settings.originDatasetId){//禁止手动移动初始数据集
+            if(e.pointclouds[0].dataset_id == Potree.settings.originDatasetId){//禁止手动移动初始数据集
                 return this.bus.dispatchEvent('forbitMoveOriginDataset') 
             }
- 
+            
+            
+            this.writeToHistory(this.getTemp(e.pointclouds) ) 
+            
         
             if(this.handleState == 'translate'){
-                Alignment.translate(e.pointcloud,e.moveVec)
+                e.pointclouds.forEach(cloud=>Alignment.translate(cloud, e.moveVec))
+                
+                
             }else if(this.handleState == 'rotate'){
-                 
-                let center = e.pointcloud.translateUser //移动到的位置就是中心
-                if(!rotateInfo){  
-                    rotateInfo = {
-                        orientationUser : e.pointcloud.orientationUser,
-                        vecStart : new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0),
-                        pointcloud: e.pointcloud
-                    }  
+                if(Potree.settings.editType == 'pano'){
+                    
+                    let center = e.intersectStart //旋转中心是mousedown的位置
+                    if(e.intersectPoint.equals(center))return
+                    if(!rotateInfo){  
+                        rotateInfo = {
+                            orientationUser : e.pointclouds[0].orientationUser,
+                            //vecStart : e.moveVec, // 首次移动向量 只有八个方向,精度太小,所以延迟 
+                            pointclouds: e.pointclouds
+                        } 
+                        this.bus.dispatchEvent({type:'rotateStart', startPoint:center})
+                        return
+                    }else if(!rotateInfo.vecStart){  
+                        let vec = new THREE.Vector3().subVectors(e.intersectPoint, center).setZ(0)
+                        if(vec.length() * e.camera.zoom >  30){  //在屏幕上距离初始点有一定距离后开始算
+                            //console.log('moveVec',vec)
+                            rotateInfo.vecStart = vec
+                        }
+                    }else{
+                        
+                        let vec = new THREE.Vector3().subVectors(e.intersectPoint, center).setZ(0)
+                        let angle = math.getAngle(rotateInfo.vecStart,vec,'z')   
+                        let diffAngle = rotateInfo.orientationUser + angle - rotateInfo.pointclouds[0].orientationUser
+                        
+                        rotateInfo.pointclouds.forEach(cloud=>{
+                            
+                            let centerNoTranfrom = Potree.Utils.datasetPosTransform({ toDataset: true, pointcloud:cloud, position: center }) //中心点在数据集中的位置
+                            Alignment.rotate(cloud, null, diffAngle)
+                             
+                            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 }) //中心点的现在位置
+                                
+                        })
+                        
+                        
+                    }
+                    this.bus.dispatchEvent({type:'rotate', endPoint: e.intersectPoint})
+                    
                 }else{ 
-                    let vec = new THREE.Vector3().subVectors(e.intersectPoint, 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 center = e.pointclouds[0].translateUser //移动到的位置就是中心
+                    if(e.intersectPoint.equals(center))return
+                    if(!rotateInfo){  
+                        rotateInfo = {
+                            orientationUser : e.pointclouds[0].orientationUser,
+                            vecStart : new THREE.Vector3().subVectors(e.intersectStart, center).setZ(0),
+                            pointcloud: e.pointclouds[0]
+                        }  
+                    }else{ 
+                        let vec = new THREE.Vector3().subVectors(e.intersectPoint, 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)
+                    }
+                }    
             } 
         })
         
         
         viewer.fpControls.addEventListener("end",(e)=>{ 
-            rotateInfo = null
+            rotateInfo = null 
+            this.prepareRecord = true
         })
         
-        
+        viewer.inputHandler.addEventListener('keydown',e=>{ 
+            if(e.keyCode == 90 && e.event.ctrlKey){//Z
+                this.undo()
+            } 
+        })  
+		 
         
         // cursor:
         
@@ -52,6 +140,7 @@ var Alignment = {
             if(e.drag)return  //仅在鼠标不按下时更新:
             
             let handleState = Alignment.handleState
+            
             if(e.hoverViewport.alignment && handleState && e.hoverViewport.alignment[handleState]){
                 if(handleState == 'translate'){
                     if( e.intersectPoint && e.intersectPoint.location ){ 
@@ -84,8 +173,11 @@ var Alignment = {
                 })
             }                
         }
-        viewer.addEventListener('global_mousemove',updateCursor)  
-        viewer.addEventListener('global_drop',updateCursor)//拖拽结束  
+        
+        if(Potree.settings.editType != 'pano'){
+            viewer.addEventListener('global_mousemove',updateCursor)  
+            viewer.addEventListener('global_drop',updateCursor)//拖拽结束  
+        }
             
         
         
@@ -131,7 +223,7 @@ var Alignment = {
 
         //viewer.updateModelBound();
         pointcloud.updateBound()
-         
+        pointcloud.getPanosBound()  
 
 
     },
@@ -145,18 +237,12 @@ var Alignment = {
         Alignment.setMatrix(pointcloud)
     },
     
-    saveTemp:function(){//记录最近一次保存后的状态,便于恢复
-        this.originData = viewer.scene.pointclouds.map(e=>{
-            return {
-                id : e.dataset_id,
-                orientationUser : e.orientationUser,
-                translateUser : e.translateUser.clone(),
-            }
-        } )
-    },
+    
+    
+    
     enter:function(){
-        this.saveTemp()  
-         
+        //this.saveTemp()  
+        this.originData = this.getTemp() 
         
         SplitScreen.splitScreen4Views({alignment:true})
          
@@ -165,8 +251,8 @@ var Alignment = {
         }) 
         
         viewer.viewports.find(e=>e.name == 'mapViewport').alignment = {rotate:true,translate:true};
-        viewer.viewports.find(e=>e.name == 'Right').alignment = {translate:true};
-        viewer.viewports.find(e=>e.name == 'Back').alignment = {translate:true};
+        viewer.viewports.find(e=>e.name == 'right').alignment = {translate:true};
+        viewer.viewports.find(e=>e.name == 'back').alignment = {translate:true};
         
         
         this.editing = true
@@ -177,20 +263,22 @@ var Alignment = {
     leave:function(){
         this.switchHandle(null)
         
-        this.originData.forEach(e=>{//恢复
+        /* this.originData.forEach(e=>{//恢复
             var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id == e.id)
             this.translate(pointcloud, new THREE.Vector3().subVectors(e.translateUser , pointcloud.translateUser))
             this.rotate(pointcloud, null, e.orientationUser - pointcloud.orientationUser)
+        }) */
+        this.originData.forEach(e=>{//恢复
+            this.applyTemp(e)
         })
         
         
-        
         SplitScreen.recoverFrom4Views()
         viewer.images360.panos.forEach(pano=>{
             viewer.updateVisible(pano.mapMarker, 'split4Screens', true)
         }) 
         this.editing = false
-        
+        this.history.length = 0
         viewer.updateFpVisiDatasets()
         
     } 
@@ -205,12 +293,17 @@ var Alignment = {
         viewer.dispatchEvent({
             type : "CursorChange", action : "remove",  name:"rotatePointcloud" 
         })
+        
+        this.bus.dispatchEvent({type:'switchHandle' , state })
+        
+        
+
     },
     
     
     save: function(){//保存所有数据集的位置和旋转
         let callback = ()=>{//保存成功后
-            this.saveTemp();
+            this.originData = this.getTemp()   //this.saveTemp();
             //需要修改 测量线的position。漫游点已经实时修改了
             
             viewer.scene.measurements.forEach(e=>e.transformByPointcloud())

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1048 - 0
src/modules/panoEdit/panoEditor.js


+ 15 - 14
src/modules/siteModel/BuildingBox.js

@@ -168,7 +168,7 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
                 this.lineMesh.name = 'buildingLines'
                 this.lineMesh.visible = false            
                 this.add(this.lineMesh) 
-                viewer.setObjectLayers(this.lineMesh, 'sceneObjects' ) 
+                viewer.setObjectLayers(this.lineMesh, 'bothMapAndScene' ) 
             }
             
             
@@ -202,12 +202,14 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
         let bound2 = pointcloud.bound;
         if(!bound.intersectsBox(bound2)) return 0;
         
-        let min = Math.min(this.zMin, bound2.min.z);
-        let max = Math.max(this.zMax, bound2.max.z); 
-        let height1 = this.zMax - this.zMin
-        let height2 = bound2.max.z-bound2.min.z
-        let coverHeight = height1 + height2 - (max-min)//重叠高度
         
+        let {zMin , zMax} =  this.getRealZ()
+        let min = Math.min(zMin, bound2.min.z);
+        let max = Math.max(zMax, bound2.max.z); 
+        let height1 = zMax - zMin
+        let height2 = bound2.max.z-bound2.min.z
+        let coverHeight = height1 + height2 - (max-min)//重叠高度 <=0是没重叠
+         
         let boxPoints = pointcloud.getUnrotBoundPoint() //获取tightBound的四个点。 如果是有旋转角度的点云,这个和pointcloud.bound的四个点是不一致的,覆盖面积小于pointcloud.bound
        
         let areaWhole = 0  
@@ -342,7 +344,7 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
         if(this.buildType == 'floor'){
             viewer.setObjectLayers(mesh, 'siteModelMapUnvisi' ) //楼层默认在地图不显示,为了不会叠加透明度
         }else{
-            viewer.setObjectLayers(mesh, 'sceneObjects' )
+            viewer.setObjectLayers(mesh, 'bothMapAndScene' )
         }
          
         
@@ -356,9 +358,8 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
     addMarker(o={} ){
         if(this.buildType=='floor')return; //楼层不需要marker
         
-        let marker = new Sprite({mat:this.getMarkerMaterial('default'), sizeInfo: markerSizeInfo, dontFixOrient: true, name:"building_marker"} )
-        
-        marker.renderOrder = 3 
+        let marker = new Sprite({mat:this.getMarkerMaterial('default'), renderOrder : 3, sizeInfo: markerSizeInfo, dontFixOrient: true, name:"building_marker"} )
+       
          
         viewer.setObjectLayers(marker, 'siteModeOnlyMapVisi' ) 
         
@@ -772,11 +773,11 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
             })
               
             if(this.buildType == 'floor'){
-                viewer.setObjectLayers(this.box, 'sceneObjects' ) 
+                viewer.setObjectLayers(this.box, 'bothMapAndScene' ) 
                 viewer.setObjectLayers(this.buildParent.box, 'siteModelMapUnvisi' ) //当选中floor或room时,building在地图不可见
             }
         }else if(this.buildType == 'room'){
-            viewer.setObjectLayers(this.buildParent.box, 'sceneObjects' )
+            viewer.setObjectLayers(this.buildParent.box, 'bothMapAndScene' )
             viewer.setObjectLayers(this.buildParent.buildParent.box, 'siteModelMapUnvisi' )
         }
         
@@ -810,12 +811,12 @@ export class BuildingBox extends ctrlPolygon{//建筑实体,包括building, fl
             
             if(this.buildType == 'floor'){
                 viewer.setObjectLayers(this.box, 'siteModelMapUnvisi' ) 
-                viewer.setObjectLayers(this.buildParent.box, 'sceneObjects' ) 
+                viewer.setObjectLayers(this.buildParent.box, 'bothMapAndScene' ) 
             }
             
         }else if(this.buildType == 'room'){
             viewer.setObjectLayers(this.buildParent.box, 'siteModelMapUnvisi' )
-            viewer.setObjectLayers(this.buildParent.buildParent.box, 'sceneObjects' )
+            viewer.setObjectLayers(this.buildParent.buildParent.box, 'bothMapAndScene' )
         }
         
         this.lineMesh.visible = false

+ 92 - 162
src/modules/siteModel/SiteModel.js

@@ -20,7 +20,7 @@ var SiteModel = {
     meshGroup: new THREE.Object3D,
     inEntity : null,
     lastPos: new THREE.Vector3(Infinity,Infinity,Infinity),
-    updateCadByEntity:true,
+    
     
     init: function(){
         
@@ -29,11 +29,18 @@ var SiteModel = {
         this.meshGroup.name = 'siteModel' 
         this.SplitScreen = SplitScreen
         
+        if(Potree.settings.editType == 'pano'){
+            return
+        }
+        
+        
+        
+        
         this.createHeightPull();
         
         if(Potree.settings.isTest && ifDrawDatasetBound){
             viewer.addEventListener('allLoaded',()=>{
- 
+            
                 viewer.scene.pointclouds.forEach(pointcloud=>{
                     let boxPoints = pointcloud.getUnrotBoundPoint();
                       
@@ -58,14 +65,6 @@ var SiteModel = {
             viewer.addEventListener('camera_changed', e => {
                 this.updateEntityAt()
             })
-
-
-
-            /* viewer.addEventListener('allLoaded',()=>{
-                viewer.images360.panos.forEach(pano=>{//初始化为不可见
-                    viewer.updateVisible(pano, 'buildingChange', false)  
-                }) 
-            }) */
         }
         
         
@@ -91,12 +90,16 @@ var SiteModel = {
         
     }, 
     
+    
+    
     updateEntityAt(force){
-        if(!this.entities.length || this.editing) return
-        Common.intervalTool.isWaiting('sitemodelCameraInterval', ()=>{ //延时update,防止卡顿
-            let currPos = viewer.scene.getActiveCamera().position
-         
+        //if(!this.entities.length || this.editing) return //编辑时也要根据位置显示不同楼层的漫游点与cad
+        
+        let fun = ()=>{ //延时update,防止卡顿
+            let currPos = viewer.mainViewport.view.position
+            
             if(force || !currPos.equals(this.lastPos)){
+                //console.log('currPos ', currPos.toArray())
                 this.lastPos.copy(currPos)
                 let entity;
                 
@@ -113,7 +116,7 @@ var SiteModel = {
                     let lastFloor = this.currentFloor   //oldEntity ? oldEntity.buildType == 'floor' ? oldEntity : oldEntity.buildType == 'room' ? oldEntity.buildParent : null : null; //基本只会是floor或room
                     let currentFloor = entity ? entity.buildType == 'floor' ? entity : entity.buildType == 'room' ? entity.buildParent : null : null; //基本只会是floor或room
                     if(currentFloor != lastFloor || force){
-                        //console.log('改变了floor',lastFloor,currentFloor)
+                        console.log('改变了floor',lastFloor,currentFloor)
                         this.currentFloor = currentFloor
                         this.bus.dispatchEvent({type:'FloorChange',currentFloor}) 
                     }
@@ -122,102 +125,17 @@ var SiteModel = {
                 force = false
                 return true 
             }
-        }, 500)  
-
+        }
         
+        if(force)fun()
+        else Common.intervalTool.isWaiting('sitemodelCameraInterval', fun , 500)  
         
-    },
-
 
-    updateCadVisibles(floor){
-        if(!this.updateCadByEntity)return
-        /* var visiClouds = this.floorplansVisi = floor ? findDatasetsAtFloor(floor) : []
-         
-        viewer.scene.pointclouds.forEach(pointcloud=>{ 
-            var floorplan = viewer.mapViewer.mapLayer.getFloorplan(pointcloud.dataset_id)
-            var visi = visiClouds.includes(pointcloud) 
-            if(floorplan){
-                viewer.updateVisible(floorplan.objectGroup, 'buildingChange', visi) 
-            }else if(!visi){
-                let changeVisi = (e)=>{
-                    viewer.updateVisible(e.floorplan.objectGroup, 'buildingChange', this.floorplansVisi.includes(pointcloud)) 
-                    viewer.mapViewer.mapLayer.removeEventListener('floorplanLoaded',  changeVisi)
-                    console.log('updateCadVisibles加载后更改显示',e)
-                } 
-                viewer.mapViewer.mapLayer.addEventListener('floorplanLoaded',  changeVisi)
-            }         
-        })
-        viewer.mapViewer.mapLayer.needUpdate = true   */
         
         
-        var visiClouds = this.floorplansVisi = floor ? findDatasetsAtFloor(floor) : []
-        viewer.updateCadVisibles(visiClouds)
-        
-        function findDatasetsAtFloor(entity){//找当前楼层需要显示哪些数据集。
-            //必要条件: 如果数据集有漫游点的话,需要包含>20%的漫游点。   (防止重叠体积很大但其实一个漫游点都不包含)
-            //充分条件(在符合必要条件之后还应该满足至少一个充分条件): 重叠体积>50%   或   包含>50%的漫游点 
-            const ratio1 = 0.2, ratio2 = 0.5, ratio3 = 0.5
-            var pointclouds = viewer.scene.pointclouds.filter(e=>{
-                
-                if(e.panos.length){
-                    var insidePanos = e.panos.filter(a=>entity.ifContainsPoint(a.position));
-                    let panoCountRatio = insidePanos.length / e.panos.length
-                    if(panoCountRatio < ratio1){ 
-                        return false
-                    }
-                    
-                    if(panoCountRatio > ratio2)return true 
-                } 
-                 
-                 
-                let volume = entity.intersectPointcloudVolume(e);
-                let volumeRatio = volume / entity.getVolume(true) //注:hole加入计算
-                if(volumeRatio > ratio3){
-                    return true 
-                } 
-            })
-            return pointclouds
-        }
     },
-    
-
-
-
-
-    /* updatePanosVisible(lastEntity, entity, force){//根据所在楼层更新marker可见性。当在楼层中时,只显示当前楼层的marker。
-        if(!entity ){//暂定:不在任何一个实体中时 显示全部漫游点(和平面图显示不同,平面图就一个都不显示) 
-            viewer.images360.panos.forEach(pano=>{
-                viewer.updateVisible(pano, 'buildingChange', true, 2)  
-            })
-             
-            this.updateCadVisibles(null)
-        }else{
-            let lastFloor = lastEntity ? lastEntity.buildType == 'floor' ? lastEntity : lastEntity.buildType == 'room' ? lastEntity.buildParent : null : null; //基本只会是floor或room
-            let currentFloor = entity ? entity.buildType == 'floor' ? entity : entity.buildType == 'room' ? entity.buildParent : null : null; //基本只会是floor或room
-            if(currentFloor != lastFloor || force){
-                //console.log('改变了floor',lastFloor,currentFloor)
-                if(lastFloor){
-                    lastFloor.panos.forEach(pano=>{
-                        viewer.updateVisible(pano, 'buildingChange', false, 2)  
-                    })
-                     
-                    
-                }else{//重置为全部不可见
-                    viewer.images360.panos.forEach(pano=>{
-                        viewer.updateVisible(pano, 'buildingChange', false, 2)  
-                    })
-                }
-                if(currentFloor){
-                    currentFloor.panos.forEach(pano=>{
-                        viewer.updateVisible(pano, 'buildingChange', true, 2)  
-                    })
-                    
-                    this.updateCadVisibles(currentFloor) 
-                }
 
-            }    
-        } 
-    }, */
+ 
 
 
 
@@ -238,7 +156,7 @@ var SiteModel = {
             if(e.name != 'mapViewport'){
                 e.layersAdd('siteModelMapUnvisi') 
             }
-            if(e.name == 'Right' || e.name == 'Back'){
+            if(e.name == 'right' || e.name == 'back'){
                 e.layersAdd('siteModeSideVisi') 
             }
         })   
@@ -266,7 +184,7 @@ var SiteModel = {
             if(e.name != 'mapViewport'){
                 e.layersRemove('siteModelMapUnvisi') 
             } 
-            if(e.name == 'Right' || e.name == 'Back'){
+            if(e.name == 'right' || e.name == 'back'){
                 e.layersRemove('siteModeSideVisi') 
             }
         })
@@ -285,10 +203,6 @@ var SiteModel = {
     
      
     
-    /* startSetSiteModel:function(pos, type){//开始创建空间模型(非编辑状态的,不绘制)
-        if(this.editing)return //编辑中不允许重新创建
-        this.clear() 
-    }, */
      
     addFloor:function(parent, dirType, sid, name){//dirType:'top'|'bottom'在上方建还是下方。如果建筑中没有楼层,默认在基底建一个
         let buildType = 'floor' 
@@ -666,6 +580,10 @@ var SiteModel = {
     },
     
     transform:function(pos, type){
+        if(Potree.settings.editType == 'pano'){ // 模型不经转换
+            return new THREE.Vector3().copy(pos).setZ(0);
+        }
+        
         if(type == 'toDataset'){
             let point = Potree.Utils.datasetPosTransform({ toDataset: true, position: pos.clone(), datasetId: Potree.settings.originDatasetId })
             return new THREE.Vector2().copy(point)
@@ -819,23 +737,7 @@ var SiteModel = {
     },
     
     
-    /* selectFloor:function(floor){ 
-        this.buildings.forEach(e=>e.unselect())
-        floor.select()
-        this.selected = floor
-        this.height_pull_box.visible = true
-        
-        this.fitPullBox()
-    },
-    selectBuilding:function(building){
-        this.buildings.forEach(e=>e.unselect())
-        building.select()
-    }
-    selectRoom:function(room){
-        this.buildings.forEach(e=>e.unselect())
-        room.select()
-    }
-     */
+     
     
     
     
@@ -1017,7 +919,7 @@ var SiteModel = {
             building: 0, floor: 1, room: 2 
         }
         
-        let traverse = (parent)=>{//返回第一个符合标准的实体
+        let traverse = (parent, buildType)=>{//返回第一个符合标准的实体
             let contains;
             if(location instanceof THREE.Vector3){
                 contains = parent.ifContainsPoint(location)
@@ -1032,9 +934,6 @@ var SiteModel = {
                     return parent
                 }else{  
                     for(let i=0,len=parent.buildChildren.length; i<len; i++){
-                        /* if(traverse(parent.buildChildren[i])){
-                            return parent.buildChildren[i]
-                        } */ 
                         let result1 = traverse(parent.buildChildren[i])
                         if(result1) return result1
                     }  
@@ -1043,7 +942,10 @@ var SiteModel = {
             
         }
         //因为建筑可能重叠,所以需要先找到最接近其中心的建筑物
-        result = Common.sortByScore(this.buildings, [], [(building)=>{   //写法类似pointInWhichPointcloud
+        result = Common.sortByScore(this.buildings, [(building)=>{
+            return traverse(building, 'building') //在building中
+            
+        }], [(building)=>{   //写法类似pointInWhichPointcloud
             let boundingBox = building.getBound()
             let center = boundingBox.getCenter(new THREE.Vector3())
             let position = location instanceof THREE.Vector3 ? location : location.position
@@ -1055,18 +957,9 @@ var SiteModel = {
         
         let building = result && result[0] && result[0].score > 1 && result[0].item 
         if(buildType == 'building' || !building)return building
-        result = traverse(building) 
-         
-         
-         
-        /* for(let i=0,len=this.buildings.length; i<len; i++){
-            result = traverse(this.buildings[i])
-            
-            if(result){break}
-        }  */
+        result = traverse(building, buildType) 
+          
          
-        
-        
         /* if(!result && buildType == 'room'){//如果要找的是room, 且按刚才的顺序找不到的话,就单独从所有rooms中找一遍。因为room可能不在floor和building内。
             let rooms = this.entities.filter(e=>e.buildType == 'room');
             result = rooms.find(e=>e.ifContainsPoint(position))
@@ -1092,7 +985,7 @@ var SiteModel = {
                     if(entity.ifContainsPoint(pano.position)){
                         entity.panos.push(pano)
                     } 
-                })
+                }) 
             }) 
         } 
         
@@ -1144,21 +1037,9 @@ var SiteModel = {
     
     
     findEntityForDataset:function(){//为每一个数据集寻找它所属的最小实体
-        var entities = this.entities.filter(e=>e.buildType == 'room' || e.buildType == 'floor' && e.buildChildren.length == 0)
+        /* var entities = this.entities.filter(e=>e.buildType == 'room' || e.buildType == 'floor' && e.buildChildren.length == 0)
         viewer.scene.pointclouds.forEach(pointcloud=>{
              
-            /* let volumes = []
-            entities.forEach(entity=>{
-                let volume = entity.intersectPointcloudVolume(pointcloud)
-                volumes.push({entity, volume})
-            })
-            volumes.sort((a,b)=>{ return b.volume-a.volume }) 
-            //console.log(volumes)
-            if(volumes.length == 0 || volumes[0].volume < 10e-4){//如果约等于0 
-                pointcloud.belongToEntity = null
-            }else{
-                pointcloud.belongToEntity = volumes[0].entity; 
-            } */
             let cloudVolume = pointcloud.getVolume()
             let scores = []
             entities.forEach(entity=>{
@@ -1179,19 +1060,62 @@ var SiteModel = {
             }else{
                 pointcloud.belongToEntity = scores[0].entity; 
             } 
+        }) */
+        
+        let getScores = (pointcloud, entities, cloudVolume)=>{ 
+            let scores = []
+            entities.forEach(entity=>{
+                let volume = entity.intersectPointcloudVolume(pointcloud) 
+                //注:默认已经findPanos过
+                let panos = entity.panos.filter(e=>pointcloud.panos.includes(e));
+                let panoCount = panos.length
+                
+                let score = volume / cloudVolume + panoCount / pointcloud.panos.length
+                 
+                scores.push({entity, volume, panoCount, score}) 
+                
+            })
+            scores.sort((a,b)=>{ return b.score-a.score }) 
+            
+            return scores
+        }
+        
+        
+        viewer.scene.pointclouds.forEach(pointcloud=>{ //先判断父级,如果父集不通过就不判断子级。
+            let cloudVolume = pointcloud.getVolume()
+            let entities = this.buildings
+            
+            while(1){
+                let scores = getScores(pointcloud, entities, cloudVolume)
+                if(scores.length == 0 || scores[0].volume/cloudVolume < 0.0001 && scores[0].volume < 3 ){//如果约等于0 
+                    pointcloud.belongToEntity = null
+                    break;
+                }else{
+                    entities = scores[0].entity.buildChildren
+                    if(entities.length == 0){
+                        pointcloud.belongToEntity = scores[0].entity; 
+                        break;
+                    }
+                } 
+            }  
         })
         
+        
+        
+        
+        
         /*
+        旧版:
         只需要考虑 floor 和 room, 因为building的只有一个基底没高度
         floor 和 room 在空间中没有完全的从属关系,因为room可以超出floor之外。所以直接混在一起来查找,但要排除有房间的楼层。 
+        (现在改为层层递进查找,否则数据集包含entity多的,会直接挂载到体积最大的房间里,即使看起来主体点云并不在该房间)
+        
         
         有的数据集虽然很高,但只有近地面的部分才是主体,这部分一般含有全部漫游点。为了防止上层的实体因体积较大而分数高,就把包含漫游点的个数也加入考虑。
         
         重叠体积大、且包含漫游点最多的最小实体将会拥有该点云。
         
-        
-        
-        
+        期望: 最好不挂载到最小子级,因为现在有房间都到房间里了。
         */
     }
     
@@ -1217,6 +1141,9 @@ var SiteModel = {
          
     }
     ,
+    
+    
+    
     gotoEntity(id, isNearBy, duration=1000) { 
         var entity = this.entities.find(e => e.sid == id)
         let aimPano
@@ -1248,9 +1175,9 @@ var SiteModel = {
             }
 
             let boundingBox = entity.getBound()
-            let position = boundingBox.getCenter(new THREE.Vector3())
+            let position = boundingBox.getCenter(new THREE.Vector3())   //中心点不一定在entity中,比如半环形建筑(所以要不要改成到漫游点呢)
             
-            if(viewer.modules.Clip.editing){
+            if(viewer.modules.Clip && viewer.modules.Clip.editing){
                 viewer.modules.Clip.bus.dispatchEvent({type:'flyToPos', position})
             }else{
                 if(math.closeTo(position, viewer.images360.position)) return 'posNoChange'  
@@ -1271,6 +1198,9 @@ var SiteModel = {
         let boundSize = boundingBox.getSize(new THREE.Vector3())
         let center = boundingBox.getCenter(new THREE.Vector3())
         this.SplitScreen.focusOnObject(boundSize, center) 
+        
+        this.gotoEntity(id, false, 0)
+        
     },
     
     removeIlligalArchi(){//删除marker数量小于3个的建筑,当保存时

+ 114 - 56
src/navigation/FirstPersonControls.js

@@ -16,11 +16,9 @@
 import * as THREE from "../../libs/three.js/build/three.module.js";
 import {Buttons} from "../defines.js";
 import {Utils} from "../utils.js"; 
-import cameraLight from "../utils/cameraLight.js";
-
-
+import cameraLight from "../utils/cameraLight.js"; 
+import Common from "../utils/Common.js"; 
  
-
 export class FirstPersonControls extends THREE.EventDispatcher {
 	constructor (viewer, viewport) {
 		super();
@@ -89,7 +87,8 @@ export class FirstPersonControls extends THREE.EventDispatcher {
                     mode = (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'pan' : 'scale' 
                 } 
             }else{
-                mode = e.buttons === Buttons.LEFT && (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan'
+                //mode = e.buttons === Buttons.LEFT && (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan'
+                mode = e.buttons === Buttons.LEFT && camera.type != 'OrthographicCamera' ? 'rotate' : 'pan'
             }
             //console.log('mode  ', mode )
             let moveSpeed = this.currentViewport.getMoveSpeed();
@@ -100,46 +99,77 @@ export class FirstPersonControls extends THREE.EventDispatcher {
 			}
             
                 
-            if (mode.includes('rotate')) {//旋转   (为什么开启调试时旋转很慢?)
+            if (mode.includes('rotate')) {//旋转 
 				
                 //来自panoramaControl updateRotation
-                 
                 if(!this.pointerDragStart){
                    return this.pointerDragStart = e.pointer.clone()
                 }
                 
-                let _matrixWorld = camera.matrixWorld
-                camera.matrixWorld = new THREE.Matrix4;//unproject 前先把相机置于原点 
                 
-                var e1 = new THREE.Vector3(this.pointerDragStart.x,this.pointerDragStart.y,-1).unproject(camera)
-                  , t = new THREE.Vector3(e.pointer.x,e.pointer.y,-1).unproject(camera)
-                  , i = Math.sqrt(e1.x * e1.x + e1.z * e1.z)
-                  , n = Math.sqrt(t.x * t.x + t.z * t.z)
-                  , o = Math.atan2(e1.y, i)
-                  , a = Math.atan2(t.y, n);
+                
+                let view = this.scene.view;
+                if(Potree.settings.rotAroundPoint && e.intersectPoint &&  !viewer.images360.isAtPano()){//定点旋转:   以当前intersect的点为target旋转,不改点在屏幕中的位置
+                    let distance = camera.position.distanceTo(e.intersectPoint.location) 
+                      
+                    //按照orbitControl的方式旋转:
+                    let rotationSpeed = 2.5;  
+                    
+                    this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed;
+                    this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed;
+
                    
-                this.pitchDelta +=  o - a  //上下旋转
-                e1.y = 0,
-                t.y = 0; 
+                    //先更新一下相机:    
+                    this.update() 
+                    view.applyToCamera(camera)
+                    //然后得到新的相机角度下,原先点在屏幕中的位置所对应的3d点现在的坐标。只需要平移一下新旧坐标差值即可。
+                    let newPointerDir = viewer.inputHandler.getMouseDirection(this.pointerStart).direction.clone().multiplyScalar(distance)
+                    let pivot = new THREE.Vector3().addVectors(camera.position, newPointerDir)  //新的3d点
+                     
+                    let moveVec = new THREE.Vector3().subVectors(pivot, e.intersectPoint.location)
+                    
+                    this.translationWorldDelta.copy(moveVec.negate()) 
+                    //立即更新下,防止因update和此drag频率不同而打滑。
+                    this.update()
+                    view.applyToCamera(camera)
+                    
+               
+                }else{
+                  
+                    
+                    let _matrixWorld = camera.matrixWorld
+                    camera.matrixWorld = new THREE.Matrix4;//unproject 前先把相机置于原点 
+                    
+                    var e1 = new THREE.Vector3(this.pointerDragStart.x,this.pointerDragStart.y,-1).unproject(camera)
+                      , t = new THREE.Vector3(e.pointer.x,e.pointer.y,-1).unproject(camera)
+                      , i = Math.sqrt(e1.x * e1.x + e1.z * e1.z)
+                      , n = Math.sqrt(t.x * t.x + t.z * t.z)
+                      , o = Math.atan2(e1.y, i)
+                      , a = Math.atan2(t.y, n);
+                       
+                    this.pitchDelta +=  o - a  //上下旋转
+                    e1.y = 0,
+                    t.y = 0; 
+                    
+                    var s = Math.acos(e1.dot(t) / e1.length() / t.length());
+                    
+                    if(!isNaN(s)){
+                        var yawDelta = s    //左右旋转 
+                        this.pointerDragStart.x > e.pointer.x && (yawDelta *= -1) 
+                        this.yawDelta += yawDelta
+                    } 
+                    
+                    
+                    //console.log('rotate:', this.pitchDelta, e.pointer.toArray(), this.pointerDragStart.toArray())
+                    
+                    
+                    this.pointerDragStart.copy(e.pointer)
+                     
+                    camera.matrixWorld = _matrixWorld ;
+                    
                 
-                var s = Math.acos(e1.dot(t) / e1.length() / t.length());
                 
-                if(!isNaN(s)){
-                    var yawDelta = s    //左右旋转 
-                    this.pointerDragStart.x > e.pointer.x && (yawDelta *= -1) 
-                    this.yawDelta += yawDelta
                 } 
-                
-                
-                //console.log('rotate:', this.pitchDelta, e.pointer.toArray(), this.pointerDragStart.toArray())
-                
-                
-                this.pointerDragStart.copy(e.pointer)
-                 
-                camera.matrixWorld = _matrixWorld ;
-                
-                
-                
 			} 
             
             if (mode.includes('pan')) {//平移 
@@ -148,15 +178,41 @@ export class FirstPersonControls extends THREE.EventDispatcher {
                    
                     //console.log(e.drag.pointerDelta, e.pointer, e.drag.end)
                     let moveVec = Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, camera )//最近一次移动向量
-                    
+                  
+                    let pointclouds;
                     let handleState = window.viewer.modules.Alignment.handleState
-                    if(viewport.alignment && handleState && viewport.alignment[handleState] && e.drag.intersectStart.pointcloud){
+                    
+                    let a = e.buttons === Buttons.LEFT && viewport.alignment && handleState && viewport.alignment[handleState] 
+                    if(Potree.settings.editType == 'pano'){//右键平移视图、左键操作点云 
+                        let PanoEditor = viewer.modules.PanoEditor
+                             
+                        if(a && PanoEditor.selectedPano){
+                            if(!PanoEditor.selectedGroup || !PanoEditor.checkIfAllLinked({group:PanoEditor.selectedGroup}) ){
+                                if(handleState == 'translate' && ( e.drag.intersectStart.pointclouds && Common.getMixedSet(PanoEditor.selectedClouds, e.drag.intersectStart.pointclouds).length  || PanoEditor.selectedPano.hovered)//拖拽到点云上 或 circle
+                                    || handleState == 'rotate' ) 
+                                {  
+                                    pointclouds = PanoEditor.selectedClouds
+                                }  
+                            }else{
+                                console.warn('选中的漫游点连通了整个数据集,不允许移动')
+                            } 
+                        }
+                        
+                        if(!pointclouds && e.buttons === Buttons.LEFT && viewport.alignment.rotateSide){
+                            return PanoEditor.rotateSideCamera(e.drag.pointerDelta.x)
+                        }
+                    }else{ 
+                        pointclouds = a && e.drag.intersectStart.pointcloud && [e.drag.intersectStart.pointcloud]
+                    }
+                      
+                    if(pointclouds){
                         this.dispatchEvent({
                             type : "transformPointcloud", 
                             intersectPoint: e.intersectPoint.orthoIntersect,   
                             intersectStart: e.drag.intersectStart.orthoIntersect,
                             moveVec,        
-                            pointcloud: e.drag.intersectStart.pointcloud,
+                            pointclouds, 
+                            camera
                         }) 
                     }else{ 
                         
@@ -361,8 +417,9 @@ export class FirstPersonControls extends THREE.EventDispatcher {
             this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer);
         }
         let prepareRotate = (e)=>{ 
-            this.pointerDragStart = e.pointer.clone() 
-            //console.log('prepareRotate')
+            this.pointerDragStart = e.pointer.clone()  
+            this.pointerStart = e.intersectPoint && e.intersectPoint.location && e.intersectPoint.location.clone().project(e.dragViewport.camera) //intersect点在屏幕中的位置
+            //console.log('prepareRotate' )
         }
         let preparePan = (e)=>{//触屏的pan点云    还是会偏移
             this.pointerDragStart = e.pointer.clone() 
@@ -375,7 +432,7 @@ export class FirstPersonControls extends THREE.EventDispatcher {
         this.viewer.addEventListener('global_mousedown'/* 'startDragging' */, (e)=>{
             if(!this.enabled)return
             this.setCurrentViewport(e)
-            prepareRotate(e)
+            prepareRotate(e) 
         })
         
         
@@ -439,23 +496,19 @@ export class FirstPersonControls extends THREE.EventDispatcher {
 
 
     setCurrentViewport(o={}){//add
-        if(!this.enabled && !o.force)return
+        if(!this.enabled && !o.force )return
         if(o.hoverViewport && this.currentViewport != o.hoverViewport ){
-            this.currentViewport = o.hoverViewport 
-            
-            if(this.currentViewport.camera.type == 'OrthographicCamera'){
-                this.lockElevationOri = true
-                this.lockRotation = true
-            }else{
-                this.lockElevationOri = false
-                this.lockRotation = false
-            }
-     
-     
-     
+            this.currentViewport = o.hoverViewport  
 			//this.viewer.setMoveSpeed(this.currentViewport.radius/100);
             this.setFPCMoveSpeed(this.currentViewport)
         }
+        if(this.currentViewport.camera.type == 'OrthographicCamera'){
+            this.lockElevationOri = true
+            this.lockRotation = true
+        }else{
+            this.lockElevationOri = false
+            this.lockRotation = false
+        }
     }
 
      
@@ -546,10 +599,10 @@ export class FirstPersonControls extends THREE.EventDispatcher {
 		}
 	}
 
-	update (delta) {
+	update (delta=1) {
         if(!this.enabled)return
  
-        
+        //console.log('update')
 		let view = this.currentViewport.view  
 
 		{ // cancel move animations on user input
@@ -647,13 +700,15 @@ export class FirstPersonControls extends THREE.EventDispatcher {
 		{ // apply rotation
 			let yaw = view.yaw;
 			let pitch = view.pitch;
-
+             
+            
 			yaw += this.yawDelta /* * delta; */
 			pitch += this.pitchDelta/*  * delta; */
 
 			view.yaw = yaw;
 			view.pitch = pitch;
             
+
             
             this.yawDelta = 0
             this.pitchDelta = 0 
@@ -678,6 +733,9 @@ export class FirstPersonControls extends THREE.EventDispatcher {
 				this.translationWorldDelta.y /* * delta */,
 				this.translationWorldDelta.z /* * delta */
 			);
+             
+            
+            
             //this.translationWorldDelta.set(0,0,0)
 		}
 

+ 44 - 25
src/navigation/InputHandler.js

@@ -573,18 +573,24 @@ export class InputHandler extends THREE.EventDispatcher {
 			}
 
             // check for a click 
+            
             if(pressDistance < Potree.config.clickMaxDragDis && pressTime<Potree.config.clickMaxPressTime){
-                if(this.hoveredElements && this.hoveredElements[0] && this.hoveredElements[0].object._listeners['click']){
-                    if (this.logMessages) console.log(`${this.constructor.name}: click ${clicked.name}`);
-                    this.hoveredElements[0].object.dispatchEvent($.extend(  
-                        this.getEventDesc(e,isTouch),
-                        {
-                            type: 'click',
-                            pressDistance     
-                        }
-                    )); 
-                  
-                }else{
+                let clickElement
+                if(this.hoveredElements){
+                    clickElement = this.hoveredElements.find(e=>e.object._listeners['click']) 
+                    if(clickElement){
+                        if (this.logMessages) console.log(`${this.constructor.name}: click ${clickObject.name}`);
+                        clickElement.object.dispatchEvent($.extend(  
+                            this.getEventDesc(e,isTouch),
+                            {
+                                type: 'click',
+                                pressDistance     
+                            }
+                        )); 
+                    }  
+                }
+                
+                if(!clickElement){
                     this.viewer.dispatchEvent($.extend(  
                         this.getEventDesc(e,isTouch),
                         {
@@ -592,9 +598,7 @@ export class InputHandler extends THREE.EventDispatcher {
                             pressDistance
                         }
                     )); 
-                   
-                }
-                 
+                } 
             }
             
 			 
@@ -692,9 +696,9 @@ export class InputHandler extends THREE.EventDispatcher {
         }
     }
 
-    getIntersect(viewport,camera,onlyGetIntersect, pickWindowSize){
+    getIntersect(viewport,camera, onlyGetIntersect, pickWindowSize, dontIntersectPointcloud){
         //add
-        let intersectPoint = viewport.noPointcloud? null : Utils.getMousePointCloudIntersection(
+        let intersectPoint = (viewport.noPointcloud || dontIntersectPointcloud)? null : Utils.getMousePointCloudIntersection(
             viewport,
             this.mouse,
             this.pointer, 
@@ -704,7 +708,7 @@ export class InputHandler extends THREE.EventDispatcher {
             {pickClipped: true, isMeasuring: this.isMeasuring, pickWindowSize}
         );
         
-           
+            
         //console.log(viewport.name , intersectPoint &&  intersectPoint.location )
         
         if(viewport.camera.type == 'OrthographicCamera'/*  == 'mapViewport' */){ 
@@ -771,9 +775,12 @@ export class InputHandler extends THREE.EventDispatcher {
         let intersectPoint
         
         
-        if(e.onlyGetIntersect || !this.drag || this.drag.object){ //没有拖拽物体,但按下鼠标了的话,不intersect
-            intersectPoint = this.getIntersect(viewport,camera,e.onlyGetIntersect, e.pickWindowSize)
-        }
+        if(e.onlyGetIntersect || !this.drag || this.drag.object || viewport.alignment ){ //没有拖拽物体,但按下鼠标了的话,不intersect
+        
+            let dontIntersectPointcloud =  this.drag && viewport.alignment && Potree.settings.editType == 'pano' || viewer.images360.flying // flying 时可能卡顿
+            //console.log('dontIntersectPointcloud',dontIntersectPointcloud)
+            intersectPoint = this.getIntersect(viewport,camera,e.onlyGetIntersect, e.pickWindowSize, dontIntersectPointcloud) //数据集多的时候卡顿
+        } 
         
         if(e.onlyGetIntersect){ 
             return intersectPoint
@@ -783,6 +790,10 @@ export class InputHandler extends THREE.EventDispatcher {
         
         let hoveredElements = []
         
+        
+        /* if(intersectPoint && intersectPoint.pointcloud){
+            console.log(intersectPoint.pointcloud.name)
+        } */
             
         
         if (this.drag) {//有拖拽(不一定拖拽了物体, 也不一定按下了鼠标)
@@ -835,10 +846,10 @@ export class InputHandler extends THREE.EventDispatcher {
                 }
             
                 let curr = hoveredElements.map(a => a.object).find(a => true);
-                let prev = this.hoveredElements.map(a => a.object).find(a => true);
+                let prev = this.lastMouseoverElement //this.hoveredElements.map(a => a.object).find(a => true);
 
                 if(curr !== prev){
-                    if(curr){
+                    if(curr){ 
                         if (this.logMessages) console.log(`${this.constructor.name}: mouseover: ${curr.name}`);
                         curr.dispatchEvent({
                             type: 'mouseover',
@@ -852,6 +863,8 @@ export class InputHandler extends THREE.EventDispatcher {
                             object: prev,
                         });
                     }
+                    
+                    this.lastMouseoverElement = curr
                 }
 
                 if(hoveredElements.length > 0){
@@ -1086,7 +1099,7 @@ export class InputHandler extends THREE.EventDispatcher {
 
 					if (hasInteractableListener) {
 						interactables.push(node);
-					}
+					}                        
 				}
 			});
 		}
@@ -1099,12 +1112,18 @@ export class InputHandler extends THREE.EventDispatcher {
         raycaster.camera = camera //add
         
       
+        if(camera.type == "OrthographicCamera"){//使无论多远,threshold区域都是一样宽的
+            raycaster.params.Line.threshold = 20/camera.zoom  
+        }else{
+            raycaster.params.Line.threshold = 0.2; 
+        }
+        raycaster.params.Line2 = {threshold :20 } //拓宽的lineWidth
+        
         
         
-		raycaster.params.Line.threshold = 0.2;
         //raycaster.layers.enableAll()//add
         viewer.setCameraLayers(raycaster,   //设置能识别到的layers(如空间模型里只有mapViewer能识别到marker)
-            ['sceneObjects','mapObjects','measure','marker','transformationTool'],
+            ['sceneObjects','mapObjects','measure',  'transformationTool'],
             this.hoverViewport && this.hoverViewport.extraEnableLayers
         )
         

+ 2 - 1
src/navigation/OrbitControls.js

@@ -83,7 +83,8 @@ export class OrbitControls extends THREE.EventDispatcher{
             let ndrag = e.drag.pointerDelta.clone()//.add(new THREE.Vector2(1,1)).multiplyScalar(0.5)
             ndrag.y *= -1
 
-			if (mode == 'rotate') {
+			if (mode == 'rotate') { 
+                 
 				this.yawDelta += ndrag.x * this.rotationSpeed;
 				this.pitchDelta += ndrag.y * this.rotationSpeed;
 

+ 1 - 1
src/objects/Magnifier.js

@@ -206,7 +206,7 @@ export default class Magnifier extends THREE.Object3D {//放大镜or望远镜
         }
         
         
-        viewer.scene.view.addEventListener('setViewDone',()=>{
+        viewer.scene.view.addEventListener('flyingDone',()=>{
             if(!this.visible)return
             let pickWindowSize = 100
             let intersect = viewer.inputHandler.getIntersect(viewer.mainViewport, viewer.mainViewport.camera, true, pickWindowSize )

+ 3 - 6
src/objects/Reticule.js

@@ -39,7 +39,7 @@ export default class Reticule extends THREE.Mesh{
         this.matrixAutoUpdate = false 
         
 
-
+        this.hide(0)
 
         //viewer.inputHandler.addInputListener(this);
         viewer.addEventListener('global_mousemove',this.move.bind(this))
@@ -68,12 +68,9 @@ export default class Reticule extends THREE.Mesh{
             this.judgeTex() 
         })
         
+         
         
-        
-        
-       
-        
-        viewer.setObjectLayers(this, 'reticule' )
+        viewer.setObjectLayers(this, 'sceneObjects' )
     }
 
     judgeTex(){ 

+ 2 - 2
src/objects/Sprite.js

@@ -24,7 +24,7 @@ export default class Sprite extends THREE.Mesh{
         let update = (e)=>{
             this.update(e) 
         }
-        viewer.mapViewer.addEventListener("camera_changed",  update) 
+        viewer.mapViewer && viewer.mapViewer.addEventListener("camera_changed",  update) 
         viewer.addEventListener("camera_changed",  update) 
         /* if(viewer.viewports.length == 1){//直接更新。如果有多个不在这更新,在"render.begin"
             this.update(e)
@@ -38,7 +38,7 @@ export default class Sprite extends THREE.Mesh{
         viewer.addEventListener("render.begin", applyMatrix) //before render  //magnifier时要禁止吗
             
         this.addEventListener('dispose', ()=>{
-            viewer.mapViewer.removeEventListener("camera_changed",  update) 
+            viewer.mapViewer && viewer.mapViewer.removeEventListener("camera_changed",  update) 
             viewer.removeEventListener("camera_changed",  update) 
             viewer.removeEventListener("raycaster", applyMatrix)        //before render
             viewer.removeEventListener("render.begin", applyMatrix)

+ 5 - 6
src/objects/tool/Measure.js

@@ -21,7 +21,7 @@ let textColor = new THREE.Color(config.measure.textColor)
 var markerMats;  
 var lineMats;  
 var planeMats 
-
+ 
 const lineDepthInfo = {
     clipDistance : 4,//消失距离
     occlusionDistance: 1,//变为backColor距离 
@@ -29,8 +29,7 @@ const lineDepthInfo = {
 const LabelDepthInfo = {
     clipDistance : 6,//消失距离
     occlusionDistance: 2,//变为backColor距离 
-}
-
+} 
 
 const markerSizeInfo = {
     minSize : 25 ,  maxSize : 65,   nearBound : 0.2, farBound : 4,
@@ -289,8 +288,8 @@ export class Measure extends ctrlPolygon{
 		
 	};
 
- 
-    
+  
+      
 	addMarker (o={}) {
          
         let marker = new Sprite({mat:this.getMarkerMaterial('default'), sizeInfo: markerSizeInfo, name:"measure_point"} )
@@ -303,7 +302,7 @@ export class Measure extends ctrlPolygon{
         marker.addEventListener('drop',(e)=>{
             viewer.inputHandler.dispatchEvent({type: 'isMeasuring',  v:false, cause:'stopDragging'}  )
         })
-        
+         
         let edge
 		{ // edges 
             edge = LineDraw.createFatLine( [ ],{material:this.getLineMat('edgeDefault')} ) 

+ 3 - 0
src/objects/tool/mapClipBox.js

@@ -273,6 +273,9 @@ export class mapClipBox extends ctrlPolygon {
         })
         bar.addEventListener('drop',()=>{
             lastPos = null 
+            viewer.dispatchEvent({
+                type : "CursorChange", action : "remove",  name:"mapClipRotate"
+            })
         })
         
         let line = LineDraw.createLine([new THREE.Vector3, new THREE.Vector3(0,lineLen,0)],{color})

+ 22 - 12
src/settings.js

@@ -119,20 +119,25 @@ const config = {//配置参数   不可修改
             pointBudget :1*1000*1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000)
         }   
         ,
+        panoEdit:{
+            maxLevelPercent: 1,  //在远处时由于pointBudget限制而展示稀疏,凑近时就变为最高质量了
+            pointBudget :1*1000*1000, //避免卡顿
+        },
+        
         low:{//highPerformance
             maxLevelPercent: 0.4, //最小为0
             percentByUser:true, //如果用户定义了percent,使用用户的
-            pointBudget :1*1000*1000,
+            pointBudget : 1*1000*1000,
         }, 
         middle:{//balanced  //不同场景相同级别所产生的numVisibleNodes和numVisiblePoints不同,如果分层比较细,可能要到level8才能看清,那么level5看到的点就很大且很少,如隧道t-e2Kb2iU
             maxLevelPercent: 0.7,
             percentByUser:true,
-            pointBudget:4*1000*1000,
+            pointBudget:browser.isMobile() ? 4*1000*1000 : 2*1000*1000, 
         },
         high:{//highQuality
             maxLevelPercent: 1, 
             percentByUser:true,
-            pointBudget:8*1000*1000,
+            pointBudget:browser.isMobile() ? 8*1000*1000 : 4*1000*1000,
         }
         //browser.isMobile() 时要不要限制下pointBudget,还是让用户自己调低质量?
         //minNodeSize?
@@ -171,7 +176,8 @@ const config = {//配置参数   不可修改
         
     },
     material:{//初始化
-        pointSize: 0.1, //0.4,//0.75,//0.4, 相对大小
+        pointSize: 0.1,  
+        realPointSize : 0.1,//实际上的ui滑动条默认大小(兼容旧的版本)
         minSize: 0.1,
         maxSize: 10000,
         pointSizeType: 'ATTENUATED', //'ADAPTIVE'//'ADAPTIVE' \ FIXED //ADAPTIVE的在小房间里大小会不太匹配,但在远景似乎更好
@@ -183,7 +189,7 @@ const config = {//配置参数   不可修改
         absolutePanoramaSize: 1.3 ,//全景漫游时的size  是fixed的模式
         
         //sizeAtPanoRtEDL : 2000,  
-        
+        pointColor:'#ffffff',
         
         
         //sizeAddAtPanoRtEDL : 0.5, //全景模式静止时的size
@@ -201,8 +207,8 @@ const config = {//配置参数   不可修改
         skybox: 1,
         pointcloud: 11,
         sceneObjects:0,//default
-        marker: 0,
-        reticule: 3,
+         
+        
         measure:4,  
         magnifier:5, 
         magnifierContent:16,
@@ -212,8 +218,8 @@ const config = {//配置参数   不可修改
         map:8,
         mapObjects:9,//default
         
-        bothMapAndScene:15,
-        
+         
+        bothMapAndScene: 3,
         
         siteModeOnlyMapVisi:12,//只能mapViewer可见
         siteModelMapUnvisi:13,//只有mapViewer不可见
@@ -232,6 +238,9 @@ const config = {//配置参数   不可修改
         
     },
     
+    panosEdit:{
+         
+    },
      
     tiling: {
         panoPreRenderRepeatDelay: 2500,
@@ -341,6 +350,7 @@ function getPrefix(){
 
  
 let settings = {//设置   可修改
+    editType : '',
     number: '', //场景序号
     originDatasetId:'',//场景原本的数据集id,应该就是数据集第一个吧
     isOfficial:false,
@@ -387,14 +397,14 @@ let settings = {//设置   可修改
         whenPointCloud:true,
         map:true,
     },
-    
+    rotAroundPoint:true,//点云模式是否能绕intersectPoint旋转
     tourTestCameraMove:false, //测试镜头时,不移动真实的镜头, 只移动frustum
     cameraAniSmoothRatio : 20, //镜头动画平滑系数,越高越平滑
     urls  : $.extend({}, config.urls), 
     
     
-    
-        
+    //panoEdit:
+    datasetsPanos:{}
 }
 
  

+ 158 - 8
src/start.js

@@ -136,9 +136,6 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
             //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。
             var location = viewer.transform.lonlatToLocal.forward(locationLonLat)  //transform.inverse()
             //初始化位置 
-            pointcloud.matrixAutoUpdate = false   //最好禁止updateMatrix  直接使用matrixWorld
-            pointcloud.orientationUser = 0  
-            pointcloud.translateUser = new THREE.Vector3;
             
             viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud) 
             
@@ -195,13 +192,12 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                     material.minSize =  config.minSize
                     material.maxSize =  config.maxSize   
                     material.pointSizeType = config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
-                    pointcloud.changePointSize(config.pointSize)  //material.size =  config.pointSize;
+                    pointcloud.changePointSize(config.realPointSize)  //material.size =  config.pointSize;
                     pointcloud.changePointOpacity(1)
                     material.shape = Potree.PointShape.SQUARE; 
                     pointcloud.color = pointcloud.material.color = dataset.color  
                     pointcloud.dataset_id = dataset.id;//供漫游点找到属于的dataset点云
-                    pointcloud.timeStamp = timeStamp
-                    pointcloud.panos = [] 
+                    pointcloud.timeStamp = timeStamp 
                     transformPointcloud(pointcloud,dataset)
                     scene.addPointCloud(pointcloud);
                     pointcloudLoaded ++;
@@ -209,7 +205,7 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
                         
                     Potree.loadPanos(dataset.id, (data) => { 
                         //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
-                        viewer.images360.addPanoData(data, dataset )
+                        viewer.images360.addPanoData(data, dataset.id )
                         panosLoaded ++; 
                         if(panosLoaded == datasetLength){
                             panosLoadDone() 
@@ -280,6 +276,160 @@ var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
  
  
  
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+//=======================================================================
+/* 
+            漫游点编辑
+ */
+//=======================================================================
+
+
+var panoEditStart = function(dom, mapDom, number, fileServer, webSite){
+    Potree.settings.editType = 'pano'
+    
+    
+    Potree.settings.unableNavigate = true
+    
+    
+    let viewer = new Potree.Viewer(dom , mapDom); 
+    let Alignment = viewer.modules.Alignment
+	viewer.setEDLEnabled(false);
+    viewer.setFOV(config.view.fov); 
+    viewer.loadSettingsFromURL(); 
+    let datasetLoaded = 0;
+  
+    if(!Potree.settings.isOfficial){ 
+        viewer.loadGUI(() => {
+            viewer.setLanguage('en'); 
+            $("#menu_tools").next().show();
+            $("#panos").show();
+            $("#alignment").show(); 
+            viewer.toggleSidebar();
+        });
+        Potree.settings.sizeFitToLevel = true 
+    }
+    
+    var pointcloudLoadDone = function( ){//所有点云cloud.js加载完毕后 
+         
+        
+        
+        viewer.scene.pointclouds.forEach(c=>{
+            transformPointcloud(c)
+        })
+     viewer.images360.loadDone() 
+        viewer.scene.add360Images(viewer.images360); 
+        
+        viewer.updateModelBound()
+        let {boundSize, center} = viewer.bound
+       
+        Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` , null, 12)
+          
+        viewer.scene.view.setView({ 
+            position: center.clone().add(new THREE.Vector3(10,5,10)), 
+            target: center
+        })
+         
+        viewer.dispatchEvent({type:'loadPointCloudDone'})
+    
+        if(!Potree.settings.UserPointDensity){
+            Potree.settings.UserPointDensity = 'panoEdit'//'middle' 
+        }
+         
+        Potree.Log('loadPointCloudDone  点云加载完毕', null, 10)  
+        
+        viewer.dispatchEvent('allLoaded');
+    }
+    
+    
+    var transformPointcloud = (pointcloud )=>{ //初始化位置  
+        viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud) 
+         
+        let orientation = pointcloud.panos[0].dataRotation.z
+        let location = pointcloud.panos[0].dataPosition.clone().negate()
+        Alignment.rotate(pointcloud, null,  orientation  )   
+        Alignment.translate(pointcloud, location )  
+        
+        pointcloud.updateMatrixWorld()
+          
+    }  
+     
+     
+     
+         
+    
+    let loadPanosDone = Potree.loadPanosDone = (datasetId, panoData)=>{ //一个数据集获取到它的panos后
+        
+        Potree.settings.datasetsPanos[datasetId] = {panoData, panos:[]}
+         
+        console.log('panoData', datasetId, panoData)
+        
+        let panoCount = panoData.length
+        let pointcloudLoaded = 0
+        
+        let datasetsCount = Object.keys(Potree.settings.datasetsPanos).length
+       
+       
+        panoData.forEach((pano, index)=>{
+            let cloudPath = `${Potree.scriptPath}/data/panoEdit/uuidcloud/${pano.uuid}/cloud.js`
+            let name = datasetId + '-'+pano.uuid
+            let timeStamp = 0
+            pano.index = index //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标! 
+            
+            Potree.loadPointCloud(cloudPath, name , name, timeStamp, e => { //开始加载点云
+                let scene = viewer.scene;
+                let pointcloud = e.pointcloud; 
+                let config = Potree.config.material
+                let material = pointcloud.material; 
+                material.minSize =  config.minSize
+                material.maxSize =  config.maxSize   
+                material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
+                pointcloud.changePointSize( 0.2 /* config.realPointSize  */   )  //material.size =  config.pointSize;
+                pointcloud.changePointOpacity(1)
+                material.shape = Potree.PointShape.SQUARE; 
+                pointcloud.color = config.pointColor  
+                pointcloud.dataset_id = datasetId;   //多个点云指向一个datasetId
+                pointcloud.panoUuid = pano.uuid
+                pointcloud.timeStamp = timeStamp
+                  
+                //transformPointcloud(pointcloud, pano)
+                scene.addPointCloud(pointcloud);
+                pointcloudLoaded ++;
+                 
+                if(pointcloudLoaded == panoCount ){
+                    datasetLoaded ++
+                    viewer.images360.addPanoData(panoData ,  datasetId )
+                    if(datasetLoaded == datasetsCount){
+                        pointcloudLoadDone()
+                    }
+                    
+                }
+                
+            })
+            
+        })
+        
+    }
+    
+    if(!Potree.settings.isOfficial){ 
+        Potree.settings.datasetsPano = {'testDataset':null}
+        Potree.loadPanosInfo( data=>{loadPanosDone('testDataset',  data.sweepLocations)} )  
+    
+    }
+    
+}
+ 
 /* var changeLog = ()=>{ //如果移动端加了test反而出不来bug的话,用这个
         
     
@@ -330,7 +480,7 @@ changeLog() */
  
  
  
-export {start}
+export {start, panoEditStart}
 
 
 

+ 6 - 1
src/utils.js

@@ -453,6 +453,8 @@ export class Utils {
             
         }
         
+        
+        let allPointclouds = [] 
 		for(let pointcloud of pointclouds){ 
             
 			let point = pointcloud.pick(viewer, viewport, camera, ray, pickParams );
@@ -462,7 +464,9 @@ export class Utils {
 			if(!point){
 				continue;
 			}
-
+            allPointclouds.push(pointcloud)
+            
+            
 			let distance = camera.position.distanceTo(point.position);
 
 			if (distance < closestDistance) {
@@ -492,6 +496,7 @@ export class Utils {
 				location: closestIntersection,
 				distance: closestDistance,
 				pointcloud: selectedPointcloud,
+                pointclouds: allPointclouds, //add
 				point: closestPoint
 			};
 		} else {

+ 48 - 6
src/utils/Common.js

@@ -1,3 +1,8 @@
+
+
+import * as THREE from "../../libs/three.js/build/three.module.js";
+
+
 var Common = {
     sortByScore: function(list, request, rank){
         var i = request ? Common.filterAll(list, request) : list
@@ -25,12 +30,18 @@ var Common = {
     
     
     //---------------
-    find : function(list, request, rank) {
-        var i = request ? Common.filterAll(list, request) : list
-        return 0 === i.length ? null : (rank && rank.forEach(function(e) {
-            i = Common.stableSort(i, e)
-        }),
-        i[0])
+    find : function(list, request, rank, sortByScore  ) { 
+        if(sortByScore){
+            var r = this.sortByScore(list, request, rank)
+            return  r && r[0] && r[0].item   
+        }else{
+            var i = request ? Common.filterAll(list, request) : list
+            return 0 === i.length ? null : (rank && rank.forEach(function(e) {
+                i = Common.stableSort(i, e)
+            }),
+            i[0])  
+        }
+        
     }
     
     ,
@@ -236,6 +247,37 @@ var Common = {
             },delayTime)
         }, */
     }
+    ,
+    pushToGroupAuto : function(items, groups, recognizeFunction){//自动分组。 items是将分到一起的组合。items.length = 1 or 2. 
+    
+        recognizeFunction = recognizeFunction || function(){}
+    
+        var atGroups = groups.filter(group=>group.find(
+            item => items[0] == item || recognizeFunction(item, items[0]) || items[1] == item || items[1] && recognizeFunction(item, items[1])
+        
+        )) 
+        if(atGroups.length){//在不同组
+            //因为items是一组的,所以先都放入组1
+            items.forEach(item=> {if(!atGroups[0].includes(item)) atGroups[0].push(item);})
+             
+            if(atGroups.length>1){//如果在不同组,说明这两个组需要合并 
+                var combineGroup = []
+                atGroups.forEach(group=>{
+                    combineGroup = Common.getUnionSet(combineGroup, group)
+                    groups.splice(groups.indexOf(group),1)
+                    
+                }) 
+                groups.push(combineGroup) 
+                
+            }
+        }else{//直接加入为一组
+           groups.push(items) 
+        }
+    },
+    
+     
+     
+    
 }  
 
 

+ 15 - 5
src/utils/CursorDeal.js

@@ -7,21 +7,31 @@ import Common from './Common'
 
 var CursorDeal = {
     priorityEvent : [//在前面的优先级高
+     
+        {'hoverPano':'pointer'}, 
+          
+        {'connectPano':`url({Potree.resourcePath}/images/connect.png),auto`},
+        {'disconnectPano':`url({Potree.resourcePath}/images/connect-dis.png),auto`},
+         
+        {'hoverLine':'pointer'},
+        {'zoomInCloud':'zoom-in'},
+        
+        
         {"movePointcloud":'move'}, 
         {"polygon_isIntersectSelf":'not-allowed'},
         {"polygon_AtWrongPlace":'not-allowed'},
         {"markerMove":'grab'},
         {'mapClipMove':'move'},
-        {'mapClipRotate':`url({Potree.resourcePath}/images/rotate-cursor.png), url({Potree.resourcePath}/images/rotate-cursor.cur),auto`},
-        {'rotatePointcloud':`url({Potree.resourcePath}/images/rotate-cursor-white.png),auto`},
+        {'mapClipRotate':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
+        {'rotatePointcloud':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
         {'siteModelFloorDrag':'row-resize'},
         {'addSth':'cell'},//or  crosshair
-        
+         
     ], 
     list:[], //当前存在的cursor状态
     currentCursorIndex:null,
     
-    init : function(viewer){
+    init : function(viewer, viewers){
         
         this.priorityEvent.forEach(e=>{//刚开始Potree.resourcePath没值,现在换
             for(let i in e){
@@ -31,7 +41,7 @@ var CursorDeal = {
         
          
         
-        this.domElements = [viewer.renderArea, viewer.mapViewer.renderArea];
+        this.domElements = viewers.map(e=>e.renderArea)  
         
         viewer.addEventListener("CursorChange",(e)=>{
             if(e.action == 'add'){

+ 5 - 1
src/utils/DrawUtil.js

@@ -55,6 +55,10 @@ var LineDraw = {
 		line.geometry.computeBoundingSphere();
         if(line.material instanceof THREE.LineDashedMaterial){
             line.computeLineDistances()
+            //line.geometry.attributes.lineDistance.needsUpdate = true;
+             
+            line.geometry.verticesNeedUpdate = true; //没用
+             
         }
 	}  
 	,
@@ -131,7 +135,7 @@ var LineDraw = {
             
             if(line.material.defines.USE_DASH != void 0){
                 //line.geometry.verticesNeedUpdate = true; //没用
-                //line.geometry.computeBoundingSphere();
+                line.geometry.computeBoundingSphere(); //for raycaster
                 line.computeLineDistances(); 
             } 
         }else{

+ 37 - 27
src/utils/SplitScreen.js

@@ -1,5 +1,7 @@
 import {View} from "../viewer/View.js";
 import Viewport from "../viewer/Viewport.js";
+import * as THREE from "../../libs/three.js/build/three.module.js";
+
 
 const viewportProps = [
     {
@@ -14,7 +16,7 @@ const viewportProps = [
         left:0,
         bottom:0.5,
         width: 0.5,height:0.5,
-        name : 'Top',   
+        name : 'top',   
         name2 : 'mapViewport', 
         axis:["x","y"],
         //axisSign:[1,1],
@@ -25,21 +27,21 @@ const viewportProps = [
         left:0.5,
         bottom:0,
         width: 0.5,height:0.5,
-        name : 'Right', 
+        name : 'right', 
         axis:["y","z"],
         //axisSign:[1,1],
         active: true,
-        //相机位置在x轴正向
+        //相机位置在x轴正向  右下角屏
     },
     {
         left:0,
         bottom:0,
         width: 0.5,height:0.5, 
-        name : 'Back', 
+        name : 'back', 
         axis:["x","z"],
         //axisSign:[-1,1],    // 从镜头方向看  x向左 所以取负 
         active: true,
-        //相机位置在y轴正向
+        //相机位置在y轴正向  左下角屏
     },
 ]
 
@@ -56,28 +58,25 @@ var SplitScreen = {
         let {boundSize, center} = viewer.bound
           
         var getOrthographicCamera = function(widthRatio, heightRatio, axis){
-            var widthPX = viewer.renderArea.clientWidth * widthRatio;
+             /*var widthPX = viewer.renderArea.clientWidth * widthRatio;
             var heightPX = viewer.renderArea.clientHeight * heightRatio;
-            var aspect = widthPX/heightPX
+              var aspect = widthPX/heightPX
             //console.log(viewer.renderArea.clientWidth,viewer.renderArea.clientHeight,aspect)
-            var width = Math.max(boundSize[axis[0]],  boundSize[axis[1]] * aspect)
-            var orthographicCamera = new THREE.OrthographicCamera(-widthPX/2, widthPX/2, heightPX/2, -heightPX/2, 0.01, 10000);
-            var margin = 50 //px 
+            var width = Math.max(boundSize[axis[0]],  boundSize[axis[1]] * aspect)   */
+            var orthographicCamera = new THREE.OrthographicCamera(-100, 100, 100, 100, 0.01, 10000);
+            /*  var margin = 50 //px 
             orthographicCamera.zoom = (widthPX - margin) / width//zoom越大视野越小
             var moveAtAxis = ['x','y','z'].find(e=>!(axis.includes(e)))
             orthographicCamera.up.set(0,0,1)
             orthographicCamera.position.copy(center)
-            orthographicCamera.position[moveAtAxis] += 1000;//偏移一些 在模型外即可
-            
-         
-           
-            
+            orthographicCamera.position[moveAtAxis] += 1000;//偏移一些 在模型外即可  
+             */  
             return orthographicCamera
         }
 
         
         
-        
+        viewer.setLimitFar(false)
         
          
         
@@ -116,14 +115,16 @@ var SplitScreen = {
             if(viewport.name != 'MainView'){
                 viewport.view.setCubeView(viewport.name)
                 viewport.view.position.copy(viewport.camera.position)
+                
             }
-             
+            
+            
             
         }
         
         
         viewer.viewports = viewports;
-        viewer.setLimitFar(false)
+        
         
         viewer.mapViewer.attachToMainViewer(true,'split4Screens','dontSet') 
         //覆盖在map上、点云等其他物体之下的一层背景
@@ -195,7 +196,7 @@ var SplitScreen = {
                     
                     Potree.settings.pointDensity = 'fourViewports' //强制降低点云质量
                     
-                    e.changePointOpacity(0.5/* newOpacityMap.get(e).get(viewport), true */);  //多数据集有的数据集很小,放大后显示特别淡
+                    e.changePointOpacity(0.6/* newOpacityMap.get(e).get(viewport), true */);  //多数据集有的数据集很小,放大后显示特别淡
                     //console.log(e.name, viewport.name, e.temp.pointOpacity, e.material.opacity)
                 }                 
             })  
@@ -209,7 +210,13 @@ var SplitScreen = {
         viewer.mapViewer.setViewLimit('expand') //多数据集距离远时可以任意远,所以不限制了。但是这样就会看到地图边界了怎么办?
         //viewer.dispatchEvent({'type': 'beginSplitView' }) 
         viewer.updateScreenSize({forceUpdateSize:true})   
-        this.viewportFitBound(mapViewport, boundSize, center)
+        
+        
+        viewports.forEach(viewport=>{
+            if(viewport.name == 'MainView')return
+            this.viewportFitBound(viewport, boundSize, center)
+        })   
+        //this.viewportFitBound(mapViewport, boundSize, center)
         //Potree.settings.ifShowMarker = false
         Potree.settings.displayMode = 'showPointCloud'
     },
@@ -410,22 +417,25 @@ var SplitScreen = {
      
     focusOnPointCloud:function(pointcloud){//三个屏都聚焦在这个点云 
         var boundSize = pointcloud.bound.getSize(new THREE.Vector3);
-	    var center = pointcloud.bound.getCenter(new THREE.Vector3);
-        this.focusOnObject(boundSize,center)
-        
+	    var center = pointcloud.bound.getCenter(new THREE.Vector3); 
+        let target = pointcloud.panosBound && pointcloud.panosBound.center  //看向pano集中的地方,也就是真正有点云的地方。(因为需要展示所有点云,所以没办法用这个做为center)
+        this.focusOnObject(boundSize,center,target)
         
+        viewer.flyToDataset({pointcloud, dontMoveMap:true, duration:0})
     }
     ,
-    focusOnObject:function(boundSize,center, duration=0){
+    focusOnObject:function(boundSize, center, target, duration=0){
         viewer.viewports.forEach(e=>{
             if(e.name == 'MainView'){
-                let len = boundSize.length()
+                /* let len = boundSize.length()
                 let distance = THREE.Math.clamp(e.view.position.distanceTo(center),  len * 0.01,  len*0.3 ) //距离限制
                 //viewer.focusOnObject({position:center}, 'point', duration, {distance, direction: e.view.direction,dontMoveMap:true} )//平移镜头
                 //为了方便定位,直接飞到中心位置:
                 e.view.setView({
-                    position:center,  duration,  quaternion: new THREE.Quaternion().setFromEuler(e.view.rotation) 
-                })
+                    position:center,  duration,  target   
+                }) */
+                
+                
             }else{
                 this.viewportFitBound(e, boundSize, center)
             }

+ 19 - 1
src/utils/math.js

@@ -410,7 +410,7 @@ var math = {
             else{//否则考虑上距离,加一丢丢近大远小的效果
                 var currentDis, nearBound, farBound
                 if(op.camera.type == "OrthographicCamera"){
-                    currentDis = (op.camera.right - op.camera.left) / op.camera.zoom
+                    currentDis = 200 / op.camera.zoom  //(op.camera.right - op.camera.left) / op.camera.zoom
                 }else{
                     currentDis = op.position.distanceTo(op.camera.position);
                 } 
@@ -593,6 +593,24 @@ var math = {
         let matrix = (new THREE.Matrix4).lookAt(position, target, new THREE.Vector3(0,0,1))
         return (new THREE.Quaternion).setFromRotationMatrix(matrix)
         
+    },
+    
+    
+    getBoundByPoints(points, minSize){ 
+        var bound = new THREE.Box3
+        points.forEach(point=>{
+            bound.expandByPoint(point)
+        }) 
+        let center = bound.getCenter(new THREE.Vector3)
+        if(minSize){
+            let minBound = (new THREE.Box3()).setFromCenterAndSize(center, minSize)
+            bound.union(minBound)
+        }
+        return {
+            bounding:bound,
+            size: bound.getSize(new THREE.Vector3),
+            center,
+        }
     }
 };
 

+ 6 - 3
src/viewer/Scene.js

@@ -352,7 +352,10 @@ export class Scene extends THREE.EventDispatcher{
 	}
 
 	getActiveCamera() {
-
+        return viewer.mainViewport.camera
+        
+        
+        
 		if(this.overrideCamera){
 			return this.overrideCamera;
 		}
@@ -403,12 +406,12 @@ export class Scene extends THREE.EventDispatcher{
         
         //add:------给空间模型的box 或其他obj------
         let light2 = new THREE.AmbientLight( 16777215, 1 );
-        viewer.setObjectLayers(light2, 'sceneObjects')
+        viewer.setObjectLayers(light2, 'bothMapAndScene')
         this.scene.add(light2)
         let light3 = new THREE.DirectionalLight( 16777215, 1);  
         light3.position.set( 10, 10, 10 );
 		light3.lookAt( new THREE.Vector3(0, 0, 0));
-        viewer.setObjectLayers(light3, 'sceneObjects')
+        viewer.setObjectLayers(light3, 'bothMapAndScene')
         this.scene.add(light3)
         //--------------------------------------------
 

+ 113 - 13
src/viewer/View.js

@@ -187,27 +187,27 @@ export class View extends THREE.EventDispatcher{
     setCubeView(dir) {
 		 
 		switch(dir) {
-			case "Front":
+			case "front":
 				this.yaw = 0;
                 this.pitch = 0;
 				break;
-			case "Back":
+			case "back":
 				this.yaw =  Math.PI;  
                 this.pitch = 0;
 				break;
-			case "Left":
+			case "left":
 				this.yaw = -Math.PI / 2;
                 this.pitch = 0;
 				break;
-			case "Right":
+			case "right":
 				this.yaw = Math.PI / 2;
                 this.pitch = 0;
 				break;
-			case "Top":
+			case "top":
 				this.yaw = 0;
                 this.pitch = -Math.PI / 2;
 				break;
-			case "Bottom":
+			case "bottom":
 				this.yaw = -Math.PI;
                 this.pitch = Math.PI / 2;
 				break;
@@ -216,19 +216,29 @@ export class View extends THREE.EventDispatcher{
     
     
     
+    isFlying(){
+        return transitions.getById(this.LookTransition).length > 0 
+    }
+    
+    cancelFlying(){//外界只能通过这个来cancel
+        transitions.cancelById(this.LookTransition, true ); 
+    }
+    
     setView( info = {}){
         // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun
-        transitions.cancelById(this.LookTransition, true ); 
-        
+        this.cancelFlying()
+         
         let done = ()=>{ 
             if(endTarget){
                 this.lookAt(endTarget); //compute radius for orbitcontrol
             }else if(endQuaternion){
                 this.rotation = new THREE.Euler().setFromQuaternion(endQuaternion)
             }
-             
-            info.callback && info.callback() 
-            this.dispatchEvent('setViewDone')
+            setTimeout(()=>{//延迟是为了使isFlying先为false
+                info.callback && info.callback()     
+                this.dispatchEvent('flyingDone')    
+            },1)
+            
         }
         
         let endPosition = new THREE.Vector3().copy(info.position)
@@ -255,7 +265,7 @@ export class View extends THREE.EventDispatcher{
             this.restrictPos()
 			
             info.onUpdate && info.onUpdate(1)
-            setTimeout(done.bind(this), 1);  //延迟一下再执行  是为了让images360里的flying先为true
+            done()
             
 		}else{
 
@@ -268,7 +278,7 @@ export class View extends THREE.EventDispatcher{
                     this.rotation = new THREE.Euler().setFromQuaternion(quaternion)
                 }
                 this.restrictPos()
- 
+                 
 
                 info.onUpdate && info.onUpdate(t)//add
             }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine /*easeInOutQuad */,null, this.LookTransition, info.cancelFun); 
@@ -277,5 +287,95 @@ export class View extends THREE.EventDispatcher{
         } 
 
     }
+    
+    
+    //平移Ortho相机
+    moveOrthoCamera(viewport,  info, duration,  easeName){//boundSize优先于endZoom。
+        let camera = viewport.camera
+        
+        let startZoom = camera.zoom 
+        let endPosition = info.endPosition 
+        let boundSize = info.boundSize
+        let endZoom = info.endZoom
+        let margin = info.margin || {x:0,y:0}/* 200 */ //像素
+        
+        if(info.bound){//需要修改boundSize以适应相机的旋转,当相机不在xy水平面上朝向z时
+            endPosition = endPosition || info.bound.getCenter(new THREE.Vector3())
+            
+            let matrixRot = new THREE.Matrix4().makeRotationFromEuler(this.rotation) 
+            matrixRot.getInverse(matrixRot)  
+            let boundingBox = info.bound.clone().applyMatrix4(matrixRot) 
+            boundSize = boundingBox.getSize(new THREE.Vector3())
+            
+        }
+        
+        this.setView({ position:endPosition,  duration, 
+            callback:()=>{//done
+                 
+            },
+            onUpdate:(progress)=>{ 
+                if(boundSize || endZoom){ 
+                    if(boundSize){
+                        let aspect = boundSize.x / boundSize.y
+                        let w, h; 
+                        
+                        if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
+                            h = boundSize.y 
+                            endZoom = (viewport.resolution.y - margin.x) / h    //注意,要在resolution不为0时执行 
+                        }else{
+                            w = boundSize.x;  
+                            endZoom = (viewport.resolution.x - margin.y) / w
+                        }  
+                        //onUpdate时更新endzoom是因为画布大小可能更改
+                    }  
+                    
+                    camera.zoom = endZoom * progress + startZoom * (1 - progress)
+                    camera.updateProjectionMatrix() 
+                } 
+            },
+            
+            Easing:easeName
+        
+        })
+          
+        
+    }
+    
+    
+    
+    zoomOrthoCamera(camera, endZoom, pointer, duration, onProgress){//定点缩放
+         
+        let startZoom = camera.zoom
+      
+        let pointerPos = new THREE.Vector3(pointer.x, pointer.y,0.5); 
+        
+       
+        transitions.start(( progress)=>{ 
+            let oldPos = pointerPos.clone().unproject(camera);
+            
+            camera.zoom = endZoom * progress + startZoom * (1 - progress)
+            camera.updateProjectionMatrix() 
+            
+            
+            let newPos = pointerPos.clone().unproject(camera);
+            
+            //定点缩放, 恢复一下鼠标所在位置的位置改变量
+            let moveVec = new THREE.Vector3().subVectors(newPos, oldPos) 
+             
+            camera.position.sub(moveVec)
+            this.position.copy(camera.position)
+            
+            onProgress && onProgress()
+            
+        } , duration, null/* done */, 0,  easing.easeInOutSine, null, "zoomInView"/* , info.cancelFun */); 
+
+    
+    
+        
+        
+    }
+    
+     
+    
 
 };

+ 13 - 5
src/viewer/map/MapViewer.js

@@ -206,9 +206,12 @@ export class MapViewer extends ViewerBase{
         var cursor = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
             transparent:true,
             opacity:0.9,
+            depthTest : false, //防止透明冲突
             map: texLoader.load(Potree.resourcePath+'/textures/pic_location128.png' )
         }))
             cursor.position.set(0,0,cursorHeight);
+            
+            
         this.cursor = cursor
        
         this.scene.add(cursor)
@@ -229,7 +232,7 @@ export class MapViewer extends ViewerBase{
     updateCursor(){
          
         var scale = math.getScaleForConstantSize( {//规定下最小最大像素
-            minSize : 80,  maxSize : 180,   nearBound : initCameraFeildWidth*0.1 , farBound : initCameraFeildWidth*2,
+            minSize : 80,  maxSize : 200,   nearBound : initCameraFeildWidth*0.1 , farBound : initCameraFeildWidth*2,
             camera:this.camera , position: this.cursor.getWorldPosition(new THREE.Vector3()),
             resolution: this.viewports[0].resolution//2
         })
@@ -291,7 +294,8 @@ export class MapViewer extends ViewerBase{
             }
             pano.mapMarker.addEventListener('click', onclick); 
             
-            pano.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走) 
+            pano.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)   
+                //console.log('panoMarker isVisible', pano.id, e.visible)
                 viewer.updateVisible(pano.mapMarker, 'panoVisible', e.visible )
                 this.needRender = true
                 
@@ -346,7 +350,7 @@ export class MapViewer extends ViewerBase{
     
         
         
-        var pano = Common.find(viewer.images360.panos,  filterFuncs, [Images360.sortFunctions.floorDistanceToPoint(intersect)]);
+        var pano = Common.find(viewer.images360.panos,  filterFuncs, [Images360.sortFunctions.floorDisSquaredToPoint(intersect)]);
         if (pano && pano != viewer.images360.currentPano ) {
            viewer.images360.flyToPano(pano)
             
@@ -391,8 +395,12 @@ export class MapViewer extends ViewerBase{
     
     moveTo(endPosition, boundSize, duration=0, easeName){//前两个参数有xy即可
         endPosition = new THREE.Vector3(endPosition.x,endPosition.y,Potree.config.map.cameraHeight)
+        this.view.moveOrthoCamera(this.viewports[0],  {endPosition, boundSize },   duration,  easeName)
+        
+       
         
-        let endZoom, startZoom = this.camera.zoom 
+        
+        /* let endZoom, startZoom = this.camera.zoom 
         
         //修改相机为bound中心,这样能看到全部(宽度范围内)
         
@@ -424,7 +432,7 @@ export class MapViewer extends ViewerBase{
             
             Easing:easeName
         
-        })
+        }) */
           
             
     }

+ 21 - 1
src/viewer/sidebar.html

@@ -317,7 +317,27 @@ Thanks to all the companies and institutions funding Potree:
         </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>
 	</div>
 
 	<div>

+ 58 - 5
src/viewer/sidebar.js

@@ -58,10 +58,19 @@ export class Sidebar{
 		this.initClippingTool();
 		this.initSettings();
         //add
-		this.initAlignment();
-        this.initClipModel();
-        this.initSiteModel()
-        this.initParitcle()
+        
+        if(Potree.settings.editType != 'pano'){
+            this.initAlignment();
+            this.initClipModel();
+            this.initSiteModel()
+            this.initParitcle()
+                
+        }else{
+            this.initPanosEdit()
+        }
+        
+        
+        
 		$('#potree_version_number').html(Potree.version.major + "." + Potree.version.minor + Potree.version.suffix);
 	}
 
@@ -198,7 +207,51 @@ export class Sidebar{
         })
   
     }
-
+    
+    initPanosEdit(){
+        let PanoEditor = viewer.modules.PanoEditor
+        let Alignment = viewer.modules.Alignment
+        
+        var pannel = $('#panos');
+        pannel.find('button[name="save"] ').on('click', ()=>{ 
+            console.log('saveData',PanoEditor.exportSavingData())
+        })
+        pannel.find('button[name="translate"] ').on('click', ()=>{ 
+            Alignment.switchHandle('translate')
+        })
+        pannel.find('button[name="rotate"] ').on('click', ()=>{ 
+            Alignment.switchHandle('rotate')
+        })
+        pannel.find('button[name="topView"] ').on('click', ()=>{ 
+            PanoEditor.switchView('top') 
+        })
+        pannel.find('button[name="sideView"] ').on('click', ()=>{ 
+            PanoEditor.switchView('right') 
+        })
+        pannel.find('button[name="3DView"] ').on('click', ()=>{ 
+            PanoEditor.switchView('mainView') 
+        })
+        
+        pannel.find('button[name="addLink"] ').on('click', ()=>{ 
+            PanoEditor.setLinkOperateState('addLink', true)
+        })
+        pannel.find('button[name="removeLink"] ').on('click', ()=>{ 
+            PanoEditor.setLinkOperateState('removeLink', true)
+        })
+        
+        pannel.find('button[name="getCloser"] ').on('click', ()=>{ 
+            PanoEditor.setZoomInState(true)
+        })
+        
+        
+    }
+    
+    
+    
+    
+    
+    
+    
 	initToolbar(){
 
 		// ANGLE

+ 292 - 208
src/viewer/viewer.js

@@ -60,7 +60,7 @@ import {RouteGuider}  from '../modules/route/RouteGuider'
 import ParticleEditor from '../modules/Particles/ParticleEditor'
 import CamAniEditor from '../modules/CameraAnimation/CamAniEditor'
 import {MeshDraw}  from '../utils/DrawUtil'
-
+import PanoEditor  from '../modules/panoEdit/panoEditor'
 
 import {OBJLoader} from "../../libs/three.js/loaders/OBJLoader.js";
 import {MTLLoader} from "../../libs/three.js/loaders/MTLLoader.js";
@@ -70,8 +70,7 @@ import EffectComposer from '../materials/postprocessing/EffectComposer'
 import {ShaderPass} from '../materials/postprocessing/ShaderPass'
 import RenderPass from '../materials/postprocessing/RenderPass'
 import FXAAShader from "../materials/postprocessing/FXAAShader"
-
-
+ 
 
 
 
@@ -84,16 +83,26 @@ export class Viewer extends ViewerBase{
 	constructor(domElement, mapArea_, args = {}){
 		super(domElement, $.extend(args,{name:'mainViewer'}));
         window.viewer = this
-        this.modules = { //add
-            Clip,
-            Alignment,
-            SiteModel,
-            RouteGuider : new RouteGuider,
-            ParticleEditor,
-            CamAniEditor
-        }
         
-        //this.testingMaxLevel = true
+        
+        
+        if(Potree.settings.editType == "pano"){
+            this.modules = { 
+                Alignment, 
+                PanoEditor,
+                SiteModel
+            }
+        }else{
+            this.modules = { 
+                Clip,
+                Alignment,
+                SiteModel,
+                RouteGuider : new RouteGuider,
+                ParticleEditor,
+                CamAniEditor, 
+            }
+        }
+       
         
          
         console.log('create viewer')
@@ -106,7 +115,7 @@ export class Viewer extends ViewerBase{
         this.visible = true
         this.fpVisiDatasets = []
         this.atDatasets = []
-        
+        this.lastPos = new THREE.Vector3(Infinity,Infinity,Infinity) 
 		//-------------
         
         var supportExtFragDepth = !!Features.EXT_DEPTH.isSupported() ;//iphoneX居然不支持
@@ -408,8 +417,9 @@ export class Viewer extends ViewerBase{
                     left:0, bottom:0, width:1, height: 1, name:'MainView' 
                 }) 
                 this.viewports = [this.mainViewport]
-                this.mapViewer = new MapViewer(mapArea/* $('#mapGaode')[0] */)
-                
+                if(Potree.settings.editType != "pano"){
+                    this.mapViewer = new MapViewer(mapArea/* $('#mapGaode')[0] */)
+                }
                 
                 
                 //---------------------------
@@ -488,11 +498,15 @@ export class Viewer extends ViewerBase{
             
             
             //-----------
-            CursorDeal.init(this)//ADD
-            
-            this.modules.SiteModel.init()
+            CursorDeal.init(this, this.mapViewer ? [this, this.mapViewer] : [this])//ADD
+            if(Potree.settings.editType == "pano"){
+                this.modules.PanoEditor.init()
+            }else{
+                this.modules.SiteModel.init()
+                this.modules.ParticleEditor.init()
+            }                
             this.modules.Alignment.init()
-            this.modules.ParticleEditor.init()
+            
             
             
             this.images360 = new Images360(this);
@@ -590,144 +604,173 @@ export class Viewer extends ViewerBase{
         
         this.addEventListener('allLoaded',  this.testPointcloudsMaxLevel.bind(this))
         
-        {
+        
+        
+        
+        if(Potree.settings.editType != 'pano'){
             
-            //更新所在数据集
-            var lastPos = new THREE.Vector3(Infinity,Infinity,Infinity)
-            this.addEventListener('camera_changed', e => {
-                Common.intervalTool.isWaiting('atWhichDataset', ()=>{ //延时update,防止卡顿
-                    let currPos = viewer.scene.getActiveCamera().position
-                 
-                    if(!currPos.equals(lastPos)){
-                        lastPos.copy(currPos)
-                        
-                        var at = this.scene.pointclouds.filter(e=>e.ifContainsPoint(currPos)) 
-                        
-                        if(Common.getDifferenceSet(at, this.atDatasets).length){
-                            //console.log('atDatasets', at) 
-                            this.atDatasets = at
-                            this.updateFpVisiDatasets()
-                            this.dispatchEvent({type:'pointcloudAtChange',pointclouds:at}) 
-                        }
-                         
-                        return true 
-                    }
-                }, 500)  
-
-            }) 
             
-        } 
-        
-        this.addEventListener('switchFloorplanSelect',(e)=>{//进入平面图设置后 切换选中的数据集
-            this.selectedFloorplan = e.pointcloud;  //绝对显示
-            this.updateFpVisiDatasets()
-            let pointclouds;
-            if(e.pointcloud){
-                pointclouds = [e.pointcloud]
-            }else if(this.fpVisiDatasets.length){
-                pointclouds = this.fpVisiDatasets
-            }
             
-            pointclouds && this.mapViewer.fitToDatasets(pointclouds) 
             
-        })
-        
-        
-        this.modules.SiteModel.bus.addEventListener('FloorChange',()=>{
-             this.updateFpVisiDatasets()
-        }) 
-        
+            this.addEventListener('switchFloorplanSelect',(e)=>{//进入平面图设置后 切换选中的数据集
+                this.selectedFloorplan = e.pointcloud;  //绝对显示
+                this.updateFpVisiDatasets()
+                let pointclouds;
+                if(e.pointcloud){
+                    pointclouds = [e.pointcloud]
+                }else if(this.fpVisiDatasets.length){
+                    pointclouds = this.fpVisiDatasets
+                }
+                
+                pointclouds && this.mapViewer.fitToDatasets(pointclouds) 
+                
+            })
+            
+            
+            
+            
+            
+            this.modules.SiteModel.bus.addEventListener('FloorChange',()=>{
+                 this.updateFpVisiDatasets()
+            }) 
+            this.mapViewer.mapLayer.addEventListener('floorplanLoaded',()=>{
+                 this.updateCadVisibles(this.fpVisiDatasets, true)   //加载完成后重新更新下
+            })
+                
+            /* this.modules.Clip.bus.addEventListener('updateSelectedDatasets',()=>{
+                 this.updateFpVisiDatasets()
+            }) */
+                
+            
+        }
         
-        /* this.modules.Clip.bus.addEventListener('updateSelectedDatasets',()=>{
-             this.updateFpVisiDatasets()
-        }) */
         
-        this.mapViewer.mapLayer.addEventListener('floorplanLoaded',()=>{
-             this.updateCadVisibles(this.fpVisiDatasets, true)   //加载完成后重新更新下
-        })
-         
-         
+        { 
+            //更新所在数据集 
+            this.addEventListener('camera_changed', e => {
+                this.updateDatasetAt() 
+            })  
+        }  
 	}
 
 
     
+    updateDatasetAt(force){//更新所在数据集
     
+        let fun = ()=>{   
+            let currPos = viewer.mainViewport.view.position
+         
+            if(force || !currPos.equals(this.lastPos)){
+                this.lastPos.copy(currPos)
+                
+                var at = this.scene.pointclouds.filter(e=>e.ifContainsPoint(currPos)) 
+                
+                if(Common.getDifferenceSet(at, this.atDatasets).length){
+                    //console.log('atDatasets', at) 
+                    this.atDatasets = at
+                    if(Potree.settings.editType != 'pano')this.updateFpVisiDatasets()
+                    this.dispatchEvent({type:'pointcloudAtChange',pointclouds:at}) 
+                }
+                force = false 
+                return true 
+            }
+        }            
+        if(force)fun()
+        else Common.intervalTool.isWaiting('atWhichDataset', fun , 500)  
+        
+    }
 
-    updatePanosVisibles(currentFloor, pointclouds){//显示数据集和当前楼层的所有panos(为了显示出平面图中所有漫游点。漫游点个数>=平面图中包含的)
+    
+    updatePanosVisibles(currentFloor){//显示当前楼层的所有panos 
         viewer.images360.panos.forEach(pano=>{
-            let visible = currentFloor && currentFloor.panos.includes(pano) || pointclouds.some(e=>e.panos.includes(pano));
+            let visible = currentFloor && currentFloor.panos.includes(pano) 
             viewer.updateVisible(pano, 'buildingChange', visible, 2)  
         })
     }
     
     
     updateFpVisiDatasets(){
-        
-        
-        
+          
         let Clip = this.modules.Clip
         let SiteModel = this.modules.SiteModel
         let Alignment = this.modules.Alignment
         var currentFloor = SiteModel.currentFloor;
         
-        /* if(Clip.editing){
+        /* if(Clip.editing){//下载页面已经改为和普通时一样,根据位置判断
             
             this.updateCadVisibles(Clip.selectedDatasets)
-            
-            
+              
         }else  */if(this.selectedFloorplan){//平面图设置中
             let pointclouds = [this.selectedFloorplan]
             this.updateCadVisibles(pointclouds)
         }else if(SiteModel.editing || Alignment.editing){//只显示勾选的,也就是显示的点云的
             let pointclouds = this.scene.pointclouds.filter(p => this.getObjVisiByReason(p,'datasetSelection')  ); 
             this.updateCadVisibles(pointclouds)
-            this.updatePanosVisibles(currentFloor, pointclouds)
+            this.updatePanosVisibles(currentFloor/* , pointclouds */)
         }else{ 
             let pointclouds = currentFloor ? this.findPointcloudsAtFloor(currentFloor) : []
             
-            if(pointclouds == 0){//如果当前不在任何楼层或楼层中无数据集,就用当前所在数据集
+            if(pointclouds.length == 0){//如果当前不在任何楼层或楼层中无数据集,就用当前所在数据集
                 pointclouds = this.atDatasets
             }
              
             this.updateCadVisibles(pointclouds)
-            this.updatePanosVisibles(currentFloor, pointclouds)
+            this.updatePanosVisibles(currentFloor/* , pointclouds */)
         }
     }
     
-    
+ 
     findPointcloudsAtFloor(entity){//找当前楼层需要显示哪些数据集。
-        //必要条件:数据集的belongToEntity 在这个entity内(否则会出现点击数据集飞过去平面图却不显示)。or 如果数据集有漫游点的话,需要包含>20%的漫游点。   (防止重叠体积很大但其实一个漫游点都不包含)
-        //充分条件(在符合必要条件之后还应该满足至少一个充分条件): 重叠体积>50%   或   包含>50%的漫游点 
+        //数据集的belongToEntity 在这个entity内(否则会出现点击数据集飞过去平面图却不显示)。or 如果数据集有漫游点的话,需要包含>20%的漫游点。   (防止重叠体积很大但其实一个漫游点都不包含)
+        //重叠体积>50%   或   包含>50%的漫游点 
          
         
-        const ratio1 = 0.2, ratio2 = 0.5, ratio3 = 0.5
+        const ratio1 = 0.2, ratio2 = 0.5, ratio3 = 0.95
+         
+        var lowScores = []
+        
+        
+        
         var pointclouds = viewer.scene.pointclouds.filter(e=>{
+            let score = 0
             
-            
-            if(e.belongToEntity && (e.belongToEntity == entity || e.belongToEntity.buildParent == entity)){//必要条件1
+            if(e.belongToEntity && (e.belongToEntity == entity || e.belongToEntity.buildParent == entity)){//条件1   若该数据集挂载到该楼层 或 该数据集挂载到的房间属于该楼层(这样能显示该层所有房间)
                 return true
             }
             
-            if(e.panos.length){//必要条件2
+            if(e.panos.length){//条件2
                 var insidePanos = e.panos.filter(a=>entity.ifContainsPoint(a.position));
                 let panoCountRatio = insidePanos.length / e.panos.length
+                 
+                if(panoCountRatio > ratio2)return true 
                 if(panoCountRatio < ratio1){ 
-                    return false
+                    score += panoCountRatio//return false
                 }
                 
-                if(panoCountRatio > ratio2)return true 
             } 
              
-             
+            //条件3
             let volume = entity.intersectPointcloudVolume(e);
             let volumeRatio = volume / entity.getVolume(true) //注:hole加入计算
-            if(volumeRatio > ratio3){
+            if(volumeRatio > ratio3){  //ratio3要高一些,因为点云bounding可能很大,包含很多无点云的空间。即使整个数据集包含entity都不一定看起来在数据集中。(千万要防止两层楼都显示了)
                 return true 
-            } 
+            }else{
+                score += volumeRatio
+            }
+            
+            lowScores.push({score, pointcloud:e})
         })
+        
+        if(pointclouds.length == 0){//从低分项挑一个出来。 
+            lowScores.sort((a,b)=>{return a.score - b.score})
+            if(lowScores[0].score > 0.4){
+                pointclouds = [lowScores[0].pointcloud]
+            } 
+        }
+        
+        
         return pointclouds
     }
-
     updateCadVisibles(visiClouds, force){
         let oldVisi = this.fpVisiDatasets
         var visiClouds = this.fpVisiDatasets = visiClouds
@@ -736,7 +779,7 @@ export class Viewer extends ViewerBase{
             var difference = Common.getDifferenceSet(oldVisi , visiClouds) 
             if(difference.length == 0)return
         }
-        
+        //console.log('visiClouds',visiClouds.map(e=>e.name))
         
         viewer.scene.pointclouds.forEach(pointcloud=>{ 
             var floorplan = viewer.mapViewer.mapLayer.getFloorplan(pointcloud.dataset_id)
@@ -1413,7 +1456,7 @@ export class Viewer extends ViewerBase{
 	
 	setTopView(view){
         view = view || this.scene.view
-		view.setCubeView("Top")
+		view.setCubeView("top")
 
 		this.fitToScreen();
 	};
@@ -2350,7 +2393,11 @@ export class Viewer extends ViewerBase{
 		TWEEN.update(timestamp);
         transitions.update(delta);
         this.transformationTool.update();
-        this.modules.ParticleEditor.update(delta)   
+        
+        if(Potree.settings.editType != 'pano'){
+            this.modules.ParticleEditor.update(delta) 
+            this.mapViewer.update(delta)            
+        }
 
 		this.dispatchEvent({
 			type: 'update',
@@ -2365,7 +2412,7 @@ export class Viewer extends ViewerBase{
         
         //add ------
         this.reticule.updateVisible() 
-        this.mapViewer.update(delta)
+        
 
 	}
 
@@ -2723,15 +2770,13 @@ export class Viewer extends ViewerBase{
             
             viewer.dispatchEvent({type: "render.begin",  viewer: viewer, viewport:view, params });
             
+             
             if(view.render){ 
                 view.render($.extend({}, params, {
                     renderer:this.renderer,   clear:this.clear.bind(this), resize:null,
-                    renderOverlay: this.renderOverlay.bind(this), force:!view.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
+                    renderOverlay: this.renderOverlay.bind(this),  force:!view.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
                 }))
-            } 
-            
-             
-            
+            }
             
             if(!view.noPointcloud ){
                 
@@ -2755,19 +2800,16 @@ export class Viewer extends ViewerBase{
                 
                 view.render || this.clear(params)  
                 pRenderer.clearTargets(params);
-                pRenderer.render(params);
-                
+                pRenderer.render(params); 
                 
-                {//渲染和地图共有的物体
-                    this.setCameraLayers(params.camera, [ 'bothMapAndScene'  ] )   
-                    this.renderer.render(this.scene.scene, params.camera);  
-                }
-                
-                this.renderOverlay(params) 
-                
-                view.afterRender && view.afterRender()
             } 
+            
              
+            view.render || this.renderOverlay(params) 
+           
+              
+            
+            view.afterRender && view.afterRender() 
 
             this.dispatchEvent({type: "render.end",  viewer: this, viewport:view  });
             
@@ -2797,18 +2839,7 @@ export class Viewer extends ViewerBase{
         
 	}
 	
-    /* renderDefault(){//测试 ios15.4.1
-        //let pRenderer = this.getPRenderer();
-        //this.clear()
-        this.renderer.autoClear = false
-        this.renderer.setRenderTarget(null)  
-        let camera =  this.scene.getActiveCamera();
-        this.setCameraLayers(camera, [     'sceneObjects',  'marker' ,   'reticule'    ,'skybox'    ]) 
-        this.renderer.render(this.scene.scene, camera);  
-        // pRenderer.clearTargets( );
-        //pRenderer.render( );   
-     
-    }  */
+   
 
 
     renderOverlay(params){
@@ -2823,8 +2854,8 @@ export class Viewer extends ViewerBase{
 
         if(params.cameraLayers) cameraLayers = params.cameraLayers
         else{
-            if(params.isMap)cameraLayers = ['reticule']
-            else cameraLayers = ['sceneObjects','marker','reticule'  /* 'bothMapAndScene' */];
+            if(params.isMap)cameraLayers = ['bothMapAndScene']
+            else cameraLayers = ['sceneObjects',   'bothMapAndScene' ];
         }
         
    
@@ -2858,7 +2889,18 @@ export class Viewer extends ViewerBase{
         }
          
     }
+      /* renderDefault(){//测试 ios15.4.1
+        //let pRenderer = this.getPRenderer();
+        //this.clear()
+        this.renderer.autoClear = false
+        this.renderer.setRenderTarget(null)  
+        let camera =  this.scene.getActiveCamera();
+        this.setCameraLayers(camera, [     'sceneObjects',  'marker' ,   'reticule'    ,'skybox'    ]) 
+        this.renderer.render(this.scene.scene, camera);  
+        // pRenderer.clearTargets( );
+        //pRenderer.render( );   
      
+    }  */
     setLimitFar(state){//切换是否limitFar
         viewer.mainViewport.camera.limitFar = !!state
         if(state){
@@ -3089,15 +3131,25 @@ export class Viewer extends ViewerBase{
     
         console.log('startScreenshot: '+sid)
         
+        let updateCamera = ()=>{ 
+            this.viewports.forEach(e=>{
+                e.view.applyToCamera(e.camera) //因为fly时只更新了view所以要强制更新下camera
+                 
+                this.dispatchEvent({  //update map  and sprite
+                    type: "camera_changed", 
+                    camera: e.camera,
+                    viewport : e
+                })   
+            })  
+        }
         
-        
-        var screenshot = ()=>{ 
+        let screenshot = ()=>{ 
             
             useMap && (viewer.mapViewer.needRender = true)
             
             
             
-            var { dataUrl  } = viewerMaster.makeScreenshot( new THREE.Vector2(width,height), null, compressRatio    );
+            let { dataUrl  } = viewerMaster.makeScreenshot( new THREE.Vector2(width,height), null, compressRatio    );
         
              
             
@@ -3108,6 +3160,31 @@ export class Viewer extends ViewerBase{
             
             
             var finish = ()=>{
+                
+                oldStates.viewports.forEach(old=>{//恢复相机
+                    var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
+                    viewport.left = old.left;
+                    viewport.width = old.width;
+                    viewport.view.copy(old.view) 
+                    viewport.view.applyToCamera(viewport.camera);  
+                     
+                }) 
+                
+                viewer.updateScreenSize({forceUpdateSize:true})//更新像素
+                
+                /* oldStates.viewports.forEach(old=>{//恢复相机
+                    var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
+                    this.dispatchEvent({  //update map
+                        type: "camera_changed", 
+                        camera: viewport.camera,
+                        viewport : viewport
+                    }) 
+                })  */
+                updateCamera()       
+                
+                
+                
+                
                 deferred.resolve(dataUrl)
                 console.log('screenshot done: '+sid)
             }
@@ -3136,37 +3213,16 @@ export class Viewer extends ViewerBase{
                     viewer.images360.flyToPano({pano:oldStates.pano, duration:0, callback:()=>{
                         finish()
                     }}) 
+                }else{
+                    finish()
                 }
                 
-                oldStates.viewports.forEach(old=>{//恢复相机
-                    var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
-                    viewport.left = old.left;
-                    viewport.width = old.width;
-                    viewport.view.copy(old.view) 
-                    viewport.view.applyToCamera(viewport.camera);  
-                     
-                }) 
-                
-                viewer.updateScreenSize({forceUpdateSize:true})//更新像素
-                
-                oldStates.viewports.forEach(old=>{//恢复相机
-                    var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
-                    this.dispatchEvent({  //update map
-                        type: "camera_changed", 
-                        camera: viewport.camera,
-                        viewport : viewport
-                    }) 
-                }) 
-                        
-            } 
-            
-            
-            if(Potree.settings.displayMode != 'showPanos') {
-                finish()
-            }
+            }  
             
             
-        }
+        }// screenshot  end
+        
+        
         
         let mapViewport = this.mapViewer.viewports[0]
         let mainViewport = this.mainViewport
@@ -3191,53 +3247,57 @@ export class Viewer extends ViewerBase{
         viewer.updateVisible(this.mapViewer.cursor, 'screenshot', false)//令mapCursor不可见
         
         
+        
         if(info.type == 'measure'){//要截图双屏 
             this.scene.measurements.forEach(e=>this.updateVisible(e,'screenshot',e == info.measurement)  )
             info.measurement.setSelected(true, 'screenshot')
             
-            this.mapViewer.attachToMainViewer(true, 'measure', 0.5  )//不要移动相机去适应
-           
-            viewer.updateScreenSize({forceUpdateSize:true, width, height}) //更新viewports相机透视
-            
-            //不同角度截图 得到三维的会不一样,因为focusOnObject是根据方向的
-            let {promise}= this.focusOnObject(info.measurement, 'measure', 0,     {basePanoSize:1024}  )
-            promise.done(()=>{ 
-                console.log('promise.done') 
-                this.viewports.forEach(e=>{
-                    e.view.applyToCamera(e.camera)
-                     
-                    this.dispatchEvent({  //update map
-                        type: "camera_changed", 
-                        camera: e.camera,
-                        viewport : e
-                    })   
-                    
-                }) 
-                
-                let waitMap = ()=>{
-                    console.log('waitMap: '+sid)
-                    this.mapViewer.waitLoadDone(screenshot.bind(this))//等待地图所有加载完
+            
+            //因为分屏后位置才最终确定,才能确定是否显示出floorplan所以先分屏
+            if(Potree.settings.floorplanEnable){ 
+                this.mapViewer.attachToMainViewer(true, 'measure', 0.5  ) 
+            }  
+            viewer.updateScreenSize({forceUpdateSize:true, width, height}) //更新viewports相机透视 使focusOnObject在此窗口大小下
+             
+            let begin = ()=>{
+                useMap = this.mapViewer.attachedToViewer 
+                updateCamera()
+                let waitTime = Potree.settings.displayMode == 'showPointCloud' ? 500 : 0 //等点云加载 网速差的话还是加载稀疏  是否要用最高质量点云
+                if(useMap){
+                    let waitMap = ()=>{
+                        //console.log('waitMap: '+sid)
+                        this.mapViewer.waitLoadDone(screenshot.bind(this))//等待地图所有加载完 
+                    }  
+                    setTimeout(waitMap.bind(this), waitTime)  
+                }else{ 
+                    setTimeout(screenshot.bind(this), waitTime)
                     
                 } 
+            }
+            
+            let {promise}= this.focusOnObject(info.measurement, 'measure', 0,     {basePanoSize:1024}  )//注意:不同角度截图 得到三维的会不一样,因为focusOnObject是根据方向的
+            promise.done(()=>{  
+                //console.log('promise.done') 
+                //根据当前位置更新floorplan显示
+                //console.log('view Pos ', this.mainViewport.view.position.toArray())
+                this.updateDatasetAt(true) 
+                this.modules.SiteModel.updateEntityAt(true) 
+                //this.updateFpVisiDatasets() 
                 
-                
-                /* if(Potree.settings.displayMode == 'showPanos'){//如果是全景图,要等全景的tile加载完 
-                    //this.images360.checkAndWaitForTiledPanoLoad(this.images360.currentPano, this.images360.qualityManager.standardSize, ()=>{//done
-                    //loadTiledPano
-                    if(!this.images360.checkAndWaitForPanoLoad(this.images360.currentPano,  this.images360.qualityManager.standardSize,   ()=>{//done   
-                        //standardSize maxNavPanoSize
-                        waitMap()
-                    })){
-                        waitMap()
-                    }
+                //console.log('currentFloor', this.modules.SiteModel.currentFloor, 'currentDataset', this.atDatasets )    
+                 
+                let floorplanShowed = this.mapViewer.mapLayer.maps.some(e => e.name.includes('floorplan') && e.objectGroup.visible)
+                if(!floorplanShowed && this.mapViewer.attachedToViewer){ 
+                    this.mapViewer.attachToMainViewer(false) //取消分屏
+                    viewer.updateScreenSize({forceUpdateSize:true, width, height}) //更新viewports相机透视
+                    let {promise} = this.focusOnObject(info.measurement, 'measure', 0,     {basePanoSize:1024}  )//因画面比例更改,重新focus
+                    promise.done(()=>{ 
+                        begin()  
+                    }) 
                 }else{
-                    waitMap()
-                }  */  //512就可以
-                
+                    begin() 
+                } 
                 
-                //调不通,暂时先用setTimeout
-                 
-                setTimeout(waitMap.bind(this), 1)
             })
             
         }else{
@@ -3386,9 +3446,9 @@ export class Viewer extends ViewerBase{
             position = getPosWithFullBound(object.points, null, target, cameraPos  )
              
             
-            if(this.mapViewer.attachedToViewer){ 
+            /* if(this.mapViewer.attachedToViewer) */{ 
                 //console.log('mapFocusOn: '+target.toArray())
-                const minBound = new THREE.Vector2(1,1)//针对垂直线,在地图上只有一个点
+                const minBound = new THREE.Vector2(4,4)//针对垂直线,在地图上只有一个点
                 //原始的bound
                 let boundOri = new THREE.Box3() 
                 object.points.forEach(e=>{ 
@@ -3513,30 +3573,54 @@ export class Viewer extends ViewerBase{
         let duration = o.duration == void 0 ? 1000 : o.duration
         var center = pointcloud.bound.getCenter(new THREE.Vector3);
         let position
-        
-        
-        if(Potree.settings.displayMode == 'showPanos'){
+        let getPano = ()=>{//获取离中心最近的pano
             let request = []
             let rank = [
                 Images360.scoreFunctions.distanceSquared({position: center}) 
             ]
             let r = Common.sortByScore(pointcloud.panos, request, rank);
-            
             if(r && r.length){
-                if(r[0].item == this.images360.currentPano) return 'posNoChange'
+                return r[0].item
+            }
+        }
+        
+        if(Potree.settings.displayMode == 'showPanos'){
+            let pano = getPano()
+            
+            if(pano){
+                if(pano == this.images360.currentPano) return 'posNoChange'
                 this.images360.flyToPano({
-                    pano:r[0].item
+                    pano 
                 })
             }else return false
         }else{ 
+            let target
+            position = center
+            if(pointcloud.panosBound){
+                
+                let panosCenter = pointcloud.panosBound.center  //pano集中的地方,也就是真正有点云的地方
+                position = panosCenter.clone() 
+                /* let ratio = 0.2
+                position.z =  center.z * ratio + panosCenter.z * (1-ratio)   //因为panos一般比较低,为了不让相机朝下时看不到点云,加一丢丢中心高度
+                  */
+                let pano = getPano()
+                if(pano){
+                    target = pano.position    //针对像隧道一样的场景, 中心点还是panosCenter都在没有点云的地方,所以还是看向其中一个漫游点好。
+                    position.z = target.z   //水平, 避免朝上或朝下
+                }   
+                
+            }
+        
+        
             if(this.modules.Clip.editing){
-                this.modules.Clip.bus.dispatchEvent({type:'flyToPos', position:center})
+                position.z = center.z   //剪裁时在中心高度,因为以点云为重点
+                this.modules.Clip.bus.dispatchEvent({type:'flyToPos', position })
             }else{
-                if(math.closeTo(center, this.images360.position)) return 'posNoChange'
-                position = center
-                viewer.scene.view.setView({position,  duration })
+                if(math.closeTo(position, this.images360.position)) return 'posNoChange'
+                 
+                viewer.scene.view.setView({position,  target, duration })
                
-                viewer.mapViewer.moveTo(position.clone(), null/* boundSizeMap */, duration)
+                o.dontMoveMap || viewer.mapViewer.moveTo(position.clone(), null , duration)
             }  
         }            
         

+ 27 - 1
改bug的历史.txt

@@ -1,6 +1,32 @@
 
-2022-05-12 
 
+
+
+
+2022.7.8
+
+为何每次截图都不一样?? 和等待时长有关系?
+点云位置一样,但稀疏程度不同。
+平面图显示的数据集还是不同 发现atDatasets和pdf显示的不同,但两次截图楼层一致,第一次多了个buildchagne到其他楼层
+
+可能因为在分屏后移动了相机,但是更新building只500ms一次
+
+
+ 为何相同位置pointInWhichEntity得到的entity不同?  其中一个多了次非force  currentDataset也不一样!
+是因为Common.intervalTool.isWaiting延时了并没有立即执行!
+
+
+
+
+
+
+
+
+
+
+
+2022-05-12 
+ 
 为什么lineMaterial 在四屏中  setSize后,遮挡失效,要移动一下镜头才恢复? 但是在双屏中没事。 updateDepthParams()也没用。 
 	只要有第三个viewport高度不为0,就会这样。 那估计是edl在别的屏幕渲染了
 	因为不同viewport需要用不同的rtEDL