检测角度是否大于180度

时间:2011-10-16 16:19:01

标签: c algorithm math computational-geometry trigonometry

我正在研究教授指定的一个问题,而我正在寻找一种方法来检测3点之间的角度是否超过180度,例如:

我想检测alpha是否超过180度。无论如何,我的教授有一个解决问题的代码,但他有一个名为zcross的函数,但我不知道它是如何工作的。有谁能告诉我?他的代码在这里:

#include <fstream.h>
#include <math.h>
#include <stdlib.h>

struct point {
    double  x;
    double  y;
    double  angle;
};

struct vector {
    double  i;
    double  j;
};

point   P[10000];
int     hull[10000];

int 
zcross (vector * u, vector * v)
{
    double  p = u->i * v->j - v->i * u->j;
    if (p > 0)
    return 1;
    if (p < 0)
    return -1;
    return 0;
}

int 
cmpP (const void *a, const void *b)
{
    if (((point *) a)->angle < ((point *) b)->angle)
    return -1;
    if (((point *) a)->angle > ((point *) b)->angle)
    return 1;
    return 0;
}

void 
main ()
{
    int     N, i, hullstart, hullend, a, b;
    double  midx, midy, length;
    vector  v1, v2;

    ifstream fin ("fc.in");
    fin >> N;
    midx = 0, midy = 0;
    for (i = 0; i < N; i++) {
        fin >> P[i].x >> P[i].y;
        midx += P[i].x;
        midy += P[i].y;
    }
    fin.close ();
    midx = (double) midx / N;
    midy = (double) midy / N;
    for (i = 0; i < N; i++)
        P[i].angle = atan2 (P[i].y - midy, P[i].x - midx);
    qsort (P, N, sizeof (P[0]), cmpP);

    hull[0] = 0;
    hull[1] = 1;
    hullend = 2;
    for (i = 2; i < N - 1; i++) {
        while (hullend > 1) {
            v1.i = P[hull[hullend - 2]].x - P[hull[hullend - 1]].x;
            v1.j = P[hull[hullend - 2]].y - P[hull[hullend - 1]].y;
            v2.i = P[i].x - P[hull[hullend - 1]].x;
            v2.j = P[i].y - P[hull[hullend - 1]].y;
            if (zcross (&v1, &v2) < 0)
                break;
            hullend--;
        }
        hull[hullend] = i;
        hullend++;
    }

    while (hullend > 1) {
        v1.i = P[hull[hullend - 2]].x - P[hull[hullend - 1]].x;
        v1.j = P[hull[hullend - 2]].y - P[hull[hullend - 1]].y;
        v2.i = P[i].x - P[hull[hullend - 1]].x;
        v2.j = P[i].y - P[hull[hullend - 1]].y;
        if (zcross (&v1, &v2) < 0)
            break;
        hullend--;
    }
    hull[hullend] = i;

    hullstart = 0;
    while (true) {
        v1.i = P[hull[hullend - 1]].x - P[hull[hullend]].x;
        v1.j = P[hull[hullend - 1]].y - P[hull[hullend]].y;
        v2.i = P[hull[hullstart]].x - P[hull[hullend]].x;
        v2.j = P[hull[hullstart]].y - P[hull[hullend]].y;
        if (hullend - hullstart > 1 && zcross (&v1, &v2) >= 0) {
            hullend--;
            continue;
        }
        v1.i = P[hull[hullend]].x - P[hull[hullstart]].x;
        v1.j = P[hull[hullend]].y - P[hull[hullstart]].y;
        v2.i = P[hull[hullstart + 1]].x - P[hull[hullstart]].x;
        v2.j = P[hull[hullstart + 1]].y - P[hull[hullstart]].y;
        if (hullend - hullstart > 1 && zcross (&v1, &v2) >= 0) {
            hullstart++;
            continue;
        }
        break;
    }

    length = 0;
    for (i = hullstart; i <= hullend; i++) {
        a = hull[i];
        if (i == hullend)
            b = hull[hullstart];
        else
            b = hull[i + 1];
        length += sqrt ((P[a].x - P[b].x) * (P[a].x - P[b].x) + (P[a].y - P[b].y) * (P[a].y - P[b].y));
    }

    ofstream fout ("fc.out");
    fout.setf (ios: :fixed);
    fout.precision (2);
    fout << length << '\n';
    fout.close ();
}

4 个答案:

答案 0 :(得分:38)

首先,我们知道如果sin(a)为负,则角度超过180度。

我们如何找到sin(a)的标志?这是交叉产品发挥作用的地方。

首先,让我们定义两个向量:

v1 = p1-p2
v2 = p3-p2

这意味着这两个向量从p2开始,一个指向p1,另一个指向p3

交叉产品定义为:

(x1, y1, z1) x (x2, y2, z2) = (y1z2-y2z1, z1x2-z2x1, x1y2-x2y1)

由于您的向量位于2d,因此z1z2为0,因此:

(x1, y1, 0) x (x2, y2, 0) = (0, 0, x1y2-x2y1)

这就是为什么他们称之为 zcross ,因为只有产品的z元素的值不是0。

现在,另一方面,我们知道:

||v1 x v2|| = ||v1|| * ||v2|| * abs(sin(a))

其中||v||是向量v的范数(大小)。另外,我们知道如果角度a小于180,那么v1 x v2将指向上方(右手规则),而如果它大于180则指向下方。所以在你的特殊情况下:

(v1 x v2).z = ||v1|| * ||v2|| * sin(a)

简单地说,如果v1 x v2的z值为正,则a小于180.如果为负,则它更大(z值为x1y2-x2y1)。如果叉积为0,则两个向量是平行的,角度为0或180,这取决于两个向量是分别具有相同方向还是相反方向。

答案 1 :(得分:3)

zcross使用vector cross product的符号(在z方向上加或减)确定角度是否大于或小于180度,正如您所说的那样。

答案 2 :(得分:1)

在3D中,找到向量的叉积,找到叉积的最小长度,基本上只是找到x,y和z的最小数。

如果最小值小于0,则矢量的角度为负。

所以在代码中:

float Vector3::Angle(const Vector3 &v) const
{
    float a = SquareLength();
    float b = v.SquareLength();
    if (a > 0.0f && b > 0.0f)
    {
        float sign = (CrossProduct(v)).MinLength();
        if (sign < 0.0f)
            return -acos(DotProduct(v) / sqrtf(a * b));
        else
            return acos(DotProduct(v) / sqrtf(a * b));
    }
    return 0.0f;
}

答案 3 :(得分:0)

另一种方法如下:

计算向量v1 = p2-p1,v2 = p2 -p3。 然后,使用叉积公式:u.v = || u || || v || COS(THETA)