avocado-old/packages/math/rectangle/index.js
2019-03-19 13:49:21 -05:00

207 lines
4.9 KiB
JavaScript

// Rectangle operations.
// **Rectangle** is a utility class to help with rectangle operations. A
// rectangle is implemented as a 4-element array. Element 0 is *x*, element
// 1 is *y*, element 2 is *width* and element 3 is *height*.
import * as Vector from '../vector';
// Check if a rectangle intersects with another rectangle.
//
// avocado> Rectangle.intersects [0, 0, 16, 16], [8, 8, 24, 24]
// true
//
// avocado> Rectangle.intersects [0, 0, 16, 16], [16, 16, 32, 32]
// false
export function intersects (l, r) {
if (l[0] >= r[0] + r[2]) {
return false;
}
if (r[0] >= l[0] + l[2]) {
return false;
}
if (l[1] >= r[1] + r[3]) {
return false;
}
if (r[1] >= l[1] + l[3]) {
return false;
}
return true;
}
// Check if a rectangle is touching a vector.
//
// avocado> Rectangle.isTouching [0, 0, 16, 16], [0, 0]
// true
//
// avocado> Rectangle.intersects [0, 0, 16, 16], [16, 16]
// false
export function isTouching (r, v) {
if (v[0] < r[0]) {
return false;
}
if (v[1] < r[1]) {
return false;
}
if (v[0] >= r[0] + r[2]) {
return false;
}
if (v[1] >= r[1] + r[3]) {
return false;
}
return true
}
// Compose a rectangle from a position vector and a size vector.
//
// avocado> Rectangle.compose [0, 0], [16, 16]
// [0, 0, 16, 16]
export function compose (l, r) {
return [l[0], l[1], r[0], r[1]];
}
// Make a deep copy of the rectangle.
//
// avocado> rectangle = [0, 0, 16, 16]
// avocado> rectangle is Rectangle.copy rectangle
// false
export function copy (r) {
return [r[0], r[1], r[2], r[3]];
}
// Convert a rectangle to an object. If you *useShortKeys*, The width and
// height keys will be named w and h, respectively.
//
// avocado> Rectangle.toObject [3, 4, 5, 6]
// {x: 3, y: 4, width: 5, height: 6}
//
// avocado> Rectangle.toObject [3, 4, 5, 6], true
// {x: 3, y: 4, w: 5, h: 6}
export function toObject (r, useShortKeys = false) {
const whKeys = useShortKeys ? ['w', 'h'] : ['width', 'height'];
const O = {x: r[0], y: r[1]};
O[whKeys[0]] = r[2];
O[whKeys[1]] = r[3];
return O;
}
export function fromObject(O) {
return [O.x, O.y, O.width, O.height];
}
// Returns the position of a rectangle.
//
// avocado> Rectangle.position [8, 8, 16, 16]
// [8, 8]
export function position(r) {
return [r[0], r[1]];
}
// Returns the size of a rectangle.
//
// avocado> Rectangle.size [8, 8, 16, 16]
// [16, 16]
export function size(r) {
return [r[2], r[3]];
}
// Compute the intersection rectangle of two rectangles.
//
// avocado> Rectangle.intersection [0, 0, 16, 16], [8, 8, 24, 24]
// [8, 8, 8, 8]
export function intersection(l, r) {
if (!intersects(l, r)) {
return [0, 0, 0, 0];
}
const x = Math.max(l[0], r[0]);
const y = Math.max(l[1], r[1]);
const lx2 = l[0] + l[2];
const rx2 = r[0] + r[2];
const ly2 = l[1] + l[3];
const ry2 = r[1] + r[3];
const w = (lx2 <= rx2 ? lx2 : rx2) - x;
const h = (ly2 <= ry2 ? ly2 : ry2) - y;
return [x, y, w, h];
}
// Returns a rectangle translated along the [*x*, *y*] axis of a vector.
//
// avocado> Rectangle.translated [0, 0, 16, 16], [8, 8]
// [8, 8, 16, 16]
export function translated(r, v) {
return compose(Vector.add(v, position(r)), size(r));
}
// Checks if a rectangle is null. A null rectangle is defined by having any
// 0-length axis.
//
// avocado> Rectangle.isNull [0, 0, 1, 1]
// false
//
// avocado> Rectangle.isNull [0, 0, 1, 0]
// true
export function isNull(r) {
if (!r) {
return true;
}
if (4 !== r.length) {
return true;
}
return Vector.isNull(size(r));
}
// Check whether a rectangle equals another rectangle.
//
// avocado> Rectangle.equals [0, 0, 0, 0], [0, 0, 0, 1]
// false
//
// avocado> Rectangle.equals [0, 0, 0, 0], [0, 0, 0, 0]
// true
export function equals(l, r) {
return l[0] === r[0] && l[1] === r[1] && l[2] === r[2] && l[3] === r[3];
}
// Returns a rectangle that is the united area of two rectangles.
//
// avocado> Rectangle.united [0, 0, 4, 4], [4, 4, 8, 8]
// [0, 0, 12, 12]
export function united(l, r) {
if (isNull(l)) {
return r;
}
if (isNull(r)) {
return l;
}
const x = Math.min(l[0], r[0]);
const y = Math.min(l[1], r[1]);
const x2 = Math.max(l[0] + l[2], r[0] + r[2]);
const y2 = Math.max(l[1] + l[3], r[1] + r[3]);
return [x, y, x2 - x, y2 - y];
}
// Round the position and size of a rectangle.
//
// avocado> Rectangle.round [3.14, 4.70, 5.32, 1.8]
// [3, 5, 5, 2]
export function round(r) {
return [
Math.round(r[0]),
Math.round(r[1]),
Math.round(r[2]),
Math.round(r[3]),
];
}
// Floor the position and size of a rectangle.
//
// avocado> Rectangle.floor [3.14, 4.70, 5.32, 1.8]
// [3, 4, 5, 1]
export function floor(r) {
return [
Math.floor(r[0]),
Math.floor(r[1]),
Math.floor(r[2]),
Math.floor(r[3]),
];
}