1use crate::aes::Aesthetic;
2use crate::data::Value;
3
4use super::util::{format_number, nice_step};
5use super::Scale;
6
7#[derive(Clone, Debug, Copy)]
9pub struct RGBAColor {
10 pub r: u8,
11 pub g: u8,
12 pub b: u8,
13 pub a: f64,
14}
15
16impl RGBAColor {
17 pub fn new(r: u8, g: u8, b: u8) -> Self {
18 RGBAColor { r, g, b, a: 1.0 }
19 }
20
21 pub fn with_alpha(mut self, a: f64) -> Self {
22 self.a = a;
23 self
24 }
25
26 pub fn lerp(&self, other: &RGBAColor, t: f64) -> RGBAColor {
28 let t = t.clamp(0.0, 1.0);
29 RGBAColor {
30 r: (self.r as f64 * (1.0 - t) + other.r as f64 * t) as u8,
31 g: (self.g as f64 * (1.0 - t) + other.g as f64 * t) as u8,
32 b: (self.b as f64 * (1.0 - t) + other.b as f64 * t) as u8,
33 a: self.a * (1.0 - t) + other.a * t,
34 }
35 }
36}
37
38pub const DEFAULT_PALETTE: &[RGBAColor] = &[
40 RGBAColor {
41 r: 248,
42 g: 118,
43 b: 109,
44 a: 1.0,
45 }, RGBAColor {
47 r: 0,
48 g: 186,
49 b: 56,
50 a: 1.0,
51 }, RGBAColor {
53 r: 97,
54 g: 156,
55 b: 255,
56 a: 1.0,
57 }, RGBAColor {
59 r: 163,
60 g: 103,
61 b: 203,
62 a: 1.0,
63 }, RGBAColor {
65 r: 231,
66 g: 138,
67 b: 0,
68 a: 1.0,
69 }, RGBAColor {
71 r: 0,
72 g: 191,
73 b: 196,
74 a: 1.0,
75 }, RGBAColor {
77 r: 199,
78 g: 124,
79 b: 255,
80 a: 1.0,
81 }, RGBAColor {
83 r: 127,
84 g: 127,
85 b: 127,
86 a: 1.0,
87 }, ];
89
90#[derive(Clone, Debug)]
92pub struct ScaleColorDiscrete {
93 aesthetic: Aesthetic,
94 name: String,
95 levels: Vec<String>,
96 palette: Vec<RGBAColor>,
97}
98
99impl ScaleColorDiscrete {
100 pub fn new(aesthetic: Aesthetic) -> Self {
101 ScaleColorDiscrete {
102 aesthetic,
103 name: String::new(),
104 levels: Vec::new(),
105 palette: DEFAULT_PALETTE.to_vec(),
106 }
107 }
108
109 pub fn with_palette(mut self, colors: Vec<RGBAColor>) -> Self {
110 self.palette = colors;
111 self
112 }
113
114 pub fn with_named_palette(mut self, name: &super::palettes::PaletteName) -> Self {
115 self.palette = super::palettes::palette(name).to_vec();
116 self
117 }
118
119 pub fn color_for_index(&self, idx: usize) -> RGBAColor {
121 self.palette[idx % self.palette.len()]
122 }
123
124 pub fn color_for_value(&self, value: &Value) -> RGBAColor {
126 let key = value.to_group_key();
127 let idx = self.levels.iter().position(|l| l == &key).unwrap_or(0);
128 self.color_for_index(idx)
129 }
130
131 pub fn levels(&self) -> &[String] {
132 &self.levels
133 }
134}
135
136impl Scale for ScaleColorDiscrete {
137 fn aesthetic(&self) -> Aesthetic {
138 self.aesthetic.clone()
139 }
140
141 fn train(&mut self, values: &[Value]) {
142 for v in values {
143 let key = v.to_group_key();
144 if !self.levels.contains(&key) {
145 self.levels.push(key);
146 }
147 }
148 }
149
150 fn map(&self, value: &Value) -> f64 {
151 let key = value.to_group_key();
152 self.levels
153 .iter()
154 .position(|l| l == &key)
155 .map(|i| i as f64)
156 .unwrap_or(0.0)
157 }
158
159 fn breaks(&self) -> Vec<(f64, String)> {
160 self.levels
161 .iter()
162 .enumerate()
163 .map(|(i, label)| (i as f64, label.clone()))
164 .collect()
165 }
166
167 fn name(&self) -> &str {
168 &self.name
169 }
170
171 fn set_name(&mut self, name: &str) {
172 self.name = name.to_string();
173 }
174
175 fn is_discrete(&self) -> bool {
176 true
177 }
178
179 fn map_to_color(&self, value: &Value) -> Option<(u8, u8, u8)> {
180 let c = self.color_for_value(value);
181 Some((c.r, c.g, c.b))
182 }
183
184 fn clone_box(&self) -> Box<dyn Scale> {
185 Box::new(self.clone())
186 }
187
188 fn reset_training(&mut self) {
189 self.levels.clear();
190 }
191}
192
193#[derive(Clone, Debug)]
195pub struct ScaleColorContinuous {
196 aesthetic: Aesthetic,
197 name: String,
198 low: RGBAColor,
199 high: RGBAColor,
200 min: f64,
201 max: f64,
202}
203
204impl ScaleColorContinuous {
205 pub fn new(aesthetic: Aesthetic) -> Self {
206 ScaleColorContinuous {
207 aesthetic,
208 name: String::new(),
209 low: RGBAColor::new(0, 0, 255),
210 high: RGBAColor::new(255, 0, 0),
211 min: f64::INFINITY,
212 max: f64::NEG_INFINITY,
213 }
214 }
215
216 pub fn with_colors(mut self, low: RGBAColor, high: RGBAColor) -> Self {
217 self.low = low;
218 self.high = high;
219 self
220 }
221
222 pub fn color_at(&self, t: f64) -> RGBAColor {
223 self.low.lerp(&self.high, t)
224 }
225}
226
227impl Scale for ScaleColorContinuous {
228 fn aesthetic(&self) -> Aesthetic {
229 self.aesthetic.clone()
230 }
231
232 fn train(&mut self, values: &[Value]) {
233 for v in values {
234 if let Some(f) = v.as_f64() {
235 if f.is_finite() {
236 if f < self.min {
237 self.min = f;
238 }
239 if f > self.max {
240 self.max = f;
241 }
242 }
243 }
244 }
245 }
246
247 fn map(&self, value: &Value) -> f64 {
248 let f = match value.as_f64() {
249 Some(f) => f,
250 None => return 0.0,
251 };
252 let range = self.max - self.min;
253 if range.abs() < f64::EPSILON {
254 0.5
255 } else {
256 (f - self.min) / range
257 }
258 }
259
260 fn breaks(&self) -> Vec<(f64, String)> {
261 if self.min > self.max || !self.min.is_finite() || !self.max.is_finite() {
262 return vec![];
263 }
264
265 let range = self.max - self.min;
266 if range.abs() < f64::EPSILON {
267 return vec![(0.5, format_number(self.min))];
268 }
269
270 let n_breaks = 5;
271 let raw_step = range / n_breaks as f64;
272 let step = nice_step(raw_step);
273
274 let start = (self.min / step).ceil() * step;
275 let mut breaks = Vec::new();
276 let mut v = start;
277 while v <= self.max + step * 0.001 {
278 let pos = self.map(&Value::Float(v));
279 breaks.push((pos, format_number(v)));
280 v += step;
281 }
282 breaks
283 }
284
285 fn name(&self) -> &str {
286 &self.name
287 }
288
289 fn set_name(&mut self, name: &str) {
290 self.name = name.to_string();
291 }
292
293 fn map_to_color(&self, value: &Value) -> Option<(u8, u8, u8)> {
294 let t = self.map(value);
295 let c = self.color_at(t);
296 Some((c.r, c.g, c.b))
297 }
298
299 fn domain(&self) -> Option<(f64, f64)> {
300 if self.min.is_finite() && self.max.is_finite() && self.min <= self.max {
301 Some((self.min, self.max))
302 } else {
303 None
304 }
305 }
306
307 fn clone_box(&self) -> Box<dyn Scale> {
308 Box::new(self.clone())
309 }
310
311 fn reset_training(&mut self) {
312 self.min = f64::INFINITY;
313 self.max = f64::NEG_INFINITY;
314 }
315}