import { EPSILON, getBoundsFromPoints, getBoundsWithRotation, lineIntersects, } from './math-utils.js';
import { deserializeXYWH, serializeXYWH, } from './xywh.js';
export class Bound {
    constructor(x = 0, y = 0, w = 0, h = 0) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }
    static fromXYWH(xywh) {
        return new Bound(xywh[0], xywh[1], xywh[2], xywh[3]);
    }
    static from(arg1) {
        return new Bound(arg1.x, arg1.y, arg1.w, arg1.h);
    }
    static fromDOMRect({ left, top, width, height }) {
        return new Bound(left, top, width, height);
    }
    static fromPoints(points) {
        const { minX, minY, maxX, maxY } = getBoundsFromPoints(points);
        return new Bound(minX, minY, maxX - minX, maxY - minY);
    }
    static fromCenter(center, width, height) {
        const [x, y] = center;
        return new Bound(x - width / 2, y - height / 2, width, height);
    }
    get points() {
        return [
            [this.x, this.y],
            [this.x + this.w, this.y],
            [this.x + this.w, this.y + this.h],
            [this.x, this.y + this.h],
        ];
    }
    get midPoints() {
        return [
            [this.x + this.w / 2, this.y],
            [this.x + this.w, this.y + this.h / 2],
            [this.x + this.w / 2, this.y + this.h],
            [this.x, this.y + this.h / 2],
        ];
    }
    get center() {
        return [this.x + this.w / 2, this.y + this.h / 2];
    }
    get minX() {
        return this.x;
    }
    get minY() {
        return this.y;
    }
    get maxX() {
        return this.x + this.w;
    }
    get maxY() {
        return this.y + this.h;
    }
    get tl() {
        return [this.x, this.y];
    }
    get tr() {
        return [this.x + this.w, this.y];
    }
    get bl() {
        return [this.x, this.y + this.h];
    }
    get br() {
        return [this.x + this.w, this.y + this.h];
    }
    get verticalLine() {
        return [
            [this.x + this.w / 2, this.y],
            [this.x + this.w / 2, this.y + this.h],
        ];
    }
    get horizontalLine() {
        return [
            [this.x, this.y + this.h / 2],
            [this.x + this.w, this.y + this.h / 2],
        ];
    }
    get upperLine() {
        return [
            [this.x, this.y],
            [this.x + this.w, this.y],
        ];
    }
    get lowerLine() {
        return [
            [this.x, this.y + this.h],
            [this.x + this.w, this.y + this.h],
        ];
    }
    get leftLine() {
        return [
            [this.x, this.y],
            [this.x, this.y + this.h],
        ];
    }
    get rightLine() {
        return [
            [this.x + this.w, this.y],
            [this.x + this.w, this.y + this.h],
        ];
    }
    containsPoint([x, y]) {
        const { minX, minY, maxX, maxY } = this;
        return minX <= x && x <= maxX && minY <= y && y <= maxY;
    }
    intersectLine(sp, ep, infinite = false) {
        const rst = [];
        [
            [this.tl, this.tr],
            [this.tl, this.bl],
            [this.tr, this.br],
            [this.bl, this.br],
        ].forEach(([p1, p2]) => {
            const p = lineIntersects(sp, ep, p1, p2, infinite);
            if (p)
                rst.push(p);
        });
        return rst.length === 0 ? null : rst;
    }
    isIntersectWithBound(bound, epsilon = EPSILON) {
        return (bound.maxX > this.minX - epsilon &&
            bound.maxY > this.minY - epsilon &&
            bound.minX < this.maxX + epsilon &&
            bound.minY < this.maxY + epsilon &&
            !this.contains(bound) &&
            !bound.contains(this));
    }
    isOverlapWithBound(bound, epsilon = EPSILON) {
        return (bound.maxX > this.minX - epsilon &&
            bound.maxY > this.minY - epsilon &&
            bound.minX < this.maxX + epsilon &&
            bound.minY < this.maxY + epsilon);
    }
    unite(bound) {
        const x1 = Math.min(this.x, bound.x), y1 = Math.min(this.y, bound.y), x2 = Math.max(this.maxX, bound.maxX), y2 = Math.max(this.maxY, bound.maxY);
        return new Bound(x1, y1, x2 - x1, y2 - y1);
    }
    include(point) {
        const x1 = Math.min(this.x, point[0]), y1 = Math.min(this.y, point[1]), x2 = Math.max(this.maxX, point[0]), y2 = Math.max(this.maxY, point[1]);
        return new Bound(x1, y1, x2 - x1, y2 - y1);
    }
    getRelativePoint([x, y]) {
        return [this.x + x * this.w, this.y + y * this.h];
    }
    toRelative([x, y]) {
        return [(x - this.x) / this.w, (y - this.y) / this.h];
    }
    serialize() {
        return serializeXYWH(this.x, this.y, this.w, this.h);
    }
    clone() {
        return new Bound(this.x, this.y, this.w, this.h);
    }
    isHorizontalCross(bound) {
        return !(this.maxY < bound.minY || this.minY > bound.maxY);
    }
    isVerticalCross(bound) {
        return !(this.maxX < bound.minX || this.minX > bound.maxX);
    }
    horizontalDistance(bound) {
        return Math.min(Math.abs(this.minX - bound.maxX), Math.abs(this.maxX - bound.minX));
    }
    verticalDistance(bound) {
        return Math.min(Math.abs(this.minY - bound.maxY), Math.abs(this.maxY - bound.minY));
    }
    expand(left, top = left, right = left, bottom = top) {
        return new Bound(this.x - left, this.y - top, this.w + left + right, this.h + top + bottom);
    }
    isPointInBound([x, y], tolerance = 0.01) {
        return (x > this.minX + tolerance &&
            x < this.maxX - tolerance &&
            y > this.minY + tolerance &&
            y < this.maxY - tolerance);
    }
    isPointNearBound([x, y], tolerance = 0.01) {
        return (Math.abs(x - this.minX) < tolerance ||
            Math.abs(x - this.maxX) < tolerance ||
            Math.abs(y - this.minY) < tolerance ||
            Math.abs(y - this.maxY) < tolerance);
    }
    contains(bound) {
        return (bound.x >= this.x &&
            bound.y >= this.y &&
            bound.maxX <= this.maxX &&
            bound.maxY <= this.maxY);
    }
    getVerticesAndMidpoints() {
        return [...this.points, ...this.midPoints];
    }
    static deserialize(s) {
        const [x, y, w, h] = deserializeXYWH(s);
        return new Bound(x, y, w, h);
    }
}
function getExpandedBound(a, b) {
    const minX = Math.min(a.x, b.x);
    const minY = Math.min(a.y, b.y);
    const maxX = Math.max(a.x + a.w, b.x + b.w);
    const maxY = Math.max(a.y + a.h, b.y + b.h);
    const width = Math.abs(maxX - minX);
    const height = Math.abs(maxY - minY);
    return {
        x: minX,
        y: minY,
        w: width,
        h: height,
    };
}
export function getCommonBound(bounds) {
    if (!bounds.length) {
        return null;
    }
    if (bounds.length === 1) {
        const { x, y, w, h } = bounds[0];
        return new Bound(x, y, w, h);
    }
    let result = bounds[0];
    for (let i = 1; i < bounds.length; i++) {
        result = getExpandedBound(result, bounds[i]);
    }
    return new Bound(result.x, result.y, result.w, result.h);
}
export function getElementsBound(bounds) {
    let minX = Number.POSITIVE_INFINITY;
    let minY = Number.POSITIVE_INFINITY;
    let maxX = Number.NEGATIVE_INFINITY;
    let maxY = Number.NEGATIVE_INFINITY;
    bounds.forEach(ele => {
        const b = getBoundsWithRotation(ele);
        minX = Math.min(minX, b.x);
        minY = Math.min(minY, b.y);
        maxX = Math.max(maxX, b.x + b.w);
        maxY = Math.max(maxY, b.y + b.h);
    });
    return new Bound(minX, minY, maxX - minX, maxY - minY);
}
export function getBoundFromPoints(points) {
    const { minX, minY, width, height } = getBoundsFromPoints(points);
    return new Bound(minX, minY, width, height);
}
export function inflateBound(bound, delta) {
    const half = delta / 2;
    const newBound = new Bound(bound.x - half, bound.y - half, bound.w + delta, bound.h + delta);
    if (newBound.w <= 0 || newBound.h <= 0) {
        throw new Error('Invalid delta range or bound size.');
    }
    return newBound;
}
export function transformPointsToNewBound(points, oldBound, oldMargin, newBound, newMargin) {
    const wholeOldMargin = oldMargin * 2;
    const wholeNewMargin = newMargin * 2;
    const oldW = Math.max(oldBound.w - wholeOldMargin, 1);
    const oldH = Math.max(oldBound.h - wholeOldMargin, 1);
    const newW = Math.max(newBound.w - wholeNewMargin, 1);
    const newH = Math.max(newBound.h - wholeNewMargin, 1);
    const transformedPoints = points.map(p => {
        return {
            ...p,
            x: newW * ((p.x - oldMargin) / oldW) + newMargin,
            y: newH * ((p.y - oldMargin) / oldH) + newMargin,
        };
    });
    return {
        points: transformedPoints,
        bound: new Bound(newBound.x, newBound.y, newW + wholeNewMargin, newH + wholeNewMargin),
    };
}
//# sourceMappingURL=bound.js.map