Voronoi diyagramının uygulanması için en kolay algoritma? [kapalı]


88

Voronoi diyagramını uygulamak için kolay algoritmalar nelerdir?

Özellikle sözde formda herhangi bir algoritma bulamadım. Lütfen Voronoi diyagram algoritması, öğretici vb.'nin bazı bağlantılarını paylaşın.


Yanıtlar:


32

Bir nokta kümesinin Delaunay üçgenlemesini hesaplamak için kolay bir algoritma, kenarları çevirmektir . Bir Delaunay üçgenlemesi, bir Voronoi diyagramının ikili grafiği olduğundan, diyagramı üçgenlemeden doğrusal zamanda oluşturabilirsiniz.

Ne yazık ki, saygısız yaklaşımın en kötü durumdaki çalışma süresi O (n ^ 2) 'dir. Fortune'un satır taraması gibi, O (n log n) süresini alan daha iyi algoritmalar mevcuttur. Yine de bunu uygulamak biraz zor. Tembel iseniz (benim gibi), Delaunay üçgenlemesinin mevcut bir uygulamasını aramanızı, onu kullanmanızı ve ardından ikili grafiği hesaplamayı öneririm.

Genel olarak, konuyla ilgili iyi bir kitap de Berg ve ark. Tarafından yazılan Hesaplamalı Geometri'dir .


19

En kolay? Kaba kuvvet yaklaşımı budur: Çıktınızdaki her piksel için, tüm noktalarda yineleyin, mesafeyi hesaplayın, en yakın olanı kullanın. Olabildiğince yavaş ama çok basit. Performans önemli değilse, işi yapar. Ben de ilginç bir iyileştirme üzerinde çalışıyorum, ancak yine de başka birinin aynı (oldukça açık) fikre sahip olup olmadığını araştırıyorum.


14

Bowyer-Watson algoritmasının anlaşılması oldukça kolaydır. İşte bir uygulama: http://paulbourke.net/papers/triangulate/ . Bu, bir dizi nokta için bir delaunay üçgenlemesidir, ancak bunu delaunay'ın çiftini, yani bir voronoi diyagramını elde etmek için kullanabilirsiniz. BTW. minimum kapsayan ağaç, delaunay üçgenlemesinin bir alt kümesidir.


12

Bir voronoi diyagramı oluşturmak için en etkili algoritma , Fortune algoritmasıdır . O (n log n) ile çalışır.

İşte C'deki referans uygulamasına bir bağlantı .

Şahsen , genişletmeyi daha kolay bulduğum için Bill Simons ve Carson Farmer'ın python uygulamasını gerçekten seviyorum .


c-uygulamasına bağlantı artık çalışmıyor gibi görünüyor :(
FutureCake



9

Stephan Fortune / Shane O'Sullivan'dan C ve C ++ 'da 2-b grafikler için ücretsiz bir voronoi uygulaması var:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

Bunu birçok yerde bulacaksınız. Yani http://www.skynet.ie/~sos/masters/ adresinde


4
Yaygın olarak referans verilen, belgelenmemiş ve bu koda dayalı olarak gördüğüm neredeyse her yeniden uygulama yanlıştır (farklı dillerde, birçok insan Voronoi'ye ihtiyaç duyar, çok azı bunu doğru şekilde taşımak için yeterince iyi anlayabilir). Gördüğüm tek çalışan bağlantı noktaları bilim / akademi topluluğundan ve çok fazla karmaşık işlev imzalarına sahip - veya büyük ölçüde optimize edilmiş (böylece çoğu amaç için kullanılamazlar) normal programcılar tarafından kullanılamaz hale geliyor.
Adam

VoronoiDiagramGenerator.cpp sınırlı işlevselliğe sahiptir. Sırasız bir kenar seti çıkaracaktır. Bundan gerçek çokgenleri çıkarmak önemsiz değildir. Artı tarafta, sınırlayıcı bir dikdörtgene karşı bir klibe sahiptir, böylece sonsuzluk noktaları oluşturulmaz.
Bram 15


6

Asıl soru Voronoi'nin nasıl uygulanacağını sorarken, bu konuda bilgi ararken aşağıdakileri söyleyen bir gönderi bulsaydım, bana çok zaman kazandırırdı:

Voronoi diyagramlarını uygulamak için internette pek çok "neredeyse doğru" C ++ kodu var. Çoğu, tohum noktaları çok yoğunlaştığında nadiren başarısızlıkları tetikler. İnternette bulduğunuz herhangi bir kodu, üzerinde çok fazla zaman harcamadan, bitmiş projenizde kullanmayı beklediğiniz puan sayısı ile kapsamlı bir şekilde test etmenizi tavsiye ederim.

Çevrimiçi bulduğum uygulamaların en iyisi, şuradan bağlanan MapManager programının bir parçasıydı: http://www.skynet.ie/~sos/mapviewer/voronoi.php Çoğunlukla işe yarıyor, ancak işlem yaparken aralıklı diyagram bozulması alıyorum 10 ^ 6 puan sipariş edin. Yolsuzluğun nasıl içeri sızdığını tam olarak çözemedim.

Dün gece bunu buldum: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm "The Boost.Polygon Voronoi kütüphanesi". Çok umut verici görünüyor. Bu, doğruluğunu ve mükemmel performansa sahip olduğunu kanıtlamak için karşılaştırma testleri ile birlikte gelir. Kütüphane, uygun bir arayüze ve belgelere sahiptir. Bu kütüphaneyi daha önce bulamadığıma şaşırdım, dolayısıyla burada yazıyorum. (Bu yazıyı araştırmamın başlarında okudum.)


4

Bu mümkün olan en hızlısı - basit bir voronoi ama harika görünüyor. Boşlukları bir ızgaraya böler, rastgele yerleştirilmiş her ızgara hücresine bir nokta yerleştirir ve bitişik hücrelerle nasıl ilişkili olduğunu bulmak için 3x3 hücreleri kontrol ederek ızgara boyunca hareket eder.

Gradyan olmadan daha hızlıdır.

En kolay 3d voronoi'nin ne olacağını sorabilirsiniz. Bilmek büyüleyici olurdu. Muhtemelen 3x3x3 hücre ve kontrol gradyanı.

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 );
}

ve burada chebychev mesafesi ile aynıdır. Buradan random2f 2d float gürültüsü kullanabilirsiniz:

https://www.shadertoy.com/view/Msl3DM

edit: Bunu C benzeri koda dönüştürdüm

Bu bir süre önceydi, bunu yapanların yararına, bunun harika olduğuna inanıyorum:

 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 );
 }

1
Neden kendini açıklayıcı olmayan bu kadar çok tek harfli değişken kullanıyorsun? Ve ne ivec2? veya vec2? Bu okunamaz.
shinzou

1
İyi bir nokta, i de onunla bütün gün mücadele düşünüyorum: answers.unity3d.com/questions/638662/... kodla bu metni güncellenen
aliential

4

Aslında https://rosettacode.org/wiki/Voronoi_diagram adresinde 25 farklı dil için uygulamalar mevcuttur.

Örneğin Java için:

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);
    }
}

3

En basit algoritma, bir voronoi diyagramının tanımından gelir: "N noktalı bir düzlemin dışbükey çokgenlere bölünmesi, öyle ki her çokgen tam olarak bir üretme noktası içerir ve belirli bir çokgendeki her nokta, üretim noktasına diğerlerinden daha yakın olur. . "wolfram'dan tanım.

Buradaki önemli kısım, her noktanın üretim noktasına diğerlerinden daha yakın olmasıdır, buradan itibaren algoritma çok basittir:

  1. Bir dizi üretme noktasına sahip olun.
  2. Tuvalinizdeki her pikseli döndürün.
  3. Her piksel için ona en yakın üretim noktasını arayın.
  4. Pikseli renklendirmek istediğiniz diyagrama bağlı olarak. Kenarlıkla ayrılmış bir diyagram istiyorsanız, ikinci ve en yakın noktayı kontrol edin, ardından bazı değerlerden küçükse kenarlık rengiyle farklarını ve rengini kontrol edin.

Bir renk diyagramı istiyorsanız, her bir oluşturma noktasıyla ilişkilendirilmiş bir renge sahip olun ve her pikseli en yakın üreten nokta ilişkili renkle renklendirin. Ve bununla ilgili, verimli değil ama uygulaması çok kolay.



0

Bu mükemmel C # kitaplığını, Fortune algoritması / Tarama hattı algoritmasına dayalı olarak Google kodunda buldum

https://code.google.com/p/fortune-voronoi/

Sadece bir Liste oluşturmanız yeterlidir. Float olarak iki sayı (koordinatlar) geçirilerek bir Vektör oluşturulabilir. Ardından listeyi Fortune.ComputeVoronoiGraph () 'a iletin

Algoritma kavramını şu Wikipedia sayfalarından biraz daha anlayabilirsiniz:

http://en.wikipedia.org/wiki/Fortune%27s_algorithm

http://en.wikipedia.org/wiki/Sweep_line_algorithm

Yine de anlayamadığım bir şey, Kısmen Sonsuz kenarlar için bir çizginin nasıl oluşturulacağıydı (koordinat geometrisi hakkında fazla bir şey bilmiyorum :-)). Birisi biliyorsa, lütfen bunu da bana bildirin.


2
Bu bağlantılar soruyu cevaplayabilirken, cevabın temel kısımlarını buraya eklemek ve referans için bağlantıyı sağlamak daha iyidir. Bağlantılı sayfa değişirse yalnızca bağlantı yanıtları geçersiz hale gelebilir.
Kmeixner

0

Bir görüntüye çizmeye çalışıyorsanız, kuyruk tabanlı bir sel doldurma algoritması kullanabilirsiniz.

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; 

}

Sıra kullanmak, bölgelerin paralel olarak yayılmasını sağlayarak toplam piksel ziyaret sayısını en aza indirir. Yığın kullanırsanız, ilk nokta tüm görüntüyü doldurur, ardından ikincisi, ona birinci noktadan daha yakın olan pikselleri doldurur. Bu devam edecek ve ziyaret sayılarını büyük ölçüde artıracak. Bir FIFO kuyruğu kullanmak, pikselleri itildikleri sırayla işler. Sonuçta elde edilen görüntüler, yığın veya sıra kullansanız da kabaca aynı olacaktır, ancak kuyruk için büyük-O, yığın algoritmasının büyük-O'suna göre doğrusalya (görüntü piksel sayısına göre) çok daha yakındır. Genel fikir, bölgelerin aynı oranda yayılacağı ve çarpışmaların genellikle bölge sınırlarına karşılık gelen noktalarda gerçekleşeceği yönündedir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.