Browse Source

Math : Fixed Bezier

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

166
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,86 +34,109 @@
#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 Construct Bezier curve with the given array of control points */ /** @brief Default constructor */
explicit Bezier(const std::array<Vector<dimensions, T>, order+1> &points) { constexpr /*implicit*/ Bezier(ZeroInitT = ZeroInit): _points{} {}
_points = points;
}
/** /** @brief Construct Bezier without initializing the contents */
* @brief Divides a Bezier curve into two curves of same order having their own control points. explicit Bezier(NoInitT) {}
* De Casteljau's algorithm is used.
* @param t The interpolation factor /** @brief Construct Bezier curve with the given array of control points */
* template<typename... U> constexpr Bezier(U... u):_points{u...} {
* @return Array of two Bezier curves of the same order static_assert(sizeof...(U) == order + 1, "Wrong number of arguments");
*/ }
std::array<Bezier<order, dimensions, T >, 2> subdivide(Float t) const {
auto i_points = calculateIntermediatePoints(t); /**
std::array<Vector<dimensions, T>, order + 1> left, right; * @brief Divides a Bezier curve into two curves of same order having their own control points.
for(UnsignedInt i=0; i<=order; ++i){ * De Casteljau's algorithm is used.
left[i] = i_points[0][i]; * @param t The interpolation factor
} *
for(UnsignedInt i = 0, j = order; j>=0; --j, ++i){ * @return Array of two Bezier curves of the same order
right[i] = i_points[i][j]; */
} std::array<Bezier<order, dimensions, T>, 2> subdivide(Float t) const {
return {Bezier<order, dimensions, T>(left), Bezier<order, dimensions, T>(right)}; auto iPoints = calculateIntermediatePoints(t);
Bezier<order, dimensions, T> 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 * @brief Finds the point in the curve for a given interpolation factor
* 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];
}; }
private: /**
* @brief Control points of Bezier
/** * @return One-dimensional array of `size` length.
* @brief Calculates and returns all intermediate points generated when using De Casteljau's algorithm *
* @param t The interpolation factor * @see @ref operator[]()
* */
*/ Vector<dimensions, T>*points() {return _points;}
std::array<std::array<Vector<dimensions,T>, order + 1>, order + 1> calculateIntermediatePoints(Float t) const {
const auto n = order + 1; constexpr const Vector<dimensions, T>*points() const {return _points;} /**< @overload */
std::array<std::array<Vector<dimensions,T>, n>, n> i_points;
for (UnsignedInt i = 0; i < n; ++i) { /**
i_points[i][0] = _points[i]; * @brief Value at given position
} *
for (UnsignedInt r = 1; r < n; ++r) { * @see @ref points()
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]; Vector<dimensions, T>&operator[](std::size_t pos) {return _points[pos];}
}
}
return i_points;
};
std::array<Vector<dimensions, T >, order + 1> _points; constexpr Vector<dimensions, T> 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<Bezier<order, dimensions, T>, order + 1> calculateIntermediatePoints(Float t) const {
std::array<Bezier<order, dimensions, T>, 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<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

100
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 {
explicit BezierTest();
void testQuadratic();
void testCubic();
};
BezierTest::BezierTest() {
addTests({&BezierTest::testQuadratic});
addTests({&BezierTest::testCubic});
}
template <class T>
inline T sqr(T t){ return t*t; }
template <class T>
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<Vector2, 3> points = {p0, p1, p2};
QuadraticBezier2D<Float> 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<Vector2, 4> points = {p0, p1, p2, p3};
CubicBezier2D<Float> 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<Float> 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<Float> 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<Float> 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) CORRADE_TEST_MAIN(Magnum::Math::Test::BezierTest)

Loading…
Cancel
Save