ggplot_rs/scale/
scale_set.rs1use crate::aes::Aesthetic;
2use crate::data::{DataFrame, Value};
3use crate::render::backend::{Linetype, PointShape};
4use crate::scale::alpha::ScaleAlphaContinuous;
5use crate::scale::color::{ScaleColorContinuous, ScaleColorDiscrete};
6use crate::scale::continuous::ScaleContinuous;
7use crate::scale::datetime::ScaleDateTime;
8use crate::scale::discrete::ScaleDiscrete;
9use crate::scale::linetype::ScaleLinetypeDiscrete;
10use crate::scale::shape::ScaleShapeDiscrete;
11use crate::scale::size::ScaleSizeContinuous;
12
13use super::Scale;
14
15pub struct ScaleSet {
17 scales: Vec<Box<dyn Scale>>,
18}
19
20impl ScaleSet {
21 pub fn new() -> Self {
22 ScaleSet { scales: Vec::new() }
23 }
24
25 pub fn add(&mut self, scale: Box<dyn Scale>) {
27 let aes = scale.aesthetic();
29 self.scales.retain(|s| s.aesthetic() != aes);
30 self.scales.push(scale);
31 }
32
33 pub fn get(&self, aes: &Aesthetic) -> Option<&dyn Scale> {
35 self.scales
36 .iter()
37 .find(|s| s.aesthetic() == *aes)
38 .map(|s| s.as_ref())
39 }
40
41 pub fn get_mut(&mut self, aes: &Aesthetic) -> Option<&mut Box<dyn Scale>> {
43 self.scales.iter_mut().find(|s| s.aesthetic() == *aes)
44 }
45
46 pub fn ensure_scale(&mut self, aes: &Aesthetic, data: &DataFrame) {
48 if self.get(aes).is_some() {
49 return;
50 }
51 if matches!(
54 aes,
55 Aesthetic::Open | Aesthetic::High | Aesthetic::Low | Aesthetic::Close
56 ) {
57 return;
58 }
59
60 let col_name = aes.col_name();
61 let values = data.column(col_name);
62
63 let is_discrete = match values {
64 Some(vals) => vals
65 .iter()
66 .any(|v| matches!(v, Value::Str(_) | Value::Bool(_))),
67 None => false,
68 };
69
70 let is_datetime = match values {
71 Some(vals) => vals.iter().any(|v| v.is_datetime()),
72 None => false,
73 };
74
75 match aes {
76 Aesthetic::Color | Aesthetic::Fill => {
77 if is_discrete {
78 let scale = ScaleColorDiscrete::new(aes.clone());
79 self.scales.push(Box::new(scale));
80 } else {
81 let scale = ScaleColorContinuous::new(aes.clone());
82 self.scales.push(Box::new(scale));
83 }
84 }
85 Aesthetic::Shape => {
86 let scale = ScaleShapeDiscrete::new();
87 self.scales.push(Box::new(scale));
88 }
89 Aesthetic::Linetype => {
90 let scale = ScaleLinetypeDiscrete::new();
91 self.scales.push(Box::new(scale));
92 }
93 Aesthetic::Size => {
94 let scale = ScaleSizeContinuous::new();
95 self.scales.push(Box::new(scale));
96 }
97 Aesthetic::Alpha => {
98 let scale = ScaleAlphaContinuous::new();
99 self.scales.push(Box::new(scale));
100 }
101 _ => {
102 if is_discrete {
103 let scale = ScaleDiscrete::new().for_aesthetic(aes.clone());
104 self.scales.push(Box::new(scale));
105 } else if is_datetime {
106 let scale = ScaleDateTime::new().for_aesthetic(aes.clone());
107 self.scales.push(Box::new(scale));
108 } else {
109 let scale = ScaleContinuous::new().for_aesthetic(aes.clone());
110 self.scales.push(Box::new(scale));
111 }
112 }
113 }
114 }
115
116 pub fn train_layer(&mut self, data: &DataFrame) {
118 for scale in &mut self.scales {
119 let col_name = scale.aesthetic().col_name().to_string();
120 if let Some(values) = data.column(&col_name) {
121 scale.train(values);
122 }
123 }
124 }
125
126 pub fn map_value(&self, aes: &Aesthetic, value: &Value) -> f64 {
128 self.get(aes).map(|s| s.map(value)).unwrap_or(0.0)
129 }
130
131 pub fn map_color(&self, aes: &Aesthetic, value: &Value) -> Option<(u8, u8, u8)> {
133 self.get(aes).and_then(|s| s.map_to_color(value))
134 }
135
136 pub fn map_shape(&self, value: &Value) -> Option<PointShape> {
138 self.get(&Aesthetic::Shape)
139 .and_then(|s| s.map_to_shape(value))
140 }
141
142 pub fn map_linetype(&self, value: &Value) -> Option<Linetype> {
144 self.get(&Aesthetic::Linetype)
145 .and_then(|s| s.map_to_linetype(value))
146 }
147
148 pub fn map_size(&self, value: &Value) -> Option<f64> {
150 self.get(&Aesthetic::Size)
151 .and_then(|s| s.map_to_size(value))
152 }
153
154 pub fn map_alpha(&self, value: &Value) -> Option<f64> {
156 self.get(&Aesthetic::Alpha)
157 .and_then(|s| s.map_to_alpha(value))
158 }
159
160 pub fn set_limits(&mut self, aes: &Aesthetic, min: f64, max: f64) {
162 if let Some(scale) = self.get_mut(aes) {
163 scale.set_limits(min, max);
164 }
165 }
166
167 pub fn sec_axis(&self, aes: &Aesthetic) -> Option<&crate::scale::sec_axis::SecAxis> {
169 self.get(aes).and_then(|s| s.sec_axis())
170 }
171
172 pub fn iter(&self) -> impl Iterator<Item = &dyn Scale> {
174 self.scales.iter().map(|s| s.as_ref())
175 }
176}
177
178impl Clone for ScaleSet {
179 fn clone(&self) -> Self {
180 ScaleSet {
181 scales: self.scales.iter().map(|s| s.clone_box()).collect(),
182 }
183 }
184}
185
186impl Default for ScaleSet {
187 fn default() -> Self {
188 Self::new()
189 }
190}