Coder Social home page Coder Social logo

rustgd / cgmath Goto Github PK

View Code? Open in Web Editor NEW
1.1K 32.0 156.0 3.27 MB

A linear algebra and mathematics library for computer graphics.

Home Page: https://docs.rs/cgmath

License: Apache License 2.0

Rust 100.00%
rust linear-algebra matrix vector computer-graphics simd simd-vector mathematics-library

cgmath's Introduction

cgmath-rs

Build Status Documentation Version License Downloads Gitter

A linear algebra and mathematics library for computer graphics.

The library provides:

  • vectors: Vector2, Vector3, Vector4
  • square matrices: Matrix2, Matrix3, Matrix4
  • a quaternion type: Quaternion
  • rotation matrices: Basis2, Basis3
  • angle units: Rad, Deg
  • points: Point2, Point3
  • perspective projections: Perspective, PerspectiveFov, Ortho
  • spatial transformations: AffineMatrix3, Transform3

Not all of the functionality has been implemented yet, and the existing code is not fully covered by the testsuite. If you encounter any mistakes or omissions please let me know by posting an issue, or even better: send me a pull request with a fix.

Conventions

cgmath interprets its vectors as column matrices (also known as "column 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 multiplication operator for Matrix * Vector, but not Vector * Matrix.

Features

Swizzling

This library offers an optional feature called "swizzling" 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

let v = Vector3::new(1.0, 2.0, 3.0);

then v.xyxz() produces a

Vector4 { x: 1.0, y: 2.0, z: 1.0, w: 3.0 }

and v.zy() produces a

Vector2 { x: 3.0, y: 2.0 }

SIMD optimizations

The current SIMD support depends on the deprecated "simd" package as well as the unstable "specialization" feature. To build this code, a pre-1.33 nightly build of Rust is required, e.g. 2019-01-01-nightly. Though the code is not useful in its present form, it has some worth preserving as starting point for a future migration (see #490).

Limitations

cgmath is not an n-dimensional library and is aimed at computer graphics applications rather than general linear algebra. It only offers the 2, 3, and 4 dimensional structures that are more than enough for most computer graphics applications. This design decision was made in order to simplify the implementation (Rust cannot parameterize over constants at compile time), and to make dimension-specific optimisations easier in the future.

Contributing

Pull requests are most welcome, especially in the realm of performance enhancements and fixing any mistakes I may have made along the way. Unit tests and benchmarks are also required, so help on that front would be most appreciated.

cgmath's People

Contributors

aloucks avatar atheriel avatar bfops avatar bitshifter avatar bors[bot] avatar brendanzab avatar csherratt avatar daseinphaos avatar elrnv avatar emberian avatar hclarke avatar jminer avatar kvark avatar lukaskalbertodt avatar luqmana avatar maikklein avatar maplant avatar mhintz avatar milibopp avatar mystise avatar neuschaefer avatar nnemec avatar nstoddard avatar osspial avatar ozkriff avatar rsaarelm avatar simnalamburt avatar spearman avatar tomaka avatar victorkoenders avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cgmath's Issues

Why not references in the `Intersect` tuples?

Right now, the Intersect trait is implemented for tuples of values, like this:

(Sphere<S>, Ray3<S>)

The problem is that you have to copy the values if you want to use them after testing the intersection. For example:

let inter = (my_sphere.clone(), my_ray.clone().intersection();
some_other_function(&my_sphere, &my_ray);

To avoid that copying, could we implement Intersect for tuples of references instead? Like this:

(&Sphere<S>, &Ray3<S>)

Or perhaps tuples of references and tuples of values could both be supported.

Point-point arithmetic should be available

It would be very useful (for me) if you could use standard operators with points,
that operated on each element piecewise (e.g. P1 {3, 1} + P2 {2, 2} = P3 {5, 3}).

To do this would require changing the point interface to include the function:

fn add_p(&self, p: &Self) -> Self;

as well as the optional functions:

fn add_self_p(&mut self, p: &Self);
fn sub_self_p(&mut self, p: &Self);

And to change the signature of sub_p to:

fn sub_p(&self, p: &Self) -> Self;

The old behaviour of sub_p could be achieved by chaining it with a to_vec() call, or
(if it's a really common operation), a new method could be added to the interface.

Benchmarks are optimized away by the compiler

I was looking into having Cargo run the benchmarks instead of using a Makefile (see #135). Cargo uses full optimizations to build the benchmarks (which I think absolutely makes sense), while this project's custom Makefile does not. And thereby many benchmarks were completely optimized away by the compiler. This is probably due to the fact, that it knows too much at compile-time.

One can use random input data or use test::black_box to alleviate this. In their current form most benchmarks are not really useful.

For reference, here is the output of running the benchmarks compiled with full optimizations:

test bench_matrix2_add_m          ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_add_self_m     ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_div_s          ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_div_self_s     ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_mul_m          ... bench:         6 ns/iter (+/- 0)
test bench_matrix2_mul_s          ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_mul_self_m     ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_neg_self       ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_rem_s          ... bench:        17 ns/iter (+/- 0)
test bench_matrix2_rem_self_s     ... bench:        17 ns/iter (+/- 0)
test bench_matrix2_sub_m          ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_sub_self_m     ... bench:         0 ns/iter (+/- 0)
test bench_matrix2_transpose      ... bench:         2 ns/iter (+/- 0)
test bench_matrix2_transpose_self ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_add_m          ... bench:         2 ns/iter (+/- 0)
test bench_matrix3_add_self_m     ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_div_s          ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_div_self_s     ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_mul_m          ... bench:         7 ns/iter (+/- 0)
test bench_matrix3_mul_s          ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_mul_self_m     ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_neg_self       ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_rem_s          ... bench:        38 ns/iter (+/- 1)
test bench_matrix3_rem_self_s     ... bench:        38 ns/iter (+/- 1)
test bench_matrix3_sub_m          ... bench:         2 ns/iter (+/- 0)
test bench_matrix3_sub_self_m     ... bench:         0 ns/iter (+/- 0)
test bench_matrix3_transpose      ... bench:         3 ns/iter (+/- 0)
test bench_matrix3_transpose_self ... bench:         0 ns/iter (+/- 0)
test bench_matrix4_add_m          ... bench:         2 ns/iter (+/- 0)
test bench_matrix4_add_self_m     ... bench:         2 ns/iter (+/- 0)
test bench_matrix4_div_s          ... bench:         0 ns/iter (+/- 0)
test bench_matrix4_div_self_s     ... bench:         0 ns/iter (+/- 0)
test bench_matrix4_mul_m          ... bench:         7 ns/iter (+/- 0)
test bench_matrix4_mul_s          ... bench:         0 ns/iter (+/- 0)
test bench_matrix4_mul_self_m     ... bench:         7 ns/iter (+/- 0)
test bench_matrix4_neg_self       ... bench:         0 ns/iter (+/- 0)
test bench_matrix4_rem_s          ... bench:        67 ns/iter (+/- 0)
test bench_matrix4_rem_self_s     ... bench:        67 ns/iter (+/- 0)
test bench_matrix4_sub_m          ... bench:         2 ns/iter (+/- 0)
test bench_matrix4_sub_self_m     ... bench:         0 ns/iter (+/- 0)
test bench_matrix4_transpose      ... bench:         5 ns/iter (+/- 0)
test bench_matrix4_transpose_self ... bench:         3 ns/iter (+/- 1)

Proposal: Triangles

Adding triangles to the library could be useful to then provide built-in functions such as plane-triangle and frustum-triangle intersections. However, the exact representation of a triangle is always application-dependent: it could be 3 vertices, 3 vertices and a normal, 3 indices into some array of vertices, etc. For this reason, in the application I am currently writing, I have written a Triangle trait to abstract away particular triangle representations:

pub trait Triangle<S> {
    fn p0 (&self) -> Point3<S>;
    fn p1 (&self) -> Point3<S>;
    fn p2 (&self) -> Point3<S>;
}

Once the trait is set up, you can provide a whole bunch of useful functions:

pub fn supporting_plane<S: BaseFloat> (t: &Triangle<S>) -> Option<Plane<S>>
pub enum PlaneIntersection {
    Front,
    Back,
    Intersecting,
    Containing
}

pub fn intersect<S: Num + BaseFloat> (p: &Plane<S>, t: &Triangle<S>) -> PlaneIntersection

When the client code wants to use these functions, they simply write a Triangle implementation for their particular triangle representation:

struct SimpleTriangle {
    _p0: Point3<f32>,
    _p1: Point3<f32>,
    _p2: Point3<f32>
}

impl Triangle<f32> for SimpleTriangle {
    fn p0 (&self) -> Point3<f32> { self._p0 }
    fn p1 (&self) -> Point3<f32> { self._p1 }
    fn p2 (&self) -> Point3<f32> { self._p2 }
}

and then they are ready to roll.

What do you think about integrating something like this into cgmath?

Remove array module

I'm beginning to feel like Array is making our type signatures really ugly. It also makes the API more complex. The closures also make it harder for LLVM to perform optimizations. I would rather have abit of code duplication in exchange for simplicity.

Few more suggested functions for quaternion module

fn transform_vec(&self, v: Vec3<S>) -> Vec3<S> {
  self.mul_q(&Quat::from_sv(0., v)).mul_q(&self.conjugate()).v
}

Rotates a Vec3 by the quaternion, assuming it's of unit length. I would think this is one of the most common operations one would want to do on a quaternion, have I made a mistake in thinking it's not in the library already?

fn between_vecs(a: Vec3<S>, b: Vec3<S>) -> Quat<S>;

Would return the minimum-angle spatial rotation needed to rotate a to b (the axis of rotation would be a cross b). This can be implemented using from_axis_angle but we can save a few trigonometric operations with a direct implementation. I needed this function at one point, but I'm not sure if it's common in other libraries.

fn to_mat4(&self) -> Mat4<S> {
  self.to_mat3().to_mat4()
}

For the sake of convenience, implement the ToMat4 trait.

Assertion macros for approximate equality

Testing code with floating point numbers and cgmath data structures one can use assertions on ApproxEq types:

assert!(a.approx_eq(&b));
assert!(a.approx_eq_eps(&b, eps));

This does not yield readable error messages upon failure. Arguably it is also a bit ugly to read/write. I propose adding macros similar to assert_eq! to alleviate this, e.g.:

assert_approx_eq!(a, b);
assert_approx_eq_eps!(a, b, eps);

I could go ahead and prepare a patch but I wanted to check first, if there is a reason why this has not been done yet or if anybody has objections to that.

Provide GLM interface

GLM is well known. It would be nice to have a compatible interface.

We don't need to change existing code, we can just add glm module with some functions, so that glm::rotate, for example, produces a matching Matrix4.

Edit: I don't have much GLM experience, so maybe someone else wants to try this out?

Changes to core Neg causing problems

I have commented out the offending lines in my branch here just to get my project compiling again as I wasn't sure what to do about this. It might be the case that you need UBaseNum, or just say no unsigned geometries!

impl Mul for Matrix{2,3,4} produces results that do not agree with `fn mul_m`

I would have thought that matrix_a * matrix_b was just a convenient shorthand for matrix_a.mul_m(&matrix_b).

But apparently this is not the case:

extern crate cgmath;

use cgmath::matrix::Matrix3;
use cgmath::matrix::Matrix;

fn main() {
    let m_a = Matrix3::new( 5.0,  4.0,  0.0,
                           -3.0,  1.0, -1.0,
                            3.0,  2.0, -2.0);
    let m_b = Matrix3::new( 5.0,  6.0,  4.0,
                            7.0,  0.0, -1.0,
                            1.0,  2.0, -2.0);
    println!("m_a.mul_m(m_b): {}", m_a.mul_m(&m_b));
    println!("m_b.mul_m(m_a): {}", m_b.mul_m(&m_a));
    println!("m_a * m_b: {}", m_a * m_b);
    println!("m_b * m_a: {}", m_b * m_a);
}

Transcript of run:

% ./bug
m_a.mul_m(m_b): [[19, 34, -14], [32, 26, 2], [-7, 2, 2]]
m_b.mul_m(m_a): [[53, 30, 16], [-9, -20, -11], [27, 14, 14]]
m_a * m_b: [[25, 24, 0], [-21, 0, 1], [3, 4, 4]]
m_b * m_a: [[25, 24, 0], [-21, 0, 1], [3, 4, 4]]

I have not yet dived deeply into the code to attempt to explain this phenomenon. But I thought I would report it and see if someone else reported back with an explanation for the current behavior.

SIMD support

This is a huge task, but I think the long term benefits are worth it. It would be helpful if we added some simd support for some operators.

We can split up BaseFloat to ScalarFloat and SIMDFloat (Suggestions for better names?). SIMDFloat is a subset of Float of primitives that can map to a vector of elements. The idea is not that we try and use a f32x4 to represent a single Vector4, but rather to create Vector4<f32x4>. This would mean you are operating on four vector4's at a time.

Benefits:

  • Ability to accelerate cgmath using simd

Cons:

  • Complexity will increase, Some traits might need to be split up because only a subset of the API can be used in a SIMD like way
  • We will also need to maintain an additional SIMD float type.

Thoughts?

rexport every struct from the root namespace

From user perspective writing cgmath::vector::Vector2 rather then cgmath::Vector2 adds nothing except verbosity. Everything is implemented as a methods, so name collisions are unlikely.

API is confusing

@cmr has expressed concerns on #rust-gamedev that the API is confusing, especially in regards to transformations. He suggested an API closer to that of GLM. Of course that would be rather difficult seeing as Rust's overloading isn't nearly as expressive as C++'s. I am curious however as to what you guys think regarding the current API. What could be changed and improved?

cc. @ozkriff @csherratt

Matrix4::invert breaks because of low precision in approx_eq

When inverting a matrix, we check that the determinant is non-zero using approx_eq. If the determinant is approximately zero, the inversion fails.

But approx_eq only goes to a precision of 10^-5.

That's problematic, because projection matrices often have very small determinants. This is especially true when the camera is zoomed out very far. In that case, we're transforming a large volume of world space to the clip space volume. Which means that the determinant tends to be very small. (This is because the determinant is the ratio of the two volumes.)

The future of cgmath

I currently am stretching myself quite thin when it comes to projects, and I feel like I am no longer able to adequately steer the development of cgmath going forwards. I am also considering moving to @sebcrozet's nalgebra library for my future projects. I can continue to merge PRs, but I can't see myself contributing much heading into the future. If somebody has the passion or energy to spare to keep things going, I am also happy to transfer ownership of the repository.

outdated docs??

Hi there, I don't know if this is still going or not? but apparently let mat4 = rota.to_matrix4(); or something like that is now let mat4: Matrix4<_> = rota.into();.

Have been trying to convert the step "A simple transformation" in https://open.gl/transformations to cgmath, will continue trying but if you could provide the "exact" same translation pointed there will love it.

Also... if this will be not more under development should I switch to nalgebra or some like that? (what about aabb and so on???

Fix transmute error

@kvark reports an error on the latest nightly:

src/cgmath/plane.rs:70:18: 70:27 error: cannot transmute from a type that contains type parameters

Basis3::from_axis_angle with unnormalized axis -> non-orthogonal matrix

extern crate cgmath;

use cgmath::*;

fn main() {
    let axis = vec3::<f64>(1.0, 0.0, 1.0);
    let a = Basis3::from_axis_angle(&axis, deg(75.0).to_rad());
    let b = Basis3::from_axis_angle(&axis.normalize(), deg(75.0).to_rad());

    println!("a = {:?}", a.as_matrix3());
    println!("b = {:?}", b.as_matrix3());
    println!("ฮ” = {:?}", a.as_matrix3().sub_m(b.as_matrix3()));
    assert!(a.approx_eq(&b));
}

I see different ways to fix this:

  • Document this behaviour in Basis3::from_axis_angle and possibly Rotation3::from_axis_angle. Assert that axis.length2() is approximately 1.0 at entry.
  • Normalize all incoming axis vectors. Document that external normalization is not required (just in case someone figures out that the algorithm needs a normalized axis vector).

Implement IterBytes for various types

Vector/point types, quaternions, and various other types would possibly benefit from implementations of std::to_bytes::IterBytes so they can be used as keys in std::hashmap::HashMaps.

I admit to being a Rust newbie, and I don't have a good sense of the idioms yet; otherwise I would be happy to open a pull request :)

Add simple constructors

Hyeon: (what's the github name?)

Personally cgmath looks a bit needlessly complicated // ex: why Vector3::new(x, y, z) not vec3(x, y, z)

Use tuples for constructing types

Once @nikomatsakis' trait reform lands, I would love to be able to do:

let vec4 = (vec2a, vec2b).to_vec4();
let vec3 = (1.3, vec2a).to_vec3();
let mat3 = (vec3, (vec2a, 3.0).to_vec3(), vec3).to_mat3();

inverse matrix4 bug

I noticed that inverse() returns some strange results when I try and invert my view matrix. Not every matrix is correctly inverted, I captured a few that we can use to test.

view [[-0.131917, -0.76871, 0.625846, 0], [-0, 0.631364, 0.775487, 0], [-0.991261, 0.1023, -0.083287, 0], [0, -1.262728, -1.550973, 1]]
view^1 [[0, 0, -1.008816, 0], [-0.614136, 0.3556, 0.081729, 0], [0.5, 1, -0.06654, 0], [0, 2, 0, 1]]
view^1*view [[0.785015, 0.352493, 0.02861, 0], [0, 1, -0, 0], [-0.10447, -0.04691, 1.013903, 0], [0, 0, 0, 1]]

view [[0.065455, -0.720002, 0.690879, 0], [-0, 0.692364, 0.721549, 0], [-0.997856, -0.047229, 0.045318, 0], [0, -1.384727, -1.443098, 1]]
view^1 [[5.333333, -2.666667, -0.652307, 0], [-0.694769, 0.749559, -0.045574, 0], [0.666667, 0.666667, 0.04373, 0], [0, 2, 0, 1]]
view^1*view [[1.309913, -0.253644, 0.020329, 0], [0, 1, -0, 0], [-5.258871, 2.65576, 0.655042, 0], [0, 0, 0, 1]]

view [[0.409936, 0.683812, -0.603617, 0], [0, 0.661778, 0.7497, 0], [0.912114, -0.307329, 0.271286, 0], [-0, -1.323555, -1.499401, 1]]
view^1 [[0, 0, 1.096354, 0], [0.755239, 0.755843, -0.339431, 0], [-0.666667, 0.666667, 0.299623, 0], [0, 2, 0, 1]]
view^1*view [[0.918853, 0.114443, 0.03647, 0], [0, 1, 0, 0], [-0.412964, -0.051435, 1.185601, 0], [0, 0, 0, 1]]

view [[-0.160691, -0.772608, 0.614211, 0], [-0, 0.622298, 0.78278, 0], [-0.987005, 0.125786, -0.099998, 0], [0, -1.244597, -1.565561, 1]]
view^1 [[0, 2, -1.013166, 0], [-0.838591, 0.349061, 0.136529, 0], [0.666667, 1, -0.108538, 0], [0, 2, 0, 1]]
view^1*view [[1.057376, 0.023141, -0.009341, 0], [0, 1, 0, 0], [-0.172148, -2.030101, 1.028027, 0], [-0, 0, 0, 1]]

Normally it should look like this

view [[-0.681878, -0.403519, 0.610094, 0], [0, 0.83407, 0.551659, 0], [-0.731466, 0.376164, -0.568734, 0], [-0, -1.66814, -1.103317, 1]]
view^1 [[-0.681878, 0, -0.731466, 0], [-0.403519, 0.83407, 0.376164, 0], [0.610093, 0.551659, -0.568734, 0], [0, 2, 0, 1]]
view^1*view [[1, 0, 0, 0], [0, 1, 0, 0], [-0, 0, 1, 0], [-0, 0, 0, 1]]

transform::ToComponents trait

Currently, Transform trait doesn't enforce the internal representation to be affine. It's fine for as long as you just transform stuff with it. Whenever you want to convert the transformation to, let's say, a GLSL friendly form, you need to constraint the internal representation more.

I propose the ToComponents to provide an ability to extract translation, rotation and scale separately. The real question here is how to represent the rotation. When we work with a matrix, we can't just return a 3x3 portion of it, because it also includes the scale. Since we have to process the matrix in some way, we might as well demand a quaternion as an output. However, the user might also want Euler angles out of it. Hence the rotation has to support everything:

trait ToComponents<S> {
    type Rotation: Rotation3<S>;
    fn to_rotation(&self) -> Self::Rotation;
    fn to_translation(&self) -> Vector3<S>;
    fn to_scale(&self) -> Vector3<S>;
}

Alternative name: AffineTransform.

cgmath::vector::Vector shadows std::vec::Vector

In modules that have

use cgmath::vector::Vector;

in them, you can no longer take slices from native rust vectors, eg.

let a = ~[1, 2, 3];
let b = a.as_slice();

"error: type ~[int] does not implement any method in scope named as_slice"

The method as_slice is supposed to come from module std::vec::Vector.

A workaround is to rename the cgmath import, eg.

use cgVector = cgmath::vector::Vector;

Possible quick fix: Rename cgmath::vector::Vector into Vec?

Question: references and code style

I don't quite follow your logic of when to pass arguments by value and when to pass by reference. For example, on the one hand you have:

fn look_at(dir: &Vector3<S>, up: &Vector3<S>) -> Matrix3<S>

which seems reasonable enough, although passing two Vector3 by value shouldn't be too expensive. However, on the other hand you have:

fn new(left: Plane<S>, right: Plane<S>, bottom: Plane<S>, top: Plane<S>, near: Plane<S>, far: Plane<S>) -> Frustum<S>

which, as far as I understand, will imply the copy of 6 planes to construct a Frustum.

Could you shed some light into this?
Thanks.

Rotation3 could use some clarification

There are lots of conventions as to what constitutes Euler angles. I think it would be good to make the convention used in cgmath explicit in the documentation of the Rotation3 trait (maybe adding some neat diagrams with axes and angles to visually convey the sense of rotation?). Another idea that goes along these lines: the convention should be required by any Rotation3 implementation and asserting it sounds like a great property to test using quickcheck.

All this is probably low priority atm but I thought I'd open this issue after spending a bit of time empirically determining the convention.

ToX -> Into<X>

Instead of the ToX traits, did you consider implementing From ?

Many methods only work with glob import

For example, working on my current project, I was only able to use Vector3::new(...).normalize(); if I did use cgmath::*; instead of a (I thought) cleaner use cgmath::{Vector3, Point3, etc.};
In addition, this requirement doesn't seem to be documented anywhere.

Building a view matrix

Currently it is difficult to correctly assemble a view matrix. Using Transform3D will end up with a matrix that is always originated towards or away from the origion.

Code example:

extern mod cgmath;

use cgmath::transform::Transform3D;
use cgmath::matrix::{Mat4, ToMat4, Matrix};
use cgmath::vector::{Vec3, Vec4};
use cgmath::projection::perspective;
use cgmath::quaternion::Quat;
use cgmath::angle::{deg, ToRad};

fn project(projection: &Mat4<f32>, view: &Mat4<f32>, model: &Mat4<f32>)
{
    // this represents any vertex, we just use one at the origin because it is easy.
    let vec = Vec4::new(0f32, 0f32, 0f32, 1f32);

    // standard way to project the point
    let projected_vec = projection.mul_m(view).mul_m(model).mul_v(&vec);

    // OpenGL does this for you, This is only needed to compare viewports
    let point = Vec3::new(projected_vec.x / projected_vec.w,
                          projected_vec.y / projected_vec.w,
                          projected_vec.z / projected_vec.w);

    // x, y must be between -1 and 1. z Must be greater then 0 and less then 1 to be in the viewport.
    println!("x: {} y: {}, z: {}", point.x, point.y, point.z);

}

fn main()
{
    let projection = perspective(deg(45f32), 1f32, 0.1f32, 10f32);
    let model = Transform3D::new(1f32,
                                 Quat::from_axis_angle(&Vec3::new(0f32, 0f32, 0f32), deg(0f32).to_rad()),
                                 Vec3::new(0f32, 0f32, 0f32));

    // rotate the camera when keeping it a position 0, 0, -5.
    // the model is always at the origin
    for angle in std::iter::range_step(0, 360, 30) {
        let view = Transform3D::new(1f32,
                                    Quat::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(angle as f32).to_rad()),
                                    Vec3::new(0f32, 0f32, -5f32));
        project(&projection, &view.get().to_mat4(), &model.get().to_mat4());        
    }
}

Output::

x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798
x: 0 y: 0, z: 0.979798

One way to create the correct matrix would be as follows

 let translate = Mat4::new(1f32, 0f32, 0f32, 0f32,
                           0f32, 1f32, 0f32, 0f32,
                           0f32, 0f32, 1f32, 0f32,
                           0f32, 0f32, -5f32, 1f32);
 let rotate = Quat::from_axis_angle(&Vec3::new(0f32, 1f32, 0f32), deg(angle as f32).to_rad()).to_mat3().to_mat4();
 let view = rotate.mul_m(&translate);
project(&projection, &view, &model.get().to_mat4());  
x: 0 y: 0, z: 0.979798
x: -1.393847 y: 0, z: 0.973547
x: -4.18154 y: 0, z: 0.939394
x: -40503776 y: 0, z: -677866.375
x: 4.181539 y: 0, z: 1.10101
x: 1.393847 y: 0, z: 1.066857
x: -0 y: 0, z: 1.060606
x: -1.393847 y: 0, z: 1.066857
x: -4.181541 y: 0, z: 1.10101
x: 40503776 y: 0, z: -677866.375
x: 4.181542 y: 0, z: 0.939394
x: 1.393847 y: 0, z: 0.973547

I think there should probably be a library function/object to help create this matrix.

Frustum::from_matrix4 is wrong

For example,

let persp = cgmath::PerspectiveFov {
    fovy: cgmath::Deg { s: 90.0f32 },
    aspect: 1.0,
    near: 0.1,
    far: 1000.0
};
let frustum = Frustum::from_matrix4(persp.to_matrix4());

println!("{}", frustum.far);

println!("{}", persp.to_matrix4().mul_v(&Vector4::new(0.0, 0.0, 1.0, 1.0)));
println!("{}", persp.to_matrix4().mul_v(&Vector4::new(0.0, 0.0, -1.0, 1.0)));
println!("{}", persp.to_matrix4().mul_v(&Vector4::new(0.0, 0.0, -999.0, 1.0)));
println!("{}", persp.to_matrix4().mul_v(&Vector4::new(0.0, 0.0, -1001.0, 1.0)));

outputs 0x + 0y + 0.000999z - 0.999999 = 0 for the far plane, i.e. z=1000, when in fact the plane should be z=-1000.

I think this is because Plane::from_vector4 uses the vector's fourth component as D, but the way from_matrix4 uses it, it should be -D. For example, the left clipping plane should have the equation v . (row(3) + row(0)) = 0 (taking the rows from the projection matrix), but since planes are represented with equations of the form A_x + B_y + C*z - D = 0, the D is negated.

I'm not sure whether Plane::from_vector4 is doing what's intended, but either it needs to negate D, or Frustum::from_matrix4 needs to be fixed somehow.

Compute OBB2/OBB3 from a set of points.

As far as I can tell from the documentation, there are no such functions. I am aware that there are different techniques to go about this, but I still think the library could be improved by providing one implementation.

AABB collision detection could be faster

For a 2D AABB, 4 checks are being performed to determine if a point is inside.

This can be cut down to 2 checks, at the cost of the internals getting a little bit switched around.
Instead of storing AABBs as 2 points, they can be stored as a centre and extent:

pub struct Aabb2<S> {
    centre: Point<S>,
    extent: Vector2<S>,
}

(I don't know what the functional difference is between Points and Vectors, but I think it doesn't matter too much)

With that, the collision checking becomes a bit more efficient, as you can take the difference of 2 centres, and compare it to the sum of the extents, effectively halving the checks required. (I wrote a quick and dirty example here). The same idea can be applied to aabb-point collision by giving assuming points have an extent of zero.

The reason I haven't put this straight into a pull request is that it fundamentally changes the internals of aabbs in a way that may confuse people who aren't aware of the change. Also, the lack of an abs() method for integer types may require some sort of workaround, which I am not sure of.

Most of the information is in the example. (http://is.gd/XNKOEn)

Why PartialEq everywhere and not Eq?

I am planning to use Point3<i32>, Point3<f32> inside structs in my code that need to be hashable. For that, I understand they need to implement Hash + Eq, but they only implement Hash + PartialEq.

Is that just an oversight, or is there a reason?

Overload basic operators

Vector +- Vector
Point +- Vector
Matrix * Vector
Quat * Vector
...
probably a lot more, but we need to keep the behavior strictly obvious (i.e. not overloading Vector*Vector as a dot product)

Update the version on crates.io

Now that breaking changes are mostly gone (I don't think you're using std::io, std::os, etc.), would it be possible to update the version of nalgebra on crates.io?

Update lib with associated types.

#[old_impl_check] was recently removed:

src/line.rs:31:1: 31:18 error: The attribute `old_impl_check` is currently unknown to the the compiler and may have meaning added to it in the future
src/line.rs:31 #[old_impl_check]
               ^~~~~~~~~~~~~~~~~
src/line.rs:31:18: 31:18 help: add #![feature(custom_attribute)] to the crate attributes to enable
src/ray.rs:28:1: 28:18 error: The attribute `old_impl_check` is currently unknown to the the compiler and may have meaning added to it in the future
src/ray.rs:28 #[old_impl_check]
              ^~~~~~~~~~~~~~~~~
src/ray.rs:28:18: 28:18 help: add #![feature(custom_attribute)] to the crate attributes to enable
error: aborting due to 2 previous errors

Removing the attributes results in:

src/line.rs:31:6: 31:7 error: the type parameter `S` is not constrained by the impl trait, self type, or predicates [E0207]
src/line.rs:31 impl<S: BaseNum, V: Vector<S>, P: Point<S, V>>  Line<P> {
                    ^
src/line.rs:31:18: 31:19 error: the type parameter `V` is not constrained by the impl trait, self type, or predicates [E0207]
src/line.rs:31 impl<S: BaseNum, V: Vector<S>, P: Point<S, V>>  Line<P> {
                                ^
src/ray.rs:28:6: 28:7 error: the type parameter `S` is not constrained by the impl trait, self type, or predicates [E0207]
src/ray.rs:28 impl<S: BaseNum, V: Vector<S>, P: Point<S, V>> Ray<P, V> {
                   ^
error: aborting due to 3 previous errors

Apparently cgmath needs associated types from the ground up to solve this problem.

Use top corner instead of center for position in the axis-aligned box struct

I'd like to use Aabb2 as a rectangle type in my code, and want the basic representation to match the range of points inside the box instead of the center: struct Aabb2 { top: Point2, size: Vec2 }. Rectangle code is mostly concerned with the edges instead of the center, so accessing those with simple arithmetic that doesn't involve dividing size components by two would be quite nice.

Code that needs the box center can use a method which derives the center point from the top and size data.

Build failing

unfortunately, cgmath is failing to build on rust-master right now. a verbose output

error: failed to find an implementation of trait std::ops::Add<vector::Vec2<S>,vector::Vec2<S>> for vector::Vec2<S>
in expansion of #[deriving(Zero)]

Breaking, missing TotalOrd impl

cgmath-rs $ make
mkdir -p lib
rustc --out-dir=lib -O src/cgmath/lib.rs
src/cgmath/vector.rs:72:58: 72:61 error: failed to find an implementation of trait std::cmp::TotalOrd for S
src/cgmath/vector.rs:72     #[inline] fn comp_min(&self) -> S { self.fold(|a, b| min(a.clone(), b.clone())) }

(with bleeding edge rust off master)

Edit: didn't catch that breaking build icon from travis, so I reckon you're on top of this one ;)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.