Browse Source

MeshTools: added removeDuplicates() / removeDuplicatesInto().

Those work without modifying the input data. The *Indexed and fuzzy
variants are missing as I don't need those right now, but might get
added later.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
8a6cceab6c
  1. 43
      src/Magnum/MeshTools/RemoveDuplicates.cpp
  2. 37
      src/Magnum/MeshTools/RemoveDuplicates.h
  3. 40
      src/Magnum/MeshTools/Test/RemoveDuplicatesTest.cpp

43
src/Magnum/MeshTools/RemoveDuplicates.cpp

@ -44,6 +44,43 @@ struct ArrayHash {
} }
}; };
std::size_t removeDuplicatesInto(const Containers::StridedArrayView2D<const char>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices) {
/* Assuming the second dimension is contiguous so we can calculate the
hashes easily */
CORRADE_ASSERT(data.empty()[0] || data.isContiguous<1>(),
"MeshTools::removeDuplicatesInto(): second data view dimension is not contiguous", {});
const std::size_t dataSize = data.size()[0];
CORRADE_ASSERT(indices.size() == dataSize,
"MeshTools::removeDuplicatesInto(): output index array has" << indices.size() << "elements but expected" << dataSize, {});
/* Table containing index of first occurence for each unique entry.
Reserving more buckets than necessary (i.e. as if each entry was
unique). */
std::unordered_map<Containers::ArrayView<const char>, UnsignedInt, ArrayHash, ArrayEqual> table{dataSize};
/* Go through all entries */
for(std::size_t i = 0; i != dataSize; ++i) {
/* Try to insert new entry into the table. The inserted index points
into the original unchanged data array. */
const Containers::ArrayView<const char> entry = data[i].asContiguous();
const auto result = table.emplace(entry, i);
/* Put the (either new or already existing) index into the output
index array */
indices[i] = result.first->second;
}
CORRADE_INTERNAL_ASSERT(dataSize >= table.size());
return table.size();
}
std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicates(const Containers::StridedArrayView2D<const char>& data) {
Containers::Array<UnsignedInt> indices{Containers::NoInit, data.size()[0]};
const std::size_t size = removeDuplicatesInto(data, indices);
return {std::move(indices), size};
}
std::size_t removeDuplicatesInPlaceInto(const Containers::StridedArrayView2D<char>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices) { std::size_t removeDuplicatesInPlaceInto(const Containers::StridedArrayView2D<char>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices) {
/* Assuming the second dimension is contiguous so we can calculate the /* Assuming the second dimension is contiguous so we can calculate the
hashes easily */ hashes easily */
@ -61,11 +98,13 @@ std::size_t removeDuplicatesInPlaceInto(const Containers::StridedArrayView2D<cha
/* Go through all entries */ /* Go through all entries */
for(std::size_t i = 0; i != dataSize; ++i) { for(std::size_t i = 0; i != dataSize; ++i) {
/* Try to insert new entry into the table */ /* Try to insert new entry into the table. The inserted index points
into the new data array that has all duplicates removed. */
const Containers::ArrayView<const char> entry = data[i].asContiguous(); const Containers::ArrayView<const char> entry = data[i].asContiguous();
const auto result = table.emplace(entry, table.size()); const auto result = table.emplace(entry, table.size());
/* Add the (either new or already existing) index into the array */ /* Put the (either new or already existing) index into the output index
array */
indices[i] = result.first->second; indices[i] = result.first->second;
/* If this is a new combination, copy the data to new (earlier) /* If this is a new combination, copy the data to new (earlier)

37
src/Magnum/MeshTools/RemoveDuplicates.h

@ -56,8 +56,8 @@ namespace Implementation {
@brief Remove duplicate data from given array in-place @brief Remove duplicate data from given array in-place
@param[in,out] data Data array, duplicate items will be cut away with order @param[in,out] data Data array, duplicate items will be cut away with order
preserved preserved
@return Size of unique prefix in the cleaned up @p data array and the resulting @return The resulting index array and size of unique prefix in the cleaned up
index array @p data array
@m_since_latest @m_since_latest
Removes duplicate data from given array by comparing the second dimension of Removes duplicate data from given array by comparing the second dimension of
@ -70,7 +70,11 @@ instead. Usage example:
@snippet MagnumMeshTools.cpp removeDuplicates @snippet MagnumMeshTools.cpp removeDuplicates
@see @ref Corrade::Containers::StridedArrayView::isContiguous() See @ref removeDuplicates(const Containers::StridedArrayView2D<const char>&)
for a variant that doesn't modify the input data in any way but instead returns
an index array pointing to original data locations.
@see @ref Corrade::Containers::StridedArrayView::isContiguous(),
@ref removeDuplicatesInPlaceInto()
*/ */
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesInPlace(const Containers::StridedArrayView2D<char>& data); MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesInPlace(const Containers::StridedArrayView2D<char>& data);
@ -84,9 +88,36 @@ MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<UnsignedInt>, std::size_t> r
Same as above, except that the index array is not allocated but put into Same as above, except that the index array is not allocated but put into
@p indices instead. Expects that @p indices has the same size as @p data. @p indices instead. Expects that @p indices has the same size as @p data.
@see @ref removeDuplicatesInto()
*/ */
MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesInPlaceInto(const Containers::StridedArrayView2D<char>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices); MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesInPlaceInto(const Containers::StridedArrayView2D<char>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices);
/**
@brief Remove duplicate data from given array
@param[in] data Data array
@return The resulting index array and count of unique items in the original
@p data array
@m_since_latest
Compared to @ref removeDuplicatesInPlace(const Containers::StridedArrayView2D<char>&)
this function doesn't modify the input data array in any way but instead
returns an index array pointing to original data locations.
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicates(const Containers::StridedArrayView2D<const char>& data);
/**
@brief Remove duplicate data from given array
@param[in] data Data array
@param[out] indices Where to put the resulting index array
@return Count of unique items in the original @p data array
@m_since_latest
Compared to @ref removeDuplicatesInPlaceInto(const Containers::StridedArrayView2D<char>&, const Containers::StridedArrayView1D<UnsignedInt>&)
this function doesn't modify the input data array in any way but instead
makes an index array pointing to original data locations.
*/
MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesInto(const Containers::StridedArrayView2D<const char>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices);
/** /**
@brief Remove duplicates from indexed data in-place @brief Remove duplicates from indexed data in-place
@param[in,out] indices Index array, which will get remapped to list just @param[in,out] indices Index array, which will get remapped to list just

40
src/Magnum/MeshTools/Test/RemoveDuplicatesTest.cpp

@ -36,9 +36,9 @@ namespace Magnum { namespace MeshTools { namespace Test { namespace {
struct RemoveDuplicatesTest: TestSuite::Tester { struct RemoveDuplicatesTest: TestSuite::Tester {
explicit RemoveDuplicatesTest(); explicit RemoveDuplicatesTest();
void removeDuplicatesInPlace(); void removeDuplicates();
void removeDuplicatesInPlaceNonContiguous(); void removeDuplicatesNonContiguous();
void removeDuplicatesInPlaceIntoWrongOutputSize(); void removeDuplicatesIntoWrongOutputSize();
template<class T> void removeDuplicatesIndexedInPlace(); template<class T> void removeDuplicatesIndexedInPlace();
void removeDuplicatesIndexedInPlaceSmallType(); void removeDuplicatesIndexedInPlaceSmallType();
void removeDuplicatesIndexedInPlaceEmptyIndices(); void removeDuplicatesIndexedInPlaceEmptyIndices();
@ -57,9 +57,9 @@ struct RemoveDuplicatesTest: TestSuite::Tester {
}; };
RemoveDuplicatesTest::RemoveDuplicatesTest() { RemoveDuplicatesTest::RemoveDuplicatesTest() {
addTests({&RemoveDuplicatesTest::removeDuplicatesInPlace, addTests({&RemoveDuplicatesTest::removeDuplicates,
&RemoveDuplicatesTest::removeDuplicatesInPlaceNonContiguous, &RemoveDuplicatesTest::removeDuplicatesNonContiguous,
&RemoveDuplicatesTest::removeDuplicatesInPlaceIntoWrongOutputSize, &RemoveDuplicatesTest::removeDuplicatesIntoWrongOutputSize,
&RemoveDuplicatesTest::removeDuplicatesIndexedInPlace<UnsignedByte>, &RemoveDuplicatesTest::removeDuplicatesIndexedInPlace<UnsignedByte>,
&RemoveDuplicatesTest::removeDuplicatesIndexedInPlace<UnsignedShort>, &RemoveDuplicatesTest::removeDuplicatesIndexedInPlace<UnsignedShort>,
&RemoveDuplicatesTest::removeDuplicatesIndexedInPlace<UnsignedInt>, &RemoveDuplicatesTest::removeDuplicatesIndexedInPlace<UnsignedInt>,
@ -79,38 +79,52 @@ RemoveDuplicatesTest::RemoveDuplicatesTest() {
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices}); &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices});
} }
void RemoveDuplicatesTest::removeDuplicatesInPlace() { void RemoveDuplicatesTest::removeDuplicates() {
Int data[]{-15, 32, 24, -15, 15, 7541, 24, 32}; Int data[]{-15, 32, 24, -15, 15, 7541, 24, 32};
std::pair<Containers::Array<UnsignedInt>, std::size_t> result = std::pair<Containers::Array<UnsignedInt>, std::size_t> result =
MeshTools::removeDuplicatesInPlace(Containers::arrayCast<2, char>(Containers::arrayView(data))); MeshTools::removeDuplicates(Containers::arrayCast<2, char>(Containers::arrayView(data)));
CORRADE_COMPARE_AS(Containers::arrayView(result.first), CORRADE_COMPARE_AS(Containers::arrayView(result.first),
Containers::arrayView<UnsignedInt>({0, 1, 2, 0, 4, 5, 2, 1}),
TestSuite::Compare::Container);
std::pair<Containers::Array<UnsignedInt>, std::size_t> resultInPlace =
MeshTools::removeDuplicatesInPlace(Containers::arrayCast<2, char>(Containers::arrayView(data)));
CORRADE_COMPARE_AS(Containers::arrayView(resultInPlace.first),
Containers::arrayView<UnsignedInt>({0, 1, 2, 0, 3, 4, 2, 1}), Containers::arrayView<UnsignedInt>({0, 1, 2, 0, 3, 4, 2, 1}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(result.second), CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(resultInPlace.second),
Containers::arrayView<Int>({-15, 32, 24, 15, 7541}), Containers::arrayView<Int>({-15, 32, 24, 15, 7541}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
} }
void RemoveDuplicatesTest::removeDuplicatesInPlaceNonContiguous() { void RemoveDuplicatesTest::removeDuplicatesNonContiguous() {
Int data[8]{}; Int data[8]{};
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
MeshTools::removeDuplicates(Containers::arrayCast<2, const char>(Containers::arrayView(data)).every({1, 2}));
MeshTools::removeDuplicatesInPlace(Containers::arrayCast<2, char>(Containers::arrayView(data)).every({1, 2})); MeshTools::removeDuplicatesInPlace(Containers::arrayCast<2, char>(Containers::arrayView(data)).every({1, 2}));
CORRADE_COMPARE(out.str(), "MeshTools::removeDuplicatesInPlaceInto(): second data view dimension is not contiguous\n"); CORRADE_COMPARE(out.str(),
"MeshTools::removeDuplicatesInto(): second data view dimension is not contiguous\n"
"MeshTools::removeDuplicatesInPlaceInto(): second data view dimension is not contiguous\n");
} }
void RemoveDuplicatesTest::removeDuplicatesInPlaceIntoWrongOutputSize() { void RemoveDuplicatesTest::removeDuplicatesIntoWrongOutputSize() {
Int data[8]{}; Int data[8]{};
UnsignedInt output[7]; UnsignedInt output[7];
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
MeshTools::removeDuplicatesInto(
Containers::arrayCast<2, const char>(Containers::arrayView(data)),
output);
MeshTools::removeDuplicatesInPlaceInto( MeshTools::removeDuplicatesInPlaceInto(
Containers::arrayCast<2, char>(Containers::arrayView(data)), Containers::arrayCast<2, char>(Containers::arrayView(data)),
output); output);
CORRADE_COMPARE(out.str(), "MeshTools::removeDuplicatesInPlaceInto(): output index array has 7 elements but expected 8\n"); CORRADE_COMPARE(out.str(),
"MeshTools::removeDuplicatesInto(): output index array has 7 elements but expected 8\n"
"MeshTools::removeDuplicatesInPlaceInto(): output index array has 7 elements but expected 8\n");
} }
template<class T> void RemoveDuplicatesTest::removeDuplicatesIndexedInPlace() { template<class T> void RemoveDuplicatesTest::removeDuplicatesIndexedInPlace() {

Loading…
Cancel
Save