Skip to content

Instantly share code, notes, and snippets.

@estama
Created October 4, 2020 12:40
Show Gist options
  • Select an option

  • Save estama/74da0ad4fe92803143628e05a3d02d14 to your computer and use it in GitHub Desktop.

Select an option

Save estama/74da0ad4fe92803143628e05a3d02d14 to your computer and use it in GitHub Desktop.
--== Spring based temporal ==--
local temporalSpring = {}
temporalSpring.__index = temporalSpring
function newTemporalSpring(spring, damp, startingValue)
local data = {spring = spring or 10, damp = damp or 2, state = startingValue or 0, vel = 0}
setmetatable(data, temporalSpring)
return data
end
function temporalSpring:get(sample, dt)
self.vel = self.vel * max(1 - self.damp * dt, 0) + (sample - self.state) * min(self.spring * dt, 1/dt)
self.state = self.state + self.vel * dt
return self.state
end
function temporalSpring:getWithSpringDamp(sample, dt, spring, damp)
self.vel = self.vel * max(1 - damp * dt, 0) + (sample - self.state) * min(spring * dt, 1/dt)
self.state = self.state + self.vel * dt
return self.state
end
function temporalSpring:set(sample)
self.state = sample
self.vel = 0
end
function temporalSpring:value()
return self.state
end
--== S-curve temporal ==--
local temporalSigmoidSmoothing = {}
temporalSigmoidSmoothing.__index = temporalSigmoidSmoothing
function newTemporalSigmoidSmoothing(inRate, startAccel, stopAccel, outRate, startingValue)
local rate = inRate or 1
local startaccel = startAccel or math.huge
local data = {[false] = rate, [true] = outRate or rate, startAccel = startaccel, stopAccel = stopAccel or startaccel, state = startingValue or 0, prevvel = 0}
setmetatable(data, temporalSigmoidSmoothing)
return data
end
function temporalSigmoidSmoothing:get(sample, dt)
local dif = sample - self.state
local prevvel = self.prevvel * max(fsign(self.prevvel * dif), 0)
local vsq = prevvel * prevvel
local absdif = abs(dif)
local difsign = sign(dif)
local acceldt
local absdif2 = absdif * 2
if vsq > absdif2 * self.stopAccel then
acceldt = -difsign * min((vsq / absdif2) * dt, abs(prevvel))
else
acceldt = difsign * self.startAccel * dt
end
local ratelimit = self[dif * self.state >= 0]
self.state = self.state + difsign * min(min(abs(prevvel + 0.5 * acceldt), ratelimit) * dt, absdif)
self.prevvel = difsign * min(abs(prevvel + acceldt), ratelimit)
return self.state
end
function temporalSigmoidSmoothing:getWithRateAccel(sample, dt, ratelimit, startAccel, stopAccel)
local dif = sample - self.state
local prevvel = self.prevvel * max(fsign(self.prevvel * dif), 0)
local vsq = prevvel * prevvel
local absdif = abs(dif)
local difsign = sign(dif)
local acceldt
local absdif2 = absdif * 2
if vsq > absdif2 * (stopAccel or startAccel) then
acceldt = -difsign * min((vsq / absdif2) * dt, abs(prevvel))
else
acceldt = difsign * startAccel * dt
end
self.state = self.state + difsign * min(min(abs(prevvel + 0.5 * acceldt), ratelimit) * dt, absdif)
self.prevvel = difsign * min(abs(prevvel + acceldt), ratelimit)
return self.state
end
function temporalSigmoidSmoothing:set(sample)
self.state = sample
self.prevvel = 0
end
function temporalSigmoidSmoothing:value()
return self.state
end
--== Exponential/Non Linear temporal==--
local temporalSmoothingNonLinear = {}
temporalSmoothingNonLinear.__index = temporalSmoothingNonLinear
function newTemporalSmoothingNonLinear(inRate, outRate, startingValue)
local rate = inRate or 1
local data = {[false] = rate, [true] = outRate or rate, state = startingValue or 0}
setmetatable(data, temporalSmoothingNonLinear)
return data
end
function temporalSmoothingNonLinear:get(sample, dt)
local st = self.state
local dif = sample - st
local ratedt = self[dif * st >= 0] * dt
st = st + dif * ratedt / (1 + ratedt)
self.state = st
return st
end
function temporalSmoothingNonLinear:getWithRate(sample, dt, rate)
local st = self.state
local ratedt = rate * dt
st = st + (sample - st) * ratedt / (1 + ratedt)
self.state = st
return st
end
function temporalSmoothingNonLinear:set(sample)
self.state = sample
end
function temporalSmoothingNonLinear:value()
return self.state
end
function temporalSmoothingNonLinear:reset()
self.state = 0
end
--== Linear temporal ==--
local temporalSmoothing = {}
temporalSmoothing.__index = temporalSmoothing
function newTemporalSmoothing(inRate, outRate, autoCenterRate, startingValue)
inRate = max(inRate or 1, 1e-307)
startingValue = startingValue or 0
local data = {[false] = inRate, [true] = max(outRate or inRate, 1e-307),
autoCenterRate = max(autoCenterRate or inRate, 1e-307),
_startingValue = startingValue,
state = startingValue}
setmetatable(data, temporalSmoothing)
if data.autoCenterRate ~= inRate then
data.getUncapped = data.getUncappedAutoCenter
end
return data
end
function temporalSmoothing:getUncappedAutoCenter(sample, dt)
local st = self.state
local dif = (sample - st)
local rate
if sample == 0 then
rate = self.autoCenterRate -- autocentering
else
rate = self[dif * st >= 0]
end
st = st + dif * min(rate * dt / abs(dif), 1)
self.state = st
return st
end
function temporalSmoothing:getUncapped(sample, dt) -- no autocenter
local st = self.state
local dif = sample - st
st = st + dif * min(self[dif * st >= 0] * dt / abs(dif), 1)
self.state = st
return st
end
function temporalSmoothing:get(sample, dt)
return max(min(self:getUncapped(sample, dt), 1), -1)
end
function temporalSmoothing:getWithRateUncapped(sample, dt, rate)
local st = self.state
local dif = (sample - st)
st = st + dif * min(rate * dt / (abs(dif) + 1e-307), 1)
self.state = st
return st
end
function temporalSmoothing:getWithRate(sample, dt, rate)
return max(min(self:getWithRateUncapped(sample, dt, rate), 1), -1)
end
function temporalSmoothing:reset()
self.state = self._startingValue
end
function temporalSmoothing:value()
return self.state
end
function temporalSmoothing:set(v)
self.state = v
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment