- CLAUDE.md with architecture overview, conventions, and strict commit rules - docs/architecture-registries.md: registry pattern details - docs/color-spaces.md: color space system guide - docs/testing.md: BDD testing guide with mamba - docs/nodes-comfyui.md: ComfyUI nodes reference - docs/data-models.md: PixelColor and PixelPalette models
269 lines
7.1 KiB
Markdown
269 lines
7.1 KiB
Markdown
# Modeles de données
|
|
|
|
## PixelColor
|
|
|
|
**Fichier** : `lib/pixel_color.py`
|
|
|
|
Dataclass représentant une couleur individuelle avec ses métadonnées.
|
|
|
|
### Attributs
|
|
|
|
| Attribut | Type | Défaut | Description |
|
|
|----------|------|--------|-------------|
|
|
| r | int | requis | Canal rouge (0-255, clampé dans `__post_init__`) |
|
|
| g | int | requis | Canal vert (0-255) |
|
|
| b | int | requis | Canal bleu (0-255) |
|
|
| name | str | "" | Nom optionnel de la couleur |
|
|
| color_space | str | "rgb" | Espace colorimétrique actif pour le mélange |
|
|
|
|
### Propriétés
|
|
|
|
```python
|
|
color = PixelColor(255, 128, 0, "Orange")
|
|
|
|
color.hex # "#ff8000"
|
|
color.rgb_tuple # (255, 128, 0)
|
|
color.rgb_normalized # (1.0, 0.502, 0.0)
|
|
color.exporter # Instance RGBExporter (lazy-loaded)
|
|
```
|
|
|
|
### Méthodes
|
|
|
|
#### mix_with(color, ratio=0.5) -> PixelColor
|
|
|
|
Mélange **en place** avec une autre couleur. Utilise le mixer de l'espace colorimétrique actuel.
|
|
|
|
```python
|
|
red = PixelColor(255, 0, 0)
|
|
blue = PixelColor(0, 0, 255)
|
|
red.mix_with(blue, 0.5) # red est maintenant (127, 0, 127)
|
|
```
|
|
|
|
**Attention** : cette méthode **modifie** l'instance. Utiliser `create_copy()` avant si on veut préserver l'original.
|
|
|
|
#### create_copy() -> PixelColor
|
|
|
|
Copie profonde de la couleur (tous les attributs).
|
|
|
|
```python
|
|
original = PixelColor(255, 0, 0, "Rouge", "hsv")
|
|
copy = original.create_copy()
|
|
# copy.r == 255, copy.name == "Rouge", copy.color_space == "hsv"
|
|
```
|
|
|
|
#### distance_to(other) -> float
|
|
|
|
Distance euclidienne en espace RGB. Utilisée pour trouver la couleur la plus proche.
|
|
|
|
```python
|
|
red = PixelColor(255, 0, 0)
|
|
orange = PixelColor(255, 128, 0)
|
|
red.distance_to(orange) # ~128.0
|
|
```
|
|
|
|
#### using_color_space(new_space) -> ColorSpaceContext
|
|
|
|
Context manager pour changer temporairement l'espace colorimétrique.
|
|
|
|
```python
|
|
color = PixelColor(255, 0, 0) # color_space = "rgb"
|
|
|
|
with color.using_color_space("hsv"):
|
|
color.mix_with(other, 0.5) # mélange en HSV
|
|
# color_space restauré a "rgb"
|
|
```
|
|
|
|
#### to_hex() -> str
|
|
|
|
Format hexadécimal majuscule : `"#FF8040"`
|
|
|
|
#### Exporter (via propriété `exporter`)
|
|
|
|
```python
|
|
color.exporter.to_hex() # "#FF8000"
|
|
color.exporter.to_hex_alpha(0.5) # "#FF800080"
|
|
color.exporter.to_rgb_string() # "rgb(255, 128, 0)"
|
|
color.exporter.to_rgba_string(0.5) # "rgba(255, 128, 0, 0.5)"
|
|
color.exporter.to_rgb_tuple() # "(255, 128, 0)"
|
|
color.exporter.to_rgba_tuple(128) # "(255, 128, 0, 128)"
|
|
color.exporter.to_hsl_string() # "hsl(30, 100%, 50%)"
|
|
color.exporter.to_css_var("primary") # "--primary: #FF8000;"
|
|
color.exporter.to_gimp_palette_line(0) # "255 128 0\tOrange"
|
|
```
|
|
|
|
### Validation
|
|
|
|
`__post_init__` clampe les valeurs RGB entre 0 et 255 :
|
|
|
|
```python
|
|
color = PixelColor(300, -10, 128)
|
|
# color.r == 255, color.g == 0, color.b == 128
|
|
```
|
|
|
|
---
|
|
|
|
## PixelPalette
|
|
|
|
**Fichier** : `lib/pixel_palette.py`
|
|
|
|
Classe pour gérer une collection de couleurs avec métadonnées et opérations de manipulation.
|
|
|
|
### Attributs
|
|
|
|
| Attribut | Type | Défaut | Description |
|
|
|----------|------|--------|-------------|
|
|
| name | str | "Untitled Palette" | Nom de la palette |
|
|
| colors | List[PixelColor] | [] | Liste des couleurs |
|
|
| raw_content | str | "" | Contenu brut (pour parsing automatique) |
|
|
| source_filename | Optional[str] | None | Fichier source |
|
|
| metadata | Dict[str, Any] | {} | Métadonnées libres |
|
|
| format_type | str | "unknown" | Format détecté après parsing |
|
|
|
|
**Note** : le `__init__` actuel est simplifié et initialise seulement `colors = []`. Le `@dataclass` et `__init__` coexistent (le `__init__` explicite a priorité).
|
|
|
|
### Protocole séquence
|
|
|
|
PixelPalette implémente le protocole séquence Python :
|
|
|
|
```python
|
|
palette = PixelPalette()
|
|
palette.add_color(255, 0, 0, "Rouge")
|
|
palette.add_color(0, 255, 0, "Vert")
|
|
|
|
len(palette) # 2
|
|
palette[0] # PixelColor(255, 0, 0, "Rouge")
|
|
for color in palette:
|
|
print(color) # Itération
|
|
```
|
|
|
|
### Propriétés
|
|
|
|
```python
|
|
palette.is_empty # True si aucune couleur
|
|
palette.color_count # Nombre de couleurs
|
|
palette.is_valid # Toujours True (une palette vide est valide)
|
|
```
|
|
|
|
### Méthodes de manipulation
|
|
|
|
#### add_color(r, g, b, name="")
|
|
|
|
```python
|
|
palette.add_color(255, 0, 0, "Rouge")
|
|
```
|
|
|
|
#### remove_color(index) -> bool
|
|
|
|
Supprime par index. Retourne `True` si suppression réussie.
|
|
|
|
```python
|
|
palette.remove_color(0) # True
|
|
palette.remove_color(99) # False (hors limites)
|
|
```
|
|
|
|
#### find_closest_color(target) -> (PixelColor, int)
|
|
|
|
Recherche linéaire par distance euclidienne RGB.
|
|
|
|
```python
|
|
target = PixelColor(250, 10, 10)
|
|
closest, index = palette.find_closest_color(target)
|
|
```
|
|
|
|
#### get_unique_colors() -> List[PixelColor]
|
|
|
|
Déduplique les couleurs par tuple RGB.
|
|
|
|
#### sort_by_hue()
|
|
|
|
Tri en place par teinte HSV (utilise `colorsys`).
|
|
|
|
#### sort_by_brightness()
|
|
|
|
Tri en place par luminosité perceptuelle : `0.299*R + 0.587*G + 0.114*B`
|
|
|
|
#### create_gradient(index1, index2, color_space="rgb")
|
|
|
|
Interpole les couleurs **entre** deux index existants.
|
|
|
|
```python
|
|
palette = PixelPalette()
|
|
palette.add_color(255, 0, 0, "Rouge") # 0
|
|
palette.add_color(0, 0, 0, "Placeholder") # 1
|
|
palette.add_color(0, 0, 0, "Placeholder") # 2
|
|
palette.add_color(0, 0, 255, "Bleu") # 3
|
|
|
|
palette.create_gradient(0, 3, "rgb")
|
|
# palette[1] et palette[2] sont maintenant des couleurs interpolées
|
|
```
|
|
|
|
**Contraintes** :
|
|
- `index1 < index2`
|
|
- Au moins une couleur entre les deux index
|
|
- Leve `ValueError` ou `IndexError` si invalide
|
|
|
|
### Factory methods
|
|
|
|
#### create_monochrome(color, count, name=None) -> PixelPalette
|
|
|
|
Crée une palette avec `count` copies d'une même couleur.
|
|
|
|
```python
|
|
red = PixelColor(255, 0, 0, "Rouge")
|
|
mono = PixelPalette.create_monochrome(red, 5)
|
|
# 5 copies indépendantes de Rouge
|
|
```
|
|
|
|
### Méthodes d'export
|
|
|
|
#### to_formatted_string(format_type, separator, include_header, include_names) -> str
|
|
|
|
Export complet avec en-tête optionnel. Utilise `PaletteExporter` en interne.
|
|
|
|
```python
|
|
palette.to_formatted_string("hex", separator="\n", include_header=True)
|
|
# "# Palette: Mon palette\n# Couleurs: 3\n...\n#FF0000\n#00FF00\n#0000FF"
|
|
```
|
|
|
|
#### to_list(format_type, include_names) -> List[str]
|
|
|
|
Liste de strings formatées.
|
|
|
|
```python
|
|
palette.to_list("hex") # ["#FF0000", "#00FF00", "#0000FF"]
|
|
```
|
|
|
|
#### to_rgb_tuples() -> List[Tuple[int, int, int]]
|
|
|
|
```python
|
|
palette.to_rgb_tuples() # [(255, 0, 0), (0, 255, 0), (0, 0, 255)]
|
|
```
|
|
|
|
### Parsing automatique
|
|
|
|
Si `raw_content` est fourni, `__post_init__` lance le parsing via `ParserRegistry` :
|
|
|
|
```python
|
|
content = "GIMP Palette\nName: Test\n#\n255 0 0\tRouge"
|
|
palette = PixelPalette(raw_content=content)
|
|
# palette.colors contient les couleurs parsées
|
|
# palette.format_type == "gimp"
|
|
```
|
|
|
|
Les parsers sont essayés dans l'ordre d'enregistrement. Le premier qui retourne `can_parse() == True` est utilisé.
|
|
|
|
---
|
|
|
|
## Relations entre modeles
|
|
|
|
```
|
|
PixelPalette
|
|
└── colors: List[PixelColor]
|
|
├── .exporter -> RGBExporter (export individuel)
|
|
└── .mix_with() -> ColorSpaceRegistry -> Mixer
|
|
|
|
PixelPalette
|
|
├── .to_formatted_string() -> PaletteExporter -> ExporterRegistry -> Exporter
|
|
└── ._parse_content() -> ParserRegistry -> Parser -> List[PixelColor]
|
|
```
|