2013/Libraries/Fusion/Animation/springCoefficients.luau

87 lines
2.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--!strict
--[[
Returns a 2x2 matrix of coefficients for a given time, damping and speed.
Specifically, this returns four coefficients - posPos, posVel, velPos, and
velVel - which can be multiplied with position and velocity like so:
local newPosition = oldPosition * posPos + oldVelocity * posVel
local newVelocity = oldPosition * velPos + oldVelocity * velVel
Special thanks to AxisAngle for helping to improve numerical precision.
]]
local function springCoefficients(
time: number,
damping: number,
speed: number
): (number, number, number, number)
-- if time or speed is 0, then the spring won't move
if time == 0 or speed == 0 then
return 1, 0, 0, 1
end
local posPos, posVel, velPos, velVel
if damping > 1 then
-- overdamped spring
-- solution to the characteristic equation:
-- z = -ζω ± Sqrt[ζ^2 - 1] ω
-- x[t] -> x0(e^(t z2) z1 - e^(t z1) z2)/(z1 - z2)
-- + v0(e^(t z1) - e^(t z2))/(z1 - z2)
-- v[t] -> x0(z1 z2(-e^(t z1) + e^(t z2)))/(z1 - z2)
-- + v0(z1 e^(t z1) - z2 e^(t z2))/(z1 - z2)
local scaledTime = time * speed
local alpha = math.sqrt(damping ^ 2 - 1)
local scaledInvAlpha = -0.5 / alpha
local z1 = -alpha - damping
local z2 = 1 / z1
local expZ1 = math.exp(scaledTime * z1)
local expZ2 = math.exp(scaledTime * z2)
posPos = (expZ2 * z1 - expZ1 * z2) * scaledInvAlpha
posVel = (expZ1 - expZ2) * scaledInvAlpha / speed
velPos = (expZ2 - expZ1) * scaledInvAlpha * speed
velVel = (expZ1 * z1 - expZ2 * z2) * scaledInvAlpha
elseif damping == 1 then
-- critically damped spring
-- x[t] -> x0(e^-tω)(1+tω) + v0(e^-tω)t
-- v[t] -> x0(t ω^2)(-e^-tω) + v0(1 - tω)(e^-tω)
local scaledTime = time * speed
local expTerm = math.exp(-scaledTime)
posPos = expTerm * (1 + scaledTime)
posVel = expTerm * time
velPos = expTerm * (-scaledTime * speed)
velVel = expTerm * (1 - scaledTime)
else
-- underdamped spring
-- factored out of the solutions to the characteristic equation:
-- α = Sqrt[1 - ζ^2]
-- x[t] -> x0(e^-tζω)(α Cos[tα] + ζω Sin[tα])/α
-- + v0(e^-tζω)(Sin[tα])/α
-- v[t] -> x0(-e^-tζω)(α^2 + ζ^2 ω^2)(Sin[tα])/α
-- + v0(e^-tζω)(α Cos[tα] - ζω Sin[tα])/α
local scaledTime = time * speed
local alpha = math.sqrt(1 - damping ^ 2)
local invAlpha = 1 / alpha
local alphaTime = alpha * scaledTime
local expTerm = math.exp(-scaledTime * damping)
local sinTerm = expTerm * math.sin(alphaTime)
local cosTerm = expTerm * math.cos(alphaTime)
local sinInvAlpha = sinTerm * invAlpha
local sinInvAlphaDamp = sinInvAlpha * damping
posPos = sinInvAlphaDamp + cosTerm
posVel = sinInvAlpha
velPos = -(sinInvAlphaDamp * damping + sinTerm * alpha)
velVel = cosTerm - sinInvAlphaDamp
end
return posPos, posVel, velPos, velVel
end
return springCoefficients