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

两条线之间的内角

  •  12
  • osanchezmon  · 技术社区  · 15 年前

    我有两行:第一行和第二行。每行由两个点定义 (P1L1(x1, y1), P2L1(x2, y2) P1L1(x1, y1), P2L3(x2, y3)) . 我想知道这两条线定义的内角。

    为此,我用横坐标计算每行的角度:

    double theta1 = atan(m1) * (180.0 / PI);
    double theta2 = atan(m2) * (180.0 / PI);
    

    在知道角度后,我计算如下:

    double angle = abs(theta2 - theta1);
    

    我的问题或怀疑是:有时我得到了正确的角度,但有时我得到了互补的角度(对我来说是外部的)。我怎么知道什么时候减去 180º 知道内角吗?有更好的算法吗?因为我尝试了一些方法:点积法, 以下公式:

    result = (m1 - m2) / (1.0 + (m1 * m2));
    

    但我总是有同样的问题,我从来不知道什么时候我有外角或内角!

    8 回复  |  直到 8 年前
        1
  •  19
  •   andand    15 年前

    我想你要找的是 inner product (您也可以查看 dot product 两个角的入口)。在你的例子中,这是由以下给出的:

    float dx21 = x2-x1;
    float dx31 = x3-x1;
    float dy21 = y2-y1;
    float dy31 = y3-y1;
    float m12 = sqrt( dx21*dx21 + dy21*dy21 );
    float m13 = sqrt( dx31*dx31 + dy31*dy31 );
    float theta = acos( (dx21*dx31 + dy21*dy31) / (m12 * m13) );

    答案是以弧度表示的。

    编辑: 这是一个完整的实现。替换p1、p2和p3中的问题值,让我知道你得到了什么。根据两条线的定义,点p1是两条线相交的顶点。

    #include <math.h>
    #include <iostream>
    
    template <typename T> class Vector2D
    {
    private:
        T x;
        T y;
    
    public:
        explicit Vector2D(const T& x=0, const T& y=0) : x(x), y(y) {}
        Vector2D(const Vector2D&ltT>& src) : x(src.x), y(src.y) {}
        virtual ~Vector2D() {}
    
        // Accessors
        inline T X() const { return x; }
        inline T Y() const { return y; }
        inline T X(const T& x) { this->x = x; }
        inline T Y(const T& y) { this->y = y; }
    
        // Vector arithmetic
        inline Vector2D<T> operator-() const
            { return Vector2D<T>(-x, -y); }
    
        inline Vector2D<T> operator+() const
            { return Vector2D<T>(+x, +y); }
    
        inline Vector2D<T> operator+(const Vector2D<T>& v) const
            { return Vector2D<T>(x+v.x, y+v.y); }
    
        inline Vector2D<T> operator-(const Vector2D<T>& v) const
            { return Vector2D<T>(x-v.x, y-v.y); }
    
        inline Vector2D<T> operator*(const T& s) const
            { return Vector2D<T>(x*s, y*s); }
    
        // Dot product
        inline T operator*(const Vector2D<T>& v) const
            { return x*v.x + y*v.y; }
    
        // l-2 norm
        inline T norm() const { return sqrt(x*x + y*y); }
    
        // inner angle (radians)
        static T angle(const Vector2D<T>& v1, const Vector2D<T>& v2)
        {
            return acos( (v1 * v2) / (v1.norm() * v2.norm()) );
        }
    };
    
    int main()
    {
        Vector2D<double> p1(215, 294);
        Vector2D<double> p2(174, 228);
        Vector2D<double> p3(303, 294);
    
        double rad = Vector2D<double>::angle(p2-p1, p3-p1);
        double deg = rad * 180.0 / M_PI;
    
        std::cout << "rad = " << rad << "\tdeg = " << deg << std::endl;
    
        p1 = Vector2D<double>(153, 457);
        p2 = Vector2D<double>(19, 457);
        p3 = Vector2D<double>(15, 470);
    
        rad = Vector2D<double>::angle(p2-p1, p3-p1);
        deg = rad * 180.0 / M_PI;
    
        std::cout << "rad = " << rad << "\tdeg = " << deg << std::endl;
    
        return 0;
    }

    上面的代码产生:

    rad = 2.12667   deg = 121.849
    rad = 0.0939257 deg = 5.38155
        2
  •  4
  •   thyrgle    15 年前
    if (result > 180)
    {
         result = 360 - result;
    }
    

    这样,它就会一直是内角。在得到结果后再添加。

        3
  •  3
  •   msmq    10 年前

    如果您希望在0度到360度之间的角度,请使用以下代码;它经过全面测试并具有功能性:

    static inline CGFloat angleBetweenLinesInRadians(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
    double angle1 = atan2(line1Start.y-line1End.y, line1Start.x-line1End.x);
    double angle2 = atan2(line2Start.y-line2End.y, line2Start.x-line2End.x);
    double result = (angle2-angle1) * 180 / 3.14;
    if (result<0) {
        result+=360;
    }
    return result;
    

    }

    注: 顺时针旋转;

        4
  •  1
  •   Jorg B Jorge    15 年前

    两个矢量之间的内角(v1,v2)=arc cos(内积(v1,v2)/(模件(v1)*模件(v2))。

    其中内积(v1,v2)=xv1*xv2+yv1*yv2

    模块(V)=sqrt(电源(XV,2)+电源(YV,2))

    因此,您问题的答案在以下示例中实现:

    #define PI   3.14159258
    
    int main()
    {
        double x1,y1,x2,y2,y3;
        double m1, m2;
        double mod1, mod2, innerp, angle;
    
        cout << "x1 :";
        cin >> x1;
        cout << "y1 :";
        cin >> y1;
        cout << "x2 :";
        cin >> x2;
        cout << "y2 :";
        cin >> y2;
        cout << "y3 :";
        cin >> y3;
    
        m1 = atan((y2-y1)/(x2-x1)) * 180 / PI;
        m2 = atan((y3-y1)/(x2-x1)) * 180 / PI;
    
        mod1   = sqrt(pow(y2-y1,2)+pow(x2-x1,2));
        mod2   = sqrt(pow(y3-y1,2)+pow(x2-x1,2));
        innerp = (x2-x1)*(x2-x1) + (y2-y1)*(y3-y1);
        angle  = acos(innerp / (mod1 * mod2)) * 180 / PI;
    
        cout << "m1 : " << m1 << endl;
        cout << "m2 : " << m2 << endl;
        cout << "angle : " << angle << endl;
    }
    
        5
  •  1
  •   rewritten    15 年前

    整个问题比给出的答案容易得多:

    当使用atan(slope)时,会丢失(字面上)一位信息,即范围(0..2*pi)中正好有两个角度(theta)和(theta+pi),它们为函数tan()提供了相同的值。

    只需使用atan2(deltax,deltay)就可以得到正确的角度。例如

    atan2(1,1) == PI/4
    atan2(-1,-1) == 5*PI/4
    

    然后减去,取绝对值,如果大于pi,则减去2*pi。

        6
  •  1
  •   Steve    14 年前

    如果你使用绝对值,你总是会得到锐角。这是正切θ=m1-m2的abs值(1+m1*m2)。如果你取反切线,你的答案将以弧度或度数表示,不管计算器是怎样设置的。对不起,这不是编程术语,我是数学老师,不是程序员…

        7
  •  0
  •   Donnie    15 年前

    得到外角和内角完全取决于减法的顺序(想想看)。你需要从较大的θ中减去较小的θ,这样才能可靠地得到内角。您可能还想使用 atan2 函数,因为您需要的数据类型。

        8
  •  0
  •   Blessed Geek    15 年前

    我希望我能正确理解你的问题,因为我想要的是锐角,而不是两条直线相交的钝角。我说的对吗?

    相交点的锐角和钝角互为180度角。即

     acute + obtuse = PI.
    

    http://www.mathworks.com/access/helpdesk/help/techdoc/ref/atan.html 证明了at an在+/-pi/2处是渐近的。

    因此,无论您是否使用 +/- 符号或正数 0 to pi 梯度符号。

    考虑以下伪代码:

    acuteAngle(m1, m2){
      a = atan(m1) - atan(m2);
    
      // if obtuse get the complementary acute angle:
      if (a>PI/2) 
        a = PI - a;
      return a;
    } 
    

    函数 acuteAngle 从数学上说明你需要做什么。

    但是,它不能用于pi/2附近的角度值,因为角度与该附近结果的二元比较是否表示钝角或锐角值得怀疑。

    因此,我们必须比较两条直线的点的坐标。 我们发现第三行是否由 [(x2,y2)(x3,y3)] 比假设的斜边短、等于或长。

    根据毕达哥拉斯定理, 如果角度正好是pi/2或90度,就会形成斜边。让我们称其假设的斜边线为l3hypo。

    通过你头脑中的几何可视化,

    • 如果第3行长于 L3Hypo,角度是钝角。
    • 如果短一点,角度是锐利的。
    • 否则,完美90。

    因此,

    L1.lengthSquared = sq(x2-x1) + sq(y2-y1)
    L2.lengthSquared = sq(x3-x1) + sq(y3-y1)
    L3Hypo.lengthSquared = L1.lengthSquared + L2.lengthSquared
    L3.lengthSquared = sq(x3-x2) + sq(y3-y2)
    

    因此,下面的伪代码,

    struct Point{
      double x, y;
    }
    
    // no need to struct, for clarity only
    struct Line{
      double lengthSquared;
    }
    
    #define sq(n) (n*n)
    int isObtuse(Point P1, P2, P3){
      Line L1, L2, L3, L3Hypo;
    
      L1.lengthSquared = sq(P2.x-P1.x) + sq(P2.y-P1.y);
      L2.lengthSquared = sq(P3.x-P1.x) + sq(P3.y-P1.y);
      L3Hypo.lengthSquared = L1.lengthSquared + L2.lengthSquared;
      L3.lengthSquared = sq(P3.x-P2.x) + sq(P3.y-P2.y);
    
      if (L3>L3Hypo) return 1; //obtuse
      else if (L3<L3Hypo) return -1; //acute
      else return 0;
    }
    

    假设你已经有了这个功能 GetGradient(点P,Q):

    double m1m2 = getGradient(P1,P2);
    double m1m3 = getGradient(P1,P3);
    double a = Abs(atan(m1m2) - atan(m1m3));
    if (isObtuse(P1, P2, P3)>0)
      a = PI - a;
    

    我可能在伪代码中犯了一些打字错误(希望不会),但我演示了这个概念的要点。如果是这样的话,有人会很乐意编辑掉打字错误。

    进一步 然而,经过仔细考虑后,我发现,由于指令的存在,精确性的斗争主要集中在它最薄弱的环节上。

    #define PI 3.14159blah..blah..blah.
    

    所以,我们不妨省去所有的麻烦,简单地做一下:

    double m1m2 = getGradient(P1,P2);
    double m1m3 = getGradient(P1,P3);
    double a = Abs(atan(m1m2) - atan(m1m3));
    double b = PI - a;
    return min(a, b);//the smaller of the two is the acute