ggplot_rs/scale/
continuous.rs1use crate::aes::Aesthetic;
2use crate::data::Value;
3
4use super::format::LabelFormatter;
5use super::sec_axis::SecAxis;
6use super::transform::ScaleTransform;
7use super::util::{format_number, nice_step};
8use super::Scale;
9
10#[derive(Clone)]
12pub struct ScaleContinuous {
13 aesthetic: Aesthetic,
14 name: String,
15 min: f64,
16 max: f64,
17 trained: bool,
18 filter_oob: bool,
19 expand: (f64, f64), pub(crate) scale_transform: ScaleTransform,
21 custom_breaks: Option<Vec<f64>>,
22 custom_labels: Option<Vec<String>>,
23 pub(crate) sec_axis: Option<SecAxis>,
24 label_formatter: Option<LabelFormatter>,
25}
26
27impl ScaleContinuous {
28 pub fn new() -> Self {
29 ScaleContinuous {
30 aesthetic: Aesthetic::X,
31 name: String::new(),
32 min: f64::INFINITY,
33 max: f64::NEG_INFINITY,
34 trained: false,
35 filter_oob: false,
36 expand: (0.05, 0.0),
37 scale_transform: ScaleTransform::Identity,
38 custom_breaks: None,
39 custom_labels: None,
40 sec_axis: None,
41 label_formatter: None,
42 }
43 }
44
45 pub fn for_aesthetic(mut self, aes: Aesthetic) -> Self {
46 self.aesthetic = aes;
47 self
48 }
49
50 pub fn with_name(mut self, name: &str) -> Self {
51 self.name = name.to_string();
52 self
53 }
54
55 pub fn with_limits(mut self, min: f64, max: f64) -> Self {
56 self.min = min;
57 self.max = max;
58 self.trained = true;
59 self.filter_oob = true;
60 self
61 }
62
63 pub fn with_transform(mut self, transform: ScaleTransform) -> Self {
64 self.scale_transform = transform;
65 self
66 }
67
68 pub fn with_breaks(mut self, breaks: Vec<f64>) -> Self {
70 self.custom_breaks = Some(breaks);
71 self
72 }
73
74 pub fn with_labels(mut self, labels: Vec<String>) -> Self {
76 self.custom_labels = Some(labels);
77 self
78 }
79
80 pub fn with_expand(mut self, mult: f64, add: f64) -> Self {
83 self.expand = (mult, add);
84 self
85 }
86
87 pub fn with_label_formatter<F>(mut self, f: F) -> Self
90 where
91 F: Fn(f64) -> String + Send + Sync + 'static,
92 {
93 self.label_formatter = Some(std::sync::Arc::new(f));
94 self
95 }
96
97 pub fn with_sec_axis(mut self, sec: SecAxis) -> Self {
99 self.sec_axis = Some(sec);
100 self
101 }
102
103 pub fn sec_axis(&self) -> Option<&SecAxis> {
105 self.sec_axis.as_ref()
106 }
107
108 fn format_label(&self, v: f64) -> String {
109 if let Some(f) = &self.label_formatter {
110 f(v)
111 } else {
112 format_number(v)
113 }
114 }
115
116 fn expanded_range(&self) -> (f64, f64) {
117 let range = self.max - self.min;
118 let mult = self.expand.0;
119 let add = self.expand.1;
120 (self.min - range * mult - add, self.max + range * mult + add)
121 }
122}
123
124impl Default for ScaleContinuous {
125 fn default() -> Self {
126 Self::new()
127 }
128}
129
130impl Scale for ScaleContinuous {
131 fn aesthetic(&self) -> Aesthetic {
132 self.aesthetic.clone()
133 }
134
135 fn train(&mut self, values: &[Value]) {
136 for v in values {
137 if let Some(f) = v.as_f64() {
138 if f.is_finite() {
139 if f < self.min {
140 self.min = f;
141 }
142 if f > self.max {
143 self.max = f;
144 }
145 }
146 }
147 }
148 self.trained = true;
149 }
150
151 fn map(&self, value: &Value) -> f64 {
152 let f = match value.as_f64() {
153 Some(f) => f,
154 None => return 0.0,
155 };
156 let (emin, emax) = self.expanded_range();
157 let range = emax - emin;
158 if range.abs() < f64::EPSILON {
159 0.5
160 } else {
161 (f - emin) / range
162 }
163 }
164
165 fn breaks(&self) -> Vec<(f64, String)> {
166 if !self.trained || self.min > self.max {
167 return vec![];
168 }
169
170 if let Some(ref custom) = self.custom_breaks {
172 return custom
173 .iter()
174 .enumerate()
175 .map(|(i, &v)| {
176 let pos = self.map(&Value::Float(v));
177 let label = if let Some(ref labels) = self.custom_labels {
178 labels
179 .get(i)
180 .cloned()
181 .unwrap_or_else(|| self.format_label(v))
182 } else {
183 self.format_label(self.scale_transform.inverse(v))
184 };
185 (pos, label)
186 })
187 .collect();
188 }
189
190 let range = self.max - self.min;
191 if range.abs() < f64::EPSILON {
192 let label = self.format_label(self.scale_transform.inverse(self.min));
193 return vec![(0.5, label)];
194 }
195
196 let (emin, emax) = self.expanded_range();
198 let n_breaks = 5;
199 let raw_step = range / n_breaks as f64;
200 let step = nice_step(raw_step);
201
202 let start = (emin / step).ceil() * step;
203 let mut breaks = Vec::new();
204 let mut v = start;
205 while v <= emax + step * 0.001 {
206 let pos = self.map(&Value::Float(v));
207 let label = self.format_label(self.scale_transform.inverse(v));
209 breaks.push((pos, label));
210 v += step;
211 }
212 breaks
213 }
214
215 fn name(&self) -> &str {
216 &self.name
217 }
218
219 fn set_name(&mut self, name: &str) {
220 self.name = name.to_string();
221 }
222
223 fn transform(&self, value: &Value) -> Value {
224 self.scale_transform.transform_value(value)
225 }
226
227 fn sec_axis(&self) -> Option<&SecAxis> {
228 self.sec_axis.as_ref()
229 }
230
231 fn set_limits(&mut self, min: f64, max: f64) {
232 self.min = min;
233 self.max = max;
234 self.trained = true;
235 }
236
237 fn filter_limits(&self) -> Option<(f64, f64)> {
238 if self.filter_oob && self.trained {
239 Some((self.min, self.max))
240 } else {
241 None
242 }
243 }
244
245 fn domain(&self) -> Option<(f64, f64)> {
246 if self.trained {
247 Some((self.min, self.max))
248 } else {
249 None
250 }
251 }
252
253 fn clone_box(&self) -> Box<dyn Scale> {
254 Box::new(self.clone())
255 }
256
257 fn reset_training(&mut self) {
258 self.min = f64::INFINITY;
259 self.max = f64::NEG_INFINITY;
260 self.trained = false;
261 }
262}