【问题标题】:Getting any point along an NSBezier path沿着 NSBezier 路径获取任何点
【发布时间】:2012-03-07 22:01:35
【问题描述】:

对于我正在编写的程序,我需要能够跟踪对象必须沿着的虚拟线(不是直线)。我正在考虑使用NSBezierPath 来画线,但我找不到沿线获取任何点的方法,我必须这样做才能沿线移动对象。

任何人都可以提出一种沿NSBezierPath 查找点的方法吗?如果那不可能,任何人都可以提出一种方法来执行上述操作吗?

【问题讨论】:

    标签: objective-c cocoa drawing nsbezierpath


    【解决方案1】:

    编辑:下面的代码仍然是准确的,但是有更快的方法来计算它。见Introduction to Fast BezierEven Faster Bezier


    有两种方法可以解决这个问题。如果您只需要沿线移动某些东西,请使用CAKeyframeAnimation。这非常简单,您永远不需要计算分数。

    另一方面,如果出于某种原因您确实需要知道该点,则您必须自己计算贝塞尔曲线。例如,您可以从iOS 5 Programming Pushing the Limits 中提取第 18 章的示例代码。 (它是为 iOS 编写的,但同样适用于 Mac。)查看CurvyTextView.m

    给定控制点 P0_P3_,以及 0 和 1 之间的偏移量(见下文),pointForOffset: 将为您提供沿路径的点:

    static double Bezier(double t, double P0, double P1, double P2,
                         double P3) {
      return 
                       pow(1-t, 3) *     P0
         + 3 *         pow(1-t, 2) * t * P1
         + 3 * (1-t) * pow(t,   2) *     P2
         +             pow(t,   3) *     P3;
    }
    
    - (CGPoint)pointForOffset:(double)t {
      double x = Bezier(t, P0_.x, P1_.x, P2_.x, P3_.x);
      double y = Bezier(t, P0_.y, P1_.y, P2_.y, P3_.y);
      return CGPointMake(x, y);
    }
    

    注意:此代码违反了我始终使用访问器而不是直接访问 ivars 的基本规则之一。这是因为它被调用了数千次,并且消除了方法调用会对性能产生重大影响。

    “偏移”不是一件容易解决的事情。它不会沿着曲线线性进行。如果您需要沿曲线均匀分布的点,则需要计算每个点的正确偏移量。这是通过以下例程完成的:

    // Simplistic routine to find the offset along Bezier that is
    // aDistance away from aPoint. anOffset is the offset used to
    // generate aPoint, and saves us the trouble of recalculating it
    // This routine just walks forward until it finds a point at least
    // aDistance away. Good optimizations here would reduce the number
    // of guesses, but this is tricky since if we go too far out, the
    // curve might loop back on leading to incorrect results. Tuning
    // kStep is good start.
    - (double)offsetAtDistance:(double)aDistance 
                     fromPoint:(CGPoint)aPoint
                        offset:(double)anOffset {
      const double kStep = 0.001; // 0.0001 - 0.001 work well
      double newDistance = 0;
      double newOffset = anOffset + kStep;
      while (newDistance <= aDistance && newOffset < 1.0) {
        newOffset += kStep;
        newDistance = Distance(aPoint, 
                               [self pointForOffset:newOffset]);
      }
      return newOffset;
    }
    

    我将Distance() 留给读者作为练习,但它当然在示例代码中。

    如果您需要,引用的代码还提供BezierPrime()angleForOffset:。 iOS:PTL 的第 18 章更详细地介绍了这一点,作为讨论如何沿任意路径绘制文本的一部分。

    【讨论】:

    • 非常感谢您的帮助!我绝对打算使用它!我还将研究这种形式的动画;从我目前所读到的,这听起来不错。
    • 顺便说一句,我在这方面做了很多工作,并且大大提高了上述代码的性能。讨论在robnapier.net/blog/fast-bezier-intro-701robnapier.net/blog/faster-bezier-722。我将很快发布关于 NEON 的曲线命中测试和优化的讨论。
    猜你喜欢
    • 1970-01-01
    • 2019-04-08
    • 2022-11-14
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多