【问题标题】:Detecting if angle is more than 180 degrees检测角度是否大于 180 度
【发布时间】:2011-10-16 16:19:00
【问题描述】:

我正在研究教授分配的一个问题,我在寻找一种方法来检测 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 ();
}

【问题讨论】:

    标签: c algorithm math computational-geometry trigonometry


    【解决方案1】:

    首先,我们知道如果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)
    

    由于您的向量是二维的,所以 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度,具体取决于两个向量的方向相同还是相反。

    【讨论】:

    • 在 2D 中,您真正在做的是计算“外积”,这是一个比叉积更普遍的概念,并且适用于任意数量的维度。他们没有在介绍性线性代数课程中教授它,这是一种耻辱。 (公式大体相同,只是没有提到“z”坐标,所以更简单。)
    【解决方案2】:

    zcross 使用vector cross product 的符号(z 方向的正负)来确定角度是大于还是小于 180 度,正如您所说的那样。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我认为值得一提的是,该函数返回 [-180°;180°] 之间的角度 - 而不是 [0;360°] 之间的角度 - 完美!
      【解决方案4】:

      另一种方法如下:

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

      【讨论】:

      • 如何处理 > 180° 的角度?
      • 标志会告诉你是否超过 180°,不是吗?
      猜你喜欢
      • 2015-04-16
      • 2016-09-15
      • 1970-01-01
      • 2011-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-06
      相关资源
      最近更新 更多