Add some more docs
This commit is contained in:
parent
2fe37fc569
commit
7fd89b63b8
10
README.md
10
README.md
@ -2,12 +2,18 @@
|
|||||||
|
|
||||||
## Highest priority:
|
## Highest priority:
|
||||||
|
|
||||||
|
- Continue fixing `to_mesh_iter`, which still doesn't yet handle
|
||||||
|
branching because (for one thing) it never tracks depth properly in
|
||||||
|
order to backtrack.
|
||||||
- Clean up `twist` - maybe make a struct or trait.
|
- Clean up `twist` - maybe make a struct or trait.
|
||||||
- Do transforms compose in the *reverse* of automata_scratch? This
|
- Do transforms compose in the *reverse* of automata_scratch? This
|
||||||
appears to be the case.
|
appears to be the case.
|
||||||
|
|
||||||
## 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.
|
||||||
@ -15,10 +21,6 @@
|
|||||||
the clockwise boundaries, the zigzag connections, the iterating over
|
the clockwise boundaries, the zigzag connections, the iterating over
|
||||||
a `Vec<Vertex>` to transform each element and make another vector.
|
a `Vec<Vertex>` to transform each element and make another vector.
|
||||||
- Docs on modules
|
- Docs on modules
|
||||||
- Consider making `to_mesh` iterative. My call stack seems needlessly
|
|
||||||
deep in spots - especially in rules which do not branch. Sections
|
|
||||||
like this should be manageable with just iteration that does not
|
|
||||||
grow anything in size.
|
|
||||||
- Grep for all TODOs in code, really.
|
- Grep for all TODOs in code, really.
|
||||||
- Look at everything in README.md in automata_scratch.
|
- Look at everything in README.md in automata_scratch.
|
||||||
- Implement some of the tougher examples from the above too, e.g. the
|
- Implement some of the tougher examples from the above too, e.g. the
|
||||||
|
|||||||
@ -98,7 +98,13 @@ impl OpenMesh {
|
|||||||
|
|
||||||
/// Treat this mesh as a 'parent' mesh to connect with any number
|
/// Treat this mesh as a 'parent' mesh to connect with any number
|
||||||
/// of 'child' meshes, all of them paired with their respective
|
/// of 'child' meshes, all of them paired with their respective
|
||||||
/// parent vertex mappings. This returns a new mesh.
|
/// parent vertex mappings. This returns a tuple of (new mesh,
|
||||||
|
/// offsets), where 'offsets' gives the offset of where child
|
||||||
|
/// meshes were shifted in the new mesh.
|
||||||
|
///
|
||||||
|
/// That is, the vertices of 'children[i]' begin at vertex
|
||||||
|
/// 'offset[i]' of the new mesh. This is needed in some cases for
|
||||||
|
/// adjusting a parent vertex mapping, like 'vmap' of Rule::Child.
|
||||||
pub fn connect(&self, children: &Vec<(OpenMesh, &Vec<usize>)>) -> (OpenMesh, Vec<usize>) {
|
pub fn connect(&self, children: &Vec<(OpenMesh, &Vec<usize>)>) -> (OpenMesh, Vec<usize>) {
|
||||||
// TODO: Clean up this description a bit
|
// TODO: Clean up this description a bit
|
||||||
// TODO: Clean up Vec<usize> stuff
|
// TODO: Clean up Vec<usize> stuff
|
||||||
|
|||||||
96
src/rule.rs
96
src/rule.rs
@ -80,9 +80,9 @@ impl<A> Rule<A> {
|
|||||||
/// 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(&self, arg: &A, iters_left: u32) -> (OpenMesh, u32) {
|
pub fn to_mesh(&self, arg: &A, iters_left: u32) -> (OpenMesh, usize) {
|
||||||
|
|
||||||
let mut evals: u32 = 1;
|
let mut evals = 1;
|
||||||
|
|
||||||
if iters_left <= 0 {
|
if iters_left <= 0 {
|
||||||
match self {
|
match self {
|
||||||
@ -122,7 +122,10 @@ impl<A> Rule<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_mesh_iter(&self, arg: &A, max_depth: usize) -> (OpenMesh, u32) {
|
/// This should be identical to to_mesh, but implemented
|
||||||
|
/// iteratively with an explicit stack rather than with recursive
|
||||||
|
/// function calls.
|
||||||
|
pub fn to_mesh_iter(&self, arg: &A, max_depth: usize) -> (OpenMesh, usize) {
|
||||||
|
|
||||||
struct State<A> {
|
struct State<A> {
|
||||||
// The set of rules we're currently handling:
|
// The set of rules we're currently handling:
|
||||||
@ -135,55 +138,55 @@ impl<A> Rule<A> {
|
|||||||
xf: Mat4,
|
xf: Mat4,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut geom = prim::empty_mesh();
|
// 'stack' stores at its last element our "current" State in
|
||||||
|
// terms of a current world transform and which Child should
|
||||||
|
// be processed next. Every element prior to this is previous
|
||||||
|
// states which must be kept around for further backtracking
|
||||||
|
// (usually because they involve multiple rules).
|
||||||
let mut stack: Vec<State<A>> = vec![];
|
let mut stack: Vec<State<A>> = vec![];
|
||||||
|
let mut geom = prim::empty_mesh();
|
||||||
|
|
||||||
// Set up starting state:
|
|
||||||
match self {
|
match self {
|
||||||
Rule::Recurse(f) => {
|
Rule::Recurse(f) => {
|
||||||
|
// Set up the stack's initial state - evaluate our own rule
|
||||||
let eval = f(arg);
|
let eval = f(arg);
|
||||||
let s = 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(),
|
||||||
};
|
});
|
||||||
stack.push(s);
|
|
||||||
geom = eval.geom;
|
geom = eval.geom;
|
||||||
},
|
},
|
||||||
Rule::EmptyRule => {
|
Rule::EmptyRule => {
|
||||||
// No geometry and nowhere to recurse...
|
// Empty rule and no geometry: quit now.
|
||||||
return (geom, 0);
|
return (geom, 0);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut count = 0;
|
// Number of times we've evaluated a Rule:
|
||||||
|
let mut eval_count = 1;
|
||||||
|
|
||||||
|
// Stack depth (update at every push & pop):
|
||||||
|
let mut n = stack.len();
|
||||||
|
|
||||||
// TODO: Why is this forcing it to build up the entire stack
|
|
||||||
// to the recursion limit, and *then* start generating
|
|
||||||
// geometry and winding the stack back down?
|
|
||||||
//
|
|
||||||
// Isn't there some way that it can generate geometry at every
|
|
||||||
// step and only have to add to the stack to handle a branch?
|
|
||||||
while !stack.is_empty() {
|
while !stack.is_empty() {
|
||||||
|
|
||||||
// TODO: This, more elegantly?
|
// TODO: This, more elegantly?
|
||||||
count += 1;
|
if eval_count > max_depth {
|
||||||
if count > max_depth {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = stack.len(); // TODO: Just keep a running total.
|
|
||||||
println!("DEBUG: stack has len {}", n);
|
println!("DEBUG: stack has len {}", n);
|
||||||
// We can increment/decrement as we push/pop.
|
|
||||||
let s = &mut stack[n-1];
|
let s = &mut stack[n-1];
|
||||||
|
|
||||||
if s.next >= s.rules.len() {
|
if s.next >= s.rules.len() {
|
||||||
// If we've run out of child rules, backtrack:
|
// If we've run out of child rules, have the *parent* node (if one) move on:
|
||||||
stack.pop();
|
|
||||||
// and have the *parent* node (if one) move on:
|
|
||||||
if n >= 2 {
|
if n >= 2 {
|
||||||
stack[n-2].next += 1;
|
stack[n-2].next += 1;
|
||||||
}
|
}
|
||||||
|
// and backtrack:
|
||||||
|
stack.pop();
|
||||||
|
n -= 1;
|
||||||
// (if there isn't one, it makes no difference,
|
// (if there isn't one, it makes no difference,
|
||||||
// because the loop will end)
|
// because the loop will end)
|
||||||
continue;
|
continue;
|
||||||
@ -194,30 +197,54 @@ impl<A> Rule<A> {
|
|||||||
Rule::Recurse(f) => {
|
Rule::Recurse(f) => {
|
||||||
// Evaluate the rule:
|
// Evaluate the rule:
|
||||||
let mut eval = f(arg);
|
let mut eval = f(arg);
|
||||||
|
eval_count += 1;
|
||||||
|
|
||||||
// Compose child transform to new 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
|
||||||
|
// 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;
|
||||||
|
|
||||||
// Adjust vmap in all of eval.children:
|
// 'new_geom' may itself be parent geometry for
|
||||||
for (i,offset) in offsets.iter().enumerate() {
|
// something in 'eval.children' (via Tag::Parent),
|
||||||
eval.children[i].vmap = eval.children[i].vmap.iter().map(|n| n + offset).collect();
|
// 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: Explain this better
|
|
||||||
|
// 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):
|
// Recurse further (i.e. put more onto stack):
|
||||||
let s2 = State {
|
stack.push(State {
|
||||||
rules: eval.children,
|
rules: eval.children,
|
||||||
next: 0,
|
next: 0,
|
||||||
xf: xf,
|
xf: xf,
|
||||||
};
|
});
|
||||||
stack.push(s2);
|
n += 1;
|
||||||
|
|
||||||
},
|
},
|
||||||
Rule::EmptyRule => {
|
Rule::EmptyRule => {
|
||||||
s.next += 1;
|
s.next += 1;
|
||||||
@ -229,10 +256,7 @@ impl<A> Rule<A> {
|
|||||||
|
|
||||||
// TODO: Handle final_geom
|
// TODO: Handle final_geom
|
||||||
|
|
||||||
// TODO: Return right number
|
return (geom, eval_count);
|
||||||
return (geom, 0);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user