Title: Find the angle between two vectors in C#
This example shows how you can find the angle between two vectors. The program has three main parts: selecting the points that define the vectors, drawing the vectors, and calculating the angle between them. The last task is the most important, but they're all interesting so I'll cover them all.
Selecting Vectors
The program stores up to three points in a list named Points.
private List Points = new List();
When you click the mouse over the program's PictureBox control, the following event handler adds the point that you clicked to the list.
private void picCanvas_MouseClick(object sender, MouseEventArgs e)
{
if (Points.Count > 2) Points = new List<PointF>();
Points.Add(e.Location);
picCanvas.Refresh();
if (Points.Count == 3)
{
double radians = VectorAngle(
Points[0], Points[1],
Points[1], Points[2]);
double degrees = radians / Math.PI * 180;
lblAngle.Text = radians.ToString("0.00") + " radians, " +
degrees.ToString("0.00") + " degrees";
}
else
lblAngle.Text = "";
}
If the Points list already holds three points, then it is full so the program starts over. It sets Points equal to a new, empty list.
Next the code adds the point that you clicked to the Points list. It also refreshes the PictureBox to draw the new situation.
If the list now contains three points, then the program needs to calculate the angle between the two vectors defined by the points.
Note that you can consider the angle between the extension of the first vector and the second vector, or you can consider the angle between the two vectors when they are moved to have a common starting point as shown in the picture on the right. The two angles are the same, so you can think about the problem in whichever way is easier for you to visualize.
If we have three points defined, the program calls the VectorAngle method described later to find the angle between the two vectors. It then displays the angle in radians and degrees in the label lblAngle.
If we do not have three points defined, the program clears the lblAngle label.
Drawing Vectors
When the program's PictureBox control needs to repaint, the following event handler executes.
private const int Radius = 4;
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (Points.Count > 1)
{
e.Graphics.DrawLines(Pens.Red, Points.ToArray());
for (int i = 1; i < Points.Count; i++)
DrawArrowhead(e.Graphics, Pens.Red, Radius,
Points[i  1], Points[i]);
}
foreach (PointF point in Points)
{
RectangleF rect = new RectangleF(
point.X  Radius, point.Y  Radius,
2 * Radius, 2 * Radius);
e.Graphics.FillEllipse(Brushes.LightBlue, rect);
e.Graphics.DrawEllipse(Pens.Blue, rect);
}
}
The constants Radius indicates the radius of the circles that the program draws for each of the selected points.
The Paint event handler does the drawing. After setting the Graphics object's SmoothingMode property to produce smooth lined and circles, the code checks whether the Points list contains more than one point. If there are more than two points, the program uses the DrawLines method to connect them in a series of line segments. (Either one or two segments depending on whether the list contains two or three points.)
It then loops through the segments and calls the DrawArrowhead method to draw arrowheads on the segments. That method is similar to the one described in the post Draw lines with arrowheads in C#. The new version is modified to pull the arrowheads back distance Radius from the segment's end point so the arrowhead doesn't sit under the points, but it's otherwise similar. See that post and download the example to see the details.
After it has drawn the segments, the code loops through the points and draws a circle at each.
Calculating Angles
Calculating the angle is the fun, mathy part. The calculation relies on two vector calculations: the dot product and the cross product. The following sections talk a bit about vectors, how you can use the dot and cross products to calculate an angle's sine and cosine, and how you can use that information to find the angle.
Vectors
Before I get to dot and cross products, I should briefly explain what a vector is. A vector is an object that has a direction and length. For example, you can think of one as an instruction like, "Go 10 units in the direction 75 degrees." It's usually easier to work with a Cartesian representation. For example, the vector <5, 9> means, "Go 5 units in the X direction and then 9 units in the Y direction."
Vector mathematics is pretty interesting. For example, if you add a point ro a vector, you basically start at that point and then move as directed by the vector. The result is the point where you end up. To calculate that final point, you simply add the starting point's coordinates to the vector's components. For example, (3, 6) + <5, 9> = (3 + 5, 6 + 9) = (8, 3).
To find the vector between two points, you subtract their components. For example, the vector starting at the point (2, 4) and ending at the point (6, 3) is <6  2, 3  4> = <8, 1>.
There is a bit more to vector mathematics, but the only additional things we really need for this example are dot products and cross products.
Dot Products
The dot product is a scalar (numeric) value calculated for two vectors. In general, if the vectors are A = <a1, a2, a3, ..., aN> and B = <b1, b2, b3, ..., bN>, then their dot product written A · B is given by A · B = a1 * b1 + a2 * b2 + a3 * b3 + ... + aN * bN. Because we're working with twodimensional vectors, this is just a1 * b1 + a2 * b2.
It turns out that the dot product is also given by A · B = A * B * cosine(angle) where A and B are the lengths of the vectors and angle is the angle between them.
This gives us a way to find the cosine of the angle: use the first formula to calculate the vectors' dot product and then divide it by the vectors' lengths.
cosine(angle) = (a1 * b1 + a2 * b2) / A / B
You can use the Math.Acos method to find the angle with this cosine, but unfortunately that is not enough to determine the angle uniquely. It turns out that angle and angle have the same cosine, as shown in the picture on the right. If the blue segments all have length 1, then the red bar shows the cosine of both angles. If you don't remember this from your high school trigonometry class, don't worry about it. Just take my word for it that angle and angle have the same cosine.
This is where the cross product comes in.
Cross Products
The cross product of two vectors is a third vector that is perpendicular to the first two. As with the dot product, you can calculate the cross product in two different ways and use them to get some information about the angle between the two vectors.
The cross product is not defined for twodimensional vectors. That may make some sense because the result will be a third vector that is perpendicular to the original vectors and you can't have a vector perpendicular to two vectors with all three lying in the same plane. To work around this, we simply pretend the two vectors are lying in the XY plane in threedimensional space.
You can calculate the cross product of twodimensional vectors by using the following formula.
A × B = < a2 * b3  a3 * b2,
a1 * b3 + a3 * b1,
a1 * b2  a2 * b1>
Because we are working with twodimensional vectors, the a3 and b3 components are zero, so this equation simplifies to the following.
A × B = <0, 0, a1 * b2  a2 * b1>
This makes sense because the result vector should be perpendicular to the first two vectors. If those vectors lie in the XY plane, then the result vector points either straight up or straight down. That means its X and Y components should be zero.
The second way you can define the cross product is to say that the new vector is perpendicular to the first two and that the new vector has length given by the formula A × B = A * B * sin(angle). Again A and B are the lengths of vectors A and B.
If you look at the earlier formula, it's easy to see that the length of the result vector is a1 * b2  a2 * b1, so now we can calculate the sine of the angle. Just use the first formula to calculate the cross product and then divide the result by the lengths of vectors A and B.
sine(angle) = (a1 * b2  a2 * b1) / A / B
As was the case with the cosine, the sine does not uniquely determine the angle. The picture on the right shows two angles with the same sine. If the blue segments have length 1, then the green bars show the angles' sines. Again if you don't remember this, don't worry about it. The gist of it is that the values angle and π  angle have the same sine.
Finding Angles
Separately the dot product and cross product cannot uniquely identify an angle, but together they can. Geometrically the dot product tells you whether the angle lies to the left or right of the Y axis and the cross product tells you whether it lies above or below the X axis.
A Quick Aside: The last two picture have assumed that the first vector is horizontal. That makes the pictures easier to understand but it is not necessary. Basically you can take the picture of the original vectors and rotate the whole thing until the first vector is horizontal. The end result will give us the angle between the two vectors whether or not you rotate them. Basically a rotated angle is still the same angle.
There's one more issue that you should understand before you see the code. Because Y coordinates increase downward in Windows Forms, everything we have talked about so far is upside down. That means angles will increase clockwise instead of counterclockwise.
The program uses the following VectorAngle method to calculate the angle between the two vectors p11 > p12 and p21 > p22.
// Return the angle between vector p11 > p12 and p21 > p22.
// Angles less than zero are to the left. Angles greater than
// zero are to the right.
private double VectorAngle(PointF p11, PointF p12, PointF p21, PointF p22)
{
// Find the vectors.
PointF v1 = new PointF(p12.X  p11.X, p12.Y  p11.Y);
PointF v2 = new PointF(p22.X  p21.X, p22.Y  p21.Y);
// Calculate the vector lengths.
double len1 = Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y);
double len2 = Math.Sqrt(v2.X * v2.X + v2.Y * v2.Y);
// Use the dot product to get the cosine.
double dot_product = v1.X * v2.X + v1.Y * v2.Y;
double cos = dot_product / len1 / len2;
// Use the cross product to get the sine.
double cross_product = v1.X * v2.Y  v1.Y * v2.X;
double sin = cross_product / len1 / len2;
// Find the angle.
double angle = Math.Acos(cos);
if (sin < 0) angle = angle;
return angle;
}
The method first subtracts the points' components to find the vectors v1 and v2. (A and B in the earlier discussion.) It stores the results in two PointF variables. (It would be nice to store the results in two actual Vector objects, but the Vector class wasn't introduced until later versions of the .NET Framework, which includes the System.Numerics namespace. We don't really need too much from that class, so I decided to write the code like this so it would work in older versions.)
The method then calculates the lengths of the vectors.
Next the code calculates the dot product and divides it by the vector lengths to get the angle's cosine. Similarly it calculates the cross product and divides it by the lengths to get the angle's sine.
Now the method uses Math.Acos to get an angle that has the calculated cosine. If you look at the earlier picture, the one with the red horizontal bar, you'll see that wee now need to determine whether the angle is the one on top of the X axis or the one below the X axis. (And then flip everything upside down because Y coordinates increase downward.)
To decide which angle to use, the program looks at the sine. If the sine is negative, then the angle is the one below the X axis, so the program negates the value angle.
Conclusion
The vector dot and cross products let you determine the angle between two vectors. The vectors can be in any arrangement, but I'm going to use them to find the angle when you turn from one line segment to another. The picture on the right shows various angles when you turn from the red segment onto one of the blue ones.
As always, download the example to see additional details and to experiment with it. Hopefully I'll be using the VectorAngle method soon in another post.
Download the example to experiment with it and to see additional details.
