uranium-core/stdlib/ease.lua

338 lines
8.5 KiB
Lua

-- nabbed straight from mirin template:
-- https://github.com/XeroOl/notitg-mirin/blob/d1e9a8e71026aeabe81c682a114ce265cbd6362a/template/ease.lua
local sqrt = math.sqrt
local sin = math.sin
local asin = math.asin
local cos = math.cos
local pow = math.pow
local exp = math.exp
local pi = math.pi
local abs = math.abs
-- ===================================================================== --
-- Utility functions
--- Flip any easing function, making it go from 1 to 0
-- Example use:
-- ```lua
-- ease {0, 20, flip(outQuad), 50, 'modname'}
-- ```
flip = setmetatable({}, {
__call = function(self, fn)
self[fn] = self[fn] or function(x) return 1 - fn(x) end
return self[fn]
end
})
-- Mix two easing functions together into a new ease
-- the new ease starts by acting like the first argument, and then ends like the second argument
-- Example: ease {0, 20, blendease(inQuad, outQuad), 100, 'modname'}
blendease = setmetatable({}, {
__index = function(self, key)
self[key] = {}
return self[key]
end,
__call = function(self, fn1, fn2)
if not self[fn1][fn2] then
local transient1 = fn1(1) <= 0.5
local transient2 = fn2(1) <= 0.5
if transient1 and not transient2 then
error('blendease: the first argument is a transient ease, but the second argument doesn\'t match')
end
if transient2 and not transient1 then
error('blendease: the second argument is a transient ease, but the first argument doesn\'t match')
end
self[fn1][fn2] = function(x)
local mixFactor = 3*x^2-2*x^3
return (1 - mixFactor) * fn1(x) + mixFactor * fn2(x)
end
end
return self[fn1][fn2]
end
})
local function param1cache(self, param1)
self.cache[param1] = self.cache[param1] or function(x)
return self.fn(x, param1)
end
return self.cache[param1]
end
local param1mt = {
__call = function(self, x, param1)
return self.fn(x, param1 or self.dp1)
end,
__index = {
param = param1cache,
params = param1cache,
}
}
-- Declare an easing function taking one custom parameter
function with1param(fn, defaultparam1)
return setmetatable({
fn = fn,
dp1 = defaultparam1,
cache = {},
}, param1mt)
end
local function param2cache(self, param1, param2)
self.cache[param1] = self.cache[param1] or {}
self.cache[param1][param2] = self.cache[param1][param2] or function(x)
return self.fn(x, param1, param2)
end
return self.cache[param1][param2]
end
local param2mt = {
__call = function(self, x, param1, param2)
return self.fn(x, param1 or self.dp1, param2 or self.dp2)
end,
__index = {
param=param2cache,
params=param2cache,
}
}
-- Declare an easing function taking two custom parameters
function with2params(fn, defaultparam1, defaultparam2)
return setmetatable({
fn = fn,
dp1 = defaultparam1,
dp2 = defaultparam2,
cache = {},
}, param2mt)
end
-- ===================================================================== --
-- Easing functions
function bounce(t) return 4 * t * (1 - t) end
function tri(t) return 1 - abs(2 * t - 1) end
function bell(t) return inOutQuint(tri(t)) end
function pop(t) return 3.5 * (1 - t) * (1 - t) * sqrt(t) end
function tap(t) return 3.5 * t * t * sqrt(1 - t) end
function pulse(t) return t < .5 and tap(t * 2) or -pop(t * 2 - 1) end
function spike(t) return exp(-10 * abs(2 * t - 1)) end
function inverse(t) return t * t * (1 - t) * (1 - t) / (0.5 - t) end
local function popElasticInternal(t, damp, count)
return (1000 ^ -(t ^ damp) - 0.001) * sin(count * pi * t)
end
local function tapElasticInternal(t, damp, count)
return (1000 ^ -((1 - t) ^ damp) - 0.001) * sin(count * pi * (1 - t))
end
local function pulseElasticInternal(t, damp, count)
if t < .5 then
return tapElasticInternal(t * 2, damp, count)
else
return -popElasticInternal(t * 2 - 1, damp, count)
end
end
popElastic = with2params(popElasticInternal, 1.4, 6)
tapElastic = with2params(tapElasticInternal, 1.4, 6)
pulseElastic = with2params(pulseElasticInternal, 1.4, 6)
impulse = with1param(function(t, damp)
t = t ^ damp
return t * (1000 ^ -t - 0.001) * 18.6
end, 0.9)
function instant() return 1 end
function linear(t) return t end
function inQuad(t) return t * t end
function outQuad(t) return -t * (t - 2) end
function inOutQuad(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 2
else
return 1 - 0.5 * (2 - t) ^ 2
end
end
function outInQuad(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 2
else
return 0.5 + 0.5 * (t - 1) ^ 2
end
end
function inCubic(t) return t * t * t end
function outCubic(t) return 1 - (1 - t) ^ 3 end
function inOutCubic(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 3
else
return 1 - 0.5 * (2 - t) ^ 3
end
end
function outInCubic(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 3
else
return 0.5 + 0.5 * (t - 1) ^ 3
end
end
function inQuart(t) return t * t * t * t end
function outQuart(t) return 1 - (1 - t) ^ 4 end
function inOutQuart(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 4
else
return 1 - 0.5 * (2 - t) ^ 4
end
end
function outInQuart(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 4
else
return 0.5 + 0.5 * (t - 1) ^ 4
end
end
function inQuint(t) return t ^ 5 end
function outQuint(t) return 1 - (1 - t) ^ 5 end
function inOutQuint(t)
t = t * 2
if t < 1 then
return 0.5 * t ^ 5
else
return 1 - 0.5 * (2 - t) ^ 5
end
end
function outInQuint(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * (1 - t) ^ 5
else
return 0.5 + 0.5 * (t - 1) ^ 5
end
end
function inExpo(t) return 1000 ^ (t - 1) - 0.001 end
function outExpo(t) return 1.001 - 1000 ^ -t end
function inOutExpo(t)
t = t * 2
if t < 1 then
return 0.5 * 1000 ^ (t - 1) - 0.0005
else
return 1.0005 - 0.5 * 1000 ^ (1 - t)
end
end
function outInExpo(t)
if t < 0.5 then
return outExpo(t * 2) * 0.5
else
return inExpo(t * 2 - 1) * 0.5 + 0.5
end
end
function inCirc(t) return 1 - sqrt(1 - t * t) end
function outCirc(t) return sqrt(-t * t + 2 * t) end
function inOutCirc(t)
t = t * 2
if t < 1 then
return 0.5 - 0.5 * sqrt(1 - t * t)
else
t = t - 2
return 0.5 + 0.5 * sqrt(1 - t * t)
end
end
function outInCirc(t)
if t < 0.5 then
return outCirc(t * 2) * 0.5
else
return inCirc(t * 2 - 1) * 0.5 + 0.5
end
end
function outBounce(t)
if t < 1 / 2.75 then
return 7.5625 * t * t
elseif t < 2 / 2.75 then
t = t - 1.5 / 2.75
return 7.5625 * t * t + 0.75
elseif t < 2.5 / 2.75 then
t = t - 2.25 / 2.75
return 7.5625 * t * t + 0.9375
else
t = t - 2.625 / 2.75
return 7.5625 * t * t + 0.984375
end
end
function inBounce(t) return 1 - outBounce(1 - t) end
function inOutBounce(t)
if t < 0.5 then
return inBounce(t * 2) * 0.5
else
return outBounce(t * 2 - 1) * 0.5 + 0.5
end
end
function outInBounce(t)
if t < 0.5 then
return outBounce(t * 2) * 0.5
else
return inBounce(t * 2 - 1) * 0.5 + 0.5
end
end
function inSine(x) return 1 - cos(x * (pi * 0.5)) end
function outSine(x) return sin(x * (pi * 0.5)) end
function inOutSine(x)
return 0.5 - 0.5 * cos(x * pi)
end
function outInSine(t)
if t < 0.5 then
return outSine(t * 2) * 0.5
else
return inSine(t * 2 - 1) * 0.5 + 0.5
end
end
function outElasticInternal(t, a, p)
return a * pow(2, -10 * t) * sin((t - p / (2 * pi) * asin(1/a)) * 2 * pi / p) + 1
end
local function inElasticInternal(t, a, p)
return 1 - outElasticInternal(1 - t, a, p)
end
function inOutElasticInternal(t, a, p)
return t < 0.5
and 0.5 * inElasticInternal(t * 2, a, p)
or 0.5 + 0.5 * outElasticInternal(t * 2 - 1, a, p)
end
function outInElasticInternal(t, a, p)
return t < 0.5
and 0.5 * outElasticInternal(t * 2, a, p)
or 0.5 + 0.5 * inElasticInternal(t * 2 - 1, a, p)
end
inElastic = with2params(inElasticInternal, 1, 0.3)
outElastic = with2params(outElasticInternal, 1, 0.3)
inOutElastic = with2params(inOutElasticInternal, 1, 0.3)
outInElastic = with2params(outInElasticInternal, 1, 0.3)
function inBackInternal(t, a) return t * t * (a * t + t - a) end
function outBackInternal(t, a) t = t - 1 return t * t * ((a + 1) * t + a) + 1 end
function inOutBackInternal(t, a)
return t < 0.5
and 0.5 * inBackInternal(t * 2, a)
or 0.5 + 0.5 * outBackInternal(t * 2 - 1, a)
end
function outInBackInternal(t, a)
return t < 0.5
and 0.5 * outBackInternal(t * 2, a)
or 0.5 + 0.5 * inBackInternal(t * 2 - 1, a)
end
inBack = with1param(inBackInternal, 1.70158)
outBack = with1param(outBackInternal, 1.70158)
inOutBack = with1param(inOutBackInternal, 1.70158)
outInBack = with1param(outInBackInternal, 1.70158)