local u={}local m={__index=u}function u.new(u,x,d,c,I)local g={}g.p=u g.v=x g.a=0 g.target=d g.angularFrequency=c or.1 g.dampingRatio=I or.1 return setmetatable(g,m)end function u.update(m,u)local x=m.angularFrequency local d=m.dampingRatio if x<EPSILON then return end if d<0 then d=0 end local c=m.target local I=m.p-c local g=m.v if d>1+EPSILON then local A=-x*d local h=x*sqrt(d*d-1)local b=A-h local R=A+h local n=exp(b*u)local y=exp(R*u)local s=(g-I*R)/(-2*h)local C=I-s m.p=(c+s*n)+C*y m.v=(s*b)*n+(C*R)*y elseif d>1-EPSILON then local d=exp(-x*u)local A=g+x*I local h=I local b=(A*u+h)*d m.p=c+b m.v=A*d-b*x else local A=x*d local h=x*sqrt(1-d*d)local b=exp(-A*u)local R=cos(h*u)local n=sin(h*u)local y=I local s=(g+A*I)/h m.p=c+b*(y*R+s*n)m.v=-b*((y*A-s*h)*R+(y*h+s*A)*n)end end return u
-- class

local spring = {}
local spring_mt = { __index = spring }

function spring.new(p0, v0, target, angularFrequency, dampingRatio)
	local self = {}
	self.p = p0
	self.v = v0
	self.a = 0
	self.target = target
	self.angularFrequency = angularFrequency or 0.1
	self.dampingRatio = dampingRatio or 0.1
	return setmetatable(self, spring_mt)
end

function spring:update(dt)
	local aF = self.angularFrequency
	local dR = self.dampingRatio

	if aF < EPSILON then
		return
	end
	if dR < 0 then
		dR = 0
	end

	local epos = self.target
	local dpos = self.p - epos
	local dvel = self.v

	if dR > 1 + EPSILON then
		local za = -aF * dR
		local zb = aF * sqrt(dR * dR - 1)
		local z1 = za - zb
		local z2 = za + zb
		local expTerm1 = exp(z1 * dt)
		local expTerm2 = exp(z2 * dt)

		local c1 = (dvel - dpos * z2) / (-2 * zb)
		local c2 = dpos - c1
		self.p = epos + c1 * expTerm1 + c2 * expTerm2
		self.v = c1 * z1 * expTerm1 + c2 * z2 * expTerm2
	elseif dR > 1 - EPSILON then
		-- critical
		local expTerm = exp(-aF * dt)

		local c1 = dvel + aF * dpos
		local c2 = dpos
		local c3 = (c1 * dt + c2) * expTerm

		self.p = epos + c3
		self.v = (c1 * expTerm) - (c3 * aF)
	else
		local omegaZeta = aF * dR
		local alpha = aF * sqrt(1 - dR * dR)
		local expTerm = exp(-omegaZeta * dt)
		local cosTerm = cos(alpha * dt)
		local sinTerm = sin(alpha * dt)

		local c1 = dpos
		local c2 = (dvel + omegaZeta * dpos) / alpha
		self.p = epos + expTerm * (c1 * cosTerm + c2 * sinTerm)
		self.v = -expTerm * ((c1 * omegaZeta - c2 * alpha) * cosTerm + (c1 * alpha + c2 * omegaZeta) * sinTerm)
	end
end

return spring
