Obtener longitud total CGPath

Tengo muchos UIBezierPath que estoy animando con CALyerAnimation @ "StrokeStart" y @ "strokeEnd".

Deseo que la animation tenga la misma velocidad para todas las routes, así que creo que podría usar la longitud de la ruta en:

DISTANCIA / TIEMPO = VELOCIDAD

¿Hay alguna manera de calcular la ruta "de longitud"?

Gracias

Shani

Si bien puede calcular la longitud de la ruta Bezier integrandola numéricamente, se puede hacer mucho más fácil dividiendo la ruta en segmentos lineales. Si los segmentos son lo suficientemente pequeños, el error de aproximación debe ser descuidado, especialmente si solo estás intentando animarlo.

Te mostraré la function para curvas cuádruples, pero también puedes incorporar fácilmente la solución para curvas cúbicas:

- (float) bezierCurveLengthFromStartPoint: (CGPoint) start toEndPoint: (CGPoint) end withControlPoint: (CGPoint) control { const int kSubdivisions = 50; const float step = 1.0f/(float)kSubdivisions; float totalLength = 0.0f; CGPoint prevPoint = start; // starting from i = 1, since for i = 0 calulated point is equal to start point for (int i = 1; i <= kSubdivisions; i++) { float t = i*step; float x = (1.0 - t)*(1.0 - t)*start.x + 2.0*(1.0 - t)*t*control.x + t*t*end.x; float y = (1.0 - t)*(1.0 - t)*start.y + 2.0*(1.0 - t)*t*control.y + t*t*end.y; CGPoint diff = CGPointMake(x - prevPoint.x, y - prevPoint.y); totalLength += sqrtf(diff.x*diff.x + diff.y*diff.y); // Pythagorean prevPoint = CGPointMake(x, y); } return totalLength; } 

EDITAR

Si no tiene acceso a los puntos de control de ruta (supongamos que creó la ruta mediante arcos), siempre puede acceder a las curvas subyacentes de Bezier mediante la function CGPathApply :

 - (void) testPath { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointZero]; [path addQuadCurveToPoint:CGPointMake(1, 1) controlPoint:CGPointMake(-2, 2)]; CGPathRef p = path.CGPath; CGPathApply(p, nil, pathFunction); } void pathFunction(void *info, const CGPathElement *element) { if (element->type == kCGPathElementAddQuadCurveToPoint) { CGPoint p; p = element->points[0]; // control point NSLog(@"%lg %lg", px, py); p = element->points[1]; // end point NSLog(@"%lg %lg", px, py); } // check other cases as well! } 

Tenga en count que no proporciona el punto de inicio de la ruta, pero es fácil seguirlo por su count.

Recientemente tuve el mismo problema, pero en este caso necesitaba la longitud completa del path independientemente de los elementos involucrados. Por ejemplo, líneas, curvas, arcos, y la solución anterior no funcionó para mí, se me ocurrió una solución general.

Aquí el código completo escrito en swift UIBezierPath.length

Esta es la extensión que contiene la propiedad de length .

 extension UIBezierPath{ var length: CGFloat{ var pathLength:CGFloat = 0.0 var current = CGPointZero var first = CGPointZero self.CGPath.forEach{ element in pathLength += element.distance(to: current, startPoint: first) if element.type == .MoveToPoint{ first = element.point } if element.type != .CloseSubpath{ current = element.point } } return pathLength } } // eg let path = UIBezierPath() path.lineWidth = 10 path.moveToPoint(CGPoint(x: 10, y: 10)) path.addArcWithCenter( CGPoint(x: 100,y: 50), radius: 50, startAngle: CGFloat(140.0.degrees), endAngle: CGFloat(40.0.degrees), clockwise: false ) path.addLineToPoint(CGPoint(x: 150, y: 30)) path.closePath() print( path.length )