/** 
 | 
 * @module echarts/core/BoundingRect 
 | 
 */ 
 | 
  
 | 
import * as matrix from './matrix'; 
 | 
import Point, { PointLike } from './Point'; 
 | 
  
 | 
const mathMin = Math.min; 
 | 
const mathMax = Math.max; 
 | 
  
 | 
const lt = new Point(); 
 | 
const rb = new Point(); 
 | 
const lb = new Point(); 
 | 
const rt = new Point(); 
 | 
  
 | 
const minTv = new Point(); 
 | 
const maxTv = new Point(); 
 | 
  
 | 
class BoundingRect { 
 | 
  
 | 
    x: number 
 | 
    y: number 
 | 
    width: number 
 | 
    height: number 
 | 
  
 | 
    constructor(x: number, y: number, width: number, height: number) { 
 | 
        if (width < 0) { 
 | 
            x = x + width; 
 | 
            width = -width; 
 | 
        } 
 | 
        if (height < 0) { 
 | 
            y = y + height; 
 | 
            height = -height; 
 | 
        } 
 | 
  
 | 
        this.x = x; 
 | 
        this.y = y; 
 | 
        this.width = width; 
 | 
        this.height = height; 
 | 
    } 
 | 
  
 | 
    union(other: BoundingRect) { 
 | 
        const x = mathMin(other.x, this.x); 
 | 
        const y = mathMin(other.y, this.y); 
 | 
  
 | 
        // If x is -Infinity and width is Infinity (like in the case of 
 | 
        // IncrementalDisplayble), x + width would be NaN 
 | 
        if (isFinite(this.x) && isFinite(this.width)) { 
 | 
            this.width = mathMax( 
 | 
                other.x + other.width, 
 | 
                this.x + this.width 
 | 
            ) - x; 
 | 
        } 
 | 
        else { 
 | 
            this.width = other.width; 
 | 
        } 
 | 
  
 | 
        if (isFinite(this.y) && isFinite(this.height)) { 
 | 
            this.height = mathMax( 
 | 
                other.y + other.height, 
 | 
                this.y + this.height 
 | 
            ) - y; 
 | 
        } 
 | 
        else { 
 | 
            this.height = other.height; 
 | 
        } 
 | 
  
 | 
        this.x = x; 
 | 
        this.y = y; 
 | 
    } 
 | 
  
 | 
    applyTransform(m: matrix.MatrixArray) { 
 | 
        BoundingRect.applyTransform(this, this, m); 
 | 
    } 
 | 
  
 | 
    calculateTransform(b: RectLike): matrix.MatrixArray { 
 | 
        const a = this; 
 | 
        const sx = b.width / a.width; 
 | 
        const sy = b.height / a.height; 
 | 
  
 | 
        const m = matrix.create(); 
 | 
  
 | 
        // 矩阵右乘 
 | 
        matrix.translate(m, m, [-a.x, -a.y]); 
 | 
        matrix.scale(m, m, [sx, sy]); 
 | 
        matrix.translate(m, m, [b.x, b.y]); 
 | 
  
 | 
        return m; 
 | 
    } 
 | 
  
 | 
    intersect(b: RectLike, mtv?: PointLike): boolean { 
 | 
        if (!b) { 
 | 
            return false; 
 | 
        } 
 | 
  
 | 
        if (!(b instanceof BoundingRect)) { 
 | 
            // Normalize negative width/height. 
 | 
            b = BoundingRect.create(b); 
 | 
        } 
 | 
  
 | 
        const a = this; 
 | 
        const ax0 = a.x; 
 | 
        const ax1 = a.x + a.width; 
 | 
        const ay0 = a.y; 
 | 
        const ay1 = a.y + a.height; 
 | 
  
 | 
        const bx0 = b.x; 
 | 
        const bx1 = b.x + b.width; 
 | 
        const by0 = b.y; 
 | 
        const by1 = b.y + b.height; 
 | 
  
 | 
        let overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0); 
 | 
        if (mtv) { 
 | 
            let dMin = Infinity; 
 | 
            let dMax = 0; 
 | 
            const d0 = Math.abs(ax1 - bx0); 
 | 
            const d1 = Math.abs(bx1 - ax0); 
 | 
            const d2 = Math.abs(ay1 - by0); 
 | 
            const d3 = Math.abs(by1 - ay0); 
 | 
            const dx = Math.min(d0, d1); 
 | 
            const dy = Math.min(d2, d3); 
 | 
            // On x axis 
 | 
            if (ax1 < bx0 || bx1 < ax0) { 
 | 
                if (dx > dMax) { 
 | 
                    dMax = dx; 
 | 
                    if (d0 < d1) { 
 | 
                        Point.set(maxTv, -d0, 0); // b is on the right 
 | 
                    } 
 | 
                    else { 
 | 
                        Point.set(maxTv, d1, 0);  // b is on the left 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
            else { 
 | 
                if (dx < dMin) { 
 | 
                    dMin = dx; 
 | 
                    if (d0 < d1) { 
 | 
                        Point.set(minTv, d0, 0); // b is on the right 
 | 
                    } 
 | 
                    else { 
 | 
                        Point.set(minTv, -d1, 0);  // b is on the left 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
  
 | 
            // On y axis 
 | 
            if (ay1 < by0 || by1 < ay0) { 
 | 
                if (dy > dMax) { 
 | 
                    dMax = dy; 
 | 
                    if (d2 < d3) { 
 | 
                        Point.set(maxTv, 0, -d2); // b is on the bottom(larger y) 
 | 
                    } 
 | 
                    else { 
 | 
                        Point.set(maxTv, 0, d3);  // b is on the top(smaller y) 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
            else { 
 | 
                if (dx < dMin) { 
 | 
                    dMin = dx; 
 | 
                    if (d2 < d3) { 
 | 
                        Point.set(minTv, 0, d2); // b is on the bottom 
 | 
                    } 
 | 
                    else { 
 | 
                        Point.set(minTv, 0, -d3);  // b is on the top 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        if (mtv) { 
 | 
            Point.copy(mtv, overlap ? minTv : maxTv); 
 | 
        } 
 | 
        return overlap; 
 | 
    } 
 | 
  
 | 
    contain(x: number, y: number): boolean { 
 | 
        const rect = this; 
 | 
        return x >= rect.x 
 | 
            && x <= (rect.x + rect.width) 
 | 
            && y >= rect.y 
 | 
            && y <= (rect.y + rect.height); 
 | 
    } 
 | 
  
 | 
    clone() { 
 | 
        return new BoundingRect(this.x, this.y, this.width, this.height); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Copy from another rect 
 | 
     */ 
 | 
    copy(other: RectLike) { 
 | 
        BoundingRect.copy(this, other); 
 | 
    } 
 | 
  
 | 
    plain(): RectLike { 
 | 
        return { 
 | 
            x: this.x, 
 | 
            y: this.y, 
 | 
            width: this.width, 
 | 
            height: this.height 
 | 
        }; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * If not having NaN or Infinity with attributes 
 | 
     */ 
 | 
    isFinite(): boolean { 
 | 
        return isFinite(this.x) 
 | 
            && isFinite(this.y) 
 | 
            && isFinite(this.width) 
 | 
            && isFinite(this.height); 
 | 
    } 
 | 
  
 | 
    isZero(): boolean { 
 | 
        return this.width === 0 || this.height === 0; 
 | 
    } 
 | 
  
 | 
    static create(rect: RectLike): BoundingRect { 
 | 
        return new BoundingRect(rect.x, rect.y, rect.width, rect.height); 
 | 
    } 
 | 
  
 | 
    static copy(target: RectLike, source: RectLike) { 
 | 
        target.x = source.x; 
 | 
        target.y = source.y; 
 | 
        target.width = source.width; 
 | 
        target.height = source.height; 
 | 
    } 
 | 
  
 | 
    static applyTransform(target: RectLike, source: RectLike, m: matrix.MatrixArray) { 
 | 
        // In case usage like this 
 | 
        // el.getBoundingRect().applyTransform(el.transform) 
 | 
        // And element has no transform 
 | 
        if (!m) { 
 | 
            if (target !== source) { 
 | 
                BoundingRect.copy(target, source); 
 | 
            } 
 | 
            return; 
 | 
        } 
 | 
        // Fast path when there is no rotation in matrix. 
 | 
        if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) { 
 | 
            const sx = m[0]; 
 | 
            const sy = m[3]; 
 | 
            const tx = m[4]; 
 | 
            const ty = m[5]; 
 | 
            target.x = source.x * sx + tx; 
 | 
            target.y = source.y * sy + ty; 
 | 
            target.width = source.width * sx; 
 | 
            target.height = source.height * sy; 
 | 
            if (target.width < 0) { 
 | 
                target.x += target.width; 
 | 
                target.width = -target.width; 
 | 
            } 
 | 
            if (target.height < 0) { 
 | 
                target.y += target.height; 
 | 
                target.height = -target.height; 
 | 
            } 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        // source and target can be same instance. 
 | 
        lt.x = lb.x = source.x; 
 | 
        lt.y = rt.y = source.y; 
 | 
        rb.x = rt.x = source.x + source.width; 
 | 
        rb.y = lb.y = source.y + source.height; 
 | 
  
 | 
        lt.transform(m); 
 | 
        rt.transform(m); 
 | 
        rb.transform(m); 
 | 
        lb.transform(m); 
 | 
  
 | 
        target.x = mathMin(lt.x, rb.x, lb.x, rt.x); 
 | 
        target.y = mathMin(lt.y, rb.y, lb.y, rt.y); 
 | 
        const maxX = mathMax(lt.x, rb.x, lb.x, rt.x); 
 | 
        const maxY = mathMax(lt.y, rb.y, lb.y, rt.y); 
 | 
        target.width = maxX - target.x; 
 | 
        target.height = maxY - target.y; 
 | 
    } 
 | 
} 
 | 
  
 | 
  
 | 
export type RectLike = { 
 | 
    x: number 
 | 
    y: number 
 | 
    width: number 
 | 
    height: number 
 | 
} 
 | 
  
 | 
export default BoundingRect; 
 |