use super::event::{EventArc, EventEdge};
use hifitime::{Epoch, Unit};
use std::cmp::Ordering;
#[cfg(feature = "python")]
use pyo3::pyfunction;
#[derive(Debug, PartialEq, Eq)]
struct EventPoint {
epoch: Epoch,
kind: EventEdge,
list_id: usize, }
impl Ord for EventPoint {
fn cmp(&self, other: &Self) -> Ordering {
self.epoch.cmp(&other.epoch).then_with(|| {
self.kind.cmp(&other.kind)
})
}
}
impl PartialOrd for EventPoint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg_attr(feature = "python", pyfunction)]
pub fn find_arc_intersections(timelines: Vec<Vec<EventArc>>) -> Vec<(Epoch, Epoch)> {
let num_timelines = timelines.len();
if num_timelines == 0 {
return Vec::new();
}
let mut all_points: Vec<EventPoint> = Vec::new();
for (list_id, arcs) in timelines.iter().enumerate() {
for arc in arcs {
all_points.push(EventPoint {
epoch: arc.rise.orbit.epoch, kind: EventEdge::Rising,
list_id,
});
all_points.push(EventPoint {
epoch: arc.fall.orbit.epoch,
kind: EventEdge::Falling,
list_id,
});
}
}
all_points.sort();
let mut result_windows: Vec<(Epoch, Epoch)> = Vec::new();
let mut intersection_start: Option<Epoch> = None;
let mut active_state: Vec<bool> = vec![false; num_timelines];
for point in all_points {
let current_epoch = point.epoch;
let was_fully_intersecting = active_state.iter().all(|&is_active| is_active);
match point.kind {
EventEdge::Rising => active_state[point.list_id] = true,
EventEdge::Falling => active_state[point.list_id] = false,
_ => unreachable!(),
}
let is_fully_intersecting = active_state.iter().all(|&is_active| is_active);
if !was_fully_intersecting && is_fully_intersecting {
intersection_start = Some(current_epoch);
} else if was_fully_intersecting && !is_fully_intersecting {
if let Some(start_epoch) = intersection_start.take() {
if current_epoch > start_epoch + Unit::Millisecond * 1 {
result_windows.push((start_epoch, current_epoch));
}
}
}
}
result_windows
}