george's picture

Math module?

On the source force page, it's mentioned that OpenTK contains a maths module. Is this accurate? I don't see it anywhere.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler.'s picture

No, it´s not accurate, but the library still is in alpha stages ;) If you have an immediate need for a math library, I think there is one in Mono .Xna (http://code.google.com/p/monoxna/).

What functionality do you actually need? The plan for OpenTK is to have some simple vector, matrix and quaternion math and probably some noise generation functions. There will be a fully managed implementation (with horrible performance) which will be sped it up with unmanaged hand-optimized plugins. I´ve been holding back until I can find a way to detect and use available plugins without incurring a large speed penalty (if you have any ideas how to do this they´d be appreciated!)

george's picture

I have no immediate need, I was just curious.
A set of classes that match the directx vector/matrix stuff would be useful, especially if the opengl interface could be extended to use them.

As for the performace of a managed implementation, a lot of operations are so small (vector arithmetic, cross/dot product etc) the cost of managed->unmanaged conversion and back probably outweighs any speed gains you might get.

the Fiddler.'s picture

A set of classes that match the directx vector/matrix stuff would be useful, especially if the opengl interface could be extended to use them.
That's the idea - the opengl bindings are written in a way that allows overloads to be easily added.

I've started checking in code for the math module. It won't be ready for some time still, but (thankfully) it's a straightforward thing to write - a nice contrast to the platform-specific code.

JTalton's picture

One good CSharp math class is Sharp3D Math.
http://www.codeplex.com/Wiki/View.aspx?ProjectName=Sharp3D
It has not been updated in a while.

I ended up writing my own Vector, TexCoord, and Matrix classes. I found it useful to make the vectors such that they could never have their values changed. I had been running into problems where I had a dictionary whose keys were vectors, and if the vectors changed at some point, the dictionary would not work right. Not sure it is a great design, but I found it useful and like it very much.

On the subject of using generics, the C5 Generic Collection Library is a little nicer than the .NET Generics.
http://www.itu.dk/research/c5/

the Fiddler.'s picture

Thanks for the links, the C5 Generic Collection is superb!

Inertia's picture

Thanks for adding a Math library :) I've started building my own yesterday, because the lack of such a library really started to annoy me. Yours seems rather complete, i'll try to help where I can.

I've gone through most of yours (basically Vector3 and Matrix, because that is what I built yesterday and is fresh in memory), and got some suggestions:

1) Is there a reason why Normalize and Divide in the Vector3 class don't try{} for a division by 0 exception? I see you want the methods to operate at fast as possible, but this will crash applications badly when not caught.

2) Couldn't find any comparison helper for floats. Maybe you could add this to the Functions.cs (refactor and change accessibility as you wish):

private const float FLOATEQUALITYTHRESHOLD = 0.000001f;
 
private static bool IsFloatEqual( float f1, float f2 )
{
  return ( Math.Abs( f1 - f2 ) <= FLOATEQUALITYTHRESHOLD );
}

The reason for this is that precision issues could lead to a == comparision result in "false", although the compared floats should be considered equal and the result be "true".

3) I believe you will be better off with having less duplicate code. e.g. the implementation of Cross(3 params) would imho be safer to have like this:

public static void Cross(ref Vector3 left, ref Vector3 right, out Vector3 result)
{
  Vector3 result = new Vector3(left)
  result = Cross(result, right);
}

else there could be problems if result is null when calling the method.

4) Regarding the operator overloads, there's always manipulation of the left parameter. I've chosen to implement it in a way that only the Struct's Add(), Sub(), Mult(), Div() Methods manipulates it's instance, while all operator overloads and static functions will always allocate memory and return a new instance. I believe I see the reason why you chose this, writing VectorA += VectorB sure is nice, but not if you write VectorC = VectorA + VectorB you have changed VectorC and VectorA.
I guess that topic is kinda like the holy Linux vs. Windows war and both sides have their points. I just thought it should be mentioned before building more overloads (I'll definitely add some if I may).

5) I belive to have found a nice way to deal with Constructors for Vectors. Here's copy&paste of some of my code, I've used an float[3] to index the Vector's components to avoid unsafe, but you should get the idea of what my point is:

 public void SetTo( float x, float y, float z )
        {
            this.elements[0] = x;
            this.elements[1] = y;
            this.elements[2] = z;
        }
 
        public void SetTo( double x, double y, double z )
        {
            this.elements[0] = (float) x;
            this.elements[1] = (float) y;
            this.elements[2] = (float) z;
        }
 
        public void SetTo( Vector3 v )
        {
            this.elements[0] = v.X;
            this.elements[1] = v.Y;
            this.elements[2] = v.Z;
        }
 
        public Vector3( )
        {
            this.elements = new float[3];
            this.SetTo( Vector3.Zero );
        }
 
        public Vector3( float x, float y, float z )
        {
            this.elements = new float[3];
            this.SetTo( x, y, z );
        }
 
        public Vector3( double x, double y, double z )
        {
            this.elements = new float[3];
            this.SetTo( x, y, z );
        }
 
        public Vector3( Vector3 v )
        {
            this.elements = new float[3];
            this.SetTo( v );
        }

The nice thing about this is that you can make a copy of another vector's values with the SetTo function, without creating a new instance.

That's all for now, please don't take this as destructive critique. I like your work and am very impressed of the Quaternion bit, always considered it very hard to visualize and debug.

Edit: mhmm should probably have started a new topic instead.

objarni's picture

I'd really like the Vectors etc. to be immutable, ie. values _cannot_ change. It makes easier programming - decrease aliasing problems like the dictionary trouble mentioned above - and the C# allocation routine is insanely fast ...

What would be really great is to do a "real" comparison between a mutable an immutable Vector3 class in C# ... anyone did that? If the difference is less than 10% in speed I'd say it's not worth the trouble having mathematical entities changeable = mutable.

I usually go by the rule "if I need those extra 10% I'd hard-code everything inside the algorithm anyway" and make the general APIs, well, general and robust. Also, thinking of how vectors are used is rewarding -- spitting 3d models made up of relatively static vectors won't burden the gc very much (and we'll use vertex arrays or similar in GL anyway, right?), if that is the concern regarding the immutable/mutable controversy. Is that the controversy?

I think, and here I hazard, that the general use of a Vector3 class would be more of a "do precomputations/small amount of computations comfortably"-kind-of-thing than "I will use Vector3 in every single leaf of my grand world".

Of course, there is a third less-elegant (?) solution too -- and that is to have two different sets of classes for mathematical entities. One mutable (Vector3m?) and one immutable (Vector3). With all the hassle that will bring ..

Wow this got longer than I first thought :) guess it is not such a clear-cut case.

my 2 cc

Inertia's picture

Forgive my stupid question, but what is your problem storing all your immutable Vectors in one place, make sure you have no references from outside left to that place and just leave them be? Some people might want to implement a particle system, collision detection, etc., where building 1000s of immutable Vectors per frame will inevitably flood memory and degrade performance.

4.b) It seems in the Quaternion class the operator overloads aren't all used in a consistent way with the vector class. This will inevitably lead to the situation where bugs are introduced because some functions update the "left" parameter while others don't.

the Fiddler.'s picture

Wow, lots of stuff here, I'll try to address everything bit by bit.

@objarni:
I'd really like the Vectors etc. to be immutable, ie. values _cannot_ change. It makes easier programming - decrease aliasing problems like the dictionary trouble mentioned above - and the C# allocation routine is insanely fast ...

What would be really great is to do a "real" comparison between a mutable an immutable Vector3 class in C# ... anyone did that? If the difference is less than 10% in speed I'd say it's not worth the trouble having mathematical entities changeable = mutable.
To find out the performance impact of allocations, I timed 10^8 Vector3 additions. I made sure that the methods were JITed first, and that the whole loop wasn't optimized-out by the compiler. Here are the results (ns per iteration, 3.2GHz Core 2 Duo):

Noop					0,00440376ns
Vector3.Add(ref a, ref b, out res)	0,03766322ns
Add(a, b, out res)			0,08392346ns
res = Add(ref a, ref b)			0,09454702ns
res = Add(a, b)				0,11466584ns

Noop establishes the baseline performance of an empty loop to ensure the rest of the results make sense. The first two functions do not allocate memory, but show the relative cost of using pass-by-value vs pass-by-reference. The last two allocate a new Vector3 on the stack, something that brings a relatively significant performance hit - an immutable version of this struct would take this hit on every operation.

In any case, this is a micro-benchmark which might not be indicative of real-world performance. I do think, however, that mutable structs are more useful in normal use cases - what would you use Vectors for? Probably to calculate object physics, setup vertex arrays and, less likely, for vertex animation. In all these cases, an immutable struct would be less efficient. I really can't think of a normal use-case where immutability would be preferrable (no, using Vectors as keys in a dictionary isn't very normal! :) ) And if the need arise, one could always avoid calling operators on the structs that should stay unchanged.

@Inertia:
1) They don't catch DivideByZero exceptions, because there is no meaningful way to respond to such an error. The best we can do is inform the user that his program has a problem and hope for the best. If he is careless his application will crash, yes, but that's better than hiding the bug under the carpet and having him wonder why his 3d model suddenly turned invisible :)

2) Oops, yes that's needed! Generally, I prefer the cast-to-integer-and-compare approach than using a set epsilon (although both have their uses). I haven't tried the integer-comparison method in C#, but I think it should work inside an unsafe and unchecked block.

3) Good catch, that's a left-over function from a previous version (deletes).

4) You might be looking at an earlier revision, as we have removed all instance members in the latest version :) We decided that there's no good reason to have both instance and static methods to do the same thing - instance methods would hardly ever be utilized. IIRC, XNA has static methods too.

5) I am not sure I see what the advantage of this method is?

        public Vector3( Vector3 v )
        {
            this.elements = new float[3];
            this.SetTo( v );
        }

vs

        public Vector3(Vector3 v)
        {
            X = v.X;
            Y = v.Y;
            Z = v.Z;
        }

Seems the same to me, although the second method avoids an allocation. You only need unsafe code to apply indexing (unless you use a C# 'union' but that's another can of worms entirely), and there's no reason why to apply indexing in the first place. In any case, the unsafe code needn't be exposed to the user.

(*)By indexing I mean sth like:

        public float get(int index)
        {
            unsafe
            {
                fixed (float* ptr = &this.X)
                    return *(ptr + index);
            }
        }

but even that can be written to avoid unsafe code:

        public float get(int index)
       {
            switch (index)
            {
                case 0: return X; break;
                case 1: return Y; break;
                case 2: return Z; break;
                default: throw new ArgumentOutOfRangeException("index", index, "Should be 0, 1 or 2.");
            }
        }

Edit:
4b) The math stuff isn't fully baked yet, these will be fixed before the release.

objarni's picture

> Forgive my stupid question, but what is your problem storing all your immutable Vectors in one place, make sure you have no r
I take it you mean mutable here.

> references from outside left to that place and just leave them be? Some people might want to implement a particle system,
> collision detection, etc., where building 1000s of immutable Vectors per frame will inevitably flood memory and degrade performance.
Well as I said it is not a clear-cut question. If you want those 10% extra, you'll have to go to C, I am the first to admin!

In your multi-thousand-particle case, I would recommend using fixed-length arrays of floats or something like that to maximize cache coherence and minimize gc work. But is that case enough to "bloat" general mathematical entities of the OpenTK? Do you know how much optimization work has gone into the "new" of .NET? A lot I tell you. It is WAY faster than malloc/new of C/C++.

When you are using mutable types, you are also "buying into" a less mathematically sane program with a higher probability of bugs. This is not such a problem if you are a lone hacker and can keep your project in your head all the time. It gets a lot worse when you build projects for any longer time, say several months or with several people involved. Eg.: which vector-arrays are "untouchable"? which can I mess around with? when do I have to clone the vector I am using and when can I change it safely? If you have ever coded any larger project in C, you know what I mean and what a fuzz the mutable char*-"strings" are.

I know this question is "sensitive" -- I just wanted to state my experience with this thing. I have given it quite some thought through the years. Basically, I think the way of the future is immutable types as much as we can, and mutable types for "inner loops" as Michael Abrash once put it IIRC. Remember Knuths famous words of wisdom: premature optimization is the root of all evil.

Cya (and I will still like OpenTK if you end up with mutable Vectors! :)