use super::super::ir::{Bbox, PlacedNode};
use super::super::{Ctx, anchors, child_path, effective_scale, layout_inst, prim, primitives};
use super::{annotate, mates, place_features};
use crate::error::Error;
use crate::resolve::{LinkKind, Program, ResolvedInst, ResolvedLink};
use crate::span::Span;
pub(in crate::layout) fn layout_node(
inst: &ResolvedInst,
path: &str,
program: &Program,
ctx: Ctx,
) -> Result<PlacedNode, Error> {
let own = effective_scale(&inst.attrs, ctx.scale, inst.span)?;
let mut children = match super::section::resolve_of(inst, program)? {
Some(super::section::OfView::Detail {
marker,
host,
letter,
}) => super::section::layout_detail(inst, path, program, own, marker, host, &letter)?,
of => {
let mut c = lay_out(
&inst.children,
path,
program,
own,
inst.span,
inst.id.is_some(),
)?;
if let Some(super::section::OfView::Section { letter }) = of {
super::section::fill_of_title(&mut c, "section", &letter, ratio_of(&inst.attrs));
}
c
}
};
let extent = flow_extent(&children);
let (sx, sy) = (
(extent.min_x + extent.max_x) / 2.0,
(extent.min_y + extent.max_y) / 2.0,
);
for c in children
.iter_mut()
.filter(|c| !anchors::is_pinned(&c.attrs))
{
c.cx -= sx;
c.cy -= sy;
}
let bbox = primitives::closed_bbox(inst, extent, own)?;
let half = inst.attrs.number("stroke-width").unwrap_or(0.0) / 2.0;
place_pinned(&mut children, bbox.inflate(-half))?;
let mut placed = prim::container(inst, bbox, children);
placed.origin = (-sx, -sy);
Ok(placed)
}
pub(in crate::layout) fn layout_root(program: &Program) -> Result<(Vec<PlacedNode>, Bbox), Error> {
let own = effective_scale(&program.scene.attrs, 1.0, Span::empty())?;
let mut children = lay_out(&program.scene.nodes, "", program, own, Span::empty(), true)?;
let extent = flow_extent(&children);
place_pinned(&mut children, extent)?;
Ok((children, extent))
}
fn lay_out(
insts: &[ResolvedInst],
path: &str,
program: &Program,
own: f64,
span: Span,
owns_links: bool,
) -> Result<Vec<PlacedNode>, Error> {
let ctx = Ctx {
scale: own,
drawing: true,
};
let mut kids = Vec::with_capacity(insts.len());
for c in insts {
kids.push(layout_inst(c, &child_path(path, c), program, ctx)?);
}
let geometry: Vec<usize> = kids
.iter()
.enumerate()
.filter(|(_, k)| {
!super::is_sheet(k.kind, &k.type_chain)
&& !anchors::is_pinned(&k.attrs)
&& !super::chrome::is_chrome(&k.attrs)
})
.map(|(i, _)| i)
.collect();
if geometry.is_empty() {
return Err(Error::at(
span,
"a drawing needs at least one geometry child",
));
}
let mut links: Vec<&ResolvedLink> = if owns_links {
program.links.iter().filter(|w| w.scope == path).collect()
} else {
Vec::new()
};
links.sort_by_key(|w| w.span.start);
let (mates, annotations): (Vec<&ResolvedLink>, Vec<&ResolvedLink>) =
links.iter().partition(|w| w.kind == LinkKind::Mate);
place_features(&mut kids, own, None)?;
mates::seat(&mut kids, geometry[0], &mates, path, own)?;
let geo_extent = geometry.iter().fold(Bbox::empty(), |b, &i| {
b.union(kids[i].bbox.shifted(kids[i].cx, kids[i].cy))
});
super::section::fill_planes(&mut kids, geo_extent, own)?;
super::section::place_detail_labels(&mut kids);
let mut lowered = annotate::lower(&kids, &annotations, path, own, None)?;
kids.append(&mut lowered);
Ok(kids)
}
fn ratio_of(attrs: &crate::resolve::AttrMap) -> f64 {
attrs.number("scale").unwrap_or(1.0)
}
fn flow_extent(kids: &[PlacedNode]) -> Bbox {
Bbox::extent_of(kids, |k| !anchors::is_pinned(&k.attrs))
}
fn place_pinned(kids: &mut [PlacedNode], anchor_box: Bbox) -> Result<(), Error> {
for k in kids.iter_mut().filter(|k| anchors::is_pinned(&k.attrs)) {
if let Some(pin) = anchors::read_pin(&k.attrs, k.span)? {
let (cx, cy) = pin.target(anchor_box, k.bbox);
k.cx = cx;
k.cy = cy;
}
if let Some((dx, dy)) = anchors::translate(&k.attrs, k.span)? {
k.cx += dx;
k.cy += dy;
}
}
Ok(())
}
#[cfg(test)]
mod tests;