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
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
copy of this software and associated documentation files (the "Software"),
@ -33,86 +34,109 @@
#include <array>
#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 <a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve">Bezier Curve</a>.
*/
template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier {
See <a href="https://en.wikipedia.org/wiki/B%C3%A9zier_curve">Bezier Curve</a>.
*/
template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier {
public:
public:
/** @brief Construct Bezier curve with the given array of control points */
explicit Bezier(const std::array<Vector<dimensions, T>, 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<Bezier<order, dimensions, T >, 2> subdivide(Float t) const {
auto i_points = calculateIntermediatePoints(t);
std::array<Vector<dimensions, T>, 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<order, dimensions, T>(left), Bezier<order, dimensions, T>(right)};
/** @brief Construct Bezier without initializing the contents */
explicit Bezier(NoInitT) {}
/** @brief Construct Bezier curve with the given array of control points */
template<typename... U> 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<Bezier<order, dimensions, T>, 2> subdivide(Float t) const {
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
* De Casteljau's algorithm is used.
* @param t The interpolation factor
*/
Vector<dimensions, T> 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<std::array<Vector<dimensions,T>, order + 1>, order + 1> calculateIntermediatePoints(Float t) const {
const auto n = order + 1;
std::array<std::array<Vector<dimensions,T>, 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<dimensions, T> 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<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];}
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>;
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>;
Vector<dimensions, T> _points[order + 1];
};
}
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
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
copy of this software and associated documentation files (the "Software"),
@ -24,58 +25,59 @@
*/
#include <Corrade/TestSuite/Tester.h>
#include <array>
#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)

Loading…
Cancel
Save