对于我正在编写的程序,我需要能够跟踪对象必须沿着的虚拟线(非直线)。我想用NSBezierPath
来画这条线,但我找不到沿着这条线画任何点的方法,我必须这样做,这样我才能沿着它移动物体。
有人能提出一种沿着NSBezierPath
找到一个点的方法吗?如果这是不可能的,有人能提出一个方法来做上面的事情吗?
编辑:下面的代码仍然准确,但有更快的计算方法。请参阅快速贝塞尔和甚至更快贝塞尔简介
有两种方法可以解决这个问题。如果你只需要沿着这条线移动一些东西,请使用CAKeyframeAnimation
。这是非常简单的,你永远不需要计算点。
另一方面,如果出于某种原因,你真的需要知道这一点,你必须自己计算Bézier。例如,您可以从iOS 5编程突破极限中提取第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);
}
注意:这段代码违反了我的一条基本规则,即总是使用访问器,而不是直接访问ivar。这是因为在中,它被调用了数千次,而消除方法调用会对性能产生重大影响
"偏移"不是一件小事。它不是沿着曲线线性前进的。如果需要沿曲线均匀分布的点,则需要计算每个点的正确偏移。这是通过以下例程完成的:
// 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的第18章:PTL更详细地介绍了这一点,作为关于如何沿任意路径绘制文本的讨论的一部分。