summaryrefslogtreecommitdiff
path: root/src/geom
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/geom
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'src/geom')
-rw-r--r--src/geom/abs.rs266
-rw-r--r--src/geom/align.rs239
-rw-r--r--src/geom/angle.rs188
-rw-r--r--src/geom/axes.rs305
-rw-r--r--src/geom/color.rs386
-rw-r--r--src/geom/corners.rs219
-rw-r--r--src/geom/dir.rs79
-rw-r--r--src/geom/ellipse.rs22
-rw-r--r--src/geom/em.rs153
-rw-r--r--src/geom/fr.rs119
-rw-r--r--src/geom/length.rs128
-rw-r--r--src/geom/macros.rs47
-rw-r--r--src/geom/mod.rs121
-rw-r--r--src/geom/paint.rs30
-rw-r--r--src/geom/path.rs54
-rw-r--r--src/geom/point.rs146
-rw-r--r--src/geom/ratio.rs133
-rw-r--r--src/geom/rel.rs246
-rw-r--r--src/geom/rounded.rs182
-rw-r--r--src/geom/scalar.rs175
-rw-r--r--src/geom/shape.rs35
-rw-r--r--src/geom/sides.rs268
-rw-r--r--src/geom/size.rs78
-rw-r--r--src/geom/smart.rs146
-rw-r--r--src/geom/stroke.rs387
-rw-r--r--src/geom/transform.rs77
26 files changed, 0 insertions, 4229 deletions
diff --git a/src/geom/abs.rs b/src/geom/abs.rs
deleted file mode 100644
index 4ca3a9a1..00000000
--- a/src/geom/abs.rs
+++ /dev/null
@@ -1,266 +0,0 @@
-use super::*;
-
-/// An absolute length.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Abs(Scalar);
-
-impl Abs {
- /// The zero length.
- pub const fn zero() -> Self {
- Self(Scalar(0.0))
- }
-
- /// The infinite length.
- pub const fn inf() -> Self {
- Self(Scalar(f64::INFINITY))
- }
-
- /// Create an absolute length from a number of raw units.
- pub const fn raw(raw: f64) -> Self {
- Self(Scalar(raw))
- }
-
- /// Create an absolute length from a value in a unit.
- pub fn with_unit(val: f64, unit: AbsUnit) -> Self {
- Self(Scalar(val * unit.raw_scale()))
- }
-
- /// Create an absolute length from a number of points.
- pub fn pt(pt: f64) -> Self {
- Self::with_unit(pt, AbsUnit::Pt)
- }
-
- /// Create an absolute length from a number of millimeters.
- pub fn mm(mm: f64) -> Self {
- Self::with_unit(mm, AbsUnit::Mm)
- }
-
- /// Create an absolute length from a number of centimeters.
- pub fn cm(cm: f64) -> Self {
- Self::with_unit(cm, AbsUnit::Cm)
- }
-
- /// Create an absolute length from a number of inches.
- pub fn inches(inches: f64) -> Self {
- Self::with_unit(inches, AbsUnit::In)
- }
-
- /// Get the value of this absolute length in raw units.
- pub const fn to_raw(self) -> f64 {
- (self.0).0
- }
-
- /// Get the value of this absolute length in a unit.
- pub fn to_unit(self, unit: AbsUnit) -> f64 {
- self.to_raw() / unit.raw_scale()
- }
-
- /// Convert this to a number of points.
- pub fn to_pt(self) -> f64 {
- self.to_unit(AbsUnit::Pt)
- }
-
- /// Convert this to a number of millimeters.
- pub fn to_mm(self) -> f64 {
- self.to_unit(AbsUnit::Mm)
- }
-
- /// Convert this to a number of centimeters.
- pub fn to_cm(self) -> f64 {
- self.to_unit(AbsUnit::Cm)
- }
-
- /// Convert this to a number of inches.
- pub fn to_inches(self) -> f64 {
- self.to_unit(AbsUnit::In)
- }
-
- /// The absolute value of this length.
- pub fn abs(self) -> Self {
- Self::raw(self.to_raw().abs())
- }
-
- /// The minimum of this and another absolute length.
- pub fn min(self, other: Self) -> Self {
- Self(self.0.min(other.0))
- }
-
- /// Set to the minimum of this and another absolute length.
- pub fn set_min(&mut self, other: Self) {
- *self = (*self).min(other);
- }
-
- /// The maximum of this and another absolute length.
- pub fn max(self, other: Self) -> Self {
- Self(self.0.max(other.0))
- }
-
- /// Set to the maximum of this and another absolute length.
- pub fn set_max(&mut self, other: Self) {
- *self = (*self).max(other);
- }
-
- /// Whether the other absolute length fits into this one (i.e. is smaller).
- /// Allows for a bit of slack.
- pub fn fits(self, other: Self) -> bool {
- self.0 + 1e-6 >= other.0
- }
-
- /// Compares two absolute lengths for whether they are approximately equal.
- pub fn approx_eq(self, other: Self) -> bool {
- self == other || (self - other).to_raw().abs() < 1e-6
- }
-
- /// Perform a checked division by a number, returning zero if the result
- /// is not finite.
- pub fn safe_div(self, number: f64) -> Self {
- let result = self.to_raw() / number;
- if result.is_finite() {
- Self::raw(result)
- } else {
- Self::zero()
- }
- }
-}
-
-impl Numeric for Abs {
- fn zero() -> Self {
- Self::zero()
- }
-
- fn is_finite(self) -> bool {
- self.0.is_finite()
- }
-}
-
-impl Debug for Abs {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{}pt", round_2(self.to_pt()))
- }
-}
-
-impl Neg for Abs {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self(-self.0)
- }
-}
-
-impl Add for Abs {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self(self.0 + other.0)
- }
-}
-
-sub_impl!(Abs - Abs -> Abs);
-
-impl Mul<f64> for Abs {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self(self.0 * other)
- }
-}
-
-impl Mul<Abs> for f64 {
- type Output = Abs;
-
- fn mul(self, other: Abs) -> Abs {
- other * self
- }
-}
-
-impl Div<f64> for Abs {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self(self.0 / other)
- }
-}
-
-impl Div for Abs {
- type Output = f64;
-
- fn div(self, other: Self) -> f64 {
- self.to_raw() / other.to_raw()
- }
-}
-
-assign_impl!(Abs += Abs);
-assign_impl!(Abs -= Abs);
-assign_impl!(Abs *= f64);
-assign_impl!(Abs /= f64);
-
-impl Rem for Abs {
- type Output = Self;
-
- fn rem(self, other: Self) -> Self::Output {
- Self(self.0 % other.0)
- }
-}
-
-impl Sum for Abs {
- fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- Self(iter.map(|s| s.0).sum())
- }
-}
-
-impl<'a> Sum<&'a Self> for Abs {
- fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
- Self(iter.map(|s| s.0).sum())
- }
-}
-
-cast! {
- Abs,
- self => Value::Length(self.into()),
-}
-
-/// Different units of absolute measurement.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub enum AbsUnit {
- /// Points.
- Pt,
- /// Millimeters.
- Mm,
- /// Centimeters.
- Cm,
- /// Inches.
- In,
-}
-
-impl AbsUnit {
- /// How many raw units correspond to a value of `1.0` in this unit.
- fn raw_scale(self) -> f64 {
- match self {
- AbsUnit::Pt => 1.0,
- AbsUnit::Mm => 2.83465,
- AbsUnit::Cm => 28.3465,
- AbsUnit::In => 72.0,
- }
- }
-}
-
-impl Debug for AbsUnit {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(match self {
- AbsUnit::Mm => "mm",
- AbsUnit::Pt => "pt",
- AbsUnit::Cm => "cm",
- AbsUnit::In => "in",
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_length_unit_conversion() {
- assert!((Abs::mm(150.0).to_cm() - 15.0) < 1e-4);
- }
-}
diff --git a/src/geom/align.rs b/src/geom/align.rs
deleted file mode 100644
index 47acd3a6..00000000
--- a/src/geom/align.rs
+++ /dev/null
@@ -1,239 +0,0 @@
-use super::*;
-
-/// Where to align something along an axis.
-#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub enum Align {
- /// Align at the left side.
- Left,
- /// Align in the horizontal middle.
- Center,
- /// Align at the right side.
- Right,
- /// Align at the top side.
- Top,
- /// Align in the vertical middle.
- Horizon,
- /// Align at the bottom side.
- Bottom,
-}
-
-impl Align {
- /// Top-left alignment.
- pub const LEFT_TOP: Axes<Self> = Axes { x: Align::Left, y: Align::Top };
-
- /// Center-horizon alignment.
- pub const CENTER_HORIZON: Axes<Self> = Axes { x: Align::Center, y: Align::Horizon };
-
- /// The axis this alignment belongs to.
- pub const fn axis(self) -> Axis {
- match self {
- Self::Left | Self::Center | Self::Right => Axis::X,
- Self::Top | Self::Horizon | Self::Bottom => Axis::Y,
- }
- }
-
- /// The inverse alignment.
- pub const fn inv(self) -> Self {
- match self {
- Self::Left => Self::Right,
- Self::Center => Self::Center,
- Self::Right => Self::Left,
- Self::Top => Self::Bottom,
- Self::Horizon => Self::Horizon,
- Self::Bottom => Self::Top,
- }
- }
-
- /// Returns the position of this alignment in a container with the given
- /// extent.
- pub fn position(self, extent: Abs) -> Abs {
- match self {
- Self::Left | Self::Top => Abs::zero(),
- Self::Center | Self::Horizon => extent / 2.0,
- Self::Right | Self::Bottom => extent,
- }
- }
-}
-
-impl From<Side> for Align {
- fn from(side: Side) -> Self {
- match side {
- Side::Left => Self::Left,
- Side::Top => Self::Top,
- Side::Right => Self::Right,
- Side::Bottom => Self::Bottom,
- }
- }
-}
-
-impl Debug for Align {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(match self {
- Self::Left => "left",
- Self::Center => "center",
- Self::Right => "right",
- Self::Top => "top",
- Self::Horizon => "horizon",
- Self::Bottom => "bottom",
- })
- }
-}
-
-/// The generic alignment representation.
-#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub enum GenAlign {
- /// Align at the start side of the text direction.
- Start,
- /// Align at the end side of the text direction.
- End,
- /// Align at a specific alignment.
- Specific(Align),
-}
-
-impl GenAlign {
- /// The axis this alignment belongs to.
- pub const fn axis(self) -> Axis {
- match self {
- Self::Start | Self::End => Axis::X,
- Self::Specific(align) => align.axis(),
- }
- }
-}
-
-impl From<Align> for GenAlign {
- fn from(align: Align) -> Self {
- Self::Specific(align)
- }
-}
-
-impl From<HorizontalAlign> for GenAlign {
- fn from(align: HorizontalAlign) -> Self {
- align.0
- }
-}
-
-impl From<VerticalAlign> for GenAlign {
- fn from(align: VerticalAlign) -> Self {
- align.0
- }
-}
-
-impl Debug for GenAlign {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Start => f.pad("start"),
- Self::End => f.pad("end"),
- Self::Specific(align) => align.fmt(f),
- }
- }
-}
-
-cast! {
- type GenAlign: "alignment",
-}
-
-cast! {
- type Axes<GenAlign>: "2d alignment",
-}
-
-cast! {
- Axes<Align>,
- self => self.map(GenAlign::from).into_value(),
-}
-
-cast! {
- Axes<Option<GenAlign>>,
- self => match (self.x, self.y) {
- (Some(x), Some(y)) => Axes::new(x, y).into_value(),
- (Some(x), None) => x.into_value(),
- (None, Some(y)) => y.into_value(),
- (None, None) => Value::None,
- },
- align: GenAlign => {
- let mut aligns = Axes::default();
- aligns.set(align.axis(), Some(align));
- aligns
- },
- aligns: Axes<GenAlign> => aligns.map(Some),
-}
-
-impl From<Axes<GenAlign>> for Axes<Option<GenAlign>> {
- fn from(axes: Axes<GenAlign>) -> Self {
- axes.map(Some)
- }
-}
-
-impl From<Axes<Align>> for Axes<Option<GenAlign>> {
- fn from(axes: Axes<Align>) -> Self {
- axes.map(GenAlign::Specific).into()
- }
-}
-
-impl From<Align> for Axes<Option<GenAlign>> {
- fn from(align: Align) -> Self {
- let mut axes = Axes::splat(None);
- axes.set(align.axis(), Some(align.into()));
- axes
- }
-}
-
-impl Resolve for GenAlign {
- type Output = Align;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- let dir = item!(dir)(styles);
- match self {
- Self::Start => dir.start().into(),
- Self::End => dir.end().into(),
- Self::Specific(align) => align,
- }
- }
-}
-
-impl Fold for GenAlign {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
-
-impl Fold for Align {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
-
-/// Utility struct to restrict a passed alignment value to the horizontal axis
-/// on cast.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct HorizontalAlign(pub GenAlign);
-
-cast! {
- HorizontalAlign,
- self => self.0.into_value(),
- align: GenAlign => {
- if align.axis() != Axis::X {
- bail!("alignment must be horizontal");
- }
- Self(align)
- },
-}
-
-/// Utility struct to restrict a passed alignment value to the vertical axis on
-/// cast.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct VerticalAlign(pub GenAlign);
-
-cast! {
- VerticalAlign,
- self => self.0.into_value(),
- align: GenAlign => {
- if align.axis() != Axis::Y {
- bail!("alignment must be vertical");
- }
- Self(align)
- },
-}
diff --git a/src/geom/angle.rs b/src/geom/angle.rs
deleted file mode 100644
index c03810d9..00000000
--- a/src/geom/angle.rs
+++ /dev/null
@@ -1,188 +0,0 @@
-use super::*;
-
-/// An angle.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Angle(Scalar);
-
-impl Angle {
- /// The zero angle.
- pub const fn zero() -> Self {
- Self(Scalar(0.0))
- }
-
- /// Create an angle from a number of raw units.
- pub const fn raw(raw: f64) -> Self {
- Self(Scalar(raw))
- }
-
- /// Create an angle from a value in a unit.
- pub fn with_unit(val: f64, unit: AngleUnit) -> Self {
- Self(Scalar(val * unit.raw_scale()))
- }
-
- /// Create an angle from a number of radians.
- pub fn rad(rad: f64) -> Self {
- Self::with_unit(rad, AngleUnit::Rad)
- }
-
- /// Create an angle from a number of degrees.
- pub fn deg(deg: f64) -> Self {
- Self::with_unit(deg, AngleUnit::Deg)
- }
-
- /// Get the value of this angle in raw units.
- pub const fn to_raw(self) -> f64 {
- (self.0).0
- }
-
- /// Get the value of this angle in a unit.
- pub fn to_unit(self, unit: AngleUnit) -> f64 {
- self.to_raw() / unit.raw_scale()
- }
-
- /// Convert this to a number of radians.
- pub fn to_rad(self) -> f64 {
- self.to_unit(AngleUnit::Rad)
- }
-
- /// Convert this to a number of degrees.
- pub fn to_deg(self) -> f64 {
- self.to_unit(AngleUnit::Deg)
- }
-
- /// The absolute value of the this angle.
- pub fn abs(self) -> Self {
- Self::raw(self.to_raw().abs())
- }
-
- /// Get the sine of this angle in radians.
- pub fn sin(self) -> f64 {
- self.to_rad().sin()
- }
-
- /// Get the cosine of this angle in radians.
- pub fn cos(self) -> f64 {
- self.to_rad().cos()
- }
-
- /// Get the tangent of this angle in radians.
- pub fn tan(self) -> f64 {
- self.to_rad().tan()
- }
-}
-
-impl Numeric for Angle {
- fn zero() -> Self {
- Self::zero()
- }
-
- fn is_finite(self) -> bool {
- self.0.is_finite()
- }
-}
-
-impl Debug for Angle {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{}deg", round_2(self.to_deg()))
- }
-}
-
-impl Neg for Angle {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self(-self.0)
- }
-}
-
-impl Add for Angle {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self(self.0 + other.0)
- }
-}
-
-sub_impl!(Angle - Angle -> Angle);
-
-impl Mul<f64> for Angle {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self(self.0 * other)
- }
-}
-
-impl Mul<Angle> for f64 {
- type Output = Angle;
-
- fn mul(self, other: Angle) -> Angle {
- other * self
- }
-}
-
-impl Div<f64> for Angle {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self(self.0 / other)
- }
-}
-
-impl Div for Angle {
- type Output = f64;
-
- fn div(self, other: Self) -> f64 {
- self.to_raw() / other.to_raw()
- }
-}
-
-assign_impl!(Angle += Angle);
-assign_impl!(Angle -= Angle);
-assign_impl!(Angle *= f64);
-assign_impl!(Angle /= f64);
-
-impl Sum for Angle {
- fn sum<I: Iterator<Item = Angle>>(iter: I) -> Self {
- Self(iter.map(|s| s.0).sum())
- }
-}
-
-/// Different units of angular measurement.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub enum AngleUnit {
- /// Radians.
- Rad,
- /// Degrees.
- Deg,
-}
-
-impl AngleUnit {
- /// How many raw units correspond to a value of `1.0` in this unit.
- fn raw_scale(self) -> f64 {
- match self {
- Self::Rad => 1.0,
- Self::Deg => PI / 180.0,
- }
- }
-}
-
-impl Debug for AngleUnit {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(match self {
- Self::Rad => "rad",
- Self::Deg => "deg",
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_angle_unit_conversion() {
- assert!((Angle::rad(2.0 * PI).to_deg() - 360.0) < 1e-4);
- assert!((Angle::deg(45.0).to_rad() - std::f64::consts::FRAC_PI_4) < 1e-4);
- }
-}
diff --git a/src/geom/axes.rs b/src/geom/axes.rs
deleted file mode 100644
index 059d3bb2..00000000
--- a/src/geom/axes.rs
+++ /dev/null
@@ -1,305 +0,0 @@
-use std::any::Any;
-use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
-
-use super::*;
-
-/// A container with a horizontal and vertical component.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Axes<T> {
- /// The horizontal component.
- pub x: T,
- /// The vertical component.
- pub y: T,
-}
-
-impl<T> Axes<T> {
- /// Create a new instance from the two components.
- pub const fn new(x: T, y: T) -> Self {
- Self { x, y }
- }
-
- /// Create a new instance with two equal components.
- pub fn splat(v: T) -> Self
- where
- T: Clone,
- {
- Self { x: v.clone(), y: v }
- }
-
- /// Map the individual fields with `f`.
- pub fn map<F, U>(self, mut f: F) -> Axes<U>
- where
- F: FnMut(T) -> U,
- {
- Axes { x: f(self.x), y: f(self.y) }
- }
-
- /// Convert from `&Axes<T>` to `Axes<&T>`.
- pub fn as_ref(&self) -> Axes<&T> {
- Axes { x: &self.x, y: &self.y }
- }
-
- /// Convert from `&Axes<T>` to `Axes<&<T as Deref>::Target>`.
- pub fn as_deref(&self) -> Axes<&T::Target>
- where
- T: Deref,
- {
- Axes { x: &self.x, y: &self.y }
- }
-
- /// Convert from `&mut Axes<T>` to `Axes<&mut T>`.
- pub fn as_mut(&mut self) -> Axes<&mut T> {
- Axes { x: &mut self.x, y: &mut self.y }
- }
-
- /// Zip two instances into an instance over a tuple.
- pub fn zip<U>(self, other: Axes<U>) -> Axes<(T, U)> {
- Axes { x: (self.x, other.x), y: (self.y, other.y) }
- }
-
- /// Whether a condition is true for at least one of fields.
- pub fn any<F>(self, mut f: F) -> bool
- where
- F: FnMut(&T) -> bool,
- {
- f(&self.x) || f(&self.y)
- }
-
- /// Whether a condition is true for both fields.
- pub fn all<F>(self, mut f: F) -> bool
- where
- F: FnMut(&T) -> bool,
- {
- f(&self.x) && f(&self.y)
- }
-
- /// Filter the individual fields with a mask.
- pub fn filter(self, mask: Axes<bool>) -> Axes<Option<T>> {
- Axes {
- x: if mask.x { Some(self.x) } else { None },
- y: if mask.y { Some(self.y) } else { None },
- }
- }
-}
-
-impl<T: Default> Axes<T> {
- /// Create a new instance with y set to its default value.
- pub fn with_x(x: T) -> Self {
- Self { x, y: T::default() }
- }
-
- /// Create a new instance with x set to its default value.
- pub fn with_y(y: T) -> Self {
- Self { x: T::default(), y }
- }
-}
-
-impl<T: Ord> Axes<T> {
- /// The component-wise minimum of this and another instance.
- pub fn min(self, other: Self) -> Self {
- Self { x: self.x.min(other.x), y: self.y.min(other.y) }
- }
-
- /// The component-wise minimum of this and another instance.
- pub fn max(self, other: Self) -> Self {
- Self { x: self.x.max(other.x), y: self.y.max(other.y) }
- }
-
- /// The minimum of width and height.
- pub fn min_by_side(self) -> T {
- self.x.min(self.y)
- }
-
- /// The minimum of width and height.
- pub fn max_by_side(self) -> T {
- self.x.max(self.y)
- }
-}
-
-impl<T> Get<Axis> for Axes<T> {
- type Component = T;
-
- fn get_ref(&self, axis: Axis) -> &T {
- match axis {
- Axis::X => &self.x,
- Axis::Y => &self.y,
- }
- }
-
- fn get_mut(&mut self, axis: Axis) -> &mut T {
- match axis {
- Axis::X => &mut self.x,
- Axis::Y => &mut self.y,
- }
- }
-}
-
-impl<T> Debug for Axes<T>
-where
- T: Debug + 'static,
-{
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- if let Axes { x: Some(x), y: Some(y) } =
- self.as_ref().map(|v| (v as &dyn Any).downcast_ref::<GenAlign>())
- {
- write!(f, "{:?} + {:?}", x, y)
- } else if (&self.x as &dyn Any).is::<Abs>() {
- write!(f, "Size({:?}, {:?})", self.x, self.y)
- } else {
- write!(f, "Axes({:?}, {:?})", self.x, self.y)
- }
- }
-}
-
-/// The two layouting axes.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Axis {
- /// The horizontal axis.
- X,
- /// The vertical axis.
- Y,
-}
-
-impl Axis {
- /// The direction with the given positivity for this axis.
- pub fn dir(self, positive: bool) -> Dir {
- match (self, positive) {
- (Self::X, true) => Dir::LTR,
- (Self::X, false) => Dir::RTL,
- (Self::Y, true) => Dir::TTB,
- (Self::Y, false) => Dir::BTT,
- }
- }
-
- /// The other axis.
- pub fn other(self) -> Self {
- match self {
- Self::X => Self::Y,
- Self::Y => Self::X,
- }
- }
-}
-
-impl Debug for Axis {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(match self {
- Self::X => "horizontal",
- Self::Y => "vertical",
- })
- }
-}
-
-impl<T> Axes<Option<T>> {
- /// Unwrap the individual fields.
- pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
- Axes {
- x: self.x.unwrap_or(other.x),
- y: self.y.unwrap_or(other.y),
- }
- }
-}
-
-impl<T> Axes<Smart<T>> {
- /// Unwrap the individual fields.
- pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
- Axes {
- x: self.x.unwrap_or(other.x),
- y: self.y.unwrap_or(other.y),
- }
- }
-}
-
-impl Axes<bool> {
- /// Select `t.x` if `self.x` is true and `f.x` otherwise and same for `y`.
- pub fn select<T>(self, t: Axes<T>, f: Axes<T>) -> Axes<T> {
- Axes {
- x: if self.x { t.x } else { f.x },
- y: if self.y { t.y } else { f.y },
- }
- }
-}
-
-impl Not for Axes<bool> {
- type Output = Self;
-
- fn not(self) -> Self::Output {
- Self { x: !self.x, y: !self.y }
- }
-}
-
-impl BitOr for Axes<bool> {
- type Output = Self;
-
- fn bitor(self, rhs: Self) -> Self::Output {
- Self { x: self.x | rhs.x, y: self.y | rhs.y }
- }
-}
-
-impl BitOr<bool> for Axes<bool> {
- type Output = Self;
-
- fn bitor(self, rhs: bool) -> Self::Output {
- Self { x: self.x | rhs, y: self.y | rhs }
- }
-}
-
-impl BitAnd for Axes<bool> {
- type Output = Self;
-
- fn bitand(self, rhs: Self) -> Self::Output {
- Self { x: self.x & rhs.x, y: self.y & rhs.y }
- }
-}
-
-impl BitAnd<bool> for Axes<bool> {
- type Output = Self;
-
- fn bitand(self, rhs: bool) -> Self::Output {
- Self { x: self.x & rhs, y: self.y & rhs }
- }
-}
-
-impl BitOrAssign for Axes<bool> {
- fn bitor_assign(&mut self, rhs: Self) {
- self.x |= rhs.x;
- self.y |= rhs.y;
- }
-}
-
-impl BitAndAssign for Axes<bool> {
- fn bitand_assign(&mut self, rhs: Self) {
- self.x &= rhs.x;
- self.y &= rhs.y;
- }
-}
-
-cast! {
- Axes<Rel<Length>>,
- self => array![self.x, self.y].into_value(),
- array: Array => {
- let mut iter = array.into_iter();
- match (iter.next(), iter.next(), iter.next()) {
- (Some(a), Some(b), None) => Axes::new(a.cast()?, b.cast()?),
- _ => bail!("point array must contain exactly two entries"),
- }
- },
-}
-
-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))
- }
-}
-
-impl<T: Fold> Fold for Axes<Option<T>> {
- type Output = Axes<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
- })
- }
-}
diff --git a/src/geom/color.rs b/src/geom/color.rs
deleted file mode 100644
index c7676c2b..00000000
--- a/src/geom/color.rs
+++ /dev/null
@@ -1,386 +0,0 @@
-use std::str::FromStr;
-
-use super::*;
-
-/// A color in a dynamic format.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Color {
- /// An 8-bit luma color.
- Luma(LumaColor),
- /// An 8-bit RGBA color.
- Rgba(RgbaColor),
- /// An 8-bit CMYK color.
- Cmyk(CmykColor),
-}
-
-impl Color {
- pub const BLACK: Self = Self::Rgba(RgbaColor::new(0x00, 0x00, 0x00, 0xFF));
- pub const GRAY: Self = Self::Rgba(RgbaColor::new(0xAA, 0xAA, 0xAA, 0xFF));
- pub const SILVER: Self = Self::Rgba(RgbaColor::new(0xDD, 0xDD, 0xDD, 0xFF));
- pub const WHITE: Self = Self::Rgba(RgbaColor::new(0xFF, 0xFF, 0xFF, 0xFF));
- pub const NAVY: Self = Self::Rgba(RgbaColor::new(0x00, 0x1f, 0x3f, 0xFF));
- pub const BLUE: Self = Self::Rgba(RgbaColor::new(0x00, 0x74, 0xD9, 0xFF));
- pub const AQUA: Self = Self::Rgba(RgbaColor::new(0x7F, 0xDB, 0xFF, 0xFF));
- pub const TEAL: Self = Self::Rgba(RgbaColor::new(0x39, 0xCC, 0xCC, 0xFF));
- pub const EASTERN: Self = Self::Rgba(RgbaColor::new(0x23, 0x9D, 0xAD, 0xFF));
- pub const PURPLE: Self = Self::Rgba(RgbaColor::new(0xB1, 0x0D, 0xC9, 0xFF));
- pub const FUCHSIA: Self = Self::Rgba(RgbaColor::new(0xF0, 0x12, 0xBE, 0xFF));
- pub const MAROON: Self = Self::Rgba(RgbaColor::new(0x85, 0x14, 0x4b, 0xFF));
- pub const RED: Self = Self::Rgba(RgbaColor::new(0xFF, 0x41, 0x36, 0xFF));
- pub const ORANGE: Self = Self::Rgba(RgbaColor::new(0xFF, 0x85, 0x1B, 0xFF));
- pub const YELLOW: Self = Self::Rgba(RgbaColor::new(0xFF, 0xDC, 0x00, 0xFF));
- pub const OLIVE: Self = Self::Rgba(RgbaColor::new(0x3D, 0x99, 0x70, 0xFF));
- pub const GREEN: Self = Self::Rgba(RgbaColor::new(0x2E, 0xCC, 0x40, 0xFF));
- pub const LIME: Self = Self::Rgba(RgbaColor::new(0x01, 0xFF, 0x70, 0xFF));
-
- /// Convert this color to RGBA.
- pub fn to_rgba(self) -> RgbaColor {
- match self {
- Self::Luma(luma) => luma.to_rgba(),
- Self::Rgba(rgba) => rgba,
- Self::Cmyk(cmyk) => cmyk.to_rgba(),
- }
- }
-
- /// Lighten this color by the given factor.
- pub fn lighten(self, factor: Ratio) -> Self {
- match self {
- Self::Luma(luma) => Self::Luma(luma.lighten(factor)),
- Self::Rgba(rgba) => Self::Rgba(rgba.lighten(factor)),
- Self::Cmyk(cmyk) => Self::Cmyk(cmyk.lighten(factor)),
- }
- }
-
- /// Darken this color by the given factor.
- pub fn darken(self, factor: Ratio) -> Self {
- match self {
- Self::Luma(luma) => Self::Luma(luma.darken(factor)),
- Self::Rgba(rgba) => Self::Rgba(rgba.darken(factor)),
- Self::Cmyk(cmyk) => Self::Cmyk(cmyk.darken(factor)),
- }
- }
-
- /// Negate this color.
- pub fn negate(self) -> Self {
- match self {
- Self::Luma(luma) => Self::Luma(luma.negate()),
- Self::Rgba(rgba) => Self::Rgba(rgba.negate()),
- Self::Cmyk(cmyk) => Self::Cmyk(cmyk.negate()),
- }
- }
-}
-
-impl Debug for Color {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Luma(c) => Debug::fmt(c, f),
- Self::Rgba(c) => Debug::fmt(c, f),
- Self::Cmyk(c) => Debug::fmt(c, f),
- }
- }
-}
-
-/// An 8-bit grayscale color.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct LumaColor(pub u8);
-
-impl LumaColor {
- /// Construct a new luma color.
- pub const fn new(luma: u8) -> Self {
- Self(luma)
- }
-
- /// Convert to an opque RGBA color.
- pub const fn to_rgba(self) -> RgbaColor {
- RgbaColor::new(self.0, self.0, self.0, u8::MAX)
- }
-
- /// Convert to CMYK as a fraction of true black.
- pub fn to_cmyk(self) -> CmykColor {
- CmykColor::new(
- round_u8(self.0 as f64 * 0.75),
- round_u8(self.0 as f64 * 0.68),
- round_u8(self.0 as f64 * 0.67),
- round_u8(self.0 as f64 * 0.90),
- )
- }
-
- /// Lighten this color by a factor.
- pub fn lighten(self, factor: Ratio) -> Self {
- let inc = round_u8((u8::MAX - self.0) as f64 * factor.get());
- Self(self.0.saturating_add(inc))
- }
-
- /// Darken this color by a factor.
- pub fn darken(self, factor: Ratio) -> Self {
- let dec = round_u8(self.0 as f64 * factor.get());
- Self(self.0.saturating_sub(dec))
- }
-
- /// Negate this color.
- pub fn negate(self) -> Self {
- Self(u8::MAX - self.0)
- }
-}
-
-impl Debug for LumaColor {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "luma({})", self.0)
- }
-}
-
-impl From<LumaColor> for Color {
- fn from(luma: LumaColor) -> Self {
- Self::Luma(luma)
- }
-}
-
-/// An 8-bit RGBA color.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct RgbaColor {
- /// Red channel.
- pub r: u8,
- /// Green channel.
- pub g: u8,
- /// Blue channel.
- pub b: u8,
- /// Alpha channel.
- pub a: u8,
-}
-
-impl RgbaColor {
- /// Construct a new RGBA color.
- pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
- Self { r, g, b, a }
- }
-
- /// Lighten this color by a factor.
- ///
- /// The alpha channel is not affected.
- pub fn lighten(self, factor: Ratio) -> Self {
- let lighten =
- |c: u8| c.saturating_add(round_u8((u8::MAX - c) as f64 * factor.get()));
- Self {
- r: lighten(self.r),
- g: lighten(self.g),
- b: lighten(self.b),
- a: self.a,
- }
- }
-
- /// Darken this color by a factor.
- ///
- /// The alpha channel is not affected.
- pub fn darken(self, factor: Ratio) -> Self {
- let darken = |c: u8| c.saturating_sub(round_u8(c as f64 * factor.get()));
- Self {
- r: darken(self.r),
- g: darken(self.g),
- b: darken(self.b),
- a: self.a,
- }
- }
-
- /// Negate this color.
- ///
- /// The alpha channel is not affected.
- pub fn negate(self) -> Self {
- Self {
- r: u8::MAX - self.r,
- g: u8::MAX - self.g,
- b: u8::MAX - self.b,
- a: self.a,
- }
- }
-}
-
-impl FromStr for RgbaColor {
- type Err = &'static str;
-
- /// Constructs a new color from hex strings like the following:
- /// - `#aef` (shorthand, with leading hashtag),
- /// - `7a03c2` (without alpha),
- /// - `abcdefff` (with alpha).
- ///
- /// The hashtag is optional and both lower and upper case are fine.
- fn from_str(hex_str: &str) -> Result<Self, Self::Err> {
- let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str);
- if hex_str.chars().any(|c| !c.is_ascii_hexdigit()) {
- return Err("color string contains non-hexadecimal letters");
- }
-
- let len = hex_str.len();
- let long = len == 6 || len == 8;
- let short = len == 3 || len == 4;
- let alpha = len == 4 || len == 8;
- if !long && !short {
- return Err("color string has wrong length");
- }
-
- let mut values: [u8; 4] = [u8::MAX; 4];
- for elem in if alpha { 0..4 } else { 0..3 } {
- let item_len = if long { 2 } else { 1 };
- let pos = elem * item_len;
-
- let item = &hex_str[pos..(pos + item_len)];
- values[elem] = u8::from_str_radix(item, 16).unwrap();
-
- if short {
- // Duplicate number for shorthand notation, i.e. `a` -> `aa`
- values[elem] += values[elem] * 16;
- }
- }
-
- Ok(Self::new(values[0], values[1], values[2], values[3]))
- }
-}
-
-impl Debug for RgbaColor {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- if f.alternate() {
- write!(f, "rgba({}, {}, {}, {})", self.r, self.g, self.b, self.a,)?;
- } else {
- write!(f, "rgb(\"#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?;
- if self.a != 255 {
- write!(f, "{:02x}", self.a)?;
- }
- write!(f, "\")")?;
- }
- Ok(())
- }
-}
-
-impl<T: Into<RgbaColor>> From<T> for Color {
- fn from(rgba: T) -> Self {
- Self::Rgba(rgba.into())
- }
-}
-
-cast! {
- RgbaColor,
- self => Value::Color(self.into()),
-}
-
-/// An 8-bit CMYK color.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct CmykColor {
- /// The cyan component.
- pub c: u8,
- /// The magenta component.
- pub m: u8,
- /// The yellow component.
- pub y: u8,
- /// The key (black) component.
- pub k: u8,
-}
-
-impl CmykColor {
- /// Construct a new CMYK color.
- pub const fn new(c: u8, m: u8, y: u8, k: u8) -> Self {
- Self { c, m, y, k }
- }
-
- /// Convert this color to RGBA.
- pub fn to_rgba(self) -> RgbaColor {
- let k = self.k as f64 / 255.0;
- let f = |c| {
- let c = c as f64 / 255.0;
- round_u8(255.0 * (1.0 - c) * (1.0 - k))
- };
-
- RgbaColor { r: f(self.c), g: f(self.m), b: f(self.y), a: 255 }
- }
-
- /// Lighten this color by a factor.
- pub fn lighten(self, factor: Ratio) -> Self {
- let lighten = |c: u8| c.saturating_sub(round_u8(c as f64 * factor.get()));
- Self {
- c: lighten(self.c),
- m: lighten(self.m),
- y: lighten(self.y),
- k: lighten(self.k),
- }
- }
-
- /// Darken this color by a factor.
- pub fn darken(self, factor: Ratio) -> Self {
- let darken =
- |c: u8| c.saturating_add(round_u8((u8::MAX - c) as f64 * factor.get()));
- Self {
- c: darken(self.c),
- m: darken(self.m),
- y: darken(self.y),
- k: darken(self.k),
- }
- }
-
- /// Negate this color.
- ///
- /// Does not affect the key component.
- pub fn negate(self) -> Self {
- Self {
- c: u8::MAX - self.c,
- m: u8::MAX - self.m,
- y: u8::MAX - self.y,
- k: self.k,
- }
- }
-}
-
-impl Debug for CmykColor {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- let g = |c| 100.0 * (c as f64 / 255.0);
- write!(
- f,
- "cmyk({:.1}%, {:.1}%, {:.1}%, {:.1}%)",
- g(self.c),
- g(self.m),
- g(self.y),
- g(self.k),
- )
- }
-}
-
-impl From<CmykColor> for Color {
- fn from(cmyk: CmykColor) -> Self {
- Self::Cmyk(cmyk)
- }
-}
-
-/// Convert to the closest u8.
-fn round_u8(value: f64) -> u8 {
- value.round() as u8
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_parse_color_strings() {
- #[track_caller]
- fn test(hex: &str, r: u8, g: u8, b: u8, a: u8) {
- assert_eq!(RgbaColor::from_str(hex), Ok(RgbaColor::new(r, g, b, a)));
- }
-
- test("f61243ff", 0xf6, 0x12, 0x43, 0xff);
- test("b3d8b3", 0xb3, 0xd8, 0xb3, 0xff);
- test("fCd2a9AD", 0xfc, 0xd2, 0xa9, 0xad);
- test("233", 0x22, 0x33, 0x33, 0xff);
- test("111b", 0x11, 0x11, 0x11, 0xbb);
- }
-
- #[test]
- fn test_parse_invalid_colors() {
- #[track_caller]
- fn test(hex: &str, message: &str) {
- assert_eq!(RgbaColor::from_str(hex), Err(message));
- }
-
- test("a5", "color string has wrong length");
- test("12345", "color string has wrong length");
- test("f075ff011", "color string has wrong length");
- test("hmmm", "color string contains non-hexadecimal letters");
- test("14B2AH", "color string contains non-hexadecimal letters");
- }
-}
diff --git a/src/geom/corners.rs b/src/geom/corners.rs
deleted file mode 100644
index 5ee1e063..00000000
--- a/src/geom/corners.rs
+++ /dev/null
@@ -1,219 +0,0 @@
-use crate::eval::{CastInfo, FromValue, IntoValue, Reflect};
-
-use super::*;
-
-/// A container with components for the four corners of a rectangle.
-#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Corners<T> {
- /// The value for the top left corner.
- pub top_left: T,
- /// The value for the top right corner.
- pub top_right: T,
- /// The value for the bottom right corner.
- pub bottom_right: T,
- /// The value for the bottom left corner.
- pub bottom_left: T,
-}
-
-impl<T> Corners<T> {
- /// Create a new instance from the four components.
- pub const fn new(top_left: T, top_right: T, bottom_right: T, bottom_left: T) -> Self {
- Self { top_left, top_right, bottom_right, bottom_left }
- }
-
- /// Create an instance with four equal components.
- pub fn splat(value: T) -> Self
- where
- T: Clone,
- {
- Self {
- top_left: value.clone(),
- top_right: value.clone(),
- bottom_right: value.clone(),
- bottom_left: value,
- }
- }
-
- /// Map the individual fields with `f`.
- pub fn map<F, U>(self, mut f: F) -> Corners<U>
- where
- F: FnMut(T) -> U,
- {
- Corners {
- top_left: f(self.top_left),
- top_right: f(self.top_right),
- bottom_right: f(self.bottom_right),
- bottom_left: f(self.bottom_left),
- }
- }
-
- /// Zip two instances into one.
- pub fn zip<U>(self, other: Corners<U>) -> Corners<(T, U)> {
- Corners {
- top_left: (self.top_left, other.top_left),
- top_right: (self.top_right, other.top_right),
- bottom_right: (self.bottom_right, other.bottom_right),
- bottom_left: (self.bottom_left, other.bottom_left),
- }
- }
-
- /// An iterator over the corners, starting with the top left corner,
- /// clockwise.
- pub fn iter(&self) -> impl Iterator<Item = &T> {
- [&self.top_left, &self.top_right, &self.bottom_right, &self.bottom_left]
- .into_iter()
- }
-
- /// Whether all sides are equal.
- pub fn is_uniform(&self) -> bool
- where
- T: PartialEq,
- {
- self.top_left == self.top_right
- && self.top_right == self.bottom_right
- && self.bottom_right == self.bottom_left
- }
-}
-
-impl<T> Get<Corner> for Corners<T> {
- type Component = T;
-
- fn get_ref(&self, corner: Corner) -> &T {
- match corner {
- Corner::TopLeft => &self.top_left,
- Corner::TopRight => &self.top_right,
- Corner::BottomRight => &self.bottom_right,
- Corner::BottomLeft => &self.bottom_left,
- }
- }
-
- fn get_mut(&mut self, corner: Corner) -> &mut T {
- match corner {
- Corner::TopLeft => &mut self.top_left,
- Corner::TopRight => &mut self.top_right,
- Corner::BottomRight => &mut self.bottom_right,
- Corner::BottomLeft => &mut self.bottom_left,
- }
- }
-}
-
-/// The four corners of a rectangle.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Corner {
- /// The top left corner.
- TopLeft,
- /// The top right corner.
- TopRight,
- /// The bottom right corner.
- BottomRight,
- /// The bottom left corner.
- BottomLeft,
-}
-
-impl<T: Reflect> Reflect for Corners<Option<T>> {
- fn describe() -> CastInfo {
- T::describe() + Dict::describe()
- }
-
- fn castable(value: &Value) -> bool {
- Dict::castable(value) || T::castable(value)
- }
-}
-
-impl<T> IntoValue for Corners<T>
-where
- T: PartialEq + IntoValue,
-{
- fn into_value(self) -> Value {
- if self.is_uniform() {
- return self.top_left.into_value();
- }
-
- let mut dict = Dict::new();
- let mut handle = |key: &str, component: T| {
- let value = component.into_value();
- if value != Value::None {
- dict.insert(key.into(), value);
- }
- };
-
- handle("top-left", self.top_left);
- handle("top-right", self.top_right);
- handle("bottom-right", self.bottom_right);
- handle("bottom-left", self.bottom_left);
-
- Value::Dict(dict)
- }
-}
-
-impl<T> FromValue for Corners<Option<T>>
-where
- T: FromValue + Clone,
-{
- fn from_value(mut value: Value) -> StrResult<Self> {
- let keys = [
- "top-left",
- "top-right",
- "bottom-right",
- "bottom-left",
- "left",
- "top",
- "right",
- "bottom",
- "rest",
- ];
-
- if let Value::Dict(dict) = &mut value {
- if dict.iter().any(|(key, _)| keys.contains(&key.as_str())) {
- let mut take = |key| dict.take(key).ok().map(T::from_value).transpose();
- let rest = take("rest")?;
- let left = take("left")?.or_else(|| rest.clone());
- let top = take("top")?.or_else(|| rest.clone());
- let right = take("right")?.or_else(|| rest.clone());
- let bottom = take("bottom")?.or_else(|| rest.clone());
- let corners = Corners {
- top_left: take("top-left")?
- .or_else(|| top.clone())
- .or_else(|| left.clone()),
- top_right: take("top-right")?
- .or_else(|| top.clone())
- .or_else(|| right.clone()),
- bottom_right: take("bottom-right")?
- .or_else(|| bottom.clone())
- .or_else(|| right.clone()),
- bottom_left: take("bottom-left")?
- .or_else(|| bottom.clone())
- .or_else(|| left.clone()),
- };
-
- dict.finish(&keys)?;
- return Ok(corners);
- }
- }
-
- if T::castable(&value) {
- Ok(Self::splat(Some(T::from_value(value)?)))
- } else {
- Err(Self::error(&value))
- }
- }
-}
-
-impl<T: Resolve> Resolve for Corners<T> {
- type Output = Corners<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Fold> Fold for Corners<Option<T>> {
- type Output = Corners<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
- })
- }
-}
diff --git a/src/geom/dir.rs b/src/geom/dir.rs
deleted file mode 100644
index 48915471..00000000
--- a/src/geom/dir.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-use super::*;
-
-/// The four directions into which content can be laid out.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Dir {
- /// Left to right.
- LTR,
- /// Right to left.
- RTL,
- /// Top to bottom.
- TTB,
- /// Bottom to top.
- BTT,
-}
-
-impl Dir {
- /// The specific axis this direction belongs to.
- pub const fn axis(self) -> Axis {
- match self {
- Self::LTR | Self::RTL => Axis::X,
- Self::TTB | Self::BTT => Axis::Y,
- }
- }
-
- /// The side this direction starts at.
- pub const fn start(self) -> Side {
- match self {
- Self::LTR => Side::Left,
- Self::RTL => Side::Right,
- Self::TTB => Side::Top,
- Self::BTT => Side::Bottom,
- }
- }
-
- /// The side this direction ends at.
- pub const fn end(self) -> Side {
- match self {
- Self::LTR => Side::Right,
- Self::RTL => Side::Left,
- Self::TTB => Side::Bottom,
- Self::BTT => Side::Top,
- }
- }
-
- /// The inverse direction.
- pub const fn inv(self) -> Self {
- match self {
- Self::LTR => Self::RTL,
- Self::RTL => Self::LTR,
- Self::TTB => Self::BTT,
- Self::BTT => Self::TTB,
- }
- }
-
- /// Whether this direction points into the positive coordinate direction.
- ///
- /// The positive directions are left-to-right and top-to-bottom.
- pub const fn is_positive(self) -> bool {
- match self {
- Self::LTR | Self::TTB => true,
- Self::RTL | Self::BTT => false,
- }
- }
-}
-
-impl Debug for Dir {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(match self {
- Self::LTR => "ltr",
- Self::RTL => "rtl",
- Self::TTB => "ttb",
- Self::BTT => "btt",
- })
- }
-}
-
-cast! {
- type Dir: "direction",
-}
diff --git a/src/geom/ellipse.rs b/src/geom/ellipse.rs
deleted file mode 100644
index ac20ffd3..00000000
--- a/src/geom/ellipse.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use super::*;
-
-/// Produce a shape that approximates an axis-aligned ellipse.
-pub fn ellipse(size: Size, fill: Option<Paint>, stroke: Option<Stroke>) -> Shape {
- // https://stackoverflow.com/a/2007782
- let z = Abs::zero();
- let rx = size.x / 2.0;
- let ry = size.y / 2.0;
- let m = 0.551784;
- let mx = m * rx;
- let my = m * ry;
- let point = |x, y| Point::new(x + rx, y + ry);
-
- let mut path = Path::new();
- path.move_to(point(-rx, z));
- path.cubic_to(point(-rx, -my), point(-mx, -ry), point(z, -ry));
- path.cubic_to(point(mx, -ry), point(rx, -my), point(rx, z));
- path.cubic_to(point(rx, my), point(mx, ry), point(z, ry));
- path.cubic_to(point(-mx, ry), point(-rx, my), point(-rx, z));
-
- Shape { geometry: Geometry::Path(path), stroke, fill }
-}
diff --git a/src/geom/em.rs b/src/geom/em.rs
deleted file mode 100644
index 8dda9ff6..00000000
--- a/src/geom/em.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-use super::*;
-
-/// A length that is relative to the font size.
-///
-/// `1em` is the same as the font size.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Em(Scalar);
-
-impl Em {
- /// The zero em length.
- pub const fn zero() -> Self {
- Self(Scalar(0.0))
- }
-
- /// The font size.
- pub const fn one() -> Self {
- Self(Scalar(1.0))
- }
-
- /// Create a font-relative length.
- pub const fn new(em: f64) -> Self {
- Self(Scalar(em))
- }
-
- /// Create an em length from font units at the given units per em.
- pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
- Self(Scalar(units.into() / units_per_em))
- }
-
- /// Create an em length from a length at the given font size.
- pub fn from_length(length: Abs, font_size: Abs) -> Self {
- let result = length / font_size;
- if result.is_finite() {
- Self(Scalar(result))
- } else {
- Self::zero()
- }
- }
-
- /// The number of em units.
- pub const fn get(self) -> f64 {
- (self.0).0
- }
-
- /// The absolute value of this em length.
- pub fn abs(self) -> Self {
- Self::new(self.get().abs())
- }
-
- /// Convert to an absolute length at the given font size.
- pub fn at(self, font_size: Abs) -> Abs {
- let resolved = font_size * self.get();
- if resolved.is_finite() {
- resolved
- } else {
- Abs::zero()
- }
- }
-}
-
-impl Numeric for Em {
- fn zero() -> Self {
- Self::zero()
- }
-
- fn is_finite(self) -> bool {
- self.0.is_finite()
- }
-}
-
-impl Debug for Em {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{}em", self.get())
- }
-}
-
-impl Neg for Em {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self(-self.0)
- }
-}
-
-impl Add for Em {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self(self.0 + other.0)
- }
-}
-
-sub_impl!(Em - Em -> Em);
-
-impl Mul<f64> for Em {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self(self.0 * other)
- }
-}
-
-impl Mul<Em> for f64 {
- type Output = Em;
-
- fn mul(self, other: Em) -> Em {
- other * self
- }
-}
-
-impl Div<f64> for Em {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self(self.0 / other)
- }
-}
-
-impl Div for Em {
- type Output = f64;
-
- fn div(self, other: Self) -> f64 {
- self.get() / other.get()
- }
-}
-
-assign_impl!(Em += Em);
-assign_impl!(Em -= Em);
-assign_impl!(Em *= f64);
-assign_impl!(Em /= f64);
-
-impl Sum for Em {
- fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- Self(iter.map(|s| s.0).sum())
- }
-}
-
-cast! {
- Em,
- self => Value::Length(self.into()),
-}
-
-impl Resolve for Em {
- type Output = Abs;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- if self.is_zero() {
- Abs::zero()
- } else {
- self.at(item!(em)(styles))
- }
- }
-}
diff --git a/src/geom/fr.rs b/src/geom/fr.rs
deleted file mode 100644
index c602634d..00000000
--- a/src/geom/fr.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-use super::*;
-
-/// A fraction of remaining space.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Fr(Scalar);
-
-impl Fr {
- /// Takes up zero space: `0fr`.
- pub const fn zero() -> Self {
- Self(Scalar(0.0))
- }
-
- /// Takes up as much space as all other items with this fraction: `1fr`.
- pub const fn one() -> Self {
- Self(Scalar(1.0))
- }
-
- /// Create a new fraction.
- pub const fn new(ratio: f64) -> Self {
- Self(Scalar(ratio))
- }
-
- /// Get the underlying number.
- pub const fn get(self) -> f64 {
- (self.0).0
- }
-
- /// The absolute value of this fraction.
- pub fn abs(self) -> Self {
- Self::new(self.get().abs())
- }
-
- /// Determine this fraction's share in the remaining space.
- pub fn share(self, total: Self, remaining: Abs) -> Abs {
- let ratio = self / total;
- if ratio.is_finite() && remaining.is_finite() {
- (ratio * remaining).max(Abs::zero())
- } else {
- Abs::zero()
- }
- }
-}
-
-impl Numeric for Fr {
- fn zero() -> Self {
- Self::zero()
- }
-
- fn is_finite(self) -> bool {
- self.0.is_finite()
- }
-}
-
-impl Debug for Fr {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{}fr", round_2(self.get()))
- }
-}
-
-impl Neg for Fr {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self(-self.0)
- }
-}
-
-impl Add for Fr {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self(self.0 + other.0)
- }
-}
-
-sub_impl!(Fr - Fr -> Fr);
-
-impl Mul<f64> for Fr {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self(self.0 * other)
- }
-}
-
-impl Mul<Fr> for f64 {
- type Output = Fr;
-
- fn mul(self, other: Fr) -> Fr {
- other * self
- }
-}
-
-impl Div<f64> for Fr {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self(self.0 / other)
- }
-}
-
-impl Div for Fr {
- type Output = f64;
-
- fn div(self, other: Self) -> f64 {
- self.get() / other.get()
- }
-}
-
-assign_impl!(Fr += Fr);
-assign_impl!(Fr -= Fr);
-assign_impl!(Fr *= f64);
-assign_impl!(Fr /= f64);
-
-impl Sum for Fr {
- fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- Self(iter.map(|s| s.0).sum())
- }
-}
diff --git a/src/geom/length.rs b/src/geom/length.rs
deleted file mode 100644
index 7d0a9841..00000000
--- a/src/geom/length.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use super::*;
-
-/// A size or distance, possibly expressed with contextual units.
-///
-/// Currently supports absolute and font-relative units, but support could quite
-/// easily be extended to other units.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Length {
- /// The absolute part.
- pub abs: Abs,
- /// The font-relative part.
- pub em: Em,
-}
-
-impl Length {
- /// The zero length.
- pub const fn zero() -> Self {
- Self { abs: Abs::zero(), em: Em::zero() }
- }
-
- /// Try to compute the absolute value of the length.
- pub fn try_abs(self) -> Option<Self> {
- (self.abs.is_zero() || self.em.is_zero())
- .then(|| Self { abs: self.abs.abs(), em: self.em.abs() })
- }
-
- /// Try to divide two lengths.
- pub fn try_div(self, other: Self) -> Option<f64> {
- if self.abs.is_zero() && other.abs.is_zero() {
- Some(self.em / other.em)
- } else if self.em.is_zero() && other.em.is_zero() {
- Some(self.abs / other.abs)
- } else {
- None
- }
- }
-}
-
-impl Debug for Length {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match (self.abs.is_zero(), self.em.is_zero()) {
- (false, false) => write!(f, "{:?} + {:?}", self.abs, self.em),
- (true, false) => self.em.fmt(f),
- (_, true) => self.abs.fmt(f),
- }
- }
-}
-
-impl Numeric for Length {
- fn zero() -> Self {
- Self::zero()
- }
-
- fn is_finite(self) -> bool {
- self.abs.is_finite() && self.em.is_finite()
- }
-}
-
-impl PartialOrd for Length {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- if self.em.is_zero() && other.em.is_zero() {
- self.abs.partial_cmp(&other.abs)
- } else if self.abs.is_zero() && other.abs.is_zero() {
- self.em.partial_cmp(&other.em)
- } else {
- None
- }
- }
-}
-
-impl From<Abs> for Length {
- fn from(abs: Abs) -> Self {
- Self { abs, em: Em::zero() }
- }
-}
-
-impl From<Em> for Length {
- fn from(em: Em) -> Self {
- Self { abs: Abs::zero(), em }
- }
-}
-
-impl Neg for Length {
- type Output = Self;
-
- fn neg(self) -> Self::Output {
- Self { abs: -self.abs, em: -self.em }
- }
-}
-
-impl Add for Length {
- type Output = Self;
-
- fn add(self, rhs: Self) -> Self::Output {
- Self { abs: self.abs + rhs.abs, em: self.em + rhs.em }
- }
-}
-
-sub_impl!(Length - Length -> Length);
-
-impl Mul<f64> for Length {
- type Output = Self;
-
- fn mul(self, rhs: f64) -> Self::Output {
- Self { abs: self.abs * rhs, em: self.em * rhs }
- }
-}
-
-impl Div<f64> for Length {
- type Output = Self;
-
- fn div(self, rhs: f64) -> Self::Output {
- Self { abs: self.abs / rhs, em: self.em / rhs }
- }
-}
-
-assign_impl!(Length += Length);
-assign_impl!(Length -= Length);
-assign_impl!(Length *= f64);
-assign_impl!(Length /= f64);
-
-impl Resolve for Length {
- type Output = Abs;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.abs + self.em.resolve(styles)
- }
-}
diff --git a/src/geom/macros.rs b/src/geom/macros.rs
deleted file mode 100644
index b1b50e22..00000000
--- a/src/geom/macros.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-/// Implement the `Sub` trait based on existing `Neg` and `Add` impls.
-macro_rules! sub_impl {
- ($a:ident - $b:ident -> $c:ident) => {
- impl std::ops::Sub<$b> for $a {
- type Output = $c;
-
- fn sub(self, other: $b) -> $c {
- self + -other
- }
- }
- };
-}
-
-/// Implement an assign trait based on an existing non-assign trait.
-macro_rules! assign_impl {
- ($a:ident += $b:ident) => {
- impl std::ops::AddAssign<$b> for $a {
- fn add_assign(&mut self, other: $b) {
- *self = *self + other;
- }
- }
- };
-
- ($a:ident -= $b:ident) => {
- impl std::ops::SubAssign<$b> for $a {
- fn sub_assign(&mut self, other: $b) {
- *self = *self - other;
- }
- }
- };
-
- ($a:ident *= $b:ident) => {
- impl std::ops::MulAssign<$b> for $a {
- fn mul_assign(&mut self, other: $b) {
- *self = *self * other;
- }
- }
- };
-
- ($a:ident /= $b:ident) => {
- impl std::ops::DivAssign<$b> for $a {
- fn div_assign(&mut self, other: $b) {
- *self = *self / other;
- }
- }
- };
-}
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
deleted file mode 100644
index b7a7ff40..00000000
--- a/src/geom/mod.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-//! Geometrical primitives.
-
-#[macro_use]
-mod macros;
-mod abs;
-mod align;
-mod angle;
-mod axes;
-mod color;
-mod corners;
-mod dir;
-mod ellipse;
-mod em;
-mod fr;
-mod length;
-mod paint;
-mod path;
-mod point;
-mod ratio;
-mod rel;
-mod rounded;
-mod scalar;
-mod shape;
-mod sides;
-mod size;
-mod smart;
-mod stroke;
-mod transform;
-
-pub use self::abs::{Abs, AbsUnit};
-pub use self::align::{Align, GenAlign, HorizontalAlign, VerticalAlign};
-pub use self::angle::{Angle, AngleUnit};
-pub use self::axes::{Axes, Axis};
-pub use self::color::{CmykColor, Color, LumaColor, RgbaColor};
-pub use self::corners::{Corner, Corners};
-pub use self::dir::Dir;
-pub use self::ellipse::ellipse;
-pub use self::em::Em;
-pub use self::fr::Fr;
-pub use self::length::Length;
-pub use self::paint::Paint;
-pub use self::path::{Path, PathItem};
-pub use self::point::Point;
-pub use self::ratio::Ratio;
-pub use self::rel::Rel;
-pub use self::rounded::rounded_rect;
-pub use self::scalar::Scalar;
-pub use self::shape::{Geometry, Shape};
-pub use self::sides::{Side, Sides};
-pub use self::size::Size;
-pub use self::smart::Smart;
-pub use self::stroke::{
- DashLength, DashPattern, LineCap, LineJoin, PartialStroke, Stroke,
-};
-pub use self::transform::Transform;
-
-use std::cmp::Ordering;
-use std::f64::consts::PI;
-use std::fmt::{self, Debug, Formatter};
-use std::hash::{Hash, Hasher};
-use std::iter::Sum;
-use std::ops::*;
-
-use crate::diag::{bail, StrResult};
-use crate::eval::{array, cast, Array, Dict, Value};
-use crate::model::{Fold, Resolve, StyleChain};
-
-/// Generic access to a structure's components.
-pub trait Get<Index> {
- /// The structure's component type.
- type Component;
-
- /// Borrow the component for the specified index.
- fn get_ref(&self, index: Index) -> &Self::Component;
-
- /// Borrow the component for the specified index mutably.
- fn get_mut(&mut self, index: Index) -> &mut Self::Component;
-
- /// Convenience method for getting a copy of a component.
- fn get(self, index: Index) -> Self::Component
- where
- Self: Sized,
- Self::Component: Copy,
- {
- *self.get_ref(index)
- }
-
- /// Convenience method for setting a component.
- fn set(&mut self, index: Index, component: Self::Component) {
- *self.get_mut(index) = component;
- }
-}
-
-/// A numeric type.
-pub trait Numeric:
- Sized
- + Debug
- + Copy
- + PartialEq
- + Neg<Output = Self>
- + Add<Output = Self>
- + Sub<Output = Self>
- + Mul<f64, Output = Self>
- + Div<f64, Output = Self>
-{
- /// The identity element for addition.
- fn zero() -> Self;
-
- /// Whether `self` is zero.
- fn is_zero(self) -> bool {
- self == Self::zero()
- }
-
- /// Whether `self` consists only of finite parts.
- fn is_finite(self) -> bool;
-}
-
-/// Round a float to two decimal places.
-pub fn round_2(value: f64) -> f64 {
- (value * 100.0).round() / 100.0
-}
diff --git a/src/geom/paint.rs b/src/geom/paint.rs
deleted file mode 100644
index 10fa9fde..00000000
--- a/src/geom/paint.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use super::*;
-
-/// How a fill or stroke should be painted.
-#[derive(Clone, Eq, PartialEq, Hash)]
-pub enum Paint {
- /// A solid color.
- Solid(Color),
-}
-
-impl<T: Into<Color>> From<T> for Paint {
- fn from(t: T) -> Self {
- Self::Solid(t.into())
- }
-}
-
-impl Debug for Paint {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Solid(color) => color.fmt(f),
- }
- }
-}
-
-cast! {
- Paint,
- self => match self {
- Self::Solid(color) => Value::Color(color),
- },
- color: Color => Self::Solid(color),
-}
diff --git a/src/geom/path.rs b/src/geom/path.rs
deleted file mode 100644
index 1c5325a3..00000000
--- a/src/geom/path.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use super::*;
-
-/// A bezier path.
-#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
-pub struct Path(pub Vec<PathItem>);
-
-/// An item in a bezier path.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum PathItem {
- MoveTo(Point),
- LineTo(Point),
- CubicTo(Point, Point, Point),
- ClosePath,
-}
-
-impl Path {
- /// Create an empty path.
- pub const fn new() -> Self {
- Self(vec![])
- }
-
- /// Create a path that describes a rectangle.
- pub fn rect(size: Size) -> Self {
- let z = Abs::zero();
- let point = Point::new;
- let mut path = Self::new();
- path.move_to(point(z, z));
- path.line_to(point(size.x, z));
- path.line_to(point(size.x, size.y));
- path.line_to(point(z, size.y));
- path.close_path();
- path
- }
-
- /// Push a [`MoveTo`](PathItem::MoveTo) item.
- pub fn move_to(&mut self, p: Point) {
- self.0.push(PathItem::MoveTo(p));
- }
-
- /// Push a [`LineTo`](PathItem::LineTo) item.
- pub fn line_to(&mut self, p: Point) {
- self.0.push(PathItem::LineTo(p));
- }
-
- /// Push a [`CubicTo`](PathItem::CubicTo) item.
- pub fn cubic_to(&mut self, p1: Point, p2: Point, p3: Point) {
- self.0.push(PathItem::CubicTo(p1, p2, p3));
- }
-
- /// Push a [`ClosePath`](PathItem::ClosePath) item.
- pub fn close_path(&mut self) {
- self.0.push(PathItem::ClosePath);
- }
-}
diff --git a/src/geom/point.rs b/src/geom/point.rs
deleted file mode 100644
index e7811e1e..00000000
--- a/src/geom/point.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-use super::*;
-
-/// A point in 2D.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Point {
- /// The x coordinate.
- pub x: Abs,
- /// The y coordinate.
- pub y: Abs,
-}
-
-impl Point {
- /// The origin point.
- pub const fn zero() -> Self {
- Self { x: Abs::zero(), y: Abs::zero() }
- }
-
- /// Create a new point from x and y coordinates.
- pub const fn new(x: Abs, y: Abs) -> Self {
- Self { x, y }
- }
-
- /// Create an instance with two equal components.
- pub const fn splat(value: Abs) -> Self {
- Self { x: value, y: value }
- }
-
- /// Create a new point with y set to zero.
- pub const fn with_x(x: Abs) -> Self {
- Self { x, y: Abs::zero() }
- }
-
- /// Create a new point with x set to zero.
- pub const fn with_y(y: Abs) -> Self {
- Self { x: Abs::zero(), y }
- }
-
- /// The component-wise minimum of this and another point.
- pub fn min(self, other: Self) -> Self {
- Self { x: self.x.min(other.x), y: self.y.min(other.y) }
- }
-
- /// The component-wise minimum of this and another point.
- pub fn max(self, other: Self) -> Self {
- Self { x: self.x.max(other.x), y: self.y.max(other.y) }
- }
-
- /// The distance between this point and the origin.
- pub fn hypot(self) -> Abs {
- Abs::raw(self.x.to_raw().hypot(self.y.to_raw()))
- }
-
- /// Transform the point with the given transformation.
- pub fn transform(self, ts: Transform) -> Self {
- Self::new(
- ts.sx.of(self.x) + ts.kx.of(self.y) + ts.tx,
- ts.ky.of(self.x) + ts.sy.of(self.y) + ts.ty,
- )
- }
-
- /// Convert to a size.
- pub fn to_size(self) -> Size {
- Size::new(self.x, self.y)
- }
-}
-
-impl Numeric for Point {
- fn zero() -> Self {
- Self::zero()
- }
-
- fn is_finite(self) -> bool {
- self.x.is_finite() && self.y.is_finite()
- }
-}
-
-impl Get<Axis> for Point {
- type Component = Abs;
-
- fn get_ref(&self, axis: Axis) -> &Abs {
- match axis {
- Axis::X => &self.x,
- Axis::Y => &self.y,
- }
- }
-
- fn get_mut(&mut self, axis: Axis) -> &mut Abs {
- match axis {
- Axis::X => &mut self.x,
- Axis::Y => &mut self.y,
- }
- }
-}
-
-impl Debug for Point {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Point({:?}, {:?})", self.x, self.y)
- }
-}
-
-impl Neg for Point {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self { x: -self.x, y: -self.y }
- }
-}
-
-impl Add for Point {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self { x: self.x + other.x, y: self.y + other.y }
- }
-}
-
-sub_impl!(Point - Point -> Point);
-
-impl Mul<f64> for Point {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self { x: self.x * other, y: self.y * other }
- }
-}
-
-impl Mul<Point> for f64 {
- type Output = Point;
-
- fn mul(self, other: Point) -> Point {
- other * self
- }
-}
-
-impl Div<f64> for Point {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self { x: self.x / other, y: self.y / other }
- }
-}
-
-assign_impl!(Point += Point);
-assign_impl!(Point -= Point);
-assign_impl!(Point *= f64);
-assign_impl!(Point /= f64);
diff --git a/src/geom/ratio.rs b/src/geom/ratio.rs
deleted file mode 100644
index fe87dd6c..00000000
--- a/src/geom/ratio.rs
+++ /dev/null
@@ -1,133 +0,0 @@
-use super::*;
-
-/// A ratio of a whole.
-///
-/// _Note_: `50%` is represented as `0.5` here, but stored as `50.0` in the
-/// corresponding [literal](crate::syntax::ast::Numeric).
-#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Ratio(Scalar);
-
-impl Ratio {
- /// A ratio of `0%` represented as `0.0`.
- pub const fn zero() -> Self {
- Self(Scalar(0.0))
- }
-
- /// A ratio of `100%` represented as `1.0`.
- pub const fn one() -> Self {
- Self(Scalar(1.0))
- }
-
- /// Create a new ratio from a value, where `1.0` means `100%`.
- pub const fn new(ratio: f64) -> Self {
- Self(Scalar(ratio))
- }
-
- /// Get the underlying ratio.
- pub const fn get(self) -> f64 {
- (self.0).0
- }
-
- /// Whether the ratio is zero.
- pub fn is_zero(self) -> bool {
- self.0 == 0.0
- }
-
- /// Whether the ratio is one.
- pub fn is_one(self) -> bool {
- self.0 == 1.0
- }
-
- /// The absolute value of this ratio.
- pub fn abs(self) -> Self {
- Self::new(self.get().abs())
- }
-
- /// Return the ratio of the given `whole`.
- pub fn of<T: Numeric>(self, whole: T) -> T {
- let resolved = whole * self.get();
- if resolved.is_finite() {
- resolved
- } else {
- T::zero()
- }
- }
-}
-
-impl Debug for Ratio {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{}%", round_2(100.0 * self.get()))
- }
-}
-
-impl Neg for Ratio {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self(-self.0)
- }
-}
-
-impl Add for Ratio {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self(self.0 + other.0)
- }
-}
-
-sub_impl!(Ratio - Ratio -> Ratio);
-
-impl Mul for Ratio {
- type Output = Self;
-
- fn mul(self, other: Self) -> Self {
- Self(self.0 * other.0)
- }
-}
-
-impl Mul<f64> for Ratio {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self(self.0 * other)
- }
-}
-
-impl Mul<Ratio> for f64 {
- type Output = Ratio;
-
- fn mul(self, other: Ratio) -> Ratio {
- other * self
- }
-}
-
-impl Div<f64> for Ratio {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self(self.0 / other)
- }
-}
-
-impl Div<Ratio> for f64 {
- type Output = Self;
-
- fn div(self, other: Ratio) -> Self {
- self / other.get()
- }
-}
-
-impl Div for Ratio {
- type Output = f64;
-
- fn div(self, other: Self) -> f64 {
- self.get() / other.get()
- }
-}
-
-assign_impl!(Ratio += Ratio);
-assign_impl!(Ratio -= Ratio);
-assign_impl!(Ratio *= Ratio);
-assign_impl!(Ratio *= f64);
-assign_impl!(Ratio /= f64);
diff --git a/src/geom/rel.rs b/src/geom/rel.rs
deleted file mode 100644
index 88972222..00000000
--- a/src/geom/rel.rs
+++ /dev/null
@@ -1,246 +0,0 @@
-use super::*;
-
-/// A value that is composed of a relative and an absolute part.
-#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Rel<T: Numeric> {
- /// The relative part.
- pub rel: Ratio,
- /// The absolute part.
- pub abs: T,
-}
-
-impl<T: Numeric> Rel<T> {
- /// The zero relative.
- pub fn zero() -> Self {
- Self { rel: Ratio::zero(), abs: T::zero() }
- }
-
- /// A relative with a ratio of `100%` and no absolute part.
- pub fn one() -> Self {
- Self { rel: Ratio::one(), abs: T::zero() }
- }
-
- /// Create a new relative from its parts.
- pub fn new(rel: Ratio, abs: T) -> Self {
- Self { rel, abs }
- }
-
- /// Whether both parts are zero.
- pub fn is_zero(self) -> bool {
- self.rel.is_zero() && self.abs == T::zero()
- }
-
- /// Whether the relative part is one and the absolute part is zero.
- pub fn is_one(self) -> bool {
- self.rel.is_one() && self.abs == T::zero()
- }
-
- /// Evaluate this relative to the given `whole`.
- pub fn relative_to(self, whole: T) -> T {
- self.rel.of(whole) + self.abs
- }
-
- /// Map the absolute part with `f`.
- pub fn map<F, U>(self, f: F) -> Rel<U>
- where
- F: FnOnce(T) -> U,
- U: Numeric,
- {
- Rel { rel: self.rel, abs: f(self.abs) }
- }
-}
-
-impl Rel<Length> {
- /// Try to divide two relative lengths.
- pub fn try_div(self, other: Self) -> Option<f64> {
- if self.rel.is_zero() && other.rel.is_zero() {
- self.abs.try_div(other.abs)
- } else if self.abs.is_zero() && other.abs.is_zero() {
- Some(self.rel / other.rel)
- } else {
- None
- }
- }
-}
-
-impl<T: Numeric> Debug for Rel<T> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match (self.rel.is_zero(), self.abs.is_zero()) {
- (false, false) => write!(f, "{:?} + {:?}", self.rel, self.abs),
- (false, true) => self.rel.fmt(f),
- (true, _) => self.abs.fmt(f),
- }
- }
-}
-
-impl From<Abs> for Rel<Length> {
- fn from(abs: Abs) -> Self {
- Rel::from(Length::from(abs))
- }
-}
-
-impl From<Em> for Rel<Length> {
- fn from(em: Em) -> Self {
- Rel::from(Length::from(em))
- }
-}
-
-impl<T: Numeric> From<T> for Rel<T> {
- fn from(abs: T) -> Self {
- Self { rel: Ratio::zero(), abs }
- }
-}
-
-impl<T: Numeric> From<Ratio> for Rel<T> {
- fn from(rel: Ratio) -> Self {
- Self { rel, abs: T::zero() }
- }
-}
-
-impl<T: Numeric + PartialOrd> PartialOrd for Rel<T> {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- if self.rel.is_zero() && other.rel.is_zero() {
- self.abs.partial_cmp(&other.abs)
- } else if self.abs.is_zero() && other.abs.is_zero() {
- self.rel.partial_cmp(&other.rel)
- } else {
- None
- }
- }
-}
-
-impl<T: Numeric> Neg for Rel<T> {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self { rel: -self.rel, abs: -self.abs }
- }
-}
-
-impl<T: Numeric> Add for Rel<T> {
- type Output = Self;
-
- fn add(self, other: Self) -> Self::Output {
- Self {
- rel: self.rel + other.rel,
- abs: self.abs + other.abs,
- }
- }
-}
-
-impl<T: Numeric> Sub for Rel<T> {
- type Output = Self;
-
- fn sub(self, other: Self) -> Self::Output {
- self + -other
- }
-}
-
-impl<T: Numeric> Mul<f64> for Rel<T> {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self::Output {
- Self { rel: self.rel * other, abs: self.abs * other }
- }
-}
-
-impl<T: Numeric> Mul<Rel<T>> for f64 {
- type Output = Rel<T>;
-
- fn mul(self, other: Rel<T>) -> Self::Output {
- other * self
- }
-}
-
-impl<T: Numeric> Div<f64> for Rel<T> {
- type Output = Self;
-
- fn div(self, other: f64) -> Self::Output {
- Self { rel: self.rel / other, abs: self.abs / other }
- }
-}
-
-impl<T: Numeric + AddAssign> AddAssign for Rel<T> {
- fn add_assign(&mut self, other: Self) {
- self.rel += other.rel;
- self.abs += other.abs;
- }
-}
-
-impl<T: Numeric + SubAssign> SubAssign for Rel<T> {
- fn sub_assign(&mut self, other: Self) {
- self.rel -= other.rel;
- self.abs -= other.abs;
- }
-}
-
-impl<T: Numeric + MulAssign<f64>> MulAssign<f64> for Rel<T> {
- fn mul_assign(&mut self, other: f64) {
- self.rel *= other;
- self.abs *= other;
- }
-}
-
-impl<T: Numeric + DivAssign<f64>> DivAssign<f64> for Rel<T> {
- fn div_assign(&mut self, other: f64) {
- self.rel /= other;
- self.abs /= other;
- }
-}
-
-impl<T: Numeric> Add<T> for Ratio {
- type Output = Rel<T>;
-
- fn add(self, other: T) -> Self::Output {
- Rel::from(self) + Rel::from(other)
- }
-}
-
-impl<T: Numeric> Add<T> for Rel<T> {
- type Output = Self;
-
- fn add(self, other: T) -> Self::Output {
- self + Rel::from(other)
- }
-}
-
-impl<T: Numeric> Add<Ratio> for Rel<T> {
- type Output = Self;
-
- fn add(self, other: Ratio) -> Self::Output {
- self + Rel::from(other)
- }
-}
-
-impl<T> Resolve for Rel<T>
-where
- T: Resolve + Numeric,
- <T as Resolve>::Output: Numeric,
-{
- type Output = Rel<<T as Resolve>::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|abs| abs.resolve(styles))
- }
-}
-
-impl Fold for Rel<Abs> {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
-
-impl Fold for Rel<Length> {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
-
-cast! {
- Rel<Abs>,
- self => self.map(Length::from).into_value(),
-}
diff --git a/src/geom/rounded.rs b/src/geom/rounded.rs
deleted file mode 100644
index f1a7ea08..00000000
--- a/src/geom/rounded.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-use super::*;
-
-/// Produce shapes that together make up a rounded rectangle.
-pub fn rounded_rect(
- size: Size,
- radius: Corners<Abs>,
- fill: Option<Paint>,
- stroke: Sides<Option<Stroke>>,
-) -> Vec<Shape> {
- let mut res = vec![];
- if fill.is_some() || (stroke.iter().any(Option::is_some) && stroke.is_uniform()) {
- res.push(Shape {
- geometry: fill_geometry(size, radius),
- fill,
- stroke: if stroke.is_uniform() { stroke.top.clone() } else { None },
- });
- }
-
- if !stroke.is_uniform() {
- for (path, stroke) in stroke_segments(size, radius, stroke) {
- if stroke.is_some() {
- res.push(Shape { geometry: Geometry::Path(path), fill: None, stroke });
- }
- }
- }
-
- res
-}
-
-/// Output the shape of the rectangle as a path or primitive rectangle,
-/// depending on whether it is rounded.
-fn fill_geometry(size: Size, radius: Corners<Abs>) -> Geometry {
- if radius.iter().copied().all(Abs::is_zero) {
- Geometry::Rect(size)
- } else {
- let mut paths = stroke_segments(size, radius, Sides::splat(None));
- assert_eq!(paths.len(), 1);
- Geometry::Path(paths.pop().unwrap().0)
- }
-}
-
-/// Output the minimum number of paths along the rectangles border.
-fn stroke_segments(
- size: Size,
- radius: Corners<Abs>,
- stroke: Sides<Option<Stroke>>,
-) -> Vec<(Path, Option<Stroke>)> {
- let mut res = vec![];
-
- let mut connection = Connection::default();
- let mut path = Path::new();
- let mut always_continuous = true;
- let max_radius = size.x.min(size.y).max(Abs::zero()) / 2.0;
-
- for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] {
- let continuous = stroke.get_ref(side) == stroke.get_ref(side.next_cw());
- connection = connection.advance(continuous && side != Side::Left);
- always_continuous &= continuous;
-
- draw_side(
- &mut path,
- side,
- size,
- radius.get(side.start_corner()).clamp(Abs::zero(), max_radius),
- radius.get(side.end_corner()).clamp(Abs::zero(), max_radius),
- connection,
- );
-
- if !continuous {
- res.push((std::mem::take(&mut path), stroke.get_ref(side).clone()));
- }
- }
-
- if always_continuous {
- path.close_path();
- }
-
- if !path.0.is_empty() {
- res.push((path, stroke.left));
- }
-
- res
-}
-
-/// Draws one side of the rounded rectangle. Will always draw the left arc. The
-/// right arc will be drawn halfway if and only if there is no connection.
-fn draw_side(
- path: &mut Path,
- side: Side,
- size: Size,
- start_radius: Abs,
- end_radius: Abs,
- connection: Connection,
-) {
- let angle_left = Angle::deg(if connection.prev { 90.0 } else { 45.0 });
- let angle_right = Angle::deg(if connection.next { 90.0 } else { 45.0 });
- let length = size.get(side.axis());
-
- // The arcs for a border of the rectangle along the x-axis, starting at (0,0).
- let p1 = Point::with_x(start_radius);
- let mut arc1 = bezier_arc(
- p1 + Point::new(
- -angle_left.sin() * start_radius,
- (1.0 - angle_left.cos()) * start_radius,
- ),
- Point::new(start_radius, start_radius),
- p1,
- );
-
- let p2 = Point::with_x(length - end_radius);
- let mut arc2 = bezier_arc(
- p2,
- Point::new(length - end_radius, end_radius),
- p2 + Point::new(
- angle_right.sin() * end_radius,
- (1.0 - angle_right.cos()) * end_radius,
- ),
- );
-
- let transform = match side {
- Side::Left => Transform::rotate(Angle::deg(-90.0))
- .post_concat(Transform::translate(Abs::zero(), size.y)),
- Side::Bottom => Transform::rotate(Angle::deg(180.0))
- .post_concat(Transform::translate(size.x, size.y)),
- Side::Right => Transform::rotate(Angle::deg(90.0))
- .post_concat(Transform::translate(size.x, Abs::zero())),
- _ => Transform::identity(),
- };
-
- arc1 = arc1.map(|x| x.transform(transform));
- arc2 = arc2.map(|x| x.transform(transform));
-
- if !connection.prev {
- path.move_to(if start_radius.is_zero() { arc1[3] } else { arc1[0] });
- }
-
- if !start_radius.is_zero() {
- path.cubic_to(arc1[1], arc1[2], arc1[3]);
- }
-
- path.line_to(arc2[0]);
-
- if !connection.next && !end_radius.is_zero() {
- path.cubic_to(arc2[1], arc2[2], arc2[3]);
- }
-}
-
-/// Get the control points for a bezier curve that describes a circular arc for
-/// a start point, an end point and a center of the circle whose arc connects
-/// the two.
-fn bezier_arc(start: Point, center: Point, end: Point) -> [Point; 4] {
- // https://stackoverflow.com/a/44829356/1567835
- let a = start - center;
- let b = end - center;
-
- let q1 = a.x.to_raw() * a.x.to_raw() + a.y.to_raw() * a.y.to_raw();
- let q2 = q1 + a.x.to_raw() * b.x.to_raw() + a.y.to_raw() * b.y.to_raw();
- let k2 = (4.0 / 3.0) * ((2.0 * q1 * q2).sqrt() - q2)
- / (a.x.to_raw() * b.y.to_raw() - a.y.to_raw() * b.x.to_raw());
-
- let control_1 = Point::new(center.x + a.x - k2 * a.y, center.y + a.y + k2 * a.x);
- let control_2 = Point::new(center.x + b.x + k2 * b.y, center.y + b.y - k2 * b.x);
-
- [start, control_1, control_2, end]
-}
-
-/// Indicates which sides of the border strokes in a 2D polygon are connected to
-/// their neighboring sides.
-#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
-struct Connection {
- prev: bool,
- next: bool,
-}
-
-impl Connection {
- /// Advance to the next clockwise side of the polygon. The argument
- /// indicates whether the border is connected on the right side of the next
- /// edge.
- pub fn advance(self, next: bool) -> Self {
- Self { prev: self.next, next }
- }
-}
diff --git a/src/geom/scalar.rs b/src/geom/scalar.rs
deleted file mode 100644
index 71fb1755..00000000
--- a/src/geom/scalar.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-use super::*;
-
-/// A 64-bit float that implements `Eq`, `Ord` and `Hash`.
-///
-/// Panics if it's `NaN` during any of those operations.
-#[derive(Default, Copy, Clone)]
-pub struct Scalar(pub f64);
-
-impl Numeric for Scalar {
- fn zero() -> Self {
- Self(0.0)
- }
-
- fn is_finite(self) -> bool {
- self.0.is_finite()
- }
-}
-
-impl From<f64> for Scalar {
- fn from(float: f64) -> Self {
- Self(float)
- }
-}
-
-impl From<Scalar> for f64 {
- fn from(scalar: Scalar) -> Self {
- scalar.0
- }
-}
-
-impl Debug for Scalar {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-impl Eq for Scalar {}
-
-impl PartialEq for Scalar {
- fn eq(&self, other: &Self) -> bool {
- assert!(!self.0.is_nan() && !other.0.is_nan(), "float is NaN");
- self.0 == other.0
- }
-}
-
-impl PartialEq<f64> for Scalar {
- fn eq(&self, other: &f64) -> bool {
- self == &Self(*other)
- }
-}
-
-impl Ord for Scalar {
- fn cmp(&self, other: &Self) -> Ordering {
- self.partial_cmp(other).expect("float is NaN")
- }
-}
-
-impl PartialOrd for Scalar {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- self.0.partial_cmp(&other.0)
- }
-
- fn lt(&self, other: &Self) -> bool {
- self.0 < other.0
- }
-
- fn le(&self, other: &Self) -> bool {
- self.0 <= other.0
- }
-
- fn gt(&self, other: &Self) -> bool {
- self.0 > other.0
- }
-
- fn ge(&self, other: &Self) -> bool {
- self.0 >= other.0
- }
-}
-
-impl Hash for Scalar {
- fn hash<H: Hasher>(&self, state: &mut H) {
- debug_assert!(!self.0.is_nan(), "float is NaN");
- self.0.to_bits().hash(state);
- }
-}
-
-impl Neg for Scalar {
- type Output = Self;
-
- fn neg(self) -> Self::Output {
- Self(-self.0)
- }
-}
-
-impl<T: Into<Self>> Add<T> for Scalar {
- type Output = Self;
-
- fn add(self, rhs: T) -> Self::Output {
- Self(self.0 + rhs.into().0)
- }
-}
-
-impl<T: Into<Self>> AddAssign<T> for Scalar {
- fn add_assign(&mut self, rhs: T) {
- self.0 += rhs.into().0;
- }
-}
-
-impl<T: Into<Self>> Sub<T> for Scalar {
- type Output = Self;
-
- fn sub(self, rhs: T) -> Self::Output {
- Self(self.0 - rhs.into().0)
- }
-}
-
-impl<T: Into<Self>> SubAssign<T> for Scalar {
- fn sub_assign(&mut self, rhs: T) {
- self.0 -= rhs.into().0;
- }
-}
-
-impl<T: Into<Self>> Mul<T> for Scalar {
- type Output = Self;
-
- fn mul(self, rhs: T) -> Self::Output {
- Self(self.0 * rhs.into().0)
- }
-}
-
-impl<T: Into<Self>> MulAssign<T> for Scalar {
- fn mul_assign(&mut self, rhs: T) {
- self.0 *= rhs.into().0;
- }
-}
-
-impl<T: Into<Self>> Div<T> for Scalar {
- type Output = Self;
-
- fn div(self, rhs: T) -> Self::Output {
- Self(self.0 / rhs.into().0)
- }
-}
-
-impl<T: Into<Self>> DivAssign<T> for Scalar {
- fn div_assign(&mut self, rhs: T) {
- self.0 /= rhs.into().0;
- }
-}
-
-impl<T: Into<Self>> Rem<T> for Scalar {
- type Output = Self;
-
- fn rem(self, rhs: T) -> Self::Output {
- Self(self.0 % rhs.into().0)
- }
-}
-
-impl<T: Into<Self>> RemAssign<T> for Scalar {
- fn rem_assign(&mut self, rhs: T) {
- self.0 %= rhs.into().0;
- }
-}
-
-impl Sum for Scalar {
- fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- Self(iter.map(|s| s.0).sum())
- }
-}
-
-impl<'a> Sum<&'a Self> for Scalar {
- fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
- Self(iter.map(|s| s.0).sum())
- }
-}
diff --git a/src/geom/shape.rs b/src/geom/shape.rs
deleted file mode 100644
index 5658c21f..00000000
--- a/src/geom/shape.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-use super::*;
-
-/// A geometric shape with optional fill and stroke.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Shape {
- /// The shape's geometry.
- pub geometry: Geometry,
- /// The shape's background fill.
- pub fill: Option<Paint>,
- /// The shape's border stroke.
- pub stroke: Option<Stroke>,
-}
-
-/// A shape's geometry.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Geometry {
- /// A line to a point (relative to its position).
- Line(Point),
- /// A rectangle with its origin in the topleft corner.
- Rect(Size),
- /// A bezier path.
- Path(Path),
-}
-
-impl Geometry {
- /// Fill the geometry without a stroke.
- pub fn filled(self, fill: Paint) -> Shape {
- Shape { geometry: self, fill: Some(fill), stroke: None }
- }
-
- /// Stroke the geometry without a fill.
- pub fn stroked(self, stroke: Stroke) -> Shape {
- Shape { geometry: self, fill: None, stroke: Some(stroke) }
- }
-}
diff --git a/src/geom/sides.rs b/src/geom/sides.rs
deleted file mode 100644
index d4b72a9d..00000000
--- a/src/geom/sides.rs
+++ /dev/null
@@ -1,268 +0,0 @@
-use crate::eval::{CastInfo, FromValue, IntoValue, Reflect};
-
-use super::*;
-
-/// A container with left, top, right and bottom components.
-#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Sides<T> {
- /// The value for the left side.
- pub left: T,
- /// The value for the top side.
- pub top: T,
- /// The value for the right side.
- pub right: T,
- /// The value for the bottom side.
- pub bottom: T,
-}
-
-impl<T> Sides<T> {
- /// Create a new instance from the four components.
- pub const fn new(left: T, top: T, right: T, bottom: T) -> Self {
- Self { left, top, right, bottom }
- }
-
- /// Create an instance with four equal components.
- pub fn splat(value: T) -> Self
- where
- T: Clone,
- {
- Self {
- left: value.clone(),
- top: value.clone(),
- right: value.clone(),
- bottom: value,
- }
- }
-
- /// Map the individual fields with `f`.
- pub fn map<F, U>(self, mut f: F) -> Sides<U>
- where
- F: FnMut(T) -> U,
- {
- Sides {
- left: f(self.left),
- top: f(self.top),
- right: f(self.right),
- bottom: f(self.bottom),
- }
- }
-
- /// Zip two instances into one.
- pub fn zip<U>(self, other: Sides<U>) -> Sides<(T, U)> {
- Sides {
- left: (self.left, other.left),
- top: (self.top, other.top),
- right: (self.right, other.right),
- bottom: (self.bottom, other.bottom),
- }
- }
-
- /// An iterator over the sides, starting with the left side, clockwise.
- pub fn iter(&self) -> impl Iterator<Item = &T> {
- [&self.left, &self.top, &self.right, &self.bottom].into_iter()
- }
-
- /// Whether all sides are equal.
- pub fn is_uniform(&self) -> bool
- where
- T: PartialEq,
- {
- self.left == self.top && self.top == self.right && self.right == self.bottom
- }
-}
-
-impl<T: Add> Sides<T> {
- /// Sums up `left` and `right` into `x`, and `top` and `bottom` into `y`.
- pub fn sum_by_axis(self) -> Axes<T::Output> {
- Axes::new(self.left + self.right, self.top + self.bottom)
- }
-}
-
-impl Sides<Rel<Abs>> {
- /// Evaluate the sides relative to the given `size`.
- pub fn relative_to(self, size: Size) -> Sides<Abs> {
- Sides {
- left: self.left.relative_to(size.x),
- top: self.top.relative_to(size.y),
- right: self.right.relative_to(size.x),
- bottom: self.bottom.relative_to(size.y),
- }
- }
-}
-
-impl<T> Get<Side> for Sides<T> {
- type Component = T;
-
- fn get_ref(&self, side: Side) -> &T {
- match side {
- Side::Left => &self.left,
- Side::Top => &self.top,
- Side::Right => &self.right,
- Side::Bottom => &self.bottom,
- }
- }
-
- fn get_mut(&mut self, side: Side) -> &mut T {
- match side {
- Side::Left => &mut self.left,
- Side::Top => &mut self.top,
- Side::Right => &mut self.right,
- Side::Bottom => &mut self.bottom,
- }
- }
-}
-
-/// The four sides of objects.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Side {
- /// The left side.
- Left,
- /// The top side.
- Top,
- /// The right side.
- Right,
- /// The bottom side.
- Bottom,
-}
-
-impl Side {
- /// The opposite side.
- pub fn inv(self) -> Self {
- match self {
- Self::Left => Self::Right,
- Self::Top => Self::Bottom,
- Self::Right => Self::Left,
- Self::Bottom => Self::Top,
- }
- }
-
- /// The next side, clockwise.
- pub fn next_cw(self) -> Self {
- match self {
- Self::Left => Self::Top,
- Self::Top => Self::Right,
- Self::Right => Self::Bottom,
- Self::Bottom => Self::Left,
- }
- }
-
- /// The next side, counter-clockwise.
- pub fn next_ccw(self) -> Self {
- match self {
- Self::Left => Self::Bottom,
- Self::Top => Self::Left,
- Self::Right => Self::Top,
- Self::Bottom => Self::Right,
- }
- }
-
- /// The first corner of the side in clockwise order.
- pub fn start_corner(self) -> Corner {
- match self {
- Self::Left => Corner::BottomLeft,
- Self::Top => Corner::TopLeft,
- Self::Right => Corner::TopRight,
- Self::Bottom => Corner::BottomRight,
- }
- }
-
- /// The second corner of the side in clockwise order.
- pub fn end_corner(self) -> Corner {
- self.next_cw().start_corner()
- }
-
- /// Return the corresponding axis.
- pub fn axis(self) -> Axis {
- match self {
- Self::Left | Self::Right => Axis::Y,
- Self::Top | Self::Bottom => Axis::X,
- }
- }
-}
-
-impl<T: Reflect> Reflect for Sides<Option<T>> {
- fn describe() -> CastInfo {
- T::describe() + Dict::describe()
- }
-
- fn castable(value: &Value) -> bool {
- Dict::castable(value) || T::castable(value)
- }
-}
-
-impl<T> IntoValue for Sides<T>
-where
- T: PartialEq + IntoValue,
-{
- fn into_value(self) -> Value {
- if self.is_uniform() {
- return self.left.into_value();
- }
-
- let mut dict = Dict::new();
- let mut handle = |key: &str, component: T| {
- let value = component.into_value();
- if value != Value::None {
- dict.insert(key.into(), value);
- }
- };
-
- handle("left", self.left);
- handle("top", self.top);
- handle("right", self.right);
- handle("bottom", self.bottom);
-
- Value::Dict(dict)
- }
-}
-
-impl<T> FromValue for Sides<Option<T>>
-where
- T: Default + FromValue + Clone,
-{
- fn from_value(mut value: Value) -> StrResult<Self> {
- let keys = ["left", "top", "right", "bottom", "x", "y", "rest"];
- if let Value::Dict(dict) = &mut value {
- if dict.iter().any(|(key, _)| keys.contains(&key.as_str())) {
- let mut take = |key| dict.take(key).ok().map(T::from_value).transpose();
- let rest = take("rest")?;
- let x = take("x")?.or_else(|| rest.clone());
- let y = take("y")?.or_else(|| rest.clone());
- let sides = Sides {
- left: take("left")?.or_else(|| x.clone()),
- top: take("top")?.or_else(|| y.clone()),
- right: take("right")?.or_else(|| x.clone()),
- bottom: take("bottom")?.or_else(|| y.clone()),
- };
-
- dict.finish(&keys)?;
- return Ok(sides);
- }
- }
-
- if T::castable(&value) {
- Ok(Self::splat(Some(T::from_value(value)?)))
- } else {
- Err(Self::error(&value))
- }
- }
-}
-
-impl<T: Resolve> Resolve for Sides<T> {
- type Output = Sides<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Fold> Fold for Sides<Option<T>> {
- type Output = Sides<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
- })
- }
-}
diff --git a/src/geom/size.rs b/src/geom/size.rs
deleted file mode 100644
index a2e32b77..00000000
--- a/src/geom/size.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use super::*;
-
-/// A size in 2D.
-pub type Size = Axes<Abs>;
-
-impl Size {
- /// The zero value.
- pub const fn zero() -> Self {
- Self { x: Abs::zero(), y: Abs::zero() }
- }
-
- /// Whether the other size fits into this one (smaller width and height).
- pub fn fits(self, other: Self) -> bool {
- self.x.fits(other.x) && self.y.fits(other.y)
- }
-
- /// Convert to a point.
- pub fn to_point(self) -> Point {
- Point::new(self.x, self.y)
- }
-}
-
-impl Numeric for Size {
- fn zero() -> Self {
- Self::zero()
- }
-
- fn is_finite(self) -> bool {
- self.x.is_finite() && self.y.is_finite()
- }
-}
-
-impl Neg for Size {
- type Output = Self;
-
- fn neg(self) -> Self {
- Self { x: -self.x, y: -self.y }
- }
-}
-
-impl Add for Size {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self { x: self.x + other.x, y: self.y + other.y }
- }
-}
-
-sub_impl!(Size - Size -> Size);
-
-impl Mul<f64> for Size {
- type Output = Self;
-
- fn mul(self, other: f64) -> Self {
- Self { x: self.x * other, y: self.y * other }
- }
-}
-
-impl Mul<Size> for f64 {
- type Output = Size;
-
- fn mul(self, other: Size) -> Size {
- other * self
- }
-}
-
-impl Div<f64> for Size {
- type Output = Self;
-
- fn div(self, other: f64) -> Self {
- Self { x: self.x / other, y: self.y / other }
- }
-}
-
-assign_impl!(Size -= Size);
-assign_impl!(Size += Size);
-assign_impl!(Size *= f64);
-assign_impl!(Size /= f64);
diff --git a/src/geom/smart.rs b/src/geom/smart.rs
deleted file mode 100644
index a6271c20..00000000
--- a/src/geom/smart.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-use crate::eval::{AutoValue, CastInfo, FromValue, IntoValue, Reflect};
-
-use super::*;
-
-/// A value that can be automatically determined.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub enum Smart<T> {
- /// The value should be determined smartly based on the circumstances.
- Auto,
- /// A specific value.
- Custom(T),
-}
-
-impl<T> Smart<T> {
- /// Whether the value is `Auto`.
- pub fn is_auto(&self) -> bool {
- matches!(self, Self::Auto)
- }
-
- /// Whether this holds a custom value.
- pub fn is_custom(&self) -> bool {
- matches!(self, Self::Custom(_))
- }
-
- /// Returns a reference the contained custom value.
- /// If the value is [`Smart::Auto`], `None` is returned.
- pub fn as_custom(self) -> Option<T> {
- match self {
- Self::Auto => None,
- Self::Custom(x) => Some(x),
- }
- }
-
- /// Map the contained custom value with `f`.
- pub fn map<F, U>(self, f: F) -> Smart<U>
- where
- F: FnOnce(T) -> U,
- {
- match self {
- Self::Auto => Smart::Auto,
- Self::Custom(x) => Smart::Custom(f(x)),
- }
- }
-
- /// Map the contained custom value with `f` if it contains a custom value,
- /// otherwise returns `default`.
- pub fn map_or<F, U>(self, default: U, f: F) -> U
- where
- F: FnOnce(T) -> U,
- {
- match self {
- Self::Auto => default,
- Self::Custom(x) => f(x),
- }
- }
-
- /// Keeps `self` if it contains a custom value, otherwise returns `other`.
- pub fn or(self, other: Smart<T>) -> Self {
- match self {
- Self::Custom(x) => Self::Custom(x),
- Self::Auto => other,
- }
- }
-
- /// Returns the contained custom value or a provided default value.
- pub fn unwrap_or(self, default: T) -> T {
- match self {
- Self::Auto => default,
- Self::Custom(x) => x,
- }
- }
-
- /// Returns the contained custom value or computes a default value.
- pub fn unwrap_or_else<F>(self, f: F) -> T
- where
- F: FnOnce() -> T,
- {
- match self {
- Self::Auto => f(),
- Self::Custom(x) => x,
- }
- }
-
- /// Returns the contained custom value or the default value.
- pub fn unwrap_or_default(self) -> T
- where
- T: Default,
- {
- self.unwrap_or_else(T::default)
- }
-}
-
-impl<T> Default for Smart<T> {
- fn default() -> Self {
- Self::Auto
- }
-}
-
-impl<T: Reflect> Reflect for Smart<T> {
- fn castable(value: &Value) -> bool {
- AutoValue::castable(value) || T::castable(value)
- }
-
- fn describe() -> CastInfo {
- T::describe() + AutoValue::describe()
- }
-}
-
-impl<T: IntoValue> IntoValue for Smart<T> {
- fn into_value(self) -> Value {
- match self {
- Smart::Custom(v) => v.into_value(),
- Smart::Auto => Value::Auto,
- }
- }
-}
-
-impl<T: FromValue> FromValue for Smart<T> {
- fn from_value(value: Value) -> StrResult<Self> {
- match value {
- Value::Auto => Ok(Self::Auto),
- v if T::castable(&v) => Ok(Self::Custom(T::from_value(v)?)),
- _ => Err(Self::error(&value)),
- }
- }
-}
-
-impl<T: Resolve> Resolve for Smart<T> {
- type Output = Smart<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T> Fold for Smart<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Smart<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.map(|inner| inner.fold(outer.unwrap_or_default()))
- }
-}
diff --git a/src/geom/stroke.rs b/src/geom/stroke.rs
deleted file mode 100644
index 66264d5d..00000000
--- a/src/geom/stroke.rs
+++ /dev/null
@@ -1,387 +0,0 @@
-use crate::eval::{Cast, FromValue};
-
-use super::*;
-
-/// A stroke of a geometric shape.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Stroke {
- /// The stroke's paint.
- pub paint: Paint,
- /// The stroke's thickness.
- pub thickness: Abs,
- /// The stroke's line cap.
- pub line_cap: LineCap,
- /// The stroke's line join.
- pub line_join: LineJoin,
- /// The stroke's line dash pattern.
- pub dash_pattern: Option<DashPattern<Abs, Abs>>,
- /// The miter limit. Defaults to 4.0, same as `tiny-skia`.
- pub miter_limit: Scalar,
-}
-
-impl Default for Stroke {
- fn default() -> Self {
- Self {
- paint: Paint::Solid(Color::BLACK),
- thickness: Abs::pt(1.0),
- line_cap: LineCap::Butt,
- line_join: LineJoin::Miter,
- dash_pattern: None,
- miter_limit: Scalar(4.0),
- }
- }
-}
-
-/// A partial stroke representation.
-///
-/// In this representation, both fields are optional so that you can pass either
-/// just a paint (`red`), just a thickness (`0.1em`) or both (`2pt + red`) where
-/// this is expected.
-#[derive(Default, Clone, Eq, PartialEq, Hash)]
-pub struct PartialStroke<T = Length> {
- /// The stroke's paint.
- pub paint: Smart<Paint>,
- /// The stroke's thickness.
- pub thickness: Smart<T>,
- /// The stroke's line cap.
- pub line_cap: Smart<LineCap>,
- /// The stroke's line join.
- pub line_join: Smart<LineJoin>,
- /// The stroke's line dash pattern.
- pub dash_pattern: Smart<Option<DashPattern<T>>>,
- /// The miter limit.
- pub miter_limit: Smart<Scalar>,
-}
-
-impl<T> PartialStroke<T> {
- /// Map the contained lengths with `f`.
- pub fn map<F, U>(self, f: F) -> PartialStroke<U>
- where
- F: Fn(T) -> U,
- {
- PartialStroke {
- paint: self.paint,
- thickness: self.thickness.map(&f),
- line_cap: self.line_cap,
- line_join: self.line_join,
- dash_pattern: self.dash_pattern.map(|pattern| {
- pattern.map(|pattern| DashPattern {
- array: pattern
- .array
- .into_iter()
- .map(|l| match l {
- DashLength::Length(v) => DashLength::Length(f(v)),
- DashLength::LineWidth => DashLength::LineWidth,
- })
- .collect(),
- phase: f(pattern.phase),
- })
- }),
- miter_limit: self.miter_limit,
- }
- }
-}
-
-impl PartialStroke<Abs> {
- /// Unpack the stroke, filling missing fields from the `default`.
- pub fn unwrap_or(self, default: Stroke) -> Stroke {
- let thickness = self.thickness.unwrap_or(default.thickness);
- let dash_pattern = self
- .dash_pattern
- .map(|pattern| {
- pattern.map(|pattern| DashPattern {
- array: pattern
- .array
- .into_iter()
- .map(|l| l.finish(thickness))
- .collect(),
- phase: pattern.phase,
- })
- })
- .unwrap_or(default.dash_pattern);
-
- Stroke {
- paint: self.paint.unwrap_or(default.paint),
- thickness,
- line_cap: self.line_cap.unwrap_or(default.line_cap),
- line_join: self.line_join.unwrap_or(default.line_join),
- dash_pattern,
- miter_limit: self.miter_limit.unwrap_or(default.miter_limit),
- }
- }
-
- /// Unpack the stroke, filling missing fields with the default values.
- pub fn unwrap_or_default(self) -> Stroke {
- self.unwrap_or(Stroke::default())
- }
-}
-
-impl<T: Debug> Debug for PartialStroke<T> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- let Self {
- paint,
- thickness,
- line_cap,
- line_join,
- dash_pattern,
- miter_limit,
- } = &self;
- if line_cap.is_auto()
- && line_join.is_auto()
- && dash_pattern.is_auto()
- && miter_limit.is_auto()
- {
- match (&self.paint, &self.thickness) {
- (Smart::Custom(paint), Smart::Custom(thickness)) => {
- write!(f, "{thickness:?} + {paint:?}")
- }
- (Smart::Custom(paint), Smart::Auto) => paint.fmt(f),
- (Smart::Auto, Smart::Custom(thickness)) => thickness.fmt(f),
- (Smart::Auto, Smart::Auto) => f.pad("1pt + black"),
- }
- } else {
- write!(f, "(")?;
- let mut sep = "";
- if let Smart::Custom(paint) = &paint {
- write!(f, "{}paint: {:?}", sep, paint)?;
- sep = ", ";
- }
- if let Smart::Custom(thickness) = &thickness {
- write!(f, "{}thickness: {:?}", sep, thickness)?;
- sep = ", ";
- }
- if let Smart::Custom(cap) = &line_cap {
- write!(f, "{}cap: {:?}", sep, cap)?;
- sep = ", ";
- }
- if let Smart::Custom(join) = &line_join {
- write!(f, "{}join: {:?}", sep, join)?;
- sep = ", ";
- }
- if let Smart::Custom(dash) = &dash_pattern {
- write!(f, "{}dash: {:?}", sep, dash)?;
- sep = ", ";
- }
- if let Smart::Custom(miter_limit) = &miter_limit {
- write!(f, "{}miter-limit: {:?}", sep, miter_limit)?;
- }
- write!(f, ")")?;
- Ok(())
- }
- }
-}
-
-impl Resolve for PartialStroke {
- type Output = PartialStroke<Abs>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- PartialStroke {
- paint: self.paint,
- thickness: self.thickness.resolve(styles),
- line_cap: self.line_cap,
- line_join: self.line_join,
- dash_pattern: self.dash_pattern.resolve(styles),
- miter_limit: self.miter_limit,
- }
- }
-}
-
-impl Fold for PartialStroke<Abs> {
- type Output = Self;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- Self {
- paint: self.paint.or(outer.paint),
- thickness: self.thickness.or(outer.thickness),
- line_cap: self.line_cap.or(outer.line_cap),
- line_join: self.line_join.or(outer.line_join),
- dash_pattern: self.dash_pattern.or(outer.dash_pattern),
- miter_limit: self.miter_limit.or(outer.miter_limit),
- }
- }
-}
-
-cast! {
- type PartialStroke: "stroke",
- thickness: Length => Self {
- thickness: Smart::Custom(thickness),
- ..Default::default()
- },
- color: Color => Self {
- paint: Smart::Custom(color.into()),
- ..Default::default()
- },
- mut dict: Dict => {
- fn take<T: FromValue>(dict: &mut Dict, key: &str) -> StrResult<Smart<T>> {
- Ok(dict.take(key).ok().map(T::from_value)
- .transpose()?.map(Smart::Custom).unwrap_or(Smart::Auto))
- }
-
- let paint = take::<Paint>(&mut dict, "paint")?;
- let thickness = take::<Length>(&mut dict, "thickness")?;
- let line_cap = take::<LineCap>(&mut dict, "cap")?;
- let line_join = take::<LineJoin>(&mut dict, "join")?;
- let dash_pattern = take::<Option<DashPattern>>(&mut dict, "dash")?;
- let miter_limit = take::<f64>(&mut dict, "miter-limit")?;
- dict.finish(&["paint", "thickness", "cap", "join", "dash", "miter-limit"])?;
-
- Self {
- paint,
- thickness,
- line_cap,
- line_join,
- dash_pattern,
- miter_limit: miter_limit.map(Scalar),
- }
- },
-}
-
-cast! {
- PartialStroke<Abs>,
- self => self.map(Length::from).into_value(),
-}
-
-/// The line cap of a stroke
-#[derive(Copy, Clone, Eq, PartialEq, Hash, Cast)]
-pub enum LineCap {
- Butt,
- Round,
- Square,
-}
-
-impl Debug for LineCap {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- LineCap::Butt => write!(f, "\"butt\""),
- LineCap::Round => write!(f, "\"round\""),
- LineCap::Square => write!(f, "\"square\""),
- }
- }
-}
-
-/// The line join of a stroke
-#[derive(Copy, Clone, Eq, PartialEq, Hash, Cast)]
-pub enum LineJoin {
- Miter,
- Round,
- Bevel,
-}
-
-impl Debug for LineJoin {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- LineJoin::Miter => write!(f, "\"miter\""),
- LineJoin::Round => write!(f, "\"round\""),
- LineJoin::Bevel => write!(f, "\"bevel\""),
- }
- }
-}
-
-/// A line dash pattern.
-#[derive(Clone, Eq, PartialEq, Hash)]
-pub struct DashPattern<T = Length, DT = DashLength<T>> {
- /// The dash array.
- pub array: Vec<DT>,
- /// The dash phase.
- pub phase: T,
-}
-
-impl<T: Debug, DT: Debug> Debug for DashPattern<T, DT> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "(array: (")?;
- for (i, elem) in self.array.iter().enumerate() {
- if i == 0 {
- write!(f, "{:?}", elem)?;
- } else {
- write!(f, ", {:?}", elem)?;
- }
- }
- write!(f, "), phase: {:?})", self.phase)?;
- Ok(())
- }
-}
-
-impl<T: Default> From<Vec<DashLength<T>>> for DashPattern<T> {
- fn from(array: Vec<DashLength<T>>) -> Self {
- Self { array, phase: T::default() }
- }
-}
-
-impl Resolve for DashPattern {
- type Output = DashPattern<Abs>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- DashPattern {
- array: self.array.into_iter().map(|l| l.resolve(styles)).collect(),
- phase: self.phase.resolve(styles),
- }
- }
-}
-
-// Same names as tikz:
-// https://tex.stackexchange.com/questions/45275/tikz-get-values-for-predefined-dash-patterns
-cast! {
- DashPattern,
-
- "solid" => Vec::new().into(),
- "dotted" => vec![DashLength::LineWidth, Abs::pt(2.0).into()].into(),
- "densely-dotted" => vec![DashLength::LineWidth, Abs::pt(1.0).into()].into(),
- "loosely-dotted" => vec![DashLength::LineWidth, Abs::pt(4.0).into()].into(),
- "dashed" => vec![Abs::pt(3.0).into(), Abs::pt(3.0).into()].into(),
- "densely-dashed" => vec![Abs::pt(3.0).into(), Abs::pt(2.0).into()].into(),
- "loosely-dashed" => vec![Abs::pt(3.0).into(), Abs::pt(6.0).into()].into(),
- "dash-dotted" => vec![Abs::pt(3.0).into(), Abs::pt(2.0).into(), DashLength::LineWidth, Abs::pt(2.0).into()].into(),
- "densely-dash-dotted" => vec![Abs::pt(3.0).into(), Abs::pt(1.0).into(), DashLength::LineWidth, Abs::pt(1.0).into()].into(),
- "loosely-dash-dotted" => vec![Abs::pt(3.0).into(), Abs::pt(4.0).into(), DashLength::LineWidth, Abs::pt(4.0).into()].into(),
-
- array: Vec<DashLength> => Self { array, phase: Length::zero() },
- mut dict: Dict => {
- let array: Vec<DashLength> = dict.take("array")?.cast()?;
- let phase = dict.take("phase").ok().map(Value::cast)
- .transpose()?.unwrap_or(Length::zero());
- dict.finish(&["array", "phase"])?;
- Self {
- array,
- phase,
- }
- },
-}
-
-/// The length of a dash in a line dash pattern
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum DashLength<T = Length> {
- LineWidth,
- Length(T),
-}
-
-impl From<Abs> for DashLength {
- fn from(l: Abs) -> Self {
- DashLength::Length(l.into())
- }
-}
-
-impl<T> DashLength<T> {
- fn finish(self, line_width: T) -> T {
- match self {
- Self::LineWidth => line_width,
- Self::Length(l) => l,
- }
- }
-}
-
-impl Resolve for DashLength {
- type Output = DashLength<Abs>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- match self {
- Self::LineWidth => DashLength::LineWidth,
- Self::Length(v) => DashLength::Length(v.resolve(styles)),
- }
- }
-}
-
-cast! {
- DashLength,
- "dot" => Self::LineWidth,
- v: Length => Self::Length(v),
-}
diff --git a/src/geom/transform.rs b/src/geom/transform.rs
deleted file mode 100644
index 1ff1dfdd..00000000
--- a/src/geom/transform.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use super::*;
-
-/// A scale-skew-translate transformation.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Transform {
- pub sx: Ratio,
- pub ky: Ratio,
- pub kx: Ratio,
- pub sy: Ratio,
- pub tx: Abs,
- pub ty: Abs,
-}
-
-impl Transform {
- /// The identity transformation.
- pub const fn identity() -> Self {
- Self {
- sx: Ratio::one(),
- ky: Ratio::zero(),
- kx: Ratio::zero(),
- sy: Ratio::one(),
- tx: Abs::zero(),
- ty: Abs::zero(),
- }
- }
-
- /// A translate transform.
- pub const fn translate(tx: Abs, ty: Abs) -> Self {
- Self { tx, ty, ..Self::identity() }
- }
-
- /// A scale transform.
- pub const fn scale(sx: Ratio, sy: Ratio) -> Self {
- Self { sx, sy, ..Self::identity() }
- }
-
- /// A rotate transform.
- pub fn rotate(angle: Angle) -> Self {
- let cos = Ratio::new(angle.cos());
- let sin = Ratio::new(angle.sin());
- Self {
- sx: cos,
- ky: sin,
- kx: -sin,
- sy: cos,
- ..Self::default()
- }
- }
-
- /// Whether this is the identity transformation.
- pub fn is_identity(self) -> bool {
- self == Self::identity()
- }
-
- /// Pre-concatenate another transformation.
- pub fn pre_concat(self, prev: Self) -> Self {
- Transform {
- sx: self.sx * prev.sx + self.kx * prev.ky,
- ky: self.ky * prev.sx + self.sy * prev.ky,
- kx: self.sx * prev.kx + self.kx * prev.sy,
- sy: self.ky * prev.kx + self.sy * prev.sy,
- tx: self.sx.of(prev.tx) + self.kx.of(prev.ty) + self.tx,
- ty: self.ky.of(prev.tx) + self.sy.of(prev.ty) + self.ty,
- }
- }
-
- /// Post-concatenate another transformation.
- pub fn post_concat(self, next: Self) -> Self {
- next.pre_concat(self)
- }
-}
-
-impl Default for Transform {
- fn default() -> Self {
- Self::identity()
- }
-}