graphics_shapes/
polygon.rs1use crate::prelude::*;
2use crate::shape_box::ShapeBox;
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8#[derive(Debug, Clone, PartialEq)]
9pub struct Polygon {
10 points: Vec<Coord>,
11 fpoints: Vec<(f32, f32)>,
12 is_regular: bool,
13 center: Coord,
14 is_convex: bool,
15}
16
17impl IntersectsContains for Polygon {}
18
19impl Polygon {
20 #[must_use]
21 pub fn new<'a, P: Into<Coord>>(points: &'a [P]) -> Self
22 where
23 Coord: From<&'a P>,
24 {
25 let points: Vec<Coord> = points.iter().map(|p| p.into()).collect();
26 let fpoints = points.iter().map(|p| (p.x as f32, p.y as f32)).collect();
27 let is_convex = is_convex(&points);
28 let mut poly = Self {
29 points: points.clone(),
30 fpoints,
31 center: Coord::default(),
32 is_regular: false,
33 is_convex,
34 };
35 poly.center = poly.top_left().mid_point(poly.bottom_right());
36 let dists: Vec<usize> = points.iter().map(|p| p.distance(poly.center)).collect();
37 poly.is_regular = dists.iter().all(|dist| dist == &dists[0]);
38 poly
39 }
40}
41
42impl Polygon {
43 #[inline]
44 #[must_use]
45 pub fn fpoints(&self) -> &Vec<(f32, f32)> {
46 &self.fpoints
47 }
48
49 #[inline]
51 #[must_use]
52 pub fn is_regular(&self) -> bool {
53 self.is_regular
54 }
55
56 #[must_use]
58 pub fn point_closest_to_center(&self) -> Coord {
59 let mut list = self.points.clone();
60 list.sort_by_key(|p| p.distance(self.center));
61 list[0]
62 }
63
64 #[must_use]
66 pub fn point_farthest_from_center(&self) -> Coord {
67 let mut list = self.points.clone();
68 list.sort_by_key(|p| p.distance(self.center));
69 *list.last().unwrap()
70 }
71
72 #[inline]
74 #[must_use]
75 pub fn is_convex(&self) -> bool {
76 self.is_convex
77 }
78}
79
80impl Shape for Polygon {
81 fn from_points(points: &[Coord]) -> Self
82 where
83 Self: Sized,
84 {
85 Polygon::new(points)
86 }
87
88 fn rebuild(&self, points: &[Coord]) -> Self
89 where
90 Self: Sized,
91 {
92 Polygon::from_points(points)
93 }
94
95 fn contains(&self, point: Coord) -> bool {
96 let mut j = self.fpoints.len() - 1;
97 let mut odd_number_of_nodes = false;
98 let fpoint = (point.x as f32, point.y as f32);
99
100 for i in 0..self.fpoints.len() {
101 if (self.fpoints[i].1 < fpoint.1 && self.fpoints[j].1 >= fpoint.1
102 || self.fpoints[j].1 < fpoint.1 && self.fpoints[i].1 >= fpoint.1)
103 && (self.fpoints[i].0 <= fpoint.0 || self.fpoints[j].0 <= fpoint.0)
104 {
105 odd_number_of_nodes ^= self.fpoints[i].0
106 + (fpoint.1 - self.fpoints[i].1) / (self.fpoints[j].1 - self.fpoints[i].1)
107 * (self.fpoints[j].0 - self.fpoints[i].0)
108 < fpoint.0;
109 }
110 j = i;
111 }
112
113 odd_number_of_nodes
114 }
115
116 fn points(&self) -> Vec<Coord> {
117 self.points.clone()
118 }
119
120 #[inline]
121 fn center(&self) -> Coord {
122 self.center
123 }
124
125 fn outline_pixels(&self) -> Vec<Coord> {
126 self.as_lines()
127 .iter()
128 .flat_map(|line| line.outline_pixels())
129 .collect()
130 }
131
132 fn filled_pixels(&self) -> Vec<Coord> {
133 let mut output = vec![];
134 let poly: Vec<(f32, f32)> = self
135 .points
136 .iter()
137 .map(|c| (c.x as f32, c.y as f32))
138 .collect();
139 let y_start = self.top();
140 let y_end = self.bottom();
141 for y in y_start..y_end {
142 let mut node = vec![];
143 let mut node_count = 0;
144 let y = y as f32;
145 let mut j = poly.len() - 1;
146 for i in 0..poly.len() {
147 if poly[i].1 < y && poly[j].1 >= y || poly[j].1 < y && poly[i].1 >= y {
148 node.push(
149 poly[i].0
150 + (y - poly[i].1) / (poly[j].1 - poly[i].1) * (poly[j].0 - poly[i].0),
151 );
152 node_count += 1;
153 }
154 j = i;
155 }
156 let mut i = 0;
157 if node_count > 0 {
158 while i < (node_count - 1) {
159 if node[i] > node[i + 1] {
160 node.swap(i, i + 1);
161 i = i.saturating_sub(1);
162 } else {
163 i += 1;
164 }
165 }
166 for i in (0..node_count - 1).step_by(2) {
167 for x in (node[i] as isize)..(node[i + 1] as isize) {
168 output.push(coord!(x + 1, y as isize));
169 }
170 }
171 }
172 }
173 output
174 }
175
176 fn to_shape_box(&self) -> ShapeBox {
177 ShapeBox::Polygon(self.clone())
178 }
179}
180
181impl Polygon {
182 #[must_use]
184 pub fn as_inner_circle(&self) -> Circle {
185 Circle::from_points(&[self.center, self.point_closest_to_center()])
186 }
187
188 #[must_use]
190 pub fn as_outer_circle(&self) -> Circle {
191 Circle::from_points(&[self.center, self.point_farthest_from_center()])
192 }
193
194 #[must_use]
196 pub fn as_avg_circle(&self) -> Circle {
197 let total: usize = self.points.iter().map(|p| p.distance(self.center)).sum();
198 let radius = total / self.points.len();
199 Circle::new(self.center, radius)
200 }
201
202 #[must_use]
204 pub fn as_circle(&self) -> Option<Circle> {
205 if self.is_regular {
206 Some(Circle::from_points(&[self.center, self.points[0]]))
207 } else {
208 None
209 }
210 }
211
212 #[must_use]
214 pub fn as_rect(&self) -> Rect {
215 Rect::new((self.left(), self.top()), (self.right(), self.bottom()))
216 }
217
218 #[must_use]
219 pub fn as_lines(&self) -> Vec<Line> {
220 let mut lines = vec![];
221 let poly = self.points.clone();
222 for i in 0..poly.len() - 1 {
223 lines.push(Line::new(poly[i], poly[i + 1]));
224 }
225 lines.push(Line::new(poly[poly.len() - 1], poly[0]));
226 lines
227 }
228
229 #[must_use]
232 pub fn as_triangles(&self) -> Option<Vec<Triangle>> {
233 if !self.is_convex {
234 return None;
235 }
236 let mut output = vec![];
237 for coords in self.points.windows(2) {
238 output.push(Triangle::new(coords[0], coords[1], self.center));
239 }
240 output.push(Triangle::new(
241 *self.points.last().unwrap(),
242 self.points[0],
243 self.center,
244 ));
245
246 Some(output)
247 }
248}
249
250fn is_convex(points: &[Coord]) -> bool {
251 let mut prev = 0;
252 for i in 0..points.len() {
253 let product = (points[(i + 1) % points.len()] - points[i])
254 .cross_product(points[(i + 2) % points.len()] - points[i]);
255 if product != 0 {
256 if product * prev < 0 {
257 return false;
258 } else {
259 prev = product;
260 }
261 }
262 }
263 true
264}