From 66ef05500a3d22686aa4ac771262f2dedea76d96 Mon Sep 17 00:00:00 2001
From: Philip Trettner <Philip.Trettner@rwth-aachen.de>
Date: Wed, 20 Nov 2019 15:09:44 +0100
Subject: [PATCH] more doc

---
 docs/conf.py                  |   2 +
 docs/getting-started.rst      |  11 +++
 docs/mesh-topology.rst        | 123 ++++++++++++++++++++++++++++++++--
 docs/misc.rst                 |   6 ++
 docs/reference.rst            |  77 +++++++++++++++++++++
 src/polymesh/Mesh.hh          |   2 +-
 src/polymesh/cursors.hh       |   2 -
 src/polymesh/low_level_api.hh |   4 --
 8 files changed, 214 insertions(+), 13 deletions(-)

diff --git a/docs/conf.py b/docs/conf.py
index e3a7e3e..4488b18 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -37,6 +37,8 @@ extensions = [
     'breathe',
 ]
 
+cpp_index_common_prefix = ['polymesh::']
+
 breathe_default_project = 'polymesh'
 
 # Add any paths that contain templates here, relative to this directory.
diff --git a/docs/getting-started.rst b/docs/getting-started.rst
index 405324b..5d3b67b 100644
--- a/docs/getting-started.rst
+++ b/docs/getting-started.rst
@@ -31,6 +31,14 @@ Quickstart
         smoothed_pos[v] = v.adjacent_vertices().avg(pos);
 
 
+Namespaces
+----------
+
+In its implementation, polymesh uses the ``polymesh::`` namespace.
+However, long namespace are cumbersome and by default ``pm::`` is provided as an alias.
+All examples will use the short version.
+
+
 CMake Integration
 -----------------
 
@@ -68,6 +76,9 @@ Working with meshes and attributes involves a lot of iteration.
 Often, aggregate statistics like averages, minimums, maximums, and mapped/filtered ranges are needed.
 Thus, polymesh provides a clean, composable, and powerful range API (see :doc:`mesh-topology` and :doc:`smart-ranges`).
 
+Polymesh is performance-oriented but still concerned with usability and safety.
+C++ Exceptions are not used but many assertions will trigger in Debug or RelWithDebInfo builds if the API is used wrongly.
+
 
 Header Structure
 ----------------
diff --git a/docs/mesh-topology.rst b/docs/mesh-topology.rst
index c447e98..3b85ae8 100644
--- a/docs/mesh-topology.rst
+++ b/docs/mesh-topology.rst
@@ -1,34 +1,145 @@
 Mesh and Topology
 =================
 
-TODO
-
 
 Mesh Class
 -------------
 
-TODO
+The core data structure of polymesh is :class:`polymesh::Mesh`, a half-edge data structure for managing mesh topology.
+
+For an introduction to this type of data structure please refer to:
+
+* https://en.wikipedia.org/wiki/Doubly_connected_edge_list
+* http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm
+* https://www.openmesh.org/media/Documentations/OpenMesh-Doc-Latest/a03930.html
+
+The Mesh only contains pure topology, no positions, no normals.
+:doc:`attributes` are externally stored and behave like value types.
+Because attributes and handles have a pointer to the mesh they belong to, a :class:`polymesh::Mesh` is non-copyable and non-movable.
+Functions returning a mesh with new ownership should use ``std::unique_ptr<pm::Mesh>`` or ``std::shared_ptr<pm::Mesh>``.
+
+:func:`polymesh::Mesh::create` is a static helper function to create a ``unique_ptr<Mesh>`` though the typical way to just use ``pm::Mesh`` as a member or as a local variable.
 
 
 Memory Model
 ------------
 
-TODO
+High-performance is one of the primary goals of polymesh and a cache-friendly contiguous data layout is often key to good performance.
+Polymesh stores its topology in 6 arrays of ints: halfedge per face, outgoing halfedge per vertex, and vertex, face, next/prev halfedge per halfedge.
+Primitives (vertices, edges, faces, halfedges) are strongly typed indices that point into these arrays (see next subsection).
+
+These arrays behave like a ``std::vector`` with size and capacity, using exponential reallocation for amortized O(1) to add a new primitive.
+Deleting primitives does not invalidate any other handle or index nor does it move data.
+The primitive is simply marked as "deleted" and is basically a hole in the array.
+Iterating over primitives ignores deleted ones by default.
+The function :func:`polymesh::Mesh::compactify()` can be used to make the mesh "compact" again, i.e. permuting all primitives such that no holes are left.
+This invalidates handles.
+
+:doc:`attributes` mirror the memory layout of their respective primitive and are thus also affected by ``compactify()``.
 
 
 Handles and Indices
 -------------------
 
-TODO
+Each primitive has a handle and an index version:
+
+* :class:`polymesh::vertex_index` and :class:`polymesh::vertex_handle`
+* :class:`polymesh::face_index` and :class:`polymesh::face_handle`
+* :class:`polymesh::edge_index` and :class:`polymesh::edge_handle`
+* :class:`polymesh::halfedge_index` and :class:`polymesh::halfedge_handle`
+
+Roughly spoken, an index is a strongly typed integer representing the index of a primitive in a :class:`polymesh::Mesh`.
+Handles are a combination of pointer to mesh and index such that topological operations like getting the face belonging to a halfedge can be done on the handle.
+
+Because handles contain a pointer to the mesh, they consume more memory than their corresponding index.
+As a rule of thumb, if primitives are stored on the heap (e.g. in an ``std::vector`` or an ``std::set``) it makes sense to store the index.
+For single primitives or local variables, handles are safer and more comfortable.
+
+Handles and indices can be explicitly cast to integer (``int(my_handle)``) to get the index.
+Invalid indices and handles can be created (e.g. ``vertex_index::invalid``), which correspond to index ``-1``.
+
+Indices can be converted to handles.
+For example, given ``Mesh m`` and ``vertex_index v`` (or as ``int i``), the following are handles (and equivalent): ``v.of(m)``, ``m[v]``, ``m.vertices()[i]``.
+
+Handles and indices can be "indexed" via subscript by attributes and functors (taking an index or handle, returning some type).
+For example, given a ``vertex_attribute<tg::pos3> pos`` and a ``vertex_handle v``, the following are equivalent and return the position of the vertex: ``pos[v]`` and ``v[pos]``.
+(This can be either read as "index the position attribute by v" or "return the position of v")
+
+Handles are "smart" in the sense that they can be directly used to iterate over mesh topology and query certain topological properties.
+For example, given a ``face_handle f``, ``f.vertices()`` returns an iterable smart range containing all vertices of this face.
+For all operations, see class references for :class:`polymesh::vertex_handle`, :class:`polymesh::face_handle`, :class:`polymesh::edge_handle`, and :class:`polymesh::halfedge_handle`.
+For more information about the ranges, see :doc:`smart-ranges`.
 
 
 Primitive Collections
 ---------------------
 
-TODO
+Handles can be used to locally navigate the topology of a mesh.
+Primitive collections are used for global topology navigation and for changing the topology.
+Given ``Mesh m``, each primitive has its own collection:
+
+* :class:`polymesh::vertex_collection` via ``m.vertices()``
+* :class:`polymesh::face_collection` via ``m.faces()``
+* :class:`polymesh::edge_collection` via ``m.edges()``
+* :class:`polymesh::halfedge_collection` via ``m.halfedges()``
+
+By default, iteration over these collections skips deleted primitives.
+This is an additional check that can affect performance slightly.
+If deleted primitives should be returned as well (or it is known that no deleted primitives exists, e.g. if :func:`polymesh::Mesh::is_compact` is true), then the ``all_<primitive>`` variants such as ``m.all_vertices()`` can be used.
+
+Topological operations such as adding faces, removing vertices, or splitting edges are all performed via their respective primitive collections.
+E.g. ``m.faces().add(v0, v1, v2)`` adds a new triangle.
+
+Example: ::
+
+    pm::Mesh m;
+    auto pos = pm::vertex_attribute<tg::pos3>(m);
+
+    for (auto v : m.vertices())
+        if (should_remove(v))
+            m.vertices().remove(v);
+
+    tg::rng rng;
+    auto f = m.faces().random(rng);
+
+    auto centroid = f.vertices().avg(pos);
+    auto v = m.faces().split(f);
+    pos[v] = centroid;
 
 
 Low-Level API
 -------------
 
+Primitive collections and handles are a kind of "high-level API".
+They perform many security checks, handle special cases, and sometimes have to do extra work to preserve some useful invariants (like the face-to-halfedge mapping always pointing to a boundary if the face lies on a boundary to have a super fast :func:`polymesh::face_handle::is_boundary` test).
+
+Sometimes, algorithms want to manipulate the half-edge data structure directly and bypass the high-level API.
+This can be done by using the helper function :func:`polymesh::low_level_api` which returns a simple wrapper object that can be used for manipulating the internals of a :class:`polymesh::Mesh`.
+
 TODO
+
+
+Copying a Mesh
+--------------
+
+Handles and attributes (which are external) refer to a mesh by reference, which makes it nontrivial to copy a mesh with all attributes.
+For security reasons, handles to one mesh cannot be used to index into a different mesh unless the handle is cast into an index beforehand.
+
+To make copying a mesh less tedious a few helpers exist:
+
+* :func:`polymesh::Mesh::copy_from` clears a mesh and copies over the topology of another mesh
+* :func:`polymesh::Mesh::copy` creates a new ``unique_ptr<Mesh>`` and copies over the topology
+* ``attribute::copy_from(attribute const&)`` copies attribute data from a different mesh
+* ``attribute::copy_to(Mesh const&)`` copies attribute data to a different mesh
+* :func:`polymesh::copy` creates a mesh and an arbitrary number of attributes and creates copies of them all (returned as tuple)
+
+Example: ::
+
+    // must be included explicitly because 
+    // it needs the relatively expensive header <tuple>
+    #include <polymesh/copy.hh>
+
+    pm::Mesh m;
+    auto pos = pm::vertex_attribute<tg::pos3>(m);
+
+    auto [m2, pos2] = pm::copy(m, pos);
diff --git a/docs/misc.rst b/docs/misc.rst
index 5edd509..41255bb 100644
--- a/docs/misc.rst
+++ b/docs/misc.rst
@@ -5,3 +5,9 @@ Assertions
 ----------
 
 
+Simple Graphs
+-------------
+
+While polymesh is primarily a mesh data structure it can also be used to represent simple graphs, i.e. undirected graphs without self loops and without multi-edges.
+Half-edge data structures cannot represent most non-manifold geometry but that limitations only applies if faces are added.
+A "wireframe mesh" (mesh without faces) can be of arbitrary topology as long as edges are unique (no multi-edge) and start and end vertex are different (no self loops).
diff --git a/docs/reference.rst b/docs/reference.rst
index 257541b..abe7e26 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -1,9 +1,69 @@
 Class Reference
 ===============
 
+Mesh
+----
+
 .. doxygenclass:: polymesh::Mesh
     :members:
 
+Handles and Indices
+-------------------
+
+.. doxygenstruct:: polymesh::primitive_index
+    :members:
+
+.. doxygenstruct:: polymesh::face_index
+    :members:
+
+.. doxygenstruct:: polymesh::vertex_index
+    :members:
+
+.. doxygenstruct:: polymesh::edge_index
+    :members:
+
+.. doxygenstruct:: polymesh::halfedge_index
+    :members:
+
+.. doxygenstruct:: polymesh::primitive_handle
+    :members:
+
+.. doxygenstruct:: polymesh::face_handle
+    :members:
+
+.. doxygenstruct:: polymesh::vertex_handle
+    :members:
+
+.. doxygenstruct:: polymesh::edge_handle
+    :members:
+
+.. doxygenstruct:: polymesh::halfedge_handle
+    :members:
+
+Ranges and Collections
+----------------------
+
+.. doxygenstruct:: polymesh::smart_range
+    :members:
+
+.. doxygenstruct:: polymesh::smart_collection
+    :members:
+
+.. doxygenstruct:: polymesh::face_collection
+    :members:
+
+.. doxygenstruct:: polymesh::vertex_collection
+    :members:
+
+.. doxygenstruct:: polymesh::edge_collection
+    :members:
+
+.. doxygenstruct:: polymesh::halfedge_collection
+    :members:
+
+Attributes
+----------
+
 .. doxygenstruct:: polymesh::vertex_attribute
     :members:
 
@@ -15,3 +75,20 @@ Class Reference
 
 .. doxygenstruct:: polymesh::halfedge_attribute
     :members:
+
+.. doxygenstruct:: polymesh::primitive_attribute
+    :members:
+
+Low-Level API
+-------------
+
+.. doxygenstruct:: polymesh::low_level_api_base
+    :members:
+
+.. doxygenstruct:: polymesh::low_level_api_mutable
+    :members:
+
+Helper
+------
+
+.. doxygenfunction:: polymesh::copy
diff --git a/src/polymesh/Mesh.hh b/src/polymesh/Mesh.hh
index 57bb10f..a15e617 100644
--- a/src/polymesh/Mesh.hh
+++ b/src/polymesh/Mesh.hh
@@ -117,7 +117,7 @@ public:
     Mesh& operator=(Mesh const&) = delete;
     Mesh& operator=(Mesh&&) = delete;
 
-    /// Creates a new mesh and returns a shared_ptr to it
+    /// Creates a new mesh and returns a unique_ptr to it
     static unique_ptr<Mesh> create() { return make_unique<Mesh>(); }
 
     /// Clears this mesh and copies mesh topology, NOT attributes!
diff --git a/src/polymesh/cursors.hh b/src/polymesh/cursors.hh
index c8b8a7e..17a8ee1 100644
--- a/src/polymesh/cursors.hh
+++ b/src/polymesh/cursors.hh
@@ -156,8 +156,6 @@ struct vertex_handle : primitive_handle<vertex_tag>
     halfedge_handle any_incoming_halfedge() const; ///< invalid if isolated
     edge_handle any_edge() const;                  ///< invalid if isolated
 
-    // TODO: make all_faces and faces = all_faces.filter(is_valid)
-
     vertex_halfedge_in_ring incoming_halfedges() const;
     vertex_halfedge_out_ring outgoing_halfedges() const;
     vertex_edge_ring edges() const;
diff --git a/src/polymesh/low_level_api.hh b/src/polymesh/low_level_api.hh
index 68dc588..aebcc64 100644
--- a/src/polymesh/low_level_api.hh
+++ b/src/polymesh/low_level_api.hh
@@ -189,11 +189,7 @@ struct low_level_attribute_api
     {
         MeshT mesh;
         auto attr = mesh.vertices().make_attribute_with_default(0.f);
-        auto attr_check = mesh.vertices().make_attribute_with_default(std::array<float, 32>());
-
         auto offset = int(size_t(&(attr.mData)) - size_t(&attr));
-        POLYMESH_ASSERT(offset == int(size_t(&(attr_check.mData)) - size_t(&attr_check)));
-
         return offset;
     }
 };
-- 
GitLab