代码之家  ›  专栏  ›  技术社区  ›  Jaanus

如何计算两条直线之间的正反夹角?

  •  13
  • Jaanus  · 技术社区  · 15 年前

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

    不过,两条线之间的夹角有问题。结果总是积极的。我需要同时检测正角度和负角度,所以如果一条线比另一条线高出15度或低出15度,那么形状显然会有所不同。

    我得到的配置是,一条线保持静止,而另一条线旋转,我需要通过比较它和静止线,了解它旋转的方向。

    编辑:针对斯韦斯特鲁普下面的评论,事实上我只有一行,我记录了它的起始位置。然后直线从起始位置开始旋转,我需要计算从起始位置到当前位置的角度。例如,如果顺时针旋转,则为正旋转;如果逆时针旋转,则为负旋转。(反之亦然。)

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

    6 回复  |  直到 6 年前
        1
  •  8
  •   brainjam    15 年前

    @Duffymo的答案是正确的,但是如果不想实现跨产品,可以使用 atan2 功能。这将返回一个介于-π和π之间的角度,您可以在每一行上使用它(或者更精确地说,在表示行的向量上)。

    如果得到第一条线的角θ(静止线),则必须将第二条线的角φ正规化为θ-π和θ+π之间(通过添加±2π)。两条直线之间的夹角为φ-θ。

        2
  •  19
  •   Pang Ajmal PraveeN    6 年前

    以下是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;
    }
    

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

        3
  •  7
  •   Pang Ajmal PraveeN    6 年前

    这是一个涉及二维向量的简单问题。两向量夹角的正弦值与两向量的叉积有关。“上”或“下”由叉积产生的向量的符号决定:如果你交叉两个向量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();
        }
    }
    

    检查第三个部件的标志。如果是正的,A是“低于”B;如果是负的,A是“高于”B-只要两个向量在Y轴右侧的两个象限中。显然,如果它们都在y轴左边的两个象限中,则相反。

    你需要思考你对“高于”和“低于”的直觉概念。如果A在第一象限(0<=θ<=90),B在第二象限(90<=θ<=180),会怎么样?”“上面”和“下面”失去了意义。

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

    这正是交叉积的作用。逆时针方向第三个分量的符号为正,顺时针方向为负(向下看旋转平面)。

        4
  •  1
  •   swestrup    15 年前

    你可以使用的一个“快速而肮脏”的方法是引入第三条参考线r。因此,给定两条线a和b,计算a和r之间的角度,然后b和r,然后减去它们。

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

        5
  •  1
  •   marikhu    6 年前
    // 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;
    }
    
        6
  •  0
  •   thecoshman    15 年前

    这个功能在雷达上工作

    在一个完整的圆(360度)内有2个天线

    因此,我相信你要找的服装只是返回值-2pi

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

    编辑

    刚刚注意到,函数在返回值时实际将rads转换为度。但同样的原则也会起作用。