mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Initial commit
This commit is contained in:
commit
cb002f4f27
50 changed files with 4666 additions and 0 deletions
195
src/spring.luau
Normal file
195
src/spring.luau
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
if not game then script = (require :: any) "test/wrap-require" end
|
||||
|
||||
--[[
|
||||
|
||||
Supported datatypes:
|
||||
- number
|
||||
- CFrame
|
||||
- Color3
|
||||
- UDim
|
||||
- UDim2
|
||||
- Vector2
|
||||
- Vector3
|
||||
|
||||
Unsupported datatypes:
|
||||
- bool
|
||||
- Rect
|
||||
- Vector2int16
|
||||
- Vector3int16
|
||||
- EnumItem
|
||||
|
||||
]]
|
||||
|
||||
local throw = require(script.Parent.throw)
|
||||
|
||||
local graph = require(script.Parent.graph)
|
||||
local create = graph.create
|
||||
local get = graph.get
|
||||
local set = graph.set
|
||||
local set_effect = graph.set_effect
|
||||
local capture = graph.capture
|
||||
|
||||
type Node<T> = graph.Node<T>
|
||||
|
||||
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3
|
||||
|
||||
type SpringData<T> = {
|
||||
alpha: number,
|
||||
duration: number,
|
||||
period: number,
|
||||
damping_ratio: number,
|
||||
|
||||
velocity: number,
|
||||
initial_velocity: number,
|
||||
initial_position: T,
|
||||
target_position: T,
|
||||
|
||||
target_updated: boolean,
|
||||
target: () -> T
|
||||
}
|
||||
|
||||
type Lerp<T> = (initial: T, target: T, alpha: number) -> T
|
||||
|
||||
local function solve(T: number, z: number, u: number, t: number): number -- alpha
|
||||
local wn = 2*math.pi / T
|
||||
local wd = wn * math.sqrt(1 - z^2)
|
||||
local a = z * wn
|
||||
|
||||
local s = math.exp(-a*t) * math.cos(wd*t)
|
||||
local v = (u/wn) * math.exp(-a*t) * math.sin(wn*t)
|
||||
return (1-s) + v
|
||||
end
|
||||
|
||||
local lerpable: { [string]: Lerp<any> } = {
|
||||
number = function(v1, v2, a)
|
||||
return v1 + (v2 - v1)*a
|
||||
end :: Lerp<number>,
|
||||
|
||||
CFrame = function(v1, v2, a)
|
||||
return v1:Lerp(v2, a)
|
||||
end :: Lerp<CFrame>,
|
||||
|
||||
Color3 = function(v1, v2, a)
|
||||
return v1:Lerp(v2, a)
|
||||
end :: Lerp<Color3>,
|
||||
|
||||
UDim = function(v1, v2, a)
|
||||
return UDim.new(
|
||||
v1.Scale + (v2.Scale - v1.Scale)*a,
|
||||
v1.Offset + (v2.Offset - v1.Offset)*a
|
||||
)
|
||||
end :: Lerp<UDim>,
|
||||
|
||||
UDim2 = function(v1, v2, a)
|
||||
return v1:Lerp(v2, a)
|
||||
end :: Lerp<UDim2>,
|
||||
|
||||
Vector2 = function(v1, v2, a)
|
||||
return v1:Lerp(v2, a)
|
||||
end :: Lerp<Vector2>,
|
||||
|
||||
Vector3 = function(v1, v2, a)
|
||||
return v1:Lerp(v2, a)
|
||||
end :: Lerp<Vector3>,
|
||||
}
|
||||
|
||||
local springs: { [SpringData<any>]: Node<any> } = {}
|
||||
setmetatable(springs, { __mode = "vs" })
|
||||
|
||||
local function spring<T>(target: () -> T, period: number?, damping_ratio: number?): () -> T
|
||||
local inputs, initial_position = capture(target)
|
||||
|
||||
local output, output_get = create(initial_position)
|
||||
|
||||
local data: SpringData<T> = {
|
||||
alpha = 0,
|
||||
duration = 0,
|
||||
period = period or 1,
|
||||
damping_ratio = damping_ratio or 1,
|
||||
velocity = 0,
|
||||
initial_velocity = 0,
|
||||
initial_position = initial_position,
|
||||
target_position = initial_position,
|
||||
target_updated = false,
|
||||
target = target
|
||||
}
|
||||
|
||||
local function input_changed(node)
|
||||
data.target_updated = true
|
||||
data.target_position = target()
|
||||
springs[data] = node
|
||||
end
|
||||
|
||||
for _, input in next, inputs do
|
||||
set_effect(input, input_changed, output)
|
||||
end
|
||||
|
||||
springs[data] = output
|
||||
|
||||
return output_get
|
||||
end
|
||||
|
||||
local remove_queue = {}
|
||||
|
||||
local function update_springs(dt: number)
|
||||
for data, output in next, springs do
|
||||
if data.target_updated then
|
||||
data.target_updated = false
|
||||
data.target_position = data.target()
|
||||
data.initial_position = get(output)
|
||||
data.alpha = 0
|
||||
data.duration = 0
|
||||
data.initial_velocity = data.velocity
|
||||
end
|
||||
|
||||
local initial_position = data.initial_position
|
||||
local target_position = data.target_position
|
||||
local target_type = typeof(target_position)
|
||||
|
||||
if target_type ~= typeof(initial_position) then
|
||||
springs[data] = nil
|
||||
warn(string.format(
|
||||
"Mismatched state value types, cancelling state update (initial value: %s, target value: %s)",
|
||||
typeof(initial_position),
|
||||
target_type
|
||||
))
|
||||
throw(`Cannot tween type { typeof(initial_position) } and { target_type }`)
|
||||
continue
|
||||
end
|
||||
|
||||
local lerp: Lerp<Animatable> = lerpable[target_type]
|
||||
|
||||
if lerp == nil then
|
||||
springs[data] = nil
|
||||
throw(`Cannot animate type { target_type }`)
|
||||
continue
|
||||
end
|
||||
|
||||
local new_time = data.duration + dt
|
||||
local new_alpha = solve(data.period, data.damping_ratio, data.initial_velocity, new_time)
|
||||
local new_velocity = -(new_alpha - data.alpha)/dt
|
||||
|
||||
local acceleration = (new_velocity - data.velocity)/dt
|
||||
|
||||
data.velocity = new_velocity
|
||||
data.alpha = new_alpha
|
||||
data.duration = new_time
|
||||
|
||||
local value = lerp(initial_position, target_position, new_alpha)
|
||||
|
||||
if math.abs(acceleration) < 0.01 then
|
||||
table.insert(remove_queue, data)
|
||||
set(output, target_position)
|
||||
else
|
||||
set(output, value)
|
||||
end
|
||||
end
|
||||
|
||||
for _, data in next, remove_queue do
|
||||
springs[data] = nil
|
||||
end
|
||||
|
||||
table.clear(remove_queue)
|
||||
end
|
||||
|
||||
return function() return spring, update_springs end
|
||||
Loading…
Add table
Add a link
Reference in a new issue