225 lines
7.8 KiB
Rust
225 lines
7.8 KiB
Rust
use std::fs::OpenOptions;
|
|
use std::io;
|
|
use std::borrow::Borrow;
|
|
|
|
use crate::xform::{Vertex, Transform};
|
|
|
|
/// Basic face-vertex mesh. `faces` contains indices of `verts` and is
|
|
/// taken in groups of 3 for each triangle.
|
|
#[derive(Clone, Debug)]
|
|
pub struct Mesh {
|
|
pub verts: Vec<Vertex>,
|
|
pub faces: Vec<usize>,
|
|
}
|
|
|
|
impl Mesh {
|
|
/// Returns a new `Mesh` whose vertices have been transformed.
|
|
pub fn transform(&self, xfm: &Transform) -> Mesh {
|
|
Mesh {
|
|
verts: xfm.transform(&self.verts),
|
|
// TODO: Is the above faster if I pack vectors into a
|
|
// bigger matrix, and transform that?
|
|
faces: self.faces.clone(), // TODO: Use Rc?
|
|
}
|
|
}
|
|
|
|
/// Write this mesh as an STL file. This will fail if any element
|
|
/// of `faces` is `Tag::Parent`.
|
|
pub fn write_stl_file(&self, fname: &str) -> io::Result<()> {
|
|
let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(fname)?;
|
|
self.write_stl(&mut file)
|
|
}
|
|
|
|
fn write_stl<W: std::io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
|
|
|
// Every group of 3 indices in self.faces is one triangle, so
|
|
// pre-allocate in the format stl_io wants:
|
|
let num_faces = self.faces.len() / 3;
|
|
let mut triangles = vec![stl_io::Triangle {
|
|
normal: [0.0; 3],
|
|
vertices: [[0.0; 3]; 3],
|
|
}; num_faces];
|
|
|
|
// Turn every face into an stl_io::Triangle:
|
|
for i in 0..num_faces {
|
|
let v0 = self.verts[self.faces[3*i + 0]].xyz();
|
|
let v1 = self.verts[self.faces[3*i + 1]].xyz();
|
|
let v2 = self.verts[self.faces[3*i + 2]].xyz();
|
|
|
|
let normal = (v1-v0).cross(&(v2-v0));
|
|
|
|
triangles[i].normal.copy_from_slice(&normal.as_slice());
|
|
triangles[i].vertices[0].copy_from_slice(v0.as_slice());
|
|
triangles[i].vertices[1].copy_from_slice(v1.as_slice());
|
|
triangles[i].vertices[2].copy_from_slice(v2.as_slice());
|
|
// TODO: Is there a cleaner way to do the above?
|
|
}
|
|
|
|
// I could also solve this with something like
|
|
// https://doc.rust-lang.org/std/primitive.slice.html#method.chunks_exact
|
|
// however I don't know what performance difference may be.
|
|
|
|
stl_io::write_stl(writer, triangles.iter())
|
|
}
|
|
|
|
pub fn to_meshfunc(&self) -> MeshFunc {
|
|
MeshFunc {
|
|
faces: self.faces.clone(),
|
|
verts: self.verts.iter().map(|v| VertexUnion::Vertex(*v)).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum VertexUnion {
|
|
/// A concrete vertex.
|
|
Vertex(Vertex),
|
|
/// A vertex argument - something like an argument to a function with
|
|
/// the given positional index.
|
|
///
|
|
/// The job of `MeshFunc.connect` is to bind these arguments to concrete
|
|
/// vertices.
|
|
Arg(usize),
|
|
}
|
|
|
|
pub fn vert_args<T: IntoIterator<Item = usize>>(v: T) -> Vec<VertexUnion> {
|
|
v.into_iter().map(|i| VertexUnion::Arg(i)).collect()
|
|
}
|
|
|
|
/// A face-vertex mesh whose vertices can either be concrete values
|
|
/// (as in `Mesh`) or aliases (indices to other vertices of some
|
|
/// hypothetical mesh). This can be turned to `mesh` only if those
|
|
/// aliases are resolved to concrete vertices with a call like
|
|
/// `connect()`.
|
|
#[derive(Clone, Debug)]
|
|
pub struct MeshFunc {
|
|
//
|
|
pub verts: Vec<VertexUnion>,
|
|
/// Indices of triangles (taken as every 3 values). Indices begin
|
|
/// with `alias_verts`, and then continue into `verts` - that is,
|
|
/// from `0..alias_verts.len()` they refer to `alias_verts`, and from
|
|
/// `alias_verts.len()..(alias_verts.len()+verts.len())` they refer to
|
|
/// `verts`.
|
|
pub faces: Vec<usize>,
|
|
}
|
|
|
|
impl MeshFunc {
|
|
|
|
pub fn to_mesh(&self) -> Mesh {
|
|
Mesh {
|
|
faces: self.faces.clone(),
|
|
verts: self.verts.iter().map(|v| match *v {
|
|
VertexUnion::Vertex(v) => v,
|
|
VertexUnion::Arg(_) => panic!("Mesh still has vertex arguments!"),
|
|
}).collect(),
|
|
}
|
|
}
|
|
|
|
/// Returns a new `MeshFunc` whose concrete vertices have
|
|
/// been transformed. Note that alias vertices are left untouched.
|
|
pub fn transform(&self, xfm: &Transform) -> MeshFunc {
|
|
let v = self.verts.iter().map(|v| {
|
|
match v {
|
|
VertexUnion::Vertex(v) => VertexUnion::Vertex(xfm.mtx * v),
|
|
a@_ => a.clone(),
|
|
}
|
|
});
|
|
|
|
MeshFunc {
|
|
verts: v.collect(),
|
|
faces: self.faces.clone(),
|
|
}
|
|
}
|
|
|
|
/// Appends any number of meshes together. Returns both a single
|
|
/// mesh, and a vector which gives the offset by which each
|
|
/// corresponding input mesh was shifted. That is, for the i'th
|
|
/// index in `meshes`, all of its triangle indices were shifted by
|
|
/// the i'th offset in the resultant mesh.
|
|
pub fn append<T, U>(meshes: T) -> (MeshFunc, Vec<usize>)
|
|
where U: Borrow<MeshFunc>,
|
|
T: IntoIterator<Item = U>
|
|
{
|
|
let mut offsets: Vec<usize> = vec![];
|
|
let mut v: Vec<VertexUnion> = vec![];
|
|
let mut f: Vec<usize> = vec![];
|
|
for mesh_ in meshes {
|
|
let mesh = mesh_.borrow();
|
|
|
|
// Position in 'verts' at which we're appending
|
|
// mesh.verts, which we need to know to shift indices:
|
|
let offset = v.len();
|
|
offsets.push(offset);
|
|
|
|
// Copy all vertices:
|
|
v.append(&mut mesh.verts.clone());
|
|
// Append its faces, applying offset:
|
|
f.extend(mesh.faces.iter().map(|n| n + offset));
|
|
}
|
|
|
|
(MeshFunc { verts: v, faces: f }, offsets)
|
|
}
|
|
|
|
/// Treat this mesh as a 'parent' mesh to connect with any number
|
|
/// of 'child' meshes, all of them paired with their respective
|
|
/// vertex argument values (i.e. `arg_vals` from `Child`).
|
|
/// This returns a tuple of (new mesh, offsets), where 'offsets'
|
|
/// gives the offset of where child meshes were shifted in the new
|
|
/// mesh.
|
|
///
|
|
/// That is, the vertices of 'children[i]' begin at vertex
|
|
/// 'offset[i]' of the new mesh. This is needed in order to adjust
|
|
/// references to vertices of a mesh in 'children' - such as
|
|
/// 'arg_vals' of `rule::Child`.
|
|
pub fn connect<T, U>(&self, children: T) -> (MeshFunc, Vec<usize>)
|
|
where U: Borrow<MeshFunc>,
|
|
T: IntoIterator<Item = (U, Vec<usize>)>
|
|
//pub fn connect(&self, children: &Vec<(OpenMesh, Vec<usize>)>) -> (OpenMesh, Vec<usize>)
|
|
{
|
|
// TODO: Clean up this description a bit
|
|
// TODO: Clean up Vec<usize> stuff
|
|
|
|
// Copy body vertices & faces:
|
|
let mut verts: Vec<VertexUnion> = self.verts.clone();
|
|
let mut faces = self.faces.clone();
|
|
|
|
let mut offsets: Vec<usize> = vec![];
|
|
|
|
for (child_,mapping) in children {
|
|
|
|
let child = child_.borrow();
|
|
|
|
// offset corresponds to the position in 'verts' at
|
|
// which we're appending everything in 'child.verts' -
|
|
// thus, the offset we shift all indices in 'children' by.
|
|
let offset = verts.len();
|
|
|
|
// Concrete vertices in 'child.verts' need to be copied:
|
|
verts.extend(child.verts.iter().filter_map(|v| {
|
|
match v {
|
|
VertexUnion::Vertex(_) => Some(v.clone()),
|
|
VertexUnion::Arg(_) => None,
|
|
}
|
|
}));
|
|
|
|
// All faces need copied, but if if the index was to
|
|
// a concrete vertex, then it needs shifted by 'offset';
|
|
// if an alias, it needs remapped.
|
|
faces.extend(child.faces.iter().map(|n|
|
|
match child.verts[*n] {
|
|
VertexUnion::Vertex(_) => n + offset,
|
|
VertexUnion::Arg(m) => mapping[m],
|
|
}
|
|
));
|
|
|
|
offsets.push(offset);
|
|
}
|
|
|
|
let m = MeshFunc {
|
|
verts: verts,
|
|
faces: faces,
|
|
};
|
|
(m, offsets)
|
|
}
|
|
}
|