Browse Source

MeshTools: de-template removeDuplicatesFuzzy().

Basically using the same idea as with the discrete version -- having the
second dimension dynamic, together with restricting the implementation to
just Float and Double.

According to the SubdivideRemoveDuplicatesBenchmark, this makes the
implementation slightly slower. I presume this is due to how minmax and
offsets are calculated which is quite cache-inefficient as it goes over
the same memory block multiple times. Added a TODO for later.
pull/449/head
Vladimír Vondruš 6 years ago
parent
commit
d846944b7a
  1. 1
      doc/custom-buildsystems-order.dot
  2. 3
      doc/snippets/MagnumMeshTools.cpp
  3. 2
      modules/FindMagnum.cmake
  4. 1
      src/Magnum/MeshTools/Combine.cpp
  5. 190
      src/Magnum/MeshTools/RemoveDuplicates.cpp
  6. 183
      src/Magnum/MeshTools/RemoveDuplicates.h
  7. 165
      src/Magnum/MeshTools/Test/RemoveDuplicatesTest.cpp
  8. 6
      src/Magnum/MeshTools/Test/SubdivideRemoveDuplicatesBenchmark.cpp
  9. 1
      src/Magnum/MeshTools/Test/SubdivideTest.cpp
  10. 1
      src/Magnum/Primitives/CMakeLists.txt
  11. 4
      src/Magnum/Primitives/Icosphere.cpp

1
doc/custom-buildsystems-order.dot

@ -79,6 +79,7 @@ digraph "Magnum library dependency order" {
MagnumOpenGLTester -> MagnumWindowlessApplication MagnumOpenGLTester -> MagnumWindowlessApplication
MagnumPrimitives -> MagnumMeshTools
MagnumPrimitives -> MagnumTrade MagnumPrimitives -> MagnumTrade
MagnumSceneGraph -> Magnum MagnumSceneGraph -> Magnum

3
doc/snippets/MagnumMeshTools.cpp

@ -23,6 +23,9 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <tuple>
#include <vector>
#include "Magnum/Math/Color.h" #include "Magnum/Math/Color.h"
#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/Math/FunctionsBatch.h"
#include "Magnum/MeshTools/CompressIndices.h" #include "Magnum/MeshTools/CompressIndices.h"

2
modules/FindMagnum.cmake

@ -416,7 +416,7 @@ elseif(CORRADE_TARGET_WINDOWS)
endif() endif()
endif() endif()
set(_MAGNUM_Primitives_DEPENDENCIES Trade) set(_MAGNUM_Primitives_DEPENDENCIES MeshTools Trade)
set(_MAGNUM_SceneGraph_DEPENDENCIES ) set(_MAGNUM_SceneGraph_DEPENDENCIES )
set(_MAGNUM_Shaders_DEPENDENCIES GL) set(_MAGNUM_Shaders_DEPENDENCIES GL)
set(_MAGNUM_Text_DEPENDENCIES TextureTools) set(_MAGNUM_Text_DEPENDENCIES TextureTools)

1
src/Magnum/MeshTools/Combine.cpp

@ -30,6 +30,7 @@
#include <Corrade/Containers/Reference.h> #include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/Algorithms.h> #include <Corrade/Utility/Algorithms.h>
#include "Magnum/Math/Functions.h"
#include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/Interleave.h"
#include "Magnum/MeshTools/Duplicate.h" #include "Magnum/MeshTools/Duplicate.h"
#include "Magnum/MeshTools/RemoveDuplicates.h" #include "Magnum/MeshTools/RemoveDuplicates.h"

190
src/Magnum/MeshTools/RemoveDuplicates.cpp

@ -26,9 +26,16 @@
#include "RemoveDuplicates.h" #include "RemoveDuplicates.h"
#include <cstring> #include <cstring>
#include <limits>
#include <numeric>
#include <unordered_map>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StridedArrayView.h> #include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Algorithms.h> #include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/MurmurHash2.h>
#include "Magnum/Math/FunctionsBatch.h"
#include "Magnum/Math/Range.h"
#include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/Concatenate.h"
#include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/Interleave.h"
#include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/MeshData.h"
@ -173,6 +180,189 @@ std::size_t removeDuplicatesIndexedInPlace(const Containers::StridedArrayView2D<
} }
} }
namespace {
template<class IndexType, class T> std::size_t removeDuplicatesFuzzyIndexedInPlaceImplementation(const Containers::StridedArrayView1D<IndexType>& indices, const Containers::StridedArrayView2D<T>& data, T epsilon) {
/* Compared to the discrete version, we don't require the second dimension
to be contiguous, as we calculate the hash from a discretized contiguous
copy */
/* Somehow ~IndexType{} doesn't work for < 4byte types, as the result is
int(-1) instead of the type I want */
CORRADE_ASSERT(data.size()[0] <= IndexType(-1),
"MeshTools::removeDuplicatesFuzzyIndexedInPlace(): a" << sizeof(IndexType) << Debug::nospace << "-byte index type is too small for" << data.size()[0] << "vertices", {});
/* Get bounds across all dimensions. When NaNs appear, those will get
collapsed together when you're lucky, or cause the whole data to
disappear when you're not -- it needs a much more specialized handling
to be robust. */
const std::size_t vectorSize = data.size()[1];
T range = T(0.0);
Containers::Array<T> offsets{Containers::NoInit, vectorSize};
{
/** @todo this isn't really cache-efficient, do differently */
std::size_t i = 0;
for(Containers::StridedArrayView1D<T> dimension: data.template transposed<0, 1>()) {
const Math::Range1D<T> minmax = Math::minmax(dimension);
range = Math::max(minmax.size(), range);
offsets[i++] = minmax.min();
}
}
/* Make epsilon so large that std::size_t can index all vectors inside the
bounds. */
epsilon = Math::max(epsilon, range/T(~std::size_t{}));
/* Table containing original vector index for each discretized vector.
Reserving more buckets than necessary (i.e. as if each vector was
unique). */
std::size_t dataSize = data.size()[0];
std::unordered_map<Containers::ArrayView<const char>, UnsignedInt, ArrayHash, ArrayEqual> table{dataSize};
/* Index array that'll be filled in each pass and then used for remapping
the `indices` */
Containers::Array<UnsignedInt> remapping{Containers::NoInit, dataSize};
/* First go with original coordinates, then move them by epsilon/2 in each
dimension. */
T moveAmount = T(0.0);
Containers::Array<std::size_t> discretized{Containers::NoInit, vectorSize};
for(std::size_t moving = 0; moving <= vectorSize; ++moving) {
for(std::size_t i = 0; i != dataSize; ++i) {
/* Take the original vector and discretize it -- append the move
amount to given dimension, subtract the minmal offset and divide
by epsilon. */
const Containers::StridedArrayView1D<T> entry = data[i];
for(std::size_t vi = 0; vi != vectorSize; ++vi) {
T c = entry[vi];
/* In iteration `0` we're not moving in any dimension, in
iteration `vectorSize` we're moving in `vectorSize - 1`
dimension */
if(vi + 1 == moving) c += moveAmount;
discretized[vi] = (c - offsets[vi])/epsilon;
}
/* Try to insert new entry into the table. The inserted index
points into the new data array that has all duplicates removed.
This is a similar workflow to removeDuplicatesInPlaceInto() with
the only difference that we're remapping an existing index array
several times over instead of creating a new one */
const auto result = table.emplace(Containers::arrayCast<const char>(discretized), table.size());
/* Add the (either new or already existing) index into the array */
remapping[i] = result.first->second;
/* If this is a new combination, copy the data to new (earlier)
position in the array. Data in [table.size()-1, i) are already
present in the [0, table.size()-1) range from previous
iterations so we aren't overwriting anything. */
if(result.second && i != table.size() - 1)
Utility::copy(entry, data[table.size() - 1]);
}
/* Remap the resulting index array */
for(auto& i: indices) i = remapping[i];
/* Move vertex coordinates by epsilon/2 in the next dimension (which
is moving + 1 in the next loop iteration) */
moveAmount = epsilon/2;
/* Next time go only through the unique prefix; clear the table for the
next pass */
dataSize = table.size();
table.clear();
}
CORRADE_INTERNAL_ASSERT(data.size()[0] >= dataSize);
return dataSize;
}
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const Containers::StridedArrayView2D<Float>& data, const Float epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, const Containers::StridedArrayView2D<Float>& data, const Float epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, const Containers::StridedArrayView2D<Float>& data, const Float epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const Containers::StridedArrayView2D<Double>& data, const Double epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, const Containers::StridedArrayView2D<Double>& data, const Double epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, const Containers::StridedArrayView2D<Double>& data, const Double epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
namespace {
template<class T> std::size_t removeDuplicatesFuzzyInPlaceIntoImplementation(const Containers::StridedArrayView2D<T>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices, const T epsilon) {
CORRADE_ASSERT(indices.size() == data.size()[0],
"MeshTools::removeDuplicatesFuzzyInPlaceInto(): output index array has" << indices.size() << "elements but expected" << data.size()[0], {});
/* A trivial index array that'll be remapped */
std::iota(indices.begin(), indices.end(), 0);
const std::size_t size = removeDuplicatesFuzzyIndexedInPlaceImplementation(Containers::stridedArrayView(indices), data, epsilon);
return size;
}
template<class T> std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesFuzzyInPlaceImplementation(const Containers::StridedArrayView2D<T>& data, const T epsilon) {
Containers::Array<UnsignedInt> indices{Containers::NoInit, data.size()[0]};
const std::size_t size = removeDuplicatesFuzzyInPlaceIntoImplementation(data, indices, epsilon);
return {std::move(indices), size};
}
}
std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView2D<Float>& data, const Float epsilon) {
return removeDuplicatesFuzzyInPlaceImplementation(data, epsilon);
}
std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView2D<Double>& data, const Double epsilon) {
return removeDuplicatesFuzzyInPlaceImplementation(data, epsilon);
}
std::size_t removeDuplicatesFuzzyInPlaceInto(const Containers::StridedArrayView2D<Float>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices, const Float epsilon) {
return removeDuplicatesFuzzyInPlaceIntoImplementation(data, indices, epsilon);
}
std::size_t removeDuplicatesFuzzyInPlaceInto(const Containers::StridedArrayView2D<Double>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices, const Double epsilon) {
return removeDuplicatesFuzzyInPlaceIntoImplementation(data, indices, epsilon);
}
namespace {
template<class T> std::size_t removeDuplicatesFuzzyIndexedInPlaceImplementation(const Containers::StridedArrayView2D<char>& indices, const Containers::StridedArrayView2D<T>& data, const T epsilon) {
CORRADE_ASSERT(indices.isContiguous<1>(), "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): second index view dimension is not contiguous", {});
if(indices.size()[1] == 4)
return removeDuplicatesFuzzyIndexedInPlaceImplementation(Containers::arrayCast<1, UnsignedInt>(indices), data, epsilon);
else if(indices.size()[1] == 2)
return removeDuplicatesFuzzyIndexedInPlaceImplementation(Containers::arrayCast<1, UnsignedShort>(indices), data, epsilon);
else {
CORRADE_ASSERT(indices.size()[1] == 1, "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): expected index type size 1, 2 or 4 but got" << indices.size()[1], {});
return removeDuplicatesFuzzyIndexedInPlaceImplementation(Containers::arrayCast<1, UnsignedByte>(indices), data, epsilon);
}
}
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView2D<char>& indices, const Containers::StridedArrayView2D<Float>& data, const Float epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView2D<char>& indices, const Containers::StridedArrayView2D<Double>& data, const Double epsilon) {
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
}
Trade::MeshData removeDuplicates(const Trade::MeshData& data) { Trade::MeshData removeDuplicates(const Trade::MeshData& data) {
return removeDuplicates(Trade::MeshData{data.primitive(), return removeDuplicates(Trade::MeshData{data.primitive(),
{}, data.indexData(), Trade::MeshIndexData{data.indices()}, {}, data.indexData(), Trade::MeshIndexData{data.indices()},

183
src/Magnum/MeshTools/RemoveDuplicates.h

@ -29,29 +29,20 @@
* @brief Function @ref Magnum::MeshTools::removeDuplicatesInPlace(), @ref Magnum::MeshTools::removeDuplicatesIndexedInPlace() * @brief Function @ref Magnum::MeshTools::removeDuplicatesInPlace(), @ref Magnum::MeshTools::removeDuplicatesIndexedInPlace()
*/ */
#include <limits> #include <utility>
#include <numeric>
#include <unordered_map>
#include <vector>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Utility/MurmurHash2.h>
#include "Magnum/Magnum.h" #include "Magnum/Magnum.h"
#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/Math/TypeTraits.h"
#include "Magnum/MeshTools/visibility.h" #include "Magnum/MeshTools/visibility.h"
#include "Magnum/Trade/Trade.h" #include "Magnum/Trade/Trade.h"
namespace Magnum { namespace MeshTools { #ifdef MAGNUM_BUILD_DEPRECATED
#include <vector>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Containers/StridedArrayView.h>
#endif
namespace Implementation { namespace Magnum { namespace MeshTools {
template<std::size_t size> class VectorHash {
public:
std::size_t operator()(const Math::Vector<size, std::size_t>& data) const {
return *reinterpret_cast<const std::size_t*>(Utility::MurmurHash2()(reinterpret_cast<const char*>(&data), sizeof(data)).byteArray());
}
};
}
/** /**
@brief Remove duplicate data from given array in-place @brief Remove duplicate data from given array in-place
@ -175,13 +166,20 @@ bit-exact matching is sufficient use @ref removeDuplicatesInPlace(const Containe
instead. instead.
If you want to remove duplicate data from an already indexed array, use If you want to remove duplicate data from an already indexed array, use
@ref removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<IndexType>&, const Containers::StridedArrayView1D<Vector>&, typename Vector::Type) instead. @ref removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView2D<Float>&, Float)
and friends instead.
If you want to remove duplicates in multiple incidental arrays, first remove If you want to remove duplicates in multiple incidental arrays, first remove
duplicates in each array separately and then combine the resulting index arrays duplicates in each array separately and then combine the resulting index arrays
back into a single one using @ref combineIndexedAttributes(). back into a single one using @ref combineIndexedAttributes().
*/ */
template<class Vector> std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView1D<Vector>& data, typename Vector::Type epsilon = Math::TypeTraits<typename Vector::Type>::epsilon()); MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView2D<Float>& data, Float epsilon = Math::TypeTraits<Float>::epsilon());
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView2D<Double>& data, Double epsilon = Math::TypeTraits<Double>::epsilon());
/** /**
@brief Remove duplicate data from given array using fuzzy comparison in-place into given output index array @brief Remove duplicate data from given array using fuzzy comparison in-place into given output index array
@ -196,7 +194,13 @@ template<class Vector> std::pair<Containers::Array<UnsignedInt>, std::size_t> re
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.
*/ */
template<class Vector> std::size_t removeDuplicatesFuzzyInPlaceInto(const Containers::StridedArrayView1D<Vector>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices, typename Vector::Type epsilon = Math::TypeTraits<typename Vector::Type>::epsilon()); MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyInPlaceInto(const Containers::StridedArrayView2D<Float>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices, Float epsilon = Math::TypeTraits<Float>::epsilon());
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyInPlaceInto(const Containers::StridedArrayView2D<Double>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices, Double epsilon = Math::TypeTraits<Double>::epsilon());
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
/** /**
@ -231,74 +235,41 @@ template<class Vector> CORRADE_DEPRECATED("use removeDuplicatesInPlace() instead
@return Size of unique prefix in the cleaned up @p data array @return Size of unique prefix in the cleaned up @p data array
@m_since_latest @m_since_latest
Compared to @ref removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView1D<Vector>&, typename Vector::Type) Compared to @ref removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView2D<Float>&, Float)
this variant is more suited for data that is already indexed as it works on this variant is more suited for data that is already indexed as it works on
the existing index array instead of allocating a new one. the existing index array instead of allocating a new one.
*/ */
template<class IndexType, class Vector> std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<IndexType>& indices, const Containers::StridedArrayView1D<Vector>& data, typename Vector::Type epsilon = Math::TypeTraits<typename Vector::Type>::epsilon()) { MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const Containers::StridedArrayView2D<Float>& data, Float epsilon = Math::TypeTraits<Float>::epsilon());
/* Somehow ~IndexType{} doesn't work for < 4byte types, as the result is
int(-1) instead of the type I want */ /**
CORRADE_ASSERT(data.size() <= IndexType(-1), "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): a" << sizeof(IndexType) << Debug::nospace << "-byte index type is too small for" << data.size() << "vertices", {}); * @overload
* @m_since_latest
/* Get bounds. When NaNs appear, those will get collapsed together when */
you're lucky, or cause the whole data to disappear when you're not -- it MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, const Containers::StridedArrayView2D<Float>& data, Float epsilon = Math::TypeTraits<Float>::epsilon());
needs a much more specialized handling to be robust. */
std::pair<Vector, Vector> minmax = Math::minmax(data); /**
* @overload
/* Make epsilon so large that std::size_t can index all vectors inside the * @m_since_latest
bounds. */ */
epsilon = Math::max(epsilon, typename Vector::Type((minmax.second-minmax.first).max()/static_cast<typename Vector::Type>(~std::size_t{}))); MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, const Containers::StridedArrayView2D<Float>& data, Float epsilon = Math::TypeTraits<Float>::epsilon());
/* Table containing original vector index for each discretized vector. /**
Reserving more buckets than necessary (i.e. as if each vector was * @overload
unique). */ * @m_since_latest
std::size_t dataSize = data.size(); */
std::unordered_map<Math::Vector<Vector::Size, std::size_t>, UnsignedInt, Implementation::VectorHash<Vector::Size>> table(dataSize); MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedInt>& indices, const Containers::StridedArrayView2D<Double>& data, Double epsilon = Math::TypeTraits<Double>::epsilon());
/* Index array that'll be filled in each pass and then used for remapping /**
the `indices` */ * @overload
Containers::Array<UnsignedInt> remapping{Containers::NoInit, dataSize}; * @m_since_latest
*/
/* First go with original coordinates, then move them by epsilon/2 in each MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedShort>& indices, const Containers::StridedArrayView2D<Double>& data, Double epsilon = Math::TypeTraits<Double>::epsilon());
direction. */
Vector moved; /**
for(std::size_t moving = 0; moving <= Vector::Size; ++moving) { * @overload
/* Go through all vectors */ * @m_since_latest
for(std::size_t i = 0; i != dataSize; ++i) { */
/* Try to insert new vertex into the table */ MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedByte>& indices, const Containers::StridedArrayView2D<Double>& data, Double epsilon = Math::TypeTraits<Double>::epsilon());
const Math::Vector<Vector::Size, std::size_t> v{(data[i] + moved - minmax.first)/epsilon};
const auto result = table.emplace(v, table.size());
/* Add the (either new or already existing) index into the array */
remapping[i] = result.first->second;
/* If this is a new combination, copy the data to new (earlier)
position in the array. Data in [table.size()-1, i) are already
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];
}
/* Remap the resulting index array */
for(auto& i: indices) i = remapping[i];
/* Move vertex coordinates by epsilon/2 in the next direction. Do that
only if we're not in the last iteration, as that would be an OOB
access otherwise. */
if(moving != Vector::Size) {
moved = Vector();
moved[moving] = epsilon/2;
}
/* Reduce to an unique prefix; clear the table for the next pass */
dataSize = table.size();
table.clear();
}
CORRADE_INTERNAL_ASSERT(data.size() >= dataSize);
return dataSize;
}
/** /**
@brief Remove duplicates from indexed data using fuzzy comparison in-place on a type-erased index array @brief Remove duplicates from indexed data using fuzzy comparison in-place on a type-erased index array
@ -306,20 +277,16 @@ template<class IndexType, class Vector> std::size_t removeDuplicatesFuzzyIndexed
Expects that the second dimension of @p indices is contiguous and represents Expects that the second dimension of @p indices is contiguous and represents
the actual 1/2/4-byte index type. Based on its size then calls the actual 1/2/4-byte index type. Based on its size then calls
@ref removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<IndexType>&, const Containers::StridedArrayView1D<Vector>&, typename Vector::Type) @ref removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView1D<UnsignedInt>&, const Containers::StridedArrayView2D<Float>&, Float)
with a concrete index type. or the other overloads.
*/ */
template<class Vector> std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView2D<char>& indices, const Containers::StridedArrayView1D<Vector>& data, typename Vector::Type epsilon = Math::TypeTraits<typename Vector::Type>::epsilon()) { MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView2D<char>& indices, const Containers::StridedArrayView2D<Float>& data, Float epsilon = Math::TypeTraits<Float>::epsilon());
CORRADE_ASSERT(indices.isContiguous<1>(), "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): second index view dimension is not contiguous", {});
if(indices.size()[1] == 4) /**
return removeDuplicatesFuzzyIndexedInPlace(Containers::arrayCast<1, UnsignedInt>(indices), data, epsilon); * @overload
else if(indices.size()[1] == 2) * @m_since_latest
return removeDuplicatesFuzzyIndexedInPlace(Containers::arrayCast<1, UnsignedShort>(indices), data, epsilon); */
else { MAGNUM_MESHTOOLS_EXPORT std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayView2D<char>& indices, const Containers::StridedArrayView2D<Double>& data, Double epsilon = Math::TypeTraits<Double>::epsilon());
CORRADE_ASSERT(indices.size()[1] == 1, "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): expected index type size 1, 2 or 4 but got" << indices.size()[1], {});
return removeDuplicatesFuzzyIndexedInPlace(Containers::arrayCast<1, UnsignedByte>(indices), data, epsilon);
}
}
/** /**
@brief Remove mesh data duplicates @brief Remove mesh data duplicates
@ -351,27 +318,13 @@ data.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData removeDuplicates(Trade::MeshData&& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData removeDuplicates(Trade::MeshData&& data);
template<class Vector> std::size_t removeDuplicatesFuzzyInPlaceInto(const Containers::StridedArrayView1D<Vector>& data, const Containers::StridedArrayView1D<UnsignedInt>& indices, typename Vector::Type epsilon) {
CORRADE_ASSERT(indices.size() == data.size(),
"MeshTools::removeDuplicatesFuzzyInPlaceInto(): output index array has" << indices.size() << "elements but expected" << data.size(), {});
/* A trivial index array that'll be remapped */
std::iota(indices.begin(), indices.end(), 0);
const std::size_t size = removeDuplicatesFuzzyIndexedInPlace(Containers::stridedArrayView(indices), data, epsilon);
return size;
}
template<class Vector> std::pair<Containers::Array<UnsignedInt>, std::size_t> removeDuplicatesFuzzyInPlace(const Containers::StridedArrayView1D<Vector>& data, typename Vector::Type epsilon) {
Containers::Array<UnsignedInt> indices{Containers::NoInit, data.size()};
const std::size_t size = removeDuplicatesFuzzyInPlaceInto(data, indices, epsilon);
return {std::move(indices), size};
}
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
template<class Vector> std::vector<UnsignedInt> removeDuplicates(std::vector<Vector>& data, typename Vector::Type epsilon) { template<class Vector> std::vector<UnsignedInt> removeDuplicates(std::vector<Vector>& data, typename Vector::Type epsilon) {
/* A trivial index array that'll be remapped and returned after */ /* A trivial index array that'll be remapped and returned after */
std::vector<UnsignedInt> indices(data.size()); std::vector<UnsignedInt> indices(data.size());
const std::size_t size = removeDuplicatesFuzzyInPlaceInto(Containers::stridedArrayView(data), Containers::stridedArrayView(indices), epsilon); const std::size_t size = removeDuplicatesFuzzyInPlaceInto(
Containers::arrayCast<2, typename Vector::Type>(Containers::stridedArrayView(data)),
Containers::stridedArrayView(indices), epsilon);
data.resize(size); data.resize(size);
return indices; return indices;
} }

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

@ -27,6 +27,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h> #include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h> #include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/FormatStl.h>
#include "Magnum/Math/Vector2.h" #include "Magnum/Math/Vector2.h"
#include "Magnum/MeshTools/RemoveDuplicates.h" #include "Magnum/MeshTools/RemoveDuplicates.h"
@ -48,17 +49,18 @@ struct RemoveDuplicatesTest: TestSuite::Tester {
void removeDuplicatesIndexedInPlaceErasedNonContiguous(); void removeDuplicatesIndexedInPlaceErasedNonContiguous();
void removeDuplicatesIndexedInPlaceErasedWrongIndexSize(); void removeDuplicatesIndexedInPlaceErasedWrongIndexSize();
void removeDuplicatesFuzzyInPlaceOneDimension(); template<class T> void removeDuplicatesFuzzyInPlaceOneDimension();
void removeDuplicatesFuzzyInPlaceMoreDimensions(); template<class T> void removeDuplicatesFuzzyInPlaceMoreDimensions();
template<class T> void removeDuplicatesFuzzyInPlaceInto();
void removeDuplicatesFuzzyInPlaceIntoWrongOutputSize(); void removeDuplicatesFuzzyInPlaceIntoWrongOutputSize();
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
void removeDuplicatesFuzzyStl(); void removeDuplicatesFuzzyStl();
#endif #endif
template<class T> void removeDuplicatesFuzzyIndexedInPlace(); template<class IndexType, class T> void removeDuplicatesFuzzyIndexedInPlace();
void removeDuplicatesFuzzyIndexedInPlaceSmallType(); void removeDuplicatesFuzzyIndexedInPlaceSmallType();
void removeDuplicatesFuzzyIndexedInPlaceEmptyIndices(); void removeDuplicatesFuzzyIndexedInPlaceEmptyIndices();
void removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices(); void removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices();
template<class T> void removeDuplicatesFuzzyIndexedInPlaceErased(); template<class IndexType, class T> void removeDuplicatesFuzzyIndexedInPlaceErased();
void removeDuplicatesFuzzyIndexedInPlaceErasedNonContiguous(); void removeDuplicatesFuzzyIndexedInPlaceErasedNonContiguous();
void removeDuplicatesFuzzyIndexedInPlaceErasedWrongIndexSize(); void removeDuplicatesFuzzyIndexedInPlaceErasedWrongIndexSize();
@ -92,21 +94,31 @@ RemoveDuplicatesTest::RemoveDuplicatesTest() {
&RemoveDuplicatesTest::removeDuplicatesIndexedInPlaceErasedNonContiguous, &RemoveDuplicatesTest::removeDuplicatesIndexedInPlaceErasedNonContiguous,
&RemoveDuplicatesTest::removeDuplicatesIndexedInPlaceErasedWrongIndexSize, &RemoveDuplicatesTest::removeDuplicatesIndexedInPlaceErasedWrongIndexSize,
&RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceOneDimension, &RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceOneDimension<Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceMoreDimensions, &RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceOneDimension<Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceMoreDimensions<Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceMoreDimensions<Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceInto<Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceInto<Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceIntoWrongOutputSize, &RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceIntoWrongOutputSize,
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
&RemoveDuplicatesTest::removeDuplicatesFuzzyStl, &RemoveDuplicatesTest::removeDuplicatesFuzzyStl,
#endif #endif
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedByte>, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedByte, Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedShort>, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedByte, Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedInt>, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedShort, Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedShort, Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedInt, Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace<UnsignedInt, Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceSmallType, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceSmallType,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndices, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndices,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedByte>, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedByte, Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedShort>, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedByte, Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedInt>, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedShort, Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedShort, Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedInt, Float>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased<UnsignedInt, Double>,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErasedNonContiguous, &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErasedNonContiguous,
&RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErasedWrongIndexSize}); &RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErasedWrongIndexSize});
@ -271,43 +283,76 @@ void RemoveDuplicatesTest::removeDuplicatesIndexedInPlaceErasedWrongIndexSize()
"MeshTools::removeDuplicatesIndexedInPlace(): expected index type size 1, 2 or 4 but got 3\n"); "MeshTools::removeDuplicatesIndexedInPlace(): expected index type size 1, 2 or 4 but got 3\n");
} }
void RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceOneDimension() { template<class T> void RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceOneDimension() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
/* Numbers with distance <=1 should be merged. In the first iteration item /* Numbers with distance <=1 should be merged. In the first iteration item
2 gets collapsed into item 1, in the second iteration item 3 gets 2 gets collapsed into item 1, in the second iteration item 3 gets
collapsed into item 1, reducing to 2 items in total. */ collapsed into item 1, reducing to 2 items in total. */
Math::Vector<1, Float> data[]{ T data[]{
1.0f, /* bucket 0 in 1st iteration, bucket 1 in 2nd */ T(1.0), /* bucket 0 in 1st iteration, bucket 1 in 2nd */
2.9f, /* bucket 2 in 1st iteration, bucket 3 in 2nd */ T(2.9), /* bucket 2 in 1st iteration, bucket 3 in 2nd */
0.0f, /* bucket 0 in 1st iteration, bucket 0 in 2nd */ T(0.0), /* bucket 0 in 1st iteration, bucket 0 in 2nd */
3.4f /* bucket 3 in 1st iteration, bucket 3 in 2nd */ T(3.4) /* bucket 3 in 1st iteration, bucket 3 in 2nd */
}; };
std::pair<Containers::Array<UnsignedInt>, std::size_t> result = MeshTools::removeDuplicatesFuzzyInPlace(Containers::stridedArrayView(data), 1.00001f); std::pair<Containers::Array<UnsignedInt>, std::size_t> result =
MeshTools::removeDuplicatesFuzzyInPlace(
Containers::arrayCast<2, T>(Containers::stridedArrayView(data)),
T(1.00001));
CORRADE_COMPARE_AS(Containers::arrayView(result.first), CORRADE_COMPARE_AS(Containers::arrayView(result.first),
Containers::arrayView<UnsignedInt>({0, 1, 0, 1}), Containers::arrayView<UnsignedInt>({0, 1, 0, 1}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(result.second), CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(result.second),
(Containers::arrayView<Math::Vector<1, Float>>({1.0f, 2.9f})), (Containers::arrayView<T>({T(1.0), T(2.9)})),
TestSuite::Compare::Container); TestSuite::Compare::Container);
} }
void RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceMoreDimensions() { template<class T> void RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceMoreDimensions() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
/* Numbers with distance 1 should be merged, numbers with distance 2 should /* Numbers with distance 1 should be merged, numbers with distance 2 should
be kept. Testing both even-odd and odd-even sequence to verify that be kept. Testing both even-odd and odd-even sequence to verify that
half-epsilon translations are applied properly. */ half-epsilon translations are applied properly. */
Vector2 data[]{ Math::Vector2<T> data[]{
{1.0f, 0.0f}, {T(1.0), T(0.0)},
{2.0f, 1.0f}, {T(2.0), T(1.0)},
{0.0f, 4.0f}, {T(0.0), T(4.0)},
{1.0f, 5.0f} {T(1.0), T(5.0)}
}; };
std::pair<Containers::Array<UnsignedInt>, std::size_t> result = MeshTools::removeDuplicatesFuzzyInPlace(Containers::stridedArrayView(data), 2.0f); std::pair<Containers::Array<UnsignedInt>, std::size_t> result =
MeshTools::removeDuplicatesFuzzyInPlace(
Containers::arrayCast<2, T>(Containers::stridedArrayView(data)),
T(2.0));
CORRADE_COMPARE_AS(Containers::arrayView(result.first), CORRADE_COMPARE_AS(Containers::arrayView(result.first),
Containers::arrayView<UnsignedInt>({0, 0, 1, 1}), Containers::arrayView<UnsignedInt>({0, 0, 1, 1}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(result.second), CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(result.second),
Containers::arrayView<Vector2>({{1.0f, 0.0f}, {0.0f, 4.0f}}), Containers::arrayView<Math::Vector2<T>>({{T(1.0), T(0.0)}, {T(0.0), T(4.0)}}),
TestSuite::Compare::Container);
}
template<class T> void RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceInto() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
/* Same as above, but using the Into variant */
Math::Vector2<T> data[]{
{T(1.0), T(0.0)},
{T(2.0), T(1.0)},
{T(0.0), T(4.0)},
{T(1.0), T(5.0)}
};
Containers::Array<UnsignedInt> indices{Containers::NoInit, Containers::arraySize(data)};
std::size_t result = MeshTools::removeDuplicatesFuzzyInPlaceInto(
Containers::arrayCast<2, T>(Containers::stridedArrayView(data)),
indices, T(2.0));
CORRADE_COMPARE_AS(indices,
Containers::arrayView<UnsignedInt>({0, 0, 1, 1}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(result),
Containers::arrayView<Math::Vector2<T>>({{T(1.0), T(0.0)}, {T(0.0), T(4.0)}}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
} }
@ -321,7 +366,9 @@ void RemoveDuplicatesTest::removeDuplicatesFuzzyInPlaceIntoWrongOutputSize() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
MeshTools::removeDuplicatesFuzzyInPlaceInto(Containers::stridedArrayView(data), output); MeshTools::removeDuplicatesFuzzyInPlaceInto(
Containers::arrayCast<2, Float>(Containers::stridedArrayView(data)),
output);
CORRADE_COMPARE(out.str(), CORRADE_COMPARE(out.str(),
"MeshTools::removeDuplicatesFuzzyInPlaceInto(): output index array has 7 elements but expected 8\n"); "MeshTools::removeDuplicatesFuzzyInPlaceInto(): output index array has 7 elements but expected 8\n");
} }
@ -348,26 +395,28 @@ void RemoveDuplicatesTest::removeDuplicatesFuzzyStl() {
} }
#endif #endif
template<class T> void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace() { template<class IndexType, class T> void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlace() {
setTestCaseTemplateName(Math::TypeTraits<T>::name()); setTestCaseTemplateName(Utility::formatString("{}, {}",
Math::TypeTraits<IndexType>::name(),
Math::TypeTraits<T>::name()));
/* Same as above, but with an explicit index buffer */ /* Same as above, but with an explicit index buffer */
T indices[]{3, 2, 0, 1, 2, 3}; IndexType indices[]{3, 2, 0, 1, 2, 3};
Vector2 data[]{ Math::Vector2<T> data[]{
{1.0f, 0.0f}, {T(1.0), T(0.0)},
{2.0f, 1.0f}, {T(2.0), T(1.0)},
{0.0f, 4.0f}, {T(0.0), T(4.0)},
{1.0f, 5.0f} {T(1.0), T(5.0)}
}; };
std::size_t count = MeshTools::removeDuplicatesFuzzyIndexedInPlace( std::size_t count = MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::stridedArrayView(indices), Containers::stridedArrayView(indices),
Containers::stridedArrayView(data), 2); Containers::arrayCast<2, T>(Containers::stridedArrayView(data)), 2);
CORRADE_COMPARE_AS(Containers::arrayView(indices), CORRADE_COMPARE_AS(Containers::arrayView(indices),
Containers::arrayView<T>({1, 1, 0, 0, 1, 1}), Containers::arrayView<IndexType>({1, 1, 0, 0, 1, 1}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(count), CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(count),
Containers::arrayView<Vector2>({{1.0f, 0.0f}, {0.0f, 4.0f}}), Containers::arrayView<Math::Vector2<T>>({{T(1.0), T(0.0)}, {T(0.0), T(4.0)}}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
} }
@ -383,7 +432,7 @@ void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceSmallType() {
Vector2 data[256]{}; Vector2 data[256]{};
MeshTools::removeDuplicatesFuzzyIndexedInPlace( MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::stridedArrayView(indices), Containers::stridedArrayView(indices),
Containers::stridedArrayView(data)); Containers::arrayCast<2, Float>(Containers::stridedArrayView(data)));
CORRADE_COMPARE(out.str(), "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): a 1-byte index type is too small for 256 vertices\n"); CORRADE_COMPARE(out.str(), "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): a 1-byte index type is too small for 256 vertices\n");
} }
@ -397,36 +446,40 @@ void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndices() {
std::size_t count = MeshTools::removeDuplicatesFuzzyIndexedInPlace( std::size_t count = MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::StridedArrayView1D<UnsignedInt>{}, Containers::StridedArrayView1D<UnsignedInt>{},
Containers::stridedArrayView(data), 2.0f); Containers::arrayCast<2, Float>(Containers::stridedArrayView(data)), 2.0f);
CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(count), CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(count),
Containers::arrayView<Vector2>({{1.0f, 0.0f}, {0.0f, 4.0f}}), Containers::arrayView<Vector2>({{1.0f, 0.0f}, {0.0f, 4.0f}}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
} }
void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices() { void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceEmptyIndicesVertices() {
CORRADE_COMPARE((MeshTools::removeDuplicatesFuzzyIndexedInPlace<UnsignedInt, Vector2>({}, {})), 0); CORRADE_COMPARE((MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::StridedArrayView1D<UnsignedInt>{},
Containers::StridedArrayView2D<Float>{})), 0);
} }
template<class T> void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased() { template<class IndexType, class T> void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErased() {
setTestCaseTemplateName(Math::TypeTraits<T>::name()); setTestCaseTemplateName(Utility::formatString("{}, {}",
Math::TypeTraits<IndexType>::name(),
Math::TypeTraits<T>::name()));
/* Same as above, but with an explicit index buffer */ /* Same as above, but with an explicit index buffer */
T indices[]{3, 2, 0, 1, 2, 3}; IndexType indices[]{3, 2, 0, 1, 2, 3};
Vector2 data[]{ Math::Vector2<T> data[]{
{1.0f, 0.0f}, {T(1.0), T(0.0)},
{2.0f, 1.0f}, {T(2.0), T(1.0)},
{0.0f, 4.0f}, {T(0.0), T(4.0)},
{1.0f, 5.0f} {T(1.0), T(5.0)}
}; };
std::size_t count = MeshTools::removeDuplicatesFuzzyIndexedInPlace( std::size_t count = MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::arrayCast<2, char>(Containers::arrayView(indices)), Containers::arrayCast<2, char>(Containers::arrayView(indices)),
Containers::stridedArrayView(data), 2); Containers::arrayCast<2, T>(Containers::stridedArrayView(data)), 2);
CORRADE_COMPARE_AS(Containers::arrayView(indices), CORRADE_COMPARE_AS(Containers::arrayView(indices),
Containers::arrayView<T>({1, 1, 0, 0, 1, 1}), Containers::arrayView<IndexType>({1, 1, 0, 0, 1, 1}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(count), CORRADE_COMPARE_AS(Containers::arrayView(data).prefix(count),
Containers::arrayView<Vector2>({{1.0f, 0.0f}, {0.0f, 4.0f}}), Containers::arrayView<Math::Vector2<T>>({{T(1.0), T(0.0)}, {T(0.0), T(4.0)}}),
TestSuite::Compare::Container); TestSuite::Compare::Container);
} }
@ -442,7 +495,7 @@ void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErasedNonContiguou
Error redirectError{&out}; Error redirectError{&out};
MeshTools::removeDuplicatesFuzzyIndexedInPlace( MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::StridedArrayView2D<char>{indices, {6, 2}, {4, 2}}, Containers::StridedArrayView2D<char>{indices, {6, 2}, {4, 2}},
Containers::stridedArrayView(data)); Containers::arrayCast<2, Float>(Containers::stridedArrayView(data)));
CORRADE_COMPARE(out.str(), CORRADE_COMPARE(out.str(),
"MeshTools::removeDuplicatesFuzzyIndexedInPlace(): second index view dimension is not contiguous\n"); "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): second index view dimension is not contiguous\n");
} }
@ -459,7 +512,7 @@ void RemoveDuplicatesTest::removeDuplicatesFuzzyIndexedInPlaceErasedWrongIndexSi
Error redirectError{&out}; Error redirectError{&out};
MeshTools::removeDuplicatesFuzzyIndexedInPlace( MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::StridedArrayView2D<char>{indices, {6, 3}}, Containers::StridedArrayView2D<char>{indices, {6, 3}},
Containers::stridedArrayView(data)); Containers::arrayCast<2, Float>(Containers::stridedArrayView(data)));
CORRADE_COMPARE(out.str(), CORRADE_COMPARE(out.str(),
"MeshTools::removeDuplicatesFuzzyIndexedInPlace(): expected index type size 1, 2 or 4 but got 3\n"); "MeshTools::removeDuplicatesFuzzyIndexedInPlace(): expected index type size 1, 2 or 4 but got 3\n");
} }

6
src/Magnum/MeshTools/Test/SubdivideRemoveDuplicatesBenchmark.cpp

@ -94,7 +94,8 @@ void SubdivideRemoveDuplicatesBenchmark::subdivideAndRemoveDuplicatesAfter() {
/* Remove duplicates after */ /* Remove duplicates after */
arrayResize(positions, MeshTools::removeDuplicatesFuzzyIndexedInPlace( arrayResize(positions, MeshTools::removeDuplicatesFuzzyIndexedInPlace(
stridedArrayView(indices), stridedArrayView(positions))); stridedArrayView(indices),
Containers::arrayCast<2, Float>(stridedArrayView(positions))));
} }
} }
@ -121,7 +122,8 @@ void SubdivideRemoveDuplicatesBenchmark::subdivideAndRemoveDuplicatesInBetween()
for(std::size_t i = 0; i != 5; ++i) { for(std::size_t i = 0; i != 5; ++i) {
MeshTools::subdivide(indices, positions, interpolator); MeshTools::subdivide(indices, positions, interpolator);
arrayResize(positions, MeshTools::removeDuplicatesFuzzyIndexedInPlace( arrayResize(positions, MeshTools::removeDuplicatesFuzzyIndexedInPlace(
stridedArrayView(indices), stridedArrayView(positions))); stridedArrayView(indices),
Containers::arrayCast<2, Float>(stridedArrayView(positions))));
} }
} }
} }

1
src/Magnum/MeshTools/Test/SubdivideTest.cpp

@ -28,6 +28,7 @@
#include <Corrade/TestSuite/Compare/Container.h> #include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h> #include <Corrade/Utility/DebugStl.h>
#include "Magnum/Math/Vector.h"
#include "Magnum/MeshTools/RemoveDuplicates.h" #include "Magnum/MeshTools/RemoveDuplicates.h"
#include "Magnum/MeshTools/Subdivide.h" #include "Magnum/MeshTools/Subdivide.h"

1
src/Magnum/Primitives/CMakeLists.txt

@ -80,6 +80,7 @@ elseif(BUILD_STATIC_PIC)
endif() endif()
target_link_libraries(MagnumPrimitives PUBLIC target_link_libraries(MagnumPrimitives PUBLIC
Magnum Magnum
MagnumMeshTools
MagnumTrade) MagnumTrade)
install(TARGETS MagnumPrimitives install(TARGETS MagnumPrimitives

4
src/Magnum/Primitives/Icosphere.cpp

@ -116,7 +116,9 @@ Trade::MeshData icosphereSolid(const UnsignedInt subdivisions) {
/** @todo i need arrayShrinkAndGiveUpMemoryIfItDoesntCauseRealloc() */ /** @todo i need arrayShrinkAndGiveUpMemoryIfItDoesntCauseRealloc() */
Containers::arrayResize<Trade::ArrayAllocator>(vertexData, Containers::arrayResize<Trade::ArrayAllocator>(vertexData,
MeshTools::removeDuplicatesFuzzyIndexedInPlace(Containers::stridedArrayView(indices), Containers::stridedArrayView(positions))*sizeof(Vertex)); MeshTools::removeDuplicatesFuzzyIndexedInPlace(
Containers::stridedArrayView(indices),
Containers::arrayCast<2, Float>(positions))*sizeof(Vertex));
} }
/* Build up the views again with correct size, fill the normals */ /* Build up the views again with correct size, fill the normals */

Loading…
Cancel
Save