Search code examples
c#trigonometryangletriangulationtriangular

Is angle between 2 vectors reflex? (C#)


I need to be able to check whether the angle between three points (A, B and C) which make up part of a shape is reflex (> PI radians), as in the diagram below (sorry for poor paint skills!):

Angle ABC

My points should always be anti-clockwise, and I always want to measure the angle on the inside of the shape.

I am currently doing this using the following code:

//triangle[] is an array of the three points I am testing, corresponding
// to [A, B, C] on the diagram above

//Vectors from B to A and C
PointF toA = PointFVectorTools.difference(triangle[0], triangle[1]);
PointF toC = PointFVectorTools.difference(triangle[2], triangle[1]);

double angle = Math.Atan2(toB.Y, toB.X) - Math.Atan2(toA.Y, toA.X);

//Put angle in range 0 to 2 PI
if (angle < 0) angle += 2 * Math.PI;
return angle > Math.PI;

This has worked in all the cases I have tried up until now, but with these co-ords it does not work: enter image description here

(Where B=(2,3) )

The angle I get back is ~-0.5, whereas I would expect ~+0.5. Any ideas why this is wrong?

UPDATE

I've attempted to implement Nico's solution, and while I understand it in theory I'm getting a real headache trying to implement it. Here is the code so far:

//Vector A -> B
float dx = triangle[1].X - triangle[0].X;
float dy = triangle[1].Y - triangle[0].Y;

//Left normal = (y, -x)
PointF leftDir = new PointF(dy, -dx);

//Vector B -> C
dx = triangle[2].X - triangle[1].X;
dy = triangle[2].Y - triangle[1].Y;

//Dot product of B->C and Left normal
float dot = dx * leftDir.X + dy * leftDir.Y;
return dot < 0;

Solution

  • In the following, I assume that the x-axis points to the right and the y-axis points upwards. If this is not the case in your scenario, you might need to switch some signs.

    If you have the line segment (x1, y1) - (x2, y2) and points are sorted counter-clockwise, you know that the shape is left of the line segment. The orthogonal direction vector that points to the line segment's left is:

    leftDir = (y1 - y2, x2 - x1)
    

    Together with the line segment, this direction defines a half space. If the following angle is convex, the third point must lie in this half space. If that's not the case, the angle is concave (which you apparently call reflex):

    You can determine if the point lies in the same half space with the dot product:

    isConcave = dot(p3 - p2, leftDir) < 0
    

    In code:

    float dx = x3 - x2;
    float dy = y3 - y2;
    float dot = dx * leftDir.x + dy * leftDir.y
    return dot < 0;