#[derive(Debug, PartialEq, Clone, Copy)]
pub struct DataPoint {
pub x: f64,
pub y: f64,
}
impl DataPoint {
pub fn new(x: f64, y: f64) -> Self {
DataPoint { x, y }
}
}
pub fn lttb(data: Vec<DataPoint>, threshold: usize) -> Vec<DataPoint> {
if threshold >= data.len() || threshold == 0 {
return data;
}
let mut sampled = Vec::with_capacity(threshold);
let every = ((data.len() - 2) as f64) / ((threshold - 2) as f64);
let mut a = 0;
sampled.push(data[a]);
for i in 0..threshold - 2 {
let mut avg_x = 0f64;
let mut avg_y = 0f64;
let avg_range_start = (((i + 1) as f64) * every) as usize + 1;
let mut end = (((i + 2) as f64) * every) as usize + 1;
if end >= data.len() {
end = data.len();
}
let avg_range_end = end;
let avg_range_length = (avg_range_end - avg_range_start) as f64;
for i in 0..(avg_range_end - avg_range_start) {
let idx = (avg_range_start + i) as usize;
avg_x += data[idx].x;
avg_y += data[idx].y;
}
avg_x /= avg_range_length;
avg_y /= avg_range_length;
let range_offs = ((i as f64) * every) as usize + 1;
let range_to = (((i + 1) as f64) * every) as usize + 1;
let point_a_x = data[a].x;
let point_a_y = data[a].y;
let mut max_area = -1f64;
let mut next_a = range_offs;
for i in 0..(range_to - range_offs) {
let idx = (range_offs + i) as usize;
let area = ((point_a_x - avg_x) * (data[idx].y - point_a_y)
- (point_a_x - data[idx].x) * (avg_y - point_a_y))
.abs()
* 0.5;
if area > max_area {
max_area = area;
next_a = idx; }
}
sampled.push(data[next_a]); a = next_a; }
sampled.push(data[data.len() - 1]);
sampled
}
#[cfg(test)]
mod tests {
use super::{lttb, DataPoint};
#[test]
fn lttb_test() {
let mut dps = vec![];
dps.push(DataPoint::new(0.0, 10.0));
dps.push(DataPoint::new(1.0, 12.0));
dps.push(DataPoint::new(2.0, 8.0));
dps.push(DataPoint::new(3.0, 10.0));
dps.push(DataPoint::new(4.0, 12.0));
let mut expected = vec![];
expected.push(DataPoint::new(0.0, 10.0));
expected.push(DataPoint::new(2.0, 8.0));
expected.push(DataPoint::new(4.0, 12.0));
assert_eq!(expected, lttb(dps, 3));
}
}