mesh-graph 0.6.0

Fast halfedge triangle mesh graph in pure Rust
Documentation
use tracing::instrument;

use crate::{FaceId, HalfedgeId, MeshGraph, VertexId, error_none, utils::unwrap_or_return};

impl MeshGraph {
    /// Return the halfedge or it's twin depending on which one is boundary, or `None` if both are not boundary.
    #[instrument(skip(self))]
    pub fn boundary_he(&self, he_id: HalfedgeId) -> Option<HalfedgeId> {
        let he = *self
            .halfedges
            .get(he_id)
            .or_else(error_none!("Halfedge not found"))?;

        if he.is_boundary() {
            return Some(he_id);
        }

        let twin_id = he.twin.or_else(error_none!("Twin missing"))?;

        let twin_he = self
            .halfedges
            .get(twin_id)
            .or_else(error_none!("Twin not found"))?;

        if twin_he.is_boundary() {
            return Some(twin_id);
        }

        None
    }

    /// Returns the vertex order of the boundary halfedge between two vertices.
    /// Useful when adding faces on boundaries.
    ///
    /// If the edge is not boundary, it always returns `[v_id2, v_id1]`.
    #[instrument(skip(self))]
    pub fn boundary_vertex_order(&mut self, v_id1: VertexId, v_id2: VertexId) -> [VertexId; 2] {
        let he_id = unwrap_or_return!(
            self.halfedge_from_to(v_id1, v_id2),
            "Couldn't find halfedge between {v_id1:?} and {v_id2:?}",
            [v_id1, v_id2]
        );

        // already checked that the halfedge exists in `halfedge_from_to()`
        let he = self.halfedges[he_id];

        if he.is_boundary() {
            [v_id1, v_id2]
        } else {
            [v_id2, v_id1]
        }
    }

    /// Returns the halfedge from the start vertex to the end vertex, if it exists. `None` otherwise.
    #[instrument(skip(self))]
    pub fn halfedge_from_to(
        &self,
        start_vertex_id: VertexId,
        end_vertex_id: VertexId,
    ) -> Option<HalfedgeId> {
        let out_hes = self
            .outgoing_halfedges
            .get(start_vertex_id)
            .or_else(error_none!("Start vertex not found"))?;

        out_hes.iter().copied().find(|he_id| {
            self.halfedges
                .get(*he_id)
                .is_some_and(|he| he.end_vertex == end_vertex_id)
        })
    }

    /// Returns the face with the given vertices, if it exists. `None` otherwise.
    pub fn face_with_vertices(
        &self,
        v_id1: VertexId,
        v_id2: VertexId,
        v_id3: VertexId,
    ) -> Option<FaceId> {
        let he_id = self.halfedge_from_to(v_id1, v_id2)?;
        let he = self
            .halfedges
            .get(he_id)
            .or_else(error_none!("Halfedge not found"))?;

        if Some(v_id3) == he.opposite_vertex(self) {
            return he.face.or_else(error_none!("No face"));
        }

        let twin_id = he.twin.or_else(error_none!("No twin"))?;
        let twin = self
            .halfedges
            .get(twin_id)
            .or_else(error_none!("Twin not found"))?;

        if Some(v_id3) == twin.opposite_vertex(self) {
            return twin.face.or_else(error_none!("No face"));
        }

        None
    }
}