In Apple docs they give you code of how to convert NSBezierPath to CGPathRef. I need to convert the other way around, from CGPathRef to NSBezierPath. UIBezierPath has a property called cgPath so if I was working on iPhone that would not be a problem, but I'm working on MacOS.
This must be an old question, and I was sure to find an answer on Internet but no luck. Could be I'm missing something. Any help appreciated.
Old question but I'm sure this will still be helpful for others. (You didn't specify Objective-C or Swift; this is an Objective-C answer.)
You can convert a CGPathRef
to an NSBezierPath
using CGPathApply()
with an applier function callback that translates the CGPathRef
points to NSBezierPath
points. The only tricky part is the conversion from CGPathRef
's quadratic curves to NSBezierPath
's cubic curves but there's an equation for that:
Any quadratic spline can be expressed as a cubic (where the cubic term is zero). The end points of the cubic will be the same as the quadratic's.
CP0 = QP0 CP3 = QP2
The two control points for the cubic are:
CP1 = QP0 + 2/3 * (QP1-QP0) CP2 = QP2 + 2/3 * (QP1-QP2)
... There is a slight error introduced due to rounding, but it is usually not noticeable.
Using the equation above, here's an NSBezierPath
category for converting from 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
Called like so:
//...get cgPath (CGPathRef) from somewhere
NSBezierPath *bezierPath = [NSBezierPath JNS_bezierPathWithCGPath:cgPath];