mirror of
				https://github.com/hannobraun/Fornjot
				synced 2025-11-04 06:07:19 +00:00 
			
		
		
		
	Merge pull request #386 from hannobraun/triangulation
Consolidate triangulation code in `fj_kernel::algorithms::triangulation`
This commit is contained in:
		
						commit
						2ef0550feb
					
				
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -633,6 +633,7 @@ dependencies = [
 | 
				
			|||||||
 "fj",
 | 
					 "fj",
 | 
				
			||||||
 "fj-debug",
 | 
					 "fj-debug",
 | 
				
			||||||
 "fj-host",
 | 
					 "fj-host",
 | 
				
			||||||
 | 
					 "fj-kernel",
 | 
				
			||||||
 "fj-math",
 | 
					 "fj-math",
 | 
				
			||||||
 "fj-operations",
 | 
					 "fj-operations",
 | 
				
			||||||
 "futures",
 | 
					 "futures",
 | 
				
			||||||
 | 
				
			|||||||
@ -44,6 +44,10 @@ path    = "../fj-debug"
 | 
				
			|||||||
version = "0.5.0"
 | 
					version = "0.5.0"
 | 
				
			||||||
path    = "../fj-host"
 | 
					path    = "../fj-host"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies.fj-kernel]
 | 
				
			||||||
 | 
					version = "0.5.0"
 | 
				
			||||||
 | 
					path    = "../fj-kernel"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies.fj-math]
 | 
					[dependencies.fj-math]
 | 
				
			||||||
version = "0.5.0"
 | 
					version = "0.5.0"
 | 
				
			||||||
path    = "../fj-math"
 | 
					path    = "../fj-math"
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@ use std::{collections::HashMap, time::Instant};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use fj_debug::DebugInfo;
 | 
					use fj_debug::DebugInfo;
 | 
				
			||||||
use fj_host::Model;
 | 
					use fj_host::Model;
 | 
				
			||||||
 | 
					use fj_kernel::algorithms::triangulate;
 | 
				
			||||||
use fj_math::{Aabb, Scalar, Triangle};
 | 
					use fj_math::{Aabb, Scalar, Triangle};
 | 
				
			||||||
use fj_operations::ToShape as _;
 | 
					use fj_operations::ToShape as _;
 | 
				
			||||||
use futures::executor::block_on;
 | 
					use futures::executor::block_on;
 | 
				
			||||||
@ -296,10 +297,12 @@ impl ShapeProcessor {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        let mut debug_info = DebugInfo::new();
 | 
					        let mut debug_info = DebugInfo::new();
 | 
				
			||||||
        let mut triangles = Vec::new();
 | 
					        let mut triangles = Vec::new();
 | 
				
			||||||
        shape
 | 
					        triangulate(
 | 
				
			||||||
            .to_shape(tolerance, &mut debug_info)
 | 
					            shape.to_shape(tolerance, &mut debug_info),
 | 
				
			||||||
            .topology()
 | 
					            tolerance,
 | 
				
			||||||
            .triangles(tolerance, &mut triangles, &mut debug_info);
 | 
					            &mut triangles,
 | 
				
			||||||
 | 
					            &mut debug_info,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ProcessedShape {
 | 
					        ProcessedShape {
 | 
				
			||||||
            aabb,
 | 
					            aabb,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,166 @@
 | 
				
			|||||||
use fj_math::Scalar;
 | 
					use std::collections::BTreeSet;
 | 
				
			||||||
use parry2d_f64::utils::point_in_triangle::{corner_direction, Orientation};
 | 
					
 | 
				
			||||||
 | 
					use fj_debug::{DebugInfo, TriangleEdgeCheck};
 | 
				
			||||||
 | 
					use fj_math::{Aabb, Scalar, Segment, Triangle};
 | 
				
			||||||
 | 
					use parry2d_f64::{
 | 
				
			||||||
 | 
					    query::{Ray as Ray2, RayCast as _},
 | 
				
			||||||
 | 
					    utils::point_in_triangle::{corner_direction, Orientation},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use parry3d_f64::query::Ray as Ray3;
 | 
				
			||||||
use spade::HasPosition;
 | 
					use spade::HasPosition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::geometry;
 | 
					use crate::{geometry, shape::Shape, topology::Face};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::Approximation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Triangulate a shape
 | 
				
			||||||
 | 
					pub fn triangulate(
 | 
				
			||||||
 | 
					    mut shape: Shape,
 | 
				
			||||||
 | 
					    tolerance: Scalar,
 | 
				
			||||||
 | 
					    out: &mut Vec<Triangle<3>>,
 | 
				
			||||||
 | 
					    debug_info: &mut DebugInfo,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    for face in shape.topology().faces() {
 | 
				
			||||||
 | 
					        let face = face.get();
 | 
				
			||||||
 | 
					        match &*face {
 | 
				
			||||||
 | 
					            Face::Face { surface, color, .. } => {
 | 
				
			||||||
 | 
					                let surface = surface.get();
 | 
				
			||||||
 | 
					                let approx = Approximation::for_face(&face, tolerance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let points: Vec<_> = approx
 | 
				
			||||||
 | 
					                    .points
 | 
				
			||||||
 | 
					                    .into_iter()
 | 
				
			||||||
 | 
					                    .map(|vertex| {
 | 
				
			||||||
 | 
					                        // Can't panic, unless the approximation wrongfully
 | 
				
			||||||
 | 
					                        // generates points that are not in the surface.
 | 
				
			||||||
 | 
					                        surface.point_model_to_surface(vertex)
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let segments: Vec<_> = approx
 | 
				
			||||||
 | 
					                    .segments
 | 
				
			||||||
 | 
					                    .into_iter()
 | 
				
			||||||
 | 
					                    .map(|segment| {
 | 
				
			||||||
 | 
					                        let [a, b] = segment.points();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Can't panic, unless the approximation wrongfully
 | 
				
			||||||
 | 
					                        // generates points that are not in the surface.
 | 
				
			||||||
 | 
					                        let a = surface.point_model_to_surface(a);
 | 
				
			||||||
 | 
					                        let b = surface.point_model_to_surface(b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        [a, b]
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // We're also going to need a point outside of the polygon, for
 | 
				
			||||||
 | 
					                // the point-in-polygon tests.
 | 
				
			||||||
 | 
					                let aabb = Aabb::<2>::from_points(
 | 
				
			||||||
 | 
					                    points.iter().map(|vertex| vertex.native()),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                let outside = aabb.max * 2.;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let mut triangles = delaunay(points);
 | 
				
			||||||
 | 
					                let face_as_polygon = segments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                triangles.retain(|t| {
 | 
				
			||||||
 | 
					                    for segment in [t[0], t[1], t[2], t[0]].windows(2) {
 | 
				
			||||||
 | 
					                        // This can't panic, as we passed `2` to `windows`. It
 | 
				
			||||||
 | 
					                        // can be cleaned up a bit, once `array_windows` is
 | 
				
			||||||
 | 
					                        // stable.
 | 
				
			||||||
 | 
					                        let segment = [segment[0], segment[1]];
 | 
				
			||||||
 | 
					                        let inverted_segment = [segment[1], segment[0]];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // If the segment is an edge of the face, we don't need
 | 
				
			||||||
 | 
					                        // to take a closer look.
 | 
				
			||||||
 | 
					                        if face_as_polygon.contains(&segment) {
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if face_as_polygon.contains(&inverted_segment) {
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // To determine if the edge is within the polygon, we
 | 
				
			||||||
 | 
					                        // determine if its center point is in the polygon.
 | 
				
			||||||
 | 
					                        let center = segment[0]
 | 
				
			||||||
 | 
					                            + (segment[1] - segment[0]) / Scalar::TWO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        let origin = center;
 | 
				
			||||||
 | 
					                        let dir = outside - center;
 | 
				
			||||||
 | 
					                        let ray = Ray2 {
 | 
				
			||||||
 | 
					                            origin: origin.to_na(),
 | 
				
			||||||
 | 
					                            dir: dir.to_na(),
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        let mut check = TriangleEdgeCheck::new(Ray3 {
 | 
				
			||||||
 | 
					                            origin: surface
 | 
				
			||||||
 | 
					                                .point_surface_to_model(&origin)
 | 
				
			||||||
 | 
					                                .to_na(),
 | 
				
			||||||
 | 
					                            dir: surface.vector_surface_to_model(&dir).to_na(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // We need to keep track of where our ray hits the
 | 
				
			||||||
 | 
					                        // edges. Otherwise, if the ray hits a vertex, we might
 | 
				
			||||||
 | 
					                        // count that hit twice, as every vertex is attached to
 | 
				
			||||||
 | 
					                        // two edges.
 | 
				
			||||||
 | 
					                        let mut hits = BTreeSet::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Use ray-casting to determine if `center` is within
 | 
				
			||||||
 | 
					                        // the face-polygon.
 | 
				
			||||||
 | 
					                        for edge in &face_as_polygon {
 | 
				
			||||||
 | 
					                            // Please note that we if we get to this point, then
 | 
				
			||||||
 | 
					                            // the point is not on a polygon edge, due to the
 | 
				
			||||||
 | 
					                            // check above. We don't need to handle any edge
 | 
				
			||||||
 | 
					                            // cases that would arise from that case.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            let edge =
 | 
				
			||||||
 | 
					                                Segment::from(edge.map(|point| point.native()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            let intersection = edge
 | 
				
			||||||
 | 
					                                .to_parry()
 | 
				
			||||||
 | 
					                                .cast_local_ray(&ray, f64::INFINITY, true)
 | 
				
			||||||
 | 
					                                .map(Scalar::from_f64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if let Some(t) = intersection {
 | 
				
			||||||
 | 
					                                // Due to slight inaccuracies, we might get
 | 
				
			||||||
 | 
					                                // different values for the same intersections.
 | 
				
			||||||
 | 
					                                // Let's round `t` before using it.
 | 
				
			||||||
 | 
					                                let eps = 1_000_000.0;
 | 
				
			||||||
 | 
					                                let t = (t * eps).round() / eps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                if hits.insert(t) {
 | 
				
			||||||
 | 
					                                    check.hits.push(t.into_f64());
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        debug_info.triangle_edge_checks.push(check);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if hits.len() % 2 == 0 {
 | 
				
			||||||
 | 
					                            // The segment is outside of the face. This means we
 | 
				
			||||||
 | 
					                            // can throw away the whole triangle.
 | 
				
			||||||
 | 
					                            return false;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // If we didn't throw away the triangle up till now, this
 | 
				
			||||||
 | 
					                    // means all its edges are within the face.
 | 
				
			||||||
 | 
					                    true
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                out.extend(triangles.into_iter().map(|triangle| {
 | 
				
			||||||
 | 
					                    let [a, b, c] = triangle.map(|point| point.canonical());
 | 
				
			||||||
 | 
					                    let mut t = Triangle::from([a, b, c]);
 | 
				
			||||||
 | 
					                    t.set_color(*color);
 | 
				
			||||||
 | 
					                    t
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Face::Triangles(triangles) => out.extend(triangles),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Create a Delaunay triangulation of all points
 | 
					/// Create a Delaunay triangulation of all points
 | 
				
			||||||
pub fn triangulate(
 | 
					fn delaunay(points: Vec<geometry::Point<2>>) -> Vec<[geometry::Point<2>; 3]> {
 | 
				
			||||||
    points: Vec<geometry::Point<2>>,
 | 
					 | 
				
			||||||
) -> Vec<[geometry::Point<2>; 3]> {
 | 
					 | 
				
			||||||
    use spade::Triangulation as _;
 | 
					    use spade::Triangulation as _;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let triangulation = spade::DelaunayTriangulation::<_>::bulk_load(points)
 | 
					    let triangulation = spade::DelaunayTriangulation::<_>::bulk_load(points)
 | 
				
			||||||
@ -39,7 +192,7 @@ pub fn triangulate(
 | 
				
			|||||||
    triangles
 | 
					    triangles
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Enables the use of `SurfacePoint` in the triangulation.
 | 
					// Enables the use of `geometry::Point` in the triangulation.
 | 
				
			||||||
impl HasPosition for geometry::Point<2> {
 | 
					impl HasPosition for geometry::Point<2> {
 | 
				
			||||||
    type Scalar = Scalar;
 | 
					    type Scalar = Scalar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
use std::collections::HashSet;
 | 
					use std::collections::HashSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fj_debug::DebugInfo;
 | 
					use fj_math::{Point, Scalar, Vector};
 | 
				
			||||||
use fj_math::{Point, Scalar, Triangle, Vector};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    geometry::{Circle, Curve, Line},
 | 
					    geometry::{Circle, Curve, Line},
 | 
				
			||||||
@ -238,18 +237,6 @@ impl Topology<'_> {
 | 
				
			|||||||
    pub fn faces(&self) -> Iter<Face> {
 | 
					    pub fn faces(&self) -> Iter<Face> {
 | 
				
			||||||
        Iter::new(self.geometry.faces)
 | 
					        Iter::new(self.geometry.faces)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Triangulate the shape
 | 
					 | 
				
			||||||
    pub fn triangles(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        tolerance: Scalar,
 | 
					 | 
				
			||||||
        out: &mut Vec<Triangle<3>>,
 | 
					 | 
				
			||||||
        debug_info: &mut DebugInfo,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        for face in &*self.geometry.faces {
 | 
					 | 
				
			||||||
            face.get().triangles(tolerance, out, debug_info);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,8 @@
 | 
				
			|||||||
use std::{
 | 
					use std::hash::{Hash, Hasher};
 | 
				
			||||||
    collections::BTreeSet,
 | 
					 | 
				
			||||||
    hash::{Hash, Hasher},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fj_debug::{DebugInfo, TriangleEdgeCheck};
 | 
					use fj_math::Triangle;
 | 
				
			||||||
use fj_math::{Aabb, Scalar, Segment, Triangle};
 | 
					 | 
				
			||||||
use parry2d_f64::query::{Ray as Ray2, RayCast as _};
 | 
					 | 
				
			||||||
use parry3d_f64::query::Ray as Ray3;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{geometry::Surface, shape::Handle};
 | 
				
			||||||
    algorithms::{triangulate, Approximation},
 | 
					 | 
				
			||||||
    geometry::Surface,
 | 
					 | 
				
			||||||
    shape::Handle,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::edges::Cycle;
 | 
					use super::edges::Cycle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -89,149 +79,6 @@ impl Face {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Triangulate the face
 | 
					 | 
				
			||||||
    pub fn triangles(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        tolerance: Scalar,
 | 
					 | 
				
			||||||
        out: &mut Vec<Triangle<3>>,
 | 
					 | 
				
			||||||
        debug_info: &mut DebugInfo,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            Self::Face { surface, color, .. } => {
 | 
					 | 
				
			||||||
                let surface = surface.get();
 | 
					 | 
				
			||||||
                let approx = Approximation::for_face(self, tolerance);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let points: Vec<_> = approx
 | 
					 | 
				
			||||||
                    .points
 | 
					 | 
				
			||||||
                    .into_iter()
 | 
					 | 
				
			||||||
                    .map(|vertex| {
 | 
					 | 
				
			||||||
                        // Can't panic, unless the approximation wrongfully
 | 
					 | 
				
			||||||
                        // generates points that are not in the surface.
 | 
					 | 
				
			||||||
                        surface.point_model_to_surface(vertex)
 | 
					 | 
				
			||||||
                    })
 | 
					 | 
				
			||||||
                    .collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let segments: Vec<_> = approx
 | 
					 | 
				
			||||||
                    .segments
 | 
					 | 
				
			||||||
                    .into_iter()
 | 
					 | 
				
			||||||
                    .map(|segment| {
 | 
					 | 
				
			||||||
                        let [a, b] = segment.points();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // Can't panic, unless the approximation wrongfully
 | 
					 | 
				
			||||||
                        // generates points that are not in the surface.
 | 
					 | 
				
			||||||
                        let a = surface.point_model_to_surface(a);
 | 
					 | 
				
			||||||
                        let b = surface.point_model_to_surface(b);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        [a, b]
 | 
					 | 
				
			||||||
                    })
 | 
					 | 
				
			||||||
                    .collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // We're also going to need a point outside of the polygon, for
 | 
					 | 
				
			||||||
                // the point-in-polygon tests.
 | 
					 | 
				
			||||||
                let aabb = Aabb::<2>::from_points(
 | 
					 | 
				
			||||||
                    points.iter().map(|vertex| vertex.native()),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                let outside = aabb.max * 2.;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let mut triangles = triangulate(points);
 | 
					 | 
				
			||||||
                let face_as_polygon = segments;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                triangles.retain(|t| {
 | 
					 | 
				
			||||||
                    for segment in [t[0], t[1], t[2], t[0]].windows(2) {
 | 
					 | 
				
			||||||
                        // This can't panic, as we passed `2` to `windows`. It
 | 
					 | 
				
			||||||
                        // can be cleaned up a bit, once `array_windows` is
 | 
					 | 
				
			||||||
                        // stable.
 | 
					 | 
				
			||||||
                        let segment = [segment[0], segment[1]];
 | 
					 | 
				
			||||||
                        let inverted_segment = [segment[1], segment[0]];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // If the segment is an edge of the face, we don't need
 | 
					 | 
				
			||||||
                        // to take a closer look.
 | 
					 | 
				
			||||||
                        if face_as_polygon.contains(&segment) {
 | 
					 | 
				
			||||||
                            continue;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        if face_as_polygon.contains(&inverted_segment) {
 | 
					 | 
				
			||||||
                            continue;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // To determine if the edge is within the polygon, we
 | 
					 | 
				
			||||||
                        // determine if its center point is in the polygon.
 | 
					 | 
				
			||||||
                        let center = segment[0]
 | 
					 | 
				
			||||||
                            + (segment[1] - segment[0]) / Scalar::TWO;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        let origin = center;
 | 
					 | 
				
			||||||
                        let dir = outside - center;
 | 
					 | 
				
			||||||
                        let ray = Ray2 {
 | 
					 | 
				
			||||||
                            origin: origin.to_na(),
 | 
					 | 
				
			||||||
                            dir: dir.to_na(),
 | 
					 | 
				
			||||||
                        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        let mut check = TriangleEdgeCheck::new(Ray3 {
 | 
					 | 
				
			||||||
                            origin: surface
 | 
					 | 
				
			||||||
                                .point_surface_to_model(&origin)
 | 
					 | 
				
			||||||
                                .to_na(),
 | 
					 | 
				
			||||||
                            dir: surface.vector_surface_to_model(&dir).to_na(),
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // We need to keep track of where our ray hits the
 | 
					 | 
				
			||||||
                        // edges. Otherwise, if the ray hits a vertex, we might
 | 
					 | 
				
			||||||
                        // count that hit twice, as every vertex is attached to
 | 
					 | 
				
			||||||
                        // two edges.
 | 
					 | 
				
			||||||
                        let mut hits = BTreeSet::new();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // Use ray-casting to determine if `center` is within
 | 
					 | 
				
			||||||
                        // the face-polygon.
 | 
					 | 
				
			||||||
                        for edge in &face_as_polygon {
 | 
					 | 
				
			||||||
                            // Please note that we if we get to this point, then
 | 
					 | 
				
			||||||
                            // the point is not on a polygon edge, due to the
 | 
					 | 
				
			||||||
                            // check above. We don't need to handle any edge
 | 
					 | 
				
			||||||
                            // cases that would arise from that case.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            let edge =
 | 
					 | 
				
			||||||
                                Segment::from(edge.map(|point| point.native()));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            let intersection = edge
 | 
					 | 
				
			||||||
                                .to_parry()
 | 
					 | 
				
			||||||
                                .cast_local_ray(&ray, f64::INFINITY, true)
 | 
					 | 
				
			||||||
                                .map(Scalar::from_f64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            if let Some(t) = intersection {
 | 
					 | 
				
			||||||
                                // Due to slight inaccuracies, we might get
 | 
					 | 
				
			||||||
                                // different values for the same intersections.
 | 
					 | 
				
			||||||
                                // Let's round `t` before using it.
 | 
					 | 
				
			||||||
                                let eps = 1_000_000.0;
 | 
					 | 
				
			||||||
                                let t = (t * eps).round() / eps;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                if hits.insert(t) {
 | 
					 | 
				
			||||||
                                    check.hits.push(t.into_f64());
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        debug_info.triangle_edge_checks.push(check);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if hits.len() % 2 == 0 {
 | 
					 | 
				
			||||||
                            // The segment is outside of the face. This means we
 | 
					 | 
				
			||||||
                            // can throw away the whole triangle.
 | 
					 | 
				
			||||||
                            return false;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // If we didn't throw away the triangle up till now, this
 | 
					 | 
				
			||||||
                    // means all its edges are within the face.
 | 
					 | 
				
			||||||
                    true
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                out.extend(triangles.into_iter().map(|triangle| {
 | 
					 | 
				
			||||||
                    let [a, b, c] = triangle.map(|point| point.canonical());
 | 
					 | 
				
			||||||
                    let mut t = Triangle::from([a, b, c]);
 | 
					 | 
				
			||||||
                    t.set_color(*color);
 | 
					 | 
				
			||||||
                    t
 | 
					 | 
				
			||||||
                }));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Self::Triangles(triangles) => out.extend(triangles),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl PartialEq for Face {
 | 
					impl PartialEq for Face {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user