|
1 |
| -use super::Primitive2d; |
| 1 | +use super::{Primitive2d, WindingOrder}; |
2 | 2 | use crate::Vec2;
|
3 | 3 |
|
4 | 4 | /// A normalized vector pointing in a direction in 2D space
|
@@ -174,20 +174,40 @@ impl BoxedPolyline2d {
|
174 | 174 | }
|
175 | 175 |
|
176 | 176 | /// A triangle in 2D space
|
177 |
| -#[derive(Clone, Debug)] |
| 177 | +#[derive(Clone, Debug, PartialEq)] |
178 | 178 | pub struct Triangle2d {
|
179 | 179 | /// The vertices of the triangle
|
180 | 180 | pub vertices: [Vec2; 3],
|
181 | 181 | }
|
182 | 182 | impl Primitive2d for Triangle2d {}
|
183 | 183 |
|
184 | 184 | impl Triangle2d {
|
185 |
| - /// Create a new `Triangle2d` from `a`, `b`, and `c`, |
| 185 | + /// Create a new `Triangle2d` from points `a`, `b`, and `c` |
186 | 186 | pub fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
|
187 | 187 | Self {
|
188 | 188 | vertices: [a, b, c],
|
189 | 189 | }
|
190 | 190 | }
|
| 191 | + |
| 192 | + /// Get the [`WindingOrder`] of the triangle |
| 193 | + #[doc(alias = "orientation")] |
| 194 | + pub fn winding_order(&self) -> WindingOrder { |
| 195 | + let [a, b, c] = self.vertices; |
| 196 | + let area = (b - a).perp_dot(c - a); |
| 197 | + if area > f32::EPSILON { |
| 198 | + WindingOrder::CounterClockwise |
| 199 | + } else if area < -f32::EPSILON { |
| 200 | + WindingOrder::Clockwise |
| 201 | + } else { |
| 202 | + WindingOrder::Invalid |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + /// Reverse the [`WindingOrder`] of the triangle |
| 207 | + /// by swapping the second and third vertices |
| 208 | + pub fn reverse(&mut self) { |
| 209 | + self.vertices.swap(1, 2); |
| 210 | + } |
191 | 211 | }
|
192 | 212 |
|
193 | 213 | /// A rectangle primitive
|
@@ -298,3 +318,37 @@ impl RegularPolygon {
|
298 | 318 | }
|
299 | 319 | }
|
300 | 320 | }
|
| 321 | + |
| 322 | +#[cfg(test)] |
| 323 | +mod tests { |
| 324 | + use super::*; |
| 325 | + |
| 326 | + #[test] |
| 327 | + fn triangle_winding_order() { |
| 328 | + let mut cw_triangle = Triangle2d::new( |
| 329 | + Vec2::new(0.0, 2.0), |
| 330 | + Vec2::new(-0.5, -1.2), |
| 331 | + Vec2::new(-1.0, -1.0), |
| 332 | + ); |
| 333 | + assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise); |
| 334 | + |
| 335 | + let ccw_triangle = Triangle2d::new( |
| 336 | + Vec2::new(0.0, 2.0), |
| 337 | + Vec2::new(-1.0, -1.0), |
| 338 | + Vec2::new(-0.5, -1.2), |
| 339 | + ); |
| 340 | + assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise); |
| 341 | + |
| 342 | + // The clockwise triangle should be the same as the counterclockwise |
| 343 | + // triangle when reversed |
| 344 | + cw_triangle.reverse(); |
| 345 | + assert_eq!(cw_triangle, ccw_triangle); |
| 346 | + |
| 347 | + let invalid_triangle = Triangle2d::new( |
| 348 | + Vec2::new(0.0, 2.0), |
| 349 | + Vec2::new(0.0, -1.0), |
| 350 | + Vec2::new(0.0, -1.2), |
| 351 | + ); |
| 352 | + assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid); |
| 353 | + } |
| 354 | +} |
0 commit comments