diff --git a/src/lib.rs b/src/lib.rs index efe7668..73864db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod index; mod math; mod ops; +mod swizzle; mod util; /** A 2D array of values which can be operated upon. @@ -522,9 +523,11 @@ impl Matrix { # Arguments - * `ms`: a [`Vector`] of [`usize`] of length M. Each entry is the index of the row that will + * `ms`: a [`Vector`] of [`usize`] of length P. Each entry is the index of the row that will appear in the result + Returns: a P×N matrix + # Panics Panics if any of the row indices in `ms` is out of bounds @@ -543,29 +546,31 @@ impl Matrix { [7, 8, 9]])) ``` */ #[must_use] - pub fn permute_rows(&self, ms: &Vector) -> Self + pub fn permute_rows(&self, ms: &Vector) -> Matrix where T: Default, { - Self::from_rows(ms.elements().map(|&m| self.row(m))) + Matrix::::from_rows(ms.elements().map(|&m| self.row(m))) } /** Apply a permutation matrix to the columns of a matrix # Arguments - * `ns`: a [`Vector`] of [`usize`] of length N. Each entry is the index of the column that will + * `ns`: a [`Vector`] of [`usize`] of length P. Each entry is the index of the column that will appear in the result + Returns: a P×N matrix + # Panics Panics if any of the column indices in `ns` is out of bounds */ #[must_use] - pub fn permute_cols(&self, ns: &Vector) -> Self + pub fn permute_cols(&self, ns: &Vector) -> Matrix where T: Default, { - Self::from_cols(ns.elements().map(|&n| self.col(n))) + Matrix::::from_cols(ns.elements().map(|&n| self.col(n))) } /** Returns the transpose $M^T$ of the matrix, or the matrix flipped across its diagonal. diff --git a/src/swizzle.rs b/src/swizzle.rs new file mode 100644 index 0000000..aa7f580 --- /dev/null +++ b/src/swizzle.rs @@ -0,0 +1,119 @@ +/** Rearrange the rows of a matrix by the given row identifiers +# Arguments + +* `mat`: the matrix to manipulate +* `i...`: variadic comma-seperated list of row selectors. The row selectors are each one of: + * const expression representing the row index of the input to copy. eg: `0` or `2` + * a letter representing the same. you can use `r,g,b,a` or `x,y,z,w` to represent index 0 through 3, + or `u,v` to represent indices 0 through 1. + * an expression in curly braces, representing a value to be copied to an entire row of + the result, eg: `{1.0}` or `{5}` + + note that the number of selectors doesnt need to match the height of the input! + +# Examples +``` +# use vector_victor::{swizzle, Vector}; +let myvec = Vector::vec([0, 1, 2, 3]); + +// each element can be selected with: +// 0: r, x, u, or 0 +// 1: g, y, v, or 1 +// 2: b, z, or 2 +// 3: a, w, or 3 +// or a result row can be filled by a new value + +assert_eq!(swizzle!(myvec, a, z, v, 0, {7}), Vector::vec([3, 2, 1, 0, 7])); +``` + +More often you wont mix and match selector "flavors". +This example unpacks a [DXT5nm](http://wiki.polycount.com/wiki/Normal_Map_Compression) color +into the red and green channels, with blue filled with 0. +``` +# use vector_victor::{swizzle, Vector}; +let myvec = Vector::vec([0, 120, 0, 255]); +assert_eq!(swizzle!(myvec, a, g, {0}), Vector::vec([255, 120, 0])); +``` */ +#[macro_export] +macro_rules! swizzle { + ($mat: expr, $( $i:tt),+) => {{ + let mut result = $mat.permute_rows(&$crate::Vector::vec([$( $crate::get!($mat, $i), )+])); + let mut _p = 0usize; + $( + $crate::sub_literal!(result, _p, $i); + _p += 1; + )+ + result + }}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! get { + ($mat:expr, x) => { + 0usize + }; + ($mat:expr, y) => { + 1usize + }; + ($mat:expr, z) => { + 2usize + }; + ($mat:expr, w) => { + 3usize + }; + ($mat:expr, r) => { + 0usize + }; + ($mat:expr, g) => { + 1usize + }; + ($mat:expr, b) => { + 2usize + }; + ($mat:expr, a) => { + 3usize + }; + ($mat:expr, u) => { + 0usize + }; + ($mat:expr, v) => { + 1usize + }; + ($mat:expr, {$i:expr}) => { + 0usize /* will be replaced later */ + }; + ($mat:expr, $i:expr) => { + $i + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! sub_literal { + ($result:expr, $p:expr, {$i:expr}) => { + $result.set_row($p, &$crate::Vector::fill($i)) + }; + ($result:expr, $p:expr, $i:expr) => {}; +} + +#[cfg(test)] +mod tests { + use crate::Matrix; + + #[test] + fn test_swizzle() { + let identity = Matrix::::identity(); + + assert_eq!( + swizzle!(identity, a, x, b, (1 + 0), { 2 - 3 }), + Matrix::mat([ + [0, 0, 0, 1], // a + [1, 0, 0, 0], // x + [0, 0, 1, 0], // b + [0, 1, 0, 0], // row 1 + [-1, -1, -1, -1] // fill(-1) + ]) + ); + } +}