some shake
This commit is contained in:
265
addons/shaker/src/shaker_graph.gd
Normal file
265
addons/shaker/src/shaker_graph.gd
Normal file
@@ -0,0 +1,265 @@
|
||||
@tool
|
||||
extends Panel
|
||||
|
||||
const TIME_LINE_SCRIPT: GDScript = preload("res://addons/shaker/src/shaker_timeline.gd")
|
||||
const ShakerBase3d = preload("res://addons/shaker/src/Vector3/ShakerBase3D.gd")
|
||||
const ShakerBase2d = preload("res://addons/shaker/src/Vector2/ShakerBase2D.gd")
|
||||
|
||||
# Graph properties
|
||||
var graph_points:Array[PackedFloat32Array]
|
||||
var y_scale: float = 32.0
|
||||
var width: float = 2.0
|
||||
var shake: Resource
|
||||
var graph_offset: Vector2 = Vector2(15.0, 0.0)
|
||||
|
||||
# Private variables
|
||||
var _graph_min: float = -1.0
|
||||
var _graph_max: float = 1.0
|
||||
var _graph_max_total: float = 1.0
|
||||
var layout_box: HBoxContainer = HBoxContainer.new()
|
||||
var axis_button: OptionButton = OptionButton.new()
|
||||
var fit_button: Button = Button.new()
|
||||
var category_button: OptionButton
|
||||
var time_line: Control
|
||||
var flip_y: bool = true
|
||||
var selected_category: int = 0
|
||||
var graph_pressing: bool = false
|
||||
var graph_middle_pressing: bool = false
|
||||
var point_color_by_axis:Array[Color] = [Color.RED, Color.GREEN, Color.DEEP_SKY_BLUE]
|
||||
var _unselected_opacity:float = .10
|
||||
|
||||
# Graph time offset property
|
||||
var graph_time_offset: float = 0.0:
|
||||
set = set_graph_time_offset,
|
||||
get = get_graph_time_offset
|
||||
|
||||
# Called when the node enters the scene tree for the first timeX
|
||||
func _ready() -> void:
|
||||
shake.property_changed.connect(on_property_changed)
|
||||
|
||||
clip_contents = true
|
||||
_setup_timeline()
|
||||
_setup_layout_box()
|
||||
_setup_fit_button()
|
||||
_setup_category_button()
|
||||
_setup_axis_button()
|
||||
|
||||
_update_graph()
|
||||
_on_fit_button_clicked()
|
||||
|
||||
# Sets up the timeline
|
||||
func _setup_timeline() -> void:
|
||||
if shake is ShakerPresetBase:
|
||||
time_line = TIME_LINE_SCRIPT.new()
|
||||
time_line.GRAPH = self
|
||||
add_child(time_line)
|
||||
|
||||
# Sets up the layout box
|
||||
func _setup_layout_box() -> void:
|
||||
add_child(layout_box)
|
||||
layout_box.set_anchors_preset(Control.PRESET_TOP_WIDE)
|
||||
layout_box.custom_minimum_size.y = 16
|
||||
layout_box.alignment = BoxContainer.ALIGNMENT_END
|
||||
|
||||
# Sets up the fit button
|
||||
func _setup_fit_button() -> void:
|
||||
layout_box.add_child(fit_button)
|
||||
fit_button.text = "Fit"
|
||||
fit_button.pressed.connect(_on_fit_button_clicked)
|
||||
|
||||
# Sets up the category button
|
||||
func _setup_category_button() -> void:
|
||||
if shake is ShakerPresetBase:
|
||||
category_button = OptionButton.new()
|
||||
category_button.item_selected.connect(_on_category_selected)
|
||||
layout_box.add_child(category_button)
|
||||
category_button.custom_minimum_size = Vector2(16, 16)
|
||||
for _category_index in shake.Categories.size():
|
||||
var _category_name: StringName = shake.Categories.keys()[_category_index]
|
||||
var _category_value: int = shake.Categories.values()[_category_index]
|
||||
category_button.add_item(_category_name, _category_value)
|
||||
if shake.Categories.size() > 0: category_button.select(0)
|
||||
|
||||
# Sets up the axis button
|
||||
func _setup_axis_button() -> void:
|
||||
if axis_button.get_parent() != layout_box:
|
||||
layout_box.add_child(axis_button)
|
||||
axis_button.position = Vector2(-32, 4)
|
||||
axis_button.custom_minimum_size = Vector2(16, 16)
|
||||
axis_button.item_selected.connect(_axis_selected)
|
||||
axis_button.add_theme_color_override("background_color", Color.GREEN)
|
||||
|
||||
var selected:int = max(axis_button.get_selected_id(), 0)
|
||||
axis_button.clear()
|
||||
var Axis:Array = []
|
||||
|
||||
if shake is ShakerTypeBase:
|
||||
Axis = shake.GraphAxis.keys()
|
||||
elif shake is ShakerPresetBase:
|
||||
var shakes:Array = shake.get_shakes_by_category(category_button.selected)
|
||||
if shakes.size() > 0 && shakes[0]:
|
||||
Axis = shakes[0].get_script().GraphAxis.keys()
|
||||
for axis in Axis:
|
||||
axis_button.add_item(axis)
|
||||
selected = min(selected, Axis.size())
|
||||
if Axis.size() > 0:
|
||||
axis_button.select(selected)
|
||||
|
||||
# Updates the graph
|
||||
func _update_graph() -> void:
|
||||
graph_points.clear()
|
||||
if not axis_button.get_selected_id() < 0:
|
||||
var _baked: float = round(shake.bake_internal)
|
||||
_graph_min = 0.0
|
||||
_graph_max = 0.0
|
||||
graph_points.resize(axis_button.item_count)
|
||||
for axis_index in axis_button.item_count:
|
||||
for i in _baked + 1:
|
||||
var _args: Array = [graph_time_offset + (i / _baked)]
|
||||
if shake is ShakerPresetBase:
|
||||
_args.append(selected_category)
|
||||
var _val = shake.callv("get_value", _args)
|
||||
if typeof(_val) != TYPE_FLOAT: _val = _val[axis_index]
|
||||
var _result: float = _val * (-1 if flip_y else 1)
|
||||
graph_points[axis_index].append(_result)
|
||||
_graph_min = min(_graph_min, _result)
|
||||
_graph_max = max(_graph_max, _result)
|
||||
_graph_max_total = max(abs(_graph_max), abs(_graph_min))
|
||||
|
||||
# Draws the graph
|
||||
func _draw() -> void:
|
||||
_draw_zero_line()
|
||||
_draw_min_max()
|
||||
_draw_graph_points()
|
||||
_draw_graph_info()
|
||||
|
||||
# Draws the zero line
|
||||
func _draw_zero_line() -> void:
|
||||
var font_size: int = 8
|
||||
draw_line(Vector2(0, size.y * 0.5), Vector2(size.x, size.y * 0.5), Color.DIM_GRAY, 1, false)
|
||||
draw_string(ThemeDB.fallback_font, Vector2(5.0, size.y * 0.5 + font_size * 0.5), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color.DIM_GRAY, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL)
|
||||
|
||||
# Draws the min/max values
|
||||
func _draw_min_max() -> void:
|
||||
var font_size: int = 8
|
||||
var _view_percent: float = (y_scale * _graph_max_total) / (size.y * 0.5)
|
||||
if (_view_percent * _graph_max_total) > 0.25:
|
||||
var _padding: int = 10
|
||||
var _up_offset: float = max((-size.y * 0.5) * min(_view_percent, 1.0), -size.y * 0.5 + _padding)
|
||||
var _down_offset: float = min((size.y * 0.5) * min(_view_percent, 1.0), size.y * 0.5 - _padding)
|
||||
var _min_max_percent: float = 1.0 / max(_view_percent, 1.0)
|
||||
draw_string(ThemeDB.fallback_font, Vector2(5.0, size.y * 0.5 + font_size * 0.5 + _up_offset), "%.2f" % (1.0 * _graph_max_total * _min_max_percent), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color.DIM_GRAY, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL)
|
||||
draw_string(ThemeDB.fallback_font, Vector2(5.0, size.y * 0.5 + font_size * 0.5 + _down_offset), "%.2f" % (1.0 * _graph_min * _min_max_percent), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color.DIM_GRAY, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL)
|
||||
|
||||
# Draws the graph points
|
||||
func _draw_graph_points() -> void:
|
||||
for axis_index in graph_points.size():
|
||||
var _point_length:int = graph_points[axis_index].size()
|
||||
var _point_color:Color = point_color_by_axis[fmod(axis_index, point_color_by_axis.size())]
|
||||
var alpha:float = _unselected_opacity if axis_index != axis_button.get_selected_id() else 1.0
|
||||
var _final_size: Vector2 = size - graph_offset
|
||||
var _offset: Vector2 = Vector2(1, y_scale)
|
||||
for point_index in _point_length:
|
||||
var _size_offset = Vector2((_final_size.x / (_point_length)) * point_index, _final_size.y * 0.5)
|
||||
var point:float = graph_points[axis_index][point_index]
|
||||
if point_index < _point_length - 1:
|
||||
var _size_offset_next = Vector2((_final_size.x / (_point_length)) * (point_index + 1), _final_size.y * 0.5)
|
||||
var point_next:float = graph_points[axis_index][point_index + 1]
|
||||
var _final_point_1: Vector2 = graph_offset + _size_offset + Vector2(0.0, point) * _offset
|
||||
var _final_point_2: Vector2 = graph_offset + _size_offset_next + Vector2(0.0, point_next) * _offset
|
||||
draw_line(_final_point_1, _final_point_2, _point_color * Color(1,1,1, alpha), width, false)
|
||||
|
||||
# Draws the graph info
|
||||
func _draw_graph_info() -> void:
|
||||
var font_size: int = 8
|
||||
var _text = "Zoom: %.2f | Zoom IN / OUT with mouse scroll" % (y_scale / (size.y * 0.5 / _graph_max_total))
|
||||
var _text_size: float = _text.length() * font_size * 0.5
|
||||
draw_string(ThemeDB.fallback_font, Vector2(size.x - _text_size, size.y - 8.0), _text, HORIZONTAL_ALIGNMENT_RIGHT, -1, font_size, Color.GRAY, TextServer.JUSTIFICATION_NONE, TextServer.DIRECTION_AUTO, TextServer.ORIENTATION_HORIZONTAL)
|
||||
|
||||
# Handles GUI input
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
_handle_mouse_button(event)
|
||||
if event is InputEventMouseMotion:
|
||||
_handle_mouse_motion(event)
|
||||
|
||||
# Handles mouse button input
|
||||
func _handle_mouse_button(event: InputEventMouseButton) -> void:
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||
y_scale -= 1.0 / _graph_max_total
|
||||
accept_event()
|
||||
elif event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||
y_scale += 1.0 / _graph_max_total
|
||||
accept_event()
|
||||
elif event.button_index == MOUSE_BUTTON_LEFT:
|
||||
if shake is ShakerPresetBase:
|
||||
graph_pressing = event.pressed
|
||||
update_timeline_position(event.position)
|
||||
if event.button_index == MOUSE_BUTTON_MIDDLE:
|
||||
if shake is ShakerPresetBase:
|
||||
graph_middle_pressing = event.pressed
|
||||
y_scale = max(y_scale, 1)
|
||||
queue_redraw()
|
||||
|
||||
# Handles mouse motion input
|
||||
func _handle_mouse_motion(event: InputEventMouseMotion) -> void:
|
||||
if graph_pressing:
|
||||
update_timeline_position(event.position)
|
||||
if graph_middle_pressing:
|
||||
graph_time_offset += -(event.relative.x / size.x)
|
||||
graph_time_offset = max(graph_time_offset, 0.0)
|
||||
|
||||
# Updates the timeline position
|
||||
func update_timeline_position(pos: Vector2) -> void:
|
||||
if shake is ShakerPresetBase:
|
||||
var _press_percent: float = max(((pos.x) - graph_offset.x) / (size.x - graph_offset.x), 0.0)
|
||||
if shake.parent is ShakerBase3d || shake.parent is ShakerBase2d:
|
||||
var _shaker_component = shake.parent
|
||||
if _shaker_component != null:
|
||||
_shaker_component.set_progress(_press_percent + graph_time_offset)
|
||||
|
||||
# Called when a property changes
|
||||
func on_property_changed(_name: StringName) -> void:
|
||||
_setup_axis_button()
|
||||
_update_graph()
|
||||
queue_redraw()
|
||||
if is_inf(y_scale) or is_nan(y_scale):
|
||||
_on_fit_button_clicked()
|
||||
|
||||
# Called when an axis is selected
|
||||
func _axis_selected(item: int) -> void:
|
||||
_update_graph()
|
||||
_on_fit_button_clicked()
|
||||
|
||||
# Called when the fit button is clicked
|
||||
func _on_fit_button_clicked() -> void:
|
||||
y_scale = (size.y * 0.5) / _graph_max_total
|
||||
graph_time_offset = 0.0
|
||||
queue_redraw()
|
||||
|
||||
# Called when a category is selected
|
||||
func _on_category_selected(item: int) -> void:
|
||||
selected_category = item
|
||||
_setup_axis_button()
|
||||
_update_graph()
|
||||
_on_fit_button_clicked()
|
||||
|
||||
# Setter for graph_time_offset
|
||||
func set_graph_time_offset(value: float) -> void:
|
||||
graph_time_offset = value
|
||||
if time_line != null:
|
||||
time_line.queue_redraw()
|
||||
_update_graph()
|
||||
queue_redraw()
|
||||
|
||||
# Getter for graph_time_offset
|
||||
func get_graph_time_offset() -> float:
|
||||
return graph_time_offset
|
||||
|
||||
func select_axis(index:int) -> void:
|
||||
if axis_button.has_selectable_items():
|
||||
axis_button.select(index)
|
||||
|
||||
func select_category(index:int) -> void:
|
||||
if category_button.has_selectable_items():
|
||||
category_button.select(index)
|
||||
Reference in New Issue
Block a user