1 | use crate::{ |
2 | draw_target::DrawTarget, |
3 | geometry::{Dimensions, Point}, |
4 | pixelcolor::PixelColor, |
5 | primitives::OffsetOutline, |
6 | primitives::{PrimitiveStyle, Rectangle}, |
7 | transform::Transform, |
8 | Drawable, |
9 | }; |
10 | |
11 | /// Styled. |
12 | #[derive (Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] |
13 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
14 | pub struct Styled<T, S> { |
15 | /// Primitive. |
16 | pub primitive: T, |
17 | /// Style. |
18 | pub style: S, |
19 | } |
20 | |
21 | impl<T, S> Styled<T, S> { |
22 | /// Creates a styled. |
23 | pub const fn new(primitive: T, style: S) -> Self { |
24 | Self { primitive, style } |
25 | } |
26 | } |
27 | |
28 | impl<T: OffsetOutline, C: PixelColor> Styled<T, PrimitiveStyle<C>> { |
29 | /// Returns the fill area. |
30 | /// |
31 | /// The returned primitive coincides with the area that would be filled by calling [`draw`] |
32 | /// on this styled primitive. |
33 | /// |
34 | /// # Examples |
35 | /// |
36 | /// ``` |
37 | /// use embedded_graphics::{ |
38 | /// pixelcolor::Rgb888, |
39 | /// prelude::*, |
40 | /// primitives::{Circle, PrimitiveStyleBuilder}, |
41 | /// }; |
42 | /// |
43 | /// let style = PrimitiveStyleBuilder::new() |
44 | /// .fill_color(Rgb888::RED) |
45 | /// .stroke_color(Rgb888::GREEN) |
46 | /// .stroke_width(6) |
47 | /// .build(); |
48 | /// |
49 | /// let center = Point::new(10, 20); |
50 | /// let diameter = 10; |
51 | /// |
52 | /// let circle = Circle::with_center(center, diameter).into_styled(style); |
53 | /// |
54 | /// assert_eq!(circle.fill_area(), Circle::with_center(center, diameter - style.stroke_width)); |
55 | /// ``` |
56 | /// |
57 | /// [`draw`]: crate::Drawable::draw |
58 | pub fn fill_area(&self) -> T { |
59 | self.style.fill_area(&self.primitive) |
60 | } |
61 | |
62 | /// Returns the stroke area. |
63 | /// |
64 | /// The outer edge of the returned primitive coincides with the outer edge of the stroke that |
65 | /// would be drawn by calling [`draw`] on this styled primitive. |
66 | /// |
67 | /// # Examples |
68 | /// |
69 | /// This example tests if a point lies on the stroke. Because the stoke area surrounds the |
70 | /// stoke and fill an additional test is required to check that the point is not inside the fill |
71 | /// area. |
72 | /// |
73 | /// ``` |
74 | /// use embedded_graphics::{ |
75 | /// pixelcolor::Rgb888, |
76 | /// prelude::*, |
77 | /// primitives::{Circle, PrimitiveStyle}, |
78 | /// }; |
79 | /// |
80 | /// let style = PrimitiveStyle::with_stroke(Rgb888::RED, 6); |
81 | /// |
82 | /// let center = Point::new(10, 20); |
83 | /// let diameter = 10; |
84 | /// |
85 | /// let circle = Circle::with_center(center, diameter).into_styled(style); |
86 | /// |
87 | /// // A point lies on the stroke if it is inside the stroke area but not in the fill area. |
88 | /// let is_on_stroke = |p| circle.stroke_area().contains(p) && !circle.fill_area().contains(p); |
89 | /// |
90 | /// assert!(is_on_stroke(center + Size::new(0, diameter / 2))); |
91 | /// assert!(!is_on_stroke(center)); |
92 | /// ``` |
93 | /// |
94 | /// [`draw`]: crate::Drawable::draw |
95 | pub fn stroke_area(&self) -> T { |
96 | self.style.stroke_area(&self.primitive) |
97 | } |
98 | } |
99 | |
100 | impl<T: StyledPixels<S>, S> Styled<T, S> { |
101 | /// Returns an iterator over the pixels in this styled primitive. |
102 | pub fn pixels(&self) -> T::Iter { |
103 | self.primitive.pixels(&self.style) |
104 | } |
105 | } |
106 | |
107 | impl<T: StyledDimensions<S>, S> Dimensions for Styled<T, S> { |
108 | fn bounding_box(&self) -> Rectangle { |
109 | self.primitive.styled_bounding_box(&self.style) |
110 | } |
111 | } |
112 | |
113 | impl<T: StyledDrawable<S>, S> Drawable for Styled<T, S> { |
114 | type Color = T::Color; |
115 | type Output = T::Output; |
116 | |
117 | fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error> |
118 | where |
119 | D: DrawTarget<Color = Self::Color>, |
120 | { |
121 | self.primitive.draw_styled(&self.style, target) |
122 | } |
123 | } |
124 | |
125 | impl<T: Transform, S: Clone> Transform for Styled<T, S> { |
126 | fn translate(&self, by: Point) -> Self { |
127 | Self { |
128 | primitive: self.primitive.translate(by), |
129 | style: self.style.clone(), |
130 | } |
131 | } |
132 | |
133 | fn translate_mut(&mut self, by: Point) -> &mut Self { |
134 | self.primitive.translate_mut(by); |
135 | |
136 | self |
137 | } |
138 | } |
139 | |
140 | /// Styled drawable. |
141 | pub trait StyledDrawable<S> { |
142 | /// Color type. |
143 | type Color: PixelColor; |
144 | /// Output type. |
145 | type Output; |
146 | |
147 | /// Draws the primitive using the given style. |
148 | fn draw_styled<D>(&self, style: &S, target: &mut D) -> Result<Self::Output, D::Error> |
149 | where |
150 | D: DrawTarget<Color = Self::Color>; |
151 | } |
152 | |
153 | /// Styled dimensions. |
154 | pub trait StyledDimensions<S> { |
155 | /// Returns the bounding box using the given style. |
156 | fn styled_bounding_box(&self, style: &S) -> Rectangle; |
157 | } |
158 | |
159 | /// Styled drawable. |
160 | pub trait StyledPixels<S> { |
161 | /// Iterator type. |
162 | type Iter; |
163 | |
164 | /// Returns an iterator over all pixels in this styled primitive. |
165 | fn pixels(&self, style: &S) -> Self::Iter; |
166 | } |
167 | |