cocoa cgpath nsbezierpath

cocoa - ¿Cómo puedo convertir NSBezierPath a CGPath



(5)

Aquí hay una versión Swift si alguien más la 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 }

¿Cómo puedo convertir entre NSBezierPath a CGPath .

Gracias.


Desde la documentación de Apple: Creación de un CGPathRef a partir de 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.


Esto funciona en Swift 3.1 y más tarde:

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


La sintaxis en Xcode 8 GM se ha simplificado aún más, el código se modificó a partir de la respuesta anterior de rob-mayoff. Utilizando esto y un ayudante para addLine(to point: CGPoint) , estoy compartiendo el código de dibujo en todas las plataformas.

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


No puedo entender por qué la respuesta aceptada agrega alguna lógica sofisticada de ruta cercana (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, implementada 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 el método del "punto de trazo NSBezierPath".

He buscado esta conversión para llamar a CGPathCreateCopyByStrokingPath() , que convierte el NSBezierPath trazo de NSBezierPath en una ruta normal, por lo que también puedes probar los golpes en los trazos, y aquí está 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 .