三角形内的点:重心坐标

Point within a triangle: barycentric co-ordinates

本文关键字:重心坐标 三角形      更新时间:2023-10-16

我正在解决一个经典问题,即确定一个点是否在三角形内,我正在使用质心坐标方法。由于某种原因(我认为这是逻辑,而不是精度),它没有通过所有的测试。会出什么问题呢?

代码如下:

#include <iostream>
using namespace std;
struct point
{
    int x;
    int y;
};

bool Place(point &A, point &B, point &C, point &P)
{
    double det = (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y);
    double factor_alpha = (B.y - C.y)*(P.x - C.x) + (C.x - B.x)*(P.y - C.y);
    double factor_beta = (C.y - A.y)*(P.x - C.x) + (A.x - C.x)*(P.y - C.y);
    double alpha =  factor_alpha / det;
    double beta =  factor_beta / det;
    double gamma = 1.0 - alpha - beta;
    bool In = false;
    if (((A.x == P.x) & (A.y == P.y)) | ((B.x == P.x) & (B.y == P.y)) | ((C.x == P.x) & (C.y == P.y))) 
        In = true; // the sneaky guys are trying to see if the vertice of the triangle belongs to it
                    // the problem statement says it does.
    if ((alpha == 0) | (beta == 0) | (gamma == 0))
            In =  true; // the point is on the edge of the triangle
    if (( (0 < alpha) & (alpha < 1)) & ((0 < beta) & (beta < 1)) & ((0 < gamma) & (gamma < 1)))
            In =  true; // in this case P is actually within the triangle area
    return In;
}

int main()
{
    point A, B, C, P;
    cin >> A.x >> A.y >> B.x >> B.y >> C.x >> C.y >> P.x >> P.y;
    Place(A, B, C, P) ? cout << "Inn" : cout << "Outn";
    return 0;
}

如果alpha, betagamma中至少有一个为0,则该点位于边缘上。
这是必要的,但不是充分的;其他也必须在[0, 1]区间。

既然你对"边缘"情况不感兴趣,你可以写

if (0 <= alpha && alpha <= 1 && 0 <= beta && beta <= 1 && 0 <= gamma && gamma <= 1)
        In = true; 

(我删除了一些括号并用逻辑&&替换了按位&)


可读性建议:
引入两个函数使代码看起来更像一个数学定义:

bool operator ==(const point& a, const point& b)
{
    return a.x == b.x && a.y == b.y;
}
bool within(double x)
{
    return 0 <= x && x <= 1;
}
bool Place(const point &A, const point &B, const point &C, const  point &P)
{   
    double det = (B.y - C.y)*(A.x - C.x) + (C.x - B.x)*(A.y - C.y);
    double factor_alpha = (B.y - C.y)*(P.x - C.x) + (C.x - B.x)*(P.y - C.y);
    double factor_beta = (C.y - A.y)*(P.x - C.x) + (A.x - C.x)*(P.y - C.y);
    double alpha = factor_alpha / det;
    double beta = factor_beta / det;
    double gamma = 1.0 - alpha - beta;
    return P == A || P == B || P == C || (within(alpha) && within(beta) && within(gamma));
}

尝试这个函数(假设您有一个Point类模板,其中T是存储类型,并且您已经重载了operator*来计算点积):

template <typename T>
bool is_point_in_triangle(const Point<3,T>& p,
                          const Point<3,T>& a,
                          const Point<3,T>& b,
                          const Point<3,T>& c) {
  typedef Point<3,T> point_type;
  point_type v0 = b-a, v1 = c-a, v2 = p-a;
  T d00 = v0*v0;
  T d01 = v0*v1;
  T d11 = v1*v1;
  T d20 = v2*v0;
  T d21 = v2*v1;
  T denom = d00*d11 - d01*d01;
  // compute parametric coordinates
  Real v = (d11 * d20 - d01 * d21) / denom;
  Real w = (d00 * d21 - d01 * d20) / denom;  
  return v >= 0. && w >= 0. && v + w <= 1.;
}

作为旁注,您使用int来存储坐标,因此您的测试可能会因为截断而失败?好运!