使用boost::math::barycentric_rational的C/C++测试程序

121 篇文章 ¥59.90 ¥99.00
本文介绍了C++编程中Boost库的boost::math::barycentric_rational函数,用于有理差值插值。提供了一个测试示例程序,展示如何利用该函数进行数据插值,并解释了代码的运行过程。这个功能对于科学计算和数据分析中的数据插值任务十分实用。

使用boost::math::barycentric_rational的C/C++测试程序

在C++编程中,Boost库提供了许多有用的数学函数和算法。其中之一是boost::math::barycentric_rational函数,它提供了有理差值插值的功能。本文将介绍boost::math::barycentric_rational函数的用法,并提供一个用于测试的C/C++示例程序。

首先,确保你的系统已经安装了Boost库。可以从Boost官方网站(https://www.boost.org/ ↗)下载并安装最新版本的Boost库。

下面是一个简单的示例程序,演示了如何使用boost::math::barycentric_rational函数进行差值插值:

#include <iostream>
#include 
// 标准C++库头文件 #include <iostream> // 输入输出流 #include <fstream> // 文件流操作 #include <limits> // 数值限制 #include <string> // 字符串处理 #include <filesystem> // 文件系统操作 #include <unordered_map> // 无序映射容器 #include <list> // 链表容器 #include <utility> // 工具函数 #include <vector> // 向量容器 // Assimp库头文件 - 用于3D模型导入导出 #include <assimp/Importer.hpp> // 模型导入器 #include <assimp/Exporter.hpp> // 模型导出器 #include <assimp/mesh.h> // 网格数据结构 #include <assimp/postprocess.h> // 后处理选项 #include <assimp/scene.h> // 场景数据结构 // CGAL库头文件 - 用于计算几何和网格处理 #include <CGAL/Exact_predicates_inexact_constructions_kernel.h> // 精确谓词内核 #include <CGAL/Polygon_mesh_processing/bbox.h> // 包围盒计算 #include <CGAL/Polygon_mesh_processing/connected_components.h> // 连通分量 #include <CGAL/Polygon_mesh_processing/clip.h> // 网格裁剪 #include <CGAL/Polygon_mesh_processing/corefinement.h> // 核心细化 #include <CGAL/Polygon_mesh_processing/polygon_mesh_to_polygon_soup.h> // 网格转多边形汤 #include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h> // 多边形汤转网格 #include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h> // 修复多边形汤 #include <CGAL/Polygon_mesh_processing/orientation.h> // 方向处理 #include <CGAL/Polygon_mesh_processing/remesh.h> // 重新网格化 #include <CGAL/Surface_mesh.h> // 表面网格 #include <CGAL/Surface_mesh_parameterization/parameterize.h> // 参数化 #include <CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h> // ARAP参数化器 #include <CGAL/Surface_mesh_parameterization/Mean_value_coordinates_parameterizer_3.h> // 均值坐标参数化器 #include <CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h> // LSCM参数化器 #include <CGAL/Surface_mesh_parameterization/Discrete_authalic_parameterizer_3.h> // 离散等面积参数化器 #include <CGAL/Surface_mesh_parameterization/Barycentric_mapping_parameterizer_3.h> // 重心映射参数化器 #include <CGAL/Surface_mesh_parameterization/Discrete_conformal_map_parameterizer_3.h> // 离散共形映射参数化器 #include <CGAL/Surface_mesh_parameterization/Iterative_authalic_parameterizer_3.h> // 迭代等面积参数化器 #include <CGAL/Optimal_bounding_box/oriented_bounding_box.h> // 最优包围盒 // 使用标准命名空间 using namespace std; // 文件系统命名空间别名 namespace fs = std::filesystem; // CGAL库命名空间别名,简化代码 namespace PMP = CGAL::Polygon_mesh_processing; // 多边形网格处理 namespace SMP = CGAL::Surface_mesh_parameterization; // 表面网格参数化 // 几何内核类型定义 - 使用精确谓词内核,保证数值稳定性 typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; //typedef CGAL::Simple_cartesian<double> Kernel; // 备选简单笛卡尔内核 typedef Kernel::Point_2 Point_2; // 2D点类型 typedef Kernel::Point_3 Point; // 3D点类型 typedef CGAL::Surface_mesh<Point> CGALMesh; // CGAL表面网格类型 typedef CGAL::SM_Vertex_index VertexIndex; // 顶点索引类型 //typedef CGALMesh::Property_map<VertexIndex, Kernel::Point_2> VertexUVPmap; // UV坐标属性映射 // 其他几何类型定义 typedef Kernel::Vector_3 Vector_3; // 3D向量 typedef Kernel::Plane_3 Plane_3; // 3D平面 typedef Kernel::Aff_transformation_3 Aff_transformation_3; // 仿射变换 namespace OB = CGAL::Optimal_bounding_box; // 最优包围盒命名空间 // 网格实体类型 - 包含网格、颜色和名称的元组 using MeshEntity = std::tuple<CGALMesh, aiColor3D, std::string>; /** * 剖切面访问者类 - 用于在网格裁剪过程中收集剖切面 * 这个类实现了CGAL的访问者模式,用于跟踪裁剪操作并生成剖切面 */ struct SectionVisitor { CGALMesh& section; // 存储生成的剖切面网格 std::map<CGALMesh::Vertex_index, CGALMesh::Vertex_index> vmap; // 顶点映射表,记录原网格顶点到剖切面顶点的映射 SectionVisitor(CGALMesh& sm) : section(sm) {} // 构造函数,传入剖切面网格引用 // 图论相关的类型定义,用于网格处理 typedef CGALMesh TriangleMesh; // 三角形网格类型 typedef boost::graph_traits<TriangleMesh> GT; // 图特征类型 typedef typename GT::face_descriptor face_descriptor; // 面描述符 typedef typename GT::halfedge_descriptor halfedge_descriptor; // 半边描述符 typedef typename GT::vertex_descriptor vertex_descriptor; // 顶点描述符 // face visitor functions void before_subface_creations(face_descriptor /*f_old*/, TriangleMesh&) {} void after_subface_creations(TriangleMesh&) {} void before_subface_created(TriangleMesh&) {} void after_subface_created(face_descriptor, TriangleMesh&) {} void before_face_copy(face_descriptor, const TriangleMesh&, TriangleMesh&) {} void after_face_copy(face_descriptor f_old, const TriangleMesh& tm_old, face_descriptor /*f_new*/, TriangleMesh& /*tm_new*/) { std::vector<CGALMesh::Vertex_index> vertices; for (auto v : vertices_around_face(tm_old.halfedge(f_old), tm_old)) { auto it = vmap.find(v); if (it != vmap.end()) { vertices.push_back(it->second); } } if (vertices.size() == 3) { section.add_face(vertices[0], vertices[1], vertices[2]); } } // edge visitor functions void before_edge_split(halfedge_descriptor /* h */, TriangleMesh& /* tm */) {} void edge_split(halfedge_descriptor /* hnew */, TriangleMesh& /* tm */) {} void after_edge_split() {} void add_retriangulation_edge(halfedge_descriptor /* h */, TriangleMesh& /* tm */) {} // edges added during split face retriangulation void before_edge_copy(halfedge_descriptor /*h_old*/, const TriangleMesh&, TriangleMesh&) {} void after_edge_copy(halfedge_descriptor /*h_old*/, const TriangleMesh&, halfedge_descriptor /* f_new */, TriangleMesh&) {} void before_edge_duplicated(halfedge_descriptor /*h_old*/, TriangleMesh&) {} // called before a patch border edge is duplicated void after_edge_duplicated(halfedge_descriptor /*h_old*/, halfedge_descriptor /* f_new */, TriangleMesh&) {} // called after a patch border edge is duplicated void intersection_edge_copy(halfedge_descriptor /* h_old1 */, const TriangleMesh& /* tm1 */, halfedge_descriptor /* h_old2 */, const TriangleMesh& /* tm2 */, halfedge_descriptor /* h_new */, TriangleMesh& /* tm_new */) {} // vertex visitor functions void new_vertex_added(std::size_t /*node_id*/, vertex_descriptor vh, const TriangleMesh& tm) { vmap[vh] = section.add_vertex(tm.point(vh)); } void intersection_point_detected(std::size_t /* node_id */, int /* sdim */, halfedge_descriptor /* principal_edge */, halfedge_descriptor /* additional_edge */, const TriangleMesh& /* tm1 */, const TriangleMesh& /* tm2 */, bool /* is_target_coplanar */, bool /* is_source_coplanar */) {} void before_vertex_copy(vertex_descriptor /*v_src*/, const TriangleMesh& /*tm_src*/, TriangleMesh& /*tm_tgt*/) {} void after_vertex_copy(vertex_descriptor v_src, const TriangleMesh& tm_src, vertex_descriptor /*v_tgt*/, TriangleMesh& /*tm_tgt*/) { vmap[v_src] = section.add_vertex(tm_src.point(v_src)); } // progress tracking void start_filtering_intersections() const {} void progress_filtering_intersections(double) const {} void end_filtering_intersections() const {} void start_triangulating_faces(std::size_t) const {} void triangulating_faces_step() const {} void end_triangulating_faces() const {} void start_handling_intersection_of_coplanar_faces(std::size_t) const {} void intersection_of_coplanar_faces_step() const {} void end_handling_intersection_of_coplanar_faces() const {} void start_handling_edge_face_intersections(std::size_t) const {} void edge_face_intersections_step() const {} void end_handling_edge_face_intersections() const {} void start_building_output() const {} void end_building_output() const {} // Required by Face_graph_output_builder void filter_coplanar_edges() const {} void detect_patches() const {} void classify_patches() const {} void classify_intersection_free_patches(const TriangleMesh&) const {} void out_of_place_operation(PMP::Corefinement::Boolean_operation_type) const {} void in_place_operation(PMP::Corefinement::Boolean_operation_type) const {} void in_place_operations(PMP::Corefinement::Boolean_operation_type, PMP::Corefinement::Boolean_operation_type) const {} }; /** * 读取剖切曲线文件 * @param pth 曲线文件路径 * @return 包含3D点坐标的向量 * * 功能:从文本文件中读取3D点坐标,每行包含x y z三个浮点数 * 文件格式:每行一个3D点,坐标用空格分隔 */ vector<Point> readCurve(const fs::path& pth) { std::vector<Point> curve; // 存储读取的曲线点 std::ifstream file(pth); // 打开文件流 // 检查文件是否成功打开 if (!file.is_open()) { std::cerr << "Failed to open file: " << pth << std::endl; return curve; } double x, y, z; // 临时存储坐标值 int pointCount = 0; // 记录读取的点数 // 逐行读取文件中的坐标 while (file >> x >> y >> z) { // 检查坐标值是否为NaN(非数字) if (std::isnan(x) || std::isnan(y) || std::isnan(z)) { std::cerr << "Invalid curve point: NaN detected" << std::endl; continue; // 跳过无效点 } curve.push_back(Point(x, y, z)); // 将有效点添加到曲线中 pointCount++; } file.close(); // 关闭文件 std::cout << "Read " << pointCount << " points from curve file" << std::endl; // 检查是否读取到有效点 if (curve.empty()) { std::cerr << "Warning: No valid points read from curve file!" << std::endl; } return curve; } /** * 生成剖切面方法1 - 创建垂直延伸的平面 * @param curve 输入曲线点集 * @return 生成的剖切面网格 * * 算法:将曲线上的每个点沿Z轴正负方向延伸,形成垂直的平面 * 每个曲线段生成两个三角形,形成连续的垂直平面 */ CGALMesh generateSurface1(const vector<Point>& curve) { CGALMesh surface; // 创建空网格 double extensionDistance = 5000; // 延伸距离,沿Z轴正负方向各延伸5000单位 // 检查输入曲线是否为空 if (curve.empty()) { std::cerr << "Error: Cannot generate surface from empty curve!" << std::endl; return surface; } std::cout << "Generating surface from " << curve.size() << " curve points" << std::endl; // 为每个曲线点创建两个顶点(上下延伸) for (const auto& p : curve) { Point p1(p.x(), p.y(), p.z() - extensionDistance); // 下方顶点 Point p2(p.x(), p.y(), p.z() + extensionDistance); // 上方顶点 surface.add_vertex(p1); surface.add_vertex(p2); } // 连接相邻点形成三角形面片 for (size_t i = 0; i < curve.size() - 1; ++i) { size_t baseIndex = i * 2; // 当前段的顶点起始索引 // 第一个三角形:当前下顶点 -> 下一上顶点 -> 当前上顶点 surface.add_face( CGALMesh::Vertex_index(baseIndex), CGALMesh::Vertex_index(baseIndex + 1), CGALMesh::Vertex_index(baseIndex + 2) ); // 第二个三角形:当前上顶点 -> 下一上顶点 -> 下一上顶点 surface.add_face( CGALMesh::Vertex_index(baseIndex + 1), CGALMesh::Vertex_index(baseIndex + 3), CGALMesh::Vertex_index(baseIndex + 2) ); } std::cout << "Generated surface with " << surface.number_of_vertices() << " vertices and " << surface.number_of_faces() << " faces" << std::endl; return surface; } /** * 生成剖切面方法2 - 创建更复杂的曲面结构 * @param curve 输入曲线点集 * @return 生成的剖切面网格 * * 算法:为每个曲线点创建三个顶点(下、中、上),形成更丰富的曲面结构 * 每个曲线段生成四个三角形,创建更平滑的曲面过渡 */ CGALMesh generateSurface2(const std::vector<Point>& curve) { CGALMesh surface; // 创建空网格 double extensionDistance = 4000; // 延伸距离 // 为每个曲线点创建三个顶点(下、中、上) for (const auto& p : curve) { Point p_down(p.x(), p.y(), p.z()); // 下方顶点(原始高度) Point p_original(p.x(), p.y(), p.z() + 2000); // 中间顶点(偏移2000) Point p_up(p.x(), p.y(), p.z() + extensionDistance); // 上方顶点(最大高度) surface.add_vertex(p_down); surface.add_vertex(p_original); surface.add_vertex(p_up); } // 连接相邻点形成四个三角形面片(确保逆时针顺序) for (size_t i = 0; i < curve.size() - 1; ++i) { // 当前点和下一个点的顶点索引 size_t curr_base = i * 3; // 当前段的顶点起始索引 size_t next_base = (i + 1) * 3; // 下一段的顶点起始索引 // 连接两个相邻截面形成四个三角形 // 三角形1:当前下 → 下一原 → 当前原 surface.add_face( CGALMesh::Vertex_index(curr_base), CGALMesh::Vertex_index(next_base + 1), CGALMesh::Vertex_index(curr_base + 1) ); // 三角形2:当前原 → 下一原 → 下一上 surface.add_face( CGALMesh::Vertex_index(curr_base + 1), CGALMesh::Vertex_index(next_base + 1), CGALMesh::Vertex_index(next_base + 2) ); // 三角形3:当前下 → 下一原 → 下一上 surface.add_face( CGALMesh::Vertex_index(curr_base), CGALMesh::Vertex_index(next_base + 1), CGALMesh::Vertex_index(next_base + 2) ); // 三角形4:当前下 → 下一上 → 当前上 surface.add_face( CGALMesh::Vertex_index(curr_base), CGALMesh::Vertex_index(next_base + 2), CGALMesh::Vertex_index(curr_base + 2) ); } /* 注释掉的网格修复代码 std::vector<Point> points; std::vector<std::vector<size_t>> polygons; PMP::polygon_mesh_to_polygon_soup(surface, points, polygons); PMP::repair_polygon_soup(points, polygons); PMP::orient_polygon_soup(points, polygons); if (PMP::is_polygon_soup_a_polygon_mesh(polygons)) { PMP::polygon_soup_to_polygon_mesh(points, polygons, surface); }*/ return surface; } /** * 计算网格的包围盒 * @param mesh 输入网格 * @return 网格的3D包围盒 * * 功能:遍历网格的所有顶点,计算整个网格的边界框 */ CGAL::Bbox_3 compute_mesh_bbox(const CGALMesh& mesh) { CGAL::Bbox_3 bbox; // 创建空包围盒 for (auto v : mesh.vertices()) { // 遍历所有顶点 const auto& p = mesh.point(v); // 获取顶点坐标 bbox += p.bbox(); // 将顶点添加到包围盒中 } return bbox; } /** * 计算边界环的包围盒 * @param mesh 输入网格 * @param border_halfedge 边界环的起始半边 * @return 边界环的3D包围盒 * * 功能:沿着边界环遍历所有顶点,计算边界环的边界框 */ CGAL::Bbox_3 compute_border_cycle_bbox(const CGALMesh& mesh, CGALMesh::Halfedge_index border_halfedge) { CGAL::Bbox_3 border_bbox; // 创建边界环包围盒 CGALMesh::Halfedge_index h = border_halfedge; // 从起始半边开始 // 沿着边界环遍历所有半边 do { auto v = mesh.target(h); // 获取当前半边目标顶点 const auto& p = mesh.point(v); // 获取顶点坐标 border_bbox += p.bbox(); // 将顶点添加到边界环包围盒中 h = mesh.next(h); // 移动到下一个半边 } while (h != border_halfedge); // 直到回到起始半边 return border_bbox; } /** * 判断网格是否为外边界封闭网格 * @param mesh 输入网格 * @return 如果是外边界封闭网格返回true,否则返回false * * 功能:判断网格是否只有一个大的外边界环,用于识别需要跳过的网格 * 判断标准: * 1. 网格不能是完全封闭的 * 2. 只能有一个边界环 * 3. 边界环必须足够大(至少20条边) * 4. 边界环的包围盒必须覆盖网格包围盒的90%以上 */ bool is_outer_closed_boundary(const CGALMesh& mesh) { // 如果网格完全封闭,则不是外边界 if (CGAL::is_closed(mesh)) return false; // 获取所有边界环 std::vector<CGALMesh::Halfedge_index> border_cycles; CGAL::Polygon_mesh_processing::extract_boundary_cycles(mesh, std::back_inserter(border_cycles)); // 仅允许单一边界环(排除有多个洞的网格) if (border_cycles.size() != 1) return false; // 检查边界环长度(排除小孔洞) CGALMesh::Halfedge_index border_halfedge = border_cycles.front(); std::size_t edge_count = 0; CGALMesh::Halfedge_index start = border_halfedge; do { edge_count++; border_halfedge = mesh.next(border_halfedge); } while (border_halfedge != start); // 边界环必须足够大(至少20条边) const std::size_t MIN_BORDER_EDGES = 20; if (edge_count < MIN_BORDER_EDGES) return false; // 包围盒验证 - 检查边界环是否覆盖了网格的大部分区域 const CGAL::Bbox_3 mesh_bbox = compute_mesh_bbox(mesh); // 计算整个网格的包围盒 const CGAL::Bbox_3 border_bbox = compute_border_cycle_bbox(mesh, start); // 计算边界环的包围盒 // 计算包围盒在各轴上的覆盖比例 const double x_ratio = (border_bbox.xmax() - border_bbox.xmin()) / (mesh_bbox.xmax() - mesh_bbox.xmin()); const double y_ratio = (border_bbox.ymax() - border_bbox.ymin()) / (mesh_bbox.ymax() - mesh_bbox.ymin()); const double z_ratio = (border_bbox.zmax() - border_bbox.zmin()) / (mesh_bbox.zmax() - mesh_bbox.zmin()); // 设定阈值:边界环包围盒需覆盖整体包围盒的至少90% const double BBOX_COVERAGE_THRESHOLD = 0.9; if (x_ratio < BBOX_COVERAGE_THRESHOLD || y_ratio < BBOX_COVERAGE_THRESHOLD || z_ratio < BBOX_COVERAGE_THRESHOLD) { return false; } return true; // 满足所有条件,是外边界封闭网格 } bool AssimpFormatId(const Assimp::Exporter& exporter, const fs::path& filepath, std::string& formatId, bool binary) { std::string fpstr(filepath.string()); size_t ipos = fpstr.find_last_of('.'); formatId = fpstr.substr(ipos + 1); bool findFormat = false; if (formatId == "gltf" || formatId == "glb") { findFormat = true; formatId += "2"; } else { for (size_t i = 0; i < exporter.GetExportFormatCount(); i++) { const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(i); if (strstr(desc->id, formatId.c_str()) != NULL) { findFormat = true; if (binary) { if (strstr(desc->id, "b") != NULL) { formatId = desc->id; break; } } else break; } } } return findFormat; } /** * 将Assimp网格转换为CGAL网格 * @param imh 输入的Assimp网格 * @param omh 输出的CGAL网格 * @param name 网格名称 * @return 转换成功返回true,失败返回false * * 功能:将Assimp库的网格数据结构转换为CGAL库的网格数据结构 * 包括顶点坐标和面片索引的转换,并进行网格修复和方向调整 */ static bool AiMeshToCgalMesh(const aiMesh& imh, CGALMesh& omh, std::string& name) { // 检查网格是否有效(至少3个顶点和1个面) if (imh.mNumVertices < 3 || imh.mNumFaces < 1) return false; // 获取网格名称 if (imh.mName.length > 0) { name = imh.mName.data; } // 准备顶点和面片数据 std::vector<Point> points; // 存储顶点坐标 std::vector<std::vector<size_t>> polygons; // 存储面片索引 // 转换顶点坐标 points.reserve(imh.mNumVertices); for (uint32_t i = 0; i < imh.mNumVertices; i++) { const aiVector3D& vet = imh.mVertices[i]; // 获取Assimp顶点 points.push_back(Point((double)vet.x, (double)vet.y, (double)vet.z)); // 转换为CGAL点 } // 转换面片索引 polygons.reserve(imh.mNumFaces); for (uint32_t i = 0; i < imh.mNumFaces; i++) { aiFace face = imh.mFaces[i]; // 获取Assimp面片 uint32_t* indices = face.mIndices; // 获取面片顶点索引 polygons.push_back({ (size_t)indices[0], (size_t)indices[1], (size_t)indices[2] }); // 转换为CGAL面片 } // 修复多边形汤(处理重复顶点、退化面片等) PMP::repair_polygon_soup(points, polygons); // 调整多边形汤的方向(确保面片法向量一致) PMP::orient_polygon_soup(points, polygons); // 检查是否可以转换为多边形网格 if (PMP::is_polygon_soup_a_polygon_mesh(polygons)) { PMP::polygon_soup_to_polygon_mesh(points, polygons, omh); // 转换为CGAL网格 return true; } return false; // 转换失败 } static bool CgalMeshToAiMesh(const CGALMesh& imh, const std::string& name, aiMesh& omh) { if (imh.is_empty() || imh.number_of_faces() == 0) return false; omh.mPrimitiveTypes = aiPrimitiveType_TRIANGLE; omh.mNumVertices = imh.number_of_vertices(); omh.mVertices = new aiVector3D[omh.mNumVertices]; std::unordered_map<VertexIndex, uint32_t> vimap; //! avoid order problem auto uvmap = imh.property_map<VertexIndex, Kernel::Point_2>("v:uv"); uint32_t vidx = 0; if (uvmap.second) { omh.mTextureCoords[0] = new aiVector3D[omh.mNumVertices]; omh.mNumUVComponents[0] = 2; uint32_t vidx = 0; for (const auto vd : CGAL::vertices(imh)) { const auto& point = imh.point(vd); omh.mVertices[vidx] = aiVector3D(static_cast<ai_real>(point.x()), static_cast<ai_real>(point.y()), static_cast<ai_real>(point.z())); const auto& uv = boost::get(uvmap.first,vd); omh.mTextureCoords[0][vidx] = aiVector3D(static_cast<ai_real>(uv.x()), static_cast<ai_real>(uv.y()), 0.0); vimap[vd] = vidx++; } } else { omh.mTextureCoords[0] = nullptr; omh.mNumUVComponents[0] = 0; uint32_t vidx = 0; for (const auto vd : CGAL::vertices(imh)) { const auto& point = imh.point(vd); omh.mVertices[vidx] = aiVector3D(static_cast<ai_real>(point.x()), static_cast<ai_real>(point.y()), static_cast<ai_real>(point.z())); vimap[vd] = vidx++; } } omh.mNumFaces = imh.number_of_faces(); omh.mFaces = new aiFace[omh.mNumFaces]; uint32_t fidx = 0; for (const auto& itr : imh.faces()) { CGAL::SM_Halfedge_index heidx = imh.halfedge(itr); const auto& fvids = imh.vertices_around_face(heidx); auto trif = &omh.mFaces[fidx]; trif->mIndices = new uint32_t[3]{ vimap[*fvids.begin()], vimap[*(++fvids.begin())], vimap[*(--fvids.end())] }; trif->mNumIndices = 3; fidx++; } omh.mName = name; return true; } static bool ImportMeshes(const fs::path& pth, std::vector<MeshEntity>& mshes) { Assimp::Importer ipt; uint32_t flags = aiProcess_Triangulate | aiProcess_JoinIdenticalVertices; // read file via ASSIMP const aiScene* scene = ipt.ReadFile(pth.u8string(), flags); // check for errors if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero { std::cerr << "ERROR::ASSIMP:: " << ipt.GetErrorString() << std::endl; return false; } aiColor3D clr; std::vector<aiColor3D> cvec; for (uint32_t i = 0; i < scene->mNumMaterials; i++) { auto mat = scene->mMaterials[i]; if (mat->Get(AI_MATKEY_BASE_COLOR, clr) != aiReturn_SUCCESS) { mat->Get(AI_MATKEY_COLOR_DIFFUSE, clr); } cvec.push_back(clr); } for (uint32_t i = 0; i < scene->mNumMeshes; i++) { auto imsh = scene->mMeshes[i]; CGALMesh omsh; std::string name; if (AiMeshToCgalMesh(*imsh, omsh, name)) { const auto clr = cvec[imsh->mMaterialIndex]; mshes.push_back({ std::move(omsh), clr, name }); } } return true; } static bool ExportMeshes(const fs::path& pth, const std::vector<MeshEntity>& items, bool texture) { if (items.empty()) { std::cerr << "Warning: No meshes to export!" << std::endl; return false; } std::cout << "Exporting " << items.size() << " meshes to: " << pth << std::endl; int midx = 0; std::vector<aiMesh*> meshes; std::vector<aiMaterial*> materials; for (const auto& item : items) { const auto& mesh = std::get<CGALMesh>(item); const auto& name = std::get<std::string>(item); const auto& color = std::get<aiColor3D>(item); std::cout << "Processing mesh '" << name << "' with " << mesh.number_of_vertices() << " vertices and " << mesh.number_of_faces() << " faces" << std::endl; if (mesh.is_empty()) { std::cerr << "Warning: Mesh '" << name << "' is empty, skipping" << std::endl; continue; } aiMesh* omsh = new aiMesh; if (CgalMeshToAiMesh(mesh, name, *omsh)) { aiMaterial* mat = new aiMaterial; if (texture) { mat->AddProperty(new aiString(name + ".png"), AI_MATKEY_TEXTURE_DIFFUSE(0)); } else { mat->AddProperty(new aiColor3D(color), 1, AI_MATKEY_COLOR_DIFFUSE); } mat->AddProperty(new aiString(name), AI_MATKEY_NAME); omsh->mMaterialIndex = midx; meshes.push_back(omsh); materials.push_back(mat); midx++; } else { std::cerr << "Warning: Failed to convert mesh '" << name << "' to Assimp format" << std::endl; delete omsh; } } aiScene* scene = new aiScene(); scene->mNumMeshes = (uint32_t)meshes.size(); scene->mMeshes = new aiMesh * [scene->mNumMeshes]; scene->mNumMaterials = (uint32_t)materials.size(); scene->mMaterials = new aiMaterial * [scene->mNumMaterials]; #ifdef _WIN32 memcpy_s(scene->mMeshes, scene->mNumMeshes * sizeof(aiMesh*), meshes.data(), scene->mNumMeshes * sizeof(aiMesh*)); memcpy_s(scene->mMaterials, scene->mNumMaterials * sizeof(aiMaterial*), materials.data(), scene->mNumMaterials * sizeof(aiMaterial*)); #elif defined __GNUC__ memcpy(scene->mMeshes, meshes.data(), scene->mNumMeshes * sizeof(aiMesh*)); memcpy(scene->mMaterials, materials.data(), scene->mNumMaterials * sizeof(aiMaterial*)); #endif aiNode* node = new aiNode; node->mNumMeshes = (uint32_t)meshes.size(); node->mMeshes = new uint32_t[node->mNumMeshes]; for (uint32_t i = 0; i < node->mNumMeshes; i++) { node->mMeshes[i] = i; } scene->mRootNode = node; std::string formatId; Assimp::Exporter exporter; int flags = aiProcess_GenSmoothNormals; if (!AssimpFormatId(exporter, pth, formatId, false)) return false; return exporter.Export(scene, formatId, pth.u8string(), flags) == aiReturn_SUCCESS; } static bool fnRepair(CGALMesh& mesh) { if (mesh.is_empty()) return false; CGAL::Polygon_mesh_processing::stitch_borders(mesh); std::vector<CGALMesh::Halfedge_index> borders; PMP::extract_boundary_cycles(mesh, std::back_inserter(borders)); if (!borders.empty()) { std::vector<CGALMesh::Face_index> new_faces; CGAL::Polygon_mesh_processing::triangulate_hole( mesh, borders[0], CGAL::parameters::face_output_iterator(std::back_inserter(new_faces))); } return true; } static bool SectionClipping(CGALMesh& msh, CGALMesh& clp, CGALMesh& section) { if (msh.is_empty()) { std::cerr << "Error: Input mesh is empty" << std::endl; return false; } if (clp.is_empty()) { std::cerr << "Error: Clip surface is empty" << std::endl; return false; } std::cout << "Input mesh has " << msh.number_of_vertices() << " vertices and " << msh.number_of_faces() << " faces" << std::endl; std::cout << "Clip surface has " << clp.number_of_vertices() << " vertices and " << clp.number_of_faces() << " faces" << std::endl; //auto fnRepair = [](CGALMesh& mesh) { // CGAL::Polygon_mesh_processing::stitch_borders(mesh); // std::vector<CGALMesh::Halfedge_index> borders; // //PMP::extract_boundary_cycles(mesh, std::back_inserter(borders)); // for (auto h : mesh.halfedges()) { // if (mesh.is_border(h)) borders.push_back(h); // } // if (!borders.empty()) { // std::vector<CGALMesh::Face_index> new_faces; // CGAL::Polygon_mesh_processing::triangulate_hole( // mesh, borders[0], CGAL::parameters::face_output_iterator(std::back_inserter(new_faces))); // } // }; std::vector<CGALMesh> meshes; PMP::split_connected_components(msh, meshes); std::cout << "Split into " << meshes.size() << " connected components" << std::endl; bool status = true; int processedComponents = 0; for (auto& spt : meshes) { size_t nf = spt.number_of_faces(); if (nf < 2) { std::cout << "Skipping component with only " << nf << " faces" << std::endl; continue; } bool is_out = is_outer_closed_boundary(spt); if (is_out) { std::cout << "Skipping outer boundary component" << std::endl; continue; } SectionVisitor visitor(section); if (!CGAL::is_closed(spt)) { std::cout << "Repairing non-closed component" << std::endl; fnRepair(spt); } if (!CGAL::is_closed(spt)) { std::cout << "Component still not closed after repair, skipping" << std::endl; continue; } std::cout << "Clipping component " << processedComponents++ << " with " << spt.number_of_vertices() << " vertices and " << spt.number_of_faces() << " faces" << std::endl; bool clipResult = PMP::clip(spt, clp, CGAL::parameters::clip_volume(true).visitor(visitor)); status = status && clipResult; if (!clipResult) { std::cerr << "Warning: Clip operation failed for component " << processedComponents - 1 << std::endl; } } std::cout << "Section mesh has " << section.number_of_vertices() << " vertices and " << section.number_of_faces() << " faces" << std::endl; return status; } static bool GenerateUVCoord(CGALMesh& target) { if (target.is_empty()) return false; std::vector<Point> points; std::vector<std::vector<size_t>> polygons; PMP::polygon_mesh_to_polygon_soup(target, points, polygons); PMP::repair_polygon_soup(points, polygons); PMP::orient_polygon_soup(points, polygons); if (PMP::is_polygon_soup_a_polygon_mesh(polygons)) { PMP::polygon_soup_to_polygon_mesh(points, polygons, target); } auto uvmap = target.add_property_map<VertexIndex, Kernel::Point_2> ("v:uv", Kernel::Point_2(0.0, 0.0)).first; /*CGALMesh::Halfedge_index bh; for (auto h : target.halfedges()) { if (target.is_border(h)) { bh = h; break; } }*/ std::vector<CGALMesh::Halfedge_index> bhs; PMP::extract_boundary_cycles(target, std::back_inserter(bhs)); //SMP::ARAP_parameterizer_3<CGALMesh> parameterizer; SMP::LSCM_parameterizer_3<CGALMesh> parameterizer; //SMP::Mean_value_coordinates_parameterizer_3<CGALMesh> parameterizer; //SMP::Discrete_authalic_parameterizer_3<CGALMesh> parameterizer; //SMP::Barycentric_mapping_parameterizer_3<CGALMesh> parameterizer; //SMP::Discrete_conformal_map_parameterizer_3<CGALMesh> parameterizer; //SMP::Iterative_authalic_parameterizer_3<CGALMesh> parameterizer; //const unsigned int iterations = 15; for (auto& bh : bhs) { auto ecode = SMP::parameterize(target, parameterizer, bh, uvmap); if (ecode != SMP::OK) { cout << "Parameterize failed" << ecode << endl; return false; } } aiVector2D min(static_cast<ai_real>(std::numeric_limits<double>::max())); aiVector2D max(static_cast<ai_real>(-std::numeric_limits<double>::max())); for (auto pnt : uvmap) { min.x = std::min(static_cast<ai_real>(min.x), static_cast<ai_real>(pnt.x())); min.y = std::min(static_cast<ai_real>(min.y), static_cast<ai_real>(pnt.y())); max.x = std::max(static_cast<ai_real>(max.x), static_cast<ai_real>(pnt.x())); max.y = std::max(static_cast<ai_real>(max.y), static_cast<ai_real>(pnt.y())); } auto extent = max - min; double length = std::max(static_cast<double>(extent.x), static_cast<double>(extent.y)); double step = 1.0 / length; for (const auto vd : CGAL::vertices(target)) { auto pnt = get(uvmap, vd); double u = (static_cast<double>(pnt.x()) - static_cast<double>(min.x)) * step; double v = (static_cast<double>(pnt.y()) - static_cast<double>(min.y)) * step; put(uvmap, vd, Kernel::Point_2(u - std::floor(u), v - std::floor(v))); } return true; } static bool SaveAfterClipOriMesh_UVCoord(const MeshEntity& input, CGALMesh& clp, vector<MeshEntity>& out, bool texture) { const CGALMesh& msh = get<0>(input); if (msh.is_empty()) return false; std::vector<CGALMesh> meshes; PMP::split_connected_components(msh, meshes); for (auto& spt : meshes) { size_t nf = spt.number_of_faces(); if (nf < 2) continue; bool is_out = is_outer_closed_boundary(spt); if (is_out) continue; if (!CGAL::is_closed(spt)) { fnRepair(spt); } if (!CGAL::is_closed(spt)) { continue; } PMP::clip(spt, clp, CGAL::parameters::clip_volume(false)); if (texture) { GenerateUVCoord(spt); } out.push_back({ spt, get<1>(input), get<2>(input) }); } return true; } /** * 主函数 - 3D模型剖切程序入口 * * 程序功能: * 1. 读取剖切曲线文件 * 2. 生成剖切面 * 3. 读取3D模型 * 4. 执行剖切操作 * 5. 导出剖切面和剖切后的模型 * * 命令行参数: * argv[1] - 剖切曲线文件路径 * argv[2] - 输入模型文件路径 * argv[3] - 输出剖切面文件路径 * argv[4] - 输出剖切后模型文件路径 * argv[5] - 可选参数 "withTex" 表示生成纹理坐标 */ int main(int argc, char** argv) { // 检查命令行参数数量 if (argc < 5) { std::cerr << "Usage: " << argv[0] << " <clip_line_file> <model_file> <output_section> <output_clipped> [withTex]" << std::endl; std::cerr << "参数说明:" << std::endl; std::cerr << " clip_line_file: 剖切曲线文件(包含3D点坐标)" << std::endl; std::cerr << " model_file: 输入3D模型文件" << std::endl; std::cerr << " output_section: 输出剖切面文件" << std::endl; std::cerr << " output_clipped: 输出剖切后模型文件" << std::endl; std::cerr << " withTex: 可选参数,生成纹理坐标" << std::endl; return 0; } // 解析命令行参数 fs::path ClipLine_inpath(argv[1]); // 剖切曲线文件路径 fs::path Model_inpath(argv[2]); // 输入模型文件路径 fs::path outpath1(argv[3]); // 输出剖切面文件路径 fs::path outpath2(argv[4]); // 输出剖切后模型文件路径 // 检查是否启用纹理坐标生成 bool withTex = (argc > 5 && strcmp(argv[5], "withTex") == 0); //bool withTex = true; // 可以强制启用纹理坐标生成 // 步骤1:读取剖切曲线 std::cout << "Reading clip line from: " << ClipLine_inpath << std::endl; vector<Point> curve = readCurve(ClipLine_inpath); // 检查曲线是否有效 if (curve.empty()) { std::cerr << "Error: No valid curve points found. Cannot proceed." << std::endl; return 1; } // 步骤2:生成剖切面 std::cout << "Generating clip surface..." << std::endl; CGALMesh clip = generateSurface1(curve); // 使用方法1生成剖切面 // 检查剖切面是否有效 if (clip.is_empty()) { std::cerr << "Error: Generated clip surface is empty!" << std::endl; return 1; } // 步骤3:读取输入模型 std::cout << "Reading model from: " << Model_inpath << std::endl; std::vector<MeshEntity> ims; // 存储导入的网格实体 if (!ImportMeshes(Model_inpath, ims)) { std::cerr << "Error: Failed to import model!" << std::endl; return 1; } std::cout << "Imported " << ims.size() << " mesh entities" << std::endl; // 步骤4:执行剖切操作 std::vector<MeshEntity> oms, ori; // oms存储剖切面,ori存储剖切后的模型 // 处理每个网格实体,生成剖切面 for (const auto& item : ims) { CGALMesh msh = get<0>(item); // 获取网格 CGALMesh clp = clip; // 复制剖切面 CGALMesh outm; // 输出剖切面 std::cout << "Processing mesh: " << std::get<2>(item) << std::endl; // 执行剖切操作 if (SectionClipping(msh, clp, outm)) { if (!outm.is_empty()) { std::cout << "Generated section with " << outm.number_of_vertices() << " vertices and " << outm.number_of_faces() << " faces" << std::endl; // 如果需要,生成纹理坐标 if (withTex) { GenerateUVCoord(outm); } // 将剖切面添加到输出列表 oms.push_back({ outm, std::get<1>(item), std::get<2>(item) }); } else { std::cout << "Warning: Generated section is empty" << std::endl; } } else { std::cout << "Warning: Section clipping failed" << std::endl; } } // 处理每个网格实体,生成剖切后的模型 for (const auto& item : ims) { CGALMesh clp = clip; SaveAfterClipOriMesh_UVCoord(item, clp, ori, false); } /* 注释掉的合并代码 std::vector<MeshEntity> all; all.reserve(oms.size() + ori.size()); all.insert(all.end(), oms.begin(), oms.end()); all.insert(all.end(), ori.begin(), ori.end()); */ // 步骤5:导出结果 std::cout << "Exporting " << oms.size() << " section meshes to: " << outpath1 << std::endl; ExportMeshes(outpath1, oms, withTex); std::cout << "Exporting " << ori.size() << " clipped meshes to: " << outpath2 << std::endl; ExportMeshes(outpath2, ori, false); return 1; // 程序成功完成 }
最新发布
09-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值