前言:从今天起,小易开始分享关于CGAL相关的知识,主要是因为vtk库不满足需求,如果小伙伴跟我有同样的需求,那么我们一起学起来吧。
主要内容:该文收录在【CGAL系列】专栏中,主要讲解关于CGAL下的Surface_Mesh相关的创建、访问、检查等内容!目的主要是了解了Surface_Mesh的构造原理后,可以与其他数据类型进行相互转换,以及不同库之间的相互转换。
感谢各位小伙伴的点赞+关注,小易会继续分享,一起进步!
目录
CGAL的Kernel
在我们学习CGAL库时,首先要了解Kernel概念,它主要定义了三种精度的Kernel:
typedef CGAL::Simple_cartesian<double> K;
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Exact_predicates_exact_constructions_kernel K;
关于Kernel的详解,大家可以通过以下链接进行了解:
计算机图形学算法 CGAL计算精度Kernel 图形处理 二三维数据处理_哔哩哔哩_bilibili
Surface_Mesh
想要了解Surface_Mesh,首先需要了解CGAL中的BGL(Boost Graph Library)、半边(half edge)等概念,下面给出链接大家可以参考:
计算机图形学算法 CGAL BGL HalfedgeGraph Halfedge Surface_mesh PropertyMaps 图形处理 二三维数据处理_哔哩哔哩_bilibili
新建Mesh
数据结构:
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;
}
这时,新建了一个由u,v,w组成的面构成的Mesh。
检查方向(check orientation)
face_descriptor f = m.add_face(u,v,x);
num = num_faces(m); //结果num = 1
问题:添加了两个面,为什么只获取到一个面的结果?
原因:方向出错。
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
}
参考CGAL 5.5.2 - 表面网格:用户手册 连接性
检查自相交
在很多算法中,对于输入的Mesh都要求是非自相交的模型。现在来检查以下上述模型是否自相交
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());
}
将u,x,v替换为v,x,w,再次查看自相交结果。
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());
}
范围和迭代器
获取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;
}
获取Surface_Mesh点、边、面的关联点
#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;
}
另外还有:vertices_around_target获取线、点等关联的点。
halfedges_around_target & halfedges_around_face
faces_around_target & faces_around_face
属性
获取点的信息及坐标值
#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;
}
另一种遍历方法
#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;
}
感谢这小伙伴的点赞+关注,小易会继续分享,一起进步!