#include #include "vde/mesh/mesh_lod.h" #include "vde/mesh/halfedge_mesh.h" #include using namespace vde::mesh; /// Create a simple box mesh (12 triangles, 8 vertices) static HalfedgeMesh make_box_mesh() { // Unit cube vertices std::vector verts = { {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, // bottom {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1}, // top }; // 12 triangles (2 per face) std::vector> tris = { {0, 2, 1}, {0, 3, 2}, // front {1, 2, 6}, {1, 6, 5}, // right {5, 6, 7}, {5, 7, 4}, // back {4, 7, 3}, {4, 3, 0}, // left {3, 7, 6}, {3, 6, 2}, // top {4, 0, 1}, {4, 1, 5}, // bottom }; HalfedgeMesh mesh; mesh.build_from_triangles(verts, tris); return mesh; } // ────────────────────────────────────────────────────── // LOD generation // ────────────────────────────────────────────────────── TEST(LODMeshTest, GenerateLOD_DefaultLevels) { auto box = make_box_mesh(); auto lod = generate_lod(box, 3, 0.5); // Default: 3 levels (base + 2 simplified) EXPECT_EQ(lod.levels.size(), 3u); EXPECT_EQ(lod.thresholds.size(), 3u); // Level 0 is the original mesh (12 faces) EXPECT_GE(lod.levels[0].num_faces(), 8u); // Each level should have ≤ faces than previous EXPECT_LE(lod.levels[1].num_faces(), lod.levels[0].num_faces()); EXPECT_LE(lod.levels[2].num_faces(), lod.levels[1].num_faces()); } TEST(LODMeshTest, GenerateLOD_SingleLevel) { auto box = make_box_mesh(); auto lod = generate_lod(box, 1, 0.5); // Only 1 level = base (no simplification) EXPECT_EQ(lod.levels.size(), 1u); EXPECT_EQ(lod.levels[0].num_faces(), 12u); } TEST(LODMeshTest, GenerateLOD_AggressiveReduction) { auto box = make_box_mesh(); auto lod = generate_lod(box, 5, 0.1); // keep only 10% each level // Should stop early when < 4 faces remain EXPECT_GE(lod.levels.size(), 1u); EXPECT_LE(lod.levels.size(), 5u); } TEST(LODMeshTest, GenerateLOD_EmptyMesh) { HalfedgeMesh empty; auto lod = generate_lod(empty, 3, 0.5); EXPECT_EQ(lod.levels.size(), 1u); // only base EXPECT_EQ(lod.levels[0].num_faces(), 0u); } // ────────────────────────────────────────────────────── // level_for_error // ────────────────────────────────────────────────────── TEST(LODMeshTest, LevelForError_ZeroError) { auto box = make_box_mesh(); auto lod = generate_lod(box, 3, 0.5); // error 0 → base level (threshold 0.0) EXPECT_EQ(lod.level_for_error(0.0), 0); } TEST(LODMeshTest, LevelForError_ThresholdBoundary) { auto box = make_box_mesh(); auto lod = generate_lod(box, 3, 0.5); // thresholds: [0.0, 100.0, 200.0] EXPECT_EQ(lod.level_for_error(50.0), 0); EXPECT_EQ(lod.level_for_error(100.0), 1); // ≥ 100 → level 1 EXPECT_EQ(lod.level_for_error(150.0), 1); EXPECT_EQ(lod.level_for_error(200.0), 2); // ≥ 200 → level 2 EXPECT_EQ(lod.level_for_error(999.0), 2); } TEST(LODMeshTest, LevelForError_NegativeError) { auto box = make_box_mesh(); auto lod = generate_lod(box, 3, 0.5); // Negative error → level 0 (base) EXPECT_EQ(lod.level_for_error(-1.0), 0); } // ────────────────────────────────────────────────────── // mesh_at // ────────────────────────────────────────────────────── TEST(LODMeshTest, MeshAt_ValidLevels) { auto box = make_box_mesh(); auto lod = generate_lod(box, 3, 0.5); EXPECT_EQ(lod.mesh_at(0).num_faces(), 12u); EXPECT_LE(lod.mesh_at(1).num_faces(), 12u); EXPECT_LE(lod.mesh_at(2).num_faces(), lod.mesh_at(1).num_faces()); } TEST(LODMeshTest, MeshAt_ClampNegative) { auto box = make_box_mesh(); auto lod = generate_lod(box, 3, 0.5); // Negative level → clamped to 0 EXPECT_EQ(lod.mesh_at(-1).num_faces(), lod.mesh_at(0).num_faces()); EXPECT_EQ(lod.mesh_at(-100).num_faces(), lod.mesh_at(0).num_faces()); } TEST(LODMeshTest, MeshAt_ClampTooHigh) { auto box = make_box_mesh(); auto lod = generate_lod(box, 3, 0.5); int last = static_cast(lod.levels.size()) - 1; EXPECT_EQ(lod.mesh_at(999).num_faces(), lod.mesh_at(last).num_faces()); } TEST(LODMeshTest, MeshAt_EmptyLOD) { LODMesh empty_lod; // mesh_at returns a static empty mesh for empty LOD EXPECT_EQ(empty_lod.mesh_at(0).num_faces(), 0u); EXPECT_EQ(empty_lod.mesh_at(5).num_faces(), 0u); } // ────────────────────────────────────────────────────── // generate_brep_lod (integration) // ────────────────────────────────────────────────────── // Forward-declare for linking only (no brep::make_box in this TU's link deps) // This test is compile-time only; runtime needs brep linked. // We test through the LOD API directly above; brep path is tested // in the brep test suite.