michelangelo 0.2.5

a work-in-process library for procedurally constructing 3D geometries
Documentation
pub use descartes::{N, P3, P2, V3, V4, M4, Iso3, Persp3, Into2d, Into3d, WithUniqueOrthogonal,
Area, LinePath, Segment};

use compact::CVec;
use compact_macros::Compact;
use std::rc::Rc;
use crate::sculpt::{Sculpture, SpannedSurface, SculptLine};

#[derive(Copy, Clone, Debug)]
pub struct Vertex {
    pub position: [f32; 3],
}

#[derive(Copy, Clone)]
pub struct Instance {
    pub instance_position: [f32; 3],
    pub instance_direction: [f32; 2],
    pub instance_color: [f32; 3],
}

impl Instance {
    pub fn with_color(color: [f32; 3]) -> Instance {
        Instance {
            instance_position: [0.0, 0.0, 0.0],
            instance_direction: [1.0, 0.0],
            instance_color: color,
        }
    }
}

#[derive(Compact, Debug)]
pub struct Mesh {
    pub vertices: CVec<Vertex>,
    pub indices: CVec<u16>,
}

impl Mesh {
    pub fn new(vertices: Vec<Vertex>, indices: Vec<u16>) -> Mesh {
        Mesh {
            vertices: vertices.into(),
            indices: indices.into(),
        }
    }

    pub fn empty() -> Mesh {
        Mesh {
            vertices: CVec::new(),
            indices: CVec::new(),
        }
    }
}

impl Clone for Mesh {
    fn clone(&self) -> Mesh {
        Mesh {
            vertices: self.vertices.to_vec().into(),
            indices: self.indices.to_vec().into(),
        }
    }
}

impl ::std::ops::Add for Mesh {
    type Output = Mesh;

    fn add(mut self, rhs: Mesh) -> Mesh {
        let self_n_vertices = self.vertices.len();
        self.vertices.extend_from_copy_slice(&rhs.vertices);
        self.indices
            .extend(rhs.indices.iter().map(|i| *i + self_n_vertices as u16));
        self
    }
}

impl ::std::ops::AddAssign for Mesh {
    fn add_assign(&mut self, rhs: Mesh) {
        let self_n_vertices = self.vertices.len();
        for vertex in rhs.vertices.iter().cloned() {
            self.vertices.push(vertex);
        }
        for index in rhs.indices.iter() {
            self.indices.push(index + self_n_vertices as u16)
        }
    }
}

impl ::std::iter::Sum for Mesh {
    fn sum<I: Iterator<Item = Mesh>>(iter: I) -> Mesh {
        let mut summed_mesh = Mesh {
            vertices: CVec::new(),
            indices: CVec::new(),
        };
        for mesh in iter {
            summed_mesh += mesh;
        }
        summed_mesh
    }
}

impl<'a> ::std::ops::AddAssign<&'a Mesh> for Mesh {
    fn add_assign(&mut self, rhs: &'a Mesh) {
        let self_n_vertices = self.vertices.len();
        for vertex in rhs.vertices.iter().cloned() {
            self.vertices.push(vertex);
        }
        for index in rhs.indices.iter() {
            self.indices.push(index + self_n_vertices as u16)
        }
    }
}

impl<'a> ::std::iter::Sum<&'a Mesh> for Mesh {
    fn sum<I: Iterator<Item = &'a Mesh>>(iter: I) -> Mesh {
        let mut summed_mesh = Mesh {
            vertices: CVec::new(),
            indices: CVec::new(),
        };
        for mesh in iter {
            summed_mesh += mesh;
        }
        summed_mesh
    }
}

use itertools::{Itertools, Position};
use lyon_tessellation::{FillTessellator, FillOptions, FillVertex, GeometryBuilder};
use lyon_tessellation::geometry_builder::{VertexId, Count};
use lyon_tessellation::path::iterator::PathIter;
use lyon_tessellation::path::PathEvent;
use lyon_tessellation::math::point;

impl GeometryBuilder<FillVertex> for Mesh {
    fn begin_geometry(&mut self) {}
    fn end_geometry(&mut self) -> Count {
        Count {
            vertices: self.vertices.len() as u32,
            indices: self.indices.len() as u32,
        }
    }
    fn abort_geometry(&mut self) {}
    fn add_vertex(&mut self, input: FillVertex) -> VertexId {
        let id = self.vertices.len();
        self.vertices.push(Vertex {
            position: [input.position.x, input.position.y, 0.0],
        });
        VertexId(id as u32)
    }
    fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
        self.indices.push(a.0 as u16);
        self.indices.push(b.0 as u16);
        self.indices.push(c.0 as u16);
    }
}

impl Mesh {
    pub fn from_area(area: &Area) -> Mesh {
        let path_iterator = PathIter::new(area.primitives.iter().flat_map(|primitive| {
            primitive
                .boundary
                .path()
                .segments()
                .with_position()
                .flat_map(|segment_with_position| {
                    let initial_move = match segment_with_position {
                        Position::First(segment) | Position::Only(segment) => Some(
                            PathEvent::MoveTo(point(segment.start().x, segment.start().y)),
                        ),
                        _ => None,
                    };

                    let segment = segment_with_position.into_inner();

                    initial_move
                        .into_iter()
                        .chain(Some(PathEvent::LineTo(point(
                            segment.end().x,
                            segment.end().y,
                        ))))
                        .collect::<Vec<_>>()
                })
        }));

        let mut tesselator = FillTessellator::new();
        let mut output = Mesh::empty();

        tesselator
            .tessellate_path(path_iterator, &FillOptions::default(), &mut output)
            .unwrap();

        output
    }

    pub fn from_path_as_band(path: &LinePath, width: N, z: N) -> Mesh {
        Self::from_path_as_band_asymmetric(path, width / 2.0, width / 2.0, z)
    }

    pub fn from_path_as_band_asymmetric(
        path: &LinePath,
        width_left: N,
        width_right: N,
        z: N,
    ) -> Mesh {
        path.shift_orthogonally(-width_left).and_then(|left_path|
            path.shift_orthogonally(width_right).map(|right_path| (left_path, right_path))
        ).map(|(left_path, right_path)| {
            let left_line = Rc::new(SculptLine::new(left_path, z));
            let right_line = Rc::new(SculptLine::new(right_path, z));

            Sculpture::new(vec![
                SpannedSurface::new(left_line, right_line).into()
            ]).to_mesh()
        }).unwrap_or(Mesh::empty())
    }
}