進め、自分。

ゲームプログラマがC++のことなどを。

角度クラス 解説

角度クラスが一通り実装できたので解説を。(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