From 6690bc23545bfe7275ad92de9e6bd11b7345caf4 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 16 Oct 2021 11:39:26 +0200 Subject: [PATCH] Revise block node contract Frames produced by block nodes are now always treated as exactly one per given region and a frame must not be larger than its respective region. Any overflow must be handled internally. This means that stack and grid don't need to search for fitting regions anymore, since the child has already does that for them. This commit further moves stack spacing into a new `SpacingNode`. --- src/eval/template.rs | 14 ++-- src/eval/walk.rs | 8 +- src/geom/gen.rs | 2 +- src/geom/sides.rs | 2 +- src/geom/spec.rs | 2 +- src/layout/grid.rs | 57 +++++++------ src/layout/image.rs | 10 +-- src/layout/mod.rs | 2 + src/layout/shape.rs | 3 +- src/layout/shaping.rs | 3 +- src/layout/spacing.rs | 50 +++++++++++ src/layout/stack.rs | 160 ++++++++++++------------------------ src/layout/tree.rs | 1 + src/library/layout.rs | 10 +-- tests/ref/layout/grid-3.png | Bin 37133 -> 36802 bytes 15 files changed, 164 insertions(+), 160 deletions(-) create mode 100644 src/layout/spacing.rs diff --git a/src/eval/template.rs b/src/eval/template.rs index 59fe293aa..639163394 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use super::Str; use crate::diag::StrResult; -use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size}; +use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size, SpecAxis}; use crate::layout::{ Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode, @@ -309,7 +309,7 @@ impl Builder { fn parbreak(&mut self) { let amount = self.style.par_spacing(); self.stack.finish_par(&self.style); - self.stack.push_soft(StackChild::Spacing(amount.into())); + self.stack.push_soft(StackChild::spacing(amount, SpecAxis::Vertical)); } /// Apply a forced page break. @@ -335,7 +335,7 @@ impl Builder { /// Push a block node into the active stack, finishing the active paragraph. fn block(&mut self, node: impl Into) { self.parbreak(); - self.stack.push(StackChild::Any(node.into(), self.style.aligns.block)); + self.stack.push(StackChild::new(node, self.style.aligns.block)); self.parbreak(); } @@ -344,7 +344,7 @@ impl Builder { match axis { GenAxis::Block => { self.stack.finish_par(&self.style); - self.stack.push_hard(StackChild::Spacing(amount)); + self.stack.push_hard(StackChild::spacing(amount, SpecAxis::Vertical)); } GenAxis::Inline => { self.stack.par.push_hard(ParChild::Spacing(amount)); @@ -508,10 +508,8 @@ impl ParBuilder { fn build(self) -> Option { let Self { align, dir, line_spacing, children, .. } = self; - (!children.is_empty()).then(|| { - let node = ParNode { dir, line_spacing, children }; - StackChild::Any(node.into(), align) - }) + (!children.is_empty()) + .then(|| StackChild::new(ParNode { dir, line_spacing, children }, align)) } } diff --git a/src/eval/walk.rs b/src/eval/walk.rs index b76300ecc..24284e4e9 100644 --- a/src/eval/walk.rs +++ b/src/eval/walk.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use super::{Eval, EvalContext, Str, Template, Value}; use crate::diag::TypResult; -use crate::geom::Align; +use crate::geom::{Align, SpecAxis}; use crate::layout::{ParChild, ParNode, StackChild, StackNode}; use crate::syntax::*; use crate::util::BoolExt; @@ -119,9 +119,9 @@ fn walk_item(ctx: &mut EvalContext, label: Str, body: Template) { StackNode { dir: style.dir, children: vec![ - StackChild::Any(label.into(), Align::Start), - StackChild::Spacing((style.text.size / 2.0).into()), - StackChild::Any(body.to_stack(&style).into(), Align::Start), + StackChild::new(label, Align::Start), + StackChild::spacing(style.text.size / 2.0, SpecAxis::Horizontal), + StackChild::new(body.to_stack(&style), Align::Start), ], } }); diff --git a/src/geom/gen.rs b/src/geom/gen.rs index 18801460b..ff8c881aa 100644 --- a/src/geom/gen.rs +++ b/src/geom/gen.rs @@ -98,7 +98,7 @@ impl Debug for Gen { } /// The two generic layouting axes. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum GenAxis { /// The axis words and lines are set along. Inline, diff --git a/src/geom/sides.rs b/src/geom/sides.rs index fc7fb3f4f..d1fcf9b78 100644 --- a/src/geom/sides.rs +++ b/src/geom/sides.rs @@ -76,7 +76,7 @@ impl Get for Sides { } /// The four sides of objects. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum Side { /// The left side. Left, diff --git a/src/geom/spec.rs b/src/geom/spec.rs index a62355482..a257214bf 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -101,7 +101,7 @@ impl Debug for Spec { } /// The two specific layouting axes. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum SpecAxis { /// The horizontal layouting axis. Horizontal, diff --git a/src/layout/grid.rs b/src/layout/grid.rs index 7ed5dd7ad..ddda27c01 100644 --- a/src/layout/grid.rs +++ b/src/layout/grid.rs @@ -330,18 +330,10 @@ impl<'a> GridLayouter<'a> { /// Layout the grid row-by-row. fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { - let base = self.regions.base.get(self.block); - for y in 0 .. self.rows.len() { match self.rows[y] { - TrackSizing::Auto => { - self.layout_auto_row(ctx, y); - } - TrackSizing::Linear(v) => { - let resolved = v.resolve(base); - let frame = self.layout_single_row(ctx, resolved, y); - self.push_row(ctx, frame); - } + TrackSizing::Auto => self.layout_auto_row(ctx, y), + TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y), TrackSizing::Fractional(v) => { self.fr += v; self.constraints.exact.set(self.block, Some(self.full)); @@ -395,7 +387,7 @@ impl<'a> GridLayouter<'a> { // Layout into a single region. if let &[first] = resolved.as_slice() { let frame = self.layout_single_row(ctx, first, y); - self.push_row(ctx, frame); + self.push_row(frame); return; } @@ -414,14 +406,39 @@ impl<'a> GridLayouter<'a> { let frames = self.layout_multi_row(ctx, &resolved, y); let len = frames.len(); for (i, frame) in frames.into_iter().enumerate() { + self.push_row(frame); if i + 1 < len { self.constraints.exact.set(self.block, Some(self.full)); + self.finish_region(ctx); } - self.push_row(ctx, frame); } } - /// Layout a row with a fixed size along the block axis. + /// Layout a row with linear sizing along the block axis. Such a row cannot + /// break across multiple regions, but it may force a region break. + fn layout_linear_row(&mut self, ctx: &mut LayoutContext, v: Linear, y: usize) { + let base = self.regions.base.get(self.block); + let resolved = v.resolve(base); + let frame = self.layout_single_row(ctx, resolved, y); + + // Skip to fitting region. + let length = frame.size.get(self.block); + while !self.regions.current.get(self.block).fits(length) + && !self.regions.in_full_last() + { + self.constraints.max.set(self.block, Some(self.used.block + length)); + self.finish_region(ctx); + + // Don't skip multiple regions for gutter and don't push a row. + if y % 2 == 1 { + return; + } + } + + self.push_row(frame); + } + + /// Layout a row with a fixed size along the block axis and return its frame. fn layout_single_row( &self, ctx: &mut LayoutContext, @@ -508,19 +525,9 @@ impl<'a> GridLayouter<'a> { outputs } - /// Push a row frame into the current or next fitting region, finishing - /// regions (including layouting fractional rows) if necessary. - fn push_row(&mut self, ctx: &mut LayoutContext, frame: Frame) { + /// Push a row frame into the current region. + fn push_row(&mut self, frame: Frame) { let length = frame.size.get(self.block); - - // Skip to fitting region. - while !self.regions.current.get(self.block).fits(length) - && !self.regions.in_full_last() - { - self.constraints.max.set(self.block, Some(self.used.block + length)); - self.finish_region(ctx); - } - *self.regions.current.get_mut(self.block) -= length; self.used.block += length; self.lrows.push(Row::Frame(frame)); diff --git a/src/layout/image.rs b/src/layout/image.rs index 4327375b7..eb2fc2217 100644 --- a/src/layout/image.rs +++ b/src/layout/image.rs @@ -23,18 +23,18 @@ impl Layout for ImageNode { let pixel_size = Spec::new(img.width() as f64, img.height() as f64); let pixel_ratio = pixel_size.x / pixel_size.y; - let mut constraints = Constraints::new(regions.expand); - constraints.set_base_if_linear(regions.base, Spec::new(self.width, self.height)); - let width = self.width.map(|w| w.resolve(regions.base.w)); let height = self.height.map(|w| w.resolve(regions.base.h)); + let mut cts = Constraints::new(regions.expand); + cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height)); + let size = match (width, height) { (Some(width), Some(height)) => Size::new(width, height), (Some(width), None) => Size::new(width, width / pixel_ratio), (None, Some(height)) => Size::new(height * pixel_ratio, height), (None, None) => { - constraints.exact.x = Some(regions.current.w); + cts.exact.x = Some(regions.current.w); if regions.current.w.is_finite() { // Fit to width. Size::new(regions.current.w, regions.current.w / pixel_ratio) @@ -48,7 +48,7 @@ impl Layout for ImageNode { let mut frame = Frame::new(size, size.h); frame.push(Point::zero(), Element::Image(self.id, size)); - vec![frame.constrain(constraints)] + vec![frame.constrain(cts)] } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index cd59e3d2c..ee74ff6fe 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -11,6 +11,7 @@ mod par; mod regions; mod shape; mod shaping; +mod spacing; mod stack; mod tree; @@ -25,6 +26,7 @@ pub use par::*; pub use regions::*; pub use shape::*; pub use shaping::*; +pub use spacing::*; pub use stack::*; pub use tree::*; diff --git a/src/layout/shape.rs b/src/layout/shape.rs index aa90707c0..3469f6602 100644 --- a/src/layout/shape.rs +++ b/src/layout/shape.rs @@ -46,7 +46,8 @@ impl Layout for ShapeNode { let mut cts = Constraints::new(regions.expand); cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height)); - // Set exact and base constraint if child is automatically sized. + // Set tight exact and base constraints if the child is + // automatically sized since we don't know what the child might do. if self.width.is_none() { cts.exact.x = Some(regions.current.w); cts.base.x = Some(regions.base.w); diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs index 4580ebf56..7bd2646d4 100644 --- a/src/layout/shaping.rs +++ b/src/layout/shaping.rs @@ -48,6 +48,7 @@ pub fn shape<'a>( /// This type contains owned or borrowed shaped text runs, which can be /// measured, used to reshape substrings more quickly and converted into a /// frame. +#[derive(Debug, Clone)] pub struct ShapedText<'a> { /// The text that was shaped. pub text: &'a str, @@ -64,7 +65,7 @@ pub struct ShapedText<'a> { } /// A single glyph resulting from shaping. -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct ShapedGlyph { /// The font face the glyph is contained in. pub face_id: FaceId, diff --git a/src/layout/spacing.rs b/src/layout/spacing.rs new file mode 100644 index 000000000..68fab3e67 --- /dev/null +++ b/src/layout/spacing.rs @@ -0,0 +1,50 @@ +use super::*; + +/// Spacing between other nodes. +#[derive(Debug)] +#[cfg_attr(feature = "layout-cache", derive(Hash))] +pub struct SpacingNode { + /// Which axis to space on. + pub axis: SpecAxis, + /// How much spacing to add. + pub amount: Linear, +} + +impl Layout for SpacingNode { + fn layout( + &self, + _: &mut LayoutContext, + regions: &Regions, + ) -> Vec>> { + let base = regions.base.get(self.axis); + let resolved = self.amount.resolve(base); + let limit = regions.current.get(self.axis); + + // Generate constraints. + let mut cts = Constraints::new(regions.expand); + if self.amount.is_relative() { + cts.base.set(self.axis, Some(base)); + } + + // If the spacing fits into the region, any larger region would also do. + // If it was limited though, any change it region size might lead to + // different results. + if resolved < limit { + cts.min.set(self.axis, Some(resolved)); + } else { + cts.exact.set(self.axis, Some(limit)); + } + + // Create frame with limited spacing size along spacing axis and zero + // extent along the other axis. + let mut size = Size::zero(); + size.set(self.axis, resolved.min(limit)); + vec![Frame::new(size, size.h).constrain(cts)] + } +} + +impl From for LayoutNode { + fn from(spacing: SpacingNode) -> Self { + Self::new(spacing) + } +} diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 765fb7ab0..4b9328a7f 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -12,15 +12,6 @@ pub struct StackNode { pub children: Vec, } -/// A child of a stack node. -#[cfg_attr(feature = "layout-cache", derive(Hash))] -pub enum StackChild { - /// Spacing between other nodes. - Spacing(Linear), - /// Any child node and how to align it in the stack. - Any(LayoutNode, Align), -} - impl Layout for StackNode { fn layout( &self, @@ -37,12 +28,31 @@ impl From for LayoutNode { } } +/// A child of a stack node. +#[cfg_attr(feature = "layout-cache", derive(Hash))] +pub struct StackChild { + /// The node itself. + pub node: LayoutNode, + /// How to align the node along the block axis. + pub align: Align, +} + +impl StackChild { + /// Create a new stack child. + pub fn new(node: impl Into, align: Align) -> Self { + Self { node: node.into(), align } + } + + /// Create a spacing stack child. + pub fn spacing(amount: impl Into, axis: SpecAxis) -> Self { + Self::new(SpacingNode { amount: amount.into(), axis }, Align::Start) + } +} + impl Debug for StackChild { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Spacing(v) => write!(f, "Spacing({:?})", v), - Self::Any(node, _) => node.fmt(f), - } + write!(f, "{:?}: ", self.align)?; + self.node.fmt(f) } } @@ -51,7 +61,7 @@ struct StackLayouter<'a> { /// The stack node to layout. stack: &'a StackNode, /// The axis of the block direction. - block: SpecAxis, + axis: SpecAxis, /// Whether the stack should expand to fill the region. expand: Spec, /// The region to layout into. @@ -63,10 +73,6 @@ struct StackLayouter<'a> { used: Gen, /// The alignment ruler for the current region. ruler: Align, - /// The constraints for the current region. - constraints: Constraints, - /// Whether the last region can fit all the remaining content. - overflowing: bool, /// Offset, alignment and frame for all children that fit into the current /// region. The exact positions are not known yet. frames: Vec<(Length, Align, Rc)>, @@ -77,23 +83,21 @@ struct StackLayouter<'a> { impl<'a> StackLayouter<'a> { /// Create a new stack layouter. fn new(stack: &'a StackNode, mut regions: Regions) -> Self { - let block = stack.dir.axis(); + let axis = stack.dir.axis(); let full = regions.current; let expand = regions.expand; // Disable expansion along the block axis for children. - regions.expand.set(block, false); + regions.expand.set(axis, false); Self { stack, - block, + axis, expand, regions, full, used: Gen::zero(), ruler: Align::Start, - constraints: Constraints::new(expand), - overflowing: false, frames: vec![], finished: vec![], } @@ -102,17 +106,12 @@ impl<'a> StackLayouter<'a> { /// Layout all children. fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { for child in &self.stack.children { - match *child { - StackChild::Spacing(amount) => self.space(amount), - StackChild::Any(ref node, align) => { - let nodes = node.layout(ctx, &self.regions); - let len = nodes.len(); - for (i, frame) in nodes.into_iter().enumerate() { - if i + 1 < len { - self.constraints.exact = self.full.to_spec().map(Some); - } - self.push_frame(frame.item, align); - } + let frames = child.node.layout(ctx, &self.regions); + let len = frames.len(); + for (i, frame) in frames.into_iter().enumerate() { + self.push_frame(frame.item, child.align); + if i + 1 < len { + self.finish_region(); } } } @@ -121,96 +120,37 @@ impl<'a> StackLayouter<'a> { self.finished } - /// Add block-axis spacing into the current region. - fn space(&mut self, amount: Linear) { - // Resolve the linear. - let full = self.full.get(self.block); - let resolved = amount.resolve(full); - - // Cap the spacing to the remaining available space. This action does - // not directly affect the constraints because of the cap. - let remaining = self.regions.current.get_mut(self.block); - let capped = resolved.min(*remaining); - - // Grow our size and shrink the available space in the region. - self.used.block += capped; - *remaining -= capped; - } - - /// Push a frame into the current or next fitting region, finishing regions - /// if necessary. + /// Push a frame into the current region. fn push_frame(&mut self, frame: Rc, align: Align) { - let size = frame.size.to_gen(self.block); - - // Don't allow `Start` after `End` in the same region. - if align < self.ruler { - self.finish_region(); - } - - // Find a fitting region. - while !self.regions.current.get(self.block).fits(size.block) { - if self.regions.in_full_last() { - self.overflowing = true; - break; - } - - self.constraints - .max - .get_mut(self.block) - .set_min(self.used.block + size.block); - - self.finish_region(); - } - - // Shrink available space in the region. - *self.regions.current.get_mut(self.block) -= size.block; - // Grow our size. let offset = self.used.block; + let size = frame.size.to_gen(self.axis); self.used.block += size.block; self.used.inline.set_max(size.inline); - self.ruler = align; + self.ruler = self.ruler.max(align); - // Remember the frame with offset and alignment. - self.frames.push((offset, align, frame)); + // Remember the frame and shrink available space in the region for the + // following children. + self.frames.push((offset, self.ruler, frame)); + *self.regions.current.get_mut(self.axis) -= size.block; } /// Finish the frame for one region. fn finish_region(&mut self) { - let expand = self.expand; - let used = self.used.to_size(self.block); - // Determine the stack's size dependening on whether the region expands. + let used = self.used.to_size(self.axis); let size = Size::new( - if expand.x { - self.constraints.exact.x = Some(self.full.w); - self.full.w - } else { - self.constraints.min.x = Some(used.w.min(self.full.w)); - used.w - }, - if expand.y { - self.constraints.exact.y = Some(self.full.h); - self.full.h - } else { - self.constraints.min.y = Some(used.h.min(self.full.h)); - used.h - }, + if self.expand.x { self.full.w } else { used.w }, + if self.expand.y { self.full.h } else { used.h }, ); - if self.overflowing { - self.constraints.min.y = None; - self.constraints.max.y = None; - self.constraints.exact = self.full.to_spec().map(Some); - } - let mut output = Frame::new(size, size.h); let mut first = true; // Place all frames. for (offset, align, frame) in self.frames.drain(..) { - let stack_size = size.to_gen(self.block); - let child_size = frame.size.to_gen(self.block); + let stack_size = size.to_gen(self.axis); + let child_size = frame.size.to_gen(self.axis); // Align along the block axis. let block = align.resolve( @@ -224,7 +164,7 @@ impl<'a> StackLayouter<'a> { }, ); - let pos = Gen::new(Length::zero(), block).to_point(self.block); + let pos = Gen::new(Length::zero(), block).to_point(self.axis); // The baseline of the stack is that of the first frame. if first { @@ -235,11 +175,15 @@ impl<'a> StackLayouter<'a> { output.push_frame(pos, frame); } + // Generate tight constraints for now. + let mut cts = Constraints::new(self.expand); + cts.exact = self.full.to_spec().map(Some); + cts.base = self.regions.base.to_spec().map(Some); + self.regions.next(); self.full = self.regions.current; self.used = Gen::zero(); self.ruler = Align::Start; - self.finished.push(output.constrain(self.constraints)); - self.constraints = Constraints::new(expand); + self.finished.push(output.constrain(cts)); } } diff --git a/src/layout/tree.rs b/src/layout/tree.rs index 900eb1a9a..181bb6113 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -103,6 +103,7 @@ impl Layout for LayoutNode { #[cfg(debug_assertions)] if !entry.check(regions) { + eprintln!("node: {:#?}", self.node); eprintln!("regions: {:#?}", regions); eprintln!( "constraints: {:#?}", diff --git a/src/library/layout.rs b/src/library/layout.rs index b98b08f13..ffe8bc69e 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -209,7 +209,7 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult { } let dir = args.named("dir")?.unwrap_or(Dir::TTB); - let spacing = args.named("spacing")?; + let spacing = args.named::("spacing")?; let list: Vec = args.all().collect(); Ok(Value::Template(Template::from_block(move |style| { @@ -220,16 +220,16 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult { for child in &list { match child { Child::Spacing(v) => { - children.push(StackChild::Spacing(*v)); + children.push(StackChild::spacing(*v, dir.axis())); delayed = None; } Child::Any(template) => { if let Some(v) = delayed { - children.push(StackChild::Spacing(v)); + children.push(StackChild::spacing(v, dir.axis())); } - let node = template.to_stack(style).into(); - children.push(StackChild::Any(node, style.aligns.block)); + let node = template.to_stack(style); + children.push(StackChild::new(node, style.aligns.block)); delayed = spacing; } } diff --git a/tests/ref/layout/grid-3.png b/tests/ref/layout/grid-3.png index d2882f231fc8b43af8dbe1c28335f11a6550818c..f4d450f8e19522cdf091b53a38c48a9ba47d3bdc 100644 GIT binary patch delta 13444 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 N`uA>E-g18N{{R+rY<&O# delta 13791 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