From 0d6f3169f186fe15a07d5b39182f3ade3f20f7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lera=20Elvo=C3=A9?= Date: Wed, 6 Dec 2023 13:16:38 +0300 Subject: [PATCH] add group instancing --- classes/deck/deck.gd | 103 +++++++++++++------- classes/deck/deck_holder.gd | 83 ++++++++++++++-- classes/deck/nodes/group_input_node.gd | 3 +- classes/deck/nodes/group_node.gd | 14 ++- classes/deck/nodes/group_output_node.gd | 3 +- graph_node_renderer/deck_holder_renderer.gd | 16 +-- 6 files changed, 165 insertions(+), 57 deletions(-) diff --git a/classes/deck/deck.gd b/classes/deck/deck.gd index 88ca228..44bffa7 100644 --- a/classes/deck/deck.gd +++ b/classes/deck/deck.gd @@ -16,11 +16,13 @@ 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. +#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: Deck # for groups +#var _belonging_to: Deck # 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]. @@ -28,7 +30,7 @@ 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 group_node: String ## Emitted when a node has been added to this deck. signal node_added(node: DeckNode) @@ -107,6 +109,9 @@ func remove_node(uuid: String, remove_connections: bool = false) -> void: if node == null: return + if node.node_type == "group_node": + DeckHolder.close_group_instance(node.group_id, node.group_instance_id) + if remove_connections: var outgoing_connections := node.outgoing_connections.duplicate(true) @@ -138,20 +143,16 @@ func group_nodes(nodes_to_group: Array) -> Deck: return x._id ) - var group := Deck.new() - group.is_group = true - group._belonging_to = self - var group_id := UUID.v4() - group.id = group_id + var group := DeckHolder.add_empty_group() var midpoint := Vector2() for node: DeckNode in nodes_to_group: - if node.node_type == "group_node": # for recursive grouping - 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.node_type == "group_node": # for recursive grouping + #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 var outgoing_connections := node.outgoing_connections.duplicate(true) @@ -175,29 +176,32 @@ func group_nodes(nodes_to_group: Array) -> Deck: midpoint /= nodes_to_group.size() var _group_node := add_node_type("group_node") - _group_node.group_id = group_id + _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 + #group.group_node = _group_node._id 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 - _group_node.input_node = input_node - _group_node.output_node = output_node - _group_node.setup_connections() + input_node.group_node = _group_node + output_node.group_node = _group_node - groups[group_id] = group + _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 get_group(uuid: String) -> Deck: + #return groups.get(uuid) func copy_nodes(nodes_to_copy: Array[String]) -> Dictionary: @@ -276,6 +280,14 @@ func paste_nodes_from_dict(nodes: Dictionary, position: Vector2 = Vector2()) -> nodes.nodes[node_id].incoming_connections = incoming_connections_res var node := DeckNode.from_dict(nodes.nodes[node_id]) + if node.node_type == "group_node": + var group := DeckHolder.make_new_group_instance(node.group_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]) @@ -298,7 +310,7 @@ func send_event(event_name: StringName, event_data: Dictionary = {}) -> void: ## Returns a [Dictionary] representation of this deck. -func to_dict(with_meta: bool = true) -> Dictionary: +func to_dict(with_meta: bool = true, group_ids: Array = []) -> Dictionary: var inner := { "nodes": {}, "variable_stack": variable_stack, @@ -306,14 +318,18 @@ func to_dict(with_meta: bool = true) -> Dictionary: "groups": {} } - for node_id in nodes.keys(): + 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 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) + #for group_id in groups.keys(): + #inner["groups"][group_id] = groups[group_id].to_dict(with_meta) if is_group: - inner["group_node"] = group_node + inner["instance_id"] = instance_id inner["group_input_node"] = group_input_node inner["group_output_node"] = group_output_node @@ -344,14 +360,27 @@ static func from_dict(data: Dictionary, path: String = "") -> Deck: var groups_data: Dictionary = data.deck.groups as Dictionary - 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() + 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) + 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 diff --git a/classes/deck/deck_holder.gd b/classes/deck/deck_holder.gd index 86de789..5d0b725 100644 --- a/classes/deck/deck_holder.gd +++ b/classes/deck/deck_holder.gd @@ -3,14 +3,15 @@ class_name DeckHolder ## A static class holding references to all decks opened in the current session. ## List of decks opened this session. -static var decks: Array[Deck] +#static var decks: Array[Deck] +static var decks: Dictionary # Dictionary[String -> id, (Deck|Dictionary[String -> instance_id, Deck])] ## Returns a new empty deck and assigns a new random ID to it. static func add_empty_deck() -> Deck: var deck := Deck.new() - DeckHolder.decks.append(deck) var uuid := UUID.v4() + decks[uuid] = deck deck.id = uuid return deck @@ -21,17 +22,81 @@ static func open_deck_from_file(path: String) -> Deck: if f.get_error() != OK: return null - var deck := Deck.from_dict(JSON.parse_string(f.get_as_text()), path) - DeckHolder.decks.append(deck) - + var deck := open_deck_from_dict(JSON.parse_string(f.get_as_text()), path) return deck +static func open_deck_from_dict(data: Dictionary, path := "") -> Deck: + var deck := Deck.from_dict(data, path) + decks[deck.id] = deck + return deck + + +static func add_group_from_dict(data: Dictionary, deck_id: String, instance_id: String) -> Deck: + var group := Deck.from_dict(data) + group.instance_id = instance_id + group.is_group = true + group.group_input_node = data.deck.group_input_node + group.group_output_node = data.deck.group_output_node + var instances: Dictionary = decks.get(deck_id, {}) + instances[instance_id] = group + decks[deck_id] = instances + return group + + +static func make_new_group_instance(group_id: String) -> Deck: + var group := get_deck(group_id) + var data := group.to_dict() + return add_group_from_dict(data, group_id, UUID.v4()) + + +static func add_empty_group() -> Deck: + var group := Deck.new() + group.is_group = true + group.id = UUID.v4() + group.instance_id = UUID.v4() + decks[group.id] = {group.instance_id: group} + + return group + + +static func get_deck(id: String) -> Deck: + if !decks.has(id): + return null + + if !(decks[id] is Dictionary): + return decks[id] + else: + return (decks[id] as Dictionary).values()[0] + + +static func get_group_instance(group_id: String, instance_id: String) -> Deck: + if !decks.has(group_id): + return null + + if decks[group_id] is Dictionary: + return (decks[group_id] as Dictionary).get(instance_id) + else: + return null + + +static func close_group_instance(group_id: String, instance_id: String) -> void: + var group_instances: Dictionary = decks.get(group_id, {}) as Dictionary + group_instances.erase(instance_id) + if group_instances.is_empty(): + decks.erase(group_id) + + ## Unloads a deck. -static func close_deck(deck: Deck) -> void: - DeckHolder.decks.erase(deck) +static func close_deck(deck_id: String) -> void: + if decks.get(deck_id) is Deck: + decks.erase(deck_id) static func send_event(event_name: StringName, event_data: Dictionary = {}) -> void: - for deck in decks: - deck.send_event(event_name, event_data) + for deck_id: String in decks: + if decks[deck_id] is Deck: + (decks[deck_id] as Deck).send_event(event_name, event_data) + else: + for deck_instance_id: String in decks[deck_id]: + (decks[deck_id][deck_instance_id] as Deck).send_event(event_name, event_data) diff --git a/classes/deck/nodes/group_input_node.gd b/classes/deck/nodes/group_input_node.gd index e2f4dad..93c7f3d 100644 --- a/classes/deck/nodes/group_input_node.gd +++ b/classes/deck/nodes/group_input_node.gd @@ -4,6 +4,8 @@ var output_count: int: get: return get_all_ports().size() - 1 +var group_node: DeckNode + func _init() -> void: name = "Group input" @@ -68,5 +70,4 @@ func _post_load() -> void: func _value_request(from_port: int) -> Variant: - var group_node := _belonging_to._belonging_to.get_node(_belonging_to.group_node) return group_node.request_value(group_node.get_input_ports()[from_port].index_of_type) diff --git a/classes/deck/nodes/group_node.gd b/classes/deck/nodes/group_node.gd index 120c41b..c251003 100644 --- a/classes/deck/nodes/group_node.gd +++ b/classes/deck/nodes/group_node.gd @@ -1,16 +1,20 @@ extends DeckNode var group_id: String +var group_instance_id: String var input_node: DeckNode var output_node: DeckNode +var input_node_id: String +var output_node_id: String + var extra_ports: Array func _init() -> void: name = "Group" node_type = "group_node" - props_to_serialize = [&"group_id", &"extra_ports"] + props_to_serialize = [&"group_id", &"group_instance_id", &"extra_ports", &"input_node_id", &"output_node_id"] appears_in_search = false @@ -24,10 +28,16 @@ func _pre_connection() -> void: func init_io() -> void: - var group: Deck = _belonging_to.groups.get(group_id) as Deck + #var group: Deck = _belonging_to.groups.get(group_id) as Deck + var group := DeckHolder.get_group_instance(group_id, group_instance_id) if !group: return + if input_node && input_node.ports_updated.is_connected(recalculate_ports): + input_node.ports_updated.disconnect(recalculate_ports) + if output_node && output_node.ports_updated.is_connected(recalculate_ports): + output_node.ports_updated.disconnect(recalculate_ports) + input_node = group.get_node(group.group_input_node) output_node = group.get_node(group.group_output_node) diff --git a/classes/deck/nodes/group_output_node.gd b/classes/deck/nodes/group_output_node.gd index 477d90b..baeb7b4 100644 --- a/classes/deck/nodes/group_output_node.gd +++ b/classes/deck/nodes/group_output_node.gd @@ -4,6 +4,8 @@ var input_count: int: get: return get_all_ports().size() - 1 +var group_node: DeckNode + func _init() -> void: name = "Group output" @@ -69,5 +71,4 @@ func _post_load() -> void: func _receive(to_input_port: int, data: Variant, extra_data: Array = []) -> void: - var group_node := _belonging_to._belonging_to.get_node(_belonging_to.group_node) group_node.send(group_node.get_output_ports()[to_input_port].index_of_type, data, extra_data) diff --git a/graph_node_renderer/deck_holder_renderer.gd b/graph_node_renderer/deck_holder_renderer.gd index 670f108..458aff2 100644 --- a/graph_node_renderer/deck_holder_renderer.gd +++ b/graph_node_renderer/deck_holder_renderer.gd @@ -89,8 +89,8 @@ func add_empty_deck() -> void: var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate() inst.deck = deck tab_container.add_content(inst, "Deck %s" % (tab_container.get_tab_count() + 1)) - tab_container.set_tab_metadata(tab_container.get_current_tab(), deck) - inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested.bind(deck)) + tab_container.set_tab_metadata(tab_container.get_current_tab(), deck.id) + inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested) ## Closes the current tab in [member tab_container] func close_current_tab() -> void: @@ -135,8 +135,9 @@ func _on_file_dialog_open_files(paths: PackedStringArray) -> void: var inst: DeckRendererGraphEdit = DECK_SCENE.instantiate() inst.deck = deck tab_container.add_content(inst, "Deck %s" % (tab_container.get_tab_count() + 1)) + tab_container.set_tab_metadata(tab_container.get_current_tab(), deck.id) inst.initialize_from_deck() - inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested.bind(deck)) + inst.group_enter_requested.connect(_on_deck_renderer_group_enter_requested) ## Gets the currently active [Deck] from [member tab_container] func get_active_deck() -> Deck: @@ -165,14 +166,15 @@ func disconnect_file_dialog_signals() -> void: ## Connected to [signal DeckRenderGraphEdit.group_entered_request] to allow entering ## groups based off the given [param group_id] and [param deck]. As well as adding ## a corresponding tab to [member tab_container] -func _on_deck_renderer_group_enter_requested(group_id: String, deck: Deck) -> void: - var group_deck := deck.get_group(group_id) +func _on_deck_renderer_group_enter_requested(group_id: String) -> void: + #var group_deck := deck.get_group(group_id) + var group_deck := DeckHolder.get_deck(group_id) var deck_renderer: DeckRendererGraphEdit = DECK_SCENE.instantiate() deck_renderer.deck = group_deck deck_renderer.initialize_from_deck() tab_container.add_content(deck_renderer, "Group %s" % (tab_container.get_tab_count() + 1)) - tab_container.set_tab_metadata(tab_container.get_current_tab(), group_deck) - deck_renderer.group_enter_requested.connect(_on_deck_renderer_group_enter_requested.bind(deck_renderer.deck)) + tab_container.set_tab_metadata(tab_container.get_current_tab(), group_id) + deck_renderer.group_enter_requested.connect(_on_deck_renderer_group_enter_requested) func _on_connections_id_pressed(id: int) -> void: