export class Bounds {
    constructor(x, y, width, height) {
        Object.defineProperty(this, "x", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "y", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "width", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "height", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
    /** Calculate the bottom point as y + height */
    get bottom() {
        return this.y + this.height;
    }
    /** Calculate right point as x + width */
    get right() {
        return this.x + this.width;
    }
    /**
     * Does this bounds intersect another bounds
     *
     * @param other Bounds to check intersection with
     */
    intersects(other) {
        return this.x < other.right && other.x < this.right && this.y < other.bottom && other.y < this.bottom;
    }
    /**
       * Is `other` bounds completely within this bounds
  
       * @param other Bounds to check is within this
       */
    containsBounds(other) {
        return Bounds.contains(this, other);
    }
    /**
       * Create a new bounds that is the intersection of the two bounds (if any))
       *
       * @param other Bounds to calculate intersection with
  
       * @returns new Bounds that is the intersection of the two bounds or null is not intersection
       * found
       */
    intersection(other) {
        const x = Math.max(this.x, other.x);
        const num1 = Math.min(this.x + this.width, other.x + other.width);
        const y = Math.max(this.y, other.y);
        const num2 = Math.min(this.y + this.height, other.y + other.height);
        if (num1 > x && num2 > y) {
            return new Bounds(x, y, num1 - x, num2 - y);
        }
        return null;
    }
    /**
     * Get the union of a `list` of Bounds
     */
    static union(list) {
        if (list.length === 0)
            throw new Error('Union on empty list is not allowed');
        let { x, y } = list[0];
        let maxX = x + list[0].width;
        let maxY = y + list[0].height;
        for (let i = 1; i < list.length; ++i) {
            const other = list[i];
            x = Math.min(x, other.x);
            maxX = Math.max(maxX, other.x + other.width);
            y = Math.min(y, other.y);
            maxY = Math.max(maxY, other.y + other.height);
        }
        return new Bounds(x, y, maxX - x, maxY - y);
    }
    /**
     * Create a new bounds that is the union of the two bounds
     *
     * @param other Bounds to calculate union with
     */
    union(other) {
        return Bounds.union([this, other]);
    }
    /**
     * Convert to JSON
     */
    toJson() {
        return { x: this.x, y: this.y, width: this.width, height: this.height };
    }
    /**
     * Convert to BBox
     */
    toBbox() {
        return [this.x, this.y, this.right, this.bottom];
    }
    /**
     * Convert to GeoJson Polygon coordinates
     */
    toPolygon() {
        return [
            [
                [this.x, this.y],
                [this.x, this.bottom],
                [this.right, this.bottom],
                [this.right, this.y],
                [this.x, this.y],
            ],
        ];
    }
    /**
     * Scale the bounding box adjusting top, left, width & height
     *
     * @param scaleX scale x parameters (left, width)
     * @param scaleY scale of y parameters (top, height)
     * @returns new bounds that has been scaled
     */
    scale(scaleX, scaleY = scaleX) {
        return new Bounds(this.x * scaleX, this.y * scaleY, this.width * scaleX, this.height * scaleY);
    }
    /**
     * Scale by a factor about center
     * @param scaleX scale of x parameters (left, width)
     * @param scaleY scale of y parameters (top, height)
     */
    scaleFromCenter(scaleX, scaleY = scaleX) {
        const newWidth = this.width * scaleX;
        const newHeight = this.height * scaleY;
        return new Bounds(this.x - 0.5 * (newWidth - this.width), this.y - 0.5 * (newHeight - this.height), newWidth, newHeight);
    }
    /**
       * Increase bounds by padding the edges
  
       * @param padX amount to pad the x parameters (left, width)
       * @param padY amount to pad the y parameters (top, height)
       */
    pad(padX, padY = padX) {
        return new Bounds(this.x - padX, this.y - padY, this.width + padX * 2, this.height + padY * 2);
    }
    /**
       * Round dimensions to integers keeping the error a low as possible
  
       * @param bias influence the rounding in favour of a larger area (+ve bias) or smaller area (-ve
       * bias). Example: bias = 0.2 would round left and top down when < 0.7 and round right and bottom up when >= 0.3
       */
    round(bias = 0) {
        const x = Math.round(this.x - bias);
        const y = Math.round(this.y - bias);
        return new Bounds(x, y, Math.round(this.right + bias) - x, Math.round(this.bottom + bias) - y);
    }
    add(bounds) {
        return new Bounds(this.x + bounds.x, this.y + bounds.y, this.width, this.height);
    }
    subtract(bounds) {
        return new Bounds(this.x - bounds.x, this.y - bounds.y, this.width, this.height);
    }
    /**
       * Find the bounds of a MultiPolygon.
       * Does not work with WGS84 when crosses antimeridian.
  
       * @param multipoly the polygon to measure
       */
    static fromMultiPolygon(multipoly) {
        if (multipoly.length === 0)
            return new Bounds(0, 0, 0, 0);
        let minX = multipoly[0][0][0][0];
        let minY = multipoly[0][0][0][1];
        let maxX = minX;
        let maxY = minY;
        for (const [poly] of multipoly) {
            for (const [x, y] of poly) {
                if (x < minX)
                    minX = x;
                else if (x > maxX)
                    maxX = x;
                if (y < minY)
                    minY = y;
                else if (y > maxY)
                    maxY = y;
            }
        }
        return new Bounds(minX, minY, maxX - minX, maxY - minY);
    }
    /**
     * Convert a BBox(minX, minY, maxX, maxY) to Bounds(x,y, width, height).
     * Does not work with WGS84 when crosses the antimeridian.
     * @param bbox
     */
    static fromBbox([x1, y1, x2, y2]) {
        return new Bounds(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x2 - x1), Math.abs(y2 - y1));
    }
    /** Convert a pair of upper left `ul` and lower right `lr` Points to Bounds */
    static fromUpperLeftLowerRight(ul, lr) {
        return new Bounds(ul.x, ul.y, lr.x - ul.x, lr.y - ul.y);
    }
    static fromJson(bounds) {
        return new Bounds(bounds.x, bounds.y, bounds.width, bounds.height);
    }
    /**
     * Use for sorting Bounding boxes by area, x, y
     */
    static compareArea(a, b) {
        const areaDiff = a.width * a.height - b.width * b.height;
        if (areaDiff !== 0)
            return areaDiff;
        const xDiff = a.x - b.x;
        return xDiff === 0 ? a.y - b.y : xDiff;
    }
    /**
     * Does BoundingBox `a` contain `b`. Is `b` within `a`
     */
    static contains(a, b) {
        return a.x <= b.x && a.x + a.width >= b.x + b.width && a.y <= b.y && a.y + a.height >= b.y + b.height;
    }
}
//# sourceMappingURL=bounds.js.map