diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-10-28 16:43:38 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-10-28 16:43:38 +0200 |
| commit | 95e9134a3c7d7a14d8c8928413fdffc665671895 (patch) | |
| tree | 822b5f6c2d23aba33cfe4d199405e493e37c3d70 /src/model | |
| parent | 66030ae5d73d85a0463562230b87f8ec7554c746 (diff) | |
Refactor `geom` module
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 4 | ||||
| -rw-r--r-- | src/model/eval.rs | 6 | ||||
| -rw-r--r-- | src/model/layout.rs | 22 | ||||
| -rw-r--r-- | src/model/ops.rs | 37 | ||||
| -rw-r--r-- | src/model/property.rs | 46 | ||||
| -rw-r--r-- | src/model/raw.rs | 170 | ||||
| -rw-r--r-- | src/model/value.rs | 34 |
7 files changed, 92 insertions, 227 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index 428865f3..3ee59ecc 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -15,7 +15,7 @@ use super::{ }; use crate::diag::{SourceResult, StrResult}; use crate::frame::{Frame, Role}; -use crate::geom::{Length, Numeric}; +use crate::geom::{Abs, Numeric}; use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing}; use crate::library::structure::{DocNode, ListItem, ListNode, DESC, ENUM, LIST}; use crate::library::text::{ParChild, ParNode}; @@ -175,7 +175,7 @@ impl Content { } /// Add weak vertical spacing above and below the node. - pub fn spaced(self, above: Option<Length>, below: Option<Length>) -> Self { + pub fn spaced(self, above: Option<Abs>, below: Option<Abs>) -> Self { if above.is_none() && below.is_none() { return self; } diff --git a/src/model/eval.rs b/src/model/eval.rs index 6658e244..c16156cd 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -11,7 +11,7 @@ use super::{ Pattern, Recipe, Scope, Scopes, StyleEntry, StyleMap, Value, Vm, }; use crate::diag::{At, SourceResult, StrResult, Trace, Tracepoint}; -use crate::geom::{Angle, Em, Fraction, Length, Ratio}; +use crate::geom::{Abs, Angle, Em, Fr, Ratio}; use crate::library; use crate::syntax::ast::TypedNode; use crate::syntax::{ast, SourceId, Span, Spanned, Unit}; @@ -465,10 +465,10 @@ impl Eval for ast::Lit { ast::LitKind::Int(v) => Value::Int(v), ast::LitKind::Float(v) => Value::Float(v), ast::LitKind::Numeric(v, unit) => match unit { - Unit::Length(unit) => Length::with_unit(v, unit).into(), + Unit::Length(unit) => Abs::with_unit(v, unit).into(), Unit::Angle(unit) => Angle::with_unit(v, unit).into(), Unit::Em => Em::new(v).into(), - Unit::Fr => Fraction::new(v).into(), + Unit::Fr => Fr::new(v).into(), Unit::Percent => Ratio::new(v / 100.0).into(), }, ast::LitKind::Str(v) => Value::Str(v.into()), diff --git a/src/model/layout.rs b/src/model/layout.rs index 5248157b..ea9d076c 100644 --- a/src/model/layout.rs +++ b/src/model/layout.rs @@ -8,10 +8,10 @@ use std::sync::Arc; use comemo::{Prehashed, Tracked}; use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry}; -use super::{Builder, Content, RawLength, Scratch}; +use super::{Builder, Content, Scratch}; use crate::diag::SourceResult; use crate::frame::{Element, Frame}; -use crate::geom::{Align, Geometry, Length, Paint, Point, Relative, Size, Spec, Stroke}; +use crate::geom::{Abs, Align, Axes, Geometry, Length, Paint, Point, Rel, Size, Stroke}; use crate::World; /// Layout content into a collection of pages. @@ -56,18 +56,18 @@ pub struct Regions { /// The base size for relative sizing. pub base: Size, /// The height of followup regions. The width is the same for all regions. - pub backlog: Vec<Length>, + pub backlog: Vec<Abs>, /// 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>, + pub last: Option<Abs>, /// Whether nodes should expand to fill the regions instead of shrinking to /// fit the content. - pub expand: Spec<bool>, + pub expand: Axes<bool>, } impl Regions { /// Create a new region sequence with exactly one region. - pub fn one(size: Size, base: Size, expand: Spec<bool>) -> Self { + pub fn one(size: Size, base: Size, expand: Axes<bool>) -> Self { Self { first: size, base, @@ -78,7 +78,7 @@ impl Regions { } /// Create a new sequence of same-size regions that repeats indefinitely. - pub fn repeat(size: Size, base: Size, expand: Spec<bool>) -> Self { + pub fn repeat(size: Size, base: Size, expand: Axes<bool>) -> Self { Self { first: size, base, @@ -108,7 +108,7 @@ impl Regions { /// 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() + Abs::zero().fits(self.first.y) && !self.in_last() } /// Whether the first region is the last usable region. @@ -173,7 +173,7 @@ impl LayoutNode { } /// Force a size for this node. - pub fn sized(self, sizing: Spec<Option<Relative<RawLength>>>) -> Self { + pub fn sized(self, sizing: Axes<Option<Rel<Length>>>) -> Self { if sizing.any(Option::is_some) { SizedNode { sizing, child: self }.pack() } else { @@ -279,7 +279,7 @@ impl Layout for EmptyNode { #[derive(Debug, Hash)] struct SizedNode { /// How to size the node horizontally and vertically. - sizing: Spec<Option<Relative<RawLength>>>, + sizing: Axes<Option<Rel<Length>>>, /// The node to be sized. child: LayoutNode, } @@ -303,7 +303,7 @@ impl Layout for SizedNode { // Select the appropriate base and expansion for the child depending // on whether it is automatically or relatively sized. - let is_auto = self.sizing.map_is_none(); + let is_auto = self.sizing.as_ref().map(Option::is_none); let base = is_auto.select(regions.base, size); let expand = regions.expand | !is_auto; diff --git a/src/model/ops.rs b/src/model/ops.rs index c521f704..c7d6ac78 100644 --- a/src/model/ops.rs +++ b/src/model/ops.rs @@ -2,9 +2,9 @@ use std::cmp::Ordering; -use super::{RawAlign, RawLength, RawStroke, Regex, Smart, Value}; +use super::{RawAlign, RawStroke, Regex, Smart, Value}; use crate::diag::StrResult; -use crate::geom::{Numeric, Relative, Spec, SpecAxis}; +use crate::geom::{Axes, Axis, Length, Numeric, Rel}; use Value::*; /// Bail with a type mismatch error. @@ -106,8 +106,8 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> { { if a.axis() != b.axis() { Value::dynamic(match a.axis() { - SpecAxis::Horizontal => Spec { x: a, y: b }, - SpecAxis::Vertical => Spec { x: b, y: a }, + Axis::X => Axes { x: a, y: b }, + Axis::Y => Axes { x: b, y: a }, }) } else { return Err(format!("cannot add two {:?} alignments", a.axis())); @@ -203,8 +203,8 @@ pub fn div(lhs: Value, rhs: Value) -> StrResult<Value> { (Length(a), Int(b)) => Length(a / b as f64), (Length(a), Float(b)) => Length(a / b), - (Length(a), Length(b)) => Float(div_length(a, b)?), - (Length(a), Relative(b)) if b.rel.is_zero() => Float(div_length(a, b.abs)?), + (Length(a), Length(b)) => Float(try_div_length(a, b)?), + (Length(a), Relative(b)) if b.rel.is_zero() => Float(try_div_length(a, b.abs)?), (Angle(a), Int(b)) => Angle(a / b as f64), (Angle(a), Float(b)) => Angle(a / b), @@ -217,9 +217,9 @@ pub fn div(lhs: Value, rhs: Value) -> StrResult<Value> { (Relative(a), Int(b)) => Relative(a / b as f64), (Relative(a), Float(b)) => Relative(a / b), - (Relative(a), Length(b)) if a.rel.is_zero() => Float(div_length(a.abs, b)?), + (Relative(a), Length(b)) if a.rel.is_zero() => Float(try_div_length(a.abs, b)?), (Relative(a), Ratio(b)) if a.abs.is_zero() => Float(a.rel / b), - (Relative(a), Relative(b)) => Float(div_relative(a, b)?), + (Relative(a), Relative(b)) => Float(try_div_relative(a, b)?), (Fraction(a), Int(b)) => Fraction(a / b as f64), (Fraction(a), Float(b)) => Fraction(a / b), @@ -230,25 +230,14 @@ pub fn div(lhs: Value, rhs: Value) -> StrResult<Value> { } /// Try to divide two lengths. -fn div_length(a: RawLength, b: RawLength) -> StrResult<f64> { - if a.length.is_zero() && b.length.is_zero() { - Ok(a.em / b.em) - } else if a.em.is_zero() && b.em.is_zero() { - Ok(a.length / b.length) - } else { - return Err("cannot divide these two lengths".into()); - } +fn try_div_length(a: Length, b: Length) -> StrResult<f64> { + a.try_div(b).ok_or_else(|| "cannot divide these two lengths".into()) } /// Try to divide two relative lengths. -fn div_relative(a: Relative<RawLength>, b: Relative<RawLength>) -> StrResult<f64> { - if a.rel.is_zero() && b.rel.is_zero() { - div_length(a.abs, b.abs) - } else if a.abs.is_zero() && b.abs.is_zero() { - Ok(a.rel / b.rel) - } else { - return Err("cannot divide these two relative lengths".into()); - } +fn try_div_relative(a: Rel<Length>, b: Rel<Length>) -> StrResult<f64> { + a.try_div(b) + .ok_or_else(|| "cannot divide these two relative lengths".into()) } /// Compute the logical "not" of a value. diff --git a/src/model/property.rs b/src/model/property.rs index 4b9342ab..2e2a76a5 100644 --- a/src/model/property.rs +++ b/src/model/property.rs @@ -5,11 +5,11 @@ use std::sync::Arc; use comemo::Prehashed; -use super::{Interruption, NodeId, RawLength, Smart, StyleChain}; -use crate::geom::{Corners, Length, Numeric, Relative, Sides, Spec}; +use super::{Interruption, NodeId, Smart, StyleChain}; +use crate::geom::{Abs, Axes, Corners, Em, Length, Numeric, Rel, Sides}; use crate::library::layout::PageNode; use crate::library::structure::{DescNode, EnumNode, ListNode}; -use crate::library::text::ParNode; +use crate::library::text::{ParNode, TextNode}; use crate::util::ReadableTypeId; /// A style property originating from a set rule or constructor. @@ -163,6 +163,26 @@ pub trait Resolve { fn resolve(self, styles: StyleChain) -> Self::Output; } +impl Resolve for Em { + type Output = Abs; + + fn resolve(self, styles: StyleChain) -> Self::Output { + if self.is_zero() { + Abs::zero() + } else { + self.at(styles.get(TextNode::SIZE)) + } + } +} + +impl Resolve for Length { + type Output = Abs; + + fn resolve(self, styles: StyleChain) -> Self::Output { + self.abs + self.em.resolve(styles) + } +} + impl<T: Resolve> Resolve for Option<T> { type Output = Option<T::Output>; @@ -179,8 +199,8 @@ impl<T: Resolve> Resolve for Smart<T> { } } -impl<T: Resolve> Resolve for Spec<T> { - type Output = Spec<T::Output>; +impl<T: Resolve> Resolve for Axes<T> { + type Output = Axes<T::Output>; fn resolve(self, styles: StyleChain) -> Self::Output { self.map(|v| v.resolve(styles)) @@ -203,12 +223,12 @@ impl<T: Resolve> Resolve for Corners<T> { } } -impl<T> Resolve for Relative<T> +impl<T> Resolve for Rel<T> where T: Resolve + Numeric, <T as Resolve>::Output: Numeric, { - type Output = Relative<<T as Resolve>::Output>; + type Output = Rel<<T as Resolve>::Output>; fn resolve(self, styles: StyleChain) -> Self::Output { self.map(|abs| abs.resolve(styles)) @@ -259,16 +279,16 @@ where } } -impl Fold for Sides<Option<Relative<Length>>> { - type Output = Sides<Relative<Length>>; +impl Fold for Sides<Option<Rel<Abs>>> { + type Output = Sides<Rel<Abs>>; fn fold(self, outer: Self::Output) -> Self::Output { self.zip(outer, |inner, outer| inner.unwrap_or(outer)) } } -impl Fold for Sides<Option<Smart<Relative<RawLength>>>> { - type Output = Sides<Smart<Relative<RawLength>>>; +impl Fold for Sides<Option<Smart<Rel<Length>>>> { + type Output = Sides<Smart<Rel<Length>>>; fn fold(self, outer: Self::Output) -> Self::Output { self.zip(outer, |inner, outer| inner.unwrap_or(outer)) @@ -286,8 +306,8 @@ where } } -impl Fold for Corners<Option<Relative<Length>>> { - type Output = Corners<Relative<Length>>; +impl Fold for Corners<Option<Rel<Abs>>> { + type Output = Corners<Rel<Abs>>; fn fold(self, outer: Self::Output) -> Self::Output { self.zip(outer, |inner, outer| inner.unwrap_or(outer)) diff --git a/src/model/raw.rs b/src/model/raw.rs index b40a88ec..b9242b51 100644 --- a/src/model/raw.rs +++ b/src/model/raw.rs @@ -1,11 +1,7 @@ -use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; -use std::ops::{Add, Div, Mul, Neg}; use super::{Fold, Resolve, Smart, StyleChain, Value}; -use crate::geom::{ - Align, Em, Get, Length, Numeric, Paint, Relative, Spec, SpecAxis, Stroke, -}; +use crate::geom::{Abs, Align, Axes, Axis, Get, Length, Paint, Stroke}; use crate::library::text::TextNode; /// The unresolved alignment representation. @@ -34,9 +30,9 @@ impl Resolve for RawAlign { impl RawAlign { /// The axis this alignment belongs to. - pub const fn axis(self) -> SpecAxis { + pub const fn axis(self) -> Axis { match self { - Self::Start | Self::End => SpecAxis::Horizontal, + Self::Start | Self::End => Axis::X, Self::Specific(align) => align.axis(), } } @@ -51,8 +47,8 @@ impl From<Align> for RawAlign { impl Debug for RawAlign { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Self::Start => f.pad("left"), - Self::End => f.pad("center"), + Self::Start => f.pad("start"), + Self::End => f.pad("end"), Self::Specific(align) => align.fmt(f), } } @@ -63,18 +59,18 @@ dynamic! { } dynamic! { - Spec<RawAlign>: "2d alignment", + Axes<RawAlign>: "2d alignment", } castable! { - Spec<Option<RawAlign>>, + Axes<Option<RawAlign>>, Expected: "1d or 2d alignment", @align: RawAlign => { - let mut aligns = Spec::default(); + let mut aligns = Axes::default(); aligns.set(align.axis(), Some(*align)); aligns }, - @aligns: Spec<RawAlign> => aligns.map(Some), + @aligns: Axes<RawAlign> => aligns.map(Some), } /// The unresolved stroke representation. @@ -83,14 +79,14 @@ castable! { /// just a paint (`red`), just a thickness (`0.1em`) or both (`2pt + red`) where /// this is expected. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct RawStroke<T = RawLength> { +pub struct RawStroke<T = Length> { /// The stroke's paint. pub paint: Smart<Paint>, /// The stroke's thickness. pub thickness: Smart<T>, } -impl RawStroke<Length> { +impl RawStroke<Abs> { /// Unpack the stroke, filling missing fields from the `default`. pub fn unwrap_or(self, default: Stroke) -> Stroke { Stroke { @@ -106,7 +102,7 @@ impl RawStroke<Length> { } impl Resolve for RawStroke { - type Output = RawStroke<Length>; + type Output = RawStroke<Abs>; fn resolve(self, styles: StyleChain) -> Self::Output { RawStroke { @@ -116,7 +112,7 @@ impl Resolve for RawStroke { } } -impl Fold for RawStroke<Length> { +impl Fold for RawStroke<Abs> { type Output = Self; fn fold(self, outer: Self::Output) -> Self::Output { @@ -151,143 +147,3 @@ dynamic! { thickness: Smart::Auto, }, } - -/// The unresolved length representation. -/// -/// Currently supports absolute and em units, but support could quite easily be -/// extended to other units that can be resolved through a style chain. -/// Probably, it would be a good idea to then move to an enum representation -/// that has a small footprint and allocates for the rare case that units are -/// mixed. -#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct RawLength { - /// The absolute part. - pub length: Length, - /// The font-relative part. - pub em: Em, -} - -impl RawLength { - /// The zero length. - pub const fn zero() -> Self { - Self { length: Length::zero(), em: Em::zero() } - } -} - -impl Debug for RawLength { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match (self.length.is_zero(), self.em.is_zero()) { - (false, false) => write!(f, "{:?} + {:?}", self.length, self.em), - (true, false) => self.em.fmt(f), - (_, true) => self.length.fmt(f), - } - } -} - -impl Resolve for Em { - type Output = Length; - - fn resolve(self, styles: StyleChain) -> Self::Output { - if self.is_zero() { - Length::zero() - } else { - self.at(styles.get(TextNode::SIZE)) - } - } -} - -impl Resolve for RawLength { - type Output = Length; - - fn resolve(self, styles: StyleChain) -> Self::Output { - self.length + self.em.resolve(styles) - } -} - -impl Numeric for RawLength { - fn zero() -> Self { - Self::zero() - } - - fn is_finite(self) -> bool { - self.length.is_finite() && self.em.is_finite() - } -} - -impl PartialOrd for RawLength { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - if self.em.is_zero() && other.em.is_zero() { - self.length.partial_cmp(&other.length) - } else if self.length.is_zero() && other.length.is_zero() { - self.em.partial_cmp(&other.em) - } else { - None - } - } -} - -impl From<Length> for RawLength { - fn from(length: Length) -> Self { - Self { length, em: Em::zero() } - } -} - -impl From<Em> for RawLength { - fn from(em: Em) -> Self { - Self { length: Length::zero(), em } - } -} - -impl From<Length> for Relative<RawLength> { - fn from(length: Length) -> Self { - Relative::from(RawLength::from(length)) - } -} - -impl Neg for RawLength { - type Output = Self; - - fn neg(self) -> Self::Output { - Self { length: -self.length, em: -self.em } - } -} - -impl Add for RawLength { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self { - length: self.length + rhs.length, - em: self.em + rhs.em, - } - } -} - -sub_impl!(RawLength - RawLength -> RawLength); - -impl Mul<f64> for RawLength { - type Output = Self; - - fn mul(self, rhs: f64) -> Self::Output { - Self { - length: self.length * rhs, - em: self.em * rhs, - } - } -} - -impl Div<f64> for RawLength { - type Output = Self; - - fn div(self, rhs: f64) -> Self::Output { - Self { - length: self.length / rhs, - em: self.em / rhs, - } - } -} - -assign_impl!(RawLength += RawLength); -assign_impl!(RawLength -= RawLength); -assign_impl!(RawLength *= f64); -assign_impl!(RawLength /= f64); diff --git a/src/model/value.rs b/src/model/value.rs index e0d5edf3..7b594adf 100644 --- a/src/model/value.rs +++ b/src/model/value.rs @@ -7,9 +7,9 @@ use std::sync::Arc; use comemo::Tracked; use siphasher::sip128::{Hasher128, SipHasher}; -use super::{ops, Args, Array, Cast, Content, Dict, Func, Layout, RawLength, Str}; +use super::{ops, Args, Array, Cast, Content, Dict, Func, Layout, Str}; use crate::diag::StrResult; -use crate::geom::{Angle, Color, Em, Fraction, Length, Ratio, Relative, RgbaColor}; +use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor}; use crate::util::EcoString; use crate::World; @@ -26,16 +26,16 @@ pub enum Value { Int(i64), /// A floating-point number: `1.2`, `10e-4`. Float(f64), - /// A length: `12pt`, `3cm`, `1.5em`. - Length(RawLength), + /// A length: `12pt`, `3cm`, `1.5em`, `1em - 2pt`. + Length(Length), /// An angle: `1.5rad`, `90deg`. Angle(Angle), /// A ratio: `50%`. Ratio(Ratio), /// A relative length, combination of a ratio and a length: `20% + 5cm`. - Relative(Relative<RawLength>), + Relative(Rel<Length>), /// A fraction: `1fr`. - Fraction(Fraction), + Fraction(Fr), /// A color value: `#f79143ff`. Color(Color), /// A string: `"string"`. @@ -87,11 +87,11 @@ impl Value { Self::Bool(_) => bool::TYPE_NAME, Self::Int(_) => i64::TYPE_NAME, Self::Float(_) => f64::TYPE_NAME, - Self::Length(_) => RawLength::TYPE_NAME, + Self::Length(_) => Length::TYPE_NAME, Self::Angle(_) => Angle::TYPE_NAME, Self::Ratio(_) => Ratio::TYPE_NAME, - Self::Relative(_) => Relative::<RawLength>::TYPE_NAME, - Self::Fraction(_) => Fraction::TYPE_NAME, + Self::Relative(_) => Rel::<Length>::TYPE_NAME, + Self::Fraction(_) => Fr::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME, Self::Str(_) => Str::TYPE_NAME, Self::Content(_) => Content::TYPE_NAME, @@ -210,8 +210,8 @@ impl From<usize> for Value { } } -impl From<Length> for Value { - fn from(v: Length) -> Self { +impl From<Abs> for Value { + fn from(v: Abs) -> Self { Self::Length(v.into()) } } @@ -385,15 +385,15 @@ macro_rules! primitive { primitive! { bool: "boolean", Bool } primitive! { i64: "integer", Int } primitive! { f64: "float", Float, Int(v) => v as f64 } -primitive! { RawLength: "length", Length } +primitive! { Length: "length", Length } primitive! { Angle: "angle", Angle } primitive! { Ratio: "ratio", Ratio } -primitive! { Relative<RawLength>: "relative length", +primitive! { Rel<Length>: "relative length", Relative, Length(v) => v.into(), Ratio(v) => v.into() } -primitive! { Fraction: "fraction", Fraction } +primitive! { Fr: "fraction", Fraction } primitive! { Color: "color", Color } primitive! { Str: "string", Str } primitive! { Content: "content", @@ -422,14 +422,14 @@ mod tests { test(false, "false"); test(12i64, "12"); test(3.14, "3.14"); - test(Length::pt(5.5), "5.5pt"); + test(Abs::pt(5.5), "5.5pt"); test(Angle::deg(90.0), "90deg"); test(Ratio::one() / 2.0, "50%"); test( - Ratio::new(0.3) + RawLength::from(Length::cm(2.0)), + Ratio::new(0.3) + Length::from(Abs::cm(2.0)), "30% + 56.69pt", ); - test(Fraction::one() * 7.55, "7.55fr"); + test(Fr::one() * 7.55, "7.55fr"); test( Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "rgb(\"#010101\")", |
