From 2435630bd91a23735506945e68f21f464b51e8ac Mon Sep 17 00:00:00 2001 From: Philip Trettner <Philip.Trettner@rwth-aachen.de> Date: Mon, 14 Oct 2019 10:02:22 +0200 Subject: [PATCH] migrate polymesh tests from glow-tests --- extern/glow | 2 +- extern/polymesh | 2 +- extern/typed-geometry | 2 +- tests/basics.cc | 401 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 tests/basics.cc diff --git a/extern/glow b/extern/glow index 81502c0..f1f7299 160000 --- a/extern/glow +++ b/extern/glow @@ -1 +1 @@ -Subproject commit 81502c09d72d8a58d38745cf492a31d974150eda +Subproject commit f1f7299ba8453e5fbc2dc7229811eba3071aa31e diff --git a/extern/polymesh b/extern/polymesh index cc1a161..a50e12b 160000 --- a/extern/polymesh +++ b/extern/polymesh @@ -1 +1 @@ -Subproject commit cc1a1619e0b14b230c8036c99227b14d50d8b486 +Subproject commit a50e12bfb73f7cf764d954200cc770261e52fb70 diff --git a/extern/typed-geometry b/extern/typed-geometry index 7deba41..f273740 160000 --- a/extern/typed-geometry +++ b/extern/typed-geometry @@ -1 +1 @@ -Subproject commit 7deba410bcb9a222072242ee9d839db27bf4d5f7 +Subproject commit f2737400016d652e77048e54885e466ce769cb6c diff --git a/tests/basics.cc b/tests/basics.cc new file mode 100644 index 0000000..bf9b6c1 --- /dev/null +++ b/tests/basics.cc @@ -0,0 +1,401 @@ +#include <doctest.hh> + +#include <random> + +#include <glm/ext.hpp> +#include <glm/glm.hpp> + +#include <polymesh/Mesh.hh> +#include <polymesh/algorithms.hh> +#include <polymesh/debug.hh> +#include <polymesh/formats/obj.hh> + +TEST_CASE("PolyMesh, Basics") +{ + polymesh::Mesh m; + + auto v0 = m.vertices().add(); + auto v1 = m.vertices().add(); + auto v2 = m.vertices().add(); + + auto i = 0; + for (auto vh : m.vertices()) + { + CHECK(vh.mesh == &m); + ++i; + } + + auto j = 0; + for (auto vh : m.vertices()) + { + CHECK(vh.mesh == &m); + ++j; + } + + CHECK(i == 3); + CHECK(i == j); + + m.assert_consistency(); + + auto f1 = m.faces().add(v0, v1, v2); + CHECK(m.faces().size() == 1); + CHECK(m.faces().count() == 1); + + m.assert_consistency(); + + auto v3 = m.vertices().add(); + auto f2 = m.faces().add(v0, v2, v3); + CHECK(m.faces().size() == 2); + CHECK(m.faces().count() == 2); + + m.assert_consistency(); + + m.faces().remove(f1); + CHECK(m.faces().size() == 1); + CHECK(m.faces().count() == 1); + + m.assert_consistency(); + + m.faces().remove(f2); + CHECK(m.faces().size() == 0); + CHECK(m.faces().count() == 0); + + m.assert_consistency(); +} + +TEST_CASE("PolyMesh, Properties") +{ + using namespace polymesh; + Mesh m; + + auto v0 = m.vertices().add(); + auto v1 = m.vertices().add(); + auto v2 = m.vertices().add(); + auto f = m.faces().add(v0, v1, v2); + + auto pos = m.vertices().make_attribute_with_default(glm::vec3(7, 7, 7)); + auto centroid = m.faces().make_attribute<glm::vec3>(); + + CHECK(pos[v0] == glm::vec3(7)); + + CHECK(valence(v0) == 2); + CHECK(valence(v1) == 2); + CHECK(valence(v2) == 2); + + CHECK(!v0.faces().empty()); + + // .faces() contains invalid ones as well! + // CHECK(v0.faces().size() == 1); + // CHECK(v0.faces().count() == 1); + // CHECK(v0.faces().first() == f); + + v0[pos] = {1, 2, 3}; + v1[pos] = {2, 3, 4}; + v2[pos] = {3, 1, 8}; + + centroid[f] = (pos[v0] + pos[v1] + pos[v2]) / 3.0f; + + CHECK(f[centroid] == glm::vec3(2, 2, 5)); + // CHECK(f[centroid] == f.adjacent_faces().); + + CHECK(f.vertices().min(pos) == glm::vec3(1, 1, 3)); + CHECK(f.vertices().max(pos) == glm::vec3(3, 3, 8)); + CHECK(f.vertices().avg(pos) == triangle_centroid(f, pos)); + + auto v3 = m.vertices().add(); + auto f2 = m.faces().add(v0, v2, v3); + CHECK(pos[v3] == glm::vec3(7)); + f2[centroid] = f[centroid]; + + CHECK(valence(v0) == 3); + CHECK(valence(v1) == 2); + CHECK(valence(v2) == 3); + CHECK(valence(v3) == 2); + + auto pos2 = pos; // copy! + pos2[v0] = {9, 9, 9}; + CHECK(pos[v0] == glm::vec3(1, 2, 3)); + CHECK(pos2[v0] == glm::vec3(9, 9, 9)); + pos[v0] = {8, 8, 8}; + CHECK(pos[v0] == glm::vec3(8, 8, 8)); + CHECK(pos2[v0] == glm::vec3(9, 9, 9)); +} + +TEST_CASE("PolyMesh, Grid") +{ + auto size = 32; + + polymesh::Mesh m; + + auto position = m.vertices().make_attribute<glm::vec3>(); + + std::vector<polymesh::vertex_handle> vs; + for (auto x = 0; x < size; ++x) + for (auto y = 0; y < size; ++y) + { + auto pos = glm::vec3(x, glm::sin(x * y * 1234.12341), y); + auto v = m.vertices().add(); + v[position] = pos; + vs.push_back(v); + } + + for (auto x = 0; x < size - 1; ++x) + for (auto y = 0; y < size - 1; ++y) + { + auto v00 = vs[(y + 0) * size + (x + 0)]; + auto v10 = vs[(y + 0) * size + (x + 1)]; + auto v01 = vs[(y + 1) * size + (x + 0)]; + auto v11 = vs[(y + 1) * size + (x + 1)]; + + m.faces().add(v00, v01, v11, v10); + } + + m.assert_consistency(); +} + +TEST_CASE("PolyMesh, GridTopoMod") +{ + std::default_random_engine rng(12345); + auto size = 5; + + polymesh::Mesh m; + + auto position = m.vertices().make_attribute<glm::vec3>(); + + std::vector<polymesh::vertex_handle> vs; + for (auto x = 0; x < size; ++x) + for (auto y = 0; y < size; ++y) + { + auto pos = glm::vec3(x, 0, y); + auto v = m.vertices().add(); + v[position] = pos; + vs.push_back(v); + } + + for (auto x = 0; x < size - 1; ++x) + for (auto y = 0; y < size - 1; ++y) + { + auto v00 = vs[(y + 0) * size + (x + 0)]; + auto v10 = vs[(y + 0) * size + (x + 1)]; + auto v01 = vs[(y + 1) * size + (x + 0)]; + auto v11 = vs[(y + 1) * size + (x + 1)]; + + m.faces().add(v00, v01, v11, v10); + } + + m.assert_consistency(); + + for (auto i = 0; i < 30; ++i) + { + std::uniform_int_distribution<int> op(0, 3); + switch (op(rng)) + { + case 0: + { + auto es = m.edges().to_vector(); + std::uniform_int_distribution<int> re(0, int(es.size() - 1)); + auto e = es[re(rng)]; + while (e.is_boundary() || valence(e.vertexA()) <= 2 || valence(e.vertexB()) <= 2) + e = es[re(rng)]; + + m.edges().rotate_next(e); + m.assert_consistency(); + } + break; + case 1: + { + auto es = m.edges().to_vector(); + std::uniform_int_distribution<int> re(0, int(es.size() - 1)); + auto e = es[re(rng)]; + while (e.is_boundary() || valence(e.vertexA()) <= 2 || valence(e.vertexB()) <= 2) + e = es[re(rng)]; + + m.edges().rotate_prev(e); + m.assert_consistency(); + } + break; + case 2: + { + auto hs = m.halfedges().to_vector(); + std::uniform_int_distribution<int> re(0, int(hs.size() - 1)); + auto h = hs[re(rng)]; + while (h.edge().is_boundary() || valence(h.vertex_to()) <= 2 || h.face().vertices().size() <= 3) + h = hs[re(rng)]; + + m.halfedges().rotate_next(h); + m.assert_consistency(); + } + break; + case 3: + { + auto hs = m.halfedges().to_vector(); + std::uniform_int_distribution<int> re(0, int(hs.size() - 1)); + auto h = hs[re(rng)]; + while (h.edge().is_boundary() || valence(h.vertex_from()) <= 2 || h.face().vertices().size() <= 3) + h = hs[re(rng)]; + + m.halfedges().rotate_prev(h); + m.assert_consistency(); + } + break; + } + } +} + +TEST_CASE("PolyMesh, GridFuzzer") +{ + std::default_random_engine rng(12345); + auto size = 6; // 8 + + for (auto _ = 0; _ < 2; ++_) + { + polymesh::Mesh m; + auto position = m.vertices().make_attribute<glm::vec3>(); + + struct Face + { + polymesh::vertex_handle v0; + polymesh::vertex_handle v1; + polymesh::vertex_handle v2; + polymesh::vertex_handle v3; + + polymesh::face_handle f0 = {}; + polymesh::face_handle f1 = {}; + int face_order = -1; + }; + + std::vector<polymesh::vertex_handle> vs; + for (auto x = 0; x < size; ++x) + for (auto y = 0; y < size; ++y) + { + auto v = m.vertices().add(); + vs.push_back(v); + position[v] = {x, 0, y}; + } + + std::vector<Face> faces; + + std::bernoulli_distribution coin(0.5); + + for (auto x = 0; x < size - 1; ++x) + for (auto y = 0; y < size - 1; ++y) + { + auto v00 = vs[(y + 0) * size + (x + 0)]; + auto v10 = vs[(y + 0) * size + (x + 1)]; + auto v01 = vs[(y + 1) * size + (x + 0)]; + auto v11 = vs[(y + 1) * size + (x + 1)]; + + faces.push_back({v00, v01, v11, v10}); + faces.back().face_order = coin(rng) ? 0 : coin(rng) ? 1 : 2; + } + + std::shuffle(faces.begin(), faces.end(), rng); + + m.assert_consistency(); + + for (auto& f : faces) + { + // print_debug(m); + + if (f.face_order == 0) + { + f.f0 = m.faces().add(f.v0, f.v1, f.v2, f.v3); + f.f1 = {}; + } + else if (f.face_order == 1) + { + f.f0 = m.faces().add(f.v0, f.v1, f.v2); + f.f1 = m.faces().add(f.v0, f.v2, f.v3); + } + else + { + f.f0 = m.faces().add(f.v1, f.v2, f.v3); + f.f1 = m.faces().add(f.v1, f.v3, f.v0); + } + + m.assert_consistency(); + } + + + // flips some faces + for (auto i = 0; i < 100; ++i) + { + std::uniform_int_distribution<> rfi(0, (int)faces.size() - 1); + auto& f = faces[rfi(rng)]; + + if (f.f0.is_valid()) // remove + { + m.faces().remove(f.f0); + if (f.f1.is_valid()) + m.faces().remove(f.f1); + + f.f0 = {}; + f.f1 = {}; + } + else // add + { + auto v0_free = false; + auto v1_free = false; + auto v2_free = false; + auto v3_free = false; + for (auto ff : f.v0.all_faces()) + if (ff.is_invalid()) + v0_free = true; + for (auto ff : f.v1.all_faces()) + if (ff.is_invalid()) + v1_free = true; + for (auto ff : f.v2.all_faces()) + if (ff.is_invalid()) + v2_free = true; + for (auto ff : f.v3.all_faces()) + if (ff.is_invalid()) + v3_free = true; + assert(v0_free); + assert(v1_free); + assert(v2_free); + assert(v3_free); + + if (f.face_order == 0) + { + f.f0 = m.faces().add(f.v0, f.v1, f.v2, f.v3); + f.f1 = {}; + } + else if (f.face_order == 1) + { + f.f0 = m.faces().add(f.v0, f.v1, f.v2); + f.f1 = m.faces().add(f.v0, f.v2, f.v3); + } + else + { + f.f0 = m.faces().add(f.v1, f.v2, f.v3); + f.f1 = m.faces().add(f.v1, f.v3, f.v0); + } + } + + m.assert_consistency(); + } + + // compactify + // CAUTION: face_handles are now invalid! + m.compactify(); + m.assert_consistency(); + + // remove some random edges + std::bernoulli_distribution coin_remove_edge(0.1); + for (auto e : m.edges()) + if (coin_remove_edge(rng)) + { + m.edges().remove(e); + m.assert_consistency(); + } + + // remove some random vertices + std::bernoulli_distribution coin_remove_vertex(0.1); + for (auto v : m.vertices()) + if (coin_remove_vertex(rng)) + { + m.vertices().remove(v); + m.assert_consistency(); + } + } +} -- GitLab