iOS DC2020のトークにあった
CGAffineTransformはどう働いてるのか?〜Swiftエンジニアのための線形代数〜1
について、自分なりの理解も込めてこの場を借りてまとめてみました。
内容は、CGAffineTransform
を使用する際に、
アフィン変換の説明に用いられる行列の形式とAppleのDocumentationの形式の違いによって、
CGAffineTransformの**ed APIを使用した際に、期待した動きにならない事象について説明されていました。
###アフィン変換の説明に用いられる形式
元々の座標(x,y)に対して、前から行列を掛け合わせて変換後の座標を取得する
\begin{eqnarray}
\left(
\begin{array}{cccc}
x' \\
y' \\
1 \\
\end{array}
\right) = \left(
\begin{array}{cccc}
a & b & t_{ x }\\
c & d & t_{ y } \\
0 & 0 & 1 \\
\end{array}
\right)
\left(
\begin{array}{cccc}
x\\
y\\
1\\
\end{array}
\right)
\end{eqnarray}
x' = ax + by + t_{ x } \\
y' = cx + dy + t_{ y }
###AppleのCGAffineTransformのDocumentationの形式
元々の座標(x,y)に対して、後から行列を掛け合わせて変換後の座標を取得する
\begin{eqnarray}
\left(
\begin{array}{cccc}
x' & y' & 1\\
\end{array}
\right) = \left(
\begin{array}{cccc}
x & y & 1\\
\end{array}
\right)
\left(
\begin{array}{cccc}
a & b & 0\\
c & d & 0\\
t_{ x } & t_{ y } & 1 \\
\end{array}
\right)
\end{eqnarray}
x' = ax + cy + t_{ x } \\
y' = bx + dy + t_{ y }
この違いによって、生まれた誤解について説明されていました。
###今回アフィン変換を使って想定する変更
CGAffineTransformを使用して、次の順番でアフィン変換を行った場合を考えてみました。
1.xを0.5倍、yを1.5倍のスケールに変換する
2.軸を90度傾ける
###想定する変更をCGAffineTransformの**ed APIを使用して実際に動かしてみる
UIView.animate(withDuration: 0.2) {
self.view.transform = CGAffineTransform.identity.scaledBy(x: 0.5, y: 1.5)
.rotated(by: 90 * .pi / 180)
}
-
scaledBy(x:y:)
を使用してxを0.5倍、yを1.5倍のスケールに変換 -
rotated(by:)
を使用して軸を90度傾ける
という順番でアフィン変換をかけるように記述してみました。
期待した状態と結果が異なるのは、CGAffineTransfoormでは、後ろから元の座標に対して演算するので、
rotateの変換、scaleの変換と処理を並べた場合は、
scaleの演算→rotateの演算の順とコードで記述した順番と逆で演算が行われる為、期待した状態と異なってしまいます。
###紹介された concatenating(_:) を使用して動かしてみる
let scale = CGAffineTransform(scaleX: 0.5, y: 1.5)
let rotate = CGAffineTransform(rotationAngle: 90 * .pi / 180)
let transform = scale.concatenating(rotate)
UIView.animate(withDuration: 0.2) {
self.transView.transform = transform
}
concatenating(_:)
では期待した結果を得ることができました。
concatenating(_:)については、ドキュメントにて
Return Value
A new affine transformation matrix. That is, t’ = t1*t2.
と記載されています。2
なのでこの場合は rotateの変換
→scaleの演算
の順番でアフィン変換が行われる為、期待した状態になります。