![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ||
![]() | ![]() | ![]() | ![]() | ![]() |
|
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
以下は、Mac OS X 10.5.5 上の Xcode 3.1 で説明しています。(10.4.11 の Xcode 2.5 からの変更も注記しています。)
| 1 README.rtf の日本語訳 2 アプリケーション 3 ファイル構成 4 MainMenu.nib |
5 BezierView クラス5.1 BezierView の変数と型5.2 BezierView のメソッド5.3 drawRect: メソッド6 機能追加のアイデア |
BezierPathLab は、NSBezierPath の機能のいくつかを実演する単純なサンプルです。NSBezierPath のさまざまな機能は、異なる線パターンや線端スタイルを作成するために使われます。NSGraphicsContext と NSAffineTransform がパスを回転するために使われます。
パスの属性を設定するために、ボタン、スライダー、カラーウェルが使われます。これらのアクションは、NSView のサブクラスである BezierView に送られ、これがベジエパスの属性のすべてを管理します。このサブクラスは、さまざまな設定をパスに適用し、ビューにパスを描画するために、-drawRect: メソッドをオーバーライドします。
まず、Xcode 上でビルドして実行してみます。アプリケーションを起動すると、次のようなウインドウが表示されます。
![]() |
| 起動直後 |
ビューの下側や右側のコントロールを動かすと、その設定に従ってビューのなかのベジエパスが再描画されます。下のカラーウェルは、線色、塗りつぶしの色、背景を設定します。その下のスライダーで回転・ズーム・線の太さを変えられます。右側は上からパスの形を選ぶラジオボタン、塗りつぶしをするかどうかのチェックボックス、線端の形を設定するラジオボタン、破線のタイプを選ぶラジオボタンです
プロジェクトを構成しているファイルのうち、ここで注目するのは、MainMenu.nib、BezierView.h と .m です。
MainMenu.nib は次のようになっています。
![]() |
| MaiinMenu.nib 内の Window |
左上のビューはカスタムビューで、NSBezierView クラスのインスタンスです。各コントロールのアクションは、このビューへ接続されています。このクラスのアウトレットは、lineColorWell(線色ウェル)、fillColorWell(塗りつぶし色ウェル)、backgroundColorWell(背景色ウェル)がビューの下の3つのカラーウェルに、angleSlider(角度スライダー)、zoomSlider(倍率スライダー)、lineWidthSlider(線幅スライダー)がその下の3つのスライダーに、pathTypeMatrix(パスタイプのマトリックス)、filledBox(塗りつぶしボックス)、capStyleMatrix(線端スタイルのマトリックス)、lineTypeMatrix(線幅タイプのマトリックス)が右側のラジオボタンとチェックボックスに接続されています。
NSBezierView のアクションには、ビュー下のカラーウェルから changeLineColor:(線色変更)、changeFillColor:(塗りつぶし色変更)、changeBackgroundColor:(背景色変更)、その下のスライダーから changeAngleSlider:(角度スライダー変更)、changeZoomSlider:(倍率スライダー変更)、changeLineWidth:(線幅スライダー変更)、右側のラジオボタンやチェックボックスから setPathType:(パスタイプ設定)、setFilled:(塗りつぶし設定)、setCapStyle:(線端スタイル設定)、changeLineType:(線種設定)にそれぞれ接続されています。
BezierView クラスBezierView の変数と型唯一の新しいクラスである BezierView を見ていきましょう。このアプリケーションの機能は、実質的にこのクラスで実装されます。まずヘッダファイルには、アウトレット以外に次のようなインスタンス変数が定義されています。
lineColor、fillColor、backgroundColor は、線色、塗りつぶし色、背景色に対応する NSColor クラスです。path は描画されるパスです。lineWith、angle は線の太さと回転角度です。pathType はパスの形の種類を表し、次のような列挙型で指定されます。
capStyle は、線端の形状で、次のような列挙型で指定されます。『Cocoa 描画ガイド』>「パス」>「NSBezierPath クラス」>「パスの属性」>「線の端点スタイル」で説明されています。
![]() |
| 線端スタイル |
filled は塗りつぶしするかどうかを表す BOOL 型変数です。
dashArray[3] は破線を定義する配列で、dashCount はその要素数です。破線の設定については、『Cocoa 描画ガイド』>「パス」>「NSBezierPath クラス」>「パスの属性」>「破線スタイル」で説明されています。配列の各要素が交互に、実線、間隔の長さを表しています。
このサンプルを実行してみるとわかりますが、配列要素自体は実線と間隔を区別せず繰り返されることに注意してください。配列要素の数が奇数の場合、たとえば、8-3-8 だと、実線8、間隔3、実線8 とですが、この次にすぐ実線 8 が続いて実線 16 となるわけではなく、次は間隔8、実線3、間隔8 となります。あくまで、最初が実線設定から始まるというだけです。
![]() |
| 8-3-8 の設定の場合の破線 |
BezierView のメソッド基本的にこのクラスのメソッドは、ほとんどがクラスのインスタンス変数を設定するものです。drawRect: メソッドで描画する際に、それらの変数が参照されることになります。ただし、拡大縮小に関しては、フレームのサイズ自体を拡大するため、インスタンス変数だけではなく、フレームのサイズも変更しています。
set... 系メソッドは直接インスタンス変数を設定します。change... 系メソッドは、各種のコントロールから呼ばれ、set... 系メソッドを使って設定を変更し、自身(changeZoomSlider: は上位ビュー)に再表示を行うように指定します。change... 系メソッドのうち、破線スタイルを設定する changeLineType: だけは、set... 系メソッドを介さずに直接設定を行っています。
set... 系メソッドでは、引数としてオブジェクトをとるものがあり、それらは引数が nil でないか調べて、そうでないなら元の変数をまず解放し、それから引数オブジェクトを copyWithZone:[self zone] でコピーしてから代入していることに注意してください。
set... 系メソッドで特別なのは、setZoom: です。これは全体の倍率を操作するため、少し複雑になっています。
MainMenu.nib で、倍率用のスライダーを調べると、1.0 〜 5.0 の範囲で動くようになっています。この数が引数として渡されることになります。バウンズの幅と高さに倍率を掛けて、それをフレームに設定しています。その後でバウンズを元の大きさに戻しています。こうすることで、倍率が 1.0 より大きい場合、バウンズは最初のサイズのままで、それがより大きいフレームに収まるように拡大縮小されて表示されることになり、スクロールビューがフレームにしたがって調整されます。この調整がうまく機能するように、changeZoomSlider: では上位ビューに再表示を命じています。次にこのメソッドが呼ばれた時も、バウンズは最初のサイズのままなので、フレームは最初のサイズに倍率を掛けたサイズになります。
awakeFromNib は、nib ファイルが読み込まれインスタンス化される時に呼び出されるメソッドです。ここでは、すべてのインスタンス変数に対して初期値を設定しています。dealloc メソッドは、このインスタンス(BezierView)が割り当て解除される時、インスタンス変数のうち、オブジェクトとして保持されているものを解放しています。これは set... 系メソッドで保持されたものです。
drawRect: メソッドさて、サンプルの中心となるのは、drawRect: メソッドです。これは大まかに次のような構造をしています。
基本的に背景を描画したあと、パスの形状に応じてパスを作成し、回転などのアフィン変換を作成して、それを適用して、最後にパスを描くか塗りつぶすことになります。このとき、グラフィックスコンテクストを保存して最後に復元していることに注意してください。
バウンズの取得は、emptySpace とあわせて、パス全体の大きさを計算するために必要です。グラフィックコンキストは保存と復元を行うために取得されます。回転用と平行移動用のアフィン変換を別々に確保しています。transform は、一時的な恒等変換(変換した結果が同じ)を新しく作成します。
これは単純です。変数で保持している色を設定し、その色でバウンズ全体を塗りつぶします。そして線色を設定しています。
保存されている pathType によって、処理を分けています。ここでは、バウンズの縦横の大きさを比べて処理を分けています。emptySpace 分の余白ができるように計算されています。bezierPathWithRect: で長方形パスを作成しています。パスが作成されているだけであることに注意してください。まだ、パスに沿って線を引いたり、塗りつぶしは行われません。このように、パスはまず作っておき、それからそれを使って線を引いたり、塗りつぶしを行います。
他のタイプも似たような事を行っています。違いは、大きさの計算とベジエパスを得るメソッドです。
保存された capStyle の設定で処理を分け、setLineCapStyle: で設定を反映させます。
破線の設定があるなら、それを設定します。phase: は、配列で示されたもののどこから開始するかです。これを使えば、間隔から開始することが可能です。実線4、間隔2、実線6、間隔3 があるとすれば、4.0 を設定すれば、2 つめの間隔から始まります。この数は、設定した配列の数値の区切りでなく、任意の数を設定できます。2.0 とすれば、最初の実線の真ん中から始まります。
まずグラフィック状態を保存し、その後で変換を設定しています。その後、concat で現在の変換に追加して適用しています。
ここでパスを描画しています。stroke はパスに沿って線を描きます。次に塗りつぶしのチェックボックスがチェックされていれば、塗りつぶし色を設定し、path で塗りつぶしています。この順番に注意してください。これにより、線幅の半分まで塗りつぶされています。これを逆にして、ビルドして実行してください。ただし、線色は最初のほうで設定されているので、線を描く直前に移します。
この順番だと、背景が塗りつぶされてから、線幅いっぱいまで線が描かれます。このようにパスを描くときは順番に気をつけてください。
![]() | ![]() |
| 線の後に塗り | 塗りの後に線 |
最後にグラフィック状態を復元しています。これは変換を行って現在の変換行列に追加しているから、こうしているのでしょうが、その効果が私にはいまいちわかりません。Cocoa ではビューがグラフィック状態を管理しているため、上位ビューなどにもれる心配がないと思うのですが。複数スレッド使用時などはこうした方がいいのかもしれません。なんとなく、昔はこうしないと問題が起こったような気もしますが…。今はどうなんでしょう。そういうわけで、グラフィック状態の保存コードを除去してみましょう。[NSGraphicsContext saveGraphicsState]; と [currentContext restoreGraphicsState]; をコメントアウトして、ビルドして実行してみます。いろいろいじってみても、特に問題はなさそうです。
パスの属性は、まだ他にもあります。『Cocoa 描画ガイド』>「パス」>「NSBezierPath クラス」>「パスの属性」を見ると、このサンプルで扱われていないものには、線の結合スタイル (line join style)、線の平坦さ (flatness)、マイター限界 (miter limit)、ワインディングルール (winding rule) があります。コントロールやメソッドを追加して、これらの属性を変更するようにできます。ワインディングルールは複数パスの重なりが必要なので、このサンプルのままではダメです。複数パスを描くようなタイプを追加する必要があります。
また、『Cocoa 描画ガイド』>「より進んだ描画テクニック」>「描画パスへの影の追加」を参考にして、影 (shadow) をつけたり、影の設定を変更できるようにするのもおもしろいでしょう。
また、グラフィック状態の属性のうち、アンチエイリアス設定 (anti-aliasing setting) のオンオフも試せます。もっとも、この程度の描画ではあまり効果が確認できないかもしれません。
| 2007.11.11 | v10.4、Xcode 2.5 で解説作成(ずっと以前の自分用のメモを修正)。 |
| 2008.09.25 | v10.5.5、Xcode 3.1 用に解説更新。 |
管理人:神吉 秀典 E-mail: