- Modification -
ポリゴンツールのドローオブジェクトの移動の実装

■ ポリゴンツールのドローオブジェクトの移動に必要なもの
ここでは、ポリゴンを移動したり、大きさを変えることを考えてみよう。つまり、マウスで選択して動かしたり、ハンドルを動かしてサイズを変えるときのことね。
Sketch のいままでのオブジェクト、四角や円や円弧は、領域を決めるとそれだけで描くものが決まった。オブジェクトを一意に決定するのに、それしか情報が必要じゃなかったからね。だけど、ポリゴンの場合は、各点の情報がある。だから、オブジェクトを動かしたときは、それを全部動かさないといけないし、サイズを変えたら、点の間の比率を変えないといけない。そのために、アフィン変換を使おう。NSAffineTransform を使えば、それをカバーしてくれる。
というわけで、インスタンス変数に NSAffineTransform の変数を用意しておくことにしよう。あと今回は、オブジェクトのサイズを変えていって、反転したときは特別な処理が必要になる。それのためのフラグも用意しておこう。
|
Sketch/DocumentModel.subproj/SKTPolygon.h (created by mkino)
|
@interface SKTPolygon : SKTGraphic {
...
NSAffineTransform* _transform;
BOOL _isFlippedVertically;
BOOL _isFlippedHorizontally;
...
}
|
この 3 つの変数を使って、オブジェクトの移動と変形を実装するぞ!
■ ポリゴンツールのドローオブジェクトの移動の実装
ドローオブジェクトの移動に必要な実装は、bezierPath でおこなうことにした。流れは、
- アンカーポイントの配列から、ベジエ曲線を作る
- そのベジエ曲線を移動、変形する
っていう感じになるんだ。
◆ アンカーポイントの配列からベジエ曲線を作る
まず、もとになるベジエ曲線を作る。これは origBezierPath っていうメソッドでやらせることにした。
|
Sketch/DocumentModel.subproj/SKTPolygon.m (created by mkino)
|
- (NSBezierPath*)origBezierPath {
NSBezierPath* path = [NSBezierPath bezierPath];
int count = [_anchorPoints count];
int i;
if (count >= 1) {
[path moveToPoint:[self pointAtIndex:0]];
if (count >= 2) {
for (i = 1; i < [_anchorPoints count]; i++) {
[path lineToPoint:[self pointAtIndex:i]];
}
}
if (_isPathClosed) {
[path closePath];
}
}
[path setLineWidth:[self strokeLineWidth]];
return path;
}
|
基本は、アンカーポイントの配列から、順々に NSBzierPath の lineToPoint: をしていく、ってことだ。まず、アンカーポイントが 1 個あったら、そこに moveToPoint: する。さらにポイントがあるなら、lineToPoint: していく。パスを閉じるなら、closePath する。これで完了。
◆ ベジエ曲線を移動、変形する
で、origBezierPath でベジエ曲線を作ったら、それを移動変形してやるんだ。それは bezierPath でやっている。
|
Sketch/DocumentModel.subproj/SKTPolygon.m (created by mkino)
|
- (NSBezierPath*)bezierPath {
NSBezierPath* path = [self origBezierPath];
NSRect bounds = [self bounds];
NSRect bezierBounds = [path bounds];
float scaleX = 0.0f, scaleY = 0.0f;
if (![self isCreating] && ([self isMoving] || [self isSizeChanging])) {
[_transform setTransformStruct:SKTNormalizeStruct];
// For moving
[_transform translateXBy:bounds.origin.x yBy:bounds.origin.y];
// For size changing
if (bezierBounds.size.width != 0) {
scaleX = bounds.size.width / bezierBounds.size.width;
}
if (bezierBounds.size.height != 0) {
scaleY = bounds.size.height / bezierBounds.size.height;
}
[_transform scaleXBy:scaleX yBy:scaleY];
// Back to origin
[_transform translateXBy:-bezierBounds.origin.x
yBy:-bezierBounds.origin.y];
// For vertical flip
if (_isFlippedVertically) {
[_transform prependTransform:SKTVerticalFlipTransform];
[_transform translateXBy:
-(2 * bezierBounds.origin.x + bezierBounds.size.width)
yBy:0.0];
}
// For horizontal flip
if (_isFlippedHorizontally) {
[_transform prependTransform:SKTHorizontalFlipTransform];
[_transform translateXBy:0.0
yBy:
-(2 * bezierBounds.origin.y + bezierBounds.size.height)];
}
}
return [_transform transformBezierPath:path];
}
|
ベジエ曲線の作成後、そのベジエ曲線の領域 bezierBounds と、移すべき領域 bounds とを求める。そして、bezierBounds から bounds へと転写するアフィン変換を求めてやるんだ。translateXBy:yBy: と scalXBy:yBy: とを使って求めることができるんだ。
あと、反転したときは、特別な処理が必要。反転用のアフィン変換をしてやる必要がある。そのための構造体を作って、それをかけてやることで実現しているんだ。垂直反転用の構造体は SKTVerticalFlipStruct = { -1, 0, 0, 1, 0, 0 }、水平反転用の構造体は SKTHorizontalFlipStruct = { 1, 0, 0, -1, 0, 0 } なんだ。
で、アフィン変換が決まったら、NSAffineTransform の transformBezierPath: を使って、ベジエ曲線を変形してやる。これで出来上がり。
|