use std::collections::HashMap;
type EventId = String;
pub struct Ev<'a> {
id: &'a EventId,
start: i64,
end: i64,
}
impl<'a> From<(&'a EventId, i64, i64)> for Ev<'a> {
fn from((id, start, end): (&'a EventId, i64, i64)) -> Self {
Ev { id, start, end }
}
}
#[derive(Default)]
pub struct Layout {
layout: HashMap<EventId, [f32; 2]>,
}
impl Layout {
fn from_map(layout: HashMap<EventId, [f32; 2]>) -> Self {
Self { layout }
}
pub fn query(&self, id: &EventId) -> Option<[f32; 2]> {
self.layout.get(id).cloned()
}
pub fn merge(&mut self, other: Layout) {
self.layout.extend(other.layout)
}
}
pub trait LayoutAlgorithm {
fn compute(events: Vec<Ev<'_>>) -> Layout;
}
pub struct NaiveAlgorithm;
impl LayoutAlgorithm for NaiveAlgorithm {
fn compute(mut events: Vec<Ev<'_>>) -> Layout {
use intervaltree::{Element, IntervalTree};
let mut columns: HashMap<&EventId, (usize, usize)> = HashMap::new();
events.sort_by_key(|e| e.start - e.end);
let tree: IntervalTree<i64, &String> =
events.iter().map(|e| (e.start..e.end, e.id)).collect();
for e in events {
if columns.contains_key(e.id) {
continue;
}
let range = e.start..e.end;
let mut siblings: Vec<_> = tree.query(range).collect();
siblings.sort_by_key(|e| e.range.start);
let num_cols = siblings.len();
for (n, Element { value: id, .. }) in siblings.into_iter().enumerate() {
columns.entry(*id).or_insert((n, num_cols));
}
}
let mut layout = HashMap::new();
for Element { value: id, .. } in tree {
let (col0, tcol) = columns.get(&id).unwrap();
let t0 = *col0 as f32 / *tcol as f32;
let t1 = (*col0 + 1) as f32 / *tcol as f32;
layout.insert(id.clone(), [t0, t1]);
}
Layout::from_map(layout)
}
}
pub struct MarkusAlgorithm;
impl LayoutAlgorithm for MarkusAlgorithm {
fn compute(mut events: Vec<Ev<'_>>) -> Layout {
events.sort_by_key(|e| e.start);
let ev_map: HashMap<_, _> = events.iter().map(|e| (e.id, e)).collect();
let mut groups: Vec<(HashMap<&EventId, usize>, usize)> = vec![];
let mut group: HashMap<&EventId, usize> = Default::default();
let mut group_width: usize = 1;
for event in events.iter() {
if group.is_empty() {
group.insert(event.id, 0);
continue;
}
let overlapped = group.iter().any(|(id, _c)| overlaps(event, ev_map[id]));
if !overlapped {
groups.push((group, group_width));
group = Default::default();
group_width = 1;
group.insert(event.id, 0);
continue;
}
for col in 0..=group_width {
let slot_used = group
.iter()
.filter(|(_, &c)| c == col)
.any(|(id, _c)| overlaps(event, ev_map[id]));
if slot_used {
continue;
}
group.insert(event.id, col);
group_width = group_width.max(col + 1);
break;
}
}
if !group.is_empty() {
groups.push((group, group_width));
}
let mut layout = HashMap::new();
for (group, width) in groups {
for (id, col) in group {
let x0 = col as f32 / width as f32;
let x1 = (col + 1) as f32 / width as f32;
layout.insert(id.clone(), [x0, x1]);
}
}
Layout::from_map(layout)
}
}
fn overlaps(e1: &Ev, e2: &Ev) -> bool {
e1.start.max(e2.start) < e1.end.min(e2.end)
}