Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ngeom library #23

Closed
brendanzab opened this issue Oct 2, 2014 · 14 comments
Closed

ngeom library #23

brendanzab opened this issue Oct 2, 2014 · 14 comments

Comments

@brendanzab
Copy link
Contributor

I am seriously considering transitioning from my cgmath library to nalgebra, seeing as it is now insanely well polished, and it would allow me to use your collision and physics stuff without having to re-invent the wheel. Some of the things I love about cgmath though is:

  • points as a separate concept to vectors with the following operators:
    • (*) : Point a -> a -> Point a
    • (/) : Point a -> a -> Point a
    • (+) : Point a -> Vector a -> Point a
    • (-) : Point a -> Point a -> Vector a
  • Separate types for radian and degree units
  • Projection structures with conversions to matrices

These things don't really make sense for a linear algebra library, but is it possible that we could have a separate library for these?

@sebcrozet
Copy link
Member

Some comments about your two last items. I will read your articles before commenting the first one.

Separate types for radian and degree units

While I am aware of the benefit of encoding the angular unit on the type (avoiding mixups between degrees and gradiants), I am not sure this benefit is enough to justify making the API more complicated. In fact, I am not sure anybody use degrees instead of radians in serious projects; and using both on the same project feels even less probable (but I might be mistaken).

Projection structures with conversions to matrices

Is it necessary to use structures instead of free functions (like nalgebra::perspective3d, cgmath::perspective, etc.)? I think the existance of a structure would be justified iff the user could do something useful with them more than just converting them to matrices.

One more thing that has to be added to nalgebra: quaternions. I never took the time to implement them…

@brendanzab
Copy link
Contributor Author

I guess less so degrees - more so the distinction between angles and scalars. I understand your hesitation in terms of API complexity though.

nalgebra::perspective3d could do the job. Do you have ortho stuff?

cc. @kvark

@tomaka
Copy link
Contributor

tomaka commented Oct 2, 2014

Is it necessary to use structures instead of free functions (like nalgebra::perspective3d, cgmath::perspective, etc.)? I think the existance of a structure would be justified iff the user could do something useful with them more than just converting them to matrices.

I don't think that a whole structure is justified, but some utility functions to build common matrices are definitely useful.

For example here is a code with cgmath that I'd rewrite for nalgebra if it had similar functions: https://github.com/tomaka/spine-rs/blob/09123d9763933ec48f8a369370b34fd07a67ee99/src/lib.rs#L431

@sebcrozet
Copy link
Member

Do you have ortho stuff?

@bjz No I don't have a nalgebra::ortho3d, but it can be added if it is needed.

For example here is a code with cgmath that I'd rewrite for nalgebra if it had similar functions: https://github.com/tomaka/spine-rs/blob/09123d9763933ec48f8a369370b34fd07a67ee99/src/lib.rs#L431

@tomaka nalgebra does have functions to build common matrices. See the constructors of Rot2 and Iso2 (Iso2 designs a rotations followed by a translation). The nalgebra equivalent of the line you cited is:

let rot = Rot2::new(Vec1::new(self.rotation.to_radians()));

// Convert to a Mat4.
let rot_mat = na::to_homogeneous(&na::to_homogeneous(&rot));

You have to call to_homogeneous once to create a 3D matrix and a second time to create a 4D one (by the way, it is very strange that you create a 2D rotation matrix and transform it to a 4D matrix − it should be a 3D matrix if you are using homogeneous coordinates for a 2D application).

The equivalent of your translation-rotation-scale matrix construction https://github.com/tomaka/spine-rs/blob/09123d9763933ec48f8a369370b34fd07a67ee99/src/lib.rs#L430-L434 is:

let scale = Mat4::new(self.scale.0, 0.0, 0.0, 0.0, 0.0, self.scale.1, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
let rot_with_trans = Iso2::new(Vec2::new(self.position.0, self.position.1), Vec1::new(self.rotation.to_radians()));
let rot_with_trans_mat = na::to_homogeneous(&na::to_homogeneous(&rot_with_trans));

rot_with_trans_mat * scale

@kvark
Copy link
Contributor

kvark commented Oct 2, 2014

I highly value the points @bjz mentions that are missing in nalgebra. Point and angle types bode well with typed oriented programming.

As for the projection structs - I find them quite useful. First, because you can store them in, to say, cameras and lights, publicly for the user to play with (how do you expect the user to modify your matrix?). Second, because not everyone needs matrices in the end. In my past project I was doing the projection in the shader manually, no matrices involved.

@sebcrozet
Copy link
Member

About Pnt vs. Vec

What I like about the distinction between points and vectors:

  • It makes it clear how Iso* and ToHomogeneous will act on them.
  • It might prevent errors (as always with a stronger type system).
  • It does not change the semantic of Vec* other than when we use it
    for geometrical operations.

What I don't like:

  • This is not a very popular distinction in most professional
    linear algebra (points do not form a vector space) and geometrical
    computation libraries I am aware of. The only framework I know
    that actively uses this distinction is OpenCascade (a CAD
    framework). However, this unpopularity might simply be due to people
    not being used to type oriented programming yet.
  • It complicates the implementation a lot (until the trait reform is
    completely implemented in rustc). More specifically, a double
    dispatch trick would have to be used so that e.g. a Rot3 can
    rotate a Vec3 as well as a Pnt3.

I would like to see examples in the gamedev community where this distinction really becomes handy. I will also try to see how it would look like on nphysics and ncollide.

About Rad and Deg

While having types for angles would be great, I think the Rad and Deg cannot be added if we do not add all the other units (seconds, meters, etc.) too. For example, if I have an object with an angular velocity of w, and I compute something like: avel = magnitude(w * time), I should end up with avel being radians (not a regular real number). It is not possible to obtain such result without having types like: w : Vec3<RadPerSec<f32>>, time : Sec<f32>, avel: Rad<f32>.

So if we want to introduce the distinction between unitless reals and "everything else" we should do it with most common SI units. If we do it only for angles, some computations will be ill-typed because they should return an angle instead of an unitless real.

@kvark
Copy link
Contributor

kvark commented Oct 3, 2014

@sebcrozet I mostly agree. The thing about Rad/Deg is that it's error prone in practice. People specify degrees easier, while the math appreciates radians more. There is another aspect here as well: whatever distance unit (for example) the user wants is completely irrelevant to the math routines it calls. Hence, the library doesn't need to know about meters and such. At the same time, angle types go into trigonometry functions, where the units matter.

@brendanzab
Copy link
Contributor Author

I am currently mucking around with some type level units atm, but it might be tricky without some type system features borrowed from Haskell land. Also the lack of multiple operator overloads is painful.

@cmr Do you know what the status of where is, so that we can move beyond the double-dispatch trick?

@emberian
Copy link

emberian commented Oct 3, 2014

I do not.

@sebcrozet
Copy link
Member

@bjz we should be able to drop the double dispatch trick as soon as rust-lang/rust#17669 lands.


After multiple experiments, here is what I will do.

About Pnt and Vec

I am going to add Pnt1 .. Pnt6 structures, and the related FloatPnt, AnyPnt traits. See #25.

The point/vector distinction is based on solid mathematical grounds (namely affine spaces) that enter the scope of linear algebra. They also help clarify a few things (ToHomogeneous, Iso*) and might prevent mistakes.

About projection structures

I am going to add them, see #26. I am not sure of the names yet. There will be one trait, namely, Projection or Projector that identifies linear applications that are indenpotent under application (i-e p(p) = p).

Being a linear application, a projector enters the scope of linear algebra.

About Rad and Deg

I am not convinced to add them. I understand that they can prevent common mistakes, but the lack of consistency when performing complex computations makes them unusable in a sound way. As I said before, for example, if we had a solid unit framework, we could do this:

let ang_vel: RadPerSec<f32> = RadPerSec::new(1.0);
let axis: Vec3<f32> = Vec3::new(1.0, 0.0, 0.0);
let axis_ang_vel: Vec3<RadPerSec<f32>> = ang_vel * axis;
let ang: Rad<f32> = na::norm(axis_ang_vel) * Sec::new(10.0);

Note the smooth transition between RadPerSec and Rad under multiplication by seconds.

But, since we do not have such framework, it is not possible to end up with a Rad<f32>, which is inconsistant with the whole philosophy of adding units. At best, adding Rad and Deg will prevent some errors on simple computations, but will feel incomplete for anything more evolved like physics simulation. As @kvark said, applications usually do not care of the scale of distance units like kg vs. g or m vs. mm. However, in my opinion, they do care about the relation between those units (e.g. rad/s), and we cannot have this right without a full unit framework (which should not be part of nalgebra).

Another significant problem with this lack of smooth transitions between units is that it makes it very dificult to write generic code with them because one will have to cast real numbers back to the correct unit (that might be an abstract type parameter) each time an irregularity like the one showed before happens. Also, without HKT, genericity wrt. the algebraic structure and not wrt the unit is too complicated.

@brendanzab
Copy link
Contributor Author

I agree with all of your design decisions here! :)

@sebcrozet
Copy link
Member

Points and projections structures are now part of nalgebra so I think I can close this now.
If anyone wish to discuss the design decisions or the Rad − Deg subject, feel free to open a dedicated issue.

@kvark
Copy link
Contributor

kvark commented Feb 2, 2015

@sebcrozet what do you think about Transform trait with Decomposed implementation in cgmath-rs? Do you consider this functionality to be in scope of nalgebra?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants