CMakeで管理するプロジェクトでEigen 3.Xを使うときのTips

Eigen 3.3以降

Eigen 3.3以降は

find_package(Eigen3 REQUIRED)

として、

target_link_libraries(mylibrary Eigen3::Eigen)

とすれば良い。

Eigen 3.3以降に関する公式ドキュメント: eigen.tuxfamily.org

なおmacOSのHomebrewで

brew install eigen

とすると、2019年3月15日現在、Eigen 3.3.7が入るので、この方法で良い。

Eigen 3.2以前

Eigen 3.2以前やEigen 3.3-betaなどは

find_package(Eigen3 REQUIRED)

としても、EIGEN3_INCLUDE_DIRなどの変数はセットされるが、上述のようなEigen3::Eigenというtargetは生成されない。そこで、

target_include_directories(mylibrary PUBLIC ${EIGEN3_INCLUDE_DIR})

などとしてヘッダーへのパスを通す必要がある。

なお、この方法だとmylibraryとEigenとの依存関係が直接的には指定できていないので、その後階層的に依存関係を構築したりする際にもしかするとどこかで弊害があるかもしれない(要確認)し、見た目にも依存関係が分かりにくい(主観)。

なおUbuntuのaptで

sudo apt install libeigen3-dev

とすると、Eigen 3.2やEigen 3.3-betaがインストールされる環境が存在するので、その場合はこのような方法をとる必要がある。

両方のケースに対応するCMake

両者に対応し、依存関係も直接指定するには、

find_package(Eigen3 REQUIRED)
if((NOT TARGET Eigen3::Eigen) AND (DEFINED EIGEN3_INCLUDE_DIR))
    add_library(AliasEigen3 INTERFACE)
    target_include_directories(AliasEigen3 INTERFACE ${EIGEN3_INCLUDE_DIR})
    add_library(Eigen3::Eigen ALIAS AliasEigen3)
endif()

とした上で

target_link_libraries(mylibrary Eigen3::Eigen)

とすれば良い。

Eigenはheader-only libraryなので、INTERFACEキーワードでtargetを設定した上で、ALIASキーワードを使ってEigen3::Eigenで指せるようにしている。

cmake.org

参考レポジトリ

下記のレポジトリの2019年3月15日現在の最新版では、上のような方法で両者のケースに対応している。

github.com github.com

CMakeでビルドした実行プログラムのあるディレクトリにリソースファイルをコピーする

やりたいこと

CMakeを用いてプロジェクトを管理しているときに、ビルドした実行ファイルと同じディレクトリ(またはそこから相対的に定義されるディレクトリ)に画像データ等のリソースデータをコピーしたいという状況を考える。

リソースファイルは、例えば次のように定義されているとする。

file(GLOB RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/*.png)

問題点

ビルド環境ごとにビルドした実行プログラムが配置されるディレクトリは異なる可能性がある。

例えば、Makefileを生成してmakeとした場合には${CMAKE_CURRENT_BINARY_DIR}に実行プログラムが配置されるが、Xcodeを用いてビルドすると、例えばDebugモードでビルドすると${CMAKE_CURRENT_BINARY_DIR}/Debugに配置される。

更に、macOS.appバンドルを生成する場合は、実行ファイルの本体は[...].app/Contents/MacOSに配置される。

したがって、リソースファイルを実行ファイルのあるディレクトリにコピーしたい場合、単にfileコマンドを使って

file(COPY ${RESOURCE_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

としても、ビルド環境によっては意図通りにコピーされないことがある。

解決策:Generator Expression

Generator Expressionを用いれば良い。これは、CMakeのドキュメントに

Generator expressions are evaluated during build system generation to produce information specific to each build configuration.

と説明にあるように、ビルド環境に応じて評価される式ということらしい。

したがって、ビルド環境依存の部分は、generator expressionsを用いることで統一的に記述することができるようになる。

CMakeに定義されているgenerator expressionsのうち、特に

$<TARGET_FILE_DIR:my-program>

という式を用いると、ビルドした実行プログラムが配置されるディレクトリを得ることができる。ただしmy-programの部分にはそれぞれのターゲット名が入る。

リソースファイルをコピーするには、これを用いて

add_custom_command(TARGET my-program POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${resources} $<TARGET_FILE_DIR:my-program>)

などと記述すれば良い。

オイラー角による回転行列の表現まとめ

免責・注意:本記事は、その正しさを一切保証しない。できるだけ出典を記すようにするが、特に、要出典・要確認等と書かれただけの箇所は注意が必要である。また、現時点では未完成で、随時追記・更新していくつもりである。

右手系・左手系について

座標系を考えるとき、右手系と左手系のどちらかを用いることになる。本記事では右手系のことしか考えない。左手系について知りたい人は、以下の内容を全て自分で変換し直して考えるか、別の記事を当たる必要がある。

右手系の採用例

左手系の採用例

世界座標系とモデル座標系のどちらの座標系を基準とみるか:Extrinsic (Fixed) vs. Intrinsic (Mobile)

本記事では extrinsic (fixed) なオイラー角表現のことしか考えない。Intrinsic (mobile) なオイラー角表現を暗黙的に採用しているシステムもあるので注意が必要である。

Extrinsic (fixed) な表現では世界座標系 (world coordinates) の座標軸を基準に回転することを考える。

Extrinsic rotations are elemental rotations that occur about the axes of the fixed coordinate system xyz. The XYZ system rotates, while xyz is fixed.

引用元:Wikipedia. Euler angles. https://en.wikipedia.org/wiki/Euler_angles

一方で、intrinsic (mobile) な表現ではモデル座標系 (model coordinates; local coordinates) の座標軸を基準に回転することを考える。そのため、回転を適用する度に次に回転する際の基準軸が世界座標系から見たときに変わる点で注意が必要である。

Intrinsic rotations are elemental rotations that occur about the axes of a coordinate system XYZ attached to a moving body.

引用元:Wikipedia. Euler angles. https://en.wikipedia.org/wiki/Euler_angles

Extrinsic (fixed) でも intrinsic (mobile) でも、どちらも同等な回転を表現することができる。

For example, the fixed XYZ Euler angle convention is described by the x→y→z sequence, while the mobile ZYX Euler angle convention is described by the z'→y'→x' sequence, but both are equivalent, as we will see later.

引用元:How is orientation in space represented with Euler angles? https://www.mecademic.com/resources/Euler-angles/Euler-angles

また、extrinsic (fixed) な回転と intrinsic (mobile) な回転を混ぜて考えることも可能である。ただし実際にこれが有益になることは少ないと思われる。

That said, each of the twelve combinations is equivalent to three other sequences. In other words, each Euler angle convention can be described in four different ways. For example, the ZYX convention is equivalent to the sequences z→y→x, x'→y'→z', y→z'→x and y→x→z'.

引用元:Mecademic, Inc. How is orientation in space represented with Euler angles? https://www.mecademic.com/resources/Euler-angles/Euler-angles

Extrinsic (Fixed) Euler XYZ

世界座標系の  x 軸周りに  \theta_x 回転させ、続いて世界座標系の  y 軸周りに  \theta_y 回転させ、続いて世界座標系の  z 軸周りに  \theta_z 回転させるような回転について、そのような記述の方法を extrinsic (fixed) Euler XYZ と表現したい。実際には単に Euler XYZ と呼ばれることも多いが、extrinsic と intrinsic の違いについて常に意識しておいた方が安全である。

そのような回転は以下の回転行列で与えられる。

\displaystyle{
\mathbf{R} = \mathbf{R}_z(\theta_z) \mathbf{R}_y(\theta_y) \mathbf{R}_x(\theta_x) \in \mathbb{R}^{3 \times 3}
}

あるモデル座標系の点  \mathbf{x}^\text{model} \in \mathbb{R}^{3 \times 1} を、上のような extrinsic (fixed) Euler XYZ によって世界座標系に配置する際には、世界座標系上での座標  \mathbf{x}^\text{world} \in \mathbb{R}^{3 \times 1}

\displaystyle{
\mathbf{x}^\text{world} = \mathbf{R}_z(\theta_z) \mathbf{R}_y(\theta_y) \mathbf{R}_x(\theta_x) \mathbf{x}^\text{model}
}

で与えられる。

XYZ とは回転行列を適用する順番を表現している。OpenGL の Legacy APIs では post-multiplication が採用されているため、記述するときには

glRotated(theta_z, 0.0, 0.0, 1.0);
glRotated(theta_y, 0.0, 1.0, 0.0);
glRotated(theta_x, 1.0, 0.0, 0.0);

という順番になると思われる(TODO:要確認)。

Extrinsic (Fixed) Euler XYZ の採用例

  • Maya のデフォルト(TODO:要出典)

オイラー角を三次元ベクトルで表現する際の注意

変数の格納順に注意が必要である。名前が XYZ となっていても、

vec3 euler_angles = { theta_x, theta_y, theta_z };

のような順序で変数を格納するのが必ずしも唯一の方法ではない。例えば、回転行列の掛け算の式に現れる順に

vec3 euler_angles = { theta_z, theta_y, theta_x };

と変数を格納する実装もあり得る。これは Eigen の eulerAngles() で採用されている(TODO:要確認)。

ベクトルによる微分のレイアウト(分子レイアウト記法と分母レイアウト記法)

2つの異なる記法と混乱

ベクトル(や行列)による微分には2つの異なるレイアウトが用いられる。

  • 分母レイアウト記法 (Denominator Layout)
  • 分子レイアウト記法 (Numerator Layout)

これらは演算結果が異なるため、意識して用いないとしばしば混乱の元となる。

以下では \mathbf{x} \in \mathbb{R}^{n \times 1} は縦ベクトルとし、y \in \mathbb{R}スカラーする。また、便宜上、ライプニッツの記法に則り「分子」は微分される関数の方、「分母」は微分する独立変数の方を表すことにする。

分母レイアウト記法 (Denominator Layout)

スカラー微分する際、分母が縦ベクトルなら結果も縦ベクトルになる。分母が横ベクトルなら結果も横ベクトルとなる。つまり、直感的には分母のベクトル(や行列)のレイアウトが結果にそのまま反映されると解釈できる。

\displaystyle{
\frac{\partial y}{\partial \mathbf{x}} = \begin{bmatrix} \frac{\partial y}{\partial x_1} \\ \vdots \\ \frac{\partial y}{\partial x_n} \end{bmatrix} \in \mathbb{R}^{n \times 1}
}

一方、スカラーによって微分する際は、分子が縦ベクトルなら結果は横ベクトルになる。分子が横ベクトルなら結果は縦ベクトルになる。

\displaystyle{
\frac{\partial \mathbf{x}}{\partial y} = \begin{bmatrix} \frac{\partial x_1}{\partial y} & \cdots & \frac{\partial x_n}{\partial y} \end{bmatrix} \in \mathbb{R}^{1 \times n}
}

分子レイアウト記法 (Numerator Layout)

スカラー微分する際、分母が縦ベクトルなら結果は横ベクトルになる。分母が横ベクトルなら結果は縦ベクトルになる。分母レイアウトとは転置したような関係となっている。

\displaystyle{
\frac{\partial y}{\partial \mathbf{x}} = \begin{bmatrix} \frac{\partial y}{\partial x_1} & \cdots & \frac{\partial y}{\partial x_n} \end{bmatrix} \in \mathbb{R}^{1 \times n}
}

一方、スカラーによって微分する際は、分子が縦ベクトルなら結果も縦ベクトルになる。分子が横ベクトルなら結果も横ベクトルになる。つまり、直感的には分子のベクトル(や行列)のレイアウトが結果にそのまま反映されると解釈できる。

\displaystyle{
\frac{\partial \mathbf{x}}{\partial y} = \begin{bmatrix} \frac{\partial x_1}{\partial y} \\ \vdots \\ \frac{\partial x_n}{\partial y} \end{bmatrix} \in \mathbb{R}^{n \times 1}
}

どちらを用いるべきか

分母レイアウト記法を目にする機会が多く感じるので、個人的にはこちらを推奨したい。

ただしヤコビ行列の定義は分子レイアウトを採用していることが多く感じるため、悩ましい。

関連リンク