from __future__ import annotations import json from backend.node_registry import register_node from backend.data_types import COLORMAPS, DEFAULT_CUSTOM_COLORMAP_STOPS, normalize_colormap_spec @register_node(display_name="Color Map") class ColorMap: @classmethod def INPUT_TYPES(cls): return { "required": { "mode": (["preset", "custom"], {"default": "preset"}), "preset": (list(COLORMAPS), { "default": "viridis", "show_when_widget_value": {"mode": ["preset"]}, }), "stops": ("STRING", { "default": json.dumps(list(DEFAULT_CUSTOM_COLORMAP_STOPS)), "colormap_stops": True, "show_when_widget_value": {"mode": ["custom"]}, }), } } RETURN_TYPES = ("COLORMAP",) RETURN_NAMES = ("colormap",) FUNCTION = "build" DESCRIPTION = ( "Build a reusable colormap. Choose a preset, or create a custom gradient with min/max colours " "and any number of intermediate stops." ) def build(self, mode: str, preset: str, stops: str | None = None, stops_json: str | None = None) -> tuple: if mode == "preset": return ({"mode": "preset", "preset": normalize_colormap_spec(preset)},) try: raw_stops = stops if stops is not None else stops_json stops_data = json.loads(raw_stops or "[]") except json.JSONDecodeError as exc: raise ValueError("Custom colormap stops must be valid JSON.") from exc spec = normalize_colormap_spec({"mode": "custom", "stops": stops_data}, fallback=None) if not (isinstance(spec, dict) and spec.get("mode") == "custom"): raise ValueError("Custom colormap must include at least min and max colours.") return (spec,)