From d2734ab044895c1291b26aded81635e7cfbef4ec Mon Sep 17 00:00:00 2001 From: Emmanouil Papadeas Date: Mon, 4 Dec 2023 14:55:38 +0200 Subject: [PATCH] Add a basic stabilizer Not as powerful as other art software, but should be enough for pixel art --- Translations/Translations.pot | 4 ++ src/Autoload/Tools.gd | 2 + src/Tools/BaseTool.gd | 13 +++++++ src/Tools/Draw.gd | 2 +- src/Tools/Eraser.gd | 5 ++- src/Tools/Pencil.gd | 5 ++- src/Tools/SelectionTools/Lasso.gd | 5 +-- src/Tools/SelectionTools/PaintSelect.gd | 5 +-- src/Tools/Shading.gd | 5 ++- src/UI/GlobalToolOptions/GlobalToolOptions.gd | 8 ++++ .../GlobalToolOptions/GlobalToolOptions.tscn | 37 ++++++++++++++++--- 11 files changed, 73 insertions(+), 18 deletions(-) diff --git a/Translations/Translations.pot b/Translations/Translations.pot index 437a7b783..c6645bfe8 100644 --- a/Translations/Translations.pot +++ b/Translations/Translations.pot @@ -1465,6 +1465,10 @@ msgstr "" msgid "Dynamics" msgstr "" +#. Found in the Dynamics options menu. A stabilizer is a feature that, when enabled, helps artists create smooth lines as they draw. +msgid "Stabilizer" +msgstr "" + #. Found in the Dynamics options menu. Pressure refers to tablet pen pressure. msgid "Pressure" msgstr "" diff --git a/src/Autoload/Tools.gd b/src/Autoload/Tools.gd index 4fc1a9ce6..a4edd80f9 100644 --- a/src/Autoload/Tools.gd +++ b/src/Autoload/Tools.gd @@ -9,6 +9,8 @@ var vertical_mirror := false var pixel_perfect := false # Dynamics +var stabilizer_enabled := false +var stabilizer_value := 16 var dynamics_alpha: int = Dynamics.NONE var dynamics_size: int = Dynamics.NONE var pen_pressure := 1.0 diff --git a/src/Tools/BaseTool.gd b/src/Tools/BaseTool.gd index 0557b8122..0ebc83207 100644 --- a/src/Tools/BaseTool.gd +++ b/src/Tools/BaseTool.gd @@ -6,6 +6,7 @@ var kname: String var tool_slot: Tools.Slot = null var cursor_text := "" var _cursor := Vector2i(Vector2.INF) +var _stabilizer_center := Vector2.ZERO var _draw_cache: Array[Vector2i] = [] ## For storing already drawn pixels @warning_ignore("unused_private_class_variable") @@ -54,6 +55,7 @@ func update_config() -> void: func draw_start(pos: Vector2i) -> void: + _stabilizer_center = pos _draw_cache = [] is_moving = true Global.current_project.can_undo = false @@ -260,6 +262,17 @@ func _snap_to_guide( return snap_to +func _get_stabilized_position(normal_pos: Vector2) -> Vector2: + if not Tools.stabilizer_enabled: + return normal_pos + var difference := normal_pos - _stabilizer_center + var distance := difference.length() / Tools.stabilizer_value + var angle := difference.angle() + var pos := _stabilizer_center + Vector2(distance, distance) * Vector2.from_angle(angle) + _stabilizer_center = pos + return pos + + func _get_draw_rect() -> Rect2i: if Global.current_project.has_selection: return Global.current_project.selection_map.get_used_rect() diff --git a/src/Tools/Draw.gd b/src/Tools/Draw.gd index 3a46db26d..26a0cf147 100644 --- a/src/Tools/Draw.gd +++ b/src/Tools/Draw.gd @@ -318,7 +318,7 @@ func _draw_tool(pos: Vector2) -> PackedVector2Array: # Bresenham's Algorithm # Thanks to https://godotengine.org/qa/35276/tile-based-line-drawing-algorithm-efficiency -func draw_fill_gap(start: Vector2, end: Vector2) -> void: +func draw_fill_gap(start: Vector2i, end: Vector2i) -> void: if Global.mirror_view: # Even brushes are not perfectly centred and are offsetted by 1 px so we add it if int(_stroke_dimensions.x) % 2 == 0: diff --git a/src/Tools/Eraser.gd b/src/Tools/Eraser.gd index 2b297ae1b..c0b2598bb 100644 --- a/src/Tools/Eraser.gd +++ b/src/Tools/Eraser.gd @@ -66,11 +66,12 @@ func draw_start(pos: Vector2i) -> void: cursor_text = "" -func draw_move(pos: Vector2i) -> void: +func draw_move(pos_i: Vector2i) -> void: + var pos := _get_stabilized_position(pos_i) pos = snap_position(pos) super.draw_move(pos) if _picking_color: # Still return even if we released Alt - if Input.is_action_pressed("draw_color_picker"): + if Input.is_action_pressed(&"draw_color_picker"): _pick_color(pos) return diff --git a/src/Tools/Pencil.gd b/src/Tools/Pencil.gd index e6a49f8cb..77897b691 100644 --- a/src/Tools/Pencil.gd +++ b/src/Tools/Pencil.gd @@ -131,11 +131,12 @@ func draw_start(pos: Vector2i) -> void: cursor_text = "" -func draw_move(pos: Vector2i) -> void: +func draw_move(pos_i: Vector2i) -> void: + var pos := _get_stabilized_position(pos_i) pos = snap_position(pos) super.draw_move(pos) if _picking_color: # Still return even if we released Alt - if Input.is_action_pressed("draw_color_picker"): + if Input.is_action_pressed(&"draw_color_picker"): _pick_color(pos) return diff --git a/src/Tools/SelectionTools/Lasso.gd b/src/Tools/SelectionTools/Lasso.gd index a0645fede..74e2f219e 100644 --- a/src/Tools/SelectionTools/Lasso.gd +++ b/src/Tools/SelectionTools/Lasso.gd @@ -12,9 +12,10 @@ func draw_start(pos: Vector2i) -> void: _last_position = pos -func draw_move(pos: Vector2i) -> void: +func draw_move(pos_i: Vector2i) -> void: if selection_node.arrow_key_move: return + var pos := _get_stabilized_position(pos_i) pos = snap_position(pos) super.draw_move(pos) if !_move: @@ -28,8 +29,6 @@ func draw_end(pos: Vector2i) -> void: if selection_node.arrow_key_move: return pos = snap_position(pos) - if !_move: - _draw_points.append(pos) super.draw_end(pos) diff --git a/src/Tools/SelectionTools/PaintSelect.gd b/src/Tools/SelectionTools/PaintSelect.gd index e924a63b8..9f787fc7e 100644 --- a/src/Tools/SelectionTools/PaintSelect.gd +++ b/src/Tools/SelectionTools/PaintSelect.gd @@ -40,9 +40,10 @@ func draw_start(pos: Vector2i) -> void: _last_position = pos -func draw_move(pos: Vector2i) -> void: +func draw_move(pos_i: Vector2i) -> void: if selection_node.arrow_key_move: return + var pos := _get_stabilized_position(pos_i) pos = snap_position(pos) super.draw_move(pos) if !_move: @@ -56,8 +57,6 @@ func draw_end(pos: Vector2i) -> void: if selection_node.arrow_key_move: return pos = snap_position(pos) - if !_move: - _draw_points.append_array(draw_tool(pos)) super.draw_end(pos) diff --git a/src/Tools/Shading.gd b/src/Tools/Shading.gd index 10d2b8450..a61f89825 100644 --- a/src/Tools/Shading.gd +++ b/src/Tools/Shading.gd @@ -237,11 +237,12 @@ func draw_start(pos: Vector2i) -> void: cursor_text = "" -func draw_move(pos: Vector2i) -> void: +func draw_move(pos_i: Vector2i) -> void: + var pos := _get_stabilized_position(pos_i) pos = snap_position(pos) super.draw_move(pos) if _picking_color: # Still return even if we released Alt - if Input.is_action_pressed("draw_color_picker"): + if Input.is_action_pressed(&"draw_color_picker"): _pick_color(pos) return diff --git a/src/UI/GlobalToolOptions/GlobalToolOptions.gd b/src/UI/GlobalToolOptions/GlobalToolOptions.gd index 3d6766122..6bffb9b4e 100644 --- a/src/UI/GlobalToolOptions/GlobalToolOptions.gd +++ b/src/UI/GlobalToolOptions/GlobalToolOptions.gd @@ -153,3 +153,11 @@ func _on_SizeMin_value_changed(value: float) -> void: func _on_SizeMax_value_changed(value: float) -> void: Tools.brush_size_max = int(value) dynamics_changed.emit() + + +func _on_enable_stabilizer_toggled(toggled_on: bool) -> void: + Tools.stabilizer_enabled = toggled_on + + +func _on_stabilizer_value_value_changed(value: float) -> void: + Tools.stabilizer_value = value diff --git a/src/UI/GlobalToolOptions/GlobalToolOptions.tscn b/src/UI/GlobalToolOptions/GlobalToolOptions.tscn index 35a4fb694..818a210ac 100644 --- a/src/UI/GlobalToolOptions/GlobalToolOptions.tscn +++ b/src/UI/GlobalToolOptions/GlobalToolOptions.tscn @@ -45,7 +45,6 @@ gradient = SubResource("3") gradient = SubResource("3") [node name="Global Tool Options" type="PanelContainer"] -custom_minimum_size = Vector2(0, 36) offset_left = 1.0 offset_right = 195.0 offset_bottom = 50.0 @@ -147,12 +146,38 @@ texture = ExtResource("6") [node name="DynamicsPanel" type="PopupPanel" parent="."] canvas_item_default_texture_filter = 0 +size = Vector2i(300, 334) [node name="VBoxContainer" type="VBoxContainer" parent="DynamicsPanel"] -offset_left = 4.0 -offset_top = 4.0 -offset_right = 186.0 -offset_bottom = 288.0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = 292.0 +offset_bottom = 326.0 + +[node name="StabilizerContainer" type="HBoxContainer" parent="DynamicsPanel/VBoxContainer"] +layout_mode = 2 + +[node name="EnableStabilizer" type="CheckButton" parent="DynamicsPanel/VBoxContainer/StabilizerContainer"] +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "Stabilizer" + +[node name="StabilizerValue" type="TextureProgressBar" parent="DynamicsPanel/VBoxContainer/StabilizerContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" +min_value = 1.0 +max_value = 64.0 +value = 16.0 +allow_greater = true +nine_patch_stretch = true +stretch_margin_left = 3 +stretch_margin_top = 3 +stretch_margin_right = 3 +stretch_margin_bottom = 3 +script = ExtResource("5") [node name="DynamicsOptions" type="GridContainer" parent="DynamicsPanel/VBoxContainer"] layout_mode = 2 @@ -412,6 +437,8 @@ offset_bottom = 23.0 [connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/Vertical" to="." method="_on_Vertical_toggled"] [connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/PixelPerfect" to="." method="_on_PixelPerfect_toggled"] [connection signal="pressed" from="ScrollContainer/CenterContainer/GridContainer/Dynamics" to="." method="_on_Dynamics_pressed"] +[connection signal="toggled" from="DynamicsPanel/VBoxContainer/StabilizerContainer/EnableStabilizer" to="." method="_on_enable_stabilizer_toggled"] +[connection signal="value_changed" from="DynamicsPanel/VBoxContainer/StabilizerContainer/StabilizerValue" to="." method="_on_stabilizer_value_value_changed"] [connection signal="value_changed" from="DynamicsPanel/VBoxContainer/LimitContainer/AlphaMin" to="." method="_on_AlphaMin_value_changed"] [connection signal="value_changed" from="DynamicsPanel/VBoxContainer/LimitContainer/AlphaMax" to="." method="_on_AlphaMax_value_changed"] [connection signal="value_changed" from="DynamicsPanel/VBoxContainer/LimitContainer/SizeMin" to="." method="_on_SizeMin_value_changed"]