wiki/notebook/2020-09-19-18-43-07.org
Gabriel Arazas b088086b06 Merge evergreen notes into the notebook
Now, it's all under the notebook umbrella. Seems to be appropriate as it
is just my notes after all.

I also updated some notes from there. I didn't keep track of what it is
this time. Something about more learning notes extracted from my
"Learning how to learn" course notes and then some. Lack of time and
hurriness just makes it difficult to track but it should be under
version control already.
2021-07-21 16:28:07 +08:00

4.3 KiB

Generating human-friendly color schemes

Though we already have Human-friendly colorspaces, it doesn't mean that we can simply generate human-friendly color schemes. Certain colors can be perceived as too bright or too dark and having them grouped in a color palette will look out-of-place and/or horrible.

Color spaces such as HSLuv and CIELUV respects perceptual uniformity which approximates how human vision see things. We can simply generate colors with the aforementioned color spaces. Here's an example of a Base16 color scheme generation from terminal.sexy ported to Rust.

use std::error::Error;
use std::collections::HashMap;
use std::env;

use hsluv;
use rand_chacha::ChaCha8Rng;
use rand::{Rng, SeedableRng};

#[macro_use]
use serde::{Deserialize, Serialize};


// Based from the implemention at terminal.sexy repo
// at https://github.com/stayradiated/terminal.sexy/blob/2e2c9bec994723a657cce8bf708d83879a50c0ce/lib/stores/random.js
// which in turn based from the demo example at https://www.hsluv.org/syntax/.
type Colors = HashMap<String, String>;

#[derive(Serialize, Deserialize, Debug)]
struct SpecialColors {
  background: String,
  foreground: String,
  cursor: String,
}

impl SpecialColors {
  pub fn new() -> Self {
    Self {
      background: String::from("#000000"),
      foreground: String::from("#000000"),
      cursor: String::from("#000000")
    }
  }
}

#[derive(Serialize, Deserialize, Debug)]
struct PywalObject {
  #[serde(default)]
  alpha: u8,

  special: SpecialColors,

  colors: Colors,

  wallpaper: Option<String>,
}

impl PywalObject {
  pub fn new() -> Self {
    Self {
      alpha: 255,
      special: SpecialColors::new(),
      colors: Colors::new(),
      wallpaper: None
    }
  }
}

/// The command-line interface simply needs a seed to be parsed as an unsigned 64-bit integer (`u64`).
fn main() -> Result<(), Box<dyn Error>> {
  let args: Vec<String> = env::args().collect();
  let mut result = PywalObject::new();

  let mut rng = match args.get(1) {
    Some(arg) => ChaCha8Rng::seed_from_u64(arg.parse::<u64>()?),
    None => ChaCha8Rng::from_entropy()
  };

  // 6 hues to pick from
  let base_hue: f64 = rng.gen_range(0.0, 360.0);
  let hues: Vec<f64> = ([0.0, 60.0, 120.0, 180.0, 240.0, 300.0])
        .iter()
        .map(|offset| (base_hue + offset) % 360.0)
        .collect();

  //  8 shades of low-saturated color
  let base_saturation: f64 = rng.gen_range(8.0, 40.0);
  let base_lightness: f64 = rng.gen_range(0.0, 10.0);
  let range_lightness: f64 = 90.0 - base_lightness;

  result.special.background = hsluv::hsluv_to_hex(
    (hues[0],
    base_saturation,
    base_lightness / 2.0)
  );

  result.special.foreground = hsluv::hsluv_to_hex(
    (hues[0],
    base_saturation,
    range_lightness)
  );

  result.special.cursor = result.special.foreground.clone();

  // Setting the colors according to the Base16 spec at http://chriskempson.com/projects/base16.
  for i in 0..7 {
    result.colors.insert(format!("color{}", i), hsluv::hsluv_to_hex(
      (hues[0], base_saturation, base_lightness + (range_lightness * ((i as f64)/7.0).powf(1.5)))
    ));
  }

  // 8 random shades
  let min_saturation: f64 = rng.gen_range(30.0, 70.0);
  let max_saturation: f64 = min_saturation + 30.0;
  let min_lightness: f64 = rng.gen_range(50.0, 70.0);
  let max_lightness: f64 = min_lightness + 20.0;

  for j in 8..16 {
    result.colors.insert(format!("color{}", j), hsluv::hsluv_to_hex(
      (hues[rng.gen_range(0, hues.len() - 1)], rng.gen_range(min_saturation, max_saturation), rng.gen_range(min_lightness, max_lightness))
    ));
  }

  println!("{:?}", result);
  Ok(())
}