summaryrefslogtreecommitdiff
path: root/src/geom
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-06-14 15:07:13 +0200
committerLaurenz <laurmaedje@gmail.com>2022-06-14 17:17:54 +0200
commit7a6c2cce7747f7632f0be012f49b548db3e62a2d (patch)
tree30103d743fcf22c6838e5ce3b6c632abe15e78b9 /src/geom
parent6832ca2a26df5a9407bd2b0266cc0bab328ebeba (diff)
Make radius configuration unconfusing
Diffstat (limited to 'src/geom')
-rw-r--r--src/geom/corners.rs122
-rw-r--r--src/geom/mod.rs2
-rw-r--r--src/geom/rect.rs44
-rw-r--r--src/geom/sides.rs27
4 files changed, 167 insertions, 28 deletions
diff --git a/src/geom/corners.rs b/src/geom/corners.rs
new file mode 100644
index 00000000..54fcd12f
--- /dev/null
+++ b/src/geom/corners.rs
@@ -0,0 +1,122 @@
+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 an instance.
+ pub fn zip<F, V, W>(self, other: Corners<V>, mut f: F) -> Corners<W>
+ where
+ F: FnMut(T, V) -> W,
+ {
+ Corners {
+ top_left: f(self.top_left, other.top_left),
+ top_right: f(self.top_right, other.top_right),
+ bottom_right: f(self.bottom_right, other.bottom_right),
+ bottom_left: f(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(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,
+}
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
index bdd08fe5..fc8ccb6c 100644
--- a/src/geom/mod.rs
+++ b/src/geom/mod.rs
@@ -4,6 +4,7 @@
mod macros;
mod align;
mod angle;
+mod corners;
mod dir;
mod em;
mod fraction;
@@ -22,6 +23,7 @@ mod transform;
pub use align::*;
pub use angle::*;
+pub use corners::*;
pub use dir::*;
pub use em::*;
pub use fraction::*;
diff --git a/src/geom/rect.rs b/src/geom/rect.rs
index dceb3577..dfea2c45 100644
--- a/src/geom/rect.rs
+++ b/src/geom/rect.rs
@@ -7,13 +7,13 @@ use std::mem;
pub struct RoundedRect {
/// The size of the rectangle.
pub size: Size,
- /// The radius at each side.
- pub radius: Sides<Length>,
+ /// The radius at each corner.
+ pub radius: Corners<Length>,
}
impl RoundedRect {
/// Create a new rounded rectangle.
- pub fn new(size: Size, radius: Sides<Length>) -> Self {
+ pub fn new(size: Size, radius: Corners<Length>) -> Self {
Self { size, radius }
}
@@ -73,20 +73,20 @@ impl RoundedRect {
let mut always_continuous = true;
for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] {
- let is_continuous = strokes.get(side) == strokes.get(side.next_cw());
- connection = connection.advance(is_continuous && side != Side::Left);
- always_continuous &= is_continuous;
+ let continuous = strokes.get(side) == strokes.get(side.next_cw());
+ connection = connection.advance(continuous && side != Side::Left);
+ always_continuous &= continuous;
draw_side(
&mut path,
side,
self.size,
- self.radius.get(side.next_ccw()),
- self.radius.get(side),
+ self.radius.get(side.start_corner()),
+ self.radius.get(side.end_corner()),
connection,
);
- if !is_continuous {
+ if !continuous {
res.push((mem::take(&mut path), strokes.get(side)));
}
}
@@ -109,8 +109,8 @@ fn draw_side(
path: &mut Path,
side: Side,
size: Size,
- radius_left: Length,
- radius_right: Length,
+ start_radius: Length,
+ end_radius: Length,
connection: Connection,
) {
let angle_left = Angle::deg(if connection.prev { 90.0 } else { 45.0 });
@@ -118,23 +118,23 @@ fn draw_side(
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(radius_left);
+ let p1 = Point::with_x(start_radius);
let mut arc1 = bezier_arc(
p1 + Point::new(
- -angle_left.sin() * radius_left,
- (1.0 - angle_left.cos()) * radius_left,
+ -angle_left.sin() * start_radius,
+ (1.0 - angle_left.cos()) * start_radius,
),
- Point::new(radius_left, radius_left),
+ Point::new(start_radius, start_radius),
p1,
);
- let p2 = Point::with_x(length - radius_right);
+ let p2 = Point::with_x(length - end_radius);
let mut arc2 = bezier_arc(
p2,
- Point::new(length - radius_right, radius_right),
+ Point::new(length - end_radius, end_radius),
p2 + Point::new(
- angle_right.sin() * radius_right,
- (1.0 - angle_right.cos()) * radius_right,
+ angle_right.sin() * end_radius,
+ (1.0 - angle_right.cos()) * end_radius,
),
);
@@ -152,16 +152,16 @@ fn draw_side(
arc2 = arc2.map(|x| x.transform(transform));
if !connection.prev {
- path.move_to(if radius_left.is_zero() { arc1[3] } else { arc1[0] });
+ path.move_to(if start_radius.is_zero() { arc1[3] } else { arc1[0] });
}
- if !radius_left.is_zero() {
+ if !start_radius.is_zero() {
path.cubic_to(arc1[1], arc1[2], arc1[3]);
}
path.line_to(arc2[0]);
- if !connection.next && !radius_right.is_zero() {
+ if !connection.next && !end_radius.is_zero() {
path.cubic_to(arc2[1], arc2[2], arc2[3]);
}
}
diff --git a/src/geom/sides.rs b/src/geom/sides.rs
index 938539fe..72748916 100644
--- a/src/geom/sides.rs
+++ b/src/geom/sides.rs
@@ -48,17 +48,17 @@ impl<T> Sides<T> {
/// Zip two instances into an instance.
pub fn zip<F, V, W>(self, other: Sides<V>, mut f: F) -> Sides<W>
where
- F: FnMut(T, V, Side) -> W,
+ F: FnMut(T, V) -> W,
{
Sides {
- left: f(self.left, other.left, Side::Left),
- top: f(self.top, other.top, Side::Top),
- right: f(self.right, other.right, Side::Right),
- bottom: f(self.bottom, other.bottom, Side::Bottom),
+ left: f(self.left, other.left),
+ top: f(self.top, other.top),
+ right: f(self.right, other.right),
+ bottom: f(self.bottom, other.bottom),
}
}
- /// An iterator over the sides.
+ /// 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()
}
@@ -157,6 +157,21 @@ impl Side {
}
}
+ /// 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) -> SpecAxis {
match self {