73 lines
2.7 KiB
Python
73 lines
2.7 KiB
Python
"""Assemblage visuel des planches d'autocollants des sets filtrés."""
|
|
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
from lib.filesystem import ensure_parent_dir
|
|
from lib.rebrickable.stats import read_rows
|
|
|
|
|
|
def load_sticker_parts(path: Path) -> List[dict]:
|
|
"""Charge la liste des autocollants par set."""
|
|
return read_rows(path)
|
|
|
|
|
|
def plot_sticker_sheets(
|
|
stickers_path: Path,
|
|
destination_path: Path,
|
|
resources_dir: Path = Path("figures/rebrickable"),
|
|
columns: int = 6,
|
|
) -> None:
|
|
"""Assemble les images d'autocollants exclusifs en grille triée par année."""
|
|
rows = load_sticker_parts(stickers_path)
|
|
rows.sort(key=lambda r: (int(r["year"]), r["set_num"], r["part_num"]))
|
|
selected: List[dict] = []
|
|
images: List[Image.Image] = []
|
|
for row in rows:
|
|
image_path = resources_dir / row["set_id"] / "stickers" / f"{row['part_num']}.jpg"
|
|
if not image_path.exists():
|
|
continue
|
|
img = Image.open(image_path).convert("RGBA")
|
|
max_side = 260
|
|
ratio = min(max_side / img.width, max_side / img.height, 1.0)
|
|
if ratio < 1.0:
|
|
img = img.resize((int(img.width * ratio), int(img.height * ratio)))
|
|
images.append(img)
|
|
selected.append(row)
|
|
if not images:
|
|
return
|
|
|
|
font = ImageFont.load_default()
|
|
def measure(text: str) -> tuple[int, int]:
|
|
bbox = ImageDraw.Draw(Image.new("RGB", (10, 10))).textbbox((0, 0), text, font=font)
|
|
return bbox[2] - bbox[0], bbox[3] - bbox[1]
|
|
|
|
labels = [f"{row['year']} • {row['set_id']} • {row['part_num']}" for row in selected]
|
|
text_height = max(measure(label)[1] for label in labels)
|
|
max_width = max(img.width for img in images)
|
|
max_height = max(img.height for img in images)
|
|
|
|
columns = max(1, columns)
|
|
rows_count = (len(images) + columns - 1) // columns
|
|
cell_width = max(max_width + 40, 240)
|
|
cell_height = max_height + text_height + 20
|
|
width = columns * cell_width
|
|
height = rows_count * cell_height
|
|
canvas = Image.new("RGBA", (width, height), (255, 255, 255, 255))
|
|
draw = ImageDraw.Draw(canvas)
|
|
for index, (img, label) in enumerate(zip(images, labels)):
|
|
col = index % columns
|
|
row_idx = index // columns
|
|
x = col * cell_width + (cell_width - img.width) // 2
|
|
y = row_idx * cell_height + 6
|
|
canvas.paste(img, (x, y), img)
|
|
text_width, _ = measure(label)
|
|
text_x = col * cell_width + (cell_width - text_width) // 2
|
|
text_y = y + img.height + 6
|
|
draw.text((text_x, text_y), label, fill="#111111", font=font)
|
|
|
|
ensure_parent_dir(destination_path)
|
|
canvas.convert("RGB").save(destination_path, "PNG")
|