#[cfg(feature = "nostd")]
use alloc::vec::Vec;
#[cfg(not(feature = "nostd"))]
use std::vec::Vec;
use super::{BoundingBox, CollisionDetector, PositionedEvent};
pub struct CollisionResolver {
#[allow(dead_code)] screen_width: f32,
screen_height: f32,
positioned_events: Vec<PositionedEvent>,
collision_margin: f32,
}
impl CollisionResolver {
pub fn new(screen_width: f32, screen_height: f32) -> Self {
Self {
screen_width,
screen_height,
positioned_events: Vec::new(),
collision_margin: 2.0, }
}
pub fn clear(&mut self) {
self.positioned_events.clear();
}
pub fn set_collision_margin(&mut self, margin: f32) {
self.collision_margin = margin;
}
pub fn add_fixed(&mut self, event: PositionedEvent) {
self.positioned_events.push(event);
}
pub fn find_position(&mut self, mut event: PositionedEvent) -> BoundingBox {
let margin = self.collision_margin;
let push_down = !matches!(event.alignment, 1..=3);
let mut bbox = event.bbox;
for _ in 0..=self.positioned_events.len() {
let overlapping: Vec<BoundingBox> = self
.positioned_events
.iter()
.filter(|e| {
e.layer == event.layer && e.bbox.expand(margin).intersects(&bbox.expand(margin))
})
.map(|e| e.bbox)
.collect();
if overlapping.is_empty() {
break;
}
bbox.y = if push_down {
overlapping
.iter()
.map(|b| b.y + b.height)
.fold(f32::MIN, f32::max)
+ margin * 2.0
} else {
overlapping.iter().map(|b| b.y).fold(f32::MAX, f32::min)
- bbox.height
- margin * 2.0
};
}
let max_y = (self.screen_height - bbox.height).max(0.0);
bbox.y = bbox.y.clamp(0.0, max_y);
event.bbox = bbox;
self.positioned_events.push(event);
bbox
}
pub fn positioned_events(&self) -> &[PositionedEvent] {
&self.positioned_events
}
}
pub struct SmartCollisionResolver {
resolver: CollisionResolver,
priority_threshold: i32,
}
impl SmartCollisionResolver {
pub fn new(screen_width: f32, screen_height: f32) -> Self {
Self {
resolver: CollisionResolver::new(screen_width, screen_height),
priority_threshold: 100,
}
}
pub fn process_events(&mut self, events: &mut [PositionedEvent]) {
events.sort_by_key(|e| -e.priority);
self.resolver.clear();
for event in events.iter_mut() {
if event.priority >= self.priority_threshold {
self.resolver.add_fixed(event.clone());
} else {
let new_bbox = self.resolver.find_position(event.clone());
event.bbox = new_bbox;
}
}
}
pub fn set_priority_threshold(&mut self, threshold: i32) {
self.priority_threshold = threshold;
}
}
impl CollisionDetector for SmartCollisionResolver {
fn check_collision(&self, bbox: &BoundingBox, existing: &[BoundingBox]) -> bool {
existing.iter().any(|e| bbox.intersects(e))
}
fn find_free_position(
&self,
bbox: &BoundingBox,
existing: &[BoundingBox],
bounds: &BoundingBox,
) -> Option<(f32, f32)> {
let step = 10.0;
for y_offset in (0..20).map(|i| i as f32 * step) {
let test_y = bbox.y + y_offset;
if test_y + bbox.height <= bounds.height {
let test_bbox = BoundingBox::new(bbox.x, test_y, bbox.width, bbox.height);
if !self.check_collision(&test_bbox, existing) {
return Some((bbox.x, test_y));
}
}
}
for y_offset in (1..20).map(|i| i as f32 * step) {
let test_y = bbox.y - y_offset;
if test_y >= 0.0 {
let test_bbox = BoundingBox::new(bbox.x, test_y, bbox.width, bbox.height);
if !self.check_collision(&test_bbox, existing) {
return Some((bbox.x, test_y));
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collision_resolver() {
let mut resolver = CollisionResolver::new(1920.0, 1080.0);
let event1 = PositionedEvent {
bbox: BoundingBox::new(100.0, 900.0, 200.0, 50.0),
layer: 0,
margin_v: 10,
margin_l: 10,
margin_r: 10,
alignment: 2,
priority: 100,
};
let event2 = PositionedEvent {
bbox: BoundingBox::new(100.0, 900.0, 200.0, 50.0),
layer: 0,
margin_v: 10,
margin_l: 10,
margin_r: 10,
alignment: 2,
priority: 50,
};
resolver.add_fixed(event1);
let new_pos = resolver.find_position(event2);
assert_ne!(new_pos.y, 900.0);
}
}