1use crate::aes::Aesthetic;
2use crate::data::Value;
3
4use super::format::LabelFormatter;
5use super::sec_axis::SecAxis;
6use super::transform::ScaleTransform;
7use super::util::{extended_breaks, format_number};
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, f64, f64),
21 position_opposite: bool,
24 pub(crate) scale_transform: ScaleTransform,
25 custom_breaks: Option<Vec<f64>>,
26 custom_labels: Option<Vec<String>>,
27 pub(crate) sec_axis: Option<SecAxis>,
28 label_formatter: Option<LabelFormatter>,
29}
30
31impl ScaleContinuous {
32 pub fn new() -> Self {
33 ScaleContinuous {
34 aesthetic: Aesthetic::X,
35 name: String::new(),
36 min: f64::INFINITY,
37 max: f64::NEG_INFINITY,
38 trained: false,
39 filter_oob: false,
40 expand: (0.05, 0.0, 0.05, 0.0),
41 position_opposite: false,
42 scale_transform: ScaleTransform::Identity,
43 custom_breaks: None,
44 custom_labels: None,
45 sec_axis: None,
46 label_formatter: None,
47 }
48 }
49
50 pub fn for_aesthetic(mut self, aes: Aesthetic) -> Self {
51 self.aesthetic = aes;
52 self
53 }
54
55 pub fn with_name(mut self, name: &str) -> Self {
56 self.name = name.to_string();
57 self
58 }
59
60 pub fn with_limits(mut self, min: f64, max: f64) -> Self {
61 self.min = min;
62 self.max = max;
63 self.trained = true;
64 self.filter_oob = true;
65 self
66 }
67
68 pub fn with_transform(mut self, transform: ScaleTransform) -> Self {
69 self.scale_transform = transform;
70 self
71 }
72
73 pub fn with_breaks(mut self, breaks: Vec<f64>) -> Self {
75 self.custom_breaks = Some(breaks);
76 self
77 }
78
79 pub fn with_labels(mut self, labels: Vec<String>) -> Self {
81 self.custom_labels = Some(labels);
82 self
83 }
84
85 pub fn with_expand(mut self, mult: f64, add: f64) -> Self {
88 self.expand = (mult, add, mult, add);
89 self
90 }
91
92 pub fn with_expand_sides(
95 mut self,
96 mult_lower: f64,
97 add_lower: f64,
98 mult_upper: f64,
99 add_upper: f64,
100 ) -> Self {
101 self.expand = (mult_lower, add_lower, mult_upper, add_upper);
102 self
103 }
104
105 pub fn with_position_opposite(mut self) -> Self {
107 self.position_opposite = true;
108 self
109 }
110
111 pub fn with_label_formatter<F>(mut self, f: F) -> Self
114 where
115 F: Fn(f64) -> String + Send + Sync + 'static,
116 {
117 self.label_formatter = Some(std::sync::Arc::new(f));
118 self
119 }
120
121 pub fn with_sec_axis(mut self, sec: SecAxis) -> Self {
123 self.sec_axis = Some(sec);
124 self
125 }
126
127 pub fn sec_axis(&self) -> Option<&SecAxis> {
129 self.sec_axis.as_ref()
130 }
131
132 fn format_label(&self, v: f64) -> String {
133 if let Some(f) = &self.label_formatter {
134 f(v)
135 } else {
136 format_number(v)
137 }
138 }
139
140 fn expanded_range(&self) -> (f64, f64) {
141 let range = self.max - self.min;
142 let (ml, al, mu, au) = self.expand;
143 (self.min - range * ml - al, self.max + range * mu + au)
144 }
145
146 pub fn is_opposite(&self) -> bool {
148 self.position_opposite
149 }
150}
151
152impl Default for ScaleContinuous {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158impl Scale for ScaleContinuous {
159 fn aesthetic(&self) -> Aesthetic {
160 self.aesthetic.clone()
161 }
162
163 fn train(&mut self, values: &[Value]) {
164 for v in values {
165 if let Some(f) = v.as_f64() {
166 if f.is_finite() {
167 if f < self.min {
168 self.min = f;
169 }
170 if f > self.max {
171 self.max = f;
172 }
173 }
174 }
175 }
176 self.trained = true;
177 }
178
179 fn map(&self, value: &Value) -> f64 {
180 let f = match value.as_f64() {
181 Some(f) => f,
182 None => return 0.0,
183 };
184 if f == f64::INFINITY {
186 return 1.0;
187 }
188 if f == f64::NEG_INFINITY {
189 return 0.0;
190 }
191 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
194 return 0.5;
195 }
196 let (emin, emax) = self.expanded_range();
197 let range = emax - emin;
198 if range.abs() < f64::EPSILON {
199 0.5
200 } else {
201 (f - emin) / range
202 }
203 }
204
205 fn expanded_domain(&self) -> Option<(f64, f64)> {
206 if !self.trained || !self.min.is_finite() || !self.max.is_finite() {
207 return None;
208 }
209 let (a, b) = self.expanded_range();
210 if !(a.is_finite() && b.is_finite()) {
211 return None;
212 }
213 if (b - a).abs() < f64::EPSILON {
214 return Some((a - 0.5, b + 0.5));
216 }
217 Some((a, b))
218 }
219
220 fn breaks(&self) -> Vec<(f64, String)> {
221 if !self.trained || self.min > self.max {
222 return vec![];
223 }
224
225 if let Some(ref custom) = self.custom_breaks {
227 return custom
228 .iter()
229 .enumerate()
230 .map(|(i, &v)| {
231 let pos = self.map(&Value::Float(v));
232 let label = if let Some(ref labels) = self.custom_labels {
233 labels
234 .get(i)
235 .cloned()
236 .unwrap_or_else(|| self.format_label(v))
237 } else {
238 self.format_label(self.scale_transform.inverse(v))
239 };
240 (pos, label)
241 })
242 .collect();
243 }
244
245 if super::util::is_degenerate_range(self.min, self.max) {
246 let label = self.format_label(self.scale_transform.inverse(self.min));
247 return vec![(0.5, label)];
248 }
249
250 let (emin, emax) = self.expanded_range();
254 let tol = (emax - emin).abs() * 1e-9;
255 let breaks: Vec<f64> = extended_breaks(self.min, self.max, 5)
256 .into_iter()
257 .filter(|&v| v >= emin - tol && v <= emax + tol)
258 .collect();
259 let shown: Vec<f64> = breaks
260 .iter()
261 .map(|&v| self.scale_transform.inverse(v))
262 .collect();
263 let labels: Vec<String> = match &self.label_formatter {
266 Some(f) => shown.iter().map(|&v| f(v)).collect(),
267 None => super::util::format_numbers(&shown),
268 };
269 breaks
270 .iter()
271 .zip(labels)
272 .map(|(&v, label)| (self.map(&Value::Float(v)), label))
273 .collect()
274 }
275
276 fn name(&self) -> &str {
277 &self.name
278 }
279
280 fn set_name(&mut self, name: &str) {
281 self.name = name.to_string();
282 }
283
284 fn transform(&self, value: &Value) -> Value {
285 self.scale_transform.transform_value(value)
286 }
287
288 fn sec_axis(&self) -> Option<&SecAxis> {
289 self.sec_axis.as_ref()
290 }
291
292 fn set_limits(&mut self, min: f64, max: f64) {
293 self.min = min;
294 self.max = max;
295 self.trained = true;
296 }
297
298 fn filter_limits(&self) -> Option<(f64, f64)> {
299 if self.filter_oob && self.trained {
300 Some((self.min, self.max))
301 } else {
302 None
303 }
304 }
305
306 fn domain(&self) -> Option<(f64, f64)> {
307 if self.trained {
308 Some((self.min, self.max))
309 } else {
310 None
311 }
312 }
313
314 fn axis_position_opposite(&self) -> bool {
315 self.position_opposite
316 }
317
318 fn clone_box(&self) -> Box<dyn Scale> {
319 Box::new(self.clone())
320 }
321
322 fn reset_training(&mut self) {
323 self.min = f64::INFINITY;
324 self.max = f64::NEG_INFINITY;
325 self.trained = false;
326 }
327}