diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-06-14 15:07:13 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-06-14 17:17:54 +0200 |
| commit | 7a6c2cce7747f7632f0be012f49b548db3e62a2d (patch) | |
| tree | 30103d743fcf22c6838e5ce3b6c632abe15e78b9 /src/geom | |
| parent | 6832ca2a26df5a9407bd2b0266cc0bab328ebeba (diff) | |
Make radius configuration unconfusing
Diffstat (limited to 'src/geom')
| -rw-r--r-- | src/geom/corners.rs | 122 | ||||
| -rw-r--r-- | src/geom/mod.rs | 2 | ||||
| -rw-r--r-- | src/geom/rect.rs | 44 | ||||
| -rw-r--r-- | src/geom/sides.rs | 27 |
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 { |
