Ответ 1
Пока вы можете рассчитать длину пути Безье, интегрируя его численно, это можно сделать намного проще, разделив путь на линейные сегменты. Если сегменты достаточно малы, ошибка аппроксимации должна быть пренебрежимой, особенно если вы просто пытаетесь ее оживить.
Я покажу вам функцию для четырехмерных кривых, но вы также можете легко включить решение для кубических кривых:
- (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;
}
ИЗМЕНИТЬ
Если у вас нет доступа к контрольным точкам маршрута (например, вы создали путь с использованием дуг), вы всегда можете получить доступ к кривым Безье, используя функцию 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", p.x, p.y);
p = element->points[1]; // end point
NSLog(@"%lg %lg", p.x, p.y);
}
// check other cases as well!
}
Обратите внимание, что он не предоставляет начальную точку пути, но легко отслеживать ее самостоятельно.