Algoritmo de dibujo suave de iPhone

Estoy trabajando en una aplicación de dibujo en el iPhone. Lo tengo funcionando pero no es bonito como se ve aquí enter image description here

Y estoy buscando alguna sugerencia para suavizar el dibujo Básicamente, lo que hice fue cuando el usuario coloca un dedo en la pantalla que llamé

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

entonces colecciono un solo toque en una matriz con

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

y cuando el usuario mueve un dedo desde la pantalla, llamé

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

entonces dibujo todos los puntos en la matriz usando

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

Y ahora quiero mejorar el dibujo para que sea más parecido a la aplicación “Sketch Book” enter image description here

Creo que hay algo que ver con el algoritmo de procesamiento de señal para reorganizar todos los puntos en la matriz, pero no estoy seguro. Cualquier ayuda sería muy apreciada.

Gracias de antemano 🙂

    La forma más fácil de suavizar una curva como esta es utilizar una curva Bezier en lugar de segmentos de línea recta. Para las matemáticas que se encuentran detrás de esto, consulte este artículo (señalado en esta respuesta ), que describe cómo calcular las curvas necesarias para alisar una curva que pasa por múltiples puntos.

    Creo que el framework de Core Plot ahora tiene la capacidad de suavizar las curvas de los trazados, por lo que podría ver el código utilizado allí para implementar este tipo de suavizado.

    No hay nada de mágico en esto, ya que estas rutinas de suavizado son rápidas y relativamente fáciles de implementar.

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

    Realmente amo el tema. Gracias por todas las implementaciones, especialmente Krzysztof Zabłocki y Yu-Sen Han. He modificado la versión de Yu-Sen Han para cambiar el grosor de la línea en función de la velocidad de la panoramización (de hecho, la distancia entre los últimos toques). También implementé el dibujo de puntos (para que las ubicaciones touchBegan y touchEnded estén cerca la una de la otra) Aquí está el resultado: enter image description here

    Para definir el grosor de la línea, he elegido una función de distancia tal:

    (No me preguntes por qué … solo creo que es muy apropiado, pero estoy seguro de que puedes encontrar uno mejor)

    enter image description here

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

    Una sugerencia más Para estar seguro de que el grosor cambia suavemente, lo he delimitado según el grosor del segmento anterior y un coef personalizado:

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

    Gracias por la entrada. Actualizo mi búsqueda porque necesito espacio.

    Busco las soluciones de curva de CorePlot y Bezier que sugirió con poco éxito.

    Para corePlot, puedo obtener el gráfico de una matriz de int pero no puedo encontrar nada relacionado con el suavizado de curvas. Aquí estoy usando CPScatterPlot con un número aleatorio.

    enter image description here

    en cuanto a la curva de Bezier, Mi búsqueda me llevó hasta aquí. Tiene algo que ver con la implementación de Spline en 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); 

    y el resultado que obtengo es:

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

    No estoy seguro de cómo ir desde allí. Así que estoy atrapado en ese frente también 🙁

    Busqué GLPaint, como último recurso. Utiliza OpenGLES y usa un sprite de “punto suave” para trazar los puntos en la matriz. Sé que es más como evitar el problema en lugar de solucionarlo. Pero supongo que compartiré mis hallazgos aquí de todos modos.

    El negro es GLPaint y el blanco es el método anterior. Y el último es el dibujo de la aplicación “Sketch Book” solo para comparar

    enter image description hereenter image description hereenter image description here

    Todavía estoy tratando de hacer esto bien, cualquier sugerencia adicional es bienvenida.

    Traduje la respuesta de kyoji a Swift, como una subclase reutilizable de UIImageView . La subclase TouchDrawImageView permite al usuario dibujar en una vista de imagen con su dedo.

    Una vez que haya agregado esta clase TouchDrawImageView a su proyecto, asegúrese de abrir su guión gráfico y

    1. seleccione TouchDrawImageView como la “Clase personalizada” de su vista de imagen
    2. marca la propiedad “Interacción del usuario habilitada” de tu vista de imagen

    Aquí está el código 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) } } 

    Para deshacerse del punto tonto en el código GLPaint.

    Cambiar en

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

    esta función

     //Ä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// 

    Sé que esta no es la solución para nuestro problema, pero es algo.