CGAL: Find face/triangle a point belongs to? - c++

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.

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.

Get vertex numbers of CGAL polygon intersection

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.

Boost graph segmentation fault

I am trying to use Boost to embed a planar graph using the Chrobak-Payne algorithm.
I am able to run the example successfully, but when I try to modify it and use different graphs it does not work correctly. I am trying to embed the second platonic graph but it does not work, and my code crashes with "Segmentation fault: 11". I assumed it is because I needed to use make_connected, make_biconnected_planar, and make_maximal_planar, but adding them did not fix it.
Here is the modified source example using the second platonic graph and the three helper functions:
//=======================================================================
// Copyright 2007 Aaron Windsor
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#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 <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>,
property<edge_index_t, int>
>
graph;
graph g(7);
add_edge(0,1,g);
add_edge(1,2,g);
add_edge(2,3,g);
add_edge(3,0,g);
add_edge(0,4,g);
add_edge(1,5,g);
add_edge(2,6,g);
add_edge(3,7,g);
add_edge(4,5,g);
add_edge(5,6,g);
add_edge(6,7,g);
add_edge(7,4,g);
make_connected(g); //Make connected (1/3)
//Compute the planar embedding as a side-effect
typedef std::vector< graph_traits<graph>::edge_descriptor > vec_t;
std::vector<vec_t> embedding(num_vertices(g));
boyer_myrvold_planarity_test(boyer_myrvold_params::graph = g,
boyer_myrvold_params::embedding =
&embedding[0]
);
make_biconnected_planar(g, &embedding[0]); //Make biconnected planar (2/3)
make_maximal_planar(g, &embedding[0]); //Make maximal planar (3/3)
//Find a canonical ordering
std::vector<graph_traits<graph>::vertex_descriptor> ordering;
planar_canonical_ordering(g, &embedding[0], 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(boost::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;
}
But for some reason I am still getting a segmentation fault. I know it is at the call:
chrobak_payne_straight_line_drawing(g,
embedding,
ordering.begin(),
ordering.end(),
straight_line_drawing
);
because it runs fine without it (but does not compute the embedding). Where is the memory issue causing this segmentation fault? The graph I am embedding is smaller than the example.
From must be a maximal planar graph with at least 3 vertices, the requirement that k > 2 is needed for success. Your call to Planar Canonical Ordering returned two vertices. Catch is chrobak_payne_straight_line_drawing does no checking for you and it asserts at the vector iterator test in the std.
add:
assert( ordering.size( ) > 2 );
before calling, or a conditional, depends what you are up to.
one more edge:
add_edge(1,4,g);
And it would have worked.

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;
}