local g={}function g.new(C)local Q=setmetatable({},g)local D=C or 0 rawset(Q,"\095\116\105\109\101\048",tick())rawset(Q,"\095\112\111\115\105\116\105\111\110\048",D)rawset(Q,"\095\118\101\108\111\099\105\116\121\048",0*D)rawset(Q,"\095\116\097\114\103\101\116",D)rawset(Q,"\095\100\097\109\112\101\114",1)rawset(Q,"\095\115\112\101\101\100",1)return Q end function g.Impulse(C,g)C.Velocity=C.Velocity+g end function g.TimeSkip(C,g)local Q=tick()local D,q=C:_positionVelocity(Q+g)rawset(C,"\095\112\111\115\105\116\105\111\110\048",D)rawset(C,"\095\118\101\108\111\099\105\116\121\048",q)rawset(C,"\095\116\105\109\101\048",Q)end function g.__index(Q,C)if g[C]then return g[C]elseif C=="\086\097\108\117\101"or C=="\080\111\115\105\116\105\111\110"or C=="\112"then local g,C=Q:_positionVelocity(tick())return g elseif C=="\086\101\108\111\099\105\116\121"or C=="\118"then local g,C=Q:_positionVelocity(tick())return C elseif C=="\084\097\114\103\101\116"or C=="\116"then return rawget(Q,"\095\116\097\114\103\101\116")elseif C=="\068\097\109\112\101\114"or C=="\100"then return rawget(Q,"\095\100\097\109\112\101\114")elseif C=="\083\112\101\101\100"or C=="\115"then return rawget(Q,"\095\115\112\101\101\100")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(C)),2)end end function g.__newindex(Q,g,C)local D=tick()if g=="\086\097\108\117\101"or g=="\080\111\115\105\116\105\111\110"or g=="\112"then local g,q=Q:_positionVelocity(D)rawset(Q,"\095\112\111\115\105\116\105\111\110\048",C)rawset(Q,"\095\118\101\108\111\099\105\116\121\048",q)elseif g=="\086\101\108\111\099\105\116\121"or g=="\118"then local g,q=Q:_positionVelocity(D)rawset(Q,"\095\112\111\115\105\116\105\111\110\048",g)rawset(Q,"\095\118\101\108\111\099\105\116\121\048",C)elseif g=="\084\097\114\103\101\116"or g=="\116"then local g,q=Q:_positionVelocity(D)rawset(Q,"\095\112\111\115\105\116\105\111\110\048",g)rawset(Q,"\095\118\101\108\111\099\105\116\121\048",q)rawset(Q,"\095\116\097\114\103\101\116",C)elseif g=="\068\097\109\112\101\114"or g=="\100"then local g,q=Q:_positionVelocity(D)rawset(Q,"\095\112\111\115\105\116\105\111\110\048",g)rawset(Q,"\095\118\101\108\111\099\105\116\121\048",q)rawset(Q,"\095\100\097\109\112\101\114",math.clamp(C,0,1))elseif g=="\083\112\101\101\100"or g=="\115"then local g,q=Q:_positionVelocity(D)rawset(Q,"\095\112\111\115\105\116\105\111\110\048",g)rawset(Q,"\095\118\101\108\111\099\105\116\121\048",q)rawset(Q,"\095\115\112\101\101\100",C<0 and 0 or C)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(g)),2)end rawset(Q,"\095\116\105\109\101\048",D)end function g._positionVelocity(C,g)local Q=g-rawget(C,"\095\116\105\109\101\048")local D=rawget(C,"\095\112\111\115\105\116\105\111\110\048")local q=rawget(C,"\095\118\101\108\111\099\105\116\121\048")local S=rawget(C,"\095\116\097\114\103\101\116")local W=rawget(C,"\095\100\097\109\112\101\114")local z=rawget(C,"\095\115\112\101\101\100")local X=D-S if z==0 then return D,0 elseif W<1 then local g=(1-W*W)^.5 local C=(q/z+W*X)/g local D=math.cos((g*z)*Q)local t=math.sin((g*z)*Q)local R=2.718281828459^((W*z)*Q)return S+(X*D+C*t)/R,(z*((g*C-W*X)*D-(g*X+W*C)*t))/R else local g=q/z+X local C=2.718281828459^(z*Q)return S+(X+(g*z)*Q)/C,(z*((g-X)-(g*z)*Q))/C end end return g
--[[
class Spring

Description:
	A physical model of a spring, useful in many applications. Properties only evaluate
	upon index making this model good for lazy applications

API:
	Spring = Spring.new(number position)
		Creates a new spring in 1D
	Spring = Spring.new(Vector3 position)
		Creates a new spring in 3D

	Spring.Position
		Returns the current position
	Spring.Velocity
		Returns the current velocity
	Spring.Target
		Returns the target
	Spring.Damper
		Returns the damper
	Spring.Speed
		Returns the speed

	Spring.Target = number/Vector3
		Sets the target
	Spring.Position = number/Vector3
		Sets the position
	Spring.Velocity = number/Vector3
		Sets the velocity
	Spring.Damper = number [0, 1]
		Sets the spring damper, defaults to 1
	Spring.Speed = number [0, infinity)
		Sets the spring speed, defaults to 1

	Spring:TimeSkip(number DeltaTime)
		Instantly skips the spring forwards by that amount of time
	Spring:Impulse(number/Vector3 velocity)
		Impulses the spring, increasing velocity by the amount given
]]


local Spring = {}

--- Creates a new spring
-- @param initial A number or Vector3 (anything with * number and addition/subtraction defined)
function Spring.new(initial)
	local self = setmetatable({}, Spring)

	local target = initial or 0
	rawset(self, "_time0", tick())
	rawset(self, "_position0", target)
	rawset(self, "_velocity0", 0*target)
	rawset(self, "_target", target)
	rawset(self, "_damper", 1)
	rawset(self, "_speed", 1)

	return self
end

--- Impulse the spring with a change in velocity
-- @param velocity The velocity to impulse with
function Spring:Impulse(velocity)
	self.Velocity = self.Velocity + velocity
end

--- Skip forwards in time
-- @param delta Time to skip forwards
function Spring:TimeSkip(delta)
	local time = tick()
	local position, velocity = self:_positionVelocity(time+delta)
	rawset(self, "_position0", position)
	rawset(self, "_velocity0", velocity)
	rawset(self, "_time0", time)
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(tick())
		return position
	elseif index == "Velocity" or index == "v" then
		local _, velocity = self:_positionVelocity(tick())
		return velocity
	elseif index == "Target" or index == "t" then
		return rawget(self, "_target")
	elseif index == "Damper" or index == "d" then
		return rawget(self, "_damper")
	elseif index == "Speed" or index == "s" then
		return rawget(self, "_speed")
	else
		error(("%q is not a valid member of Spring"):format(tostring(index)), 2)
	end
end

function Spring:__newindex(index, value)
	local time = tick()

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

	rawset(self, "_time0", time)
end

function Spring:_positionVelocity(time)
	local dt = time - rawget(self, "_time0")
	local p0 = rawget(self, "_position0")
	local v0 = rawget(self, "_velocity0")
	local t = rawget(self, "_target")
	local d = rawget(self, "_damper")
	local s = rawget(self, "_speed")

	local c0 = p0-t
	if s == 0 then
		return p0, 0
	elseif d<1 then
		local c	 = (1-d*d)^0.5
		local c1 = (v0/s+d*c0)/c
		local co = math.cos(c*s*dt)
		local si = math.sin(c*s*dt)
		local e  = 2.718281828459045^(d*s*dt)
		return t+(c0*co+c1*si)/e,
		       s*((c*c1-d*c0)*co-(c*c0+d*c1)*si)/e
	else
		local c1 = v0/s+c0
		local e  = 2.718281828459045^(s*dt)
		return t+(c0+c1*s*dt)/e,
		       s*(c1-c0-c1*s*dt)/e
	end
end

return Spring
