Skip to content

Instantly share code, notes, and snippets.

@juliensagot
Last active September 26, 2023 23:11
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save juliensagot/9749c3a1df28c38fb9f9 to your computer and use it in GitHub Desktop.
Save juliensagot/9749c3a1df28c38fb9f9 to your computer and use it in GitHub Desktop.
Convert NSBezierPath to CGPath (Swift 5.1, Xcode 11.3 (11C29))
extension NSBezierPath {
/// A `CGPath` object representing the current `NSBezierPath`.
var cgPath: CGPath {
let path = CGMutablePath()
let points = UnsafeMutablePointer<NSPoint>.allocate(capacity: 3)
if elementCount > 0 {
var didClosePath = true
for index in 0..<elementCount {
let pathType = element(at: index, associatedPoints: points)
switch pathType {
case .moveTo:
path.move(to: points[0])
case .lineTo:
path.addLine(to: points[0])
didClosePath = false
case .curveTo:
path.addCurve(to: points[2], control1: points[0], control2: points[1])
didClosePath = false
case .closePath:
path.closeSubpath()
didClosePath = true
@unknown default:
break
}
}
if !didClosePath { path.closeSubpath() }
}
points.deallocate()
return path
}
}
@rasronin
Copy link

Pure awesomeness. Thank you.

@kaishin
Copy link

kaishin commented May 24, 2015

👍

@keefo
Copy link

keefo commented Jan 17, 2021

Alert, This code has a bug at line 24.

path.addCurve(to: CGPoint(x: points[0].x, y: points[0].y), control1: control1, control2: control2)

It should be

path.addCurve(to: points[2], control1: points[0], control2: points[1])

@juliensagot
Copy link
Author

@keefo Do you have an example of a non-working path so I can check?

@dceddia
Copy link

dceddia commented Apr 11, 2021

Thanks for this!

@juliensagot I was curious about @keefo's comment and found that Apple's docs say it is indeed (and confusingly) ordered 2, 0, 1 instead of 0, 1, 2. Check this docs page - the relevant part is the last sentence in the Discussion section:

For curve operations, the order of the points is controlPoint1 (points[0]), controlPoint2 (points[1]), endPoint (points[2]).

@keefo
Copy link

keefo commented Apr 11, 2021

Hi @juliensagot

@keefo Do you have an example of a non-working path so I can check?

I use this original code to draw a circle in my project.
it gives me weird results. So, I checked the Apple doc and updated line 24. It works now in my project.

https://github.com/keefo/NeewerLite/blob/main/NeewerLite/NeewerLite/Common/ColorWheel.swift#L112
https://github.com/keefo/NeewerLite/blob/main/NeewerLite/NeewerLite/Common/NSBezierPathExtensions.swift#L28

@juliensagot
Copy link
Author

@keefo @dceddia Thank you guys! I've updated the gist 🙏

@pkclsoft
Copy link

Why do you call closeSubPath() at line 31? If you want a path that is not closed, then this causes issues.

@iSapozhnik
Copy link

If you are targeting macOS 14 then cgPath is already part of the NSBezierPath API - https://developer.apple.com/videos/play/wwdc2023/10054/?time=676

@pkclsoft
Copy link

If you are targeting macOS 14 then cgPath is already part of the NSBezierPath API - https://developer.apple.com/videos/play/wwdc2023/10054/?time=676

LOL, do you mean "finally"? It is great that it's been added, but this would have been a logical thing to have had for a long time now. Thanks for pointing that out though, as there are new elements in the case that are not handled by this gist that the new built-in probably does.

@iSapozhnik
Copy link

If you are targeting macOS 14 then cgPath is already part of the NSBezierPath API - https://developer.apple.com/videos/play/wwdc2023/10054/?time=676

LOL, do you mean "finally"? It is great that it's been added, but this would have been a logical thing to have had for a long time now. Thanks for pointing that out though, as there are new elements in the case that are not handled by this gist that the new built-in probably does.

Even though I did not mean "finally" but I share your view. It's sad to see how appkit is still years behind uikit but on the other hand I'm happy that there is at least some work is going on improving appkit API.

@juliensagot
Copy link
Author

If you are targeting macOS 14 then cgPath is already part of the NSBezierPath API - https://developer.apple.com/videos/play/wwdc2023/10054/?time=676

Nice catch! What a time to be alive 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment