Browse Source

Math : Fixed Bezier

pull/165/head
Ashwin Ravichandran 10 years ago
parent
commit
94e08a6e86
  1. 112
      src/Magnum/Math/Bezier.h
  2. 66
      src/Magnum/Math/Test/BezierTest.cpp

112
src/Magnum/Math/Bezier.h

@ -6,6 +6,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
Vladimír Vondruš <mosra@centrum.cz> Vladimír Vondruš <mosra@centrum.cz>
Copyright © 2016 Ashwin Ravichandran <ashwinravichandran24@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -33,22 +34,29 @@
#include <array> #include <array>
#include "Vector.h" #include "Vector.h"
namespace Magnum { namespace Math { namespace Magnum { namespace Math {
/** /**
@brief Bezier @brief Bezier
@tparam order Order of Bezier curve @tparam order Order of Bezier curve
@tparam dimensions Dimensions of the control points @tparam dimensions Dimensions of the control points
@tparam T Underlying data type @tparam T Underlying data type
See <a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve">Bezier Curve</a>. See <a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve">Bezier Curve</a>.
*/ */
template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier { template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier {
public: 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 */ /** @brief Construct Bezier curve with the given array of control points */
explicit Bezier(const std::array<Vector<dimensions, T>, order+1> &points) { template<typename... U> constexpr Bezier(U... u):_points{u...} {
_points = points; static_assert(sizeof...(U) == order + 1, "Wrong number of arguments");
} }
/** /**
@ -58,16 +66,16 @@ namespace Magnum { namespace Math {
* *
* @return Array of two Bezier curves of the same order * @return Array of two Bezier curves of the same order
*/ */
std::array<Bezier<order, dimensions, T >, 2> subdivide(Float t) const { std::array<Bezier<order, dimensions, T>, 2> subdivide(Float t) const {
auto i_points = calculateIntermediatePoints(t); auto iPoints = calculateIntermediatePoints(t);
std::array<Vector<dimensions, T>, order + 1> left, right; Bezier<order, dimensions, T> left, right;
for(UnsignedInt i=0; i<=order; ++i){ for(std::size_t i = 0; i <= order; ++i) {
left[i] = i_points[0][i]; left[i] = iPoints[0][i];
} }
for(UnsignedInt i = 0, j = order; j>=0; --j, ++i){ for(std::size_t i = 0, j = order; i <= order; --j, ++i) {
right[i] = i_points[i][j]; right[i] = iPoints[i][j];
} }
return {Bezier<order, dimensions, T>(left), Bezier<order, dimensions, T>(right)}; return {left, right};
} }
/** /**
@ -75,10 +83,29 @@ namespace Magnum { namespace Math {
* De Casteljau's algorithm is used. * De Casteljau's algorithm is used.
* @param t The interpolation factor * @param t The interpolation factor
*/ */
Vector<dimensions, T> lerp (Float t) const { Vector<dimensions, T> lerp(Float t) const {
auto i_points= calculateIntermediatePoints(t); auto iPoints = calculateIntermediatePoints(t);
return i_points[0][order]; return iPoints[0][order];
}; }
/**
* @brief Control points of Bezier
* @return One-dimensional array of `size` length.
*
* @see @ref operator[]()
*/
Vector<dimensions, T>*points() {return _points;}
constexpr const Vector<dimensions, T>*points() const {return _points;} /**< @overload */
/**
* @brief Value at given position
*
* @see @ref points()
*/
Vector<dimensions, T>&operator[](std::size_t pos) {return _points[pos];}
constexpr Vector<dimensions, T> operator[](std::size_t pos) const {return _points[pos];} /**< @overload */
private: private:
@ -87,32 +114,29 @@ namespace Magnum { namespace Math {
* @param t The interpolation factor * @param t The interpolation factor
* *
*/ */
std::array<std::array<Vector<dimensions,T>, order + 1>, order + 1> calculateIntermediatePoints(Float t) const { std::array<Bezier<order, dimensions, T>, order + 1> calculateIntermediatePoints(Float t) const {
const auto n = order + 1; std::array<Bezier<order, dimensions, T>, order + 1> iPoints;
std::array<std::array<Vector<dimensions,T>, n>, n> i_points; for(std::size_t i = 0; i <= order; ++i) {
for (UnsignedInt i = 0; i < n; ++i) { iPoints[i][0] = _points[i];
i_points[i][0] = _points[i];
} }
for (UnsignedInt r = 1; r < n; ++r) { for(std::size_t r = 1; r <= order; ++r) {
for (UnsignedInt i = 0; i < n - r; ++i) { for(std::size_t i = 0; i <= order - r; ++i) {
i_points[i][r] = (1 - t) * i_points[i][r - 1] + t * i_points[i + 1][r - 1]; iPoints[i][r] = (1 - t)*iPoints[i][r - 1] + t*iPoints[i + 1][r - 1];
} }
} }
return i_points; return iPoints;
}; }
std::array<Vector<dimensions, T >, order + 1> _points;
};
template<UnsignedInt dimensions, class T> using QuadraticBezier = Bezier<2, dimensions, T>; Vector<dimensions, T> _points[order + 1];
template<UnsignedInt dimensions, class T> using CubicBezier = Bezier <3, dimensions, T>; };
template<class T> using QuadraticBezier2D = QuadraticBezier<2, T>;
template<class T> using QuadraticBezier3D = QuadraticBezier<3, T>;
template<class T> using CubicBezier2D = CubicBezier<2, T>;
template<class T> using CubicBezier3D = CubicBezier<3, T>;
} template<UnsignedInt dimensions, class T> using QuadraticBezier = Bezier<2, dimensions, T>;
template<UnsignedInt dimensions, class T> using CubicBezier = Bezier<3, dimensions, T>;
template<class T> using QuadraticBezier2D = QuadraticBezier<2, T>;
template<class T> using QuadraticBezier3D = QuadraticBezier<3, T>;
template<class T> using CubicBezier2D = CubicBezier<2, T>;
template<class T> using CubicBezier3D = CubicBezier<3, T>;
} }}
#endif //Magnum_Math_Bezier_h #endif

66
src/Magnum/Math/Test/BezierTest.cpp

@ -3,6 +3,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
Vladimír Vondruš <mosra@centrum.cz> Vladimír Vondruš <mosra@centrum.cz>
Copyright © 2016 Ashwin Ravichandran <ashwinravichandran24@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -24,58 +25,59 @@
*/ */
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <array>
#include "Magnum/Math/Bezier.h" #include "Magnum/Math/Bezier.h"
#include "Magnum/Math/Functions.h"
namespace Magnum { namespace Math { namespace Test {
struct BezierTest: Corrade::TestSuite::Tester { namespace Magnum { namespace Math { namespace Test {
explicit BezierTest();
void testQuadratic(); typedef Math::Vector<2, Float> Vector2;
void testCubic(); struct BezierTest : Corrade::TestSuite::Tester {
explicit BezierTest();
}; void implicitConstructor();
BezierTest::BezierTest() { void quadratic();
addTests({&BezierTest::testQuadratic});
addTests({&BezierTest::testCubic});
}
template <class T> void cubic();
inline T sqr(T t){ return t*t; } };
template <class T> BezierTest::BezierTest() {
inline T cube(T t){ return t*t*t; } addTests({&BezierTest::implicitConstructor,
&BezierTest::quadratic,
&BezierTest::cubic});
}
void BezierTest::implicitConstructor() {
QuadraticBezier2D<Float> bezier;
Vector2 zero;
for(int i = 0; i < 3; ++i) {
CORRADE_COMPARE(bezier[i], zero);
}
}
void BezierTest::testQuadratic() { void BezierTest::quadratic() {
typedef Math::Vector<2, Float> Vector2;
Vector2 p0(0.0f, 0.0f), p1(10.0f, 15.0f), p2(20.0f, 4.0f); Vector2 p0(0.0f, 0.0f), p1(10.0f, 15.0f), p2(20.0f, 4.0f);
std::array<Vector2, 3> points = {p0, p1, p2}; QuadraticBezier2D<Float> bezier(p0, p1, p2);
QuadraticBezier2D<Float> bezier(points);
for(Float t = 0.0; t <= 1.0f; t += 0.01f) { 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; Vector2 expected = Math::pow<2>(1 - t)*p0 + 2*(1 - t)*t*p1 + Math::pow<2>(t)*p2;
CORRADE_COMPARE(bezier.lerp(t), expected); CORRADE_COMPARE(bezier.lerp(t), expected);
} }
} }
void BezierTest::testCubic() { void BezierTest::cubic() {
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); Vector2 p0(0.0f, 0.0f), p1(10.0f, 15.0f), p2(20.0f, 4.0f), p3(5.0f, -20.0f);
std::array<Vector2, 4> points = {p0, p1, p2, p3}; CubicBezier2D<Float> bezier(p0, p1, p2, p3);
CubicBezier2D<Float> bezier(points);
for(Float t = 0.0; t <= 1.0f; t += 0.01f) { for(Float t = 0.0; t <= 1.0f; t += 0.01f) {
Vector2 expected = cube(1-t)*p0 Vector2 expected = Math::pow<3>(1 - t)*p0
+ 3*sqr(1-t)*t*p1 + 3*Math::pow<2>(1 - t)*t*p1
+ 3*(1-t)*sqr(t) *p2 + 3*(1 - t)*Math::pow<2>(t)*p2
+ cube(t)*p3; + Math::pow<3>(t)*p3;
CORRADE_COMPARE(bezier.lerp(t), expected); CORRADE_COMPARE(bezier.lerp(t), expected);
} }
} }
}}} }}}
CORRADE_TEST_MAIN(Magnum::Math::Test::BezierTest) CORRADE_TEST_MAIN(Magnum::Math::Test::BezierTest)

Loading…
Cancel
Save