CGAL笔记之单元格复合体和多面体篇—三维多面体曲面
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
CGAL笔记之单元格复合体和多面体篇—三维多面体曲面
- 1 介绍
- 2 定义
- 3 示例程序
- 3.1 第一个使用默认值的例子
- 3.2 顶点中的几何示例
- 3.3 仿射变换示例
- 3.4 示例计算平面方程
- 3.5 使用向量而不是列表表示的示例
- 3.6 循环器写入对象文件格式示例(关闭)
- 3.7 使用 Euler 运算符构建立方体的示例
- 3.8 画一个多面体
- 4 文件输入输出
- 5 扩展顶点、半边和小平面
- 6个 高级示例程序
- 6.1 创建细分曲面的示例
- 6.2 使用增量构建器和修改器机制的示例
1 介绍
三维多面体曲面由顶点、边、面以及它们之间的重合关系组成。下面的组织是半边数据结构,它将可表示表面的类别限制为可定向的 2-流形 - 有边界和无边界。如果曲面是封闭的,称它为多面体*,*例如以下模型:
多面体表面被实现为一个容器类,它管理顶点、半边、小平面及其关联,并保持它们的组合完整性。它基于halfedge数据结构的高度灵活的设计。然而,可以在不知道底层设计的情况下使用和理解多面体表面。本章中的一些示例也逐渐介绍了这种灵活性的首次应用。
2 定义
三维多面体表面由顶点、边、面和它们的重合关系组成。每条边由两个方向相反的半边表示。用半边存储的事件如下图所示:Polyhedron_3<PolyhedronTraits_3>
、V
、E
、F
顶点代表空间中的点。边是两个端点之间的直线段。刻面是没有孔的平面多边形。小平面由沿其边界的半边的圆形序列定义。多面体表面本身可以有孔(至少有两个面围绕它,因为一个面不能有孔)。沿孔边界的半边称为边界半边,没有入射面。如果一条边的其中一条半边是边界半边,则该边是边界边。一个曲面是封闭的如果它不包含边界半边。封闭曲面是三维多面体的边界表示。约定是从多面体的外部看,半边沿逆时针方向围绕小平面。这意味着半边沿顺时针方向围绕顶点。由半边方向定义的小平面实体面的概念扩展到具有边界边的多面体表面,尽管它们不定义封闭对象。如果小平面考虑法向量,则法向量指向外(遵循右手法则)。
这个定义的一个含义是多面体表面总是一个可定向和有边界的定向 2-流形,即多面体表面上每个点的邻域要么同胚于一个圆盘,要么同胚于一个半圆盘,除了顶点许多具有边界的孔和表面可以连接。另一个含义是避免自相交的最小可表示表面是三角形(对于具有边界边的多面体表面)或四面体(对于多面体)。可定向 2-流形的边界表示在欧拉运算下是封闭的。它们通过在表面上创建或关闭孔的操作进行扩展。
除了关联关系之外的其他交集是不允许的。但是,这不会在操作中自动验证,因为自相交不容易有效地检查。只维护多面体表面的组合完整性(使用欧拉运算),不考虑点的坐标或任何其他几何信息。Polyhedron_3<PolyhedronTraits_3>
Polyhedron_3<PolyhedronTraits_3>
可以表示多面体表面以及多面体。界面的设计方式很容易忽略边界边缘并且只使用多面体。
3 示例程序
多面体曲面基于半边数据结构的高度灵活设计。这种灵活性的示例可以在扩展顶点、半边和面部分以及半边数据结构一章的示例部分中找到。此设计不是理解以下示例的先决条件。
3.1 第一个使用默认值的例子
第一个示例使用内核作为特征类来实例化多面体。它创建一个四面体并将对其半边之一的引用存储在Halfedge_handle
. 句柄,也称为普通迭代器,用于保留对半边、顶点或面的引用以供将来使用。还有一种Halfedge_iterator
用于枚举半边的类型。这种迭代器类型可以用在任何需要句柄的地方。Halfedge_const_handle
还提供了相应的常量Halfedge_const_iterator
多面体和带有前缀的句柄和迭代器Vertex_
。Facet_
该示例继续测试半边是否实际指代四面体。此测试检查由半边引用的连接组件h
,而不是整个多面体表面。此示例仅适用于多面体曲面的组合级别。下一个示例添加几何。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Halfedge_handle Halfedge_handle;
int main() {Polyhedron P;Halfedge_handle h = P.make_tetrahedron();if ( P.is_tetrahedron(h))return 0;return 1;
}
3.2 顶点中的几何示例
我们将几何添加到四面体的构造中。四个点作为参数传递给构造。此示例还演示了顶点迭代器的使用以及对顶点中点的访问。注意自然访问符号v->point()
。类似地,存储在顶点、半边和面中的所有信息都可以使用给定句柄或迭代器的成员函数访问。例如,给定一个半边句柄,h
我们可以编写h->next()
一个半边句柄来获取下一个半边的半边句柄,h->opposite()
对于相反的半边,h->vertex()
对于 的尖端处的入射顶点h
,等等。该程序的输出将是
1 0 0
0 1 0
0 0 1
0 0 0
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Vertex_iterator Vertex_iterator;
int main() {Point_3 p( 1.0, 0.0, 0.0);Point_3 q( 0.0, 1.0, 0.0);Point_3 r( 0.0, 0.0, 1.0);Point_3 s( 0.0, 0.0, 0.0);Polyhedron P;P.make_tetrahedron( p, q, r, s);CGAL::IO::set_ascii_mode( std::cout);for ( Vertex_iterator v = P.vertices_begin(); v != P.vertices_end(); ++v)std::cout << v->point() << std::endl;return 0;
}
为方便起见,多面体提供了一个点迭代器。上面的循环通过使用和 ostream 迭代器适配器for
简化为单个语句。std::copy
std::copy( P.points_begin(), P.points_end(),
std::ostream_iterator<Point_3>(std::cout, "\n" ));
3.3 仿射变换示例
仿射变换A
可以充当变换点的函子,并且可以方便地为多面体曲面定义点迭代器。因此,假设我们只需要转换多面体的点坐标P
,std::transform
就可以在一行中完成这项工作。
std::transform ( P.points_begin(), P.points_end(), P.points_begin(), A);
3.4 示例计算平面方程
多面体表面已经规定为每个面存储一个平面方程。但是,它不提供计算平面方程的功能。
此示例计算多面体表面的平面方程。实际的计算是在Plane_equation
仿函数中实现的。根据算法(精确/不精确)和小平面的形状(凸/非凸)不同的方法是有用的。我们在这里假设严格的凸面和精确的算术。在我们的示例中,具有坐标的齐次表示int
就足够了。四面体的四个平面方程是程序的输出。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
#include <algorithm>
struct Plane_equation {template <class Facet>typename Facet::Plane_3 operator()( Facet& f) {typename Facet::Halfedge_handle h = f.halfedge();typedef typename Facet::Plane_3 Plane;return Plane( h->vertex()->point(),h->next()->vertex()->point(),h->next()->next()->vertex()->point());}
};
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point_3;
typedef Kernel::Plane_3 Plane_3;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
int main() {Point_3 p( 1, 0, 0);Point_3 q( 0, 1, 0);Point_3 r( 0, 0, 1);Point_3 s( 0, 0, 0);Polyhedron P;P.make_tetrahedron( p, q, r, s);std::transform( P.facets_begin(), P.facets_end(), P.planes_begin(),Plane_equation());CGAL::IO::set_pretty_mode( std::cout);std::copy( P.planes_begin(), P.planes_end(),std::ostream_iterator<Plane_3>( std::cout, "\n"));return 0;
}
3.5 使用向量而不是列表表示的示例
多面体类模板实际上有四个参数,其中三个具有默认值。在我们上面的示例中明确地为三个参数使用默认值 - 忽略第四个参数,这将是容器类的标准分配器 - 多面体的定义如下所示:
typedef CGAL::Polyhedron_3< Traits,
CGAL::Polyhedron_items_3,
CGAL::HalfedgeDS_default> Polyhedron;
该类Polyhedron_items_3
包含用于顶点、边和面的类型。该类HalfedgeDS_default
定义了使用的半边数据结构,在本例中是基于列表的表示。另一种方法是基于矢量的表示。使用向量为多面体表面的元素提供了随机访问并且空间效率更高,但不能任意删除元素。使用列表允许任意删除,但仅提供双向迭代器并且空间效率较低。以下示例再次创建具有给定点的四面体,但采用基于矢量的表示形式。
如果预留容量不足以创建新项目,基于矢量的表示会自动调整大小。在调整所有句柄、迭代器和循环器的大小时,它们将变得无效。他们在 halfedge 数据结构中的正确更新是昂贵的,因此建议提前保留足够的空间,如评论中的替代构造函数所示。
请注意,触发调整大小操作的是多面体而非底层半边数据结构,因为调整大小操作需要满足一些先决条件,例如有效关联,只有多面体才能保证。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/HalfedgeDS_vector.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Point_3 Point_3;
typedef CGAL::Polyhedron_3< Kernel,CGAL::Polyhedron_items_3,CGAL::HalfedgeDS_vector> Polyhedron;
int main() {Point_3 p( 1.0, 0.0, 0.0);Point_3 q( 0.0, 1.0, 0.0);Point_3 r( 0.0, 0.0, 1.0);Point_3 s( 0.0, 0.0, 0.0);Polyhedron P; // alternative constructor: Polyhedron P(4,12,4);P.make_tetrahedron( p, q, r, s);CGAL::IO::set_ascii_mode( std::cout);std::copy( P.points_begin(), P.points_end(),std::ostream_iterator<Point_3>( std::cout, "\n"));return 0;
}
3.6 循环器写入对象文件格式示例(关闭)
std::cout
我们创建一个四面体并使用对象文件格式 (OFF)将其写入。此示例使用 STL 算法 ( std::copy
, std::distance
)、STLstd::ostream_iterator
和 CGAL 循环器。多面体表面为围绕小平面的逆时针圆形半边序列和围绕顶点的顺时针圆形半边序列提供了方便的循环器。
但是,不建议使用该函数在 facet 输出的内部循环中计算顶点索引std::distance
,因为对于非随机访问迭代器,它需要线性时间,这会导致二次运行时间。为了更好的运行时间,顶点索引需要单独存储并在写入面之前计算一次。例如,它可以存储在顶点本身或散列结构中。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
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() {Point_3 p( 0.0, 0.0, 0.0);Point_3 q( 1.0, 0.0, 0.0);Point_3 r( 0.0, 1.0, 0.0);Point_3 s( 0.0, 0.0, 1.0);Polyhedron P;P.make_tetrahedron( p, q, r, s);// Write polyhedron in Object File Format (OFF).CGAL::IO::set_ascii_mode( std::cout);std::cout << "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>( std::cout, "\n"));for ( Facet_iterator i = P.facets_begin(); i != P.facets_end(); ++i) {Halfedge_facet_circulator j = i->facet_begin();// Facets in polyhedral surfaces are at least triangles.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;}return 0;
}
3.7 使用 Euler 运算符构建立方体的示例
欧拉算子是修改多面体表面的自然方式。我们为多面体提供了一组操作:split_facet()
、join_facet()
、split_vertex()
、join_vertex()
、split_loop()
和join_loop()
。我们添加了更多方便的运算符,例如[split_edge()
。 但是,它们可以使用上面的六个运算符来实现。此外,我们提供了更多操作符来处理具有边界边的多面体表面,例如,创建和删除孔。关于欧拉算子的定义和说明图,我们参考了参考手册。
以下示例实现了一个将单位立方体附加到多面体表面的函数。为了跟踪立方体创建过程中的不同步骤,一系列草图可能有助于为程序代码中出现的不同句柄添加标签。下图显示了创建序列中的六个选定步骤。这些步骤在程序代码中也有标注。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
template <class Poly>
typename Poly::Halfedge_handle make_cube_3( Poly& P) {// appends a cube of size [0,1]^3 to the polyhedron P.CGAL_precondition( P.is_valid());typedef typename Poly::Point_3 Point;typedef typename Poly::Halfedge_handle Halfedge_handle;Halfedge_handle h = P.make_tetrahedron( Point( 1, 0, 0),Point( 0, 0, 1),Point( 0, 0, 0),Point( 0, 1, 0));Halfedge_handle g = h->next()->opposite()->next(); // Fig. (a)P.split_edge( h->next());P.split_edge( g->next());P.split_edge( g); // Fig. (b)h->next()->vertex()->point() = Point( 1, 0, 1);g->next()->vertex()->point() = Point( 0, 1, 1);g->opposite()->vertex()->point() = Point( 1, 1, 0); // Fig. (c)Halfedge_handle f = P.split_facet( g->next(),g->next()->next()->next()); // Fig. (d)Halfedge_handle e = P.split_edge( f);e->vertex()->point() = Point( 1, 1, 1); // Fig. (e)P.split_facet( e, f->next()->next()); // Fig. (f)CGAL_postcondition( P.is_valid());return h;
}
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Halfedge_handle Halfedge_handle;
int main() {Polyhedron P;Halfedge_handle h = make_cube_3( P);return (P.is_tetrahedron(h) ? 1 : 0);
}
3.8 画一个多面体
可以通过调用CGAL::draw()
函数来可视化多面体,如下例所示。此函数打开一个显示给定多面体的新窗口。对该函数的调用是阻塞的,也就是说,只要用户关闭窗口,程序就会继续。
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/draw_polyhedron.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
int main(int argc, char* argv[])
{Polyhedron P;std::ifstream in1((argc>1)?argv[1]:CGAL::data_file_path("meshes/cross_quad.off"));in1 >> P;CGAL::draw(P);return EXIT_SUCCESS;
}
4 文件输入输出
文件 I/O 目前只考虑表面的拓扑结构及其点坐标。它忽略可能的平面方程或任何用户添加的属性,例如颜色。
CGAL 中支持输出和输入的默认文件格式是目标文件格式 (OFF),文件扩展名为.off
. 修饰符set_pretty_mode()
可用于在输出中允许(一些)结构化注释。否则,输出将没有注释。写作的默认是没有评论。由于此文件格式是默认格式,因此为它提供了 iostream 运算符。
#include <CGAL/IO/Polyhedron_iostream.h>
template <class PolyhedronTraits_3>
ostream& operator<<( ostream& out,
const CGAL::Polyhedron_3<PolyhedronTraits_3>& P);
template <class PolyhedronTraits_3>
istream& operator>>( istream& in,
CGAL::Polyhedron_3<PolyhedronTraits_3>& P);
支持写入的其他格式包括 OpenInventor ( .iv
)、VRML 1.0 和 2.0 ( .wrl
)和.obj
。这些输出函数作为流运算符提供,现在作用于相应格式的流类型。
#include <CGAL/IO/Polyhedron_inventor_ostream.h>
#include <CGAL/IO/Polyhedron_VRML_1_ostream.h>
#include <CGAL/IO/Polyhedron_VRML_2_ostream.h>
template <class PolyhedronTraits_3>
Inventor_ostream& operator<<( Inventor_ostream& out,
const CGAL::Polyhedron_3<PolyhedronTraits_3>& P);
template <class PolyhedronTraits_3>
VRML_1_ostream& operator<<( VRML_1_ostream& out,
const CGAL::Polyhedron_3<PolyhedronTraits_3>& P);
template <class PolyhedronTraits_3>
VRML_2_ostream& operator<<( VRML_2_ostream& out,
const CGAL::Polyhedron_3<PolyhedronTraits_3>& P);
所有这些文件格式的共同点是它们将表面表示为一组小平面。每个方面都是指向一组顶点的索引列表。顶点表示为坐标三元组。多面体表面的文件 I/OPolyhedron_3
对这些格式施加了某些限制。它们必须代表一个允许的多面体表面,例如,一个 2 流形且没有孤立的顶点。
5 扩展顶点、半边和小平面
在使用向量而不是列表表示的示例部分中,我们已经了解了如何更改默认列表表示
typedef CGAL::Polyhedron_3< Traits,
CGAL::Polyhedron_items_3,
CGAL::HalfedgeDS_default> Polyhedron;
到底层半边数据结构的基于向量的表示。现在我们想仔细看看第二个模板参数,Polyhedron_items_3
它指定使用哪种顶点、半边和面。的实现Polyhedron_items_3
看起来有点涉及嵌套包装类模板。但是忽略这个技术细节,剩下的是三个局部类型定义,它们定义了多面体表面的、 和Vertex
。请注意,我们在这里使用而不是 facet。面是用于半边数据结构的术语。只有多面体表面的顶层给出了别名,可以面对面地重命名。Halfedge``Face``Face
class Polyhedron_items_3 {public:
template < class Refs, class Traits>
struct Vertex_wrapper {typedef typename Traits::Point_3 Point;
typedef CGAL::HalfedgeDS_vertex_base<Refs, CGAL::Tag_true, Point> Vertex;
};
template < class Refs, class Traits>
struct Halfedge_wrapper {typedef CGAL::HalfedgeDS_halfedge_base<Refs> Halfedge;
};
template < class Refs, class Traits>
struct Face_wrapper {typedef typename Traits::Plane_3 Plane;
typedef CGAL::HalfedgeDS_face_base<Refs, CGAL::Tag_true, Plane> Face;
};
};
如果我们在参考手册中查找 typedef 中使用的三个类的定义,我们将看到确认默认多面体使用所有支持的关联、顶点类中的点和面类中的平面方程。请注意包装类是如何提供两个模板参数的,Refs
我们稍后会讨论它Traits
,它是多面体表面使用的几何特征类,它在这里为我们提供了点和平面方程的类型。
使用此示例代码,我们可以编写自己的项目类。相反,如果我们只想交换一个类,我们会说明一种更简单的方法。我们使用没有平面方程但添加了颜色属性的更简单的面。为了简化顶点、半边或面类的创建,始终建议从给定的基类之一派生。即使基类不包含任何数据,它也会提供方便的类型定义。因此,我们从基类派生,必要时重复强制构造函数——这不是面的情况,而是顶点的情况——并添加颜色属性。
template <class Refs>
struct My_face : public CGAL::HalfedgeDS_face_base<Refs> {CGAL::IO::Color color;
};
新的项目类派生自旧的项目类,包含面部 typedef 的包装器被覆盖。请注意,包装器的名称及其模板参数是固定的。即使在本例中没有使用模板参数,它们也不能更改。
struct My_items : public CGAL::Polyhedron_items_3 {template <class Refs, class Traits>
struct Face_wrapper {typedef My_face<Refs> Face;
};
};
当我们将新的项目类与多面体表面一起使用时,我们的新面类将用于半边数据结构,并且颜色属性在类型中可用Polyhedron_3::Facet
。但是,Polyhedron_3::Facet
与我们的本地人脸类型定义不同My_face
,但它是从中派生的。因此,除了构造函数之外,我们放入本地面类型的所有内容都可以在该Polyhedron_::Facet
类型中使用。
将所有部分放在一起,完整的示例程序说明了颜色属性在定义后是多么容易访问。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/IO/Color.h>
#include <CGAL/Polyhedron_3.h>
// A face type with a color member variable.
template <class Refs>
struct My_face : public CGAL::HalfedgeDS_face_base<Refs> {CGAL::IO::Color color;
};
// An items type using my face.
struct My_items : public CGAL::Polyhedron_items_3 {template <class Refs, class Traits>struct Face_wrapper {typedef My_face<Refs> Face;};
};
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel, My_items> Polyhedron;
typedef Polyhedron::Halfedge_handle Halfedge_handle;
int main() {Polyhedron P;Halfedge_handle h = P.make_tetrahedron();h->facet()->color = CGAL::IO::red();return 0;
}
我们回到Refs
包装类的第一个模板参数 。该参数为我们提供了局部类型,允许我们在顶点、半边和小平面之间进行进一步的引用,这在当前的设计中还没有准备好。这些本地类型分别是Polyhedron_3::Vertex_handle
, Polyhedron_3::Halfedge_handle
,Polyhedron_3::Facet_handle
和.._const_handle
。我们现在向面类添加一个新的顶点引用,如下所示。可以添加封装和访问功能以进行更彻底的设计。
template <class Refs>
struct My_face : public CGAL::HalfedgeDS_face_base<Refs> {typedef typename Refs::Vertex_handle Vertex_handle;
Vertex_handle vertex_ref;
};
6个 高级示例程序
6.1 创建细分曲面的示例
该程序从标准输入中读取一个多面体曲面,并将经过优化的多面体曲面写入标准输出。输入和输出的对象文件格式为 OFF,文件扩展名为`.off
细化是一个单独的步骤,它是 3 \sqrt{3} 3 -scheme 创建细分曲面的方案。每一步都围绕一个新的中心顶点将一个面细分为三角形,平滑旧顶点的位置,并翻转旧边。该计划是按照这个大纲组织的。在每个部分中,程序都有效地利用了新创建的顶点、边和面已添加到序列末尾的知识。该程序只需要额外的处理内存用于旧顶点的平滑步骤。
上图显示了三个示例对象,每个都细分了四次。左侧序列的初始对象是三个单元立方体的闭合曲面,它们粘在一起。此处显示的示例程序只能处理闭合曲面,但扩展示例examples/Polyhedron/polyhedron_prog_subdiv_with_boundary.cpp
可以处理带边界的曲面。因此,中间序列从其中一个刻面已被移除的同一表面开始。边界细分为一个漂亮的圆圈。第三个序列使用对象表示中的技巧创建锐边。锋利的边缘实际上是一个孔,其顶点坐标将孔挤压关闭以形成边缘。示例目录examples/Polyhedron/
包含此处使用的 OFF 文件。
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <fstream>
#include <cassert>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef Kernel::Vector_3 Vector;
typedef Kernel::Point_3 Point;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::Vertex Vertex;
typedef Polyhedron::Vertex_iterator Vertex_iterator;
typedef Polyhedron::Halfedge_handle Halfedge_handle;
typedef Polyhedron::Edge_iterator Edge_iterator;
typedef Polyhedron::Facet_iterator Facet_iterator;
typedef Polyhedron::Halfedge_around_vertex_const_circulator HV_circulator;
typedef Polyhedron::Halfedge_around_facet_circulator HF_circulator;
void create_center_vertex( Polyhedron& P, Facet_iterator f) {Vector vec( 0.0, 0.0, 0.0);std::size_t order = 0;HF_circulator h = f->facet_begin();do {vec = vec + ( h->vertex()->point() - CGAL::ORIGIN);++ order;} while ( ++h != f->facet_begin());assert( order >= 3); // guaranteed by definition of polyhedronPoint center = CGAL::ORIGIN + (vec / static_cast<double>(order));Halfedge_handle new_center = P.create_center_vertex( f->halfedge());new_center->vertex()->point() = center;
}
struct Smooth_old_vertex {Point operator()( const Vertex& v) const {CGAL_precondition((CGAL::circulator_size( v.vertex_begin()) & 1) == 0);std::size_t degree = CGAL::circulator_size( v.vertex_begin()) / 2;double alpha = ( 4.0 - 2.0 * std::cos( 2.0 * CGAL_PI / degree)) / 9.0;Vector vec = (v.point() - CGAL::ORIGIN) * ( 1.0 - alpha);HV_circulator h = v.vertex_begin();do {vec = vec + ( h->opposite()->vertex()->point() - CGAL::ORIGIN)* alpha / static_cast<double>(degree);++ h;assert( h != v.vertex_begin()); // even degree guaranteed++ h;} while ( h != v.vertex_begin());return (CGAL::ORIGIN + vec);}
};
void flip_edge( Polyhedron& P, Halfedge_handle e) {Halfedge_handle h = e->next();P.join_facet( e);P.split_facet( h, h->next()->next());
}
void subdiv( Polyhedron& P) {if ( P.size_of_facets() == 0)return;// We use that new vertices/halfedges/facets are appended at the end.std::size_t nv = P.size_of_vertices();Vertex_iterator last_v = P.vertices_end();-- last_v; // the last of the old verticesEdge_iterator last_e = P.edges_end();-- last_e; // the last of the old edgesFacet_iterator last_f = P.facets_end();-- last_f; // the last of the old facetsFacet_iterator f = P.facets_begin(); // create new center verticesdo {create_center_vertex( P, f);} while ( f++ != last_f);std::vector<Point> pts; // smooth the old verticespts.reserve( nv); // get intermediate space for the new points++ last_v; // make it the past-the-end position againstd::transform( P.vertices_begin(), last_v, std::back_inserter( pts),Smooth_old_vertex());std::copy( pts.begin(), pts.end(), P.points_begin());Edge_iterator e = P.edges_begin(); // flip the old edges++ last_e; // make it the past-the-end position againwhile ( e != last_e) {Halfedge_handle h = e;++e; // careful, incr. before flip since flip destroys current edgeflip_edge( P, h);};CGAL_postcondition( P.is_valid());
}
int main(int argc, char* argv[]) {Polyhedron P;std::ifstream in1((argc>1)?argv[1]:CGAL::data_file_path("meshes/cube_quad.off"));in1 >> P;P.normalize_border();if ( P.size_of_border_edges() != 0) {std::cerr << "The input object has border edges. Cannot subdivide."<< std::endl;std::exit(1);}subdiv( P);std::cout << std::setprecision(17) << P;return 0;
}
6.2 使用增量构建器和修改器机制的示例
实用程序类Polyhedron_incremental_builder_3
有助于从点列表创建多面体表面,然后是表示为点列表索引的小平面列表。这对于实现常见文件格式的文件阅读器特别有用。它在这里用于创建一个三角形。
修改器机制允许以受控方式访问多面体表面的内部表示,即半边数据结构。修饰符基本上是使用函数对象的回调机制。调用时,函数对象接收内部 halfedge 数据结构作为参数并可以对其进行修改。返回时,多面体可以检查半边数据结构的有效性。这样的修饰符对象必须始终返回一个半边数据结构,该数据结构是一个有效的多面体表面。有效性检查在成员函数的末尾作为昂贵的后置条件实现Polyhedron_3::delegate()
,即默认情况下不调用它,只有在激活昂贵的检查时才会调用。
在这个例子中,Build_triangle
是这样一个函数对象派生自. 多面体的成员函数接受这个函数对象,并通过引用其内部使用的半边数据结构来调用它。因此,这个成员函数可以在半边数据结构中创建三角形。Modifier_base<HalfedgeDS>``Polyhedron_3::delegate()``Modifier_base::operator()()``Build_triangle
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Polyhedron_3.h>
#include <cassert>
// A modifier creating a triangle with the incremental builder.
template <class HDS>
class Build_triangle : public CGAL::Modifier_base<HDS> {public:Build_triangle() {}void operator()( HDS& hds) {// Postcondition: hds is a valid polyhedral surface.CGAL::Polyhedron_incremental_builder_3<HDS> B( hds, true);B.begin_surface( 3, 1, 6);typedef typename HDS::Vertex Vertex;typedef typename Vertex::Point Point;B.add_vertex( Point( 0, 0, 0));B.add_vertex( Point( 1, 0, 0));B.add_vertex( Point( 0, 1, 0));B.begin_facet();B.add_vertex_to_facet( 0);B.add_vertex_to_facet( 1);B.add_vertex_to_facet( 2);B.end_facet();B.end_surface();}
};
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
typedef Polyhedron::HalfedgeDS HalfedgeDS;
int main() {Polyhedron P;Build_triangle<HalfedgeDS> triangle;P.delegate( triangle);assert( P.is_triangle( P.halfedges_begin()));return 0;
}
CGAL笔记之单元格复合体和多面体篇—三维多面体曲面相关推荐
- CGAL笔记之单元格复合体和多面体篇—曲面网格
CGAL笔记之单元格复合体和多面体篇-曲面网格 0.前言 1.用法 1.示例 2.连通性 3.范围和迭代器 3.1 示例 4.循环器 1.示例 5.属性 1.示例 6.边界 7.Surface Mes ...
- CGAL笔记之单元格复合体和多面体篇——多面体凸分解
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 CGAL笔记之单元格复合体和多面体篇--多面体凸分解 1 介绍 2 接口使用 1 介绍 对于非凸多面体的许多应用,有一个有效的解决方案 ...
- CGAL笔记之单元格复合体和多面体篇——多面体的 3D Minkowski 和
CGAL笔记之单元格复合体和多面体篇--多面体的 3D 闵可夫斯基和 介绍 分解法 特性和约束 用法 介绍 如下图,勺子和星星的 Minkowski 和. 分解法 计算非凸多面体闵可夫斯基和的分解方法 ...
- PHPSpreadsheet学习笔记——访问单元格
文章目录 一.实例化对象 二.访问单元格 2.1获取单元格 2.2单元格赋值 2.3excel数据类型 2.3.1在单元格设置公式 2.3.2在单元格设置日期和时间值 2.3.3 设置带前导零的数字 ...
- 王佩丰excel教程笔记(单元格设置)
设置单元格格式对话框 合并后居中 跨越合并可以一次合并多行 改字体 改字体颜色 填充单元格背景色 在设置单元格选项卡下可以给一个单元格绘制线条 例如斜线 Alt+回车 实现一个单元格换行. 框线设置 ...
- vba excel学习笔记-从单元格获取数据,修改数据
从单元格获取数据 获取sheet名字为 "sheetname"的表格中的第一行第五列的值 赋值给变量a a = Sheets("sheetname").Cell ...
- vb 变量赋值为当前选定单元格_第7篇:根据Excel选定行对Word模板填充数据(补充修改)...
题外:由于漏掉了勾选Microsoft Word 16.0 Object Library这个关键步骤(不勾选是无法导出的),故修改后再次发出.本期我是从知乎上的郭大牛老师那里学的,非常建议大家去阅读, ...
- 设置Table单元格颜色(Table篇三)
组件:Y_XIAO_03 视图:V_MAIN 窗口:W_MAIN Application:Y_XIAO_03 创建组件. 创建节点. 参考类型(重要字段:CELL_COLOR TYPE WDUI_TA ...
- list Control实现单元格编辑与插入Combo Box
之前写过一篇(list Control实现单元格编辑)文章,那篇文章不是很完善执行的时候有时会出错,这篇文章经过完善后还加入了Combo Box功能! 这里我就只是晒一下我的代码; 头文件: [c-s ...
最新文章
- 第一部分:基础知识(第一章)屏幕部分续
- 项目经理常扮演的角色
- php代码怎么复制_PHP_PHP网站备份程序代码分享,效果图:PHP代码 复制代码 代码 - phpStudy...
- 【Java进阶】Spring Cloud中Zuul配置与案例实现
- Elasticsearch实战:给博客打造全文检索
- Qt 线程基础(QThread、QtConcurrent等)
- 光阴似箭——绿巨人NVIDIA显卡发展简史回顾
- 【2】puppet笔记 - package、service、user资源
- 英特尔® 硬件加速执行管理器安装指南 — Mac OS X*
- 安装慢_python安装第三方库太慢,很容易失败报错?教你如何提速
- Centos6.3 PHP编译安装JSON模块报错解决
- 大智慧加密指标源码恢复,指标破解工具
- Toplitz矩阵 Hankel矩阵 Hilbert矩阵
- android编程闪退,软件闪退怎么办
- 由 Apache Kylin 组建的 Kyligence 公司获得数百万美元的天使轮投资
- DDSM database、INbreast database、MIAS等乳腺MG数据获取方式
- 微信小程序 画布 组件
- Tik Tok跨境电商与传统跨境电商有什么区别?
- nginx ssl 漏洞 修复
- 深入理解debuginfo