pex-geom
Version:
Geometry intersection and bounding volume helpers for PEX.
277 lines (248 loc) • 6.28 kB
JavaScript
/** @module rect */
import { vec2 } from "pex-math";
/**
* Creates a new rectangle.
* @returns {import("./types.js").rect}
*/
export function create() {
return [
[Infinity, Infinity],
[-Infinity, -Infinity],
];
}
/**
* Reset a rectangle.
* @param {import("./types.js").rect} a
* @returns {import("./types.js").rect}
*/
export function empty(a) {
a[0][0] = a[0][1] = Infinity;
a[1][0] = a[1][1] = -Infinity;
return a;
}
/**
* Copies a rectangle.
* @param {import("./types.js").rect} a
* @returns {import("./types.js").rect}
*/
export function copy(a) {
return [a[0].slice(), a[1].slice()];
}
/**
* Sets a rectangle to another.
* @param {import("./types.js").rect} a
* @param {import("./types.js").rect} b
* @returns {import("./types.js").rect}
*/
export function set(a, b) {
a[0][0] = b[0][0];
a[0][1] = b[0][1];
a[1][0] = b[1][0];
a[1][1] = b[1][1];
return a;
}
/**
* Checks if a rectangle is empty.
* @param {import("./types.js").rect} a
* @returns {boolean}
*/
export function isEmpty(a) {
return a[0][0] > a[1][0] || a[0][1] > a[1][1];
}
/**
* Updates a rectangle from a list of points.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2[] | import("./types.js").TypedArray} points
* @returns {import("./types.js").rect}
*/
export function fromPoints(a, points) {
const isTypedArray = !Array.isArray(points);
for (let i = 0; i < points.length / (isTypedArray ? 2 : 1); i++) {
includePoint(a, isTypedArray ? points.slice(i * 2) : points[i]);
}
return a;
}
/**
* Returns a list of 4 points from a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2[]} points
* @returns {import("./types.js").vec2[]}
*/
export function getCorners(a, points = []) {
points[0] = a[0].slice();
points[1] = [a[0][1], a[1][0]];
points[2] = a[1].slice();
points[3] = [a[1][0], a[0][1]];
return points;
}
/**
* Scales a rectangle.
* @param {import("./types.js").rect} a
* @param {number} n
* @returns {import("./types.js").rect}
*/
export function scale(a, n) {
a[0][0] *= n;
a[0][1] *= n;
a[1][0] *= n;
a[1][1] *= n;
return a;
}
/**
* Sets the size of a rectangle using width and height.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} size
* @returns {import("./types.js").rect}
*/
export function setSize(a, size) {
a[1][0] = a[0][0] + size[0];
a[1][1] = a[0][1] + size[1];
return a;
}
/**
* Returns the size of a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} out
* @returns {import("./types.js").vec2}
*/
export function size(a, out = []) {
out[0] = width(a);
out[1] = height(a);
return out;
}
/**
* Returns the width of a rectangle.
* @param {import("./types.js").rect} a
* @returns {number}
*/
export function width(a) {
return a[1][0] - a[0][0];
}
/**
* Returns the height of a rectangle.
* @param {import("./types.js").rect} a
* @returns {number}
*/
export function height(a) {
return a[1][1] - a[0][1];
}
/**
* Returns the aspect ratio of a rectangle.
* @param {import("./types.js").rect} a
* @returns {number}
*/
export function aspectRatio(a) {
return width(a) / height(a);
}
/**
* Sets the position of a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} p
* @returns {import("./types.js").rect}
*/
export function setPosition(a, [x, y]) {
const w = width(a);
const h = height(a);
a[0][0] = x;
a[0][1] = y;
a[1][0] = x + w;
a[1][1] = y + h;
return a;
}
/**
* Returns the center of a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} out
* @returns {import("./types.js").rect}
*/
export function center(a, out = []) {
out[0] = a[0][0] + width(a) * 0.5;
out[1] = a[0][1] + height(a) * 0.5;
return out;
}
/**
* Checks if a point is inside a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} p
* @returns {boolean}
*/
export function containsPoint(a, [x, y]) {
return x >= a[0][0] && x <= a[1][0] && y >= a[0][1] && y <= a[1][1];
}
/**
* Checks if a rectangle is inside another rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").rect} b
* @returns {boolean}
*/
export function containsRect(a, b) {
return containsPoint(a, b[0]) && containsPoint(a, b[1]);
}
/**
* Includes a point in a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} p
* @returns {import("./types.js").rect}
*/
export function includePoint(a, [x, y]) {
const minx = a[0][0];
const miny = a[0][1];
const maxx = a[1][0];
const maxy = a[1][1];
a[0][0] = minx > x ? x : minx;
a[0][1] = miny > y ? y : miny;
a[1][0] = maxx < x ? x : maxx;
a[1][1] = maxy < y ? y : maxy;
return a;
}
/**
* Includes a rectangle in another rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").rect} b
* @returns {import("./types.js").rect}
*/
export function includeRect(a, b) {
includePoint(a, b[0]);
includePoint(a, b[1]);
return a;
}
/**
* Maps a point into the dimensions of a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} p
* @returns {import("./types.js").vec2}
*/
export function mapPoint(a, p) {
const minx = a[0][0];
const miny = a[0][1];
const maxx = a[1][0];
const maxy = a[1][1];
p[0] = Math.max(minx, Math.min(p[0], maxx)) - minx;
p[1] = Math.max(miny, Math.min(p[1], maxy)) - miny;
return p;
}
/**
* Clamps a point into the dimensions of a rectangle.
* @param {import("./types.js").rect} a
* @param {import("./types.js").vec2} p
* @returns {import("./types.js").vec2}
*/
export function clampPoint(a, p) {
const minx = a[0][0];
const miny = a[0][1];
const maxx = a[1][0];
const maxy = a[1][1];
p[0] = Math.max(minx, Math.min(p[0], maxx));
p[1] = Math.max(miny, Math.min(p[1], maxy));
return p;
}
/**
* Prints a rect to a string.
* @param {import("./types.js").rect} a
* @param {number} [precision=4]
* @returns {string}
*/
export function toString(a, precision = 4) {
// prettier-ignore
return `[${vec2.toString(a[0], precision)}, ${vec2.toString(a[1], precision)}]`;
}