namespace Impact { export class People { private totalImpact : number ; private center : Vector ; public alive : boolean ; public resolution : Structure . Resolution ; public collection : Array < Person > ; private static MIN_REFINEMENT = 50 ; private static MAX_DELTA = 0.7 ; private static DAMPING_FACTOR = 0.25 ; private static REFINEMENT_DELTA = 4 ; private static REDRAW_THRESHOLD = 35 ; private static SIZING_DELTA = 0.057 ; private static MAX_FRAME_DIFF = 75 ; constructor ( ) { this . collection = [ ] ; this . resolution = "High" ; this . totalImpact = 0 ; this . alive = true ; } public centerize ( width , height ) { this . center = new Vector ( width / 2.0 , height / 2.0 ) ; } public push ( p ) { this . totalImpact += p . details . impact ; return this . collection . push ( p ) ; } public forEach ( f ) { this . collection . forEach ( f ) ; } public pack ( callback ) { let [ w , h ] = [ this . center . x * 2 , this . center . y * 2 ] ; let unit = Math . min ( w / this . totalImpact , h / this . totalImpact ) * this . delta ( ) ; this . alive = true ; for ( let p of this . collection ) { p . draw ( unit ) ; } this . position ( performance . now ( ) , callback ) ; } public reset ( ) { this . alive = false ; for ( let p of this . collection ) { p . reset ( ) ; } } public clear ( ) { this . alive = false ; this . totalImpact = 0 ; this . collection . length = 0 ; } private position ( ts , callback , iteration = 1 ) { if ( ! this . alive ) { return ; } let redraw ; let iterations = Math . max ( People . MIN_REFINEMENT , this . collection . length * People . REFINEMENT_DELTA ) ; if ( this . resolution == "High" ) { redraw = iteration % Math . ceil ( this . collection . length / People . REDRAW_THRESHOLD ) == 0 ; } else { redraw = iteration == iterations ; } this . collection . sort ( ( a , b ) => { let c = this . center ; return a . point . distanceFrom ( c ) - b . point . distanceFrom ( c ) ; } ) ; for ( let i = 0 ; i < this . collection . length ; i ++ ) { for ( let n = i + 1 ; n < this . collection . length ; n ++ ) { this . collection [ i ] . detract ( this . collection [ n ] ) ; } } for ( let p of this . collection ) { p . attract ( this . center , People . DAMPING_FACTOR / iteration ) ; if ( redraw ) { p . position ( ) ; } } if ( iteration < iterations ) { Helpers . onHighRes ( this . resolution , ( next_ts ) => { if ( iteration > 2 && ( next_ts - ts ) > People . MAX_FRAME_DIFF ) { this . resolution = "Low" ; } this . position ( next_ts , callback , iteration + 1 ) ; } ) ; } else { let pToMove = this . collection . filter ( ( p ) => p . offScreen ( ) ) ; let offset = 0 ; for ( let p of pToMove ) { p . recenter ( offset ) ; p . moveTo ( this . findClosestTo ( p ) ) ; p . position ( ) ; offset += ( p . radius * 2 ) + p . padding ; } callback ( ) ; } } public closestToCenter ( ) { return this . collection [ 0 ] ; } private findClosestTo ( person ) { let closest = this . collection [ 0 ] , point = person . point ; for ( let p of this . collection ) { if ( person . collidingOn ( p , Axis . Y ) && person . details . id != p . details . id && point . distanceFrom ( p . point ) < point . distanceFrom ( closest . point ) ) { closest = p ; } } return closest ; } private delta ( ) { let l = this . collection . length ; return ( l == 1 ) ? People . MAX_DELTA : 1 + ( ( l - 1 ) * People . SIZING_DELTA ) ; } } }