From 5a222dcc932c3aac7e025020c2c3d18758e25510 Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Mon, 20 Apr 2020 18:45:21 -0400 Subject: [PATCH] Added a happy accident? --- README.md | 6 +++ src/examples.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 33 +++++++++++++- 3 files changed, 143 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9127c03..3c175ff 100644 --- a/README.md +++ b/README.md @@ -2,6 +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 - Adaptive subdivision - which means having to generalize past some `vmap` stuff. - Try some non-deterministic examples @@ -56,3 +59,6 @@ - Would being able to name a rule node (perhaps conditionally under some compile-time flag) help for debugging? - Use an actual logging framework. +- Take a square. Wrap it around to a torus. Now add a twist (about + the axis that is normal to the square). This is simple, but it looks + pretty cool. diff --git a/src/examples.rs b/src/examples.rs index 336ac21..da4dcd9 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -140,6 +140,7 @@ pub fn twist(f: f32, subdiv: usize) -> Rule<()> { #[derive(Copy, Clone)] pub struct TorusCtxt { init: bool, + count: usize, stack: [Transform; 3], } @@ -162,22 +163,25 @@ pub fn twisty_torus() -> Rule { }); let rad = 1.0; - let rad2 = 5.0; + let rad2 = 8.0; let dx0 = 2.0; let ang = 0.1; let recur = move |self_: Rc>| -> RuleEval { - //let y = &Vector3::y_axis(); + let x = &Vector3::x_axis(); let z = &Vector3::z_axis(); let stack = self_.ctxt.stack; + let count = self_.ctxt.count; let next_rule = Rule { eval: self_.eval.clone(), ctxt: TorusCtxt { init: false, + count: count + 1, stack: [ - stack[0], - Transform::new().rotate(z, 0.05) * stack[1], - Transform::new().translate(0.0, 0.0, 0.1) * stack[2], + Transform::new().translate(0.1, 0.0, 0.0).rotate(x, 0.01) * stack[0], + // stack[0], //Transform::new().rotate(z, 0.05 * (count as f32)).translate(0.0, rad2, 0.0), + Transform::new().rotate(z, 0.30) * stack[1], + stack[2], ], }, }; @@ -218,6 +222,103 @@ pub fn twisty_torus() -> Rule { eval: Rc::new(recur), ctxt: TorusCtxt { init: true, + count: 0, + stack: [ + Transform::new().translate(0.0, rad2, 0.0), + Transform::new().translate(rad, 0.0, 0.0), + Transform::new(), // .translate(dx0, 0.0, 0.0), + ], + }, + } +} + +// This was a mistake that I'd like to understand later: +#[derive(Copy, Clone)] +pub struct WindChimeCtxt { + init: bool, + count: usize, + stack: [Transform; 3], +} + +pub fn wind_chime_mistake_thing() -> Rule { + let subdiv = 8; + let seed = vec![ + vertex(-0.5, -0.5, 0.0), + vertex(-0.5, 0.5, 0.0), + vertex( 0.5, 0.5, 0.0), + vertex( 0.5, -0.5, 0.0), + ]; + let seed = util::subdivide_cycle(&seed, subdiv); + + let n = seed.len(); + let geom = Rc::new(util::zigzag_to_parent(seed.clone(), n)); + let (vc, faces) = util::connect_convex(&seed, true); + let final_geom = Rc::new(OpenMesh { + verts: vec![vc], + faces: faces, + }); + + let rad = 1.0; + let rad2 = 8.0; + let dx0 = 2.0; + let ang = 0.1; + + let recur = move |self_: Rc>| -> RuleEval { + let x = &Vector3::x_axis(); + let z = &Vector3::z_axis(); + let stack = self_.ctxt.stack; + let count = self_.ctxt.count; + let next_rule = Rule { + eval: self_.eval.clone(), + ctxt: WindChimeCtxt { + init: false, + count: count + 1, + stack: [ + Transform::new().rotate(x, 0.01) * stack[0], + // stack[0], //Transform::new().rotate(z, 0.05 * (count as f32)).translate(0.0, rad2, 0.0), + Transform::new().rotate(z, 0.30) * stack[1], + Transform::new().translate(0.1, 0.0, 0.0) * stack[2], + ], + }, + }; + let xf = stack.iter().fold(Transform::new(), |acc,m| acc * (*m)); + if self_.ctxt.init { + let mut s2 = seed.clone(); + let (centroid, f) = util::connect_convex(&s2, false); + s2.push(centroid); + let n2 = s2.len(); + let g = OpenMesh { verts: s2, faces: f }; + RuleEval { + geom: Rc::new(g.transform(&xf)), + final_geom: Rc::new(prim::empty_mesh()), + children: vec![ + Child { + rule: Rc::new(next_rule), + xf: Transform::new(), + vmap: (0..n2).collect(), + }, + ], + } + } else { + RuleEval { + geom: Rc::new(geom.transform(&xf)), + final_geom: Rc::new(final_geom.transform(&xf)), + children: vec![ + Child { + rule: Rc::new(next_rule), + xf: Transform::new(), + vmap: (0..n).collect(), + }, + ], + } + } + }; + + Rule { + eval: Rc::new(recur), + ctxt: WindChimeCtxt { + init: true, + count: 0, stack: [ Transform::new().translate(0.0, rad2, 0.0), Transform::new().translate(rad, 0.0, 0.0), diff --git a/src/lib.rs b/src/lib.rs index 74ba7d1..29e0f73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ mod tests { use std::rc::Rc; use std::time::Instant; use rule::Rule; + use nalgebra::*; fn run_test(rule: Rule, iters: usize, name: &str, use_old: bool) { let r = Rc::new(rule); @@ -37,9 +38,33 @@ mod tests { mesh.write_stl_file(&fname).unwrap(); } + #[test] + fn xform_order() { + let geom = prim::cube(); + + let y = &Vector3::y_axis(); + + let dx = 4.0; + let r = -0.5; + + let trans = xform::Transform::new().translate(dx, 0.0, 0.0); + let rot = xform::Transform::new().rotate(y, r); + + let xf1 = trans.rotate(y, r); + let xf2 = rot.translate(dx, 0.0, 0.0); + + // Rotate entire space, *then* translate in that rotated plane: + geom.transform(&trans).transform(&rot).write_stl_file("xform_apply_trans_rot.stl").unwrap(); + geom.transform(&(rot * trans)).write_stl_file("xform_mul_rot_trans.stl").unwrap(); + geom.transform(&xf2).write_stl_file("xform_rot_trans.stl").unwrap(); + // Translate cube, *then* rotate it: + geom.transform(&rot).transform(&trans).write_stl_file("xform_apply_rot_trans.stl").unwrap(); + geom.transform(&(trans * rot)).write_stl_file("xform_mul_trans_rot.stl").unwrap(); + geom.transform(&xf1).write_stl_file("xform_trans_rot.stl").unwrap(); + } + // TODO: These tests don't test any conditions, so this is useful // short-hand to run, but not very meaningful as a test. - #[test] fn cube_thing() { run_test(examples::cube_thing(), 3, "cube_thing3", false); @@ -52,7 +77,11 @@ mod tests { #[test] fn twisty_torus() { - run_test(examples::twisty_torus(), 50, "twisty_torus", false); + run_test(examples::twisty_torus(), 400, "twisty_torus", false); + } + + fn wind_chime_mistake_thing() { + run_test(examples::wind_chime_mistake_thing(), 400, "wind_chime_mistake_thing", false); } // This one is very time-consuming to run: