diff --git a/application/test_geometric_modeling.cpp b/application/test_geometric_modeling.cpp new file mode 100644 index 0000000..6c5e391 --- /dev/null +++ b/application/test_geometric_modeling.cpp @@ -0,0 +1,2412 @@ +/** + * @file test_geometric_modeling.cpp + * @brief 几何建模系统综合测试套件 (扩展版) + * + * 覆盖范围: + * Part 1 - 独立图元建模测试 (TC-P-*) + * Part 2 - 变换操作测试 (TC-T-*) + * Part 3 - 布尔运算测试 (TC-B-*) + * Part 4 - Polyline 拉伸体扩展测试 (TC-PE-*) + * Part 5 - Helixline 拉伸体扩展测试 (TC-HE-*) + * Part 6 - 布尔运算扩展测试 (TC-BE-*) + * + * 编译方式 (示例): + * cl /std:c++17 /EHsc test_geometric_modeling.cpp -I -L -lPE + * + * 依赖: + * - solve.h : create_blobtree / bake_blobtree / create_solver / generate_polymesh / ... + * - primitive_descriptor.h : 所有 descriptor 类型 + * - math/math_defs.hpp : pi / two_pi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include <../primitive_process/interface/base/primitive.hpp> +#include "SDF_Visualize/sdf_visualizer.hpp" + +// ============================================================ +// 轻量测试框架 +// ============================================================ +namespace test_fw +{ + +struct Result { + bool passed; + std::string name; + std::string message; +}; + +static std::vector g_results; // 修复原代码中的类型错误 (原为 std::vector) +static int g_pass = 0; +static int g_fail = 0; + +void run(const std::string& name, std::function test_fn) +{ + std::cout << "[RUN ] " << name << std::endl; + try { + test_fn(); + g_results.push_back({true, name, "OK"}); + ++g_pass; + std::cout << "[PASS] " << name << "\n\n"; + } catch (const std::exception& ex) { + g_results.push_back({false, name, ex.what()}); + ++g_fail; + std::cout << "[FAIL] " << name << "\n reason: " << ex.what() << "\n\n"; + } catch (...) { + g_results.push_back({false, name, "unknown exception"}); + ++g_fail; + std::cout << "[FAIL] " << name << "\n reason: unknown exception\n\n"; + } +} + +void print_summary() +{ + std::cout << "============================\n"; + std::cout << " TOTAL : " << (g_pass + g_fail) << "\n"; + std::cout << " PASSED : " << g_pass << "\n"; + std::cout << " FAILED : " << g_fail << "\n"; + std::cout << "============================\n"; + if (g_fail > 0) { + std::cout << "\nFailed tests:\n"; + for (const auto& r : g_results) { + if (!r.passed) std::cout << " - " << r.name << " : " << r.message << "\n"; + } + } +} + +#define EXPECT_TRUE(cond) \ + do { \ + if (!(cond)) throw std::runtime_error("EXPECT_TRUE failed: " #cond " at line " + std::to_string(__LINE__)); \ + } while (0) + +#define EXPECT_NE_NULL(ptr) \ + do { \ + if ((ptr) == nullptr) \ + throw std::runtime_error("Expected non-null pointer: " #ptr " at line " + std::to_string(__LINE__)); \ + } while (0) + +#define EXPECT_NO_THROW(expr) \ + do { \ + try { \ + expr; \ + } catch (...) { \ + throw std::runtime_error("Unexpected exception in: " #expr " at line " + std::to_string(__LINE__)); \ + } \ + } while (0) + +} // namespace test_fw + +// ============================================================ +// 公共辅助函数 +// ============================================================ +static s_settings make_default_settings(int resolution = 36) +{ + s_settings s{}; + s.resolution = resolution; + s.scene_aabb_margin = 1e-5; + s.restricted_primitive_bounding_test = true; + return s; +} + +/** 单图元 → 完整流水线 */ +static auto run_pipeline_single(primitive* prim, int resolution = 72) +{ + auto rt = create_blobtree(); + blobtree_add_primitive_node(rt, prim); + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + + auto solver = create_solver(baked, make_default_settings(resolution)); + auto result = generate_polymesh(solver); + print_statistics(solver); + destroy_solver(solver); + destroy_baked_blobtree(baked); + return result; +} + +/** + * @brief 两图元布尔运算 — 含 SDF 可视化输出(原版,保留兼容) + */ +static auto run_pipeline_boolean(primitive* prim_a, primitive* prim_b, int bool_op, int resolution = 36) +{ + auto rt = create_blobtree(); + auto iter_a = blobtree_add_primitive_node(rt, prim_a); + auto iter_b = blobtree_add_primitive_node(rt, prim_b); + blobtree_add_operation_node(rt, iter_a, iter_b, static_cast(bool_op)); + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + + // std::filesystem::path base_dir = "E:\\projects\\ImplicitSurfaceNetwork-xj\\application\\SDF_Visualize"; + // std::filesystem::path xy_file_path = base_dir / "sdf_slice_xy.txt"; + // std::string sdf_xy_path = xy_file_path.string(); + // dump_sdf_slice_xy(*baked, sdf_xy_path, 0.5, 256, 0.2); + // std::cout << "Profile (XY) SDF 数据已保存到: " << sdf_xy_path << std::endl; + + auto solver = create_solver(baked, make_default_settings(resolution)); + auto result = generate_polymesh(solver); + print_statistics(solver); + destroy_solver(solver); + destroy_baked_blobtree(baked); + return result; +} + +/** + * @brief 两图元布尔运算 — 无 SDF 文件输出(扩展测试专用) + * 避免在无对应目录的环境中出现路径错误 + */ +static auto run_pipeline_boolean_clean(primitive* prim_a, primitive* prim_b, node_operation op, int resolution = 36) +{ + auto rt = create_blobtree(); + auto iter_a = blobtree_add_primitive_node(rt, prim_a); + auto iter_b = blobtree_add_primitive_node(rt, prim_b); + blobtree_add_operation_node(rt, iter_a, iter_b, op); + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + + auto solver = create_solver(baked, make_default_settings(resolution)); + auto result = generate_polymesh(solver); + print_statistics(solver); + destroy_solver(solver); + destroy_baked_blobtree(baked); + return result; +} + +// ============================================================ +// ──────────────────────────────────────────────────────────── +// Descriptor 构建辅助(避免重复样板代码) +// ──────────────────────────────────────────────────────────── + +/** + * @brief 构建一个直线轴 descriptor(两点,无弯折) + * @param from 起点 + * @param to 终点 + * @param pts 外部持有的点数组(生命周期需 >= descriptor 使用期) + * @param buls 外部持有的 bulge 数组 + * @param ref_normal 参考法向(Frenet 帧的 "up" 方向) + */ +static polyline_descriptor_t make_straight_axis(vector3d from, + vector3d to, + std::vector& pts, + std::vector& buls, + vector3d ref_normal = {1.0, 0.0, 0.0}) +{ + pts = {from, to}; + buls = {0.0}; + polyline_descriptor_t axis{}; + axis.point_number = 2; + axis.points = pts.data(); + axis.bulge_number = 1; + axis.bulges = buls.data(); + axis.reference_normal = ref_normal; + axis.is_close = false; + return axis; +} + +/** + * @brief 构建一个规则多边形 profile(凸多边形,N 边,内切圆半径 r) + * @param n 边数(≥3) + * @param r 外接圆半径 + * @param pts 外部持有的点数组 + * @param buls 外部持有的 bulge 数组(全 0 = 纯直线边) + */ +static polyline_descriptor_t make_regular_polygon_profile(int n, + double r, + std::vector& pts, + std::vector& buls) +{ + pts.clear(); + buls.clear(); + for (int i = 0; i < n; ++i) { + double angle = 2.0 * pi * i / n; + pts.push_back({r * std::cos(angle), r * std::sin(angle), 0.0}); + buls.push_back(0.0); + } + polyline_descriptor_t p{}; + p.point_number = static_cast(n); + p.points = pts.data(); + p.bulge_number = static_cast(n); + p.bulges = buls.data(); + p.reference_normal = {0.0, 0.0, 1.0}; + p.is_close = true; + return p; +} + +// ============================================================ +// ============================================================ +// PART 1 : 独立图元建模测试 (TC-P-*) [原有测试,保持不变] +// ============================================================ +// ============================================================ + +void tc_p_01_default_sphere() +{ + auto dc = create_primitive_data_center(); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + EXPECT_NE_NULL(sph); + auto result = run_pipeline_single(sph); + EXPECT_TRUE(result.success); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +void tc_p_02_default_cylinder() +{ + auto dc = create_primitive_data_center(); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + EXPECT_NE_NULL(cyl); + auto result = run_pipeline_single(cyl); + EXPECT_TRUE(result.success); + destroy_primitive(cyl); + destroy_primitive_data_center(dc); +} + +void tc_p_03_default_extrude_polyline() +{ + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +void tc_p_04_default_extrude_helixline() +{ + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +void tc_p_05_circular_profile_extrude() +{ + static std::vector circle_pts = { + {1.0, 0.0, 0.0}, + {0.0, 1.0, 0.0}, + {-1.0, 0.0, 0.0}, + {0.0, -1.0, 0.0} + }; + constexpr double b90 = 0.41421356237; + static std::vector circle_bulges = {b90, b90, b90, b90}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = circle_pts.data(); + profile.bulge_number = 4; + profile.bulges = circle_bulges.data(); + profile.reference_normal = {0, 0, 1}; + profile.is_close = true; + + static std::vector axis_pts = { + {0.0, 0.0, 0.0}, + {0.0, 0.0, 3.0} + }; + static std::vector axis_bul = {0.0}; + + polyline_descriptor_t axis{}; + axis.point_number = 2; + axis.points = axis_pts.data(); + axis.bulge_number = 1; + axis.bulges = axis_bul.data(); + axis.reference_normal = {1, 0, 0}; + axis.is_close = false; + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +void tc_p_06_triangle_profile_extrude() +{ + const double H = 0.5 * std::sqrt(3.0); + static std::vector tri_pts = { + {0.0, H * 2.0 / 3.0, 0.0}, + {-0.5, -H / 3.0, 0.0}, + {0.5, -H / 3.0, 0.0} + }; + static std::vector tri_bulges = {0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 3; + profile.points = tri_pts.data(); + profile.bulge_number = 3; + profile.bulges = tri_bulges.data(); + profile.reference_normal = {0, 0, 1}; + profile.is_close = true; + + static std::vector axis_pts = { + {0.0, 0.0, 0.0}, + {0.0, 0.0, 2.0} + }; + static std::vector axis_bul = {0.0}; + + polyline_descriptor_t axis{}; + axis.point_number = 2; + axis.points = axis_pts.data(); + axis.bulge_number = 1; + axis.bulges = axis_bul.data(); + axis.reference_normal = {1, 0, 0}; + axis.is_close = false; + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +void tc_p_07_custom_helixline_extrude() +{ + // Profile 必须定义在螺旋起始截面的 (B, N) 坐标系内。 + // 对于 radius=1.5, advance_per_round=3.0, start_direction=(1,0,0) 的右旋螺旋: + // N = -start_direction = (-1, 0, 0) + // k = radius * 2π / advance_per_round = π + // B = (0, 1/√(k²+1), -k/√(k²+1)) ≈ (0, +0.30331447, -0.95289051) + // 所以 profile 点坐标 = local_x * B + local_y * N(其中 local_x=±1, local_y=±0.430871) + static std::vector profile_pts = { + {+0.43087100, -0.30331447, +0.95289051}, // (-1, -0.430871) in (B,N) space + {+0.43087100, +0.30331447, -0.95289051}, // (+1, -0.430871) + {-0.43087100, +0.30331447, -0.95289051}, // (+1, +0.430871) + {-0.43087100, -0.30331447, +0.95289051} // (-1, +0.430871) + }; + static std::vector profile_bulges = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = profile_pts.data(); + profile.bulge_number = 4; + profile.bulges = profile_bulges.data(); + profile.reference_normal = {0, 0, 1}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 10.0}; + axis.radius = 1.5; + axis.advance_per_round = 3.0; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +void tc_p_08_degenerate_tiny_sphere() +{ + auto dc = create_primitive_data_center(); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + EXPECT_NE_NULL(sph); + primitive_apply_scale(sph, {0.001, 0.001, 0.001}); + EXPECT_NO_THROW(run_pipeline_single(sph, 16)); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +// ============================================================ +// ============================================================ +// PART 2 : 变换操作测试 (TC-T-*) [原有测试,保持不变] +// ============================================================ +// ============================================================ + +void tc_t_01_sphere_translation() +{ + auto dc = create_primitive_data_center(); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_translation(sph, {2.0, 0.0, 0.0}); + auto aabb = sph->fetch_aabb(); + auto center = aabb.center(); + EXPECT_TRUE(std::abs(center.x() - 2.0) < 1e-6); + EXPECT_TRUE(std::abs(center.y()) < 1e-6); + EXPECT_TRUE(std::abs(center.z()) < 1e-6); + auto result = run_pipeline_single(sph); + EXPECT_TRUE(result.success); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +void tc_t_02_cylinder_multi_axis_translation() +{ + auto dc = create_primitive_data_center(); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + primitive_apply_translation(cyl, {1.0, 0.0, 0.0}); + primitive_apply_translation(cyl, {0.0, -2.0, 0.0}); + primitive_apply_translation(cyl, {0.0, 0.0, 3.0}); + auto aabb = cyl->fetch_aabb(); + auto center = aabb.center(); + EXPECT_TRUE(std::abs(center.x() - 1.0) < 1e-6); + EXPECT_TRUE(std::abs(center.y() + 2.0) < 1e-6); + EXPECT_TRUE(std::abs(center.z() - 3.5) < 1e-6); + auto result = run_pipeline_single(cyl); + EXPECT_TRUE(result.success); + destroy_primitive(cyl); + destroy_primitive_data_center(dc); +} + +void tc_t_03_sphere_uniform_scale() +{ + auto dc = create_primitive_data_center(); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_scale(sph, {2.0, 2.0, 2.0}); + auto aabb = sph->fetch_aabb(); + auto sizes = aabb.sizes(); + EXPECT_TRUE(std::abs(sizes.x() - 4.0) < 1e-6); + EXPECT_TRUE(std::abs(sizes.y() - 4.0) < 1e-6); + EXPECT_TRUE(std::abs(sizes.z() - 4.0) < 1e-6); + auto result = run_pipeline_single(sph); + EXPECT_TRUE(result.success); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +void tc_t_04_cylinder_nonuniform_scale() +{ + auto dc = create_primitive_data_center(); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + primitive_apply_scale(cyl, {0.5, 1.0, 2.0}); + auto aabb = cyl->fetch_aabb(); + auto sizes = aabb.sizes(); + EXPECT_TRUE(std::abs(sizes.x() - 1.0) < 1e-5); + EXPECT_TRUE(std::abs(sizes.y() - 2.0) < 1e-5); + EXPECT_TRUE(std::abs(sizes.z() - 2.0) < 1e-5); + auto result = run_pipeline_single(cyl); + EXPECT_TRUE(result.success); + destroy_primitive(cyl); + destroy_primitive_data_center(dc); +} + +void tc_t_05_rotation_90deg_x() +{ + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + double ay = 90.0 * pi / 180.0; + primitive_apply_rotation(ext, {std::sin(ay / 2.0), 0.0, 0.0, std::cos(ay / 2.0)}); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +void tc_t_06_rotation_45deg_y() +{ + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + double ay = 45.0 * pi / 180.0; + primitive_apply_rotation(ext, {0.0, std::sin(ay / 2.0), 0.0, std::cos(ay / 2.0)}); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +void tc_t_07_combined_transform() +{ + auto dc = create_primitive_data_center(); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_scale(sph, {1.5, 2.0, 0.8}); + double az = 30.0 * pi / 180.0; + primitive_apply_rotation(sph, {0.0, 0.0, std::sin(az / 2.0), std::cos(az / 2.0)}); + primitive_apply_translation(sph, {3.0, -1.0, 0.5}); + auto aabb = sph->fetch_aabb(); + auto c = aabb.center(); + EXPECT_TRUE(std::abs(c.x() - 3.0) < 0.5); + EXPECT_TRUE(std::abs(c.y() + 1.0) < 0.5); + auto result = run_pipeline_single(sph); + EXPECT_TRUE(result.success); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +void tc_t_08_large_scale() +{ + auto dc = create_primitive_data_center(); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_scale(sph, {100.0, 100.0, 100.0}); + auto aabb = sph->fetch_aabb(); + auto sizes = aabb.sizes(); + EXPECT_TRUE(std::abs(sizes.x() - 200.0) < 1e-3); + EXPECT_NO_THROW(run_pipeline_single(sph, 24)); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +void tc_t_09_four_rotations_identity() +{ + auto dc = create_primitive_data_center(); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + auto aabb_init = cyl->fetch_aabb(); + double az = 90.0 * pi / 180.0; + for (int i = 0; i < 4; ++i) primitive_apply_rotation(cyl, {0.0, 0.0, std::sin(az / 2.0), std::cos(az / 2.0)}); + auto aabb_final = cyl->fetch_aabb(); + EXPECT_TRUE((aabb_final.min() - aabb_init.min()).norm() < 1e-5); + EXPECT_TRUE((aabb_final.max() - aabb_init.max()).norm() < 1e-5); + destroy_primitive(cyl); + destroy_primitive_data_center(dc); +} + +// ============================================================ +// ============================================================ +// PART 3 : 布尔运算测试 (TC-B-*) [原有测试,保持不变] +// ============================================================ +// ============================================================ + +void tc_b_01_sphere_sphere_union() +{ + auto dc = create_primitive_data_center(); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_translation(sph2, {1.2, 0.0, 0.0}); + auto rt = create_blobtree(); + auto it1 = blobtree_add_primitive_node(rt, sph1); + auto it2 = blobtree_add_primitive_node(rt, sph2); + blobtree_add_operation_node(rt, it1, it2, UNION_OP); + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + auto solver = create_solver(baked, make_default_settings()); + auto result = generate_polymesh(solver); + EXPECT_TRUE(result.success); + print_statistics(solver); + destroy_solver(solver); + destroy_baked_blobtree(baked); + destroy_primitive(sph1); + destroy_primitive(sph2); + destroy_primitive_data_center(dc); +} + +void tc_b_02_sphere_sphere_intersection() +{ + auto dc = create_primitive_data_center(); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_translation(sph2, {1.0, 0.0, 0.0}); + auto result = run_pipeline_boolean(sph1, sph2, INTERSECTION_OP); + EXPECT_TRUE(result.success); + destroy_primitive(sph1); + destroy_primitive(sph2); + destroy_primitive_data_center(dc); +} + +void tc_b_03_sphere_sphere_difference() +{ + auto dc = create_primitive_data_center(); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_scale(sph1, {2.0, 2.0, 2.0}); + primitive_apply_translation(sph2, {1.2, 0.0, 0.0}); + auto result = run_pipeline_boolean(sph1, sph2, DIFFERENCE_OP); + EXPECT_TRUE(result.success); + destroy_primitive(sph1); + destroy_primitive(sph2); + destroy_primitive_data_center(dc); +} + +void tc_b_04_sphere_minus_cylinder_drill() +{ + auto dc = create_primitive_data_center(); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + primitive_apply_scale(cyl, {0.4, 0.4, 4.0}); + double ax = 90.0 * pi / 180.0; + primitive_apply_rotation(cyl, {std::sin(ax / 2.0), 0.0, 0.0, std::cos(ax / 2.0)}); + primitive_apply_translation(cyl, {0.0, 1.5, -0.2}); + auto result = run_pipeline_boolean(sph, cyl, DIFFERENCE_OP); + EXPECT_TRUE(result.success); + destroy_primitive(sph); + destroy_primitive(cyl); + destroy_primitive_data_center(dc); +} + +void tc_b_05_three_sphere_union_chain() +{ + auto dc = create_primitive_data_center(); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph3 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_translation(sph2, {1.5, 0.0, 0.0}); + primitive_apply_translation(sph3, {3.0, 0.0, 0.0}); + auto rt = create_blobtree(); + auto it1 = blobtree_add_primitive_node(rt, sph1); + auto it2 = blobtree_add_primitive_node(rt, sph2); + auto it3 = blobtree_add_primitive_node(rt, sph3); + auto op1 = blobtree_add_operation_node(rt, it1, it2, UNION_OP); + blobtree_add_operation_node(rt, op1, it3, UNION_OP); + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + auto solver = create_solver(baked, make_default_settings()); + auto result = generate_polymesh(solver); + EXPECT_TRUE(result.success); + print_statistics(solver); + destroy_solver(solver); + destroy_baked_blobtree(baked); + destroy_primitive(sph1); + destroy_primitive(sph2); + destroy_primitive(sph3); + destroy_primitive_data_center(dc); +} + +void tc_b_06_cylinder_cylinder_intersection() +{ + auto dc = create_primitive_data_center(); + auto cyl1 = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + auto cyl2 = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + primitive_apply_translation(cyl1, {0.0, 0.0, -0.5}); + double ax = 90.0 * pi / 180.0; + primitive_apply_rotation(cyl2, {std::sin(ax / 2.0), 0.0, 0.0, std::cos(ax / 2.0)}); + auto result = run_pipeline_boolean(cyl1, cyl2, INTERSECTION_OP); + EXPECT_TRUE(result.success); + destroy_primitive(cyl1); + destroy_primitive(cyl2); + destroy_primitive_data_center(dc); +} + +void tc_b_07_extrude_union_sphere() +{ + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_scale(sph, {0.8, 0.8, 0.8}); + primitive_apply_translation(sph, {0.8, 2.0, 0.2}); + auto result = run_pipeline_boolean(ext, sph, UNION_OP); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +void tc_b_08_disjoint_spheres_intersection() +{ + auto dc = create_primitive_data_center(); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_translation(sph2, {10.0, 0.0, 0.0}); + EXPECT_NO_THROW(run_pipeline_boolean(sph1, sph2, INTERSECTION_OP)); + destroy_primitive(sph1); + destroy_primitive(sph2); + destroy_primitive_data_center(dc); +} + +void tc_b_09_contained_sphere_difference() +{ + auto dc = create_primitive_data_center(); + auto small = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto big = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + primitive_apply_scale(big, {3.0, 3.0, 3.0}); + EXPECT_NO_THROW(run_pipeline_boolean(small, big, DIFFERENCE_OP)); + destroy_primitive(small); + destroy_primitive(big); + destroy_primitive_data_center(dc); +} + +void tc_b_10_complex_four_primitive_tree() +{ + auto dc = create_primitive_data_center(); + auto cyl1 = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto cyl2 = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + primitive_apply_translation(cyl1, {1.0, 0.0, 0.0}); + primitive_apply_translation(sph1, {1.0, 0.0, 0.0}); + primitive_apply_scale(sph2, {0.5, 1.0, 1.5}); + // primitive_apply_translation(cyl2, {-1.0, 0.0, 0.0}); + primitive_apply_translation(cyl2, {-1.0, 0.0, 0.1}); + + auto rt = create_blobtree(); + auto it_c1 = blobtree_add_primitive_node(rt, cyl1); + auto it_s1 = blobtree_add_primitive_node(rt, sph1); + auto it_s2 = blobtree_add_primitive_node(rt, sph2); + auto it_c2 = blobtree_add_primitive_node(rt, cyl2); + auto op1 = blobtree_add_operation_node(rt, it_c1, it_s1, INTERSECTION_OP); + auto op2 = blobtree_add_operation_node(rt, op1, it_s2, UNION_OP); + blobtree_add_operation_node(rt, it_c2, op2, DIFFERENCE_OP); + + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + EXPECT_NO_THROW({ + auto solver = create_solver(baked, make_default_settings()); + auto result = generate_polymesh(solver); + EXPECT_TRUE(result.success); + print_statistics(solver); + destroy_solver(solver); + }); + destroy_baked_blobtree(baked); + destroy_primitive(cyl1); + destroy_primitive(sph1); + destroy_primitive(sph2); + destroy_primitive(cyl2); + destroy_primitive_data_center(dc); +} + +// ============================================================ +// ============================================================ +// PART 4 : Polyline 拉伸体扩展测试 (TC-PE-*) +// ============================================================ +// ============================================================ + +// ────────────────────────────────────────────────────────────── +// TC-PE-01 : 五边形截面 + 沿 Z 轴拉伸(凸多边形) +// 目标 : 验证奇数多边形(5 边)的凸轮廓能正确建模 +// 输入 : 正五边形 (r=0.8), 直线轴 (0,0,0)→(0,0,2) +// 预期 : 网格生成正常;结果为五棱柱 +// ────────────────────────────────────────────────────────────── +void tc_pe_01_pentagon_profile_straight_z_axis() +{ + static std::vector pts; + static std::vector buls; + auto profile = make_regular_polygon_profile(5, 0.8, pts, buls); + + static std::vector axis_pts; + static std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, {0.0, 0.0, 2.0}, axis_pts, axis_buls, {1.0, 0.0, 0.0}); + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + EXPECT_TRUE(result.mesh.num_vertices > 0); + EXPECT_TRUE(result.mesh.num_faces > 0); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-02 : 六边形截面 + 斜向轴(非坐标轴方向) +// 目标 : 验证轴方向不对齐坐标轴时的拉伸建模 +// 输入 : 正六边形截面; 轴 (0,0,0)→(1,1,1) 对角方向 +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_pe_02_hexagon_profile_diagonal_axis() +{ + static std::vector pts; + static std::vector buls; + auto profile = make_regular_polygon_profile(6, 0.6, pts, buls); + + // [已修复] 原始轴终点 {1.5, 1.5, 1.5} 的 y=1.5≠0,违反 polyline extrude axis 必须在 ZX 平面内的约束。 + // 修复:将终点改为 {1.5, 0.0, 1.5}(去除 y 分量),轴方向变为 (1,0,1)。 + // 同步将 reference_normal 更新为 {0,1,0},以确保其与新轴方向 (1,0,1) 正交。 + static std::vector axis_pts; + static std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, + {1.5, 0.0, 1.5}, + axis_pts, + axis_buls, + {0.0, 1.0, 0.0}); // 轴在 ZX 平面内,参考法向 Y 轴 + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-03 : L 形凹多边形截面 + 短轴 +// 目标 : 验证凹(非凸)截面能正确建模(6 顶点 L 形) +// 输入 : L 形截面; 轴 (0,0,0)→(0,0,1.5) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_pe_03_L_shape_concave_profile() +{ + // 顶点顺序 (CCW 看 -Z 方向): + // (0,0) → (2,0) → (2,1) → (1,1) → (1,2) → (0,2) + static std::vector pts = { + {0.0, 0.0, 0.0}, + {2.0, 0.0, 0.0}, + {2.0, 1.0, 0.0}, + {1.0, 1.0, 0.0}, + {1.0, 2.0, 0.0}, + {0.0, 2.0, 0.0} + }; + static std::vector buls = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 6; + profile.points = pts.data(); + profile.bulge_number = 6; + profile.bulges = buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + static std::vector axis_pts; + static std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, {0.0, 0.0, 1.5}, axis_pts, axis_buls, {1.0, 0.0, 0.0}); + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-04 : 含圆弧边(正 bulge)的截面 + 直线轴 (扇形截面) +// 目标 : 验证正 bulge(凸弧)截面可正确建模 +// 输入 : 3 顶点扇形截面, 每段均为 60° 圆弧 (bulge = tan(π/12) ≈ 0.268) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_pe_04_arc_positive_bulge_profile() +{ + const double r = 0.8; + const double b60 = std::tan(pi / 12.0); // tan(15°),对应 60° 弧 + + // 正三角形的三个顶点 + static std::vector pts = { + {r, 0.0, 0.0}, + {r * std::cos(2.0 * pi / 3.0), r * std::sin(2.0 * pi / 3.0), 0.0}, + {r * std::cos(4.0 * pi / 3.0), r * std::sin(4.0 * pi / 3.0), 0.0} + }; + static std::vector buls = {b60, b60, b60}; // 全正 bulge → 凸弧 + + polyline_descriptor_t profile{}; + profile.point_number = 3; + profile.points = pts.data(); + profile.bulge_number = 3; + profile.bulges = buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + static std::vector axis_pts; + static std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, {0.0, 0.0, 2.0}, axis_pts, axis_buls, {1.0, 0.0, 0.0}); + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-05 : 含负 bulge 截面(内凹弧)+ 直线轴 +// 目标 : 验证负 bulge(凹弧)截面可正确建模(类似齿轮齿槽轮廓) +// 输入 : 正方形轮廓, 其中两条边有负 bulge (-0.3) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_pe_05_arc_negative_bulge_profile() +{ + static std::vector pts = { + {-0.7, -0.7, 0.0}, + {0.7, -0.7, 0.0}, + {0.7, 0.7, 0.0}, + {-0.7, 0.7, 0.0} + }; + static std::vector buls = {-0.3, 0.0, -0.3, 0.0}; // 两边内凹弧 + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = pts.data(); + profile.bulge_number = 4; + profile.bulges = buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + static std::vector axis_pts; + static std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, {0.0, 0.0, 1.8}, axis_pts, axis_buls, {1.0, 0.0, 0.0}); + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-06 : 含锐角顶点的截面 + 直线轴(剪刀形轮廓) +// 目标 : 验证截面包含约 30° 锐角顶点时系统稳健性 +// 输入 : 细长菱形 (纵横比约 6:1), 顶角约 18° +// 预期 : 不崩溃;网格生成(可能退化) +// ────────────────────────────────────────────────────────────── +void tc_pe_06_sharp_acute_angle_profile() +{ + // 细长菱形:顶角约 20° + static std::vector pts = { + {0.0, 0.9, 0.0}, // 顶点 (极尖) + {-0.15, 0.0, 0.0}, // 右侧宽点 + {0.0, -0.9, 0.0}, // 底顶点 (极尖) + {0.15, 0.0, 0.0} // 左侧宽点 + }; + static std::vector buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = pts.data(); + profile.bulge_number = 4; + profile.bulges = buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + static std::vector axis_pts; + static std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, {0.0, 0.0, 2.0}, axis_pts, axis_buls, {1.0, 0.0, 0.0}); + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + EXPECT_NO_THROW(run_pipeline_single(ext, 24)); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-07 : 矩形截面 + L 形折线轴(单次 90° 弯折) +// 目标 : 验证折线轴(单弯折)的建模 +// 输入 : 小矩形截面 (0.3×0.4); 轴: (0,0,0)→(2,0,0)→(2,0,2) +// 预期 : 网格生成正常;结果为 L 形管状体 +// ────────────────────────────────────────────────────────────── +void tc_pe_07_rectangular_profile_L_shape_axis() +{ + // 小矩形截面(宽 0.6, 高 0.8) + static std::vector prof_pts = { + {-0.3, -0.4, 0.0}, + {0.3, -0.4, 0.0}, + {0.3, 0.4, 0.0}, + {-0.3, 0.4, 0.0} + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + // L 形轴:沿 X 轴走 2 个单位,再沿 Z 轴走 2 个单位 + static std::vector axis_pts = { + {0.0, 0.0, 0.0}, + {2.0, 0.0, 0.0}, + {2.0, 0.0, 2.0} + }; + static std::vector axis_buls = {0.0, 0.0}; // 直线段 + + polyline_descriptor_t axis{}; + axis.point_number = 3; + axis.points = axis_pts.data(); + axis.bulge_number = 2; + axis.bulges = axis_buls.data(); + axis.reference_normal = {0.0, 1.0, 0.0}; // Y 轴作参考法向 + axis.is_close = false; + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); // ERROR !! + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-08 : 圆截面 + Z 形折线轴(多次弯折) +// 目标 : 验证轴包含多个弯折点时建模的稳健性 +// 输入 : 圆形截面 (r=0.25); 轴: Z 形 (5 点,4 段) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_pe_08_circular_profile_Z_shape_axis() +{ + // 圆形截面(4 段 90° 圆弧近似) + const double b90 = 0.41421356237; + static std::vector prof_pts = { + {0.25, 0.0, 0.0}, + {0.0, 0.25, 0.0}, + {-0.25, 0.0, 0.0}, + {0.0, -0.25, 0.0} + }; + static std::vector prof_buls = {b90, b90, b90, b90}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + // Z 形轴:(0,0,0)→(1,0,0)→(1,0,1)→(2,0,1)→(2,0,2) + static std::vector axis_pts = { + {0.0, 0.0, 0.0}, + {1.0, 0.0, 0.0}, + {1.0, 0.0, 1.0}, + {2.0, 0.0, 1.0}, + {2.0, 0.0, 2.0} + }; + static std::vector axis_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t axis{}; + axis.point_number = 5; + axis.points = axis_pts.data(); + axis.bulge_number = 4; + axis.bulges = axis_buls.data(); + axis.reference_normal = {0.0, 1.0, 0.0}; + axis.is_close = false; + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-09 : 矩形截面 + 弧线轴(轴本身有 bulge) +// 目标 : 验证当拉伸路径为曲线(非折线)时的建模 +// 输入 : 小矩形截面; 轴: 单段弧线 (bulge=0.5, 近似 90° 弧) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_pe_09_rectangular_profile_arc_axis() +{ + static std::vector prof_pts = { + {-0.2, -0.2, 0.0}, + {0.2, -0.2, 0.0}, + {0.2, 0.2, 0.0}, + {-0.2, 0.2, 0.0} + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + // 弧线轴: 从 (0,0,0) 到 (2,0,2),bulge=0.5 产生圆弧路径 + static std::vector axis_pts = { + {0.0, 0.0, 0.0}, + {2.0, 0.0, 2.0} + }; + static std::vector axis_buls = {0.5}; + + polyline_descriptor_t axis{}; + axis.point_number = 2; + axis.points = axis_pts.data(); + axis.bulge_number = 1; + axis.bulges = axis_buls.data(); + axis.reference_normal = {0.0, 1.0, 0.0}; + axis.is_close = false; + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-10 : 参数化测试 — 不同边数凸多边形 (N=3..8) +// 目标 : 系统性验证各种凸多边形截面 (三角形到八边形) +// 预期 : 所有情况下网格生成正常 +// ────────────────────────────────────────────────────────────── +void tc_pe_10_parametric_polygon_n_sides() +{ + for (int n = 3; n <= 8; ++n) { + std::vector pts; + std::vector buls; + auto profile = make_regular_polygon_profile(n, 0.7, pts, buls); + + std::vector axis_pts; + std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, {0.0, 0.0, 1.5}, axis_pts, axis_buls, {1.0, 0.0, 0.0}); + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + ext->update_geometry(static_cast(&desc), 0); + + std::string sub_name = "TC-PE-10 N=" + std::to_string(n) + " 边形"; + auto result = run_pipeline_single(ext, 24); + if (!result.success) { + destroy_primitive(ext); + destroy_primitive_data_center(dc); + throw std::runtime_error(sub_name + " mesh generation failed"); + } + destroy_primitive(ext); + destroy_primitive_data_center(dc); + } +} + +// ────────────────────────────────────────────────────────────── +// TC-PE-11 : 高纵横比拉伸体(细长管) +// 目标 : 验证极端细长几何(长轴/截面尺寸 >> 1)下的数值稳定性 +// 输入 : 小圆形截面 (r=0.1); 轴长度 = 10.0 +// 预期 : 不崩溃;网格生成成功 +// ────────────────────────────────────────────────────────────── +void tc_pe_11_high_aspect_ratio_extrusion() +{ + const double b90 = 0.41421356237; + static std::vector prof_pts = { + {0.1, 0.0, 0.0}, + {0.0, 0.1, 0.0}, + {-0.1, 0.0, 0.0}, + {0.0, -0.1, 0.0} + }; + static std::vector prof_buls = {b90, b90, b90, b90}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + static std::vector axis_pts; + static std::vector axis_buls; + auto axis = make_straight_axis({0.0, 0.0, 0.0}, {0.0, 0.0, 10.0}, axis_pts, axis_buls, {1.0, 0.0, 0.0}); + + extrude_polyline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + EXPECT_NO_THROW(run_pipeline_single(ext, 24)); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ============================================================ +// ============================================================ +// PART 5 : Helixline 拉伸体扩展测试 (TC-HE-*) +// ============================================================ +// ============================================================ + +// ────────────────────────────────────────────────────────────── +// TC-HE-01 : 单圈螺旋(360°)+ 方形截面 +// 目标 : 验证恰好一圈螺旋(total angle = 2π)的建模 +// 输入 : 方形截面 (0.2×0.2); radius=1.0, advance_per_round=1.0 +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_he_01_single_turn_square_profile() +{ + // Profile 必须定义在螺旋起始截面的 (B, N) 坐标系内,使截面与切向量 T 垂直。 + // 参数: radius=1.0, advance_per_round=1.0 → k = 2π, L = √(k²+1) ≈ 6.3623 + // N = (-1, 0, 0) (指向螺旋轴心的内法向) + // B = (0, +0.15717673, -0.98757049) + // profile 点 = local_x * B + local_y * N,local_(x,y) ∈ {±0.1} + static std::vector prof_pts = { + {+0.10000000, -0.01571767, +0.09875705}, // (-0.1, -0.1) in (B,N) space + {+0.10000000, +0.01571767, -0.09875705}, // (+0.1, -0.1) + {-0.10000000, +0.01571767, -0.09875705}, // (+0.1, +0.1) + {-0.10000000, -0.01571767, +0.09875705} // (-0.1, +0.1) + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 1.0}; // 高度 = advance_per_round × 1圈 = 1.0 + axis.radius = 1.0; + axis.advance_per_round = 1.0; // 每圈上升 1.0 → 1 圈后到 axis_end + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-02 : 三圈螺旋(1080°)+ 方形截面 +// 目标 : 验证多圈螺旋(3 turns)的建模完整性 +// 输入 : 方形截面 (0.15×0.15); radius=0.8, 3 圈 +// 预期 : 网格正常生成;顶点数相比单圈显著增多 +// ────────────────────────────────────────────────────────────── +void tc_he_02_three_turns_square_profile() +{ + // radius=0.8, advance_per_round=1.0 → k = 1.6π, L ≈ 5.1249 + // B = (0, +0.19511986, -0.98077940), N = (-1, 0, 0) + // profile 点 = local_x * B + local_y * N,local_(x,y) ∈ {±0.075} + static std::vector prof_pts = { + {+0.07500000, -0.01463399, +0.07355846}, // (-0.075, -0.075) + {+0.07500000, +0.01463399, -0.07355846}, // (+0.075, -0.075) + {-0.07500000, +0.01463399, -0.07355846}, // (+0.075, +0.075) + {-0.07500000, -0.01463399, +0.07355846} // (-0.075, +0.075) + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 3.0}; // 3 圈 × advance 1.0 = 3.0 + axis.radius = 0.8; + axis.advance_per_round = 1.0; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-03 : 左旋螺旋 (is_righthanded = false) +// 目标 : 验证左手螺旋与右手螺旋均可正确建模 +// 输入 : 方形截面; radius=1.0, 2 圈, 左旋 +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_he_03_left_handed_helix() +{ + // radius=1.0, advance_per_round=1.0 → 与 TC-HE-01 相同的 k=2π + // 左旋不改变 B/N(start_direction 未变),截面坐标系相同。 + // B = (0, +0.15717673, -0.98757049), N = (-1, 0, 0) + // profile 点 = local_x * B + local_y * N,local_(x,y) ∈ {±0.12} + static std::vector prof_pts = { + {+0.12000000, -0.01886121, +0.11850846}, // (-0.12, -0.12) + {+0.12000000, +0.01886121, -0.11850846}, // (+0.12, -0.12) + {-0.12000000, +0.01886121, -0.11850846}, // (+0.12, +0.12) + {-0.12000000, -0.01886121, +0.11850846} // (-0.12, +0.12) + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 2.0}; + axis.radius = 1.0; + axis.advance_per_round = 1.0; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = false; // ← 左旋 + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-04 : 密排螺旋(小节距) +// 目标 : 验证 advance_per_round 极小(接近截面尺寸)时的建模 +// 输入 : 方形截面 (0.1×0.1); advance_per_round=0.13 (节距仅略大于截面) +// 预期 : 不崩溃;网格生成(可能有数值挑战) +// ────────────────────────────────────────────────────────────── +void tc_he_04_tight_pitch_helix() +{ + // radius=0.6, advance_per_round=0.12 → k = 10π ≈ 31.4159, L ≈ 31.432 + // B = (0, +0.03181488, -0.99949378), N = (-1, 0, 0) + // profile 点 = local_x * B + local_y * N,local_(x,y) ∈ {±0.05} + static std::vector prof_pts = { + {+0.05000000, -0.00159074, +0.04997469}, // (-0.05, -0.05) + {+0.05000000, +0.00159074, -0.04997469}, // (+0.05, -0.05) + {-0.05000000, +0.00159074, -0.04997469}, // (+0.05, +0.05) + {-0.05000000, -0.00159074, +0.04997469} // (-0.05, +0.05) + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 0.65}; // 5 圈,节距极小 + axis.radius = 0.6; + axis.advance_per_round = 0.13; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + EXPECT_NO_THROW(run_pipeline_single(ext, 30)); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-05 : 大节距螺旋(稀疏螺旋) +// 目标 : 验证 advance_per_round 很大(弹簧感)时的建模 +// 输入 : 方形截面 (0.2×0.2); advance_per_round=3.0 (每圈上升 3 倍直径) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_he_05_large_pitch_helix() +{ + // radius=1.0, advance_per_round=3.0 → k = 2π/3 ≈ 2.0944, L ≈ 2.3228 + // B = (0, +0.43087077, -0.90241364), N = (-1, 0, 0) + // profile 点 = local_x * B + local_y * N,local_(x,y) ∈ {±0.1} + static std::vector prof_pts = { + {+0.10000000, -0.04308708, +0.09024136}, // (-0.1, -0.1) + {+0.10000000, +0.04308708, -0.09024136}, // (+0.1, -0.1) + {-0.10000000, +0.04308708, -0.09024136}, // (+0.1, +0.1) + {-0.10000000, -0.04308708, +0.09024136} // (-0.1, +0.1) + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 6.0}; // 2 圈, advance=3.0 + axis.radius = 1.0; + axis.advance_per_round = 3.0; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-06 : 螺旋线 + 圆形截面(圆管弹簧) +// 目标 : 验证圆形截面(bulge 近似)在螺旋路径上的建模 +// 输入 : 4 点圆弧近似截面 (r=0.15); 2 圈螺旋 +// 预期 : 网格正常生成;外形类似弹簧 +// ────────────────────────────────────────────────────────────── +void tc_he_06_circular_profile_helix_spring() +{ + // radius=0.9, advance_per_round=1.0 → k = 1.8π ≈ 5.6549, L ≈ 5.7434 + // B = (0, +0.17413698, -0.98472144), N = (-1, 0, 0) + // 圆形截面 4 点(外接圆 r=0.15),依次对应 2D 方向 +B, +N, -B, -N + // 点坐标 = local_x * B + local_y * N + const double b90 = 0.41421356237; + static std::vector prof_pts = { + {+0.00000000, +0.02612055, -0.14770822}, // ( 0.15, 0 ) in (B,N) + {-0.15000000, +0.00000000, 0.00000000 }, // ( 0, 0.15) + {-0.00000000, -0.02612055, +0.14770822}, // (-0.15, 0 ) + {+0.15000000, +0.00000000, 0.00000000 } // ( 0, -0.15) + }; + static std::vector prof_buls = {b90, b90, b90, b90}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 2.0}; + axis.radius = 0.9; + axis.advance_per_round = 1.0; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-07 : 螺旋线 + 三角形截面 +// 目标 : 验证非矩形截面(三角形)在螺旋路径上的建模 +// 输入 : 等边三角形截面 (边长 0.25); 2 圈螺旋 +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_he_07_triangular_profile_helix() +{ + // radius=0.8, advance_per_round=1.0 → k = 1.6π ≈ 5.0265, L ≈ 5.1249 + // B = (0, +0.19511986, -0.98077940), N = (-1, 0, 0) + // 等边三角形(外接圆 r3=0.15),顶点方向 0°、120°、240° in (B,N) space + // 点坐标 = local_x * B + local_y * N + const double r3 = 0.15; + static std::vector prof_pts = { + {+0.00000000, +0.02926798, -0.14711691}, // (r3, 0 ) in (B,N) + {-0.12990381, -0.01463399, +0.07355846}, // r3*(cos120°, sin120°) + {+0.12990381, -0.01463399, +0.07355846} // r3*(cos240°, sin240°) + }; + static std::vector prof_buls = {0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 3; + profile.points = prof_pts.data(); + profile.bulge_number = 3; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 2.0}; + axis.radius = 0.8; + axis.advance_per_round = 1.0; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-08 : 大半径螺旋(轴从大半径切入) +// 目标 : 验证螺旋半径相对截面尺寸很大时的几何建模 +// 输入 : 小方形截面 (0.1×0.1); radius=3.0 (远大于截面) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_he_08_large_radius_helix() +{ + // radius=3.0, advance_per_round=1.0 → k = 6π ≈ 18.850, L ≈ 18.877 + // B = (0, +0.05297715, -0.99859572), N = (-1, 0, 0) + // profile 点 = local_x * B + local_y * N,local_(x,y) ∈ {±0.05} + static std::vector prof_pts = { + {+0.05000000, -0.00264886, +0.04992979}, // (-0.05, -0.05) + {+0.05000000, +0.00264886, -0.04992979}, // (+0.05, -0.05) + {-0.05000000, +0.00264886, -0.04992979}, // (+0.05, +0.05) + {-0.05000000, -0.00264886, +0.04992979} // (-0.05, +0.05) + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, 2.0}; + axis.radius = 3.0; + axis.advance_per_round = 1.0; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + ext->update_geometry(static_cast(&desc), 0); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-09 : 参数化测试 — 不同圈数 (0.5, 1, 2, 4 圈) +// 目标 : 系统性验证各种圈数下的螺旋建模 +// 预期 : 所有情况下不崩溃;网格生成成功 +// ────────────────────────────────────────────────────────────── +void tc_he_09_parametric_turns() +{ + // 各测试: {advance_per_round, height} 决定圈数 = height / advance_per_round + const std::vector> cases = { + {2.0, 1.0}, // 0.5 圈 + {1.0, 1.0}, // 1 圈 + {1.0, 2.0}, // 2 圈 + {1.0, 4.0}, // 4 圈 + }; + + for (auto& [adv, h] : cases) { + // Profile 点必须在螺旋起始截面的 (B,N) 坐标系内。 + // radius=0.8, start_direction=(1,0,0) → N=(-1,0,0) + // k = radius * 2π / adv, L = √(k²+1) + // B = (0, 1/L, -k/L) + // 对于 adv=2.0: k≈2.5133, B≈(0,+0.36970,-0.92915) + // 对于 adv=1.0: k≈5.0265, B≈(0,+0.19512,-0.98078) + const double r_helix = 0.8; + const double k = r_helix * 2.0 * pi / adv; + const double L = std::sqrt(k * k + 1.0); + // B = (0, 1/L, -k/L), N = (-1, 0, 0) + // point = local_x * B + local_y * N, (local_x, local_y) ∈ {±0.1} + const double By = 1.0 / L, Bz = -k / L; + std::vector prof_pts = { + {+0.1, -0.1 * By, +0.1 * Bz}, // (-0.1, -0.1) in (B,N) space → local_y→x=-(-0.1)=+0.1 + {+0.1, +0.1 * By, -0.1 * Bz}, // (+0.1, -0.1) + {-0.1, +0.1 * By, -0.1 * Bz}, // (+0.1, +0.1) + {-0.1, -0.1 * By, +0.1 * Bz} // (-0.1, +0.1) + }; + std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile{}; + profile.point_number = 4; + profile.points = prof_pts.data(); + profile.bulge_number = 4; + profile.bulges = prof_buls.data(); + profile.reference_normal = {0.0, 0.0, 1.0}; + profile.is_close = true; + + helixline_descriptor_t axis{}; + axis.axis_start = {0.0, 0.0, 0.0}; + axis.axis_end = {0.0, 0.0, h}; + axis.radius = 0.8; + axis.advance_per_round = adv; + axis.start_direction = {1.0, 0.0, 0.0}; + axis.is_righthanded = true; + + extrude_helixline_descriptor_t desc{}; + desc.profile_number = 1; + desc.profiles = &profile; + desc.axis = axis; + + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + + std::cout << "descriptor: turns=" << h / adv << ", adv=" << adv << ", height=" << h << std::endl; + std::cout << "profile points: \np1:" << desc.profiles[0].points[0].x << ", " << desc.profiles[0].points[0].y << ", " + << desc.profiles[0].points[0].z << "\np2:" << desc.profiles[0].points[1].x << ", " + << desc.profiles[0].points[1].y << ", " << desc.profiles[0].points[1].z + << "\np3:" << desc.profiles[0].points[2].x << ", " << desc.profiles[0].points[2].y << ", " + << desc.profiles[0].points[2].z << "\np4:" << desc.profiles[0].points[3].x << ", " + << desc.profiles[0].points[3].y << ", " << desc.profiles[0].points[3].z << std::endl; + + ext->update_geometry(static_cast(&desc), 0); + + double turns = h / adv; + auto result = run_pipeline_single(ext, 28); + if (!result.success) { + destroy_primitive(ext); + destroy_primitive_data_center(dc); + throw std::runtime_error("TC-HE-09 failed for turns=" + std::to_string(turns)); + } + destroy_primitive(ext); + destroy_primitive_data_center(dc); + } +} + +// ────────────────────────────────────────────────────────────── +// TC-HE-10 : 螺旋轴经过缩放变换后的建模 +// 目标 : 验证在 primitive 级别施加缩放后螺旋体的建模 +// 输入 : 默认螺旋体 (default extrude_helixline); scale (2, 2, 1) +// 预期 : 变换后网格仍然正常生成 +// ────────────────────────────────────────────────────────────── +void tc_he_10_helix_with_scale_transform() +{ + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + EXPECT_NE_NULL(ext); + + primitive_apply_scale(ext, {2.0, 2.0, 1.0}); + auto result = run_pipeline_single(ext); + EXPECT_TRUE(result.success); + + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ============================================================ +// ============================================================ +// PART 6 : 布尔运算扩展测试 (TC-BE-*) +// ============================================================ +// ============================================================ + +// ────────────────────────────────────────────────────────────── +// TC-BE-01 : 两 Polyline 拉伸体并集(并排管道) +// 目标 : 验证两个相同类型拉伸体的 UNION 运算 +// 输入 : ext_A @ (0,0,0), ext_B @ (2.5,0,0),部分重叠 +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_be_01_two_extrude_polyline_union() +{ + auto dc = create_primitive_data_center(); + auto ext_a = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_b = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + + primitive_apply_translation(ext_b, {2.5, 0.0, 0.0}); + + auto result = run_pipeline_boolean_clean(ext_a, ext_b, UNION_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-02 : 两 Polyline 拉伸体交集(正交截取) +// 目标 : 验证两拉伸体交集(INTERSECTION)运算 +// 输入 : ext_A 沿 Z 轴; ext_B 绕 Y 轴旋转 90° (沿 X 轴), 均在 origin 附近 +// 预期 : 网格生成正常(结果为截面处的交叠体) +// ────────────────────────────────────────────────────────────── +void tc_be_02_two_extrude_polyline_intersection() +{ + auto dc = create_primitive_data_center(); + auto ext_a = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_b = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + + // ext_b 绕 Y 轴旋转 90° 使其方向改变 + const double ay = 90.0 * pi / 180.0; + primitive_apply_rotation(ext_b, {0.0, std::sin(ay / 2.0), 0.0, std::cos(ay / 2.0)}); + + auto result = run_pipeline_boolean_clean(ext_a, ext_b, INTERSECTION_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-03 : Polyline 拉伸体减去拉伸体(槽口切割) +// 目标 : 大拉伸体减去小拉伸体,结果为开槽形状 +// 输入 : ext_A (大,默认); ext_B (小,缩放 0.3×0.3×2.5) 穿过 ext_A +// 预期 : 网格正常生成;结果包含开槽特征 +// ────────────────────────────────────────────────────────────── +void tc_be_03_extrude_difference_slot() +{ + auto dc = create_primitive_data_center(); + auto ext_a = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_b = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + + // ext_b 缩小成一个细长体穿入 ext_a + primitive_apply_scale(ext_b, {0.3, 0.3, 2.5}); + primitive_apply_translation(ext_b, {0.5, 0.5, 0.0}); + + auto result = run_pipeline_boolean_clean(ext_a, ext_b, DIFFERENCE_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-04 : 螺旋线拉伸体并集球体(混合类型组合) +// 目标 : 不同图元类型(helixline + sphere)的 UNION +// 输入 : helixline ext @ origin; sphere (r=0.5) @ (0,0,3)(螺旋末端) +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_be_04_helixline_extrude_union_sphere() +{ + auto dc = create_primitive_data_center(); + auto hel = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + + primitive_apply_scale(sph, {0.5, 0.5, 0.5}); + primitive_apply_translation(sph, {0.0, 0.0, 1.5}); + + auto result = run_pipeline_boolean_clean(hel, sph, UNION_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(hel); + destroy_primitive(sph); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-05 : 圆柱减去螺旋线拉伸体(螺纹切割仿真) +// 目标 : 模拟在圆柱上切割螺纹的操作(cylinder - helix extrude) +// 输入 : 圆柱 (r=1, h=3); 螺旋拉伸体 (小方截面, 嵌入圆柱表面) +// 预期 : 网格正常生成;结果为带螺纹槽的圆柱 +// ────────────────────────────────────────────────────────────── +void tc_be_05_cylinder_minus_helixline_thread() +{ + auto dc = create_primitive_data_center(); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + auto hel = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + + // 将圆柱拉伸到合适高度 + primitive_apply_scale(cyl, {2.0, 2.0, 3.0}); + // 螺旋体配置已在 initialize 中为默认值(半径约 1.5, 2 圈等) + // 将螺旋轴心对齐圆柱中心 + primitive_apply_translation(hel, {0.0, 0.0, 0.0}); + + auto result = run_pipeline_boolean_clean(cyl, hel, DIFFERENCE_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(cyl); + destroy_primitive(hel); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-06 : 三个 Polyline 拉伸体链式并集 (A ∪ B) ∪ C +// 目标 : 验证三个拉伸体的多级布尔树 +// 输入 : 三个拉伸体分别沿 X 轴排列,部分重叠 +// 预期 : 网格正常生成;结果为三段相连的管道 +// ────────────────────────────────────────────────────────────── +void tc_be_06_three_extrude_polyline_chain_union() +{ + auto dc = create_primitive_data_center(); + auto ext_a = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_b = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_c = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + + primitive_apply_translation(ext_b, {3.0, 0.0, 0.0}); + primitive_apply_translation(ext_c, {6.0, 0.0, 0.0}); + + auto rt = create_blobtree(); + auto it_a = blobtree_add_primitive_node(rt, ext_a); + auto it_b = blobtree_add_primitive_node(rt, ext_b); + auto it_c = blobtree_add_primitive_node(rt, ext_c); + auto op_ab = blobtree_add_operation_node(rt, it_a, it_b, UNION_OP); + blobtree_add_operation_node(rt, op_ab, it_c, UNION_OP); + + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + + auto solver = create_solver(baked, make_default_settings()); + auto result = generate_polymesh(solver); + EXPECT_TRUE(result.success); + print_statistics(solver); + destroy_solver(solver); + destroy_baked_blobtree(baked); + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive(ext_c); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-07 : 布尔链(先并集后差集): (Poly ∪ Sphere) - Cylinder +// 目标 : 验证先 UNION 再 DIFFERENCE 的多步布尔链 +// 预期 : 网格正常生成;三图元参与 +// ────────────────────────────────────────────────────────────── +void tc_be_07_boolean_chain_union_then_difference() +{ + auto dc = create_primitive_data_center(); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto sph = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + + // ext @ origin; sph @ (2, 0, 0) (部分与 ext 重叠) + primitive_apply_translation(sph, {2.0, 0.0, 0.0}); + // cyl 作为钻孔: 沿 X 轴旋转后穿过结合体 + primitive_apply_scale(cyl, {0.5, 0.5, 4.0}); + const double ax = 90.0 * pi / 180.0; + primitive_apply_rotation(cyl, {std::sin(ax / 2.0), 0.0, 0.0, std::cos(ax / 2.0)}); + primitive_apply_translation(cyl, {1.0, 0.0, 0.0}); + + auto rt = create_blobtree(); + auto it_e = blobtree_add_primitive_node(rt, ext); + auto it_s = blobtree_add_primitive_node(rt, sph); + auto it_c = blobtree_add_primitive_node(rt, cyl); + auto op_es = blobtree_add_operation_node(rt, it_e, it_s, UNION_OP); + blobtree_add_operation_node(rt, op_es, it_c, DIFFERENCE_OP); + + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + auto solver = create_solver(baked, make_default_settings()); + auto result = generate_polymesh(solver); + EXPECT_TRUE(result.success); + print_statistics(solver); + destroy_solver(solver); + destroy_baked_blobtree(baked); + destroy_primitive(ext); + destroy_primitive(sph); + destroy_primitive(cyl); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-08 : 多类型混合并集(圆柱 + Polyline 拉伸体) +// 目标 : 验证不同类型图元的混合 UNION +// 输入 : cylinder 沿 Z 轴; extrude_polyline 旋转 90° 与之正交 +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_be_08_cylinder_union_extrude_polyline() +{ + auto dc = create_primitive_data_center(); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + auto ext = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + + // 默认圆柱沿 Z; ext 旋转使其沿 X 轴延伸 + const double ay = 90.0 * pi / 180.0; + primitive_apply_rotation(ext, {0.0, std::sin(ay / 2.0), 0.0, std::cos(ay / 2.0)}); + primitive_apply_translation(ext, {0.0, 0.0, 0.5}); // 居中到圆柱高度中部 + + auto result = run_pipeline_boolean_clean(cyl, ext, UNION_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(cyl); + destroy_primitive(ext); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-09 : 相切球(边界接触情形) +// 目标 : 验证两球恰好外切(表面相切、内部不重叠)时的 UNION +// 输入 : sph_A (r=1) @ (0,0,0); sph_B (r=1) @ (2,0,0)(恰好相切) +// 预期 : 不崩溃;网格生成(结果含切点特征) +// ────────────────────────────────────────────────────────────── +void tc_be_09_tangent_spheres_boundary() +{ + auto dc = create_primitive_data_center(); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + + primitive_apply_translation(sph2, {2.0, 0.0, 0.0}); // 恰好外切 + + EXPECT_NO_THROW(run_pipeline_boolean_clean(sph1, sph2, UNION_OP)); + + destroy_primitive(sph1); + destroy_primitive(sph2); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-10 : 薄壁交集(近重合面的交集) +// 目标 : 验证两个拉伸体仅轻微重叠(薄层交集)时系统稳健性 +// 输入 : ext_A (2×2 截面); ext_B 偏移 (1.95,0,0)(两者仅 0.05 重叠) +// 预期 : 不崩溃;网格生成(结果为极薄片) +// ────────────────────────────────────────────────────────────── +void tc_be_10_thin_wall_intersection() +{ + auto dc = create_primitive_data_center(); + auto ext_a = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_b = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + + // 默认拉伸体 x 范围约 [-2,2],平移 3.95 后两者仅 0.05 重叠 + primitive_apply_translation(ext_b, {3.95, 0.0, 0.0}); + + EXPECT_NO_THROW(run_pipeline_boolean_clean(ext_a, ext_b, INTERSECTION_OP)); + + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-11 : 极端尺寸差异布尔运算(大比例 vs. 小比例) +// 目标 : 验证图元尺寸相差悬殊(100×)时的布尔运算稳健性 +// 输入 : sph_big (scale×5) @ origin; sph_small (scale×0.3) @ (3,0,0) +// sph_small 完全在 sph_big 外部(分离)—— UNION 应产生两个独立体 +// 预期 : 不崩溃;网格生成成功 +// ────────────────────────────────────────────────────────────── +void tc_be_11_extreme_size_ratio_union() +{ + auto dc = create_primitive_data_center(); + auto big = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto small = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + + primitive_apply_scale(big, {5.0, 5.0, 5.0}); + primitive_apply_scale(small, {0.5, 0.5, 0.5}); + primitive_apply_translation(small, {6.0, 0.0, 0.0}); // 在大球外侧 + + EXPECT_NO_THROW(run_pipeline_boolean_clean(big, small, UNION_OP)); + + destroy_primitive(big); + destroy_primitive(small); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-12 : 完全包含差集(大球 - 小球 = 挖空球壳) +// 目标 : 验证 A 完全包含 B,A - B 的结果为球壳 +// 输入 : big (r=2) @ origin; small (r=0.8) @ origin(同心) +// 预期 : 网格正常生成;顶点/面数 > 0(球壳结构) +// ────────────────────────────────────────────────────────────── +void tc_be_12_concentric_sphere_shell() +{ + auto dc = create_primitive_data_center(); + auto big = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto sml = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + + primitive_apply_scale(big, {2.0, 2.0, 2.0}); + primitive_apply_scale(sml, {0.8, 0.8, 0.8}); + + auto result = run_pipeline_boolean_clean(big, sml, DIFFERENCE_OP); + EXPECT_TRUE(result.success); + EXPECT_TRUE(result.mesh.num_vertices > 0); + EXPECT_TRUE(result.mesh.num_faces > 0); + + destroy_primitive(big); + destroy_primitive(sml); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-13 : 螺旋线与圆柱交集(螺纹嵌合仿真) +// 目标 : 模拟螺纹嵌合(helix extrude ∩ cylinder)的几何建模 +// 输入 : helixline ext 与一个同轴圆柱做交集 +// 预期 : 网格正常生成;结果为嵌合在圆柱表面的螺旋带 +// ────────────────────────────────────────────────────────────── +void tc_be_13_helixline_intersection_cylinder() +{ + auto dc = create_primitive_data_center(); + auto hel = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + + // 圆柱沿 Z 轴,高度与螺旋线高度相近,半径略大于螺旋半径 + primitive_apply_scale(cyl, {2.0, 2.0, 3.0}); // 半径 2, 高度 3 + + auto result = run_pipeline_boolean_clean(hel, cyl, INTERSECTION_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(hel); + destroy_primitive(cyl); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-14 : 复杂 5 图元混合布尔树 +// 目标 : 验证包含多种图元类型的深层布尔树 (depth=3) +// 运算 : ((ext_poly ∪ sphere) ∩ cylinder) - (helix ∪ sphere2) +// 预期 : 不崩溃;网格生成正常 +// ────────────────────────────────────────────────────────────── +void tc_be_14_complex_five_primitive_tree() +{ + auto dc = create_primitive_data_center(); + auto ext_p = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto sph1 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + auto cyl = create_primitive(dc, PRIMITIVE_TYPE_CYLINDER); + auto hel = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); + auto sph2 = create_primitive(dc, PRIMITIVE_TYPE_SPHERE); + + // 对各图元施加适当变换使其在空间上产生交叠 + primitive_apply_scale(sph1, {1.5, 1.5, 1.5}); + primitive_apply_scale(cyl, {2.5, 2.5, 2.5}); + primitive_apply_scale(sph2, {0.5, 0.5, 0.5}); + primitive_apply_translation(sph2, {1.0, 0.0, 1.0}); + + auto rt = create_blobtree(); + auto it_ep = blobtree_add_primitive_node(rt, ext_p); + auto it_s1 = blobtree_add_primitive_node(rt, sph1); + auto it_c = blobtree_add_primitive_node(rt, cyl); + auto it_h = blobtree_add_primitive_node(rt, hel); + auto it_s2 = blobtree_add_primitive_node(rt, sph2); + + // (ext_p ∪ sph1) + auto op_union1 = blobtree_add_operation_node(rt, it_ep, it_s1, UNION_OP); + // ∩ cylinder + auto op_isect = blobtree_add_operation_node(rt, op_union1, it_c, INTERSECTION_OP); + // (hel ∪ sph2) + auto op_union2 = blobtree_add_operation_node(rt, it_h, it_s2, UNION_OP); + // - (hel ∪ sph2) + blobtree_add_operation_node(rt, op_isect, op_union2, DIFFERENCE_OP); + + auto baked = bake_blobtree(rt); + destroy_blobtree(rt); + + EXPECT_NO_THROW({ + auto solver = create_solver(baked, make_default_settings(28)); + auto result = generate_polymesh(solver); + EXPECT_TRUE(result.success); + print_statistics(solver); + destroy_solver(solver); + }); + + destroy_baked_blobtree(baked); + destroy_primitive(ext_p); + destroy_primitive(sph1); + destroy_primitive(cyl); + destroy_primitive(hel); + destroy_primitive(sph2); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-15 : 拉伸体与自身并集(幂等性验证) +// 目标 : 两个完全相同的拉伸体做 UNION,结果与单图元相同 +// 输入 : 两个完全相同的默认 extrude_polyline +// 预期 : 网格正常生成;不崩溃 +// ────────────────────────────────────────────────────────────── +void tc_be_15_identical_primitives_union() +{ + auto dc = create_primitive_data_center(); + auto ext_a = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_b = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + // 不做任何变换,两图元完全重合 + + EXPECT_NO_THROW(run_pipeline_boolean_clean(ext_a, ext_b, UNION_OP)); + + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive_data_center(dc); +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-16 : 随机参数压力测试(Polyline Extrude 布尔运算) +// 目标 : 以不同随机偏移对两个 extrude_polyline 做所有三种布尔运算 +// 验证在随机参数下系统不崩溃 +// 运算 : 分别测试 UNION / INTERSECTION / DIFFERENCE +// 预期 : 三种运算均不抛出异常 +// ────────────────────────────────────────────────────────────── +void tc_be_16_random_offset_all_boolean_ops() +{ + // 固定种子确保可复现性 + std::mt19937 rng(42); + std::uniform_real_distribution<> dist_offset(-1.5, 1.5); + std::uniform_real_distribution<> dist_scale(0.5, 2.0); + + const std::vector ops = {UNION_OP, INTERSECTION_OP, DIFFERENCE_OP}; + const std::vector opname = {"UNION", "INTERSECTION", "DIFFERENCE"}; + + for (size_t op_idx = 0; op_idx < ops.size(); ++op_idx) { + auto dc = create_primitive_data_center(); + auto ext_a = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + auto ext_b = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_POLYLINE); + + vector3d offset = {dist_offset(rng), dist_offset(rng), dist_offset(rng)}; + vector3d scale_b = {dist_scale(rng), dist_scale(rng), dist_scale(rng)}; + + primitive_apply_scale(ext_b, scale_b); + primitive_apply_translation(ext_b, offset); + + try { + run_pipeline_boolean_clean(ext_a, ext_b, ops[op_idx], 24); + } catch (const std::exception& e) { + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive_data_center(dc); + throw std::runtime_error(std::string("TC-BE-16 ") + opname[op_idx] + " failed: " + e.what()); + } + + destroy_primitive(ext_a); + destroy_primitive(ext_b); + destroy_primitive_data_center(dc); + } +} + +// ────────────────────────────────────────────────────────────── +// TC-BE-17 : 螺旋线拉伸体差集(两个螺旋线,交叉抵消) +// 目标 : 两个螺旋线拉伸体(一右旋一左旋)做差集 +// 预期 : 网格正常生成 +// ────────────────────────────────────────────────────────────── +void tc_be_17_two_helixlines_difference() +{ + auto dc = create_primitive_data_center(); + auto hel_rh = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); // 右旋 (默认) + auto hel_lh = create_primitive(dc, PRIMITIVE_TYPE_EXTRUDE_HELIXLINE); // 左旋 (通过自定义 desc) + + // 构建左旋螺旋描述符 + static std::vector prof_pts = { + {-0.1, -0.1, 0.0}, + {0.1, -0.1, 0.0}, + {0.1, 0.1, 0.0}, + {-0.1, 0.1, 0.0} + }; + static std::vector prof_buls = {0.0, 0.0, 0.0, 0.0}; + + polyline_descriptor_t profile_lh{}; + profile_lh.point_number = 4; + profile_lh.points = prof_pts.data(); + profile_lh.bulge_number = 4; + profile_lh.bulges = prof_buls.data(); + profile_lh.reference_normal = {0.0, 0.0, 1.0}; + profile_lh.is_close = true; + + helixline_descriptor_t axis_lh{}; + axis_lh.axis_start = {0.0, 0.0, 0.0}; + axis_lh.axis_end = {0.0, 0.0, 2.0}; + axis_lh.radius = 1.5; + axis_lh.advance_per_round = 0.5; + axis_lh.start_direction = {1.0, 0.0, 0.0}; + axis_lh.is_righthanded = false; // ← 左旋 + + extrude_helixline_descriptor_t desc_lh{}; + desc_lh.profile_number = 1; + desc_lh.profiles = &profile_lh; + desc_lh.axis = axis_lh; + hel_lh->update_geometry(static_cast(&desc_lh), 0); + + auto result = run_pipeline_boolean_clean(hel_rh, hel_lh, DIFFERENCE_OP); + EXPECT_TRUE(result.success); + + destroy_primitive(hel_rh); + destroy_primitive(hel_lh); + destroy_primitive_data_center(dc); +} + +// ============================================================ +// 主函数 : 注册并执行所有测试 +// ============================================================ +int main() +{ + using namespace test_fw; + + std::cout << "====================================================\n"; + std::cout << " 几何建模系统 · 综合测试套件 (扩展版)\n"; + std::cout << "====================================================\n\n"; + + //// ---- Part 1: 独立图元建模 ---- + // std::cout << "--- Part 1: 独立图元建模测试 ---\n\n"; + // run("TC-P-01 默认单位球", tc_p_01_default_sphere); + // run("TC-P-02 默认单位圆柱", tc_p_02_default_cylinder); + // run("TC-P-03 默认Polyline拉伸体", tc_p_03_default_extrude_polyline); + // run("TC-P-04 默认Helixline拉伸体", tc_p_04_default_extrude_helixline); // 自相交 + // run("TC-P-05 圆形截面Polyline拉伸体", tc_p_05_circular_profile_extrude); + // run("TC-P-06 三角形截面Polyline拉伸体", tc_p_06_triangle_profile_extrude); + // run("TC-P-07 自定义螺旋线拉伸体", tc_p_07_custom_helixline_extrude); // 自相交 + // run("TC-P-08 极端情况: 极小球", tc_p_08_degenerate_tiny_sphere); + + //// ---- Part 2: 变换操作 ---- + // std::cout << "--- Part 2: 变换操作测试 ---\n\n"; + // run("TC-T-01 球体平移", tc_t_01_sphere_translation); + // run("TC-T-02 圆柱多轴平移", tc_t_02_cylinder_multi_axis_translation); + // run("TC-T-03 球体均匀缩放", tc_t_03_sphere_uniform_scale); + // run("TC-T-04 圆柱非均匀缩放", tc_t_04_cylinder_nonuniform_scale); + run("TC-T-05 绕X轴旋转90°", tc_t_05_rotation_90deg_x); + run("TC-T-06 绕Y轴旋转45°", tc_t_06_rotation_45deg_y); + //run("TC-T-07 组合变换(缩放→旋转→平移)", tc_t_07_combined_transform); + // run("TC-T-08 极端缩放(×100)", tc_t_08_large_scale); + //run("TC-T-09 四次90°旋转=恒等变换", tc_t_09_four_rotations_identity); + + //// ---- Part 3: 布尔运算 (原有) ---- + // std::cout << "--- Part 3: 布尔运算测试 (基础) ---\n\n"; + // run("TC-B-01 球-球 并集(重叠)", tc_b_01_sphere_sphere_union); + // run("TC-B-02 球-球 交集(部分重叠)", tc_b_02_sphere_sphere_intersection); + // run("TC-B-03 球-球 差集", tc_b_03_sphere_sphere_difference); + // run("TC-B-04 球-圆柱 差集(钻孔)", tc_b_04_sphere_minus_cylinder_drill); + // run("TC-B-05 三球链式并集 (A∪B)∪C", tc_b_05_three_sphere_union_chain); + // run("TC-B-06 正交圆柱-圆柱 交集", tc_b_06_cylinder_cylinder_intersection); + //run("TC-B-07 拉伸体∪球(混合类型)", tc_b_07_extrude_union_sphere); + // run("TC-B-08 不相交球 交集(空集边界)", tc_b_08_disjoint_spheres_intersection); + // run("TC-B-09 包含关系球 差集(空集边界)", tc_b_09_contained_sphere_difference); + // run("TC-B-10 复杂四图元混合布尔树", tc_b_10_complex_four_primitive_tree); + + //// ---- Part 4: Polyline 拉伸体扩展 ---- + // std::cout << "--- Part 4: Polyline 拉伸体扩展测试 ---\n\n"; + //run("TC-PE-01 五边形截面+直线Z轴", tc_pe_01_pentagon_profile_straight_z_axis); + // run("TC-PE-02 六边形截面+斜向轴", tc_pe_02_hexagon_profile_diagonal_axis); + // run("TC-PE-03 L形凹多边形截面", tc_pe_03_L_shape_concave_profile); + // run("TC-PE-04 正bulge圆弧截面(扇形)", tc_pe_04_arc_positive_bulge_profile); + // run("TC-PE-05 负bulge圆弧截面(内凹弧)", tc_pe_05_arc_negative_bulge_profile); + // run("TC-PE-06 锐角截面(细长菱形)", tc_pe_06_sharp_acute_angle_profile); + // run("TC-PE-07 矩形截面+L形折线轴", tc_pe_07_rectangular_profile_L_shape_axis); + // run("TC-PE-08 圆截面+Z形折线轴(多弯折)", tc_pe_08_circular_profile_Z_shape_axis); + // run("TC-PE-09 矩形截面+弧线轴", tc_pe_09_rectangular_profile_arc_axis); + // run("TC-PE-10 参数化多边形截面(N=3~8)", tc_pe_10_parametric_polygon_n_sides); + // run("TC-PE-11 高纵横比细长拉伸体", tc_pe_11_high_aspect_ratio_extrusion); + + //// ---- Part 5: Helixline 拉伸体扩展 ---- // ERROR + // std::cout << "--- Part 5: Helixline 拉伸体扩展测试 ---\n\n"; + //run("TC-HE-01 单圈螺旋+方形截面", tc_he_01_single_turn_square_profile); + // run("TC-HE-02 三圈螺旋+方形截面", tc_he_02_three_turns_square_profile); + // run("TC-HE-03 左旋螺旋", tc_he_03_left_handed_helix); + // run("TC-HE-04 密排螺旋(小节距)", tc_he_04_tight_pitch_helix); + // run("TC-HE-05 稀疏螺旋(大节距)", tc_he_05_large_pitch_helix); + // run("TC-HE-06 圆形截面螺旋弹簧", tc_he_06_circular_profile_helix_spring); + // run("TC-HE-07 三角形截面螺旋", tc_he_07_triangular_profile_helix); + // run("TC-HE-08 大半径螺旋", tc_he_08_large_radius_helix); + // run("TC-HE-09 参数化圈数(0.5/1/2/4圈)", tc_he_09_parametric_turns); // error + //run("TC-HE-10 螺旋体+缩放变换", tc_he_10_helix_with_scale_transform); + + //// ---- Part 6: 布尔运算扩展 ---- + // std::cout << "--- Part 6: 布尔运算扩展测试 ---\n\n"; + // run("TC-BE-01 两Polyline拉伸体 UNION", tc_be_01_two_extrude_polyline_union); // 自相交 + //run("TC-BE-02 两Polyline拉伸体 INTERSECTION", tc_be_02_two_extrude_polyline_intersection); + // run("TC-BE-03 拉伸体差集(槽口切割)", tc_be_03_extrude_difference_slot); + //run("TC-BE-04 螺旋拉伸体∪球(混合类型)", tc_be_04_helixline_extrude_union_sphere); + //run("TC-BE-05 圆柱-螺旋拉伸体(螺纹切割)", tc_be_05_cylinder_minus_helixline_thread); // error? + // run("TC-BE-06 三拉伸体链式并集(A∪B)∪C", tc_be_06_three_extrude_polyline_chain_union); + // run("TC-BE-07 布尔链 (Poly∪Sphere)-Cyl", tc_be_07_boolean_chain_union_then_difference); + // run("TC-BE-08 圆柱∪Polyline拉伸体(正交混合)", tc_be_08_cylinder_union_extrude_polyline); + // run("TC-BE-09 相切球(边界接触) UNION", tc_be_09_tangent_spheres_boundary); + // run("TC-BE-10 薄壁交集(近重合面)", tc_be_10_thin_wall_intersection); + //run("TC-BE-11 极端尺寸差异 UNION", tc_be_11_extreme_size_ratio_union); + // run("TC-BE-12 同心球壳(完全包含差集)", tc_be_12_concentric_sphere_shell); + //run("TC-BE-13 螺旋∩圆柱(螺纹嵌合)", tc_be_13_helixline_intersection_cylinder); // error? + // run("TC-BE-14 复杂5图元混合布尔树", tc_be_14_complex_five_primitive_tree); // error? + // run("TC-BE-15 完全重合图元 UNION(幂等性)", tc_be_15_identical_primitives_union); + // run("TC-BE-16 随机偏移压力测试(三种布尔运算)", tc_be_16_random_offset_all_boolean_ops); + //run("TC-BE-17 两螺旋线拉伸体差集", tc_be_17_two_helixlines_difference); + + print_summary(); + return (test_fw::g_fail > 0) ? 1 : 0; +} \ No newline at end of file