function Spring.TimeSkip(W,A)local X=W._clock()local Z,p=W:_positionVelocity(X+A)W._position0=Z W._velocity0=p W._time0=X end function Spring.__index(W,A)if Spring[A]then return Spring[A]elseif A=="\086\097\108\117\101"or A=="\080\111\115\105\116\105\111\110"or A=="\112"then local A,X=W:_positionVelocity(W._clock())return A elseif A=="\086\101\108\111\099\105\116\121"or A=="\118"then local A,X=W:_positionVelocity(W._clock())return X elseif A=="\084\097\114\103\101\116"or A=="\116"then return W._target elseif A=="\068\097\109\112\101\114"or A=="\100"then return W._damper elseif A=="\083\112\101\101\100"or A=="\115"then return W._speed elseif A=="\067\108\111\099\107"then return W._clock else error(("\037\113 \105\115 \110\111\116 \097 \118\097\108\105\100 \109\101\109\098\101\114 \111\102 \083\112\114\105\110\103"):format(tostring(A)),2)end end function Spring.__newindex(X,A,W)local Z=X._clock()if A=="\086\097\108\117\101"or A=="\080\111\115\105\116\105\111\110"or A=="\112"then local A,p=X:_positionVelocity(Z)X._position0=W X._velocity0=p X._time0=Z elseif A=="\086\101\108\111\099\105\116\121"or A=="\118"then local A,p=X:_positionVelocity(Z)X._position0=A X._velocity0=W X._time0=Z elseif A=="\084\097\114\103\101\116"or A=="\116"then local A,p=X:_positionVelocity(Z)X._position0=A X._velocity0=p X._target=W X._time0=Z elseif A=="\068\097\109\112\101\114"or A=="\100"then local A,p=X:_positionVelocity(Z)X._position0=A X._velocity0=p X._damper=math.clamp(W,0,1)X._time0=Z elseif A=="\083\112\101\101\100"or A=="\115"then local A,p=X:_positionVelocity(Z)X._position0=A X._velocity0=p X._speed=W<0 and 0 or W X._time0=Z elseif A=="\067\108\111\099\107"then local A,p=X:_positionVelocity(Z)X._position0=A X._velocity0=p X._clock=W X._time0=W()else error(("\037\113 \105\115 \110\111\116 \097 \118\097\108\105\100 \109\101\109\098\101\114 \111\102 \083\112\114\105\110\103"):format(tostring(A)),2)end end function Spring._positionVelocity(W,A)local X=W._position0 local Z=W._velocity0 local p=W._target local u=W._damper local L=W._speed local x=L*(A-W._time0)local R=u*u local e,r,c if R<1 then e=math.sqrt(1-R)local A=math.exp(-u*x)/e c,r=A*math.cos(e*x),A*math.sin(e*x)elseif R==1 then e=1 local A=math.exp(-u*x)/e c,r=A,A*x else e=math.sqrt(R-1)local A=math.exp((-u+e)*x)/(2*e)local W=math.exp((-u-e)*x)/(2*e)c,r=A+W,A-W end local d=e*c+u*r local j=1-(e*c+u*r)local f=r/L local K=-L*r local n=L*r local s=e*c-u*r return(d*X+j*p)+f*Z,(K*X+n*p)+s*Z end return Spring

function Spring:TimeSkip(delta)
	local now = self._clock()
	local position, velocity = self:_positionVelocity(now+delta)
	self._position0 = position
	self._velocity0 = velocity
	self._time0 = now
end

function Spring:__index(index)
	if Spring[index] then
		return Spring[index]
	elseif index == "Value" or index == "Position" or index == "p" then
		local position, _ = self:_positionVelocity(self._clock())
		return position
	elseif index == "Velocity" or index == "v" then
		local _, velocity = self:_positionVelocity(self._clock())
		return velocity
	elseif index == "Target" or index == "t" then
		return self._target
	elseif index == "Damper" or index == "d" then
		return self._damper
	elseif index == "Speed" or index == "s" then
		return self._speed
	elseif index == "Clock" then
		return self._clock
	else
		error(("%q is not a valid member of Spring"):format(tostring(index)), 2)
	end
end

function Spring:__newindex(index, value)
	local now = self._clock()

	if index == "Value" or index == "Position" or index == "p" then
		local _, velocity = self:_positionVelocity(now)
		self._position0 = value
		self._velocity0 = velocity
		self._time0 = now
	elseif index == "Velocity" or index == "v" then
		local position, _ = self:_positionVelocity(now)
		self._position0 = position
		self._velocity0 = value
		self._time0 = now
	elseif index == "Target" or index == "t" then
		local position, velocity = self:_positionVelocity(now)
		self._position0 = position
		self._velocity0 = velocity
		self._target = value
		self._time0 = now
	elseif index == "Damper" or index == "d" then
		local position, velocity = self:_positionVelocity(now)
		self._position0 = position
		self._velocity0 = velocity
		self._damper = math.clamp(value, 0, 1)
		self._time0 = now
	elseif index == "Speed" or index == "s" then
		local position, velocity = self:_positionVelocity(now)
		self._position0 = position
		self._velocity0 = velocity
		self._speed = value < 0 and 0 or value
		self._time0 = now
	elseif index == "Clock" then
		local position, velocity = self:_positionVelocity(now)
		self._position0 = position
		self._velocity0 = velocity
		self._clock = value
		self._time0 = value()
	else
		error(("%q is not a valid member of Spring"):format(tostring(index)), 2)
	end
end

function Spring:_positionVelocity(now)
	local p0 = self._position0
	local v0 = self._velocity0
	local p1 = self._target
	local d = self._damper
	local s = self._speed

	local t = s*(now - self._time0)
	local d2 = d*d

	local h, si, co
	if d2 < 1 then
		h = math.sqrt(1 - d2)
		local ep = math.exp(-d*t)/h
		co, si = ep*math.cos(h*t), ep*math.sin(h*t)
	elseif d2 == 1 then
		h = 1
		local ep = math.exp(-d*t)/h
		co, si = ep, ep*t
	else
		h = math.sqrt(d2 - 1)
		local u = math.exp((-d + h)*t)/(2*h)
		local v = math.exp((-d - h)*t)/(2*h)
		co, si = u + v, u - v
	end

	local a0 = h*co + d*si
	local a1 = 1 - (h*co + d*si)
	local a2 = si/s

	local b0 = -s*si
	local b1 = s*si
	local b2 = h*co - d*si

	return
		a0*p0 + a1*p1 + a2*v0,
		b0*p0 + b1*p1 + b2*v0
end

return Spring
