2020-08-20 15:58:18 -06:00
// bluemath.js
// This library adds advanced math objects
2022-02-07 18:11:49 -07:00
function deg2rad ( deg ) {
return ( ( Math . PI * 2 ) / 360 ) * deg ;
}
function rad2deg ( rad ) {
return ( 1 / deg2rad ( 1 ) ) * rad ;
}
2020-08-20 15:58:18 -06:00
class vector {
2022-02-07 18:11:49 -07:00
constructor ( dimentions = [ 0 ] ) {
var me = this ;
2022-02-08 17:10:24 -07:00
this . d = Array . isArray ( dimentions ) ? dimentions . map ( d => ! isNaN ( d ) ? d : 0 ) : [ 0 ] ;
2022-02-07 18:11:49 -07:00
if ( me . d . length == 2 ) {
2022-02-08 17:10:24 -07:00
this . getRadAngle = function ( from ) {
from = from instanceof vector ? from : new vector ( [ 0 , 0 ] ) ;
return Math . atan2 ( me . d [ 1 ] - from . d [ 1 ] , me . d [ 0 ] - from . d [ 0 ] ) ;
2022-02-07 18:11:49 -07:00
}
2022-02-08 17:10:24 -07:00
this . getDegAngle = function ( from ) {
return rad2deg ( me . getRadAngle ( from ) ) ;
2022-02-07 18:11:49 -07:00
}
this . resize = function ( newLength ) {
var angle = me . getDegAngle ( ) ;
var soh = Math . sin ( angle ) * newLength ;
var cah = Math . cos ( angle ) * newLength ;
2022-02-08 17:10:24 -07:00
return new vector ( [ cah , soh ] ) ;
}
this . rotate = function ( rad ) {
var theta = me . getRadAngle ( ) + rad ;
var amp = me . getDistance ( ) ;
return new vector ( [ Math . cos ( theta ) * amp , Math . sin ( theta ) * amp ] ) ;
}
this . reflect = function ( surfaceVector ) {
var theta = ( 2 * ( surfaceVector . getDegAngle ( ) + 90 ) ) - me . getDegAngle ( ) ;
var amp = me . getDistance ( ) ;
return new vector ( [ Math . cos ( theta ) * amp , Math . sin ( theta ) * amp ] ) ;
}
this . getPerp = function ( ) {
return me . rotate ( deg2rad ( 90 ) ) ;
2022-02-07 18:11:49 -07:00
}
2022-02-08 17:10:24 -07:00
this . slope = function ( ) {
return me . d [ 1 ] / me . d [ 0 ] ;
}
this . yIntercept = function ( ) {
if ( Math . abs ( me . slope ( ) ) != Infinity ) {
return - ( me . slope ( ) * me . d [ 0 ] ) + me . d [ 1 ] ;
} else {
return me . d [ 0 ] ;
}
}
this . xIntercept = function ( ) {
return - me . yIntercept ( ) / me . slope ( ) ;
}
2022-02-07 18:11:49 -07:00
}
}
2022-02-07 18:15:06 -07:00
add ( vec = new vector ( [ ... this . d . map ( d => 0 ) ] ) ) {
2022-02-07 18:11:49 -07:00
vec = vec instanceof vector ? vec : new vector ( [ 0 ] ) ;
2022-02-08 17:10:24 -07:00
var longest = vec . d . length >= this . d . length ? vec . d : this . d ;
2022-02-07 18:11:49 -07:00
var shortest = vec . d . length < this . d . length ? vec . d : this . d ;
return new vector ( [ ... longest . map ( function ( value , index ) {
var small = shortest [ index ] ;
2022-02-07 18:15:06 -07:00
if ( ! isNaN ( small ) ) { return value + small ; } else { return value ; }
2022-02-07 18:11:49 -07:00
} ) ] ) ;
}
mult ( vec = new vector ( [ ... this . d . map ( d => 1 ) ] ) ) {
2022-02-08 17:10:24 -07:00
vec = vec instanceof vector ? vec : new vector ( [ ... this . d . map ( d => 1 ) ] ) ;
var longest = vec . d . length >= this . d . length ? vec . d : this . d ;
2022-02-07 18:11:49 -07:00
var shortest = vec . d . length < this . d . length ? vec . d : this . d ;
return new vector ( [ ... longest . map ( function ( value , index ) {
var small = shortest [ index ] ;
2022-02-07 18:15:06 -07:00
if ( ! isNaN ( small ) ) { return value * small ; } else { value ; }
2022-02-07 18:11:49 -07:00
} ) ] ) ;
}
mag ( mult = 1 ) {
mult = ! isNaN ( mult ) ? mult : 1 ;
return this . mult ( new vector ( [ ... this . d . map ( d => mult ) ] ) ) ;
}
square ( ) {
return this . mult ( this ) ;
2020-08-20 15:58:18 -06:00
}
2022-02-07 18:11:49 -07:00
getDistance ( origin = new vector ( [ ... this . d . map ( d => 0 ) ] ) ) {
origin = origin instanceof vector ? origin : new vector ( [ ... this . d . map ( d => 0 ) ] ) ;
return Math . sqrt ( this . d . map ( function ( value , index ) {
var ori = ! isNaN ( origin . d [ index ] ) ? origin . d [ index ] : 0 ;
2022-02-08 17:10:24 -07:00
return Math . pow ( Math . abs ( ori - value ) , 2 ) ;
2022-02-07 18:11:49 -07:00
} ) . reduce ( ( a , b ) => a + b ) ) ;
}
}
2022-02-08 17:10:24 -07:00
class lineSeg {
constructor ( pointA , pointB ) {
this . pointA = pointA ;
this . pointB = pointB ;
}
intersect ( line ) {
if ( line instanceof lineSeg ) {
var x1 = this . pointA . d [ 0 ] ;
var x2 = this . pointB . d [ 0 ] ;
var x3 = line . pointA . d [ 0 ] ;
var x4 = line . pointB . d [ 0 ] ;
var y1 = this . pointA . d [ 1 ] ;
var y2 = this . pointB . d [ 1 ] ;
var y3 = line . pointA . d [ 1 ] ;
var y4 = line . pointB . d [ 1 ] ;
var deltaAX = x1 - x2 ;
var deltaBX = x3 - x4 ;
var deltaAY = y1 - y2 ;
var deltaBY = y3 - y4 ;
// I don't know wtf to call these
var ground = deltaAX * deltaBY - deltaAY * deltaBX ;
var Across = x1 * y2 - y1 * x2 ;
var Bcross = x3 * y4 - y3 * x4 ;
var x = ( Across * deltaBX - deltaAX * Bcross ) / ground ;
var y = ( Across * deltaBY - deltaAY * Bcross ) / ground ;
if (
( new bound ( [ x1 ] , [ x2 ] ) ) . inbounds ( [ x ] ) &&
( new bound ( [ x3 ] , [ x4 ] ) ) . inbounds ( [ x ] ) &&
( new bound ( [ y1 ] , [ y2 ] ) ) . inbounds ( [ y ] ) &&
( new bound ( [ y3 ] , [ y4 ] ) ) . inbounds ( [ y ] ) ) {
return new vector ( [ x , y ] ) ;
} else {
return false ;
}
} else {
throw new TypeError ( "must check against line" ) ;
}
}
}
2022-02-07 18:11:49 -07:00
function rad2vec ( rad ) {
var x = Math . cos ( rad ) ;
var y = Math . sin ( rad ) ;
return new vector ( [ x , y ] ) ;
}
function deg2vec ( deg ) { //abstraction, really just for sanity
return rad2vec ( deg2rad ( deg ) ) ;
}
class bound {
constructor ( min = [ - Infinity ] , max = [ Infinity ] ) {
2022-11-10 11:52:39 -07:00
// Failsafe if bare numbers are provided instead of an array
2022-02-07 18:11:49 -07:00
// Insure both inputs are arrays with valid numbers
2022-11-10 11:52:39 -07:00
min = Array . isArray ( min ) ? min . map ( value => ! isNaN ( value ) ? value : - Infinity ) : ( ! isNaN ( min ) ? [ min ] : [ - Infinity ] ) ;
max = Array . isArray ( max ) ? max . map ( value => ! isNaN ( value ) ? value : - Infinity ) : ( ! isNaN ( max ) ? [ max ] : [ Infinity ] ) ;
2022-02-07 18:11:49 -07:00
// Insure both arrays are equal in size
while ( min . length != max . length ) {
if ( min . length > max . length ) { max . push ( Infinity ) ;
} else { min . push ( - Infinity ) ; }
}
// Accept filtered bounds
this . min = min ;
this . max = max ;
}
inbounds ( valueArray = [ ... this . min . map ( v => 0 ) ] ) {
if ( valueArray . length != this . min . length ) { return false }
return valueArray . map ( ( value , index ) => ( value >= this . min [ index ] ) && ( value <= this . max [ index ] ) ) . reduce ( ( a , b ) => a && b ) ;
}
normalize ( valueArray = [ ... this . min . map ( v => 0 ) ] ) {
if ( valueArray . length != this . min . length ) { return [ ... this . min . map ( v => 0 ) ] ; }
return valueArray . map ( ( value , index ) => ( value - this . min [ index ] ) / ( this . max [ index ] - this . min [ index ] ) ) ;
2022-02-08 12:14:09 -07:00
}
2022-02-07 18:11:49 -07:00
clamp ( valueArray = [ ... this . min . map ( v => 0 ) ] ) {
if ( valueArray . length != this . min . length ) { return [ ... this . min . map ( ( v , i ) => Math . min ( Math . max ( 0 , v ) , this . max [ i ] ) ) ] ; }
return valueArray . map ( ( value , index ) => Math . min ( Math . max ( value , this . min [ index ] ) , this . max [ index ] ) ) ;
2020-08-20 15:58:18 -06:00
}
2022-11-10 13:30:20 -07:00
outOfBoundsBy ( valueArray = [ ... this . min . map ( v => 0 ) ] ) {
if ( this . inbounds ( valueArray ) ) {
return [ ... this . min . map ( e => 0 ) ] ;
} else {
return this . min . map ( ( value , index ) => {
if ( undefined == valueArray [ index ] ) { return NaN }
if ( valueArray [ index ] < value ) { return - ( value - valueArray [ index ] ) }
if ( valueArray [ index ] > this . max [ index ] ) { return valueArray [ index ] - this . max [ index ] }
return 0 ;
} ) ;
}
}
2020-08-20 15:58:18 -06:00
}
2022-02-08 10:18:26 -07:00
class boundValue {
constructor ( bounds = new bound ( ) , value = 0 ) {
this . bounds = bounds instanceof bound ? bounds : new bound ( ) ;
this . value = ! isNaN ( value ) ? value : 0 ;
}
setValue ( value = 0 ) {
value = ! isNaN ( value ) ? value : 0 ;
return this . value = this . bounds . clamp ( [ value ] ) [ 0 ] ;
}
getValue ( ) {
return this . bounds . clamp ( [ this . value ] ) [ 0 ] ;
}
getValuePercent ( ) {
return this . bounds . normalize ( [ this . value ] ) [ 0 ] ;
}
modValue ( mod = 0 ) {
mod = ! isNaN ( mod ) ? mod : 0 ;
2022-11-10 13:43:19 -07:00
var outOfBoundsBy = this . bounds . outOfBoundsBy ( [ this . value + mod ] ) [ 0 ] ;
2022-11-10 13:30:20 -07:00
return { newValue : this . setValue ( this . value + mod ) , remainder : outOfBoundsBy } ;
2022-02-08 10:18:26 -07:00
}
}
2022-02-08 12:14:09 -07:00
function normalizeArray ( array ) {
array = Array . isArray ( array ) ? array . map ( e => ! isNaN ( e ) ? e : 0 ) : [ 1 ] ;
var sum = array . reduce ( ( a , b ) => a + b ) ;
return array . map ( v => v / sum ) ;
}
2022-02-08 12:37:06 -07:00
class matrix {
constructor ( data ) {
var me = this ;
this . data = [ ] ;
this . rows = 0 ;
this . cols = 0 ;
if ( ! Array . isArray ( data ) ) {
me . data . push ( [ typeof data == "number" ? data : 0 ] ) ;
me . cols = me . data . length ;
me . rows = me . data [ 0 ] . length ;
} else {
me . cols = data . length ;
me . rows = data [ 0 ] . length ;
for ( var i = 0 ; i < me . cols ; i ++ ) {
var thisCol = [ ] ;
for ( var j = 0 ; j < me . rows ; j ++ ) {
thisCol . push ( data [ i ] [ j ] ) ;
}
me . data . push ( thisCol ) ;
}
}
this . getRow = function ( row ) {
return me . data . map ( function ( col ) { return col [ row ] ; } ) ;
}
this . addRow = function ( rowData ) {
if ( ! Array . isArray ( rowData ) ) { return console . error ( "Not provided an array: " + rowData ) ; }
me . data . forEach ( function ( col , i ) { if ( ! ( rowData [ i ] ) ) { rowData [ i ] = 0 ; } col . push ( rowData [ i ] ) ; } ) ;
}
this . getCol = function ( col ) {
return Array . from ( me . data [ col ] ) ;
}
this . addCol = function ( colData ) {
if ( ! Array . isArray ( colData ) ) { return console . error ( "Not provided an array: " + colData ) ; }
colData = me . data [ 0 ] . map ( function ( a , x ) { if ( colData [ x ] ) { return colData [ x ] ; } else { return 0 ; } } ) ;
me . data . push ( colData ) ;
}
this . transpose = function ( ) {
var resultCols = me . rows ;
var newData = [ ] ;
for ( var c = 0 ; c < resultCols ; c ++ ) {
newData . push ( me . getRow ( c ) ) ;
}
return new matrix ( newData ) ;
}
this . multMat = function ( matB ) {
if ( ! ( matB instanceof matrix ) ) { return console . error ( "Must provide matrix for this method" ) ; }
if ( me . cols != matB . rows ) { return console . error ( "The number of columns in the first matrix should be equal to the number of rows in the second\n" + JSON . stringify ( me . data ) + " x " + JSON . stringify ( matB . data ) ) }
var resultRows = me . rows ;
var resultCols = matB . cols ;
var newData = [ ] ;
for ( var c = 0 ; c < resultCols ; c ++ ) {
var bcol = matB . getCol ( c ) ;
var thisCol = [ ] ;
for ( var r = 0 ; r < resultRows ; r ++ ) {
thisCol . push ( me . getRow ( r ) . map ( function ( a , i ) { var res = a * bcol [ i ] ; return isNaN ( res ) ? 0 : res ; } ) . reduce ( function ( a , b ) { return a + b ; } ) ) ;
}
newData . push ( thisCol ) ;
}
return new matrix ( newData ) ;
}
this . multNum = function ( num ) {
if ( typeof num != "number" ) { return console . error ( "Must provide a number, provided " + ( typeof num ) ) ; }
return new matrix ( me . data . map ( function ( a ) { return a . map ( function ( b ) { var res = b * num ; return isNaN ( res ) ? 0 : res ; } ) ; } ) ) ;
}
this . addMat = function ( matB ) {
if ( ! ( matB instanceof matrix ) ) { return console . error ( "Must provide matrix for this method" ) ; }
if ( me . cols != matB . cols ) { return console . error ( "Matrices must be of the same width" ) ; }
if ( me . rows != matB . rows ) { return console . error ( "Matrices must be of the same height" ) ; }
return new matrix ( me . data . map ( function ( col , x ) { return col . map ( function ( a , y ) { var res = a + matB . data [ x ] [ y ] ; return isNaN ( res ) ? 0 : res ; } ) ; } ) ) ;
}
}
}