mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Refactor
This commit is contained in:
parent
67e87110c7
commit
f2de9b0e63
25 changed files with 315 additions and 364 deletions
|
|
@ -5,9 +5,8 @@ local flags = require(script.Parent.flags)
|
|||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_node = graph.create_node
|
||||
local assert_owning_scope = graph.assert_owning_scope
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local set_owner = graph.set_owner
|
||||
|
||||
function create_binding<T>(updater: (T) -> T, binding: T)
|
||||
if flags.strict then
|
||||
|
|
@ -31,12 +30,7 @@ function create_binding<T>(updater: (T) -> T, binding: T)
|
|||
end
|
||||
end
|
||||
|
||||
local owner = assert_owning_scope()
|
||||
|
||||
local node = create_node(binding, updater)
|
||||
|
||||
set_owner(node, owner)
|
||||
evaluate_node(node)
|
||||
evaluate_node(create_node(assert_stable_scope(), updater, binding))
|
||||
end
|
||||
|
||||
type PropertyBinding = {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ local typeof = game and typeof or require "test/mock".typeof :: never
|
|||
local throw = require(script.Parent.throw)
|
||||
local graph = require(script.Parent.graph)
|
||||
local get_scope = graph.get_scope
|
||||
local add_cleanup = graph.add_cleanup
|
||||
local push_cleanup = graph.push_cleanup
|
||||
|
||||
local function helper(obj: any)
|
||||
return
|
||||
|
|
@ -21,13 +21,13 @@ local function cleanup(value: unknown)
|
|||
local scope = get_scope()
|
||||
|
||||
if not scope then
|
||||
throw "cannot cleanup in a non-reactive scope"
|
||||
throw "cannot cleanup outside a stable or reactive scope"
|
||||
end; assert(scope)
|
||||
|
||||
if type(value) == "function" then
|
||||
add_cleanup(scope, value :: () -> ())
|
||||
push_cleanup(scope, value :: () -> ())
|
||||
else
|
||||
add_cleanup(scope, helper(value))
|
||||
push_cleanup(scope, helper(value))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,17 @@ if not game then script = require "test/relative-string" end
|
|||
|
||||
local graph = require(script.Parent.graph)
|
||||
local create_node = graph.create_node
|
||||
local set_owner = graph.set_owner
|
||||
local track = graph.track
|
||||
local assert_owning_scope = graph.assert_owning_scope
|
||||
local push_child_to_scope = graph.push_child_to_scope
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
|
||||
local function derive<T>(source: () -> T): () -> T
|
||||
local owner = assert_owning_scope()
|
||||
local node = create_node(assert_stable_scope(), source, false :: any)
|
||||
|
||||
local node = create_node(false :: any, source)
|
||||
|
||||
set_owner(node, owner)
|
||||
evaluate_node(node)
|
||||
|
||||
return function()
|
||||
track(node)
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,16 +2,12 @@ if not game then script = require "test/relative-string" end
|
|||
|
||||
local graph = require(script.Parent.graph)
|
||||
local create_node = graph.create_node
|
||||
local assert_owning_scope = graph.assert_owning_scope
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local set_owner = graph.set_owner
|
||||
|
||||
local function effect<T>(callback: (T) -> T, initial_value: T)
|
||||
local owner = assert_owning_scope()
|
||||
local node = create_node(assert_stable_scope(), callback, initial_value)
|
||||
|
||||
local node = create_node(initial_value, callback)
|
||||
|
||||
set_owner(node, owner)
|
||||
evaluate_node(node)
|
||||
end
|
||||
|
||||
|
|
|
|||
104
src/graph.luau
104
src/graph.luau
|
|
@ -3,7 +3,7 @@ if not game then script = require "test/relative-string" end
|
|||
local throw = require(script.Parent.throw)
|
||||
local flags = require(script.Parent.flags)
|
||||
|
||||
export type StartNode<T> = {
|
||||
export type SourceNode<T> = {
|
||||
cache: T,
|
||||
[number]: Node<T>
|
||||
}
|
||||
|
|
@ -16,12 +16,11 @@ export type Node<T> = {
|
|||
owned: { Node<T> } | false,
|
||||
owner: Node<T> | false,
|
||||
|
||||
parents: { StartNode<T> },
|
||||
parents: { SourceNode<T> },
|
||||
[number]: Node<T> -- children
|
||||
}
|
||||
|
||||
-- reactive scope stack
|
||||
local scopes = { n = 0 } :: { [number]: Node<any>, n: number }
|
||||
local scopes = { n = 0 } :: { [number]: Node<any>, n: number } -- scopes stack
|
||||
|
||||
local function ycall<T, U>(fn: (T) -> U, arg: T): (boolean, string|U)
|
||||
local thread = coroutine.create(pcall)
|
||||
|
|
@ -40,46 +39,37 @@ local function get_scope(): Node<unknown>?
|
|||
return scopes[scopes.n]
|
||||
end
|
||||
|
||||
local function assert_owning_scope(): Node<unknown>
|
||||
local function assert_stable_scope(): Node<unknown>
|
||||
local scope = get_scope()
|
||||
|
||||
if not scope then
|
||||
local caller_name = debug.info(2, "n")
|
||||
return throw(`cannot use {caller_name}() in a non-reactive scope`)
|
||||
return throw(`cannot use {caller_name}() outside a stable or reactive scope`)
|
||||
elseif scope.effect then
|
||||
throw("cannot create new reactive scope in a tracking reactive scope")
|
||||
throw("cannot create a new reactive scope inside another reactive scope")
|
||||
end
|
||||
|
||||
return scope
|
||||
end
|
||||
|
||||
local function add_child<T>(parent: StartNode<any>, child: Node<any>)
|
||||
local function push_child<T>(parent: SourceNode<any>, child: Node<any>)
|
||||
table.insert(parent, child)
|
||||
table.insert(child.parents, parent)
|
||||
end
|
||||
|
||||
local function set_owner(node: Node<any>, owner: Node<any>)
|
||||
node.owner = owner
|
||||
if owner.owned then
|
||||
table.insert(owner.owned, node)
|
||||
else
|
||||
owner.owned = { node }
|
||||
end
|
||||
end
|
||||
|
||||
local function open_scope<T>(node: Node<T>)
|
||||
local function push_scope<T>(node: Node<T>)
|
||||
local n = scopes.n + 1
|
||||
scopes.n = n
|
||||
scopes[n] = node
|
||||
end
|
||||
|
||||
local function close_scope()
|
||||
local function pop_scope()
|
||||
local n = scopes.n
|
||||
scopes.n = n - 1
|
||||
scopes[n] = nil
|
||||
end
|
||||
|
||||
local function add_cleanup<T>(node: Node<T>, cleanup: () -> ())
|
||||
local function push_cleanup<T>(node: Node<T>, cleanup: () -> ())
|
||||
if node.cleanups then
|
||||
table.insert(node.cleanups, cleanup)
|
||||
else
|
||||
|
|
@ -87,34 +77,35 @@ local function add_cleanup<T>(node: Node<T>, cleanup: () -> ())
|
|||
end
|
||||
end
|
||||
|
||||
local function run_cleanups<T>(node: Node<T>)
|
||||
local function flush_cleanups<T>(node: Node<T>)
|
||||
if node.cleanups then
|
||||
for _, fn in next, node.cleanups do
|
||||
local ok, err: string? = pcall(fn)
|
||||
if not ok then throw(`cleanup error: {err}`) end
|
||||
end
|
||||
|
||||
table.clear(node.cleanups)
|
||||
end
|
||||
end
|
||||
|
||||
local function find_and_swap_pop<T>(t: { T }, v: T)
|
||||
local idx = table.find(t, v) :: number
|
||||
local i = table.find(t, v) :: number
|
||||
local n = #t
|
||||
t[idx] = t[n]
|
||||
t[i] = t[n]
|
||||
t[n] = nil
|
||||
end
|
||||
|
||||
local function unparent<T>(node: Node<T>)
|
||||
local parents = node.parents
|
||||
|
||||
for i, parent in next, parents do
|
||||
for i, parent in parents do
|
||||
find_and_swap_pop(parent, node)
|
||||
parents[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function destroy<T>(node: Node<T>)
|
||||
run_cleanups(node)
|
||||
flush_cleanups(node)
|
||||
unparent(node)
|
||||
|
||||
if node.owner then
|
||||
|
|
@ -141,28 +132,28 @@ local function evaluate_node<T>(node: Node<T>)
|
|||
local cur_value = node.cache
|
||||
|
||||
if flags.strict then
|
||||
run_cleanups(node)
|
||||
flush_cleanups(node)
|
||||
destroy_owned(node)
|
||||
|
||||
open_scope(node)
|
||||
push_scope(node)
|
||||
|
||||
local ok, new_value = ycall(node.effect :: (T) -> T, cur_value)
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
if not ok then throw(new_value :: string) end
|
||||
|
||||
node.cache = new_value :: T
|
||||
end
|
||||
|
||||
run_cleanups(node)
|
||||
flush_cleanups(node)
|
||||
destroy_owned(node)
|
||||
|
||||
open_scope(node)
|
||||
push_scope(node)
|
||||
|
||||
local ok, new_value = pcall(node.effect :: (T) -> T, node.cache)
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
if not ok then
|
||||
table.clear(update_queue)
|
||||
|
|
@ -175,7 +166,7 @@ local function evaluate_node<T>(node: Node<T>)
|
|||
return cur_value ~= new_value
|
||||
end
|
||||
|
||||
local function queue_children<T>(node: StartNode<T>)
|
||||
local function queue_children_for_update<T>(node: SourceNode<T>)
|
||||
local i = update_queue.n
|
||||
while node[1] do
|
||||
i += 1
|
||||
|
|
@ -198,7 +189,7 @@ local function flush_update_queue()
|
|||
--assert(node.effect)
|
||||
|
||||
if node.owner and evaluate_node(node) then
|
||||
queue_children(node)
|
||||
queue_children_for_update(node)
|
||||
end
|
||||
|
||||
update_queue[i] = false :: any
|
||||
|
|
@ -210,9 +201,9 @@ local function flush_update_queue()
|
|||
_flushing = false
|
||||
end
|
||||
|
||||
local function update<T>(root: StartNode<T>)
|
||||
local function update_descendants<T>(root: SourceNode<T>)
|
||||
local n0 = update_queue.n
|
||||
queue_children(root)
|
||||
queue_children_for_update(root)
|
||||
|
||||
if flags.batch then return end
|
||||
|
||||
|
|
@ -223,7 +214,7 @@ local function update<T>(root: StartNode<T>)
|
|||
|
||||
-- check if node is still owned in case destroyed after queued
|
||||
if node.owner and evaluate_node(node) then
|
||||
queue_children(node)
|
||||
queue_children_for_update(node)
|
||||
end
|
||||
|
||||
update_queue[i] = false :: any -- false instead of nil to avoid sparse
|
||||
|
|
@ -233,27 +224,37 @@ local function update<T>(root: StartNode<T>)
|
|||
update_queue.n = n0
|
||||
end
|
||||
|
||||
local function track<T>(node: StartNode<T>)
|
||||
local function push_child_to_scope<T>(node: SourceNode<T>)
|
||||
local scope = get_scope()
|
||||
if scope and scope.effect then -- do not track nodes with no effect
|
||||
add_child(node, scope)
|
||||
push_child(node, scope)
|
||||
end
|
||||
end
|
||||
|
||||
local function create_node<T>(value: T, effect: false | (T) -> T): Node<T>
|
||||
return {
|
||||
local function create_node<T>(owner: false | Node<any>, effect: false | (T) -> T, value: T): Node<T>
|
||||
local node: Node<T> = {
|
||||
cache = value,
|
||||
effect = effect,
|
||||
cleanups = false,
|
||||
|
||||
owner = false,
|
||||
owner = owner,
|
||||
owned = false,
|
||||
|
||||
parents = {},
|
||||
}
|
||||
|
||||
if owner then
|
||||
if owner.owned then
|
||||
table.insert(owner.owned, node)
|
||||
else
|
||||
owner.owned = { node }
|
||||
end
|
||||
end
|
||||
|
||||
return node
|
||||
end
|
||||
|
||||
local function create_start_node<T>(value: T): StartNode<T>
|
||||
local function create_source_node<T>(value: T): SourceNode<T>
|
||||
return { cache = value }
|
||||
end
|
||||
|
||||
|
|
@ -262,20 +263,19 @@ local function get_children<T>(node: Node<T>): { Node<unknown> }
|
|||
end
|
||||
|
||||
return table.freeze {
|
||||
open_scope = open_scope,
|
||||
close_scope = close_scope,
|
||||
push_scope = push_scope,
|
||||
pop_scope = pop_scope,
|
||||
evaluate_node = evaluate_node,
|
||||
get_scope = get_scope,
|
||||
assert_owning_scope = assert_owning_scope,
|
||||
add_cleanup = add_cleanup,
|
||||
set_owner = set_owner,
|
||||
assert_stable_scope = assert_stable_scope,
|
||||
push_cleanup = push_cleanup,
|
||||
destroy = destroy,
|
||||
run_cleanups = run_cleanups,
|
||||
track = track,
|
||||
update = update,
|
||||
add_child = add_child,
|
||||
flush_cleanups = flush_cleanups,
|
||||
push_child_to_scope = push_child_to_scope,
|
||||
update_descendants = update_descendants,
|
||||
push_child = push_child,
|
||||
create_node = create_node,
|
||||
create_start_node = create_start_node,
|
||||
create_source_node = create_source_node,
|
||||
get_children = get_children,
|
||||
flush_update_queue = flush_update_queue,
|
||||
scopes = scopes
|
||||
|
|
|
|||
|
|
@ -4,15 +4,14 @@ local throw = require(script.Parent.throw)
|
|||
local flags = require(script.Parent.flags)
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
type StartNode<T> = graph.StartNode<T>
|
||||
type SourceNode<T> = graph.SourceNode<T>
|
||||
local create_node = graph.create_node
|
||||
local create_start_node = graph.create_start_node
|
||||
local set_owner = graph.set_owner
|
||||
local track = graph.track
|
||||
local update = graph.update
|
||||
local assert_owning_scope = graph.assert_owning_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local create_source_node = graph.create_source_node
|
||||
local push_child_to_scope = graph.push_child_to_scope
|
||||
local update_descendants = graph.update_descendants
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local push_scope = graph.push_scope
|
||||
local pop_scope = graph.pop_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local destroy = graph.destroy
|
||||
|
||||
|
|
@ -28,14 +27,12 @@ local function check_primitives(t: {})
|
|||
end
|
||||
|
||||
local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI, K) -> VO): () -> { VO }
|
||||
local owner = assert_owning_scope()
|
||||
|
||||
local subowner = create_node(false, false)
|
||||
set_owner(subowner, owner)
|
||||
local owner = assert_stable_scope()
|
||||
local subowner = create_node(owner, false, false)
|
||||
|
||||
local input_cache = {} :: Map<K, VI>
|
||||
local output_cache = {} :: Map<K, VO>
|
||||
local input_nodes = {} :: Map<K, StartNode<VI>>
|
||||
local input_nodes = {} :: Map<K, SourceNode<VI>>
|
||||
local remove_queue = {} :: { K }
|
||||
local scopes = {} :: Map<K, Node<unknown>>
|
||||
|
||||
|
|
@ -59,7 +56,7 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
table.clear(remove_queue)
|
||||
|
||||
open_scope(subowner)
|
||||
push_scope(subowner)
|
||||
|
||||
-- process new or changed values
|
||||
for i, v in next, data do
|
||||
|
|
@ -67,23 +64,22 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
if cv ~= v then
|
||||
if cv == nil then -- create new scope and run transform
|
||||
local scope = create_node(false, false)
|
||||
local scope = create_node(subowner, false, false)
|
||||
scopes[i] = scope :: Node<any>
|
||||
|
||||
local node = create_start_node(v)
|
||||
local node = create_source_node(v)
|
||||
|
||||
set_owner(scope, subowner)
|
||||
open_scope(scope)
|
||||
push_scope(scope)
|
||||
|
||||
local ok, result = pcall(transform, function()
|
||||
track(node)
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end, i)
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
if not ok then
|
||||
close_scope() -- subowner scope
|
||||
pop_scope() -- subowner scope
|
||||
error(result, 0)
|
||||
end
|
||||
|
||||
|
|
@ -91,14 +87,14 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
output_cache[i] = result
|
||||
else -- update source
|
||||
input_nodes[i].cache = v
|
||||
update(input_nodes[i])
|
||||
update_descendants(input_nodes[i])
|
||||
end
|
||||
|
||||
input_cache[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
local output_array = table.create(#scopes)
|
||||
for _, v in next, output_cache do
|
||||
|
|
@ -109,29 +105,26 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
return output_array
|
||||
end
|
||||
|
||||
local node = create_node(false :: any, function()
|
||||
local node = create_node(owner, function()
|
||||
return update_children(input())
|
||||
end)
|
||||
set_owner(node, owner)
|
||||
end, false :: any)
|
||||
|
||||
evaluate_node(node)
|
||||
|
||||
return function()
|
||||
track(node)
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end
|
||||
end
|
||||
|
||||
local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () -> K) -> VO): () -> { VO }
|
||||
local owner = assert_owning_scope()
|
||||
|
||||
local subowner = create_node(false, false)
|
||||
set_owner(subowner, owner)
|
||||
local owner = assert_stable_scope()
|
||||
local subowner = create_node(owner, false, false)
|
||||
|
||||
local cur_input_cache_up = {} :: Map<VI, K>
|
||||
local new_input_cache_up = {} :: Map<VI, K>
|
||||
local output_cache = {} :: Map<VI, VO>
|
||||
local input_nodes = {} :: Map<VI, StartNode<K>>
|
||||
local input_nodes = {} :: Map<VI, SourceNode<K>>
|
||||
local scopes = {} :: Map<VI, Node<unknown>>
|
||||
|
||||
local function update_children(data: Map<K, VI>)
|
||||
|
|
@ -147,7 +140,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
end
|
||||
end
|
||||
|
||||
open_scope(subowner)
|
||||
push_scope(subowner)
|
||||
|
||||
-- process data
|
||||
for i, v in next, data do
|
||||
|
|
@ -156,23 +149,22 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
local cv = cur_input_cache[v]
|
||||
|
||||
if cv == nil then -- create new scope and run transform
|
||||
local scope = create_node(false, false)
|
||||
local scope = create_node(subowner, false, false)
|
||||
scopes[v] = scope :: Node<any>
|
||||
|
||||
local node = create_start_node(i)
|
||||
local node = create_source_node(i)
|
||||
|
||||
set_owner(scope, subowner)
|
||||
open_scope(scope)
|
||||
push_scope(scope)
|
||||
|
||||
local ok, result = pcall(transform, v, function()
|
||||
track(node)
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end)
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
if not ok then
|
||||
close_scope() -- subowner scope
|
||||
pop_scope() -- subowner scope
|
||||
error(result, 0)
|
||||
end
|
||||
|
||||
|
|
@ -181,14 +173,14 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
else -- update source
|
||||
if cv ~= i then
|
||||
input_nodes[v].cache = i
|
||||
update(input_nodes[v])
|
||||
update_descendants(input_nodes[v])
|
||||
end
|
||||
|
||||
cur_input_cache[v] = nil
|
||||
end
|
||||
end
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
-- remove old values
|
||||
for v in next, cur_input_cache do
|
||||
|
|
@ -212,15 +204,14 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
return output_array
|
||||
end
|
||||
|
||||
local node = create_node(false :: any, function()
|
||||
local node = create_node(owner, function()
|
||||
return update_children(input())
|
||||
end)
|
||||
set_owner(node, owner)
|
||||
end, false :: any)
|
||||
|
||||
evaluate_node(node)
|
||||
|
||||
return function()
|
||||
track(node)
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ local throw = require(script.Parent.throw)
|
|||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_node = graph.create_node
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local push_scope = graph.push_scope
|
||||
local pop_scope = graph.pop_scope
|
||||
local destroy = graph.destroy
|
||||
|
||||
local refs = {}
|
||||
|
||||
local function root<T...>(fn: (destroy: () -> ()) -> T...): T...
|
||||
local node = create_node(false, false)
|
||||
local node = create_node(false, false, false)
|
||||
|
||||
refs[node] = true -- prevent gc of root node
|
||||
|
||||
|
|
@ -21,11 +21,11 @@ local function root<T...>(fn: (destroy: () -> ()) -> T...): T...
|
|||
destroy(node)
|
||||
end
|
||||
|
||||
open_scope(node)
|
||||
push_scope(node)
|
||||
|
||||
local result = { pcall(fn, destroy) }
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
if not result[1] then
|
||||
refs[node] = nil
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@ if not game then script = require "test/relative-string" end
|
|||
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_start_node = graph.create_start_node
|
||||
local track = graph.track
|
||||
local update = graph.update
|
||||
local create_source_node = graph.create_source_node
|
||||
local push_child_to_scope = graph.push_child_to_scope
|
||||
local update_descendants = graph.update_descendants
|
||||
|
||||
export type Source<T> = (() -> T) & ((value: T) -> T)
|
||||
|
||||
local function source<T>(initial_value: T): Source<T>
|
||||
local node = create_start_node(initial_value)
|
||||
local node = create_source_node(initial_value)
|
||||
|
||||
return function(...): T
|
||||
if select("#", ...) == 0 then -- no args were given
|
||||
track(node)
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ local function source<T>(initial_value: T): Source<T>
|
|||
end
|
||||
|
||||
node.cache = v
|
||||
update(node)
|
||||
update_descendants(node)
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -24,14 +24,13 @@ Unsupported datatypes:
|
|||
local throw = require(script.Parent.throw)
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
type StartNode<T> = graph.StartNode<T>
|
||||
type SourceNode<T> = graph.SourceNode<T>
|
||||
local create_node = graph.create_node
|
||||
local create_start_node = graph.create_start_node
|
||||
local assert_owning_scope = graph.assert_owning_scope
|
||||
local create_source_node = graph.create_source_node
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local update = graph.update
|
||||
local set_owner = graph.set_owner
|
||||
local track = graph.track
|
||||
local update_descendants = graph.update_descendants
|
||||
local push_child_to_scope = graph.push_child_to_scope
|
||||
|
||||
local UPDATE_RATE = 120
|
||||
local TOLERANCE = 0.0001
|
||||
|
|
@ -146,11 +145,11 @@ 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>]: StartNode<any> } = {}
|
||||
local springs: { [SpringData<any>]: SourceNode<any> } = {}
|
||||
setmetatable(springs, { __mode = "v" })
|
||||
|
||||
local function spring<T>(source: () -> T, period: number?, damping_ratio: number?): () -> T
|
||||
local owner = assert_owning_scope()
|
||||
local owner = assert_stable_scope()
|
||||
|
||||
-- https://en.wikipedia.org/wiki/Damping
|
||||
|
||||
|
|
@ -182,7 +181,7 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
|||
source_value = false :: any,
|
||||
}
|
||||
|
||||
local output = create_start_node(false :: any)
|
||||
local output = create_source_node(false :: any)
|
||||
|
||||
local function updater_effect()
|
||||
local value = source()
|
||||
|
|
@ -192,9 +191,8 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
|||
return value
|
||||
end
|
||||
|
||||
local updater = create_node(false :: any, updater_effect)
|
||||
local updater = create_node(owner, updater_effect, false :: any)
|
||||
|
||||
set_owner(updater, owner)
|
||||
evaluate_node(updater)
|
||||
|
||||
-- set initial position to goal
|
||||
|
|
@ -204,7 +202,7 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
|||
output.cache = data.source_value
|
||||
|
||||
return function()
|
||||
track(output)
|
||||
push_child_to_scope(output)
|
||||
return output.cache
|
||||
end
|
||||
end
|
||||
|
|
@ -269,7 +267,7 @@ local function update_spring_sources()
|
|||
output.cache = vec6_to_type[typeof(data.source_value)](x0_123, x0_456)
|
||||
end
|
||||
|
||||
update(output)
|
||||
update_descendants(output)
|
||||
end
|
||||
|
||||
for _, data in next, remove_queue do
|
||||
|
|
|
|||
|
|
@ -3,20 +3,19 @@ if not game then script = require "test/relative-string" end
|
|||
local throw = require(script.Parent.throw)
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
type StartNode<T> = graph.StartNode<T>
|
||||
type SourceNode<T> = graph.SourceNode<T>
|
||||
local create_node = graph.create_node
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local set_owner = graph.set_owner
|
||||
local track = graph.track
|
||||
local push_child_to_scope = graph.push_child_to_scope
|
||||
local destroy = graph.destroy
|
||||
local assert_owning_scope = graph.assert_owning_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local push_scope = graph.push_scope
|
||||
local pop_scope = graph.pop_scope
|
||||
|
||||
type Map<K, V> = { [K]: V }
|
||||
|
||||
local function switch<T, U>(source: () -> T): (map: Map<T, ((() -> U)?)>) -> () -> U?
|
||||
local owner = assert_owning_scope()
|
||||
local owner = assert_stable_scope()
|
||||
|
||||
return function(map)
|
||||
local last_scope: Node<false>?
|
||||
|
|
@ -38,28 +37,26 @@ local function switch<T, U>(source: () -> T): (map: Map<T, ((() -> U)?)>) -> ()
|
|||
throw "map must map a value to a function"
|
||||
end
|
||||
|
||||
local new_scope = create_node(false, false)
|
||||
local new_scope = create_node(owner, false, false)
|
||||
last_scope = new_scope :: Node<any>
|
||||
|
||||
set_owner(new_scope, owner)
|
||||
open_scope(new_scope)
|
||||
push_scope(new_scope)
|
||||
|
||||
local ok, result = pcall(component)
|
||||
|
||||
close_scope()
|
||||
pop_scope()
|
||||
|
||||
if not ok then error(result, 0) end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
local node = create_node(nil :: U?, update)
|
||||
local node = create_node(owner, update, nil)
|
||||
|
||||
set_owner(node, owner)
|
||||
evaluate_node(node)
|
||||
|
||||
return function()
|
||||
track(node)
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue