2012-12-22 11 views
6

Ho cercato una risposta per ore ma ho avuto difficoltà a trovare qualcosa sull'argomento.UIBezierPath interseca

Ho una domanda relativa all'obiettivo-c. Sto facendo un'applicazione in cui un UIView controlla i tocchi da parte dell'utente e se l'utente tocca e muove il dito, viene disegnato un percorso usando UIBezierPath. Se l'utente disegna in modo che il percorso si intersechi, esso dovrebbe scomparire dallo schermo. Quando l'utente ha finito di disegnare il pattern, una linea dall'ultimo punto del percorso dovrebbe connettersi automaticamente con il primo punto del percorso (sto usando il metodo "closePath" per questo), se questa linea si interseca con un'altra "linea" "nel percorso il percorso dovrebbe anche sparire dallo schermo.

Memorizzo tutti i punti di contatto in un CGPoint che memorizzo in un'altra classe denominata Linea come punto A e punto B. Sto quindi salvando la "linea" in un NSMutableArray chiamato "linee". Ogni volta che un punto viene aggiunto al percorso, controllo se la linea tra quel punto e il punto disegnato prima si interseca con una qualsiasi delle "linee" in linee usando un metodo (- (BOOL) checkLineIntersection: (CGPoint) p1 : (CGPoint) p2: (CGPoint) p3: (CGPoint) p4) Ho ottenuto da questo tutorial "http://www.iossourcecode.com/2012/08/02/how-to-make-a-game-like- cut-the-corda-parte-2 /".

Il problema

Il problema è che quando faccio funzionare l'applicazione funziona a volte, ma a volte quando disegno in modo che le linee si intersecano il percorso non scompare. Non riesco a capire perché ... Sembra come succede più spesso quando disegno lentamente.

Il codice:

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:

012.351.641,061 mila
#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 

Spero che qualcuno potrebbe essere in grado di rispondere a questa. Scusa se è qualcosa di ovvio. Grazie in anticipo!

risposta

8

Il problema è nel metodo checkLineIntersection. Con

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

si controlla solo se la parte interna delle linee di segmenti si intersecano. Ma se l'inizio o il punto finale del primo segmento di linea è uguale a l'inizio o il punto finale del secondo segmento di linea, ua e ub sarà 0.0 o 1.0.

La soluzione è quella di includere un termine dell'intervallo nella condizione:

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

Questo sembrava funzionare come previsto nel mio programma di test.

Alcune ulteriori osservazioni:

  • Penso che si dovrebbe attivare il collegamento

    if (denominator == 0.0f) return NO; 
    

    di nuovo per evitare la divisione per zero.

  • In touchesMoved, si potrebbe aggiungere la nuova linea per la matrice dopo verifica di intersezioni. Ora la nuova riga viene inserita per prima, il che significa che viene controllata contro se stessa per le intersezioni.

  • Hai dichiarato Line come sottoclasse di UIView, ma questa non è realmente una classe di visualizzazione. Potresti semplicemente dichiarare Line come sottoclasse di NSObject.


aggiunto: Il seguente metodo potrebbe funzionare ancora meglio, perché evita la divisione e quindi possibili problemi di overflow con i piccoli denominatori:

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

Risposta meravigliosa! Grazie. – JesperWingardh

+0

@JesperWingardh: prego. –

0

ho trovato un'altra soluzione per controlla se la linea interseca se stessa. Con il framework SceneKit è possibile creare forme da UIBezierPath. Ma se il percorso si interseca, il riquadro di delimitazione del nodo verrà azzerato.

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