Skip to content

Commit

Permalink
feature: add box3, plane3, planes3, sphere3
Browse files Browse the repository at this point in the history
  • Loading branch information
0b5vr committed Feb 14, 2022
1 parent 3134c6d commit 52c047e
Show file tree
Hide file tree
Showing 31 changed files with 669 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/math/box3/Box3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Vector3 } from '../vec3/Vector3';
import { box3ContainsPoint } from './box3ContainsPoint';
import type { RawBox3 } from './RawBox3';

/**
* A 3D box.
*/
export class Box3 {
public min: Vector3;
public max: Vector3;

public constructor( min: Vector3 = Vector3.zero, max: Vector3 = Vector3.zero ) {
this.min = min;
this.max = max;
}

/**
* Itself but {@link RawBox3} form.
*/
public get raw(): RawBox3 {
return [ this.min.elements, this.max.elements ];
}

/**
* Test if given point is contained in the box or not.
*
* @param point A point
*/
public containsPoint( point: Vector3 ): boolean {
return box3ContainsPoint( this.raw, point.elements );
}

/**
* Convert {@link RawBox3} to class form.
* @param box A {@link RawBox3}
*/
public static fromRaw( box: RawBox3 ): Box3 {
return new Box3( new Vector3( box[ 0 ] ), new Vector3( box[ 1 ] ) );
}
}
3 changes: 3 additions & 0 deletions src/math/box3/RawBox3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { RawVector3 } from '../vec3/RawVector3';

export type RawBox3 = [ min: RawVector3, max: RawVector3 ];
18 changes: 18 additions & 0 deletions src/math/box3/box3ContainsPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { RawBox3 } from './RawBox3';
import type { RawVector3 } from '../vec3/RawVector3';

/**
* Test if given point is contained in given box or not.
* @param box A box
* @param point A point
*/
export function box3ContainsPoint( box: RawBox3, point: RawVector3 ): boolean {
return (
box[ 0 ][ 0 ] <= point[ 0 ] &&
box[ 1 ][ 0 ] >= point[ 0 ] &&
box[ 0 ][ 1 ] <= point[ 1 ] &&
box[ 1 ][ 1 ] >= point[ 1 ] &&
box[ 0 ][ 2 ] <= point[ 2 ] &&
box[ 1 ][ 2 ] >= point[ 2 ]
);
}
3 changes: 3 additions & 0 deletions src/math/box3/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './Box3';
export * from './box3ContainsPoint';
export * from './RawBox3';
16 changes: 16 additions & 0 deletions src/math/box3/tests/box3ContainsPoint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { box3ContainsPoint } from '../box3ContainsPoint';
import type { RawBox3 } from '../RawBox3';

const box222: RawBox3 = [ [ -1.0, -1.0, -1.0 ], [ 1.0, 1.0, 1.0 ] ];

describe( 'planesContainPoint', () => {
it( 'returns true if point is inside of the box', () => {
const subject = box3ContainsPoint( box222, [ 0.0, 0.5, 0.5 ] );
expect( subject ).toBe( true );
} );

it( 'returns false if point is outside of the box', () => {
const subject = box3ContainsPoint( box222, [ -2.0, 0.0, 0.0 ] );
expect( subject ).toBe( false );
} );
} );
3 changes: 3 additions & 0 deletions src/math/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
export * from './box3';
export * from './euler';
export * from './mat3';
export * from './mat4';
export * from './plane3';
export * from './quat';
export * from './sphere3';
export * from './vec';
export * from './vec3';
export * from './vec4';
Expand Down
68 changes: 68 additions & 0 deletions src/math/plane3/Plane3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Matrix3 } from '../mat3/Matrix3';
import { Matrix4 } from '../mat4/Matrix4';
import { Vector3 } from '../vec3/Vector3';
import { plane3ApplyMatrix4 } from './plane3ApplyMatrix4';
import { plane3DistanceToPoint } from './plane3DistanceToPoint';
import { plane3Normalize } from './plane3Normalize';
import type { RawPlane3 } from './RawPlane3';

/**
* A 3D plane.
*/
export class Plane3 {
public normal: Vector3;
public distance: number;

/**
* Itself but {@link RawPlane3} form.
*/
public get raw(): RawPlane3 {
return [ this.normal.elements, this.distance ];
}

/**
* Normalized plane?
* This normalizes the normal and also divide distance by its original normal length.
*/
public get normalized(): Plane3 {
return Plane3.fromRaw( plane3Normalize( this.raw ) );
}

public constructor( normal: Vector3 = Vector3.pz, distance = 0.0 ) {
this.normal = normal;
this.distance = distance;
}

/**
* Apply given matrix4 to given plane.
*
* @param matrix A matrix4 which will be applied to the plane
* @param normalMatrix A normalMatrix made out of {@link matrix}. Optional
*/
public applyMatrix4( matrix: Matrix4, normalMatrix?: Matrix3 ): Plane3 {
return Plane3.fromRaw(
plane3ApplyMatrix4(
this.raw,
matrix.elements,
normalMatrix?.elements ?? matrix.normalMatrix.elements,
)
);
}

/**
* Return a signed distance from given plane to the given point.
*
* @param point A point
*/
public distanceToPoint( point: Vector3 ): number {
return plane3DistanceToPoint( this.raw, point.elements );
}

/**
* Convert {@link RawPlane3} to class form.
* @param plane A {@link RawPlane3}
*/
public static fromRaw( plane: RawPlane3 ): Plane3 {
return new Plane3( new Vector3( plane[ 0 ] ), plane[ 1 ] );
}
}
86 changes: 86 additions & 0 deletions src/math/plane3/Planes3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Box3 } from '../box3/Box3';
import { Matrix4 } from '../mat4/Matrix4';
import { Plane3 } from './Plane3';
import { Sphere3 } from '../sphere3/Sphere3';
import { Vector3 } from '../vec3/Vector3';
import { planes3ContainPoint } from './planes3ContainPoint';
import { planes3FromBox3 } from './planes3FromBox3';
import { planes3FromProjectionMatrix } from './planes3FromProjectionMatrix';
import { planes3IntersectBox3 } from './planes3IntersectBox3';
import { planes3IntersectSphere3 } from './planes3IntersectSphere3';
import type { RawPlane3 } from './RawPlane3';

/**
* A set of {@link Plane3}.
*/
export class Planes3 {
public planes: Plane3[];

/**
* Itself but {@link RawPlane3}[] form.
*/
public get raw(): RawPlane3[] {
return this.planes.map( ( plane ) => plane.raw );
}

public constructor( planes: Plane3[] ) {
this.planes = planes;
}

/**
* Test if given point is contained in the planes or not.
*
* @param point A point
*/
public containPoint( point: Vector3 ): boolean {
return planes3ContainPoint( this.raw, point.elements );
}

/**
* Test if given box intersects with the planes or not.
*
* @param box A box3
*/
public intersectBox3( box: Box3 ): boolean {
return planes3IntersectBox3( this.raw, box.raw );
}

/**
* Test if given sphere intersects with the planes or not.
*
* It does not do strict intersection test but still should work well with frustum cull use cases.
* See the test case for more details.
*
* @param sphere A sphere3
*/
public intersectSphere3( sphere: Sphere3 ): boolean {
return planes3IntersectSphere3( this.raw, sphere.raw );
}

/**
* Convert {@link RawPlane3}[] to class form.
*
* @param planes A {@link RawPlane3}[]
*/
public static fromRaw( planes: RawPlane3[] ): Planes3 {
return new Planes3( planes.map( ( plane ) => Plane3.fromRaw( plane ) ) );
}

/**
* Generate a set of plane3 out of a {@link RawBox3}.
*
* @param box A box
*/
public static fromBox3( box: Box3 ): Planes3 {
return Planes3.fromRaw( planes3FromBox3( box.raw ) );
}

/**
* Create frustum planes out of given projection matrix.
*
* @param matrix A projection matrix
*/
public static fromProjectionMatrix( matrix: Matrix4 ): Planes3 {
return Planes3.fromRaw( planes3FromProjectionMatrix( matrix.elements ) );
}
}
3 changes: 3 additions & 0 deletions src/math/plane3/RawPlane3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { RawVector3 } from '../vec3/RawVector3';

export type RawPlane3 = [ normal: RawVector3, distance: number ];
11 changes: 11 additions & 0 deletions src/math/plane3/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export * from './Plane3';
export * from './plane3ApplyMatrix4';
export * from './plane3DistanceToPoint';
export * from './plane3Normalize';
export * from './Planes3';
export * from './planes3ContainPoint';
export * from './planes3FromBox3';
export * from './planes3FromProjectionMatrix';
export * from './planes3IntersectBox3';
export * from './planes3IntersectSphere3';
export * from './RawPlane3';
31 changes: 31 additions & 0 deletions src/math/plane3/plane3ApplyMatrix4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { vec3ApplyMatrix3 } from '../vec3/vec3ApplyMatrix3';
import { vec3ApplyMatrix4 } from '../vec3/vec3ApplyMatrix4';
import { vecDot } from '../vec/vecDot';
import { vecNormalize } from '../vec/vecNormalize';
import { vecScale } from '../vec/vecScale';
import type { RawMatrix3 } from '../mat3/RawMatrix3';
import type { RawMatrix4 } from '../mat4/RawMatrix4';
import type { RawPlane3 } from './RawPlane3';

/**
* Apply given matrix4 to given plane.
*
* @param plane A plane
* @param matrix A matrix4 which will be applied to the plane
* @param normalMatrix A normalMatrix made out of {@link matrix}
*/
export function plane3ApplyMatrix4(
[ normal, distance ]: RawPlane3,
matrix: RawMatrix4,
normalMatrix: RawMatrix3,
): RawPlane3 {
// normalなんだからnormalMatrix当てればヨシ!
const newNormal = vecNormalize( vec3ApplyMatrix3( normal, normalMatrix ) );

// とりあえず一点観測してあとでdot取り直しちゃおうぜ!
const coplanar = vecScale( normal, -distance );
const refPoint = vec3ApplyMatrix4( coplanar, matrix );
const newDistance = -vecDot( refPoint, normal );

return [ newNormal, newDistance ];
}
14 changes: 14 additions & 0 deletions src/math/plane3/plane3DistanceToPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { vecDot } from '../vec/vecDot';
import type { RawPlane3 } from './RawPlane3';
import type { RawVector3 } from '../vec3/RawVector3';

/**
* Return a signed distance from given plane to the given point.
* Make sure the `normal` is normalized.
*/
export function plane3DistanceToPoint(
[ normal, distance ]: RawPlane3,
point: RawVector3,
): number {
return vecDot( normal, point ) + distance;
}
16 changes: 16 additions & 0 deletions src/math/plane3/plane3Normalize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { vecLength } from '../vec/vecLength';
import { vecScale } from '../vec/vecScale';
import type { RawPlane3 } from './RawPlane3';

/**
* Normalize a given plane?
* This normalizes the normal and also divide distance by its original normal length.
*
* I don't come up with any use cases other than {@link planesFromProjectionMatrix}.
*
* @param plane The plane you want to normalize
*/
export function plane3Normalize( [ normal, distance ]: RawPlane3 ): RawPlane3 {
const invL = 1.0 / vecLength( normal );
return [ vecScale( normal, invL ), distance * invL ];
}
13 changes: 13 additions & 0 deletions src/math/plane3/planes3ContainPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { plane3DistanceToPoint } from './plane3DistanceToPoint';
import type { RawPlane3 } from './RawPlane3';
import type { RawVector3 } from '../vec3/RawVector3';

/**
* Test if given point is contained in given planes or not.
*
* @param planes A list of planes
* @param point A point
*/
export function planes3ContainPoint( planes: RawPlane3[], point: RawVector3 ): boolean {
return planes.every( ( plane ) => plane3DistanceToPoint( plane, point ) >= 0.0 );
}
18 changes: 18 additions & 0 deletions src/math/plane3/planes3FromBox3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { RawBox3 } from '../box3/RawBox3';
import type { RawPlane3 } from './RawPlane3';

/**
* Generate a set of plane3 out of a {@link RawBox3}.
*
* @param box A box
*/
export function planes3FromBox3( box: RawBox3 ): RawPlane3[] {
return [
[ [ 1.0, 0.0, 0.0 ], -box[ 0 ][ 0 ] ], // xn
[ [ -1.0, 0.0, 0.0 ], box[ 1 ][ 0 ] ], // xp
[ [ 0.0, 1.0, 0.0 ], -box[ 0 ][ 1 ] ], // yn
[ [ 0.0, -1.0, 0.0 ], box[ 1 ][ 1 ] ], // yp
[ [ 0.0, 0.0, 1.0 ], -box[ 0 ][ 2 ] ], // yn
[ [ 0.0, 0.0, -1.0 ], box[ 1 ][ 2 ] ], // yp
];
}
25 changes: 25 additions & 0 deletions src/math/plane3/planes3FromProjectionMatrix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { plane3Normalize } from './plane3Normalize';
import type { RawMatrix4 } from '../mat4/RawMatrix4';
import type { RawPlane3 } from './RawPlane3';

/**
* Create frustum planes out of given projection matrix.
*
* @param m A projection matrix
*/
export function planes3FromProjectionMatrix( m: RawMatrix4 ): RawPlane3[] {
const
m11 = m[ 0 ], m12 = m[ 4 ], m13 = m[ 8 ], m14 = m[ 12 ],
m21 = m[ 1 ], m22 = m[ 5 ], m23 = m[ 9 ], m24 = m[ 13 ],
m31 = m[ 2 ], m32 = m[ 6 ], m33 = m[ 10 ], m34 = m[ 14 ],
m41 = m[ 3 ], m42 = m[ 7 ], m43 = m[ 11 ], m44 = m[ 15 ];

return [
plane3Normalize( [ [ m41 - m11, m42 - m12, m43 - m13 ], m44 - m14 ] ), // xp
plane3Normalize( [ [ m41 + m11, m42 + m12, m43 + m13 ], m44 + m14 ] ), // xn
plane3Normalize( [ [ m41 - m21, m42 - m22, m43 - m23 ], m44 - m24 ] ), // yp
plane3Normalize( [ [ m41 + m21, m42 + m22, m43 + m23 ], m44 + m24 ] ), // yn
plane3Normalize( [ [ m41 - m31, m42 - m32, m43 - m33 ], m44 - m34 ] ), // zn
plane3Normalize( [ [ m41 + m31, m42 + m32, m43 + m33 ], m44 + m34 ] ), // zp
];
}
Loading

0 comments on commit 52c047e

Please sign in to comment.