halfedge.cpp 32.9 KB
Newer Older
TheNumbat's avatar
TheNumbat committed
1
2
3
4
5
6

#include "halfedge.h"

#include <map>
#include <set>
#include <sstream>
TheNumbat's avatar
TheNumbat committed
7
#include <unordered_map>
TheNumbat's avatar
TheNumbat committed
8
9
10

#include "../gui/widgets.h"

TheNumbat's avatar
TheNumbat committed
11
12
13
14
15
Halfedge_Mesh::Halfedge_Mesh() { next_id = Gui::n_Widget_IDs; }

Halfedge_Mesh::Halfedge_Mesh(const GL::Mesh &mesh) {
    next_id = Gui::n_Widget_IDs;
    from_mesh(mesh);
TheNumbat's avatar
TheNumbat committed
16
17
}

TheNumbat's avatar
TheNumbat committed
18
19
20
21
Halfedge_Mesh::Halfedge_Mesh(const std::vector<std::vector<Index>> &polygons,
                             const std::vector<Vec3> &verts) {
    next_id = Gui::n_Widget_IDs;
    from_poly(polygons, verts);
TheNumbat's avatar
TheNumbat committed
22
23
}

TheNumbat's avatar
TheNumbat committed
24
25
26
27
28
29
30
void Halfedge_Mesh::clear() {
    halfedges.clear();
    vertices.clear();
    edges.clear();
    faces.clear();
    boundaries.clear();
    render_dirty_flag = true;
TheNumbat's avatar
TheNumbat committed
31
    next_id = Gui::n_Widget_IDs;
TheNumbat's avatar
TheNumbat committed
32
33
}

TheNumbat's avatar
TheNumbat committed
34
35
36
37
38
39
40
41
42
43
44
45
void Halfedge_Mesh::copy_to(Halfedge_Mesh &mesh) { copy_to(mesh, 0); }

Halfedge_Mesh::ElementRef Halfedge_Mesh::copy_to(Halfedge_Mesh &mesh, unsigned int eid) {

    // Clear any existing elements.
    mesh.clear();
    ElementRef ret = vertices_begin();

    // These maps will be used to identify elements of the old mesh
    // with elements of the new mesh.  (Note that we can use a single
    // map for both interior and boundary faces, because the map
    // doesn't care which list of faces these iterators come from.)
TheNumbat's avatar
TheNumbat committed
46
47
48
49
    std::unordered_map<unsigned int, HalfedgeRef> halfedgeOldToNew(n_halfedges());
    std::unordered_map<unsigned int, VertexRef> vertexOldToNew(n_vertices());
    std::unordered_map<unsigned int, EdgeRef> edgeOldToNew(n_edges());
    std::unordered_map<unsigned int, FaceRef> faceOldToNew(n_faces());
TheNumbat's avatar
TheNumbat committed
50
51
52
53
54
55
56

    // Copy geometry from the original mesh and create a map from
    // pointers in the original mesh to those in the new mesh.
    for (HalfedgeCRef h = halfedges_begin(); h != halfedges_end(); h++) {
        auto hn = mesh.halfedges.insert(mesh.halfedges.end(), *h);
        if (h->id() == eid)
            ret = hn;
TheNumbat's avatar
TheNumbat committed
57
        halfedgeOldToNew[h->id()] = hn;
TheNumbat's avatar
TheNumbat committed
58
59
60
61
62
    }
    for (VertexCRef v = vertices_begin(); v != vertices_end(); v++) {
        auto vn = mesh.vertices.insert(mesh.vertices.end(), *v);
        if (v->id() == eid)
            ret = vn;
TheNumbat's avatar
TheNumbat committed
63
        vertexOldToNew[v->id()] = vn;
TheNumbat's avatar
TheNumbat committed
64
65
66
67
68
    }
    for (EdgeCRef e = edges_begin(); e != edges_end(); e++) {
        auto en = mesh.edges.insert(mesh.edges.end(), *e);
        if (e->id() == eid)
            ret = en;
TheNumbat's avatar
TheNumbat committed
69
        edgeOldToNew[e->id()] = en;
TheNumbat's avatar
TheNumbat committed
70
71
72
73
74
    }
    for (FaceCRef f = faces_begin(); f != faces_end(); f++) {
        auto fn = mesh.faces.insert(mesh.faces.end(), *f);
        if (f->id() == eid)
            ret = fn;
TheNumbat's avatar
TheNumbat committed
75
        faceOldToNew[f->id()] = fn;
TheNumbat's avatar
TheNumbat committed
76
77
78
79
80
    }
    for (FaceCRef b = boundaries_begin(); b != boundaries_end(); b++) {
        auto bn = mesh.boundaries.insert(mesh.boundaries.end(), *b);
        if (b->id() == eid)
            ret = bn;
TheNumbat's avatar
TheNumbat committed
81
        faceOldToNew[b->id()] = bn;
TheNumbat's avatar
TheNumbat committed
82
83
84
85
    }

    // "Search and replace" old pointers with new ones.
    for (HalfedgeRef he = mesh.halfedges_begin(); he != mesh.halfedges_end(); he++) {
TheNumbat's avatar
TheNumbat committed
86
87
88
89
90
        he->next() = halfedgeOldToNew[he->next()->id()];
        he->twin() = halfedgeOldToNew[he->twin()->id()];
        he->vertex() = vertexOldToNew[he->vertex()->id()];
        he->edge() = edgeOldToNew[he->edge()->id()];
        he->face() = faceOldToNew[he->face()->id()];
TheNumbat's avatar
TheNumbat committed
91
92
    }
    for (VertexRef v = mesh.vertices_begin(); v != mesh.vertices_end(); v++)
TheNumbat's avatar
TheNumbat committed
93
        v->halfedge() = halfedgeOldToNew[v->halfedge()->id()];
TheNumbat's avatar
TheNumbat committed
94
    for (EdgeRef e = mesh.edges_begin(); e != mesh.edges_end(); e++)
TheNumbat's avatar
TheNumbat committed
95
        e->halfedge() = halfedgeOldToNew[e->halfedge()->id()];
TheNumbat's avatar
TheNumbat committed
96
    for (FaceRef f = mesh.faces_begin(); f != mesh.faces_end(); f++)
TheNumbat's avatar
TheNumbat committed
97
        f->halfedge() = halfedgeOldToNew[f->halfedge()->id()];
TheNumbat's avatar
TheNumbat committed
98
    for (FaceRef b = mesh.boundaries_begin(); b != mesh.boundaries_end(); b++)
TheNumbat's avatar
TheNumbat committed
99
        b->halfedge() = halfedgeOldToNew[b->halfedge()->id()];
TheNumbat's avatar
TheNumbat committed
100
101
102
103

    mesh.render_dirty_flag = true;
    mesh.next_id = next_id;
    return ret;
TheNumbat's avatar
TheNumbat committed
104
105
106
107
}

Vec3 Halfedge_Mesh::Vertex::neighborhood_center() const {

TheNumbat's avatar
TheNumbat committed
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
    Vec3 c;
    float d = 0.0f; // degree (i.e., number of neighbors)

    // Iterate over neighbors.
    HalfedgeCRef h = _halfedge;
    do {
        // Add the contribution of the neighbor,
        // and increment the number of neighbors.
        c += h->next()->vertex()->pos;
        d += 1.0f;
        h = h->twin()->next();
    } while (h != _halfedge);

    c /= d; // compute the average
    return c;
TheNumbat's avatar
TheNumbat committed
123
124
125
}

unsigned int Halfedge_Mesh::Vertex::degree() const {
TheNumbat's avatar
TheNumbat committed
126
127
128
129
130
131
132
133
    unsigned int d = 0;
    HalfedgeCRef h = _halfedge;
    do {
        if (!h->face()->is_boundary())
            d++;
        h = h->twin()->next();
    } while (h != _halfedge);
    return d;
TheNumbat's avatar
TheNumbat committed
134
135
136
}

unsigned int Halfedge_Mesh::Face::degree() const {
TheNumbat's avatar
TheNumbat committed
137
138
139
140
141
142
143
    unsigned int d = 0;
    HalfedgeCRef h = _halfedge;
    do {
        d++;
        h = h->next();
    } while (h != _halfedge);
    return d;
TheNumbat's avatar
TheNumbat committed
144
145
146
}

bool Halfedge_Mesh::Vertex::on_boundary() const {
TheNumbat's avatar
TheNumbat committed
147
148
149
150
151
152
153
    bool bound = false;
    HalfedgeCRef h = _halfedge;
    do {
        bound = bound || h->is_boundary();
        h = h->twin()->next();
    } while (!bound && h != _halfedge);
    return bound;
TheNumbat's avatar
TheNumbat committed
154
155
156
}

bool Halfedge_Mesh::Edge::on_boundary() const {
TheNumbat's avatar
TheNumbat committed
157
    return _halfedge->is_boundary() || _halfedge->twin()->is_boundary();
TheNumbat's avatar
TheNumbat committed
158
159
160
}

Vec3 Halfedge_Mesh::Vertex::normal() const {
TheNumbat's avatar
TheNumbat committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    Vec3 n;
    Vec3 pi = pos;
    HalfedgeCRef h = halfedge();
    if (on_boundary()) {
        do {
            Vec3 pj = h->next()->vertex()->pos;
            Vec3 pk = h->next()->next()->vertex()->pos;
            n += cross(pj - pi, pk - pi);
            h = h->next()->twin();
        } while (h != halfedge());
    } else {
        do {
            Vec3 pj = h->next()->vertex()->pos;
            Vec3 pk = h->next()->next()->vertex()->pos;
            n += cross(pj - pi, pk - pi);
            h = h->twin()->next();
        } while (h != halfedge());
    }
    return n.unit();
TheNumbat's avatar
TheNumbat committed
180
181
182
}

Vec3 Halfedge_Mesh::Edge::normal() const {
TheNumbat's avatar
TheNumbat committed
183
    return (halfedge()->face()->normal() + halfedge()->twin()->face()->normal()).unit();
TheNumbat's avatar
TheNumbat committed
184
185
186
}

Vec3 Halfedge_Mesh::Face::normal() const {
TheNumbat's avatar
TheNumbat committed
187
188
189
190
191
192
193
194
195
    Vec3 n;
    HalfedgeCRef h = halfedge();
    do {
        Vec3 pi = h->vertex()->pos;
        Vec3 pj = h->next()->vertex()->pos;
        n += cross(pi, pj);
        h = h->next();
    } while (h != halfedge());
    return n.unit();
TheNumbat's avatar
TheNumbat committed
196
197
}

TheNumbat's avatar
TheNumbat committed
198
Vec3 Halfedge_Mesh::Vertex::center() const { return pos; }
TheNumbat's avatar
TheNumbat committed
199
200

float Halfedge_Mesh::Edge::length() const {
TheNumbat's avatar
TheNumbat committed
201
    return (_halfedge->vertex()->pos - _halfedge->twin()->vertex()->pos).norm();
TheNumbat's avatar
TheNumbat committed
202
203
204
}

Vec3 Halfedge_Mesh::Edge::center() const {
TheNumbat's avatar
TheNumbat committed
205
    return 0.5f * (_halfedge->vertex()->pos + _halfedge->twin()->vertex()->pos);
TheNumbat's avatar
TheNumbat committed
206
207
208
}

Vec3 Halfedge_Mesh::Face::center() const {
TheNumbat's avatar
TheNumbat committed
209
210
211
212
213
214
215
216
217
    Vec3 c;
    float d = 0.0f;
    HalfedgeCRef h = _halfedge;
    do {
        c += h->vertex()->pos;
        d += 1.0f;
        h = h->next();
    } while (h != _halfedge);
    return c / d;
TheNumbat's avatar
TheNumbat committed
218
219
220
}

unsigned int Halfedge_Mesh::id_of(ElementRef elem) {
TheNumbat's avatar
TheNumbat committed
221
222
223
224
225
226
    unsigned int id;
    std::visit(overloaded{[&](Halfedge_Mesh::VertexRef vert) { id = vert->id(); },
                          [&](Halfedge_Mesh::EdgeRef edge) { id = edge->id(); },
                          [&](Halfedge_Mesh::FaceRef face) { id = face->id(); }, [&](auto) {}},
               elem);
    return id;
TheNumbat's avatar
TheNumbat committed
227
228
229
}

Vec3 Halfedge_Mesh::normal_of(Halfedge_Mesh::ElementRef elem) {
TheNumbat's avatar
TheNumbat committed
230
231
232
233
234
235
236
237
238
239
    Vec3 n;
    std::visit(overloaded{[&](Halfedge_Mesh::VertexRef vert) { n = vert->normal(); },
                          [&](Halfedge_Mesh::EdgeRef edge) { n = edge->normal(); },
                          [&](Halfedge_Mesh::FaceRef face) { n = face->normal(); },
                          [&](Halfedge_Mesh::HalfedgeRef he) { n = he->edge()->normal(); }},
               elem);
    if (flip_orientation) {
        n = -n;
    }
    return n;
TheNumbat's avatar
TheNumbat committed
240
241
242
}

Vec3 Halfedge_Mesh::center_of(Halfedge_Mesh::ElementRef elem) {
TheNumbat's avatar
TheNumbat committed
243
244
245
246
247
248
249
    Vec3 pos;
    std::visit(overloaded{[&](Halfedge_Mesh::VertexRef vert) { pos = vert->center(); },
                          [&](Halfedge_Mesh::EdgeRef edge) { pos = edge->center(); },
                          [&](Halfedge_Mesh::FaceRef face) { pos = face->center(); },
                          [&](Halfedge_Mesh::HalfedgeRef he) { pos = he->edge()->center(); }},
               elem);
    return pos;
TheNumbat's avatar
TheNumbat committed
250
251
}

TheNumbat's avatar
TheNumbat committed
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
void Halfedge_Mesh::to_mesh(GL::Mesh &mesh, bool split_faces) const {

    std::vector<GL::Mesh::Vert> verts;
    std::vector<GL::Mesh::Index> idxs;

    if (split_faces) {

        std::vector<GL::Mesh::Vert> face_verts;
        for (FaceCRef f = faces_begin(); f != faces_end(); f++) {

            if (f->is_boundary())
                continue;

            HalfedgeCRef h = f->halfedge();
            face_verts.clear();
            do {
                VertexCRef v = h->vertex();
                Vec3 n = v->normal();
                if (flip_orientation)
                    n = -n;
                face_verts.push_back({v->pos, n, f->id()});
                h = h->next();
            } while (h != f->halfedge());

            Vec3 v0 = face_verts[0].pos;
            for (size_t i = 1; i <= face_verts.size() - 2; i++) {
                Vec3 v1 = face_verts[i].pos;
                Vec3 v2 = face_verts[i + 1].pos;
                Vec3 n = cross(v1 - v0, v2 - v0).unit();
                if (flip_orientation)
                    n = -n;
                GL::Mesh::Index idx = (GL::Mesh::Index)verts.size();
                verts.push_back({v0, n, f->_id});
                verts.push_back({v1, n, f->_id});
                verts.push_back({v2, n, f->_id});
                idxs.push_back(idx);
                idxs.push_back(idx + 1);
                idxs.push_back(idx + 2);
            }
        }

    } else {

        // Need to build this map to get vertex's linear index in O(lg n)
        std::map<VertexCRef, Index> vref_to_idx;
        Index i = 0;
        for (VertexCRef v = vertices_begin(); v != vertices_end(); v++, i++) {
            vref_to_idx[v] = i;
            Vec3 n = v->normal();
            if (flip_orientation)
                n = -n;
            verts.push_back({v->pos, n, v->_id});
        }

        for (FaceCRef f = faces_begin(); f != faces_end(); f++) {

            if (f->is_boundary())
                continue;

            std::vector<Index> face_verts;
            HalfedgeCRef h = f->halfedge();
            do {
                face_verts.push_back(vref_to_idx[h->vertex()]);
                h = h->next();
            } while (h != f->halfedge());

            assert(face_verts.size() >= 3);
            for (size_t j = 1; j <= face_verts.size() - 2; j++) {
                idxs.push_back((GL::Mesh::Index)face_verts[0]);
                idxs.push_back((GL::Mesh::Index)face_verts[j]);
                idxs.push_back((GL::Mesh::Index)face_verts[j + 1]);
            }
        }
    }

    mesh = GL::Mesh(std::move(verts), std::move(idxs));
TheNumbat's avatar
TheNumbat committed
328
329
}

TheNumbat's avatar
TheNumbat committed
330
void Halfedge_Mesh::mark_dirty() { render_dirty_flag = true; }
TheNumbat's avatar
TheNumbat committed
331
332

std::string Halfedge_Mesh::validate() const {
TheNumbat's avatar
TheNumbat committed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412

    if (!check_finite())
        return "A vertex position was set to a non-finite value.";

    std::set<HalfedgeCRef> permutation;

    // Check valid halfedge permutation
    for (HalfedgeCRef h = halfedges_begin(); h != halfedges_end(); h++) {

        // Check whether each halfedge's next points to a unique halfedge
        if (permutation.find(h->next()) == permutation.end()) {
            permutation.insert(h->next());
        } else {
            return "A halfedge is the next of more than one halfedge!";
        }
    }
    for (HalfedgeCRef h = halfedges_begin(); h != halfedges_end(); h++) {

        // Check whether each halfedge was pointed to by a halfedge
        if (permutation.find(h) == permutation.end()) {
            return "A halfedge is the next of zero halfedges!";
        }
    }

    // Check twin relationships
    for (HalfedgeCRef h = halfedges_begin(); h != halfedges_end(); h++) {

        if (h->twin() == h) {
            return "A halfedge's twin points to itself!";
        }
        if (h->twin()->twin() != h) {
            return "A halfedge's twin's twin does not point to itself!";
        }
    }

    // Check whether each halfedge incident on a vertex points to that vertex
    for (VertexCRef v = vertices_begin(); v != vertices_end(); v++) {
        HalfedgeCRef h = v->halfedge();
        do {
            if (h->vertex() != v) {
                return "A halfedge does not point to its vertex!";
            }
            h = h->twin()->next();
        } while (h != v->halfedge());
    }

    // Check whether each halfedge incident on an edge points to that edge
    for (EdgeCRef e = edges_begin(); e != edges_end(); e++) {
        HalfedgeCRef h = e->halfedge();
        do {
            if (h->edge() != e) {
                return "A halfedge does not point to its edge!";
            }
            h = h->twin();
        } while (h != e->halfedge());
    }

    // Check whether each halfedge incident on a face points to that face
    for (FaceCRef f = faces_begin(); f != faces_end(); f++) {
        HalfedgeCRef h = f->halfedge();
        do {
            if (h->face() != f) {
                return "A halfedge does not point to its face!";
            }
            h = h->next();
        } while (h != f->halfedge());
    }

    // Check whether each halfedge incident on a boundary loop points to that boundary loop
    for (FaceCRef b = boundaries_begin(); b != boundaries_end(); b++) {
        HalfedgeCRef h = b->halfedge();
        do {
            if (h->face() != b) {
                return "A halfedge does not point to its boundary loop!";
            }
            h = h->next();
        } while (h != b->halfedge());
    }

    return {};
TheNumbat's avatar
TheNumbat committed
413
414
}

TheNumbat's avatar
TheNumbat committed
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
std::string Halfedge_Mesh::from_mesh(const GL::Mesh &mesh) {

    auto idx = mesh.indices();
    auto v = mesh.verts();

    std::vector<std::vector<Index>> polys;
    std::vector<Vec3> verts(v.size());

    for (size_t i = 0; i < idx.size(); i += 3) {
        if (idx[i] != idx[i + 1] && idx[i] != idx[i + 2] && idx[i + 1] != idx[i + 2])
            polys.push_back({idx[i], idx[i + 1], idx[i + 2]});
    }
    for (size_t i = 0; i < v.size(); i++) {
        verts[i] = v[i].pos;
    }

    std::string err = from_poly(polys, verts);
    if (!err.empty())
        return err;

    err = validate();
    if (!err.empty())
        return err;

    return {};
TheNumbat's avatar
TheNumbat committed
440
441
442
443
}

bool Halfedge_Mesh::check_finite() const {

TheNumbat's avatar
TheNumbat committed
444
445
446
447
448
449
450
    for (VertexCRef v = vertices_begin(); v != vertices_end(); v++) {
        Vec3 p = v->pos;
        bool finite = std::isfinite(p.x) && std::isfinite(p.y) && std::isfinite(p.z);
        if (!finite)
            return false;
    }
    return true;
TheNumbat's avatar
TheNumbat committed
451
452
453
454
}

bool Halfedge_Mesh::subdivide(SubD strategy) {

TheNumbat's avatar
TheNumbat committed
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
    std::vector<std::vector<Index>> polys;
    std::vector<Vec3> verts;
    std::unordered_map<unsigned int, Index> layout;

    switch (strategy) {
    case SubD::linear: {
        linear_subdivide_positions();
    } break;

    case SubD::catmullclark: {
        if (boundaries.size())
            return false;
        catmullclark_subdivide_positions();
    } break;

    case SubD::loop: {
TheNumbat's avatar
TheNumbat committed
471
472
        if (boundaries.size())
            return false;
TheNumbat's avatar
TheNumbat committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
        for (FaceRef f = faces_begin(); f != faces_end(); f++) {
            if (f->degree() != 3)
                return false;
        }
        loop_subdivide();
        return true;
    } break;

    default:
        assert(false);
    }

    Index idx = 0;
    size_t nV = vertices.size();
    size_t nE = edges.size();
    size_t nF = faces.size();
    verts.resize(nV + nE + nF);

    for (VertexRef v = vertices_begin(); v != vertices_end(); v++, idx++) {
        verts[idx] = v->new_pos;
        layout[v->id()] = idx;
    }
    for (EdgeRef e = edges_begin(); e != edges_end(); e++, idx++) {
        verts[idx] = e->new_pos;
        layout[e->id()] = idx;
    }
    for (FaceRef f = faces_begin(); f != faces_end(); f++, idx++) {
        verts[idx] = f->new_pos;
        layout[f->id()] = idx;
    }

    for (auto f = faces_begin(); f != faces_end(); f++) {
        Index i = layout[f->id()];
        HalfedgeRef h = f->halfedge();
        do {
            Index j = layout[h->edge()->id()];
            Index k = layout[h->next()->vertex()->id()];
            Index l = layout[h->next()->edge()->id()];
            std::vector<Index> quad = {i, j, k, l};
            polys.push_back(quad);
            h = h->next();
        } while (h != f->halfedge());
    }

    from_poly(polys, verts);
    return true;
TheNumbat's avatar
TheNumbat committed
519
520
}

TheNumbat's avatar
TheNumbat committed
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
std::string Halfedge_Mesh::from_poly(const std::vector<std::vector<Index>> &polygons,
                                     const std::vector<Vec3> &verts) {

    // This method initializes the halfedge data structure from a raw list of
    // polygons, where each input polygon is specified as a list of vertex indices.
    // The input must describe a manifold, oriented surface, where the orientation
    // of a polygon is determined by the order of vertices in the list. Polygons
    // must have at least three vertices.  Note that there are no special conditions
    // on the vertex indices, i.e., they do not have to start at 0 or 1, nor does
    // the collection of indices have to be contiguous.  Overall, this initializer
    // is designed to be robust but perhaps not incredibly fast (though of course
    // this does not affect the performance of the resulting data structure).  One
    // could also implement faster initializers that handle important special cases
    // (e.g., all triangles, or data that is known to be manifold). Since there are
    // no strong conditions on the indices of polygons, we assume that the list of
    // vertex positions is given in lexicographic order (i.e., that the lowest index
    // appearing in any polygon corresponds to the first entry of the list of
    // positions and so on).

    // define some types, to improve readability
    typedef std::vector<Index> IndexList;
    typedef IndexList::const_iterator IndexListCRef;
    typedef std::vector<IndexList> PolygonList;
    typedef PolygonList::const_iterator PolygonListCRef;
    typedef std::pair<Index, Index> IndexPair; // ordered pair of vertex indices,
                                               // corresponding to an edge of an
                                               // oriented polygon

    // Clear any existing elements.
    clear();

    // Since the vertices in our halfedge mesh are stored in a linked list,
    // we will temporarily need to keep track of the correspondence between
    // indices of vertices in our input and pointers to vertices in the new
    // mesh (which otherwise can't be accessed by index).  Note that since
    // we're using a general-purpose map (rather than, say, a vector), we can
    // be a bit more flexible about the indexing scheme: input vertex indices
    // aren't required to be 0-based or 1-based; in fact, the set of indices
    // doesn't even have to be contiguous.  Taking advantage of this fact makes
    // our conversion a bit more robust to different types of input, including
    // data that comes from a subset of a full mesh.

    // maps a vertex index to the corresponding vertex
    std::map<Index, VertexRef> indexToVertex;

    // Also store the vertex degree, i.e., the number of polygons that use each
    // vertex; this information will be used to check that the mesh is manifold.
    std::map<VertexRef, Size> vertexDegree;

    // First, we do some basic sanity checks on the input.
    for (PolygonListCRef p = polygons.begin(); p != polygons.end(); p++) {
        if (p->size() < 3) {
            // Refuse to build the mesh if any of the polygons have fewer than three
            // vertices.(Note that if we omit this check the code will still
            // constructsomething fairlymeaningful for 1- and 2-point polygons, but
            // enforcing this stricter requirement on the input will help simplify code
            // further downstream, since it can be certain it doesn't have to check for
            // these rather degenerate cases.)
            return "Each polygon must have at least three vertices.";
        }

        // We want to count the number of distinct vertex indices in this
        // polygon, to make sure it's the same as the number of vertices
        // in the polygon---if they disagree, then the polygon is not valid
        // (or at least, for simplicity we don't handle polygons of this type!).
        std::set<Index> polygonIndices;

        // loop over polygon vertices
        for (IndexListCRef i = p->begin(); i != p->end(); i++) {
            polygonIndices.insert(*i);

            // allocate one vertex for each new index we encounter
            if (indexToVertex.find(*i) == indexToVertex.end()) {
                VertexRef v = new_vertex();
                v->halfedge() = halfedges.end(); // this vertex doesn't yet point to any halfedge
                indexToVertex[*i] = v;
                vertexDegree[v] = 1; // we've now seen this vertex only once
            } else {
                // keep track of the number of times we've seen this vertex
                vertexDegree[indexToVertex[*i]]++;
            }
        } // end loop over polygon vertices

        // check that all vertices of the current polygon are distinct
        Size degree = p->size(); // number of vertices in this polygon
        if (polygonIndices.size() < degree) {
            std::stringstream stream;
            stream << "One of the input polygons does not have distinct vertices!" << std::endl;
            stream << "(vertex indices:";
            for (IndexListCRef i = p->begin(); i != p->end(); i++) {
                stream << " " << *i;
            }
            stream << ")" << std::endl;
            return stream.str();
        } // end check that polygon vertices are distinct

    } // end basic sanity checks on input

    // The number of faces is just the number of polygons in the input.
    Size nFaces = polygons.size();
    for (Size i = 0; i < nFaces; i++)
        new_face();

    // We will store a map from ordered pairs of vertex indices to
    // the corresponding halfedge object in our new (halfedge) mesh;
    // this map gets constructed during the next loop over polygons.
    std::map<IndexPair, HalfedgeRef> pairToHalfedge;

    // Next, we actually build the halfedge connectivity by again looping over
    // polygons
    PolygonListCRef p;
    FaceRef f;
    for (p = polygons.begin(), f = faces.begin(); p != polygons.end(); p++, f++) {

        std::vector<HalfedgeRef> faceHalfedges; // cyclically ordered list of the half
                                                // edges of this face
        Size degree = p->size();                // number of vertices in this polygon

        // loop over the halfedges of this face (equivalently, the ordered pairs of
        // consecutive vertices)
        for (Index i = 0; i < degree; i++) {

            Index a = (*p)[i];                // current index
            Index b = (*p)[(i + 1) % degree]; // next index, in cyclic order
            IndexPair ab(a, b);
            HalfedgeRef hab;

            // check if this halfedge already exists; if so, we have a problem!
            if (pairToHalfedge.find(ab) != pairToHalfedge.end()) {
                std::stringstream stream;
                stream << "Found multiple oriented edges with indices (" << a << ", " << b << ")."
                       << std::endl;
                stream << "This means that either (i) more than two faces contain this "
                          "edge (hence the surface is nonmanifold), or"
                       << std::endl;
                stream << "(ii) there are exactly two faces containing this edge, but "
                          "they have the same orientation (hence the surface is"
                       << std::endl;
                stream << "not consistently oriented." << std::endl;
                return stream.str();
            } else // otherwise, the halfedge hasn't been allocated yet
            {
                // so, we point this vertex pair to a new halfedge
                hab = new_halfedge();
                pairToHalfedge[ab] = hab;

                // link the new halfedge to its face
                hab->face() = f;
                hab->face()->halfedge() = hab;

                // also link it to its starting vertex
                hab->vertex() = indexToVertex[a];
                hab->vertex()->halfedge() = hab;

                // keep a list of halfedges in this face, so that we can later
                // link them together in a loop (via their "next" pointers)
                faceHalfedges.push_back(hab);
            }

            // Also, check if the twin of this halfedge has already been constructed
            // (during construction of a different face).  If so, link the twins
            // together and allocate their shared halfedge.  By the end of this pass
            // over polygons, the only halfedges that will not have a twin will hence
            // be those that sit along the domain boundary.
            IndexPair ba(b, a);
            std::map<IndexPair, HalfedgeRef>::iterator iba = pairToHalfedge.find(ba);
            if (iba != pairToHalfedge.end()) {
                HalfedgeRef hba = iba->second;

                // link the twins
                hab->twin() = hba;
                hba->twin() = hab;

                // allocate and link their edge
                EdgeRef e = new_edge();
                hab->edge() = e;
                hba->edge() = e;
                e->halfedge() = hab;
            } else { // If we didn't find a twin...
                // ...mark this halfedge as being twinless by pointing
                // it to the end of the list of halfedges. If it remains
                // twinless by the end of the current loop over polygons,
                // it will be linked to a boundary face in the next pass.
                hab->twin() = halfedges.end();
            }
        } // end loop over the current polygon's halfedges

        // Now that all the halfedges of this face have been allocated,
        // we can link them together via their "next" pointers.
        for (Index i = 0; i < degree; i++) {
            Index j = (i + 1) % degree; // index of the next halfedge, in cyclic order
            faceHalfedges[i]->next() = faceHalfedges[j];
        }

    } // done building basic halfedge connectivity

    // For each vertex on the boundary, advance its halfedge pointer to one that
    // is also on the boundary.
    for (VertexRef v = vertices_begin(); v != vertices_end(); v++) {
        // loop over halfedges around vertex
        HalfedgeRef h = v->halfedge();
        do {
            if (h->twin() == halfedges.end()) {
                v->halfedge() = h;
                break;
            }

            h = h->twin()->next();
        } while (h != v->halfedge()); // end loop over halfedges around vertex

    } // done advancing halfedge pointers for boundary vertices

    // Next we construct new faces for each boundary component.
    for (HalfedgeRef h = halfedges_begin(); h != halfedges_end(); h++) // loop over all halfedges
    {
        // Any halfedge that does not yet have a twin is on the boundary of the
        // domain. If we follow the boundary around long enough we will of course
        // eventually make a closed loop; we can represent this boundary loop by a
        // new face. To make clear the distinction between faces and boundary loops,
        // the boundary face will (i) have a flag indicating that it is a boundary
        // loop, and (ii) be stored in a list of boundaries, rather than the usual
        // list of faces.  The reason we need the both the flag *and* the separate
        // list is that faces are often accessed in two fundamentally different
        // ways: either by (i) local traversal of the neighborhood of some mesh
        // element using the halfedge structure, or (ii) global traversal of all
        // faces (or boundary loops).
        if (h->twin() == halfedges.end()) {
            FaceRef b = new_boundary();
            // keep a list of halfedges along the boundary, so we can link them together
            std::vector<HalfedgeRef> boundaryHalfedges;

            // We now need to walk around the boundary, creating new
            // halfedges and edges along the boundary loop as we go.
            HalfedgeRef i = h;
            do {
                // create a twin, which becomes a halfedge of the boundary loop
                HalfedgeRef t = new_halfedge();
                // keep a list of all boundary halfedges, in cyclic order
                boundaryHalfedges.push_back(t);
                i->twin() = t;
                t->twin() = i;
                t->face() = b;
                t->vertex() = i->next()->vertex();

                // create the shared edge
                EdgeRef e = new_edge();
                e->halfedge() = i;
                i->edge() = e;
                t->edge() = e;

                // Advance i to the next halfedge along the current boundary loop
                // by walking around its target vertex and stopping as soon as we
                // find a halfedge that does not yet have a twin defined.
                i = i->next();
                while (i != h && // we're done if we end up back at the beginning of
                                 // the loop
                       i->twin() != halfedges.end()) // otherwise, we're looking for
                                                     // the next twinless halfedge
                                                     // along the loop
                {
                    i = i->twin();
                    i = i->next();
                }
            } while (i != h);

            b->halfedge() = boundaryHalfedges.front();

            // The only pointers that still need to be set are the "next" pointers of
            // the twins; these we can set from the list of boundary halfedges, but we
            // must use the opposite order from the order in the list, since the
            // orientation of the boundary loop is opposite the orientation of the
            // halfedges "inside" the domain boundary.
            Size degree = boundaryHalfedges.size();
            for (Index id = 0; id < degree; id++) {
                Index q = (id - 1 + degree) % degree;
                boundaryHalfedges[id]->next() = boundaryHalfedges[q];
            }
        } // end construction of one of the boundary loops

        // Note that even though we are looping over all halfedges, we will still
        // construct the appropriate number of boundary loops (and not, say, one
        // loop per boundary halfedge).  The reason is that as we continue to
        // iterate through halfedges, we check whether their twin has been assigned,
        // and since new twins may have been assigned earlier in this loop, we will
        // end up skipping many subsequent halfedges.

    } // done adding "virtual" faces corresponding to boundary loops

    // To make later traversal of the mesh easier, we will now advance the
    // halfedge
    // associated with each vertex such that it refers to the *first* non-boundary
    // halfedge, rather than the last one.
    for (VertexRef v = vertices_begin(); v != vertices_end(); v++) {
        v->halfedge() = v->halfedge()->twin()->next();
    }

    // Finally, we check that all vertices are manifold.
    for (VertexRef v = vertices.begin(); v != vertices.end(); v++) {
        // First check that this vertex is not a "floating" vertex;
        // if it is then we do not have a valid 2-manifold surface.
        if (v->halfedge() == halfedges.end()) {
            return "Some vertices are not referenced by any polygon.";
        }

        // Next, check that the number of halfedges emanating from this vertex in
        // our half edge data structure equals the number of polygons containing
        // this vertex, which we counted during our first pass over the mesh.  If
        // not, then our vertex is not a "fan" of polygons, but instead has some
        // other (nonmanifold) structure.
        Size count = 0;
        HalfedgeRef h = v->halfedge();
        do {
            if (!h->face()->is_boundary()) {
                count++;
            }
            h = h->twin()->next();
        } while (h != v->halfedge());

        Size cmp = vertexDegree[v];
        if (count != cmp) {
            return "At least one of the vertices is nonmanifold.";
        }
    } // end loop over vertices

    // Now that we have the connectivity, we copy the list of vertex
    // positions into member variables of the individual vertices.
    if (verts.size() < vertices.size()) {
        std::stringstream stream;
        stream
            << "The number of vertex positions is different from the number of distinct vertices!"
            << std::endl;
        stream << "(number of positions in input: " << verts.size() << ")" << std::endl;
        stream << "(number of vertices in mesh: " << vertices.size() << ")" << std::endl;
        return stream.str();
    }

    // Since an STL map internally sorts its keys, we can iterate over the map
    // from vertex indices to vertex iterators to visit our (input) vertices in
    // lexicographic order
    int i = 0;
    for (std::map<Index, VertexRef>::const_iterator e = indexToVertex.begin();
         e != indexToVertex.end(); e++) {
        // grab a pointer to the vertex associated with the current key (i.e., the
        // current index)
        VertexRef v = e->second;

        // set the att of this vertex to the corresponding
        // position in the input
        v->pos = verts[i];
        i++;
    }
    return {};
TheNumbat's avatar
TheNumbat committed
873
}