diff --git a/addons/block_code/block_code_plugin.gd b/addons/block_code/block_code_plugin.gd index d8857282..9471f743 100644 --- a/addons/block_code/block_code_plugin.gd +++ b/addons/block_code/block_code_plugin.gd @@ -45,6 +45,8 @@ const DISABLED_CLASSES := [ func _enter_tree(): + Types.init_cast_graph() + main_panel = MainPanel.instantiate() main_panel.undo_redo = get_undo_redo() diff --git a/addons/block_code/drag_manager/drag_manager.gd b/addons/block_code/drag_manager/drag_manager.gd index 5eb50026..fdc21cf9 100644 --- a/addons/block_code/drag_manager/drag_manager.gd +++ b/addons/block_code/drag_manager/drag_manager.gd @@ -45,22 +45,23 @@ func _process(_delta): if snap_point.block == null: push_error("Warning: a snap point does not reference it's parent block.") continue - if snap_point.block.on_canvas and snap_point.block_type == dragging.block_type: - var snap_global_pos: Vector2 = snap_point.get_global_rect().position - var temp_dist: float = dragging_global_pos.distance_to(snap_global_pos) - if temp_dist < closest_dist: - # Check if any parent node is this node - var is_child: bool = false - var parent = snap_point - while parent is SnapPoint: - if parent.block == dragging: - is_child = true - - parent = parent.block.get_parent() - - if not is_child: - closest_dist = temp_dist - closest_snap_point = snap_point + if snap_point.block.on_canvas: + if Types.can_cast(dragging.block_type, snap_point.block_type): + var snap_global_pos: Vector2 = snap_point.get_global_rect().position + var temp_dist: float = dragging_global_pos.distance_to(snap_global_pos) + if temp_dist < closest_dist: + # Check if any parent node is this node + var is_child: bool = false + var parent = snap_point + while parent is SnapPoint: + if parent.block == dragging: + is_child = true + + parent = parent.block.get_parent() + + if not is_child: + closest_dist = temp_dist + closest_snap_point = snap_point if closest_dist > 80.0: closest_snap_point = null diff --git a/addons/block_code/types/types.gd b/addons/block_code/types/types.gd index e0aa602b..8f8b03e6 100644 --- a/addons/block_code/types/types.gd +++ b/addons/block_code/types/types.gd @@ -14,3 +14,143 @@ enum BlockType { COLOR, NODE } + +const cast_relationships = [ + [BlockType.INT, BlockType.FLOAT, "float(%s)"], + [BlockType.FLOAT, BlockType.INT, "int(%s)"], + [BlockType.INT, BlockType.STRING, "str(%s)"], + [BlockType.FLOAT, BlockType.STRING, "str(%s)"], +] + +# Directed graph, edges are CastGraphEdge +static var cast_graph: Dictionary + + +class CastGraphEdge: + var to: BlockType + var cast_format: String + + func _init(p_to: BlockType, p_cast_format: String): + to = p_to + cast_format = p_cast_format + + +static func init_cast_graph(): + cast_graph = {} + + for rel in cast_relationships: + if not cast_graph.has(rel[0]): + cast_graph[rel[0]] = [] + + if not cast_graph.has(rel[1]): + cast_graph[rel[1]] = [] + + var edges: Array = cast_graph[rel[0]] + + edges.append(CastGraphEdge.new(rel[1], rel[2])) + + +# Graph recursive utils +static var prev: Dictionary +static var dist: Dictionary +const INT_MAX: int = 1000000000 + + +static func dijkstra(source: BlockType): + prev = {} + dist = {} + + var queue := PriorityQueue.new() + + dist[source] = 0 + queue.push(source, 0) + + for v in cast_graph.keys(): + if v != source: + dist[v] = INT_MAX + prev[v] = null + queue.push(v, INT_MAX) + + while not queue.is_empty(): + var u = queue.pop() + + if !cast_graph.has(u): + continue + + for edge in cast_graph[u]: + var v = edge.to + var alt = dist[u] + 1 + if alt < dist[v]: + dist[v] = alt + prev[v] = CastGraphEdge.new(u, edge.cast_format) + queue.update_priority(v, alt) + + +static func can_cast(type: BlockType, parent_type: BlockType) -> bool: + if type == parent_type: + return true + + if cast_graph.has(type) and cast_graph.has(parent_type): + dijkstra(type) + return dist[parent_type] < INT_MAX + return false + + +static func cast(val: String, type: BlockType, parent_type: BlockType): + if type == parent_type: + return val + + if cast_graph.has(type) and cast_graph.has(parent_type): + dijkstra(type) + if dist[parent_type] < INT_MAX: + var prev_edge = prev[parent_type] + var cast_string = "%s" + while prev_edge: + cast_string %= prev_edge.cast_format + if prev.has(prev_edge.to): + prev_edge = prev[prev_edge.to] + else: + prev_edge = null + + return cast_string % val + + return null + + +# TODO: replace with max heap +class PriorityQueue: + var data: Array = [] + + func _init(): + data = [] + + func push(element, priority): + data.append([element, priority]) + _sort() + + func _sort(): + data.sort_custom(func(a, b): a[1] < b[1]) + + func pop(): + if data.size() > 0: + return data.pop_front()[0] + return null + + func peek(): + if data.size() > 0: + return data[0][0] + return null + + func is_empty(): + return data.size() == 0 + + func update_priority(element, priority): + var found_pair = null + for pair in data: + if pair[0] == element: + found_pair = pair + break + + if found_pair: + found_pair[1] = priority + _sort() diff --git a/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd b/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd index 8801ffdb..72bdce83 100644 --- a/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd +++ b/addons/block_code/ui/blocks/utilities/parameter_input/parameter_input.gd @@ -52,7 +52,8 @@ func get_snapped_block() -> Block: func get_string() -> String: var snapped_block: Block = get_snapped_block() if snapped_block: - return snapped_block.get_parameter_string() + var generated_string = snapped_block.get_parameter_string() + return Types.cast(generated_string, snapped_block.block_type, block_type) var text: String = get_plain_text()