diff --git a/src/UI/Dialogs/ImageEffects/ColorCurvesDialog.tscn b/src/UI/Dialogs/ImageEffects/ColorCurvesDialog.tscn index e14f5c577..5a03d37d7 100644 --- a/src/UI/Dialogs/ImageEffects/ColorCurvesDialog.tscn +++ b/src/UI/Dialogs/ImageEffects/ColorCurvesDialog.tscn @@ -2,19 +2,19 @@ [ext_resource type="PackedScene" uid="uid://bybqhhayl5ay5" path="res://src/UI/Dialogs/ImageEffects/ImageEffectParent.tscn" id="1_4g7xo"] [ext_resource type="Script" path="res://src/UI/Dialogs/ImageEffects/ColorCurvesDialog.gd" id="2_xkivc"] -[ext_resource type="Script" path="res://src/UI/Nodes/CurveEditor/CurveEdit.gd" id="3_fp0lm"] +[ext_resource type="Script" path="res://src/UI/Nodes/CurveEditor/CurveEdit.gd" id="3_3yyhs"] -[sub_resource type="Curve" id="Curve_p6hdh"] +[sub_resource type="Curve" id="Curve_gvi51"] _data = [Vector2(0, 0), 0.0, 1.0, 0, 1, Vector2(1, 1), 1.0, 0.0, 1, 0] point_count = 2 [node name="ColorCurvesDialog" instance=ExtResource("1_4g7xo")] title = "Color Curves" -size = Vector2i(362, 440) +size = Vector2i(362, 481) script = ExtResource("2_xkivc") [node name="VBoxContainer" parent="." index="3"] -offset_bottom = 391.0 +offset_bottom = 432.0 [node name="ShowAnimate" parent="VBoxContainer" index="0"] visible = false @@ -51,10 +51,11 @@ popup/item_6/id = 6 popup/item_7/text = "Value" popup/item_7/id = 7 -[node name="CurveEdit" type="Control" parent="VBoxContainer" index="4"] +[node name="CurveEdit" type="VBoxContainer" parent="VBoxContainer" index="4"] +custom_minimum_size = Vector2(32, 150) layout_mode = 2 size_flags_vertical = 3 -script = ExtResource("3_fp0lm") -curve = SubResource("Curve_p6hdh") +script = ExtResource("3_3yyhs") +curve = SubResource("Curve_gvi51") [connection signal="item_selected" from="VBoxContainer/ColorOptions/ChannelOptionButton" to="." method="_on_channel_option_button_item_selected"] diff --git a/src/UI/Nodes/CurveEditor/CurveControlPoint.gd b/src/UI/Nodes/CurveEditor/CurveControlPoint.gd index 0bbcdc434..af97a9abd 100644 --- a/src/UI/Nodes/CurveEditor/CurveControlPoint.gd +++ b/src/UI/Nodes/CurveEditor/CurveControlPoint.gd @@ -18,7 +18,8 @@ var max_y: float var left_slope: CurveEditTangentPoint var right_slope: CurveEditTangentPoint -@onready var parent := get_parent() as CurveEdit +@onready var parent := get_parent() as Control +@onready var grandparent := parent.get_parent() as CurveEdit func _ready() -> void: @@ -45,7 +46,7 @@ func _draw() -> void: func initialize(curve: Curve, index: int) -> void: if not is_instance_valid(parent): await ready - position = parent.transform_point(curve.get_point_position(index)) - OFFSET + position = grandparent.transform_point(curve.get_point_position(index)) - OFFSET var left_tangent := curve.get_point_left_tangent(index) var right_tangent := curve.get_point_right_tangent(index) if left_tangent != INF: @@ -81,7 +82,7 @@ func _on_gui_input(event: InputEvent) -> void: moving = true else: moving = false - parent.update_controls() + grandparent.update_controls() elif event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: removed.emit(get_index()) elif moving and event is InputEventMouseMotion: diff --git a/src/UI/Nodes/CurveEditor/CurveEdit.gd b/src/UI/Nodes/CurveEditor/CurveEdit.gd index 21f533166..e7c4ef154 100644 --- a/src/UI/Nodes/CurveEditor/CurveEdit.gd +++ b/src/UI/Nodes/CurveEditor/CurveEdit.gd @@ -6,7 +6,7 @@ # https://github.com/RodZill4/material-maker/blob/master/material_maker/widgets/curve_edit/curve_editor.gd @tool class_name CurveEdit -extends Control +extends VBoxContainer signal value_changed(value: Curve) @@ -17,23 +17,111 @@ signal value_changed(value: Curve) queue_redraw() update_controls() +## Array of dictionaries of key [String] and value [Array] of type [CurveEdit.CurvePoint]. +var presets: Array[Dictionary] = [ + {"Linear": [CurvePoint.new(0.0, 0.0, 0.0, 1.0), CurvePoint.new(1.0, 1.0, 1.0, 0.0)]}, + { + "Ease out": + [ + CurvePoint.new(0.0, 0.0, 0.0, 4.0), + CurvePoint.new(0.292893, 0.707107, 1.0, 1.0), + CurvePoint.new(1.0, 1.0, 0.0, 0.0) + ] + }, + { + "Ease in out": + [ + CurvePoint.new(0.0, 0.0, 0.0, 0.0), + CurvePoint.new(0.5, 0.5, 3.0, 3.0), + CurvePoint.new(1.0, 1.0, 0.0, 0.0) + ] + }, + { + "Ease in": + [ + CurvePoint.new(0.0, 0.0, 0.0, 0.0), + CurvePoint.new(0.707107, 0.292893, 1.0, 1.0), + CurvePoint.new(1.0, 1.0, 4.0, 0.0) + ] + }, + { + "Sawtooth": + [ + CurvePoint.new(0.0, 0.0, 0.0, 2.0), + CurvePoint.new(0.5, 1.0, 2.0, -2.0), + CurvePoint.new(1.0, 0.0, -2.0, 0.0) + ] + }, + { + "Bounce": + [ + CurvePoint.new(0.0, 0.0, 0.0, 5.0), + CurvePoint.new(0.15, 0.65, 2.45201, 2.45201), + CurvePoint.new(0.5, 1.0, 0.0, 0.0), + CurvePoint.new(0.85, 0.65, -2.45201, -2.45201), + CurvePoint.new(1.0, 0.0, -5.0, 0.0) + ] + }, + { + "Bevel": + [ + CurvePoint.new(0.0, 0.0, 0.0, 2.38507), + CurvePoint.new(0.292893, 0.707107, 2.34362, 0.428147), + CurvePoint.new(1.0, 1.0, 0.410866, 0.0) + ] + } +] +var curve_editor := Control.new() +var hbox := HBoxContainer.new() + + +class CurvePoint: + var pos: Vector2 + var left_tangent: float + var right_tangent: float + + func _init(x: float, y: float, _left_tangent := 0.0, _right_tangent := 0.0) -> void: + pos = Vector2(x, y) + left_tangent = _left_tangent + right_tangent = _right_tangent + func _ready() -> void: if not is_instance_valid(curve): curve = Curve.new() - gui_input.connect(_on_gui_input) + if custom_minimum_size.is_zero_approx(): + custom_minimum_size = Vector2(32, 150) + curve_editor.gui_input.connect(_on_gui_input) resized.connect(_on_resize) - queue_redraw() - update_controls() + curve_editor.size_flags_vertical = Control.SIZE_EXPAND_FILL + add_child(curve_editor) + add_child(hbox) + var presets_button := MenuButton.new() + presets_button.text = "Presets" + presets_button.flat = false + presets_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + presets_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + presets_button.get_popup().id_pressed.connect(_on_presets_item_selected) + for preset in presets: + presets_button.get_popup().add_item(preset.keys()[0]) + var invert_button := Button.new() + invert_button.text = "Invert" + invert_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + invert_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + invert_button.pressed.connect(_on_invert_button_pressed) + hbox.add_child(presets_button) + hbox.add_child(invert_button) + _on_resize.call_deferred() func update_controls() -> void: - for c in get_children(): - c.queue_free() + for c in curve_editor.get_children(): + if c is CurveEditControlPoint: + c.queue_free() for i in curve.point_count: var p := curve.get_point_position(i) var control_point := CurveEditControlPoint.new() - add_child(control_point) + curve_editor.add_child(control_point) control_point.initialize(curve, i) control_point.position = transform_point(p) - control_point.OFFSET if i == 0 or i == curve.point_count - 1: @@ -41,7 +129,7 @@ func update_controls() -> void: control_point.position.x, control_point.position.x, -control_point.OFFSET.y, - size.y - control_point.OFFSET.y + available_size().y - control_point.OFFSET.y ) if i == 0: control_point.set_control_point_visibility(true, false) @@ -51,7 +139,7 @@ func update_controls() -> void: var min_x := transform_point(curve.get_point_position(i - 1)).x + 1 var max_x := transform_point(curve.get_point_position(i + 1)).x - 1 control_point.set_constraint( - min_x, max_x, -control_point.OFFSET.y, size.y - control_point.OFFSET.y + min_x, max_x, -control_point.OFFSET.y, available_size().y - control_point.OFFSET.y ) control_point.moved.connect(_on_control_point_moved) control_point.removed.connect(_on_control_point_removed) @@ -66,12 +154,18 @@ static func to_texture(from_curve: Curve, width := 256) -> CurveTexture: return texture +func available_size() -> Vector2: + if curve_editor.size.is_zero_approx(): + return Vector2.ONE + return curve_editor.size + + func transform_point(p: Vector2) -> Vector2: - return (Vector2(0.0, 1.0) + Vector2(1.0, -1.0) * p) * size + return (Vector2(0.0, 1.0) + Vector2(1.0, -1.0) * p) * available_size() func reverse_transform_point(p: Vector2) -> Vector2: - return Vector2(0.0, 1.0) + Vector2(1.0, -1.0) * p / size + return Vector2(0.0, 1.0) + Vector2(1.0, -1.0) * p / available_size() func _draw() -> void: @@ -89,8 +183,8 @@ func _draw() -> void: if show_axes: for i in range(5): var p := transform_point(0.25 * Vector2(i, i)) - draw_line(Vector2(p.x, 0), Vector2(p.x, size.y - 1), axes_color) - draw_line(Vector2(0, p.y), Vector2(size.x - 1, p.y), axes_color) + draw_line(Vector2(p.x, 0), Vector2(p.x, available_size().y - 1), axes_color) + draw_line(Vector2(0, p.y), Vector2(available_size().x - 1, p.y), axes_color) var points := PackedVector2Array() for i in range(curve.point_count - 1): var p1 := curve.get_point_position(i) @@ -117,16 +211,16 @@ func _draw() -> void: func _on_control_point_moved(index: int) -> void: - var control_point := get_child(index) as CurveEditControlPoint + var control_point := curve_editor.get_child(index) as CurveEditControlPoint var new_point := reverse_transform_point(control_point.position + control_point.OFFSET) curve.set_point_offset(index, new_point.x) curve.set_point_value(index, new_point.y) if is_instance_valid(control_point.left_slope): - var slope_vector := control_point.left_slope.position / size + var slope_vector := control_point.left_slope.position / available_size() if slope_vector.x != 0: curve.set_point_left_tangent(index, -slope_vector.y / slope_vector.x) if is_instance_valid(control_point.right_slope): - var slope_vector := control_point.right_slope.position / size + var slope_vector := control_point.right_slope.position / available_size() if slope_vector.x != 0: curve.set_point_right_tangent(index, -slope_vector.y / slope_vector.x) queue_redraw() @@ -151,3 +245,23 @@ func _on_gui_input(event: InputEvent) -> void: func _on_resize() -> void: queue_redraw() update_controls() + + +func _on_presets_item_selected(index: int) -> void: + curve.clear_points() + var preset_points: Array = presets[index].values()[0] + for point: CurvePoint in preset_points: + curve.add_point(point.pos, point.left_tangent, point.right_tangent) + curve = curve # Call setter + + +func _on_invert_button_pressed() -> void: + var copy_curve := curve.duplicate() as Curve + curve.clear_points() + for i in copy_curve.point_count: + var point := copy_curve.get_point_position(i) + point.y = 1.0 - point.y + var left_tangent := -copy_curve.get_point_left_tangent(i) + var right_tangent := -copy_curve.get_point_right_tangent(i) + curve.add_point(point, left_tangent, right_tangent) + curve = curve # Call setter diff --git a/src/UI/Nodes/CurveEditor/CurveTangentPoint.gd b/src/UI/Nodes/CurveEditor/CurveTangentPoint.gd index 99816addd..9cdb51a20 100644 --- a/src/UI/Nodes/CurveEditor/CurveTangentPoint.gd +++ b/src/UI/Nodes/CurveEditor/CurveTangentPoint.gd @@ -4,7 +4,7 @@ class_name CurveEditTangentPoint extends Control -const OFFSET := -Vector2(0, 0) +const OFFSET := Vector2(0, 0) @export var distance := 30