如何在NSBezierPath
到CGPath
之间进行转换。
感谢。
答案 0 :(得分:36)
直接来自Apple文档:Creating a CGPathRef From an NSBezierPath Object
以下是相关代码。
@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
rdar://15758302:NSBezierPath到CGPath。
答案 1 :(得分:21)
Xcode 8 GM中的语法进一步简化,代码修改自上面的rob-mayoff的回答。使用这个和addLine(to point: CGPoint)
的帮助器我正在共享绘图代码跨平台。
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
}
}
答案 2 :(得分:11)
这适用于Swift 3.1及更高版本:
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
}
}
答案 3 :(得分:5)
如果其他人发现需要它,那么这是一个Swift版本:
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
}
答案 4 :(得分:2)
我无法弄清楚为什么接受的答案会增加一些复杂的近路逻辑(可能在某些情况下需要),但对于那些只需要 pristine 转换路径的人来说,这是该代码的清理版本,实现为常规方法:
- (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;
}
顺便说一句,我需要这个来实现“NSBezierPath stroke包含点”方法。
我已经查找了这个转换来调用CGPathCreateCopyByStrokingPath()
,将NSBezierPath
笔画大纲转换为常规路径,因此您也可以测试笔画上的匹配,这是解决方案:
// 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);
这类似于Qt的QPainterPathStroker
,但对于NSBezierPath
。
答案 5 :(得分:2)
c#Xamarin
private CGPath convertNSBezierPathToCGPath(NSBezierPath sourcePath)
{
CGPath destinationPath = new CGPath();
int i, numElements;
// Then draw the path elements.
numElements = (int)Convert.ToInt64(sourcePath.ElementCount);
if (numElements > 0)
{
CGPath path = new CGPath();
CGPoint[] points;
bool didClosePath = true;
for (i = 0; i < numElements; i++)
{
switch (sourcePath.ElementAt(i, out points))
{
case NSBezierPathElement.MoveTo:
path.MoveToPoint(points[0]);
break;
case NSBezierPathElement.LineTo:
path.MoveToPoint(points[0]);
didClosePath = false;
break;
case NSBezierPathElement.CurveTo:
path.AddCurveToPoint(cp1: points[0], cp2: points[1], points[2]);
didClosePath = false;
break;
case NSBezierPathElement.ClosePath:
path.CloseSubpath();
didClosePath = true;
break;
}
}
if (!didClosePath)
path.CloseSubpath();
destinationPath = new CGPath(path);
}
return destinationPath;
}
答案 6 :(得分:0)