沿着简单的三次贝塞尔曲线找到一个点,一个给定的距离。 (在iPhone上!)

时间:2010-10-30 13:53:22

标签: iphone ipad bezier

想象一下,您使用curveToPoint创建了一个完全正常的四点贝塞尔曲线(两点和两个控制点):controlPoint1:controlPoint2:在您的可可应用程序中:

simple cubic bezier curve example
你如何在曲线上找到点(和切线)?


稍后:根据Michal的回答,对于完整,简化,解决方案,请点击:
Find the tangent of a point on a cubic bezier curve (on an iPhone)

只需复制并粘贴以下代码:https://stackoverflow.com/a/31317254/294884

4 个答案:

答案 0 :(得分:28)

计算位置背后有一些简单的数学,你可以在讨论Bézier曲线的每篇论文中读到它,甚至在维基百科上也是如此。无论如何,我可以与遇到麻烦的每个人在代码中实际实现它,所以我写了这个样本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

这就是我得到的:

alt text

答案 1 :(得分:3)

近似t是迈克尔提出的沿着曲线的距离,对于某些曲线和某些目的来说可能很麻烦。可悲的是,我已经在没有运气的情况下搜索了一段时间,以便Obj-C实现正确的解决方案。

然而,解决方案是迈克“波马克斯”卡梅曼以惊人的Primer on Bezier Curves以相当奇妙的方式描述的。它甚至包含所有代码,在处理和公共领域编写。我很惊讶没有人把它转换成Obj-C。非常诱惑,我是。

答案 2 :(得分:0)

任何Beziér曲线都可以简单地视为具有向量或复系数的多项式函数。然后通过阶数为3的多项式函数生成三次Beziér曲线,例如截图中的曲线,曲线上的每个点都描述曲线多项式的结果 B(t),进行评估对于特定输入值 t 。如果我没弄错,一旦你知道用于创建曲线的多项式,你就可以简单地求解 B(t)= a + bi ,其中 a + bi 描述了要在其中找到 t 值的复平面上的点。在这样的多项式中找到根是一个众所周知的问题,并且可以代数地求解2阶或更低阶的曲线,并且使用诸如前向牛顿之类的方法用于更高阶的多项式。如果你知道生成多项式,那么找到这些导数当然也应该非常简单。 Beziér通常来自“模板多项式”,其中只有在绘制不同曲线时才会更改系数,因此您可以在文档中的某处查找。

答案 3 :(得分:0)

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

enter image description here