polyhedra 0.1.0

Polyhedra is a small library for creating polyhedra.
Documentation
use core::fmt::Debug;
use petgraph;
use petgraph::graph::EdgeIndex;

use crate::geometry::*;
use crate::topology::{Boundary, Tiling};

#[derive(Debug)]
/// Represents an arbitrary polyhedron.
/// It provides access to information about vertices, edges and faces,
/// their relation to each other and their positions in 3D space.
pub struct Polyhedron {
    pub(crate) graph: VertexGraph,
    pub(crate) faces: Vec<Face>,
}

impl Polyhedron {
    /// Returns an iterator over the `Face`s of this polyhedron.
    pub fn faces(&self) -> impl IntoIterator<Item = &Face> {
        &self.faces
    }

    /// Returns an iterator over the VertexHandles of this polyhedron.
    pub fn vertices<'a>(&'a self) -> impl IntoIterator<Item = VertexHandle> + 'a {
        self.graph.node_indices().map(VertexHandle::new)
    }

    /// Returns the faces that share an edge with the given face.
    pub fn adjacent_faces(&self, face: &Face) -> Vec<&Face> {
        let mut faces = Vec::new();
        for (node_a, node_b) in face.edges() {
            let edge = self.graph.find_edge(node_a.ix, node_b.ix);
            let edge = edge.expect("Illegal edge access");
            let (face_a, face_b) = self.edge_adjacent_faces(edge);
            if face_a == face {
                faces.push(face_b);
            } else {
                faces.push(face_a);
            }
        }
        faces
    }

    pub(crate) fn edge_adjacent_faces(&self, edge: EdgeIndex) -> (&Face, &Face) {
        let (node_a, node_b) = self
            .graph
            .edge_endpoints(edge)
            .expect("Illegal edge access");

        let mut faces = self
            .faces
            .iter()
            .filter(|face| {
                face.nodes.contains(&VertexHandle::new(node_a))
                    && face.nodes.contains(&VertexHandle::new(node_b))
            })
            .collect::<Vec<&Face>>();

        match faces.len() {
            2 => (
                faces.pop().expect("Illegal Face removal"),
                faces.pop().expect("Illegal Face removal"),
            ),
            _ => panic!("Illegal number of faces {}", faces.len()),
        }
    }

    pub(crate) fn assert_consistency(&self) -> Result<(), String> {
        for face in self.faces() {
            for (v1, v2) in face.edges() {
                if self.graph.find_edge_undirected(v1.ix, v2.ix).is_none() {
                    return Err(format!(
                        "Found invalid edge present in face, \
                         but not in graph: ({:?}, {:?}) in in polyhedron {:?}",
                        v1, v2, self
                    ));
                }
            }
        }
        Ok(())
    }
}

impl Into<VertexGraph> for Polyhedron {
    fn into(self) -> VertexGraph {
        self.graph
    }
}

impl Tiling<Face, VertexHandle, Point> for Polyhedron {
    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &Face> + 'a> {
        Box::new(self.faces.iter())
    }

    fn adjacent(&self, node: &Face) -> Vec<(Boundary<VertexHandle>, Option<&Face>)> {
        node.edges()
            .into_iter()
            .map(|(node_a, node_b)| {
                let edge = self.graph.find_edge_undirected(node_a.ix, node_b.ix);
                let face_r = {
                    match edge {
                        Some((edge, _dir)) => {
                            let (face_a, face_b) = self.edge_adjacent_faces(edge);
                            let face = if node == face_a { face_b } else { face_a };
                            Some(face)
                        }
                        None => None,
                    }
                };

                debug_assert_ne!(
                    edge, None,
                    "Found invalid edge ({:?},{:?}) in polyhedron {:?} ",
                    node_a, node_b, self
                );
                debug_assert_ne!(face_r, None, "Found invalid face in polyhedron");

                let boundary = Boundary(node_a, node_b);

                (boundary, face_r)
            })
            .collect()
    }

    fn vertex_position(&self, vertex: &VertexHandle) -> Point {
        *(self.graph.node_weight(vertex.ix).unwrap())
    }

    fn tile_position(&self, tile: &Face) -> Point {
        tile.center(&self.graph)
    }
}