Quels sont les algorithmes simples pour implémenter le diagramme de Voronoi?
Je n'ai trouvé aucun algorithme spécialement sous pseudo-forme. Veuillez partager quelques liens de l'algorithme de diagramme de Voronoi, du tutoriel, etc.
Un algorithme simple pour calculer la triangulation de Delaunay d'un ensemble de points est retournement des bords . Étant donné qu'une triangulation de Delaunay est le double graphique d'un diagramme de Voronoï, vous pouvez construire le diagramme à partir de la triangulation en temps linéaire.
Malheureusement, le pire temps d'exécution de l'approche de retournement est O (n ^ 2). Il existe de meilleurs algorithmes tels que le balayage de ligne de Fortune, qui prennent du temps O (n log n). Cependant, c'est un peu délicat à mettre en œuvre. Si vous êtes paresseux (comme moi), je suggère de rechercher une implémentation existante d'une triangulation de Delaunay, de l'utiliser, puis de calculer le double graphique.
En général, un bon livre sur le sujet est Computational Geometry par de Berg et al.
Le plus simple? C'est l'approche par force brute: pour chaque pixel de votre sortie, parcourez tous les points, calculez la distance, utilisez le plus proche. Aussi lent que possible, mais très simple. Si la performance n'est pas importante, elle fait l'affaire. J'ai moi-même travaillé sur un raffinement intéressant, mais je cherche toujours à voir si quelqu'un d'autre a eu la même idée (plutôt évidente).
L'algorithme de Bowyer-Watson est assez facile à comprendre. Voici une implémentation: http://paulbourke.net/papers/triangulate/ . C'est une triangulation delaunay pour un ensemble de points mais vous pouvez l'utiliser pour obtenir le dual du delaunay, c'est-à-dire. un diagramme de voronoi. BTW. l'arbre couvrant minimum est un sous-ensemble de triangulation delaunay.
L'algorithme le plus efficace pour construire un diagramme de voronoi est algorithme de Fortune . Il s'exécute en O (n log n).
Voici un lien vers son implémentation de référence en C .
Personnellement, j'aime vraiment le implémentation de python par Bill Simons et Carson Farmer, car j'ai trouvé plus facile à étendre.
La page Wikipedia ( http://en.wikipedia.org/wiki/Voronoi_diagram ) a une section Algorithmes avec des liens vers des algorithmes pour implémenter les diagrammes de Voronoi.
Il existe une implémentation de voronoi librement disponible pour les graphiques 2D en C et en C++ de Stephan Fortune/Shane O'Sullivan:
VoronoiDiagramGenerator.cpp
VoronoiDiagramGenerator.h
Vous le trouverez à de nombreux endroits. C'est à dire. à http://www.skynet.ie/~sos/masters/
Voici une implémentation javascript qui utilise quat-tree et permet une construction incrémentale.
Alors que la question d'origine demandait comment implémenter Voronoi, si j'avais trouvé un article qui disait ce qui suit lorsque je cherchais des informations sur ce sujet, cela m'aurait fait gagner beaucoup de temps:
Il y a beaucoup de code C++ "presque correct" sur Internet pour implémenter les diagrammes de Voronoi. La plupart ont rarement déclenché des échecs lorsque les points de départ deviennent très denses. Je recommanderais de tester intensivement tout code que vous trouverez en ligne avec le nombre de points que vous prévoyez d'utiliser dans votre projet fini avant de perdre trop de temps dessus.
La meilleure des implémentations que j'ai trouvées en ligne faisait partie du programme MapManager lié à partir d'ici: http://www.skynet.ie/~sos/mapviewer/voronoi.php Cela fonctionne principalement mais je suis obtenir une corruption de diagramme intermittente lorsqu'il s'agit de l'ordre de 10 ^ 6 points. Je n'ai pas pu déterminer exactement comment la corruption s'installe.
Hier soir, j'ai trouvé ceci: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm "La bibliothèque Boost.Polygon Voronoi". Cela semble très prometteur. Cela vient avec des tests de référence pour prouver sa précision et ses excellentes performances. La bibliothèque a une interface et une documentation appropriées. Je suis surpris de ne pas avoir trouvé cette bibliothèque avant maintenant, d'où mon écriture à ce sujet ici. (J'ai lu cet article au début de mes recherches.)
En fait, il existe des implémentations pour 25 langues différentes disponibles sur https://rosettacode.org/wiki/Voronoi_diagram
Par exemple pour 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);
}
}
C'est le plus rapide possible - c'est un simple voronoi mais il a fière allure. Il divise les espaces en une grille, place un point dans chaque cellule de la grille placé au hasard et se déplace le long de la grille en vérifiant les cellules 3x3 pour trouver comment il se rapporte aux cellules adjacentes.
C'est plus rapide sans le gradient.
Vous pouvez vous demander quel serait le voronoi 3d le plus simple. Ce serait fascinant de savoir. Probablement 3x3x3 cellules et vérification du gradient.
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 );
}
et voici la même chose avec la distance chebychev. vous pouvez utiliser un bruit flottant random2f 2d à partir d'ici:
https://www.shadertoy.com/view/Msl3DM
edit: je l'ai converti en code de type C
C'était il y a quelque temps, pour le bénéfice de ceux qui l'ont fait, je pense que c'est 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 );
}
Vérifiez la solution de force brute présentée avec un pseudo-code par Richard Franks dans sa réponse à la question Comment puis-je dériver un diagramme de Voronoi étant donné son ensemble de points et sa triangulation de Delaunay?
L'algorithme le plus simple vient de la définition d'un diagramme de voronoi: "La partition d'un plan avec n points en polygones convexes de telle sorte que chaque polygone contient exactement un point générateur et chaque point dans un polygone donné est plus proche de son point générateur qu'à toute autre. "définition de wolfram.
La partie importante ici est que chaque point est plus proche du point de génération que tout autre, à partir de là, l'algorithme est très simple:
Si vous voulez un diagramme de couleurs, associez une couleur à chaque point de génération et une couleur à chaque pixel avec sa couleur associée au point de génération le plus proche. Et c'est tout, ce n'est pas efficace mais très facile à mettre en œuvre.
Trouvé cette excellente bibliothèque C # sur le code Google basé sur l'algorithme de Fortune/l'algorithme de ligne de balayage
https://code.google.com/p/fortune-voronoi/
Il vous suffit de créer une liste. Un vecteur peut être créé en passant deux nombres (coordonnées) comme flottant. Passez ensuite la liste dans Fortune.ComputeVoronoiGraph ()
Vous pouvez comprendre un peu plus le concept de l'algorithme à partir de ces pages wikipedia:
http://en.wikipedia.org/wiki/Fortune%27s_algorithm
http://en.wikipedia.org/wiki/Sweep_line_algorithm
Bien qu'une chose que je n'ai pas pu comprendre est de savoir comment créer une ligne pour des arêtes partiellement infinies (je ne sais pas grand-chose sur la géométrie des coordonnées :-)). Si quelqu'un le sait, faites-le moi savoir également.
Si vous essayez de le dessiner sur une image, vous pouvez utiliser un algorithme de remplissage par saturation basé sur la file d'attente.
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'utilisation d'une file d'attente garantira que les régions se répartissent en parallèle, minimisant le nombre total de visites de pixels. Si vous utilisez une pile, le premier point remplira toute l'image, puis le second remplira tous les pixels plus proches que le premier point. Cela continuera, augmentant considérablement le nombre de visites. L'utilisation d'une file d'attente FIFO traite les pixels dans l'ordre dans lequel ils sont poussés. Les images résultantes seront à peu près les mêmes que vous utilisiez la pile ou la file d'attente, mais le big-O pour la file d'attente est beaucoup plus proche de linéaire (par rapport au nombre de pixels de l'image) que le big-O de l'algorithme de pile. L'idée générale est que les régions se propageront au même rythme et que les collisions se produiront généralement exactement aux points qui correspondent aux limites des régions.