feat: Implement draggable torso and head IK targets in the test harness

- Added draggable handles for the torso and head to the test harness.
- Updated `IK_HANDLE_PATHS` to include new entries for "Head" and "Torso".
- Implemented distinct colors for the torso (magenta) and head (yellow) markers.
- Added a visual aid (aim line) to indicate the head's LookAt target direction.
- Ensured that dragging the torso moves only the torso marker, allowing for limb stretching towards stationary targets.
This commit is contained in:
2026-08-21 12:39:31 -04:00
parent 6b273c049c
commit ab5c79ab6a
14 changed files with 1459 additions and 174 deletions
+23 -3
View File
@@ -48,10 +48,10 @@ const DEFAULT_POSITIONS: Dictionary = {
"right_lower_leg": Vector2(165, 210),
}
const FILE_VERSION := "1.4"
const FILE_VERSION := "1.5"
const FILE_FILTER := "*.stk ; Stickman Files"
const SUPPORTED_VERSIONS: Array[String] = ["1.0", "1.1", "1.2", "1.3", "1.4"]
const SUPPORTED_VERSIONS: Array[String] = ["1.0", "1.1", "1.2", "1.3", "1.4", "1.5"]
# Phase 8: rig proportions (master_rig.tscn rest pose — see spec §2).
const PROPORTIONS: Dictionary = {
@@ -66,6 +66,22 @@ const X_AXIS_PARTS: PackedStringArray = [
"left_upper_arm", "left_lower_arm", "right_upper_arm", "right_lower_arm",
]
# Phase 9 Round 5: map each body part to its silhouette-guide joint (see
# whole_stickman_preview.gd GUIDE_JOINTS). Used to compute the per-part
# guide_offset (bbox center guide joint, preview space) at save time.
const GUIDE_JOINT_FOR_PART: Dictionary = {
"head": "Neck",
"torso": "Hips",
"left_upper_arm": "LeftShoulder",
"left_lower_arm": "LeftElbow",
"right_upper_arm": "RightShoulder",
"right_lower_arm": "RightElbow",
"left_upper_leg": "Hips",
"left_lower_leg": "LeftKnee",
"right_upper_leg": "Hips",
"right_lower_leg": "RightKnee",
}
const SETTINGS_PATH := "user://settings.json"
const SETTINGS_VERSION := "1.0"
const DEFAULT_GRID_SIZE := 15
@@ -413,6 +429,9 @@ func _collect_all_shape_data() -> Dictionary:
var rot := _whole_preview.get_part_rotation(part_name)
var scl := _whole_preview.get_part_scale(part_name)
var pl := _compute_part_pivot_length(shapes_arr, part_name)
var pivot_vector := Vector2(float(pl["pivot"]["x"]), float(pl["pivot"]["y"]))
var joint_preview := _whole_preview.get_guide_joint_preview(GUIDE_JOINT_FOR_PART[part_name])
var guide_offset := (pos + pivot_vector) - joint_preview
all_data[part_name] = {
"shapes": shapes_arr,
"position": {"x": pos.x, "y": pos.y},
@@ -420,6 +439,7 @@ func _collect_all_shape_data() -> Dictionary:
"scale": {"x": scl.x, "y": scl.y},
"pivot": pl["pivot"],
"length": pl["length"],
"guide_offset": {"x": guide_offset.x, "y": guide_offset.y},
}
return all_data
@@ -449,7 +469,7 @@ func _apply_json_data(json: Variant) -> String:
var version: String = dict.get("version", "")
if version not in SUPPORTED_VERSIONS:
return "Unsupported file version: '%s' (expected '1.0', '1.1', '1.2', '1.3', or '1.4')" % version
return "Unsupported file version: '%s' (expected '1.0', '1.1', '1.2', '1.3', '1.4', or '1.5')" % version
_stickman_name_edit.text = dict.get("stickman_name", "")