mirror of
https://github.com/hannobraun/Fornjot
synced 2025-07-22 18:06:05 +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();
|
let mut triangles = Vec::new();
|
||||||
for triangle in triangulation.inner_faces() {
|
for triangle in triangulation.inner_faces() {
|
||||||
let [v0, v1, v2] = triangle.vertices().map(|vertex| *vertex.data());
|
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,
|
v0.point_surface,
|
||||||
v1.point_surface,
|
v1.point_surface,
|
||||||
v2.point_surface,
|
v2.point_surface,
|
||||||
])
|
]);
|
||||||
.expect("invalid triangle")
|
assert!(
|
||||||
.winding();
|
triangle.is_valid(),
|
||||||
|
"Expecting triangles created by triangulation to be valid.",
|
||||||
|
);
|
||||||
|
|
||||||
let required_winding = match coord_handedness {
|
let required_winding = match coord_handedness {
|
||||||
Handedness::LeftHanded => Winding::Cw,
|
Handedness::LeftHanded => Winding::Cw,
|
||||||
Handedness::RightHanded => Winding::Ccw,
|
Handedness::RightHanded => Winding::Ccw,
|
||||||
};
|
};
|
||||||
|
|
||||||
let triangle = if triangle_winding == required_winding {
|
let triangle = if triangle.winding() == required_winding {
|
||||||
[v0, v1, v2]
|
[v0, v1, v2]
|
||||||
} else {
|
} else {
|
||||||
[v0, v2, v1]
|
[v0, v2, v1]
|
||||||
|
@ -42,7 +42,7 @@ impl Polygon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_triangle(&self, triangle: impl Into<Triangle<2>>) -> bool {
|
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;
|
let mut might_be_hole = true;
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ pub fn export_stl(
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let points = mesh
|
let points = mesh
|
||||||
.triangles()
|
.triangles()
|
||||||
.map(|triangle| triangle.inner.points())
|
.map(|triangle| triangle.inner.points)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let vertices = points.iter().map(|points| {
|
let vertices = points.iter().map(|points| {
|
||||||
@ -136,7 +136,7 @@ pub fn export_obj(
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for (cnt, t) in mesh.triangles().enumerate() {
|
for (cnt, t) in mesh.triangles().enumerate() {
|
||||||
// write each point of the triangle
|
// 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 }
|
wavefront_rs::obj::writer::Writer { auto_newline: true }
|
||||||
.write(
|
.write(
|
||||||
&mut write,
|
&mut write,
|
||||||
|
@ -80,7 +80,7 @@ impl Mesh<Point<3>> {
|
|||||||
) {
|
) {
|
||||||
let triangle = triangle.into();
|
let triangle = triangle.into();
|
||||||
|
|
||||||
for point in triangle.points() {
|
for point in triangle.points {
|
||||||
self.push_vertex(point);
|
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
|
// The triangle is valid only, if the three points are not on the
|
||||||
// same line.
|
// 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 {
|
if other_origin_is_not_on_self {
|
||||||
|
@ -78,7 +78,7 @@ impl Transform {
|
|||||||
|
|
||||||
/// Transform the given triangle
|
/// Transform the given triangle
|
||||||
pub fn transform_triangle(&self, triangle: &Triangle<3>) -> Triangle<3> {
|
pub fn transform_triangle(&self, triangle: &Triangle<3>) -> Triangle<3> {
|
||||||
let [a, b, c] = &triangle.points();
|
let [a, b, c] = &triangle.points;
|
||||||
Triangle::from([
|
Triangle::from([
|
||||||
self.transform_point(a),
|
self.transform_point(a),
|
||||||
self.transform_point(b),
|
self.transform_point(b),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use approx::AbsDiffEq;
|
||||||
use parry3d_f64::query::{Ray, RayCast as _};
|
use parry3d_f64::query::{Ray, RayCast as _};
|
||||||
|
|
||||||
use crate::Vector;
|
use crate::Vector;
|
||||||
@ -11,34 +12,35 @@ use super::{Point, Scalar};
|
|||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Triangle<const D: usize> {
|
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> {
|
impl<const D: usize> Triangle<D> {
|
||||||
/// Construct a triangle from three points
|
/// Construct a triangle from three points
|
||||||
///
|
pub fn from_points(points: [impl Into<Point<D>>; 3]) -> Self {
|
||||||
/// 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>> {
|
|
||||||
let points = points.map(Into::into);
|
let points = points.map(Into::into);
|
||||||
|
Self { points }
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the triangle's points
|
/// # Determine whether the triangle is valid
|
||||||
pub fn points(&self) -> [Point<D>; 3] {
|
///
|
||||||
self.points
|
/// 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
|
/// Normalize the triangle
|
||||||
@ -81,7 +83,7 @@ impl Triangle<2> {
|
|||||||
impl Triangle<3> {
|
impl Triangle<3> {
|
||||||
/// Convert the triangle to a Parry triangle
|
/// Convert the triangle to a Parry triangle
|
||||||
pub fn to_parry(self) -> parry3d_f64::shape::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
|
/// Cast a ray against the Triangle
|
||||||
@ -117,16 +119,10 @@ where
|
|||||||
P: Into<Point<D>>,
|
P: Into<Point<D>>,
|
||||||
{
|
{
|
||||||
fn from(points: [P; 3]) -> Self {
|
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.
|
/// Winding direction of a triangle.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub enum Winding {
|
pub enum Winding {
|
||||||
@ -158,35 +154,37 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn valid_triangle_2d() {
|
fn valid_triangle_2d() {
|
||||||
let a = Point::from([0.0, 0.0]);
|
let a = Point::from([0.0, 0.0]);
|
||||||
let b = Point::from([1.0, 1.0]);
|
let b = Point::from([1.0, 0.0]);
|
||||||
let c = Point::from([1.0, 2.0]);
|
let c = Point::from([0.0, 1.0]);
|
||||||
let _triangle = Triangle::from([a, b, c]);
|
|
||||||
|
assert!(Triangle::from_points([a, b, c]).is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_triangle_3d() {
|
fn valid_triangle_3d() {
|
||||||
let a = Point::from([0.0, 0.0, 0.0]);
|
let a = Point::from([0.0, 0.0, 0.0]);
|
||||||
let b = Point::from([1.0, 1.0, 0.0]);
|
let b = Point::from([0.0, 1.0, 0.0]);
|
||||||
let c = Point::from([1.0, 2.0, 0.0]);
|
let c = Point::from([1.0, 0.0, 0.0]);
|
||||||
let _triangle = Triangle::from([a, b, c]);
|
|
||||||
|
assert!(Triangle::from_points([a, b, c]).is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn invalid_triangle_2d() {
|
fn invalid_triangle_2d() {
|
||||||
let a = Point::from([0.0, 0.0]);
|
let a = Point::from([0.0, 0.0]);
|
||||||
let b = Point::from([1.0, 1.0]);
|
let b = Point::from([1.0, 0.0]);
|
||||||
let c = Point::from([2.0, 2.0]);
|
let c = Point::from([2.0, 0.0]);
|
||||||
let _triangle = Triangle::from([a, b, c]);
|
|
||||||
|
assert!(!Triangle::from_points([a, b, c]).is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn invalid_triangle_3d() {
|
fn invalid_triangle_3d() {
|
||||||
let a = Point::from([0.0, 0.0, 0.0]);
|
let a = Point::from([0.0, 0.0, 0.0]);
|
||||||
let b = Point::from([1.0, 1.0, 1.0]);
|
let b = Point::from([1.0, 0.0, 0.0]);
|
||||||
let c = Point::from([2.0, 2.0, 2.0]);
|
let c = Point::from([2.0, 0.0, 0.0]);
|
||||||
let _triangle = Triangle::from([a, b, c]);
|
|
||||||
|
assert!(!Triangle::from_points([a, b, c]).is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -29,7 +29,7 @@ impl From<&Mesh<fj_math::Point<3>>> for Vertices {
|
|||||||
let mut m = Mesh::new();
|
let mut m = Mesh::new();
|
||||||
|
|
||||||
for triangle in mesh.triangles() {
|
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 normal = (b - a).cross(&(c - a)).normalize();
|
||||||
let color = triangle.color;
|
let color = triangle.color;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user