polyhedron 0.1.3

A half edge and radial edge implementation.
Documentation
use std::ops::Neg;

use linear_isomorphic::InnerSpace;

use crate::data_container::StorageBuffer;
use crate::handles::edge_handle::EdgeHandle;
use crate::prelude::{half_edge_handle::HalfEdgeHandle, vert_handle::VertHandle};
use crate::topo_ids::HalfEdgeId;
use crate::{FaceData, FaceId, MeshData, MeshStorage, TopoFace, VertData, VertId};

/// Topological handle to a face.
pub struct FaceHandle<C: MeshStorage, const MANIFOLD: bool>
{
    pub(crate) mesh_data: *mut MeshData<C, MANIFOLD>,
    pub(crate) id: FaceId,
}

impl<C: MeshStorage, const MANIFOLD: bool> FaceHandle<C, MANIFOLD>
{
    /// Id of the handle.
    pub fn id(&self) -> FaceId { self.id }

    /// Handle to an arbitrary half edge in the face.
    pub fn half_edge(&self) -> HalfEdgeHandle<C, MANIFOLD>
    {
        unsafe {
            let half_edge_id = (*self.mesh_data).face_ref(self.id).half_edge;

            HalfEdgeHandle {
                mesh_data: self.mesh_data,
                id: half_edge_id,
            }
        }
    }

    /// Read the stored data for this face.
    pub fn read_data(&self) -> &FaceData<C>
    {
        unsafe {
            (*self.mesh_data)
                .geometry_data
                .face_data
                .read(self.id.to_index() as u64)
        }
    }

    /// Update the stored data for this face.
    pub fn update_data<F: FnMut(&mut FaceData<C>)>(&mut self, update: F)
    {
        unsafe {
            (*self.mesh_data)
                .geometry_data
                .face_data
                .update(self.id.to_index() as u64, update)
        }
    }

    /// Fast but naive method for the un-normalized normal of this face.
    pub fn naive_normal<S>(&self) -> VertData<C>
    where
        VertData<C>: linear_isomorphic::InnerSpace<S> + Neg<Output = VertData<C>>,
        S: linear_isomorphic::RealField,
    {
        use linear_isomorphic::VectorSpace;
        let e1: VertData<C> = self.half_edge().dir();
        let e2: VertData<C> = self.half_edge().face_prev().dir();

        return e2.cross(&e1);
    }

    /// Unit normal.
    pub fn normal<S>(&self) -> VertData<C>
    where
        VertData<C>: linear_isomorphic::InnerSpace<S> + Neg<Output = VertData<C>>,
        S: linear_isomorphic::RealField,
    {
        self.naive_normal().normalized()
    }

    /// Area of this face (assumes a triangle face).
    pub fn area<S>(&self) -> S
    where
        VertData<C>: linear_isomorphic::InnerSpace<S> + Neg<Output = VertData<C>>,
        S: linear_isomorphic::RealField,
    {
        self.naive_normal().norm()
    }

    /// Iterator over vertex handles to the vertices in the face.
    pub fn vertices(&self) -> impl Iterator<Item = VertHandle<C, MANIFOLD>>
    {
        self.half_edge().iter_face().map(|h| h.source())
    }

    /// Make a shallow copy of the handle.
    pub fn replicate(&self) -> Self
    {
        FaceHandle {
            mesh_data: self.mesh_data,
            id: self.id,
        }
    }

    /// Remove this face without cleaning remaining elements.
    pub fn unclean_delete(self)
    {
        unsafe {
            let hedge = self.half_edge();
            for h in hedge.iter_face()
            {
                (*self.mesh_data).half_edge_mut(h.id()).face = FaceId::default();
            }

            *(*self.mesh_data).face_mut(self.id) = TopoFace::default();
            (*self.mesh_data)
                .topology_data
                .available_face_ids
                .push(self.id);
        }
    }

    /// Delete this element, then remove any degenerate elements that touched
    /// it.
    pub fn delete(self)
    {
        unsafe {
            if !MANIFOLD
            {
                let edges: Vec<_> =
                    self.half_edge().iter_face().map(|h| h.edge().id).collect();
                let mesh_data = self.mesh_data;
                self.unclean_delete();

                for e in edges
                {
                    if (*mesh_data).edge_ref(e).id.is_void()
                    {
                        continue;
                    }

                    let edge_handle = EdgeHandle { mesh_data, id: e };
                    if edge_handle.half_edge().id().is_void()
                    {
                        edge_handle.delete();
                    }
                }
            }
            else
            {
                let half_edges: Vec<_> =
                    self.half_edge().iter_face().map(|h| h.id).collect();
                let mesh_data = self.mesh_data;
                self.unclean_delete();

                for h in half_edges
                {
                    if (*mesh_data).half_edge_ref(h).id.is_void()
                    {
                        continue;
                    }

                    let hedge_handle = HalfEdgeHandle { mesh_data, id: h };
                    if hedge_handle.iter_orbit().count() == 0
                    {
                        hedge_handle.delete();
                    }
                }
            }
        }
    }

    /// Split the face and connect it to the prescribed vertex.
    pub fn split_and_join_to_vertex(&self, vn: VertId) -> VertId
    {
        unsafe {
            let half_edges: Vec<_> =
                self.half_edge().iter_face().map(|h| h.id()).collect();

            let mut faces = Vec::new();
            faces.push(self.id());
            for _ in 1..half_edges.len()
            {
                faces.push((*self.mesh_data).reserve_face());
            }

            let valence = half_edges.len();
            let mut new_edges = vec![[HalfEdgeId::default(); 2]; valence];
            for (i, h) in half_edges.iter().enumerate()
            {
                let vid = (*self.mesh_data).half_edge_ref(*h).source;
                let data_index = if MANIFOLD
                {
                    h.to_index() as u64 / 2
                }
                else
                {
                    (*self.mesh_data).half_edge_ref(*h).edge.to_index() as u64
                };

                let data = (*self.mesh_data)
                    .geometry_data
                    .edge_data
                    .read(data_index)
                    .clone();
                let hn = (*self.mesh_data).add_edge(vid, vn, data);
                let p = (*self.mesh_data).half_edge_ref(hn).orbit_next;

                new_edges[(i + valence - 1) % valence][1] = hn;
                new_edges[i][0] = p;
            }

            for (i, [incoming, outgoing]) in new_edges.into_iter().enumerate()
            {
                let fids = [incoming, half_edges[i], outgoing];
                for i in 0..3
                {
                    let (hedge1, hedge2) = crate::get_two_mut(
                        &mut (*self.mesh_data).topology_data.topo_half_edges,
                        fids[i].to_index(),
                        fids[(i + 1) % 3].to_index(),
                    );
                    hedge1.attach_next_in_face(hedge2);
                }

                crate::attach_face(&mut (*self.mesh_data).topology_data, &fids, faces[i]);
            }

            vn
        }
    }

    /// Split this face allocating the new vertex to the prescribed data.
    pub fn split_and_fit(&self, data: VertData<C>) -> VertId
    {
        unsafe {
            let vn = (*self.mesh_data).add_vert(data);

            self.split_and_join_to_vertex(vn)
        }
    }

    /// Split this face by adding a new vertex in the barycenter then connecting
    /// it to the edge sof the input face.
    pub fn split<S>(&self) -> VertId
    where
        VertData<C>: linear_isomorphic::InnerSpace<S> + Neg<Output = VertData<C>>,
        S: linear_isomorphic::RealField,
    {
        let mut count = 1;
        let mut average = self.half_edge().source().read_data().clone();
        for v in self
            .half_edge()
            .iter_face()
            .skip(1)
            .map(|h| h.source().read_data().clone())
        {
            average = average + v;
            count += 1;
        }

        average = average * (S::from(1).unwrap() / S::from(count).unwrap());

        self.split_and_fit(average)
    }
}