Browse Source

MeshTools: clean up and clarify removeDuplicates().

I spent a week (!) thinking the extra remapping array is not necessary.
Actually, it is (though with a non-shitty hashmap the allocation could
be done for both) -- this was an university assignment almost a decade
ago and it wouldn't pass if it would be wasting time.

But the english of past me was horrible. Yes.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
274759c0be
  1. 51
      src/Magnum/MeshTools/RemoveDuplicates.h

51
src/Magnum/MeshTools/RemoveDuplicates.h

@ -63,15 +63,15 @@ no interpolation is done. Note that this function is meant to be used for
floating-point data (or generally with non-zero @p epsilon), for discrete data floating-point data (or generally with non-zero @p epsilon), for discrete data
the usual sorting method is much more efficient. the usual sorting method is much more efficient.
If you want to remove duplicate data from already indexed array, first remove If you want to remove duplicate data from an already indexed array, first
duplicates as if the array wasn't indexed at all and then use @ref duplicate() remove duplicates as if the array wasn't indexed at all and then use
to combine the two index arrays: @ref duplicate() to combine the two index arrays:
@snippet MagnumMeshTools.cpp removeDuplicates1 @snippet MagnumMeshTools.cpp removeDuplicates1
Removing duplicates in multiple indcidental arrays is also possible --- first Removing duplicates in multiple indcidental arrays is also possible --- first
remove duplicates in each array separately and then use @ref combineIndexedArrays() remove duplicates in each array separately and then use @ref combineIndexedArrays()
to combine the resulting index arrays to single index array and reorder the to combine the resulting index arrays to single index array, and reorder the
data accordingly: data accordingly:
@snippet MagnumMeshTools.cpp removeDuplicates2 @snippet MagnumMeshTools.cpp removeDuplicates2
@ -86,35 +86,42 @@ template<class Vector> std::vector<UnsignedInt> removeDuplicates(std::vector<Vec
bounds. */ bounds. */
epsilon = Math::max(epsilon, typename Vector::Type((minmax.second-minmax.first).max()/~std::size_t{})); epsilon = Math::max(epsilon, typename Vector::Type((minmax.second-minmax.first).max()/~std::size_t{}));
/* Resulting index array */ /* Resulting index array. Because we'll be remapping these, we need to
std::vector<UnsignedInt> resultIndices(data.size()); start from a 0..n sequence. */
std::iota(resultIndices.begin(), resultIndices.end(), 0); std::vector<UnsignedInt> indices(data.size());
std::iota(indices.begin(), indices.end(), 0);
/* Table containing original vector index for each discretized vector. /* Table containing original vector index for each discretized vector.
Reserving more buckets than necessary (i.e. as if each vector was Reserving more buckets than necessary (i.e. as if each vector was
unique). */ unique). */
std::unordered_map<Math::Vector<Vector::Size, std::size_t>, UnsignedInt, Implementation::VectorHash<Vector::Size>> table(data.size()); std::unordered_map<Math::Vector<Vector::Size, std::size_t>, UnsignedInt, Implementation::VectorHash<Vector::Size>> table(data.size());
/* Index array for each pass, new data array */ /* Index array that'll be filled in each pass and then used for remapping
std::vector<UnsignedInt> indices; the `indices` */
indices.reserve(data.size()); std::vector<UnsignedInt> remapping(data.size());
/* First go with original coordinates, then move them by epsilon/2 in each /* First go with original coordinates, then move them by epsilon/2 in each
direction. */ direction. */
Vector moved; Vector moved;
for(std::size_t moving = 0; moving <= Vector::Size; ++moving) { for(std::size_t moving = 0; moving <= Vector::Size; ++moving) {
/* Clear the table for this pass */
table.clear();
/* Go through all vectors */ /* Go through all vectors */
for(std::size_t i = 0; i != data.size(); ++i) { for(std::size_t i = 0; i != data.size(); ++i) {
/* Try to insert new vertex to the table */ /* Try to insert new vertex into the table */
const Math::Vector<Vector::Size, std::size_t> v((data[i] + moved - minmax.first)/epsilon); const Math::Vector<Vector::Size, std::size_t> v{(data[i] + moved - minmax.first)/epsilon};
const auto result = table.emplace(v, table.size()); const auto result = table.emplace(v, table.size());
/* Add the (either new or already existing) index to index array */ /* Add the (either new or already existing) index into index array */
indices.push_back(result.first->second); remapping[i] = result.first->second;
/* If this is new combination, copy the data to new (earlier) /* If this is a new combination, copy the data to new (earlier)
possition in the array */ position in the array. Data in [table.size()-1, i) are already
if(result.second && i != table.size()-1) data[table.size()-1] = data[i]; present in the [0, table.size()-1) range from previous
iterations so we aren't overwriting anything. */
if(result.second && i != table.size() - 1)
data[table.size()-1] = data[i];
} }
/* Shrink the data array */ /* Shrink the data array */
@ -122,21 +129,17 @@ template<class Vector> std::vector<UnsignedInt> removeDuplicates(std::vector<Vec
data.resize(table.size()); data.resize(table.size());
/* Remap the resulting index array */ /* Remap the resulting index array */
for(auto& i: resultIndices) i = indices[i]; for(auto& i: indices) i = remapping[i];
/* Finished */ /* Finished */
if(moving == Vector::Size) continue; if(moving == Vector::Size) continue;
/* Move vertex coordinates by epsilon/2 in next direction */ /* Move vertex coordinates by epsilon/2 in the next direction */
moved = Vector(); moved = Vector();
moved[moving] = epsilon/2; moved[moving] = epsilon/2;
/* Clear the structures for next pass */
table.clear();
indices.clear();
} }
return resultIndices; return indices;
} }
}} }}

Loading…
Cancel
Save