Spectrum_palette_ppx.PalettePalette module type and runtime utilities.
Palette module type and runtime utilities for color palettes.
Provides the signature that all generated palette modules must implement, plus helper functions for building custom palettes with efficient nearest-color lookup.
module type M = sig ... endSignature for palette modules generated by the %palette PPX extension.
Exception raised when an invalid color name is provided to of_string.
Opaque type for nearest-neighbor search index.
Contains an octree in LAB color space and a reverse lookup map from LAB coordinates to original RGBA colors.
val nearest_index_of_color_list : ?leaf_size:int -> Gg.v4 list -> nearest_indexBuild a nearest-neighbor index from a list of colors.
The index uses LAB color space for perceptual distance calculations. This is used internally by generated palette modules.
(* Build index for custom palette *)
let my_colors = [
Gg.Color.v_srgb 1.0 0.0 0.0; (* red *)
Gg.Color.v_srgb 0.0 1.0 0.0; (* green *)
Gg.Color.v_srgb 0.0 0.0 1.0; (* blue *)
] in
let index = Spectrum_palette_ppx.Palette.nearest_index_of_color_list my_colors in
Printf.printf "Built index for %d colors\n" (List.length my_colors) (* Use custom leaf size for larger palettes *)
let colors = Spectrum_palettes.Terminal.Xterm256.color_list in
let index = Spectrum_palette_ppx.Palette.nearest_index_of_color_list
~leaf_size:16 colors in
Printf.printf "Built optimized index\n"val nearest_with_index : nearest_index -> Gg.v4 -> Gg.v4Query the nearest color using a prebuilt index.
Use this when you need to query the same palette multiple times. Building the index once and reusing it is more efficient than repeated linear searches.
(* Build index once *)
let colors = [
Gg.Color.v_srgb 1.0 0.0 0.0;
Gg.Color.v_srgb 0.0 1.0 0.0;
Gg.Color.v_srgb 0.0 0.0 1.0;
] in
let index = Spectrum_palette_ppx.Palette.nearest_index_of_color_list colors in
(* Use multiple times *)
let orange = Gg.Color.v_srgb 1.0 0.5 0.0 in
let nearest = Spectrum_palette_ppx.Palette.nearest_with_index index orange in
let rgba = Spectrum_tools.Convert.Color.to_rgba nearest in
Printf.printf "Orange maps to RGB(%d, %d, %d)\n" rgba.r rgba.g rgba.b (* Query many colors efficiently *)
let index = Spectrum_palette_ppx.Palette.nearest_index_of_color_list
Spectrum_palettes.Terminal.Basic.color_list in
let test_colors = [
Gg.Color.v_srgb 0.9 0.2 0.1;
Gg.Color.v_srgb 0.1 0.9 0.2;
Gg.Color.v_srgb 0.2 0.1 0.9;
] in
List.iter (fun color ->
let nearest = Spectrum_palette_ppx.Palette.nearest_with_index index color in
let rgba = Spectrum_tools.Convert.Color.to_rgba nearest in
Printf.printf "-> RGB(%d, %d, %d)\n" rgba.r rgba.g rgba.b
) test_colorsCreate a reusable nearest-color lookup function for a color list.
Convenience function that builds an index and returns a lookup function. The index is built once and captured in the returned function's closure.
(* Create lookup function *)
let my_palette = [
Gg.Color.v_srgb 1.0 0.0 0.0; (* red *)
Gg.Color.v_srgb 0.0 1.0 0.0; (* green *)
Gg.Color.v_srgb 0.0 0.0 1.0; (* blue *)
] in
let find_nearest = Spectrum_palette_ppx.Palette.nearest_of_list my_palette in
(* Use the function multiple times *)
let orange = Gg.Color.v_srgb 1.0 0.5 0.0 in
let nearest = find_nearest orange in
Printf.printf "Found nearest color\n" (* Quantize image colors *)
let quantize_to_palette colors target_palette =
let find_nearest = Spectrum_palette_ppx.Palette.nearest_of_list target_palette in
List.map find_nearest colors
in
let image_colors = [
Gg.Color.v_srgb 0.95 0.15 0.10;
Gg.Color.v_srgb 0.85 0.75 0.15;
Gg.Color.v_srgb 0.10 0.85 0.90;
] in
let quantized = quantize_to_palette
image_colors
Spectrum_palettes.Terminal.Basic.color_list in
Printf.printf "Quantized %d colors\n" (List.length quantized)Convert an RGBA color to LAB 3D coordinates (for testing/debugging).
The LAB color space is perceptually uniform, meaning numerical distance corresponds to perceived color difference. Alpha channel is ignored.
(* Convert red to LAB *)
let red = Gg.Color.v_srgb 1.0 0.0 0.0 in
let lab = Spectrum_palette_ppx.Palette.lab3_of_color red in
let l = Gg.V3.x lab in
let a = Gg.V3.y lab in
let b = Gg.V3.z lab in
Printf.printf "Red in LAB: L=%.2f, a=%.2f, b=%.2f\n" l a b (* Calculate perceptual distance between two colors *)
let color1 = Gg.Color.v_srgb 0.8 0.2 0.2 in
let color2 = Gg.Color.v_srgb 0.9 0.3 0.1 in
let lab1 = Spectrum_palette_ppx.Palette.lab3_of_color color1 in
let lab2 = Spectrum_palette_ppx.Palette.lab3_of_color color2 in
let distance = Gg.V3.norm (Gg.V3.sub lab1 lab2) in
Printf.printf "Perceptual distance: %.2f\n" distance (* Compare RGB vs LAB distance *)
let c1 = Gg.Color.v_srgb 0.5 0.5 0.0 in
let c2 = Gg.Color.v_srgb 0.0 0.5 0.5 in
(* RGB distance (not perceptual) *)
let rgb_dist = sqrt ((0.5 ** 2.0) +. (0.5 ** 2.0)) in
(* LAB distance (perceptual) *)
let lab1 = Spectrum_palette_ppx.Palette.lab3_of_color c1 in
let lab2 = Spectrum_palette_ppx.Palette.lab3_of_color c2 in
let lab_dist = Gg.V3.norm (Gg.V3.sub lab1 lab2) in
Printf.printf "RGB distance: %.2f, LAB distance: %.2f\n"
rgb_dist lab_distAlpha channel is ignored.