From 45ab4ed9e0aa30c36ff340936b65c1d27b94e0a8 Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Tue, 31 Mar 2020 15:30:25 -0400 Subject: [PATCH] Added helper function to avoid juggling of indices --- README.md | 9 +++++---- src/examples.rs | 36 +++++++++++++----------------------- src/openmesh.rs | 11 +++++++++-- src/rule.rs | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index d6e8f80..031ab5b 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ ## Highest priority: - If my `closure_try2` branch seems to be working: start converting - other things and cleaning them up. Modify `twist` to have the - things I wrote this for in the first place! + other things and cleaning everything up. (`twist` is still ugly.) - See `automata_scratch/examples.py` and implement some of the tougher examples. - `spiral_nested_2` & `spiral_nested_3` (how to compose @@ -21,10 +20,12 @@ - What patterns can I factor out? I do some things regularly, like: the clockwise boundaries, the zigzag connections, the iterating over a `Vec` to transform each element and make another vector. - - I have seen many of my bugs come from: all this arithmetic on - indices. I generate vertex maps more or less manually. - Docs on modules - Grep for all TODOs in code, really. +- Look at performance. Can I save on copies of geometry by using + `Rc` or the like? In many cases I have nothing but copied + geometry. Can I pre-allocate vectors instead of + extending/appending? - Look at everything in README.md in automata_scratch. ## If I'm bored: diff --git a/src/examples.rs b/src/examples.rs index 7abd767..39368ea 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -375,7 +375,6 @@ fn twist(f: f32, subdiv: usize) -> Rule { vertex( 0.5, 0.0, 0.5), vertex(-0.5, 0.0, 0.5), ], &xf); - //let seed_sub = util::subdivide_cycle(&seed, subdiv); let dx0: f32 = 1.5; let dy: f32 = 0.1/f; let ang: f32 = 0.05/f; @@ -434,12 +433,12 @@ fn twist(f: f32, subdiv: usize) -> Rule { let start = move |self_: Rc| -> RuleEval { - let xform = |dx, i, ang0| -> Mat4 { - (geometry::Rotation3::from_axis_angle(&y, ang0 + (qtr * (i as f32))).to_homogeneous() * + let xform = |dx, i, ang0, div| -> Mat4 { + (geometry::Rotation3::from_axis_angle(&y, ang0 + (qtr / div * (i as f32))).to_homogeneous() * geometry::Translation3::new(dx, 0.0, 0.0).to_homogeneous()) }; - let make_child = |i, dx, incr, ang0| -> (Child, OpenMesh) { + let make_child = |i, incr, xform| -> (OpenMesh, Child) { let seed_orig = transform(&seed, &incr); let seed_sub = util::subdivide_cycle(&seed_orig, subdiv); @@ -447,32 +446,23 @@ fn twist(f: f32, subdiv: usize) -> Rule { let c = Child { rule: Rc::new(Rule { eval: (recur.clone())(incr) }), - xf: xform(dx, i, ang0), - vmap: ((n+1)*i..(n+1)*(i+count)).collect(), // N.B. - // note n+1, not n. the +1 is for the centroid below - // TODO: The above vmap is wrong when I call - // 'make_child' twice and then append. + xf: xform, + vmap: (0..(n+1)).collect(), + // N.B. n+1, not n. the +1 is for the centroid below }; - let mut vs = transform(&seed_sub, &c.xf); + let mut vs = transform(&seed_sub, &xform); // and in the process, generate faces for these seeds: let (centroid, f) = util::connect_convex(&vs, false); vs.push(centroid); - (c, OpenMesh { verts: vs, faces: f }) + (OpenMesh { verts: vs, faces: f }, c) }; - // First generate 'count' children, each one shifted/rotated - // differently: - let children_inner = (0..count).map(|i| make_child(i, dx0, incr_inner, 0.0)); - let children_outer = (0..count).map(|i| make_child(i + count, dx0*2.0, incr_outer, qtr/2.0)); - // TODO: the +count is only to work around vmap kludges - - let (children, meshes): (Vec<_>, Vec<_>) = children_inner.chain(children_outer).unzip(); + // Generate 'count' children, shifted/rotated differently: + let children_inner = (0..count).map(|i| make_child(i, incr_inner, xform(dx0, i, 0.0, 1.0))); + let children_outer = (0..count).map(|i| make_child(i, incr_outer, xform(dx0*2.0, i, qtr/2.0, 2.0))); - RuleEval { - geom: OpenMesh::append(meshes), - final_geom: prim::empty_mesh(), - children: children, - } + RuleEval::from_pairs( + children_inner.chain(children_outer), prim::empty_mesh()) }; Rule { eval: Box::new(start) } diff --git a/src/openmesh.rs b/src/openmesh.rs index d44897d..33de6ac 100644 --- a/src/openmesh.rs +++ b/src/openmesh.rs @@ -45,10 +45,16 @@ pub struct OpenMesh { impl OpenMesh { - pub fn append(meshes: T) -> OpenMesh + /// 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(meshes: T) -> (OpenMesh, Vec) where U: Borrow, T: IntoIterator { + let mut offsets: Vec = vec![]; let mut v: Vec = vec![]; let mut f: Vec = vec![]; for mesh_ in meshes { @@ -57,6 +63,7 @@ impl OpenMesh { // 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()); @@ -70,7 +77,7 @@ impl OpenMesh { })); } - OpenMesh { verts: v, faces: f } + (OpenMesh { verts: v, faces: f }, offsets) } /// Returns a new `OpenMesh` whose vertices have been transformed. diff --git a/src/rule.rs b/src/rule.rs index 0347816..8cc0428 100644 --- a/src/rule.rs +++ b/src/rule.rs @@ -1,5 +1,6 @@ use crate::openmesh::{OpenMesh, Tag, Mat4}; //use crate::prim; +use std::borrow::Borrow; use std::rc::Rc; pub type RuleFn = Box) -> RuleEval>; @@ -254,3 +255,35 @@ impl Rule { } } + +impl RuleEval { + /// Turn an iterator of (OpenMesh, Child) into a single RuleEval. + /// All meshes are merged, and the `vmap` in each child has the + /// correct offsets applied to account for this merge. + /// + /// (`final_geom` is passed through to the RuleEval unmodified.) + pub fn from_pairs(m: T, final_geom: OpenMesh) -> RuleEval + where U: Borrow, + T: IntoIterator + { + let (meshes, children): (Vec<_>, Vec<_>) = m.into_iter().unzip(); + let (mesh, offsets) = OpenMesh::append(meshes); + + // Patch up vmap in each child, and copy everything else: + let children2: Vec = children.iter().zip(offsets.iter()).map(|(c,off)| { + Child { + rule: c.rule.clone(), + xf: c.xf.clone(), + // simply add offset: + vmap: c.vmap.iter().map(|i| i + off).collect(), + } + }).collect(); + + RuleEval { + geom: mesh, + final_geom: final_geom, + children: children2, + } + } + +}