download
raw
11.6 kB
import { Triangle, Vector3, Line3, Sphere, Plane } from 'three';
import { SeparatingAxisBounds } from './SeparatingAxisBounds.js';
import { closestPointsSegmentToSegment, sphereIntersectTriangle } from './MathUtilities.js';
const ZERO_EPSILON = 1e-15;
function isNearZero( value ) {
return Math.abs( value ) < ZERO_EPSILON;
}
export class ExtendedTriangle extends Triangle {
constructor( ...args ) {
super( ...args );
this.isExtendedTriangle = true;
this.satAxes = new Array( 4 ).fill().map( () => new Vector3() );
this.satBounds = new Array( 4 ).fill().map( () => new SeparatingAxisBounds() );
this.points = [ this.a, this.b, this.c ];
this.sphere = new Sphere();
this.plane = new Plane();
this.needsUpdate = true;
}
intersectsSphere( sphere ) {
return sphereIntersectTriangle( sphere, this );
}
update() {
const a = this.a;
const b = this.b;
const c = this.c;
const points = this.points;
const satAxes = this.satAxes;
const satBounds = this.satBounds;
const axis0 = satAxes[ 0 ];
const sab0 = satBounds[ 0 ];
this.getNormal( axis0 );
sab0.setFromPoints( axis0, points );
const axis1 = satAxes[ 1 ];
const sab1 = satBounds[ 1 ];
axis1.subVectors( a, b );
sab1.setFromPoints( axis1, points );
const axis2 = satAxes[ 2 ];
const sab2 = satBounds[ 2 ];
axis2.subVectors( b, c );
sab2.setFromPoints( axis2, points );
const axis3 = satAxes[ 3 ];
const sab3 = satBounds[ 3 ];
axis3.subVectors( c, a );
sab3.setFromPoints( axis3, points );
this.sphere.setFromPoints( this.points );
this.plane.setFromNormalAndCoplanarPoint( axis0, a );
this.needsUpdate = false;
}
}
ExtendedTriangle.prototype.closestPointToSegment = ( function () {
const point1 = new Vector3();
const point2 = new Vector3();
const edge = new Line3();
return function distanceToSegment( segment, target1 = null, target2 = null ) {
const { start, end } = segment;
const points = this.points;
let distSq;
let closestDistanceSq = Infinity;
// check the triangle edges
for ( let i = 0; i < 3; i ++ ) {
const nexti = ( i + 1 ) % 3;
edge.start.copy( points[ i ] );
edge.end.copy( points[ nexti ] );
closestPointsSegmentToSegment( edge, segment, point1, point2 );
distSq = point1.distanceToSquared( point2 );
if ( distSq < closestDistanceSq ) {
closestDistanceSq = distSq;
if ( target1 ) target1.copy( point1 );
if ( target2 ) target2.copy( point2 );
}
}
// check end points
this.closestPointToPoint( start, point1 );
distSq = start.distanceToSquared( point1 );
if ( distSq < closestDistanceSq ) {
closestDistanceSq = distSq;
if ( target1 ) target1.copy( point1 );
if ( target2 ) target2.copy( start );
}
this.closestPointToPoint( end, point1 );
distSq = end.distanceToSquared( point1 );
if ( distSq < closestDistanceSq ) {
closestDistanceSq = distSq;
if ( target1 ) target1.copy( point1 );
if ( target2 ) target2.copy( end );
}
return Math.sqrt( closestDistanceSq );
};
} )();
ExtendedTriangle.prototype.intersectsTriangle = ( function () {
const saTri2 = new ExtendedTriangle();
const arr1 = new Array( 3 );
const arr2 = new Array( 3 );
const cachedSatBounds = new SeparatingAxisBounds();
const cachedSatBounds2 = new SeparatingAxisBounds();
const cachedAxis = new Vector3();
const dir = new Vector3();
const dir1 = new Vector3();
const dir2 = new Vector3();
const tempDir = new Vector3();
const edge = new Line3();
const edge1 = new Line3();
const edge2 = new Line3();
const tempPoint = new Vector3();
function triIntersectPlane( tri, plane, targetEdge ) {
// find the edge that intersects the other triangle plane
const points = tri.points;
let count = 0;
let startPointIntersection = - 1;
for ( let i = 0; i < 3; i ++ ) {
const { start, end } = edge;
start.copy( points[ i ] );
end.copy( points[ ( i + 1 ) % 3 ] );
edge.delta( dir );
const startIntersects = isNearZero( plane.distanceToPoint( start ) );
if ( isNearZero( plane.normal.dot( dir ) ) && startIntersects ) {
// if the edge lies on the plane then take the line
targetEdge.copy( edge );
count = 2;
break;
}
// check if the start point is near the plane because "intersectLine" is not robust to that case
const doesIntersect = plane.intersectLine( edge, tempPoint );
if ( ! doesIntersect && startIntersects ) {
tempPoint.copy( start );
}
// ignore the end point
if ( ( doesIntersect || startIntersects ) && ! isNearZero( tempPoint.distanceTo( end ) ) ) {
if ( count <= 1 ) {
// assign to the start or end point and save which index was snapped to
// the start point if necessary
const point = count === 1 ? targetEdge.start : targetEdge.end;
point.copy( tempPoint );
if ( startIntersects ) {
startPointIntersection = count;
}
} else if ( count >= 2 ) {
// if we're here that means that there must have been one point that had
// snapped to the start point so replace it here
const point = startPointIntersection === 1 ? targetEdge.start : targetEdge.end;
point.copy( tempPoint );
count = 2;
break;
}
count ++;
if ( count === 2 && startPointIntersection === - 1 ) {
break;
}
}
}
return count;
}
// TODO: If the triangles are coplanar and intersecting the target is nonsensical. It should at least
// be a line contained by both triangles if not a different special case somehow represented in the return result.
return function intersectsTriangle( other, target = null, suppressLog = false ) {
if ( this.needsUpdate ) {
this.update();
}
if ( ! other.isExtendedTriangle ) {
saTri2.copy( other );
saTri2.update();
other = saTri2;
} else if ( other.needsUpdate ) {
other.update();
}
const plane1 = this.plane;
const plane2 = other.plane;
if ( Math.abs( plane1.normal.dot( plane2.normal ) ) > 1.0 - 1e-10 ) {
// perform separating axis intersection test only for coplanar triangles
const satBounds1 = this.satBounds;
const satAxes1 = this.satAxes;
arr2[ 0 ] = other.a;
arr2[ 1 ] = other.b;
arr2[ 2 ] = other.c;
for ( let i = 0; i < 4; i ++ ) {
const sb = satBounds1[ i ];
const sa = satAxes1[ i ];
cachedSatBounds.setFromPoints( sa, arr2 );
if ( sb.isSeparated( cachedSatBounds ) ) return false;
}
const satBounds2 = other.satBounds;
const satAxes2 = other.satAxes;
arr1[ 0 ] = this.a;
arr1[ 1 ] = this.b;
arr1[ 2 ] = this.c;
for ( let i = 0; i < 4; i ++ ) {
const sb = satBounds2[ i ];
const sa = satAxes2[ i ];
cachedSatBounds.setFromPoints( sa, arr1 );
if ( sb.isSeparated( cachedSatBounds ) ) return false;
}
// check crossed axes
for ( let i = 0; i < 4; i ++ ) {
const sa1 = satAxes1[ i ];
for ( let i2 = 0; i2 < 4; i2 ++ ) {
const sa2 = satAxes2[ i2 ];
cachedAxis.crossVectors( sa1, sa2 );
cachedSatBounds.setFromPoints( cachedAxis, arr1 );
cachedSatBounds2.setFromPoints( cachedAxis, arr2 );
if ( cachedSatBounds.isSeparated( cachedSatBounds2 ) ) return false;
}
}
if ( target ) {
// TODO find two points that intersect on the edges and make that the result
if ( ! suppressLog ) {
console.warn( 'ExtendedTriangle.intersectsTriangle: Triangles are coplanar which does not support an output edge. Setting edge to 0, 0, 0.' );
}
target.start.set( 0, 0, 0 );
target.end.set( 0, 0, 0 );
}
return true;
} else {
// find the edge that intersects the other triangle plane
const count1 = triIntersectPlane( this, plane2, edge1 );
if ( count1 === 1 && other.containsPoint( edge1.end ) ) {
if ( target ) {
target.start.copy( edge1.end );
target.end.copy( edge1.end );
}
return true;
} else if ( count1 !== 2 ) {
return false;
}
// find the other triangles edge that intersects this plane
const count2 = triIntersectPlane( other, plane1, edge2 );
if ( count2 === 1 && this.containsPoint( edge2.end ) ) {
if ( target ) {
target.start.copy( edge2.end );
target.end.copy( edge2.end );
}
return true;
} else if ( count2 !== 2 ) {
return false;
}
// find swap the second edge so both lines are running the same direction
edge1.delta( dir1 );
edge2.delta( dir2 );
if ( dir1.dot( dir2 ) < 0 ) {
let tmp = edge2.start;
edge2.start = edge2.end;
edge2.end = tmp;
}
// check if the edges are overlapping
const s1 = edge1.start.dot( dir1 );
const e1 = edge1.end.dot( dir1 );
const s2 = edge2.start.dot( dir1 );
const e2 = edge2.end.dot( dir1 );
const separated1 = e1 < s2;
const separated2 = s1 < e2;
if ( s1 !== e2 && s2 !== e1 && separated1 === separated2 ) {
return false;
}
// assign the target output
if ( target ) {
tempDir.subVectors( edge1.start, edge2.start );
if ( tempDir.dot( dir1 ) > 0 ) {
target.start.copy( edge1.start );
} else {
target.start.copy( edge2.start );
}
tempDir.subVectors( edge1.end, edge2.end );
if ( tempDir.dot( dir1 ) < 0 ) {
target.end.copy( edge1.end );
} else {
target.end.copy( edge2.end );
}
}
return true;
}
};
} )();
ExtendedTriangle.prototype.distanceToPoint = ( function () {
const target = new Vector3();
return function distanceToPoint( point ) {
this.closestPointToPoint( point, target );
return point.distanceTo( target );
};
} )();
ExtendedTriangle.prototype.distanceToTriangle = ( function () {
const point = new Vector3();
const point2 = new Vector3();
const cornerFields = [ 'a', 'b', 'c' ];
const line1 = new Line3();
const line2 = new Line3();
return function distanceToTriangle( other, target1 = null, target2 = null ) {
const lineTarget = target1 || target2 ? line1 : null;
if ( this.intersectsTriangle( other, lineTarget ) ) {
if ( target1 || target2 ) {
if ( target1 ) lineTarget.getCenter( target1 );
if ( target2 ) lineTarget.getCenter( target2 );
}
return 0;
}
let closestDistanceSq = Infinity;
// check all point distances
for ( let i = 0; i < 3; i ++ ) {
let dist;
const field = cornerFields[ i ];
const otherVec = other[ field ];
this.closestPointToPoint( otherVec, point );
dist = otherVec.distanceToSquared( point );
if ( dist < closestDistanceSq ) {
closestDistanceSq = dist;
if ( target1 ) target1.copy( point );
if ( target2 ) target2.copy( otherVec );
}
const thisVec = this[ field ];
other.closestPointToPoint( thisVec, point );
dist = thisVec.distanceToSquared( point );
if ( dist < closestDistanceSq ) {
closestDistanceSq = dist;
if ( target1 ) target1.copy( thisVec );
if ( target2 ) target2.copy( point );
}
}
for ( let i = 0; i < 3; i ++ ) {
const f11 = cornerFields[ i ];
const f12 = cornerFields[ ( i + 1 ) % 3 ];
line1.set( this[ f11 ], this[ f12 ] );
for ( let i2 = 0; i2 < 3; i2 ++ ) {
const f21 = cornerFields[ i2 ];
const f22 = cornerFields[ ( i2 + 1 ) % 3 ];
line2.set( other[ f21 ], other[ f22 ] );
closestPointsSegmentToSegment( line1, line2, point, point2 );
const dist = point.distanceToSquared( point2 );
if ( dist < closestDistanceSq ) {
closestDistanceSq = dist;
if ( target1 ) target1.copy( point );
if ( target2 ) target2.copy( point2 );
}
}
}
return Math.sqrt( closestDistanceSq );
};
} )();

Xet Storage Details

Size:
11.6 kB
·
Xet hash:
ddb04cd7db63494c2ee1dad5d2a0c3a56615ed692ac117d22032ffd7afc07115

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.