如何将 NSBezierPath 转换为 CGPath

发布于 2024-08-12 13:50:46 字数 79 浏览 4 评论 0原文

我如何在 NSBezierPathCGPath 之间进行转换。

谢谢。

How can i convert between NSBezierPath to CGPath.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

江挽川 2024-08-19 13:50:46

来自Apple文档: 从 NSBezierPath 对象创建 CGPathRef

这是相关代码。

@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 到 CGPath。

Right from 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 to CGPath.

青丝拂面 2024-08-19 13:50:46

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
    }
}

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

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
    }
}
橙幽之幻 2024-08-19 13:50:46

这适用于 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
    }

}

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
    }

}
甜味超标? 2024-08-19 13:50:46

为了更好地使用 macOS - CGMutablePath

但是,如果你想要 cgPath对于 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
  }
}

For macOS better 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
  }
}
£烟消云散 2024-08-19 13:50:46

如果其他人发现需要的话,这里有一个 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
}

Here is a Swift version if anyone else finds a need for 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
}
好久不见√ 2024-08-19 13:50:46

我不明白为什么接受的答案添加了一些复杂的闭合路径逻辑(也许在某些情况下需要),但对于那些只需要路径的原始转换的人来说,这里是干净的该代码的版本,作为常规方法实现:

- (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 笔划包含点”方法。

我寻找了这个转换来调用 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

I can't figure out why the accepted answer adds some sophisticated close-path logic (maybe it is needed under some circumstances), but for those who just need a pristine conversion of the path, here is cleaned version of that code, implemented as a regular method:

- (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 "NSBezierPath stroke contains point" method.

I've looked for this conversion to call CGPathCreateCopyByStrokingPath(), that converts NSBezierPath stroke outline to regular path, so you can test hits on strokes too, 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.

来日方长 2024-08-19 13:50:46

时间:2019-03-17 标签: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;

}

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;

}
谈场末日恋爱 2024-08-19 13:50:46

如果您的目标平台是 macOS 14,则 cgPath 已经是 NSBezierPath API 的一部分。

WWDC 视频:https://developer.apple.com/videos/ play/wwdc2023/10054/?time=676

文档:https: //developer.apple.com/documentation/appkit/nsbezierpath/4191603-cgpath

If you are targeting macOS 14 then cgPath is already part of the NSBezierPath API.

WWDC video: https://developer.apple.com/videos/play/wwdc2023/10054/?time=676

Documentation: https://developer.apple.com/documentation/appkit/nsbezierpath/4191603-cgpath

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文