ass_renderer/collision/
resolver.rs1#[cfg(feature = "nostd")]
4use alloc::vec::Vec;
5#[cfg(not(feature = "nostd"))]
6use std::vec::Vec;
7
8use super::{BoundingBox, CollisionDetector, PositionedEvent};
9
10pub struct CollisionResolver {
12 #[allow(dead_code)] screen_width: f32,
14 screen_height: f32,
15 positioned_events: Vec<PositionedEvent>,
16 collision_margin: f32,
17}
18
19impl CollisionResolver {
20 pub fn new(screen_width: f32, screen_height: f32) -> Self {
22 Self {
23 screen_width,
24 screen_height,
25 positioned_events: Vec::new(),
26 collision_margin: 2.0, }
28 }
29
30 pub fn clear(&mut self) {
32 self.positioned_events.clear();
33 }
34
35 pub fn set_collision_margin(&mut self, margin: f32) {
37 self.collision_margin = margin;
38 }
39
40 pub fn add_fixed(&mut self, event: PositionedEvent) {
42 self.positioned_events.push(event);
43 }
44
45 pub fn find_position(&mut self, mut event: PositionedEvent) -> BoundingBox {
52 let margin = self.collision_margin;
53 let push_down = !matches!(event.alignment, 1..=3);
54 let mut bbox = event.bbox;
55
56 for _ in 0..=self.positioned_events.len() {
59 let overlapping: Vec<BoundingBox> = self
60 .positioned_events
61 .iter()
62 .filter(|e| {
63 e.layer == event.layer && e.bbox.expand(margin).intersects(&bbox.expand(margin))
64 })
65 .map(|e| e.bbox)
66 .collect();
67 if overlapping.is_empty() {
68 break;
69 }
70 bbox.y = if push_down {
71 overlapping
72 .iter()
73 .map(|b| b.y + b.height)
74 .fold(f32::MIN, f32::max)
75 + margin * 2.0
76 } else {
77 overlapping.iter().map(|b| b.y).fold(f32::MAX, f32::min)
78 - bbox.height
79 - margin * 2.0
80 };
81 }
82
83 let max_y = (self.screen_height - bbox.height).max(0.0);
85 bbox.y = bbox.y.clamp(0.0, max_y);
86
87 event.bbox = bbox;
88 self.positioned_events.push(event);
89 bbox
90 }
91
92 pub fn positioned_events(&self) -> &[PositionedEvent] {
94 &self.positioned_events
95 }
96}
97
98pub struct SmartCollisionResolver {
100 resolver: CollisionResolver,
101 priority_threshold: i32,
102}
103
104impl SmartCollisionResolver {
105 pub fn new(screen_width: f32, screen_height: f32) -> Self {
107 Self {
108 resolver: CollisionResolver::new(screen_width, screen_height),
109 priority_threshold: 100,
110 }
111 }
112
113 pub fn process_events(&mut self, events: &mut [PositionedEvent]) {
115 events.sort_by_key(|e| -e.priority);
117
118 self.resolver.clear();
119
120 for event in events.iter_mut() {
121 if event.priority >= self.priority_threshold {
122 self.resolver.add_fixed(event.clone());
124 } else {
125 let new_bbox = self.resolver.find_position(event.clone());
127 event.bbox = new_bbox;
128 }
129 }
130 }
131
132 pub fn set_priority_threshold(&mut self, threshold: i32) {
134 self.priority_threshold = threshold;
135 }
136}
137
138impl CollisionDetector for SmartCollisionResolver {
139 fn check_collision(&self, bbox: &BoundingBox, existing: &[BoundingBox]) -> bool {
140 existing.iter().any(|e| bbox.intersects(e))
141 }
142
143 fn find_free_position(
144 &self,
145 bbox: &BoundingBox,
146 existing: &[BoundingBox],
147 bounds: &BoundingBox,
148 ) -> Option<(f32, f32)> {
149 let step = 10.0; for y_offset in (0..20).map(|i| i as f32 * step) {
154 let test_y = bbox.y + y_offset;
155 if test_y + bbox.height <= bounds.height {
156 let test_bbox = BoundingBox::new(bbox.x, test_y, bbox.width, bbox.height);
157 if !self.check_collision(&test_bbox, existing) {
158 return Some((bbox.x, test_y));
159 }
160 }
161 }
162
163 for y_offset in (1..20).map(|i| i as f32 * step) {
165 let test_y = bbox.y - y_offset;
166 if test_y >= 0.0 {
167 let test_bbox = BoundingBox::new(bbox.x, test_y, bbox.width, bbox.height);
168 if !self.check_collision(&test_bbox, existing) {
169 return Some((bbox.x, test_y));
170 }
171 }
172 }
173
174 None
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_collision_resolver() {
184 let mut resolver = CollisionResolver::new(1920.0, 1080.0);
185
186 let event1 = PositionedEvent {
187 bbox: BoundingBox::new(100.0, 900.0, 200.0, 50.0),
188 layer: 0,
189 margin_v: 10,
190 margin_l: 10,
191 margin_r: 10,
192 alignment: 2,
193 priority: 100,
194 };
195
196 let event2 = PositionedEvent {
197 bbox: BoundingBox::new(100.0, 900.0, 200.0, 50.0),
198 layer: 0,
199 margin_v: 10,
200 margin_l: 10,
201 margin_r: 10,
202 alignment: 2,
203 priority: 50,
204 };
205
206 resolver.add_fixed(event1);
207 let new_pos = resolver.find_position(event2);
208
209 assert_ne!(new_pos.y, 900.0);
211 }
212}