Dibujando una curva de bezier entre un set de puntos dados

¿Cuál es la mejor forma de dibujar una curva bezier, en la aplicación iOS, que pasa por un set de puntos dados

Una forma un poco más genérica de hacerlo se puede lograr, por ejemplo, mirando el proyecto BEMSimpleLineGraph GitHub (ver aquí para más información: bemsimplelinegraph ). Aquí extraí un método para dibujar una curva de bezier a través de una list de puntos dada.

El file de encabezado (BezierLine.h):

#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <CoreGraphics/CoreGraphics.h> @interface BezierLine : NSObject /* Draws a bezier curved line on the given context with points: Array of CGPoint values */ -(void) drawBezierCurveInContext:(CGContextRef)context withPoints:(NSArray*)points lineColor:(UIColor*)color lineWidth:(CGFloat)lineWidth; @end 

La implementación (BezierLine.m):

 #import "BezierLine.h" @implementation BezierLine -(void) drawBezierCurveInContext:(CGContextRef)context withPoints:(NSArray*)points lineColor:(UIColor*)color lineWidth:(CGFloat)lineWidth { if (points.count < 2) return; CGPoint CP1; CGPoint CP2; // LINE UIBezierPath *line = [UIBezierPath bezierPath]; CGPoint p0; CGPoint p1; CGPoint p2; CGPoint p3; CGFloat tensionBezier1 = 0.3; CGFloat tensionBezier2 = 0.3; CGPoint previousPoint1; CGPoint previousPoint2; [line moveToPoint:[[points objectAtIndex:0] CGPointValue]]; for (int i = 0; i < points.count - 1; i++) { p1 = [[points objectAtIndex:i] CGPointValue]; p2 = [[points objectAtIndex:i + 1] CGPointValue]; const CGFloat maxTension = 1.0f / 3.0f; tensionBezier1 = maxTension; tensionBezier2 = maxTension; if (i > 0) { // Exception for first line because there is no previous point p0 = previousPoint1; if (p2.y - p1.y == p1.y - p0.y) tensionBezier1 = 0; } else { tensionBezier1 = 0; p0 = p1; } if (i < points.count - 2) { // Exception for last line because there is no next point p3 = [[points objectAtIndex:i + 2] CGPointValue]; if (p3.y - p2.y == p2.y - p1.y) tensionBezier2 = 0; } else { p3 = p2; tensionBezier2 = 0; } // The tension should never exceed 0.3 if (tensionBezier1 > maxTension) tensionBezier1 = maxTension; if (tensionBezier2 > maxTension) tensionBezier2 = maxTension; // First control point CP1 = CGPointMake(p1.x + (p2.x - p1.x)/3, p1.y - (p1.y - p2.y)/3 - (p0.y - p1.y)*tensionBezier1); // Second control point CP2 = CGPointMake(p1.x + 2*(p2.x - p1.x)/3, (p1.y - 2*(p1.y - p2.y)/3) + (p2.y - p3.y)*tensionBezier2); [line addCurveToPoint:p2 controlPoint1:CP1 controlPoint2:CP2]; previousPoint1 = p1; previousPoint2 = p2; } CGContextSetAllowsAntialiasing(context, YES); CGContextSetStrokeColorWithColor(context, color.CGColor); CGContextSetLineWidth(context, lineWidth); CGContextAddPath(context, line.CGPath); CGContextDrawPath(context, kCGPathStroke); } @end 

Puede usarlo, por ejemplo, creando un context de image usando UIGraphicsBeginImageContext y recuperando el context con UIGraphicsGetCurrentContext ().

De lo contrario, es posible que desee cambiar el código y asignar la ruta resultante a un CALayer y agregarlo a una UIView.

Espero que esto ayude.

Sé que esto puede llegar tarde, pero solo para cualquiera que esté buscando la respuesta correcta. En lugar de usar addLineToPoint para dibujar la línea recta. Puede usar addCurveToPoint para dibujar la curva. p.ej

 [bezierPath moveToPoint:CGPointMake(0, 0)]; [bezierPath addCurveToPoint:CGPointMake(40, 100) controlPoint1:CGPointMake(20, 0) controlPoint2:CGPointMake(20, 100)]; [bezierPath addCurveToPoint:CGPointMake(80, 50) controlPoint1:CGPointMake(60, 100) controlPoint2:CGPointMake(60, 50)]; // and you may don't want to close the path // [bezierPath closePath]; 

Depende de ti elegir los puntos de control de la curva. Acabo de usar el x = last_point_x + 20; y = last_point_y para el punto de control uno, y x = current_point_x – 20; y = current_point_y;

y es posible que desee utilizar otro valor en lugar de 20, ya que puede tener un ancho de segmento diferente de la curva.

Puedes search fácilmente un ejemplo de cómo crear la curva de bezier en la web. Encontré este tutorial corto como un ejemplo.

Puede crear una curva cercana de bezier, por ejemplo, con el siguiente fragment de código:

 UIBezierPath* path = [UIBezierPath bezierPath]; [path moveToPoint:pt1]; [path addLineToPoint:pt2]; [path addLineToPoint:pt3]; [path closePath]; 

Espero que sirva como punto de partida.

 UIBezierPath *aPath = [UIBezierPath bezierPath]; // Set the starting point of the shape. [aPath moveToPoint:CGPointMake(100.0, 0.0)]; // Draw the lines. [aPath addLineToPoint:CGPointMake(200.0, 40.0)]; [aPath addLineToPoint:CGPointMake(160, 140)]; [aPath addLineToPoint:CGPointMake(40.0, 140)]; [aPath addLineToPoint:CGPointMake(0.0, 40.0)]; [aPath closePath]; 

Intente esto.

 UIImageView *waterLevel = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,200,200)]; UIGraphicsBeginImageContext(waterLevel.frame.size); [waterLevel.image drawAtPoint:CGPointZero]; //define BezierPath UIBezierPath *bezierPath = [UIBezierPath bezierPath]; // Set the starting point of the shape. [bezierPath moveToPoint:CGPointMake(0, 0)]; [bezierPath addLineToPoint:CGPointMake(waterLevel.frame.size.width, 0)]; [bezierPath addLineToPoint:CGPointMake(waterLevel.frame.size.width, waterLevel.frame.size.height)]; [bezierPath addLineToPoint:CGPointMake(0, waterLevel.frame.size.height)]; [bezierPath closePath]; bezierPath.lineWidth = 15; //set the stoke color [[UIColor blackColor] setStroke]; //draw the path [bezierPath stroke]; // Add to the current Graphic context CGContextRef context = UIGraphicsGetCurrentContext(); CGContextAddPath(context,bezierPath.CGPath); waterLevel.image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); [self.view addSubview:waterLevel]; 

Puede ser mucho más eficiente usando el método CGPointFromString :

  NSArray *pointArray = @[@"{3.0,2.5}",@"{100.0,30.2}", @"{100.0,200.0}", @"{3.0,200.0}"]; // draw the path UIBezierPath *aPath = [UIBezierPath bezierPath]; for (NSString *pointString in pointArray) { if ([pointArray indexOfObject:pointString] == 0) [aPath moveToPoint:CGPointFromString(pointString)]; else [aPath addLineToPoint:CGPointFromString(pointString)]; } [aPath closePath];