Add a pose review harness and record the blocked pose capture
Full build / Import + headless tests (push) Successful in 43s
Full build / Full desktop build + optional release (push) Skipped

This commit is contained in:
zwitschi committed 2026-11-03 10:59:23 +01:00
1 parent 2d67e14e52
commit 7a5b902797
7 files changed
+358 -13

No files matched your search

+12 -5
View File
@@ -52,21 +52,24 @@ def next_free_path(path: Path) -> Path:
index += 1
def build_sheet(source: Path, output: Path) -> Path:
def build_sheet(source: Path, output: Path, views: list[str] | None = None) -> Path:
"""Tile one capture per (variant, view). `views` defaults to the six baseline
views; the pose harness passes pose names instead (see the module docstring)."""
from PIL import Image, ImageDraw
views = views or VIEWS
cell_w, cell_h = 320, 180
label_h = 22
header_h = 28
sheet_w = cell_w * len(VIEWS)
sheet_w = cell_w * len(views)
sheet_h = header_h + (cell_h + label_h) * len(VARIANTS)
sheet = Image.new("RGB", (sheet_w, sheet_h), (24, 26, 30))
draw = ImageDraw.Draw(sheet)
for column, view in enumerate(VIEWS):
for column, view in enumerate(views):
draw.text((column * cell_w + 8, 8), view.replace("_", " ").title(),
fill=(220, 224, 230))
for row, variant in enumerate(VARIANTS):
for column, view in enumerate(VIEWS):
for column, view in enumerate(views):
path = latest_for(source, f"{variant}__{view}")
if path is None:
raise FileNotFoundError(
@@ -132,6 +135,9 @@ def main(argv: list[str] | None = None) -> int:
help="directory containing the capture_npc_baseline.gd PNGs")
sheet.add_argument("--out", type=Path, default=None,
help="output JPEG (default: <dir>/sheet.jpg; never overwritten)")
sheet.add_argument("--views", default="",
help="comma-separated view tokens instead of the six baseline views "
"(pose harness: idle,walk_contact,walk_pass,sit)")
compare = sub.add_parser("compare")
compare.add_argument("--before", required=True, type=Path)
compare.add_argument("--after", required=True, type=Path)
@@ -143,8 +149,9 @@ def main(argv: list[str] | None = None) -> int:
if not output.is_absolute():
output = ROOT / output
output = next_free_path(output)
views = [v.strip() for v in args.views.split(",") if v.strip()] or None
try:
written = build_sheet(source, output)
written = build_sheet(source, output, views)
except FileNotFoundError as exc:
print(exc)
return 2
+13
View File
@@ -14,6 +14,19 @@ class NpcBaselineTests(unittest.TestCase):
self.assertEqual({row[1] for row in rows}, set(npc_baseline.VIEWS))
self.assertEqual(len({row[2] for row in rows}), 48)
def test_sheet_accepts_a_custom_view_list(self):
from PIL import Image
with tempfile.TemporaryDirectory() as tmp:
folder = Path(tmp)
for variant in npc_baseline.VARIANTS:
for view in ("idle", "sit"):
Image.new("RGB", (32, 18), (40, 60, 80)).save(
folder / f"{variant}__{view}.png")
out = npc_baseline.build_sheet(folder, folder / "sheet.jpg", ["idle", "sit"])
with Image.open(out) as sheet:
# 2 views x 320 px cells.
self.assertEqual(sheet.width, 640)
def test_latest_capture_prefers_the_full_stem(self):
with tempfile.TemporaryDirectory() as tmp:
folder = Path(tmp)