From f64d95b5cbdcbfe3b1ecb7ff27e931e988f0c1ae Mon Sep 17 00:00:00 2001
From: Philip Trettner <Philip.Trettner@rwth-aachen.de>
Date: Tue, 3 Dec 2019 18:56:15 +0100
Subject: [PATCH] fixed collapse_halfedge for many cases

---
 src/polymesh/algorithms/properties.hh         |   1 +
 .../impl/impl_low_level_api_mutable.hh        | 212 +++++++++++++-----
 2 files changed, 162 insertions(+), 51 deletions(-)

diff --git a/src/polymesh/algorithms/properties.hh b/src/polymesh/algorithms/properties.hh
index 7b6988b..f5be01b 100644
--- a/src/polymesh/algorithms/properties.hh
+++ b/src/polymesh/algorithms/properties.hh
@@ -606,6 +606,7 @@ inline bool can_collapse(halfedge_handle h)
         if (v == ignore_v0 || v == ignore_v1 || v == v_from)
             continue;
 
+        // TODO: can be made faster by buffering these vertices
         if (v_from.adjacent_vertices().contains(v))
             return false;
     }
diff --git a/src/polymesh/impl/impl_low_level_api_mutable.hh b/src/polymesh/impl/impl_low_level_api_mutable.hh
index bac58a5..7059f61 100644
--- a/src/polymesh/impl/impl_low_level_api_mutable.hh
+++ b/src/polymesh/impl/impl_low_level_api_mutable.hh
@@ -793,9 +793,6 @@ inline void low_level_api_mutable::halfedge_collapse(halfedge_index h) const
     auto h0 = h;
     auto h1 = opposite(h);
 
-    POLYMESH_ASSERT(!is_boundary(h0) && "boundaries are not supported yet");
-    POLYMESH_ASSERT(!is_boundary(h1) && "boundaries are not supported yet");
-
     auto v_to = to_vertex_of(h);
     auto v_from = from_vertex_of(h);
 
@@ -807,58 +804,171 @@ inline void low_level_api_mutable::halfedge_collapse(halfedge_index h) const
     auto h1_prev = prev_halfedge_of(h1);
     auto h1_next = next_halfedge_of(h1);
 
-    // TODO: preserve adjacent non-triangles
-    POLYMESH_ASSERT(prev_halfedge_of(h0_prev) == h0_next && "non-triangle adjacent faces not supported yet");
-    POLYMESH_ASSERT(prev_halfedge_of(h1_prev) == h1_next && "non-triangle adjacent faces not supported yet");
+    auto is_to_wrapped = h0_next == h1;
+    auto is_from_wrapped = h1_next == h0;
 
-    // fix faces
-    auto h0_prev_opp = opposite(h0_prev);
-    auto h1_next_opp = opposite(h1_next);
-    auto fA = face_of(h0_prev_opp);
-    auto fB = face_of(h1_next_opp);
-    face_of(h0_next) = fA;
-    face_of(h1_prev) = fB;
-    if (halfedge_of(fA) == h0_prev_opp)
-        halfedge_of(fA) = h0_next;
-    if (halfedge_of(fB) == h1_next_opp)
-        halfedge_of(fB) = h1_prev;
+    auto is_h0_boundary = is_boundary(h0);
+    auto is_h1_boundary = is_boundary(h1);
 
-    // fix vertices
-    auto hv = h1;
-    do
+    auto is_h0_triangle = !is_h0_boundary && next_halfedge_of(h0_next) == h0_prev;
+    auto is_h1_triangle = !is_h1_boundary && next_halfedge_of(h1_next) == h1_prev;
+
+    auto is_valence_2_from = h0_prev == opposite(h1_next);
+
+    // special cases for self-connected edges
+    if (is_to_wrapped && is_from_wrapped) // complete wraparound
     {
-        // point to collapsed vertex
-        POLYMESH_ASSERT(to_vertex_of(hv) == v_from);
-        to_vertex_of(hv) = v_to;
+        POLYMESH_ASSERT(f0.is_invalid() && "self-connected cannot have face");
+        POLYMESH_ASSERT(f1.is_invalid() && "self-connected cannot have face");
 
-        // advance
-        hv = opposite(next_halfedge_of(hv));
-    } while (hv != h1);
-
-    if (outgoing_halfedge_of(v_to) == h1)
-        outgoing_halfedge_of(v_to) = h0_next;
-
-    auto vA = to_vertex_of(h0_next);
-    auto vB = to_vertex_of(h1_next);
-    if (outgoing_halfedge_of(vA) == h0_prev)
-        outgoing_halfedge_of(vA) = next_halfedge_of(h0_prev_opp);
-    if (outgoing_halfedge_of(vB) == h1_next_opp)
-        outgoing_halfedge_of(vB) = h1_prev;
-
-    // fix next-prev
-    connect_prev_next(h1_prev, next_halfedge_of(h1_next_opp));
-    connect_prev_next(prev_halfedge_of(h1_next_opp), h1_prev);
-
-    connect_prev_next(h0_next, next_halfedge_of(h0_prev_opp));
-    connect_prev_next(prev_halfedge_of(h0_prev_opp), h0_next);
-
-    // mark for deletion
-    set_removed(v_from);
-    set_removed(f0);
-    set_removed(f1);
-    set_removed(edge_of(h));
-    set_removed(edge_of(h0_prev));
-    set_removed(edge_of(h1_next));
+        outgoing_halfedge_of(v_to) = halfedge_index::invalid;
+
+        set_removed(v_from);
+        set_removed(edge_of(h));
+    }
+    else if (is_from_wrapped) // from is self-connected
+    {
+        POLYMESH_ASSERT(f0 == f1 && "how can they have different faces?");
+
+        connect_prev_next(h1_prev, h0_next);
+        auto& oh = outgoing_halfedge_of(v_to);
+        if (oh == h1)
+            oh = h0_next;
+
+        if (f0.is_valid())
+        {
+            auto& fh = halfedge_of(f0);
+            if (fh == h0 || fh == h1)
+                fh = h0_next;
+        }
+
+        set_removed(v_from);
+        set_removed(edge_of(h));
+    }
+    else if (is_to_wrapped) // to is self-connected
+    {
+        POLYMESH_ASSERT(f0 == f1 && "how can they have different faces?");
+
+        connect_prev_next(h0_prev, h1_next);
+        outgoing_halfedge_of(v_to) = h1_next;
+
+        auto h = opposite(h1_next);
+        while (h != h1)
+        {
+            to_vertex_of(h) = v_to;
+            h = opposite(next_halfedge_of(h));
+        }
+
+        if (f0.is_valid())
+        {
+            auto& fh = halfedge_of(f0);
+            if (fh == h0 || fh == h1)
+                fh = h0_next;
+        }
+
+        fix_boundary_state_of(v_to);
+
+        set_removed(v_from);
+        set_removed(edge_of(h));
+    }
+    else
+    {
+        auto h0_prev_opp = opposite(h0_prev);
+        auto h1_next_opp = opposite(h1_next);
+
+        // fix faces (only if triangle because then the triangle is removed)
+        if (is_h0_triangle)
+        {
+            auto fA = face_of(h0_prev_opp);
+
+            face_of(h0_next) = fA;
+
+            if (fA.is_valid() && halfedge_of(fA) == h0_prev_opp)
+                halfedge_of(fA) = h0_next;
+        }
+        if (is_h1_triangle)
+        {
+            auto fB = face_of(h1_next_opp);
+
+            face_of(h1_prev) = fB;
+
+            if (fB.is_valid() && halfedge_of(fB) == h1_next_opp)
+                halfedge_of(fB) = h1_prev;
+        }
+
+        // fix vertices
+        auto hv = h1;
+        do
+        {
+            // point to collapsed vertex
+            POLYMESH_ASSERT(to_vertex_of(hv) == v_from);
+            to_vertex_of(hv) = v_to;
+
+            // advance
+            hv = opposite(next_halfedge_of(hv));
+        } while (hv != h1);
+
+        // fix outgoing of v_to
+        if (outgoing_halfedge_of(v_to) == h1)
+            outgoing_halfedge_of(v_to) = h0_next;
+
+        // fix outgoing of opposite of h0/h1 (if triangle) and prev/next
+        if (is_h0_triangle)
+        {
+            auto vA = to_vertex_of(h0_next);
+            if (outgoing_halfedge_of(vA) == h0_prev)
+                outgoing_halfedge_of(vA) = next_halfedge_of(h0_prev_opp);
+
+            // fix next-prev
+            connect_prev_next(h0_next, next_halfedge_of(h0_prev_opp));
+            if (is_valence_2_from)
+                connect_prev_next(prev_halfedge_of(prev_halfedge_of(h0_prev_opp)), h0_next);
+            else
+                connect_prev_next(prev_halfedge_of(h0_prev_opp), h0_next);
+        }
+        else
+        {
+            if (!is_valence_2_from)
+                connect_prev_next(h0_prev, h0_next);
+        }
+        if (is_h1_triangle)
+        {
+            auto vB = to_vertex_of(h1_next);
+            if (outgoing_halfedge_of(vB) == h1_next_opp)
+                outgoing_halfedge_of(vB) = h1_prev;
+
+            // fix next-prev
+            if (is_valence_2_from)
+                connect_prev_next(h1_prev, next_halfedge_of(next_halfedge_of(h1_next_opp)));
+            else
+                connect_prev_next(h1_prev, next_halfedge_of(h1_next_opp));
+            connect_prev_next(prev_halfedge_of(h1_next_opp), h1_prev);
+        }
+        else
+        {
+            if (is_valence_2_from)
+                connect_prev_next(h1_prev, h0_next);
+            else
+                connect_prev_next(h1_prev, h1_next);
+        }
+
+        // vertex might be boundary now
+        fix_boundary_state_of(v_to);
+
+        // mark for deletion
+        set_removed(v_from);
+        set_removed(edge_of(h));
+        if (is_h0_triangle)
+        {
+            set_removed(f0);
+            set_removed(edge_of(h0_prev));
+        }
+        if (is_h1_triangle)
+        {
+            set_removed(f1);
+            set_removed(edge_of(h1_next));
+        }
+    }
 }
 
 inline void low_level_api_mutable::edge_rotate_next(edge_index e) const
-- 
GitLab