Got rid of EmptyRule
This commit is contained in:
parent
7fd89b63b8
commit
94dabbee25
@ -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.
|
||||||
|
|||||||
@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
173
src/rule.rs
173
src/rule.rs
@ -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,42 +81,27 @@ 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 {
|
return (rs.final_geom, 1);
|
||||||
Rule::Recurse(f) => {
|
|
||||||
let rs: RuleEval<A> = f(arg);
|
|
||||||
return (rs.final_geom, 1);
|
|
||||||
}
|
|
||||||
Rule::EmptyRule => {
|
|
||||||
return (prim::empty_mesh(), evals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
// TODO: This logic is more or less right, but it
|
||||||
Rule::Recurse(f) => {
|
// could perhaps use some un-tupling or something.
|
||||||
let rs: RuleEval<A> = f(arg);
|
|
||||||
// TODO: This logic is more or less right, but it
|
|
||||||
// could perhaps use some un-tupling or something.
|
|
||||||
|
|
||||||
let subgeom: Vec<(OpenMesh, &Vec<usize>)> = rs.children.iter().map(|sub| {
|
let subgeom: Vec<(OpenMesh, &Vec<usize>)> = rs.children.iter().map(|sub| {
|
||||||
// Get sub-geometry (still un-transformed):
|
// Get sub-geometry (still un-transformed):
|
||||||
let (submesh, eval) = sub.rule.to_mesh(arg, iters_left - 1);
|
let (submesh, eval) = sub.rule.to_mesh(arg, iters_left - 1);
|
||||||
// Tally up eval count:
|
// Tally up eval count:
|
||||||
evals += eval;
|
evals += eval;
|
||||||
|
|
||||||
let m2 = submesh.transform(&sub.xf);
|
let m2 = submesh.transform(&sub.xf);
|
||||||
|
|
||||||
(m2, &sub.vmap)
|
(m2, &sub.vmap)
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
// 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
|
||||||
@ -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 {
|
// Set up the stack's initial state - evaluate our own rule
|
||||||
Rule::Recurse(f) => {
|
let eval = (self.eval)(arg);
|
||||||
// Set up the stack's initial state - evaluate our own rule
|
stack.push(State {
|
||||||
let eval = f(arg);
|
rules: eval.children,
|
||||||
stack.push(State {
|
next: 0,
|
||||||
rules: eval.children,
|
xf: nalgebra::geometry::Transform3::identity().to_homogeneous(),
|
||||||
next: 0,
|
});
|
||||||
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,63 +167,56 @@ impl<A> Rule<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let child = &s.rules[s.next];
|
let child = &s.rules[s.next];
|
||||||
match child.rule {
|
// Evaluate the rule:
|
||||||
Rule::Recurse(f) => {
|
let mut eval = (child.rule.eval)(arg);
|
||||||
// Evaluate the rule:
|
eval_count += 1;
|
||||||
let mut eval = f(arg);
|
|
||||||
eval_count += 1;
|
|
||||||
|
|
||||||
// Make an updated world transform:
|
// Make an updated world transform:
|
||||||
let xf = s.xf * child.xf; // TODO: Check order on this
|
let xf = s.xf * child.xf; // TODO: Check order on this
|
||||||
|
|
||||||
// This rule produced some geometry which we'll
|
// This rule produced some geometry which we'll
|
||||||
// combine with the 'global' geometry:
|
// combine with the 'global' geometry:
|
||||||
let new_geom = eval.geom.transform(&xf);
|
let new_geom = eval.geom.transform(&xf);
|
||||||
println!("DEBUG: Connecting {} faces, vmap={:?}, faces={:?}",
|
println!("DEBUG: Connecting {} faces, vmap={:?}, faces={:?}",
|
||||||
new_geom.verts.len(), child.vmap, new_geom.faces);
|
new_geom.verts.len(), child.vmap, new_geom.faces);
|
||||||
let (g, offsets) = geom.connect(&vec![(new_geom, &child.vmap)]);
|
let (g, offsets) = geom.connect(&vec![(new_geom, &child.vmap)]);
|
||||||
geom = g;
|
geom = g;
|
||||||
|
|
||||||
// 'new_geom' may itself be parent geometry for
|
// 'new_geom' may itself be parent geometry for
|
||||||
// something in 'eval.children' (via Tag::Parent),
|
// something in 'eval.children' (via Tag::Parent),
|
||||||
// and vmap is there to resolve those Tag::Parent
|
// and vmap is there to resolve those Tag::Parent
|
||||||
// references to the right vertices in 'new_geom'.
|
// references to the right vertices in 'new_geom'.
|
||||||
//
|
//
|
||||||
// However, we connect() on the global geometry
|
// However, we connect() on the global geometry
|
||||||
// which we merged 'new_geom' into, not 'new_geom'
|
// which we merged 'new_geom' into, not 'new_geom'
|
||||||
// directly. To account for this, we must shift
|
// directly. To account for this, we must shift
|
||||||
// vmap by the offset that 'geom.connect' gave us:
|
// vmap by the offset that 'geom.connect' gave us:
|
||||||
for (offset, child) in offsets.iter().zip(eval.children.iter_mut()) {
|
for (offset, child) in offsets.iter().zip(eval.children.iter_mut()) {
|
||||||
child.vmap = child.vmap.iter().map(|n| {
|
child.vmap = child.vmap.iter().map(|n| {
|
||||||
n + offset
|
n + offset
|
||||||
}).collect();
|
}).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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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?
|
// TODO: Recursion depth? What does that even mean here?
|
||||||
// Maybe something more like 'branch depth'?
|
// Maybe something more like 'branch depth'?
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user