Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate Angle from Two Points and a Direction Vector

Tags:

math

vector

I have two vectors in a game. One vector is the player, one vector is an object. I also have a vector that specifies the direction the player if facing. The direction vector has no z part. It is a point that has a magnitude of 1 placed somewhere around the origin.

I want to calculate the angle between the direction the soldier is currently facing and the object, so I can correctly pan some audio (stereo only).

The diagram below describes my problem. I want to calculate the angle between the two dashed lines. One dashed line connects the player and the object, and the other is a line representing the direction the player is facing from the point the player is at.

Angle Problem

At the moment, I am doing this (assume player, object and direction are all vectors with 3 points, x, y and z):

Vector3d v1 = direction;
Vector3d v2 = object - player;
v1.normalise();
v2.normalise();
float angle = acos(dotProduct(v1, v2));

But it seems to give me incorrect results. Any advice?

Test of code:

Vector3d soldier = Vector3d(1.f, 1.f, 0.f);
Vector3d object = Vector3d(1.f, -1.f, 0.f);
Vector3d dir = Vector3d(1.f, 0.f, 0.f);

Vector3d v1 = dir;
Vector3d v2 = object - soldier;

long steps = 360;
for (long step = 0; step < steps; step++) {
    float rad = (float)step * (M_PI / 180.f);
    v1.x = cosf(rad);
    v1.y = sinf(rad);
    v1.normalise();

    float dx = dotProduct(v2, v1);
    float dy = dotProduct(v2, soldier);
    float vangle = atan2(dx, dy);
}
like image 471
oggmonster Avatar asked Dec 28 '22 01:12

oggmonster


2 Answers

You shoud always use atan2 when computing angular deltas, and then normalize. The reason is that for example acos is a function with domain -1...1; even normalizing if the input absolute value (because of approximations) gets bigger than 1 the function will fail even if it's clear that in such a case you would have liked an angle of 0 or PI instead. Also acos cannot measure the full range -PI..PI and you'd need to use explicitly sign tests to find the correct quadrant.

Instead atan2 only singularity is at (0, 0) (where of course it doesn't make sense to compute an angle) and its codomain is the full circle -PI...PI.

Here is an example in C++

// Absolute angle 1
double a1 = atan2(object.y - player.y, object.x - player.x);

// Absolute angle 2
double a2 = atan2(direction.y, direction.x);

// Relative angle
double rel_angle = a1 - a2;

// Normalize to -PI .. +PI
rel_angle -= floor((rel_angle + PI)/(2*PI)) * (2*PI) - PI;

In the case of a general 3d orientation you need two orthogonal directions, e.g. the vector of where the nose is pointing to and the vector to where your right ear is. In that case the formulas are just slightly more complex, but simpler if you have the dot product handy:

// I'm assuming that '*' is defined as the dot product
// between two vectors: x1*x2 + y1*y2 + z1*z2
double dx = (object - player) * nose_direction;
double dy = (object - player) * right_ear_direction;
double angle = atan2(dx, dy); // Already in -PI ... PI range

enter image description here

like image 149
6502 Avatar answered Feb 16 '23 01:02

6502


In 3D space, you also need to compute the axis:

Vector3d axis = normalise(crossProduct(normalise(v1), normalise(v2)));
like image 31
arul Avatar answered Feb 16 '23 01:02

arul