Got rid of EmptyRule

This commit is contained in:
Chris Hodapp 2020-03-06 10:36:17 -05:00
parent 7fd89b63b8
commit 94dabbee25
3 changed files with 93 additions and 129 deletions

View File

@ -11,9 +11,6 @@
## Important but less critical: ## 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? - Why must I repeat myself so much in these definitions?
- The notation for transforms is really cumbersome. Some syntactic - The notation for transforms is really cumbersome. Some syntactic
sugar might go far. sugar might go far.

View File

@ -43,12 +43,12 @@ impl CurveHorn {
final_geom: prim::empty_mesh(), final_geom: prim::empty_mesh(),
children: vec![ children: vec![
Child { Child {
rule: Rule::Recurse(Self::recur), rule: Rule { eval: Self::recur },
xf: self.id_xform, xf: self.id_xform,
vmap: vec![0,1,2,3], vmap: vec![0,1,2,3],
}, },
Child { Child {
rule: Rule::Recurse(Self::recur), rule: Rule { eval: Self::recur },
xf: self.flip180, xf: self.flip180,
vmap: vec![3,2,1,0], vmap: vec![3,2,1,0],
}, },
@ -94,7 +94,7 @@ impl CurveHorn {
final_geom: final_geom, final_geom: final_geom,
children: vec![ children: vec![
Child { Child {
rule: Rule::Recurse(Self::recur), rule: Rule { eval: Self::recur },
xf: self.incr, xf: self.incr,
vmap: vec![0,1,2,3], vmap: vec![0,1,2,3],
}, },
@ -137,7 +137,7 @@ impl CubeThing {
Matrix4::new_scaling(0.5) * Matrix4::new_scaling(0.5) *
geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous(); geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous();
Child { Child {
rule: Rule::Recurse(Self::rec), rule: Rule { eval: Self::rec },
xf: m, xf: m,
vmap: vec![], vmap: vec![],
} }
@ -217,22 +217,22 @@ impl RamHorn {
final_geom: prim::empty_mesh(), final_geom: prim::empty_mesh(),
children: vec![ children: vec![
Child { Child {
rule: Rule::Recurse(Self::ram_horn), rule: Rule { eval: Self::ram_horn },
xf: opening_xform(0.0), xf: opening_xform(0.0),
vmap: vec![5,2,6,8], vmap: vec![5,2,6,8],
}, },
Child { Child {
rule: Rule::Recurse(Self::ram_horn), rule: Rule { eval: Self::ram_horn },
xf: opening_xform(1.0), xf: opening_xform(1.0),
vmap: vec![4,1,5,8], vmap: vec![4,1,5,8],
}, },
Child { Child {
rule: Rule::Recurse(Self::ram_horn), rule: Rule { eval: Self::ram_horn },
xf: opening_xform(2.0), xf: opening_xform(2.0),
vmap: vec![7,0,4,8], vmap: vec![7,0,4,8],
}, },
Child { Child {
rule: Rule::Recurse(Self::ram_horn), rule: Rule { eval: Self::ram_horn },
xf: opening_xform(3.0), xf: opening_xform(3.0),
vmap: vec![6,3,7,8], vmap: vec![6,3,7,8],
}, },
@ -279,7 +279,7 @@ impl RamHorn {
final_geom: final_geom, final_geom: final_geom,
children: vec![ children: vec![
Child { Child {
rule: Rule::Recurse(Self::ram_horn), rule: Rule { eval: Self::ram_horn },
xf: incr, xf: incr,
vmap: vec![0,1,2,3], vmap: vec![0,1,2,3],
}, },
@ -338,7 +338,7 @@ impl Twist {
let children: Vec<Child<Twist>> = (0..self.count).map(|i| { let children: Vec<Child<Twist>> = (0..self.count).map(|i| {
let xf = xform(i); let xf = xform(i);
Child { Child {
rule: Rule::Recurse(Self::recur), rule: Rule { eval: Self::recur },
xf: xf, xf: xf,
vmap: (n*i..n*(i+self.count)).collect(), // N.B. vmap: (n*i..n*(i+self.count)).collect(), // N.B.
} }
@ -380,7 +380,7 @@ impl Twist {
final_geom: prim::empty_mesh(), // TODO: Close properly final_geom: prim::empty_mesh(), // TODO: Close properly
children: vec![ children: vec![
Child { Child {
rule: Rule::Recurse(Self::recur), rule: Rule { eval: Self::recur },
xf: incr, xf: incr,
vmap: (0..n).collect(), vmap: (0..n).collect(),
}, },
@ -406,7 +406,7 @@ pub fn main() {
fn run_test<A>(a: A, r: Rule<A>, iters: u32, name: &str) { fn run_test<A>(a: A, r: Rule<A>, iters: u32, name: &str) {
println!("Running {}...", name); println!("Running {}...", name);
let (mesh, nodes) = r.to_mesh(&a, iters); let (mesh, nodes) = r.to_mesh(&a, iters);
println!("Merged {} nodes", nodes); println!("Evaluated {} rules", nodes);
let fname = format!("{}.stl", name); let fname = format!("{}.stl", name);
println!("Writing {}...", fname); println!("Writing {}...", fname);
mesh.write_stl_file(&fname).unwrap(); mesh.write_stl_file(&fname).unwrap();
@ -415,19 +415,19 @@ pub fn main() {
fn run_test_iter<A>(a: A, r: Rule<A>, iters: usize, name: &str) { fn run_test_iter<A>(a: A, r: Rule<A>, iters: usize, name: &str) {
println!("Running {}...", name); println!("Running {}...", name);
let (mesh, nodes) = r.to_mesh_iter(&a, iters); let (mesh, nodes) = r.to_mesh_iter(&a, iters);
println!("Merged {} nodes", nodes); println!("Evaluated {} rules", nodes);
let fname = format!("{}.stl", name); let fname = format!("{}.stl", name);
println!("Writing {}...", fname); println!("Writing {}...", fname);
mesh.write_stl_file(&fname).unwrap(); 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 // this can't work on its own because the resultant OpenMesh still
// has parent references: // has parent references:
//run_test(Rule::Recurse(recur), 100, "curve_horn_thing"); //run_test(Rule { eval: recur }, 100, "curve_horn_thing");
run_test(CurveHorn::init(), Rule::Recurse(CurveHorn::start), 100, "curve_horn2"); run_test(CurveHorn::init(), Rule { eval: CurveHorn::start }, 100, "curve_horn2");
run_test(RamHorn::init(), Rule::Recurse(RamHorn::start), 200, "ram_horn"); run_test(RamHorn::init(), Rule { eval: RamHorn::start }, 200, "ram_horn");
run_test(Twist::init(), Rule::Recurse(Twist::start), 200, "twist"); 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");
} }

View File

@ -6,11 +6,8 @@ use crate::prim;
/// - produces geometry when it is evaluated /// - produces geometry when it is evaluated
/// - tells what other rules to invoke, and what to do with their /// - tells what other rules to invoke, and what to do with their
/// geometry /// geometry
pub enum Rule<A> { pub struct Rule<A> {
/// Produce some geometry, and possibly recurse further. pub eval: fn (&A) -> RuleEval<A>,
Recurse(fn (&A) -> RuleEval<A>),
/// Produce nothing and recurse no further.
EmptyRule,
} }
// TODO: Rename rules? // TODO: Rename rules?
// TODO: It may be possible to have just a 'static' rule that requires // TODO: It may be possible to have just a 'static' rule that requires
@ -84,21 +81,11 @@ impl<A> Rule<A> {
let mut evals = 1; let mut evals = 1;
let rs: RuleEval<A> = (self.eval)(arg);
if iters_left <= 0 { if iters_left <= 0 {
match self {
Rule::Recurse(f) => {
let rs: RuleEval<A> = f(arg);
return (rs.final_geom, 1); return (rs.final_geom, 1);
} }
Rule::EmptyRule => {
return (prim::empty_mesh(), evals);
}
}
}
match self {
Rule::Recurse(f) => {
let rs: RuleEval<A> = f(arg);
// TODO: This logic is more or less right, but it // TODO: This logic is more or less right, but it
// could perhaps use some un-tupling or something. // could perhaps use some un-tupling or something.
@ -116,11 +103,6 @@ impl<A> Rule<A> {
// Connect geometry from this rule (not child rules): // Connect geometry from this rule (not child rules):
return (rs.geom.connect(&subgeom).0, evals); return (rs.geom.connect(&subgeom).0, evals);
} }
Rule::EmptyRule => {
return (prim::empty_mesh(), evals);
}
}
}
/// This should be identical to to_mesh, but implemented /// This should be identical to to_mesh, but implemented
/// iteratively with an explicit stack rather than with recursive /// iteratively with an explicit stack rather than with recursive
@ -146,22 +128,14 @@ impl<A> Rule<A> {
let mut stack: Vec<State<A>> = vec![]; let mut stack: Vec<State<A>> = vec![];
let mut geom = prim::empty_mesh(); let mut geom = prim::empty_mesh();
match self {
Rule::Recurse(f) => {
// Set up the stack's initial state - evaluate our own rule // Set up the stack's initial state - evaluate our own rule
let eval = f(arg); let eval = (self.eval)(arg);
stack.push(State { stack.push(State {
rules: eval.children, rules: eval.children,
next: 0, next: 0,
xf: nalgebra::geometry::Transform3::identity().to_homogeneous(), xf: nalgebra::geometry::Transform3::identity().to_homogeneous(),
}); });
geom = eval.geom; geom = eval.geom;
},
Rule::EmptyRule => {
// Empty rule and no geometry: quit now.
return (geom, 0);
},
}
// Number of times we've evaluated a Rule: // Number of times we've evaluated a Rule:
let mut eval_count = 1; let mut eval_count = 1;
@ -193,10 +167,8 @@ impl<A> Rule<A> {
} }
let child = &s.rules[s.next]; let child = &s.rules[s.next];
match child.rule {
Rule::Recurse(f) => {
// Evaluate the rule: // Evaluate the rule:
let mut eval = f(arg); let mut eval = (child.rule.eval)(arg);
eval_count += 1; eval_count += 1;
// Make an updated world transform: // Make an updated world transform:
@ -245,11 +217,6 @@ impl<A> Rule<A> {
xf: xf, xf: xf,
}); });
n += 1; n += 1;
},
Rule::EmptyRule => {
s.next += 1;
},
}
} }
// TODO: Recursion depth? What does that even mean here? // TODO: Recursion depth? What does that even mean here?
// Maybe something more like 'branch depth'? // Maybe something more like 'branch depth'?