/**
* A static utilitites class for all things math.
* @module BB.MathUtils
* @class BB.MathUtils
* @static
*/
define(['./BB', './BB.Vector2'],
function( BB, Vector2){
'use strict';
BB.Vector2 = Vector2;
BB.MathUtils = function() {};
/**
* Scales value using min and max. This is the inverse of BB.MathUtils.lerp(...).
* @method norm
* @static
* @param {Number} value The value to be scaled.
* @param {Number} min
* @param {Number} max
* @return {Number} Returns the scaled value.
*/
BB.MathUtils.norm = function(value, min, max) {
if (typeof value !== "number") {
throw new Error("BB.MathUtils.norm: value is not a number type");
} else if (typeof min !== "number") {
throw new Error("BB.MathUtils.norm: min is not a number type");
} else if (typeof max !== "number") {
throw new Error("BB.MathUtils.norm: max is not a number type");
}
return (value - min) / (max - min);
};
/**
* Linear interpolate norm from min and max. This is the inverse of BB.MathUtils.norm(...).
* @method lerp
* @static
* @param {Number} value
* @param {Number} min
* @param {Number} max
* @return {Number} Returns the lerped norm.
*/
BB.MathUtils.lerp = function(norm, min, max) {
if (typeof norm !== "number") {
throw new Error("BB.MathUtils.lerp: norm is not a number type");
} else if (typeof min !== "number") {
throw new Error("BB.MathUtils.lerp: min is not a number type");
} else if (typeof max !== "number") {
throw new Error("BB.MathUtils.lerp: max is not a number type");
}
return (max - min) * norm + min;
};
/**
* Constrains value using min and max as the upper and lower bounds.
* @method clamp
* @static
* @param {Number} value The value to be clamped.
* @param {Number} min The lower limit to clamp value by.
* @param {Number} max The upper limit to clamp value by.
* @return {Number} The clamped value.
*/
BB.MathUtils.clamp = function(value, min, max) {
if (typeof value !== "number") {
throw new Error("BB.MathUtils.clamp: norm is not a number type");
} else if (typeof min !== "number") {
throw new Error("BB.MathUtils.clamp: min is not a number type");
} else if (typeof max !== "number") {
throw new Error("BB.MathUtils.clamp: max is not a number type");
}
return Math.max(min, Math.min(max, value));
};
/**
* Maps (scales) value between sourceMin and sourceMax to destMin and destMax.
* @method map
* @static
* @param {Number} value The value to be mapped.
* @param {Number} sourceMin
* @param {Number} sourceMax
* @param {Number} destMin
* @param {Number} destMax
* @return {Number} Returns the mapped value.
*/
BB.MathUtils.map = function(value, sourceMin, sourceMax, destMin, destMax) {
if (typeof value !== "number") {
throw new Error("BB.MathUtils.map: value is not a number type");
} else if (typeof sourceMin !== "number") {
throw new Error("BB.MathUtils.map: sourceMin is not a number type");
} else if (typeof sourceMax !== "number") {
throw new Error("BB.MathUtils.map: sourceMax is not a number type");
} else if (typeof destMin !== "number") {
throw new Error("BB.MathUtils.map: destMin is not a number type");
} else if (typeof destMax !== "number") {
throw new Error("BB.MathUtils.map: destMax is not a number type");
}
return this.lerp(this.norm(value, sourceMin, sourceMax), destMin, destMax);
};
/**
* Get the distance between two points.
* @method dist
* @static
* @param {Number} p1x The x value of the first point.
* @param {Number} p1y The y value of the first point.
* @param {Number} p2x The x value of the second point.
* @param {Number} p2y The y value of the second point.
* @return {Number} Returns the distance between (p1x, p1y) and (p2x, p2y).
*/
BB.MathUtils.dist = function(p1x, p1y, p2x, p2y){
if (typeof p1x !== "number") {
throw new Error("BB.MathUtils.dist: p1x is not a number type");
} else if (typeof p1y !== "number") {
throw new Error("BB.MathUtils.dist: p1y is not a number type");
} else if (typeof p2x !== "number") {
throw new Error("BB.MathUtils.dist: p2x is not a number type");
} else if (typeof p2y !== "number") {
throw new Error("BB.MathUtils.dist: p2y is not a number type");
}
return Math.sqrt(Math.pow(p2x - p1x, 2) + Math.pow(p2y - p1y, 2));
};
/**
* Get the angle between two points in radians. For degrees process this
* return value through BB.MathUtils.radToDegree(...).
* @method angleBtwn
* @static
* @param {Number} p1x The x value of the first point.
* @param {Number} p1y The y value of the first point.
* @param {Number} p2x The x value of the second point.
* @param {Number} p2y The y value of the second point.
* @return {Number} Returns the angle between (p1x, p1y) and (p2x, p2y) in
* radians.
*/
BB.MathUtils.angleBtw = function(p1x, p1y, p2x, p2y){
if (typeof p1x !== "number") {
throw new Error("BB.MathUtils.angleBtwn: p1x is not a number type");
} else if (typeof p1y !== "number") {
throw new Error("BB.MathUtils.angleBtwn: p1y is not a number type");
} else if (typeof p2x !== "number") {
throw new Error("BB.MathUtils.angleBtwn: p2x is not a number type");
} else if (typeof p2y !== "number") {
throw new Error("BB.MathUtils.angleBtwn: p2y is not a number type");
}
return Math.atan2( p2x - p1x, p2y - p1y );
};
/**
* Translate radians into degrees.
* @method radToDeg
* @static
* @param {[type]} radians
* @return {[type]} Returns radians in degrees.
*/
BB.MathUtils.radToDeg = function(radians) {
if (typeof radians !== "number") {
throw new Error("BB.MathUtils.radToDegree: radians is not a number type");
}
return radians * (180.0 / Math.PI);
};
/**
* Translate degrees into radians.
* @method degToRad
* @static
* @param {[type]} degrees
* @return {[type]} Returns degrees in radians.
*/
BB.MathUtils.degToRad = function(degrees) {
if (typeof degrees !== "number") {
throw new Error("BB.MathUtils.degToRad: degrees is not a number type");
}
return degrees * (Math.PI / 180.0);
};
/**
* Translate from polar coordinates to cartesian coordinates.
* @method polarToCartesian
* @static
* @param {Number} radius The straight line distance from the origin.
* @param {Number} degrees The angle in degrees measured clockwise from the
* positive x axis.
* @return {Array} An array of length two where the first element is
* the x value and the second element is the y value.
*/
BB.MathUtils.polarToCartesian = function(radius, degrees) {
if (typeof radius !== "number" || typeof degrees !== "number") {
throw new Error("BB.MathUtils.polarToCartesian: invalid arguments, function expects two Number type parameters.");
}
return [ radius * Math.cos(degrees), radius * Math.sin(degrees) ];
};
/**
* Translate from cartesian to polar coordinates.
* @method cartesianToPolar
* @static
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @return {Array} An array of length two where the first element is the
* polar radius and the second element is the polar angle in degrees
* measured clockwise from the positive x axis.
*/
BB.MathUtils.cartesianToPolar = function(x, y) {
if (typeof x !== "number" || typeof y !== "number") {
throw new Error("BB.MathUtils.cartesianToPolar: invalid arguments, function expects two Number type parameters.");
}
return [ Math.sqrt((x * x) + (y * y)), Math.atan(y / x) ];
};
/**
* return a random int between a min and a max
* @method randomInt
* @static
* @param {Number} min minimum value ( default to 0 if only one argument is passed )
* @param {Number} max maximum value
* @return {Number} random integer
*/
BB.MathUtils.randomInt = function( min, max) {
if( arguments.length === 0 ){
throw new Error("BB.MathUtils.cartesianToPolar: requires at least one argument");
}
else if( arguments.length === 1 ){
return Math.floor(0 + Math.random() * (min - 0 + 1));
}
else {
return Math.floor(min + Math.random() * (max - min + 1));
}
};
/**
* return a random float between a min and a max
* @method randomFloat
* @static
* @param {Number} min minimum value ( default to 0 if only one argument is passed )
* @param {Number} max maximum value
* @return {Number} random float
*/
BB.MathUtils.randomFloat = function( min, max ) {
if( arguments.length === 0 ){
throw new Error("BB.MathUtils.cartesianToPolar: requires at least one argument");
}
else if( arguments.length === 1 ){
return 0 + Math.random() * (min - 0);
}
else {
return min + Math.random() * (max - min);
}
};
// P5.js perlin noise stuff
var perlin = null;
var PERLIN_YWRAPB = 4;
var PERLIN_YWRAP = 1<<PERLIN_YWRAPB;
var PERLIN_ZWRAPB = 8;
var PERLIN_ZWRAP = 1<<PERLIN_ZWRAPB;
var PERLIN_SIZE = 4095;
var perlin_octaves = 4; // default to medium smooth
var perlin_amp_falloff = 0.5; // 50% reduction/octave
function scaled_cosine(i) {
return 0.5*(1.0-Math.cos(i*Math.PI));
}
/**
* Returns the Perlin noise value at specified coordinates. Perlin noise is
* a random sequence generator producing a more natural ordered, harmonic
* succession of numbers compared to the standard <b>random()</b> function.
* This function is taken almost verbatim from P5.js.
* @method noise
* @param {Number} x x-coordinate in noise space
* @param {Number} y y-coordinate in noise space
* @param {Number} z z-coordinate in noise space
* @return {Number} Perlin noise value (between 0 and 1) at specified
* coordinates
*/
BB.MathUtils.noise = function(x, y, z) {
y = y || 0;
z = z || 0;
if (perlin === null) {
perlin = new Array(PERLIN_SIZE + 1);
for (var i = 0; i < PERLIN_SIZE + 1; i++) {
perlin[i] = Math.random();
}
}
if (x<0) { x=-x; }
if (y<0) { y=-y; }
if (z<0) { z=-z; }
var xi=Math.floor(x), yi=Math.floor(y), zi=Math.floor(z);
var xf = x - xi;
var yf = y - yi;
var zf = z - zi;
var rxf, ryf;
var r=0;
var ampl=0.5;
var n1,n2,n3;
for (var o=0; o<perlin_octaves; o++) {
var of=xi+(yi<<PERLIN_YWRAPB)+(zi<<PERLIN_ZWRAPB);
rxf = scaled_cosine(xf);
ryf = scaled_cosine(yf);
n1 = perlin[of&PERLIN_SIZE];
n1 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n1);
n2 = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
n2 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n2);
n1 += ryf*(n2-n1);
of += PERLIN_ZWRAP;
n2 = perlin[of&PERLIN_SIZE];
n2 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n2);
n3 = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
n3 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n3);
n2 += ryf*(n3-n2);
n1 += scaled_cosine(zf)*(n2-n1);
r += n1*ampl;
ampl *= perlin_amp_falloff;
xi<<=1;
xf*=2;
yi<<=1;
yf*=2;
zi<<=1;
zf*=2;
if (xf>=1.0) { xi++; xf--; }
if (yf>=1.0) { yi++; yf--; }
if (zf>=1.0) { zi++; zf--; }
}
return r;
};
return BB.MathUtils;
});