仅使用余弦来检查Delaunay的“翻转条件”

问题描述

在寻找确定点是否在外接圆内的方法时,我遇到了this答案,该答案使用了一种有趣的方法在点和三角形之间构造四边形并测试flip condition看看新点是否构成更好的delaunay三角形,并因此在原始三角形的外接圆之内。

delaunay翻转条件处理角度,但是,我发现的answer只是计算角度的余弦值。而不是检查角度的总和是否小于或等于180°,它采用所有(负)余弦中的最小值,比较这两个结果以确定点是否在圆中。

以下是该答案的代码(为方便起见,在此处复制):

#include <array>
#include <algorithm>

struct pnt_t
{
  int x,y;

  pnt_t ccw90() const
    { return { -y,x }; }

  double length() const
    { return std::hypot(x,y); }

  pnt_t &operator -=(const pnt_t &rhs)
  {
    x -= rhs.x;
    y -= rhs.y;
    return *this;
  }

  friend pnt_t operator -(const pnt_t &lhs,const pnt_t &rhs)
    { return pnt_t(lhs) -= rhs; }

  friend int operator *(const pnt_t &lhs,const pnt_t &rhs)
    { return lhs.x * rhs.x + lhs.y * rhs.y; }
};

int side(const pnt_t &a,const pnt_t &b,const pnt_t &p)
{
  int cp = (b - a).ccw90() * (p - a);
  return (cp > 0) - (cp < 0);
}

void make_ccw(std::array<pnt_t,3> &t)
{
  if (side(t[0],t[1],t[2]) < 0)
    std::swap(t[0],t[1]);
}

double ncos(pnt_t a,const pnt_t &o,pnt_t b)
{
  a -= o;
  b -= o;
  return -(a * b) / (a.length() * b.length());
}

bool inside_circle(std::array<pnt_t,3> t,const pnt_t &p)
{
  make_ccw(t);

  std::array<int,3> s = 
    { side(t[0],p),side(t[1],t[2],side(t[2],t[0],p) };

  unsigned outside = std::count(std::begin(s),std::end(s),-1);
  if (outside != 1)
    return outside == 0;

  while (s[0] >= 0)
  {
    std::rotate(std::begin(t),std::begin(t) + 1,std::end(t));
    std::rotate(std::begin(s),std::begin(s) + 1,std::end(s));
  }

  double 
    min_org = std::min({
      ncos(t[0],t[2]),ncos(t[2],t[1]),ncos(t[1],ncos(p,t[0]) }),min_alt = std::min({
      ncos(t[1],t[0]),ncos(t[0],p,t[1]) });

  return min_org <= min_alt;
}

我无法理解其工作原理。

“角度之和”与“所有余弦的最小值”如何关联?某些角度的余弦值始终为负,我认为您可以将三角形的位置任意地落在该负范围内。那么该测试如何有效?

另外,在收集了两组“最小余弦”(而不是两组角度和)之后,最终测试是查看哪个最小值最小。同样,我看不到这与通过使用翻转条件确定三角形是否有效的原始测试有什么关系。

我想念什么?

解决方法

好消息是,有一个众所周知的函数,可以通过计算以下所示的行列式来确定点D是否位于三角形ABC的外接圆内。如果InCircle返回大于零,则D位于外接圆内,需要翻转。该公式确实假定三角形ABC是按逆时针顺序给出的(因此具有正面积)。

enter image description here

我从成等人的书中得到了这个方程。 “ Delaunay Mesh Generation”(2013年),但您应该可以在其他地方找到它。 https://github.com/gwlucastrig/Tinfour提供了开放源Java实现,但是我敢肯定,您可以在其他地方找到示例,其中一些示例可能更适合您的需求。