typst/src/geom.rs
2020-08-30 22:18:55 +02:00

320 lines
8.5 KiB
Rust

//! Geometrical types.
use std::fmt::{self, Debug, Formatter};
use std::ops::*;
use crate::layout::prelude::*;
/// A value in two dimensions.
#[derive(Default, Copy, Clone, Eq, PartialEq)]
pub struct Value2<T> {
/// The horizontal component.
pub x: T,
/// The vertical component.
pub y: T,
}
impl<T: Clone> Value2<T> {
/// Create a new 2D-value from two values.
pub fn new(x: T, y: T) -> Self {
Self { x, y }
}
/// Create a new 2D-value with `x` set to a value and `y` to default.
pub fn with_x(x: T) -> Self
where
T: Default,
{
Self { x, y: T::default() }
}
/// Create a new 2D-value with `y` set to a value and `x` to default.
pub fn with_y(y: T) -> Self
where
T: Default,
{
Self { x: T::default(), y }
}
/// Create a 2D-value with `x` and `y` set to the same value `s`.
pub fn with_all(s: T) -> Self {
Self { x: s.clone(), y: s }
}
/// Get the specificed component.
pub fn get(self, axis: SpecAxis) -> T {
match axis {
Horizontal => self.x,
Vertical => self.y,
}
}
/// Borrow the specificed component mutably.
pub fn get_mut(&mut self, axis: SpecAxis) -> &mut T {
match axis {
Horizontal => &mut self.x,
Vertical => &mut self.y,
}
}
/// Return the primary value of this specialized 2D-value.
pub fn primary(self, axes: LayoutAxes) -> T {
if axes.primary.axis() == Horizontal {
self.x
} else {
self.y
}
}
/// Borrow the primary value of this specialized 2D-value mutably.
pub fn primary_mut(&mut self, axes: LayoutAxes) -> &mut T {
if axes.primary.axis() == Horizontal {
&mut self.x
} else {
&mut self.y
}
}
/// Return the secondary value of this specialized 2D-value.
pub fn secondary(self, axes: LayoutAxes) -> T {
if axes.primary.axis() == Horizontal {
self.y
} else {
self.x
}
}
/// Borrow the secondary value of this specialized 2D-value mutably.
pub fn secondary_mut(&mut self, axes: LayoutAxes) -> &mut T {
if axes.primary.axis() == Horizontal {
&mut self.y
} else {
&mut self.x
}
}
/// Returns the generalized version of a `Size2D` dependent on the layouting
/// axes, that is:
/// - `x` describes the primary axis instead of the horizontal one.
/// - `y` describes the secondary axis instead of the vertical one.
pub fn generalized(self, axes: LayoutAxes) -> Self {
match axes.primary.axis() {
Horizontal => self,
Vertical => Self { x: self.y, y: self.x },
}
}
/// Returns the specialized version of this generalized Size2D (inverse to
/// `generalized`).
pub fn specialized(self, axes: LayoutAxes) -> Self {
// In fact, generalized is its own inverse. For reasons of clarity
// at the call site, we still have this second function.
self.generalized(axes)
}
/// Swap the `x` and `y` values.
pub fn swap(&mut self) {
std::mem::swap(&mut self.x, &mut self.y);
}
}
impl<T: Debug> Debug for Value2<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_list().entry(&self.x).entry(&self.y).finish()
}
}
/// A position or extent in 2-dimensional space.
pub type Size = Value2<f64>;
impl Size {
/// The zeroed size.
pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
/// Whether the given size fits into this one, that is, both coordinate
/// values are smaller or equal.
pub fn fits(self, other: Self) -> bool {
self.x >= other.x && self.y >= other.y
}
/// Return a size padded by the paddings of the given box.
pub fn padded(self, padding: Margins) -> Self {
Size {
x: self.x + padding.left + padding.right,
y: self.y + padding.top + padding.bottom,
}
}
/// Return a size reduced by the paddings of the given box.
pub fn unpadded(self, padding: Margins) -> Self {
Size {
x: self.x - padding.left - padding.right,
y: self.y - padding.top - padding.bottom,
}
}
/// The anchor position along the given axis for an item with the given
/// alignment in a container with this size.
///
/// This assumes the size to be generalized such that `x` corresponds to the
/// primary axis.
pub fn anchor(self, align: LayoutAlign, axes: LayoutAxes) -> Self {
Size {
x: anchor(self.x, align.primary, axes.primary),
y: anchor(self.y, align.secondary, axes.secondary),
}
}
}
fn anchor(length: f64, align: GenAlign, dir: Dir) -> f64 {
match (dir.is_positive(), align) {
(true, Start) | (false, End) => 0.0,
(_, Center) => length / 2.0,
(true, End) | (false, Start) => length,
}
}
impl Neg for Size {
type Output = Size;
fn neg(self) -> Size {
Size { x: -self.x, y: -self.y }
}
}
/// A value in four dimensions.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct Value4<T> {
/// The left extent.
pub left: T,
/// The top extent.
pub top: T,
/// The right extent.
pub right: T,
/// The bottom extent.
pub bottom: T,
}
impl<T: Clone> Value4<T> {
/// Create a new box from four sizes.
pub fn new(left: T, top: T, right: T, bottom: T) -> Self {
Value4 { left, top, right, bottom }
}
/// Create a box with all four fields set to the same value `s`.
pub fn with_all(value: T) -> Self {
Value4 {
left: value.clone(),
top: value.clone(),
right: value.clone(),
bottom: value,
}
}
/// Get a mutable reference to the value for the specified direction at the
/// alignment.
///
/// Center alignment is treated the same as origin alignment.
pub fn get_mut(&mut self, mut dir: Dir, align: GenAlign) -> &mut T {
if align == End {
dir = dir.inv();
}
match dir {
LTR => &mut self.left,
RTL => &mut self.right,
TTB => &mut self.top,
BTT => &mut self.bottom,
}
}
/// Set all values to the given value.
pub fn set_all(&mut self, value: T) {
*self = Value4::with_all(value);
}
}
/// A length in four dimensions.
pub type Margins = Value4<f64>;
impl Margins {
/// The zero margins.
pub const ZERO: Margins = Margins {
left: 0.0,
top: 0.0,
right: 0.0,
bottom: 0.0,
};
}
macro_rules! implement_traits {
($ty:ident, $t:ident, $o:ident
reflexive {$(
($tr:ident($tf:ident), $at:ident($af:ident), [$($f:ident),*])
)*}
numbers { $(($w:ident: $($rest:tt)*))* }
) => {
$(impl $tr for $ty {
type Output = $ty;
fn $tf($t, $o: $ty) -> $ty {
$ty { $($f: $tr::$tf($t.$f, $o.$f),)* }
}
}
impl $at for $ty {
fn $af(&mut $t, $o: $ty) { $($at::$af(&mut $t.$f, $o.$f);)* }
})*
$(implement_traits!(@$w f64, $ty $t $o $($rest)*);)*
};
(@front $num:ty, $ty:ident $t:ident $o:ident
$tr:ident($tf:ident),
[$($f:ident),*]
) => {
impl $tr<$ty> for $num {
type Output = $ty;
fn $tf($t, $o: $ty) -> $ty {
$ty { $($f: $tr::$tf($t as f64, $o.$f),)* }
}
}
};
(@back $num:ty, $ty:ident $t:ident $o:ident
$tr:ident($tf:ident), $at:ident($af:ident),
[$($f:ident),*]
) => {
impl $tr<$num> for $ty {
type Output = $ty;
fn $tf($t, $o: $num) -> $ty {
$ty { $($f: $tr::$tf($t.$f, $o as f64),)* }
}
}
impl $at<$num> for $ty {
fn $af(&mut $t, $o: $num) { $($at::$af(&mut $t.$f, $o as f64);)* }
}
};
}
macro_rules! implement_size {
($ty:ident($t:ident, $o:ident) [$($f:ident),*]) => {
implement_traits! {
$ty, $t, $o
reflexive {
(Add(add), AddAssign(add_assign), [$($f),*])
(Sub(sub), SubAssign(sub_assign), [$($f),*])
}
numbers {
(front: Mul(mul), [$($f),*])
(back: Mul(mul), MulAssign(mul_assign), [$($f),*])
(back: Div(div), DivAssign(div_assign), [$($f),*])
}
}
};
}
implement_size! { Size(self, other) [x, y] }