想象一下,您有一个完全正常的四点贝塞尔曲线(两个点和两个控制点),在您的可可应用程序中使用 curveToPoint:controlPoint1:controlPoint2: 创建:
你如何沿着曲线找到点(和切线)?
稍后:对于基于以下 Michal 答案的完整、简化的解决方案,请单击:
在三次贝塞尔曲线上查找点的切线(在 iPhone 上)
只需复制并粘贴以下代码:https ://stackoverflow.com/a/31317254/294884
想象一下,您有一个完全正常的四点贝塞尔曲线(两个点和两个控制点),在您的可可应用程序中使用 curveToPoint:controlPoint1:controlPoint2: 创建:
你如何沿着曲线找到点(和切线)?
稍后:对于基于以下 Michal 答案的完整、简化的解决方案,请单击:
在三次贝塞尔曲线上查找点的切线(在 iPhone 上)
只需复制并粘贴以下代码:https ://stackoverflow.com/a/31317254/294884
计算位置背后有一些简单的数学,您可以在每篇讨论贝塞尔曲线的论文中阅读它,甚至在维基百科上。无论如何,我可以与在代码中实际实现它有困难的每个人联系起来,所以我编写了这个 UIView 示例,因为它可能是让你开始的最简单的方法。
#import "MBBezierView.h"
CGFloat bezierInterpolation(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) {
CGFloat t2 = t * t;
CGFloat t3 = t2 * t;
return a + (-a * 3 + t * (3 * a - a * t)) * t
+ (3 * b + t * (-6 * b + b * 3 * t)) * t
+ (c * 3 - c * 3 * t) * t2
+ d * t3;
}
@implementation MBBezierView
- (void)drawRect:(CGRect)rect {
CGPoint p1, p2, p3, p4;
p1 = CGPointMake(30, rect.size.height * 0.33);
p2 = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
p3 = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
p4 = CGPointMake(-30 + CGRectGetMaxX(rect), rect.size.height * 0.66);
[[UIColor blackColor] set];
[[UIBezierPath bezierPathWithRect:rect] fill];
[[UIColor redColor] setStroke];
UIBezierPath *bezierPath = [[[UIBezierPath alloc] init] autorelease];
[bezierPath moveToPoint:p1];
[bezierPath addCurveToPoint:p4 controlPoint1:p2 controlPoint2:p3];
[bezierPath stroke];
[[UIColor brownColor] setStroke];
for (CGFloat t = 0.0; t <= 1.00001; t += 0.05) {
CGPoint point = CGPointMake(bezierInterpolation(t, p1.x, p2.x, p3.x, p4.x), bezierInterpolation(t, p1.y, p2.y, p3.y, p4.y));
UIBezierPath *pointPath = [UIBezierPath bezierPathWithArcCenter:point radius:5 startAngle:0 endAngle:2*M_PI clockwise:YES];
[pointPath stroke];
}
}
@end
这就是我得到的:
t 是 Michal 提出的沿曲线的距离的近似值对于某些曲线和某些目的可能会很麻烦。可悲的是,我已经搜索了很长一段时间都没有找到正确解决方案的 Obj-C 实现。
然而,Mike "Pomax" Kamermans 在他惊人的 Bezier Curves 入门中以一种相当奇妙的方式描述了这个解决方案。它甚至拥有在处理和公共领域中编写的所有代码。我很惊讶还没有人将它转换为 Obj-C。很受诱惑,我就是。
任何贝塞尔曲线都可以简单地视为具有矢量或复系数的多项式函数。然后,三次贝塞尔曲线(例如屏幕截图中的曲线)将由 3 阶多项式函数生成,曲线上的每个点描述曲线多项式的结果B(t),针对特定输入值t进行评估。如果我没记错的话,一旦您知道用于创建曲线的多项式,您就可以简单地求解B(t) = a+bi,其中a+bi描述了您想要找到t的复平面上的点价值。在这样的多项式中求根是一个很好理解的问题,并且可以通过代数方式求解 2 阶或更低阶的曲线,并且可以使用一些方法(如前向牛顿法)来求解更高阶的多项式。如果您知道生成多项式,那么找到导数当然也应该非常简单。贝塞尔曲线通常是从“模板多项式”中绘制的,其中在绘制不同曲线时仅更改系数,因此您可能可以在文档中的某处查找它。
根据 Michal 的回答,用 Swift 为我的项目编写了这个。也许会有所帮助,只需将其留在这里并解释原始 Fattie 的屏幕截图:
let targetPoint = CGPoint(x: bezierInterpolation(t: distance, p1: sP1.point.x, cp1: sP1.nextMarker.x, cp2: sP2.previousMarker.x, p2: sP2.point.x),
y: bezierInterpolation(t: distance, p1: sP1.point.y, cp1: sP1.nextMarker.y, cp2: sP2.previousMarker.y, p2: sP2.point.y))
func bezierInterpolation(t: CGFloat, p1: CGFloat, cp1: CGFloat, cp2: CGFloat, p2: CGFloat) -> CGFloat {
let t2: CGFloat = t * t;
let t3: CGFloat = t2 * t;
return p1 + (-p1 * 3 + t * (3 * p1 - p1 * t)) * t
+ (3 * cp1 + t * (-6 * cp1 + cp1 * 3 * t)) * t
+ (cp2 * 3 - cp2 * 3 * t) * t2
+ p2 * t3;
}