• Advertisement
Sign in to follow this  

[C#] [XNA] Using the dot product to find the angle between two vectors.

This topic is 3035 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Here I have a simple method that returns whether the angle (theta) is obtuse, perpendicular or acute based on the result of the dot product.
        public static Angle GetAngleBetweenVectors(Vector3 vector_1, Vector3 vector_2)
        {
            float theta = Vector3.Dot(vector_1, vector_2);

            if (theta < 0)
            {
                return Angle.Obtuse;
            }
            if (theta > 0)
            {
                return Angle.Acute;
            }
            if (theta == 0)
            {
                return Angle.Perpendicular;
            }
            return Angle.Perpendicular;
        }

The problem is that in XNA if I have two triangles, one with its normal pointing "left" (0,-1,0) and the other pointing "forward" (0,0,-1), then the dot product between them returns perpendicular. However, the two triangles themselves are not perpendicular (they are at 270°). How can I work out a fast way of checking the correct angle between two triangles? Do I have to use the cross product between the triangle normal and "up" (0,1,0) to get the triangle plane vector or is there a faster way? Thank you.

Share this post


Link to post
Share on other sites
Advertisement
You need a better description of what you're trying here. Why do you think 0, -1, 0 and 0, 0, -1 are at 270 degrees rather than 90 degrees?

Share this post


Link to post
Share on other sites
Apologies.


^
|
| (Tri 2) [Normal = (0,0,-1)]
|
______________
|
|
|
(Tri 1) [Normal = (-1,0,0) <---- |
|
|
|


The -ve halfspace of the triangle plane is ignored so therefore the angle between triangle 1 and triangle 2 is 270°.

Share this post


Link to post
Share on other sites
I still don't see why that would give you 270 rather than 90. Assume I'm a complete idiot and spell it out for me in simple terms.

Share this post


Link to post
Share on other sites
It is probably me being the idiot here but:


^
|
___ | (Tri 2) [Normal = (0,0,-1)]
| | |
| Θ ______________
| |.............
--|.............
|.............
(Tri 1) [Normal = (-1,0,0) <---- |............. (Solid)
|.............
|.............
|.............


If the negative half space is considered solid and therefore ignored, the angle (theta) between the face of triangle 1 and the face of triangle 2 (in non solid space) is 270°.

If the two areas solid/non solid areas were swapped then the angle theta would be 90°.

Does that make sense?

Share this post


Link to post
Share on other sites
Quote:
Original post by Spa8nky
Apologies.


^
|
| (Tri 2) [Normal = (0,0,-1)]
|
______________
|
|
|
(Tri 1) [Normal = (-1,0,0) <---- |
|
|
|


The -ve halfspace of the triangle plane is ignored so therefore the angle between triangle 1 and triangle 2 is 270°.
It seems that what you're looking for is the signed angle between the two triangle normals.

In 3-d, you have to use an external reference if you want to determine the sign of the angle between two vectors. I believe in your example it may work to use the direction of the edge shared by the two triangles as the reference (although I'd have to play around with it a bit to be sure).

Here's what a 3-d 'signed angle' function might look like:
float signed_angle(vector3 v1, vector3 v2, vector3 reference) {
vector3 c = cross(v1,v2);
float angle = atan2(length(c),dot(v1,v2));
return dot(c,reference) < 0 ? -angle: angle;
}

Share this post


Link to post
Share on other sites
Quote:
Here's what a 3-d 'signed angle' function might look like


Hi jyk, a couple of things about your method confuse me:

Your code returns an angle of ±90° when converted from radians. If the angle is -90° should I then return 360-90 (270°)?

I'm also not sure I understand the reference angle to which you refer. How can it be determined for any two triangles with any position?

Thanks

Share this post


Link to post
Share on other sites
Quote:
Your code returns an angle of ±90° when converted from radians. If the angle is -90° should I then return 360-90 (270°)?
Assuming a typical implementation of atan2, the function will return an angle in the range [-180,180] (more or less) when converted from radians. If you need the angle in some other range, just adjust the result as necessary (e.g. by adding 360 for negative angles, as in your example).

Quote:
I'm also not sure I understand the reference angle to which you refer. How can it be determined for any two triangles with any position?
It's not a reference angle, but a reference vector (note that the third argument to the function is a vector, not a scalar).

I'm assuming that the two triangles whose normals you wish to compare share an edge (if they don't, then that's a different problem). What I'm suggesting is that you use the direction vector associated with this shared edge as the reference vector for the 'signed angle' function.

To elaborate a bit, say the vertices of the first triangle are v1, v2, and v3, and that the edge v2->v3 is shared with the other triangle. You would then use 'v3 - v2' as the reference vector for the 'signed angle' function. (You might need to negate the reference vector to get the values that you expect - it just depends on what conventions you're using.)

Share this post


Link to post
Share on other sites
Sorry I meant to say reference vector as I can see it is a vector being passed into the method.

The triangles will not share an edge in all cases, unfortunately.

Share this post


Link to post
Share on other sites
Quote:
The triangles will not share an edge in all cases, unfortunately.
In that case, I think we'll need more information (as SiCrane suggested earlier). To determine the sign of the angle between the normals of two arbitrarily oriented and possibly disjoint triangles, you're going to need some sort of reference or metric, and what to use for that reference or metric is necessarily context-dependent.

Can you tell us what problem you're trying to solve exactly?

Share this post


Link to post
Share on other sites
Also I could not get your method to work with acute angles such as the following:


public static Angle? GetSignedAngleBetweenVectors(Vector3 v1, Vector3 v2, Vector3 reference)
{
Vector3 c = Vector3.Cross(v1, v2);
float theta = Vector3.Dot(v1, v2);
float angle = (float)Math.Atan2(c.Length(), theta);

theta = Vector3.Dot(c, reference);

if (theta < 0)
{
return Angle.Obtuse;
}
if (theta > 0)
{
return Angle.Acute;
}
if (theta == 0)
{
return Angle.Perpendicular;
}

return null;
}






The normals are:

Triangle 1: (0.573, 0, -0.81)
Triangle 2: (0, 0, 1)

Visualization:

EDIT: Removed as it wasn't showing correctly with "pre" tags

They share the same edge which can be shown as (0,1,0) when normalized.

Share this post


Link to post
Share on other sites
Quote:

Can you tell us what problem you're trying to solve exactly?


Absolutely. I am trying to determine which collision response to use when a bounding volume intersects a triangle.

If there is more than one triangle being intersected the angle in open space between them must be found to determine how to respond correctly.

Here is an extract from the response code:


switch (triangles_Intersected)
{
case 0:
break;
case 1:
position_Projected += list_Contact[0].normal * list_Contact[0].penetration;
break;
case 2:
switch (Math_Extension.GetSignedAngleBetweenVectors(Game1.debug_Triangle[0].Normal_Normalized, Game1.debug_Triangle[1].Normal_Normalized, Vector3.Up))
{
case Angle.Acute:
for (int i = 0; i < 2; i++)
{
// Project the position by the penetration along the normal for both contacts
position_Projected += list_Contact.normal * list_Contact.penetration;
}
break;
case Angle.Obtuse:
int index_Closest = 0;
// Check both contacts generated from triangle intersection
for (int i = 0; i < 2; i++)
{
// If the penetration distance is the closest (I.e. smallest)
if (Contact.KeepClosest(list_Contact.penetration))
{
// Set the index to the current contact index
index_Closest = i;
}
}
// Use the smallest penetration to adjust the projected position
position_Projected += list_Contact[index_Closest].normal * list_Contact[index_Closest].penetration;
break;
case Angle.Perpendicular:
for (int i = 0; i < 2; i++)
{
// Project the position by the penetration along the normal for both contacts
position_Projected += list_Contact.normal * list_Contact.penetration;
}
break;
}
break;
case 3:
Console.WriteLine("3 triangles intersected, what do I do now?");
break;
default:
break;
}



Hope that helps and thanks for helping me out.

Share this post


Link to post
Share on other sites
Quote:
Also I could not get your method to work with acute angles such as the following:

*** Source Snippet Removed ***
The method I posted works. However, what you've shown above isn't the method I posted (in fact, it's not even close :). Also, unless there are some constraints imposed on your geometry that we're not aware of, I don't think using the global up vector as the reference vector (as in your follow-up post) will work.

In any case, it looks to me like the real problem here is one of collision response. This can be a non-trivial problem to solve, but it's been discussed quite a bit in this forum, so a search of the forum archives might yield some threads that could be of use to you.

Share this post


Link to post
Share on other sites
I think your problem is underconstrained. How are you going to tell the difference between these two situations with just the triangle normals?

^
|
| (Tri 2) [Normal = (0,0,-1)]
|
______________
|.............
|.............
|.............
(Tri 1) [Normal = (-1,0,0) <---- |.............
|.............
|.............
|.............

|..
|..
|..
(Tri 1) [Normal = (-1,0,0) <---- |..
|..
^ |..
(Tri 2) [Normal = (0,0,-1)] | |..
______________ ..
.................

Share this post


Link to post
Share on other sites
Quote:

I think your problem is underconstrained. How are you going to tell the difference between these two situations with just the triangle normals?


Agreed.

For my triangle I know the following:

A,B,C - 3 non colinear points that make up the triangle
E0,E1,E2 - 3 triangle edges
Plane - Triangle plane
Normal - Triangle/Plane normal (both normalized and non normalized)

Is that everything I need to determine the difference?

Quote:

(in fact, it's not even close :).


Damn :( The edge shared by triangle 1 and triangle two when normalized equals (0,1,0), (honestly) I must have messed something up though.

I am still without a clue as to determine theta in open space and at your mercy.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
I think your problem is underconstrained. How are you going to tell the difference between these two situations with just the triangle normals?

^
|
| (Tri 2) [Normal = (0,0,-1)]
|
______________
|.............
|.............
|.............
(Tri 1) [Normal = (-1,0,0) |.............
|.............
|.............
|.............

|..
|..
|..
(Tri 1) [Normal = (-1,0,0) |..
|..
^ |..
(Tri 2) [Normal = (0,0,-1)] | |..
______________ ..
.................



Had a good think about this after some sleep and decided that I could differentiate between the two cases above by finding the central point of one triangle (tri.A + tri.B + tri.C)/3 and then calculating whether the that point lay in the +ve/-ve halfspace of the plane.

The sign of the distance would indicate which halfspace the point lay in:

float distance = Vector3.Dot(Centre, p.Normal) - p.D;

This way I could tell the difference between the two.

What do you guys think of that idea?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement