ggplot_rs/scale/
gradient.rs1use crate::aes::Aesthetic;
2use crate::data::Value;
3
4use super::color::RGBAColor;
5use super::util::{format_number, nice_step};
6use super::Scale;
7
8#[derive(Clone, Debug)]
10pub struct ScaleColorGradient2 {
11 aesthetic: Aesthetic,
12 name: String,
13 low: RGBAColor,
14 mid: RGBAColor,
15 high: RGBAColor,
16 midpoint: f64,
17 min: f64,
18 max: f64,
19}
20
21impl ScaleColorGradient2 {
22 pub fn new(aesthetic: Aesthetic) -> Self {
23 ScaleColorGradient2 {
24 aesthetic,
25 name: String::new(),
26 low: RGBAColor::new(0, 0, 255),
27 mid: RGBAColor::new(255, 255, 255),
28 high: RGBAColor::new(255, 0, 0),
29 midpoint: 0.0,
30 min: f64::INFINITY,
31 max: f64::NEG_INFINITY,
32 }
33 }
34
35 pub fn with_colors(mut self, low: RGBAColor, mid: RGBAColor, high: RGBAColor) -> Self {
36 self.low = low;
37 self.mid = mid;
38 self.high = high;
39 self
40 }
41
42 pub fn with_midpoint(mut self, midpoint: f64) -> Self {
43 self.midpoint = midpoint;
44 self
45 }
46}
47
48impl Scale for ScaleColorGradient2 {
49 fn aesthetic(&self) -> Aesthetic {
50 self.aesthetic.clone()
51 }
52
53 fn train(&mut self, values: &[Value]) {
54 for v in values {
55 if let Some(f) = v.as_f64() {
56 if f.is_finite() {
57 if f < self.min {
58 self.min = f;
59 }
60 if f > self.max {
61 self.max = f;
62 }
63 }
64 }
65 }
66 }
67
68 fn map(&self, value: &Value) -> f64 {
69 let f = match value.as_f64() {
70 Some(f) => f,
71 None => return 0.0,
72 };
73 let range = self.max - self.min;
74 if range.abs() < f64::EPSILON {
75 0.5
76 } else {
77 (f - self.min) / range
78 }
79 }
80
81 fn breaks(&self) -> Vec<(f64, String)> {
82 if self.min > self.max || !self.min.is_finite() || !self.max.is_finite() {
83 return vec![];
84 }
85 let range = self.max - self.min;
86 if super::util::is_degenerate_range(self.min, self.max) {
87 return vec![(0.5, format_number(self.min))];
88 }
89 let n_breaks = 5;
90 let raw_step = range / n_breaks as f64;
91 let step = nice_step(raw_step);
92 let start = (self.min / step).ceil() * step;
93 let values = super::util::stepped_breaks(start, self.max, step);
94 let labels = super::util::format_numbers(&values);
95 values
96 .iter()
97 .zip(labels)
98 .map(|(&v, l)| (self.map(&Value::Float(v)), l))
99 .collect()
100 }
101
102 fn name(&self) -> &str {
103 &self.name
104 }
105
106 fn set_name(&mut self, name: &str) {
107 self.name = name.to_string();
108 }
109
110 fn map_to_color(&self, value: &Value) -> Option<(u8, u8, u8)> {
111 let f = value.as_f64()?;
112
113 let c = if f <= self.midpoint {
115 let range = self.midpoint - self.min;
116 let t = if range.abs() < f64::EPSILON {
117 0.0
118 } else {
119 (f - self.min) / range
120 };
121 self.low.lerp(&self.mid, t)
122 } else {
123 let range = self.max - self.midpoint;
124 let t = if range.abs() < f64::EPSILON {
125 1.0
126 } else {
127 (f - self.midpoint) / range
128 };
129 self.mid.lerp(&self.high, t)
130 };
131
132 Some((c.r, c.g, c.b))
133 }
134
135 fn domain(&self) -> Option<(f64, f64)> {
136 if self.min.is_finite() && self.max.is_finite() && self.min <= self.max {
137 Some((self.min, self.max))
138 } else {
139 None
140 }
141 }
142
143 fn clone_box(&self) -> Box<dyn Scale> {
144 Box::new(self.clone())
145 }
146
147 fn reset_training(&mut self) {
148 self.min = f64::INFINITY;
149 self.max = f64::NEG_INFINITY;
150 }
151}