summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-08 15:08:26 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-08 15:45:14 +0200
commit712c00ecb72b67da2c0788e5d3eb4dcc6366b2a7 (patch)
treef5d7ef4341a4728c980d020cc173fa6bb70feaff /src/eval
parent977ac77e6a3298be2644a8231e93acbef9f7f396 (diff)
Em units
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/content.rs2
-rw-r--r--src/eval/layout.rs25
-rw-r--r--src/eval/mod.rs15
-rw-r--r--src/eval/ops.rs5
-rw-r--r--src/eval/raw.rs133
-rw-r--r--src/eval/value.rs33
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");