123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125 |
- ( function () {
-
- const Visible = 0;
- const Deleted = 1;
- const _v1 = new THREE.Vector3();
- const _line3 = new THREE.Line3();
- const _plane = new THREE.Plane();
- const _closestPoint = new THREE.Vector3();
- const _triangle = new THREE.Triangle();
- class ConvexHull {
- constructor() {
- this.tolerance = - 1;
- this.faces = [];
- this.newFaces = [];
-
-
-
-
-
-
-
-
-
-
- this.assigned = new VertexList();
- this.unassigned = new VertexList();
- this.vertices = [];
- }
- setFromPoints( points ) {
- if ( Array.isArray( points ) !== true ) {
- console.error( 'THREE.ConvexHull: Points parameter is not an array.' );
- }
- if ( points.length < 4 ) {
- console.error( 'THREE.ConvexHull: The algorithm needs at least four points.' );
- }
- this.makeEmpty();
- for ( let i = 0, l = points.length; i < l; i ++ ) {
- this.vertices.push( new VertexNode( points[ i ] ) );
- }
- this.compute();
- return this;
- }
- setFromObject( object ) {
- const points = [];
- object.updateMatrixWorld( true );
- object.traverse( function ( node ) {
- const geometry = node.geometry;
- if ( geometry !== undefined ) {
- if ( geometry.isGeometry ) {
- console.error( 'THREE.ConvexHull no longer supports Geometry. Use THREE.BufferGeometry instead.' );
- return;
- } else if ( geometry.isBufferGeometry ) {
- const attribute = geometry.attributes.position;
- if ( attribute !== undefined ) {
- for ( let i = 0, l = attribute.count; i < l; i ++ ) {
- const point = new THREE.Vector3();
- point.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld );
- points.push( point );
- }
- }
- }
- }
- } );
- return this.setFromPoints( points );
- }
- containsPoint( point ) {
- const faces = this.faces;
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
- const face = faces[ i ];
- if ( face.distanceToPoint( point ) > this.tolerance ) return false;
- }
- return true;
- }
- intersectRay( ray, target ) {
-
- const faces = this.faces;
- let tNear = - Infinity;
- let tFar = Infinity;
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
- const face = faces[ i ];
- const vN = face.distanceToPoint( ray.origin );
- const vD = face.normal.dot( ray.direction );
-
- if ( vN > 0 && vD >= 0 ) return null;
- const t = vD !== 0 ? - vN / vD : 0;
-
- if ( t <= 0 ) continue;
- if ( vD > 0 ) {
-
- tFar = Math.min( t, tFar );
- } else {
-
- tNear = Math.max( t, tNear );
- }
- if ( tNear > tFar ) {
-
- return null;
- }
- }
-
- if ( tNear !== - Infinity ) {
- ray.at( tNear, target );
- } else {
- ray.at( tFar, target );
- }
- return target;
- }
- intersectsRay( ray ) {
- return this.intersectRay( ray, _v1 ) !== null;
- }
- makeEmpty() {
- this.faces = [];
- this.vertices = [];
- return this;
- }
- addVertexToFace( vertex, face ) {
- vertex.face = face;
- if ( face.outside === null ) {
- this.assigned.append( vertex );
- } else {
- this.assigned.insertBefore( face.outside, vertex );
- }
- face.outside = vertex;
- return this;
- }
- removeVertexFromFace( vertex, face ) {
- if ( vertex === face.outside ) {
-
- if ( vertex.next !== null && vertex.next.face === face ) {
-
- face.outside = vertex.next;
- } else {
-
- face.outside = null;
- }
- }
- this.assigned.remove( vertex );
- return this;
- }
- removeAllVerticesFromFace( face ) {
- if ( face.outside !== null ) {
-
- const start = face.outside;
- let end = face.outside;
- while ( end.next !== null && end.next.face === face ) {
- end = end.next;
- }
- this.assigned.removeSubList( start, end );
- start.prev = end.next = null;
- face.outside = null;
- return start;
- }
- }
- deleteFaceVertices( face, absorbingFace ) {
- const faceVertices = this.removeAllVerticesFromFace( face );
- if ( faceVertices !== undefined ) {
- if ( absorbingFace === undefined ) {
-
- this.unassigned.appendChain( faceVertices );
- } else {
-
- let vertex = faceVertices;
- do {
-
-
- const nextVertex = vertex.next;
- const distance = absorbingFace.distanceToPoint( vertex.point );
- if ( distance > this.tolerance ) {
- this.addVertexToFace( vertex, absorbingFace );
- } else {
- this.unassigned.append( vertex );
- }
- vertex = nextVertex;
- } while ( vertex !== null );
- }
- }
- return this;
- }
- resolveUnassignedPoints( newFaces ) {
- if ( this.unassigned.isEmpty() === false ) {
- let vertex = this.unassigned.first();
- do {
-
- const nextVertex = vertex.next;
- let maxDistance = this.tolerance;
- let maxFace = null;
- for ( let i = 0; i < newFaces.length; i ++ ) {
- const face = newFaces[ i ];
- if ( face.mark === Visible ) {
- const distance = face.distanceToPoint( vertex.point );
- if ( distance > maxDistance ) {
- maxDistance = distance;
- maxFace = face;
- }
- if ( maxDistance > 1000 * this.tolerance ) break;
- }
- }
- if ( maxFace !== null ) {
- this.addVertexToFace( vertex, maxFace );
- }
- vertex = nextVertex;
- } while ( vertex !== null );
- }
- return this;
- }
- computeExtremes() {
- const min = new THREE.Vector3();
- const max = new THREE.Vector3();
- const minVertices = [];
- const maxVertices = [];
- for ( let i = 0; i < 3; i ++ ) {
- minVertices[ i ] = maxVertices[ i ] = this.vertices[ 0 ];
- }
- min.copy( this.vertices[ 0 ].point );
- max.copy( this.vertices[ 0 ].point );
- for ( let i = 0, l = this.vertices.length; i < l; i ++ ) {
- const vertex = this.vertices[ i ];
- const point = vertex.point;
- for ( let j = 0; j < 3; j ++ ) {
- if ( point.getComponent( j ) < min.getComponent( j ) ) {
- min.setComponent( j, point.getComponent( j ) );
- minVertices[ j ] = vertex;
- }
- }
- for ( let j = 0; j < 3; j ++ ) {
- if ( point.getComponent( j ) > max.getComponent( j ) ) {
- max.setComponent( j, point.getComponent( j ) );
- maxVertices[ j ] = vertex;
- }
- }
- }
- 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
- };
- }
-
- computeInitialHull() {
- const vertices = this.vertices;
- const extremes = this.computeExtremes();
- const min = extremes.min;
- const max = extremes.max;
-
-
-
- let maxDistance = 0;
- let index = 0;
- for ( let i = 0; i < 3; i ++ ) {
- const distance = max[ i ].point.getComponent( i ) - min[ i ].point.getComponent( i );
- if ( distance > maxDistance ) {
- maxDistance = distance;
- index = i;
- }
- }
- const v0 = min[ index ];
- const v1 = max[ index ];
- let v2;
- let v3;
- maxDistance = 0;
- _line3.set( v0.point, v1.point );
- for ( let i = 0, l = this.vertices.length; i < l; i ++ ) {
- const vertex = vertices[ i ];
- if ( vertex !== v0 && vertex !== v1 ) {
- _line3.closestPointToPoint( vertex.point, true, _closestPoint );
- const distance = _closestPoint.distanceToSquared( vertex.point );
- if ( distance > maxDistance ) {
- maxDistance = distance;
- v2 = vertex;
- }
- }
- }
- maxDistance = - 1;
- _plane.setFromCoplanarPoints( v0.point, v1.point, v2.point );
- for ( let i = 0, l = this.vertices.length; i < l; i ++ ) {
- const vertex = vertices[ i ];
- if ( vertex !== v0 && vertex !== v1 && vertex !== v2 ) {
- const distance = Math.abs( _plane.distanceToPoint( vertex.point ) );
- if ( distance > maxDistance ) {
- maxDistance = distance;
- v3 = vertex;
- }
- }
- }
- const faces = [];
- if ( _plane.distanceToPoint( v3.point ) < 0 ) {
-
- faces.push( Face.create( v0, v1, v2 ), Face.create( v3, v1, v0 ), Face.create( v3, v2, v1 ), Face.create( v3, v0, v2 ) );
- for ( let i = 0; i < 3; i ++ ) {
- const j = ( i + 1 ) % 3;
- faces[ i + 1 ].getEdge( 2 ).setTwin( faces[ 0 ].getEdge( j ) );
- faces[ i + 1 ].getEdge( 1 ).setTwin( faces[ j + 1 ].getEdge( 0 ) );
- }
- } else {
-
- faces.push( Face.create( v0, v2, v1 ), Face.create( v3, v0, v1 ), Face.create( v3, v1, v2 ), Face.create( v3, v2, v0 ) );
- for ( let i = 0; i < 3; i ++ ) {
- const j = ( i + 1 ) % 3;
- faces[ i + 1 ].getEdge( 2 ).setTwin( faces[ 0 ].getEdge( ( 3 - i ) % 3 ) );
- faces[ i + 1 ].getEdge( 0 ).setTwin( faces[ j + 1 ].getEdge( 1 ) );
- }
- }
- for ( let i = 0; i < 4; i ++ ) {
- this.faces.push( faces[ i ] );
- }
- for ( let i = 0, l = vertices.length; i < l; i ++ ) {
- const vertex = vertices[ i ];
- if ( vertex !== v0 && vertex !== v1 && vertex !== v2 && vertex !== v3 ) {
- maxDistance = this.tolerance;
- let maxFace = null;
- for ( let j = 0; j < 4; j ++ ) {
- const distance = this.faces[ j ].distanceToPoint( vertex.point );
- if ( distance > maxDistance ) {
- maxDistance = distance;
- maxFace = this.faces[ j ];
- }
- }
- if ( maxFace !== null ) {
- this.addVertexToFace( vertex, maxFace );
- }
- }
- }
- return this;
- }
- reindexFaces() {
- const activeFaces = [];
- for ( let i = 0; i < this.faces.length; i ++ ) {
- const face = this.faces[ i ];
- if ( face.mark === Visible ) {
- activeFaces.push( face );
- }
- }
- this.faces = activeFaces;
- return this;
- }
- nextVertexToAdd() {
-
- if ( this.assigned.isEmpty() === false ) {
- let eyeVertex,
- maxDistance = 0;
- const eyeFace = this.assigned.first().face;
- let vertex = eyeFace.outside;
- do {
- const distance = eyeFace.distanceToPoint( vertex.point );
- if ( distance > maxDistance ) {
- maxDistance = distance;
- eyeVertex = vertex;
- }
- vertex = vertex.next;
- } while ( vertex !== null && vertex.face === eyeFace );
- return eyeVertex;
- }
- }
-
-
- computeHorizon( eyePoint, crossEdge, face, horizon ) {
-
- this.deleteFaceVertices( face );
- face.mark = Deleted;
- let edge;
- if ( crossEdge === null ) {
- edge = crossEdge = face.getEdge( 0 );
- } else {
-
-
- edge = crossEdge.next;
- }
- do {
- const twinEdge = edge.twin;
- const oppositeFace = twinEdge.face;
- if ( oppositeFace.mark === Visible ) {
- if ( oppositeFace.distanceToPoint( eyePoint ) > this.tolerance ) {
-
- this.computeHorizon( eyePoint, twinEdge, oppositeFace, horizon );
- } else {
-
- horizon.push( edge );
- }
- }
- edge = edge.next;
- } while ( edge !== crossEdge );
- return this;
- }
- addAdjoiningFace( eyeVertex, horizonEdge ) {
-
- const face = Face.create( eyeVertex, horizonEdge.tail(), horizonEdge.head() );
- this.faces.push( face );
- face.getEdge( - 1 ).setTwin( horizonEdge.twin );
- return face.getEdge( 0 );
- }
-
- addNewFaces( eyeVertex, horizon ) {
- this.newFaces = [];
- let firstSideEdge = null;
- let previousSideEdge = null;
- for ( let i = 0; i < horizon.length; i ++ ) {
- const horizonEdge = horizon[ i ];
- const sideEdge = this.addAdjoiningFace( eyeVertex, horizonEdge );
- if ( firstSideEdge === null ) {
- firstSideEdge = sideEdge;
- } else {
-
- sideEdge.next.setTwin( previousSideEdge );
- }
- this.newFaces.push( sideEdge.face );
- previousSideEdge = sideEdge;
- }
- firstSideEdge.next.setTwin( previousSideEdge );
- return this;
- }
- addVertexToHull( eyeVertex ) {
- const horizon = [];
- this.unassigned.clear();
- this.removeVertexFromFace( eyeVertex, eyeVertex.face );
- this.computeHorizon( eyeVertex.point, null, eyeVertex.face, horizon );
- this.addNewFaces( eyeVertex, horizon );
- this.resolveUnassignedPoints( this.newFaces );
- return this;
- }
- cleanup() {
- this.assigned.clear();
- this.unassigned.clear();
- this.newFaces = [];
- return this;
- }
- compute() {
- let vertex;
- this.computeInitialHull();
- while ( ( vertex = this.nextVertexToAdd() ) !== undefined ) {
- this.addVertexToHull( vertex );
- }
- this.reindexFaces();
- this.cleanup();
- return this;
- }
- }
- class Face {
- constructor() {
- this.normal = new THREE.Vector3();
- this.midpoint = new THREE.Vector3();
- this.area = 0;
- this.constant = 0;
- this.outside = null;
- this.mark = Visible;
- this.edge = null;
- }
- static create( a, b, c ) {
- const face = new Face();
- const e0 = new HalfEdge( a, face );
- const e1 = new HalfEdge( b, face );
- const e2 = new HalfEdge( c, face );
- e0.next = e2.prev = e1;
- e1.next = e0.prev = e2;
- e2.next = e1.prev = e0;
- face.edge = e0;
- return face.compute();
- }
- getEdge( i ) {
- let edge = this.edge;
- while ( i > 0 ) {
- edge = edge.next;
- i --;
- }
- while ( i < 0 ) {
- edge = edge.prev;
- i ++;
- }
- return edge;
- }
- compute() {
- const a = this.edge.tail();
- const b = this.edge.head();
- const c = this.edge.next.head();
- _triangle.set( a.point, b.point, c.point );
- _triangle.getNormal( this.normal );
- _triangle.getMidpoint( this.midpoint );
- this.area = _triangle.getArea();
- this.constant = this.normal.dot( this.midpoint );
- return this;
- }
- distanceToPoint( point ) {
- return this.normal.dot( point ) - this.constant;
- }
- }
- class HalfEdge {
- constructor( vertex, face ) {
- this.vertex = vertex;
- this.prev = null;
- this.next = null;
- this.twin = null;
- this.face = face;
- }
- head() {
- return this.vertex;
- }
- tail() {
- return this.prev ? this.prev.vertex : null;
- }
- length() {
- const head = this.head();
- const tail = this.tail();
- if ( tail !== null ) {
- return tail.point.distanceTo( head.point );
- }
- return - 1;
- }
- lengthSquared() {
- const head = this.head();
- const tail = this.tail();
- if ( tail !== null ) {
- return tail.point.distanceToSquared( head.point );
- }
- return - 1;
- }
- setTwin( edge ) {
- this.twin = edge;
- edge.twin = this;
- return this;
- }
- }
- class VertexNode {
- constructor( point ) {
- this.point = point;
- this.prev = null;
- this.next = null;
- this.face = null;
- }
- }
- class VertexList {
- constructor() {
- this.head = null;
- this.tail = null;
- }
- first() {
- return this.head;
- }
- last() {
- return this.tail;
- }
- clear() {
- this.head = this.tail = null;
- return this;
- }
- insertBefore( target, vertex ) {
- vertex.prev = target.prev;
- vertex.next = target;
- if ( vertex.prev === null ) {
- this.head = vertex;
- } else {
- vertex.prev.next = vertex;
- }
- target.prev = vertex;
- return this;
- }
- insertAfter( target, vertex ) {
- vertex.prev = target;
- vertex.next = target.next;
- if ( vertex.next === null ) {
- this.tail = vertex;
- } else {
- vertex.next.prev = vertex;
- }
- target.next = vertex;
- return this;
- }
- append( vertex ) {
- if ( this.head === null ) {
- this.head = vertex;
- } else {
- this.tail.next = vertex;
- }
- vertex.prev = this.tail;
- vertex.next = null;
- this.tail = vertex;
- return this;
- }
- appendChain( vertex ) {
- if ( this.head === null ) {
- this.head = vertex;
- } else {
- this.tail.next = vertex;
- }
- vertex.prev = this.tail;
- while ( vertex.next !== null ) {
- vertex = vertex.next;
- }
- this.tail = vertex;
- return this;
- }
- remove( vertex ) {
- if ( vertex.prev === null ) {
- this.head = vertex.next;
- } else {
- vertex.prev.next = vertex.next;
- }
- if ( vertex.next === null ) {
- this.tail = vertex.prev;
- } else {
- vertex.next.prev = vertex.prev;
- }
- return this;
- }
- removeSubList( a, b ) {
- if ( a.prev === null ) {
- this.head = b.next;
- } else {
- a.prev.next = b.next;
- }
- if ( b.next === null ) {
- this.tail = a.prev;
- } else {
- b.next.prev = a.prev;
- }
- return this;
- }
- isEmpty() {
- return this.head === null;
- }
- }
- THREE.ConvexHull = ConvexHull;
- } )();
|