64

I'm currently developing a simple 2D game for Android. I have a stationary object that's situated in the center of the screen and I'm trying to get that object to rotate and point to the area on the screen that the user touches. I have the constant coordinates that represent the center of the screen and I can get the coordinates of the point that the user taps on. I'm using the formula outlined in this forum: How to get angle between two points?

  • It says as follows "If you want the the angle between the line defined by these two points and the horizontal axis:

    double angle = atan2(y2 - y1, x2 - x1) * 180 / PI;".
    
  • I implemented this, but I think the fact the I'm working in screen coordinates is causing a miscalculation, since the Y-coordinate is reversed. I'm not sure if this is the right way to go about it, any other thoughts or suggestions are appreciated.

Laurel
  • 5,965
  • 14
  • 31
  • 57
kingrichard2005
  • 7,179
  • 21
  • 86
  • 118
  • 6
    Technically you can't get the angle between two *points*. You can get the angle between two *vectors* though. – ChrisF Apr 20 '10 at 16:13
  • 22
    Pretty sure he means "the angle between a line drawn between two points, and the horizontal axis" – Jason Hall Apr 20 '10 at 16:23
  • 2
    I'm sorry, let me rephrase my title, how do I get the angle between the line defined by these two points and the horizontal access that cuts through my object at the center of the screen?? – kingrichard2005 Apr 20 '10 at 16:24

8 Answers8

101

Assumptions: x is the horizontal axis, and increases when moving from left to right. y is the vertical axis, and increases from bottom to top. (touch_x, touch_y) is the point selected by the user. (center_x, center_y) is the point at the center of the screen. theta is measured counter-clockwise from the +x axis. Then:

delta_x = touch_x - center_x
delta_y = touch_y - center_y
theta_radians = atan2(delta_y, delta_x)

Edit: you mentioned in a comment that y increases from top to bottom. In that case,

delta_y = center_y - touch_y

But it would be more correct to describe this as expressing (touch_x, touch_y) in polar coordinates relative to (center_x, center_y). As ChrisF mentioned, the idea of taking an "angle between two points" is not well defined.

Jim Lewis
  • 43,505
  • 7
  • 82
  • 96
  • Thanks Jim, whether I'm using Cartesian or polar coordinates, how would I know if the calculated angle is based on the horizontal line that cuts through my object and not the horizontal at the top of the screen, which is coming out of the origin at the top-left?? – kingrichard2005 Apr 20 '10 at 16:50
  • 1
    @kingrichard2005: Since delta_x and delta_y are calculated with respect to the center point (the location of your object), theta will also be calculated with respect to the same point. – Jim Lewis Apr 20 '10 at 17:05
  • How would this change if y increases from top to bottom? – Scorb Mar 18 '21 at 18:08
  • @Scorb Changing between Y increasing top-to-bottom, or bottom-to-top, amounts to flipping the sign in the delta_y calculation. For top-to-bottom you would use the formula I added in the edit. – Jim Lewis Mar 18 '21 at 22:38
33

Had a need for similar functionality myself, so after much hair pulling I came up with the function below

/**
 * Fetches angle relative to screen centre point
 * where 3 O'Clock is 0 and 12 O'Clock is 270 degrees
 * 
 * @param screenPoint
 * @return angle in degress from 0-360.
 */
public double getAngle(Point screenPoint) {
    double dx = screenPoint.getX() - mCentreX;
    // Minus to correct for coord re-mapping
    double dy = -(screenPoint.getY() - mCentreY);

    double inRads = Math.atan2(dy, dx);

    // We need to map to coord system when 0 degree is at 3 O'clock, 270 at 12 O'clock
    if (inRads < 0)
        inRads = Math.abs(inRads);
    else
        inRads = 2 * Math.PI - inRads;

    return Math.toDegrees(inRads);
}
Jeffmagma
  • 452
  • 2
  • 8
  • 20
Dave Discoid
  • 331
  • 3
  • 2
27

A few answers here have tried to explain the "screen" issue where top left is 0,0 and bottom right is (positive) screen width, screen height. Most grids have the Y axis as positive above X not below.

The following method will work with screen values instead of "grid" values. The only difference to the excepted answer is the Y values are inverted.

/**
 * Work out the angle from the x horizontal winding anti-clockwise 
 * in screen space. 
 * 
 * The value returned from the following should be 315. 
 * <pre>
 * x,y -------------
 *     |  1,1
 *     |    \
 *     |     \
 *     |     2,2
 * </pre>
 * @param p1
 * @param p2
 * @return - a double from 0 to 360
 */
public static double angleOf(PointF p1, PointF p2) {
    // NOTE: Remember that most math has the Y axis as positive above the X.
    // However, for screens we have Y as positive below. For this reason, 
    // the Y values are inverted to get the expected results.
    final double deltaY = (p1.y - p2.y);
    final double deltaX = (p2.x - p1.x);
    final double result = Math.toDegrees(Math.atan2(deltaY, deltaX)); 
    return (result < 0) ? (360d + result) : result;
}
Tigger
  • 8,980
  • 5
  • 36
  • 40
  • 1
    Just ran a test with different values for x and y; and this answer produces the accurate results for the screen coordinates. – Velusamy Velu Apr 03 '18 at 22:38
  • Why do you add 360d to the result if the result <0? How can result be < then 0? –  May 28 '19 at 18:36
  • 1
    @Mike : In the sample code, the result would be `-45` instead of `315` without the correction for a negative. Both values are correct, but from memory I only wanted positive values and `0`. – Tigger May 28 '19 at 23:54
  • @Tigger ok I see now :) Thank you –  May 29 '19 at 16:49
2

in android i did this using kotlin:

private fun angleBetweenPoints(a: PointF, b: PointF): Double {
        val deltaY = abs(b.y - a.y)
        val deltaX = abs(b.x - a.x)
        return Math.toDegrees(atan2(deltaY.toDouble(), deltaX.toDouble()))
    }
j2emanue
  • 60,549
  • 65
  • 286
  • 456
2
fun calculateAngle(
    touchX: Float,
    touchY: Float,
    centerX: Float,
    centerY: Float
): Float {
    val deltaX = centerX - touchX
    val deltaY = centerY - touchY
    return Math.toDegrees(atan2(deltaY.toDouble(), deltaX.toDouble())).toFloat()
}

This function will return value like

If we + 180 to the return value then we will get the value from right to left like

360(<=> 0) -> 45 -> 90 -> 135 -> 180 -> 225 -> 270 -> 315

similar to the angle when we drawArc

Linh
  • 57,942
  • 23
  • 262
  • 279
  • Some JavaScript implementation: `function calcAngle(ax, ay, bx, by){var dx = bx - ax; var dy = by - ay; return Math.atan2(dy, dx) * 180 / Math.PI;}` – Rodrigo Polo Dec 02 '21 at 11:29
1

"the origin is at the top-left of the screen and the Y-Coordinate increases going down, while the X-Coordinate increases to the right like normal. I guess my question becomes, do I have to convert the screen coordinates to Cartesian coordinates before applying the above formula?"

If you were calculating the angle using Cartesian coordinates, and both points were in quadrant 1 (where x>0 and y>0), the situation would be identical to screen pixel coordinates (except for the upside-down-Y thing. If you negate Y to get it right-side up, it becomes quadrant 4...). Converting screen pixel coordinates to Cartesian doesnt really change the angle.

1

with pygame:

dy = p1.y - p2.y
dX = p2.x - p1.x

rads = atan2(dy,dx)
degs = degrees(rads)
if degs < 0 :
   degs +=90

it work for me

quang_tran
  • 19
  • 1
  • 2
0

If you wanto get slope as radian -> m = math.atan2((jy-iy),(jx-ix))

and degree ---> m = math.atan2((jy-iy),(jx-ix))*180/math.pi