2012-05-17 17 views
5

Sto scrivendo un'app per iPad in cui sto eseguendo il rendering di oggetti XML che rappresentano forme in grafica sullo schermo. Uno degli oggetti che sto cercando di rendere è archi. Essenzialmente questi archi mi forniscono un rettangolo di delimitazione e un angolo di inizio e fine.Disegna un'ellisse con angolo iniziale e finale in Objective-C

attributi proprio:

  • x
  • y
  • larghezza
  • altezza
  • startAngle
  • endAngle

Con questi valori devo disegnare l'arco (che è essenzialmente parte di un'ellisse). Non riesco a utilizzare quanto segue:

UIBezierPath *arc = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x, y, width, height)]; 
    [UIColor blackColor] setStroke]; 
    [arc stroke]; 

perché disegna un'ellisse completa. Fondamentalmente ho bisogno di quanto sopra, ma deve prendere in considerazione gli angoli di inizio e di fine in modo che venga visualizzata solo una parte dell'ellisse. Sto pensando che questo comporti sia disegnando una curva cubica di Bezier o una curva di Bezier quadratica. Il problema è che non ho idea di come calcolare il punto iniziale, il punto finale o i punti di controllo con le informazioni che mi vengono fornite.

risposta

9

Probabilmente si può ottenere quello che vuoi attraverso la creazione di un percorso clip intorno al disegno dell'ellisse.

CGContextSaveGState(theCGContext); 
CGPoint center = CGPointMake(x + width/2.0, y + height/2.0); 
UIBezierPath* clip = [UIBezierPath bezierPathWithArcCenter:center 
                radius:max(width, height) 
               startAngle:startAngle 
                endAngle:endAngle 
               clockwise:YES]; 
[clip addLineToPoint:center]; 
[clip closePath]; 
[clip addClip]; 

UIBezierPath *arc = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x, y, width, height)]; 
[[UIColor blackColor] setStroke]; 
[arc stroke]; 

CGContextRestoreGState(theCGContext); 

Il raggio esatto per il ritaglio non è importante. Deve essere abbastanza grande in modo che solo clip l'ellisse alle estremità, non attraverso l'arco desiderato.

+0

Grazie, ha funzionato come un incantesimo (ancora nuovo per Objective-C e per la grafica di disegno). –

2

Si dovrà utilizzare addQuadCurveToPoint:controlPoint: invece di bezierPathWithOvalInRect.

La definizione del metodo è come: -

- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint 

che è CGPoint sono gli argomenti (input) per entrambi i parametri.

Sarà inoltre necessario impostare il punto di partenza utilizzando moveToPoint: prima di chiamare addQuadCurveToPoint che fungerà da punto corrente (Come è possibile vedere il loro punto di partenza non come parametro nel metodo).

Nel tuo caso si avrà

1> x, y come punto di partenza

2> x + y + larghezza e l'altezza finale

non è necessario angoli qui o puoi implementare la tua logica per utilizzare gli angoli.Maggiendo un'immagine per rendere le cose chiare.

Here is the image describing the start point,end point and control point

+0

Una curva bezier quadratica non funzionerà. Forse puoi calcolare i punti di controllo per una curva bezier cubica in modo tale che un'ellisse sia disegnata con le proprietà specificate.Senza la matematica necessaria per tradurre le informazioni fornite in una curva di bezier, questo non risponde alla domanda. – thundersteele

+0

In realtà, penso che se vuoi disegnare il percorso esplicitamente (cioè NON con clipping) hai bisogno dell'equazione parametrica per l'eliporto. Quindi dovrai creare una serie di segmenti brevi (quadratici o cubici o anche lineari) che APPROSSIMANO l'elipse. Il ritaglio è migliore. – samson

+1

Questo è un grande Product Site Documentation Site: http://www.techotopia.com/index.php/An_iPhone_Graphics_Drawing_Tutorial_using_Quartz_2D#Drawing_an_Ellipse_or_Circle – Aardvark

3

Questa categoria aiuterà.

#import <Foundation/Foundation.h> 

@interface UIBezierPath (OvalSegment) 

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle; 
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle angleStep:(CGFloat)angleStep; 

@end 

#import "UIBezierPath+OvalSegment.h" 

@implementation UIBezierPath (OvalSegment) 

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle angleStep:(CGFloat)angleStep { 
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 
    CGFloat xRadius = CGRectGetWidth(rect)/2.0f; 
    CGFloat yRadius = CGRectGetHeight(rect)/2.0f; 

    UIBezierPath *ellipseSegment = [UIBezierPath new]; 

    CGPoint firstEllipsePoint = [self ellipsePointForAngle:startAngle withCenter:center xRadius:xRadius yRadius:yRadius]; 
    [ellipseSegment moveToPoint:firstEllipsePoint]; 

    for (CGFloat angle = startAngle + angleStep; angle < endAngle; angle += angleStep) { 
     CGPoint ellipsePoint = [self ellipsePointForAngle:angle withCenter:center xRadius:xRadius yRadius:yRadius]; 
     [ellipseSegment addLineToPoint:ellipsePoint]; 
    } 

    CGPoint lastEllipsePoint = [self ellipsePointForAngle:endAngle withCenter:center xRadius:xRadius yRadius:yRadius]; 
    [ellipseSegment addLineToPoint:lastEllipsePoint]; 

    return ellipseSegment; 
} 

+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle { 
    return [UIBezierPath bezierPathWithOvalInRect:rect startAngle:startAngle endAngle:endAngle angleStep:M_PI/20.0f]; 
} 

+ (CGPoint)ellipsePointForAngle:(CGFloat)angle withCenter:(CGPoint)center xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius { 
    CGFloat x = center.x + xRadius * cosf(angle); 
    CGFloat y = center.y - yRadius * sinf(angle); 
    return CGPointMake(x, y); 
} 

@end 
+0

Ottima categoria !!! Giusto per renderlo perfetto, il primo punto per aggiungere la linea a non dovrebbe includere il punto iniziale che è firstEllipsePoint nel codice sopra. Quindi l'istruzione 'for' dovrebbe essere' for (CGFloat angle = startAngle + angleStep; angle

+0

@RungeZhai: è vero, fatto! – kanobius

+0

L'ellipsePointForAngle sembra non essere corretto oppure esiste una discrepanza diversa per il metodo che utilizzo. Quando l'angolo aumenta intorno all'ovale, sembra leggermente spostato avanti e indietro. Ciò è parzialmente causato dall'inclusione della larghezza e dell'altezza dell'ellisse, che è importante e diversa dal semplice trovare un punto su un cerchio da un angolo. – VagueExplanation