commit
ccc81b4760
7 changed files with 200 additions and 0 deletions
|
@ -18,6 +18,7 @@ name = "cgmath"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
unstable = []
|
unstable = []
|
||||||
|
swizzle = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
approx = "0.1"
|
approx = "0.1"
|
||||||
|
|
26
README.md
26
README.md
|
@ -32,6 +32,32 @@ vectors"), meaning when transforming a vector with a matrix, the matrix goes
|
||||||
on the left. This is reflected in the fact that cgmath implements the
|
on the left. This is reflected in the fact that cgmath implements the
|
||||||
multiplication operator for Matrix * Vector, but not Vector * Matrix.
|
multiplication operator for Matrix * Vector, but not Vector * Matrix.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Swizzling
|
||||||
|
This library offers an optional feature called
|
||||||
|
["swizzling"](https://en.wikipedia.org/wiki/Swizzling_(computer_graphics))
|
||||||
|
widely familiar to GPU programmers. To enable swizzle operators, pass the
|
||||||
|
`--features="swizzle"` option to cargo. Enabling this feature will increase
|
||||||
|
the size of the cgmath library by approximately 0.6MB. This isn't an
|
||||||
|
issue if the library is linked in the "normal" way by adding cgmath as a
|
||||||
|
dependency in Cargo.toml, which will link cgmath statically so all unused
|
||||||
|
swizzle operators will be optimized away by the compiler in release mode.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
If we have
|
||||||
|
```rust
|
||||||
|
let v = Vector3::new(1.0, 2.0, 3.0);
|
||||||
|
```
|
||||||
|
then `v.xyxz()` produces a
|
||||||
|
```rust
|
||||||
|
Vector4 { x: 1.0, y: 2.0, z: 1.0, w: 3.0 }
|
||||||
|
```
|
||||||
|
and `v.zy()` produces a
|
||||||
|
```rust
|
||||||
|
Vector2 { x: 3.0, y: 2.0 }
|
||||||
|
```
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
cgmath is _not_ an n-dimensional library and is aimed at computer graphics
|
cgmath is _not_ an n-dimensional library and is aimed at computer graphics
|
||||||
|
|
96
build.rs
Normal file
96
build.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::env;
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
|
/// Generate the name of the swizzle function and what it returns.
|
||||||
|
/// NOTE: This function assumes that variables are in ASCII format
|
||||||
|
#[cfg(feature = "swizzle")]
|
||||||
|
fn gen_swizzle_nth<'a>(variables: &'a str, mut i: usize, upto: usize) -> Option<(String, String)> {
|
||||||
|
debug_assert!(i > 0); // zeroth permutation is empty
|
||||||
|
let mut swizzle_impl = String::new();
|
||||||
|
let mut swizzle = String::new();
|
||||||
|
let n = variables.len()+1;
|
||||||
|
for _ in 0..upto {
|
||||||
|
if i == 0 { break; }
|
||||||
|
if i % n == 0 { return None; }
|
||||||
|
let c = variables.as_bytes()[i%n - 1] as char;
|
||||||
|
swizzle.push(c);
|
||||||
|
swizzle_impl.push_str(&format!("self.{}, ", c));
|
||||||
|
i = i/n;
|
||||||
|
}
|
||||||
|
Some((swizzle, swizzle_impl))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A function that generates swizzle functions as a string.
|
||||||
|
/// `variables`: swizzle variables (e.g. "xyz")
|
||||||
|
/// `upto`: largest output vector size (e.g. for `variables = "xy"` and `upto = 4`, `xyxy()` is a
|
||||||
|
/// valid swizzle operator.
|
||||||
|
/// NOTE: This function assumes that variables are in ASCII format
|
||||||
|
#[cfg(feature = "swizzle")]
|
||||||
|
fn gen_swizzle_functions(variables: &'static str, upto: usize) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
let nn = (variables.len()+1).pow(upto as u32);
|
||||||
|
for i in 1..nn {
|
||||||
|
if let Some((swizzle_name, swizzle_impl)) = gen_swizzle_nth(variables, i, upto) {
|
||||||
|
let dim = format!("{}", swizzle_name.len());
|
||||||
|
result.push_str(
|
||||||
|
&format!("
|
||||||
|
/// Swizzle operator that creates a new type with dimension {2} from variables `{0}`.
|
||||||
|
#[inline] pub fn {0}(&self) -> $vector_type{2}<$S> {{ $vector_type{2}::new({1}) }}\n",
|
||||||
|
swizzle_name, swizzle_impl, dim));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "swizzle"))]
|
||||||
|
fn gen_swizzle_functions(_: &'static str, _: usize) -> String {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// This script generates the macro for building swizzle operators for multidimensional
|
||||||
|
/// vectors and points. This macro is included in macros.rs
|
||||||
|
fn main() {
|
||||||
|
// save the file to output directory
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let swizzle_file_path = Path::new(&out_dir).join("swizzle_operator_macro.rs");
|
||||||
|
|
||||||
|
// This is the string representing the generated macro
|
||||||
|
let data = format!(
|
||||||
|
"/// Generate glm/glsl style swizzle operators
|
||||||
|
macro_rules! impl_swizzle_functions {{
|
||||||
|
($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, x) => {{
|
||||||
|
{x3}
|
||||||
|
}};
|
||||||
|
($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xy) => {{
|
||||||
|
{xy3}
|
||||||
|
}};
|
||||||
|
($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xyz) => {{
|
||||||
|
{xyz3}
|
||||||
|
}};
|
||||||
|
($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, x) => {{
|
||||||
|
{x4}
|
||||||
|
}};
|
||||||
|
($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xy) => {{
|
||||||
|
{xy4}
|
||||||
|
}};
|
||||||
|
($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyz) => {{
|
||||||
|
{xyz4}
|
||||||
|
}};
|
||||||
|
($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyzw) => {{
|
||||||
|
{xyzw4}
|
||||||
|
}};
|
||||||
|
}}", x3 = gen_swizzle_functions("x", 3),
|
||||||
|
xy3 = gen_swizzle_functions("xy", 3),
|
||||||
|
xyz3 = gen_swizzle_functions("xyz", 3),
|
||||||
|
x4 = gen_swizzle_functions("x", 4),
|
||||||
|
xy4 = gen_swizzle_functions("xy", 4),
|
||||||
|
xyz4 = gen_swizzle_functions("xyz", 4),
|
||||||
|
xyzw4 = gen_swizzle_functions("xyzw", 4));
|
||||||
|
let mut f = File::create(swizzle_file_path)
|
||||||
|
.expect("Unable to create file that defines the swizzle operator macro.");
|
||||||
|
f.write_all(data.as_bytes()).expect("Unable to write swizzle operator macro.");
|
||||||
|
}
|
|
@ -476,3 +476,5 @@ macro_rules! impl_mint_conversions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/swizzle_operator_macro.rs"));
|
||||||
|
|
|
@ -69,6 +69,8 @@ impl<S: BaseNum> Point1<S> {
|
||||||
pub fn new(x: S) -> Point1<S> {
|
pub fn new(x: S) -> Point1<S> {
|
||||||
Point1 { x: x }
|
Point1 { x: x }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_swizzle_functions!(Point1, Point2, Point3, S, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseNum> Point2<S> {
|
impl<S: BaseNum> Point2<S> {
|
||||||
|
@ -76,6 +78,8 @@ impl<S: BaseNum> Point2<S> {
|
||||||
pub fn new(x: S, y: S) -> Point2<S> {
|
pub fn new(x: S, y: S) -> Point2<S> {
|
||||||
Point2 { x: x, y: y }
|
Point2 { x: x, y: y }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_swizzle_functions!(Point1, Point2, Point3, S, xy);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseNum> Point3<S> {
|
impl<S: BaseNum> Point3<S> {
|
||||||
|
@ -83,6 +87,8 @@ impl<S: BaseNum> Point3<S> {
|
||||||
pub fn new(x: S, y: S, z: S) -> Point3<S> {
|
pub fn new(x: S, y: S, z: S) -> Point3<S> {
|
||||||
Point3 { x: x, y: y, z: z }
|
Point3 { x: x, y: y, z: z }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_swizzle_functions!(Point1, Point2, Point3, S, xyz);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseNum> Point3<S> {
|
impl<S: BaseNum> Point3<S> {
|
||||||
|
|
|
@ -605,6 +605,8 @@ impl<S: BaseNum> Vector1<S> {
|
||||||
pub fn unit_x() -> Vector1<S> {
|
pub fn unit_x() -> Vector1<S> {
|
||||||
Vector1::new(S::one())
|
Vector1::new(S::one())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseNum> Vector2<S> {
|
impl<S: BaseNum> Vector2<S> {
|
||||||
|
@ -632,6 +634,8 @@ impl<S: BaseNum> Vector2<S> {
|
||||||
pub fn extend(self, z: S) -> Vector3<S> {
|
pub fn extend(self, z: S) -> Vector3<S> {
|
||||||
Vector3::new(self.x, self.y, z)
|
Vector3::new(self.x, self.y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, xy);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseNum> Vector3<S> {
|
impl<S: BaseNum> Vector3<S> {
|
||||||
|
@ -674,6 +678,8 @@ impl<S: BaseNum> Vector3<S> {
|
||||||
pub fn truncate(self) -> Vector2<S> {
|
pub fn truncate(self) -> Vector2<S> {
|
||||||
Vector2::new(self.x, self.y)
|
Vector2::new(self.x, self.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, xyz);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: BaseNum> Vector4<S> {
|
impl<S: BaseNum> Vector4<S> {
|
||||||
|
@ -718,6 +724,8 @@ impl<S: BaseNum> Vector4<S> {
|
||||||
_ => panic!("{:?} is out of range", n),
|
_ => panic!("{:?} is out of range", n),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_swizzle_functions!(Vector1, Vector2, Vector3, Vector4, S, xyzw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dot product of two vectors.
|
/// Dot product of two vectors.
|
||||||
|
|
61
tests/swizzle.rs
Normal file
61
tests/swizzle.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright 2013-2017 The CGMath Developers. For a full listing of the authors,
|
||||||
|
// refer to the Cargo.toml file at the top-level directory of this distribution.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#![cfg(feature = "swizzle")]
|
||||||
|
|
||||||
|
extern crate cgmath;
|
||||||
|
|
||||||
|
use cgmath::{Point1, Point2, Point3, Vector1, Vector2, Vector3, Vector4};
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
#[test]
|
||||||
|
fn test_point_swizzle() {
|
||||||
|
let p1 = Point1::new(1.0);
|
||||||
|
let p2 = Point2::new(1.0, 2.0);
|
||||||
|
let p3 = Point3::new(1.0, 2.0, 3.0);
|
||||||
|
assert_eq!(p1.x(), p1);
|
||||||
|
assert_eq!(p2.x(), p1);
|
||||||
|
assert_eq!(p2.y(), Point1::new(2.0));
|
||||||
|
assert_eq!(p2.xx(), Point2::new(1.0, 1.0));
|
||||||
|
assert_eq!(p2.xy(), p2);
|
||||||
|
assert_eq!(p2.yx(), Point2::new(2.0, 1.0));
|
||||||
|
assert_eq!(p2.yy(), Point2::new(2.0, 2.0));
|
||||||
|
assert_eq!(p3.x(), p1);
|
||||||
|
assert_eq!(p3.y(), Point1::new(2.0));
|
||||||
|
assert_eq!(p3.xy(), p2);
|
||||||
|
assert_eq!(p3.zy(), Point2::new(3.0, 2.0));
|
||||||
|
assert_eq!(p3.yyx(), Point3::new(2.0, 2.0, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vector_swizzle() {
|
||||||
|
let p1 = Vector1::new(1.0);
|
||||||
|
let p2 = Vector2::new(1.0, 2.0);
|
||||||
|
let p3 = Vector3::new(1.0, 2.0, 3.0);
|
||||||
|
let p4 = Vector4::new(1.0, 2.0, 3.0, 4.0);
|
||||||
|
assert_eq!(p1.x(), p1);
|
||||||
|
assert_eq!(p2.x(), p1);
|
||||||
|
assert_eq!(p2.y(), Vector1::new(2.0));
|
||||||
|
assert_eq!(p2.xx(), Vector2::new(1.0, 1.0));
|
||||||
|
assert_eq!(p2.xy(), p2);
|
||||||
|
assert_eq!(p2.yx(), Vector2::new(2.0, 1.0));
|
||||||
|
assert_eq!(p2.yy(), Vector2::new(2.0, 2.0));
|
||||||
|
assert_eq!(p3.x(), p1);
|
||||||
|
assert_eq!(p3.y(), Vector1::new(2.0));
|
||||||
|
assert_eq!(p3.xy(), p2);
|
||||||
|
assert_eq!(p3.zy(), Vector2::new(3.0, 2.0));
|
||||||
|
assert_eq!(p3.yyx(), Vector3::new(2.0, 2.0, 1.0));
|
||||||
|
assert_eq!(p4.xyxy(), Vector4::new(1.0, 2.0, 1.0, 2.0));
|
||||||
|
}
|
Loading…
Reference in a new issue