Merge pull request #2434 from hannobraun/triangle

Allow degenerate triangles
This commit is contained in:
Hanno Braun 2024-07-30 18:42:29 +02:00 committed by GitHub
commit d1d730aed0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 55 additions and 55 deletions

View File

@ -47,20 +47,22 @@ pub fn triangulate(
let mut triangles = Vec::new();
for triangle in triangulation.inner_faces() {
let [v0, v1, v2] = triangle.vertices().map(|vertex| *vertex.data());
let triangle_winding = Triangle::<2>::from_points([
let triangle = Triangle::<2>::from_points([
v0.point_surface,
v1.point_surface,
v2.point_surface,
])
.expect("invalid triangle")
.winding();
]);
assert!(
triangle.is_valid(),
"Expecting triangles created by triangulation to be valid.",
);
let required_winding = match coord_handedness {
Handedness::LeftHanded => Winding::Cw,
Handedness::RightHanded => Winding::Ccw,
};
let triangle = if triangle_winding == required_winding {
let triangle = if triangle.winding() == required_winding {
[v0, v1, v2]
} else {
[v0, v2, v1]

View File

@ -42,7 +42,7 @@ impl Polygon {
}
pub fn contains_triangle(&self, triangle: impl Into<Triangle<2>>) -> bool {
let [a, b, c] = triangle.into().points();
let [a, b, c] = triangle.into().points;
let mut might_be_hole = true;

View File

@ -89,7 +89,7 @@ pub fn export_stl(
) -> Result<(), Error> {
let points = mesh
.triangles()
.map(|triangle| triangle.inner.points())
.map(|triangle| triangle.inner.points)
.collect::<Vec<_>>();
let vertices = points.iter().map(|points| {
@ -136,7 +136,7 @@ pub fn export_obj(
) -> Result<(), Error> {
for (cnt, t) in mesh.triangles().enumerate() {
// write each point of the triangle
for v in t.inner.points() {
for v in t.inner.points {
wavefront_rs::obj::writer::Writer { auto_newline: true }
.write(
&mut write,

View File

@ -80,7 +80,7 @@ impl Mesh<Point<3>> {
) {
let triangle = triangle.into();
for point in triangle.points() {
for point in triangle.points {
self.push_vertex(point);
}

View File

@ -99,7 +99,7 @@ impl<const D: usize> Line<D> {
// The triangle is valid only, if the three points are not on the
// same line.
Triangle::from_points([a, b, c]).is_ok()
Triangle::from_points([a, b, c]).is_valid()
};
if other_origin_is_not_on_self {

View File

@ -78,7 +78,7 @@ impl Transform {
/// Transform the given triangle
pub fn transform_triangle(&self, triangle: &Triangle<3>) -> Triangle<3> {
let [a, b, c] = &triangle.points();
let [a, b, c] = &triangle.points;
Triangle::from([
self.transform_point(a),
self.transform_point(b),

View File

@ -1,3 +1,4 @@
use approx::AbsDiffEq;
use parry3d_f64::query::{Ray, RayCast as _};
use crate::Vector;
@ -11,34 +12,35 @@ use super::{Point, Scalar};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub struct Triangle<const D: usize> {
points: [Point<D>; 3],
/// The points that make up the triangle
pub points: [Point<D>; 3],
}
impl<const D: usize> Triangle<D> {
/// Construct a triangle from three points
///
/// Returns an error, if the points don't form a triangle.
pub fn from_points(
points: [impl Into<Point<D>>; 3],
) -> Result<Self, NotATriangle<D>> {
pub fn from_points(points: [impl Into<Point<D>>; 3]) -> Self {
let points = points.map(Into::into);
let area = {
let [a, b, c] = points.map(Point::to_xyz);
(b - a).cross(&(c - a)).magnitude()
};
// A triangle is not valid if it doesn't span any area
if area != Scalar::from(0.0) {
Ok(Self { points })
} else {
Err(NotATriangle { points })
}
Self { points }
}
/// Access the triangle's points
pub fn points(&self) -> [Point<D>; 3] {
self.points
/// # Determine whether the triangle is valid
///
/// A triangle is valid, if it is not degenerate. In a degenerate triangle,
/// the three points do not form an actual triangle, but a line or even a
/// single point.
///
/// ## Implementation Note
///
/// Right now, this function computes the area of the triangle, and compares
/// it against [`Scalar`]'s default epsilon value. This might not be
/// flexible enough for all use cases.
///
/// Long-term, it might become necessary to add some way to override the
/// epsilon value used within this function.
pub fn is_valid(&self) -> bool {
let [a, b, c] = self.points;
let area = (b - a).outer(&(c - a)).magnitude();
area > Scalar::default_epsilon()
}
/// Normalize the triangle
@ -81,7 +83,7 @@ impl Triangle<2> {
impl Triangle<3> {
/// Convert the triangle to a Parry triangle
pub fn to_parry(self) -> parry3d_f64::shape::Triangle {
self.points().map(|vertex| vertex.to_na()).into()
self.points.map(|vertex| vertex.to_na()).into()
}
/// Cast a ray against the Triangle
@ -117,16 +119,10 @@ where
P: Into<Point<D>>,
{
fn from(points: [P; 3]) -> Self {
Self::from_points(points).expect("invalid triangle")
Self::from_points(points)
}
}
/// Returned by [`Triangle::from_points`], if the points don't form a triangle
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct NotATriangle<const D: usize> {
pub points: [Point<D>; 3],
}
/// Winding direction of a triangle.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum Winding {
@ -158,35 +154,37 @@ mod tests {
#[test]
fn valid_triangle_2d() {
let a = Point::from([0.0, 0.0]);
let b = Point::from([1.0, 1.0]);
let c = Point::from([1.0, 2.0]);
let _triangle = Triangle::from([a, b, c]);
let b = Point::from([1.0, 0.0]);
let c = Point::from([0.0, 1.0]);
assert!(Triangle::from_points([a, b, c]).is_valid());
}
#[test]
fn valid_triangle_3d() {
let a = Point::from([0.0, 0.0, 0.0]);
let b = Point::from([1.0, 1.0, 0.0]);
let c = Point::from([1.0, 2.0, 0.0]);
let _triangle = Triangle::from([a, b, c]);
let b = Point::from([0.0, 1.0, 0.0]);
let c = Point::from([1.0, 0.0, 0.0]);
assert!(Triangle::from_points([a, b, c]).is_valid());
}
#[test]
#[should_panic]
fn invalid_triangle_2d() {
let a = Point::from([0.0, 0.0]);
let b = Point::from([1.0, 1.0]);
let c = Point::from([2.0, 2.0]);
let _triangle = Triangle::from([a, b, c]);
let b = Point::from([1.0, 0.0]);
let c = Point::from([2.0, 0.0]);
assert!(!Triangle::from_points([a, b, c]).is_valid());
}
#[test]
#[should_panic]
fn invalid_triangle_3d() {
let a = Point::from([0.0, 0.0, 0.0]);
let b = Point::from([1.0, 1.0, 1.0]);
let c = Point::from([2.0, 2.0, 2.0]);
let _triangle = Triangle::from([a, b, c]);
let b = Point::from([1.0, 0.0, 0.0]);
let c = Point::from([2.0, 0.0, 0.0]);
assert!(!Triangle::from_points([a, b, c]).is_valid());
}
#[test]

View File

@ -29,7 +29,7 @@ impl From<&Mesh<fj_math::Point<3>>> for Vertices {
let mut m = Mesh::new();
for triangle in mesh.triangles() {
let [a, b, c] = triangle.inner.points();
let [a, b, c] = triangle.inner.points;
let normal = (b - a).cross(&(c - a)).normalize();
let color = triangle.color;