Source: BoundingBox.js

/**
 * Represents an axis-aligned rectangular bounding box that can be wrapped tightly around geometric elements.
 * The box is defined by two opposite vertices (low and high corners) and provides a comprehensive set of
 * utility methods for manipulating and analyzing bounding boxes.
 */
class BoundingBox {
    /**
     * Creates a new BoundingBox instance.
     * @param {Object} [options] - Configuration options for the bounding box
     * @param {number} [options.xLow=1e20] - X coordinate of the lower corner
     * @param {number} [options.yLow=1e20] - Y coordinate of the lower corner
     * @param {number} [options.xHigh=-1e20] - X coordinate of the upper corner
     * @param {number} [options.yHigh=-1e20] - Y coordinate of the upper corner
     */
    constructor(options) {
        Object.assign(this, {
            xLow: 1e20,
            yLow: 1e20,
            xHigh: -1e20,
            yHigh: -1e20
        });
        Object.assign(this, options);
    }

    /**
     * Initializes the bounding box from an array of coordinates.
     * @param {number[]} x - Array containing coordinates in order [xLow, yLow, xHigh, yHigh]
     */
    fromArray(x) {
        this.xLow = x[0];
        this.yLow = x[1];
        this.xHigh = x[2];
        this.yHigh = x[3];
    }

    /**
     * Resets the bounding box to an empty state by setting coordinates to extreme values.
     */
    toEmpty() {
        this.xLow = 1e20;
        this.yLow = 1e20;
        this.xHigh = -1e20;
        this.yHigh = -1e20;
    }

    /**
     * Checks if the bounding box is empty (has no valid area).
     * A box is considered empty if its low corner coordinates are greater than its high corner coordinates.
     * @returns {boolean} True if the box is empty, false otherwise
     */
    isEmpty() {
        return this.xLow > this.xHigh || this.yLow > this.yHigh;
    }

    /**
     * Converts the bounding box coordinates to an array.
     * @returns {number[]} Array of coordinates in order [xLow, yLow, xHigh, yHigh]
     */
    toArray() {
        return [this.xLow, this.yLow, this.xHigh, this.yHigh];
    }

    /**
      * Creates a space-separated string representation of the bounding box coordinates.
      * @returns {string} String in format "xLow yLow xHigh yHigh"
      */
    toString() {
        return this.xLow.toString() + " " + this.yLow.toString() + " " + this.xHigh.toString() + " " + this.yHigh.toString();
    }

    /**
     * Enlarges this bounding box to include another bounding box.
     * If this box is empty, it will adopt the dimensions of the input box.
     * If the input box is null, no changes are made.
     * @param {BoundingBox|null} box - The bounding box to merge with this one
     */
    mergeBox(box) {
        if (box == null)
            return;

        if (this.isEmpty())
            Object.assign(this, box);
        else {
            this.xLow = Math.min(this.xLow, box.xLow);
            this.yLow = Math.min(this.yLow, box.yLow);
            this.xHigh = Math.max(this.xHigh, box.xHigh);
            this.yHigh = Math.max(this.yHigh, box.yHigh);
        }
    }

    /**
     * Enlarges this bounding box to include a point.
     * @param {{x: number, y: number}} p - The point to include in the bounding box
     */
    mergePoint(p) {
        this.xLow = Math.min(this.xLow, p.x);
        this.yLow = Math.min(this.yLow, p.y);
        this.xHigh = Math.max(this.xHigh, p.x);
        this.yHigh = Math.max(this.yHigh, p.y);
    }

    /**
     * Moves the bounding box by the specified displacement.
     * @param {number} dx - Displacement along the x-axis
     * @param {number} dy - Displacement along the y-axis
     */
    shift(dx, dy) {
        this.xLow += dx;
        this.yLow += dy;
        this.xHigh += dx;
        this.yHigh += dy;
    }

    /**
     * Quantizes the bounding box coordinates by dividing by a specified value and rounding down.
     * This creates a grid-aligned bounding box.
     * @param {number} side - The value to divide coordinates by
     */
    quantize(side) {
        this.xLow = Math.floor(this.xLow / side);
        this.yLow = Math.floor(this.yLow / side);
        this.xHigh = Math.floor((this.xHigh - 1) / side) + 1;
        this.yHigh = Math.floor((this.yHigh - 1) / side) + 1;
    }

    /**
     * Calculates the width of the bounding box.
     * @returns {number} The difference between xHigh and xLow
     */
    width() {
        return this.xHigh - this.xLow;
    }

    /**
     * Calculates the height of the bounding box.
     * @returns {number} The difference between yHigh and yLow
     */
    height() {
        return this.yHigh - this.yLow;
    }

    /**
     * Calculates the center point of the bounding box.
     * @returns {{x: number, y: number}} The coordinates of the center point
     */
    center() {
        return { x: (this.xLow + this.xHigh) / 2, y: (this.yLow + this.yHigh) / 2 };
    }

    /**
     * Gets the coordinates of a specific corner of the bounding box.
     * @param {number} i - Corner index (0: bottom-left, 1: bottom-right, 2: top-left, 3: top-right)
     * @returns {{x: number, y: number}} The coordinates of the specified corner
     */
    corner(i) {
        // To avoid the switch
        let v = this.toArray();
        return { x: v[0 + (i & 0x1) << 1], y: v[1 + (i & 0x2)] };
    }

    /**
     * Checks if this bounding box intersects with another bounding box.
     * @param {BoundingBox} box - The other bounding box to check intersection with
     * @returns {boolean} True if the boxes intersect, false otherwise
     */
    intersects(box) {
        return xLow <= box.xHigh && xHigh >= box.xLow && yLow <= box.yHigh && yHigh >= box.yLow;
    }

    /**
     * Prints the bounding box coordinates to the console in a formatted string.
     * Output format: "BOX=xLow, yLow, xHigh, yHigh" with values rounded to 2 decimal places
     */
    print() {
        console.log("BOX=" + this.xLow.toFixed(2) + ", " + this.yLow.toFixed(2) + ", " + this.xHigh.toFixed(2) + ", " + this.yHigh.toFixed(2))
    }

}

export { BoundingBox }