colors-transform 0.2.11

Module for convert and transform colors
Documentation
use super::normalize::bound_ratio;
use super::ColorTuple;

static RGB_UNIT_MAX: f32 = 255.0;
static HUE_MAX: f32 = 360.0;

fn get_min(rgb: &[f32]) -> f32 {
  rgb.iter().fold(std::f32::MAX, |a, &b| a.min(b))
}

fn get_max(rgb: &[f32]) -> f32 {
  rgb.iter().fold(std::f32::MIN, |a, &b| a.max(b))
}

pub fn rgb_to_hex(rgb: &ColorTuple) -> (String, String, String) {
  fn to_hex(n: f32) -> String {
    let s = format!("{:x}", n.round() as i32);
    if s.len() == 1 {
      "0".to_string() + &s
    } else {
      s
    }
  }
  let (r, g, b) = *rgb;

  (to_hex(r), to_hex(g), to_hex(b))
}

pub fn rgb_to_hsl(rgb: &ColorTuple) -> ColorTuple {
  let (r, g, b) = *rgb;
  let rgb_arr: Vec<f32> = [r, g, b].iter().map(|p| p / RGB_UNIT_MAX).collect();
  let max = get_max(&rgb_arr);
  let min = get_min(&rgb_arr);
  let luminace = (max + min) / 2.0;

  if max.eq(&min) {
    return (0.0, 0.0, luminace * 100.0);
  }

  let max_min_delta = max - min;
  let saturation =
    if luminace > 0.5 { max_min_delta / (2.0 - max - min) } else { max_min_delta / (max + min) };

  let red = rgb_arr[0];
  let green = rgb_arr[1];
  let blue = rgb_arr[2];

  let hue = if red.eq(&max) {
    let x = if g < b { 6.0 } else { 0.0 };
    (green - blue) / max_min_delta + x
  } else if green.eq(&max) {
    (blue - red) / max_min_delta + 2.0
  } else {
    (red - green) / max_min_delta + 4.0
  };

  (hue * 60.0, saturation * 100.0, luminace * 100.0)
}

fn calc_rgb_unit(unit: f32, temp1: f32, temp2: f32) -> f32 {
  let mut result = temp2;
  if 6.0 * unit < 1.0 {
    result = temp2 + (temp1 - temp2) * 6.0 * unit
  } else if 2.0 * unit < 1.0 {
    result = temp1
  } else if 3.0 * unit < 2.0 {
    result = temp2 + (temp1 - temp2) * (2.0 / 3.0 - unit) * 6.0
  }
  result * RGB_UNIT_MAX
}
pub fn hsl_to_rgb(hsl: &ColorTuple) -> ColorTuple {
  let (_h, _s, _l) = *hsl;
  let h = _h / 360.0;
  let s = _s / 100.0;
  let l = _l / 100.0;

  if s == 0.0 {
    let unit = RGB_UNIT_MAX * l;
    return (unit, unit, unit);
  }

  let temp1 = if l < 0.5 { l * (1.0 + s) } else { l + s - l * s };

  let temp2 = 2.0 * l - temp1;
  let hue = h;

  let one_third = 1.0 / 3.0;
  let temp_r = bound_ratio(hue + one_third);
  let temp_g = bound_ratio(hue);
  let temp_b = bound_ratio(hue - one_third);

  let r = calc_rgb_unit(temp_r, temp1, temp2);
  let g = calc_rgb_unit(temp_g, temp1, temp2);
  let b = calc_rgb_unit(temp_b, temp1, temp2);
  (r, g, b)
}

pub fn hex_num_to_rgb(num: usize) -> ColorTuple {
  let r = (num >> 16) as f32;
  let g = ((num >> 8) & 0x00FF) as f32;
  let b = (num & 0x0000_00FF) as f32;

  (r, g, b)
}

pub fn rgb_invert(rgb: &ColorTuple) -> ColorTuple {
  let (r, g, b) = rgb;
  (RGB_UNIT_MAX - r, RGB_UNIT_MAX - g, RGB_UNIT_MAX - b)
}

pub fn invert_hue(hue: f32) -> f32 {
  (hue + 180.0) % HUE_MAX
}

pub fn as_rounded_rgb_tuple(t: &ColorTuple) -> (u16, u16, u16) {
  let (r, g, b) = *t;
  (r.round() as u16, g.round() as u16, b.round() as u16)
}

pub fn as_rounded_hsl_tuple(t: &ColorTuple) -> (u16, u16, u16) {
  let (h, s, l) = *t;
  (h.round() as u16, s.round() as u16, l.round() as u16)
}

pub fn round_ratio(r: f32) -> f32 {
  (r * 100.0).round() / 100.0
}