mirror of
https://github.com/boostorg/polygon.git
synced 2026-01-27 19:12:14 +00:00
361 lines
28 KiB
HTML
361 lines
28 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
<html><head>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"><title>Voronoi Basic Tutorial</title><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>
|
||
<h1>Voronoi Basic Tutorial<br>
|
||
</h1>
|
||
|
||
<p>In this tutorial we will cover the basic usage of the Boost.Polygon
|
||
Voronoi library that should be enough for the 95% of cases. Below we will
|
||
discuss the following topics:<br>
|
||
</p>
|
||
|
||
<ul>
|
||
|
||
<li>preparing input geometries;<br>
|
||
</li>
|
||
<li>Voronoi diagram construction;</li>
|
||
|
||
<li>Voronoi graph traversal;<br>
|
||
</li>
|
||
|
||
<li>associating the user data with the Voronoi primitives;</li>
|
||
<li>Voronoi diagram discretization and rendering.</li>
|
||
</ul>
|
||
|
||
In the example that goes through this tutorial (<a href="../example/voronoi_basic_tutorial.cpp">voronoi_basic_tutorial.cpp</a>)
|
||
we
|
||
are going to construct the Voronoi diagram of a few points and
|
||
segments.
|
||
On the image below one may see the corresponding rendered Voronoi
|
||
graph. The primary Voronoi edges
|
||
are marked with
|
||
the black color, non-primary with green, input geometries have blue
|
||
color. In
|
||
case you forgot, we split each input segment onto three sites (segment
|
||
itself and both endpoints), edges that go between those sites are
|
||
considered to be non-primary.<br>
|
||
|
||
<br>
|
||
|
||
<img style="border: 2px solid ; width: 300px; height: 300px;" alt="" src="images/voronoi4.png"><br>
|
||
|
||
<br>
|
||
|
||
And before you proceed don't forget to:<span style="font-family: Courier New,Courier,monospace;"><br>
|
||
<br>
|
||
#include "boost/polygon/voronoi.hpp"<br>
|
||
using boost::polygon;</span><br>
|
||
|
||
<h2>Preparing Input Geometries</h2>Below is the example of how the user provided point and segment classes might look like:<br>
|
||
<br><span style="font-family: Courier New,Courier,monospace;">struct Point {<br> int a;<br>
|
||
int b;<br>
|
||
Point (int x, int y) : a(x), b(y) {}<br>
|
||
|
||
};</span><span style="font-family: Courier New,Courier,monospace;"><br>
|
||
<br>struct Segment {</span><span style="font-family: Courier New,Courier,monospace;"></span><br>
|
||
<span style="font-family: Courier New,Courier,monospace;">
|
||
Point p0;<br>
|
||
Point p1;<br>
|
||
Segment (int x1, int y1, int x2, int y2) : p0(x1, y1), p1(x2, y2) {}<br>
|
||
</span><span style="font-family: Courier New,Courier,monospace;"></span>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">};<br>
|
||
<br>
|
||
</span>As we are going to use the default routines defined in the
|
||
voronoi.hpp header to construct the Voronoi diagram, we are required to map
|
||
our point and segment classes to the corresponding Boost.Polygon concepts:<br>
|
||
<br>
|
||
<span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">template <></span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"><br>
|
||
struct geometry_concept<Point> { typedef point_concept type; };</span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;"><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"></span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> <span class="Apple-converted-space"></span></span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"></span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;"><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">template <></span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;"><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">struct point_traits<</span><span style="font-family: Courier New,Courier,monospace;">Point</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">> {</span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;"><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> typedef int coordinate_type;</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"><br>
|
||
<span class="Apple-converted-space"> </span></span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;"><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> static inline coordinate_type get(const </span><span style="font-family: Courier New,Courier,monospace;">Point</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">& point,<span class="Apple-converted-space"> </span></span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">orientation_2d orient) {<br>
|
||
return </span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">(orient == HORIZONTAL) ? point.a : point.b;</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"></span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;"><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> }<br>
|
||
};</span><br>
|
||
<span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">
|
||
<br>
|
||
template <><br>
|
||
</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">struct geometry_concept<Segment> { typedef segment_concept type; };<br>
|
||
<br>
|
||
</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">template <></span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
|
||
<span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">struct point_traits<</span><span style="font-family: Courier New,Courier,monospace;">Segment</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">> {</span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
|
||
<span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> typedef int coordinate_type;<br>
|
||
typedef Point point_type;<br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
|
||
</span>
|
||
<span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> <span class="Apple-converted-space"> </span></span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
|
||
<span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> static inline coordinate_type get(const </span><span style="font-family: Courier New,Courier,monospace;">Segment</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">& segment,<span class="Apple-converted-space"> direction_1d dir</span></span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">) {<br>
|
||
return </span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;">dir.to_int() ? segment.p1() : segment.p0();</span><span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"></span><br style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium;">
|
||
<span style="color: rgb(0, 0, 0); font-family: 'Courier New'; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; font-size: medium; display: inline ! important; float: none;"> }<br>};</span><br>
|
||
<br>
|
||
It's also possible to use the native Boost.Polygon types as <a href="../../../boost/polygon/point_data.hpp">point_data</a> and <a href="../../../boost/polygon/segment_data.hpp">segment_data</a>, that won't require the above mapping.<br>
|
||
<br>
|
||
So once we are done we can create the sample input:<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">std::vector<Point>
|
||
points;<br>
|
||
points.push_back(Point(0, 0));<br>
|
||
points.push_back(Point(1, 6));<br>
|
||
std::vector<Segment> segments;<br>
|
||
segments.push_back(Segment(-4, 5, 5, -1));<br>
|
||
segments.push_back(Segment(3, -11, 13, -1));</span><span style="font-family: Courier New,Courier,monospace;"><br>
|
||
</span>
|
||
<h2>Construction of the Voronoi Diagram<br>
|
||
</h2>At this point we are ready to construct the Voronoi diagram:<br>
|
||
<span style="font-family: Courier New,Courier,monospace;"><br>
|
||
</span><span style="font-family: Courier New,Courier,monospace;">
|
||
voronoi_diagram<double> vd;<br>
|
||
construct_voronoi(points.begin(), points.end(), segments.begin(), segments.end(), &vd);<br>
|
||
</span><br>
|
||
So brief, isn't that awesome!<br>
|
||
|
||
<h2>Traversing Voronoi Graph</h2>
|
||
|
||
Voronoi graph traversal is the basic
|
||
operation one would like to do once the Voronoi diagram is constructed.
|
||
There are three ways to do that and we are going to cover all of them:<br>
|
||
|
||
<ul>
|
||
|
||
<li>simply iterating over the Voronoi edges (counts each edge twice):<br>
|
||
<span style="font-family: Courier New,Courier,monospace;"><br>
|
||
int iterate_primary_edges1(const voronoi_diagram<double> &vd)
|
||
{<br>
|
||
int result = 0;<br>
|
||
for (voronoi_diagram<double>::const_edge_iterator it =
|
||
vd.edges().begin();<br>
|
||
it != vd.edges().end(); ++it) {<br>
|
||
if (it->is_primary())<br>
|
||
++result;<br>
|
||
}<br>
|
||
return result;<br>
|
||
}</span><br>
|
||
<span style="font-family: Courier New,Courier,monospace;"> </span><br>
|
||
</li>
|
||
<li>iterating over the Voronoi cells and then traversing edges around
|
||
each cell (counts each edge twice):<br>
|
||
<br>
|
||
<span style="font-family: Courier New,Courier,monospace;">int
|
||
iterate_primary_edges2(const voronoi_diagram<double> &vd) {<br>
|
||
int result = 0;<br>
|
||
for (voronoi_diagram<double>::const_cell_iterator it =
|
||
vd.cells().begin();<br>
|
||
it != vd.cells().end(); ++it) {<br>
|
||
const voronoi_diagram<double>::cell_type
|
||
&cell = *it;<br>
|
||
const voronoi_diagram<double>::edge_type *edge
|
||
= cell.incident_edge();<br>
|
||
// This is convenient way to iterate edges around
|
||
Voronoi cell.<br>
|
||
do {<br>
|
||
if (edge->is_primary())<br>
|
||
++result;<br>
|
||
edge = edge->next();<br>
|
||
} while (edge != cell.incident_edge());<br>
|
||
}<br>
|
||
return result;<br>
|
||
}</span><br>
|
||
<br>
|
||
</li>
|
||
<li>iterating over the Voronoi
|
||
vertices and then traversing edges around each vertex (the number of the
|
||
iterations through each edge is equal to the number of finite endpoints
|
||
of the edge):<span style="font-family: Courier New,Courier,monospace;"></span><br>
|
||
<span style="font-family: Courier New,Courier,monospace;">int
|
||
iterate_primary_edges3(const voronoi_diagram<double> &vd) {<br>
|
||
int result = 0;<br>
|
||
for (voronoi_diagram<double>::const_vertex_iterator it =
|
||
vd.vertices().begin();<br>
|
||
it != vd.vertices().end(); ++it) {<br>
|
||
const voronoi_diagram<double>::vertex_type
|
||
&vertex = *it;<br>
|
||
const voronoi_diagram<double>::edge_type *edge
|
||
= vertex.incident_edge();<br>
|
||
// This is convenient way to iterate edges around
|
||
Voronoi vertex.<br>
|
||
do {<br>
|
||
if (edge->is_primary())<br>
|
||
++result;<br>
|
||
edge = edge->rot_next();<br>
|
||
} while (edge != vertex.incident_edge());<br>
|
||
}<br>
|
||
return result;<br>
|
||
}</span></li>
|
||
</ul>
|
||
|
||
This should give a very nice idea on how to do the Voronoi
|
||
diagram traversal. Notice that while the output from the first two methods should
|
||
be the same, it wouldn't for the third one. The reason is that in the
|
||
last case we will iterate only once through the edges with a single
|
||
finite endpoint and will skip all the edges with no finite endpoints.<br>
|
||
|
||
<h2>Associating User Data with Voronoi Primitives</h2>
|
||
|
||
A few simple cases of associating the user data with the Voronoi primitives are
|
||
following:<br>
|
||
|
||
<ul>
|
||
|
||
<li>associating number of incident edges with each cell, vertex;</li>
|
||
<li>associating color information with each edge;</li>
|
||
<li>using DFS or BFS on the Voronoi graph requires to mark visited
|
||
edges/vertices/cells.</li>
|
||
</ul>
|
||
|
||
We will consider the first example and will associate the total number
|
||
of incident edges with each cell.<br>
|
||
|
||
Note: Each Voronoi primitive contains mutable pointer to the void* type,
|
||
that allows to associate any type of data with it.<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">std::vector<int>
|
||
counts;</span><br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">// This is
|
||
required as reallocation of underlying vector will invalidate all the
|
||
pointers.<br>
|
||
counts.reserve(vd.num_cells());<br>
|
||
for (voronoi_diagram<double>::const_cell_iterator it =
|
||
vd.cells().begin();<br>
|
||
it != vd.cells().end(); ++it) {<br>
|
||
const voronoi_diagram<double>::cell_type &cell = *it;<br>
|
||
const voronoi_diagram<double>::edge_type *edge =
|
||
cell.incident_edge();<br>
|
||
int count = 0;<br>
|
||
do {<br>
|
||
++count;<br>
|
||
edge = edge->next();<br>
|
||
} while (edge != cell.incident_edge());<br>
|
||
counts.push_back(count);<br>
|
||
cell.data(&counts.back());<br>
|
||
}</span><span style="font-family: Courier New,Courier,monospace;"><br>
|
||
</span><br>
|
||
|
||
Note: In the example above we could not use count variable
|
||
without the vector, because the pointer to it will become invalid as soon as
|
||
we leave the scope of the enclosing for-loop.<br>
|
||
|
||
<h2>Rendering Voronoi Diagram</h2>
|
||
|
||
There are two main issues that don't allow to strictly render the resulting
|
||
Voronoi diagram using such rendering tools as OpenGL or DirectX.
|
||
Those are:<br>
|
||
|
||
<ul>
|
||
|
||
<li>Some of the Voronoi edges are infinite, so should be clipped;</li>
|
||
<li>Some of the Voronoi edge are parabolic arcs, so should be
|
||
discretized.</li>
|
||
</ul>
|
||
|
||
Note: This would be the issues not only for rendering tools.
|
||
Basically every task that requires diagram to be represented as a set
|
||
of finite segments will fall into this category.<br>
|
||
|
||
<br>
|
||
|
||
All the above functionality is already implemented in the Voronoi
|
||
utils.
|
||
Before clipping an edge we are going to define the clipping rectangle.
|
||
It is practical to choose it so that it wraps all the input geometries
|
||
plus some offset.<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"><br>
|
||
bounding_rectangle<double> bbox;<br>for (std::vector<Point>::iterator it = points.begin(); it != points.end(); ++it)<br>
|
||
bbox.update(it->a, it->b);<br>
|
||
for (std::vector<Segment>::iterator it = segments.begin(); it != segments.end(); ++it) {<br>
|
||
bbox.update(it->p0.a, it->p0.b);<br>
|
||
bbox.update(it->p1.a, it->p1.b);<br>
|
||
}<br>
|
||
// Add 10% offset to the bounding rectangle.<br>
|
||
bbox = voronoi_utils<double>::scale(bbox, 1.1);</span><span style="font-family: Courier New,Courier,monospace;"><br>
|
||
</span><br>
|
||
|
||
Now lets consider that we have the 3rd party library function that
|
||
draws segments using the following prototype:<span style="font-family: Courier New,Courier,monospace;"><br>
|
||
<br>
|
||
void draw_segment(double x1, double y1, double x2, double y2);<br>
|
||
<br>
|
||
</span>Then the function that renders our diagram edges will have the
|
||
following implementation:<br>
|
||
|
||
<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;">void
|
||
render_diagram(const voronoi_diagram<double> &vd,<br>
|
||
|
||
const voronoi_utils<double>::brect_type &bbox) {<br>
|
||
int visited = 1;<br>
|
||
for (voronoi_diagram<double>::const_edge_iterator it =
|
||
vd.edges().begin();<br>
|
||
it != vd.edges().end(); ++it) {<br>
|
||
// We use data pointer to mark visited edges.<br>
|
||
it->data(&visited);<br>
|
||
// Don't render the same edge twice.<br>
|
||
if (it->twin()->data()) continue;<br>
|
||
voronoi_utils<double>::point_set_type polyline;<br>
|
||
if (it->is_linear())<br>
|
||
voronoi_utils<double>::clip(*it,
|
||
bbox, polyline);<br>
|
||
else<br>
|
||
// Parabolic edges are always finite.<br>
|
||
|
||
voronoi_utils<double>::discretize(*it, 1E-1, polyline);<br>
|
||
// Note: discretized edge segments may also lie
|
||
outside of the bbox.<br>
|
||
for (int i = 1; i < polyline.size(); ++i)<br>
|
||
draw_segment(polyline[i-1].x(),
|
||
polyline[i-1].y(),<br>
|
||
|
||
polyline[i].x(), polyline[i].y());<br>
|
||
}<br>
|
||
}<br>
|
||
<br>
|
||
</span><a href="../example/voronoi_visualizer.cpp">voronoi_visualizer.cpp</a>
|
||
contains a simple fully featured implementation of the Voronoi diagram
|
||
renderer using the Qt libraries. It was used to generate all the .png
|
||
drawings under the ./libs/polygon/example directory.<span style="text-decoration: underline;"><br>
|
||
</span><span style="font-family: Courier New,Courier,monospace;">
|
||
<br>
|
||
</span>I hope the reader managed to get to this point and found the
|
||
basic tutorial to be useful (in the end it's not so basic). Worth
|
||
to notice that construction of the Voronoi diagram takes only two lines
|
||
of code, everything else is about initializing input data structures,
|
||
traversing Voronoi graph, associating data with the diagram primitives and
|
||
using the <a href="voronoi_utils.htm">Voronoi utilities</a>. In the
|
||
default mode the Voronoi diagram operates with the signed int (32-bit) input
|
||
coordinate type and double (64-bit) output coordinate type. In the <a href="voronoi_advanced_tutorial.htm">advanced Voronoi tutorial</a> we
|
||
explain why this is enough for the 95% of cases and how to expand the
|
||
algorithm coordinate types for the other 5%.<br>
|
||
|
||
<span style="font-family: Courier New,Courier,monospace;"></span><br>
|
||
|
||
<table class="docinfo" id="table1" frame="void" rules="none">
|
||
|
||
<colgroup> <col class="docinfo-name"><col class="docinfo-content"> </colgroup>
|
||
<tbody valign="top">
|
||
<tr>
|
||
<th class="docinfo-name">Copyright:</th>
|
||
<td>Copyright <20> Andrii Sydorchuk 2010-2012.</td>
|
||
</tr>
|
||
<tr class="field">
|
||
<th class="docinfo-name">License:</th>
|
||
<td class="field-body">Distributed under the Boost Software
|
||
License, Version 1.0. (See accompanying file <tt class="literal"> <span class="pre">LICENSE_1_0.txt</span></tt> or copy at <a class="reference" target="_top" href="http://www.boost.org/LICENSE_1_0.txt">
|
||
http://www.boost.org/LICENSE_1_0.txt</a>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
</body></html>
|