redshift
Version:
A JavaScript UX framework. Handles animation, UI physics and user input tracking.
353 lines (274 loc) • 11.8 kB
JavaScript
/*
Calculators
----------------------------------------
Simple I/O snippets
*/
"use strict";
var utils = require('./utils.js'),
calc = {
/*
Angle between points
Translates the hypothetical line so that the 'from' coordinates
are at 0,0, then return the angle using .angleFromCenter()
@param [object]: X and Y coordinates of from point
@param [object]: X and Y cordinates of to point
@return [radian]: Angle between the two points in radians
*/
angle: function (pointA, pointB) {
var from = pointB ? pointA : {x: 0, y: 0},
to = pointB || pointA,
point = {
x: to.x - from.x,
y: to.y - from.y
};
return this.angleFromCenter(point.x, point.y);
},
/*
Angle from center
Returns the current angle, in radians, of a defined point
from a center (assumed 0,0)
@param [number]: X coordinate of second point
@param [number]: Y coordinate of second point
@return [radian]: Angle between 0, 0 and point in radians
*/
angleFromCenter: function (x, y) {
return this.radiansToDegrees(Math.atan2(y, x));
},
/*
Convert degrees to radians
@param [number]: Value in degrees
@return [number]: Value in radians
*/
degreesToRadians: function (degrees) {
return degrees * Math.PI / 180;
},
/*
Dilate
Change the progression between a and b according to dilation.
So dilation = 0.5 would change
a --------- b
to
a ---- b
@param [number]: Previous value
@param [number]: Current value
@param [number]: Dilate progress by x
@return [number]: Previous value plus the dilated difference
*/
dilate: function (a, b, dilation) {
return a + ((b - a) * dilation);
},
/*
Distance
Returns the distance between (0,0) and pointA, unless pointB
is provided, then we return the difference between the two.
@param [object/number]: x and y or just x of point A
@param [object/number]: (optional): x and y or just x of point B
@return [number]: The distance between the two points
*/
distance: function (pointA, pointB) {
return (typeof pointA === "number") ? this.distance1D(pointA, pointB) : this.distance2D(pointA, pointB);
},
/*
Distance 1D
Returns the distance between point A and point B
@param [number]: Point A
@param [number]: (optional): Point B
@return [number]: The distance between the two points
*/
distance1D: function (pointA, pointB) {
var bIsNum = (typeof pointB === 'number'),
from = bIsNum ? pointA : 0,
to = bIsNum ? pointB : pointA;
return absolute(to - from);
},
/*
Distance 2D
Returns the distance between (0,0) and point A, unless point B
is provided, then we return the difference between the two.
@param [object]: x and y of point A
@param [object]: (optional): x and y of point B
@return [number]: The distance between the two points
*/
distance2D: function (pointA, pointB) {
var bIsObj = (typeof pointB === "object"),
from = bIsObj ? pointA : {x: 0, y: 0},
to = bIsObj ? pointB : pointA,
point = {
x: absolute(to.x - from.x),
y: absolute(to.y - from.y)
};
return this.hypotenuse(point.x, point.y);
},
/*
Hypotenuse
Returns the hypotenuse, side C, given the lengths of sides A and B.
@param [number]: Length of A
@param [number]: Length of B
@return [number]: Length of C
*/
hypotenuse: function (a, b) {
var a2 = a * a,
b2 = b * b,
c2 = a2 + b2;
return Math.sqrt(c2);
},
/*
Offset between two inputs
Calculate the difference between two different inputs
@param [Point]: First input
@param [Point]: Second input
@return [Offset]: Distance metrics between two points
*/
offset: function (a, b) {
var offset = {};
for (var key in b) {
if (b.hasOwnProperty(key)) {
if (a.hasOwnProperty(key)) {
offset[key] = b[key] - a[key];
} else {
offset[key] = 0;
}
}
}
if (isNum(offset.x) && isNum(offset.y)) {
offset.angle = this.angle(a, b);
offset.distance = this.distance2D(a, b);
}
return offset;
},
/*
Point from angle and distance
@param [object]: 2D point of origin
@param [number]: Angle from origin
@param [number]: Distance from origin
@return [object]: Calculated 2D point
*/
pointFromAngleAndDistance: function (origin, angle, distance) {
var point = {};
point.x = distance * Math.cos(angle) + origin.x;
point.y = distance * Math.sin(angle) + origin.y;
return point;
},
/*
Progress within given range
Given a lower limit and an upper limit, we return the progress
(expressed as a number 0-1) represented by the given value, and
limit that progress to within 0-1.
@param [number]: Value to find progress within given range
@param [number]: Lower limit if full range given, upper if not
@param [number] (optional): Upper limit of range
@return [number]: Progress of value within range as expressed 0-1
*/
progress: function (value, limitA, limitB) {
var bIsNum = (typeof limitB === 'number'),
from = bIsNum ? limitA : 0,
to = bIsNum ? limitB : limitA,
range = to - from,
progress = (value - from) / range;
return progress;
},
/*
Convert radians to degrees
@param [number]: Value in radians
@return [number]: Value in degrees
*/
radiansToDegrees: function (radians) {
return radians * 180 / Math.PI;
},
/*
Calculate relative value
Takes the operator and value from a string, ie "+=5", and applies
to the current value to resolve a new target.
@param [number]: Current value
@param [string]: Relative value
@return [number]: New value
*/
relativeValue: function (current, rel) {
var newValue = current,
equation = rel.split('='),
operator = equation[0],
splitVal = utils.splitValUnit(equation[1]);
switch (operator) {
case '+':
newValue += splitVal.value;
break;
case '-':
newValue -= splitVal.value;
break;
case '*':
newValue *= splitVal.value;
break;
case '/':
newValue /= splitVal.value;
break;
}
if (splitVal.unit) {
newValue += splitVal.unit;
}
return newValue;
},
/*
Restrict value to range
Return value within the range of lowerLimit and upperLimit
@param [number]: Value to keep within range
@param [number]: Lower limit of range
@param [number]: Upper limit of range
@return [number]: Value as limited within given range
*/
restricted: function (value, min, max) {
var restricted = (min !== undefined) ? Math.max(value, min) : value;
restricted = (max !== undefined) ? Math.min(restricted, max) : restricted;
return restricted;
},
/*
Convert x per second to per frame velocity based on fps
@param [number]: Unit per second
@param [number]: Frame duration in ms
*/
speedPerFrame: function (xps, frameDuration) {
return (isNum(xps)) ? xps / (1000 / frameDuration) : 0;
},
/*
Convert velocity into velicity per second
@param [number]: Unit per frame
@param [number]: Frame duration in ms
*/
speedPerSecond: function (velocity, frameDuration) {
return velocity * (1000 / frameDuration);
},
/*
Value in range from progress
Given a lower limit and an upper limit, we return the value within
that range as expressed by progress (a number from 0-1)
@param [number]: The progress between lower and upper limits expressed 0-1
@param [number]: Lower limit of range, or upper if limit2 not provided
@param [number] (optional): Upper limit of range
@return [number]: Value as calculated from progress within range (not limited within range)
*/
value: function (progress, limitA, limitB) {
var bIsNum = (typeof limitB === 'number'),
from = bIsNum ? limitA : 0,
to = bIsNum ? limitB : limitA;
return (- progress * from) + (progress * to) + from;
},
/*
Eased value in range from progress
Given a lower limit and an upper limit, we return the value within
that range as expressed by progress (a number from 0-1)
@param [number]: The progress between lower and upper limits expressed 0-1
@param [number]: Lower limit of range, or upper if limit2 not provided
@param [number]: Upper limit of range
@param [function]: Easing to apply to value
@return [number]: Value as calculated from progress within range (not limited within range)
*/
valueEased: function (progress, from, to, easing) {
var easedProgress = easing(progress);
return this.value(easedProgress, from, to);
}
},
/*
Caching functions used multiple times to reduce filesize and increase performance
*/
isNum = utils.isNum,
absolute = Math.abs;
module.exports = calc;