Je travaille sur une application de dessin sur iPhone. Je l'ai fait fonctionner mais pas joli comme on le voit ici
Et je cherche n'importe quelle suggestion pour lisser le dessin Fondamentalement, ce que j'ai fait, c'est quand l'utilisateur place un doigt sur l'écran que j'ai appelé
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
puis je recueille une seule touche dans un tableau avec
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
et lorsque l'utilisateur laisse un doigt sur l'écran, j'ai appelé
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
puis je dessine tous les points du 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 davantage à l'application "Sketch Book"
Je pense qu'il y a quelque chose à voir 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 :)
Le moyen le plus simple de lisser une courbe comme celle-ci consiste à utiliser une courbe de Bézier au lieu de segments de ligne droite. Pour les mathématiques derrière cela, voir cet article (pointé dans cette réponse ), qui décrit comment calculer les courbes nécessaires pour lisser une courbe qui passe par plusieurs points.
Je crois que le Core Plot framework a maintenant 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 rien de magique à tout 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'adore vraiment le sujet. Merci pour toutes les implémentations, en particulier Krzysztof Zabłocki et Yu-Sen Han. J'ai modifié la version de Yu-Sen Han afin de changer l'épaisseur du trait en fonction de la vitesse de 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 proches les uns des autres) Voici le résultat:
Pour définir l'épaisseur de la ligne, j'ai choisi une telle fonction de la distance:
(Ne me demandez pas pourquoi ... je pense que ça va bien, mais je suis sûr que vous pouvez en trouver un meilleur)
CGFloat dist = distance(previousPoint1, currentPoint);
CGFloat newWidth = 4*(atan(-dist/15+1) + M_PI/2)+2;
Encore un indice. 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);
J'ai traduit réponse de kyoji en Swift, comme une sous-classe réutilisable de UIImageView
. La sous-classe TouchDrawImageView
permet à l'utilisateur de dessiner sur une vue d'image avec son doigt.
Une fois que vous avez ajouté cette classe TouchDrawImageView
à votre projet, assurez-vous d'ouvrir votre storyboard et
TouchDrawImageView
comme "Classe personnalisée" de votre vue d'imageVoici le code de TouchDrawImageView.Swift
:
import UIKit
class TouchDrawImageView: UIImageView {
var previousPoint1 = CGPoint()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
previousPoint1 = touch.previousLocation(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, 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)
}
}
Merci pour la contribution. Je mets à jour ma quête ici parce que j'ai besoin d'espace pour cela.
Je recherche les solutions de courbes corePlot et Bézier que vous avez suggérées avec peu de succès.
Pour le corePlot, je peux obtenir le graphique à partir d'un tableau d'int, mais je ne trouve rien lié au lissage de courbe.BTW Ici, j'utilise CPScatterPlot avec un certain nombre aléatoire.
quant à la courbe de Bézier, ma quête m'a amené à ici C'est quelque chose à voir avec l'implémentation Spline dans iOS
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 que j'obtiens est:
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 vraiment comment y aller. Je suis donc coincé sur ce front aussi :(
J'ai cherché 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 le résoudre. Mais je suppose que je partagerai mes conclusions 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
J'essaie toujours de bien faire les choses, toute autre suggestion est la bienvenue.
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.