Lissajousの波形更新メソッド
波形を更新するメソッドupdateBezierPathは以下の様になります。 各発振器出力を取り出して、一方をX軸の値、もう一方をY軸の値として線を繋げていくとリサージュ図形ができ上がります。でき上がった図形をsetBezierPath:でセットして終了です。
コメントにも書きましたが、各発振器の出力波形が先に更新されている事が前提となっています。
//
// Lissajous.m
//
#pragma mark -
#pragma mark Update path
// OscillatorのbezierPathが先に更新されている事
- (void)updateBezierPath
{
int i;
NSPoint px,py;
NSBezierPath *path = [NSBezierPath bezierPath];
NSBezierPath *waveformX = [[self x] bezierPath];
NSBezierPath *waveformY = [[self y] bezierPath];
if([waveformX elementCount] != [waveformY elementCount]) return;
for(i=0;i<[[self resolution] intValue];i++)
{
[waveformX elementAtIndex:i associatedPoints:&px];
[waveformY elementAtIndex:i associatedPoints:&py];
if(i == 0)
[path moveToPoint:NSMakePoint(px.y,py.y)];
else
[path lineToPoint:NSMakePoint(px.y,py.y)];
}
[path closePath];
[self setBezierPath:path];
}
【失敗例】誰が出しているエラーメッセージなの?
X軸の波形とY軸の波形のelementCountを比較して、一致していなければ何もしないようにしていますが、これを省略するとresolutionを一度減らしてから増やす時に、コンソールに以下のメッセージが表示されます。
- _segmentIndexForElementIndex:: index (14) beyond bounds (14)
これはNSBezierPathが出すエラーメッセージの様です。例えばresolutionを14にしてから200にすると、二つのOscillatorに順番にupdateBezierPathメッセージが送られるため、waveformXが200角形、waveformYが14角形の状態でこのメソッドが呼ばれてから、waveformXが200角形、waveformYが200角形の状態でこのメソッドが呼ばれます。
waveformXが200角形、waveformYが14角形の状態では、0番目のエレメントから13番目のエレメントにアクセスしている時はよいのですが、14番目のエレメントにアクセスしようとすると配列の範囲外にアクセスする事になるので上記のエラーメッセージが表示されるわけです。
_segmentIndexForElementIndexというのはNSBezierPathが出しているエラーメッセージではないか?と気づいて、ようやくエラーの原因に気づきました。エラーメッセージにクラス名を含めてくれると助かるのですが…
波形の更新が必要になる時
まずresolutionのSetterを修正します。resolutionに値をセットしてから各発振器に出力波形を更新するようにメッセージを送っています。こうすればOscillatorがLissajousのupdateBezierPathを呼び出すので、その後LissajousのupdateBezierPathを呼び出す必要はありません。
NSSetのmakeObjectsPerformSelector:は便利なメソッドで、NSSetの中に入っている各オブジェクトにセレクタで指定したメッセージを送る事ができます。ループを回すよりもコンパクトに記述できます。
//
// Lissajous.m
//
- (void)setResolution:(NSNumber *)value
{
[self setValue:value forKey:@"resolution" action:@"setResolution"];
[[self mutableSetValueForKey:@"oscillators"] makeObjectsPerformSelector:@selector(updateBezierPath)];
}
OscillatorのupdateBezierPathを修正する
ただし初期化コードが呼びだされる順番が関係する為か、lissajous関連からresolutionを取得できない場合がありました。そこでそのような場合は固定値200とする様にしてあります。
waveform属性を削除して、PathHolderのbezierPath属性を使う事にしたので、このメソッド名もupdateBezierPathに変更します。
Setterと初期化メソッドでupdateWaveformを呼び出しているので、メソッド名を修正します。
//
// OscillatorCD.m
// RepeatingMotifGenerator
//
- (void)updateBezierPath
{
int i,resolution;
double output,normalizedPhase;
NSBezierPath *path = [NSBezierPath bezierPath];
Lissajous *lissajous = [self lissajous];
if(lissajous == nil)
resolution = 200;
else
resolution = [[lissajous resolution] intValue];
for(i=0;i<resolution;i++)
{
normalizedPhase = i/(double)resolution;
output = [self output:normalizedPhase];
if(i == 0)
[path moveToPoint:NSMakePoint(i,output)];
else
[path lineToPoint:NSMakePoint(i,output)];
}
[self setBezierPath:path];
[lissajous updateBezierPath];
}
- (void)setAmplitude:(NSNumber *)value
{
[self setValue:value forKey:@"amplitude" action:@"setAmplitude"];
[self updateBezierPath];
}
- (void)setFrequency:(NSNumber *)value
{
[self setValue:value forKey:@"frequency" action:@"setFrequency"];
[self updateBezierPath];
}
- (void)setPhaseLag:(NSNumber *)value
{
[self setValue:value forKey:@"phaseLag" action:@"setPhaseLag"];
[self updateBezierPath];
}
- (void)commonAwake
{
[self updateBezierPath];
}
OscillatorCDのインターフェイスファイルからupdateWaveformメソッドの宣言を削除します。
//
// OscillatorCD.h
// RepeatingMotifGenerator
//
// Copyright 2008 NovemberKou. All rights reserved.
//
#import <CoreData/CoreData.h>
#import "PathHolder.h"
@class Lissajous;
@interface OscillatorCD : PathHolder
{
}
- (void)updateWaveform;
- (NSNumber *)amplitude;
- (void)setAmplitude:(NSNumber *)value;
- (NSNumber *)frequency;
- (void)setFrequency:(NSNumber *)value;
- (NSNumber *)phaseLag;
- (void)setPhaseLag:(NSNumber *)value;
- (Lissajous *)lissajous;
- (void)setLissajous:(Lissajous *)value;
@end
PathHolderのインターフェイスファイルにupdateBezierPathメソッドの宣言を追加します。
//
// PathHolder.h
// RepeatingMotifGenerator
//
// Copyright 2008 NovemberKou. All rights reserved.
//
#import <CoreData/CoreData.h>
#import "RMGRoot.h"
@interface PathHolder : RMGRoot
{
}
- (void)updateBezierPath;
- (NSBezierPath *)bezierPath;
- (void)setBezierPath:(NSBezierPath *)value;
@end
PathHolderにはサブクラスでオーバーライドされる事を前提として、空のupdateBezierPathメソッドを実装しておきます。PathHolderはこのメソッドとbezierPath属性のアクセサメソッドしかないので、ここで全て載せておきます。
//
// PathHolder.m
// RepeatingMotifGenerator
//
// Copyright 2008 NovemberKou. All rights reserved.
//
#import "PathHolder.h"
@implementation PathHolder
#pragma mark -
#pragma mark Update path
- (void)updateBezierPath
{
}
#pragma mark -
#pragma mark Accessor methods
- (NSBezierPath *)bezierPath {return VFK("bezierPath");}
- (void)setBezierPath:(NSBezierPath *)value {[self setValue:value forKey:@"bezierPath" action:nil];}
@end

ホーム
前へ