我正在开发一个程序,我需要找到位于某个半径的某个笛卡尔点的圆圈内的所有直线。
目前,对于每一个圆圈,我都在迭代所有线条并检查线条是否在任何位置进入/接触圆圈。
代码基本上就是这样。
for (int i = 0; i < num_circles; i++)
{
for (int j = 0; j < num_lines; j++)
{
if(lineIntersectWithCircle(circle[i], lines[j]))
{
//Append line[j] to a list of lines intersecting with circle[i];
//some code
}
}
}
我一直在想很多方法来优化它,但我遇到了麻烦。
我按最小笛卡尔距离对圆进行了排序,并按最大距离排序。这种方式可以稍微优化,但它很小,因为一旦达到line[j].max > circle[i].min
的点,你仍然必须遍历所有其余的行。
我的交叉检查方法很好,我只想尽量减少调用它的次数。
这样做有好办法吗?
答案 0 :(得分:0)
最便宜的方法是在更昂贵的交叉点测试之前检查两个形状(线和圆)的边界范围/矩形。有可能你甚至可以计算线/圈的飞行范围,而不是预先计算,并且仍然可以获得不错的性能提升,除非你的线/圆交叉已经很便宜了。
一种非常有效的方法,但需要更多工作的方法就是创建一个网格。您可以使用上面计算的边界矩形来便宜地查看形状相交的网格单元格。
struct GridNode
{
// Points to the index of the next node in the grid cell
// or -1 if we're at the end of the singly-linked list.
int next_node;
// Points to the index of the shape being stored.
int shape;
};
struct GridCell
{
// Points to the first node or -1 if the cell is empty.
int first_node;
};
struct Grid
{
// Stores the cells in the grid. This is just illustrative
// code. You should dynamically allocate this with adjustable
// grid widths and heights based on your needs.
struct GridCell cells[grid_width * grid_height];
// Stores the nodes in the grid (one or more nodes per shape
// inserted depending on how many it intersects). This is
// a variable-sized array you can realloc needed (ex: double
// the size when you're out of room).
struct GridNode* nodes;
// The maximum number of nodes we can store before realloc.
int node_cap;
// The number of nodes inserted so far. realloc when this
// exceeds node_cap.
int node_num;
};
......这样的事情。这样,大多数情况下,您可以向网格插入元素,只执行一些整数(模拟指针)操作,并将一些网格节点条目添加到此可变大小的nodes
数组中。堆分配很少发生。
我发现在实践中,如果你有很多动态元素从一个单元格移动到另一个单元格,就像在2D视频游戏中一样,在我们需要快速碰撞检测的同时,一切都在不断移动,甚至可以与竞争对手相媲美用于搜索的四叉树,如果您仔细考虑节点的内存布局,以便在迭代通过与您正在测试的形状相交的网格单元时最小化缓存未命中。在构建网格之后,您甚至可以执行后传,以根据您需要交叉点搜索的效率重新排列每个节点的内存,以进行缓存友好列表迭代。如果你想得到想象,你可以使用Bresenham来确切地确定一条线相交的网格单元格,例如,但考虑到你正在做的二次复杂性,你可以指数地改进,而不必费心去做,只是在一个非常简单的边界矩形方法。
基本上要找到一个交叉点,首先抓住形状的边界矩形。然后查看它在网格中相交的单元格。现在检查与原始形状相交的网格单元格中包含的形状的交集。通过这种方式,除了巨大的形状(O(n)的最坏情况)之外,你可以努力实现恒定时间的复杂性,这种情况很可能是一种罕见的情况。
当事情发生变化时,我甚至可以在三维中找到它们。它们通常比八叉树,BVH和kd树变体便宜,它们提供广泛的搜索加速,但代价是更昂贵的构建和更新,如果你对每个网格单元使用单链表的策略不必单独分配节点,即使使用第三维也可以将其存储在非常合理的内存中。我不会将其三维版本用于光线追踪,但它对于3D碰撞检测非常有用,例如检测每一帧移动的粒子之间的碰撞。
答案 1 :(得分:0)
与任何事情一样,取决于您的使用案例。如果您有固定数量的线或不经常添加,您可能需要预先计算一些计算所需的计算,以确定线的任何部分是否在圆心的半径距离内
从线与点之间的最短距离的公式开始,并比较该距离小于圆的半径:
lineIntersectWithCircle(size_t circle, size_t line){
struct circle C = circle_cache[circle]; //these may be separate arrays
struct line L = line_cache[line]; //from your point data
long tmp = C.x * L.y10 - C.y * L.x10 + L.delta;
return (tmp*tmp < L.sides * C.R2);
}
现在检查应该是4个整数乘法,2个加/减和比较。
bool lineIntersectWithCircle(size_t circle, size_t line){
struct circle C = circle_cache[circle]; //these may be separate arrays
struct line L = line_cache[line]; //from your point data
//if the bounding boxes don't intersect neither does the line
//this may not be _that_ helpful and you would need to:
// figure out the bounding boxes for each line/circle
// and cache additional data
if (C.leftx > L.rightx || L.leftx > C.rightx) //a box is to the side
return 0;
if (C.topy < L.boty || L.topy < C.boty) //a box is below/above
return 0;
//the bounding boxes intersected so check exact calculation
long tmp = C.x * L.y10 - C.y * L.x10 + L.delta;
return (tmp*tmp < L.sides * C.R2);
}
...但你可能想检查我的数学 - 它已经有一段时间了。此外,我假设点数将是整数 - 根据需要更改为浮点数 - 它应该仍然相对较快。
如果速度不够快,您可以为圆和线的边界框添加其他数据
{{1}}