Найпростіший для реалізації алгоритм діаграми Вороного? [зачинено]


88

Які прості алгоритми для реалізації діаграми Вороного?

Я не міг знайти жодного алгоритму спеціально в псевдо формі. Будь ласка, поділіться деякими посиланнями алгоритму діаграми Вороного, навчального посібника тощо.


1
Дивіться код та API за адресою saturnapi.com/vpartition/voronoi-seed-partition-plot
FullStack

Відповіді:


32

Простим алгоритмом для обчислення триангуляції Делоне множини точок є гортання ребер . Оскільки тріангуляція Делоне є подвійним графіком діаграми Вороного, ви можете побудувати діаграму з тріангуляції за лінійний час.

На жаль, найгірший час роботи підхоплюючого підходу - O (n ^ 2). Існують кращі алгоритми, такі як розгортка рядків Фортуни, які займають час O (n log n). Це дещо складно реалізувати. Якщо ви ліниві (як і я), я б запропонував шукати існуючу реалізацію триангуляції Делоне, використовувати її, а потім обчислити подвійний графік.

Загалом, хороша книга на цю тему - « Обчислювальна геометрія » де Берга та співавт.


19

Найпростіший? Це підхід грубої сили: Для кожного пікселя у вашому виході перебирайте всі точки, обчислюйте відстань, використовуйте найближчу. Як можна повільніше, але дуже просто. Якщо продуктивність не важлива, це робить свою справу. Я сам працював над цікавим вдосконаленням, але все ще шукав, чи хтось ще не мав тієї ж (досить очевидної) ідеї.


14

Алгоритм Бойєра-Ватсона зрозуміти досить просто. Ось реалізація: http://paulbourke.net/papers/triangulate/ . Це тріангуляція делоне для набору точок, але ти можеш використовувати його для отримання дуалу делоне, тобто вороной-діаграми. До речі. мінімальне дерево, що охоплює, - це підмножина триангуляції делоне.


12

Найефективнішим алгоритмом побудови діаграми Вороного є алгоритм Фортуни . Він працює в O (n log n).

Ось посилання на його еталонної реалізації в C .

Особисто мені дуже подобається реалізація python Біллом Саймонсом та Карсоном Фармером, оскільки мені було простіше розширити.


посилання на реалізацію c, здається, вже не працює :(
FutureCake

1
Інтернет-архів @FutureCake на порятунок: web.archive.org/web/20181018224943/http://ect.bell-labs.com/who/…
polettix


9

Існує вільно доступна реалізація voronoi для 2-d графіків на C та на C ++ від Stephan Fortune / Shane O'Sullivan:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

Ви знайдете його в багатьох місцях. Тобто за адресою http://www.skynet.ie/~sos/masters/


4
Широкі посилання, недокументи та майже кожна повторна реалізація, яку я бачив на основі цього коду, є помилковою (різними мовами багатьом людям потрібен Voronoi, мало хто може його зрозуміти настільки добре, щоб правильно портувати). Єдині робочі порти, які я бачив, - це науково-наукове співтовариство і мають надто складні підписи функцій - або масово оптимізовані (так що їх не можна використовувати для більшості цілей), що робить їх непридатними для звичайних програмістів.
Адам

VoronoiDiagramGenerator.cpp має обмежені функціональні можливості. Він виведе невпорядкований набір країв. Витягувати з цього фактичні багатокутники нетривіально. З позитивного боку, він має кліп проти обмежуючого прямокутника, тому не створюються точки нескінченності.
Брем


6

Хоча оригінальне питання задає питання про те, як реалізувати Voronoi, якби я знайшов допис, в якому говорилося наступне, коли я шукав інформацію на цю тему, це заощадило б мені багато часу:

В Інтернеті є багато «майже правильного» коду на C ++ для реалізації діаграм Вороного. Більшість з них рідко викликають відмови, коли насіннєві точки стають дуже щільними. Я б порадив широко протестувати будь-який код, який ви знайдете в Інтернеті, з кількістю балів, які ви очікуєте використовувати у своєму готовому проекті, перш ніж витрачати на це занадто багато часу.

Найкраща реалізація, яку я знайшов в Інтернеті, була частиною програми MapManager, на яку посилається тут: http://www.skynet.ie/~sos/mapviewer/voronoi.php Це здебільшого працює, але при роботі з порядок 10 ^ 6 балів. Я не зміг точно з’ясувати, як корупція закрадається.

Вчора ввечері я знайшов це: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm "Бібліотека Boost.Polygon Voronoi". Це виглядає дуже перспективно. Це поставляється з контрольними тестами, щоб довести свою точність та чудові показники. Бібліотека має належний інтерфейс та документацію. Я здивований, що раніше не знайшов цієї бібліотеки, отже, і написав про неї тут. (Я прочитав цю публікацію на початку свого дослідження.)


4

Насправді на сайті https://rosettacode.org/wiki/Voronoi_diagram доступні реалізації для 25 різних мов.

Наприклад, для 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);
    }
}

3

Найпростіший алгоритм походить із визначення діаграми Вороного: "Розбиття площини з n точками на опуклі багатокутники, такі що кожен багатокутник містить рівно одну твірну точку, і кожна точка даного багатокутника ближче до своєї твірної точки, ніж до будь-якої іншої . "визначення з вольфраму.

Важлива частина тут полягає в тому, що кожна точка знаходиться ближче до точки генерації, ніж будь-яка інша, звідси алгоритм дуже простий:

  1. Мати масив генеруючих точок.
  2. Прокрутіть кожен піксель на вашому полотні.
  3. Для кожного пікселя шукайте найближчу до нього точку генерації.
  4. Залежно від того, на якій діаграмі ви хочете отримати кольоровий піксель. Якщо ви хочете, щоб діаграма була розділена межею, перевірте другу, найближчу точку, а потім перевірте їх різницю та колір кольором межі, якщо вона менша за якесь значення.

Якщо ви хочете отримати кольорову діаграму, тоді колір має бути пов’язаний з кожною генеруючою точкою, а кожен піксель - кольором, пов’язаним з найближчою генеруючою точкою. І це все, це не ефективно, але дуже просто у впровадженні.


3

Це найшвидше з можливих - це простий вороний, але виглядає чудово. Він розділяє простір на сітку, розміщує крапку в кожній випадково розміщеній комірці сітки та рухається вздовж сітки, перевіряючи клітинки 3x3, щоб дізнатись, як вона відноситься до сусідніх комірок.

Це швидше без градієнта.

Ви можете запитати, що було б найпростішим 3d-вороноі. Це було б захоплююче знати. Можливо, клітинки 3x3x3 та градієнт перевірки.

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

і тут те саме з чебичевською дистанцією. Ви можете використовувати випадковий 2d 2d поплавковий шум звідси:

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

редагувати: я перетворив це на код типу C

Це було деякий час тому, на користь тих, хто що, я вважаю, що це круто:

 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? чи vec2? Це не читається.
shinzou

Хороший момент, я думаю, що я теж з цим боровся цілий день: answer.unity3d.com/questions/638662/… оновив цей текст кодом
алієнт


0

Знайшов цю чудову бібліотеку C # у коді google на основі алгоритму Fortune / алгоритму розгортки

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

Вам просто потрібно створити Список. Вектор можна створити, передавши два числа (координати) як плаваючі. Потім передайте список у Fortune.ComputeVoronoiGraph ()

Ви можете зрозуміти концепцію алгоритму трохи більше на цих сторінках Вікіпедії:

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

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

Хоча однієї речі, яку я не зміг зрозуміти, це те, як створити лінію для Частково Нескінченних ребер (не знаю багато про геометрію координат :-)). Якщо хтось знає, будь ласка, повідомте мені це також.


2
Хоча ці посилання можуть відповісти на питання, краще включити сюди основні частини відповіді та надати посилання для довідки. Відповіді лише на посилання можуть стати недійсними, якщо пов’язана сторінка зміниться.
Kmeixner

0

Якщо ви намагаєтеся намалювати його на зображенні, ви можете використовувати алгоритм заповнення затоплення, що базується на черзі.

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; 

}

Використання черги забезпечить паралельне розповсюдження регіонів, мінімізуючи загальну кількість відвідувань пікселів. Якщо ви використовуєте стек, перша точка заповнить ціле зображення, а друга - пікселі, розташовані ближче до нього, ніж перша точка. Це продовжуватиметься, значно збільшуючи кількість відвідувань. Використання черги FIFO обробляє пікселі в тому порядку, в якому вони були розсунуті. Отримані зображення будуть приблизно однаковими, незалежно від того, чи використовуєте ви стек або чергу, але велике значення O для черги набагато ближче до лінійного (щодо кількості пікселів зображення), ніж велике O алгоритму стека. Загальна ідея полягає в тому, що регіони будуть поширюватися з однаковою швидкістю, і зіткнення, як правило, відбуватимуться саме в точках, що відповідають межам регіону.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.