1 | use crate::scalar::Scalar; |
2 | use crate::traits::Transformation; |
3 | use crate::LineSegment; |
4 | use crate::{point, Box2D, Point}; |
5 | |
6 | /// A 2D triangle defined by three points `a`, `b` and `c`. |
7 | #[derive (Copy, Clone, Debug, PartialEq)] |
8 | #[cfg_attr (feature = "serialization" , derive(Serialize, Deserialize))] |
9 | pub struct Triangle<S> { |
10 | pub a: Point<S>, |
11 | pub b: Point<S>, |
12 | pub c: Point<S>, |
13 | } |
14 | |
15 | impl<S: Scalar> Triangle<S> { |
16 | #[inline ] |
17 | fn get_barycentric_coords_for_point(&self, point: Point<S>) -> (S, S, S) { |
18 | let v0 = self.b - self.a; |
19 | let v1 = self.c - self.a; |
20 | let v2 = point - self.a; |
21 | let inv = S::ONE / v0.cross(v1); |
22 | let a = v0.cross(v2) * inv; |
23 | let b = v2.cross(v1) * inv; |
24 | let c = S::ONE - a - b; |
25 | |
26 | (a, b, c) |
27 | } |
28 | |
29 | pub fn contains_point(&self, point: Point<S>) -> bool { |
30 | let coords = self.get_barycentric_coords_for_point(point); |
31 | |
32 | coords.0 > S::ZERO && coords.1 > S::ZERO && coords.2 > S::ZERO |
33 | } |
34 | |
35 | /// Returns a conservative range of x that contains this triangle. |
36 | #[inline ] |
37 | pub fn bounding_range_x(&self) -> (S, S) { |
38 | let min_x = self.a.x.min(self.b.x).min(self.c.x); |
39 | let max_x = self.a.x.max(self.b.x).max(self.c.x); |
40 | |
41 | (min_x, max_x) |
42 | } |
43 | |
44 | /// Returns a conservative range of y that contains this triangle. |
45 | #[inline ] |
46 | pub fn bounding_range_y(&self) -> (S, S) { |
47 | let min_y = self.a.y.min(self.b.y).min(self.c.y); |
48 | let max_y = self.a.y.max(self.b.y).max(self.c.y); |
49 | |
50 | (min_y, max_y) |
51 | } |
52 | |
53 | /// Returns the smallest rectangle that contains this triangle. |
54 | #[inline ] |
55 | pub fn bounding_box(&self) -> Box2D<S> { |
56 | let (min_x, max_x) = self.bounding_range_x(); |
57 | let (min_y, max_y) = self.bounding_range_y(); |
58 | |
59 | Box2D { |
60 | min: point(min_x, min_y), |
61 | max: point(max_x, max_y), |
62 | } |
63 | } |
64 | |
65 | #[inline ] |
66 | pub fn ab(&self) -> LineSegment<S> { |
67 | LineSegment { |
68 | from: self.a, |
69 | to: self.b, |
70 | } |
71 | } |
72 | |
73 | #[inline ] |
74 | pub fn ba(&self) -> LineSegment<S> { |
75 | LineSegment { |
76 | from: self.b, |
77 | to: self.a, |
78 | } |
79 | } |
80 | |
81 | #[inline ] |
82 | pub fn bc(&self) -> LineSegment<S> { |
83 | LineSegment { |
84 | from: self.b, |
85 | to: self.c, |
86 | } |
87 | } |
88 | |
89 | #[inline ] |
90 | pub fn cb(&self) -> LineSegment<S> { |
91 | LineSegment { |
92 | from: self.c, |
93 | to: self.b, |
94 | } |
95 | } |
96 | |
97 | #[inline ] |
98 | pub fn ca(&self) -> LineSegment<S> { |
99 | LineSegment { |
100 | from: self.c, |
101 | to: self.a, |
102 | } |
103 | } |
104 | |
105 | #[inline ] |
106 | pub fn ac(&self) -> LineSegment<S> { |
107 | LineSegment { |
108 | from: self.a, |
109 | to: self.c, |
110 | } |
111 | } |
112 | |
113 | /// [Not implemented] Applies the transform to this triangle and returns the results. |
114 | #[inline ] |
115 | pub fn transform<T: Transformation<S>>(&self, transform: &T) -> Self { |
116 | Triangle { |
117 | a: transform.transform_point(self.a), |
118 | b: transform.transform_point(self.b), |
119 | c: transform.transform_point(self.c), |
120 | } |
121 | } |
122 | |
123 | /// Test for triangle-triangle intersection. |
124 | pub fn intersects(&self, other: &Self) -> bool { |
125 | // TODO: This should be optimized. |
126 | // A bounding rect check should speed this up dramatically. |
127 | // Inlining and reusing intermediate computation of the intersections |
128 | // functions below and using SIMD would help too. |
129 | self.ab().intersects(&other.ab()) |
130 | || self.ab().intersects(&other.bc()) |
131 | || self.ab().intersects(&other.ac()) |
132 | || self.bc().intersects(&other.ab()) |
133 | || self.bc().intersects(&other.bc()) |
134 | || self.bc().intersects(&other.ac()) |
135 | || self.ac().intersects(&other.ab()) |
136 | || self.ac().intersects(&other.bc()) |
137 | || self.ac().intersects(&other.ac()) |
138 | || self.contains_point(other.a) |
139 | || other.contains_point(self.a) |
140 | || *self == *other |
141 | } |
142 | |
143 | /// Test for triangle-segment intersection. |
144 | #[inline ] |
145 | pub fn intersects_line_segment(&self, segment: &LineSegment<S>) -> bool { |
146 | self.ab().intersects(segment) |
147 | || self.bc().intersects(segment) |
148 | || self.ac().intersects(segment) |
149 | || self.contains_point(segment.from) |
150 | } |
151 | } |
152 | |
153 | #[test ] |
154 | fn test_triangle_contains() { |
155 | assert!(Triangle { |
156 | a: point(0.0, 0.0), |
157 | b: point(1.0, 0.0), |
158 | c: point(0.0, 1.0), |
159 | } |
160 | .contains_point(point(0.2, 0.2))); |
161 | assert!(!Triangle { |
162 | a: point(0.0, 0.0), |
163 | b: point(1.0, 0.0), |
164 | c: point(0.0, 1.0), |
165 | } |
166 | .contains_point(point(1.2, 0.2))); |
167 | |
168 | // Triangle vertex winding should not matter |
169 | assert!(Triangle { |
170 | a: point(1.0, 0.0), |
171 | b: point(0.0, 0.0), |
172 | c: point(0.0, 1.0), |
173 | } |
174 | .contains_point(point(0.2, 0.2))); |
175 | |
176 | // Point exactly on the edge counts as outside the triangle. |
177 | assert!(!Triangle { |
178 | a: point(0.0, 0.0), |
179 | b: point(1.0, 0.0), |
180 | c: point(0.0, 1.0), |
181 | } |
182 | .contains_point(point(0.0, 0.0))); |
183 | } |
184 | |
185 | #[test ] |
186 | fn test_segments() { |
187 | let t: Triangle = Triangle { |
188 | a: point(x:1.0, y:2.0), |
189 | b: point(x:3.0, y:4.0), |
190 | c: point(x:5.0, y:6.0), |
191 | }; |
192 | |
193 | assert_eq!(t.ab(), t.ba().flip()); |
194 | assert_eq!(t.ac(), t.ca().flip()); |
195 | assert_eq!(t.bc(), t.cb().flip()); |
196 | } |
197 | |
198 | #[test ] |
199 | fn test_triangle_intersections() { |
200 | let t1 = Triangle { |
201 | a: point(1.0, 1.0), |
202 | b: point(6.0, 1.0), |
203 | c: point(3.0, 6.0), |
204 | }; |
205 | |
206 | let t2 = Triangle { |
207 | a: point(2.0, 2.0), |
208 | b: point(0.0, 3.0), |
209 | c: point(1.0, 6.0), |
210 | }; |
211 | |
212 | assert!(t1.intersects(&t2)); |
213 | assert!(t2.intersects(&t1)); |
214 | |
215 | // t3 and t1 have an overlapping edge, they are "touching" but not intersecting. |
216 | let t3 = Triangle { |
217 | a: point(6.0, 5.0), |
218 | b: point(6.0, 1.0), |
219 | c: point(3.0, 6.0), |
220 | }; |
221 | |
222 | assert!(!t1.intersects(&t3)); |
223 | assert!(!t3.intersects(&t1)); |
224 | |
225 | // t4 is entirely inside t1. |
226 | let t4 = Triangle { |
227 | a: point(2.0, 2.0), |
228 | b: point(5.0, 2.0), |
229 | c: point(3.0, 4.0), |
230 | }; |
231 | |
232 | assert!(t1.intersects(&t4)); |
233 | assert!(t4.intersects(&t1)); |
234 | |
235 | // Triangles intersect themselves. |
236 | assert!(t1.intersects(&t1)); |
237 | assert!(t2.intersects(&t2)); |
238 | assert!(t3.intersects(&t3)); |
239 | assert!(t4.intersects(&t4)); |
240 | } |
241 | |
242 | #[test ] |
243 | fn test_segment_intersection() { |
244 | let tri = Triangle { |
245 | a: point(1.0, 1.0), |
246 | b: point(6.0, 1.0), |
247 | c: point(3.0, 6.0), |
248 | }; |
249 | |
250 | let l1 = LineSegment { |
251 | from: point(2.0, 0.0), |
252 | to: point(3.0, 4.0), |
253 | }; |
254 | |
255 | assert!(tri.intersects_line_segment(&l1)); |
256 | |
257 | let l2 = LineSegment { |
258 | from: point(1.0, 3.0), |
259 | to: point(0.0, 4.0), |
260 | }; |
261 | |
262 | assert!(!tri.intersects_line_segment(&l2)); |
263 | |
264 | // The segment is entirely inside the triangle. |
265 | let inside = LineSegment { |
266 | from: point(2.0, 2.0), |
267 | to: point(5.0, 2.0), |
268 | }; |
269 | |
270 | assert!(tri.intersects_line_segment(&inside)); |
271 | |
272 | // A triangle does not intersect its own segments. |
273 | assert!(!tri.intersects_line_segment(&tri.ab())); |
274 | assert!(!tri.intersects_line_segment(&tri.bc())); |
275 | assert!(!tri.intersects_line_segment(&tri.ac())); |
276 | } |
277 | |
278 | #[test ] |
279 | fn test_bounding_box() { |
280 | let t1 = Triangle { |
281 | a: point(10.0, 20.0), |
282 | b: point(35.0, 40.0), |
283 | c: point(50.0, 10.0), |
284 | }; |
285 | let r1 = Box2D { |
286 | min: point(10.0, 10.0), |
287 | max: point(50.0, 40.0), |
288 | }; |
289 | |
290 | let t2 = Triangle { |
291 | a: point(5.0, 30.0), |
292 | b: point(25.0, 10.0), |
293 | c: point(35.0, 40.0), |
294 | }; |
295 | let r2 = Box2D { |
296 | min: point(5.0, 10.0), |
297 | max: point(35.0, 40.0), |
298 | }; |
299 | |
300 | let t3 = Triangle { |
301 | a: point(1.0, 1.0), |
302 | b: point(2.0, 5.0), |
303 | c: point(0.0, 4.0), |
304 | }; |
305 | let r3 = Box2D { |
306 | min: point(0.0, 1.0), |
307 | max: point(2.0, 5.0), |
308 | }; |
309 | |
310 | let cases = std::vec![(t1, r1), (t2, r2), (t3, r3)]; |
311 | for &(tri, r) in &cases { |
312 | assert_eq!(tri.bounding_box(), r); |
313 | } |
314 | } |
315 | |