mat_maker_gd/material_maker/widgets/graph_minimap/graph_minimap.gd

268 lines
7.5 KiB
GDScript

extends Panel
# Borrowed from ProtonGraph
# Courtesy of pycbouh, original code can be found there:
# https://gist.github.com/pycbouh/7a88b55697138b646f69da0de40f71ac
# Node references
var graph_edit : GraphEdit
var graph_hscroll : HScrollBar
var graph_vscroll : VScrollBar
var minimap_button : Button
# Public properties
export var toolbar_icon : Texture
# Private properties
var _graph_nodes : Array = [] # of Dictionary
var _graph_lines : Array = [] # of Dictionary
var _camera_position : Vector2 = Vector2(100, 50)
var _camera_size : Vector2 = Vector2(200, 200)
var _graph_proportions : Vector2 = Vector2(1, 1)
var _zoom_level : float = 1.0
var _map_padding : Vector2 = Vector2(5, 5)
var _graph_padding : Vector2 = Vector2(0, 0)
var _is_pressing : bool = false
func _enter_tree() -> void:
_update_node_references()
func _ready() -> void:
_update_theme()
_update_map()
if (graph_edit):
minimap_button = ToolButton.new()
minimap_button.icon = toolbar_icon
minimap_button.hint_tooltip = "Toggle graph minimap."
graph_edit.get_zoom_hbox().add_child(minimap_button)
minimap_button.connect("pressed", self, "_on_minimap_button_pressed")
graph_edit.connect("draw", self, "_on_graph_edit_changed")
graph_edit.connect("update_minimap", self, "_on_graph_edit_changed")
# Update size based on editor scale
rect_min_size *= 1.0 # EditorUtil.get_editor_scale()
update()
func _gui_input(event) -> void:
if (event is InputEventMouseButton && event.button_index == BUTTON_LEFT):
if (event.is_pressed()):
_is_pressing = true
var click_location = _convert_to_graph_position(event.position - _map_padding) - _graph_padding
if (graph_edit):
var scroll_offset = get_scroll_offset()
graph_edit.scroll_offset = click_location + scroll_offset - _camera_size / 2
elif (_is_pressing):
_is_pressing = false
accept_event()
elif (event is InputEventMouseMotion && _is_pressing):
var click_location = _convert_to_graph_position(event.position - _map_padding) - _graph_padding
if (graph_edit):
var scroll_offset = get_scroll_offset()
graph_edit.scroll_offset = click_location + scroll_offset - _camera_size / 2
accept_event()
func _draw() -> void:
var centering_offset = _convert_from_graph_position(_graph_padding)
var map_offset = _map_padding + centering_offset
for node in _graph_nodes:
var position = _convert_from_graph_position(node.position) + map_offset
var size = _convert_from_graph_position(node.size)
var node_shape = Rect2(position - size / 2, size)
if (node.is_comment):
var comment_background = node.node_color.linear_interpolate(Color.black, 0.65)
draw_rect(node_shape, comment_background, true)
draw_rect(node_shape, node.node_color, false)
else:
draw_rect(node_shape, node.node_color)
for line in _graph_lines:
var from_position = _convert_from_graph_position(line.from_position) + map_offset
var to_position = _convert_from_graph_position(line.to_position) + map_offset
draw_line(from_position, to_position, line.from_color)
var camera_center = _convert_from_graph_position(_camera_position + _camera_size / 2) + map_offset
var camera_viewport = _convert_from_graph_position(_camera_size)
var camera_position = (camera_center - camera_viewport / 2)
draw_rect(Rect2(camera_position, camera_viewport), Color(0.65, 0.65, 0.65, 0.2), true)
draw_rect(Rect2(camera_position, camera_viewport), Color(0.65, 0.65, 0.65, 0.45), false)
func _update_theme() -> void:
if (!Engine.editor_hint || !is_inside_tree()):
return
modulate.a = 0.85
func _update_map() -> void:
if (!graph_edit):
return
# Update graph spatial information
var scroll_offset = get_scroll_offset()
var graph_size = get_graph_size()
_zoom_level = graph_edit.zoom
_camera_position = graph_edit.scroll_offset - scroll_offset
_camera_size = graph_edit.rect_size
var render_size = get_render_size()
var target_ratio = render_size.x / render_size.y
var graph_ratio = graph_size.x / graph_size.y
_graph_proportions = graph_size
_graph_padding = Vector2(0, 0)
if (graph_ratio > target_ratio):
_graph_proportions.x = graph_size.x
_graph_proportions.y = graph_size.x / target_ratio
_graph_padding.y = abs(graph_size.y - _graph_proportions.y) / 2
else:
_graph_proportions.x = graph_size.y * target_ratio
_graph_proportions.y = graph_size.y
_graph_padding.x = abs(graph_size.x - _graph_proportions.x) / 2
# Update node information
_graph_nodes = []
_graph_lines = []
for child in graph_edit.get_children():
if !(child is GraphNode):
continue
var node_data := {
"position": (child.offset + child.rect_size / 2) * _zoom_level - scroll_offset,
"size": child.rect_size * _zoom_level,
"node_color": Color.white,
"is_comment": child.comment,
}
var child_color = child.get("minimap_color")
if (child_color):
node_data.node_color = child_color
_graph_nodes.append(node_data)
for connection in graph_edit.get_connection_list():
var from_child = graph_edit.get_node_or_null(connection.from)
var to_child = graph_edit.get_node_or_null(connection.to)
if (!from_child || !to_child):
continue # We got caught in between two resources switching, some data is outdated
var from_slot_position = from_child.rect_size if from_child.comment else from_child.rect_size / 2
var to_slot_position = 0 if from_child.comment else to_child.rect_size / 2
var line_data := {
"from_position": (from_child.offset + from_slot_position) * _zoom_level - scroll_offset,
"to_position": (to_child.offset + to_slot_position) * _zoom_level - scroll_offset,
"from_color": Color.white,
"to_color": Color.white,
}
var from_child_color = from_child.get("minimap_color")
if (from_child_color):
line_data.from_color = from_child_color
var to_child_color = to_child.get("minimap_color")
if (to_child_color):
line_data.to_color = to_child_color
_graph_lines.append(line_data)
update()
func _update_node_references() -> void:
var parent_node = get_parent()
if !(parent_node is GraphEdit):
return
graph_edit = parent_node
var top_layer
for c in graph_edit.get_children():
if c.get_class() == "GraphEditFilter":
top_layer = c
break
for c in top_layer.get_children():
if c is HScrollBar:
graph_hscroll = c
elif c is VScrollBar:
graph_vscroll = c
### Helpers
func _convert_from_graph_position(graph_position : Vector2) -> Vector2:
var position = Vector2(0, 0)
var render_size = get_render_size()
position.x = graph_position.x * render_size.x / _graph_proportions.x
position.y = graph_position.y * render_size.y / _graph_proportions.y
return position
func _convert_to_graph_position(position : Vector2) -> Vector2:
var graph_position = Vector2(0, 0)
var render_size = get_render_size()
graph_position.x = position.x * _graph_proportions.x / render_size.x
graph_position.y = position.y * _graph_proportions.y / render_size.y
return graph_position
func get_render_size() -> Vector2:
if (!is_inside_tree()):
return Vector2.ZERO
return rect_size - 2 * _map_padding
func get_scroll_offset() -> Vector2:
if (!graph_hscroll || !graph_vscroll):
return Vector2(0, 0)
return Vector2(graph_hscroll.min_value, graph_vscroll.min_value)
func get_graph_size() -> Vector2:
if (!graph_hscroll || !graph_vscroll):
return Vector2(1, 1)
var scroll_offset = get_scroll_offset()
var graph_size = Vector2(graph_hscroll.max_value, graph_vscroll.max_value) - scroll_offset
if (graph_size.x == 0):
graph_size.x = 1
if (graph_size.y == 0):
graph_size.y = 1
return graph_size
### Event handlers
func _on_graph_edit_changed() -> void:
_update_map()
func _on_minimap_button_pressed() -> void:
if (visible):
hide()
else:
show()