1use 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
22pub trait PixelCanvasInterface<const H: usize, const W: usize, P: PixelInterface> {
26 fn table(&self) -> &PixelTable<H, W, P>;
28}
29
30pub trait PixelCanvasMutInterface<const H: usize, const W: usize, P: PixelMutInterface>:
32 PixelCanvasInterface<H, W, P>
33{
34 fn table_mut(&mut self) -> &mut PixelTable<H, W, P>;
36}
37
38#[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
179pub trait SharedPixelCanvasExt<const H: usize, const W: usize, P: PixelInterface>:
183 PixelCanvasInterface<H, W, P>
184{
185 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 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 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
215pub trait SharedMutPixelCanvasExt<const H: usize, const W: usize, P: PixelMutInterface>:
219 PixelCanvasMutInterface<H, W, P>
220{
221 fn clear(&mut self)
223 where
224 <P as PixelInterface>::ColorType: Default + Clone,
225 {
226 self.fill(P::ColorType::default())
227 }
228
229 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 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 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
310pub 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
323pub trait PixelCanvasMutExt<const H: usize, const W: usize>:
325 SharedMutPixelCanvasExt<H, W, Pixel>
326{
327 }
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}