Java 计算由两点定义的线之间的角度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2676719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:43:28  来源:igfitidea点击:

Calculating the angle between the line defined by two points

javaandroidgraphics

提问by kingrichard2005

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?

我目前正在为 Android 开发一个简单的 2D 游戏。我有一个位于屏幕中央的静止物体,我试图让该物体旋转并指向用户触摸的屏幕区域。我有代表屏幕中心的常量坐标,我可以获得用户点击的点的坐标。我正在使用本论坛中概述的公式: 如何获得两点之间的角度?

  • 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.

  • 它说如下“如果你想要这两个点定义的线和水平轴之间的角度:

    double angle = atan2(y2 - y1, x2 - x1) * 180 / PI;".
    
  • 我实现了这一点,但我认为我在屏幕坐标中工作的事实会导致计算错误,因为 Y 坐标是反向的。我不确定这是否是正确的方法,任何其他想法或建议表示赞赏。

采纳答案by Jim Lewis

Assumptions: xis the horizontal axis, and increases when moving from left to right. yis 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. thetais measured counter-clockwise from the +xaxis. Then:

假设: x是水平轴,从左向右移动时增加。 y是纵轴,从下往上增加。(touch_x, touch_y)是用户选择的点。(center_x, center_y)是屏幕中心的点。 theta+x轴逆时针方向测量。然后:

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,

编辑:您在评论中提到 y 从上到下增加。在这种情况下,

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.

但是将其描述为(touch_x, touch_y)以相对于 的极坐标表示会更正确(center_x, center_y)。正如 ChrisF 所提到的,“两点之间的角度”的想法没有明确定义。

回答by Dave Discoid

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);
}

回答by Golfcabalist

"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?"

“原点在屏幕的左上角,Y 坐标向下增加,而 X 坐标像往常一样向右增加。我想我的问题变成了,我是否必须将屏幕坐标转换为笛卡尔坐标在应用上述公式之前?”

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(其中 x>0 和 y>0)中,则情况将与屏幕像素坐标相同(倒置 Y 的东西除外。如果您否定 Y 使其正面朝上,它变成象限 4 ......)。将屏幕像素坐标转换为笛卡尔坐标并不会真正改变角度。

回答by Tigger

A few answers here have tried to explain the "screen" issue where top leftis 0,0and bottom rightis (positive) screen width, screen height. Most grids have the Yaxis as positive above Xnot below.

这里的一些答案试图解释“屏幕”问题 where top leftis0,0bottom rightis (positive) screen width, screen height。大多数网格的Y轴为正上方X而不是下方。

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

以下方法将使用屏幕值而不是“网格”值。与例外答案的唯一区别是Y值被反转。

/**
 * 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;
}

回答by quang_tran

with pygame:

与 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

它对我有用

回答by j2emanue

in android i did this using kotlin:

在 android 中,我使用 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()))
    }