wiki/2020-09-19-18-43-07.org
Gabriel Arazas aea7015cd5 Update the files for convention
Apparently, the convention (at least starting from 2018) is to make the
keywords and block names to be in lowercase as stated from one of the
following discussions at
https://orgmode.org/list/87tuuw3n15.fsf@nicolasgoaziou.fr/.

The files was updated with a one liner of shell. However, this is Emacs
and org-mode does have an API to let you do stuff in your config and
interact with the documents internally so it is not an elegant solution
in any way.
2021-04-02 00:08:15 +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(())
}