Am einfachsten zu implementierender Algorithmus des Voronoi-Diagramms? [geschlossen]


88

Was sind die einfachen Algorithmen zur Implementierung des Voronoi-Diagramms?

Ich konnte keinen Algorithmus speziell in Pseudoform finden. Bitte teilen Sie einige Links von Voronoi Diagrammalgorithmus, Tutorial usw.


Antworten:


32

Ein einfacher Algorithmus zum Berechnen der Delaunay-Triangulation einer Punktmenge ist das Umdrehen von Kanten . Da eine Delaunay-Triangulation der duale Graph eines Voronoi-Diagramms ist, können Sie das Diagramm aus der Triangulation in linearer Zeit erstellen.

Leider ist die Worst-Case-Laufzeit des Flip-Ansatzes O (n ^ 2). Es gibt bessere Algorithmen wie Fortunes Line Sweep, die O (n log n) Zeit benötigen. Dies ist jedoch etwas schwierig zu implementieren. Wenn Sie faul sind (so wie ich), würde ich vorschlagen, nach einer vorhandenen Implementierung einer Delaunay-Triangulation zu suchen, diese zu verwenden und dann den dualen Graphen zu berechnen.

Ein gutes Buch zu diesem Thema ist im Allgemeinen Computational Geometry von de Berg et al.


19

Am einfachsten? Das ist der Brute-Force-Ansatz: Durchlaufen Sie für jedes Pixel in Ihrer Ausgabe alle Punkte, berechnen Sie die Entfernung und verwenden Sie die nächstgelegene. Langsam wie möglich, aber sehr einfach. Wenn Leistung nicht wichtig ist, erledigt sie den Job. Ich habe selbst an einer interessanten Verfeinerung gearbeitet, aber immer noch gesucht, ob jemand die gleiche (ziemlich offensichtliche) Idee hatte.


14

Der Bowyer-Watson-Algorithmus ist recht einfach zu verstehen. Hier ist eine Implementierung: http://paulbourke.net/papers/triangulate/ . Es ist eine Delaunay-Triangulation für eine Reihe von Punkten, aber Sie können sie verwenden, um das Dual des Delaunay zu erhalten, dh ein Voronoi-Diagramm. Übrigens. Der minimale Spannbaum ist eine Teilmenge der Delaunay-Triangulation.


12

Der effizienteste Algorithmus zum Erstellen eines Voronoi-Diagramms ist der Fortune-Algorithmus . Es läuft in O (n log n).

Hier ist ein Link zu seiner Referenz - Implementierung in C .

Persönlich mag ich die Python-Implementierung von Bill Simons und Carson Farmer sehr, da ich es einfacher fand, sie zu erweitern.


Der Link zur C-Implementierung scheint nicht mehr zu funktionieren :(
FutureCake



9

Es gibt eine frei verfügbare Voronoi-Implementierung für 2D-Graphen in C und in C ++ von Stephan Fortune / Shane O'Sullivan:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

Sie finden es an vielen Orten. Dh unter http://www.skynet.ie/~sos/masters/


4
Weit verbreitet, undokumentiert und fast jede Neuimplementierung, die ich basierend auf diesem Code gesehen habe, ist falsch (in verschiedenen Sprachen benötigen viele Leute Voronoi, nur wenige können es gut genug verstehen, um richtig zu portieren). Die einzigen funktionierenden Ports, die ich gesehen habe, stammen aus der Wissenschaft / Wissenschaft und haben massiv überkomplizierte Funktionssignaturen - oder massiv optimiert (so dass sie für die meisten Zwecke nicht verwendet werden können), wodurch sie für normale Programmierer unbrauchbar werden.
Adam

VoronoiDiagramGenerator.cpp verfügt über eingeschränkte Funktionen. Es wird ein ungeordneter Satz von Kanten ausgegeben. Es ist nicht trivial, daraus tatsächliche Polygone zu extrahieren. Auf der positiven Seite befindet sich ein Clip gegen ein begrenzendes Rechteck, sodass keine Unendlichkeitspunkte generiert werden.
Bram


6

Während die ursprüngliche Frage nach der Implementierung von Voronoi fragt, hätte ich viel Zeit gespart, wenn ich bei der Suche nach Informationen zu diesem Thema einen Beitrag gefunden hätte, der Folgendes besagt:

Es gibt viel "fast korrekten" C ++ - Code im Internet für die Implementierung von Voronoi-Diagrammen. Die meisten haben selten Fehler ausgelöst, wenn die Startpunkte sehr dicht werden. Ich würde empfehlen, jeden Code, den Sie online finden, ausführlich mit der Anzahl der Punkte zu testen, die Sie voraussichtlich in Ihrem fertigen Projekt verwenden werden, bevor Sie zu viel Zeit damit verschwenden.

Die beste Implementierung, die ich online gefunden habe, war Teil des MapManager-Programms, das von hier aus verlinkt wurde: http://www.skynet.ie/~sos/mapviewer/voronoi.php Es funktioniert meistens, aber ich bekomme zeitweise Diagrammbeschädigungen, wenn ich damit umgehe 10 ^ 6 Punkte bestellen. Ich konnte nicht genau herausfinden, wie sich die Korruption einschleicht.

Letzte Nacht habe ich Folgendes gefunden: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm "The Boost.Polygon Voronoi library". Es sieht sehr vielversprechend aus. Dies beinhaltet Benchmark-Tests, um die Genauigkeit zu beweisen und eine hervorragende Leistung zu erzielen. Die Bibliothek verfügt über eine ordnungsgemäße Benutzeroberfläche und Dokumentation. Ich bin überrascht, dass ich diese Bibliothek vorher nicht gefunden habe, daher schreibe ich hier darüber. (Ich habe diesen Beitrag früh in meiner Forschung gelesen.)


4

Derzeit sind Implementierungen für 25 verschiedene Sprachen unter https://rosettacode.org/wiki/Voronoi_diagram verfügbar

ZB für 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);
    }
}

3

Der einfachste Algorithmus ergibt sich aus der Definition eines Voronoi-Diagramms: "Die Aufteilung einer Ebene mit n Punkten in konvexe Polygone, sodass jedes Polygon genau einen Erzeugungspunkt enthält und jeder Punkt in einem bestimmten Polygon näher an seinem Erzeugungspunkt liegt als an jedem anderen . "Definition von Wolfram.

Der wichtige Teil hier ist, dass jeder Punkt näher am Erzeugungspunkt liegt als jeder andere. Von hier aus ist der Algorithmus sehr einfach:

  1. Haben Sie eine Reihe von Erzeugungspunkten.
  2. Durchlaufen Sie jedes Pixel auf Ihrer Leinwand.
  3. Suchen Sie für jedes Pixel nach dem nächstgelegenen Erzeugungspunkt.
  4. Je nachdem, welches Diagramm Sie erhalten möchten, färben Sie das Pixel. Wenn Sie möchten, dass ein Diagramm durch einen Rand getrennt wird, suchen Sie nach dem zweitnächsten Punkt und dann den Unterschied und die Farbe mit der Rahmenfarbe, wenn diese kleiner als ein Wert ist.

Wenn Sie ein Farbdiagramm wünschen, müssen Sie jedem Erzeugungspunkt eine Farbe und jedem Pixel die Farbe zuordnen, die dem nächsten Erzeugungspunkt am nächsten liegt. Und das war's auch schon, es ist nicht effizient, aber sehr einfach zu implementieren.


3

Dies ist die schnellstmögliche - es ist eine einfache Voronoi, aber es sieht gut aus. Es unterteilt Leerzeichen in ein Raster, platziert einen Punkt in jeder zufällig platzierten Rasterzelle und bewegt sich entlang des Rasters, wobei 3x3-Zellen überprüft werden, um festzustellen, wie sie sich auf benachbarte Zellen beziehen.

Ohne Gefälle geht es schneller.

Sie können fragen, was die einfachste 3D-Voronoi wäre. Es wäre faszinierend zu wissen. Wahrscheinlich 3x3x3 Zellen und Überprüfung des Gradienten.

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 );
}

und hier ist das gleiche mit chebychev Abstand. Sie können ein random2f 2d Float-Rauschen von hier aus verwenden:

https://www.shadertoy.com/view/Msl3DM

edit: Ich habe dies in C-ähnlichen Code konvertiert

Dies ist eine Weile her, zum Wohle derer, die es mögen, finde ich das cool:

 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 );
 }

Warum verwenden Sie so viele Ein-Buchstaben-Variablen, die nicht selbsterklärend sind? Und was ist ivec2? oder vec2? Das ist unlesbar.
Shinzou

Guter Punkt, ich glaube, ich hatte auch den ganzen Tag damit zu kämpfen: answers.unity3d.com/questions/638662/… hat diesen Text mit Code aktualisiert
aliential


0

Fand diese ausgezeichnete C # -Bibliothek auf Google-Code basierend auf dem Fortune-Algorithmus / Sweep-Line-Algorithmus

https://code.google.com/p/fortune-voronoi/

Sie müssen nur eine Liste erstellen. Ein Vektor kann erstellt werden, indem zwei Zahlen (Koordinaten) als float übergeben werden. Übergeben Sie dann die Liste an Fortune.ComputeVoronoiGraph ()

Sie können das Konzept des Algorithmus auf diesen Wikipedia-Seiten etwas besser verstehen:

http://en.wikipedia.org/wiki/Fortune%27s_algorithm

http://en.wikipedia.org/wiki/Sweep_line_algorithm

Eine Sache, die ich nicht verstehen konnte, ist, wie man eine Linie für teilweise unendliche Kanten erstellt (ich weiß nicht viel über die Koordinatengeometrie :-)). Wenn jemand es weiß, lass es mich bitte auch wissen.


2
Während diese Links die Frage beantworten können, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Kmeixner

0

Wenn Sie versuchen, es in ein Bild zu zeichnen, können Sie einen warteschlangenbasierten Flood-Filling-Algorithmus verwenden.

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; 

}

Durch die Verwendung einer Warteschlange wird sichergestellt, dass sich die Regionen parallel verteilen, wodurch die Gesamtzahl der Pixelbesuche minimiert wird. Wenn Sie einen Stapel verwenden, füllt der erste Punkt das gesamte Bild, der zweite alle Pixel, die näher als der erste Punkt liegen. Dies wird fortgesetzt und die Anzahl der Besuche erheblich erhöht. Bei Verwendung einer FIFO-Warteschlange werden Pixel in der Reihenfolge verarbeitet, in der sie verschoben werden. Die resultierenden Bilder sind ungefähr gleich, unabhängig davon, ob Sie einen Stapel oder eine Warteschlange verwenden, aber das Big-O für die Warteschlange ist viel näher an der Linearität (in Bezug auf die Anzahl der Bildpixel) als das Big-O des Stapelalgorithmus. Die allgemeine Idee ist, dass sich die Regionen mit der gleichen Geschwindigkeit ausbreiten und Kollisionen im Allgemeinen genau an Punkten auftreten, die den Regionsgrenzen entsprechen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.