diff --git a/src/Magnum/Math/Bezier.h b/src/Magnum/Math/Bezier.h new file mode 100644 index 000000000..b3f1bda6a --- /dev/null +++ b/src/Magnum/Math/Bezier.h @@ -0,0 +1,117 @@ +#ifndef Magnum_Math_Bezier_h +#define Magnum_Math_Bezier_h + +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Math::Bezier + */ + +#include +#include "Vector.h" + +namespace Magnum { namespace Math { + /** + @brief Bezier + @tparam order Order of Bezier curve + @tparam dimensions Dimensions of the control points + @tparam T Underlying data type + + See Bezier Curve. + */ + template class Bezier { + + public: + + /** @brief Construct Bezier curve with the given array of control points */ + explicit Bezier(const std::array, order+1> &points) { + _points = points; + } + + /** + * @brief Divides a Bezier curve into two curves of same order having their own control points. + * De Casteljau's algorithm is used. + * @param t The interpolation factor + * + * @return Array of two Bezier curves of the same order + */ + std::array, 2> subdivide(Float t) const { + auto i_points = calculateIntermediatePoints(t); + std::array, order + 1> left, right; + for(UnsignedInt i=0; i<=order; ++i){ + left[i] = i_points[0][i]; + } + for(UnsignedInt i = 0, j = order; j>=0; --j, ++i){ + right[i] = i_points[i][j]; + } + return {Bezier(left), Bezier(right)}; + } + + /** + * @brief Finds the point in the curve for a given interpolation factor + * De Casteljau's algorithm is used. + * @param t The interpolation factor + */ + Vector lerp (Float t) const { + auto i_points= calculateIntermediatePoints(t); + return i_points[0][order]; + }; + + private: + + /** + * @brief Calculates and returns all intermediate points generated when using De Casteljau's algorithm + * @param t The interpolation factor + * + */ + std::vector>> calculateIntermediatePoints(Float t) const { + const auto n = order + 1; + std::vector>> i_points(n,std::vector>(n)); + for (UnsignedInt i = 0; i < n; ++i) { + i_points[i][0] = _points[i]; + } + for (UnsignedInt r = 1; r < n; ++r) { + for (UnsignedInt i = 0; i < n - r; ++i) { + i_points[i][r] = (1 - t) * i_points[i][r - 1] + t * i_points[i + 1][r - 1]; + } + } + return i_points; + }; + + std::array, order + 1> _points; + }; + + template using QuadraticBezier = Bezier<2, dimensions, T>; + template using CubicBezier = Bezier <3, dimensions, T>; + template using QuadraticBezier2D = QuadraticBezier<2, T>; + template using QuadraticBezier3D = QuadraticBezier<3, T>; + template using CubicBezier2D = CubicBezier<2, T>; + template using CubicBezier3D = CubicBezier<3, T>; + + } + +} +#endif //Magnum_Math_Bezier_h diff --git a/src/Magnum/Math/CMakeLists.txt b/src/Magnum/Math/CMakeLists.txt index a39dca0ab..c0d0990f5 100644 --- a/src/Magnum/Math/CMakeLists.txt +++ b/src/Magnum/Math/CMakeLists.txt @@ -25,6 +25,7 @@ set(MagnumMath_HEADERS Angle.h + Bezier.h BoolVector.h Color.h Complex.h diff --git a/src/Magnum/Math/Test/BezierTest.cpp b/src/Magnum/Math/Test/BezierTest.cpp new file mode 100644 index 000000000..54fd303f2 --- /dev/null +++ b/src/Magnum/Math/Test/BezierTest.cpp @@ -0,0 +1,80 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include "Magnum/Math/Bezier.h" + +namespace Magnum { namespace Math { namespace Test { + + struct BezierTest: Corrade::TestSuite::Tester { + explicit BezierTest(); + + void testQuadratic(); + + void testCubic(); + + }; + + BezierTest::BezierTest() { + addTests({&BezierTest::testQuadratic}); + addTests({&BezierTest::testCubic}); + } + + template + inline T sqr(T t){ return t*t; } + + template + inline T cube(T t){ return t*t*t; } + + + void BezierTest::testQuadratic() { + typedef Math::Vector<2, Float> Vector2; + Vector2 p0(0.0f, 0.0f), p1(10.0f, 15.0f), p2(20.0f, 4.0f); + std::array points = {p0, p1, p2}; + QuadraticBezier2D bezier(points); + for(Float t = 0.0; t <= 1.0f; t += 0.01f) { + Vector2 expected = sqr(1-t)*p0 + 2*(1-t)*t*p1 + sqr(t) *p2; + CORRADE_COMPARE(bezier.lerp(t), expected); + } + } + + void BezierTest::testCubic() { + typedef Math::Vector<2, Float> Vector2; + Vector2 p0(0.0f, 0.0f), p1(10.0f, 15.0f), p2(20.0f, 4.0f), p3(5.0f, -20.0f); + std::array points = {p0, p1, p2, p3}; + CubicBezier2D bezier(points); + for(Float t = 0.0; t <= 1.0f; t += 0.01f) { + Vector2 expected = cube(1-t)*p0 + + 3*sqr(1-t)*t*p1 + + 3*(1-t)*sqr(t) *p2 + + cube(t)*p3; + CORRADE_COMPARE(bezier.lerp(t), expected); + } + } + + + }}} + +CORRADE_TEST_MAIN(Magnum::Math::Test::BezierTest) diff --git a/src/Magnum/Math/Test/CMakeLists.txt b/src/Magnum/Math/Test/CMakeLists.txt index 9eeba4cfb..39fd953fb 100644 --- a/src/Magnum/Math/Test/CMakeLists.txt +++ b/src/Magnum/Math/Test/CMakeLists.txt @@ -43,6 +43,7 @@ corrade_add_test(MathMatrix4Test Matrix4Test.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathSwizzleTest SwizzleTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathUnitTest UnitTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathAngleTest AngleTest.cpp LIBRARIES MagnumMathTestLib) +corrade_add_test(MathBezierTest BezierTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathRangeTest RangeTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathDualTest DualTest.cpp LIBRARIES MagnumMathTestLib)