2012-12-22 87 views
6

我一直在寻找一个小时的答案,但无法找到关于该主题的任何内容。UIBezierPath交叉

我有一个Objective-c相关的问题。我正在制作一个应用程序,其中UIView检查用户的触摸,并且如果用户触摸并移动他/她的手指,则会绘制使用UIBezierPath的路径。如果用户绘制路径使其自身相交,则它应该从屏幕上消失。当用户完成绘制模式时,路径中最后一个点的一条线应该自动与路径中的第一个点连接(我使用方法“closePath”进行此操作),如果此线与另一个“线”相交“在路径中,路径也应该从屏幕上消失。

我将CGPoint中存储的每个接触点存储在另一个名为Line的点中作为点A和点B.然后将“线”保存到名为“lines”的NSMutableArray中。每次将一个点添加到路径中时,我都会检查该点与其之前绘制的点之间的线是否与使用方法的线中的任何“线”相交( - (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; } 

您检查部段相交的线只有在内饰部分。但是,如果第一线段的起点或终点等于,则第二线段uaub的起点或终点将为0.01.0

的解决方案是包括在条件的区间的一端:

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

这似乎按预期在我的测试程序来工作。

一些进一步的说明:

  • 我想你应该重新激活快捷

    if (denominator == 0.0f) return NO; 
    

    零,以避免分裂。

  • touchesMoved中,您可以在检查交叉点之后将新行添加到数组。现在先插入新行,这意味着它会针对交叉点进行检查。

  • 您已声明Line作为UIView的子类,但这不是一个真正的视图类。您可以将Line声明为NSObject的子类。


新增:下面的方法可能效果更好,因为它避免了分裂,并因此与小分母可能溢出问题:

-(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) 
    }