Algorithme de dessin d’esquisse lisse pour iPhone

Je travaille sur une application de croquis sur l’iPhone. Je l’ai fait fonctionner mais pas joli comme vu ici entrer la description de l'image ici

Et je suis à la recherche de toute suggestion pour lisser le dessin Fondamentalement, ce que j’ai fait, c’est lorsque l’utilisateur place un doigt sur l’écran, j’ai appelé

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 

alors je collectionne une seule touche dans un tableau avec

 - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 

et lorsque l’utilisateur a un doigt de l’écran, j’ai appelé

 - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 

alors je dessine tous les points dans le tableau en utilisant

 NSMutableArray *points = [collectedArray points]; CGPoint firstPoint; [[points objectAtIndex:0] getValue:&firstPoint]; CGContextMoveToPoint(context, firstPoint.x, firstPoint.y); CGContextSetLineCap(context, kCGLineCapRound); CGContextSetLineJoin(context, kCGLineJoinRound); for (int i=1; i < [points count]; i++) { NSValue *value = [points objectAtIndex:i]; CGPoint point; [value getValue:&point]; CGContextAddLineToPoint(context, point.x, point.y); } CGContextStrokePath(context); UIGraphicsPushContext(context); 

Et maintenant je veux améliorer le dessin pour qu’il ressemble plus à l’application “Sketch Book” entrer la description de l'image ici

Je pense qu’il y a quelque chose à faire avec l’algorithme de traitement du signal pour réorganiser tous les points du tableau, mais je ne suis pas sûr. Toute aide serait très appréciée.

Merci d’avance 🙂

    La manière la plus simple de lisser une courbe comme celle-ci consiste à utiliser une courbe de Bézier au lieu de segments de ligne droite. Pour en savoir plus, consultez cet article (indiqué dans cette réponse ), qui décrit comment calculer les courbes requirejses pour lisser une courbe passant par plusieurs points.

    Je pense que le framework Core Plot a désormais la capacité de lisser les courbes des tracés, vous pouvez donc regarder le code utilisé pour implémenter ce type de lissage.

    Il n’y a pas de magie à cela, car ces routines de lissage sont rapides et relativement faciles à mettre en œuvre.

     CGPoint midPoint(CGPoint p1, CGPoint p2) { return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5); } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; previousPoint1 = [touch previousLocationInView:self]; previousPoint2 = [touch previousLocationInView:self]; currentPoint = [touch locationInView:self]; } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; previousPoint2 = previousPoint1; previousPoint1 = [touch previousLocationInView:self]; currentPoint = [touch locationInView:self]; // calculate mid point CGPoint mid1 = midPoint(previousPoint1, previousPoint2); CGPoint mid2 = midPoint(currentPoint, previousPoint1); UIGraphicsBeginImageContext(self.imageView.frame.size); CGContextRef context = UIGraphicsGetCurrentContext(); [self.imageView.image drawInRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height)]; CGContextMoveToPoint(context, mid1.x, mid1.y); // Use QuadCurve is the key CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y); CGContextSetLineCap(context, kCGLineCapRound); CGContextSetLineWidth(context, 2.0); CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); CGContextStrokePath(context); self.imageView.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); } 

    J’aime vraiment le sujet. Merci pour toutes les mises en œuvre, en particulier Krzysztof Zabłocki et Yu-Sen Han. J’ai modifié la version de Yu-Sen Han afin de modifier l’épaisseur de la ligne en fonction de la vitesse du panoramique (en fait, la distance entre les dernières touches). J’ai également implémenté le dessin par points (pour les emplacements touchBegan et touchEnded étant proches les uns des autres) Voici le résultat: entrer la description de l'image ici

    Pour définir l’épaisseur du trait, j’ai choisi une telle fonction de distance:

    (Ne me demande pas pourquoi … mais je pense que ça va bien, mais je suis sûr que tu en trouveras une meilleure)

    entrer la description de l'image ici

     CGFloat dist = distance(previousPoint1, currentPoint); CGFloat newWidth = 4*(atan(-dist/15+1) + M_PI/2)+2; 

    Un indice de plus. Pour être sûr que l’épaisseur change en douceur, je l’ai limitée en fonction de l’épaisseur du segment précédent et d’un coef personnalisé:

     self.lineWidth = MAX(MIN(newWidth,lastWidth*WIDTH_RANGE_COEF),lastWidth/WIDTH_RANGE_COEF); 

    Merci pour l’entrée. Je mets à jour ma quête ici parce que j’ai besoin d’espace pour cela.

    Je recherche les solutions de courbes corePlot et Bezier que vous avez suggérées avec peu de succès.

    Pour le corePlot, je peux obtenir le tracé du graphe à partir d’un tableau d’int, mais je ne trouve rien en rapport avec le lissage des courbes.BTW Ici, j’utilise CPScatterPlot avec un certain nombre aléatoire.

    entrer la description de l'image ici

    comme pour la courbe de Bézier, ma quête m’amène ici

      CatmullRomSpline *myC = [[CatmullRomSpline alloc] initAtPoint:CGPointMake(1.0, 1.0)]; [myC addPoint:CGPointMake(1.0, 1.5)]; [myC addPoint:CGPointMake(1.0, 1.15)]; [myC addPoint:CGPointMake(1.0, 1.25)]; [myC addPoint:CGPointMake(1.0, 1.23)]; [myC addPoint:CGPointMake(1.0, 1.24)]; [myC addPoint:CGPointMake(1.0, 1.26)]; NSLog(@"xxppxx %@",[myC asPointArray]); NSLog(@"xxppxx2 %@",myC.curves); 

    et le résultat est le suivant:

      2011-02-24 14:45:53.915 DVA[10041:40b] xxppxx ( "NSPoint: {1, 1}", "NSPoint: {1, 1.26}" ) 2011-02-24 14:45:53.942 DVA[10041:40b] xxppxx2 ( "QuadraticBezierCurve: 0x59eea70" ) 

    Je ne sais pas trop comment y aller. Donc, je suis coincé sur ce front aussi 🙁

    J’ai consulté GLPaint comme dernière ressource. Il utilise OpenGLES et utilise un sprite “soft dot” pour tracer les points dans le tableau. Je sais que c’est plus comme éviter le problème que de le réparer. Mais je suppose que je partagerai mes découvertes ici de toute façon.

    Le noir est GLPaint et le blanc est l’ancienne méthode. Et le dernier est le dessin de l’application “Sketch Book” juste pour comparer

    entrer la description de l'image icientrer la description de l'image icientrer la description de l'image ici

    J’essaie toujours de bien faire les choses, toute autre suggestion est la bienvenue.

    J’ai traduit la réponse de kyoji dans Swift, en tant que sous-classe réutilisable de UIImageView . La sous-classe TouchDrawImageView permet à l’utilisateur de dessiner sur une image avec son doigt.

    Une fois que vous avez ajouté cette classe TouchDrawImageView à votre projet, veillez à ouvrir votre storyboard et

    1. sélectionnez TouchDrawImageView comme “Classe personnalisée” de votre vue image
    2. cochez la propriété “Interaction utilisateur activée” de votre vue image

    Voici le code de TouchDrawImageView.swift :

     import UIKit class TouchDrawImageView: UIImageView { var previousPoint1 = CGPoint() override func touchesBegan(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } previousPoint1 = touch.previousLocation(in: self) } override func touchesMoved(_ touches: Set, with event: UIEvent?) { guard let touch = touches.first else { return } let previousPoint2 = previousPoint1 previousPoint1 = touch.previousLocation(in: self) let currentPoint = touch.location(in: self) // calculate mid point let mid1 = midPoint(p1: previousPoint1, p2: previousPoint2) let mid2 = midPoint(p1: currentPoint, p2: previousPoint1) UIGraphicsBeginImageContext(self.frame.size) guard let context = UIGraphicsGetCurrentContext() else { return } if let image = self.image { image.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)) } context.move(to: mid1) context.addQuadCurve(to: mid2, control: previousPoint1) context.setLineCap(.round) context.setLineWidth(2.0) context.setStrokeColor(red: 1.0, green: 0, blue: 0, alpha: 1.0) context.strokePath() self.image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() } func midPoint(p1: CGPoint, p2: CGPoint) -> CGPoint { return CGPoint(x: (p1.x + p2.x) / 2.0, y: (p1.y + p2.y) / 2.0) } } 

    Pour se débarrasser du point idiot dans le code GLPaint.

    Changer

     -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 

    cette fonction

     //Ändrat av OLLE /* // Convert touch point from UIView referential to OpenGL one (upside-down flip) if (firstTouch) { firstTouch = NO; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; } else { location = [touch locationInView:self]; location.y = bounds.size.height - location.y; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; } */ location = [touch locationInView:self]; location.y = bounds.size.height - location.y; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; //Ändrat av OLLE// 

    Je sais que ce n’est pas la solution à notre problème, mais c’est quelque chose.