imageproc/drawing/
polygon.rs

1use crate::definitions::Image;
2use crate::drawing::line::{draw_antialiased_line_segment_mut, draw_line_segment_mut};
3use crate::drawing::Canvas;
4use crate::point::Point;
5use image::{GenericImage, ImageBuffer};
6use std::cmp::{max, min};
7
8#[must_use = "the function does not modify the original image"]
9fn draw_polygon_with<I, L>(
10    image: &I,
11    poly: &[Point<i32>],
12    color: I::Pixel,
13    plotter: L,
14) -> Image<I::Pixel>
15where
16    I: GenericImage,
17    L: Fn(&mut Image<I::Pixel>, (f32, f32), (f32, f32), I::Pixel),
18{
19    let mut out = ImageBuffer::new(image.width(), image.height());
20    out.copy_from(image, 0, 0).unwrap();
21    draw_polygon_with_mut(&mut out, poly, color, plotter);
22    out
23}
24
25fn draw_polygon_with_mut<C, L>(canvas: &mut C, poly: &[Point<i32>], color: C::Pixel, plotter: L)
26where
27    C: Canvas,
28    L: Fn(&mut C, (f32, f32), (f32, f32), C::Pixel),
29{
30    if poly.is_empty() {
31        return;
32    }
33    if poly[0] == poly[poly.len() - 1] {
34        panic!(
35            "First point {:?} == last point {:?}",
36            poly[0],
37            poly[poly.len() - 1]
38        );
39    }
40
41    let mut y_min = i32::MAX;
42    let mut y_max = i32::MIN;
43    for p in poly {
44        y_min = min(y_min, p.y);
45        y_max = max(y_max, p.y);
46    }
47
48    let (width, height) = canvas.dimensions();
49
50    // Intersect polygon vertical range with image bounds
51    y_min = max(0, min(y_min, height as i32 - 1));
52    y_max = max(0, min(y_max, height as i32 - 1));
53
54    let mut closed: Vec<Point<i32>> = poly.to_vec();
55    closed.push(poly[0]);
56
57    let edges: Vec<&[Point<i32>]> = closed.windows(2).collect();
58    let mut intersections = Vec::new();
59
60    for y in y_min..y_max + 1 {
61        for edge in &edges {
62            let p0 = edge[0];
63            let p1 = edge[1];
64
65            if p0.y <= y && p1.y >= y || p1.y <= y && p0.y >= y {
66                if p0.y == p1.y {
67                    // Need to handle horizontal lines specially
68                    intersections.push(p0.x);
69                    intersections.push(p1.x);
70                } else if p0.y == y || p1.y == y {
71                    if p1.y > y {
72                        intersections.push(p0.x);
73                    }
74                    if p0.y > y {
75                        intersections.push(p1.x);
76                    }
77                } else {
78                    let fraction = (y - p0.y) as f32 / (p1.y - p0.y) as f32;
79                    let inter = p0.x as f32 + fraction * (p1.x - p0.x) as f32;
80                    intersections.push(inter.round() as i32);
81                }
82            }
83        }
84
85        intersections.sort_unstable();
86        intersections.chunks(2).for_each(|range| {
87            let mut from = min(range[0], width as i32);
88            let mut to = min(range[1], width as i32 - 1);
89            if from < width as i32 && to >= 0 {
90                // draw only if range appears on the canvas
91                from = max(0, from);
92                to = max(0, to);
93
94                for x in from..to + 1 {
95                    canvas.draw_pixel(x as u32, y as u32, color);
96                }
97            }
98        });
99
100        intersections.clear();
101    }
102
103    for edge in &edges {
104        let start = (edge[0].x as f32, edge[0].y as f32);
105        let end = (edge[1].x as f32, edge[1].y as f32);
106        plotter(canvas, start, end, color);
107    }
108}
109
110/// Draws a polygon and its contents on a new copy of an image.
111///
112/// Draws as much of a filled polygon as lies within image bounds. The provided
113/// list of points should be an open path, i.e. the first and last points must not be equal.
114/// An implicit edge is added from the last to the first point in the slice.
115pub fn draw_polygon<I>(image: &I, poly: &[Point<i32>], color: I::Pixel) -> Image<I::Pixel>
116where
117    I: GenericImage,
118{
119    draw_polygon_with(image, poly, color, draw_line_segment_mut)
120}
121
122/// Draws a polygon and its contents on an image in place.
123///
124/// Draws as much of a filled polygon as lies within image bounds. The provided
125/// list of points should be an open path, i.e. the first and last points must not be equal.
126/// An implicit edge is added from the last to the first point in the slice.
127pub fn draw_polygon_mut<C>(canvas: &mut C, poly: &[Point<i32>], color: C::Pixel)
128where
129    C: Canvas,
130{
131    draw_polygon_with_mut(canvas, poly, color, draw_line_segment_mut);
132}
133
134/// Draws an anti-aliased polygon polygon and its contents on a new copy of an image.
135///
136/// Draws as much of a filled polygon as lies within image bounds. The provided
137/// list of points should be an open path, i.e. the first and last points must not be equal.
138/// An implicit edge is added from the last to the first point in the slice.
139///
140/// The parameters of blend are (line color, original color, line weight).
141/// Consider using [`interpolate`](fn.interpolate.html) for blend.
142pub fn draw_antialiased_polygon<I, B>(
143    image: &I,
144    poly: &[Point<i32>],
145    color: I::Pixel,
146    blend: B,
147) -> Image<I::Pixel>
148where
149    I: GenericImage,
150    B: Fn(I::Pixel, I::Pixel, f32) -> I::Pixel,
151{
152    draw_polygon_with(image, poly, color, |image, start, end, color| {
153        draw_antialiased_line_segment_mut(
154            image,
155            (start.0 as i32, start.1 as i32),
156            (end.0 as i32, end.1 as i32),
157            color,
158            &blend,
159        )
160    })
161}
162
163/// Draws an anti-aliased polygon and its contents on an image in place.
164///
165/// Draws as much of a filled polygon as lies within image bounds. The provided
166/// list of points should be an open path, i.e. the first and last points must not be equal.
167/// An implicit edge is added from the last to the first point in the slice.
168///
169/// The parameters of blend are (line color, original color, line weight).
170/// Consider using [`interpolate`](fn.interpolate.html) for blend.
171pub fn draw_antialiased_polygon_mut<I, B>(
172    image: &mut I,
173    poly: &[Point<i32>],
174    color: I::Pixel,
175    blend: B,
176) where
177    I: GenericImage,
178    B: Fn(I::Pixel, I::Pixel, f32) -> I::Pixel,
179{
180    draw_polygon_with_mut(image, poly, color, |image, start, end, color| {
181        draw_antialiased_line_segment_mut(
182            image,
183            (start.0 as i32, start.1 as i32),
184            (end.0 as i32, end.1 as i32),
185            color,
186            &blend,
187        )
188    });
189}
190
191/// Draws the outline of a polygon on an image in place.
192///
193/// Draws as much of the outline of the polygon as lies within image bounds. The provided
194/// list of points should be in polygon order and be an open path, i.e. the first
195/// and last points must not be equal. The edges of the polygon will be drawn in the order
196/// that they are provided, and an implicit edge will be added from the last to the first
197/// point in the slice.
198pub fn draw_hollow_polygon<I>(
199    image: &mut I,
200    poly: &[Point<f32>],
201    color: I::Pixel,
202) -> Image<I::Pixel>
203where
204    I: GenericImage,
205{
206    let mut out = ImageBuffer::new(image.width(), image.height());
207    out.copy_from(image, 0, 0).unwrap();
208    draw_hollow_polygon_mut(&mut out, poly, color);
209    out
210}
211
212/// Draws the outline of a polygon on an image in place.
213///
214/// Draws as much of the outline of the polygon as lies within image bounds. The provided
215/// list of points should be in polygon order and be an open path, i.e. the first
216/// and last points must not be equal. The edges of the polygon will be drawn in the order
217/// that they are provided, and an implicit edge will be added from the last to the first
218/// point in the slice.
219pub fn draw_hollow_polygon_mut<C>(canvas: &mut C, poly: &[Point<f32>], color: C::Pixel)
220where
221    C: Canvas,
222{
223    if poly.is_empty() {
224        return;
225    }
226    if poly.len() < 2 {
227        panic!(
228            "Polygon only has {} points, but at least two are needed.",
229            poly.len(),
230        );
231    }
232    if poly[0] == poly[poly.len() - 1] {
233        panic!(
234            "First point {:?} == last point {:?}",
235            poly[0],
236            poly[poly.len() - 1]
237        );
238    }
239    for window in poly.windows(2) {
240        crate::drawing::draw_line_segment_mut(
241            canvas,
242            (window[0].x, window[0].y),
243            (window[1].x, window[1].y),
244            color,
245        );
246    }
247    let first = poly[0];
248    let last = poly.iter().last().unwrap();
249    crate::drawing::draw_line_segment_mut(canvas, (first.x, first.y), (last.x, last.y), color);
250}