| 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 | |