1use crate::aes::Aesthetic;
2use crate::data::Value;
3
4use super::util::format_number;
5use super::Scale;
6
7#[derive(Clone, Debug)]
9pub struct ScaleSizeContinuous {
10 name: String,
11 min: f64,
12 max: f64,
13 trained: bool,
14 range: (f64, f64),
16}
17
18impl ScaleSizeContinuous {
19 pub fn new() -> Self {
20 ScaleSizeContinuous {
21 name: String::new(),
22 min: f64::INFINITY,
23 max: f64::NEG_INFINITY,
24 trained: false,
25 range: (1.0, 6.0),
26 }
27 }
28
29 pub fn with_range(mut self, min: f64, max: f64) -> Self {
30 self.range = (min, max);
31 self
32 }
33
34 pub fn with_name(mut self, name: &str) -> Self {
35 self.name = name.to_string();
36 self
37 }
38}
39
40impl Default for ScaleSizeContinuous {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl Scale for ScaleSizeContinuous {
47 fn aesthetic(&self) -> Aesthetic {
48 Aesthetic::Size
49 }
50
51 fn train(&mut self, values: &[Value]) {
52 for v in values {
53 if let Some(f) = v.as_f64() {
54 if f.is_finite() {
55 if f < self.min {
56 self.min = f;
57 }
58 if f > self.max {
59 self.max = f;
60 }
61 }
62 }
63 }
64 self.trained = true;
65 }
66
67 fn map(&self, value: &Value) -> f64 {
68 let f = match value.as_f64() {
69 Some(f) => f,
70 None => return 0.0,
71 };
72 let range = self.max - self.min;
73 if range.abs() < f64::EPSILON {
74 0.5
75 } else {
76 ((f - self.min) / range).clamp(0.0, 1.0)
77 }
78 }
79
80 fn breaks(&self) -> Vec<(f64, String)> {
81 if !self.trained || self.min > self.max {
82 return vec![];
83 }
84 let range = self.max - self.min;
85 if range.abs() < f64::EPSILON {
86 return vec![(0.5, format_number(self.min))];
87 }
88 let n_breaks = 4;
89 let step = super::util::nice_step(range / n_breaks as f64);
90 let start = (self.min / step).ceil() * step;
91 let mut breaks = Vec::new();
92 let mut v = start;
93 while v <= self.max + step * 0.001 {
94 let pos = self.map(&Value::Float(v));
95 breaks.push((pos, format_number(v)));
96 v += step;
97 }
98 breaks
99 }
100
101 fn name(&self) -> &str {
102 &self.name
103 }
104
105 fn set_name(&mut self, name: &str) {
106 self.name = name.to_string();
107 }
108
109 fn map_to_size(&self, value: &Value) -> Option<f64> {
110 let t = self.map(value);
111 let (lo, hi) = self.range;
112 Some(lo + t * (hi - lo))
113 }
114
115 fn clone_box(&self) -> Box<dyn Scale> {
116 Box::new(self.clone())
117 }
118
119 fn reset_training(&mut self) {
120 self.min = f64::INFINITY;
121 self.max = f64::NEG_INFINITY;
122 self.trained = false;
123 }
124}