2011-01-05 13 views
14

Ich erstelle eine Zeichnung App (Text) für das iPad mit OpenGL. Ich habe mir bereits das Beispiel GLPaint von Apple angesehen und meine App basiert nun auf diesem Code. Meine App sollte nur zum Zeichnen von Text dienen, nicht zum Malen von Bildern.Zeichnen App auf dem iPad mit OpenGL

Nun, meine App funktioniert, ich kann etwas Text schreiben. Aber das Schreiben ist nicht wirklich gut, es macht keinen Spaß zu schreiben. Der Zeichenpfad ist nicht glatt, sondern winklig, weil ich eine Linie von einem Punkt zum anderen zeichne. Und der Pfad hat überall die gleiche Breite. Meine Idee ist: Wenn Sie schnell schreiben, ist die Linie dünner als wenn Sie langsam schreiben. Es sollte die gleiche Erfahrung sein wie das Schreiben mit einem echten Stift.

Wie kann ich den Pfad viel glatter aussehen lassen? Wie kann ich die Breite der Linie abhängig von der Geschwindigkeit des Schreibens variieren?

Hier können Sie sehen, was ich meine:

example

Antwort

27

Der beste Weg, um die Zeichnung zu glätten, ist eine Bezeir-Kurve. Hier ist mein Code. Es ist eine modifizierte Version, die ich auf dem Apple-Entwickler-Website gefunden, aber ich erinnere mich nicht den Original-Link:

CGPoint drawBezier(CGPoint origin, CGPoint control, CGPoint destination, int segments) 
{ 
CGPoint vertices[segments/2]; 
CGPoint midPoint; 
glDisable(GL_TEXTURE_2D); 
float x, y; 

float t = 0.0; 
for(int i = 0; i < (segments/2); i++) 
{ 
    x = pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x; 
    y = pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y; 
    vertices[i] = CGPointMake(x, y); 
    t += 1.0/(segments); 

} 
//windowHeight is the height of you drawing canvas. 
midPoint = CGPointMake(x, windowHeight - y); 
glVertexPointer(2, GL_FLOAT, 0, vertices); 
glDrawArrays(GL_POINTS, 0, segments/2); 
return midPoint; 
} 

Das auf drei Punkten basierend wird remis. Das Steuerelement ist der Mittelpunkt, den Sie zurückgeben müssen. Der neue Mittelpunkt wird sich von dem vorherigen unterscheiden. Wenn Sie den obigen Code durchlaufen, wird nur die halbe Zeile angezeigt. Der nächste Strich füllt ihn aus. Dies ist erforderlich. mein Code, um diese Funktion für den Aufruf (die oben in C ist, dann ist dies in Obj-C):

//Invert the Y axis to conform the iPhone top-down approach 
    invertedYBegCoord = self.bounds.size.height - [[currentStroke objectAtIndex:i] CGPointValue].y; 
    invertedYEndCoord = self.bounds.size.height - [[currentStroke objectAtIndex:i+1] CGPointValue].y; 
    invertedYThirdCoord = self.bounds.size.height - [[currentStroke objectAtIndex:i+2] CGPointValue].y; 
    //Figure our how many dots you need 
    count = MAX(ceilf(sqrtf(([[currentStroke objectAtIndex:i+2] CGPointValue].x - [[currentStroke objectAtIndex:i] CGPointValue].x) 
     * ([[currentStroke objectAtIndex:i+2] CGPointValue].x - [[currentStroke objectAtIndex:i] CGPointValue].x) 
     + ((invertedYThirdCoord - invertedYBegCoord) * (invertedYThirdCoord - invertedYBegCoord)))/pointCount), 1); 

    newMidPoint = drawBezier(CGPointMake([[currentStroke objectAtIndex:i] CGPointValue].x, invertedYBegCoord), CGPointMake([[currentStroke objectAtIndex:i+1] CGPointValue].x, invertedYEndCoord), CGPointMake([[currentStroke objectAtIndex:i+2] CGPointValue].x, invertedYThirdCoord), count); 

    int loc = [currentStroke count]-1; 
    [currentStroke insertObject:[NSValue valueWithCGPoint:newMidPoint] atIndex:loc]; 
    [currentStroke removeObjectAtIndex:loc-1]; 

Dieser Code wird den Mittelpunkt basierend auf invertierten iPad Punkte, und stellen Sie die ‚Kontrolle‘ als die aktuelle erhalten Punkt.

Das wird die Kanten glätten. In Bezug auf die Linienbreite musst du nur noch die Geschwindigkeit dieser Zeichnung finden. Es ist am einfachsten, nur die Länge Ihrer Linie zu finden. Dies kann leicht unter Verwendung von Komponentenmathematik durchgeführt werden. Ich habe keinen Code dafür, aber ist eine Grundierung für Komponente Mathematics von einer Physik-Website. Oder Sie können (oben) einfach durch eine Zahl teilen, um herauszufinden, wie dick die Linie sein muss (Zählung verwendet Komponenten Mathematik).

Ich speicherpunkt Daten in einem Array namens currentStroke, falls es nicht offensichtlich war.

Das sollte alles sein, was Sie brauchen.

EDIT:

Punkte zu speichern, sollten Sie touchesBegin und touchesEnd:

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event 
{ 
    self.currentStroke = [NSMutableArray array]; 
    CGPoint point = [ [touches anyObject] locationInView:self]; 
    [currentStroke addObject:[NSValue valueWithCGPoint:point]]; 
    [self draw]; 

} 

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event 
{ 
    CGPoint point = [ [touches anyObject] locationInView:self]; 
    [currentStroke addObject:[NSValue valueWithCGPoint:point]]; 
    [self draw]; 
} 


- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    CGPoint point = [ [touches anyObject] locationInView:self]; 
    [currentStroke addObject:[NSValue valueWithCGPoint:point]]; 
    [self draw]; 
} 

, dass die Anwendung so ziemlich eine ganze Zeichnung ist da. Wenn Sie GL_Paint verwenden, verwenden Sie bereits Punkt-Sprites, auf denen dieses System basiert.

+0

Vielen Dank, das hilft wirklich. Aber ich bin mir nicht sicher, wo ich den zweiten Teil des Codes implementieren soll. Im GLPaint-Beispiel gibt es eine Funktion renderLineFromPoint: toPoint :, die jedes Mal aufgerufen wird, wenn der Benutzer seinen Finger bewegt. Muss ich den zweiten Teil Ihres Codes in diese Funktion einfügen? – burki

+0

Oder könnten Sie den ganzen Code hinzufügen? Ich meine die vollständigen Methoden. Das wäre sehr nett. – burki

+0

Zusätzliche BerührungenBeginn, berührtMoved und berührtEnd. Das ist so ziemlich eine komplette openGL-Zeichnungsanwendung in der Post. – Beaker

4

In Bezug auf den zweiten Teil Ihrer Frage (wie die Breite der Linie auf die Geschwindigkeit des Schreibens abhängig zu variieren), sollten Sie sein in der Lage, dies zu erreichen, indem Sie die UITouchtimestamp Eigenschaft in Ihrem touchesBegan:withEvent: und touchesMoved:withEvent: Methode nutzen.

Sie können den Zeitunterschied zwischen zwei aufeinanderfolgenden Berührungsereignissen berechnen, indem Sie den Zeitstempel des letzten Objekts UITouch speichern und mit dem neuen vergleichen. Wenn Sie die Entfernung der Wischbewegung durch die Zeitdifferenz teilen, sollten Sie die Bewegungsgeschwindigkeit messen.

Jetzt müssen Sie nur noch einen Weg finden, um die Schreibgeschwindigkeit in die Zeilenbreite zu konvertieren, was wahrscheinlich darauf hinausläuft, einen beliebigen Wert auszuwählen und anzupassen, bis Sie mit dem Ergebnis zufrieden sind.

+0

Danke bis jetzt. Ich habe schon an so etwas gedacht. Wäre schön, wenn du wüsstest, wie ich mein erstes Problem lösen kann. Hoffe, du bist glücklich genug mit Upvote. – burki

Verwandte Themen