Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add winding order for Triangle2d #10620

Merged
merged 6 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions crates/bevy_math/src/primitives/dim2.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::Primitive2d;
use super::{Primitive2d, WindingOrder};
use crate::Vec2;

/// A normalized vector pointing in a direction in 2D space
Expand Down Expand Up @@ -174,20 +174,40 @@ impl BoxedPolyline2d {
}

/// A triangle in 2D space
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Triangle2d {
/// The vertices of the triangle
pub vertices: [Vec2; 3],
}
impl Primitive2d for Triangle2d {}

impl Triangle2d {
/// Create a new `Triangle2d` from `a`, `b`, and `c`,
/// Create a new `Triangle2d` from points `a`, `b`, and `c`
pub fn new(a: Vec2, b: Vec2, c: Vec2) -> Self {
Self {
vertices: [a, b, c],
}
}

/// Get the [`WindingOrder`] of the triangle
#[doc(alias = "orientation")]
pub fn winding_order(&self) -> WindingOrder {
let [a, b, c] = self.vertices;
let area = (b - a).perp_dot(c - a);
if area > f32::EPSILON {
WindingOrder::CounterClockwise
} else if area < -f32::EPSILON {
WindingOrder::Clockwise
} else {
WindingOrder::Invalid
}
}

/// Reverse the [`WindingOrder`] of the triangle
/// by swapping the second and third vertices
pub fn reverse(&mut self) {
self.vertices.swap(1, 2);
}
}

/// A rectangle primitive
Expand Down Expand Up @@ -298,3 +318,37 @@ impl RegularPolygon {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn triangle_winding_order() {
let mut cw_triangle = Triangle2d::new(
Vec2::new(0.0, 2.0),
Vec2::new(-0.5, -1.2),
Vec2::new(-1.0, -1.0),
);
assert_eq!(cw_triangle.winding_order(), WindingOrder::Clockwise);

let ccw_triangle = Triangle2d::new(
Vec2::new(0.0, 2.0),
Vec2::new(-1.0, -1.0),
Vec2::new(-0.5, -1.2),
);
assert_eq!(ccw_triangle.winding_order(), WindingOrder::CounterClockwise);

// The clockwise triangle should be the same as the counterclockwise
// triangle when reversed
cw_triangle.reverse();
assert_eq!(cw_triangle, ccw_triangle);

let invalid_triangle = Triangle2d::new(
Vec2::new(0.0, 2.0),
Vec2::new(0.0, -1.0),
Vec2::new(0.0, -1.2),
);
assert_eq!(invalid_triangle.winding_order(), WindingOrder::Invalid);
}
}
13 changes: 13 additions & 0 deletions crates/bevy_math/src/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@ pub trait Primitive2d {}

/// A marker trait for 3D primitives
pub trait Primitive3d {}

/// The winding order for a set of points
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WindingOrder {
/// A clockwise winding order
Clockwise,
/// A counterclockwise winding order
CounterClockwise,
/// An invalid winding order indicating that it could not be computed reliably.
/// This often happens in *degenerate cases* where the points lie on the same line
#[doc(alias = "Degenerate")]
Invalid,
}