Spaces:
Running
Running
File size: 2,373 Bytes
414dc55 | 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 | """Visual provider abstraction.
``CompositeProvider`` renders procedural pixel art and caches PNGs to disk so each
portrait/scene/prop is drawn once - instant, offline, deterministic, no model.
"""
from __future__ import annotations
from pathlib import Path
from typing import Protocol
from ..config import Settings
from ..constants import SPRITES_DIR
from ..schemas.visual import VisualDescriptor
from . import compositor
from .cache import cached_png
class VisualProvider(Protocol):
is_local: bool
def portrait_path(self, descriptor: VisualDescriptor | None, key: str) -> Path: ...
def portrait_sheet_path(self, descriptor: VisualDescriptor | None, key: str) -> Path: ...
def scene_path(self, name: str, key: str, accent: str | None = None) -> Path: ...
def prop_path(self, name: str, key: str, accent: str | None = None) -> Path: ...
class CompositeProvider:
is_local = True
def __init__(self, cache_dir: Path | None = None) -> None:
self._cache = cache_dir or (SPRITES_DIR / "cache")
def portrait_path(self, descriptor: VisualDescriptor | None, key: str) -> Path:
accent = descriptor.accent_color if descriptor else ""
mood = descriptor.mood if descriptor else ""
return cached_png(
self._cache, ("portrait", key, mood or "", accent or ""),
lambda: compositor.render_portrait(descriptor, key),
)
def portrait_sheet_path(self, descriptor: VisualDescriptor | None, key: str) -> Path:
accent = descriptor.accent_color if descriptor else ""
mood = descriptor.mood if descriptor else ""
return cached_png(
self._cache, ("sheet", key, mood or "", accent or ""),
lambda: compositor.render_portrait_sheet(descriptor, key),
)
def scene_path(self, name: str, key: str, accent: str | None = None) -> Path:
return cached_png(
self._cache, ("scene", key, name, accent or ""),
lambda: compositor.render_scene(name, key, accent),
)
def prop_path(self, name: str, key: str, accent: str | None = None) -> Path:
return cached_png(
self._cache, ("prop", key, name, accent or ""),
lambda: compositor.render_prop(name, key, accent),
)
def make_visual_provider(settings: Settings) -> VisualProvider:
return CompositeProvider()
|