mirror of
https://github.com/centau/vide.git
synced 2026-08-20 23:01:37 +00:00
This commit is contained in:
parent
96e577a626
commit
bf1b1978d9
8 changed files with 279 additions and 226 deletions
13
src/bind.lua
13
src/bind.lua
|
|
@ -47,7 +47,7 @@ local function traceback() -- ensures trace begins outside of any vide library f
|
|||
return debug.traceback("", s)
|
||||
end
|
||||
|
||||
function setup(instance: Instance, deriver: () -> unknown, setter: (Instance) -> ())
|
||||
function setup(instance: Instance, setter: (Instance) -> ())
|
||||
if flags.strict then
|
||||
local fn = setter
|
||||
local trace = traceback()
|
||||
|
|
@ -70,7 +70,8 @@ function setup(instance: Instance, deriver: () -> unknown, setter: (Instance) ->
|
|||
weak[key] = instance
|
||||
|
||||
local function ref()
|
||||
local _ = nodes -- prevent gc of state while instance exists
|
||||
--todo:local _ = nodes -- prevent gc of state while instance exists
|
||||
local _ = setter
|
||||
local instance = weak[key] :: Instance
|
||||
hold[key] = instance.Parent and instance or nil -- prevent gc of instance while parented
|
||||
end
|
||||
|
|
@ -79,8 +80,10 @@ function setup(instance: Instance, deriver: () -> unknown, setter: (Instance) ->
|
|||
instance:GetPropertyChangedSignal("Parent"):Connect(ref)
|
||||
end
|
||||
|
||||
-- todo: move `fn` as arg?
|
||||
|
||||
local function bind_property(instance: Instance, property: string, fn: () -> unknown)
|
||||
setup(instance, fn, function(instance_weak: any)
|
||||
setup(instance, function(instance_weak: any)
|
||||
instance_weak[property] = fn()
|
||||
end)
|
||||
end
|
||||
|
|
@ -89,7 +92,7 @@ local function bind_parent(instance: Instance, fn: () -> Instance?)
|
|||
instance.Destroying:Connect(function()
|
||||
instance= nil :: any -- allow gc when destroyed
|
||||
end)
|
||||
setup(instance, fn, function(instance)
|
||||
setup(instance, function(instance)
|
||||
local _ = instance -- state will strongly reference instance when parent is bound
|
||||
instance.Parent = fn()
|
||||
end)
|
||||
|
|
@ -99,7 +102,7 @@ local function bind_children(parent: Instance, fn: () -> { Instance })
|
|||
local currentChildrenSet: { [Instance]: true } = {} -- cache of all children parented before update
|
||||
local newChildrenSet: { [Instance]: true } = {} -- cache of all children parented after update
|
||||
|
||||
setup(parent, fn, function(parent_weak)
|
||||
setup(parent, function(parent_weak)
|
||||
local newChildren = fn() -- all (and only) children that should be parented after this update
|
||||
if newChildren and type(newChildren) ~= "table" then
|
||||
throw(`Cannot parent instance of type { type(newChildren) } `)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
if not game then script = (require :: any) "test/wrap-require" end
|
||||
|
||||
local create = require(script.create)
|
||||
local wrap = require(script.wrap)
|
||||
local source = require(script.source)
|
||||
local watch = require(script.watch)
|
||||
local derive = require(script.derive)
|
||||
local map = require(script.map)
|
||||
|
|
@ -14,18 +14,17 @@ local map = require(script.map)
|
|||
-- local Changed = require(script.Change)
|
||||
-- local Created = require(script.Created)
|
||||
|
||||
local spring, updateSprings = require(script.spring)()
|
||||
local spring, update_springs = require(script.spring)()
|
||||
|
||||
local flags = require(script.flags)
|
||||
|
||||
type Map<K, V> = { [K]: V }
|
||||
type Unwrapper = <T>(T) -> T
|
||||
type Setter<T> = ( (new: T, force: true?) -> T ) & ( (update: (old: T) -> T, force: true?) -> T )
|
||||
|
||||
local vide = {
|
||||
-- core
|
||||
create = create,
|
||||
wrap = wrap,
|
||||
source = source,
|
||||
derive = derive,
|
||||
map = map,
|
||||
watch = watch,
|
||||
|
|
@ -45,7 +44,7 @@ local vide = {
|
|||
|
||||
-- test
|
||||
step = function(dt: number)
|
||||
updateSprings(dt)
|
||||
update_springs(dt)
|
||||
end
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ local set = graph.set
|
|||
|
||||
type State<T> = (() -> T) & ((T) -> T)
|
||||
|
||||
local function wrap<T>(value: T | () -> T): State<T>
|
||||
local function source<T>(value: T | () -> T): State<T>
|
||||
if type(value) == "function" then value = value() end
|
||||
|
||||
local node = create(value :: T)
|
||||
|
|
@ -29,4 +29,4 @@ local function wrap<T>(value: T | () -> T): State<T>
|
|||
end
|
||||
end
|
||||
|
||||
return wrap
|
||||
return source
|
||||
152
src/spring.lua
152
src/spring.lua
|
|
@ -29,18 +29,20 @@ local create = graph.create
|
|||
local get = graph.get
|
||||
local set = graph.set
|
||||
|
||||
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: number;
|
||||
Velocity: number;
|
||||
InitialVelocity: number;
|
||||
Initial: T;
|
||||
Target: T;
|
||||
Input: State<T>;
|
||||
alpha: number,
|
||||
duration: number,
|
||||
period: number,
|
||||
damping_ratio: number,
|
||||
velocity: number,
|
||||
initial_velocity: number,
|
||||
initial_position: T,
|
||||
target_position: T,
|
||||
target: () -> T
|
||||
}
|
||||
|
||||
type Lerp<T> = (initial: T, target: T, alpha: number) -> T
|
||||
|
|
@ -88,78 +90,76 @@ local lerpable: { [string]: Lerp<any> } = {
|
|||
end :: Lerp<Vector3>,
|
||||
}
|
||||
|
||||
local activeSprings: { [State<any>]: SpringData<any> } = {}
|
||||
setmetatable(activeSprings, { __mode = "ks" })
|
||||
local springs: { [SpringData<any>]: Node<any> } = {}
|
||||
setmetatable(springs, { __mode = "vs" })
|
||||
|
||||
local function spring<T>(input: MaybeState<T>, period: number, damping: number?): State<T>
|
||||
local initial = unwrap(input) :: T
|
||||
local output = create(initial)
|
||||
local function spring<T>(target: () -> T, period: number, damping_ratio: number?): () -> T
|
||||
local initial_position = target()
|
||||
local node = create(initial_position)
|
||||
|
||||
local springData: SpringData<T> = {
|
||||
Alpha = 0,
|
||||
Duration = 0,
|
||||
Period = period,
|
||||
Damping = damping or 1,
|
||||
Velocity = 0,
|
||||
InitialVelocity = 0,
|
||||
Initial = initial,
|
||||
Target = initial,
|
||||
Input = input :: any,
|
||||
LastInput = initial,
|
||||
LastOutput = initial
|
||||
local data: SpringData<T> = {
|
||||
alpha = 0,
|
||||
duration = 0,
|
||||
period = period,
|
||||
damping_ratio = damping_ratio or 1,
|
||||
velocity = 0,
|
||||
initial_velocity = 0,
|
||||
initial_position = initial_position,
|
||||
target_position = initial_position,
|
||||
target = target
|
||||
}
|
||||
|
||||
activeSprings[output] = springData
|
||||
springs[data] = node
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
local function updateSprings(dt: number)
|
||||
for node, data in next, activeSprings do
|
||||
local currentTarget = get(data.Input)
|
||||
if currentTarget ~= data.Target then
|
||||
data.Target = currentTarget
|
||||
data.Initial = get(node)
|
||||
data.Target = get(data.Input)
|
||||
data.Alpha = 0
|
||||
data.Duration = 0
|
||||
data.InitialVelocity = data.Velocity
|
||||
end
|
||||
|
||||
local initial: Animatable = data.Initial
|
||||
local target: Animatable = data.Target
|
||||
local targetType: string = typeof(target)
|
||||
|
||||
if targetType ~= typeof(initial) then
|
||||
activeSprings[node] = nil
|
||||
warn(string.format(
|
||||
"Mismatched state value types, cancelling state update (initial value: %s, target value: %s)",
|
||||
typeof(initial),
|
||||
targetType
|
||||
))
|
||||
throw(`Cannot tween type { typeof(initial) } and { targetType }`)
|
||||
continue
|
||||
end
|
||||
|
||||
local lerp: Lerp<Animatable> = lerpable[targetType]
|
||||
|
||||
if lerp == nil then
|
||||
activeSprings[node] = nil
|
||||
throw(`Cannot animate type { targetType }`)
|
||||
continue
|
||||
end
|
||||
|
||||
local newTime = data.Duration + dt
|
||||
local newAlpha = solve(data.Period, data.Damping, data.InitialVelocity, newTime)
|
||||
|
||||
data.Velocity = -(newAlpha - data.Alpha)/dt
|
||||
data.Alpha = newAlpha
|
||||
data.Duration = newTime
|
||||
|
||||
local value = lerp(initial, target, newAlpha)
|
||||
|
||||
set(node, value)
|
||||
return function()
|
||||
return get(node)
|
||||
end
|
||||
end
|
||||
|
||||
return function() return spring, updateSprings end
|
||||
local function update_springs(dt: number)
|
||||
for data, output in next, springs do
|
||||
if data.target() ~= data.target_position then
|
||||
data.target = data.target()
|
||||
data.initial_position = get(output)
|
||||
data.alpha = 0
|
||||
data.duration = 0
|
||||
data.initial_velocity = data.velocity
|
||||
end
|
||||
|
||||
local initial_position: Animatable = data.initial_position
|
||||
local target_position: Animatable = data.target_position
|
||||
local target_type: string = 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)
|
||||
|
||||
data.velocity = -(new_alpha - data.alpha)/dt
|
||||
data.alpha = new_alpha
|
||||
data.duration = new_time
|
||||
|
||||
local value = lerp(initial_position, target_position, new_alpha)
|
||||
|
||||
set(output, value)
|
||||
end
|
||||
end
|
||||
|
||||
return function() return spring, update_springs end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue