diff --git a/README.md b/README.md index a8d80ea..9fef1ca 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,9 @@ ## Highest priority: -- Make a series of guidelines for *exactly* how to order - transformations so that I'm actually constructing things to be - correct instead of just throwing shit at the wall. - See my "Composing Transformations" link in log.org. -- Adaptive subdivision - which means having to generalize past some - `arg_vals` stuff. +- Continue to refine the 'barbs' example, which broke some new ground. +- Implement the continuous parametric transformations from 2020-05-07 + in my notes. This will require some new abstractions. - Try some non-deterministic examples. - Get identical or near-identical meshes to `ramhorn_branch` from Python. (Should just be a matter of tweaking parameters.) @@ -42,7 +39,9 @@ method for recursive calls). - Docs on modules - Compute global scale factor, and perhaps pass it to a rule (to - eventually be used for, perhaps, adaptive subdivision) + eventually be used for, perhaps, adaptive subdivision). Note that + one can find the scale factors by taking the length of the first 3 + columns of the transform matrix (supposedly). - swept-isocontour stuff from `/mnt/dev/graphics_misc/isosurfaces_2018_2019/spiral*.py`. This will probably require that I figure out parametric curves @@ -83,7 +82,7 @@ things too - this relates to dynamical systems and eigenvalues.) Later note: I have a feeling I was dead wrong about a bunch of this. -## Reflections +## Reflections & Quick Notes - My old Python version composed rules in the opposite order and I think this made things more complicated. I didn't realize that I @@ -94,3 +93,6 @@ - Generalizing to space curves moves this away from the "discrete automata" roots, but it still ends up needing the machinery I made for discrete automata. +- If you *pre* multiply a transformation: you are transforming the + entire global space. If you *post* multiply: you are transforming + the current local space. \ No newline at end of file diff --git a/src/examples.rs b/src/examples.rs index f87571d..3cf9ddd 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -5,8 +5,8 @@ use rand::Rng; use crate::util; use crate::util::VecExt; -use crate::mesh::{Mesh, MeshFunc, VertexUnion}; -use crate::xform::{Transform, Vertex, vertex, Mat4}; +use crate::mesh::{Mesh, MeshFunc, VertexUnion, vert_args}; +use crate::xform::{Transform, Vertex, vertex, Mat4, id}; use crate::rule::{Rule, RuleFn, RuleEval, Child}; use crate::prim; @@ -58,66 +58,49 @@ pub fn barbs() -> Rule<()> { VertexUnion::Vertex(vertex( 0.5, -0.5, 0.0)), @bn, ]; - let n = base_verts.len(); - let barb_incr: Transform = Transform::new(). - translate(0.0, 0.0, 0.5). + let barb_incr = id().translate(0.0, 0.0, 0.5). rotate(&Vector3::y_axis(), -0.2). scale(0.8); let b = base_verts.clone(); - let barb = move |self_: Rc>| -> RuleEval<()> { + let barb = rule_fn!((), self_ => { let mut next_verts = b.clone(); - let (a0, a1) = next_verts.append_indexed( - &mut (0..4).map(|i| VertexUnion::Arg(i)).collect() - ); + let (a0, a1) = next_verts.append_indexed(vert_args(0..4)); let geom = util::parallel_zigzag(next_verts.clone(), b0..bn, a0..a1); let final_geom = MeshFunc { - verts: (0..4).map(|i| VertexUnion::Arg(i)).collect(), + verts: vert_args(0..4), faces: vec![ 0, 2, 1, 0, 3, 2 ], }; RuleEval { geom: Rc::new(geom.transform(&barb_incr)), final_geom: Rc::new(final_geom), // no transform needed (no vertices) - children: vec![ - Child { - rule: self_.clone(), - xf: barb_incr, - arg_vals: (0..n).collect(), - } - ] + children: vec![ child_iter!(self_, barb_incr, b0..bn) ], } - }; - let barb_ = Rc::new(barb); + }); - let main_barb_trans = |i| { - Transform::new(). - rotate(&Vector3::z_axis(), -std::f32::consts::FRAC_PI_2 * (i as f32)). - rotate(&Vector3::y_axis(), -std::f32::consts::FRAC_PI_2). - translate(0.5, 0.0, 0.5) + let main_barb_xf = |i| { + id().rotate(&Vector3::z_axis(), -std::f32::consts::FRAC_PI_2 * (i as f32)). + rotate(&Vector3::y_axis(), -std::f32::consts::FRAC_PI_2). + translate(0.5, 0.0, 0.5) }; - - let main_incr: Transform = Transform::new(). - translate(0.0, 0.0, 1.0). + let main_incr = id().translate(0.0, 0.0, 1.0). rotate(&Vector3::z_axis(), 0.15). rotate(&Vector3::x_axis(), 0.1). scale(0.95); + let b = base_verts.clone(); - let main = move |self_: Rc>| -> RuleEval<()> { + let main = rule_fn!((), self_ => { let mut next_verts = b.clone(); - let (a0, a1) = next_verts.append_indexed( - &mut (0..4).map(|i| VertexUnion::Arg(i)).collect() - ); + let (a0, a1) = next_verts.append_indexed(vert_args(0..4)); // This contributes no faces of its own - just vertices. - let geom = MeshFunc { - verts: next_verts.clone(), - faces: vec![], - }; + let geom = MeshFunc { verts: next_verts.clone(), faces: vec![] }; + // (unless recursion ends here, of course) let final_geom = MeshFunc { - verts: (0..4).map(|i| VertexUnion::Arg(i)).collect(), + verts: vert_args(0..4), faces: vec![ 0, 2, 1, 0, 3, 2 ], }; @@ -125,59 +108,30 @@ pub fn barbs() -> Rule<()> { geom: Rc::new(geom), final_geom: Rc::new(final_geom), children: vec![ - Child { - rule: self_.clone(), - xf: main_incr, - arg_vals: (0..n).collect(), - }, - Child { - rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), - xf: main_barb_trans(0), - arg_vals: vec![b0 + 0, b0 + 1, a0 + 1, a0 + 0], - }, - Child { - rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), - xf: main_barb_trans(1), - arg_vals: vec![b0 + 1, b0 + 2, a0 + 2, a0 + 1], - }, - Child { - rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), - xf: main_barb_trans(2), - arg_vals: vec![b0 + 2, b0 + 3, a0 + 3, a0 + 2], - }, - Child { - rule: Rc::new(Rule { eval: barb_.clone(), ctxt: () }), - xf: main_barb_trans(3), - arg_vals: vec![b0 + 3, b0 + 0, a0 + 0, a0 + 3], - }, - // TODO: Factor out repetition + child_iter!(self_, main_incr, b0..bn), + child!(rule!(barb, ()), main_barb_xf(0), b0 + 0, b0 + 1, a0 + 1, a0 + 0), + child!(rule!(barb, ()), main_barb_xf(1), b0 + 1, b0 + 2, a0 + 2, a0 + 1), + child!(rule!(barb, ()), main_barb_xf(2), b0 + 2, b0 + 3, a0 + 3, a0 + 2), + child!(rule!(barb, ()), main_barb_xf(3), b0 + 3, b0 + 0, a0 + 0, a0 + 3), + // TODO: Factor out repetition? ], } - }; + }); - let main_ = Rc::new(main); - let base = move |self_: Rc>| -> RuleEval<()> { + let base = rule_fn!((), _s => { RuleEval { geom: Rc::new(MeshFunc { verts: base_verts.clone(), - faces: vec![ - b0, b0 + 1, b0 + 2, - b0, b0 + 2, b0 + 3, - ], + faces: vec![ b0, b0 + 1, b0 + 2, b0, b0 + 2, b0 + 3 ], }), // TODO: This might be buggy and leave some vertices lying around final_geom: Rc::new(prim::empty_meshfunc()), - children: vec![ - Child { - rule: Rc::new(Rule { eval: main_.clone(), ctxt: () }), - xf: Transform::new(), - arg_vals: (0..n).collect(), - }, - ], + children: vec![ child_iter!(rule!(main, ()), id(), b0..bn) ], } - }; + }); - Rule { eval: Rc::new(base), ctxt: () } + //rule!(Rc::new(base), ()) + Rule { eval: base, ctxt: () } } /* diff --git a/src/lib.rs b/src/lib.rs index 461e7a0..5830c6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod mesh; +#[macro_use] pub mod rule; pub mod prim; #[macro_use] diff --git a/src/mesh.rs b/src/mesh.rs index b7a84aa..79d4336 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -82,6 +82,10 @@ pub enum VertexUnion { Arg(usize), } +pub fn vert_args>(v: T) -> Vec { + v.into_iter().map(|i| VertexUnion::Arg(i)).collect() +} + /// A face-vertex mesh whose vertices can either be concrete values /// (as in `Mesh`) or aliases (indices to other vertices of some /// hypothetical mesh). This can be turned to `mesh` only if those diff --git a/src/rule.rs b/src/rule.rs index e4c4aac..8ce3d1b 100644 --- a/src/rule.rs +++ b/src/rule.rs @@ -80,6 +80,50 @@ pub struct Child { pub arg_vals: Vec, } +#[macro_export] +macro_rules! child { + ( $Rule:expr, $Xform:expr, $( $Arg:expr ),* ) => { + Child { + rule: /*std::rc::Rc::new*/($Rule), + xf: $Xform, + arg_vals: vec![$($Arg,)*], + } + } +} + +#[macro_export] +macro_rules! child_iter { + ( $Rule:expr, $Xform:expr, $Args:expr ) => { + Child { + rule: /*std::rc::Rc::new*/($Rule), + xf: $Xform, + arg_vals: $Args.collect(), // does this even need a macro? + } + } +} + +#[macro_export] +macro_rules! rule { + ( $RuleFn:expr, $Ctxt:expr ) => { + std::rc::Rc::new(Rule { + eval: $RuleFn.clone(), + ctxt: $Ctxt, + }) + } +} + +#[macro_export] +macro_rules! rule_fn { + ( $Ty:ty, $Self:ident => $Body:expr ) => { + std::rc::Rc::new(move |$Self: std::rc::Rc>| -> RuleEval<$Ty> { + let $Self = $Self.clone(); + $Body + }) + } +} +// TODO: Shouldn't I fully-qualify Rule & RuleEval? +// TODO: Document all of the above macros + impl Rule { /// Convert this `Rule` to mesh data, recursively (depth first). diff --git a/src/util.rs b/src/util.rs index b7779c4..2d2ee2c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -24,15 +24,15 @@ macro_rules! vec_indexed { } pub trait VecExt { - fn append_indexed(&mut self, other: &mut Vec) -> (usize, usize); + fn append_indexed(&mut self, other: Vec) -> (usize, usize); } impl VecExt for Vec { // Like `append`, but returning `(a, b)` which give the range of // elements just inserted. - fn append_indexed(&mut self, other: &mut Vec) -> (usize, usize) { + fn append_indexed(&mut self, mut other: Vec) -> (usize, usize) { let a = self.len(); - self.append(other); + self.append(&mut other); let b = self.len(); (a, b) } diff --git a/src/xform.rs b/src/xform.rs index 40e1615..9f5e582 100644 --- a/src/xform.rs +++ b/src/xform.rs @@ -57,6 +57,11 @@ impl Mul for Transform { } } +/// Convenience function for identity transformation +pub fn id() -> Transform { + Transform::new() +} + /* impl<'a> Mul for &'a Transform { type Output = Self;