CGAL::Nef_polyhedron 参考文档详解

       CGAL中的 Nef_polyhedron_3 是处理3D 多面体布尔运算(交、并、差、补)的核心类,特别适合处理带孔洞、非流形、自相交的复杂 3D 几何体,是工业级几何处理的重要工具。Nef_P支持任意拓扑结构的布尔运算,并且能保证布尔运算的精确无误差。

这篇文章基于Nef_polyhedron的参考文档,对Nef_polyhedron做一个学习记录。

参见:CGAL 6.1.1 - 3D Boolean Operations on Nef Polyhedra: User Manual

1 引言

        在实体建模中,主要使用两种表示方案:构造实体几何(CSG)和边界表示(B-rep)。

构造实体几何(CSG) 是一种通过简单几何基元 + 布尔运算构建复杂三维实体的建模方法,核心是用 “搭积木 + 逻辑运算” 描述物体,数据紧凑、修改方便,广泛用于 CAD、3D 打印与游戏开发。“搭积木 + 逻辑运算”这一过程通过逻辑树来完成,CSG模型并不是真正的几何实体,无法直接渲染 / 显示,需实时计算边界(BRep 转换)

B-rep:描述实体边界上所有低维特征(顶点、边、面)的邻接关系与几何属性,是存顶点、边、面,真正的网格。但普通网格一做布尔就容易崩:出现洞、细线、尖点、多个面贴在一起 → 流行结构变成 非流形。

        而CGAL团队设计的Nef多面体就很好的将上面两个结合起来:

        Nef多面体是真正的网格,能够存储点线面,能够处理带洞、空心、交叉、非流形、无限大平面……等任何复杂形状,并且bool运算不会崩溃。

2 定义

Nef 多面体 = 从无限大平面(半空间)出发,通过交、并、差、补,拼出来的任何 3D 形状。

这个类的层次关系

/*
Nef_polyhedron_3				← 外壳类(对外接口)
	↓
Nef_polyhedron_3_rep			← 实际存储数据的“本体”
	↓
SNC_structure					← 核心SNC 结构的具体实现
	↓
Vertex/Edge/Facet/Volume		← 3D 全局几何元素
Sphere_map (球面映射)			← 顶点局部邻域结构
*/

Nef_polyhedron_3:对外接口,主要用到这个。

Nef_polyhedron_3_rep:两个核心成员

SNC_structure snc_;       // 真正的 SNC 结构(顶点/边/面/体+拓扑)
SNC_point_locator* pl_;   // 点定位器,用于判断一个点是否在nef内部

SNC_structure:核心数据结构:定义如下

元素维度作用关键特性
Vertex0D顶点每个顶点绑定一个 Sphere_map(球面映射)
Halfedge1D有向边(正反两条)互指反向边,关联顶点 + 面
Halffacet2D有向面(正反两个)由边构成环(外环 + 内环),关联体
Volume3D体(空间区域)由壳(Shell)组成,标记 “内部 / 外部”

Sphere_map(球面映射):SNC_structure下的另一个结构,每个顶点都有一个 “球面映射”,这是 Nef 能处理非流形的核心,Nef在顶点处套一个无限小的球,球面上的几何 / 拓扑结构(边、面的交点和连线)就是这个顶点的球面映射;它用 2D 球面拓扑,完整描述了 3D 顶点周围的所有邻接关系,哪怕是非流形顶点也能精准表示。

把顶点周围的所有面 / 边,与这个小球求交:

        边与球面的交点 → 球面映射的顶点(SVertex)

        面与球面的交线 → 球面映射的边(SHalfedge)

        球面被分割后的区域 → 球面映射的面(SFace)

3 Infimaximal Box 无穷极大盒

用一个 足够大但有限 的虚拟盒子,把无限的东西 框住,让计算机能像处理普通有限模型一样计算。

4 正则化布尔运算

普通布尔运算可能会产生:孤点,细线,薄片

这些现实中不存在,也没用。

正则化 = 自动把这些垃圾去掉,只保留真正的 “实体”。

公式:正则化 = 先取内部,再取闭包

5 示例程序

下面将展示官网给的几个示例,方便快速上手这个类,每个示例中详细解释。

这些例子中涉及到CGAL中的代数运算内核,Surface_mesh 类,Polyhedron_3类,以及简单可视化。

#include<CGAL/Exact_integer.h>
#include<CGAL/Homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>

typedef CGAL::Homogeneous<CGAL::Exact_integer> Kernel;
typedef CGAL::Nef_polyhedron_3<Kernel> NefP;

void test1() {
	//空集
	NefP N0(NefP::EMPTY);
	//全空间
	NefP N1(NefP::COMPLETE);

	//complement() 求补集
	assert(N0==N1.complement());
	assert(N0!=N1);
}

显然空集的补集等于全集。

5.1 构造和比较

#include<CGAL/Exact_integer.h>
#include<CGAL/Extended_homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<cassert>

typedef CGAL::Extended_homogeneous<CGAL::Exact_integer> Kernel;
typedef CGAL::Nef_polyhedron_3<Kernel> NefP;
typedef NefP::Plane_3 Plane_3;

void test2(){
	NefP N0;										//默认空集构造
	NefP N1(NefP::EMPTY);							//空集构造
	NefP N2(NefP::COMPLETE);						//全集构造
	NefP N3(Plane_3(1, 2, 5, -1));					//指定平面构造,默认半空间包含平面
	NefP N4(Plane_3(1, 2, 5, -1), NefP::INCLUDED);	//指定平面构造,包含平面
	NefP N5(Plane_3(1, 2, 5, -1), NefP::EXCLUDED);	//指定平面构造,不包含平面

	assert(N0 == N1);								//空集相等
	assert(N3 == N4);								//包含平面和默认半空间相等
	assert(N0 != N2);								//空集和全集不相等
	assert(N3 != N5);								//包含平面和不包含平面不相等

	assert(N4 >= N5);
	assert(N5 <= N4);
	assert(N4 > N5);
	assert(N5 < N4);

	N5 = N5.closure();								//闭包操作后包含平面
	assert(N4 >= N5);								
	assert(N4 <= N5);
}

值得注意的是,对于

Nef_polyhedron Nef(Plane_3(A,B,C,D));

Nef所代表的半空间为:Ax+By+Cz+D<=0 , 例子中已经展示了默认是带INCLUDE包含平面。

为什么是<=0,官方文档如下:

创建一个 Nef 多面体,该多面体包含位于p负侧的半空间,若b==INCLUDED则包含p,若b==EXCLUDED则排除p

5.2 点集运算

为了方便理解,下图展示了Nef布尔运算的符号表示

#include<CGAL/Exact_integer.h>
#include<CGAL/Extended_homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<cassert>

typedef CGAL::Extended_homogeneous<CGAL::Exact_integer> Kernel;
typedef CGAL::Nef_polyhedron_3<Kernel> NefP;
typedef NefP::Plane_3 Plane_3;

void test3() {
	//半空间的判断,对于平面Ax+By+Cz+D=0;
	//有NefP N(Plane_3(A, B, C, D));
	//则N表示半空间Ax+By+Cz+D>=0,包含平面
	NefP N1(Plane_3(1, 0, 0, -1));				//x=1		半空间X<=1
	NefP N2(Plane_3(-1, 0, 0, -1));				//x=-1		半空间X>=-1
	NefP N3(Plane_3(0, 1, 0, -1));				//y=1		半空间Y<=1
	NefP N4(Plane_3(0, -1, 0, -1));				//y=-1		半空间Y>=-1
	NefP N5(Plane_3(0, 0, 1, -1));				//z=1		半空间Z<=1
	NefP N6(Plane_3(0, 0, -1, -1));				//z=-1		半空间Z>=-1

	NefP I1(!N1 + !N2);							//x<-1 u x>1
	NefP I2(N3 - !N4);							//-1<=y<=1
	NefP I3(N5 ^ N6);							//Z<-1 u Z>1
	NefP Cube1(I2*!I1);							//y=[-1,1] ,x=[-1,1]
	Cube1 *= !I3;//Cube1=Cube1*!I3				//y=[-1,1] ,x=[-1,1],z=[-1,1]
	NefP Cube2 = N1 * N2 * N3 * N4 * N5 * N6;	//y=[-1,1] ,x=[-1,1],z=[-1,1]

	assert(Cube1 == Cube2);
	assert(Cube1 == Cube1.closure());			//cube1本身就是闭集
	//
	assert(Cube1 == Cube1.regularization());	//正则化,闭集的正则化是它自己
	//
	assert((N1 - N1.boundary()) == N1.interior());//闭集减去边界是内部
	assert(I1.closure() == I1.complement().interior().complement());	//闭集的闭包是它的补集的内部的补集
	assert(I1.regularization() == I1.interior().closure());				//正则化是内部的闭包
}

5.3 仿射变换

#include<CGAL/Exact_integer.h>
#include<CGAL/Extended_homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/IO/Nef_polyhedron_iostream_3.h>
#include<cassert>

typedef CGAL::Extended_homogeneous<CGAL::Exact_integer> Kernel;
typedef CGAL::Nef_polyhedron_3<Kernel> NefP;
typedef NefP::Plane_3 Plane_3;
typedef NefP::Vector_3 Vector_3;
typedef NefP::Aff_transformation_3 Aff_transformation_3;


void test4() {
	NefP N1(Plane_3(0,1,0,0));			//y<=0
	//平移变换:x'=x+5,y'=y+7,z'=z+9
	Aff_transformation_3 translate(CGAL::TRANSLATION, Vector_3(5,7,9));		//y+7<=0
	
	//矩阵变换,对任意点 (x,y,z),变换后为 (x,−z,y);		(0,1,0,7)->(0,0,1,7) ->z-7<=0
	Aff_transformation_3 rotX90(
		1,0,0,
		0,0,-1,
		0, 1, 0,
		1);		//最后一个1为齐次因子										//z-7<=0
	//缩放,缩放倍数为3/2
	Aff_transformation_3 scale(CGAL::SCALING, 3, 2);					//2z-21<=0

	N1.transform(translate);
	assert(N1 == NefP(Plane_3(0, 1, 0, -7)));
	N1.transform(rotX90);
	assert(N1 == NefP(Plane_3(0, 0, 1, -7)));
	N1.transform(scale);
	assert(N1 == NefP(Plane_3(0, 0, 2, -21)));
}


拓展:

Aff_transformation_3是CGAL代数内核中的一个类,主要提供仿射变换,支持:

常量名称中文释义
CGAL::TRANSLATION平移变换
CGAL::ROTATION旋转变换
CGAL::SCALING缩放变换
CGAL::REFLECTION反射变换(镜像)
CGAL::IDENTITY恒等变换

此外还支持矩阵变换。

5.4 Polyhedron_3 转 Nef_Polyhedron_3

#include<CGAL/Exact_integer.h>
#include<CGAL/Homogeneous.h>
#include<CGAL/Polyhedron_3.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/IO/Nef_polyhedron_iostream_3.h>
#include<cassert>
#include<iostream>
using namespace std;

typedef CGAL::Homogeneous<CGAL::Exact_integer> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef CGAL::Nef_polyhedron_3<Kernel> Nef_Polyhedron;
typedef Kernel::Vector_3 Vector_3;
typedef Kernel::Aff_transformation_3 Aff_transformation_3;

void test5() {
	Polyhedron P;
	ifstream input_file("D:/cube.off");
	if (!input_file.is_open()) {
		cout << "error opening file" << endl;
	}

	input_file >> P;
	input_file.close();
	if (P.empty()) {
		cout << "error reading file" << endl;
	}

	if (P.is_closed()) {
		Nef_Polyhedron N1(P);
		Nef_Polyhedron N2(N1);
		Aff_transformation_3 aff(CGAL::TRANSLATION,Vector_3(2,2,3,1));
		N2.transform(aff);
		N1 += N2;
		if (N1.is_simple()) {
			ofstream output_file("D:/union.off"); 
			if (!output_file.is_open()) {
				cout << "error opening output file" << endl;
			}

			N1.convert_to_Polyhedron(P);

			output_file << P;
			output_file.close();

			cout << "成功写入"<<endl;
		}
		else
			cerr << "N1不是2流行" << endl;

	}
	else {
		std::cout << "input polyhedron is not closed!" << std::endl;
	}
}

这里的cube.off文件可以在CGAL的example文档下找到:路径为examples\data\meshs\cube.off。

当然你也可以创建一个记事本,修改名称为cube.off,粘贴如下内容:

OFF
8 12 0
-1 -1 -1
-1 1 -1
1 1 -1
1 -1 -1
-1 -1 1
-1 1 1
1 1 1
1 -1 1
3 0 1 2
3 0 2 3
3 0 3 4
3 0 4 1
3 1 4 5
3 1 5 2
3 2 5 6
3 2 6 3
3 3 6 7
3 3 7 4
3 4 7 5
3 5 7 6

这个例子主要做的事为:将cube.off文件表示的立方体,从Polyhedron转换为Nef_polyhedron格式的N1,在通过拷贝构造N2,对N2做平移变换,之后布尔求和得到新的N1,最后再将N1输出为Polyhedron格式。

我们用meshlab打开这个cube.off ,和union.off文件

如下图

左图为cube.off,右图为union.off,

值得注意的是:

Aff_transformation_3 aff(CGAL::TRANSLATION,Vector_3(2,2,3,1));

中的平移参数最后一个1为齐次因子,省略也行,另外如果参数太小,平移变换的长度小于边长,这两个cube就会重叠,N1就不再是2流型。

5.5 Nef_polyhedron转换为Polyhedron

这个例子展示了Nef_polyhedron转换为Polyhedron的过程

#include<CGAL/Exact_predicates_exact_constructions_kernel.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/Polyhedron_3.h>
#include<CGAL/Surface_mesh.h>
#include<CGAL/boost/graph/convert_nef_polyhedron_to_polygon_mesh.h>

#include <CGAL/draw_polyhedron.h>
#include <CGAL/draw_surface_mesh.h>

#include<iostream>
#include<fstream>
#include<sstream>

typedef CGAL::Exact_predicates_exact_constructions_kernel Exact_kernel;
typedef CGAL::Polyhedron_3<Exact_kernel> Polyhedron;
typedef CGAL::Surface_mesh<Exact_kernel::Point_3> Surface_mesh;
typedef CGAL::Nef_polyhedron_3<Exact_kernel> Nef_polyhedron;

void fill_cube_1(Polyhedron& poly)
{
	std::string input =
		"OFF\n\
		8 12 0\n\
		-1 -1 -1\n\
		-1 1 -1\n\
		1 1 -1\n\
		1 -1 -1\n\
		-1 -1 1\n\
		-1 1 1\n\
		1 1 1\n\
		1 -1 1\n\
		3  0 1 3\n\
		3  3 1 2\n\
		3  0 4 1\n\
		3  1 4 5\n\
		3  3 2 7\n\
		3  7 2 6\n\
		3  4 0 3\n\
		3  7 4 3\n\
		3  6 4 7\n\
		3  6 5 4\n\
		3  1 5 6\n\
		3  2 1 6";

	std::stringstream ss;
	ss << input;
	ss >> poly;
}

void fill_cube_2(Polyhedron& poly)
{
	std::string input =
		"OFF\n\
		8 12 0\n\
		-0.5 -0.5 -0.5\n\
		-0.5 0.5 -0.5\n\
		0.5 0.5 -0.5\n\
		0.5 -0.5 -0.5\n\
		-0.5 -0.5 0.5\n\
		-0.5 0.5 0.5\n\
		0.5 0.5 0.5\n\
		0.5 -0.5 0.5\n\
		3  0 1 3\n\
		3  3 1 2\n\
		3  0 4 1\n\
		3  1 4 5\n\
		3  3 2 7\n\
		3  7 2 6\n\
		3  4 0 3\n\
		3  7 4 3\n\
		3  6 4 7\n\
		3  6 5 4\n\
		3  1 5 6\n\
		3  2 1 6";

	std::stringstream ss;
	ss << input;
	ss >> poly;
}

void test6() {
	Polyhedron c1, c2;
	fill_cube_1(c1);
	fill_cube_2(c2);

	Nef_polyhedron n1(c1), n2(c2);
	Nef_polyhedron n3 = n1-n2;

	Surface_mesh output;
	CGAL::convert_nef_polyhedron_to_polygon_mesh(n3,output);
	std::ofstream out_file("D:/output.off");
	out_file << output;
	out_file.close();
}

这个例子展示了Nef_polyhedron转换为Polyhedron的过程,c1和c2都是立方体只是边长不同,通过布尔运算从n1中剔除n2,同样用Mehslab展示output.off文件:

可以看到在大立方体中心挖去了一个小立方体。

5.6 Extended Kernel的使用

#include<CGAL/Exact_integer.h>
//Extended_homogeneous.h支持无限点,用于表示半空间和无限平面
#include<CGAL/Extended_homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/IO/Nef_polyhedron_iostream_3.h>
#include<CGAL/Polyhedron_3.h>
#include<fstream>

typedef CGAL::Exact_integer NT;
typedef CGAL::Polyhedron_3<CGAL::Extended_homogeneous<NT>> Polyhedron;

//vc++写法,等价于typedef CGAL::Extended_homogeneous<NT> Kernel;
struct Kernel :public CGAL::Extended_homogeneous<NT> {};

typedef CGAL::Nef_polyhedron_3<Kernel> Nef_polyhedron;
typedef Nef_polyhedron::Point_3 Point_3;
//有理数类型
typedef Nef_polyhedron::RT RT;		
typedef Nef_polyhedron::Plane_3 Plane_3;
typedef Nef_polyhedron::Vertex_const_iterator Vertex_const_iterator;

void test7() {

	Polyhedron P;
	std::ifstream input_file("D:/cube.off");
	if (!input_file.is_open()) {
		std::cout << "error opening file" << std::endl;
		return;
	}
	input_file >> P;
	input_file.close();
	Nef_polyhedron N(P);

	Vertex_const_iterator v;
	for (v = N.vertices_begin(); v != N.vertices_end();++v) {
		//获取当前顶点坐标
		Point_3 p(v->point());
		//hx()、hy()、hz()分别是点p的x、y、z坐标的齐次坐标分量
		if (p.hx().degree()>0||p.hy().degree()>0||p.hz().degree()>0) {
			std::cout << "extended vertex at" << p << std::endl;
		}
		else {
			std::cout << "standard vertex at" << p << std::endl;
		}

		//坐标原点,RT(0,1)==0/1
		if (p==Point_3(RT(0,1),RT(0,1),RT(0,1))) {
			std::cout << "found vertex (right,back,top) of the infimaximal box" << std::endl;
		}

	}
}

Extended_homogeneous.h支持无限点,用于表示半空间和无限平面,相比于Homogeneous范围更大。

5.7 画Nef Polyhedron

#include<CGAL/Exact_predicates_exact_constructions_kernel.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/Polyhedron_3.h>
#include<CGAL/draw_nef_3.h>
#include<CGAL/Surface_mesh/Surface_mesh.h>
#include<CGAL/boost/graph/convert_nef_polyhedron_to_polygon_mesh.h>


#include<fstream>
#include<iostream>
using namespace std;

typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef CGAL::Nef_polyhedron_3<Kernel> Nef_polyhedron;
typedef CGAL::Surface_mesh<Kernel::Point_3> Surface_mesh;



void test8(int argc,char** argv) {
	Polyhedron P1, P2;
	ifstream input_file1((argc>1?argv[1]:CGAL::data_file_path("D:/CGAL_examples/data/meshes/cross_quad.off")));
	input_file1 >> P1;
	input_file1.close();
	ifstream input_file2((argc > 2 ? argv[2] : CGAL::data_file_path("D:/CGAL_examples/data/meshes/beam.off")));
	input_file2 >> P2;
	input_file2.close();

	Nef_polyhedron N1(P1);
	Nef_polyhedron N2(P2);
	Nef_polyhedron sub = N1 - N2;
	Surface_mesh mesh;
	CGAL::convert_nef_polyhedron_to_polygon_mesh(sub,mesh);
	ofstream output_file("D:/cross.off");
	output_file << mesh;
	output_file.close();
	
	//CGAL::draw(sub);

}

这个例子主要解释如何可视化Nef, 依旧是CGAL::draw,不过需要有QT依赖,展示

6 IO操作

下面两个示例是CGAL官方给的IO操作的方法

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Nef_polyhedron_3.h>
#include <CGAL/IO/Nef_polyhedron_iostream_3.h>
 
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel>  Polyhedron;
typedef CGAL::Nef_polyhedron_3<Kernel>  Nef_polyhedron;
 
int main() {
 
  Polyhedron P;
  std::cin >> P;
  Nef_polyhedron N(P);
 
  std::cout << "Exact_predicates_exact_constructions_kernel + SNC_indexed_items"
            << std::endl
            << "  allows efficient handling of input "
                 "using floating point coordinates"
            << std::endl;
 
  if(N.is_simple()) {
    N.convert_to_polyhedron(P);
    std::cout << P;
  }
  else {
    std::cout << N;
  }
}
#include <CGAL/Exact_integer.h>
#include <CGAL/Homogeneous.h>
#include <CGAL/Extended_homogeneous.h>
#include <CGAL/Nef_polyhedron_3.h>
#include <CGAL/IO/Nef_polyhedron_iostream_3.h>
#include <fstream>
 
typedef CGAL::Exact_integer  NT;
typedef CGAL::Homogeneous<NT>  SK;
typedef CGAL::Extended_homogeneous<NT>  EK;
typedef CGAL::Nef_polyhedron_3<SK>  Nef_polyhedron_S;
typedef CGAL::Nef_polyhedron_3<EK>  Nef_polyhedron_E;
 
int main() {
  Nef_polyhedron_E E;
  Nef_polyhedron_S S;
 
  std::cin >> E;
 
  if(E.is_bounded()) {
    std::ofstream out("temp.nef3");
    out << E;
    std::ifstream in("temp.nef3");
    in >> S;
  }
}

7 更多示例程序

7.1 探索球面映射

通过get_sphere_map()函数遍历球面映射,该函数会将指定顶点的球面映射以Nef_polyhedron_S2类型返回。

#include<CGAL/Exact_integer.h>
#include<CGAL/Homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/IO/Nef_polyhedron_iostream_3.h>
#include<CGAL/Polyhedron_3.h>
#include<iostream>

typedef CGAL::Exact_integer RT;
typedef CGAL::Homogeneous<RT> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef CGAL::Nef_polyhedron_3<Kernel> Nef_polyhedron;

typedef Nef_polyhedron::Vertex_const_iterator Vertex_const_iterator;
typedef Nef_polyhedron::Nef_polyhedron_S2 S2;
typedef S2::SVertex_const_handle SVertex_const_handle;
typedef S2::SHalfedge_const_handle SHalfedge_const_handle;
typedef S2::SHalfloop_const_handle Shalfloop_const_handle;

typedef S2::SFace_const_iterator SFace_const_iterator;
typedef S2::SFace_cycle_const_iterator SFace_cycle_const_iterator;


void test3() {
	//
	Polyhedron P;
	std::ifstream input("D:/cube.off");
	if (!input.is_open()) {
		std::cout << "error" << std::endl;
	}
	input >> P;
	input.close();

	Nef_polyhedron N(P);
	//获取N的起始顶点的表面球
	S2 S(N.get_sphere_map(N.vertices_begin()));

	int i = 0;
	//遍历球面映射的每一个曲面
	for (SFace_const_iterator sf = S.sfaces_begin();sf!=S.sfaces_end();++sf) {
		//遍历每个曲面上的元素
		std::cout << "Sface on sphere index:" << i++ << "		start with an element:" << std::endl;
		for (SFace_cycle_const_iterator it = sf->sface_cycles_begin(); it != sf->sface_cycles_end();++it) {
			if (it.is_svertex()) {
				SVertex_const_handle vh(it);
				std::cout << "vertex with location" << vh->point() << std::endl;
			}
			else if (it.is_shalfedge()) {
				SHalfedge_const_handle hh(it);
				std::cout << "Halfedge with source" << hh->source()->point() << "	to   " << hh->target()->point() << std::endl; 
			}
			else if (it.is_shalfloop()) {
				Shalfloop_const_handle lh(it);
				std::cout << "loop int plane" << lh->circle() << std::endl;
			}
		}
	}
}


7.2 探索壳结构

        Nef 多面体的 Shell(外壳)依附于某个 Volume(体积块)的连通表面。每个半面(halffacet)、球面面(sface)、球面半边(shalfedge)都只属于一个 Shell。

Nef多面体的壳是与某个体积相关联的曲面连通部分。下图阐释了壳的概念,展示了一个包含两个体积与三个壳的Nef多面体。

图里是一个「悬浮正方形片 + 带天线的立方体」的 Nef 多面体:

  • Volume 1:外部空间(整个世界除了立方体内部)
  • Volume 2:立方体的实心内部

对应的 3 个 Shell:

  1. Shell 1:左边悬浮正方形片的完整表面
  2. Shell 2:立方体的外表面,包括天线外壁
  3. Shell 3:立方体的内表面,内壁,把立方体内部和外部隔开

每个shell的构成

shell1

  • 2 个 halffacet(正反两个半面)
  • 8 个 shalfedge(4 条边,每条拆成 2 个方向相反的半边)
  • 4 个顶点

shell2

  • 顶点:立方体 8 个顶点 + 天线顶端端点
  • halffacet:所有朝外的半面
  • shalfedge:所有外表面的半边

shell3

  • 顶点:立方体 8 个顶点 + 天线与立方体连接的端点
  • halffacet:所有朝内的半面
  • shalfedge:和 Shell 2 是同一批边(只是方向朝内)

如下图,我们放大看天线根部,分析球面图上的元素归属:

  • 球面图上有 3 个元素:
    1. 两个 shalfloop(大圆半环):分别对应和小球相交的两个半面
    2. 一个 svertex:天线穿过小球的点
  • 上方 shalfloop:在朝外的半面上 → 属于 Shell 2(外表面)
  • 下方 shalfloop:在朝内的半面上 → 属于 Shell 3(内表面)
  • svertex:和外表面关联 → 属于 Shell 2

这个球就是天线根部顶点的表面球映射,对应的数据结构是Nef_polyhedron_S2,参见

CGAL 6.1.1 - 2D Boolean Operations on Nef Polygons Embedded on the Sphere

下面展示的是读取一个 3D 模型,遍历它的所有体积和外壳,找到每个外壳上坐标最小的顶点并输出。

#include<CGAL/Exact_integer.h>
#include<CGAL/Homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/IO/Nef_polyhedron_iostream_3.h>
#include<CGAL/Polyhedron_3.h>
#include<iostream>
using namespace std;

typedef CGAL::Homogeneous<CGAL::Exact_integer> Kernel;
typedef CGAL::Nef_polyhedron_3<Kernel> Nef_polyhedron;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Nef_polyhedron::Vertex_const_handle Vertex_const_handle;
typedef Nef_polyhedron::Halfedge_const_handle Halfedge_const_handle;
typedef Nef_polyhedron::Halffacet_const_handle Halffacet_const_handle;
typedef Nef_polyhedron::SHalfedge_const_handle SHalfedge_const_handle;
typedef Nef_polyhedron::SHalfloop_const_handle SHalfloop_const_handle;
typedef Nef_polyhedron::SFace_const_handle SFace_const_handle; 
typedef Nef_polyhedron::Volume_const_iterator Volume_const_iterator;
typedef Nef_polyhedron::Shell_entry_const_iterator Shell_entry_const_iterator;
typedef Kernel::Point_3 Point_3;

class Shell_explorer {
	bool first;						//是不是第一个点
	Vertex_const_handle v_min;		//保存最小的点
public:
	Shell_explorer():first(true){}
	//如果是第一个点或者新点更小,更新最小的点
	void visit(Vertex_const_handle v){
		if (this->first||CGAL::lexicographically_xyz_smaller(v->point(),v_min->point())) {
			v_min = v;
			first = false;			//不在是第一个		
		}
	}

	void visit(Halfedge_const_handle){}
	void visit(Halffacet_const_handle){}
	void visit(SHalfedge_const_handle){}
	void visit(SHalfloop_const_handle){}
	void visit(SFace_const_handle){}

	Vertex_const_handle& minimal_vertex() { return this->v_min; }
	void reset_minimal_vertex() { first = true; }
};

void test4() {
	Polyhedron P;
	ifstream input("D:/cube.off");	
	input >> P;				//读取Polyhedron_3模型
	Nef_polyhedron N(P);	//转换为Nef_polyhedron

	int ic = 0;
	Volume_const_iterator c;
	Shell_explorer SE;

	//遍历多面体的所有体元
	CGAL_forall_volumes(c, N) {
		cout << "Volume" << ic++ << endl;
		int is = 0;
		Shell_entry_const_iterator it;

		//遍历这个体元的所有外壳
		CGAL_forall_shells_of(it, c) {
			//先假设第一个点为最小点
			SE.reset_minimal_vertex();

			//CGAL 自动把这个外壳上的 点、边、面 全部遍历一遍,每遇到一个元素,就调用 SE.visit(..)
			// 因为我们只实现了顶点的 visit,所以:只有顶点会被处理,自动找到最小顶点
			N.visit_shell_objects(SFace_const_handle(it),SE);

			Point_3 p(SE.minimal_vertex()->point());
			cout << "minimal vertex of shell" << is++ << "is at" << p << endl;
		}
	}	
}

函数:

visit_shell_objects(SFace_const_handle sf, Visitor& V)

作用:从一个球面面(SFacesf 开始,遍历整个 Shell,并将 Shell 内的所有元素依次交给访问器 V 处理。

参数要求:第二个参数 V 必须是一个类,且提供以下 visit 方法(可以是空实现):

  • visit(Vertex_const_handle):处理 3D 顶点
  • visit(Halfedge_const_handle):处理半边(注意:HalfedgeSVertex 是同一类型)
  • visit(Halffacet_const_handle):处理半面
  • visit(SHalfedge_const_handle):处理球面半边
  • visit(SHalfloop_const_handle):处理球面半环
  • visit(SFace_const_handle):处理球面面

程序的整个步骤

  • 对每个 Shell,调用 reset_minimal_vertex() 初始化访问器。
  • 调用 visit_shell_objects(sf, SE),CGAL 自动遍历 Shell 内所有元素。
  • 每当遇到顶点时,SE.visit(Vertex_const_handle) 会被触发,更新最小顶点。
  • 遍历结束后,通过 SE.minimal_vertex() 获取该 Shell 的最小顶点并输出。
  • 重复上述步骤,处理所有 Shell
7.3 点定位

这个示例主要展示了点定位的方法,传给locate()一个具体的点,可以拿到这个点所在结构的抽象对象,适用于判断点是否在内部,碰撞检测等。

#include<CGAL/Exact_integer.h>
#include<CGAL/Homogeneous.h>
#include<CGAL/Nef_polyhedron_3.h>
#include<CGAL/IO/Nef_polyhedron_iostream_3.h>
#include<CGAL/Polyhedron_3.h>
#include<iostream>
using namespace std;


typedef CGAL::Homogeneous<CGAL::Exact_integer> Kernel;
typedef CGAL::Nef_polyhedron_3<Kernel> Nef_polyhedron;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Kernel::Point_3 Point_3;

typedef Nef_polyhedron::Vertex_const_handle Vertex_const_handle;
typedef Nef_polyhedron::Halfedge_const_handle Halfedge_const_handle;
typedef Nef_polyhedron::Halffacet_const_handle Halffacet_const_handle;
typedef Nef_polyhedron::Volume_const_handle Volume_const_handle;
typedef Nef_polyhedron::Object_handle Object_handle;

void test5() {
	Polyhedron P;
	ifstream input("D:/cube.off");
	input >> P;
	Nef_polyhedron N(P);
	
	Vertex_const_handle v;
	Halfedge_const_handle e;
	Halfedge_const_handle f;
	Volume_const_handle c;

	Object_handle o = N.locate(Point_3(0,0,0));
	if (CGAL::assign(v,o)) {
		cout << "Locating vertex" << endl;
	}
	else if(CGAL::assign(e,o)){
		cout << "Locating edge" << endl;
	}
	else if(CGAL::assign(f,o)){
		cout << "Locating facet" << endl;
	}
	else if(CGAL::assign(c,o)){
		cout << "Locating volume" << endl;
	}

}

int main() {
	test5();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值