Skip to content

Commit a7422b6

Browse files
blackbird2003greyishsong
authored andcommitted
Add Loop Subdivision Unit Test
1 parent 43d78d1 commit a7422b6

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ ipch/
3636
# Vim
3737
.vim/
3838
*.swp
39+
# Unit Test Data
40+
test/input/
41+
test/ans/
3942

4043
# For Pre-compiled static libs -------------------
4144
deps/libdandelion-*.a

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ set(DANDELION_SIMULATION_SOURCES
6565
)
6666
set(TEST_SOURCES
6767
basic_tests.cpp
68+
geometry_tests.cpp
6869
)
6970

7071
set(SOURCES

test/geometry_tests.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include <Eigen/Core>
2+
#include <Eigen/Geometry>
3+
#include <catch2/catch_amalgamated.hpp>
4+
#include <fstream>
5+
#include <random>
6+
#include <spdlog/spdlog.h>
7+
8+
#include "../src/geometry/halfedge.h"
9+
#include "../src/scene/group.h"
10+
#include "../src/scene/object.h"
11+
#include "../src/utils/formatter.hpp"
12+
#include "../src/utils/math.hpp"
13+
14+
using Eigen::AngleAxisf;
15+
using Eigen::Matrix4f;
16+
using Eigen::Scaling;
17+
using Eigen::Translation3f;
18+
using Eigen::Vector3f;
19+
using Eigen::Vector4f;
20+
using std::default_random_engine;
21+
using std::random_device;
22+
using std::uniform_real_distribution;
23+
24+
using std::pair;
25+
using std::set;
26+
using std::string;
27+
using std::unordered_map;
28+
using std::vector;
29+
30+
constexpr float threshold = 1e-3f;
31+
constexpr float threshold_squ = 1e-6f;
32+
33+
TEST_CASE("Loop Subdivision", "[geometry]")
34+
{
35+
36+
vector<pair<string, string>> test_cases = {
37+
{"../input/geometry/cube.obj", "../ans/geometry/loop_subdivision/cube.txt"},
38+
{"../input/geometry/sphere.obj", "../ans/geometry/loop_subdivision/sphere.txt"},
39+
{"../input/geometry/cow.dae", "../ans/geometry/loop_subdivision/cow.txt"},
40+
{"../input/geometry/teapot.dae", "../ans/geometry/loop_subdivision/teapot.txt"},
41+
{"../input/geometry/bunny.obj", "../ans/geometry/loop_subdivision/bunny.txt"}};
42+
43+
size_t case_id = 0;
44+
for (const auto& test_case : test_cases) {
45+
auto model_path = test_case.first;
46+
auto std_result_path = test_case.second;
47+
48+
spdlog::info("Case #{}: Testing loop subdivision of: {}", ++case_id, model_path);
49+
50+
// Step 1. Load Test and STD Data
51+
Group test_group("Test group");
52+
bool model_load_ok = test_group.load(model_path);
53+
REQUIRE(model_load_ok);
54+
REQUIRE(!test_group.objects.empty());
55+
56+
HalfedgeMesh test_mesh(**test_group.objects.begin());
57+
test_mesh.loop_subdivide();
58+
59+
std::ifstream std_result_file(std_result_path);
60+
REQUIRE(std_result_file.is_open());
61+
62+
size_t std_vertex_count;
63+
std_result_file >> std_vertex_count;
64+
vector<Vector3f> std_vertices(std_vertex_count);
65+
66+
for (size_t i = 0; i < std_vertex_count; ++i) {
67+
float x, y, z;
68+
std_result_file >> x >> y >> z;
69+
std_vertices[i] = Vector3f(x, y, z);
70+
}
71+
72+
size_t std_edge_count;
73+
std_result_file >> std_edge_count;
74+
set<pair<size_t, size_t>> std_edges;
75+
76+
for (size_t i = 0; i < std_edge_count; ++i) {
77+
size_t v1, v2;
78+
std_result_file >> v1 >> v2;
79+
std_edges.insert({v1, v2});
80+
}
81+
82+
std_result_file.close();
83+
84+
// Step 2. Point Check and Mapping
85+
size_t test_vertex_count = test_mesh.vertices.size;
86+
INFO("Vertex count: Test: " << test_vertex_count << ", Expected: " << std_vertex_count);
87+
REQUIRE(test_vertex_count == std_vertex_count);
88+
89+
unordered_map<Vertex*, size_t> test_vertex_id;
90+
91+
for (Vertex* v = test_mesh.vertices.head; v != nullptr; v = v->next_node) {
92+
std::optional<size_t> id = std::nullopt;
93+
for (size_t i = 0; i < test_vertex_count; i++) {
94+
if ((v->pos - std_vertices[i]).squaredNorm() < threshold_squ) {
95+
id = i;
96+
break;
97+
}
98+
}
99+
INFO("At least one vertex has wrong position.");
100+
REQUIRE(id.has_value());
101+
test_vertex_id[v] = id.value();
102+
}
103+
104+
// Step 3. Edge Check
105+
size_t test_edge_count = test_mesh.edges.size;
106+
INFO("Edge count: Test: " << test_edge_count << ", Expected: " << std_edge_count);
107+
REQUIRE(test_edge_count == std_edge_count);
108+
109+
for (Edge* e = test_mesh.edges.head; e != nullptr; e = e->next_node) {
110+
Vertex* v1 = e->halfedge->from;
111+
Vertex* v2 = e->halfedge->inv->from;
112+
size_t id1 = test_vertex_id[v1];
113+
size_t id2 = test_vertex_id[v2];
114+
115+
auto edge_pair = std::make_pair(id1, id2);
116+
auto edge_pair_reversed = std::make_pair(id2, id1);
117+
118+
auto it = std_edges.find(edge_pair);
119+
if (it == std_edges.end()) {
120+
it = std_edges.find(edge_pair_reversed);
121+
}
122+
INFO("At least one edge connects wrong vertexs.");
123+
REQUIRE(it != std_edges.end());
124+
}
125+
spdlog::info("Test Pass: loop subdivision of: {}", model_path);
126+
}
127+
}

0 commit comments

Comments
 (0)