import { Vector3 } from './Vector3.js';
const vector = new Vector3();
const center = new Vector3();
const size = new Vector3();
const points = [
new Vector3(),
new Vector3(),
new Vector3(),
new Vector3(),
new Vector3(),
new Vector3(),
new Vector3(),
new Vector3()
];
/**
* Class representing an axis-aligned bounding box (AABB).
*
* @author {@link https://github.com/Mugen87|Mugen87}
*/
class AABB {
/**
* Constructs a new AABB with the given values.
*
* @param {Vector3} min - The minimum bounds of the AABB.
* @param {Vector3} max - The maximum bounds of the AABB.
*/
constructor( min = new Vector3(), max = new Vector3() ) {
/**
* The minimum bounds of the AABB.
* @type {Vector3}
*/
this.min = min;
/**
* The maximum bounds of the AABB.
* @type {Vector3}
*/
this.max = max;
}
/**
* Sets the given values to this AABB.
*
* @param {Vector3} min - The minimum bounds of the AABB.
* @param {Vector3} max - The maximum bounds of the AABB.
* @return {AABB} A reference to this AABB.
*/
set( min, max ) {
this.min = min;
this.max = max;
return this;
}
/**
* Copies all values from the given AABB to this AABB.
*
* @param {AABB} aabb - The AABB to copy.
* @return {AABB} A reference to this AABB.
*/
copy( aabb ) {
this.min.copy( aabb.min );
this.max.copy( aabb.max );
return this;
}
/**
* Creates a new AABB and copies all values from this AABB.
*
* @return {AABB} A new AABB.
*/
clone() {
return new this.constructor().copy( this );
}
/**
* Ensures the given point is inside this AABB and stores
* the result in the given vector.
*
* @param {Vector3} point - A point in 3D space.
* @param {Vector3} result - The result vector.
* @return {Vector3} The result vector.
*/
clampPoint( point, result ) {
result.copy( point ).clamp( this.min, this.max );
return result;
}
/**
* Returns true if the given point is inside this AABB.
*
* @param {Vector3} point - A point in 3D space.
* @return {Boolean} The result of the containments test.
*/
containsPoint( point ) {
return point.x < this.min.x || point.x > this.max.x ||
point.y < this.min.y || point.y > this.max.y ||
point.z < this.min.z || point.z > this.max.z ? false : true;
}
/**
* Expands this AABB by the given point. So after this method call,
* the given point lies inside the AABB.
*
* @param {Vector3} point - A point in 3D space.
* @return {AABB} A reference to this AABB.
*/
expand( point ) {
this.min.min( point );
this.max.max( point );
return this;
}
/**
* Computes the center point of this AABB and stores it into the given vector.
*
* @param {Vector3} result - The result vector.
* @return {Vector3} The result vector.
*/
getCenter( result ) {
return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
}
/**
* Computes the size (width, height, depth) of this AABB and stores it into the given vector.
*
* @param {Vector3} result - The result vector.
* @return {Vector3} The result vector.
*/
getSize( result ) {
return result.subVectors( this.max, this.min );
}
/**
* Returns true if the given AABB intersects this AABB.
*
* @param {AABB} aabb - The AABB to test.
* @return {Boolean} The result of the intersection test.
*/
intersectsAABB( aabb ) {
return aabb.max.x < this.min.x || aabb.min.x > this.max.x ||
aabb.max.y < this.min.y || aabb.min.y > this.max.y ||
aabb.max.z < this.min.z || aabb.min.z > this.max.z ? false : true;
}
/**
* Returns true if the given bounding sphere intersects this AABB.
*
* @param {BoundingSphere} sphere - The bounding sphere to test.
* @return {Boolean} The result of the intersection test.
*/
intersectsBoundingSphere( sphere ) {
// find the point on the AABB closest to the sphere center
this.clampPoint( sphere.center, vector );
// if that point is inside the sphere, the AABB and sphere intersect.
return vector.squaredDistanceTo( sphere.center ) <= ( sphere.radius * sphere.radius );
}
/**
* Returns true if the given plane intersects this AABB.
*
* Reference: Testing Box Against Plane in Real-Time Collision Detection
* by Christer Ericson (chapter 5.2.3)
*
* @param {Plane} plane - The plane to test.
* @return {Boolean} The result of the intersection test.
*/
intersectsPlane( plane ) {
const normal = plane.normal;
this.getCenter( center );
size.subVectors( this.max, center ); // positive extends
// compute the projection interval radius of b onto L(t) = c + t * plane.normal
const r = size.x * Math.abs( normal.x ) + size.y * Math.abs( normal.y ) + size.z * Math.abs( normal.z );
// compute distance of box center from plane
const s = plane.distanceToPoint( center );
return Math.abs( s ) <= r;
}
/**
* Returns the normal for a given point on this AABB's surface.
*
* @param {Vector3} point - The point on the surface
* @param {Vector3} result - The result vector.
* @return {Vector3} The result vector.
*/
getNormalFromSurfacePoint( point, result ) {
// from https://www.gamedev.net/forums/topic/551816-finding-the-aabb-surface-normal-from-an-intersection-point-on-aabb/
result.set( 0, 0, 0 );
let distance;
let minDistance = Infinity;
this.getCenter( center );
this.getSize( size );
// transform point into local space of AABB
vector.copy( point ).sub( center );
// x-axis
distance = Math.abs( size.x - Math.abs( vector.x ) );
if ( distance < minDistance ) {
minDistance = distance;
result.set( 1 * Math.sign( vector.x ), 0, 0 );
}
// y-axis
distance = Math.abs( size.y - Math.abs( vector.y ) );
if ( distance < minDistance ) {
minDistance = distance;
result.set( 0, 1 * Math.sign( vector.y ), 0 );
}
// z-axis
distance = Math.abs( size.z - Math.abs( vector.z ) );
if ( distance < minDistance ) {
result.set( 0, 0, 1 * Math.sign( vector.z ) );
}
return result;
}
/**
* Sets the values of the AABB from the given center and size vector.
*
* @param {Vector3} center - The center point of the AABB.
* @param {Vector3} size - The size of the AABB per axis.
* @return {AABB} A reference to this AABB.
*/
fromCenterAndSize( center, size ) {
vector.copy( size ).multiplyScalar( 0.5 ); // compute half size
this.min.copy( center ).sub( vector );
this.max.copy( center ).add( vector );
return this;
}
/**
* Computes an AABB that encloses the given set of points.
*
* @param {Array<Vector3>} points - An array of 3D vectors representing points in 3D space.
* @return {AABB} A reference to this AABB.
*/
fromPoints( points ) {
this.min.set( Infinity, Infinity, Infinity );
this.max.set( - Infinity, - Infinity, - Infinity );
for ( let i = 0, l = points.length; i < l; i ++ ) {
this.expand( points[ i ] );
}
return this;
}
/**
* Transforms this AABB with the given 4x4 transformation matrix.
*
* @param {Matrix4} matrix - The 4x4 transformation matrix.
* @return {AABB} A reference to this AABB.
*/
applyMatrix4( matrix ) {
const min = this.min;
const max = this.max;
points[ 0 ].set( min.x, min.y, min.z ).applyMatrix4( matrix );
points[ 1 ].set( min.x, min.y, max.z ).applyMatrix4( matrix );
points[ 2 ].set( min.x, max.y, min.z ).applyMatrix4( matrix );
points[ 3 ].set( min.x, max.y, max.z ).applyMatrix4( matrix );
points[ 4 ].set( max.x, min.y, min.z ).applyMatrix4( matrix );
points[ 5 ].set( max.x, min.y, max.z ).applyMatrix4( matrix );
points[ 6 ].set( max.x, max.y, min.z ).applyMatrix4( matrix );
points[ 7 ].set( max.x, max.y, max.z ).applyMatrix4( matrix );
return this.fromPoints( points );
}
/**
* Returns true if the given AABB is deep equal with this AABB.
*
* @param {AABB} aabb - The AABB to test.
* @return {Boolean} The result of the equality test.
*/
equals( aabb ) {
return ( aabb.min.equals( this.min ) ) && ( aabb.max.equals( this.max ) );
}
/**
* Transforms this instance into a JSON object.
*
* @return {Object} The JSON object.
*/
toJSON() {
return {
type: this.constructor.name,
min: this.min.toArray( new Array() ),
max: this.max.toArray( new Array() )
};
}
/**
* Restores this instance from the given JSON object.
*
* @param {Object} json - The JSON object.
* @return {AABB} A reference to this AABB.
*/
fromJSON( json ) {
this.min.fromArray( json.min );
this.max.fromArray( json.max );
return this;
}
}
export { AABB };