web-dev-qa-db-fra.com

Comment obtenir une liste de points d'un UIBezierPath?

J'ai un UIBezierPath dont j'ai besoin de prendre une liste de points.

Dans Qt, il existe une fonction appelée pointAtPercent qui répond à mes besoins, mais je ne trouve rien d’équivalent dans Objective-C.

Est-ce que quelqu'un sait comment faire ça?

41
Alexandre

Vous pourriez essayer ceci:

UIBezierPath *yourPath; // Assume this has some points in it
CGPath yourCGPath = yourPath.CGPath;
NSMutableArray *bezierPoints = [NSMutableArray array];
CGPathApply(yourCGPath, bezierPoints, MyCGPathApplierFunc);

La fonction applicateur de chemin d'accès recevra à tour de rôle chacun des éléments de chemin d'accès.
Découvrez CGPathApplierFunction et CGPathApply .

La fonction applicateur de chemin pourrait ressembler à ceci

void MyCGPathApplierFunc (void *info, const CGPathElement *element) {
    NSMutableArray *bezierPoints = (NSMutableArray *)info;

    CGPoint *points = element->points;
    CGPathElementType type = element->type;

    switch(type) {
        case kCGPathElementMoveToPoint: // contains 1 point
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
            break;

        case kCGPathElementAddLineToPoint: // contains 1 point
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];            
            break;

        case kCGPathElementAddQuadCurveToPoint: // contains 2 points
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]];            
            break;

        case kCGPathElementAddCurveToPoint: // contains 3 points
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]];
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[2]]];
            break;

        case kCGPathElementCloseSubpath: // contains no point
            break;
    }
}
64
Moritz

@ Moritz Excellente réponse! Pour trouver une solution rapide, je me suis heurté à une approche dans ce post et ai implémenté une extension CGPath comme celle-ci pour obtenir les points du chemin:

extension CGPath {
    func points() -> [CGPoint]
    {
        var bezierPoints = [CGPoint]()
        self.forEach({ (element: CGPathElement) in
            let numberOfPoints: Int = {
                switch element.type {
                case .MoveToPoint, .AddLineToPoint: // contains 1 point
                    return 1
                case .AddQuadCurveToPoint: // contains 2 points
                    return 2
                case .AddCurveToPoint: // contains 3 points
                    return 3
                case .CloseSubpath:
                    return 0
                }
            }()
            for index in 0..<numberOfPoints {
                let point = element.points[index]
                bezierPoints.append(point)
            }
        })
        return bezierPoints
    }

    func forEach(@noescape body: @convention(block) (CGPathElement) -> Void) {
        typealias Body = @convention(block) (CGPathElement) -> Void
        func callback(info: UnsafeMutablePointer<Void>, element: UnsafePointer<CGPathElement>) {
            let body = unsafeBitCast(info, Body.self)
            body(element.memory)
        }
        let unsafeBody = unsafeBitCast(body, UnsafeMutablePointer<Void>.self)
        CGPathApply(self, unsafeBody, callback)
    }
}
14
FBente

J'ai écrit un article sur trouver le point le plus proche sur UIBezierPath , qui est très similaire à ce que recherche le PO. J'ai également créé un projet de démonstration sur Swift: https://github.com/andrew8712/BezierPathClosestPoint

2
Andrey Gordeev

j'ai modifié @FBente post pour fonctionner avec Swift 3

extension CGPath {
func points() -> [CGPoint]
{
    var bezierPoints = [CGPoint]()
    self.forEach(body: { (element: CGPathElement) in
        let numberOfPoints: Int = {
            switch element.type {
            case .moveToPoint, .addLineToPoint: // contains 1 point
                return 1
            case .addQuadCurveToPoint: // contains 2 points
                return 2
            case .addCurveToPoint: // contains 3 points
                return 3
            case .closeSubpath:
                return 0
            }
        }()
        for index in 0..<numberOfPoints {
            let point = element.points[index]
            bezierPoints.append(point)
        }
    })
    return bezierPoints
}

func forEach( body: @convention(block) (CGPathElement) -> Void) {
    typealias Body = @convention(block) (CGPathElement) -> Void
    func callback(info: UnsafeMutableRawPointer, element: UnsafePointer<CGPathElement>) {
        let body = unsafeBitCast(info, to: Body.self)
        body(element.pointee)
    }
    let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self)
    self.apply(info: unsafeBody, function: callback as! CGPathApplierFunction)
}
}
2

Mise à jour de l'extension de FBente pour Swift 3:

extension CGPath {
  func points() -> [CGPoint]
  {
    var bezierPoints = [CGPoint]()
    self.forEach({ (element: CGPathElement) in
      let numberOfPoints: Int = {
        switch element.type {
        case .moveToPoint, .addLineToPoint: // contains 1 point
          return 1
        case .addQuadCurveToPoint: // contains 2 points
          return 2
        case .addCurveToPoint: // contains 3 points
          return 3
        case .closeSubpath:
          return 0
        }
      }()
      for index in 0..<numberOfPoints {
        let point = element.points[index]
        bezierPoints.append(point)
      }
    })
    return bezierPoints
  }

  func forEach(_ body: @convention(block) (CGPathElement) -> Void) {
    typealias Body = @convention(block) (CGPathElement) -> Void
    func callback(info: UnsafeMutableRawPointer, element: UnsafePointer<CGPathElement>) {
      let body = unsafeBitCast(info, to: Body.self)
      body(element.pointee)
    }
    let unsafeBody = unsafeBitCast(body, to: UnsafeMutableRawPointer.self)
    self.apply(info: unsafeBody, function: callback as! CGPathApplierFunction)
  }
}
0
Joel