use crate::ast::document::{Page, SafeZone, SafeZoneType};
use crate::ast::value::dim_to_px;
use crate::diagnostics::Diagnostic;
use super::nodes::{node_bbox, node_id_and_span};
fn zone_rect_px(zone: &SafeZone) -> Option<(f64, f64, f64, f64)> {
let x = dim_to_px(zone.x.value, &zone.x.unit)?;
let y = dim_to_px(zone.y.value, &zone.y.unit)?;
let w = dim_to_px(zone.w.value, &zone.w.unit)?;
let h = dim_to_px(zone.h.value, &zone.h.unit)?;
Some((x, y, w, h))
}
fn intersects(a: (f64, f64, f64, f64), b: (f64, f64, f64, f64)) -> bool {
let (ax, ay, aw, ah) = a;
let (bx, by, bw, bh) = b;
ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by
}
pub(super) fn check_safe_zones(
page: &Page,
page_w: f64,
page_h: f64,
diagnostics: &mut Vec<Diagnostic>,
) {
for zone in &page.safe_zones {
let Some(zone_rect) = zone_rect_px(zone) else {
continue;
};
let label_suffix = match &zone.label {
Some(label) => format!(" (\"{label}\")"),
None => String::new(),
};
for node in &page.children {
let Some(node_rect) = node_bbox(node, page_w, page_h) else {
continue;
};
let (nx, ny, nw, nh) = node_rect;
if nx <= 0.0 && ny <= 0.0 && nx + nw >= page_w && ny + nh >= page_h {
continue;
}
let overlaps = intersects(node_rect, zone_rect);
let (node_id, node_span) = node_id_and_span(node);
match zone.zone_type {
SafeZoneType::Exclusion => {
if overlaps {
diagnostics.push(Diagnostic::advisory(
"safe_zone.violation",
format!(
"node '{}' overlaps exclusion safe-zone '{}'{}",
node_id, zone.id, label_suffix
),
node_span,
Some(node_id.to_owned()),
));
}
}
SafeZoneType::Required => {
if !overlaps {
diagnostics.push(Diagnostic::advisory(
"safe_zone.violation",
format!(
"node '{}' falls entirely outside required safe-zone '{}'{}",
node_id, zone.id, label_suffix
),
node_span,
Some(node_id.to_owned()),
));
}
}
}
}
}
}