diff --git a/src/MeshTools/Test/CMakeLists.txt b/src/MeshTools/Test/CMakeLists.txt index e98374b66..369e6d411 100644 --- a/src/MeshTools/Test/CMakeLists.txt +++ b/src/MeshTools/Test/CMakeLists.txt @@ -7,6 +7,7 @@ corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp) corrade_add_test(MeshToolsSubdivideTest SubdivideTest.cpp) # corrade_add_test(MeshToolsSubdivideCleanBenchmark SubdivideCleanBenchmark.h SubdivideCleanBenchmark.cpp MagnumPrimitives) corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools) +corrade_add_test(MeshToolsTransformTest TransformTest.cpp LIBRARIES MagnumMeshTools) # Graceful assert for testing set_target_properties(MeshToolsCombineIndexedArraysTest MeshToolsInterleaveTest MeshToolsSubdivideTest PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) diff --git a/src/MeshTools/Test/TransformTest.cpp b/src/MeshTools/Test/TransformTest.cpp new file mode 100644 index 000000000..b6f3e16c4 --- /dev/null +++ b/src/MeshTools/Test/TransformTest.cpp @@ -0,0 +1,109 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include +#include + +#include "Math/Matrix3.h" +#include "Magnum.h" +#include "MeshTools/Transform.h" + +namespace Magnum { namespace MeshTools { namespace Test { + +class TransformTest: public Corrade::TestSuite::Tester { + public: + explicit TransformTest(); + + void transformVectors2D(); + void transformVectors3D(); + + void transformPoints2D(); + void transformPoints3D(); +}; + +TransformTest::TransformTest() { + addTests(&TransformTest::transformVectors2D, + &TransformTest::transformVectors3D, + + &TransformTest::transformPoints2D, + &TransformTest::transformPoints3D); +} + +constexpr static std::array points2D{{ + {-3.0f, 4.0f}, + { 2.5f, -15.0f} +}}; + +constexpr static std::array points2DRotated{{ + {-4.0f, -3.0f}, + {15.0f, 2.5f} +}}; + +constexpr static std::array points2DRotatedTranslated{{ + {-4.0f, -4.0f}, + {15.0f, 1.5f} +}}; + +constexpr static std::array points3D{{ + {-3.0f, 4.0f, 34.0f}, + { 2.5f, -15.0f, 1.5f} +}}; + +constexpr static std::array points3DRotated{{ + {-4.0f, -3.0f, 34.0f}, + {15.0f, 2.5f, 1.5f} +}}; + +constexpr static std::array points3DRotatedTranslated{{ + {-4.0f, -4.0f, 34.0f}, + {15.0f, 1.5f, 1.5f} +}}; + +void TransformTest::transformVectors2D() { + auto matrix = MeshTools::transformVectors(Matrix3::rotation(Deg(90.0f)), points2D); + auto complex = MeshTools::transformVectors(Complex::rotation(Deg(90.0f)), points2D); + + CORRADE_COMPARE(matrix, points2DRotated); + CORRADE_COMPARE(complex, points2DRotated); +} + +void TransformTest::transformVectors3D() { + auto matrix = MeshTools::transformVectors(Matrix4::rotationZ(Deg(90.0f)), points3D); + auto complex = MeshTools::transformVectors(Quaternion::rotation(Deg(90.0f), Vector3::zAxis()), points3D); + + CORRADE_COMPARE(matrix, points3DRotated); + CORRADE_COMPARE(complex, points3DRotated); +} + +void TransformTest::transformPoints2D() { + auto matrix = MeshTools::transformPoints( + Matrix3::translation(Vector2::yAxis(-1.0f))*Matrix3::rotation(Deg(90.0f)), points2D); + + CORRADE_COMPARE(matrix, points2DRotatedTranslated); +} + +void TransformTest::transformPoints3D() { + auto matrix = MeshTools::transformPoints( + Matrix4::translation(Vector3::yAxis(-1.0f))*Matrix4::rotationZ(Deg(90.0f)), points3D); + auto complex = MeshTools::transformPoints( + DualQuaternion::translation(Vector3::yAxis(-1.0f))*DualQuaternion::rotation(Deg(90.0f), Vector3::zAxis()), points3D); + + CORRADE_COMPARE(matrix, points3DRotatedTranslated); + CORRADE_COMPARE(complex, points3DRotatedTranslated); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::MeshTools::Test::TransformTest) diff --git a/src/MeshTools/Transform.h b/src/MeshTools/Transform.h index 9ad5b3ceb..9ce7b07ae 100644 --- a/src/MeshTools/Transform.h +++ b/src/MeshTools/Transform.h @@ -16,27 +16,116 @@ */ /** @file - * @brief Function Magnum::MeshTools::transform() + * @brief Function Magnum::MeshTools::transformVectorsInPlace(), Magnum::MeshTools::transformVectors(), Magnum::MeshTools::transformPointsInPlace(), Magnum::MeshTools::transformPoints() */ -#include "Math/Matrix.h" +#include "Math/Complex.h" +#include "Math/DualQuaternion.h" +#include "Math/Matrix3.h" namespace Magnum { namespace MeshTools { /** -@brief Transform vertices using given matrix +@brief Transform vectors in-place using given transformation -Usable for mesh transformations that would otherwise negatively affect -dependent objects, such as (uneven) scaling. Example usage: +Usable for one-time mesh transformations that would otherwise negatively affect +dependent objects, such as (uneven) scaling. Accepts any forward-iterable type +with compatible vector type as @p vectors. Expects that @ref Math::Quaternion "Quaternion" +or @ref Math::Complex "Complex" is normalized, no further requirements are for +transformation matrices. +Unlike in transformPointsInPlace(), the transformation does not involve +translation. + +Example usage: +@code +std::vector vectors; +auto transformation = Quaternion::rotation(35.0_degf, Vector3::yAxis()); +MeshTools::transformVectorsInPlace(rotation, vectors); +@endcode + +@see transformVectors(), Matrix3::transformVector(), Matrix4::transformVector(), + Complex::transformVectorNormalized(), Quaternion::transformVectorNormalized() +@todo GPU transform feedback implementation (otherwise this is only bad joke) +*/ +template void transformVectorsInPlace(const Math::Quaternion& normalizedQuaternion, U& vectors) { + for(auto& vector: vectors) vector = normalizedQuaternion.transformVectorNormalized(vector); +} + +/** @overload */ +template void transformVectorsInPlace(const Math::Complex& normalizedComplex, U& vectors) { + for(auto& vector: vectors) vector = normalizedComplex.transformVectorNormalized(vector); +} + +/** @overload */ +template void transformVectorsInPlace(const Math::Matrix3& matrix, U& vectors) { + for(auto& vector: vectors) vector = matrix.transformVector(vector); +} + +/** @overload */ +template void transformVectorsInPlace(const Math::Matrix4& matrix, U& vectors) { + for(auto& vector: vectors) vector = matrix.transformVector(vector); +} + +/** +@brief Transform vectors using given transformation + +Returns transformed vectors instead of modifying them in-place. See +transformVectorsInPlace() for more information. +*/ +template U transformVectors(const T& transformation, U vectors) { + U result(std::move(vectors)); + transformVectorsInPlace(transformation, result); + return result; +} + +/** +@brief Transform points in-place using given transformation + +Usable for one-time mesh transformations that would otherwise negatively affect +dependent objects, such as (uneven) scaling. Accepts any forward-iterable type +with compatible vector type as @p vectors. Expects that +@ref Math::DualQuaternion "DualQuaternion" is normalized, no further +requirements are for transformation matrices. + +Unlike in transformVectorsInPlace(), the transformation also involves +translation. + +Example usage: @code -std::vector vertices; -MeshTools::transform(Matrix4::scaling({2.0f, 0.5f, 0.0f}), vertices); +std::vector points; +auto transformation = DualQuaternion::rotation(35.0_degf, Vector3::yAxis())* + DualQuaternion::translation({0.5f, -1.0f, 3.0f}); +MeshTools::transformPointsInPlace(rotation, points); @endcode + +@see transformPoints(), Matrix3::transformPoint(), Matrix4::transformPoint(), + DualQuaternion::transformPointNormalized() +*/ +template void transformPointsInPlace(const Math::DualQuaternion& normalizedDualQuaternion, U& points) { + for(auto& point: points) point = normalizedDualQuaternion.transformPointNormalized(point); +} + +/** @overload */ +template void transformPointsInPlace(const Math::Matrix3& matrix, U& points) { + for(auto& point: points) point = matrix.transformPoint(point); +} + +/** @overload */ +template void transformPointsInPlace(const Math::Matrix4& matrix, U& points) { + for(auto& point: points) point = matrix.transformPoint(point); +} + +/** +@brief Transform points using given transformation + +Returns transformed points instead of modifying them in-place. See +transformPointsInPlace() for more information. */ -template inline void transform(const Math::Matrix& matrix, U& vertices) { - for(Math::Vector& vertex: vertices) - vertex = matrix*vertex; +template U transformPoints(const T& transformation, U vectors) { + U result(std::move(vectors)); + transformPointsInPlace(transformation, result); + return result; } }}