Quels sont les algorithmes simples pour implémenter le diagramme de Voronoi?
Je n'ai trouvé aucun algorithme spécialement sous forme de pseudo. Veuillez partager quelques liens d'algorithme de diagramme de Voronoi, tutoriel, etc.
Quels sont les algorithmes simples pour implémenter le diagramme de Voronoi?
Je n'ai trouvé aucun algorithme spécialement sous forme de pseudo. Veuillez partager quelques liens d'algorithme de diagramme de Voronoi, tutoriel, etc.
Réponses:
Un algorithme simple pour calculer la triangulation de Delaunay d'un ensemble de points consiste à inverser les bords . Puisqu'une triangulation de Delaunay est le double graphe d'un diagramme de Voronoi, vous pouvez construire le diagramme à partir de la triangulation en temps linéaire.
Malheureusement, le pire des cas d'exécution de l'approche de retournement est O (n ^ 2). De meilleurs algorithmes tels que le balayage de ligne de Fortune existent, qui prennent du temps O (n log n). Ceci est cependant un peu délicat à mettre en œuvre. Si vous êtes paresseux (comme je le suis), je suggérerais de rechercher une implémentation existante d'une triangulation de Delaunay, de l'utiliser, puis de calculer le double graphe.
En général, un bon livre sur le sujet est Computational Geometry de de Berg et al.
Le plus simple? C'est l'approche de la 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 le travail. 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 de 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 la triangulation de delaunay.
L'algorithme le plus efficace pour construire un diagramme voronoï est l'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 beaucoup l' implémentation python de Bill Simons et Carson Farmer, car je l'ai trouvé plus facile à étendre.
La page Wikipédia ( http://en.wikipedia.org/wiki/Voronoi_diagram ) contient une section Algorithmes avec des liens vers des algorithmes pour la mise en œuvre des diagrammes de Voronoi.
Il existe une implémentation voronoi disponible gratuitement pour les graphes 2D en C et en C ++ de Stephan Fortune / Shane O'Sullivan:
VoronoiDiagramGenerator.cpp
VoronoiDiagramGenerator.h
Vous le trouverez à de nombreux endroits. Ie à http://www.skynet.ie/~sos/masters/
Voici une implémentation javascript qui utilise quat-tree et permet une construction incrémentielle.
Alors que la question initiale demande comment mettre en œuvre 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 d'amorçage deviennent très denses. Je recommanderais de tester de manière approfondie tout code que vous trouvez 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 une corruption de diagramme intermittente lorsque je traite avec commandez 10 ^ 6 points. Je n'ai pas été en mesure de comprendre 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 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ù mes écrits à 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);
}
}
L'algorithme le plus simple provient de la définition d'un diagramme de voronoï: "Le partitionnement d'un plan à n points en polygones convexes de telle sorte que chaque polygone contient exactement un point générateur et chaque point d'un polygone donné est plus proche de son point générateur que de tout autre . "définition de wolfram.
La partie importante ici est que chaque point est plus proche du point générateur 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 colorez chaque pixel avec la couleur associée au point de génération le plus proche. Et c'est à peu près tout, ce n'est pas efficace mais très facile à mettre en œuvre.
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ée 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 dégradé.
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 de Chebychev. vous pouvez utiliser un bruit flottant random2f 2d à partir d'ici:
https://www.shadertoy.com/view/Msl3DM
edit: j'ai converti ceci en code comme C
C'était il y a quelque temps, pour le bénéfice de ceux qui le disent, 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 );
}
ivec2
? ou vec2
? Ceci est illisible.
Vérifiez la solution de force brute présentée avec pseudo-code par Richard Franks dans sa réponse à la question Comment dériver un diagramme de Voronoi étant donné son ensemble de points et sa triangulation de Delaunay?
J'ai trouvé cette excellente bibliothèque C # sur le code Google basée 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) sous forme de 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, c'est comment créer une ligne pour les arêtes partiellement infinies (je ne sais pas grand-chose sur la géométrie des coordonnées :-)). Si quelqu'un sait, merci de me le faire savoir également.
Si vous essayez de le dessiner sur une image, vous pouvez utiliser un algorithme de remplissage 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 de lui 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 pile ou file d'attente, mais le big-O pour la file d'attente est beaucoup plus proche du linéaire (en relation avec le 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.