Browse Source

QuickHull3: Initial Hull

Mugen87 8 years ago
parent
commit
9c6dfc2877
3 changed files with 280 additions and 13 deletions
  1. 1 0
      src/Three.js
  2. 5 5
      src/math/convexhull/Face.js
  3. 274 8
      src/math/convexhull/QuickHull3.js

+ 1 - 0
src/Three.js

@@ -99,6 +99,7 @@ export { QuaternionLinearInterpolant } from './math/interpolants/QuaternionLinea
 export { LinearInterpolant } from './math/interpolants/LinearInterpolant.js';
 export { DiscreteInterpolant } from './math/interpolants/DiscreteInterpolant.js';
 export { CubicInterpolant } from './math/interpolants/CubicInterpolant.js';
+export { QuickHull3 } from './math/convexhull/QuickHull3.js';
 export { Interpolant } from './math/Interpolant.js';
 export { Triangle } from './math/Triangle.js';
 export { _Math as Math } from './math/Math.js';

+ 5 - 5
src/math/convexhull/Face.js

@@ -56,14 +56,14 @@ Object.assign( Face.prototype, {
     while ( i > 0 ) {
 
       edge = edge.next;
-      i -= 1;
+      i --;
 
     }
 
     while ( i < 0 ) {
 
       edge = edge.prev;
-      i += 1;
+      i ++;
 
     }
 
@@ -83,11 +83,11 @@ Object.assign( Face.prototype, {
       var b = this.edge.end();
       var c = this.edge.next.end();
 
-      triangle.set( a, b, c );
+      triangle.set( a.point, b.point, c.point );
 
       triangle.normal( this.normal );
       triangle.midpoint( this.midpoint );
-      triangle.area( this.area );
+      this.area = triangle.area();
 
       this.constant = this.normal.dot( this.midpoint );
 
@@ -95,7 +95,7 @@ Object.assign( Face.prototype, {
 
     };
 
-  },
+  }(),
 
   distanceToPlane: function ( point ) {
 

+ 274 - 8
src/math/convexhull/QuickHull3.js

@@ -1,5 +1,9 @@
 import { Vertex } from './Vertex';
 import { VertexList } from './VertexList';
+import { Face } from './Face';
+import { Vector3 } from '../Vector3';
+import { Line3 } from '../Line3';
+import { Plane } from '../Plane';
 import { Visible, NonConvex, Deleted } from '../../constants';
 
 /**
@@ -36,7 +40,7 @@ function QuickHull3( points ) {
 	this.claimed = new VertexList();
 	this.unclaimed = new VertexList();
 
-	this.vertices = []; // vertices of the hull (internal representation of points)
+	this.vertices = []; // vertices of the hull (internal representation of given points)
 
 	for ( var i = 0; i < this.count.points; i ++ ) {
 
@@ -133,6 +137,8 @@ Object.assign( QuickHull3.prototype, {
 
 		if ( faceVertices !== undefined ) {
 
+			// mark the vertices to be reassigned to some other face
+
 			this.unclaimed.appendChain( faceVertices );
 
 			if ( absorbingFace === undefined ) {
@@ -184,7 +190,7 @@ Object.assign( QuickHull3.prototype, {
 
 		do {
 
-			// buffer next reference, see .deleteFaceVertices()
+			// buffer 'next' reference, see .deleteFaceVertices()
 
 			var nextVertex = vertex.next;
 
@@ -223,17 +229,277 @@ Object.assign( QuickHull3.prototype, {
 
 	},
 
-  clone: function () {
+	// Computes the extremes of a tetrahedron which will be the initial hull
 
-    return new this.constructor().copy( this );
+	computeExtremes: function () {
 
-  },
+		var min = new Vector3();
+		var max = new Vector3();
 
-	copy: function ( other ) {
+		var minVertices = [];
+		var maxVertices = [];
 
-    return this;
+		var i, l, j;
 
-	}
+		// initially assume that the first vertex is the min/max
+
+		for ( i = 0; i < 3; i ++ ) {
+
+			minVertices[ i ] = maxVertices[ i ] = this.vertices[ 0 ];
+
+		}
+
+		min.copy( this.vertices[ 0 ].point );
+		max.copy( this.vertices[ 0 ].point );
+
+		// compute the min/max vertex on all six directions
+
+		for ( i = 0, l = this.vertices.length; i < l ; i ++ ) {
+
+			var vertex = this.vertices[ i ];
+			var point = vertex.point;
+
+			// update the min coordinates
+
+			for ( j = 0; j < 3; j ++ ) {
+
+				if ( point.getComponent( j ) < min.getComponent( j ) ) {
+
+					min.setComponent( j, point.getComponent( j ) );
+					minVertices[ j ] = vertex;
+
+				}
+
+			}
+
+			// update the max coordinates
+
+			for ( j = 0; j < 3; j ++ ) {
+
+				if ( point.getComponent( j ) > max.getComponent( j ) ) {
+
+					max.setComponent( j, point.getComponent( j ) );
+					maxVertices[ j ] = vertex;
+
+				}
+
+			}
+
+		}
+
+		// use min/max vectors to compute epsilon
+
+		this.tolerance = 3 * Number.EPSILON * (
+			Math.max( Math.abs( min.x ), Math.abs( max.x ) ) +
+			Math.max( Math.abs( min.y ), Math.abs( max.y ) ) +
+			Math.max( Math.abs( min.z ), Math.abs( max.z ) )
+		);
+
+		return { min: minVertices, max: maxVertices };
+
+	},
+
+	// Computes the initial tetrahedron assigning to its faces all the points that are candidates to form part of the hull
+
+	computeInitialHull: function () {
+
+		var line3, plane, closestPoint;
+
+		return function computeInitialHull () {
+
+			if ( line3 === undefined ) {
+
+				line3 = new Line3();
+				plane = new Plane();
+				closestPoint = new Vector3();
+
+			}
+
+			var vertex, vertices = this.vertices;
+			var extremes = this.computeExtremes();
+			var min = extremes.min;
+			var max = extremes.max;
+
+			var v0, v1, v2, v3;
+			var i, l, j;
+
+			// 1. Find the two vertices 'v0' and 'v1' with the greatest 1d separation
+			// (max.x - min.x)
+			// (max.y - min.y)
+			// (max.z - min.z)
+
+			var distance, maxDistance = 0;
+			var index = 0;
+
+			for ( i = 0; i < 3; i ++ ) {
+
+				distance = max[ i ].point.getComponent( i ) - min[ i ].point.getComponent( i );
+
+				if ( distance > maxDistance ) {
+
+					maxDistance = distance;
+					index = i;
+
+				}
+
+			}
+
+			v0 = min[ index ];
+			v1 = max[ index ];
+
+			// 2. The next vertex 'v2' is the one farthest to the line formed by 'v0' and 'v1'
+
+			maxDistance = 0;
+			line3.set( v0.point, v1.point );
+
+			for ( i = 0, l = this.vertices.length; i < l; i ++ ) {
+
+				vertex = vertices[ i ];
+
+				if ( vertex !== v0 && vertex !== v1 ) {
+
+					line3.closestPointToPoint( vertex.point, true, closestPoint );
+
+					distance = closestPoint.distanceToSquared( vertex.point );
+
+					if ( distance > maxDistance ) {
+
+						maxDistance = distance;
+						v2 = vertex;
+
+					}
+
+				}
+
+			}
+
+			// 3. The next vertex 'v3' is the one farthest to the plane 'v0', 'v1', 'v2'
+
+			maxDistance = 0;
+			plane.setFromCoplanarPoints( v0.point, v1.point, v2.point );
+
+			for ( i = 0, l = this.vertices.length; i < l; i ++ ) {
+
+				vertex = vertices[ i ];
+
+				if ( vertex !== v0 && vertex !== v1 && vertex !== v2 ) {
+
+					distance = Math.abs( plane.distanceToPoint( vertex.point ) );
+
+					if ( distance > maxDistance ) {
+
+						maxDistance = distance;
+						v3 = vertex;
+
+					}
+
+				}
+
+			}
+
+			var faces = [];
+
+			if ( plane.distanceToPoint( v3.point ) < 0 ) {
+
+				// the face is not able to see the point so 'plane.normal' is pointing outside the tetrahedron
+
+				faces.push(
+					Face.create( v0, v1, v2 ),
+					Face.create( v3, v1, v0 ),
+					Face.create( v3, v2, v1 ),
+					Face.create( v3, v0, v2 )
+				);
+
+				// set the twin edge
+
+				for ( i = 0; i < 3; i ++ ) {
+
+					j = ( i + 1 ) % 3;
+
+					// join face[ i ] i > 0, with the first face
+
+					faces[ i + 1 ].getEdge( 2 ).setTwin( faces[ 0 ].getEdge( j ) );
+
+					// join face[ i ] with face[ i + 1 ], 1 <= i <= 3
+
+					faces[ i + 1 ].getEdge( 1 ).setTwin( faces[ j + 1 ].getEdge( 0 ) );
+
+				}
+
+			} else {
+
+				// the face is able to see the point so 'plane.normal' is pointing inside the tetrahedron
+
+				faces.push(
+					Face.create( v0, v2, v1 ),
+					Face.create( v3, v0, v1 ),
+					Face.create( v3, v1, v2 ),
+					Face.create( v3, v2, v0 )
+				);
+
+				// set the twin edge
+
+				for ( i = 0; i < 3; i ++ ) {
+
+					j = ( i + 1 ) % 3;
+
+					// join face[ i ] i > 0, with the first face
+
+					faces[ i + 1 ].getEdge( 2 ).setTwin( faces[ 0 ].getEdge( ( 3 - i ) % 3 ) );
+
+					// join face[ i ] with face[ i + 1 ]
+
+					faces[ i + 1 ].getEdge( 0 ).setTwin( faces[ j + 1 ].getEdge( 1 ) );
+
+				}
+
+			}
+
+			// the initial hull is the tetrahedron
+
+			for ( i = 0; i < 4; i ++ ) {
+
+      	this.faces.push( faces[ i ] );
+
+			}
+
+			// initial assignment of vertices to the faces of the tetrahedron
+
+			for ( i = 0, l = vertices.length; i < l; i ++ ) {
+
+				vertex = vertices[i];
+
+				if ( vertex !== v0 && vertex !== v1 && vertex !== v2 && vertex !== v3 ) {
+
+					maxDistance = this.tolerance;
+					var maxFace = null;
+
+					for ( j = 0; j < 4; j ++ ) {
+
+						distance = this.faces[ j ].distanceToPlane( vertex.point );
+
+						if ( distance > maxDistance ) {
+
+							maxDistance = distance;
+							maxFace = this.faces[ j ];
+
+						}
+
+					}
+
+					if ( maxFace !== null ) {
+
+          	this.addVertexToFace( vertex, maxFace );
+
+        	}
+
+				}
+
+			}
+
+		};
+
+	}()
 
 } );