1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-19 01:29:49 +00:00

[WIP] Added dynamics, such as pen pressure (finally!) and mouse velocity affecting alpha and brush size

Alpha currently produces ugly results when drawing on top of opaque colors. See the comment in Draw.gd like 445 for more details.
This commit is contained in:
Emmanouil Papadeas 2023-02-17 15:29:55 +02:00
parent fdb24c008e
commit c60675b4e7
5 changed files with 546 additions and 34 deletions

View file

@ -5,7 +5,6 @@ signal cel_changed
enum LayerTypes { PIXEL, GROUP }
enum GridTypes { CARTESIAN, ISOMETRIC, ALL }
enum PressureSensitivity { NONE, ALPHA, SIZE, ALPHA_AND_SIZE }
enum ColorFrom { THEME, CUSTOM }
enum ButtonSize { SMALL, BIG }
@ -74,7 +73,6 @@ var show_x_symmetry_axis := false
var show_y_symmetry_axis := false
# Preferences
var pressure_sensitivity_mode = PressureSensitivity.NONE
var open_last_project := false
var quit_confirmation := false
var smooth_zoom := true

View file

@ -2,11 +2,28 @@ extends Node
signal color_changed(color, button)
var pen_pressure := 1.0
enum Dynamics { NONE, PRESSURE, VELOCITY }
var horizontal_mirror := false
var vertical_mirror := false
var pixel_perfect := false
# Dynamics
var dynamics_alpha: int = Dynamics.NONE
var dynamics_size: int = Dynamics.NONE
var pen_pressure := 1.0
var pen_pressure_min := 0.2
var pen_pressure_max := 0.8
var pressure_buf := [0, 0] # past pressure value buffer
var mouse_velocity := 1.0
var mouse_velocity_min_thres := 0.2
var mouse_velocity_max_thres := 0.8
var mouse_velocity_max := 1000.0
var alpha_min := 0.1
var alpha_max := 1.0
var brush_size_min := 1
var brush_size_max := 4
var tools := {
"RectSelect":
Tool.new(
@ -218,7 +235,7 @@ func _ready() -> void:
_tool_buttons = Global.control.find_node("ToolButtons")
for t in tools:
add_tool_button(tools[t])
var tool_shortcut: String = Tools.tools[t].shortcut
var tool_shortcut: String = tools[t].shortcut
var left_tool_shortcut := "left_%s_tool" % tool_shortcut
var right_tool_shortcut := "right_%s_tool" % tool_shortcut
Keychain.actions[left_tool_shortcut] = Keychain.InputAction.new("", "Left")
@ -399,9 +416,27 @@ func handle_draw(position: Vector2, event: InputEvent) -> void:
if event is InputEventMouseMotion:
pen_pressure = event.pressure
if Global.pressure_sensitivity_mode == Global.PressureSensitivity.NONE:
pen_pressure = 1.0
# Workaround https://github.com/godotengine/godot/issues/53033#issuecomment-930409407
# If a pressure value of 1 is encountered, "correct" the value by
# extrapolating from the delta of the past two values. This will
# correct the jumping to 1 error while also allowing values that
# are "supposed" to be 1.
if pen_pressure == 1 && pressure_buf[0] != 0:
pen_pressure = min(1, pressure_buf[0] + pressure_buf[0] - pressure_buf[1])
pressure_buf.pop_back()
pressure_buf.push_front(pen_pressure)
pen_pressure = range_lerp(pen_pressure, pen_pressure_min, pen_pressure_max, 0.0, 1.0)
pen_pressure = clamp(pen_pressure, 0.0, 1.0)
mouse_velocity = event.speed.length() / mouse_velocity_max
mouse_velocity = range_lerp(
mouse_velocity, mouse_velocity_min_thres, mouse_velocity_max_thres, 0.0, 1.0
)
mouse_velocity = clamp(mouse_velocity, 0.0, 1.0)
if dynamics_alpha != Dynamics.PRESSURE and dynamics_size != Dynamics.PRESSURE:
pen_pressure = 1.0
if dynamics_alpha != Dynamics.VELOCITY and dynamics_size != Dynamics.VELOCITY:
mouse_velocity = 1.0
if not position.is_equal_approx(_last_position):
_last_position = position
_slots[BUTTON_LEFT].tool_node.cursor_move(position)
@ -418,3 +453,11 @@ func handle_draw(position: Vector2, event: InputEvent) -> void:
if not _slots[BUTTON_RIGHT].tool_node.cursor_text.empty():
text += " %s" % _slots[BUTTON_RIGHT].tool_node.cursor_text
Global.cursor_position_label.text = text
func get_alpha_dynamic(strength := 1.0) -> float:
if dynamics_alpha == Dynamics.PRESSURE:
strength *= lerp(alpha_min, alpha_max, pen_pressure)
elif dynamics_alpha == Dynamics.VELOCITY:
strength *= lerp(alpha_min, alpha_max, mouse_velocity)
return strength

View file

@ -2,6 +2,7 @@ extends BaseTool
var _brush := Brushes.get_default_brush()
var _brush_size := 1
var _brush_size_dynamics := 1
var _cache_limit := 3
var _brush_interpolate := 0
var _brush_image := Image.new()
@ -11,7 +12,7 @@ var _picking_color := false
var _undo_data := {}
var _drawer := Drawer.new()
var _mask := PoolByteArray()
var _mask := PoolRealArray()
var _mirror_brushes := {}
var _draw_line := false
@ -155,9 +156,9 @@ func update_mirror_brush() -> void:
func update_mask(can_skip := true) -> void:
if can_skip and Global.pressure_sensitivity_mode == Global.PressureSensitivity.NONE:
if can_skip and Tools.dynamics_alpha == Tools.Dynamics.NONE:
if _mask:
_mask = PoolByteArray()
_mask = PoolRealArray()
return
var size: Vector2 = Global.current_project.size
_is_mask_size_zero = false
@ -165,7 +166,7 @@ func update_mask(can_skip := true) -> void:
# See: https://github.com/Orama-Interactive/Pixelorama/pull/439
var nulled_array := []
nulled_array.resize(size.x * size.y)
_mask = PoolByteArray(nulled_array)
_mask = PoolRealArray(nulled_array)
func update_line_polylines(start: Vector2, end: Vector2) -> void:
@ -211,9 +212,16 @@ func draw_tool(position: Vector2) -> void:
func _prepare_tool() -> void:
if !Global.current_project.layers[Global.current_project.current_layer].can_layer_get_drawn():
return
var strength := _strength
if Global.pressure_sensitivity_mode == Global.PressureSensitivity.ALPHA:
strength *= Tools.pen_pressure
_brush_size_dynamics = _brush_size
var strength: float = Tools.get_alpha_dynamic(_strength)
if Tools.dynamics_size == Tools.Dynamics.PRESSURE:
_brush_size_dynamics = round(
lerp(Tools.brush_size_min, Tools.brush_size_max, Tools.pen_pressure)
)
elif Tools.dynamics_size == Tools.Dynamics.VELOCITY:
_brush_size_dynamics = round(
lerp(Tools.brush_size_min, Tools.brush_size_max, Tools.mouse_velocity)
)
_drawer.pixel_perfect = Tools.pixel_perfect if _brush_size == 1 else false
_drawer.horizontal_mirror = Tools.horizontal_mirror
_drawer.vertical_mirror = Tools.vertical_mirror
@ -232,14 +240,16 @@ func _prepare_tool() -> void:
func _prepare_circle_tool(fill: bool) -> void:
var circle_tool_map := _create_circle_indicator(_brush_size, fill)
var circle_tool_map := _create_circle_indicator(_brush_size_dynamics, fill)
# Go through that BitMap and build an Array of the "displacement" from the center of the bits
# that are true.
var diameter := _brush_size * 2 + 1
var diameter := _brush_size_dynamics * 2 + 1
for n in range(0, diameter):
for m in range(0, diameter):
if circle_tool_map.get_bit(Vector2(m, n)):
_circle_tool_shortcut.append(Vector2(m - _brush_size, n - _brush_size))
_circle_tool_shortcut.append(
Vector2(m - _brush_size_dynamics, n - _brush_size_dynamics)
)
# Make sure to always have invoked _prepare_tool() before this. This computes the coordinates to be
@ -293,8 +303,8 @@ func draw_fill_gap(start: Vector2, end: Vector2) -> void:
# Compute the array of coordinates that should be drawn
func _compute_draw_tool_pixel(position: Vector2) -> PoolVector2Array:
var result := PoolVector2Array()
var start := position - Vector2.ONE * (_brush_size >> 1)
var end := start + Vector2.ONE * _brush_size
var start := position - Vector2.ONE * (_brush_size_dynamics >> 1)
var end := start + Vector2.ONE * _brush_size_dynamics
for y in range(start.y, end.y):
for x in range(start.x, end.x):
result.append(Vector2(x, y))
@ -303,7 +313,7 @@ func _compute_draw_tool_pixel(position: Vector2) -> PoolVector2Array:
# Compute the array of coordinates that should be drawn
func _compute_draw_tool_circle(position: Vector2, fill := false) -> PoolVector2Array:
var size := Vector2(_brush_size, _brush_size)
var size := Vector2(_brush_size_dynamics, _brush_size_dynamics)
var pos = position - (size / 2).floor()
if _circle_tool_shortcut:
return _draw_tool_circle_from_map(position)
@ -419,7 +429,6 @@ func _set_pixel(position: Vector2, ignore_mirroring := false) -> void:
func _set_pixel_no_cache(position: Vector2, ignore_mirroring := false) -> void:
position = _stroke_project.tiles.get_canon_position(position)
if !_stroke_project.can_pixel_get_drawn(position):
return
@ -430,10 +439,21 @@ func _set_pixel_no_cache(position: Vector2, ignore_mirroring := false) -> void:
else:
var i := int(position.x + position.y * _stroke_project.size.x)
if _mask.size() >= i + 1:
if _mask[i] < Tools.pen_pressure:
_mask[i] = Tools.pen_pressure
var alpha_dynamic: float = Tools.get_alpha_dynamic()
var alpha: float = images[0].get_pixelv(position).a
if _mask[i] < alpha_dynamic:
# Overwrite colors to avoid additive blending between strokes of
# brushes that are larger than 1px
# This is not a proper solution and it does not work if the pixels
# in the background are not transparent
var overwrite = _drawer.color_op.get("overwrite")
if overwrite != null and _mask[i] > alpha:
_drawer.color_op.overwrite = true
_mask[i] = alpha_dynamic
for image in images:
_drawer.set_pixel(image, position, tool_slot.color, ignore_mirroring)
if overwrite != null:
_drawer.color_op.overwrite = overwrite
else:
for image in images:
_drawer.set_pixel(image, position, tool_slot.color, ignore_mirroring)
@ -444,7 +464,7 @@ func _draw_brush_image(_image: Image, _src_rect: Rect2, _dst: Vector2) -> void:
func _create_blended_brush_image(image: Image) -> Image:
var size := image.get_size() * _brush_size
var size := image.get_size() * _brush_size_dynamics
var brush := Image.new()
brush.copy_from(image)
brush = _blend_image(brush, tool_slot.color, _brush_interpolate / 100.0)

View file

@ -1,23 +1,61 @@
extends PanelContainer
enum { ALPHA, SIZE }
var alpha_last_pressed: BaseButton = null
var size_last_pressed: BaseButton = null
onready var grid_container: GridContainer = find_node("GridContainer")
onready var horizontal_mirror: BaseButton = grid_container.get_node("Horizontal")
onready var vertical_mirror: BaseButton = grid_container.get_node("Vertical")
onready var pixel_perfect: BaseButton = grid_container.get_node("PixelPerfect")
onready var dynamics: Button = $"%Dynamics"
onready var dynamics_panel: PopupPanel = $DynamicsPanel
onready var alpha_pressure_button: CheckButton = $"%AlphaPressureButton"
onready var alpha_velocity_button: CheckButton = $"%AlphaVelocityButton"
onready var size_pressure_button: CheckButton = $"%SizePressureButton"
onready var size_velocity_button: CheckButton = $"%SizeVelocityButton"
onready var alpha_group: ButtonGroup = alpha_pressure_button.group
onready var size_group: ButtonGroup = size_pressure_button.group
func _ready() -> void:
# Resize tools panel when window gets resized
get_tree().get_root().connect("size_changed", self, "_on_resized")
horizontal_mirror.pressed = Tools.horizontal_mirror
vertical_mirror.pressed = Tools.vertical_mirror
pixel_perfect.pressed = Tools.pixel_perfect
alpha_pressure_button.connect(
"toggled",
self,
"_on_Dynamics_toggled",
[alpha_pressure_button, ALPHA, Tools.Dynamics.PRESSURE]
)
alpha_velocity_button.connect(
"toggled",
self,
"_on_Dynamics_toggled",
[alpha_velocity_button, ALPHA, Tools.Dynamics.VELOCITY]
)
size_pressure_button.connect(
"toggled",
self,
"_on_Dynamics_toggled",
[size_pressure_button, SIZE, Tools.Dynamics.PRESSURE]
)
size_velocity_button.connect(
"toggled",
self,
"_on_Dynamics_toggled",
[size_velocity_button, SIZE, Tools.Dynamics.VELOCITY]
)
func _on_resized() -> void:
var tool_panel_size: Vector2 = rect_size
var column_n = tool_panel_size.x / 36.5
var tool_panel_size := rect_size
var column_n := tool_panel_size.x / 36.5
if column_n < 1:
column_n = 1
@ -65,3 +103,80 @@ func _on_PixelPerfect_toggled(button_pressed: bool) -> void:
if !button_pressed:
file_name = "pixel_perfect_off.png"
Global.change_button_texturerect(texture_button, file_name)
func _on_Dynamics_pressed() -> void:
var pos := dynamics.rect_global_position + Vector2(0, 32)
dynamics_panel.popup(Rect2(pos, dynamics_panel.rect_size))
func _on_Dynamics_toggled(
button_pressed: bool, button: BaseButton, property: int, dynamic: int
) -> void:
if button_pressed:
var last_pressed: BaseButton
# The button calling this method is the one that was just selected
# var pressed_button: BaseButton
match property:
ALPHA:
last_pressed = alpha_last_pressed
# pressed_button = alpha_group.get_pressed_button()
SIZE:
last_pressed = size_last_pressed
# pressed_button = size_group.get_pressed_button()
if last_pressed == button:
# The button calling the method was the last one that was selected (we clicked it twice in a row)
# Toggle it off and set last_pressed to null so we can click it a third time to toggle it back on
button.pressed = false
_set_last_pressed_button(property, null)
# Update the last button pressed if we clicked something different
else:
_set_last_pressed_button(property, button)
var final_dynamic := dynamic
if not button.pressed:
final_dynamic = Tools.Dynamics.NONE
match property:
ALPHA:
Tools.dynamics_alpha = final_dynamic
SIZE:
Tools.dynamics_size = final_dynamic
func _set_last_pressed_button(prop: int, value: BaseButton) -> void:
match prop:
ALPHA:
alpha_last_pressed = value
SIZE:
size_last_pressed = value
func _on_ThresholdPressureMin_value_changed(value: float) -> void:
Tools.pen_pressure_min = value
func _on_ThresholdPressureMax_value_changed(value: float) -> void:
Tools.pen_pressure_max = value
func _on_ThresholdVelocityMin_value_changed(value: float) -> void:
Tools.mouse_velocity_min_thres = value
func _on_ThresholdVelocityMax_value_changed(value: float) -> void:
Tools.mouse_velocity_max_thres = value
func _on_AlphaMin_value_changed(value: float) -> void:
Tools.alpha_min = value
func _on_AlphaMax_value_changed(value: float) -> void:
Tools.alpha_max = value
func _on_SizeMin_value_changed(value: float) -> void:
Tools.brush_size_min = int(value)
func _on_SizeMax_value_changed(value: float) -> void:
Tools.brush_size_max = int(value)

View file

@ -1,9 +1,14 @@
[gd_scene load_steps=5 format=2]
[gd_scene load_steps=8 format=2]
[ext_resource path="res://assets/graphics/misc/horizontal_mirror_off.png" type="Texture" id=1]
[ext_resource path="res://assets/graphics/misc/vertical_mirror_off.png" type="Texture" id=2]
[ext_resource path="res://src/UI/GlobalToolOptions/GlobalToolOptions.gd" type="Script" id=3]
[ext_resource path="res://assets/graphics/misc/pixel_perfect_off.png" type="Texture" id=4]
[ext_resource path="res://src/UI/Nodes/ValueSlider.gd" type="Script" id=5]
[sub_resource type="ButtonGroup" id=1]
[sub_resource type="ButtonGroup" id=2]
[node name="Global Tool Options" type="PanelContainer"]
margin_left = 1.0
@ -20,18 +25,16 @@ margin_bottom = 43.0
rect_min_size = Vector2( 36, 36 )
[node name="CenterContainer" type="CenterContainer" parent="ScrollContainer"]
margin_right = 180.0
margin_bottom = 36.0
margin_right = 183.0
margin_bottom = 32.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="GridContainer" type="GridContainer" parent="ScrollContainer/CenterContainer"]
margin_left = 38.0
margin_top = 2.0
margin_right = 142.0
margin_bottom = 34.0
margin_right = 183.0
margin_bottom = 32.0
size_flags_vertical = 0
columns = 3
columns = 4
[node name="Horizontal" type="Button" parent="ScrollContainer/CenterContainer/GridContainer" groups=["UIButtons"]]
margin_right = 32.0
@ -99,7 +102,340 @@ margin_right = 11.0
margin_bottom = 10.0
texture = ExtResource( 4 )
[node name="Dynamics" type="Button" parent="ScrollContainer/CenterContainer/GridContainer"]
unique_name_in_owner = true
margin_left = 108.0
margin_right = 183.0
margin_bottom = 32.0
mouse_default_cursor_shape = 2
text = "Dynamics"
[node name="DynamicsPanel" type="PopupPanel" parent="."]
margin_left = 7.0
margin_top = 7.0
margin_right = 267.0
margin_bottom = 267.0
rect_min_size = Vector2( 260, 260 )
[node name="VBoxContainer" type="VBoxContainer" parent="DynamicsPanel"]
margin_left = 4.0
margin_top = 4.0
margin_right = 256.0
margin_bottom = 256.0
[node name="DynamicsOptions" type="GridContainer" parent="DynamicsPanel/VBoxContainer"]
margin_right = 252.0
margin_bottom = 108.0
columns = 3
[node name="Control" type="Control" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
margin_right = 82.0
margin_bottom = 20.0
rect_min_size = Vector2( 20, 20 )
size_flags_horizontal = 3
[node name="PressureLabel" type="Label" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
margin_left = 86.0
margin_top = 3.0
margin_right = 167.0
margin_bottom = 17.0
size_flags_horizontal = 3
text = "Pressure"
align = 1
[node name="VelocityLabel" type="Label" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
margin_left = 171.0
margin_top = 3.0
margin_right = 252.0
margin_bottom = 17.0
size_flags_horizontal = 3
text = "Velocity"
align = 1
[node name="AlphaLabel" type="Label" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
margin_top = 37.0
margin_right = 82.0
margin_bottom = 51.0
text = "Alpha"
[node name="AlphaPressureButton" type="CheckButton" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
unique_name_in_owner = true
margin_left = 86.0
margin_top = 24.0
margin_right = 167.0
margin_bottom = 64.0
mouse_default_cursor_shape = 2
group = SubResource( 1 )
[node name="AlphaVelocityButton" type="CheckButton" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
unique_name_in_owner = true
margin_left = 171.0
margin_top = 24.0
margin_right = 252.0
margin_bottom = 64.0
mouse_default_cursor_shape = 2
group = SubResource( 1 )
[node name="SizeLabel" type="Label" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
margin_top = 81.0
margin_right = 82.0
margin_bottom = 95.0
text = "Size"
[node name="SizePressureButton" type="CheckButton" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
unique_name_in_owner = true
margin_left = 86.0
margin_top = 68.0
margin_right = 167.0
margin_bottom = 108.0
mouse_default_cursor_shape = 2
group = SubResource( 2 )
[node name="SizeVelocityButton" type="CheckButton" parent="DynamicsPanel/VBoxContainer/DynamicsOptions"]
unique_name_in_owner = true
margin_left = 171.0
margin_top = 68.0
margin_right = 252.0
margin_bottom = 108.0
mouse_default_cursor_shape = 2
group = SubResource( 2 )
[node name="LimitsHeader" type="HBoxContainer" parent="DynamicsPanel/VBoxContainer"]
margin_top = 112.0
margin_right = 252.0
margin_bottom = 126.0
[node name="Label" type="Label" parent="DynamicsPanel/VBoxContainer/LimitsHeader"]
margin_right = 76.0
margin_bottom = 14.0
theme_type_variation = "Header"
text = "Value limits"
[node name="HSeparator" type="HSeparator" parent="DynamicsPanel/VBoxContainer/LimitsHeader"]
margin_left = 80.0
margin_right = 252.0
margin_bottom = 14.0
size_flags_horizontal = 3
[node name="LimitContainer" type="GridContainer" parent="DynamicsPanel/VBoxContainer"]
margin_top = 130.0
margin_right = 252.0
margin_bottom = 162.0
columns = 3
[node name="AlphaLabel" type="Label" parent="DynamicsPanel/VBoxContainer/LimitContainer"]
margin_right = 82.0
margin_bottom = 14.0
size_flags_horizontal = 3
text = "Alpha"
[node name="AlphaMin" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/LimitContainer"]
margin_left = 86.0
margin_right = 167.0
margin_bottom = 14.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 1.0
step = 0.01
value = 0.1
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 5 )
prefix = "Min"
snap_step = 0.1
[node name="AlphaMax" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/LimitContainer"]
margin_left = 171.0
margin_right = 252.0
margin_bottom = 14.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 1.0
step = 0.01
value = 1.0
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 5 )
prefix = "Max"
snap_step = 0.1
[node name="SizeLabel" type="Label" parent="DynamicsPanel/VBoxContainer/LimitContainer"]
margin_top = 18.0
margin_right = 82.0
margin_bottom = 32.0
size_flags_horizontal = 3
text = "Size"
[node name="SizeMin" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/LimitContainer"]
margin_left = 86.0
margin_top = 18.0
margin_right = 167.0
margin_bottom = 32.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 25.0
value = 1.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 )
prefix = "Min"
[node name="SizeMax" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/LimitContainer"]
margin_left = 171.0
margin_top = 18.0
margin_right = 252.0
margin_bottom = 32.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 25.0
value = 4.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 )
prefix = "Max"
[node name="ThresholdsHeader" type="HBoxContainer" parent="DynamicsPanel/VBoxContainer"]
margin_top = 166.0
margin_right = 252.0
margin_bottom = 180.0
[node name="Label" type="Label" parent="DynamicsPanel/VBoxContainer/ThresholdsHeader"]
margin_right = 70.0
margin_bottom = 14.0
theme_type_variation = "Header"
text = "Thresholds"
[node name="HSeparator" type="HSeparator" parent="DynamicsPanel/VBoxContainer/ThresholdsHeader"]
margin_left = 74.0
margin_right = 252.0
margin_bottom = 14.0
size_flags_horizontal = 3
[node name="ThresholdContainer" type="GridContainer" parent="DynamicsPanel/VBoxContainer"]
margin_top = 184.0
margin_right = 252.0
margin_bottom = 216.0
columns = 3
[node name="ThresholdPressureLabel" type="Label" parent="DynamicsPanel/VBoxContainer/ThresholdContainer"]
margin_right = 82.0
margin_bottom = 14.0
size_flags_horizontal = 3
text = "Pressure"
[node name="ThresholdPressureMin" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/ThresholdContainer"]
margin_left = 86.0
margin_right = 167.0
margin_bottom = 14.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 1.0
step = 0.01
value = 0.2
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 5 )
prefix = "Min"
snap_step = 0.1
[node name="ThresholdPressureMax" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/ThresholdContainer"]
margin_left = 171.0
margin_right = 252.0
margin_bottom = 14.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 1.0
step = 0.01
value = 0.8
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 5 )
prefix = "Max"
snap_step = 0.1
[node name="ThresholdVelocityLabel" type="Label" parent="DynamicsPanel/VBoxContainer/ThresholdContainer"]
margin_top = 18.0
margin_right = 82.0
margin_bottom = 32.0
size_flags_horizontal = 3
text = "Velocity"
[node name="ThresholdVelocityMin" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/ThresholdContainer"]
margin_left = 86.0
margin_top = 18.0
margin_right = 167.0
margin_bottom = 32.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 1.0
step = 0.01
value = 0.2
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 5 )
prefix = "Min"
snap_step = 0.1
[node name="ThresholdVelocityMax" type="TextureProgress" parent="DynamicsPanel/VBoxContainer/ThresholdContainer"]
margin_left = 171.0
margin_top = 18.0
margin_right = 252.0
margin_bottom = 32.0
mouse_default_cursor_shape = 2
size_flags_horizontal = 3
theme_type_variation = "ValueSlider"
max_value = 1.0
step = 0.01
value = 0.8
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource( 5 )
prefix = "Max"
snap_step = 0.1
[connection signal="resized" from="." to="." method="_on_resized"]
[connection signal="toggled" from="ScrollContainer/CenterContainer/GridContainer/Horizontal" to="." method="_on_Horizontal_toggled"]
[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="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"]
[connection signal="value_changed" from="DynamicsPanel/VBoxContainer/LimitContainer/SizeMax" to="." method="_on_SizeMax_value_changed"]
[connection signal="value_changed" from="DynamicsPanel/VBoxContainer/ThresholdContainer/ThresholdPressureMin" to="." method="_on_ThresholdPressureMin_value_changed"]
[connection signal="value_changed" from="DynamicsPanel/VBoxContainer/ThresholdContainer/ThresholdPressureMax" to="." method="_on_ThresholdPressureMax_value_changed"]
[connection signal="value_changed" from="DynamicsPanel/VBoxContainer/ThresholdContainer/ThresholdVelocityMin" to="." method="_on_ThresholdVelocityMin_value_changed"]
[connection signal="value_changed" from="DynamicsPanel/VBoxContainer/ThresholdContainer/ThresholdVelocityMax" to="." method="_on_ThresholdVelocityMax_value_changed"]