两条线之间的内角

时间:2010-05-31 22:50:22

标签: c++ math trigonometry lines angle

我有两行:Line1和Line2。每行由两个点(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 个答案:

答案 0 :(得分:19)

我认为您正在寻找的是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<T>& 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

答案 1 :(得分:4)

if (result > 180)
{
     result = 360 - result;
}

这样它始终是内角。只需在得到结果后添加它。

答案 2 :(得分:3)

如果你想要在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;

}

注意:顺时针旋转;

答案 3 :(得分:1)

2个向量之间的内角(v1,v2)= arc cos(内积(v1,v2)/(模块(v1)*模块(v2)))。

内在产品(v1,v2)= xv1 * xv2 + yv1 * yv2

module(v)= sqrt(pow(xv,2)+ pow(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;
}

答案 4 :(得分:1)

重点比给定答案容易得多:

当你使用atan(斜率)时,你会丢失(字面上)一位信息,即在范围内(0..2 * PI)正好有两个角度(θ)和(θ+ PI),这给出了函数tan()的值相同。

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

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

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

答案 5 :(得分:1)

如果使用绝对值,您将始终获得锐角。即,m1-m2的正切θ= abs值超过(1 + m1 * m2)。如果您采用反正切,您的答案将以弧度或度数为单位,但计算器已设置。对不起,这不是编程术语,我是数学老师,不是程序员...

答案 6 :(得分:0)

获得外角与内角完全取决于减法的顺序(想一想)。您需要从较大的θ中减去较小的θ,以便可靠地始终获得内角。由于您期望的数据类型,您可能还想使用atan2函数。

答案 7 :(得分:0)

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

交叉点的锐角和钝角相互成180度互补。即。

 acute + obtuse = PI.

http://www.mathworks.com/access/helpdesk/help/techdoc/ref/atan.html 表明atan在+/- pi / 2时是渐近的。

因此,无论您使用渐变的+/-符号还是正{1}}符号,atan的两个结果之间的最大差异为pi或180度。

考虑以下伪代码:

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; } 以数学方式说明了您需要做什么。

然而,它不能用于PI / 2附近的角度值,因为角度的二进制比较与该邻域的结果是否有问题是否是一个钝角或锐角的问题。

因此,我们必须比较两条线的点的坐标。 我们发现acuteAngle形成的第3行是否比假设的斜边短,等于或长。

凭借毕达哥拉斯定理, 如果角度恰好为PI / 2或90度,则形成斜边。让我们称他假设的斜边线L3Hypo。

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

  • 如果第3行长于 L3Hypo,角度很钝。
  • 如果更短,则角度是锐角。
  • 否则,完美90。

因此,

[(x2,y2)(x3,y3)]

因此,以下伪代码

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)

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

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

我可能在伪代码中犯了一些拼写错误(希望不会),但我展示了这个概念的要点。如果是这样,有人可以如此友善地编辑错别字。

<强> 另外 然而,在仔细研究之后,我发现由于指令而导致精确度的争夺在其最薄弱的环节上发挥作用

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.