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 range.abs() < f64::EPSILON {
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 mut breaks = Vec::new();
94 let mut v = start;
95 while v <= self.max + step * 0.001 {
96 let pos = self.map(&Value::Float(v));
97 breaks.push((pos, format_number(v)));
98 v += step;
99 }
100 breaks
101 }
102
103 fn name(&self) -> &str {
104 &self.name
105 }
106
107 fn set_name(&mut self, name: &str) {
108 self.name = name.to_string();
109 }
110
111 fn map_to_color(&self, value: &Value) -> Option<(u8, u8, u8)> {
112 let f = value.as_f64()?;
113
114 let c = if f <= self.midpoint {
116 let range = self.midpoint - self.min;
117 let t = if range.abs() < f64::EPSILON {
118 0.0
119 } else {
120 (f - self.min) / range
121 };
122 self.low.lerp(&self.mid, t)
123 } else {
124 let range = self.max - self.midpoint;
125 let t = if range.abs() < f64::EPSILON {
126 1.0
127 } else {
128 (f - self.midpoint) / range
129 };
130 self.mid.lerp(&self.high, t)
131 };
132
133 Some((c.r, c.g, c.b))
134 }
135
136 fn domain(&self) -> Option<(f64, f64)> {
137 if self.min.is_finite() && self.max.is_finite() && self.min <= self.max {
138 Some((self.min, self.max))
139 } else {
140 None
141 }
142 }
143
144 fn clone_box(&self) -> Box<dyn Scale> {
145 Box::new(self.clone())
146 }
147
148 fn reset_training(&mut self) {
149 self.min = f64::INFINITY;
150 self.max = f64::NEG_INFINITY;
151 }
152}