2012-12-22 5 views
6

나는 몇 시간 동안 해답을 찾고 있었지만 그 주제에 관해서는 아무 것도 찾지 못했다.UIBezierPath intersect

나는 Objective-c와 관련된 질문이 있습니다. UIView에서 사용자의 터치를 확인하고 사용자가 손가락을 터치하고 이동하면 UIBezierPath를 사용하는 경로가 그려지는 응용 프로그램을 만들고 있습니다. 사용자가 경로가 교차하도록 그리면 화면에서 사라집니다. 사용자가 패턴 그리기를 마쳤 으면 경로의 마지막 점에서 선이 경로의 첫 번째 점과 자동으로 연결됩니다 (이 경우 "closePath"메서드 사용).이 선이 다른 선과 교차하면 "경로에서 경로가 화면에서 사라져야합니다.

내가 가리키는 다른 클래스에 저장하는 CGPoint의 모든 점을 점 A와 점 B로 저장합니다. 그런 다음 "선"을 "선"이라는 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; } 

하면 세그먼트가 교차하는 라인의 경우에만 내부 부분을 확인합니다. 그러나 첫 번째 선분 의 시작 또는 끝 점이과 같으면 두 번째 선분의 시작 또는 끝 점 uaub0.0 또는 1.0이됩니다.

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

이 내 테스트 프로그램에서 예상대로 작동 듯 :

이 솔루션은 상태에서 간격의 한쪽 끝을 포함하는 것입니다.

일부 추가 발언 :

  • 나는 당신이 0으로 분열을 피하기 위해 다시 바로 가기

    if (denominator == 0.0f) return NO; 
    

    을 활성화한다고 생각합니다.

  • touchesMoved에서 교차로를 확인한 후 에 새 줄을 추가 할 수 있습니다. 이제 새 줄이 먼저 삽입됩니다. 즉, 교차점에 대해 자체 검사가 수행됩니다.

  • UIView의 하위 클래스로 Line을 선언했지만 실제로는 뷰 클래스가 아닙니다. LineNSObject의 하위 클래스로 선언 할 수 있습니다.


ADDED :이 때문에 작은 분모 가능한 오버 플로우 문제 분할을 방지하고 있기 때문에 다음의 방법은 더 나은 작동 할 수 있습니다 : 나는 또 다른 해결 방법을 발견했습니다

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