You've already forked etude_lego_jurassic_world
Filtre les pièces techniques et documente l’étape 28
This commit is contained in:
@@ -9,7 +9,7 @@ from matplotlib.lines import Line2D
|
||||
|
||||
from lib.filesystem import ensure_parent_dir
|
||||
from lib.color_sort import lab_sort_key, sort_hex_colors_lab
|
||||
from lib.rebrickable.color_ignores import is_ignored_color_rgb
|
||||
from lib.rebrickable.color_ignores import is_ignored_color_rgb, is_ignored_part_category
|
||||
from lib.rebrickable.parts_inventory import normalize_boolean
|
||||
from lib.rebrickable.stats import read_rows
|
||||
|
||||
@@ -21,18 +21,38 @@ def sort_colors_perceptually(colors: Iterable[dict]) -> List[dict]:
|
||||
return sorted(colors, key=lambda color: index_map[color["color_rgb"]])
|
||||
|
||||
|
||||
def load_used_colors(parts_path: Path, colors_path: Path, minifig_only: bool = False) -> List[dict]:
|
||||
def load_part_categories(parts_catalog_path: Path) -> Dict[str, str]:
|
||||
"""Indexe les catégories par part_num."""
|
||||
categories: Dict[str, str] = {}
|
||||
with parts_catalog_path.open() as catalog_file:
|
||||
import csv
|
||||
|
||||
reader = csv.DictReader(catalog_file)
|
||||
for row in reader:
|
||||
categories[row["part_num"]] = row["part_cat_id"]
|
||||
return categories
|
||||
|
||||
|
||||
def load_used_colors(
|
||||
parts_path: Path,
|
||||
colors_path: Path,
|
||||
parts_catalog_path: Path,
|
||||
minifig_only: bool = False,
|
||||
) -> List[dict]:
|
||||
"""Charge les couleurs utilisées (hors rechanges) et leurs quantités totales.
|
||||
|
||||
Si minifig_only est vrai, ne conserve que les pièces marquées is_minifig_part=true.
|
||||
Sinon, exclut les pièces de minifig.
|
||||
"""
|
||||
rows = read_rows(parts_path)
|
||||
categories = load_part_categories(parts_catalog_path)
|
||||
colors_lookup = {(row["rgb"], normalize_boolean(row["is_trans"])): row["name"] for row in read_rows(colors_path)}
|
||||
totals: Dict[Tuple[str, str], int] = {}
|
||||
for row in rows:
|
||||
if is_ignored_color_rgb(row["color_rgb"]):
|
||||
continue
|
||||
if is_ignored_part_category(categories[row["part_num"]]):
|
||||
continue
|
||||
if minifig_only and row.get("is_minifig_part") != "true":
|
||||
continue
|
||||
if not minifig_only and row.get("is_minifig_part") == "true":
|
||||
@@ -85,11 +105,12 @@ def build_background(width: float, height: float, resolution: int = 600) -> np.n
|
||||
def plot_colors_grid(
|
||||
parts_path: Path,
|
||||
colors_path: Path,
|
||||
parts_catalog_path: Path,
|
||||
destination_path: Path,
|
||||
minifig_only: bool = False,
|
||||
) -> None:
|
||||
"""Dessine une grille artistique des couleurs utilisées."""
|
||||
colors = load_used_colors(parts_path, colors_path, minifig_only=minifig_only)
|
||||
colors = load_used_colors(parts_path, colors_path, parts_catalog_path, minifig_only=minifig_only)
|
||||
positions = build_hex_positions(len(colors))
|
||||
x_values = [x for x, _ in positions]
|
||||
y_values = [y for _, y in positions]
|
||||
|
||||
@@ -1,8 +1,54 @@
|
||||
"""Couleurs à exclure des palettes."""
|
||||
"""Couleurs et catégories de pièces à exclure des palettes."""
|
||||
|
||||
import csv
|
||||
from pathlib import Path
|
||||
from typing import Set
|
||||
|
||||
|
||||
IGNORED_COLOR_RGB = {"0033b2", "05131d"}
|
||||
IGNORED_PART_CATEGORY_IDS = {
|
||||
"1", # Baseplates
|
||||
"8", # Technic Bricks
|
||||
"12", # Technic Connectors
|
||||
"17", # Gear Parts
|
||||
"18", # Hinges, Arms and Turntables
|
||||
"22", # Pneumatics
|
||||
"23", # Panels
|
||||
"25", # Technic Steering, Suspension and Engine
|
||||
"26", # Technic Special
|
||||
"29", # Wheels and Tyres
|
||||
"30", # Tubes and Hoses
|
||||
"31", # String, Bands and Reels
|
||||
"34", # Supports, Girders and Cranes
|
||||
"40", # Technic Panels
|
||||
"43", # Znap
|
||||
"44", # Mechanical
|
||||
"45", # Electronics
|
||||
"46", # Technic Axles
|
||||
"51", # Technic Beams
|
||||
"52", # Technic Gears
|
||||
"53", # Technic Pins
|
||||
"54", # Technic Bushes
|
||||
"55", # Technic Beams Special
|
||||
}
|
||||
|
||||
|
||||
def is_ignored_color_rgb(rgb: str) -> bool:
|
||||
"""Retourne vrai si le code couleur doit être ignoré."""
|
||||
return rgb.strip().lower() in IGNORED_COLOR_RGB
|
||||
|
||||
|
||||
def is_ignored_part_category(part_cat_id: str) -> bool:
|
||||
"""Retourne vrai si la catégorie de pièce est exclue."""
|
||||
return part_cat_id.strip() in IGNORED_PART_CATEGORY_IDS
|
||||
|
||||
|
||||
def load_ignored_part_numbers(parts_catalog_path: Path) -> Set[str]:
|
||||
"""Charge les références de pièces dont la catégorie est exclue."""
|
||||
ignored: Set[str] = set()
|
||||
with parts_catalog_path.open() as parts_file:
|
||||
reader = csv.DictReader(parts_file)
|
||||
for row in reader:
|
||||
if is_ignored_part_category(row["part_cat_id"]):
|
||||
ignored.add(row["part_num"])
|
||||
return ignored
|
||||
|
||||
@@ -25,10 +25,17 @@ def build_colors_lookup(colors_path: Path) -> Dict[Tuple[str, str], str]:
|
||||
return colors
|
||||
|
||||
|
||||
def aggregate_colors_by_set(parts: Iterable[dict], colors_lookup: Dict[Tuple[str, str], str]) -> List[dict]:
|
||||
def aggregate_colors_by_set(
|
||||
parts: Iterable[dict],
|
||||
colors_lookup: Dict[Tuple[str, str], str],
|
||||
ignored_parts: set[str] | None = None,
|
||||
) -> List[dict]:
|
||||
"""Agrège les quantités par set et par couleur."""
|
||||
ignored = ignored_parts or set()
|
||||
totals: Dict[Tuple[str, str, str, str, str], dict] = {}
|
||||
for row in parts:
|
||||
if row["part_num"] in ignored:
|
||||
continue
|
||||
if is_ignored_color_rgb(row["color_rgb"]):
|
||||
continue
|
||||
key = (row["set_num"], row["set_id"], row["year"], row["color_rgb"], row["is_translucent"])
|
||||
|
||||
Reference in New Issue
Block a user