太さの変わるBezier曲線の生成 - その24 [考え中 - 太さの変わるBezier曲線]
先週の続き。前回までNSBezierPathの復習を軽くやった。NSBezierPathのelementAtIndex:associatedPoints:メソッドを使って自分の端点座標を順にとってきて、それに中間制御点を追加してAGG方式のSmoothingを行うことにした。
具体的なメソッドの中身
もう少し詳しく考えてみる。
- 端点の位置をサブパスごとに取り出して保持する
- 制御点を削除する
- Smoothingしながらサブパスを追加する
つまりNSBezierPathのカテゴリのメソッドは
@interface NSBezierPath (DCNSBezierPathSmoothing) - (void)smoothingWithKValue:(float)kvalue; @end @implementation NSBezierPath (DCNSBezierPathSmoothing) - (void)smoothingWithKValue:(float)kvalue { NSArray *subpaths = [self createSubPathArray]; // (1) [self removeAllPoints]; // (2) [self rebuildUsing:subpaths withKValue:kvalue]; // (3) } @endとなる。(1)でDCAnchorsOfSubPathのインスタンスの配列を作る。(2)で潔く制御点を全部削除する。そして(3)でDCAnchorsOfSubPathのインスタンスの配列から新しく制御点を作る。
端点座標を保持するクラス
サブパスの端点位置の保持のために小さなクラスを用意することにする。
@interface DCAnchorsOfSubPath : NSObject { NSMutableArray *anchors; // array of NSValue of NSPoint BOOL isClosed; }anchorsは端点の位置の配列で、isClosedはそのサブパスが閉じているかどうかを保持する。
このクラスはメソッドとして
- (void)appendAnchorOfElement:(NSBezierPathElement)type withPoints:(NSPointArray)points; - (void)addSmoothedSubPathTo:(NSBezierPath *)path withKValue:(float)kvalue;を持つ。 ひとつめはNSBezierPathから抜き出したエレメントを使って端点の座標だけを取り出す。呼ぶ方はエレメントがなくなるまでこの呼び出しを繰り返す。
ふたつめのメソッドは保持した端点からAGG方式の制御点を計算しながら引数のpathにcurveToPoint:で追加していく。
サブパスのはじめや終わりがこのままでは呼び出し側が判断しないといけないので
typedef enum { DCNeedToStartNewSubPath, DCShouldAppendPoint, DCEndOfSubPath, DCDontKnowHowToDo } DCElementTypeForSubPath; + (DCElementTypeForSubPath)typeForSubPath:(NSBezierPathElement)element;というのを用意しておく。引数のNSBezierPathElementの値に従って
- サブパスが始まった(ので新しいDCAnchorsOfSubPathのオブジェクトを作れ)
- appendAnchorOfElement:withPoints:を呼んで追加しろ
- サブパスが終わった
これを呼び出す側のNSBezierPathのカテゴリのプライベートメソッドは
- (NSArray *)createSubPathArray { NSMutableArray *subpaths = [NSMutableArray array]; // (0) int num = [self elementCount]; int n; NSPoint points[3]; DCAnchorsOfSubPath *subpath = nil; for (n = 0 ; n < num ; n ++) { int type = [self elementAtIndex:n associatedPoints:points]; // (1) int anchorType = [DCAnchorsOfSubPath typeForSubPath:type]; // (2) if (anchorType == DCNeedToStartNewSubPath) { // (3) if (subpath != nil) [subpaths addObject:subpath]; subpath = [DCAnchorsOfSubPath subpath]; } [subpath appendAnchorOfElement:type withPoints:points]; // (4) if (anchorType == DCEndOfSubPath) { // (5) [subpaths addObject:subpath]; subpath = nil; } } if (subpath != nil) [subpaths addObject:subpath]; // (6) return subpaths; }という決まりきった形になる。まず(0)でサブパスのオブジェクトを保持する配列を作る。そして順番にエレメントを取り出す。(1)でエレメントのNSBezierPathElementをしらべてDCAnchorsOfSubPathのDCElementTypeForSubPathに変換する。もしDCNeedToStartNewSubPathだったら新しいDCAnchorsOfSubPathのインスタンスを作る。すでにあったら古い方を配列に保持する。
(4)で制御点を加える。これはどんなタイプでも必要なので場合分けはない。
(5)で、もしDCEndOfSubPathだったら配列に保持する。DCEndOfSubPathはNSClosePathBezierPathElementだった場合に対応しているので、サブパスが閉じていない場合には出現しない。このへんはサブパスの最後はDCEndOfSubPathになってるようにしたほうがわかりやすいかもしれない。ということで(6)でもし保持のこりがあったら配列に入れて終わる。
2009-05-25 23:53
nice!(0)
コメント(0)
トラックバック(0)
コメント 0