Sign in to follow this  

2D & 3D vector operators

This topic is 3860 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, At work I had to implement a 3D vector for the Nth time, and some questions about which operators to implement arised. basically, we have a vector like this: struct vector3D { float x,y,z; }; and we have implemented the obvious operators: = + - * / *= += == , etc Then, there are less obvious operations that are more subject to discussion: < > <= >= ^ | & , etc My question is: is there a more mathematically correct, or more standard, or coommon way to implement them? or they're just subject to the free will of every developer? For example, I've seen some vectors that have the Cross Product and Dot Product implemented as named methods, while others implement them with the | and ^ operators... is this common/standard? I've also seen the comparison operators implemented like this: bool operator < (right) { return x < right.x && y < right.y && z < right.z } And to me it looks correct and on par with the == operator, but to me, other implementations would be more useful... So, what you people do about these ones? Btw, here They're trying to make a standard math library for games: http://www.convexhull.com/wiki/?n=GameMath.HomePage

Share this post


Link to post
Share on other sites
Quote:
Original post by vicviper
I've also seen the comparison operators implemented like this:

bool operator < (right) { return x < right.x && y < right.y && z < right.z }


It's incorrect, because it's not a complete order (and operator < is universally accepted to be complete).

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by vicviper
I've also seen the comparison operators implemented like this:

bool operator < (right) { return x < right.x && y < right.y && z < right.z }


It's incorrect, because it's not a complete order (and operator < is universally accepted to be complete).


Ok, so which would be the correct implementation of operator < ? or, it is not possible on a vector?

Share this post


Link to post
Share on other sites
There is no one which makes sense in any geometric way. However, you can still provide a complete order relationship, which is kind of arbitrary: the lexicographic order.

Share this post


Link to post
Share on other sites
Quote:
Original post by vicviper
Then, there are less obvious operations that are more subject to discussion:

< > <= >= ^ | & , etc

My question is: is there a more mathematically correct, or more standard, or coommon way to implement them? or they're just subject to the free will of every developer?

You are not required to implement every operator. Just implement those that make sense for your type. For a 3D-vector operator> has no common meaning, so don't implement it. (You could make it a shortcut for comparing lengths of vectors, but then the code becomes unreadable, so don't do it.)
Quote:

For example, I've seen some vectors that have the Cross Product and Dot Product implemented as named methods, while others implement them with the | and ^ operators... is this common/standard?

Which product should be used for operator* ? Ask yourself, the inner (dot) product or the outer (cross) product ? Calling a function by name makes it clear.

Share this post


Link to post
Share on other sites
Quote:

Quote:

For example, I've seen some vectors that have the Cross Product and Dot Product implemented as named methods, while others implement them with the | and ^ operators... is this common/standard?

Which product should be used for operator* ? Ask yourself, the inner (dot) product or the outer (cross) product ? Calling a function by name makes it clear.

nmi, the * operator needs to be implement for multiplications with scalars ;). As fot the >= <= > < operators, i personally whould make 4 functions so that it would be clearer what i am intending to do.
for example if you want to compare elementwise, you can make a function with the name
EWCmpGE(Vec3 &v1, Vec3 &v2), whose name is a short for "elementwise comparison greater or equal"
you can use GT for "greater", LT for less, and LE fot less or equal, like in fortran :). it may not be so "beautiful" as the operators, but the names are still short, descriptive, and you are not mathematically incorrect.

Share this post


Link to post
Share on other sites
You can still overload the * operator for dot or cross product, but it can be misleading. Some libraries used it for the cross product others for the dot product. I generally use * for the dot product and the & or the ^ operator for the cross product.

You also have to be careful with operator precedence.

Share this post


Link to post
Share on other sites
I typically overload operator* to take a vector or a scalar as an argument; it's usually pretty clear which one is being used. I recommend implementing dot() and cross() as well though, since some people expect those functions.

operator/ I use for scalars, or to mean a cross product in reversed order * -1. The latter comes in handy for long expressions.

As far as comparisons, I use them to compare the magnitude of a vector. It usually comes into play when sorting a std::vector of Vector3D to find the nearest objects, etc. Also I recommend having a static Vector3D::epsilon for your comparisons, because as I'm sure you know A*3.0f-A*2.0f rarely equals A.

operator= I use to either copy a vector or reassign its magnitude.

I don't touch the bitwise operators though; there's no situation where a clear meaning has come up.

Share this post


Link to post
Share on other sites

From what I can read from everybody's responses, it is not wise to implement the < <= >= > operators since everybody would expect something different from them, and they can be misleading.

Now I noticed something else:

Some of you implement the * operator as a dot product, that is:

float operator * (vector) { x*vector.x + y*vector.y + z*vector.z }

While I think others just implement a component per component multiply:

vector operator * (vector) { x*vector.x , y*vector.y , z*vector.z }

Performing it as a dot product looks like more mathematically correct, but
I can see times where what you need is just multiply component per component,
and when you want a dotproduct, do it specifically with the .dot() method


Share this post


Link to post
Share on other sites
Quote:
Original post by nmi
You are not required to implement every operator. Just implement those that make sense for your type. For a 3D-vector operator> has no common meaning, so don't implement it. (You could make it a shortcut for comparing lengths of vectors, but then the code becomes unreadable, so don't do it.)

Just because there is no common or logical meaning doesn't mean there isn't a practical reason for implementing it. For instance, implementing the less-than operator allows you to store the vector in a number of different sorted containers and otherwise use sorting methods on lists of vectors. Why would you want to sort vectors if there is no logical ordering? One purpose would be to remove duplicate vertexes. Using the lexicographic order, you can easily sort vertexes and remove adjacent duplicates in O(n log n).

Vector-vector multiplication is an opt-debated operation, since some people think it isn't well-defined enough to implement. I personally implement it as a component-wise multiplication, however that's simply because I've done a lot of shader programming in the past few months and it makes the most sense to me. I don't use operators for the dot and cross product because it feels way too arbitrary to me, but again that's just personal preference.

Share this post


Link to post
Share on other sites
As long as you are clear what they do, using operators is ok. true, * is also used sometimes for vector scaling, but scaling is usually achieved through the use of matrices. Best is to use names, like scale(), dot() and cross(). You also have to decide if the operators return by value (accessor) or by reference (modifier). I'd stick with accessors, and return by value.

Vector scale(const Vector& other) const { return Vector(x*other.x, y*other.y, z*other.z); }

the operator == or != have some significance, but you might as well use a function.

bool isEqual(const Vector& other, float threshold=1.0E-6) const { ... }

Share this post


Link to post
Share on other sites
Quote:
Original post by oliii
You also have to decide if the operators return by value (accessor) or by reference (modifier). I'd stick with accessors, and return by value.

Vector scale(const Vector& other) const { return Vector(x*other.x, y*other.y, z*other.z); }



We have that discussion at work too, we decided to go this way:

vector.Scale(vector); modifies the operated vector and returns a reference
vector.Scaled(vector) const; does not modify and returns a new value

This discussion arised with the normalize method... we difference between normalize and normalized



Share this post


Link to post
Share on other sites
At work we use functions like Inverse() and Inverse_In_Place(), Normalize() and Normalize_In_Place(), etc., so there's no ambiguity at all between accessors and modifiers. My personal library just uses accessors everywhere, but that's just me.

Share this post


Link to post
Share on other sites
When overloading operators what you really want to pay attention to is operator precedence. For example, the "^" operator has a lower precedence than the "*" operator, if you overload them for the Cross Product and the Dot Product respectively, does this preserve the natural execution order one would expect?

Before you even get to that stage however, you need to make sure that the overload is natural and unambiguous, meaning that the operation performed is the first and only interpretation of that operator that comes to mind, or at least is the predominant interpretation by a wide margin.


I used to be in the camp that supported the more terse "overload the operators" camp, but have since realized the problems therein, so I changed much of my math classes.

Share this post


Link to post
Share on other sites

This topic is 3860 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this