Foreword: Starting today, Xiaoyi began to share knowledge about CGAL, mainly because the vtk library does not meet the needs. If my friends have the same needs as me, then let's learn together.
Main content: This article is included in the [CGAL Series] column, mainly explaining the creation, access, inspection and other content related to Surface_Mesh under CGAL! The main purpose is that after understanding the construction principle of Surface_Mesh, it can be converted with other data types and converted between different libraries.
Thank you for your likes + attention, Xiaoyi will continue to share and make progress together!
Table of contents
Get all points of Surface_Mesh
Get the associated points of Surface_Mesh points, edges, and faces
Kernel of CGAL
When we study the CGAL library, we must first understand the Kernel concept, which mainly defines three kinds of precision Kernel:
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Exact_predicates_exact_constructions_kernel K;
For the detailed explanation of Kernel, you can use the following link to learn about it:
Surface_Mesh
To understand Surface_Mesh, you first need to understand concepts such as BGL (Boost Graph Library) and half edge (half edge) in CGAL. The links below are for your reference:
CGAL 5.5.2 - Halfedge Data Structure: User's Manual
CGAL 5.5.2 - CGAL and Boost Graphics Library: User's Manual
CGAL 5.5.2 - Surface Meshes: User's Manual
New Mesh
data structure:
Surface_mesh::Vertex_index
Surface_mesh::Halfedge_index
Surface_mesh::Face_index
Surface_mesh::Edge_index
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
int main()
{
Mesh m;
// Add the points as vertices
vertex_descriptor u = m.add_vertex(K::Point_3(0,1,0));
vertex_descriptor v = m.add_vertex(K::Point_3(0,0,0));
vertex_descriptor w = m.add_vertex(K::Point_3(1,1,0));
vertex_descriptor x = m.add_vertex(K::Point_3(1,0,0));
m.add_face(u,v,w);
int num = num_faces(m); //结果num = 1
return 0;
}
At this time, a new Mesh composed of surfaces composed of u, v, and w is created.
check orientation
face_descriptor f = m.add_face(u,v,x);
num = num_faces(m); //结果num = 1
Question: Two faces are added, why only one face is obtained?
Reason: Wrong orientation.
if(f == Mesh::null_face())
{
std::cerr<<"The face could not be added because of an orientation error."<<std::endl;
f = m.add_face(u,x,v);
num = num_faces(m);//num = 2
}
Refer to CGAL 5.5.2 - Surface Meshes: User Manual Connectivity
Check for self-intersection
In many algorithms, the input Mesh is required to be a non-self-intersecting model. Now let's check if the above model is self-intersecting as follows
Mesh m;
// Add the points as vertices
vertex_descriptor u = m.add_vertex(K::Point_3(0,1,0));
vertex_descriptor v = m.add_vertex(K::Point_3(0,0,0));
vertex_descriptor w = m.add_vertex(K::Point_3(1,1,0));
vertex_descriptor x = m.add_vertex(K::Point_3(1,0,0));
m.add_face(u,v,w);
int num = num_faces(m);
face_descriptor f = m.add_face(u,v,x);
num = num_faces(m);
if(f == Mesh::null_face())
{
std::cerr<<"The face could not be added because of an orientation error."<<std::endl;
f = m.add_face(u,x,v);
num = num_faces(m);
//结果intersect = true; 即当前模型为自相交模型
bool intersect = CGAL::Polygon_mesh_processing::does_self_intersect(m);
assert(f != Mesh::null_face());
}
Replace u,x,v with v,x,w and see the self-intersection result again.
Mesh m;
// Add the points as vertices
vertex_descriptor u = m.add_vertex(K::Point_3(0,1,0));
vertex_descriptor v = m.add_vertex(K::Point_3(0,0,0));
vertex_descriptor w = m.add_vertex(K::Point_3(1,1,0));
vertex_descriptor x = m.add_vertex(K::Point_3(1,0,0));
m.add_face(u,v,w);
int num = num_faces(m);
face_descriptor f = m.add_face(u,v,x);
num = num_faces(m);
if(f == Mesh::null_face())
{
std::cerr<<"The face could not be added because of an orientation error."<<std::endl;
f = m.add_face(v, x, w);
num = num_faces(m);
bool intersect = CGAL::Polygon_mesh_processing::does_self_intersect(m);
//结果 intersect = false;
assert(f != Mesh::null_face());
}
ranges and iterators
Get all points of Surface_Mesh
#include <vector>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
int main()
{
Mesh m;
// u x
// +------------+
// | |
// | |
// | f |
// | |
// | |
// +------------+
// v w
// Add the points as vertices
vertex_descriptor u = m.add_vertex(K::Point_3(0,1,0));
vertex_descriptor v = m.add_vertex(K::Point_3(0,0,0));
vertex_descriptor w = m.add_vertex(K::Point_3(1,0,0));
vertex_descriptor x = m.add_vertex(K::Point_3(1,1,0));
/* face_descriptor f = */ m.add_face(u,v,w,x);
{
std::cout << "all vertices " << std::endl;
// The vertex iterator type is a nested type of the Vertex_range
Mesh::Vertex_range::iterator vb, ve;
Mesh::Vertex_range r = m.vertices();
// The iterators can be accessed through the C++ range API
vb = r.begin();
ve = r.end();
// or the boost Range API
vb = boost::begin(r);
ve = boost::end(r);
// or with boost::tie, as the CGAL range derives from std::pair
for(boost::tie(vb, ve) = m.vertices(); vb != ve; ++vb){
std::cout << *vb << std::endl;
}
// Instead of the classical for loop one can use
// the boost macro for a range
for(vertex_descriptor vd : m.vertices()){
std::cout << vd << std::endl;
}
// or the C++11 for loop. Note that there is a ':' and not a ',' as in BOOST_FOREACH
for(vertex_descriptor vd : m.vertices()){
std::cout << vd << std::endl;
}
}
return 0;
}
Get the associated points of Surface_Mesh points, edges, and faces
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <vector>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
int main()
{
Mesh m;
// u x
// +------------+
// | |
// | |
// | f |
// | |
// | |
// +------------+
// v w
// Add the points as vertices
vertex_descriptor u = m.add_vertex(K::Point_3(0,1,0));
vertex_descriptor v = m.add_vertex(K::Point_3(0,0,0));
vertex_descriptor w = m.add_vertex(K::Point_3(1,0,0));
vertex_descriptor x = m.add_vertex(K::Point_3(1,1,0));
face_descriptor f = m.add_face(u,v,w,x);
{
std::cout << "vertices around vertex " << v << std::endl;
CGAL::Vertex_around_target_circulator<Mesh> vbegin(m.halfedge(v),m), done(vbegin);
do {
std::cout << *vbegin++ << std::endl;
} while(vbegin != done);
}
{
std::cout << "vertices around face " << f << std::endl;
CGAL::Vertex_around_face_iterator<Mesh> vbegin, vend;
for(boost::tie(vbegin, vend) = vertices_around_face(m.halfedge(f), m);
vbegin != vend;
++vbegin){
std::cout << *vbegin << std::endl;
}
}
// or the same again, but directly with a range based loop
for(vertex_descriptor vd : vertices_around_face(m.halfedge(f), m)){
std::cout << vd << std::endl;
}
return 0;
}
In addition: vertices_around_target gets associated points such as lines and points.
halfedges_around_target & halfedges_around_face
faces_around_target & faces_around_face
Attributes
Get point information and coordinates
#include <string>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef Mesh::Vertex_index vertex_descriptor;
typedef Mesh::Face_index face_descriptor;
int main()
{
Mesh m;
vertex_descriptor v0 = m.add_vertex(K::Point_3(0,2,0));
vertex_descriptor v1 = m.add_vertex(K::Point_3(2,2,0));
vertex_descriptor v2 = m.add_vertex(K::Point_3(0,0,0));
vertex_descriptor v3 = m.add_vertex(K::Point_3(2,0,0));
vertex_descriptor v4 = m.add_vertex(K::Point_3(1,1,0));
m.add_face(v3, v1, v4);
m.add_face(v0, v4, v1);
m.add_face(v0, v2, v4);
m.add_face(v2, v3, v4);
// give each vertex a name, the default is empty
Mesh::Property_map<vertex_descriptor,std::string> name;
bool created;
boost::tie(name, created) = m.add_property_map<vertex_descriptor,std::string>("v:name","");
assert(created);
// add some names to the vertices
name[v0] = "hello";
name[v2] = "world";
{
// You get an existing property, and created will be false
Mesh::Property_map<vertex_descriptor,std::string> name;
bool created;
boost::tie(name, created) = m.add_property_map<vertex_descriptor,std::string>("v:name", "");
assert(! created);
}
// You can't get a property that does not exist
Mesh::Property_map<face_descriptor,std::string> gnus;
bool found;
boost::tie(gnus, found) = m.property_map<face_descriptor,std::string>("v:gnus");
assert(! found);
// retrieve the point property for which exists a convenience function
Mesh::Property_map<vertex_descriptor, K::Point_3> location = m.points();
for(vertex_descriptor vd : m.vertices()) {
std::cout << name[vd] << " @ " << location[vd] << std::endl;
}
std::vector<std::string> props = m.properties<vertex_descriptor>();
for(std::string p : props){
std::cout << p << std::endl;
}
// delete the string property again
m.remove_property_map(name);
return 0;
}
Another traversal method
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <iostream>
typedef CGAL::Simple_cartesian<double> K;
typedef K::Point_3 Point_3;
namespace My {
struct Mesh: public CGAL::Surface_mesh<Point_3> {
typedef CGAL::Surface_mesh<Point_3> Base;
std::string name;
};
} // namespace My
#define CGAL_GRAPH_TRAITS_INHERITANCE_CLASS_NAME My::Mesh
#define CGAL_GRAPH_TRAITS_INHERITANCE_BASE_CLASS_NAME CGAL::Surface_mesh<::Point_3>
#include <CGAL/boost/graph/graph_traits_inheritance_macros.h>
int main()
{
My::Mesh mesh;
CGAL::make_triangle(Point_3(0,0,0), Point_3(1,0,0), Point_3(1,1,1), mesh);
typedef boost::graph_traits<My::Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::property_map<My::Mesh,CGAL::vertex_point_t>::type Point_property_map;
Point_property_map ppm = get(CGAL::vertex_point, mesh);
for(vertex_descriptor vd : vertices(mesh)){
if (vd != boost::graph_traits<My::Mesh>::null_vertex()){
std::cout << vd << " at " << get(ppm, vd) << std::endl;
}
}
std::cout << CGAL::Polygon_mesh_processing::bbox(mesh) << std::endl;
return 0;
}
Thank you for your likes + attention, Xiaoyi will continue to share and make progress together!