use itertools::Itertools;
use tui::{
style::Color,
widgets::{
GraphType,
canvas::{Line as CanvasLine, Points},
},
};
use super::{Context, Data, Point, TimeChart};
impl TimeChart<'_> {
pub(crate) fn draw_points(&self, ctx: &mut Context<'_>) {
for dataset in &self.datasets {
let Data::Some { times, values } = dataset.data else {
continue;
};
let Some(current_time) = times.last() else {
continue;
};
let color = dataset.style.fg.unwrap_or(Color::Reset);
let left_edge = self.x_axis.bounds.get_bounds()[0];
for (curr, next) in values
.iter_along_base(times)
.rev()
.map(|(&time, &val)| {
let from_start = -(current_time.duration_since(time).as_millis() as f64);
(from_start, self.scaling.scale(val))
})
.tuple_windows()
{
if curr.0 == left_edge {
ctx.draw(&Points {
coords: &[curr],
color,
});
break;
} else if next.0 < left_edge {
let interpolated = interpolate_point(&next, &curr, left_edge);
ctx.draw(&CanvasLine {
x1: curr.0,
y1: curr.1,
x2: left_edge,
y2: interpolated,
color,
});
break;
} else {
if let GraphType::Line = dataset.graph_type {
ctx.draw(&CanvasLine {
x1: curr.0,
y1: curr.1,
x2: next.0,
y2: next.1,
color,
});
} else {
ctx.draw(&Points {
coords: &[curr],
color,
});
}
}
}
}
}
}
fn interpolate_point(older_point: &Point, newer_point: &Point, x: f64) -> f64 {
let delta_x = newer_point.0 - older_point.0;
let delta_y = newer_point.1 - older_point.1;
let slope = delta_y / delta_x;
(older_point.1 + (x - older_point.0) * slope).max(0.0)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn time_graph_test_interpolation() {
let data = [(-3.0, 8.0), (-1.0, 6.0), (0.0, 5.0)];
assert_eq!(interpolate_point(&data[1], &data[2], 0.0), 5.0);
assert_eq!(interpolate_point(&data[1], &data[2], -0.25), 5.25);
assert_eq!(interpolate_point(&data[1], &data[2], -0.5), 5.5);
assert_eq!(interpolate_point(&data[0], &data[1], -1.0), 6.0);
assert_eq!(interpolate_point(&data[0], &data[1], -1.5), 6.5);
assert_eq!(interpolate_point(&data[0], &data[1], -2.0), 7.0);
assert_eq!(interpolate_point(&data[0], &data[1], -2.5), 7.5);
assert_eq!(interpolate_point(&data[0], &data[1], -3.0), 8.0);
}
}