How can I convert NSBezierPath to CGPath

How can I convert between NSBezierPath to CGPath .

Thank.

+29
cocoa nsbezierpath cgpath
Nov 29 '09 at 13:38
source share
7 answers

To the right of the Apple documentation: creating a CGPathRef from an NSBezierPath object

Here is the relevant code.

 @implementation NSBezierPath (BezierPathQuartzUtilities) // This method works only in OS X v10.2 and later. - (CGPathRef)quartzPath { int i, numElements; // Need to begin a path here. CGPathRef immutablePath = NULL; // Then draw the path elements. numElements = [self elementCount]; if (numElements > 0) { CGMutablePathRef path = CGPathCreateMutable(); NSPoint points[3]; BOOL didClosePath = YES; for (i = 0; i < numElements; i++) { switch ([self elementAtIndex:i associatedPoints:points]) { case NSMoveToBezierPathElement: CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); break; case NSLineToBezierPathElement: CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); didClosePath = NO; break; case NSCurveToBezierPathElement: CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y); didClosePath = NO; break; case NSClosePathBezierPathElement: CGPathCloseSubpath(path); didClosePath = YES; break; } } // Be sure the path is closed or Quartz may not do valid hit detection. if (!didClosePath) CGPathCloseSubpath(path); immutablePath = CGPathCreateCopy(path); CGPathRelease(path); } return immutablePath; } @end 

Bug reporter

rdar: // 15758302 : NSBezierPath path to CGPath.

+36
Dec 24 '09 at 0:03
source share

The syntax in Xcode 8 GM has been further simplified, the code has been modified from the rob-mayoff answer above. Using this and the helper for addLine(to point: CGPoint) , I use a cross platform to draw code.

 extension NSBezierPath { public var cgPath: CGPath { let path = CGMutablePath() var points = [CGPoint](repeating: .zero, count: 3) for i in 0 ..< self.elementCount { let type = self.element(at: i, associatedPoints: &points) switch type { case .moveToBezierPathElement: path.move(to: points[0]) case .lineToBezierPathElement: path.addLine(to: points[0]) case .curveToBezierPathElement: path.addCurve(to: points[2], control1: points[0], control2: points[1]) case .closePathBezierPathElement: path.closeSubpath() } } return path } } 
+21
Sep 08 '16 at 7:35
source share

This works in Swift 3.1 and later:

 import AppKit public extension NSBezierPath { public var cgPath: CGPath { let path = CGMutablePath() var points = [CGPoint](repeating: .zero, count: 3) for i in 0 ..< self.elementCount { let type = self.element(at: i, associatedPoints: &points) switch type { case .moveToBezierPathElement: path.move(to: points[0]) case .lineToBezierPathElement: path.addLine(to: points[0]) case .curveToBezierPathElement: path.addCurve(to: points[2], control1: points[0], control2: points[1]) case .closePathBezierPathElement: path.closeSubpath() } } return path } } 
+11
Aug 09 '16 at 21:15
source share

Here is the Swift version if anyone else needs it:

 extension IXBezierPath { // Adapted from : https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Paths/Paths.html#//apple_ref/doc/uid/TP40003290-CH206-SW2 // See also: http://www.dreamincode.net/forums/topic/370959-nsbezierpath-to-cgpathref-in-swift/ func CGPath(forceClose forceClose:Bool) -> CGPathRef? { var cgPath:CGPathRef? = nil let numElements = self.elementCount if numElements > 0 { let newPath = CGPathCreateMutable() let points = NSPointArray.alloc(3) var bDidClosePath:Bool = true for i in 0 ..< numElements { switch elementAtIndex(i, associatedPoints:points) { case NSBezierPathElement.MoveToBezierPathElement: CGPathMoveToPoint(newPath, nil, points[0].x, points[0].y ) case NSBezierPathElement.LineToBezierPathElement: CGPathAddLineToPoint(newPath, nil, points[0].x, points[0].y ) bDidClosePath = false case NSBezierPathElement.CurveToBezierPathElement: CGPathAddCurveToPoint(newPath, nil, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y ) bDidClosePath = false case NSBezierPathElement.ClosePathBezierPathElement: CGPathCloseSubpath(newPath) bDidClosePath = true } if forceClose && !bDidClosePath { CGPathCloseSubpath(newPath) } } cgPath = CGPathCreateCopy(newPath) } return cgPath } 
+5
Jan 06 '16 at 14:57
source share

I like this library, which adds UIKit functionality to MacOS. Once enabled, you can install and read CGPath for NSBezierPath https://github.com/iccir/XUIKit

+3
Dec 20 '13 at 8:14
source share

I canโ€™t understand why the accepted answer adds some complex logic to the close path (maybe it is necessary in some circumstances), but for those who just need the initial path conversion, the version of this code that is implemented as a regular method is cleared here:

 - (CGMutablePathRef)CGPathFromPath:(NSBezierPath *)path { CGMutablePathRef cgPath = CGPathCreateMutable(); NSInteger n = [path elementCount]; for (NSInteger i = 0; i < n; i++) { NSPoint ps[3]; switch ([path elementAtIndex:i associatedPoints:ps]) { case NSMoveToBezierPathElement: { CGPathMoveToPoint(cgPath, NULL, ps[0].x, ps[0].y); break; } case NSLineToBezierPathElement: { CGPathAddLineToPoint(cgPath, NULL, ps[0].x, ps[0].y); break; } case NSCurveToBezierPathElement: { CGPathAddCurveToPoint(cgPath, NULL, ps[0].x, ps[0].y, ps[1].x, ps[1].y, ps[2].x, ps[2].y); break; } case NSClosePathBezierPathElement: { CGPathCloseSubpath(cgPath); break; } default: NSAssert(0, @"Invalid NSBezierPathElement"); } } return cgPath; } 



Btw, I needed this to implement the "NSBezierPath stroke contains a point" method.

I was looking for this conversion to call CGPathCreateCopyByStrokingPath() , which converts the NSBezierPath hatch NSBezierPath to the normal path, so that you can also check hits on hits, and here is the solution:

 // stroke (0,0) to (10,0) width 5 --> rect (0, -2.5) (10 x 5) NSBezierPath *path = [[NSBezierPath alloc] init]; [path moveToPoint:NSMakePoint(0.0, 0.0)]; [path lineToPoint:NSMakePoint(10.0, 0.0)]; [path setLineWidth:5.0]; CGMutablePathRef cgPath = [self CGPathFromPath:path]; CGPathRef strokePath = CGPathCreateCopyByStrokingPath(cgPath, NULL, [path lineWidth], [path lineCapStyle], [path lineJoinStyle], [path miterLimit]); CGPathRelease(cgPath); NSLog(@"%@", NSStringFromRect(NSRectFromCGRect(CGPathGetBoundingBox(strokePath)))); // {{0, -2.5}, {10, 5}} CGPoint point = CGPointMake(1.0, 1.0); BOOL hit = CGPathContainsPoint(strokePath, NULL, point, (bool)[path windingRule]); NSLog(@"%@: %@", NSStringFromPoint(NSPointFromCGPoint(point)), (hit ? @"yes" : @"no")); // {1, 1}: yes CGPathRelease(strokePath); 

This is similar to QPainterPathStroker from Qt, but for NSBezierPath .

+2
Feb 07 '17 at 15:46
source share

For macOS, it's better to use - CGMutablePath

But, if you want cgPath for NSBezierPath :

Swift 5.0

 extension NSBezierPath { var cgPath: CGPath { let path = CGMutablePath() var points = [CGPoint](repeating: .zero, count: 3) for i in 0 ..< self.elementCount { let type = self.element(at: i, associatedPoints: &points) switch type { case .moveTo: path.move(to: points[0]) case .lineTo: path.addLine(to: points[0]) case .curveTo: path.addCurve(to: points[2], control1: points[0], control2: points[1]) case .closePath: path.closeSubpath() @unknown default: break } } return path } } 
0
May 6 '19 at
source share



All Articles