ページ

2008年4月21日月曜日

RubberBand(その6)拡大縮小処理

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Knob をつかんだ時の処理を加える。



ソース:RubberBand-3.zip

さてどう実装するか。Knobはつかんだ位置によって処理が微妙に異なる。

例えば左上のKnobを左上方向へ移動させた場合、Origin の移動+ Sizeの拡大となる。


一方、右下の Knobを右下へ移動すると Originは動かず、Sizeの拡大だけとなる。



移動量を dx, dy とすると、最初のケースは次のように表せる。

 x' = x + dx
y' = y + dy
w' = w - dx
h' = h - dy

このケースでは dx < 0 dy < 0

 x' = x + 0
y' = y + 0
w' = w + dx
h' = h + dy

このケースでは dx > 0, dy > 0。

実はこれらは行列として表せる。


2番目はこう。


これはアフィン変換と呼ばれる行列演算。
Knobの位置毎に Origin と Size の行列が定義できるから、これをパラメータ化すれば処理自体は1種類で済む。dx と dy は可変だが、行列で必要なのはその符号なのでパラメータとしては -1, 0, 1 として表せる。プログラムではパラメータを構造体で定義する。
typedef struct _ResizeRule {
CGFloat x, y, w, h;
} ResizeRule;


Knob毎のパラメータを用意してやる。
ResizeRule rules[8] = {
{1, 1,-1,-1}, // TOP_LEFT
{0, 1, 0,-1}, // TOP_MIDDLE
{0, 1, 1,-1}, // TOP_RIGHT
{1, 0,-1, 0}, // MIDDLE_LEFT
{0, 0, 1, 0}, // MIDDLE_RIGHT
{1, 0,-1, 1}, // BOTTOM_LEFT
{0, 0, 0, 1}, // BOTTOM_MIDDLE
{0, 0, 1, 1} // BOTTOM_RIGHT
};


rules のインデックス値と、Knobのenum値を合わせておくのを忘れずに。

パラメータが準備できたので後はこれを使う処理を実装するだけ。
- (void)mouseDown:(NSEvent*)event
{
NSPoint pp, cp;
cp = [self convertPoint:[event locationInWindow] fromView:nil];
int knob_type = [self knobAtPoint:cp];
CGFloat dx, dy;

if (knob_type != KNOB_NON) {
while ([event type] != NSLeftMouseUp) {
pp = cp;
event = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
cp = [self convertPoint:[event locationInWindow] fromView:nil];
dx = cp.x - pp.x;
dy = cp.y - pp.y;

ResizeRule rule = rules[knob_type];

_rect.origin.x += dx * rule.x;
_rect.origin.y += dy * rule.y;
_rect.size.width += dx * rule.w;
_rect.size.height += dy * rule.h;

[self setNeedsDisplay:YES];
}
}
}

これだけ。ずいぶんスッキリと実装できた。

- - - - -
Sketch より短くできた、なんて喜んでいたら、いくつか問題が見つかった。
次回はこれを直そう。