【问题标题】:Find a point, a given distance, along a simple cubic bezier curve. (On an iPhone!)沿着简单的三次贝塞尔曲线找到一个给定距离的点。 (在 iPhone 上!)
【发布时间】:2010-10-30 13:53:21
【问题描述】:

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


如何沿着曲线找到点(和切线)?


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

只需复制并粘贴来自https://stackoverflow.com/a/31317254/294884的代码

【问题讨论】:

    标签: iphone ipad bezier


    【解决方案1】:

    计算位置背后有一些简单的数学运算,您可以在每篇讨论贝塞尔曲线的论文中阅读它,甚至在维基百科上。无论如何,我可以与在代码中实际实现它有困难的每个人联系起来,所以我编写了这个 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
    

    这是我得到的:

    【讨论】:

    • @Joe Blow:这真的是你想要的吗?你说你想要一个沿曲线距离为 D 的点。 Michal,如果我错了,请纠正我,但是您的代码只是在不同的参数值下评估曲线,这与长度不同。正如您所看到的,这些点在峰处更靠近,而在曲线中间相距更远。
    • 嗨,克罗姆——你说得对,它们只是沿着曲线大约等距分布。显然,在数学上没有真正简单的方法来获得精确的间距。这种常见的近似值适用于游戏引擎等。另见 4089443。
    • 你好,米歇尔,我已经实现了这段代码,但它显示的是白屏而不是绘图。有什么问题,因为我在视图中调用此方法确实加载了 [self drawRect:bgImage.frame];怎么了?
    • 嗨@crom - 重申一下,它们只是沿着曲线大约等距分布。做等距间距是非常困难的,我的意思是难以置信的困难。有多难?这是关于这个问题的典型数学论文! geometrictools.com/Documentation/… 特别注意下面丹尼尔的非常有用的链接。 (无价链接丹尼尔,再次感谢。)
    • 嘿@Michael,你能看看我在stackoverflow.com/questions/26857850/…的问题吗
    【解决方案2】:

    t 是 Michal 提出的沿曲线的距离的近似值对于某些曲线和某些目的可能会很麻烦。可悲的是,我已经搜索了很长一段时间都没有找到正确解决方案的 Obj-C 实现。

    然而,Mike "Pomax" Kamermans 在他惊人的Primer on Bezier Curves 中以一种相当奇妙的方式描述了该解决方案。它甚至拥有在处理和公共领域编写的所有代码。我很惊讶还没有人将它转换为 Obj-C。很受诱惑,我就是。

    【讨论】:

    • 非常有用的链接@Daniel - 谢谢。华丽的。这是其中一页……pomax.github.io/bezierinfo……“太有用了!”呵呵。再次感谢该链接非常有用,我正在赏金你!太棒了。
    • 哈哈哈,很高兴 Primer 找到了用途。如果您发现任何遗漏或不清楚的地方,请告诉我 =P
    【解决方案3】:

    任何贝塞尔曲线都可以简单地视为具有矢量或复系数的多项式函数。三次贝塞尔曲线,如屏幕截图中的曲线,然后将由 3 阶多项式函数生成,曲线上的每个点描述曲线多项式的结果 B(t),评估对于特定的输入值t。如果我没记错的话,一旦您知道用于创建曲线的多项式,您就可以简单地求解 B(t) = a+bi,其中 a+bi描述复平面上您要为其找到 t 值的点。在这样的多项式中求根是一个很好理解的问题,并且可以通过代数方式求解 2 阶或更低阶的曲线,并使用诸如前向牛顿法等方法求解更高阶的多项式。如果您知道生成多项式,那么找到导数当然也应该非常简单。贝塞尔曲线通常是从“模板多项式”中绘制的,其中在绘制不同曲线时仅更改系数,因此您可以在文档中的某处查找它。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-16
      • 2011-05-04
      • 2017-12-12
      • 2020-12-05
      • 2016-04-22
      • 2011-03-10
      • 2016-02-28
      相关资源
      最近更新 更多