Add some macros to make things less verbose

This commit is contained in:
Chris Hodapp 2020-05-14 15:41:15 -04:00
parent b7da8bea41
commit 7b04b91746
7 changed files with 99 additions and 89 deletions

View File

@ -2,12 +2,9 @@
## Highest priority: ## Highest priority:
- Make a series of guidelines for *exactly* how to order - Continue to refine the 'barbs' example, which broke some new ground.
transformations so that I'm actually constructing things to be - Implement the continuous parametric transformations from 2020-05-07
correct instead of just throwing shit at the wall. in my notes. This will require some new abstractions.
See my "Composing Transformations" link in log.org.
- Adaptive subdivision - which means having to generalize past some
`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.)
@ -42,7 +39,9 @@
method for recursive calls). method for recursive calls).
- Docs on modules - Docs on modules
- Compute global scale factor, and perhaps pass it to a rule (to - 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 - swept-isocontour stuff from
`/mnt/dev/graphics_misc/isosurfaces_2018_2019/spiral*.py`. This `/mnt/dev/graphics_misc/isosurfaces_2018_2019/spiral*.py`. This
will probably require that I figure out parametric curves will probably require that I figure out parametric curves
@ -83,7 +82,7 @@
things too - this relates to dynamical systems and eigenvalues.) things too - this relates to dynamical systems and eigenvalues.)
Later note: I have a feeling I was dead wrong about a bunch of this. 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 - My old Python version composed rules in the opposite order and I
think this made things more complicated. I didn't realize that 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 - Generalizing to space curves moves this away from the "discrete
automata" roots, but it still ends up needing the machinery I made automata" roots, but it still ends up needing the machinery I made
for discrete automata. 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.

View File

@ -5,8 +5,8 @@ use rand::Rng;
use crate::util; use crate::util;
use crate::util::VecExt; use crate::util::VecExt;
use crate::mesh::{Mesh, MeshFunc, VertexUnion}; use crate::mesh::{Mesh, MeshFunc, VertexUnion, vert_args};
use crate::xform::{Transform, Vertex, vertex, Mat4}; use crate::xform::{Transform, Vertex, vertex, Mat4, id};
use crate::rule::{Rule, RuleFn, RuleEval, Child}; use crate::rule::{Rule, RuleFn, RuleEval, Child};
use crate::prim; use crate::prim;
@ -58,66 +58,49 @@ pub fn barbs() -> Rule<()> {
VertexUnion::Vertex(vertex( 0.5, -0.5, 0.0)), VertexUnion::Vertex(vertex( 0.5, -0.5, 0.0)),
@bn, @bn,
]; ];
let n = base_verts.len();
let barb_incr: Transform = Transform::new(). let barb_incr = id().translate(0.0, 0.0, 0.5).
translate(0.0, 0.0, 0.5).
rotate(&Vector3::y_axis(), -0.2). rotate(&Vector3::y_axis(), -0.2).
scale(0.8); scale(0.8);
let b = base_verts.clone(); let b = base_verts.clone();
let barb = move |self_: Rc<Rule<()>>| -> RuleEval<()> { let barb = rule_fn!((), self_ => {
let mut next_verts = b.clone(); let mut next_verts = b.clone();
let (a0, a1) = next_verts.append_indexed( let (a0, a1) = next_verts.append_indexed(vert_args(0..4));
&mut (0..4).map(|i| VertexUnion::Arg(i)).collect()
);
let geom = util::parallel_zigzag(next_verts.clone(), b0..bn, a0..a1); let geom = util::parallel_zigzag(next_verts.clone(), b0..bn, a0..a1);
let final_geom = MeshFunc { 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 ], faces: vec![ 0, 2, 1, 0, 3, 2 ],
}; };
RuleEval { RuleEval {
geom: Rc::new(geom.transform(&barb_incr)), geom: Rc::new(geom.transform(&barb_incr)),
final_geom: Rc::new(final_geom), // no transform needed (no vertices) final_geom: Rc::new(final_geom), // no transform needed (no vertices)
children: vec![ children: vec![ child_iter!(self_, barb_incr, b0..bn) ],
Child {
rule: self_.clone(),
xf: barb_incr,
arg_vals: (0..n).collect(),
} }
] });
}
};
let barb_ = Rc::new(barb);
let main_barb_trans = |i| { let main_barb_xf = |i| {
Transform::new(). id().rotate(&Vector3::z_axis(), -std::f32::consts::FRAC_PI_2 * (i as f32)).
rotate(&Vector3::z_axis(), -std::f32::consts::FRAC_PI_2 * (i as f32)).
rotate(&Vector3::y_axis(), -std::f32::consts::FRAC_PI_2). rotate(&Vector3::y_axis(), -std::f32::consts::FRAC_PI_2).
translate(0.5, 0.0, 0.5) translate(0.5, 0.0, 0.5)
}; };
let main_incr = id().translate(0.0, 0.0, 1.0).
let main_incr: Transform = Transform::new().
translate(0.0, 0.0, 1.0).
rotate(&Vector3::z_axis(), 0.15). rotate(&Vector3::z_axis(), 0.15).
rotate(&Vector3::x_axis(), 0.1). rotate(&Vector3::x_axis(), 0.1).
scale(0.95); scale(0.95);
let b = base_verts.clone(); let b = base_verts.clone();
let main = move |self_: Rc<Rule<()>>| -> RuleEval<()> { let main = rule_fn!((), self_ => {
let mut next_verts = b.clone(); let mut next_verts = b.clone();
let (a0, a1) = next_verts.append_indexed( let (a0, a1) = next_verts.append_indexed(vert_args(0..4));
&mut (0..4).map(|i| VertexUnion::Arg(i)).collect()
);
// This contributes no faces of its own - just vertices. // This contributes no faces of its own - just vertices.
let geom = MeshFunc { let geom = MeshFunc { verts: next_verts.clone(), faces: vec![] };
verts: next_verts.clone(), // (unless recursion ends here, of course)
faces: vec![],
};
let final_geom = MeshFunc { 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 ], faces: vec![ 0, 2, 1, 0, 3, 2 ],
}; };
@ -125,59 +108,30 @@ pub fn barbs() -> Rule<()> {
geom: Rc::new(geom), geom: Rc::new(geom),
final_geom: Rc::new(final_geom), final_geom: Rc::new(final_geom),
children: vec![ children: vec![
Child { child_iter!(self_, main_incr, b0..bn),
rule: self_.clone(), child!(rule!(barb, ()), main_barb_xf(0), b0 + 0, b0 + 1, a0 + 1, a0 + 0),
xf: main_incr, child!(rule!(barb, ()), main_barb_xf(1), b0 + 1, b0 + 2, a0 + 2, a0 + 1),
arg_vals: (0..n).collect(), 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),
Child { // TODO: Factor out repetition?
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
], ],
} }
}; });
let main_ = Rc::new(main); let base = rule_fn!((), _s => {
let base = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
RuleEval { RuleEval {
geom: Rc::new(MeshFunc { geom: Rc::new(MeshFunc {
verts: base_verts.clone(), verts: base_verts.clone(),
faces: vec![ faces: vec![ b0, b0 + 1, b0 + 2, b0, b0 + 2, b0 + 3 ],
b0, b0 + 1, b0 + 2,
b0, b0 + 2, b0 + 3,
],
}), }),
// TODO: This might be buggy and leave some vertices lying around // TODO: This might be buggy and leave some vertices lying around
final_geom: Rc::new(prim::empty_meshfunc()), final_geom: Rc::new(prim::empty_meshfunc()),
children: vec![ children: vec![ child_iter!(rule!(main, ()), id(), b0..bn) ],
Child {
rule: Rc::new(Rule { eval: main_.clone(), ctxt: () }),
xf: Transform::new(),
arg_vals: (0..n).collect(),
},
],
} }
}; });
Rule { eval: Rc::new(base), ctxt: () } //rule!(Rc::new(base), ())
Rule { eval: base, ctxt: () }
} }
/* /*

View File

@ -1,4 +1,5 @@
pub mod mesh; pub mod mesh;
#[macro_use]
pub mod rule; pub mod rule;
pub mod prim; pub mod prim;
#[macro_use] #[macro_use]

View File

@ -82,6 +82,10 @@ pub enum VertexUnion {
Arg(usize), Arg(usize),
} }
pub fn vert_args<T: IntoIterator<Item = usize>>(v: T) -> Vec<VertexUnion> {
v.into_iter().map(|i| VertexUnion::Arg(i)).collect()
}
/// A face-vertex mesh whose vertices can either be concrete values /// A face-vertex mesh whose vertices can either be concrete values
/// (as in `Mesh`) or aliases (indices to other vertices of some /// (as in `Mesh`) or aliases (indices to other vertices of some
/// hypothetical mesh). This can be turned to `mesh` only if those /// hypothetical mesh). This can be turned to `mesh` only if those

View File

@ -80,6 +80,50 @@ pub struct Child<S> {
pub arg_vals: Vec<usize>, pub arg_vals: Vec<usize>,
} }
#[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<Rule<$Ty>>| -> RuleEval<$Ty> {
let $Self = $Self.clone();
$Body
})
}
}
// TODO: Shouldn't I fully-qualify Rule & RuleEval?
// TODO: Document all of the above macros
impl<S> Rule<S> { impl<S> Rule<S> {
/// Convert this `Rule` to mesh data, recursively (depth first). /// Convert this `Rule` to mesh data, recursively (depth first).

View File

@ -24,15 +24,15 @@ macro_rules! vec_indexed {
} }
pub trait VecExt<T> { pub trait VecExt<T> {
fn append_indexed(&mut self, other: &mut Vec<T>) -> (usize, usize); fn append_indexed(&mut self, other: Vec<T>) -> (usize, usize);
} }
impl<T> VecExt<T> for Vec<T> { impl<T> VecExt<T> for Vec<T> {
// Like `append`, but returning `(a, b)` which give the range of // Like `append`, but returning `(a, b)` which give the range of
// elements just inserted. // elements just inserted.
fn append_indexed(&mut self, other: &mut Vec<T>) -> (usize, usize) { fn append_indexed(&mut self, mut other: Vec<T>) -> (usize, usize) {
let a = self.len(); let a = self.len();
self.append(other); self.append(&mut other);
let b = self.len(); let b = self.len();
(a, b) (a, b)
} }

View File

@ -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 { impl<'a> Mul for &'a Transform {
type Output = Self; type Output = Self;