ggplot_rs/scale/
modified.rs1use crate::aes::Aesthetic;
6use crate::data::Value;
7use crate::render::backend::{Linetype, PointShape};
8
9use super::sec_axis::SecAxis;
10use super::Scale;
11
12fn adjust_lightness((r, g, b): (u8, u8, u8), f: f64) -> (u8, u8, u8) {
15 let f = f.clamp(-1.0, 1.0);
16 let ch = |c: u8| -> u8 {
17 let c = c as f64;
18 let out = if f >= 0.0 {
19 c + (255.0 - c) * f
20 } else {
21 c * (1.0 + f)
22 };
23 out.round().clamp(0.0, 255.0) as u8
24 };
25 (ch(r), ch(g), ch(b))
26}
27
28pub struct ScaleColorModified {
32 inner: Box<dyn Scale>,
33 aesthetic: Aesthetic,
34 lightness: f64,
35}
36
37impl ScaleColorModified {
38 pub fn new(inner: Box<dyn Scale>, target: Aesthetic, lightness: f64) -> Self {
39 ScaleColorModified {
40 inner,
41 aesthetic: target,
42 lightness,
43 }
44 }
45}
46
47impl Scale for ScaleColorModified {
48 fn aesthetic(&self) -> Aesthetic {
49 self.aesthetic.clone()
50 }
51 fn train(&mut self, values: &[Value]) {
52 self.inner.train(values)
53 }
54 fn map(&self, value: &Value) -> f64 {
55 self.inner.map(value)
56 }
57 fn breaks(&self) -> Vec<(f64, String)> {
58 self.inner.breaks()
59 }
60 fn name(&self) -> &str {
61 self.inner.name()
62 }
63 fn set_name(&mut self, name: &str) {
64 self.inner.set_name(name)
65 }
66 fn is_discrete(&self) -> bool {
67 self.inner.is_discrete()
68 }
69 fn map_to_color(&self, value: &Value) -> Option<(u8, u8, u8)> {
70 self.inner
71 .map_to_color(value)
72 .map(|c| adjust_lightness(c, self.lightness))
73 }
74 fn map_to_shape(&self, value: &Value) -> Option<PointShape> {
75 self.inner.map_to_shape(value)
76 }
77 fn map_to_linetype(&self, value: &Value) -> Option<Linetype> {
78 self.inner.map_to_linetype(value)
79 }
80 fn map_to_size(&self, value: &Value) -> Option<f64> {
81 self.inner.map_to_size(value)
82 }
83 fn map_to_alpha(&self, value: &Value) -> Option<f64> {
84 self.inner.map_to_alpha(value)
85 }
86 fn sec_axis(&self) -> Option<&SecAxis> {
87 None
88 }
89 fn domain(&self) -> Option<(f64, f64)> {
90 self.inner.domain()
91 }
92 fn clone_box(&self) -> Box<dyn Scale> {
93 Box::new(ScaleColorModified {
94 inner: self.inner.clone_box(),
95 aesthetic: self.aesthetic.clone(),
96 lightness: self.lightness,
97 })
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn darken_and_lighten() {
107 assert_eq!(adjust_lightness((100, 100, 100), -0.5), (50, 50, 50));
108 assert_eq!(adjust_lightness((100, 100, 100), 0.0), (100, 100, 100));
109 assert_eq!(adjust_lightness((100, 100, 100), 1.0), (255, 255, 255));
110 assert_eq!(adjust_lightness((100, 100, 100), -1.0), (0, 0, 0));
111 }
112}