I need to be able to calculate the Reock compactness value for an arbitrary polygon (guaranteed to be simple). This value is defined as the ratio of the polygon's area to the area of its minimum bounding circle. I have the following data structure:
#include <vector>
#include <array>
using namespace std;
typedef array<int, 2> coordinate;
class Polygon {
vector<coordinate> border; // contains all coordinates
double get_area();
// a bunch of other data here...
};
class Polygon_Group {
vector<Polygon> borders;
double get_area();
};
This structure cannot be changed. I need a way to find the Reock score for an object of type Polygon_Group, and I need it to be as fast as possible. I have played around with Miniball for this, but it is really quite slow for me with large amounts of points. (Here's a small example of my miniball code)
#include "Miniball.hpp"
typedef std::vector<std::vector<double> >::const_iterator PointIterator;
typedef std::vector<double>::const_iterator CoordIterator;
typedef Miniball::Miniball <Miniball::CoordAccessor<PointIterator, CoordIterator> > MB;
double get_reock(Polygon_Group pg) {
// the list of points as vector<int, 2>
vector<coordinate> lp;
// the list of points as vector<double>, because
// through experimentation, this was all that worked
vector<vector<double> > p;
for (Polygon poly : pg.borders) {
lp.insert(lp.end(), poly.border.begin(), poly.border.end());
}
p.reserve(lp.size());
for (int i = 0; i < lp.size(); i++) {
p.emplace_back(lp[i].begin(), lp[i].end());
}
lp.clear();
MB mb (2, p.begin(), p.end());
return (pg.get_area() / (mb.squared_radius() * PI));
}
As stated above, this function was too slow for my needs (taking 0.4 seconds in some cases).
Really, what I need to know is how to quickly collapse the all the points in Polygon_Group into some sort of list, and then have some way to find the minimum bounding circle of those points. I can handle the area myself.
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 try to implement a simple 3d voronoi application with Voro++. I have a container containing particles. After putting all the particles in the container, how can I get the vertices of all voronoicells computed by Voro++.
from the documentation it should be something like this:
http://math.lbl.gov/voro++/examples/polygons/
the doc says:
On line 47, a call is made to the face_vertices routine, which returns information about which vertices comprise each face. It is a vector of integers with a specific format: the first entry is a number k corresponding to the number of vertices making up a face, and this is followed k additional entries describing which vertices make up this face. For example, the sequence (3, 16, 20, 13) would correspond to a triangular face linking vertices 16, 20, and 13 together. On line 48, the vertex positions are returned: this corresponds to a vector of triplets (x, y, z) describing the position of each vertex.
i modified the example script so that it stores every particles cell connections and vertex positions in vectors. but please verify this!
hope this helps!
#include "voro++.hh"
#include "container.hh"
#include <v_compute.hh>
#include <c_loops.hh>
#include <vector>
#include <iostream>
using namespace voro;
int main() {
// Set up constants for the container geometry
const double x_min=-5,x_max=5;
const double y_min=-5,y_max=5;
const double z_min=0,z_max=10;
unsigned int i,j;
int id,nx,ny,nz;
double x,y,z;
std::vector<int> neigh;
voronoicell_neighbor c;
// Set up the number of blocks that the container is divided into
const int n_x=6,n_y=6,n_z=6;
// Create a container with the geometry given above, and make it
// non-periodic in each of the three coordinates. Allocate space for
// eight particles within each computational block
container con(x_min,x_max,y_min,y_max,z_min,z_max,n_x,n_y,n_z,
false,false,false,8);
//Randomly add particles into the container
con.import("pack_six_cube");
// Save the Voronoi network of all the particles to text files
// in gnuplot and POV-Ray formats
con.draw_cells_gnuplot("pack_ten_cube.gnu");
con.draw_cells_pov("pack_ten_cube_v.pov");
// Output the particles in POV-Ray format
con.draw_particles_pov("pack_ten_cube_p.pov");
// Loop over all particles in the container and compute each Voronoi
// cell
c_loop_all cl(con);
int dimension = 0;
if(cl.start()) do if(con.compute_cell(c,cl)) {
dimension+=1;
} while (cl.inc());
std::vector<std::vector<int> > face_connections(dimension);
std::vector<std::vector<double> > vertex_positions(dimension);
int counter = 0;
if(cl.start()) do if(con.compute_cell(c,cl)) {
cl.pos(x,y,z);id=cl.pid();
std::vector<int> f_vert;
std::vector<double> v;
// Gather information about the computed Voronoi cell
c.neighbors(neigh);
c.face_vertices(f_vert);
c.vertices(x,y,z,v);
face_connections[counter] = f_vert;
vertex_positions[counter] = v;
std::cout << f_vert.size() << std::endl;
std::cout << v.size() << std::endl;
counter += 1;
} while (cl.inc());
}
Regards, Lukas
I'm trying to get area of 2d polygon in 3d space. Is there any way to do this by Boost::Geometry? Here is my implementation, but it returns 0 all the time:
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
namespace bg = boost::geometry;
typedef bg::model::point<double, 3, bg::cs::cartesian> point3d;
int main()
{
bg::model::multi_point<point3d> square;
bg::read_wkt("MULTIPOINT((0 0 0), (0 2 0), (0 2 2), (0 0 2), (0 0 0))", square);
double area = bg::area(square);
std::cout << "Area: " << area << std::endl;
return 0;
}
UPD: Actually, I have the same issue with the simple 2d multi point square:
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/io/wkt/wkt.hpp>
namespace bg = boost::geometry;
typedef bg::model::point<double, 2, bg::cs::cartesian> point2d;
int main()
{
bg::model::multi_point<point2d> square;
bg::read_wkt("MULTIPOINT((0 0), (2 0), (2 2), (0 2))", square);
double area = bg::area(square);
std::cout << "Area: " << area << std::endl;
return 0;
}
Here is the result:
$ ./test_area
Area: 0
UPD: Looks like area calculation in the boost::geometry availabe only for the 2 dimensional polygons.
I wouldn't expect a collection of points to have an area. You would need the equivalent of a model::polygon<poind3d> but that does not appear to be supported at the moment.
If the points are guaranteed to be co-planar and the segment do not intersect each other, you could decompose the polygons as a series of triangles and compute the area with a little bit of linear-algebra, based on the following formula for the area of a triangle:
In case of non-convex polygons, the sum of the areas need to be adapted to subtract areas outside the polygon. The easiest way to achieve this is by using signed areas for the triangles, including positive contributions from right-hand triangles, and negative contributions from left-hand triangles:
Note that there seems to be some plans to include a cross_product implementation in Boost, but it doesn't appear to be included as of version 1.56. The following replacement should do the trick for your use-case:
point3d cross_product(const point3d& p1, const point3d& p2)
{
double x = bg::get<0>(p1);
double y = bg::get<1>(p1);
double z = bg::get<2>(p1);
double u = bg::get<0>(p2);
double v = bg::get<1>(p2);
double w = bg::get<2>(p2);
return point3d(y*w-z*v, z*u-x*w, x*v-y*u);
}
point3d cross_product(const bg::model::segment<point3d>& p1
, const bg::model::segment<point3d>& p2)
{
point3d v1(p1.second);
point3d v2(p2.second);
bg::subtract_point(v1, p1.first);
bg::subtract_point(v2, p2.first);
return cross_product(v1, v2);
}
The area can then be computed with something such as:
// compute the are of a collection of 3D points interpreted as a 3D polygon
// Note that there are no checks as to whether or not the points are
// indeed co-planar.
double area(bg::model::multi_point<point3d>& polygon)
{
if (polygon.size()<3) return 0;
bg::model::segment<point3d> v1(polygon[1], polygon[0]);
bg::model::segment<point3d> v2(polygon[2], polygon[0]);
// Compute the cross product for the first pair of points, to handle
// shapes that are not convex.
point3d n1 = cross_product(v1, v2);
double normSquared = bg::dot_product(n1, n1);
if (normSquared > 0)
{
bg::multiply_value(n1, 1.0/sqrt(normSquared));
}
// sum signed areas of triangles
double result = 0.0;
for (size_t i=1; i<polygon.size(); ++i)
{
bg::model::segment<point3d> v1(polygon[0], polygon[i-1]);
bg::model::segment<point3d> v2(polygon[0], polygon[i]);
result += bg::dot_product(cross_product(v1, v2), n1);
}
result *= 0.5;
return abs(result);
}
I am not familiar with the geometry section of boost, but with my knowledge of geometry, I can say that it would not be much different in 3D than 2D. Although there might be something in boost already, You could write your own method that does this fairly easily.
EDIT:
da code monkey pointed out that the shoelace formula would be more efficient in this way, because it is less complicated, and faster.
Original Idea below:
To calculate this, I would first tessellate the polygon into triangles, because any polygon can be split up into a number of triangles. I would take each of these triangles, and calculate the area of each of them. To do this in 3d space, the same concepts apply. To get the base, you take △ABC and arbitrarily assign —AB as base, —BC as height, and —CA as hypotenuse. Just do (—AB*—BC)/2 . Just add up the areas of each triangle.
I do not know if boost has a built in tessellate method, and this would be fairly difficult to implement in c++, but you would probably want to create a triangle fan of some sort. (NOTE: this only applies to convex polygons). If you do have a concave polygon, you should look into this: http://www.cs.unc.edu/~dm/CODE/GEM/chapter.html I will leave putting this into c++ as an exercise, but the process is fairly simple.
I have a set of 2D points each with an associated id. (e.g. if the points are stored in an array, the id is the index into each point 0,....,n-1 ).
Now I create a Delaunay triangulation of these points and want to list down all the finite edges. For each edge, I would like to have the ids of the points represented by corresponding 2 vertices. Example: if there's an edge between point 0 and point 2 then (0,2). Is this possible?
#include <vector>
#include <CGAL\Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL\Delaunay_triangulation_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Delaunay_triangulation_2<K> Delaunay;
typedef K::Point_2 Point;
void load_points(std::vector<Point>& rPoints)
{
rPoints.push_back(Point(10,10)); // first point
rPoints.push_back(Point(60,10)); // second point
rPoints.push_back(Point(30,40)); // third point
rPoints.push_back(Point(40,80)); // fourth point
}
void main()
{
std::vector<Point> points;
load_points(points);
Delaunay dt;
dt.insert(points.begin(),points.end());
for(Delaunay::Finite_edges_iterator it = dt.finite_edges_begin(); it != dt.finite_edges_end(); ++it)
{
}
}
First you need to use a vertex type with info as in these examples. Then an edge is a pair containing a handle to a face as well as the index of the vertex in the face that is opposite to the edge.
if you have:
Delaunay::Edge e=*it;
indices you are looking for are:
int i1= e.first->vertex( (e.second+1)%3 )->info();
int i2= e.first->vertex( (e.second+2)%3 )->info();
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;
}