最简单的Voronoi图算法实现?

时间:2009-06-10 00:02:55

标签: algorithm diagram voronoi

实现Voronoi图的简单算法是什么?

我找不到任何特殊的伪形式算法。请分享Voronoi图算法,教程等的一些链接。

14 个答案:

答案 0 :(得分:31)

计算点集的Delaunay三角剖分的简单算法是flipping edges。由于Delaunay三角剖分是Voronoi图的双重图,因此您可以在线性时间内从三角剖分构建图。

不幸的是,翻转方法的最坏情况运行时间是O(n ^ 2)。存在更好的算法,例如Fortune的行扫描,这需要O(n log n)时间。尽管如此,这有点棘手。如果你是懒惰的(就像我一样),我建议寻找Delaunay三角测量的现有实现,使用它,然后计算双图。

总的来说,de Berg等人的一本关于这个主题的好书是Computational Geometry

答案 1 :(得分:20)

最简单的?这是蛮力方法:对于输出中的每个像素,迭代所有点,计算距离,使用最接近的。尽可能慢,但非常简单。如果表现不重要,那就完成了工作。我自己一直在做一个有趣的改进,但仍然在寻找是否有其他人有相同(相当明显)的想法。

答案 2 :(得分:14)

Bowyer-Watson算法很容易理解。这是一个实现:http://paulbourke.net/papers/triangulate/。对于一组点,它是一个delaunay三角剖分,但你可以用它来得到delaunay的对偶,即。一个voronoi图。 BTW。最小生成树是delaunay三角剖分的一个子集。

答案 3 :(得分:11)

构建voronoi图的最有效算法是Fortune's algorithm。它以O(n log n)运行。

以下是指向reference implementation in C的链接。

我个人非常喜欢Bill Simons和Carson Farmer的python implementation,因为我发现它更容易扩展。

答案 4 :(得分:10)

维基百科页面(http://en.wikipedia.org/wiki/Voronoi_diagram)有一个算法部分,其中包含实现Voronoi图的算法的链接。

答案 5 :(得分:9)

在Stephan Fortune / Shane O'Sullivan的C和C ++中有一个免费的voronoi实现二维图:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

你会在很多地方找到它。即在http://www.skynet.ie/~sos/masters/

答案 6 :(得分:8)

这是一个使用quat-tree并允许增量构建的javascript实现。

http://code.google.com/p/javascript-voronoi/

答案 7 :(得分:6)

虽然最初的问题是关于如何实现Voronoi,但是当我在搜索关于这个主题的信息时,我发现了一个帖子说了以下内容,这会给我节省很多时间:

互联网上有很多“几乎正确”的C ++代码用于实现Voronoi图。当种子点变得非常密集时,大多数都很少触发失败。 我建议您在完成的项目中使用您希望在完成的项目中使用的点数来广泛测试您在线找到的任何代码,然后再浪费太多时间。

我在网上找到的最好的实现是从这里链接的MapManager程序的一部分: http://www.skynet.ie/~sos/mapviewer/voronoi.php 它主要起作用,但在处理订单10 ^ 6点时,我得到间歇性的图表损坏。 我无法弄清楚腐败是如何蔓延的。

昨晚我发现了这个: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm “The Boost.Polygon Voronoi图书馆”。 看起来很有希望。这带来了基准测试,以证明它的准确性和卓越的性能。 该库具有适当的界面和文档。 我很惊讶我之前没有找到这个库,所以我在这里写了这篇文章。 (我在研究的早期就读过这篇文章。)

答案 8 :(得分:3)

这是最快的 - 它是一个简单的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 );
}

这与chebychev距离相同。你可以从这里使用random2f 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 );
 }

答案 9 :(得分:3)

实际上https://rosettacode.org/wiki/Voronoi_diagram

上有25种不同语言的实现

E.g for Java:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JFrame;

public class Voronoi extends JFrame {
    static double p = 3;
    static BufferedImage I;
    static int px[], py[], color[], cells = 100, size = 1000;

    public Voronoi() {
        super("Voronoi Diagram");
        setBounds(0, 0, size, size);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        int n = 0;
        Random rand = new Random();
        I = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
        px = new int[cells];
        py = new int[cells];
        color = new int[cells];
        for (int i = 0; i < cells; i++) {
            px[i] = rand.nextInt(size);
            py[i] = rand.nextInt(size);
            color[i] = rand.nextInt(16777215);

        }
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                n = 0;
                for (byte i = 0; i < cells; i++) {
                    if (distance(px[i], x, py[i], y) < distance(px[n], x, py[n], y)) {
                        n = i;

                    }
                }
                I.setRGB(x, y, color[n]);

            }
        }

        Graphics2D g = I.createGraphics();
        g.setColor(Color.BLACK);
        for (int i = 0; i < cells; i++) {
            g.fill(new Ellipse2D .Double(px[i] - 2.5, py[i] - 2.5, 5, 5));
        }

        try {
            ImageIO.write(I, "png", new File("voronoi.png"));
        } catch (IOException e) {

        }

    }

    public void paint(Graphics g) {
        g.drawImage(I, 0, 0, this);
    }

    static double distance(int x1, int x2, int y1, int y2) {
        double d;
        d = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); // Euclidian
    //  d = Math.abs(x1 - x2) + Math.abs(y1 - y2); // Manhattan
    //  d = Math.pow(Math.pow(Math.abs(x1 - x2), p) + Math.pow(Math.abs(y1 - y2), p), (1 / p)); // Minkovski
        return d;
    }

    public static void main(String[] args) {
        new Voronoi().setVisible(true);
    }
}

答案 10 :(得分:1)

在问题Richard Franks

的答案中,How do I derive a Voronoi diagram given its point set and its Delaunay triangulation?检查使用伪代码提供的暴力解决方案

答案 11 :(得分:1)

最简单的算法来自voronoi图的定义: “将具有n个点的平面划分为凸多边形,使得每个多边形只包含一个生成点,并且给定多边形中的每个点都更接近其生成点而不是任何其他点。”来自wolfram的定义

这里的重要部分是关于每个点比任何其他点更接近生成点,从这里算法非常简单:

  1. 有一系列生成点。
  2. 遍历画布上的每个像素。
  3. 为每个像素寻找最接近它的生成点。
  4. 取决于您希望为像素着色的图表。 如果你想要一个用边框分隔的图表,检查第二个到最近的点,然后检查它们的差异和颜色与边框颜色是否小于某个值。
  5. 如果你想要一个颜色图表,那么每个像素都有一个与每个生成点和颜色相关联的颜色,并且它与颜色的最近生成点相关。 这就是它,它不是有效的,但很容易实现。

答案 12 :(得分:0)

基于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边创建一条线(对坐标几何不太了解:-))。如果有人知道,请告诉我。

答案 13 :(得分:0)

如果尝试将其绘制到图像上,则可以使用基于队列的洪水填充算法。

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更接近线性(相对于图像像素数)。通常的想法是,区域将以相同的速度扩散,并且碰撞通常会恰好发生在与区域边界相对应的点上。