Misc formatting & not-yet-ready NestedSpiral example

This commit is contained in:
Chris Hodapp 2021-01-21 17:03:04 -05:00
parent a664dbba16
commit 51cab46299
5 changed files with 213 additions and 82 deletions

View File

@ -367,3 +367,94 @@ impl Sierpinski {
return s < 0.01; return s < 0.01;
} }
} }
pub struct NestedSpiral {
base: Vec<Vertex>,
verts: Vec<Vertex>,
faces: Vec<usize>,
seeds: Vec<Transform>,
incr: Vec<Transform>,
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<Transform>) {
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<Transform> = 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,
};
}
}

View File

@ -3,8 +3,8 @@ pub mod mesh;
pub mod prim; pub mod prim;
#[macro_use] #[macro_use]
pub mod util; pub mod util;
pub mod xform;
pub mod examples; pub mod examples;
pub mod xform;
//pub use crate::examples; //pub use crate::examples;
//pub use crate::openmesh::test_thing; //pub use crate::openmesh::test_thing;
@ -12,9 +12,9 @@ pub mod examples;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use nalgebra::*;
use std::rc::Rc; use std::rc::Rc;
use std::time::Instant; use std::time::Instant;
use nalgebra::*;
#[test] #[test]
fn xform_order() { fn xform_order() {
@ -24,7 +24,7 @@ mod tests {
let dx = 4.0; let dx = 4.0;
let r = -0.5; let r = -0.5;
let trans = xform::Transform::new().translate(dx, 0.0, 0.0); let trans = xform::Transform::new().translate(dx, 0.0, 0.0);
let rot = xform::Transform::new().rotate(y, r); let rot = xform::Transform::new().rotate(y, r);
@ -32,13 +32,27 @@ mod tests {
let xf2 = rot.translate(dx, 0.0, 0.0); let xf2 = rot.translate(dx, 0.0, 0.0);
// Rotate entire space, *then* translate in that rotated plane: // 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(&trans)
geom.transform(&(rot * trans)).write_stl_file("xform_mul_rot_trans.stl").unwrap(); .transform(&rot)
geom.transform(&xf2).write_stl_file("xform_rot_trans.stl").unwrap(); .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: // Translate cube, *then* rotate it:
geom.transform(&rot).transform(&trans).write_stl_file("xform_apply_rot_trans.stl").unwrap(); geom.transform(&rot)
geom.transform(&(trans * rot)).write_stl_file("xform_mul_trans_rot.stl").unwrap(); .transform(&trans)
geom.transform(&xf1).write_stl_file("xform_trans_rot.stl").unwrap(); .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 // TODO: These tests don't test any conditions, so this is useful
@ -55,7 +69,6 @@ mod tests {
let fname = format!("{}.stl", name); let fname = format!("{}.stl", name);
println!("Writing {}...", fname); println!("Writing {}...", fname);
m.write_stl_file(&fname).unwrap(); m.write_stl_file(&fname).unwrap();
} }
#[test] #[test]
@ -70,7 +83,6 @@ mod tests {
let fname = format!("{}.stl", name); let fname = format!("{}.stl", name);
println!("Writing {}...", fname); println!("Writing {}...", fname);
m.write_stl_file(&fname).unwrap(); m.write_stl_file(&fname).unwrap();
} }
#[test] #[test]
@ -85,7 +97,6 @@ mod tests {
let fname = format!("{}.stl", name); let fname = format!("{}.stl", name);
println!("Writing {}...", fname); println!("Writing {}...", fname);
m.write_stl_file(&fname).unwrap(); m.write_stl_file(&fname).unwrap();
} }
#[test] #[test]
@ -101,7 +112,21 @@ mod tests {
let fname = format!("{}.stl", name); let fname = format!("{}.stl", name);
println!("Writing {}...", fname); println!("Writing {}...", fname);
m.write_stl_file(&fname).unwrap(); 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: // need this for now:

View File

@ -1,7 +1,7 @@
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io; use std::io;
use crate::xform::{Vertex, Transform}; use crate::xform::{Transform, Vertex};
/// Basic face-vertex mesh. `faces` contains indices of `verts` and is /// Basic face-vertex mesh. `faces` contains indices of `verts` and is
/// taken in groups of 3 for each triangle. /// 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 /// Write this mesh as an STL file. This will fail if any element
/// of `faces` is `Tag::Parent`. /// of `faces` is `Tag::Parent`.
pub fn write_stl_file(&self, fname: &str) -> io::Result<()> { 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) self.write_stl(&mut file)
} }
fn write_stl<W: std::io::Write>(&self, writer: &mut W) -> io::Result<()> { 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 // Every group of 3 indices in self.faces is one triangle, so
// pre-allocate in the format stl_io wants: // pre-allocate in the format stl_io wants:
let num_faces = self.faces.len() / 3; let num_faces = self.faces.len() / 3;
let mut triangles = vec![stl_io::Triangle { let mut triangles = vec![
normal: [0.0; 3], stl_io::Triangle {
vertices: [[0.0; 3]; 3], normal: [0.0; 3],
}; num_faces]; vertices: [[0.0; 3]; 3],
};
num_faces
];
// Turn every face into an stl_io::Triangle: // Turn every face into an stl_io::Triangle:
for i in 0..num_faces { for i in 0..num_faces {
let v0 = self.verts[self.faces[3*i + 0]].xyz(); let v0 = self.verts[self.faces[3 * i + 0]].xyz();
let v1 = self.verts[self.faces[3*i + 1]].xyz(); let v1 = self.verts[self.faces[3 * i + 1]].xyz();
let v2 = self.verts[self.faces[3*i + 2]].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].normal.copy_from_slice(&normal.as_slice());
triangles[i].vertices[0].copy_from_slice(v0.as_slice()); triangles[i].vertices[0].copy_from_slice(v0.as_slice());

View File

@ -1,4 +1,4 @@
use crate::mesh::{Mesh}; use crate::mesh::Mesh;
use crate::xform::{vertex, Transform}; use crate::xform::{vertex, Transform};
/// Returns an empty mesh (no vertices, no faces). /// Returns an empty mesh (no vertices, no faces).
@ -23,18 +23,9 @@ pub fn cube() -> Mesh {
vertex(1.0, 1.0, 1.0), vertex(1.0, 1.0, 1.0),
], ],
faces: vec![ faces: vec![
0, 3, 1, 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,
0, 2, 3, 7, 0, 1, 5, 0, 5, 4,
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))
} }

View File

@ -1,6 +1,6 @@
use crate::mesh::Mesh;
use crate::xform::Vertex;
use std::ops::Range; use std::ops::Range;
use crate::mesh::{Mesh};
use crate::xform::{Vertex};
/// This is like `vec!`, but it can handle elements that are given /// This is like `vec!`, but it can handle elements that are given
/// with `@var element` rather than `element`, e.g. like /// with `@var element` rather than `element`, e.g. like
@ -42,40 +42,49 @@ impl<T> VecExt<T> for Vec<T> {
/// (thus, the returned length will be `count*p.len()`). /// (thus, the returned length will be `count*p.len()`).
pub fn subdivide_cycle(p: &Vec<Vertex>, count: usize) -> Vec<Vertex> { pub fn subdivide_cycle(p: &Vec<Vertex>, count: usize) -> Vec<Vertex> {
let n = p.len(); let n = p.len();
(0..n).map(|i| { (0..n)
// The inner loop is interpolating between v1 and v2: .map(|i| {
let v1 = &p[i]; // The inner loop is interpolating between v1 and v2:
let v2 = &p[(i+1) % n]; let v1 = &p[i];
(0..count).map(move |j| { let v2 = &p[(i + 1) % n];
// This is just lerping in count+1 equally spaced steps: (0..count).map(move |j| {
let f = (j as f32) / (count as f32); // This is just lerping in count+1 equally spaced steps:
v1*(1.0-f) + v2*f 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 // TODO: This can be generalized to an iterator or to IntoIterator
// trait bound // trait bound
pub fn parallel_zigzag_faces(r1: Range<usize>, r2: Range<usize>) -> Vec<usize> { pub fn parallel_zigzag_faces(r1: Range<usize>, r2: Range<usize>) -> Vec<usize> {
let count = r1.end - r1.start; 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"); panic!("Ranges must have the same size");
} }
if (r2.end > r1.start && r2.end < r1.end) || if (r2.end > r1.start && r2.end < r1.end) || (r1.end > r2.start && r1.end < r2.end) {
(r1.end > r2.start && r1.end < r2.end) {
panic!("Ranges cannot overlap"); panic!("Ranges cannot overlap");
} }
(0..count).map(|i0| { (0..count)
// i0 is an *offset* for the 'current' index. .map(|i0| {
// i1 is for the 'next' index, wrapping back to 0. // i0 is an *offset* for the 'current' index.
let i1 = (i0 + 1) % count; // i1 is for the 'next' index, wrapping back to 0.
vec![ let i1 = (i0 + 1) % count;
// Mind winding order! vec![
r1.start + i1, r2.start + i0, r1.start + i0, // Mind winding order!
r2.start + i1, r2.start + i0, r1.start + i1, r1.start + i1,
] r2.start + i0,
}).flatten().collect() r1.start + i0,
r2.start + i1,
r2.start + i0,
r1.start + i1,
]
})
.flatten()
.collect()
} }
pub fn parallel_zigzag_mesh(verts: Vec<Vertex>, main: Range<usize>, parent: Range<usize>) -> Mesh { pub fn parallel_zigzag_mesh(verts: Vec<Vertex>, main: Range<usize>, parent: Range<usize>) -> Mesh {
@ -85,8 +94,10 @@ pub fn parallel_zigzag_mesh(verts: Vec<Vertex>, main: Range<usize>, parent: Rang
} }
} }
pub fn parallel_zigzag2<T,U>(main: T, parent: U) -> Vec<usize> pub fn parallel_zigzag2<T, U>(main: T, parent: U) -> Vec<usize>
where T: IntoIterator<Item=usize>, U: IntoIterator<Item=usize> where
T: IntoIterator<Item = usize>,
U: IntoIterator<Item = usize>,
{ {
let m: Vec<usize> = main.into_iter().collect(); let m: Vec<usize> = main.into_iter().collect();
let p: Vec<usize> = parent.into_iter().collect(); let p: Vec<usize> = parent.into_iter().collect();
@ -95,16 +106,18 @@ where T: IntoIterator<Item=usize>, U: IntoIterator<Item=usize>
panic!("Vectors must be the same size!") panic!("Vectors must be the same size!")
} }
(0..l).map(|i0| { (0..l)
// i0 is an *offset* for the 'current' index. .map(|i0| {
// i1 is for the 'next' index, wrapping back to 0. // i0 is an *offset* for the 'current' index.
let i1 = (i0 + 1) % l; // i1 is for the 'next' index, wrapping back to 0.
vec![ let i1 = (i0 + 1) % l;
// Mind winding order! vec![
m[i1], p[i0], m[i0], // Mind winding order!
p[i1], p[i0], m[i1], m[i1], p[i0], m[i0], p[i1], p[i0], m[i1],
] ]
}).flatten().collect() })
.flatten()
.collect()
} }
pub fn centroid(verts: &Vec<Vertex>) -> Vertex { pub fn centroid(verts: &Vec<Vertex>) -> Vertex {
@ -118,17 +131,22 @@ pub fn centroid(verts: &Vec<Vertex>) -> Vertex {
} }
pub fn connect_convex(range: Range<usize>, target: usize, as_parent: bool) -> Vec<usize> { pub fn connect_convex(range: Range<usize>, target: usize, as_parent: bool) -> Vec<usize> {
let count = range.end - range.start; let count = range.end - range.start;
if as_parent { if as_parent {
(0..count).map(|i0| { (0..count)
let i1 = (i0 + 1) % count; .map(|i0| {
vec![range.start + i1, range.start + i0, target] let i1 = (i0 + 1) % count;
}).flatten().collect() vec![range.start + i1, range.start + i0, target]
})
.flatten()
.collect()
} else { } else {
(0..count).map(|i0| { (0..count)
let i1 = (i0 + 1) % count; .map(|i0| {
vec![range.start + i0, range.start + i1, target] let i1 = (i0 + 1) % count;
}).flatten().collect() vec![range.start + i0, range.start + i1, target]
})
.flatten()
.collect()
} }
} }