Rule now has, again, a type param
This commit is contained in:
parent
bd7105e333
commit
05a0aed8eb
@ -10,7 +10,7 @@ use crate::util;
|
|||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
fn cube_thing() -> Rule {
|
fn cube_thing() -> Rule<()> {
|
||||||
|
|
||||||
// Quarter-turn in radians:
|
// Quarter-turn in radians:
|
||||||
let qtr = std::f32::consts::FRAC_PI_2;
|
let qtr = std::f32::consts::FRAC_PI_2;
|
||||||
@ -29,7 +29,7 @@ fn cube_thing() -> Rule {
|
|||||||
id.rotate(z, -qtr),
|
id.rotate(z, -qtr),
|
||||||
];
|
];
|
||||||
|
|
||||||
let rec = move |self_: Rc<Rule>| -> RuleEval {
|
let rec = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
|
||||||
|
|
||||||
let xforms = turns.iter().map(|xf| xf.scale(0.5).translate(6.0, 0.0, 0.0));
|
let xforms = turns.iter().map(|xf| xf.scale(0.5).translate(6.0, 0.0, 0.0));
|
||||||
RuleEval {
|
RuleEval {
|
||||||
@ -43,11 +43,11 @@ fn cube_thing() -> Rule {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Rule { eval: Box::new(rec) }
|
Rule { eval: Box::new(rec), ctxt: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meant to be a copy of twist_from_gen from Python & automata_scratch
|
// Meant to be a copy of twist_from_gen from Python & automata_scratch
|
||||||
fn twist(f: f32, subdiv: usize) -> Rule {
|
fn twist(f: f32, subdiv: usize) -> Rule<()> {
|
||||||
// TODO: Clean this code up. It was a very naive conversion from
|
// TODO: Clean this code up. It was a very naive conversion from
|
||||||
// the non-closure version.
|
// the non-closure version.
|
||||||
let xf = Transform::new().rotate(&Vector3::x_axis(), -0.7);
|
let xf = Transform::new().rotate(&Vector3::x_axis(), -0.7);
|
||||||
@ -73,7 +73,7 @@ fn twist(f: f32, subdiv: usize) -> Rule {
|
|||||||
|
|
||||||
let seed2 = seed.clone();
|
let seed2 = seed.clone();
|
||||||
// TODO: Why do I need the above?
|
// TODO: Why do I need the above?
|
||||||
let recur = move |incr: Transform| -> RuleFn {
|
let recur = move |incr: Transform| -> RuleFn<()> {
|
||||||
|
|
||||||
let seed_next = incr.transform(&seed2);
|
let seed_next = incr.transform(&seed2);
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ fn twist(f: f32, subdiv: usize) -> Rule {
|
|||||||
faces: faces,
|
faces: faces,
|
||||||
});
|
});
|
||||||
|
|
||||||
let c = move |self_: Rc<Rule>| -> RuleEval {
|
let c = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
|
||||||
RuleEval {
|
RuleEval {
|
||||||
geom: geom.clone(),
|
geom: geom.clone(),
|
||||||
final_geom: final_geom.clone(),
|
final_geom: final_geom.clone(),
|
||||||
@ -103,15 +103,15 @@ fn twist(f: f32, subdiv: usize) -> Rule {
|
|||||||
// TODO: Can a macro do anything to clean up some of the
|
// TODO: Can a macro do anything to clean up some of the
|
||||||
// repetition with HOFs & closures?
|
// repetition with HOFs & closures?
|
||||||
|
|
||||||
let start = move |_| -> RuleEval {
|
let start = move |_| -> RuleEval<()> {
|
||||||
|
|
||||||
let child = |incr, dx, i, ang0, div| -> (OpenMesh, Child) {
|
let child = |incr, dx, i, ang0, div| -> (OpenMesh, Child<()>) {
|
||||||
let xform = Transform::new().
|
let xform = Transform::new().
|
||||||
rotate(&y, ang0 + (qtr / div * (i as f32))).
|
rotate(&y, ang0 + (qtr / div * (i as f32))).
|
||||||
translate(dx, 0.0, 0.0);
|
translate(dx, 0.0, 0.0);
|
||||||
|
|
||||||
let c = Child {
|
let c = Child {
|
||||||
rule: Rc::new(Rule { eval: (recur.clone())(incr) }),
|
rule: Rc::new(Rule { eval: (recur.clone())(incr), ctxt: () }),
|
||||||
// TODO: Cleanliness fix - can macros clean up above?
|
// TODO: Cleanliness fix - can macros clean up above?
|
||||||
xf: xform,
|
xf: xform,
|
||||||
vmap: (0..(n+1)).collect(),
|
vmap: (0..(n+1)).collect(),
|
||||||
@ -131,10 +131,10 @@ fn twist(f: f32, subdiv: usize) -> Rule {
|
|||||||
RuleEval::from_pairs(inner.chain(outer), prim::empty_mesh())
|
RuleEval::from_pairs(inner.chain(outer), prim::empty_mesh())
|
||||||
};
|
};
|
||||||
|
|
||||||
Rule { eval: Box::new(start) }
|
Rule { eval: Box::new(start), ctxt: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ramhorn() -> Rule {
|
fn ramhorn() -> Rule<()> {
|
||||||
|
|
||||||
let v = Unit::new_normalize(Vector3::new(-1.0, 0.0, 1.0));
|
let v = Unit::new_normalize(Vector3::new(-1.0, 0.0, 1.0));
|
||||||
let incr: Transform = Transform::new().
|
let incr: Transform = Transform::new().
|
||||||
@ -170,7 +170,7 @@ fn ramhorn() -> Rule {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
let recur = move |self_: Rc<Rule>| -> RuleEval {
|
let recur = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
|
||||||
RuleEval {
|
RuleEval {
|
||||||
geom: geom.clone(),
|
geom: geom.clone(),
|
||||||
final_geom: final_geom.clone(),
|
final_geom: final_geom.clone(),
|
||||||
@ -193,7 +193,7 @@ fn ramhorn() -> Rule {
|
|||||||
translate(0.0, 0.0, -1.0)
|
translate(0.0, 0.0, -1.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
let start = move |_| -> RuleEval {
|
let start = move |_| -> RuleEval<()> {
|
||||||
|
|
||||||
//let ofn = opening_xform.clone();
|
//let ofn = opening_xform.clone();
|
||||||
|
|
||||||
@ -244,22 +244,22 @@ fn ramhorn() -> Rule {
|
|||||||
final_geom: Rc::new(prim::empty_mesh()),
|
final_geom: Rc::new(prim::empty_mesh()),
|
||||||
children: vec![
|
children: vec![
|
||||||
Child {
|
Child {
|
||||||
rule: Rc::new(Rule { eval: Box::new(recur.clone()) }),
|
rule: Rc::new(Rule { eval: Box::new(recur.clone()), ctxt: () }),
|
||||||
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: Rc::new(Rule { eval: Box::new(recur.clone()) }),
|
rule: Rc::new(Rule { eval: Box::new(recur.clone()), ctxt: () }),
|
||||||
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: Rc::new(Rule { eval: Box::new(recur.clone()) }),
|
rule: Rc::new(Rule { eval: Box::new(recur.clone()), ctxt: () }),
|
||||||
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: Rc::new(Rule { eval: Box::new(recur.clone()) }),
|
rule: Rc::new(Rule { eval: Box::new(recur.clone()), ctxt: () }),
|
||||||
xf: opening_xform(3.0),
|
xf: opening_xform(3.0),
|
||||||
vmap: vec![6,3,7,8],
|
vmap: vec![6,3,7,8],
|
||||||
},
|
},
|
||||||
@ -272,7 +272,7 @@ fn ramhorn() -> Rule {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Rule { eval: Box::new(start) }
|
Rule { eval: Box::new(start), ctxt: () }
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -429,7 +429,7 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn run_test_iter(r: &Rc<Rule>, iters: usize, name: &str) {
|
fn run_test_iter<S>(r: &Rc<Rule<S>>, iters: usize, name: &str) {
|
||||||
println!("Running {}...", name);
|
println!("Running {}...", name);
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let n = 5;
|
let n = 5;
|
||||||
|
|||||||
38
src/rule.rs
38
src/rule.rs
@ -4,16 +4,18 @@ use crate::xform::{Transform};
|
|||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub type RuleFn = Box<dyn Fn(Rc<Rule>) -> RuleEval>;
|
pub type RuleFn<S> = Box<dyn Fn(Rc<Rule<S>>) -> RuleEval<S>>;
|
||||||
|
|
||||||
/// Definition of a rule. In general, a `Rule`:
|
/// Definition of a rule. In general, a `Rule`:
|
||||||
///
|
///
|
||||||
/// - 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 struct Rule {
|
pub struct Rule<S> {
|
||||||
pub eval: RuleFn,
|
pub eval: RuleFn<S>,
|
||||||
|
pub ctxt: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
// no function call.
|
// no function call.
|
||||||
// TODO: Do I benefit with Rc<Rule> below so Rule can be shared?
|
// TODO: Do I benefit with Rc<Rule> below so Rule can be shared?
|
||||||
@ -34,7 +36,7 @@ pub struct Rule {
|
|||||||
/// - if recursion continues, the rules of `children` are evaluated,
|
/// - if recursion continues, the rules of `children` are evaluated,
|
||||||
/// and the resultant geometry is transformed and then connected with
|
/// and the resultant geometry is transformed and then connected with
|
||||||
/// `geom`.
|
/// `geom`.
|
||||||
pub struct RuleEval {
|
pub struct RuleEval<S> {
|
||||||
/// The geometry generated at just this iteration
|
/// The geometry generated at just this iteration
|
||||||
pub geom: Rc<OpenMesh>,
|
pub geom: Rc<OpenMesh>,
|
||||||
|
|
||||||
@ -49,16 +51,16 @@ pub struct RuleEval {
|
|||||||
/// The child invocations (used if recursion continues). The
|
/// The child invocations (used if recursion continues). The
|
||||||
/// 'parent' mesh, from the perspective of all geometry produced
|
/// 'parent' mesh, from the perspective of all geometry produced
|
||||||
/// by `children`, is `geom`.
|
/// by `children`, is `geom`.
|
||||||
pub children: Vec<Child>,
|
pub children: Vec<Child<S>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Child` evaluations, pairing another `Rule` with the
|
/// `Child` evaluations, pairing another `Rule` with the
|
||||||
/// transformations and parent vertex mappings that should be applied
|
/// transformations and parent vertex mappings that should be applied
|
||||||
/// to it.
|
/// to it.
|
||||||
pub struct Child {
|
pub struct Child<S> {
|
||||||
|
|
||||||
/// Rule to evaluate to produce geometry
|
/// Rule to evaluate to produce geometry
|
||||||
pub rule: Rc<Rule>,
|
pub rule: Rc<Rule<S>>,
|
||||||
|
|
||||||
/// The transform to apply to all geometry produced by `rule`
|
/// The transform to apply to all geometry produced by `rule`
|
||||||
/// (including its own `geom` and `final_geom` if needed, as well
|
/// (including its own `geom` and `final_geom` if needed, as well
|
||||||
@ -73,7 +75,7 @@ pub struct Child {
|
|||||||
pub vmap: Vec<usize>,
|
pub vmap: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rule {
|
impl<S> Rule<S> {
|
||||||
|
|
||||||
// TODO: Do I want to make 'geom' shared somehow, maybe with Rc? I
|
// TODO: Do I want to make 'geom' shared somehow, maybe with Rc? I
|
||||||
// could end up having a lot of identical geometry that need not be
|
// could end up having a lot of identical geometry that need not be
|
||||||
@ -86,11 +88,11 @@ impl Rule {
|
|||||||
/// Convert this `Rule` to mesh data, recursively (depth first).
|
/// Convert this `Rule` to mesh data, recursively (depth first).
|
||||||
/// `iters_left` sets the maximum recursion depth. This returns
|
/// `iters_left` sets the maximum recursion depth. This returns
|
||||||
/// (geometry, number of rule evaluations).
|
/// (geometry, number of rule evaluations).
|
||||||
pub fn to_mesh(s: Rc<Self>, iters_left: u32) -> (OpenMesh, usize) {
|
pub fn to_mesh(s: Rc<Rule<S>>, iters_left: u32) -> (OpenMesh, usize) {
|
||||||
|
|
||||||
let mut evals = 1;
|
let mut evals = 1;
|
||||||
|
|
||||||
let rs: RuleEval = (s.eval)(s.clone());
|
let rs: RuleEval<S> = (s.eval)(s.clone());
|
||||||
if iters_left <= 0 {
|
if iters_left <= 0 {
|
||||||
return ((*rs.final_geom).clone(), 1);
|
return ((*rs.final_geom).clone(), 1);
|
||||||
// TODO: This is probably wrong because of the way that
|
// TODO: This is probably wrong because of the way that
|
||||||
@ -120,11 +122,11 @@ impl Rule {
|
|||||||
/// 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
|
||||||
/// function calls.
|
/// function calls.
|
||||||
pub fn to_mesh_iter(s: Rc<Self>, max_depth: usize) -> (OpenMesh, usize) {
|
pub fn to_mesh_iter(s: Rc<Rule<S>>, max_depth: usize) -> (OpenMesh, usize) {
|
||||||
|
|
||||||
struct State {
|
struct State<S> {
|
||||||
// The set of rules we're currently handling:
|
// The set of rules we're currently handling:
|
||||||
rules: Vec<Child>,
|
rules: Vec<Child<S>>,
|
||||||
// The next element of 'children' to handle:
|
// The next element of 'children' to handle:
|
||||||
next: usize,
|
next: usize,
|
||||||
// World transform of the *parent* of 'rules', that is,
|
// World transform of the *parent* of 'rules', that is,
|
||||||
@ -142,7 +144,7 @@ impl Rule {
|
|||||||
//
|
//
|
||||||
// We evaluate our own rule to initialize the stack:
|
// We evaluate our own rule to initialize the stack:
|
||||||
let eval = (s.eval)(s.clone());
|
let eval = (s.eval)(s.clone());
|
||||||
let mut stack: Vec<State> = vec![State {
|
let mut stack: Vec<State<S>> = vec![State {
|
||||||
rules: eval.children,
|
rules: eval.children,
|
||||||
next: 0,
|
next: 0,
|
||||||
xf: Transform::new(),
|
xf: Transform::new(),
|
||||||
@ -259,21 +261,21 @@ impl Rule {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuleEval {
|
impl<S> RuleEval<S> {
|
||||||
/// Turn an iterator of (OpenMesh, Child) into a single RuleEval.
|
/// Turn an iterator of (OpenMesh, Child) into a single RuleEval.
|
||||||
/// All meshes are merged, and the `vmap` in each child has the
|
/// All meshes are merged, and the `vmap` in each child has the
|
||||||
/// correct offsets applied to account for this merge.
|
/// correct offsets applied to account for this merge.
|
||||||
///
|
///
|
||||||
/// (`final_geom` is passed through to the RuleEval unmodified.)
|
/// (`final_geom` is passed through to the RuleEval unmodified.)
|
||||||
pub fn from_pairs<T, U>(m: T, final_geom: OpenMesh) -> RuleEval
|
pub fn from_pairs<T, U>(m: T, final_geom: OpenMesh) -> RuleEval<S>
|
||||||
where U: Borrow<OpenMesh>,
|
where U: Borrow<OpenMesh>,
|
||||||
T: IntoIterator<Item = (U, Child)>
|
T: IntoIterator<Item = (U, Child<S>)>
|
||||||
{
|
{
|
||||||
let (meshes, children): (Vec<_>, Vec<_>) = m.into_iter().unzip();
|
let (meshes, children): (Vec<_>, Vec<_>) = m.into_iter().unzip();
|
||||||
let (mesh, offsets) = OpenMesh::append(meshes);
|
let (mesh, offsets) = OpenMesh::append(meshes);
|
||||||
|
|
||||||
// Patch up vmap in each child, and copy everything else:
|
// Patch up vmap in each child, and copy everything else:
|
||||||
let children2: Vec<Child> = children.iter().zip(offsets.iter()).map(|(c,off)| {
|
let children2: Vec<Child<S>> = children.iter().zip(offsets.iter()).map(|(c,off)| {
|
||||||
Child {
|
Child {
|
||||||
rule: c.rule.clone(),
|
rule: c.rule.clone(),
|
||||||
xf: c.xf.clone(),
|
xf: c.xf.clone(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user