import { isBetween , nearlyEquals } from '@mathigon/fermat' ; import { Line } from './line' ; import { ORIGIN , Point } from './point' ; import { Polygon } from './polygon' ; import { GeoShape , SimplePoint , TransformMatrix } from './utilities' ; /** A rectangle, defined by its top left vertex, width and height. */ export class Rectangle implements GeoShape { readonly type = 'rectangle' ; constructor ( readonly p , readonly w = 1 , readonly h = w ) { } /** Creates the smallest rectangle containing all given points. */ static aroundPoints ( points ) { let xMin = Infinity ; let xMax = - Infinity ; let yMin = Infinity ; let yMax = - Infinity ; for ( const p of points ) { xMin = xMin < p . x ? xMin : p . x ; xMax = xMax > p . x ? xMax : p . x ; yMin = yMin < p . y ? yMin : p . y ; yMax = yMax > p . y ? yMax : p . y ; } return new Rectangle ( new Point ( xMin , yMin ) , xMax - xMin , yMax - yMin ) ; } get center ( ) { return new Point ( this . p . x + this . w / 2 , this . p . y + this . h / 2 ) ; } get centroid ( ) { return this . center ; } get circumference ( ) { return 2 * Math . abs ( this . w ) + 2 * Math . abs ( this . h ) ; } get area ( ) { return Math . abs ( this . w * this . h ) ; } returns { Segment [ ] } get edges ( ) { return this . polygon . edges ; } returns { Point [ ] } get points ( ) { return this . polygon . points ; } /** A polygon class representing this rectangle. */ get polygon ( ) { const b = new Point ( this . p . x + this . w , this . p . y ) ; const c = new Point ( this . p . x + this . w , this . p . y + this . h ) ; const d = new Point ( this . p . x , this . p . y + this . h ) ; return new Polygon ( this . p , b , c , d ) ; } collision ( r ) { return ( this . p . x < r . p . x + r . w && this . p . x + this . w > r . p . x && this . p . y < r . p . y + r . h && this . p . y + this . h > r . p . y ) ; } padding ( top , right , bottom , left ) { return new Rectangle ( this . p . shift ( - left , - top ) , this . w + left + right , this . h + top + bottom ) ; } contains ( p , tolerance ? ) { return isBetween ( p . x , this . p . x , this . p . x + this . w , tolerance ) && isBetween ( p . y , this . p . y , this . p . y + this . h , tolerance ) ; } project ( p ) { let q = undefined ; for ( const e of this . edges ) { const q1 = e . project ( p ) ; if ( ! q || Point . distance ( p , q1 ) < Point . distance ( p , q ) ) q = q1 ; } return q ! ; } at ( _t ) { return this . p ; } transform ( m ) { return this . polygon . transform ( m ) ; } /** Rotates this rectangle by a given angle (in radians), optionally around point `c`. */ rotate ( a , c = ORIGIN ) { if ( nearlyEquals ( a , 0 ) ) return this ; return this . polygon . rotate ( a , c ) ; } reflect ( l ) { return this . polygon . reflect ( l ) ; } scale ( sx , sy = sx ) { return new Rectangle ( this . p . scale ( sx , sy ) , this . w * sx , this . h * sy ) ; } shift ( x , y = x ) { return new Rectangle ( this . p . shift ( x , y ) , this . w , this . h ) ; } translate ( p ) { return this . shift ( p . x , p . y ) ; } equals ( _other ) { return false ; } toString ( ) { return `Rectangle(p: ${ this . p }, w: ${ this . w }, h: ${ this . h })` ; } }