NSAffineTransform メモ

Cocoa アプリケーションは Quartz を使って描画処理を行う。その際に、基本的には NSBezierPath を使って線を引く。座標変換したいときに自前で座標を計算してもよいが、NSAffineTransform というアフィン変換のためのクラスが用意されている。

アフィン変換そのものについては特にめずらしい処理ではないのだが、NSAffineTransform を実際に使うにあたってのメモを書いておく。

NSAffineTransform を現在のコンテキストに適用するには、set もしくは concat を使う。両者の違いは重要。set はコンテキストにすでに存在する変換行列を置き換える。一方で concat はコンテキストにある変換行列との積を計算して set する。

アフィン変換は行列計算なので、行列を累積することで複数の変換を適用でき、また、行列の逆行列を掛けることで変換処理をリセットできる。ちょうどスタックの PUSH と POP のような関係にある。NSAffineTransform では invert を使うことで逆行列を計算できる。

concat は PUSH 処理にあたり、そのときの行列の逆行列を concat するとそれは POP 処理にあたる。set はこうしたことが出来ないので、アフィン変換の履歴が必要な場合には使用できない。

コードで PUSH と POP の部分を書いてみると次のようになる。

NSAffineTransform *transform1 = [NSAffineTransform transform];
NSAffineTransform *transform2 = [NSAffineTransform transform];

[transform1 rotateByDegrees:45.0];       // 45.0度回転
[transform2 translateXBy:-10.0 yBy:-20.0]; // (-10, -20)移動

[transform2 concat];  // PUSH にあたる処理
[transform1 concat];  // PUSH にあたる処理

/** ここで何らかの描画処理 **/

[transform1 invert];
[transform1 concat];  // POP にあたる処理
[transform2 invert];
[transform2 concat];  // POP にあたる処理

この例では、二つのアフィン変換を行っている。まず原点を移動し、回転処理をする。描画が終了したら回転した座標を戻して、原点も戻している。こうすることで、これに続く描画処理はもとの座標系を使うことができる。