#include #include "vde/mesh/visualization_quality.h" #include "vde/mesh/halfedge_mesh.h" using namespace vde::mesh; // ═══════════════════════════════════════════════════════════ // Helper: create a simple cube mesh // ═══════════════════════════════════════════════════════════ static std::vector cube_verts() { return { Point3D(0,0,0), Point3D(1,0,0), Point3D(1,1,0), Point3D(0,1,0), Point3D(0,0,1), Point3D(1,0,1), Point3D(1,1,1), Point3D(0,1,1) }; } static std::vector> cube_tris() { return { {0,1,2}, {0,2,3}, // bottom {4,7,6}, {4,6,5}, // top {0,4,5}, {0,5,1}, // front {1,5,6}, {1,6,2}, // right {2,6,7}, {2,7,3}, // back {3,7,4}, {3,4,0}, // left }; } static HalfedgeMesh make_cube_mesh() { HalfedgeMesh mesh; mesh.build_from_triangles(cube_verts(), cube_tris()); return mesh; } static HalfedgeMesh make_tetrahedron_mesh() { std::vector verts = { Point3D(0,0,0), Point3D(1,0,0), Point3D(0.5,0.866,0), Point3D(0.5,0.289,0.816) }; std::vector> tris = { {0,1,2}, {0,3,1}, {1,3,2}, {2,3,0} }; HalfedgeMesh mesh; mesh.build_from_triangles(verts, tris); return mesh; } // ═══════════════════════════════════════════════════════════ // 环境光遮蔽测试 // ═══════════════════════════════════════════════════════════ TEST(AmbientOcclusionTest, EmptyMesh) { HalfedgeMesh empty; auto result = ambient_occlusion(empty); EXPECT_EQ(result.vertex_ao.size(), 0u); EXPECT_EQ(result.avg_ao, 1.0); } TEST(AmbientOcclusionTest, TetrahedronBasic) { auto mesh = make_tetrahedron_mesh(); auto result = ambient_occlusion(mesh, 64); EXPECT_EQ(result.vertex_ao.size(), mesh.num_vertices()); EXPECT_EQ(result.samples, 64); for (double ao : result.vertex_ao) { EXPECT_GE(ao, 0.0); EXPECT_LE(ao, 1.0); } } TEST(AmbientOcclusionTest, CubeAoBounds) { auto mesh = make_cube_mesh(); auto result = ambient_occlusion(mesh, 128); EXPECT_LE(result.min_ao, result.avg_ao); EXPECT_GE(result.max_ao, result.avg_ao); EXPECT_GE(result.min_ao, 0.0); EXPECT_LE(result.max_ao, 1.0); } // ═══════════════════════════════════════════════════════════ // 边高亮测试 // ═══════════════════════════════════════════════════════════ TEST(EdgeHighlightTest, EmptyMesh) { HalfedgeMesh empty; auto result = edge_highlighting(empty); EXPECT_EQ(result.total_edges, 0); EXPECT_EQ(result.hard_count, 0); } TEST(EdgeHighlightTest, CubeNoHardEdges) { auto mesh = make_cube_mesh(); // edges on a cube are flat → all dihedral angles = 90° (π/2) auto result = edge_highlighting(mesh, 1.2); // threshold > π/2 EXPECT_GT(result.total_edges, 0); EXPECT_EQ(result.hard_count, 0); } TEST(EdgeHighlightTest, CubeHardEdgesLowThreshold) { auto mesh = make_cube_mesh(); auto result = edge_highlighting(mesh, 0.5236); // ~30° EXPECT_GT(result.total_edges, 0); EXPECT_GT(result.hard_count, 0); EXPECT_EQ(result.hard_count, result.total_edges); } // ═══════════════════════════════════════════════════════════ // 线框叠加测试 // ═══════════════════════════════════════════════════════════ TEST(WireframeOverlayTest, TetrahedronWireframe) { auto mesh = make_tetrahedron_mesh(); auto result = wireframe_overlay(mesh); EXPECT_EQ(result.vertices.size(), 4u); EXPECT_EQ(result.wire_edges.size(), 6u); } TEST(WireframeOverlayTest, CubeWireframe) { auto mesh = make_cube_mesh(); auto result = wireframe_overlay(mesh); EXPECT_EQ(result.vertices.size(), 8u); EXPECT_EQ(result.wire_edges.size(), 18u); } // ═══════════════════════════════════════════════════════════ // 法线可视化测试 // ═══════════════════════════════════════════════════════════ TEST(NormalVisualizationTest, EmptyMesh) { HalfedgeMesh empty; auto result = normal_visualization(empty); EXPECT_EQ(result.face_centers.size(), 0u); EXPECT_EQ(result.face_normals.size(), 0u); } TEST(NormalVisualizationTest, TetrahedronNormals) { auto mesh = make_tetrahedron_mesh(); auto result = normal_visualization(mesh); EXPECT_EQ(result.face_centers.size(), mesh.num_faces()); EXPECT_EQ(result.face_normals.size(), mesh.num_faces()); EXPECT_EQ(result.vertex_positions.size(), mesh.num_vertices()); EXPECT_EQ(result.vertex_normals.size(), mesh.num_vertices()); for (const auto& n : result.face_normals) { EXPECT_NEAR(n.norm(), 1.0, 1e-9); } for (const auto& n : result.vertex_normals) { EXPECT_NEAR(n.norm(), 1.0, 1e-9); } } TEST(NormalVisualizationTest, CubeNormals) { auto mesh = make_cube_mesh(); auto result = normal_visualization(mesh); EXPECT_EQ(result.face_centers.size(), mesh.num_faces()); EXPECT_EQ(result.face_normals.size(), mesh.num_faces()); for (const auto& n : result.face_normals) { EXPECT_NEAR(n.norm(), 1.0, 1e-9); } }