文章目录
- [1 介绍](#1 介绍)
- [2 细分法](#2 细分法)
- [3 一个简单的例子:Catmull-Clark 细分](#3 一个简单的例子:Catmull-Clark 细分)
- [4 Catmull-Clark 细化](#4 Catmull-Clark 细化)
- [5 Refinement Host](#5 Refinement Host)
- [6 Geometry Policy](#6 Geometry Policy)
- [7 四种细分方法](#7 四种细分方法)
- [8 示例:自定义细分方法](#8 示例:自定义细分方法)
- [9 实施历史](#9 实施历史)
原文地址: https://doc.cgal.org/latest/Subdivision_method_3/index.html#Chapter_3D_Surface_Subdivision_Methods
细分方法递归地细化控制网格并生成逼近极限表面的点。 该包由四种流行的细分方法及其细化主机组成。 支持的细分方法包括 Catmull-Clark、Loop、Doo-Sabin 和 √3 细分。 它们各自的细化宿主是 Pqq、Ptq、Dqq 和 √ 3细化。 通过替换细化主机的几何计算,可以轻松扩展这些方法的变体。
1 介绍
细分方法是从任意多边形网格生成平滑表面的简单而强大的方法。 与基于样条的曲面(例如 NURBS)或其他基于数值的建模技术不同,细分方法的用户不需要细分方法的数学知识。 几何形状的自然直觉足以控制细分方法。
Subdivision_method_3 适用于 Polyhedron_3 和 Surface_mesh 类,因为它们是 MutableFaceGraph 概念的模型,其目标是易于使用和扩展。 Subdivision_method_3 不是一个类,而是一个命名空间,其中包含四种流行的细分方法及其细化函数。 其中包括 Catmull-Clark、Loop、Doo-Sabin 和 √ 3细分。 通过替换细化主机的几何计算,可以轻松扩展这些方法的变体。
2 细分法
在本章中,我们将解释细分方法的一些基础知识。 我们仅关注有助于您理解包设计的主题。 有关细分方法的详细信息可以在[6]中找到。 本节介绍的一些术语将在后面的章节中再次使用。 如果您只对使用特定的细分方法感兴趣, 一个快速示例:Catmull-Clark 细分提供了有关使用 Catmull-Clark 细分的快速教程。
细分方法递归地细化粗网格并生成更接近光滑表面的近似值。 粗网格可以具有任意形状,但必须是 二维流形。 在 二维 流形中,每个内部点都有一个与 2D 圆盘同胚的邻域。 非流形的细分方法已经开发出来,但在 Subdivision_method_3 中没有考虑。 该章节预告片展示了在 CAD 模型上进行 Catmull-Clark 细分的步骤。 通过四等分模式反复细化粗网格,并生成新的点以近似平滑的表面。
实践中使用了许多细化模式。 Subdivision_method_3 支持四种最流行的模式,每种模式都被 Catmull-Clark[1]、Loop[4]、Doo-Sabin[2] 和 √3 subdivision[3] 使用(下图中从左到右) 。 我们通过它们的拓扑特征而不是相关的细分方法来命名这些模式。 PQQ 表示原始四边形四边形。 PTQ 表示原三角形四等分。 DQQ 表示对偶四边形四等分。 √3表示三角剖分向细分面收敛的速度。
该图演示了 5 价顶点/面的 1-disk上的这四种细化模式。 细化的网格显示在源网格下方。 细化网格上的点是通过对源网格上的相邻点进行平均而生成的。 称为模板的图确定其点对细化点的位置有贡献的源邻域。 细化模式通常定义多个模板。 例如,PQQ 细化有一个顶点节点模板,它定义了输入顶点的 1 环; 边缘节点模板,定义输入边缘的 1 环; 以及一个面节点模板,它定义了一个输入面。 PQQ 细化的模板如下图所示。 顶行中的蓝色邻域表示红色细化节点的相应模板。
带有权重的模板称为几何模板。细分方法为每个模板定义一个几何掩模,并通过对掩模加权的源点进行平均来生成新的点。几何掩模是精心挑选的,以满足一定的表面光滑度和形状质量的要求。Catmull-Clark细分的几何掩模如下图所示。
此处显示的权重未标准化,n 是顶点的价数。 生成的点(红色)是通过加权点的总和来计算的。 例如,Catmull-Clark 面节点是通过对其模板上每个点的 1/4 求和来计算的。
模板可以具有无限数量的几何蒙版。 例如,PQQ 细化的面部节点可以通过每个模板节点的 1/5 而不是 1/4 的总和来计算。 尽管在 Subdivision_method_3 中具有任何类型的几何蒙版都是合法的,但结果曲面可能是奇数、不平滑或什至不存在。 [6] 解释了为质量细分表面设计掩模的细节。
3 一个简单的例子:Catmull-Clark 细分
假设您熟悉 Surface_mesh,您可以毫不费力地将 Subdivision_method_3 集成到您的程序中。
文件 Subdivision_method_3/CatmullClark_subdivision.cpp
cpp
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/subdivision_method_3.h>
#include <CGAL/Timer.h>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> PolygonMesh;
using namespace std;
using namespace CGAL;
namespace params = CGAL::parameters;
int main(int argc, char** argv) {
if (argc > 4) {
cerr << "Usage: CatmullClark_subdivision [d] [filename_in] [filename_out] \n";
cerr << " d -- the depth of the subdivision (default: 1) \n";
cerr << " filename_in -- the input mesh (.off) (default: data/quint_tris.off) \n";
cerr << " filename_out -- the output mesh (.off) (default: result.off)" << endl;
return 1;
}
int d = (argc > 1) ? boost::lexical_cast<int>(argv[1]) : 1;
const std::string in_file = (argc > 2) ? argv[2] : CGAL::data_file_path("meshes/quint_tris.off");
const char* out_file = (argc > 3) ? argv[3] : "result.off";
PolygonMesh pmesh;
std::ifstream in(in_file);
if(in.fail()) {
std::cerr << "Could not open input file " << in_file << std::endl;
return 1;
}
in >> pmesh;
Timer t;
t.start();
Subdivision_method_3::CatmullClark_subdivision(pmesh, params::number_of_iterations(d));
std::cerr << "Done (" << t.time() << " s)" << std::endl;
std::ofstream out(out_file);
out << pmesh;
return 0;
}
此示例演示了在 Surface_mesh 上使用 Catmull-Clark 细分方法。 只有一行值得详细解释:
c++
Subdivision_method_3::CatmullClark_subdivision(pmesh, params::number_of_iterations(d));
Subdivision_method_3 指定细分函数的命名空间。 CatmullClark_subdivision(P, params::number_of_iterations(d)) 在 d 次细化迭代后计算多边形网格 pmesh 的 Catmull-Clark 细分曲面。 多边形网格 pmesh 通过引用传递,并通过细分函数进行修改(即细分)。
此示例展示如何使用 Subdivision_method_3 细分 Surface_mesh。 应用程序定义的多边形网格可以使用专门的内核和/或专门的内部容器。 应用程序定义的多边形网格与 Subdivision_method_3 一起使用有一个主要限制:内部容器中的基元(例如顶点、半边和面)是按顺序排序的(例如 std::vector 和 std::list)。 这意味着迭代器按照原语的创建/插入顺序遍历原语。
细化版主对这两个限制做了详细的解释。
4 Catmull-Clark 细化
Subdivision_method_3 旨在允许自定义细分方法。 本节介绍 Subdivision_method_3 中 Catmull-Clark 细分函数的实现。 该实现演示了将 PQQ 细化定制为 Catmull-Clark 细分。
当开发细分方法时,选择细化模式,然后开发一组几何掩模来定位新点。 实现细分方法需要三个关键组件:
- 可以表示任意 二维流形的网格数据结构,
- 细化网格数据结构的过程,
- 以及计算新点的几何掩模。
E. Catmull 和 J. Clark 选择 PQQ 细化作为其细分方法,并开发了一组几何掩模来从控制网格生成(或更准确地说,近似)B 样条曲面。Subdivision_method_3
提供了一个将 Catmull-Clark 细分方法的所有三个组成部分粘合在一起的函数。
cpp
template <class PolygonMesh, class Mask, class NamedParameters>
void PQQ(PolygonMesh& p, Mask mask, NamedParameters np)
PolygonMesh 必须是 Polyhedron_3、Surface_mesh 或 MutableFaceGraph 概念的任何其他模型的实例。 它是任意 二维 流形的通用网格数据结构。 PQQ() 细化控制网格 p,是一个细化主机,它使用策略类 Mask 作为其几何计算的一部分。 在细化过程中,PQQ() 通过与掩模配合来计算并分配新点。 为了实现Catmull-Clark细分,几何策略Mask必须实现Catmull-Clark细分的几何掩模。 迭代次数以及顶点图可以使用命名参数 np 来指定。
为了实现几何掩码,我们需要知道细化主机如何与其几何掩码进行通信。 PQQ 细化定义了三个模板,因此 Catmull-Clark 细分需要三个几何掩模。 以下类定义了用于 PQQ 细化的模板的接口。
cpp
template <class PolygonMesh>
class PQQMask_3 {
void face_node(boost::graph_traits<PolygonMesh>::face_descriptor face, Point_3& pt);
void edge_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor edge, Point_3& pt);
void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor vertex, Point_3& pt);
};
PQQMask_3 中的每个类函数根据图元描述符的邻域计算一个新点,并将新点分配给 Point_3& pt。
我们用 Catmull-Clark 细分的几何掩模来实现每个类函数。
cpp
template <class PolygonMesh>
class CatmullClark_mask_3 {
typedef boost::graph_traits<PolygonMesh>::face_descriptor face_descriptor;
typedef boost::property_traits<VertexPointMap>::value_type Point_3;
Polygonmesh pmesh;
VertexPointMap vpm;
CatmullClark_mask_3(PolygonMesh &pmesh, VertexPointMap vpm)
: pmesh(pmesh, vpm(vpm)
{}
void face_node(face_descriptor face, Point_3& pt) {
int n = 0;
Point_3 p(0,0,0);
for(halfedge_descriptor hd : halfedges_around_face(face,pmesh)){
p = p + get(vpm, (target(hd,pmesh)) - ORIGIN);
++n;
}
pt = ORIGIN + (p - ORIGIN)/FT(n);
}
};
void edge_node(halfedge_descriptor edge, Point_3& pt) {
Point_3 p1 = get(vpm,target(edge, pmesh));
Point_3 p2 = get(vpm,source(edge, pmesh));
Point_3 f1, f2;
face_node(face(edge,pmesh), f1);
face_node(face(opposite(edge,pmesh),pmesh), f2);
pt = Point_3((p1[0]+p2[0]+f1[0]+f2[0])/4,
(p1[1]+p2[1]+f1[1]+f2[1])/4,
(p1[2]+p2[2]+f1[2]+f2[2])/4 );
}
void vertex_node(vertex_descriptor vertex, Point_3& pt) {
Halfedge_around_target_circulator<PolygonMesh> vcir(vertex,pmesh);
typename boost::graph_traits<PolygonMesh>::degree_size_type n = degree(vertex,pmesh);
FT Q[] = {0.0, 0.0, 0.0}, R[] = {0.0, 0.0, 0.0};
Point_3 S = get(vpm,vertex);
Point_3 q;
for (int i = 0; i < n; i++, ++vcir) {
Point_3 p2 = get(vpm, target(opposite(*vcir,pmesh),pmesh));
R[0] += (S[0]+p2[0])/2;
R[1] += (S[1]+p2[1])/2;
R[2] += (S[2]+p2[2])/2;
face_node(face(*vcir,pmesh), q);
Q[0] += q[0];
Q[1] += q[1];
Q[2] += q[2];
}
R[0] /= n; R[1] /= n; R[2] /= n;
Q[0] /= n; Q[1] /= n; Q[2] /= n;
pt = Point_3((Q[0] + 2*R[0] + S[0]*(n-3))/n,
(Q[1] + 2*R[1] + S[1]*(n-3))/n,
(Q[2] + 2*R[2] + S[2]*(n-3))/n );
}
};
为了调用 Catmull-Clark 细分方法,我们使用刚刚定义的 Catmull-Clark 掩码调用 PQQ()。
cpp
PQQ(pmesh, CatmullClark_mask_3(pmesh), params::number_of_iterations(depth));
Loop、Doo-Sabin 和 √3 细分使用类似的过程来实现:选择细化主机并实施几何策略。 开发自己的细分方法的关键是实现细化主体和几何策略的正确组合。 接下来的两节将对此进行解释。
5 Refinement Host
细化主机是多边形网格类和几何掩模类的模板函数。 它细化输入的多边形网格,并通过几何蒙版计算新点。 Subdivision_method_3 支持四种细化主机:原始四边形四等分 (PQQ)、原始三角形四等分 (PTQ)、对偶四边形四等分 (DQQ) 和 √3 三角剖分。 它们分别由 Catmull-Clark、Loop、Doo-Sabin 和 √3 细分使用。
cpp
namespace Subdivision_method_3 {
template <class PolygonMesh, class Mask, class NamedParameters>
void PQQ(PolygonMesh& pmesh, Mask mask, NamedParameters np);
template <class PolygonMesh, class Mask, class NamedParameters>
void PTQ(PolygonMesh& pmesh, Mask mask, NamedParameters np);
template <class PolygonMesh, class Mask, class NamedParameters>
void DQQ(PolygonMesh& pmesh, Mask mask, NamedParameters np)
template <class PolygonMesh, class Mask, class NamedParameters>
void Sqrt3(PolygonMesh& pmesh, Mask mask, NamedParameters np)
}
网格类必须是MutableFaceGraph的模型,并且必须是三角形网格或多边形网格,掩模是实现细分方法的几何掩模的策略类。
细化主机细化输入多边形网格,维护模板(即控制网格和细化网格之间的映射),并调用几何蒙版来计算新点。 在 Subdivision_method_3 中,细化被实现为一系列连接操作(主要是欧拉操作)。 连接操作的顺序在维护模板时起着关键作用。 通过将源子网格的顺序与细化顶点相匹配,基元中不需要任何标志来注册模板。 它避免了细化主机对多边形网格类的数据依赖。 为了使排序技巧发挥作用,多边形网格类必须有一个顺序容器(例如向量或链表)作为内部存储。 顺序容器保证多边形网格的迭代器始终按照图元的插入顺序遍历图元。 非顺序结构(例如树或映射)不提供所需的排序,因此不能与 Subdivision_method_3 一起使用。
尽管 Subdivision_method_3 不需要标志来支持细化和模板,但它仍然需要知道如何计算和存储几何数据(即点)。 Subdivision_method_3 的类具有作为可选模板参数的顶点属性映射,该映射提供顶点和点之间的映射。
细化主机 PQQ 和 DQQ 在一般多边形网格上工作,PTQ 和 Sqrt3 在三角多边形网格上工作。 PTQ 和 Sqrt3 在非三角多边形网格上的结果未定义。 Subdivision_method_3在细化之前不验证网格特性的前提条件。
有关细化实现的详细信息,感兴趣的用户应参考[5]。
6 Geometry Policy
几何策略定义了一组几何掩码。 每个几何掩模都被实现为计算细分表面的新点的成员函数。
每个几何掩模接收控制网格的图元描述符(例如halfedge_descriptor),并将Point_3返回到细分的顶点。 该函数收集基元描述符的顶点邻居(即模板上的节点),并根据邻居和掩码(即模板权重)计算新点。
该图显示了 Catmull-Clark 细分的几何掩模。 此处显示的权重未标准化,n 是顶点的价数。 新点是通过模板上加权点的总和来计算的。 以下代码显示了面节点的几何掩模的实现。 Catmull-Clark 几何策略的完整列表位于 Catmull-Clark 细分部分。
cpp
template <class PolygonMesh, class VertexPointMap>
class CatmullClark_mask_3 {
typedef boost::graph_traits<PolygonMesh>::face_descriptor face_descriptor;
typedef boost::property_traits<VertexPointMap>::value_type Point_3;
CatmullClark_mask_3(PolygonMesh &pmesh, VertexPointMap vpm);
void face_node(face_descriptor face, Point_3& pt) {
int n = 0;
Point_3 p(0,0,0);
for(halfedge_descriptor hd : halfedges_around_face(face,pmesh)){
p = p + get(vpm, (target(hd,pmesh)) - ORIGIN);
++n;
}
pt = ORIGIN + (p - ORIGIN)/FT(n);
}
};
在此示例中,计算基于 Point_3 是 CGAL::Point_3 的假设。 这是一个假设,但不是限制。 您可以使用任何点类,只要它在多边形网格中定义为 Point_3 即可。 您可能需要修改几何策略以支持特殊点的计算和分配。 这种扩展在图形应用程序中并不罕见。 例如,您可能想要细分细分曲面的纹理坐标。
Catmull-Clark 细分的细化主机需要三个用于没有开放边界的多边形网格的几何蒙版:顶点节点蒙版、边缘节点蒙版和面节点蒙版。 为了支持具有边界的多边形网格,还需要边界节点掩码。 下面列出了 Catmull-Clark 细分的边界节点掩码,其中 ept 返回分割边的新点,vpt 返回边所指向的顶点上的新点。
cpp
void border_node(halfedge_descriptor edge, Point_3& ept, Point_3& vpt) {
Point_3 ep1 = get(vpm, target(edge, pmesh));
Point_3 ep2 = get(vpm, target(opposite(edge, pmesh), pmesh));
ept = Point_3((ep1[0]+ep2[0])/2, (ep1[1]+ep2[1])/2, (ep1[2]+ep2[2])/2);
Halfedge_around_target_circulator<Poly> vcir(edge, pmesh);
Point_3 vp1 = get(vpm,target(opposite(*vcir, pmesh ), pmesh));
Point_3 vp0 = get(vpm, target(*vcir, pmesh));
--vcir;
Point_3 vp_1 = get(vpm, target(opposite(*vcir, pmesh), pmesh));
vpt = Point_3((vp_1[0] + 6*vp0[0] + vp1[0])/8,
(vp_1[1] + 6*vp0[1] + vp1[1])/8,
(vp_1[2] + 6*vp0[2] + vp1[2])/8 );
}
下面列出了所有四个细化主机的掩码接口。 DQQMask_3 没有边界节点模板,因为 DQQ 细化的细化主机不支持当前版本中的全局边界。 这可能会在未来的版本中发生变化。
cpp
template <class PolygonMesh>
class PQQMask_3 {
void face_node(boost::graph_traits<PolygonMesh>::face_descriptor, Point_3&);
void edge_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&);
void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor, Point_3&);
void border_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&, Point_3&);
};
template <class PolygonMesh>
class PTQMask_3 {
void edge_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&);
void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor, Point_3&);
void border_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor, Point_3&, Point_3_&);
};
template <class PolygonMesh>
class DQQMask_3 {
public:
void corner_node(boost::graph_traits<PolygonMesh>::halfedge_descriptor edge, Point_3& pt);
};
template <class PolygonMesh>
class Sqrt3Mask_3 {
public:
void vertex_node(boost::graph_traits<PolygonMesh>::vertex_descriptor vertex, Point_3& pt);
};
Catmull Clark mask_3、Loop_mask_3、DooSabin_mask_3 和 Sqrt3_mask_3 的源代码是学习这些模板接口的最佳来源。
7 四种细分方法
Subdivision_method_3 通过专门化各自的细化主机来支持 Catmull-Clark、Loop、Doo-Sabin 和 √3 细分。 它们被设计用于任何 MutableFaceGraph 模型,例如 Polyhedron_3 和 Surface_mesh。 如果您的应用程序使用具有专门几何内核的多边形网格,则需要使用基于该内核的几何策略来专门化细化主机。
cpp
namespace Subdivision_method_3 {
template <class PolygonMesh, class NamedParameters>
void CatmullClark_subdivision(PolygonMesh& pmesh, NamedParameters np) {
PQQ(pmesh, CatmullClark_mask_3<PolygonMesh>(pmesh), np);
}
template <class PolygonMesh, class NamedParameters>
void Loop_subdivision(PolygonMesh& pmesh, NamedParameters np) {
PTQ(pmesh, Loop_mask_3<PolygonMesh>(pmesh) , np);
}
template <class PolygonMesh, class NamedParameters>
void DooSabin_subdivision(PolygonMesh& pmesh, NamedParameters np) {
DQQ(pmesh, DooSabin_mask_3<PolygonMesh>(pmesh), np);
}
template <class PolygonMesh, class NamedParameters>
void Sqrt3_subdivision(PolygonMesh& pmesh, NamedParameters np) {
Sqrt3(pmesh, Sqrt3_mask_3<PolygonMesh>(pmesh), np);
}
}
8 示例:自定义细分方法
Subdivision_method_3 支持在 Polyhedron_3 上使用具有笛卡尔坐标的点的四种实用细分方法。 通过具有自定义几何掩模的细化主机的专业化,可以支持更多细分方法。 以下示例开发了一种细分方法,生成改进的循环细分曲面。
文件 Subdivision_method_3/Customized_subdivision.cpp
cpp
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/subdivision_method_3.h>
#include <CGAL/Timer.h>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <fstream>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> PolygonMesh;
using namespace std;
using namespace CGAL;
namespace params = CGAL::parameters;
// ======================================================================
template <class Poly>
class WLoop_mask_3 {
typedef Poly PolygonMesh;
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::property_map<PolygonMesh, vertex_point_t>::type Vertex_pmap;
typedef typename boost::property_traits<Vertex_pmap>::value_type Point;
typedef typename boost::property_traits<Vertex_pmap>::reference Point_ref;
PolygonMesh& pmesh;
Vertex_pmap vpm;
public:
WLoop_mask_3(PolygonMesh& pmesh)
: pmesh(pmesh), vpm(get(CGAL::vertex_point, pmesh))
{}
void edge_node(halfedge_descriptor hd, Point& pt) {
Point_ref p1 = get(vpm, target(hd,pmesh));
Point_ref p2 = get(vpm, target(opposite(hd,pmesh),pmesh));
Point_ref f1 = get(vpm, target(next(hd,pmesh),pmesh));
Point_ref f2 = get(vpm, target(next(opposite(hd,pmesh),pmesh),pmesh));
pt = Point((3*(p1[0]+p2[0])+f1[0]+f2[0])/8,
(3*(p1[1]+p2[1])+f1[1]+f2[1])/8,
(3*(p1[2]+p2[2])+f1[2]+f2[2])/8 );
}
void vertex_node(vertex_descriptor vd, Point& pt) {
double R[] = {0.0, 0.0, 0.0};
Point_ref S = get(vpm,vd);
std::size_t n = 0;
for(halfedge_descriptor hd : halfedges_around_target(vd, pmesh)){
++n;
Point_ref p = get(vpm, target(opposite(hd,pmesh),pmesh));
R[0] += p[0]; R[1] += p[1]; R[2] += p[2];
}
if (n == 6) {
pt = Point((10*S[0]+R[0])/16, (10*S[1]+R[1])/16, (10*S[2]+R[2])/16);
} else if (n == 3) {
double B = (5.0/8.0 - std::sqrt(3+2*std::cos(6.283/n))/64.0)/n;
double A = 1-n*B;
pt = Point((A*S[0]+B*R[0]), (A*S[1]+B*R[1]), (A*S[2]+B*R[2]));
} else {
double B = 3.0/8.0/n;
double A = 1-n*B;
pt = Point((A*S[0]+B*R[0]), (A*S[1]+B*R[1]), (A*S[2]+B*R[2]));
}
}
void border_node(halfedge_descriptor hd, Point& ept, Point& vpt) {
Point_ref ep1 = get(vpm, target(hd,pmesh));
Point_ref ep2 = get(vpm, target(opposite(hd,pmesh),pmesh));
ept = Point((ep1[0]+ep2[0])/2, (ep1[1]+ep2[1])/2, (ep1[2]+ep2[2])/2);
Halfedge_around_target_circulator<Poly> vcir(hd,pmesh);
Point_ref vp1 = get(vpm, target(opposite(*vcir,pmesh),pmesh));
Point_ref vp0 = get(vpm, target(*vcir,pmesh));
--vcir;
Point_ref vp_1 = get(vpm,target(opposite(*vcir,pmesh),pmesh));
vpt = Point((vp_1[0] + 6*vp0[0] + vp1[0])/8,
(vp_1[1] + 6*vp0[1] + vp1[1])/8,
(vp_1[2] + 6*vp0[2] + vp1[2])/8 );
}
};
int main(int argc, char **argv) {
if (argc > 4) {
cerr << "Usage: Customized_subdivision [d] [filename_in] [filename_out] \n";
cerr << " d -- the depth of the subdivision (default: 1) \n";
cerr << " filename_in -- the input mesh (.off) (default: data/quint_tris.off) \n";
cerr << " filename_out -- the output mesh (.off) (default: result.off)" << endl;
return 1;
}
int d = (argc > 1) ? boost::lexical_cast<int>(argv[1]) : 1;
const std::string in_file = (argc > 2) ? argv[2] : CGAL::data_file_path("meshes/quint_tris.off");
const char* out_file = (argc > 3) ? argv[3] : "result.off";
PolygonMesh pmesh;
std::ifstream in(in_file);
if(in.fail()) {
std::cerr << "Could not open input file " << in_file << std::endl;
return 1;
}
in >> pmesh;
Timer t;
t.start();
Subdivision_method_3::PTQ(pmesh, WLoop_mask_3<PolygonMesh>(pmesh), params::number_of_iterations(d));
std::cerr << "Done (" << t.time() << " s)" << std::endl;
std::ofstream out(out_file);
out << pmesh;
return 0;
}
几何掩模生成的点在语义上需要收敛到光滑的表面。 这是细分曲面理论提出的要求。 Subdivision_method_3 不强制执行此要求,也不会验证细分网格的平滑度。 Subdivision_method_3保证了细分网格的拓扑属性。 属-n 2-流形确保被细分为属-n 2-流形。 但是,当专门处理设计不当的几何蒙版时,Subdivision_method_3 可能会生成奇怪的、不平滑的或什至不存在的表面。
9 实施历史
该软件包最初由 Le-Jeng Andy Shiue 开发。 对于 CGAL 4.11,它被 Andreas Fabri 和 Mael Rouxel-Labbé 推广到适用于任何 MutableFaceGraph 模型。