อัลกอริทึมที่ง่ายที่สุดของแผนภาพ Voronoi ในการนำไปใช้? [ปิด]


88

อัลกอริทึมง่ายๆในการใช้แผนภาพ Voronoi คืออะไร?

ฉันไม่พบอัลกอริทึมใดเป็นพิเศษในรูปแบบหลอก โปรดแบ่งปันลิงก์บางส่วนของอัลกอริทึมแผนภาพ Voronoi บทช่วยสอนและอื่น ๆ


1
ดูโค้ดและ API ได้ที่saturnapi.com/vpartition/voronoi-seed-partition-plot
FullStack

คำตอบ:


32

อัลกอริทึมที่ง่ายในการคำนวณการหาค่าสามเหลี่ยมของชุดจุด Delaunay คือ ขอบพลิก เนื่องจากรูปสามเหลี่ยม Delaunay เป็นกราฟคู่ของแผนภาพ Voronoi คุณสามารถสร้างแผนภาพจากการหารูปสามเหลี่ยมในเวลาเชิงเส้น

น่าเสียดายที่ในกรณีที่เลวร้ายที่สุดเวลาทำงานของวิธีพลิกคือ O (n ^ 2) อัลกอริทึมที่ดีกว่าเช่นการกวาดสายของ Fortune มีอยู่ซึ่งใช้เวลา O (n log n) สิ่งนี้ค่อนข้างยุ่งยากในการนำไปใช้ หากคุณขี้เกียจ (เหมือนฉัน) ฉันขอแนะนำให้มองหาการใช้งานสามเหลี่ยม Delaunay ที่มีอยู่แล้วใช้มันแล้วคำนวณกราฟคู่

โดยทั่วไปหนังสือที่ดีในหัวข้อนี้คือComputational Geometryโดย de Berg et al


19

ง่ายที่สุด? นั่นคือวิธีการบังคับที่ดุร้าย: สำหรับแต่ละพิกเซลในผลลัพธ์ของคุณให้วนซ้ำทุกจุดคำนวณระยะทางใช้ใกล้เคียงที่สุด ช้าเท่าที่จะทำได้ แต่เรียบง่ายมาก หากประสิทธิภาพไม่สำคัญก็จะได้ผล ฉันได้ทำการปรับแต่งที่น่าสนใจด้วยตัวเอง แต่ยังคงค้นหาว่ามีใครมีความคิดแบบเดียวกัน (ค่อนข้างชัดเจน) บ้าง


14

อัลกอริทึม Bowyer-Watson ค่อนข้างเข้าใจง่าย นี่คือการดำเนินการ: http://paulbourke.net/papers/triangulate/ มันเป็นรูปสามเหลี่ยมแบบ delaunay สำหรับชุดของจุด แต่คุณสามารถใช้มันเพื่อหาคู่ของ delaunay นั่นคือ voronoi-diagram BTW. ต้นไม้ที่ทอดข้ามขั้นต่ำเป็นส่วนย่อยของการหารูปสามเหลี่ยมที่ล่าช้า


12

อัลกอริทึมที่มีประสิทธิภาพที่สุดในการสร้างแผนภาพโวโรโนอิคืออัลกอริทึมของฟอร์จูนอัลกอริทึมฟอร์จูนมันทำงานใน O (n log n)

นี่คือลิงค์ไปยังการใช้งานอ้างอิงของเขาใน CC

โดยส่วนตัวแล้วฉันชอบการใช้งาน pythonของ Bill Simons และ Carson Farmer มากเพราะฉันพบว่ามันง่ายกว่าที่จะขยาย


ลิงก์ไปยังการใช้งาน c ดูเหมือนจะใช้งานไม่ได้อีกต่อไป :(
FutureCake

1
@FutureCake คลังอินเทอร์เน็ตเพื่อช่วยเหลือ: web.archive.org/web/20181018224943/http://ect.bell-labs.com/who/…
polettix


9

มีการใช้งาน voronoi อย่างอิสระสำหรับกราฟ 2 มิติใน C และใน C ++ จาก Stephan Fortune / Shane O'Sullivan:

VoronoiDiagramGenerator.cpp 

VoronoiDiagramGenerator.h 

คุณจะพบได้ในหลาย ๆ ที่ เช่นที่http://www.skynet.ie/~sos/masters/


4
มีการอ้างอิงอย่างกว้างขวางไม่มีเอกสารและเกือบทุกการใช้งานซ้ำที่ฉันเคยเห็นตามรหัสนี้ไม่ถูกต้อง (ในภาษาต่างๆหลายคนต้องการ Voronoi มีเพียงไม่กี่คนที่เข้าใจดีพอที่จะพอร์ตได้อย่างถูกต้อง) พอร์ตการทำงานเดียวที่ฉันเคยเห็นมาจากชุมชนวิทยาศาสตร์ / สถาบันการศึกษาและมีลายเซ็นฟังก์ชันที่ซับซ้อนมากเกินไป - หรือปรับให้เหมาะสมอย่างหนาแน่น (เพื่อให้ไม่สามารถใช้เพื่อวัตถุประสงค์ส่วนใหญ่ได้) ทำให้โปรแกรมเมอร์ทั่วไปไม่สามารถใช้งานได้
Adam

VoronoiDiagramGenerator.cpp มีฟังก์ชันที่ จำกัด มันจะส่งออกชุดขอบที่ไม่เรียงลำดับ ในการแยกรูปหลายเหลี่ยมจริงออกจากสิ่งนี้ไม่ใช่เรื่องเล็กน้อย ในด้านบวกจะมีคลิปตัดกับสี่เหลี่ยมผืนผ้าล้อมรอบดังนั้นจึงไม่มีการสร้างจุดอินฟินิตี้
Bram


6

ในขณะที่คำถามเดิมถามเกี่ยวกับวิธีใช้งาน Voronoi แต่ฉันพบโพสต์ที่กล่าวต่อไปนี้เมื่อฉันค้นหาข้อมูลเกี่ยวกับเรื่องนี้มันจะช่วยให้ฉันประหยัดเวลาได้มาก:

มีโค้ด C ++ ที่ "เกือบถูกต้อง" บนอินเทอร์เน็ตสำหรับการใช้งานไดอะแกรม Voronoi ส่วนใหญ่แทบจะไม่เกิดความล้มเหลวเมื่อจุดเมล็ดมีความหนาแน่นมาก ฉันอยากจะแนะนำให้ทดสอบรหัสใด ๆ ที่คุณพบทางออนไลน์โดยมีจำนวนคะแนนที่คุณคาดว่าจะใช้ในโครงการที่เสร็จแล้วก่อนที่คุณจะเสียเวลากับมันมากเกินไป

การใช้งานที่ดีที่สุดที่ฉันพบทางออนไลน์เป็นส่วนหนึ่งของโปรแกรม 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 "The Boost.Polygon Voronoi library" มันดูมีอนาคตมาก สิ่งนี้มาพร้อมกับการทดสอบเกณฑ์มาตรฐานเพื่อพิสูจน์ความแม่นยำและประสิทธิภาพที่ยอดเยี่ยม ไลบรารีมีอินเทอร์เฟซและเอกสารที่เหมาะสม ฉันแปลกใจที่ไม่พบห้องสมุดนี้มาก่อนจึงเขียนถึงที่นี่ (ฉันอ่านโพสต์นี้ก่อนในการค้นคว้าของฉัน)


4

นี่เป็นวิธีที่เร็วที่สุด - มันเป็น voronoi ที่เรียบง่าย แต่ดูดี มันแบ่งช่องว่างออกเป็นตารางวางจุดในแต่ละเซลล์กริดที่วางแบบสุ่มและเลื่อนไปตามตารางตรวจสอบเซลล์ 3x3 เพื่อค้นหาว่าเกี่ยวข้องกับเซลล์ที่อยู่ติดกันอย่างไร

เร็วขึ้นโดยไม่ต้องไล่ระดับสี

คุณอาจถามว่า voronoi 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 );
}

และนี่ก็เหมือนกันกับระยะทาง chebychev คุณสามารถใช้เสียงลอยตัวแบบ random2f 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 );
 }

1
เหตุใดคุณจึงใช้ตัวแปรตัวอักษรเดียวจำนวนมากที่ไม่สามารถอธิบายตนเองได้ แล้วอะไรivec2ล่ะ? หรือvec2? ไม่สามารถอ่านได้
ชินโซ

1
จุดดีฉันคิดว่าฉันต้องดิ้นรนทั้งวันด้วยเช่นกัน: answers.unity3d.com/questions/638662/…อัปเดตข้อความนี้ด้วยรหัส
aliential

4

จริงๆแล้วมีการใช้งานสำหรับ 25 ภาษาที่แตกต่างกันในhttps://rosettacode.org/wiki/Voronoi_diagram

เช่นสำหรับ 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. ขึ้นอยู่กับไดอะแกรมที่คุณต้องการให้พิกเซลเป็นสี หากคุณต้องการให้ไดอะแกรมคั่นด้วยเส้นขอบให้ตรวจสอบจุดที่สองถึงใกล้ที่สุดจากนั้นตรวจสอบความแตกต่างและสีด้วยสีของเส้นขอบหากมีขนาดเล็กกว่าค่าบางค่า

หากคุณต้องการไดอะแกรมสีให้มีสีที่เกี่ยวข้องกับทุกจุดที่สร้างและระบายสีทุกพิกเซลโดยมีจุดสร้างสีที่สัมพันธ์กันมากที่สุด เกี่ยวกับเรื่องนี้มันไม่มีประสิทธิภาพ แต่ใช้งานง่ายมาก



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 ประมวลผลพิกเซลตามลำดับที่พุช ภาพที่ได้จะใกล้เคียงกันไม่ว่าคุณจะใช้สแต็กหรือคิว แต่ big-O สำหรับคิวนั้นใกล้เคียงกับเชิงเส้นมาก (สัมพันธ์กับจำนวนพิกเซลของภาพ) มากกว่า big-O ของสแต็ก algoritm แนวคิดทั่วไปคือพื้นที่จะแพร่กระจายในอัตราเดียวกันและการชนกันโดยทั่วไปจะเกิดขึ้นที่จุดที่สอดคล้องกับขอบเขตของภูมิภาค

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.