I am trying to find out the intersection points of two unit-radius circles in CGAL. Inspired by the CGAL sample codes and tutorials, I have succeeded in creating the following code. But unfortunately, I am unable to print out the points. I have noticed that the size of the vector is the number of intersection points. Any help will be appreciated since I am new to CGAL.
#include <CGAL/Circular_kernel_intersections.h>
#include <CGAL/Exact_circular_kernel_2.h>
typedef CGAL::Exact_circular_kernel_2 Circular_k;
typedef CGAL::Point_2<Circular_k> Point_2;
typedef CGAL::Circle_2<Circular_k> Circle_2;
typedef CGAL::Circular_arc_2<Circular_k> Circular_arc_2;
typedef CGAL::CK2_Intersection_traits<Circular_k, Circle_2, Circle_2>::type Intersection_result;
using namespace std;
int main() {
Point_2 p(2,2), r(5.5,2);
Circle_2 c1(p,1), c2(r,1);
vector<Intersection_result> res;
intersection(c1,c2,back_inserter(res));
cout << res.size() << endl;
}
I tested your code and it seems to work. Note that the intersection points are stored as Circular_arc_point_2. Using your code I added only the following lines:
using boostRetVal = std::pair<CGAL::Circular_arc_point_2<CGAL::Filtered_bbox_circular_kernel_2<CGAL::Circular_kernel_2<CGAL::Cartesian<CGAL::Gmpq>, CGAL::Algebraic_kernel_for_circles_2_2<CGAL::Gmpq> > > > , unsigned>;
for(const auto& element : res) {
auto algPoint = std::get<0>( boost::get< boostRetVal >(element) );
auto point = Point_2(to_double(algPoint.x()), to_double(algPoint.y()));
std::cout << point << std::endl;
}
As points I used p(0,0), r(2,0), as circles c1(p,4), c2(r,1), then the received output was:
2
7/4 -4360591588697965/4503599627370496
7/4 4360591588697965/4503599627370496
Related
I'm using R-Tree boost. I added a hundred thousand points in r-tree boost. Now I want to cluster and group my points like this link. It seems like that I should calculate k-mean value from points. How is it possible to calculate k-mean value from r-tree points geometry.
There are various clustering algorithms having different properties and inputs. What needs to be considered before choosing an algorithm is what do you want to achieve. k-means referred by you in the question aims to partition set of points into k clusters. So the input is the desired number of clusters. On the other hand, the algorithm described in the blog you linked, a variant of greedy clustering algorithm aims to partition set of points into circular clusters of some size. The input is the radius of the desired cluster.
There are various algorithms performing k-means clustering used for different data and applications like separating 2 n-dimensional subsets with hyperplane or clustering with Voronoi diagram (Lloyd's algorithm) often called k-means algorithm. There are also density-based clustering algorithms mentioned by #Anony-Mousse in the comments under your question.
In the article, you mentioned it's a hierarchical version of greedy clustering. They have to calculate the clusters for multiple zoom levels and to avoid analyzing all of the points each time they use the centroids of clusters from the previously analyzed level as a source of points for clustering for the next level. However, in this answer, I'll show how to implement this algorithm for one level only. So the input will be a set of points and a size of cluster as a radius. If you need hierarchical version you should calculate centroids of the output clusters and use them as the input of the algorithm for the next level.
Using Boost.Geometry R-tree the algorithm for one level (so not hierarchical) could be implemented like this (in C++11):
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <vector>
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<double, 2, bg::cs::cartesian> point_t;
typedef bg::model::box<point_t> box_t;
typedef std::vector<point_t> cluster_t;
// used in the rtree constructor with Boost.Range adaptors
// to generate std::pair<point_t, std::size_t> from point_t on the fly
template <typename First, typename Second>
struct pair_generator
{
typedef std::pair<First, Second> result_type;
template<typename T>
inline result_type operator()(T const& v) const
{
return result_type(v.value(), v.index());
}
};
// used to hold point-related information during clustering
struct point_data
{
point_data() : used(false) {}
bool used;
};
// find clusters of points using cluster radius r
void find_clusters(std::vector<point_t> const& points,
double r,
std::vector<cluster_t> & clusters)
{
typedef std::pair<point_t, std::size_t> value_t;
typedef pair_generator<point_t, std::size_t> value_generator;
if (r < 0.0)
return; // or return error
// create rtree holding std::pair<point_t, std::size_t>
// from container of points of type point_t
bgi::rtree<value_t, bgi::rstar<4> >
rtree(points | boost::adaptors::indexed()
| boost::adaptors::transformed(value_generator()));
// create container holding point states
std::vector<point_data> points_data(rtree.size());
// for all pairs contained in the rtree
for(auto const& v : rtree)
{
// ignore points that were used before
if (points_data[v.second].used)
continue;
// current point
point_t const& p = v.first;
double x = bg::get<0>(p);
double y = bg::get<1>(p);
// find all points in circle of radius r around current point
std::vector<value_t> res;
rtree.query(
// return points that are in a box enclosing the circle
bgi::intersects(box_t{{x-r, y-r},{x+r, y+r}})
// and were not used before
// and are indeed in the circle
&& bgi::satisfies([&](value_t const& v){
return points_data[v.second].used == false
&& bg::distance(p, v.first) <= r;
}),
std::back_inserter(res));
// create new cluster
clusters.push_back(cluster_t());
// add points to this cluster and mark them as used
for(auto const& v : res) {
clusters.back().push_back(v.first);
points_data[v.second].used = true;
}
}
}
int main()
{
std::vector<point_t> points;
for (double x = 0.0 ; x < 10.0 ; x += 1.0)
for (double y = 0.0 ; y < 10.0 ; y += 1.0)
points.push_back(point_t{x, y});
std::vector<cluster_t> clusters;
find_clusters(points, 3.0, clusters);
for(size_t i = 0 ; i < clusters.size() ; ++i) {
std::cout << "Cluster " << i << std::endl;
for (auto const& p : clusters[i]) {
std::cout << bg::wkt(p) << std::endl;
}
}
}
See also their implementation: https://github.com/mapbox/supercluster/blob/master/index.js#L216
Furthermore, take into account the remarks of #Anony-Mousse about the accuracy of distance calculation on the globe. The solution above is for cartesian coordinate system. If you want to use different coordinate system then you have to define point type differently, e.g. use bg::cs::spherical_equatorial<bg::degree> or bg::cs::geographic<bg::degree> instead of bg::cs::cartesian. You also have to generate the query bounding box differently. But bg::distance() will automatically return correct distance after changing the point type.
I have been using boost::geometry library in a program, mostly for handling polygon objects.
I am now trying to optimize my code to scale better with larger polygons. One my functions checks for a given polygon and a given point (usually inside the polygon) the minimum and maximum distance between the point and polygon outer ring.
I do it by looping on the polygon edges:
polygon pol;
point myPoint;
double min = 9999999, max = 0;
for(auto it1 = boost::begin(bg::exterior_ring(pol)); it1 != boost::end(bg::exterior_ring(pol)); ++it1){
double distance = bg::distance(*it1, myPoint);
if(max < distance)
max = distance;
if(min > distance)
min = distance;
}
I am hoping that there are algorithms faster than this one, linear in the polygon number of edges. Is there such a thing already inside the boost::geometry library?
I'd suggest you can use the builtin strategies for finding the minimum distance between the polygon and the point:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/algorithms/distance.hpp>
namespace bg = boost::geometry;
using point = bg::model::d2::point_xy<double>;
using polygon = bg::model::polygon<point>;
int main() {
polygon pol;
boost::geometry::read_wkt(
"POLYGON((2 1.3,2.4 1.7,2.8 1.8,3.4 1.2,3.7 1.6,3.4 2,4.1 3,5.3 2.6,5.4 1.2,4.9 0.8,2.9 0.7,2 1.3)"
"(4.0 2.0, 4.2 1.4, 4.8 1.9, 4.4 2.2, 4.0 2.0))", pol);
point myPoint(7, 7);
double min = 9999999, max = 0;
std::cout << "Minimal distance: " << bg::distance(pol, myPoint);
}
Prints
Minimal distance: 4.71699
Further hints:
You should consider ranking the distances first using comparable_distance. As you can see the sample there suggests looping over all the sampled distances... so I don't think the library has a better offering at this time.
More sophisticated algorithms are planned, of which a number may be related to this problem:
http://boost-geometry.203548.n3.nabble.com/distance-between-geometries-td4025549.html
mailing list thread http://lists.boost.org/geometry/2013/08/2446.php
another here http://lists.boost.org/geometry/2013/09/2494.php
Note also that Boost Geometry Index has a related predicate comparable_distance_far but it's not exposed as of yet.
Summary
You can improve at least a bit by using comparable_distance here for now.
Features have been planned and it looks like there is a good chance that requesting them on the mailing list/Boost Trac will help getting them there.
For best performances you should use an RTree with boost::geometry::index. Creating the RTree has a cost, but then computing the ditance of a point to any of the (multi)polygon ring will be much faster. Example code:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <iostream>
#include <vector>
int main()
{
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<double, 2, bg::cs::cartesian> point;
typedef bg::model::polygon<point> polygon;
point p{ 0, 0 };
// create some polygon and fill it with data
polygon poly;
double a = 0;
double as = bg::math::two_pi<double>() / 100;
for (int i = 0; i < 100; ++i, a += as)
{
double c = cos(a);
double s = sin(a);
poly.outer().push_back(point{10 * c, 10 * s});
poly.inners().resize(1);
poly.inners()[0].push_back(point{5 * c, 5 * s});
}
// make sure it is valid
bg::correct(poly);
// create rtree containing objects of type bg::model::pointing_segment
typedef bg::segment_iterator<polygon const> segment_iterator;
typedef std::iterator_traits<segment_iterator>::value_type segment_type;
bgi::rtree<segment_type, bgi::rstar<4> > rtree(bg::segments_begin(poly),
bg::segments_end(poly));
// get 1 nearest segment
std::vector<segment_type> result;
rtree.query(bgi::nearest(p, 1), std::back_inserter(result));
BOOST_ASSERT(!result.empty());
std::cout << bg::wkt(result[0]) << ", " << bg::distance(p, result[0]) << std::endl;
return 0;
}
I'm using CGAL and got into some weird bug, which I can't reproduce in a small test program. Here is the test code that works as given, but when I have the exact same code in my larger program (a ROS node) it spits an error:
#include <vector>
#include <boost/shared_ptr.hpp>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/Boolean_set_operations_2.h>
#include "print.h"
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::FT Ft;
typedef Kernel::Point_2 Point;
typedef Kernel::Segment_2 Segment;
typedef Kernel::Direction_2 Direction;
typedef Kernel::Line_2 Line;
typedef Kernel::Vector_2 Vector;
typedef CGAL::Polygon_2<Kernel> Polygon;
typedef CGAL::Polygon_with_holes_2<Kernel> PolygonWithHoles;
main() {
Polygon poly;
float scale = 4.0/100;
float max_y = 500*scale;
poly.push_back(Point(76*scale, max_y-496*scale));
poly.push_back(Point(660*scale, max_y-496*scale));
poly.push_back(Point(660*scale, max_y-48*scale));
poly.push_back(Point(71*scale, max_y-54*scale));
// Holes must be clock wise!!
Polygon holes[10];
holes[0].push_back(Point(131*scale, max_y-86*scale));
holes[0].push_back(Point(179*scale, max_y-85*scale));
holes[0].push_back(Point(180*scale, max_y-238*scale));
holes[0].push_back(Point(133*scale, max_y-239*scale));
holes[1].push_back(Point(237*scale, max_y-84*scale));
holes[1].push_back(Point(286*scale, max_y-84*scale));
holes[1].push_back(Point(288*scale, max_y-237*scale));
holes[1].push_back(Point(240*scale, max_y-238*scale));
// Why does this hole make intersection() error?
holes[2].push_back(Point(345*scale, max_y-84*scale));
holes[2].push_back(Point(393*scale, max_y-83*scale));
holes[2].push_back(Point(396*scale, max_y-236*scale));
holes[2].push_back(Point(348*scale, max_y-236*scale));
PolygonWithHoles polyHoles(poly);
polyHoles.outer_boundary() = poly;
for (int i=0; i<3; ++i)
polyHoles.add_hole(holes[i]);
std::cout << "\nPolygon:" << std::endl;
print_polygon_with_holes(polyHoles);
Polygon selection;
float minx = -5.7669;
float miny = -2.13124;
float maxx = 0.396996;
float maxy = 4.88933;
selection.push_back(Point(minx, miny));
selection.push_back(Point(maxx, miny));
selection.push_back(Point(maxx, maxy));
selection.push_back(Point(minx, maxy));
std::cout << "\nSelection:" << std::endl;
print_polygon(selection);
std::vector<PolygonWithHoles> result;
CGAL::intersection(polyHoles, selection, std::back_inserter(result));
std::cout << "Intersection:" << std::endl;
if (!result.empty())
print_polygon_with_holes(result.front());
}
The error:
terminate called after throwing an instance of 'CGAL::Precondition_exception'
what(): CGAL ERROR: precondition violation!
Expr: comp_f(object, parentP->object) != SMALLER
File: /usr/include/CGAL/Multiset.h
Line: 2128
I found 2 ways to fix this:
Shift one point a little bit: replace 83 with 84.
Use Exact_predicates_exact_constructions_kernel
My question is: what could cause the problem to exist only in the larger program?
I'd like to keep using the unexact_constructions, I don't see why I'd have to use the exact_constructions in this case (points aren't close to each other or anything), but since I don't know what the intersection() algorithm does I might be wrong about that.
The intersect function constructs new points - new points of intersections. If you are not using exact constructions, then these new points are not promised to be correct. If you then use these inexact points for further computations you'll run into problems. Unless you have significant constraints on the running time, I believe you're better of with exact constructions.
Example: Consider the unit circle x^2+y^2=1 and the line y=x and let p be a constructed point of intersection. Then circle.has_on_boundary (p) will return TRUE only if you use the exact constructions.
I'm using CGAL with Qt to draw Voronoi diagram. I used CGAL::Voronoi_diagram_2<DT,AT,AP>since I need the faces. This is the example code:
for(Face_iterator f = VD.faces_begin(); f != VD.faces_end(); f++)
{
Ccb_halfedge_circulator ec_start = (f)->ccb();
Ccb_halfedge_circulator ec = ec_start;
do {
if (!ec->has_source())
{
}
else
QpolyF << QPointF(((Halfedge_handle)ec)->source()->point().x(), ((Halfedge_handle)ec)->source()->point().y());
} while ( ++ec != ec_start );
VectPolygon.push_back(QpolyF);
QpolyF.clear();}
I need to clip the rays that has source or target in infinity. If I use the Cropped_voronoi_from_delaunay to generate voronoi it only gives the segments not the faces. these are the typedefs:
typedef K::Line_2 Line_2;
typedef CGAL::Delaunay_triangulation_2<K> Delaunay_triangulation_2;
typedef Delaunay_triangulation_2::Face_iterator dt_Face_iterator;
typedef Delaunay_triangulation_2::Edge_circulator dt_Edge_circulator;
// typedefs for defining the adaptor
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Delaunay_triangulation_2<K> DT;
typedef CGAL::Delaunay_triangulation_adaptation_traits_2<DT> AT;
typedef CGAL::Delaunay_triangulation_caching_degeneracy_removal_policy_2<DT> AP;
typedef CGAL::Voronoi_diagram_2<DT,AT,AP> VD;
// typedef for the result type of the point location
typedef AT::Site_2 Site_2;
typedef AT::Point_2 Point_2;
typedef VD::Locate_result Locate_result;
typedef VD::Vertex_handle Vertex_handle;
typedef VD::Face_handle Face_handle;
typedef VD::Face_iterator Face_iterator;
typedef VD::Halfedge_handle Halfedge_handle;
typedef VD::Ccb_halfedge_circulator Ccb_halfedge_circulator;
There is some experimental code here: http://code.google.com/p/cgal-voronoi-cropping that crop a voronoi diagram to a rectangle, the result being a HDS. See main.cpp in the test directory
I know it's possible to do it with CGAL, but I found a workaround for now. in Qt, QPolygon class has the function to find intersected polygons. Qpolygon::intersected(yourPolygon).
this is the results:
The following will generate a random point cloud, find its Voronoi diagram, crop that diagram to the cloud's bounding box, and generate well-known text polygons.
I'm not sure how to integrate this with Qt, but, presumably, once you have the polygons this part will be easy(ish).
//Finds the cropped Voronoi diagram of a set of points and saves it as WKT
//Compile with: g++ main.cpp -Wall -lCGAL -lgmp
//Author: Richard Barnes (rbarnes.org)
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Regular_triangulation_filtered_traits_2.h>
#include <CGAL/Regular_triangulation_adaptation_traits_2.h>
#include <CGAL/Regular_triangulation_adaptation_policies_2.h>
#include <CGAL/Regular_triangulation_2.h>
#include <CGAL/Voronoi_diagram_2.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/bounding_box.h>
#include <CGAL/Polygon_2.h>
#include <iostream>
#include <cstdint>
//Used to convert otherwise infinite rays into looooong line segments
const int RAY_LENGTH = 1000;
typedef CGAL::Exact_predicates_exact_constructions_kernel K;
typedef CGAL::Regular_triangulation_filtered_traits_2<K> Traits;
typedef CGAL::Regular_triangulation_2<Traits> RT2;
typedef CGAL::Regular_triangulation_adaptation_traits_2<RT2> AT;
typedef CGAL::Regular_triangulation_degeneracy_removal_policy_2<RT2> DRP;
typedef CGAL::Voronoi_diagram_2<RT2, AT, DRP> VD;
int main(int argc, char **argv){
std::vector<RT2::Weighted_point> wpoints;
std::cout.precision(4);
std::cout.setf(std::ios::fixed);
//Generated random points
for(int i=0;i<100;i++)
//Weight of 0 gives a Voronoi diagram. Non-zero weight gives a power diagram
wpoints.push_back(RT2::Weighted_point(K::Point_2(rand()%100,rand()%100), 0));
//Find the bounding box of the points. This will be used to crop the Voronoi
//diagram later.
const K::Iso_rectangle_2 bbox = CGAL::bounding_box(wpoints.begin(), wpoints.end());
//Create a Regular Triangulation from the points
RT2 rt(wpoints.begin(), wpoints.end());
rt.is_valid();
//Wrap the triangulation with a Voronoi diagram adaptor. This is necessary to
//get the Voronoi faces.
VD vd(rt);
//CGAL often returns objects that are either segments or rays. This converts
//these objects into segments. If the object would have resolved into a ray,
//that ray is intersected with the bounding box defined above and returned as
//a segment.
const auto ConvertToSeg = [&](const CGAL::Object seg_obj, bool outgoing) -> K::Segment_2 {
//One of these will succeed and one will have a NULL pointer
const K::Segment_2 *dseg = CGAL::object_cast<K::Segment_2>(&seg_obj);
const K::Ray_2 *dray = CGAL::object_cast<K::Ray_2>(&seg_obj);
if (dseg) { //Okay, we have a segment
return *dseg;
} else { //Must be a ray
const auto &source = dray->source();
const auto dsx = source.x();
const auto dsy = source.y();
const auto &dir = dray->direction();
const auto tpoint = K::Point_2(dsx+RAY_LENGTH*dir.dx(),dsy+RAY_LENGTH*dir.dy());
if(outgoing)
return K::Segment_2(
dray->source(),
tpoint
);
else
return K::Segment_2(
tpoint,
dray->source()
);
}
};
//First line of WKT CSV output
std::cout<<"\"id\",\"geom\"\n";
int fnum = 0;
//Loop over the faces of the Voronoi diagram in some arbitrary order
for(VD::Face_iterator fit = vd.faces_begin(); fit!=vd.faces_end();++fit,fnum++){
CGAL::Polygon_2<K> pgon;
//Edge circulators traverse endlessly around a face. Make a note of the
//starting point so we know when to quit.
VD::Face::Ccb_halfedge_circulator ec_start = fit->ccb();
//Current location of the edge circulator
VD::Face::Ccb_halfedge_circulator ec = ec_start;
do {
//A half edge circulator representing a ray doesn't carry direction
//information. To get it, we take the dual of the dual of the half-edge.
//The dual of a half-edge circulator is the edge of a Delaunay triangle.
//The dual of the edge of Delaunay triangle is either a segment or a ray.
// const CGAL::Object seg_dual = rt.dual(ec->dual());
const CGAL::Object seg_dual = vd.dual().dual(ec->dual());
//Convert the segment/ray into a segment
const auto this_seg = ConvertToSeg(seg_dual, ec->has_target());
pgon.push_back(this_seg.source());
//If the segment has no target, it's a ray. This means that the next
//segment will also be a ray. We need to connect those two rays with a
//segment. The following accomplishes this.
if(!ec->has_target()){
const CGAL::Object nseg_dual = vd.dual().dual(ec->next()->dual());
const auto next_seg = ConvertToSeg(nseg_dual, ec->next()->has_target());
pgon.push_back(next_seg.target());
}
} while ( ++ec != ec_start ); //Loop until we get back to the beginning
//In order to crop the Voronoi diagram, we need to convert the bounding box
//into a polygon. You'd think there'd be an easy way to do this. But there
//isn't (or I haven't found it).
CGAL::Polygon_2<K> bpoly;
bpoly.push_back(K::Point_2(bbox.xmin(),bbox.ymin()));
bpoly.push_back(K::Point_2(bbox.xmax(),bbox.ymin()));
bpoly.push_back(K::Point_2(bbox.xmax(),bbox.ymax()));
bpoly.push_back(K::Point_2(bbox.xmin(),bbox.ymax()));
//Perform the intersection. Since CGAL is very general, it believes the
//result might be multiple polygons with holes.
std::list<CGAL::Polygon_with_holes_2<K>> isect;
CGAL::intersection(pgon, bpoly, std::back_inserter(isect));
//But we know better. The intersection of a convex polygon and a box is
//always a single polygon without holes. Let's assert this.
assert(isect.size()==1);
//And recover the polygon of interest
auto &poly_w_holes = isect.front();
auto &poly_outer = poly_w_holes.outer_boundary();
//Print the polygon as a WKT polygon
std::cout<<fnum<<", "
"\"POLYGON ((";
for(auto v=poly_outer.vertices_begin();v!=poly_outer.vertices_end();v++)
std::cout<<v->x()<<" "<<v->y()<<", ";
std::cout<<poly_outer.vertices_begin()->x()<<" "<<poly_outer.vertices_begin()->y()<<"))\"\n";
}
return 0;
}
I have read many articles on drawing planar graphs on the plane, I tried a lot of libraries.
In the end, I need to specify the input graph, the output to obtain new coordinates of its vertices, so that the edges do not intersect.
The choice fell on the function chrobak_payne_straight_line_drawing from Boost Graph Library.
I tested it on the following graph: http://cboard.cprogramming.com/attachments/cplusplus-programming/11153d1322430048-graph-planarization-boost-library-1grb.jpg
But it did not work and calls System.AccessViolationException at the 125 line of file chrobak_payne_drawing.hpp:
delta_x[v3] = 1;
But on this graph it works: http://cboard.cprogramming.com/attachments/cplusplus-programming/11154d1322430090-graph-planarization-boost-library-2gr.jpg
Please help, I need to function to work with all planar graphs and does not cause critical errors. Below I present the code, which I tested the first graph and got a critical error, by the way, I'm using visual studio 2010.
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/property_map/property_map.hpp>
#include <vector>
#include <stack>
#include <boost/graph/planar_canonical_ordering.hpp>
#include <boost/graph/is_straight_line_drawing.hpp>
#include <boost/graph/chrobak_payne_drawing.hpp>
#include <boost/graph/boyer_myrvold_planar_test.hpp>
using namespace boost;
//a class to hold the coordinates of the straight line embedding
struct coord_t
{
std::size_t x;
std::size_t y;
};
int main(int argc, char** argv)
{
typedef adjacency_list
< vecS,
vecS,
undirectedS,
property<vertex_index_t, int>
> graph;
//Define the storage type for the planar embedding
typedef std::vector< std::vector< graph_traits<graph>::edge_descriptor > >
embedding_storage_t;
typedef boost::iterator_property_map
< embedding_storage_t::iterator,
property_map<graph, vertex_index_t>::type
>
embedding_t;
// Create the graph - a maximal planar graph on 7 vertices. The functions
// planar_canonical_ordering and chrobak_payne_straight_line_drawing both
// require a maximal planar graph. If you start with a graph that isn't
// maximal planar (or you're not sure), you can use the functions
// make_connected, make_biconnected_planar, and make_maximal planar in
// sequence to add a set of edges to any undirected planar graph to make
// it maximal planar.
graph g(5);
add_edge(0,3, g);
add_edge(0,4, g);
add_edge(1,3, g);
add_edge(1,4, g);
add_edge(2,3, g);
add_edge(2,4, g);
// Create the planar embedding
embedding_storage_t embedding_storage(num_vertices(g));
embedding_t embedding(embedding_storage.begin(), get(vertex_index,g));
boyer_myrvold_planarity_test(boyer_myrvold_params::graph = g,
boyer_myrvold_params::embedding = embedding
);
// Find a canonical ordering
std::vector<graph_traits<graph>::vertex_descriptor> ordering;
planar_canonical_ordering(g, embedding, std::back_inserter(ordering));
//Set up a property map to hold the mapping from vertices to coord_t's
typedef std::vector< coord_t > straight_line_drawing_storage_t;
typedef boost::iterator_property_map
< straight_line_drawing_storage_t::iterator,
property_map<graph, vertex_index_t>::type
>
straight_line_drawing_t;
straight_line_drawing_storage_t straight_line_drawing_storage
(num_vertices(g));
straight_line_drawing_t straight_line_drawing
(straight_line_drawing_storage.begin(),
get(vertex_index,g)
);
// Compute the straight line drawing
chrobak_payne_straight_line_drawing(g,
embedding,
ordering.begin(),
ordering.end(),
straight_line_drawing
);
std::cout << "The straight line drawing is: " << std::endl;
graph_traits<graph>::vertex_iterator vi, vi_end;
for(tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
{
coord_t coord(get(straight_line_drawing,*vi));
std::cout << *vi << " -> (" << coord.x << ", " << coord.y << ")"
<< std::endl;
}
// Verify that the drawing is actually a plane drawing
if (is_straight_line_drawing(g, straight_line_drawing))
std::cout << "Is a plane drawing." << std::endl;
else
std::cout << "Is not a plane drawing." << std::endl;
return 0;
}
Mayber i must first make graph maximal planar? But I did not succeed .. Please help me add to my code the necessary functions for this, I'm not quite sure what step to convert the graph.
Thanks in advance, you - my last hope!
This looks like a bug in boost::graph. I have a different access violation there, perhaps because of a different boost version, but there's definitely a bug.
left[v] = right[leftmost];
vertex_t next_to_rightmost;
for(vertex_t temp = leftmost; temp != rightmost;
temp = right[temp]
)
{
next_to_rightmost = temp;
}
right[next_to_rightmost] = graph_traits<Graph>::null_vertex();
Here when leftmost==rightmost, the for loop is not executed and next_to_rightmost remains uninitialized. Crash! That's what I'm seeing.
The graph you were using was not maximal planar. You need to use the functions make_connected, make_biconnected_planar and make_maximal_planar before you get to a canonical ordering or an embedding. Though the comment in the source explains this, their use is not exactly intuitive and I could not find a source using them and the function chrobak_payne_straight_line_drawing in combination. Here is an example I adapted from the original source and other examples.