最简单的Voronoi图算法实现? [关闭]
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最简单的Voronoi图算法实现? [关闭]相关的知识,希望对你有一定的参考价值。
实现Voronoi图的简单算法是什么?
我找不到任何特殊的伪形式算法。请分享Voronoi图算法,教程等的一些链接。
计算点集的Delaunay三角剖分的简单算法是flipping edges。由于Delaunay三角剖分是Voronoi图的双重图,因此您可以在线性时间内从三角剖分构造图。
不幸的是,翻转方法的最坏情况运行时间是O(n ^ 2)。存在更好的算法,例如Fortune的行扫描,这需要O(n log n)时间。尽管如此,这有点棘手。如果你是懒惰的(就像我一样),我建议寻找Delaunay三角测量的现有实现,使用它,然后计算双图。
一般来说,关于这个主题的好书是de Berg等人的Computational Geometry。
这是最快的 - 它是一个简单的voronoi但它看起来很棒。它将空格划分为网格,在随机放置的每个网格单元格中放置一个点,并沿网格移动检查3x3单元格以查找它与相邻单元格的关系。
没有渐变,它会更快。
你可能会问最简单的3d voronoi是什么。知道这将是非常有趣的。可能是3x3x3细胞并检查梯度。
http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm
float voronoi( in vec2 x )
{
ivec2 p = floor( x );
vec2 f = fract( x );
float res = 8.0;
for( int j=-1; j<=1; j++ )
for( int i=-1; i<=1; i++ )
{
ivec2 b = ivec2( i, j );
vec2 r = vec2( b ) - f + random2f( p + b );
float d = dot( r, r );
res = min( res, d );
}
return sqrt( res );
}
这里与切比雪夫距离相同。你可以从这里使用随机2d 2d浮动噪音:
https://www.shadertoy.com/view/Msl3DM
编辑:我已将此转换为C代码
这是不久前,为了那些人的利益,我相信这很酷:
function rndng ( n: float ): float
{//random number -1, 1
var e = ( n *321.9)%1;
return (e*e*111.0)%2-1;
}
function voronoi( vtx: Vector3 )
{
var px = Mathf.Floor( vtx.x );
var pz = Mathf.Floor( vtx.z );
var fx = Mathf.Abs(vtx.x%1);
var fz = Mathf.Abs(vtx.z%1);
var res = 8.0;
for( var j=-1; j<=1; j++ )
for( var i=-1; i<=1; i++ )
{
var rx = i - fx + nz2d(px+i ,pz + j ) ;
var rz = j - fz + nz2d(px+i ,pz + j ) ;
var d = Vector2.Dot(Vector2(rx,rz),Vector2(rx,rz));
res = Mathf.Min( res, d );
}
return Mathf.Sqrt( res );
}
检查Richard Franks在How do I derive a Voronoi diagram given its point set and its Delaunay triangulation?问题的答案中使用伪代码提供的暴力解决方案
最简单的算法来自于voronoi图的定义:“将具有n个点的平面划分为凸多边形,使得每个多边形恰好包含一个生成点,并且给定多边形中的每个点都比其他任何点更接近其生成点“。来自wolfram的定义。
这里的重要部分是关于每个点比任何其他点更接近生成点,从这里算法非常简单:
- 有一系列生成点。
- 遍历画布上的每个像素。
- 对于每个像素,寻找最接近它的生成点。
- 根据您希望为像素着色的图表。如果您想要一个用边框分隔的图表,请检查第二个到最近点,然后检查它们的差异和颜色与边框颜色是否小于某个值。
如果你想要一个颜色图,那么每个像素都有一个颜色与每个生成点和颜色相关联,并且它与颜色的最近生成点相关。这就是它,它不是有效的,但很容易实现。
基于Fortune的算法/扫描线算法在谷歌代码上找到了这个优秀的C#库
https://code.google.com/p/fortune-voronoi/
您只需要创建一个List。可以通过将两个数字(坐标)作为float传递来创建Vector。然后将列表传递给Fortune.ComputeVoronoiGraph()
您可以从这些维基百科页面中更多地了解算法的概念:
http://en.wikipedia.org/wiki/Fortune%27s_algorithm
http://en.wikipedia.org/wiki/Sweep_line_algorithm
虽然我无法理解的一件事是如何为Partial Infinite边创建一条线(对坐标几何不太了解:-))。如果有人知道,请告诉我。
如果您尝试将其绘制到图像,则可以使用基于队列的泛洪填充算法。
Voronoi::draw(){
// define colors for each point in the diagram;
// make a structure to hold {pixelCoords,sourcePoint} queue objects
// initialize a struct of two closest points for each pixel on the map
// initialize an empty queue;
// for each point in diagram:
// for the push object, first set the pixelCoords to pixel coordinates of point;
// set the sourcePoint of the push object to the current point;
// push the queue object;
// while queue is not empty:
// dequeue a queue object;
// step through cardinal neighbors n,s,e,w:
// if the current dequeued source point is closer to the neighboring pixel than either of the two closest:
// set a boolean doSortAndPush to false;
// if only one close neighbor is set:
// add sourcePoint to closestNeighbors for pixel;
// set doSortAndPush to true;
// elif sourcePoint is closer to pixel than it's current close neighbor points:
// replace the furthest neighbor point with sourcePoint;
// set doSortAndPush to true;
// if flag doSortAndPush is true:
// re-sort closest neighbors;
// enqueue object made of neighbor pixel coordinates and sourcePoint;
// for each pixel location:
// if distance to closest point within a radius for point drawing:
// color pixel the point color;
// elif distances to the two closest neighbors are roughly equal:
// color the pixel to your border color;
// else
// color the pixel the color of the point's region;
}
使用队列将确保区域并行传播,从而最大限度地减少像素访问的总次数。如果使用堆栈,则第一个点将填充整个图像,然后第二个点将填充比第一个点更靠近它的任何像素。这将继续,大大增加访问次数。使用FIFO队列按照按下的顺序处理像素。无论你使用堆栈还是队列,结果图像都大致相同,但是队列的big-O比堆栈算法的big-O更接近线性(相对于图像像素的数量)。一般的想法是,区域将以相同的速率扩散,并且碰撞通常恰好在对应于区域边界的点处发生。
最简单的?这是蛮力方法:对于输出中的每个像素,迭代所有点,计算距离,使用最接近的。尽可能慢,但非常简单。如果表现不重要,那就完成了工作。我自己一直在努力进行一项有趣的改进,但仍然在寻找是否有其他人有同样的(相当明显的)想法。
Bowyer-Watson算法很容易理解。这是一个实现:http://paulbourke.net/papers/triangulate/。对于一组点,它是一个delaunay三角剖分,但你可以用它来得到delaunay的对偶,即。一个voronoi图。 BTW。最小生成树是delaunay三角剖分的子集。
构建voronoi图的最有效算法是Fortune's algorithm。它以O(n log n)运行。
这是他的reference implementation in C的链接。
我个人非常喜欢Bill Simons和Carson Farmer的python implementation,因为我发现它更容易扩展。
维基百科页面(http://en.wikipedia.org/wiki/Voronoi_diagram)有一个算法部分,其中包含用于实现Voronoi图的算法的链接。
Stephan Fortune / Shane O'Sullivan为C和C ++中的二维图提供了一个免费的voronoi实现:
VoronoiDiagramGenerator.cpp
VoronoiDiagramGenerator.h
你会在很多地方找到它。即在http://www.skynet.ie/~sos/masters/
这是一个使用quat-tree并允许增量构造的javascript实现。
http://code.google.com/p/javascript-voronoi/
虽然最初的问题是关于如何实现Voronoi,但是当我在搜索关于这个主题的信息时,我发现了一个帖子说下面的内容,这会给我节省很多时间:
互联网上有很多“几乎正确”的C ++代码用于实现Voronoi图。当种子点变得非常密集时,大多数都很少触发失败。我建议您在完成的项目中使用您希望在完成的项目中使用的点数来广泛测试您在线找到的任何代码,然后再浪费太多时间。
我在网上发现的最好的实现是从这里链接的MapManager程序的一部分:Voronoi图及matlab实现