UNPKG

@valeera/mathx

Version:

A math library written in TS.

168 lines (124 loc) 4.77 kB
import { Vector3, Vector3Like } from "../vector/Vector3"; import { IRay3 } from "./interfaces/IRay3"; import { ISphere } from "../shape/interfaces/ISphere"; import { Plane3 } from "../shape/Plane3"; import { ITriangle3, Triangle3 } from "../shape"; import { EPSILON } from "../constants"; import { Matrix4, Matrix4Like } from "../matrix"; import { Vector2Like } from "../vector"; let dis: number; let r2: number; let d2: number; const v = new Vector3(); export class Ray3 implements IRay3 { public static at = <T extends Vector3Like = Vector3>(a: IRay3, b: number, out: T = new Vector3() as T): T => { Vector3.multiplyScalar(a.direction, b, out); return Vector3.add(a.position, out, out); }; public static distanceToPlane = (ray: IRay3, plane: Plane3) => { const denominator = Vector3.dot(plane.normal, ray.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(ray.position) === 0) { return 0; } return null; } const t = -(Vector3.dot(ray.position, plane.normal) + plane.distance) / denominator; return t >= 0 ? t : null; }; public static distanceToPoint = (a: IRay3, point: Vector3Like): number => { return Math.sqrt(Ray3.distanceSqToPoint(a, point)); }; public static distanceSqToPoint = (a: IRay3, point: Vector3Like): number => { Vector3.minus(point, a.position, v); dis = Vector3.dot(v, a.direction); if (dis < 0) { return Vector3.distanceToSquared(a.position, point); } Vector3.multiplyScalar(a.direction, dis, v); Vector3.add(v, a.position, v); return Vector3.distanceToSquared(v, point); }; public static fromCameraMatrixAndScreenCoord(projectionMatrix: Matrix4Like, worldMatrix: Matrix4Like, screenCoord: Vector2Like, out: Ray3 = new Ray3): Ray3 { Vector3.fromMatrix4Translate(worldMatrix, out.position); Vector3.fromXYZ(screenCoord[0], screenCoord[1], 0.5, out.direction); Matrix4.unproject(out.direction, projectionMatrix, worldMatrix, out.direction); Vector3.minus(out.direction, out.position, out.direction); Vector3.normalize(out.direction, out.direction); return out; } public static lookAt = (a: IRay3, b: Vector3Like, out: Ray3 = new Ray3()): Ray3 => { if (a !== out) { Vector3.fromArray(a.position, 0, out.position); } Vector3.normalize(Vector3.minus(b, a.position, out.direction)); return out; }; public static intersectPlane3Point = <T extends Vector3Like = Vector3>( ray: IRay3, plane: Plane3, out: T = new Vector3() as T, ): T | null => { const t = Ray3.distanceToPlane(ray, plane); if (t === null) { return null; } return Ray3.at(ray, t, out); }; public static intersectSpherePoint = <T extends Vector3Like = Vector3>( ray: IRay3, sphere: ISphere, out: T = new Vector3() as T, ): null | T => { Vector3.minus(sphere.position, ray.position, v); dis = Vector3.dot(v, ray.direction); d2 = Vector3.dot(v, v) - dis * dis; r2 = sphere.radius * sphere.radius; if (d2 > r2) return null; const thc = Math.sqrt(r2 - d2); const t0 = dis - thc; const t1 = dis + thc; if (t0 < 0 && t1 < 0) return null; if (t0 < 0) return Ray3.at(ray, t1, out); return Ray3.at(ray, t0, out); }; public static intersectsTriangle3Point = <T extends Vector3Like = Vector3>(ray: IRay3, triangle: ITriangle3, epsilon = EPSILON, out: T): null | T => { const plane = new Plane3(); plane.fromTriangle3(triangle); const result = Ray3.intersectPlane3Point(ray, plane, out); if (!result) { return null; } const isInTriangle = Triangle3.containsPoint(triangle, epsilon, out); if (!isInTriangle) { return null; } return out; } public static intersectsPlane3 = (ray: IRay3, plane: Plane3): boolean => { const distToPoint = plane.distanceToPoint(ray.position); if (distToPoint === 0) { return true; } const denominator = Vector3.dot(plane.normal, ray.direction); if (denominator * distToPoint < 0) { return true; } return false; }; public static intersectsSphere = (ray: IRay3, sphere: ISphere): boolean => { return Ray3.distanceSqToPoint(ray, sphere.position) <= sphere.radius * sphere.radius; }; public static recast = (ray: IRay3, distance: number, out: Ray3 = new Ray3()): Ray3 => { v.set(Ray3.at(ray, distance)); out.direction.set(v); return out; }; public position: Vector3 = new Vector3(); public direction: Vector3 = new Vector3(); public constructor(position: Vector3Like = Vector3.VECTOR3_ZERO, direction: Vector3Like = Vector3.VECTOR3_BACK) { this.position.set(position); Vector3.normalize(direction, this.direction); } }