快速计算三角形内的点数

时间:2013-02-07 18:05:09

标签: algorithm geometry computational-geometry

我需要找到给定列表中三角形内的点数。这里的问题是,可能有多达一百万点。

我尝试了一种简单的方法:如果三角形的面积等于通过一次取两个三角形点而形成的3个三角形的面积之和,并检查其内部。这没有任何精度错误,因为我没有除以2来找到该区域。

但我需要更快的东西。目标是速度。是否可以通过某种预处理使其更快,忽略某些基于某些标准或类似的点?

修改: 忘了添加一个关键细节。一旦给出的点数是固定的。然后这些点是静态的,需要检查多达一百万个三角形...

编辑2 :原来一个好的(也许是最佳的)方法是使用线扫描。不过,谢谢你的回答!

5 个答案:

答案 0 :(得分:8)

如果点位于每一侧的左侧(右侧),则该点位于三角形内部。您可以计算由要测试的点构成的向量的交叉积(实际上只是它的一个分量)以及三角形顶点之一和位于三角形两侧的3个向量(全部以顺时针方式或全部在逆时针方向)。查看所有3的计算组件是否具有相同的符号(全部3个负数或全部3个正数)。这将告诉你重点是。快速,没有精度问题,至少,如果你使用整数来完成任务。

一旦你看到每个点位于其中一个三角形边的错误一侧,你就可以停止进一步的计算。

C:

中的示例代码
#include <stdio.h>

#define SCREEN_HEIGHT 22
#define SCREEN_WIDTH  78

// Simulated frame buffer
char Screen[SCREEN_HEIGHT][SCREEN_WIDTH];

void SetPixel(int x, int y, char color)
{
  if ((x < 0) || (x >= SCREEN_WIDTH) ||
      (y < 0) || (y >= SCREEN_HEIGHT))
    return;

  Screen[y][x] = color;
}

void Visualize(void)
{
  int x, y;

  for (y = 0; y < SCREEN_HEIGHT; y++)
  {
    for (x = 0; x < SCREEN_WIDTH; x++)
      printf("%c", Screen[y][x]);

    printf("\n");
  }
}

typedef struct
{
  int x, y;
} Point2D;

int main(void)
{
  // triangle vertices
  Point2D vx0 = { SCREEN_WIDTH / 2, SCREEN_HEIGHT / 7 };
  Point2D vx1 = { SCREEN_WIDTH * 6 / 7, SCREEN_HEIGHT * 2 / 3 };
  Point2D vx2 = { SCREEN_WIDTH / 7, SCREEN_HEIGHT * 6 / 7 };
  // vectors lying on triangle sides
  Point2D v0, v1, v2;
  // current point coordinates
  int x, y;

  // calculate side vectors

  v0.x = vx1.x - vx0.x;
  v0.y = vx1.y - vx0.y;

  v1.x = vx2.x - vx1.x;
  v1.y = vx2.y - vx1.y;

  v2.x = vx0.x - vx2.x;
  v2.y = vx0.y - vx2.y;

  // process all points

  for (y = 0; y < SCREEN_HEIGHT; y++)
    for (x = 0; x < SCREEN_WIDTH; x++)
    {
      int z1 = (x - vx0.x) * v0.y - (y - vx0.y) * v0.x;
      int z2 = (x - vx1.x) * v1.y - (y - vx1.y) * v1.x;
      int z3 = (x - vx2.x) * v2.y - (y - vx2.y) * v2.x;

      if ((z1 * z2 > 0) && (z1 * z3 > 0))
        SetPixel(x, y, '+'); // point is to the right (left) of all vectors
      else
        SetPixel(x, y, '-');
    }

  // draw triangle vertices

  SetPixel(vx0.x, vx0.y, '0');
  SetPixel(vx1.x, vx1.y, '1');
  SetPixel(vx2.x, vx2.y, '2');

  // visualize the result

  Visualize();

  return 0;
}

输出(ideone):

------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
---------------------------------------0--------------------------------------
--------------------------------------++++------------------------------------
------------------------------------++++++++----------------------------------
----------------------------------+++++++++++++-------------------------------
--------------------------------+++++++++++++++++-----------------------------
------------------------------++++++++++++++++++++++--------------------------
----------------------------++++++++++++++++++++++++++------------------------
--------------------------+++++++++++++++++++++++++++++++---------------------
-------------------------++++++++++++++++++++++++++++++++++-------------------
-----------------------+++++++++++++++++++++++++++++++++++++++----------------
---------------------+++++++++++++++++++++++++++++++++++++++++++--------------
-------------------+++++++++++++++++++++++++++++++++++++++++++++++1-----------
-----------------++++++++++++++++++++++++++++++++++++-------------------------
---------------++++++++++++++++++++++++---------------------------------------
-------------++++++++++++-----------------------------------------------------
-----------2------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------

答案 1 :(得分:7)

根据计算几何学,最快的方法是通过重心坐标变换。如果你有一个固定的三角形和许多测试点,那么这种方法将特别快,因为一旦你计算了三角形的3个点的重心坐标,你就完成了大部分的工作。这是完整的算法,其中ABC是三角形,P是被测点:

// Compute vectors        
v0 = C - A
v1 = B - A
v2 = P - A

// Compute dot products
dot00 = dot(v0, v0)
dot01 = dot(v0, v1)
dot02 = dot(v0, v2)
dot11 = dot(v1, v1)
dot12 = dot(v1, v2)

// Compute barycentric coordinates
invDenom = 1 / (dot00 * dot11 - dot01 * dot01)
u = (dot11 * dot02 - dot01 * dot12) * invDenom
v = (dot00 * dot12 - dot01 * dot02) * invDenom

// Check if point is in triangle
return (u >= 0) && (v >= 0) && (u + v < 1)

这里重心坐标是相对于A计算的,但是B或C也会起作用。

要测试其他点,您只需要重新计算v2,dot02,dot12,u和v。例如invDenom等数量保持不变。

答案 2 :(得分:4)

简单的预滤镜是为了消除坐标明显位于三角形边界之外的任何点。 e.g。

  a
+
|\
| \ b
|c \
+---+  d

A和D显然在外面。 A的坐标远远高于三角形的最大Y,D显然超出了三角形的最大X.

留下B和C进行测试。

答案 3 :(得分:3)

您还可以使用quadtree来加速计算。

计算三角形的四叉树(以任意分辨率停止),对于每个节点(正方形),存储一个标志,指示节点是完全在内部,完全在外部还是部分在三角形内部。部分位于三角形内的节点可能具有子节点(取决于深度)

对于每个点,遍历四叉树。如果我们访问完全位于三角形外部或内部的节点,我们都已设置好。如果我们不确定我们是否在三角形中(节点部分在三角形内)并且当前节点有子节点,我们将递归地测试其子节点。如果我们点击了部分位于三角形内部的叶节点,请进行分析点 - 三角形包含检查。

答案 4 :(得分:1)

首先按y坐标和y坐标x坐标的关系对列表中给出的点进行排序。现在从最低y坐标的下方开始(考虑到x轴的平行线)并将其向上移动1个单位,您还可以得到由三角形的终点形成的线段方程。现在消除了Marc B建议的一些明显的点。对于具有与x轴假想的平行线y坐标相同的其余点,每次向上移动一个单位步骤,通过放置检查它们是在三角形内部还是外部在连接三角形端点的线的方程中。通过在先前根据y坐标排序的坐标列表上进行二分搜索,可以轻松地使这些点具有相同的y坐标。这样你的算法就会为每个查询带来O(Yrangeoftriangle * nlogn)。还有很多关于codechef问题的问题。