1use crate::{BoundingRect, Line, Point, Polygon, Rect, RotatedRect, Vec2};
2
3use rten_tensor::{MatrixLayout, NdTensorViewMut};
4
5fn clamp_to_bounds(p: Point, height: i32, width: i32) -> Point {
8 Point::from_yx(
9 p.y.clamp(0, height.saturating_sub(1).max(0)),
10 p.x.clamp(0, width.saturating_sub(1).max(0)),
11 )
12}
13
14pub fn stroke_rect<T: Copy>(mut mask: NdTensorViewMut<T, 2>, rect: Rect, value: T, width: u32) {
19 let width = width as i32;
20
21 fill_rect(
23 mask.view_mut(),
24 Rect::from_tlbr(rect.top(), rect.left(), rect.bottom(), rect.left() + width),
25 value,
26 );
27
28 fill_rect(
30 mask.view_mut(),
31 Rect::from_tlbr(
32 rect.top(),
33 rect.left() + width,
34 rect.top() + width,
35 rect.right() - width,
36 ),
37 value,
38 );
39
40 fill_rect(
42 mask.view_mut(),
43 Rect::from_tlbr(
44 rect.top(),
45 rect.right() - width,
46 rect.bottom(),
47 rect.right(),
48 ),
49 value,
50 );
51
52 fill_rect(
54 mask.view_mut(),
55 Rect::from_tlbr(
56 rect.bottom() - width,
57 rect.left() + width,
58 rect.bottom(),
59 rect.right() - width,
60 ),
61 value,
62 );
63}
64
65pub fn fill_rect<T: Copy>(mut mask: NdTensorViewMut<T, 2>, rect: Rect, value: T) {
67 for y in rect.top()..rect.bottom() {
68 for x in rect.left()..rect.right() {
69 mask[[y as usize, x as usize]] = value;
70 }
71 }
72}
73
74struct BreshamPoints {
80 current: Point,
82
83 remaining_steps: u32,
85
86 dx: i32,
88
89 dy: i32,
91
92 error: i32,
95
96 x_step: i32,
98
99 y_step: i32,
101}
102
103impl BreshamPoints {
104 fn new(l: Line) -> BreshamPoints {
105 let dx = (l.end.x - l.start.x).abs();
106 let dy = (l.end.y - l.start.y).abs();
107
108 BreshamPoints {
109 current: l.start,
110 remaining_steps: dx.max(dy) as u32,
111
112 dx: dx * 2,
114 dy: dy * 2,
115
116 error: if dx >= dy { dy * 2 - dx } else { dx * 2 - dy },
117 x_step: (l.end.x - l.start.x).signum(),
118 y_step: (l.end.y - l.start.y).signum(),
119 }
120 }
121}
122
123impl Iterator for BreshamPoints {
124 type Item = Point;
125
126 fn next(&mut self) -> Option<Point> {
127 if self.remaining_steps == 0 {
128 return None;
129 }
130
131 let current = self.current;
132 self.remaining_steps -= 1;
133
134 if self.x_step == 0 {
135 self.current.y += self.y_step;
137 } else if self.y_step == 0 {
138 self.current.x += self.x_step;
140 } else if self.dx >= self.dy {
141 if self.error >= 0 {
144 self.current.y += self.y_step;
145 self.error -= self.dx;
146 }
147 self.error += self.dy;
148 self.current.x += self.x_step;
149 } else {
150 if self.error >= 0 {
153 self.current.x += self.x_step;
154 self.error -= self.dy
155 }
156 self.error += self.dx;
157 self.current.y += self.y_step;
158 }
159
160 Some(current)
161 }
162}
163
164pub fn draw_line<T: Copy>(mut image: NdTensorViewMut<T, 2>, line: Line, value: T, width: u32) {
166 if width == 0 {
167 return;
168 }
169
170 if width == 1 {
171 let img_height: i32 = image.rows().try_into().unwrap();
174 let img_width: i32 = image.cols().try_into().unwrap();
175
176 let start = clamp_to_bounds(line.start, img_height, img_width);
177 let end = clamp_to_bounds(line.end, img_height, img_width);
178 let clamped = Line::from_endpoints(start, end);
179 for p in BreshamPoints::new(clamped) {
180 image[p.coord()] = value;
181 }
182 } else {
183 let line = line.to_f32();
185 let line_vec = Vec2::from_xy(line.width(), line.height());
186 let rrect = RotatedRect::new(
187 line.center(),
188 line_vec.perpendicular(),
189 line_vec.length(),
190 width as f32,
191 );
192
193 let corners: [Point<i32>; 4] = rrect
194 .corners()
195 .map(|c| Point::from_yx(c.y as i32, c.x as i32));
196
197 for p in Polygon::new(corners).fill_iter() {
198 if let Some(img_val) = image.get_mut(p.coord()) {
199 *img_val = value;
200 }
201 }
202 }
203}
204
205pub fn draw_polygon<T: Copy>(
207 mut image: NdTensorViewMut<T, 2>,
208 poly: &[Point],
209 value: T,
210 width: u32,
211) {
212 for edge in Polygon::new(poly).edges() {
213 draw_line(image.view_mut(), edge, value, width);
214 }
215}
216
217#[derive(Clone, Copy, Debug)]
219struct Edge {
220 start_y: i32,
222
223 y_steps: u32,
225
226 x: i32,
228
229 error: i32,
232
233 error_incr: i32,
235
236 error_decr: i32,
238
239 x_step: i32,
241
242 extra_x_step: i32,
244}
245
246pub struct FillIter {
252 edges: Vec<Edge>,
254
255 active_edges: Vec<Edge>,
259
260 bounds: Rect,
262
263 cursor: Point,
265}
266
267impl FillIter {
268 pub(crate) fn new(poly: Polygon<i32, &[Point]>) -> FillIter {
269 let mut edges: Vec<_> = poly
270 .edges()
271 .filter(|e| e.start.y != e.end.y)
273 .map(|e| {
274 let (start, end) = if e.start.y <= e.end.y {
276 (e.start, e.end)
277 } else {
278 (e.end, e.start)
279 };
280
281 let delta_x = end.x - start.x;
282 let delta_y = end.y - start.y;
283
284 Edge {
285 start_y: start.y,
286 y_steps: delta_y as u32,
287
288 x: start.x,
289
290 x_step: delta_x / delta_y,
292
293 error: if delta_x >= 0 {
296 0
297 } else {
298 -delta_y + 1
300 },
301 error_incr: delta_x.abs() % delta_y,
302 error_decr: delta_y,
303 extra_x_step: delta_x.signum(),
304 }
305 })
306 .collect();
307 edges.sort_by_key(|e| -e.start_y);
308
309 let active_edges = Vec::with_capacity(edges.len());
310
311 let bounds = poly.bounding_rect();
312 let mut iter = FillIter {
313 edges,
314 active_edges,
315 bounds,
316 cursor: if bounds.is_empty() {
317 bounds.bottom_right()
321 } else {
322 bounds.top_left()
323 },
324 };
325 iter.update_active_edges();
326
327 iter
328 }
329
330 fn update_active_edges(&mut self) {
332 self.active_edges.retain_mut(|e| {
334 e.y_steps -= 1;
335 if e.y_steps > 0 {
336 e.x += e.x_step;
339 e.error += e.error_incr;
340 if e.error > 0 {
341 e.error -= e.error_decr;
342 e.x += e.extra_x_step;
343 }
344 true
345 } else {
346 false
347 }
348 });
349
350 while let Some(edge) = self.edges.last().copied() {
352 if edge.start_y > self.cursor.y {
353 break;
356 }
357 self.edges.pop();
358 self.active_edges.push(edge);
359 }
360
361 self.active_edges
365 .sort_by_key(|e| (e.x, e.x_step, e.extra_x_step));
366 }
367}
368
369impl Iterator for FillIter {
370 type Item = Point;
371
372 fn next(&mut self) -> Option<Point> {
373 while !self.active_edges.is_empty() {
374 let current = self.cursor;
375 let intersections = self
376 .active_edges
377 .iter()
378 .fold(0, |i, e| if e.x <= current.x { i + 1 } else { i });
379
380 self.cursor.move_by(0, 1);
381 if self.cursor.x == self.bounds.right() {
382 self.cursor.move_to(current.y + 1, self.bounds.left());
383 self.update_active_edges();
384 }
385
386 if intersections % 2 == 1 {
387 return Some(current);
388 }
389 }
390 None
391 }
392}
393
394pub type Rgb<T = u8> = [T; 3];
395
396#[derive(Copy, Clone)]
398struct PainterState<T> {
399 stroke: Rgb<T>,
400 stroke_width: u32,
401}
402
403pub struct Painter<'a, T> {
413 surface: NdTensorViewMut<'a, T, 3>,
415 saved_states: Vec<PainterState<T>>,
416 state: PainterState<T>,
417}
418
419impl<'a, T: Copy + Default> Painter<'a, T> {
420 pub fn new(surface: NdTensorViewMut<'a, T, 3>) -> Painter<'a, T> {
422 Painter {
423 surface,
424 state: PainterState {
425 stroke: [T::default(); 3],
426 stroke_width: 1,
427 },
428 saved_states: Vec::new(),
429 }
430 }
431
432 pub fn save(&mut self) {
434 self.saved_states.push(self.state);
435 }
436
437 pub fn restore(&mut self) {
439 if let Some(state) = self.saved_states.pop() {
440 self.state = state;
441 }
442 }
443
444 pub fn with_save<F: Fn(&mut Self)>(&mut self, f: F) {
450 self.save();
451 f(self);
452 self.restore();
453 }
454
455 pub fn set_stroke(&mut self, stroke: Rgb<T>) {
457 self.state.stroke = stroke;
458 }
459
460 pub fn set_stroke_width(&mut self, width: u32) {
461 self.state.stroke_width = width;
462 }
463
464 pub fn draw_polygon(&mut self, points: &[Point]) {
466 for i in 0..3 {
467 draw_polygon(
468 self.surface.slice_mut([i]),
469 points,
470 self.state.stroke[i],
471 self.state.stroke_width,
472 );
473 }
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use rten_tensor::prelude::*;
480 use rten_tensor::{MatrixLayout, NdTensor, NdTensorView};
481 use rten_testing::TestCases;
482
483 use crate::tests::print_grid;
484 use crate::{BoundingRect, Painter, Point, Polygon, Rect};
485
486 use super::{draw_polygon, stroke_rect};
487
488 fn nonzero_points<T: Default + PartialEq>(grid: NdTensorView<T, 2>) -> Vec<Point> {
490 let mut points = Vec::new();
491 for y in 0..grid.rows() {
492 for x in 0..grid.cols() {
493 if grid[[y, x]] != T::default() {
494 points.push(Point::from_yx(y as i32, x as i32))
495 }
496 }
497 }
498 points
499 }
500
501 fn image_from_2d_array<const M: usize, const N: usize>(xs: [[i32; N]; M]) -> NdTensor<i32, 2> {
503 let mut image = NdTensor::zeros([M, N]);
504 for y in 0..M {
505 for x in 0..N {
506 image[[y, x]] = xs[y][x];
507 }
508 }
509 image
510 }
511
512 fn compare_images(a: NdTensorView<i32, 2>, b: NdTensorView<i32, 2>) {
514 assert_eq!(a.rows(), b.rows());
515 assert_eq!(a.cols(), b.cols());
516
517 for y in 0..a.rows() {
518 for x in 0..a.cols() {
519 if a[[y, x]] != b[[y, x]] {
520 print_grid(a);
521 panic!("mismatch at coord [{}, {}]", y, x);
522 }
523 }
524 }
525 }
526
527 #[test]
528 fn test_draw_polygon() {
529 #[derive(Debug)]
530 struct Case {
531 points: &'static [[i32; 2]],
532 expected: NdTensor<i32, 2>,
533 }
534
535 let cases = [
536 Case {
538 points: &[[0, 0], [0, 4], [4, 4], [4, 0]],
539 expected: image_from_2d_array([
540 [1, 1, 1, 1, 1],
541 [1, 0, 0, 0, 1],
542 [1, 0, 0, 0, 1],
543 [1, 0, 0, 0, 1],
544 [1, 1, 1, 1, 1],
545 ]),
546 },
547 Case {
549 points: &[[0, 2], [2, 0], [4, 2], [2, 4]],
550 expected: image_from_2d_array([
551 [0, 0, 1, 0, 0],
552 [0, 1, 0, 1, 0],
553 [1, 0, 0, 0, 1],
554 [0, 1, 0, 1, 0],
555 [0, 0, 1, 0, 0],
556 ]),
557 },
558 Case {
560 points: &[[0, 2], [2, 1], [4, 2], [2, 3]],
561 expected: image_from_2d_array([
562 [0, 0, 1, 0, 0],
563 [0, 1, 1, 0, 0],
564 [0, 1, 0, 1, 0],
565 [0, 0, 1, 1, 0],
566 [0, 0, 1, 0, 0],
567 ]),
568 },
569 ];
570
571 cases.test_each(|case| {
572 let points: Vec<_> = case
573 .points
574 .iter()
575 .map(|[y, x]| Point::from_yx(*y, *x))
576 .collect();
577
578 let mut image = NdTensor::zeros(case.expected.shape());
579 draw_polygon(image.view_mut(), &points, 1, 1 );
580 compare_images(image.view(), case.expected.view());
581 })
582 }
583
584 #[test]
585 fn test_painter_draw_polygon() {
586 let [width, height] = [6, 6];
587 let mut img = NdTensor::zeros([3, height, width]);
588 let mut painter = Painter::new(img.view_mut());
589 let [r, g, b] = [255, 100, 50];
590 painter.set_stroke([r, g, b]);
591
592 painter.draw_polygon(&Rect::from_tlbr(2, 2, 5, 5).corners());
593
594 let expected_r = image_from_2d_array([
595 [0, 0, 0, 0, 0, 0],
596 [0, 0, 0, 0, 0, 0],
597 [0, 0, r, r, r, r],
598 [0, 0, r, 0, 0, r],
599 [0, 0, r, 0, 0, r],
600 [0, 0, r, r, r, r],
601 ]);
602 let expected_g = expected_r.map(|&x| if x == r { g } else { 0 });
603 let expected_b = expected_r.map(|&x| if x == r { b } else { 0 });
604
605 compare_images(img.slice([0]), expected_r.view());
606 compare_images(img.slice([1]), expected_g.view());
607 compare_images(img.slice([2]), expected_b.view());
608 }
609
610 #[test]
611 fn test_painter_save_restore() {
612 let [width, height] = [6, 6];
613 let mut img = NdTensor::zeros([3, height, width]);
614 let mut painter = Painter::new(img.view_mut());
615
616 let r1 = 255;
617 let r2 = 50;
618
619 painter.set_stroke([r1, 0, 0]);
621
622 painter.with_save(|painter| {
623 painter.set_stroke([r2, 0, 0]);
624 painter.draw_polygon(&Rect::from_tlbr(3, 3, 4, 4).corners());
625 });
626
627 painter.draw_polygon(&Rect::from_tlbr(2, 2, 5, 5).corners());
629
630 let expected = image_from_2d_array([
631 [0, 0, 0, 0, 0, 0],
632 [0, 0, 0, 0, 0, 0],
633 [0, 0, r1, r1, r1, r1],
634 [0, 0, r1, r2, r2, r1],
635 [0, 0, r1, r2, r2, r1],
636 [0, 0, r1, r1, r1, r1],
637 ]);
638 compare_images(img.slice([0]), expected.view());
639 }
640
641 #[test]
642 fn test_stroke_rect() {
643 let mut mask = NdTensor::zeros([10, 10]);
644 let rect = Rect::from_tlbr(4, 4, 9, 9);
645
646 stroke_rect(mask.view_mut(), rect, 1, 1);
647 let points = nonzero_points(mask.view());
648
649 assert_eq!(
650 Polygon::new(&points).bounding_rect(),
651 rect.adjust_tlbr(0, 0, -1, -1)
652 );
653 }
654}