Algoritma diagram Voronoi termudah untuk diimplementasikan? [Tutup]
88
Apa algoritma mudah untuk mengimplementasikan diagram Voronoi?
Saya tidak dapat menemukan algoritme apa pun secara khusus dalam bentuk semu. Silakan bagikan beberapa tautan algoritma diagram Voronoi, tutorial, dll.
Algoritme yang mudah untuk menghitung triangulasi Delaunay dari kumpulan titik adalah membalik tepi . Karena triangulasi Delaunay adalah grafik rangkap dari diagram Voronoi, Anda dapat membuat diagram dari triangulasi tersebut dalam waktu linier.
Sayangnya, waktu berjalan kasus terburuk dari pendekatan membalik adalah O (n ^ 2). Ada algoritma yang lebih baik seperti sapuan garis Fortune, yang membutuhkan waktu O (n log n). Ini agak sulit untuk diterapkan. Jika Anda malas (seperti saya), saya sarankan mencari implementasi yang ada dari triangulasi Delaunay, gunakan, lalu hitung grafik ganda.
Secara umum, buku yang bagus tentang topik ini adalah Computational Geometry oleh de Berg et al.
Paling mudah? Itulah pendekatan brute-force: Untuk setiap piksel dalam output Anda, lakukan iterasi melalui semua titik, hitung jarak, gunakan yang terdekat. Lambat mungkin, tetapi sangat sederhana. Jika kinerja tidak penting, itu berhasil. Saya sendiri sedang mengerjakan perbaikan yang menarik, tetapi masih mencari untuk melihat apakah ada orang lain yang memiliki ide yang sama (agak jelas).
Algoritma Bowyer-Watson cukup mudah dipahami. Berikut adalah implementasinya: http://paulbourke.net/papers/triangulate/ . Ini adalah triangulasi penundaan untuk sekumpulan poin tetapi Anda dapat menggunakannya untuk mendapatkan dua penundaan, yaitu diagram voronoi. BTW. pohon rentang minimum adalah bagian dari triangulasi penundaan.
Direferensikan secara luas, tidak berdokumen, dan hampir setiap implementasi ulang yang pernah saya lihat berdasarkan kode ini salah (dalam berbagai bahasa, banyak orang membutuhkan Voronoi, hanya sedikit yang dapat memahaminya dengan cukup baik untuk melakukan porting dengan benar). Satu-satunya port yang berfungsi yang pernah saya lihat berasal dari komunitas sains / akademisi dan memiliki tanda tangan fungsi yang sangat rumit - atau dioptimalkan secara masif (sehingga tidak dapat digunakan untuk sebagian besar tujuan) membuatnya tidak dapat digunakan oleh programmer normal.
Adam
VoronoiDiagramGenerator.cpp memiliki fungsionalitas terbatas. Ini akan menampilkan satu set tepi yang tidak berurutan. Untuk mengekstrak poligon sebenarnya dari ini tidak sepele. Di sisi positifnya, itu menampilkan klip pada persegi panjang pembatas, jadi tidak ada titik tak terhingga yang dihasilkan.
Bram
8
Berikut adalah implementasi javascript yang menggunakan quat-tree dan memungkinkan konstruksi tambahan.
Sementara pertanyaan asli menanyakan tentang bagaimana menerapkan Voronoi, jika saya menemukan sebuah posting yang mengatakan berikut ini ketika saya sedang mencari info tentang hal ini, itu akan menghemat banyak waktu saya:
Ada banyak kode C ++ yang "hampir benar" di internet untuk mengimplementasikan diagram Voronoi. Kebanyakan jarang memicu kegagalan ketika seed point menjadi sangat padat. Saya akan merekomendasikan untuk menguji kode apa pun yang Anda temukan online secara ekstensif dengan jumlah poin yang ingin Anda gunakan dalam proyek Anda yang sudah selesai sebelum Anda membuang terlalu banyak waktu untuk itu.
Implementasi terbaik yang saya temukan secara online adalah bagian dari program MapManager yang ditautkan dari sini:
http://www.skynet.ie/~sos/mapviewer/voronoi.php
Sebagian besar berfungsi tetapi saya mengalami kerusakan diagram intermiten saat menangani pesan 10 ^ 6 poin. Saya belum bisa memastikan bagaimana tepatnya korupsi merayap.
Tadi malam saya menemukan ini:
http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm
"The Boost.Polygon Voronoi library". Ini terlihat sangat menjanjikan. Ini dilengkapi dengan tes benchmark untuk membuktikan keakuratannya dan memiliki kinerja yang hebat. Perpustakaan memiliki antarmuka dan dokumentasi yang tepat. Saya terkejut saya tidak menemukan perpustakaan ini sebelumnya, oleh karena itu saya menulis tentangnya di sini. (Saya membaca posting ini di awal penelitian saya.)
Ini adalah yang tercepat - ini adalah voronoi sederhana tetapi terlihat bagus. Ini membagi ruang menjadi kisi, menempatkan titik di setiap sel kisi ditempatkan secara acak dan bergerak di sepanjang kisi memeriksa sel 3x3 untuk menemukan hubungannya dengan sel yang berdekatan.
Lebih cepat tanpa gradien.
Anda mungkin bertanya apa voronoi 3d termudah. Akan sangat menarik untuk diketahui. Mungkin sel 3x3x3 dan memeriksa gradien.
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 );
}
dan di sini sama dengan jarak chebychev. Anda dapat menggunakan suara float random2f 2d dari sini:
edit: Saya telah mengubahnya menjadi C seperti kode
Ini sudah beberapa waktu yang lalu, untuk kepentingan mereka yang apa, saya yakin ini keren:
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 );
}
Algoritme paling sederhana berasal dari definisi diagram voronoi: "Pemartisian bidang dengan n titik menjadi poligon cembung sehingga setiap poligon mengandung tepat satu titik pembangkitan dan setiap titik dalam poligon tertentu lebih dekat ke titik pembangkitannya daripada titik lainnya . "definisi dari wolfram.
Bagian penting di sini adalah tentang setiap titik yang lebih dekat ke titik pembangkitan daripada yang lain, dari sini algoritme sangat sederhana:
Memiliki berbagai titik pembangkit.
Ulangi setiap piksel di kanvas Anda.
Untuk setiap piksel, cari titik penghasil terdekat.
Bergantung pada diagram apa yang Anda inginkan untuk mewarnai piksel. Jika Anda ingin diagram dipisahkan dengan bingkai, periksa titik terdekat kedua, lalu periksa perbedaan dan warnanya dengan warna bingkai jika lebih kecil dari beberapa nilai.
Jika Anda menginginkan diagram warna, maka miliki warna yang terkait dengan setiap titik penghasil dan warna setiap piksel dengan warna terkait titik penghasil terdekat. Dan hanya itu, ini tidak efisien tetapi sangat mudah diterapkan.
Anda hanya perlu membuat Daftar. Vektor dapat dibuat dengan memasukkan dua angka (koordinat) sebagai float. Kemudian berikan daftar tersebut ke Fortune.ComputeVoronoiGraph ()
Anda dapat memahami lebih banyak tentang konsep algoritme dari halaman wikipedia ini:
Meskipun satu hal yang tidak dapat saya pahami adalah cara membuat garis untuk tepi Sebagian Tak Terbatas (tidak tahu banyak tentang geometri koordinat :-)). Jika seseorang tahu, beri tahu saya juga.
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik menyertakan bagian penting dari jawaban di sini dan menyediakan tautan untuk referensi. Jawaban link saja bisa menjadi tidak valid jika halaman tertaut berubah.
Kmeixner
0
Jika Anda mencoba menggambarnya ke gambar, Anda dapat menggunakan algoritme pengisian banjir berbasis antrean.
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;
}
Menggunakan antrean akan memastikan bahwa wilayah tersebar secara paralel, meminimalkan jumlah total kunjungan piksel. Jika Anda menggunakan tumpukan, titik pertama akan mengisi seluruh gambar, kemudian titik kedua akan mengisi piksel apa pun yang lebih dekat daripada titik pertama. Ini akan berlanjut, sangat meningkatkan jumlah kunjungan. Menggunakan antrian FIFO memproses piksel dalam urutan yang mereka dorong. Gambar yang dihasilkan akan kurang lebih sama apakah Anda menggunakan stack atau queue, tetapi big-O untuk antrian jauh lebih dekat ke linier (dalam kaitannya dengan jumlah piksel gambar) daripada big-O algoritme stack. Ide umumnya adalah bahwa wilayah akan menyebar dengan kecepatan yang sama dan tabrakan umumnya akan terjadi tepat di titik yang sesuai dengan batas wilayah.
Jawaban:
Algoritme yang mudah untuk menghitung triangulasi Delaunay dari kumpulan titik adalah membalik tepi . Karena triangulasi Delaunay adalah grafik rangkap dari diagram Voronoi, Anda dapat membuat diagram dari triangulasi tersebut dalam waktu linier.
Sayangnya, waktu berjalan kasus terburuk dari pendekatan membalik adalah O (n ^ 2). Ada algoritma yang lebih baik seperti sapuan garis Fortune, yang membutuhkan waktu O (n log n). Ini agak sulit untuk diterapkan. Jika Anda malas (seperti saya), saya sarankan mencari implementasi yang ada dari triangulasi Delaunay, gunakan, lalu hitung grafik ganda.
Secara umum, buku yang bagus tentang topik ini adalah Computational Geometry oleh de Berg et al.
sumber
Paling mudah? Itulah pendekatan brute-force: Untuk setiap piksel dalam output Anda, lakukan iterasi melalui semua titik, hitung jarak, gunakan yang terdekat. Lambat mungkin, tetapi sangat sederhana. Jika kinerja tidak penting, itu berhasil. Saya sendiri sedang mengerjakan perbaikan yang menarik, tetapi masih mencari untuk melihat apakah ada orang lain yang memiliki ide yang sama (agak jelas).
sumber
Algoritma Bowyer-Watson cukup mudah dipahami. Berikut adalah implementasinya: http://paulbourke.net/papers/triangulate/ . Ini adalah triangulasi penundaan untuk sekumpulan poin tetapi Anda dapat menggunakannya untuk mendapatkan dua penundaan, yaitu diagram voronoi. BTW. pohon rentang minimum adalah bagian dari triangulasi penundaan.
sumber
Algoritma yang paling efisien untuk membuat diagram voronoi adalah algoritma Fortune . Ini berjalan di O (n log n).
Berikut ini adalah link untuk nya implementasi referensi di C .
Secara pribadi saya sangat menyukai implementasi python oleh Bill Simons dan Carson Farmer, karena saya merasa lebih mudah untuk memperpanjang.
sumber
Halaman Wikipedia ( http://en.wikipedia.org/wiki/Voronoi_diagram ) memiliki bagian Algoritma dengan link ke algoritma untuk mengimplementasikan diagram Voronoi.
sumber
Ada implementasi voronoi yang tersedia secara gratis untuk grafik 2-d di C dan di C ++ dari Stephan Fortune / Shane O'Sullivan:
Anda akan menemukannya di banyak tempat. Yaitu di http://www.skynet.ie/~sos/masters/
sumber
Berikut adalah implementasi javascript yang menggunakan quat-tree dan memungkinkan konstruksi tambahan.
http://code.google.com/p/javascript-voronoi/
sumber
Sementara pertanyaan asli menanyakan tentang bagaimana menerapkan Voronoi, jika saya menemukan sebuah posting yang mengatakan berikut ini ketika saya sedang mencari info tentang hal ini, itu akan menghemat banyak waktu saya:
Ada banyak kode C ++ yang "hampir benar" di internet untuk mengimplementasikan diagram Voronoi. Kebanyakan jarang memicu kegagalan ketika seed point menjadi sangat padat. Saya akan merekomendasikan untuk menguji kode apa pun yang Anda temukan online secara ekstensif dengan jumlah poin yang ingin Anda gunakan dalam proyek Anda yang sudah selesai sebelum Anda membuang terlalu banyak waktu untuk itu.
Implementasi terbaik yang saya temukan secara online adalah bagian dari program MapManager yang ditautkan dari sini: http://www.skynet.ie/~sos/mapviewer/voronoi.php Sebagian besar berfungsi tetapi saya mengalami kerusakan diagram intermiten saat menangani pesan 10 ^ 6 poin. Saya belum bisa memastikan bagaimana tepatnya korupsi merayap.
Tadi malam saya menemukan ini: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm "The Boost.Polygon Voronoi library". Ini terlihat sangat menjanjikan. Ini dilengkapi dengan tes benchmark untuk membuktikan keakuratannya dan memiliki kinerja yang hebat. Perpustakaan memiliki antarmuka dan dokumentasi yang tepat. Saya terkejut saya tidak menemukan perpustakaan ini sebelumnya, oleh karena itu saya menulis tentangnya di sini. (Saya membaca posting ini di awal penelitian saya.)
sumber
Ini adalah yang tercepat - ini adalah voronoi sederhana tetapi terlihat bagus. Ini membagi ruang menjadi kisi, menempatkan titik di setiap sel kisi ditempatkan secara acak dan bergerak di sepanjang kisi memeriksa sel 3x3 untuk menemukan hubungannya dengan sel yang berdekatan.
Lebih cepat tanpa gradien.
Anda mungkin bertanya apa voronoi 3d termudah. Akan sangat menarik untuk diketahui. Mungkin sel 3x3x3 dan memeriksa gradien.
http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm
dan di sini sama dengan jarak chebychev. Anda dapat menggunakan suara float random2f 2d dari sini:
https://www.shadertoy.com/view/Msl3DM
edit: Saya telah mengubahnya menjadi C seperti kode
Ini sudah beberapa waktu yang lalu, untuk kepentingan mereka yang apa, saya yakin ini keren:
sumber
ivec2
? atauvec2
? Ini tidak bisa dibaca.Sebenarnya ada implementasi untuk 25 bahasa berbeda yang tersedia di https://rosettacode.org/wiki/Voronoi_diagram
Misalnya untuk Java:
sumber
Algoritme paling sederhana berasal dari definisi diagram voronoi: "Pemartisian bidang dengan n titik menjadi poligon cembung sehingga setiap poligon mengandung tepat satu titik pembangkitan dan setiap titik dalam poligon tertentu lebih dekat ke titik pembangkitannya daripada titik lainnya . "definisi dari wolfram.
Bagian penting di sini adalah tentang setiap titik yang lebih dekat ke titik pembangkitan daripada yang lain, dari sini algoritme sangat sederhana:
Jika Anda menginginkan diagram warna, maka miliki warna yang terkait dengan setiap titik penghasil dan warna setiap piksel dengan warna terkait titik penghasil terdekat. Dan hanya itu, ini tidak efisien tetapi sangat mudah diterapkan.
sumber
Periksa solusi brute-force yang disajikan dengan pseudo-code oleh Richard Franks dalam jawabannya pada pertanyaan Bagaimana saya mendapatkan diagram Voronoi berdasarkan kumpulan titik dan triangulasi Delaunaynya?
sumber
Menemukan perpustakaan C # yang sangat baik ini di kode google berdasarkan algoritma Fortune / algoritma garis Sapu
https://code.google.com/p/fortune-voronoi/
Anda hanya perlu membuat Daftar. Vektor dapat dibuat dengan memasukkan dua angka (koordinat) sebagai float. Kemudian berikan daftar tersebut ke Fortune.ComputeVoronoiGraph ()
Anda dapat memahami lebih banyak tentang konsep algoritme dari halaman wikipedia ini:
http://en.wikipedia.org/wiki/Fortune%27s_algorithm
http://en.wikipedia.org/wiki/Sweep_line_algorithm
Meskipun satu hal yang tidak dapat saya pahami adalah cara membuat garis untuk tepi Sebagian Tak Terbatas (tidak tahu banyak tentang geometri koordinat :-)). Jika seseorang tahu, beri tahu saya juga.
sumber
Jika Anda mencoba menggambarnya ke gambar, Anda dapat menggunakan algoritme pengisian banjir berbasis antrean.
Menggunakan antrean akan memastikan bahwa wilayah tersebar secara paralel, meminimalkan jumlah total kunjungan piksel. Jika Anda menggunakan tumpukan, titik pertama akan mengisi seluruh gambar, kemudian titik kedua akan mengisi piksel apa pun yang lebih dekat daripada titik pertama. Ini akan berlanjut, sangat meningkatkan jumlah kunjungan. Menggunakan antrian FIFO memproses piksel dalam urutan yang mereka dorong. Gambar yang dihasilkan akan kurang lebih sama apakah Anda menggunakan stack atau queue, tetapi big-O untuk antrian jauh lebih dekat ke linier (dalam kaitannya dengan jumlah piksel gambar) daripada big-O algoritme stack. Ide umumnya adalah bahwa wilayah akan menyebar dengan kecepatan yang sama dan tabrakan umumnya akan terjadi tepat di titik yang sesuai dengan batas wilayah.
sumber