Erica Sadun предоставляет набор полезных функций для решения UIBezierPath и CGPathRef.
Этот код используется в this book.
Она не обеспечивает реализацию уплощения CGPathRef, но это можно легко сделать с помощью функций, которые можно найти здесь: https://github.com/erica/iOS-Drawing/blob/master/C05/Quartz%20Book%20Pack/Bezier/BezierFunctions.m
Particulary, эти функции помогут Дискретизируют нелинейные сегменты Безье:
float CubicBezier(float t, float start, float c1, float c2, float end)
float QuadBezier(float t, float start, float c1, float end)
CGPoint CubicBezierPoint(CGFloat t, CGPoint start, CGPoint c1, CGPoint c2, CGPoint end);
CGPoint QuadBezierPoint(CGFloat t, CGPoint start, CGPoint c1, CGPoint end);
В принципе, инициализируйте пустой CGMutablePathRef и для каждого элемента CGPath в исходном пути, либо скопируйте его, если он линейный, либо дискретизируйте его в зависимости от степени сегмента Безье.
Вы также можете применить Ramer–Douglas–Peucker algorithm для удаления ненужных точек.
Вы также можете использовать: - (NSArray *) interpolatedPathPoints
, который возвращает NSArray точек, которые могут быть использованы для построения аппроксимации пути. Алгоритм наивен, поэтому вам нужно упростить результат в случае, например, когда кубический путь Безье будет линейным (если контрольные точки выровнены); как и раньше, алгоритм Рамера-Дугласа-Пьюкера выполняет эту работу.
Вот как выглядит фактическая дискретизация. Код не является самодостаточным, вам придется использовать все зависимости.
- (NSArray *) interpolatedPathPoints
{
NSMutableArray *points = [NSMutableArray array];
BezierElement *current = nil;
int overkill = 3;
for (BezierElement *element in self.elements)
{
switch (element.elementType)
{
case kCGPathElementMoveToPoint:
case kCGPathElementAddLineToPoint:
[points addObject:[NSValue valueWithCGPoint:element.point]];
current = element;
break;
case kCGPathElementCloseSubpath:
current = nil;
break;
case kCGPathElementAddCurveToPoint:
{
for (int i = 1; i < NUMBER_OF_BEZIER_SAMPLES * overkill; i++)
{
CGFloat percent = (CGFloat) i/(CGFloat) (NUMBER_OF_BEZIER_SAMPLES * overkill);
CGPoint p = CubicBezierPoint(percent, current.point, element.controlPoint1, element.controlPoint2, element.point);
[points addObject:[NSValue valueWithCGPoint:p]];
}
[points addObject:[NSValue valueWithCGPoint:element.point]];
current = element;
break;
}
case kCGPathElementAddQuadCurveToPoint:
{
for (int i = 1; i < NUMBER_OF_BEZIER_SAMPLES * overkill; i++)
{
CGFloat percent = (CGFloat) i/(CGFloat) (NUMBER_OF_BEZIER_SAMPLES * overkill);
CGPoint p = QuadBezierPoint(percent, current.point, element.controlPoint1, element.point);
[points addObject:[NSValue valueWithCGPoint:p]];
}
[points addObject:[NSValue valueWithCGPoint:element.point]];
current = element;
break;
}
}
}
return points;
}
Код принадлежит Эрика Садун. Смотрите здесь для полной реализации: https://github.com/erica/iOS-Drawing
Rob Napier также писал о кривых Безье в iOS 6 Pushing the limits, Глава 26 Fancy Text Layout.Он не пытался сгладить полный UIBezierPath, только один кубический маршрут Безье, определенный четырьмя точками, но на самом деле это то же самое (дискретизация пути Безье). . Также вы можете найти эту статью интересной: http://robnapier.net/faster-bezier
Использование ' UIBezierPath', устанавливает ли 'path.flatness = 1' то, что вы ищете? –
К сожалению, нет, потому что в качестве свойства чертежа «плоскость» влияет только на то, как визуализируется кривая, а не на то, как она хранится. –
Хм ... В этом случае ваш лучший выбор - это, вероятно, перебрать все точки с помощью 'CGPathApply' и построить новый путь, используя соответствующие функции, чтобы получить плоский эффект. –