rasterization/lib.rs
1#![crate_name = "rasterization"]
2#![no_std]
3#![forbid(unsafe_code)]
4#![warn(
5 missing_docs,
6 missing_debug_implementations,
7 missing_copy_implementations
8)]
9#![doc(
10 html_logo_url = "https://raw.githubusercontent.com/pic16f877ccs/image/pixelization/quadrant_parts.png"
11)]
12#![doc = include_str!("../README.md")]
13//! <style>
14//! .rustdoc-hidden { display: none; }
15//! </style>
16use colorous::Gradient;
17use core::fmt::{Debug, Display};
18use core::iter::FusedIterator;
19use core::ops::{Add, AddAssign, Mul, Neg, Range, Sub};
20use num::{One, Unsigned, Zero};
21use num_convert::FromAs;
22
23/// Enum for selecting the gradient direction and setting the color gradient type.
24#[derive(Debug, Clone, Copy)]
25pub enum DirectionGradient {
26 /// The gradient direction is from bottom to top.
27 Top(Gradient),
28 /// The gradient direction is from top to bottom.
29 Bottom(Gradient),
30 /// The gradient direction is from left to right.
31 Right(Gradient),
32 /// The gradient direction is from right to left.
33 Left(Gradient),
34 /// The gradient direction is from bottom right to top left.
35 TopLeft(Gradient),
36 /// The gradient direction is from bottom left to top right.
37 TopRight(Gradient),
38 /// The gradient direction is from top right to bottom left.
39 BottomLeft(Gradient),
40 /// The gradient direction is from top left to bottom rightt.
41 BottomRight(Gradient),
42}
43
44/// An iterator of successive coordinates of a filled semicircle, using Bresenham's algorithm.
45///
46/// The semicircles are exactly equal in diameter to the circle.
47#[derive(Default, Debug, Clone, PartialEq)]
48pub struct SemicircleFilled<T> {
49 x: T,
50 y: T,
51 err: T,
52}
53
54impl<T> SemicircleFilled<T> {
55 /// Creates a new `SemicircleFilled` iterator that generates pixel coordinates.
56 ///
57 /// # Panics
58 ///
59 /// This function will panic if the radius cannot be converted to type `T` or
60 ///
61 /// if the radius exceeds the practical limit of 100,000,000.
62 ///
63 /// This limit exists to prevent overflow in the calculation of the error term.
64 ///
65 /// # Arguments
66 ///
67 /// * `radius` - A non-negative integer representing the radius of the semicircle.
68 ///
69 /// # Examples
70 ///
71 /// Basic usage:
72 ///
73 /// ```rust
74 /// use rasterization::SemicircleFilled;
75 ///
76 /// let radius = 5_u32;
77 /// let semicircle_iter = SemicircleFilled::<i32>::new(radius);
78 /// let vec = semicircle_iter.collect::<Vec<_>>();
79 /// assert_eq!(vec, vec![(-5..5, -1), (-5..5, -2), (-4..4, -3), (-3..3, -4), (-2..2, -5)]);
80 /// ```
81 #[inline]
82 pub fn new<U>(radius: U) -> Self
83 where
84 U: Unsigned + Display + Copy,
85 T: From<i8>
86 + TryFrom<U>
87 + Sub<Output = T>
88 + Neg<Output = T>
89 + Default
90 + Mul<Output = T>
91 + FromAs<isize>
92 + PartialOrd
93 + Copy,
94 <T as TryFrom<U>>::Error: Debug,
95 {
96 let Ok(r) = <T as TryFrom<U>>::try_from(radius) else {
97 panic!(
98 "{}",
99 format_args!("Not possible to convert radius to {}", radius)
100 )
101 };
102 let m_radius = <T as FromAs<isize>>::from_as(100_000_000_isize);
103 if r < m_radius {
104 return Self {
105 x: -r,
106 y: 0.into(),
107 err: <T as From<i8>>::from(2) - <T as From<i8>>::from(2) * r,
108 };
109 }
110
111 panic!("Radius is too large")
112 }
113}
114
115impl Iterator for SemicircleFilled<i32> {
116 type Item = (Range<i32>, i32);
117
118 #[inline]
119 fn next(&mut self) -> Option<Self::Item> {
120 if self.x == 0 {
121 return None;
122 }
123
124 let xy = (self.x..-self.x, -(self.y + 1));
125
126 // FIXME: This loop creates redundant iterations!
127 loop {
128 let y_tmp = self.y;
129 let err_tmp = self.err;
130
131 if err_tmp <= self.y {
132 self.y += 1;
133 self.err += 2 * self.y + 2;
134 };
135
136 if err_tmp > self.x || self.err > self.y {
137 self.x += 1;
138 self.err += 2 * self.x + 2;
139 }
140
141 if y_tmp != self.y || self.x == 0 {
142 break;
143 }
144 }
145
146 Some(xy)
147 }
148}
149
150impl Iterator for SemicircleFilled<isize> {
151 type Item = (Range<isize>, isize);
152
153 #[inline]
154 fn next(&mut self) -> Option<Self::Item> {
155 if self.x == 0 {
156 return None;
157 }
158
159 let xy = (self.x..-self.x, -(self.y + 1));
160
161 // FIXME: This loop creates redundant iterations!
162 loop {
163 let y_tmp = self.y;
164 let err_tmp = self.err;
165
166 if err_tmp <= self.y {
167 self.y += 1;
168 self.err += 2 * self.y + 2;
169 };
170
171 if err_tmp > self.x || self.err > self.y {
172 self.x += 1;
173 self.err += 2 * self.x + 2;
174 }
175
176 if y_tmp != self.y || self.x == 0 {
177 break;
178 }
179 }
180
181 Some(xy)
182 }
183}
184
185impl Iterator for SemicircleFilled<i64> {
186 type Item = (Range<i64>, i64);
187
188 #[inline]
189 fn next(&mut self) -> Option<Self::Item> {
190 if self.x == 0 {
191 return None;
192 }
193
194 let xy = (self.x..-self.x, -(self.y + 1));
195
196 // FIXME: This loop creates redundant iterations!
197 loop {
198 let y_tmp = self.y;
199 let err_tmp = self.err;
200
201 if err_tmp <= self.y {
202 self.y += 1;
203 self.err += 2 * self.y + 2;
204 };
205
206 if err_tmp > self.x || self.err > self.y {
207 self.x += 1;
208 self.err += 2 * self.x + 2;
209 }
210
211 if y_tmp != self.y || self.x == 0 {
212 break;
213 }
214 }
215
216 Some(xy)
217 }
218}
219
220impl<T> FusedIterator for SemicircleFilled<T> where SemicircleFilled<T>: Iterator {}
221
222/// The trait for rasterization of given figures.
223pub trait Rasterization: Iterator {
224 /// An iterator adapter that creates (x, y) coordinates for the filled full circle.
225 ///
226 /// # Examples
227 ///
228 /// Basic usage:
229 ///
230 /// ```rust
231 /// use rasterization::{Rasterization, SemicircleFilled};
232 ///
233 /// let iter = SemicircleFilled::<i32>::new(2_usize).circle();
234 /// let vec = iter.collect::<Vec<_>>();
235 /// assert_eq!(vec, vec![(-2, -1), (-2, 0), (-1, -1), (-1, 0), (0, -1), (0, 0),
236 /// (1, -1), (1, 0), (-1, -2), (-1, 1), (0, -2), (0, 1)]);
237 /// ```
238 #[inline]
239 fn circle<T, R>(self) -> impl Iterator<Item = (T, T)> + Clone + Debug
240 where
241 Self: Sized + Iterator<Item = (R, T)> + Clone + Debug,
242 T: Add<Output = T> + One + Sub<Output = T> + Neg<Output = T> + Copy + Debug,
243 R: Iterator<Item = T> + Clone + Debug,
244 {
245 self.flat_map(|(range, y)| range.flat_map(move |x| [(x, y), (x, -y - T::one())]))
246 }
247
248 /// The iterator adapter adds an offset to a two-element tuple.
249 ///
250 /// # Examples
251 ///
252 /// Basic usage:
253 ///
254 /// ```rust
255 ///
256 /// use rasterization::{Rasterization, SemicircleFilled};
257 ///
258 /// let radius = 2_usize;
259 /// let offset_x = radius as i32;
260 /// let offset_y = radius as i32;
261 /// let iter = SemicircleFilled::<i32>::new(radius).circle().offset(offset_x, offset_y);
262 /// let vec = iter.collect::<Vec<_>>();
263 /// assert_eq!(vec, vec![(0, 1), (0, 2), (1, 1), (1, 2), (2, 1), (2, 2),
264 /// (3, 1), (3, 2), (1, 0), (1, 3), (2, 0), (2, 3)]);
265 /// ```
266 #[inline]
267 fn offset<T>(self, offset_x: T, offset_y: T) -> impl Iterator<Item = (T, T)> + Clone + Debug
268 where
269 Self: Sized + Iterator<Item = (T, T)> + Clone + Debug,
270 T: Add<Output = T> + Copy + Debug,
271 {
272 self.map(move |(x, y)| (x + offset_x, y + offset_y))
273 }
274
275 /// An iterator adapter that creates (x, y) coordinates for the filled long circle.
276 ///
277 /// # Examples
278 ///
279 /// Basic usage:
280 ///
281 /// ```rust
282 /// use rasterization::{Rasterization, SemicircleFilled};
283 ///
284 /// let iter = SemicircleFilled::<i32>::new(2_u64).circle_long(1, -1);
285 /// let vec = iter.collect::<Vec<_>>();
286 /// assert_eq!(vec, vec![(-1, -1), (-1, 0), (0, -1), (0, 0)]);
287 /// ```
288 #[inline]
289 fn circle_long<T>(self, start: T, end: T) -> impl Iterator<Item = (T, T)> + Clone + Debug
290 where
291 Self: Sized + Iterator<Item = (Range<T>, T)> + Clone + Debug,
292 T: Add<Output = T> + One + Sub<Output = T> + Neg<Output = T> + Copy + AddAssign + Debug,
293 Range<T>: Iterator<Item = T> + Clone + Debug,
294 {
295 self.flat_map(move |(mut range, y)| {
296 range.start += start;
297 range.end += end;
298 range.flat_map(move |x| [(x, y), (x, -y - T::one())])
299 })
300 }
301
302 /// An iterator adapter that creates (x, y) coordinates for the filled top semicircle.
303 ///
304 /// # Examples
305 ///
306 /// Basic usage:
307 ///
308 /// ```rust
309 /// use rasterization::{Rasterization, SemicircleFilled};
310 ///
311 /// let iter = SemicircleFilled::<isize>::new(2_u8).semicircle_top();
312 /// let vec = iter.collect::<Vec<_>>();
313 /// assert_eq!(vec, vec![(-2, -1), (-1, -1), (0, -1), (1, -1), (-1, -2), (0, -2)]);
314 /// ```
315 #[inline]
316 fn semicircle_top<T, R>(self) -> impl Iterator<Item = (T, T)> + Clone + Debug
317 where
318 Self: Sized + Iterator<Item = (R, T)> + Clone + Debug,
319 T: Add<Output = T> + One + Sub<Output = T> + Neg<Output = T> + Copy,
320 R: Iterator<Item = T> + Clone + Debug,
321 {
322 self.flat_map(|(range, y)| range.map(move |x| (x, y)))
323 }
324
325 /// An iterator adapter that creates (x, y) coordinates for the filled bottom semicircle.
326 ///
327 /// # Examples
328 ///
329 /// Basic usage:
330 ///
331 /// ```rust
332 /// use rasterization::{Rasterization, SemicircleFilled};
333 ///
334 /// let iter = SemicircleFilled::<isize>::new(2_u16).semicircle_bottom();
335 /// let vec = iter.collect::<Vec<_>>();
336 /// assert_eq!(vec, vec![(-2, 0), (-1, 0), (0, 0), (1, 0), (-1, 1), (0, 1)]);
337 /// ```
338 #[inline]
339 fn semicircle_bottom<T, R>(self) -> impl Iterator<Item = (T, T)> + Clone + Debug
340 where
341 Self: Sized + Iterator<Item = (R, T)> + Clone + Debug,
342 T: Add<Output = T> + One + Sub<Output = T> + Neg<Output = T> + Copy,
343 R: Iterator<Item = T> + Clone + Debug,
344 {
345 self.flat_map(|(range, y)| range.map(move |x| (x, -y - T::one())))
346 }
347
348 /// An iterator adapter that creates (x, y) coordinates for the filled top long semicircle.
349 ///
350 /// # Examples
351 ///
352 /// Basic usage:
353 ///
354 /// ```rust
355 /// use rasterization::{Rasterization, SemicircleFilled};
356 ///
357 /// let iter = SemicircleFilled::<isize>::new(2_u32).semicircle_top_long(0, 1);
358 /// let vec = iter.collect::<Vec<_>>();
359 /// assert_eq!(vec, vec![(-2, -1), (-1, -1), (0, -1), (1, -1), (2, -1), (-1, -2), (0, -2), (1, -2)]);
360 /// ```
361 #[inline]
362 fn semicircle_top_long<T>(
363 self,
364 start: T,
365 end: T,
366 ) -> impl Iterator<Item = (T, T)> + Clone + Debug
367 where
368 Self: Sized + Iterator<Item = (Range<T>, T)> + Clone + Debug,
369 T: Add<Output = T> + One + Sub<Output = T> + Neg<Output = T> + Copy + AddAssign,
370 Range<T>: Iterator<Item = T> + Clone + Debug,
371 {
372 self.flat_map(move |(mut range, y)| {
373 range.start += start;
374 range.end += end;
375 range.map(move |x| (x, y))
376 })
377 }
378
379 /// An iterator adapter that creates (x, y) coordinates for the filled bottom long semicircle.
380 ///
381 /// # Examples
382 ///
383 /// Basic usage:
384 ///
385 /// ```rust
386 /// use rasterization::{Rasterization, SemicircleFilled};
387 ///
388 /// let iter = SemicircleFilled::<isize>::new(2_usize).semicircle_bottom_long(0, 1);
389 /// let vec = iter.collect::<Vec<_>>();
390 /// assert_eq!(vec, vec![(-2, 0), (-1, 0), (0, 0), (1, 0), (2, 0), (-1, 1), (0, 1), (1, 1)]);
391 /// ```
392 #[inline]
393 fn semicircle_bottom_long<T>(
394 self,
395 start: T,
396 end: T,
397 ) -> impl Iterator<Item = (T, T)> + Clone + Debug
398 where
399 Self: Sized + Iterator<Item = (Range<T>, T)> + Clone + Debug,
400 T: Add<Output = T> + One + Sub<Output = T> + Neg<Output = T> + Copy + AddAssign,
401 Range<T>: Iterator<Item = T> + Clone + Debug,
402 {
403 self.flat_map(move |(mut range, y)| {
404 range.start += start;
405 range.end += end;
406 range.map(move |x| (x, -y - T::one()))
407 })
408 }
409
410 /// An iterator adapter that creates the (x, y) coordinates for the filled circle of the first quadrant.
411 ///
412 /// # Examples
413 ///
414 /// Basic usage:
415 ///
416 /// ```rust
417 /// use rasterization::{Rasterization, SemicircleFilled};
418 ///
419 /// let iter = SemicircleFilled::<isize>::new(3_u64).first_quadrant(0);
420 /// let vec = iter.collect::<Vec<_>>();
421 /// assert_eq!(vec, vec![(0, -1), (1, -1), (2, -1), (0, -2), (1, -2), (2, -2), (0, -3), (1, -3)]);
422 /// ```
423 #[inline]
424 fn first_quadrant<T>(self, end: T) -> impl Iterator<Item = (T, T)> + Clone + Debug
425 where
426 Self: Sized + Iterator<Item = (Range<T>, T)> + Clone + Debug,
427 T: Add<Output = T> + Zero + Sub<Output = T> + Neg<Output = T> + Copy + AddAssign,
428 Range<T>: Iterator<Item = T> + Clone + Debug,
429 {
430 self.flat_map(move |(mut range, y)| {
431 range.start = T::zero();
432 range.end += end;
433 range.map(move |x| (x, y))
434 })
435 }
436
437 /// An iterator adapter that creates the (x, y) coordinates for the filled circle of the second quadrant.
438 ///
439 /// # Examples
440 ///
441 /// Basic usage:
442 ///
443 /// ```rust
444 /// use rasterization::{Rasterization, SemicircleFilled};
445 ///
446 /// let iter = SemicircleFilled::<i64>::new(3_u8).second_quadrant(0);
447 /// let vec = iter.collect::<Vec<_>>();
448 /// assert_eq!(vec, vec![(-3, -1), (-2, -1), (-1, -1), (-3, -2), (-2, -2), (-1, -2), (-2, -3), (-1, -3)]);
449 /// ```
450 #[inline]
451 fn second_quadrant<T>(self, end: T) -> impl Iterator<Item = (T, T)> + Clone + Debug
452 where
453 Self: Sized + Iterator<Item = (Range<T>, T)> + Clone + Debug,
454 T: Add<Output = T> + Zero + Sub<Output = T> + Neg<Output = T> + Copy + AddAssign,
455 Range<T>: Iterator<Item = T> + Clone + Debug,
456 {
457 self.flat_map(move |(mut range, y)| {
458 range.end = T::zero() + end;
459 range.map(move |x| (x, y))
460 })
461 }
462
463 /// An iterator adapter that creates the (x, y) coordinates for the filled circle of the third quadrant.
464 ///
465 /// # Examples
466 ///
467 /// Basic usage:
468 ///
469 /// ```rust
470 /// use rasterization::{Rasterization, SemicircleFilled};
471 ///
472 /// let iter = SemicircleFilled::<i64>::new(3_u16).third_quadrant(0);
473 /// let vec = iter.collect::<Vec<_>>();
474 /// assert_eq!(vec, vec![(-3, 0), (-2, 0), (-1, 0), (-3, 1), (-2, 1), (-1, 1), (-2, 2), (-1, 2)]);
475 /// ```
476 #[inline]
477 fn third_quadrant<T>(self, end: T) -> impl Iterator<Item = (T, T)> + Clone + Debug
478 where
479 Self: Sized + Iterator<Item = (Range<T>, T)> + Clone + Debug,
480 T: Add<Output = T> + One + Zero + Sub<Output = T> + Neg<Output = T> + Copy + AddAssign,
481 Range<T>: Iterator<Item = T> + Clone + Debug,
482 {
483 self.flat_map(move |(mut range, y)| {
484 range.end = T::zero() + end;
485 range.map(move |x| (x, -y - T::one()))
486 })
487 }
488
489 /// An iterator adapter that creates the (x, y) coordinates for the filled circle of the fourth quadrant.
490 ///
491 /// # Examples
492 ///
493 /// Basic usage:
494 ///
495 /// ```rust
496 /// use rasterization::{Rasterization, SemicircleFilled};
497 ///
498 /// let iter = SemicircleFilled::<i64>::new(3_u32).fourth_quadrant(0);
499 /// let vec = iter.collect::<Vec<_>>();
500 /// assert_eq!(vec, vec![(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2)]);
501 /// ```
502 #[inline]
503 fn fourth_quadrant<T>(self, end: T) -> impl Iterator<Item = (T, T)> + Clone + Debug
504 where
505 Self: Sized + Iterator<Item = (Range<T>, T)> + Clone + Debug,
506 T: Add<Output = T> + One + Zero + Sub<Output = T> + Neg<Output = T> + Copy + AddAssign,
507 Range<T>: Iterator<Item = T> + Clone + Debug,
508 {
509 self.flat_map(move |(mut range, y)| {
510 range.start = T::zero();
511 range.end += end;
512 range.map(move |x| (x, -y - T::one()))
513 })
514 }
515
516 /// The iterator adapter fills a circle or part of it with a gradient color from crate [colorous].
517 /// Possible options are: vertical, horizontal or diagonal.
518 ///
519 /// # Examples
520 ///
521 /// Basic usage:
522 ///
523 /// ```rust
524 /// use rasterization::{Rasterization, SemicircleFilled,
525 /// DirectionGradient::BottomRight};
526 /// use colorous::Gradient;
527 ///
528 /// let radius = 256_usize;
529 /// let offset_x = radius as i32;
530 /// let offset_y = radius as i32;
531 /// let offset = (radius as f32 * 1.414).ceil() as i32;
532 /// let size = (offset * 2) as usize;
533 /// let iter = SemicircleFilled::<i32>::new(radius)
534 /// .circle()
535 /// .take(3)
536 /// .gradient(offset, size, BottomRight(colorous::BROWN_GREEN));
537 /// let vec = iter.collect::<Vec<_>>();
538 /// assert_eq!(vec, vec![(-256, -1, [162, 103, 27]), (-256, 0, [163, 104, 27]), (-255, -1, [163, 104, 27])]);
539 /// ```
540 /// [colorous]: https://crates.io/crates/colorous
541 #[inline]
542 fn gradient<T>(
543 self,
544 offset: T,
545 size: usize,
546 dir_grd: DirectionGradient,
547 ) -> impl Iterator<Item = (T, T, [u8; 3])> + Clone + Debug
548 where
549 Self: Sized + Iterator<Item = (T, T)> + Clone + Debug,
550 T: Add<Output = T> + Sub<Output = T> + Copy,
551 usize: FromAs<T>,
552 {
553 self.map(move |(x, y)| {
554 let grad_arg = match dir_grd {
555 DirectionGradient::Left(grad) => (offset - x, grad),
556 DirectionGradient::TopLeft(grad) => (offset - (x + y), grad),
557 DirectionGradient::Top(grad) => (offset - y, grad),
558 DirectionGradient::TopRight(grad) => (x - y + offset, grad),
559 DirectionGradient::Right(grad) => (x + offset, grad),
560 DirectionGradient::BottomRight(grad) => (x + y + offset, grad),
561 DirectionGradient::Bottom(grad) => (y + offset, grad),
562 DirectionGradient::BottomLeft(grad) => (offset - (x - y), grad),
563 };
564
565 (
566 x,
567 y,
568 grad_arg
569 .1
570 .eval_rational(usize::from_as(grad_arg.0), size)
571 .as_array(),
572 )
573 })
574 }
575}
576
577impl<T: ?Sized> Rasterization for T where T: Iterator {}
578
579impl DoubleEndedIterator for SemicircleFilled<i32> {
580 #[inline]
581 fn next_back(&mut self) -> Option<Self::Item> {
582 if self.x == 0 {
583 return None;
584 }
585
586 loop {
587 let x_tmp = self.x;
588 let y_tmp = self.y;
589 let err_tmp = self.err;
590
591 if err_tmp <= self.y {
592 self.y += 1;
593 self.err += 2 * self.y + 2;
594 };
595
596 if err_tmp > self.x || self.err > self.y {
597 self.x += 1;
598 self.err += 2 * self.x + 2;
599 if self.y == y_tmp {
600 return Some((-(self.y + 1)..(self.y + 1), (self.x - 1)));
601 }
602 }
603
604 if x_tmp != self.x {
605 let xy = (-self.y..self.y, (self.x - 1));
606
607 return Some(xy);
608 }
609 }
610 }
611}
612
613impl DoubleEndedIterator for SemicircleFilled<i64> {
614 #[inline]
615 fn next_back(&mut self) -> Option<Self::Item> {
616 if self.x == 0 {
617 return None;
618 }
619
620 loop {
621 let x_tmp = self.x;
622 let y_tmp = self.y;
623 let err_tmp = self.err;
624
625 if err_tmp <= self.y {
626 self.y += 1;
627 self.err += 2 * self.y + 2;
628 };
629
630 if err_tmp > self.x || self.err > self.y {
631 self.x += 1;
632 self.err += 2 * self.x + 2;
633 if self.y == y_tmp {
634 return Some((-(self.y + 1)..(self.y + 1), (self.x - 1)));
635 }
636 }
637
638 if x_tmp != self.x {
639 let xy = (-self.y..self.y, (self.x - 1));
640
641 return Some(xy);
642 }
643 }
644 }
645}
646
647impl DoubleEndedIterator for SemicircleFilled<isize> {
648 #[inline]
649 fn next_back(&mut self) -> Option<Self::Item> {
650 if self.x == 0 {
651 return None;
652 }
653
654 loop {
655 let x_tmp = self.x;
656 let y_tmp = self.y;
657 let err_tmp = self.err;
658
659 if err_tmp <= self.y {
660 self.y += 1;
661 self.err += 2 * self.y + 2;
662 };
663
664 if err_tmp > self.x || self.err > self.y {
665 self.x += 1;
666 self.err += 2 * self.x + 2;
667 if self.y == y_tmp {
668 return Some((-(self.y + 1)..(self.y + 1), (self.x - 1)));
669 }
670 }
671
672 if x_tmp != self.x {
673 let xy = (-self.y..self.y, (self.x - 1));
674
675 return Some(xy);
676 }
677 }
678 }
679}