feat: Implement Stickman Factory and Test Harness for runtime rigging

- Added StickmanFactory to load and instantiate .stk files into master_rig.tscn.
- Created a Test Harness scene for debugging bone scales, vector drawing offsets, and IK limits.
- Updated stk_rig_adapter to fit head bone and mount head shapes as full geometry.
- Enhanced README and PROJECT documentation to reflect new features and usage.
- Introduced UI elements for file selection and visual debugging in the Test Harness.
- Refactored code to improve clarity and maintainability, including removal of unused functions.
This commit is contained in:
2026-08-18 21:44:56 -04:00
parent 48d98ce0fe
commit 1252728c06
10 changed files with 618 additions and 95 deletions
+30 -1
View File
@@ -239,6 +239,30 @@ The Whole Stickman preview treats all shapes in a body-part panel as **one combi
2. A confirmation dialog warns that the current stickman will be cleared.
3. Confirm to reset all panels and the preview (zoom resets to 100%, pan offset to origin).
### 14. Test Harness & Stickman Factory (Phase 9)
Phase 9 adds the **runtime pipeline** that turns a saved `.stk` file into a live, rigged `master_rig.tscn` instance — plus a standalone staging scene for debugging it in isolation. **Neither is wired into the editor.**
**Stickman Factory (`res://scripts/stickman_factory.gd`)** — the runtime entry point:
| Method | Signature | Behavior |
|---|---|---|
| `load_stk` | `static func load_stk(path: String) -> Dictionary` | Reads a `.stk` file via `FileAccess` + `JSON.parse_string`. Returns `{}` and `push_warning` on failure. |
| `spawn_from_data` | `static func spawn_from_data(stk_data: Dictionary) -> Node2D` | Instantiates `res://master_rig.tscn`, calls `StkRigAdapter.apply(stk_data, rig)`, returns the rig root. |
| `spawn` | `static func spawn(path: String) -> Node2D` | Chains `load_stk()``spawn_from_data()`. Returns `null` on empty data. |
The factory is the intended runtime API: `StickmanFactory.spawn("res://stickmen/basic.stk")` yields a rigged `Node2D` ready to add to the scene tree. `StkRigAdapter` (Phase 8, extended by Phase 9) now also fits the head bone (`Head.position.y = -proportions.torso_length`) and mounts the head as **full geometry** like every other part (clearing its inline `@tool` circle script).
**Test Harness (`res://scenes/test_harness.tscn`)** — run via **F6** on the scene (NOT via the main editor scene). Controls:
- **Open .stk…** — `FileDialog` filtered to `*.stk`; quick-select buttons for `res://stickmen/break.stk`, `res://stickmen/basic.stk`, `res://stickmen/test.stk`.
- **Show Bones / Show IK Handles** — checkboxes toggling the debug overlay.
- **Loaded filename** — status label showing the currently loaded file.
- **Pan / zoom** — middle-mouse drag to pan, mouse-wheel to zoom the `Camera2D`; the camera recenters on each spawn.
- **IK drag** — click and drag any of the 4 limb `Marker2D` IK targets (`IK_Targets/Left_Hand`, `Right_Hand`, `Left_Leg`, `Right_Leg`); the scene's `SkeletonModificationStack2D` TwoBoneIK flexes the limb live. The harness enables the modification stack after each spawn.
Debug overlay (a world-space `Node2D` `_draw()`): bone lines drawn between each `Bone2D` global origin and its parent's (color-coded left cyan / right orange / central white, with joint dots) when **Show Bones** is on; colored markers at the four IK targets when **Show IK Handles** is on. Each load frees the previous rig and spawns a fresh one.
## File format (`.stk`)
Files are UTF-8 JSON, pretty-printed with tab indentation. The format is versioned and designed to remain **backward/forward compatible** — new fields can be added without breaking older files.
@@ -373,7 +397,10 @@ Behavior:
| `res://scenes/stickman_editor.tscn` | **Main scene** — editor layout, File/Edit/View menu bar, dialogs (`GridConfigDialog` + SpinBox), column containers (unique-name nodes). |
| `res://scenes/body_part_panel.tscn` | Reusable single body-part editor panel (title, drawing area, context menu, `ColorPickerPopup`); expands vertically in its column. |
| `res://scripts/stickman_editor.gd` | Editor controller — File/Edit/View menu actions, save/load/clear, JSON v1.4 serialization with multi-shape/rotation/scale, `part_order`, and Phase 8 `proportions`/`pivot`/`length`, `settings.json` load/save, editor-wide shape clipboard (Copy/Paste across panels), broadcast of grid/snap settings to panels, Reset Views, populates panels, coordinates selection across panels. |
| `res://scripts/stk_rig_adapter.gd` | **Phase 8.** Standalone runtime adapter (`class_name StkRigAdapter`, `static func apply(stk_data, rig)`): fits an instantiated `master_rig.tscn` to a loaded `.stk` by re-fitting the 8 limb bones (`Skeleton2D/Torso/...` `Bone2D` lengths + lower-bone origins), recalibrating the IK targets (`IK_Targets/Left|Right_Hand`, `Left|Right_Leg`), and mounting the `.stk` shapes onto the `Body/*` visual nodes (open shapes → `Line2D`, closed → `Polygon2D` fill + `Line2D` outline, width 16). **Not used by the editor** — consumed by a future runtime pipeline. |
| `res://scripts/stk_rig_adapter.gd` | **Phase 8, extended by Phase 9.** Standalone runtime adapter (`class_name StkRigAdapter`, `static func apply(stk_data, rig)`): fits an instantiated `master_rig.tscn` to a loaded `.stk` by re-fitting the 8 limb bones (`Skeleton2D/Torso/...` `Bone2D` lengths + lower-bone origins), recalibrating the IK targets (`IK_Targets/Left|Right_Hand`, `Left|Right_Leg`), mounting the `.stk` shapes onto the `Body/*` visual nodes (open shapes → `Line2D`, closed → `Polygon2D` fill + `Line2D` outline, width 16), and (Phase 9) fitting the head bone (`Head.position.y = -proportions.torso_length`) while mounting the head as **full geometry** — it clears the head's inline `@tool` circle script and mounts `.stk` head shapes as `Line2D`/`Polygon2D`. **Not used by the editor** — consumed by the runtime pipeline. |
| `res://scripts/stickman_factory.gd` | **Phase 9.** Runtime entry point (`class_name StickmanFactory`, `extends RefCounted`); a static factory that turns a `.stk` file into a live, rigged `master_rig.tscn` instance. `load_stk(path)` reads + parses the file (`{}` + `push_warning` on failure); `spawn_from_data(stk_data)` instantiates `res://master_rig.tscn` and calls `StkRigAdapter.apply(stk_data, rig)`; `spawn(path)` chains them (`null` on empty data). **Not used by the editor.** |
| `res://scripts/test_harness.gd` | **Phase 9.** Standalone staging scene (run via **F6** on `res://scenes/test_harness.tscn`, not wired into the editor) for debugging bone scales, vector-drawing offsets, and IK limits in isolation. Top UI bar: "Open .stk…" / quick-select buttons (`stickmen/break.stk`, `stickmen/basic.stk`, `stickmen/test.stk`), "Show Bones" / "Show IK Handles" toggles, loaded-filename label. `SubViewport` world + enabled `Camera2D` (middle-mouse pan, wheel zoom, recenter on spawn); each load frees the previous rig and spawns a fresh one via `StickmanFactory.spawn()`. A world-space debug overlay draws bone lines and IK-target markers; the 4 limb `Marker2D` IK targets are click-draggable, flexing limbs live via `SkeletonModificationStack2D` TwoBoneIK (enabled after each spawn). |
| `res://scenes/test_harness.tscn` | **Phase 9.** Standalone staging scene backing `scripts/test_harness.gd` (run via **F6**; not wired into the editor). |
| `res://scripts/body_part_panel.gd` | Multi-shape creation, vertex editing, shape dragging, per-panel zoom & pan, grid drawing & snap-to-grid, ColorPicker, shape/vertex delete, Z-ordering (Send Back / Bring Forward), shape Copy/Paste, shape Mirror X/Y, drawing (fill + outline for closed shapes). |
| `res://scripts/whole_stickman_preview.gd` | Assembly preview, drag-to-reposition, part selection with white bounding box, rotation gizmo (circle below box) with Ctrl 15° snap, scale gizmo (corner crosses) with Ctrl aspect lock, part Z-ordering (Send Back / Bring Forward) via `part_order`, part Mirror X/Y (scale negation), zoom & pan, grid drawing & snap-to-grid, pose silhouette guide (Phase 7), part hit-bounds, labels. |
| `res://addons/curved_lines_2d/` | Scalable Vector Shapes 2D addon (v2.27.7) — required dependency. |
@@ -456,3 +483,5 @@ BodyPartPanel.shape_selected() ---(bound to part_name)---> stickman_editor
> **Phase 7:** Adds the pose silhouette guide to the Whole Stickman preview — a semi-transparent, color-coded stick figure (left cyan-blue, right orange-red, central white) whose 13 joint anchors match the rest-pose pivots of the future `master_rig.tscn` rig, drawn above the grid and in front of user parts (ghosting over them, below the selection gizmos and drag highlight), centered in the preview at the default view. Toggleable via the dynamic **View → Hide/Show Pose Guide** label (on by default, persisted to `settings.json`). The guide is a view aid only: **no `.stk` format change** — `FILE_VERSION` stays `"1.3"`.
> **Phase 8:** The `.stk` export evolved to **v1.4** with write-only metadata the editor never reads back. A top-level `proportions` object stores the 5 master-rig rest-pose bone lengths (`upper_arm_length` 168.0, `lower_arm_length` 200.0, `upper_leg_length` 200.0, `lower_leg_length` 200.0, `torso_length` 391.5) — hardcoded constants from `master_rig.tscn`, not measured from the user's shapes. Each `body_parts` entry gains `pivot` (local bounding-box center = rotation origin) and `length` (bbox extent along the segment axis: width for arms, height for torso/legs/head). v1.0v1.3 files load unchanged and gain these keys on their next save. A new standalone `res://scripts/stk_rig_adapter.gd` (`class_name StkRigAdapter`) fits an instantiated `master_rig.tscn` to a loaded `.stk` (bone fitting + IK recalibration + visual shape mount); it is **not** used by the editor and is reserved for a future runtime pipeline.
> **Phase 9:** Adds the **runtime pipeline** for turning a `.stk` file into a live, rigged `master_rig.tscn` instance, plus a standalone staging scene to debug it. A new `res://scripts/stickman_factory.gd` (`class_name StickmanFactory`) provides the runtime entry point: `load_stk(path)` reads/parses a `.stk` (`FileAccess` + `JSON.parse_string`, `{}` + `push_warning` on failure), `spawn_from_data(stk_data)` instantiates `master_rig.tscn` and applies `StkRigAdapter.apply(stk_data, rig)`, and `spawn(path)` chains them (`null` on empty data). `StkRigAdapter` is extended to also fit the head bone (`Head.position.y = -proportions.torso_length`) and to mount the head as **full geometry** like every other part — clearing its inline `@tool` circle script and mounting `.stk` head shapes as `Line2D`/`Polygon2D` (removed the dead `_mount_head_circle`, `_compute_shapes_bbox`, and `_first_shape_color` helpers). A new standalone scene/resource pair, `res://scenes/test_harness.tscn` + `res://scripts/test_harness.gd` (run via **F6**), loads `.stk` files (open dialog + quick-select for `stickmen/break.stk`, `stickmen/basic.stk`, `stickmen/test.stk`), toggles a world-space debug overlay (bone lines + IK-target markers), pans/zooms a `Camera2D`, and lets you click-drag the 4 limb IK targets with live TwoBoneIK flexing (`SkeletonModificationStack2D` enabled on spawn). Neither the factory nor the harness is wired into the editor. **No `.stk` format change** — `FILE_VERSION` stays `"1.4"`.