UNPKG

@valeera/mathx

Version:

A math library written in TS.

262 lines (216 loc) 6.7 kB
import { Vector3, IVector3, Vector3Like } from "../vector/Vector3"; import { ICube } from "./interfaces/ICube"; import { ISphere } from "./interfaces/ISphere"; import { ITriangle3 } from "./interfaces/ITriangle3"; // import Matrix3 from "../matrix/Matrix3"; const v1: Vector3 = new Vector3(); const v2: Vector3 = new Vector3(); const v0: Vector3 = new Vector3(); const f1: Vector3 = new Vector3(); const f2: Vector3 = new Vector3(); const f0: Vector3 = new Vector3(); const ta: Vector3 = new Vector3(); // const ma: Matrix3 = new Matrix3(); const tb: Vector3 = new Vector3(); const vA: Vector3 = new Vector3(); export class Cube implements ICube { public static center = <T extends Vector3Like = Vector3>(a: ICube, out: T = new Vector3() as T): T => { Vector3.add(a.min, a.max, out); return Vector3.multiplyScalar(out, 0.5, out); }; public static clampPoint = <T extends Vector3Like = Vector3>( a: ICube, point: Vector3Like, out: T = new Vector3() as T, ): T => { return Vector3.clamp(point, a.min, a.max, out); }; public static containsPoint = (a: ICube, b: Vector3Like): boolean => { return ( b[0] >= a.min[0] && b[0] <= a.max[0] && b[1] >= a.min[1] && b[1] <= a.max[1] && b[2] >= a.min[2] && b[2] <= a.max[2] ); }; public static containsCube = (a: ICube, b: ICube): boolean => { return ( a.min[0] <= b.min[0] && b.max[0] <= a.max[0] && a.min[1] <= b.min[1] && b.max[1] <= a.max[1] && a.min[2] <= b.min[2] && b.max[2] <= a.max[2] ); }; public static depth = (a: ICube): number => { return a.max[2] - a.min[2]; }; public static equals = (a: ICube, b: ICube): boolean => { return Vector3.equals(a.min, b.min) && Vector3.equals(a.max, b.max); }; public static getSize = <T extends Vector3Like = Vector3>(a: ICube, out: T = new Vector3() as T): T => { return Vector3.minus(a.max, a.min, out); }; public static height = (a: ICube): number => { return a.max[1] - a.min[1]; }; public static intersect = (a: ICube, b: ICube, out: Cube = new Cube()): Cube => { Vector3.max(a.min, b.min, out.min); Vector3.min(a.max, b.max, out.max); return out; }; public static intersectsBox = (a: ICube, b: ICube): boolean => { return !( b.max[0] < a.min[0] || b.min[0] > a.max[0] || b.max[1] < a.min[1] || b.min[1] > a.max[1] || b.max[2] < a.min[2] || b.min[2] > a.max[2] ); }; public static intersectsSphere = (a: ICube, b: ISphere): boolean => { Cube.clampPoint(a, b.position, ta); return Vector3.distanceToSquared(ta, b.position) <= b.radius * b.radius; }; public static intersectsTriangle = (a: ICube, b: ITriangle3): boolean => { if (Cube.isEmpty(a)) { return false; } Cube.center(a, ta); Vector3.minus(a.max, ta, tb); // translate triangle to aabb origin Vector3.minus(b.a, ta, v0); Vector3.minus(b.b, ta, v1); Vector3.minus(b.c, ta, v2); // compute edge vectors for triangle Vector3.minus(v1, v0, f0); Vector3.minus(v2, v1, f1); Vector3.minus(v0, v2, f2); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) const axes = [ 0, -f0[2], f0[1], 0, -f1[2], f1[1], 0, -f2[2], f2[1], f0[2], 0, -f0[0], f1[2], 0, -f1[0], f2[2], 0, -f2[0], -f0[1], f0[0], 0, -f1[1], f1[0], 0, -f2[1], f2[0], 0, ]; if (!satForAxes(axes, v0, v1, v2, tb)) { return false; } // test 3 face normals from the aabb // ta = Matrix3.identity(); ??? if (!satForAxes(axes, v0, v1, v2, tb)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here Vector3.cross(f0, f1, ta); // axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(ta, v0, v1, v2, tb); }; public static isEmpty = (a: ICube): boolean => { return a.max[0] < a.min[0] || a.max[0] < a.min[0] || a.max[0] < a.min[0]; }; public static round = (a: ICube, out: Cube = new Cube()): Cube => { Vector3.round(a.min, out.min); Vector3.round(a.max, out.max); return out; }; public static size = <T extends Vector3Like = Vector3>(a: ICube, out: T = new Vector3() as T): T => { return Vector3.minus(a.max, a.min, out); }; public static stretch = ( a: ICube, b: Float32Array | number[] | IVector3, c: Float32Array | number[] | IVector3, out: Cube = new Cube(), ): Cube => { Vector3.add(a.min, b, out.min); Vector3.add(a.max, c, out.max); return out; }; public static surfaceArea = (a: ICube): number => { Cube.getSize(a, ta); return (ta.x * ta.y + ta.x * ta.z + ta.y * ta.z) * 2; }; public static translate = (a: ICube, b: Float32Array | number[] | IVector3, out: Cube = new Cube()): Cube => { Vector3.add(a.min, b, out.min); Vector3.add(a.max, b, out.max); return out; }; public static union = (a: ICube, b: ICube, out: Cube = new Cube()): Cube => { Vector3.min(a.min, b.min, out.min); Vector3.max(a.max, b.max, out.max); return out; }; public static volume = (a: ICube): number => { return (a.max[0] - a.min[0]) * (a.max[1] - a.min[1]) * (a.max[2] - a.min[2]); }; public static width = (a: ICube): number => { return a.max[0] - a.min[0]; }; public min: Vector3 = new Vector3(); public max: Vector3 = new Vector3(); public constructor(a: Vector3Like = new Vector3(), b: Vector3Like = Vector3.VECTOR3_ONE) { Vector3.min(a, b, this.min); Vector3.max(a, b, this.max); } } let i: number; let j: number; let p0: number; let p1: number; let p2: number; let r: number; function satForAxes( axes: number[] | Float32Array, v0: Float32Array, v1: Float32Array, v2: Float32Array, extents: Float32Array, ) { for (i = 0, j = axes.length - 3; i <= j; i += 3) { Vector3.fromArray(axes, i, vA); // project the aabb onto the seperating axis r = extents[0] * Math.abs(vA[0]) + extents[1] * Math.abs(vA[1]) + extents[2] * Math.abs(vA[2]); // project all 3 vertices of the triangle onto the seperating axis p0 = Vector3.dot(v0, vA); p1 = Vector3.dot(v1, vA); p2 = Vector3.dot(v2, vA); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is seperating and we can exit return false; } } return true; }