56

I need to calculate the angle in degrees between two points for my own Point class, Point a shall be the center point.

Method:

public float getAngle(Point target) {
    return (float) Math.toDegrees(Math.atan2(target.x - x, target.y - y));
}

Test 1: // returns 45

Point a = new Point(0, 0);
    System.out.println(a.getAngle(new Point(1, 1)));

Test 2: // returns -90, expected: 270

Point a = new Point(0, 0);
    System.out.println(a.getAngle(new Point(-1, 0)));

How can i convert the returned result into a number between 0 and 359?

Aich
  • 962
  • 2
  • 9
  • 19
  • 9
    Just noticed that atan2 arguments are in the reverse order: must be atan2(y, x) – alexm Apr 02 '12 at 03:01
  • I don't know what goes wrong, but my order works fine, the reverse order wouldn't. – Aich Apr 02 '12 at 03:09
  • 2
    You can only measure the angle between three points. You might assume `(0, 0)` or `(min(x1,x2), min(y1,y2))` is one of the points but you can't draw an angle between two points. – Peter Lawrey Apr 02 '12 at 07:16
  • @Peter Lawrey Point a represents the center. So I mean the angle of the center point and the line a to new Point(x, y). Or doesn't make this sense? – Aich Apr 02 '12 at 16:37
  • So you mean the angle between line `(0, 0)` to `(1, 0)` and `(0,0)` to `(x, y)` ? – Peter Lawrey Apr 02 '12 at 19:18

9 Answers9

96

you could add the following:

public float getAngle(Point target) {
    float angle = (float) Math.toDegrees(Math.atan2(target.y - y, target.x - x));

    if(angle < 0){
        angle += 360;
    }

    return angle;
}

by the way, why do you want to not use a double here?

John Ericksen
  • 10,995
  • 4
  • 45
  • 75
  • 1
    Since the angle would only ever be below 0 for an instant, I see no reason to use a while loop here. – Makoto Apr 02 '12 at 02:43
  • I call this method hundreds of time in a game frame. I guess I have a little more performance with less precision and I also don't need the additional digits. – Aich Apr 02 '12 at 02:52
  • Oh, and if you are dealing with these numbers in a game -90 and 270 will be treated exactly the same in terms of delta direction. – John Ericksen Apr 02 '12 at 02:56
  • You don't have to check whether it's >= 360. This can never happen, since atan2 returns a value between +/- pi/2. – Dawood ibn Kareem Apr 02 '12 at 03:00
  • @johncarl I write a game engine and i want users of the game engine give the ability to easily display the point angle. The user shouldnt bother about converting the returned value. – Aich Apr 02 '12 at 03:19
  • @David Wallace: Good point, Ive modified the code. Aichi: Fair enough, good luck with your engine! – John Ericksen Apr 02 '12 at 03:27
  • 3
    @johncarl should be tan2(deltay/deltax) – mihajlv Sep 14 '13 at 15:26
  • If the value of x or y is greater than the value of centerX, centerY, the value of the opposite sign is given (- -> +) – 최봉재 Jan 15 '18 at 05:20
  • I'd suggest return (angle + 360) % 360; – Joeri Apr 29 '18 at 13:02
32

I started with johncarls solution, but needed to adjust it to get exactly what I needed. Mainly, I needed it to rotate clockwise when the angle increased. I also needed 0 degrees to point NORTH. His solution got me close, but I decided to post my solution as well in case it helps anyone else.

I've added some additional comments to help explain my understanding of the function in case you need to make simple modifications.

/**
 * Calculates the angle from centerPt to targetPt in degrees.
 * The return should range from [0,360), rotating CLOCKWISE, 
 * 0 and 360 degrees represents NORTH,
 * 90 degrees represents EAST, etc...
 *
 * Assumes all points are in the same coordinate space.  If they are not, 
 * you will need to call SwingUtilities.convertPointToScreen or equivalent 
 * on all arguments before passing them  to this function.
 *
 * @param centerPt   Point we are rotating around.
 * @param targetPt   Point we want to calcuate the angle to.  
 * @return angle in degrees.  This is the angle from centerPt to targetPt.
 */
public static double calcRotationAngleInDegrees(Point centerPt, Point targetPt)
{
    // calculate the angle theta from the deltaY and deltaX values
    // (atan2 returns radians values from [-PI,PI])
    // 0 currently points EAST.  
    // NOTE: By preserving Y and X param order to atan2,  we are expecting 
    // a CLOCKWISE angle direction.  
    double theta = Math.atan2(targetPt.y - centerPt.y, targetPt.x - centerPt.x);

    // rotate the theta angle clockwise by 90 degrees 
    // (this makes 0 point NORTH)
    // NOTE: adding to an angle rotates it clockwise.  
    // subtracting would rotate it counter-clockwise
    theta += Math.PI/2.0;

    // convert from radians to degrees
    // this will give you an angle from [0->270],[-180,0]
    double angle = Math.toDegrees(theta);

    // convert to positive range [0-360)
    // since we want to prevent negative angles, adjust them now.
    // we can assume that atan2 will not return a negative value
    // greater than one partial rotation
    if (angle < 0) {
        angle += 360;
    }

    return angle;
}
Joseph Snow
  • 2,436
  • 1
  • 21
  • 22
  • 2
    Instead of adding Pi/2 (an inexact approximation) to the radian value before converting it to degrees, would make more sense to add 90 after it is converted to degrees. –  Nov 19 '13 at 05:00
  • Here I'd also suggest return (angle + 360) % 360; – Joeri Apr 29 '18 at 13:03
3

Based on Saad Ahmed's answer, here is a method that can be used for any two points.

public static double calculateAngle(double x1, double y1, double x2, double y2)
{
    double angle = Math.toDegrees(Math.atan2(x2 - x1, y2 - y1));
    // Keep angle between 0 and 360
    angle = angle + Math.ceil( -angle / 360 ) * 360;

    return angle;
}
Stevoisiak
  • 23,794
  • 27
  • 122
  • 225
1

If you want the "bearing" degrees from north, so:

Direction Degees
North 0
North East 45
East 90
South East 135
South 180
South West -135
West -95
North West -45

you can do this:

public static final double RAD_360_DEG = Math.PI * 360d / 180d;
public static final double RAD_180_DEG = Math.PI * 180d / 180d;
public static final double RAD_90_DEG = Math.PI * 90d / 180d;

/**
 * @return The angle from north from p1 to p2.  Returns (in radians) -180 to 180, with 0 as north.
 */
public static double getAngleBearing(double p1x, double p1y, double p2x, double p2y) {
    double result = Math.atan2(p2y - p1y, p2x - p1x) + RAD_90_DEG;
    
    if (result > RAD_180_DEG) {
        result = result - RAD_360_DEG;
    }
    
    return result;
}

double bearingAngle = Math.toDegrees(getAngleBearing(...));
Craigo
  • 3,384
  • 30
  • 22
1

The javadoc for Math.atan(double) is pretty clear that the returning value can range from -pi/2 to pi/2. So you need to compensate for that return value.

Spencer Kormos
  • 8,381
  • 3
  • 28
  • 45
0
angle = Math.toDegrees(Math.atan2(target.x - x, target.y - y));

now for orientation of circular values to keep angle between 0 and 359 can be:

angle = angle + Math.ceil( -angle / 360 ) * 360
Saad Ahmed
  • 1,077
  • 9
  • 9
  • I think it should be Math.atan2(target.y - y, target.x - x), so the y components first instead of the x components. – VollNoob Apr 19 '18 at 14:13
0

Why is everyone complicating this?

The only problem is Math.atan2( x , y)

The corret answer is Math.atan2( y, x)

All they did was mix the variable order for Atan2 causing it to reverse the degree of rotation.

All you had to do was look up the syntax https://www.google.com/amp/s/www.geeksforgeeks.org/java-lang-math-atan2-java/amp/

0

my realization:

private double searchAngle(Point posOne, Point posTwo) {
  int sumPos = (posOne.x * posTwo.x) + (posOne.y * posTwo.y);
  double moduleOne = Math.sqrt( (posOne.x * posOne.x) + (posOne.y * posOne.y) );
  double moduleTwo = Math.sqrt( (posTwo.x * posTwo.x) + (posTwo.y * posTwo.y) );

  return Math.toDegrees( Math.acos( sumPos / (Math.abs( moduleOne ) * Math.abs( moduleTwo )) ) );
}

Input:

  • posOne: (x = 50, y = 43)
  • posTwo: (x = 12, y = 42)

Output is

  • 33.35907305958513

in degrees.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Madman
  • 53
  • 1
  • 10
-2

What about something like :

angle = angle % 360;
Rudi Kershaw
  • 12,332
  • 7
  • 52
  • 77
Julian
  • 13
  • 1