"""studio.rigging — auto-rigging foundation (Phase 3). Picked stack (see ../../PHASE_3_RESEARCH.md): segment.flood_fill_segment — flood-fill from corners + alpha fast path joints.detect_joints — distance-transform + mask-topology hybrid overlay.render_overlay — numpy + PIL debug visualisation sidecar.load_sidecar/save_sidecar — operator-annotation JSON I/O Top-level entry point: rig_sprite(image_path) — tries sidecar first, falls back to auto-detect Mesh skinning and pose deformation belong to Session 3 and are not implemented here. """ from __future__ import annotations from pathlib import Path from typing import Tuple import numpy as np from PIL import Image from pixel_cursor.rigging import Skeleton from .deform import deform_sprite, forward_kinematics from .joints import detect_joints from .overlay import render_overlay from .parts import ( CANONICAL_HUMANOID_PIECES, PartsMask, Piece, canonical_humanoid_pieces, load_parts, parts_json_path_for, parts_png_path_for, parts_preview_path_for, save_parts, save_preview, ) from .rigid_piece import deform_sprite_rigid from .segment import flood_fill_segment from .sidecar import ( SCHEMA_VERSION, load_sidecar, save_sidecar, sidecar_path_for, sidecar_template, ) from .skin import compute_skin_weights def rig_sprite(image_path: Path | str) -> Tuple[Skeleton, np.ndarray, str]: """Produce a 14-joint skeleton for a sprite on disk. Strategy: sidecar JSON first (operator-authoritative), auto-detect fallback. Returns (skeleton, mask, source) where source ∈ {"sidecar", "auto"}. """ path = Path(image_path) image = np.asarray(Image.open(path)) mask = flood_fill_segment(image) sidecar = load_sidecar(path, image_shape=mask.shape) if sidecar is not None: return sidecar, mask, "sidecar" return detect_joints(mask), mask, "auto" __all__ = [ "flood_fill_segment", "detect_joints", "render_overlay", "rig_sprite", "load_sidecar", "save_sidecar", "sidecar_path_for", "sidecar_template", "SCHEMA_VERSION", "compute_skin_weights", "forward_kinematics", "deform_sprite", "CANONICAL_HUMANOID_PIECES", "PartsMask", "Piece", "canonical_humanoid_pieces", "load_parts", "parts_json_path_for", "parts_png_path_for", "parts_preview_path_for", "save_parts", "save_preview", "deform_sprite_rigid", ]