Thuật toán dễ thực hiện nhất của sơ đồ Voronoi? [đóng cửa]


88

Các thuật toán dễ thực hiện sơ đồ Voronoi là gì?

Tôi không thể tìm thấy bất kỳ thuật toán nào đặc biệt ở dạng giả. Xin chia sẻ một số liên kết của thuật toán sơ đồ Voronoi, hướng dẫn, v.v.


Câu trả lời:


32

Một thuật toán dễ dàng để tính toán tam giác Delaunay của một tập điểm là lật các cạnh . Vì tam giác Delaunay là biểu đồ kép của biểu đồ Voronoi, bạn có thể xây dựng biểu đồ từ tam giác theo thời gian tuyến tính.

Thật không may, thời gian chạy trong trường hợp xấu nhất của phương pháp lật là O (n ^ 2). Các thuật toán tốt hơn như quét dòng của Fortune tồn tại, mất O (n log n) thời gian. Tuy nhiên, điều này hơi khó thực hiện. Nếu bạn lười biếng (như tôi), tôi khuyên bạn nên tìm cách triển khai hiện có của tam giác Delaunay, sử dụng nó, rồi tính toán đồ thị kép.

Nói chung, một cuốn sách hay về chủ đề này là Hình học tính toán của de Berg et al.


19

Dễ nhất? Đó là cách tiếp cận brute-force: Đối với mỗi pixel trong đầu ra của bạn, hãy lặp lại qua tất cả các điểm, tính toán khoảng cách, sử dụng điểm gần nhất. Chậm nhất có thể, nhưng rất đơn giản. Nếu hiệu suất không quan trọng, nó sẽ thực hiện công việc. Bản thân tôi đang làm việc trên một cải tiến thú vị, nhưng vẫn đang tìm kiếm để xem có ai khác có cùng ý tưởng (khá rõ ràng) hay không.


14

Thuật toán Bowyer-Watson khá dễ hiểu. Đây là cách triển khai: http://paulbourke.net/papers/triangulate/ . Đó là một tam giác delaunay cho một tập hợp điểm nhưng bạn có thể sử dụng nó để lấy điểm kép của delaunay, tức là một sơ đồ voronoi. BTW. cây bao trùm tối thiểu là một tập con của tam giác delaunay.


12

Thuật toán hiệu quả nhất để xây dựng một biểu đồ voronoi là thuật toán của Fortune . Nó chạy trong O (n log n).

Dưới đây là một liên kết đến ông thực hiện tham chiếu trong C .

Cá nhân tôi thực sự thích việc triển khai python bởi Bill Simons và Carson Farmer, vì tôi thấy nó dễ dàng mở rộng hơn.


liên kết đến triển khai c dường như không hoạt động nữa :(
FutureCake

1
@FutureCake Internet Lưu trữ để giải cứu: web.archive.org/web/20181018224943/http://ect.bell-labs.com/who/...
polettix


9

Có một triển khai voronoi có sẵn miễn phí cho đồ thị 2-d trong C và trong C ++ từ Stephan Fortune / Shane O'Sullivan:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

Bạn sẽ tìm thấy nó ở nhiều nơi. Tức là tại http://www.skynet.ie/~sos/masters/


4
Được tham chiếu rộng rãi, không có tài liệu và gần như mọi lần thực hiện lại mà tôi đã thấy dựa trên mã này đều sai (trong các ngôn ngữ khác nhau, nhiều người cần Voronoi, ít người có thể hiểu nó đủ tốt để chuyển chính xác). Các cổng hoạt động duy nhất mà tôi đã thấy là từ cộng đồng khoa học / học viện và có các chữ ký chức năng quá phức tạp - hoặc được tối ưu hóa hàng loạt (để chúng không thể được sử dụng cho hầu hết các mục đích) khiến chúng không thể sử dụng được bởi các lập trình viên bình thường.
Adam

VoronoiDiagramGenerator.cpp có chức năng hạn chế. Nó sẽ xuất ra một tập hợp các cạnh không có thứ tự. Để trích xuất các đa giác thực tế từ điều này là không tầm thường. Về mặt tích cực, nó có một clip dựa trên một hình chữ nhật bao quanh, vì vậy không có điểm vô cực nào được tạo ra.
Bram


6

Trong khi câu hỏi ban đầu hỏi về cách triển khai Voronoi, tôi đã tìm thấy một bài đăng nói như sau khi tôi tìm kiếm thông tin về chủ đề này, nó sẽ giúp tôi tiết kiệm rất nhiều thời gian:

Có rất nhiều mã C ++ "gần đúng" trên internet để triển khai sơ đồ Voronoi. Hầu hết đều hiếm khi gây ra thất bại khi các điểm hạt giống dày đặc. Tôi khuyên bạn nên kiểm tra bất kỳ mã nào bạn tìm thấy trên mạng với số điểm bạn mong đợi để sử dụng trong dự án đã hoàn thành của mình trước khi bạn lãng phí quá nhiều thời gian vào nó.

Cách triển khai tốt nhất mà tôi tìm thấy trực tuyến là một phần của chương trình MapManager được liên kết từ đây: http://www.skynet.ie/~sos/mapviewer/voronoi.php Nó hầu như hoạt động nhưng tôi bị lỗi sơ đồ không liên tục khi xử lý đặt hàng 10 ^ 6 điểm. Tôi không thể tìm ra chính xác cách thức mà tham nhũng đang len lỏi vào.

Đêm qua tôi đã tìm thấy cái này: http://www.boost.org/doc/libs/1_53_0_beta1/libs/polygon/doc/voronoi_main.htm "Thư viện Boost.Polygon Voronoi". Nó trông rất hứa hẹn. Điều này đi kèm với các bài kiểm tra điểm chuẩn để chứng minh độ chính xác và hiệu suất tuyệt vời. Thư viện có giao diện và tài liệu phù hợp. Tôi ngạc nhiên vì không tìm thấy thư viện này trước đây, do đó tôi viết về nó ở đây. (Tôi đã đọc bài đăng này sớm trong quá trình nghiên cứu của mình.)


4

Trên thực tế, có các triển khai cho 25 ngôn ngữ khác nhau có sẵn trên https://rosettacode.org/wiki/Voronoi_diagram

Ví dụ: đối với 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

Thuật toán đơn giản nhất xuất phát từ định nghĩa của biểu đồ voronoi: "Việc phân chia một mặt phẳng có n điểm thành các đa giác lồi sao cho mỗi đa giác chứa chính xác một điểm sinh và mọi điểm trong một đa giác nhất định gần với điểm sinh của nó hơn bất kỳ điểm nào khác . "định nghĩa từ wolfram.

Phần quan trọng ở đây là mọi điểm đều gần với điểm tạo hơn bất kỳ điểm nào khác, từ đây thuật toán rất đơn giản:

  1. Có một loạt các điểm tạo.
  2. Lặp lại từng pixel trên canvas của bạn.
  3. Đối với mỗi pixel, hãy tìm điểm tạo gần nhất với nó.
  4. Tùy thuộc vào sơ đồ bạn muốn lấy màu pixel. Nếu bạn muốn một sơ đồ được phân tách bằng đường viền, hãy kiểm tra điểm thứ hai đến điểm gần nhất, sau đó kiểm tra sự khác biệt và màu sắc của chúng với màu đường viền nếu nó nhỏ hơn một số giá trị.

Nếu bạn muốn có một sơ đồ màu thì hãy có một màu được liên kết với mọi điểm tạo và tô màu cho mọi pixel với màu liên kết với điểm tạo gần nhất của nó. Và đó là về nó, nó không hiệu quả nhưng rất dễ thực hiện.


3

Đây là cách nhanh nhất có thể - đó là một voronoi đơn giản nhưng nó trông rất tuyệt. Nó chia các không gian thành một lưới, đặt một dấu chấm trong mỗi ô lưới được đặt ngẫu nhiên và di chuyển dọc theo lưới kiểm tra các ô 3x3 để tìm xem nó liên quan như thế nào với các ô liền kề.

Nó nhanh hơn mà không có gradient.

Bạn có thể hỏi voronoi 3d dễ nhất là gì. Sẽ rất thú vị nếu biết. Có lẽ là ô 3x3x3 và kiểm tra độ dốc.

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

và ở đây cũng vậy với khoảng cách chebychev. bạn có thể sử dụng tiếng ồn float ngẫu nhiên 2f từ đây:

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

chỉnh sửa: Tôi đã chuyển đổi mã này thành mã C like

Đây là một thời gian trước đây, vì lợi ích của những người làm việc đó, tôi tin rằng điều này thật tuyệt:

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

Tại sao bạn sử dụng quá nhiều biến một chữ cái mà không tự giải thích? Và những gì ivec2? hoặc vec2? Điều này là không thể đọc được.
shinzou

Điểm tốt, tôi nghĩ rằng tôi phải vật lộn cả ngày với nó quá: answers.unity3d.com/questions/638662/... cập nhật văn bản này với mã
aliential


0

Tìm thấy thư viện C # tuyệt vời này trên mã google dựa trên thuật toán Fortune / Thuật toán dòng quét

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

Bạn chỉ cần tạo một Danh sách. Một Vector có thể được tạo bằng cách chuyển hai số (tọa độ) dưới dạng float. Sau đó chuyển danh sách vào Fortune.ComputeVoronoiGraph ()

Bạn có thể hiểu khái niệm thuật toán nhiều hơn một chút từ các trang wikipedia này:

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

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

Mặc dù một điều tôi không thể hiểu là làm thế nào để tạo một đường cho các cạnh Vô hạn một phần (không biết nhiều về hình học tọa độ :-)). Nếu ai đó biết, xin vui lòng cho tôi biết điều đó.


2
Mặc dù các liên kết này có thể trả lời câu hỏi, nhưng tốt hơn là bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo. Các câu trả lời chỉ có liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
Kmeixner

0

Nếu bạn đang cố gắng vẽ nó thành hình ảnh, bạn có thể sử dụng thuật toán lấp đầy lũ lụt dựa trên hàng đợi.

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ử dụng hàng đợi sẽ đảm bảo rằng các vùng trải dài song song, giảm thiểu tổng số lượt truy cập pixel. Nếu bạn sử dụng ngăn xếp, điểm đầu tiên sẽ lấp đầy toàn bộ hình ảnh, sau đó điểm thứ hai sẽ lấp đầy bất kỳ pixel nào gần nó hơn điểm đầu tiên. Điều này sẽ tiếp tục, tăng đáng kể số lượt truy cập. Sử dụng hàng đợi FIFO xử lý các pixel theo thứ tự mà chúng được đẩy. Các hình ảnh kết quả sẽ gần giống nhau cho dù bạn sử dụng ngăn xếp hay hàng đợi, nhưng big-O cho hàng đợi gần với tuyến tính hơn (liên quan đến số pixel hình ảnh) so với big-O của ngăn xếp algoritm. Ý tưởng chung là các vùng sẽ lan truyền với tốc độ như nhau và các vụ va chạm nói chung sẽ xảy ra chính xác tại các điểm tương ứng với ranh giới vùng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.