canvas-sketch-util
Version:
Utilities for sketching in Canvas, WebGL and generative art
207 lines (181 loc) • 5.05 kB
JavaScript
var defined = require('defined');
var wrap = require('./lib/wrap');
var EPSILON = Number.EPSILON;
function clamp (value, min, max) {
return min < max
? (value < min ? min : value > max ? max : value)
: (value < max ? max : value > min ? min : value);
}
function clamp01 (v) {
return clamp(v, 0, 1);
}
function lerp (min, max, t) {
return min * (1 - t) + max * t;
}
function inverseLerp (min, max, t) {
if (Math.abs(min - max) < EPSILON) return 0;
else return (t - min) / (max - min);
}
function smoothstep (min, max, t) {
var x = clamp(inverseLerp(min, max, t), 0, 1);
return x * x * (3 - 2 * x);
}
function toFinite (n, defaultValue) {
defaultValue = defined(defaultValue, 0);
return typeof n === 'number' && isFinite(n) ? n : defaultValue;
}
function expandVector (dims) {
if (typeof dims !== 'number') throw new TypeError('Expected dims argument');
return function (p, defaultValue) {
defaultValue = defined(defaultValue, 0);
var scalar;
if (p == null) {
// No vector, create a default one
scalar = defaultValue;
} else if (typeof p === 'number' && isFinite(p)) {
// Expand single channel to multiple vector
scalar = p;
}
var out = [];
var i;
if (scalar == null) {
for (i = 0; i < dims; i++) {
out[i] = toFinite(p[i], defaultValue);
}
} else {
for (i = 0; i < dims; i++) {
out[i] = scalar;
}
}
return out;
};
}
function lerpArray (min, max, t, out) {
out = out || [];
if (min.length !== max.length) {
throw new TypeError('min and max array are expected to have the same length');
}
for (var i = 0; i < min.length; i++) {
out[i] = lerp(min[i], max[i], t);
}
return out;
}
function newArray (n, initialValue) {
n = defined(n, 0);
if (typeof n !== 'number') throw new TypeError('Expected n argument to be a number');
var out = [];
for (var i = 0; i < n; i++) out.push(initialValue);
return out;
}
function linspace (n, opts) {
n = defined(n, 0);
if (typeof n !== 'number') throw new TypeError('Expected n argument to be a number');
opts = opts || {};
if (typeof opts === 'boolean') {
opts = { endpoint: true };
}
var offset = defined(opts.offset, 0);
if (opts.endpoint) {
return newArray(n).map(function (_, i) {
return n <= 1 ? 0 : ((i + offset) / (n - 1));
});
} else {
return newArray(n).map(function (_, i) {
return (i + offset) / n;
});
}
}
function lerpFrames (values, t, out) {
t = clamp(t, 0, 1);
var len = values.length - 1;
var whole = t * len;
var frame = Math.floor(whole);
var fract = whole - frame;
var nextFrame = Math.min(frame + 1, len);
var a = values[frame % values.length];
var b = values[nextFrame % values.length];
if (typeof a === 'number' && typeof b === 'number') {
return lerp(a, b, fract);
} else if (Array.isArray(a) && Array.isArray(b)) {
return lerpArray(a, b, fract, out);
} else {
throw new TypeError('Mismatch in value type of two array elements: ' + frame + ' and ' + nextFrame);
}
}
function mod (a, b) {
return ((a % b) + b) % b;
}
function degToRad (n) {
return n * Math.PI / 180;
}
function radToDeg (n) {
return n * 180 / Math.PI;
}
function fract (n) {
return n - Math.floor(n);
}
function sign (n) {
if (n > 0) return 1;
else if (n < 0) return -1;
else return 0;
}
// Specific function from Unity / ofMath, not sure its needed?
// function lerpWrap (a, b, t, min, max) {
// return wrap(a + wrap(b - a, min, max) * t, min, max)
// }
function pingPong (t, length) {
t = mod(t, length * 2);
return length - Math.abs(t - length);
}
function damp (a, b, lambda, dt) {
return lerp(a, b, 1 - Math.exp(-lambda * dt));
}
function dampArray (a, b, lambda, dt, out) {
out = out || [];
for (var i = 0; i < a.length; i++) {
out[i] = damp(a[i], b[i], lambda, dt);
}
return out;
}
function mapRange (value, inputMin, inputMax, outputMin, outputMax, clamp) {
// Reference:
// https://openframeworks.cc/documentation/math/ofMath/
if (Math.abs(inputMin - inputMax) < EPSILON) {
return outputMin;
} else {
var outVal = ((value - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin);
if (clamp) {
if (outputMax < outputMin) {
if (outVal < outputMax) outVal = outputMax;
else if (outVal > outputMin) outVal = outputMin;
} else {
if (outVal > outputMax) outVal = outputMax;
else if (outVal < outputMin) outVal = outputMin;
}
}
return outVal;
}
}
module.exports = {
mod: mod,
fract: fract,
sign: sign,
degToRad: degToRad,
radToDeg: radToDeg,
wrap: wrap,
pingPong: pingPong,
linspace: linspace,
lerp: lerp,
lerpArray: lerpArray,
inverseLerp: inverseLerp,
lerpFrames: lerpFrames,
clamp: clamp,
clamp01: clamp01,
smoothstep: smoothstep,
damp: damp,
dampArray: dampArray,
mapRange: mapRange,
expand2D: expandVector(2),
expand3D: expandVector(3),
expand4D: expandVector(4)
};