7

I need to calculate the angle between 3 points. For this, I do the following:

  1. Grab the 3 points (previous, current and next, it's within a loop)
  2. Calculate the distance between the points with Pythagoras
  3. Calculate the angle using Math.acos

This seems to work fine for shapes without angels of over 180 degrees, however if a shape has such an corner it calculates the short-side. Here's an illustration to show what I mean (the red values are wrong):

A scatch illustrating what goes wrong with the calculations

This is the code that does the calculations:

// Pythagoras for calculating distance between two points (2D)
pointDistance = function (p1x, p1y, p2x, p2y) {
    return Math.sqrt((p1x - p2x)*(p1x - p2x) + (p1y - p2y)*(p1y - p2y));
};

// Get the distance between the previous, current and next points
// vprev, vcur and vnext are objects that look like this:
//     { x:float, y:float, z:float }
lcn = pointDistance(vcur.x, vcur.z, vnext.x, vnext.z);
lnp = pointDistance(vnext.x, vnext.z, vprev.x, vprev.z);
lpc = pointDistance(vprev.x, vprev.z, vcur.x, vcur.z);

// Calculate and print the angle
Math.acos((lcn*lcn + lpc*lpc - lnp*lnp)/(2*lcn*lpc))*180/Math.PI

Is there something wrong in the code, did I forget to do something, or should it be done a completely different way?

Broxzier
  • 2,909
  • 17
  • 36
  • 2
    You could use [`Math.atan2()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2) to calculate angles from coordinates, it'll make calculations simpler. You can see a working example [in this fiddle](http://jsfiddle.net/92jWG/6/). – Teemu Aug 01 '13 at 14:40
  • How can a triangle have an angle over 180 degrees? Sum of angles is 180 degrees..... – Jiminion Aug 01 '13 at 14:49
  • 1
    The problem is that `cos(90)` and `cos(270)` are both 0 so when doing acos(0) it will have to choose what to give you and apparently chooses 90 as a preference. One way to fix this maybe to use atan2 as Teemu suggests and work out the angles of each line segment and subtract them. I think if you do it in the right order this should give you the full information (though might need normalising since it could be between -360 and 360). – Chris Aug 01 '13 at 14:53
  • @Jim: This isn't about triangles. Its about two path segments which share an endpoint. The diagram in the question should explain it clearly. – Chris Aug 01 '13 at 14:53
  • I'm thinking the code is prob. right, but you are not interpreting the result correctly, as it is just the angle of the 2D triangle in 3D pace, so it might not be what you are expecting. – Jiminion Aug 01 '13 at 14:55
  • Aren't points colinear if long = short + short2 – Jiminion Aug 01 '13 at 15:00
  • @Teemu: I'll look more into `atan2()`, I've used it before, thanks. @Jim: I'm 100% sure there won't be any situation where `long = short + short`. – Broxzier Aug 01 '13 at 15:03
  • This is nothign to do with 3d space. The problem is that if its a left turn (from the point of view of walking the path from the bottom) the angle should be between 180 and 360 and if its a right turn the angle should be between 0 and 180. – Chris Aug 01 '13 at 15:04
  • @Chris: Indeed. Now if there is an easy way to see if you turn left or right I'd be set. – Broxzier Aug 01 '13 at 15:05
  • So the angle is correct, but just off by +/- 180 sometimes? – Jiminion Aug 01 '13 at 15:19
  • @Jim: Close. Its actually that sometimes the angle he wants is `360-(angle he gets)` – Chris Aug 01 '13 at 15:21
  • @Broxzier: I've got a solution for you now. :) – Chris Aug 01 '13 at 15:25
  • If you are circling around something, you might need to pass in a center point, or else know that prev->curr->next is a particular direction. I think you will need an if; even mathematicians use them.... – Jiminion Aug 01 '13 at 15:26
  • @Chris: Great, thanks alot! I'll try it out as soon as I can! @ Jim: Circling around wont work with complicated shapes, such as the letter 'U' where the center is not always on the left. – Broxzier Aug 01 '13 at 22:21

3 Answers3

4

HI there your math and calculations are perfect. Your running into the same problem most people do on calculators, which is orientation. What I would do is find out if the point lies to the left or right of the vector made by the first two points using this code, which I found from

Determine which side of a line a point lies

isLeft = function(ax,ay,bx,by,cx,cy){
 return ((bx - ax)*(cy - ay) - (by - ay)*(cx - ax)) > 0;
}

Where ax and ay make up your first point bx by your second and cx cy your third.

if it is to the left just add 180 to your angle

Community
  • 1
  • 1
ZJS
  • 3,991
  • 2
  • 15
  • 22
  • 1
    I think you mean `angle = !ifLeft(....) ? 360 - angle : angle;` instead of `angle + 180`. – Broxzier Aug 02 '13 at 06:49
  • I guess so I didn't go through the question fully but figuring out if it is left or right of the line is the key. I hope that bit of code helped good luck! – ZJS Aug 02 '13 at 15:09
1

I've got a working but not necessarily brief example of how this can work:

var point1x = 0, point1y = 0,
    point2x = 10, point2y = 10,
    point3x = 20, point3y = 10,
    point4x = 10, point4y = 20;

var slope1 = Math.atan2(point2y-point1y,point2x-point1x)*180/Math.PI;
var slope2 = Math.atan2(point3y-point2y,point3x-point2x)*180/Math.PI;
var slope3 = Math.atan2(point4y-point3y,point4x-point3x)*180/Math.PI;
alert(slope1);
alert(slope2);
alert(slope3);
var Angle1 = slope1-slope2;
var Angle2 = slope2-slope3;
alert(180-Angle1);
alert(180-Angle2);

(see http://jsfiddle.net/ZUESt/1/)

To explain the multiple steps the slopeN variables are the slopes of the individual line segments. AngleN is the amount turned at each junction (ie point N+1). A positive angle is a right turn and a negative angle a left turn.

You can then subtract this angle from 180 to get the actual interior angle that you want.

It should be noted that this code can of course be compressed and that five lines are merely outputting variables to see what is going on. I'll let you worry about optimizing it for your own use with this being a proof of concept.

Chris
  • 27,210
  • 6
  • 71
  • 92
  • And now I'm wondering if using dot product might be the better way of doing this anyway... – Chris Aug 01 '13 at 15:29
  • 1
    Nope. Dot product has the same problem of effectively just giving you the cosine of the angle which then suffers the same problem as before. – Chris Aug 01 '13 at 15:47
0

You need to check boundary conditions (apparently, if points are colinear) and apply the proper calculation to find the angle. Also, a triangle can't have any (interior) angle greater than 180 degress. Sum of angle of triangle is 180 degrees.

Jiminion
  • 5,080
  • 1
  • 31
  • 54