Files
ViewDesignEngine/tests/mesh/test_mesh_lod.cpp
T
2026-07-24 16:14:09 +00:00

152 lines
5.6 KiB
C++

#include <gtest/gtest.h>
#include "vde/mesh/mesh_lod.h"
#include "vde/mesh/halfedge_mesh.h"
#include <cmath>
using namespace vde::mesh;
/// Create a simple box mesh (12 triangles, 8 vertices)
static HalfedgeMesh make_box_mesh() {
// Unit cube vertices
std::vector<Point3D> 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<std::array<int, 3>> 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<int>(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.