diff --git a/src/Magnum/Math/Bezier.h b/src/Magnum/Math/Bezier.h new file mode 100644 index 000000000..b9ed69cca --- /dev/null +++ b/src/Magnum/Math/Bezier.h @@ -0,0 +1,140 @@ +#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š + Copyright © 2016 Ashwin Ravichandran + + 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 Default constructor */ + constexpr /*implicit*/ Bezier(ZeroInitT = ZeroInit): _points{} {} + + /** @brief Construct Bezier without initializing the contents */ + explicit Bezier(NoInitT) {} + + /** @brief Construct Bezier curve with the given array of control points */ + template constexpr Bezier(Vector first, U... next):_points{first, next...} { + static_assert(sizeof...(U) + 1 == order + 1, "Bezier : Wrong number of arguments"); + } + + /** + * @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 iPoints = calculateIntermediatePoints(t); + Bezier left, right; + for(std::size_t i = 0; i <= order; ++i) { + left[i] = iPoints[0][i]; + } + for(std::size_t i = 0, j = order; i <= order; --j, ++i) { + right[i] = iPoints[i][j]; + } + return {left, 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 iPoints = calculateIntermediatePoints(t); + return iPoints[0][order]; + } + + /** + * @brief Control points of Bezier + * @return One-dimensional array of `size` length. + * + * @see @ref operator[]() + */ + Vector* points() { return _points; } + constexpr const Vector* points() const { return _points; } /**< @overload */ + + /** + * @brief Value at given position + * + * @see @ref points() + */ + Vector& operator[](std::size_t pos) { return _points[pos]; } + constexpr Vector operator[](std::size_t pos) const { return _points[pos]; } /**< @overload */ + + private: + + /** + * @brief Calculates and returns all intermediate points generated when using De Casteljau's algorithm + * @param t The interpolation factor + * + */ + std::array, order + 1> calculateIntermediatePoints(Float t) const { + std::array, order + 1> iPoints; + for(std::size_t i = 0; i <= order; ++i) { + iPoints[i][0] = _points[i]; + } + for(std::size_t r = 1; r <= order; ++r) { + for(std::size_t i = 0; i <= order - r; ++i) { + iPoints[i][r] = (1 - t)*iPoints[i][r - 1] + t*iPoints[i + 1][r - 1]; + } + } + return iPoints; + } + + Vector _points[order + 1]; +}; + +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 + diff --git a/src/Magnum/Math/CMakeLists.txt b/src/Magnum/Math/CMakeLists.txt index a39dca0ab..d6783821f 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..da4368f01 --- /dev/null +++ b/src/Magnum/Math/Test/BezierTest.cpp @@ -0,0 +1,83 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 + Vladimír Vondruš + Copyright © 2016 Ashwin Ravichandran + + 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" +#include "Magnum/Math/Functions.h" + + +namespace Magnum { namespace Math { namespace Test { + +typedef Math::Vector<2, Float> Vector2; + +struct BezierTest : Corrade::TestSuite::Tester { + explicit BezierTest(); + + void implicitConstructor(); + + void quadratic(); + + void cubic(); +}; + +BezierTest::BezierTest() { + addTests({&BezierTest::implicitConstructor, + &BezierTest::quadratic, + &BezierTest::cubic}); +} + +void BezierTest::implicitConstructor() { + QuadraticBezier2D bezier; + Vector2 zero; + for(int i = 0; i < 3; ++i) { + CORRADE_COMPARE(bezier[i], zero); + } +} + +void BezierTest::quadratic() { + Vector2 p0(0.0f, 0.0f), p1(10.0f, 15.0f), p2(20.0f, 4.0f); + QuadraticBezier2D bezier(p0, p1, p2); + for(Float t = 0.0; t <= 1.0f; t += 0.01f) { + Vector2 expected = Math::pow<2>(1 - t)*p0 + 2*(1 - t)*t*p1 + Math::pow<2>(t)*p2; + CORRADE_COMPARE(bezier.lerp(t), expected); + } +} + +void BezierTest::cubic() { + Vector2 p0(0.0f, 0.0f), p1(10.0f, 15.0f), p2(20.0f, 4.0f), p3(5.0f, -20.0f); + CubicBezier2D bezier(p0, p1, p2, p3); + for(Float t = 0.0; t <= 1.0f; t += 0.01f) { + Vector2 expected = Math::pow<3>(1 - t)*p0 + + 3*Math::pow<2>(1 - t)*t*p1 + + 3*(1 - t)*Math::pow<2>(t)*p2 + + Math::pow<3>(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)