角度クラス 解説
角度クラスが一通り実装できたので解説を。(3年越しだな…)
まずは使い方。
void func( Angle a ) { ... } ~~~ Angle a(0.5f); // 180度 Degree d(180.f); // 180度 Radian r(pi); // 180度 // どれでもOK func(a); func(d); func(r); // 四則演算 //a += 0.1f; // NG 単位の不明な数値との算術は禁止 a += Angle(0.1f); // OK a += d; // OK // 比較 Angle(0.1f) == Angle(0.5f); // false Angle(0.1f) < Angle(0.5f); // true a.value(1.f); // 値セット auto v = a.value(); // 値ゲット auto a2 = a.toAngle(); // Angle型へ変換 auto d2 = a.toDegree(); // Degree型へ変換 auto r2 = a.toRadian(); // Radian型へ変換 a = Angle(-0.5f).abs(); // 絶対値 =0.5f // 正規化 [0.0~1.0)の範囲に変換 = [0度~360度) a = Angle(1.5f).normalize(); // 0.5f a = Angle(-1.25f).normalize(); // 0.75f // 正と負それぞれの方向で正規化 (-1.0~1.0) = (-360度~360度) a = Angle(-1.25f).signedNormalize(); // -0.25f // 角度差を計算(符号あり最短距離)[-180度~180度) d = Degree(10.f).distanceFrom( Degree(350.f) ); // -20.f // 指定した角度を中心に反転させた角度を計算する d = Degree(10.f).reflect( Degree(30.f) ); // 50.f d = Degree(350.f).reflect( Degree(10.f) ); // 390.f // 三角関数 v = a.sin(); v = a.cos(); v = a.tan(); // 定数 d = Degree::Zero(); // 0 d = Degree::One(); // 360 d = Degree::Half(); // 180
ざっとこんな感じで。
続いて実装方法。
まずはコアなクラステンプレート部分。
template <typename T, template <typename> class Policy> class BasicAngles;
Tは値の型。具体的にはfloatやdoubleです。
PolicyはAngle,Degree,Radianの特性を反映する部分ですね。
実際下記のように定義しています。
/// 正規化角度ポリシー template <typename T> struct AnglePolicy { static constexpr T kCycle = static_cast<T>(1.0); ///< 1周の値 }; /// ラジアンポリシー template <typename T> struct RadianPolicy { static constexpr T kCycle = Konst<T>::pi2; ///< 1周の値 }; /// デグリーポリシー template <typename T> struct DegreePolicy { static constexpr T kCycle = static_cast<T>(360); ///< 1周の値 };
せっかくポリシー分けたんですけど、見ての通り1周の値しかないです。
まあ、3種類の違いってそこしかないですからね。
で、これらから次の定義をしています。
/// 正規化角度クラス template <typename T> using BasicAngle = detail::BasicAngles<T, detail::AnglePolicy>; /// ラジアンクラス template <typename T> using BasicRadian = detail::BasicAngles<T, detail::RadianPolicy>; /// デグリークラス template <typename T> using BasicDegree = detail::BasicAngles<T, detail::DegreePolicy>;
double型のRadianを作りたかったら、
BasicRadian<double>
を使います。
ここから更に、次の定義をしています。
自分は普段 double を使うことがほとんどないので、
floatが標準として扱いやすいようにですね。
構成としてはstd::stringとstd::basic_stringの関係を意識してます。
/// 標準の正規化角度クラス using Angle = BasicAngle<float>; /// 標準のラジアンクラス using Radian = BasicRadian<float>; /// 標準のデグリークラス using Degree = BasicDegree<float>;
型を柔軟に吸収したい場合は次の関数を使います。
template <typename T> constexpr BasicAngle<T> MakeAngle( T value ) noexcept { return BasicAngle<T>(value); } template <typename T> constexpr BasicRadian<T> MakeRadian( T value ) noexcept { return BasicRadian<T>(value); } template <typename T> constexpr BasicDegree<T> MakeDegree( T value ) noexcept { return BasicDegree<T>(value); }
ざっと以上ですね。
実装の中身は下記のリンクからどうぞ。
tofu/Angle.hpp at develop · fsawa/tofu · GitHub
tofu/Angle.cpp at develop · fsawa/tofu · GitHub