Broxzier
Broxzier

Reputation: 2949

Incorrect angle, wrong side calculated

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?

Upvotes: 7

Views: 478

Answers (3)

ZJS
ZJS

Reputation: 4051

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

Upvotes: 4

Chris
Chris

Reputation: 27627

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.

Upvotes: 1

Jiminion
Jiminion

Reputation: 5168

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.

Upvotes: 0

Related Questions