diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-08 15:08:26 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-08 15:45:14 +0200 |
| commit | 712c00ecb72b67da2c0788e5d3eb4dcc6366b2a7 (patch) | |
| tree | f5d7ef4341a4728c980d020cc173fa6bb70feaff /src/eval | |
| parent | 977ac77e6a3298be2644a8231e93acbef9f7f396 (diff) | |
Em units
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/content.rs | 2 | ||||
| -rw-r--r-- | src/eval/layout.rs | 25 | ||||
| -rw-r--r-- | src/eval/mod.rs | 15 | ||||
| -rw-r--r-- | src/eval/ops.rs | 5 | ||||
| -rw-r--r-- | src/eval/raw.rs | 133 | ||||
| -rw-r--r-- | src/eval/value.rs | 33 |
6 files changed, 177 insertions, 36 deletions
diff --git a/src/eval/content.rs b/src/eval/content.rs index 3c1fb310..166cf6f0 100644 --- a/src/eval/content.rs +++ b/src/eval/content.rs @@ -456,7 +456,7 @@ impl<'a> Builder<'a> { }) .unwrap_or_default() { - par.push_front(ParChild::Spacing(Spacing::Relative(indent))) + par.push_front(ParChild::Spacing(indent.into())); } let node = ParNode(par).pack(); diff --git a/src/eval/layout.rs b/src/eval/layout.rs index 09b69253..f92a31f5 100644 --- a/src/eval/layout.rs +++ b/src/eval/layout.rs @@ -5,12 +5,10 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::Hash; use std::sync::Arc; -use super::{Barrier, RawAlign, StyleChain}; +use super::{Barrier, RawAlign, RawLength, Resolve, StyleChain}; use crate::diag::TypResult; use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; -use crate::geom::{ - Align, Length, Numeric, Paint, Point, Relative, Sides, Size, Spec, Transform, -}; +use crate::geom::{Align, Length, Paint, Point, Relative, Sides, Size, Spec}; use crate::library::graphics::MoveNode; use crate::library::layout::{AlignNode, PadNode}; use crate::util::Prehashed; @@ -163,7 +161,7 @@ impl LayoutNode { } /// Force a size for this node. - pub fn sized(self, sizing: Spec<Option<Relative<Length>>>) -> Self { + pub fn sized(self, sizing: Spec<Option<Relative<RawLength>>>) -> Self { if sizing.any(Option::is_some) { SizedNode { sizing, child: self }.pack() } else { @@ -191,7 +189,7 @@ impl LayoutNode { } /// Pad this node at the sides. - pub fn padded(self, padding: Sides<Relative<Length>>) -> Self { + pub fn padded(self, padding: Sides<Relative<RawLength>>) -> Self { if !padding.left.is_zero() || !padding.top.is_zero() || !padding.right.is_zero() @@ -204,13 +202,9 @@ impl LayoutNode { } /// Transform this node's contents without affecting layout. - pub fn moved(self, offset: Point) -> Self { - if !offset.is_zero() { - MoveNode { - transform: Transform::translate(offset.x, offset.y), - child: self, - } - .pack() + pub fn moved(self, delta: Spec<Relative<RawLength>>) -> Self { + if delta.any(|r| !r.is_zero()) { + MoveNode { delta, child: self }.pack() } else { self } @@ -294,7 +288,7 @@ impl Layout for EmptyNode { #[derive(Debug, Hash)] struct SizedNode { /// How to size the node horizontally and vertically. - sizing: Spec<Option<Relative<Length>>>, + sizing: Spec<Option<Relative<RawLength>>>, /// The node to be sized. child: LayoutNode, } @@ -311,8 +305,9 @@ impl Layout for SizedNode { // Resolve the sizing to a concrete size. let size = self .sizing + .resolve(styles) .zip(regions.base) - .map(|(s, b)| s.map(|v| v.resolve(b))) + .map(|(s, b)| s.map(|v| v.relative_to(b))) .unwrap_or(regions.first); // Select the appropriate base and expansion for the child depending diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 8b777a64..3f580178 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -43,7 +43,7 @@ use parking_lot::{MappedRwLockWriteGuard, RwLockWriteGuard}; use unicode_segmentation::UnicodeSegmentation; use crate::diag::{At, StrResult, Trace, Tracepoint, TypResult}; -use crate::geom::{Angle, Fraction, Length, Ratio}; +use crate::geom::{Angle, Em, Fraction, Length, Ratio}; use crate::library; use crate::syntax::ast::*; use crate::syntax::{Span, Spanned}; @@ -245,10 +245,13 @@ impl Eval for Lit { LitKind::Bool(v) => Value::Bool(v), LitKind::Int(v) => Value::Int(v), LitKind::Float(v) => Value::Float(v), - LitKind::Length(v, unit) => Value::Length(Length::with_unit(v, unit)), - LitKind::Angle(v, unit) => Value::Angle(Angle::with_unit(v, unit)), - LitKind::Percent(v) => Value::Ratio(Ratio::new(v / 100.0)), - LitKind::Fractional(v) => Value::Fraction(Fraction::new(v)), + LitKind::Numeric(v, unit) => match unit { + Unit::Length(unit) => Length::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::Percent => Ratio::new(v / 100.0).into(), + }, LitKind::Str(ref v) => Value::Str(v.clone()), }) } @@ -735,7 +738,7 @@ impl Eval for IncludeExpr { /// Process an import of a module relative to the current location. fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> { // Load the source file. - let full = ctx.resolve(path); + let full = ctx.complete_path(path); let id = ctx.sources.load(&full).map_err(|err| match err.kind() { std::io::ErrorKind::NotFound => error!(span, "file not found"), _ => error!(span, "failed to load source file ({})", err), diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 0ba4320e..89802949 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -150,8 +150,8 @@ pub fn mul(lhs: Value, rhs: Value) -> StrResult<Value> { (Length(a), Int(b)) => Length(a * b as f64), (Length(a), Float(b)) => Length(a * b), - (Int(a), Length(b)) => Length(a as f64 * b), - (Float(a), Length(b)) => Length(a * b), + (Int(a), Length(b)) => Length(b * a as f64), + (Float(a), Length(b)) => Length(b * a), (Angle(a), Int(b)) => Angle(a * b as f64), (Angle(a), Float(b)) => Angle(a * b), @@ -194,7 +194,6 @@ 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(a / b), (Angle(a), Int(b)) => Angle(a / b as f64), (Angle(a), Float(b)) => Angle(a / b), diff --git a/src/eval/raw.rs b/src/eval/raw.rs index 337638f9..622a0562 100644 --- a/src/eval/raw.rs +++ b/src/eval/raw.rs @@ -1,8 +1,9 @@ use std::fmt::{self, Debug, Formatter}; +use std::ops::{Add, Div, Mul, Neg}; use super::{Resolve, StyleChain}; -use crate::geom::{Align, SpecAxis}; -use crate::library::text::ParNode; +use crate::geom::{Align, Em, Length, Numeric, Relative, SpecAxis}; +use crate::library::text::{ParNode, TextNode}; /// The unresolved alignment representation. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -47,3 +48,131 @@ impl Debug for RawAlign { } } } + +/// 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, Ord, PartialOrd, 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 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 Numeric for RawLength { + fn zero() -> Self { + Self::zero() + } + + fn is_finite(self) -> bool { + self.length.is_finite() && self.em.is_finite() + } +} + +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/eval/value.rs b/src/eval/value.rs index 12948d72..1851cf28 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -4,9 +4,9 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use super::{ops, Args, Array, Content, Context, Dict, Func, Layout, StrExt}; +use super::{ops, Args, Array, Content, Context, Dict, Func, Layout, RawLength, StrExt}; use crate::diag::{with_alternative, At, StrResult, TypResult}; -use crate::geom::{Angle, Color, Fraction, Length, Ratio, Relative, RgbaColor}; +use crate::geom::{Angle, Color, Em, Fraction, Length, Ratio, Relative, RgbaColor}; use crate::library::text::RawNode; use crate::syntax::{Span, Spanned}; use crate::util::EcoString; @@ -25,13 +25,13 @@ pub enum Value { /// A floating-point number: `1.2`, `10e-4`. Float(f64), /// A length: `12pt`, `3cm`. - Length(Length), + Length(RawLength), /// 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<Length>), + Relative(Relative<RawLength>), /// A fraction: `1fr`. Fraction(Fraction), /// A color value: `#f79143ff`. @@ -77,10 +77,10 @@ impl Value { Self::Bool(_) => bool::TYPE_NAME, Self::Int(_) => i64::TYPE_NAME, Self::Float(_) => f64::TYPE_NAME, - Self::Length(_) => Length::TYPE_NAME, + Self::Length(_) => RawLength::TYPE_NAME, Self::Angle(_) => Angle::TYPE_NAME, Self::Ratio(_) => Ratio::TYPE_NAME, - Self::Relative(_) => Relative::TYPE_NAME, + Self::Relative(_) => Relative::<RawLength>::TYPE_NAME, Self::Fraction(_) => Fraction::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME, Self::Str(_) => EcoString::TYPE_NAME, @@ -320,6 +320,18 @@ impl From<usize> for Value { } } +impl From<Length> for Value { + fn from(v: Length) -> Self { + Self::Length(v.into()) + } +} + +impl From<Em> for Value { + fn from(v: Em) -> Self { + Self::Length(v.into()) + } +} + impl From<RgbaColor> for Value { fn from(v: RgbaColor) -> Self { Self::Color(v.into()) @@ -546,10 +558,10 @@ macro_rules! castable { primitive! { bool: "boolean", Bool } primitive! { i64: "integer", Int } primitive! { f64: "float", Float, Int(v) => v as f64 } -primitive! { Length: "length", Length } +primitive! { RawLength: "length", Length } primitive! { Angle: "angle", Angle } primitive! { Ratio: "ratio", Ratio } -primitive! { Relative<Length>: "relative length", Relative, Length(v) => v.into(), Ratio(v) => v.into() } +primitive! { Relative<RawLength>: "relative length", Relative, Length(v) => v.into(), Ratio(v) => v.into() } primitive! { Fraction: "fraction", Fraction } primitive! { Color: "color", Color } primitive! { EcoString: "string", Str } @@ -685,7 +697,10 @@ mod tests { test(Length::pt(5.5), "5.5pt"); test(Angle::deg(90.0), "90deg"); test(Ratio::one() / 2.0, "50%"); - test(Ratio::new(0.3) + Length::cm(2.0), "30% + 56.69pt"); + test( + Ratio::new(0.3) + RawLength::from(Length::cm(2.0)), + "30% + 56.69pt", + ); test(Fraction::one() * 7.55, "7.55fr"); test(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101"); |
