diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-07-02 19:59:52 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-07-02 20:07:43 +0200 |
| commit | ebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch) | |
| tree | 2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/geom/rounded.rs | |
| parent | 3ab19185093d7709f824b95b979060ce125389d8 (diff) | |
Move everything into `crates/` directory
Diffstat (limited to 'src/geom/rounded.rs')
| -rw-r--r-- | src/geom/rounded.rs | 182 |
1 files changed, 0 insertions, 182 deletions
diff --git a/src/geom/rounded.rs b/src/geom/rounded.rs deleted file mode 100644 index f1a7ea08..00000000 --- a/src/geom/rounded.rs +++ /dev/null @@ -1,182 +0,0 @@ -use super::*; - -/// Produce shapes that together make up a rounded rectangle. -pub fn rounded_rect( - size: Size, - radius: Corners<Abs>, - fill: Option<Paint>, - stroke: Sides<Option<Stroke>>, -) -> Vec<Shape> { - let mut res = vec![]; - if fill.is_some() || (stroke.iter().any(Option::is_some) && stroke.is_uniform()) { - res.push(Shape { - geometry: fill_geometry(size, radius), - fill, - stroke: if stroke.is_uniform() { stroke.top.clone() } else { None }, - }); - } - - if !stroke.is_uniform() { - for (path, stroke) in stroke_segments(size, radius, stroke) { - if stroke.is_some() { - 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(size: Size, radius: Corners<Abs>) -> Geometry { - if radius.iter().copied().all(Abs::is_zero) { - Geometry::Rect(size) - } else { - let mut paths = stroke_segments(size, radius, 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( - size: Size, - radius: Corners<Abs>, - stroke: Sides<Option<Stroke>>, -) -> Vec<(Path, Option<Stroke>)> { - let mut res = vec![]; - - let mut connection = Connection::default(); - let mut path = Path::new(); - let mut always_continuous = true; - let max_radius = size.x.min(size.y).max(Abs::zero()) / 2.0; - - for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] { - let continuous = stroke.get_ref(side) == stroke.get_ref(side.next_cw()); - connection = connection.advance(continuous && side != Side::Left); - always_continuous &= continuous; - - draw_side( - &mut path, - side, - size, - radius.get(side.start_corner()).clamp(Abs::zero(), max_radius), - radius.get(side.end_corner()).clamp(Abs::zero(), max_radius), - connection, - ); - - if !continuous { - res.push((std::mem::take(&mut path), stroke.get_ref(side).clone())); - } - } - - if always_continuous { - path.close_path(); - } - - if !path.0.is_empty() { - res.push((path, stroke.left)); - } - - res -} - -/// Draws one side of the rounded rectangle. Will always draw the left arc. The -/// right arc will be drawn halfway if and only if there is no connection. -fn draw_side( - path: &mut Path, - side: Side, - size: Size, - start_radius: Abs, - end_radius: Abs, - connection: Connection, -) { - 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 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(start_radius); - let mut arc1 = bezier_arc( - p1 + Point::new( - -angle_left.sin() * start_radius, - (1.0 - angle_left.cos()) * start_radius, - ), - Point::new(start_radius, start_radius), - p1, - ); - - let p2 = Point::with_x(length - end_radius); - let mut arc2 = bezier_arc( - p2, - Point::new(length - end_radius, end_radius), - p2 + Point::new( - angle_right.sin() * end_radius, - (1.0 - angle_right.cos()) * end_radius, - ), - ); - - let transform = match side { - Side::Left => Transform::rotate(Angle::deg(-90.0)) - .post_concat(Transform::translate(Abs::zero(), size.y)), - Side::Bottom => Transform::rotate(Angle::deg(180.0)) - .post_concat(Transform::translate(size.x, size.y)), - Side::Right => Transform::rotate(Angle::deg(90.0)) - .post_concat(Transform::translate(size.x, Abs::zero())), - _ => Transform::identity(), - }; - - arc1 = arc1.map(|x| x.transform(transform)); - arc2 = arc2.map(|x| x.transform(transform)); - - if !connection.prev { - path.move_to(if start_radius.is_zero() { arc1[3] } else { arc1[0] }); - } - - if !start_radius.is_zero() { - path.cubic_to(arc1[1], arc1[2], arc1[3]); - } - - path.line_to(arc2[0]); - - if !connection.next && !end_radius.is_zero() { - path.cubic_to(arc2[1], arc2[2], arc2[3]); - } -} - -/// Get the control points for a bezier curve that describes a circular arc for -/// a start point, an end point and a center of the circle whose arc connects -/// the two. -fn bezier_arc(start: Point, center: Point, end: Point) -> [Point; 4] { - // https://stackoverflow.com/a/44829356/1567835 - let a = start - center; - 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); - - [start, control_1, control_2, end] -} - -/// Indicates which sides of the border strokes in a 2D polygon are connected to -/// their neighboring sides. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] -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, next: bool) -> Self { - Self { prev: self.next, next } - } -} |
