summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-02-22 12:42:02 +0100
committerLaurenz <laurmaedje@gmail.com>2022-02-22 12:42:02 +0100
commit2bf32c51bceb2f3a8b7ebea3d7c7d6d96757591b (patch)
tree3524388a7394dd35ccef10b89a7a034e6ae1ab60 /src/layout
parentc7e52f20483971a39f54c56700b31980f744a410 (diff)
Remove layout cache
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/constraints.rs88
-rw-r--r--src/layout/incremental.rs451
-rw-r--r--src/layout/mod.rs334
-rw-r--r--src/layout/regions.rs102
4 files changed, 0 insertions, 975 deletions
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/mod.rs b/src/layout/mod.rs
deleted file mode 100644
index afb2621b..00000000
--- a/src/layout/mod.rs
+++ /dev/null
@@ -1,334 +0,0 @@
-//! 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::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::library::{AlignNode, PadNode, TransformNode, MOVE};
-use crate::util::Prehashed;
-use crate::Vm;
-
-/// A node that can be layouted into a sequence of regions.
-///
-/// Layout return one frame per used region alongside constraints that define
-/// whether the result is reusable in other regions.
-pub trait Layout {
- /// Layout the node into the given regions, producing constrained frames.
- fn layout(
- &self,
- vm: &mut Vm,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>>;
-
- /// Convert to a packed node.
- fn pack(self) -> LayoutNode
- where
- Self: Debug + Hash + Sized + Sync + Send + 'static,
- {
- LayoutNode::new(self)
- }
-}
-
-/// A type-erased layouting node with a precomputed hash.
-#[derive(Clone, Hash)]
-pub struct LayoutNode(Arc<Prehashed<dyn Bounds>>);
-
-impl LayoutNode {
- /// Pack any layoutable node.
- pub fn new<T>(node: T) -> Self
- where
- T: Layout + Debug + Hash + Sync + Send + 'static,
- {
- Self(Arc::new(Prehashed::new(node)))
- }
-
- /// Check whether the contained node is a specific layout node.
- pub fn is<T: 'static>(&self) -> bool {
- self.0.as_any().is::<T>()
- }
-
- /// The type id of this node.
- pub fn id(&self) -> TypeId {
- self.0.as_any().type_id()
- }
-
- /// Try to downcast to a specific layout node.
- pub fn downcast<T>(&self) -> Option<&T>
- where
- T: Layout + Debug + Hash + 'static,
- {
- self.0.as_any().downcast_ref()
- }
-
- /// Force a size for this node.
- pub fn sized(self, sizing: Spec<Option<Linear>>) -> Self {
- if sizing.any(Option::is_some) {
- SizedNode { sizing, child: self }.pack()
- } else {
- self
- }
- }
-
- /// Fill the frames resulting from a node.
- pub fn filled(self, fill: Paint) -> Self {
- FillNode { fill, child: self }.pack()
- }
-
- /// Stroke the frames resulting from a node.
- pub fn stroked(self, stroke: Stroke) -> Self {
- StrokeNode { stroke, child: self }.pack()
- }
-
- /// Set alignments for this node.
- pub fn aligned(self, aligns: Spec<Option<Align>>) -> Self {
- if aligns.any(Option::is_some) {
- AlignNode { aligns, child: self }.pack()
- } else {
- self
- }
- }
-
- /// Pad this node at the sides.
- pub fn padded(self, padding: Sides<Linear>) -> Self {
- if !padding.left.is_zero()
- || !padding.top.is_zero()
- || !padding.right.is_zero()
- || !padding.bottom.is_zero()
- {
- PadNode { padding, child: self }.pack()
- } else {
- self
- }
- }
-
- /// Transform this node's contents without affecting layout.
- pub fn moved(self, offset: Point) -> Self {
- if !offset.is_zero() {
- TransformNode::<MOVE> {
- transform: Transform::translation(offset.x, offset.y),
- child: self,
- }
- .pack()
- } else {
- self
- }
- }
-}
-
-impl Layout for LayoutNode {
- #[track_caller]
- fn layout(
- &self,
- 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)
- }
- }
-
- fn pack(self) -> LayoutNode {
- self
- }
-}
-
-impl Default for LayoutNode {
- fn default() -> Self {
- EmptyNode.pack()
- }
-}
-
-impl Debug for LayoutNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-impl PartialEq for LayoutNode {
- fn eq(&self, other: &Self) -> bool {
- self.0.eq(&other.0)
- }
-}
-
-trait Bounds: Layout + Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
-}
-
-impl<T> Bounds for T
-where
- T: Layout + Debug + Hash + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
-
-/// A layout node that produces an empty frame.
-///
-/// The packed version of this is returned by [`PackedNode::default`].
-#[derive(Debug, Hash)]
-struct EmptyNode;
-
-impl Layout for EmptyNode {
- fn layout(
- &self,
- _: &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)])
- }
-}
-
-/// Fix the size of a node.
-#[derive(Debug, Hash)]
-struct SizedNode {
- /// How to size the node horizontally and vertically.
- sizing: Spec<Option<Linear>>,
- /// The node to be sized.
- child: LayoutNode,
-}
-
-impl Layout for SizedNode {
- fn layout(
- &self,
- 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));
-
- // The "pod" is the region into which the child will be layouted.
- let pod = {
- // Resolve the sizing to a concrete size.
- let size = self
- .sizing
- .zip(regions.base)
- .map(|(s, b)| s.map(|v| v.resolve(b)))
- .unwrap_or(regions.current);
-
- // Select the appropriate base and expansion for the child depending
- // on whether it is automatically or linearly sized.
- let base = is_auto.select(regions.base, size);
- let expand = regions.expand | !is_auto;
-
- Regions::one(size, base, expand)
- };
-
- 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);
- 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)
- }
-}
-
-/// Fill the frames resulting from a node.
-#[derive(Debug, Hash)]
-struct FillNode {
- /// How to fill the frames resulting from the `child`.
- fill: Paint,
- /// The node to fill.
- child: LayoutNode,
-}
-
-impl Layout for FillNode {
- fn layout(
- &self,
- vm: &mut Vm,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
- let mut frames = self.child.layout(vm, regions, styles)?;
- for Constrained { item: frame, .. } in &mut frames {
- let shape = Shape::filled(Geometry::Rect(frame.size), self.fill);
- Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
- }
- Ok(frames)
- }
-}
-
-/// Stroke the frames resulting from a node.
-#[derive(Debug, Hash)]
-struct StrokeNode {
- /// How to stroke the frames resulting from the `child`.
- stroke: Stroke,
- /// The node to stroke.
- child: LayoutNode,
-}
-
-impl Layout for StrokeNode {
- fn layout(
- &self,
- vm: &mut Vm,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
- let mut frames = self.child.layout(vm, regions, styles)?;
- for Constrained { item: frame, .. } in &mut frames {
- let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke);
- Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
- }
- Ok(frames)
- }
-}
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),
- )
- }))
- }
-}