1use cgmath::{BaseFloat, Point2, Vector2};
4use cgmath::prelude::*;
5
6use crate::{Aabb2, Line2, Ray2};
7use crate::prelude::*;
8use crate::primitive::util::{get_bound, get_max_point};
9
10#[derive(Debug, Clone, PartialEq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct ConvexPolygon<S> {
17 pub vertices: Vec<Point2<S>>,
19}
20
21impl<S> ConvexPolygon<S> {
22 pub fn new(vertices: Vec<Point2<S>>) -> Self {
24 Self { vertices }
25 }
26}
27
28impl<S> Primitive for ConvexPolygon<S>
29where
30 S: BaseFloat,
31{
32 type Point = Point2<S>;
33
34 fn support_point<T>(&self, direction: &Vector2<S>, transform: &T) -> Point2<S>
35 where
36 T: Transform<Point2<S>>,
37 {
38 if self.vertices.len() < 10 {
39 get_max_point(self.vertices.iter(), direction, transform)
40 } else {
41 support_point(&self.vertices, direction, transform)
42 }
43 }
44}
45
46impl<S> ComputeBound<Aabb2<S>> for ConvexPolygon<S>
47where
48 S: BaseFloat,
49{
50 fn compute_bound(&self) -> Aabb2<S> {
51 get_bound(self.vertices.iter())
52 }
53}
54
55impl<S> Discrete<Ray2<S>> for ConvexPolygon<S>
56where
57 S: BaseFloat,
58{
59 fn intersects(&self, ray: &Ray2<S>) -> bool {
61 for j in 0..self.vertices.len() - 1 {
62 let i = if j == 0 {
63 self.vertices.len() - 1
64 } else {
65 j - 1
66 };
67 let normal = Vector2::new(
68 self.vertices[j].y - self.vertices[i].y,
69 self.vertices[i].x - self.vertices[j].x,
70 );
71 if ray.direction.dot(normal) < S::zero()
73 && ray.intersection(&Line2::new(self.vertices[i], self.vertices[j]))
75 .is_some()
76 {
77 return true;
78 }
79 }
80
81 false
82 }
83}
84
85impl<S> Continuous<Ray2<S>> for ConvexPolygon<S>
86where
87 S: BaseFloat,
88{
89 type Result = Point2<S>;
90
91 fn intersection(&self, ray: &Ray2<S>) -> Option<Point2<S>> {
93 for j in 0..self.vertices.len() - 1 {
94 let i = if j == 0 {
95 self.vertices.len() - 1
96 } else {
97 j - 1
98 };
99 let normal = Vector2::new(
100 self.vertices[j].y - self.vertices[i].y,
101 self.vertices[i].x - self.vertices[j].x,
102 );
103 if ray.direction.dot(normal) < S::zero() {
105 if let point @ Some(_) =
107 ray.intersection(&Line2::new(self.vertices[i], self.vertices[j]))
108 {
109 return point;
110 }
111 }
112 }
113
114 None
115 }
116}
117
118fn support_point<P, T>(vertices: &[P], direction: &P::Diff, transform: &T) -> P
119where
120 P: EuclideanSpace,
121 P::Scalar: BaseFloat,
122 T: Transform<P>,
123{
124 let direction = transform.inverse_transform_vector(*direction).unwrap();
125
126 let mut start_index: i32 = 0;
129 let mut max_dot = vertices[0].dot(direction);
130 if max_dot < P::Scalar::zero() {
131 start_index = vertices.len() as i32 / 2;
132 max_dot = dot_index(vertices, start_index, &direction);
133 }
134
135 let left_dot = dot_index(vertices, start_index - 1, &direction);
136 let right_dot = dot_index(vertices, start_index + 1, &direction);
137
138 let p = if start_index == 0 && max_dot > left_dot && max_dot > right_dot {
140 vertices[0]
141 } else {
142 let mut add: i32 = 1;
144 let mut previous_dot = left_dot;
145 if left_dot > max_dot && left_dot > right_dot {
146 add = -1;
147 previous_dot = right_dot;
148 }
149
150 let mut index = start_index + add;
152 let mut current_dot = max_dot;
153 if index == vertices.len() as i32 {
154 index = 0;
155 }
156 if index == -1 {
157 index = vertices.len() as i32 - 1;
158 }
159 while index != start_index {
160 let next_dot = dot_index(vertices, index + add, &direction);
161 if current_dot > previous_dot && current_dot > next_dot {
162 break;
163 }
164 previous_dot = current_dot;
165 current_dot = next_dot;
166 index += add;
167 if index == vertices.len() as i32 {
168 index = 0;
169 }
170 if index == -1 {
171 index = vertices.len() as i32 - 1;
172 }
173 }
174 vertices[index as usize]
175 };
176
177 transform.transform_point(p)
178}
179
180#[inline]
181fn dot_index<P>(vertices: &[P], index: i32, direction: &P::Diff) -> P::Scalar
182where
183 P: EuclideanSpace,
184 P::Scalar: BaseFloat,
185{
186 let index_u = index as usize;
187 if index_u == vertices.len() {
188 vertices[0]
189 } else if index == -1 {
190 vertices[vertices.len() - 1]
191 } else {
192 vertices[index_u]
193 }.dot(*direction)
194}
195
196#[cfg(test)]
197mod tests {
198 use cgmath::{Basis2, Decomposed, Point2, Rad, Vector2};
199 use approx::assert_ulps_eq;
200
201 use super::*;
202 use {Aabb2, Ray2};
203
204 #[test]
205 fn test_support_point() {
206 let vertices = vec![
207 Point2::new(5., 5.),
208 Point2::new(4., 6.),
209 Point2::new(3., 7.),
210 Point2::new(2., 6.),
211 Point2::new(1., 6.),
212 Point2::new(0., 5.),
213 Point2::new(-1., 4.),
214 Point2::new(-3., 3.),
215 Point2::new(-6., 1.),
216 Point2::new(-5., 0.),
217 Point2::new(-4., -1.),
218 Point2::new(-2., -3.),
219 Point2::new(0., -7.),
220 Point2::new(1., -8.),
221 Point2::new(2., -5.),
222 Point2::new(3., 0.),
223 Point2::new(4., 3.),
224 ];
225 let t = transform(0., 0., 0.);
226 let point = support_point(&vertices, &Vector2::new(-1., 0.), &t);
227 assert_eq!(Point2::new(-5., 0.), point);
228
229 let point = support_point(&vertices, &Vector2::new(0., -1.), &t);
230 assert_eq!(Point2::new(1., -8.), point);
231
232 let point = support_point(&vertices, &Vector2::new(0., 1.), &t);
233 assert_eq!(Point2::new(3., 7.), point);
234
235 let point = support_point(&vertices, &Vector2::new(1., 0.), &t);
236 assert_eq!(Point2::new(5., 5.), point);
237 }
238
239 #[test]
240 fn test_bound() {
241 let vertices = vec![
242 Point2::new(5., 5.),
243 Point2::new(4., 6.),
244 Point2::new(3., 7.),
245 Point2::new(2., 6.),
246 Point2::new(1., 6.),
247 Point2::new(0., 5.),
248 Point2::new(-1., 4.),
249 Point2::new(-3., 3.),
250 Point2::new(-6., 1.),
251 Point2::new(-5., 0.),
252 Point2::new(-4., -1.),
253 Point2::new(-2., -3.),
254 Point2::new(0., -7.),
255 Point2::new(1., -8.),
256 Point2::new(2., -5.),
257 Point2::new(3., 0.),
258 Point2::new(4., 3.),
259 ];
260 let polygon = ConvexPolygon::new(vertices);
261 assert_eq!(
262 Aabb2::new(Point2::new(-6., -8.), Point2::new(5., 7.)),
263 polygon.compute_bound()
264 );
265 }
266
267 #[test]
268 fn test_ray_discrete() {
269 let vertices = vec![
270 Point2::new(5., 5.),
271 Point2::new(4., 6.),
272 Point2::new(3., 7.),
273 Point2::new(2., 6.),
274 Point2::new(1., 6.),
275 Point2::new(0., 5.),
276 Point2::new(-1., 4.),
277 Point2::new(-3., 3.),
278 Point2::new(-6., 1.),
279 Point2::new(-5., 0.),
280 Point2::new(-4., -1.),
281 Point2::new(-2., -3.),
282 Point2::new(0., -7.),
283 Point2::new(1., -8.),
284 Point2::new(2., -5.),
285 Point2::new(3., 0.),
286 Point2::new(4., 3.),
287 ];
288 let polygon = ConvexPolygon::new(vertices);
289 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., -1.));
290 assert!(polygon.intersects(&ray));
291 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., 1.));
292 assert!(!polygon.intersects(&ray));
293 let ray = Ray2::new(Point2::new(0., 7.2), Vector2::new(1., 0.));
294 assert!(!polygon.intersects(&ray));
295 let ray = Ray2::new(Point2::new(0., 6.8), Vector2::new(1., 0.));
296 assert!(polygon.intersects(&ray));
297 }
298
299 #[test]
300 fn test_ray_discrete_transformed() {
301 let vertices = vec![
302 Point2::new(5., 5.),
303 Point2::new(4., 6.),
304 Point2::new(3., 7.),
305 Point2::new(2., 6.),
306 Point2::new(1., 6.),
307 Point2::new(0., 5.),
308 Point2::new(-1., 4.),
309 Point2::new(-3., 3.),
310 Point2::new(-6., 1.),
311 Point2::new(-5., 0.),
312 Point2::new(-4., -1.),
313 Point2::new(-2., -3.),
314 Point2::new(0., -7.),
315 Point2::new(1., -8.),
316 Point2::new(2., -5.),
317 Point2::new(3., 0.),
318 Point2::new(4., 3.),
319 ];
320 let polygon = ConvexPolygon::new(vertices);
321 let t = transform(0., 0., 0.);
322 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., -1.));
323 assert!(polygon.intersects_transformed(&ray, &t));
324 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., 1.));
325 assert!(!polygon.intersects_transformed(&ray, &t));
326 let ray = Ray2::new(Point2::new(0., 7.2), Vector2::new(1., 0.));
327 assert!(!polygon.intersects_transformed(&ray, &t));
328 let ray = Ray2::new(Point2::new(0., 6.8), Vector2::new(1., 0.));
329 assert!(polygon.intersects_transformed(&ray, &t));
330 let t = transform(0., -2., 0.);
331 assert!(!polygon.intersects_transformed(&ray, &t));
332 let t = transform(0., 0., 0.3);
333 assert!(polygon.intersects_transformed(&ray, &t));
334 }
335
336 #[test]
337 fn test_ray_continuous() {
338 let vertices = vec![
339 Point2::new(5., 5.),
340 Point2::new(4., 6.),
341 Point2::new(3., 7.),
342 Point2::new(2., 6.),
343 Point2::new(1., 6.),
344 Point2::new(0., 5.),
345 Point2::new(-1., 4.),
346 Point2::new(-3., 3.),
347 Point2::new(-6., 1.),
348 Point2::new(-5., 0.),
349 Point2::new(-4., -1.),
350 Point2::new(-2., -3.),
351 Point2::new(0., -7.),
352 Point2::new(1., -8.),
353 Point2::new(2., -5.),
354 Point2::new(3., 0.),
355 Point2::new(4., 3.),
356 ];
357 let polygon = ConvexPolygon::new(vertices);
358 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., -1.));
359 assert_eq!(Some(Point2::new(0., 5.)), polygon.intersection(&ray));
360 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., 1.));
361 assert_eq!(None, polygon.intersection(&ray));
362 let ray = Ray2::new(Point2::new(0., 7.2), Vector2::new(1., 0.));
363 assert_eq!(None, polygon.intersection(&ray));
364 let ray = Ray2::new(Point2::new(0., 6.8), Vector2::new(1., 0.));
365 let p = polygon.intersection(&ray).unwrap();
366 assert_ulps_eq!(2.8, p.x);
367 assert_ulps_eq!(6.8, p.y);
368 }
369
370 #[test]
371 fn test_ray_continuous_transformed() {
372 let vertices = vec![
373 Point2::new(5., 5.),
374 Point2::new(4., 6.),
375 Point2::new(3., 7.),
376 Point2::new(2., 6.),
377 Point2::new(1., 6.),
378 Point2::new(0., 5.),
379 Point2::new(-1., 4.),
380 Point2::new(-3., 3.),
381 Point2::new(-6., 1.),
382 Point2::new(-5., 0.),
383 Point2::new(-4., -1.),
384 Point2::new(-2., -3.),
385 Point2::new(0., -7.),
386 Point2::new(1., -8.),
387 Point2::new(2., -5.),
388 Point2::new(3., 0.),
389 Point2::new(4., 3.),
390 ];
391 let t = transform(0., 0., 0.);
392 let polygon = ConvexPolygon::new(vertices);
393 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., -1.));
394 assert_eq!(
395 Some(Point2::new(0., 5.)),
396 polygon.intersection_transformed(&ray, &t)
397 );
398 let ray = Ray2::new(Point2::new(0., 10.), Vector2::new(0., 1.));
399 assert_eq!(None, polygon.intersection_transformed(&ray, &t));
400 let ray = Ray2::new(Point2::new(0., 7.2), Vector2::new(1., 0.));
401 assert_eq!(None, polygon.intersection_transformed(&ray, &t));
402 let ray = Ray2::new(Point2::new(0., 6.8), Vector2::new(1., 0.));
403 let p = polygon.intersection_transformed(&ray, &t).unwrap();
404 assert_ulps_eq!(2.8, p.x);
405 assert_ulps_eq!(6.8, p.y);
406 let t = transform(0., -2., 0.);
407 assert_eq!(None, polygon.intersection_transformed(&ray, &t));
408 let t = transform(0., 0., 0.3);
409 let p = polygon.intersection_transformed(&ray, &t).unwrap();
410 assert_ulps_eq!(0.38913357, p.x);
411 assert_ulps_eq!(6.8, p.y);
412 }
413
414 fn transform(dx: f32, dy: f32, rot: f32) -> Decomposed<Vector2<f32>, Basis2<f32>> {
415 Decomposed {
416 scale: 1.,
417 rot: Rotation2::from_angle(Rad(rot)),
418 disp: Vector2::new(dx, dy),
419 }
420 }
421}