diff --git a/src/polymesh/EdgeHandle.hh b/src/polymesh/EdgeHandle.hh
deleted file mode 100644
index b398187f8764b27e1176a040ebe5bf9be03761d8..0000000000000000000000000000000000000000
--- a/src/polymesh/EdgeHandle.hh
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include "EdgeIndex.hh"
-
-namespace polymesh
-{
-struct Mesh;
-
-struct EdgeHandle
-{
-    Mesh const* mesh;
-    EdgeIndex idx;
-
-    EdgeHandle(Mesh const* mesh, EdgeIndex idx) : mesh(mesh), idx(idx) {}
-};
-}
diff --git a/src/polymesh/EdgeIndex.hh b/src/polymesh/EdgeIndex.hh
deleted file mode 100644
index d44ff035e4e2aa44ba39bea2ec6e01fc674ad7bd..0000000000000000000000000000000000000000
--- a/src/polymesh/EdgeIndex.hh
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-namespace polymesh
-{
-struct EdgeIndex
-{
-    int value = -1;
-
-    EdgeIndex() = default;
-    explicit EdgeIndex(int idx) : value(idx) {}
-
-    bool is_valid() const { return value >= 0; }
-    static EdgeIndex invalid() { return {}; }
-
-    bool operator==(EdgeIndex const& rhs) const { return value == rhs.value; }
-    bool operator!=(EdgeIndex const& rhs) const { return value != rhs.value; }
-};
-}
diff --git a/src/polymesh/FaceHandle.hh b/src/polymesh/FaceHandle.hh
deleted file mode 100644
index 5c7eb487e8cf11e933d070d938668c546cb33549..0000000000000000000000000000000000000000
--- a/src/polymesh/FaceHandle.hh
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include "FaceIndex.hh"
-
-namespace polymesh
-{
-struct Mesh;
-
-struct FaceHandle
-{
-    Mesh const* mesh;
-    FaceIndex idx;
-
-    FaceHandle(Mesh const* mesh, FaceIndex idx) : mesh(mesh), idx(idx) {}
-};
-}
diff --git a/src/polymesh/FaceIndex.hh b/src/polymesh/FaceIndex.hh
deleted file mode 100644
index 46b561ad12dcff7a0247a2f11ab0fd78d8abe573..0000000000000000000000000000000000000000
--- a/src/polymesh/FaceIndex.hh
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-namespace polymesh
-{
-struct FaceIndex
-{
-    int value = -1;
-
-    FaceIndex() = default;
-    explicit FaceIndex(int idx) : value(idx) {}
-
-    bool is_valid() const { return value >= 0; }
-    static FaceIndex invalid() { return {}; }
-
-    bool operator==(FaceIndex const& rhs) const { return value == rhs.value; }
-    bool operator!=(FaceIndex const& rhs) const { return value != rhs.value; }
-};
-
-}
diff --git a/src/polymesh/HalfedgeHandle.hh b/src/polymesh/HalfedgeHandle.hh
deleted file mode 100644
index 718b48b1263f0e0223b868f2cae1b6ece9779fbc..0000000000000000000000000000000000000000
--- a/src/polymesh/HalfedgeHandle.hh
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include "HalfedgeIndex.hh"
-
-namespace polymesh
-{
-struct Mesh;
-
-struct HalfedgeHandle
-{
-    Mesh const* mesh;
-    HalfedgeIndex idx;
-
-    HalfedgeHandle(Mesh const* mesh, HalfedgeIndex idx) : mesh(mesh), idx(idx) {}
-};
-}
diff --git a/src/polymesh/HalfedgeIndex.hh b/src/polymesh/HalfedgeIndex.hh
deleted file mode 100644
index a6a412484ae8f436a58cfafd332a4062d9673071..0000000000000000000000000000000000000000
--- a/src/polymesh/HalfedgeIndex.hh
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-namespace polymesh
-{
-struct HalfedgeIndex
-{
-    int value = -1;
-
-    HalfedgeIndex() = default;
-    explicit HalfedgeIndex(int idx) : value(idx) {}
-
-    bool is_valid() const { return value >= 0; }
-    static HalfedgeIndex invalid() { return {}; }
-
-    bool operator==(HalfedgeIndex const& rhs) const { return value == rhs.value; }
-    bool operator!=(HalfedgeIndex const& rhs) const { return value != rhs.value; }
-};
-
-}
diff --git a/src/polymesh/Iterators.hh b/src/polymesh/Iterators.hh
deleted file mode 100644
index 853c2ba179f8b0496023b4134f9eff88f690b315..0000000000000000000000000000000000000000
--- a/src/polymesh/Iterators.hh
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-
-#include <cassert>
-
-#include "EdgeHandle.hh"
-#include "FaceHandle.hh"
-#include "HalfedgeHandle.hh"
-#include "VertexHandle.hh"
-
-// For iterator interfaces, see http://anderberg.me/2016/07/04/c-custom-iterators/
-// Note: some iterator methods are implemented at the end of Mesh.hh to ensure inlining
-
-namespace polymesh
-{
-struct skipping_vertex_iterator
-{
-    skipping_vertex_iterator() = default;
-    skipping_vertex_iterator(VertexHandle handle) : handle(handle) {}
-
-    VertexHandle operator*() const { return handle; }
-    skipping_vertex_iterator& operator++();
-    skipping_vertex_iterator operator++(int)
-    {
-        auto i = *this;
-        return ++i;
-    }
-    bool operator==(skipping_vertex_iterator const& rhs) const
-    {
-        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
-        return handle.idx == rhs.handle.idx;
-    }
-    bool operator!=(skipping_vertex_iterator const& rhs) const
-    {
-        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
-        return handle.idx != rhs.handle.idx;
-    }
-
-private:
-    VertexHandle handle;
-};
-
-struct vertex_iterator
-{
-    vertex_iterator() = default;
-    vertex_iterator(VertexHandle handle) : handle(handle) {}
-
-    VertexHandle operator*() const { return handle; }
-    vertex_iterator& operator++();
-    vertex_iterator operator++(int)
-    {
-        auto i = *this;
-        return ++i;
-    }
-    bool operator==(vertex_iterator const& rhs) const
-    {
-        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
-        return handle.idx == rhs.handle.idx;
-    }
-    bool operator!=(vertex_iterator const& rhs) const
-    {
-        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
-        return handle.idx != rhs.handle.idx;
-    }
-
-private:
-    VertexHandle handle;
-};
-}
diff --git a/src/polymesh/Mesh.hh b/src/polymesh/Mesh.hh
index 0e5ba04442b807e409473f89458067cf3a58b092..9db31abad4fc24e4c3f7caae79c57d76d30eaa15 100644
--- a/src/polymesh/Mesh.hh
+++ b/src/polymesh/Mesh.hh
@@ -1,20 +1,30 @@
 #pragma once
 
 #include <cstddef>
+#include <memory>
 #include <vector>
 
-#include "EdgeHandle.hh"
-#include "FaceHandle.hh"
-#include "HalfedgeHandle.hh"
-#include "VertexHandle.hh"
-
-#include "Ranges.hh"
+#include "cursors.hh"
+#include "ranges.hh"
 
 namespace polymesh
 {
+using SharedMesh = std::shared_ptr<Mesh>;
+
 /**
- * @brief Half-Edge Mesh Datastructure
+ * @brief Half-edge Mesh Datastructure
+ *
+ * * Primitives are accessed via the smart collections mesh.<primitive>()
+ *   (where <primitive> can be vertices, edges, faces, or halfedges)
+ *
+ * * Primitives can be added via <primitive>().add()
+ *   (added primitives are at the end of the collection)
  *
+ * * Primitives can be deleted via <primitive>().delete(...)
+ *   (deleted primitives are invalidated (flagged for removal). call compactify() to remove them)
+ *
+ * * `for (auto h : <primitive>())` iterates over _all_ primitives, including invalid ones
+ *   (`for (auto h : valid_<primitive>())` skips over invalid ones)
  */
 struct Mesh
 {
@@ -24,51 +34,35 @@ public:
     /// vertices(), faces(), ...
 
     /// get handle from index
-    FaceHandle handle_of(FaceIndex idx) const { return {this, idx}; }
-    EdgeHandle handle_of(EdgeIndex idx) const { return {this, idx}; }
-    VertexHandle handle_of(VertexIndex idx) const { return {this, idx}; }
-    HalfedgeHandle handle_of(HalfedgeIndex idx) const { return {this, idx}; }
+    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}; }
 
-    /// number of primitives
-    /// CAUTION: includes deleted ones!
-    int size_faces() const { return mFaces.size(); }
-    int size_vertices() const { return mVertices.size(); }
-    int size_halfedges() const { return mHalfedges.size(); }
-    int size_edges() const { return mHalfedges.size() >> 1; }
-
-    /// iterator ranges for primitives
+    /// smart collections for primitives INCLUDING deleted ones
+    /// Also primary interfaces for querying size and adding primitives
     ///
     /// CAUTION: includes deleted ones!
     ///   use compactify() to ensure that no deleted ones exist
-    ///   use valid_xyz() to skip deleted ones during iteration (slower)
+    ///   use valid_<primitive>() to skip deleted ones during iteration (slower)
     ///
-    /// NOTE: adding primitives does NOT invalidate ranges. (newly added ones are processed at the end)
+    /// NOTE: adding primitives does NOT invalidate ranges. (newly added ones are NOT processed though)
     ///       deleting primitives does NOT invalidate ranges.
-    vertex_range vertices() const { return {vertices_begin(), vertices_end()}; }
-    skipping_vertex_range valid_vertices() const { return {valid_vertices_begin(), valid_vertices_end()}; }
-
-    /// begin/end for ranges
-    vertex_iterator vertices_begin() const { return {{this, VertexIndex(0)}}; }
-    vertex_iterator vertices_end() const { return {{this, VertexIndex::invalid()}}; }
-    skipping_vertex_iterator valid_vertices_begin() const { return {{this, VertexIndex(0)}}; }
-    skipping_vertex_iterator valid_vertices_end() const { return {{this, VertexIndex::invalid()}}; }
+    vertex_collection vertices() { return {this}; }
+    face_collection faces() { return {this}; }
+    edge_collection edges() { return {this}; }
+    halfedge_collection halfedges() { return {this}; }
 
-    // modifiers
-public:
-    /// TODO:
-    /// add_vertex/he/h/...
-
-    /// Adds a single non-connected vertex
-    /// Does NOT invalidate iterators!
-    VertexHandle add_vertex();
-
-    /// Adds a face consisting of N vertices
-    template <size_t N>
-    FaceHandle add_face(const VertexHandle (&vhandles)[N]);
-    FaceHandle add_face(VertexHandle v0, VertexHandle v1, VertexHandle v2);
-    FaceHandle add_face(VertexHandle v0, VertexHandle v1, VertexHandle v2, VertexHandle v3);
-    FaceHandle add_face(std::vector<VertexHandle> vhandles);
-    FaceHandle add_face(VertexHandle const *vhandles, size_t vcnt);
+    /// smart collections for VALID primitives (EXCLUDING deleted ones)
+    ///
+    /// 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}; }
 
     // helper
 public:
@@ -81,184 +75,466 @@ public:
     /// Asserts that mesh invariants hold, e.g. that the half-edge stored in a face actually bounds that face
     void assert_consistency() const;
 
+    // ctor
+public:
+    Mesh() = default;
+
+    /// Meshes can be neither moved nor copied because properties depend on the Mesh address
+    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:
     // reserves a certain number of primitives
     void reserve_faces(size_t capacity) { mFaces.reserve(capacity); }
     void reserve_vertices(size_t capacity) { mVertices.reserve(capacity); }
+    void reserve_edges(size_t capacity) { mHalfedges.reserve(capacity * 2); }
     void reserve_halfedges(size_t capacity) { mHalfedges.reserve(capacity); }
 
-    // returns the next valid idx starting AFTER the given one
-    // NOTE: the result can be invalid if the input was the last valid one
-    VertexIndex next_valid_idx_after(VertexIndex idx) const;
-    EdgeIndex next_valid_idx_after(EdgeIndex idx) const;
-    FaceIndex next_valid_idx_after(FaceIndex idx) const;
-    HalfedgeIndex next_valid_idx_after(HalfedgeIndex idx) const;
-    // returns the next valid idx starting BEFORE the given one counting downwards
-    VertexIndex prev_valid_idx_before(VertexIndex idx) const;
-    EdgeIndex prev_valid_idx_before(EdgeIndex idx) const;
-    FaceIndex prev_valid_idx_before(FaceIndex idx) const;
-    HalfedgeIndex prev_valid_idx_before(HalfedgeIndex idx) const;
+    int size_faces() const { return mFaces.size(); }
+    int size_vertices() const { return mVertices.size(); }
+    int size_edges() const { return mHalfedges.size() >> 1; }
+    int size_halfedges() const { return mHalfedges.size(); }
+
+    int size_valid_faces() const { return mFaces.size() - mDeletedFaces; }
+    int size_valid_vertices() const { return mVertices.size() - mDeletedVertices; }
+    int size_valid_edges() const { return (mHalfedges.size() - mDeletedHalfedges) >> 1; }
+    int size_valid_halfedges() const { return mHalfedges.size() - mDeletedHalfedges; }
+
+    // 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;
+
+    /// Adds a single non-connected vertex
+    /// Does NOT invalidate iterators!
+    vertex_handle 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)
+    face_handle add_face(vertex_handle const *vhandles, size_t vcnt);
+
+    // Iterators
+    vertex_iterator vertices_begin() const { return {{this, vertex_index(0)}}; }
+    vertex_iterator vertices_end() const { return {{this, vertex_index(mVertices.size())}}; }
+    valid_vertex_iterator valid_vertices_begin() const { return {{this, vertex_index(0)}}; }
+    valid_vertex_iterator valid_vertices_end() const { return {{this, vertex_index(mVertices.size())}}; }
+
+    face_iterator faces_begin() const { return {{this, face_index(0)}}; }
+    face_iterator faces_end() const { return {{this, face_index(mFaces.size())}}; }
+    valid_face_iterator valid_faces_begin() const { return {{this, face_index(0)}}; }
+    valid_face_iterator valid_faces_end() const { return {{this, face_index(mFaces.size())}}; }
+
+    edge_iterator edges_begin() const { return {{this, edge_index(0)}}; }
+    edge_iterator edges_end() const { return {{this, edge_index(mHalfedges.size() >> 1)}}; }
+    valid_edge_iterator valid_edges_begin() const { return {{this, edge_index(0)}}; }
+    valid_edge_iterator valid_edges_end() const { return {{this, edge_index(mHalfedges.size() >> 1)}}; }
+
+    halfedge_iterator halfedges_begin() const { return {{this, halfedge_index(0)}}; }
+    halfedge_iterator halfedges_end() const { return {{this, halfedge_index(mHalfedges.size())}}; }
+    valid_halfedge_iterator valid_halfedges_begin() const { return {{this, halfedge_index(0)}}; }
+    valid_halfedge_iterator valid_halfedges_end() const { return {{this, halfedge_index(mHalfedges.size())}}; }
 
     // internal datastructures
 private:
-    struct Face
+    struct face
     {
-        HalfedgeIndex halfedge; ///< one half-edge bounding this face
+        halfedge_index halfedge; ///< one half-edge bounding this face
 
         bool is_valid() const { return halfedge.is_valid(); }
-        void set_deleted() { halfedge = HalfedgeIndex::invalid(); }
+        void set_deleted() { halfedge = halfedge_index::invalid(); }
     };
 
-    struct Vertex
+    struct vertex
     {
-        HalfedgeIndex outgoing_halfedge;
+        halfedge_index outgoing_halfedge;
 
         /// a vertex can be valid even without outgoing halfedge
         bool is_valid() const { return outgoing_halfedge.value >= -1; }
-        void set_deleted() { outgoing_halfedge = HalfedgeIndex(-2); }
+        void set_deleted() { outgoing_halfedge = halfedge_index(-2); }
     };
 
-    struct Halfedge
+    struct halfedge
     {
-        VertexIndex vertex;
-        FaceIndex face;              ///< might be invalid if boundary
-        HalfedgeIndex next_halfedge; ///< CCW
-        HalfedgeIndex prev_halfedge; ///< CW
+        vertex_index vertex;
+        face_index face;              ///< might be invalid if boundary
+        halfedge_index next_halfedge; ///< CCW
+        halfedge_index prev_halfedge; ///< CW
         // opposite half-edge idx is "idx ^ 1"
         // edge idx is "idx >> 1"
 
         bool is_valid() const { return vertex.is_valid(); }
         bool is_boundary() const { return !face.is_valid(); }
-        void set_deleted() { vertex = VertexIndex::invalid(); }
+        void set_deleted() { vertex = vertex_index::invalid(); }
     };
 
     // internal primitives
 private:
-    std::vector<Face> mFaces;
-    std::vector<Vertex> mVertices;
-    std::vector<Halfedge> mHalfedges;
+    std::vector<face> mFaces;
+    std::vector<vertex> mVertices;
+    std::vector<halfedge> mHalfedges;
 
     // internal state
 private:
     bool mCompact = true;
+    int mDeletedFaces = 0;
+    int mDeletedVertices = 0;
+    int mDeletedHalfedges = 0;
+
+    // friends
+private:
+    friend struct vertex_handle;
+    friend struct vertex_collection;
+    friend struct vertex_iterator;
+    friend struct valid_vertex_iterator;
+    friend struct valid_vertex_collection;
+
+    friend struct face_handle;
+    friend struct face_collection;
+    friend struct face_iterator;
+    friend struct valid_face_iterator;
+    friend struct valid_face_collection;
+
+    friend struct edge_handle;
+    friend struct edge_collection;
+    friend struct edge_iterator;
+    friend struct valid_edge_iterator;
+    friend struct valid_edge_collection;
+
+    friend struct halfedge_handle;
+    friend struct halfedge_collection;
+    friend struct halfedge_iterator;
+    friend struct valid_halfedge_iterator;
+    friend struct valid_halfedge_collection;
 };
 
 /// ======== IMPLEMENTATION ========
 
-VertexHandle Mesh::add_vertex()
+vertex_handle Mesh::add_vertex()
 {
     auto idx = (int)mVertices.size();
-    mVertices.push_back(Vertex());
-    return handle_of(VertexIndex(idx));
+    mVertices.push_back(vertex());
+    /// TODO: properties
+    return handle_of(vertex_index(idx));
 }
 
-FaceHandle Mesh::add_face(const VertexHandle *vhandles, size_t vcnt)
+face_handle Mesh::add_face(const vertex_handle *vhandles, size_t vcnt)
 {
     auto fidx = (int)mFaces.size();
-    Face f;
+    face f;
+    /// TODO: properties
     assert(0 && "implement me"); /// TODO
     mFaces.push_back(f);
-    return handle_of(FaceIndex(fidx));
-}
-
-FaceHandle Mesh::add_face(VertexHandle v0, VertexHandle v1, VertexHandle v2)
-{
-    VertexHandle vs[3] = {v0, v1, v2};
-    return add_face(vs);
-}
-
-FaceHandle Mesh::add_face(VertexHandle v0, VertexHandle v1, VertexHandle v2, VertexHandle v3)
-{
-    VertexHandle vs[4] = {v0, v1, v2, v3};
-    return add_face(vs);
-}
-
-FaceHandle Mesh::add_face(std::vector<VertexHandle> vhandles)
-{
-    return add_face(vhandles.data(), vhandles.size());
+    return handle_of(face_index(fidx));
 }
 
-template <size_t N>
-FaceHandle Mesh::add_face(const VertexHandle (&vhandles)[N])
+vertex_index Mesh::next_valid_idx_from(vertex_index idx) const
 {
-    return add_face(vhandles, N);
-}
-
-VertexIndex Mesh::next_valid_idx_after(VertexIndex idx) const
-{
-    for (auto i = idx.value + 1; i < (int)mVertices.size(); ++i)
+    for (auto i = idx.value; i < (int)mVertices.size(); ++i)
         if (mVertices[i].is_valid())
-            return VertexIndex(i);
-    return {}; // invalid
+            return vertex_index(i);
+    return vertex_index(mVertices.size()); // end index
 }
 
-VertexIndex Mesh::prev_valid_idx_before(VertexIndex idx) const
+vertex_index Mesh::prev_valid_idx_from(vertex_index idx) const
 {
-    for (auto i = idx.value - 1; i >= 0; --i)
+    for (auto i = idx.value; i >= 0; --i)
         if (mVertices[i].is_valid())
-            return VertexIndex(i);
+            return vertex_index(i);
     return {}; // invalid
 }
 
-EdgeIndex Mesh::next_valid_idx_after(EdgeIndex idx) const
+edge_index Mesh::next_valid_idx_from(edge_index idx) const
 {
-    for (auto i = (idx.value + 1) << 1; i < (int)mHalfedges.size(); i += 2)
+    for (auto i = idx.value << 1; i < (int)mHalfedges.size(); i += 2)
         if (mHalfedges[i].is_valid())
-            return EdgeIndex(i >> 1);
-    return {}; // invalid
+            return edge_index(i >> 1);
+    return edge_index(mHalfedges.size() >> 1); // end index
 }
 
-EdgeIndex Mesh::prev_valid_idx_before(EdgeIndex idx) const
+edge_index Mesh::prev_valid_idx_from(edge_index idx) const
 {
-    for (auto i = (idx.value - 1) << 1; i >= 0; i -= 2)
+    for (auto i = idx.value << 1; i >= 0; i -= 2)
         if (mHalfedges[i].is_valid())
-            return EdgeIndex(i >> 1);
+            return edge_index(i >> 1);
     return {}; // invalid
 }
 
-FaceIndex Mesh::next_valid_idx_after(FaceIndex idx) const
+face_index Mesh::next_valid_idx_from(face_index idx) const
 {
-    for (auto i = idx.value + 1; i < (int)mFaces.size(); ++i)
+    for (auto i = idx.value; i < (int)mFaces.size(); ++i)
         if (mFaces[i].is_valid())
-            return FaceIndex(i);
-    return {}; // invalid
+            return face_index(i);
+    return face_index(mFaces.size()); // end index
 }
 
-FaceIndex Mesh::prev_valid_idx_before(FaceIndex idx) const
+face_index Mesh::prev_valid_idx_from(face_index idx) const
 {
-    for (auto i = idx.value - 1; i >= 0; --i)
+    for (auto i = idx.value; i >= 0; --i)
         if (mFaces[i].is_valid())
-            return FaceIndex(i);
+            return face_index(i);
     return {}; // invalid
 }
 
-HalfedgeIndex Mesh::next_valid_idx_after(HalfedgeIndex idx) const
+halfedge_index Mesh::next_valid_idx_from(halfedge_index idx) const
 {
-    for (auto i = idx.value + 1; i < (int)mHalfedges.size(); ++i)
+    for (auto i = idx.value; i < (int)mHalfedges.size(); ++i)
         if (mHalfedges[i].is_valid())
-            return HalfedgeIndex(i);
-    return {}; // invalid
+            return halfedge_index(i);
+    return halfedge_index(mHalfedges.size()); // end index
 }
 
-HalfedgeIndex Mesh::prev_valid_idx_before(HalfedgeIndex idx) const
+halfedge_index Mesh::prev_valid_idx_from(halfedge_index idx) const
 {
-    for (auto i = idx.value - 1; i >= 0; --i)
+    for (auto i = idx.value; i >= 0; --i)
         if (mHalfedges[i].is_valid())
-            return HalfedgeIndex(i);
+            return halfedge_index(i);
     return {}; // invalid
 }
 
 /// ======== ITERATOR IMPLEMENTATION ========
 
-skipping_vertex_iterator &skipping_vertex_iterator::operator++()
+valid_vertex_iterator &valid_vertex_iterator::operator++()
 {
-    handle.idx = handle.mesh->next_valid_idx_after(handle.idx);
+    handle.idx.value++;
+    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
     return *this;
 }
 vertex_iterator &vertex_iterator::operator++()
 {
     handle.idx.value++;
-    if (handle.idx.value >= handle.mesh->size_vertices())
-        handle.idx.value = -1;
     return *this;
 }
+
+valid_face_iterator &valid_face_iterator::operator++()
+{
+    handle.idx.value++;
+    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
+    return *this;
+}
+face_iterator &face_iterator::operator++()
+{
+    handle.idx.value++;
+    return *this;
+}
+
+valid_edge_iterator &valid_edge_iterator::operator++()
+{
+    handle.idx.value++;
+    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
+    return *this;
+}
+edge_iterator &edge_iterator::operator++()
+{
+    handle.idx.value++;
+    return *this;
+}
+
+valid_halfedge_iterator &valid_halfedge_iterator::operator++()
+{
+    handle.idx.value++;
+    handle.idx = handle.mesh->next_valid_idx_from(handle.idx);
+    return *this;
+}
+halfedge_iterator &halfedge_iterator::operator++()
+{
+    handle.idx.value++;
+    return *this;
+}
+
+/// ======== RANGES IMPLEMENTATION ========
+
+// - Vertices -
+
+int vertex_collection::size() const
+{
+    return mesh->size_vertices();
+}
+
+void vertex_collection::reserve(int capacity) const
+{
+    mesh->reserve_vertices(capacity);
+}
+
+vertex_handle vertex_collection::add() const
+{
+    return mesh->add_vertex();
+}
+
+vertex_iterator vertex_collection::begin() const
+{
+    return mesh->vertices_begin();
+}
+
+vertex_iterator vertex_collection::end() const
+{
+    return mesh->vertices_end();
+}
+
+int valid_vertex_collection::size() const
+{
+    return mesh->size_valid_vertices();
+}
+
+valid_vertex_iterator valid_vertex_collection::begin() const
+{
+    return mesh->valid_vertices_begin();
+}
+
+valid_vertex_iterator valid_vertex_collection::end() const
+{
+    return mesh->valid_vertices_end();
+}
+
+// - Faces -
+
+int face_collection::size() const
+{
+    return mesh->size_faces();
+}
+
+void face_collection::reserve(int capacity) const
+{
+    mesh->reserve_faces(capacity);
+}
+
+face_handle face_collection::add_face(const vertex_handle *vhandles, size_t vcnt) const
+{
+    return mesh->add_face(vhandles, vcnt);
+}
+
+face_handle face_collection::add_face(vertex_handle v0, vertex_handle v1, vertex_handle v2) const
+{
+    vertex_handle vs[3] = {v0, v1, v2};
+    return add_face(vs);
+}
+
+face_handle face_collection::add_face(vertex_handle v0, vertex_handle v1, vertex_handle v2, vertex_handle v3) const
+{
+    vertex_handle vs[4] = {v0, v1, v2, v3};
+    return add_face(vs);
+}
+
+face_handle face_collection::add_face(std::vector<vertex_handle> const &vhandles) const
+{
+    return add_face(vhandles.data(), vhandles.size());
+}
+
+template <size_t N>
+face_handle face_collection::add_face(const vertex_handle (&vhandles)[N]) const
+{
+    return add_face(vhandles, N);
+}
+
+face_iterator face_collection::begin() const
+{
+    return mesh->faces_begin();
+}
+
+face_iterator face_collection::end() const
+{
+    return mesh->faces_end();
+}
+
+int valid_face_collection::size() const
+{
+    return mesh->size_valid_faces();
+}
+
+valid_face_iterator valid_face_collection::begin() const
+{
+    return mesh->valid_faces_begin();
+}
+
+valid_face_iterator valid_face_collection::end() const
+{
+    return mesh->valid_faces_end();
+}
+
+// - Edges -
+
+int edge_collection::size() const
+{
+    return mesh->size_edges();
+}
+
+void edge_collection::reserve(int capacity) const
+{
+    mesh->reserve_edges(capacity);
+}
+
+edge_iterator edge_collection::begin() const
+{
+    return mesh->edges_begin();
+}
+
+edge_iterator edge_collection::end() const
+{
+    return mesh->edges_end();
+}
+
+int valid_edge_collection::size() const
+{
+    return mesh->size_valid_edges();
+}
+
+valid_edge_iterator valid_edge_collection::begin() const
+{
+    return mesh->valid_edges_begin();
+}
+
+valid_edge_iterator valid_edge_collection::end() const
+{
+    return mesh->valid_edges_end();
+}
+
+// - Halfedges -
+
+int halfedge_collection::size() const
+{
+    return mesh->size_halfedges();
+}
+
+void halfedge_collection::reserve(int capacity) const
+{
+    mesh->reserve_halfedges(capacity);
+}
+
+halfedge_iterator halfedge_collection::begin() const
+{
+    return mesh->halfedges_begin();
+}
+
+halfedge_iterator halfedge_collection::end() const
+{
+    return mesh->halfedges_end();
+}
+
+int valid_halfedge_collection::size() const
+{
+    return mesh->size_valid_halfedges();
+}
+
+valid_halfedge_iterator valid_halfedge_collection::begin() const
+{
+    return mesh->valid_halfedges_begin();
+}
+
+valid_halfedge_iterator valid_halfedge_collection::end() const
+{
+    return mesh->valid_halfedges_end();
+}
 }
diff --git a/src/polymesh/Ranges.hh b/src/polymesh/Ranges.hh
deleted file mode 100644
index cf754cbc8b827609012b61712d0afbd1b71b314e..0000000000000000000000000000000000000000
--- a/src/polymesh/Ranges.hh
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "Iterators.hh"
-
-namespace polymesh
-{
-template<typename IteratorT>
-struct iterator_range
-{
-    IteratorT _begin;
-    IteratorT _end;
-
-    IteratorT begin() const { return _begin; }
-    IteratorT end() const { return _end; }
-};
-
-using vertex_range = iterator_range<vertex_iterator>;
-using skipping_vertex_range = iterator_range<skipping_vertex_iterator>;
-}
diff --git a/src/polymesh/VertexHandle.hh b/src/polymesh/VertexHandle.hh
deleted file mode 100644
index 4e859aef5efd29ed1f054fae9741b161ee59feed..0000000000000000000000000000000000000000
--- a/src/polymesh/VertexHandle.hh
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include "VertexIndex.hh"
-
-namespace polymesh
-{
-struct Mesh;
-
-struct VertexHandle
-{
-    Mesh const* mesh;
-    VertexIndex idx;
-
-    VertexHandle(Mesh const* mesh, VertexIndex idx) : mesh(mesh), idx(idx) {}
-};
-}
diff --git a/src/polymesh/VertexIndex.hh b/src/polymesh/VertexIndex.hh
deleted file mode 100644
index ab55c5747356bec975aa7c3875eb4a76b4dc00af..0000000000000000000000000000000000000000
--- a/src/polymesh/VertexIndex.hh
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-namespace polymesh
-{
-struct VertexIndex
-{
-    int value = -1;
-
-    VertexIndex() = default;
-    explicit VertexIndex(int idx) : value(idx) {}
-
-    bool is_valid() const { return value >= 0; }
-    static VertexIndex invalid() { return {}; }
-
-    bool operator==(VertexIndex const& rhs) const { return value == rhs.value; }
-    bool operator!=(VertexIndex const& rhs) const { return value != rhs.value; }
-};
-}
diff --git a/src/polymesh/cursors.hh b/src/polymesh/cursors.hh
new file mode 100644
index 0000000000000000000000000000000000000000..15de6afa3fe110eb5b483a6e0e822bced0c56dee
--- /dev/null
+++ b/src/polymesh/cursors.hh
@@ -0,0 +1,118 @@
+#pragma once
+
+namespace polymesh
+{
+struct Mesh;
+
+// ======================== INDICES ========================
+
+struct face_index
+{
+    int value = -1;
+
+    face_index() = default;
+    explicit face_index(int idx) : value(idx) {}
+
+    bool is_valid() const { return value >= 0; }
+    static face_index invalid() { return {}; }
+
+    bool operator==(face_index const& rhs) const { return value == rhs.value; }
+    bool operator!=(face_index const& rhs) const { return value != rhs.value; }
+};
+
+struct vertex_index
+{
+    int value = -1;
+
+    vertex_index() = default;
+    explicit vertex_index(int idx) : value(idx) {}
+
+    bool is_valid() const { return value >= 0; }
+    static vertex_index invalid() { return {}; }
+
+    bool operator==(vertex_index const& rhs) const { return value == rhs.value; }
+    bool operator!=(vertex_index const& rhs) const { return value != rhs.value; }
+};
+
+struct edge_index
+{
+    int value = -1;
+
+    edge_index() = default;
+    explicit edge_index(int idx) : value(idx) {}
+
+    bool is_valid() const { return value >= 0; }
+    static edge_index invalid() { return {}; }
+
+    bool operator==(edge_index const& rhs) const { return value == rhs.value; }
+    bool operator!=(edge_index const& rhs) const { return value != rhs.value; }
+};
+
+struct halfedge_index
+{
+    int value = -1;
+
+    halfedge_index() = default;
+    explicit halfedge_index(int idx) : value(idx) {}
+
+    bool is_valid() const { return value >= 0; }
+    static halfedge_index invalid() { return {}; }
+
+    bool operator==(halfedge_index const& rhs) const { return value == rhs.value; }
+    bool operator!=(halfedge_index const& rhs) const { return value != rhs.value; }
+};
+
+// ======================== HANDLES ========================
+
+struct face_handle
+{
+    Mesh const* mesh;
+    face_index idx;
+
+    face_handle(Mesh const* mesh, face_index idx) : mesh(mesh), idx(idx) {}
+
+    bool operator==(face_index const& rhs) const { return idx == rhs; }
+    bool operator!=(face_index const& rhs) const { return idx != rhs; }
+    bool operator==(face_handle const& rhs) const { return mesh == rhs.mesh && idx == rhs.idx; }
+    bool operator!=(face_handle const& rhs) const { return mesh != rhs.mesh || idx != rhs.idx; }
+};
+
+struct vertex_handle
+{
+    Mesh const* mesh;
+    vertex_index idx;
+
+    vertex_handle(Mesh const* mesh, vertex_index idx) : mesh(mesh), idx(idx) {}
+
+    bool operator==(vertex_index const& rhs) const { return idx == rhs; }
+    bool operator!=(vertex_index const& rhs) const { return idx != rhs; }
+    bool operator==(vertex_handle const& rhs) const { return mesh == rhs.mesh && idx == rhs.idx; }
+    bool operator!=(vertex_handle const& rhs) const { return mesh != rhs.mesh || idx != rhs.idx; }
+};
+
+struct edge_handle
+{
+    Mesh const* mesh;
+    edge_index idx;
+
+    edge_handle(Mesh const* mesh, edge_index idx) : mesh(mesh), idx(idx) {}
+
+    bool operator==(edge_index const& rhs) const { return idx == rhs; }
+    bool operator!=(edge_index const& rhs) const { return idx != rhs; }
+    bool operator==(edge_handle const& rhs) const { return mesh == rhs.mesh && idx == rhs.idx; }
+    bool operator!=(edge_handle const& rhs) const { return mesh != rhs.mesh || idx != rhs.idx; }
+};
+
+struct halfedge_handle
+{
+    Mesh const* mesh;
+    halfedge_index idx;
+
+    halfedge_handle(Mesh const* mesh, halfedge_index idx) : mesh(mesh), idx(idx) {}
+
+    bool operator==(halfedge_index const& rhs) const { return idx == rhs; }
+    bool operator!=(halfedge_index const& rhs) const { return idx != rhs; }
+    bool operator==(halfedge_handle const& rhs) const { return mesh == rhs.mesh && idx == rhs.idx; }
+    bool operator!=(halfedge_handle const& rhs) const { return mesh != rhs.mesh || idx != rhs.idx; }
+};
+}
diff --git a/src/polymesh/iterators.hh b/src/polymesh/iterators.hh
new file mode 100644
index 0000000000000000000000000000000000000000..96ed27cab8222e02d44c5c93ee76690aa8de3981
--- /dev/null
+++ b/src/polymesh/iterators.hh
@@ -0,0 +1,227 @@
+#pragma once
+
+#include <cassert>
+
+#include "cursors.hh"
+
+// For iterator interfaces, see http://anderberg.me/2016/07/04/c-custom-iterators/
+// Note: some iterator methods are implemented at the end of Mesh.hh to ensure inlining
+
+namespace polymesh
+{
+struct valid_vertex_iterator
+{
+    valid_vertex_iterator() = default;
+    valid_vertex_iterator(vertex_handle handle) : handle(handle) {}
+
+    vertex_handle operator*() const { return handle; }
+    valid_vertex_iterator& operator++();
+    valid_vertex_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(valid_vertex_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(valid_vertex_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    vertex_handle handle;
+};
+
+struct vertex_iterator
+{
+    vertex_iterator() = default;
+    vertex_iterator(vertex_handle handle) : handle(handle) {}
+
+    vertex_handle operator*() const { return handle; }
+    vertex_iterator& operator++();
+    vertex_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(vertex_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(vertex_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    vertex_handle handle;
+};
+
+struct valid_face_iterator
+{
+    valid_face_iterator() = default;
+    valid_face_iterator(face_handle handle) : handle(handle) {}
+
+    face_handle operator*() const { return handle; }
+    valid_face_iterator& operator++();
+    valid_face_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(valid_face_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(valid_face_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    face_handle handle;
+};
+
+struct face_iterator
+{
+    face_iterator() = default;
+    face_iterator(face_handle handle) : handle(handle) {}
+
+    face_handle operator*() const { return handle; }
+    face_iterator& operator++();
+    face_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(face_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(face_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    face_handle handle;
+};
+
+struct valid_edge_iterator
+{
+    valid_edge_iterator() = default;
+    valid_edge_iterator(edge_handle handle) : handle(handle) {}
+
+    edge_handle operator*() const { return handle; }
+    valid_edge_iterator& operator++();
+    valid_edge_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(valid_edge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(valid_edge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    edge_handle handle;
+};
+
+struct edge_iterator
+{
+    edge_iterator() = default;
+    edge_iterator(edge_handle handle) : handle(handle) {}
+
+    edge_handle operator*() const { return handle; }
+    edge_iterator& operator++();
+    edge_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(edge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(edge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    edge_handle handle;
+};
+
+struct valid_halfedge_iterator
+{
+    valid_halfedge_iterator() = default;
+    valid_halfedge_iterator(halfedge_handle handle) : handle(handle) {}
+
+    halfedge_handle operator*() const { return handle; }
+    valid_halfedge_iterator& operator++();
+    valid_halfedge_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(valid_halfedge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(valid_halfedge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    halfedge_handle handle;
+};
+
+struct halfedge_iterator
+{
+    halfedge_iterator() = default;
+    halfedge_iterator(halfedge_handle handle) : handle(handle) {}
+
+    halfedge_handle operator*() const { return handle; }
+    halfedge_iterator& operator++();
+    halfedge_iterator operator++(int)
+    {
+        auto i = *this;
+        return ++i;
+    }
+    bool operator==(halfedge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx == rhs.handle.idx;
+    }
+    bool operator!=(halfedge_iterator const& rhs) const
+    {
+        assert(handle.mesh == rhs.handle.mesh && "comparing iterators from different meshes");
+        return handle.idx != rhs.handle.idx;
+    }
+
+private:
+    halfedge_handle handle;
+};
+}
diff --git a/src/polymesh/ranges.hh b/src/polymesh/ranges.hh
new file mode 100644
index 0000000000000000000000000000000000000000..74ee945b47f786027609aca7e56156648d7e0007
--- /dev/null
+++ b/src/polymesh/ranges.hh
@@ -0,0 +1,160 @@
+#pragma once
+
+#include <cstddef>
+#include <vector>
+
+#include "iterators.hh"
+
+namespace polymesh
+{
+/// Collection of all vertices of a mesh, including deleted ones
+/// Basically a smart std::vector
+struct vertex_collection
+{
+    Mesh* mesh;
+
+    /// Number of vertices, INCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+    /// Ensures that a given number of vertices can be stored without reallocation
+    void reserve(int capacity) const;
+
+    /// Adds a new vertex and returns its handle
+    /// Does NOT invalidate any iterator!
+    vertex_handle add() const;
+
+    // TODO: delete
+
+    // Iteration:
+    vertex_iterator begin() const;
+    vertex_iterator end() const;
+};
+
+/// Same as vertex_collection but only including valid, non-deleted vertices
+/// (a bit slower than the normal collection)
+/// (if mesh->is_compact(), identical to vertex_collection)
+struct valid_vertex_collection
+{
+    Mesh const* mesh;
+
+    /// Number of vertices, EXCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+
+    // Iteration:
+    valid_vertex_iterator begin() const;
+    valid_vertex_iterator end() const;
+};
+
+/// Collection of all faces of a mesh, including deleted ones
+/// Basically a smart std::vector
+struct face_collection
+{
+    Mesh* mesh;
+
+    /// Number of vertices, INCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+    /// Ensures that a given number of faces can be stored without reallocation
+    void reserve(int capacity) const;
+
+    /// 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)
+    template <size_t N>
+    face_handle add_face(const vertex_handle (&vhandles)[N]) const;
+    face_handle add_face(vertex_handle v0, vertex_handle v1, vertex_handle v2) const;
+    face_handle add_face(vertex_handle v0, vertex_handle v1, vertex_handle v2, vertex_handle v3) const;
+    face_handle add_face(std::vector<vertex_handle> const &vhandles) const;
+    face_handle add_face(vertex_handle const *vhandles, size_t vcnt) const;
+
+    // TODO: delete
+
+    // Iteration:
+    face_iterator begin() const;
+    face_iterator end() const;
+};
+
+/// Same as face_collection but only including valid, non-deleted faces
+/// (a bit slower than the normal collection)
+/// (if mesh->is_compact(), identical to face_collection)
+struct valid_face_collection
+{
+    Mesh const* mesh;
+
+    /// Number of faces, EXCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+
+    // Iteration:
+    valid_face_iterator begin() const;
+    valid_face_iterator end() const;
+};
+
+/// Collection of all edges of a mesh, including deleted ones
+/// Basically a smart std::vector
+struct edge_collection
+{
+    Mesh* mesh;
+
+    /// Number of vertices, INCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+    /// Ensures that a given number of edges can be stored without reallocation
+    void reserve(int capacity) const;
+
+    // Iteration:
+    edge_iterator begin() const;
+    edge_iterator end() const;
+};
+
+/// Same as edge_collection but only including valid, non-deleted edges
+/// (a bit slower than the normal collection)
+/// (if mesh->is_compact(), identical to edge_collection)
+struct valid_edge_collection
+{
+    Mesh const* mesh;
+
+    /// Number of edges, EXCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+
+    // Iteration:
+    valid_edge_iterator begin() const;
+    valid_edge_iterator end() const;
+};
+
+/// Collection of all half-edges of a mesh, including deleted ones
+/// Basically a smart std::vector
+struct halfedge_collection
+{
+    Mesh* mesh;
+
+    /// Number of vertices, INCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+    /// Ensures that a given number of half-edges can be stored without reallocation
+    void reserve(int capacity) const;
+
+    // Iteration:
+    halfedge_iterator begin() const;
+    halfedge_iterator end() const;
+};
+
+/// Same as halfedge_collection but only including valid, non-deleted halfedges
+/// (a bit slower than the normal collection)
+/// (if mesh->is_compact(), identical to halfedge_collection)
+struct valid_halfedge_collection
+{
+    Mesh const* mesh;
+
+    /// Number of halfedges, EXCLUDING deleted/invalid ones
+    /// O(1) computation
+    int size() const;
+
+    // Iteration:
+    valid_halfedge_iterator begin() const;
+    valid_halfedge_iterator end() const;
+};
+
+}