tri-mesh 0.6.1

A triangle mesh data structure including basic operations.
Documentation
//! See [Mesh](crate::mesh::Mesh).

use crate::mesh::*;
use std::collections::HashSet;

/// # Connected components
impl Mesh {
    ///
    /// Finds the connected set of faces starting from the given face.
    ///
    pub fn connected_component(&self, start_face_id: FaceID) -> HashSet<FaceID> {
        self.connected_component_with_limit(start_face_id, &|_| false)
    }

    ///
    /// Finds all the sets of connected faces.
    ///
    pub fn connected_components(&self) -> Vec<HashSet<FaceID>> {
        self.connected_components_with_limit(&|_| false)
    }

    ///
    /// Finds the connected set of faces starting from the given face and limited by the given limit function.
    ///
    pub fn connected_component_with_limit(
        &self,
        start_face_id: FaceID,
        limit: &dyn Fn(HalfEdgeID) -> bool,
    ) -> HashSet<FaceID> {
        let mut component = HashSet::new();
        component.insert(start_face_id);
        let mut to_be_tested = vec![start_face_id];
        while let Some(test_face) = to_be_tested.pop() {
            for halfedge_id in self.face_halfedge_iter(test_face) {
                if !limit(halfedge_id) {
                    if let Some(face_id) =
                        self.walker_from_halfedge(halfedge_id).as_twin().face_id()
                    {
                        if !component.contains(&face_id) {
                            component.insert(face_id);
                            to_be_tested.push(face_id);
                        }
                    }
                }
            }
        }
        component
    }

    ///
    /// Finds all the sets of connected faces which are limited by the given limit function.
    ///
    pub fn connected_components_with_limit(
        &self,
        limit: &dyn Fn(HalfEdgeID) -> bool,
    ) -> Vec<HashSet<FaceID>> {
        let mut components: Vec<HashSet<FaceID>> = Vec::new();
        for face_id in self.face_iter() {
            if components
                .iter()
                .find(|com| com.contains(&face_id))
                .is_none()
            {
                components.push(self.connected_component_with_limit(face_id, limit));
            }
        }
        components
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use three_d_asset::{Indices, Positions, TriMesh};

    #[test]
    fn test_one_connected_component() {
        let mesh = create_connected_test_object();
        let cc = mesh.connected_component(mesh.face_iter().next().unwrap());
        assert_eq!(cc.len(), mesh.no_faces());
    }

    #[test]
    fn test_connected_components() {
        let mesh = create_unconnected_test_object();
        let cc = mesh.connected_components();

        assert_eq!(cc.len(), 3);

        assert_eq!(cc[0].len() + cc[1].len() + cc[2].len(), 15);
        assert!(cc.iter().find(|vec| vec.len() == 12).is_some());
        assert!(cc.iter().find(|vec| vec.len() == 2).is_some());
        assert!(cc.iter().find(|vec| vec.len() == 1).is_some());
    }

    fn create_connected_test_object() -> Mesh {
        TriMesh {
            positions: Positions::F64(vec![
                vec3(1.0, -1.0, -1.0),
                vec3(1.0, -1.0, 1.0),
                vec3(-1.0, -1.0, 1.0),
                vec3(-1.0, -1.0, -1.0),
                vec3(1.0, 1.0, -1.0),
                vec3(1.0, 1.0, 1.0),
                vec3(-1.0, 1.0, 1.0),
                vec3(-1.0, 1.0, -1.0),
            ]),
            indices: Indices::U8(vec![
                0, 1, 2, 0, 2, 3, 4, 7, 6, 4, 6, 5, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2,
                7, 3, 4, 0, 3, 4, 3, 7,
            ]),
            ..Default::default()
        }
        .into()
    }

    fn create_unconnected_test_object() -> Mesh {
        TriMesh {
            positions: Positions::F64(vec![
                vec3(1.0, -1.0, -1.0),
                vec3(1.0, -1.0, 1.0),
                vec3(-1.0, -1.0, 1.0),
                vec3(-1.0, -1.0, -1.0),
                vec3(1.0, 1.0, -1.0),
                vec3(1.0, 1.0, 1.0),
                vec3(-1.0, 1.0, 1.0),
                vec3(-1.0, 1.0, -1.0),
                vec3(-1.0, 2.0, -1.0),
                vec3(-1.0, 3.0, -1.0),
                vec3(-2.0, 4.0, -1.0),
                vec3(-2.0, 1.0, -1.0),
                vec3(-1.0, 3.0, -2.0),
                vec3(-2.0, 4.0, -3.0),
                vec3(-2.0, 1.0, -4.0),
            ]),
            indices: Indices::U8(vec![
                0, 1, 2, 0, 2, 3, 4, 7, 6, 4, 6, 5, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2,
                7, 3, 4, 0, 3, 4, 3, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14,
            ]),
            ..Default::default()
        }
        .into()
    }
}