¿Cómo get una list de puntos de un UIBezierPath?

Tengo un UIBezierPath del que necesito tomar una list de puntos.

En Qt hay una function llamada pointAtPercent que se ajusta a mis necesidades, pero no puedo encontrar nada equivalente en Objective-C .

¿Alguien sabe como hacer esto?

Puedes probar esto:

 UIBezierPath *yourPath; // Assume this has some points in it CGPath yourCGPath = yourPath.CGPath; NSMutableArray *bezierPoints = [NSMutableArray array]; CGPathApply(yourCGPath, bezierPoints, MyCGPathApplierFunc); 

La function del aplicador de ruta se entregará a cada uno de los elementos de la ruta de acceso.
Echa un vistazo a CGPathApplierFunction y CGPathApply .

La function del aplicador de ruta podría verse algo como esto

 void MyCGPathApplierFunc (void *info, const CGPathElement *element) { NSMutableArray *bezierPoints = (NSMutableArray *)info; CGPoint *points = element->points; CGPathElementType type = element->type; switch(type) { case kCGPathElementMoveToPoint: // contains 1 point [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; break; case kCGPathElementAddLineToPoint: // contains 1 point [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; break; case kCGPathElementAddQuadCurveToPoint: // contains 2 points [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]]; break; case kCGPathElementAddCurveToPoint: // contains 3 points [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]]; [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]]; [bezierPoints addObject:[NSValue valueWithCGPoint:points[2]]]; break; case kCGPathElementCloseSubpath: // contains no point break; } } 

@Moritz ¡Gran respuesta! Para encontrar una solución como Swift, tropecé con un enfoque en esta publicación e implementé una extensión CGPath como esta para get los puntos de la ruta:

 extension CGPath { func points() -> [CGPoint] { var bezierPoints = [CGPoint]() self.forEach({ (element: CGPathElement) in let numberOfPoints: Int = { switch element.type { case .MoveToPoint, .AddLineToPoint: // contains 1 point return 1 case .AddQuadCurveToPoint: // contains 2 points return 2 case .AddCurveToPoint: // contains 3 points return 3 case .CloseSubpath: return 0 } }() for index in 0..<numberOfPoints { let point = element.points[index] bezierPoints.append(point) } }) return bezierPoints } func forEach(@noescape body: @convention(block) (CGPathElement) -> Void) { typealias Body = @convention(block) (CGPathElement) -> Void func callback(info: UnsafeMutablePointer<Void>, element: UnsafePointer<CGPathElement>) { let body = unsafeBitCast(info, Body.self) body(element.memory) } let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer<Void>.self) CGPathApply(self, unsafeBody, callback) } } 

Creo que estabas tratando de hacer algo como esto:

https://math.stackexchange.com/questions/26846/is-there-an-explicit-form-for-cubic-bézier-curves

y=u0(1−x^3)+3u1(1−x^2)x+3u2(1−x)x^2+u3x^3

Este es mi método para imprimir todos los valores de esa function.

 - (void)logXY { float u0 = 0; float u1 = 0.05; float u2 = 0.25; float u3 = 1; for (float x = 0; x <= 10.0; x = x + 0.1) { float y = u0 * (1 - x * x * x) + 3 * u1 * (1 - x * x) * x + 3 * u2 * (1 - x) * x * x + u3 * x * x * x; NSLog(@"x: %f\ty: %f", x, y); } } 

Y la salida es:

 x: 0.000000 y: 0.000000 x: 0.100000 y: 0.022600 x: 0.200000 y: 0.060800 x: 0.300000 y: 0.115200 x: 0.400000 y: 0.186400 x: 0.500000 y: 0.275000 x: 0.600000 y: 0.381600 x: 0.700000 y: 0.506800 x: 0.800000 y: 0.651200 x: 0.900000 y: 0.815400 x: 1.000000 y: 1.000000 x: 1.100000 y: 1.205600 x: 1.200000 y: 1.432800 x: 1.300000 y: 1.682200 x: 1.400000 y: 1.954401 x: 1.500000 y: 2.250001 x: 1.600000 y: 2.569601 x: 1.700000 y: 2.913801 x: 1.800000 y: 3.283201 x: 1.900000 y: 3.678401 x: 2.000000 y: 4.100001 x: 2.100000 y: 4.548601 x: 2.200000 y: 5.024800 x: 2.300000 y: 5.529200 x: 2.400000 y: 6.062399 x: 2.500000 y: 6.625000 x: 2.600000 y: 7.217597 x: 2.700000 y: 7.840797 x: 2.799999 y: 8.495197 x: 2.899999 y: 9.181394 x: 2.999999 y: 9.899996 

Escribí un artículo sobre cómo encontrar el punto más cercano en UIBezierPath , que es muy similar a lo que está buscando el OP. También he creado un proyecto de demostración en Swift: https://github.com/andrew8712/BezierPathClosestPoint

He modificado la publicación de @FBente para trabajar con swift 3

 extension CGPath { func points() -> [CGPoint] { var bezierPoints = [CGPoint]() self.forEach(body: { (element: CGPathElement) in let numberOfPoints: Int = { switch element.type { case .moveToPoint, .addLineToPoint: // contains 1 point return 1 case .addQuadCurveToPoint: // contains 2 points return 2 case .addCurveToPoint: // contains 3 points return 3 case .closeSubpath: return 0 } }() for index in 0..<numberOfPoints { let point = element.points[index] bezierPoints.append(point) } }) return bezierPoints } func forEach( body: @convention(block) (CGPathElement) -> Void) { typealias Body = @convention(block) (CGPathElement) -> Void func callback(info: UnsafeMutableRawPointer, element: UnsafePointer<CGPathElement>) { let body = unsafeBitCast(info, to: Body.self) body(element.pointee) } let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self) self.apply(info: unsafeBody, function: callback as! CGPathApplierFunction) } } 

Actualización de la extensión de FBente para Swift 3:

 extension CGPath { func points() -> [CGPoint] { var bezierPoints = [CGPoint]() self.forEach({ (element: CGPathElement) in let numberOfPoints: Int = { switch element.type { case .moveToPoint, .addLineToPoint: // contains 1 point return 1 case .addQuadCurveToPoint: // contains 2 points return 2 case .addCurveToPoint: // contains 3 points return 3 case .closeSubpath: return 0 } }() for index in 0..<numberOfPoints { let point = element.points[index] bezierPoints.append(point) } }) return bezierPoints } func forEach(_ body: @convention(block) (CGPathElement) -> Void) { typealias Body = @convention(block) (CGPathElement) -> Void func callback(info: UnsafeMutableRawPointer, element: UnsafePointer<CGPathElement>) { let body = unsafeBitCast(info, to: Body.self) body(element.pointee) } let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self) self.apply(info: unsafeBody, function: callback as! CGPathApplierFunction) } }