2012-12-22 29 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) 
    }