--[[
	Updates spring.position, moving it closer to target, depending on deltaTime.
]]

function Spring:update(deltaTime)
	local differenceVector = self.position - self.target
	if self.zeta > 1 + EPSILON then
		-- Over damped
		local zetaA = -self.omega * self.zeta
		local zetaB = self.omega * (self.zeta * self.zeta - 1) ^ 0.5
		local zeta0, zeta1 = zetaA - zetaB, zetaA + zetaB
		local expt0, expt1 = math.exp(zeta0 * deltaTime), math.exp(zeta1 * deltaTime)
		local c1 = (self.velocity - differenceVector * zeta1) / (-2 * zetaB)
		local c2 = differenceVector - c1
		self.position = self.target + c1 * expt0 + c2 * expt1
		self.velocity = c1 * zeta1 * expt0 + c2 * zeta1 * expt1
	elseif self.zeta > 1 - EPSILON then
		-- Critically damped
		local expt = math.exp(-self.omega * deltaTime)
		local c1 = self.velocity + self.omega * differenceVector
		local c2 = (c1 * deltaTime + differenceVector) * expt
		self.position = self.target + c2
		self.velocity = (c1 * expt) - (c2 * self.omega)
	else
		-- Under damped
		local omegaZeta = self.omega * self.zeta
		local alpha = self.omega * (1 - self.zeta * self.zeta) ^ 0.5
		local exp = math.exp(-deltaTime * omegaZeta)
		local cos = math.cos(deltaTime * alpha)
		local sin = math.sin(deltaTime * alpha)
		local c2 = (self.velocity + differenceVector * omegaZeta) / alpha

		self.position = self.target + exp * (differenceVector * cos + c2 * sin)
		self.velocity =
			-exp * ((differenceVector * omegaZeta - c2 * alpha) * cos + (differenceVector * alpha + c2 * omegaZeta) * sin)
	end
end

return Spring
