From 51cab46299c404f1c06517b7146edfc7cc44c77f Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Thu, 21 Jan 2021 17:03:04 -0500 Subject: [PATCH] Misc formatting & not-yet-ready NestedSpiral example --- src/examples.rs | 91 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 49 ++++++++++++++++------ src/mesh.rs | 28 ++++++++----- src/prim.rs | 19 +++------ src/util.rs | 108 ++++++++++++++++++++++++++++-------------------- 5 files changed, 213 insertions(+), 82 deletions(-) diff --git a/src/examples.rs b/src/examples.rs index ead6af5..2465dd4 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -367,3 +367,94 @@ impl Sierpinski { return s < 0.01; } } + +pub struct NestedSpiral { + base: Vec, + verts: Vec, + faces: Vec, + seeds: Vec, + incr: Vec, + max_count: usize, +} + +impl NestedSpiral { + pub fn new() -> NestedSpiral { + let d = vertex(0.5, 0.0, 0.5); + // 'Base' vertices, used throughout: + let mut base = vec![ + (vertex(-0.5, 0.0, -0.5)), + (vertex(0.5, 0.0, -0.5)), + (vertex(0.5, 0.0, 0.5)), + (vertex(-0.5, 0.0, 0.5)), + ]; + base = id().scale(0.3).transform(&base); + // TODO: Check winding order + + let rot_y = |ang| id().rotate(&Vector3::y_axis(), ang); + let rot_y_and_trans = |ang, dx| rot_y(ang).translate(dx, 0.0, 0.0); + + // Pairs of (seed transform, incremental transform): + let xforms: Vec<(Transform, Transform)> = vec![ + (id(), id().translate(0.0, 0.1, 0.0)), + (rot_y_and_trans(0.0, 3.0), rot_y(-0.03)), + (rot_y_and_trans(0.0, 1.0), rot_y(0.07)), + (rot_y_and_trans(0.0, 0.5), rot_y(-0.2)), + ]; + // TODO: Does it make sense that this should be the reverse of the Python ones? + + // TODO: I still need Cartesian product of various different seeds. + // e.g. rot_y_and_trans(0.0, 0.5) should be N entries with various rotations + // instead of 0.0. + + let (seeds, incr) = xforms.iter().cloned().unzip(); + + NestedSpiral { + base: base, + verts: vec![], + faces: vec![], + incr: incr, + seeds: seeds, + max_count: 500, + } + } + + pub fn incr(&mut self, idx: usize, b: [usize; 4], xforms: Vec) { + + if idx >= self.max_count { + self.faces.extend_from_slice(&[b[0], b[2], b[1], b[0], b[3], b[2]]); + return; + } + + // Compute global transform with product of all 'xforms': + let global = xforms.iter().fold(id(), |acc, m| acc * (*m)); + + // Generate geometry: + let g = global.transform(&self.base); + let (n0, n1) = self.verts.append_indexed(g); + self.faces.append(&mut util::parallel_zigzag2(b.to_vec(), n0..n1)); + + // Increment the individual transformations: + let xforms_next: Vec = xforms.iter() + .zip(self.incr.iter()) + .map(|(m,incr)| ((*incr) * (*m))) + .collect(); + + self.incr(idx + 1, [n0, n0 + 1, n0 + 2, n0 + 3], xforms_next); + } + + pub fn run(mut self) -> Mesh { + // TODO: Generate all seed geometry & transforms + + let global = self.seeds.iter().fold(id(), |acc, m| acc * (*m)); + let g = global.transform(&self.base); + let (n0, n1) = self.verts.append_indexed(g); + //let (n0, _) = self.verts.append_indexed(self.base.clone()); + + self.incr(0, [n0, n0 + 1, n0 + 2, n0 + 3], self.seeds.clone()); + + return Mesh { + verts: self.verts, + faces: self.faces, + }; + } +} diff --git a/src/lib.rs b/src/lib.rs index fad2d77..295e601 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,8 @@ pub mod mesh; pub mod prim; #[macro_use] pub mod util; -pub mod xform; pub mod examples; +pub mod xform; //pub use crate::examples; //pub use crate::openmesh::test_thing; @@ -12,9 +12,9 @@ pub mod examples; #[cfg(test)] mod tests { use super::*; + use nalgebra::*; use std::rc::Rc; use std::time::Instant; - use nalgebra::*; #[test] fn xform_order() { @@ -24,7 +24,7 @@ mod tests { let dx = 4.0; let r = -0.5; - + let trans = xform::Transform::new().translate(dx, 0.0, 0.0); let rot = xform::Transform::new().rotate(y, r); @@ -32,13 +32,27 @@ mod tests { let xf2 = rot.translate(dx, 0.0, 0.0); // Rotate entire space, *then* translate in that rotated plane: - geom.transform(&trans).transform(&rot).write_stl_file("xform_apply_trans_rot.stl").unwrap(); - geom.transform(&(rot * trans)).write_stl_file("xform_mul_rot_trans.stl").unwrap(); - geom.transform(&xf2).write_stl_file("xform_rot_trans.stl").unwrap(); + geom.transform(&trans) + .transform(&rot) + .write_stl_file("xform_apply_trans_rot.stl") + .unwrap(); + geom.transform(&(rot * trans)) + .write_stl_file("xform_mul_rot_trans.stl") + .unwrap(); + geom.transform(&xf2) + .write_stl_file("xform_rot_trans.stl") + .unwrap(); // Translate cube, *then* rotate it: - geom.transform(&rot).transform(&trans).write_stl_file("xform_apply_rot_trans.stl").unwrap(); - geom.transform(&(trans * rot)).write_stl_file("xform_mul_trans_rot.stl").unwrap(); - geom.transform(&xf1).write_stl_file("xform_trans_rot.stl").unwrap(); + geom.transform(&rot) + .transform(&trans) + .write_stl_file("xform_apply_rot_trans.stl") + .unwrap(); + geom.transform(&(trans * rot)) + .write_stl_file("xform_mul_trans_rot.stl") + .unwrap(); + geom.transform(&xf1) + .write_stl_file("xform_trans_rot.stl") + .unwrap(); } // TODO: These tests don't test any conditions, so this is useful @@ -55,7 +69,6 @@ mod tests { let fname = format!("{}.stl", name); println!("Writing {}...", fname); m.write_stl_file(&fname).unwrap(); - } #[test] @@ -70,7 +83,6 @@ mod tests { let fname = format!("{}.stl", name); println!("Writing {}...", fname); m.write_stl_file(&fname).unwrap(); - } #[test] @@ -85,7 +97,6 @@ mod tests { let fname = format!("{}.stl", name); println!("Writing {}...", fname); m.write_stl_file(&fname).unwrap(); - } #[test] @@ -101,7 +112,21 @@ mod tests { let fname = format!("{}.stl", name); println!("Writing {}...", fname); m.write_stl_file(&fname).unwrap(); + } + #[test] + fn nested_spiral() { + let name = "nested_spiral"; + println!("---------------------------------------------------"); + let b = examples::NestedSpiral::new(); + //let b = examples::Sierpinski::new(0.51, 0.10, 0.1); + let m = b.run(); + + println!("Got {} verts...", m.verts.len()); + + let fname = format!("{}.stl", name); + println!("Writing {}...", fname); + m.write_stl_file(&fname).unwrap(); } } // need this for now: diff --git a/src/mesh.rs b/src/mesh.rs index 9d99e0b..46d5789 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -1,7 +1,7 @@ use std::fs::OpenOptions; use std::io; -use crate::xform::{Vertex, Transform}; +use crate::xform::{Transform, Vertex}; /// Basic face-vertex mesh. `faces` contains indices of `verts` and is /// taken in groups of 3 for each triangle. @@ -25,27 +25,33 @@ impl Mesh { /// 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)?; + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(fname)?; self.write_stl(&mut file) } fn write_stl(&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]; + 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 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)); + 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()); diff --git a/src/prim.rs b/src/prim.rs index e8e2f1d..811981b 100644 --- a/src/prim.rs +++ b/src/prim.rs @@ -1,4 +1,4 @@ -use crate::mesh::{Mesh}; +use crate::mesh::Mesh; use crate::xform::{vertex, Transform}; /// Returns an empty mesh (no vertices, no faces). @@ -23,18 +23,9 @@ pub fn cube() -> Mesh { vertex(1.0, 1.0, 1.0), ], faces: vec![ - 0, 3, 1, - 0, 2, 3, - 1, 7, 5, - 1, 3, 7, - 5, 6, 4, - 5, 7, 6, - 4, 2, 0, - 4, 6, 2, - 2, 7, 3, - 2, 6, 7, - 0, 1, 5, - 0, 5, 4, + 0, 3, 1, 0, 2, 3, 1, 7, 5, 1, 3, 7, 5, 6, 4, 5, 7, 6, 4, 2, 0, 4, 6, 2, 2, 7, 3, 2, 6, + 7, 0, 1, 5, 0, 5, 4, ], - }.transform(&Transform::new().translate(-0.5, -0.5, -0.5)) + } + .transform(&Transform::new().translate(-0.5, -0.5, -0.5)) } diff --git a/src/util.rs b/src/util.rs index 8667a15..4b1f73c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,6 @@ +use crate::mesh::Mesh; +use crate::xform::Vertex; use std::ops::Range; -use crate::mesh::{Mesh}; -use crate::xform::{Vertex}; /// This is like `vec!`, but it can handle elements that are given /// with `@var element` rather than `element`, e.g. like @@ -42,40 +42,49 @@ impl VecExt for Vec { /// (thus, the returned length will be `count*p.len()`). pub fn subdivide_cycle(p: &Vec, count: usize) -> Vec { let n = p.len(); - (0..n).map(|i| { - // The inner loop is interpolating between v1 and v2: - let v1 = &p[i]; - let v2 = &p[(i+1) % n]; - (0..count).map(move |j| { - // This is just lerping in count+1 equally spaced steps: - let f = (j as f32) / (count as f32); - v1*(1.0-f) + v2*f + (0..n) + .map(|i| { + // The inner loop is interpolating between v1 and v2: + let v1 = &p[i]; + let v2 = &p[(i + 1) % n]; + (0..count).map(move |j| { + // This is just lerping in count+1 equally spaced steps: + let f = (j as f32) / (count as f32); + v1 * (1.0 - f) + v2 * f + }) }) - }).flatten().collect() + .flatten() + .collect() } // TODO: This can be generalized to an iterator or to IntoIterator // trait bound pub fn parallel_zigzag_faces(r1: Range, r2: Range) -> Vec { let count = r1.end - r1.start; - if (r2.end - r2.start) != count { + if (r2.end - r2.start) != count { panic!("Ranges must have the same size"); } - if (r2.end > r1.start && r2.end < r1.end) || - (r1.end > r2.start && r1.end < r2.end) { + if (r2.end > r1.start && r2.end < r1.end) || (r1.end > r2.start && r1.end < r2.end) { panic!("Ranges cannot overlap"); } - (0..count).map(|i0| { - // i0 is an *offset* for the 'current' index. - // i1 is for the 'next' index, wrapping back to 0. - let i1 = (i0 + 1) % count; - vec![ - // Mind winding order! - r1.start + i1, r2.start + i0, r1.start + i0, - r2.start + i1, r2.start + i0, r1.start + i1, - ] - }).flatten().collect() + (0..count) + .map(|i0| { + // i0 is an *offset* for the 'current' index. + // i1 is for the 'next' index, wrapping back to 0. + let i1 = (i0 + 1) % count; + vec![ + // Mind winding order! + r1.start + i1, + r2.start + i0, + r1.start + i0, + r2.start + i1, + r2.start + i0, + r1.start + i1, + ] + }) + .flatten() + .collect() } pub fn parallel_zigzag_mesh(verts: Vec, main: Range, parent: Range) -> Mesh { @@ -85,8 +94,10 @@ pub fn parallel_zigzag_mesh(verts: Vec, main: Range, parent: Rang } } -pub fn parallel_zigzag2(main: T, parent: U) -> Vec -where T: IntoIterator, U: IntoIterator +pub fn parallel_zigzag2(main: T, parent: U) -> Vec +where + T: IntoIterator, + U: IntoIterator, { let m: Vec = main.into_iter().collect(); let p: Vec = parent.into_iter().collect(); @@ -95,16 +106,18 @@ where T: IntoIterator, U: IntoIterator panic!("Vectors must be the same size!") } - (0..l).map(|i0| { - // i0 is an *offset* for the 'current' index. - // i1 is for the 'next' index, wrapping back to 0. - let i1 = (i0 + 1) % l; - vec![ - // Mind winding order! - m[i1], p[i0], m[i0], - p[i1], p[i0], m[i1], - ] - }).flatten().collect() + (0..l) + .map(|i0| { + // i0 is an *offset* for the 'current' index. + // i1 is for the 'next' index, wrapping back to 0. + let i1 = (i0 + 1) % l; + vec![ + // Mind winding order! + m[i1], p[i0], m[i0], p[i1], p[i0], m[i1], + ] + }) + .flatten() + .collect() } pub fn centroid(verts: &Vec) -> Vertex { @@ -118,17 +131,22 @@ pub fn centroid(verts: &Vec) -> Vertex { } pub fn connect_convex(range: Range, target: usize, as_parent: bool) -> Vec { - let count = range.end - range.start; if as_parent { - (0..count).map(|i0| { - let i1 = (i0 + 1) % count; - vec![range.start + i1, range.start + i0, target] - }).flatten().collect() + (0..count) + .map(|i0| { + let i1 = (i0 + 1) % count; + vec![range.start + i1, range.start + i0, target] + }) + .flatten() + .collect() } else { - (0..count).map(|i0| { - let i1 = (i0 + 1) % count; - vec![range.start + i0, range.start + i1, target] - }).flatten().collect() + (0..count) + .map(|i0| { + let i1 = (i0 + 1) % count; + vec![range.start + i0, range.start + i1, target] + }) + .flatten() + .collect() } }