summaryrefslogtreecommitdiff
path: root/src/geom/rect.rs
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2022-05-03 11:40:27 +0200
committerMartin Haug <mhaug@live.de>2022-05-03 12:59:41 +0200
commit6a8a0ec6ec8bb8cf346ee0dd2c45ddcfbee7fbe6 (patch)
tree0d8716d2fa0c01a2319bb84be0283253bc6490fe /src/geom/rect.rs
parent33213abe7dfcb8d8065faadd2f5b72ec4b718af1 (diff)
Code Review: Heap is Stack. Unsafe is Good.
Spaghetti Code is Style.
Diffstat (limited to 'src/geom/rect.rs')
-rw-r--r--src/geom/rect.rs138
1 files changed, 55 insertions, 83 deletions
diff --git a/src/geom/rect.rs b/src/geom/rect.rs
index f0da2db6..aa670f0a 100644
--- a/src/geom/rect.rs
+++ b/src/geom/rect.rs
@@ -3,7 +3,7 @@ use super::*;
use std::mem;
/// A rectangle with rounded corners.
-#[derive(Debug, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Rect {
size: Size,
radius: Sides<Length>,
@@ -19,7 +19,7 @@ impl Rect {
/// in the foreground. The function will output multiple items if the stroke
/// properties differ by side.
pub fn shapes(
- &self,
+ self,
fill: Option<Paint>,
stroke: Sides<Option<Stroke>>,
) -> Vec<Shape> {
@@ -28,48 +28,64 @@ impl Rect {
res.push(Shape {
geometry: self.fill_geometry(),
fill,
- stroke: stroke.is_uniform().then(|| stroke.top).flatten(),
+ stroke: if stroke.is_uniform() { stroke.top } else { None },
});
}
if !stroke.is_uniform() {
- for (path, stroke) in self.stroke_segments(Some(stroke)) {
- if !stroke.is_some() {
- continue;
+ for (path, stroke) in self.stroke_segments(stroke) {
+ if stroke.is_some() {
+ res.push(Shape {
+ geometry: Geometry::Path(path),
+ fill: None,
+ stroke,
+ });
}
- 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(self) -> Geometry {
+ if self.radius.iter().copied().all(Length::is_zero) {
+ Geometry::Rect(self.size)
+ } else {
+ let mut paths = self.stroke_segments(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(
- &self,
- strokes: Option<Sides<Option<Stroke>>>,
+ self,
+ strokes: Sides<Option<Stroke>>,
) -> Vec<(Path, Option<Stroke>)> {
- let strokes = strokes.unwrap_or_else(|| Sides::splat(None));
let mut res = vec![];
- let mut connection = Connection::None;
+ let mut connection = Connection::default();
let mut path = Path::new();
let mut always_continuous = true;
for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] {
- let radius = [self.radius.get(side.next_ccw()), self.radius.get(side)];
-
- let stroke_continuity = strokes.get(side) == strokes.get(side.next_cw());
- connection = connection.advance(stroke_continuity && side != Side::Left);
- always_continuous &= stroke_continuity;
-
- draw_side(&mut path, side, self.size, radius[0], radius[1], connection);
-
- if !stroke_continuity {
+ let is_continuous = strokes.get(side) == strokes.get(side.next_cw());
+ connection = connection.advance(is_continuous && side != Side::Left);
+ always_continuous &= is_continuous;
+
+ draw_side(
+ &mut path,
+ side,
+ self.size,
+ self.radius.get(side.next_ccw()),
+ self.radius.get(side),
+ connection,
+ );
+
+ if !is_continuous {
res.push((mem::take(&mut path), strokes.get(side)));
}
}
@@ -84,19 +100,6 @@ impl Rect {
res
}
-
- /// Output the shape of the rectangle as a path or primitive rectangle,
- /// depending on whether it is rounded.
- fn fill_geometry(&self) -> Geometry {
- if self.radius.iter().copied().all(Length::is_zero) {
- Geometry::Rect(self.size)
- } else {
- let mut paths = self.stroke_segments(None);
- assert_eq!(paths.len(), 1);
-
- Geometry::Path(paths.pop().unwrap().0)
- }
- }
}
/// Draws one side of the rounded rectangle. Will always draw the left arc. The
@@ -110,19 +113,18 @@ fn draw_side(
connection: Connection,
) {
let reversed = |angle: Angle, radius, rotate, mirror_x, mirror_y| {
- let [a, b, c, d] = angle.bezier_arc(radius, rotate, mirror_x, mirror_y);
+ let [a, b, c, d] = bezier_arc(angle, radius, rotate, mirror_x, mirror_y);
[d, c, b, a]
};
- let angle_left = Angle::deg(if connection.left() { 90.0 } else { 45.0 });
- let angle_right = Angle::deg(if connection.right() { 90.0 } else { 45.0 });
+ 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 (arc1, arc2) = match side {
Side::Top => {
let arc1 = reversed(angle_left, radius_left, true, true, false)
.map(|x| x + Point::with_x(radius_left));
- let arc2 = (-angle_right)
- .bezier_arc(radius_right, true, true, false)
+ let arc2 = bezier_arc(-angle_right, radius_right, true, true, false)
.map(|x| x + Point::with_x(size.x - radius_right));
(arc1, arc2)
@@ -131,8 +133,7 @@ fn draw_side(
let arc1 = reversed(-angle_left, radius_left, false, false, false)
.map(|x| x + Point::new(size.x, radius_left));
- let arc2 = angle_right
- .bezier_arc(radius_right, false, false, false)
+ let arc2 = bezier_arc(angle_right, radius_right, false, false, false)
.map(|x| x + Point::new(size.x, size.y - radius_right));
(arc1, arc2)
@@ -141,8 +142,7 @@ fn draw_side(
let arc1 = reversed(-angle_left, radius_left, true, false, false)
.map(|x| x + Point::new(size.x - radius_left, size.y));
- let arc2 = angle_right
- .bezier_arc(radius_right, true, false, false)
+ let arc2 = bezier_arc(angle_right, radius_right, true, false, false)
.map(|x| x + Point::new(radius_right, size.y));
(arc1, arc2)
@@ -151,15 +151,14 @@ fn draw_side(
let arc1 = reversed(angle_left, radius_left, false, false, true)
.map(|x| x + Point::with_y(size.y - radius_left));
- let arc2 = (-angle_right)
- .bezier_arc(radius_right, false, false, true)
+ let arc2 = bezier_arc(-angle_right, radius_right, false, false, true)
.map(|x| x + Point::with_y(radius_right));
(arc1, arc2)
}
};
- if !connection.left() {
+ if !connection.prev {
path.move_to(if radius_left.is_zero() { arc1[3] } else { arc1[0] });
}
@@ -169,51 +168,24 @@ fn draw_side(
path.line_to(arc2[0]);
- if !connection.right() && !radius_right.is_zero() {
+ if !connection.next && !radius_right.is_zero() {
path.cubic_to(arc2[1], arc2[2], arc2[3]);
}
}
/// A state machine that indicates which sides of the border strokes in a 2D
/// polygon are connected to their neighboring sides.
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-enum Connection {
- None,
- Left,
- Right,
- Both,
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+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, right: bool) -> Self {
- match self {
- Self::Right | Self::Both => {
- if right {
- Self::Both
- } else {
- Self::Left
- }
- }
- Self::Left | Self::None => {
- if right {
- Self::Right
- } else {
- Self::None
- }
- }
- }
- }
-
- /// Whether there is a connection on the left.
- fn left(self) -> bool {
- matches!(self, Self::Left | Self::Both)
- }
-
- /// Whether there is a connection on the right.
- fn right(self) -> bool {
- matches!(self, Self::Right | Self::Both)
+ pub fn advance(self, next: bool) -> Self {
+ Self { prev: self.next, next }
}
}