前提知识:多面体表面
多面体表面是通过点(vertices)、边(edges)、没有洞的面(facets) 入射关系(incidence relationship)表示的。属于二维可定向流形(简单来说是分里面和外面的,莫比乌斯环属于不可定向流形)的多面体表面可以通过半边(halfedge)来存储这些结构。
所谓半边,是指构成多面体表面每条边的两条方向相反的半边。
如上图所示,多面体的每一个面(facets)是由一串有顺序的循环的半边定义的,这些半边是该面的边缘。
在三维空间中,多面体表面是一个封闭的表面,这要求构成多面体的面不可以有洞。一个洞周围的半边被称为border halfedge,border halfedge是没有incident facet的(也就是面的某条半边没有对面的那条半边)。一条边是border halfedge当且仅当它的其中一条halfedge是border halfedge。因此,如果一个表面不含border halfedge,那它就是封闭的,至少需要两个其他的面与之相邻。
人为规定,从多面体的外部观察,围绕面的半边按照逆时针排列,而这就意味着点周围的半边是按照顺时针排列的。
代码:
//构建一个正四面体,以off格式输出到屏幕并写入off文件
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Facet_iterator Facet_iterator;
typedef Polyhedron::Halfedge_around_facet_circulator Halfedge_facet_circulator;
int main() {
/*--step 1. 建立正四面体--*/
//设置四面体的4个顶点,类型为Point_3(const)
Point_3 p(0.0, 0.0, 0.0);//点(0.0,0.0,0.0)
Point_3 q(1.0, 0.0, 0.0);//点(1.0,0.0,0.0)
Point_3 r(0.0, 1.0, 0.0);//点(0.0,1.0,0.0)
Point_3 s(0.0, 0.0, 1.0);//点(0.0,0.0,1.0)
//建立多面体变量P
Polyhedron P;
//从点p,q,r,s构建一个四面体,返回入射顶点初始化为p的四面体的半边。
P.make_tetrahedron(p, q, r, s);
/*--step 2. 准备输出--*/
//将IO流std::cout的格式设置为ascii码
CGAL::set_ascii_mode(std::cout);
//step 2.1 输出到屏幕
//OFF文件格式
/*--------------------------------------------------------------------------------------------------
OFF文件全是以OFF关键字开始的ASCII文件。
下一行说明顶点的数量、面片的数量、边的数量。边的数量可以安全地省略。
顶点按每行一个列出x、y、z坐标。
在顶点列表后,面片按照每行一个列表,对于每个面片,顶点的数量是指定的,接下来是顶点索引列表。
例如:
OFF
顶点数 面片数 边数(总是0)
x y z
x y z
...
n个顶点 顶点1的索引 顶点2的索引 … 顶点n的索引(顶点索引从0开始)
...
----------------------------------------------------------------------------------------------------*/
//输出关键字OFF,顶点数,面数,边数(0)
std::cout << "OFF" << std::endl << P.size_of_vertices() << ' '
<< P.size_of_facets() << " 0" << std::endl;
//将多面体P点序列p,q,r,s连续(iterator)写入(std::copy)输出流(std::ostream)中,并打印在屏幕上
std::copy(P.points_begin(), P.points_end(),
std::ostream_iterator<Point_3>(std::cout, "\n"));
//输出每个面的顶点索引列表
for (Facet_iterator i = P.facets_begin(); i != P.facets_end(); ++i) {//迭代所有面
Halfedge_facet_circulator j = i->facet_begin();//围绕面的半边
CGAL_assertion(CGAL::circulator_size(j) >= 3);//使用断言检查每个面至少是三角形(有三条半边)(没有洞)
std::cout << CGAL::circulator_size(j) << ' ';//输出面的顶点数
do {
std::cout << ' ' << std::distance(P.vertices_begin(), j->vertex());//通过计算两个迭代器间的距离(中间有多少个元素)计算点索引号
} while (++j != i->facet_begin());//一个面的循环结束
std::cout << std::endl;
}//多面体循环结束
//step 2.2 输出到文件
//文件指针F
std::ofstream F;
//使用写方式创建并打开文件regular_tetrahedron.off
F.open("data/regular_tetrahedron.off");
//输出关键字OFF,顶点数,面数,边数(0)
F << "OFF" << std::endl << P.size_of_vertices() << ' '
<< P.size_of_facets() << " 0" << std::endl;
std::copy(P.points_begin(), P.points_end(),
std::ostream_iterator<Point_3>(F, "\n"));
for (Facet_iterator i = P.facets_begin(); i != P.facets_end(); ++i) {
Halfedge_facet_circulator j = i->facet_begin();
CGAL_assertion(CGAL::circulator_size(j) >= 3);
F << CGAL::circulator_size(j) << ' ';
do {
F << ' ' << std::distance(P.vertices_begin(), j->vertex());
} while (++j != i->facet_begin());
F << std::endl;
}
//关闭文件
F.close();
//程序结束
return 0;
}
结果:
-
输出:
-
使用meshlab查看off文件:
参考
CGAL文档
https://doc.cgal.org/latest/Polyhedron/index.html
一位好心人的翻译
https://www.douban.com/note/545941784/
关于OFF文件格式
https://blog.csdn.net/A_L_A_N/article/details/84874463
P.S. 我的公众号