So-net無料ブログ作成

太さの変わるBezier曲線の生成 - その24 [考え中 - 太さの変わるBezier曲線]

先週の続き。前回までNSBezierPathの復習を軽くやった。NSBezierPathのelementAtIndex:associatedPoints:メソッドを使って自分の端点座標を順にとってきて、それに中間制御点を追加してAGG方式のSmoothingを行うことにした。

具体的なメソッドの中身

もう少し詳しく考えてみる。

  1. 端点の位置をサブパスごとに取り出して保持する
  2. 制御点を削除する
  3. 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の値に従って
  1. サブパスが始まった(ので新しいDCAnchorsOfSubPathのオブジェクトを作れ)
  2. appendAnchorOfElement:withPoints:を呼んで追加しろ
  3. サブパスが終わった
という意味だとする。呼び出し側はそれに従ってサブパスのオブジェクトを処理する。

これを呼び出す側の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)でもし保持のこりがあったら配列に入れて終わる。


nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0