Mac用プロットライブラリ-9 [考え中 - プロットライブラリ]
もっとサクサクと進めたい設計の続き。
それぞれのレイヤがどんなことをするのかを考える。
Automatic Layer
Automatic layerはその名の通りAutomatic optionを受け付ける唯一のレイヤになる。それ以下のレイヤではAutomatic optionは全部解決済みで、必要なものはPrimitiveに分解され、大きさが決まっていると考える。
値として「Automatic」を受けるoptionには
axes | 座標軸の描画 |
plotRange | 描画領域の指定 |
lineStype | 線のスタイル |
surfaceColor | 面の色づけ |
ViewPoint | 3次元の視点 |
なんかが考えられる。その他にもAutomaticを受けるoptionは後からでも増やせるようにしておく。Automatic layerでこれらのoptionを展開して具体的なprimitiveやその属性に変換する。座標軸なんかを構成するprimitiveは前回書いた「ornaments」オブジェクトの方に収納させることになる。
次のPrimitive layerを生成し変換済みのPrimitiveを渡す。
Primitive Layer
Automaticが解決されたPrimitiveをデータとして受け、描画領域を決定する。描画領域のAspectRatioを計算し、全体をNormalizeされた座標に変換する。primitive layerではすべての描画Objectは前述したprimitiveである。group以外はそれぞれ専用のデータ構造を持つが、groupの実体は(カテゴリで拡張された)NSArrayにとりあえずしておこう。
次のNormalized layerを生成し、NormalizeしたPrimitiveを渡す。
Normalized Layer
一旦Normalizeされた座標を実際のView座標にマップする。そのとき、もとのPlot座標とは無関係なText Objectのサイズや座標軸のtickの長さなどを考慮した上でマップしなければならないのでこれはずいぶん難しい。座標軸を描くか描かないかでPlot領域の大きさは変化する。また、Primitive Layerで計算したAspectRatioもこのレイヤで処理することになる。
次のView layerを生成し、View用に座標変換したprimitiveを渡す。
View Layer
実際に描画を行うレイヤである。渡されたprimitiveを
- NSBezierPath
- NSAttributedString
- NSImage
に展開し、その他の
- 影付け
- 透明度
- objectの重なり具合
などの処理をほどこしてNSViewに描画する。これはもう単にやるだけでめんどくさいけどそれほど難しくない。
3次元の場合
3次元の場合はもう少し複雑になってしまう。下の図のような感じ。
基本的には同じで2次元でやったことを3次元に拡張すると考えよう。そしてView3Dの代わりに射影変換を行い、2次元Primitiveに変換する。この方法だと3次元と2次元の2回Normalizeされることになり、無駄が多いけど、これの方がそれぞれのレイヤは単純化されるはず。
下位レイヤ生成の際の座標変換
下位レイヤを生成するときに座標変換が必要になる場合がある。それは
- Primitive Layer → Normalized Layer
- Normalized Layer → View Layer
の二通り。どちらの場合も座標変換は上位レイヤが責任を持つということにする。
例えば座標変換は以下のようなユーティリティクラスを使用することにしよう。こんな感じ。
@interface CPCoordinateTransform : NSObject { } - (id)initForNormalizedCoordinateFromRect:(CPRect)boundingBox; - (id)initForViewCoordinateToRect(NSRect)boundingBox; - (id)transform:(id)drawable; @end
-initForNormalizedCoordinateFromRect:はPrimitive LayerからNormalized Layerへの変換を準備する。boundingBoxは描画したい領域を渡す。一般にはAutomatic Layerで指定されたPlotRangeオプションに従う。例えばPlotRangeオプションがAllの場合、すべてのオブジェクトを含むような四角形を計算し、渡すことになる。
-initForViewCoordinateToRect:はNormalized LayerからView Layerの変換を準備する。boundingBoxはPrimitive LayerからNormalized Layerの場合と異なり、描画先のViewの領域を指定する。
boundingBoxは描画オブジェクトに対してboundingBoxを投げると再帰的に呼び出され、全体のboundingBoxが得られる。これは実装がどうなるか想像がつきやすいね。
CPRect rect; rect = [drawableObject boundingBox];
-transform:を呼ぶと渡した描画オブジェクトの座標が変換された新しいオブジェクトを生成して返す。
今回はずいぶん具体的だった。こんな設計のはじめの段階であまりコードをイメージするのは良くないかもしれないけど。
コメント 0