From 84711d67c011f4d23fe99f0da2c6575668b01884 Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Wed, 1 Apr 2020 12:03:13 -0400 Subject: [PATCH] Make Transform struct --- README.md | 2 -- src/examples.rs | 49 ++++++++++++++++-------------------- src/lib.rs | 1 + src/openmesh.rs | 22 +++------------- src/prim.rs | 6 ++--- src/rule.rs | 9 ++++--- src/util.rs | 3 ++- src/xform.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 102 insertions(+), 57 deletions(-) create mode 100644 src/xform.rs diff --git a/README.md b/README.md index 4e57c48..0ef9a9f 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@ - Elegance & succinctness (my recent closure work may help with this): - Why must I repeat myself so much in these definitions? - - The notation for transforms is really cumbersome. Some syntactic - sugar might go far. - What patterns can I factor out? I do some things regularly, like: the clockwise boundaries, the zigzag connections, the iterating over a `Vec` to transform each element and make another vector. diff --git a/src/examples.rs b/src/examples.rs index 51eafc8..638757a 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -2,7 +2,8 @@ use std::rc::Rc; use nalgebra::*; //pub mod examples; -use crate::openmesh::{OpenMesh, Mat4, vertex, transform}; +use crate::openmesh::{OpenMesh}; +use crate::xform::{Transform, vertex}; use crate::rule::{Rule, RuleFn, RuleEval, Child}; use crate::prim; use crate::util; @@ -16,19 +17,18 @@ fn cube_thing() -> Rule { let z = &Vector3::z_axis(); // Each element of this turns to a branch for the recursion: - let turns: Vec = vec![ - geometry::Transform3::identity().to_homogeneous(), - geometry::Rotation3::from_axis_angle(y, qtr).to_homogeneous(), - geometry::Rotation3::from_axis_angle(y, qtr * 2.0).to_homogeneous(), - geometry::Rotation3::from_axis_angle(y, qtr * 3.0).to_homogeneous(), - geometry::Rotation3::from_axis_angle(z, qtr).to_homogeneous(), - geometry::Rotation3::from_axis_angle(z, -qtr).to_homogeneous(), + let id = Transform::new(); + let turns: Vec = vec![ + id.clone(), + id.rotate(y, qtr), + id.rotate(y, qtr * 2.0), + id.rotate(y, qtr * 3.0), + id.rotate(z, qtr), + id.rotate(z, -qtr), ]; - let gen_xform = |rot: &Mat4| -> Mat4 { - (rot * - Matrix4::new_scaling(0.5) * - geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous()) + let gen_xform = |rot: &Transform| -> Transform { + rot.scale(0.5).translate(6.0, 0.0, 0.0) }; let rec = move |self_: Rc| -> RuleEval { @@ -367,13 +367,13 @@ impl RamHorn { fn twist(f: f32, subdiv: usize) -> Rule { // TODO: Clean this code up. It was a very naive conversion from // the non-closure version. - let xf = geometry::Rotation3::from_axis_angle(&Vector3::x_axis(), -0.7).to_homogeneous(); + let xf = Transform::new().rotate(&Vector3::x_axis(), -0.7); let seed = { let s = vec![vertex(-0.5, 0.0, -0.5), vertex( 0.5, 0.0, -0.5), vertex( 0.5, 0.0, 0.5), vertex(-0.5, 0.0, 0.5)]; - util::subdivide_cycle(&transform(&s, &xf), subdiv) + util::subdivide_cycle(&xf.transform(&s), subdiv) }; let n = seed.len(); let dx0: f32 = 1.5; @@ -385,19 +385,14 @@ fn twist(f: f32, subdiv: usize) -> Rule { let qtr = std::f32::consts::FRAC_PI_2; let y = Vector3::y_axis(); - let incr_inner = geometry::Translation3::new(-dx0, 0.0, 0.0).to_homogeneous() * - geometry::Rotation3::from_axis_angle(&y, ang).to_homogeneous() * - geometry::Translation3::new(dx0, dy, 0.0).to_homogeneous(); - let incr_outer = geometry::Translation3::new(-dx0*2.0, 0.0, 0.0).to_homogeneous() * - geometry::Rotation3::from_axis_angle(&y, ang/2.0).to_homogeneous() * - geometry::Translation3::new(dx0*2.0, dy, 0.0).to_homogeneous(); - // TODO: Cleanliness fix - transforms? + let incr_inner = Transform::new().translate(-dx0, 0.0, 0.0).rotate(&y, ang).translate(dx0, dy, 0.0); + let incr_outer = Transform::new().translate(-dx0*2.0, 0.0, 0.0).rotate(&y, ang/2.0).translate(dx0*2.0, dy, 0.0); let seed2 = seed.clone(); // TODO: Why do I need the above? - let recur = move |incr: Mat4| -> RuleFn { + let recur = move |incr: Transform| -> RuleFn { - let seed_next = transform(&seed2, &incr); + let seed_next = incr.transform(&seed2); // TODO: Cleanliness fix - utility function to make a zigzag mesh? let geom = OpenMesh { @@ -437,11 +432,9 @@ fn twist(f: f32, subdiv: usize) -> Rule { let start = move |_| -> RuleEval { - let xform = |dx, i, ang0, div| -> Mat4 { - (geometry::Rotation3::from_axis_angle(&y, ang0 + (qtr / div * (i as f32))).to_homogeneous() * - geometry::Translation3::new(dx, 0.0, 0.0).to_homogeneous()) + let xform = |dx, i, ang0, div| -> Transform { + Transform::new().rotate(&y, ang0 + (qtr / div * (i as f32))).translate(dx, 0.0, 0.0) }; - // TODO: Cleanliness fix - transforms? let make_child = |incr, xform| -> (OpenMesh, Child) { @@ -452,7 +445,7 @@ fn twist(f: f32, subdiv: usize) -> Rule { vmap: (0..(n+1)).collect(), // N.B. n+1, not n. the +1 is for the centroid below. }; - let mut vs = transform(&seed, &xform); + let mut vs = xform.transform(&seed); // and in the process, generate faces for these seeds: let (centroid, f) = util::connect_convex(&vs, false); vs.push(centroid); diff --git a/src/lib.rs b/src/lib.rs index 32daf02..7073454 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub mod openmesh; pub mod rule; pub mod prim; pub mod util; +pub mod xform; //pub use crate::examples; //pub use crate::openmesh::test_thing; diff --git a/src/openmesh.rs b/src/openmesh.rs index 33de6ac..2114549 100644 --- a/src/openmesh.rs +++ b/src/openmesh.rs @@ -1,24 +1,8 @@ -//pub mod openmesh; - -use nalgebra::*; use std::fs::OpenOptions; use std::io; use std::borrow::Borrow; -/// A type for mesh vertices. Initialize with [vertex][self::vertex]. -pub type Vertex = Vector4; -/// A type for homogeneous transforms -pub type Mat4 = Matrix4; - -/// Initializes a vertex: -pub fn vertex(x: f32, y: f32, z: f32) -> Vertex { - Vertex::new(x, y, z, 1.0) -} - -/// Transforms a vector of vertices: -pub fn transform(verts: &Vec, xf: &Mat4) -> Vec { - verts.iter().map(|v| xf * v).collect() -} +use crate::xform::{Vertex, Transform}; /// A type for a 'tagged' vertex index referring either to an index of /// a mesh, or of its parent. @@ -81,9 +65,9 @@ impl OpenMesh { } /// Returns a new `OpenMesh` whose vertices have been transformed. - pub fn transform(&self, xfm: &Mat4) -> OpenMesh { + pub fn transform(&self, xfm: &Transform) -> OpenMesh { OpenMesh { - verts: self.verts.iter().map(|v| xfm * v).collect(), + verts: xfm.transform(&self.verts), // TODO: Is the above faster if I pack vectors into a // bigger matrix, and transform that? faces: self.faces.clone(), // TODO: Use Rc? diff --git a/src/prim.rs b/src/prim.rs index 26e36ec..a872715 100644 --- a/src/prim.rs +++ b/src/prim.rs @@ -1,5 +1,5 @@ -use nalgebra::*; -use crate::openmesh::{OpenMesh, Tag, vertex}; +use crate::openmesh::{OpenMesh, Tag}; +use crate::xform::{vertex, Transform}; /// Returns an empty mesh (no vertices, no faces). pub fn empty_mesh() -> OpenMesh { @@ -36,5 +36,5 @@ pub fn cube() -> OpenMesh { Tag::Body(0), Tag::Body(1), Tag::Body(5), Tag::Body(0), Tag::Body(5), Tag::Body(4), ], - }.transform(&geometry::Translation3::new(-0.5, -0.5, -0.5).to_homogeneous()) + }.transform(&Transform::new().translate(-0.5, -0.5, -0.5)) } diff --git a/src/rule.rs b/src/rule.rs index 8cc0428..8c82d50 100644 --- a/src/rule.rs +++ b/src/rule.rs @@ -1,4 +1,5 @@ -use crate::openmesh::{OpenMesh, Tag, Mat4}; +use crate::openmesh::{OpenMesh, Tag}; +use crate::xform::{Transform}; //use crate::prim; use std::borrow::Borrow; use std::rc::Rc; @@ -62,7 +63,7 @@ pub struct Child { /// The transform to apply to all geometry produced by `rule` /// (including its own `geom` and `final_geom` if needed, as well /// as all sub-geometry produced recursively). - pub xf: Mat4, + pub xf: Transform, /// The parent vertex mapping: a mapping to apply to turn a /// Tag::Parent vertex reference into a vertex index of the parent @@ -127,7 +128,7 @@ impl Rule { next: usize, // World transform of the *parent* of 'rules', that is, // not including any transform of any element of 'rules'. - xf: Mat4, + xf: Transform, // How many levels 'deeper' can we recurse? depth: usize, } @@ -143,7 +144,7 @@ impl Rule { let mut stack: Vec = vec![State { rules: eval.children, next: 0, - xf: nalgebra::geometry::Transform3::identity().to_homogeneous(), + xf: Transform::new(), depth: max_depth, }]; let mut geom = eval.geom; diff --git a/src/util.rs b/src/util.rs index 0359f8a..1d58629 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,5 @@ -use crate::openmesh::{Tag, Vertex}; +use crate::openmesh::{Tag}; +use crate::xform::{Vertex}; //use crate::rule::{Rule, Child}; /// Linearly subdivides a list of points that are to be treated as a diff --git a/src/xform.rs b/src/xform.rs new file mode 100644 index 0000000..40e1615 --- /dev/null +++ b/src/xform.rs @@ -0,0 +1,67 @@ +use nalgebra::*; +use std::ops::Mul; + +/// A type for mesh vertices. Initialize with [vertex][self::vertex]. +pub type Vertex = Vector4; +/// A type for homogeneous transforms +pub type Mat4 = Matrix4; + +#[derive(Clone, Copy)] +pub struct Transform { + pub mtx: Mat4, +} + +/// Initializes a vertex: +pub fn vertex(x: f32, y: f32, z: f32) -> Vertex { + Vertex::new(x, y, z, 1.0) +} + +impl Transform { + + pub fn new() -> Transform { + Transform { + mtx: geometry::Transform3::identity().to_homogeneous(), + } + } + + pub fn rotate(&self, axis: &Unit>, angle: f32) -> Transform { + Transform { + mtx: self.mtx * Matrix4::from_axis_angle(axis, angle), + } + } + + pub fn translate(&self, x: f32, y: f32, z: f32) -> Transform { + let xf = geometry::Translation3::new(x, y, z); + Transform { + mtx: self.mtx * xf.to_homogeneous(), + } + } + + pub fn scale(&self, f: f32) -> Transform { + let xf = Matrix4::new_scaling(f); + Transform { + mtx: self.mtx * xf, + } + } + + /// Transforms a vector of vertices: + pub fn transform(&self, verts: &Vec) -> Vec { + verts.iter().map(|v| self.mtx * v).collect() + } +} + +impl Mul for Transform { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + Transform { mtx: self.mtx * rhs.mtx } + } +} + +/* +impl<'a> Mul for &'a Transform { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + &Transform { mtx: self.mtx * rhs.mtx } + } +} +*/