ios 如何从 UIBezierPath 获取点列表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3051760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 17:20:50  来源:igfitidea点击:

How to get a list of points from a UIBezierPath?

iosobjective-ciphoneuibezierpath

提问by Alexandre

I have a UIBezierPaththat I need to take a list of points from.

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

In Qtthere is a function called pointAtPercentthat would fit my needs but I can't find anything equivalent in Objective-C.

Qt有一个叫功能pointAtPercent,将适合我的需要,但我找不到任何等值Objective-C

Does anyone know how to do this?

有谁知道如何做到这一点?

回答by Moritz

You might try this:

你可以试试这个:

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

The path applier function will be handed each of the path's path elements in turn.
Check out CGPathApplierFunctionand CGPathApply.

路径应用器函数将依次处理路径的每个路径元素。
查看CGPathApplierFunctionCGPathApply

The path applier function might look something like this

路径应用器函数可能看起来像这样

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

回答by FBente

@Moritz Great answer! To find a Swift like solution I strumbled across an approach in this postand implemented a CGPath extension like this to get the points from the path:

@Moritz 很好的答案!为了找到类似 Swift 的解决方案,我在这篇文章中偶然发现了一种方法,并实现了这样的 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)
    }
}

回答by Adam

I think you were trying to do something like this:

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

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

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

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

This is my method for printing all values of that function.

这是我打印该函数的所有值的方法。

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

And output is:

输出是:

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

回答by Chigo Godwin Anyaso

i have modified @FBente post to work with swift 3

我已经修改了@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)
}
}

回答by Andrey Gordeev

I wrote an article about finding the closest point on UIBezierPath, which is very similar to what the OP is looking for. I've also created a demo project on Swift: https://github.com/andrew8712/BezierPathClosestPoint

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

回答by Joel

Updated FBente's extension for Swift 3:

为 Swift 3 更新了 FBente 的扩展:

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

回答by Rodrigo Fava

swift 4.0:

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