Mesh.hh 48.3 KB
Newer Older
Philip Trettner's avatar
Philip Trettner committed
1
2
#pragma once

Philip Trettner's avatar
Philip Trettner committed
3
#include <cstddef>
4
#include <memory>
Philip Trettner's avatar
Philip Trettner committed
5
6
#include <vector>

7
#include "attributes.hh"
Philip Trettner's avatar
Philip Trettner committed
8
#include "cursors.hh"
9
#include "ranges.hh"
Philip Trettner's avatar
Philip Trettner committed
10

Philip Trettner's avatar
Philip Trettner committed
11
12
namespace polymesh
{
13
14
using SharedMesh = std::shared_ptr<Mesh>;

Philip Trettner's avatar
Philip Trettner committed
15
/**
Philip Trettner's avatar
Philip Trettner committed
16
 * @brief Half-edge Mesh Data Structure
17
 *
Philip Trettner's avatar
Philip Trettner committed
18
19
 *  * Primitives are accessed via the smart collections mesh.<primitive>()
 *    (where <primitive> can be vertices, edges, faces, or halfedges)
20
 *
Philip Trettner's avatar
Philip Trettner committed
21
22
 *  * Primitives can be added via <primitive>().add()
 *    (added primitives are at the end of the collection)
Philip Trettner's avatar
Philip Trettner committed
23
 *
Philip Trettner's avatar
Philip Trettner committed
24
25
 *  * Primitives can be removed via <primitive>().remove(...)
 *    (removed primitives are invalidated (flagged for removal). call compactify() to remove them)
26
 *
Philip Trettner's avatar
Philip Trettner committed
27
28
29
30
31
32
 *  * `for (auto h : <primitive>())` iterates over _all_ primitives, including invalid ones
 *    (`for (auto h : valid_<primitive>())` skips over invalid ones)
 *
 * For more concept documents see:
 *  * http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm
 *  * https://www.openmesh.org/media/Documentations/OpenMesh-Doc-Latest/a03930.html
Philip Trettner's avatar
Philip Trettner committed
33
 */
Philip Trettner's avatar
Philip Trettner committed
34
class Mesh
Philip Trettner's avatar
Philip Trettner committed
35
{
Philip Trettner's avatar
Philip Trettner committed
36
37
    // accessors and iterators
public:
Philip Trettner's avatar
Philip Trettner committed
38
    /// smart collections for primitives INCLUDING (flagged-to-)removed ones
39
    /// Also primary interfaces for querying size and adding primitives
Philip Trettner's avatar
Philip Trettner committed
40
    ///
Philip Trettner's avatar
Philip Trettner committed
41
42
43
    /// CAUTION: includes removed ones!
    ///   use compactify() to ensure that no removed ones exist
    ///   use valid_<primitive>() to skip removed ones during iteration (slower)
Philip Trettner's avatar
Philip Trettner committed
44
    ///
45
    /// NOTE: adding primitives does NOT invalidate ranges. (newly added ones are NOT processed though)
Philip Trettner's avatar
Philip Trettner committed
46
    ///       deleting primitives does NOT invalidate ranges.
47
48
49
50
    vertex_collection vertices() { return {this}; }
    face_collection faces() { return {this}; }
    edge_collection edges() { return {this}; }
    halfedge_collection halfedges() { return {this}; }
51
52
53
54
55
    // const versions:
    const_vertex_collection vertices() const { return {this}; }
    const_face_collection faces() const { return {this}; }
    const_edge_collection edges() const { return {this}; }
    const_halfedge_collection halfedges() const { return {this}; }
Philip Trettner's avatar
Philip Trettner committed
56

Philip Trettner's avatar
Philip Trettner committed
57
    /// smart collections for VALID primitives (EXCLUDING removed ones)
58
59
60
61
62
63
64
65
66
    ///
    /// NOTE: if mesh.is_compact() is guaranteed, <primitive>() is faster than valid_<primitive>()
    ///
    /// NOTE: adding primitives does NOT invalidate ranges. (newly added ones are NOT processed though)
    ///       deleting primitives does NOT invalidate ranges. (they will be skipped)
    valid_vertex_collection valid_vertices() const { return {this}; }
    valid_face_collection valid_faces() const { return {this}; }
    valid_edge_collection valid_edges() const { return {this}; }
    valid_halfedge_collection valid_halfedges() const { return {this}; }
Philip Trettner's avatar
Philip Trettner committed
67

Philip Trettner's avatar
Philip Trettner committed
68
69
70
71
72
73
74
75
76
77
78
79
    /// get handle from index
    face_handle handle_of(face_index idx) const { return {this, idx}; }
    edge_handle handle_of(edge_index idx) const { return {this, idx}; }
    vertex_handle handle_of(vertex_index idx) const { return {this, idx}; }
    halfedge_handle handle_of(halfedge_index idx) const { return {this, idx}; }

    /// get handle from index, subscript version
    face_handle operator[](face_index idx) const { return handle_of(idx); }
    edge_handle operator[](edge_index idx) const { return handle_of(idx); }
    vertex_handle operator[](vertex_index idx) const { return handle_of(idx); }
    halfedge_handle operator[](halfedge_index idx) const { return handle_of(idx); }

Philip Trettner's avatar
Philip Trettner committed
80
81
82
83
    // helper
public:
    /// Returns true if the mesh is guaranteed compact, otherwise call compactify() to be sure
    bool is_compact() const { return mCompact; }
Philip Trettner's avatar
Philip Trettner committed
84
    /// Removes all invalid/removed primitives
Philip Trettner's avatar
Philip Trettner committed
85
86
87
88
89
90
    /// NOTE: cheap no-op if already compact
    void compactify();

    /// Asserts that mesh invariants hold, e.g. that the half-edge stored in a face actually bounds that face
    void assert_consistency() const;

91
92
93
94
    // ctor
public:
    Mesh() = default;

95
    /// Meshes can be neither moved nor copied because attributes depend on the Mesh address
96
97
98
99
100
101
102
103
104
105
    Mesh(Mesh const &) = delete;
    Mesh(Mesh &&) = delete;
    Mesh &operator=(Mesh const &) = delete;
    Mesh &operator=(Mesh &&) = delete;

    /// Creates a new mesh and returns a shared_ptr to it
    static SharedMesh create() { return std::make_shared<Mesh>(); }

    // internal helper
private:
Philip Trettner's avatar
Philip Trettner committed
106
107
108
    // reserves a certain number of primitives
    void reserve_faces(size_t capacity) { mFaces.reserve(capacity); }
    void reserve_vertices(size_t capacity) { mVertices.reserve(capacity); }
109
    void reserve_edges(size_t capacity) { mHalfedges.reserve(capacity * 2); }
Philip Trettner's avatar
Philip Trettner committed
110
111
    void reserve_halfedges(size_t capacity) { mHalfedges.reserve(capacity); }

Philip Trettner's avatar
Philip Trettner committed
112
113
114
115
    int size_faces() const { return (int)mFaces.size(); }
    int size_vertices() const { return (int)mVertices.size(); }
    int size_edges() const { return (int)mHalfedges.size() >> 1; }
    int size_halfedges() const { return (int)mHalfedges.size(); }
116

Philip Trettner's avatar
Philip Trettner committed
117
118
119
120
    int size_valid_faces() const { return (int)mFaces.size() - mRemovedFaces; }
    int size_valid_vertices() const { return (int)mVertices.size() - mRemovedVertices; }
    int size_valid_edges() const { return ((int)mHalfedges.size() - mRemovedHalfedges) >> 1; }
    int size_valid_halfedges() const { return (int)mHalfedges.size() - mRemovedHalfedges; }
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

    // returns the next valid idx (returns the given one if valid)
    // NOTE: the result can be invalid if no valid one was found
    vertex_index next_valid_idx_from(vertex_index idx) const;
    edge_index next_valid_idx_from(edge_index idx) const;
    face_index next_valid_idx_from(face_index idx) const;
    halfedge_index next_valid_idx_from(halfedge_index idx) const;
    // returns the next valid idx (returns the given one if valid) counting DOWNWARDS
    vertex_index prev_valid_idx_from(vertex_index idx) const;
    edge_index prev_valid_idx_from(edge_index idx) const;
    face_index prev_valid_idx_from(face_index idx) const;
    halfedge_index prev_valid_idx_from(halfedge_index idx) const;

    // Iterators
    vertex_iterator vertices_begin() const { return {{this, vertex_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
136
    vertex_iterator vertices_end() const { return {{this, vertex_index(size_vertices())}}; }
137
    valid_vertex_iterator valid_vertices_begin() const { return {{this, vertex_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
138
    valid_vertex_iterator valid_vertices_end() const { return {{this, vertex_index(size_vertices())}}; }
139
140

    face_iterator faces_begin() const { return {{this, face_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
141
    face_iterator faces_end() const { return {{this, face_index(size_faces())}}; }
142
    valid_face_iterator valid_faces_begin() const { return {{this, face_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
143
    valid_face_iterator valid_faces_end() const { return {{this, face_index(size_faces())}}; }
144
145

    edge_iterator edges_begin() const { return {{this, edge_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
146
    edge_iterator edges_end() const { return {{this, edge_index(size_edges())}}; }
147
    valid_edge_iterator valid_edges_begin() const { return {{this, edge_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
148
    valid_edge_iterator valid_edges_end() const { return {{this, edge_index(size_edges())}}; }
149
150

    halfedge_iterator halfedges_begin() const { return {{this, halfedge_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
151
    halfedge_iterator halfedges_end() const { return {{this, halfedge_index(size_halfedges())}}; }
152
    valid_halfedge_iterator valid_halfedges_begin() const { return {{this, halfedge_index(0)}}; }
Philip Trettner's avatar
Philip Trettner committed
153
    valid_halfedge_iterator valid_halfedges_end() const { return {{this, halfedge_index(size_halfedges())}}; }
Philip Trettner's avatar
Philip Trettner committed
154

Philip Trettner's avatar
Philip Trettner committed
155
156
157
158
159
160
161
    /// Adds a single non-connected vertex
    /// Does NOT invalidate iterators!
    vertex_index add_vertex();

    /// Adds a face consisting of N vertices
    /// The vertices must already be sorted in CCW order
    /// (note: trying to add already existing halfedges triggers assertions)
Philip Trettner's avatar
Philip Trettner committed
162
163
164
165
    face_index add_face(vertex_handle const *v_handles, size_t vcnt);
    face_index add_face(vertex_index const *v_indices, size_t vcnt);
    face_index add_face(halfedge_handle const *half_loop, size_t vcnt);
    face_index add_face(halfedge_index const *half_loop, size_t vcnt);
Philip Trettner's avatar
Philip Trettner committed
166
167
168
169
170

    /// Adds an edge between two existing, distinct vertices
    /// if edge already exists, returns it
    edge_index add_or_get_edge(vertex_index v_from, vertex_index v_to);

171
    /// same as add_or_get_edge but returns the apattrriate half-edge
Philip Trettner's avatar
Philip Trettner committed
172
173
    halfedge_index add_or_get_halfedge(vertex_index v_from, vertex_index v_to);

Philip Trettner's avatar
Philip Trettner committed
174
175
176
177
178
179
180
181
    /// removes a face (actually sets the removed status)
    /// modifies all adjacent vertices so that they correctly report is_boundary true
    void remove_face(face_index f_idx);
    /// removes both adjacent faces, then removes both half edges
    void remove_edge(edge_index e_idx);
    /// removes all adjacent edges, then the vertex
    void remove_vertex(vertex_index v_idx);

182
    // attributes
Philip Trettner's avatar
Philip Trettner committed
183
184
185
186
187
188
189
190
191
192
193
194
195
    bool is_boundary(vertex_index idx) const;
    bool is_boundary(halfedge_index idx) const;

    /// Returns the opposite of a given valid half-edge
    halfedge_index opposite(halfedge_index he) const;

    /// Makes two half-edges adjacent
    /// Ensures:
    ///     * he_in->next == he_out
    ///     * he_out->prev == he_in
    /// Requires:
    ///     * he_in->is_free()
    ///     * he_out->is_free()
Philip Trettner's avatar
Philip Trettner committed
196
197
    /// Only works if a free incident half-edge is available
    void make_adjacent(halfedge_index he_in, halfedge_index he_out);
Philip Trettner's avatar
Philip Trettner committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212

    /// finds the next free incoming half-edge around a certain vertex
    /// starting from in_begin, EXCLUDING in_end (if in_begin == in_end, the whole vertex is searched)
    /// returns invalid index if no edge is found
    halfedge_index find_free_incident(halfedge_index in_begin, halfedge_index in_end) const;
    /// finds a free incident incoming half-edge around a given vertex
    halfedge_index find_free_incident(vertex_index v) const;

    /// returns half-edge going from `from`, point to `to`
    /// returns invalid index if not exists
    halfedge_index find_halfedge(vertex_index from, vertex_index to) const;

    /// returns edge index belonging to a half-edge
    edge_index edge_of(halfedge_index idx) const { return edge_index(idx.value >> 1); }
    /// returns a half-edge belonging to an edge
Philip Trettner's avatar
Philip Trettner committed
213
214
215
    halfedge_index halfedge_of(edge_index idx, int i) const { return halfedge_index((idx.value << 1) + i); }

    /// returns the vertex that this half-edge is pointing to
216
    vertex_index to_vertex_of(halfedge_index idx) const { return halfedge(idx).to_vertex; }
Philip Trettner's avatar
Philip Trettner committed
217
    /// returns the vertex that this half-edge is leaving from
218
    vertex_index from_vertex_of(halfedge_index idx) const { return halfedge(opposite(idx)).to_vertex; }
Philip Trettner's avatar
Philip Trettner committed
219

Philip Trettner's avatar
Philip Trettner committed
220
    // internal datastructures
Philip Trettner's avatar
Philip Trettner committed
221
private:
Philip Trettner's avatar
Philip Trettner committed
222
    // 4 byte per face
223
    struct face_info
Philip Trettner's avatar
Philip Trettner committed
224
    {
225
        halfedge_index halfedge; ///< one half-edge bounding this face
Philip Trettner's avatar
Philip Trettner committed
226
227

        bool is_valid() const { return halfedge.is_valid(); }
Philip Trettner's avatar
Philip Trettner committed
228
        void set_removed() { halfedge = halfedge_index::invalid(); }
Philip Trettner's avatar
Philip Trettner committed
229
230
    };

Philip Trettner's avatar
Philip Trettner committed
231
    // 4 byte per vertex
232
    struct vertex_info
Philip Trettner's avatar
Philip Trettner committed
233
    {
234
        halfedge_index outgoing_halfedge;
Philip Trettner's avatar
Philip Trettner committed
235
236
237

        /// a vertex can be valid even without outgoing halfedge
        bool is_valid() const { return outgoing_halfedge.value >= -1; }
Philip Trettner's avatar
Philip Trettner committed
238
        bool is_isolated() const { return !outgoing_halfedge.is_valid(); }
Philip Trettner's avatar
Philip Trettner committed
239
        void set_removed() { outgoing_halfedge = halfedge_index(-2); }
Philip Trettner's avatar
Philip Trettner committed
240
        // is_boundary: check if outgoing_halfedge is boundary
Philip Trettner's avatar
Philip Trettner committed
241
    };
Philip Trettner's avatar
Philip Trettner committed
242

Philip Trettner's avatar
Philip Trettner committed
243
    // 32 byte per edge
244
    struct halfedge_info
Philip Trettner's avatar
Philip Trettner committed
245
    {
Philip Trettner's avatar
Philip Trettner committed
246
        vertex_index to_vertex;       ///< half-edge points towards this vertex
247
248
249
        face_index face;              ///< might be invalid if boundary
        halfedge_index next_halfedge; ///< CCW
        halfedge_index prev_halfedge; ///< CW
Philip Trettner's avatar
Philip Trettner committed
250
251
252
        // opposite half-edge idx is "idx ^ 1"
        // edge idx is "idx >> 1"

Philip Trettner's avatar
Philip Trettner committed
253
254
255
256
257
        bool is_valid() const { return to_vertex.is_valid(); }

        /// a half-edge is free if it is a boundary, aka has no associated face
        bool is_free() const { return !face.is_valid(); }

Philip Trettner's avatar
Philip Trettner committed
258
259
        // CAUTION: remove both HE belonging to an edge
        void set_removed() { to_vertex = vertex_index::invalid(); }
Philip Trettner's avatar
Philip Trettner committed
260
261
262
263
    };

    // internal primitives
private:
264
265
266
267
268
269
270
271
272
273
274
275
    std::vector<face_info> mFaces;
    std::vector<vertex_info> mVertices;
    std::vector<halfedge_info> mHalfedges;

    struct face_info &face(face_index i) { return mFaces[i.value]; }
    struct face_info const &face(face_index i) const { return mFaces[i.value]; }
    struct vertex_info &vertex(vertex_index i) { return mVertices[i.value]; }
    struct vertex_info const &vertex(vertex_index i) const { return mVertices[i.value]; }
    struct halfedge_info &halfedge(halfedge_index i) { return mHalfedges[i.value]; }
    struct halfedge_info const &halfedge(halfedge_index i) const { return mHalfedges[i.value]; }
    struct halfedge_info &halfedge(edge_index i, int h) { return mHalfedges[(i.value >> 1) + h]; }
    struct halfedge_info const &halfedge(edge_index i, int h) const { return mHalfedges[(i.value >> 1) + h]; }
Philip Trettner's avatar
Philip Trettner committed
276
277
278
279

    // internal state
private:
    bool mCompact = true;
Philip Trettner's avatar
Philip Trettner committed
280
281
282
    int mRemovedFaces = 0;
    int mRemovedVertices = 0;
    int mRemovedHalfedges = 0;
283

Philip Trettner's avatar
Philip Trettner committed
284
285
    std::vector<halfedge_index> mFaceInsertCache;

286
    // attributes
Philip Trettner's avatar
Philip Trettner committed
287
private:
288
    // linked lists of all attributes
289
290
291
292
293
294
295
296
297
298
299
300
301
    mutable vertex_attribute_base *mVertexAttrs = nullptr;
    mutable face_attribute_base *mFaceAttrs = nullptr;
    mutable edge_attribute_base *mEdgeAttrs = nullptr;
    mutable halfedge_attribute_base *mHalfedgeAttrs = nullptr;

    void register_attr(vertex_attribute_base *attr) const;
    void deregister_attr(vertex_attribute_base *attr) const;
    void register_attr(face_attribute_base *attr) const;
    void deregister_attr(face_attribute_base *attr) const;
    void register_attr(edge_attribute_base *attr) const;
    void deregister_attr(edge_attribute_base *attr) const;
    void register_attr(halfedge_attribute_base *attr) const;
    void deregister_attr(halfedge_attribute_base *attr) const;
Philip Trettner's avatar
Philip Trettner committed
302

303
304
305
306
307
308
309
    // friends
private:
    friend struct vertex_handle;
    friend struct vertex_collection;
    friend struct vertex_iterator;
    friend struct valid_vertex_iterator;
    friend struct valid_vertex_collection;
310
    friend struct const_vertex_collection;
311
    friend struct vertex_attribute_base;
312
313
314
315
316
317

    friend struct face_handle;
    friend struct face_collection;
    friend struct face_iterator;
    friend struct valid_face_iterator;
    friend struct valid_face_collection;
318
    friend struct const_face_collection;
319
    friend struct face_attribute_base;
320
321
322
323
324
325

    friend struct edge_handle;
    friend struct edge_collection;
    friend struct edge_iterator;
    friend struct valid_edge_iterator;
    friend struct valid_edge_collection;
326
    friend struct const_edge_collection;
327
    friend struct edge_attribute_base;
328
329
330
331
332
333

    friend struct halfedge_handle;
    friend struct halfedge_collection;
    friend struct halfedge_iterator;
    friend struct valid_halfedge_iterator;
    friend struct valid_halfedge_collection;
334
    friend struct const_halfedge_collection;
335
    friend struct halfedge_attribute_base;
Philip Trettner's avatar
Philip Trettner committed
336
};
Philip Trettner's avatar
Philip Trettner committed
337
338
339

/// ======== IMPLEMENTATION ========

340
inline vertex_index Mesh::add_vertex()
Philip Trettner's avatar
Philip Trettner committed
341
{
Philip Trettner's avatar
Philip Trettner committed
342
    auto idx = vertex_index((int)mVertices.size());
343
    mVertices.push_back(vertex_info());
Philip Trettner's avatar
Philip Trettner committed
344

345
    // notify attributes
Philip Trettner's avatar
Philip Trettner committed
346
    auto vCnt = mVertices.size();
347
    for (auto p = mVertexAttrs; p; p = p->mNextAttribute)
Philip Trettner's avatar
Philip Trettner committed
348
349
        p->resize(vCnt);

Philip Trettner's avatar
Philip Trettner committed
350
    return idx;
Philip Trettner's avatar
Philip Trettner committed
351
352
}

353
inline face_index Mesh::add_face(const vertex_handle *v_handles, size_t vcnt)
Philip Trettner's avatar
Philip Trettner committed
354
{
Philip Trettner's avatar
Philip Trettner committed
355
356
357
358
359
360
    mFaceInsertCache.resize(vcnt);
    for (auto i = 0u; i < vcnt; ++i)
        mFaceInsertCache[i] = find_halfedge(v_handles[i].idx, v_handles[(i + 1) % vcnt].idx);
    return add_face(mFaceInsertCache.data(), vcnt);
}

361
inline face_index Mesh::add_face(const vertex_index *v_indices, size_t vcnt)
Philip Trettner's avatar
Philip Trettner committed
362
363
364
365
366
367
368
{
    mFaceInsertCache.resize(vcnt);
    for (auto i = 0u; i < vcnt; ++i)
        mFaceInsertCache[i] = find_halfedge(v_indices[i], v_indices[(i + 1) % vcnt]);
    return add_face(mFaceInsertCache.data(), vcnt);
}

369
inline face_index Mesh::add_face(const halfedge_handle *half_loop, size_t vcnt)
Philip Trettner's avatar
Philip Trettner committed
370
371
372
373
374
375
376
{
    mFaceInsertCache.resize(vcnt);
    for (auto i = 0u; i < vcnt; ++i)
        mFaceInsertCache[i] = half_loop[i].idx;
    return add_face(mFaceInsertCache.data(), vcnt);
}

377
inline face_index Mesh::add_face(const halfedge_index *half_loop, size_t vcnt)
Philip Trettner's avatar
Philip Trettner committed
378
379
{
    assert(vcnt >= 3 && "no support for less-than-triangular faces");
380
    /// TODO: attributes
Philip Trettner's avatar
Philip Trettner committed
381
382
383

    auto fidx = face_index((int)mFaces.size());

Philip Trettner's avatar
Philip Trettner committed
384
    // ensure that half-edges are adjacent at each vertex
Philip Trettner's avatar
Philip Trettner committed
385
    for (auto i = 0u; i < vcnt; ++i)
Philip Trettner's avatar
Philip Trettner committed
386
    {
Philip Trettner's avatar
Philip Trettner committed
387
388
389
390
391
392
        auto h0 = half_loop[i];
        auto h1 = half_loop[(i + 1) % vcnt];

        // half-edge must form a chain
        assert(to_vertex_of(h0) == from_vertex_of(h1));
        // half-edge must be free, i.e. allow a new polygon
393
        assert(halfedge(h0).is_free());
Philip Trettner's avatar
Philip Trettner committed
394
395
396
397
398

        // make them adjacent
        make_adjacent(h0, h1);

        // link face
399
        halfedge(h0).face = fidx;
Philip Trettner's avatar
Philip Trettner committed
400
401
    }

Philip Trettner's avatar
Philip Trettner committed
402
    // set up face data
403
    face_info f;
Philip Trettner's avatar
Philip Trettner committed
404
    f.halfedge = half_loop[0];
Philip Trettner's avatar
Philip Trettner committed
405
    mFaces.push_back(f);
Philip Trettner's avatar
Philip Trettner committed
406

407
    // notify attributes
Philip Trettner's avatar
Philip Trettner committed
408
    auto fCnt = mFaces.size();
409
    for (auto p = mFaceAttrs; p; p = p->mNextAttribute)
Philip Trettner's avatar
Philip Trettner committed
410
411
        p->resize(fCnt);

Philip Trettner's avatar
Philip Trettner committed
412
    return fidx;
Philip Trettner's avatar
Philip Trettner committed
413
414
}

415
inline edge_index Mesh::add_or_get_edge(vertex_index v_from, vertex_index v_to)
Philip Trettner's avatar
Philip Trettner committed
416
417
418
419
420
421
422
423
{
    assert(v_from != v_to);

    // already exists?
    auto he = find_halfedge(v_from, v_to);
    if (he.is_valid())
        return edge_of(he);

424
425
    auto &vd_from = vertex(v_from);
    auto &vd_to = vertex(v_to);
Philip Trettner's avatar
Philip Trettner committed
426
427
428
429
430
431

    // allocate new
    auto he_size = (int)mHalfedges.size();
    auto h_from_to_idx = halfedge_index(he_size + 0);
    auto h_to_from_idx = halfedge_index(he_size + 1);
    auto eidx = edge_index(he_size >> 1);
432
433
    halfedge_info h_from_to;
    halfedge_info h_to_from;
Philip Trettner's avatar
Philip Trettner committed
434
435
436
437
438
439
440
441
442
443
444
445
446
447

    // setup data (self-connected edge)
    h_from_to.to_vertex = v_to;
    h_to_from.to_vertex = v_from;
    h_from_to.next_halfedge = h_to_from_idx;
    h_to_from.next_halfedge = h_from_to_idx;

    // link from vertex
    if (vd_from.is_isolated())
        vd_from.outgoing_halfedge = h_from_to_idx;
    else
    {
        auto from_in_idx = find_free_incident(v_from);
        assert(from_in_idx.is_valid() && "vertex is already fully connected");
448
        auto &from_in = halfedge(from_in_idx);
Philip Trettner's avatar
Philip Trettner committed
449
        auto from_out_idx = from_in.next_halfedge;
450
        auto &from_out = halfedge(from_out_idx);
Philip Trettner's avatar
Philip Trettner committed
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465

        from_in.next_halfedge = h_from_to_idx;
        h_from_to.prev_halfedge = from_in_idx;

        h_to_from.next_halfedge = from_out_idx;
        from_out.prev_halfedge = h_to_from_idx;
    }

    // link to vertex
    if (vd_to.is_isolated())
        vd_to.outgoing_halfedge = h_from_to_idx;
    else
    {
        auto to_in_idx = find_free_incident(v_to);
        assert(to_in_idx.is_valid() && "vertex is already fully connected");
466
        auto &to_in = halfedge(to_in_idx);
Philip Trettner's avatar
Philip Trettner committed
467
        auto to_out_idx = to_in.next_halfedge;
468
        auto &to_out = halfedge(to_out_idx);
Philip Trettner's avatar
Philip Trettner committed
469
470
471
472
473
474
475
476
477
478
479

        to_in.next_halfedge = h_to_from_idx;
        h_to_from.prev_halfedge = to_in_idx;

        h_from_to.next_halfedge = to_out_idx;
        to_out.prev_halfedge = h_from_to_idx;
    }

    // finalize
    mHalfedges.push_back(h_from_to);
    mHalfedges.push_back(h_to_from);
Philip Trettner's avatar
Philip Trettner committed
480

481
    // notify attributes
Philip Trettner's avatar
Philip Trettner committed
482
483
    auto hCnt = mHalfedges.size();
    auto eCnt = hCnt >> 1;
484
    for (auto p = mHalfedgeAttrs; p; p = p->mNextAttribute)
Philip Trettner's avatar
Philip Trettner committed
485
        p->resize(hCnt);
486
    for (auto p = mEdgeAttrs; p; p = p->mNextAttribute)
Philip Trettner's avatar
Philip Trettner committed
487
488
        p->resize(eCnt);

Philip Trettner's avatar
Philip Trettner committed
489
490
491
    return eidx;
}

492
inline halfedge_index Mesh::add_or_get_halfedge(vertex_index v_from, vertex_index v_to)
Philip Trettner's avatar
Philip Trettner committed
493
494
495
496
{
    auto e = add_or_get_edge(v_from, v_to);
    auto h0 = halfedge_of(e, 0);
    auto h1 = halfedge_of(e, 1);
497
    return halfedge(h0).to_vertex == v_to ? h0 : h1;
Philip Trettner's avatar
Philip Trettner committed
498
499
}

500
inline void Mesh::make_adjacent(halfedge_index he_in, halfedge_index he_out)
Philip Trettner's avatar
Philip Trettner committed
501
502
{
    // see http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm ::makeAdjacent
503
504
    auto &in = halfedge(he_in);
    auto &out = halfedge(he_out);
Philip Trettner's avatar
Philip Trettner committed
505
506
507
508
509
510

    auto he_b = in.next_halfedge;
    auto he_d = out.prev_halfedge;

    // already correct
    if (he_b == he_out)
Philip Trettner's avatar
Philip Trettner committed
511
        return;
Philip Trettner's avatar
Philip Trettner committed
512
513
514

    // find free half-edge after `out` but before `in`
    auto he_g = find_free_incident(opposite(he_out), he_in);
Philip Trettner's avatar
Philip Trettner committed
515
    assert(he_g.is_valid()); // unable to make adjacent
Philip Trettner's avatar
Philip Trettner committed
516

517
518
519
    auto &b = halfedge(he_b);
    auto &d = halfedge(he_d);
    auto &g = halfedge(he_g);
Philip Trettner's avatar
Philip Trettner committed
520
521

    auto he_h = g.next_halfedge;
522
    auto &h = halfedge(he_d);
Philip Trettner's avatar
Philip Trettner committed
523

524
    // attrerly rewire
Philip Trettner's avatar
Philip Trettner committed
525
526
527
528
529
530
531
532
533
534
    in.next_halfedge = he_out;
    out.prev_halfedge = he_in;

    g.next_halfedge = he_b;
    b.prev_halfedge = he_g;

    d.next_halfedge = he_h;
    h.prev_halfedge = he_d;
}

Philip Trettner's avatar
Philip Trettner committed
535
536
inline void Mesh::remove_face(face_index f_idx)
{
Philip Trettner's avatar
Philip Trettner committed
537
    auto &f = face(f_idx);
Philip Trettner's avatar
Philip Trettner committed
538
539
540
541
542
543
    f.set_removed(); //< mark removed

    auto he_begin = f.halfedge;
    auto he = he_begin;
    do
    {
Philip Trettner's avatar
Philip Trettner committed
544
        auto &h = halfedge(he);
Philip Trettner's avatar
Philip Trettner committed
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
        assert(h.face == f_idx);

        // set half-edge face to invalid
        h.face = face_index::invalid();

        // outgoing vertex half-edge
        // (vertex correctly reports is_boundary)
        vertex(from_vertex_of(he)).outgoing_halfedge = he;

        // advance
        he = h.next_halfedge;
    } while (he != he_begin);
}

inline void Mesh::remove_edge(edge_index e_idx)
{
Philip Trettner's avatar
Philip Trettner committed
561
562
563
564
565
    auto hi_in = halfedge_of(e_idx, 0);
    auto hi_out = halfedge_of(e_idx, 1);

    auto &h_in = halfedge(hi_in);
    auto &h_out = halfedge(hi_out);
Philip Trettner's avatar
Philip Trettner committed
566

Philip Trettner's avatar
Philip Trettner committed
567
568
    auto f0 = h_in.face;
    auto f1 = h_out.face;
Philip Trettner's avatar
Philip Trettner committed
569
570
571
572
573
574
575
576

    // remove adjacent faces
    if (f0.is_valid())
        remove_face(f0);
    if (f1.is_valid())
        remove_face(f1);

    // rewire vertices
Philip Trettner's avatar
Philip Trettner committed
577
578
579
580
581
582
583
584
585
    auto &v_in_to = vertex(h_in.to_vertex);
    auto &v_out_to = vertex(h_out.to_vertex);

    auto hi_out_prev = h_out.prev_halfedge;
    auto hi_out_next = h_out.next_halfedge;

    auto hi_in_prev = h_in.prev_halfedge;
    auto hi_in_next = h_in.next_halfedge;

Philip Trettner's avatar
Philip Trettner committed
586
587
    // modify vertex if outgoing half-edge is going to be removed
    if (v_in_to.outgoing_halfedge == hi_out)
Philip Trettner's avatar
Philip Trettner committed
588
    {
Philip Trettner's avatar
Philip Trettner committed
589
590
591
592
        if (hi_in_next == hi_out) // v_in_to becomes isolated
            v_in_to.outgoing_halfedge = halfedge_index::invalid();
        else
            v_in_to.outgoing_halfedge = hi_in_next;
Philip Trettner's avatar
Philip Trettner committed
593
    }
Philip Trettner's avatar
Philip Trettner committed
594

Philip Trettner's avatar
Philip Trettner committed
595
    if (v_out_to.outgoing_halfedge == hi_in)
Philip Trettner's avatar
Philip Trettner committed
596
    {
Philip Trettner's avatar
Philip Trettner committed
597
598
599
600
        if (hi_out_next == hi_in) // v_out_to becomes isolated
            v_out_to.outgoing_halfedge = halfedge_index::invalid();
        else
            v_out_to.outgoing_halfedge = hi_out_next;
Philip Trettner's avatar
Philip Trettner committed
601
602
    }

Philip Trettner's avatar
Philip Trettner committed
603
604
605
606
607
608
609
610
611
612
    // reqire half-edges
    halfedge(hi_out_prev).next_halfedge = hi_in_next;
    halfedge(hi_in_next).prev_halfedge = hi_out_prev;

    halfedge(hi_in_prev).next_halfedge = hi_out_next;
    halfedge(hi_out_next).prev_halfedge = hi_in_prev;

    // remove half-edges
    h_in.set_removed();
    h_out.set_removed();
Philip Trettner's avatar
Philip Trettner committed
613
614
615
616
}

inline void Mesh::remove_vertex(vertex_index v_idx)
{
Philip Trettner's avatar
Philip Trettner committed
617
    auto &v = vertex(v_idx);
Philip Trettner's avatar
Philip Trettner committed
618
619
620
621
622
623
624
625
626

    // remove all outgoing edges
    while (!v.is_isolated())
        remove_edge(edge_of(v.outgoing_halfedge));

    // mark removed
    v.set_removed();
}

627
inline halfedge_index Mesh::find_free_incident(halfedge_index in_begin, halfedge_index in_end) const
Philip Trettner's avatar
Philip Trettner committed
628
{
629
    assert(halfedge(in_begin).to_vertex == halfedge(in_end).to_vertex);
Philip Trettner's avatar
Philip Trettner committed
630
631
632
633

    auto he = in_begin;
    do
    {
634
635
        auto const &h = halfedge(he);
        assert(h.to_vertex == halfedge(in_end).to_vertex);
Philip Trettner's avatar
Philip Trettner committed
636
637
638
639
640
641
642
643
644
645
646
647

        // free? found one!
        if (h.is_free())
            return he;

        // next half-edge of vertex
        he = opposite(h.next_halfedge);
    } while (he != in_end);

    return halfedge_index::invalid();
}

648
inline halfedge_index Mesh::find_free_incident(vertex_index v) const
Philip Trettner's avatar
Philip Trettner committed
649
{
650
    auto in_begin = opposite(vertex(v).outgoing_halfedge);
Philip Trettner's avatar
Philip Trettner committed
651
652
653
    return find_free_incident(in_begin, in_begin);
}

654
inline halfedge_index Mesh::find_halfedge(vertex_index from, vertex_index to) const
Philip Trettner's avatar
Philip Trettner committed
655
{
656
    auto he_begin = vertex(from).outgoing_halfedge;
Philip Trettner's avatar
Philip Trettner committed
657
658
659
    if (!he_begin.is_valid())
        return halfedge_index::invalid(); // isolated vertex

Philip Trettner's avatar
Philip Trettner committed
660
661
662
    auto he = he_begin;
    do
    {
663
        auto const &h = halfedge(he);
Philip Trettner's avatar
Philip Trettner committed
664
665
666
667
668
669
670
671
672
673
674
675
676

        // found?
        if (h.to_vertex == to)
            return he;

        // advance
        he = opposite(h.next_halfedge);

    } while (he != he_begin);

    return halfedge_index::invalid(); // not found
}

677
inline bool Mesh::is_boundary(vertex_index idx) const
Philip Trettner's avatar
Philip Trettner committed
678
{
679
    auto const &v = vertex(idx);
Philip Trettner's avatar
Philip Trettner committed
680
681
682
    return v.outgoing_halfedge.is_valid() && is_boundary(v.outgoing_halfedge);
}

683
inline bool Mesh::is_boundary(halfedge_index idx) const
Philip Trettner's avatar
Philip Trettner committed
684
{
685
    return halfedge(idx).is_free();
Philip Trettner's avatar
Philip Trettner committed
686
687
}

688
inline halfedge_index Mesh::opposite(halfedge_index he) const
Philip Trettner's avatar
Philip Trettner committed
689
690
{
    return halfedge_index(he.value ^ 1);
Philip Trettner's avatar
Philip Trettner committed
691
692
}

693
inline vertex_index Mesh::next_valid_idx_from(vertex_index idx) const
Philip Trettner's avatar
Philip Trettner committed
694
{
695
    for (auto i = idx.value; i < (int)mVertices.size(); ++i)
Philip Trettner's avatar
Philip Trettner committed
696
        if (mVertices[i].is_valid())
697
            return vertex_index(i);
Philip Trettner's avatar
Philip Trettner committed
698
    return vertex_index(size_vertices()); // end index
Philip Trettner's avatar
Philip Trettner committed
699
700
}

701
inline vertex_index Mesh::prev_valid_idx_from(vertex_index idx) const
Philip Trettner's avatar
Philip Trettner committed
702
{
703
    for (auto i = idx.value; i >= 0; --i)
Philip Trettner's avatar
Philip Trettner committed
704
        if (mVertices[i].is_valid())
705
            return vertex_index(i);
Philip Trettner's avatar
Philip Trettner committed
706
707
708
    return {}; // invalid
}

709
inline edge_index Mesh::next_valid_idx_from(edge_index idx) const
Philip Trettner's avatar
Philip Trettner committed
710
{
711
    for (auto i = idx.value << 1; i < (int)mHalfedges.size(); i += 2)
Philip Trettner's avatar
Philip Trettner committed
712
        if (mHalfedges[i].is_valid())
713
            return edge_index(i >> 1);
Philip Trettner's avatar
Philip Trettner committed
714
    return edge_index(size_edges()); // end index
Philip Trettner's avatar
Philip Trettner committed
715
716
}

717
inline edge_index Mesh::prev_valid_idx_from(edge_index idx) const
Philip Trettner's avatar
Philip Trettner committed
718
{
719
    for (auto i = idx.value << 1; i >= 0; i -= 2)
Philip Trettner's avatar
Philip Trettner committed
720
        if (mHalfedges[i].is_valid())
721
            return edge_index(i >> 1);
Philip Trettner's avatar
Philip Trettner committed
722
723
724
    return {}; // invalid
}

725
inline face_index Mesh::next_valid_idx_from(face_index idx) const
Philip Trettner's avatar
Philip Trettner committed
726
{
727
    for (auto i = idx.value; i < (int)mFaces.size(); ++i)
Philip Trettner's avatar
Philip Trettner committed
728
        if (mFaces[i].is_valid())
729
            return face_index(i);
Philip Trettner's avatar
Philip Trettner committed
730
    return face_index(size_faces()); // end index
Philip Trettner's avatar
Philip Trettner committed
731
732
}

733
inline face_index Mesh::prev_valid_idx_from(face_index idx) const
Philip Trettner's avatar
Philip Trettner committed
734
{
735
    for (auto i = idx.value; i >= 0; --i)
Philip Trettner's avatar
Philip Trettner committed
736
        if (mFaces[i].is_valid())
737
            return face_index(i);
Philip Trettner's avatar
Philip Trettner committed
738
739
740
    return {}; // invalid
}

741
inline halfedge_index Mesh::next_valid_idx_from(halfedge_index idx) const
Philip Trettner's avatar
Philip Trettner committed
742
{
743
    for (auto i = idx.value; i < (int)mHalfedges.size(); ++i)
Philip Trettner's avatar
Philip Trettner committed
744
        if (mHalfedges[i].is_valid())
745
            return halfedge_index(i);
Philip Trettner's avatar
Philip Trettner committed
746
    return halfedge_index(size_halfedges()); // end index
Philip Trettner's avatar
Philip Trettner committed
747
748
}

749
inline halfedge_index Mesh::prev_valid_idx_from(halfedge_index idx) const
Philip Trettner's avatar
Philip Trettner committed
750
{
751
    for (auto i = idx.value; i >= 0; --i)
Philip Trettner's avatar
Philip Trettner committed
752
        if (mHalfedges[i].is_valid())
753
            return halfedge_index(i);
Philip Trettner's avatar
Philip Trettner committed
754
755
756
757
758
    return {}; // invalid
}

/// ======== ITERATOR IMPLEMENTATION ========

759
inline valid_vertex_iterator &valid_vertex_iterator::operator++()
Philip Trettner's avatar
Philip Trettner committed
760
{
761
762
    handle.idx.value++;
    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
Philip Trettner's avatar
Philip Trettner committed
763
764
    return *this;
}
765
inline vertex_iterator &vertex_iterator::operator++()
Philip Trettner's avatar
Philip Trettner committed
766
767
768
769
{
    handle.idx.value++;
    return *this;
}
770

771
inline valid_face_iterator &valid_face_iterator::operator++()
772
773
774
775
776
{
    handle.idx.value++;
    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
    return *this;
}
777
inline face_iterator &face_iterator::operator++()
778
779
780
781
782
{
    handle.idx.value++;
    return *this;
}

783
inline valid_edge_iterator &valid_edge_iterator::operator++()
784
785
786
787
788
{
    handle.idx.value++;
    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
    return *this;
}
789
inline edge_iterator &edge_iterator::operator++()
790
791
792
793
794
{
    handle.idx.value++;
    return *this;
}

795
inline valid_halfedge_iterator &valid_halfedge_iterator::operator++()
796
797
798
799
800
{
    handle.idx.value++;
    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
    return *this;
}
801
inline halfedge_iterator &halfedge_iterator::operator++()
802
803
804
805
806
807
808
809
810
{
    handle.idx.value++;
    return *this;
}

/// ======== RANGES IMPLEMENTATION ========

// - Vertices -

811
inline int vertex_collection::size() const
812
813
814
815
{
    return mesh->size_vertices();
}

816
inline void vertex_collection::reserve(int capacity) const
817
818
819
820
{
    mesh->reserve_vertices(capacity);
}

821
inline vertex_handle vertex_collection::add() const
822
{
Philip Trettner's avatar
Philip Trettner committed
823
    return mesh->handle_of(mesh->add_vertex());
824
825
}

826
inline vertex_iterator vertex_collection::begin() const
827
828
829
830
{
    return mesh->vertices_begin();
}

831
inline vertex_iterator vertex_collection::end() const
832
833
834
835
{
    return mesh->vertices_end();
}

836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
inline int const_vertex_collection::size() const
{
    return mesh->size_vertices();
}

inline vertex_iterator const_vertex_collection::begin() const
{
    return mesh->vertices_begin();
}

inline vertex_iterator const_vertex_collection::end() const
{
    return mesh->vertices_end();
}

851
inline int valid_vertex_collection::size() const
852
853
854
855
{
    return mesh->size_valid_vertices();
}

856
inline valid_vertex_iterator valid_vertex_collection::begin() const
857
858
859
860
{
    return mesh->valid_vertices_begin();
}

861
inline valid_vertex_iterator valid_vertex_collection::end() const
862
863
864
865
866
867
{
    return mesh->valid_vertices_end();
}

// - Faces -

868
inline int face_collection::size() const
869
870
871
872
{
    return mesh->size_faces();
}

873
inline void face_collection::reserve(int capacity) const
874
875
876
877
{
    mesh->reserve_faces(capacity);
}

878
inline face_handle face_collection::add(const vertex_handle *v_handles, size_t vcnt) const
Philip Trettner's avatar
Philip Trettner committed
879
880
881
882
{
    return mesh->handle_of(mesh->add_face(v_handles, vcnt));
}

883
inline face_handle face_collection::add(const halfedge_handle *half_loop, size_t vcnt) const
Philip Trettner's avatar
Philip Trettner committed
884
885
886
887
{
    return mesh->handle_of(mesh->add_face(half_loop, vcnt));
}

888
inline face_handle face_collection::add(std::vector<vertex_handle> const &v_handles) const
Philip Trettner's avatar
Philip Trettner committed
889
{
Philip Trettner's avatar
Philip Trettner committed
890
    return add(v_handles.data(), v_handles.size());
Philip Trettner's avatar
Philip Trettner committed
891
892
}

893
inline face_handle face_collection::add(std::vector<halfedge_handle> const &half_loop) const
894
{
Philip Trettner's avatar
Philip Trettner committed
895
    return add(half_loop.data(), half_loop.size());
896
897
}

898
inline face_handle face_collection::add(vertex_handle v0, vertex_handle v1, vertex_handle v2) const
899
{
Philip Trettner's avatar
Philip Trettner committed
900
    halfedge_index hs[3] = {
Philip Trettner's avatar
Philip Trettner committed
901
902
903
        mesh->add_or_get_halfedge(v0.idx, v1.idx), //
        mesh->add_or_get_halfedge(v1.idx, v2.idx), //
        mesh->add_or_get_halfedge(v2.idx, v0.idx), //
Philip Trettner's avatar
Philip Trettner committed
904
905
    };
    return mesh->handle_of(mesh->add_face(hs, 3));
906
907
}

908
inline face_handle face_collection::add(vertex_handle v0, vertex_handle v1, vertex_handle v2, vertex_handle v3) const
909
{
Philip Trettner's avatar
Philip Trettner committed
910
    halfedge_index hs[4] = {
Philip Trettner's avatar
Philip Trettner committed
911
912
913
914
        mesh->add_or_get_halfedge(v0.idx, v1.idx), //
        mesh->add_or_get_halfedge(v1.idx, v2.idx), //
        mesh->add_or_get_halfedge(v2.idx, v3.idx), //
        mesh->add_or_get_halfedge(v3.idx, v0.idx), //
Philip Trettner's avatar
Philip Trettner committed
915
916
917
918
    };
    return mesh->handle_of(mesh->add_face(hs, 4));
}

919
inline face_handle face_collection::add(halfedge_handle h0, halfedge_handle h1, halfedge_handle h2) const
Philip Trettner's avatar
Philip Trettner committed
920
921
922
{
    halfedge_index hs[3] = {h0.idx, h1.idx, h2.idx};
    return mesh->handle_of(mesh->add_face(hs, 3));
923
924
}

925
inline face_handle face_collection::add(halfedge_handle h0, halfedge_handle h1, halfedge_handle h2, halfedge_handle h3) const
Philip Trettner's avatar
Philip Trettner committed
926
927
928
929
930
931
{
    halfedge_index hs[4] = {h0.idx, h1.idx, h2.idx, h3.idx};
    return mesh->handle_of(mesh->add_face(hs, 4));
}

template <size_t N>
932
inline face_handle face_collection::add(const vertex_handle (&v_handles)[N]) const
933
{
Philip Trettner's avatar
Philip Trettner committed
934
935
936
937
    halfedge_index hs[N];
    for (auto i = 0u; i < N; ++i)
        hs[i] = mesh->find_halfedge(v_handles[i].idx, v_handles[(i + 1) % N].idx);
    return mesh->handle_of(mesh->add_face(hs, N));
938
939
940
}

template <size_t N>
941
inline face_handle face_collection::add(const halfedge_handle (&half_loop)[N]) const
942
{
Philip Trettner's avatar
Philip Trettner committed
943
944
945
946
    halfedge_index hs[N];
    for (auto i = 0u; i < N; ++i)
        hs[i] = half_loop[i].idx;
    return mesh->handle_of(mesh->add_face(hs, N));
947
948
}

949
inline face_iterator face_collection::begin() const
950
951
952
953
{
    return mesh->faces_begin();
}

954
inline face_iterator face_collection::end() const
955
956
957
958
{
    return mesh->faces_end();
}

959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
inline int const_face_collection::size() const
{
    return mesh->size_faces();
}

inline face_iterator const_face_collection::begin() const
{
    return mesh->faces_begin();
}

inline face_iterator const_face_collection::end() const
{
    return mesh->faces_end();
}

974
inline int valid_face_collection::size() const
975
976
977
978
{
    return mesh->size_valid_faces();
}

979
inline valid_face_iterator valid_face_collection::begin() const
980
981
982
983
{
    return mesh->valid_faces_begin();
}

984
inline valid_face_iterator valid_face_collection::end() const
985
986
987
988
989
990
{
    return mesh->valid_faces_end();
}

// - Edges -

991
inline int edge_collection::size() const
992
993
994
995
{
    return mesh->size_edges();
}

996
inline void edge_collection::reserve(int capacity) const
997
998
999
1000
{
    mesh->reserve_edges(capacity);
}

For faster browsing, not all history is shown. View entire blame