6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CGAffineTransformを使ったアフィン変換で気をつけること

Posted at

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度傾ける

期待.gif

###想定する変更を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度傾ける

という順番でアフィン変換をかけるように記述してみました。

実行結果
実際.gif

期待した状態と結果が異なるのは、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
}

実行結果
別.gif

concatenating(_:)では期待した結果を得ることができました。
concatenating(_:)については、ドキュメントにて

Return Value
A new affine transformation matrix. That is, t’ = t1*t2.

と記載されています。2
なのでこの場合は rotateの変換scaleの演算の順番でアフィン変換が行われる為、期待した状態になります。

  1. https://fortee.jp/iosdc-japan-2020/proposal/95413b51-d63a-4b82-b4fc-af808da6fc78

  2. https://github.com/nicklockwood/SwiftFormat/blob/master/README.md#config-file

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?