Module Spectrum_palette_ppx.Palette

Palette 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 ... end

Signature for palette modules generated by the %palette PPX extension.

exception InvalidColorName of string

Exception raised when an invalid color name is provided to of_string.

type nearest_index

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_index

Build 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"
  • parameter leaf_size

    Optional octree leaf size (default determined by oktree)

  • parameter color_list

    List of RGBA colors to index

val nearest_with_index : nearest_index -> Gg.v4 -> Gg.v4

Query 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_colors
  • parameter index

    Prebuilt nearest-neighbor index

  • parameter target

    Target color to match

val nearest_of_list : ?leaf_size:int -> Gg.v4 list -> Gg.v4 -> Gg.v4

Create 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)
  • parameter leaf_size

    Optional octree leaf size

  • parameter color_list

    List of colors to create palette from

  • returns

    Function that finds nearest color in the palette

val lab3_of_color : Gg.v4 -> Gg.V3.t

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_dist

Alpha channel is ignored.