game_vector_bounds.js

import Vec2 from "./vec2.js";

/**
 * @module Bounds
 * @fileoverview Contains bounds class.
 */

/**
 * @class
 * Basic bounding box.
 */
class Bounds {
    /**
     * Create a bounding box from width and height (centered)
     * @param {number} width Width of bounding box
     * @param {number} height Height of bounding box
     * @returns {Bounds} Resulting bounding box
     */
    static fromWidthAndHeight(width, height) {
        const w2 = width / 2;
        const h2 = height / 2;

        return new Bounds(
            new Vec2(-w2, -h2),
            new Vec2(w2, h2)
        );
    }

    /**
     * Create a new bounding box (parameters do not have to necessarily be min and max).
     * @param {Vec2} from First corner
     * @param {Vec2} to Second corner
     * @constructor
     */
    constructor(from = new Vec2(), to = new Vec2()) {
        this._min = Vec2.min(from, to);
        this._max = Vec2.max(from, to);
    }

    /**
     * Get a copy of the bounding box.
     * @returns {Bounds} Copied bounding box
     */
    copy() {
        return new Bounds(this._min, this._max);
    }


    //
    // Getters
    //

    /**
     * Minimum (top-left) point
     * @returns {Vec2} Minimum corner
     */
    get min() {
        return this._min.copy();
    }

    /**
     * Maximum (bottom-right) point
     * @returns {Vec2} Maximum corner
     */
    get max() {
        return this._max.copy();
    }

    /**
     * Get the width of the bounding box.
     * @returns {number} Width of bounds
     */
    get width() {
        return this._max.x - this._min.x;
    }

    /**
     * Get the height of the bounding box.
     * @returns {number} Height of bounds
     */
    get height() {
        return this._max.y - this._min.y;
    }

    /**
     * Get the x value of the left side of the bounds.
     * @returns {number} Left side position along x-axis
     */
    get left() {
        return this._min.x;
    }

    /**
     * Get the x value of the right side of the bounds.
     * @returns {number} Right side position along x-axis
     */
    get right() {
        return this._max.x;
    }

    /**
     * Get the y value of the top side of the bounds.
     * @returns {number} Top side position along y-axis
     */
    get top() {
        return this._min.y;
    }

    /**
     * Get the y value of the bottom side of the bounds.
     * @returns {number} Bottom side position along y-axis
     */
    get bottom() {
        return this._max.y;
    }

    /**
     * Get the top-right corner of the bounding box.
     * @returns {Vec2} Top-right corner
     */
    get topRight() {
        return new Vec2(this._max.x, this._min.y);
    }

    /**
     * Get the bottom-left corner of the bounding box.
     * @returns {Vec2} Bottom-left corner
     */
    get bottomLeft() {
        return new Vec2(this._min.x, this._max.y);
    }


    //
    // Checks & Calculations
    //

    /**
     * Offset the bounding box.
     * @param {Vec2} offset Vector to offset by
     * @returns {Bounds} Offset bounding box
     */
    add(offset) {
        return new Bounds(
            this._min.add(offset),
            this._max.add(offset)
        );
    }

    /**
     * Merge the bounds with another.
     * @param {Bounds} other Other bounding box to merge with
     * @returns {Bounds} Merged/combined bounding box
     */
    merge(other) {
        return new Bounds(
            Vec2.min(this._min, other._min),
            Vec2.max(this._max, other._max)
        );
    }

    /**
     * Scale the bounding box.
     * @param {number|Vec2} val Value to scale bounds by
     * @returns {Bounds} Scaled bounding box
     */
    scale(val) {
        val = Vec2.from(val);

        return new Bounds(
            this._min.mul(val),
            this._max.mul(val)
        );
    }

    /**
     * Check if a point is within the bounding box.
     * @param {Vec2} pos Point to check for
     * @returns {boolean} Whether point is within bounds
     */
    within(pos) {
        return pos.x >= this._min.x && pos.x <= this._max.x
            && pos.y >= this._min.y && pos.y <= this._max.y;
    }

    /**
     * Check if the bounding box overlaps another.
     * @param {Bounds} other Other bounding box to check with
     * @returns {boolean} Whether bounding boxes overlap
     */
    overlaps(other) {
        if (this.right < other.left || this.left > other.right)
            return false;

        return !(this.bottom < other.top || this.top > other.bottom);
    }
}

export default Bounds;