mandelbruhst_cli/
palette.rs1use crate::opts::Interval;
2use anyhow::{anyhow, Result};
3use image::Rgb;
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, Serialize, Deserialize)]
7pub struct ColorPalette {
8 pub color_vals: Vec<ConfigRGB>,
9}
10
11#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
12pub struct ConfigRGB {
13 pub value: f64,
14 pub red: u8,
17 pub green: u8,
18 pub blue: u8,
19}
20
21impl ConfigRGB {
22 pub fn to_rgb(&self) -> Rgb<u8> {
23 Rgb([self.red, self.green, self.blue])
24 }
25
26 pub fn lerp(&self, o: &Self, value: f64) -> Rgb<u8> {
27 let r_interval = Interval {
28 lower: self.red as f64,
29 upper: o.red as f64,
30 };
31 let g_interval = Interval {
32 lower: self.green as f64,
33 upper: o.green as f64,
34 };
35 let b_interval = Interval {
36 lower: self.blue as f64,
37 upper: o.blue as f64,
38 };
39 let frac = (value - self.value) / (o.value - self.value);
40
41 Rgb([
42 r_interval.lerp(frac) as u8,
43 g_interval.lerp(frac) as u8,
44 b_interval.lerp(frac) as u8,
45 ])
46 }
47}
48
49impl ColorPalette {
50 pub fn new(color_vals: Vec<ConfigRGB>) -> Result<ColorPalette> {
52 if color_vals.len() < 2 {
53 return Err(anyhow!("need color vals"));
54 }
55
56 let mut sorted_colors = color_vals;
57 sorted_colors.sort_by(|a, b| a.value.partial_cmp(&b.value).unwrap());
58
59 let first = sorted_colors
60 .first()
61 .expect("more than 2 vals is an assertion")
62 .value;
63 let last = sorted_colors.last().unwrap().value;
64
65 if first != 0.0 || last != 1.0 {
66 return Err(anyhow!("need vals for 0.0 and 1.0"));
67 }
68
69 Ok(ColorPalette {
70 color_vals: sorted_colors,
71 })
72 }
73
74 pub fn value(&self, value: f64) -> Rgb<u8> {
75 if value > 1. {
76 return self.color_vals.last().unwrap().to_rgb();
77 }
78
79 match self
80 .color_vals
81 .binary_search_by(|&color| color.value.partial_cmp(&value).unwrap())
82 {
83 Ok(i) => self.color_vals[i].to_rgb(),
84 Err(i) => {
85 let c1 = self.color_vals[i - 1];
86 let c2 = self.color_vals[i];
87 c1.lerp(&c2, value)
88 }
89 }
90 }
91}