use super::*;
pub(super) fn accumulate_extent(n: &PlacedNode, ox: f64, oy: f64, rot: f64, bbox: &mut Bbox) {
let turn = |x: f64, y: f64, deg: f64| -> (f64, f64) {
if deg == 0.0 {
return (x, y);
}
let (s, c) = deg.to_radians().sin_cos();
(x * c - y * s, x * s + y * c)
};
let (dx, dy) = turn(n.cx, n.cy, rot);
let (wx, wy) = (ox + dx, oy + dy);
let total = rot + n.rotation;
let b = &n.bbox;
for (x, y) in [
(b.min_x, b.min_y),
(b.max_x, b.min_y),
(b.min_x, b.max_y),
(b.max_x, b.max_y),
] {
let (px, py) = turn(x, y, total);
*bbox = bbox.union(Bbox {
min_x: wx + px,
min_y: wy + py,
max_x: wx + px,
max_y: wy + py,
});
}
if n.attrs.get("clip").is_some() {
return;
}
for c in &n.children {
accumulate_extent(c, wx, wy, total, bbox);
}
}
pub(super) fn finish(
program: &Program,
nodes: Vec<PlacedNode>,
scene_bbox: Bbox,
routing: routing::Routing,
) -> Result<LaidOut, Error> {
let pad = primitives::padding(&program.scene.attrs, Span::empty())?;
let mut bbox = scene_bbox;
for n in &nodes {
accumulate_extent(n, 0.0, 0.0, 0.0, &mut bbox);
}
let link_points = routing.links.iter().flat_map(|w| &w.path);
let air_points = routing.strays.iter().flat_map(|a| [&a.from, &a.to]);
for &(x, y) in link_points.chain(air_points) {
bbox.min_x = bbox.min_x.min(x);
bbox.min_y = bbox.min_y.min(y);
bbox.max_x = bbox.max_x.max(x);
bbox.max_y = bbox.max_y.max(y);
}
for t in routing.links.iter().flat_map(|w| &w.texts) {
let font = crate::font::Font::of(&t.attrs);
let size = t.attrs.number("font-size").unwrap_or(0.0);
let ls = t.attrs.number("letter-spacing").unwrap_or(0.0);
let lsp = t.attrs.number("line-spacing").unwrap_or(0.0);
let (hw, hh) = (
text::approx_width(&t.content, font, size, ls) / 2.0,
text::approx_height(&t.content, size, lsp) / 2.0,
);
bbox.min_x = bbox.min_x.min(t.position.0 - hw);
bbox.min_y = bbox.min_y.min(t.position.1 - hh);
bbox.max_x = bbox.max_x.max(t.position.0 + hw);
bbox.max_y = bbox.max_y.max(t.position.1 + hh);
}
let vb = ViewBox {
x: bbox.min_x - pad.left,
y: bbox.min_y - pad.top,
w: bbox.w() + pad.left + pad.right,
h: bbox.h() + pad.top + pad.bottom,
};
let canvas_fill = program.scene.attrs.get("fill").cloned();
let physical = pages_only(&nodes).map(|scale| (vb.w / scale, vb.h / scale));
Ok(LaidOut {
viewbox: vb,
nodes,
links: routing.links,
link_report: routing.report,
strays: routing.strays,
vars: program.vars.clone(),
sheet: program.sheet.clone(),
canvas_fill,
gradients: Vec::new(),
hatches: Vec::new(),
clips: Vec::new(),
physical,
})
}
fn pages_only(nodes: &[PlacedNode]) -> Option<f64> {
if nodes.is_empty() || !nodes.iter().all(|n| page::is_page(&n.type_chain)) {
return None;
}
Some(nodes[0].attrs.number("px-per-unit").unwrap_or(4.0))
}