1use std::sync::Arc;
2
3use crate::aes::Aesthetic;
4use crate::data::Value;
5
6use super::format::LabelFormatter;
7use super::sec_axis::SecAxis;
8use super::transform::ScaleTransform;
9use super::util::extended_breaks;
10use super::Scale;
11
12pub type LocalizedLabels = Arc<dyn Fn(Option<&crate::locale::Locale>) -> Vec<String> + Send + Sync>;
14
15#[derive(Clone)]
17pub struct ScaleContinuous {
18 aesthetic: Aesthetic,
19 name: String,
20 min: f64,
21 max: f64,
22 trained: bool,
23 filter_oob: bool,
24 expand: (f64, f64, f64, f64),
26 position_opposite: bool,
29 pub(crate) scale_transform: ScaleTransform,
30 custom_breaks: Option<Vec<f64>>,
31 custom_labels: Option<Vec<String>>,
32 localized_labels: Option<LocalizedLabels>,
34 pub(crate) sec_axis: Option<SecAxis>,
35 label_formatter: Option<LabelFormatter>,
36 locale: Option<crate::locale::Locale>,
38}
39
40impl ScaleContinuous {
41 pub fn new() -> Self {
42 ScaleContinuous {
43 aesthetic: Aesthetic::X,
44 name: String::new(),
45 min: f64::INFINITY,
46 max: f64::NEG_INFINITY,
47 trained: false,
48 filter_oob: false,
49 expand: (0.05, 0.0, 0.05, 0.0),
50 position_opposite: false,
51 scale_transform: ScaleTransform::Identity,
52 custom_breaks: None,
53 custom_labels: None,
54 localized_labels: None,
55 sec_axis: None,
56 label_formatter: None,
57 locale: None,
58 }
59 }
60
61 pub fn for_aesthetic(mut self, aes: Aesthetic) -> Self {
62 self.aesthetic = aes;
63 self
64 }
65
66 pub fn with_name(mut self, name: &str) -> Self {
67 self.name = name.to_string();
68 self
69 }
70
71 pub fn with_limits(mut self, min: f64, max: f64) -> Self {
72 self.min = min;
73 self.max = max;
74 self.trained = true;
75 self.filter_oob = true;
76 self
77 }
78
79 pub fn with_transform(mut self, transform: ScaleTransform) -> Self {
80 self.scale_transform = transform;
81 self
82 }
83
84 pub fn with_breaks(mut self, breaks: Vec<f64>) -> Self {
86 self.custom_breaks = Some(breaks);
87 self
88 }
89
90 pub fn with_labels(mut self, labels: Vec<String>) -> Self {
92 self.custom_labels = Some(labels);
93 self
94 }
95
96 pub fn with_localized_labels<F>(mut self, f: F) -> Self
101 where
102 F: Fn(Option<&crate::locale::Locale>) -> Vec<String> + Send + Sync + 'static,
103 {
104 self.localized_labels = Some(Arc::new(f));
105 self
106 }
107
108 pub fn with_expand(mut self, mult: f64, add: f64) -> Self {
111 self.expand = (mult, add, mult, add);
112 self
113 }
114
115 pub fn with_expand_sides(
118 mut self,
119 mult_lower: f64,
120 add_lower: f64,
121 mult_upper: f64,
122 add_upper: f64,
123 ) -> Self {
124 self.expand = (mult_lower, add_lower, mult_upper, add_upper);
125 self
126 }
127
128 pub fn with_position_opposite(mut self) -> Self {
130 self.position_opposite = true;
131 self
132 }
133
134 pub fn with_label_formatter<F>(mut self, f: F) -> Self
137 where
138 F: Fn(f64) -> String + Send + Sync + 'static,
139 {
140 self.label_formatter = Some(std::sync::Arc::new(f));
141 self
142 }
143
144 pub fn with_locale(mut self, locale: crate::locale::Locale) -> Self {
149 self.locale = Some(locale);
150 self
151 }
152
153 pub fn with_sec_axis(mut self, sec: SecAxis) -> Self {
155 self.sec_axis = Some(sec);
156 self
157 }
158
159 pub fn sec_axis(&self) -> Option<&SecAxis> {
161 self.sec_axis.as_ref()
162 }
163
164 fn format_label(&self, v: f64) -> String {
165 if let Some(f) = &self.label_formatter {
166 f(v)
167 } else {
168 super::util::format_number_opt(v, self.locale.as_ref())
169 }
170 }
171
172 fn expanded_range(&self) -> (f64, f64) {
173 let range = self.max - self.min;
174 let (ml, al, mu, au) = self.expand;
175 (self.min - range * ml - al, self.max + range * mu + au)
176 }
177
178 pub fn is_opposite(&self) -> bool {
180 self.position_opposite
181 }
182}
183
184impl Default for ScaleContinuous {
185 fn default() -> Self {
186 Self::new()
187 }
188}
189
190impl Scale for ScaleContinuous {
191 fn set_default_locale(&mut self, locale: &crate::locale::Locale) {
192 if self.locale.is_none() {
193 self.locale = Some(locale.clone());
194 }
195 }
196
197 fn locale(&self) -> Option<&crate::locale::Locale> {
198 self.locale.as_ref()
199 }
200
201 fn aesthetic(&self) -> Aesthetic {
202 self.aesthetic.clone()
203 }
204
205 fn train(&mut self, values: &[Value]) {
206 for v in values {
207 if let Some(f) = v.as_f64() {
208 if f.is_finite() {
209 if f < self.min {
210 self.min = f;
211 }
212 if f > self.max {
213 self.max = f;
214 }
215 }
216 }
217 }
218 self.trained = true;
219 }
220
221 fn map(&self, value: &Value) -> f64 {
222 let f = match value.as_f64() {
223 Some(f) => f,
224 None => return 0.0,
225 };
226 if f == f64::INFINITY {
228 return 1.0;
229 }
230 if f == f64::NEG_INFINITY {
231 return 0.0;
232 }
233 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
236 return 0.5;
237 }
238 let (emin, emax) = self.expanded_range();
239 let range = emax - emin;
240 if range.abs() < f64::EPSILON {
241 0.5
242 } else {
243 (f - emin) / range
244 }
245 }
246
247 fn expanded_domain(&self) -> Option<(f64, f64)> {
248 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
249 return None;
250 }
251 let (a, b) = self.expanded_range();
252 if !(a.is_finite() && b.is_finite()) {
253 return None;
254 }
255 if (b - a).abs() < f64::EPSILON {
256 return Some((a - 0.5, b + 0.5));
258 }
259 Some((a, b))
260 }
261
262 fn breaks(&self) -> Vec<(f64, String)> {
263 if !self.trained || self.min > self.max {
264 return vec![];
265 }
266
267 if let Some(ref custom) = self.custom_breaks {
269 let localized = self
270 .localized_labels
271 .as_ref()
272 .map(|f| f(self.locale.as_ref()));
273 let custom_labels = localized.as_ref().or(self.custom_labels.as_ref());
274 return custom
275 .iter()
276 .enumerate()
277 .map(|(i, &v)| {
278 let pos = self.map(&Value::Float(v));
279 let label = if let Some(labels) = custom_labels {
280 labels
281 .get(i)
282 .cloned()
283 .unwrap_or_else(|| self.format_label(v))
284 } else {
285 self.format_label(self.scale_transform.inverse(v))
286 };
287 (pos, label)
288 })
289 .collect();
290 }
291
292 if super::util::is_degenerate_range(self.min, self.max) {
293 let label = self.format_label(self.scale_transform.inverse(self.min));
294 return vec![(0.5, label)];
295 }
296
297 let (emin, emax) = self.expanded_range();
301 let tol = (emax - emin).abs() * 1e-9;
302 let breaks: Vec<f64> = extended_breaks(self.min, self.max, 5)
303 .into_iter()
304 .filter(|&v| v >= emin - tol && v <= emax + tol)
305 .collect();
306 let shown: Vec<f64> = breaks
307 .iter()
308 .map(|&v| self.scale_transform.inverse(v))
309 .collect();
310 let labels: Vec<String> = match &self.label_formatter {
313 Some(f) => shown.iter().map(|&v| f(v)).collect(),
314 None => super::util::format_numbers_opt(&shown, self.locale.as_ref()),
315 };
316 breaks
317 .iter()
318 .zip(labels)
319 .map(|(&v, label)| (self.map(&Value::Float(v)), label))
320 .collect()
321 }
322
323 fn name(&self) -> &str {
324 &self.name
325 }
326
327 fn set_name(&mut self, name: &str) {
328 self.name = name.to_string();
329 }
330
331 fn transform(&self, value: &Value) -> Value {
332 self.scale_transform.transform_value(value)
333 }
334
335 fn sec_axis(&self) -> Option<&SecAxis> {
336 self.sec_axis.as_ref()
337 }
338
339 fn set_limits(&mut self, min: f64, max: f64) {
340 self.min = min;
341 self.max = max;
342 self.trained = true;
343 }
344
345 fn filter_limits(&self) -> Option<(f64, f64)> {
346 if self.filter_oob && self.trained {
347 Some((self.min, self.max))
348 } else {
349 None
350 }
351 }
352
353 fn domain(&self) -> Option<(f64, f64)> {
354 if self.trained {
355 Some((self.min, self.max))
356 } else {
357 None
358 }
359 }
360
361 fn axis_position_opposite(&self) -> bool {
362 self.position_opposite
363 }
364
365 fn clone_box(&self) -> Box<dyn Scale> {
366 Box::new(self.clone())
367 }
368
369 fn reset_training(&mut self) {
370 self.min = f64::INFINITY;
371 self.max = f64::NEG_INFINITY;
372 self.trained = false;
373 }
374}