【问题标题】:How can i convert NSBezierPath to CGPath如何将 NSBezierPath 转换为 CGPath
【发布时间】:2009-11-29 13:38:59
【问题描述】:

如何在 NSBezierPathCGPath 之间进行转换。

谢谢。

【问题讨论】:

  • 我看不出这是那个问题的重复。那是在询问如何将 CGPath 填充到存档中或以其他方式对其进行序列化;这是关于从一种路径对象转换为另一种路径对象,而不必从一种路径对象序列化并反序列化到另一种。
  • 我喜欢这个,一个为 MacOS 添加 UIKit 功能的库。包含后,您可以设置和读取CGPathNSBezierPath github.com/iccir/XUIKit

标签: cocoa cgpath nsbezierpath


【解决方案1】:

来自 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。

【讨论】:

  • 我认为 Apple 将该代码放入文档中而不是仅仅在 NSBezierPath 中添加一个 CGPath 访问器,这真是太神奇了。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 在 Swift 3 中没有 moveTo 或 addLineTo 这样的东西,是吗?
    • 这作为CGPath 上的便利初始化器会更好,因此可以使用更标准的CGPath(from: bezierPath) 语法
    • NSColor 有一个cgColor 属性,NSGraphicsContext 有一个cgContext 属性,而NSColorSpace 有一个cgColorSpace 属性。 NSImage 有 `cgImage(forProposedRect:context:hints:)` 方法。
    • 另外,Swift 不支持 CGPath 上的便捷初始化器。如果您尝试声明一个,则会收到错误消息“CF 类型的扩展中不支持便利初始化程序”。
    【解决方案4】:

    为了更好地使用 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
      }
    }
    

    【讨论】:

      【解决方案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
      }
      

      【讨论】:

        【解决方案6】:

        我无法弄清楚为什么接受的答案会添加一些复杂的关闭路径逻辑(可能在某些情况下需要),但对于那些只需要路径的原始转换的人,这是该代码的清理版本,作为常规方法实现:

        - (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 contains point”方法。

        我已经寻找这种转换来调用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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-01-24
            • 1970-01-01
            • 2017-11-03
            • 1970-01-01
            • 1970-01-01
            • 2019-02-19
            相关资源
            最近更新 更多