class_name Deck var nodes: Dictionary enum Types{ ERROR = -1, BOOL, NUMERIC, STRING, ARRAY, DICTIONARY, } static var type_assoc: Dictionary = { Types.ERROR: DeckType.DeckTypeError, Types.BOOL: DeckType.DeckTypeBool, Types.NUMERIC: DeckType.DeckTypeNumeric, Types.STRING: DeckType.DeckTypeString, Types.ARRAY: DeckType.DeckTypeArray, Types.DICTIONARY: DeckType.DeckTypeDictionary, } var variable_stack: Dictionary = {} var save_path: String = "" var is_group: bool = false var groups: Dictionary = {} #Dictionary[String -> Deck.id, Deck] var id: String = "" var _belonging_to: Deck # for groups var group_input_node: String var group_output_node: String var group_node: String signal node_added(node: DeckNode) signal node_removed(node: DeckNode) 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) 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_added.emit(node) return node func get_node(uuid: String) -> DeckNode: return nodes.get(uuid) func connect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port: int, to_input_port: int) -> bool: # first, check that we can do the type conversion. var type_a: Types = from_node.get_output_ports()[from_output_port].type var type_b: Types = to_node.get_input_ports()[to_input_port].type var err: DeckType = (type_assoc[type_b]).from(type_assoc[type_a].new()) if err is DeckType.DeckTypeError: print(err.error_message) return false # TODO: prevent duplicate connections from_node.add_outgoing_connection(from_output_port, to_node._id, to_input_port) return true func disconnect_nodes(from_node: DeckNode, to_node: DeckNode, from_output_port: int, to_input_port: int) -> void: var hash = {to_node._id: to_input_port}.hash() from_node.remove_outgoing_connection(from_output_port, hash) to_node.remove_incoming_connection(to_input_port) func is_empty() -> bool: return nodes.is_empty() && variable_stack.is_empty() func remove_node(uuid: String) -> void: var node = nodes.get(uuid) nodes.erase(uuid) node_removed.emit(node) func group_nodes(nodes_to_group: Array) -> Deck: if nodes_to_group.is_empty(): return null var node_ids_to_keep := nodes_to_group.map( func(x: DeckNode): 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 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 for from_port: int in node.outgoing_connections: for connection: Dictionary in node.outgoing_connections[from_port]: if !(connection.keys()[0] in node_ids_to_keep): disconnect_nodes(node, get_node(connection.keys()[0]), from_port, connection.values()[0]) midpoint += node.position_as_vector2() remove_node(node._id) group.add_node_inst(node, node._id) midpoint /= nodes_to_group.size() var _group_node := add_node_type("group_node") _group_node.group_id = group_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 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() groups[group_id] = group return group func get_group(uuid: String) -> Deck: return groups.get(uuid) func to_dict(with_meta: bool = true) -> Dictionary: var inner := { "nodes": {}, "variable_stack": variable_stack, "id": id, "groups": {} } for node_id in nodes.keys(): inner["nodes"][node_id] = nodes[node_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["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 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 := deck.add_node_type(nodes_data[node_id].node_type, node_id, false) node._id = node_id node.name = nodes_data[node_id].name node._belonging_to = deck node.position = nodes_data[node_id].position for prop in nodes_data[node_id].props: node.set(prop, nodes_data[node_id].props[prop]) node._pre_connection() for connection_id in nodes_data[node_id].outgoing_connections: var connection_data = nodes_data[node_id].outgoing_connections[connection_id] for connection in connection_data: connection[connection.keys()[0]] = int(connection.values()[0]) node.outgoing_connections[int(connection_id)] = connection_data for connection_id in nodes_data[node_id].incoming_connections: var connection_data = nodes_data[node_id].incoming_connections[connection_id] for connection in connection_data: connection_data[connection] = int(connection_data[connection]) node.incoming_connections[int(connection_id)] = connection_data for i in node.ports.size(): var port_value: Variant if (nodes_data[node_id].port_values as Array).size() <= i: port_value = null else: port_value = (nodes_data[node_id].port_values as Array)[i] node.ports[i].value = port_value for key in nodes_data[node_id].meta: node.set_meta(key, str_to_var(nodes_data[node_id].meta[key])) node._post_load() 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() return deck