Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Vector Math Library  (Read 27894 times)

0 Members and 1 Guest are viewing this topic.

RixCoder

  • Newbie
  • *
  • Posts: 40
    • View Profile
Vector Math Library
« on: September 19, 2009, 07:03:12 am »
Since I've seen people request vector math functionality built into sfml a few times I created a simple header file to include into the projects that should do just that.

Its just a namespace packed with templated functions for handling just about all of the operations you should need, overloaded for both 2D and 3D sfml vectors.

Some functions included:
Length
Normalize
Rotate ( 2D vector only )
Angle Between 2 vectors ( functions for both radians and degrees )
Dot Product
Cross Product

Some other random stuff:
functions for converting degrees to radians and vice-versa

to use simply include the header file in your project
example of use:
Code: [Select]
#include "RixMath.h"

sf::vector2f v1(10, 1);
sf::vector2f v2(1, 20);
 float result = sfm::Dot(v1, v2);


you can catch a link to the header file HERE

Enjoy =)

If there are any problems let me know and please give credit if used in your project!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Vector Math Library
« Reply #1 on: September 19, 2009, 11:04:46 am »
Hi

Why don't you post it on the wiki? :)
Laurent Gomila - SFML developer

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Vector Math Library
« Reply #2 on: September 19, 2009, 04:41:25 pm »
Hi, I have got some improvement suggestions:
  • Identifiers that begin with an underscore and an upper letter are reserved for compiler and implementation (as well as identifiers containing "__"). That concerns your header guard _H_SFMATH_.
  • There is no reason the include the full SFML graphics package, this just increases compile time. Only include the headers you need.
  • Why do you use macros for PI and the deg/rad conversion? Functions and constants are more typesafe and prevent some of the preprocessor problems like multiple evaluation.
  • The notion of magnitude is used incorrectly. The magnitude of an euclidean vector is defined by its length (square root of the dot product with itsself), not its squared length.
  • Your function Rotate() is unnecessarily slow, as it computes both sine and cosine twice. Besides, it can be confusing it takes a radian value, since SFML always works with degrees.
  • What is _isnan? This is not part of the C++ standard library. Use std::numeric_limits instead.
  • The function AngleBetweenR() doesn't return a value, even though its return type is declared float.
  • Why do you in AngleBetweenD() manually convert between radians and degrees, although you already have the macros?
  • Convert() is a too general name, it's not clear that a conversion from 2D to 3D vector is performed.
  • Consider const-correctness. Functions which don't change their parameters shouldn't take references to non-const. For example Dot().
  • You are inconsistent with return values. Some functions return the result, others store it in their third parameter (like Cross()). I would unify that.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Tank

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1486
    • View Profile
    • Blog
    • Email
Vector Math Library
« Reply #3 on: September 19, 2009, 07:57:35 pm »
Good points Nexus.

Also the macro for getting a random float number is not needed IMHO, since SFML has float sf::Randomizer::Random( float, float ). But besides of that, thank you for your effort. :)

RixCoder

  • Newbie
  • *
  • Posts: 40
    • View Profile
Vector Math Library
« Reply #4 on: September 19, 2009, 08:10:10 pm »
yeah i needed to get a dot product in one of my recent projects using sfml so i figured id go all out on creating a vector math header for people. I saw a lot opinions in one post about the subject, nexus you were there, and figured I would just throw one up for people to use at their will. I kind of hashed it together from a few math libraries I used over my time in school.

I'm gonna get the changes in you mentioned Nexus, and yeah ill just remove the random float function since its redundant Tank.

This should make both parties happy about sf::vector, if you want the functionality, download the header =), if not, IGNORE ME! ( venture brothers anyone? ).  

Also what is the specific include for sf::vector2/3?

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Vector Math Library
« Reply #5 on: September 19, 2009, 08:44:41 pm »
I'm glad I could help you. :)

At the moment, I am developping my own vector algebra library, but it has to prove usability. Also, I've chosen a rather complicated way to be generic for more types than float/double/long double. I don't even know if I will ever need this. There are still a lot of things to do...

Quote from: "RixCoder"
Also what is the specific include for sf::vector2/3?
The paths are:
Code: [Select]
#include <SFML/System/Vector2.hpp>
#include <SFML/System/Vector3.hpp>
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

RixCoder

  • Newbie
  • *
  • Posts: 40
    • View Profile
Vector Math Library
« Reply #6 on: September 19, 2009, 09:46:16 pm »
ok all changes should be in except for :

You are inconsistent with return values. Some functions return the result, others store it in their third parameter (like Cross()). I would unify that.

I have cross take a reference to a result to skip out on the whole copy process of passing by value.

Other than that I think everything else was taken care of. Original post updated to reflect changes.

Anything else let me know

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Vector Math Library
« Reply #7 on: September 19, 2009, 10:41:16 pm »
Quote from: "RixCoder"
I have cross take a reference to a result to skip out on the whole copy process of passing by value.
I hope you're joking.

You know there is a reference that must be copied like this? Besides, there is the overhead for dereferencing it, too. Passing by reference is not free, although that's a popular belief. Your approach might pay out in case of very big objects, but not for 3 floats. If you have a good compiler and you consider some things, it's very probable that RVO will be applied and the copy will disappear completely. Additionally, the vector instance must be created before (outside the function) - you don't win anything. It's possible that this way makes performance even worse, for example because of the useless default-constructor call of sf::Vector3.

Anyway, considerations of this kind should be no reason to make the interface ugly. For example, you can't use the cross product in a temporary context, you always have to create an additional vector instance to hold the result. And believe me, there are much more crucial operations than copying some floats. Rather optimize obvious things like calling sin() twice. What you are doing, is premature optimization. You should at least take a profiler instead of basing your code on assumptions.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

RixCoder

  • Newbie
  • *
  • Posts: 40
    • View Profile
Vector Math Library
« Reply #8 on: September 19, 2009, 11:28:29 pm »
Nexus, I appreciate the feedback and the help tidying up the header but relax on the hostility in your criticism. Maybe im just reading this the wrong way but im just trying to help people out here. I'm not pretending to be some kind of guru here I only started programming period about 2 years ago. I'm just trying to contribute something to SFML if I can since its being given to us for free.

Also the reason why I believed passing by reference was faster was due to my assumption that a reference would be passed as a pointer is ( 4 byte object ). I'm apparently wrong and I appreciate it being pointed out.

The changes are all in now, including a fix to the 2d cross product that was offbase.

phear-

  • Jr. Member
  • **
  • Posts: 64
    • MSN Messenger - LOApokalypse@hotmail.com
    • View Profile
    • http://gtproductions.org
Vector Math Library
« Reply #9 on: September 20, 2009, 12:04:55 am »
Quote from: "RixCoder"
Nexus, I appreciate the feedback and the help tidying up the header but relax on the hostility in your criticism. Maybe im just reading this the wrong way but im just trying to help people out here. I'm not pretending to be some kind of guru here I only started programming period about 2 years ago. I'm just trying to contribute something to SFML if I can since its being given to us for free.

Also the reason why I believed passing by reference was faster was due to my assumption that a reference would be passed as a pointer is ( 4 byte object ). I'm apparently wrong and I appreciate it being pointed out.

The changes are all in now, including a fix to the 2d cross product that was offbase.


I appreciate you making this for others so that we don't have to do it ourselves. People will always point out your flaws, but you just need to subtract the "criticism" and get the point from their advice. The internet is a weird medium for discussion because you often think someone is being harsh or demeaning when they really aren't. Thanks again :)
Eugene Alfonso
GTP | Twitter

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Vector Math Library
« Reply #10 on: September 20, 2009, 12:32:24 am »
Quote from: "RixCoder"
Nexus, I appreciate the feedback and the help tidying up the header but relax on the hostility in your criticism.
Sorry, I really don't mean it like this. It's just the fact that there are so many misbeliefs of common programming techniques, I really see them a lot. Sometimes, it seems like people once heard something, but they have never spent any further thought on it (not at you, in general). Especially regarding performance issues, many programmers make their lives much harder than necessary, but as a result, they achieve nothing. Quite recently, there was the thing with pointers in STL containers ("use malloc because its faster").

I just want to make clear that those things are really important, they're not minor matters at all. They do affect you in programming. It's important to make stable and usable programs first, and care about micro-optimizations later. Of course, this doesn't imply you shouldn't think about performance before. But changes, whose success can be doubted and who won't really affect the program, should be given lower priority. However, you are right, I'll try to say this friendlier next time. I hope, my statements helped you nevertheless.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Vector Math Library
« Reply #11 on: September 20, 2009, 02:14:02 am »
Quote from: "RixCoder"
Also the reason why I believed passing by reference was faster was due to my assumption that a reference would be passed as a pointer is ( 4 byte object ). I'm apparently wrong and I appreciate it being pointed out.
Your assumption is correct (in most usual cases).

In spite of that, you have to consider other operations, too. I will try to explain them (not taking function inlining into account).
Code: [Select]
sf::Vector3f Left(3, 4, -7);
sf::Vector3f Right(-2, 1, 5);

sf::Vector3f Result;      // The standard constructor initializes 3 floats with 0
Dot(Left, Right, Result);

The function call may expand to that:
Code: [Select]
void Dot(const sf::Vector3f& v1, const sf::Vector3f& v2, sf::Vector3f& vResult)
// note: 3 references copied, probably 12 bytes.
{
    vResult.x = v1.y * v2.z - v1.z * v2.y;
    vResult.y = v1.z * v2.x - v1.x * v2.z;
    vResult.z = v1.x * v2.y - v1.y * v2.x;
    // Without optimizations, there are 15 dereferencings! (Every time you
    // access a vector through the point operator.) Perhaps, they're
    // optimized away (for example by inlining), but keep this in mind
    // nonetheless. Besides, three assignments to x, y and z are executed.
}

Now let's take a look at the other version.
Code: [Select]
sf::Vector3f Dot(const sf::Vector3f& v1, const sf::Vector3f& v2)
// 2 reference parameters copied, but we need the address of the target
// (explained later)
{
    // 12 dereferencings remain. By passing Left and Right by value, we
    // could trade them off against 6 float copies. Not very wise, if bigger
    // types than float are treated.

    // But regarding the essential stuff:
    return sf::Vector3f(
        v1.y * v2.z - v1.z * v2.y,
        v1.z * v2.x - v1.x * v2.z,
        v1.x * v2.y - v1.y * v2.x); // creates a temporary object
} // copies the object for the return value

sf::Vector3f Destination = Dot(Left, Right); // = copies it again.

Looks like many copies. But we've forgotten one thing - RVO. Return value optimization is - at modern compilers - often applied in order to elide unnecessary copies. But how does the compiler know that the copy is unnecessary? It may not elide any copies because sometimes, the original object shall not be changed.

In this case, the first copy (return value) refers to a temporary object, so nobody cares if we access it directly. At initialization time (copy-constructor called by = in declaration outside the function) the same thing applies, no one requires the origin any more. The 2 copies can be omitted. Then there is the actual construction, which finally initializes our vector called Result. The construction contains 3 float initializations, the object can be constructed in-place outside the function (for this, the function requires the object's address). Like this, there isn't even a temporary.

So the first version performs the following operations which are important for the initialization of Result:
  • 3 zero-initialized floats (default constructor outside function)
  • 3 additional dereferencings inside the function (write access to vResult)
  • 3 assignments to x, y, z inside the function
Given the compiler knows RVO, which is likely, the second version is able to initialize the object directly:
  • 3 value-initialized floats (parametrized constructor)
Ok, this is float, how about bigger types? The operations remain the same. However, the second version may pay even more out. The reason is, default construction plus assignment is almost always less efficient than direct value-construction.

By the way: C++0x, the next C++ standard, expands the explained concept by introducing rvalue references which support actual move semantics. Then, temporaries aren't only a hint to apply RVO anymore. Working with current C++, we can partially achieve move semantics by std::auto_ptr. Unfortunately, this approach is not always optimal as a result of the required heap allocation.

Yet, one shouln't rely on RVO in every case. If huge objects (e.g. a std::vector<std::string> with many elements) are returned by value, it's not bad to be careful. By contrast, a sf::Vector3<T> instance is small, and even if RVO can't be applied, the consequences are not too tragic.

At least, the possibility to write the following is worth risking it:
Code: [Select]
AnyFunction(CrossProduct(Left, Right));
Because that is not very pleasant (not least because the pass of the named object Result to AnyFunction might require a further copy, depending on the parameter type).
Code: [Select]
sf::Vector3f Result;
CrossProduct(Left, Right, Result);
AnyFunction(Result);
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

RixCoder

  • Newbie
  • *
  • Posts: 40
    • View Profile
Vector Math Library
« Reply #12 on: September 20, 2009, 03:35:33 am »
Yeah I can definitely see your point, if not even from the slight optimization route, the better simplicity of use is nice enough.

I think I covered everything you put up there, any other changes let me know but that should cover just about everything. Sorry for lashin out, its just that I've been reading a few posts on the sfml boards where people are acting snobby/rude/general a-hole'ish when its totally un-needed in a forum where people are supposed to be helping eachother out.

But you did help me out and I learned from it so all good in my book ;) Thanks again

12pool3

  • Newbie
  • *
  • Posts: 2
    • View Profile
Vector Math Library
« Reply #13 on: April 11, 2010, 03:45:22 pm »
Hey, very nice library.
I understand that a 'cross product', in it's sense, is only defined in 3-dimensional space; but there is that 'hack' for getting the perpendicular vector of a 2-D vector using the cross product.
You extend the 2-D vector to 3-D and add a z-component of zero and then find the cross product of that and a unit vector parallel to the z-axis (0,0,1); if you return the result of this as a 2-D vector (the z component will be zero anyway), then you get a vector that is perpendicular to the initial 2-D vector.
I was wondering if you could implement this somehow in the 2D vector class, maybe as a new method 'perpendicular' or whatever. (your existing 'cross' method for the Vector 2D class only returns the magnitude/z-value of the cross product between the two 2d vectors)

Anyway, I really hope you continue with this, it's very helpful.

bullno1

  • Jr. Member
  • **
  • Posts: 66
    • View Profile
Vector Math Library
« Reply #14 on: April 11, 2010, 04:05:41 pm »
Any chance for SSE support?  :wink: