Home Reference Source

src/Plane.js

import { Vector3 } from "./Vector3.js";

/**
 * A vector.
 *
 * @type {Vector3}
 * @private
 */

const a = new Vector3();

/**
 * A vector.
 *
 * @type {Vector3}
 * @private
 */

const b = new Vector3();

/**
 * A plane.
 */

export class Plane {

	/**
	 * Constructs a new plane.
	 *
	 * @param {Vector3} [normal] - The normal.
	 * @param {Number} [constant] - The constant.
	 */

	constructor(normal = new Vector3(1, 0, 0), constant = 0) {

		/**
		 * The normal.
		 *
		 * @type {Vector3}
		 */

		this.normal = normal;

		/**
		 * The constant.
		 *
		 * @type {Number}
		 */

		this.constant = constant;

	}

	/**
	 * Sets the normal and the constant.
	 *
	 * @param {Vector3} normal - The normal.
	 * @param {Number} constant - The constant.
	 * @return {Plane} This plane.
	 */

	set(normal, constant) {

		this.normal.copy(normal);
		this.constant = constant;

		return this;

	}

	/**
	 * Sets the components of this plane.
	 *
	 * @param {Number} x - The X component of the normal.
	 * @param {Number} y - The Y component of the normal.
	 * @param {Number} z - The Z component of the normal.
	 * @param {Number} w - The constant.
	 * @return {Plane} This plane.
	 */

	setComponents(x, y, z, w) {

		this.normal.set(x, y, z);
		this.constant = w;

		return this;

	}

	/**
	 * Copies the given plane.
	 *
	 * @param {Plane} p - A plane.
	 * @return {Plane} This plane.
	 */

	copy(p) {

		this.normal.copy(p.normal);
		this.constant = p.constant;

		return this;

	}

	/**
	 * Clones this plane.
	 *
	 * @return {Plane} The cloned plane.
	 */

	clone() {

		return new this.constructor().copy(this);

	}

	/**
	 * Sets this plane from a normal and a coplanar point.
	 *
	 * @param {Vector3} n - The normal.
	 * @param {Vector3} p - The coplanar point.
	 * @return {Sphere} This sphere.
	 */

	setFromNormalAndCoplanarPoint(n, p) {

		this.normal.copy(n);
		this.constant = -p.dot(this.normal);

		return this;

	}

	/**
	 * Sets this plane from three distinct coplanar points.
	 *
	 * @param {Vector3} p0 - A coplanar point.
	 * @param {Vector3} p1 - A coplanar point.
	 * @param {Vector3} p2 - A coplanar point.
	 * @return {Plane} This plane.
	 */

	setFromCoplanarPoints(p0, p1, p2) {

		const normal = a.subVectors(p2, p1).cross(b.subVectors(p0, p1)).normalize();

		this.setFromNormalAndCoplanarPoint(normal, a);

		return this;

	}

	/**
	 * Normalizes this plane.
	 *
	 * @return {Plane} This plane.
	 */

	normalize() {

		const inverseNormalLength = 1.0 / this.normal.length();

		this.normal.multiplyScalar(inverseNormalLength);
		this.constant *= inverseNormalLength;

		return this;

	}

	/**
	 * Negates this plane.
	 *
	 * @return {Plane} This plane.
	 */

	negate() {

		this.normal.negate();
		this.constant = -this.constant;

		return this;

	}

	/**
	 * Calculates the distance from this plane to the given point.
	 *
	 * @param {Vector3} p - A point.
	 * @return {Number} The length.
	 */

	distanceToPoint(p) {

		return this.normal.dot(p) + this.constant;

	}

	/**
	 * Calculates the distance from this plane to the given sphere.
	 *
	 * @param {Sphere} s - A sphere.
	 * @return {Number} The length.
	 */

	distanceToSphere(s) {

		return this.distanceToPoint(s.center) - s.radius;

	}

	/**
	 * Projects the given point on this plane.
	 *
	 * @param {Vector3} p - A point.
	 * @param {Vector3} [target] - A target vector. If none is provided, a new one will be created.
	 * @return {Vector3} The projected point.
	 */

	projectPoint(p, target) {

		return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(p)).add(p);

	}

	/**
	 * Calculates a coplanar point and returns it.
	 *
	 * @param {Vector3} [target] - A target vector. If none is provided, a new one will be created.
	 * @return {Vector3} A coplanar plane.
	 */

	coplanarPoint(target) {

		return target.copy(this.normal).multiplyScalar(-this.constant);

	}

	/**
	 * Translates this plane.
	 *
	 * @param {Vector3} offset - An offset.
	 * @return {Plane} This plane.
	 */

	translate(offset) {

		this.constant -= offset.dot(this.normal);

		return this;

	}

	/**
	 * Finds the point of intersection between this plane and a given line.
	 *
	 * @param {Line3} l - A line.
	 * @param {Vector3} [target] - A target vector. If none is provided, a new one will be created.
	 * @return {Vector3} The intersection point.
	 */

	intersectLine(l, target) {

		const direction = l.delta(a);
		const denominator = this.normal.dot(direction);

		if(denominator === 0) {

			// The line is coplanar, return origin.
			if(this.distanceToPoint(l.start) === 0) {

				target.copy(l.start);

			}

		} else {

			const t = -(l.start.dot(this.normal) + this.constant) / denominator;

			if(t >= 0 && t <= 1) {

				target.copy(direction).multiplyScalar(t).add(l.start);

			}

		}

		return target;

	}

	/**
	 * Checks if this plane intersects with the given line.
	 *
	 * @param {Line3} l - A line.
	 * @return {Boolean} Whether this plane intersects with the given line.
	 */

	intersectsLine(l) {

		const startSign = this.distanceToPoint(l.start);
		const endSign = this.distanceToPoint(l.end);

		return ((startSign < 0 && endSign > 0) || (endSign < 0 && startSign > 0));

	}

	/**
	 * Checks if this plane intersects with the given box.
	 *
	 * @param {Box3} b - A box.
	 * @return {Boolean} Whether this plane intersects with the given box.
	 */

	intersectsBox(b) {

		return b.intersectsPlane(this);

	}

	/**
	 * Checks if this plane intersects with the given sphere.
	 *
	 * @param {Sphere} s - A sphere.
	 * @return {Boolean} Whether this plane intersects with the given sphere.
	 */

	intersectsSphere(s) {

		return s.intersectsPlane(this);

	}

	/**
	 * Checks if this plane equals the given one.
	 *
	 * @param {Plane} p - A plane.
	 * @return {Boolean} Whether this plane equals the given one.
	 */

	equals(p) {

		return (p.normal.equals(this.normal) && (p.constant === this.constant));

	}

}