use crate::rect::{self, Rectangle};
pub fn fit(view: Rectangle, agent: (f32, f32), anchors: &[Rectangle]) -> Option<Rectangle> {
let mut free_regions = vec![view];
for &anchor in anchors {
let mut updated = Vec::new();
for region in &free_regions {
updated.extend(rect::boolean::subtract(*region, anchor));
}
free_regions = updated;
}
free_regions.retain(|r| r.width >= agent.0 && r.height >= agent.1);
if free_regions.is_empty() {
return None;
}
free_regions.sort_by(|a, b| {
a.y.partial_cmp(&b.y)
.unwrap()
.then(a.x.partial_cmp(&b.x).unwrap())
});
let chosen = free_regions[0];
Some(Rectangle {
x: chosen.x,
y: chosen.y,
width: agent.0,
height: agent.1,
})
}
pub mod ext {
use super::*;
pub fn walk_to_fit(view: Rectangle, agent: (f32, f32), anchors: &[Rectangle]) -> Rectangle {
if let Some(r) = super::fit(view, agent, anchors) {
return r;
}
let step = (agent.0.min(agent.1) / 2.0).max(1.0);
let mut radius = step;
loop {
let max = radius as i32;
for dy in (0..=max).map(|v| v as f32).step_by(step as usize) {
for dx in (0..=max).map(|v| v as f32).step_by(step as usize) {
let candidate = Rectangle {
x: view.x + dx,
y: view.y + dy,
width: agent.0,
height: agent.1,
};
if !anchors.iter().any(|a| rect::intersects(&candidate, a)) {
return candidate;
}
}
}
radius += step;
}
}
}