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 inputconst 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 valuesconst 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 wrapCGAL::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 resultstd::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 inputconst 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 valuesconst 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 wrapCGAL::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 resultstd::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 inputconst 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 valuesconst 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 wrapCGAL::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 resultstd::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;
}

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

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

相关文章

springcloudalibaba01

整合springcloud 和 springcloudalibaba&#xff0c;&#xff0c;&#xff0c; 版本对应关系 <dependencyManagement><dependencies><!--每个springcloud的工具都有一个版本每个springcloud alibaba的工具都有一个版本统一版本--> <!-- 整合…

vivado约束方法6

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

音视频参数介绍

一、视频参数概念 单个视频帧&#xff1a;可以简单地理解成为一张图片 单个视频帧主要的参数概念&#xff1a; 分辨率&#xff1a; 分辨率是指图像或显示器上像素的数量&#xff0c;通常用横向像素数乘以纵向像素数表示。例如&#xff0c;1920x1080 表示宽度为1920像素&…

linux系统命令

linux常用命令 端口相关文件目录管理文件查看文件属性日志查看系统命令防火墙相关命令 端口相关 netstat -ntpl # 查询linux系统tcp端口情况 fuser -n tcp 80 # 查询80端口是否被占用 lsof -i:<port> lsof -i:9099 | grep java lsof -i :9099 | awk {print $2, $1, $3}文…

DTC 故障严重程度

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

人工智能_机器学习066_SVM支持向量机_对偶问题转化_强对偶问题_弱对偶问题_弱对偶问题详解---人工智能工作笔记0106

上一节我们就得到了9,这个公式,这个公式要求,先去求maxL(x,lamada) ,也就是求,lamada是多少的时候,对应的, L(x,lamada) = f(x) + h(x) * lamada <=P 中的这个h(x) * lamada,最大,因为h(x)是小于0的也就是,lamada是什么的时候,h(x) * lamada最大,也就是越接近于0对吧. 然…

【Qt5】ui文件最后会变成头文件

2023年12月14日&#xff0c;周四下午 我也是今天下午偶然间发现这个的 在使用Qt的uic&#xff08;User Interface Compiler&#xff09;工具编译ui文件时&#xff0c;会生成对应的头文件。 在Qt中&#xff0c;ui文件是用于描述用户界面的XML文件&#xff0c;而头文件是用于在…

CMS—评论功能设计

一、需求分析 1.1、常见行为 1.敏感词过滤 2.新增评论&#xff08;作品下、评论下&#xff09; 3.删除评论&#xff08;作品作者、上级评论者、本级作者&#xff09; 4.上级评论删除关联下级评论 5.逻辑状态变更&#xff08;上线、下线、废弃...&#xff09; 6.上逻辑状态变更…

UG NX二次开发(C++)-库缺少需要的入口点的原因与解决方案

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、“库缺少需要的入口点”错误展示3、可能出现的原因与解决方案3.1 对于采用CTRL+U方式调用3.2 对于menu菜单下调用1、前言 在UG NX二次开发过程中,有时会遇到形形色色的bug,比如有个读…

【深度学习】机器学习概述(二)优化算法之梯度下降法(批量BGD、随机SGD、小批量)

​ 文章目录 一、基本概念二、机器学习的三要素1. 模型a. 线性模型b. 非线性模型 2. 学习准则a. 损失函数b. 风险最小化准则 3. 优化机器学习问题转化成为一个最优化问题a. 参数与超参数b. 梯度下降法梯度下降法的迭代公式具体的参数更新公式学习率的选择 c. 随机梯度下降批量…

(第5天)进阶 RHEL 7 安装单机 Oracle 19C NON-CDB 数据库

进阶 RHEL 7 安装单机 Oracle 19C NON-CDB 数据库(第5天) 真快,实战第 5 天了,我们来讲讲 19C 的数据库安装吧!19C 是未来几年 Oracle 数据库的大趋势,同样的作为长期稳定版,11GR2 在 2020 年 10 月份官方就宣布停止 Support 了,19C 将成为新的长期稳定版,并持续支持…

转载: iOS 优雅的处理网络数据

转载&#xff1a; iOS 优雅的处理网络数据 原文链接&#xff1a;https://juejin.cn/post/6952682593372340237 相信大家平时在用 App 的时候, 往往有过这样的体验&#xff0c;那就是加载网络数据等待的时间过于漫长&#xff0c;滚动浏览时伴随着卡顿&#xff0c;甚至在没有网…

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

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

JVM虚拟机系统性学习-JVM调优实战之内存溢出、高并发场景调优

调优实战-内存溢出的定位与分析 首先&#xff0c;对于以下代码如果造成内存溢出该如何进行定位呢&#xff1f;通过 jmap 与 MAT 工具进行定位分析 代码如下&#xff1a; public class TestJvmOutOfMemory {public static void main(String[] args) {List<Object> list…

Python | 高斯分布拟合示例

什么是正态分布或高斯分布&#xff1f; 当我们绘制一个数据集&#xff08;如直方图&#xff09;时&#xff0c;图表的形状就是我们所说的分布。最常见的连续值形状是钟形曲线&#xff0c;也称为高斯分布或正态分布。 它以德国数学家卡尔弗里德里希高斯的名字命名。遵循高斯分布…

算法通关村第十二关—字符串转换(青铜)

一、转换成小写字母 LeetCode709.给你一个字符串s&#xff0c;将该字符串中的大写字母转换成相同的小写字母&#xff0c;返回新的字符串。 示例1&#xff1a; 输入&#xff1a;s"Hello" 输出&#xff1a;"hello" 示例2&#xff1a; 输入&#xff1a;s&qu…

C语言——输出魔方阵

目录 一、前言&#xff1a; 二、算法设计&#xff1a; 三、代码实现&#xff1a; 五、效果展示&#xff1a; 一、前言&#xff1a; 魔方矩阵又称幻方&#xff0c;是有相同的行数和列数&#xff0c;并在每行每列、对角线上的和都相等的矩阵。魔方矩阵中的每个元素不能相同。你…

外包干了4个月,测试技术退步明显

先说一下自己的情况&#xff0c;本科生&#xff0c;20年通过校招进入杭州某软件公司&#xff0c;干了3年的功能测试&#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…

jmeter,断言:响应断言、Json断言

一、响应断言 接口A请求正常返回值如下&#xff1a; {"status": 10013, "message": "user sign timeout"} 在该接口下创建【响应断言】元件&#xff0c;配置如下&#xff1a; 若断言成功&#xff0c;则查看结果树的接口显示绿色&#xff0c;若…