latkit 0.2.1

Columnar data drawn as vertices, edges, lines, and areas on the GPU.
Documentation
//! Bakes `colormaps/catalog.txt` into `OUT_DIR/colormaps.rs`: per map its name, label, kind, color
//! count, and 256 sRGB texels, made by the crate's own `ramp`.

use crate::ramp::{Spread, texels};
use std::env;
use std::fmt::Write as _;
use std::fs;
use std::path::{Path, PathBuf};

pub(crate) fn generate() {
    let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let catalog = fs::read_to_string(manifest.join("colormaps/catalog.txt")).unwrap();
    let mut entries = String::new();
    let mut consts = String::new();
    let mut names = Vec::new();
    for (index, line) in catalog.lines().filter(|l| !l.trim().is_empty()).enumerate() {
        let (head, hex) = line.split_once('|').expect("name kind label|colors");
        let mut words = head.splitn(3, ' ');
        let (name, kind, label) = (words.next().unwrap(), words.next().unwrap(), words.next().unwrap());
        let colors: Vec<[u8; 3]> = hex
            .as_bytes()
            .as_chunks::<6>()
            .0
            .iter()
            .map(|digits| {
                let byte =
                    |at: usize| u8::from_str_radix(std::str::from_utf8(&digits[at..at + 2]).unwrap(), 16).unwrap();
                [byte(0), byte(2), byte(4)]
            })
            .collect();
        let (kind, spread) = match kind {
            "sequential" | "multihue" => ("Sequential", Spread::Continuous),
            "diverging" => ("Diverging", Spread::Continuous),
            "cyclic" => ("Cyclic", Spread::Cyclic),
            "categorical" => ("Categorical", Spread::Categorical),
            other => panic!("unknown colormap kind {other}"),
        };
        let stops: Vec<_> =
            colors.iter().map(|c| (None, [c[0], c[1], c[2], 255].map(|b| f32::from(b) / 255.0))).collect();
        let texels: Vec<String> = texels(spread, &stops).iter().map(|t| format!("{t:?}")).collect();
        writeln!(
            entries,
            "    Entry {{ name: {name:?}, label: {label:?}, kind: ColormapKind::{kind}, count: {}, texels: [{}] }},",
            colors.len(),
            texels.join(", ")
        )
        .unwrap();
        let doc = format!("/// {label}, a {} colormap.", kind.to_lowercase());
        writeln!(
            consts,
            "    {doc}\n    pub const {}: Colormap = Colormap(Map::Catalog({index}));",
            name.to_uppercase()
        )
        .unwrap();
        names.push(format!("{name:?}"));
    }
    let count = names.len();
    let rust = format!(
        "// Generated by build/colormaps.rs from colormaps/catalog.txt. Do not edit.\n\n\
         static CATALOG: [Entry; {count}] = [\n{entries}];\n\n\
         #[allow(clippy::doc_markdown, reason = \"the docs name colormaps by their labels\")]\n\
         impl Colormap {{\n{consts}\n    /// The names of every colormap in the catalog.\n    pub const NAMES: [&'static str; {count}] = [{}];\n}}\n",
        names.join(", ")
    );
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
    fs::write(out.join("colormaps.rs"), rust).unwrap();
    println!("cargo::rerun-if-changed={}", Path::new("colormaps").display());
}