embedded_graphics/primitives/arc/
mod.rs

1//! The arc primitive
2
3use crate::{
4    geometry::{Angle, Dimensions, Point, Size},
5    primitives::{Circle, PointsIter, Primitive, Rectangle},
6    transform::Transform,
7};
8
9mod points;
10mod styled;
11
12pub use points::Points;
13pub use styled::StyledPixelsIterator;
14
15/// Arc primitive
16///
17/// # Examples
18///
19/// ## Create some arcs with different styles
20///
21/// ```rust
22/// use embedded_graphics::{
23///     pixelcolor::Rgb565,
24///     prelude::*,
25///     primitives::{Arc, PrimitiveStyle, PrimitiveStyleBuilder},
26/// };
27/// # use embedded_graphics::mock_display::MockDisplay;
28/// # let mut display = MockDisplay::default();
29///
30/// // Arc with 1 pixel wide white stroke with top-left point at (10, 20) with a diameter of 30
31/// Arc::new(Point::new(10, 20), 30, 0.0.deg(), 90.0.deg())
32///     .into_styled(PrimitiveStyle::with_stroke(Rgb565::WHITE, 1))
33///     .draw(&mut display)?;
34///
35/// // Arc with styled stroke with top-left point at (15, 25) with a diameter of 20
36/// let style = PrimitiveStyleBuilder::new()
37///     .stroke_color(Rgb565::RED)
38///     .stroke_width(3)
39///     .build();
40///
41/// Arc::new(Point::new(15, 25), 20, 180.0.deg(), -90.0.deg())
42///     .into_styled(style)
43///     .draw(&mut display)?;
44/// # Ok::<(), core::convert::Infallible>(())
45/// ```
46#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
47#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
48pub struct Arc {
49    /// Top-left point of the bounding-box of the circle supporting the arc
50    pub top_left: Point,
51
52    /// Diameter of the circle supporting the arc
53    pub diameter: u32,
54
55    /// Angle at which the arc starts
56    pub angle_start: Angle,
57
58    /// Angle defining the arc sweep starting at angle_start
59    pub angle_sweep: Angle,
60}
61
62impl Arc {
63    /// Create a new arc delimited with a top-left point with a specific diameter and start and sweep angles
64    pub const fn new(
65        top_left: Point,
66        diameter: u32,
67        angle_start: Angle,
68        angle_sweep: Angle,
69    ) -> Self {
70        Arc {
71            top_left,
72            diameter,
73            angle_start,
74            angle_sweep,
75        }
76    }
77
78    /// Create a new arc centered around a given point with a specific diameter and start and sweep angles
79    pub const fn with_center(
80        center: Point,
81        diameter: u32,
82        angle_start: Angle,
83        angle_sweep: Angle,
84    ) -> Self {
85        Self::from_circle(
86            Circle::with_center(center, diameter),
87            angle_start,
88            angle_sweep,
89        )
90    }
91
92    /// Creates an arc based on a circle.
93    ///
94    /// The resulting arc will match the `top_left` and `diameter` of the base circle.
95    pub const fn from_circle(circle: Circle, angle_start: Angle, angle_sweep: Angle) -> Self {
96        Self {
97            top_left: circle.top_left,
98            diameter: circle.diameter,
99            angle_start,
100            angle_sweep,
101        }
102    }
103
104    /// Returns a circle with the same `top_left` and `diameter` as this arc.
105    pub const fn to_circle(&self) -> Circle {
106        Circle::new(self.top_left, self.diameter)
107    }
108
109    /// Return the center point of the arc.
110    pub fn center(&self) -> Point {
111        self.bounding_box().center()
112    }
113}
114
115impl Primitive for Arc {}
116
117impl PointsIter for Arc {
118    type Iter = Points;
119
120    fn points(&self) -> Self::Iter {
121        Points::new(self)
122    }
123}
124
125impl Dimensions for Arc {
126    fn bounding_box(&self) -> Rectangle {
127        Rectangle::new(self.top_left, Size::new(self.diameter, self.diameter))
128    }
129}
130
131impl Transform for Arc {
132    /// Translate the arc from its current position to a new position by (x, y) pixels,
133    /// returning a new `Arc`. For a mutating transform, see `translate_mut`.
134    ///
135    /// ```
136    /// # use embedded_graphics::primitives::Arc;
137    /// # use embedded_graphics::prelude::*;
138    /// let arc = Arc::new(Point::new(5, 10), 10, 0.0.deg(), 90.0.deg());
139    /// let moved = arc.translate(Point::new(10, 10));
140    ///
141    /// assert_eq!(moved.top_left, Point::new(15, 20));
142    /// ```
143    fn translate(&self, by: Point) -> Self {
144        Self {
145            top_left: self.top_left + by,
146            ..*self
147        }
148    }
149
150    /// Translate the arc from its current position to a new position by (x, y) pixels.
151    ///
152    /// ```
153    /// # use embedded_graphics::primitives::Arc;
154    /// # use embedded_graphics::prelude::*;
155    /// let mut arc = Arc::new(Point::new(5, 10), 10, 0.0.deg(), 90.0.deg());
156    /// arc.translate_mut(Point::new(10, 10));
157    ///
158    /// assert_eq!(arc.top_left, Point::new(15, 20));
159    /// ```
160    fn translate_mut(&mut self, by: Point) -> &mut Self {
161        self.top_left += by;
162
163        self
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170    use crate::geometry::AngleUnit;
171
172    #[test]
173    fn negative_dimensions() {
174        let arc = Arc::new(Point::new(-15, -15), 10, 0.0.deg(), 90.0.deg());
175
176        assert_eq!(
177            arc.bounding_box(),
178            Rectangle::new(Point::new(-15, -15), Size::new(10, 10))
179        );
180    }
181
182    #[test]
183    fn dimensions() {
184        let arc = Arc::new(Point::new(5, 15), 10, 0.0.deg(), 90.0.deg());
185
186        assert_eq!(
187            arc.bounding_box(),
188            Rectangle::new(Point::new(5, 15), Size::new(10, 10))
189        );
190    }
191
192    #[test]
193    fn it_handles_negative_coordinates() {
194        let positive = Arc::new(Point::new(10, 10), 5, 0.0.deg(), 90.0.deg()).points();
195
196        let negative = Arc::new(Point::new(-10, -10), 5, 0.0.deg(), 90.0.deg()).points();
197
198        assert!(negative.eq(positive.map(|p| p - Point::new(20, 20))));
199    }
200
201    #[test]
202    fn center_is_correct() {
203        // odd diameter
204        let arc = Arc::new(Point::new(10, 10), 5, 0.0.deg(), 90.0.deg());
205        assert_eq!(arc.center(), Point::new(12, 12));
206
207        // even diameter
208        let arc = Arc::new(Point::new(10, 10), 6, 0.0.deg(), 90.0.deg());
209        assert_eq!(arc.center(), Point::new(12, 12));
210
211        // odd diameter
212        let arc = Arc::with_center(Point::new(10, 10), 5, 0.0.deg(), 90.0.deg());
213        assert_eq!(arc.center(), Point::new(10, 10));
214
215        // even diameter
216        let arc = Arc::with_center(Point::new(10, 10), 6, 0.0.deg(), 90.0.deg());
217        assert_eq!(arc.center(), Point::new(10, 10));
218    }
219}