add a method to make group instances unique, making them independent (#143)

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>
This commit is contained in:
Lera Elvoé 2024-04-11 14:56:33 +00:00 committed by yagich
parent 2ac9db8350
commit a518e46b0f
6 changed files with 87 additions and 9 deletions

View file

@ -25,7 +25,7 @@ var id: String = ""
## If this is a group, this is the local ID of this instance of the group. ## If this is a group, this is the local ID of this instance of the group.
var instance_id: String = "" var instance_id: String = ""
## The parent deck of this deck, if this is a group. ## The parent deck of this deck, if this is a group.
#var _belonging_to: Deck # for groups var _belonging_to: String = "" # for groups
## The ID of this group's input node. Used only if [member is_group] is [code]true[/code]. ## The ID of this group's input node. Used only if [member is_group] is [code]true[/code].
var group_input_node: String var group_input_node: String
## The ID of this group's input node. Used only if [member is_group] is [code]true[/code]. ## The ID of this group's input node. Used only if [member is_group] is [code]true[/code].
@ -264,7 +264,7 @@ func group_nodes(nodes_to_group: Array) -> Deck:
return x._id return x._id
) )
var group := DeckHolder.add_empty_group() var group := DeckHolder.add_empty_group(id)
var midpoint := Vector2() var midpoint := Vector2()
@ -436,8 +436,11 @@ func paste_nodes_from_dict(nodes_to_paste: Dictionary, position: Vector2 = Vecto
nodes_to_paste.nodes[node_id].incoming_connections = incoming_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 node := DeckNode.from_dict(nodes_to_paste.nodes[node_id])
var group_needs_unique := false
if node.node_type == "group_node": if node.node_type == "group_node":
var group := DeckHolder.make_new_group_instance(node.group_id) 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 node.group_instance_id = group.instance_id
group.get_node(group.group_input_node).group_node = node group.get_node(group.group_input_node).group_node = node
group.get_node(group.group_output_node).group_node = node group.get_node(group.group_output_node).group_node = node
@ -445,6 +448,8 @@ func paste_nodes_from_dict(nodes_to_paste: Dictionary, position: Vector2 = Vecto
node.output_node = group.get_node(group.group_output_node) node.output_node = group.get_node(group.group_output_node)
node.init_io() node.init_io()
add_node_inst(node, ids_map[node_id]) 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: func paste_nodes_from_json(json: String, position: Vector2 = Vector2()) -> void:
@ -478,6 +483,29 @@ func get_referenced_groups() -> Array[String]:
return res 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. ## Returns a [Dictionary] representation of this deck.
func to_dict(with_meta: bool = true, group_ids: Array = []) -> Dictionary: func to_dict(with_meta: bool = true, group_ids: Array = []) -> Dictionary:
var inner := { var inner := {
@ -536,7 +564,7 @@ static func from_dict(data: Dictionary, path: String = "") -> Deck:
var group_id: String = node.group_id var group_id: String = node.group_id
var group_instance_id: String = node.group_instance_id var group_instance_id: String = node.group_instance_id
var group_data: Dictionary = groups_data[group_id] var group_data: Dictionary = groups_data[group_id]
var group := DeckHolder.add_group_from_dict(group_data, group_id, group_instance_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_input_node).group_node = node
group.get_node(group.group_output_node).group_node = node group.get_node(group.group_output_node).group_node = node
node.init_io() node.init_io()

View file

@ -49,10 +49,11 @@ static func open_deck_from_dict(data: Dictionary, path := "") -> Deck:
return deck return deck
static func add_group_from_dict(data: Dictionary, deck_id: String, instance_id: String) -> Deck: static func add_group_from_dict(data: Dictionary, deck_id: String, instance_id: String, parent: String = "") -> Deck:
var group := Deck.from_dict(data) var group := Deck.from_dict(data)
group.instance_id = instance_id group.instance_id = instance_id
group.is_group = true group.is_group = true
group._belonging_to = parent
group.group_input_node = data.deck.group_input_node group.group_input_node = data.deck.group_input_node
group.group_output_node = data.deck.group_output_node group.group_output_node = data.deck.group_output_node
var instances: Dictionary = decks.get(deck_id, {}) var instances: Dictionary = decks.get(deck_id, {})
@ -64,20 +65,47 @@ static func add_group_from_dict(data: Dictionary, deck_id: String, instance_id:
group.connect_rpc_signals() group.connect_rpc_signals()
signals.deck_added.emit(deck_id) signals.deck_added.emit(deck_id)
groups_emitted.append(deck_id) groups_emitted.append(deck_id)
#print(decks)
return group return group
static func make_new_group_instance(group_id: String) -> Deck: static func make_new_group_instance(group_id: String, parent: String = "") -> Deck:
var group := get_deck(group_id) var group := get_deck(group_id)
var data := group.to_dict() var data := group.to_dict()
return add_group_from_dict(data, group_id, UUID.v4()) return add_group_from_dict(data, group_id, UUID.v4(), parent)
static func add_empty_group() -> Deck: static func make_group_instance_unique(group_id: String, instance_id: String, parent_deck_id: String, group_node_id: String) -> Deck:
if not decks.has(group_id):
return null
var group_node := get_deck(parent_deck_id).get_node(group_node_id)
var inst := get_group_instance(group_id, instance_id)
var new_id := UUID.v4()
var new_inst_id := UUID.v4()
inst.id = new_id
group_node.group_id = new_id
group_node.group_instance_id = new_inst_id
var data := inst.to_dict()
#data.instance_id = new_inst_id
(decks[group_id] as Dictionary).erase(instance_id)
var new_instance := add_group_from_dict(data, new_id, new_inst_id, parent_deck_id)
group_node.init_io()
#TODO: some weird close bug
var instances := inst.get_referenced_group_instances()
for instance: Dictionary in instances:
make_group_instance_unique(instance.group_id, instance.instance_id, instance.parent_id, instance.group_node)
return new_instance
static func add_empty_group(parent: String = "") -> Deck:
var group := Deck.new() var group := Deck.new()
group.is_group = true group.is_group = true
group.id = UUID.v4() group.id = UUID.v4()
group._belonging_to = parent
group.instance_id = UUID.v4() group.instance_id = UUID.v4()
decks[group.id] = {group.instance_id: group} decks[group.id] = {group.instance_id: group}
connect_group_signals(group) connect_group_signals(group)

View file

@ -24,6 +24,7 @@ func _init() -> void:
func _receive(to_input_port: int, data: Variant) -> void: func _receive(to_input_port: int, data: Variant) -> void:
print(_belonging_to.get_reference_count())
var data_to_print := "" var data_to_print := ""
if to_input_port == 1: if to_input_port == 1:
data = await resolve_input_port_value_async(0) data = await resolve_input_port_value_async(0)

View file

@ -48,6 +48,10 @@ func init_io() -> void:
setup_connections() setup_connections()
func make_unique() -> Deck:
return DeckHolder.make_group_instance_unique(group_id, group_instance_id, _belonging_to.id, _id)
func setup_connections() -> void: func setup_connections() -> void:
input_node.ports_updated.connect(recalculate_ports) input_node.ports_updated.connect(recalculate_ports)
output_node.ports_updated.connect(recalculate_ports) output_node.ports_updated.connect(recalculate_ports)
@ -74,6 +78,8 @@ func recalculate_ports() -> void:
func _receive(to_input_port: int, data: Variant): func _receive(to_input_port: int, data: Variant):
var i = DeckHolder.get_group_instance(group_id, group_instance_id).get_node(input_node_id)
#i.send(get_input_ports()[to_input_port].index_of_type, data)
input_node.send(get_input_ports()[to_input_port].index_of_type, data) input_node.send(get_input_ports()[to_input_port].index_of_type, data)

View file

@ -220,6 +220,16 @@ func _gui_input(event: InputEvent) -> void:
refresh_connections() refresh_connections()
get_viewport().set_input_as_handled() get_viewport().set_input_as_handled()
if event.is_action_pressed("debug_make_unique") and get_selected_nodes().size() == 1:
var node: DeckNode = get_selected_nodes().map(
func(x: DeckNodeRendererGraphNode):
return x.node
)[0]
if node.node_type != "group_node":
return
node.make_unique()
if event.is_action_pressed("rename_node") and get_selected_nodes().size() == 1: if event.is_action_pressed("rename_node") and get_selected_nodes().size() == 1:
var node: DeckNodeRendererGraphNode = get_selected_nodes()[0] var node: DeckNodeRendererGraphNode = get_selected_nodes()[0]
var pos := get_viewport_rect().position + get_global_mouse_position() var pos := get_viewport_rect().position + get_global_mouse_position()

View file

@ -45,6 +45,11 @@ toggle_bottom_dock={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":78,"key_label":0,"unicode":110,"echo":false,"script":null) "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":78,"key_label":0,"unicode":110,"echo":false,"script":null)
] ]
} }
debug_make_unique={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194335,"key_label":0,"unicode":0,"echo":false,"script":null)
]
}
[rendering] [rendering]