edge_flip.md 6.51 KB
Newer Older
TheNumbat's avatar
TheNumbat committed
1
2
3
---
layout: default
title: "Edge Flip Tutorial"
allai5's avatar
allai5 committed
4
5
6
7
permalink: /meshedit/local/edge_flip
nav_order: 1
parent: Local Operations
grand_parent: "A2: MeshEdit"
TheNumbat's avatar
TheNumbat committed
8
9
10
11
12
13
14
15
16
17
---

# Edge Flip Tutorial

Here we provide a step-by-step guide to implementing a simplified version of the _EdgeFlip_ operation for a pair of triangles---the final version, however, must be implemented for general polygons (i.e., any _n_-gon). The basic strategy for implementing the other local operations is quite similar to the procedure outlined below.

**Note:** if you're not familiar with C++, you should definitely take a moment to learn about the [standard library class](http://en.cppreference.com/w/cpp/container/vector) `std::vector`, especially the method `push_back()`, which will make it easy to accumulate a list of pointers as you walk around a polygon, vertex, etc.

We now consider the case of a triangle-triangle edge flip.

allai5's avatar
allai5 committed
18
### PHASE 0: Draw a Diagram
TheNumbat's avatar
TheNumbat committed
19
20
21

Suppose we have a pair of triangles (a,b,c) and (c,b,d). After flipping the edge (b,c), we should now have triangles (a,d,c) and (a,b,d). A good first step for implementing any local mesh operation is to draw a diagram that clearly labels all elements affected by the operation:

allai5's avatar
allai5 committed
22
<center><img src="edge_flip_diagram.png"></center>
TheNumbat's avatar
TheNumbat committed
23
24
25

Here we have drawn a diagram of the region around the edge both before and after the edge operation (in this case, "flip"), labeling each type of element (halfedge, vertex, edge, and face) from zero to the number of elements. It is important to include every element affected by the operation, thinking very carefully about which elements will be affected. If elements are omitted during this phase, everything will break---even if the code written in the two phases is correct! In this example, for instance, we need to remember to include the halfedges "outside" the neighborhood, since their "twin" pointers will be affected.

allai5's avatar
allai5 committed
26
### PHASE I: Collect elements
TheNumbat's avatar
TheNumbat committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40

Once you've drawn your diagram, simply collect all the elements from the "before" picture. Give them the same names as in your diagram, so that you can debug your code by comparing with the picture.

    // HALFEDGES
    HalfedgeRef h0 = e0->halfedge();
    HalfedgeRef h1 = h0->next();
    HalfedgeRef h2 = h1->next();
    HalfedgeRef h3 = h0->twin();
    HalfedgeRef h4 = h3->next();
    HalfedgeRef h5 = h4->next();
    HalfedgeRef h6 = h1->twin();
    HalfedgeRef h7 = h2->twin();
    HalfedgeRef h8 = h4->twin();
    HalfedgeRef h9 = h5->twin();
allai5's avatar
allai5 committed
41

TheNumbat's avatar
TheNumbat committed
42
43
44
45
    // VERTICES
    VertexRef v0 = h0->vertex();
    VertexRef v1 = h3->vertex();
    // ...you fill in the rest!...
allai5's avatar
allai5 committed
46

TheNumbat's avatar
TheNumbat committed
47
48
49
50
    // EDGES
    EdgeRef e1 = h5->edge();
    EdgeRef e2 = h4->edge();
    // ...you fill in the rest!...
allai5's avatar
allai5 committed
51

TheNumbat's avatar
TheNumbat committed
52
53
54
55
    // FACES
    FaceRef f0 = h0->face();
    // ...you fill in the rest!...

allai5's avatar
allai5 committed
56
### PHASE II: Allocate new elements
TheNumbat's avatar
TheNumbat committed
57
58
59
60
61
62
63

If your edge operation requires new elements, now is the time to allocate them. For the edge flip, we don't need any new elements; but suppose that for some reason we needed a new vertex v4\. At this point we would allocate the new vertex via

    VertexRef v4 = mesh.new_vertex();

(The name used for this new vertex should correspond to the label you give it in your "after" picture.) Likewise, new edges, halfedges, and faces can be allocated via the methods `mesh.new_edge()`, `mesh.new_halfedge()`, and `mesh.new_face()`.

allai5's avatar
allai5 committed
64
### PHASE III: Reassign Elements
TheNumbat's avatar
TheNumbat committed
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

Next, update the pointers for all the mesh elements that are affected by the edge operation. Be exhaustive! In other words, go ahead and specify every pointer for every element, even if it did not change. Once things are working correctly, you can always optimize by removing unnecessary assignments. But get it working correctly first! Correctness is more important than efficiency.

    // HALFEDGES
    h0->next() = h1;
    h0->twin() = h3;
    h0->vertex() = v2;
    h0->edge() = e0;
    h0->face() = f0;
    h1->next() = h2;
    h1->twin() = h7;
    h1->vertex() = v3;
    h1->edge() = e3;
    h1->face() = f0;
    // ...you fill in the rest!...
allai5's avatar
allai5 committed
80

TheNumbat's avatar
TheNumbat committed
81
82
83
84
85
86
    // ...and don't forget about the "outside" elements!...
    h9->next() = h9->next(); // didn't change, but set it anyway!
    h9->twin() = h4;
    h9->vertex() = v1;
    h9->edge() = e1;
    h9->face() = h9->face(); // didn't change, but set it anyway!
allai5's avatar
allai5 committed
87

TheNumbat's avatar
TheNumbat committed
88
89
90
91
92
    // VERTICES
    v0->halfedge() = h2;
    v1->halfedge() = h5;
    v2->halfedge() = h4;
    v3->halfedge() = h3;
allai5's avatar
allai5 committed
93

TheNumbat's avatar
TheNumbat committed
94
95
    // EDGES
    e0->halfedge() = h0; //...you fill in the rest!...
allai5's avatar
allai5 committed
96

TheNumbat's avatar
TheNumbat committed
97
98
99
    // FACES
    f0->halfedge() = h0; //...you fill in the rest!...

allai5's avatar
allai5 committed
100
### PHASE IV: Delete unused elements
TheNumbat's avatar
TheNumbat committed
101
102
103
104
105
106
107

If your edge operation eliminates elements, now is the best time to deallocate them: at this point, you can be sure that they are no longer needed. For instance, since we do not need the vertex allocated in PHASE II, we could write

    mesh.erase(v4);

You should be careful that this mesh element is not referenced by any other element in the mesh. But if your "before" and "after" diagrams are correct, that should not be an issue!

allai5's avatar
allai5 committed
108
### Design considerations
TheNumbat's avatar
TheNumbat committed
109
110
111
112
113
114
115
116
117

The basic algorithm outlined above will handle most edge flips, but you should also think carefully about possible corner-cases. You should also think about other design issues, like "how much should this operation cost?" For instance, for this simple triangle-triangle edge flip it might be reasonable to:

*   Ignore requests to flip boundary edges (i.e., just return immediately if either neighboring face is a boundary loop).
*   Ignore requests to perform any edge flip that would make the surface non-manifold or otherwise invalidate the mesh.
*   Not add or delete any elements. Since there are the same number of mesh elements before and after the flip, you should only need to reassign pointers.
*   Perform only a constant amount of work -- the cost of flipping a single edge should **not** be proportional to the size of the mesh!

Formally proving that your code is correct in all cases is challenging, but at least try to think about what could go wrong in degenerate cases (e.g., vertices of low degree, or very small meshes like a tetrahedron). The biggest challenge in properly implementing this type of local operation is making sure that all the pointers still point to the right place in the modified mesh, and will likely be the cause of most of your crashes! To help mitigate this, Scotty3D will automatically attempt to ``validate`` your mesh after each operation, and will warn you if it detects abnormalities. Note that it will still crash if you leave references to deleted mesh elements!