UNPKG

@threeify/math

Version:

Computer graphics oriented, High performance, tree-shakeable, TypeScript 3D vector math library.

399 lines (359 loc) 9.31 kB
import { Box3 } from './Box3.js'; import { Mat4 } from './Mat4.js'; import { sphereIsEmpty } from './Sphere.Functions.js'; import { Sphere } from './Sphere.js'; import { mat4TransformPosition3, vec3ToString } from './Vec3.Functions.js'; import { vec3Add, vec3Clamp, vec3Distance, vec3Equals, vec3Max, vec3Min, vec3Subtract } from './Vec3.Functions.js'; import { Vec3 } from './Vec3.js'; /** * Calculates and returns the center point of the given box. */ export function box3Center(box: Box3, result = new Vec3()): Vec3 { return result.set( (box.min.x + box.max.x) * 0.5, (box.min.y + box.max.y) * 0.5, (box.min.z + box.max.z) * 0.5 ); } /** * Creates and returns a new Box3 with an empty state. */ export function box3Empty(result = new Box3()): Box3 { result.min.x = result.min.y = result.min.z = Number.POSITIVE_INFINITY; result.max.x = result.max.y = result.max.z = Number.NEGATIVE_INFINITY; return result; } /** * Creates a random Vec3 within the given box and returns it. */ export function box3RandomSample(b: Box3, result = new Vec3()): Vec3 { return result.set( Math.random() * (b.max.x - b.min.x) + b.min.x, Math.random() * (b.max.y - b.min.y) + b.min.y, Math.random() * (b.max.z - b.min.z) + b.min.z ); } /** * Checks if the given box is empty. */ export function box3IsEmpty(b: Box3): boolean { return b.max.x < b.min.x || b.max.y < b.min.y || b.max.z < b.min.z; } /** * Expands the first box by the second box and returns the result. */ export function box3ExpandByBox3(a: Box3, b: Box3, result = new Box3()): Box3 { vec3Min(a.min, b.min, result.min); vec3Max(a.max, b.max, result.max); return result; } /** * Expands a box by a Vec3 and returns the result. */ export function box3ExpandByVec3(b: Box3, v: Vec3, result = new Box3()): Box3 { vec3Min(b.min, v, result.min); vec3Max(b.max, v, result.max); return result; } /** * Expands a box by a Vec3 and returns the result. */ export function box3GrowByVec3(b: Box3, v: Vec3, result = new Box3()): Box3 { vec3Subtract(b.min, v, result.min); vec3Add(b.max, v, result.max); return result; } /** * Expands a box by a scalar and returns the result. */ export function box3GrowByScalar( b: Box3, s: number, result = new Box3() ): Box3 { return box3GrowByVec3(b, new Vec3(s, s, s), result); } /** * Calculates the intersection of two boxes and returns the result. */ export function box3IntersectWithBox3( a: Box3, b: Box3, result = new Box3() ): Box3 { vec3Max(a.min, b.min, result.min); vec3Min(a.max, b.max, result.max); return result; } /** * Translates a box by a Vec3 offset and returns the result. */ export function box3Translate( b: Box3, offset: Vec3, result = new Box3() ): Box3 { vec3Add(b.min, offset, result.min); vec3Add(b.max, offset, result.max); return result; } /** * Checks if two boxes are equal. */ export function box3Equals(a: Box3, b: Box3): boolean { return vec3Equals(a.min, b.min) && vec3Equals(a.max, b.max); } /** * Creates a Box3 from an array of Vec3 coordinates and returns the result. */ export function box3FromVec3Array( array: Float32Array, result = new Box3() ): Box3 { let minX = +Number.POSITIVE_INFINITY; let minY = +Number.POSITIVE_INFINITY; let minZ = +Number.POSITIVE_INFINITY; let maxX = Number.NEGATIVE_INFINITY; let maxY = Number.NEGATIVE_INFINITY; let maxZ = Number.NEGATIVE_INFINITY; for (let i = 0, l = array.length; i < l; i += 3) { const x = array[i]; const y = array[i + 1]; const z = array[i + 2]; if (x < minX) { minX = x; } if (y < minY) { minY = y; } if (z < minZ) { minZ = z; } if (x > maxX) { maxX = x; } if (y > maxY) { maxY = y; } if (z > maxZ) { maxZ = z; } } result.min.set(minX, minY, minZ); result.max.set(maxX, maxY, maxZ); return result; } /** * Creates a Box3 from an array of Vec3 points and returns the result. */ export function box3FromVec3s(points: Vec3[], result = new Box3()): Box3 { box3Empty(result); for (let i = 0, il = points.length; i < il; i++) { box3ExpandByVec3(result, points[i], result); } return result; } /** * Creates a Box3 from a center point and size vector and returns the result. */ export function box3FromCenterAndSize( center: Vec3, size: Vec3, result = new Box3() ): Box3 { result.min.set( center.x - size.x * 0.5, center.y - size.y * 0.5, center.z - size.z * 0.5 ); result.max.set( center.x + size.x * 0.5, center.y + size.y * 0.5, center.z + size.z * 0.5 ); return result; } /** * Creates a Box3 from a Sphere and returns the result. */ export function sphereToBox3(s: Sphere, result = new Box3()): Box3 { if (sphereIsEmpty(s)) { return box3Empty(result); } result.set(s.center, s.center); return box3GrowByScalar(result, s.radius, result); } /** * Checks if a Box3 contains a given Vec3 point. */ export function box3ContainsVec3(box: Box3, point: Vec3): boolean { return !( point.x < box.min.x || point.x > box.max.x || point.y < box.min.y || point.y > box.max.y || point.z < box.min.z || point.z > box.max.z ); } /** * Checks if a Box3 contains another Box3. */ export function box3ContainsBox3(box: Box3, queryBox: Box3): boolean { return ( box.min.x <= queryBox.min.x && queryBox.max.x <= box.max.x && box.min.y <= queryBox.min.y && queryBox.max.y <= box.max.y && box.min.z <= queryBox.min.z && queryBox.max.z <= box.max.z ); } /** * Clamps a Vec3 point to the boundaries of a Box3 and returns the result. */ export function vec3ClampToBox3( point: Vec3, box: Box3, result: Vec3 = new Vec3() ): Vec3 { return vec3Clamp(point, box.min, box.max, result); } /** * Calculates the distance from a Vec3 point to a Box3 and returns the result. */ export function box3DistanceToVec3(box: Box3, point: Vec3): number { const clampedPoint = vec3ClampToBox3(point, box); return vec3Distance(clampedPoint, point); } /** * Transforms a Box3 by a Mat4 and returns the result. */ // TODO: ensure convention for transform<Primitive>() is consistent across types. export function mat4TransformBox3(m: Mat4, b: Box3, result = new Box3()): Box3 { box3Empty(result); if (box3IsEmpty(b)) { return result; } // NOTE: I am using a binary pattern to specify all 2^3 combinations below const v = new Vec3(); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.min.x, b.min.y, b.min.z), v), result ); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.min.x, b.min.y, b.max.z), v), result ); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.min.x, b.max.y, b.min.z), v), result ); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.min.x, b.max.y, b.max.z), v), result ); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.max.x, b.min.y, b.min.z), v), result ); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.max.x, b.min.y, b.max.z), v), result ); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.max.x, b.max.y, b.min.z), v), result ); box3ExpandByVec3( result, mat4TransformPosition3(m, v.set(b.max.x, b.max.y, b.max.z), v), result ); return result; } /** * Translates a Box3 by a Vec3 offset and returns the result. */ export function translateBox3( b: Box3, offset: Vec3, result = new Box3() ): Box3 { b.clone(result); vec3Add(result.min, offset, result.min); vec3Add(result.max, offset, result.max); return result; } /** * Checks if two Box3 intersect. */ export function box3IntersectsBox3(a: Box3, b: Box3): boolean { return ( a.min.x <= b.max.x && a.max.x >= b.min.x && a.min.y <= b.max.y && a.max.y >= b.min.y && a.min.z <= b.max.z && a.max.z >= b.min.z ); } /** * Calculates the dimensional size of a Box3 and returns the result as a Vec3. */ export function box3Size(b: Box3): Vec3 { return vec3Subtract(b.max, b.min); } /** * Calculates the maximum size dimension of a Box3 and returns the result. */ export function box3MaxSize(b: Box3): number { const { max, min } = b; return Math.max(max.x - min.x, max.y - min.y, max.z - min.z); } /** * Scales a Box3 by a Vec3 scale and returns the result. */ export function box3Scale(b: Box3, scale: Vec3, result = new Box3()): Box3 { result.min.x = b.min.x * scale.x; result.min.y = b.min.y * scale.y; result.min.z = b.min.z * scale.z; result.max.x = b.max.x * scale.x; result.max.y = b.max.y * scale.y; result.max.z = b.max.z * scale.z; return result; } /** * Expands a Box3 by a Vec3 point and returns the result. */ export function box3ExpandByPoint3( b: Box3, p: Vec3, result = new Box3() ): Box3 { result.min.x = Math.min(b.min.x, p.x); result.min.y = Math.min(b.min.y, p.y); result.min.z = Math.min(b.min.z, p.z); result.max.x = Math.max(b.max.x, p.x); result.max.y = Math.max(b.max.y, p.y); result.max.z = Math.max(b.max.z, p.z); return result; } export function box3ToString(b: Box3): string { return `(min: ${vec3ToString(b.min)}, max: ${vec3ToString(b.max)})`; }