mirror of
https://github.com/hannobraun/Fornjot
synced 2025-07-16 15:06:06 +00:00
Merge pull request #2434 from hannobraun/triangle
Allow degenerate triangles
This commit is contained in:
commit
d1d730aed0
@ -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]
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user