X/Y abstractions

This commit is contained in:
Laurenz 2021-11-26 16:32:06 +01:00
parent 393d74f9bb
commit 3a15922d2f
29 changed files with 414 additions and 487 deletions

View File

@ -226,7 +226,3 @@ impl Debug for Arg {
Debug::fmt(&self.value.v, f)
}
}
dynamic! {
Args: "arguments",
}

View File

@ -494,6 +494,10 @@ impl Eval for ClosureExpr {
// Put the remaining arguments into the sink.
if let Some(sink) = &sink {
dynamic! {
Args: "arguments",
}
ctx.scopes.def_mut(sink, args.take());
}

View File

@ -3,7 +3,7 @@ use std::convert::TryFrom;
use super::{Dynamic, Value};
use crate::diag::StrResult;
use crate::geom::{Align, Get, Spec};
use crate::geom::{Align, Spec, SpecAxis};
use crate::util::EcoString;
use Value::*;
@ -94,14 +94,18 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
if let (Some(&a), Some(&b)) =
(a.downcast::<Align>(), b.downcast::<Align>())
{
if a.axis() == b.axis() {
return Err(format!("cannot add two {:?} alignments", a.axis()));
dynamic! {
Spec<Align>: "2d alignment",
}
let mut aligns = Spec::default();
aligns.set(a.axis(), Some(a));
aligns.set(b.axis(), Some(b));
return Ok(Dyn(Dynamic::new(aligns)));
return if a.axis() != b.axis() {
Ok(Dyn(Dynamic::new(match a.axis() {
SpecAxis::Horizontal => Spec { x: a, y: b },
SpecAxis::Vertical => Spec { x: b, y: a },
})))
} else {
Err(format!("cannot add two {:?} alignments", a.axis()))
};
}
}

View File

@ -266,8 +266,8 @@ impl<'a> PdfExporter<'a> {
let mut page_writer = self.writer.page(page_id);
page_writer.parent(page_tree_ref);
let w = page.size.w.to_f32();
let h = page.size.h.to_f32();
let w = page.size.x.to_f32();
let h = page.size.y.to_f32();
page_writer.media_box(Rect::new(0.0, 0.0, w, h));
page_writer.contents(content_id);
@ -366,7 +366,7 @@ impl<'a> PageExporter<'a> {
fn export(mut self, frame: &Frame) -> Page {
// Make the coordinate system start at the top-left.
self.bottom = frame.size.h.to_f32();
self.bottom = frame.size.y.to_f32();
self.content.transform([1.0, 0.0, 0.0, -1.0, 0.0, self.bottom]);
self.write_frame(&frame);
Page {
@ -397,8 +397,8 @@ impl<'a> PageExporter<'a> {
self.transform(translation.pre_concat(group.transform));
if group.clips {
let w = group.frame.size.w.to_f32();
let h = group.frame.size.h.to_f32();
let w = group.frame.size.x.to_f32();
let h = group.frame.size.y.to_f32();
self.content.move_to(0.0, 0.0);
self.content.line_to(w, 0.0);
self.content.line_to(w, h);
@ -471,8 +471,8 @@ impl<'a> PageExporter<'a> {
match shape.geometry {
Geometry::Rect(size) => {
let w = size.w.to_f32();
let h = size.h.to_f32();
let w = size.x.to_f32();
let h = size.y.to_f32();
if w > 0.0 && h > 0.0 {
self.content.rect(x, y, w, h);
}
@ -533,8 +533,8 @@ impl<'a> PageExporter<'a> {
fn write_image(&mut self, x: f32, y: f32, id: ImageId, size: Size) {
self.image_map.insert(id);
let name = format_eco!("Im{}", self.image_map.map(id));
let w = size.w.to_f32();
let h = size.h.to_f32();
let w = size.x.to_f32();
let h = size.y.to_f32();
self.content.save_state();
self.content.transform([w, 0.0, 0.0, -h, x, y + h]);
self.content.x_object(Name(name.as_bytes()));
@ -550,8 +550,8 @@ impl<'a> PageExporter<'a> {
// Compute the bounding box of the transformed link.
for point in [
pos,
pos + Point::with_x(size.w),
pos + Point::with_y(size.h),
pos + Point::with_x(size.x),
pos + Point::with_y(size.y),
pos + size.to_point(),
] {
let t = point.transform(self.state.transform);

View File

@ -25,7 +25,7 @@ impl Frame {
#[track_caller]
pub fn new(size: Size) -> Self {
assert!(size.is_finite());
Self { size, baseline: size.h, elements: vec![] }
Self { size, baseline: size.y, elements: vec![] }
}
/// Add an element at a position in the background.
@ -58,13 +58,15 @@ impl Frame {
/// Resize the frame to a new size, distributing new space according to the
/// given alignments.
pub fn resize(&mut self, new: Size, aligns: Spec<Align>) {
let offset = Point::new(
aligns.x.resolve(new.w - self.size.w),
aligns.y.resolve(new.h - self.size.h),
);
self.size = new;
self.baseline += offset.y;
self.translate(offset);
if self.size != new {
let offset = Point::new(
aligns.x.resolve(new.x - self.size.x),
aligns.y.resolve(new.y - self.size.y),
);
self.size = new;
self.baseline += offset.y;
self.translate(offset);
}
}
/// Move the contents of the frame by an offset.

View File

@ -53,21 +53,6 @@ impl Gen<Length> {
pub fn to_point(self, main: SpecAxis) -> Point {
self.to_spec(main).to_point()
}
/// Convert to a size.
pub fn to_size(self, main: SpecAxis) -> Size {
self.to_spec(main).to_size()
}
}
impl<T> Gen<Option<T>> {
/// Unwrap the individual fields.
pub fn unwrap_or(self, other: Gen<T>) -> Gen<T> {
Gen {
cross: self.cross.unwrap_or(other.cross),
main: self.main.unwrap_or(other.main),
}
}
}
impl<T> Get<GenAxis> for Gen<T> {

View File

@ -42,7 +42,7 @@ impl Linear {
}
/// Whether there is a linear component.
pub fn is_relative(&self) -> bool {
pub fn is_relative(self) -> bool {
!self.rel.is_zero()
}
}

View File

@ -16,7 +16,6 @@ mod point;
mod relative;
mod scalar;
mod sides;
mod size;
mod spec;
mod transform;
@ -34,7 +33,6 @@ pub use point::*;
pub use relative::*;
pub use scalar::*;
pub use sides::*;
pub use size::*;
pub use spec::*;
pub use transform::*;

View File

@ -26,9 +26,9 @@ impl Path {
let point = Point::new;
let mut path = Self::new();
path.move_to(point(z, z));
path.line_to(point(size.w, z));
path.line_to(point(size.w, size.h));
path.line_to(point(z, size.h));
path.line_to(point(size.x, z));
path.line_to(point(size.x, size.y));
path.line_to(point(z, size.y));
path.close_path();
path
}
@ -37,8 +37,8 @@ impl Path {
pub fn ellipse(size: Size) -> Self {
// https://stackoverflow.com/a/2007782
let z = Length::zero();
let rx = size.w / 2.0;
let ry = size.h / 2.0;
let rx = size.x / 2.0;
let ry = size.y / 2.0;
let m = 0.551784;
let mx = m * rx;
let my = m * ry;

View File

@ -2,7 +2,7 @@ use super::*;
/// A 64-bit float that implements `Eq`, `Ord` and `Hash`.
///
/// Panics if its `NaN` during any of those operations.
/// Panics if it's `NaN` during any of those operations.
#[derive(Default, Copy, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Scalar(pub f64);

View File

@ -33,11 +33,13 @@ impl<T> Sides<T> {
}
}
impl Sides<Length> {
/// A size with `left` and `right` summed into `width`, and `top` and
/// `bottom` summed into `height`.
pub fn size(self) -> Size {
Size::new(self.left + self.right, self.top + self.bottom)
impl<T> Sides<T>
where
T: Add,
{
/// Sums up `left` and `right` into `x`, and `top` and `bottom` into `y`.
pub fn sum_by_axis(self) -> Spec<T::Output> {
Spec::new(self.left + self.right, self.top + self.bottom)
}
}
@ -45,10 +47,10 @@ impl Sides<Linear> {
/// Resolve the linear sides relative to the given `size`.
pub fn resolve(self, size: Size) -> Sides<Length> {
Sides {
left: self.left.resolve(size.w),
top: self.top.resolve(size.h),
right: self.right.resolve(size.w),
bottom: self.bottom.resolve(size.h),
left: self.left.resolve(size.x),
top: self.top.resolve(size.y),
right: self.right.resolve(size.x),
bottom: self.bottom.resolve(size.y),
}
}
}

View File

@ -1,131 +0,0 @@
use super::*;
/// A size in 2D.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct Size {
/// The width.
pub w: Length,
/// The height.
pub h: Length,
}
impl Size {
/// The zero size.
pub const fn zero() -> Self {
Self { w: Length::zero(), h: Length::zero() }
}
/// Create a new size from width and height.
pub const fn new(w: Length, h: Length) -> Self {
Self { w, h }
}
/// Create an instance with two equal components.
pub const fn splat(v: Length) -> Self {
Self { w: v, h: v }
}
/// Whether the other size fits into this one (smaller width and height).
pub fn fits(self, other: Self) -> bool {
self.w.fits(other.w) && self.h.fits(other.h)
}
/// Whether both components are finite.
pub fn is_finite(self) -> bool {
self.w.is_finite() && self.h.is_finite()
}
/// Whether any of the two components is infinite.
pub fn is_infinite(self) -> bool {
self.w.is_infinite() || self.h.is_infinite()
}
/// Convert to a point.
pub const fn to_point(self) -> Point {
Point::new(self.w, self.h)
}
/// Convert to a Spec.
pub const fn to_spec(self) -> Spec<Length> {
Spec::new(self.w, self.h)
}
/// Convert to the generic representation.
pub const fn to_gen(self, main: SpecAxis) -> Gen<Length> {
match main {
SpecAxis::Horizontal => Gen::new(self.h, self.w),
SpecAxis::Vertical => Gen::new(self.w, self.h),
}
}
}
impl Get<SpecAxis> for Size {
type Component = Length;
fn get(self, axis: SpecAxis) -> Length {
match axis {
SpecAxis::Horizontal => self.w,
SpecAxis::Vertical => self.h,
}
}
fn get_mut(&mut self, axis: SpecAxis) -> &mut Length {
match axis {
SpecAxis::Horizontal => &mut self.w,
SpecAxis::Vertical => &mut self.h,
}
}
}
impl Debug for Size {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Size({:?}, {:?})", self.w, self.h)
}
}
impl Neg for Size {
type Output = Self;
fn neg(self) -> Self {
Self { w: -self.w, h: -self.h }
}
}
impl Add for Size {
type Output = Self;
fn add(self, other: Self) -> Self {
Self { w: self.w + other.w, h: self.h + other.h }
}
}
sub_impl!(Size - Size -> Size);
impl Mul<f64> for Size {
type Output = Self;
fn mul(self, other: f64) -> Self {
Self { w: self.w * other, h: self.h * other }
}
}
impl Mul<Size> for f64 {
type Output = Size;
fn mul(self, other: Size) -> Size {
other * self
}
}
impl Div<f64> for Size {
type Output = Self;
fn div(self, other: f64) -> Self {
Self { w: self.w / other, h: self.h / other }
}
}
assign_impl!(Size -= Size);
assign_impl!(Size += Size);
assign_impl!(Size *= f64);
assign_impl!(Size /= f64);

View File

@ -1,7 +1,10 @@
use std::any::Any;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
use super::*;
/// A container with a horizontal and vertical component.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct Spec<T> {
/// The horizontal component.
pub x: T,
@ -31,9 +34,18 @@ impl<T> Spec<T> {
Spec { x: f(self.x), y: f(self.y) }
}
/// Convert from `&Spec<T>` to `Spec<&T>`.
pub fn as_ref(&self) -> Spec<&T> {
Spec { x: &self.x, y: &self.y }
}
/// Convert from `&mut Spec<T>` to `Spec<&mut T>`.
pub fn as_mut(&mut self) -> Spec<&mut T> {
Spec { x: &mut self.x, y: &mut self.y }
}
/// Zip two instances into an instance over a tuple.
pub fn zip<U>(self, other: impl Into<Spec<U>>) -> Spec<(T, U)> {
let other = other.into();
pub fn zip<U>(self, other: Spec<U>) -> Spec<(T, U)> {
Spec {
x: (self.x, other.x),
y: (self.y, other.y),
@ -56,6 +68,14 @@ impl<T> Spec<T> {
f(&self.x) && f(&self.y)
}
/// Filter the individual fields with a mask.
pub fn filter(self, mask: Spec<bool>) -> Spec<Option<T>> {
Spec {
x: if mask.x { Some(self.x) } else { None },
y: if mask.y { Some(self.y) } else { None },
}
}
/// Convert to the generic representation.
pub fn to_gen(self, main: SpecAxis) -> Gen<T> {
match main {
@ -65,39 +85,6 @@ impl<T> Spec<T> {
}
}
impl From<Size> for Spec<Length> {
fn from(size: Size) -> Self {
size.to_spec()
}
}
impl Spec<Length> {
/// The zero value.
pub fn zero() -> Self {
Self { x: Length::zero(), y: Length::zero() }
}
/// Convert to a point.
pub fn to_point(self) -> Point {
Point::new(self.x, self.y)
}
/// Convert to a size.
pub fn to_size(self) -> Size {
Size::new(self.x, self.y)
}
}
impl<T> Spec<Option<T>> {
/// Unwrap the individual fields.
pub fn unwrap_or(self, other: Spec<T>) -> Spec<T> {
Spec {
x: self.x.unwrap_or(other.x),
y: self.y.unwrap_or(other.y),
}
}
}
impl<T> Get<SpecAxis> for Spec<T> {
type Component = T;
@ -116,9 +103,20 @@ impl<T> Get<SpecAxis> for Spec<T> {
}
}
impl<T: Debug> Debug for Spec<T> {
impl<T> Debug for Spec<T>
where
T: Debug + 'static,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Spec({:?}, {:?})", self.x, self.y)
if let Spec { x: Some(x), y: Some(y) } =
self.as_ref().map(|v| (v as &dyn Any).downcast_ref::<Align>())
{
write!(f, "{:?}-{:?}", x, y)
} else if (&self.x as &dyn Any).is::<Length>() {
write!(f, "Size({:?}, {:?})", self.x, self.y)
} else {
write!(f, "Spec({:?}, {:?})", self.x, self.y)
}
}
}
@ -159,3 +157,148 @@ impl Debug for SpecAxis {
})
}
}
/// A size in 2D.
pub type Size = Spec<Length>;
impl Size {
/// The zero value.
pub fn zero() -> Self {
Self { x: Length::zero(), y: Length::zero() }
}
/// Whether the other size fits into this one (smaller width and height).
pub fn fits(self, other: Self) -> bool {
self.x.fits(other.x) && self.y.fits(other.y)
}
/// Whether both components are finite.
pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite()
}
/// Whether any of the two components is infinite.
pub fn is_infinite(self) -> bool {
self.x.is_infinite() || self.y.is_infinite()
}
/// Convert to a point.
pub fn to_point(self) -> Point {
Point::new(self.x, self.y)
}
}
impl Neg for Size {
type Output = Self;
fn neg(self) -> Self {
Self { x: -self.x, y: -self.y }
}
}
impl Add for Size {
type Output = Self;
fn add(self, other: Self) -> Self {
Self { x: self.x + other.x, y: self.y + other.y }
}
}
sub_impl!(Size - Size -> Size);
impl Mul<f64> for Size {
type Output = Self;
fn mul(self, other: f64) -> Self {
Self { x: self.x * other, y: self.y * other }
}
}
impl Mul<Size> for f64 {
type Output = Size;
fn mul(self, other: Size) -> Size {
other * self
}
}
impl Div<f64> for Size {
type Output = Self;
fn div(self, other: f64) -> Self {
Self { x: self.x / other, y: self.y / other }
}
}
assign_impl!(Size -= Size);
assign_impl!(Size += Size);
assign_impl!(Size *= f64);
assign_impl!(Size /= f64);
impl<T> Spec<Option<T>> {
/// Whether the individual fields are some.
pub fn map_is_some(&self) -> Spec<bool> {
self.as_ref().map(Option::is_some)
}
/// Whether the individual fields are none.
pub fn map_is_none(&self) -> Spec<bool> {
self.as_ref().map(Option::is_none)
}
/// Unwrap the individual fields.
pub fn unwrap_or(self, other: Spec<T>) -> Spec<T> {
Spec {
x: self.x.unwrap_or(other.x),
y: self.y.unwrap_or(other.y),
}
}
}
impl Spec<bool> {
/// Select `t.x` if `self.x` is true and `f.x` otherwise and same for `y`.
pub fn select<T>(self, t: Spec<T>, f: Spec<T>) -> Spec<T> {
Spec {
x: if self.x { t.x } else { f.x },
y: if self.y { t.y } else { f.y },
}
}
}
impl Not for Spec<bool> {
type Output = Self;
fn not(self) -> Self::Output {
Self { x: !self.x, y: !self.y }
}
}
impl BitOr for Spec<bool> {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self { x: self.x | rhs.x, y: self.y | rhs.y }
}
}
impl BitAnd for Spec<bool> {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
Self { x: self.x & rhs.x, y: self.y & rhs.y }
}
}
impl BitOrAssign for Spec<bool> {
fn bitor_assign(&mut self, rhs: Self) {
self.x |= rhs.x;
self.y |= rhs.y;
}
}
impl BitAndAssign for Spec<bool> {
fn bitand_assign(&mut self, rhs: Self) {
self.x &= rhs.x;
self.y &= rhs.y;
}
}

View File

@ -2,7 +2,7 @@ use std::rc::Rc;
use super::Regions;
use crate::frame::Frame;
use crate::geom::{Length, Linear, Size, Spec};
use crate::geom::{Length, Size, Spec};
/// Constrain a frame with constraints.
pub trait Constrain {
@ -65,8 +65,8 @@ impl Constraints {
Self {
min: Spec::default(),
max: Spec::default(),
exact: regions.current.to_spec().map(Some),
base: regions.base.to_spec().map(Some),
exact: regions.current.map(Some),
base: regions.base.map(Some),
expand: regions.expand,
}
}
@ -80,18 +80,6 @@ impl Constraints {
&& verify(self.exact, current, Length::approx_eq)
&& verify(self.base, base, Length::approx_eq)
}
/// Set the appropriate base constraints for linear width and height sizing.
pub fn set_base_if_linear(&mut self, base: Size, sizing: Spec<Option<Linear>>) {
// The full sizes need to be equal if there is a relative component in
// the sizes.
if sizing.x.map_or(false, |l| l.is_relative()) {
self.base.x = Some(base.w);
}
if sizing.y.map_or(false, |l| l.is_relative()) {
self.base.y = Some(base.h);
}
}
}
/// Verify a single constraint.

View File

@ -51,7 +51,7 @@ impl Regions {
/// Whether the current region is full and a region break is called for.
pub fn is_full(&self) -> bool {
Length::zero().fits(self.current.h) && !self.in_last()
Length::zero().fits(self.current.y) && !self.in_last()
}
/// Whether `current` is the last usable region.

View File

@ -29,10 +29,9 @@ impl Layout for AlignNode {
ctx: &mut LayoutContext,
regions: &Regions,
) -> Vec<Constrained<Rc<Frame>>> {
// Along axes with specified alignment, the child doesn't need to expand.
// The child only needs to expand along an axis if there's no alignment.
let mut pod = regions.clone();
pod.expand.x &= self.aligns.x.is_none();
pod.expand.y &= self.aligns.y.is_none();
pod.expand &= self.aligns.map_is_none();
// Layout the child.
let mut frames = self.child.layout(ctx, &pod);
@ -40,23 +39,17 @@ impl Layout for AlignNode {
for (Constrained { item: frame, cts }, (current, base)) in
frames.iter_mut().zip(regions.iter())
{
// The possibly larger size in which we align the frame.
let new = Size::new(
if regions.expand.x { current.w } else { frame.size.w },
if regions.expand.y { current.h } else { frame.size.h },
);
let aligns = self.aligns.unwrap_or(Spec::new(Align::Left, Align::Top));
Rc::make_mut(frame).resize(new, aligns);
// Align in the target size. The target size depends on whether we
// should expand.
let target = regions.expand.select(current, frame.size);
let default = Spec::new(Align::Left, Align::Top);
let aligns = self.aligns.unwrap_or(default);
Rc::make_mut(frame).resize(target, aligns);
// Set constraints.
cts.expand = regions.expand;
cts.base.x.and_set(Some(base.w));
cts.base.y.and_set(Some(base.h));
cts.exact = Spec::new(
regions.expand.x.then(|| current.w),
regions.expand.y.then(|| current.h),
);
cts.base = base.filter(cts.base.map_is_some());
cts.exact = current.filter(regions.expand);
}
frames

View File

@ -157,10 +157,10 @@ impl<'a> FlowLayouter<'a> {
/// Layout absolute spacing.
fn layout_absolute(&mut self, amount: Linear) {
// Resolve the linear, limiting it to the remaining available space.
let resolved = amount.resolve(self.full.h);
let limited = resolved.min(self.regions.current.h);
self.regions.current.h -= limited;
self.used.h += limited;
let resolved = amount.resolve(self.full.y);
let limited = resolved.min(self.regions.current.y);
self.regions.current.y -= limited;
self.used.y += limited;
self.items.push(FlowItem::Absolute(resolved));
}
@ -195,9 +195,9 @@ impl<'a> FlowLayouter<'a> {
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;
self.used.h += size.h;
self.used.w.set_max(size.w);
self.regions.current.h -= size.h;
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));
if i + 1 < len {
@ -210,16 +210,13 @@ impl<'a> FlowLayouter<'a> {
fn finish_region(&mut self) {
// Determine the size of the flow in this region dependening on whether
// the region expands.
let mut size = Size::new(
if self.expand.x { self.full.w } else { self.used.w },
if self.expand.y { self.full.h } else { self.used.h },
);
let mut size = self.expand.select(self.full, self.used);
// Account for fractional spacing in the size calculation.
let remaining = self.full.h - self.used.h;
if self.fr.get() > 0.0 && self.full.h.is_finite() {
self.used.h = self.full.h;
size.h = self.full.h;
let remaining = self.full.y - self.used.y;
if self.fr.get() > 0.0 && self.full.y.is_finite() {
self.used.y = self.full.y;
size.y = self.full.y;
}
let mut output = Frame::new(size);
@ -243,10 +240,10 @@ impl<'a> FlowLayouter<'a> {
ruler = ruler.max(aligns.y);
// Align horizontally and vertically.
let x = aligns.x.resolve(size.w - frame.size.w);
let y = before + ruler.resolve(size.h - self.used.h);
let x = aligns.x.resolve(size.x - frame.size.x);
let y = before + ruler.resolve(size.y - self.used.y);
let pos = Point::new(x, y);
before += frame.size.h;
before += frame.size.y;
// The baseline of the flow is that of the first frame.
if first {
@ -261,8 +258,8 @@ impl<'a> FlowLayouter<'a> {
// Generate tight constraints for now.
let mut cts = Constraints::new(self.expand);
cts.exact = self.full.to_spec().map(Some);
cts.base = self.regions.base.to_spec().map(Some);
cts.exact = self.full.map(Some);
cts.base = self.regions.base.map(Some);
// Advance to the next region.
self.regions.next();

View File

@ -178,7 +178,7 @@ impl<'a> GridLayouter<'a> {
rcols: vec![Length::zero(); cols.len()],
cols,
rows,
full: regions.current.h,
full: regions.current.y,
regions,
used: Size::zero(),
fr: Fractional::zero(),
@ -220,7 +220,7 @@ impl<'a> GridLayouter<'a> {
case = Case::Fitting;
}
TrackSizing::Linear(v) => {
let resolved = v.resolve(self.regions.base.w);
let resolved = v.resolve(self.regions.base.x);
*rcol = resolved;
linear += resolved;
}
@ -232,7 +232,7 @@ impl<'a> GridLayouter<'a> {
}
// Size that is not used by fixed-size columns.
let available = self.regions.current.w - linear;
let available = self.regions.current.x - linear;
if available >= Length::zero() {
// Determine size of auto columns.
let (auto, count) = self.measure_auto_columns(ctx, available);
@ -254,18 +254,18 @@ impl<'a> GridLayouter<'a> {
}
// Children could depend on base.
self.cts.base = self.regions.base.to_spec().map(Some);
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.w),
Case::Exact => self.cts.exact.x = Some(self.regions.current.w),
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.
self.used.w = self.rcols.iter().sum();
self.used.x = self.rcols.iter().sum();
}
/// Measure the size that is available to auto columns.
@ -287,7 +287,7 @@ impl<'a> GridLayouter<'a> {
let mut resolved = Length::zero();
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 size = Size::new(available, self.regions.base.y);
let mut pod =
Regions::one(size, self.regions.base, Spec::splat(false));
@ -295,11 +295,11 @@ impl<'a> GridLayouter<'a> {
// base, for auto it's already correct and for fr we could
// only guess anyway.
if let TrackSizing::Linear(v) = self.rows[y] {
pod.base.h = v.resolve(self.regions.base.h);
pod.base.y = v.resolve(self.regions.base.y);
}
let frame = node.layout(ctx, &pod).remove(0).item;
resolved.set_max(frame.size.w);
resolved.set_max(frame.size.x);
}
}
@ -382,17 +382,15 @@ 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) {
// All widths should be `rcol` except the base for auto columns.
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.
pod.mutate(|size| size.x = rcol);
if self.cols[x] == TrackSizing::Auto {
pod.base.w = self.regions.base.w;
pod.base.x = self.regions.base.x;
}
let mut sizes =
node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h);
node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.y);
// For each region, we want to know the maximum height any
// column requires.
@ -425,7 +423,7 @@ impl<'a> GridLayouter<'a> {
for (target, (current, _)) in
resolved[.. len - 1].iter_mut().zip(self.regions.iter())
{
target.set_max(current.h);
target.set_max(current.y);
}
}
@ -444,13 +442,13 @@ impl<'a> GridLayouter<'a> {
/// Layout a row with linear height. Such a row cannot break across multiple
/// regions, but it may force a region break.
fn layout_linear_row(&mut self, ctx: &mut LayoutContext, v: Linear, y: usize) {
let resolved = v.resolve(self.regions.base.h);
let resolved = v.resolve(self.regions.base.y);
let frame = self.layout_single_row(ctx, resolved, y);
// Skip to fitting region.
let height = frame.size.h;
while !self.regions.current.h.fits(height) && !self.regions.in_last() {
self.cts.max.y = Some(self.used.h + height);
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);
self.finish_region(ctx);
// Don't skip multiple regions for gutter and don't push a row.
@ -469,21 +467,18 @@ impl<'a> GridLayouter<'a> {
height: Length,
y: usize,
) -> Frame {
let mut output = Frame::new(Size::new(self.used.w, height));
let mut output = Frame::new(Size::new(self.used.x, height));
let mut pos = Point::zero();
for (x, &rcol) in self.rcols.iter().enumerate() {
if let Some(node) = self.cell(x, y) {
let size = Size::new(rcol, height);
// Set the base to the size for non-auto rows.
let mut base = self.regions.base;
if self.cols[x] != TrackSizing::Auto {
base.w = size.w;
}
if self.rows[y] != TrackSizing::Auto {
base.h = size.h;
}
// Set the base to the region's base for auto rows and to the
// size for linear and fractional rows.
let base = Spec::new(self.cols[x], self.rows[y])
.map(|s| s == TrackSizing::Auto)
.select(self.regions.base, size);
let pod = Regions::one(size, base, Spec::splat(true));
let frame = node.layout(ctx, &pod).remove(0);
@ -506,15 +501,15 @@ impl<'a> GridLayouter<'a> {
// Prepare frames.
let mut outputs: Vec<_> = heights
.iter()
.map(|&h| Frame::new(Size::new(self.used.w, h)))
.map(|&h| Frame::new(Size::new(self.used.x, h)))
.collect();
// Prepare regions.
let size = Size::new(self.used.w, heights[0]);
let size = Size::new(self.used.x, heights[0]);
let mut pod = Regions::one(size, self.regions.base, Spec::splat(true));
pod.backlog = heights[1 ..]
.iter()
.map(|&h| Size::new(self.used.w, h))
.map(|&h| Size::new(self.used.x, h))
.collect::<Vec<_>>()
.into_iter();
@ -522,12 +517,10 @@ 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.mutate(|size| size.w = rcol);
// Set the horizontal base back to the parent region's base for
// auto columns.
// All widths should be `rcol` except the base for auto columns.
pod.mutate(|size| size.x = rcol);
if self.cols[x] == TrackSizing::Auto {
pod.base.w = self.regions.base.w;
pod.base.x = self.regions.base.x;
}
// Push the layouted frames into the individual output frames.
@ -545,8 +538,8 @@ impl<'a> GridLayouter<'a> {
/// Push a row frame into the current region.
fn push_row(&mut self, frame: Frame) {
self.regions.current.h -= frame.size.h;
self.used.h += frame.size.h;
self.regions.current.y -= frame.size.y;
self.used.y += frame.size.y;
self.lrows.push(Row::Frame(frame));
}
@ -556,10 +549,10 @@ impl<'a> GridLayouter<'a> {
// there are fr rows.
let mut size = self.used;
if self.fr.get() > 0.0 && self.full.is_finite() {
size.h = self.full;
size.y = self.full;
self.cts.exact.y = Some(self.full);
} else {
self.cts.min.y = Some(size.h);
self.cts.min.y = Some(size.y);
}
// The frame for the region.
@ -571,20 +564,20 @@ impl<'a> GridLayouter<'a> {
let frame = match row {
Row::Frame(frame) => frame,
Row::Fr(v, y) => {
let remaining = self.full - self.used.h;
let remaining = self.full - self.used.y;
let height = v.resolve(self.fr, remaining);
self.layout_single_row(ctx, height, y)
}
};
let height = frame.size.h;
let height = frame.size.y;
output.merge_frame(pos, frame);
pos.y += height;
}
self.regions.next();
self.full = self.regions.current.h;
self.used.h = Length::zero();
self.full = self.regions.current.y;
self.used.y = Length::zero();
self.fr = Fractional::zero();
self.finished.push(output.constrain(self.cts));
self.cts = Constraints::new(self.expand);

View File

@ -47,16 +47,16 @@ impl Layout for ImageNode {
let pxh = img.height() as f64;
let pixel_ratio = pxw / pxh;
let current_ratio = current.w / current.h;
let current_ratio = current.x / current.y;
let wide = pixel_ratio > current_ratio;
// The space into which the image will be placed according to its fit.
let canvas = if expand.x && expand.y {
current
} else if expand.x || (wide && current.w.is_finite()) {
Size::new(current.w, current.h.min(current.w.safe_div(pixel_ratio)))
} else if current.h.is_finite() {
Size::new(current.w.min(current.h * pixel_ratio), current.h)
} else if expand.x || (wide && current.x.is_finite()) {
Size::new(current.x, current.y.min(current.x.safe_div(pixel_ratio)))
} else if current.y.is_finite() {
Size::new(current.x.min(current.y * pixel_ratio), current.y)
} else {
Size::new(Length::pt(pxw), Length::pt(pxh))
};
@ -65,9 +65,9 @@ impl Layout for ImageNode {
let size = match self.fit {
ImageFit::Contain | ImageFit::Cover => {
if wide == (self.fit == ImageFit::Contain) {
Size::new(canvas.w, canvas.w / pixel_ratio)
Size::new(canvas.x, canvas.x / pixel_ratio)
} else {
Size::new(canvas.h * pixel_ratio, canvas.h)
Size::new(canvas.y * pixel_ratio, canvas.y)
}
}
ImageFit::Stretch => canvas,

View File

@ -143,15 +143,6 @@ dynamic! {
Align: "alignment",
}
dynamic! {
Spec<Option<Align>>: "2d alignment",
@align: Align => {
let mut aligns = Spec::default();
aligns.set(align.axis(), Some(*align));
aligns
},
}
dynamic! {
FontFamily: "font family",
Value::Str(string) => Self::Named(string.to_lowercase()),
@ -162,3 +153,15 @@ castable! {
Expected: "color",
Value::Color(color) => Paint::Solid(color),
}
castable! {
Spec<Option<Align>>,
Expected: "1d or 2d alignment",
@align: Align => {
let mut aligns = Spec::default();
aligns.set(align.axis(), Some(*align));
aligns
},
@aligns: Spec<Align> => aligns.map(Some),
}

View File

@ -54,29 +54,17 @@ impl Layout for PadNode {
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));
cts.exact.y.and_set(Some(current.h));
cts.base.x.and_set(Some(base.w));
cts.base.y.and_set(Some(base.h));
// Also set base constraints if the padding is relative.
if self.padding.left.is_relative() || self.padding.right.is_relative() {
cts.base.x = Some(base.w);
}
if self.padding.top.is_relative() || self.padding.bottom.is_relative() {
cts.base.y = Some(base.h);
}
// 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] {
if let Some(x) = spec.x.as_mut() {
*x += padding.size().w;
}
if let Some(y) = spec.y.as_mut() {
*y += padding.size().h;
}
spec.as_mut()
.zip(padding.sum_by_axis())
.map(|(s, p)| s.as_mut().map(|v| *v += p));
}
}
@ -86,7 +74,7 @@ impl Layout for PadNode {
/// Shrink a size by padding relative to the size itself.
fn shrink(size: Size, padding: Sides<Linear>) -> Size {
size - padding.resolve(size).size()
size - padding.resolve(size).sum_by_axis()
}
/// Grow a size by padding relative to the grown size.
@ -109,12 +97,6 @@ fn shrink(size: Size, padding: Sides<Linear>) -> Size {
/// <=> (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),
)
size.zip(padding.sum_by_axis())
.map(|(s, p)| (s + p.abs).safe_div(1.0 - p.rel.get()))
}

View File

@ -30,16 +30,16 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
if let Some(width) = width {
page.class = PaperClass::Custom;
page.size.w = width;
page.size.x = width;
}
if flip.unwrap_or(false) {
std::mem::swap(&mut page.size.w, &mut page.size.h);
std::mem::swap(&mut page.size.x, &mut page.size.y);
}
if let Some(height) = height {
page.class = PaperClass::Custom;
page.size.h = height;
page.size.y = height;
}
if let Some(margins) = margins {
@ -95,7 +95,7 @@ impl PageNode {
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
// When one of the lengths is infinite the page fits its content along
// that axis.
let expand = self.size.to_spec().map(Length::is_finite);
let expand = self.size.map(Length::is_finite);
let regions = Regions::repeat(self.size, self.size, expand);
// Layout the child.

View File

@ -208,7 +208,7 @@ impl<'a> ParLayouter<'a> {
for (range, child) in par.ranges().zip(&par.children) {
match *child {
ParChild::Spacing(Spacing::Linear(v)) => {
let resolved = v.resolve(regions.current.w);
let resolved = v.resolve(regions.current.x);
items.push(ParItem::Absolute(resolved));
ranges.push(range);
}
@ -230,7 +230,7 @@ impl<'a> ParLayouter<'a> {
}
}
ParChild::Node(ref node) => {
let size = Size::new(regions.current.w, regions.base.h);
let size = Size::new(regions.current.x, regions.base.y);
let expand = Spec::splat(false);
let pod = Regions::one(size, regions.base, expand);
let frame = node.layout(ctx, &pod).remove(0);
@ -292,26 +292,26 @@ impl<'a> ParLayouter<'a> {
// fit the line will yield the same line break. Therefore,
// the width of the region must not fit the width of the
// tried line.
if !stack.regions.current.w.fits(line.size.w) {
stack.cts.max.x.set_min(line.size.w);
if !stack.regions.current.x.fits(line.size.x) {
stack.cts.max.x.set_min(line.size.x);
}
// Same as above, but for height.
if !stack.regions.current.h.fits(line.size.h) {
let too_large = stack.size.h + self.leading + line.size.h;
if !stack.regions.current.y.fits(line.size.y) {
let too_large = stack.size.y + self.leading + line.size.y;
stack.cts.max.y.set_min(too_large);
}
stack.push(last_line);
stack.cts.min.y = Some(stack.size.h);
stack.cts.min.y = Some(stack.size.y);
start = last_end;
line = LineLayout::new(ctx, &self, start .. end);
}
}
// If the line does not fit vertically, we start a new region.
while !stack.regions.current.h.fits(line.size.h) {
while !stack.regions.current.y.fits(line.size.y) {
if stack.regions.in_last() {
stack.overflowing = true;
break;
@ -320,7 +320,7 @@ impl<'a> ParLayouter<'a> {
// Again, the line must not fit. It would if the space taken up
// plus the line height would fit, therefore the constraint
// below.
let too_large = stack.size.h + self.leading + line.size.h;
let too_large = stack.size.y + self.leading + line.size.y;
stack.cts.max.y.set_min(too_large);
stack.finish_region(ctx);
@ -329,7 +329,7 @@ impl<'a> ParLayouter<'a> {
// If the line does not fit horizontally or we have a mandatory
// line break (i.e. due to "\n"), we push the line into the
// stack.
if mandatory || !stack.regions.current.w.fits(line.size.w) {
if mandatory || !stack.regions.current.x.fits(line.size.x) {
start = end;
last = None;
@ -339,23 +339,23 @@ impl<'a> ParLayouter<'a> {
// paragraph, we want to force an empty line.
if mandatory && end == self.bidi.text.len() {
let line = LineLayout::new(ctx, &self, end .. end);
if stack.regions.current.h.fits(line.size.h) {
if stack.regions.current.y.fits(line.size.y) {
stack.push(line);
}
}
stack.cts.min.y = Some(stack.size.h);
stack.cts.min.y = Some(stack.size.y);
} else {
// Otherwise, the line fits both horizontally and vertically
// and we remember it.
stack.cts.min.x.set_max(line.size.w);
stack.cts.min.x.set_max(line.size.x);
last = Some((line, end));
}
}
if let Some((line, _)) = last {
stack.push(line);
stack.cts.min.y = Some(stack.size.h);
stack.cts.min.y = Some(stack.size.y);
}
stack.finish(ctx)
@ -467,9 +467,9 @@ impl<'a> LineLayout<'a> {
ParItem::Fractional(v) => fr += v,
ParItem::Text(ShapedText { size, baseline, .. })
| ParItem::Frame(Frame { size, baseline, .. }) => {
width += size.w;
width += size.x;
top.set_max(baseline);
bottom.set_max(size.h - baseline);
bottom.set_max(size.y - baseline);
}
}
}
@ -489,8 +489,8 @@ impl<'a> LineLayout<'a> {
/// Build the line's frame.
fn build(&self, ctx: &LayoutContext, width: Length) -> Frame {
let size = Size::new(self.size.w.max(width), self.size.h);
let remaining = size.w - self.size.w;
let size = Size::new(self.size.x.max(width), self.size.y);
let remaining = size.x - self.size.x;
let mut output = Frame::new(size);
let mut offset = Length::zero();
@ -507,7 +507,7 @@ impl<'a> LineLayout<'a> {
let x = offset + self.par.align.resolve(remaining);
let y = self.baseline - frame.baseline;
offset += frame.size.w;
offset += frame.size.x;
// Add to the line's frame.
output.merge_frame(Point::new(x, y), frame);
@ -602,12 +602,12 @@ impl<'a> LineStack<'a> {
/// Push a new line into the stack.
fn push(&mut self, line: LineLayout<'a>) {
self.regions.current.h -= line.size.h + self.leading;
self.regions.current.y -= line.size.y + self.leading;
self.size.w.set_max(line.size.w);
self.size.h += line.size.h;
self.size.x.set_max(line.size.x);
self.size.y += line.size.y;
if !self.lines.is_empty() {
self.size.h += self.leading;
self.size.y += self.leading;
}
self.fractional |= !line.fr.is_zero();
@ -617,14 +617,14 @@ impl<'a> LineStack<'a> {
/// Finish the frame for one region.
fn finish_region(&mut self, ctx: &LayoutContext) {
if self.regions.expand.x || self.fractional {
self.size.w = self.regions.current.w;
self.cts.exact.x = Some(self.regions.current.w);
self.size.x = self.regions.current.x;
self.cts.exact.x = Some(self.regions.current.x);
}
if self.overflowing {
self.cts.min.y = None;
self.cts.max.y = None;
self.cts.exact = self.full.to_spec().map(Some);
self.cts.exact = self.full.map(Some);
}
let mut output = Frame::new(self.size);
@ -632,7 +632,7 @@ impl<'a> LineStack<'a> {
let mut first = true;
for line in self.lines.drain(..) {
let frame = line.build(ctx, self.size.w);
let frame = line.build(ctx, self.size.x);
let pos = Point::with_y(offset);
if first {
@ -640,7 +640,7 @@ impl<'a> LineStack<'a> {
first = false;
}
offset += frame.size.h + self.leading;
offset += frame.size.y + self.leading;
output.merge_frame(pos, frame);
}
@ -648,7 +648,7 @@ impl<'a> LineStack<'a> {
self.regions.next();
self.full = self.regions.current;
self.cts = Constraints::new(self.regions.expand);
self.cts.base = self.regions.base.to_spec().map(Some);
self.cts.base = self.regions.base.map(Some);
self.size = Size::zero();
}

View File

@ -138,8 +138,8 @@ impl Layout for ShapeNode {
// the result is really a square or circle.
let size = frames[0].item.size;
let mut pod = regions.clone();
pod.current.w = size.w.max(size.h).min(pod.current.w);
pod.current.h = pod.current.w;
pod.current.x = size.x.max(size.y).min(pod.current.x);
pod.current.y = pod.current.x;
pod.expand = Spec::splat(true);
frames = node.layout(ctx, &pod);
}
@ -153,7 +153,7 @@ impl Layout for ShapeNode {
let default = Length::pt(30.0);
let mut size = Size::new(
if regions.expand.x {
regions.current.w
regions.current.x
} else {
// For rectangle and ellipse, the default shape is a bit
// wider than high.
@ -162,16 +162,16 @@ impl Layout for ShapeNode {
ShapeKind::Rect | ShapeKind::Ellipse => 1.5 * default,
}
},
if regions.expand.y { regions.current.h } else { default },
if regions.expand.y { regions.current.y } else { default },
);
// Don't overflow the region.
size.w = size.w.min(regions.current.w);
size.h = size.h.min(regions.current.h);
size.x = size.x.min(regions.current.x);
size.y = size.y.min(regions.current.y);
if matches!(self.kind, ShapeKind::Square | ShapeKind::Circle) {
size.w = size.w.min(size.h);
size.h = size.w;
size.x = size.x.min(size.y);
size.y = size.x;
}
Frame::new(size)
@ -194,11 +194,7 @@ impl Layout for ShapeNode {
}
// Ensure frame size matches regions size if expansion is on.
let expand = regions.expand;
frame.size = Size::new(
if expand.x { regions.current.w } else { frame.size.w },
if expand.y { regions.current.h } else { frame.size.h },
);
frame.size = regions.expand.select(regions.current, frame.size);
// Return tight constraints for now.
vec![frame.constrain(Constraints::tight(regions))]

View File

@ -35,50 +35,36 @@ impl Layout for SizedNode {
ctx: &mut LayoutContext,
regions: &Regions,
) -> Vec<Constrained<Rc<Frame>>> {
// Generate constraints.
let mut cts = Constraints::new(regions.expand);
cts.set_base_if_linear(regions.base, self.sizing);
// Set tight exact and base constraints if the child is
// automatically sized since we don't know what the child might do.
if self.sizing.x.is_none() {
cts.exact.x = Some(regions.current.w);
cts.base.x = Some(regions.base.w);
}
// Same here.
if self.sizing.y.is_none() {
cts.exact.y = Some(regions.current.h);
cts.base.y = Some(regions.base.h);
}
// Resolve width and height relative to the region's base.
let width = self.sizing.x.map(|w| w.resolve(regions.base.w));
let height = self.sizing.y.map(|h| h.resolve(regions.base.h));
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 = {
let size = Size::new(
width.unwrap_or(regions.current.w),
height.unwrap_or(regions.current.h),
);
// 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);
let base = Size::new(
if width.is_some() { size.w } else { regions.base.w },
if height.is_some() { size.h } else { regions.base.h },
);
// 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;
let expand = Spec::new(
width.is_some() || regions.expand.x,
height.is_some() || regions.expand.y,
);
// TODO: Allow multiple regions if only width is set.
Regions::one(size, base, expand)
};
let mut frames = self.child.layout(ctx, &pod);
frames[0].cts = cts;
// Set base & exact constraints if the child is automatically sized
// since we don't know what the child might do. Also set base if our
// sizing is relative.
let frame = &mut frames[0];
frame.cts = Constraints::new(regions.expand);
frame.cts.exact = regions.current.filter(is_auto);
frame.cts.base = regions.base.filter(is_auto | is_rel);
frames
}
}

View File

@ -128,7 +128,6 @@ impl<'a> StackLayouter<'a> {
let expand = regions.expand;
let full = regions.current;
// Disable expansion along the block axis for children.
let mut regions = regions.clone();
regions.expand.set(axis, false);
@ -210,11 +209,8 @@ impl<'a> StackLayouter<'a> {
fn finish_region(&mut self) {
// Determine the size of the stack in this region dependening on whether
// the region expands.
let used = self.used.to_size(self.axis);
let mut size = Size::new(
if self.expand.x { self.full.w } else { used.w },
if self.expand.y { self.full.h } else { used.h },
);
let used = self.used.to_spec(self.axis);
let mut size = self.expand.select(self.full, used);
// Expand fully if there are fr spacings.
let full = self.full.get(self.axis);
@ -263,8 +259,8 @@ impl<'a> StackLayouter<'a> {
// Generate tight constraints for now.
let mut cts = Constraints::new(self.expand);
cts.exact = self.full.to_spec().map(Some);
cts.base = self.regions.base.to_spec().map(Some);
cts.exact = self.full.map(Some);
cts.base = self.regions.base.map(Some);
// Advance to the next region.
self.regions.next();

View File

@ -57,8 +57,7 @@ impl Layout for TransformNode {
let mut frames = self.child.layout(ctx, regions);
for Constrained { item: frame, .. } in frames.iter_mut() {
let x = self.origin.x.resolve(frame.size.w);
let y = self.origin.y.resolve(frame.size.h);
let Spec { x, y } = self.origin.zip(frame.size).map(|(o, s)| o.resolve(s));
let transform = Transform::translation(x, y)
.pre_concat(self.transform)
.pre_concat(Transform::translation(-x, -y));

View File

@ -27,9 +27,6 @@ impl BoolExt for bool {
/// Additional methods for options.
pub trait OptionExt<T> {
/// Replace `self` with `other` if `self` is `Some`.
fn and_set(&mut self, other: Option<T>);
/// Sets `other` as the value if `self` is `None` or if it contains a value
/// larger than `other`.
fn set_min(&mut self, other: T)
@ -44,12 +41,6 @@ pub trait OptionExt<T> {
}
impl<T> OptionExt<T> for Option<T> {
fn and_set(&mut self, other: Option<T>) {
if self.is_some() {
*self = other;
}
}
fn set_min(&mut self, other: T)
where
T: Ord,

View File

@ -387,8 +387,8 @@ fn print_error(source: &SourceFile, line: usize, error: &Error) {
fn draw(ctx: &Context, frames: &[Rc<Frame>], dpp: f32) -> sk::Pixmap {
let pad = Length::pt(5.0);
let width = 2.0 * pad + frames.iter().map(|l| l.size.w).max().unwrap_or_default();
let height = pad + frames.iter().map(|l| l.size.h + pad).sum::<Length>();
let width = 2.0 * pad + frames.iter().map(|l| l.size.x).max().unwrap_or_default();
let height = pad + frames.iter().map(|l| l.size.y + pad).sum::<Length>();
let pxw = (dpp * width.to_f32()) as u32;
let pxh = (dpp * height.to_f32()) as u32;
@ -414,13 +414,13 @@ fn draw(ctx: &Context, frames: &[Rc<Frame>], dpp: f32) -> sk::Pixmap {
let mut background = sk::Paint::default();
background.set_color(sk::Color::WHITE);
let w = frame.size.w.to_f32();
let h = frame.size.h.to_f32();
let w = frame.size.x.to_f32();
let h = frame.size.y.to_f32();
let rect = sk::Rect::from_xywh(0.0, 0.0, w, h).unwrap();
canvas.fill_rect(rect, &background, ts, None);
draw_frame(&mut canvas, ts, &mask, ctx, frame);
ts = ts.pre_translate(0.0, (frame.size.h + pad).to_f32());
ts = ts.pre_translate(0.0, (frame.size.y + pad).to_f32());
}
canvas
@ -469,8 +469,8 @@ fn draw_group(
) {
let ts = ts.pre_concat(convert_typst_transform(group.transform));
if group.clips {
let w = group.frame.size.w.to_f32();
let h = group.frame.size.h.to_f32();
let w = group.frame.size.x.to_f32();
let h = group.frame.size.y.to_f32();
let rect = sk::Rect::from_xywh(0.0, 0.0, w, h).unwrap();
let path = sk::PathBuilder::from_rect(rect).transform(ts).unwrap();
let rule = sk::FillRule::default();
@ -565,8 +565,8 @@ fn draw_shape(
) {
let path = match shape.geometry {
Geometry::Rect(size) => {
let w = size.w.to_f32();
let h = size.h.to_f32();
let w = size.x.to_f32();
let h = size.y.to_f32();
let rect = sk::Rect::from_xywh(0.0, 0.0, w, h).unwrap();
sk::PathBuilder::from_rect(rect)
}
@ -613,8 +613,8 @@ fn draw_image(
*dest = sk::ColorU8::from_rgba(r, g, b, a).premultiply();
}
let view_width = size.w.to_f32();
let view_height = size.h.to_f32();
let view_width = size.x.to_f32();
let view_height = size.y.to_f32();
let scale_x = view_width as f32 / pixmap.width() as f32;
let scale_y = view_height as f32 / pixmap.height() as f32;