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