Click here to Skip to main content
Click here to Skip to main content

Fortune's Voronoi algorithm implemented in C#

By , 21 Apr 2013
 

 

NOTE: Code has moved to Google Code! 

Introduction 

Given a set of two dimensional vectors (or data points), a Voronoi graph is a separation of those points into compartments where all points inside one compartment are closer to the contained data point than to any other data point. I won't give any demonstration here, but if you want to know more about Voronoi graphs, check out this.

The applications of Voronoi graphs are quite broad. Very useful for a lot of optimization problems (in most cases, the Delaunay Triangulation which can be easily derived from a Vononoi graph is used there), it ranges to computing topological maps from bitmaps.

[This is an article for freaks. After a rather painful experience writing the thing I hope it will benefit everyone who is looking for this algorithm in a civilized language (or simply does not want to use Fortune's original C implementation).]

In 1987, Steve Fortune described an algorithm to compute such a graph by using a sweep line in combination with a binary tree. A PowerPoint explanation of the algorithm (the one I used to implement it) can be found here. Note that I did not use the linked data structure to represent a graph - I think that is an unnecessary difficulty in the age of ArrayLists and HashSets.

The Implementation

Data points are represented by my own Vector class. It can do much more than needed here (but there was no reason to strip it before bringing it) but I won't explain it here. The most important fact is that although working with doubles the Vector class automatically rounds values to 10 digits (or whatever is set in the Vector.Precision field). Yes, sadly, this is very important if you want to sort of compare doubles.

A VoronoiGraph is a class that only contains a HashSet of vertices (as 2D vectors) and a HashSet of VoronoiEdges - each with references to the left and right data point and (of course) the two vertices that bound the edge. If the edge is (partially or completely) unbounded, the vector Fortune.VVUnknown is used.

BinaryPriorityQueue is used for the sweep line event queue.

Usage

The algorithm itself (Fortune.ComputeVoronoiGraph(IEnumerable)) takes any IEnumerable containing only two dimensional vectors. It will return a VoronoiGraph. The algorithm's complexity is O(n ld(n)) with a factor of about 10 microseconds on my machine (2GHz).

License

This article, along with any associated source code and files, is licensed under The Mozilla Public License 1.1 (MPL 1.1)

About the Author

BenDi
Software Developer (Senior)
Germany Germany
Member
I did my diploma in Dresden and Sydney where I dealt with algorithms, agents and other cool AI stuff. Now I moved to Frankfurt to work on my PhD dealing with software structures for artificial intelligence systems. If I can, I do things in C# and ASP.NET, but if I have to, my C++, Java and SQL are not that bad.
Long Live .NET.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionMESSAGE FROM THE OWNER: Help improve this project on Google Code!memberBenDi21 Apr '13 - 6:59 
Hi all!
 
I am thrilled and humbled by all the feedback I got for this project. I'm happy some people found it useful.
 
It is hard to overlook that the code was never polished from all the questions and change request, but since I can't fulfill all of these, I have
 
MOVED THE PROJECT to Google Code: https://code.google.com/p/fortune-voronoi/[^]
 
Feel free to submit Fixes, Comments, Refactorings, UIs, whatever you feel like!
 
If someone would like to help with the reviews, let me know.
 
Cheers,
 
Ben
QuestionRounding error when creating circles for new nodesmemberBwd Yeti19 Apr '13 - 0:50 
On line 350 of FortuneVoronoi.cs in CircleCheckDataNode(), after creating a new circle for three points the code checks if the bottom of the circle is below the most recently added point, since if the circle is above that it would defy the sweep line
if(VC.Y>=ys)
However you round the circle y value to 10 decimal places, so in extreme situations where the new node is at the exact bottom of the circle it can have higher precision on its y value and invalidate the circle, throwing off the whole graph. The if statement needs changed to something like
if(VC.Y>ys || Math.Abs(VC.Y - ys) < 1e-10)
to account for it
 
http://puu.sh/2CVs2.png
http://puu.sh/2CVsK.png
An example graph exhibiting this problem, with the offending point marked red, before and after fixing
AnswerRe: Rounding error when creating circles for new nodesmemberBenDi21 Apr '13 - 6:56 
Thanks! I was trying to reduce the rounding problems for a while now, good work.
 
To make changes more easy in the future and since I don't have a lot of time to work on this, I moved the code to Google Code.
QuestionDon't know where to start [modified]memberMember 998975818 Apr '13 - 16:58 
I always find it harder to read someone else's code. I was hoping to use this to get me started in making some really unique terrain generation, but I can't seem to understand where to start. If someone could send a visual studio project with the most simple beginning I would greatly appreciate it.

modified 19 Apr '13 - 9:29.

GeneralMy vote of 4memberPehGuevara7 Mar '13 - 21:40 
Almost no comments in the code. Some things could be implemented in a nicer way.
But the code is working fine.
QuestionGetting regions from graphmemberBixel6 Feb '13 - 14:14 
I've been able to draw the data nicely with the help of XNA, but how do I get a region? so for instance I can retrieve point data and edge data to draw point and lines, but is there a way to define a region? Here is a simple windows form I set up >> http://nbixel.tumblr.com/post/37385174326/been-wanting-to-create-procedural-map-generation[^] - Ive since created a new program for more felxibility, like move points or removing them. But i want to add a method of create primitives based on the region.
AnswerRe: Getting regions from graphmemberBenDi6 Feb '13 - 23:50 
Do you want to get the polygons of the voronoi regions? (Might be tricky, you need to collect all the intersection and somehow order them.).
Or do you want to triangulate the data points (so called 'Delauney triangualation')? This is very simple, you just have to connect the original data points every time they have a common voronoi edge, which will magically lead to triangles.
GeneralRe: Getting regions from graphmemberBixel7 Feb '13 - 12:06 
oohh interesting... I actually want the regions, but I will try and experiment with the getting the triangles as well. I figured it would be harder than just simply tally up all fixed points associated with the center point. Since I have a working GUI I can have the user pick the fixed points and create a poly shape from that. Of course this would be a little tedious but that's a good start until I find a way to code it. I may post a link to the program at some point so people can try it out.
QuestionSeparating braches of a medial axis sampled from inner voronoi points of a polygonmemberMember 917583217 Jul '12 - 16:19 
I am looking for a method that will allow me to separate all edges and vertices that describe a sampled medial axis from from a closed polygon of points.
 
I would like to accomplish this so I can then fit an individual geometric component to each branch of the medial axis, whether it be a line, spline, or arc.
 
I was thinking of creating a method for the separation using a depth first search that marked each vertex with a degree of greater than one as the split point. I would like to find a algorithm or library that might accomplish this if possible. No need to recreate the wheel. Looking for some good ideas...
QuestionFinding distance for VoronoiGraph vertizes representing Medial Axis of closed boundarymemberMember 917583214 Jul '12 - 5:02 
I am attempting to use this Voronoi algorithm for medial axis computation of a closed boundary of lines, curves, or splines. I have sampled each boundary edge type to create a point at some distance value for a line or arc length of a curve. I then pass these set of points, which represent a filtered closed polygon of the boundary, into the ComputeVoronoiGraph function. I am returned VoronoiGraph vertizes that represent the sampled medial axis.
 
One of the other challenges is that I would like to fit lines, arcs, or splines to the vertizes. But that is a different story...
 
By definition a medial axis is represented as the center points of all inscribed circles that fit within the boundary, which is what I currently have. Although what I would like to also find is the radius of each of the vertizes (inscribed circle center points) that are returned from the ComputeVoronoiGraph function. Any pointers on how to accomplish this? Can I get this information from the algorithm somehow?
 
My intention is to compute the medial axis and filter it similar to the Scale Axis Theorem

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 22 Apr 2013
Article Copyright 2005 by BenDi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid