15

我有两条线: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));

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

4

8 回答 8

23

我认为您正在寻找的是两个角度的内积(您可能还想查看点积条目)。在你的情况下,这是由:

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

于 2010-05-31T23:51:03.703 回答
5
if (result > 180)
{
     result = 360 - result;
}

这样,它将始终是内角。得到结果后添加即可。

于 2010-05-31T23:10:42.443 回答
4

If you want in between angle in 0 degree to 360 degree then use following code; Its fully tested and functional:

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;

}

Note: Rotation will be clockwise;

于 2015-04-07T13:49:11.370 回答
2

整点比给定的答案容易得多:

当您使用 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 中减去。

于 2010-07-07T15:15:46.000 回答
1

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

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

模块(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;
}
于 2010-06-01T00:33:01.010 回答
1

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

于 2011-02-03T21:13:23.583 回答
0

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

相交的锐角和钝角是 180 度的互补角。IE

 acute + obtuse = PI.

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

因此,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;
} 

该函数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(Point 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
于 2010-06-01T09:52:37.110 回答
0

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

于 2010-05-31T23:15:59.903 回答