Implement Phase 6 features for Stickman Editor
- Updated BUGS.md with new issues related to selection bounding box, touchpad panning speed, and zooming behavior. - Enhanced PROJECT.md with detailed specifications for touchpad controls, recent colors in the color picker, and a status bar for project information. - Modified project.godot to update compatibility version to 4.7. - Added a status bar in stickman_editor.tscn to display cursor coordinates and snap status. - Updated body_part_panel.gd to handle recent colors and cursor detection over drawing areas. - Enhanced stickman_editor.gd to manage cursor coordinates display and recent colors tracking. - Improved whole_stickman_preview.gd to render selection gizmos on top of other parts. - Created master_rig.tscn for the stickman rig structure with bones and IK targets. - Developed master_rig_builder.gd to automate the rig building process with remote transforms and IK modifications.
This commit is contained in:
@@ -92,6 +92,9 @@ var _pan_start: Vector2 = Vector2.ZERO
|
||||
var _part_order: Array[String] = []
|
||||
var _context_menu: PopupMenu
|
||||
|
||||
# Phase 6: selection gizmos rendered on top
|
||||
var _selected_gizmo_bounds: Rect2 = Rect2()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public API
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -215,6 +218,14 @@ func reset_view() -> void:
|
||||
preview_area.queue_redraw()
|
||||
|
||||
|
||||
func is_cursor_over_preview(global_pos: Vector2) -> bool:
|
||||
return preview_area.get_global_rect().has_point(global_pos)
|
||||
|
||||
|
||||
func global_to_world(global_pos: Vector2) -> Vector2:
|
||||
return _screen_to_world(global_pos - preview_area.global_position)
|
||||
|
||||
|
||||
func get_part_order() -> Array:
|
||||
return _part_order.duplicate()
|
||||
|
||||
@@ -249,6 +260,7 @@ func _on_preview_draw() -> void:
|
||||
preview_area.draw_set_transform(_pan_offset, 0.0, Vector2(_zoom, _zoom))
|
||||
|
||||
_draw_grid()
|
||||
_selected_gizmo_bounds = Rect2()
|
||||
|
||||
for part_name: String in _part_order:
|
||||
if not _part_shapes.has(part_name):
|
||||
@@ -305,31 +317,11 @@ func _on_preview_draw() -> void:
|
||||
var label_pos := Vector2(bounds.position.x, bounds.position.y - 14)
|
||||
preview_area.draw_string(font, label_pos, label, HORIZONTAL_ALIGNMENT_LEFT, -1, 11, Color.WHITE)
|
||||
|
||||
# Phase 4: Draw gizmos for selected part
|
||||
# Store bounds for selected part (gizmos drawn on top later)
|
||||
if part_name == _selected_part:
|
||||
var bounds := _compute_transformed_bounds_from_points(all_raw, center, rot_deg, scl)
|
||||
if bounds.has_area():
|
||||
# White bounding box
|
||||
preview_area.draw_rect(bounds, Color.WHITE, false, 1.5 / _zoom)
|
||||
|
||||
# Rotation circle
|
||||
var rot_center := Vector2(bounds.position.x + bounds.size.x * 0.5, bounds.end.y + ROTATION_CIRCLE_OFFSET)
|
||||
preview_area.draw_circle(rot_center, ROTATION_CIRCLE_RADIUS / _zoom, Color.WHITE)
|
||||
|
||||
# Scale crosses at 4 corners
|
||||
var cross_half := SCALE_CROSS_SIZE / _zoom
|
||||
for i: int in range(4):
|
||||
var corner := _get_corner_position(bounds, i)
|
||||
# Horizontal line
|
||||
preview_area.draw_line(
|
||||
corner + Vector2(-cross_half, 0),
|
||||
corner + Vector2(cross_half, 0),
|
||||
Color.WHITE, 1.5 / _zoom)
|
||||
# Vertical line
|
||||
preview_area.draw_line(
|
||||
corner + Vector2(0, -cross_half),
|
||||
corner + Vector2(0, cross_half),
|
||||
Color.WHITE, 1.5 / _zoom)
|
||||
_selected_gizmo_bounds = bounds
|
||||
|
||||
# Highlight dragged part
|
||||
if not _dragging_part.is_empty() and _interaction == Interaction.TRANSLATE:
|
||||
@@ -337,6 +329,24 @@ func _on_preview_draw() -> void:
|
||||
if bounds is Rect2:
|
||||
preview_area.draw_rect(bounds as Rect2, Color(1.0, 0.8, 0.0, 0.35), false, 1.5)
|
||||
|
||||
# Draw selection gizmos on top of all parts
|
||||
if _selected_gizmo_bounds.has_area():
|
||||
var b: Rect2 = _selected_gizmo_bounds
|
||||
preview_area.draw_rect(b, Color.WHITE, false, 1.5 / _zoom)
|
||||
var rot_center := Vector2(b.position.x + b.size.x * 0.5, b.end.y + ROTATION_CIRCLE_OFFSET)
|
||||
preview_area.draw_circle(rot_center, ROTATION_CIRCLE_RADIUS / _zoom, Color.WHITE)
|
||||
var cross_half := SCALE_CROSS_SIZE / _zoom
|
||||
for i: int in range(4):
|
||||
var corner := _get_corner_position(b, i)
|
||||
preview_area.draw_line(
|
||||
corner + Vector2(-cross_half, 0),
|
||||
corner + Vector2(cross_half, 0),
|
||||
Color.WHITE, 1.5 / _zoom)
|
||||
preview_area.draw_line(
|
||||
corner + Vector2(0, -cross_half),
|
||||
corner + Vector2(0, cross_half),
|
||||
Color.WHITE, 1.5 / _zoom)
|
||||
|
||||
|
||||
func _draw_grid() -> void:
|
||||
var gs := float(_grid_size)
|
||||
@@ -455,6 +465,21 @@ func _compute_original_bounds_size(part_name: String) -> Vector2:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
func _on_preview_gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMagnifyGesture:
|
||||
var mag := event as InputEventMagnifyGesture
|
||||
var cursor_local := preview_area.get_local_mouse_position()
|
||||
var world := _screen_to_world(cursor_local)
|
||||
_zoom = clampf(_zoom * mag.factor, MIN_ZOOM, MAX_ZOOM)
|
||||
_pan_offset = cursor_local - world * _zoom
|
||||
preview_area.queue_redraw()
|
||||
return
|
||||
|
||||
if event is InputEventPanGesture:
|
||||
var pan := event as InputEventPanGesture
|
||||
_pan_offset -= pan.delta * 3.0
|
||||
preview_area.queue_redraw()
|
||||
return
|
||||
|
||||
if event is InputEventMouseButton:
|
||||
var mb := event as InputEventMouseButton
|
||||
|
||||
@@ -470,11 +495,17 @@ func _on_preview_gui_input(event: InputEvent) -> void:
|
||||
|
||||
# Mouse wheel -> zoom
|
||||
if mb.button_index == MOUSE_BUTTON_WHEEL_UP and mb.pressed:
|
||||
var cursor_local := mb.position
|
||||
var world := _screen_to_world(cursor_local)
|
||||
_zoom = clampf(_zoom * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)
|
||||
_pan_offset = cursor_local - world * _zoom
|
||||
preview_area.queue_redraw()
|
||||
return
|
||||
if mb.button_index == MOUSE_BUTTON_WHEEL_DOWN and mb.pressed:
|
||||
var cursor_local := mb.position
|
||||
var world := _screen_to_world(cursor_local)
|
||||
_zoom = clampf(_zoom / ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)
|
||||
_pan_offset = cursor_local - world * _zoom
|
||||
preview_area.queue_redraw()
|
||||
return
|
||||
|
||||
@@ -495,7 +526,7 @@ func _on_preview_gui_input(event: InputEvent) -> void:
|
||||
var mm := event as InputEventMouseMotion
|
||||
|
||||
if _is_panning:
|
||||
_pan_offset += (mm.position - _pan_start) / _zoom
|
||||
_pan_offset += (mm.position - _pan_start)
|
||||
_pan_start = mm.position
|
||||
preview_area.queue_redraw()
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user