Relative angle between 2 vectors

Blitz3D Forums/Blitz3D Programming/Relative angle between 2 vectors

big10p(Posted 2007) [#1]
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.)


tonyg(Posted 2007) [#2]
Isn't is angle=atan2(y1-y2,x1-x2)?


big10p(Posted 2007) [#3]
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



b32(Posted 2007) [#4]
And this ?



big10p(Posted 2007) [#5]
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.


GfK(Posted 2007) [#6]
10p - your code (3 posts up) has a bug in it; the correct syntax for Atan2 is Atan2(y,x), not x,y.


big10p(Posted 2007) [#7]
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!


GfK(Posted 2007) [#8]
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.


big10p(Posted 2007) [#9]
Yeah, but it still doesn't seem to work. :/


b32(Posted 2007) [#10]
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:

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


big10p(Posted 2007) [#11]
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.


Jasu(Posted 2007) [#12]
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.


big10p(Posted 2007) [#13]
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))



Jasu(Posted 2007) [#14]
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.


big10p(Posted 2007) [#15]
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.