Relative angle between 2 vectors

Blitz3D Forums/Blitz3D Programming/Relative angle between 2 vectors

I needed to find the angle between two 2D vectors, and found the following on the net:
For 2D Vectors
This is relatively simple because there is only one degree of freedom for 2D rotations. If v1 and v2 are normalised so that |v1|=|v2|=1, then,

angle = acos(v1•v2)

The only problem is, this won't give all possible values between 0 and 360 degrees, or -180 and +180 degrees. In other words, it won't tell us if v1 is ahead or behind v2, to go from v1 to v2 is the opposite direction from v2 to v1.

In most math libraries acos will usually return a value between 0 and PI (in radians) which is 0 and 180 degrees.

If we want a + or - value to indicate which vector is ahead, then we probably need to use the atan2 function (as explained on this page). using:

angle of 2 relative to 1= atan2(v2.y,v2.x) - atan2(v1.y,v1.x)


I can get the first method to work fine, but as the text suggests, this isn't any use to me - I need the relative angle of v1 to v2 (i.e. a result in the range -180 to +180).

I've tried the second method, using ATan2, but can't seem to get it to work. Part of the reason is probably because the atan2 function in the text works with Radians, whereas blitz ATan2 works with degrees.

I tried converting to radians, doing the calculation, then converting back to degrees, but still no joy. I may have been doing the conversion incorrectly, but frankly, I think this is a bit of a kludge, anyway.

So, is there a neat way to do this using blitz's ATan2 function?

Cheers.

(P.S. This has probably been asked a load of times, but searching and looking through the code archives was fruitless.)

Isn't is angle=atan2(y1-y2,x1-x2)?

Doesn't seem to work.
	Graphics 640,480,0,2
	SetBuffer BackBuffer()

	Type vectorT
		Field dx#
		Field dy#
	End Type

	; Static vector.
	v1.vectorT = New vectorT
	v1\dx = 100
	v1\dy = 50
	
	; Mouse vector.
	v2.vectorT = New vectorT
	
	base_x = 320
	base_y = 240
	
	While Not KeyHit(1)
		v2\dx = MouseX() - base_x
		v2\dy = MouseY() - base_y
		
		Cls
		
		Color 255,0,0
		Line base_x, base_y, base_x + v1\dx, base_y + v1\dy
		Line base_x, base_y, base_x + v2\dx, base_y + v2\dy
		
		Color 0,255,0
		Oval base_x - 4, base_y - 4, 9, 9, 1
		
		ang# = ATan2(v1\dy - v2\dy, v1\dx - v2\dx) ; ????
		
		Color 255,255,255
		Text 10,10,"Angle: " + ang#
		
		VWait : Flip False
	Wend

	End


And this ?
Graphics 800, 600, 0, 2
SetBuffer BackBuffer()

ang1# = Rand(360)
ang2# = Rand(360)
ang2r# = ang2#

Repeat
	
	;draw angles
	Cls
	Origin 400, 300

	;red=angle1	
	Color 255, 0, 0
	x1# = Cos(ang1) * 120
	y1# = Sin(ang1) * 120
	Line 0, 0, x1, y1
	Text x1 * 1.2, y1 * 1.2, ang1, 1, 1
	
	;aqua=angle2
	Color 0, 255, 255
	x2# = Cos(ang2) * 120
	y2# = Sin(ang2) * 120
	Line 0, 0, x2, y2
	Text x2 * 1.2, y2 * 1.2, ang2, 1, 1
	
	Origin 0, 0


	;calculate angles from coordinates
	Color 255, 255, 255
	angle1# = (ATan2(y1, x1) + 360) Mod 360
	angle2# = (ATan2(y2, x2) + 360) Mod 360
	
	;show calculated angles
	Text 0, 60, "angle1= " + angle1
	Text 0, 80, "angle2= " + angle2

	;calculate differences
	angdif1# = (angle2 - angle1)
	angdif2# = (angle2 + 360 - angle1)
	angdif3# = (angle2 - 360 - angle1)

	;determine smallest difference	
	If (Abs(angdif1) < Abs(angdif2)) And (Abs(angdif1) < Abs(angdif3)) Then angdif# = angdif1#
	If (Abs(angdif2) < Abs(angdif1)) And (Abs(angdif2) < Abs(angdif3)) Then angdif# = angdif2#
	If (Abs(angdif3) < Abs(angdif2)) And (Abs(angdif3) < Abs(angdif1)) Then angdif# = angdif3#
	
	;show difference
	Text 0, 100, "difference= " + angdif#
	
	;show usage
	Text 0, 0, "usage:"
	Text 0, 20, "use cursor keys to move angle1, use space to apply difference"

	;cursor keys = move angle 1
	If KeyDown(203) Then ang1 = ang1 - 1
	If KeyDown(205) Then ang1 = ang1 + 1
	
	;automove with space
	If KeyHit(57) Then
		ang2r# = angle2 - angdif
	End If	
	If ang2r <> ang2 Then ang2 = ang2 + Sgn(ang2r - ang2)	
	


	Flip

Until KeyHit(1)
WaitKey()

End


Thanks b32, but that looks pretty complicated - lots of If tests, etc. Isn't there a way to do this using pure maths on vectors? The original text I posted seems to say there is.

10p - your code (3 posts up) has a bug in it; the correct syntax for Atan2 is Atan2(y,x), not x,y.

ah, yeah - I do know about the param order, that's just a typo in the code I posted. :) I'll change it.

Gah, trying to find an answer to this on the net is a real pain - every other language apart from blitz seems to work in radians!

Well, converting from degrees to radians is easy.

radian# = (180.0 / Pi)

This'll tell you that one radian = 57 (ish) degrees.

I can't remember if Pi is a function or a constant, so you might need brackets on it to get it to return something other than 0.

Yeah, but it still doesn't seem to work. :/

Well, you could replace all the if's with simply the difference between atan(y1,x1)-atan(y2,x2). This is basically what it says in the first post. It will work, if you keep the difference between the angles small enough. However if one angle is 359 and the other 10, it will take the longest route instead of the shortest:
Graphics 800, 600, 0, 2
SetBuffer BackBuffer()

ang1# = Rand(360)
ang2# = Rand(360)
ang2r# = ang2#

Repeat
	
	;draw angles
	Cls
	Origin 400, 300

	;red=angle1	
	Color 255, 0, 0
	x1# = Cos(ang1) * 120
	y1# = Sin(ang1) * 120
	Line 0, 0, x1, y1
	Text x1 * 1.2, y1 * 1.2, ang1, 1, 1
	
	;aqua=angle2
	Color 0, 255, 255
	x2# = Cos(ang2) * 120
	y2# = Sin(ang2) * 120
	Line 0, 0, x2, y2
	Text x2 * 1.2, y2 * 1.2, ang2, 1, 1
	
	Origin 0, 0

;-----------------------------------------------------------------------------
	;calculate angles from coordinates
	Color 255, 255, 255
	angle1# = ATan2(y1, x1); + 360) Mod 360
	angle2# = ATan2(y2, x2); + 360) Mod 360
	
	;calculate differences
	angdif# = (angle2 - angle1)
;-----------------------------------------------------------------------------

	;show difference
	Text 0, 100, "difference= " + angdif#
	
	;show usage
	Text 0, 0, "usage:"
	Text 0, 20, "use cursor keys to move angle1, use space to apply difference"

	;cursor keys = move angle 1
	If KeyDown(203) Then ang1 = ang1 - 1
	If KeyDown(205) Then ang1 = ang1 + 1
	
	;automove with space
	If KeyHit(57) Then
		ang2r# = angle2 - angdif
	End If	
	If ang2r <> ang2 Then ang2 = ang2 + Sgn(ang2r - ang2)	
	

	Flip

Until KeyHit(1)
WaitKey()

End

edit: I removed the "Mod 360"'s and it seems to be a bit better

Yeah, that's basically doing what I tried originally - the equation cited in the original post. It partially works, but not always - which is basically useless. :)

It seems I can get it to work by adjusting the resultant angle with:
		If ang < -180.0 Then ang = ang + 360.0
		If ang > 180.0 Then ang = ang - 360.0

but this is still a bit of a kludge that the original text equation doesn't seem to require. Oh, well.

The vector solution that was referred to somewhere, is probably dot product.
dot product = x1*x2+y1*y2
angle difference = Acos((dot product)/(Sqr(x1*x1+y1*y1)*Sqr(x2*x2+y2*y2)))
The result is in radians, so
result = angle*180/PI
So,
angle# = Acos((x1*x2+y1*y2)/(Sqr(x1*x1+y1*y1)*Sqr(x2*x2+y2*y2)))*180/PI
But this gives the absolute value of angle difference ( range 0 - +180), not the direction (range -180 - +180). So you would have to calculate the sign separately. I bet there's a nifty way for that too, but I just came back to work from my vacation and my brain refuces to co-operate.

This is the best I've come up with, so far:
ang# = ATan2(y2,x2) - ATan2(y1,x1)
If Abs(ang) > 180 Then ang = ang - (360.0 * Sgn(ang))


Yep, using vector calculation is not always the most simple way. Recently I wanted to calculate ricochet angle using vector calculations. Banged my head to the keyboard for hours but finally found a solution. I had done it before, using just angles (was way more simpler). Maybe I should test both for speed. After all, that's what counts.

Heh - I know what you mean. :)

I started out thinking working with vectors would be the sensible thing to do, but it's turning out that I always need to convert these to angles for calculations, anyway. I think I'll just stick to working with angles, where possible.