use energyml_sheet::text;
pub const MAX_WIDTH_PX: f64 = 150.0;
pub const GAP_PX: f64 = 4.0;
pub fn width_px(text: &str, size_px: f64) -> f64 {
text::string_width_pt(text, size_px)
}
pub fn fit(line: &str, size_px: f64, max: f64) -> String {
if width_px(line, size_px) <= max {
return line.to_string();
}
let ellipsis = "…";
let mut out = String::new();
for ch in line.chars() {
let mut next = out.clone();
next.push(ch);
if width_px(&next, size_px) + width_px(ellipsis, size_px) > max {
break;
}
out = next;
}
out.push_str(ellipsis);
out
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Rect2 {
pub x0: f64,
pub y0: f64,
pub x1: f64,
pub y1: f64,
}
impl Rect2 {
pub fn hits(&self, other: &Rect2, gap: f64) -> bool {
self.x0 < other.x1 + gap
&& other.x0 < self.x1 + gap
&& self.y0 < other.y1 + gap
&& other.y0 < self.y1 + gap
}
pub fn within(&self, bounds: &Rect2) -> bool {
self.x0 >= bounds.x0 && self.x1 <= bounds.x1 && self.y0 >= bounds.y0 && self.y1 <= bounds.y1
}
}
pub struct Placer {
bounds: Rect2,
taken: Vec<Rect2>,
}
impl Placer {
pub fn new(bounds: Rect2) -> Self {
Self {
bounds,
taken: Vec::new(),
}
}
pub fn block(&mut self, rect: Rect2) {
self.taken.push(rect);
}
pub fn place(&mut self, positions: &[Rect2]) -> Option<usize> {
let at = positions
.iter()
.position(|r| r.within(&self.bounds) && !self.taken.iter().any(|t| t.hits(r, GAP_PX)))?;
self.taken.push(positions[at]);
Some(at)
}
}