Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

When calculating the normal of a vector, which is considered canon:

Returning a copy:

Vector2D Vector2D::Normalize() const {
    double a1 = GetX();
    double a2 = GetY();
    double a3 = GetZ();
    double length = GetLength();
    assert(Math::IsEqual(length, 0.0) == false);
    if(Math::IsEqual(length, 0.0)) {
        throw Exception("Can not normalize a Null Vector.");
    }
    return Vector2D(a1 / length, a2 / length, a3 / length);
}

Or altering the object itself?:

void Vector2D::Normalize() {
    double a1 = GetX();
    double a2 = GetY();
    double a3 = GetZ();
    double length = GetLength();
    assert(Math::IsEqual(length, 0.0) == false);
    if(Math::IsEqual(length, 0.0)) {
        throw Exception("Can not normalize a Null Vector.");
    }
    SetTerminal(a1 / length, a2 / length, a3 / length);
}

P.S. IsEqual verifies the following: (std::fabs(a - b) <= 0.0001)

share|improve this question
    
Software is so flexible that there's rarely a canonical way to do anything. Or to steal a meme: if you think in absolutes you're gonna to have a bad time. The "best way" to think about questions and what to do is "What's a great c++ method design to calculate normals for a 3D engine?" Which is a totally different question than "What's a great way to calculate normals for a parallel physics simulation?" etc... –  Patrick Hughes Jul 28 '12 at 15:40
add comment

5 Answers

up vote 7 down vote accepted

You should have both versions, but not in the same place.

A function that takes a simple object like a vector generally should not modify it. Why? Because then you couldn't do this:

normalize(vec3(0.3, 0.0, 0.0));

If normalize takes a vec3&, then you can't pass it as a temporary in C++. You must use a named variable, for no real reason. It's also a lot harder to use it as part of an expression:

normalize(someVec + someVec);

At the same time, if you have a vector, it's not a bad idea to want to be able to normalize it in-place. For that, you use a member function, which is your indication that you're modifying the object, not simply returning a new one.

So your member normalize should be non-const, but your free function version should take its argument by const&.

share|improve this answer
add comment

There's no reason not to have both.

That said, I would lean towards returning a copy. It makes the code using your math library much more natural and readable. Some reasons for modifying in place were performance concerns, but honestly modern compilers do not have any trouble with optimizing away returned copies of temporaries. Turn on SSE2, fast math, and LTCG on MSVC 2010+ (or the equivalents in GCC 4.5+) and you'll be unlikely to optimize any gnarly "fast" API better than the compiler can optimize an easy to use "naive" API.

(As always, benchmark and check any optimization assumptions.)

share|improve this answer
    
What would be a case for (or against) using one over the other? Particularly the in-place solution since I've already implemented the copy-returning version. That is, if I did implement both, would the in-place version ever get used? See ChrisE's answer to this question for a potential reason for the in-place solution –  Casey Jul 28 '12 at 3:34
    
I disgree with ChrisE simply because you should not have a free function version. While I'm quite aware "good C++ practice" says otherwise, the reality is that IDEs and tools onl operate at the member layer; there's no easy way to ask the IDE "what are all the free functions that operate on vec4". That said, the modifying version might be handy, but personally I don't use them. Using the modifying version and assigning the result back to the input variable is still much much clearer, and again the compiler should optimiz it the same. –  Sean Middleditch Jul 28 '12 at 19:46
add comment

I would always have both, because:

  • I don't want to have to use clone/copy methods every time I need a modified copy.
  • I don't want to have to type a whole assignment statement every time I want to modify the original.

I suggest implementing both, and using naming to distinguish them: eg. normalize() modifies the object in place, and normalized() returns a normalized version of the object.

share|improve this answer
add comment

I really like the approach where the vector itself gets normalized. Why? Because in a lot of cases you won't need the original vector after normalization. If you do, a clone or assign method should be simple to implement and help in these cases.

So if I need to keep my original vector and a normalized copy:

Vector2D vectorCopy;
vectorCopy.assign(myVector); 
// or vectorCopy = myVector if you're implementing this in 
// a language that allows operator overloading
myVector.Normalize();

What I encountered quite a bit when working with vectors is that I want to normalize a vector, but know about the length it had beforehand. With a bad Vector class implementation, this would look like this:

float length = myVector.Length();
myVector.Normalize();

This will use a square-root twice which is totally unnecessary. Especially when you use operations like this frequently (eg. several times in an update loop), you're going to start appreciating implementations where the Normalize method also returns the length of the vector before normalization:

float length = myVector.Normalize(); // <3 <3
share|improve this answer
add comment

Another consideration I would offer in favor of returning a copy would be to make your vector class immutable. This could simplify any future development involving asynchronous operations (a likely scenario where vectors are concerned). http://stackoverflow.com/questions/7970438/immutable-data-and-locking is a simple discussion. There are plenty of other deeper discussions on the subject.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.