tsmathlib
Version:
Typescript Version Math/Physics/CG Library
305 lines (258 loc) • 7.14 kB
text/typescript
/*
* @Author: Xu.Wang
* @Date: 2020-04-01 16:53:50
* @Last Modified by: Xu.Wang
* @Last Modified time: 2020-04-15 22:59:04
*/
import { swap, clampV3 } from '../math/math_utils'
import { Ray } from './ray'
import { Vector3 } from '../math/vector3'
import { Point3 } from '../math/point3'
export class BoundingBox {
private _lower: Vector3
private _upper: Vector3
/**
* Creates an instance of bounding box.
* @param p1
* @param p2
*/
constructor(p1?: Vector3, p2?: Vector3) {
if (p1 === undefined || p2 === undefined) {
this._lower = new Vector3(
Number.MAX_VALUE,
Number.MAX_VALUE,
Number.MAX_VALUE
)
this._upper = new Vector3(
-Number.MAX_VALUE,
-Number.MAX_VALUE,
-Number.MAX_VALUE
)
} else {
this._lower = new Vector3(
Math.min(p1.x(), p2.x()),
Math.min(p1.y(), p2.y()),
Math.min(p1.z(), p2.z())
)
this._upper = new Vector3(
Math.max(p1.x(), p2.x()),
Math.max(p1.y(), p2.y()),
Math.max(p1.z(), p2.z())
)
}
}
setBboxWithCenter(center: Vector3, radius: number) {
let p1 = center.add(radius)
let p2 = center.sub(radius)
this._lower = new Vector3(
Math.min(p1.x(), p2.x()),
Math.min(p1.y(), p2.y()),
Math.min(p1.z(), p2.z())
)
this._upper = new Vector3(
Math.max(p1.x(), p2.x()),
Math.max(p1.y(), p2.y()),
Math.max(p1.z(), p2.z())
)
}
lower(): Vector3 {
return this._lower
}
setLower(l: Vector3) {
this._lower = l
}
setUpper(u: Vector3) {
this._upper = u
}
upper(): Vector3 {
return this._upper
}
// Getter Method
width(): number {
return this.upper().x() - this.lower().x()
}
height(): number {
return this.upper().y() - this.lower().y()
}
depth(): number {
return this.upper().z() - this.lower().z()
}
length(axis: number): number {
return this.upper().data()[axis] - this.lower().data()[axis]
}
midPoint(): Vector3 {
return this.upper().add(this.lower()).div(2)
}
diagonalLength(): number {
return this.upper().sub(this.lower()).length()
}
diagonalLengthSquared(): number {
return this.upper().sub(this.lower()).lengthSquared()
}
contains(point: Vector3): boolean {
if (this.upper().x() < point.x() || this.lower().x() > point.x()) {
return false
}
if (this.upper().y() < point.y() || this.lower().y() > point.y()) {
return false
}
if (this.upper().z() < point.z() || this.lower().z() > point.z()) {
return false
}
return true
}
overlaps(other: BoundingBox): boolean {
if (
this.upper().x() < other.lower().x() ||
this.lower().x() > other.upper().x()
) {
return false
}
if (
this.upper().y() < other.lower().y() ||
this.lower().y() > other.upper().y()
) {
return false
}
if (
this.upper().y() < other.lower().z() ||
this.lower().z() > other.upper().y()
) {
return false
}
return true
}
corner(idx: number): Vector3 {
let h = 1.0 / 2.0
let Vector3offset = new Array<Vector3>()
Vector3offset.push(new Vector3(-h, -h, -h))
Vector3offset.push(new Vector3(h, -h, -h))
Vector3offset.push(new Vector3(-h, h, -h))
Vector3offset.push(new Vector3(h, h, -h))
Vector3offset.push(new Vector3(-h, -h, h))
Vector3offset.push(new Vector3(h, -h, h))
Vector3offset.push(new Vector3(-h, h, h))
Vector3offset.push(new Vector3(h, h, h))
let corv3: Vector3 = new Vector3(this.width(), this.height(), this.depth())
return corv3.mul(Vector3offset[idx]).add(this.midPoint())
}
clamp(val: Vector3): Vector3 {
return clampV3(val, this.lower(), this.upper())
}
isEmpty(): boolean {
return (
this.lower().x() >= this.upper().x() ||
this.lower().y() >= this.upper().y() ||
this.lower().z() >= this.upper().z()
)
}
// Setter Method
reset() {
this._lower = new Vector3(
Number.MAX_VALUE,
Number.MAX_VALUE,
Number.MAX_VALUE
)
this._upper = new Vector3(
-Number.MAX_VALUE,
-Number.MAX_VALUE,
-Number.MAX_VALUE
)
}
mergeWithBbox(bbox: BoundingBox) {
this._lower = new Vector3(
Math.min(this.lower().x(), bbox.lower().x()),
Math.min(this.lower().y(), bbox.lower().y()),
Math.min(this.lower().z(), bbox.lower().z())
)
this._upper = new Vector3(
Math.max(this.upper().x(), bbox.upper().x()),
Math.max(this.upper().y(), bbox.upper().y()),
Math.max(this.upper().z(), bbox.upper().z())
)
}
mergeWithPoint(point: Point3) {
this._lower = new Vector3(
Math.min(this.lower().x(), point.x),
Math.min(this.lower().y(), point.y),
Math.min(this.lower().z(), point.z)
)
this._upper = new Vector3(
Math.max(this.upper().x(), point.x),
Math.max(this.upper().y(), point.y),
Math.max(this.upper().z(), point.z)
)
}
expand(delta: number) {
this.lower().isub(delta)
this.upper().iadd(delta)
}
// Intersection Method
intersects(ray: Ray): boolean {
let Min = 0
let Max = Number.MAX_VALUE
let rayInvDir = ray.direction.rdiv(1)
for (let i = 0; i < 3; ++i) {
let tNear =
(this.lower().data()[i] - ray.origin.data()[i]) * rayInvDir.data()[i]
let tFar =
(this.upper().data()[i] - ray.origin.data()[i]) * rayInvDir.data()[i]
if (tNear > tFar) {
let val = swap(tNear, tFar)
tNear = val[0]
tFar = val[1]
}
Min = tNear > Min ? tNear : Min
Max = tFar < Max ? tFar : Max
if (Min > Max) {
return false
}
}
return true
}
closestIntersection(ray: Ray): BoundingBoxRayIntersection3 {
let intersection = new BoundingBoxRayIntersection3()
let Min = 0
let Max = Number.MAX_VALUE
let rayInvDir = ray.direction.rdiv(1)
for (let i = 0; i < 3; ++i) {
let tNear =
(this.lower().data()[i] - ray.origin.data()[i]) * rayInvDir.data()[i]
let tFar =
(this.upper().data()[i] - ray.origin.data()[i]) * rayInvDir.data()[i]
if (tNear > tFar) {
let val = swap(tNear, tFar)
tNear = val[0]
tFar = val[1]
}
Min = tNear > Min ? tNear : Min
Max = tFar < Max ? tFar : Max
if (Min > Max) {
intersection.isIntersecting = false
return intersection
}
}
intersection.isIntersecting = true
if (this.contains(ray.origin)) {
intersection.Near = Max
intersection.Far = Number.MAX_VALUE
} else {
intersection.Near = Min
intersection.Far = Max
}
return intersection
}
}
export class BoundingBoxRayIntersection3 {
// ! True if the box and ray intersects.
isIntersecting: boolean
// ! Distance to the first intersection point.
Near: number
// ! Distance to the second (and the last) intersection point.
Far: number
constructor() {
this.isIntersecting = false
this.Near = Number.MAX_VALUE
this.Far = Number.MAX_VALUE
}
}