Change name of 'vmap' to 'arg_vals' to fit with MeshFunc

This commit is contained in:
Chris Hodapp 2020-05-13 15:02:22 -04:00
parent ce3ca34b70
commit b7da8bea41
4 changed files with 84 additions and 88 deletions

View File

@ -6,18 +6,8 @@
transformations so that I'm actually constructing things to be transformations so that I'm actually constructing things to be
correct instead of just throwing shit at the wall. correct instead of just throwing shit at the wall.
See my "Composing Transformations" link in log.org. See my "Composing Transformations" link in log.org.
- My "Barbs" example revealed another pesky limitation: a parent vertex
cannot refer to a parent vertex of the parent itself. This came up
because I had a rule inheriting 4 vertices (one side of a cube), and
creating 4 new vertices (the opposite side of a cube). I wanted its
child rules to be able to create faces that had 2 vertices of the
parent and 2 vertices that the parent inherited (basically grandparent
vertices) - think of one of the remaining 4 sides of the cube. I have
no way to do this and no easy workarounds I can see, given that the
rule does not have access to the exact vertex positions (so just making
new vertices that are 'close' and connecting them isn't an option).
- Adaptive subdivision - which means having to generalize past some - Adaptive subdivision - which means having to generalize past some
`vmap` stuff. `arg_vals` stuff.
- Try some non-deterministic examples. - Try some non-deterministic examples.
- Get identical or near-identical meshes to `ramhorn_branch` from - Get identical or near-identical meshes to `ramhorn_branch` from
Python. (Should just be a matter of tweaking parameters.) Python. (Should just be a matter of tweaking parameters.)

View File

@ -39,7 +39,7 @@ pub fn cube_thing() -> Rule<()> {
children: xforms.map(move |xf| Child { children: xforms.map(move |xf| Child {
rule: self_.clone(), rule: self_.clone(),
xf: xf, xf: xf,
vmap: vec![], arg_vals: vec![],
}).collect(), }).collect(),
} }
}; };
@ -85,7 +85,7 @@ pub fn barbs() -> Rule<()> {
Child { Child {
rule: self_.clone(), rule: self_.clone(),
xf: barb_incr, xf: barb_incr,
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
} }
] ]
} }
@ -128,27 +128,27 @@ pub fn barbs() -> Rule<()> {
Child { Child {
rule: self_.clone(), rule: self_.clone(),
xf: main_incr, xf: main_incr,
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
}, },
Child { Child {
rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }),
xf: main_barb_trans(0), xf: main_barb_trans(0),
vmap: vec![b0 + 0, b0 + 1, a0 + 1, a0 + 0], arg_vals: vec![b0 + 0, b0 + 1, a0 + 1, a0 + 0],
}, },
Child { Child {
rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }),
xf: main_barb_trans(1), xf: main_barb_trans(1),
vmap: vec![b0 + 1, b0 + 2, a0 + 2, a0 + 1], arg_vals: vec![b0 + 1, b0 + 2, a0 + 2, a0 + 1],
}, },
Child { Child {
rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }),
xf: main_barb_trans(2), xf: main_barb_trans(2),
vmap: vec![b0 + 2, b0 + 3, a0 + 3, a0 + 2], arg_vals: vec![b0 + 2, b0 + 3, a0 + 3, a0 + 2],
}, },
Child { Child {
rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }),
xf: main_barb_trans(3), xf: main_barb_trans(3),
vmap: vec![b0 + 3, b0 + 0, a0 + 0, a0 + 3], arg_vals: vec![b0 + 3, b0 + 0, a0 + 0, a0 + 3],
}, },
// TODO: Factor out repetition // TODO: Factor out repetition
], ],
@ -171,7 +171,7 @@ pub fn barbs() -> Rule<()> {
Child { Child {
rule: Rc::new(Rule { eval: main_.clone(), ctxt: () }), rule: Rc::new(Rule { eval: main_.clone(), ctxt: () }),
xf: Transform::new(), xf: Transform::new(),
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
}, },
], ],
} }
@ -233,7 +233,7 @@ pub fn twist(f: f32, subdiv: usize) -> Rule<()> {
Child { Child {
rule: self_.clone(), rule: self_.clone(),
xf: incr, xf: incr,
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
}, },
], ],
} }
@ -254,7 +254,7 @@ pub fn twist(f: f32, subdiv: usize) -> Rule<()> {
rule: Rc::new(Rule { eval: (recur.clone())(incr), ctxt: () }), 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(), arg_vals: (0..(n+1)).collect(),
// N.B. n+1, not n. the +1 is for the centroid below. // N.B. n+1, not n. the +1 is for the centroid below.
}; };
let mut vs = xform.transform(&seed); let mut vs = xform.transform(&seed);
@ -334,7 +334,7 @@ pub fn nest_spiral_2() -> Rule<NestSpiral2Ctxt> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: Transform::new(), xf: Transform::new(),
vmap: (0..n2).collect(), arg_vals: (0..n2).collect(),
}, },
], ],
} }
@ -346,7 +346,7 @@ pub fn nest_spiral_2() -> Rule<NestSpiral2Ctxt> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: Transform::new(), xf: Transform::new(),
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
}, },
], ],
} }
@ -372,7 +372,7 @@ pub fn nest_spiral_2() -> Rule<NestSpiral2Ctxt> {
}, },
}), }),
xf: Transform::new(), xf: Transform::new(),
vmap: vec![], // no parent vertices arg_vals: vec![], // no parent vertices
} }
}; };
@ -463,7 +463,7 @@ pub fn twisty_torus() -> Rule<TorusCtxt> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: Transform::new(), xf: Transform::new(),
vmap: (0..n2).collect(), arg_vals: (0..n2).collect(),
}, },
], ],
} }
@ -475,7 +475,7 @@ pub fn twisty_torus() -> Rule<TorusCtxt> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: Transform::new(), xf: Transform::new(),
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
}, },
], ],
} }
@ -538,7 +538,7 @@ pub fn twisty_torus_hardcode() -> Rule<()> {
Child { Child {
rule: self_.clone(), rule: self_.clone(),
xf: incr, xf: incr,
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
}, },
], ],
} }
@ -557,7 +557,7 @@ pub fn twisty_torus_hardcode() -> Rule<()> {
Child { Child {
rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }), rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }),
xf: incr, xf: incr,
vmap: (0..n2).collect(), arg_vals: (0..n2).collect(),
}, },
], ],
} }
@ -633,7 +633,7 @@ pub fn wind_chime_mistake_thing() -> Rule<WindChimeCtxt> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: Transform::new(), xf: Transform::new(),
vmap: (0..n2).collect(), arg_vals: (0..n2).collect(),
}, },
], ],
} }
@ -645,7 +645,7 @@ pub fn wind_chime_mistake_thing() -> Rule<WindChimeCtxt> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: Transform::new(), xf: Transform::new(),
vmap: (0..n).collect(), arg_vals: (0..n).collect(),
}, },
], ],
} }
@ -712,7 +712,7 @@ pub fn ramhorn() -> Rule<()> {
Child { Child {
rule: self_.clone(), rule: self_.clone(),
xf: incr, xf: incr,
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
], ],
} }
@ -779,22 +779,22 @@ pub fn ramhorn() -> Rule<()> {
Child { Child {
rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }), rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }),
xf: opening_xform(0.0), xf: opening_xform(0.0),
vmap: vec![5,2,6,8], arg_vals: vec![5,2,6,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }), rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }),
xf: opening_xform(1.0), xf: opening_xform(1.0),
vmap: vec![4,1,5,8], arg_vals: vec![4,1,5,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }), rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }),
xf: opening_xform(2.0), xf: opening_xform(2.0),
vmap: vec![7,0,4,8], arg_vals: vec![7,0,4,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }), rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }),
xf: opening_xform(3.0), xf: opening_xform(3.0),
vmap: vec![6,3,7,8], arg_vals: vec![6,3,7,8],
}, },
// TODO: These vertex mappings appear to be right. // TODO: These vertex mappings appear to be right.
// Explain *why* they are right. // Explain *why* they are right.
@ -893,22 +893,22 @@ pub fn ramhorn_branch(depth: usize, f: f32) -> Rule<RamHornCtxt> {
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(0.0), xf: opening_xform(0.0),
vmap: vec![5,2,6,8], arg_vals: vec![5,2,6,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(1.0), xf: opening_xform(1.0),
vmap: vec![4,1,5,8], arg_vals: vec![4,1,5,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(2.0), xf: opening_xform(2.0),
vmap: vec![7,0,4,8], arg_vals: vec![7,0,4,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(3.0), xf: opening_xform(3.0),
vmap: vec![6,3,7,8], arg_vals: vec![6,3,7,8],
}, },
// TODO: These vertex mappings appear to be right. // TODO: These vertex mappings appear to be right.
// Explain *why* they are right. // Explain *why* they are right.
@ -940,7 +940,7 @@ pub fn ramhorn_branch(depth: usize, f: f32) -> Rule<RamHornCtxt> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: incr, xf: incr,
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
], ],
} }
@ -970,7 +970,7 @@ pub fn ramhorn_branch(depth: usize, f: f32) -> Rule<RamHornCtxt> {
Child { Child {
rule: Rc::new(Rule { eval: Rc::new(trans.clone()), ctxt: self_.ctxt }), rule: Rc::new(Rule { eval: Rc::new(trans.clone()), ctxt: self_.ctxt }),
xf: Transform::new(), xf: Transform::new(),
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
], ],
} }
@ -1066,22 +1066,22 @@ pub fn ramhorn_branch_random(depth: usize, f: f32) -> Rule<RamHornCtxt2> {
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(0.0), xf: opening_xform(0.0),
vmap: vec![5,2,6,8], arg_vals: vec![5,2,6,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(1.0), xf: opening_xform(1.0),
vmap: vec![4,1,5,8], arg_vals: vec![4,1,5,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(2.0), xf: opening_xform(2.0),
vmap: vec![7,0,4,8], arg_vals: vec![7,0,4,8],
}, },
Child { Child {
rule: Rc::new(Rule { eval: recur.clone(), ctxt }), rule: Rc::new(Rule { eval: recur.clone(), ctxt }),
xf: opening_xform(3.0), xf: opening_xform(3.0),
vmap: vec![6,3,7,8], arg_vals: vec![6,3,7,8],
}, },
// TODO: These vertex mappings appear to be right. // TODO: These vertex mappings appear to be right.
// Explain *why* they are right. // Explain *why* they are right.
@ -1114,7 +1114,7 @@ pub fn ramhorn_branch_random(depth: usize, f: f32) -> Rule<RamHornCtxt2> {
Child { Child {
rule: Rc::new(next_rule), rule: Rc::new(next_rule),
xf: incr, xf: incr,
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
], ],
} }
@ -1144,7 +1144,7 @@ pub fn ramhorn_branch_random(depth: usize, f: f32) -> Rule<RamHornCtxt2> {
Child { Child {
rule: Rc::new(Rule { eval: Rc::new(trans.clone()), ctxt: self_.ctxt }), rule: Rc::new(Rule { eval: Rc::new(trans.clone()), ctxt: self_.ctxt }),
xf: Transform::new(), xf: Transform::new(),
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
], ],
} }
@ -1178,7 +1178,7 @@ impl CurveHorn {
Child { Child {
rule: Rule { eval: Rc::new(move || self.do_nothing()) }, rule: Rule { eval: Rc::new(move || self.do_nothing()) },
xf: self.id_xform, xf: self.id_xform,
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
], ],
} }
@ -1215,12 +1215,12 @@ impl CurveHorn {
Child { Child {
rule: Rule { eval: Rc::new(move || self.recur()) }, rule: Rule { eval: Rc::new(move || self.recur()) },
xf: self.id_xform, xf: self.id_xform,
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
Child { Child {
rule: Rule { eval: Rc::new(move || self.recur()) }, rule: Rule { eval: Rc::new(move || self.recur()) },
xf: self.flip180, xf: self.flip180,
vmap: vec![3,2,1,0], arg_vals: vec![3,2,1,0],
}, },
], ],
} }
@ -1266,7 +1266,7 @@ impl CurveHorn {
Child { Child {
rule: Rule { eval: Rc::new(move || self.recur()) }, rule: Rule { eval: Rc::new(move || self.recur()) },
xf: self.incr, xf: self.incr,
vmap: vec![0,1,2,3], arg_vals: vec![0,1,2,3],
}, },
], ],
} }

View File

@ -74,8 +74,11 @@ impl Mesh {
pub enum VertexUnion { pub enum VertexUnion {
/// A concrete vertex. /// A concrete vertex.
Vertex(Vertex), Vertex(Vertex),
/// An 'unbound' vertex - something like an argument to a function with /// A vertex argument - something like an argument to a function with
/// the given positional index. /// the given positional index.
///
/// The job of `MeshFunc.connect` is to bind these arguments to concrete
/// vertices.
Arg(usize), Arg(usize),
} }
@ -155,13 +158,15 @@ impl MeshFunc {
/// 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 tuple of (new mesh, /// vertex argument values (i.e. `arg_vals` from `Child`).
/// offsets), where 'offsets' gives the offset of where child /// This returns a tuple of (new mesh, offsets), where 'offsets'
/// meshes were shifted in the new mesh. /// gives the offset of where child meshes were shifted in the new
/// mesh.
/// ///
/// That is, the vertices of 'children[i]' begin at vertex /// That is, the vertices of 'children[i]' begin at vertex
/// 'offset[i]' of the new mesh. This is needed in some cases for /// 'offset[i]' of the new mesh. This is needed in order to adjust
/// adjusting a parent vertex mapping, like 'vmap' of Rule::Child. /// references to vertices of a mesh in 'children' - such as
/// 'arg_vals' of `rule::Child`.
pub fn connect<T, U>(&self, children: T) -> (MeshFunc, Vec<usize>) pub fn connect<T, U>(&self, children: T) -> (MeshFunc, Vec<usize>)
where U: Borrow<MeshFunc>, where U: Borrow<MeshFunc>,
T: IntoIterator<Item = (U, Vec<usize>)> T: IntoIterator<Item = (U, Vec<usize>)>

View File

@ -46,6 +46,8 @@ pub struct RuleEval<S> {
/// ///
/// Parent vertex references will be resolved directly to `geom` /// Parent vertex references will be resolved directly to `geom`
/// with no mapping. /// with no mapping.
/// (TODO: Does this make sense? Nowhere else do I treat Arg(n) as
/// an index - it's always a positional argument.)
pub final_geom: Rc<MeshFunc>, pub final_geom: Rc<MeshFunc>,
/// The child invocations (used if recursion continues). The /// The child invocations (used if recursion continues). The
@ -67,12 +69,15 @@ pub struct Child<S> {
/// as all sub-geometry produced recursively). /// as all sub-geometry produced recursively).
pub xf: Transform, pub xf: Transform,
/// The parent vertex mapping: a mapping to apply to turn a /// The 'argument values' to apply to vertex arguments of a `MeshFunc`
/// Tag::Parent vertex reference into a vertex index of the parent /// from `geom` and `final_geom` that `rule` produces when evaluated.
/// mesh. That is, if `rule` produces a `MeshFunc` with a face /// The values of this are treated as indices into the parent
/// of `Tag::Parent(n)`, this will correspond to index `vmap[n]` /// `RuleEval` that produced this `Child`.
/// in the parent mesh. ///
pub vmap: Vec<usize>, /// In specific: if `arg_vals[i] = j` and `rule` produces some `geom` or
/// `final_geom`, then any vertex of `VertexUnion::Arg(i)` will be mapped
/// to `geom.verts[j]` in the *parent* geometry.
pub arg_vals: Vec<usize>,
} }
impl<S> Rule<S> { impl<S> Rule<S> {
@ -88,7 +93,7 @@ impl<S> Rule<S> {
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
// sub.vmap is used below. final_geom is not supposed to // sub.arg_vals is used below. final_geom is not supposed to
// have any vertex mapping applied. // have any vertex mapping applied.
} }
@ -103,7 +108,7 @@ impl<S> Rule<S> {
let m2 = submesh.transform(&sub.xf); let m2 = submesh.transform(&sub.xf);
(m2, sub.vmap.clone()) (m2, sub.arg_vals.clone())
// TODO: Fix clone? // TODO: Fix clone?
}).collect(); }).collect();
@ -181,8 +186,8 @@ impl<S> Rule<S> {
// geometry properly: // geometry properly:
let final_geom = eval.final_geom.transform(&xf); let final_geom = eval.final_geom.transform(&xf);
// TODO: Fix the awful hack below. I do this only to // TODO: Fix the awful hack below. I do this only to
// generate an identity mapping for vmap when I don't // generate an identity mapping for arg_vals when I don't
// actually need vmap. // actually need arg_vals.
let m = { let m = {
let mut m_ = 0; let mut m_ = 0;
for v in &final_geom.verts { for v in &final_geom.verts {
@ -197,10 +202,10 @@ impl<S> Rule<S> {
} }
m_ + 1 m_ + 1
}; };
let vmap: Vec<usize> = (0..m).collect(); let arg_vals: Vec<usize> = (0..m).collect();
let (geom2, _) = new_geom.connect(vec![(final_geom, vmap)]); let (geom2, _) = new_geom.connect(vec![(final_geom, arg_vals)]);
geom = geom.connect(vec![(geom2, child.vmap.clone())]).0; geom = geom.connect(vec![(geom2, child.arg_vals.clone())]).0;
// TODO: Fix clone? // TODO: Fix clone?
// If we end recursion on one child, we must end it // If we end recursion on one child, we must end it
@ -216,23 +221,19 @@ impl<S> Rule<S> {
continue; continue;
} }
let (g, offsets) = geom.connect(vec![(new_geom, child.vmap.clone())]); let (g, offsets) = geom.connect(vec![(new_geom, child.arg_vals.clone())]);
geom = g; geom = g;
// 'new_geom' may itself be parent geometry for // 'eval.children' may contain (via 'arg_vals') references to
// 'eval.children' (via Tag::Parent), and vmap is there to // indices of 'new_geom'. However, we don't connect() to
// resolve Tag::Parent references to the right vertices in // 'new_geom', but to the global geometry we just merged it
// 'new_geom'. // into. To account for this, we must shift 'arg_vals' by
// // the offset that 'geom.connect' gave us.
// 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:
let off = offsets[0]; let off = offsets[0];
// (We pass a one-element vector to geom.connect() above // (We pass a one-element vector to geom.connect() above
// so offsets always has just one element.) // so offsets always has just one element.)
for child in eval.children.iter_mut() { for child in eval.children.iter_mut() {
child.vmap = child.vmap.iter().map(|n| n + off).collect(); child.arg_vals = child.arg_vals.iter().map(|n| n + off).collect();
} }
// We're done evaluating this rule, so increment 'next'. // We're done evaluating this rule, so increment 'next'.
@ -263,7 +264,7 @@ impl<S> Rule<S> {
impl<S> RuleEval<S> { impl<S> RuleEval<S> {
/// Turn an iterator of (MeshFunc, Child) into a single RuleEval. /// Turn an iterator of (MeshFunc, Child) into a single RuleEval.
/// All meshes are merged, and the `vmap` in each child has the /// All meshes are merged, and the `arg_vals` 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.)
@ -274,13 +275,13 @@ impl<S> RuleEval<S> {
let (meshes, children): (Vec<_>, Vec<_>) = m.into_iter().unzip(); let (meshes, children): (Vec<_>, Vec<_>) = m.into_iter().unzip();
let (mesh, offsets) = MeshFunc::append(meshes); let (mesh, offsets) = MeshFunc::append(meshes);
// Patch up vmap in each child, and copy everything else: // Patch up arg_vals in each child, and copy everything else:
let children2: Vec<Child<S>> = 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(),
// simply add offset: // simply add offset:
vmap: c.vmap.iter().map(|i| i + off).collect(), arg_vals: c.arg_vals.iter().map(|i| i + off).collect(),
} }
}).collect(); }).collect();