如何计算两条线之间的正负角度?

时间:2010-04-18 19:16:15

标签: math geometry 2d

There is a very handy set of 2d geometry utilities here.

尽管如此,angleBetweenLines存在问题。结果总是积极的。我需要检测正角度和负角度,所以如果一条线在另一条线“上方”或“下方”15度,则形状显然看起来不同。

我的配置是一条线保持静止,而另一条线旋转,我需要通过将它与固定线进行比较来了解它旋转的方向。

编辑:为了回应下面的swestrup评论,情况实际上是我有一条线,我记录了它的起始位置。然后该线从其起始位置旋转,我需要计算从其起始位置到当前位置的角度。例如,如果它顺时针旋转,则为正旋转;如果逆时针,则为负。 (反之亦然。)

如何改进算法,使其根据线的位置返回正负角度?

6 个答案:

答案 0 :(得分:20)

这是brainjam建议的实现。 (它符合我的约束,线条之间的差异保证足够小,不需要对任何东西进行标准化。)

CGFloat angleBetweenLinesInRad(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
    CGFloat a = line1End.x - line1Start.x;
    CGFloat b = line1End.y - line1Start.y;
    CGFloat c = line2End.x - line2Start.x;
    CGFloat d = line2End.y - line2Start.y;

    CGFloat atanA = atan2(a, b);
    CGFloat atanB = atan2(c, d);

    return atanA - atanB;
}

我喜欢它简洁明了。矢量版本会更简洁吗?

答案 1 :(得分:8)

@ duffymo的回答是正确的,但如果您不想实现跨产品,则可以使用atan2功能。这将返回-π和π之间的角度,您可以在每条线上使用它(或者更准确地说是表示线条的矢量)。

如果你得到第一个(固定线)的角度θ,你必须将第二条线的角度φ标准化,使其在θ-π和θ+π之间(加上±2π)。那么两条线之间的角度将是φ-θ。

答案 2 :(得分:7)

这是一个涉及2D矢量的简单问题。两个矢量之间的角度的正弦值与两个矢量之间的交叉积相关。并且“上方”或“下方”由交叉乘积产生的向量的符号确定:如果跨越两个向量A和B,并且产生的交叉积为正,则A“低于”B;如果它是否定的,则A“高于”B.有关详细信息,请参阅Mathworld

以下是我在Java中编写代码的方法:

package cruft;

import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * VectorUtils
 * User: Michael
 * Date: Apr 18, 2010
 * Time: 4:12:45 PM
 */
public class VectorUtils
{
    private static final int DEFAULT_DIMENSIONS = 3;
    private static final NumberFormat DEFAULT_FORMAT = new DecimalFormat("0.###");

    public static void main(String[] args)
    {
        double [] a = { 1.0, 0.0, 0.0 };
        double [] b = { 0.0, 1.0, 0.0 };

        double [] c = VectorUtils.crossProduct(a, b);

        System.out.println(VectorUtils.toString(c));
    }

    public static double [] crossProduct(double [] a, double [] b)
    {
        assert ((a != null) && (a.length >= DEFAULT_DIMENSIONS ) && (b != null) && (b.length >= DEFAULT_DIMENSIONS));

        double [] c = new double[DEFAULT_DIMENSIONS];

        c[0] = +a[1]*b[2] - a[2]*b[1];
        c[1] = +a[2]*b[0] - a[0]*b[2];
        c[2] = +a[0]*b[1] - a[1]*b[0];

        return c;
    }

    public static String toString(double [] a)
    {
        StringBuilder builder = new StringBuilder(128);

        builder.append("{ ");

        for (double c : a)
        {
            builder.append(DEFAULT_FORMAT.format(c)).append(' ');
        }

        builder.append("}");

        return builder.toString();
    }
}

检查第3个组件的符号。如果是正数,A是“低于”B;如果它是负的,A是“高于”B - 只要两个矢量位于y轴右侧的两个象限中。显然,如果它们都位于y轴左侧的两个象限中,则反之亦然。

您需要考虑“上方”和“下方”的直观概念。如果A在第一象限(0 <=θ<= 90)并且B在第二象限(90 <=θ<= 180)中怎么办? “上方”和“下方”失去了意义。

  

然后线从它旋转   起始位置,我需要   从起始角度计算角度   位置到当前位置。例如,如果   它顺时针旋转,它是   正旋转;如果   逆时针,然后是负面的。 (要么   反之亦然。)

这正是跨产品的用途。第三个分量的符号对于逆时针是正的而对于顺时针是负的(当你向下看旋转的平面时)。

答案 3 :(得分:1)

您可以使用的一种“快速且脏”的方法是引入第三条参考线R.因此,给定两条线A和B,计算A和R之间的角度,然后计算B和R,并减去它们。

这大约是实际需要的计算量的两倍,但很容易解释和调试。

答案 4 :(得分:1)

// Considering two vectors CA and BA
// Computing angle from CA to BA
// Thanks to code shared by Jaanus, but atan2(y,x) is used wrongly.

float getAngleBetweenVectorsWithSignInDeg(Point2f C, Point2f A, Point2f B)
{      
    float a = A.x - C.x;
    float b = A.y - C.y;
    float c = B.x - C.x;
    float d = B.y - C.y;

    float angleA = atan2(b, a);
    float angleB = atan2(d, c);
    cout << "angleA: " << angleA << "rad, " << angleA * 180 / M_PI << " deg" << endl;
    cout << "angleB: " << angleB << "rad, " << angleB * 180 / M_PI << " deg" << endl;
    float rotationAngleRad = angleB - angleA;
    float thetaDeg = rotationAngleRad * 180.0f / M_PI;
    return thetaDeg;
}

答案 5 :(得分:0)

该功能在RADS中起作用

整圈(360度)有2pi RADS

因此,我相信您正在寻找的answear只是返回值 - 2pi

如果您要求让一个函数同时返回两个值,那么您要求中断语言,函数只能返回单个值。您可以传递两个指针,它可以用来设置值,以便在结束后您的程序可以继续工作后更改可以保留。但这并不是解决这个问题的明智方法。

修改

注意到该函数在返回值时实际将Rads转换为Degrees。但同样的原则也会奏效。