Browse Source

Math: Minor Bézier cleanup and documentation improvements.

Removed the points() access function, as it has no real value.
pull/175/merge
Vladimír Vondruš 10 years ago
parent
commit
8bf1f5e5d8
  1. 136
      src/Magnum/Math/Bezier.h
  2. 38
      src/Magnum/Math/Test/BezierTest.cpp
  3. 3
      src/Magnum/Math/Test/CMakeLists.txt

136
src/Magnum/Math/Bezier.h

@ -1,6 +1,5 @@
#ifndef Magnum_Math_Bezier_h #ifndef Magnum_Math_Bezier_h
#define Magnum_Math_Bezier_h #define Magnum_Math_Bezier_h
/* /*
This file is part of Magnum. This file is part of Magnum.
@ -28,46 +27,59 @@
*/ */
/** @file /** @file
* @brief Class @ref Magnum::Math::Bezier * @brief Class @ref Magnum::Math::Bezier, alias @ref Magnum::Math::QuadraticBezier, @ref Magnum::Math::QuadraticBezier2D, @ref Magnum::Math::QuadraticBezier3D, @ref Magnum::Math::CubicBezier, @ref Magnum::Math::CubicBezier2D, @ref Magnum::Math::CubicBezier3D
*/ */
#include <array> #include <array>
#include "Vector.h"
#include "Magnum/Math/Vector.h"
namespace Magnum { namespace Math { namespace Magnum { namespace Math {
/** /**
@brief Bezier @brief Bézier curve
@tparam order Order of Bezier curve @tparam order Order of Bézier curve
@tparam dimensions Dimensions of the control points @tparam dimensions Dimensions of 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>. Implementation of M-order N-dimensional
[Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
@see @ref QuadraticBezier, @ref CubicBezier, @ref QuadraticBezier2D,
@ref QuadraticBezier3D, @ref CubicBezier2D, @ref CubicBezier3D
*/ */
template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier { template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier {
public: public:
typedef T Type; /**< @brief Underlying data type */
/** @brief Default constructor */ enum: UnsignedInt {
constexpr /*implicit*/ Bezier(ZeroInitT = ZeroInit): _points{} {} Order = order, /**< Order of Bézier curve */
Dimensions = dimensions /**< Dimensions of control points */
};
/** @brief Construct Bezier without initializing the contents */ /**
explicit Bezier(NoInitT) {} * @brief Default constructor
*
* Construct the curve with all control points being zero vectors.
*/
constexpr /*implicit*/ Bezier(ZeroInitT = ZeroInit) noexcept: _data{} {}
/** @brief Construct Bezier curve with the given array of control points */ /** @brief Construct Bézier without initializing the contents */
template<typename... U> constexpr Bezier(Vector<dimensions, T> first, U... next):_points{first, next...} { explicit Bezier(NoInitT) noexcept {}
static_assert(sizeof...(U) + 1 == order + 1, "Bezier : Wrong number of arguments");
/** @brief Construct Bézier curve with given array of control points */
template<typename... U> constexpr Bezier(const Vector<dimensions, T>& first, U... next) noexcept: _data{first, next...} {
static_assert(sizeof...(U) + 1 == order + 1, "Wrong number of arguments");
} }
/** /**
* @brief Divides a Bezier curve into two curves of same order having their own control points. * @brief Subdivide the curve
* De Casteljau's algorithm is used.
* @param t The interpolation factor * @param t The interpolation factor
* *
* @return Array of two Bezier curves of the same order * Divides the curve into two Bézier curves of same order having their
* own control points. Uses the [De Casteljau's algorithm](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm).
*/ */
std::array<Bezier<order, dimensions, T>, 2> subdivide(Float t) const { std::array<Bezier<order, dimensions, T>, 2> subdivide(Float t) const {
auto iPoints = calculateIntermediatePoints(t); const auto iPoints = calculateIntermediatePoints(t);
Bezier<order, dimensions, T> left, right; Bezier<order, dimensions, T> left, right;
for(std::size_t i = 0; i <= order; ++i) { for(std::size_t i = 0; i <= order; ++i) {
left[i] = iPoints[0][i]; left[i] = iPoints[0][i];
@ -79,43 +91,31 @@ template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier {
} }
/** /**
* @brief Finds the point in the curve for a given interpolation factor * @brief Interpolate the curve
* De Casteljau's algorithm is used.
* @param t The interpolation factor * @param t The interpolation factor
*
* Finds the point in the curve for a given interpolation factor. Uses
* the [De Casteljau's algorithm](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm).
*/ */
Vector<dimensions, T> lerp(Float t) const { Vector<dimensions, T> lerp(Float t) const {
auto iPoints = calculateIntermediatePoints(t); const auto iPoints = calculateIntermediatePoints(t);
return iPoints[0][order]; return iPoints[0][order];
} }
/** /**
* @brief Control points of Bezier * @brief Control point access
* @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() * @p i should not be larger than @ref Order.
*/ */
Vector<dimensions, T>& operator[](std::size_t pos) { return _points[pos]; } Vector<dimensions, T>& operator[](std::size_t i) { return _data[i]; }
constexpr Vector<dimensions, T> operator[](std::size_t pos) const { return _points[pos]; } /**< @overload */ constexpr Vector<dimensions, T> operator[](std::size_t i) const { return _data[i]; } /**< @overload */
private: private:
/* Calculates and returns all intermediate points generated when using De Casteljau's algorithm */
/**
* @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> calculateIntermediatePoints(Float t) const {
std::array<Bezier<order, dimensions, T>, order + 1> iPoints; std::array<Bezier<order, dimensions, T>, order + 1> iPoints;
for(std::size_t i = 0; i <= order; ++i) { for(std::size_t i = 0; i <= order; ++i) {
iPoints[i][0] = _points[i]; iPoints[i][0] = _data[i];
} }
for(std::size_t r = 1; r <= order; ++r) { for(std::size_t r = 1; r <= order; ++r) {
for(std::size_t i = 0; i <= order - r; ++i) { for(std::size_t i = 0; i <= order - r; ++i) {
@ -125,16 +125,64 @@ template<UnsignedInt order, UnsignedInt dimensions, class T> class Bezier {
return iPoints; return iPoints;
} }
Vector<dimensions, T> _points[order + 1]; Vector<dimensions, T> _data[order + 1];
}; };
/**
@brief Quadratic Bézier curve
Convenience alternative to `Bezier<2, dimensions, T>`. See @ref Bezier for more
information.
@see @ref QuadraticBezier2D, @ref QuadraticBezier3D
*/
template<UnsignedInt dimensions, class T> using QuadraticBezier = Bezier<2, dimensions, T>; template<UnsignedInt dimensions, class T> using QuadraticBezier = Bezier<2, dimensions, T>;
template<UnsignedInt dimensions, class T> using CubicBezier = Bezier<3, dimensions, T>;
/**
@brief Two-dimensional quadratic Bézier curve
Convenience alternative to `QuadraticBezier<2, T>`. See @ref QuadraticBezier
and @ref Bezier for more information.
@see @ref QuadraticBezier3D
*/
template<class T> using QuadraticBezier2D = QuadraticBezier<2, T>; template<class T> using QuadraticBezier2D = QuadraticBezier<2, T>;
/**
@brief Three-dimensional quadratic Bézier curve
Convenience alternative to `QuadraticBezier<3, T>`. See @ref QuadraticBezier
and @ref Bezier for more information.
@see @ref QuadraticBezier2D
*/
template<class T> using QuadraticBezier3D = QuadraticBezier<3, T>; template<class T> using QuadraticBezier3D = QuadraticBezier<3, T>;
/**
@brief Cubic Bézier curve
Convenience alternative to `Bezier<3, dimensions, T>`. See @ref Bezier for more
information.
@see @ref CubicBezier2D, @ref CubicBezier3D
*/
template<UnsignedInt dimensions, class T> using CubicBezier = Bezier<3, dimensions, T>;
/**
@brief Two-dimensional cubic Bézier curve
Convenience alternative to `CubicBezier<2, T>`. See @ref CubicBezier
and @ref Bezier for more information.
@see @ref CubicBezier3D
*/
template<class T> using CubicBezier2D = CubicBezier<2, T>; template<class T> using CubicBezier2D = CubicBezier<2, T>;
/**
@brief Three-dimensional cubic Bézier curve
Convenience alternative to `CubicBezier<3, T>`. See @ref CubicBezier
and @ref Bezier for more information.
@see @ref CubicBezier2D
*/
template<class T> using CubicBezier3D = CubicBezier<3, T>; template<class T> using CubicBezier3D = CubicBezier<3, T>;
}} }}
#endif #endif

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

@ -25,55 +25,57 @@
*/ */
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/Math/Bezier.h" #include "Magnum/Math/Bezier.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/Math/Functions.h" #include "Magnum/Math/Functions.h"
namespace Magnum { namespace Math { namespace Test { namespace Magnum { namespace Math { namespace Test {
typedef Math::Vector<2, Float> Vector2; typedef Math::Vector2<Float> Vector2;
typedef Math::QuadraticBezier2D<Float> QuadraticBezier2D;
typedef Math::CubicBezier2D<Float> CubicBezier2D;
struct BezierTest : Corrade::TestSuite::Tester { struct BezierTest : Corrade::TestSuite::Tester {
explicit BezierTest(); explicit BezierTest();
void implicitConstructor(); void implicitConstructor();
void quadratic(); void lerpQuadratic();
void lerpCubic();
void cubic();
}; };
BezierTest::BezierTest() { BezierTest::BezierTest() {
addTests({&BezierTest::implicitConstructor, addTests({&BezierTest::implicitConstructor,
&BezierTest::quadratic,
&BezierTest::cubic}); &BezierTest::lerpQuadratic,
&BezierTest::lerpCubic});
} }
void BezierTest::implicitConstructor() { void BezierTest::implicitConstructor() {
QuadraticBezier2D<Float> bezier; QuadraticBezier2D bezier;
Vector2 zero;
for(int i = 0; i < 3; ++i) { for(int i = 0; i < 3; ++i) {
CORRADE_COMPARE(bezier[i], zero); CORRADE_COMPARE(bezier[i], Vector2{});
} }
} }
void BezierTest::quadratic() { void BezierTest::lerpQuadratic() {
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);
QuadraticBezier2D<Float> bezier(p0, p1, p2); QuadraticBezier2D bezier(p0, p1, p2);
for(Float t = 0.0; t <= 1.0f; t += 0.01f) { 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; 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::cubic() { void BezierTest::lerpCubic() {
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);
CubicBezier2D<Float> bezier(p0, p1, p2, p3); CubicBezier2D bezier(p0, p1, p2, p3);
for(Float t = 0.0; t <= 1.0f; t += 0.01f) { for(Float t = 0.0; t <= 1.0f; t += 0.01f) {
Vector2 expected = Math::pow<3>(1 - t)*p0 Vector2 expected = Math::pow<3>(1 - t)*p0 +
+ 3*Math::pow<2>(1 - t)*t*p1 3*Math::pow<2>(1 - t)*t*p1 +
+ 3*(1 - t)*Math::pow<2>(t)*p2 3*(1 - t)*Math::pow<2>(t)*p2 +
+ Math::pow<3>(t)*p3; Math::pow<3>(t)*p3;
CORRADE_COMPARE(bezier.lerp(t), expected); CORRADE_COMPARE(bezier.lerp(t), expected);
} }
} }

3
src/Magnum/Math/Test/CMakeLists.txt

@ -43,7 +43,6 @@ corrade_add_test(MathMatrix4Test Matrix4Test.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathSwizzleTest SwizzleTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathSwizzleTest SwizzleTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathUnitTest UnitTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathUnitTest UnitTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathAngleTest AngleTest.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(MathRangeTest RangeTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathDualTest DualTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathDualTest DualTest.cpp LIBRARIES MagnumMathTestLib)
@ -52,6 +51,8 @@ corrade_add_test(MathDualComplexTest DualComplexTest.cpp LIBRARIES MagnumMathTes
corrade_add_test(MathQuaternionTest QuaternionTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathQuaternionTest QuaternionTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathDualQuaternionTest DualQuaternionTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathDualQuaternionTest DualQuaternionTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathBezierTest BezierTest.cpp LIBRARIES MagnumMathTestLib)
set_property(TARGET set_property(TARGET
MathVectorTest MathVectorTest
MathMatrixTest MathMatrixTest

Loading…
Cancel
Save