如何从UIBezierPath获取点列表?

时间:2010-06-16 08:39:10

标签: ios objective-c iphone uibezierpath

我有一个UIBezierPath我需要从中获取一个点列表。

Qt中有一个名为pointAtPercent的函数可以满足我的需求,但我找不到Objective-C中的任何内容。

有谁知道怎么做?

7 个答案:

答案 0 :(得分:64)

你可以试试这个:

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

路径应用程序功能将依次传递每个路径的路径元素 查看CGPathApplierFunctionCGPathApply

路径应用程序功能可能看起来像这样

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

答案 1 :(得分:14)

@Moritz很棒的回答!为了找到类似Swift的解决方案,我在这个post中搜索了一个方法并实现了这样的CGPath扩展来从路径中获取点:

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

答案 2 :(得分:6)

我认为你试图做这样的事情:

https://math.stackexchange.com/questions/26846/is-there-an-explicit-form-for-cubic-bézier-curves

y=u0(1−x^3)+3u1(1−x^2)x+3u2(1−x)x^2+u3x^3

这是我打印该功能的所有值的方法。

- (void)logXY {

    float u0 = 0;
    float u1 = 0.05;
    float u2 = 0.25;
    float u3 = 1;

    for (float x = 0; x <= 10.0; x = x + 0.1) {
        float y = u0 * (1 - x * x * x) + 3 * u1 * (1 - x * x) * x + 3 * u2 * (1 - x) * x * x + u3 * x * x * x;

        NSLog(@"x: %f\ty: %f", x, y);
    }
}

输出是:

x: 0.000000 y: 0.000000
x: 0.100000 y: 0.022600
x: 0.200000 y: 0.060800
x: 0.300000 y: 0.115200
x: 0.400000 y: 0.186400
x: 0.500000 y: 0.275000
x: 0.600000 y: 0.381600
x: 0.700000 y: 0.506800
x: 0.800000 y: 0.651200
x: 0.900000 y: 0.815400
x: 1.000000 y: 1.000000
x: 1.100000 y: 1.205600
x: 1.200000 y: 1.432800
x: 1.300000 y: 1.682200
x: 1.400000 y: 1.954401
x: 1.500000 y: 2.250001
x: 1.600000 y: 2.569601
x: 1.700000 y: 2.913801
x: 1.800000 y: 3.283201
x: 1.900000 y: 3.678401
x: 2.000000 y: 4.100001
x: 2.100000 y: 4.548601
x: 2.200000 y: 5.024800
x: 2.300000 y: 5.529200
x: 2.400000 y: 6.062399
x: 2.500000 y: 6.625000
x: 2.600000 y: 7.217597
x: 2.700000 y: 7.840797
x: 2.799999 y: 8.495197
x: 2.899999 y: 9.181394
x: 2.999999 y: 9.899996

答案 3 :(得分:2)

我写了一篇关于finding the closest point on UIBezierPath的文章,这与OP正在寻找的非常相似。我还在Swift上创建了一个演示项目:https://github.com/andrew8712/BezierPathClosestPoint

答案 4 :(得分:2)

我修改了@FBente帖子以使用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)
}
}

答案 5 :(得分:0)

更新了FBente对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)
  }
}

答案 6 :(得分:0)

swift 4.0:

    var bezierPoints = NSMutableArray()
    yourPath.apply(info: &bezierPoints, function: { info, element in

        guard let resultingPoints = info?.assumingMemoryBound(to: NSMutableArray.self) else {
            return
        }

        let points = element.pointee.points
        let type = element.pointee.type

        switch type {
        case .moveToPoint:
            resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])

        case .addLineToPoint:
            resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])

        case .addQuadCurveToPoint:
            resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])
            resultingPoints.pointee.add([NSNumber(value: Float(points[1].x)), NSNumber(value: Float(points[1].y))])

        case .addCurveToPoint:
            resultingPoints.pointee.add([NSNumber(value: Float(points[0].x)), NSNumber(value: Float(points[0].y))])
            resultingPoints.pointee.add([NSNumber(value: Float(points[1].x)), NSNumber(value: Float(points[1].y))])
            resultingPoints.pointee.add([NSNumber(value: Float(points[2].x)), NSNumber(value: Float(points[2].y))])

        case .closeSubpath:
            break
        }
    })