- Modification -
ベジエ曲線ツールのドローオブジェクトの編集の実装

■ ベジエ曲線ツールのドローオブジェクトの編集に必要なもの
ベジエ曲線ツールの編集も、ポリゴンツールの編集のときと大きくは変わらない。ダブルクリックすると編集モードに入る。で、アンカーポイントを選択して動かせるんだ。
ポリゴンツールと違ってやっかいなのは、1 つはコントロールポイントの取り扱い。もう 1 つは、パスが閉じているときの処理だ。パスが閉じているときは、最初のアンカーポイントと、最後のアンカーポイントが重なっているので、これを両方とも動かしてやらないといけないとこに注意。
実際の実装は 2 つのメソッドをオーバーライドする。アンカーポイントとコントロールポイントを動かすための、editWithEvent:inView:。もう 1 つは、ポイントを削除するための deleteForEditingInView: だ。
■ ベジエ曲線ツールのドローオブジェクトの編集の実装
編集のためには editWithEvent:inView: をオーバーライドする。ここでの処理は、まずクリックされた点が何かを決めないといけない。アンカーポイントなのか?コントロールポイントなのか?それとも何もない?
|
Sketch/DocumentModel.subproj/SKTBezierPath.m (created by mkino)
|
- (BOOL)editWithEvent:(NSEvent*)event inView:(SKTGraphicView*)view {
NSPoint point = [view convertPoint:[event locationInWindow] fromView:nil];
...
int pointIndex = [self pointUnderPoint:point];
...
if (pointIndex < 0) {
return NO;
}
if ([self isAnchorPoint:pointIndex]) {
_selectedPointIndex = pointIndex;
}
else {
if (_selectedPointIndex < 0) {
return NO;
}
}
...
|
まず pointUnderPoint: で、クリックされたポイントを調べる。ポイントがなければ、おしまい。
アンカーポイントをクリックしたら、そのアンカーポイントを選択する。もしアンカーポイントが選択されているときに、その前後のコントロールポイントがクリックされたら、そのポイントをハンドルするんだ。
そして、マウスがドラッグされたら、ポイントを動かすよ。
|
Sketch/DocumentModel.subproj/SKTBezierPath.m (created by mkino)
|
- (BOOL)editWithEvent:(NSEvent*)event inView:(SKTGraphicView*)view {
...
while (1) {
event = [[view window] nextEventMatchingMask:
(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
if ([event type] == NSLeftMouseDragged) {
point = [view convertPoint:[event locationInWindow] fromView:nil];
if (snapsToGrid) {
point.x = floor((point.x / spacing) + 0.5) * spacing;
point.y = floor((point.y / spacing) + 0.5) * spacing;
}
[self movePointAtIndex:pointIndex toPoint:point];
if ([self isAnchorPoint:pointIndex]) {
int index = [self indexOfLastAnchorPoint];
diffSize.width = point.x - oldPoint.x;
diffSize.height = point.y - oldPoint.y;
if (_isPathClosed &&
(pointIndex == 0 || pointIndex == index))
{
[self movePointAtIndex:0 toPoint:point];
[self movePointAtIndex:index toPoint:point];
[self movePointAtIndex:1 bySize:diffSize];
[self movePointAtIndex:index - 1 bySize:diffSize];
}
else {
if (pointIndex > 0) {
[self movePointAtIndex:pointIndex - 1 bySize:diffSize];
}
if (pointIndex < index) {
[self movePointAtIndex:pointIndex + 1 bySize:diffSize];
}
}
}
}
...
|
基本的には、マウスが動いたところに選択されたポイントを動かすだけだ。ただし、考慮しないといけないケースがいくつかある。まず、アンカーポイントをドラッグしたとき。そのときは、両端のコントロールポイントもいっしょに動かすんだ。そこで、ポイントを動かす差分を求めて、コントロールポイントも動かしてやる。
さらに気をつけないといけないのは、アンカーポイントが最初か最後のとき。この場合は、最初と最後をいっしょに動かしてやらないといけない。その場合の処理も必要なんだ。
■ アンカーポイントの削除の実装
続いて、アンカーポイントの削除を。deleteForEditingInView: で行う。
|
Sketch/DocumentModel.subproj/SKTPolygon.m (created by mkino)
|
- (void)deleteForEditingInView:(SKTGraphicView*)view {
NSRect drawingBounds;
if (_selectedPointIndex != -1) {
// If the points is last one
if ([_points count] == 1) {
[[self document] removeGraphic:self];
return;
}
drawingBounds = [self drawingBounds];
[self removeAnchorPointAtIndex:_selectedPointIndex];
_selectedPointIndex = -1;
[self setBounds:[[self origBezierPath] bounds]];
drawingBounds = NSUnionRect(drawingBounds, [self drawingBounds]);
[view setNeedsDisplayInRect:drawingBounds];
}
}
|
このメソッドは、あまりポリゴンのものと変わらない。ベジエ曲線特有の処理は、removeAnchorPointAtIndex: で行われているんだ。これは、指定したアンカーポイントと、その前後のコントロールポイントを削除するためのメソッドだぜ。
|
Sketch/DocumentModel.subproj/SKTPolygon.m (created by mkino)
|
- (void)removeAnchorPointAtIndex:(int)index {
int lastIndex;
if (![self isAnchorPoint:index]) {
return;
}
lastIndex = [self indexOfLastAnchorPoint];
if (index == 0 || index == lastIndex) {
if(_isPathClosed) {
[_points replaceObjectAtIndex:lastIndex
withObject:[_points objectAtIndex:3]];
[_points replaceObjectAtIndex:lastIndex - 1
withObject:[_points objectAtIndex:2]];
[_points removeObjectsInRange:NSMakeRange(0, 3)];
}
else {
if (index == 0) {
[_points removeObjectsInRange:NSMakeRange(0, 3)];
}
else if (index == lastIndex) {
[_points removeObjectsInRange:NSMakeRange(lastIndex - 2, 3)];
}
}
}
else {
[_points removeObjectsInRange:NSMakeRange(index - 1, 3)];
}
}
|
このメソッドでは、指定されたポイントとその前後 1 つずつのポイントを削除しているんだ。ただし、いくつかのケースがある。普通の場合、つまり弧の途中のアンカーポイントを削除するときは、普通に 3 つ消せばいい。
まず、弧が閉じていない場合。さらに、その両端を削除する場合。このときは、アンカーポイントと、前後ではなくて、線の先にある 2 つのコントロールポイントを削除する必要があるんだ。
そして、弧が閉じていて、最初か最後のアンカーポイントを削除するとき。このときは、最初のアンカーポイントを削除して、かつ最後のアンカーポイントを 2 つめのアンカーポイントのところまで、移動してやる必要があるんだ。なかなかめんどくさいねー。
|