use crate::core::data::DataPoint;
#[derive(Debug, Clone, PartialEq)]
pub enum Selection {
Point {
indices: Vec<usize>,
},
Interval {
x: (f64, f64),
y: Option<(f64, f64)>,
},
Multi {
selections: Vec<Selection>,
},
}
impl Selection {
pub fn point(indices: Vec<usize>) -> Self {
Self::Point { indices }
}
pub fn interval_x(x_min: f64, x_max: f64) -> Self {
let (x_min, x_max) = if x_min <= x_max {
(x_min, x_max)
} else {
(x_max, x_min)
};
Self::Interval {
x: (x_min, x_max),
y: None,
}
}
pub fn interval_xy(x_min: f64, x_max: f64, y_min: f64, y_max: f64) -> Self {
let (x_min, x_max) = if x_min <= x_max {
(x_min, x_max)
} else {
(x_max, x_min)
};
let (y_min, y_max) = if y_min <= y_max {
(y_min, y_max)
} else {
(y_max, y_min)
};
Self::Interval {
x: (x_min, x_max),
y: Some((y_min, y_max)),
}
}
pub fn is_empty(&self) -> bool {
match self {
Self::Point { indices } => indices.is_empty(),
Self::Interval { x, .. } => (x.1 - x.0).abs() < f64::EPSILON,
Self::Multi { selections } => selections.is_empty(),
}
}
pub fn contains_point(&self, point: &DataPoint, index: usize) -> bool {
match self {
Self::Point { indices } => indices.contains(&index),
Self::Interval { x, y } => {
let in_x = point.x >= x.0 && point.x <= x.1;
let in_y = y.is_none_or(|(y_min, y_max)| point.y >= y_min && point.y <= y_max);
in_x && in_y
}
Self::Multi { selections } => selections.iter().any(|s| s.contains_point(point, index)),
}
}
}
pub fn filter_by_selection(data: &[DataPoint], selection: &Selection) -> Vec<DataPoint> {
data.iter()
.enumerate()
.filter(|(i, p)| selection.contains_point(p, *i))
.map(|(_, p)| *p)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_data() -> Vec<DataPoint> {
vec![
DataPoint::new(1.0, 10.0),
DataPoint::new(2.0, 20.0),
DataPoint::new(3.0, 30.0),
DataPoint::new(4.0, 40.0),
DataPoint::new(5.0, 50.0),
]
}
#[test]
fn test_point_selection() {
let sel = Selection::point(vec![1, 3]);
let data = sample_data();
assert!(sel.contains_point(&data[1], 1));
assert!(sel.contains_point(&data[3], 3));
assert!(!sel.contains_point(&data[0], 0));
}
#[test]
fn test_interval_x_selection() {
let sel = Selection::interval_x(2.0, 4.0);
let data = sample_data();
assert!(!sel.contains_point(&data[0], 0)); assert!(sel.contains_point(&data[1], 1)); assert!(sel.contains_point(&data[2], 2)); assert!(sel.contains_point(&data[3], 3)); assert!(!sel.contains_point(&data[4], 4)); }
#[test]
fn test_interval_xy_selection() {
let sel = Selection::interval_xy(2.0, 4.0, 15.0, 35.0);
let data = sample_data();
assert!(!sel.contains_point(&data[0], 0)); assert!(sel.contains_point(&data[1], 1)); assert!(sel.contains_point(&data[2], 2)); assert!(!sel.contains_point(&data[3], 3)); }
#[test]
fn test_interval_auto_sort() {
let sel = Selection::interval_x(5.0, 2.0);
let data = sample_data();
assert!(sel.contains_point(&data[2], 2)); }
#[test]
fn test_filter_by_selection() {
let data = sample_data();
let sel = Selection::interval_x(2.0, 4.0);
let filtered = filter_by_selection(&data, &sel);
assert_eq!(filtered.len(), 3);
assert_eq!(filtered[0].x, 2.0);
assert_eq!(filtered[1].x, 3.0);
assert_eq!(filtered[2].x, 4.0);
}
#[test]
fn test_multi_selection() {
let sel = Selection::Multi {
selections: vec![Selection::point(vec![0]), Selection::interval_x(4.0, 5.0)],
};
let data = sample_data();
assert!(sel.contains_point(&data[0], 0));
assert!(!sel.contains_point(&data[1], 1));
assert!(sel.contains_point(&data[3], 3));
assert!(sel.contains_point(&data[4], 4));
}
#[test]
fn test_empty_selection() {
assert!(Selection::point(vec![]).is_empty());
assert!(Selection::interval_x(3.0, 3.0).is_empty());
assert!(Selection::Multi { selections: vec![] }.is_empty());
}
#[test]
fn test_filter_empty_result() {
let data = sample_data();
let sel = Selection::interval_x(100.0, 200.0);
let filtered = filter_by_selection(&data, &sel);
assert!(filtered.is_empty());
}
}