mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Merge reactive scope refactor
This commit is contained in:
parent
0e439f084f
commit
efc4798ddb
48 changed files with 2750 additions and 1949 deletions
|
|
@ -24,10 +24,14 @@ Unsupported datatypes:
|
|||
local throw = require(script.Parent.throw)
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create = graph.create
|
||||
local set = graph.set
|
||||
local set_effect = graph.set_effect
|
||||
local capture = graph.capture
|
||||
type StartNode<T> = graph.StartNode<T>
|
||||
local create_node = graph.create_node
|
||||
local create_start_node = graph.create_start_node
|
||||
local get_scope = graph.get_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local update = graph.update
|
||||
local set_owner = graph.set_owner
|
||||
local track = graph.track
|
||||
|
||||
local UPDATE_RATE = 120
|
||||
local TOLERANCE = 0.0001
|
||||
|
|
@ -38,6 +42,8 @@ local function Vec3(x: number?, y: number?, z: number?)
|
|||
return Vector3.new(x, y, z)
|
||||
end
|
||||
|
||||
local ZERO = Vec3(0, 0, 0)
|
||||
|
||||
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3
|
||||
|
||||
type SpringData<T> = {
|
||||
|
|
@ -62,21 +68,20 @@ type Vec6ToType<T> = (Vec3, Vec3) -> T
|
|||
|
||||
local type_to_vec6 = {
|
||||
number = function(v)
|
||||
return Vec3(v, 0, 0), Vec3()
|
||||
return Vec3(v, 0, 0), ZERO
|
||||
end :: TypeToVec6<number>,
|
||||
|
||||
CFrame = function(v)
|
||||
-- todo: proper rotation tween
|
||||
return v.Position, Vec3(v:ToEulerAnglesXYZ())
|
||||
end :: TypeToVec6<CFrame>,
|
||||
|
||||
Color3 = function(v)
|
||||
-- todo: hsv
|
||||
return Vec3(v.R, v.G, v.B), Vec3()
|
||||
-- todo: hsv, oklab?
|
||||
return Vec3(v.R, v.G, v.B), ZERO
|
||||
end :: TypeToVec6<Color3>,
|
||||
|
||||
UDim = function(v)
|
||||
return Vec3(v.Scale, v.Offset, 0), Vec3()
|
||||
return Vec3(v.Scale, v.Offset, 0), ZERO
|
||||
end :: TypeToVec6<UDim>,
|
||||
|
||||
UDim2 = function(v)
|
||||
|
|
@ -84,11 +89,11 @@ local type_to_vec6 = {
|
|||
end :: TypeToVec6<UDim2>,
|
||||
|
||||
Vector2 = function(v)
|
||||
return Vec3(v.X, v.Y, 0), Vec3()
|
||||
return Vec3(v.X, v.Y, 0), ZERO
|
||||
end :: TypeToVec6<Vector2>,
|
||||
|
||||
Vector3 = function(v)
|
||||
return v, Vec3()
|
||||
return v, ZERO
|
||||
end :: TypeToVec6<Vector3>,
|
||||
|
||||
Rect = function(v)
|
||||
|
|
@ -141,19 +146,16 @@ setmetatable(vec6_to_type, invalid_type)
|
|||
|
||||
-- maps spring data to its corresponding output node
|
||||
-- lifetime of spring data is tied to output node
|
||||
local springs: { [SpringData<any>]: Node<any> } = {}
|
||||
local springs: { [SpringData<any>]: StartNode<any> } = {}
|
||||
setmetatable(springs, { __mode = "v" })
|
||||
|
||||
local function spring<T>(source: () -> T, period: number?, damping_ratio: number?): () -> T
|
||||
local inputs, initial_value = capture(source)
|
||||
local output, output_get = create(initial_value)
|
||||
|
||||
local vtype = typeof(initial_value)
|
||||
|
||||
local x1_123, x1_456 = type_to_vec6[vtype](initial_value)
|
||||
local owner = get_scope()
|
||||
if not owner then
|
||||
throw("cannot derive in non-reactive scope")
|
||||
end; assert(owner)
|
||||
|
||||
-- https://en.wikipedia.org/wiki/Damping
|
||||
-- todo: calculate damped freq at 10tau instead of natural freq
|
||||
|
||||
local w_n = 2*math.pi / (period or 1)
|
||||
local z = damping_ratio or 1
|
||||
|
|
@ -166,34 +168,42 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
|||
k = k,
|
||||
c = c,
|
||||
|
||||
x0_123 = x1_123,
|
||||
x1_123 = x1_123,
|
||||
v_123 = Vec3(),
|
||||
x0_123 = ZERO,
|
||||
x1_123 = ZERO,
|
||||
v_123 = ZERO,
|
||||
|
||||
x0_456 = x1_456,
|
||||
x1_456 = x1_456,
|
||||
v_456 = Vec3(),
|
||||
x0_456 = ZERO,
|
||||
x1_456 = ZERO,
|
||||
v_456 = ZERO,
|
||||
|
||||
source_value = initial_value,
|
||||
source_value = false :: any,
|
||||
}
|
||||
|
||||
local output = create_start_node(false :: any)
|
||||
|
||||
-- reschedule spring for simulation on input update
|
||||
local function input_updated(node)
|
||||
local v = source()
|
||||
data.x1_123, data.x1_456 = type_to_vec6[typeof(v)](v)
|
||||
data.source_value = v
|
||||
springs[data] = node -- todo: investigate why insertion is not O(1) at ~20k springs
|
||||
local function updater_effect()
|
||||
local value = source()
|
||||
data.x1_123, data.x1_456 = type_to_vec6[typeof(value)](value)
|
||||
data.source_value = value
|
||||
springs[data] = output -- todo: investigate why insertion is not O(1) at ~20k springs
|
||||
return value
|
||||
end
|
||||
|
||||
-- unused field, use so output prevents gc of inputs
|
||||
output.derive = source :: any
|
||||
local updater = create_node(false :: any, updater_effect)
|
||||
|
||||
-- register above function as side-effect for all inputs
|
||||
for _, input in next, inputs do
|
||||
set_effect(input, input_updated, output)
|
||||
set_owner(updater, owner)
|
||||
evaluate_node(updater)
|
||||
|
||||
-- set initial position to goal
|
||||
data.x0_123, data.x0_456 = data.x1_123, data.x1_456
|
||||
|
||||
-- set output to goal
|
||||
output.cache = data.source_value
|
||||
|
||||
return function()
|
||||
track(output)
|
||||
return output.cache
|
||||
end
|
||||
|
||||
return output_get, data
|
||||
end
|
||||
|
||||
local function step_springs(dt: number)
|
||||
|
|
@ -251,10 +261,12 @@ local function update_spring_sources()
|
|||
if (v_123 + v_456 + dx_123 + dx_456).Magnitude < TOLERANCE then
|
||||
-- close enough to target, unshedule spring and set value to target
|
||||
table.insert(remove_queue, data)
|
||||
set(output, data.source_value)
|
||||
output.cache = data.source_value
|
||||
else
|
||||
set(output, vec6_to_type[typeof(data.source_value)](x0_123, x0_456))
|
||||
output.cache = vec6_to_type[typeof(data.source_value)](x0_123, x0_456)
|
||||
end
|
||||
|
||||
update(output)
|
||||
end
|
||||
|
||||
for _, data in next, remove_queue do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue