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.
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:
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.
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.
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.
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.
La pagina Wikipedia ( http://en.wikipedia.org/wiki/Voronoi_diagram ) ha una sezione Algoritmi con collegamenti ad algoritmi per l'implementazione dei diagrammi di Voronoi.
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/
Ecco un'implementazione javascript che utilizza quat-tree e consente la costruzione incrementale.
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.)
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);
}
}
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:
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.
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 );
}
ivec2
? o vec2
? Questo è illeggibile.
Controllare la soluzione di forza bruta presentata con pseudo-codice da Richard Franks nella sua risposta alla domanda Come posso derivare un diagramma di Voronoi dato il suo insieme di punti e la sua triangolazione di Delaunay?
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.
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.