cgal教程 3D Alpha Wrapping

文章目录

  • 3D Alpha Wrapping (3D alpha 包裹)
  • 1 介绍
  • 2 方法
    • 2.1 算法
    • 2.2 保证
  • 3 接口
  • 4 选择参数
    • 4.1 alpha
    • 4.2 Offset
    • 4.3 关于“双面”包裹的注意事项
  • 5 性能
  • 6 例子

3D Alpha Wrapping (3D alpha 包裹)

原文地址: https://doc.cgal.org/latest/Alpha_wrap_3/index.html#Chapter_3D_Alpha_wrapping

该组件采用 3D 三角形网格、三角形汤或点集作为输入,并生成严格包含输入的有效三角表面网格(水密、无交集和 二维流形)。 该算法通过从输入的松散边界框开始收缩包裹并细化 3D Delaunay 三角剖分来进行。 两个用户定义的参数(alpha 和 offset)分别可以控制收缩包裹过程可以进入的空腔的最大尺寸,以及最终表面网格与输入的紧密度。 一旦组合起来,这些参数就提供了一种以输入的保真度换取输出的复杂性的方法。

在这里插入图片描述

1 介绍

几何建模和处理中的各种任务需要将3D对象表示为有效的表面网格,其中“有效”是指3D对象。指的是水密、无相交、可定向和 二维流形的网格。这种表示提供了内部/外部和测地线邻域的明确定义的概念。

3D 数据通常通过测量和重建来获取,由人类设计,或通过不完善的自动化过程生成。因此,它们可能会表现出各种各样的缺陷,包括间隙、缺失数据、自相交、简并性(例如零体积结构)和非流形特征。

鉴于可能存在的缺陷种类繁多,人们提出了许多方法和数据结构来修复特定缺陷,通常目的是保证修复的 3D 模型中的特定属性。可靠地修复所有类型的缺陷是众所周知的困难,并且通常是一个不适定问题,因为对于给定的带有缺陷的 3D 模型存在许多有效的解决方案。此外,输入模型可能过于复杂,具有不必要的几何细节、虚假拓扑结构、不重要的内部组件或过于精细的离散化。对于防撞、路径规划或模拟等应用,获取输入的近似值可能比修复输入更有意义。这里的近似是指能够滤除内部结构、精细细节和空腔以及将输入包裹在用户定义的偏移裕度内的方法。

给定输入 3D 几何形状,我们解决计算保守近似的问题,其中保守意味着保证输出严格包围输入。我们寻求无条件的鲁棒性,即输出网格应该有效(定向、二维流形且无自相交),即使对于具有许多缺陷和简并性的原始输入也是如此。默认输入是 3D 三角形汤,但通用接口为其他类型的有限 3D 图元(例如三角形汤和点集)敞开了大门。

在这里插入图片描述

在这里插入图片描述

2 方法

人们设计了许多方法将 3D 模型封装在一个体积内,这些方法具有运行时间和近似质量(即紧密度)之间的不同平衡。在最简单的情况下,轴对齐或定向的边界框显然满足一些所需的属性;然而,近似误差是不可控的并且通常非常大。计算输入的凸包也匹配一些所需的属性并提高结果的质量,尽管代价是增加运行时间。然而,近似值仍然很粗糙,特别是在有多个组件的情况下。

凸包实际上是 alpha 形状的特例 (Chapter_3D_Alpha_Shapes)。从数学上讲,α 形状是 Delaunay 三角剖分的子复形,单纯形是复形的一部分,具体取决于其最小(空)Delaunay 球的大小。直观上,构建 3D Alpha 形状可以被认为是用用户定义的半径 alpha 的空球来雕刻 3D 空间。 Alpha 形状产生可证明的、良好的形状分段线性近似[1],但是是在点集上定义的,而我们希望处理更一般的情况输入数据,例如三角汤。即使在对三角形汤进行采样之后,阿尔法形状也不能保证对于任何阿尔法都是保守的。最后,内部结构也被雕刻在体积内,而不是被过滤掉。

受 alpha 形状的启发,我们用收缩包裹替换上述雕刻概念收缩包裹:我们迭代地构建 3D Delaunay 三角剖分的子复形从包围输入的简单 3D Delaunay 三角剖分开始,然后迭代删除位于复合体边界上的合格四面体。此外,随着收缩的进行,底层的三角测量——以及随之而来的复杂——也被细化。因此,我们不是像 alpha 形状那样从输入数据的凸包进行雕刻,而是通过类似 Delaunay 细化的算法构建一个全新的网格。细化算法在偏移体积的边界上插入斯坦纳点,偏移体积定义为输入的无符号距离场的水平集。

此过程既可以防止在输出中创建内部结构,又可以避免多余的计算。此外,将网格结构与输入的几何和离散化分离有几个优点:(1)底层数据不限于特定格式(三角形汤、多边形汤、点云等),因为所有这些都需要正在回答三个基本几何查询:(a) 点与输入之间的距离,(b) 查询点在输入上的投影,© 四面体与输入之间的相交测试,以及 (2)用户可以更自由地以输入的紧密度换取最终的网格复杂性,因为在输入的大偏移量上构造保守近似需要更少的网格元素。

2.1 算法

初始化。该算法通过将松散边界框的八个角顶点插入 3D Delaunay 三角剖分来初始化。在 CGAL 的 3D Delaunay 三角剖分中,所有三角形面都与两个四面体单元相邻。 Delaunay 三角剖分边界的每个小面(与三角剖分顶点的凸包的一个小面重合)都与所谓的 无穷大 相邻四面体单元,一个连接到所谓的无限顶点的抽象单元,以确保上述的双面邻接。最初,所有无限单元都标记为外部,所有有限四面体单元都标记为内部。

收缩包装。收缩包裹算法通过从外到内遍历 Delaunay 三角剖分的单元,从一个单元到其相邻单元进行泛洪填充,并尽可能将相邻单元标记为外部(术语“可能”将在后面指定)。洪水填充是通过 Delaunay 三角形面的优先级队列实现的,该优先级队列表示面的两个相邻单元之间从外到内的遍历。这些三角形面在下文中称为

给定一个外部单元及其相邻的内部单元,如果满足以下条件,则公共面(即门)被称为 alpha 可遍历它的外接圆半径大于用户定义的参数 alpha,其中外接圆半径是指相关三角形的德劳内球的半径。直观上,小于 alpha 的空腔是不可访问的,因为它们的门不可 alpha 穿过。

优先级队列由凸包上的 alpha 可遍历门初始化,仅包含 alpha 可遍历门,并按门外接圆半径的降序排序。遍历可以被视为一个连续的过程,沿着门的双 Voronoi 边缘前进,并用一束空球包围着门。

在这里插入图片描述

图 62.3(左)铅笔画的空心圆(蓝色)外接 2D Delaunay 三角剖分(黑色)中的 Delaunay 边(绿色)。 从顶部三角形外心 c1 到底部三角形外心 c2,由 e(红色虚线)表示的对偶 Voronoi 边是没有 Delaunay 顶点的最大圆的中心迹。 (右)与左示例相对应的图表。 x 轴对应于位于 Voronoi 边 e(从 c1 到 c2)上的空圆中心的位置。 y 轴是对应空心圆的半径值。 在这种情况下,这支空心圆铅笔的最小半径位于绿色 Delaunay 边缘的中点。 在我们的算法中,当空圆的铅笔的最小半径小于 alpha 时,门(绿色 Delaunay 边)被认为是不可 alpha 遍历的。

当通过 alpha 可遍历的面 f 从外部单元 co 遍历到内部单元 ci 时,将测试两个标准以防止包装过程与输入发生冲突:

(1) 我们检查 f 的双 Voronoi 边(即两个入射单元的外心之间的线段)与偏移曲面(定义为输入的无符号等值面的水平集)之间的交点。 如果存在一个或多个交点,则沿着从外向内定向的双 Voronoi 边的第一个交点将作为 Steiner 点插入到三角剖分中。

(2) 如果对偶 Voronoi 边不与偏移曲面相交,但相邻单元 ci 与输入相交,我们计算 ci 的外心在偏移曲面上的投影,并将其作为 Steiner 点插入三角剖分中(这会破坏 ci)。

在上述每次 Steiner 点插入之后,所有新的事件单元都被标记为内部,并且新的 alpha 可遍历门被推入优先级队列。

如果以上两个标准都不满足,则遍历相邻小区 ci 并将其标记为外部。 将内部与外部单元分开的 ci 的 Alpha 可遍历方面被作为新门推入优先级队列。

一旦队列清空(由于插入新的斯坦纳点,面(及其外接半径)变得更小,这一过程就得到保证)构造阶段终止。 输出三角形表面网格是从 Delaunay 三角剖分中提取的,作为将内部单元与外部单元分开的面集。

下图以二维方式描述了该算法的步骤。

在这里插入图片描述

图 62.4 二维收缩包裹算法的步骤。该算法通过将输入(红色)的松散边界框的角插入 Delaunay 三角剖分中来初始化,并且所有有限三角形都标记在内部(灰色)。从队列中弹出的当前门(绿色边缘)是可 alpha 遍历的。当与门相邻的三角形不与输入相交时,它会被标记在外面,并且新的 alpha 可遍历门会被推送到队列中。当相邻三角形与输入相交时,将计算一个新的斯坦纳点(大绿色圆盘)并将其插入到三角剖分中,所有相邻三角形都在内部标记,新的 alpha 可遍历门被推入队列,并恢复遍历。灰色边缘描绘了 Delaunay 三角剖分。蓝色边缘描绘了 Voronoi 图。粉色圆圈描绘了半径为 alpha 的空圆。输出边(深蓝色)将内部三角形与外部三角形分开。

2.2 保证

该算法被证明可以终止并生成严格包围输入数据的 2 流形三角表面网格。 证明的关键要素是我们从外到内换行,并且绝不允许在内部标记与输入相交的单元格。 此外,导致三角测量细化的两个标准插入斯坦纳点,保证破坏需要细化的单元并减少相邻面的圆周半径。

由于主要的细化标准是在双 Voronoi 边与输入偏移之间插入交集,或者将 Voronoi 顶点投影到输入偏移上,因此该算法与基于 Delaunay 滤波和 细化(参见 Chapter_3D_Mesh_Generation)。

3 接口

我们的算法将一组 3D 三角形作为输入,以三角形汤或三角形表面网格的形式提供,以及两个用户定义的标量参数:alpha 和偏移值。 它通过从输入的松散边界框开始收缩包装和细化 3D Delaunay 三角剖分来进行。 参数 alpha 指的是在缠绕过程中无法穿过的空腔或孔的大小,因此指的是最终的细节级别,因为 alpha 的作用类似于常见 Delaunay 细化算法 (Chapter_3D_Mesh_Generation) 中的尺寸字段。 参数偏移量是指细化三角剖分的顶点与输入之间的距离,因此较大的偏移量会转化为输入的松散包围。 第二个参数提供了一种控制紧密性和复杂性之间权衡的方法。

该组件的主要入口点是生成 alpha 换行的全局函数 CGAL::alpha_wrap_3(); 该函数将多边形汤或多边形网格作为输入。 输入连通性没有先决条件,因此它可以采用任意三角形汤,具有岛屿、自相交或重叠,以及组合或几何简并性。

底层特征类必须是内核概念的模型。 它应该使用浮点数类型,因为不精确性是该算法固有的,因为偏移表面上的新顶点没有闭合形式描述。

输出是一个三角形表面网格,其类型由用户选择,但必须是 MutableFaceGraph 概念的模型。

4 选择参数

算法的两个参数会影响输出网格的详细程度和复杂性。

4.1 alpha

主要参数 alpha 控制 Delaunay 面在收缩包裹过程中是否可遍历。 Alpha 的主要目的是控制包裹过程中使用的空球的大小,从而确定哪些特征将出现在输出中:事实上,如果一个面的外接圆半径大于 alpha,则它是可 alpha 遍历的; 因此,该算法只能通过直径大于 alpha 的海峡或孔洞进行收缩包裹。 第二个不太直接的结果是,只要面的外接半径大于 alpha,单元内的事件就会被访问并可能被细化。 因此,当算法终止时,所有面的外接半径均小于 alpha。 因此,该参数的行为也类似于输出的三角形面上的大小标准。

在这里插入图片描述

图 62.5 alpha 参数对输出的影响。 (左)通过原始点云表面重建生成的输入三角形网格具有许多非流形边和顶点、多余的几何细节和虚假拓扑结构。 (右)该组件保守地近似输入,并根据 alpha 参数生成具有不同复杂度和输入保真度的有效网格。 α 值越小,收缩包装过程进入型腔的深度就越深。 alpha 参数从左到右递减,分别为输入边界框最长对角线的 1/50、1/100 和 1/300。 大的 alpha 将产生不太复杂的输出,但不太忠实于输入。

4.2 Offset

第二个参数是偏移距离,它控制与输入的距离,从而控制输出网格顶点所在的偏移等值面的定义。 该参数控制结果的紧密度,这反过来又会产生一些后果。 首先,将顶点定位在远离输入的位置使算法能够生成不太复杂的网格,尤其是在凸区域。 这种行为的一个简单例子是一个非常密集的球体网格,对于该球体来说,尽可能紧密的包络也将非常密集。 其次,等值面距离输入越远,通过第一个标准插入的新点就越多(即通过与双 Voronoi 边相交,请参见截面算法); 因此,输出质量在三角形元素的角度方面得到改善。 最后,根据 alpha 参数的值,大的偏移量也可以提供破坏功能。 然而,使用较小的偏移参数往往会更好地保留锐利特征,因为投影施泰纳点往往会投影到凸形锐利特征上。

在这里插入图片描述

图 62.6 偏移参数对输出的影响。 (左)通过在参数空间中对 NURBS CAD 模型进行网格划分而生成的输入网格。 (右)偏移量越小,样本点距离输入最近。 偏移参数从左到右递减,分别为输入边界框最长对角线的 1/50、1/200 和 1/1000。 对于所有细节级别,alpha 参数等于输入边界框最长对角线的 1/50。 较大的偏移量将产生不太复杂且三角形质量更好的输出。 然而,当偏移参数较小时,清晰的特征(红色边缘)会得到很好的保留。

在这里插入图片描述

图 62.7 斯坦纳点。 投影施泰纳点(绿色)是通过将三角形外心投影到偏移量上来计算的。 交点 Steiner 点(蓝色)被计算为 Voronoi 边缘和偏移之间的第一个交点。 (左)当偏移参数较小时,算法会产生更多的投影斯坦纳点,这往往会改善凸锐特征的保留。 (右)当偏移参数较大时,算法会产生更多的斯坦纳交点,这往往会在 3D 中生成角度质量更好的三角形。

默认情况下,我们建议将offset参数设置为alpha的一小部分,这样alpha就成为控制最终细节层次的主要参数。

下图说明了这两个参数的影响。

在这里插入图片描述

图 62.8 自行车模型上的不同 alpha 和偏移值(533,000 个三角形)。 x轴表示等于输入边界框最长对角线的1/5000、1/2000、1/500、1/200、1/50、1/20和1/5的偏移值,从左到右 正确的。 y 轴表示从下到上等于输入边界框最长对角线的 1/300、1/100、1/50、1/20 和 1/5 的 alpha 值。 每个细节级别下方的数字代表其三角形的数量。 根据 alpha 值,偏移量太小或太大将产生具有更高复杂性的输出网格。 对于每个 alpha,复杂度较低的模型可以用作从近距离到远距离的碰撞检测的尺度空间表示。

4.3 关于“双面”包裹的注意事项

偏移参数对于我们的方法至关重要,因为它保证输出是闭合的 2 流形表面网格。 事实上,即使输入是零体积结构(例如单个 3D 三角形),输出包裹也是包围所述三角形的薄体积(图 62.2)。

用户应该记住,环绕算法无法确定它是作用于无符号距离场的内部还是外部,因此在输入和 alpha 值有空洞的情况下会产生两侧环绕 小于孔的尺寸。

在这里插入图片描述

图 62.9 两侧包裹。 (左)以 2D 形式包裹兔子,并减小 alpha 值。 (右)以 3D 方式包裹充满缺陷的兔子。 最右边的一列描绘了内部的剪辑可视化。 当 alpha 相对于孔的直径足够小时,算法会生成两侧包裹。

5 性能

下图绘制了 Thingi10k 数据集上包裹算法的计算时间,以及输出三角形网格的复杂度。

在这里插入图片描述

图 62.9 Thingi10k 数据集上不同 alpha 值的执行时间和输出复杂度。 Alpha 从边界框对角线长度的 1/20 增加到 1/200。 x 轴表示输出包裹网格的复杂性(以三角形面的数量表示)。 y 轴表示总计算时间(以秒为单位)。 点的颜色和直径代表输入三角形汤中的面数,范围从 10(绿色)到 3154000(蓝色)。

6 例子

下面是一个输入三角形网格的示例,其中 alpha 设置为边界框最长对角边长度的 1/20,偏移量设置为 alpha 的 1/30(即边界框对角边长度的 1/600)。

文件 Alpha_wrap_3/triangle_mesh_wrap.cpp

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/alpha_wrap_3.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Real_timer.h>
#include <iostream>
#include <string>
namespace PMP = CGAL::Polygon_mesh_processing;
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point_3 = K::Point_3;
using Mesh = CGAL::Surface_mesh<Point_3>;
int main(int argc, char** argv)
{
  // Read the input
  const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/armadillo.off");
  std::cout << "Reading " << filename << "..." << std::endl;
  Mesh mesh;
  if(!PMP::IO::read_polygon_mesh(filename, mesh) || is_empty(mesh) || !is_triangle_mesh(mesh))
  {
    std::cerr << "Invalid input." << std::endl;
    return EXIT_FAILURE;
  }
  std::cout << "Input: " << num_vertices(mesh) << " vertices, " << num_faces(mesh) << " faces" << std::endl;
  // Compute the alpha and offset values
  const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 20.;
  const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.;
  CGAL::Bbox_3 bbox = CGAL::Polygon_mesh_processing::bbox(mesh);
  const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
                                       CGAL::square(bbox.ymax() - bbox.ymin()) +
                                       CGAL::square(bbox.zmax() - bbox.zmin()));
  const double alpha = diag_length / relative_alpha;
  const double offset = diag_length / relative_offset;
  std::cout << "alpha: " << alpha << ", offset: " << offset << std::endl;
  // Construct the wrap
  CGAL::Real_timer t;
  t.start();
  Mesh wrap;
  CGAL::alpha_wrap_3(mesh, alpha, offset, wrap);
  t.stop();
  std::cout << "Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl;
  std::cout << "Took " << t.time() << " s." << std::endl;
  // Save the result
  std::string input_name = std::string(filename);
  input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1);
  input_name = input_name.substr(0, input_name.find_last_of("."));
  std::string output_name = input_name
                            + "_" + std::to_string(static_cast<int>(relative_alpha))
                            + "_" + std::to_string(static_cast<int>(relative_offset)) + ".off";
  std::cout << "Writing to " << output_name << std::endl;
  CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17));
  return EXIT_SUCCESS;
}

由于非流形或方向不兼容,某些三角形汤可能无法表示为网格。 尽管如此,这样的三角形汤仍然是包装算法的有效输入,如下例所示。

文件 Alpha_wrap_3/triangle_soup_wrap.cpp

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/alpha_wrap_3.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/IO/polygon_soup_io.h>
#include <CGAL/Real_timer.h>
#include <array>
#include <iostream>
#include <string>
#include <vector>
namespace AW3 = CGAL::Alpha_wraps_3;
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point_3 = K::Point_3;
using Mesh = CGAL::Surface_mesh<Point_3>;
int main(int argc, char** argv)
{
  std::cout.precision(17);
  // Read the input
  const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/blobby-shuffled.off");
  std::cout << "Reading " << filename << "..." << std::endl;
  std::vector<Point_3> points;
  std::vector<std::array<std::size_t, 3> > faces;
  if(!CGAL::IO::read_polygon_soup(filename, points, faces) || faces.empty())
  {
    std::cerr << "Invalid input." << std::endl;
    return EXIT_FAILURE;
  }
  std::cout << "Input: " << points.size() << " points, " << faces.size() << " faces" << std::endl;
  // Compute the alpha and offset values
  const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 20.;
  const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 600.;
  CGAL::Bbox_3 bbox;
  for(const Point_3& p : points)
    bbox += p.bbox();
  const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
                                       CGAL::square(bbox.ymax() - bbox.ymin()) +
                                       CGAL::square(bbox.zmax() - bbox.zmin()));
  const double alpha = diag_length / relative_alpha;
  const double offset = diag_length / relative_offset;
  // Construct the wrap
  CGAL::Real_timer t;
  t.start();
  Mesh wrap;
  CGAL::alpha_wrap_3(points, faces, alpha, offset, wrap);
  t.stop();
  std::cout << "Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl;
  std::cout << "Took " << t.time() << " s." << std::endl;
  // Save the result
  std::string input_name = std::string(filename);
  input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1);
  input_name = input_name.substr(0, input_name.find_last_of("."));
  std::string output_name = input_name
                            + "_" + std::to_string(static_cast<int>(relative_alpha))
                            + "_" + std::to_string(static_cast<int>(relative_offset)) + ".off";
  std::cout << "Writing to " << output_name << std::endl;
  CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17));
  return EXIT_SUCCESS;
}

这是一个点云的示例。

文件 Alpha_wrap_3/point_set_wrap.cpp

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/alpha_wrap_3.h>
#include <CGAL/IO/read_points.h>
#include <CGAL/Real_timer.h>
#include <iostream>
#include <string>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point_3 = K::Point_3;
using Point_container = std::vector<Point_3>;
using Mesh = CGAL::Surface_mesh<Point_3>;
int main(int argc, char** argv)
{
  // Read the input
  const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("points_3/oni.pwn");
  std::cout << "Reading " << filename << "..." << std::endl;
  Point_container points;
  if(!CGAL::IO::read_points(filename, std::back_inserter(points)) || points.empty())
  {
    std::cerr << "Invalid input." << std::endl;
    return EXIT_FAILURE;
  }
  std::cout << points.size() << " points" << std::endl;
  // Compute the alpha and offset values
  const double relative_alpha = (argc > 2) ? std::stod(argv[2]) : 10.;
  const double relative_offset = (argc > 3) ? std::stod(argv[3]) : 300.;
  CGAL::Bbox_3 bbox = CGAL::bbox_3(std::cbegin(points), std::cend(points));
  const double diag_length = std::sqrt(CGAL::square(bbox.xmax() - bbox.xmin()) +
                                       CGAL::square(bbox.ymax() - bbox.ymin()) +
                                       CGAL::square(bbox.zmax() - bbox.zmin()));
  const double alpha = diag_length / relative_alpha;
  const double offset = diag_length / relative_offset;
  std::cout << "absolute alpha = " << alpha << " absolute offset = " << offset << std::endl;
  // Construct the wrap
  CGAL::Real_timer t;
  t.start();
  Mesh wrap;
  CGAL::alpha_wrap_3(points, alpha, offset, wrap);
  t.stop();
  std::cout << "Result: " << num_vertices(wrap) << " vertices, " << num_faces(wrap) << " faces" << std::endl;
  std::cout << "Took " << t.time() << " s." << std::endl;
  // Save the result
  std::string input_name = std::string(filename);
  input_name = input_name.substr(input_name.find_last_of("/") + 1, input_name.length() - 1);
  input_name = input_name.substr(0, input_name.find_last_of("."));
  std::string output_name = input_name + "_" + std::to_string(static_cast<int>(relative_alpha))
                            + "_" + std::to_string(static_cast<int>(relative_offset)) + ".off";
  std::cout << "Writing to " << output_name << std::endl;
  CGAL::IO::write_polygon_mesh(output_name, wrap, CGAL::parameters::stream_precision(17));
  return EXIT_SUCCESS;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数智赋能进行时 百望云荣获第四届长三角“金融科技领军企业”奖

近日&#xff0c;由上海金融业联合会、上海市银行同业公会、华东师范大学指导&#xff0c;《金融电子化》杂志社有限责任公司、华东师范大学长三角金融科技研究院等单位联合主办&#xff0c;上海市互联网金融行业协会等单位协办的“2023长三角金融科技节——长三角经济圈金融科…

微服务实战系列之ZooKeeper(上)

前言 历经1个多月的创作和总结&#xff0c;纵观博主微服务系列博文&#xff0c;大致脉络覆盖了以下几个方面&#xff1a; 数据方面&#xff08;缓存&安全&#xff09; 比如Redis、MemCache、Ehcache、J2cache&#xff08;两级缓存框架&#xff09;、RSA加密、Sign签名…传…

力扣22. 括号生成(java 回溯法)

Problem: 22. 括号生成 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 我们首先要知道&#xff0c;若想生成正确的括号我们需要让右括号去适配左括号&#xff0c;在此基础上我们利用回溯去解决此题目 1.题目给定n个括号&#xff0c;即当回溯决策路径长度等于 2 n 2n…

学习笔记9——JUC三种量级的锁机制

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://baobeihuijia.com/bbhj/contents/3/197325.html 多线程访问共享资源冲突 临界区&#xff1a;一段代码块存在对共享资源的多线程读写操作&#xff0c;称这段代码块为临界区 竞态条件&#xff1a;多个线程在临界…

Anaconda文件目录(打开默认路径)更改

Anaconda 文件默认目录更改 每次打开 Anaconda 都在C盘怎么办&#xff0c;如何改为D盘或是其他盘符位置&#xff1f; 可以进行下述操作。 1. 单次修改路径 单次修改路径&#xff1a;在 exe 文件(Anaconda Prompt (Anaconda_py))中写入下面代码&#xff1a; jupyter notebook …

微信小程序ec-canvas(echarts)显示地图【以甘肃省为例】

文章目录 一、效果图二、实现1、下载echarts插件2、定制图形&#xff0c;生成 echarts.min.js 文件3、小程序中使用&#xff08;1&#xff09;下载甘肃地图&#xff08;2&#xff09;使用 参考文档《微信小程序使用echarts显示全国地图》《如何在微信小程序开发中使用echarts以…

详解Keras3.0 KerasCV API: StableDiffusion image-generation model

Stable Diffusion 图像生成模型&#xff0c;可用于根据简短的文本描述&#xff08;称为“提示”&#xff09;生成图片 keras_cv.models.StableDiffusion(img_height512, img_width512, jit_compileTrue) 参数说明 img_height&#xff1a;int&#xff0c;要生成的图像的高度…

安路IP核应用举例(OSC、UART)

1.OSC(内部振荡器) 按照Project->New Project顺序新建工程后&#xff0c;后按照Tools->IP Generator顺序&#xff0c;创建IP核&#xff0c;如下图&#xff1a; 安路FPGA的内置OSC振荡模块频率可选30MHz、60MHz。 可选Verilog或VHDL语言。 如图&#xff0c;生成的.v文件只…

美国 AGU 发布 AI 应用手册,明确 6 大指导方针

爆发性的 AI 应用&#xff1a;风险与机遇并存 在空间和环境科学领域&#xff0c;AI 工具的应用越来越广泛——诸如天气预报和气候模拟&#xff0c;能源及水资源管理等等。可以说&#xff0c;我们正在经历前所未有的 AI 应用爆发&#xff0c;面对其中的机遇与风险&#xff0c;更…

DTC 故障严重程度

文章目录 简介DTC严重性 位定义DTC 类别定义参考 简介 DTCSeverityMask&#xff08;DTC严重性掩码&#xff09;/ DTCSeverity&#xff08;DTC严重性&#xff09;包含了DTC严重性和DTC类别信息。 DTCSeverityMask&#xff08;DTC严重性掩码&#xff09;&#xff0f;DTCSeverit…

找不到mfc100u.dll,程序无法继续执行?三步即可搞定

在使用电脑过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到mfc100u.dll”。mfc100u.dll是Microsoft Foundation Class&#xff08;MFC&#xff09;库中的一个版本特定的DLL文件。MFC是微软公司为简化Windows应用程序开发而提供的一套C类库。它包…

接口测试工具Postman接口测试图文教程

一、前言 在前后端分离开发时&#xff0c;后端工作人员完成系统接口开发后&#xff0c;需要与前端人员对接&#xff0c;测试调试接口&#xff0c;验证接口的正确性可用性。而这要求前端开发进度和后端进度保持基本一致&#xff0c;任何一方的进度跟不上&#xff0c;都无法及时…

牛客网BC107矩阵转置

答案&#xff1a; #include <stdio.h> int main() {int n0, m0,i0,j0,a0,b0;int arr1[10][10]{0},arr2[10][10]{0}; //第一个数组用来储存原矩阵&#xff0c;第二个数组用来储存转置矩阵scanf("%d%d",&n,&m); if((n>1&&n<10)&&am…

【最新版】PyCharm基础调试功能详解

文章目录 一、断点1. 断点的类型a. 行断点b. 异常断点 2. 设置断点a. 设置行断点b. 设置异常断点 3. 管理断点a. 删除断点b. 将断点静音 二、调试功能0. 测试代码1. 设置断点2. 调试的多种启动方式3. 观察调试控制台a. 步过b. 步入c. 单步执行代码d. 步出e. 运行到光标处f. 重新…

vivado约束方法6

生成的时钟 定时约束向导建议在的输出上创建一个生成的时钟顺序单元&#xff0c;当它直接或通过驱动其他顺序单元的时钟引脚时一些互连逻辑。与PLL或MMCM不同&#xff0c;用户逻辑不能将主时钟&#xff0c;因此向导仅提供指定除法系数的选项&#xff0c;如中所示如下图所示&am…

protobuf基础学习

部分内容出自&#xff1a;https://blog.csdn.net/baidu_32237719/article/details/99723353 proto文件来预先定义的消息格式。数据包是按照proto文件所定义的消息格式完成二进制码流的编码和解码。proto文件&#xff0c;简单地说&#xff0c;就是一个消息的协议文件&#xff0c…

Cloudflare始终使用HTTPS且带参数跳转到www的域名

文章目录 设置教程设置图跳转实测 设置教程 关闭 SSL/TLS -> 边缘证书 的 Always Use HTTPS 规则 -> 页面规则 -> URL: http://www.example.com/* 设置成始终使用HTTPS 规则 -> 页面规则 -> URL: example.com/* 设置成 转发URL301重定向到 to https://www.ex…

sql 数据类型注入+tamper

数字型 0-9 查询语句&#xff1a; $sql"select * from sy_guestbook where id$i"; 字符型 a-z 中文 标点符号 加入了单引号 查询语句&#xff1a; $sql"select * from sy_guestbook where gTpl$g"; simple order by 16--select * from sy_guestbook w…

【Spring】Spring AOP

Spring AOP AOP概述什么是AOP Spring AOP快速入门1.引入AOP依赖2. 编写AOP程序 Spring AOP 详解Spring AOP 核心概念切点(Pointcut)连接点(Join Point)通知(Advice)切面(Aspect) 通知类型PointCut切面优先级Order切点表达式execution表达式annotation自定义注解切面类 AOP原理代…

记录一下github深度学习的错误

解决办法&#xff1a;Anaconda\envs\pytorch_gpu\Lib\site-packages\visdom\server 修改run_server.py中注释掉第1917行的代码 def download_scripts_and_run(): #download_scripts() ~~~~~~~~这行 main() 替换static 获取方式&#xff1a;GitHub - littledee…