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.
370 lines
15 KiB
370 lines
15 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: 5.1.2020.09.18
|
|
|
|
#pragma once
|
|
|
|
// UniqueVerticesSimplices allows mesh generation and elimination of duplicate
|
|
// and/or unused vertices. The vertices have type VertexType, which must have
|
|
// a less-than comparison predicate because it is used as the key type in
|
|
// std::map. The IndexType can be any signed or unsigned integer type, not
|
|
// including 1-byte types or bool. The mesh can be in any dimension D >= 2. In
|
|
// 2 dimensions, the mesh is a collection of edges. In 3 dimensions, the mesh
|
|
// is a collection of triangles. Generally, the mesh is a collection of
|
|
// D-dimensional simplices. The following operations are supported, either for
|
|
// a mesh topology consisting of indices or of arrays, each array representing
|
|
// a simplex.
|
|
//
|
|
// 1. Generate an indexed simplex representation from an array of simplices,
|
|
// each simplex represented by D contiguous vertices. Presumably, the
|
|
// simplices share vertices. The output is an array of unique simplices
|
|
// (a vertex pool) and an array of D-element arrays of indices into the
|
|
// pool, each such array representing a simplex.
|
|
//
|
|
// 2. Remove duplicate vertices from a vertex pool used by an indexed
|
|
// simplex representation. A new vertex pool of unique vertices is
|
|
// generated and the indexed simplices are modified to be indices into
|
|
// this vertex pool.
|
|
//
|
|
// 3. Remove unused vertices from a vertex pool used by an indexed simplex
|
|
// representation. A new vertex pool of unique vertices is generated and
|
|
// the indexed simplices are modified to be indices into the new vertex
|
|
// pool.
|
|
//
|
|
// 4. Remove duplicate and unused vertices from a vertex pool, a combination
|
|
// of the operations in #2 and #3.
|
|
//
|
|
// In the Geometric Tools distribution, the class is used for polygon Boolean
|
|
// operations (D = 2) and for compactifying triangle meshes (D = 3).
|
|
|
|
#include <Mathematics/Logger.h>
|
|
#include <array>
|
|
#include <map>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
namespace gte
|
|
{
|
|
template <typename VertexType, typename IndexType, size_t Dimension>
|
|
class UniqueVerticesSimplices
|
|
{
|
|
public:
|
|
UniqueVerticesSimplices()
|
|
{
|
|
// The index type must be an integral type that does not include
|
|
// bool. MSVS 2019 16.7.3 does not trigger this static assertion
|
|
// when IndexType is bool. Instead, it complains about using
|
|
// .data() for the std::vector<bool> objects and about comparing
|
|
// bool values to 0 in the LogAssert statements, after which the
|
|
// compilation terminates.
|
|
static_assert(
|
|
std::is_integral<IndexType>::value &&
|
|
!std::is_same<IndexType, bool>::value,
|
|
"Invalid index type.");
|
|
}
|
|
|
|
// See #1 in the comments at the beginning of this file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is a positive multiple of D
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outIndices.size() = inVertices.size()
|
|
// 3. 0 <= outIndices[i] < outVertices.size()
|
|
void GenerateIndexedSimplices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<IndexType>& outIndices)
|
|
{
|
|
LogAssert(
|
|
inVertices.size() > 0 &&
|
|
inVertices.size() % Dimension == 0,
|
|
"Invalid number of vertices.");
|
|
|
|
outIndices.resize(inVertices.size());
|
|
RemoveDuplicates(inVertices, outVertices, outIndices.data());
|
|
}
|
|
|
|
// See #1 in the comments at the beginning of this file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is a positive multiple of Dimension
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outSimplices.size() = inVertices.size() / Dimension
|
|
// 3. 0 <= outSimplices[s][d] < outVertices.size()
|
|
void GenerateIndexedSimplices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<std::array<IndexType, Dimension>>& outSimplices)
|
|
{
|
|
LogAssert(
|
|
inVertices.size() > 0 &&
|
|
inVertices.size() % Dimension == 0,
|
|
"Invalid number of vertices.");
|
|
|
|
outSimplices.resize(inVertices.size() / Dimension);
|
|
IndexType* outIndices = reinterpret_cast<IndexType*>(outSimplices.data());
|
|
RemoveDuplicates(inVertices, outVertices, outIndices);
|
|
}
|
|
|
|
// See #2 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inIndices.size() is a positive multiple of Dimension
|
|
// 3. 0 <= inIndices[i] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outIndices.size() = inIndices.size()
|
|
// 3. 0 <= outIndices[i] < outVertices.size()
|
|
void RemoveDuplicateVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<IndexType> const& inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<IndexType>& outIndices)
|
|
{
|
|
LogAssert(
|
|
inVertices.size() > 0,
|
|
"Invalid number of vertices.");
|
|
LogAssert(
|
|
inIndices.size() > 0 &&
|
|
inIndices.size() % Dimension == 0,
|
|
"Invalid number of indices.");
|
|
IndexType const numVertices = static_cast<IndexType>(inVertices.size());
|
|
for (auto index : inIndices)
|
|
{
|
|
LogAssert(
|
|
0 <= index && index < numVertices,
|
|
"Invalid index.");
|
|
}
|
|
|
|
std::vector<IndexType> inToOutMapping(inVertices.size());
|
|
RemoveDuplicates(inVertices, outVertices, inToOutMapping.data());
|
|
|
|
outIndices.resize(inIndices.size());
|
|
for (size_t i = 0; i < inIndices.size(); ++i)
|
|
{
|
|
outIndices[i] = inToOutMapping[inIndices[i]];
|
|
}
|
|
}
|
|
|
|
// See #2 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inSimplices.size() is positive
|
|
// 3. 0 <= inSimplices[s][d] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices has unique vertices
|
|
// 2. outSimplices.size() = inSimplices.size()
|
|
// 3. 0 <= outSimplices[s][d] < outVertices.size()
|
|
void RemoveDuplicateVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<std::array<IndexType, Dimension>> const& inSimplices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<std::array<IndexType, Dimension>>& outSimplices)
|
|
{
|
|
LogAssert(
|
|
inVertices.size() > 0,
|
|
"Invalid number of vertices.");
|
|
LogAssert(inSimplices.size() > 0,
|
|
"Invalid number of simplices.");
|
|
IndexType const numVertices = static_cast<IndexType>(inVertices.size());
|
|
for (auto const& simplex : inSimplices)
|
|
{
|
|
for (size_t d = 0; d < Dimension; ++d)
|
|
{
|
|
LogAssert(
|
|
0 <= simplex[d] && simplex[d] < numVertices,
|
|
"Invalid index.");
|
|
}
|
|
}
|
|
|
|
std::vector<IndexType> inToOutMapping(inVertices.size());
|
|
RemoveDuplicates(inVertices, outVertices, inToOutMapping.data());
|
|
|
|
size_t const numSimplices = inSimplices.size();
|
|
outSimplices.resize(numSimplices);
|
|
for (size_t s = 0; s < numSimplices; ++s)
|
|
{
|
|
for (size_t d = 0; d < Dimension; ++d)
|
|
{
|
|
outSimplices[s][d] = inToOutMapping[inSimplices[s][d]];
|
|
}
|
|
}
|
|
}
|
|
|
|
// See #3 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inIndices.size() is a positive multiple of Dimension
|
|
// 3. 0 <= inIndices[i] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices.size() is positive
|
|
// 2. outIndices.size() = inIndices.size()
|
|
// 3. 0 <= outIndices[i] < outVertices.size()
|
|
// 4. each outVertices[v] occurs at least once in outIndices[]
|
|
void RemoveUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<IndexType> const& inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<IndexType>& outIndices)
|
|
{
|
|
LogAssert(inVertices.size() > 0,
|
|
"Invalid number of vertices.");
|
|
LogAssert(
|
|
inIndices.size() > 0 &&
|
|
inIndices.size() % Dimension == 0,
|
|
"Invalid number of indices.");
|
|
IndexType const numVertices = static_cast<IndexType>(inVertices.size());
|
|
for (auto index : inIndices)
|
|
{
|
|
LogAssert(
|
|
0 <= index && index < numVertices,
|
|
"Invalid index.");
|
|
}
|
|
|
|
outIndices.resize(inIndices.size());
|
|
RemoveUnused(inVertices, inIndices.size(), inIndices.data(),
|
|
outVertices, outIndices.data());
|
|
}
|
|
|
|
// See #3 in the comments at the beginning of the file. The
|
|
// preconditions are
|
|
// 1. inVertices.size() is positive
|
|
// 2. inSimplices.size() is positive
|
|
// 3. 0 <= inSimplices[s][d] < inVertices.size()
|
|
// The postconditions are
|
|
// 1. outVertices.size() is positive
|
|
// 2. outSimplices.size() = inSimplices.size()
|
|
// 3. 0 <= outSimplices[s][d] < outVertices.size()
|
|
// 4. each outVertices[v] occurs at least once in outSimplices[][]
|
|
void RemoveUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<std::array<IndexType, Dimension>> const& inSimplices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<std::array<IndexType, Dimension>>& outSimplices)
|
|
{
|
|
LogAssert(
|
|
inVertices.size() > 0,
|
|
"Invalid number of vertices.");
|
|
LogAssert(
|
|
inSimplices.size() > 0,
|
|
"Invalid number of simplices.");
|
|
IndexType const numVertices = static_cast<IndexType>(inVertices.size());
|
|
for (auto const& simplex : inSimplices)
|
|
{
|
|
for (size_t d = 0; d < Dimension; ++d)
|
|
{
|
|
LogAssert(
|
|
0 <= simplex[d] && simplex[d] < numVertices,
|
|
"Invalid index.");
|
|
}
|
|
}
|
|
|
|
outSimplices.resize(inSimplices.size());
|
|
size_t const numInIndices = Dimension * inSimplices.size();
|
|
IndexType const* inIndices = reinterpret_cast<IndexType const*>(inSimplices.data());
|
|
IndexType* outIndices = reinterpret_cast<IndexType*>(outSimplices.data());
|
|
RemoveUnused(inVertices, numInIndices, inIndices, outVertices, outIndices);
|
|
}
|
|
|
|
// See #4 and the preconditions for RemoveDuplicateVertices and for
|
|
// RemoveUnusedVertices.
|
|
void RemoveDuplicateAndUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<IndexType> const& inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<IndexType>& outIndices)
|
|
{
|
|
std::vector<VertexType> tempVertices;
|
|
std::vector<IndexType> tempIndices;
|
|
RemoveDuplicateVertices(inVertices, inIndices, tempVertices, tempIndices);
|
|
RemoveUnusedVertices(tempVertices, tempIndices, outVertices, outIndices);
|
|
}
|
|
|
|
// See #4 and the preconditions for RemoveDuplicateVertices and for
|
|
// RemoveUnusedVertices.
|
|
void RemoveDuplicateAndUnusedVertices(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<std::array<IndexType, Dimension>> const& inSimplices,
|
|
std::vector<VertexType>& outVertices,
|
|
std::vector<std::array<IndexType, Dimension>>& outSimplices)
|
|
{
|
|
std::vector<VertexType> tempVertices;
|
|
std::vector<std::array<IndexType, Dimension>> tempSimplices;
|
|
RemoveDuplicateVertices(inVertices, inSimplices, tempVertices, tempSimplices);
|
|
RemoveUnusedVertices(tempVertices, tempSimplices, outVertices, outSimplices);
|
|
}
|
|
|
|
private:
|
|
void RemoveDuplicates(
|
|
std::vector<VertexType> const& inVertices,
|
|
std::vector<VertexType>& outVertices,
|
|
IndexType* inToOutMapping)
|
|
{
|
|
// Construct the unique vertices.
|
|
size_t const numInVertices = inVertices.size();
|
|
size_t numOutVertices = 0;
|
|
std::map<VertexType, IndexType> vmap;
|
|
for (size_t i = 0; i < numInVertices; ++i)
|
|
{
|
|
auto const iter = vmap.find(inVertices[i]);
|
|
if (iter != vmap.end())
|
|
{
|
|
// The vertex is a duplicate of one inserted earlier into
|
|
// the map. Its index i will be modified to that of the
|
|
// first-found vertex.
|
|
inToOutMapping[i] = iter->second;
|
|
}
|
|
else
|
|
{
|
|
// The vertex occurs for the first time.
|
|
vmap.insert(std::make_pair(inVertices[i],
|
|
static_cast<IndexType>(numOutVertices)));
|
|
inToOutMapping[i] = static_cast<IndexType>(numOutVertices);
|
|
++numOutVertices;
|
|
}
|
|
}
|
|
|
|
// Pack the unique vertices into an array.
|
|
outVertices.resize(numOutVertices);
|
|
for (auto const& element : vmap)
|
|
{
|
|
outVertices[element.second] = element.first;
|
|
}
|
|
}
|
|
|
|
void RemoveUnused(
|
|
std::vector<VertexType> const& inVertices,
|
|
size_t const numInIndices,
|
|
IndexType const* inIndices,
|
|
std::vector<VertexType>& outVertices,
|
|
IndexType* outIndices)
|
|
{
|
|
// Get the unique set of used indices.
|
|
std::set<IndexType> usedIndices;
|
|
for (size_t i = 0; i < numInIndices; ++i)
|
|
{
|
|
usedIndices.insert(inIndices[i]);
|
|
}
|
|
|
|
// Locate the used vertices and pack them into an array.
|
|
outVertices.resize(usedIndices.size());
|
|
size_t numOutVertices = 0;
|
|
std::map<IndexType, IndexType> vmap;
|
|
for (auto oldIndex : usedIndices)
|
|
{
|
|
outVertices[numOutVertices] = inVertices[oldIndex];
|
|
vmap.insert(std::make_pair(oldIndex, static_cast<IndexType>(numOutVertices)));
|
|
++numOutVertices;
|
|
}
|
|
|
|
// Reassign the old indices to the new indices.
|
|
for (size_t i = 0; i < numInIndices; ++i)
|
|
{
|
|
outIndices[i] = vmap.find(inIndices[i])->second;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|