L'algoritmo più semplice da implementare del diagramma di Voronoi? [chiuso]


88

Quali sono gli algoritmi facili per implementare il diagramma di Voronoi?

Non sono riuscito a trovare alcun algoritmo specialmente in forma pseudo. Si prega di condividere alcuni collegamenti dell'algoritmo del diagramma di Voronoi, tutorial ecc.


Risposte:


32

Un semplice algoritmo per calcolare la triangolazione di Delaunay di un insieme di punti è il capovolgimento dei bordi . Poiché una triangolazione di Delaunay è il doppio grafico di un diagramma di Voronoi, è possibile costruire il diagramma dalla triangolazione in tempo lineare.

Sfortunatamente, il tempo di esecuzione nel caso peggiore dell'approccio flipping è O (n ^ 2). Esistono algoritmi migliori come lo sweep di linea di Fortune, che richiedono tempo O (n log n). Questo è un po 'complicato da implementare però. Se sei pigro (come me), ti suggerirei di cercare un'implementazione esistente di una triangolazione Delaunay, usarla e quindi calcolare il doppio grafico.

In generale, un buon libro sull'argomento è Computational Geometry di de Berg et al.


19

Il più semplice? Questo è l'approccio della forza bruta: per ogni pixel nel tuo output, itera attraverso tutti i punti, calcola la distanza, usa il più vicino. Lento come può essere, ma molto semplice. Se le prestazioni non sono importanti, fa il lavoro. Ho lavorato anch'io a un perfezionamento interessante, ma sto ancora cercando di vedere se qualcun altro ha avuto la stessa (piuttosto ovvia) idea.


14

L'algoritmo Bowyer-Watson è abbastanza facile da capire. Ecco un'implementazione: http://paulbourke.net/papers/triangulate/ . È una triangolazione delaunay per un insieme di punti ma puoi usarla per ottenere il duale del delaunay, cioè un diagramma di voronoi. BTW. lo spanning tree minimo è un sottoinsieme della triangolazione delaunay.


12

L'algoritmo più efficiente per costruire un diagramma di voronoi è l'algoritmo di Fortune . Funziona in O (n log n).

Ecco un link al suo implementazione di riferimento in C .

Personalmente mi piace molto l' implementazione di Python di Bill Simons e Carson Farmer, poiché l'ho trovata più facile da estendere.


il collegamento all'implementazione c sembra non funzionare più :(
FutureCake



9

C'è un'implementazione voronoi disponibile gratuitamente per i grafici 2-d in C e in C ++ da Stephan Fortune / Shane O'Sullivan:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

Lo troverai in molti posti. Vale a dire su http://www.skynet.ie/~sos/masters/


4
Ampiamente referenziato, non documentato e quasi ogni reimplementazione che ho visto sulla base di questo codice è sbagliata (in diverse lingue, molte persone hanno bisogno di Voronoi, pochi possono capirlo abbastanza bene per portarlo correttamente). Le uniche porte funzionanti che ho visto provengono dalla comunità scientifica / accademica e hanno firme di funzioni estremamente complicate o ottimizzate in modo massiccio (in modo che non possano essere utilizzate per la maggior parte degli scopi) rendendole inutilizzabili dai normali programmatori.
Adam

VoronoiDiagramGenerator.cpp ha funzionalità limitate. Produrrà un insieme non ordinato di bordi. Estrarre poligoni reali da questo non è banale. Sul lato positivo, presenta una clip su un rettangolo di delimitazione, quindi non vengono generati punti infinito.
Bram


6

Mentre la domanda originale chiede come implementare Voronoi, se avessi trovato un post che dicesse quanto segue quando stavo cercando informazioni su questo argomento, mi avrebbe risparmiato molto tempo:

C'è un sacco di codice C ++ "quasi corretto" su Internet per l'implementazione dei diagrammi di Voronoi. La maggior parte ha raramente provocato guasti quando i punti seme diventano molto densi. Consiglierei di testare ampiamente qualsiasi codice che trovi online con il numero di punti che prevedi di utilizzare nel tuo progetto finito prima di sprecare troppo tempo su di esso.

La migliore delle implementazioni che ho trovato online faceva parte del programma MapManager collegato da qui: http://www.skynet.ie/~sos/mapviewer/voronoi.php Per lo più funziona ma ricevo un danneggiamento intermittente del diagramma quando si tratta di ordine 10 ^ 6 punti. Non sono stato in grado di capire esattamente come si sta insinuando la corruzione.

Ieri sera ho trovato questo: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm "The Boost.Polygon Voronoi library". Sembra molto promettente. Questo viene fornito con test di benchmark per dimostrare che è accurato e ha prestazioni eccezionali. La libreria ha un'interfaccia e una documentazione adeguate. Sono sorpreso di non aver trovato questa libreria prima d'ora, da qui il mio scritto qui. (Ho letto questo post all'inizio della mia ricerca.)


4

Attualmente ci sono implementazioni per 25 lingue diverse disponibili su https://rosettacode.org/wiki/Voronoi_diagram

Ad esempio per 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

L'algoritmo più semplice deriva dalla definizione di un diagramma di voronoi: "La partizione di un piano con n punti in poligoni convessi in modo tale che ogni poligono contenga esattamente un punto generatore e ogni punto in un dato poligono sia più vicino al suo punto generatore che a qualsiasi altro . "definizione da wolfram.

La parte importante qui è che ogni punto è più vicino al punto di generazione di qualsiasi altro, da qui l'algoritmo è molto semplice:

  1. Avere una serie di punti di generazione.
  2. Passa attraverso ogni pixel sulla tua tela.
  3. Per ogni pixel cerca il punto di generazione più vicino ad esso.
  4. A seconda di quale diagramma si desidera ottenere il colore del pixel. Se vuoi un diagramma separato da un bordo, controlla il secondo al punto più vicino, quindi controlla la loro differenza e il colore con il colore del bordo se è più piccolo di un valore.

Se si desidera un diagramma dei colori, avere un colore associato a ogni punto di generazione e colore a ogni pixel con il colore associato al punto di generazione più vicino. E questo è tutto, non è efficiente ma molto facile da implementare.


3

Questo è il più veloce possibile: è un semplice voronoi ma ha un bell'aspetto. Divide gli spazi in una griglia, posiziona un punto in ogni cella della griglia posizionata casualmente e si sposta lungo la griglia controllando 3x3 celle per scoprire come si relaziona alle celle adiacenti.

È più veloce senza il gradiente.

Potresti chiedere quale sarebbe il voronoi 3d più semplice. Sarebbe affascinante saperlo. Probabilmente 3x3x3 celle e controllo del gradiente.

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

e qui è lo stesso con la distanza chebychev. puoi usare un rumore float 2d random2f da qui:

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

modifica: l'ho convertito in C come il codice

Questo è stato un po 'di tempo fa, a beneficio di coloro che cosa, credo che sia bello:

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

Perché usi così tante variabili di una lettera che non sono autoesplicative? E cosa ivec2? o vec2? Questo è illeggibile.
shinzou

Buon punto, penso di aver faticato anche tutto il giorno con esso: answer.unity3d.com/questions/638662/… ha aggiornato questo testo con il codice
aliential


0

Ho trovato questa eccellente libreria C # sul codice Google basata sull'algoritmo di Fortune / Sweep line

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

Hai solo bisogno di creare un elenco. Un vettore può essere creato passando due numeri (coordinate) come float. Quindi passare l'elenco a Fortune.ComputeVoronoiGraph ()

Puoi capire un po 'di più il concetto di algoritmo da queste pagine di wikipedia:

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

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

Anche se una cosa che non ero in grado di capire è come creare una linea per bordi parzialmente infiniti (non so molto sulla geometria delle coordinate :-)). Se qualcuno lo sa, per favore fatemelo sapere.


2
Sebbene questi collegamenti possano rispondere alla domanda, è meglio includere le parti essenziali della risposta qui e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
Kmeixner

0

Se stai cercando di disegnarlo su un'immagine, puoi utilizzare un algoritmo di riempimento delle inondazioni basato sulla coda.

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; 

}

L'utilizzo di una coda garantirà che le regioni si diffondano in parallelo, riducendo al minimo il numero totale di visite di pixel. Se si utilizza una pila, il primo punto riempirà l'intera immagine, quindi il secondo riempirà tutti i pixel più vicini rispetto al primo punto. Ciò continuerà, aumentando notevolmente il numero di visite. L'utilizzo di una coda FIFO elabora i pixel nell'ordine in cui vengono inviati. Le immagini risultanti saranno più o meno le stesse sia che si utilizzi lo stack o la coda, ma il big-O per la coda è molto più vicino al lineare (in relazione al numero di pixel dell'immagine) rispetto al big-O dell'algoritmo dello stack. L'idea generale è che le regioni si diffonderanno alla stessa velocità e le collisioni generalmente si verificheranno esattamente nei punti che corrispondono ai confini della regione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.