You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

271 lines
8.2 KiB

// David Eberly, Geometric Tools, Redmond WA 98052
// Copyright (c) 1998-2021
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
// Version: 4.0.2019.08.13
#pragma once
#include <Mathematics/Logger.h>
#include <array>
#include <map>
#include <memory>
namespace gte
{
class VEManifoldMesh
{
public:
// Vertex data types.
class Vertex;
typedef std::shared_ptr<Vertex>(*VCreator)(int);
typedef std::map<int, std::shared_ptr<Vertex>> VMap;
// Edge data types.
class Edge;
typedef std::shared_ptr<Edge>(*ECreator)(int, int);
typedef std::map<std::pair<int, int>, std::shared_ptr<Edge>> EMap;
// Vertex object.
class Vertex
{
public:
virtual ~Vertex() = default;
Vertex(int v)
:
V(v)
{
}
// The unique vertex index.
int V;
// The edges (if any) sharing the vertex.
std::array<std::weak_ptr<Edge>, 2> E;
};
// Edge object.
class Edge
{
public:
virtual ~Edge() = default;
Edge(int v0, int v1)
:
V{ v0, v1 }
{
}
// Vertices, listed as a directed edge <V[0],V[1]>.
std::array<int, 2> V;
// Adjacent edges. E[i] points to edge sharing V[i].
std::array<std::weak_ptr<Edge>, 2> E;
};
// Construction and destruction.
virtual ~VEManifoldMesh() = default;
VEManifoldMesh(VCreator vCreator = nullptr, ECreator eCreator = nullptr)
:
mVCreator(vCreator ? vCreator : CreateVertex),
mECreator(eCreator ? eCreator : CreateEdge),
mThrowOnNonmanifoldInsertion(true)
{
}
// Member access.
inline VMap const& GetVertices() const
{
return mVMap;
}
inline EMap const& GetEdges() const
{
return mEMap;
}
// If the insertion of an edge fails because the mesh would become
// nonmanifold, the default behavior is to throw an exception. You
// can disable this behavior and continue gracefully without an
// exception.
void ThrowOnNonmanifoldInsertion(bool doException)
{
mThrowOnNonmanifoldInsertion = doException;
}
// If <v0,v1> is not in the mesh, an Edge object is created and
// returned; otherwise, <v0,v1> is in the mesh and nullptr is
// returned. If the insertion leads to a nonmanifold mesh, the
// call fails with a nullptr returned.
std::shared_ptr<Edge> Insert(int v0, int v1)
{
std::pair<int, int> ekey(v0, v1);
if (mEMap.find(ekey) != mEMap.end())
{
// The edge already exists. Return a null pointer as a
// signal to the caller that the insertion failed.
return nullptr;
}
// Add the new edge.
std::shared_ptr<Edge> edge = mECreator(v0, v1);
mEMap[ekey] = edge;
// Add the vertices if they do not already exist.
for (int i = 0; i < 2; ++i)
{
int v = edge->V[i];
std::shared_ptr<Vertex> vertex;
auto viter = mVMap.find(v);
if (viter == mVMap.end())
{
// This is the first time the vertex is encountered.
vertex = mVCreator(v);
mVMap[v] = vertex;
// Update the vertex.
vertex->E[0] = edge;
}
else
{
// This is the second time the vertex is encountered.
vertex = viter->second;
LogAssert(vertex != nullptr, "Unexpected condition.");
// Update the vertex.
if (vertex->E[1].lock())
{
if (mThrowOnNonmanifoldInsertion)
{
LogError("The mesh must be manifold.");
}
else
{
return nullptr;
}
}
vertex->E[1] = edge;
// Update the adjacent edge.
auto adjacent = vertex->E[0].lock();
LogAssert(adjacent != nullptr, "Unexpected condition.");
for (int j = 0; j < 2; ++j)
{
if (adjacent->V[j] == v)
{
adjacent->E[j] = edge;
break;
}
}
// Update the edge.
edge->E[i] = adjacent;
}
}
return edge;
}
// If <v0,v1> is in the mesh, it is removed and 'true' is returned;
// otherwise, <v0,v1> is not in the mesh and 'false' is returned.
bool Remove(int v0, int v1)
{
std::pair<int, int> ekey(v0, v1);
auto eiter = mEMap.find(ekey);
if (eiter == mEMap.end())
{
// The edge does not exist.
return false;
}
// Get the edge.
std::shared_ptr<Edge> edge = eiter->second;
// Remove the vertices if necessary (when they are not shared).
for (int i = 0; i < 2; ++i)
{
// Inform the vertices the edge is being deleted.
auto viter = mVMap.find(edge->V[i]);
LogAssert(viter != mVMap.end(), "Unexpected condition.");
std::shared_ptr<Vertex> vertex = viter->second;
LogAssert(vertex != nullptr, "Unexpected condition.");
if (vertex->E[0].lock() == edge)
{
// One-edge vertices always have pointer at index zero.
vertex->E[0] = vertex->E[1];
vertex->E[1].reset();
}
else if (vertex->E[1].lock() == edge)
{
vertex->E[1].reset();
}
else
{
LogError("Unexpected condition.");
}
// Remove the vertex if you have the last reference to it.
if (!vertex->E[0].lock() && !vertex->E[1].lock())
{
mVMap.erase(vertex->V);
}
// Inform adjacent edges the edge is being deleted.
auto adjacent = edge->E[i].lock();
if (adjacent)
{
for (int j = 0; j < 2; ++j)
{
if (adjacent->E[j].lock() == edge)
{
adjacent->E[j].reset();
break;
}
}
}
}
mEMap.erase(ekey);
return true;
}
// A manifold mesh is closed if each vertex is shared twice.
bool IsClosed() const
{
for (auto const& element : mVMap)
{
auto vertex = element.second;
if (!vertex->E[0].lock() || !vertex->E[1].lock())
{
return false;
}
}
return true;
}
protected:
// The vertex data and default vertex creation.
static std::shared_ptr<Vertex> CreateVertex(int v0)
{
return std::make_shared<Vertex>(v0);
}
VCreator mVCreator;
VMap mVMap;
// The edge data and default edge creation.
static std::shared_ptr<Edge> CreateEdge(int v0, int v1)
{
return std::make_shared<Edge>(v0, v1);
}
ECreator mECreator;
EMap mEMap;
bool mThrowOnNonmanifoldInsertion; // default: true
};
}