This commit is contained in:
Lysec
2026-01-21 01:32:39 +01:00
2 changed files with 78 additions and 1 deletions
+41
View File
@@ -310,3 +310,44 @@ def lab_distance(lab1: LAB, lab2: LAB) -> float:
da = lab1[1] - lab2[1]
db = lab1[2] - lab2[2]
return math.sqrt(dL * dL + da * da + db * db)
def find_closest_color(
compare_to: str,
colors: list[dict[str, str]]
) -> str:
"""
Find the closest named color from a list (matugen-compatible).
Uses Lab color space Euclidean distance for perceptual color matching.
Args:
compare_to: Hex color to compare (e.g., "#ff5500")
colors: List of {"name": "...", "color": "#..."} dicts
Returns:
Name of the closest color, or empty string if no colors provided
"""
if not colors:
return ""
# Parse target color
target = Color.from_hex(compare_to)
target_lab = rgb_to_lab(target.r, target.g, target.b)
closest_name = ""
closest_dist = float('inf')
for entry in colors:
try:
entry_color = Color.from_hex(entry["color"])
entry_lab = rgb_to_lab(entry_color.r, entry_color.g, entry_color.b)
dist = lab_distance(target_lab, entry_lab)
if dist < closest_dist:
closest_dist = dist
closest_name = entry["name"]
except (KeyError, ValueError):
# Skip invalid entries
continue
return closest_name
+37 -1
View File
@@ -17,7 +17,7 @@ try:
except ImportError:
tomllib = None
from .color import Color
from .color import Color, find_closest_color
class TemplateRenderer:
@@ -285,6 +285,10 @@ class TemplateRenderer:
result = re.sub(pattern, replace, template_text)
# Process escape sequences (matugen-compatible)
# \\ in template becomes \ in output
result = result.replace('\\\\', '\\')
if self._error_count > 0:
print(f"Template rendering completed with {self._error_count} error(s)", file=sys.stderr)
@@ -318,6 +322,10 @@ class TemplateRenderer:
self._current_file = None
return success
def _substitute_closest_color(self, text: str, closest_color: str) -> str:
"""Substitute {{closest_color}} in text."""
return re.sub(r"\{\{\s*closest_color\s*\}\}", closest_color, text)
def process_config_file(self, config_path: Path):
"""Process Matugen TOML configuration file."""
if not tomllib:
@@ -339,10 +347,38 @@ class TemplateRenderer:
self.render_file(Path(input_path).expanduser(), Path(output_path).expanduser())
# Handle closest_color if configured (matugen-compatible)
closest_color_value = ""
colors_to_compare = template.get("colors_to_compare")
compare_to = template.get("compare_to")
if colors_to_compare and compare_to:
# Render compare_to to get the actual hex color
rendered_compare_to = self.render(compare_to)
# Find the closest color name
closest_color_value = find_closest_color(rendered_compare_to, colors_to_compare)
# Execute pre_hook if specified
pre_hook = template.get("pre_hook")
if pre_hook:
import subprocess
# Substitute closest_color first, then render color variables
if closest_color_value:
pre_hook = self._substitute_closest_color(pre_hook, closest_color_value)
pre_hook = self.render(pre_hook)
try:
subprocess.run(pre_hook, shell=True, check=False)
except Exception as e:
print(f"Error running pre_hook for {name}: {e}", file=sys.stderr)
# Execute post_hook if specified
post_hook = template.get("post_hook")
if post_hook:
import subprocess
# Substitute closest_color first, then render color variables
if closest_color_value:
post_hook = self._substitute_closest_color(post_hook, closest_color_value)
post_hook = self.render(post_hook)
try:
subprocess.run(post_hook, shell=True, check=False)
except Exception as e: