Archived

This topic is now archived and is closed to further replies.

apanjocko

angles between two vectors

Recommended Posts

if you have two vectors (in world space), how do you calculate the x, y (you could skip the z angle) between them? i have done like this, but it seems to sometimes give slightly bad results (looks right most of the time though!):

// first vector, the surface vector of the terrain

_terrainNormal = _terrain.GetTerrainNormal( _x, _y );
	
// second vector

Vector3 tankNormal = new Vector3( 0, 0, -1 );

// calculate angles


_angleX = (float) Math.Asin( ( _terrainNormal.X - tankNormal.X ) / ( Math.Sqrt( _terrainNormal.X * _terrainNormal.X + _terrainNormal.Z * _terrainNormal.Z )) );

_angleY = (float) Math.Asin( ( _terrainNormal.Y - tankNormal.Y ) / ( Math.Sqrt( _terrainNormal.Y * _terrainNormal.Y + _terrainNormal.Z * _terrainNormal.Z )) );



that is, im doing the regular sin(angle) = a/b thingy here, and sort of "projecting" the vectors on a 2d picture. like, when calculating angleX i totally forgett about the difference between the vectors in Y. can you do it like this?

if you can do like that, maybe there is something wrong with the code that get the terrain normal?  this code takes three points and makes it a normal:

   

		public Vector3 GetTerrainNormal( float x, float y )
		{
			Vector3 p1 = Vector3.Empty;
			Vector3 p2 = Vector3.Empty;
			Vector3 p3 = Vector3.Empty;

			float fPosX = (x / _spacing);
			float fPosY = (_terrainHeight-y)/_spacing;

			int posX = (int) fPosX;
			int posY = (int) fPosY;

			float percentX = fPosX - (int) posX;
			float percentY = fPosY - (int) posY;

			float balance = percentX + percentY;

			int triangle;

			if( balance < 1 ) triangle = 0;
			else triangle = 1;

			if( triangle == 0 ) // top left triangle

			{
				p1.X = GridXToTerrainX( posX );
				p1.Y = GridYToTerrainY( posY );
				p1.Z = GridZToTerrainZ( GetGridHeight( posX, posY ) );

				p2.X = GridXToTerrainX( posX + 1 );
				p2.Y = GridYToTerrainY( posY );
				p2.Z = GridZToTerrainZ( GetGridHeight( posX + 1, posY ) );

				p3.X = GridXToTerrainX( posX );
				p3.Y = GridYToTerrainY( posY + 1 );
				p3.Z = GridZToTerrainZ( GetGridHeight( posX, posY + 1 ) );
			}
			else // bottom right triangle

			{
				p1.X = GridXToTerrainX( posX );
				p1.Y = GridYToTerrainY( posY + 1 );
				p1.Z = GridZToTerrainZ( GetGridHeight( posX, posY + 1 ) );

				p2.X = GridXToTerrainX( posX + 1 );
				p2.Y = GridYToTerrainY( posY );
				p2.Z = GridZToTerrainZ( GetGridHeight( posX + 1, posY ) );

				p3.X = GridXToTerrainX( posX + 1 );
				p3.Y = GridYToTerrainY( posY + 1 );
				p3.Z = GridZToTerrainZ( GetGridHeight( posX + 1, posY + 1 ) );
			}

			// TextWriter.AddText( "   triangle: " + triangle );


			// calculate the x and y angles to the terrain plane normal from the tank normal


			Vector3 v12 = p1 - p2;
			Vector3 v13 = p1 - p3;

			Vector3 normalTerrain = Vector3.Cross( v12, v13 );

			// TextWriter.AddText( "   normal: (" + normalTerrain.X + ", " + normalTerrain.Y + ", " + normalTerrain.Z + ")" );


			normalTerrain.Normalize();

			return normalTerrain;
		}


thanks for any help! [edited by - apanjocko on October 23, 2003 9:21:32 PM]

Share this post


Link to post
Share on other sites
You can use the dot-product to get the angle


A.B = |A||B|cos(theta)
where theta is the angle between the 2 vectors

also
A.B = (A.x * B.x) + (A.y * B.y)

you know what A and B are so you can solve for theta


cos(theta) = A.B / |A||B|

theta = acos((A.xB.x + A.yB.y) / (sqr(A.x^2 + A.y^2) * sqr(B.x^2 + B.y^2)))

Share this post


Link to post
Share on other sites
well, that''s not what i want,
you see,
you only get one,
i want two angles, x and y - how much to rotate in the world X axis and how much to rotate in the world Y axis... that angle is not helping me in this case

Share this post


Link to post
Share on other sites
the reason why you use the arctangent2 instead of atan, acos or asin is beacuse of their precision at their limits. atan2 doesn''t have anyh precision limits to it. Read the help files and documentation for a full explaination.

Also, check out this post http://www.gamedev.net/community/forums/topic.asp?topic_id=185968 (recent) in the maths forum. one by minorlogic. It will save me ALOT of explaining hopefully


me off - taking gf out to the hair stylists.

Share this post


Link to post
Share on other sites
in the post by ob, the code he uses is quite bad (mathematically it is completely correct, but for computational maths and physics (as one of my university subjects was called) it''s not accurate) as
(original)
theta = acos((A.xB.x + A.yB.y) / (sqr(A.x^2 + A.y^2) * sqr(B.x^2 + B.y^2)))

is loosing precision twice NOT including the floating point precision.

calling sqrt twice isn''t good. on really small vectors, it buggers up basically. instead of being accurate to about 12 sig figures (1.xxxx numbers) it will drop to about 10. I can post an example if you wish.
original
theta = acos((Ai*Bi+Aj*Bj+Ak*Bk)/(sqrt(Ai*Ai+Aj*Aj+Ak*Ak)*sqrt(Bi*Bi+Bj*Bj+Bk*Bk)))
rearrange to
theta = acos((Ai*Bi+Aj*Bj+Ak*Bk)/sqrt((Ai*Ai+Aj*Aj+Ak*Ak)*(Bi*Bi+Bj*Bj+Bk*Bk)))

That is one part that will help. The other is that acos (as I said before) looses precision in at different angles.

Bascially, use atan2 like EDI said to get around that

try
atan2l(cross(&v1,&v2).length(),dot(&v1,&v2));

(v1 and v2 are vectors)
(cross is the cross product)
(dot is the dot product)
(length is the length of a vector - in this case the cross product)


Hope I didn''t confuse you too much.

Also - not sure how old you are, but atleast at my university I took up some electives (I''m an engineer) in the physics and maths strands (which may be part of a double latter) called computational maths and physics. there is alot of image manipulation, and alot of maths stuff that you learn, like the one above (quite simple) in it. if your at uni, see if you are able to do some similar electives. my advice anyway.

Share this post


Link to post
Share on other sites
thank you very much for your time,
i mangaged to solve it with tan, and will as soon as i've read the thread you linked to try it with atan2.

this however does the work with fewer operations:

_angleX = (float) Math.Atan( ( -_terrainNormal.X + tankNormal.X ) / tankNormal.Z );

_angleY = (float) Math.Atan( ( -_terrainNormal.Y + tankNormal.Y ) / tankNormal.Z );

why does everyone have to make it so complicated?

[edited by - apanjocko on October 24, 2003 7:37:49 AM]

[edited by - apanjocko on October 24, 2003 7:39:59 AM]

Share this post


Link to post
Share on other sites
and another thing
that still only gives me the "absolute" angle between the vectors. i needed two angles projected on the XZ plane and the YZ plane... which my version solves...

[edited by - apanjocko on October 24, 2003 7:41:42 AM]

Share this post


Link to post
Share on other sites
it works on all of them, just depends on how many dimensions you use. The code that I use has 4 dimensions (yes, that''s x,y,z and that other one w).

but the descrepency that you are getting will still be there, but less unless you use atan2

_angleY = (float) Math.Atan( ( -_terrainNormal.Y + tankNormal.Y ) / tankNormal.Z );

to
_angleY = (float) Math.Atan2( ( -_terrainNormal.Y + tankNormal.Y ) , tankNormal.Z );

should do it.

bascially, the atan2 versions are alot more accurate than atan. It will provide the best accuracy.

when using floating point, atan/acos/asin is accurate to 8 sig figures (when about 1 or 2 degrees off) and atan2 is accurate to its full 20 sig figures.

Fiddle with it

Share this post


Link to post
Share on other sites
thanks,
yeah i got it working i remember but it wasn''t that easy, just converting it strictly like that over atan2 yielded really strange results... had to change some signs here and there.

but what i meant was Optikal''s solution is less than adequate in this case. I will get an angle that is of no use to me whatsoever.

thanks for all help

Share this post


Link to post
Share on other sites