/** * 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 }