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.

fireball003
sumber
1
Lihat kode dan API di saturnapi.com/vpartition/voronoi-seed-partition-plot
FullStack

Jawaban:

32

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.

rodion
sumber
19

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).

Suricou Raven
sumber
14

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.

Gigamegs
sumber
12

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.

Marco Pashkov
sumber
tautan ke implementasi-c sepertinya tidak berfungsi lagi :(
FutureCake
1
@FutureCake Internet Archive to the rescue: web.archive.org/web/20181018224943/http://ect.bell-labs.com/who/…
polettix
9

Ada implementasi voronoi yang tersedia secara gratis untuk grafik 2-d di C dan di C ++ dari Stephan Fortune / Shane O'Sullivan:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

Anda akan menemukannya di banyak tempat. Yaitu di http://www.skynet.ie/~sos/masters/

ADAIR LEMBUT MERAH
sumber
4
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
6

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.)

mrdunk
sumber
4

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

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:

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:

 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 );
 }
aliential
sumber
1
Mengapa Anda menggunakan begitu banyak variabel satu huruf yang tidak cukup jelas? Dan apa ivec2? atau vec2? Ini tidak bisa dibaca.
shinzou
1
Poin bagus, saya pikir saya berjuang sepanjang hari dengannya juga: answer.unity3d.com/questions/638662/… memperbarui teks ini dengan kode
aliential
4

Sebenarnya ada implementasi untuk 25 bahasa berbeda yang tersedia di https://rosettacode.org/wiki/Voronoi_diagram

Misalnya untuk 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);
    }
}
0xBADF00D
sumber
3

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:

  1. Memiliki berbagai titik pembangkit.
  2. Ulangi setiap piksel di kanvas Anda.
  3. Untuk setiap piksel, cari titik penghasil terdekat.
  4. 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.

Alon Kellner
sumber
0

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.

Hetansh
sumber
2
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.

RollerSimmer
sumber