Boostのバージョンを上げたらBoost QVMが入ってました."QVM: Quaternions, Vectors, Matrices"だそうです.
低次元のベクトル演算を対象にしており,2D/3Dの回転や並進がサクッと実装できそうなライブラリです.
ここ見ると他のライブラリ依存や自作のクラスに対して演算を定義しやすいようなので,「あー,こっちのVector型とこっちのvec3型の間で内積がほしい・・・」みたいなときに使えるはずです.
ちょっと触ってみたので,基本操作をメモついでに書いておきます.
書きやすく,Eigenほどコンパイル遅くならないあたり良さげ.Boostに入っているので共用コードでも使いやすいかと.
(MSVC 14.0(VS 2015), Boost 1.62.0で動作確認)
#include <iostream>
#include <boost/qvm/all.hpp>
//円周率
template<typename T> constexpr const T pi() { return static_cast<T>(std::atan2(0.0, -1.0)); }
// ベクトル表示用
template <typename T, int D>
std::ostream& operator<<(std::ostream& os, const boost::qvm::vec<T, D>& v)
{
os << "[ ";
for (int i = 0; i < D - 1; ++i) os << v.a[i] << ", ";
if (D > 0) os << v.a[D - 1];
os << " ]";
return os;
}
// 行列表示用
template <typename T, int Rows, int Cols>
std::ostream& operator<<(std::ostream& os, const boost::qvm::mat<T, Rows, Cols>& m)
{
os << "[ ";
for (int r = 0; r < Rows - 1; ++r)
{
for (int c = 0; c < Cols - 1; ++c) os << m.a[r][c] << ", ";
if (Cols > 0) os << m.a[r][Cols - 1];
os << "\n ";
}
if (Rows > 0)
{
for (int c = 0; c < Cols - 1; ++c) os << m.a[Rows - 1][c] << ", ";
if (Cols > 0) os << m.a[Rows - 1][Cols - 1];
}
os << " ]";
return os;
}
// ここから
int main()
{
using namespace boost::qvm;
// ゼロベクトル
vec<float, 3> vec_1 = zero_vec<float, 3>();
std::cout << "vec_1: " << vec_1 << std::endl;
// Initializer Listでベクトルの初期化
vec<float, 3> vec_2 { 0.3f, 0.4f, 0.5f };
std::cout << "vec_2: " << vec_1 << std::endl;
//ベクトルの要素アクセス
A<0>(vec_1) = 0.1f; // A<k>(v)でk要素目にアクセス
A1(vec_1) = 0.2f; // A1(v)でA<1>(v)にアクセスもできる(k = [0 ... 9])
Z(vec_1) = 0.3f; // X, Y, Z, WでA0, A1, A2, A3にアクセスできる
std::cout << "vec_1: " << vec_1 << std::endl;
std::cout << "vec_1(ZYX): " << vec<float, 3>(ZYX(vec_1)) << std::endl; // アクセス順を変える
// ------------------------
std::cout << std::endl;
// ------------------------
// 色々べクトルの演算
std::cout << "スカラー倍: " << vec_1 * 2.0f << std::endl;
std::cout << "和: " << vec_1 + vec_2 << std::endl;
std::cout << "内積: " << dot(vec_1, vec_2) << std::endl;
std::cout << "外積: " << cross(vec_1, vec_2) << std::endl;
std::cout << "大きさ(2-ノルム): " << mag(vec_1) << std::endl;
std::cout << "大きさの2乗: " << mag_sqr(vec_1) << std::endl;
std::cout << "正規化: " << normalized(vec_1) << std::endl;
std::cout << "対角行列: \n" << mat<float, 3, 3>(diag_mat(vec_1)) << std::endl;
// ------------------------
std::cout << std::endl;
// ------------------------
// 単位行列で初期化
mat<float, 3, 3> mat_1 = identity_mat<float, 3>();
std::cout << "mat_1: \n" << mat_1 << std::endl;
// Initializer Listで行列の初期化
mat<float, 3, 3> mat_2 = {
0.1f, 0.2f, 0.3f,
0.4f, 0.5f, 0.6f,
0.7f, 0.8f, 0.9f,
};
std::cout << "mat_2: \n" << mat_2 << std::endl;
//行列の要素アクセス
A<0, 1>(mat_1) = 0.1f;
A12(mat_1) = 0.2f;
std::cout << "mat_1: " << mat_1 << std::endl;
// ------------------------
std::cout << std::endl;
// ------------------------
// 色々行列の演算
std::cout << "スカラー倍: \n" << mat_1 * 2.0f << std::endl; // スカラー倍は各要素に作用
std::cout << "行列積: \n" << mat_1 * mat_2 << std::endl;
std::cout << "行列とベクトルの積: " << mat_1 * vec_1 << std::endl;
std::cout << "転置: \n" << mat<float, 3, 3>(transposed(mat_1)) << std::endl;
std::cout << "行列式: " << determinant(mat_1) << std::endl;
// ------------------------
std::cout << std::endl;
// ------------------------
// 回転行列
// vec_1軸回りにpi/6回転する回転行列(回転軸-回転角)
mat<float, 3, 3> rmat_1 = rot_mat<3, decltype(vec_1), float>(vec_1, pi<float>() / 6);
std::cout << "rmat_1: \n" << rmat_1 << std::endl;
// x軸回りにpi/4回転する回転行列
mat<float, 3, 3> rmat_2 = rotx_mat<3, float>(pi<float>() / 4);
std::cout << "rmat_2: \n" << rmat_2 << std::endl;
// ------------------------
std::cout << std::endl;
// ------------------------
//クォータニオンで回転を補完する
quat<float> quat_begin = identity_quat<float>();
quat<float> quat_end = rotx_quat(pi<float>() / 2) * roty_quat(pi<float>() / 3);
for (float t = 0.0f; t < 1.0f; t += 0.1f)
{
const auto& rmat = convert_to<mat<float, 3, 3>>(slerp(quat_begin, quat_end, t));
std::cout << "t: " << t << ", rmat: \n" << rmat << std::endl;
}
}
うーん,正直Eigen使えば良くねって感じが