Reputation: 7640
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
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
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
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
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