Как я могу преобразовать между NSBezierPath
в CGPath
.
Спасибо.
Как я могу преобразовать между NSBezierPath
в CGPath
.
Спасибо.
Прямо из документации Apple: Создание CGPathRef из объекта NSBezierPath
Вот соответствующий код.
@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.
CGPath
к NSBezierPath
.
- person Erik Aigner; 05.03.2016
Синтаксис в 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 ..< elementCount {
let type = 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:
continue
}
}
return path
}
}
Это работает в 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
}
}
CGPath
, поэтому можно было бы использовать более стандартный синтаксис CGPath(from: bezierPath)
- person Ky Leggiero; 04.12.2017
NSColor
имеет свойство cgColor
, NSGraphicsContext
имеет свойство cgContext
, а NSColorSpace
имеет свойство cgColorSpace
. NSImage
имеет метод `cgImage(forProposedRect:context:hints:)`.
- person rob mayoff; 04.12.2017
CGPath
. Если вы попытаетесь объявить его, вы получите сообщение об ошибке «Удобные инициализаторы не поддерживаются в расширениях типов CF».
- person rob mayoff; 30.05.2019
Для macOS лучше использовать - CGMutablePath
Но если вы хотите cgPath
вместо NSBezierPath
:
Свифт 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
}
}
Вот версия 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
}
Я не могу понять, почему принятый ответ добавляет некоторую сложную логику близкого пути (возможно, это необходимо при некоторых обстоятельствах), но для тех, кому просто нужно нетронутое преобразование пути, здесь очищено версия этого кода, реализованная как обычный метод:
- (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);
Это похоже на QPainterPathStroker
из Qt, но для NSBezierPath
.
С# 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;
}
CGPath
дляNSBezierPath
github.com/iccir/XUIKit. - person Wizard of Kneup   schedule 20.12.2013