Sign in to follow this  
Berger

Angle Between Two Lines (clockwise)

Recommended Posts

I have a question whos math is probably pretty basic, but still requires a bit of thought: Say I have 2 lines connected, represented by 3 points, and I want to find the angle between them. But not just the regular angle, I want to find the angle always in a CLOCKWISE motion, starting with one of the lines being "first". So say I have the first line (represented by points 1 and 2), and I sweep that line in a clockwise direction until I meet the line formed by points 2 and 3, I will get a particular degree measurement with that method. What formulas/psuedocode, etc, can I use to calculate the degree measurement through this method? Any help would be greatly appreciated, thanks. :)

Share this post


Link to post
Share on other sites
The negation of the arcsine of the ratio of the cross product to the product of the lengths of the lines.

-arcsine(cross(v1,v2)/|v1||v2|)

Where v1 and v2 are (p1-p2) and (p3-p2).

Actually this only works if the angle is between -90 and 90 degrees...

A simple, general method, in C++ style pseudocode:

v1 = p1-p2
v2 = p3-p2
angle = -(180/pi)*atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x+v1.y*v2.y))

[Edited by - Vorpy on March 21, 2008 12:11:25 AM]

Share this post


Link to post
Share on other sites
The way I usually think of this type of 2D problem is by using complex numbers to represent points. I keep the angles in radians. Convert them to degrees if you must.

#include <complex>
#include <iostream>

typedef std::complex<double> Point2D;

double clockwise_angle(Point2D p1, Point2D p2, Point2D p3){
return std::arg((p1-p2)/(p3-p2));
}

int main(){
Point2D p1(1,1), p2(2,0), p3(3,0);
std::cout << clockwise_angle(p1,p2,p3) << std::endl;
}






Oh, of course there was nothing wrong with Vorpy's solution. I just thought I would introduce a different perspective on the problem.

Share this post


Link to post
Share on other sites
Using the cross-product to solve this problem is backed by the relation
| a x b | = | a | * | b | * | sin( < a , b > ) |
It works for 3D only and for any angle, but can be resolved for angles in [0°,+90°] only due to the absolute function around the sine. (Notice also the abs around the cross-product!)

Using the dot-product is backed by the definition
a . b = | a | * | b | * cos( < a , b > )
It works for any dimensionality and any angle, but can be resolved after all for angles in [0°,+180°].

The way using atan2() works for 2 dimensions only, but can be resolved for the full range of angles [-180°,+180°]. Of course, one can project 3D points onto the plane spanned by the point's pairwise difference vectors, so reducing the 3D space to a 2D space.

So far none of these methods especially consider a CW or CCW order. Here is the problem what CW/CCW means in 3D space at all. It can be expressed by a direction vector that denotes the winding direction, but the OP doesn't declare how to yield in that vector. If such a vector were given, then the both possible results of a cross-product between the difference vectors could be compared to the winding vector using the dot-product, so determining whether the computed angle or its counterpart 360°-angle is the actual result.

[Edited by - haegarr on March 21, 2008 11:05:05 AM]

Share this post


Link to post
Share on other sites
Hey thanks both for the follow-up.

haegarr, I tested Vorpy's solution and it seems to work. But you say it doesn't consider clockwise motion? I don't know much in the way of math, but I determined the only way to measure angles meaningfully between multiple lines in a sequence was this way. So how does his solution work without measuring in one direction only? Isn't a modulus-360 (+180/-180) measurement of an angle meaningless if you don't consider a rotational direction? I'm not asking to nitpick, this is just a very important part of my program and need to make sure I get it right :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Berger
I tested Vorpy's solution and it seems to work

Okay, let's take a deeper look. BTW: You never told us whether you deal with a 2 or 3 (or whatever) dimensional space (or else I've mised it). So, have you tested his 3D or his 2D solution?

The cross-product of 2 direction vectors
a x b
results in a vector
(a) that is perpendicular to the plane spanned by a and b if a and b are not co-linear and don't vanish,
(b) which length is proprotional to the length of a,
(c) which length is proprotional to the length of b,
(d) which length is proprotional to the sine of the angle <a,b> between the both vector, see below.
The latter 3 properties are expressed by the formula
| a x b | = | a | * | b | * | sin( <a,b> ) |
The length of the resulting vector is always positive, and hence all parts on the right side must be positive. So the sine is wrapped by an abs operator. However, one can drop the abs around the sine if one defines that the angle between both vectors must just be in the range [0,+180°] (what is a legal way, see below).

Re-ordering the formula yields in
<a,b> = | asin( | a x b | / ( | a | * | b | ) ) |
Comparing this with Vorpy's corresponding formula show some non-marginal differences, besides others that a scalar is the argument of the sine (not a vector).

The other thing is that of the range. If defining the angle being in the range [-180°,+180°] and hence using the abs around the sine/arcsine, then for a full circle each function value will in general occur 4 times, reducing the useable result angle to 360°/4=90°. (Draw |sin(x)| for 0<=x<=360° on paper and look how often e.g. 0,7 occurs as function value.) The abs around the sine makes the (periodic) sine even-symmetric! If, on the other hand, only angles between [0°,180°] are defined and hence the abs around sine/arcsine can be dropped, then the intersection of [0°,+180°] and the result range [-90°,+90°] of the arcsine still give [0°,+90°] as result.

To make it clear by example rather than by mathematical theory, look at this:
| a | * | b | * sin( 45° ) = ?
| a | * | b | * sin( 135° ) = ?
and, if all angles are allowed, | a | * | b | * | sin( -45° ) | = ?
Yes, all results are the same!

Quote:
Original post by Berger
So how does his solution work without measuring in one direction only?

Both the cross-product as well as the dot-product use the term "angle between vectors a and b" (<a,b>). There is no distinction to "angle between vectors b and a". EDIT <a,b> can be understood as the shortest arc between the vectors. ENDEDIT That is the reason why restricting the angle to [0°,+180°] is possible.

Quote:
Original post by Berger
Isn't a modulus-360 (+180/-180) measurement of an angle meaningless if you don't consider a rotational direction?

You're right, but your OP explicitely requests a rotational direction, namely CW! If you pick one of the both difference vectors as start vector, and want to compute the angle from there to the second vector on the CW arc, then you need a "winding vector" when in 3D to define what CW actually means. I've understood your OP that you wanted to do so. Perhaps I misunderstood it.


Well, I really should not write so looooong posts ;)

Share this post


Link to post
Share on other sites
Ok, let me clear up two things:

Sorry, I should have originally stated this is for 2-D only.

Clockwise direction isn't necessary, but some constant direction is. In other words, always clockwise or always counterclockwise, or anything that would yield the same results. I should have been clearer on this too, it's just that I thought it wouldn't matter in the answer, and tried to make the question simpler.

I'm pretty sure you need to consider which line is first, even if that's inherint to the formula, otherwise the results would give wrong results.

Given those clarifications, does it make sense that Vorpy's solution seems to work? Or will I need extra/different formulas? I'm trying to nail this down 100% so I don't find out 3000 lines of code later that I need to rewrite :D

Thanks very much for the reply :)

Share this post


Link to post
Share on other sites
Yes, Vorpy's atan2 solution will work. If you always need a positive angle, add 360 degrees when the formula returns a negative. Also, never feed it zero-length vectors or it'll die.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this