diff --git a/docs/algorithms.rst b/docs/algorithms.rst
index f1779f9ba2c10e447f9780ccaf17e9facb017121..01273ecf62494f3c8d82ccf39b30c758db9c2f3d 100644
--- a/docs/algorithms.rst
+++ b/docs/algorithms.rst
@@ -60,6 +60,145 @@ A small helper that merges vertices based on a user criterion.
 
 .. doxygenfunction:: polymesh::deduplicate
 
+
+Delaunay
+--------
+
+An incomplete collection of algorithms for Delaunay triangulations.
+
+::
+
+    #include <polymesh/algorithms/delaunay.hh>
+
+    pm::Mesh m;
+    auto pos = m.vertices().make_attribute<tg::pos3>();
+    load(...);
+
+    // makes mesh surface delaunay via flipping
+    pm::make_delaunay(m, pos);
+
+.. doxygenfunction:: polymesh::make_delaunay
+
+There is also a 2D version that starts with a vertex-only mesh and creates faces via 2D Delaunay:
+
+.. doxygenfunction:: polymesh::create_delaunay_triangulation
+
+
+Edge Split
+----------
+
+A generic edge splitting routine.
+
+::
+
+    #include <polymesh/algorithms/edge_split.hh>
+
+    pm::Mesh m;
+    auto pos = m.vertices().make_attribute<tg::pos3>();
+    load(...);
+
+    auto const target_edge_length = 0.1f;
+ 
+    // split all edges longer than 0.1 in descending length order
+    pm::split_edges_trimesh(m,
+        // function to provide a priority value and signal if the edge should not be split
+        [&](pm::edge_handle e) -> tg::optional<float> {
+            auto const l = pm::edge_length(e, pos);
+            if (l < target_edge_length)
+                return {};
+            return l;
+        },
+        // a function performing the split
+        [&](pm::vertex_handle v, pm::halfedge_handle, pm::vertex_handle v_from, pm::vertex_handle v_to) {
+            pos[v] = mix(pos[v_to], pos[v_from], 0.5f);
+            // .. and propagate other attributes if desire
+        });
+
+For polygonal meshes, splitting edges can either be very simple (only insert a vertex) or complex (re-triangulate polygons, ensure user constraints).
+The following function provides a generic interface:
+
+.. doxygenfunction:: polymesh::split_edges
+
+If the mesh is known to be triangular (and should stay triangular), then the split is well defined and the split function only has to fill in missing attributes:
+
+.. doxygenfunction:: polymesh::split_edges_trimesh
+
+These functions guarantee that meshes stay *compact* (see :ref:`memory-model`) and are, in general, quite fast.
+
+
+Normal Estimation
+-----------------
+
+A simple estimation algorithm for computing smoothed vertex normals.
+Edges that should not be smoothed over can be declared in a generic interface.
+
+::
+
+    #include <polymesh/algorithms/normal_estimation.hh>
+
+    pm::Mesh m;
+    auto pos = m.vertices().make_attribute<tg::pos3>();
+    load(...);
+
+    auto fnormals = pm::face_normals(pos);
+
+    // compute smooth vertex normals without smoothing over more than 60° edges
+    auto vnormals = pm::normal_estimation(fnormals, [&](pm::edge_handle e) {
+        return dot(fnormals[e.faceA()], fnormals[e.faceB()]) < 0.5;
+    });
+
+    // .. or just smooth over everything
+    auto vnormals2 = pm::normal_estimation(pos, [](auto) { return false; });
+
+This function has two versions, one based on face normals, the other on vertex positions (which internally computes face normals):
+
+.. doxygenfunction:: polymesh::normal_estimation(face_attribute<Vec3> const&, IsHardEdgeF&&)
+
+.. doxygenfunction:: polymesh::normal_estimation(vertex_attribute<Pos3> const&, IsHardEdgeF&&)
+
+
+Normalization
+-------------
+
+An incomplete collection of helpers for ensuring different normalization constraints.
+
+::
+
+    #include <polymesh/algorithms/normalize.hh>
+
+    pm::Mesh m;
+    auto pos = m.vertices().make_attribute<tg::pos3>();
+    load(...);
+
+    // translates and uniformly rescales the mesh
+    // so it fits in the [-1, 1] cube and is centered at the origin
+    pm::normalize(pos);
+
+.. doxygenfunction:: polymesh::normalize
+
+
+Cache Optimization
+------------------
+
+An incomplete collection of algorithms that reorder the internal memory layout to improve cache coherence in different scenarios.
+
+::
+
+    #include <polymesh/algorithms/cache-optimization.hh>
+
+    pm::Mesh m;
+    load(...);
+
+    // reorder memory layout for improved performance when performing vertex-traversal algorithms
+    pm::optimize_for_vertex_traversal(m);
+
+
+.. doxygenfunction:: polymesh::optimize_for_face_traversal
+
+.. doxygenfunction:: polymesh::optimize_for_vertex_traversal
+
+
+
 TODO: preserve line breaks in doxygen
 
-TODO: decimate, dedup, delaunay, edge_split, subdivision, interpolation, iteration, normal-estimation, normalize, operations, optimizations, sampling, smoothing, stats, topology, tracing, triangulate
+TODO: decimate, subdivision, interpolation, iteration, sampling, smoothing, stats, topology, tracing, triangulate
diff --git a/docs/mesh-topology.rst b/docs/mesh-topology.rst
index 72fa4788b7f40fac7d2c9643b8f4102f99dbc341..7185499368df63e290ea5a7e54d9105bd0ff6b90 100644
--- a/docs/mesh-topology.rst
+++ b/docs/mesh-topology.rst
@@ -43,6 +43,7 @@ Functions returning a mesh with new ownership should use ``std::unique_ptr<pm::M
 
 :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:
 
 Memory Model
 ------------
diff --git a/docs/reference.rst b/docs/reference.rst
index 675a7940671feedbd09dff8b4ad3f00983cb9e1e..8ff3de521ea509eecf24167236be43d5ab81ad14 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -215,6 +215,40 @@ Algorithms
 
 .. doxygenfunction:: polymesh::deduplicate
 
+.. doxygenfunction:: polymesh::make_delaunay
+
+.. doxygenfunction:: polymesh::create_delaunay_triangulation
+
+.. doxygenfunction:: polymesh::split_edges
+
+.. doxygenfunction:: polymesh::split_edges_trimesh
+
+.. doxygenfunction:: polymesh::normal_estimation(face_attribute<Vec3> const&, IsHardEdgeF&&)
+
+.. doxygenfunction:: polymesh::normal_estimation(vertex_attribute<Pos3> const&, IsHardEdgeF&&)
+
+.. doxygenfunction:: polymesh::normalize
+
+.. doxygenfunction:: polymesh::remove_faces
+
+.. doxygenfunction:: polymesh::remove_edges_and_faces
+
+.. doxygenfunction:: polymesh::optimize_for_face_traversal
+
+.. doxygenfunction:: polymesh::optimize_for_vertex_traversal
+
+.. doxygenfunction:: polymesh::optimize_edges_for_faces
+
+.. doxygenfunction:: polymesh::optimize_vertices_for_faces
+
+.. doxygenfunction:: polymesh::optimize_edges_for_vertices
+
+.. doxygenfunction:: polymesh::optimize_faces_for_vertices
+
+.. doxygenfunction:: polymesh::cache_coherent_face_layout
+
+.. doxygenfunction:: polymesh::cache_coherent_vertex_layout
+
 
 Low-Level API
 -------------
diff --git a/src/polymesh/algorithms.hh b/src/polymesh/algorithms.hh
index bc6d5db191f75a5dc4d9dd031598b08314a16127..759b1c26072a9ea192c0f5d6219ddb732f7e41a2 100644
--- a/src/polymesh/algorithms.hh
+++ b/src/polymesh/algorithms.hh
@@ -15,6 +15,7 @@
 // - intersections
 // - statistics
 
+#include "algorithms/cache-optimization.hh"
 #include "algorithms/components.hh"
 #include "algorithms/decimate.hh"
 #include "algorithms/deduplicate.hh"
@@ -24,7 +25,6 @@
 #include "algorithms/iteration.hh"
 #include "algorithms/normalize.hh"
 #include "algorithms/operations.hh"
-#include "algorithms/optimization.hh"
 #include "algorithms/sampling.hh"
 #include "algorithms/smoothing.hh"
 #include "algorithms/stats.hh"
diff --git a/src/polymesh/algorithms/optimization.cc b/src/polymesh/algorithms/cache-optimization.cc
similarity index 99%
rename from src/polymesh/algorithms/optimization.cc
rename to src/polymesh/algorithms/cache-optimization.cc
index 5ff1291563a473c5949ea096cbd782957a992dc9..b4833d172ba2c95e36018e74ac68c976b5c5da32 100644
--- a/src/polymesh/algorithms/optimization.cc
+++ b/src/polymesh/algorithms/cache-optimization.cc
@@ -1,4 +1,4 @@
-#include "optimization.hh"
+#include "cache-optimization.hh"
 
 #include <polymesh/detail/permutation.hh>
 #include <polymesh/detail/random.hh>
diff --git a/src/polymesh/algorithms/optimization.hh b/src/polymesh/algorithms/cache-optimization.hh
similarity index 100%
rename from src/polymesh/algorithms/optimization.hh
rename to src/polymesh/algorithms/cache-optimization.hh
diff --git a/src/polymesh/algorithms/normalize.hh b/src/polymesh/algorithms/normalize.hh
index cc354c6b83be510eff8ce807cb8328e235188e42..e1340d31c1665dd1253b1f7bad983aac8744dc28 100644
--- a/src/polymesh/algorithms/normalize.hh
+++ b/src/polymesh/algorithms/normalize.hh
@@ -5,7 +5,7 @@
 
 namespace polymesh
 {
-// Applies a translation and a uniform rescaling such that the mesh is centerd at (0,0,0) and withing the [-1 .. 1] cube
+/// Applies a translation and a uniform rescaling such that the mesh is centerd at (0,0,0) and within the [-1 .. 1] cube
 template <class Pos3>
 void normalize(vertex_attribute<Pos3>& pos)
 {