82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
import numpy as np
|
|
import cv2
|
|
from typing import Tuple, List
|
|
|
|
# Image and Grid constants
|
|
IMG_SIZE = 128
|
|
GRID_SIZE = 16
|
|
SPOT_RADIUS = 6 # Approximate radius in pixels
|
|
|
|
# Colors (BGR)
|
|
FACE_COLOR = (180, 220, 240) # Pale cream/tan
|
|
SPOT_COLOR = (50, 50, 200) # Reddish-orange
|
|
EYE_COLOR = (0, 0, 0)
|
|
|
|
def extract_coords(pid: int, mode: str = "standard") -> List[Tuple[int, int]]:
|
|
"""Extracts four (x, y) coordinates from a 32-bit PID/EC.
|
|
|
|
Standard (Little-Endian): Byte 0 (LL), 1 (LR), 2 (UL), 3 (UR)
|
|
BDSP (Big-Endian): Byte 3 (LL), 2 (LR), 1 (UL), 0 (UR)
|
|
"""
|
|
bytes_list = [
|
|
(pid >> 0) & 0xFF, # Byte 0
|
|
(pid >> 8) & 0xFF, # Byte 1
|
|
(pid >> 16) & 0xFF, # Byte 2
|
|
(pid >> 24) & 0xFF, # Byte 3
|
|
]
|
|
|
|
if mode == "bdsp":
|
|
# BDSP reads the bytes in reverse order
|
|
ordered_bytes = [bytes_list[3], bytes_list[2], bytes_list[1], bytes_list[0]]
|
|
else:
|
|
ordered_bytes = bytes_list
|
|
|
|
coords = []
|
|
for byte in ordered_bytes:
|
|
x = byte & 0x0F
|
|
y = (byte >> 4) & 0x0F
|
|
coords.append((x, y))
|
|
return coords
|
|
|
|
def generate_spinda_face(pid: int, mode: str = "standard") -> np.ndarray:
|
|
"""Generates a procedural Spinda face with spots based on the PID."""
|
|
# Create blank canvas (white)
|
|
img = np.ones((IMG_SIZE, IMG_SIZE, 3), dtype=np.uint8) * 255
|
|
|
|
# Draw Ears
|
|
cv2.circle(img, (IMG_SIZE // 2 - 40, IMG_SIZE // 2 - 50), 20, FACE_COLOR, -1)
|
|
cv2.circle(img, (IMG_SIZE // 2 - 40, IMG_SIZE // 2 - 50), 20, (0, 0, 0), 2)
|
|
cv2.circle(img, (IMG_SIZE // 2 + 40, IMG_SIZE // 2 - 50), 20, FACE_COLOR, -1)
|
|
cv2.circle(img, (IMG_SIZE // 2 + 40, IMG_SIZE // 2 - 50), 20, (0, 0, 0), 2)
|
|
|
|
# Draw main face (oval)
|
|
center = (IMG_SIZE // 2, IMG_SIZE // 2)
|
|
axes = (50, 60)
|
|
cv2.ellipse(img, center, axes, 0, 0, 360, FACE_COLOR, -1)
|
|
cv2.ellipse(img, center, axes, 0, 0, 360, (0, 0, 0), 2) # Outline
|
|
|
|
# Fixed Eyes
|
|
cv2.circle(img, (center[0] - 15, center[1] - 10), 4, EYE_COLOR, -1)
|
|
cv2.circle(img, (center[0] + 15, center[1] - 10), 4, EYE_COLOR, -1)
|
|
|
|
# Define Spot Zones (Relative to the 16x16 grid)
|
|
# Heuristic mapping for a more "Pokemon-like" layout
|
|
# Spot 1 (LL), Spot 2 (LR), Spot 3 (UL), Spot 4 (UR)
|
|
# These offsets are designed to place spots in their respective quadrants
|
|
quadrant_offsets = [
|
|
(center[0] - 45, center[1] + 5), # LL (Face)
|
|
(center[0] + 5, center[1] + 5), # LR (Face)
|
|
(center[0] - 45, center[1] - 55), # UL (Ear/Upper Face)
|
|
(center[0] + 5, center[1] - 55), # UR (Ear/Upper Face)
|
|
]
|
|
|
|
coords = extract_coords(pid, mode=mode)
|
|
for i, (x, y) in enumerate(coords):
|
|
offset_x, offset_y = quadrant_offsets[i]
|
|
px = int(offset_x + x * 2.5) # Scale grid to quadrant
|
|
py = int(offset_y + y * 2.5)
|
|
|
|
cv2.circle(img, (px, py), SPOT_RADIUS, SPOT_COLOR, -1)
|
|
|
|
return img
|