diff --git a/README.md b/README.md index 639c710..9fb5c6a 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,6 @@ ## Important but less critical: -- Do I actually need `EmptyRule` or can I get rid of a bunch of - extraneous nesting? `RuleEval` with empty meshes and no children is - equivalent. I have used `EmptyRule` exactly zero times. - Why must I repeat myself so much in these definitions? - The notation for transforms is really cumbersome. Some syntactic sugar might go far. diff --git a/src/examples.rs b/src/examples.rs index f481376..be3d22a 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -43,12 +43,12 @@ impl CurveHorn { final_geom: prim::empty_mesh(), children: vec![ Child { - rule: Rule::Recurse(Self::recur), + rule: Rule { eval: Self::recur }, xf: self.id_xform, vmap: vec![0,1,2,3], }, Child { - rule: Rule::Recurse(Self::recur), + rule: Rule { eval: Self::recur }, xf: self.flip180, vmap: vec![3,2,1,0], }, @@ -94,7 +94,7 @@ impl CurveHorn { final_geom: final_geom, children: vec![ Child { - rule: Rule::Recurse(Self::recur), + rule: Rule { eval: Self::recur }, xf: self.incr, vmap: vec![0,1,2,3], }, @@ -137,7 +137,7 @@ impl CubeThing { Matrix4::new_scaling(0.5) * geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous(); Child { - rule: Rule::Recurse(Self::rec), + rule: Rule { eval: Self::rec }, xf: m, vmap: vec![], } @@ -217,22 +217,22 @@ impl RamHorn { final_geom: prim::empty_mesh(), children: vec![ Child { - rule: Rule::Recurse(Self::ram_horn), + rule: Rule { eval: Self::ram_horn }, xf: opening_xform(0.0), vmap: vec![5,2,6,8], }, Child { - rule: Rule::Recurse(Self::ram_horn), + rule: Rule { eval: Self::ram_horn }, xf: opening_xform(1.0), vmap: vec![4,1,5,8], }, Child { - rule: Rule::Recurse(Self::ram_horn), + rule: Rule { eval: Self::ram_horn }, xf: opening_xform(2.0), vmap: vec![7,0,4,8], }, Child { - rule: Rule::Recurse(Self::ram_horn), + rule: Rule { eval: Self::ram_horn }, xf: opening_xform(3.0), vmap: vec![6,3,7,8], }, @@ -279,7 +279,7 @@ impl RamHorn { final_geom: final_geom, children: vec![ Child { - rule: Rule::Recurse(Self::ram_horn), + rule: Rule { eval: Self::ram_horn }, xf: incr, vmap: vec![0,1,2,3], }, @@ -338,7 +338,7 @@ impl Twist { let children: Vec> = (0..self.count).map(|i| { let xf = xform(i); Child { - rule: Rule::Recurse(Self::recur), + rule: Rule { eval: Self::recur }, xf: xf, vmap: (n*i..n*(i+self.count)).collect(), // N.B. } @@ -380,7 +380,7 @@ impl Twist { final_geom: prim::empty_mesh(), // TODO: Close properly children: vec![ Child { - rule: Rule::Recurse(Self::recur), + rule: Rule { eval: Self::recur }, xf: incr, vmap: (0..n).collect(), }, @@ -406,7 +406,7 @@ pub fn main() { fn run_test(a: A, r: Rule, iters: u32, name: &str) { println!("Running {}...", name); let (mesh, nodes) = r.to_mesh(&a, iters); - println!("Merged {} nodes", nodes); + println!("Evaluated {} rules", nodes); let fname = format!("{}.stl", name); println!("Writing {}...", fname); mesh.write_stl_file(&fname).unwrap(); @@ -415,19 +415,19 @@ pub fn main() { fn run_test_iter(a: A, r: Rule, iters: usize, name: &str) { println!("Running {}...", name); let (mesh, nodes) = r.to_mesh_iter(&a, iters); - println!("Merged {} nodes", nodes); + println!("Evaluated {} rules", nodes); let fname = format!("{}.stl", name); println!("Writing {}...", fname); mesh.write_stl_file(&fname).unwrap(); } - - run_test(CubeThing::init(), Rule::Recurse(CubeThing::rec), 3, "cube_thing"); + + run_test(CubeThing::init(), Rule { eval: CubeThing::rec }, 3, "cube_thing"); // this can't work on its own because the resultant OpenMesh still // has parent references: - //run_test(Rule::Recurse(recur), 100, "curve_horn_thing"); - run_test(CurveHorn::init(), Rule::Recurse(CurveHorn::start), 100, "curve_horn2"); - run_test(RamHorn::init(), Rule::Recurse(RamHorn::start), 200, "ram_horn"); - run_test(Twist::init(), Rule::Recurse(Twist::start), 200, "twist"); + //run_test(Rule { eval: recur }, 100, "curve_horn_thing"); + run_test(CurveHorn::init(), Rule { eval: CurveHorn::start }, 100, "curve_horn2"); + run_test(RamHorn::init(), Rule { eval: RamHorn::start }, 200, "ram_horn"); + run_test(Twist::init(), Rule { eval: Twist::start }, 200, "twist"); - run_test_iter(CurveHorn::init(), Rule::Recurse(CurveHorn::start), 100, "curve_horn2_iter"); + run_test_iter(CurveHorn::init(), Rule { eval: CurveHorn::start }, 100, "curve_horn2_iter"); } diff --git a/src/rule.rs b/src/rule.rs index b6b2887..58c0627 100644 --- a/src/rule.rs +++ b/src/rule.rs @@ -6,11 +6,8 @@ use crate::prim; /// - produces geometry when it is evaluated /// - tells what other rules to invoke, and what to do with their /// geometry -pub enum Rule { - /// Produce some geometry, and possibly recurse further. - Recurse(fn (&A) -> RuleEval), - /// Produce nothing and recurse no further. - EmptyRule, +pub struct Rule { + pub eval: fn (&A) -> RuleEval, } // TODO: Rename rules? // TODO: It may be possible to have just a 'static' rule that requires @@ -84,42 +81,27 @@ impl Rule { let mut evals = 1; + let rs: RuleEval = (self.eval)(arg); if iters_left <= 0 { - match self { - Rule::Recurse(f) => { - let rs: RuleEval = f(arg); - return (rs.final_geom, 1); - } - Rule::EmptyRule => { - return (prim::empty_mesh(), evals); - } - } + return (rs.final_geom, 1); } - match self { - Rule::Recurse(f) => { - let rs: RuleEval = f(arg); - // TODO: This logic is more or less right, but it - // could perhaps use some un-tupling or something. + // TODO: This logic is more or less right, but it + // could perhaps use some un-tupling or something. - let subgeom: Vec<(OpenMesh, &Vec)> = rs.children.iter().map(|sub| { - // Get sub-geometry (still un-transformed): - let (submesh, eval) = sub.rule.to_mesh(arg, iters_left - 1); - // Tally up eval count: - evals += eval; - - let m2 = submesh.transform(&sub.xf); - - (m2, &sub.vmap) - }).collect(); - - // Connect geometry from this rule (not child rules): - return (rs.geom.connect(&subgeom).0, evals); - } - Rule::EmptyRule => { - return (prim::empty_mesh(), evals); - } - } + let subgeom: Vec<(OpenMesh, &Vec)> = rs.children.iter().map(|sub| { + // Get sub-geometry (still un-transformed): + let (submesh, eval) = sub.rule.to_mesh(arg, iters_left - 1); + // Tally up eval count: + evals += eval; + + let m2 = submesh.transform(&sub.xf); + + (m2, &sub.vmap) + }).collect(); + + // Connect geometry from this rule (not child rules): + return (rs.geom.connect(&subgeom).0, evals); } /// This should be identical to to_mesh, but implemented @@ -146,22 +128,14 @@ impl Rule { let mut stack: Vec> = vec![]; let mut geom = prim::empty_mesh(); - match self { - Rule::Recurse(f) => { - // Set up the stack's initial state - evaluate our own rule - let eval = f(arg); - stack.push(State { - rules: eval.children, - next: 0, - xf: nalgebra::geometry::Transform3::identity().to_homogeneous(), - }); - geom = eval.geom; - }, - Rule::EmptyRule => { - // Empty rule and no geometry: quit now. - return (geom, 0); - }, - } + // Set up the stack's initial state - evaluate our own rule + let eval = (self.eval)(arg); + stack.push(State { + rules: eval.children, + next: 0, + xf: nalgebra::geometry::Transform3::identity().to_homogeneous(), + }); + geom = eval.geom; // Number of times we've evaluated a Rule: let mut eval_count = 1; @@ -193,63 +167,56 @@ impl Rule { } let child = &s.rules[s.next]; - match child.rule { - Rule::Recurse(f) => { - // Evaluate the rule: - let mut eval = f(arg); - eval_count += 1; + // Evaluate the rule: + let mut eval = (child.rule.eval)(arg); + eval_count += 1; - // Make an updated world transform: - let xf = s.xf * child.xf; // TODO: Check order on this + // Make an updated world transform: + let xf = s.xf * child.xf; // TODO: Check order on this - // This rule produced some geometry which we'll - // combine with the 'global' geometry: - let new_geom = eval.geom.transform(&xf); - println!("DEBUG: Connecting {} faces, vmap={:?}, faces={:?}", - new_geom.verts.len(), child.vmap, new_geom.faces); - let (g, offsets) = geom.connect(&vec![(new_geom, &child.vmap)]); - geom = g; + // This rule produced some geometry which we'll + // combine with the 'global' geometry: + let new_geom = eval.geom.transform(&xf); + println!("DEBUG: Connecting {} faces, vmap={:?}, faces={:?}", + new_geom.verts.len(), child.vmap, new_geom.faces); + let (g, offsets) = geom.connect(&vec![(new_geom, &child.vmap)]); + geom = g; - // 'new_geom' may itself be parent geometry for - // something in 'eval.children' (via Tag::Parent), - // and vmap is there to resolve those Tag::Parent - // references to the right vertices in 'new_geom'. - // - // However, we connect() on the global geometry - // which we merged 'new_geom' into, not 'new_geom' - // directly. To account for this, we must shift - // vmap by the offset that 'geom.connect' gave us: - for (offset, child) in offsets.iter().zip(eval.children.iter_mut()) { - child.vmap = child.vmap.iter().map(|n| { - n + offset - }).collect(); - } - - // TODO: Why does below work? - if (s.next + 1) >= s.rules.len() { - let m = stack.len(); - if m >= 2 { - stack[m-2].next += 1; - } - stack.pop(); - n -= 1; - } - // I guess we are "done" with the rule after we've - // evaluated it, and it is then safe to increment - // s.next. - - // Recurse further (i.e. put more onto stack): - stack.push(State { - rules: eval.children, - next: 0, - xf: xf, - }); - n += 1; - }, - Rule::EmptyRule => { - s.next += 1; - }, + // 'new_geom' may itself be parent geometry for + // something in 'eval.children' (via Tag::Parent), + // and vmap is there to resolve those Tag::Parent + // references to the right vertices in 'new_geom'. + // + // However, we connect() on the global geometry + // which we merged 'new_geom' into, not 'new_geom' + // directly. To account for this, we must shift + // vmap by the offset that 'geom.connect' gave us: + for (offset, child) in offsets.iter().zip(eval.children.iter_mut()) { + child.vmap = child.vmap.iter().map(|n| { + n + offset + }).collect(); } + + // TODO: Why does below work? + if (s.next + 1) >= s.rules.len() { + let m = stack.len(); + if m >= 2 { + stack[m-2].next += 1; + } + stack.pop(); + n -= 1; + } + // I guess we are "done" with the rule after we've + // evaluated it, and it is then safe to increment + // s.next. + + // Recurse further (i.e. put more onto stack): + stack.push(State { + rules: eval.children, + next: 0, + xf: xf, + }); + n += 1; } // TODO: Recursion depth? What does that even mean here? // Maybe something more like 'branch depth'?