use retroglyph_core::{Pos, Rect};
#[derive(Debug, Clone)]
pub struct HitTester<Id> {
hits: Vec<(Rect, Id)>,
}
impl<Id> HitTester<Id> {
#[must_use]
pub const fn new() -> Self {
Self { hits: Vec::new() }
}
pub fn push(&mut self, rect: Rect, id: Id) {
self.hits.push((rect, id));
}
pub fn clear(&mut self) {
self.hits.clear();
}
#[must_use]
pub const fn len(&self) -> usize {
self.hits.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.hits.is_empty()
}
}
impl<Id: Copy> HitTester<Id> {
#[must_use]
pub fn topmost_at(&self, pos: Pos) -> Option<Id> {
self.hits
.iter()
.rev()
.find(|(rect, _)| rect.contains_pos(pos))
.map(|&(_, id)| id)
}
}
impl<Id> Default for HitTester<Id> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn topmost_at_prefers_the_most_recently_pushed_overlap() {
let mut hits = HitTester::new();
hits.push(Rect::new(0, 0, 10, 10), "back");
hits.push(Rect::new(5, 5, 10, 10), "front");
assert_eq!(hits.topmost_at(Pos::new(6, 6)), Some("front")); assert_eq!(hits.topmost_at(Pos::new(1, 1)), Some("back")); assert_eq!(hits.topmost_at(Pos::new(20, 20)), None); }
#[test]
fn clear_empties_the_registry() {
let mut hits = HitTester::new();
hits.push(Rect::new(0, 0, 5, 5), 1);
assert!(!hits.is_empty());
hits.clear();
assert!(hits.is_empty());
assert_eq!(hits.len(), 0);
assert_eq!(hits.topmost_at(Pos::new(0, 0)), None);
}
#[test]
fn default_is_empty() {
let hits: HitTester<()> = HitTester::default();
assert!(hits.is_empty());
}
}