UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

299 lines (272 loc) 9.24 kB
import { Matrix3 } from './Matrix3.js'; import { Vector3 } from './Vector3.js'; const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); /** * A two dimensional surface that extends infinitely in 3D space, represented * in [Hessian normal form]{@link http://mathworld.wolfram.com/HessianNormalForm.html} * by a unit length normal vector and a constant. */ class Plane { /** * Constructs a new plane. * * @param {Vector3} [normal=(1,0,0)] - A unit length vector defining the normal of the plane. * @param {number} [constant=0] - The signed distance from the origin to the plane. */ constructor(normal = new Vector3(1, 0, 0), constant = 0) { /** * This flag can be used for type testing. * * @type {boolean} * @readonly * @default true */ this.isPlane = true; /** * A unit length vector defining the normal of the plane. * * @type {Vector3} */ this.normal = normal; /** * The signed distance from the origin to the plane. * * @type {number} * @default 0 */ this.constant = constant; } /** * Sets the plane components by copying the given values. * * @param {Vector3} normal - The normal. * @param {number} constant - The constant. * @return {Plane} A reference to this plane. */ set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } /** * Sets the plane components by defining `x`, `y`, `z` as the * plane normal and `w` as the constant. * * @param {number} x - The value for the normal's x component. * @param {number} y - The value for the normal's y component. * @param {number} z - The value for the normal's z component. * @param {number} w - The constant value. * @return {Plane} A reference to this plane. */ setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } /** * Sets the plane from the given normal and coplanar point (that is a point * that lies onto the plane). * * @param {Vector3} normal - The normal. * @param {Vector3} point - A coplanar point. * @return {Plane} A reference to this plane. */ setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } /** * Sets the plane from three coplanar points. The winding order is * assumed to be counter-clockwise, and determines the direction of * the plane normal. * * @param {Vector3} a - The first coplanar point. * @param {Vector3} b - The second coplanar point. * @param {Vector3} c - The third coplanar point. * @return {Plane} A reference to this plane. */ setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } /** * Copies the values of the given plane to this instance. * * @param {Plane} plane - The plane to copy. * @return {Plane} A reference to this plane. */ copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } /** * Normalizes the plane normal and adjusts the constant accordingly. * * @return {Plane} A reference to this plane. */ normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } /** * Negates both the plane normal and the constant. * * @return {Plane} A reference to this plane. */ negate() { this.constant *= -1; this.normal.negate(); return this; } /** * Returns the signed distance from the given point to this plane. * * @param {Vector3} point - The point to compute the distance for. * @return {number} The signed distance. */ distanceToPoint(point) { return this.normal.dot(point) + this.constant; } /** * Returns the signed distance from the given sphere to this plane. * * @param {Sphere} sphere - The sphere to compute the distance for. * @return {number} The signed distance. */ distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } /** * Projects a the given point onto the plane. * * @param {Vector3} point - The point to project. * @param {Vector3} target - The target vector that is used to store the method's result. * @return {Vector3} The projected point on the plane. */ projectPoint(point, target) { return target.copy(point).addScaledVector(this.normal, -this.distanceToPoint(point)); } /** * Returns the intersection point of the passed line and the plane. Returns * `null` if the line does not intersect. Returns the line's starting point if * the line is coplanar with the plane. * * @param {Line3} line - The line to compute the intersection for. * @param {Vector3} target - The target vector that is used to store the method's result. * @return {?Vector3} The intersection point. */ intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(line.start).addScaledVector(direction, t); } /** * Returns `true` if the given line segment intersects with (passes through) the plane. * * @param {Line3} line - The line to test. * @return {boolean} Whether the given line segment intersects with the plane or not. */ intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } /** * Returns `true` if the given bounding box intersects with the plane. * * @param {Box3} box - The bounding box to test. * @return {boolean} Whether the given bounding box intersects with the plane or not. */ intersectsBox(box) { return box.intersectsPlane(this); } /** * Returns `true` if the given bounding sphere intersects with the plane. * * @param {Sphere} sphere - The bounding sphere to test. * @return {boolean} Whether the given bounding sphere intersects with the plane or not. */ intersectsSphere(sphere) { return sphere.intersectsPlane(this); } /** * Returns a coplanar vector to the plane, by calculating the * projection of the normal at the origin onto the plane. * * @param {Vector3} target - The target vector that is used to store the method's result. * @return {Vector3} The coplanar point. */ coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } /** * Apply a 4x4 matrix to the plane. The matrix must be an affine, homogeneous transform. * * The optional normal matrix can be pre-computed like so: * ```js * const optionalNormalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); * ``` * * @param {Matrix4} matrix - The transformation matrix. * @param {Matrix4} [optionalNormalMatrix] - A pre-computed normal matrix. * @return {Plane} A reference to this plane. */ applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } /** * Translates the plane by the distance defined by the given offset vector. * Note that this only affects the plane constant and will not affect the normal vector. * * @param {Vector3} offset - The offset vector. * @return {Plane} A reference to this plane. */ translate(offset) { this.constant -= offset.dot(this.normal); return this; } /** * Returns `true` if this plane is equal with the given one. * * @param {Plane} plane - The plane to test for equality. * @return {boolean} Whether this plane is equal with the given one. */ equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } /** * Returns a new plane with copied values from this instance. * * @return {Plane} A clone of this instance. */ clone() { return new this.constructor().copy(this); } } export { Plane };