summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval/layout.rs (renamed from src/layout/mod.rs)185
-rw-r--r--src/eval/mod.rs2
-rw-r--r--src/eval/template.rs6
-rw-r--r--src/eval/value.rs3
-rw-r--r--src/export/render.rs30
-rw-r--r--src/layout/constraints.rs88
-rw-r--r--src/layout/incremental.rs451
-rw-r--r--src/layout/regions.rs102
-rw-r--r--src/lib.rs41
-rw-r--r--src/library/align.rs13
-rw-r--r--src/library/columns.rs25
-rw-r--r--src/library/flow.rs31
-rw-r--r--src/library/grid.rs91
-rw-r--r--src/library/hide.rs4
-rw-r--r--src/library/image.rs20
-rw-r--r--src/library/mod.rs8
-rw-r--r--src/library/pad.rs19
-rw-r--r--src/library/page.rs9
-rw-r--r--src/library/par.rs24
-rw-r--r--src/library/place.rs16
-rw-r--r--src/library/shape.rs23
-rw-r--r--src/library/stack.rs25
-rw-r--r--src/library/transform.rs4
23 files changed, 232 insertions, 988 deletions
diff --git a/src/layout/mod.rs b/src/eval/layout.rs
index afb2621b..7608023a 100644
--- a/src/layout/mod.rs
+++ b/src/eval/layout.rs
@@ -1,22 +1,14 @@
//! Layouting infrastructure.
-mod constraints;
-mod incremental;
-mod regions;
-
-pub use constraints::*;
-pub use incremental::*;
-pub use regions::*;
-
use std::any::{Any, TypeId};
use std::fmt::{self, Debug, Formatter};
-use std::hash::{Hash, Hasher};
+use std::hash::Hash;
use std::sync::Arc;
use crate::diag::TypResult;
use crate::eval::StyleChain;
use crate::frame::{Element, Frame, Geometry, Shape, Stroke};
-use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform};
+use crate::geom::{Align, Length, Linear, Paint, Point, Sides, Size, Spec, Transform};
use crate::library::{AlignNode, PadNode, TransformNode, MOVE};
use crate::util::Prehashed;
use crate::Vm;
@@ -32,7 +24,7 @@ pub trait Layout {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>>;
+ ) -> TypResult<Vec<Arc<Frame>>>;
/// Convert to a packed node.
fn pack(self) -> LayoutNode
@@ -43,6 +35,102 @@ pub trait Layout {
}
}
+/// A sequence of regions to layout into.
+#[derive(Debug, Clone)]
+pub struct Regions {
+ /// The (remaining) size of the first region.
+ pub first: Size,
+ /// The base size for relative sizing.
+ pub base: Size,
+ /// The height of followup regions. The width is the same for all regions.
+ pub backlog: std::vec::IntoIter<Length>,
+ /// The height of the final region that is repeated once the backlog is
+ /// drained. The width is the same for all regions.
+ pub last: Option<Length>,
+ /// Whether nodes should expand to fill the regions instead of shrinking to
+ /// fit the content.
+ pub expand: Spec<bool>,
+}
+
+impl Regions {
+ /// Create a new region sequence with exactly one region.
+ pub fn one(size: Size, base: Size, expand: Spec<bool>) -> Self {
+ Self {
+ first: size,
+ base,
+ backlog: vec![].into_iter(),
+ last: None,
+ expand,
+ }
+ }
+
+ /// Create a new sequence of same-size regions that repeats indefinitely.
+ pub fn repeat(size: Size, base: Size, expand: Spec<bool>) -> Self {
+ Self {
+ first: size,
+ base,
+ backlog: vec![].into_iter(),
+ last: Some(size.y),
+ expand,
+ }
+ }
+
+ /// Create new regions where all sizes are mapped with `f`.
+ ///
+ /// Note that since all regions must have the same width, the width returned
+ /// by `f` is ignored for the backlog and the final region.
+ pub fn map<F>(&self, mut f: F) -> Self
+ where
+ F: FnMut(Size) -> Size,
+ {
+ let x = self.first.x;
+ Self {
+ first: f(self.first),
+ base: f(self.base),
+ backlog: self
+ .backlog
+ .as_slice()
+ .iter()
+ .map(|&y| f(Size::new(x, y)).y)
+ .collect::<Vec<_>>()
+ .into_iter(),
+ last: self.last.map(|y| f(Size::new(x, y)).y),
+ expand: self.expand,
+ }
+ }
+
+ /// Whether the first region is full and a region break is called for.
+ pub fn is_full(&self) -> bool {
+ Length::zero().fits(self.first.y) && !self.in_last()
+ }
+
+ /// Whether the first region is the last usable region.
+ ///
+ /// If this is true, calling `next()` will have no effect.
+ pub fn in_last(&self) -> bool {
+ self.backlog.len() == 0 && self.last.map_or(true, |height| self.first.y == height)
+ }
+
+ /// Advance to the next region if there is any.
+ pub fn next(&mut self) {
+ if let Some(height) = self.backlog.next().or(self.last) {
+ self.first.y = height;
+ self.base.y = height;
+ }
+ }
+
+ /// An iterator that returns the sizes of the first and all following
+ /// regions, equivalently to what would be produced by calling
+ /// [`next()`](Self::next) repeatedly until all regions are exhausted.
+ /// This iterater may be infinite.
+ pub fn iter(&self) -> impl Iterator<Item = Size> + '_ {
+ let first = std::iter::once(self.first);
+ let backlog = self.backlog.as_slice().iter();
+ let last = self.last.iter().cycle();
+ first.chain(backlog.chain(last).map(|&h| Size::new(self.first.x, h)))
+ }
+}
+
/// A type-erased layouting node with a precomputed hash.
#[derive(Clone, Hash)]
pub struct LayoutNode(Arc<Prehashed<dyn Bounds>>);
@@ -136,41 +224,9 @@ impl Layout for LayoutNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
- let styles = styles.barred(self.id());
-
- let hash = {
- let mut state = fxhash::FxHasher64::default();
- self.hash(&mut state);
- styles.hash(&mut state);
- state.finish()
- };
-
- // This is not written with `unwrap_or_else`, because then the
- // #[track_caller] annotation doesn't work.
- if let Some(frames) = vm.layout_cache.get(hash, regions) {
- Ok(frames)
- } else {
- vm.level += 1;
- let frames = self.0.layout(vm, regions, styles)?;
- vm.level -= 1;
-
- let entry = FramesEntry::new(frames.clone(), vm.level);
-
- #[cfg(debug_assertions)]
- if !entry.check(regions) {
- eprintln!("node: {:#?}", self.0);
- eprintln!("regions: {regions:#?}");
- eprintln!(
- "constraints: {:#?}",
- frames.iter().map(|c| c.cts).collect::<Vec<_>>(),
- );
- panic!("constraints did not match regions they were created for");
- }
-
- vm.layout_cache.insert(hash, entry);
- Ok(frames)
- }
+ ) -> TypResult<Vec<Arc<Frame>>> {
+ // TODO(query)
+ self.0.layout(vm, regions, styles.barred(self.id()))
}
fn pack(self) -> LayoutNode {
@@ -221,11 +277,10 @@ impl Layout for EmptyNode {
_: &mut Vm,
regions: &Regions,
_: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
- let size = regions.expand.select(regions.current, Size::zero());
- let mut cts = Constraints::new(regions.expand);
- cts.exact = regions.current.filter(regions.expand);
- Ok(vec![Frame::new(size).constrain(cts)])
+ ) -> TypResult<Vec<Arc<Frame>>> {
+ Ok(vec![Arc::new(Frame::new(
+ regions.expand.select(regions.first, Size::zero()),
+ ))])
}
}
@@ -244,10 +299,7 @@ impl Layout for SizedNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
- let is_auto = self.sizing.map_is_none();
- let is_rel = self.sizing.map(|s| s.map_or(false, Linear::is_relative));
-
+ ) -> TypResult<Vec<Arc<Frame>>> {
// The "pod" is the region into which the child will be layouted.
let pod = {
// Resolve the sizing to a concrete size.
@@ -255,30 +307,25 @@ impl Layout for SizedNode {
.sizing
.zip(regions.base)
.map(|(s, b)| s.map(|v| v.resolve(b)))
- .unwrap_or(regions.current);
+ .unwrap_or(regions.first);
// Select the appropriate base and expansion for the child depending
// on whether it is automatically or linearly sized.
+ let is_auto = self.sizing.map_is_none();
let base = is_auto.select(regions.base, size);
let expand = regions.expand | !is_auto;
Regions::one(size, base, expand)
};
+ // Layout the child.
let mut frames = self.child.layout(vm, &pod, styles)?;
- let Constrained { item: frame, cts } = &mut frames[0];
// Ensure frame size matches regions size if expansion is on.
- let target = regions.expand.select(regions.current, frame.size);
+ let frame = &mut frames[0];
+ let target = regions.expand.select(regions.first, frame.size);
Arc::make_mut(frame).resize(target, Align::LEFT_TOP);
- // Set base & exact constraints if the child is automatically sized
- // since we don't know what the child might have done. Also set base if
- // our sizing is relative.
- *cts = Constraints::new(regions.expand);
- cts.exact = regions.current.filter(regions.expand | is_auto);
- cts.base = regions.base.filter(is_rel | is_auto);
-
Ok(frames)
}
}
@@ -298,9 +345,9 @@ impl Layout for FillNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let mut frames = self.child.layout(vm, regions, styles)?;
- for Constrained { item: frame, .. } in &mut frames {
+ for frame in &mut frames {
let shape = Shape::filled(Geometry::Rect(frame.size), self.fill);
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
}
@@ -323,9 +370,9 @@ impl Layout for StrokeNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let mut frames = self.child.layout(vm, regions, styles)?;
- for Constrained { item: frame, .. } in &mut frames {
+ for frame in &mut frames {
let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke);
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 1b61ac15..a67a21d4 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -12,6 +12,7 @@ mod capture;
mod class;
mod collapse;
mod func;
+mod layout;
mod ops;
mod scope;
mod show;
@@ -23,6 +24,7 @@ pub use class::*;
pub use collapse::*;
pub use dict::*;
pub use func::*;
+pub use layout::*;
pub use scope::*;
pub use show::*;
pub use styles::*;
diff --git a/src/eval/template.rs b/src/eval/template.rs
index a1d2297d..b4e3c76d 100644
--- a/src/eval/template.rs
+++ b/src/eval/template.rs
@@ -7,10 +7,10 @@ use std::ops::{Add, AddAssign};
use typed_arena::Arena;
use super::{
- CollapsingBuilder, Interruption, Property, Show, ShowNode, StyleMap, StyleVecBuilder,
+ CollapsingBuilder, Interruption, Layout, LayoutNode, Property, Show, ShowNode,
+ StyleMap, StyleVecBuilder,
};
use crate::diag::StrResult;
-use crate::layout::{Layout, LayoutNode};
use crate::library::prelude::*;
use crate::library::{
DecoNode, FlowChild, FlowNode, ListItem, ListKind, ListNode, PageNode, ParChild,
@@ -240,7 +240,7 @@ impl Layout for Template {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let sya = Arena::new();
let tpa = Arena::new();
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 952c7293..009a1463 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -4,10 +4,9 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
-use super::{ops, Args, Array, Class, Dict, Func, Template};
+use super::{ops, Args, Array, Class, Dict, Func, Layout, Template};
use crate::diag::StrResult;
use crate::geom::{Angle, Color, Fractional, Length, Linear, Relative, RgbaColor};
-use crate::layout::Layout;
use crate::syntax::Spanned;
use crate::util::EcoString;
diff --git a/src/export/render.rs b/src/export/render.rs
index 8b7aa46d..1a08b60b 100644
--- a/src/export/render.rs
+++ b/src/export/render.rs
@@ -1,6 +1,5 @@
//! Rendering into raster images.
-use std::collections::{hash_map::Entry, HashMap};
use std::io::Read;
use image::{GenericImageView, Rgba};
@@ -8,26 +7,12 @@ use tiny_skia as sk;
use ttf_parser::{GlyphId, OutlineBuilder};
use usvg::FitTo;
-use crate::font::{Face, FaceId};
+use crate::font::Face;
use crate::frame::{Element, Frame, Geometry, Group, Shape, Stroke, Text};
use crate::geom::{self, Length, Paint, PathElement, Size, Transform};
use crate::image::{Image, RasterImage, Svg};
use crate::Context;
-/// Caches rendering artifacts.
-#[derive(Default, Clone)]
-pub struct RenderCache {
- /// Glyphs prepared for rendering.
- glyphs: HashMap<(FaceId, GlyphId), pixglyph::Glyph>,
-}
-
-impl RenderCache {
- /// Create a new, empty rendering cache.
- pub fn new() -> Self {
- Self::default()
- }
-}
-
/// Export a frame into a rendered image.
///
/// This renders the frame at the given number of pixels per printer's point and
@@ -131,7 +116,6 @@ fn render_text(
text: &Text,
) {
let face = ctx.fonts.get(text.face_id);
- let cache = &mut ctx.render_cache;
let mut x = 0.0;
for glyph in &text.glyphs {
@@ -141,7 +125,7 @@ fn render_text(
render_svg_glyph(canvas, ts, mask, text, face, id)
.or_else(|| render_bitmap_glyph(canvas, ts, mask, text, face, id))
- .or_else(|| render_outline_glyph(canvas, ts, mask, cache, text, face, id));
+ .or_else(|| render_outline_glyph(canvas, ts, mask, text, face, id));
x += glyph.x_advance.resolve(text.size).to_f32();
}
@@ -227,7 +211,6 @@ fn render_outline_glyph(
canvas: &mut sk::Pixmap,
ts: sk::Transform,
mask: Option<&sk::ClipMask>,
- cache: &mut RenderCache,
text: &Text,
face: &Face,
id: GlyphId,
@@ -255,15 +238,10 @@ fn render_outline_glyph(
return Some(());
}
+ // TODO(query)
// Try to retrieve a prepared glyph or prepare it from scratch if it
// doesn't exist, yet.
- let glyph = match cache.glyphs.entry((text.face_id, id)) {
- Entry::Occupied(entry) => entry.into_mut(),
- Entry::Vacant(entry) => {
- let glyph = pixglyph::Glyph::load(face.ttf(), id)?;
- entry.insert(glyph)
- }
- };
+ let glyph = pixglyph::Glyph::load(face.ttf(), id)?;
// Rasterize the glyph with `pixglyph`.
let bitmap = glyph.rasterize(ts.tx, ts.ty, ppem);
diff --git a/src/layout/constraints.rs b/src/layout/constraints.rs
deleted file mode 100644
index 3bdbc4bc..00000000
--- a/src/layout/constraints.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-use std::sync::Arc;
-
-use super::Regions;
-use crate::frame::Frame;
-use crate::geom::{Length, Size, Spec};
-
-/// Constrain a frame with constraints.
-pub trait Constrain {
- /// Reference-count the frame and wrap it with constraints.
- fn constrain(self, cts: Constraints) -> Constrained<Arc<Frame>>;
-}
-
-impl Constrain for Frame {
- fn constrain(self, cts: Constraints) -> Constrained<Arc<Frame>> {
- Constrained::new(Arc::new(self), cts)
- }
-}
-
-/// Carries an item that is only valid in certain regions and the constraints
-/// that describe these regions.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct Constrained<T> {
- /// The item that is only valid if the constraints are fullfilled.
- pub item: T,
- /// Constraints on regions in which the item is valid.
- pub cts: Constraints,
-}
-
-impl<T> Constrained<T> {
- /// Constrain an item with constraints.
- pub fn new(item: T, cts: Constraints) -> Self {
- Self { item, cts }
- }
-}
-
-/// Describe regions that match them.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct Constraints {
- /// The minimum available length in the region.
- pub min: Spec<Option<Length>>,
- /// The maximum available length in the region.
- pub max: Spec<Option<Length>>,
- /// The available length in the region.
- pub exact: Spec<Option<Length>>,
- /// The base length of the region used for relative length resolution.
- pub base: Spec<Option<Length>>,
- /// The expand settings of the region.
- pub expand: Spec<bool>,
-}
-
-impl Constraints {
- /// Create a new region constraint.
- pub fn new(expand: Spec<bool>) -> Self {
- Self {
- min: Spec::default(),
- max: Spec::default(),
- exact: Spec::default(),
- base: Spec::default(),
- expand,
- }
- }
-
- /// Create tight constraints for a region.
- pub fn tight(regions: &Regions) -> Self {
- Self {
- min: Spec::default(),
- max: Spec::default(),
- exact: regions.current.map(Some),
- base: regions.base.map(Some),
- expand: regions.expand,
- }
- }
-
- /// Check whether the constraints are fullfilled in a region with the given
- /// properties.
- pub fn check(&self, current: Size, base: Size, expand: Spec<bool>) -> bool {
- self.expand == expand
- && verify(self.min, current, |m, c| c.fits(m))
- && verify(self.max, current, |m, c| m.fits(c))
- && verify(self.exact, current, Length::approx_eq)
- && verify(self.base, base, Length::approx_eq)
- }
-}
-
-/// Verify a single constraint.
-fn verify(spec: Spec<Option<Length>>, size: Size, f: fn(Length, Length) -> bool) -> bool {
- spec.zip(size).all(|&(opt, s)| opt.map_or(true, |m| f(m, s)))
-}
diff --git a/src/layout/incremental.rs b/src/layout/incremental.rs
deleted file mode 100644
index b68ddcdc..00000000
--- a/src/layout/incremental.rs
+++ /dev/null
@@ -1,451 +0,0 @@
-use std::cmp::Reverse;
-use std::collections::HashMap;
-use std::sync::Arc;
-
-use itertools::Itertools;
-
-use super::{Constrained, Regions};
-use crate::frame::Frame;
-use crate::geom::Scalar;
-
-const TEMP_LEN: usize = 4;
-
-/// Caches layouting artifacts.
-#[derive(Default, Clone)]
-pub struct LayoutCache {
- /// Maps from node hashes to the resulting frames and regions in which the
- /// frames are valid. The right hand side of the hash map is a vector of
- /// results because across one or more compilations, multiple different
- /// layouts of the same node may have been requested.
- frames: HashMap<u64, Vec<FramesEntry>>,
- /// In how many compilations this cache has been used.
- age: usize,
- /// What cache eviction policy should be used.
- policy: EvictionPolicy,
- /// The maximum number of entries this cache should have. Can be exceeded if
- /// there are more must-keep entries.
- max_size: usize,
-}
-
-impl LayoutCache {
- /// Create a new, empty layout cache.
- pub fn new(policy: EvictionPolicy, max_size: usize) -> Self {
- Self {
- frames: HashMap::default(),
- age: 0,
- policy,
- max_size,
- }
- }
-
- /// Whether the cache is empty.
- pub fn is_empty(&self) -> bool {
- self.frames.values().all(|entry| entry.is_empty())
- }
-
- /// Amount of items in the cache.
- pub fn len(&self) -> usize {
- self.frames.values().map(Vec::len).sum()
- }
-
- /// The number of levels stored in the cache.
- pub fn levels(&self) -> usize {
- self.entries().map(|entry| entry.level + 1).max().unwrap_or(0)
- }
-
- /// An iterator over all entries in the cache.
- pub fn entries(&self) -> impl Iterator<Item = &FramesEntry> + '_ {
- self.frames.values().flatten()
- }
-
- /// Fetch matching cached frames if there are any.
- pub fn get(
- &mut self,
- hash: u64,
- regions: &Regions,
- ) -> Option<Vec<Constrained<Arc<Frame>>>> {
- self.frames
- .get_mut(&hash)?
- .iter_mut()
- .find_map(|entry| entry.lookup(regions))
- }
-
- /// Insert a new frame entry into the cache.
- pub fn insert(&mut self, hash: u64, entry: FramesEntry) {
- self.frames.entry(hash).or_default().push(entry);
- }
-
- /// Clear the cache.
- pub fn clear(&mut self) {
- self.frames.clear();
- }
-
- /// Retains all elements for which the closure on the level returns `true`.
- pub fn retain<F>(&mut self, mut f: F)
- where
- F: FnMut(usize) -> bool,
- {
- for entries in self.frames.values_mut() {
- entries.retain(|entry| f(entry.level));
- }
- }
-
- /// Prepare the cache for the next round of compilation.
- pub fn turnaround(&mut self) {
- self.age += 1;
- for entry in self.frames.values_mut().flatten() {
- if entry.temperature[0] > 0 {
- entry.used_cycles += 1;
- }
-
- let last = *entry.temperature.last().unwrap();
- for i in (1 .. TEMP_LEN).rev() {
- entry.temperature[i] = entry.temperature[i - 1];
- }
-
- entry.temperature[0] = 0;
- entry.ancient_hits += last as usize;
- entry.age += 1;
- }
-
- self.evict();
- self.frames.retain(|_, v| !v.is_empty());
- }
-
- /// Evict the cache according to the policy.
- fn evict(&mut self) {
- let len = self.len();
- if len <= self.max_size {
- return;
- }
-
- match self.policy {
- EvictionPolicy::LeastRecentlyUsed => {
- // We find the element with the largest cooldown that cannot fit
- // anymore.
- let threshold = self
- .entries()
- .map(|f| Reverse(f.cooldown()))
- .k_smallest(len - self.max_size)
- .last()
- .unwrap()
- .0;
-
- for entries in self.frames.values_mut() {
- entries.retain(|f| f.cooldown() < threshold);
- }
- }
- EvictionPolicy::LeastFrequentlyUsed => {
- let threshold = self
- .entries()
- .map(|f| Scalar(f.hits() as f64 / f.age() as f64))
- .k_smallest(len - self.max_size)
- .last()
- .unwrap()
- .0;
-
- for entries in self.frames.values_mut() {
- entries.retain(|f| f.hits() as f64 / f.age() as f64 > threshold);
- }
- }
- EvictionPolicy::Patterns => {
- let kept = self.entries().filter(|f| f.properties().must_keep()).count();
-
- let remaining_capacity = self.max_size - kept.min(self.max_size);
- if len - kept <= remaining_capacity {
- return;
- }
-
- let threshold = self
- .entries()
- .filter(|f| !f.properties().must_keep())
- .map(|f| Scalar(f.hits() as f64 / f.age() as f64))
- .k_smallest((len - kept) - remaining_capacity)
- .last()
- .unwrap()
- .0;
-
- for entries in self.frames.values_mut() {
- entries.retain(|f| {
- f.properties().must_keep()
- || f.hits() as f64 / f.age() as f64 > threshold
- });
- }
- }
- EvictionPolicy::None => {}
- }
- }
-}
-
-/// Cached frames from past layouting.
-#[derive(Debug, Clone)]
-pub struct FramesEntry {
- /// The cached frames for a node.
- frames: Vec<Constrained<Arc<Frame>>>,
- /// How nested the frame was in the context is was originally appearing in.
- level: usize,
- /// For how long the element already exists.
- age: usize,
- /// How much the element was accessed during the last five compilations, the
- /// most recent one being the first element.
- temperature: [u8; TEMP_LEN],
- /// All past usages that do not fit in the temperature array.
- ancient_hits: usize,
- /// Amount of cycles in which the element has been used at all.
- used_cycles: usize,
-}
-
-impl FramesEntry {
- /// Construct a new instance.
- pub fn new(frames: Vec<Constrained<Arc<Frame>>>, level: usize) -> Self {
- Self {
- frames,
- level,
- age: 1,
- temperature: [0; TEMP_LEN],
- ancient_hits: 0,
- used_cycles: 0,
- }
- }
-
- /// Checks if the cached frames are valid in the given regions and returns
- /// them if so.
- pub fn lookup(&mut self, regions: &Regions) -> Option<Vec<Constrained<Arc<Frame>>>> {
- self.check(regions).then(|| {
- self.temperature[0] = self.temperature[0].saturating_add(1);
- self.frames.clone()
- })
- }
-
- /// Checks if the cached frames are valid in the given regions.
- pub fn check(&self, regions: &Regions) -> bool {
- let mut iter = regions.iter();
- self.frames.iter().all(|frame| {
- iter.next().map_or(false, |(current, base)| {
- frame.cts.check(current, base, regions.expand)
- })
- })
- }
-
- /// How nested the frame was in the context is was originally appearing in.
- pub fn level(&self) -> usize {
- self.level
- }
-
- /// The number of compilation cycles this item has remained in the cache.
- pub fn age(&self) -> usize {
- self.age
- }
-
- /// Whether this element was used in the last compilation cycle.
- pub fn hit(&self) -> bool {
- self.temperature[0] != 0
- }
-
- /// Get the total amount of hits over the lifetime of this item.
- pub fn hits(&self) -> usize {
- self.temperature.into_iter().map(usize::from).sum::<usize>() + self.ancient_hits
- }
-
- /// The amount of consecutive cycles in which this item has not been used.
- pub fn cooldown(&self) -> usize {
- let mut cycle = 0;
- for &temp in &self.temperature[.. self.age.min(TEMP_LEN)] {
- if temp > 0 {
- return cycle;
- }
- cycle += 1;
- }
- cycle
- }
-
- /// Properties that describe how this entry's temperature evolved.
- pub fn properties(&self) -> PatternProperties {
- let mut all_zeros = true;
- let mut multi_use = false;
- let mut decreasing = true;
- let mut sparse = false;
- let mut abandoned = false;
-
- let mut last = None;
- let mut all_same = true;
-
- for (i, &temp) in self.temperature.iter().enumerate() {
- if temp == 0 && !all_zeros {
- sparse = true;
- }
-
- if temp != 0 {
- all_zeros = false;
- }
-
- if all_zeros && i == 1 {
- abandoned = true;
- }
-
- if temp > 1 {
- multi_use = true;
- }
-
- if let Some(prev) = last {
- if prev > temp {
- decreasing = false;
- }
-
- if temp != prev {
- all_same = false;
- }
- }
-
- last = Some(temp);
- }
-
- if self.age > TEMP_LEN && self.age - TEMP_LEN <= self.ancient_hits {
- multi_use = true;
- }
-
- if self.ancient_hits > 0 {
- all_zeros = false;
- }
-
- PatternProperties {
- mature: self.age > TEMP_LEN,
- hit: self.temperature[0] >= 1,
- top_level: self.level == 0,
- all_zeros,
- multi_use,
- decreasing: decreasing && !all_same,
- sparse,
- abandoned,
- }
- }
-}
-
-/// Cache eviction strategies.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum EvictionPolicy {
- /// Evict the least recently used item.
- LeastRecentlyUsed,
- /// Evict the least frequently used item.
- LeastFrequentlyUsed,
- /// Use the pattern verdicts.
- Patterns,
- /// Do not evict.
- None,
-}
-
-impl Default for EvictionPolicy {
- fn default() -> Self {
- Self::Patterns
- }
-}
-
-/// Describes the properties that this entry's temperature array has.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct PatternProperties {
- /// There only are zero values.
- pub all_zeros: bool,
- /// The entry exists for more or equal time as the temperature array is long.
- pub mature: bool,
- /// The entry was used more than one time in at least one compilation.
- pub multi_use: bool,
- /// The entry was used in the last compilation.
- pub hit: bool,
- /// The temperature is monotonously decreasing in non-terminal temperature fields.
- pub decreasing: bool,
- /// There are zero temperatures after non-zero temperatures.
- pub sparse: bool,
- /// There are multiple zero temperatures at the front of the temperature array.
- pub abandoned: bool,
- /// If the item is on the top level.
- pub top_level: bool,
-}
-
-impl PatternProperties {
- /// Check if it is vital to keep an entry based on its properties.
- pub fn must_keep(&self) -> bool {
- // Keep an undo stack.
- (self.top_level && !self.mature)
- // Keep the most recently created items, even if they have not yet
- // been used.
- || (self.all_zeros && !self.mature)
- || (self.multi_use && !self.abandoned)
- || self.hit
- || self.sparse
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::geom::{Size, Spec};
- use crate::layout::Constraints;
-
- fn empty_frames() -> Vec<Constrained<Arc<Frame>>> {
- vec![Constrained {
- item: Arc::new(Frame::default()),
- cts: Constraints::new(Spec::splat(false)),
- }]
- }
-
- fn zero_regions() -> Regions {
- Regions::one(Size::zero(), Size::zero(), Spec::splat(false))
- }
-
- #[test]
- fn test_layout_incremental_temperature() {
- let mut cache = LayoutCache::new(EvictionPolicy::None, 20);
- let regions = zero_regions();
- cache.policy = EvictionPolicy::None;
- cache.insert(0, FramesEntry::new(empty_frames(), 0));
-
- let entry = cache.frames.get(&0).unwrap().first().unwrap();
- assert_eq!(entry.age(), 1);
- assert_eq!(entry.temperature, [0, 0, 0, 0]);
- assert_eq!(entry.ancient_hits, 0);
- assert_eq!(entry.used_cycles, 0);
- assert_eq!(entry.level, 0);
-
- cache.get(0, &regions).unwrap();
- let entry = cache.frames.get(&0).unwrap().first().unwrap();
- assert_eq!(entry.age(), 1);
- assert_eq!(entry.temperature, [1, 0, 0, 0]);
- assert_eq!(entry.ancient_hits, 0);
-
- cache.turnaround();
- let entry = cache.frames.get(&0).unwrap().first().unwrap();
- assert_eq!(entry.age(), 2);
- assert_eq!(entry.temperature, [0, 1, 0, 0]);
- assert_eq!(entry.ancient_hits, 0);
- assert_eq!(entry.used_cycles, 1);
-
- cache.get(0, &regions).unwrap();
- for _ in 0 .. 4 {
- cache.turnaround();
- }
-
- let entry = cache.frames.get(&0).unwrap().first().unwrap();
- assert_eq!(entry.age(), 6);
- assert_eq!(entry.temperature, [0, 0, 0, 0]);
- assert_eq!(entry.ancient_hits, 2);
- assert_eq!(entry.used_cycles, 2);
- }
-
- #[test]
- fn test_layout_incremental_properties() {
- let mut cache = LayoutCache::new(EvictionPolicy::None, 20);
- cache.policy = EvictionPolicy::None;
- cache.insert(0, FramesEntry::new(empty_frames(), 1));
-
- let props = cache.frames.get(&0).unwrap().first().unwrap().properties();
- assert_eq!(props.top_level, false);
- assert_eq!(props.mature, false);
- assert_eq!(props.multi_use, false);
- assert_eq!(props.hit, false);
- assert_eq!(props.decreasing, false);
- assert_eq!(props.sparse, false);
- assert_eq!(props.abandoned, true);
- assert_eq!(props.all_zeros, true);
- assert_eq!(props.must_keep(), true);
- }
-}
diff --git a/src/layout/regions.rs b/src/layout/regions.rs
deleted file mode 100644
index 3f8b6d25..00000000
--- a/src/layout/regions.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use crate::geom::{Length, Size, Spec};
-
-/// A sequence of regions to layout into.
-#[derive(Debug, Clone)]
-pub struct Regions {
- /// The remaining size of the current region.
- pub current: Size,
- /// The base size for relative sizing.
- pub base: Size,
- /// The height of followup regions. The width is the same for all regions.
- pub backlog: std::vec::IntoIter<Length>,
- /// The height of the final region that is repeated once the backlog is
- /// drained. The width is the same for all regions.
- pub last: Option<Length>,
- /// Whether nodes should expand to fill the regions instead of shrinking to
- /// fit the content.
- pub expand: Spec<bool>,
-}
-
-impl Regions {
- /// Create a new region sequence with exactly one region.
- pub fn one(size: Size, base: Size, expand: Spec<bool>) -> Self {
- Self {
- current: size,
- base,
- backlog: vec![].into_iter(),
- last: None,
- expand,
- }
- }
-
- /// Create a new sequence of same-size regions that repeats indefinitely.
- pub fn repeat(size: Size, base: Size, expand: Spec<bool>) -> Self {
- Self {
- current: size,
- base,
- backlog: vec![].into_iter(),
- last: Some(size.y),
- expand,
- }
- }
-
- /// Create new regions where all sizes are mapped with `f`.
- ///
- /// Note that since all regions must have the same width, the width returned
- /// by `f` is ignored for the backlog and the final region.
- pub fn map<F>(&self, mut f: F) -> Self
- where
- F: FnMut(Size) -> Size,
- {
- let x = self.current.x;
- Self {
- current: f(self.current),
- base: f(self.base),
- backlog: self
- .backlog
- .as_slice()
- .iter()
- .map(|&y| f(Size::new(x, y)).y)
- .collect::<Vec<_>>()
- .into_iter(),
- last: self.last.map(|y| f(Size::new(x, y)).y),
- expand: self.expand,
- }
- }
-
- /// Whether the current region is full and a region break is called for.
- pub fn is_full(&self) -> bool {
- Length::zero().fits(self.current.y) && !self.in_last()
- }
-
- /// Whether `current` is the last usable region.
- ///
- /// If this is true, calling `next()` will have no effect.
- pub fn in_last(&self) -> bool {
- self.backlog.len() == 0
- && self.last.map_or(true, |height| self.current.y == height)
- }
-
- /// Advance to the next region if there is any.
- pub fn next(&mut self) {
- if let Some(height) = self.backlog.next().or(self.last) {
- self.current.y = height;
- self.base.y = height;
- }
- }
-
- /// An iterator that returns pairs of `(current, base)` that are equivalent
- /// to what would be produced by calling [`next()`](Self::next) repeatedly
- /// until all regions are exhausted.
- pub fn iter(&self) -> impl Iterator<Item = (Size, Size)> + '_ {
- let first = std::iter::once((self.current, self.base));
- let backlog = self.backlog.as_slice().iter();
- let last = self.last.iter().cycle();
- first.chain(backlog.chain(last).map(|&height| {
- (
- Size::new(self.current.x, height),
- Size::new(self.base.x, height),
- )
- }))
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 13c63885..cfbdcae7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -44,7 +44,6 @@ pub mod font;
pub mod frame;
pub mod geom;
pub mod image;
-pub mod layout;
pub mod library;
pub mod loading;
pub mod parse;
@@ -58,11 +57,9 @@ use std::sync::Arc;
use crate::diag::TypResult;
use crate::eval::{Eval, Module, Scope, Scopes, StyleMap};
-use crate::export::RenderCache;
use crate::font::FontStore;
use crate::frame::Frame;
use crate::image::ImageStore;
-use crate::layout::{EvictionPolicy, LayoutCache};
use crate::loading::Loader;
use crate::source::{SourceId, SourceStore};
@@ -76,10 +73,6 @@ pub struct Context {
pub fonts: FontStore,
/// Stores decoded images.
pub images: ImageStore,
- /// Caches layouting artifacts.
- pub layout_cache: LayoutCache,
- /// Caches rendering artifacts.
- pub render_cache: RenderCache,
/// The standard library scope.
std: Scope,
/// The default styles.
@@ -115,11 +108,6 @@ impl Context {
pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
Vm::new(self).typeset(id)
}
-
- /// Garbage-collect caches.
- pub fn turnaround(&mut self) {
- self.layout_cache.turnaround();
- }
}
/// A builder for a [`Context`].
@@ -128,8 +116,6 @@ impl Context {
pub struct ContextBuilder {
std: Option<Scope>,
styles: Option<StyleMap>,
- policy: EvictionPolicy,
- max_size: usize,
}
impl ContextBuilder {
@@ -146,21 +132,6 @@ impl ContextBuilder {
self
}
- /// The policy for eviction of the layout cache.
- pub fn cache_policy(mut self, policy: EvictionPolicy) -> Self {
- self.policy = policy;
- self
- }
-
- /// The maximum number of entries the layout cache should have.
- ///
- /// Note that this can be exceeded if more entries are categorized as [must
- /// keep][crate::layout::PatternProperties::must_keep].
- pub fn cache_max_size(mut self, max_size: usize) -> Self {
- self.max_size = max_size;
- self
- }
-
/// Finish building the context by providing the `loader` used to load
/// fonts, images, source files and other resources.
pub fn build(self, loader: Arc<dyn Loader>) -> Context {
@@ -169,8 +140,6 @@ impl ContextBuilder {
fonts: FontStore::new(Arc::clone(&loader)),
images: ImageStore::new(Arc::clone(&loader)),
loader,
- layout_cache: LayoutCache::new(self.policy, self.max_size),
- render_cache: RenderCache::new(),
std: self.std.unwrap_or_else(library::new),
styles: self.styles.unwrap_or_default(),
}
@@ -179,12 +148,7 @@ impl ContextBuilder {
impl Default for ContextBuilder {
fn default() -> Self {
- Self {
- std: None,
- styles: None,
- policy: EvictionPolicy::default(),
- max_size: 2000,
- }
+ Self { std: None, styles: None }
}
}
@@ -198,8 +162,6 @@ pub struct Vm<'a> {
pub fonts: &'a mut FontStore,
/// Stores decoded images.
pub images: &'a mut ImageStore,
- /// Caches layouting artifacts.
- pub layout_cache: &'a mut LayoutCache,
/// The default styles.
pub styles: &'a StyleMap,
/// The stack of imported files that led to evaluation of the current file.
@@ -223,7 +185,6 @@ impl<'a> Vm<'a> {
sources: &mut ctx.sources,
fonts: &mut ctx.fonts,
images: &mut ctx.images,
- layout_cache: &mut ctx.layout_cache,
styles: &ctx.styles,
route: vec![],
modules: HashMap::new(),
diff --git a/src/library/align.rs b/src/library/align.rs
index a7e6d3cb..8ea9ddaf 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -27,7 +27,7 @@ impl Layout for AlignNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
// The child only needs to expand along an axis if there's no alignment.
let mut pod = regions.clone();
pod.expand &= self.aligns.map_is_none();
@@ -40,20 +40,13 @@ impl Layout for AlignNode {
// Layout the child.
let mut frames = self.child.layout(vm, &pod, passed.chain(&styles))?;
- for ((current, base), Constrained { item: frame, cts }) in
- regions.iter().zip(&mut frames)
- {
+ for (region, frame) in regions.iter().zip(&mut frames) {
// Align in the target size. The target size depends on whether we
// should expand.
- let target = regions.expand.select(current, frame.size);
+ let target = regions.expand.select(region, frame.size);
let default = Spec::new(Align::Left, Align::Top);
let aligns = self.aligns.unwrap_or(default);
Arc::make_mut(frame).resize(target, aligns);
-
- // Set constraints.
- cts.expand = regions.expand;
- cts.base = base.filter(cts.base.map_is_some());
- cts.exact = current.filter(regions.expand | cts.exact.map_is_some());
}
Ok(frames)
diff --git a/src/library/columns.rs b/src/library/columns.rs
index 049fa8b9..bae23dd3 100644
--- a/src/library/columns.rs
+++ b/src/library/columns.rs
@@ -32,23 +32,23 @@ impl Layout for ColumnsNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
// Separating the infinite space into infinite columns does not make
// much sense.
- if regions.current.x.is_infinite() {
+ if regions.first.x.is_infinite() {
return self.child.layout(vm, regions, styles);
}
// Determine the width of the gutter and each column.
let columns = self.columns.get();
let gutter = styles.get(Self::GUTTER).resolve(regions.base.x);
- let width = (regions.current.x - gutter * (columns - 1) as f64) / columns as f64;
+ let width = (regions.first.x - gutter * (columns - 1) as f64) / columns as f64;
// Create the pod regions.
let pod = Regions {
- current: Size::new(width, regions.current.y),
+ first: Size::new(width, regions.first.y),
base: Size::new(width, regions.base.y),
- backlog: std::iter::once(&regions.current.y)
+ backlog: std::iter::once(&regions.first.y)
.chain(regions.backlog.as_slice())
.flat_map(|&height| std::iter::repeat(height).take(columns))
.skip(1)
@@ -66,18 +66,18 @@ impl Layout for ColumnsNode {
let mut finished = vec![];
// Stitch together the columns for each region.
- for (current, base) in regions.iter().take(total_regions) {
+ for region in regions.iter().take(total_regions) {
// The height should be the parent height if the node shall expand.
// Otherwise its the maximum column height for the frame. In that
// case, the frame is first created with zero height and then
// resized.
- let height = if regions.expand.y { current.y } else { Length::zero() };
- let mut output = Frame::new(Size::new(regions.current.x, height));
+ let height = if regions.expand.y { region.y } else { Length::zero() };
+ let mut output = Frame::new(Size::new(regions.first.x, height));
let mut cursor = Length::zero();
for _ in 0 .. columns {
let frame = match frames.next() {
- Some(frame) => frame.item,
+ Some(frame) => frame,
None => break,
};
@@ -89,17 +89,14 @@ impl Layout for ColumnsNode {
let x = if dir.is_positive() {
cursor
} else {
- regions.current.x - cursor - width
+ regions.first.x - cursor - width
};
output.push_frame(Point::with_x(x), frame);
cursor += width + gutter;
}
- let mut cts = Constraints::new(regions.expand);
- cts.base = base.map(Some);
- cts.exact = current.map(Some);
- finished.push(output.constrain(cts));
+ finished.push(Arc::new(output));
}
Ok(finished)
diff --git a/src/library/flow.rs b/src/library/flow.rs
index 135fd327..bc3bf0f1 100644
--- a/src/library/flow.rs
+++ b/src/library/flow.rs
@@ -31,7 +31,7 @@ impl Layout for FlowNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let mut layouter = FlowLayouter::new(regions);
for (child, map) in self.0.iter() {
@@ -96,7 +96,7 @@ pub struct FlowLayouter {
regions: Regions,
/// Whether the flow should expand to fill the region.
expand: Spec<bool>,
- /// The full size of `regions.current` that was available before we started
+ /// The full size of `regions.size` that was available before we started
/// subtracting.
full: Size,
/// The size used by the frames for the current region.
@@ -106,7 +106,7 @@ pub struct FlowLayouter {
/// Spacing and layouted nodes.
items: Vec<FlowItem>,
/// Finished frames for previous regions.
- finished: Vec<Constrained<Arc<Frame>>>,
+ finished: Vec<Arc<Frame>>,
}
/// A prepared item in a flow layout.
@@ -125,7 +125,7 @@ impl FlowLayouter {
/// Create a new flow layouter.
pub fn new(regions: &Regions) -> Self {
let expand = regions.expand;
- let full = regions.current;
+ let full = regions.first;
// Disable vertical expansion for children.
let mut regions = regions.clone();
@@ -148,8 +148,8 @@ impl FlowLayouter {
SpacingKind::Linear(v) => {
// Resolve the linear and limit it to the remaining space.
let resolved = v.resolve(self.full.y);
- let limited = resolved.min(self.regions.current.y);
- self.regions.current.y -= limited;
+ let limited = resolved.min(self.regions.first.y);
+ self.regions.first.y -= limited;
self.used.y += limited;
self.items.push(FlowItem::Absolute(resolved));
}
@@ -177,7 +177,7 @@ impl FlowLayouter {
if let Some(placed) = node.downcast::<PlaceNode>() {
if placed.out_of_flow() {
let frame = node.layout(vm, &self.regions, styles)?.remove(0);
- self.items.push(FlowItem::Placed(frame.item));
+ self.items.push(FlowItem::Placed(frame));
return Ok(());
}
}
@@ -197,11 +197,11 @@ impl FlowLayouter {
let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later.
- let size = frame.item.size;
+ let size = frame.size;
self.used.y += size.y;
self.used.x.set_max(size.x);
- self.regions.current.y -= size.y;
- self.items.push(FlowItem::Frame(frame.item, aligns));
+ self.regions.first.y -= size.y;
+ self.items.push(FlowItem::Frame(frame, aligns));
if i + 1 < len {
self.finish_region();
@@ -251,21 +251,16 @@ impl FlowLayouter {
}
}
- // Generate tight constraints for now.
- let mut cts = Constraints::new(self.expand);
- cts.exact = self.full.map(Some);
- cts.base = self.regions.base.map(Some);
-
// Advance to the next region.
self.regions.next();
- self.full = self.regions.current;
+ self.full = self.regions.first;
self.used = Size::zero();
self.fr = Fractional::zero();
- self.finished.push(output.constrain(cts));
+ self.finished.push(Arc::new(output));
}
/// Finish layouting and return the resulting frames.
- pub fn finish(mut self) -> Vec<Constrained<Arc<Frame>>> {
+ pub fn finish(mut self) -> Vec<Arc<Frame>> {
if self.expand.y {
while self.regions.backlog.len() > 0 {
self.finish_region();
diff --git a/src/library/grid.rs b/src/library/grid.rs
index 5f1edb1c..fc62f8eb 100644
--- a/src/library/grid.rs
+++ b/src/library/grid.rs
@@ -38,7 +38,7 @@ impl Layout for GridNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
// Prepare grid layout by unifying content and gutter tracks.
let layouter = GridLayouter::new(
self.tracks.as_deref(),
@@ -105,8 +105,6 @@ pub struct GridLayouter<'a> {
rcols: Vec<Length>,
/// Rows in the current region.
lrows: Vec<Row>,
- /// Whether the grid itself should expand to fill the region.
- expand: Spec<bool>,
/// The full height of the current region.
full: Length,
/// The used-up size of the current region. The horizontal size is
@@ -114,10 +112,8 @@ pub struct GridLayouter<'a> {
used: Size,
/// The sum of fractional ratios in the current region.
fr: Fractional,
- /// Constraints for the active region.
- cts: Constraints,
/// Frames for finished regions.
- finished: Vec<Constrained<Arc<Frame>>>,
+ finished: Vec<Arc<Frame>>,
}
/// Produced by initial row layout, auto and linear rows are already finished,
@@ -177,8 +173,7 @@ impl<'a> GridLayouter<'a> {
cols.pop();
rows.pop();
- let expand = regions.expand;
- let full = regions.current.y;
+ let full = regions.first.y;
let rcols = vec![Length::zero(); cols.len()];
let lrows = vec![];
@@ -195,17 +190,15 @@ impl<'a> GridLayouter<'a> {
styles,
rcols,
lrows,
- expand,
full,
used: Size::zero(),
fr: Fractional::zero(),
- cts: Constraints::new(expand),
finished: vec![],
}
}
/// Determines the columns sizes and then layouts the grid row-by-row.
- pub fn layout(mut self, vm: &mut Vm) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ pub fn layout(mut self, vm: &mut Vm) -> TypResult<Vec<Arc<Frame>>> {
self.measure_columns(vm)?;
for y in 0 .. self.rows.len() {
@@ -219,7 +212,6 @@ impl<'a> GridLayouter<'a> {
TrackSizing::Auto => self.layout_auto_row(vm, y)?,
TrackSizing::Linear(v) => self.layout_linear_row(vm, v, y)?,
TrackSizing::Fractional(v) => {
- self.cts.exact.y = Some(self.full);
self.lrows.push(Row::Fr(v, y));
self.fr += v;
}
@@ -232,22 +224,6 @@ impl<'a> GridLayouter<'a> {
/// Determine all column sizes.
fn measure_columns(&mut self, vm: &mut Vm) -> TypResult<()> {
- enum Case {
- /// The column sizing is only determined by specified linear sizes.
- PurelyLinear,
- /// The column sizing would be affected by the region size if it was
- /// smaller.
- Fitting,
- /// The column sizing is affected by the region size.
- Exact,
- /// The column sizing would be affected by the region size if it was
- /// larger.
- Overflowing,
- }
-
- // The different cases affecting constraints.
- let mut case = Case::PurelyLinear;
-
// Sum of sizes of resolved linear tracks.
let mut linear = Length::zero();
@@ -258,23 +234,18 @@ impl<'a> GridLayouter<'a> {
// fractional tracks.
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
match col {
- TrackSizing::Auto => {
- case = Case::Fitting;
- }
+ TrackSizing::Auto => {}
TrackSizing::Linear(v) => {
let resolved = v.resolve(self.regions.base.x);
*rcol = resolved;
linear += resolved;
}
- TrackSizing::Fractional(v) => {
- case = Case::Fitting;
- fr += v;
- }
+ TrackSizing::Fractional(v) => fr += v,
}
}
// Size that is not used by fixed-size columns.
- let available = self.regions.current.x - linear;
+ let available = self.regions.first.x - linear;
if available >= Length::zero() {
// Determine size of auto columns.
let (auto, count) = self.measure_auto_columns(vm, available)?;
@@ -285,25 +256,10 @@ impl<'a> GridLayouter<'a> {
if remaining >= Length::zero() {
if !fr.is_zero() {
self.grow_fractional_columns(remaining, fr);
- case = Case::Exact;
}
} else {
self.shrink_auto_columns(available, count);
- case = Case::Exact;
}
- } else if matches!(case, Case::Fitting) {
- case = Case::Overflowing;
- }
-
- // Children could depend on base.
- self.cts.base = self.regions.base.map(Some);
-
- // Set constraints depending on the case we hit.
- match case {
- Case::PurelyLinear => {}
- Case::Fitting => self.cts.min.x = Some(self.used.x),
- Case::Exact => self.cts.exact.x = Some(self.regions.current.x),
- Case::Overflowing => self.cts.max.x = Some(linear),
}
// Sum up the resolved column sizes once here.
@@ -342,7 +298,7 @@ impl<'a> GridLayouter<'a> {
pod.base.y = v.resolve(self.regions.base.y);
}
- let frame = node.layout(vm, &pod, self.styles)?.remove(0).item;
+ let frame = node.layout(vm, &pod, self.styles)?.remove(0);
resolved.set_max(frame.size.x);
}
}
@@ -403,7 +359,7 @@ impl<'a> GridLayouter<'a> {
for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) {
let mut pod = self.regions.clone();
- pod.current.x = rcol;
+ pod.first.x = rcol;
// All widths should be `rcol` except the base for auto columns.
if self.cols[x] == TrackSizing::Auto {
@@ -413,7 +369,7 @@ impl<'a> GridLayouter<'a> {
let mut sizes = node
.layout(vm, &pod, self.styles)?
.into_iter()
- .map(|frame| frame.item.size.y);
+ .map(|frame| frame.size.y);
// For each region, we want to know the maximum height any
// column requires.
@@ -443,10 +399,8 @@ impl<'a> GridLayouter<'a> {
// eaten up by any fr rows.
if self.fr.is_zero() {
let len = resolved.len();
- for (target, (current, _)) in
- resolved[.. len - 1].iter_mut().zip(self.regions.iter())
- {
- target.set_max(current.y);
+ for (region, target) in self.regions.iter().zip(&mut resolved[.. len - 1]) {
+ target.set_max(region.y);
}
}
@@ -456,7 +410,6 @@ impl<'a> GridLayouter<'a> {
for (i, frame) in frames.into_iter().enumerate() {
self.push_row(frame);
if i + 1 < len {
- self.cts.exact.y = Some(self.full);
self.finish_region(vm)?;
}
}
@@ -472,8 +425,7 @@ impl<'a> GridLayouter<'a> {
// Skip to fitting region.
let height = frame.size.y;
- while !self.regions.current.y.fits(height) && !self.regions.in_last() {
- self.cts.max.y = Some(self.used.y + height);
+ while !self.regions.first.y.fits(height) && !self.regions.in_last() {
self.finish_region(vm)?;
// Don't skip multiple regions for gutter and don't push a row.
@@ -509,7 +461,7 @@ impl<'a> GridLayouter<'a> {
let pod = Regions::one(size, base, Spec::splat(true));
let frame = node.layout(vm, &pod, self.styles)?.remove(0);
- output.push_frame(pos, frame.item);
+ output.push_frame(pos, frame);
}
pos.x += rcol;
@@ -540,7 +492,7 @@ impl<'a> GridLayouter<'a> {
let mut pos = Point::zero();
for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) {
- pod.current.x = rcol;
+ pod.first.x = rcol;
// All widths should be `rcol` except the base for auto columns.
if self.cols[x] == TrackSizing::Auto {
@@ -550,7 +502,7 @@ impl<'a> GridLayouter<'a> {
// Push the layouted frames into the individual output frames.
let frames = node.layout(vm, &pod, self.styles)?;
for (output, frame) in outputs.iter_mut().zip(frames) {
- output.push_frame(pos, frame.item);
+ output.push_frame(pos, frame);
}
}
@@ -562,7 +514,7 @@ impl<'a> GridLayouter<'a> {
/// Push a row frame into the current region.
fn push_row(&mut self, frame: Frame) {
- self.regions.current.y -= frame.size.y;
+ self.regions.first.y -= frame.size.y;
self.used.y += frame.size.y;
self.lrows.push(Row::Frame(frame));
}
@@ -574,9 +526,6 @@ impl<'a> GridLayouter<'a> {
let mut size = self.used;
if self.fr.get() > 0.0 && self.full.is_finite() {
size.y = self.full;
- self.cts.exact.y = Some(self.full);
- } else {
- self.cts.min.y = Some(size.y.min(self.full));
}
// The frame for the region.
@@ -599,13 +548,11 @@ impl<'a> GridLayouter<'a> {
pos.y += height;
}
- self.cts.base = self.regions.base.map(Some);
- self.finished.push(output.constrain(self.cts));
+ self.finished.push(Arc::new(output));
self.regions.next();
- self.full = self.regions.current.y;
+ self.full = self.regions.first.y;
self.used.y = Length::zero();
self.fr = Fractional::zero();
- self.cts = Constraints::new(self.expand);
Ok(())
}
diff --git a/src/library/hide.rs b/src/library/hide.rs
index ea2227f1..dcc73088 100644
--- a/src/library/hide.rs
+++ b/src/library/hide.rs
@@ -19,11 +19,11 @@ impl Layout for HideNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let mut frames = self.0.layout(vm, regions, styles)?;
// Clear the frames.
- for Constrained { item: frame, .. } in &mut frames {
+ for frame in &mut frames {
*frame = Arc::new(Frame { elements: vec![], ..**frame });
}
diff --git a/src/library/image.rs b/src/library/image.rs
index 05877126..000aec73 100644
--- a/src/library/image.rs
+++ b/src/library/image.rs
@@ -39,24 +39,24 @@ impl Layout for ImageNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let img = vm.images.get(self.0);
let pxw = img.width() as f64;
let pxh = img.height() as f64;
let px_ratio = pxw / pxh;
// Find out whether the image is wider or taller than the target size.
- let &Regions { current, expand, .. } = regions;
- let current_ratio = current.x / current.y;
- let wide = px_ratio > current_ratio;
+ let &Regions { first, expand, .. } = regions;
+ let region_ratio = first.x / first.y;
+ let wide = px_ratio > region_ratio;
// The space into which the image will be placed according to its fit.
let target = if expand.x && expand.y {
- current
- } else if expand.x || (!expand.y && wide && current.x.is_finite()) {
- Size::new(current.x, current.y.min(current.x.safe_div(px_ratio)))
- } else if current.y.is_finite() {
- Size::new(current.x.min(current.y * px_ratio), current.y)
+ first
+ } else if expand.x || (!expand.y && wide && first.x.is_finite()) {
+ Size::new(first.x, first.y.min(first.x.safe_div(px_ratio)))
+ } else if first.y.is_finite() {
+ Size::new(first.x.min(first.y * px_ratio), first.y)
} else {
Size::new(Length::pt(pxw), Length::pt(pxh))
};
@@ -91,7 +91,7 @@ impl Layout for ImageNode {
frame.link(url);
}
- Ok(vec![frame.constrain(Constraints::tight(regions))])
+ Ok(vec![Arc::new(frame)])
}
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index ad66f2c3..980f45c8 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -66,14 +66,12 @@ pub mod prelude {
pub use crate::diag::{with_alternative, At, StrResult, TypResult};
pub use crate::eval::{
- Arg, Args, Cast, Construct, Func, Merge, Property, Scope, Set, Show, ShowNode,
- Smart, StyleChain, StyleMap, StyleVec, Template, Value,
+ Arg, Args, Cast, Construct, Func, Layout, LayoutNode, Merge, Property, Regions,
+ Scope, Set, Show, ShowNode, Smart, StyleChain, StyleMap, StyleVec, Template,
+ Value,
};
pub use crate::frame::*;
pub use crate::geom::*;
- pub use crate::layout::{
- Constrain, Constrained, Constraints, Layout, LayoutNode, Regions,
- };
pub use crate::syntax::{Span, Spanned};
pub use crate::util::{EcoString, OptionExt};
pub use crate::Vm;
diff --git a/src/library/pad.rs b/src/library/pad.rs
index ca45a1ca..05b658bd 100644
--- a/src/library/pad.rs
+++ b/src/library/pad.rs
@@ -33,14 +33,12 @@ impl Layout for PadNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
// Layout child into padded regions.
let pod = regions.map(|size| shrink(size, self.padding));
let mut frames = self.child.layout(vm, &pod, styles)?;
- for ((current, base), Constrained { item: frame, cts }) in
- regions.iter().zip(&mut frames)
- {
+ for frame in &mut frames {
// Apply the padding inversely such that the grown size padded
// yields the frame's size.
let padded = grow(frame.size, self.padding);
@@ -51,19 +49,6 @@ impl Layout for PadNode {
let frame = Arc::make_mut(frame);
frame.size = padded;
frame.translate(offset);
-
- // Set exact and base constraints if the child had them. Also set
- // base if our padding is relative.
- let is_rel = self.padding.sum_by_axis().map(Linear::is_relative);
- cts.exact = current.filter(cts.exact.map_is_some());
- cts.base = base.filter(is_rel | cts.base.map_is_some());
-
- // Inflate min and max contraints by the padding.
- for spec in [&mut cts.min, &mut cts.max] {
- spec.as_mut()
- .zip(padding.sum_by_axis())
- .map(|(s, p)| s.as_mut().map(|v| *v += p));
- }
}
Ok(frames)
diff --git a/src/library/page.rs b/src/library/page.rs
index 07266ec6..718234c6 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -115,15 +115,12 @@ impl PageNode {
// Layout the child.
let regions = Regions::repeat(size, size, size.map(Length::is_finite));
- let mut frames: Vec<_> = child
- .layout(vm, &regions, styles)?
- .into_iter()
- .map(|c| c.item)
- .collect();
+ let mut frames = child.layout(vm, &regions, styles)?;
let header = styles.get_ref(Self::HEADER);
let footer = styles.get_ref(Self::FOOTER);
+ // Realize header and footer.
for frame in &mut frames {
let size = frame.size;
let padding = padding.resolve(size);
@@ -136,7 +133,7 @@ impl PageNode {
let w = size.x - padding.left - padding.right;
let area = Size::new(w, h);
let pod = Regions::one(area, area, area.map(Length::is_finite));
- let sub = template.layout(vm, &pod, styles)?.remove(0).item;
+ let sub = template.layout(vm, &pod, styles)?.remove(0);
Arc::make_mut(frame).push_frame(pos, sub);
}
}
diff --git a/src/library/par.rs b/src/library/par.rs
index 857d986f..eab67c53 100644
--- a/src/library/par.rs
+++ b/src/library/par.rs
@@ -86,7 +86,7 @@ impl Layout for ParNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
// Collect all text into one string used for BiDi analysis.
let text = self.collect_text();
let level = Level::from_dir(styles.get(Self::DIR));
@@ -113,7 +113,7 @@ impl Layout for ParNode {
// If the line doesn't fit anymore, we push the last fitting attempt
// into the stack and rebuild the line from its end. The resulting
// line cannot be broken up further.
- if !regions.current.x.fits(line.size.x) {
+ if !regions.first.x.fits(line.size.x) {
if let Some((last_line, last_end)) = last.take() {
lines.push(last_line);
start = last_end;
@@ -124,7 +124,7 @@ impl Layout for ParNode {
// Finish the current line if there is a mandatory line break (i.e.
// due to "\n") or if the line doesn't fit horizontally already
// since no shorter line will be possible.
- if mandatory || !regions.current.x.fits(line.size.x) {
+ if mandatory || !regions.first.x.fits(line.size.x) {
lines.push(line);
start = end;
last = None;
@@ -139,7 +139,7 @@ impl Layout for ParNode {
// Determine the paragraph's width: Fit to width if we shoudn't expand
// and there's no fractional spacing.
- let mut width = regions.current.x;
+ let mut width = regions.first.x;
if !regions.expand.x && lines.iter().all(|line| line.fr.is_zero()) {
width = lines.iter().map(|line| line.size.x).max().unwrap_or_default();
}
@@ -149,15 +149,13 @@ impl Layout for ParNode {
let mut finished = vec![];
let mut first = true;
let mut output = Frame::new(Size::with_x(width));
- let mut cts = Constraints::tight(&regions);
// Stack the lines into one frame per region.
for line in lines {
- while !regions.current.y.fits(line.size.y) && !regions.in_last() {
- finished.push(output.constrain(cts));
+ while !regions.first.y.fits(line.size.y) && !regions.in_last() {
+ finished.push(Arc::new(output));
output = Frame::new(Size::with_x(width));
regions.next();
- cts = Constraints::tight(&regions);
first = true;
}
@@ -170,11 +168,11 @@ impl Layout for ParNode {
output.size.y += frame.size.y;
output.merge_frame(pos, frame);
- regions.current.y -= line.size.y + leading;
+ regions.first.y -= line.size.y + leading;
first = false;
}
- finished.push(output.constrain(cts));
+ finished.push(Arc::new(output));
Ok(finished)
}
}
@@ -316,7 +314,7 @@ impl<'a> ParLayout<'a> {
}
ParChild::Spacing(kind) => match *kind {
SpacingKind::Linear(v) => {
- let resolved = v.resolve(regions.current.x);
+ let resolved = v.resolve(regions.first.x);
items.push(ParItem::Absolute(resolved));
ranges.push(range);
}
@@ -326,10 +324,10 @@ impl<'a> ParLayout<'a> {
}
},
ParChild::Node(node) => {
- let size = Size::new(regions.current.x, regions.base.y);
+ let size = Size::new(regions.first.x, regions.base.y);
let pod = Regions::one(size, regions.base, Spec::splat(false));
let frame = node.layout(vm, &pod, styles)?.remove(0);
- items.push(ParItem::Frame(Arc::take(frame.item)));
+ items.push(ParItem::Frame(Arc::take(frame)));
ranges.push(range);
}
}
diff --git a/src/library/place.rs b/src/library/place.rs
index 9bcd3c37..34746d5b 100644
--- a/src/library/place.rs
+++ b/src/library/place.rs
@@ -26,11 +26,11 @@ impl Layout for PlaceNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let out_of_flow = self.out_of_flow();
// The pod is the base area of the region because for absolute
- // placement we don't really care about the already used area (current).
+ // placement we don't really care about the already used area.
let pod = {
let finite = regions.base.map(Length::is_finite);
let expand = finite & (regions.expand | out_of_flow);
@@ -38,26 +38,20 @@ impl Layout for PlaceNode {
};
let mut frames = self.0.layout(vm, &pod, styles)?;
- let Constrained { item: frame, cts } = &mut frames[0];
// If expansion is off, zero all sizes so that we don't take up any
// space in our parent. Otherwise, respect the expand settings.
- let target = regions.expand.select(regions.current, Size::zero());
+ let frame = &mut frames[0];
+ let target = regions.expand.select(regions.first, Size::zero());
Arc::make_mut(frame).resize(target, Align::LEFT_TOP);
- // Set base constraint because our pod size is base and exact
- // constraints if we needed to expand or offset.
- *cts = Constraints::new(regions.expand);
- cts.base = regions.base.map(Some);
- cts.exact = regions.current.filter(regions.expand | out_of_flow);
-
Ok(frames)
}
}
impl PlaceNode {
/// Whether this node wants to be placed relative to its its parent's base
- /// origin. instead of relative to the parent's current flow/cursor
+ /// origin. Instead of relative to the parent's current flow/cursor
/// position.
pub fn out_of_flow(&self) -> bool {
self.0
diff --git a/src/library/shape.rs b/src/library/shape.rs
index 518254ce..955aef56 100644
--- a/src/library/shape.rs
+++ b/src/library/shape.rs
@@ -49,7 +49,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let mut frames;
if let Some(child) = &self.0 {
let mut padding = styles.get(Self::PADDING);
@@ -60,48 +60,47 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
// Pad the child.
let child = child.clone().padded(Sides::splat(padding));
- let mut pod = Regions::one(regions.current, regions.base, regions.expand);
+ let mut pod = Regions::one(regions.first, regions.base, regions.expand);
frames = child.layout(vm, &pod, styles)?;
// Relayout with full expansion into square region to make sure
// the result is really a square or circle.
if is_quadratic(S) {
let length = if regions.expand.x || regions.expand.y {
- let target = regions.expand.select(regions.current, Size::zero());
+ let target = regions.expand.select(regions.first, Size::zero());
target.x.max(target.y)
} else {
- let size = frames[0].item.size;
+ let size = frames[0].size;
let desired = size.x.max(size.y);
- desired.min(regions.current.x).min(regions.current.y)
+ desired.min(regions.first.x).min(regions.first.y)
};
- pod.current = Size::splat(length);
+ pod.first = Size::splat(length);
pod.expand = Spec::splat(true);
frames = child.layout(vm, &pod, styles)?;
- frames[0].cts = Constraints::tight(regions);
}
} else {
// The default size that a shape takes on if it has no child and
// enough space.
let mut size =
- Size::new(Length::pt(45.0), Length::pt(30.0)).min(regions.current);
+ Size::new(Length::pt(45.0), Length::pt(30.0)).min(regions.first);
if is_quadratic(S) {
let length = if regions.expand.x || regions.expand.y {
- let target = regions.expand.select(regions.current, Size::zero());
+ let target = regions.expand.select(regions.first, Size::zero());
target.x.max(target.y)
} else {
size.x.min(size.y)
};
size = Size::splat(length);
} else {
- size = regions.expand.select(regions.current, size);
+ size = regions.expand.select(regions.first, size);
}
- frames = vec![Frame::new(size).constrain(Constraints::tight(regions))];
+ frames = vec![Arc::new(Frame::new(size))];
}
- let frame = Arc::make_mut(&mut frames[0].item);
+ let frame = Arc::make_mut(&mut frames[0]);
// Add fill and/or stroke.
let fill = styles.get(Self::FILL);
diff --git a/src/library/stack.rs b/src/library/stack.rs
index c7985d87..6768a0a0 100644
--- a/src/library/stack.rs
+++ b/src/library/stack.rs
@@ -31,7 +31,7 @@ impl Layout for StackNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let mut layouter = StackLayouter::new(self.dir, regions);
// Spacing to insert before the next node.
@@ -106,7 +106,7 @@ pub struct StackLayouter {
/// fractional spacing.
items: Vec<StackItem>,
/// Finished frames for previous regions.
- finished: Vec<Constrained<Arc<Frame>>>,
+ finished: Vec<Arc<Frame>>,
}
/// A prepared item in a stack layout.
@@ -124,7 +124,7 @@ impl StackLayouter {
pub fn new(dir: Dir, regions: &Regions) -> Self {
let axis = dir.axis();
let expand = regions.expand;
- let full = regions.current;
+ let full = regions.first;
// Disable expansion along the block axis for children.
let mut regions = regions.clone();
@@ -149,7 +149,7 @@ impl StackLayouter {
SpacingKind::Linear(v) => {
// Resolve the linear and limit it to the remaining space.
let resolved = v.resolve(self.regions.base.get(self.axis));
- let remaining = self.regions.current.get_mut(self.axis);
+ let remaining = self.regions.first.get_mut(self.axis);
let limited = resolved.min(*remaining);
*remaining -= limited;
self.used.main += limited;
@@ -183,11 +183,11 @@ impl StackLayouter {
let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later.
- let size = frame.item.size.to_gen(self.axis);
+ let size = frame.size.to_gen(self.axis);
self.used.main += size.main;
self.used.cross.set_max(size.cross);
- *self.regions.current.get_mut(self.axis) -= size.main;
- self.items.push(StackItem::Frame(frame.item, align));
+ *self.regions.first.get_mut(self.axis) -= size.main;
+ self.items.push(StackItem::Frame(frame, align));
if i + 1 < len {
self.finish_region();
@@ -245,21 +245,16 @@ impl StackLayouter {
}
}
- // Generate tight constraints for now.
- let mut cts = Constraints::new(self.expand);
- cts.exact = self.full.map(Some);
- cts.base = self.regions.base.map(Some);
-
// Advance to the next region.
self.regions.next();
- self.full = self.regions.current;
+ self.full = self.regions.first;
self.used = Gen::zero();
self.fr = Fractional::zero();
- self.finished.push(output.constrain(cts));
+ self.finished.push(Arc::new(output));
}
/// Finish layouting and return the resulting frames.
- pub fn finish(mut self) -> Vec<Constrained<Arc<Frame>>> {
+ pub fn finish(mut self) -> Vec<Arc<Frame>> {
self.finish_region();
self.finished
}
diff --git a/src/library/transform.rs b/src/library/transform.rs
index 5cdb2f6f..84553fb5 100644
--- a/src/library/transform.rs
+++ b/src/library/transform.rs
@@ -49,11 +49,11 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
+ ) -> TypResult<Vec<Arc<Frame>>> {
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
let mut frames = self.child.layout(vm, regions, styles)?;
- for Constrained { item: frame, .. } in &mut frames {
+ for frame in &mut frames {
let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s));
let transform = Transform::translation(x, y)
.pre_concat(self.transform)