use super::axis_options::AxisOptions;
use crate::geometry::Range;
use crate::time::{TimeSpan, TimeStamp};
#[derive(Default, Clone)]
pub struct Axis {
pub options: AxisOptions,
pub label: Option<String>,
range: Range<f64>,
}
impl Axis {
pub fn set_limits(&mut self, begin: f64, end: f64) {
self.range.set_begin(begin);
self.range.set_end(end);
}
pub fn begin(&self) -> f64 {
self.range.begin()
}
pub fn end(&self) -> f64 {
self.range.end()
}
pub fn timespan(&self) -> TimeSpan {
TimeSpan::new(TimeStamp::new(self.begin()), TimeStamp::new(self.end()))
}
pub fn domain(&self) -> f64 {
self.end() - self.begin()
}
fn calc_tick_spacing(&self, n_ticks: usize) -> f64 {
let domain: f64 = self.domain();
let scale = domain.log10().floor();
let approx = (10.0_f64).powf(-scale) * domain / (n_ticks as f64);
let options = vec![0.1, 0.2, 0.5, 1.0, 2.0, 5.0];
let best = options
.iter()
.min_by_key(|x| (((*x - approx).abs() * 1_000_000.0) as i64))
.unwrap();
trace!(
"domain: {}, Scale {}, approx: {}, best: {}",
domain,
scale,
approx,
best
);
let tick_width = best * (10.0_f64).powf(scale);
tick_width
}
pub fn calc_tiks(&self, n_ticks: usize) -> Vec<(f64, String)> {
trace!("Calculating ticks!");
let tick_step = self.calc_tick_spacing(n_ticks);
trace!("tickz {:?}", tick_step);
let mut x = ceil_to_multiple_of(self.range.begin(), tick_step);
let mut res = vec![];
while x < self.range.end() {
let label = x.to_string();
res.push((x, label));
x += tick_step;
}
trace!("Ticks: {:?}", res);
res
}
}
fn ceil_to_multiple_of(x: f64, step: f64) -> f64 {
let offset = x % step;
if offset > 0.0 {
x + step - offset
} else if offset < 0.0 {
x - offset
} else {
x
}
}
#[cfg(test)]
mod tests {
use super::Axis;
#[test]
fn tick_calculation() {
let mut axis = Axis::default();
axis.set_limits(12.0, 87.0);
let ticks = axis.calc_tiks(7);
assert_eq!(
vec![
(20.0, "20".to_string()),
(30.0, "30".to_string()),
(40.0, "40".to_string()),
(50.0, "50".to_string()),
(60.0, "60".to_string()),
(70.0, "70".to_string()),
(80.0, "80".to_string()),
],
ticks
);
}
#[test]
fn tick_calculation_negative() {
let mut axis = Axis::default();
axis.set_limits(-44.0, 46.0);
let ticks = axis.calc_tiks(7);
assert_eq!(
vec![
(-40.0, "-40".to_string()),
(-30.0, "-30".to_string()),
(-20.0, "-20".to_string()),
(-10.0, "-10".to_string()),
(0.0, "0".to_string()),
(10.0, "10".to_string()),
(20.0, "20".to_string()),
(30.0, "30".to_string()),
(40.0, "40".to_string()),
],
ticks
);
}
}