简单地说,我正在寻找一个等价于NSBezierPath的-bezierPathByFlatteningPath,可以在iOS上使用。对于我来说,这是一个直接处理CGPath的函数还是UIBezierPath上的一个方法并不重要,因为这两个函数可以很容易地来回转换。CGPath参考和UIBezierPath类引用都不表示存在任何这样的函数或方法。
另外:我知道CGPath的CGPathApply函数,而且我缺乏时间和技能来通过在CGPathApplierFunction中迭代路径的元素来实现自己的扁平算法。我正在寻找一个现有的解决方案--一个更实用的函数,一个UIBezierPath上的类别,等等。
发布于 2014-03-28 16:49:56
Erica提供了一组处理UIBezierPath和CGPathRef的有用函数。
此代码用于这本书。
她没有提供CGPathRef扁平化的实现,但是可以很容易地使用可以在这里找到的函数:https://github.com/erica/iOS-Drawing/blob/master/C05/Quartz%20Book%20Pack/Bezier/BezierFunctions.m。
特别是,这些函数将有助于离散非线性Bezier段:
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元素,如果它是线性的,就复制它,或者根据Bezier段的程度来离散它。
您还可能希望应用Ramer-Douglas-Peucker算法来删除不必要的点。
您还可以直接使用:- (NSArray *) interpolatedPathPoints,它返回点的NSArray,可以用来构建路径的近似。算法太天真了,所以你必须简化结果,例如,三次Bezier路径是线性的(如果控制点对齐);就像以前一样,Ramer Peucker算法完成了这项工作。
下面是实际的离散化。代码不是自包含的,您必须使用所有的依赖项。
- (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
罗伯纳皮尔还写了贝塞尔曲线在iOS 6突破极限,第26章幻想文本布局。他不是试图平平一个完整的UIBezierPath,只有一个用四点定义的三次Bezier路径,但实际上这是完全相同的事情(离散Bezier路径),您可能会发现本文很有趣:http://robnapier.net/faster-bezier
发布于 2013-09-18 05:53:59
基本上,您想要做的是将路径转换为将所有曲线和quadCurves替换为直线。您可以使用CGPathApply轻松地完成此操作,并将其包装在UIBezierPath上的一个类别中。
#import <CoreGraphics/CoreGraphics.h>
@interface UIBezierPath (Flatten)
- (UIBezierPath *)bezierPathByFlatteningPath;
@end
void __flattenBezierPath(void *target, const CGPathElement *element) {
UIBezierPath *newPath = (__bridge UIBezierPath *)(target);
switch (element->type) {
case kCGPathElementMoveToPoint:
[newPath moveToPoint:element->points[0]];
break;
case kCGPathElementAddLineToPoint:
[newPath addLineToPoint:element->points[0]];
break;
case kCGPathElementCloseSubpath:
[newPath closePath];
break;
case kCGPathElementAddCurveToPoint:
[newPath addLineToPoint:element->points[2]];
break;
case kCGPathElementAddQuadCurveToPoint:
[newPath addLineToPoint:element->points[1]];
break;
}
}
@implementation UIBezierPath (Flatten)
- (UIBezierPath *)bezierPathByFlatteningPath
{
UIBezierPath *newPath = [UIBezierPath bezierPath];
CGPathApply(self.CGPath, (__bridge void *)(newPath), __flattenBezierPath);
return newPath;
}
@end效果如下,绿色是原始路径,红色是该方法生成的路径,黄色是使用setFlatness:MAXFLOAT生成的路径。

https://stackoverflow.com/questions/18858011
复制相似问题