summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-11-18 00:58:32 +0100
committerLaurenz <laurmaedje@gmail.com>2021-11-18 00:58:32 +0100
commitb2e6a297893348a871fba8997017a9fc98d5674b (patch)
treec88f18c063d97d91712185c58ccf1cd745663cc1
parentd9c529347d7f46eb2f4698d256b1906c1ced6b76 (diff)
Refactoring
-rw-r--r--src/eval/template.rs4
-rw-r--r--src/frame.rs7
-rw-r--r--src/geom/spec.rs5
-rw-r--r--src/layout/mod.rs17
-rw-r--r--src/library/align.rs6
-rw-r--r--src/library/flow.rs2
-rw-r--r--src/library/grid.rs28
-rw-r--r--src/library/pad.rs94
-rw-r--r--src/library/shape.rs7
-rw-r--r--src/library/stack.rs2
-rw-r--r--src/library/transform.rs8
-rw-r--r--tests/ref/coma.pngbin55410 -> 55412 bytes
12 files changed, 104 insertions, 76 deletions
diff --git a/src/eval/template.rs b/src/eval/template.rs
index 6c1223cb..7801dc79 100644
--- a/src/eval/template.rs
+++ b/src/eval/template.rs
@@ -9,7 +9,7 @@ use crate::diag::StrResult;
use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size};
use crate::layout::{Layout, PackedNode};
use crate::library::{
- Decoration, DocumentNode, FlowChild, FlowNode, PadNode, PageNode, ParChild, ParNode,
+ Decoration, DocumentNode, FlowChild, FlowNode, PageNode, ParChild, ParNode,
PlacedNode, Spacing,
};
use crate::style::Style;
@@ -400,7 +400,7 @@ impl PageBuilder {
let Self { size, padding, hard } = self;
(!child.children.is_empty() || (keep && hard)).then(|| PageNode {
size,
- child: PadNode { padding, child: child.pack() }.pack(),
+ child: child.pack().padded(padding),
})
}
}
diff --git a/src/frame.rs b/src/frame.rs
index 94234ae9..9feb6959 100644
--- a/src/frame.rs
+++ b/src/frame.rs
@@ -64,6 +64,13 @@ impl Frame {
}
}
+ /// Move all elements in the frame by an offset.
+ pub fn translate(&mut self, offset: Point) {
+ for (point, _) in &mut self.elements {
+ *point += offset;
+ }
+ }
+
/// An iterator over all non-frame elements in this and nested frames.
pub fn elements(&self) -> Elements {
Elements { stack: vec![(0, Point::zero(), self)] }
diff --git a/src/geom/spec.rs b/src/geom/spec.rs
index 3b49b54a..82bedf9e 100644
--- a/src/geom/spec.rs
+++ b/src/geom/spec.rs
@@ -23,11 +23,6 @@ impl<T> Spec<T> {
Self { x: v.clone(), y: v }
}
- /// Borrows the individual fields.
- pub fn as_ref(&self) -> Spec<&T> {
- Spec { x: &self.x, y: &self.y }
- }
-
/// Maps the individual fields with `f`.
pub fn map<F, U>(self, mut f: F) -> Spec<U>
where
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 3ac32722..8f46c049 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -17,9 +17,9 @@ use std::rc::Rc;
use crate::font::FontStore;
use crate::frame::Frame;
-use crate::geom::{Align, Linear, Spec};
+use crate::geom::{Align, Linear, Sides, Spec};
use crate::image::ImageStore;
-use crate::library::{AlignNode, DocumentNode, MoveNode, SizedNode};
+use crate::library::{AlignNode, DocumentNode, MoveNode, PadNode, SizedNode};
use crate::Context;
/// Layout a document node into a collection of frames.
@@ -129,6 +129,19 @@ impl PackedNode {
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 { child: self, padding }.pack()
+ } else {
+ self
+ }
+ }
}
impl Layout for PackedNode {
diff --git a/src/library/align.rs b/src/library/align.rs
index 19c52f98..5aeef543 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -49,7 +49,6 @@ impl Layout for AlignNode {
pod.expand.y &= self.aligns.y.is_none();
let mut frames = self.child.layout(ctx, &pod);
-
for (Constrained { item: frame, cts }, (current, _)) in
frames.iter_mut().zip(regions.iter())
{
@@ -67,10 +66,7 @@ impl Layout for AlignNode {
let frame = Rc::make_mut(frame);
frame.size = canvas;
frame.baseline += offset.y;
-
- for (point, _) in &mut frame.elements {
- *point += offset;
- }
+ frame.translate(offset);
cts.expand = regions.expand;
cts.exact = current.to_spec().map(Some);
diff --git a/src/library/flow.rs b/src/library/flow.rs
index 185e60bb..5271eca9 100644
--- a/src/library/flow.rs
+++ b/src/library/flow.rs
@@ -61,7 +61,7 @@ impl Layout for FlowNode {
pub enum FlowChild {
/// Vertical spacing between other children.
Spacing(Spacing),
- /// A node and how to align it in the flow.
+ /// An arbitrary node.
Node(PackedNode),
}
diff --git a/src/library/grid.rs b/src/library/grid.rs
index 347132e1..6bd72388 100644
--- a/src/library/grid.rs
+++ b/src/library/grid.rs
@@ -288,17 +288,17 @@ impl<'a> GridLayouter<'a> {
for y in 0 .. self.rows.len() {
if let Some(node) = self.cell(x, y) {
let size = Size::new(available, self.regions.base.h);
- let mut regions =
+ let mut pod =
Regions::one(size, self.regions.base, Spec::splat(false));
// For linear rows, we can already resolve the correct
// base, for auto it's already correct and for fr we could
// only guess anyway.
if let TrackSizing::Linear(v) = self.rows[y] {
- regions.base.h = v.resolve(self.regions.base.h);
+ pod.base.h = v.resolve(self.regions.base.h);
}
- let frame = node.layout(ctx, &regions).remove(0).item;
+ let frame = node.layout(ctx, &pod).remove(0).item;
resolved.set_max(frame.size.w);
}
}
@@ -376,17 +376,17 @@ impl<'a> GridLayouter<'a> {
// Determine the size for each region of the row.
for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) {
- let mut regions = self.regions.clone();
- regions.mutate(|size| size.w = rcol);
+ let mut pod = self.regions.clone();
+ pod.mutate(|size| size.w = rcol);
// Set the horizontal base back to the parent region's base for
// auto columns.
if self.cols[x] == TrackSizing::Auto {
- regions.base.w = self.regions.base.w;
+ pod.base.w = self.regions.base.w;
}
let mut sizes =
- node.layout(ctx, &regions).into_iter().map(|frame| frame.item.size.h);
+ node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h);
for (target, size) in resolved.iter_mut().zip(&mut sizes) {
target.set_max(size);
@@ -475,8 +475,8 @@ impl<'a> GridLayouter<'a> {
base.h = size.h;
}
- let regions = Regions::one(size, base, Spec::splat(true));
- let frame = node.layout(ctx, &regions).remove(0);
+ let pod = Regions::one(size, base, Spec::splat(true));
+ let frame = node.layout(ctx, &pod).remove(0);
output.push_frame(pos, frame.item);
}
@@ -501,8 +501,8 @@ impl<'a> GridLayouter<'a> {
// Prepare regions.
let size = Size::new(self.used.w, resolved[0]);
- let mut regions = Regions::one(size, self.regions.base, Spec::splat(true));
- regions.backlog = resolved[1 ..]
+ let mut pod = Regions::one(size, self.regions.base, Spec::splat(true));
+ pod.backlog = resolved[1 ..]
.iter()
.map(|&h| Size::new(self.used.w, h))
.collect::<Vec<_>>()
@@ -512,16 +512,16 @@ 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) {
- regions.mutate(|size| size.w = rcol);
+ pod.mutate(|size| size.w = rcol);
// Set the horizontal base back to the parent region's base for
// auto columns.
if self.cols[x] == TrackSizing::Auto {
- regions.base.w = self.regions.base.w;
+ pod.base.w = self.regions.base.w;
}
// Push the layouted frames into the individual output frames.
- let frames = node.layout(ctx, &regions);
+ let frames = node.layout(ctx, &pod);
for (output, frame) in outputs.iter_mut().zip(frames) {
output.push_frame(pos, frame.item);
}
diff --git a/src/library/pad.rs b/src/library/pad.rs
index a1c8c6f9..829272cc 100644
--- a/src/library/pad.rs
+++ b/src/library/pad.rs
@@ -8,7 +8,6 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let right = args.named("right")?;
let bottom = args.named("bottom")?;
let body: Template = args.expect("body")?;
-
let padding = Sides::new(
left.or(all).unwrap_or_default(),
top.or(all).unwrap_or_default(),
@@ -17,17 +16,17 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
);
Ok(Value::Template(Template::from_inline(move |style| {
- PadNode { padding, child: body.pack(style) }
+ body.pack(style).padded(padding)
})))
}
/// A node that adds padding to its child.
#[derive(Debug, Hash)]
pub struct PadNode {
- /// The amount of padding.
- pub padding: Sides<Linear>,
/// The child node whose sides to pad.
pub child: PackedNode,
+ /// The amount of padding.
+ pub padding: Sides<Linear>,
}
impl Layout for PadNode {
@@ -37,43 +36,23 @@ impl Layout for PadNode {
regions: &Regions,
) -> Vec<Constrained<Rc<Frame>>> {
// Layout child into padded regions.
- let mut frames = self.child.layout(
- ctx,
- &regions.map(|size| size - self.padding.resolve(size).size()),
- );
+ let pod = regions.map(|size| shrink(size, self.padding));
+ let mut frames = self.child.layout(ctx, &pod);
for (Constrained { item: frame, cts }, (current, base)) in
frames.iter_mut().zip(regions.iter())
{
- fn solve_axis(length: Length, padding: Linear) -> Length {
- (length + padding.abs).safe_div(1.0 - padding.rel.get())
- }
-
- // Solve for the size `padded` that satisfies (approximately):
- // `padded - padding.resolve(padded).size() == size`
- let padded = Size::new(
- solve_axis(frame.size.w, self.padding.left + self.padding.right),
- solve_axis(frame.size.h, self.padding.top + self.padding.bottom),
- );
-
+ // Apply the padding inversely such that the grown size padded
+ // yields the frame's size.
+ let padded = grow(frame.size, self.padding);
let padding = self.padding.resolve(padded);
- let origin = Point::new(padding.left, padding.top);
-
- // Create a new larger frame and place the child's frame inside it.
- let empty = Frame::new(padded, frame.baseline + origin.y);
- let prev = std::mem::replace(frame, Rc::new(empty));
- let new = Rc::make_mut(frame);
- new.push_frame(origin, prev);
+ let offset = Point::new(padding.left, padding.top);
- // Inflate min and max contraints by the padding.
- for spec in [&mut cts.min, &mut cts.max] {
- if let Some(x) = spec.x.as_mut() {
- *x += padding.size().w;
- }
- if let Some(y) = spec.y.as_mut() {
- *y += padding.size().h;
- }
- }
+ // Grow the frame and translate everything in the frame inwards.
+ let frame = Rc::make_mut(frame);
+ frame.size = padded;
+ frame.baseline += offset.y;
+ frame.translate(offset);
// Set exact and base constraints if the child had them.
cts.exact.x.and_set(Some(current.w));
@@ -89,8 +68,53 @@ impl Layout for PadNode {
if self.padding.top.is_relative() || self.padding.bottom.is_relative() {
cts.base.y = Some(base.h);
}
+
+ // Inflate min and max contraints by the padding.
+ for spec in [&mut cts.min, &mut cts.max] {
+ if let Some(x) = spec.x.as_mut() {
+ *x += padding.size().w;
+ }
+ if let Some(y) = spec.y.as_mut() {
+ *y += padding.size().h;
+ }
+ }
}
frames
}
}
+
+/// Shrink a size by padding relative to the size itself.
+fn shrink(size: Size, padding: Sides<Linear>) -> Size {
+ size - padding.resolve(size).size()
+}
+
+/// Grow a size by padding relative to the grown size.
+/// This is the inverse operation to `shrink()`.
+///
+/// For the horizontal axis the derivation looks as follows.
+/// (Vertical axis is analogous.)
+///
+/// Let w be the grown target width,
+/// s be given width,
+/// l be the left padding,
+/// r be the right padding,
+/// p = l + r.
+///
+/// We want that: w - l.resolve(w) - r.resolve(w) = s
+///
+/// Thus: w - l.resolve(w) - r.resolve(w) = s
+/// <=> w - p.resolve(w) = s
+/// <=> w - p.rel * w - p.abs = s
+/// <=> (1 - p.rel) * w = s + p.abs
+/// <=> w = (s + p.abs) / (1 - p.rel)
+fn grow(size: Size, padding: Sides<Linear>) -> Size {
+ fn solve_axis(length: Length, padding: Linear) -> Length {
+ (length + padding.abs).safe_div(1.0 - padding.rel.get())
+ }
+
+ Size::new(
+ solve_axis(size.w, padding.left + padding.right),
+ solve_axis(size.h, padding.top + padding.bottom),
+ )
+}
diff --git a/src/library/shape.rs b/src/library/shape.rs
index 5d9b4152..7c543958 100644
--- a/src/library/shape.rs
+++ b/src/library/shape.rs
@@ -1,7 +1,6 @@
use std::f64::consts::SQRT_2;
use super::prelude::*;
-use super::PadNode;
use crate::util::RcExt;
/// `rect`: A rectangle with optional content.
@@ -113,10 +112,8 @@ impl Layout for ShapeNode {
if matches!(self.kind, ShapeKind::Circle | ShapeKind::Ellipse) {
// Padding with this ratio ensures that a rectangular child fits
// perfectly into a circle / an ellipse.
- storage = PadNode {
- padding: Sides::splat(Relative::new(0.5 - SQRT_2 / 4.0).into()),
- child: child.clone(),
- };
+ let ratio = Relative::new(0.5 - SQRT_2 / 4.0);
+ storage = child.clone().padded(Sides::splat(ratio.into()));
node = &storage;
}
diff --git a/src/library/stack.rs b/src/library/stack.rs
index cc02592f..3d91bec7 100644
--- a/src/library/stack.rs
+++ b/src/library/stack.rs
@@ -75,7 +75,7 @@ impl Layout for StackNode {
pub enum StackChild {
/// Spacing between other nodes.
Spacing(Spacing),
- /// Any block node and how to align it in the stack.
+ /// An arbitrary node.
Node(PackedNode),
}
diff --git a/src/library/transform.rs b/src/library/transform.rs
index ef9caf2e..10752358 100644
--- a/src/library/transform.rs
+++ b/src/library/transform.rs
@@ -30,14 +30,10 @@ impl Layout for MoveNode {
for (Constrained { item: frame, .. }, (_, base)) in
frames.iter_mut().zip(regions.iter())
{
- let offset = Point::new(
+ Rc::make_mut(frame).translate(Point::new(
self.offset.x.map(|x| x.resolve(base.w)).unwrap_or_default(),
self.offset.y.map(|y| y.resolve(base.h)).unwrap_or_default(),
- );
-
- for (point, _) in &mut Rc::make_mut(frame).elements {
- *point += offset;
- }
+ ));
}
frames
diff --git a/tests/ref/coma.png b/tests/ref/coma.png
index 04356991..d4c6c3de 100644
--- a/tests/ref/coma.png
+++ b/tests/ref/coma.png
Binary files differ