From fb60bd788c5650dc65dd23e423f503c72e99c7f4 Mon Sep 17 00:00:00 2001 From: LMW Date: Thu, 7 Aug 2025 11:43:21 +0800 Subject: [PATCH] Improve curve editor --- addons/material_maker/types/curve.gd | 5 +- .../widgets/curve_edit/control_point.gd | 42 ++++++- .../widgets/curve_edit/control_point.tscn | 36 +++--- .../widgets/curve_edit/curve_dialog.gd | 1 + .../widgets/curve_edit/curve_dialog.tscn | 109 +++++++++++++++++- .../widgets/curve_edit/curve_edit.gd | 8 +- .../widgets/curve_edit/curve_edit.tscn | 4 +- .../widgets/curve_edit/curve_editor.gd | 107 ++++++++++++++++- .../widgets/curve_edit/curve_editor.tscn | 6 +- .../widgets/curve_edit/curve_view.gd | 7 +- .../widgets/curve_edit/curve_view.tscn | 4 +- .../widgets/curve_edit/presets_selector.gd | 1 + .../widgets/curve_edit/settings_panel.gd | 10 ++ .../widgets/curve_edit/settings_panel.gd.uid | 1 + .../widgets/curve_edit/slope_point.gd | 25 +++- .../widgets/lattice_edit/lattice_dialog.tscn | 2 +- .../widgets/polygon_edit/control_point.gd | 45 +++++++- .../widgets/polygon_edit/control_point.tscn | 4 +- .../widgets/polygon_edit/polygon_dialog.tscn | 36 +++++- .../widgets/polygon_edit/polygon_editor.tscn | 5 +- .../widgets/polygon_edit/polygon_view.gd | 9 +- .../widgets/polygon_edit/settings_panel.gd | 10 ++ .../polygon_edit/settings_panel.gd.uid | 1 + 23 files changed, 426 insertions(+), 52 deletions(-) create mode 100644 material_maker/widgets/curve_edit/settings_panel.gd create mode 100644 material_maker/widgets/curve_edit/settings_panel.gd.uid create mode 100644 material_maker/widgets/polygon_edit/settings_panel.gd create mode 100644 material_maker/widgets/polygon_edit/settings_panel.gd.uid diff --git a/addons/material_maker/types/curve.gd b/addons/material_maker/types/curve.gd index 240d815bb..4bde7de23 100644 --- a/addons/material_maker/types/curve.gd +++ b/addons/material_maker/types/curve.gd @@ -38,7 +38,7 @@ func compare(curve) -> bool: return false return true -func add_point(x : float, y : float, ls : float = INF, rs : float = INF) -> void: +func add_point(x : float, y : float, ls : float = INF, rs : float = INF) -> int: for i in points.size(): if x < points[i].p.x: if ls == INF: @@ -46,8 +46,9 @@ func add_point(x : float, y : float, ls : float = INF, rs : float = INF) -> void if rs == INF: rs = 0 points.insert(i, Point.new(x, y, ls, rs)) - return + return i points.append(Point.new(x, y, ls, rs)) + return -1 func remove_point(index : int) -> bool: if index <= 0 or index >= points.size() - 1: diff --git a/material_maker/widgets/curve_edit/control_point.gd b/material_maker/widgets/curve_edit/control_point.gd index e1b129753..58b17c803 100644 --- a/material_maker/widgets/curve_edit/control_point.gd +++ b/material_maker/widgets/curve_edit/control_point.gd @@ -1,16 +1,19 @@ extends Control var moving : bool = false +var hovering : bool = false +var is_selected : bool = false var min_x : float var max_x : float var min_y : float var max_y : float -const OFFSET : Vector2 = Vector2(3, 3) +const OFFSET : Vector2 = Vector2(4, 4) signal moved(index) signal removed(index) +signal selected(index) func _ready(): pass # Replace with function body. @@ -18,10 +21,16 @@ func _ready(): func _draw(): var current_theme : Theme = mm_globals.main_window.theme var color : Color = current_theme.get_color("font_color", "Label") + var selected_color : Color = current_theme.get_color("icon_pressed_color", "Button") for c in get_children(): if c.visible: - draw_line(OFFSET, c.position+OFFSET, color) - draw_rect(Rect2(0, 0, 7, 7), color) + draw_line(OFFSET, c.position+OFFSET, color, 0.5, true) + draw_rect(Rect2(Vector2.ZERO, custom_minimum_size), selected_color if is_selected else color, true) + draw_rect(Rect2(Vector2.ZERO, custom_minimum_size), color.inverted(), false) + if hovering or moving: + draw_rect(Rect2(-custom_minimum_size * 0.5, custom_minimum_size * 2.0), color, false, 1.0) + draw_rect(Rect2(-custom_minimum_size*0.5-Vector2(1.0,1.0), + custom_minimum_size*2.0+Vector2(2.0,2.0)), color.inverted(), false, 1.0) func initialize(p : MMCurve.Point) -> void: position = get_parent().transform_point(p.p)-OFFSET @@ -41,13 +50,27 @@ func _on_ControlPoint_gui_input(event): if event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: moving = true + is_selected = true + emit_signal("selected", get_index()) + queue_redraw() else: moving = false + is_selected = false + queue_redraw() get_parent().update_controls() elif event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: emit_signal("removed", get_index()) elif moving and event is InputEventMouseMotion: - position += event.relative + var new_pos : Vector2 = position + event.relative + if event.is_command_or_control_pressed(): + if get_parent().axes_density > 1.0: + var snap : float = 1.0 / (get_parent().axes_density - 1.0) + new_pos = position + (event.position - custom_minimum_size / 2.0) + new_pos /= get_parent_control().size + new_pos = snapped(new_pos, Vector2(snap, snap)) + new_pos *= get_parent_control().size + new_pos -= custom_minimum_size/2.0 + position = new_pos if position.x < min_x: position.x = min_x elif position.x > max_x: @@ -61,3 +84,14 @@ func _on_ControlPoint_gui_input(event): func update_tangents() -> void: queue_redraw() emit_signal("moved", get_index()) + emit_signal("selected", get_index()) + + +func _on_mouse_entered() -> void: + queue_redraw() + hovering = true + + +func _on_mouse_exited() -> void: + queue_redraw() + hovering = false diff --git a/material_maker/widgets/curve_edit/control_point.tscn b/material_maker/widgets/curve_edit/control_point.tscn index cf2dbee93..c354fe991 100644 --- a/material_maker/widgets/curve_edit/control_point.tscn +++ b/material_maker/widgets/curve_edit/control_point.tscn @@ -1,40 +1,42 @@ [gd_scene load_steps=3 format=3 uid="uid://bhr7y7noxv5e6"] -[ext_resource type="Script" path="res://material_maker/widgets/curve_edit/slope_point.gd" id="1"] -[ext_resource type="Script" path="res://material_maker/widgets/curve_edit/control_point.gd" id="2"] +[ext_resource type="Script" uid="uid://dl376nblfgqfs" path="res://material_maker/widgets/curve_edit/slope_point.gd" id="1"] +[ext_resource type="Script" uid="uid://lfjxe7jh8oyu" path="res://material_maker/widgets/curve_edit/control_point.gd" id="2"] [node name="ControlPoint" type="Control"] +custom_minimum_size = Vector2(8, 8) +layout_mode = 3 +anchors_preset = 0 offset_left = 56.9864 offset_top = 33.8615 offset_right = 63.9864 offset_bottom = 40.8615 -custom_minimum_size = Vector2(7, 7) script = ExtResource("2") -__meta__ = { -"_edit_use_anchors_": false -} [node name="LeftSlope" type="Control" parent="."] -offset_left = -18.5235 -offset_right = -11.5235 -offset_bottom = 7.0 custom_minimum_size = Vector2(7, 7) +anchors_preset = 0 +offset_left = -18.524 +offset_right = -11.524 +offset_bottom = 7.0 script = ExtResource("1") -__meta__ = { -"_edit_use_anchors_": false -} distance = -30.0 [node name="RightSlope" type="Control" parent="."] -offset_left = 15.6919 -offset_right = 22.6919 +custom_minimum_size = Vector2(7, 7) +anchors_preset = 0 +offset_left = 15.692 +offset_right = 22.692 offset_bottom = 7.0 script = ExtResource("1") -__meta__ = { -"_edit_use_anchors_": false -} distance = 30.0 [connection signal="gui_input" from="." to="." method="_on_ControlPoint_gui_input"] +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] [connection signal="gui_input" from="LeftSlope" to="LeftSlope" method="_on_ControlPoint_gui_input"] +[connection signal="mouse_entered" from="LeftSlope" to="LeftSlope" method="_on_mouse_entered"] +[connection signal="mouse_exited" from="LeftSlope" to="LeftSlope" method="_on_mouse_exited"] [connection signal="gui_input" from="RightSlope" to="RightSlope" method="_on_ControlPoint_gui_input"] +[connection signal="mouse_entered" from="RightSlope" to="RightSlope" method="_on_mouse_entered"] +[connection signal="mouse_exited" from="RightSlope" to="RightSlope" method="_on_mouse_exited"] diff --git a/material_maker/widgets/curve_edit/curve_dialog.gd b/material_maker/widgets/curve_edit/curve_dialog.gd index 3cbccd64c..1458c17a8 100644 --- a/material_maker/widgets/curve_edit/curve_dialog.gd +++ b/material_maker/widgets/curve_edit/curve_dialog.gd @@ -36,3 +36,4 @@ func _on_Invert_pressed() -> void: for p in old_curve.points: new_curve.add_point(p.p.x, 1.0 - p.p.y, -p.ls, -p.rs) $VBoxContainer/EditorContainer/CurveEditor.set_curve(new_curve) + $VBoxContainer/EditorContainer/CurveEditor.update_control_ui() diff --git a/material_maker/widgets/curve_edit/curve_dialog.tscn b/material_maker/widgets/curve_edit/curve_dialog.tscn index f1b39a16f..4f43b25e4 100644 --- a/material_maker/widgets/curve_edit/curve_dialog.tscn +++ b/material_maker/widgets/curve_edit/curve_dialog.tscn @@ -1,11 +1,14 @@ -[gd_scene load_steps=4 format=3 uid="uid://dmfusfaiojjvf"] +[gd_scene load_steps=6 format=3 uid="uid://dmfusfaiojjvf"] [ext_resource type="PackedScene" uid="uid://c2ns17avhb2nx" path="res://material_maker/widgets/curve_edit/curve_editor.tscn" id="1"] -[ext_resource type="Script" path="res://material_maker/widgets/curve_edit/curve_dialog.gd" id="2"] -[ext_resource type="Script" path="res://material_maker/widgets/curve_edit/presets_selector.gd" id="3"] +[ext_resource type="Script" uid="uid://06osuiqfmilt" path="res://material_maker/widgets/curve_edit/curve_dialog.gd" id="2"] +[ext_resource type="Script" uid="uid://pgn60kk5fcj6" path="res://material_maker/widgets/curve_edit/settings_panel.gd" id="2_fe25e"] +[ext_resource type="Script" uid="uid://df24muo083lfx" path="res://material_maker/widgets/curve_edit/presets_selector.gd" id="3"] +[ext_resource type="PackedScene" uid="uid://rflulhsuy3ax" path="res://material_maker/widgets/float_edit/float_edit.tscn" id="3_fe25e"] [node name="CurveDialog" type="Window"] title = "Edit curve" +position = Vector2i(0, 36) size = Vector2i(300, 300) exclusive = true min_size = Vector2i(300, 300) @@ -20,16 +23,107 @@ offset_top = 5.0 offset_right = -5.0 offset_bottom = -5.0 +[node name="CurveViewSettingsContainer" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 4 + +[node name="CurveViewSettings" type="PanelContainer" parent="VBoxContainer/CurveViewSettingsContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +theme_type_variation = &"MM_PanelMenuBar" +script = ExtResource("2_fe25e") + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/CurveViewSettingsContainer/CurveViewSettings"] +layout_mode = 2 +size_flags_horizontal = 0 + +[node name="GridLabel" type="Label" parent="VBoxContainer/CurveViewSettingsContainer/CurveViewSettings/HBoxContainer"] +layout_mode = 2 +text = "Grid" + +[node name="GridDensity" parent="VBoxContainer/CurveViewSettingsContainer/CurveViewSettings/HBoxContainer" instance=ExtResource("3_fe25e")] +unique_name_in_owner = true +layout_mode = 2 +value = 4.0 +min_value = 1.0 +max_value = 16.0 +step = 1.0 +float_only = true + [node name="EditorContainer" type="MarginContainer" parent="VBoxContainer"] clip_contents = true layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 12 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 12 [node name="CurveEditor" parent="VBoxContainer/EditorContainer" instance=ExtResource("1")] +unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 +[node name="ControlUIContainer" type="VBoxContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 23) +layout_mode = 2 + +[node name="ControlUI" type="HBoxContainer" parent="VBoxContainer/ControlUIContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 4 + +[node name="PositionLabel" type="Label" parent="VBoxContainer/ControlUIContainer/ControlUI"] +layout_mode = 2 +text = "Position" + +[node name="PositionX" parent="VBoxContainer/ControlUIContainer/ControlUI" instance=ExtResource("3_fe25e")] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 0) +layout_mode = 2 +value = 1.0 +step = 0.01 +float_only = true + +[node name="PositionY" parent="VBoxContainer/ControlUIContainer/ControlUI" instance=ExtResource("3_fe25e")] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 0) +layout_mode = 2 +value = 0.0 +step = 0.01 +float_only = true + +[node name="Spacer" type="Control" parent="VBoxContainer/ControlUIContainer/ControlUI"] +custom_minimum_size = Vector2(12, 0) +layout_mode = 2 + +[node name="SlopeLabel" type="Label" parent="VBoxContainer/ControlUIContainer/ControlUI"] +layout_mode = 2 +text = "Handles" + +[node name="LeftSlope" parent="VBoxContainer/ControlUIContainer/ControlUI" instance=ExtResource("3_fe25e")] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 0) +layout_mode = 2 +value = 0.0 +min_value = -89.0 +max_value = 89.0 +step = 0.1 +float_only = true + +[node name="RightSlope" parent="VBoxContainer/ControlUIContainer/ControlUI" instance=ExtResource("3_fe25e")] +unique_name_in_owner = true +custom_minimum_size = Vector2(70, 0) +layout_mode = 2 +value = 45.0 +min_value = -89.0 +max_value = 89.0 +step = 0.1 +float_only = true + [node name="HSeparator" type="HSeparator" parent="VBoxContainer"] layout_mode = 2 @@ -63,7 +157,16 @@ layout_mode = 2 text = "Cancel" [connection signal="close_requested" from="." to="." method="_on_Cancel_pressed"] +[connection signal="ready" from="VBoxContainer/CurveViewSettingsContainer/CurveViewSettings" to="VBoxContainer/CurveViewSettingsContainer/CurveViewSettings" method="_on_ready"] +[connection signal="value_changed" from="VBoxContainer/CurveViewSettingsContainer/CurveViewSettings/HBoxContainer/GridDensity" to="VBoxContainer/CurveViewSettingsContainer/CurveViewSettings" method="_on_grid_density_value_changed"] [connection signal="value_changed" from="VBoxContainer/EditorContainer/CurveEditor" to="." method="_on_CurveEditor_value_changed"] +[connection signal="gui_input" from="VBoxContainer/EditorContainer/CurveEditor/@Control@283287" to="VBoxContainer/EditorContainer/CurveEditor/@Control@283287" method="_on_ControlPoint_gui_input"] +[connection signal="mouse_entered" from="VBoxContainer/EditorContainer/CurveEditor/@Control@283287" to="VBoxContainer/EditorContainer/CurveEditor/@Control@283287" method="_on_mouse_entered"] +[connection signal="mouse_exited" from="VBoxContainer/EditorContainer/CurveEditor/@Control@283287" to="VBoxContainer/EditorContainer/CurveEditor/@Control@283287" method="_on_mouse_exited"] +[connection signal="value_changed" from="VBoxContainer/ControlUIContainer/ControlUI/PositionX" to="VBoxContainer/EditorContainer/CurveEditor" method="_on_position_x_value_changed"] +[connection signal="value_changed" from="VBoxContainer/ControlUIContainer/ControlUI/PositionY" to="VBoxContainer/EditorContainer/CurveEditor" method="_on_position_y_value_changed"] +[connection signal="value_changed" from="VBoxContainer/ControlUIContainer/ControlUI/LeftSlope" to="VBoxContainer/EditorContainer/CurveEditor" method="_on_left_slope_value_changed"] +[connection signal="value_changed" from="VBoxContainer/ControlUIContainer/ControlUI/RightSlope" to="VBoxContainer/EditorContainer/CurveEditor" method="_on_right_slope_value_changed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/Invert" to="." method="_on_Invert_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/OK" to="." method="_on_OK_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/Cancel" to="." method="_on_Cancel_pressed"] diff --git a/material_maker/widgets/curve_edit/curve_edit.gd b/material_maker/widgets/curve_edit/curve_edit.gd index 8028c6453..7f51d227d 100644 --- a/material_maker/widgets/curve_edit/curve_edit.gd +++ b/material_maker/widgets/curve_edit/curve_edit.gd @@ -5,14 +5,17 @@ var value = null: set = set_value signal updated(curve, old_value) + func _ready(): set_value(MMCurve.new()) + func set_value(v) -> void: value = v.duplicate() $CurveView.curve = value $CurveView.queue_redraw() + func _on_CurveEdit_pressed(): var dialog = preload("res://material_maker/widgets/curve_edit/curve_dialog.tscn").instantiate() var content_scale_factor = mm_globals.main_window.get_window().content_scale_factor @@ -25,10 +28,12 @@ func _on_CurveEdit_pressed(): set_value(new_curve.value) emit_signal("updated", new_curve.value.duplicate(), null if new_curve.value.compare(new_curve.previous_value) else new_curve.previous_value) + func on_value_changed(v) -> void: set_value(v) emit_signal("updated", v.duplicate(), null) + func _get_drag_data(_position) -> MMCurve: var duplicated_value = value.duplicate() var view = CurveView.new(duplicated_value) @@ -39,9 +44,10 @@ func _get_drag_data(_position) -> MMCurve: set_drag_preview(button) return duplicated_value + func _can_drop_data(_position, data) -> bool: return data is MMCurve - + func _drop_data(_position, data) -> void: var old_curve : MMCurve = value diff --git a/material_maker/widgets/curve_edit/curve_edit.tscn b/material_maker/widgets/curve_edit/curve_edit.tscn index 0d76cf330..fd7cb1d83 100644 --- a/material_maker/widgets/curve_edit/curve_edit.tscn +++ b/material_maker/widgets/curve_edit/curve_edit.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://cvv8rhglg3jlm"] -[ext_resource type="Script" path="res://material_maker/widgets/curve_edit/curve_edit.gd" id="1"] +[ext_resource type="Script" uid="uid://cd0ugv3swm5os" path="res://material_maker/widgets/curve_edit/curve_edit.gd" id="1"] [ext_resource type="PackedScene" uid="uid://yeaj0tj7b08i" path="res://material_maker/widgets/curve_edit/curve_view.tscn" id="2"] [node name="CurveEdit" type="Button"] @@ -21,7 +21,5 @@ offset_left = 0.0 offset_top = 0.0 offset_right = 0.0 offset_bottom = 0.0 -grow_horizontal = 2 -grow_vertical = 2 [connection signal="pressed" from="." to="." method="_on_CurveEdit_pressed"] diff --git a/material_maker/widgets/curve_edit/curve_editor.gd b/material_maker/widgets/curve_edit/curve_editor.gd index 4d19d459c..a8bc8bfb9 100644 --- a/material_maker/widgets/curve_edit/curve_editor.gd +++ b/material_maker/widgets/curve_edit/curve_editor.gd @@ -4,16 +4,22 @@ extends "res://material_maker/widgets/curve_edit/curve_view.gd" signal value_changed(value) +var point_index : int = 0 + func _ready(): super._ready() update_controls() + update_control_ui() + %ControlUI.hide() + func set_curve(c : MMCurve) -> void: curve = c queue_redraw() update_controls() + func update_controls() -> void: for c in get_children(): c.queue_free() @@ -35,8 +41,10 @@ func update_controls() -> void: control_point.set_constraint(min_x, max_x, -control_point.OFFSET.y, size.y-control_point.OFFSET.y) control_point.connect("moved", Callable(self, "_on_ControlPoint_moved")) control_point.connect("removed", Callable(self, "_on_ControlPoint_removed")) + control_point.selected.connect(_on_ControlPoint_selected) emit_signal("value_changed", curve) + func _on_ControlPoint_moved(index): var control_point = get_child(index) curve.points[index].p = reverse_transform_point(control_point.position+control_point.OFFSET) @@ -44,25 +52,116 @@ func _on_ControlPoint_moved(index): var slope_vector = control_point.get_node("LeftSlope").position/size if slope_vector.x != 0: curve.points[index].ls = -slope_vector.y / slope_vector.x + %LeftSlope.value = snappedf(-rad_to_deg(atan(curve.points[index].ls)), 0.01) + if control_point.has_node("RightSlope"): var slope_vector = control_point.get_node("RightSlope").position/size if slope_vector.x != 0: curve.points[index].rs = -slope_vector.y / slope_vector.x + %RightSlope.value = snappedf(rad_to_deg(atan(curve.points[index].rs)), 0.01) + update_control_ui() queue_redraw() emit_signal("value_changed", curve) + func _on_ControlPoint_removed(index): if curve.remove_point(index): + %ControlUI.hide() queue_redraw() update_controls() + +func update_control_ui(): + %ControlUI.show() + %LeftSlope.value = snappedf(-rad_to_deg(atan(curve.points[point_index].ls)), 1e-2) + %RightSlope.value = snappedf(rad_to_deg(atan(curve.points[point_index].rs)), 1e-2) + %PositionY.value = snappedf(curve.points[point_index].p.y, 1e-3) + if point_index == 0 or point_index == curve.points.size()-1: + %PositionX.editable = false + %PositionX.modulate = Color.WEB_GRAY + if point_index == 0: + %PositionX.value = 0 + %LeftSlope.value = 0 + %LeftSlope.editable = false + %RightSlope.editable = true + %LeftSlope.modulate = Color.WEB_GRAY + %RightSlope.modulate = Color.WHITE + else: + %PositionX.value = 1 + %RightSlope.value = 0 + %LeftSlope.editable = true + %RightSlope.editable = false + %LeftSlope.modulate = Color.WHITE + %RightSlope.modulate = Color.WEB_GRAY + else: + %PositionX.editable = true + %PositionY.editable = true + %PositionX.modulate = Color.WHITE + + %LeftSlope.editable = true + %RightSlope.editable = true + %LeftSlope.modulate = Color.WHITE + %RightSlope.modulate = Color.WHITE + + + %PositionX.min_value = curve.points[point_index-1].p.x + %PositionX.max_value = curve.points[point_index+1].p.x + %PositionX.value = snappedf(clamp( + curve.points[point_index].p.x, + %PositionX.min_value, %PositionX.max_value),1e-3) + +func _on_ControlPoint_selected(index): + point_index = index + update_control_ui() + + func _on_CurveEditor_gui_input(event): if event is InputEventMouseButton: - if event.button_index == MOUSE_BUTTON_LEFT and event.double_click: - var new_point_position = reverse_transform_point(get_local_mouse_position()) - curve.add_point(new_point_position.x, new_point_position.y, 0.0, 0.0) - update_controls() + if event.button_index == MOUSE_BUTTON_LEFT: + if event.pressed and %ControlUI.visible: + # release float fields' focus to make text submission work + %ControlUI.hide() + %ControlUI.show() + if event.double_click: + var new_point_position = reverse_transform_point(get_local_mouse_position()) + var new_index = curve.add_point(new_point_position.x, new_point_position.y, 0.0, 0.0) + point_index = new_index + _on_ControlPoint_selected(new_index) + update_controls() + func _on_resize() -> void: super._on_resize() update_controls() + + +func _on_position_x_value_changed(value: Variant) -> void: + curve.points[point_index].p.x = clamp( + value, %PositionX.min_value, %PositionX.max_value) + queue_redraw() + update_controls() + + +func _on_position_y_value_changed(value: Variant) -> void: + curve.points[point_index].p.y = clamp( + value, %PositionY.min_value, %PositionY.max_value) + queue_redraw() + update_controls() + + +func _on_left_slope_value_changed(value: Variant) -> void: + if point_index != 0: + var rad = deg_to_rad(clamp(value, %LeftSlope.min_value, %LeftSlope.max_value)) + curve.points[point_index].ls = snappedf(-tan(rad), 1e-2) + update_controls() + queue_redraw() + emit_signal("value_changed", curve) + + +func _on_right_slope_value_changed(value: Variant) -> void: + if point_index != curve.points.size() - 1: + var rad = deg_to_rad(clamp(value, %RightSlope.min_value, %RightSlope.max_value)) + curve.points[point_index].rs = snappedf(tan(rad), 1e-2) + update_controls() + queue_redraw() + emit_signal("value_changed", curve) diff --git a/material_maker/widgets/curve_edit/curve_editor.tscn b/material_maker/widgets/curve_edit/curve_editor.tscn index 7d3478894..e334e6cf4 100644 --- a/material_maker/widgets/curve_edit/curve_editor.tscn +++ b/material_maker/widgets/curve_edit/curve_editor.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=3 format=3 uid="uid://c2ns17avhb2nx"] [ext_resource type="PackedScene" uid="uid://yeaj0tj7b08i" path="res://material_maker/widgets/curve_edit/curve_view.tscn" id="1"] -[ext_resource type="Script" path="res://material_maker/widgets/curve_edit/curve_editor.gd" id="2"] +[ext_resource type="Script" uid="uid://cvdqhv71gc0un" path="res://material_maker/widgets/curve_edit/curve_editor.gd" id="2"] [node name="CurveEditor" instance=ExtResource("1")] offset_left = 10.0 @@ -10,5 +10,9 @@ offset_right = -10.0 offset_bottom = -10.0 mouse_filter = 0 script = ExtResource("2") +show_axes = true [connection signal="gui_input" from="." to="." method="_on_CurveEditor_gui_input"] +[connection signal="gui_input" from="ControlPoint" to="ControlPoint" method="_on_ControlPoint_gui_input"] +[connection signal="mouse_entered" from="ControlPoint" to="ControlPoint" method="_on_mouse_entered"] +[connection signal="mouse_exited" from="ControlPoint" to="ControlPoint" method="_on_mouse_exited"] diff --git a/material_maker/widgets/curve_edit/curve_view.gd b/material_maker/widgets/curve_edit/curve_view.gd index a38504369..bd010aec7 100644 --- a/material_maker/widgets/curve_edit/curve_view.gd +++ b/material_maker/widgets/curve_edit/curve_view.gd @@ -4,6 +4,7 @@ class_name CurveView extends Control @export var show_axes : bool = false +@export var axes_density : int = 5 var curve : MMCurve @@ -29,8 +30,8 @@ func _draw(): var axes_color : Color = bg.lerp(fg, 0.25) var curve_color : Color = bg.lerp(fg, 0.75) if show_axes: - for i in range(5): - var p = transform_point(0.25*Vector2(i, i)) + for i in range(axes_density): + var p = transform_point(1.0/(axes_density-1)*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) var points : PackedVector2Array = PackedVector2Array() @@ -54,7 +55,7 @@ func _draw(): var x = p1.x+(p2.x-p1.x)*t p = transform_point(Vector2(x, p1.y*omt3 + yac*omt2*t*3.0 + ybc*omt*t2*3.0 + p2.y*t3)) points.push_back(p) - draw_polyline(points, curve_color) + draw_polyline(points, curve_color, 0.5, true) func _on_resize() -> void: queue_redraw() diff --git a/material_maker/widgets/curve_edit/curve_view.tscn b/material_maker/widgets/curve_edit/curve_view.tscn index fcdc86776..b2b7c3804 100644 --- a/material_maker/widgets/curve_edit/curve_view.tscn +++ b/material_maker/widgets/curve_edit/curve_view.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://yeaj0tj7b08i"] -[ext_resource type="Script" path="res://material_maker/widgets/curve_edit/curve_view.gd" id="1"] +[ext_resource type="Script" uid="uid://dk7v6u1dpxt0n" path="res://material_maker/widgets/curve_edit/curve_view.gd" id="1"] [node name="CurveView" type="Control"] layout_mode = 3 @@ -11,5 +11,7 @@ offset_left = 4.0 offset_top = 4.0 offset_right = -4.0 offset_bottom = -4.0 +grow_horizontal = 2 +grow_vertical = 2 mouse_filter = 2 script = ExtResource("1") diff --git a/material_maker/widgets/curve_edit/presets_selector.gd b/material_maker/widgets/curve_edit/presets_selector.gd index aaad3204f..0c926be93 100644 --- a/material_maker/widgets/curve_edit/presets_selector.gd +++ b/material_maker/widgets/curve_edit/presets_selector.gd @@ -33,3 +33,4 @@ func _menu_item_selected(index : int) -> void: var curve = MMCurve.new() curve.points = presets[index].curve get_parent().get_parent().get_node("EditorContainer/CurveEditor").set_curve(curve) + get_parent().get_parent().get_node("ControlUIContainer/ControlUI").hide() diff --git a/material_maker/widgets/curve_edit/settings_panel.gd b/material_maker/widgets/curve_edit/settings_panel.gd new file mode 100644 index 000000000..5146f1c06 --- /dev/null +++ b/material_maker/widgets/curve_edit/settings_panel.gd @@ -0,0 +1,10 @@ +extends PanelContainer + + +func _on_ready() -> void: + %GridDensity.value = %CurveEditor.axes_density - 1.0 + + +func _on_grid_density_value_changed(value: Variant) -> void: + %CurveEditor.axes_density = value + 1.0 + %CurveEditor.queue_redraw() diff --git a/material_maker/widgets/curve_edit/settings_panel.gd.uid b/material_maker/widgets/curve_edit/settings_panel.gd.uid new file mode 100644 index 000000000..9269a6d2e --- /dev/null +++ b/material_maker/widgets/curve_edit/settings_panel.gd.uid @@ -0,0 +1 @@ +uid://pgn60kk5fcj6 diff --git a/material_maker/widgets/curve_edit/slope_point.gd b/material_maker/widgets/curve_edit/slope_point.gd index c05e20e7f..54061ebcc 100644 --- a/material_maker/widgets/curve_edit/slope_point.gd +++ b/material_maker/widgets/curve_edit/slope_point.gd @@ -3,6 +3,8 @@ extends Control @export var distance : float var moving = false +var hovering : bool = false +var is_selected : bool = false const OFFSET = -Vector2(0, 0) @@ -12,12 +14,17 @@ func _ready(): func _draw(): var current_theme : Theme = mm_globals.main_window.theme var color : Color = current_theme.get_color("font_color", "Label") - draw_circle(Vector2(3.0, 3.0), 3.0, color) + var selected_color : Color = current_theme.get_color("icon_pressed_color", "Button") + draw_circle(custom_minimum_size*0.5, 3.0, selected_color if is_selected else color) + if hovering or is_selected: + draw_circle(custom_minimum_size*0.5, 6.0, color, false, 0.5, true) func _on_ControlPoint_gui_input(event : InputEvent): if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: + is_selected = true + queue_redraw() if event.double_click: var parent = get_parent() var vector : Vector2 @@ -27,18 +34,30 @@ func _on_ControlPoint_gui_input(event : InputEvent): vector = parent.get_parent().get_child(parent.get_index()+1).position-parent.position vector = distance*vector.normalized() position = vector-OFFSET - if event.is_control_or_command_pressed(): + if not event.shift_pressed: get_parent().get_child(1-get_index()).position = -vector-OFFSET get_parent().update_tangents() else: moving = true else: + is_selected = false + queue_redraw() moving = false elif moving and event is InputEventMouseMotion: var vector = get_global_mouse_position()-get_parent().get_global_rect().position+OFFSET vector *= sign(vector.x) vector = distance*vector.normalized() position = vector-OFFSET - if event.is_command_or_control_pressed(): + if not event.shift_pressed: get_parent().get_child(1-get_index()).position = -vector-OFFSET get_parent().update_tangents() + + +func _on_mouse_entered() -> void: + hovering = true + queue_redraw() + + +func _on_mouse_exited() -> void: + hovering = false + queue_redraw() diff --git a/material_maker/widgets/lattice_edit/lattice_dialog.tscn b/material_maker/widgets/lattice_edit/lattice_dialog.tscn index 034212935..060d1a449 100644 --- a/material_maker/widgets/lattice_edit/lattice_dialog.tscn +++ b/material_maker/widgets/lattice_edit/lattice_dialog.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=3 format=3 uid="uid://b1o4dgfm2i376"] -[ext_resource type="Script" path="res://material_maker/widgets/lattice_edit/lattice_dialog.gd" id="1_smyqc"] +[ext_resource type="Script" uid="uid://b6nk3nlq0esen" path="res://material_maker/widgets/lattice_edit/lattice_dialog.gd" id="1_smyqc"] [ext_resource type="PackedScene" uid="uid://dicq2cut03ved" path="res://material_maker/widgets/lattice_edit/lattice_editor.tscn" id="2_w8wwg"] [node name="LatticeDialog" type="Window"] diff --git a/material_maker/widgets/polygon_edit/control_point.gd b/material_maker/widgets/polygon_edit/control_point.gd index b78631a31..72bc5da51 100644 --- a/material_maker/widgets/polygon_edit/control_point.gd +++ b/material_maker/widgets/polygon_edit/control_point.gd @@ -5,6 +5,7 @@ var is_moving : bool = false var has_moved : bool = false var selectable : bool = false var is_selected : bool = false +var hovered : bool = false var editor : Control = null static var control_size : float = 4 @@ -15,13 +16,18 @@ signal removed(index) func _draw(): var current_theme : Theme = mm_globals.main_window.theme + var moving_color : Color = current_theme.get_color("icon_pressed_color", "Button") var color : Color = Color(1.0, 0.0, 0.0) if is_selected else current_theme.get_color("font_color", "Label") if circle: draw_circle(Vector2(control_size, control_size), control_size+1, color) draw_arc(Vector2(control_size, control_size), control_size+1, 0, TAU, 8, color.inverted()) else: - draw_rect(Rect2(Vector2(0, 0), custom_minimum_size), color) + draw_rect(Rect2(Vector2(0, 0), custom_minimum_size), moving_color if is_moving else color) draw_rect(Rect2(Vector2(0, 0), custom_minimum_size), color.inverted(), false) + if is_moving or hovered: + draw_rect(Rect2(-custom_minimum_size*0.5, custom_minimum_size*2.0), current_theme.get_color("font_color", "Label"), false, 1.0) + draw_rect(Rect2(-custom_minimum_size*0.5-Vector2(1.0,1.0), + custom_minimum_size*2.0+Vector2(2.0,2.0)), current_theme.get_color("font_color", "Label").inverted(), false, 1.0) func initialize(p : Vector2, e : Control = null) -> void: editor = e if e != null else get_parent() @@ -46,14 +52,49 @@ func _on_ControlPoint_gui_input(event): if event.pressed: is_moving = true has_moved = false + queue_redraw() if selectable: selected.emit(get_index(), event.is_command_or_control_pressed(), event.shift_pressed) else: is_moving = false moved.emit(get_index()) + queue_redraw() elif event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: removed.emit(get_index()) elif is_moving and event is InputEventMouseMotion: - position += event.relative + var new_pos : Vector2 = position + event.relative + if event.is_command_or_control_pressed(): + var value : Vector2 + var parent = get_parent().owner + if parent.name == "Preview2D": + new_pos = position + event.position + value = parent.pos_to_value(new_pos, true, false) + var snap : float = 0.0 + var preview2D = get_parent().owner + var grid = preview2D.get_node("Guides") + if grid != null and grid.visible: + snap = grid.grid_size + if snap > 0.0: + value.x = round((value.x-0.5)*snap)/snap+0.5 + value.y = round((value.y-0.5)*snap)/snap+0.5 + new_pos = parent.value_to_pos(value, true, false) - size*0.5 + elif parent.name == "PolygonDialog": + if get_parent().axes_density > 1.0: + new_pos = position + event.position + var snap : float = 1.0 / (get_parent().axes_density - 1.0) + value = get_parent().reverse_transform_point(new_pos) + value = snapped(value, Vector2(snap, snap)) + new_pos = get_parent().transform_point(value) - size*0.5 + position = new_pos has_moved = true moved.emit(get_index()) + + +func _on_mouse_entered() -> void: + hovered = true + queue_redraw() + + +func _on_mouse_exited() -> void: + hovered = false + queue_redraw() diff --git a/material_maker/widgets/polygon_edit/control_point.tscn b/material_maker/widgets/polygon_edit/control_point.tscn index 04b842a30..e798ec177 100644 --- a/material_maker/widgets/polygon_edit/control_point.tscn +++ b/material_maker/widgets/polygon_edit/control_point.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://crupd18xhx3me"] -[ext_resource type="Script" path="res://material_maker/widgets/polygon_edit/control_point.gd" id="1"] +[ext_resource type="Script" uid="uid://umn8pi28lhrc" path="res://material_maker/widgets/polygon_edit/control_point.gd" id="1"] [node name="ControlPoint" type="Control"] custom_minimum_size = Vector2(7, 7) @@ -11,3 +11,5 @@ offset_bottom = 7.0 script = ExtResource("1") [connection signal="gui_input" from="." to="." method="_on_ControlPoint_gui_input"] +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] diff --git a/material_maker/widgets/polygon_edit/polygon_dialog.tscn b/material_maker/widgets/polygon_edit/polygon_dialog.tscn index 96d8ade1a..fb468e5db 100644 --- a/material_maker/widgets/polygon_edit/polygon_dialog.tscn +++ b/material_maker/widgets/polygon_edit/polygon_dialog.tscn @@ -1,7 +1,9 @@ -[gd_scene load_steps=3 format=3 uid="uid://da62d56oqbxsd"] +[gd_scene load_steps=5 format=3 uid="uid://da62d56oqbxsd"] [ext_resource type="PackedScene" uid="uid://djura7a50b2aq" path="res://material_maker/widgets/polygon_edit/polygon_editor.tscn" id="1"] -[ext_resource type="Script" path="res://material_maker/widgets/polygon_edit/polygon_dialog.gd" id="2"] +[ext_resource type="Script" uid="uid://be2rg42127n0g" path="res://material_maker/widgets/polygon_edit/polygon_dialog.gd" id="2"] +[ext_resource type="Script" uid="uid://c8ojysjh2qimr" path="res://material_maker/widgets/polygon_edit/settings_panel.gd" id="2_05k5u"] +[ext_resource type="PackedScene" uid="uid://rflulhsuy3ax" path="res://material_maker/widgets/float_edit/float_edit.tscn" id="3_k7vr5"] [node name="PolygonDialog" type="Window"] script = ExtResource("2") @@ -15,6 +17,29 @@ offset_top = 5.0 offset_right = -5.0 offset_bottom = -5.0 +[node name="PolygonViewSettings" type="PanelContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +theme_type_variation = &"MM_PanelMenuBar" +script = ExtResource("2_05k5u") + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/PolygonViewSettings"] +layout_mode = 2 +size_flags_horizontal = 0 + +[node name="GridLabel" type="Label" parent="VBoxContainer/PolygonViewSettings/HBoxContainer"] +layout_mode = 2 +text = "Grid" + +[node name="GridDensity" parent="VBoxContainer/PolygonViewSettings/HBoxContainer" instance=ExtResource("3_k7vr5")] +unique_name_in_owner = true +layout_mode = 2 +value = 4.0 +min_value = 1.0 +max_value = 16.0 +step = 1.0 +float_only = true + [node name="EditorContainer" type="MarginContainer" parent="VBoxContainer"] clip_contents = true layout_mode = 2 @@ -26,8 +51,11 @@ theme_override_constants/margin_right = 4 theme_override_constants/margin_bottom = 4 [node name="PolygonEditor" parent="VBoxContainer/EditorContainer" instance=ExtResource("1")] +unique_name_in_owner = true custom_minimum_size = Vector2(400, 400) layout_mode = 2 +show_axes = true +axes_density = 5 [node name="HSeparator" type="HSeparator" parent="VBoxContainer"] layout_mode = 2 @@ -47,6 +75,10 @@ layout_mode = 2 text = "Cancel" [connection signal="close_requested" from="." to="." method="_on_Cancel_pressed"] +[connection signal="ready" from="VBoxContainer/PolygonViewSettings" to="." method="_on_polygon_view_settings_ready"] +[connection signal="ready" from="VBoxContainer/PolygonViewSettings" to="VBoxContainer/PolygonViewSettings" method="_on_ready"] +[connection signal="value_changed" from="VBoxContainer/PolygonViewSettings/HBoxContainer/GridDensity" to="VBoxContainer/PolygonViewSettings" method="_on_grid_density_value_changed"] [connection signal="value_changed" from="VBoxContainer/EditorContainer/PolygonEditor" to="." method="_on_PolygonEditor_value_changed"] +[connection signal="gui_input" from="VBoxContainer/EditorContainer/PolygonEditor/@Control@242305" to="VBoxContainer/EditorContainer/PolygonEditor/@Control@242305" method="_on_ControlPoint_gui_input"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/OK" to="." method="_on_OK_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/Cancel" to="." method="_on_Cancel_pressed"] diff --git a/material_maker/widgets/polygon_edit/polygon_editor.tscn b/material_maker/widgets/polygon_edit/polygon_editor.tscn index d865a93b9..0d61fff54 100644 --- a/material_maker/widgets/polygon_edit/polygon_editor.tscn +++ b/material_maker/widgets/polygon_edit/polygon_editor.tscn @@ -1,18 +1,17 @@ [gd_scene load_steps=3 format=3 uid="uid://djura7a50b2aq"] [ext_resource type="PackedScene" uid="uid://yeaj0tj7b08i" path="res://material_maker/widgets/curve_edit/curve_view.tscn" id="1"] -[ext_resource type="Script" path="res://material_maker/widgets/polygon_edit/polygon_editor.gd" id="2"] +[ext_resource type="Script" uid="uid://511g8g7afvi8" path="res://material_maker/widgets/polygon_edit/polygon_editor.gd" id="2"] [node name="PolygonEditor" instance=ExtResource("1")] offset_left = 0.0 offset_top = 0.0 offset_right = -1.0 offset_bottom = -1.0 -grow_horizontal = 2 -grow_vertical = 2 mouse_filter = 0 script = ExtResource("2") draw_area = true auto_rescale = true [connection signal="gui_input" from="." to="." method="_on_PolygonEditor_gui_input"] +[connection signal="gui_input" from="@Control@242306" to="@Control@242306" method="_on_ControlPoint_gui_input"] diff --git a/material_maker/widgets/polygon_edit/polygon_view.gd b/material_maker/widgets/polygon_edit/polygon_view.gd index cd8891b1a..9951507db 100644 --- a/material_maker/widgets/polygon_edit/polygon_view.gd +++ b/material_maker/widgets/polygon_edit/polygon_view.gd @@ -6,6 +6,8 @@ extends Control @export var draw_area : bool = true @export var auto_rescale : bool = true +@export var show_axes : bool = false +@export var axes_density : int = 5 var polygon : MMPolygon @@ -45,10 +47,15 @@ func _draw(): var curve_color : Color = bg.lerp(fg, 0.75) if draw_area: draw_rect(Rect2(draw_offset, draw_size), axes_color, false) + if show_axes: + for i in range(axes_density): + var step : float = 1/(axes_density-1.0)*i + draw_line(transform_point(Vector2(0.0, step)), transform_point(Vector2(1.0, step)), axes_color) + draw_line(transform_point(Vector2(step, 0.0)), transform_point(Vector2(step, 1.0)), axes_color) var tp : Vector2 = transform_point(polygon.points[polygon.points.size()-1 if closed else 0]) for p in polygon.points: var tnp = transform_point(p) - draw_line(tp, tnp, curve_color) + draw_line(tp, tnp, curve_color, 0.5, true) tp = tnp func _on_resize() -> void: diff --git a/material_maker/widgets/polygon_edit/settings_panel.gd b/material_maker/widgets/polygon_edit/settings_panel.gd new file mode 100644 index 000000000..514b49de3 --- /dev/null +++ b/material_maker/widgets/polygon_edit/settings_panel.gd @@ -0,0 +1,10 @@ +extends PanelContainer + + +func _on_ready() -> void: + %GridDensity.value = %PolygonEditor.axes_density - 1.0 + + +func _on_grid_density_value_changed(value: Variant) -> void: + %PolygonEditor.axes_density = value + 1.0 + %PolygonEditor.queue_redraw() diff --git a/material_maker/widgets/polygon_edit/settings_panel.gd.uid b/material_maker/widgets/polygon_edit/settings_panel.gd.uid new file mode 100644 index 000000000..a39222e49 --- /dev/null +++ b/material_maker/widgets/polygon_edit/settings_panel.gd.uid @@ -0,0 +1 @@ +uid://c8ojysjh2qimr