РЕДАКТИРОВАТЬ: приведенный ниже код все еще точен, но есть гораздо более быстрые способы его расчета. См. Введение в Fast Bezier и Even Faster Bezier .
Есть два способа приблизиться к этому. Если вам просто нужно переместить что-то по линии, используйте CAKeyframeAnimation
. Это довольно просто, и вам никогда не нужно вычислять очки.
Если, с другой стороны, вам по какой-то причине вам действительно необходимо знать точку, вы должны сами рассчитать Безье. Например, вы можете взять пример кода для главы 18 из Программирование на iOS 5 «Раздвигая пределы» . (Он написан для 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);
}
ПРИМЕЧАНИЕ. Этот код нарушает одно из моих основных правил - всегда использовать аксессоры, а не прямой доступ к иварам. Это потому, что он вызывается много тысяч раз, а устранение вызова метода оказывает значительное влияние на производительность.
"Смещение" - это не тривиальная вещь, которую нужно выработать. Это не происходит линейно вдоль кривой. Если вам нужны равномерно расположенные точки вдоль кривой, вам необходимо рассчитать правильное смещение для каждой точки. Это делается с помощью этой процедуры:
// 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:
, если они вам нужны. Глава 18 iOS: PTL описывает это более подробно в рамках дискуссии о том, как рисовать текст по произвольному пути.