Temporary repository used to save branch code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2412 lines
107 KiB

/**
* @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<include_dir> -L<lib_dir> -lPE
*
* 依赖:
* - solve.h : create_blobtree / bake_blobtree / create_solver / generate_polymesh / ...
* - primitive_descriptor.h : 所有 descriptor 类型
* - math/math_defs.hpp : pi / two_pi
*/
#include <iostream>
#include <sstream>
#include <vector>
#include <cmath>
#include <cassert>
#include <stdexcept>
#include <string>
#include <functional>
#include <fstream>
#include <filesystem>
#include <random>
#include <array>
#include <solve.h>
#include <math/math_defs.hpp>
#include <primitive_descriptor.h>
#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<Result> g_results; // 修复原代码中的类型错误 (原为 std::vector<r>)
static int g_pass = 0;
static int g_fail = 0;
void run(const std::string& name, std::function<void()> 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<node_operation>(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<vector3d>& pts,
std::vector<double>& 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<vector3d>& pts,
std::vector<double>& 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<uint32_t>(n);
p.points = pts.data();
p.bulge_number = static_cast<uint32_t>(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<vector3d> 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<double> 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<vector3d> axis_pts = {
{0.0, 0.0, 0.0},
{0.0, 0.0, 3.0}
};
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts = {
{0.0, 0.0, 0.0},
{0.0, 0.0, 2.0}
};
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> pts;
static std::vector<double> buls;
auto profile = make_regular_polygon_profile(5, 0.8, pts, buls);
static std::vector<vector3d> axis_pts;
static std::vector<double> 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<const void*>(&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<vector3d> pts;
static std::vector<double> 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<vector3d> axis_pts;
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts;
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts;
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts;
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts;
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts = {
{0.0, 0.0, 0.0},
{2.0, 0.0, 0.0},
{2.0, 0.0, 2.0}
};
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts = {
{0.0, 0.0, 0.0},
{2.0, 0.0, 2.0}
};
static std::vector<double> 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<const void*>(&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<vector3d> pts;
std::vector<double> buls;
auto profile = make_regular_polygon_profile(n, 0.7, pts, buls);
std::vector<vector3d> axis_pts;
std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<vector3d> axis_pts;
static std::vector<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<vector3d> 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<double> 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<const void*>(&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<std::pair<double, double>> 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<vector3d> 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<double> 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<const void*>(&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<node_operation> ops = {UNION_OP, INTERSECTION_OP, DIFFERENCE_OP};
const std::vector<std::string> 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<vector3d> 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<double> 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<const void*>(&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;
}