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
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
|
|
};
|
|
}
|
|
|