From 94e08a6e86e81a0bc38ceb21869505f367aaf8d3 Mon Sep 17 00:00:00 2001 From: Ashwin Ravichandran Date: Sun, 31 Jul 2016 06:35:06 -0700 Subject: [PATCH] Math : Fixed Bezier --- src/Magnum/Math/Bezier.h | 166 ++++++++++++++++------------ src/Magnum/Math/Test/BezierTest.cpp | 100 +++++++++-------- 2 files changed, 146 insertions(+), 120 deletions(-) diff --git a/src/Magnum/Math/Bezier.h b/src/Magnum/Math/Bezier.h index c7bbdc19b..211c64495 100644 --- a/src/Magnum/Math/Bezier.h +++ b/src/Magnum/Math/Bezier.h @@ -6,6 +6,7 @@ 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"), @@ -33,86 +34,109 @@ #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 +/** +@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 { +See Bezier Curve. +*/ +template class Bezier { - public: + public: - /** @brief Construct Bezier curve with the given array of control points */ - explicit Bezier(const std::array, order+1> &points) { - _points = points; - } + /** @brief Default constructor */ + constexpr /*implicit*/ Bezier(ZeroInitT = ZeroInit): _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 Construct Bezier without initializing the contents */ + explicit Bezier(NoInitT) {} + + /** @brief Construct Bezier curve with the given array of control points */ + template constexpr Bezier(U... u):_points{u...} { + static_assert(sizeof...(U) == order + 1, "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 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::array, order + 1>, order + 1> calculateIntermediatePoints(Float t) const { - const auto n = order + 1; - std::array, n>, n> i_points; - 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; - }; + /** + * @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];} - std::array, order + 1> _points; - }; + 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; + } - 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>; + 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 //Magnum_Math_Bezier_h +}} +#endif diff --git a/src/Magnum/Math/Test/BezierTest.cpp b/src/Magnum/Math/Test/BezierTest.cpp index cc2a3f5c0..da4368f01 100644 --- a/src/Magnum/Math/Test/BezierTest.cpp +++ b/src/Magnum/Math/Test/BezierTest.cpp @@ -3,6 +3,7 @@ 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"), @@ -24,58 +25,59 @@ */ #include -#include #include "Magnum/Math/Bezier.h" +#include "Magnum/Math/Functions.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); - } - } +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)