export interface Point { x : number ; y : number ; } export interface Size { width : number ; height : number ; } export interface BoundingBox extends Point , Size { } export interface NamedBounds extends BoundingBox { name : string ; } export class Bounds implements BoundingBox { public readonly x : number ; public readonly y : number ; public readonly width : number ; public readonly height : number ; public constructor ( x , y , width , height ) { this . x = x ; this . y = y ; this . width = width ; this . height = height ; } /** Calculate the bottom point as y + height */ public get bottom ( ) : number { return this . y + this . height ; } /** Calculate right point as x + width */ public get right ( ) : number { return this . x + this . width ; } param other public intersects ( other ) { return this . x < other . right && other . x < this . right && this . y < other . bottom && other . y < this . bottom ; } param other public containsBounds ( other ) { return Bounds . contains ( this , other ) ; } param other returns public 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
     */ public 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 ) ; } param other public union ( other ) { return Bounds . union ( [ this , other ] ) ; } /**
     * Convert to JSON
     */ public toJson ( ) { return { x : this . x , y : this . y , width : this . width , height : this . height } ; } /**
     * Convert to BBox
     */ public toBbox ( ) { return [ this . x , this . y , this . right , this . bottom ] ; } /**
     * Convert to GeoJson Polygon coordinates
     */ public toPolygon ( ) { return [ [ [ this . x , this . y ] , [ this . x , this . bottom ] , [ this . right , this . bottom ] , [ this . right , this . y ] , [ this . x , this . y ] , ] , ] ; } param scaleX param scaleY returns public scale ( scaleX , scaleY = scaleX ) { return new Bounds ( this . x * scaleX , this . y * scaleY , this . width * scaleX , this . height * scaleY ) ; } param scaleX param scaleY public 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 ) ; } param padX param padY public pad ( padX , padY = padX ) { return new Bounds ( this . x - padX , this . y - padY , this . width + padX * 2 , this . height + padY * 2 ) ; } param bias public 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 ) ; } public add ( bounds ) { return new Bounds ( this . x + bounds . x , this . y + bounds . y , this . width , this . height ) ; } public subtract ( bounds ) { return new Bounds ( this . x - bounds . x , this . y - bounds . y , this . width , this . height ) ; } param multipoly public 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 ) ; } param bbox public 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 */ public static fromUpperLeftLowerRight ( ul , lr ) { return new Bounds ( ul . x , ul . y , lr . x - ul . x , lr . y - ul . y ) ; } public static fromJson ( bounds ) { return new Bounds ( bounds . x , bounds . y , bounds . width , bounds . height ) ; } /**
     * Use for sorting Bounding boxes by area, x, y
     */ public 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`
     */ public 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 ; } }