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

Java 计算由两点定义的直线之间的角度,java,android,graphics,Java,Android,Graphics,我目前正在为Android开发一个简单的2D游戏。我有一个固定的物体,它位于屏幕的中心,我试图让这个物体旋转,指向屏幕上用户触摸的区域。我有代表屏幕中心的恒定坐标,我可以得到用户点击的点的坐标。我正在使用本论坛中概述的公式: 它的意思是“如果你想知道由这两点定义的直线与水平轴之间的角度: double angle = atan2(y2 - y1, x2 - x1) * 180 / PI;". 我实现了这一点,但我认为我在屏幕坐标中工作这一事实导致了计算错误,因为Y坐标是反向的。我不确定这是

我目前正在为Android开发一个简单的2D游戏。我有一个固定的物体,它位于屏幕的中心,我试图让这个物体旋转,指向屏幕上用户触摸的区域。我有代表屏幕中心的恒定坐标,我可以得到用户点击的点的坐标。我正在使用本论坛中概述的公式:

  • 它的意思是“如果你想知道由这两点定义的直线与水平轴之间的角度:

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


假设:
x
是水平轴,从左向右移动时增加。
y
是垂直轴,从下到上递增。
(touch\u x,touch\u y)
是垂直轴 用户选择的点。
(center\u x,center\u y)
是该点的中心 屏幕。
θ
+x
轴逆时针测量。然后:

delta_x = touch_x - center_x
delta_y = touch_y - center_y
theta_radians = atan2(delta_y, delta_x)
编辑:您在评论中提到y从上到下递增。在 案例

但将其描述为表达
(touch\u x,touch\u y)
在相对于
(中心x,中心y)
的极坐标中。如ChrisF所述,
“两点之间的角度”的概念没有很好的定义。

我自己也需要类似的功能,所以在拉了很多头发之后,我想出了下面的功能

/**
 * 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);
}
/**
*获取相对于屏幕中心点的角度
*其中3点为0,12点为270度
* 
*@param屏幕点
*@返回角度,以0-360度为单位。
*/
公共双getAngle(点屏幕点){
double dx=screenPoint.getX()-mCentreX;
//减去以更正坐标重新映射
double dy=-(screenPoint.getY()-mCentreY);
双inRads=数学atan2(dy,dx);
//当0度在3点,270度在12点时,我们需要映射到坐标系
如果(印度卢比<0)
inRads=数学绝对值(inRads);
其他的
inRads=2*Math.PI-inRads;
返回数学toDegrees(印度卢比);
}
“原点位于屏幕的左上角,Y坐标向下增加,而X坐标向右增加,与正常情况相同。我想我的问题是,在应用上述公式之前,我是否必须将屏幕坐标转换为笛卡尔坐标?”


如果您使用笛卡尔坐标计算角度,并且两个点都在象限1(其中x>0和y>0),则情况将与屏幕像素坐标相同(除了颠倒的y坐标。如果您将y取反以使其正面朝上,则它将成为象限4…)。将屏幕像素坐标转换为笛卡尔坐标并不会真正改变角度。

这里的一些答案试图解释“屏幕”问题,其中左上方的
0,0,右下方的
为(正)
screen width,screen height
。大多数网格的
Y
轴为正上方
X
下方

以下方法将使用屏幕值而不是“网格”值。与例外答案的唯一区别是
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;
}
/**
*逆时针计算x水平绕组的角度
*在屏幕空间。
* 
*从下面返回的值应为315。
* 
*x,y-------------
*     |  1,1
*     |    \
*     |     \
*     |     2,2
* 
*@param p1
*@param p2
*@return-从0到360的双精度
*/
公共静态双角度(点F p1,点F p2){
//注意:请记住,大多数数学的Y轴在X轴上方为正。
//但是,对于屏幕,下面的Y为正值。因此,
//将Y值反转以获得预期结果。
最终双三角=(p1.y-p2.y);
最终双deltaX=(p2.x-p1.x);
最终双重结果=数学toDegrees(数学atan2(deltaY,deltaX));
返回(结果<0)-(360d+结果):结果;
}
使用pygame:

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()))
    }
dy=p1.y-p2.y
dX=p2.x-p1.x
rads=atan2(dy,dx)
degs=度(rads)
如果degs<0:
degs+=90

在安卓系统中,它对我有用我使用kotlin实现了这一点:

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()
}
此函数将返回如下值

如果我们将
+180
转换为返回值,那么我们将从从右向左获得该值

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


与我们绘制圆弧时的角度类似,从技术上讲,你无法得到两点之间的角度。但是你可以得到两个向量之间的角度。他很确定的意思是“两点之间绘制的直线与水平轴之间的角度”“对不起,让我重新表述一下我的标题,我如何获得由这两点定义的直线与在屏幕中心穿过我的对象的水平通道之间的角度??谢谢Jim,无论我使用的是笛卡尔坐标还是极坐标,我怎么知道计算出的角度是否基于穿过我的对象的水平线,而不是屏幕顶部的水平线,该水平线来自左上角的原点???@Kinghrichard2005:因为delta_x和delta_y是根据中心点(对象的位置)计算的,θ也将根据同一点计算。如果y从上到下增加,该值将如何变化?@Scorb在y从上到下或从下到上增加之间的变化相当于在delta_y计算中翻转符号。对于自上而下,您将使用我在编辑中添加的公式;这个答案产生了
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()))
    }
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()
}