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 set_default_locale(&mut self, locale: &crate::locale::Locale) {
36 for s in &mut self.scales {
37 s.set_default_locale(locale);
38 }
39 }
40
41 pub fn get(&self, aes: &Aesthetic) -> Option<&dyn Scale> {
43 self.scales
44 .iter()
45 .find(|s| s.aesthetic() == *aes)
46 .map(|s| s.as_ref())
47 }
48
49 pub fn get_mut(&mut self, aes: &Aesthetic) -> Option<&mut Box<dyn Scale>> {
51 self.scales.iter_mut().find(|s| s.aesthetic() == *aes)
52 }
53
54 pub fn ensure_scale(&mut self, aes: &Aesthetic, data: &DataFrame) {
56 if self.get(aes).is_some() {
57 return;
58 }
59 if matches!(
62 aes,
63 Aesthetic::Open
64 | Aesthetic::High
65 | Aesthetic::Low
66 | Aesthetic::Close
67 | Aesthetic::Slope
68 | Aesthetic::Intercept
69 | Aesthetic::Xintercept
74 | Aesthetic::Yintercept
75 ) {
76 return;
77 }
78 let values = data.column(aes.col_name());
79
80 let is_discrete = match values {
81 Some(vals) => vals
82 .iter()
83 .any(|v| matches!(v, Value::Str(_) | Value::Bool(_))),
84 None => false,
85 };
86
87 let is_datetime = match values {
88 Some(vals) => vals.iter().any(|v| v.is_datetime()),
89 None => false,
90 };
91
92 match aes {
93 Aesthetic::Color | Aesthetic::Fill => {
94 if is_discrete {
95 let scale = ScaleColorDiscrete::new(aes.clone());
96 self.scales.push(Box::new(scale));
97 } else {
98 let scale = ScaleColorContinuous::new(aes.clone());
99 self.scales.push(Box::new(scale));
100 }
101 }
102 Aesthetic::Shape => {
103 let scale = ScaleShapeDiscrete::new();
104 self.scales.push(Box::new(scale));
105 }
106 Aesthetic::Linetype => {
107 let scale = ScaleLinetypeDiscrete::new();
108 self.scales.push(Box::new(scale));
109 }
110 Aesthetic::Size => {
111 let scale = ScaleSizeContinuous::new();
112 self.scales.push(Box::new(scale));
113 }
114 Aesthetic::Alpha => {
115 let scale = ScaleAlphaContinuous::new();
116 self.scales.push(Box::new(scale));
117 }
118 _ => {
119 if is_discrete {
120 let scale = ScaleDiscrete::new().for_aesthetic(aes.clone());
121 self.scales.push(Box::new(scale));
122 } else if is_datetime {
123 let scale = ScaleDateTime::new().for_aesthetic(aes.clone());
124 self.scales.push(Box::new(scale));
125 } else {
126 let scale = ScaleContinuous::new().for_aesthetic(aes.clone());
127 self.scales.push(Box::new(scale));
128 }
129 }
130 }
131 }
132
133 pub fn train_layer(&mut self, data: &DataFrame) {
135 for scale in &mut self.scales {
136 let col_name = scale.aesthetic().col_name().to_string();
137 if let Some(values) = data.column(&col_name) {
138 scale.train(values);
139 }
140 }
141 }
142
143 pub fn map_value(&self, aes: &Aesthetic, value: &Value) -> f64 {
145 self.get(aes).map(|s| s.map(value)).unwrap_or(0.0)
146 }
147
148 pub fn map_color(&self, aes: &Aesthetic, value: &Value) -> Option<(u8, u8, u8)> {
150 self.get(aes).and_then(|s| s.map_to_color(value))
151 }
152
153 pub fn map_shape(&self, value: &Value) -> Option<PointShape> {
155 self.get(&Aesthetic::Shape)
156 .and_then(|s| s.map_to_shape(value))
157 }
158
159 pub fn map_linetype(&self, value: &Value) -> Option<Linetype> {
161 self.get(&Aesthetic::Linetype)
162 .and_then(|s| s.map_to_linetype(value))
163 }
164
165 pub fn map_size(&self, value: &Value) -> Option<f64> {
167 self.get(&Aesthetic::Size)
168 .and_then(|s| s.map_to_size(value))
169 }
170
171 pub fn map_alpha(&self, value: &Value) -> Option<f64> {
173 self.get(&Aesthetic::Alpha)
174 .and_then(|s| s.map_to_alpha(value))
175 }
176
177 pub fn set_limits(&mut self, aes: &Aesthetic, min: f64, max: f64) {
179 if let Some(scale) = self.get_mut(aes) {
180 scale.set_limits(min, max);
181 }
182 }
183
184 pub fn sec_axis(&self, aes: &Aesthetic) -> Option<&crate::scale::sec_axis::SecAxis> {
186 self.get(aes).and_then(|s| s.sec_axis())
187 }
188
189 pub fn iter(&self) -> impl Iterator<Item = &dyn Scale> {
191 self.scales.iter().map(|s| s.as_ref())
192 }
193}
194
195impl Clone for ScaleSet {
196 fn clone(&self) -> Self {
197 ScaleSet {
198 scales: self.scales.iter().map(|s| s.clone_box()).collect(),
199 }
200 }
201}
202
203impl Default for ScaleSet {
204 fn default() -> Self {
205 Self::new()
206 }
207}