Get vertex numbers of CGAL polygon intersection - c++

I want to efficiently create a list of the vertex number of a polygon that intersects. This in order to check if I move the polygons slightly I can check whether it is the same intersection or a completely different one. Presumably if at least one vertex number remains in the list it is the same intersection.
So I have polygon P and Q at t=0 intersecting, this should give me vertex number [1,2,3] for P and [2] for polygon Q, see image for clarification. At t=1 I would get [1,2] for P and [2] for Q.
Currently I first retrieve the intersection and than for both polygons loop through the vertices and use the has_on_boundary function to determine if it is part of the intersection and store the vertex number. See code:
//Standard Includes
#include <iostream>
using std::cout;
using std::endl;
//Include CGAL options
#define CGAL_HEADER_ONLY 1 //Defining to use the CGAL library as header only
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
//Typedefs for CGAL Uses.
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2;
typedef CGAL::Polygon_2<Kernel> Polygon_2;
typedef CGAL::Polygon_with_holes_2<Kernel> Polygon_with_holes_2;
typedef std::vector<Polygon_with_holes_2> Pwh_list_2;
int main() {
Polygon_2 P;
P.push_back(Point_2(-1, 1));
P.push_back(Point_2(-0.01, -1));
P.push_back(Point_2(0, -1));
P.push_back(Point_2(0.01, -1));
P.push_back(Point_2(1, 1));
Polygon_2 Q;
Q.push_back(Point_2(-1, -1));
Q.push_back(Point_2(1, -1));
Q.push_back(Point_2(0, 1));
Pwh_list_2 polygon_intersections;
CGAL::intersection(P, Q, std::back_inserter(polygon_intersections));
//For ease now only assume one intersection
Polygon_2 intersection = polygon_intersections[0].outer_boundary();
//Get vertex number of P in intersection
int vertex_nr = 0;
std::set<int> overlapping_vertices_P;
cout << "Find intersection vertices of Polygon P" << endl;
for (auto it = P.vertices_begin(); it != P.vertices_end(); ++it, ++vertex_nr) {
if (intersection.has_on_boundary(*it)) {
overlapping_vertices_P.insert(vertex_nr);
cout << vertex_nr << endl;
}
}
//Get vertex number of Q in intersection
vertex_nr = 0;
std::set<int> overlapping_vertices_Q;
cout << "Find intersection vertices of Polygon Q" << endl;
for (auto it = Q.vertices_begin(); it != Q.vertices_end(); ++it, ++vertex_nr) {
if (intersection.has_on_boundary(*it)) {
overlapping_vertices_Q.insert(vertex_nr);
cout << vertex_nr << endl;
}
}
return 0;
}
Question: It works but seems very inefficient to me. Is there a better way to do this?
Any help or suggestions are highly appreciated.

Related

Given points, calculate normals and perform polygonal surface reconstruction in CGAL

I have .xyz files and I want to recreate the surface using 'polygonal surface reconstruction' in CGAL. Normals are needed to reconstruct the surface, so I want to first calculate these in CGAL and then use the points and their normals as input for the polygonal surface reconstruction.
However, the output and input types are different for both:
The output of the normal estimation is a list of vectorpairs, see https://doc.cgal.org/latest/Point_set_processing_3/index.html#Point_set_processing_3NormalOrientation : 12.1.1 Example; while the input for polygonal surface reconstruction is a point_vector ((https://doc.cgal.org/latest/Polygonal_surface_reconstruction/index.html).
I tried to create a "convert_function" which converts the data formats, but I am not a C++ programmer thus I have difficulty converting. If anybody could help me with this, that would be great, because I've been stuck on it for hours. My goal is to make this process as time efficient as possible, so better ideas on how to tackle this problem are highly appreciated.
data types:
->Normal calculation:
typedef std::pair<Point, Vector> PointVectorPair;
->Surface reconstruction:
typedef boost::tuple<Point, Vector, int> PNI;
typedef std::vector<PNI> Point_vector;
typedef CGAL::Nth_of_tuple_property_map<0, PNI> Point_map;
typedef CGAL::Nth_of_tuple_property_map<1, PNI> Normal_map;
typedef CGAL::Nth_of_tuple_property_map<2, PNI> Plane_index_map;
My invert function does not work because the syntax is not correct:
std::vector<PNI> convert_function(const std::list<PointVectorPair>& list)
{
std::vector<PNI> out;//a temporary object to store the output
for (std::size_t i = 0; i < list.size(); ++i) {
{
out[i].get<0>() = list[i].get<0>();
out[i].get<1>() = list[i].get<1>();
}
return out;
}
A little further on I then use the following:
std::vector<PNI> points2; // store points
points2 = convert_function(points);
The complete code right now is the following:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_xyz_points.h>
#include <CGAL/IO/Writer_OFF.h>
#include <CGAL/property_map.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Shape_detection/Efficient_RANSAC.h>
#include <CGAL/Polygonal_surface_reconstruction.h>
//extras formal calculation
#include <CGAL/compute_average_spacing.h>
#include <CGAL/pca_estimate_normals.h>
#include <CGAL/mst_orient_normals.h>
#include <utility> // defines std::pair
#include <list>
///////////// TRY TO COMBINE NORMAL CALCULATION + RANSAC + POLYFIT
#ifdef CGAL_USE_SCIP // defined (or not) by CMake scripts, do not define by hand
#include <CGAL/SCIP_mixed_integer_program_traits.h>
typedef CGAL::SCIP_mixed_integer_program_traits<double> MIP_Solver;
#elif defined(CGAL_USE_GLPK) // defined (or not) by CMake scripts, do not define by hand
#include <CGAL/GLPK_mixed_integer_program_traits.h>
typedef CGAL::GLPK_mixed_integer_program_traits<double> MIP_Solver;
#endif
#if defined(CGAL_USE_GLPK) || defined(CGAL_USE_SCIP)
#include <CGAL/Timer.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
// Point with normal, and plane index
//
typedef boost::tuple<Point, Vector, int> PNI;
typedef std::vector<PNI> Point_vector;
//bij normals calc: Point with normal vector stored in a std::pair.
//typedef std::pair<Point, Vector> PointVectorPair;
typedef CGAL::Nth_of_tuple_property_map<0, PNI> Point_map;
typedef CGAL::Nth_of_tuple_property_map<1, PNI> Normal_map;
typedef CGAL::Nth_of_tuple_property_map<2, PNI> Plane_index_map;
typedef std::pair<Point, Vector> PointVectorPair;
typedef CGAL::Shape_detection::Efficient_RANSAC_traits<Kernel, Point_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection::Plane<Traits> Plane;
typedef CGAL::Shape_detection::Point_to_shape_index_map<Traits> Point_to_shape_index_map;
typedef CGAL::Polygonal_surface_reconstruction<Kernel> Polygonal_surface_reconstruction;
typedef CGAL::Surface_mesh<Point> Surface_mesh;
// Concurrency
typedef CGAL::Parallel_if_available_tag Concurrency_tag;
std::vector<PNI> convert_function(const std::list<PointVectorPair>& list)
{
std::vector<PNI> out;//a temporary object to store the output
for (std::size_t i = 0; i < list.size(); ++i) {
{
out[i].get<0>() = list[i].get<0>();
out[i].get<1>() = list[i].get<1>();
}
return out;
}
/*
* This example first extracts planes from the input point cloud
* (using RANSAC with default parameters) and then reconstructs
* the surface model from the planes.
*/
int main(int argc, char* argv[])
{
const char* fname = (argc > 1) ? argv[1] : "data/47038.xyz";
// Reads a .xyz point set file in points[].
std::list<PointVectorPair> points;
std::ifstream stream(fname);
if (!stream ||
!CGAL::read_xyz_points(stream,
std::back_inserter(points),
CGAL::parameters::point_map(CGAL::First_of_pair_property_map<PointVectorPair>())))
{
std::cerr << "Error: cannot read file " << fname << std::endl;
return EXIT_FAILURE;
}
std::cout << "Calculating normals...";
CGAL::Timer t;
t.start();
// Estimates normals direction.
// Note: pca_estimate_normals() requires a range of points
// as well as property maps to access each point's position and normal.
const int nb_neighbors = 18; // K-nearest neighbors = 3 rings
if (argc > 2 && std::strcmp(argv[2], "-r") == 0) // Use a fixed neighborhood radius
{
// First compute a spacing using the K parameter
double spacing
= CGAL::compute_average_spacing<Concurrency_tag>
(points, nb_neighbors,
CGAL::parameters::point_map(CGAL::First_of_pair_property_map<PointVectorPair>()));
// Then, estimate normals with a fixed radius
CGAL::pca_estimate_normals<Concurrency_tag>
(points,
0, // when using a neighborhood radius, K=0 means no limit on the number of neighbors returns
CGAL::parameters::point_map(CGAL::First_of_pair_property_map<PointVectorPair>()).
normal_map(CGAL::Second_of_pair_property_map<PointVectorPair>()).
neighbor_radius(2. * spacing)); // use 2*spacing as neighborhood radius
}
else // Use a fixed number of neighbors
{
CGAL::pca_estimate_normals<Concurrency_tag>
(points, nb_neighbors,
CGAL::parameters::point_map(CGAL::First_of_pair_property_map<PointVectorPair>()).
normal_map(CGAL::Second_of_pair_property_map<PointVectorPair>()));
}
// Orients normals.
// Note: mst_orient_normals() requires a range of points
// as well as property maps to access each point's position and normal.
std::list<PointVectorPair>::iterator unoriented_points_begin =
CGAL::mst_orient_normals(points, nb_neighbors,
CGAL::parameters::point_map(CGAL::First_of_pair_property_map<PointVectorPair>()).
normal_map(CGAL::Second_of_pair_property_map<PointVectorPair>()));
// Optional: delete points with an unoriented normal
// if you plan to call a reconstruction algorithm that expects oriented normals.
points.erase(unoriented_points_begin, points.end());
/// Convert pointvectorpair to pointvector
std::vector<PNI> points2; // store points
points2 = convert_function(points);
///
/// use points + normals for surface reconstruction
///
// Shape detection
Efficient_ransac ransac;
ransac.set_input(points2);
ransac.add_shape_factory<Plane>();
std::cout << "Extracting planes...";
t.reset();
ransac.detect();
Efficient_ransac::Plane_range planes = ransac.planes();
std::size_t num_planes = planes.size();
std::cout << " Done. " << num_planes << " planes extracted. Time: " << t.time() << " sec." << std::endl;
// Stores the plane index of each point as the third element of the tuple.
Point_to_shape_index_map shape_index_map(points, planes);
for (std::size_t i = 0; i < points.size(); ++i) {
// Uses the get function from the property map that accesses the 3rd element of the tuple.
int plane_index = get(shape_index_map, i);
points2[i].get<2>() = plane_index;
}
//////////////////////////////////////////////////////////////////////////
std::cout << "Generating candidate faces...";
t.reset();
Polygonal_surface_reconstruction algo(
points,
Point_map(),
Normal_map(),
Plane_index_map()
);
std::cout << " Done. Time: " << t.time() << " sec." << std::endl;
//////////////////////////////////////////////////////////////////////////
Surface_mesh model;
std::cout << "Reconstructing...";
t.reset();
if (!algo.reconstruct<MIP_Solver>(model)) {
std::cerr << " Failed: " << algo.error_message() << std::endl;
return EXIT_FAILURE;
}
const std::string& output_file("data/cube_result_47038.off");
std::ofstream output_stream(output_file.c_str());
if (output_stream && CGAL::write_off(output_stream, model)) {
// flush the buffer
output_stream << std::flush;
std::cout << " Done. Saved to " << output_file << ". Time: " << t.time() << " sec." << std::endl;
}
else {
std::cerr << " Failed saving file." << std::endl;
return EXIT_FAILURE;
}
//////////////////////////////////////////////////////////////////////////
// Also stores the candidate faces as a surface mesh to a file
Surface_mesh candidate_faces;
algo.output_candidate_faces(candidate_faces);
const std::string& candidate_faces_file("data/cube_candidate_faces_47038.off");
std::ofstream candidate_stream(candidate_faces_file.c_str());
if (candidate_stream && CGAL::write_off(candidate_stream, candidate_faces)) {
// flush the buffer
output_stream << std::flush;
std::cout << "Candidate faces saved to " << candidate_faces_file << "." << std::endl;
}
return EXIT_SUCCESS;
}
#else
int main(int, char**)
{
std::cerr << "This test requires either GLPK or SCIP.\n";
return EXIT_SUCCESS;
}
#endif // defined(CGAL_USE_GLPK) || defined(CGAL_USE_SCIP)
Thank you,
Barbara
You try to use operator[] on a list:
std::vector<PNI> convert_function(const std::list<PointVectorPair>& list)
{
...
for (std::size_t i = 0; i < list.size(); ++i)
{
out[i].get<0>() = list[i].get<0>();
You simply cannot do list[i] because std::list doesn't offer it.
https://en.cppreference.com/w/cpp/container/list
Instead you can use:
auto it = list.begin();
and
it != list.end();
and
it++;
combined with
(*it).get<0>();
that lets you start at the beginning, detect the end and advance an iterator. Combined with a dereference of the iterator.
You will also need to resize() your out vector, before trying to write to it.

Boost Graph bellman_ford_shortest_paths with labeled_graph

I'm trying to run the Bellman-Ford algorithm using the Boost Library. I have a labeled graph, but I'm getting an exception invalid conversion from ‘void*’ to ‘int. Any help would only be appreciated. Here is my code:
// g++ -std=c++17 -Wall test.c++ -l boost_system && ./a.out
#include <iostream> // for cout
#include <utility> // for pair
#include <algorithm> // for for_each
#include <vector> // For dist[] and pred[]
#include <limits> // To reliably indicate infinity
#include <map>
#include <list>
#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/directed_graph.hpp>
#include <boost/graph/labeled_graph.hpp>
#include <boost/graph/bellman_ford_shortest_paths.hpp>
using namespace boost;
using namespace std;
class Node
{
public:
int id;
int group;
};
struct EdgeProperties {
double weight;
EdgeProperties(){}
EdgeProperties(double w){ weight = w; }
};
typedef labeled_graph<adjacency_list<hash_setS, hash_setS, directedS, Node, EdgeProperties>, int> Graph;
int main(){
cout << "Calling main()" << endl;
Graph g;
// populate the graph
{
add_vertex( 0, g );
g[0].id = 0;
g[0].group = 10;
add_vertex( 1, g );
g[1].id = 1;
g[1].group = 20;
add_edge_by_label( 0, 1, EdgeProperties(110), g);
add_edge_by_label( 1, 0, EdgeProperties(222), g);
print_graph(g, get(&Node::id, g));
cout << "There are " << num_vertices(g) << " nodes and " << num_edges(g) << " edges in the graph" << endl;
}
// number of verticies in the graph
auto n = num_vertices(g);
// weight map
auto ewp = weight_map(get(&EdgeProperties::weight, g.graph()));
const int source = 0;
const int target = 1;
// Distance Map (with n elements of value infinity; source's value is 0)
auto inf = numeric_limits<double>::max();
vector<double> dist(n, inf);
dist[source] = 0.0;
// Predecessor Map (with n elements)
vector<int> pred(n);
bellman_ford_shortest_paths(
g.graph(),
n,
ewp
.distance_map(make_iterator_property_map(dist.begin(), get(&Node::id, g)))
.predecessor_map(make_iterator_property_map(pred.begin(), get(&Node::id, g)))
);
return 0;
}
I saw the example on https://www.boost.org/doc/libs/1_53_0/libs/graph/example/bellman-example.cpp but the example uses not a labeled graph.
Here is a live preview of my code:
https://wandbox.org/permlink/WsQA8A0IyRvGWTIj
Thank you
The source of the problem has been touched upon in the existing answer you accepted.
However, there's more to this.
Firstly, you're pretty much "within your right" to want use Node::id as the vertex index, and there could be many good reasons to use something else than vector as the vertex container selector¹.
Secondly, that stuff should... probably have worked. bellman_ford documents:
The PredecessorMap type must be a Read/Write Property Map which key and vertex types the same as the vertex descriptor type of the graph.
And iterator_property_map documents:
This property map is an adaptor that converts any random access iterator into a Lvalue Property Map. The OffsetMap type is responsible for converting key objects to integers that can be used as offsets with the random access iterator.
Now LValuePropertyMap might in fact be readonly, but in this case it clearly shouldn't be.
When using make_iterator_property_map with the additional id-map parameter, it should in fact be behaving like any associative property map both the key and value types vertex_descriptor as required by the algorithm.
UPDATE See "BONUS" below
I might dive in a little more detail later to see why that didn't work, but for now let's just work around the issue without modifying the graph model:
Live On Coliru
auto gg = g.graph();
auto id = get(&Node::id, gg);
std::map<Graph::vertex_descriptor, Graph::vertex_descriptor> assoc_pred;
bellman_ford_shortest_paths(gg, n,
weight_map(get(&EdgeProperties::weight, gg))
.distance_map(make_iterator_property_map(dist.begin(), id))
.predecessor_map(make_assoc_property_map(assoc_pred))
);
That works as it should and as expected:
Calling main()
1 --> 0
0 --> 1
There are 2 nodes and 2 edges in the graph
BONUS
I found the missing link: the predecessor map was defined with the wrong value-type:
vector<Graph::vertex_descriptor> pred(n);
Will obviously work: Live On Coliru
¹ that's subtly different from the vertex descriptor, but related in the sense that the choice of vertex container will usually predict the actual type of vertex descriptor

CGAL Triangulation edge iterator includes origin

I'm currently learning to perform 3D triangulation using CGAL, and so far I've managed to create a regular tetrahedron by inserting and triangulating 4 vertices. But when I try to loop over the edges of the tetrahedron and obtain the vertices corresponding to that edge, I get the origin as a vertex or duplicates of previous edges. In 2D it all works fine, but in 3D things go wrong. I think this is related to an infinite/undefined vertex being included, but I don't know how to deal with this. Any help would be appreciated.
My code (modified from this question):
#include <vector>
#include <iostream>
#include <cmath>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_3.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Delaunay_triangulation_3<K> Delaunay;
typedef K::Point_3 Point;
void load_points(std::vector<Point>& rPoints)
{
rPoints.push_back(Point(1.0, 0.0, -1.0/sqrt(2))); // first point
rPoints.push_back(Point(-1.0, 0.0, -1.0/sqrt(2))); // second point
rPoints.push_back(Point(0.0, 1.0, 1.0/sqrt(2))); // third point
rPoints.push_back(Point(0.0, -1.0, 1.0/sqrt(2))); // fourth point
}
int 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)
{
Point i1= it->first->vertex( (it->second+1)%3 )->point();
Point i2= it->first->vertex( (it->second+2)%3 )->point();
std::cout << "i1: " << i1 << "\t i2: " << i2 << "\n";
}
return 0;
}
The documentation for an edge indicates that it's a triple: (c,i,j) is the edge of cell c whose vertices indices are i and j.
So in your code you should write:
Point i1= it->first->vertex( it->second )->point();
Point i2= it->first->vertex( it->third )->point();

Compute intersection area of two polygons with cgal

Given the vertices of two convex polygons, what is the simplest way of computing the area of their intersection using cgal?
Because you are working with convex polygons, there is no need to worry about holes. So the simplest code that I can think of is basically construct the polygons, call intersection, loop over intersection and total up the area::
#include <iostream>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/Polygon_2_algorithms.h>
typedef CGAL::Simple_cartesian<double> K;
typedef K::Point_2 Point;
typedef CGAL::Polygon_2<K> Polygon_2;
typedef CGAL::Polygon_with_holes_2<K> Polygon_with_holes_2;
using std::cout; using std::endl;
int main(){
Point points[] = { Point(0,0), Point(1,0), Point(1,1), Point(0,1)};
Point points2[] = { Point(0.5,0.5), Point(1.5,0.5), Point(1.5,1.5), Point(0.5,1.5)};
Polygon_2 poly1(points, points+4);
Polygon_2 poly2(points2, points2+4);
//CGAL::General_polygon_with_holes_2<K> poly3;
std::list<Polygon_with_holes_2> polyI;
CGAL::intersection(poly1, poly2, std::back_inserter(polyI));
double totalArea = 0;
typedef std::list<Polygon_with_holes_2>::iterator LIT;
for(LIT lit = polyI.begin(); lit!=polyI.end(); lit++){
totalArea+=lit->outer_boundary().area();
}
cout << "TotalArea::" << totalArea;
}

CGAL: Find face/triangle a point belongs to?

After reading about it I've come to this:
#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 >& points)
{
points.push_back(Point(1., 1.));
points.push_back(Point(2., 1.));
points.push_back(Point(2., 2.));
points.push_back(Point(1., 2.));
}
int main()
{
std::vector< Point > points;
load_points(points);
Delaunay dt;
dt.insert(points.begin(), points.end());
std::cout << dt.number_of_vertices() << std::endl;
typedef std::vector<Delaunay::Face_handle> Faces;
Faces faces;
std::back_insert_iterator<Faces> result( faces );
result = dt.get_conflicts ( Delaunay::Point(1.5, 1.5),
std::back_inserter(faces) );
return 0;
}
That should find the faces whose circumcircle contains the point. After that, I'd have to take these triangles and use a method to test if the point is inside them I think (does CGAL do this? I know it's easy to implement though).
Anyway, how can I get the triangles out of faces?
Answer is
CGAL::Triangle_2<K> f = dt.triangle(faces[0]);
std::cout << dt.triangle(faces[0]) << std::endl;
std::cout << dt.triangle(faces[1]) << std::endl;
etc
I don't know how to use the Triangle class well but it's a start at least.
I was gonna make an actual answer but stackoverflow didn't allow me to so.
You should use the locate member function which documentation can be found here.