diff --git a/Draw.cc b/Draw.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a8e21e6b59ae337dfa30836e18b2cb8cdc3ae906
--- /dev/null
+++ b/Draw.cc
@@ -0,0 +1,136 @@
+/*
+ * Author: pschmidt
+ */
+#include "Draw.hh"
+
+#include <ACG/Scenegraph/TransformNode.hh>
+#include <ACG/Scenegraph/MaterialNode.hh>
+#include <ACG/Scenegraph/PointNode.hh>
+#include <ACG/Scenegraph/LineNode.hh>
+
+namespace Utils
+{
+
+Draw::Draw()
+    : translation_(0.0),
+      transform_node_(nullptr)
+{
+
+}
+
+Draw::~Draw()
+{
+    clear();
+}
+
+void Draw::set_translation(ACG::Vec3d _t)
+{
+    translation_ = _t;
+    apply_translation();
+}
+
+void Draw::reset_translation()
+{
+    translation_ = ACG::Vec3d(0.0);
+    apply_translation();
+}
+
+void Draw::apply_translation()
+{
+    transform_node()->loadIdentity();
+    transform_node()->translate(translation_);
+}
+
+void Draw::clear()
+{
+    point_nodes_.clear();
+    line_nodes_.clear();
+
+    if (transform_node_)
+        transform_node_->delete_subtree();
+
+    transform_node_ = nullptr;
+}
+
+void Draw::point(ACG::Vec2d _p, Color _color, float _width/* = 10.0f*/)
+{
+    point(ACG::Vec3d(_p[0], 0.0, -_p[1]), _color, _width);
+}
+
+void Draw::point(ACG::Vec3d _p, Color _color, float _width/* = 10.0f*/)
+{
+    PointNode* node = point_node(_width);
+
+    node->add_color(_color);
+    node->add_point(_p);
+    node->show();
+}
+
+void Draw::line(ACG::Vec2d _from, ACG::Vec2d _to, Color _color, float _width/* = 4.0f*/)
+{
+    return line(ACG::Vec3d(_from[0], 0.0, -_from[1]), ACG::Vec3d(_to[0], 0.0, -_to[1]), _color, _width);
+}
+
+void Draw::line(ACG::Vec3d _from, ACG::Vec3d _to, Color _color, float _width/* = 4.0f*/)
+{
+    LineNode* node = line_node(_width);
+
+    node->add_color(_color);
+    node->add_line(_from, _to);
+    node->show();
+}
+
+Draw::TransformNode* Draw::transform_node()
+{
+    if (!transform_node_)
+    {
+        transform_node_ = new TransformNode();
+        PluginFunctions::addGlobalNode(transform_node_);
+        apply_translation();
+    }
+
+    return transform_node_;
+}
+
+ACG::SceneGraph::PointNode* Draw::point_node(float _width)
+{
+    // Already there?
+    for (auto& node : point_nodes_)
+    {
+        auto* material_node = dynamic_cast<ACG::SceneGraph::MaterialNode*>(node->parent());
+        if (material_node->point_size() == _width)
+            return node;
+    }
+
+    // Add new node
+    auto* material_node = new ACG::SceneGraph::MaterialNode(transform_node());
+    material_node->enable_blending();
+
+    auto* point_node = new ACG::SceneGraph::PointNode(material_node);
+    material_node->set_point_size(_width);
+    point_node->enablePicking(false);
+    point_node->drawMode(ACG::SceneGraph::DrawModes::POINTS_COLORED);
+    point_nodes_.push_back(point_node);
+
+    return point_node;
+}
+
+ACG::SceneGraph::LineNode* Draw::line_node(float _width)
+{
+    // Already there?
+    for (auto& node : line_nodes_)
+    {
+        if (node->line_width() == _width)
+            return node;
+    }
+
+    // Add new node
+    LineNode* node = new ACG::SceneGraph::LineNode(ACG::SceneGraph::LineNode::LineSegmentsMode, transform_node());
+    node->set_line_width(_width);
+    node->enablePicking(false);
+    line_nodes_.push_back(node);
+
+    return node;
+}
+
+} // namespace Utils
diff --git a/Draw.hh b/Draw.hh
new file mode 100644
index 0000000000000000000000000000000000000000..0ce38452fbf3ba6402203cdf4e2349a3aabf563b
--- /dev/null
+++ b/Draw.hh
@@ -0,0 +1,57 @@
+/*
+ * Author: pschmidt
+ */
+#pragma once
+
+#include <memory>
+#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
+#include <ObjectTypes/TriangleMesh/TriangleMeshTypes.hh>
+
+#include "RWTHColors.hh"
+
+#include <memory>
+#include <unordered_map>
+
+namespace ACG { namespace SceneGraph { class TransformNode; } }
+namespace ACG { namespace SceneGraph { class PointNode; } }
+namespace ACG { namespace SceneGraph { class LineNode; } }
+
+namespace Utils
+{
+
+class Draw
+{
+public:
+    using TransformNode = ACG::SceneGraph::TransformNode;
+    using PointNode = ACG::SceneGraph::PointNode;
+    using LineNode = ACG::SceneGraph::LineNode;
+
+    using Color = ACG::Vec4f;
+
+public:
+    Draw();
+    ~Draw();
+
+    void set_translation(ACG::Vec3d _t);
+    void reset_translation();
+
+    void clear();
+    void point(ACG::Vec2d _p, Color _color, float _width = 10.0f);
+    void point(ACG::Vec3d _p, Color _color, float _width = 10.0f);
+    void line(ACG::Vec2d _from, ACG::Vec2d _to, Color _color, float _width = 4.0f);
+    void line(ACG::Vec3d _from, ACG::Vec3d _to, Color _color, float _width = 4.0f);
+
+private:
+    void apply_translation();
+    TransformNode* transform_node();
+    PointNode* point_node(float _width);
+    LineNode* line_node(float _width);
+
+private:
+    ACG::Vec3d translation_;
+    TransformNode* transform_node_;
+    std::vector<PointNode*> point_nodes_;
+    std::vector<LineNode*> line_nodes_;
+};
+
+} // namespace Utils
diff --git a/Embedding.cc b/Embedding.cc
new file mode 100644
index 0000000000000000000000000000000000000000..19292e672ecf561ce8c01d897eb9b784c7301e8a
--- /dev/null
+++ b/Embedding.cc
@@ -0,0 +1,4614 @@
+#include "Embedding.hh"
+#include <math.h>
+#include <QDebug>
+#include <random>
+
+/*!
+ * \brief Embedding::Embedding
+ */
+Embedding::Embedding() {
+  qDebug() << "Start of MetaMesh ctor";
+  base_mesh_ = nullptr;
+  meta_mesh_ = nullptr;
+  qDebug() << "Initialized MetaMesh";
+}
+
+/*!
+ * \brief Embedding::CopyInitialization initializes the meta mesh as a copy of the
+ * base mesh
+ * \param base_mesh
+ * \param meta_mesh
+ */
+void Embedding::CopyInitialization(TriMesh &base_mesh, PolyMesh &meta_mesh) {
+  qDebug() << "Copying the base mesh into the metamesh.";
+  boundaries_ = false;
+  initial_triangulation_ = true;
+  base_mesh_ = &base_mesh;
+  meta_mesh_ = &meta_mesh;
+  meta_mesh.clear();
+  InitializeProperties();
+  for (auto bvh : base_mesh_->vertices()) {
+    auto mvh = meta_mesh_->add_vertex(base_mesh_->point(bvh));
+    base_mesh_->property(bv_connection_, bvh) = mvh;
+    meta_mesh_->property(mv_connection_, mvh) = bvh;
+    base_mesh_->property(bsplithandle_, bvh) =
+        OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(voronoiID_, bvh) =
+        OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  }
+  for (auto bfh : base_mesh_->faces()) {
+    auto bheh0 = base_mesh_->halfedge_handle(bfh);
+    auto bheh1 = base_mesh_->next_halfedge_handle(bheh0);
+    auto bheh2 = base_mesh_->next_halfedge_handle(bheh1);
+    auto bvh0 = base_mesh_->from_vertex_handle(bheh0);
+    auto bvh1 = base_mesh_->from_vertex_handle(bheh1);
+    auto bvh2 = base_mesh_->from_vertex_handle(bheh2);
+    auto mvh0 = base_mesh_->property(bv_connection_, bvh0);
+    auto mvh1 = base_mesh_->property(bv_connection_, bvh1);
+    auto mvh2 = base_mesh_->property(bv_connection_, bvh2);
+    auto mheh0 = base_mesh_->property(bhe_connection_,
+                     base_mesh_->opposite_halfedge_handle(bheh0));
+    auto mheh1 = base_mesh_->property(bhe_connection_,
+                     base_mesh_->opposite_halfedge_handle(bheh1));
+    auto mheh2 = base_mesh_->property(bhe_connection_,
+                     base_mesh_->opposite_halfedge_handle(bheh2));
+    if (meta_mesh_->is_valid_handle(mheh0)) {
+      mheh0 = meta_mesh_->opposite_halfedge_handle(mheh0);
+    }
+    if (meta_mesh_->is_valid_handle(mheh1)) {
+      mheh1 = meta_mesh_->opposite_halfedge_handle(mheh1);
+    }
+    if (meta_mesh_->is_valid_handle(mheh2)) {
+      mheh2 = meta_mesh_->opposite_halfedge_handle(mheh2);
+    }
+    auto mfh = AddFace({mvh0, mvh1, mvh2}, {mheh0, mheh1, mheh2});
+    mheh0 = meta_mesh_->halfedge_handle(mfh);
+    mheh1 = meta_mesh_->next_halfedge_handle(mheh0);
+    mheh2 = meta_mesh_->next_halfedge_handle(mheh1);
+    base_mesh_->property(bhe_connection_, bheh0) = mheh0;
+    base_mesh_->property(bhe_connection_, bheh1) = mheh1;
+    base_mesh_->property(bhe_connection_, bheh2) = mheh2;
+    meta_mesh_->property(mhe_connection_, mheh0) = bheh0;
+    meta_mesh_->property(mhe_connection_, mheh1) = bheh1;
+    meta_mesh_->property(mhe_connection_, mheh2) = bheh2;
+    base_mesh_->property(next_heh_, bheh0) =
+        OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, bheh1) =
+        OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, bheh2) =
+        OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(halfedge_weight_, bheh0) = 1.0;
+    base_mesh_->property(halfedge_weight_, bheh1) = 1.0;
+    base_mesh_->property(halfedge_weight_, bheh2) = 1.0;
+  }
+  for (auto bheh : base_mesh_->halfedges()) {
+    if (base_mesh_->is_boundary(bheh)) {
+      auto mheh = meta_mesh_->opposite_halfedge_handle(
+        base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(bheh)));
+      base_mesh_->property(bhe_connection_, bheh) = mheh;
+      meta_mesh_->property(mhe_connection_, mheh) = bheh;
+      meta_mesh_->set_boundary(mheh);
+      meta_mesh_->set_halfedge_handle(meta_mesh_->from_vertex_handle(mheh), mheh);
+    }
+  }
+  for (auto mheh : meta_mesh_->halfedges()) {
+    if (meta_mesh_->is_boundary(mheh)) {
+      auto mhehn = meta_mesh_->halfedge_handle(meta_mesh_->to_vertex_handle(mheh));
+      meta_mesh_->set_next_halfedge_handle(mheh, mhehn);
+    }
+  }
+  ColorizeMetaMesh();
+  initial_triangulation_ = false;
+}
+
+/*!
+ * \brief Embedding::SelectionTriangulation triangulate selected meta vertices
+ * \param base_mesh
+ * \param meta_mesh
+ * \param type
+ */
+void Embedding::SelectionTriangulation(TriMesh& base_mesh, PolyMesh& meta_mesh) {
+  qDebug() << "Entering InitialTriangulation function.";
+  boundaries_ = false;
+  initial_triangulation_ = true;
+  base_mesh_ = &base_mesh;
+  meta_mesh_ = &meta_mesh;
+  meta_mesh.clear();
+  std::vector<OpenMesh::VertexHandle> meta_mesh_points;
+  for (auto vh : base_mesh_->vertices()) {
+    if (base_mesh_->status(vh).selected() == true) {
+      meta_mesh_points.push_back(vh);
+    }
+  }
+  if (meta_mesh_points.empty()) {
+    CopyInitialization(base_mesh, meta_mesh);
+    return;
+  }
+  TriangulationPipeline(meta_mesh_points);
+  initial_triangulation_ = false;
+}
+
+/*!
+ * \brief Embedding::RandomTriangulation
+ * 1 / ratio chance of a vertex being randomly selected as a meta vertex
+ * \param base_mesh
+ * \param meta_mesh
+ * \param input
+ * \param rtype
+ * \param type
+ */
+void Embedding::RandomTriangulation(TriMesh &base_mesh, PolyMesh &meta_mesh, double input,
+                                    RandomType rtype) {
+  initial_triangulation_ = true;
+  boundaries_ = false;
+  base_mesh_ = &base_mesh;
+  meta_mesh_ = &meta_mesh;
+  meta_mesh.clear();
+  std::random_device rd;  //Will be used to obtain a seed for the random number engine
+  std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
+  std::uniform_int_distribution<> dis0(0, static_cast<int>(1.0/input));
+  std::uniform_int_distribution<> dis1(0, static_cast<int>(base_mesh_->n_vertices())-1);
+  std::vector<OpenMesh::VertexHandle> meta_mesh_points;
+
+  // Find boundary vertices
+  OpenMesh::VPropHandleT<bool> traversed_boundary;
+  base_mesh_->add_property(traversed_boundary, "Mark boundaries that have been traversed");
+  for (auto bvh : base_mesh_->vertices()) {
+    base_mesh_->property(traversed_boundary, bvh) = false;
+  }
+  for (auto bvh : base_mesh_->vertices()) {
+    // Go into each boundary once
+    if (base_mesh_->is_boundary(bvh)
+        && base_mesh_->property(traversed_boundary, bvh) == false) {
+      uint step = 0;
+      // Find the right stepsize to evenly distribute boundary vertices
+      if (rtype == RATIO) {
+        step = static_cast<uint>(1/input);
+      } else if (rtype == TOTAL) {
+        step = static_cast<uint>(base_mesh_->n_vertices()/input);
+      }
+      base_mesh_->property(traversed_boundary, bvh) = true;
+      meta_mesh_points.push_back(bvh);
+      uint currstep = 1;
+      auto bheh = base_mesh_->halfedge_handle(bvh);
+      while (!base_mesh_->property(traversed_boundary,
+                                   base_mesh_->to_vertex_handle(bheh))) {
+        base_mesh_->property(traversed_boundary,
+                             base_mesh_->to_vertex_handle(bheh)) = true;
+        if (currstep == step) {
+          meta_mesh_points.push_back(base_mesh_->to_vertex_handle(bheh));
+          currstep = 0;
+        }
+        ++currstep;
+        bheh = base_mesh_->next_halfedge_handle(bheh);
+      }
+    }
+  }
+  base_mesh_->remove_property(traversed_boundary);
+
+  if (rtype == RATIO) {
+    for (auto bvh : base_mesh_->vertices()) {
+      if (dis0(gen) == 1 && !base_mesh_->is_boundary(bvh)) {
+        meta_mesh_points.push_back(bvh);
+      }
+    }
+  } else if (rtype == TOTAL) {
+    while (meta_mesh_points.size()<input*10000) {
+      meta_mesh_points.push_back(base_mesh_->vertex_handle(static_cast<uint>(dis1(gen))));
+    }
+  }
+  while (!TriangulationPipeline(meta_mesh_points)) {
+    meta_mesh_points.clear();
+    qDebug() << "Automatically remeshing";
+    meta_mesh_->clear();
+    if (rtype == RATIO) {
+      for (auto vh : base_mesh_->vertices()) {
+        if (dis0(gen) == 1)
+          meta_mesh_points.push_back(vh);
+      }
+    } else if (rtype == TOTAL) {
+      for (unsigned long i=0; i<input*10000; ++i) {
+        meta_mesh_points.push_back(base_mesh_->vertex_handle(static_cast<uint>(dis1(gen))));
+      }
+    }
+  }
+  initial_triangulation_ = false;
+}
+
+/*!
+ * \brief Embedding::TriangulationPipeline
+ * \param meta_mesh_points
+ * \param type
+ * \return
+ */
+bool Embedding::TriangulationPipeline(
+    std::vector<OpenMesh::VertexHandle> meta_mesh_points) {
+  debug_hard_stop_ = false;
+  InitializeProperties();
+  CreateMetaMeshVertices(meta_mesh_points);
+  if (!TriangulateMetaMesh())
+    return false;
+  CleanUpBaseMesh();
+  TestHalfedgeConsistency();
+  ColorizeMetaMesh();
+  return true;
+}
+
+/*!
+ * \brief Embedding::InitializeProperties
+ */
+void Embedding::InitializeProperties() {
+  base_mesh_->add_property(voronoiID_, "Voronoi area");
+  base_mesh_->add_property(bsplithandle_, "Vertex to collapse into, undefined if not introduced"
+                                          "by a splitting operation");
+  base_mesh_->add_property(bv_connection_, "BVV_Connection");
+  meta_mesh_->add_property(mv_connection_, "MVV_Connection");
+  base_mesh_->add_property(bhe_connection_, "Pointer from base he to meta heh");
+  meta_mesh_->add_property(mhe_connection_, "Pointer from meta he to first base heh");
+  base_mesh_->add_property(bhe_border_, "Pointer from border base he to meta heh");
+  meta_mesh_->add_property(mhe_border_, "Pointer from meta he to first border base heh");
+  base_mesh_->add_property(next_heh_, "Pointer from base he to next heh on meta edge");
+  base_mesh_->add_property(halfedge_weight_, "Pointer from base he to meta heh");
+}
+
+/*!
+ * \brief Embedding::CreateMetaMeshVertices
+ * \param meta_mesh_points
+ */
+void Embedding::CreateMetaMeshVertices(std::vector<OpenMesh::VertexHandle> meta_mesh_points) {
+  qDebug() << "Start appointing BaseMesh properties:";
+  qDebug() << "is bv_connection_ valid? " << bv_connection_.is_valid();
+  qDebug() << "is bsplithandle_ valid? " << bsplithandle_.is_valid();
+  for (auto bvh : base_mesh_->vertices()) {
+    base_mesh_->property(bv_connection_, bvh) = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+    base_mesh_->property(bsplithandle_, bvh) = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+  for (auto bheh : base_mesh_->halfedges()) {
+    base_mesh_->property(next_heh_, bheh) = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(bhe_connection_, bheh)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(halfedge_weight_, bheh) = 1.0;
+    if (base_mesh_->is_boundary(bheh)) {
+      boundaries_ = true;
+    }
+  }
+
+  qDebug() << "Start appointing MetaMesh properties:";
+  for (auto mpoint : meta_mesh_points) {
+    auto mvhnew = meta_mesh_->add_vertex(base_mesh_->point(mpoint));
+    meta_mesh_->property(mv_connection_, mvhnew) = mpoint;
+    base_mesh_->property(bv_connection_, mpoint) = mvhnew;
+  }
+  qDebug() << "Finish appointing MetaMesh properties:";
+}
+
+/*!
+ * \brief Embedding::TriangulateMetaMesh
+ * \param type
+ * \return
+ */
+bool Embedding::TriangulateMetaMesh() {
+  OpenMesh::VPropHandleT<double> voronoidistance;
+  OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> to_heh;
+  OpenMesh::HPropHandleT<int> multiplicity_heh;
+  base_mesh_->add_property(voronoidistance, "Voronoi distance from center");
+  base_mesh_->add_property(to_heh, "Incoming edge from the shortest path");
+  base_mesh_->add_property(multiplicity_heh, "Mark duplicate paths for elimination");
+  qDebug() << "Entering Delaunay:";
+  if (!Delaunay(voronoidistance, to_heh, multiplicity_heh)) {
+      return false;
+  }
+
+  A_StarTriangulation();
+
+  base_mesh_->remove_property(voronoidistance);
+  base_mesh_->remove_property(to_heh);
+  base_mesh_->remove_property(multiplicity_heh);
+  return true;
+}
+
+/*!
+ * \brief Embedding::PreProcessEdges global preprocessing
+ */
+void Embedding::PreProcessEdges() {
+  for (auto beh : base_mesh_->edges()) {
+    auto bheh = base_mesh_->halfedge_handle(beh, 0);
+    ConditionalSplit(bheh);
+  }
+  base_mesh_->update_normals();
+}
+
+/*!
+ * \brief Embedding::ProcessEdge local preprocessing
+ * \param meh
+ */
+void Embedding::ProcessEdge(OpenMesh::EdgeHandle meh) {
+  ProcessHalfedge(meta_mesh_->halfedge_handle(meh, 0));
+  ProcessHalfedge(meta_mesh_->halfedge_handle(meh, 1));
+}
+
+/*!
+ * \brief Embedding::ProcessVertex local preprocessing
+ * \param bvh
+ */
+void Embedding::ProcessVertex(OpenMesh::VertexHandle bvh) {
+  std::list<OpenMesh::HalfedgeHandle> splits;
+  for (auto bvoheh : base_mesh_->voh_range(bvh)) {
+    if (IsSectorBorder(base_mesh_->to_vertex_handle(bvoheh))) {
+      splits.push_back(bvoheh);
+    }
+  }
+  while (!splits.empty()) {
+    ConditionalSplit(splits.front());
+    splits.pop_front();
+  }
+}
+
+/*!
+ * \brief Embedding::CleanupVertex perform legal base collapses around vertex bvh
+ * \param bvh
+ */
+void Embedding::CleanupVertex(OpenMesh::VertexHandle bvh) {
+  std::list<OpenMesh::VertexHandle> collapses;
+  for (auto bvhit : base_mesh_->vv_range(bvh)) {
+    if (base_mesh_->is_valid_handle(bvhit)
+        && !base_mesh_->status(bvhit).deleted()
+        && base_mesh_->is_valid_handle(base_mesh_->property(bsplithandle_, bvhit))
+        && !base_mesh_->status(base_mesh_->property(bsplithandle_, bvhit)).deleted()) {
+      collapses.push_back(bvhit);
+    }
+  }
+  while (!collapses.empty()) {
+    ConditionalCollapse(collapses.front());
+    collapses.pop_front();
+  }
+}
+
+/*!
+ * \brief Embedding::CleanupFace cleans up the faces around mheh
+ * \param mheh
+ */
+void Embedding::CleanupFace(OpenMesh::HalfedgeHandle mheh) {
+  auto mhehiter = mheh;
+  do {
+    if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehiter))) {
+      CleanupHalfedge(mhehiter);
+    }
+    mhehiter = meta_mesh_->next_halfedge_handle(mhehiter);
+  } while (mhehiter != mheh);
+}
+
+/*!
+ * \brief Embedding::CleanupHalfedge cleans up mheh
+ * \param mheh
+ */
+void Embedding::CleanupHalfedge(OpenMesh::HalfedgeHandle mheh) {
+  auto linehalfedges = GetBaseHalfedges(mheh);
+  auto mhehnext = meta_mesh_->next_halfedge_handle(mheh);
+  while (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehnext))) {
+    mhehnext = meta_mesh_->next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(mhehnext));
+  }
+  linehalfedges.push_back(meta_mesh_->property(mhe_connection_, mhehnext));
+  for (auto blheh : linehalfedges) {
+    auto bvhfrom = base_mesh_->from_vertex_handle(blheh);
+    if (base_mesh_->is_valid_handle(bvhfrom)
+        && base_mesh_->is_valid_handle(base_mesh_->property(bsplithandle_, bvhfrom))
+        && !base_mesh_->status(bvhfrom).deleted()
+        && !base_mesh_->status(base_mesh_->property(bsplithandle_, bvhfrom)).deleted()) {
+      ConditionalCollapse(bvhfrom);
+    }
+    for (auto bheh : LeftHalfCircle(blheh)) {
+      auto bvhto = base_mesh_->to_vertex_handle(bheh);
+      if (base_mesh_->is_valid_handle(bvhto)
+          && base_mesh_->is_valid_handle(base_mesh_->property(bsplithandle_, bvhto))
+          && !base_mesh_->status(bvhto).deleted()
+          && !base_mesh_->status(base_mesh_->property(bsplithandle_, bvhto)).deleted()) {
+        ConditionalCollapse(bvhto);
+      }
+    }
+  }
+  BaseGarbageCollection();
+}
+
+/*!
+ * \brief Embedding::ProcessNeighbors pre process the neighboring edges around mheh
+ * this is called before tracing
+ * \param meh
+ */
+void Embedding::ProcessNeighbors(OpenMesh::EdgeHandle meh) {
+  auto mheh0 = meta_mesh_->halfedge_handle(meh, 0);
+  auto mheh1 = meta_mesh_->halfedge_handle(meh, 1);
+
+  // Find the ACTUAL neighbors that are already traced, not the neighbors in the meta mesh
+  auto neighbor0 = meta_mesh_->opposite_halfedge_handle(
+        meta_mesh_->prev_halfedge_handle(mheh0));
+  while (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, neighbor0))
+         && neighbor0 != mheh0) {
+    neighbor0 = meta_mesh_->opposite_halfedge_handle(
+          meta_mesh_->prev_halfedge_handle(neighbor0));
+  }
+  auto neighbor1 = meta_mesh_->next_halfedge_handle(
+        meta_mesh_->opposite_halfedge_handle(mheh0));
+  while (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, neighbor1))
+         && neighbor1 != mheh0) {
+    neighbor1 = meta_mesh_->next_halfedge_handle(
+          meta_mesh_->opposite_halfedge_handle(neighbor1));
+  }
+  auto neighbor2 = meta_mesh_->opposite_halfedge_handle(
+        meta_mesh_->prev_halfedge_handle(mheh1));
+  while (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, neighbor2))
+         && neighbor2 != mheh1) {
+    neighbor2 = meta_mesh_->opposite_halfedge_handle(
+          meta_mesh_->prev_halfedge_handle(neighbor2));
+  }
+  auto neighbor3 = meta_mesh_->next_halfedge_handle(
+        meta_mesh_->opposite_halfedge_handle(mheh1));
+  while (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, neighbor3))
+         && neighbor3 != mheh1) {
+    neighbor3 = meta_mesh_->next_halfedge_handle(
+          meta_mesh_->opposite_halfedge_handle(neighbor3));
+  }
+
+  // neighbor0 and neighbor 2 were found searching towards the left but
+  // ProcessHalfedge is left-facing, so process the opposite halfedges.
+  if (neighbor0 != mheh0) {
+    ProcessHalfedge(meta_mesh_->opposite_halfedge_handle(neighbor0));
+    ProcessHalfedge(neighbor1);
+  }
+  if (neighbor2 != mheh1) {
+    ProcessHalfedge(meta_mesh_->opposite_halfedge_handle(neighbor2));
+    ProcessHalfedge(neighbor3);
+  }
+}
+
+/*!
+ * \brief Embedding::ProcessFace
+ * Iterate over the currently traced patch that mheh is part of and call pre-processing
+ * on all traversed meta halfedges
+ * \param mheh
+ */
+void Embedding::ProcessFace(OpenMesh::HalfedgeHandle mheh) {
+  // Edge case: Trying to process the face of an mheh which has an adjacent valence 1 vertex
+  // If mheh points towards that vertex there is no problem in the iteration
+  // If mheh points away from that vertex it immediately iterates into itself and terminates
+  // -> no processing
+  // Fix this by changing mheh to its opposite halfedge if it points away from a valence 1 vertex
+  // This cannot go wrong since only one of the two vertices of mheh can be valence 1 in any
+  // scenario where pre-processing is required (both valence 1 can only happen in
+  // initial triangulation)
+  // The previous solution works for edges next to valence 1 being traced but breaks face splits
+  // so instead just find an adjacent traced edge and iterate from there; this works in both
+  // cases.
+  auto mhehf = meta_mesh_->next_halfedge_handle(mheh);
+  auto mhehb = meta_mesh_->next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(mheh));
+  if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehf))) {
+    mheh = mhehf;
+  } else if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehb))) {
+    mheh = mhehb;
+  } else {
+  // In this case BOTH vertices have to be empty, so the state has to be initial triangulation
+    assert(initial_triangulation_);
+    return;
+  }
+  auto mhehtemp = mheh;
+  do {
+    if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehtemp))
+        && base_mesh_->property(bhe_connection_, meta_mesh_->property(mhe_connection_, mhehtemp))
+        == mhehtemp) {
+      ProcessHalfedge(mhehtemp);
+      mhehtemp = meta_mesh_->next_halfedge_handle(mhehtemp);
+    } else {
+      mhehtemp = meta_mesh_->next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(mhehtemp));
+    }
+  } while (mhehtemp != mheh);
+}
+
+/*!
+ * \brief Embedding::ProcessHalfedge pre-process base halfedges around meta halfedge mheh
+ * \param mheh
+ */
+void Embedding::ProcessHalfedge(OpenMesh::HalfedgeHandle mheh) {
+  auto linehalfedges = GetBaseHalfedges(mheh);
+  auto mhehnext = meta_mesh_->next_halfedge_handle(mheh);
+  while (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehnext))) {
+    mhehnext = meta_mesh_->next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(mhehnext));
+  }
+  linehalfedges.push_back(meta_mesh_->property(mhe_connection_, mhehnext));
+  for (auto blheh : linehalfedges) {
+    for (auto bheh : LeftHalfCircle(blheh)) {
+      ConditionalSplit(bheh);
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::ConditionalSplit split bheh if permissible
+ * \param bheh
+ * \return
+ */
+bool Embedding::ConditionalSplit(OpenMesh::HalfedgeHandle bheh) {
+  // Don't split boundary halfedges for now since it is leading to crashes.
+  // This could be enabled (I was in the middle of adding boundary split functionality)
+  // But there is not enough time
+  // TODO: remove this check and fix boundary splits in CollapseBaseHe and SplitBaseHe
+  // After testing quite a bit without splitting boundary halfedges this seems to be working quite
+  // well. Splitting boundary halfedges may not be necessary at all.
+  if (base_mesh_->is_boundary(bheh)
+      || base_mesh_->is_boundary(base_mesh_->opposite_halfedge_handle(bheh))) {
+    return false;
+  }
+
+  if (!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))) {
+    bool split1 = false;
+    bool split2 = false;
+    bool split3 = false;
+    for (auto bheh : base_mesh_->voh_range(base_mesh_->from_vertex_handle(bheh))) {
+      if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))) {
+        split1 = true;
+        auto mvh0 = base_mesh_->property(bv_connection_,
+                                         base_mesh_->to_vertex_handle(bheh));
+        if (meta_mesh_->is_valid_handle(mvh0)
+            && meta_mesh_->to_vertex_handle(base_mesh_->property(
+               bhe_connection_, bheh)) != mvh0
+            && meta_mesh_->from_vertex_handle(base_mesh_->property(
+               bhe_connection_, bheh)) != mvh0) {
+          split2 = true;
+        }
+      }
+    }
+    for (auto bheh : base_mesh_->voh_range(base_mesh_->to_vertex_handle(bheh))) {
+      if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))) {
+        split2 = true;
+        auto mvh0 = base_mesh_->property(bv_connection_,
+                                         base_mesh_->from_vertex_handle(bheh));
+        if (meta_mesh_->is_valid_handle(mvh0)
+            && meta_mesh_->to_vertex_handle(base_mesh_->property(
+               bhe_connection_, bheh)) != mvh0
+            && meta_mesh_->from_vertex_handle(base_mesh_->property(
+               bhe_connection_, bheh)) != mvh0) {
+          split1 = true;
+        }
+      }
+    }
+    auto bvid0 = base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bheh));
+    auto bvid1 = base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bheh));
+    if (bvid0 != bvid1 && initial_triangulation_) {
+      auto meh = GetMetaEdge(base_mesh_->from_vertex_handle(bheh));
+      if (!meta_mesh_->is_valid_handle(meh)) {
+        split3 = true;
+      } else {
+        auto mvid0 = meta_mesh_->from_vertex_handle(meta_mesh_->halfedge_handle(meh, 0));
+        auto mvid1 = meta_mesh_->to_vertex_handle(meta_mesh_->halfedge_handle(meh, 0));
+        if (!((mvid0 == bvid0 && mvid1 == bvid1)  || (mvid0 == bvid1 && mvid1 == bvid0))) {
+          split3 = true;
+        }
+      }
+    }
+
+    if ((split1 && split2) || (split3 && (split1 || split2)))  {
+      SplitBaseHe(bheh);
+      return true;
+    }
+  } else if ((meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+              base_mesh_->from_vertex_handle(bheh))))
+             && (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+                 base_mesh_->to_vertex_handle(bheh))))) {
+    SplitBaseHe(bheh);
+    return true;
+  }
+  return false;
+}
+
+/*!
+ * \brief Embedding::LeftHalfCircle
+ * \param bheh
+ * \return the halfedges surrounding bheh in a left halfcircle and
+ * stopping when reaching a meta halfedge (used for preprocessing)
+ */
+std::vector<OpenMesh::HalfedgeHandle> Embedding::LeftHalfCircle(OpenMesh::HalfedgeHandle bheh) {
+  std::vector<OpenMesh::HalfedgeHandle> retval;
+  retval.push_back(bheh);
+  auto curr = base_mesh_->opposite_halfedge_handle(base_mesh_->prev_halfedge_handle(bheh));
+  retval.push_back(curr);
+  while (curr != bheh && !meta_mesh_->is_valid_handle(
+           base_mesh_->property(bhe_connection_, curr))) {
+    curr = base_mesh_->opposite_halfedge_handle(base_mesh_->prev_halfedge_handle(curr));
+    retval.push_back(curr);
+  }
+  return retval;
+}
+
+/*!
+ * \brief Embedding::GetBaseHalfedges
+ * \param mheh
+ * \return the base halfedges of meta halfedge mheh
+ */
+std::vector<OpenMesh::HalfedgeHandle> Embedding::GetBaseHalfedges(
+    OpenMesh::HalfedgeHandle mheh) {
+  std::vector<OpenMesh::HalfedgeHandle> retval;
+  auto bheh = meta_mesh_->property(mhe_connection_, mheh);
+  assert(base_mesh_->is_valid_handle(bheh));
+  retval.push_back(bheh);
+  auto curr = base_mesh_->property(next_heh_, bheh);
+  while (base_mesh_->is_valid_handle(curr)) {
+    retval.push_back(curr);
+    curr = base_mesh_->property(next_heh_, curr);
+  }
+  return retval;
+}
+
+/*!
+ * \brief Embedding::CleanUpBaseMesh collapses new bhehs where allowed and collects garbage it
+ * \param garbagecollection collect garbage if set to true
+ */
+void Embedding::CleanUpBaseMesh(bool garbagecollection) {
+  std::queue<OpenMesh::VertexHandle> hehqueue;
+  for (auto bvh : base_mesh_->vertices()) {
+    if (base_mesh_->is_valid_handle(bvh)
+        && !base_mesh_->status(bvh).deleted()
+        && base_mesh_->is_valid_handle(base_mesh_->property(bsplithandle_, bvh))
+        && !base_mesh_->status(base_mesh_->property(bsplithandle_, bvh)).deleted()) {
+      auto bvhnew = ConditionalCollapse(bvh);
+      if (base_mesh_->is_valid_handle(bvhnew)) {
+        hehqueue.push(bvhnew);
+      }
+    }
+    if (debug_hard_stop_) {
+      break;
+    }
+  }
+  // More passes to clear up topologically blocked halfedges whose merges depend on order
+  // this is tricky because there may be a few that _can't_ be merged no matter what
+  unsigned long ctr = hehqueue.size()*2;
+  while (!hehqueue.empty() && ctr > 0) {
+    auto bvh = hehqueue.front();
+    hehqueue.pop();
+    auto bvhnew = ConditionalCollapse(bvh);
+    if (base_mesh_->is_valid_handle(bvhnew)) {
+      hehqueue.push(bvhnew);
+    }
+    --ctr;
+  }
+
+  if (garbagecollection) {
+    BaseGarbageCollection();
+  }
+}
+
+/*!
+ * \brief Embedding::ConditionalCollapse collapse bvh into its bsplithandle_ halfedge
+ * if permissible
+ * \param bvh
+ * \return
+ */
+OpenMesh::VertexHandle  Embedding::ConditionalCollapse(OpenMesh::VertexHandle bvh) {
+  auto bheh = base_mesh_->property(bsplithandle_, bvh);
+  auto bheho = base_mesh_->opposite_halfedge_handle(bheh);
+  if (base_mesh_->status(bheh).deleted() || base_mesh_->status(bvh).deleted()) {
+    return OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  }
+  auto bvh0 = base_mesh_->from_vertex_handle(bheh);
+  auto bvh1 = base_mesh_->to_vertex_handle(bheh);
+
+  // Sanity check
+  assert(base_mesh_->from_vertex_handle(bheh) == bvh);
+
+  bool merge = true;
+
+  auto meh0 = GetMetaEdge(bvh0);
+  auto meh1 = GetMetaEdge(bvh1);
+  // Collapsing into a deleted edge
+  if (base_mesh_->status(bheh).deleted()) {
+    merge = false;
+    qDebug() << "Vertex " << bvh0.idx() << " points towards deleted halfedge "
+             << bheh.idx() << " so it cannot be merged. Presumably something is wrong.";
+  }
+
+  // Collapsing from one meta halfedge into another
+  if (meta_mesh_->is_valid_handle(meh0) && meta_mesh_->is_valid_handle(meh1)
+      && !(meh0 == meh1)) {
+    merge = false;
+  }
+
+  // Collapsing from a meta halfedge into itself but not along the edge
+  if (meta_mesh_->is_valid_handle(meh0) && meh0 == meh1 && meta_mesh_->edge_handle(
+        base_mesh_->property(bhe_connection_, bheh)) != meh0) {
+    merge = false;
+  }
+
+  // Collapsing from a meta vertex into a meta halfedge that doesn't end
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh0))) {
+    for (auto bih : base_mesh_->vih_range(bvh1)) {
+      if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bih))
+          && !(base_mesh_->property(next_heh_, bih) == bheho
+               || bih == bheh)) {
+        merge = false;
+      }
+    }
+  }
+
+  // Collapsing a meta vertex
+  if (base_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh0))) {
+    merge = false;
+  }
+
+  // Collapsing into a meta vertex but not along a meta edge when the collapsed vertex lies on a meta edge
+  auto mhehx = meta_mesh_->edge_handle(base_mesh_->property(bhe_connection_, bheh));
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh1))
+      && meta_mesh_->is_valid_handle(meh0)
+      && mhehx != meh0) {
+    merge = false;
+  }
+
+  // Collapsing of inhabited edges onto boundary edges to avoid blocking
+  if (meta_mesh_->is_valid_handle(meh0) && base_mesh_->is_boundary(bvh1)) {
+    merge = false;
+  }
+
+  // Collapsing of a 2-loop can lead to a scenario where two neighboring base vertices are meta vertices
+  // and also doubly connected. eg: A---B
+  //                                 \ /
+  //                                  C
+  // with A and B being meta vertices and C not a meta vertex. The rules so far would allow collapsing
+  // the CB halfedge, but this destroys the loop and should not be allowed before first removing the loop
+  // in the meta mesh. Avoid this.
+  // Conditions: 1. Collapsing into a meta vertex
+  // 2: Collapsing from a meta edge
+  // 3: The meta edge is also in one of the triangles of the collapsed edge eg. AC
+  // 4: The other edge of that triangle belongs to a meta edge as well eg. AB
+  // This specifically does not have to be a DIFFERENT meta edge but just ANY meta edge since otherwise a
+  // self-edge of B going BA->AC->CB would be illegally collapsed.
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh1))               // C1
+      && meta_mesh_->is_valid_handle(meh0)                                                  // C2
+      && ((meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_,
+                                       base_mesh_->prev_halfedge_handle(bheh)))             // C3+4 left
+          && meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_,
+                                         base_mesh_->next_halfedge_handle(bheh))))
+        || (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_,               // C3+4 right
+                                        base_mesh_->next_halfedge_handle(bheho)))
+            && meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_,
+                                           base_mesh_->prev_halfedge_handle(bheho)))))) {
+    merge = false;
+  }
+
+
+  // Other illegal collapses provided by OpenMesh; retry later in this case since these may be blocked by
+  // collapsing order and collapseable later.
+  if (!base_mesh_->is_collapse_ok(bheh)) {
+    return bvh;
+  }
+
+  // This one should not be necessary, delete later and fix.
+  /*
+  if (base_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))) {
+    merge = false;
+  }
+  */
+  if (merge) {
+    CollapseBaseHe(bheh);
+  }
+  return OpenMesh::PolyConnectivity::InvalidVertexHandle;
+}
+
+/*!
+ * \brief Embedding::SplitBaseHe
+ * Retain properties correctly while splitting halfedges and return the first half of the split
+ * edge, also marks new vertices introduced by the split with a handle to the vertex to collapse
+ * them into to undo the split.
+ * \param bheh
+ * \return the halfedge starting at the from_vertex of bheh and pointing towards the new vertex
+ * resulting from the split.
+ */
+OpenMesh::HalfedgeHandle  Embedding::SplitBaseHe(OpenMesh::HalfedgeHandle bheh) {
+  auto opp = base_mesh_->opposite_halfedge_handle(bheh);
+  auto vertex_colors_pph = base_mesh_->vertex_colors_pph();
+  auto vertex_color = base_mesh_->property(vertex_colors_pph,
+                                           base_mesh_->from_vertex_handle(bheh));
+
+  auto weight = base_mesh_->property(halfedge_weight_, bheh);
+  bool lbound = base_mesh_->is_boundary(bheh);
+  bool rbound = base_mesh_->is_boundary(base_mesh_->opposite_halfedge_handle(bheh));
+
+  OpenMesh::HalfedgeHandle border0, border1, mborder0, mborder1;
+  if (initial_triangulation_) {
+    border0 = base_mesh_->property(bhe_border_, bheh);
+    border1 = base_mesh_->property(bhe_border_, opp);
+    mborder0 = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    if (meta_mesh_->is_valid_handle(border0)) {
+      if (meta_mesh_->property(mhe_border_, border0) == bheh) {
+        mborder0 = bheh;
+      }
+    }
+    mborder1 = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    if (meta_mesh_->is_valid_handle(border1)) {
+      if (meta_mesh_->property(mhe_border_, border1) == opp) {
+        mborder1 = opp;
+      }
+    }
+  }
+
+  OpenMesh::HalfedgeHandle bhe_connection0 = base_mesh_->property(bhe_connection_, bheh);
+  OpenMesh::HalfedgeHandle bhe_connection1 = base_mesh_->property(bhe_connection_, opp);
+  OpenMesh::HalfedgeHandle next_heh0 = base_mesh_->property(next_heh_, bheh);
+  OpenMesh::HalfedgeHandle next_heh1 = base_mesh_->property(next_heh_, opp);
+
+  OpenMesh::HalfedgeHandle mhe_connection0 = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  if (meta_mesh_->is_valid_handle(bhe_connection0)){
+    if (meta_mesh_->property(mhe_connection_, bhe_connection0) == bheh) {
+      mhe_connection0 = bheh;
+    }
+  }
+  OpenMesh::HalfedgeHandle mhe_connection1 = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  if (meta_mesh_->is_valid_handle(bhe_connection1)){
+    if (meta_mesh_->property(mhe_connection_, bhe_connection1) == opp) {
+      mhe_connection1 = opp;
+    }
+  }
+  auto prev = base_mesh_->prev_halfedge_handle(bheh);
+
+  bool backsplitadjust = (base_mesh_->property(bsplithandle_,
+                          base_mesh_->from_vertex_handle(bheh))
+                          == bheh);
+  bool frontsplitadjust = (base_mesh_->property(bsplithandle_,
+                          base_mesh_->to_vertex_handle(bheh))
+                          == base_mesh_->opposite_halfedge_handle(bheh));
+
+
+  TriMesh::Point splitpoint = (base_mesh_->point(base_mesh_->from_vertex_handle(bheh))
+      + base_mesh_->point(base_mesh_->to_vertex_handle(bheh)) )/2.0;
+  auto bvh = base_mesh_->split(base_mesh_->edge_handle(bheh), splitpoint);
+
+  auto back = base_mesh_->next_halfedge_handle(prev);
+  auto left = base_mesh_->next_halfedge_handle(back);
+  auto front = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(left));
+  auto right = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(front));
+  assert (bvh == base_mesh_->to_vertex_handle(back));
+
+  if (lbound) {
+    front = base_mesh_->next_halfedge_handle(back);
+    right = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(front));
+    base_mesh_->set_boundary(back);
+    base_mesh_->set_boundary(front);
+    base_mesh_->set_halfedge_handle(bvh, front);
+  }
+  if (rbound) {
+    base_mesh_->set_boundary(base_mesh_->opposite_halfedge_handle(back));
+    base_mesh_->set_boundary(base_mesh_->opposite_halfedge_handle(front));
+    base_mesh_->set_halfedge_handle(bvh, base_mesh_->opposite_halfedge_handle(back));
+  }
+
+
+  base_mesh_->property(bhe_connection_, back) = bhe_connection0;
+  base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(back))
+      = bhe_connection1;
+  base_mesh_->property(bhe_connection_, front) = bhe_connection0;
+  base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(front))
+      = bhe_connection1;
+  if (!lbound) {
+    base_mesh_->property(bhe_connection_, left)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(left))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+  if (!rbound) {
+    base_mesh_->property(bhe_connection_, right)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(right))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+  if (!lbound) {
+    base_mesh_->property(next_heh_, left)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(left))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+  if (!rbound) {
+    base_mesh_->property(next_heh_, right)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(right))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+  base_mesh_->property(halfedge_weight_, back)
+      = weight;
+  base_mesh_->property(halfedge_weight_, base_mesh_->opposite_halfedge_handle(back))
+      = weight;
+  base_mesh_->property(halfedge_weight_, front)
+      = weight;
+  base_mesh_->property(halfedge_weight_, base_mesh_->opposite_halfedge_handle(front))
+      = weight;
+  if (!lbound) {
+    base_mesh_->property(halfedge_weight_, left)
+        = 2.0*weight;
+    base_mesh_->property(halfedge_weight_, base_mesh_->opposite_halfedge_handle(left))
+        = 2.0*weight;
+  }
+  if (!rbound) {
+    base_mesh_->property(halfedge_weight_, right)
+        = 2.0*weight;
+    base_mesh_->property(halfedge_weight_, base_mesh_->opposite_halfedge_handle(right))
+        = 2.0*weight;
+  }
+  base_mesh_->property(bv_connection_, bvh)
+      = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  assert (!meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh)));
+  base_mesh_->property(vertex_colors_pph, bvh) = vertex_color;
+
+  if (backsplitadjust) {
+    base_mesh_->property(bsplithandle_, base_mesh_->from_vertex_handle(back)) = back;
+    base_mesh_->property(bsplithandle_, bvh) = front;
+  } else if (frontsplitadjust) {
+    base_mesh_->property(bsplithandle_, base_mesh_->to_vertex_handle(front))
+        = base_mesh_->opposite_halfedge_handle(front);
+    base_mesh_->property(bsplithandle_, bvh) = base_mesh_->opposite_halfedge_handle(back);
+  } else {
+    base_mesh_->property(bsplithandle_, bvh) = base_mesh_->opposite_halfedge_handle(back);
+  }
+
+  if (base_mesh_->is_valid_handle(mhe_connection0)) {
+    meta_mesh_->property(mhe_connection_, bhe_connection0) = back;
+  }
+  if (base_mesh_->is_valid_handle(mhe_connection1)) {
+    meta_mesh_->property(mhe_connection_, bhe_connection1)
+        = base_mesh_->opposite_halfedge_handle(front);
+  }
+  if (base_mesh_->is_valid_handle(next_heh0) ||
+      base_mesh_->is_valid_handle(next_heh1)) {
+    base_mesh_->property(next_heh_, front) = next_heh0;
+    base_mesh_->property(next_heh_, back) = front;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(back)) = next_heh1;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(front))
+        = base_mesh_->opposite_halfedge_handle(back);
+    if (base_mesh_->is_valid_handle(next_heh0)) {
+      auto next = base_mesh_->opposite_halfedge_handle(next_heh0);
+      base_mesh_->property(next_heh_, next) = base_mesh_->opposite_halfedge_handle(front);
+    }
+    if (base_mesh_->is_valid_handle(next_heh1)) {
+      auto prev = base_mesh_->opposite_halfedge_handle(next_heh1);
+      base_mesh_->property(next_heh_, prev) = back;
+    }
+  } else if (meta_mesh_->is_valid_handle(bhe_connection0)) {
+    base_mesh_->property(next_heh_, back) = front;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(front))
+        = base_mesh_->opposite_halfedge_handle(back);
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(back))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, front)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  } else {
+    base_mesh_->property(next_heh_, back)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(back))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, front)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(front))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+
+  if (!lbound) {
+    auto v_left = base_mesh_->opposite_halfedge_handle(
+          base_mesh_->next_halfedge_handle(left));
+    base_mesh_->property(bhe_border_, left) = base_mesh_->property(bhe_border_, v_left);
+    base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(left)) =
+        base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(v_left));
+  }
+
+  if (!rbound) {
+    auto v_right = base_mesh_->prev_halfedge_handle(
+          base_mesh_->opposite_halfedge_handle(right));
+    base_mesh_->property(bhe_border_, right) = base_mesh_->property(bhe_border_, v_right);
+    base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(right)) =
+        base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(v_right));
+  }
+
+  base_mesh_->property(bhe_border_, back) =
+      OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(back)) =
+      OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+
+  if (!meta_mesh_->is_valid_handle(bhe_connection0)) {
+    assert(!meta_mesh_->is_valid_handle(bhe_connection1));
+    assert(!IsSectorBorder(bvh));
+    assert(ValidA_StarEdge(back,
+                           OpenMesh::PolyConnectivity::InvalidHalfedgeHandle));
+    assert(ValidA_StarEdge(base_mesh_->opposite_halfedge_handle(back),
+                           OpenMesh::PolyConnectivity::InvalidHalfedgeHandle));
+  }
+
+  if (initial_triangulation_) {
+    base_mesh_->property(bhe_border_, front) = border0;
+    base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(front)) = border1;
+    if (base_mesh_->is_valid_handle(mborder0)) {
+      meta_mesh_->property(mhe_border_, border0) = front;
+    }
+    if (base_mesh_->is_valid_handle(mborder1)) {
+      meta_mesh_->property(mhe_border_, border1) = base_mesh_->opposite_halfedge_handle(front);
+    }
+
+    base_mesh_->property(voronoiID_, bvh) = base_mesh_->property(voronoiID_,
+               base_mesh_->from_vertex_handle(back));
+    if (!meta_mesh_->is_valid_handle(bhe_connection0)) {
+      assert(ValidA_StarEdge(front, border0));
+      assert(ValidA_StarEdge(base_mesh_->opposite_halfedge_handle(front), border1));
+    }
+  }
+  return back;
+}
+
+void Embedding::CollapseBaseHe(OpenMesh::HalfedgeHandle bheh) {
+  // Ensure bheh is valid
+  assert(base_mesh_->is_valid_handle(bheh));
+
+  auto bvh0 = base_mesh_->from_vertex_handle(bheh);
+  auto bheho = base_mesh_->opposite_halfedge_handle(bheh);
+
+  bool lbound = base_mesh_->is_boundary(bheh);
+  bool rbound = base_mesh_->is_boundary(bheho);
+
+  // Ensure only vertices not from the original mesh are collapsed.
+  assert (base_mesh_->property(bsplithandle_, bvh0) == bheh);
+
+  // Adjust property pointers towards bheh if it lies on a meta edge
+  AdjustPointersForBheCollapse(bheh);
+
+  // Transfer properties and pointers over from redundant edges
+  assert(base_mesh_->is_valid_handle(base_mesh_->prev_halfedge_handle(bheh)));
+  assert(base_mesh_->is_valid_handle(base_mesh_->opposite_halfedge_handle(
+                                     base_mesh_->next_halfedge_handle(bheh))));
+  if (!lbound) {
+    MergeProperties(base_mesh_->prev_halfedge_handle(bheh),
+       base_mesh_->opposite_halfedge_handle(base_mesh_->next_halfedge_handle(bheh)));
+  }
+  if (debug_hard_stop_) return;
+  assert(base_mesh_->is_valid_handle(base_mesh_->next_halfedge_handle(bheho)));
+  assert(base_mesh_->is_valid_handle(base_mesh_->opposite_halfedge_handle(
+                                     base_mesh_->prev_halfedge_handle(bheho))));
+  if (!rbound){
+    MergeProperties(base_mesh_->next_halfedge_handle(bheho),
+       base_mesh_->opposite_halfedge_handle(base_mesh_->prev_halfedge_handle(bheho)));
+  }
+  if (debug_hard_stop_) return;
+
+  base_mesh_->collapse(bheh);
+}
+
+/*!
+ * \brief Embedding::LowLevelBaseCollapse reimplemented collapse method on the base mesh, not sure
+ * if this is used at all? TODO: check if this needs deletion.
+ * \param bheh
+ */
+void Embedding::LowLevelBaseCollapse(OpenMesh::HalfedgeHandle bheh) {
+  // See PolyConnectivity::collapse and PolyConnectivity::collapse_edge
+  OpenMesh::HalfedgeHandle  h  = bheh;
+  OpenMesh::HalfedgeHandle  hn = base_mesh_->next_halfedge_handle(h);
+  OpenMesh::HalfedgeHandle  hp = base_mesh_->prev_halfedge_handle(h);
+
+  OpenMesh::HalfedgeHandle  o  = base_mesh_->opposite_halfedge_handle(h);
+  OpenMesh::HalfedgeHandle  on = base_mesh_->next_halfedge_handle(o);
+  OpenMesh::HalfedgeHandle  op = base_mesh_->prev_halfedge_handle(o);
+
+  OpenMesh::FaceHandle      fh = base_mesh_->face_handle(h);
+  OpenMesh::FaceHandle      fo = base_mesh_->face_handle(o);
+
+  OpenMesh::VertexHandle    vh = base_mesh_->to_vertex_handle(h);
+  OpenMesh::VertexHandle    vo = base_mesh_->to_vertex_handle(o);
+
+  // halfedge -> vertex
+  for (auto vih_it : base_mesh_->vih_range(vo)) {
+    base_mesh_->set_vertex_handle(vih_it, vh);
+  }
+
+  // halfedge -> halfedge
+  base_mesh_->set_next_halfedge_handle(hp, hn);
+  base_mesh_->set_next_halfedge_handle(op, on);
+
+
+  // face -> halfedge
+  if (base_mesh_->is_valid_handle(fh))  {
+    base_mesh_->set_halfedge_handle(fh, hn);
+  }
+  if (base_mesh_->is_valid_handle(fo))  {
+    base_mesh_->set_halfedge_handle(fo, on);
+  }
+
+
+  // vertex -> halfedge
+  if (base_mesh_->halfedge_handle(vh) == o)  {
+    base_mesh_->set_halfedge_handle(vh, hn);
+  }
+  base_mesh_->adjust_outgoing_halfedge(vh);
+  base_mesh_->set_isolated(vo);
+
+  // delete stuff
+  base_mesh_->status(base_mesh_->edge_handle(h)).set_deleted(true);
+  base_mesh_->status(vo).set_deleted(true);
+  if (base_mesh_->has_halfedge_status())
+  {
+    base_mesh_->status(h).set_deleted(true);
+    base_mesh_->status(o).set_deleted(true);
+  }
+
+  // How to handle loops then?
+}
+
+/*!
+ * \brief Embedding::AdjustPointersForBheCollapse lots of pointer operations to ensure a
+ * base collapse operation does not disrupt the embedding
+ * \param bheh
+ */
+void Embedding::AdjustPointersForBheCollapse(OpenMesh::HalfedgeHandle bheh) {
+  auto bheho = base_mesh_->opposite_halfedge_handle(bheh);
+
+  // move next_heh_ pointers and meta->base pointers if applicable
+  if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh))
+      && !base_mesh_->status(base_mesh_->property(next_heh_, bheh)).deleted()
+      && base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheho))
+      && !base_mesh_->status(base_mesh_->property(next_heh_, bheho)).deleted()) {
+    auto bhehn = base_mesh_->property(next_heh_, bheh);
+    auto bhehno = base_mesh_->opposite_halfedge_handle(bhehn);
+    auto bhehpo = base_mesh_->property(next_heh_, bheho);
+    auto bhehp = base_mesh_->opposite_halfedge_handle(bhehpo);
+    assert(base_mesh_->property(next_heh_, bhehp) == bheh);
+    assert(base_mesh_->property(next_heh_, bhehno) == bheho);
+    base_mesh_->property(next_heh_, bhehp) = bhehn;
+    base_mesh_->property(next_heh_, bhehno) = bhehpo;
+    assert(base_mesh_->property(bhe_connection_, bhehp)
+           == base_mesh_->property(bhe_connection_, bhehn));
+    assert(base_mesh_->property(bhe_connection_, bhehno)
+           == base_mesh_->property(bhe_connection_, bhehpo));
+  } else if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh))
+             && !base_mesh_->status(base_mesh_->property(next_heh_, bheh)).deleted()) {
+    assert(meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+               base_mesh_->from_vertex_handle(bheh))));
+    meta_mesh_->property(mhe_connection_, base_mesh_->property(bhe_connection_, bheh))
+        = base_mesh_->property(next_heh_, bheh);
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+                           base_mesh_->property(next_heh_, bheh)))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  } else if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheho))
+             && !base_mesh_->status(base_mesh_->property(next_heh_, bheho)).deleted()) {
+    assert(meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+               base_mesh_->to_vertex_handle(bheh))));
+    meta_mesh_->property(mhe_connection_, base_mesh_->property(bhe_connection_, bheho))
+        = base_mesh_->property(next_heh_, bheho);
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+                           base_mesh_->property(next_heh_, bheho)))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  } else {
+    // Sanity check; don't collapse 1 edge long meta edges.
+    assert(!base_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh)));
+  }
+
+  // bheh property deletion for visual debugging
+  base_mesh_->property(next_heh_, bheh)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  base_mesh_->property(next_heh_, bheho)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  base_mesh_->property(bhe_connection_, bheh)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  base_mesh_->property(bhe_connection_, bheho)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+
+  // TESTS
+  // Traversal tests
+  if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh))
+      && base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheho))) {
+    assert(base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+           base_mesh_->property(next_heh_, bheh)))
+           == base_mesh_->property(next_heh_, bheho));
+    assert(base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+           base_mesh_->property(next_heh_, bheho)))
+           == base_mesh_->property(next_heh_, bheh));
+  } else if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh))) {
+    assert(base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+           base_mesh_->property(next_heh_, bheh))) != bheho);
+  } else if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheho))) {
+    assert(base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+           base_mesh_->property(next_heh_, bheho))) != bheh);
+  }
+  // Connection tests
+  if (base_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))
+      && !base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheho))) {
+    // Ensure new connectivity
+    assert(meta_mesh_->property(mhe_connection_, base_mesh_->property(bhe_connection_, bheh))
+           == base_mesh_->property(next_heh_, bheh));
+    assert(!base_mesh_->is_valid_handle(base_mesh_->property(next_heh_,
+            base_mesh_->opposite_halfedge_handle(
+            base_mesh_->property(next_heh_, bheh)))));
+  }
+  if (base_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheho))
+      && !base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh))) {
+    // Ensure new connectivity
+    assert(meta_mesh_->property(mhe_connection_, base_mesh_->property(bhe_connection_, bheho))
+           == base_mesh_->property(next_heh_, bheho));
+    assert(!base_mesh_->is_valid_handle(base_mesh_->property(next_heh_,
+            base_mesh_->opposite_halfedge_handle(
+            base_mesh_->property(next_heh_, bheho)))));
+  }
+  // bsplithandle tests
+  assert(base_mesh_->property(bsplithandle_, base_mesh_->to_vertex_handle(bheh)) != bheho);
+}
+
+/*!
+ * \brief Embedding::MergeProperties
+  * Takes two edges and transfers the properties of and pointers
+  * pointing to the first to the second if they're not empty.
+ * \param bheh0
+ * \param bheh1
+ */
+void Embedding::MergeProperties(OpenMesh::HalfedgeHandle bheh0,
+                                OpenMesh::HalfedgeHandle bheh1) {
+  auto bheh0o = base_mesh_->opposite_halfedge_handle(bheh0);
+  auto bheh1o = base_mesh_->opposite_halfedge_handle(bheh1);
+  auto nprop0 = base_mesh_->property(next_heh_, bheh0);
+  auto nprop0o = base_mesh_->property(next_heh_, bheh0o);
+  auto nprop1 = base_mesh_->property(next_heh_, bheh1);
+  auto nprop1o = base_mesh_->property(next_heh_, bheh1o);
+  auto bhcprop0 = base_mesh_->property(bhe_connection_, bheh0);
+  auto bhcprop0o = base_mesh_->property(bhe_connection_, bheh0o);
+  auto bhcprop1 = base_mesh_->property(bhe_connection_, bheh1);
+  auto bhcprop1o = base_mesh_->property(bhe_connection_, bheh1o);
+  auto wprop0 = base_mesh_->property(halfedge_weight_, bheh0);
+  auto wprop0o = base_mesh_->property(halfedge_weight_, bheh0o);
+  auto wprop1 = base_mesh_->property(halfedge_weight_, bheh1);
+  auto wprop1o = base_mesh_->property(halfedge_weight_, bheh1o);
+  auto mhcprop0 = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  auto mhcprop0o = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  auto mhcprop1 = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  auto mhcprop1o = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  if (meta_mesh_->is_valid_handle(bhcprop0)) {
+    mhcprop0 = meta_mesh_->property(mhe_connection_, bhcprop0);
+  }
+  if (meta_mesh_->is_valid_handle(bhcprop0o)) {
+    mhcprop0o = meta_mesh_->property(mhe_connection_, bhcprop0o);
+  }
+  if (meta_mesh_->is_valid_handle(bhcprop1)) {
+    mhcprop1 = meta_mesh_->property(mhe_connection_, bhcprop1);
+  }
+  if (meta_mesh_->is_valid_handle(bhcprop1o)) {
+    mhcprop1o = meta_mesh_->property(mhe_connection_, bhcprop1o);
+  }
+  auto bsprop0 = base_mesh_->property(bsplithandle_, base_mesh_->from_vertex_handle(bheh0));
+  auto bsprop0o = base_mesh_->property(bsplithandle_, base_mesh_->to_vertex_handle(bheh0));
+  // Sanity check
+  assert((!(mhcprop0 == bheh0) && !(mhcprop0o == bheh0o))
+         || (!(mhcprop1 == bheh1) && !(mhcprop1o == bheh1o)));
+  if (meta_mesh_->is_valid_handle(bhcprop0)) {
+    // more checks
+    assert(meta_mesh_->is_valid_handle(bhcprop0o) && !meta_mesh_->is_valid_handle(bhcprop1)
+           && !meta_mesh_->is_valid_handle(bhcprop1o));
+    assert(!base_mesh_->is_valid_handle(nprop1) && !base_mesh_->is_valid_handle(nprop1o));
+    // property merging
+    // transfer bhe_connection properties
+    base_mesh_->property(bhe_connection_, bheh1) = bhcprop0;
+    base_mesh_->property(bhe_connection_, bheh1o) = bhcprop0o;
+
+    // check the sides
+    if (!base_mesh_->is_valid_handle(nprop0o)) {
+      assert(mhcprop0 == bheh0);
+      meta_mesh_->property(mhe_connection_, bhcprop0) = bheh1;
+    // if this is not the end of the edge fix the next_heh pointers
+    } else {
+      assert(base_mesh_->is_valid_handle(nprop0o));
+      base_mesh_->property(next_heh_, bheh1o) = nprop0o;
+      base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(nprop0o))
+          = bheh1;
+      assert(base_mesh_->property(bhe_connection_,
+                                  base_mesh_->opposite_halfedge_handle(nprop0o))
+             == base_mesh_->property(bhe_connection_, bheh1));
+    }
+    // repeat for the other side
+    if (!base_mesh_->is_valid_handle(nprop0)) {
+      assert(mhcprop0o == bheh0o);
+      meta_mesh_->property(mhe_connection_, bhcprop0o) = bheh1o;
+    } else {
+      assert(base_mesh_->is_valid_handle(nprop0));
+      base_mesh_->property(next_heh_, bheh1) = nprop0;
+      base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(nprop0))
+          = bheh1o;
+      assert(base_mesh_->property(bhe_connection_,
+                                  base_mesh_->opposite_halfedge_handle(nprop0))
+             == base_mesh_->property(bhe_connection_, bheh1o));
+    }
+  }
+  // transfer bsplithandle properties
+  if (bsprop0 == bheh0) {
+    base_mesh_->property(bsplithandle_, base_mesh_->from_vertex_handle(bheh0)) = bheh1;
+  }
+  if (bsprop0o == bheh0o) {
+    base_mesh_->property(bsplithandle_, base_mesh_->to_vertex_handle(bheh0)) = bheh1o;
+  }
+
+  assert(static_cast<int>(wprop0) == static_cast<int>(wprop0o));
+  assert(static_cast<int>(wprop1) == static_cast<int>(wprop1o));
+  base_mesh_->property(halfedge_weight_, bheh1) = std::min(wprop0, wprop1);
+  base_mesh_->property(halfedge_weight_, bheh1o) = std::min(wprop0, wprop1);
+
+  // bheh0 property deletion for visual debugging
+  base_mesh_->property(next_heh_, bheh0)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  base_mesh_->property(next_heh_, bheh0o)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  base_mesh_->property(bhe_connection_, bheh0)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  base_mesh_->property(bhe_connection_, bheh0o)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+
+  // TESTS:
+  // Traversal tests
+  if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh0))) {
+    assert(base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+           base_mesh_->property(next_heh_, bheh0))) == bheh1o);
+    assert(base_mesh_->property(next_heh_, bheh0)
+           == base_mesh_->property(next_heh_, bheh1));
+  }
+  if (base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh0o))) {
+    assert(base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(
+           base_mesh_->property(next_heh_, bheh0o))) == bheh1);
+    assert(base_mesh_->property(next_heh_, bheh0o)
+           == base_mesh_->property(next_heh_, bheh1o));
+  }
+  // Connection tests
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh0))) {
+    assert(base_mesh_->property(bhe_connection_, bheh0)
+           == base_mesh_->property(bhe_connection_, bheh1));
+    assert(base_mesh_->property(bhe_connection_, bheh0o)
+           == base_mesh_->property(bhe_connection_, bheh1o));
+  }
+  if (!meta_mesh_->is_valid_handle(bhcprop0)
+      && !meta_mesh_->is_valid_handle(bhcprop1)) {
+    assert(!base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh1)));
+    if (meta_mesh_->is_valid_handle(bhcprop0o)
+        || meta_mesh_->is_valid_handle(bhcprop1o)) {
+      assert(meta_mesh_->property(mhe_connection_,
+             base_mesh_->property(bhe_connection_, bheh1o)) == bheh1o);
+    }
+  }
+  if (!meta_mesh_->is_valid_handle(bhcprop0o)
+      && !meta_mesh_->is_valid_handle(bhcprop1o)) {
+    assert(!base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh1o)));
+    if (meta_mesh_->is_valid_handle(bhcprop0)
+        || meta_mesh_->is_valid_handle(bhcprop1)) {
+      assert(meta_mesh_->property(mhe_connection_,
+             base_mesh_->property(bhe_connection_, bheh1)) == bheh1);
+    }
+  }
+  // bsplithandle tests
+  assert(base_mesh_->property(bsplithandle_, base_mesh_->from_vertex_handle(bheh0)) != bheh0);
+  assert(base_mesh_->property(bsplithandle_, base_mesh_->to_vertex_handle(bheh0)) != bheh0o);
+  // existence tests
+  assert(!base_mesh_->status(bheh0).deleted());
+  assert(!base_mesh_->status(bheh0o).deleted());
+  assert(!base_mesh_->status(bheh1).deleted());
+  assert(!base_mesh_->status(bheh1o).deleted());
+}
+
+/*!
+ * \brief Embedding::Delaunay delaunay triangulation
+ * \param voronoidistance
+ * \param to_heh
+ * \param multiplicity_heh
+ * \param type
+ * \return true for success, false for failure
+ */
+bool Embedding::Delaunay(OpenMesh::VPropHandleT<double> voronoidistance,
+                         OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> to_heh,
+                         OpenMesh::HPropHandleT<int> multiplicity_heh) {
+
+  for (auto bvh : base_mesh_->vertices())  {
+    base_mesh_->property(voronoiID_, bvh) = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+    base_mesh_->property(to_heh, bvh) = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(voronoidistance, bvh)=0.0;
+  }
+
+  for (auto bheh : base_mesh_->halfedges())  {
+    base_mesh_->property(multiplicity_heh, bheh) = 0;
+  }
+
+  std::queue<OpenMesh::VertexHandle> metavhqueue;
+  for (auto mvh : meta_mesh_->vertices()){
+    //qDebug() << "Meta Vertex: " << mvh.idx();
+    auto bvh = meta_mesh_->property(mv_connection_, mvh);
+    base_mesh_->property(voronoiID_, bvh)=mvh;
+    metavhqueue.push(bvh);
+  }
+  qDebug() << "Creating Voronoi Regions";
+  NaiveVoronoi(metavhqueue, voronoidistance, to_heh);
+  qDebug() << "Finished Voronoi Regions";
+
+  for (auto fh : base_mesh_->faces()) {
+    auto bheh0 = base_mesh_->halfedge_handle(fh);
+    auto bheh1 = base_mesh_->next_halfedge_handle(bheh0);
+    auto bheh2 = base_mesh_->next_halfedge_handle(bheh1);
+    auto vh0 = base_mesh_->from_vertex_handle(bheh0);
+    auto vh1 = base_mesh_->to_vertex_handle(bheh0);
+    auto vh2 = base_mesh_->to_vertex_handle(bheh1);
+    auto metavh0 = base_mesh_->property(voronoiID_, vh0);
+    auto metavh1 = base_mesh_->property(voronoiID_, vh1);
+    auto metavh2 = base_mesh_->property(voronoiID_, vh2);
+    if (metavh0 != metavh1 && metavh0 != metavh2 && metavh1 != metavh2) {
+      auto mheh0 = FindHalfedge(bheh0);
+      auto mheh1 = FindHalfedge(bheh1);
+      auto mheh2 = FindHalfedge(bheh2);
+      auto mf = AddFace({metavh0, metavh1, metavh2}, {mheh0, mheh1, mheh2});
+      SetFaceProperties(fh, mf);
+      mheh0 = meta_mesh_->halfedge_handle(mf);
+      mheh1 = meta_mesh_->next_halfedge_handle(mheh0);
+      mheh2 = meta_mesh_->next_halfedge_handle(mheh1);
+
+      SetBorderProperties(meta_mesh_->property(mhe_border_, mheh0), mheh0);
+      SetBorderProperties(meta_mesh_->property(mhe_border_, mheh1), mheh1);
+      SetBorderProperties(meta_mesh_->property(mhe_border_, mheh2), mheh2);
+    }
+  }
+  qDebug() << "Done adding all basic faces.";
+
+  qDebug() << "Applying mesh fixes.";
+  if (meta_mesh_->n_vertices()>0) {
+    std::vector<int> genus = VoronoiGenus();
+    for (auto mvh : meta_mesh_->vertices()) {
+      if (genus.at(static_cast<unsigned long>(mvh.idx())) != 1) {
+        qDebug() << "Voronoi Region " << mvh.idx() << " has a euler characteristic of "
+                 << genus.at(static_cast<unsigned long>(mvh.idx()))
+                 << " perhaps you should choose different seed points.";
+        return false;
+      }
+    }
+  }
+
+  if (boundaries_) {
+    qDebug() << "Collecting boundary faces.";
+    for (auto mvh : meta_mesh_->vertices()) {
+      if (base_mesh_->is_boundary(meta_mesh_->property(mv_connection_, mvh))) {
+        auto mheh = meta_mesh_->halfedge_handle(mvh);
+        if (!meta_mesh_->is_valid_handle(mheh)) {
+          TraverseBoundary(mvh);
+        } else {
+          auto mvhnext = meta_mesh_->to_vertex_handle(mheh);
+          // Traverse the boundary if there is still an undetected boundary halfedge
+          if (!base_mesh_->is_boundary(meta_mesh_->property(mv_connection_, mvhnext))) {
+            TraverseBoundary(mvh);
+          }
+        }
+      }
+    }
+  }
+
+  if (boundaries_) {
+    qDebug() << "Collecting Voronoi borders near boundaries.";
+    for (auto bfh : base_mesh_->faces()) {
+      std::list<int> neighbors;
+      for (auto bfhiter : base_mesh_->ff_range(bfh)) {
+        if (!neighbors.empty()
+            && std::binary_search(neighbors.begin(), neighbors.end(), bfhiter.idx())) {
+          qDebug() << "Found a pair of Voronoi regions with more than one border,"
+                      " try triangulating again with a different configuration.";
+          return false;
+        }
+        neighbors.push_back(bfhiter.idx());
+      }
+    }
+    VoronoiBorders();
+  }
+
+  bool correct_voronoi_borders = true;
+  for (auto bheh : base_mesh_->halfedges()) {
+    if ((base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bheh))
+        != base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bheh)))
+        && !meta_mesh_->is_valid_handle(base_mesh_->property(bhe_border_, bheh))) {
+        qDebug() << "Invalid border mheh "
+                 << base_mesh_->property(bhe_border_, bheh).idx();
+      base_mesh_->status(bheh).set_selected(true);
+      correct_voronoi_borders = false;
+      debug_hard_stop_ = true;
+    }
+  }
+  if (!correct_voronoi_borders) {
+    qDebug() << "border detection failed, exiting triangulation.";
+    return false;
+  }
+  if (boundaries_) {
+    AddBoundaries();
+  }
+  return true;
+}
+
+/*!
+ * \brief Embedding::TraverseBoundary traverse the boundary that mvh lies on and add faces
+ * along the boundary that were missed by the voronoi triangulation
+ * \param mvh
+ */
+void Embedding::TraverseBoundary(OpenMesh::VertexHandle mvh) {
+  assert(base_mesh_->is_boundary(meta_mesh_->property(mv_connection_, mvh)));
+  OpenMesh::HalfedgeHandle bhehstart = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  // Exactly one outgoing halfedge should be a boundary
+  for (auto bvoheh : base_mesh_->voh_range(meta_mesh_->property(mv_connection_, mvh))) {
+    if (base_mesh_->is_boundary(bvoheh)) {
+      assert(!base_mesh_->is_valid_handle(bhehstart));
+      bhehstart = bvoheh;
+    }
+  }
+  assert(base_mesh_->is_valid_handle(bhehstart));
+  auto bhehcurr = bhehstart;
+  std::vector<OpenMesh::VertexHandle> newfacev = {mvh};
+  std::vector<OpenMesh::HalfedgeHandle> newfaceh = {};
+  std::vector<OpenMesh::HalfedgeHandle> boundaryborders = {};
+  // Traverse the boundary
+  do {
+    assert(base_mesh_->is_boundary(bhehcurr));
+    auto currVID = newfacev.back();
+    auto newVID = base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bhehcurr));
+    // Stepping over a voronoi region border: Register the new region as part of the face
+    if (newVID != currVID) {
+      newfacev.push_back(newVID);
+      auto mhehnew = FindHalfedge(bhehcurr);
+      newfaceh.push_back(mhehnew);
+      boundaryborders.push_back(bhehcurr);
+    }
+    // Stepping into a meta vertex: Complete the face
+    if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+                                    base_mesh_->to_vertex_handle(bhehcurr)))) {
+      assert(newVID == base_mesh_->property(bv_connection_,
+                                            base_mesh_->to_vertex_handle(bhehcurr)));
+      // Less than 3 means no new face was found
+      if (newfacev.size() > 2) {
+        newfaceh.push_back(OpenMesh::PolyConnectivity::InvalidHalfedgeHandle);
+        auto mfh = AddFace(newfacev, newfaceh);
+        CopyFaceProperties(mfh, boundaryborders);
+        auto mheh = meta_mesh_->halfedge_handle(mfh);
+        auto mhehiter = mheh;
+        do {
+          if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_border_, mhehiter))) {
+            SetBorderProperties(meta_mesh_->property(mhe_border_, mhehiter), mhehiter);
+          }
+          mhehiter = meta_mesh_->next_halfedge_handle(mhehiter);
+        } while (mhehiter != mheh);
+      }
+      // Start collecting the next face, starting with the current mvh
+      newfacev.clear();
+      newfaceh.clear();
+      boundaryborders.clear();
+      newfacev.push_back(newVID);
+    }
+
+    bhehcurr = base_mesh_->next_halfedge_handle(bhehcurr);
+  } while (bhehcurr != bhehstart);
+}
+
+/*!
+ * \brief Embedding::fact
+ * \param n
+ * \return n!
+ */
+int Embedding::fact(int n)
+{
+  return (n == 1 || n == 0) ? 1 : fact(n - 1) * n;
+}
+
+/*!
+ * \brief Embedding::nCk
+ * \param n
+ * \param k
+ * \return nCk(n,k)
+ */
+int Embedding::nCk(int n, int k) {
+  return (fact(n)/(fact(k)*fact(n-k)));
+}
+
+/*!
+ * \brief Embedding::VoronoiGenus
+ * \return the vector with the geni (or euler characteristics? one of those)
+ * of the voronoi regions
+ */
+std::vector<int> Embedding::VoronoiGenus() {
+  // Initialize faces as 1 to make this work, disk topology
+  std::vector<int> faces(meta_mesh_->n_vertices(), 0);
+  std::vector<int> edges(meta_mesh_->n_vertices(), 0);
+  std::vector<int> vertices(meta_mesh_->n_vertices(), 0);
+  std::vector<int> genus;
+
+  for (auto fh : base_mesh_->faces()) {
+    auto heh0 = base_mesh_->halfedge_handle(fh);
+    auto heh1 = base_mesh_->next_halfedge_handle(heh0);
+    auto id0 = base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(heh0));
+    auto id1 = base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(heh0));
+    auto id2 = base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(heh1));
+    if (id0 == id1 && id1 == id2) {
+      faces.at(static_cast<unsigned long>(id0.idx())) += 1;
+    }
+  }
+  for (auto eh : base_mesh_->edges()) {
+    auto vh0 = base_mesh_->from_vertex_handle(base_mesh_->halfedge_handle(eh, 0));
+    auto vh1 = base_mesh_->to_vertex_handle(base_mesh_->halfedge_handle(eh, 0));
+    auto id0 = base_mesh_->property(voronoiID_, vh0);
+    auto id1 = base_mesh_->property(voronoiID_, vh1);
+    if (id0 == id1) {
+      edges.at(static_cast<unsigned long>(id0.idx())) += 1;
+    }
+  }
+  for (auto vh : base_mesh_->vertices()) {
+    auto id = base_mesh_->property(voronoiID_, vh);
+    vertices.at(static_cast<unsigned long>(id.idx())) += 1;
+  }
+  for (unsigned long i=0; i<meta_mesh_->n_vertices(); ++i) {
+    int g = vertices.at(i) - edges.at(i) + faces.at(i);
+    genus.push_back(g);
+  }
+  return genus;
+}
+
+/*!
+ * \brief Embedding::SetFaceProperties link a base face with a meta face (connect their
+ * property handles etc.)
+ * \param bf
+ * \param mf
+ * \param voronoidistance
+ * \param to_heh
+ * \param type
+ */
+void Embedding::SetFaceProperties(OpenMesh::FaceHandle bf,
+                                  OpenMesh::FaceHandle mf) {
+  auto bheh0 = base_mesh_->halfedge_handle(bf);
+  auto bheh1 = base_mesh_->next_halfedge_handle(bheh0);
+  auto bheh2 = base_mesh_->next_halfedge_handle(bheh1);
+  auto mheh0 = meta_mesh_->halfedge_handle(mf);
+  auto mheh1 = meta_mesh_->next_halfedge_handle(mheh0);
+  auto mheh2 = meta_mesh_->next_halfedge_handle(mheh1);
+  meta_mesh_->property(mhe_connection_, mheh0) = bheh0;
+  meta_mesh_->property(mhe_connection_, mheh1) = bheh1;
+  meta_mesh_->property(mhe_connection_, mheh2) = bheh2;
+  meta_mesh_->property(mhe_border_, mheh0) = bheh0;
+  meta_mesh_->property(mhe_border_, mheh1) = bheh1;
+  meta_mesh_->property(mhe_border_, mheh2) = bheh2;
+}
+
+/*!
+ * \brief Embedding::CopyFaceProperties
+ * \param mfh
+ * \param boundaryborders
+ */
+void Embedding::CopyFaceProperties(OpenMesh::FaceHandle mfh,
+                std::vector<OpenMesh::HalfedgeHandle> boundaryborders) {
+  auto mheh = meta_mesh_->halfedge_handle(mfh);
+  auto mhehiter = mheh;
+  uint borderctr = 0;
+  uint mfval = meta_mesh_->valence(mfh);
+  for (uint i=0; i<boundaryborders.size(); ++i) {
+    auto bhehb = boundaryborders.at(i);
+    assert(meta_mesh_->from_vertex_handle(mhehiter)
+           == base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bhehb)));
+    assert(meta_mesh_->to_vertex_handle(mhehiter)
+           == base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bhehb)));
+    mhehiter = meta_mesh_->next_halfedge_handle(mhehiter);
+  }
+  mhehiter = mheh;
+  do {
+    if (meta_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_,
+                                    meta_mesh_->opposite_halfedge_handle(mhehiter)))) {
+      meta_mesh_->property(mhe_connection_, mhehiter) = base_mesh_->opposite_halfedge_handle(
+        meta_mesh_->property(mhe_connection_, meta_mesh_->opposite_halfedge_handle(mhehiter)));
+    } else if (borderctr < mfval-1) {
+      auto bhehb = boundaryborders.at(borderctr);
+      assert(base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bhehb))
+             == meta_mesh_->from_vertex_handle(mhehiter));
+      assert(base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bhehb))
+             == meta_mesh_->to_vertex_handle(mhehiter));
+      meta_mesh_->property(mhe_connection_, mhehiter) = boundaryborders.at(borderctr);
+    }
+    if (meta_mesh_->is_valid_handle(meta_mesh_->property(mhe_border_,
+                                    meta_mesh_->opposite_halfedge_handle(mhehiter)))) {
+      meta_mesh_->property(mhe_border_, mhehiter) = base_mesh_->opposite_halfedge_handle(
+        meta_mesh_->property(mhe_border_, meta_mesh_->opposite_halfedge_handle(mhehiter)));
+    } else if (borderctr < mfval-1) {
+      meta_mesh_->property(mhe_border_, mhehiter) = boundaryborders.at(borderctr);
+    }
+    assert(base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehiter))
+           || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mhehiter)));
+    ++borderctr;
+    mhehiter = meta_mesh_->next_halfedge_handle(mhehiter);
+  } while (mheh != mhehiter);
+}
+
+/*!
+ * \brief Embedding::AddFace adds a meta face
+ * \param mvh vertices
+ * \param mheh halfedges, going mvh(i) -> mheh(i) -> mvh(i+1)
+ * Use the opposite halfedges of adjacent faces for these, or an invalid handle if they don't exist
+ * yet and in that case AddFace will make them.
+ * \return the facehandle
+ */
+OpenMesh::FaceHandle Embedding::AddFace(std::vector<OpenMesh::VertexHandle> mvh,
+                       std::vector<OpenMesh::HalfedgeHandle> mheh) {
+  assert(mvh.size() == mheh.size());
+  for (uint i = 0; i<mheh.size(); ++i) {
+    if (!meta_mesh_->is_valid_handle(mheh.at(i))) {
+      mheh.at(i) = meta_mesh_->new_edge(mvh.at(i), mvh.at((i+1)%mvh.size()));
+    }
+  }
+  for (uint i = 0; i<mheh.size(); ++i) {
+    assert(meta_mesh_->from_vertex_handle(mheh.at(i)) == mvh.at(i));
+    assert(meta_mesh_->to_vertex_handle(mheh.at(i)) == mvh.at((i+1)%mvh.size()));
+  }
+
+  // consistent with mesh_->add_face()
+  auto fnew(meta_mesh_->new_face());
+
+  // Face->Halfedge pointer
+  meta_mesh_->set_halfedge_handle(fnew, mheh.front());
+
+  // Halfedge->Face Pointers
+  for (uint i = 0; i<mheh.size(); ++i) {
+    meta_mesh_->set_face_handle(mheh.at(i), fnew);
+  }
+
+  // Halfedge->Next Halfedge pointers
+  for (uint i = 0; i<mheh.size(); ++i) {
+    meta_mesh_->set_next_halfedge_handle(mheh.at(i), mheh.at((i+1)%mvh.size()));
+  }
+
+  // Halfedge->Vertex Pointers
+  for (uint i = 0; i<mheh.size(); ++i) {
+    meta_mesh_->set_vertex_handle(mheh.at(i), mvh.at((i+1)%mvh.size()));
+  }
+
+  // Vertex->Halfedge Pointers
+  //meta_mesh_->set_isolated(mvh0);
+  for (uint i = 0; i<mheh.size(); ++i) {
+    auto mheho = meta_mesh_->opposite_halfedge_handle(mheh.at(i));
+    if (!meta_mesh_->is_valid_handle(meta_mesh_->halfedge_handle(mvh.at(i)))
+        || meta_mesh_->is_boundary(mheho)) {
+      meta_mesh_->set_halfedge_handle(mvh.at((i+1)%mvh.size()), mheho);
+    }
+  }
+
+  assert(meta_mesh_->is_valid_handle(meta_mesh_->halfedge_handle(fnew)));  
+  for (uint i = 0; i<mheh.size(); ++i) {
+    assert(meta_mesh_->is_valid_handle(meta_mesh_->next_halfedge_handle(mheh.at(i))));
+    assert(meta_mesh_->is_valid_handle((meta_mesh_->halfedge_handle(mvh.at(i)))));
+  }
+  return fnew;
+}
+
+/*!
+ * \brief Embedding::AddBoundaries set meta properties and pointers for boundaries.
+ */
+void Embedding::AddBoundaries() {
+  for (auto mheh : meta_mesh_->halfedges()) {
+    if (!meta_mesh_->is_valid_handle(meta_mesh_->next_halfedge_handle(mheh))
+        && base_mesh_->is_boundary(base_mesh_->halfedge_handle(
+           meta_mesh_->property(mv_connection_, meta_mesh_->from_vertex_handle(mheh))))) {
+      meta_mesh_->set_boundary(mheh);
+      meta_mesh_->set_next_halfedge_handle(mheh, meta_mesh_->halfedge_handle(
+                                             meta_mesh_->to_vertex_handle(mheh)));
+      meta_mesh_->set_halfedge_handle(meta_mesh_->from_vertex_handle(mheh), mheh);
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::FindHalfedge
+ * \param bheh bheh lying on a mheh
+ * \return the opposite meta halfedge of the meta halfedge bheh lies on, or nothing
+ * if this is invalid
+ */
+OpenMesh::HalfedgeHandle Embedding::FindHalfedge(OpenMesh::HalfedgeHandle bheh) {
+  auto oppmheh = base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(bheh));
+  if (!meta_mesh_->is_valid_handle(oppmheh)) {
+    return OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  } else {
+    return meta_mesh_->opposite_halfedge_handle(oppmheh);
+  }
+}
+
+/*!
+ * \brief Embedding::VoronoiBorders iteration over all border regions
+ */
+void Embedding::VoronoiBorders() {
+  // Considering the inclusion of boundaries in the meta mesh a local border traversal
+  // becomes more complicated since it would involve iterating around boundaries.
+  // Considering that, iterating over all base halfedges should be faster.
+  // However this leaves us unable to determine which border between regions a halfedge
+  // lies on if those two regions have two borders with each other.
+  // So keep in mind that using a global solution requires forbidding initial triangulations
+  // where two faces have more than 1 boundary to each other.
+  for (auto mheh : meta_mesh_->halfedges()) {
+    if (!meta_mesh_->is_boundary(mheh)
+        && meta_mesh_->is_valid_handle(meta_mesh_->property(mhe_border_, mheh))) {
+      SetBorderProperties(meta_mesh_->property(mhe_border_, mheh), mheh);
+    }
+  }
+  if (boundaries_) {
+    for (auto beh : base_mesh_->edges()) {
+      auto bheh0 = base_mesh_->halfedge_handle(beh, 0);
+        if (!base_mesh_->is_valid_handle(base_mesh_->property(bhe_border_, bheh0))) {
+        auto bheh1 = base_mesh_->halfedge_handle(beh, 1);
+        auto mvh0 = base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bheh0));
+        auto mvh1 = base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bheh0));
+        if (mvh0 != mvh1) {
+          auto mheh0 = meta_mesh_->find_halfedge(mvh0, mvh1);
+          if (!meta_mesh_->is_valid_handle(mheh0)) {
+            qDebug() << "Didn't find a halfedge between mvh " << mvh0.idx()
+                     << "and " << mvh1.idx();
+          }
+          auto mheh1 = meta_mesh_->opposite_halfedge_handle(mheh0);
+          base_mesh_->property(bhe_border_, bheh0) = mheh0;
+          base_mesh_->property(bhe_border_, bheh1) = mheh1;
+        }
+      }
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::SetBorderProperties iteration over one specific border region
+ * TODO: improve this for complex borders, those may break this function at the current state.
+ * \param bheh
+ * \param mheh
+ */
+void Embedding::SetBorderProperties(OpenMesh::HalfedgeHandle bheh,
+                               OpenMesh::HalfedgeHandle mheh) {
+  assert(base_mesh_->is_valid_handle(bheh));
+  if (base_mesh_->property(bhe_border_, bheh) == mheh) {
+    return;
+  }
+  base_mesh_->property(bhe_border_, bheh) = mheh;
+  base_mesh_->property(bhe_border_, base_mesh_->opposite_halfedge_handle(bheh))
+      = meta_mesh_->opposite_halfedge_handle(mheh);
+  auto bheh0 = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(bheh));
+  auto bheh1 = base_mesh_->next_halfedge_handle(bheh0);
+  auto mv0 = meta_mesh_->from_vertex_handle(mheh);
+  auto mv1 = meta_mesh_->to_vertex_handle(mheh);
+  auto bvIDprop0 = base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bheh));
+  auto bvIDprop1 = base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bheh));
+  assert(mv0 == bvIDprop0);
+  assert(mv1 == bvIDprop1);
+  if (base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bheh0)) == mv0
+      && base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bheh0)) == mv1) {
+    SetBorderProperties(bheh0, mheh);
+  } else if (base_mesh_->property(voronoiID_, base_mesh_->from_vertex_handle(bheh1)) == mv0
+      && base_mesh_->property(voronoiID_, base_mesh_->to_vertex_handle(bheh1)) == mv1) {
+    SetBorderProperties(bheh1, mheh);
+  }
+}
+
+/*!
+ * \brief Embedding::A_StarTriangulation
+ * \param type
+ */
+void Embedding::A_StarTriangulation() {
+  for (auto mheh : meta_mesh_->halfedges()) {
+    if (meta_mesh_->is_boundary(mheh)
+        || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheh))) {
+      if (debug_hard_stop_)
+        return;
+      Trace(mheh, true, false);
+    }
+  }
+  for (auto mheh : meta_mesh_->halfedges()) {
+    if (!(meta_mesh_->is_boundary(mheh)
+        || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheh)))) {
+      if (base_mesh_->property(bhe_connection_, meta_mesh_->property(mhe_connection_, mheh))
+          != mheh) {
+        if (!meta_mesh_->is_boundary(meta_mesh_->from_vertex_handle(mheh))
+            && !meta_mesh_->is_boundary(meta_mesh_->to_vertex_handle(mheh))) {
+          if (debug_hard_stop_)
+            return;
+          Trace(mheh, true, false);
+          assert(base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mheh)));
+        }
+      }
+    }
+  }
+  // Triangulate non-triangular faces
+  if (boundaries_) {
+    // switch initial_triangulation_ off temporarily or correct paths can't be found
+    initial_triangulation_ = false;
+    for (auto mheh : meta_mesh_->halfedges()) {
+      if (!(meta_mesh_->is_boundary(mheh)
+          || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheh)))) {
+        if (base_mesh_->property(bhe_connection_, meta_mesh_->property(mhe_connection_, mheh))
+            != mheh) {
+          if (meta_mesh_->is_boundary(meta_mesh_->from_vertex_handle(mheh))
+              || meta_mesh_->is_boundary(meta_mesh_->to_vertex_handle(mheh))) {
+            if (debug_hard_stop_)
+              return;
+            Trace(mheh, false, false);
+          }
+        }
+      }
+    }
+    for (auto mfh : meta_mesh_->faces()) {
+      if (meta_mesh_->valence(mfh) != 3) {
+        auto mhehstart = meta_mesh_->halfedge_handle(mfh);
+        auto mheh0 = meta_mesh_->next_halfedge_handle(
+              meta_mesh_->next_halfedge_handle(mhehstart));
+        auto mheh1 = meta_mesh_->next_halfedge_handle(mheh0);
+        while (mheh1 != mhehstart) {
+          auto mhehnew = AddMetaEdge(mhehstart, mheh0);
+          Trace(mhehnew, false, false);
+          mheh0 = mheh1;
+          mheh1 = meta_mesh_->next_halfedge_handle(mheh1);
+        }
+      }
+    }
+    initial_triangulation_ = true;
+  }
+
+  base_mesh_->update_normals();
+}
+
+/*!
+ * \brief Embedding::NaiveVoronoi build voronoi regions
+ * \param queue
+ * \param voronoidistance
+ * \param to_heh
+ */
+void Embedding::NaiveVoronoi(std::queue<OpenMesh::VertexHandle> queue,
+                             OpenMesh::VPropHandleT<double> voronoidistance,
+                             OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> to_heh) {
+  while (!queue.empty()) {
+    auto vh = queue.front();
+    for (auto vvh : base_mesh_->vv_range(vh)){
+      if (!meta_mesh_->is_valid_handle(base_mesh_->property(voronoiID_, vvh))) {
+        base_mesh_->property(voronoiID_, vvh) = base_mesh_->property(voronoiID_, vh);
+        base_mesh_->property(voronoidistance, vvh) = base_mesh_->property(voronoidistance, vh) +
+            base_mesh_->calc_edge_length(base_mesh_->find_halfedge(vh,vvh));
+        base_mesh_->property(to_heh, vvh) = base_mesh_->find_halfedge(vh,vvh);
+        queue.push(vvh);
+      } else  {
+        auto newdist = base_mesh_->property(voronoidistance, vh) +
+            base_mesh_->calc_edge_length(base_mesh_->find_halfedge(vh,vvh));
+        if (newdist < base_mesh_->property(voronoidistance, vvh)) {
+          base_mesh_->property(voronoidistance, vvh) = newdist;
+          base_mesh_->property(voronoiID_, vvh) = base_mesh_->property(voronoiID_, vh);
+          base_mesh_->property(to_heh, vvh) = base_mesh_->find_halfedge(vh,vvh);
+          queue.push(vvh);
+          if (base_mesh_->property(voronoiID_, vvh) != base_mesh_->property(voronoiID_, vh)) {
+            base_mesh_->property(voronoiID_, vvh) = base_mesh_->property(voronoiID_, vh);
+          }
+        }
+      }
+    }
+    queue.pop();
+  }
+
+  ColorizeVoronoiRegions();
+}
+
+/*!
+ * \brief Embedding::GetMetaEdge
+ * \param bvh
+ * \return the meta edge bvh lies on or InvalidHandle if it lies on none
+ */
+OpenMesh::EdgeHandle Embedding::GetMetaEdge(OpenMesh::VertexHandle bvh) {
+  if(meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh))) {
+    //qDebug() << "Called GetMetaEdge on a Meta Vertex";
+    return OpenMesh::PolyConnectivity::InvalidEdgeHandle;
+  }
+  for (auto bheh : base_mesh_->voh_range(bvh)) {
+    auto mheh = base_mesh_->property(bhe_connection_, bheh);
+    if (meta_mesh_->is_valid_handle(mheh)) {
+      return meta_mesh_->edge_handle(mheh);
+    }
+  }
+  return OpenMesh::PolyConnectivity::InvalidEdgeHandle;
+}
+
+/*!
+ * \brief Embedding::MetaHalfedgeWeight
+ * \param mheh
+ * \return the log of the product of edge weights of the base halfedges in mheh
+ */
+double Embedding::MetaHalfedgeWeight(OpenMesh::HalfedgeHandle mheh) {
+  double weight = 0.0;
+  std::vector<OpenMesh::HalfedgeHandle> hes = GetBaseHalfedges(mheh);
+  for (auto bheh : hes) {
+    weight += std::log(base_mesh_->property(halfedge_weight_, bheh));
+  }
+  return weight;
+}
+
+/*!
+ * \brief Embedding::MetaVertexWeight
+ * \param mvh
+ * \return Vertex weight based on halfedge weight of outgoing base halfedges
+ */
+double Embedding::MetaVertexWeight(OpenMesh::VertexHandle mvh) {
+  double weight = 0.0;
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    auto bheh = meta_mesh_->property(mhe_connection_, moh);
+    weight += std::log(base_mesh_->property(halfedge_weight_, bheh));
+  }
+  return weight;
+}
+
+/*!
+ * \brief Embedding::CalculateEdgeLength
+ * \param mheh
+ * \return edge length of mheh
+ */
+double Embedding::CalculateEdgeLength(OpenMesh::HalfedgeHandle mheh) {
+  assert(meta_mesh_->is_valid_handle(mheh));
+  auto bheh = meta_mesh_->property(mhe_connection_, mheh);
+  assert(base_mesh_->is_valid_handle(bheh));
+  double length = base_mesh_->calc_edge_length(bheh);
+  bheh = base_mesh_->property(next_heh_, bheh);
+  while (base_mesh_->is_valid_handle(bheh)) {
+    length += base_mesh_->calc_edge_length(bheh);
+    bheh = base_mesh_->property(next_heh_, bheh);
+  }
+  return length;
+}
+
+/*!
+ * \brief Embedding::CalculateEdgeLength
+ * \param meh
+ * \return edge length of meh
+ */
+double Embedding::CalculateEdgeLength(OpenMesh::EdgeHandle meh) {
+  return CalculateEdgeLength(meta_mesh_->halfedge_handle(meh, 0));
+}
+
+/*!
+ * \brief Embedding::CalculateFlippedEdgeLength
+ * Calculates the length mheh has after rotation.
+ * This method is only safe if the incident faces of mheh are triangles
+ * mheh will also be retraced in the process (avoiding this would be difficult
+ * without changing a lot of code and removing base mesh cleanup operations)
+ * \param mheh
+ * \return length of flipped mheh
+ */
+double Embedding::CalculateFlippedEdgeLength(OpenMesh::HalfedgeHandle mheh) {
+  Rotate(meta_mesh_->edge_handle(mheh));
+  double length = CalculateEdgeLength(mheh);
+  Rotate(meta_mesh_->edge_handle(mheh));
+
+  return length;
+}
+
+/*!
+ * \brief Embedding::CalculateFlippedEdgeLength
+ * \param meh
+ * \return length of flipped mehs
+ */
+double Embedding::CalculateFlippedEdgeLength(OpenMesh::EdgeHandle meh) {
+  return CalculateFlippedEdgeLength(meta_mesh_->halfedge_handle(meh, 0));
+}
+
+/*!
+ * \brief Embedding::CleanMetaMesh
+ * Delete all meta mesh face handles with valence <3 so that OpenFlipper can display it
+ * Not recommended outside of debugging since behavior afterwards is undefined. This only
+ * serves for visualization of strange edge cases.
+ */
+void Embedding::CleanMetaMesh() {
+  for (auto mfh : meta_mesh_->faces()) {
+    auto mheh = meta_mesh_->halfedge_handle(mfh);
+    auto mvh0 = meta_mesh_->from_vertex_handle(mheh);
+    auto mvh1 = meta_mesh_->to_vertex_handle(mheh);
+    auto mvh2 = meta_mesh_->to_vertex_handle(meta_mesh_->next_halfedge_handle(mheh));
+    if (mvh0 == mvh1) {
+      meta_mesh_->status(mfh).set_deleted(true);
+      meta_mesh_->set_face_handle(mheh, OpenMesh::PolyConnectivity::InvalidFaceHandle);
+    } else if (mvh0 == mvh2) {
+      meta_mesh_->status(mfh).set_deleted(true);
+      meta_mesh_->set_face_handle(mheh, OpenMesh::PolyConnectivity::InvalidFaceHandle);
+      meta_mesh_->set_face_handle(meta_mesh_->next_halfedge_handle(mheh)
+                                  , OpenMesh::PolyConnectivity::InvalidFaceHandle);
+    }
+  }
+  for (auto mheh : meta_mesh_->halfedges()) {
+    if (!meta_mesh_->is_valid_handle(meta_mesh_->face_handle(mheh))) {
+      meta_mesh_->set_boundary(mheh);
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::MetaGarbageCollection
+ * \param vh_update
+ * \param heh_update
+ * \param fh_update
+ */
+void Embedding::MetaGarbageCollection(std::vector<OpenMesh::VertexHandle*> vh_update,
+                                  std::vector<OpenMesh::HalfedgeHandle*> heh_update,
+                                  std::vector<OpenMesh::FaceHandle*> fh_update)
+{
+  for (const auto& bheh : base_mesh_->halfedges()) {
+    if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))) {
+      heh_update.push_back(&base_mesh_->property(bhe_connection_, bheh));
+    }
+  }
+  for (const auto& bvh : base_mesh_->vertices()) {
+    if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh))) {
+      vh_update.push_back(&base_mesh_->property(bv_connection_, bvh));
+    }
+  }
+
+  meta_mesh_->garbage_collection(vh_update, heh_update, fh_update);
+}
+
+/*!
+ * \brief Embedding::BaseGarbageCollection
+ * \param vh_update
+ * \param heh_update
+ * \param fh_update
+ */
+void Embedding::BaseGarbageCollection(std::vector<OpenMesh::VertexHandle*> vh_update,
+                                      std::vector<OpenMesh::HalfedgeHandle*> heh_update,
+                                      std::vector<OpenMesh::FaceHandle*> fh_update)
+{
+  OpenMesh::VPropHandleT<unsigned long> bsplithandle_temp;
+  OpenMesh::HPropHandleT<unsigned long> next_heh_temp;
+  std::vector<OpenMesh::HalfedgeHandle> bsplithandles(base_mesh_->n_vertices());
+  std::vector<OpenMesh::HalfedgeHandle> next_hehs(base_mesh_->n_halfedges());
+  base_mesh_->add_property(bsplithandle_temp, "Temporary property for storing vertex "
+      "indices for the bsplithandle_ property since the garbage collection is useless");
+  base_mesh_->add_property(next_heh_temp, "Temporary property for storing vertex "
+      "indices for the next_heh_ property since the garbage collection is useless");
+  for (const auto& bvh : base_mesh_->vertices()) {
+    auto bsprop = base_mesh_->property(bsplithandle_, bvh);
+    //assert(base_mesh_->is_valid_handle(bsprop)
+    //       || !bsprop.is_valid());
+    unsigned long index = static_cast<unsigned long>(bvh.idx());
+    bsplithandles.at(index) = bsprop;
+    base_mesh_->property(bsplithandle_temp, bvh) = index;
+    if (bsprop != OpenMesh::PolyConnectivity::InvalidHalfedgeHandle) {
+      heh_update.push_back(&bsplithandles.at(index));
+    }
+  }
+  for (const auto& bheh : base_mesh_->halfedges()) {
+    auto nhprop = base_mesh_->property(next_heh_, bheh);
+    //assert(base_mesh_->is_valid_handle(nhprop)
+    //       || !nhprop.is_valid());
+    unsigned long index = static_cast<unsigned long>(bheh.idx());
+    next_hehs.at(index) = nhprop;
+    base_mesh_->property(next_heh_temp, bheh) = index;
+    if (nhprop != OpenMesh::PolyConnectivity::InvalidHalfedgeHandle) {
+      heh_update.push_back(&next_hehs.at(index));
+    }
+  }
+
+  for (const auto& mheh : meta_mesh_->halfedges()) {
+    if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mheh))) {
+      heh_update.push_back(&meta_mesh_->property(mhe_connection_, mheh));
+    }
+  }
+  for (const auto& mvh : meta_mesh_->vertices()) {
+    if (base_mesh_->is_valid_handle(meta_mesh_->property(mv_connection_, mvh))) {
+      vh_update.push_back(&meta_mesh_->property(mv_connection_, mvh));
+    }
+  }
+
+  base_mesh_->garbage_collection(vh_update, heh_update, fh_update);
+
+  // Write bsplithandles and next_hehs back into their properties.
+  for (const auto& bvh : base_mesh_->vertices()) {
+    base_mesh_->property(bsplithandle_, bvh) = bsplithandles.at(
+          base_mesh_->property(bsplithandle_temp, bvh));
+    //assert(base_mesh_->is_valid_handle(base_mesh_->property(bsplithandle_, bvh))
+    //       || !base_mesh_->property(bsplithandle_, bvh).is_valid());
+  }
+  for (const auto& bheh : base_mesh_->halfedges()) {
+    base_mesh_->property(next_heh_, bheh) = next_hehs.at(
+          base_mesh_->property(next_heh_temp, bheh));
+    //assert(base_mesh_->is_valid_handle(base_mesh_->property(next_heh_, bheh))
+    //       || !base_mesh_->property(next_heh_, bheh).is_valid());
+  }
+  base_mesh_->remove_property(bsplithandle_temp);
+  base_mesh_->remove_property(next_heh_temp);
+}
+
+/*!
+ * \brief Embedding::ColorizeMetaMesh
+ */
+void Embedding::ColorizeMetaMesh() {
+  /*/ enable this to mark all meta mesh vertices in the base mesh
+  ColorizeMetaMeshVertices();
+  //*/
+  ColorizeMetaEdges();
+  /*/ enable this to mark all voronoi borders in the base mesh
+  ColorizeBorders();
+  //*/
+}
+
+/*!
+ * \brief Embedding::ColorizeMetaMeshVertices
+ */
+void Embedding::ColorizeMetaMeshVertices() {
+  if (!debug_hard_stop_) {
+    for (auto bvh : base_mesh_->vertices()) {
+      base_mesh_->status(bvh).set_selected(false);
+    }
+  }
+  for (auto mvh : meta_mesh_->vertices()) {
+    auto bvh = meta_mesh_->property(mv_connection_, mvh);
+    base_mesh_->status(bvh).set_selected(true);
+  }
+}
+
+/*!
+ * \brief Embedding::ColorizeMetaEdges
+ */
+void Embedding::ColorizeMetaEdges() {
+  //*
+  draw_.clear();
+  auto colorgenerator = new ACG::HaltonColors();
+  auto numcolors = meta_mesh_->n_edges();
+  if (numcolors != 0) {
+    std::vector<ACG::Vec4f> colors;
+    colorgenerator->generateNextNColors(static_cast<int>(numcolors)
+                                        , std::back_inserter(colors));
+    for (auto meh : meta_mesh_->edges()) {
+      //qDebug() << meh.idx() << " < " << numcolors;
+      auto mheh = meta_mesh_->halfedge_handle(meh, 0);
+      auto frompoint = meta_mesh_->point(meta_mesh_->from_vertex_handle(mheh));
+      auto topoint = meta_mesh_->point(meta_mesh_->to_vertex_handle(mheh));
+      draw_.line(frompoint, topoint, colors.at(
+                   static_cast<unsigned long>(meh.idx())));
+    }
+    for (auto beh : base_mesh_->edges()) {
+      auto bheh = base_mesh_->halfedge_handle(beh, 0);
+      auto mheh = base_mesh_->property(bhe_connection_, bheh);
+      if (meta_mesh_->is_valid_handle(mheh)) {
+        auto meh = meta_mesh_->edge_handle(mheh);
+        //qDebug() << meh.idx() << " < " << numcolors;
+        assert(base_mesh_->is_valid_handle(base_mesh_->from_vertex_handle(bheh)));
+        assert(base_mesh_->is_valid_handle(base_mesh_->to_vertex_handle(bheh)));
+        auto frompoint = base_mesh_->point(base_mesh_->from_vertex_handle(bheh));
+        auto topoint = base_mesh_->point(base_mesh_->to_vertex_handle(bheh));
+        draw_.line(frompoint, topoint, colors.at(
+                     static_cast<unsigned long>(meh.idx())));
+      }
+    }
+  }
+  /*/ Alternative: selection instead of colorizing.
+  for (auto bheh : base_mesh_->halfedges()) {
+    base_mesh_->status(bheh).set_selected(false);
+  }
+  for (auto bheh : base_mesh_->halfedges()) {
+    if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))
+      base_mesh_->status(bheh).set_selected(true);
+  }
+  //*/
+}
+
+/*!
+ * \brief Embedding::ColorizeVoronoiRegions
+ */
+void Embedding::ColorizeVoronoiRegions() {
+  auto vertex_colors_pph = base_mesh_->vertex_colors_pph();
+  auto colorgenerator = new ACG::HaltonColors();
+  auto numcolors = meta_mesh_->n_vertices();
+  if (numcolors != 0) {
+    std::vector<ACG::Vec4f> colors;
+    colorgenerator->generateNextNColors(static_cast<int>(numcolors)
+                                        , std::back_inserter(colors));
+    for (auto vh : base_mesh_->vertices()) {
+      auto voronoiregion = base_mesh_->property(voronoiID_, vh).idx();
+      base_mesh_->property(vertex_colors_pph, vh) =
+          colors.at(static_cast<unsigned long>(voronoiregion));
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::ColorizeBorders
+ */
+void Embedding::ColorizeBorders() {
+  /* Comments: Switch the code to these to colorize with haltoncolors instead of selecting.
+  auto halfedge_colors_pph = base_mesh_->halfedge_colors_pph();
+  auto colorgenerator = new ACG::HaltonColors();
+  */
+  auto numcolors = meta_mesh_->n_halfedges();
+  if (numcolors != 0) {
+    /*
+    std::vector<ACG::Vec4f> colors;
+    colorgenerator->generateNextNColors(static_cast<int>(numcolors)
+                                        , std::back_inserter(colors));
+                                        */
+    for (auto heh : base_mesh_->halfedges()) {
+      if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_border_, heh))) {
+        /*
+        auto border = base_mesh_->property(bhe_border_, heh).idx();
+        base_mesh_->property(halfedge_colors_pph, heh) =
+            colors.at(static_cast<unsigned long>(border));
+            */
+        base_mesh_->status(heh).set_selected(true);
+      } else {
+        /*
+        base_mesh_->property(halfedge_colors_pph, heh) =
+            ACG::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
+            */
+        //base_mesh_->status(heh).set_selected(false);
+      }
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::TestHalfedgeConsistency expensive test, will also only work in debug mode.
+ * Could probably be deleted but I'll leave it here just in case.
+ */
+void Embedding::TestHalfedgeConsistency() {
+  for (auto eh : base_mesh_->edges()) {
+    auto heh0 = base_mesh_->halfedge_handle(eh, 0);
+    auto heh1 = base_mesh_->halfedge_handle(eh, 1);
+    bool halfedgeconnectedprop0 =
+        (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, heh0)));
+    bool halfedgeconnectedprop1 =
+        (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, heh1)));
+    assert(halfedgeconnectedprop0 == halfedgeconnectedprop1);
+  }
+}
+
+/*!
+ * \brief Embedding::Trace Most important function: Traces a meta edge into the base mesh
+ * \param mheh the meta edge to be traced
+ * \param cleanup switch: cleanup after tracing or not
+ * \param mark_trace switch: mark the trace or not
+ * \param facetype preprocessedface or not? Current implementation calls the pre-processing
+ * in Trace itself. UNPROCESSEDFACE should call a trace method that does its own base edge
+ * splits where needed: this is not implemented yet.
+ * TODO: Implement Advanced trace or remove the switch.
+ */
+void Embedding::Trace(OpenMesh::HalfedgeHandle mheh, bool cleanup, bool mark_trace,
+                      TraceFaceAttr facetype)  {
+  auto bheh = meta_mesh_->property(mhe_connection_, mheh);
+  if (base_mesh_->is_valid_handle(bheh)
+      && base_mesh_->property(bhe_connection_, bheh) == mheh) {
+    return;
+  }
+
+  if (meta_mesh_->is_boundary(mheh)
+      || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheh))) {
+    BoundaryTrace(mheh);
+  } else {
+    switch (facetype) {
+      case UNPROCESSEDFACE :
+        AdvancedTrace(mheh);
+        break;
+      case PREPROCESSEDFACE :
+        SimpleTrace(mheh, cleanup, mark_trace);
+        break;
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::Retrace
+ * Retraces the given meta halfedge
+ * \param mheh
+ * \param cleanup
+ * \param mark_trace
+ * \param facetype
+ */
+void Embedding::Retrace(OpenMesh::HalfedgeHandle mheh, bool cleanup, bool mark_trace,
+                        TraceFaceAttr facetype) {
+  if (!meta_mesh_->is_valid_handle(mheh)
+      || meta_mesh_->status(mheh).deleted()) {
+    qDebug() << "Invalid or deleted halfedge mheh cannot be retraced";
+    return;
+  }
+  RemoveMetaEdgeFromBaseMesh(mheh);
+  Trace(mheh, cleanup, mark_trace, facetype);
+}
+
+/*!
+ * \brief Embedding::BoundaryTrace Trace a boundary edge. This is a special case but also
+ * much easier since the meta edge must then also lie on the boundary by definition.
+ * \param mheh
+ * \param mark_trace
+ */
+void Embedding::BoundaryTrace(OpenMesh::HalfedgeHandle mheh, bool mark_trace) {
+  //qDebug() << "Tracing boundary halfedge mheh " << mheh.idx();
+  // align the edge along the boundary
+  if (!meta_mesh_->is_boundary(mheh)) {
+    mheh = meta_mesh_->opposite_halfedge_handle(mheh);
+  }
+  assert(meta_mesh_->is_boundary(mheh));
+
+  auto mvh0 = meta_mesh_->from_vertex_handle(mheh);
+  auto mvh1 = meta_mesh_->to_vertex_handle(mheh);
+  auto bvh0 = meta_mesh_->property(mv_connection_, mvh0);
+  auto bvh1 = meta_mesh_->property(mv_connection_, mvh1);
+
+  auto bheh = base_mesh_->halfedge_handle(bvh0);
+  assert(base_mesh_->is_boundary(bheh));
+
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh))) {
+    qDebug() << "Boundary mheh" << mheh.idx() << " is already traced or blocked by "
+                                                 " another edge.";
+    return;
+  }
+
+  if (mark_trace) {
+    base_mesh_->status(bheh).set_selected(true);
+  }
+  std::vector<OpenMesh::HalfedgeHandle> path = {bheh};
+  while (!meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+                                     base_mesh_->to_vertex_handle(bheh)))) {
+    bheh = base_mesh_->next_halfedge_handle(bheh);
+    if (mark_trace) {
+      base_mesh_->status(bheh).set_selected(true);
+    }
+    assert(base_mesh_->is_boundary(bheh));
+    path.push_back(bheh);
+    //base_mesh_->status(bheh).set_selected(true);
+  }
+  auto bvhe = base_mesh_->to_vertex_handle(bheh);
+  assert(bvhe == bvh1);
+
+  InsertPath(mheh, path);
+
+  if (initial_triangulation_) {
+    ProcessEdge(meta_mesh_->edge_handle(mheh));
+  }
+}
+
+/*!
+ * \brief Embedding::SimpleTrace Trace function, traces mheh.
+ * \param mheh
+ * \param cleanup
+ * \param mark_trace
+ */
+void Embedding::SimpleTrace(OpenMesh::HalfedgeHandle mheh, bool cleanup, bool mark_trace) {
+  //qDebug() << "Entering A*";
+  ProcessFace(mheh);
+
+  auto bheh = FindA_StarStartingHalfedges(mheh);
+  auto path = A_StarBidirectional(bheh.first, bheh.second, mark_trace, mheh);
+  if (debug_hard_stop_) {
+    return;
+  }
+  //qDebug() << "Entering Insertpath";
+  if (!path.empty()) {
+    InsertPath(mheh, path);
+  }
+
+  //qDebug() << "Entering ProcessEdge";
+  // post processing during initial triangulation
+  if (initial_triangulation_) {
+    ProcessEdge(meta_mesh_->edge_handle(mheh));
+  } else if (cleanup) {
+    CleanupFace(mheh);
+    CleanupFace(meta_mesh_->opposite_halfedge_handle(mheh));
+  }
+  //base_mesh_->update_normals();
+}
+
+/*!
+ * \brief Embedding::AdvancedTrace
+ * Unimplemented method. This Trace method should handle all neccessary split operations
+ * inside the sector it traces in by itself. I will leave this here since implementing it
+ * used to be an idea, but there was no time left.
+ * TODO: Implement this
+ * \param mheh
+ */
+void Embedding::AdvancedTrace(OpenMesh::HalfedgeHandle mheh) {
+  meta_mesh_->status(mheh).deleted();
+}
+
+/*!
+ * \brief Embedding::A_StarBidirectional Bidirectional A* Algorithm to find the
+ * best path quickly, see these:
+ *  https://en.wikipedia.org/wiki/A*_search_algorithm
+ *  https://www.cs.princeton.edu/courses/archive/spr06/cos423/Handouts/EPP%20shortest%20path%20algorithms.pdf
+ * \param bheh0 Starting halfedge 0 found by FindA_StarStartingHalfedges
+ * \param bheh1 Starting halfedge 1 found by FindA_StarStartingHalfedges
+ * \param mark_trace mark the trace y/n?
+ * \param mheh meta halfedge to be traced
+ * \return a vector of ordered base halfedges representing the mheh. these still
+ * need to be input and their pointers adjusted afterwards.
+ */
+std::vector<OpenMesh::HalfedgeHandle> Embedding::A_StarBidirectional(
+    OpenMesh::HalfedgeHandle bheh0, OpenMesh::HalfedgeHandle bheh1,
+    bool mark_trace, OpenMesh::HalfedgeHandle mheh) {
+  if (mark_trace) {
+    for (auto bheh : base_mesh_->halfedges()) {
+      base_mesh_->status(bheh).set_selected(false);
+    }
+  }
+  auto bvh0 = base_mesh_->from_vertex_handle(bheh0);
+  auto bvh1 = base_mesh_->from_vertex_handle(bheh1);
+
+  const double inf = std::numeric_limits<double>::infinity();
+  double shortest = inf;
+  OpenMesh::VertexHandle middle;
+
+  // Heaps that always display the current vertex with lowest value from direction 0 and 1
+  // Heap data structure allows easy access for the next element needed in the algorithm
+  struct cmp {
+    bool operator()(const std::pair<OpenMesh::VertexHandle, double> &a,
+                    const std::pair<OpenMesh::VertexHandle, double> &b) {
+      return a.second > b.second;
+    }
+  };
+  std::priority_queue<std::pair<OpenMesh::VertexHandle, double>,
+      std::vector<std::pair<OpenMesh::VertexHandle, double>>, cmp> minheap0;
+  std::priority_queue<std::pair<OpenMesh::VertexHandle, double>,
+      std::vector<std::pair<OpenMesh::VertexHandle, double>>, cmp> minheap1;
+
+  OpenMesh::VPropHandleT<double> gscore0;
+  OpenMesh::VPropHandleT<double> gscore1;
+  base_mesh_->add_property(gscore0, "Distance from bvh0");
+  base_mesh_->add_property(gscore1, "Distance from bvh1");
+
+  minheap0.push(std::make_pair(bvh0, A_StarHeuristic(bvh0, bvh1, bvh0)));
+  assert(minheap0.top().second == 0.0);
+  minheap1.push(std::make_pair(bvh1, A_StarHeuristic(bvh1, bvh0, bvh1)));
+  assert(minheap1.top().second == 0.0);
+
+  // Saving the previous vh for each vh operated through; used for path reconstruction
+  OpenMesh::VPropHandleT<OpenMesh::VertexHandle> predecessor0;
+  OpenMesh::VPropHandleT<OpenMesh::VertexHandle> predecessor1;
+  base_mesh_->add_property(predecessor0, "Previous vertex on shortest path");
+  base_mesh_->add_property(predecessor1, "Previous vertex on shortest path");
+
+  OpenMesh::VPropHandleT<bool> closed_set0;
+  base_mesh_->add_property(closed_set0, "Vertices in the first closed set");
+  OpenMesh::VPropHandleT<bool> closed_set1;
+  base_mesh_->add_property(closed_set1, "Vertices in the second closed set");
+  for (auto vh : base_mesh_->vertices()) {
+    base_mesh_->property(closed_set0, vh) = false;
+    base_mesh_->property(closed_set1, vh) = false;
+    base_mesh_->property(predecessor0, vh) = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+    base_mesh_->property(predecessor1, vh) = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+    base_mesh_->property(gscore0, vh) = inf;
+    base_mesh_->property(gscore1, vh) = inf;
+  }
+  base_mesh_->property(gscore0, bvh0) = 0.0;
+  base_mesh_->property(gscore1, bvh1) = 0.0;
+
+
+  // Search while a shortest path hasnt been found and the heaps are not empty
+  while (!minheap0.empty()
+         && !minheap1.empty()
+         && shortest + A_StarHeuristic(bvh0, bvh1, bvh1)
+         > base_mesh_->property(gscore0, minheap0.top().first)
+         + base_mesh_->property(gscore1, minheap1.top().first)) {
+    //qDebug() << "Start of A* Loop";
+    OpenMesh::VertexHandle bvhc;
+    // One side of the search
+    if (minheap0.top().second < minheap1.top().second) {
+      bvhc = minheap0.top().first;
+      //qDebug() << "-Heap Value0: " << minheap0.top().second << " -A_Star_Heuristic0: "
+      //         << A_StarHeuristic(bvh0, bvh1, curr) << " -gScore0: " << gscore0.at(curr);
+      minheap0.pop();
+      // Only deal with vertices not in the closed set yet
+      if (base_mesh_->property(closed_set0, bvhc) == false) {
+        base_mesh_->property(closed_set0, bvhc) = true;
+        // Iterate over the neighboring vertices
+        auto neighbors0 = A_StarNeighbors(bvhc, mheh, closed_set0, bvh1, bheh0);
+        //if (neighbors0.empty())
+        //  qDebug() << "Empty A* Neighborhood0.";
+        for (auto bvhn : neighbors0) {
+          // If a better gscore is found set it for neighbors
+          auto tentative_gScore = inf;
+          if (!base_mesh_->is_valid_handle(base_mesh_->find_halfedge(bvhc, bvhn))) {
+            qDebug() << "Invalid seed halfedge bheh0";
+          } else {
+            //base_mesh_->status(base_mesh_->find_halfedge(curr, vh)).set_selected(true);
+            auto bhehw = base_mesh_->find_halfedge(bvhc, bvhn);
+            // use reduced cost (modified graph)
+            double edge_weight = base_mesh_->calc_edge_length(bhehw);
+            // Increase the weight of non-original halfedges
+            edge_weight *= base_mesh_->property(halfedge_weight_, bhehw);
+            tentative_gScore = base_mesh_->property(gscore0, bvhc) + edge_weight;
+          }
+          //qDebug() << "n_vertices: " << base_mesh_->n_vertices() << " - vh.idx(): "
+          //         << vh.idx();
+          if (base_mesh_->property(gscore0, bvhn) > tentative_gScore) {
+            base_mesh_->property(gscore0, bvhn) = tentative_gScore;
+            base_mesh_->property(predecessor0, bvhn) = bvhc;
+
+            // Push the new neighbors into the heap adding the heuristic value for speed
+            // The same value may be added twice but since a minheap is used and only the
+            // first time it comes through it will not be in the closed set yet this is ok
+            minheap0.push(std::make_pair(bvhn, tentative_gScore + A_StarHeuristic(bvh0, bvh1, bvhn)));
+            if (mark_trace) {
+              base_mesh_->status(base_mesh_->find_halfedge(bvhc, bvhn)).set_selected(true);
+            }
+          }
+        }
+        // Check if a connection with the other side has been made, if so check if it is minimal
+        if (bvhc != base_mesh_->from_vertex_handle(bheh0) &&
+            base_mesh_->property(gscore1, bvhc) < inf) {
+          auto newdist = base_mesh_->property(gscore1, bvhc)
+              + base_mesh_->property(gscore0, bvhc);
+          if (shortest > newdist) {
+            shortest = newdist;
+            middle = bvhc;
+            //qDebug() << "found new shortest path of length: " << shortest;
+          }
+        }
+      }
+    // Other side of the search
+    } else {
+      bvhc = minheap1.top().first;
+      //qDebug() << "-Heap Value1: " << minheap1.top().second << " -A_Star_Heuristic1: "
+      //         << A_StarHeuristic(bvh1, bvh0, curr) << " -gScore1: " << gscore1.at(curr);
+      minheap1.pop();
+      // Only deal with vertices not in the closed set yet
+      if (base_mesh_->property(closed_set1, bvhc) == false) {
+        base_mesh_->property(closed_set1, bvhc) = true;
+        // Iterate over the neighboring vertices
+        auto neighbors1 = A_StarNeighbors(bvhc, mheh, closed_set1, bvh0, bheh1);
+        //if (neighbors1.empty())
+        //  qDebug() << "Empty A* Neighborhood1.";
+        for (auto bvhn : neighbors1) {
+          // If a better gscore is found set it for neighbors
+          auto tentative_gScore = inf;
+          if (!base_mesh_->is_valid_handle(base_mesh_->find_halfedge(bvhc, bvhn))) {
+            qDebug() << "Invalid seed halfedge bheh1";
+          } else {
+            //base_mesh_->status(base_mesh_->find_halfedge(curr, vh)).set_selected(true);
+            auto bhehw = base_mesh_->find_halfedge(bvhc, bvhn);
+            // use reduced cost (modified graph)
+            double edge_weight = base_mesh_->calc_edge_length(bhehw);
+            // Increase the weight of non-original halfedges
+            edge_weight *= base_mesh_->property(halfedge_weight_, bhehw);
+            tentative_gScore = base_mesh_->property(gscore1, bvhc) + edge_weight;
+          }
+          //qDebug() << "n_vertices: " << base_mesh_->n_vertices() << " - vh.idx(): "
+          //         << vh.idx();
+          if (base_mesh_->property(gscore1, bvhn) > tentative_gScore) {
+            base_mesh_->property(gscore1, bvhn) = tentative_gScore;
+            base_mesh_->property(predecessor1, bvhn) = bvhc;
+
+            // Push the new neighbors into the heap adding the heuristic value for speed
+            // The same value may be added twice but since a minheap is used and only the
+            // first time it comes through it will not be in the closed set yet this is ok
+            minheap1.push(std::make_pair(bvhn, tentative_gScore + A_StarHeuristic(bvh1, bvh0, bvhn)));
+            if (mark_trace) {
+              base_mesh_->status(base_mesh_->find_halfedge(bvhc, bvhn)).set_selected(true);
+            }
+          }
+        }
+        // Check if a connection with the other side has been made, if so check if it is minimal
+        if (bvhc != base_mesh_->from_vertex_handle(bheh1) &&
+            base_mesh_->property(gscore0, bvhc) < inf) {
+          auto newdist = base_mesh_->property(gscore1, bvhc)
+              + base_mesh_->property(gscore0, bvhc);
+          if (shortest > newdist) {
+            shortest = newdist;
+            middle = bvhc;
+            //qDebug() << "found new shortest path of length: " << shortest;
+          }
+        }
+      }
+    }
+  }
+  //qDebug() << "Finished A* Loop";
+  if (!base_mesh_->is_valid_handle(middle)) {
+    debug_hard_stop_=true;
+    if (!mark_trace) {
+      qWarning() << "A* Tracing failed for vertices " << bvh0.idx() << " and "
+                 << bvh1.idx() << " retracing to mark.";
+      A_StarBidirectional(bheh0, bheh1, true, mheh);
+      qDebug() << "Marking the closed set";
+      for (auto bvh : base_mesh_->vertices()) {
+        if (base_mesh_->property(closed_set0, bvh) ||
+            base_mesh_->property(closed_set1, bvh)) {
+          base_mesh_->status(bvh).set_selected(true);
+        }
+      }
+      qDebug() << "Marking bheh0 " << bheh0.idx() << "and bheh1 " << bheh1.idx()
+               << "they are opposites: "
+               << (bheh0 == base_mesh_->opposite_halfedge_handle(bheh1));
+      base_mesh_->status(bheh0).set_selected(true);
+      base_mesh_->status(bheh1).set_selected(true);
+    } else {
+      MarkVertex(bvh0);
+      MarkVertex(bvh1);
+    }
+  }
+  base_mesh_->remove_property(closed_set0);
+  base_mesh_->remove_property(closed_set1);
+  base_mesh_->remove_property(gscore0);
+  base_mesh_->remove_property(gscore1);
+  return A_StarReconstructPath(middle, predecessor0, predecessor1);
+}
+
+/*!
+ * \brief Embedding::Distance
+ * \param bvhf
+ * \param bvhv
+ * \return direct spatial distance between vertices bvhf and bvhv
+ */
+double Embedding::Distance(OpenMesh::VertexHandle bvhf, OpenMesh::VertexHandle bvhv) {
+  auto pf = base_mesh_->point(bvhf);
+  auto pv = base_mesh_->point(bvhv);
+  double distfv = std::sqrt((pf[0]-pv[0])*(pf[0]-pv[0])
+      +(pf[1]-pv[1])*(pf[1]-pv[1])
+      +(pf[2]-pv[2])*(pf[2]-pv[2]));
+  return distfv;
+}
+
+/*!
+ * \brief Embedding::A_StarHeuristic The heuristic used in A* here, detailed explanation:
+ * https://www.cs.princeton.edu/courses/archive/spr06/cos423/Handouts/EPP%20shortest%20path%20algorithms.pdf
+ * Averaging the distance heuristic from both sides makes it admissible and feasible
+ * Variables corresponding to page 4, Bidirectional A* Search
+ * p_f(v): 1/2(pi_f(v)-pi_r(v)) + (pi_r(t)/2)
+ * Distance(bvhf, bvhv) : pi_f(v)
+ * Distance(bvhr, bvhv) : pi_r(v)
+ * Distance(bvhr, bvhf) : pi_r(t)
+ * Then call it with flipped bvhf and bvhr for the opposite direction p_r(v).
+ * p_r(v): 1/2(pi_r(v)-pi_f(v)) + (pi_r(s)/2)
+ * \param bvhf
+ * \param bvhr
+ * \param bvhv
+ * \return heuristical values to sort the heaps by
+ */
+double Embedding::A_StarHeuristic(OpenMesh::VertexHandle bvhf,
+                                   OpenMesh::VertexHandle bvhr,
+                                   OpenMesh::VertexHandle bvhv) {
+  return 0.5 * (Distance(bvhf, bvhv) - Distance(bvhr, bvhv))
+      + (Distance(bvhr, bvhf) / 2);
+}
+
+/*!
+ * \brief Embedding::A_StarNeighbors
+ * \param bvh
+ * \param mheh
+ * \param closed_set
+ * \param towardsbvh
+ * \param bheh
+ * \return the VV_Neighborhood excluding vertices already closed by A* and border vertices
+ */
+std::vector<OpenMesh::VertexHandle> Embedding::A_StarNeighbors(OpenMesh::VertexHandle bvh,
+               OpenMesh::HalfedgeHandle mheh,
+               OpenMesh::VPropHandleT<bool> closed_set,
+               OpenMesh::VertexHandle towardsbvh,
+               OpenMesh::HalfedgeHandle bheh) {
+  std::vector<OpenMesh::VertexHandle> neighbors;
+  if (bvh == towardsbvh && base_mesh_->from_vertex_handle(bheh) != bvh) {
+    return neighbors;
+  }
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh))) {
+    // Ensure that the seed halfedge originates from the initial vertex
+    assert(base_mesh_->from_vertex_handle(bheh) == bvh);
+    // Ensure that the seed halfedge is empty
+    assert(!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bheh)));
+    auto bhehiter = bheh;
+    do {
+      if (!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bhehiter))
+          && (!IsSectorBorder(base_mesh_->to_vertex_handle(bhehiter))
+              || base_mesh_->to_vertex_handle(bhehiter) == towardsbvh)) {
+        neighbors.push_back(base_mesh_->to_vertex_handle(bhehiter));
+      }
+      bhehiter = base_mesh_->next_halfedge_handle(
+            base_mesh_->opposite_halfedge_handle(bhehiter));
+    } while (!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bhehiter))
+             && bhehiter != bheh);
+  } else {
+    for (auto voh_it: base_mesh_->voh_range(bvh)) {
+      auto vv_it = base_mesh_->to_vertex_handle(voh_it);
+      if ((((base_mesh_->property(closed_set, vv_it) == false)
+          && !IsSectorBorder(vv_it)))
+          && ValidA_StarEdge(voh_it, mheh)) {
+        neighbors.push_back(vv_it);
+      }
+    }
+  }
+
+  return neighbors;
+}
+
+/*!
+ * \brief Embedding::ValidA_StarEdge
+ * \param bheh
+ * \param mheh
+ * \return whether bheh is a Valid edge for A* traversal based on the bhe_border_
+ * properties. These properties describe voronoi regions, so this method is only
+ * really used during initial triangulation to ensure traces don't block each other.
+ */
+bool Embedding::ValidA_StarEdge(OpenMesh::HalfedgeHandle bheh,
+                                OpenMesh::HalfedgeHandle mheh) {
+  if (!initial_triangulation_) {
+    return true;
+  }
+  return (!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_border_, bheh))
+          || (base_mesh_->property(bhe_border_, bheh) == mheh)
+          || (base_mesh_->property(bhe_border_, bheh) ==
+              meta_mesh_->opposite_halfedge_handle(mheh)));
+}
+
+/*!
+ * \brief Embedding::A_StarReconstructPath reconstructs a meta halfedge based on the output
+ * of the A_StarBidirectional function
+ * \param middle the middle halfedge of mheh
+ * \param predecessor0 predecessor property in one direction from the middle
+ * \param predecessor1 predecessor property in the other direction from the middle
+ * \return An ordered vector of halfedges representing the meta edge that was traced
+ */
+std::vector<OpenMesh::HalfedgeHandle> Embedding::A_StarReconstructPath(
+    OpenMesh::VertexHandle middle,
+    OpenMesh::VPropHandleT<OpenMesh::VertexHandle> predecessor0,
+    OpenMesh::VPropHandleT<OpenMesh::VertexHandle> predecessor1)  {
+  std::list<OpenMesh::VertexHandle> total_path;
+  std::vector<OpenMesh::HalfedgeHandle> returnpath;
+  if (!base_mesh_->is_valid_handle(middle)) {
+    // qDebug() << "A_StarReconstructPath returning empty path";
+    return returnpath;
+  }
+  total_path.push_back(middle);
+  auto curr = base_mesh_->property(predecessor0, middle);
+  while (base_mesh_->is_valid_handle(curr)) {
+    total_path.push_front(curr);
+    curr = base_mesh_->property(predecessor0, curr);
+  }
+  curr = middle;
+  curr = base_mesh_->property(predecessor1, middle);
+  while (base_mesh_->is_valid_handle(curr)) {
+    total_path.push_back(curr);
+    curr = base_mesh_->property(predecessor1, curr);
+  }
+  OpenMesh::VertexHandle vh0 = total_path.front();
+  OpenMesh::VertexHandle vh1;
+  total_path.pop_front();
+  while (!total_path.empty()) {
+    vh1 = vh0;
+    vh0 = total_path.front();
+    auto heh = base_mesh_->find_halfedge(vh1, vh0);
+    returnpath.push_back(heh);
+    total_path.pop_front();
+  }
+  base_mesh_->remove_property(predecessor0);
+  base_mesh_->remove_property(predecessor1);
+  return returnpath;
+}
+
+/*!
+ * \brief Embedding::IsSectorBorder
+ * \param bvh
+ * \param exceptions
+ * \return true if bvh lies on a meta_mesh element (vertex or edge) which is NOT
+ * included in the list of exception
+ */
+bool Embedding::IsSectorBorder(OpenMesh::VertexHandle bvh,
+                               std::list<OpenMesh::HalfedgeHandle> exceptions) {
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh))) {
+    if (!exceptions.empty() && meta_mesh_->to_vertex_handle(exceptions.front()) ==
+        base_mesh_->property(bv_connection_, bvh)) {
+      return false;
+    }
+    return true;
+  }
+  for (auto voh_it : base_mesh_->voh_range(bvh)) {
+    auto mhehprop = base_mesh_->property(bhe_connection_, voh_it);
+    if (meta_mesh_->is_valid_handle(mhehprop)) {
+      for (auto mheh : exceptions) {
+        if (mheh == mhehprop) {
+          return false;
+        }
+        if (meta_mesh_->opposite_halfedge_handle(mheh) == mhehprop) {
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+/*!
+ * \brief Embedding::InsertPath Inserts a list of base halfedges saved in path into the mesh
+ * as meta halfedge mheh (called after A* triangulation)
+ * \param mheh
+ * \param path
+ */
+void Embedding::InsertPath(OpenMesh::HalfedgeHandle mheh,
+                           std::vector<OpenMesh::HalfedgeHandle> path) {
+  if (path.empty()) {
+    qDebug() << "Couldn't insert empty path";
+  } else {
+    auto opp = meta_mesh_->opposite_halfedge_handle(mheh);
+    meta_mesh_->property(mhe_connection_, mheh) = path.front();
+    meta_mesh_->property(mhe_connection_, opp) =
+        base_mesh_->opposite_halfedge_handle(path.back());
+    auto prev = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    while (!path.empty()) {
+      auto curr = path.back();
+      base_mesh_->property(next_heh_, curr) = prev;
+      base_mesh_->property(bhe_connection_, curr) = mheh;
+      base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(curr)) = opp;
+      path.pop_back();
+      if (base_mesh_->is_valid_handle(prev)) {
+        base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(prev))
+            = base_mesh_->opposite_halfedge_handle(curr);
+      }
+      prev = curr;
+    }
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(prev))
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+}
+
+/*!
+ * \brief Embedding::FindA_StarStartingHalfedges  Find A* Starting halfedges through a series
+ * of halfedge pointer operations. This is more complicated than it first sounds because it has
+ * to handle an initial triangulation where edges are traced in an arbitrary order and some edges
+ * may already be traced while others arent. Traces starting in the wrong sectors will fail so
+ * the starting halfedges need to be right.
+ * \param mheh the halfedge to be traced.
+ * \param verbose
+ * \return Two starting halfedges for use in A* tracing.
+ */
+std::pair<OpenMesh::HalfedgeHandle, OpenMesh::HalfedgeHandle>
+Embedding::FindA_StarStartingHalfedges(OpenMesh::HalfedgeHandle mheh, bool verbose) {
+  std::pair<OpenMesh::HalfedgeHandle, OpenMesh::HalfedgeHandle> output;
+  //ProcessNeighbors(meta_mesh_->edge_handle(mheh));
+  auto mvh0 = meta_mesh_->from_vertex_handle(mheh);
+  auto mvh1 = meta_mesh_->to_vertex_handle(mheh);
+  output.first = base_mesh_->halfedge_handle(meta_mesh_->property(mv_connection_, mvh0));
+  output.second = base_mesh_->halfedge_handle(meta_mesh_->property(mv_connection_, mvh1));
+  assert(mvh0 == base_mesh_->property(bv_connection_,
+                                      base_mesh_->from_vertex_handle(output.first)));
+  assert(mvh1 == base_mesh_->property(bv_connection_,
+                                      base_mesh_->from_vertex_handle(output.second)));
+  auto iter = meta_mesh_->opposite_halfedge_handle(meta_mesh_->prev_halfedge_handle(mheh));
+  while (iter != mheh) {
+    assert(meta_mesh_->from_vertex_handle(iter) == mvh0);
+    if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, iter))) {
+      if (base_mesh_->property(bhe_connection_, (meta_mesh_->property(mhe_connection_, iter)))
+          == iter) {
+        output.first = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(
+                                      meta_mesh_->property(mhe_connection_, iter)));
+        assert(mvh0 == base_mesh_->property(bv_connection_,
+                                            base_mesh_->from_vertex_handle(output.first)));
+        goto endloop1;
+      }
+    }
+    auto bvhx = meta_mesh_->property(mv_connection_, meta_mesh_->to_vertex_handle(iter));
+    for (auto bhehiter : base_mesh_->voh_range(meta_mesh_->property(mv_connection_,
+                                              meta_mesh_->from_vertex_handle(iter)))) {
+      if (base_mesh_->to_vertex_handle(bhehiter) == bvhx) {
+        output.first = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(
+                                      bhehiter));
+        assert(mvh0 == base_mesh_->property(bv_connection_,
+                                            base_mesh_->from_vertex_handle(output.first)));
+        goto endloop1;
+      }
+    }
+    iter = meta_mesh_->opposite_halfedge_handle(meta_mesh_->prev_halfedge_handle(iter));
+  }
+  endloop1:
+
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, output.first)) ||
+      (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+          base_mesh_->to_vertex_handle(output.first)))
+       && base_mesh_->property(bv_connection_, base_mesh_->to_vertex_handle(output.first))
+          != mvh1)) {
+    SplitBaseHe(base_mesh_->next_halfedge_handle(output.first));
+    output.first = base_mesh_->opposite_halfedge_handle(
+          base_mesh_->prev_halfedge_handle(output.first));
+    assert(mvh0 == base_mesh_->property(bv_connection_,
+                                        base_mesh_->from_vertex_handle(output.first)));
+  }
+
+  auto opp = meta_mesh_->opposite_halfedge_handle(mheh);
+  iter = meta_mesh_->opposite_halfedge_handle(meta_mesh_->prev_halfedge_handle(opp));
+  while (iter != opp) {
+    assert(meta_mesh_->from_vertex_handle(iter) == mvh1);
+    if (base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, iter))) {
+      if (base_mesh_->property(bhe_connection_, (meta_mesh_->property(mhe_connection_, iter)))
+          == iter) {
+        output.second = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(
+                                      meta_mesh_->property(mhe_connection_, iter)));
+        goto endloop2;
+      }
+    }
+    auto bvhx = meta_mesh_->property(mv_connection_, meta_mesh_->to_vertex_handle(iter));
+    for (auto bhehiter : base_mesh_->voh_range(meta_mesh_->property(mv_connection_,
+                                              meta_mesh_->from_vertex_handle(iter)))) {
+      if (base_mesh_->to_vertex_handle(bhehiter) == bvhx) {
+        output.second = base_mesh_->next_halfedge_handle(base_mesh_->opposite_halfedge_handle(
+                                      bhehiter));
+        goto endloop2;
+      }
+    }
+    iter = meta_mesh_->opposite_halfedge_handle(meta_mesh_->prev_halfedge_handle(iter));
+  }
+  endloop2:
+
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, output.second)) ||
+      (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+          base_mesh_->to_vertex_handle(output.second)))
+       && base_mesh_->property(bv_connection_, base_mesh_->to_vertex_handle(output.second))
+          != mvh0)) {
+    assert(mvh0 == base_mesh_->property(bv_connection_,
+                                        base_mesh_->from_vertex_handle(output.first)));
+    SplitBaseHe(base_mesh_->next_halfedge_handle(output.second));
+    assert(mvh0 == base_mesh_->property(bv_connection_,
+                                        base_mesh_->from_vertex_handle(output.first)));
+    output.second = base_mesh_->opposite_halfedge_handle(
+          base_mesh_->prev_halfedge_handle(output.second));
+  }
+
+  // Make sure the chosen starting halfedges start at the given vertices and are still empty.
+  assert(base_mesh_->is_valid_handle(output.first));
+  assert(base_mesh_->is_valid_handle(output.second));
+  assert(!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, output.first)));
+  assert(!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, output.second)));
+  auto bvcp1 = base_mesh_->property(bv_connection_, base_mesh_->from_vertex_handle(output.first));
+  auto bvcp2 = base_mesh_->property(bv_connection_, base_mesh_->from_vertex_handle(output.second));
+  assert(bvcp1 ==  mvh0);
+  assert(bvcp2 ==  mvh1);
+
+  if (verbose
+      && meta_mesh_->from_vertex_handle(mheh) == meta_mesh_->to_vertex_handle(mheh)) {
+    qDebug() << "Trace from mvertex " << meta_mesh_->from_vertex_handle(mheh).idx()
+             << " to itself using starting halfedges " << output.first.idx()
+             << " and " << output.second.idx();
+  }
+
+  return output;
+}
+
+/*!
+ * \brief Embedding::MarkVertex Marks a meta vertex in black while coloring its
+ * vv_neighborhood white, making it easy to see for visual debugging.
+ * \param bvh
+ */
+void Embedding::MarkVertex(OpenMesh::VertexHandle bvh) {
+  qDebug() << "Marking vertex: " << bvh.idx() << "in white.";
+  auto vertex_colors_pph = base_mesh_->vertex_colors_pph();
+  base_mesh_->property(vertex_colors_pph, bvh) = ACG::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
+  for (auto vvh : base_mesh_->vv_range(bvh)) {
+    if (base_mesh_->property(vertex_colors_pph, vvh) != ACG::Vec4f(0.0f, 0.0f, 0.0f, 1.0f)) {
+      base_mesh_->property(vertex_colors_pph, vvh) = ACG::Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::AddMetaEdge Adds a meta edge (it will still need to be traced
+ * into the base mesh)
+ * \param mheh0 the previous edge of mheh
+ * \param mheh1 the previous edge of the opposite edge of mheh
+ * \return a halfedge of the new edge, specifically the next halfedge of mheh0
+ */
+OpenMesh::HalfedgeHandle Embedding::AddMetaEdge(OpenMesh::HalfedgeHandle mheh0,
+                                                OpenMesh::HalfedgeHandle mheh1) {
+  auto mhehnew0 = meta_mesh_->new_edge(meta_mesh_->to_vertex_handle(mheh0),
+                                      meta_mesh_->to_vertex_handle(mheh1));
+  auto mhehnew1 = meta_mesh_->opposite_halfedge_handle(mhehnew0);
+  auto fnew0 = meta_mesh_->face_handle(mheh0);
+  auto fnew1(meta_mesh_->new_face());
+  auto next0 = meta_mesh_->next_halfedge_handle(mheh0);
+  auto next1 = meta_mesh_->next_halfedge_handle(mheh1);
+  auto mvh0 = meta_mesh_->to_vertex_handle(mheh0);
+  auto mvh1 = meta_mesh_->to_vertex_handle(mheh1);
+
+  meta_mesh_->set_next_halfedge_handle(mheh0, mhehnew0);
+  meta_mesh_->set_next_halfedge_handle(mheh1, mhehnew1);
+  meta_mesh_->set_next_halfedge_handle(mhehnew0, next1);
+  meta_mesh_->set_next_halfedge_handle(mhehnew1, next0);
+
+  meta_mesh_->set_vertex_handle(mhehnew0, mvh1);
+  meta_mesh_->set_vertex_handle(mhehnew1, mvh0);
+
+  meta_mesh_->set_halfedge_handle(fnew0, mhehnew0);
+  meta_mesh_->set_halfedge_handle(fnew1, mhehnew1);
+
+  meta_mesh_->set_face_handle(mhehnew0, fnew0);
+  meta_mesh_->set_face_handle(mhehnew1, fnew1);
+
+  auto iter = meta_mesh_->next_halfedge_handle(mhehnew0);
+  while (iter != mhehnew0) {
+    meta_mesh_->set_face_handle(iter, fnew0);
+    iter = meta_mesh_->next_halfedge_handle(iter);
+  }
+  iter = meta_mesh_->next_halfedge_handle(mhehnew1);
+  while (iter != mhehnew1) {
+    meta_mesh_->set_face_handle(iter, fnew1);
+    iter = meta_mesh_->next_halfedge_handle(iter);
+  }
+
+  // Properties
+  meta_mesh_->property(mhe_connection_, mhehnew0) =
+      OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  meta_mesh_->property(mhe_connection_, mhehnew1) =
+      OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  meta_mesh_->property(mhe_border_, mhehnew0) =
+      OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  meta_mesh_->property(mhe_border_, mhehnew1) =
+      OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+
+  return mhehnew0;
+}
+
+/*!
+ * \brief Embedding::Rotate
+ * \param meh
+ */
+void Embedding::Rotate(OpenMesh::EdgeHandle meh) {
+  if (!IsRotationOkay(meh)) {
+    qDebug() << "Meta edge " << meh.idx() << " cannot be rotated";
+  } else {
+    MetaRotation(meh);
+    auto mheh = meta_mesh_->halfedge_handle(meh, 0);
+    RemoveMetaEdgeFromBaseMesh(mheh);
+    Trace(mheh, true, false);
+  }
+}
+
+/*!
+ * \brief Embedding::IsRotationOkay
+ * \param meh
+ * \return whether meh can be rotateds
+ */
+bool Embedding::IsRotationOkay(OpenMesh::EdgeHandle meh) {
+  auto mheh0 = meta_mesh_->halfedge_handle(meh, 0);
+  auto mheh1 = meta_mesh_->halfedge_handle(meh, 1);
+
+  // existence check
+  if (!meta_mesh_->is_valid_handle(meh)
+      || !meta_mesh_->is_valid_handle(mheh0)
+      || !meta_mesh_->is_valid_handle(mheh1)
+      || !base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mheh0))
+      || !base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mheh1))) {
+    return false;
+  }
+
+  // boundary check
+  if (meta_mesh_->is_boundary(meta_mesh_->halfedge_handle(meh, 0))
+      || meta_mesh_->is_boundary(meta_mesh_->halfedge_handle(meh, 1))) {
+    return false;
+  }
+
+  // loop check
+  return (meta_mesh_->next_halfedge_handle(mheh0) != mheh1
+          && meta_mesh_->next_halfedge_handle(mheh1) != mheh0);
+}
+
+// Flips an edge in the meta mesh, but not in the underlying base mesh
+void Embedding::MetaRotation(OpenMesh::EdgeHandle meh) {
+  //qDebug() << "MetaFlip function called";
+  assert(IsRotationOkay(meh));
+  auto mheh0 = meta_mesh_->halfedge_handle(meh, 0);
+  auto mheh1 = meta_mesh_->halfedge_handle(meh, 1);
+  auto mhehprev0 = meta_mesh_->prev_halfedge_handle(mheh0);
+  auto mhehprev1 = meta_mesh_->prev_halfedge_handle(mheh1);
+  auto mhehnext0 = meta_mesh_->next_halfedge_handle(mheh0);
+  auto mhehnext1 = meta_mesh_->next_halfedge_handle(mheh1);
+  auto mhehnextnext0 = meta_mesh_->next_halfedge_handle(mhehnext0);
+  auto mhehnextnext1 = meta_mesh_->next_halfedge_handle(mhehnext1);
+  auto fh0 = meta_mesh_->face_handle(mheh0);
+  auto fh1 = meta_mesh_->face_handle(mheh1);
+  auto mvh0 = meta_mesh_->to_vertex_handle(mheh0);
+  auto mvh1 = meta_mesh_->to_vertex_handle(mheh1);
+  auto mvh01 = meta_mesh_->to_vertex_handle(mhehnext0);
+  auto mvh11 = meta_mesh_->to_vertex_handle(mhehnext1);
+
+  // Flip next_halfedge connections
+  meta_mesh_->set_next_halfedge_handle(mhehprev0, mhehnext1);
+  meta_mesh_->set_next_halfedge_handle(mhehprev1, mhehnext0);
+  meta_mesh_->set_next_halfedge_handle(mhehnext0, mheh0);
+  meta_mesh_->set_next_halfedge_handle(mhehnext1, mheh1);
+  meta_mesh_->set_next_halfedge_handle(mheh0, mhehnextnext1);
+  meta_mesh_->set_next_halfedge_handle(mheh1, mhehnextnext0);
+
+  // Ensure correct face->heh connections
+  meta_mesh_->set_halfedge_handle(fh0, mheh0);
+  meta_mesh_->set_halfedge_handle(fh1, mheh1);
+
+  // Ensure correct heh->face connections
+  meta_mesh_->set_face_handle(mhehprev1, fh0);
+  meta_mesh_->set_face_handle(mhehprev0, fh1);
+  meta_mesh_->set_face_handle(mhehnext0, fh0);
+  meta_mesh_->set_face_handle(mhehnext1, fh1);
+
+  // Ensure correct heh->vertex connections
+  meta_mesh_->set_vertex_handle(mheh0, mvh11);
+  meta_mesh_->set_vertex_handle(mheh1, mvh01);
+
+  // Ensure correct vertex->heh connections while preserving boundary rule
+  if (meta_mesh_->halfedge_handle(mvh0) == mheh1) {
+    meta_mesh_->set_halfedge_handle(mvh0, mhehnext0);
+  }
+  if (meta_mesh_->halfedge_handle(mvh1) == mheh0) {
+    meta_mesh_->set_halfedge_handle(mvh1, mhehnext1);
+  }
+
+  // Check for correctness
+  auto mheh03 = meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(
+                                                 meta_mesh_->next_halfedge_handle(mheh0)));
+  auto mheh13 = meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(
+                                                 meta_mesh_->next_halfedge_handle(mheh1)));
+  assert(mheh0 == mheh03);
+  assert(mheh1 == mheh13);
+  assert(meta_mesh_->from_vertex_handle(mheh0) == meta_mesh_->to_vertex_handle(mhehnext0));
+  assert(meta_mesh_->from_vertex_handle(mheh1) == meta_mesh_->to_vertex_handle(mhehnext1));
+  assert(meta_mesh_->to_vertex_handle(mheh0) == meta_mesh_->to_vertex_handle(mhehnext1));
+  assert(meta_mesh_->to_vertex_handle(mheh1) == meta_mesh_->to_vertex_handle(mhehnext0));
+}
+
+/*!
+ * \brief Embedding::RemoveMetaEdgeFromBaseMesh
+ * \param mheh meta halfedge to be removed from the basemesh (it stays in the meta mesh)
+ * \param cleanup
+ */
+void Embedding::RemoveMetaEdgeFromBaseMesh(OpenMesh::HalfedgeHandle mheh, bool cleanup) {
+  assert(meta_mesh_->is_valid_handle(mheh));
+  assert(base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mheh)));
+  auto halfedges = GetBaseHalfedges(mheh);
+  assert(base_mesh_->property(bhe_connection_, halfedges.front()) == mheh);
+  auto mheho = meta_mesh_->opposite_halfedge_handle(mheh);
+  assert(base_mesh_->property(bhe_connection_,
+         base_mesh_->opposite_halfedge_handle(halfedges.back())) == mheho);
+  meta_mesh_->property(mhe_connection_, mheh)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  meta_mesh_->property(mhe_connection_, mheho)
+      = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+
+  for (auto bheh : halfedges) {
+    base_mesh_->property(bhe_connection_, bheh)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(bheh))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, bheh)
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(bheh))
+        = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+  }
+
+  // Cleanup
+  if (cleanup) {
+    for (auto bheh : halfedges) {
+      std::queue<OpenMesh::VertexHandle> cleanupqueue;
+      for (auto bvhit : base_mesh_->vv_range(base_mesh_->to_vertex_handle(bheh))) {
+        if (!base_mesh_->status(bvhit).deleted()
+            && base_mesh_->is_valid_handle(base_mesh_->property(bsplithandle_, bvhit))) {
+          cleanupqueue.push(bvhit);
+        }
+      }
+      while (!cleanupqueue.empty()) {
+        if (base_mesh_->is_valid_handle(cleanupqueue.front())
+            && !base_mesh_->status(cleanupqueue.front()).deleted()) {
+          ConditionalCollapse(cleanupqueue.front());
+        }
+        cleanupqueue.pop();
+      }
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::RemoveMetaEdgeFromMetaMesh
+ * \param mheh meta halfedge to be removed, from the meta mesh only (stays in the base mesh
+ * if not removed from there first which it usually should be)
+ */
+void Embedding::RemoveMetaEdgeFromMetaMesh(OpenMesh::HalfedgeHandle mheh) {
+  auto mheho = meta_mesh_->opposite_halfedge_handle(mheh);
+  auto mhehn = meta_mesh_->next_halfedge_handle(mheh);
+  auto mhehp = meta_mesh_->prev_halfedge_handle(mheh);
+  auto mhehon = meta_mesh_->next_halfedge_handle(mheho);
+  auto mhehop = meta_mesh_->prev_halfedge_handle(mheho);
+  auto mvh0 = meta_mesh_->from_vertex_handle(mheh);
+  auto mvh1 = meta_mesh_->to_vertex_handle(mheh);
+  auto mfh0 = meta_mesh_->face_handle(mheh);
+  auto mfh1 = meta_mesh_->face_handle(mheho);
+
+  // back
+  if (mhehp != mheho) {
+    // he - he
+    meta_mesh_->set_next_halfedge_handle(mhehp, mhehon);
+    // v - he
+    if (meta_mesh_->halfedge_handle(mvh0) == mheh) {
+      meta_mesh_->set_halfedge_handle(mvh0, mhehon);
+    }
+  } else {
+    assert(base_mesh_->is_valid_handle(meta_mesh_->property(mv_connection_, mvh0)));
+    base_mesh_->property(bv_connection_, meta_mesh_->property(mv_connection_, mvh0))
+        = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+    meta_mesh_->set_isolated(mvh0);
+    meta_mesh_->status(mvh0).set_deleted(true);
+  }
+  // front
+  if (mhehn != mheho) {
+    // he - he
+    meta_mesh_->set_next_halfedge_handle(mhehop, mhehn);
+    // v - he
+    if (meta_mesh_->halfedge_handle(mvh1) == mheho) {
+      meta_mesh_->set_halfedge_handle(mvh1, mhehn);
+    }
+  } else {
+    assert(base_mesh_->is_valid_handle(meta_mesh_->property(mv_connection_, mvh1)));
+    base_mesh_->property(bv_connection_, meta_mesh_->property(mv_connection_, mvh1))
+        = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+    meta_mesh_->set_isolated(mvh1);
+    meta_mesh_->status(mvh1).set_deleted(true);
+  }
+  if (meta_mesh_->is_valid_handle(mfh0)) {
+    // f - he
+    meta_mesh_->set_halfedge_handle(mfh0, mhehn);
+    // he - f
+    auto mhehiter = mhehn;
+    do {
+      meta_mesh_->set_face_handle(mhehiter, mfh0);
+      mhehiter = meta_mesh_->next_halfedge_handle(mhehiter);
+    } while (mhehn != mhehiter);
+  }
+  // delete stuff
+  if (meta_mesh_->is_valid_handle(mfh1) && mfh1 != mfh0) {
+    meta_mesh_->status(mfh1).set_deleted(true);
+  }
+  meta_mesh_->status(meta_mesh_->edge_handle(mheh)).set_deleted(true);
+  if (meta_mesh_->has_halfedge_status()) {
+    meta_mesh_->status(mheh).set_deleted(true);
+    meta_mesh_->status(mheho).set_deleted(true);
+  }
+}
+
+/*!
+ * \brief Embedding::MiddleBvh
+ * \param meh
+ * \return the base vertex at the middle of meh (roughly since the embedding is discrete)
+ */
+OpenMesh::VertexHandle Embedding::MiddleBvh(OpenMesh::EdgeHandle meh) {
+  auto bhehleft = meta_mesh_->property(mhe_connection_, meta_mesh_->halfedge_handle(meh, 0));
+  auto bhehright = meta_mesh_->property(mhe_connection_, meta_mesh_->halfedge_handle(meh, 1));
+  if (bhehleft == base_mesh_->opposite_halfedge_handle(bhehright)) {
+    return base_mesh_->to_vertex_handle(SplitBaseHe(bhehleft));
+  }
+  double leftdist = base_mesh_->calc_edge_length(bhehleft);
+  double rightdist = base_mesh_->calc_edge_length(bhehright);
+
+  while(base_mesh_->to_vertex_handle(bhehleft)
+        != base_mesh_->to_vertex_handle(bhehright)) {
+    if (leftdist < rightdist) {
+      bhehleft = base_mesh_->property(next_heh_, bhehleft);
+      leftdist += base_mesh_->calc_edge_length(bhehleft);
+    } else {
+      bhehright = base_mesh_->property(next_heh_, bhehright);
+      rightdist += base_mesh_->calc_edge_length(bhehright);
+    }
+  }
+  return base_mesh_->to_vertex_handle(bhehleft);
+}
+
+/*!
+ * \brief Embedding::MiddleBvh
+ * \param mheh
+ * \return the base vertex at the middle of mheh (roughly since the embedding is discrete)
+ */
+OpenMesh::VertexHandle Embedding::MiddleBvh(OpenMesh::HalfedgeHandle mheh) {
+  return MiddleBvh(meta_mesh_->edge_handle(mheh));
+}
+
+/*!
+ * \brief Embedding::EdgeSplit splits meh at bvh
+ * \param meh
+ * \param bvh
+ * \param verbose
+ */
+void Embedding::EdgeSplit(OpenMesh::EdgeHandle meh, OpenMesh::VertexHandle bvh, bool verbose) {
+  assert(meta_mesh_->is_valid_handle(meh));
+  if (!base_mesh_->is_valid_handle(bvh)) {
+    bvh = MiddleBvh(meh);
+  }
+
+  if (verbose) {
+    qDebug() << "Split the edge in the meta mesh";
+  }
+  MetaEdgeSplit(meh, bvh);
+  auto mvh = base_mesh_->property(bv_connection_, bvh);
+  auto start = meta_mesh_->halfedge_handle(mvh);
+  auto iter = start;
+  if (verbose) {
+    qDebug() << "Trace the new edges in the base mesh";
+  }
+  do {
+    if (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, iter))) {
+      Trace(iter);
+      if (debug_hard_stop_) return;
+    }
+    iter = meta_mesh_->opposite_halfedge_handle(meta_mesh_->prev_halfedge_handle(iter));
+  } while (iter != start);
+}
+
+/*!
+ * \brief Embedding::MetaEdgeSplit the part of the split performed on the meta mesh
+ * \param meh
+ * \param bvh
+ */
+void Embedding::MetaEdgeSplit(OpenMesh::EdgeHandle meh, OpenMesh::VertexHandle bvh) {
+  OpenMesh::HalfedgeHandle mhehb;
+  auto mheha = meta_mesh_->halfedge_handle(meh, 0);
+  auto mvh1 = meta_mesh_->to_vertex_handle(mheha);
+
+  // Add bvh into the meta mesh
+  auto mvhnew = meta_mesh_->add_vertex(base_mesh_->point(bvh));
+
+  // ////////////////
+  // Connect mhehb
+  mhehb = meta_mesh_->new_edge(mvhnew, mvh1);
+
+  // Boundary properties:
+  if (meta_mesh_->is_boundary(mheha)) {
+    meta_mesh_->set_boundary(mhehb);
+  }
+  if (meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheha))) {
+    meta_mesh_->set_boundary(meta_mesh_->opposite_halfedge_handle(mhehb));
+  }
+
+  // he - he
+  if (meta_mesh_->next_halfedge_handle(mheha)
+      == meta_mesh_->opposite_halfedge_handle(mheha)) {
+    meta_mesh_->set_next_halfedge_handle(mhehb, meta_mesh_->opposite_halfedge_handle(mhehb));
+  } else {
+    meta_mesh_->set_next_halfedge_handle(mhehb, meta_mesh_->next_halfedge_handle(mheha));
+    meta_mesh_->set_next_halfedge_handle(meta_mesh_->prev_halfedge_handle(
+                                         meta_mesh_->opposite_halfedge_handle(mheha)),
+                                         meta_mesh_->opposite_halfedge_handle(mhehb));
+  }
+  // he - v
+  meta_mesh_->set_vertex_handle(mhehb, mvh1);
+  meta_mesh_->set_vertex_handle(meta_mesh_->opposite_halfedge_handle(mhehb), mvhnew);
+  // v - he
+  if (meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheha))) {
+    meta_mesh_->set_halfedge_handle(mvhnew, meta_mesh_->opposite_halfedge_handle(mheha));
+  } else {
+    meta_mesh_->set_halfedge_handle(mvhnew, mhehb);
+  }
+  if (meta_mesh_->halfedge_handle(mvh1) == meta_mesh_->opposite_halfedge_handle(mheha)) {
+    meta_mesh_->set_halfedge_handle(mvh1, meta_mesh_->opposite_halfedge_handle(mhehb));
+  }
+  // he - f
+  meta_mesh_->set_face_handle(mhehb, meta_mesh_->face_handle(mheha));
+  meta_mesh_->set_face_handle(meta_mesh_->opposite_halfedge_handle(mhehb),
+       meta_mesh_->face_handle(meta_mesh_->opposite_halfedge_handle(mheha)));
+  // Rewire mheha
+  // he - he
+  meta_mesh_->set_next_halfedge_handle(mheha, mhehb);
+  meta_mesh_->set_next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(mhehb),
+                                       meta_mesh_->opposite_halfedge_handle(mheha));
+  // he - v
+  meta_mesh_->set_vertex_handle(mheha, mvhnew);
+
+  // ///////////////////
+  // Properties
+  // vertex properties
+  meta_mesh_->property(mv_connection_, mvhnew) = bvh;
+  base_mesh_->property(bv_connection_, bvh) = mvhnew;
+  // halfedge properties
+  for (auto bvoheh : base_mesh_->voh_range(bvh)) {
+    if (base_mesh_->property(bhe_connection_, bvoheh)
+        == meta_mesh_->opposite_halfedge_handle(mheha)) {
+      meta_mesh_->property(mhe_connection_, meta_mesh_->opposite_halfedge_handle(mheha))
+          = bvoheh;
+      base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(bvoheh))
+          = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    }
+    if (base_mesh_->property(bhe_connection_, bvoheh) == mheha) {
+      meta_mesh_->property(mhe_connection_, mhehb) = bvoheh;
+      base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(bvoheh))
+          = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+    }
+  }
+  OverwriteHalfedgeConnection(mhehb, meta_mesh_->property(mhe_connection_, mhehb));
+
+  // Triangulate the two faces adjacent to the new vertex.
+  auto start = mheha;
+  auto iter = meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(
+        meta_mesh_->next_halfedge_handle(start)));
+  while (iter != start) {
+    AddMetaEdge(start, meta_mesh_->prev_halfedge_handle(iter));
+    iter = meta_mesh_->next_halfedge_handle(iter);
+  }
+  start = meta_mesh_->opposite_halfedge_handle(mhehb);
+  iter = meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(
+        meta_mesh_->next_halfedge_handle(start)));
+  while (iter != start) {
+    if (meta_mesh_->is_valid_handle(meta_mesh_->face_handle(iter))) {
+      AddMetaEdge(start, meta_mesh_->prev_halfedge_handle(iter));
+    }
+    iter = meta_mesh_->next_halfedge_handle(iter);
+  }
+}
+
+/*!
+ * \brief Embedding::OverwriteHalfedgeConnection
+ * Overwrites the bhe_connection_ parameters of an embedded halfedge starting at bheh
+ * to correspond to meta halfedge mheh.
+ * This is useful for splits since it saves us two Trace calls.
+ * \param mheh
+ * \param bheh
+ */
+void Embedding::OverwriteHalfedgeConnection(OpenMesh::HalfedgeHandle mheh,
+                                            OpenMesh::HalfedgeHandle bheh) {
+  assert(base_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+                                     base_mesh_->from_vertex_handle(bheh))));
+  assert(base_mesh_->property(bv_connection_, base_mesh_->from_vertex_handle(bheh))
+         == meta_mesh_->from_vertex_handle(mheh));
+  meta_mesh_->property(mhe_connection_, mheh) = bheh;
+  base_mesh_->property(bhe_connection_, bheh) = mheh;
+  base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(bheh))
+      = meta_mesh_->opposite_halfedge_handle(mheh);
+  assert(base_mesh_->property(next_heh_, base_mesh_->opposite_halfedge_handle(bheh))
+         == OpenMesh::PolyConnectivity::InvalidHalfedgeHandle);
+  while (!meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_,
+             base_mesh_->to_vertex_handle(bheh)))) {
+    bheh = base_mesh_->property(next_heh_, bheh);
+    base_mesh_->property(bhe_connection_, bheh) = mheh;
+    base_mesh_->property(bhe_connection_, base_mesh_->opposite_halfedge_handle(bheh))
+        = meta_mesh_->opposite_halfedge_handle(mheh);
+  }
+  assert(base_mesh_->property(next_heh_, bheh)
+         == OpenMesh::PolyConnectivity::InvalidHalfedgeHandle);
+  meta_mesh_->property(mhe_connection_, meta_mesh_->opposite_halfedge_handle(mheh))
+      = base_mesh_->opposite_halfedge_handle(bheh);
+  // Ensure connectivity
+  assert(base_mesh_->property(bv_connection_, base_mesh_->from_vertex_handle(
+                                meta_mesh_->property(mhe_connection_, mheh)))
+         == meta_mesh_->from_vertex_handle(mheh));
+  assert(base_mesh_->property(bv_connection_, base_mesh_->from_vertex_handle(
+         meta_mesh_->property(mhe_connection_, meta_mesh_->opposite_halfedge_handle(mheh))))
+         == meta_mesh_->from_vertex_handle(meta_mesh_->opposite_halfedge_handle(mheh)));
+}
+
+/*!
+ * \brief Embedding::FaceSplit Splits the meta face of mheh at base vertex bvhs
+ * \param bvh
+ * \param mheh
+ * \param verbose
+ */
+void Embedding::FaceSplit(OpenMesh::VertexHandle bvh, OpenMesh::HalfedgeHandle mheh,
+                          bool verbose) {
+  assert(!IsSectorBorder(bvh));
+  if (!meta_mesh_->is_valid_handle(mheh)) {
+    mheh = FindFaceHalfedge(bvh);
+  }
+  if (verbose) {
+    qDebug() << "Meta Face split";
+  }
+  MetaFaceSplit(mheh, bvh);
+  if (verbose) {
+    qDebug() << "Vertex processing";
+  }
+  ProcessVertex(bvh);
+  auto mvh = base_mesh_->property(bv_connection_, bvh);
+  if (verbose) {
+    qDebug() << "Face split loop";
+  }
+  for (auto mvoheh : meta_mesh_->voh_range(mvh)) {
+    if (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mvoheh))) {
+      Trace(mvoheh);
+      if (debug_hard_stop_) return;
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::FindFaceHalfedge
+ * \param bvh
+ * \return a halfedge of the meta face bvh lies on. or InvalidHandle if bvh does not
+ * lie on a meta face.
+ */
+OpenMesh::HalfedgeHandle Embedding::FindFaceHalfedge(OpenMesh::VertexHandle bvh) {
+  std::queue<OpenMesh::VertexHandle> queue;
+  OpenMesh::VPropHandleT<bool> marked;
+  base_mesh_->add_property(marked, "Marked search traversal.");
+  queue.push(bvh);
+  while (!queue.empty()) {
+    auto vh = queue.front();
+    queue.pop();
+    if (!base_mesh_->property(marked, vh)) {
+      base_mesh_->property(marked, vh) = true;
+      for (auto bvoheh : base_mesh_->voh_range(vh)) {
+        if (meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, bvoheh))) {
+          auto iter = base_mesh_->opposite_halfedge_handle(
+                base_mesh_->prev_halfedge_handle(bvoheh));
+          while (!meta_mesh_->is_valid_handle(base_mesh_->property(bhe_connection_, iter))) {
+            if (base_mesh_->property(marked, base_mesh_->to_vertex_handle(iter))) {
+              base_mesh_->remove_property(marked);
+              return base_mesh_->property(bhe_connection_, bvoheh);
+            }
+            iter = base_mesh_->opposite_halfedge_handle(
+                            base_mesh_->prev_halfedge_handle(iter));
+          }
+        }
+        if (!base_mesh_->property(marked, base_mesh_->to_vertex_handle(bvoheh))) {
+          queue.push(base_mesh_->to_vertex_handle(bvoheh));
+        }
+      }
+    }
+  }
+  base_mesh_->remove_property(marked);
+  return OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+}
+
+/*!
+ * \brief Embedding::MetaFaceSplit meta mesh part of the face split
+ * \param mheh
+ * \param bvh
+ */
+void Embedding::MetaFaceSplit(OpenMesh::HalfedgeHandle mheh, OpenMesh::VertexHandle bvh) {
+  // Add bvh into the meta mesh
+  // try highlevel method, implement low level if this does not work
+  meta_mesh_->split(meta_mesh_->face_handle(mheh), base_mesh_->point(bvh));
+
+  // add properties
+  auto mvh = meta_mesh_->to_vertex_handle(meta_mesh_->next_halfedge_handle(mheh));
+  meta_mesh_->property(mv_connection_, mvh) = bvh;
+  base_mesh_->property(bv_connection_, bvh) = mvh;
+}
+
+/*!
+ * \brief Embedding::Relocate
+ * Relocates mvh to bvh
+ * The lowlevel method is better for most purposes, but less robust to extreme edge cases
+ * If you are relocating a vertex with edges that cannot be traced properly regardless of order
+ * eg. a vertex with 3 self-edges in a genus 4 mesh, do not use the low level method
+ * the high level method is a concatenation of a split and a collapse and should thus always work
+ * but it doesnt clean the base mesh up nearly as well.
+ * \param mvh
+ * \param bvh
+ * \param verbose
+ * \param lowlevel enabled by default, but disable it where this fails. The low level method still
+ * needs a bit of work and I might not have enough time to make it handle all edge cases (some
+ * edge cases will remain impossible regardless)
+ */
+void Embedding::Relocate(OpenMesh::VertexHandle mvh, OpenMesh::VertexHandle bvh, bool verbose,
+                         bool lowlevel) {
+  if (!base_mesh_->is_valid_handle(bvh)) {
+    return;
+  }
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh))
+      && !lowlevel) {
+    return;
+  }
+  // Disallow relocation of boundary vertices away from their boundary.
+  if (meta_mesh_->is_boundary(mvh)) {
+    auto mehb = GetMetaEdge(bvh);
+    if (!base_mesh_->is_boundary(bvh)) {
+      if (verbose) {
+        qDebug() << "Cannot relocate a boundary vertex away from its boundary.";
+      }
+      return;
+    } else if (mehb != meta_mesh_->edge_handle(meta_mesh_->halfedge_handle(mvh))
+                && mehb != meta_mesh_->edge_handle(
+                 meta_mesh_->prev_halfedge_handle(meta_mesh_->halfedge_handle(mvh)))) {
+      return;
+    }
+  }
+
+  if (lowlevel) {
+    LowLevelRelocate(mvh, bvh, verbose);
+  } else {
+    CompositeRelocate(mvh, bvh, verbose);
+  }
+}
+
+/*!
+ * \brief Embedding::LowLevelRelocate
+ * Relocation as a lower level function than CompositeRelocate
+ * Fails if all edges around mvh are self-edges (eg. genus 4 object with 1 vertex)
+ * \param mvh
+ * \param bvh
+ * \param verbose
+ */
+void Embedding::LowLevelRelocate(OpenMesh::VertexHandle mvh, OpenMesh::VertexHandle bvh,
+                                 bool verbose) {
+  // This method does not do edge splits, relocate only inside the patch.
+  auto meh = GetMetaEdge(bvh);
+  if (IsSectorBorder(bvh)
+      && meta_mesh_->is_valid_handle(meh)
+      && !(meta_mesh_->to_vertex_handle(meta_mesh_->halfedge_handle(meh,0)) == mvh
+         || meta_mesh_->to_vertex_handle(meta_mesh_->halfedge_handle(meh,1)) == mvh)) {
+    if (verbose) {
+      qDebug() << "Cannot relocate meta vertex " << mvh.idx() << " to base vertex "
+               << bvh.idx() << " since it doesn't lie in an incident face";
+    }
+    return;
+  }
+
+  // Ensure that bvh lies in the patch around mvh
+  auto mheh = FindFaceHalfedge(bvh);
+  auto mhehiter = mheh;
+  bool correct = false;
+  do {
+    if (meta_mesh_->to_vertex_handle(mhehiter) == mvh) {
+      correct = true;
+      mheh = mhehiter;
+    } else {
+      mhehiter = meta_mesh_->next_halfedge_handle(mhehiter);
+    }
+  } while (mhehiter!=mheh);
+  if (!correct) {
+    if (verbose) {
+      qDebug() << "Cannot relocate meta vertex " << mvh.idx() << " to base vertex "
+               << bvh.idx() << " since it doesn't lie in an incident face";
+    }
+    return;
+  }
+
+  // Do the relocation
+  // Relocate mvh to bvh
+  base_mesh_->property(bv_connection_, meta_mesh_->property(mv_connection_, mvh))
+      = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  meta_mesh_->property(mv_connection_, mvh) = bvh;
+  base_mesh_->property(bv_connection_, bvh) = mvh;
+  // Delete the edges inside the patch:
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    if (!(meta_mesh_->from_vertex_handle(moh) == meta_mesh_->to_vertex_handle(moh))
+        || base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, moh))) {
+      RemoveMetaEdgeFromBaseMesh(moh);
+    }
+  }
+  // Use this opportunity to clean up the mesh while the patch is empty
+  CleanUpBaseMesh();
+  // Retrace the patch around mvh
+  // Order can be very important when doing this, as otherwise vertices can be blocked off
+  // by edges. Order of retracing:
+  // 1: Boundary edges
+  // 2: A representative of each connected component in the patch around mvh.
+  // In most cases there will only be one component
+  // 3: Self-edges. The representatives of components should wall those off and confine them
+  // to areas so that they cannot vanish
+  // 4: All other edges that are non-duplicate
+  // 5: duplicates
+  OpenMesh::HPropHandleT<bool> he_selected;
+  OpenMesh::VPropHandleT<bool> v_selected;
+  OpenMesh::VPropHandleT<bool> group_selected;
+  std::queue<OpenMesh::HalfedgeHandle> tracingqueue;
+  meta_mesh_->add_property(he_selected, "select edges for tracing order");
+  meta_mesh_->add_property(group_selected, "select groups for tracing order");
+  meta_mesh_->add_property(v_selected, "select vertices for tracing order");
+  // 0: initialize properties
+  meta_mesh_->property(group_selected, mvh) = false;
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    meta_mesh_->property(he_selected, moh) = false;
+    meta_mesh_->property(he_selected, meta_mesh_->opposite_halfedge_handle(moh)) = false;
+    meta_mesh_->property(group_selected, meta_mesh_->to_vertex_handle(moh)) = false;
+    meta_mesh_->property(v_selected, meta_mesh_->to_vertex_handle(moh)) = false;
+  }
+
+  // 1: find boundary edges.
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    meta_mesh_->property(he_selected, moh) = false;
+    meta_mesh_->property(he_selected, meta_mesh_->opposite_halfedge_handle(moh)) = false;
+    if (!meta_mesh_->property(he_selected, moh)
+        && (meta_mesh_->is_boundary(moh)
+        || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(moh)))) {
+      tracingqueue.push(moh);
+      meta_mesh_->property(he_selected, moh) = true;
+      meta_mesh_->property(he_selected, meta_mesh_->opposite_halfedge_handle(moh)) = true;
+      meta_mesh_->property(group_selected, meta_mesh_->to_vertex_handle(moh)) = true;
+      meta_mesh_->property(v_selected, meta_mesh_->to_vertex_handle(moh)) = true;
+      TraverseGroup(mvh, moh, group_selected);
+    }
+  }
+
+  // 2: find group representatives
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    // non-self-edges
+    if (meta_mesh_->from_vertex_handle(moh)
+        != meta_mesh_->to_vertex_handle(moh)) {
+      // unselected vertex -> new group found -> take the representative and select it
+      if (!meta_mesh_->property(he_selected, moh)
+          && !meta_mesh_->property(group_selected, meta_mesh_->to_vertex_handle(moh))) {
+        tracingqueue.push(moh);
+        meta_mesh_->property(he_selected, moh) = true;
+        meta_mesh_->property(he_selected, meta_mesh_->opposite_halfedge_handle(moh)) = true;
+        meta_mesh_->property(group_selected, meta_mesh_->to_vertex_handle(moh)) = true;
+        meta_mesh_->property(v_selected, meta_mesh_->to_vertex_handle(moh)) = true;
+        TraverseGroup(mvh, moh, group_selected);
+      }
+    }
+  }
+
+  // 3: find self-edges
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    // Also make sure the self-edge is not selected (can happen if boundary self-edge)
+    if (meta_mesh_->from_vertex_handle(moh) != meta_mesh_->to_vertex_handle(moh)
+        && !meta_mesh_->property(he_selected, moh)) {
+      tracingqueue.push(moh);
+      meta_mesh_->property(he_selected, moh) = true;
+      meta_mesh_->property(he_selected, meta_mesh_->opposite_halfedge_handle(moh)) = true;
+      meta_mesh_->property(v_selected, meta_mesh_->to_vertex_handle(moh)) = true;
+    }
+  }
+
+  // 4: add the remaining non-duplicate edges to the qeuue
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    if (!meta_mesh_->property(he_selected, moh) && !meta_mesh_->property(v_selected,
+              meta_mesh_->to_vertex_handle(moh))) {
+      tracingqueue.push(moh);
+      meta_mesh_->property(he_selected, moh) = true;
+      meta_mesh_->property(he_selected, meta_mesh_->opposite_halfedge_handle(moh)) = true;
+      meta_mesh_->property(v_selected, meta_mesh_->to_vertex_handle(moh)) = true;
+    }
+  }
+
+  // 5: add duplicate edges
+  for (auto moh : meta_mesh_->voh_range(mvh)) {
+    if (!meta_mesh_->property(he_selected, moh)) {
+      tracingqueue.push(moh);
+      meta_mesh_->property(he_selected, moh) = true;
+      meta_mesh_->property(he_selected, meta_mesh_->opposite_halfedge_handle(moh)) = true;
+    }
+  }
+
+  // Now that the order is set: finally do the tracing
+  while (!tracingqueue.empty()) {
+    auto mhehc = tracingqueue.front();
+    Trace(mhehc);
+    tracingqueue.pop();
+    if (debug_hard_stop_) {
+      qDebug() << "Relocation failed";
+      base_mesh_->status(bvh).set_selected(true);
+      return;
+    }
+  }
+
+  meta_mesh_->remove_property(he_selected);
+  meta_mesh_->remove_property(v_selected);
+  meta_mesh_->remove_property(group_selected);
+}
+
+/*!
+ * \brief Embedding::TraverseGroup
+ * Traverses a patch border group and marks its members
+ * \param mvh
+ * \param moh
+ * \param groupselected
+ */
+void Embedding::TraverseGroup(OpenMesh::VertexHandle mvh,
+                              OpenMesh::HalfedgeHandle moh,
+                              OpenMesh::VPropHandleT<bool> groupselected) {
+  assert(meta_mesh_->from_vertex_handle(moh) == mvh);
+  assert(meta_mesh_->property(groupselected, meta_mesh_->to_vertex_handle(moh)));
+  auto mheh = meta_mesh_->next_halfedge_handle(moh);
+  while (!meta_mesh_->property(groupselected, meta_mesh_->to_vertex_handle(mheh))
+         && meta_mesh_->from_vertex_handle(mheh) != meta_mesh_->to_vertex_handle(mheh)) {
+    if (meta_mesh_->to_vertex_handle(mheh) == mvh) {
+      mheh = meta_mesh_->next_halfedge_handle(
+            meta_mesh_->opposite_halfedge_handle(mheh));
+    } else {
+      meta_mesh_->property(groupselected, meta_mesh_->to_vertex_handle(mheh)) = true;
+      mheh = meta_mesh_->next_halfedge_handle(mheh);
+    }
+  }
+}
+
+/*!
+ * \brief Embedding::CompositeRelocate
+ * Relocation as a composite of a split and a collapse
+ * \param mvh
+ * \param bvh
+ * \param verbose
+ */
+void Embedding::CompositeRelocate(OpenMesh::VertexHandle mvh,
+                                  OpenMesh::VertexHandle bvh, bool verbose) {
+  if (!IsSectorBorder(bvh)) {
+    //qDebug() << "Relocation to Face";
+    //qDebug() << "Find Face halfedge";
+    auto mheh = FindFaceHalfedge(bvh);
+    auto mhehiter = mheh;
+    bool correct = false;
+    do {
+      if (meta_mesh_->to_vertex_handle(mhehiter) == mvh) {
+        correct = true;
+        mheh = mhehiter;
+      } else {
+        mhehiter = meta_mesh_->next_halfedge_handle(mhehiter);
+      }
+    } while (mhehiter!=mheh);
+    if (!correct) {
+      if (verbose) {
+        qDebug() << "Cannot relocate meta vertex " << mvh.idx() << " to base vertex "
+                 << bvh.idx() << " since it doesn't lie in an incident face";
+      }
+      return;
+    }
+
+    //qDebug() << "Relocate: Face Split";
+    // Insert the new point
+    Split(bvh, mheh, verbose);
+    if (debug_hard_stop_) return;
+    auto mhehc = meta_mesh_->next_halfedge_handle(mheh);
+    assert(meta_mesh_->is_valid_handle(mhehc));
+    auto mvhn = meta_mesh_->to_vertex_handle(mhehc);
+    // Swap to ensure the relocated vertex has the same ID as before relocation
+    //qDebug() << "Relocate: Swap";
+    MetaSwap(meta_mesh_->from_vertex_handle(mhehc), meta_mesh_->to_vertex_handle(mhehc));
+    if (debug_hard_stop_) return;
+    //qDebug() << "Relocate: Collapse";
+    // Collapse the old point into the new point
+    Collapse(mhehc, verbose);
+    if (debug_hard_stop_) return;
+    for (auto miheh : meta_mesh_->vih_range(mvhn)) {
+      //qDebug() << "Relocate: Retracing halfedge " << miheh.idx();
+      Retrace(miheh);
+    }
+  } else {
+    //qDebug() << "Relocation to Edge";
+    auto meh = GetMetaEdge(bvh);
+    auto mvh0 = meta_mesh_->to_vertex_handle(meta_mesh_->halfedge_handle(meh, 0));
+    auto mvh1 = meta_mesh_->to_vertex_handle(meta_mesh_->halfedge_handle(meh, 1));
+    if (mvh0 != mvh && mvh1 != mvh) {
+      if (verbose) {
+        qDebug() << "Cannot relocate meta vertex " << mvh.idx() << " to base vertex "
+                 << bvh.idx() << " since it doesn't lie in an incident face";
+      }
+      return;
+    }
+    if (!meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh))) {
+      //qDebug() << "Relocate: Edge Split";
+      // Insert the new point
+      Split(bvh, OpenMesh::PolyConnectivity::InvalidHalfedgeHandle, verbose);
+      if (debug_hard_stop_) return;
+      auto mhehc = meta_mesh_->opposite_halfedge_handle(
+            meta_mesh_->find_halfedge(base_mesh_->property(bv_connection_, bvh), mvh));
+      assert(meta_mesh_->is_valid_handle(mhehc));
+      auto mvhn = meta_mesh_->to_vertex_handle(mhehc);
+      // Swap to ensure the relocated vertex has the same ID as before relocation
+      //qDebug() << "Relocate: Swap";
+      MetaSwap(meta_mesh_->from_vertex_handle(mhehc), meta_mesh_->to_vertex_handle(mhehc));
+      if (debug_hard_stop_) return;
+      //qDebug() << "Relocate: Collapse";
+      // Collapse the old point into the new point
+      Collapse(mhehc, verbose);
+      if (debug_hard_stop_) return;
+      for (auto miheh : meta_mesh_->vih_range(mvhn)) {
+        //qDebug() << "Relocate: Retracing halfedge " << miheh.idx();
+        Retrace(miheh);
+      }
+    }
+    //qDebug() << "Relocation finished.";
+  }
+}
+
+/*!
+ * \brief Embedding::Split
+ * Splits at point bvh; if it is on an edge performs an edge split otherwise
+ * a face split. mheh is an optional parameter for face splits denoting a
+ * half-edge of the nearest face. Use for optimization, otherwise the nearest
+ * half-edge is searched for from bvh.
+ * \param bvh
+ * \param mheh
+ * \param verbose
+ */
+void Embedding::Split(OpenMesh::VertexHandle bvh, OpenMesh::HalfedgeHandle mheh,
+                      bool verbose) {
+  if (meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvh))) {
+    qDebug() << "Cannot split at vertex " << bvh.idx() << " since it is already a meta vertex";
+  } else if (IsSectorBorder(bvh)) {
+    //qDebug() << "Edge split";
+    EdgeSplit(GetMetaEdge(bvh), bvh, verbose);
+  } else {
+    //qDebug() << "Face split";
+    FaceSplit(bvh, mheh, verbose);
+  }
+}
+
+/*!
+ * \brief Embedding::Collapse
+ * Collapses the FROM vertex into the TO vertex
+ * \param mheh
+ * \param verbose
+ */
+void Embedding::Collapse(OpenMesh::HalfedgeHandle mheh, bool verbose) {
+  if (debug_hard_stop_) return;
+
+  //qDebug() << "Calling collapse";
+  // Check if collapsing is ok
+  if (!IsCollapseOkay(mheh, verbose)) {
+    if (verbose) {
+      qDebug() << "Cannot collapse halfedge " << mheh.idx();
+    }
+    return;
+  }
+
+  auto mvh = meta_mesh_->to_vertex_handle(mheh);
+  auto mheho = meta_mesh_->opposite_halfedge_handle(mheh);
+  auto mhehn = meta_mesh_->next_halfedge_handle(mheh);
+  auto mhehp = meta_mesh_->prev_halfedge_handle(mheh);
+  auto mhehon = meta_mesh_->next_halfedge_handle(mheho);
+  auto mhehop = meta_mesh_->prev_halfedge_handle(mheho);
+  auto mvhdel = meta_mesh_->from_vertex_handle(mheh);
+  auto bvhdel = meta_mesh_->property(mv_connection_, mvhdel);
+
+  if (verbose
+      && !meta_mesh_->is_boundary(mheh)
+      && !meta_mesh_->is_boundary(mheho)) {
+    int ringeuler = OneRingEuler(mvhdel);
+    if (ringeuler != 1) {
+      qDebug() << "The patch around the vertex " << mvhdel.idx() << " that is to be collapsed"
+                  " has euler characteristic " << ringeuler << ".";
+    }
+  }
+
+  //  Two pass tracing
+  //qDebug() << "First Trace Pass.";
+  // First pass : trace into the right topological area, bend in the meta mesh
+  // this step implicitly collapses the meta edge
+  if (!meta_mesh_->is_boundary(mheh)) {
+    while (!meta_mesh_->status(mheh).deleted()
+           &&  meta_mesh_->prev_halfedge_handle(mheh) !=
+               meta_mesh_->opposite_halfedge_handle(mheh)) {
+      auto mhehcurr = meta_mesh_->prev_halfedge_handle(mheh);
+      BendMetaEdgeForwards(mhehcurr);
+      if (meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mhehcurr))
+          || (meta_mesh_->prev_halfedge_handle(mheh) ==
+          meta_mesh_->opposite_halfedge_handle(mheh)
+          && meta_mesh_->from_vertex_handle(mhehcurr)
+              != meta_mesh_->to_vertex_handle(mhehcurr))) {
+        assert(meta_mesh_->prev_halfedge_handle(mheh) ==
+               meta_mesh_->opposite_halfedge_handle(mheh));
+        // Remove the A->B edge
+        RemoveMetaEdgeFromBaseMesh(mheh);
+        RemoveMetaEdgeFromMetaMesh(mheh);
+      }
+      if (meta_mesh_->is_valid_handle(mhehcurr)
+          && !meta_mesh_->status(mhehcurr).deleted()) {
+        RemoveMetaEdgeFromBaseMesh(mhehcurr);
+        if (debug_hard_stop_) return;
+        Trace(mhehcurr, true, false);
+        if (debug_hard_stop_) return;
+      } else {
+        assert(!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehcurr)));
+      }
+    }
+  } else {
+    while (!meta_mesh_->status(mheh).deleted()
+           &&  meta_mesh_->next_halfedge_handle(
+               meta_mesh_->opposite_halfedge_handle(mheh)) != mheh) {
+      auto mhehcurr = meta_mesh_->next_halfedge_handle(
+            meta_mesh_->opposite_halfedge_handle(mheh));
+      BendMetaEdgeBackwards(mhehcurr);
+      if (meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mhehcurr))
+          || (meta_mesh_->prev_halfedge_handle(mheh) ==
+          meta_mesh_->opposite_halfedge_handle(mheh)
+          && meta_mesh_->from_vertex_handle(mhehcurr)
+              != meta_mesh_->to_vertex_handle(mhehcurr))) {
+        assert(meta_mesh_->next_halfedge_handle(
+               meta_mesh_->opposite_halfedge_handle(mheh)) == mheh);
+        // Remove the A->B edge
+        RemoveMetaEdgeFromBaseMesh(mheh);
+        RemoveMetaEdgeFromMetaMesh(mheh);
+      }
+      if (meta_mesh_->is_valid_handle(mhehcurr)
+          && !meta_mesh_->status(mhehcurr).deleted()) {
+        RemoveMetaEdgeFromBaseMesh(mhehcurr);
+        if (debug_hard_stop_) return;
+        Trace(mhehcurr, true, false);
+        if (debug_hard_stop_) return;
+      } else {
+        assert(!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mhehcurr)));
+      }
+    }
+  }
+
+  if (!meta_mesh_->status(mheh).deleted()) {
+    //qDebug() << "Foo";
+    // Remove the A->B edge here if it was encased by a self-edge
+    RemoveMetaEdgeFromBaseMesh(mheh);
+    RemoveMetaEdgeFromMetaMesh(mheh);
+  }
+
+  // Remove 1-Loop (case: self-edge)
+  if (mhehn == mhehop && mhehon == mheh) {
+    auto mhehouter = meta_mesh_->next_halfedge_handle(
+          meta_mesh_->opposite_halfedge_handle(mhehn));
+    assert(meta_mesh_->from_vertex_handle(mhehn) == meta_mesh_->to_vertex_handle(mhehn));
+    if (meta_mesh_->is_valid_handle(mhehn)
+        && !meta_mesh_->status(mhehn).deleted()) {
+      RemoveSelfLoop(mhehn);
+    }
+    if (meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(mhehouter))
+        == mhehouter) {
+      RemoveLoop(mhehouter);
+      Retrace(mhehouter, true, false);
+      if (debug_hard_stop_) return;
+    }
+  } else if (mhehon == mhehp && mhehn == mheho) {
+    auto mhehouter = meta_mesh_->next_halfedge_handle(
+          meta_mesh_->opposite_halfedge_handle(mhehon));
+    assert(meta_mesh_->from_vertex_handle(mhehon) == meta_mesh_->to_vertex_handle(mhehon));
+    if (meta_mesh_->is_valid_handle(mhehon)
+        && !meta_mesh_->status(mhehon).deleted()) {
+      RemoveSelfLoop(mhehon);
+    }
+    if (meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(mhehouter))
+        == mhehouter) {
+      RemoveLoop(mhehouter);
+      Retrace(mhehouter, true, false);
+      if (debug_hard_stop_) return;
+    }
+  } else {
+    // Remove 2-Loops
+    if (meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(mhehn))
+        == mhehn) {
+      RemoveLoop(mhehn);
+    }
+    if (meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(mhehop))
+        == mhehop) {
+      RemoveLoop(mhehop);
+    }
+    if (meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(mhehp))
+        == mhehp) {
+      RemoveLoop(mhehp);
+    }
+    if (meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(mhehon))
+        == mhehon) {
+      RemoveLoop(mhehon);
+    }
+  }
+
+  //qDebug() << "Second Trace Pass.";
+  // Second pass : Retrace to look nicer
+  for (auto mhehit : meta_mesh_->voh_range(mvh)) {
+    if (!meta_mesh_->status(mhehit).deleted()) {
+      Retrace(mhehit, true, false);
+      if (debug_hard_stop_) return;
+    }
+  }
+
+  // ensure disconnection of the collapsed vertex
+  assert(!meta_mesh_->is_valid_handle(base_mesh_->property(bv_connection_, bvhdel)));
+  assert(meta_mesh_->status(mvhdel).deleted());
+
+  // Sanity tests
+  for (auto mvoheh : meta_mesh_->voh_range(mvh)) {
+    assert(!meta_mesh_->status(mvoheh).deleted());
+    assert(base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mvoheh)));
+    assert(base_mesh_->property(bhe_connection_, meta_mesh_->property(mhe_connection_, mvoheh))
+           == mvoheh);
+  }
+
+  //qDebug() << "Collapse finished";
+}
+
+/*!
+ * \brief Embedding::IsCollapseOkay
+ * \param mheh
+ * \param verbose
+ * \return whether mheh can be collapsed
+ */
+bool Embedding::IsCollapseOkay(OpenMesh::HalfedgeHandle mheh, bool verbose) {
+  if (!meta_mesh_->is_valid_handle(meta_mesh_->edge_handle(mheh)))
+  {
+    if (verbose) {
+      qDebug() << "Invalid halfedge handle";
+    }
+    return false;
+  }
+  // is edge already deleted?
+  if (meta_mesh_->status(meta_mesh_->edge_handle(mheh)).deleted())
+  {
+    if (verbose) {
+      qDebug() << "Deleted halfedge";
+    }
+    return false;
+  }
+
+  // is a boundary vertex being collapsed along a non-boundary edge?
+  if (meta_mesh_->is_boundary(meta_mesh_->from_vertex_handle(mheh))
+      && !meta_mesh_->is_boundary(mheh)
+      && !meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheh))) {
+    if (verbose) {
+      qDebug() << "Cannot collapse boundary into non-boundary";
+    }
+    return false;
+  }
+
+  if (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mheh)))
+  {
+    if (verbose) {
+      qDebug() << "Connected to an invalid halfedge, this should NOT happen";
+    }
+    return false;
+  }
+  if (!base_mesh_->is_valid_handle(meta_mesh_->property(mhe_connection_, mheh)))
+  {
+    if (verbose) {
+      qDebug() << "Unconnected halfedge, this should NOT happen";
+    }
+    return false;
+  }
+
+  // Don't allow collapses when the number of halfedges of the face is equal to the
+  // total number of edges (equivalent to the same faces condition, but includes
+  // cases where there are self-edges).
+  size_t ctr = 1;
+  auto mhehnext = meta_mesh_->next_halfedge_handle(mheh);
+  while (mheh != mhehnext) {
+    ++ctr;
+    mhehnext = meta_mesh_->next_halfedge_handle(mhehnext);
+  }
+  if (ctr >= meta_mesh_->n_edges()) {
+    if (verbose) {
+      qDebug() << "minimal mesh reached";
+    }
+    return false;
+  }
+
+  // is vertex connected to itself?
+  if (meta_mesh_->from_vertex_handle(mheh) == meta_mesh_->to_vertex_handle(mheh)) {
+    if (verbose) {
+     qDebug() << "self-edge";
+    }
+    return false;
+  }
+
+  // Special case, two non-boundary edges encased by two boundary edges:
+  //    A    eg. here: collapsing the A->B halfedge with A->C
+  //   /|\   and C->A being boundaries. Allowing this collapse would result
+  //  | B |  in 3 connections between A and C, the middle edge is removed as a loop,
+  //   \|/   and the remaining face is no longer a triangle.
+  //    C    If C->A and A->C are not both boundaries this works fine as two loop edges
+  // can be collapsed.
+  if (meta_mesh_->next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(
+      meta_mesh_->next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(mheh))))
+      == mheh
+      && meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(
+           meta_mesh_->next_halfedge_handle(mheh)))
+      && meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(
+           meta_mesh_->prev_halfedge_handle(
+             meta_mesh_->opposite_halfedge_handle(mheh))))
+      && meta_mesh_->valence(meta_mesh_->face_handle(mheh)) == 3
+      && meta_mesh_->valence(meta_mesh_->face_handle(
+                             meta_mesh_->opposite_halfedge_handle(mheh))) == 3) {
+    if (verbose) {
+      qDebug() << "encased between two boundary faces";
+    }
+    return false;
+  }
+
+  // Are the two faces the same triangles?
+  // Deprecated test with the new addition counting edges
+  auto mhehiter0 = meta_mesh_->next_halfedge_handle(mheh);
+  auto mhehiter1 = meta_mesh_->prev_halfedge_handle(
+        meta_mesh_->opposite_halfedge_handle(mheh));
+  bool sameface = true;
+  while (mhehiter0 != mheh) {
+    if (mhehiter0 != meta_mesh_->opposite_halfedge_handle(mhehiter1)) {
+      sameface = false;
+    }
+    mhehiter0 = meta_mesh_->next_halfedge_handle(mhehiter0);
+    mhehiter1 = meta_mesh_->prev_halfedge_handle(mhehiter1);
+  }
+  if (sameface && meta_mesh_->valence(meta_mesh_->face_handle(mheh)) == 3) {
+    if (verbose) {
+      qDebug() << "same triangle";
+    }
+    return false;
+  }
+
+  // TODO: think of more exceptions
+  return true;
+}
+
+/*!
+ * \brief Embedding::BendMetaEdgeForwards
+ * A trick used in collapse, bending edges so as to always trace inside disk topology areas
+ * \param mheh
+ */
+void Embedding::BendMetaEdgeForwards(OpenMesh::HalfedgeHandle mheh) {
+  if (meta_mesh_->status(mheh).deleted()) {
+    assert(meta_mesh_->from_vertex_handle(mheh) == meta_mesh_->to_vertex_handle(mheh));
+    return;
+  }
+  auto newtomvh = meta_mesh_->to_vertex_handle(meta_mesh_->next_halfedge_handle(mheh));
+
+  auto mhehopp = meta_mesh_->opposite_halfedge_handle(mheh);
+  auto mhehoppprev = meta_mesh_->prev_halfedge_handle(mhehopp);
+  auto mhehnext = meta_mesh_->next_halfedge_handle(mheh);
+  auto mhehnewnext = meta_mesh_->next_halfedge_handle(mhehnext);
+
+  auto mfh = meta_mesh_->face_handle(mheh);
+  auto mfho = meta_mesh_->face_handle(mhehopp);
+
+  // sever old connections
+  // he - he
+  meta_mesh_->set_next_halfedge_handle(mhehoppprev, mhehnext);
+  // v - he
+  if (meta_mesh_->halfedge_handle(meta_mesh_->to_vertex_handle(mheh)) == mhehopp) {
+    meta_mesh_->set_halfedge_handle(meta_mesh_->to_vertex_handle(mheh),
+    meta_mesh_->next_halfedge_handle(meta_mesh_->opposite_halfedge_handle(mhehnext)));
+  }
+
+
+  // add new connections
+  // he - he
+  meta_mesh_->set_next_halfedge_handle(mhehnext, mhehopp);
+  meta_mesh_->set_next_halfedge_handle(mheh, mhehnewnext);
+
+  // he - v
+  meta_mesh_->set_vertex_handle(mheh, newtomvh);
+
+  // f - v
+  meta_mesh_->set_halfedge_handle(mfh, mheh);
+
+  // v - f
+  meta_mesh_->set_face_handle(mhehnext, mfho);
+}
+
+/*!
+ * \brief Embedding::BendMetaEdgeBackwards
+ * A trick used in collapse, bending edges so as to always trace inside disk topology areas
+ * \param mheh
+ */
+void Embedding::BendMetaEdgeBackwards(OpenMesh::HalfedgeHandle mheh) {
+  if (meta_mesh_->status(mheh).deleted()) {
+    assert(meta_mesh_->from_vertex_handle(mheh) == meta_mesh_->to_vertex_handle(mheh));
+    return;
+  }
+  auto newfrommvh = meta_mesh_->from_vertex_handle(meta_mesh_->prev_halfedge_handle(mheh));
+
+  auto mhehprev = meta_mesh_->prev_halfedge_handle(mheh);
+  auto mhehopp = meta_mesh_->opposite_halfedge_handle(mheh);
+  auto mhehoppnext = meta_mesh_->next_halfedge_handle(mhehopp);
+  auto mhehprevopp = meta_mesh_->opposite_halfedge_handle(mhehprev);
+  auto mhehprevprev = meta_mesh_->prev_halfedge_handle(mhehprev);
+
+  auto mfh = meta_mesh_->face_handle(mheh);
+  auto mfho = meta_mesh_->face_handle(mhehopp);
+
+  // sever old connections
+  // he - he
+  meta_mesh_->set_next_halfedge_handle(mhehprev, mhehoppnext);
+  // v - he
+  if (meta_mesh_->halfedge_handle(meta_mesh_->from_vertex_handle(mheh)) == mheh) {
+    meta_mesh_->set_halfedge_handle(meta_mesh_->from_vertex_handle(mheh), mhehprevopp);
+  }
+
+
+  // add new connections
+  // he - he
+  meta_mesh_->set_next_halfedge_handle(mhehopp, mhehprev);
+  meta_mesh_->set_next_halfedge_handle(mhehprevprev, mheh);
+
+  // he - v
+  meta_mesh_->set_vertex_handle(mhehopp, newfrommvh);
+
+  // f - v
+  meta_mesh_->set_halfedge_handle(mfh, mheh);
+
+  // v - f
+  meta_mesh_->set_face_handle(mhehprev, mfho);
+}
+
+/*!
+ * \brief Embedding::RemoveLoop
+ * removes a loop of edges AB->BA->AB.. by deleting BA
+ * \param mheh is AB
+ */
+void Embedding::RemoveLoop(OpenMesh::HalfedgeHandle mheh) {
+  // make sure this is a loop
+  if(meta_mesh_->next_halfedge_handle(meta_mesh_->next_halfedge_handle(mheh)) != mheh) {
+    return;
+  }
+
+  // if the loop is already deleted, return  (should not happen=
+  if (meta_mesh_->status(mheh).deleted()
+      || meta_mesh_->status(meta_mesh_->next_halfedge_handle(mheh)).deleted()) {
+    qDebug() << "Found a halfedge pointing towards a deleted halfedge in RemoveLoop.";
+    return;
+  }
+
+  // if the deleted edge would be a boundary, remove the other edge instead.
+  if (meta_mesh_->is_boundary(meta_mesh_->next_halfedge_handle(mheh))
+      || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(
+                                   meta_mesh_->next_halfedge_handle(mheh)))) {
+    // Avoid endless loops; if both edges are boundaries keep both.
+    if (meta_mesh_->is_boundary(mheh)
+        || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheh))) {
+      return;
+    }
+    RemoveLoop(meta_mesh_->next_halfedge_handle(mheh));
+    return;
+  }
+
+  auto mheho = meta_mesh_->opposite_halfedge_handle(mheh);
+  auto mhehdel = meta_mesh_->next_halfedge_handle(mheh);
+  auto mhehdelo = meta_mesh_->opposite_halfedge_handle(mhehdel);
+  auto mvh0 = meta_mesh_->from_vertex_handle(mheh);
+  auto mvh1 = meta_mesh_->to_vertex_handle(mheh);
+  auto mfh = meta_mesh_->face_handle(mhehdelo);
+  auto mfhdel = meta_mesh_->face_handle(mheh);
+
+  // he -> he
+  meta_mesh_->set_next_halfedge_handle(meta_mesh_->prev_halfedge_handle(mhehdelo), mheh);
+  meta_mesh_->set_next_halfedge_handle(mheh, meta_mesh_->next_halfedge_handle(mhehdelo));
+
+  if (meta_mesh_->next_halfedge_handle(mheho) == mhehdelo) {
+    meta_mesh_->set_next_halfedge_handle(mheho, mheh);
+  }
+
+  // v -> he
+  if (meta_mesh_->halfedge_handle(mvh0) == mhehdelo) {
+    meta_mesh_->set_halfedge_handle(mvh0, mheh);
+  }
+  if (meta_mesh_->halfedge_handle(mvh1) == mhehdel) {
+    meta_mesh_->set_halfedge_handle(mvh1, meta_mesh_->opposite_halfedge_handle(mheh));
+  }
+
+  // he -> f
+  if (meta_mesh_->is_valid_handle(mfh)) meta_mesh_->set_face_handle(mheh, mfh);
+
+  // f -> he
+  if (meta_mesh_->is_valid_handle(mfh)) meta_mesh_->set_halfedge_handle(mfh, mheh);
+
+  // delete stuff in the base mesh
+  RemoveMetaEdgeFromBaseMesh(mhehdel);
+
+  // delete stuff in the meta mesh
+  meta_mesh_->status(mfhdel).set_deleted(true);
+  meta_mesh_->status(meta_mesh_->edge_handle(mhehdel)).set_deleted(true);
+  if (meta_mesh_->has_halfedge_status()) {
+    meta_mesh_->status(mhehdel).set_deleted(true);
+    meta_mesh_->status(mhehdelo).set_deleted(true);
+  }
+
+  assert(meta_mesh_->prev_halfedge_handle(mheh) != mhehdel);
+  assert(meta_mesh_->prev_halfedge_handle(meta_mesh_->next_halfedge_handle(mheh)) != mhehdelo);
+}
+
+/*!
+ * \brief Embedding::RemoveSelfLoop
+ * removes a self loop mheh->mheh->mheh...
+ * \param mheh
+ */
+void Embedding::RemoveSelfLoop(OpenMesh::HalfedgeHandle mheh) {
+  // Avoid removing boundaries
+  if (meta_mesh_->is_boundary(mheh)
+      || meta_mesh_->is_boundary(meta_mesh_->opposite_halfedge_handle(mheh))) {
+    return;
+  }
+
+  assert(meta_mesh_->next_halfedge_handle(mheh) == mheh);
+  auto mheho = meta_mesh_->opposite_halfedge_handle(mheh);
+  auto mhehop = meta_mesh_->prev_halfedge_handle(mheho);
+  auto mhehon = meta_mesh_->next_halfedge_handle(mheho);
+  // if this assert fails the face of mheho has only 2 edges, don't allow it
+  assert(mhehop != mhehon);
+  auto mfhdel = meta_mesh_->face_handle(mheh);
+  auto mvh = meta_mesh_->to_vertex_handle(mheh);
+
+  // he - he
+  meta_mesh_->set_next_halfedge_handle(mhehop, mhehon);
+
+  // v - he
+  if (meta_mesh_->halfedge_handle(mvh) == mheh
+      || meta_mesh_->halfedge_handle(mvh) == mheho) {
+    meta_mesh_->set_halfedge_handle(mvh, mhehon);
+  }
+
+  // delete stuff in the base mesh
+  RemoveMetaEdgeFromBaseMesh(mheh);
+
+  // delete stuff
+  meta_mesh_->status(mfhdel).set_deleted(true);
+  meta_mesh_->status(meta_mesh_->edge_handle(mheh)).set_deleted(true);
+  if (meta_mesh_->has_halfedge_status()) {
+    meta_mesh_->status(mheh).set_deleted(true);
+    meta_mesh_->status(mheho).set_deleted(true);
+  }
+}
+
+/*!
+ * \brief Embedding::OneRingEuler
+ * \param mvh
+ * \return the euler characteristic of the 1-ring around mvh
+ */
+int Embedding::OneRingEuler(OpenMesh::VertexHandle mvh) {
+  auto mheh = meta_mesh_->halfedge_handle(mvh);
+  // V - E + F = g
+  // Find euler characteristic of mvhdel 1-ring
+  OpenMesh::VPropHandleT<bool> v_marked;
+  OpenMesh::HPropHandleT<bool> h_marked;
+  OpenMesh::FPropHandleT<bool> f_marked;
+  meta_mesh_->add_property(v_marked, "marked vertices");
+  meta_mesh_->add_property(h_marked, "marked halfedges");
+  meta_mesh_->add_property(f_marked, "marked faces");
+  auto iter = mheh;
+  do {
+    meta_mesh_->property(h_marked, iter) = false;
+    meta_mesh_->property(v_marked, meta_mesh_->to_vertex_handle(iter)) = false;
+    meta_mesh_->property(f_marked, meta_mesh_->face_handle(iter)) = false;
+    if (meta_mesh_->to_vertex_handle(iter) == mvh) {
+      iter = meta_mesh_->opposite_halfedge_handle(iter);
+    } else {
+      iter = meta_mesh_->next_halfedge_handle(iter);
+    }
+  } while (iter != mheh);
+  int n_v = 0;
+  int n_f = 0;
+  int n_e = 0;
+  iter = mheh;
+  do {
+    if (!meta_mesh_->property(h_marked, iter)) {
+      ++n_e;
+      meta_mesh_->property(h_marked, iter) = true;
+      meta_mesh_->property(h_marked, meta_mesh_->opposite_halfedge_handle(iter)) = true;
+    }
+    if (!meta_mesh_->property(v_marked, meta_mesh_->to_vertex_handle(iter))) {
+      ++n_v;
+      meta_mesh_->property(v_marked, meta_mesh_->to_vertex_handle(iter)) = true;
+    }
+    if (!meta_mesh_->property(f_marked, meta_mesh_->face_handle(iter))) {
+      ++n_f;
+      meta_mesh_->property(f_marked, meta_mesh_->face_handle(iter)) = true;
+    }
+    if (meta_mesh_->to_vertex_handle(iter) == mvh) {
+      iter = meta_mesh_->opposite_halfedge_handle(iter);
+    } else {
+      iter = meta_mesh_->next_halfedge_handle(iter);
+    }
+  } while (iter != mheh);
+  int euler = n_v-n_e+n_f;
+  meta_mesh_->remove_property(v_marked);
+  meta_mesh_->remove_property(h_marked);
+  meta_mesh_->remove_property(f_marked);
+  return euler;
+}
+
+/*!
+ * \brief Embedding::MetaSwap
+ * swaps all pointers and properties relevant to the embedding structure between mvh0 and mvh1
+ * this is used in the composite relocation method to preserve vertex ids
+ * \param mvh0
+ * \param mvh1
+ */
+void Embedding::MetaSwap(OpenMesh::VertexHandle mvh0, OpenMesh::VertexHandle mvh1) {
+  // Store mvh0 into temp
+  auto tempcolor = meta_mesh_->color(mvh0);
+  auto temppoint = meta_mesh_->point(mvh0);
+  auto tempnormal = meta_mesh_->normal(mvh0);
+  auto temphalfedgehandle = meta_mesh_->halfedge_handle(mvh0);
+  auto tempmvconnection = meta_mesh_->property(mv_connection_, mvh0);
+  std::queue<OpenMesh::HalfedgeHandle> to_halfedges;
+  auto curr = meta_mesh_->opposite_halfedge_handle(temphalfedgehandle);
+  while (meta_mesh_->to_vertex_handle(curr) == mvh0) {
+    meta_mesh_->set_vertex_handle(curr, OpenMesh::PolyConnectivity::InvalidVertexHandle);
+    to_halfedges.push(curr);
+    curr = meta_mesh_->prev_halfedge_handle(meta_mesh_->opposite_halfedge_handle(curr));
+  }
+
+  // Write mvh1 into mvh0
+  meta_mesh_->set_color(mvh0, meta_mesh_->color(mvh1));
+  meta_mesh_->set_point(mvh0, meta_mesh_->point(mvh1));
+  meta_mesh_->set_normal(mvh0, meta_mesh_->normal(mvh1));
+  meta_mesh_->set_halfedge_handle(mvh0, meta_mesh_->halfedge_handle(mvh1));
+  meta_mesh_->property(mv_connection_, mvh0) = meta_mesh_->property(mv_connection_, mvh1);
+  base_mesh_->property(bv_connection_, meta_mesh_->property(mv_connection_, mvh0)) = mvh0;
+  curr = meta_mesh_->opposite_halfedge_handle(meta_mesh_->halfedge_handle(mvh1));
+  while (meta_mesh_->to_vertex_handle(curr) == mvh1) {
+    meta_mesh_->set_vertex_handle(curr, mvh0);
+    curr = meta_mesh_->prev_halfedge_handle(meta_mesh_->opposite_halfedge_handle(curr));
+  }
+
+  // write temp into mvh1
+  meta_mesh_->set_color(mvh1, tempcolor);
+  meta_mesh_->set_point(mvh1, temppoint);
+  meta_mesh_->set_normal(mvh1, tempnormal);
+  meta_mesh_->set_halfedge_handle(mvh1, temphalfedgehandle);
+  meta_mesh_->property(mv_connection_, mvh1) = tempmvconnection;
+  base_mesh_->property(bv_connection_, meta_mesh_->property(mv_connection_, mvh1)) = mvh1;
+  while (!to_halfedges.empty()) {
+    auto mheh = to_halfedges.front();
+    to_halfedges.pop();
+    meta_mesh_->set_vertex_handle(mheh, mvh1);
+  }
+}
+
+/*!
+ * \brief Embedding::DumpMetaMeshHalfedgeInfo Infodump, useful for some debugging.
+ */
+void Embedding::DumpMetaMeshHalfedgeInfo() {
+  qDebug() << "The metamesh has " << meta_mesh_->n_halfedges() << " halfedges.";
+  for (auto mheh : meta_mesh_->halfedges()) {
+    qDebug() << "Meta Halfedge " << mheh.idx() << " pointing from vertex "
+             << meta_mesh_->from_vertex_handle(mheh).idx() << " to vertex "
+             << meta_mesh_->to_vertex_handle(mheh).idx() << " with next halfedge handle "
+             << meta_mesh_->next_halfedge_handle(mheh).idx() << " and previous halfedge handle "
+             << meta_mesh_->prev_halfedge_handle(mheh).idx() << " and opposite halfedge handle "
+             << meta_mesh_->opposite_halfedge_handle(mheh).idx();
+  }
+}
diff --git a/Embedding.hh b/Embedding.hh
new file mode 100644
index 0000000000000000000000000000000000000000..6fb035b03c76fb76ac8b1e5b54e81cc3b585923a
--- /dev/null
+++ b/Embedding.hh
@@ -0,0 +1,220 @@
+#pragma once
+
+#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
+#include <ObjectTypes/PolyMesh/PolyMesh.hh>
+#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
+#include <OpenFlipper/common/Types.hh>
+#include <ACG/Utils/HaltonColors.hh>
+#include <Draw.hh>
+#include <queue>
+#include <cmath>
+
+class Embedding {
+public:
+  Embedding();
+  ~Embedding() {}
+
+  enum TraceFaceAttr {UNPROCESSEDFACE, PREPROCESSEDFACE};
+  enum RandomType {RATIO, TOTAL};
+
+  void CopyInitialization(TriMesh& base_mesh, PolyMesh& meta_mesh);
+  void SelectionTriangulation(TriMesh& base_mesh, PolyMesh& meta_mesh);
+  void RandomTriangulation(TriMesh& base_mesh, PolyMesh& meta_mesh, double ratio,
+                           RandomType rtype);
+  void ColorizeMetaMesh();
+
+  void Trace(OpenMesh::HalfedgeHandle mheh, bool cleanup = true,
+             bool mark_trace = false,
+             TraceFaceAttr facetype = PREPROCESSEDFACE);
+  void Retrace(OpenMesh::HalfedgeHandle mheh, bool cleanup = true,
+               bool mark_trace = false,
+               TraceFaceAttr facetype = PREPROCESSEDFACE);
+  void Collapse(OpenMesh::HalfedgeHandle mheh, bool verbose = false);
+  bool IsCollapseOkay(OpenMesh::HalfedgeHandle mheh, bool verbose = false);
+  void Rotate(OpenMesh::EdgeHandle meh);
+  bool IsRotationOkay(OpenMesh::EdgeHandle meh);
+  void Relocate(OpenMesh::VertexHandle mvh, OpenMesh::VertexHandle bvh, bool verbose = false,
+                bool lowlevel = true);
+  void Split(OpenMesh::VertexHandle bvh, OpenMesh::HalfedgeHandle
+             mheh = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle,
+             bool verbose = false);
+  bool ErrStatus() {return debug_hard_stop_;}
+  void DumpMetaMeshHalfedgeInfo();
+
+  TriMesh* GetBaseMesh() {return base_mesh_;}
+  PolyMesh* GetMetaMesh() {return meta_mesh_;}
+  OpenMesh::EdgeHandle GetMetaEdge(OpenMesh::VertexHandle bvh);
+
+  bool IsSectorBorder(OpenMesh::VertexHandle bvh, std::list<OpenMesh::HalfedgeHandle>
+                       exceptions = {});
+
+  double MetaHalfedgeWeight(OpenMesh::HalfedgeHandle mheh);
+  double MetaVertexWeight(OpenMesh::VertexHandle mvh);
+
+  double CalculateEdgeLength(OpenMesh::HalfedgeHandle mheh);
+  double CalculateEdgeLength(OpenMesh::EdgeHandle meh);
+  double CalculateFlippedEdgeLength(OpenMesh::HalfedgeHandle mheh);
+  double CalculateFlippedEdgeLength(OpenMesh::EdgeHandle meh);
+
+  OpenMesh::VertexHandle MiddleBvh(OpenMesh::EdgeHandle meh);
+  OpenMesh::VertexHandle MiddleBvh(OpenMesh::HalfedgeHandle mheh);
+
+  void CleanMetaMesh();
+  void CleanUpBaseMesh(bool garbagecollection = true);
+
+  void MetaGarbageCollection(std::vector<OpenMesh::VertexHandle*> vh_update = {},
+                         std::vector<OpenMesh::HalfedgeHandle*> heh_update = {},
+                         std::vector<OpenMesh::FaceHandle*> fh_update= {});
+  void BaseGarbageCollection(std::vector<OpenMesh::VertexHandle*> vh_update = {},
+                             std::vector<OpenMesh::HalfedgeHandle*> heh_update = {},
+                             std::vector<OpenMesh::FaceHandle*> fh_update= {});
+  // Removing an edge
+  void RemoveMetaEdgeFromBaseMesh(OpenMesh::HalfedgeHandle mheh, bool cleanup = true);
+  void RemoveMetaEdgeFromMetaMesh(OpenMesh::HalfedgeHandle mheh);
+
+  void MarkVertex(OpenMesh::VertexHandle bvh);
+
+  friend class Testing;
+
+  OpenMesh::VPropHandleT<OpenMesh::VertexHandle> bv_connection_;
+  OpenMesh::VPropHandleT<OpenMesh::VertexHandle> mv_connection_;
+  OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> bsplithandle_;
+  OpenMesh::HPropHandleT<OpenMesh::HalfedgeHandle> bhe_connection_;
+  OpenMesh::HPropHandleT<OpenMesh::HalfedgeHandle> mhe_connection_;
+  OpenMesh::HPropHandleT<OpenMesh::HalfedgeHandle> next_heh_;
+  OpenMesh::HPropHandleT<double> halfedge_weight_;
+
+private:
+  enum SplitTraversal {LEFT, RIGHT};
+  bool TriangulationPipeline(
+      std::vector<OpenMesh::VertexHandle> meta_mesh_points);
+  void InitializeProperties();
+  bool TriangulateMetaMesh();
+  void PreProcessEdges();
+  void ProcessHalfedge(OpenMesh::HalfedgeHandle mheh);
+  void ProcessEdge(OpenMesh::EdgeHandle meh);
+  void ProcessVertex(OpenMesh::VertexHandle bvh);
+  void CleanupVertex(OpenMesh::VertexHandle bvh);
+  void CleanupFace(OpenMesh::HalfedgeHandle mheh);
+  void CleanupHalfedge(OpenMesh::HalfedgeHandle mheh);
+  void ProcessNeighbors(OpenMesh::EdgeHandle meh);
+  void ProcessFace(OpenMesh::HalfedgeHandle mheh);
+  bool ConditionalSplit(OpenMesh::HalfedgeHandle bheh);
+  std::vector<OpenMesh::HalfedgeHandle> LeftHalfCircle(OpenMesh::HalfedgeHandle bheh);
+  std::vector<OpenMesh::HalfedgeHandle> GetBaseHalfedges(OpenMesh::HalfedgeHandle mheh);
+  OpenMesh::VertexHandle ConditionalCollapse(OpenMesh::VertexHandle bvh);
+  OpenMesh::HalfedgeHandle SplitBaseHe(OpenMesh::HalfedgeHandle bheh);
+  void CollapseBaseHe(OpenMesh::HalfedgeHandle bheh);
+  void LowLevelBaseCollapse(OpenMesh::HalfedgeHandle bheh);
+  void AdjustPointersForBheCollapse(OpenMesh::HalfedgeHandle bheh);
+  void MergeProperties(OpenMesh::HalfedgeHandle bheh0, OpenMesh::HalfedgeHandle bheh1);
+  void CreateMetaMeshVertices(std::vector<OpenMesh::VertexHandle> meta_mesh_points);
+  bool Delaunay(OpenMesh::VPropHandleT<double> voronoidistance,
+                OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> to_heh,
+                OpenMesh::HPropHandleT<int> multiplicity_heh);
+  void TraverseBoundary(OpenMesh::VertexHandle mvh);
+  int fact(int n);
+  int nCk(int n, int k);
+  std::vector<int> VoronoiGenus();
+  void SetFaceProperties(OpenMesh::FaceHandle bf,
+                         OpenMesh::FaceHandle mf);
+  void CopyFaceProperties(OpenMesh::FaceHandle mfh,
+                          std::vector<OpenMesh::HalfedgeHandle> boundaryborders);
+  OpenMesh::FaceHandle AddFace(std::vector<OpenMesh::VertexHandle> mvh,
+                               std::vector<OpenMesh::HalfedgeHandle> mheh);
+  void AddBoundaries();
+  OpenMesh::HalfedgeHandle FindHalfedge(OpenMesh::HalfedgeHandle bheh);
+
+  void VoronoiBorders();
+  void SetBorderProperties(OpenMesh::HalfedgeHandle bheh,
+                      OpenMesh::HalfedgeHandle mheh);
+
+  void A_StarTriangulation();
+
+  void NaiveVoronoi(std::queue<OpenMesh::VertexHandle> queue,
+                    OpenMesh::VPropHandleT<double> voronoidistance,
+                    OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> to_heh);
+
+  void ColorizeMetaMeshVertices();
+  void ColorizeMetaEdges();
+  void ColorizeVoronoiRegions();
+  void ColorizeBorders();
+
+  void TestHalfedgeConsistency();
+
+  void BoundaryTrace(OpenMesh::HalfedgeHandle mheh, bool mark_trace = false);
+  void SimpleTrace(OpenMesh::HalfedgeHandle mheh, bool cleanup = true,
+                   bool mark_trace = false);
+  void AdvancedTrace(OpenMesh::HalfedgeHandle mheh);
+  std::vector<OpenMesh::HalfedgeHandle> A_StarBidirectional(OpenMesh::HalfedgeHandle bheh0,
+                    OpenMesh::HalfedgeHandle bheh1, bool mark_trace = false,
+                    OpenMesh::HalfedgeHandle mheh
+                    = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle);
+  double Distance(OpenMesh::VertexHandle bvhf, OpenMesh::VertexHandle bvhv);
+  double A_StarHeuristic(OpenMesh::VertexHandle bvhf, OpenMesh::VertexHandle bvhr,
+                           OpenMesh::VertexHandle bvhv);
+  std::vector<OpenMesh::VertexHandle> A_StarNeighbors(OpenMesh::VertexHandle bvh,
+        OpenMesh::HalfedgeHandle mheh,
+        OpenMesh::VPropHandleT<bool> closed_set,
+        OpenMesh::VertexHandle towardsbvh,
+        OpenMesh::HalfedgeHandle bheh = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle);
+  bool ValidA_StarEdge(OpenMesh::HalfedgeHandle bheh,
+                       OpenMesh::HalfedgeHandle mheh);
+  std::vector<OpenMesh::HalfedgeHandle> A_StarReconstructPath(OpenMesh::VertexHandle middle,
+             OpenMesh::VPropHandleT<OpenMesh::VertexHandle> predecessor0,
+             OpenMesh::VPropHandleT<OpenMesh::VertexHandle> predecessor1);
+
+  std::pair<OpenMesh::HalfedgeHandle, OpenMesh::HalfedgeHandle> FindA_StarStartingHalfedges(
+      OpenMesh::HalfedgeHandle mheh, bool verbose = false);
+  void InsertPath(OpenMesh::HalfedgeHandle mheh, std::vector<OpenMesh::HalfedgeHandle> path);
+
+  // Adding an edge
+  OpenMesh::HalfedgeHandle AddMetaEdge(OpenMesh::HalfedgeHandle mheh0,
+                                       OpenMesh::HalfedgeHandle mheh);
+
+  // Rotation
+  void MetaRotation(OpenMesh::EdgeHandle meh);
+
+  // Split
+  // Edges
+  void EdgeSplit(OpenMesh::EdgeHandle meh, OpenMesh::VertexHandle bvh
+             = OpenMesh::PolyConnectivity::InvalidVertexHandle, bool verbose = false);
+  void MetaEdgeSplit(OpenMesh::EdgeHandle meh, OpenMesh::VertexHandle bvh);
+  void OverwriteHalfedgeConnection(OpenMesh::HalfedgeHandle mheh,
+                                   OpenMesh::HalfedgeHandle bheh);
+  // Faces
+  void FaceSplit(OpenMesh::VertexHandle bvh, OpenMesh::HalfedgeHandle
+                 mheh = OpenMesh::PolyConnectivity::InvalidHalfedgeHandle,
+                 bool verbose = false);
+  OpenMesh::HalfedgeHandle FindFaceHalfedge(OpenMesh::VertexHandle bvh);
+  void MetaFaceSplit(OpenMesh::HalfedgeHandle mheh, OpenMesh::VertexHandle bvh);
+
+  // Collapse
+  void BendMetaEdgeForwards(OpenMesh::HalfedgeHandle mheh);
+  void BendMetaEdgeBackwards(OpenMesh::HalfedgeHandle mheh);
+  void RemoveLoop(OpenMesh::HalfedgeHandle mheh);
+  void RemoveSelfLoop(OpenMesh::HalfedgeHandle mheh);
+  int OneRingEuler(OpenMesh::VertexHandle mvh);
+
+  // Relocate
+  void LowLevelRelocate(OpenMesh::VertexHandle mvh, OpenMesh::VertexHandle bvh,
+                        bool verbose = false);
+  void TraverseGroup(OpenMesh::VertexHandle mvh,
+                     OpenMesh::HalfedgeHandle moh,
+                     OpenMesh::VPropHandleT<bool> groupselected);
+  void CompositeRelocate(OpenMesh::VertexHandle mvh, OpenMesh::VertexHandle bvh,
+                         bool verbose = false);
+  void MetaSwap(OpenMesh::VertexHandle mvh0, OpenMesh::VertexHandle mvh1);
+
+  bool debug_hard_stop_ = false;
+  bool initial_triangulation_ = false;
+  bool boundaries_ = false;
+
+  TriMesh* base_mesh_;
+  PolyMesh* meta_mesh_;
+  Utils::Draw draw_;
+
+  OpenMesh::VPropHandleT<OpenMesh::VertexHandle> voronoiID_;
+  OpenMesh::HPropHandleT<OpenMesh::HalfedgeHandle> bhe_border_;
+  OpenMesh::HPropHandleT<OpenMesh::HalfedgeHandle> mhe_border_;
+};
diff --git a/IsotropicRemesher.cc b/IsotropicRemesher.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b2d481d11c25c37a10682791c814e19d32beb146
--- /dev/null
+++ b/IsotropicRemesher.cc
@@ -0,0 +1,1002 @@
+#include "IsotropicRemesher.hh"
+#include <ACG/Geometry/bsp/TriangleBSPT.hh>
+#include <QDebug>
+#include <QGraphicsView>
+#include <algorithm>
+
+// TODO: if you are going to use the Remesher change the output path here
+#define SCREENSHOT_PATH "/home/jschnathmeier/Screenshots"
+
+/*!
+ * \brief IsotropicRemesher::Remesh
+ * \param embedding: embedding object, needs to be triangulated
+ * \param target_length: target edge length as compared to the object diagonal length
+ * \param iterations: number of times the algorithm runs
+ * \param alpha: slack variable for the  upper length limit for splits
+ * \param beta: slack variable for the lower length limit for collapses
+ * \param smtype: smoothing options
+ * \param screenshot: screenshot function; no screenshots if no function
+ * \param limitflips: only do flips that don't increase edge length
+ * \param strtype: straightening options
+ * \param corder: collapse order options
+ */
+void IsotropicRemesher::Remesh(Embedding* embedding,
+                               double target_length,
+                               uint iterations,
+                               double alpha,
+                               double beta,
+                               SmoothingType smtype,
+                               std::function<void (QString, QString, double, double)> screenshot,
+                               bool limitflips,
+                               StraighteningType strtype,
+                               CollapsingOrder corder) {
+  debug_hard_stop_ = false;
+  using namespace PluginFunctions;
+  qDebug() << "Running the Isotropic Remeshing algorithm";
+  auto base_mesh = embedding->GetBaseMesh();
+
+  const double inf = std::numeric_limits<double>::infinity();
+  ACG::Vec3d minCorner = {+inf, +inf, +inf};
+  ACG::Vec3d maxCorner = {-inf, -inf, -inf};
+  for (auto bvh : base_mesh->vertices()) {
+      const auto& p = base_mesh->point(bvh);
+      minCorner.minimize(p);
+      maxCorner.maximize(p);
+  }
+
+  double scale = (maxCorner-minCorner).length();
+  double high = target_length * alpha * scale;
+  double low = high * beta / alpha;
+
+  if (screenshot) {
+    QDir dir(QString(SCREENSHOT_PATH) + "/target" + QString::number(target_length)
+             + "-iterations" + QString::number(iterations));
+    if (!dir.exists()) {
+      dir.mkpath(".");
+    }
+    screenshot(QString("IsoRemesh0_Start"),
+               QString(SCREENSHOT_PATH) + "/target" + QString::number(target_length)
+               + "-iterations" + QString::number(iterations) ,
+               0, 0);
+  }
+  Stopwatch* swatchtotal = new Stopwatch();
+  for (uint i = 0; i < iterations; ++i) {
+    Stopwatch* swatchit = new Stopwatch();
+    Stopwatch* swatchlocal = new Stopwatch();
+    qDebug() << "Iteration" << i+1 << "; Splitting...";
+    Splits(embedding, high);
+    if (debug_hard_stop_) return;
+    embedding->MetaGarbageCollection();
+    embedding->CleanUpBaseMesh();
+    if (screenshot) {
+      screenshot(QString("IsoRemeshIt_") + QString::number(i) + QString(".0Splits"),
+                 QString(SCREENSHOT_PATH) + "/target" + QString::number(target_length)
+                 + "-iterations" + QString::number(iterations) ,
+                 0, 0);
+    }
+    qDebug() << "Time elapsed: " << swatchlocal->Delta() << "ms.";
+    swatchlocal->Reset();
+    qDebug() << "Iteration" << i+1 << "; Collapsing...";
+    Collapses(embedding, low, corder);
+    if (debug_hard_stop_) return;
+    embedding->MetaGarbageCollection();
+    embedding->CleanUpBaseMesh();
+    if (screenshot) {
+      screenshot(QString("IsoRemeshIt_") + QString::number(i) + QString(".1Collapses"),
+                 QString(SCREENSHOT_PATH) + "/target" + QString::number(target_length)
+                 + "-iterations" + QString::number(iterations) ,
+                 0, 0);
+    }
+    qDebug() << "Time elapsed: " << swatchlocal->Delta() << "ms.";
+    swatchlocal->Reset();
+    qDebug() << "Iteration" << i+1 << "; Flipping...";
+    Flips(embedding, limitflips);
+    if (debug_hard_stop_) return;
+    embedding->MetaGarbageCollection();
+    embedding->CleanUpBaseMesh();
+    if (screenshot) {
+      screenshot(QString("IsoRemeshIt_") + QString::number(i) + QString(".2Flips"),
+                 QString(SCREENSHOT_PATH) + "/target" + QString::number(target_length)
+                 + "-iterations" + QString::number(iterations) ,
+                 0, 0);
+    }
+    qDebug() << "Time elapsed: " << swatchlocal->Delta() << "ms.";
+    swatchlocal->Reset();
+    qDebug() << "Iteration" << i+1 << "; Smoothing...";
+    if (strtype == IMPLICIT) {
+      Smoothing(embedding, smtype, true);
+    } else {
+      Smoothing(embedding, smtype, false);
+    }
+    if (debug_hard_stop_) return;
+    embedding->CleanUpBaseMesh();
+    if (screenshot) {
+      screenshot(QString("IsoRemeshIt_") + QString::number(i) + QString(".3Smoothing"),
+                 QString(SCREENSHOT_PATH) + "/target" + QString::number(target_length)
+                 + "-iterations" + QString::number(iterations) ,
+                 0, 0);
+    }
+    qDebug() << "Time elapsed: " << swatchlocal->Delta() << "ms.";
+    swatchlocal->Reset();
+    qDebug() << "Iteration" << i+1 << "; Straightening...";
+    Straightening(embedding, strtype);
+    if (debug_hard_stop_) return;
+    embedding->CleanUpBaseMesh();
+    qDebug() << "Time elapsed: " << swatchlocal->Delta() << "ms.";
+    qDebug() << "Iteration" << i+1 << " finished, time elapsed: "
+             << swatchit->Delta() << "ms.";
+    if (screenshot) {
+      qDebug() << "Iteration" << i+1 << "Screenshot";
+      screenshot(QString("IsoRemeshIt_") + QString::number(i) + QString(".4Straightening"),
+                 QString(SCREENSHOT_PATH) + "/target" + QString::number(target_length)
+                 + "-iterations" + QString::number(iterations) ,
+                 0, 0);
+    }
+  }
+  qDebug() << "Finished " << iterations << " iterations of Isotropic Remeshing"
+              " with target edge length " << target_length * scale;
+  qDebug() << "Time elapsed: " << swatchtotal->Delta() << "ms.";
+}
+
+/*!
+ * \brief IsotropicRemesher::Splits
+ * \param embedding
+ * \param high: split edges longer than this
+ */
+void IsotropicRemesher::Splits(Embedding* embedding, double high) {
+  auto meta_mesh = embedding->GetMetaMesh();
+  std::vector<OpenMesh::EdgeHandle> medges;
+  for (auto meh : meta_mesh->edges()) {
+    medges.push_back(meh);
+  }
+  std::random_shuffle(medges.begin(), medges.end());
+  for (auto meh : medges) {
+    if (embedding->CalculateEdgeLength(meh) > high) {
+      embedding->Split(embedding->MiddleBvh(meh));
+      if (embedding->ErrStatus()) {
+        qDebug() << "Splitting failed for meta edge " << meh.idx();
+        debug_hard_stop_ = true;
+      }
+    }
+    if (meh.idx()%10 == 0) {
+      embedding->CleanUpBaseMesh();
+    }
+  }
+}
+
+/*!
+ * \brief IsotropicRemesher::Collapses
+ * \param embedding
+ * \param low: collapse edges shorter than this
+ * \param corder: collapse ordering (random / optimizing by valence / optimizing by
+ * edge weight heuristic)
+ */
+void IsotropicRemesher::Collapses(Embedding *embedding, double low,
+                                  CollapsingOrder corder) {
+  auto meta_mesh = embedding->GetMetaMesh();
+  OpenMesh::VPropHandleT<bool> collapseblocked;
+  meta_mesh->add_property(collapseblocked, "Blocks collapsing");
+  for (auto mvh : meta_mesh->vertices()) {
+    meta_mesh->property(collapseblocked, mvh) = false;
+  }
+
+  std::vector<OpenMesh::HalfedgeHandle> mhalfedges;
+  for (auto mheh : meta_mesh->halfedges()) {
+    mhalfedges.push_back(mheh);
+  }
+  std::random_shuffle(mhalfedges.begin(), mhalfedges.end());
+  std::multimap<OpenMesh::HalfedgeHandle, double> mhehs;
+  switch(corder) {
+  case RANDOM: {
+    for (auto mheh : mhalfedges) {
+      mhehs.insert(std::make_pair(mheh, 0.0));
+    }
+    break;
+  }
+  case VALENCE: {
+    for (auto mheh : mhalfedges) {
+      double valw = 0.0;
+
+      auto mheh0 = meta_mesh->prev_halfedge_handle(mheh);
+      auto mheh1 = meta_mesh->opposite_halfedge_handle(meta_mesh->next_halfedge_handle(mheh));
+      auto mheho = meta_mesh->opposite_halfedge_handle(mheh);
+      auto mheh2 = meta_mesh->prev_halfedge_handle(mheho);
+      auto mheh3 = meta_mesh->opposite_halfedge_handle(meta_mesh->next_halfedge_handle(mheho));
+
+      auto mvh0 = meta_mesh->from_vertex_handle(mheh);
+      auto mvh1 = meta_mesh->to_vertex_handle(mheh);
+      auto mvh2 = meta_mesh->from_vertex_handle(mheh0);
+      auto mvh3 = meta_mesh->from_vertex_handle(mheh2);
+
+      valw = meta_mesh->valence(mvh0) + meta_mesh->valence(mvh1);
+      if (meta_mesh->from_vertex_handle(mheh0)
+          == meta_mesh->from_vertex_handle(mheh1)) {
+        valw -= meta_mesh->valence(mvh2);
+      }
+      if (meta_mesh->from_vertex_handle(mheh2)
+          == meta_mesh->from_vertex_handle(mheh3)) {
+        valw -= meta_mesh->valence(mvh3);
+      }
+
+      mhehs.insert(std::make_pair(mheh, valw));
+    }
+    break;
+  }
+  case SPLITWEIGHT: {
+    for (auto mheh : mhalfedges) {
+      double splitw = 0.0;
+
+      auto mheh0 = meta_mesh->prev_halfedge_handle(mheh);
+      auto mheh1 = meta_mesh->opposite_halfedge_handle(meta_mesh->next_halfedge_handle(mheh));
+      auto mheho = meta_mesh->opposite_halfedge_handle(mheh);
+      auto mheh2 = meta_mesh->prev_halfedge_handle(mheho);
+      auto mheh3 = meta_mesh->opposite_halfedge_handle(meta_mesh->next_halfedge_handle(mheho));
+
+      auto mvh0 = meta_mesh->from_vertex_handle(mheh);
+
+      int newmvh1edges = static_cast<int>(meta_mesh->valence(mvh0)) - 1;
+      if (meta_mesh->from_vertex_handle(mheh0)
+          == meta_mesh->from_vertex_handle(mheh1)) {
+        splitw -= embedding->MetaHalfedgeWeight(mheh0);
+        newmvh1edges -= 1;
+      }
+      auto mhehiter = meta_mesh->prev_halfedge_handle(
+            meta_mesh->opposite_halfedge_handle(mheh0));
+      while (mhehiter != mheh3) {
+        splitw -= embedding->MetaHalfedgeWeight(mhehiter);
+        mhehiter = meta_mesh->prev_halfedge_handle(
+              meta_mesh->opposite_halfedge_handle(mhehiter));
+      }
+      if (meta_mesh->from_vertex_handle(mheh2)
+          == meta_mesh->from_vertex_handle(mheh3)) {
+        splitw -= embedding->MetaHalfedgeWeight(mheh3);
+        newmvh1edges -= 1;
+      }
+      splitw += newmvh1edges * embedding->MetaHalfedgeWeight(mheh);
+      mhehs.insert(std::make_pair(mheh, splitw));
+    }
+    break;
+  }
+  }
+
+  uint mhehcounter = 0;
+
+  for (auto pair : mhehs) {
+    auto mheh = pair.first;
+    if (meta_mesh->is_valid_handle(mheh)
+        && !meta_mesh->status(mheh).deleted()) {
+      auto meh = meta_mesh->edge_handle(mheh);
+      if (!meta_mesh->property(collapseblocked,
+             meta_mesh->from_vertex_handle(mheh))
+          && embedding->IsCollapseOkay(mheh)
+          && embedding->CalculateEdgeLength(meh) < low) {
+        // If a collapse is about to happen block off the 1-Ring from being
+        // collapsed in this iteration.
+        for (auto mvh : meta_mesh->vv_range(meta_mesh->from_vertex_handle(mheh))) {
+          meta_mesh->property(collapseblocked, mvh) = true;
+        }
+        embedding->Collapse(mheh);
+      }
+    }
+    if (mheh.idx() % 100 == 0) {
+      qDebug() << "Collapsing mheh: " << mhehcounter;
+    }
+    ++mhehcounter;
+  }
+  embedding->CleanUpBaseMesh();
+  embedding->MetaGarbageCollection();//);
+  meta_mesh->remove_property(collapseblocked);
+}
+
+/*!
+ * \brief IsotropicRemesher::Flips
+ * \param embedding
+ * \param limitflips: if this is true flips are only done when they don't increase
+ * edge length  (expensive check)
+ */
+void IsotropicRemesher::Flips(Embedding *embedding, bool limitflips) {
+  auto meta_mesh = embedding->GetMetaMesh();
+  OpenMesh::EPropHandleT<int> flipvalue;
+  meta_mesh->add_property(flipvalue, "EP_flipvalue");
+  std::vector<OpenMesh::EdgeHandle> medges;
+  for (auto meh : meta_mesh->edges()) {
+    meta_mesh->property(flipvalue, meh) = FlipEval(embedding, meh);
+    medges.push_back(meh);
+  }
+  std::random_shuffle(medges.begin(), medges.end());
+  std::multimap<OpenMesh::EdgeHandle, double> mehs;
+  for (auto meh : medges) {
+    mehs.insert(std::make_pair(meh, -embedding->MetaHalfedgeWeight(
+                               meta_mesh->halfedge_handle(meh, 0))));
+  }
+  for (auto mpair : mehs) {
+    auto meh = mpair.first;
+    double newlen, oldlen;
+    if (meta_mesh->property(flipvalue, meh) > 0) {
+      auto v0 = meta_mesh->to_vertex_handle(meta_mesh->halfedge_handle(meh, 0));
+      auto v1 = meta_mesh->to_vertex_handle(meta_mesh->halfedge_handle(meh, 1));
+      auto v2 = meta_mesh->to_vertex_handle(meta_mesh->next_halfedge_handle(
+                                              meta_mesh->halfedge_handle(meh, 0)));
+      auto v3 = meta_mesh->to_vertex_handle(meta_mesh->next_halfedge_handle(
+                                              meta_mesh->halfedge_handle(meh, 1)));
+      bool flip = true;
+      for (auto ee : meta_mesh->ve_range(v0)) {
+        if (meta_mesh->property(flipvalue, ee) > meta_mesh->property(flipvalue, meh)) {
+          flip = false;
+        }
+      }
+      for (auto ee : meta_mesh->ve_range(v1)) {
+        if (meta_mesh->property(flipvalue, ee) > meta_mesh->property(flipvalue, meh)){
+          flip = false;
+        }
+      }
+      if (flip) {
+        if (limitflips) {
+          oldlen = embedding->CalculateEdgeLength(meh);
+        }
+        embedding->Rotate(meh);
+        if (limitflips) {
+          newlen = embedding->CalculateEdgeLength(meh);
+        }
+        if (!limitflips || oldlen >= newlen) {
+          for (auto ee : meta_mesh->ve_range(v0)) {
+            meta_mesh->property(flipvalue, ee) = FlipEval(embedding, ee);
+          }
+          for (auto ee : meta_mesh->ve_range(v1)) {
+            meta_mesh->property(flipvalue, ee) = FlipEval(embedding, ee);
+          }
+          for (auto ee : meta_mesh->ve_range(v2)) {
+            meta_mesh->property(flipvalue, ee) = FlipEval(embedding, ee);
+          }
+          for (auto ee : meta_mesh->ve_range(v3)) {
+            meta_mesh->property(flipvalue, ee) = FlipEval(embedding, ee);
+          }
+        } else {
+          embedding->Rotate(meh);
+        }
+      }
+    }
+    if (meh.idx()%10 == 0) {
+      embedding->CleanUpBaseMesh();
+    }
+  }
+  meta_mesh->remove_property(flipvalue);
+}
+
+/*!
+ * \brief IsotropicRemesher::FlipEval: evaluate whether an edge should be flipped
+ * \param embedding
+ * \param meh
+ * \return the improvement of the meshes' valence deviation if this split
+ * was performed
+ */
+int IsotropicRemesher::FlipEval(Embedding *embedding, OpenMesh::EdgeHandle meh) {
+  auto meta_mesh = embedding->GetMetaMesh();
+  auto heh0 = meta_mesh->halfedge_handle(meh, 0);
+  auto heh1 = meta_mesh->halfedge_handle(meh, 1);
+  auto fv = meta_mesh->to_vertex_handle(heh0);
+  auto lv = meta_mesh->to_vertex_handle(meta_mesh->next_halfedge_handle(heh0));
+  auto bv = meta_mesh->to_vertex_handle(heh1);
+  auto rv = meta_mesh->to_vertex_handle(meta_mesh->next_halfedge_handle(heh1));
+  uint local_objective_old = (meta_mesh->valence(bv)-6)*(meta_mesh->valence(bv)-6)
+          +(meta_mesh->valence(fv)-6)*(meta_mesh->valence(fv)-6)
+          +(meta_mesh->valence(rv)-6)*(meta_mesh->valence(rv)-6)
+          +(meta_mesh->valence(lv)-6)*(meta_mesh->valence(lv)-6);
+  uint local_objective_new = (meta_mesh->valence(bv)-7)*(meta_mesh->valence(bv)-7)
+          +(meta_mesh->valence(fv)-7)*(meta_mesh->valence(fv)-7)
+          +(meta_mesh->valence(rv)-5)*(meta_mesh->valence(rv)-5)
+          +(meta_mesh->valence(lv)-5)*(meta_mesh->valence(lv)-5);
+  int local_improvement = static_cast<int>(local_objective_old - local_objective_new);
+  // Triangles,
+  // Old: (bv, rv, fv), (bv, fv, lv)
+  auto fnold1 = (meta_mesh->point(rv)-meta_mesh->point(bv))
+          %(meta_mesh->point(fv)-meta_mesh->point(bv));
+  auto fnold2 = (meta_mesh->point(fv)-meta_mesh->point(bv))
+          %(meta_mesh->point(lv)-meta_mesh->point(bv));
+  // New: (bv, rv, lv), (fv, lv, rv)
+  auto fnnew1 = (meta_mesh->point(rv)-meta_mesh->point(bv))
+          %(meta_mesh->point(lv)-meta_mesh->point(bv));
+  auto fnnew2 = (meta_mesh->point(lv)-meta_mesh->point(fv))
+          %(meta_mesh->point(rv)-meta_mesh->point(fv));
+
+  bool geometrically_sound = false;
+  if(((fnold1|fnnew1) > 0 || (fnold2|fnnew1) > 0)
+          && ((fnold1|fnnew2) > 0 || (fnold2|fnnew2) > 0))
+      geometrically_sound = true;
+
+  if (local_improvement > 0
+          && embedding->IsRotationOkay(meh)
+          && geometrically_sound) {
+      return local_improvement;
+  }
+  return 0;
+}
+
+/*!
+ * \brief IsotropicRemesher::Smoothing
+ * \param embedding
+ * \param stype: smoothing options; forestfire, vertexweights or vertexdistances
+ * \param shakeup: smoothes vertices twice, moving them away from center first and then
+ * back to it. Not recommended as it is very expensive and doesn't seem to help much.
+ */
+void IsotropicRemesher::Smoothing(Embedding *embedding, SmoothingType stype, bool shakeup) {
+  switch(stype) {
+    case FORESTFIRE: {
+      qDebug() << "Forest Fire Smoothing";
+      SmoothingFF(embedding);
+      break;
+    }
+    case VERTEXWEIGHTS: {
+      qDebug() << "Vertex Weights Smoothing";
+      SmoothingVWD(embedding, VERTEXWEIGHTS, shakeup);
+      break;
+    }
+    case VERTEXDISTANCES:{
+      qDebug() << "Vertex Distances Smoothing";
+      SmoothingVWD(embedding, VERTEXDISTANCES, shakeup);
+      break;
+    }
+  }
+}
+
+/*!
+ * \brief IsotropicRemesher::SmoothingVWD
+ * \param embedding
+ * \param stype
+ * \param shakeup
+ */
+void IsotropicRemesher::SmoothingVWD(Embedding *embedding, SmoothingType stype
+                                     , bool shakeup) {
+  auto meta_mesh = embedding->GetMetaMesh();
+  auto base_mesh = embedding->GetBaseMesh();
+  OpenMesh::VPropHandleT<std::vector<double>> distances;
+  OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> direction;
+  base_mesh->add_property(distances, "Distances from each patch vertex");
+  std::vector<OpenMesh::VertexHandle> mvhlist;
+  std::vector<OpenMesh::VertexHandle *> mvhlist_pointers;
+  for (auto mvh : meta_mesh->vertices()) {
+    mvhlist.push_back(mvh);
+  }
+  std::random_shuffle(mvhlist.begin(), mvhlist.end());
+  uint ctr = 0;
+  for (auto mvh : mvhlist) {
+    if (meta_mesh->is_valid_handle(mvh)
+        && !meta_mesh->status(mvh).deleted()) {
+      if (ctr%10 == 0) {
+        qDebug() << "Smoothing meta vertex " << ctr;
+      }
+      for (auto bvh : base_mesh->vertices()) {
+        base_mesh->property(distances, bvh) = {};
+      }
+      SmoothVertexVWD(embedding, &distances, mvh, stype, shakeup);
+      if (debug_hard_stop_) return;
+      embedding->CleanUpBaseMesh();
+    }
+    ++ctr;
+  }
+  embedding->MetaGarbageCollection();//mvhlist_pointers);
+  base_mesh->remove_property(distances);
+}
+
+/*!
+ * \brief IsotropicRemesher::SmoothVertexVWD
+ * \param embedding
+ * \param distances
+ * \param mvh
+ * \param stype
+ * \param shakeup
+ */
+void IsotropicRemesher::SmoothVertexVWD(Embedding *embedding,
+                        const OpenMesh::VPropHandleT<std::vector<double>>* distances,
+                        OpenMesh::VertexHandle mvh,
+                        SmoothingType stype,
+                        bool shakeup) {
+  const double inf = std::numeric_limits<double>::infinity();
+  auto meta_mesh = embedding->GetMetaMesh();
+  auto base_mesh = embedding->GetBaseMesh();
+  // Ensure the vertex is moved twice in order to clean up the underlying basemesh
+  if (shakeup) {
+    embedding->Relocate(mvh, base_mesh->to_vertex_handle(meta_mesh->property(
+      embedding->mhe_connection_, meta_mesh->opposite_halfedge_handle(
+      meta_mesh->halfedge_handle(mvh)))));
+    embedding->CleanUpBaseMesh();
+  }
+  std::list<OpenMesh::HalfedgeHandle> mih_list;
+  for (auto mhehit : meta_mesh->vih_range(mvh)) {
+    if (meta_mesh->is_valid_handle(mhehit)
+        && !meta_mesh->status(mhehit).deleted()) {
+      mih_list.push_back(mhehit);
+    }
+  }
+  //qDebug() << "Smoothing Vertex " << mvh.idx();
+  uint it = 0;
+  double mindist = inf;
+  auto bvhmin = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  struct cmp {
+    bool operator()(const std::pair<OpenMesh::VertexHandle, std::vector<double>> &a,
+                    const std::pair<OpenMesh::VertexHandle, std::vector<double>> &b) {
+      return a.second.back() > b.second.back();
+    }
+  };
+  std::priority_queue<std::pair<OpenMesh::VertexHandle, std::vector<double>>,
+      std::vector<std::pair<OpenMesh::VertexHandle, std::vector<double>>>, cmp> minheap;
+  for (auto mhehit : mih_list) {
+    //qDebug() << "Measuring sector distances for boundary vertex "
+    //         << meta_mesh->from_vertex_handle(mhehit).idx();
+    if (meta_mesh->from_vertex_handle(mhehit) == meta_mesh->to_vertex_handle(mhehit)) {
+      //qDebug() << "Self Edge";
+      break;
+    }
+    auto mvhit = meta_mesh->from_vertex_handle(mhehit);
+    auto bvhit = meta_mesh->property(embedding->mv_connection_, mvhit);
+    assert(base_mesh->is_valid_handle(bvhit));
+    assert(embedding->IsSectorBorder(bvhit, mih_list));
+    std::queue<OpenMesh::VertexHandle> patchvertices;
+    auto bhehit = meta_mesh->property(embedding->mhe_connection_, mhehit);
+    assert(base_mesh->property(*distances,
+           base_mesh->to_vertex_handle(bhehit)).size() == it);
+    assert(base_mesh->property(embedding->bhe_connection_, bhehit) == mhehit);
+    if (embedding->IsSectorBorder(base_mesh->to_vertex_handle(bhehit), mih_list)) {
+        qDebug() << "The first bheh " << base_mesh->to_vertex_handle(bhehit).idx()
+                 << "of an mheh ends in a sector border outside its patch, this should "
+                    "be impossible.";
+        qDebug() << "The bheh starts with a meta vertex:" << meta_mesh->is_valid_handle(
+                        base_mesh->property(embedding->bv_connection_,
+                        base_mesh->from_vertex_handle(bhehit)));
+        qDebug() << "The bheh ends in a meta vertex:" << meta_mesh->is_valid_handle(
+                        base_mesh->property(embedding->bv_connection_,
+                        base_mesh->to_vertex_handle(bhehit)));
+        qDebug() << "The bheh ends in:" << base_mesh->property(embedding->bv_connection_,
+                     base_mesh->to_vertex_handle(bhehit)).idx() << " the middle vertex "
+                     "of the patch is:" << mvh.idx();
+    }
+    base_mesh->property(*distances, base_mesh->to_vertex_handle(bhehit)).push_back(
+        base_mesh->calc_edge_length(bhehit));
+    minheap.push(std::make_pair(base_mesh->to_vertex_handle(bhehit),
+                 base_mesh->property(*distances, base_mesh->to_vertex_handle(bhehit))));
+    // patchvertices is now populated by vertices inside the patch surrounding the seed
+    // meta vertex
+    //qDebug() << "Distance Scoring Loop";
+    while(!minheap.empty()) {
+      auto bvhcurr = minheap.top().first;
+      minheap.pop();
+
+      bool borderparity = (meta_mesh->is_boundary(mvh) == base_mesh->is_boundary(bvhcurr));
+
+      //qDebug() << "Distance Scoring";
+      if (stype == VERTEXWEIGHTS) {
+        auto currdist = DistanceScoreVW(embedding, distances, bvhcurr,
+                                        static_cast<uint>(mih_list.size()));
+        if (borderparity && currdist < mindist) {
+          mindist = currdist;
+          bvhmin = bvhcurr;
+        }
+      } else if (stype == VERTEXDISTANCES) {
+        auto currdist = DistanceScoreVD(embedding, distances, bvhcurr,
+                                        static_cast<uint>(mih_list.size()));
+        if (borderparity && currdist < mindist) {
+          mindist = currdist;
+          bvhmin = bvhcurr;
+        }
+      } else {
+        qFatal("Called smoothing with invalid parameters.");
+        return;
+      }
+
+      auto bvhcurrdistprop = base_mesh->property(*distances, bvhcurr);
+      if (bvhcurrdistprop.size() < it+1) {
+        qDebug() << "Iteration " << it << ", Vertex " << bvhcurr.idx()
+                 << "'s distance property only has size "
+                 << bvhcurrdistprop.size();
+        qDebug() << "The vertex is a meta vertex:"
+                 << meta_mesh->is_valid_handle(base_mesh->property(
+                                                 embedding->bv_connection_, bvhcurr));
+        qDebug() << "The vertex is a sector border:"
+                 << embedding->IsSectorBorder(bvhcurr);
+        qDebug() << "The vertex is a sector border (with exceptions):"
+                 << embedding->IsSectorBorder(bvhcurr, mih_list);
+        base_mesh->status(bvhcurr).set_selected(true);
+        embedding->MarkVertex(bvhcurr);
+        qDebug() << "Marking the middle of the patch";
+        embedding->MarkVertex(meta_mesh->property(embedding->mv_connection_, mvh));
+        debug_hard_stop_ = true;
+        return;
+      }
+
+      //qDebug() << "Finding Neighbors";
+      // Find neighbors, limited by sector border
+      for (auto boheh : base_mesh->voh_range(bvhcurr)) {
+        if (!embedding->IsSectorBorder(base_mesh->to_vertex_handle(boheh), mih_list)) {
+          if (base_mesh->property(*distances, base_mesh->to_vertex_handle(boheh)).size() < it+1) {
+            base_mesh->property(*distances, base_mesh->to_vertex_handle(boheh)).push_back(
+              base_mesh->property(*distances, bvhcurr).at(it)
+                  + base_mesh->calc_edge_length(boheh));
+            assert(base_mesh->property(*distances, base_mesh->to_vertex_handle(boheh)).size()
+                   == it+1);
+            minheap.push(std::make_pair(base_mesh->to_vertex_handle(boheh),
+                         base_mesh->property(*distances, base_mesh->to_vertex_handle(boheh))));
+          } else if (base_mesh->property(*distances, base_mesh->to_vertex_handle(boheh)).at(it) >
+              base_mesh->property(*distances, bvhcurr).at(it) + base_mesh->calc_edge_length(boheh)) {
+            base_mesh->property(*distances, base_mesh->to_vertex_handle(boheh)).at(it)
+                = base_mesh->property(*distances, bvhcurr).at(it) + base_mesh->calc_edge_length(boheh);
+            minheap.push(std::make_pair(base_mesh->to_vertex_handle(boheh),
+                         base_mesh->property(*distances, base_mesh->to_vertex_handle(boheh))));
+          }
+        }
+      }
+    }
+    ++it;
+  }
+  // The new, faster relocate method sadly cannot yet handle complex patches.
+  // Make sure to use the old method if a patch is complex, so check that here.
+  bool simple = true;
+  for (auto moh : meta_mesh->voh_range(mvh)) {
+    auto mvhit = meta_mesh->to_vertex_handle(moh);
+    if (mvhit == mvh) {
+      simple = false;
+    }
+  }
+  if (simple) {
+    embedding->Relocate(mvh, bvhmin);
+  } else {
+    embedding->Relocate(mvh, bvhmin, false);
+  }
+  if (embedding->ErrStatus()) {
+    qDebug() << "Relocation failed for vertex " << mih_list.front().idx();
+    debug_hard_stop_ = true;
+  }
+}
+
+/*!
+ * \brief IsotropicRemesher::DistanceScoreVW
+ * \param embedding
+ * \param distances
+ * \param bvh
+ * \param numv
+ * \return the distance score for vertex weight smoothing; the sum of the quadratic
+ * distances from the patch vertices (minimize this)
+ */
+double IsotropicRemesher::DistanceScoreVW(Embedding *embedding,
+                          const OpenMesh::VPropHandleT<std::vector<double>>* distances,
+                          OpenMesh::VertexHandle bvh, uint numv) {
+  const double inf = std::numeric_limits<double>::infinity();
+  auto base_mesh = embedding->GetBaseMesh();
+  if (base_mesh->property(*distances, bvh).size() < numv) {
+    return inf;
+  }
+  double retval = 0;
+  for (double num : base_mesh->property(*distances, bvh)) {
+    retval += num*num;
+  }
+  if (base_mesh->is_valid_handle(base_mesh->property(embedding->bsplithandle_, bvh))) {
+    retval *= 2;
+  }
+  return retval;
+}
+
+/*!
+ * \brief IsotropicRemesher::DistanceScoreVD
+ * \param embedding
+ * \param distances
+ * \param bvh
+ * \param numv
+ * \return the distance score for vertex distance smoothing; returns the highest distance
+ * bvh has from any of the patch vertices; (minimize this)
+ */
+double IsotropicRemesher::DistanceScoreVD(Embedding *embedding,
+                          const OpenMesh::VPropHandleT<std::vector<double>>* distances,
+                          OpenMesh::VertexHandle bvh, uint numv) {
+  const double inf = std::numeric_limits<double>::infinity();
+  auto base_mesh = embedding->GetBaseMesh();
+  if (base_mesh->property(*distances, bvh).size() < numv) {
+    return inf;
+  }
+  double retval = 0;
+  for (double num : base_mesh->property(*distances, bvh)) {
+    if (num > retval) {
+      retval = num;
+    }
+  }
+  if (base_mesh->is_valid_handle(base_mesh->property(embedding->bsplithandle_, bvh))) {
+    retval *= 2;
+  }
+  return retval;
+}
+
+/*!
+ * \brief IsotropicRemesher::SmoothingFF
+ * \param embedding
+ */
+void IsotropicRemesher::SmoothingFF(Embedding *embedding) {
+  const double inf = std::numeric_limits<double>::infinity();
+  auto meta_mesh = embedding->GetMetaMesh();
+  auto base_mesh = embedding->GetBaseMesh();
+  OpenMesh::VPropHandleT<double> distance;
+  OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> direction;
+  base_mesh->add_property(distance, "Distance from the patch border");
+  base_mesh->add_property(direction, "Direction to aquire first neighbors");
+  std::queue<OpenMesh::VertexHandle> mvhlist;
+  for (auto mvh : meta_mesh->vertices()) {
+    mvhlist.push(mvh);
+  }
+  while (!mvhlist.empty()) {
+    auto mvh = mvhlist.front();
+    mvhlist.pop();
+    if (meta_mesh->is_valid_handle(mvh)
+        && !meta_mesh->status(mvh).deleted()) {
+      if (mvh.idx()%10 == 0) {
+        qDebug() << "Smoothing meta vertex " << mvh.idx();
+      }
+      for (auto bvh : base_mesh->vertices()) {
+        base_mesh->property(distance, bvh) = inf;
+        base_mesh->property(direction, bvh) =
+            OpenMesh::PolyConnectivity::InvalidHalfedgeHandle;
+      }
+      std::list<OpenMesh::HalfedgeHandle> mih_list;
+      for (auto mhehit : meta_mesh->vih_range(mvh)) {
+        mih_list.push_back(mhehit);
+      }
+      SmoothVertexFF(embedding, distance, direction, mih_list);
+      if (debug_hard_stop_) return;
+      embedding->CleanUpBaseMesh();
+      embedding->MetaGarbageCollection();
+    }
+  }
+  base_mesh->remove_property(distance);
+  base_mesh->remove_property(direction);
+}
+
+/*!
+ * \brief IsotropicRemesher::SmoothVertexFF
+ * \param embedding
+ * \param distance: property used for smoothing
+ * \param direction: property used for smoothing
+ * \param mih_list: list of incoming meta halfedges towards the center of the patch
+ */
+void IsotropicRemesher::SmoothVertexFF(Embedding *embedding,
+                        OpenMesh::VPropHandleT<double> distance,
+                        OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> direction,
+                        std::list<OpenMesh::HalfedgeHandle> mih_list) {
+  const double inf = std::numeric_limits<double>::infinity();
+  auto meta_mesh = embedding->GetMetaMesh();
+  auto base_mesh = embedding->GetBaseMesh();
+  auto mvh = meta_mesh->to_vertex_handle(mih_list.front());
+  struct cmp {
+    bool operator()(const std::pair<OpenMesh::VertexHandle, double> &a,
+                    const std::pair<OpenMesh::VertexHandle, double> &b) {
+      return a.second > b.second;
+    }
+  };
+  std::priority_queue<std::pair<OpenMesh::VertexHandle, double>,
+      std::vector<std::pair<OpenMesh::VertexHandle, double>>, cmp> minheap;
+
+  for (auto mhehit : mih_list) {
+    auto mheprev = meta_mesh->prev_halfedge_handle(mhehit);
+    while (meta_mesh->from_vertex_handle(mheprev) != mvh) {
+      auto bhehit = meta_mesh->property(embedding->mhe_connection_, mheprev);
+      assert(bhehit.is_valid());
+      assert(base_mesh->is_valid_handle(bhehit));
+      auto bvhit = base_mesh->from_vertex_handle(bhehit);
+      base_mesh->property(distance, bvhit) = 0.0;
+      base_mesh->property(direction, bvhit) = bhehit;
+      minheap.push(std::make_pair(bvhit, 0.0));
+      while (base_mesh->property(embedding->next_heh_, bhehit).is_valid()) {
+        bhehit = base_mesh->property(embedding->next_heh_, bhehit);
+        bvhit = base_mesh->from_vertex_handle(bhehit);
+        base_mesh->property(distance, bvhit) = 0.0;
+        base_mesh->property(direction, bvhit) = bhehit;
+        minheap.push(std::make_pair(bvhit, 0.0));
+      }
+      mheprev = meta_mesh->prev_halfedge_handle(mheprev);
+    }
+  }
+
+  OpenMesh::VertexHandle middle = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  double maxdist = -inf;
+
+  while (!minheap.empty()) {
+    auto bvcurr = minheap.top().first;
+    auto neighbors = ClosedNeighborhoodFF(embedding, bvcurr, distance, direction, mih_list);
+    if (!neighbors.empty()) {
+      for (auto neighbor : neighbors) {
+        //base_mesh->status(base_mesh->find_halfedge(bvcurr, neighbor)).set_selected(true);
+        minheap.push(std::make_pair(neighbor, base_mesh->property(distance, neighbor)));
+      }
+    }
+    double newdist = minheap.top().second;
+    if (base_mesh->is_valid_handle(base_mesh->property(embedding->bsplithandle_,
+                                                       minheap.top().first))) {
+      newdist *= 2;
+    }
+    if (maxdist < newdist) {
+      maxdist = newdist;
+      middle = minheap.top().first;
+    }
+    minheap.pop();
+  }
+
+  assert(middle.is_valid());
+
+  embedding->Relocate(meta_mesh->to_vertex_handle(mih_list.front()), middle);
+  if (embedding->ErrStatus()) {
+    qDebug() << "Relocation failed for vertex " << mih_list.front().idx();
+    debug_hard_stop_ = true;
+  }
+  //qDebug() << "Finished Relocating";
+}
+
+/*!
+ * \brief IsotropicRemesher::ClosedNeighborhoodFF
+ * \param embedding
+ * \param bvh
+ * \param distance
+ * \param direction
+ * \param mih_list
+ * \return the vertices in the neighborhood of bvh, considering patch borders
+ */
+std::vector<OpenMesh::VertexHandle> IsotropicRemesher::ClosedNeighborhoodFF(
+    Embedding* embedding, OpenMesh::VertexHandle bvh,
+    OpenMesh::VPropHandleT<double> distance,
+    OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> direction,
+    std::list<OpenMesh::HalfedgeHandle> mih_list) {
+  auto base_mesh = embedding->GetBaseMesh();
+  auto meta_mesh = embedding->GetMetaMesh();
+  std::vector<OpenMesh::VertexHandle> retval;
+  auto bdistance = base_mesh->property(distance, bvh);
+  auto mvh = meta_mesh->to_vertex_handle(mih_list.front());
+  if (embedding->IsSectorBorder(bvh, mih_list) ||
+      (base_mesh->property(embedding->bv_connection_, bvh).is_valid()
+       && base_mesh->property(embedding->bv_connection_, bvh) != mvh)) {
+    auto bheh0 = base_mesh->property(direction, bvh);
+    if (!base_mesh->is_valid_handle(bheh0)) {
+      bheh0 = base_mesh->halfedge_handle(bvh);
+    }
+    auto bhehit = base_mesh->opposite_halfedge_handle(base_mesh->prev_halfedge_handle(bheh0));
+    auto start = bhehit;
+    do {
+      auto blength = base_mesh->calc_edge_length(bhehit);
+      auto bvdis = base_mesh->property(distance, bvh);
+      auto itdis = base_mesh->property(distance, base_mesh->to_vertex_handle(bhehit));
+      if (bvdis + blength < itdis) {
+        base_mesh->property(distance, base_mesh->to_vertex_handle(bhehit)) = bvdis + blength;
+        retval.push_back(base_mesh->to_vertex_handle(bhehit));
+      }
+      bhehit = base_mesh->opposite_halfedge_handle(base_mesh->prev_halfedge_handle(bhehit));
+    } while(start != bhehit
+            && !embedding->IsSectorBorder(base_mesh->to_vertex_handle(bhehit), mih_list));
+  } else {
+    for (auto bvhit : base_mesh->vv_range(bvh)) {
+      if (!embedding->IsSectorBorder(bvhit, mih_list)) {
+        auto blength = base_mesh->calc_edge_length(base_mesh->find_halfedge(bvhit, bvh));
+        auto itdistance = base_mesh->property(distance, bvhit);
+        if (bdistance + blength < itdistance) {
+          itdistance = bdistance + blength;
+          base_mesh->property(distance, bvhit) = itdistance;
+          retval.push_back(bvhit);
+        }
+      }
+    }
+  }
+  return retval;
+}
+
+/*!
+ * \brief IsotropicRemesher::Straightening
+ * \param embedding
+ * \param strtype
+ */
+void IsotropicRemesher::Straightening(Embedding *embedding, StraighteningType strtype) {
+  auto meta_mesh = embedding->GetMetaMesh();
+  auto base_mesh = embedding->GetBaseMesh();
+  uint ctr = 0;
+  switch(strtype) {
+  case NONE: {
+    break;
+  }
+  case IMPLICIT: {
+    // Straightening handled in smoothing by moving vertices twice.
+    break;
+  }
+  case SIMPLE: {
+    // Straighten edgewise
+    std::vector<OpenMesh::EdgeHandle> medges;
+    for (auto meh : meta_mesh->edges()) {
+      medges.push_back(meh);
+    }
+    std::random_shuffle(medges.begin(), medges.end());
+    for (auto meh : medges) {
+      if (ctr%100 == 0) {
+        qDebug() << "Straightening edge " << ctr;
+      }
+      embedding->Retrace(meta_mesh->halfedge_handle(meh, 0));
+      ++ctr;
+      if (ctr%20 == 0) {
+        embedding->CleanUpBaseMesh();
+      }
+    }
+    break;
+  }
+  case PATCHWISE: {
+  // Straighten patchwise
+    std::vector<OpenMesh::VertexHandle> mverts;
+    for (auto mvh : meta_mesh->vertices()) {
+      mverts.push_back(mvh);
+    }
+    std::random_shuffle(mverts.begin(), mverts.end());
+    for (auto mvh : mverts) {
+      if (ctr%10 == 0) {
+        qDebug() << "Straightening vertex " << ctr;
+      }
+      if (!meta_mesh->status(mvh).deleted()) {
+        if (embedding->ErrStatus()) {
+          debug_hard_stop_  = true;
+          return;
+        }
+
+        // Non-original vertices may slip away upon cleanup if the patch around them
+        // is cleared. In that case cleanup that patch edgewise
+        if (base_mesh->is_valid_handle(base_mesh->property(embedding->bsplithandle_,
+              meta_mesh->property(embedding->mv_connection_, mvh)))) {
+          for (auto moh : meta_mesh->voh_range(mvh)) {
+            embedding->Retrace(moh);
+          }
+        } else {
+          enum facetype{noselfedgesdisc, selfedgesorhighergenus, boundary};
+          facetype ft = noselfedgesdisc;
+          for (auto moh : meta_mesh->voh_range(mvh)) {
+            auto mvhit = meta_mesh->to_vertex_handle(moh);
+            if (mvhit == mvh) {
+              ft = selfedgesorhighergenus;
+            }
+            if (meta_mesh->is_boundary(meta_mesh->to_vertex_handle(moh))
+                && ft == noselfedgesdisc) {
+              ft = boundary;
+            }
+          }
+          switch(ft) {
+          case noselfedgesdisc: {
+            for (auto moh : meta_mesh->voh_range(mvh)) {
+              embedding->RemoveMetaEdgeFromBaseMesh(moh);
+            }
+            embedding->BaseGarbageCollection();
+            for (auto moh : meta_mesh->voh_range(mvh)) {
+              embedding->Trace(moh);
+            }
+            break;
+          }
+          case boundary: {
+            for (auto moh : meta_mesh->voh_range(mvh)) {
+              embedding->RemoveMetaEdgeFromBaseMesh(moh);
+            }
+            embedding->BaseGarbageCollection();
+            auto mheh0 = meta_mesh->halfedge_handle(mvh);
+            auto mheh1 = meta_mesh->prev_halfedge_handle(mheh0);
+            embedding->Trace(mheh0);
+            if (mheh0 != mheh1) {
+              embedding->Trace(mheh1);
+            }
+            for (auto moh : meta_mesh->voh_range(mvh)) {
+              if (moh != mheh0 && moh != mheh1) {
+                embedding->Trace(moh);
+              }
+            }
+            break;
+          }
+          case selfedgesorhighergenus: {
+            for (auto moh : meta_mesh->voh_range(mvh)) {
+              embedding->Retrace(moh);
+            }
+            break;
+          }
+          }
+        }
+      }
+      if (ctr%20 == 0) {
+        embedding->CleanUpBaseMesh();
+      }
+      ++ctr;
+    }
+    break;
+  }
+  }
+}
diff --git a/IsotropicRemesher.hh b/IsotropicRemesher.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2c531f45bfe6374414132f0a4542ae14e2246d12
--- /dev/null
+++ b/IsotropicRemesher.hh
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
+#include <ObjectTypes/PolyMesh/PolyMesh.hh>
+#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
+#include <OpenFlipper/common/Types.hh>
+#include "Embedding.hh"
+#include "Stopwatch.hh"
+
+#include <functional>
+#include <map>
+
+/*!
+ * \brief The IsotropicRemesher class
+ */
+class IsotropicRemesher {
+public:
+  IsotropicRemesher() {}
+  ~IsotropicRemesher() {}
+
+  enum SmoothingType{FORESTFIRE, VERTEXWEIGHTS, VERTEXDISTANCES};
+  enum StraighteningType{NONE, IMPLICIT, SIMPLE, PATCHWISE};
+  enum CollapsingOrder{RANDOM, VALENCE, SPLITWEIGHT};
+
+  void Remesh(Embedding* embedding,
+              double target_length,
+              uint iterations = 1,
+              double alpha = 4.0/3.0,
+              double beta = 4.0/5.0,
+              SmoothingType smtype = VERTEXWEIGHTS,
+              std::function<void (QString, QString, double, double)> screenshot = {},
+              bool limitflips = false,
+              StraighteningType strtype = PATCHWISE,
+              CollapsingOrder corder = SPLITWEIGHT);
+  bool DebugStopStatus() {return debug_hard_stop_;}
+
+private:
+  void Splits(Embedding* embedding, double high);
+  void Collapses(Embedding* embedding, double low, CollapsingOrder corder = SPLITWEIGHT);
+  void Flips(Embedding* embedding, bool limitflips = false);
+  int FlipEval(Embedding* embedding, OpenMesh::EdgeHandle meh);
+  void Smoothing(Embedding* embedding, SmoothingType stype = FORESTFIRE, bool shakeup = true);
+  void SmoothingVWD(Embedding* embedding, SmoothingType stype, bool shakeup = true);
+  void SmoothVertexVWD(Embedding* embedding,
+                    const OpenMesh::VPropHandleT<std::vector<double>>* distances,
+                    OpenMesh::VertexHandle mvh,
+                    SmoothingType stype,
+                    bool shakeup = true);
+  double DistanceScoreVW(Embedding* embedding,
+                         const OpenMesh::VPropHandleT<std::vector<double>>* distances,
+                         OpenMesh::VertexHandle bvh, uint numv);
+  double DistanceScoreVD(Embedding* embedding,
+                         const OpenMesh::VPropHandleT<std::vector<double>>* distances,
+                         OpenMesh::VertexHandle bvh, uint numv);
+  void SmoothingFF(Embedding* embedding);
+  void SmoothVertexFF(Embedding* embedding, OpenMesh::VPropHandleT<double> distance,
+                    OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> direction,
+                    std::list<OpenMesh::HalfedgeHandle> mih_list);
+  std::vector<OpenMesh::VertexHandle> ClosedNeighborhoodFF(Embedding* embedding,
+       OpenMesh::VertexHandle bvh, OpenMesh::VPropHandleT<double> distance,
+       OpenMesh::VPropHandleT<OpenMesh::HalfedgeHandle> direction,
+       std::list<OpenMesh::HalfedgeHandle> mih_list);
+  void Straightening(Embedding* embedding, StraighteningType strtype = PATCHWISE);
+
+
+  bool debug_hard_stop_ = false;
+};
diff --git a/MetaMeshPlugin.cc b/MetaMeshPlugin.cc
index fe23121887f280b737060aff4eb8667d0edada7b..a854c7a495302a5800084bc573da48e66c877174 100644
--- a/MetaMeshPlugin.cc
+++ b/MetaMeshPlugin.cc
@@ -4,26 +4,761 @@
 #include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
 
 #include <OpenFlipper/BasePlugin/PluginFunctions.hh>
+#include <iostream>
+#include <QDebug>
 
+
+/*!
+ * \brief MetaMeshPlugin::initializePlugin
+ */
 void MetaMeshPlugin::initializePlugin()
 {
 	widget_ = new MetaMeshToolbox();
 	
-	connect(widget_->button_dispatch, SIGNAL(clicked()), this, SLOT(dispatch()));
+  connect(widget_->button_triangulate_, SIGNAL(clicked()), this, SLOT(triangulate()));
+  connect(widget_->button_randomize_ratio_, SIGNAL(clicked()), this,
+          SLOT(randomizeratio()));
+  connect(widget_->button_randomize_total_, SIGNAL(clicked()), this,
+          SLOT(randomizetotal()));
+  connect(widget_->button_run_test_, SIGNAL(clicked()), this, SLOT(test_meta_mesh()));
+  connect(widget_->button_retrace_, SIGNAL(clicked()), this, SLOT(retrace()));
+  connect(widget_->button_rotate_, SIGNAL(clicked()), this, SLOT(rotate()));
+  connect(widget_->button_split_, SIGNAL(clicked()), this, SLOT(split()));
+  connect(widget_->button_collapse_, SIGNAL(clicked()), this, SLOT(collapse()));
+  connect(widget_->button_relocate_, SIGNAL(clicked()), this, SLOT(relocate()));
+  connect(widget_->button_remesh_, SIGNAL(clicked()), this, SLOT(remesh()));
+  connect(widget_->button_dummy1_, SIGNAL(clicked()), this, SLOT(DummySlotFunction1()));
+  connect(widget_->button_dummy2_, SIGNAL(clicked()), this, SLOT(DummySlotFunction2()));
+  connect(widget_->button_next_, SIGNAL(clicked()), this, SLOT(nxt()));
+  connect(widget_->button_opp_, SIGNAL(clicked()), this, SLOT(opp()));
+  connect(widget_->button_prev_, SIGNAL(clicked()), this, SLOT(prv()));
 	
 	emit addToolbox("MetaMesh", widget_);
+
+  printf("Trying to create MetaMesh object\n");
+  embedding_ = new Embedding();
+  testing_ = new Testing(embedding_);
+  printf("Created MetaMesh object\n");
 }
 
+/*!
+ * \brief MetaMeshPlugin::pluginsInitialized
+ */
 void MetaMeshPlugin::pluginsInitialized()
 {
+  printf("Plugins Initialized\n");
+}
 
+/*!
+ * \brief MetaMeshPlugin::slotObjectUpdated
+ * \param _identifier
+ */
+void MetaMeshPlugin::slotObjectUpdated(int _identifier)
+{
+  if (_identifier == meta_mesh_id_ && !selection_mutex_
+      && widget_->checkbox_copy_markings_->checkState() == Qt::Checked) {
+    selection_mutex_ = true;
+    copyselection();
+    selection_mutex_ = false;
+  }
 }
 
-void MetaMeshPlugin::dispatch()
+/*!
+ * \brief MetaMeshPlugin::triangulate triangulates a meta mesh from marked vertices on the currently
+ * loaded mesh
+ */
+void MetaMeshPlugin::triangulate()
 {
-	using namespace PluginFunctions;
-	for (auto* o : objects(TARGET_OBJECTS, DATA_TRIANGLE_MESH)) {
-		TriMesh& mesh = *triMesh(o);
-		// ...
-	}
+  qDebug() << "Triangulate Button pressed";
+  using namespace PluginFunctions;
+  for (auto* o : objects(TARGET_OBJECTS, DATA_TRIANGLE_MESH)) {
+    emit addEmptyObject(DATA_POLY_MESH, meta_mesh_id_);
+    base_mesh_id_ = o->id();
+    assert(meta_mesh_id_ >= 0);
+    meta_mesh_ = polyMesh(meta_mesh_id_);
+    meta_mesh_->clear();
+    TriMesh& base_mesh = *triMesh(o);
+
+    qDebug() << "Call triangulation on MetaMesh  in object loop";
+    embedding_->SelectionTriangulation(base_mesh, *meta_mesh_);
+    meta_mesh_->update_normals();
+    embedding_->GetBaseMesh()->update_normals();
+
+    //meta_mesh_->clear();
+    for (auto bvh : embedding_->GetBaseMesh()->vertices()) {
+      embedding_->GetBaseMesh()->status(bvh).set_selected(false);
+    }
+    emit updatedObject(o->id(), UPDATE_ALL);
+    qDebug() << "Exited InitialTriangulation function";
+  }
+  if (embedding_->ErrStatus()) {
+    widget_->checkbox_copy_markings_->setCheckState(Qt::Unchecked);
+  }
+  calculateoffset();
+  addoffset();
+  embedding_->ColorizeMetaMesh();
+  emit updatedObject(base_mesh_id_, UPDATE_ALL);
+  emit updatedObject(meta_mesh_id_, UPDATE_ALL);
+}
+
+/*!
+ * \brief MetaMeshPlugin::randomizetotal randomize an absolute number of vertices
+ */
+void MetaMeshPlugin::randomizetotal() {
+  randomize(Embedding::RandomType::TOTAL);
+}
+
+/*!
+ * \brief MetaMeshPlugin::randomizeratio randomize a ratio of vertices
+ */
+void MetaMeshPlugin::randomizeratio() {
+  randomize(Embedding::RandomType::RATIO);
+}
+
+/*!
+ * \brief MetaMeshPlugin::randomize randomize meta mesh vertices and triangulate
+ * \param type
+ */
+void MetaMeshPlugin::randomize(Embedding::RandomType type) {
+  using namespace PluginFunctions;
+  for (auto* o : objects(TARGET_OBJECTS, DATA_TRIANGLE_MESH)) {
+    emit addEmptyObject(DATA_POLY_MESH, meta_mesh_id_);
+    base_mesh_id_ = o->id();
+    assert(meta_mesh_id_ >= 0);
+    meta_mesh_ = polyMesh(meta_mesh_id_);
+    meta_mesh_->clear();
+    TriMesh& base_mesh = *triMesh(o);
+
+    qDebug() << "Call triangulation on MetaMesh  in object loop";
+    embedding_->RandomTriangulation(base_mesh, *meta_mesh_,
+         widget_->input_ratio_->value(), type);
+    meta_mesh_->update_normals();
+    embedding_->GetBaseMesh()->update_normals();
+    emit updatedObject(o->id(), UPDATE_ALL);
+    qDebug() << "Exited InitialTriangulation function";
+  }
+  if (embedding_->ErrStatus()) {
+    widget_->checkbox_copy_markings_->setCheckState(Qt::Unchecked);
+  }
+  calculateoffset();
+  addoffset();
+  embedding_->ColorizeMetaMesh();
+  emit updatedObject(meta_mesh_id_, UPDATE_ALL);
+}
+
+/*!
+ * \brief MetaMeshPlugin::test_meta_mesh run selected test
+ */
+void MetaMeshPlugin::test_meta_mesh() {
+  using namespace PluginFunctions;
+  if (embedding_->GetMetaMesh() == nullptr) {
+    qDebug() << "Run some triangulation before calling tests on the meta mesh.";
+    return;
+  }
+
+  preoperation();
+
+  switch (widget_->combo_box_tests_->currentIndex()) {
+  case 0: {
+    testing_->DisplayMeshInformation();
+    break;
+  }
+  case 1: {
+    testing_->MeshTests();
+    break;
+  }
+  case 2: {
+    testing_->OperationTests();
+    break;
+  }
+  case 3: {
+    testing_->TestFlips();
+    break;
+  }
+  case 4: {
+    testing_->TestSplits();
+    break;
+  }
+  case 5: {
+    testing_->TestCollapses();
+    break;
+  }
+  case 6: {
+    testing_->TestRelocation();
+  }
+  }
+  if (embedding_->ErrStatus()) {
+    widget_->checkbox_copy_markings_->setCheckState(Qt::Unchecked);
+    embedding_->GetBaseMesh()->update_normals();
+    emit updatedObject(base_mesh_id_, UPDATE_ALL);
+    addoffset();
+    embedding_->ColorizeMetaMesh();
+    return;
+  }
+
+  postoperation(UPDATE_ALL, UPDATE_ALL);
+}
+
+/*!
+ * \brief MetaMeshPlugin::retrace retrace the selected meta edges / halfedges. None selected means
+ * everything will be retraced.
+ */
+void MetaMeshPlugin::retrace() {
+  using namespace PluginFunctions;
+  preoperation();
+  auto retracequeue = GetHalfedgeSelection();
+  bool emptyqueue = retracequeue.empty();
+  while (!retracequeue.empty()) {
+    embedding_->Retrace(retracequeue.front(), true);
+    retracequeue.pop();
+  }
+
+  // If nothing is selected retrace everything
+  if (emptyqueue) {
+    for (auto meh : embedding_->GetMetaMesh()->edges()) {
+      auto mheh = embedding_->GetMetaMesh()->halfedge_handle(meh, 0);
+      embedding_->Retrace(mheh);
+    }
+  }
+
+  postoperation(UPDATE_ALL, UPDATE_TOPOLOGY);
+}
+
+/*!
+ * \brief MetaMeshPlugin::rotate rotate all selected meta edges and halfedges
+ */
+void MetaMeshPlugin::rotate() {
+  using namespace PluginFunctions;
+  preoperation();
+  auto rotationqueue = GetHalfedgeSelection();
+
+  while (!rotationqueue.empty()) {
+    embedding_->Rotate(embedding_->GetMetaMesh()->edge_handle(rotationqueue.front()));
+    rotationqueue.pop();
+  }
+
+  postoperation(UPDATE_ALL, UPDATE_TOPOLOGY);
+}
+
+/*!
+ * \brief MetaMeshPlugin::split split faces and edges at selected base vertices
+ */
+void MetaMeshPlugin::split() {
+  using namespace PluginFunctions;
+  preoperation();
+
+  for (auto bvh : embedding_->GetBaseMesh()->vertices()) {
+    if (embedding_->GetBaseMesh()->status(bvh).selected()) {
+      embedding_->Split(bvh);
+      embedding_->GetBaseMesh()->status(bvh).set_selected(false);
+    }
+  }
+
+  postoperation(UPDATE_TOPOLOGY, UPDATE_TOPOLOGY);
+}
+
+/*!
+ * \brief MetaMeshPlugin::collapse collapse all selected meta edges and halfedges
+ */
+void MetaMeshPlugin::collapse() {
+  using namespace PluginFunctions;
+  preoperation();
+
+  auto collapsequeue = GetHalfedgeSelection();
+  while (!collapsequeue.empty()) {
+    embedding_->Collapse(collapsequeue.front(), true);
+    collapsequeue.pop();
+  }
+
+  postoperation(UPDATE_TOPOLOGY, UPDATE_TOPOLOGY);
+}
+
+/*!
+ * \brief MetaMeshPlugin::relocate relocate selected meta vertex to selected non-meta vertex
+ */
+void MetaMeshPlugin::relocate() {
+  using namespace PluginFunctions;
+  preoperation();
+
+  OpenMesh::VertexHandle mvh = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  OpenMesh::VertexHandle bvh = OpenMesh::PolyConnectivity::InvalidVertexHandle;
+  for (auto bvh_iter : embedding_->GetBaseMesh()->vertices()) {
+    if (embedding_->GetBaseMesh()->status(bvh_iter).selected()) {
+      if (embedding_->GetBaseMesh()->property(embedding_->bv_connection_, bvh_iter).is_valid()) {
+        mvh = embedding_->GetBaseMesh()->property(embedding_->bv_connection_, bvh_iter);
+      } else {
+        bvh = bvh_iter;
+      }
+      embedding_->GetBaseMesh()->status(bvh_iter).set_selected(false);
+    }
+  }
+  for (auto mvh_iter : embedding_->GetMetaMesh()->vertices()) {
+    if (embedding_->GetMetaMesh()->status(mvh_iter).selected()) {
+      mvh = mvh_iter;
+      embedding_->GetMetaMesh()->status(mvh_iter).set_selected(false);
+    }
+  }
+  if (bvh.is_valid() && mvh.is_valid()) {
+    embedding_->Relocate(mvh, bvh, true);
+  }
+
+  postoperation(UPDATE_TOPOLOGY, UPDATE_TOPOLOGY);
+}
+
+/*!
+ * \brief MetaMeshPlugin::remesh calls IsotropicRemesher::Remesh with menu selected parameters
+ */
+void MetaMeshPlugin::remesh() {
+  using namespace PluginFunctions;
+  preoperation();
+
+  IsotropicRemesher remesher;
+  auto take_screenshot = [&](const QString& name, const QString& dir,
+      const int& width = 0, const int& height = 0) {
+    screenshot(name, dir, width, height);
+  };
+
+  switch(widget_->combo_box_remeshing_->currentIndex()) {
+  case IsotropicRemesher::FORESTFIRE: {
+    remesher.Remesh(embedding_,  widget_->input_length_->value(),
+                     static_cast<uint>(widget_->input_iterations_->value()), 4.0/3.0, 4.0/5.0,
+                     IsotropicRemesher::FORESTFIRE, take_screenshot);
+    break;
+  }
+  case IsotropicRemesher::VERTEXWEIGHTS:{
+    remesher.Remesh(embedding_,  widget_->input_length_->value(),
+                     static_cast<uint>(widget_->input_iterations_->value()), 4.0/3.0, 4.0/5.0,
+                     IsotropicRemesher::VERTEXWEIGHTS, take_screenshot);
+    break;
+  }
+  case IsotropicRemesher::VERTEXDISTANCES:{
+    remesher.Remesh(embedding_,  widget_->input_length_->value(),
+                     static_cast<uint>(widget_->input_iterations_->value()), 4.0/3.0, 4.0/5.0,
+                     IsotropicRemesher::VERTEXDISTANCES, take_screenshot);
+    break;
+  }
+  }
+
+  postoperation(UPDATE_TOPOLOGY, UPDATE_TOPOLOGY);
+}
+
+/*!
+ * \brief MetaMeshPlugin::calculateoffset calculate an offset to separate base and meta mesh.
+ */
+void MetaMeshPlugin::calculateoffset() {
+  const double inf = std::numeric_limits<double>::infinity();
+  ACG::Vec3d minCorner = {+inf, +inf, +inf};
+  ACG::Vec3d maxCorner = {-inf, -inf, -inf};
+  for (auto bvh : embedding_->GetBaseMesh()->vertices()) {
+    auto p = embedding_->GetBaseMesh()->point(bvh);
+    maxCorner.maximize(p);
+    minCorner.minimize(p);
+  }
+  meta_mesh_visualization_offset_[0] = (maxCorner[0]-minCorner[0])*1.3;
+}
+
+/*!
+ * \brief MetaMeshPlugin::addoffset move the meta mesh away from the base mesh
+ */
+void MetaMeshPlugin::addoffset() {
+  for (auto mvh : embedding_->GetMetaMesh()->vertices()) {
+    embedding_->GetMetaMesh()->point(mvh) += meta_mesh_visualization_offset_/2.0;
+  }
+  for (auto bvh : embedding_->GetBaseMesh()->vertices()) {
+    embedding_->GetBaseMesh()->point(bvh) -= meta_mesh_visualization_offset_/2.0;
+  }
+}
+
+/*!
+ * \brief MetaMeshPlugin::removeoffset move the meta mesh back to the base mesh
+ */
+void MetaMeshPlugin::removeoffset() {
+  for (auto mvh : embedding_->GetMetaMesh()->vertices()) {
+    embedding_->GetMetaMesh()->point(mvh) -= meta_mesh_visualization_offset_/2.0;
+  }
+  for (auto bvh : embedding_->GetBaseMesh()->vertices()) {
+    embedding_->GetBaseMesh()->point(bvh) += meta_mesh_visualization_offset_/2.0;
+  }
+}
+
+/*!
+ * \brief MetaMeshPlugin::copyselection copy selected elements from the meta mesh
+ * into the base mesh (select the corresponding elements)
+ */
+void MetaMeshPlugin::copyselection() {
+  if (!metameshsanitychecks(false))
+    return;
+  using namespace PluginFunctions;
+  removeoffset();
+  for (auto bheh : embedding_->GetBaseMesh()->halfedges()) {
+    embedding_->GetBaseMesh()->status(bheh).set_selected(false);
+  }
+  for (auto mheh : embedding_->GetMetaMesh()->halfedges()) {
+    auto bheh = embedding_->GetMetaMesh()->property(embedding_->mhe_connection_, mheh);
+    embedding_->GetBaseMesh()->status(bheh).set_selected(
+          embedding_->GetMetaMesh()->status(mheh).selected());
+    while (embedding_->GetBaseMesh()->status(bheh).selected() &&
+           embedding_->GetBaseMesh()->property(embedding_->next_heh_, bheh)
+           != OpenMesh::PolyConnectivity::InvalidHalfedgeHandle) {
+      bheh = embedding_->GetBaseMesh()->property(embedding_->next_heh_, bheh);
+      embedding_->GetBaseMesh()->status(bheh).set_selected(true);
+    }
+  }
+  for (auto beh : embedding_->GetBaseMesh()->edges()) {
+    embedding_->GetBaseMesh()->status(beh).set_selected(false);
+  }
+  for (auto meh : embedding_->GetMetaMesh()->edges()) {
+    auto bheh = embedding_->GetMetaMesh()->property(embedding_->mhe_connection_,
+                embedding_->GetMetaMesh()->halfedge_handle(meh, 0));
+    auto beh = embedding_->GetBaseMesh()->edge_handle(bheh);
+    embedding_->GetBaseMesh()->status(beh).set_selected(
+          embedding_->GetMetaMesh()->status(meh).selected());
+    while (embedding_->GetBaseMesh()->status(beh).selected() &&
+           embedding_->GetBaseMesh()->property(embedding_->next_heh_, bheh)
+           != OpenMesh::PolyConnectivity::InvalidHalfedgeHandle) {
+      bheh = embedding_->GetBaseMesh()->property(embedding_->next_heh_, bheh);
+      auto beh = embedding_->GetBaseMesh()->edge_handle(bheh);
+      embedding_->GetBaseMesh()->status(beh).set_selected(true);
+    }
+  }
+
+  embedding_->GetBaseMesh()->garbage_collection();
+
+  addoffset();
+  emit updatedObject(base_mesh_id_, UPDATE_SELECTION);
+  UpdateMetaMesh(UPDATE_SELECTION);
+}
+
+/*!
+ * \brief MetaMeshPlugin::DummySlotFunction1 function for the first dummy button,
+ * used to test various things on the mesh
+ */
+void MetaMeshPlugin::DummySlotFunction1() {
+  using namespace PluginFunctions;
+  for (auto bheh : embedding_->GetBaseMesh()->halfedges()) {
+    if (embedding_->GetMetaMesh()->is_valid_handle(
+          embedding_->GetBaseMesh()->property(
+            embedding_->bhe_connection_, bheh))) {
+      embedding_->GetBaseMesh()->status(bheh).set_selected(true);
+    }
+  }
+  emit updatedObject(base_mesh_id_, UPDATE_SELECTION);
+}
+
+/*!
+ * \brief MetaMeshPlugin::DummySlotFunction2 function for the first dummy button,
+ * used to test various things on the mesh
+ */
+void MetaMeshPlugin::DummySlotFunction2() {
+  using namespace PluginFunctions;
+  for (auto mheh : meta_mesh_->halfedges()) {
+    if (meta_mesh_->is_boundary(mheh)) {
+      meta_mesh_->status(mheh).set_selected(true);
+    }
+  }
+  emit updatedObject(meta_mesh_id_, UPDATE_SELECTION);
+  emit updatedObject(base_mesh_id_, UPDATE_SELECTION);
+}
+
+/*!
+ * \brief MetaMeshPlugin::nxt move all selections on meta halfedges forward to their
+ * next_halfedge_handle
+ */
+void MetaMeshPlugin::nxt() {
+  if (!metameshsanitychecks())
+    return;
+  std::vector<OpenMesh::EdgeHandle> unmark_eh;
+  std::vector<OpenMesh::HalfedgeHandle> unmark_heh;
+  std::vector<OpenMesh::EdgeHandle> mark_eh;
+  std::vector<OpenMesh::HalfedgeHandle> mark_heh;
+  for (auto meh : embedding_->GetMetaMesh()->edges()) {
+    if (embedding_->GetMetaMesh()->status(meh).selected()) {
+      auto mheh = embedding_->GetMetaMesh()->halfedge_handle(meh, 0);
+      unmark_eh.push_back(meh);
+      mark_eh.push_back(embedding_->GetMetaMesh()->edge_handle(
+                          embedding_->GetMetaMesh()->next_halfedge_handle(mheh)));
+    }
+  }
+  for (auto mheh : embedding_->GetMetaMesh()->halfedges()) {
+    if (embedding_->GetMetaMesh()->status(mheh).selected()) {
+      unmark_heh.push_back(mheh);
+      mark_heh.push_back(embedding_->GetMetaMesh()->next_halfedge_handle(mheh));
+    }
+  }
+  for (auto unmark : unmark_eh) {
+    embedding_->GetMetaMesh()->status(unmark).set_selected(false);
+  }
+  for (auto unmark : unmark_heh) {
+    embedding_->GetMetaMesh()->status(unmark).set_selected(false);
+  }
+  for (auto mark : mark_eh) {
+    embedding_->GetMetaMesh()->status(mark).set_selected(true);
+  }
+  for (auto mark : mark_heh) {
+    embedding_->GetMetaMesh()->status(mark).set_selected(true);
+  }
+  UpdateMetaMesh(UPDATE_SELECTION_EDGES);
+  UpdateMetaMesh(UPDATE_SELECTION_HALFEDGES);
+}
+
+/*!
+ * \brief MetaMeshPlugin::opp move all selections of meta halfedges to
+ * their opposite_halfedge_handle
+ */
+void MetaMeshPlugin::opp() {
+  if (!metameshsanitychecks())
+    return;
+  std::vector<OpenMesh::EdgeHandle> unmark_eh;
+  std::vector<OpenMesh::HalfedgeHandle> unmark_heh;
+  std::vector<OpenMesh::EdgeHandle> mark_eh;
+  std::vector<OpenMesh::HalfedgeHandle> mark_heh;
+  for (auto meh : embedding_->GetMetaMesh()->edges()) {
+    if (embedding_->GetMetaMesh()->status(meh).selected()) {
+      auto mheh = embedding_->GetMetaMesh()->halfedge_handle(meh, 0);
+      unmark_eh.push_back(meh);
+      mark_eh.push_back(embedding_->GetMetaMesh()->edge_handle(
+                          embedding_->GetMetaMesh()->opposite_halfedge_handle(mheh)));
+    }
+  }
+  for (auto mheh : embedding_->GetMetaMesh()->halfedges()) {
+    if (embedding_->GetMetaMesh()->status(mheh).selected()) {
+      unmark_heh.push_back(mheh);
+      mark_heh.push_back(embedding_->GetMetaMesh()->opposite_halfedge_handle(mheh));
+    }
+  }
+  for (auto unmark : unmark_eh) {
+    embedding_->GetMetaMesh()->status(unmark).set_selected(false);
+  }
+  for (auto unmark : unmark_heh) {
+    embedding_->GetMetaMesh()->status(unmark).set_selected(false);
+  }
+  for (auto mark : mark_eh) {
+    embedding_->GetMetaMesh()->status(mark).set_selected(true);
+  }
+  for (auto mark : mark_heh) {
+    embedding_->GetMetaMesh()->status(mark).set_selected(true);
+  }
+  UpdateMetaMesh(UPDATE_SELECTION_EDGES);
+  UpdateMetaMesh(UPDATE_SELECTION_HALFEDGES);
+}
+
+/*!
+ * \brief MetaMeshPlugin::prv move all halfedge selections on the meta mesh
+ * to their prev_halfedge_handle
+ */
+void MetaMeshPlugin::prv() {
+  if (!metameshsanitychecks())
+    return;
+  std::vector<OpenMesh::EdgeHandle> unmark_eh;
+  std::vector<OpenMesh::HalfedgeHandle> unmark_heh;
+  std::vector<OpenMesh::EdgeHandle> mark_eh;
+  std::vector<OpenMesh::HalfedgeHandle> mark_heh;
+  for (auto meh : embedding_->GetMetaMesh()->edges()) {
+    if (embedding_->GetMetaMesh()->status(meh).selected()) {
+      auto mheh = embedding_->GetMetaMesh()->halfedge_handle(meh, 0);
+      unmark_eh.push_back(meh);
+      mark_eh.push_back(embedding_->GetMetaMesh()->edge_handle(
+                          embedding_->GetMetaMesh()->prev_halfedge_handle(mheh)));
+    }
+  }
+  for (auto mheh : embedding_->GetMetaMesh()->halfedges()) {
+    if (embedding_->GetMetaMesh()->status(mheh).selected()) {
+      unmark_heh.push_back(mheh);
+      mark_heh.push_back(embedding_->GetMetaMesh()->prev_halfedge_handle(mheh));
+    }
+  }
+  for (auto unmark : unmark_eh) {
+    embedding_->GetMetaMesh()->status(unmark).set_selected(false);
+  }
+  for (auto unmark : unmark_heh) {
+    embedding_->GetMetaMesh()->status(unmark).set_selected(false);
+  }
+  for (auto mark : mark_eh) {
+    embedding_->GetMetaMesh()->status(mark).set_selected(true);
+  }
+  for (auto mark : mark_heh) {
+    embedding_->GetMetaMesh()->status(mark).set_selected(true);
+  }
+  UpdateMetaMesh(UPDATE_SELECTION_EDGES);
+  UpdateMetaMesh(UPDATE_SELECTION_HALFEDGES);
+}
+
+/*!
+ * \brief MetaMeshPlugin::metameshsanitychecks checks a few conditions that make
+ * visualizing the meta_mesh_ unsafe (crashes the GPUoptimizer or other problems)
+ * \param verbose
+ * \return false if the meta mesh should not be virualized
+ */
+bool MetaMeshPlugin::metameshsanitychecks(bool verbose) {
+  if (meta_mesh_ == nullptr) {
+    if (verbose)
+      qDebug() << "The meta mesh is a null pointer.";
+    return false;
+  }
+  if (meta_mesh_->n_vertices() < 3) {
+    if (verbose)
+      qDebug() << "meta_mesh_ n_vertices is " << meta_mesh_->n_vertices()
+               << "but it needs to be at least 3 to be visualized.";
+    return false;
+  }
+  if (meta_mesh_->n_edges() == 0) {
+    if (verbose)
+      qDebug() << "The meta mesh has no edges.";
+    return false;
+  }
+  return true;
+}
+
+/*!
+ * \brief MetaMeshPlugin::MetaMeshUpdateNormals
+ */
+void MetaMeshPlugin::MetaMeshUpdateNormals() {
+  if (metameshsanitychecks(false)) {
+    meta_mesh_->update_normals();
+  }
+}
+
+/*!
+ * \brief MetaMeshPlugin::EmbeddingGarbageCollection
+ */
+void MetaMeshPlugin::EmbeddingGarbageCollection() {
+  if (metameshsanitychecks(false)) {
+    embedding_->MetaGarbageCollection();
+  }
+}
+
+/*!
+ * \brief MetaMeshPlugin::UpdateMetaMesh
+ * \param _type
+ */
+void MetaMeshPlugin::UpdateMetaMesh(const UpdateType &_type) {
+  if (metameshsanitychecks(false)) {
+    emit updatedObject(meta_mesh_id_, _type);
+  }
+}
+
+/*!
+ * \brief MetaMeshPlugin::GetHalfedgeSelection
+ * \return a list of selected meta halfedges (when they or their corresponding base
+ * halfedges are selected)
+ */
+std::queue<OpenMesh::HalfedgeHandle> MetaMeshPlugin::GetHalfedgeSelection() {
+  std::queue<OpenMesh::HalfedgeHandle> output;
+  if (widget_->checkbox_copy_markings_->checkState() == Qt::Checked) {
+    for (auto mheh : embedding_->GetMetaMesh()->halfedges()) {
+      if (embedding_->GetMetaMesh()->status(mheh).selected()) {
+          embedding_->GetMetaMesh()->status(mheh).set_selected(false);
+          output.push(mheh);
+      }
+    }
+    for (auto meh : embedding_->GetMetaMesh()->edges()) {
+      if (embedding_->GetMetaMesh()->status(meh).selected()) {
+        auto mheh = embedding_->GetMetaMesh()->halfedge_handle(meh, 0);
+        embedding_->GetMetaMesh()->status(mheh).set_selected(false);
+        output.push(mheh);
+      }
+    }
+  }
+  if (widget_->checkbox_copy_markings_->checkState() == Qt::Unchecked) {
+    for (auto bheh : embedding_->GetBaseMesh()->halfedges()) {
+      if (embedding_->ErrStatus()) {
+        break;
+      }
+      if (embedding_->GetBaseMesh()->status(bheh).selected()) {
+        embedding_->GetBaseMesh()->status(bheh).set_selected(false);
+        auto mheh = embedding_->GetBaseMesh()->property(embedding_->bhe_connection_, bheh);
+        if (mheh.is_valid()) {
+          output.push(mheh);
+        }
+      }
+    }
+    for (auto beh : embedding_->GetBaseMesh()->edges()) {
+      if (embedding_->ErrStatus()) {
+        break;
+      }
+      if (embedding_->GetBaseMesh()->status(beh).selected()) {
+        auto bheh = embedding_->GetBaseMesh()->halfedge_handle(beh, 0);
+        embedding_->GetBaseMesh()->status(beh).set_selected(false);
+        auto mheh = embedding_->GetBaseMesh()->property(embedding_->bhe_connection_, bheh);
+        if (mheh.is_valid()) {
+          output.push(mheh);
+        }
+      }
+    }
+  }
+  return output;
+}
+
+/*!
+ * \brief MetaMeshPlugin::screenshot updates view and takes(saves) a screenshot
+ * this function will be handed over to IsotropicRemesher
+ * \param name filename
+ * \param dir directory
+ * \param width dimension X
+ * \param height dimension Y
+ */
+void MetaMeshPlugin::screenshot(const QString& name, const QString& dir,
+                                const int& width, const int& height) {
+  //qDebug() << "screenshotfunction";
+  using namespace PluginFunctions;
+  // Take viewer snapshot
+
+  QCoreApplication::processEvents();
+  EmbeddingGarbageCollection();
+  MetaMeshUpdateNormals();
+  embedding_->BaseGarbageCollection();
+  addoffset();
+  QCoreApplication::processEvents();
+  embedding_->ColorizeMetaMesh();
+  UpdateMetaMesh(UPDATE_ALL);
+  embedding_->GetBaseMesh()->update_normals();
+  emit updatedObject(base_mesh_id_, UPDATE_ALL);
+  QCoreApplication::processEvents();
+
+  QImage image;
+  PluginFunctions::viewerSnapshot(PluginFunctions::ACTIVE_VIEWER, image, width, height);
+  image.save(dir + QDir::separator() + name + QString(".png"), "PNG");
+
+  removeoffset();
+}
+
+/*!
+ * \brief MetaMeshPlugin::preoperation functions to be called before operations on the
+ * mesh are performed. Currently only removes the meta mesh offset, but this wrapper
+ * is here for consistency with postoperation()
+ */
+void MetaMeshPlugin::preoperation() {
+  removeoffset();
+}
+
+/*!
+ * \brief MetaMeshPlugin::postoperation functions to be called after operations on the
+ * mesh are performed such as garbage collection, updating normals, adding an offset
+ * to the meta mesh, colorizing it etc.
+ * \param typem meta mesh update type eg. UPDATE_SELECTION
+ * \param typeb base mesh update type eg. UPDATE_TOPOLOGY
+ * \param verbose
+ */
+void MetaMeshPlugin::postoperation(const UpdateType &typem, const UpdateType &typeb,
+                                   bool verbose) {
+  if (embedding_->ErrStatus()
+      || !metameshsanitychecks(verbose)) {
+    widget_->checkbox_copy_markings_->setCheckState(Qt::Unchecked);
+    embedding_->GetBaseMesh()->update_normals();
+    emit updatedObject(base_mesh_id_, typeb);
+    addoffset();
+    embedding_->ColorizeMetaMesh();
+    embedding_->CleanMetaMesh();
+    return;
+  }
+  embedding_->GetBaseMesh()->update_normals();
+
+  EmbeddingGarbageCollection();
+  MetaMeshUpdateNormals();
+
+  addoffset();
+  embedding_->ColorizeMetaMesh();
+
+  UpdateMetaMesh(typem);
+  emit updatedObject(base_mesh_id_, typeb);
 }
diff --git a/MetaMeshPlugin.hh b/MetaMeshPlugin.hh
index 83083a5c75faced7a6b91bc8cd15f522186d656b..3d109145648b099423ce61be515d75719f63b826 100644
--- a/MetaMeshPlugin.hh
+++ b/MetaMeshPlugin.hh
@@ -3,23 +3,31 @@
 #include <OpenFlipper/BasePlugin/BaseInterface.hh>
 #include <OpenFlipper/BasePlugin/ToolboxInterface.hh>
 #include <OpenFlipper/BasePlugin/LoggingInterface.hh>
+#include <OpenFlipper/BasePlugin/LoadSaveInterface.hh>
 #include <OpenFlipper/common/Types.hh>
 
 #include "MetaMeshToolbox.hh"
+#include "Embedding.hh"
+#include "IsotropicRemesher.hh"
+#include "Testing.hh"
 
-class MetaMeshPlugin : public QObject, BaseInterface, ToolboxInterface, LoggingInterface
+class MetaMeshPlugin : public QObject, BaseInterface, ToolboxInterface,
+    LoggingInterface, LoadSaveInterface
 {
-	Q_OBJECT
-	Q_INTERFACES(BaseInterface)
+  Q_OBJECT
+  Q_INTERFACES(BaseInterface)
 	Q_INTERFACES(ToolboxInterface)
-	Q_INTERFACES(LoggingInterface)
+  Q_INTERFACES(LoggingInterface)
+  Q_INTERFACES(LoadSaveInterface)
 
 	Q_PLUGIN_METADATA(IID "org.OpenFlipper.Plugins.Plugin-MetaMesh")
 
 signals:
+
 	// BaseInterface
-	void updateView();
-	void updatedObject(int _identifier, const UpdateType& _type) override;
+  void updateView() override;
+  void updatedObject(int _identifier, const UpdateType& _type) override;
+
 	
 	// ToolboxInterface
 	void addToolbox(QString _name, QWidget* _widget) override;
@@ -28,20 +36,64 @@ signals:
 	void log(Logtype _type, QString _message) override;
 	void log(QString _message) override;
 
+  // LoadSaveInterface
+  void addEmptyObject(DataType _type, int& _id) override;
+
 public:
 	QString name() override { return QString("MetaMesh"); }
-	QString description() override { return QString(""); }
+  QString description() override { return QString(""); }
 
 public slots:
 	QString version() override { return QString("1.0"); }
-	void dispatch();
+  void triangulate();
+  void randomizetotal();
+  void randomizeratio();
+  void test_meta_mesh();
+  void retrace();
+  void rotate();
+  void split();
+  void collapse();
+  void relocate();
+  void remesh();
+  void togglecopy();
+  void copyselection();
+  void DummySlotFunction1();
+  void DummySlotFunction2();
+  void nxt();
+  void opp();
+  void prv();
 
 private slots:
 	// BaseInterface
 	void initializePlugin() override;
 	void pluginsInitialized() override;
 
+  void slotObjectUpdated(int _identifier) override;
+
 private:
+  bool metameshsanitychecks(bool verbose = true);
+  void randomize(Embedding::RandomType type);
+  void calculateoffset();
+  void addoffset();
+  void removeoffset();
+  void MetaMeshUpdateNormals();
+  void EmbeddingGarbageCollection();
+  void UpdateMetaMesh(const UpdateType &_type);
+  std::queue<OpenMesh::HalfedgeHandle> GetHalfedgeSelection();
+  void screenshot(const QString& name, const QString& dir,
+                  const int& width = 0, const int& height = 0);
+  void preoperation();
+  void postoperation(const UpdateType& typem, const UpdateType &typeb,
+                     bool verbose = false);
+
+  bool selection_mutex_ = false;
+
 	MetaMeshToolbox* widget_;
+  Embedding* embedding_;
+  Testing* testing_;
+  int base_mesh_id_=-1;
+  int meta_mesh_id_=-1;
+  PolyMesh* meta_mesh_;
+  ACG::Vec3d meta_mesh_visualization_offset_ = {0.0f, 0.0f, 0.0f};
 };
 
diff --git a/MetaMeshToolbox.cc b/MetaMeshToolbox.cc
index 71488796626c3923821bdaf0f3beca1ae1123401..0331249a350f9f0aa01263eb729c559fa11c6bdc 100644
--- a/MetaMeshToolbox.cc
+++ b/MetaMeshToolbox.cc
@@ -1,12 +1,104 @@
 #include "MetaMeshToolbox.hh"
 
-#include <QVBoxLayout>
+#include <QGridLayout>
+#include <QLabel>
 
 MetaMeshToolbox::MetaMeshToolbox(QWidget* parent) : QWidget(parent)
 {
-	button_dispatch = new QPushButton("Dispatch");
-	
-	auto* layout = new QVBoxLayout();
-	layout->addWidget(button_dispatch);
-	this->setLayout(layout);
+  QGridLayout* layout = new QGridLayout(this);
+
+  vbox_ = new QVBoxLayout;
+  checkbox_implicit_dijkstra_ = new QRadioButton("Implicit Dijkstra from Voronoi");
+  checkbox_a_star_triangulation_ = new QRadioButton("Bidirectional A* triangulation");
+  checkbox_a_star_midpoint_triangulation_ = new QRadioButton("A* with midpoints from Voronoi");
+  mode_selection_ = new QGroupBox("Initial triangulation type");
+  checkbox_a_star_triangulation_->setChecked(true);
+  vbox_->addWidget(checkbox_implicit_dijkstra_);
+  vbox_->addWidget(checkbox_a_star_triangulation_);
+  vbox_->addWidget(checkbox_a_star_midpoint_triangulation_);
+  vbox_->addStretch(1);
+  mode_selection_->setLayout(vbox_);
+
+  combo_box_tests_ = new QComboBox;
+  combo_box_tests_->addItem("Display Mesh Info");
+  combo_box_tests_->addItem("Mesh Properties");
+  combo_box_tests_->addItem("Mesh Operations");
+  combo_box_tests_->addItem("Rotations");
+  combo_box_tests_->addItem("Splits");
+  combo_box_tests_->addItem("Collapses");
+  combo_box_tests_->addItem("Relocations");
+
+  combo_box_remeshing_ = new QComboBox;
+  combo_box_remeshing_->addItem("Forest Fire");
+  combo_box_remeshing_->addItem("Vertex Weights");
+  combo_box_remeshing_->addItem("Vertex Distances");
+
+  button_triangulate_ = new QPushButton("Triangulate");
+  button_randomize_ratio_ = new QPushButton("Randomize a ratio of points");
+  button_randomize_total_ = new QPushButton("Randomize a number of points");
+  button_run_test_ = new QPushButton("Run test");
+  checkbox_copy_markings_ = new QCheckBox("Selection Copying");
+  button_retrace_ = new QPushButton("Retrace");
+  button_rotate_ = new QPushButton("Rotate");
+  button_split_ = new QPushButton("Split");
+  button_collapse_ = new QPushButton("Collapse");
+  button_relocate_ = new QPushButton("Relocate");
+  button_dummy1_ = new QPushButton("Insert");
+  button_dummy2_ = new QPushButton("Delete");
+  button_next_ = new QPushButton("nxt");
+  button_opp_ = new QPushButton("opp");
+  button_prev_ = new QPushButton("prv");
+  button_remesh_ = new QPushButton("Remesh");
+
+  checkbox_copy_markings_->setCheckState(Qt::Checked);
+
+  input_ratio_ = new QDoubleSpinBox();
+  input_ratio_->setRange(0,1);
+  input_ratio_->setDecimals(4);
+  input_ratio_->setSingleStep(0.0001);
+  input_ratio_->setValue(0.003);
+  input_ratio_->setToolTip("Ratio of Base Mesh Vertices to be "
+                           "randomly assigned as Meta Mesh vertices");
+
+  input_length_ = new QDoubleSpinBox();
+  input_length_->setRange(0,1);
+  input_length_->setDecimals(3);
+  input_length_->setSingleStep(0.001);
+  input_length_->setValue(0.2);
+  input_length_->setToolTip("Edge length in % of object diagonal");
+
+  input_iterations_ = new QSpinBox();
+  input_iterations_->setRange(1,100);
+  input_iterations_->setValue(10);
+  input_iterations_->setSingleStep(1);
+  input_iterations_->setToolTip("Number of times the algorithm runs");
+
+  QLabel* label_length = new QLabel("Length:");
+  QLabel* label_iterations = new QLabel("Iterations:");
+
+  layout->addWidget(button_triangulate_, 0, 0, 1, 6);
+  layout->addWidget(button_randomize_ratio_, 1, 0, 1, 6);
+  layout->addWidget(button_randomize_total_, 2, 0, 1, 6);
+  layout->addWidget(mode_selection_, 3, 0, 1, 6);
+  layout->addWidget(input_ratio_, 4, 0, 1, 6);
+  layout->addWidget(button_run_test_, 5, 0, 1, 3);
+  layout->addWidget(combo_box_tests_, 5, 3, 1, 3);
+  layout->addWidget(checkbox_copy_markings_, 6, 0, 1, 3);
+  layout->addWidget(button_retrace_, 6, 3, 1, 3);
+  layout->addWidget(button_rotate_, 7, 0, 1, 3);
+  layout->addWidget(button_split_, 7, 3, 1, 3);
+  layout->addWidget(button_collapse_, 8, 0, 1, 3);
+  layout->addWidget(button_relocate_, 8, 3, 1, 3);
+  layout->addWidget(button_dummy1_, 9, 0, 1, 3);
+  layout->addWidget(button_dummy2_, 9, 3, 1, 3);
+  layout->addWidget(button_next_, 10, 0, 1, 2);
+  layout->addWidget(button_opp_, 10, 2, 1, 2);
+  layout->addWidget(button_prev_, 10, 4, 1, 2);
+  layout->addWidget(button_remesh_, 11, 0, 1, 3);
+  layout->addWidget(combo_box_remeshing_, 11, 3, 1, 3);
+  layout->addWidget(label_length, 12, 0, 1, 3);
+  layout->addWidget(input_length_, 13, 0, 1, 3);
+  layout->addWidget(label_iterations, 12, 3, 1, 3);
+  layout->addWidget(input_iterations_, 13, 3, 1, 3);
+  this->setLayout(layout);
 }
diff --git a/MetaMeshToolbox.hh b/MetaMeshToolbox.hh
index 0a224058b2ee7b38e2f438284683ad9e30b06e85..ebe4ff06a974e5dc93a0e535918bdf871ea4f554 100644
--- a/MetaMeshToolbox.hh
+++ b/MetaMeshToolbox.hh
@@ -1,15 +1,47 @@
 #pragma once
 
 #include <QtGui>
+#include <QGroupBox>
 #include <QPushButton>
+#include <QDoubleSpinBox>
+#include <QVBoxLayout>
+#include <QRadioButton>
+#include <QComboBox>
+#include <QCheckBox>
 
 class MetaMeshToolbox : public QWidget
 {
-	Q_OBJECT
+  Q_OBJECT
 
 public:
-	MetaMeshToolbox(QWidget* parent = nullptr);
-	
-	QPushButton* button_dispatch;
+  MetaMeshToolbox(QWidget* parent = nullptr);
+
+    QGroupBox* mode_selection_;
+    QVBoxLayout* vbox_;
+    QRadioButton* checkbox_implicit_dijkstra_;
+    QRadioButton* checkbox_a_star_triangulation_;
+    QRadioButton* checkbox_a_star_midpoint_triangulation_;
+    QComboBox* combo_box_tests_;
+    QComboBox* combo_box_remeshing_;
+
+    QPushButton* button_triangulate_;
+    QPushButton* button_randomize_ratio_;
+    QPushButton* button_randomize_total_;
+    QPushButton* button_run_test_;
+    QCheckBox* checkbox_copy_markings_;
+    QPushButton* button_retrace_;
+    QPushButton* button_rotate_;
+    QPushButton* button_split_;
+    QPushButton* button_collapse_;
+    QPushButton* button_relocate_;
+    QPushButton* button_remesh_;
+    QPushButton* button_dummy1_;
+    QPushButton* button_dummy2_;
+    QPushButton* button_next_;
+    QPushButton* button_opp_;
+    QPushButton* button_prev_;
+    QDoubleSpinBox* input_length_;
+    QDoubleSpinBox* input_ratio_;
+    QSpinBox* input_iterations_;
 };
 
diff --git a/RWTHColors.hh b/RWTHColors.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3ac1a7ef4cefda8ff882fa9dd33befd7c6e1ee1d
--- /dev/null
+++ b/RWTHColors.hh
@@ -0,0 +1,23 @@
+/**
+ * Colors.hh
+ */
+#pragma once
+
+#include <ACG/Math/VectorT.hh>
+#include <QColor>
+
+namespace Utils {
+
+static constexpr ACG::Vec4f RWTH_BLUE(0.0, 0.329, 0.624, 1.0);
+static constexpr ACG::Vec4f RWTH_GREEN(0.341, 0.671, 0.153, 1.0);
+static constexpr ACG::Vec4f RWTH_ORANGE(0.965, 0.659, 0.0, 1.0);
+static constexpr ACG::Vec4f RWTH_VIOLETT(0.380, 0.129, 0.345, 1.0);
+static constexpr ACG::Vec4f RWTH_RED(0.800, 0.027, 0.118, 1.0);
+static constexpr ACG::Vec4f RWTH_MAGENTA(0.890, 0.000, 0.400, 1.0);
+static constexpr ACG::Vec4f RWTH_YELLOW(1.000, 0.929, 0.000, 1.0);
+
+static constexpr ACG::Vec4f RWTH_BLACK(0.0, 0.0, 0.0, 1.0);
+static constexpr ACG::Vec4f RWTH_GRAY_75(0.392, 0.396, 0.404, 1.0);
+static constexpr ACG::Vec4f RWTH_GRAY_50(0.612, 0.620, 0.624, 1.0);
+
+} // namespace Utils
diff --git a/Stopwatch.cc b/Stopwatch.cc
new file mode 100644
index 0000000000000000000000000000000000000000..af9d8b7b955414fc3b66f40db7c7a9ca868d30a3
--- /dev/null
+++ b/Stopwatch.cc
@@ -0,0 +1,27 @@
+#include "Stopwatch.hh"
+
+/*!
+ * \brief Stopwatch::Stopwatch a simple stopwatch, starts timer when initialized
+ */
+Stopwatch::Stopwatch() {
+  Reset();
+}
+
+/*!
+ * \brief Stopwatch::Delta
+ * \return the time passed since the last timer reset
+ */
+double Stopwatch::Delta() {
+  auto end = std::chrono::time_point_cast<
+      std::chrono::milliseconds>(std::chrono::system_clock::now());
+  auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(end-start_).count();
+  return delta;
+}
+
+/*!
+ * \brief Stopwatch::Reset reset the timer
+ */
+void Stopwatch::Reset() {
+  start_ = std::chrono::time_point_cast<
+      std::chrono::milliseconds>(std::chrono::system_clock::now());
+}
diff --git a/Stopwatch.hh b/Stopwatch.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3988f5d37a778a27c535e7af47a7a1b014254898
--- /dev/null
+++ b/Stopwatch.hh
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <chrono>
+
+// A simple stopwatch with ms precision
+class Stopwatch {
+public:
+  Stopwatch();
+  ~Stopwatch();
+  double Delta();
+  void Reset();
+
+private:
+  std::chrono::time_point<std::chrono::system_clock> start_;
+};
diff --git a/Testing.cc b/Testing.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b43bdb32b318fa034ac9d7fd5f082239f9f84979
--- /dev/null
+++ b/Testing.cc
@@ -0,0 +1,649 @@
+#include "Testing.hh"
+#include <QDebug>
+#include <math.h>
+#include <random>
+
+#define BASE_MESH_ embedding_->base_mesh_
+#define META_MESH_ embedding_->meta_mesh_
+
+/*!
+ * \brief Testing::Testing
+ * \param embedding
+ */
+Testing::Testing(Embedding* embedding) : embedding_(embedding) {
+
+}
+
+/*!
+ * \brief Testing::TriangulationTests tests the triangulation, however the bhe_border
+ * and mhe_border properties cannot be tested properly on meshes with boundaries, thus
+ * this test is deprecated and disabled for now.
+ */
+void Testing::TriangulationTests() {
+  ResetStop();
+  if (META_MESH_ == nullptr) {
+    qDebug() << "Uninitialized Meta Mesh, run some triangulation first.";
+    return;
+  }
+
+  qDebug() << "Deprecated test";
+  return;
+  qDebug() << "Testing Meta Mesh properties and connectivity after an intial triangulation.";
+
+  Stopwatch* swatch = new Stopwatch();
+
+  TestVertices();
+  if (stop_) return;
+  TestHalfedgesInitialTriangulation();
+  if (stop_) return;
+  TestFaces();
+  if (stop_) return;
+
+  double delta = swatch->Delta();
+  qDebug() << "All tests passed, time elapsed: " << delta;
+
+  DisplayMeshInformation();
+}
+
+/*!
+ * \brief Testing::MeshTests Calls various tests on the mesh that should always pass.
+ * These tests do not change the meta mesh
+ */
+void Testing::MeshTests() {
+  ResetStop();
+  if (META_MESH_ == nullptr) {
+    qDebug() << "Uninitialized Meta Mesh, run some triangulation first.";
+    return;
+  }
+  qDebug() << "Testing Meta Mesh properties and connectivity (this should always pass).";
+
+  Stopwatch* swatch = new Stopwatch();
+
+  TestVertices();
+  if (stop_) return;
+  TestHalfedges();
+  if (stop_) return;
+  TestFaces();
+  if (stop_) return;
+  TestMetaMesh();
+  if (stop_) return;
+
+  double delta = swatch->Delta();
+  qDebug() << "All tests passed, time elapsed: " << delta << "ms.";
+
+  DisplayMeshInformation();
+}
+
+/*!
+ * \brief Testing::OperationTests Spams basic operations on the mesh and tests for
+ * inconsistencies. This will drastically change the meta mesh.
+ */
+void Testing::OperationTests() {
+  ResetStop();
+  if (META_MESH_ == nullptr) {
+    qDebug() << "Uninitialized Meta Mesh, run some triangulation first.";
+    return;
+  }
+
+  qDebug() << "Testing basic Trimesh operations";
+  Stopwatch* swatch = new Stopwatch();
+
+  TestFlips(true);
+  if (stop_) return;
+  TestSplits(true);
+  if (stop_) return;
+  TestCollapses(true);
+  if (stop_) return;
+  TestRelocation(true);
+  if (stop_) return;
+
+  double delta = swatch->Delta();
+  qDebug() << "All tests passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::DisplayMeshInformation outputs various information on the meta mesh
+ * such as numbers of elements, avg. edge valence and standard deviation from it etc.
+ */
+void Testing::DisplayMeshInformation() {
+  unsigned long baseeuler = BASE_MESH_->n_vertices() - BASE_MESH_->n_edges()
+      + BASE_MESH_->n_faces();
+  unsigned long  metaeuler = META_MESH_->n_vertices() - META_MESH_->n_edges()
+      + META_MESH_->n_faces();
+  double basegenus = (2-baseeuler)/2.0;
+  double metagenus = (2-metaeuler)/2.0;
+  double avgmetaedgelength = 0.0;
+  for (auto meh : META_MESH_->edges()) {
+    avgmetaedgelength += embedding_->CalculateEdgeLength(meh);
+  }
+  avgmetaedgelength /= META_MESH_->n_edges();
+  double metaedgelengthdeviation = 0.0;
+  for (auto meh : META_MESH_->edges()) {
+    metaedgelengthdeviation += (avgmetaedgelength-embedding_->CalculateEdgeLength(meh))
+        *(avgmetaedgelength-embedding_->CalculateEdgeLength(meh));
+  }
+  metaedgelengthdeviation /= META_MESH_->n_edges();
+  double avgvalence = 0.0;
+  double valencedeviation = 0.0;
+  for (auto mvh : META_MESH_->vertices()) {
+    avgvalence += META_MESH_->valence(mvh);
+    valencedeviation += (6-META_MESH_->valence(mvh))*(6-META_MESH_->valence(mvh));
+  }
+  avgvalence /= META_MESH_->n_vertices();
+  valencedeviation /= META_MESH_->n_vertices();
+  qDebug() << "Base Mesh- V: " << BASE_MESH_->n_vertices()
+           << "E: " << BASE_MESH_->n_edges()
+           << "F: " << BASE_MESH_->n_faces()
+           << "Eul: " << baseeuler
+           << "Gen: " << basegenus;
+  qDebug() << "Meta Mesh- V: " << META_MESH_->n_vertices()
+           << "E: " << META_MESH_->n_edges()
+           << "F: " << META_MESH_->n_faces()
+           << "Eul: " << metaeuler
+           << "Gen: " << metagenus;
+  qDebug() << "Embedding- Avg. Edge Length: " << avgmetaedgelength
+           << " deviation: " << metaedgelengthdeviation
+           << " avg valence: " << avgvalence
+           << " deviation from 6: " << valencedeviation;
+}
+
+/*!
+ * \brief Testing::TestVertices
+ */
+void Testing::TestVertices() {
+  if (META_MESH_->n_vertices() == 0) {
+    qDebug() << "Meta Mesh has no vertices.";
+  } else {
+    TestVertexConnections();
+    if (stop_) return;
+
+    //qDebug() << "Passed all vertex tests.";
+  }
+}
+
+/*!
+ * \brief Testing::TestHalfedges
+ */
+void Testing::TestHalfedges() {
+  if (META_MESH_->n_halfedges() == 0) {
+    qDebug() << "Meta Mesh has no halfedges.";
+    return;
+  } else {
+    TestHalfedgeConnections();
+    if (stop_) return;
+    TestHalfedgeSymmetry();
+    if (stop_) return;
+
+    //qDebug() << "Passed all halfedge tests.";
+  }
+}
+
+/*!
+ * \brief Testing::TestHalfedgesInitialTriangulation
+ */
+void Testing::TestHalfedgesInitialTriangulation() {
+  if (META_MESH_->n_halfedges() == 0) {
+    qDebug() << "Meta Mesh has no halfedges.";
+    return;
+  } else {
+    TestHalfedgeConnections();
+    if (stop_) return;
+    TestHalfedgeSymmetry();
+    if (stop_) return;
+    TestVoronoiBorderSymmetry();
+    if (stop_) return;
+    TestVoronoiBorderConnectivity();
+    if (stop_) return;
+
+    //qDebug() << "Passed all halfedge tests.";
+  }
+}
+
+/*!
+ * \brief Testing::TestFaces
+ */
+void Testing::TestFaces() {
+  if (META_MESH_->n_faces() == 0) {
+    qDebug() << "Meta Mesh has no faces.";
+  } else {
+    TestTriMesh();
+    if (stop_) return;
+
+    //qDebug() << "Passed all face tests.";
+  }
+}
+
+/*!
+ * \brief Testing::TestMetaMesh
+ */
+void Testing::TestMetaMesh() {
+  if (META_MESH_->n_faces() == 0) {
+    qDebug() << "Meta Mesh has no faces.";
+  } else {
+    TestMetaMeshHalfedgeConnectivity();
+    if (stop_) return;
+
+    //qDebug() << "Passed all meta mesh connectivity tests.";
+  }
+}
+
+
+/*!
+ * \brief Testing::TestVertexConnections
+ * Test that each meta vertex is connected to a corresponding
+ * base vertex that is also connected to it
+ */
+void Testing::TestVertexConnections() {
+  Stopwatch* swatch = new Stopwatch();
+  for (auto mvh : META_MESH_->vertices()) {
+    VisualAssert(BASE_MESH_->is_valid_handle(
+                   META_MESH_->property(embedding_->mv_connection_, mvh)),
+        "Test failed: Unconnected Meta Vertex", {});
+    if (stop_) return;
+    VisualAssert(BASE_MESH_->property(embedding_->bv_connection_,
+                 META_MESH_->property(embedding_->mv_connection_, mvh)) == mvh,
+        "Test failed: Base Vertex that Meta Vertex is connected to is not connected back",
+        {META_MESH_->property(embedding_->mv_connection_, mvh)});
+    if (stop_) return;
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestVertexConnections passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestHalfedgeConnections
+ * Test that each meta halfedge is connected to a base halfedge that in turn
+ * is also connected to it and forms a chain of base half_edges connected via
+ * next_heh until it reaches the meta vertex.
+ */
+void Testing::TestHalfedgeConnections() {
+  Stopwatch* swatch = new Stopwatch();
+  for (auto mheh : META_MESH_->halfedges()) {
+    TestHalfedgeConnection(mheh);
+    if (stop_) return;
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestHalfedgeConnections passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestHalfedgeConnection
+ * \param mheh
+ */
+void Testing::TestHalfedgeConnection(OpenMesh::HalfedgeHandle mheh) {
+  auto bheh = META_MESH_->property(embedding_->mhe_connection_, mheh);
+  // Meta Halfedge has a valid connection that has not been deleted
+  VisualAssert(BASE_MESH_->is_valid_handle(bheh)
+               && !BASE_MESH_->status(bheh).deleted(),
+               "Test failed: The meta halfedge " + std::to_string(mheh.idx()) +
+               " has no valid base vertex connection at bheh "
+               + std::to_string(bheh.idx()), {});
+  if (stop_) return;
+  // The connected base halfedge also has a connection to the meta halfedge
+  VisualAssert(BASE_MESH_->property(embedding_->bhe_connection_, bheh) == mheh,
+               "Test failed: The base halfedge is not connected back to the meta halfedge",
+              {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh)});
+  if (stop_) return;
+  // Both originate from the same vertex
+  VisualAssert(BASE_MESH_->property(embedding_->bv_connection_,
+    BASE_MESH_->from_vertex_handle(bheh)) == META_MESH_->from_vertex_handle(mheh),
+    "Test failed: The base halfedge originates from a different vertex than the "
+    "meta halfedge", {BASE_MESH_->from_vertex_handle(bheh),
+    META_MESH_->property(embedding_->mv_connection_, META_MESH_->from_vertex_handle(mheh))});
+  if (stop_) return;
+  auto curr = BASE_MESH_->property(embedding_->next_heh_, bheh);
+  // Test the connectivity of the meta halfedge, it should be a chain of
+  // base halfedges connected to the next one via next_heh_ and connected to the meta
+  // halfdge via bhe_connection_
+  while (BASE_MESH_->is_valid_handle(curr)) {
+    //BASE_MESH_->status(curr).set_selected(true);
+    auto mheh0 = BASE_MESH_->property(embedding_->bhe_connection_, bheh);
+    auto mheh1 = BASE_MESH_->property(embedding_->bhe_connection_, curr);
+    VisualAssert(BASE_MESH_->property(embedding_->bhe_connection_, curr) == mheh,
+            "Test failed: A base halfedge is not connected to its meta halfedge;"
+            " The previous halfedge" + std::to_string(bheh.idx()) + " is connected"
+            " to meta halfedge " + std::to_string(mheh0.idx()) + " and the current"
+            " halfedge " + std::to_string(curr.idx()) + " is connected to meta"
+            " halfedge " + std::to_string(mheh1.idx()) +".",
+            {BASE_MESH_->from_vertex_handle(curr), BASE_MESH_->to_vertex_handle(curr)});
+    if (stop_) return;
+    bheh = curr;
+    curr = BASE_MESH_->property(embedding_->next_heh_, curr);
+  }
+  if (curr.is_valid()) {
+    BASE_MESH_->status(bheh).set_selected(true);
+  }
+  VisualAssert(!curr.is_valid(),
+          "Test failed: A meta halfedge contains a halfedge not in the base mesh, the "
+          "next_heh_ pointed to by bheh " + std::to_string(bheh.idx()) + " is "
+          "valid but not in the base mesh. Marking bheh.",
+          {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh)});
+  if (stop_) return;
+
+  // Test that the final halfedge ends in a base vertex connected to the meta vertex the
+  // meta halfedge ends in
+  VisualAssert(BASE_MESH_->property(embedding_->bv_connection_,
+     BASE_MESH_->to_vertex_handle(bheh)) == META_MESH_->to_vertex_handle(mheh),
+     "Test failed: The base halfedge ends in a different vertex than the "
+     "meta halfedge", {BASE_MESH_->to_vertex_handle(bheh),
+     META_MESH_->property(embedding_->mv_connection_, META_MESH_->to_vertex_handle(mheh))});
+  if (stop_) return;
+}
+
+/*!
+ * \brief Testing::TestHalfedgeSymmetry
+ */
+void Testing::TestHalfedgeSymmetry() {
+  Stopwatch* swatch = new Stopwatch();
+  for (auto bheh : BASE_MESH_->halfedges()) {
+    auto mheh0 = BASE_MESH_->property(embedding_->bhe_connection_, bheh);
+    auto mheh1 = BASE_MESH_->property(embedding_->bhe_connection_,
+                                      BASE_MESH_->opposite_halfedge_handle(bheh));
+    if (!META_MESH_->is_valid_handle(mheh0)) {
+      VisualAssert(!META_MESH_->is_valid_handle(mheh1),
+        "Test failed: A base halfedge is connected but not its opposite halfedge",
+        {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh),
+        BASE_MESH_->from_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh)),
+        BASE_MESH_->to_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh))});
+      if (stop_) return;
+    } else {
+      VisualAssert(META_MESH_->opposite_halfedge_handle(mheh0) == mheh1,
+        "Test failed: A base halfedge is connected but its opposite halfedge is not"
+        " connected the the opposite meta halfedge",
+        {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh),
+        BASE_MESH_->from_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh)),
+        BASE_MESH_->to_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh))});
+      if (stop_) return;
+    }
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestHalfedgeSymmetry passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestVoronoiBorderSymmetry
+ */
+void Testing::TestVoronoiBorderSymmetry() {
+  Stopwatch* swatch = new Stopwatch();
+  for (auto bheh : BASE_MESH_->halfedges()) {
+    auto mheh0 = BASE_MESH_->property(embedding_->bhe_border_, bheh);
+    auto mheh1 = BASE_MESH_->property(embedding_->bhe_border_,
+                                      BASE_MESH_->opposite_halfedge_handle(bheh));
+    if (!META_MESH_->is_valid_handle(mheh0)) {
+      VisualAssert(!META_MESH_->is_valid_handle(mheh1),
+        "Test failed: A base halfedge is a border but not its opposite halfedge",
+        {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh),
+        BASE_MESH_->from_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh)),
+        BASE_MESH_->to_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh))});
+      if (stop_) return;
+    } else {
+      VisualAssert(META_MESH_->opposite_halfedge_handle(mheh0) == mheh1,
+        "Test failed: A base halfedge is a border but its opposite halfedge is not"
+        " connected a border to the opposite meta halfedge",
+        {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh),
+        BASE_MESH_->from_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh)),
+        BASE_MESH_->to_vertex_handle(BASE_MESH_->opposite_halfedge_handle(bheh))});
+      if (stop_) return;
+    }
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestVoronoiBorderSymmetry passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestVoronoiBorderConnectivity
+ * Ensure the correctness and consistency of the voronoi borders, marked by the
+ * property handles bhe_border_ and mhe_border_
+ */
+void Testing::TestVoronoiBorderConnectivity() {
+  Stopwatch* swatch = new Stopwatch();
+  for (auto mheh : META_MESH_->halfedges()) {
+    auto bheh = META_MESH_->property(embedding_->mhe_border_, mheh);
+    // Traverse the border on the base mesh and test it
+    TestBorderTraversal(bheh, mheh);
+    if (stop_) return;
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestVoronoiBorderConnectivity passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestBorderTraversal
+ * \param bheh
+ * \param mheh
+ */
+void Testing::TestBorderTraversal(OpenMesh::HalfedgeHandle bheh,
+                                  OpenMesh::HalfedgeHandle mheh) {
+  // Test for existence
+  VisualAssert(BASE_MESH_->is_valid_handle(bheh),
+               "Test failed: Base halfedge " + std::to_string(bheh.idx()) + " should be"
+               " a border region but does not exist", {});
+  if (stop_) return;
+  // Test for reciprocality
+  VisualAssert(BASE_MESH_->property(embedding_->bhe_border_, bheh) == mheh,
+               "Test failed: Base halfedge " + std::to_string(bheh.idx()) + " is not "
+               "connected to the proper meta halfedge demarking its border",
+               {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh)});
+  if (stop_) return;
+  auto bheh0 = BASE_MESH_->next_halfedge_handle(BASE_MESH_->opposite_halfedge_handle(bheh));
+  auto bheh1 = BASE_MESH_->next_halfedge_handle(bheh0);
+  auto mv0 = META_MESH_->from_vertex_handle(mheh);
+  auto mv1 = META_MESH_->to_vertex_handle(mheh);
+  // Test for correctness based on voronoi region
+  VisualAssert(mv0 == BASE_MESH_->property(embedding_->voronoiID_,
+                                     BASE_MESH_->from_vertex_handle(bheh)),
+               "Test failed: The voronoiID of the halfedges does not correspond "
+               "to its border property",
+               {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh)});
+  if (stop_) return;
+  VisualAssert(mv1 == BASE_MESH_->property(embedding_->voronoiID_,
+                                     BASE_MESH_->to_vertex_handle(bheh)),
+         "Test failed: The voronoiID of the halfedges does not correspond "
+         "to its border property",
+         {BASE_MESH_->from_vertex_handle(bheh), BASE_MESH_->to_vertex_handle(bheh)});
+  if (stop_) return;
+  // Traverse further
+  if (BASE_MESH_->property(embedding_->voronoiID_, BASE_MESH_->from_vertex_handle(bheh0))
+      == mv0
+      && BASE_MESH_->property(embedding_->voronoiID_, BASE_MESH_->to_vertex_handle(bheh0))
+      == mv1) {
+    if (!stop_) TestBorderTraversal(bheh0, mheh);
+  } else if (BASE_MESH_->property(embedding_->voronoiID_, BASE_MESH_->from_vertex_handle(bheh1))
+             == mv0
+      && BASE_MESH_->property(embedding_->voronoiID_, BASE_MESH_->to_vertex_handle(bheh1))
+             == mv1) {
+    if (!stop_) TestBorderTraversal(bheh1, mheh);
+  }
+}
+
+/*!
+ * \brief Testing::TestTriMesh
+ * Make sure the meta mesh is a TriMesh by testing each face
+ */
+void Testing::TestTriMesh() {
+  Stopwatch* swatch = new Stopwatch();
+  for (auto mfh : META_MESH_->faces()) {
+    auto mheh = META_MESH_->halfedge_handle(mfh);
+    auto mheh3 = META_MESH_->next_halfedge_handle(
+                 META_MESH_->next_halfedge_handle(
+                 META_MESH_->next_halfedge_handle(mheh)));
+    // In a triangular face the halfedge 3 after the current halfedge is the current halfedge.
+    VisualAssert(mheh == mheh3,
+                 "Test failed: a meta face is not a triangle", {});
+    if (stop_) return;
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestTriMesh passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestMetaMeshHalfedgeConnectivity
+ */
+void Testing::TestMetaMeshHalfedgeConnectivity() {
+  Stopwatch* swatch = new Stopwatch();
+  for (auto mheh : META_MESH_->halfedges()) {
+    auto bvh0 = META_MESH_->property(embedding_->mv_connection_,
+                               META_MESH_->from_vertex_handle(mheh));
+    auto bvh1 = META_MESH_->property(embedding_->mv_connection_,
+                               META_MESH_->to_vertex_handle(mheh));
+    auto mheh1 = META_MESH_->next_halfedge_handle(mheh);
+    VisualAssert(META_MESH_->is_valid_handle(mheh1), "mheh " + std::to_string(mheh.idx())
+                 + " has an invalid next_halfedge_handle " + std::to_string(mheh1.idx()), {});
+    auto mheh2 = META_MESH_->next_halfedge_handle(mheh1);
+    VisualAssert(META_MESH_->is_valid_handle(mheh1), "mheh1 " + std::to_string(mheh1.idx())
+                 + " has an invalid next_halfedge_handle " + std::to_string(mheh2.idx()), {});
+    auto bvh2 = META_MESH_->property(embedding_->mv_connection_,
+                               META_MESH_->to_vertex_handle(mheh1));
+    auto bvh3 = META_MESH_->property(embedding_->mv_connection_,
+                                     META_MESH_->to_vertex_handle(mheh2));
+    if (META_MESH_->is_boundary(mheh)) {
+      VisualAssert(META_MESH_->is_boundary(META_MESH_->next_halfedge_handle(mheh)),
+        "Test failed: a boundary halfedge does not point towards another"
+        " boundary halfedge", {bvh0, bvh1, bvh2});
+    } else {
+      VisualAssert(bvh0 == bvh3,
+        "Test failed: a halfedge is not part of a triangle.", {bvh0, bvh1, bvh2, bvh3});
+    }
+    if (stop_) return;
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestMetaMeshHalfedgeConnectivity passed, time elapsed: "
+           << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestFlips
+ * \param nostop
+ */
+void Testing::TestFlips(bool nostop) {
+  Stopwatch* swatch = new Stopwatch();
+  if (nostop) ResetStop();
+  for (auto meh : META_MESH_->edges()) {
+    if (META_MESH_->is_valid_handle(meh) && !META_MESH_->status(meh).deleted()) {
+      if (embedding_->IsRotationOkay(meh)) {
+        auto mvh0 = META_MESH_->to_vertex_handle(META_MESH_->halfedge_handle(meh, 0));
+        auto mvh1 = META_MESH_->to_vertex_handle(META_MESH_->halfedge_handle(meh, 1));
+        embedding_->Rotate(meh);
+        embedding_->Rotate(meh);
+        auto mvh2 = META_MESH_->to_vertex_handle(META_MESH_->halfedge_handle(meh, 0));
+        auto mvh3 = META_MESH_->to_vertex_handle(META_MESH_->halfedge_handle(meh, 1));
+        VisualAssert(((mvh0 == mvh3) && (mvh1 == mvh2)),
+                     "After rotating meta edge" + std::to_string(meh.idx()) + " twice "
+                     "the start/end vertices mismatched.", {});
+        if (stop_) return;
+        embedding_->CleanUpBaseMesh();
+      }
+    }
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestFlips passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestSplits
+ * \param nostop
+ */
+void Testing::TestSplits(bool nostop) {
+  Stopwatch* swatch = new Stopwatch();
+  if (nostop) ResetStop();
+  // Test Edge splits
+  for (auto meh : META_MESH_->edges()) {
+    if (meh.idx()%5 == 0) {
+      embedding_->Split(embedding_->MiddleBvh(meh));
+    }
+  }
+  // Test Face splits
+  for (auto bvh : BASE_MESH_->vertices()) {
+    if (bvh.idx()%500 == 0) {
+      embedding_->Split(bvh);
+    }
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestSplits passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestCollapses
+ * \param nostop
+ */
+void Testing::TestCollapses(bool nostop) {
+  Stopwatch* swatch = new Stopwatch();
+  if (nostop) ResetStop();
+  size_t n_e = META_MESH_->n_edges()+1;
+  //qDebug() << "Number of edges: " << META_MESH_->n_edges();
+  while (n_e > META_MESH_->n_edges()) {
+    n_e = META_MESH_->n_edges();
+    size_t temp = n_e;
+    while (temp > 0) {
+      if (embedding_->IsCollapseOkay(
+            META_MESH_->halfedge_handle(META_MESH_->edge_handle(temp-1), 0))) {
+        embedding_->Collapse(META_MESH_->halfedge_handle(META_MESH_->edge_handle(temp-1), 0),
+                             true);
+        embedding_->MetaGarbageCollection();
+        embedding_->CleanUpBaseMesh();
+        //qDebug() << "Number of edges: " << META_MESH_->n_edges();
+        if (embedding_->ErrStatus()) {
+          return;
+        }
+        //qDebug() << "Number of edges: " << META_MESH_->n_edges();
+      }
+      --temp;
+      if (temp > META_MESH_->n_edges())
+        temp = META_MESH_->n_edges();
+    }
+  }
+  double delta = swatch->Delta();
+  qDebug() << "TestCollapses passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::TestRelocation
+ * \param nostop
+ */
+void Testing::TestRelocation(bool nostop) {
+  Stopwatch* swatch = new Stopwatch();
+  if (nostop) ResetStop();
+  std::random_device rd;  //Will be used to obtain a seed for the random number engine
+  std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
+  std::uniform_int_distribution<> dis(0, static_cast<int>(BASE_MESH_->n_vertices())-1);
+  for (auto mvh : META_MESH_->vertices()) {
+    auto bvh = BASE_MESH_->vertex_handle(dis(gen));
+    embedding_->Relocate(mvh, bvh);
+  }
+
+  double delta = swatch->Delta();
+  qDebug() << "TestRelocation passed, time elapsed: " << delta << "ms.";
+}
+
+/*!
+ * \brief Testing::VisualAssert like an assert(), but doesn't crash the program. Instead
+ * stops ongoing tests and visually marks faulty parts in the mesh for visual debugging
+ * \param condition assert(condition), otherwise ->
+ * \param message output message if condition fails
+ * \param visualize visualization if condition fails
+ */
+void Testing::VisualAssert(bool condition, std::string message,
+                           std::vector<OpenMesh::VertexHandle> visualize) {
+  if (!condition) {
+    stop_ = true;
+    qDebug() << QString::fromStdString(message);
+    for (auto v : visualize) {
+      VisualizeVertex(v);
+    }
+  }
+}
+
+/*!
+ * \brief Testing::VisualizeVertex
+ * \param bvh vertex to be visualized
+ */
+void Testing::VisualizeVertex(OpenMesh::VertexHandle bvh) {
+  qDebug() << "Marking vertex: " << bvh.idx() << "in white.";
+  auto vertex_colors_pph = BASE_MESH_->vertex_colors_pph();
+  BASE_MESH_->property(vertex_colors_pph, bvh) = ACG::Vec4f(0.0f, 0.0f, 0.0f, 1.0f);
+  for (auto vvh : BASE_MESH_->vv_range(bvh)) {
+    if (BASE_MESH_->property(vertex_colors_pph, vvh) != ACG::Vec4f(0.0f, 0.0f, 0.0f, 1.0f)) {
+      BASE_MESH_->property(vertex_colors_pph, vvh) = ACG::Vec4f(1.0f, 1.0f, 1.0f, 1.0f);
+    }
+  }
+}
diff --git a/Testing.hh b/Testing.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2e6457fbf5c14e54d99c25d73d31bd28c6a95d0e
--- /dev/null
+++ b/Testing.hh
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
+#include <ObjectTypes/PolyMesh/PolyMesh.hh>
+#include <ObjectTypes/TriangleMesh/TriangleMesh.hh>
+#include <OpenFlipper/common/Types.hh>
+#include <ACG/Utils/HaltonColors.hh>
+
+#include "Embedding.hh"
+#include "Stopwatch.hh"
+
+class Testing {
+public:
+  Testing(Embedding* embedding);
+  ~Testing() {}
+
+  void TriangulationTests();
+  void MeshTests();
+  void OperationTests();
+  void ResetStop() {stop_=false;}
+  void DisplayMeshInformation();
+
+  // Triangulation
+  void TestVertices();
+  void TestHalfedges();
+  void TestHalfedgesInitialTriangulation();
+  void TestFaces();
+  void TestMetaMesh();
+
+  void TestVertexConnections();
+  void TestHalfedgeConnections();
+  void TestHalfedgeSymmetry();
+  void TestVoronoiBorderSymmetry();
+  void TestVoronoiBorderConnectivity();
+  void TestTriMesh();
+  void TestMetaMeshHalfedgeConnectivity();
+
+  // Basic Operation Tests
+  void TestFlips(bool nostop = false);
+  void TestSplits(bool nostop = false);
+  void TestCollapses(bool nostop = false);
+  void TestRelocation(bool nostop = false);
+
+private:
+  void TestHalfedgeConnection(OpenMesh::HalfedgeHandle mheh);
+  void TestBorderTraversal(OpenMesh::HalfedgeHandle bheh,
+                           OpenMesh::HalfedgeHandle mheh);
+  void VisualAssert(bool condition, std::string message,
+                    std::vector<OpenMesh::VertexHandle> visualize);
+  void VisualizeVertex(OpenMesh::VertexHandle bvh);
+
+  bool stop_=false;
+  Embedding* embedding_;
+};
diff --git a/renamer.py b/renamer.py
new file mode 100644
index 0000000000000000000000000000000000000000..12b309ae0bbc9aa26fb582dbfbf1d53f206eb794
--- /dev/null
+++ b/renamer.py
@@ -0,0 +1,22 @@
+# A simple script to rename the output screenshots of IsotropicRemesher
+# so that their names end in sorted numbers; this allows easy use as
+# frames to render a video, eg. with ffmpeg:
+# ffmpeg -f image2 -pix_fmt yuv420p -i target1-iterations20/IsoRemesh%d.png t1-i20.mp4
+
+from glob import glob
+import os
+
+files_list = glob("*")
+for file in files_list:
+    numbers = [file]
+    for s in file:
+        if s.isdigit():
+            numbers.append(s)
+    l = len(numbers)
+    if l == 2:
+        numbers.append("0")
+    if l == 3:
+        numbers.append(str(int(numbers[1])*5+int(numbers[2])+1))
+    if l == 4:
+        numbers.append(str((int(numbers[1])*10+int(numbers[2]))*5+int(numbers[3])+1))
+    os.rename(numbers[0], "IsoRemesh"+numbers[-1]+".png")