Pregunta

¿Cómo puedo convertir entre NSBezierPath a CGPath .

Gracias.

¿Fue útil?

Solución

Derecho de la documentación de Apple: Crear un CGPathRef desde un objeto NSBezierPath

Aquí está el código relevante.

@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

Reportero de errores

rdar: // 15758302 : NSBezierPath a CGPath.

Otros consejos

La sintaxis en Xcode 8 GM se ha simplificado aún más, el código modificado de la respuesta anterior de rob-mayoff. Usando esto y un ayudante para addLine (para señalar: CGPoint) estoy compartiendo código de dibujo multiplataforma.

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

Esto funciona en Swift 3.1 y versiones posteriores:

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
    }

}

Aquí hay una versión Swift si alguien más lo necesita:

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
}

No puedo entender por qué la respuesta aceptada agrega una lógica sofisticada de ruta cerrada (tal vez sea necesaria en algunas circunstancias), pero para aquellos que solo necesitan una conversión prístina de la ruta, Aquí está la versión limpia de ese código, implementado como un método regular:

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

Por cierto, necesitaba esto para implementar " NSBezierPath stroke contiene punto " método.

He buscado esta conversión para llamar a CGPathCreateCopyByStrokingPath () , que convierte el esquema de trazo NSBezierPath en una ruta normal, para que pueda probar golpes en trazos también, y aquí es la solución:

// 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);

Esto es similar a QPainterPathStroker de Qt, pero para NSBezierPath .

Para un mejor uso de macOS: CGMutablePath

Pero, si desea cgPath para 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
  }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top