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
56 | Aesthetic::High
57 | Aesthetic::Low
58 | Aesthetic::Close
59 | Aesthetic::Slope
60 | Aesthetic::Intercept
61 | Aesthetic::Xintercept
66 | Aesthetic::Yintercept
67 ) {
68 return;
69 }
70 let values = data.column(aes.col_name());
71
72 let is_discrete = match values {
73 Some(vals) => vals
74 .iter()
75 .any(|v| matches!(v, Value::Str(_) | Value::Bool(_))),
76 None => false,
77 };
78
79 let is_datetime = match values {
80 Some(vals) => vals.iter().any(|v| v.is_datetime()),
81 None => false,
82 };
83
84 match aes {
85 Aesthetic::Color | Aesthetic::Fill => {
86 if is_discrete {
87 let scale = ScaleColorDiscrete::new(aes.clone());
88 self.scales.push(Box::new(scale));
89 } else {
90 let scale = ScaleColorContinuous::new(aes.clone());
91 self.scales.push(Box::new(scale));
92 }
93 }
94 Aesthetic::Shape => {
95 let scale = ScaleShapeDiscrete::new();
96 self.scales.push(Box::new(scale));
97 }
98 Aesthetic::Linetype => {
99 let scale = ScaleLinetypeDiscrete::new();
100 self.scales.push(Box::new(scale));
101 }
102 Aesthetic::Size => {
103 let scale = ScaleSizeContinuous::new();
104 self.scales.push(Box::new(scale));
105 }
106 Aesthetic::Alpha => {
107 let scale = ScaleAlphaContinuous::new();
108 self.scales.push(Box::new(scale));
109 }
110 _ => {
111 if is_discrete {
112 let scale = ScaleDiscrete::new().for_aesthetic(aes.clone());
113 self.scales.push(Box::new(scale));
114 } else if is_datetime {
115 let scale = ScaleDateTime::new().for_aesthetic(aes.clone());
116 self.scales.push(Box::new(scale));
117 } else {
118 let scale = ScaleContinuous::new().for_aesthetic(aes.clone());
119 self.scales.push(Box::new(scale));
120 }
121 }
122 }
123 }
124
125 pub fn train_layer(&mut self, data: &DataFrame) {
127 for scale in &mut self.scales {
128 let col_name = scale.aesthetic().col_name().to_string();
129 if let Some(values) = data.column(&col_name) {
130 scale.train(values);
131 }
132 }
133 }
134
135 pub fn map_value(&self, aes: &Aesthetic, value: &Value) -> f64 {
137 self.get(aes).map(|s| s.map(value)).unwrap_or(0.0)
138 }
139
140 pub fn map_color(&self, aes: &Aesthetic, value: &Value) -> Option<(u8, u8, u8)> {
142 self.get(aes).and_then(|s| s.map_to_color(value))
143 }
144
145 pub fn map_shape(&self, value: &Value) -> Option<PointShape> {
147 self.get(&Aesthetic::Shape)
148 .and_then(|s| s.map_to_shape(value))
149 }
150
151 pub fn map_linetype(&self, value: &Value) -> Option<Linetype> {
153 self.get(&Aesthetic::Linetype)
154 .and_then(|s| s.map_to_linetype(value))
155 }
156
157 pub fn map_size(&self, value: &Value) -> Option<f64> {
159 self.get(&Aesthetic::Size)
160 .and_then(|s| s.map_to_size(value))
161 }
162
163 pub fn map_alpha(&self, value: &Value) -> Option<f64> {
165 self.get(&Aesthetic::Alpha)
166 .and_then(|s| s.map_to_alpha(value))
167 }
168
169 pub fn set_limits(&mut self, aes: &Aesthetic, min: f64, max: f64) {
171 if let Some(scale) = self.get_mut(aes) {
172 scale.set_limits(min, max);
173 }
174 }
175
176 pub fn sec_axis(&self, aes: &Aesthetic) -> Option<&crate::scale::sec_axis::SecAxis> {
178 self.get(aes).and_then(|s| s.sec_axis())
179 }
180
181 pub fn iter(&self) -> impl Iterator<Item = &dyn Scale> {
183 self.scales.iter().map(|s| s.as_ref())
184 }
185}
186
187impl Clone for ScaleSet {
188 fn clone(&self) -> Self {
189 ScaleSet {
190 scales: self.scales.iter().map(|s| s.clone_box()).collect(),
191 }
192 }
193}
194
195impl Default for ScaleSet {
196 fn default() -> Self {
197 Self::new()
198 }
199}