3D Surface Subdivision Methods 3D 曲面细分方法

news2024/7/30 7:55:31

文章目录

  • 1 介绍
  • 2 细分法
  • 3 一个简单的例子:Catmull-Clark 细分
  • 4 Catmull-Clark 细化
  • 5 Refinement Host
  • 6 Geometry Policy
  • 7 四种细分方法
  • 8 示例:自定义细分方法
  • 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

#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 细分方法。 只有一行值得详细解释:

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 细分方法的所有三个组成部分粘合在一起的函数。

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 细化的模板的接口。

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 细分的几何掩模来实现每个类函数。

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()。

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 细分使用。

在这里插入图片描述

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 细分部分。

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 返回边所指向的顶点上的新点。

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 细化的细化主机不支持当前版本中的全局边界。 这可能会在未来的版本中发生变化。

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。 如果您的应用程序使用具有专门几何内核的多边形网格,则需要使用基于该内核的几何策略来专门化细化主机。

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

#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 模型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1367892.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入了解选择重传协议(Selective Repeat Protocol)

选择重传协议&#xff08;Selective Repeat Protocol&#xff09;是计算机网络中常用的一种数据链路层协议&#xff0c;用于确保数据的可靠传输。本文将深入探讨选择重传协议的原理、应用场景及优缺点。 选择重传协议的核心思想是在发送端发送数据时&#xff0c;每一帧都被编号…

git ssh key 配置

一、Profile Settings-->SSH Keys 我们点击这里会有详情的文档介绍生成sshkey。 ssh-keygen -t rsa -b 2048 -C "邮箱" --回车... 将生成的id_rsa.pub粘贴到如下保存 git config --global user.name "用户名" git config --global user.email "邮…

苹果电脑交互式原型设计软件Axure RP 9 mac特色介绍

Axure RP 9 for Mac是一款交互式原型设计软件&#xff0c;使用axure rp9以最佳的方式展示您的作品&#xff0c;优化现代浏览器并为现代工作流程设计。同时确保您的解决方案正确完整地构建。Axure RP 9 for Mac为您整理笔记&#xff0c;将其分配给UI元素&#xff0c;并合并屏幕注…

apk反编译修改教程系列---修改apk包名等信息 让一个应用拥有无限分身 手机电脑同步演示【九】

往期教程&#xff1a; apk反编译修改教程系列-----修改apk应用名称 任意修改名称 签名【一】 apk反编译修改教程系列-----任意修改apk版本号 版本名 防止自动更新【二】 apk反编译修改教程系列-----修改apk中的图片 任意更换apk桌面图片【三】 apk反编译修改教程系列---简单…

Java后端开发——SSM整合实验

文章目录 Java后端开发——SSM整合实验一、常用方式整合SSM框架二、纯注解方式整合SSM框架 Java后端开发——SSM整合实验 一、常用方式整合SSM框架 1.搭建数据库环境&#xff1a;MySQL数据库中创建一个名称为ssm的数据库&#xff0c;在该数据库中创建一个名称为tb_book的表 …

Spark MLlib ----- ALS算法

补充 在谈ALS(Alternating Least Squares)之前首先来谈谈LS,即最小二乘法。LS算法是ALS的基础,是一种数优化技术,也是一种常用的机器学习算法,他通过最小化误差平方和寻找数据的最佳匹配,利用最小二乘法寻找最优的未知数据,保证求的数据与已知的数据误差最小。LS也被用…

Fastdata极数公司介绍

【写在开头】 前不久看到一句话和一个新闻&#xff0c;“数据作为主要生产要素&#xff0c;以资源化为起点&#xff0c;经历资产化、资本化阶段&#xff0c;逐步实现数据价值。” 2023年10月25日&#xff0c;国家数据局正式揭牌&#xff0c;由国家发展和改革委员会管理。 初看…

4.2 MATRIX MULTIPLICATION

矩阵-矩阵乘法&#xff0c;或简称矩阵乘法&#xff0c;在 i X j&#xff08;i 行 by j 列&#xff09;矩阵 M 和 j x k 矩阵 N 之间产生 i X k 矩阵P。矩阵乘法是基本线性代数子程序&#xff08;BLAS&#xff09;标准的重要组成部分&#xff08;见第3章中的“线性代数函数”边栏…

SpringBoot-开启Actuator监控

Spring Boot Actuator是Spring Boot提供的一种管理和监控应用程序的框架&#xff0c;可以帮助我们了解应用程序的运行状况&#xff0c;提供HTTP端点来暴露应用程序的不同方面&#xff0c;如健康状况、指标、日志和运行时信息等。 开启Actuator监控&#xff0c;我们可以通过HTT…

IDEA+Git——项目分支管理

IDEAGit——项目分支管理 1. 前言2. 基础知识点2.1. 分支区分2.2. Git 代码提交规范2.3. 四个工作区域2.4. 文件的四种状态2.5. 常用命令2.6 注重点 3. IDEA分支管理 1. 前言 在Git中&#xff0c;分支是项目的不同版本&#xff0c;当开始开发一个新项目时&#xff0c;主分支通常…

基于Java SSM框架实现班级同学录管理系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现班级同学录管理系统演示 SSM框架 当今流行的“SSM组合框架”是Spring SpringMVC MyBatis的缩写&#xff0c;受到很多的追捧&#xff0c;“组合SSM框架”是强强联手、各司其职、协调互补的团队精神。web项目的框架&#xff0c;通常更简单的数据源。Spri…

LabVIEW开发自动光学焊点检测系统

LabVIEW开发自动光学焊点检测系统 LabVIEW于开发了一个自动光学焊点检测系统&#xff0c;旨在提高电子元件焊接的质量和效率。通过利用LabVIEW的高级视觉开发模块&#xff0c;该系统能够准确地识别和分类电路板上的不同焊点类型&#xff0c;如桥接、虚焊、漏焊和多锡。这一进步…

Hibernate实战之操作MySQL数据库(2024-1-8)

Hibernate实战之操作MySQL数据库 2024.1.8 前提环境&#xff08;JavaMySQLNavicatVS Code&#xff09;1、Hibernate简介1.1 了解HQL 2、MySQL数据库建表2.1 编写SQL脚本2.2 MySQL执行脚本 3、Java操作MySQL实例&#xff08;Hibernate&#xff09;3.1 准备依赖的第三方jar包3.2 …

【S32K 进阶之旅】 NXP S32K3 以太网 RMII 接口调试(3)

前言 两章我们使用大联大世平集团开发的 NXP S32K344 开发板和 NXP 官网开放的 TCP/IP stacks 例程完成了 NXP S32K3 以太网功能的开发环境搭建和软件调试&#xff0c;下面验证 RMII 百兆通信基本功能。没看过第一节的小伙伴请移步《【S32K 进阶之旅】 NXP S32K3 以太网 RMII 接…

《矩阵分析》笔记

来源&#xff1a;【《矩阵分析》期末速成 主讲人&#xff1a;苑长&#xff08;5小时冲上90&#xff09;】https://www.bilibili.com/video/BV1A24y1p76q?vd_sourcec4e1c57e5b6ca4824f87e74170ffa64d 这学期考矩阵论&#xff0c;使用教材是《矩阵论简明教程》&#xff0c;因为没…

爬虫-5-数据提取-正则,xpath

#免责声明:本文仅供学习&#xff0c;请遵纪守法。 ԅ(ㅂԅ)

静态网页设计——个人图书馆(HTML+CSS+JavaScript)(dw、sublime Text、webstorm、HBuilder X)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; https://www.bilibili.com/video/BV1VN4y1q7cz/?vd_source5f425e0074a7f92921f53ab87712357b 源码&#xff1a;https://space.bilibili.co…

【嵌入式】git工具基础命令学习笔记 | 嵌入式Linux

文章目录 前言一、git基础指令二、git分支管理2.1、一些基础命令2.2、时间回溯2.3、新建分支&#xff0c;删除分支&#xff0c;重命名分支 前言 参考&#xff1a;飞凌嵌入式ElfBoard ELF-1 软件学习手册 chatGPT 一、git基础指令 首先我们新建一个名叫git的文件夹&#xff0…

【Java集合篇】HashMap的put方法是如何实现的?

HashMap的put方法是如何实现的 ✔️典型解析✔️ 拓展知识仓✔️HashMap put方法的优缺点有哪些✔️如何避免HashMap put方法的哈希冲突✔️如何避免HashMap put方法的哈希重 ✔️源码解读✔️putVal 方法主要实现如下&#xff0c;为了更好的帮助大家阅读&#xff0c;提升效率&…

MySql01:初识

1.mysql数据库2.配置环境变量3. 列的类型和属性&#xff0c;索引&#xff0c;注释3.1 类型3.2 属性3.3 主键(主键索引)3.4 注释 4.结构化查询语句分类&#xff1a;5.列类型--表列类型设置 1.mysql数据库 数据库&#xff1a; ​ 数据仓库&#xff0c;存储数据&#xff0c;以前我…