Crocsx
Crocsx

Reputation: 7640

Get Angle of 2 vector3

I have 2 points :

v1 = {x:1,y:0,z:0}
v2 = {x:0,y:2,z:0}

I would like to calculate the angle beetwen those 2 points. I know how to make it in 2D, but not in 3D, I'm a bit lost ^^

basically like in Unity :

http://docs.unity3d.com/ScriptReference/Vector3.Angle.html

Thanks for the tips :3

Upvotes: 0

Views: 219

Answers (4)

comingstorm
comingstorm

Reputation: 26117

Using acos(a.b/(|a||b|) is unstable when the angle is small (or near 180 degrees). In either the 2D or 3D case, the solution is to use atan2(,) instead.

For 3D, compute both the dot product, and the length of the cross product:

let dot = v1.x * v2.x + v1.y * v2.y + v3.z * v3.z;
let crossX = v1.y * v2.z - v1.z * v2.y;
let crossY = v1.z * v2.x - v1.x * v2.z;
let crossZ = v1.x * v2.y - v1.y * v2.x;
let crosslen = sqrt(crossX*crossX + crossY*crossY + crossZ*crossZ);
return atan2(dot, crosslen);

Upvotes: 3

Francis Cugler
Francis Cugler

Reputation: 7925

The basic mathematical formula to calculate an angle between two vectors is as such:

Consider a & b to be vectors.

a dot b = abs(a)*abs(b)*cos(angle)

-> cos-1((a dot b) / ( abs(a)*abs(b))

Even though you are working in javascript the concept of doing the calculations are the same: What I can do for you is show you the functions from my Vector3 class that are written in c++

// -----------------------------------------------------------------------
// GetCosAngle()
// Returns The cos(Angle) Value Between This Vector And Vector V. This
// Is Less Expensive Than Using GetAngle
inline float Vector3::GetCosAngle( const Vector3 &v3, const bool bNormalized ) {
    // a . b = |a||b|cos(angle)
    // -> cos-1((a.b)/(|a||b|))

    // Make Sure We Do Not Divide By Zero
    float fMagA = Length();
    if ( fMagA <= Math::ZERO ) {
        // This (A) Is An Invalid Vector
        return 0;
    }

    float fValue = 0;

    if ( bNormalized ) {
        // v3 Is Already Normalized
        fValue = Dot(v3)/fMagA;
    }
    else {
        float fMagB = v3.Length();
        if ( fMagB <= Math::ZERO) {
            // B Is An Invalid Vector
            return 0;
        }

        fValue = Dot(v3)/(fMagA*fMagB);
    }

    // Correct Value Due To Rounding Problem
    Math::Constrain( -1.0f, 1.0f, fValue );

    return fValue;

} // GetCosAngle

// -----------------------------------------------------------------------
// GetAngle()
// Returns The Angle Between This Vector And Vector V in Radians.
//         This Is More Expensive Than Using GetCosAngle
inline float Vector3::GetAngle( const Vector3 &v3, const bool bNormalized, bool bRadians ) {
    // a . b = |a||b|cos(angle)
    // -> cos-1((a.b)/(|a||b|))

    if ( bRadians ) {
        return acos( this->GetCosAngle( v3 ) );
    }
    else {
        // Convert To Degrees
        return Math::Radian2Degree( acos( GetCosAngle( v3, bNormalized ) ) );
    }

} // GetAngle

Here are the additional functions from within the Vector3 class that are used to do these calculations:

// -----------------------------------------------------------------------
// Length()
// Return The Length Of This Vector
inline float Vector3::Length() const {

    return sqrtf( _fX * _fX +
                  _fY * _fY +
                  _fZ * _fZ );

} // Length

// -----------------------------------------------------------------------
// Dot()
// Return The Dot Product Between This Vector And Another One
inline float Vector3::Dot( const Vector3 v3 ) const {

    return ( _fX * v3._fX + 
             _fY * v3._fY +
             _fZ * v3._fZ );
} // Dot

Here are my Math functions and values that are used within these calculations:

const float Math::PI            = 4.0f  * atan(1.0f); // tan(pi/4) = 1
const float Math::PI_INVx180    = 180.0f / Math::PI;
const float Math::ZERO          = (float)1e-7;

// -----------------------------------------------------------------------
// Constrain()
// Prevent Value From Going Outside The Min, Max Range.
template<class T>
inline void Math::Constrain( T min, T max, T &value ) {

    if ( value < min ) {
        value = min;
        return;
    }

    if ( value > max ) {
        value = max;
    }

} // Constrain

/ -----------------------------------------------------------------------
// Radian2Degree()
// Convert Angle In Radians To Degrees
inline float Math::Radian2Degree( float fRadians ) {
    return fRadians * PI_INVx180;
} // Radian2Degree

Now as I stated these belong to a couple of my c++ classes that are within one of my math libraries. This is used as a demonstration on how one would calculate an angle between two vectors.

Upvotes: 0

Arif Burhan
Arif Burhan

Reputation: 505

Use vector dot product:

a . b = |a||b|cos $\theta$

In your notation:

v1.x * v2.x + v1.y * v2.y + v1.z * v2.z

While

|a| = sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z)

and

|b| = sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z)

Divide top result by bottom two results, then take its arccos, remembering angle will be in radians.

Upvotes: 0

Andrew Taylor
Andrew Taylor

Reputation: 692

Sounds like for this project you're going to want to find a vector maths reference you can read without going crazy.

For this case, the dot product v1.v2 = x1*x2 + y1*y2 + z1*z2 = |v1| * |v2| * cos theta, which means your angle is

function angle(v1, v2) {
    return Math.acos(dotProduct(v1, v2) / (length(v1) * length(v2)));
}

function dotProduct(v1, v2) {
    return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}

function length(v) {
    return Math.sqrt(dotProduct(v, v));
}

You can make that a lot faster, but if you want speed then probably use a library.

Upvotes: 2

Related Questions