- Fortune’s Algorithm Voronoi Diagram को O(n log n) समय में बना सकता है, लेकिन इसे लागू करना कठिन है; इसलिए जब तक बड़े पैमाने के diagrams को बार-बार generate करने की जरूरत न हो, O(n²) implementation या कोई library ज्यादा व्यावहारिक है
- Voronoi Diagram कई site के आधार पर plane को सबसे नजदीकी क्षेत्रों में बांटता है, और boundaries उन points से बनती हैं जो दो sites से समान दूरी पर होते हैं
- algorithm बाएं से दाएं चलने वाली sweep line और parabolic arcs की front line, यानी beachline, को maintain करता है, और केवल site event व circle event को process करता है
- site event नया arc insert करके मौजूदा arc को divide करता है और incomplete edge बनाता है, जबकि circle event बीच वाले arc को हटाते हुए Voronoi Vertex और half edge को complete करता है
- practical implementation में event queue, beachline, incomplete edge map और DCEL को साथ संभालना पड़ता है; invalid circle events हटाना और बचे हुए edges को साफ करना complexity को काफी बढ़ा देता है
Implementation की कठिनाई और scope
- Fortune’s Algorithm Voronoi Diagram को O(n log n) समय में generate करने वाला algorithm है
- अगर मकसद real-world use है, तो सीधे implement करने के बजाय पहले required scale पर विचार करना बेहतर है
- अगर हर सेकंड कई बड़े diagrams generate करने की जरूरत नहीं है, तो O(n²) implementation आसान विकल्प हो सकता है
- और ज्यादा practical alternative मौजूदा library का उपयोग है
- algorithm का visual output दिलचस्प होता है, लेकिन implementation process कठिन और काफी frustrating हो सकता है
Voronoi Diagram का basic concept
- Voronoi Diagram plane को कई regions में बांटने का तरीका है, और procedural map generation में अक्सर इस्तेमाल होता है
- input के रूप में चुने गए points को site या seed कहा जाता है
- हर site से जुड़ा cell plane पर उन points का set है जो उस site के सबसे नजदीक होते हैं
- cells की boundary उन points से बनी होती है जो दो sites से समान दूरी पर होते हैं
- cells के edges जहां मिलते हैं, वह Voronoi Vertex तीन sites से समान दूरी वाला point होता है
sweep line, beachline, event
- Fortune’s Algorithm बाएं से दाएं move करने वाली vertical line, यानी sweep line, का इस्तेमाल करता है
- जब sweep line किसी site से मिलती है, तो उस site को focus मानकर parabolic arc बनता है, और sweep line दूर जाने पर arc बड़ा होता जाता है
- अलग-अलग sites के दो arcs जहां मिलते हैं, वह point दोनों sites से समान दूरी पर होता है, इसलिए cell boundary बनता है
- जब दो boundaries मिलती हैं, तो diagram का vertex बनता है
- active arcs की front line को beachline कहा जाता है
- actual implementation में sweep line को pixel-by-pixel move नहीं किया जाता; केवल calculable specific points यानी event process किए जाते हैं
- site event: पहले से ज्ञात site coordinates से defined होता है, और process होने पर beachline में नया arc add होता है
- circle event: beachline के तीन arcs से defined होता है, और process होने पर एक arc हटता है तथा Voronoi Vertex और half edge बनते हैं
parabola से boundary ढूंढना
- algorithm में parabola को सामान्य
y = ax^2 + bx + cform के बजाय locus definition से handle किया जाता है - parabola एक focus point और एक directrix से defined होता है
- focus point site होता है
- directrix sweep line होती है
- same sweep line को directrix के रूप में इस्तेमाल करने वाले दो parabolas का intersection दो sites से समान दूरी पर होता है
- इसलिए दो parabolas का intersection निकालने पर दो sites के बीच का equiedge मिल सकता है
- parabola का x coordinate calculate करने का pseudocode, और sweep line की position बदलने पर दो parabolas का intersection boundary के साथ कैसे move करता है, इसका example इस्तेमाल किया गया है
beachline representation और site event processing
- beachline का हर arc केवल संबंधित site के coordinates से represent किया जा सकता है
- sweep line सभी arcs पर common रूप से लागू होती है
- implementation में arc को अलग object नहीं, बल्कि 2D coordinate के रूप में handle किया जाता है
- beachline को simple points के order के रूप में represent किया जा सकता है
- उदाहरण:
[arc1, arc2],[arc1, arc2, arc3] - same site का arc beachline में कई बार appear हो सकता है
- उदाहरण:
[arc1, arc3, arc1, arc2]
- उदाहरण:
- site event होने पर, नए site से बाईं ओर line खींचने पर beachline का जो arc मिलता है, उसे ढूंढा जाता है, और नया arc उस arc को split करता है
- नया site
Lअगर existing beachline[.., i, j, k, ..]केjको split करे, तो structure[.., i, j, L, j, k, ..]बन जाता है - sites x-coordinate order में queue में जाते हैं, और process होने पर beachline व event candidates update होते हैं
circle event और circumcircle
- beachline के तीन arcs
[.., i, j, k, ..]में जब दो boundaries मिलती हैं, तो बीच वाला arcjगायब हो जाता है - उस समय तीन sites से गुजरने वाला circumcircle मौजूद होता है, और circle का center तीनों sites से समान दूरी पर होता है
- circumcircle का center Voronoi Vertex बनता है
- circle event को circle के right endpoint, यानी circle point, के आधार पर event queue में रखा जाता है
- अगर कोई नया site circle point तक पहुंचने से पहले circle के अंदर मिल जाए, तो existing circle event invalid हो जाता है
- क्योंकि नया site बीच वाले arc को पहले split कर देता है, इसलिए तीन arcs का combination अब बना नहीं रहता
- existing triple
i, j, kगायब हो जाता है औरi, j, L,L, j, kजैसे नए triples को check करना पड़ता है
incomplete edge और half edge
- incomplete edge ऐसी line है जिसका एक endpoint fixed है, लेकिन दूसरा endpoint दो parabolic arcs के intersection से defined होता है
- site event में नया arc insert होने पर दो incomplete edges बनते हैं
- fixed point वह coordinate है जहां नया arc existing beachline से मिला था
- अगर नया arc
jexisting arciको split करे, तो[i, j],[j, i]intersections से संबंधित edges बनते हैं
- circle event में जब दो incomplete edges collide करते हैं, तो collision point Voronoi Vertex बनता है
- existing incomplete edge इस point पर half edge के रूप में complete हो जाता है, और नए adjacent हुए दो arcs के बीच नया incomplete edge बनता है
केवल counterclockwise circles ही circle event बनते हैं
- जब beachline में
[i, j, k, j, i]हो, तोijkऔरkjiदोनों circle बना सकते हैं, लेकिन दोनों valid circle event नहीं होते - middle arc तभी गायब होता है जब boundaries वास्तव में converge करने वाली दिशा में हों
- program में तीन points की orientation determinant से तय की जाती है
- determinant negative हो तो counterclockwise direction है और circle event बनता है
- determinant positive हो तो clockwise direction है और circle event नहीं बनता
- determinant 0 हो तो तीनों points collinear हैं, इसलिए circle नहीं है
पूरा algorithm flow
- input sites को x-coordinate के आधार पर sort करके site event के रूप में queue में डाला जाता है
- queue खाली होने तक next event निकालकर process किया जाता है
- site event processing:
- आगे बचे हुए circle events में से वे events हटाए जाते हैं जिनके circle के अंदर नया site आ जाता है
- beachline में वह arc ढूंढा जाता है जिसे नया site split करेगा
- नया arc insert करके existing arc को split किया जाता है
- दो incomplete edges add किए जाते हैं
- नए बने triples circle event बना सकते हैं या नहीं, यह check किया जाता है
- circle event processing:
- circumcircle center को Voronoi Vertex के रूप में add किया जाता है
- middle arc को beachline से हटाया जाता है
- हटाए गए arc के कारण invalid होने वाले future circle events हटाए जाते हैं
- नए adjacent हुए arcs के triples check करके circle event add किया जाता है
- queue खाली होने पर बचे हुए incomplete edges को diagram boundary तक extend किया जाता है, और boundary से मिलने वाली जगह पर Voronoi Vertex बनाया जाता है
Odin implementation के data structures
- example implementation C alternative language Odin में लिखा गया है
- पूरा code RedPenguin101/voronoi repository में है
- basic types:
V2:[2]intform वाला 2D pointPointPair: दोV2का pairEvent:{site: bool, a, b, c: V2}struct
Eventका अर्थ type के अनुसार बदलता है- site event में
asite coordinate होता है औरb,cइस्तेमाल नहीं होते - circle event में
a,b,cevent बनाने वाले beachline के तीन arcs होते हैं
- site event में
Fortunestruct यह state manage करता हैbeachline:V2arrayqueue:Eventarrayincomplete_edges:PointPair -> V2mapvd: Voronoi Diagram store करने वाला DCEL
Implementation में छोड़े गए या simplify किए गए हिस्से
- beachline को vector से represent किया गया है, लेकिन efficiency बढ़ाने के लिए binary tree ज्यादा suitable है
- event queue conceptually priority queue है, लेकिन example implementation में इसे array में sorted insertion तरीके से handle किया गया है
- circle event invalidation future events को iterate करके check करने का तरीका अपनाता है, और इसमें faster method की जरूरत का TODO है
clean_beachline_edgesbeachline के दोनों ends से unnecessary arcs काटने की procedure है- implementation में same x-coordinate वाले sites, circle point और site के same होने, reference point collisions जैसी edge cases शामिल हैं
- queue खाली होने के बाद बचे हुए incomplete edges, twin के बिना half edges और vertices को clean up करने वाला final step केवल simple math processing के रूप में handle किया गया है
DCEL से Voronoi Diagram store करना
- Voronoi Diagram आमतौर पर Doubly Connected Edge List(DCEL) में store किया जाता है
- DCEL vertex और edge से बने cell-complex को आसानी से manipulate करने के लिए represent करने वाला data structure है
- यह edge-centric representation है, लेकिन vertex और face information भी साथ store करता है
- सामान्य edge directionless होता है, लेकिन DCEL में हर edge को दोनों directions के दो half edge के रूप में store किया जाता है
- Voronoi Diagram में DCEL में store होने वाला vertex site नहीं, बल्कि Voronoi Vertex होता है
- edge
Eका destinationE.twin.originसे मिलता है, और right faceE.twin.leftसे मिलता है
1 टिप्पणियां
Hacker News की टिप्पणियां
कुछ समय पहले मैंने ClojureScript में Fortune algorithm के चलते हुए process को animation के रूप में दिखाने वाला implementation बनाया था: https://voronoi.ajwerner.net/#/app-diagrams
यह सचमुच एक खूबसूरत algorithm है
हालांकि उस project के बाद मुझे Fortune algorithm से थोड़ी चिढ़ हो गई, क्योंकि इसकी floating-point numerical stability अच्छी नहीं है
अगर points एक ही सीधी रेखा पर हों, या floating-point के हिसाब से लगभग सीधी रेखा पर हों, तो यह टूट सकता है
अगर मुझे सही याद है, तो इस मामले में delaunator बेहतर है: https://github.com/mapbox/delaunator
reference page पर “old” implementation का link दिख रहा है; जानना चाहूंगा कि मौजूदा animation version को भी open source के रूप में जारी करने की कोई संभावना है या नहीं
कुछ साल पहले मैंने ऐसी 3D visualization बनाई थी: https://x.com/KangarooPhysics/status/1253336959755251716
uBlock Origin के लिए मशहूर Raymond Hill का JavaScript implementation है: https://github.com/gorhill/Javascript-Voronoi
यहां मैंने इसे थोड़ा बदलकर move करने लायक बनाया है: https://animations.adgent.com/voronoi.html
सोचता हूं कि क्या video को input के रूप में लेकर उसे Voronoi तरीके से दिखाने वाले algorithm में डाला जा सकता है
उस point पर शायद वह strictly Voronoi diagram न रहे, लेकिन काफी शानदार दिखेगा
D3.js में एक नया implementation है: https://github.com/d3/d3-delaunay
उस page के नीचे इस्तेमाल किए गए sweep algorithm की explanation और JavaScript के अलावा दूसरी languages में implementations की list है
पुराना d3-voronoi deprecated होने वाला है, लेकिन यहां देखा जा सकता है: https://github.com/d3/d3-voronoi
अगर edges में दिलचस्पी नहीं है और बस हर point को अलग रंग से रंगना है, तो seed points से शुरू होने वाला flood fill variant इस्तेमाल किया जा सकता है
pixel को stack में तभी डालें जब उस color की दूरी उस pixel पर पहले से लगे color से कम हो
apex के ऊपर से 2D orthographic projection में render करने पर z-buffer सबसे नजदीकी vertex का pixel सुरक्षित रखेगा
shader से भी करने का तरीका होगा, लेकिन classic 3D cone demo समझने और implement करने में बहुत आसान है
दिलचस्प है कि D3, Fortune algorithm से https://mapbox.github.io/delaunator/ पर चला गया
वजह यह बताई गई है कि “Delaunay triangulation या Voronoi diagram बनाते समय यह d3-voronoi से 5~10 गुना तेज, numerically ज्यादा robust है, इसमें Canvas rendering built-in है, और यह Delaunay graph traversal तथा कई improvements देता है”
अभी tiles calculate करने वाला code दर्दनाक रूप से naive है
नई discussion: https://github.com/KaliedaRik/Scrawl-canvas/discussions/120
इस article की वजह से मैंने ढूंढा कि Steve आजकल कहां हैं
मैं उन्हें कई दशक पहले जानता था
संबंधित पढ़ने लायक पोस्ट: https://news.ycombinator.com/item?id=37998923 - Fortune algorithm से O(n log n) में Voronoi diagram और Delaunay triangulation बनाना (2020)
पिछली पोस्ट और discussion में दूसरे algorithms के छोटे summaries भी हैं
निजी तौर पर मुझे अभी भी Jump Flooding Algorithm सबसे ज्यादा पसंद है: https://en.wikipedia.org/wiki/Jump_flooding_algorithm