const duplicate = < T > ( obj ) : T => JSON . parse ( JSON . stringify ( obj ) ) ; const getRandomInt = ( min , max ) : number => { const MIN = Math . ceil ( min ) ; const MAX = Math . floor ( max ) ; return Math . floor ( Math . random ( ) * ( MAX - MIN ) ) + MIN ; } ; const getRandomItem = < T > ( array ) : T => array [ getRandomInt ( 0 , array . length - 1 ) ] ; const rotatePointBasedOnDir = ( p , dir ) : IPoint => { const point = { x : 0 , y : 0 } ; const nP = Array . isArray ( p ) ? { x : p [ 0 ] , y : p [ 1 ] } : { ... p } ; switch ( dir ) { case 0 : point . x = nP . x ; point . y = nP . y ; break ; case 2 : point . x = nP . y * - 1 ; point . y = nP . x ; break ; case 4 : point . x = nP . x * - 1 ; point . y = nP . y * - 1 ; break ; case 6 : point . x = nP . y ; point . y = nP . x * - 1 ; } return point ; } ; /** returns the direction of the point in relation to the origin at (0, 0) */ const getRelativeDirection = ( position ) : 0 | 2 | 4 | 6 => { if ( Math . abs ( position . x ) > Math . abs ( position . y ) ) { return Math . sign ( position . x ) === 1 ? 2 : 6 ; } else { return Math . sign ( position . y ) === 1 ? 4 : 0 ; } } ; const transformConnectionPosition = ( position , direction ) : IPoint => { const dir = getRelativeDirection ( position ) ; switch ( dir ) { case 0 : position . y += 1 ; break ; case 2 : position . x -= 1 ; break ; case 4 : position . y -= 1 ; break ; case 6 : position . x += 1 ; } return rotatePointBasedOnDir ( position , direction ) ; } ; const switchSizeBasedOnDirection = ( size , direction ) : IPoint => { if ( size . width !== size . height && ( direction === 2 || direction === 3 || direction === 6 || direction === 7 ) ) { return { x : size . height , y : size . width } ; } return { x : size . width , y : size . height } ; } ; const intToDir = ( i ) : 'north' | 'east' | 'south' | 'west' => { switch ( i ) { case 0 : return 'north' ; case 2 : return 'east' ; case 4 : return 'south' ; case 6 : return 'west' ; } } ; const nearestPowerOf2 = ( n ) : number => Math . pow ( 2 , Math . ceil ( Math . log2 ( n ) ) ) ; const uniqueInArray = < T > ( array ) : T [ ] => [ ... new Set ( array ) ] ; const equalArrays = < T > ( array1 , array2 ) : boolean => array1 && array2 && array1 . length === array2 . length && array1 . sort ( ) . every ( ( value , index ) => value === array2 . sort ( ) [ index ] ) ; const areObjectsEquivalent = < T extends Record < string , any > > ( a , b ) : boolean => { const aProps = Object . getOwnPropertyNames ( a ) ; const bProps = Object . getOwnPropertyNames ( b ) ; if ( aProps . length !== bProps . length ) return false ; for ( const propName of aProps ) { if ( a [ propName ] !== b [ propName ] ) return false ; } return true ; } ; const areArraysEquivalent = < T > ( a , b ) : boolean => a !== undefined && b !== undefined && a . length === b . length && a . every ( ( v , i ) => areObjectsEquivalent ( v , b [ i ] ) ) ; const timer = ( name ) : { stop : ( ) => void ; } => { const start = new Date ( ) ; return { stop : ( ) => { const end = new Date ( ) ; const time = end . getTime ( ) - start . getTime ( ) ; console . log ( 'Timer:' , name , 'finished in' , time , 'ms' ) ; } , } ; } ; const objectHasOwnProperty = ( obj , key ) : boolean => Object . prototype . hasOwnProperty . call ( obj , key ) ; export default { duplicate , getRandomInt , getRandomItem , getRelativeDirection , rotatePointBasedOnDir , transformConnectionPosition , switchSizeBasedOnDirection , intToDir , nearestPowerOf2 , uniqueInArray , equalArrays , areObjectsEquivalent , areArraysEquivalent , timer , objectHasOwnProperty , } ;