mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
a518e46b0f
closes #97 when copying group nodes across decks (including in and out of groups), they become unique and completely independent copies of the original. this is done recursively, so in the case of copying: - group X - contained in Deck A - has another group Z into Deck B, group X will become group Y, group Z will become group W. there is a rare bug that will sometimes cause the deck to save with no groups at all, which i haven't been able to hunt down and don't know how to replicate at the moment. Reviewed-on: https://codeberg.org/StreamGraph/StreamGraph/pulls/143 Co-authored-by: Lera Elvoé <yagich@poto.cafe> Co-committed-by: Lera Elvoé <yagich@poto.cafe>
583 lines
20 KiB
GDScript
583 lines
20 KiB
GDScript
# (c) 2023-present Eroax
|
|
# (c) 2023-present Yagich
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
class_name Deck
|
|
## A deck/graph with nodes.
|
|
##
|
|
## A container for [DeckNode]s, managing connections between them.
|
|
|
|
## The [DeckNode]s that belong to this deck. The key is the node's id, the value
|
|
## is the [DeckNode] instance.
|
|
var nodes: Dictionary
|
|
|
|
|
|
## A map of variables set on this deck.
|
|
var variable_stack: Dictionary = {}
|
|
## The path to save this deck on the file system.
|
|
var save_path: String = ""
|
|
|
|
var is_group: bool = false
|
|
## List of groups belonging to this deck, in the format of[br]
|
|
## [code]Dictionary[String -> Deck.id, Deck][/code]
|
|
#var groups: Dictionary = {}
|
|
## A unique identifier for this deck, or an ID for the group this deck represents.
|
|
var id: String = ""
|
|
## If this is a group, this is the local ID of this instance of the group.
|
|
var instance_id: String = ""
|
|
## The parent deck of this deck, if this is a group.
|
|
var _belonging_to: String = "" # for groups
|
|
## The ID of this group's input node. Used only if [member is_group] is [code]true[/code].
|
|
var group_input_node: String
|
|
## The ID of this group's input node. Used only if [member is_group] is [code]true[/code].
|
|
var group_output_node: String
|
|
## The ID of the group node this group is represented by, contained in this deck's parent deck.
|
|
## Used only if [member is_group] is [code]true[/code].
|
|
## @experimental
|
|
#var group_node: String
|
|
|
|
var emit_group_signals: bool = true
|
|
var emit_node_added_signal: bool = true
|
|
|
|
## Emitted when a node has been added to this deck.
|
|
signal node_added(node: DeckNode)
|
|
## Emitted when a node has been removed from this deck.
|
|
signal node_removed(node: DeckNode)
|
|
## Emitted when nodes have been connected.
|
|
signal nodes_connected(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int)
|
|
## Emitted when nodes have been disconnected.
|
|
signal nodes_disconnected(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int)
|
|
## Emitted when the [member variable_stack] has been modified.
|
|
signal variables_updated()
|
|
|
|
#region group signals
|
|
signal node_added_to_group(node: DeckNode, assign_id: String, assign_to_self: bool, deck: Deck)
|
|
signal node_removed_from_group(node_id: String, remove_connections: bool, deck: Deck)
|
|
signal nodes_connected_in_group(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int, deck: Deck)
|
|
signal nodes_disconnected_in_group(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int, deck: Deck)
|
|
signal node_port_value_updated(node_id: String, port_idx: int, new_value: Variant, deck: Deck)
|
|
signal node_renamed(node_id: String, new_name: String, deck: Deck)
|
|
signal node_moved(node_id: String, new_position: Dictionary, deck: Deck)
|
|
#endregion
|
|
|
|
|
|
func connect_rpc_signals() -> void:
|
|
node_added.connect(RPCSignalLayer._on_deck_node_added)
|
|
node_removed.connect(RPCSignalLayer._on_deck_node_removed)
|
|
|
|
nodes_connected.connect(RPCSignalLayer._on_deck_nodes_connected.bind(id))
|
|
nodes_disconnected.connect(RPCSignalLayer._on_deck_nodes_disconnected.bind(id))
|
|
|
|
variables_updated.connect(RPCSignalLayer._on_deck_variables_updated.bind(id))
|
|
|
|
|
|
## Instantiate a node by its' [member DeckNode.node_type] and add it to this deck.[br]
|
|
## See [method add_node_inst] for parameter descriptions.
|
|
func add_node_type(type: String, assign_id: String = "", assign_to_self: bool = true) -> DeckNode:
|
|
var node_inst: DeckNode = NodeDB.instance_node(type)
|
|
|
|
return add_node_inst(node_inst, assign_id, assign_to_self)
|
|
|
|
|
|
## Add a [DeckNode] instance to this deck.[br]
|
|
## If [param assign_id] is empty, the node will get its' ID (re-)assigned.
|
|
## Otherwise, it will be assigned to be that value.[br]
|
|
## If [param assign_to_self] is [code]true[/code], the node's
|
|
## [member DeckNode._belonging_to] property will be set to [code]self[/code].
|
|
func add_node_inst(node: DeckNode, assign_id: String = "", assign_to_self: bool = true) -> DeckNode:
|
|
if assign_to_self:
|
|
node._belonging_to = self
|
|
|
|
if assign_id == "":
|
|
var uuid := UUID.v4()
|
|
nodes[uuid] = node
|
|
node._id = uuid
|
|
else:
|
|
nodes[assign_id] = node
|
|
node._id = assign_id
|
|
|
|
if emit_node_added_signal:
|
|
node_added.emit(node)
|
|
|
|
if is_group and emit_group_signals:
|
|
node_added_to_group.emit(node, node._id, assign_to_self, self)
|
|
|
|
node.port_value_updated.connect(
|
|
func(port_idx: int, new_value: Variant):
|
|
if is_group and emit_group_signals:
|
|
node_port_value_updated.emit(node._id, port_idx, new_value, self)
|
|
)
|
|
|
|
node.renamed.connect(
|
|
func(new_name: String):
|
|
if is_group and emit_group_signals:
|
|
node_renamed.emit(node._id, new_name, self)
|
|
)
|
|
|
|
node.position_updated.connect(
|
|
func(new_position: Dictionary):
|
|
if is_group and emit_group_signals:
|
|
node_moved.emit(node._id, new_position, self)
|
|
)
|
|
|
|
node.connect_rpc_signals()
|
|
|
|
return node
|
|
|
|
|
|
## Get a node belonging to this deck by its' ID.
|
|
func get_node(uuid: String) -> DeckNode:
|
|
return nodes.get(uuid)
|
|
|
|
|
|
## Returns [code]true[/code] if the connection between two nodes is legal.
|
|
func is_valid_connection(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int) -> bool:
|
|
# do not connect to self
|
|
if from_node_id == to_node_id:
|
|
return false
|
|
|
|
var from_node := get_node(from_node_id)
|
|
var to_node := get_node(to_node_id)
|
|
|
|
var usage_from: Port.UsageType = from_node.get_output_ports()[from_output_port].usage_type
|
|
var usage_to: Port.UsageType = to_node.get_input_ports()[to_input_port].usage_type
|
|
# incompatible usages
|
|
if (usage_from != Port.UsageType.BOTH) and (usage_to != Port.UsageType.BOTH):
|
|
if usage_from != usage_to:
|
|
return false
|
|
|
|
var type_from: DeckType.Types = from_node.get_output_ports()[from_output_port].type
|
|
var type_to: DeckType.Types = to_node.get_input_ports()[to_input_port].type
|
|
# incompatible types
|
|
if not DeckType.can_convert(type_from, type_to):
|
|
return false
|
|
|
|
# duplicate connection
|
|
if from_node.has_outgoing_connection_exact(from_output_port, to_node_id, to_input_port):
|
|
return false
|
|
|
|
return true
|
|
|
|
|
|
## Attempt to connect two nodes. Returns [code]true[/code] if the connection succeeded.
|
|
func connect_nodes(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int) -> bool:
|
|
if not is_valid_connection(from_node_id, to_node_id, from_output_port, to_input_port):
|
|
return false
|
|
|
|
var from_node := get_node(from_node_id)
|
|
var to_node := get_node(to_node_id)
|
|
|
|
if to_node.has_incoming_connection(to_input_port):
|
|
var connection: Dictionary = to_node.incoming_connections[to_input_port]
|
|
var node_id: String = connection.keys()[0]
|
|
var node_out_port: int = connection.values()[0]
|
|
disconnect_nodes(node_id, to_node_id, node_out_port, to_input_port)
|
|
|
|
if is_group and emit_group_signals:
|
|
nodes_connected_in_group.emit(from_node_id, to_node_id, from_output_port, to_input_port, self)
|
|
|
|
from_node.add_outgoing_connection(from_output_port, to_node._id, to_input_port)
|
|
nodes_connected.emit(from_node_id, to_node_id, from_output_port, to_input_port)
|
|
return true
|
|
|
|
|
|
## Remove a connection from two nodes.
|
|
func disconnect_nodes(from_node_id: String, to_node_id: String, from_output_port: int, to_input_port: int) -> void:
|
|
var from_node := get_node(from_node_id)
|
|
var to_node := get_node(to_node_id)
|
|
from_node.remove_outgoing_connection(from_output_port, to_node_id, to_input_port)
|
|
to_node.remove_incoming_connection(to_input_port)
|
|
if is_group and emit_group_signals:
|
|
nodes_disconnected_in_group.emit(from_node_id, to_node_id, from_output_port, to_input_port, self)
|
|
|
|
nodes_disconnected.emit(from_node_id, to_node_id, from_output_port, to_input_port)
|
|
|
|
|
|
## Returns true if this deck has no nodes and no variables.
|
|
func is_empty() -> bool:
|
|
return nodes.is_empty() and variable_stack.is_empty()
|
|
|
|
|
|
## Remove a node from this deck.
|
|
func remove_node(uuid: String, remove_connections: bool = false, force: bool = false, keep_group_instances: bool = false) -> void:
|
|
var node := get_node(uuid)
|
|
if node == null:
|
|
return
|
|
|
|
if not node.user_can_delete and not force:
|
|
return
|
|
|
|
if node.node_type == "group_node" and not keep_group_instances:
|
|
DeckHolder.close_group_instance(node.group_id, node.group_instance_id)
|
|
|
|
if remove_connections:
|
|
var outgoing_connections := node.outgoing_connections.duplicate(true)
|
|
|
|
for output_port: int in outgoing_connections:
|
|
for to_node: String in outgoing_connections[output_port]:
|
|
for to_port: int in outgoing_connections[output_port][to_node]:
|
|
disconnect_nodes(uuid, to_node, output_port, to_port)
|
|
|
|
var incoming_connections := node.incoming_connections.duplicate(true)
|
|
|
|
for input_port: int in incoming_connections:
|
|
for from_node: String in incoming_connections[input_port]:
|
|
disconnect_nodes(from_node, uuid, incoming_connections[input_port][from_node], input_port)
|
|
|
|
nodes.erase(uuid)
|
|
|
|
node_removed.emit(node)
|
|
|
|
if is_group and emit_group_signals:
|
|
node_removed_from_group.emit(uuid, remove_connections, self)
|
|
|
|
|
|
## Set a variable on this deck.
|
|
func set_variable(var_name: String, value: Variant) -> void:
|
|
variable_stack[var_name] = value
|
|
variables_updated.emit()
|
|
|
|
|
|
func update_variable(old_name: String, new_name: String, new_value: Variant) -> void:
|
|
variable_stack.erase(old_name)
|
|
variable_stack[new_name] = new_value
|
|
|
|
|
|
func remove_variable(name: String) -> void:
|
|
variable_stack.erase(name)
|
|
|
|
|
|
## Group the [param nodes_to_group] into a new deck and return it.
|
|
## Returns [code]null[/code] on failure.[br]
|
|
## Adds a group node to this deck, and adds group input and output nodes in the group.
|
|
func group_nodes(nodes_to_group: Array) -> Deck:
|
|
if nodes_to_group.is_empty():
|
|
return null
|
|
|
|
# don't include nodes that can't be grouped/deleted
|
|
nodes_to_group = nodes_to_group.filter(
|
|
func(x: DeckNode):
|
|
return x.user_can_delete
|
|
)
|
|
|
|
var node_ids_to_keep := nodes_to_group.map(
|
|
func(x: DeckNode):
|
|
return x._id
|
|
)
|
|
|
|
var group := DeckHolder.add_empty_group(id)
|
|
|
|
var midpoint := Vector2()
|
|
|
|
var rightmost := -INF
|
|
var leftmost := INF
|
|
for node: DeckNode in nodes_to_group:
|
|
#if node.node_type == "group_node":
|
|
#var _group_id: String = node.group_id
|
|
#var _group: Deck = groups[_group_id]
|
|
#groups.erase(_group)
|
|
#group.groups[_group_id] = _group
|
|
#_group._belonging_to = group
|
|
|
|
if node.position.x > rightmost:
|
|
rightmost = node.position.x
|
|
if node.position.x < leftmost:
|
|
leftmost = node.position.x
|
|
|
|
var outgoing_connections := node.outgoing_connections.duplicate(true)
|
|
|
|
for from_port: int in outgoing_connections:
|
|
for to_node: String in outgoing_connections[from_port]:
|
|
for to_port: int in outgoing_connections[from_port][to_node]:
|
|
if to_node not in node_ids_to_keep:
|
|
disconnect_nodes(node._id, to_node, from_port, to_port)
|
|
|
|
var incoming_connections := node.incoming_connections.duplicate(true)
|
|
|
|
for to_port: int in incoming_connections:
|
|
for from_node: String in incoming_connections[to_port]:
|
|
if from_node not in node_ids_to_keep:
|
|
disconnect_nodes(from_node, node._id, incoming_connections[to_port][from_node], to_port)
|
|
|
|
midpoint += node.position_as_vector2()
|
|
remove_node(node._id, false, true)
|
|
group.add_node_inst(node, node._id)
|
|
|
|
midpoint /= nodes_to_group.size()
|
|
|
|
emit_node_added_signal = false
|
|
var _group_node := add_node_type("group_node")
|
|
_group_node.group_id = group.id
|
|
_group_node.group_instance_id = group.instance_id
|
|
_group_node.position.x = midpoint.x
|
|
_group_node.position.y = midpoint.y
|
|
_group_node.position_updated.emit(_group_node.position)
|
|
#group.group_node = _group_node._id
|
|
node_added.emit(_group_node)
|
|
emit_node_added_signal = true
|
|
|
|
var input_node := group.add_node_type("group_input")
|
|
var output_node := group.add_node_type("group_output")
|
|
group.group_input_node = input_node._id
|
|
group.group_output_node = output_node._id
|
|
|
|
input_node.position.x = leftmost - 350
|
|
output_node.position.x = rightmost + 350
|
|
input_node.position.y = midpoint.y
|
|
output_node.position.y = midpoint.y
|
|
input_node.position_updated.emit(input_node.position)
|
|
output_node.position_updated.emit(output_node.position)
|
|
|
|
input_node.group_node = _group_node
|
|
output_node.group_node = _group_node
|
|
|
|
_group_node.input_node_id = input_node._id
|
|
_group_node.output_node_id = output_node._id
|
|
#_group_node.setup_connections()
|
|
_group_node.init_io()
|
|
|
|
return group
|
|
|
|
|
|
## Get a group belonging to this deck by its ID.
|
|
#func get_group(uuid: String) -> Deck:
|
|
#return groups.get(uuid)
|
|
|
|
|
|
func copy_nodes(nodes_to_copy: Array[String]) -> Dictionary:
|
|
var d := {"nodes": {}}
|
|
|
|
for node_id: String in nodes_to_copy:
|
|
d.nodes[node_id] = get_node(node_id).to_dict()
|
|
|
|
for node: String in d.nodes:
|
|
var outgoing_connections: Dictionary = d.nodes[node].outgoing_connections.duplicate(true)
|
|
|
|
for from_port: int in outgoing_connections:
|
|
for to_node: String in outgoing_connections[from_port]:
|
|
if to_node not in nodes_to_copy:
|
|
(d.nodes[node].outgoing_connections[from_port] as Dictionary).erase(to_node)
|
|
|
|
var keys_to_erase := []
|
|
for from_port: int in d.nodes[node].outgoing_connections:
|
|
if (d.nodes[node].outgoing_connections[from_port] as Dictionary).is_empty():
|
|
keys_to_erase.append(from_port)
|
|
|
|
for key in keys_to_erase:
|
|
(d.nodes[node].outgoing_connections as Dictionary).erase(key)
|
|
|
|
var incoming_connections: Dictionary = d.nodes[node].incoming_connections.duplicate(true)
|
|
|
|
for to_port: int in incoming_connections:
|
|
for from_node: String in incoming_connections[to_port]:
|
|
if from_node not in nodes_to_copy:
|
|
(d.nodes[node].incoming_connections[to_port] as Dictionary).erase(from_node)
|
|
|
|
keys_to_erase.clear()
|
|
for to_port: int in d.nodes[node].incoming_connections:
|
|
if (d.nodes[node].incoming_connections[to_port] as Dictionary).is_empty():
|
|
keys_to_erase.append(to_port)
|
|
|
|
for key in keys_to_erase:
|
|
(d.nodes[node].incoming_connections as Dictionary).erase(key)
|
|
keys_to_erase.clear()
|
|
|
|
for node: Dictionary in d.nodes.values().slice(1):
|
|
node.position.x = node.position.x - d.nodes.values()[0].position.x
|
|
node.position.y = node.position.y - d.nodes.values()[0].position.y
|
|
|
|
d.nodes.values()[0].position.x = 0
|
|
d.nodes.values()[0].position.y = 0
|
|
|
|
return d
|
|
|
|
|
|
func copy_nodes_json(nodes_to_copy: Array[String]) -> String:
|
|
return JSON.stringify(copy_nodes(nodes_to_copy))
|
|
|
|
|
|
func allocate_ids(count: int) -> Array[String]:
|
|
var res: Array[String] = []
|
|
for i in count:
|
|
res.append(UUID.v4())
|
|
return res
|
|
|
|
|
|
func paste_nodes_from_dict(nodes_to_paste: Dictionary, position: Vector2 = Vector2()) -> void:
|
|
if not nodes_to_paste.get("nodes"):
|
|
return
|
|
|
|
var new_ids := allocate_ids(nodes_to_paste.nodes.size())
|
|
var ids_map := {}
|
|
for i: int in nodes_to_paste.nodes.keys().size():
|
|
var node_id: String = nodes_to_paste.nodes.keys()[i]
|
|
ids_map[node_id] = new_ids[i]
|
|
|
|
for node_id: String in nodes_to_paste.nodes:
|
|
nodes_to_paste.nodes[node_id]._id = ids_map[node_id]
|
|
|
|
nodes_to_paste.nodes[node_id].position.x += position.x
|
|
nodes_to_paste.nodes[node_id].position.y += position.y
|
|
|
|
var outgoing_connections: Dictionary = nodes_to_paste.nodes[node_id].outgoing_connections as Dictionary
|
|
var outgoing_connections_res := {}
|
|
for from_port in outgoing_connections:
|
|
outgoing_connections_res[from_port] = {}
|
|
for to_node_id in outgoing_connections[from_port]:
|
|
outgoing_connections_res[from_port][ids_map[to_node_id]] = outgoing_connections[from_port][to_node_id]
|
|
|
|
var incoming_connections: Dictionary = nodes_to_paste.nodes[node_id].incoming_connections as Dictionary
|
|
var incoming_connections_res := {}
|
|
for to_port in incoming_connections:
|
|
incoming_connections_res[to_port] = {}
|
|
for from_node_id in incoming_connections[to_port]:
|
|
incoming_connections_res[to_port][ids_map[from_node_id]] = incoming_connections[to_port][from_node_id]
|
|
|
|
nodes_to_paste.nodes[node_id].outgoing_connections = outgoing_connections_res
|
|
nodes_to_paste.nodes[node_id].incoming_connections = incoming_connections_res
|
|
|
|
var node := DeckNode.from_dict(nodes_to_paste.nodes[node_id])
|
|
var group_needs_unique := false
|
|
if node.node_type == "group_node":
|
|
var group := DeckHolder.make_new_group_instance(node.group_id, id)
|
|
var old_group := DeckHolder.get_group_instance(node.group_id, node.group_instance_id)
|
|
group_needs_unique = old_group._belonging_to != id
|
|
node.group_instance_id = group.instance_id
|
|
group.get_node(group.group_input_node).group_node = node
|
|
group.get_node(group.group_output_node).group_node = node
|
|
node.input_node = group.get_node(group.group_input_node)
|
|
node.output_node = group.get_node(group.group_output_node)
|
|
node.init_io()
|
|
add_node_inst(node, ids_map[node_id])
|
|
if group_needs_unique:
|
|
node.make_unique()
|
|
|
|
|
|
func paste_nodes_from_json(json: String, position: Vector2 = Vector2()) -> void:
|
|
paste_nodes_from_dict(JSON.parse_string(json), position)
|
|
|
|
|
|
func duplicate_nodes(nodes_to_copy: Array[String]) -> void:
|
|
if nodes_to_copy.is_empty():
|
|
return
|
|
|
|
var position := get_node(nodes_to_copy[0]).position_as_vector2() + Vector2(50, 50)
|
|
var d := copy_nodes(nodes_to_copy)
|
|
paste_nodes_from_dict(d, position)
|
|
|
|
|
|
func send_event(event_name: StringName, event_data: Dictionary = {}) -> void:
|
|
for node: DeckNode in nodes.values():
|
|
node._event_received(event_name, event_data)
|
|
|
|
|
|
func get_referenced_groups() -> Array[String]:
|
|
# this is expensive
|
|
# recursively returns a list of all groups referenced by this deck
|
|
var res: Array[String] = []
|
|
for node_id: String in nodes:
|
|
var node := get_node(node_id)
|
|
if node.node_type != "group_node":
|
|
continue
|
|
res.append(node.group_id)
|
|
res.append_array(DeckHolder.get_deck(node.group_id).get_referenced_groups())
|
|
return res
|
|
|
|
|
|
func get_referenced_group_instances() -> Array[Dictionary]:
|
|
var res: Array[Dictionary] = []
|
|
for node_id: String in nodes:
|
|
var node := get_node(node_id)
|
|
if node.node_type != "group_node":
|
|
continue
|
|
res.append({
|
|
"group_id": node.group_id,
|
|
"instance_id": node.group_instance_id,
|
|
"parent_id": node._belonging_to.id,
|
|
"group_node": node_id,
|
|
})
|
|
res.append_array(DeckHolder.get_group_instance(node.group_id, node.group_instance_id).get_referenced_group_instances())
|
|
return res
|
|
|
|
|
|
func _to_string() -> String:
|
|
if not is_group:
|
|
return "DeckNode:%s" % id
|
|
else:
|
|
return "DeckNode:%s::%s" % [id, instance_id]
|
|
|
|
|
|
## Returns a [Dictionary] representation of this deck.
|
|
func to_dict(with_meta: bool = true, group_ids: Array = []) -> Dictionary:
|
|
var inner := {
|
|
"nodes": {},
|
|
"variable_stack": variable_stack,
|
|
"id": id,
|
|
"groups": {}
|
|
}
|
|
|
|
for node_id: String in nodes.keys():
|
|
inner["nodes"][node_id] = nodes[node_id].to_dict(with_meta)
|
|
if (nodes[node_id] as DeckNode).node_type == "group_node":
|
|
if nodes[node_id].group_id not in group_ids:
|
|
inner["groups"][nodes[node_id].group_id] = DeckHolder.get_deck(nodes[node_id].group_id).to_dict(with_meta, group_ids)
|
|
group_ids.append(nodes[node_id].group_id)
|
|
|
|
#for group_id in groups.keys():
|
|
#inner["groups"][group_id] = groups[group_id].to_dict(with_meta)
|
|
|
|
if is_group:
|
|
inner["instance_id"] = instance_id
|
|
inner["group_input_node"] = group_input_node
|
|
inner["group_output_node"] = group_output_node
|
|
|
|
var d := {"deck": inner}
|
|
|
|
if with_meta:
|
|
d["meta"] = {}
|
|
for meta in get_meta_list():
|
|
d["meta"][meta] = var_to_str(get_meta(meta))
|
|
return d
|
|
|
|
|
|
## Create a new deck from a [Dictionary] representation, such as one created by [method to_dict].
|
|
static func from_dict(data: Dictionary, path: String = "") -> Deck:
|
|
var deck := Deck.new()
|
|
deck.save_path = path
|
|
deck.variable_stack = data.deck.variable_stack
|
|
deck.id = data.deck.id
|
|
|
|
for key in data.meta:
|
|
deck.set_meta(key, str_to_var(data.meta[key]))
|
|
|
|
var nodes_data: Dictionary = data.deck.nodes as Dictionary
|
|
|
|
for node_id in nodes_data:
|
|
var node := DeckNode.from_dict(nodes_data[node_id])
|
|
deck.add_node_inst(node, node_id)
|
|
|
|
var groups_data: Dictionary = data.deck.groups as Dictionary
|
|
|
|
for node_id: String in deck.nodes:
|
|
var node := deck.get_node(node_id)
|
|
if node.node_type != "group_node":
|
|
continue
|
|
var group_id: String = node.group_id
|
|
var group_instance_id: String = node.group_instance_id
|
|
var group_data: Dictionary = groups_data[group_id]
|
|
var group := DeckHolder.add_group_from_dict(group_data, group_id, group_instance_id, deck.id)
|
|
group.get_node(group.group_input_node).group_node = node
|
|
group.get_node(group.group_output_node).group_node = node
|
|
node.init_io()
|
|
|
|
#for group_id: String in groups_data:
|
|
#var group := Deck.from_dict(groups_data[group_id])
|
|
#group._belonging_to = deck
|
|
#group.is_group = true
|
|
#deck.groups[group_id] = group
|
|
#group.group_node = groups_data[group_id]["deck"]["group_node"]
|
|
#group.group_input_node = groups_data[group_id]["deck"]["group_input_node"]
|
|
#group.group_output_node = groups_data[group_id]["deck"]["group_output_node"]
|
|
#deck.get_node(group.group_node).init_io()
|
|
|
|
|
|
return deck
|