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::linewidth::ScaleLinewidthContinuous;
11use crate::scale::shape::ScaleShapeDiscrete;
12use crate::scale::size::ScaleSizeContinuous;
13
14use super::Scale;
15
16pub struct ScaleSet {
18 scales: Vec<Box<dyn Scale>>,
19}
20
21impl ScaleSet {
22 pub fn new() -> Self {
23 ScaleSet { scales: Vec::new() }
24 }
25
26 pub fn add(&mut self, scale: Box<dyn Scale>) {
28 let aes = scale.aesthetic();
30 self.scales.retain(|s| s.aesthetic() != aes);
31 self.scales.push(scale);
32 }
33
34 pub fn set_default_locale(&mut self, locale: &crate::locale::Locale) {
37 for s in &mut self.scales {
38 s.set_default_locale(locale);
39 }
40 }
41
42 pub fn get(&self, aes: &Aesthetic) -> Option<&dyn Scale> {
44 self.scales
45 .iter()
46 .find(|s| s.aesthetic() == *aes)
47 .map(|s| s.as_ref())
48 }
49
50 pub fn get_mut(&mut self, aes: &Aesthetic) -> Option<&mut Box<dyn Scale>> {
52 self.scales.iter_mut().find(|s| s.aesthetic() == *aes)
53 }
54
55 pub fn ensure_scale(&mut self, aes: &Aesthetic, data: &DataFrame) {
57 if self.get(aes).is_some() {
58 return;
59 }
60 if matches!(
63 aes,
64 Aesthetic::Open
65 | Aesthetic::High
66 | Aesthetic::Low
67 | Aesthetic::Close
68 | Aesthetic::Slope
69 | Aesthetic::Intercept
70 | Aesthetic::Xintercept
75 | Aesthetic::Yintercept
76 | Aesthetic::Tooltip
79 | Aesthetic::DataAttr(_)
80 | Aesthetic::Class
81 | Aesthetic::GroupWrap(_)
82 | Aesthetic::Fontface
85 | Aesthetic::Hjust
87 | Aesthetic::Vjust
88 | Aesthetic::Lower
93 | Aesthetic::Middle
94 | Aesthetic::Upper
95 | Aesthetic::Outliers
96 | Aesthetic::From
98 | Aesthetic::To
99 | Aesthetic::Subgroup
100 | Aesthetic::Sets
101 ) {
102 return;
103 }
104 let fallback: &[&str] = match aes {
108 Aesthetic::X => &["xmin", "xmax", "xend"],
109 Aesthetic::Y => &["ymin", "ymax", "yend"],
110 _ => &[],
111 };
112 let values = data
113 .column(aes.col_name())
114 .or_else(|| fallback.iter().find_map(|c| data.column(c)));
115
116 let is_discrete = match values {
117 Some(vals) => vals
118 .iter()
119 .any(|v| matches!(v, Value::Str(_) | Value::Bool(_))),
120 None => false,
121 };
122
123 let is_datetime = match values {
124 Some(vals) => vals.iter().any(|v| v.is_datetime()),
125 None => false,
126 };
127
128 match aes {
129 Aesthetic::Color | Aesthetic::Fill => {
130 if is_discrete {
131 let scale = ScaleColorDiscrete::new(aes.clone());
132 self.scales.push(Box::new(scale));
133 } else {
134 let scale = ScaleColorContinuous::new(aes.clone());
135 self.scales.push(Box::new(scale));
136 }
137 }
138 Aesthetic::Shape => {
139 let scale = ScaleShapeDiscrete::new();
140 self.scales.push(Box::new(scale));
141 }
142 Aesthetic::Linetype => {
143 let scale = ScaleLinetypeDiscrete::new();
144 self.scales.push(Box::new(scale));
145 }
146 Aesthetic::Size => {
147 let scale = ScaleSizeContinuous::new();
148 self.scales.push(Box::new(scale));
149 }
150 Aesthetic::Linewidth => {
151 let scale = ScaleLinewidthContinuous::new();
152 self.scales.push(Box::new(scale));
153 }
154 Aesthetic::Alpha => {
155 let scale = ScaleAlphaContinuous::new();
156 self.scales.push(Box::new(scale));
157 }
158 _ => {
159 if is_discrete {
160 let scale = ScaleDiscrete::new().for_aesthetic(aes.clone());
161 self.scales.push(Box::new(scale));
162 } else if is_datetime {
163 let scale = ScaleDateTime::new().for_aesthetic(aes.clone());
164 self.scales.push(Box::new(scale));
165 } else {
166 let scale = ScaleContinuous::new().for_aesthetic(aes.clone());
167 self.scales.push(Box::new(scale));
168 }
169 }
170 }
171 }
172
173 pub fn train_layer(&mut self, data: &DataFrame) {
175 for scale in &mut self.scales {
176 let col_name = scale.aesthetic().col_name().to_string();
177 if let Some(values) = data.column(&col_name) {
178 scale.train(values);
179 }
180 }
181 }
182
183 pub fn map_value(&self, aes: &Aesthetic, value: &Value) -> f64 {
185 self.get(aes).map(|s| s.map(value)).unwrap_or(0.0)
186 }
187
188 pub fn map_color(&self, aes: &Aesthetic, value: &Value) -> Option<(u8, u8, u8)> {
190 self.get(aes).and_then(|s| s.map_to_color(value))
191 }
192
193 pub fn na_fill_pattern(&self, aes: &Aesthetic) -> Option<crate::render::backend::FillPattern> {
198 self.get(aes).and_then(|s| s.na_fill_pattern())
199 }
200
201 pub fn map_shape(&self, value: &Value) -> Option<PointShape> {
203 self.get(&Aesthetic::Shape)
204 .and_then(|s| s.map_to_shape(value))
205 }
206
207 pub fn map_linetype(&self, value: &Value) -> Option<Linetype> {
209 self.get(&Aesthetic::Linetype)
210 .and_then(|s| s.map_to_linetype(value))
211 }
212
213 pub fn map_size(&self, value: &Value) -> Option<f64> {
215 self.get(&Aesthetic::Size)
216 .and_then(|s| s.map_to_size(value))
217 }
218
219 pub fn map_alpha(&self, value: &Value) -> Option<f64> {
221 self.get(&Aesthetic::Alpha)
222 .and_then(|s| s.map_to_alpha(value))
223 }
224
225 pub fn map_linewidth(&self, value: &Value) -> Option<f64> {
227 self.get(&Aesthetic::Linewidth)
228 .and_then(|s| s.map_to_size(value))
229 }
230
231 pub fn set_limits(&mut self, aes: &Aesthetic, min: f64, max: f64) {
233 if let Some(scale) = self.get_mut(aes) {
234 scale.set_limits(min, max);
235 }
236 }
237
238 pub fn sec_axis(&self, aes: &Aesthetic) -> Option<&crate::scale::sec_axis::SecAxis> {
240 self.get(aes).and_then(|s| s.sec_axis())
241 }
242
243 pub fn iter(&self) -> impl Iterator<Item = &dyn Scale> {
245 self.scales.iter().map(|s| s.as_ref())
246 }
247}
248
249impl Clone for ScaleSet {
250 fn clone(&self) -> Self {
251 ScaleSet {
252 scales: self.scales.iter().map(|s| s.clone_box()).collect(),
253 }
254 }
255}
256
257impl Default for ScaleSet {
258 fn default() -> Self {
259 Self::new()
260 }
261}