diff --git a/src/geom/angle.rs b/src/geom/angle.rs index 023b1d98e..b6fa3f41a 100644 --- a/src/geom/angle.rs +++ b/src/geom/angle.rs @@ -50,6 +50,11 @@ impl Angle { pub fn to_unit(self, unit: AngularUnit) -> f64 { self.to_raw() / unit.raw_scale() } + + /// The absolute value of the this angle. + pub fn abs(self) -> Self { + Self::raw(self.to_raw().abs()) + } } impl Debug for Angle { diff --git a/src/geom/fr.rs b/src/geom/fr.rs index ed0c83293..5f3e5534b 100644 --- a/src/geom/fr.rs +++ b/src/geom/fr.rs @@ -29,6 +29,11 @@ impl Fractional { pub fn is_zero(self) -> bool { self.0 == 0.0 } + + /// The absolute value of the this fractional. + pub fn abs(self) -> Self { + Self::new(self.get().abs()) + } } impl Debug for Fractional { diff --git a/src/geom/length.rs b/src/geom/length.rs index f8484f75d..b9eb7b753 100644 --- a/src/geom/length.rs +++ b/src/geom/length.rs @@ -92,6 +92,11 @@ impl Length { self.0.into_inner().is_infinite() } + /// The absolute value of the this length. + pub fn abs(self) -> Self { + Self::raw(self.to_raw().abs()) + } + /// The minimum of this and another length. pub fn min(self, other: Self) -> Self { Self(self.0.min(other.0)) diff --git a/src/geom/relative.rs b/src/geom/relative.rs index 056af2066..9122af84a 100644 --- a/src/geom/relative.rs +++ b/src/geom/relative.rs @@ -42,6 +42,11 @@ impl Relative { pub fn is_zero(self) -> bool { self.0 == 0.0 } + + /// The absolute value of the this relative. + pub fn abs(self) -> Self { + Self::new(self.get().abs()) + } } impl Debug for Relative { diff --git a/src/library/mod.rs b/src/library/mod.rs index d02c9608f..ca99d43ba 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -61,6 +61,7 @@ pub fn new() -> Scope { std.def_func("repr", repr); std.def_func("len", len); std.def_func("rgb", rgb); + std.def_func("abs", abs); std.def_func("min", min); std.def_func("max", max); std.def_func("lower", lower); diff --git a/src/library/utility.rs b/src/library/utility.rs index b26ed1549..0ece88acd 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -45,6 +45,21 @@ pub fn rgb(_: &mut EvalContext, args: &mut Arguments) -> TypResult { ))) } +/// `abs`: The absolute value of a numeric value. +pub fn abs(_: &mut EvalContext, args: &mut Arguments) -> TypResult { + let Spanned { v, span } = args.expect("numeric value")?; + Ok(match v { + Value::Int(v) => Value::Int(v.abs()), + Value::Float(v) => Value::Float(v.abs()), + Value::Length(v) => Value::Length(v.abs()), + Value::Angle(v) => Value::Angle(v.abs()), + Value::Relative(v) => Value::Relative(v.abs()), + Value::Fractional(v) => Value::Fractional(v.abs()), + Value::Linear(_) => bail!(span, "cannot take absolute value of a linear"), + _ => bail!(span, "expected numeric value"), + }) +} + /// `min`: The minimum of a sequence of values. pub fn min(_: &mut EvalContext, args: &mut Arguments) -> TypResult { minmax(args, Ordering::Less) diff --git a/tests/typ/utility/math.typ b/tests/typ/utility/math.typ index 05c3639f5..aeb0d6adb 100644 --- a/tests/typ/utility/math.typ +++ b/tests/typ/utility/math.typ @@ -1,6 +1,24 @@ // Test math functions. // Ref: false +--- +// Test `abs` function. +#test(abs(-3), 3) +#test(abs(3), 3) +#test(abs(-0.0), 0.0) +#test(abs(0.0), -0.0) +#test(abs(-3.14), 3.14) +#test(abs(-12pt), 12pt) +#test(abs(50%), 50%) + +--- +// Error: 6-16 cannot take absolute value of a linear +#abs(10pt + 50%) + +--- +// Error: 6-17 expected numeric value +#abs("no number") + --- // Test `min` and `max` functions. #test(min(2, -4), -4)