mirror of https://github.com/mosra/magnum.git
Browse Source
Next few commits will add requirement for "strongly typed" angles in all
function parameters, e.g.:
Matrix3::rotation(24.0_degf);
Math::sin(1.047_radf);
The purpose is to make angle entering less error-prone, e.g. not passing
degrees when radians should be etc.
pull/7/head
7 changed files with 397 additions and 0 deletions
@ -0,0 +1,29 @@ |
|||||||
|
/*
|
||||||
|
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Magnum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License version 3 |
||||||
|
only, as published by the Free Software Foundation. |
||||||
|
|
||||||
|
Magnum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License version 3 for more details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Angle.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { |
||||||
|
|
||||||
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||||
|
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Rad<float>&); |
||||||
|
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Deg<float>&); |
||||||
|
#ifndef MAGNUM_TARGET_GLES |
||||||
|
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Rad<double>&); |
||||||
|
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Deg<double>&); |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,248 @@ |
|||||||
|
#ifndef Magnum_Math_Angle_h |
||||||
|
#define Magnum_Math_Angle_h |
||||||
|
/*
|
||||||
|
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Magnum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License version 3 |
||||||
|
only, as published by the Free Software Foundation. |
||||||
|
|
||||||
|
Magnum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License version 3 for more details. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Class Magnum::Math::Deg, Magnum::Math::Rad and related operators. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Utility/Debug.h> |
||||||
|
|
||||||
|
#include "Math/Constants.h" |
||||||
|
#include "Math/Math.h" |
||||||
|
#include "Math/Unit.h" |
||||||
|
|
||||||
|
#include "magnumVisibility.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Angle in degrees |
||||||
|
|
||||||
|
Along with Rad provides convenience classes to make angle specification and |
||||||
|
conversion less error-prone. |
||||||
|
|
||||||
|
@section Rad-usage Usage |
||||||
|
|
||||||
|
You can enter the value either by using literal: |
||||||
|
@code |
||||||
|
auto degrees = 60.0_degf; // type is Deg<float>
|
||||||
|
auto radians = 1.047_rad; // type is Rad<double>
|
||||||
|
@endcode |
||||||
|
|
||||||
|
Or explicitly convert unitless value (such as output from some function) to |
||||||
|
either degrees or radians: |
||||||
|
@code |
||||||
|
double foo(); |
||||||
|
|
||||||
|
Deg<float> degrees(35.0f); |
||||||
|
Rad<double> radians(foo()); |
||||||
|
//degrees = 60.0f; // error, no implicit conversion
|
||||||
|
@endcode |
||||||
|
|
||||||
|
The classes support all arithmetic operations, such as addition, subtraction |
||||||
|
or multiplication/division by unitless number: |
||||||
|
@code |
||||||
|
auto a = 60.0_degf + 17.35_degf; |
||||||
|
auto b = -a + 23.0_degf*4; |
||||||
|
//auto c = 60.0_degf*45.0_degf; // error, undefined resulting unit
|
||||||
|
@endcode |
||||||
|
|
||||||
|
It is also possible to compare angles with all comparison operators, but |
||||||
|
comparison of degrees and radians is not possible without explicit conversion |
||||||
|
to common type: |
||||||
|
@code |
||||||
|
Rad<float> angle(); |
||||||
|
|
||||||
|
Deg<float> x = angle(); // convert to degrees for easier comparison
|
||||||
|
if(x < 30.0_degf) foo(); |
||||||
|
//if(x > 1.57_radf) bar(); // error, both need to be of the same type
|
||||||
|
@endcode |
||||||
|
|
||||||
|
It is possible to seamlessly convert between degrees and radians and explicitly |
||||||
|
convert the value back to underlying type: |
||||||
|
@code |
||||||
|
float sine(Rad<float> angle); |
||||||
|
float a = sine(60.0_degf); // the same as sine(1.047_radf)
|
||||||
|
Deg<double> b = 1.047_rad; // the same as 60.0_deg
|
||||||
|
float d = double(b); // 60.0
|
||||||
|
//float e = b; // error, no implicit conversion
|
||||||
|
@endcode |
||||||
|
|
||||||
|
@section Rad-conversions Requirement of explicit conversion |
||||||
|
|
||||||
|
The requirement of explicit conversions from and to unitless types helps to |
||||||
|
reduce unit-based errors. Consider following example with implicit conversions |
||||||
|
allowed: |
||||||
|
@code |
||||||
|
float std::sin(float angle); |
||||||
|
float sine(Rad<float> angle); |
||||||
|
|
||||||
|
float a = 60.0f; // degrees
|
||||||
|
sine(a); // silent error, sine() expected radians
|
||||||
|
|
||||||
|
auto b = 60.0_degf; // degrees
|
||||||
|
std::sin(b); // silent error, std::sin() expected radians
|
||||||
|
@endcode |
||||||
|
|
||||||
|
These silent errors are easily avoided by requiring explicit conversions: |
||||||
|
@code |
||||||
|
//sine(angleInDegrees); // compilation error
|
||||||
|
sine(Deg<float>(angleInDegrees)); // explicitly specifying unit
|
||||||
|
|
||||||
|
//std::sin(angleInDegrees); // compilation error
|
||||||
|
std::sin(float(Rad<float>(angleInDegrees)); // required explicit conversion hints
|
||||||
|
// to user that this case needs special
|
||||||
|
// attention (i.e., conversion to radians)
|
||||||
|
@endcode |
||||||
|
*/ |
||||||
|
template<class T> class Deg: public Unit<Deg, T> { |
||||||
|
public: |
||||||
|
/** @brief Default constructor */ |
||||||
|
inline constexpr /*implicit*/ Deg() {} |
||||||
|
|
||||||
|
/** @brief Explicit constructor from unitless type */ |
||||||
|
inline constexpr explicit Deg(T value): Unit<Deg, T>(value) {} |
||||||
|
|
||||||
|
/** @brief Copy constructor */ |
||||||
|
inline constexpr /*implicit*/ Deg(Unit<Deg, T> value): Unit<Deg, T>(value) {} |
||||||
|
|
||||||
|
/** @brief Construct from another underlying type */ |
||||||
|
template<class U> inline constexpr explicit Deg(Unit<Deg, U> value): Unit<Deg, T>(value) {} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct degrees from radians |
||||||
|
* |
||||||
|
* Performs conversion from radians to degrees, i.e.: |
||||||
|
* @f[ |
||||||
|
* deg = 180 \frac {rad} \pi |
||||||
|
* @f] |
||||||
|
*/ |
||||||
|
inline constexpr /*implicit*/ Deg(Unit<Rad, T> value); |
||||||
|
}; |
||||||
|
|
||||||
|
#ifndef CORRADE_GCC46_COMPATIBILITY |
||||||
|
/** @relates Deg
|
||||||
|
@brief Double-precision degree value literal |
||||||
|
|
||||||
|
Example usage: |
||||||
|
@code |
||||||
|
double cosine = Math::cos(60.0_deg); // cosine = 0.5
|
||||||
|
double cosine = Math::cos(1.047_rad); // cosine = 0.5
|
||||||
|
@endcode |
||||||
|
@see operator""_degf(), operator""_rad() |
||||||
|
@note Not available on GCC < 4.7. Use Deg::Deg(T) instead. |
||||||
|
*/ |
||||||
|
inline constexpr Deg<double> operator "" _deg(long double value) { return Deg<double>(value); } |
||||||
|
|
||||||
|
/** @relates Deg
|
||||||
|
@brief Single-precision degree value literal |
||||||
|
|
||||||
|
Example usage: |
||||||
|
@code |
||||||
|
float tangent = Math::tan(60.0_degf); // tangent = 1.732f
|
||||||
|
float tangent = Math::tan(1.047_radf); // tangent = 1.732f
|
||||||
|
@endcode |
||||||
|
@see operator""_deg(), operator""_radf() |
||||||
|
@note Not available on GCC < 4.7. Use Deg::Deg(T) instead. |
||||||
|
*/ |
||||||
|
inline constexpr Deg<float> operator "" _degf(long double value) { return Deg<float>(value); } |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Angle in radians |
||||||
|
|
||||||
|
See Deg for more information. |
||||||
|
*/ |
||||||
|
template<class T> class Rad: public Unit<Rad, T> { |
||||||
|
public: |
||||||
|
/** @brief Default constructor */ |
||||||
|
inline constexpr /*implicit*/ Rad() {} |
||||||
|
|
||||||
|
/** @brief Construct from unitless type */ |
||||||
|
inline constexpr explicit Rad(T value): Unit<Rad, T>(value) {} |
||||||
|
|
||||||
|
/** @brief Copy constructor */ |
||||||
|
inline constexpr /*implicit*/ Rad(Unit<Rad, T> value): Unit<Rad, T>(value) {} |
||||||
|
|
||||||
|
/** @brief Construct from another underlying type */ |
||||||
|
template<class U> inline constexpr explicit Rad(Unit<Rad, U> value): Unit<Rad, T>(value) {} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct radians from degrees |
||||||
|
* |
||||||
|
* Performs conversion from degrees to radians, i.e.: |
||||||
|
* @f[ |
||||||
|
* rad = deg \frac \pi 180 |
||||||
|
* @f] |
||||||
|
*/ |
||||||
|
inline constexpr /*implicit*/ Rad(Unit<Deg, T> value); |
||||||
|
}; |
||||||
|
|
||||||
|
#ifndef CORRADE_GCC46_COMPATIBILITY |
||||||
|
/** @relates Rad
|
||||||
|
@brief Double-precision radian value literal |
||||||
|
|
||||||
|
See operator""_rad() for more information. |
||||||
|
@see operator""_radf(), operator""_deg() |
||||||
|
@note Not available on GCC < 4.7. Use Rad::Rad(T) instead. |
||||||
|
*/ |
||||||
|
inline constexpr Rad<double> operator "" _rad(long double value) { return Rad<double>(value); } |
||||||
|
|
||||||
|
/** @relates Rad
|
||||||
|
@brief Single-precision radian value literal |
||||||
|
|
||||||
|
See operator""_degf() for more information. |
||||||
|
@see operator""_rad(), operator""_degf() |
||||||
|
@note Not available on GCC < 4.7. Use Rad::Rad(T) instead. |
||||||
|
*/ |
||||||
|
inline constexpr Rad<float> operator "" _radf(long double value) { return Rad<float>(value); } |
||||||
|
#endif |
||||||
|
|
||||||
|
template<class T> inline constexpr Deg<T>::Deg(Unit<Rad, T> value): Unit<Deg, T>(T(180)*T(value)/Math::Constants<T>::pi()) {} |
||||||
|
template<class T> inline constexpr Rad<T>::Rad(Unit<Deg, T> value): Unit<Rad, T>(T(value)*Math::Constants<T>::pi()/T(180)) {} |
||||||
|
|
||||||
|
/** @debugoperator{Magnum::Math::Rad} */ |
||||||
|
template<class T> Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Rad<T>& value) { |
||||||
|
debug << "Rad("; |
||||||
|
debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); |
||||||
|
debug << T(value) << ")"; |
||||||
|
debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); |
||||||
|
return debug; |
||||||
|
} |
||||||
|
|
||||||
|
/** @debugoperator{Magnum::Math::Deg} */ |
||||||
|
template<class T> Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Deg<T>& value) { |
||||||
|
debug << "Deg("; |
||||||
|
debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); |
||||||
|
debug << T(value) << ")"; |
||||||
|
debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); |
||||||
|
return debug; |
||||||
|
} |
||||||
|
|
||||||
|
/* Explicit instantiation for commonly used types */ |
||||||
|
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||||
|
extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Rad<float>&); |
||||||
|
extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Deg<float>&); |
||||||
|
#ifndef MAGNUM_TARGET_GLES |
||||||
|
extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Rad<double>&); |
||||||
|
extern template Corrade::Utility::Debug MAGNUM_EXPORT operator<<(Corrade::Utility::Debug, const Deg<double>&); |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,115 @@ |
|||||||
|
/*
|
||||||
|
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Magnum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License version 3 |
||||||
|
only, as published by the Free Software Foundation. |
||||||
|
|
||||||
|
Magnum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU Lesser General Public License version 3 for more details. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <sstream> |
||||||
|
#include <TestSuite/Tester.h> |
||||||
|
|
||||||
|
#include "Math/Angle.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace Math { namespace Test { |
||||||
|
|
||||||
|
class AngleTest: public Corrade::TestSuite::Tester { |
||||||
|
public: |
||||||
|
explicit AngleTest(); |
||||||
|
|
||||||
|
void construct(); |
||||||
|
void literals(); |
||||||
|
void conversion(); |
||||||
|
|
||||||
|
void debugDeg(); |
||||||
|
void debugRad(); |
||||||
|
}; |
||||||
|
|
||||||
|
typedef Math::Deg<float> Deg; |
||||||
|
typedef Math::Rad<float> Rad; |
||||||
|
typedef Math::Deg<double> Degd; |
||||||
|
typedef Math::Rad<double> Radd; |
||||||
|
|
||||||
|
AngleTest::AngleTest() { |
||||||
|
addTests(&AngleTest::construct, |
||||||
|
&AngleTest::literals, |
||||||
|
&AngleTest::conversion, |
||||||
|
|
||||||
|
&AngleTest::debugDeg, |
||||||
|
&AngleTest::debugRad); |
||||||
|
} |
||||||
|
|
||||||
|
void AngleTest::construct() { |
||||||
|
/* Default constructor */ |
||||||
|
constexpr Degd a; |
||||||
|
constexpr Deg m; |
||||||
|
CORRADE_COMPARE(double(a), 0.0f); |
||||||
|
CORRADE_COMPARE(float(m), 0.0f); |
||||||
|
|
||||||
|
/* Value constructor */ |
||||||
|
constexpr Deg b(25.0); |
||||||
|
constexpr Radd n(3.14); |
||||||
|
CORRADE_COMPARE(float(b), 25.0); |
||||||
|
CORRADE_COMPARE(double(n), 3.14); |
||||||
|
|
||||||
|
/* Copy constructor */ |
||||||
|
constexpr Deg c(b); |
||||||
|
constexpr Radd o(n); |
||||||
|
CORRADE_COMPARE(c, b); |
||||||
|
CORRADE_COMPARE(o, n); |
||||||
|
|
||||||
|
/* Conversion operator */ |
||||||
|
constexpr Degd d(b); |
||||||
|
constexpr Rad p(n); |
||||||
|
CORRADE_COMPARE(double(d), 25.0); |
||||||
|
CORRADE_COMPARE(float(p), 3.14f); |
||||||
|
} |
||||||
|
|
||||||
|
void AngleTest::literals() { |
||||||
|
constexpr auto a = 25.0_deg; |
||||||
|
constexpr auto b = 25.0_degf; |
||||||
|
CORRADE_VERIFY((std::is_same<decltype(a), const Degd>::value)); |
||||||
|
CORRADE_VERIFY((std::is_same<decltype(b), const Deg>::value)); |
||||||
|
CORRADE_COMPARE(double(a), 25.0); |
||||||
|
CORRADE_COMPARE(float(b), 25.0f); |
||||||
|
|
||||||
|
constexpr auto m = 3.14_rad; |
||||||
|
constexpr auto n = 3.14_radf; |
||||||
|
CORRADE_VERIFY((std::is_same<decltype(m), const Radd>::value)); |
||||||
|
CORRADE_VERIFY((std::is_same<decltype(n), const Rad>::value)); |
||||||
|
CORRADE_COMPARE(double(m), 3.14); |
||||||
|
CORRADE_COMPARE(float(n), 3.14f); |
||||||
|
} |
||||||
|
|
||||||
|
void AngleTest::conversion() { |
||||||
|
constexpr Deg a(Rad(1.57079633f)); |
||||||
|
CORRADE_COMPARE(float(a), 90.0f); |
||||||
|
|
||||||
|
constexpr Rad b(Deg(90.0)); |
||||||
|
CORRADE_COMPARE(float(b), 1.57079633f); |
||||||
|
} |
||||||
|
|
||||||
|
void AngleTest::debugDeg() { |
||||||
|
std::ostringstream o; |
||||||
|
|
||||||
|
Debug(&o) << Deg(90.0); |
||||||
|
CORRADE_COMPARE(o.str(), "Deg(90)\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void AngleTest::debugRad() { |
||||||
|
std::ostringstream o; |
||||||
|
|
||||||
|
Debug(&o) << Rad(1.5708); |
||||||
|
CORRADE_COMPARE(o.str(), "Rad(1.5708)\n"); |
||||||
|
} |
||||||
|
|
||||||
|
}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::Math::Test::AngleTest) |
||||||
Loading…
Reference in new issue