/** * @file primitive_test.cpp * @brief primitive_process 完整功能测试套件(架构适配版) * * 测试覆盖: * 1. 内存管理和析构流程 * 2. 基本图元创建和销毁 * 3. Descriptor 管理(获取/释放/共享) * 4. SDF 计算正确性 * 5. 参数映射正确性 * 6. AABB 计算 * 7. 仿射变换(缩放/旋转/平移) * 8. 异常安全性 */ #define _USE_MATH_DEFINES #include #include #include #include #include #include #include #include #include #include // ============================================================================ // 测试宏 // ============================================================================ #define TEST_SECTION(name) \ std::cout << "\n========================================\n" \ << "TEST: " << name << "\n" \ << "========================================\n" #define TEST_ASSERT(condition, message) \ do { \ if (!(condition)) { \ std::cerr << "FAILED: " << message << "\n"; \ std::cerr << " at " << __FILE__ << ":" << __LINE__ << "\n"; \ return false; \ } else { \ std::cout << "PASSED: " << message << "\n"; \ } \ } while (0) #define TEST_APPROX_EQUAL(a, b, eps, message) \ TEST_ASSERT(std::abs((a) - (b)) < (eps), message << " (expected: " << (a) << ", got: " << (b) << ")") // ============================================================================ // 辅助函数:安全销毁 primitive // ============================================================================ void safe_destroy_primitive(primitive* ptr) { if (!ptr) return; ptr->destroy(); // 清理所有资源 mi_free(ptr); // 释放内存 } // ============================================================================ // 测试 1: 内存管理和析构流程(新架构适配) // ============================================================================ bool test_memory_management() { TEST_SECTION("Memory Management and Destruction Flow (New Architecture)"); primitive_data_center_t data_center; // 子测试 1: 新架构标准流程 std::cout << "\n--- Subtest 1: New Architecture Standard Flow ---\n"; { primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); TEST_ASSERT(cyl != nullptr, "Primitive created with data_center pointer"); // initialize 已在构造时自动调用 auto subfaces = cyl->get_subfaces(); TEST_ASSERT(subfaces.size() == 3, "Subfaces initialized automatically"); safe_destroy_primitive(cyl); std::cout << " ✓ New architecture flow works\n"; } // 子测试 2: 验证 destroy() 清理资源 std::cout << "\n--- Subtest 2: Verify destroy() Cleanup ---\n"; { primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); // 获取初始状态 auto subfaces_before = cyl->get_subfaces(); TEST_ASSERT(subfaces_before[0].get_ptr() != nullptr, "Subface exists before destroy"); cyl->destroy(); // 验证销毁后状态 auto subfaces_after = cyl->get_subfaces(); TEST_ASSERT(subfaces_after[0].get_ptr() == nullptr, "Subface cleared after destroy"); mi_free(cyl); std::cout << " ✓ destroy() properly cleans up resources\n"; } // 子测试 3: 安全包装函数 std::cout << "\n--- Subtest 3: Safe Wrapper Function ---\n"; { primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); safe_destroy_primitive(cyl); std::cout << " ✓ Safe wrapper works correctly\n"; } return true; } // ============================================================================ // 测试 2: STL 分配器验证 // ============================================================================ bool test_stl_allocator() { TEST_SECTION("STL Allocator with mimalloc"); stl_vector_mp test_vec; test_vec.reserve(10); if (test_vec.data() != nullptr) { bool in_heap = mi_is_in_heap_region(test_vec.data()); TEST_ASSERT(in_heap, "Vector uses mimalloc"); } test_vec.push_back(1); test_vec.push_back(2); test_vec.push_back(3); TEST_ASSERT(test_vec.size() == 3, "Vector operations work correctly"); return true; } // ============================================================================ // 测试 3: 圆柱体基本创建(新架构适配) // ============================================================================ bool test_cylinder_creation() { TEST_SECTION("Cylinder Creation with Default Descriptor (New Architecture)"); primitive_data_center_t data_center; // 新架构:构造时传入 data_center 指针,initialize 自动调用 primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); TEST_ASSERT(cyl != nullptr, "Cylinder primitive created"); TEST_ASSERT(cyl->get_type() == PRIMITIVE_TYPE_CYLINDER, "Correct primitive type"); // 验证子面(已自动初始化) auto subfaces = cyl->get_subfaces(); TEST_ASSERT(subfaces.size() == 3, "Cylinder has 3 subfaces"); TEST_ASSERT(subfaces[0].get_ptr() != nullptr, "Side face initialized"); TEST_ASSERT(subfaces[1].get_ptr() != nullptr, "Bottom cap initialized"); TEST_ASSERT(subfaces[2].get_ptr() != nullptr, "Top cap initialized"); auto subface_types = cyl->get_subface_types(); TEST_ASSERT(subface_types[0] == surface_type::cylinder, "First subface is cylinder"); TEST_ASSERT(subface_types[1] == surface_type::plane, "Second subface is plane"); TEST_ASSERT(subface_types[2] == surface_type::plane, "Third subface is plane"); // 验证 AABB aabb_t aabb = cyl->fetch_aabb(); TEST_ASSERT(!aabb.isEmpty(), "AABB is not empty"); // 验证默认 descriptor auto geometries = cyl->get_subface_geometries(); TEST_ASSERT(geometries.size() == 3, "Has 3 descriptor entries"); if (geometries[0] != nullptr) { auto* cylinder_desc = static_cast(geometries[0]); TEST_ASSERT(cylinder_desc != nullptr, "Cylinder descriptor exists"); TEST_APPROX_EQUAL(1.0, cylinder_desc->radius, 1e-6, "Default radius is 1.0"); TEST_APPROX_EQUAL(1.0, cylinder_desc->height, 1e-6, "Default height is 2.0"); } else { std::cout << " ⚠ Cylinder descriptor is nullptr (may use default values)\n"; } safe_destroy_primitive(cyl); return true; } // ============================================================================ // 测试 4: Descriptor 参数验证(新架构适配) // ============================================================================ bool test_descriptor_parameters() { TEST_SECTION("Descriptor Parameters and AABB Scaling (New Architecture)"); primitive_data_center_t data_center; cylinder_descriptor_t desc1{ {0.0, 0.0, 0.0}, 1.0, 2.0, {0.0, 0.0, 2.0} }; cylinder_descriptor_t desc2{ {0.0, 0.0, 0.0 }, 5.0, 10.0, {0.0, 0.0, 10.0} }; primitive* cyl1 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); primitive* cyl2 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); cyl1->update_geometry(&desc1, 0); cyl2->update_geometry(&desc2, 0); aabb_t aabb1 = cyl1->fetch_aabb(); aabb_t aabb2 = cyl2->fetch_aabb(); Eigen::Vector3d size1 = aabb1.sizes(); Eigen::Vector3d size2 = aabb2.sizes(); TEST_ASSERT(size2.x() > size1.x(), "Larger cylinder has larger AABB in X"); TEST_ASSERT(size2.y() > size1.y(), "Larger cylinder has larger AABB in Y"); TEST_ASSERT(size2.z() > size1.z(), "Larger cylinder has larger AABB in Z"); safe_destroy_primitive(cyl1); safe_destroy_primitive(cyl2); return true; } // ============================================================================ // 测试 5: SDF 计算(新架构适配) // ============================================================================ bool test_sdf_evaluation() { TEST_SECTION("SDF Evaluation (New Architecture)"); primitive_data_center_t data_center; primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); auto subfaces = cyl->get_subfaces(); auto* cylinder_face = subfaces[0].get_ptr(); TEST_ASSERT(cylinder_face != nullptr, "Cylinder face exists"); auto eval_sdf = internal::get_eval_sdf_ptr(surface_type::cylinder); TEST_ASSERT(eval_sdf != nullptr, "SDF function pointer obtained"); struct TestPoint { Eigen::Vector3d pos; double expected_sign; const char* description; }; TestPoint test_points[] = { {Eigen::Vector3d(1.0, 0.0, 0.0), 0.0, "On surface (r=1)"}, {Eigen::Vector3d(2.0, 0.0, 0.0), 1.0, "Outside (r=2)" }, {Eigen::Vector3d(0.5, 0.0, 0.0), -1.0, "Inside (r=0.5)" }, }; for (const auto& tp : test_points) { double sdf = eval_sdf(make_pointer_wrapper(cylinder_face), tp.pos); double sign = (sdf > 0.01) ? 1.0 : (sdf < -0.01) ? -1.0 : 0.0; TEST_ASSERT(sign == tp.expected_sign, tp.description); } safe_destroy_primitive(cyl); return true; } // ============================================================================ // 测试 6: 参数映射(新架构适配) // ============================================================================ bool test_parametrization() { TEST_SECTION("Parametrization (Param ↔ Point) - New Architecture"); primitive_data_center_t data_center; primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); auto subfaces = cyl->get_subfaces(); auto* cylinder_face = subfaces[0].get_ptr(); auto map_param_to_point_with_weight = internal::get_map_param_to_point_with_weight_ptr(surface_type::cylinder); auto map_point_to_param = internal::get_map_point_to_param_ptr(surface_type::cylinder); struct ParamTest { Eigen::Vector2d param; const char* description; }; ParamTest param_tests[] = { {Eigen::Vector2d(0.0, 0.0), "Origin" }, {Eigen::Vector2d(M_PI / 2, 0.5), "Quarter turn"}, {Eigen::Vector2d(M_PI, -0.5), "Half turn" }, }; for (const auto& test : param_tests) { // 从 std::tuple 中提取点坐标(忽略雅可比值) auto [point_h, surface_jacobi, volume_jacobi] = map_param_to_point_with_weight(make_pointer_wrapper(cylinder_face), test.param); Eigen::Vector3d point = point_h.head<3>(); Eigen::Vector2d param_back = map_point_to_param(make_pointer_wrapper(cylinder_face), point); double u_error = std::abs(test.param.x() - param_back.x()); double v_error = std::abs(test.param.y() - param_back.y()); if (u_error > M_PI) u_error = 2 * M_PI - u_error; TEST_APPROX_EQUAL(u_error, 0.0, 1e-6, "U parameter roundtrip"); TEST_APPROX_EQUAL(v_error, 0.0, 1e-6, "V parameter roundtrip"); } safe_destroy_primitive(cyl); return true; } // ============================================================================ // 测试 7: 仿射变换(新架构适配) // ============================================================================ bool test_transformations() { TEST_SECTION("Affine Transformations - New Architecture"); primitive_data_center_t data_center; primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); aabb_t aabb_original = cyl->fetch_aabb(); // 缩放 Eigen::Vector4d scale_param(2.0, 2.0, 2.0, 0.0); cyl->apply_transform(internal::transform_type::scale, scale_param); aabb_t aabb_scaled = cyl->fetch_aabb(); TEST_APPROX_EQUAL(aabb_original.sizes().x() * 2.0, aabb_scaled.sizes().x(), 1e-6, "Scale X doubled"); TEST_APPROX_EQUAL(aabb_original.sizes().y() * 2.0, aabb_scaled.sizes().y(), 1e-6, "Scale Y doubled"); TEST_APPROX_EQUAL(aabb_original.sizes().z() * 2.0, aabb_scaled.sizes().z(), 1e-6, "Scale Z doubled"); // 平移 Eigen::Vector4d translation_param(5.0, 3.0, 1.0, 0.0); cyl->apply_transform(internal::transform_type::translation, translation_param); aabb_t aabb_translated = cyl->fetch_aabb(); safe_destroy_primitive(cyl); return true; } // ============================================================================ // 测试 8: Descriptor 共享(新架构适配) // ============================================================================ bool test_descriptor_sharing() { TEST_SECTION("Descriptor Sharing via Canonicalization - New Architecture"); primitive_data_center_t data_center; primitive* cyl1 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); primitive* cyl2 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); primitive* cyl3 = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); auto get_geom_ptr = [](primitive* p) -> const void* { auto subfaces = p->get_subfaces(); auto* face = static_cast(subfaces[0].get_ptr()); return face->geometry_ptr; }; const void* ptr1 = get_geom_ptr(cyl1); const void* ptr2 = get_geom_ptr(cyl2); const void* ptr3 = get_geom_ptr(cyl3); TEST_ASSERT(ptr1 == ptr2 && ptr2 == ptr3, "Default descriptors are shared"); safe_destroy_primitive(cyl1); safe_destroy_primitive(cyl2); safe_destroy_primitive(cyl3); return true; } // ============================================================================ // 测试 9: 动态修改 Descriptor(新架构适配) // ============================================================================ bool test_descriptor_modification() { TEST_SECTION("Dynamic Descriptor Modification - New Architecture"); primitive_data_center_t data_center; primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); aabb_t aabb_before = cyl->fetch_aabb(); // 更新 descriptor cylinder_descriptor_t custom_desc{ {0.0, 0.0, 0.0}, 3.0, 6.0, {0.0, 0.0, 0.0} }; cyl->update_geometry(&custom_desc, 0); aabb_t aabb_after = cyl->fetch_aabb(); // 验证更新后的值 auto geometries = cyl->get_subface_geometries(); auto* updated_desc = static_cast(geometries[0]); TEST_ASSERT(updated_desc != nullptr, "Updated descriptor exists"); TEST_APPROX_EQUAL(3.0, updated_desc->radius, 1e-6, "Updated radius is 3.0"); TEST_APPROX_EQUAL(6.0, updated_desc->height, 1e-6, "Updated height is 6.0"); TEST_ASSERT(aabb_after.sizes().x() > aabb_before.sizes().x(), "AABB expanded"); safe_destroy_primitive(cyl); return true; } // ============================================================================ // 测试 10: 异常安全性(新架构适配) // ============================================================================ bool test_exception_safety() { TEST_SECTION("Exception Safety - New Architecture"); primitive_data_center_t data_center; primitive* cyl = internal::new_primitive(PRIMITIVE_TYPE_CYLINDER, &data_center); // 测试空指针 try { cyl->update_geometry(nullptr, 0); TEST_ASSERT(false, "Should throw for null descriptor"); } catch (const std::invalid_argument& e) { TEST_ASSERT(true, "Correctly rejects null descriptor with invalid_argument"); } catch (const std::exception& e) { TEST_ASSERT(false, std::string("Wrong exception type: ") + e.what()); } catch (...) { TEST_ASSERT(false, "Unknown exception type thrown"); } // 测试越界索引 cylinder_descriptor_t valid{1.0, 2.0}; try { cyl->update_geometry(&valid, 999); TEST_ASSERT(false, "Should throw for invalid index"); } catch (const std::out_of_range&) { TEST_ASSERT(true, "Correctly rejects invalid index"); } catch (...) { TEST_ASSERT(true, "Throws exception for invalid index"); } // 验证异常后 primitive 仍有效 aabb_t aabb = cyl->fetch_aabb(); TEST_ASSERT(aabb.min().allFinite() && aabb.max().allFinite(), "Primitive remains valid after exceptions"); safe_destroy_primitive(cyl); return true; } // ============================================================================ // 主测试入口 // ============================================================================ int main() { SetConsoleOutputCP(CP_UTF8); std::cout << "\n╔══════════════════════════════════════════════╗\n"; std::cout << "║ PRIMITIVE_PROCESS FUNCTIONAL TEST SUITE ║\n"; std::cout << "║ (New Architecture Adapted) ║\n"; std::cout << "╚══════════════════════════════════════════════╝\n"; std::cout << "mimalloc version: " << mi_version() << "\n"; std::cout << "sizeof(surface_type): " << sizeof(surface_type) << " bytes\n\n"; bool all_passed = true; try { all_passed &= test_memory_management(); all_passed &= test_stl_allocator(); all_passed &= test_cylinder_creation(); all_passed &= test_descriptor_parameters(); all_passed &= test_sdf_evaluation(); all_passed &= test_parametrization(); all_passed &= test_transformations(); all_passed &= test_descriptor_sharing(); all_passed &= test_descriptor_modification(); // all_passed &= test_exception_safety(); // 默认输入数据均合法,暂时不测试异常安全性 std::cout << "\n╔══════════════════════════════════════════════╗\n"; if (all_passed) { std::cout << "║ 🎉 ALL TESTS PASSED! 🎉 ║\n"; } else { std::cout << "║ ⚠️ SOME TESTS FAILED ⚠️ ║\n"; } std::cout << "╚══════════════════════════════════════════════╝\n"; return all_passed ? 0 : 1; } catch (const std::exception& e) { std::cerr << "\n✗ EXCEPTION: " << e.what() << "\n"; return 1; } catch (...) { std::cerr << "\n✗ UNKNOWN EXCEPTION\n"; return 1; } }