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
How to get a list of points from a UIBezierPath?
提问by Alexandre
I have a UIBezierPaththat I need to take a list of points from.
我有一个UIBezierPath,我需要从中获取点列表。
In Qt
there is a function called pointAtPercent
that 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.
路径应用器函数将依次处理路径的每个路径元素。
查看CGPathApplierFunction和CGPathApply。
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
}
})