Browse Source

MeshTools: subdivideInPlace() accepts strided indices of any type.

Also vastly improved tests and docs.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
a1924f55c4
  1. 55
      src/Magnum/MeshTools/Subdivide.h
  2. 77
      src/Magnum/MeshTools/Test/SubdivideTest.cpp

55
src/Magnum/MeshTools/Subdivide.h

@ -31,12 +31,15 @@
#include <vector>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Assert.h>
#include "Magnum/Magnum.h"
namespace Magnum { namespace MeshTools {
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class Vertex, class Interpolator> void subdivideInPlace(Containers::ArrayView<UnsignedInt> indices, Containers::ArrayView<Vertex> vertices, Interpolator interpolator);
template<class IndexType, class Vertex, class Interpolator> void subdivideInPlace(const Containers::StridedArrayView1D<IndexType>& indices, const Containers::StridedArrayView1D<Vertex>& vertices, Interpolator interpolator);
#endif
/**
@ -50,14 +53,14 @@ template<class Vertex, class Interpolator> void subdivideInPlace(Containers::Arr
Goes through all triangle faces and subdivides them into four new. Removing
duplicate vertices in the mesh is up to the user.
@see @ref subdivideInPlace()
@see @ref subdivideInPlace(), @ref removeDuplicatesInPlace()
*/
template<class Vertex, class Interpolator> void subdivide(std::vector<UnsignedInt>& indices, std::vector<Vertex>& vertices, Interpolator interpolator) {
CORRADE_ASSERT(!(indices.size()%3), "MeshTools::subdivide(): index count is not divisible by 3!", );
CORRADE_ASSERT(!(indices.size()%3), "MeshTools::subdivide(): index count is not divisible by 3", );
vertices.resize(vertices.size() + indices.size());
indices.resize(indices.size()*4);
subdivideInPlace(Containers::arrayView(indices), Containers::arrayView(vertices), interpolator);
subdivideInPlace(Containers::stridedArrayView(indices), Containers::stridedArrayView(vertices), interpolator);
}
/**
@ -68,16 +71,32 @@ template<class Vertex, class Interpolator> void subdivide(std::vector<UnsignedIn
@param[in,out] vertices Vertex array to operate on
@param interpolator Functor or function pointer which interpolates
two adjacent vertices: @cpp Vertex interpolator(Vertex a, Vertex b) @ce
Assumes the original triangle mesh indices are in the first quarter of the
@p indices array and original vertices in a prefix of the @p vertices array,
with @cpp indices.size() @ce left at the end of it for newly added vertices.
Then goes through all those triangle faces and subdivides them into four new,
filling up the remaining space in both. Removing duplicate vertices in the mesh
is up to the user.
@m_since_latest
Assuming the original mesh has @f$ i @f$ indices and @f$ v @f$ vertices,
expects the @p indices array to have a size of @f$ 4i @f$ (as every triangle
face would be divided into four new), with the original indices being in the
first quarter, and the @p vertices array to have a size of @f$ v + i @f$ (as
every original triangle face will get three new vertices). Removing duplicate
vertices in the mesh is up to the user.
Generally, for @f$ k @f$ subsequent subdivisions, the resulting index and
vertex array sizes @f$ i' @f$ and @f$ v' @f$ will be as following. To subdivide
the mesh multiple times in-place, pass correctly sized prefix of the arrays to
each step. @f[
\begin{array}{rcl}
i' & = & 4^k i \\
v' & = & v + \frac{1}{3}(i' - i)
\end{array}
@f]
@see @ref subdivide(), @ref removeDuplicatesInPlace()
*/
template<class Vertex, class Interpolator> void subdivideInPlace(Containers::ArrayView<UnsignedInt> indices, Containers::ArrayView<Vertex> vertices, Interpolator interpolator) {
CORRADE_ASSERT(!(indices.size()%12), "MeshTools::subdivideInto(): can't divide" << indices.size() << "indices to four parts with each having triangle faces", );
template<class IndexType, class Vertex, class Interpolator> void subdivideInPlace(const Containers::StridedArrayView1D<IndexType>& indices, const Containers::StridedArrayView1D<Vertex>& vertices, Interpolator interpolator) {
CORRADE_ASSERT(!(indices.size()%12), "MeshTools::subdivideInPlace(): can't divide" << indices.size() << "indices to four parts with each having triangle faces", );
/* Somehow ~IndexType{} doesn't work for < 4byte types, as the result is
int(-1) instead of the type I want */
CORRADE_ASSERT(vertices.size() <= IndexType(-1), "MeshTools::subdivideInPlace(): a" << sizeof(IndexType) << Debug::nospace << "-byte index type is too small for" << vertices.size() << "vertices", );
/* Subdivide each face to four new */
const std::size_t indexCount = indices.size()/4;
@ -85,7 +104,7 @@ template<class Vertex, class Interpolator> void subdivideInPlace(Containers::Arr
std::size_t vertexOffset = vertices.size() - indexCount;
for(std::size_t i = 0; i != indexCount; i += 3) {
/* Interpolate each side */
UnsignedInt newVertices[3];
IndexType newVertices[3];
for(int j = 0; j != 3; ++j) {
newVertices[j] = vertexOffset;
vertices[vertexOffset++] = interpolator(vertices[indices[i+j]], vertices[indices[i+(j+1)%3]]);
@ -120,6 +139,14 @@ template<class Vertex, class Interpolator> void subdivideInPlace(Containers::Arr
}
}
/**
* @overload
* @m_since_latest
*/
template<class IndexType, class Vertex, class Interpolator> void subdivideInPlace(const Containers::ArrayView<IndexType>& indices, const Containers::StridedArrayView1D<Vertex>& vertices, Interpolator interpolator) {
subdivideInPlace(Containers::stridedArrayView(indices), vertices, interpolator);
}
}}
#endif

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

@ -25,6 +25,7 @@
#include <sstream>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/MeshTools/RemoveDuplicates.h"
@ -35,8 +36,11 @@ namespace Magnum { namespace MeshTools { namespace Test { namespace {
struct SubdivideTest: TestSuite::Tester {
explicit SubdivideTest();
void wrongIndexCount();
void subdivide();
void subdivideWrongIndexCount();
template<class T> void subdivideInPlace();
void subdivideInPlaceWrongIndexCount();
void subdivideInPlaceSmallIndexType();
};
typedef Math::Vector<1, Int> Vector1;
@ -44,29 +48,74 @@ typedef Math::Vector<1, Int> Vector1;
inline Vector1 interpolator(Vector1 a, Vector1 b) { return (a[0]+b[0])/2; }
SubdivideTest::SubdivideTest() {
addTests({&SubdivideTest::wrongIndexCount,
&SubdivideTest::subdivide});
addTests({&SubdivideTest::subdivide,
&SubdivideTest::subdivideWrongIndexCount,
&SubdivideTest::subdivideInPlace<UnsignedByte>,
&SubdivideTest::subdivideInPlace<UnsignedShort>,
&SubdivideTest::subdivideInPlace<UnsignedInt>,
&SubdivideTest::subdivideInPlaceWrongIndexCount,
&SubdivideTest::subdivideInPlaceSmallIndexType});
}
void SubdivideTest::wrongIndexCount() {
std::stringstream ss;
Error redirectError{&ss};
void SubdivideTest::subdivide() {
std::vector<Vector1> positions{0, 2, 6, 8};
std::vector<UnsignedInt> indices{0, 1, 2, 1, 2, 3};
MeshTools::subdivide(indices, positions, interpolator);
CORRADE_COMPARE_AS(indices,
(std::vector<UnsignedInt>{4, 5, 6, 7, 8, 9, 0, 4, 6, 4, 1, 5, 6, 5, 2, 1, 7, 9, 7, 2, 8, 9, 8, 3}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(positions,
(std::vector<Vector1>{0, 2, 6, 8, 1, 4, 3, 4, 7, 5}),
TestSuite::Compare::Container);
}
void SubdivideTest::subdivideWrongIndexCount() {
std::stringstream out;
Error redirectError{&out};
std::vector<Vector1> positions;
std::vector<UnsignedInt> indices{0, 1};
MeshTools::subdivide(indices, positions, interpolator);
CORRADE_COMPARE(ss.str(), "MeshTools::subdivide(): index count is not divisible by 3!\n");
CORRADE_COMPARE(out.str(), "MeshTools::subdivide(): index count is not divisible by 3\n");
}
void SubdivideTest::subdivide() {
std::vector<Vector1> positions{0, 2, 6, 8};
std::vector<UnsignedInt> indices{0, 1, 2, 1, 2, 3};
MeshTools::subdivide(indices, positions, interpolator);
template<class T> void SubdivideTest::subdivideInPlace() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
T indices[6*4]{0, 1, 2, 1, 2, 3, /* and 18 more */};
Vector1 positions[4 + 6]{0, 2, 6, 8, /* and 6 more */};
MeshTools::subdivideInPlace(Containers::stridedArrayView(indices),
Containers::stridedArrayView(positions), interpolator);
CORRADE_COMPARE_AS(Containers::arrayView(indices),
Containers::arrayView<T>({4, 5, 6, 7, 8, 9, 0, 4, 6, 4, 1, 5, 6, 5, 2, 1, 7, 9, 7, 2, 8, 9, 8, 3}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(Containers::arrayView(positions),
Containers::arrayView<Vector1>({0, 2, 6, 8, 1, 4, 3, 4, 7, 5}),
TestSuite::Compare::Container);
}
void SubdivideTest::subdivideInPlaceWrongIndexCount() {
std::stringstream out;
Error redirectError{&out};
UnsignedInt indices[6*4 + 1]{0, 1, 2, 1, 2, 3, /* and 18+1 more */};
Vector1 positions[]{0};
MeshTools::subdivideInPlace(Containers::stridedArrayView(indices),
Containers::stridedArrayView(positions), interpolator);
CORRADE_COMPARE(out.str(), "MeshTools::subdivideInPlace(): can't divide 25 indices to four parts with each having triangle faces\n");
}
CORRADE_COMPARE(indices.size(), 24);
void SubdivideTest::subdivideInPlaceSmallIndexType() {
std::stringstream out;
Error redirectError{&out};
CORRADE_VERIFY(positions == (std::vector<Vector1>{0, 2, 6, 8, 1, 4, 3, 4, 7, 5}));
CORRADE_COMPARE(indices, (std::vector<UnsignedInt>{4, 5, 6, 7, 8, 9, 0, 4, 6, 4, 1, 5, 6, 5, 2, 1, 7, 9, 7, 2, 8, 9, 8, 3}));
UnsignedByte indices[6*4]{0, 1, 2, 1, 2, 3, /* and 18 more */};
Vector1 positions[256]{};
MeshTools::subdivideInPlace(Containers::stridedArrayView(indices),
Containers::stridedArrayView(positions), interpolator);
CORRADE_COMPARE(out.str(), "MeshTools::subdivideInPlace(): a 1-byte index type is too small for 256 vertices\n");
}
}}}}

Loading…
Cancel
Save