3

在 Apple 文档中,他们为您提供了如何将 NSBezierPath 转换为 CGPathRef 的代码。我需要反过来转换,从 CGPathRef 到 NSBezierPath。UIBezierPath 有一个名为 cgPath 的属性,所以如果我在 iPhone 上工作这不会有问题,但我在 MacOS 上工作。

这一定是一个老问题,我肯定会在互联网上找到答案,但没有运气。可能是我错过了什么。任何帮助表示赞赏。

4

1 回答 1

6

老问题,但我相信这仍然对其他人有帮助。(您没有指定 Objective-C 或 Swift;这是一个 Objective-C 的答案。)

您可以使用将点转换为点的应用程序函数回调将 a 转换CGPathRefNSBezierPathusing 。唯一棘手的部分是从的二次曲线到的三次曲线的转换,但有一个方程式CGPathApply()CGPathRefNSBezierPathCGPathRefNSBezierPath

任何二次样条都可以表示为三次(三次项为零)。三次方的端点将与二次方的端点相同。

 CP0 = QP0
 CP3 = QP2 

三次方的两个控制点是:

 CP1 = QP0 + 2/3 * (QP1-QP0)
 CP2 = QP2 + 2/3 * (QP1-QP2)

...由于四舍五入而引入了轻微错误,但通常不明显。

使用上面的等式,这是一个NSBezierPath用于转换的类别CGPathRef

NSBezierPath+BezierPathWithCGPath.h

@interface NSBezierPath (BezierPathWithCGPath)
+ (NSBezierPath *)JNS_bezierPathWithCGPath:(CGPathRef)cgPath; //prefixed as Apple may add bezierPathWithCGPath: method someday
@end

NSBezierPath+BezierPathWithCGPath.m

static void CGPathToBezierPathApplierFunction(void *info, const CGPathElement *element) {
    NSBezierPath *bezierPath = (__bridge NSBezierPath *)info;
    CGPoint *points = element->points;
    switch(element->type) {
        case kCGPathElementMoveToPoint: [bezierPath moveToPoint:points[0]]; break;
        case kCGPathElementAddLineToPoint: [bezierPath lineToPoint:points[0]]; break;
        case kCGPathElementAddQuadCurveToPoint: {
            NSPoint qp0 = bezierPath.currentPoint, qp1 = points[0], qp2 = points[1], cp1, cp2;
            CGFloat m = (2.0 / 3.0);
            cp1.x = (qp0.x + ((qp1.x - qp0.x) * m));
            cp1.y = (qp0.y + ((qp1.y - qp0.y) * m));
            cp2.x = (qp2.x + ((qp1.x - qp2.x) * m));
            cp2.y = (qp2.y + ((qp1.y - qp2.y) * m));
            [bezierPath curveToPoint:qp2 controlPoint1:cp1 controlPoint2:cp2];
            break;
        }
        case kCGPathElementAddCurveToPoint: [bezierPath curveToPoint:points[2] controlPoint1:points[0] controlPoint2:points[1]]; break;
        case kCGPathElementCloseSubpath: [bezierPath closePath]; break;
    }
}

@implementation NSBezierPath (BezierPathWithCGPath)
+ (NSBezierPath *)JNS_bezierPathWithCGPath:(CGPathRef)cgPath {
    NSBezierPath *bezierPath = [NSBezierPath bezierPath];
    CGPathApply(cgPath, (__bridge void *)bezierPath, CGPathToBezierPathApplierFunction);
    return bezierPath;
}
@end

像这样调用:

//...get cgPath (CGPathRef) from somewhere
NSBezierPath *bezierPath = [NSBezierPath JNS_bezierPathWithCGPath:cgPath];
于 2018-02-27T14:32:52.363 回答