Merge reactive scope refactor

This commit is contained in:
aaron 2023-09-15 12:54:42 +01:00
parent 0e439f084f
commit efc4798ddb
48 changed files with 2750 additions and 1949 deletions

View file

@ -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