summaryrefslogtreecommitdiff
path: root/src/geom/spec.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-11-26 16:32:06 +0100
committerLaurenz <laurmaedje@gmail.com>2021-11-26 16:32:06 +0100
commit3a15922d2ffc041c3523edb479f008a9034fd400 (patch)
tree988fe103c0752696c1fade2123142a8db5361ab7 /src/geom/spec.rs
parent393d74f9bb0d4c71a69108d5be261103c39f47f3 (diff)
X/Y abstractions
Diffstat (limited to 'src/geom/spec.rs')
-rw-r--r--src/geom/spec.rs219
1 files changed, 181 insertions, 38 deletions
diff --git a/src/geom/spec.rs b/src/geom/spec.rs
index 608643d8..640d6231 100644
--- a/src/geom/spec.rs
+++ b/src/geom/spec.rs
@@ -1,7 +1,10 @@
+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)]
+#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct Spec<T> {
/// The horizontal component.
pub x: T,
@@ -31,9 +34,18 @@ impl<T> Spec<T> {
Spec { x: f(self.x), y: f(self.y) }
}
+ /// Convert from `&Spec<T>` to `Spec<&T>`.
+ pub fn as_ref(&self) -> Spec<&T> {
+ Spec { x: &self.x, y: &self.y }
+ }
+
+ /// Convert from `&mut Spec<T>` to `Spec<&mut T>`.
+ pub fn as_mut(&mut self) -> Spec<&mut T> {
+ Spec { x: &mut self.x, y: &mut self.y }
+ }
+
/// Zip two instances into an instance over a tuple.
- pub fn zip<U>(self, other: impl Into<Spec<U>>) -> Spec<(T, U)> {
- let other = other.into();
+ pub fn zip<U>(self, other: Spec<U>) -> Spec<(T, U)> {
Spec {
x: (self.x, other.x),
y: (self.y, other.y),
@@ -56,6 +68,14 @@ impl<T> Spec<T> {
f(&self.x) && f(&self.y)
}
+ /// Filter the individual fields with a mask.
+ pub fn filter(self, mask: Spec<bool>) -> Spec<Option<T>> {
+ Spec {
+ x: if mask.x { Some(self.x) } else { None },
+ y: if mask.y { Some(self.y) } else { None },
+ }
+ }
+
/// Convert to the generic representation.
pub fn to_gen(self, main: SpecAxis) -> Gen<T> {
match main {
@@ -65,39 +85,6 @@ impl<T> Spec<T> {
}
}
-impl From<Size> for Spec<Length> {
- fn from(size: Size) -> Self {
- size.to_spec()
- }
-}
-
-impl Spec<Length> {
- /// The zero value.
- pub fn zero() -> Self {
- Self { x: Length::zero(), y: Length::zero() }
- }
-
- /// Convert to a point.
- pub fn to_point(self) -> Point {
- Point::new(self.x, self.y)
- }
-
- /// Convert to a size.
- pub fn to_size(self) -> Size {
- Size::new(self.x, self.y)
- }
-}
-
-impl<T> Spec<Option<T>> {
- /// Unwrap the individual fields.
- pub fn unwrap_or(self, other: Spec<T>) -> Spec<T> {
- Spec {
- x: self.x.unwrap_or(other.x),
- y: self.y.unwrap_or(other.y),
- }
- }
-}
-
impl<T> Get<SpecAxis> for Spec<T> {
type Component = T;
@@ -116,9 +103,20 @@ impl<T> Get<SpecAxis> for Spec<T> {
}
}
-impl<T: Debug> Debug for Spec<T> {
+impl<T> Debug for Spec<T>
+where
+ T: Debug + 'static,
+{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Spec({:?}, {:?})", self.x, self.y)
+ if let Spec { x: Some(x), y: Some(y) } =
+ self.as_ref().map(|v| (v as &dyn Any).downcast_ref::<Align>())
+ {
+ write!(f, "{:?}-{:?}", x, y)
+ } else if (&self.x as &dyn Any).is::<Length>() {
+ write!(f, "Size({:?}, {:?})", self.x, self.y)
+ } else {
+ write!(f, "Spec({:?}, {:?})", self.x, self.y)
+ }
}
}
@@ -159,3 +157,148 @@ impl Debug for SpecAxis {
})
}
}
+
+/// A size in 2D.
+pub type Size = Spec<Length>;
+
+impl Size {
+ /// The zero value.
+ pub fn zero() -> Self {
+ Self { x: Length::zero(), y: Length::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)
+ }
+
+ /// Whether both components are finite.
+ pub fn is_finite(self) -> bool {
+ self.x.is_finite() && self.y.is_finite()
+ }
+
+ /// Whether any of the two components is infinite.
+ pub fn is_infinite(self) -> bool {
+ self.x.is_infinite() || self.y.is_infinite()
+ }
+
+ /// Convert to a point.
+ pub fn to_point(self) -> Point {
+ Point::new(self.x, self.y)
+ }
+}
+
+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);
+
+impl<T> Spec<Option<T>> {
+ /// Whether the individual fields are some.
+ pub fn map_is_some(&self) -> Spec<bool> {
+ self.as_ref().map(Option::is_some)
+ }
+
+ /// Whether the individual fields are none.
+ pub fn map_is_none(&self) -> Spec<bool> {
+ self.as_ref().map(Option::is_none)
+ }
+
+ /// Unwrap the individual fields.
+ pub fn unwrap_or(self, other: Spec<T>) -> Spec<T> {
+ Spec {
+ x: self.x.unwrap_or(other.x),
+ y: self.y.unwrap_or(other.y),
+ }
+ }
+}
+
+impl Spec<bool> {
+ /// Select `t.x` if `self.x` is true and `f.x` otherwise and same for `y`.
+ pub fn select<T>(self, t: Spec<T>, f: Spec<T>) -> Spec<T> {
+ Spec {
+ x: if self.x { t.x } else { f.x },
+ y: if self.y { t.y } else { f.y },
+ }
+ }
+}
+
+impl Not for Spec<bool> {
+ type Output = Self;
+
+ fn not(self) -> Self::Output {
+ Self { x: !self.x, y: !self.y }
+ }
+}
+
+impl BitOr for Spec<bool> {
+ type Output = Self;
+
+ fn bitor(self, rhs: Self) -> Self::Output {
+ Self { x: self.x | rhs.x, y: self.y | rhs.y }
+ }
+}
+
+impl BitAnd for Spec<bool> {
+ type Output = Self;
+
+ fn bitand(self, rhs: Self) -> Self::Output {
+ Self { x: self.x & rhs.x, y: self.y & rhs.y }
+ }
+}
+
+impl BitOrAssign for Spec<bool> {
+ fn bitor_assign(&mut self, rhs: Self) {
+ self.x |= rhs.x;
+ self.y |= rhs.y;
+ }
+}
+
+impl BitAndAssign for Spec<bool> {
+ fn bitand_assign(&mut self, rhs: Self) {
+ self.x &= rhs.x;
+ self.y &= rhs.y;
+ }
+}