File size: 2,510 Bytes
7faaef2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""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",
]