Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Direction of shortest rotation between two vectors

my question is regarding working out the direction of the smallest angle between two vectors in 2D. I am making a game in C++ where one of the obstacles is a heat seeking missile launcher. I have it working by calculating the vector between the target and bullet, normalising the vector and then multiplying it by its speed. However, I am now coming back to this class to make it better. Instead of instantly locking onto the player I want it to only do so only when the bullets vector is within a certain angle (the angle between the bullets vector and the vector bulletloc->target). Otherwise I want it to slowly pan towards the target by a degrees thus giving the player enough space to avoid it. I have done all this (in a vb.net project so i could simplify the problem, work it out then re write in in C++). However the bullet always rotates clockwise towards the target even if the quickest route would be counter clockwise. So the problem is working out the direction to apply the rotation in so the smallest angle is covered. Here is my code so you can try and see what I am describing:

    Function Rotate(ByVal a As Double, ByVal tp As Point, ByVal cp As Point, ByVal cv As Point)
    'params a = angle, tp = target point, cp = current point, cv = current vector of bullet'
    Dim dir As RotDir 'direction to turn in'
    Dim tv As Point 'target vector cp->tp'
    Dim d As Point 'destination point (d) = cp + vector'
    Dim normal As Point
    Dim x1 As Double
    Dim y1 As Double
    Dim VeritcleResolution As Integer = 600

    tp.Y = VeritcleResolution - tp.Y 'modify y parts to exist in plane with origin (0,0) in bottom left'
    cp.Y = VeritcleResolution - cp.Y
    cv.Y = cv.Y * -1

    tv.X = tp.X - cp.X 'work out cp -> tp'
    tv.Y = tp.Y - cp.Y

    'calculate angle between vertor to target and vecrot currntly engaed on'
    Dim tempx As Double
    Dim tempy As Double

    tempx = cv.X * tv.X
    tempy = cv.Y * tv.Y

    Dim DotProduct As Double

    DotProduct = tempx + tempy 'dot product of cp-> d and cp -> tp'

    Dim magCV As Double 'magnitude of current vector'
    Dim magTV As Double 'magnitude of target vector'

    magCV = Math.Sqrt(Math.Pow(cv.X, 2) + Math.Pow(cv.Y, 2))
    magTV = Math.Sqrt(Math.Pow(tv.X, 2) + Math.Pow(tv.Y, 2))

    Dim VectorAngle As Double

    VectorAngle = Acos(DotProduct / (magCV * magTV))
    VectorAngle = VectorAngle * 180 / PI 'angle between cp->d and cp->tp'

    If VectorAngle < a Then 'if the angle is small enough translate directly towards target'
        cv = New Point(tp.X - cp.X, tp.Y - cp.Y)
        magCV = Math.Sqrt((cv.X ^ 2) + (cv.Y ^ 2))

        If magCV = 0 Then
            x1 = 0
            y1 = 0
        Else
            x1 = cv.X / magCV
            y1 = cv.Y / magCV
        End If

        normal = New Point(x1 * 35, y1 * 35)
        normal.Y = normal.Y * -1

        cv = normal
    ElseIf VectorAngle > a Then 'otherwise smootly translate towards the target'
        Dim x As Single
        d = New Point(cp.X + cv.X, cp.Y + cv.Y)


        a = (a * -1) * PI / 180 'THIS LINE CONTROL DIRECTION a = (a*-1) * PI / 180 would make the rotation counter clockwise'

        'rotate the point'
        d.X -= cp.X
        d.Y -= cp.Y

        d.X = (d.X * Cos(a)) - (d.Y * Sin(a))
        d.Y = (d.X * Sin(a)) + (d.Y * Cos(a))

        d.X += cp.X
        d.Y += cp.Y

        cv.X = d.X - cp.X
        cv.Y = d.Y - cp.Y

        cv.Y = cv.Y * -1
    End If

    Return cv

End Function

One idea I had was to work out the bearing of the two vectors and if the difference is greater than 180 degrees, rotate clockwise otherwise rotate counter clockwise, any ideas would be helpful. Thanks.

EDIT: I would like to add that this site is very helpful. I often use questions posed by others to solve my own problems and I want to take the chance to say thanks.

like image 689
David Kimbrey Avatar asked Jun 13 '12 20:06

David Kimbrey


People also ask

How do I find the rotation between 2 vectors?

First step, you want to find the angle between the two vectors using the dot product. Next, to find the axis of rotation, use the cross product. Knowing that the cross product will yield a vector perpendicular to both u and v , crossing them in either order will give an appropriate axis.

How do you find the smallest angle between two vectors?

Start with the vectors on top of each other. Drag one around until they are 180^{\circ} from each other, then keep going. Notice that the angle then gets smaller. The angle that is calculated is always the smallest angle between them, which is always between 0^{\circ} and 180^{\circ}.

How do you find the quaternion of two vectors?

Quaternion q; vector a = crossproduct(v1, v2); q. xyz = a; q.w = sqrt((v1. Length ^ 2) * (v2. Length ^ 2)) + dotproduct(v1, v2);

How do you convert quaternions to vectors?

First we convert the 3D vector into a quaternion, to do this we set the imaginary pars of the quaternion to the x,y and z values of the vector, the real part of the quaternion is set to zero. This quaternion is therefore not normalised like the quaternion representing the rotation.


1 Answers

As you've written in your code, the angle between two (normalized) vectors is the inverse cosine of their dot product.

To get a signed angle, you can use a third vector representing the normal of the plane that the other two vectors lie on -- in your 2D case, this would be a 3D vector pointing straight "up", say (0, 0, 1).

Then, take the cross-product of the first vector (the one you want the angle to be relative to) with the second vector (note cross-product is not commutative). The sign of the angle should be the same as the sign of the dot product between the resulting vector and the plane normal.

In code (C#, sorry) -- note all vectors are assumed to be normalized:

public static double AngleTo(this Vector3 source, Vector3 dest)
{
    if (source == dest) {
        return 0;
    }
    double dot; Vector3.Dot(ref source, ref dest, out dot);
    return Math.Acos(dot);
}

public static double SignedAngleTo(this Vector3 source, Vector3 dest, Vector3 planeNormal)
{
    var angle = source.AngleTo(dest);
    Vector3 cross; Vector3.Cross(ref source, ref dest, out cross);
    double dot; Vector3.Dot(ref cross, ref planeNormal, out dot);
    return dot < 0 ? -angle : angle;
}

This works by taking advantage of the fact that the cross product between two vectors yields a third vector which is perpendicular (normal) to the plane defined by the first two (so it's inherently a 3D operation). a x b = -(b x a), so the vector will always be perpendicular to the plane, but on a different side depending on the (signed) angle between a and b (there's something called the right-hand rule).

So the cross product gives us a signed vector perpendicular to the plane which changes direction when the angle between the vectors passes 180°. If we know in advance a vector perpendicular to the plane which is pointing straight up, then we can tell whether the cross product is in the same direction as that plane normal or not by checking the sign of their dot product.

like image 129
Cameron Avatar answered Oct 06 '22 13:10

Cameron