2012-12-22 14 views
6

私は何時間も答えを探していましたが、トピックで何かを見つけるのは難しいです。UIBezierPath intersect

私はObjective-cに関する質問があります。私は、UIViewがユーザーからの接触をチェックするアプリケーションを作成しています。ユーザーが指で触れて移動すると、UIBezierPathを使用するパスが描画されます。ユーザーがパスを描くようにパスを描くと、パスはスクリーンから消えます。ユーザーがパターンの描画を完了すると、パス内の最後のポイントからのラインは自動的にパス内の最初のポイントに接続する必要があります(この場合、メソッド "closePath"を使用しています)。このラインが別の "ラインパス内ではパスも画面から消えます。

すべてのタッチポイントをCGPointに保存し、Lineという別のクラスにA点とB点として保存します。次に、 "lines"という名前のNSMutableArrayに "line"を保存します。ポイントがパスに追加されるたびに、そのポイントとその前に描画されたポイントとの間の線が、メソッド内のどの「線」と交差するかをチェックします( - (BOOL)checkLineIntersection:(CGPoint)p1 :(CGPoint)p2:(CGPoint)p3:(CGPoint)p4)このチュートリアルから得た "http://www.iossourcecode.com/2012/08/02/how-to-make-a-game-like-カットロープ部分2/"。

問題

問題は、私がアプリケーションを実行すると、それは時々動作しますが、私はそう描くとき時々ラインはパスが消えない交差するということです。なぜ私は理解できません...ゆっくりと描くと、もっと頻繁に起こるようです。

コード:

MyView.h:

#import <UIKit/UIKit.h> 
#import "Line.h" 
@interface MyView : UIView { 

NSMutableArray *pathArray; 
UIBezierPath *myPath; 
NSMutableArray *lines; 
Line *line; 
} 

@end 

MyView.m:

#import "MyView.h" 

@implementation MyView 


- (id)initWithFrame:(CGRect)frame 
{ 
self = [super initWithFrame:frame]; 
if (self) { 
    // Initialization code 
    pathArray=[[NSMutableArray alloc]init]; 

} 
return self; 
} 

- (void)drawRect:(CGRect)rect 
{ 
[[UIColor redColor] setStroke]; 
[[UIColor blueColor] setFill]; 

for (UIBezierPath *_path in pathArray) { 
    //[_path fill]; 

    [_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0]; 
} 
} 

#pragma mark - Touch Methods 
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
myPath = [[UIBezierPath alloc]init]; 
lines = [[NSMutableArray alloc]init]; 
myPath.lineWidth=1; 

UITouch *mytouch = [[event allTouches] anyObject]; 
[myPath moveToPoint:[mytouch locationInView:mytouch.view]]; 

[pathArray addObject:myPath]; 

} 

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{ 

if(myPath.isEmpty) { 

} else { 

    UITouch *mytouch = [[event allTouches] anyObject]; 
    [myPath addLineToPoint:[mytouch locationInView:mytouch.view]]; 

    CGPoint pointA = [mytouch previousLocationInView:mytouch.view]; 
    CGPoint pointB = [mytouch locationInView:mytouch.view]; 

    line = [[Line alloc]init]; 
    [line setPointA:pointA]; 
    [line setPointB:pointB]; 

    [lines addObject:line]; 

    for(Line *l in lines) { 

     CGPoint pa = l.pointA; 
     CGPoint pb = l.pointB; 

     //NSLog(@"Point A: %@", NSStringFromCGPoint(pa)); 
     //NSLog(@"Point B: %@", NSStringFromCGPoint(pb)); 

     if ([self checkLineIntersection:pointA :pointB :pa :pb]) 
     { 
      [pathArray removeLastObject]; 
      [myPath removeAllPoints]; 
      [self setNeedsDisplay]; 
      NSLog(@"Removed path!"); 
      return; 
     } 
    } 
} 
[self setNeedsDisplay]; 
} 

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
if(myPath.isEmpty) { 


} else if ([lines count] != 0){ 
    line = [[Line alloc]init]; 
    line = [lines lastObject]; 
    CGPoint pointA = line.pointA; 
    line = [[Line alloc]init]; 
    line = [lines objectAtIndex:0]; 
    CGPoint pointB = line.pointA; 

    [myPath closePath]; 
    for(Line *l in lines) { 

     CGPoint pa = l.pointA; 
     CGPoint pb = l.pointB; 

     if ([self checkLineIntersection:pointA :pointB :pa :pb]) 
     { 
      [pathArray removeLastObject]; 
      [myPath removeAllPoints]; 
      [self setNeedsDisplay]; 
      NSLog(@"Removed path!"); 
      return; 
     } 
    } 
} 
[self setNeedsDisplay]; 
} 

-(BOOL)checkLineIntersection:(CGPoint)p1 :(CGPoint)p2 :(CGPoint)p3 :(CGPoint)p4 
{ 
CGFloat denominator = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y); 

/* 
// In this case the lines are parallel so you assume they don't intersect 
if (denominator == 0.0f) 
    return NO; 
*/ 

CGFloat ua = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x))/denominator; 
CGFloat ub = ((p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x))/denominator; 

if (ua > 0.0 && ua < 1.0 && ub > 0.0 && ub < 1.0) 
{ 
    return YES; 
} 

return NO; 
} 


@end 

Line.h:

#import <UIKit/UIKit.h> 

@interface Line : UIView 

@property (nonatomic, assign) CGPoint pointA; 
@property (nonatomic, assign) CGPoint pointB; 

@end 

Line.m:

#import "Line.h" 

@implementation Line 

@synthesize pointA; 
@synthesize pointB; 

- (id)initWithFrame:(CGRect)frame 
{ 
self = [super initWithFrame:frame]; 
if (self) { 
    // Initialization code 
} 
return self; 
} 

/* 
// Only override drawRect: if you perform custom drawing. 
// An empty implementation adversely affects performance during animation. 
- (void)drawRect:(CGRect)rect 
{ 
// Drawing code 
} 
*/ 

@end 

私は、誰かがこれを答えることができるかもしれません願っています。申し訳ありませんが、それは明らかなものです。前もって感謝します!

答えて

8

問題はcheckLineIntersectionメソッドにあります。

if (ua > 0.0 && ua < 1.0 && ub > 0.0 && ub < 1.0) { return YES; } 

であなただけのセグメントが交差するラインの内側部分かどうかを確認します。しかし、最初の線分の開始点または終了点がに等しい場合、uaおよびubの開始点または終了点は0.0または1.0となります。

ソリューションは、状態の区間の一方の端を含めることです。

if (ua > 0.0 && ua <= 1.0 && ub > 0.0 && ub <= 1.0) { return YES; } 

これは私のテストプログラムで期待どおりに動作するように見えました。

さらにいくつかの発言:

  • 私はあなたがゼロ除算を避けるために再びショートカット

    if (denominator == 0.0f) return NO; 
    

    を活性化させるべきだと思います。

  • touchesMovedの場合、の交差点を確認した後、に新しい行を追加することができます。これで、新しい線が最初に挿入されます。つまり、交差点があるかどうかがチェックされます。

  • UIViewのサブクラスとしてLineと宣言しましたが、実際にはビュークラスではありません。 NSObjectのサブクラスとしてLineを宣言することができます。


を追加しました:それはそれゆえ、小さな分母で可能なオーバーフローの問題分裂を回避しているため、以下の方法は、より良い仕事かもしれません:私は別の回避策を見つけた

-(BOOL)checkLineIntersection:(CGPoint)p1 :(CGPoint)p2 :(CGPoint)p3 :(CGPoint)p4 
{ 
    CGFloat denominator = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y); 
    CGFloat ua = (p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x); 
    CGFloat ub = (p2.x - p1.x) * (p1.y - p3.y) - (p2.y - p1.y) * (p1.x - p3.x); 
    if (denominator < 0) { 
     ua = -ua; ub = -ub; denominator = -denominator; 
    } 
    return (ua > 0.0 && ua <= denominator && ub > 0.0 && ub <= denominator); 
} 
+0

ワンダフル答え!ありがとうございました。 – JesperWingardh

+0

@JesperWingardh:どうぞよろしくお願いいたします。 –

0

をその線が自己と交差するかどうかを確認します。 SceneKitフレームワークを使用すると、UIBezierPathからシェイプを作成することができます。しかし、パスが交差する場合、ノードのバウンディングボックスはゼロになります。

let path = UIBezierPath() 

    //... 

    let testGeometry = SCNShape(path:path, extrusionDepth: 0.5) 
    let testNode = SCNNode(geometry: testGeometry) 

    if (testNode.boundingBox.max - testNode.boundingBox.min).length() > 0 { 
    // No intersection (or empty) 
    }