iPhoneアプリ開発、その(67) 文字だって回転リベンジ

テーマ:
 というわけで、なんとなくCTMの仕組みがわかってきたのでおさらいで文字を半円上に配置してみる。

テン・シー・シー-fig.1
半径100ピクセルの半円で配置させるために、文字の描画位置をY座標-100.0としておく。

 X座標を-6としてるのは、だいたいこれくらいで文字の幅の中間くらいになるだろうという目分量。
 ほんらいは、文字幅を測ってその半分にするのが正しいんだろうけど、どうやって文字幅測定したらいいか不明。

テン・シー・シー-fig.2
で、次にCGContextTranslateCTMで原点を画面中央に移動。

 画面中央はdrawRectに渡されるrect変数から決定できる。

テン・シー・シー-fig.3
最後にCGContextRotateCTMで-90度回転。

 これで、スタート位置に着いたので、あとはCGContextRotateCTMを+20度なり+30度でループすれば半円で文字を描画することになる。
 今回は9回のループなので、以下の計算で指定しています。

CGContextRotateCTM(context, (180.0 / 9.0) / 360.0 * (M_PI * 2.0));


 で結果がこれ。

テン・シー・シー-fig.4

 CGContextSetShadowで影なんかつけてみたりして。引数の意味は各自で調査してください。私は適当に入れてます。影のずれと、広がり具合を指定できるっぽい。他に色とかも指定できるみたい。


CGContextSetShadow(context, CGSizeMake(10,10), 20.0);


 で、ここまでできたら、あとは選択されている値(たとえば2とする)だけ強調して表示できればいいわけですよ。色変えるのは簡単なんで、せっかくだしCTM使って文字を拡大してみようかと...
 ただしCTMは積み重なるタイプなんで、一度拡大すると、それ以後ずっと拡大になるわけで、それを防止するために、選択されている値の時だけ2倍にして、直後に0.5倍している。

if (inValue == 2)
CGContextScaleCTM(context, 2.0, 2.0);
CGContextSetShadow(context, CGSizeMake(10,10), 20.0);
[str drawAtPoint:CGPointMake(-6.0, -100.0) withFont:font];
if (inValue == 2)
CGContextScaleCTM(context, 0.5, 0.5);

 ちなみに、スケーリングだけだから、この手のやり方で通じるけど、もうちょっと複雑な変形だとどうすんのと思ったんだけど、アフィン変換なんだから逆行列を使えばいいじゃんと思った人。あんたすげ~よ。正解。ちゃんと用意されてます。興味がある人は調べてみてください。
 俺は、もう一つの方法を使います。

CGContextSaveGState
CGContextRestoreGState

てやつで、CGContextSaveGStateで現状のCTMを保存してくれて、CGContextRestoreGStateで復帰させてくれます。何にも考えずに使えて楽なんですが、線の太さや色なんかも保存、復帰してくれるので、そこんとこは注意が必要。
 この場合

if (inValue == 2) {
CGContextSaveGState(context);
CGContextScaleCTM(context, 2.0, 2.0);
}
CGContextSetShadow(context, CGSizeMake(10,10), 20.0);
[str drawAtPoint:CGPointMake(-6.0, -100.0) withFont:font];
if (inValue == 2)
CGContextRestoreGState(context);

こんな感じ。かけた倍数の逆の倍数をかけて戻す方法や逆行列は、かける値が2倍じゃなくもうちょっと複雑な値だとどうしても誤差が入るので、こっちの方がいいんじゃないかと思われ。

 で、実行するとこんな感じ。

テン・シー・シー-fig.5
ちゃんちゃん。

 まあ、この前からCTMに付き合ってる人はオチが読めたよね。そ~です、スケーリングした時点でY座標の-100.0もスケーリングされて-200.0になるわけですわ。
 以下次回!

------------
サンプルプロジェクト:helloCTM-4.zip
AD