function Spring.TimeSkip(k,K)local W=k._clock()local A,d=k:_positionVelocity(W+K)k._position0=A k._velocity0=d k._time0=W end function Spring.__index(k,K)if Spring[K]then return Spring[K]elseif K=="\086\097\108\117\101"or K=="\080\111\115\105\116\105\111\110"or K=="\112"then local K,W=k:_positionVelocity(k._clock())return K elseif K=="\086\101\108\111\099\105\116\121"or K=="\118"then local K,W=k:_positionVelocity(k._clock())return W elseif K=="\084\097\114\103\101\116"or K=="\116"then return k._target elseif K=="\068\097\109\112\101\114"or K=="\100"then return k._damper elseif K=="\083\112\101\101\100"or K=="\115"then return k._speed elseif K=="\067\108\111\099\107"then return k._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(K)),2)end end function Spring.__newindex(W,K,k)local A=W._clock()if K=="\086\097\108\117\101"or K=="\080\111\115\105\116\105\111\110"or K=="\112"then local K,d=W:_positionVelocity(A)W._position0=k W._velocity0=d W._time0=A elseif K=="\086\101\108\111\099\105\116\121"or K=="\118"then local K,d=W:_positionVelocity(A)W._position0=K W._velocity0=k W._time0=A elseif K=="\084\097\114\103\101\116"or K=="\116"then local K,d=W:_positionVelocity(A)W._position0=K W._velocity0=d W._target=k W._time0=A elseif K=="\068\097\109\112\101\114"or K=="\100"then local K,d=W:_positionVelocity(A)W._position0=K W._velocity0=d W._damper=math.clamp(k,0,1)W._time0=A elseif K=="\083\112\101\101\100"or K=="\115"then local K,d=W:_positionVelocity(A)W._position0=K W._velocity0=d W._speed=k<0 and 0 or k W._time0=A elseif K=="\067\108\111\099\107"then local K,d=W:_positionVelocity(A)W._position0=K W._velocity0=d W._clock=k W._time0=k()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(K)),2)end end function Spring._positionVelocity(k,K)local W=k._position0 local A=k._velocity0 local d=k._target local T=k._damper local L=k._speed local U=L*(K-k._time0)local E=T*T local J,Y,z if E<1 then J=math.sqrt(1-E)local K=math.exp(-T*U)/J z,Y=K*math.cos(J*U),K*math.sin(J*U)elseif E==1 then J=1 local K=math.exp(-T*U)/J z,Y=K,K*U else J=math.sqrt(E-1)local K=math.exp((-T+J)*U)/(2*J)local k=math.exp((-T-J)*U)/(2*J)z,Y=K+k,K-k end local a=J*z+T*Y local e=1-(J*z+T*Y)local p=Y/L local O=-L*Y local P=L*Y local B=J*z-T*Y return(a*W+e*d)+p*A,(O*W+P*d)+B*A end return Spring
--- Skip forwards in now
-- @param delta now to skip forwards
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
