Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 234
Broxzier Avatar asked Aug 01 '13 14:08

Broxzier


People also ask

How do you find the sides of an angle?

If you have the hypotenuse, multiply it by sin(θ) to get the length of the side opposite to the angle. Alternatively, multiply the hypotenuse by cos(θ) to get the side adjacent to the angle. If you have the non-hypotenuse side adjacent to the angle, divide it by cos(θ) to get the length of the hypotenuse.


2 Answers

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

like image 183
ZJS Avatar answered Sep 28 '22 13:09

ZJS


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.

like image 44
Chris Avatar answered Sep 28 '22 11:09

Chris