-
Notifications
You must be signed in to change notification settings - Fork 102
Description
Hello!
First of all, I wanted to thank you for the wonderful lectures (youtube, and lecture notes). I am a visual computing master's student from TU Wien and wish we had this course in our university.
I did the coding assignments by following the lecture notes and recordings to the best of my ability. However, in assignment 6, I had some problems. All my unit tests other than TEST_F(HarmonicBasesTest, compute) were successful. In the end, I managed to find my bug by extending the unit tests---the unit tests are extremely helpful, so thank you!! I basically converted some of the missing JS unit tests to C++. If I did not miss anything, these tests were not available in the C++ version.
Below you can find the extended version of the unit tests for assignment 6 (i.e., test-decomp.cpp).
TEST_F(HarmonicBasesTest, buildPrimalSpanningTree) {
TreeCotree treeCotree = TreeCotree(HB.mesh, HB.geometry);
treeCotree.buildPrimalSpanningTree();
auto nSelfEdges = 0;
for (const auto& v : HB.mesh->vertices()) {
if (treeCotree.vertexParent[v] == v) {
nSelfEdges++;
}
}
EXPECT_TRUE(nSelfEdges == 1)
<< "TreeCotree: buildPrimalSpanningTree does not have "
"exactly one self-edge (the root)";
Vertex root;
for (const Vertex& v : HB.mesh->vertices()) {
if (treeCotree.vertexParent[v] == v) {
root = v;
break;
}
}
const auto n = HB.mesh->nVertices();
bool connected = true;
for (Vertex& v : HB.mesh->vertices()) {
auto path_length = 0;
while (v != root && path_length < n + 1) {
v = treeCotree.vertexParent[v];
path_length += 1;
}
if (v != root) {
connected = false;
break;
}
}
EXPECT_TRUE(connected)
<< "TreeCotree: buildPrimalSpanningTree graph is not connected";
}
TEST_F(HarmonicBasesTest, inDualSpanningCotree) {
TreeCotree treeCotree = TreeCotree(HB.mesh, HB.geometry);
const auto& he = HB.mesh->edge(0).halfedge();
auto& f1 = he.face();
auto& f2 = he.twin().face();
treeCotree.faceParent[f1] = f2;
EXPECT_TRUE(treeCotree.inDualSpanningCotree(he))
<< "TreeCotree: buildPrimalSpanningTree not recognizes positively "
"oriented halfedges in tree";
f1 = he.twin().face();
f2 = he.face();
treeCotree.faceParent[f1] = f2;
EXPECT_TRUE(treeCotree.inDualSpanningCotree(he))
<< "TreeCotree: buildPrimalSpanningTree not recognizes negatifely "
"oriented "
"halfedges in tree";
treeCotree.faceParent.clear();
EXPECT_FALSE(treeCotree.inDualSpanningCotree(he))
<< "TreeCotree: buildPrimalSpanningTree does not rejects halfedges not "
"in tree";
}
TEST_F(HarmonicBasesTest, buildDualSpanningTree) {
TreeCotree treeCotree = TreeCotree(HB.mesh, HB.geometry);
treeCotree.buildPrimalSpanningTree();
treeCotree.buildDualSpanningCoTree();
auto nSelfEdges = 0;
for (const auto& f : HB.mesh->faces()) {
if (treeCotree.faceParent[f] == f) {
nSelfEdges++;
}
}
EXPECT_TRUE(nSelfEdges == 1)
<< "TreeCotree: buildDualSpanningTree does not have "
"exactly one self-edge (the root)";
Face root;
for (const Face& f : HB.mesh->faces()) {
if (treeCotree.faceParent[f] == f) {
root = f;
break;
}
}
const auto n = HB.mesh->nFaces();
bool connected = true;
for (Face& f : HB.mesh->faces()) {
auto path_length = 0;
while (f != root && path_length < n + 1) {
f = treeCotree.faceParent[f];
path_length += 1;
}
if (f != root) {
connected = false;
break;
}
}
EXPECT_TRUE(connected)
<< "TreeCotree: buildDualSpanningTree graph is not connected";
for (const auto& e : HB.mesh->edges()) {
const auto& h = e.halfedge();
EXPECT_FALSE(treeCotree.inPrimalSpanningTree(h) &&
treeCotree.inDualSpanningCotree(h));
}
}
TEST_F(HarmonicBasesTest, buildGenerators) {
const auto g = 1 - HB.mesh->eulerCharacteristic() / 2;
auto treeCotree = TreeCotree(HB.mesh, HB.geometry);
treeCotree.buildGenerators();
EXPECT_EQ(treeCotree.generators.size(), 2 * g)
<< "does not have 2g generators";
}
TEST_F(HarmonicBasesTest, buildClosedPrimalOneForm) {
auto edgeIdx = HB.mesh->getEdgeIndices();
std::vector<Halfedge> generator;
for (auto i = 0; i < 5; i++) {
generator.push_back(HB.mesh->edge(i).halfedge());
}
auto& w = HB.buildClosedPrimalOneForm(generator);
for (const auto& e : HB.mesh->edges()) {
auto i = edgeIdx[e];
if (i < 5) {
EXPECT_TRUE(w[i] != 0) << "HarmonicBases: buildClosedPrimalOneForm "
"has not correct nonzero entries";
} else {
EXPECT_EQ(w[i], 0) << "HarmonicBases: buildClosedPrimalOneForm has "
"not correct nonzero entries";
}
}
generator.clear();
for (auto i = 0; i < 5; i++) {
if (i % 2 == 0) {
generator.push_back(HB.mesh->edge(i).halfedge());
} else {
generator.push_back(HB.mesh->edge(i).halfedge().twin());
}
}
w = HB.buildClosedPrimalOneForm(generator);
for (auto e : HB.mesh->edges()) {
auto i = edgeIdx[e];
if (i < 5) {
if (i % 2 == 0) {
EXPECT_EQ(w[i], 1) << "HarmonicBases: buildClosedPrimalOneForm "
"incorrect orientation";
} else {
EXPECT_EQ(w[i], -1)
<< "HarmonicBases: buildClosedPrimalOneForm "
"incorrect orientation";
}
} else {
EXPECT_EQ(w[i], 0) << "HarmonicBases: buildClosedPrimalOneForm "
"incorrect orientation";
}
}
}I am in no way ensuring their correctness, so beware of using it.
Sorry for opening an issue this would have been better as a discussion thread.