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