summaryrefslogtreecommitdiff
path: root/src/geom
diff options
context:
space:
mode:
Diffstat (limited to 'src/geom')
-rw-r--r--src/geom/angle.rs45
-rw-r--r--src/geom/sides.rs46
-rw-r--r--src/geom/transform.rs24
3 files changed, 115 insertions, 0 deletions
diff --git a/src/geom/angle.rs b/src/geom/angle.rs
index 888442f7..65270ebd 100644
--- a/src/geom/angle.rs
+++ b/src/geom/angle.rs
@@ -64,6 +64,51 @@ impl Angle {
pub fn cos(self) -> f64 {
self.to_rad().cos()
}
+
+ /// Get the control points for a bezier curve that describes a circular arc
+ /// of this angle with the given radius.
+ pub fn bezier_arc(
+ self,
+ radius: Length,
+ rotate: bool,
+ mirror_x: bool,
+ mirror_y: bool,
+ ) -> [Point; 4] {
+ let end = Point::new(self.cos() * radius - radius, self.sin() * radius);
+ let center = Point::new(-radius, Length::zero());
+
+ let mut ts = if mirror_y {
+ Transform::mirror_y()
+ } else {
+ Transform::identity()
+ };
+
+ if mirror_x {
+ ts = ts.pre_concat(Transform::mirror_x());
+ }
+
+ if rotate {
+ ts = ts.pre_concat(Transform::rotate(Angle::deg(90.0)));
+ }
+
+ let a = center * -1.0;
+ 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);
+
+ [
+ Point::zero(),
+ control_1.transform(ts),
+ control_2.transform(ts),
+ end.transform(ts),
+ ]
+ }
}
impl Numeric for Angle {
diff --git a/src/geom/sides.rs b/src/geom/sides.rs
index 3584a1ce..255c21ee 100644
--- a/src/geom/sides.rs
+++ b/src/geom/sides.rs
@@ -31,6 +31,32 @@ impl<T> Sides<T> {
bottom: value,
}
}
+
+ /// Maps 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),
+ }
+ }
+
+ /// Returns an iterator over the sides.
+ pub fn iter(&self) -> impl Iterator<Item = &T> {
+ [&self.left, &self.top, &self.right, &self.bottom].into_iter()
+ }
+
+ /// Returns 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> Sides<T>
@@ -100,4 +126,24 @@ impl Side {
Self::Bottom => Self::Top,
}
}
+
+ /// The next side, clockwise.
+ pub fn clockwise(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 counter_clockwise(self) -> Self {
+ match self {
+ Self::Left => Self::Bottom,
+ Self::Top => Self::Left,
+ Self::Right => Self::Top,
+ Self::Bottom => Self::Right,
+ }
+ }
}
diff --git a/src/geom/transform.rs b/src/geom/transform.rs
index 28a1af80..de2a9781 100644
--- a/src/geom/transform.rs
+++ b/src/geom/transform.rs
@@ -24,6 +24,30 @@ impl Transform {
}
}
+ /// Transform by mirroring along the x-axis.
+ pub fn mirror_x() -> Self {
+ Self {
+ sx: Ratio::one(),
+ ky: Ratio::zero(),
+ kx: Ratio::zero(),
+ sy: -Ratio::one(),
+ tx: Length::zero(),
+ ty: Length::zero(),
+ }
+ }
+
+ /// Transform by mirroring along the y-axis.
+ pub fn mirror_y() -> Self {
+ Self {
+ sx: -Ratio::one(),
+ ky: Ratio::zero(),
+ kx: Ratio::zero(),
+ sy: Ratio::one(),
+ tx: Length::zero(),
+ ty: Length::zero(),
+ }
+ }
+
/// A translate transform.
pub const fn translate(tx: Length, ty: Length) -> Self {
Self { tx, ty, ..Self::identity() }