pixelart/pixels/canvas/
mod.rs

1//! Module contains types related to a [`PixelCanvas`].
2
3use crate::image::{PixelImageBuilder, PixelImageStyle};
4
5use self::{drawable::Drawable, pen::Pen, table::PixelTable};
6
7use super::{
8    color::PixelColor,
9    maybe::MaybePixel,
10    position::{
11        IntoPixelStrictPosition, PixelStrictPositionInterface, SingleCycle, MAIN_DIRECTIONS,
12    },
13    Pixel, PixelInitializer, PixelInterface, PixelMutInterface, PixelMutPosition,
14};
15
16pub mod drawable;
17pub mod pen;
18pub mod row;
19pub mod table;
20pub mod templates;
21
22/// Interface that any read_only pixel canvas may want to implement.
23///
24/// Using this we can have access to later extension methods.
25pub trait PixelCanvasInterface<const H: usize, const W: usize, P: PixelInterface> {
26    /// A read-only reference to underlying [`PixelTable`].
27    fn table(&self) -> &PixelTable<H, W, P>;
28}
29
30/// Interface that any mutable pixel canvas may want to implement.
31pub trait PixelCanvasMutInterface<const H: usize, const W: usize, P: PixelMutInterface>:
32    PixelCanvasInterface<H, W, P>
33{
34    /// A mutable reference to underlying [`PixelTable`].
35    fn table_mut(&mut self) -> &mut PixelTable<H, W, P>;
36}
37
38/// A [`PixelCanvas`], the highest level api to work and clear interact
39/// with the underlying [`PixelTable`] and pixels.
40#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
41pub struct PixelCanvas<const H: usize, const W: usize = H, P: PixelInterface = Pixel> {
42    table: PixelTable<H, W, P>,
43}
44
45pub type MaybePixelCanvas<const H: usize, const W: usize = H> = PixelCanvas<H, W, MaybePixel>;
46
47impl<const H: usize, const W: usize, P: PixelInterface> PixelCanvas<H, W, P> {
48    #[allow(private_bounds)]
49    #[must_use = "This function returns a new table."]
50    pub fn flip_x(&mut self) -> PixelCanvas<H, W, P>
51    where
52        P: PixelMutPosition + Clone,
53    {
54        let mut canvas = self.clone();
55        canvas.rows.reverse();
56        canvas.sync_positions();
57        canvas
58    }
59
60    #[allow(private_bounds)]
61    #[must_use = "This function returns a new table."]
62    pub fn flip_y(&mut self) -> PixelCanvas<H, W, P>
63    where
64        P: PixelMutPosition + Clone,
65    {
66        let mut canvas = self.clone();
67        canvas.iter_mut().for_each(|row| row.reverse());
68        canvas.sync_positions();
69        canvas
70    }
71}
72
73impl<const H: usize, const W: usize, P> Default for PixelCanvas<H, W, P>
74where
75    P: PixelInterface + PixelInitializer,
76    <P as PixelInterface>::ColorType: Default + Clone,
77{
78    fn default() -> Self {
79        Self {
80            table: Default::default(),
81        }
82    }
83}
84
85impl<const H: usize, const W: usize, P: PixelInterface + PixelInitializer> PixelCanvas<H, W, P> {
86    pub fn new(fill_color: impl Into<P::ColorType> + Clone) -> Self {
87        Self {
88            table: PixelTable::new(fill_color),
89        }
90    }
91}
92
93impl<const H: usize, const W: usize, P: PixelInterface> std::ops::Deref for PixelCanvas<H, W, P> {
94    type Target = PixelTable<H, W, P>;
95
96    fn deref(&self) -> &Self::Target {
97        &self.table
98    }
99}
100
101impl<const H: usize, const W: usize, P: PixelInterface> std::ops::DerefMut
102    for PixelCanvas<H, W, P>
103{
104    fn deref_mut(&mut self) -> &mut Self::Target {
105        &mut self.table
106    }
107}
108
109impl<const H: usize, const W: usize, P: PixelInterface> PixelCanvasInterface<H, W, P>
110    for PixelCanvas<H, W, P>
111{
112    fn table(&self) -> &PixelTable<H, W, P> {
113        &self.table
114    }
115}
116
117impl<const H: usize, const W: usize, P: PixelInterface> PixelCanvasInterface<H, W, P>
118    for &PixelCanvas<H, W, P>
119{
120    fn table(&self) -> &PixelTable<H, W, P> {
121        &self.table
122    }
123}
124
125impl<const H: usize, const W: usize, P: PixelInterface> PixelCanvasInterface<H, W, P>
126    for &mut PixelCanvas<H, W, P>
127{
128    fn table(&self) -> &PixelTable<H, W, P> {
129        &self.table
130    }
131}
132
133impl<const H: usize, const W: usize, P: PixelMutInterface> PixelCanvasMutInterface<H, W, P>
134    for PixelCanvas<H, W, P>
135{
136    fn table_mut(&mut self) -> &mut PixelTable<H, W, P> {
137        &mut self.table
138    }
139}
140
141impl<const H: usize, const W: usize, P: PixelMutInterface> PixelCanvasMutInterface<H, W, P>
142    for &mut PixelCanvas<H, W, P>
143{
144    fn table_mut(&mut self) -> &mut PixelTable<H, W, P> {
145        &mut self.table
146    }
147}
148
149fn _fill_inside<
150    const H: usize,
151    const W: usize,
152    P: PixelMutInterface,
153    I: SharedMutPixelCanvasExt<H, W, P>,
154>(
155    canvas: &mut I,
156    base_color: Option<P::ColorType>,
157    color: impl Into<P::ColorType> + Clone,
158    point_inside: impl IntoPixelStrictPosition<H, W>,
159) where
160    P::ColorType: PartialEq + Clone,
161{
162    let pos = point_inside.into_pixel_strict_position();
163    let base_color = base_color.unwrap_or(canvas.table()[&pos].color().clone());
164
165    canvas.update_color_at(&pos, color.clone());
166
167    for dir in
168        SingleCycle::new(super::position::Direction::Up).filter(|dir| MAIN_DIRECTIONS.contains(dir))
169    {
170        if let Ok(new_pos) = pos.checked_direction(dir, 1) {
171            if canvas.color_at(&new_pos) == &base_color {
172                canvas.update_color_at(&new_pos, color.clone());
173                _fill_inside(canvas, Some(base_color.clone()), color.clone(), new_pos)
174            }
175        }
176    }
177}
178
179/// Extensions for any type that implements [`PixelCanvasInterface`].
180///
181/// This trait is implemented for any canvas of [`PixelInterface`].
182pub trait SharedPixelCanvasExt<const H: usize, const W: usize, P: PixelInterface>:
183    PixelCanvasInterface<H, W, P>
184{
185    /// Get an [`PixelImageBuilder`] based on this canvas with [`PixelImageStyle`] specified.
186    fn image_builder(&self, style: PixelImageStyle) -> PixelImageBuilder<H, W, P, Self>
187    where
188        Self: Sized,
189    {
190        PixelImageBuilder::new(self, style)
191    }
192
193    /// Get an [`PixelImageBuilder`] based on this canvas with default [`PixelImageStyle`].
194    fn default_image_builder(&self) -> PixelImageBuilder<H, W, P, Self>
195    where
196        Self: Sized,
197    {
198        PixelImageBuilder::new_default_style(self)
199    }
200
201    /// Gets the color of a pixel at given position.
202    fn color_at<'a>(&'a self, pos: impl PixelStrictPositionInterface<H, W>) -> &P::ColorType
203    where
204        P: 'a,
205    {
206        self.table()[pos].color()
207    }
208}
209
210impl<const H: usize, const W: usize, T, P: PixelInterface> SharedPixelCanvasExt<H, W, P> for T where
211    T: PixelCanvasInterface<H, W, P>
212{
213}
214
215/// Extensions for any type that implements [`PixelCanvasInterface`].
216///
217/// This trait is implemented for any canvas of [`PixelInterface`].
218pub trait SharedMutPixelCanvasExt<const H: usize, const W: usize, P: PixelMutInterface>:
219    PixelCanvasMutInterface<H, W, P>
220{
221    /// Updates every pixel's color to default which is white.
222    fn clear(&mut self)
223    where
224        <P as PixelInterface>::ColorType: Default + Clone,
225    {
226        self.fill(P::ColorType::default())
227    }
228
229    /// Fills all pixels color.
230    fn fill(&mut self, color: impl Into<P::ColorType>)
231    where
232        <P as PixelInterface>::ColorType: Clone,
233    {
234        let color = color.into();
235        self.table_mut().for_each_pixel_mut(|pixel| {
236            pixel.update_color(color.clone());
237        })
238    }
239
240    /// Update color of a pixel at the given position.
241    fn update_color_at(
242        &mut self,
243        pos: impl PixelStrictPositionInterface<H, W>,
244        color: impl Into<P::ColorType>,
245    ) -> P::ColorType {
246        self.table_mut()[pos].update_color(color)
247    }
248
249    /// Keep filling pixels with new color until we encounter a new color.
250    fn fill_inside(
251        &mut self,
252        color: impl Into<P::ColorType> + std::clone::Clone,
253        point_inside: impl IntoPixelStrictPosition<H, W>,
254    ) where
255        Self: Sized,
256        <P as PixelInterface>::ColorType: PartialEq + Clone,
257    {
258        _fill_inside::<H, W, P, _>(self, None, color, point_inside)
259    }
260
261    fn draw<const HD: usize, const WD: usize>(
262        &mut self,
263        start_pos: impl IntoPixelStrictPosition<H, W>,
264        drawable: impl Drawable<HD, WD>,
265    ) where
266        Self: Sized,
267        <P as PixelInterface>::ColorType: From<PixelColor>,
268    {
269        drawable.draw_on(start_pos, self)
270    }
271
272    fn draw_exact(
273        &mut self,
274        start_pos: impl IntoPixelStrictPosition<H, W>,
275        drawable: impl Drawable<H, W>,
276    ) where
277        Self: Sized,
278        <P as PixelInterface>::ColorType: From<PixelColor>,
279    {
280        drawable.draw_on_exact(start_pos, self)
281    }
282
283    fn draw_exact_abs(&mut self, drawable: impl Drawable<H, W>)
284    where
285        Self: Sized,
286        <P as PixelInterface>::ColorType: From<PixelColor>,
287    {
288        drawable.draw_on_exact_abs(self)
289    }
290
291    fn attach_new_pen(
292        &mut self,
293        color: impl Into<P::ColorType>,
294        start_pos: impl IntoPixelStrictPosition<H, W>,
295    ) -> Pen<pen::CanvasAttachedMarker<H, W, P, Self>>
296    where
297        Self: Sized,
298        <P as PixelInterface>::ColorType: From<PixelColor>,
299    {
300        let pen = Pen::new(color);
301        pen.attach(self, start_pos)
302    }
303}
304
305impl<const H: usize, const W: usize, T, P: PixelMutInterface> SharedMutPixelCanvasExt<H, W, P> for T where
306    T: PixelCanvasMutInterface<H, W, P>
307{
308}
309
310/// Extensions for any type that implements [`PixelCanvasInterface`].
311///
312/// This trait is only implemented for canvas of [`Pixel`] type.
313pub trait PixelCanvasExt<const H: usize, const W: usize>:
314    SharedPixelCanvasExt<H, W, Pixel>
315{
316}
317
318impl<const H: usize, const W: usize, T> PixelCanvasExt<H, W> for T where
319    T: PixelCanvasInterface<H, W, Pixel>
320{
321}
322
323/// Extensions for any type that implements [`PixelCanvasInterface`].
324pub trait PixelCanvasMutExt<const H: usize, const W: usize>:
325    SharedMutPixelCanvasExt<H, W, Pixel>
326{
327    // /// Keep filling pixels with new color until we encounter a new color.
328    // fn fill_inside(
329    //     &mut self,
330    //     color: impl IntoPixelColor,
331    //     point_inside: impl IntoPixelStrictPosition<H, W>,
332    // ) where
333    //     Self: Sized,
334    // {
335    //     _fill_inside::<H, W, _>(self, None, color, point_inside)
336    // }
337}
338
339impl<const H: usize, const W: usize, T> PixelCanvasMutExt<H, W> for T where
340    T: PixelCanvasMutInterface<H, W, Pixel>
341{
342}
343
344#[cfg(test)]
345mod tests {
346    use crate::pixels::{
347        color::PixelColorExt,
348        position::{PixelPositionInterface, StrictPositions},
349        PixelIterExt, PixelIterMutExt,
350    };
351
352    use super::*;
353
354    #[test]
355    fn test_fill_inside() {
356        let mut canvas = PixelCanvas::<5>::default();
357        canvas
358            .iter_pixels_mut()
359            .filter_position(|p| p.column() == p.row())
360            .update_colors(PixelColor::RED);
361
362        canvas.fill_inside(PixelColor::BLUE, StrictPositions::BottomLeft);
363
364        canvas.update_color_at(StrictPositions::TopRight, PixelColor::BLACK);
365
366        let image_builder = canvas.default_image_builder().with_scale(5);
367        image_builder.save("arts/fill_inside.png").unwrap();
368    }
369}