1

私が書いているプログラムの場合、オブジェクトが移動しなければならない仮想線(直線ではない)をトレースできる必要があります。線を引くために使用することを考えていNSBezierPathましたが、線に沿って点を取得する方法が見つかりません。オブジェクトを線に沿って移動できるようにする必要があります。

誰かがに沿ってポイントを見つける方法を提案できますNSBezierPathか?それが不可能な場合、誰かが上記を行う方法を提案できますか?

4

1 に答える 1

4

編集:以下のコードはまだ正確ですが、それを計算するためのはるかに高速な方法があります。FastBezierおよびさらに高速なBezierの概要を参照してください。


これにアプローチする方法は2つあります。線に沿って何かを移動する必要がある場合は、を使用しCAKeyframeAnimationます。これは非常に簡単で、ポイントを計算する必要はありません。

一方、何らかの理由で実際にポイントを知る必要がある場合は、ベジェを自分で計算する必要があります。たとえば、iOS5プログラミングの限界を押し上げることから第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に直接アクセスするのではなく、常にアクセサーを使用するという私の基本的なルールの1つに違反しています。これは、何千回も呼び出され、メソッド呼び出しを排除するとパフォーマンスに大きな影響を与えるためです。

「オフセット」は簡単なことではありません。曲線に沿って直線的に進むことはありません。曲線に沿って等間隔のポイントが必要な場合は、各ポイントの正しいオフセットを計算する必要があります。これは、次のルーチンで実行されます。

// 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章では、任意のパスに沿ってテキストを描画する方法に関する説明の一部として、これについて詳しく説明しています。

于 2012-01-11T14:21:58.570 に答える