/**
* Utils
* @module Utils
*/
/**
* Linear interpolation between to values
* @param {Number} a from
* @param {Number} b to
* @param {Number} p value between 0 and 1
* @returns {Number}
*/
function lerp(a, b, p) {
var _p = Number(p);
_p = Math.max(0, Math.min(1, _p));
return a + _p * (b - a);
}
/**
* Framerate independent linear interpolation
* @param {Number} a from
* @param {Number} b to
* @param {Number} p value between 0 and 1
* @param {Number} dt delta time in ms since last frame
* @param {Number} targetFPS target (context) fps
* @returns {Number}
*/
function fi_lerp(a, b, p, dt, targetFPS = 60) {
return lerp(a, b, 1 - Math.pow(1 - p, dt / (1000 / targetFPS)));
}
/**
* Limit the number to a given number of digits
* @param {Number} n
* @param {Number} digits
* @returns {Number}
*/
function fixed(n, digits) {
digits = digits || 3;
return parseFloat(n.toFixed(digits));
}
/**
* Limit the range of a number
* @param Number} n the number
* @param Number} min minimum
* @param Number} max maximum
* @returns {Number} the clamped number
*/
function clamp(n, min, max) {
return n <= min ? min : n >= max ? max : n;
}
/**
* Check if the given Object can be used as a vector, this simply checks if the object has a x and y property
* @param {Object|Vector} v
* @returns {Boolean}
*/
function isVector(v) {
return v && typeof v.x !== "undefined" && typeof v.y !== "undefined";
}
/**
* Check if the two lines intersect
* @param {Number} x1 start x coordinate of line 1
* @param {Number} y1 start y coordinate of line 1
* @param {Number} x2 end x coordinate of line 1
* @param {Number} y2 end y coordinate of line 1
* @param {Number} x3 start x coordinate of line 2
* @param {Number} y3 start y coordinate of line 2
* @param {Number} x4 end x coordinate of line 2
* @param {Number} y4 end y coordinate of line 2
* @returns {Boolean}
*/
function lineIntersectsLine(x1, y1, x2, y2, x3, y3, x4, y4) {
let uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
let uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
return uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1;
}
/**
* Get the point where the two lines intersect
* @param {Number} x1 start x coordinate of line 1
* @param {Number} y1 start y coordinate of line 1
* @param {Number} x2 end x coordinate of line 1
* @param {Number} y2 end y coordinate of line 1
* @param {Number} x3 start x coordinate of line 2
* @param {Number} y3 start y coordinate of line 2
* @param {Number} x4 end x coordinate of line 2
* @param {Number} y4 end y coordinate of line 2
* @returns {Boolean} false if there is no intersection
* @returns {Object} object with x and y if there is an intersection
*/
function lineIntersection(x1, y1, x2, y2, x3, y3, x4, y4) {
// Check if none of the lines are of length 0
if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) {
return false
}
denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
// Lines are parallel
if (denominator === 0) {
return false
}
let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator
let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator
// is the intersection along the segments
if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
return false
}
// Return a object with the x and y coordinates of the intersection
let x = x1 + ua * (x2 - x1)
let y = y1 + ua * (y2 - y1)
return { x, y }
}
/**
* convert the given variable to a point in array form, this is used in some internal math functions
*
* @example
* point(new Vector(1,2)) => [1,2]
* point({x:1, y:2}) => [1,2]
* point([1,2]) => [1,2]
*
* @param {Vector|Object|Array} p a point as object, vector or array
* @returns {Array} Array with two entries `[x, y]` never a reference to the original point
*/
function point(p) {
if (isVector(p)) {
return [p.x, p.y];
}
if (Array.isArray(p) && p.length === 2) {
return [p[0], p[1]];
}
throw new Error("provided variable is not a point");
}
/**
* get the quadrant of the position
* 1 = positive x, positive y
* 2 = negative x, positive y
* 3 = negative x, negative y
* 4 = positive x, negative y
* @param {Number} x
* @param {Number} y
* @returns {Number} the quadrant
*/
function quadrant(x, y) {
if (x > 0) {
return y >= 0 ? 1 : 4;
} else if (x < 0) {
return y >= 0 ? 2 : 3;
} else if (y > 0) {
return 1;
} else if (y < 0) {
return 3;
}
return 0;
}
/**
* triangle circumcenter
* @param {Number} a
* @param {Number} b
* @param {Number} c
* @returns {Array}
*/
function circumcenter(a, b, c) {
a = point(a);
b = point(b);
c = point(c);
const ad = a[0] * a[0] + a[1] * a[1];
const bd = b[0] * b[0] + b[1] * b[1];
const cd = c[0] * c[0] + c[1] * c[1];
const D = 2 * (a[0] * (b[1] - c[1]) + b[0] * (c[1] - a[1]) + c[0] * (a[1] - b[1]));
return [
1 / D * (ad * (b[1] - c[1]) + bd * (c[1] - a[1]) + cd * (a[1] - b[1])),
1 / D * (ad * (c[0] - b[0]) + bd * (a[0] - c[0]) + cd * (b[0] - a[0])),
];
}
function rangeIntersect(a1, a2, b1, b2) {
let min = a1 < b1 ? [a1, a2] : [b1, b2];
let max = a1 < b1 ? [b1, b2] : [a1, a2];
if (min[1] < max[0]) return null;
return [
max[0],
min[1] < max[1] ? min[1] : max[1]
]
}
export {
lerp,
fi_lerp,
fixed,
clamp,
isVector,
lineIntersectsLine,
lineIntersection,
point,
quadrant,
circumcenter,
rangeIntersect
}