しばらく探し回っていましたが、この問題の解決策が見つかりません。3次ベジェ曲線(4点で定義)があり、曲線に沿って等間隔に配置された一連の点を取得したいとします。例として、曲線に沿ってテキストを配置することを考えてください。
ここで問題はt
、一定の増分で入力(0-1からの補間値)した場合、ポイントが等間隔に配置されないことです。カーブに沿った距離は、カーブが曲がる場合は短くなり、カーブが直線の場合は長くなります。
では、ベジェ曲線に沿って点を均等に配置するにはどうすればよいですか?
しばらく探し回っていましたが、この問題の解決策が見つかりません。3次ベジェ曲線(4点で定義)があり、曲線に沿って等間隔に配置された一連の点を取得したいとします。例として、曲線に沿ってテキストを配置することを考えてください。
ここで問題はt
、一定の増分で入力(0-1からの補間値)した場合、ポイントが等間隔に配置されないことです。カーブに沿った距離は、カーブが曲がる場合は短くなり、カーブが直線の場合は長くなります。
では、ベジェ曲線に沿って点を均等に配置するにはどうすればよいですか?
回答:
それは数学の問題です。したがって、ベジエ曲線はx
とy
コンポーネントの両方で次の式を持ちます。
B_x(t) = (1-t)^3 * P0_x + (1-t)^2 * t * P1_x + (1-t) * t^2 * P2_x + t^3 * P3_x
B_y(t) = (1-t)^3 * P0_y + (1-t)^2 * t * P1_y + (1-t) * t^2 * P2_x + t^3 * P3_y
t
曲線に沿って移動する長さは、次の式gamma
で与えられます。
length_gamma(t) = integration( sqrt( derivative( gamma_x(s) ) ^2 + derivative( gamma_y(s) ) ^2 ) )
積分に対する人間が書き込み可能なソリューションはないため、概算する必要があります。
取り替えgamma(t)
式でB(t)
長さを取得するlength_B
ことにより、旅t
ベジェセグメントに沿っています。それがから0
に移動するとしL
ます。
次に、等間隔のポイントに対応するとのn
間の値を選択します。たとえば、フォームの長さをfromからに。0
L
k*L/n
k
0
n
次に、関数を逆にする必要があるlength_B
のでt
、長さから背中を計算できますl
。それはかなり多くの数学であり、私は地獄のように怠惰です、自分でやってみてください。できない場合は、数学のstackexchangeにアクセスできます。より完全な答えは、とにかくそこに行くことができます。
その逆length_B
関数(または妥当な近似)を取得すると、処理は非常に簡単になります。
l[k]
原点から離れた、指定されたパスの長さを作成します(P0_x,P1_x)
。t[k]
を使用して対応するものを計算しlength_B_inverse
ます。(B_x(t[k]),B_y(t[k]))
ます。ええと、それはしばらく時間が経ちました...
しかし、私はこの問題をようやく解決することができました!
必要なものはすべてこの投稿にあります:ベジエに沿って2つの惑星間で船を移動し、加速のためのいくつかの方程式を欠いています
マルコが言ったことを拡張すると、これを行うための一般的な手法は、必要な固定長のステップよりもはるかに小さい増分でカーブを下って行き、結果の出力ポイント(およびおそらく距離?)をテーブルに格納することです。
次に、テーブルを移動して、歩きたい距離の整数倍に最も近いポイントを除くすべてのエントリを破棄します。
次に、実行時に非常に迅速に直接インデックス付けできるテーブルが残ります。距離の5倍の大きさのスポットに行きたい場合は、インデックス[5]でテーブルを調べます。
2つのステップを1つで実行し、最初に追加のアイテムをテーブルに実際に格納することはできませんが、2つのステップで視覚化して理解する方が簡単です。
テーブルを事前に計算せずに実際にこれをその場で実際に計算する手法を見たことがあります(反復/ルート検索も使用していませんでした!)。残念ながら、詳細をまったく覚えていません)
覚えている、または見つけたら、情報を投稿します!
ステップ1-1 / Nの増分で曲線を補間してN + 1ポイントを生成します。Nは、視覚的に良好な結果を得るには十分な大きさであるが、簡単に計算するには十分に小さい必要があります。ほとんどの状況では50の値で問題ありませんが、特定のケースに合わせて調整する必要があります。これを「補間ポイント」と呼びます。
または、セグメントの短いリストを生成し、目的の最大セグメント長よりも長い各セグメントを再帰的に分割することもできます(最初に、開始が終了に非常に近いSカーブを考慮して、少なくとも4つのセグメントを生成する必要があります)。
ステップ2-補間されたポイントと各ポイント間の間隔を使用して、「ラインを歩く」。
Unityコードはここに残しておきます。
public static Vector2[] InterpolateBezier(Vector2 start, Vector2 startControlPoint,
Vector2 end, Vector2 endControlPoint, int segments)
{
Vector2[] interpolated = new Vector2[segments + 1];
interpolated[0] = start;
interpolated[segments] = end;
float step = 1f / segments;
for (int i = 1; i < segments; i++)
{
interpolated[i] = GetBezierPosition(start, startControlPoint, end,
endControlPoint, i * step);
}
return interpolated;
}
public static Vector2 GetBezierPosition(Vector2 start, Vector2 startControlPoint,
Vector2 end, Vector2 endControlPoint, float t)
{
float omt = 1f - t;
float omt2 = omt * omt;
float t2 = t * t;
return
start * (omt2 * omt) +
startControlPoint * (3f * omt2 * t) +
endControlPoint * (3f * omt * t2) +
end * (t2 * t);
}
public static List<Vector2> WalkLine(Vector2[] points, float spacing, float offset = 0)
{
List<Vector2> result = new List<Vector2>();
spacing = spacing > 0.00001f ? spacing : 0.00001f;
float distanceNeeded = offset;
while (distanceNeeded < 0)
{
distanceNeeded += spacing;
}
Vector2 current = points[0];
Vector2 next = points[1];
int i = 1;
int last = points.Length - 1;
while (true)
{
Vector2 diff = next - current;
float dist = diff.magnitude;
if (dist >= distanceNeeded)
{
current += diff * (distanceNeeded / dist);
result.Add(current);
distanceNeeded = spacing;
}
else if (i != last)
{
distanceNeeded -= dist;
current = next;
next = points[++i];
}
else
{
break;
}
}
return result;
}
これはかなり良い結果を与えるいくつかのアルゴリズムです:
Point Index(float pos) const
{
int count = p.NumPoints();
Vector val(0.0,0.0,0.0);
for(int i=0;i<count;i++)
{
val += bin(pos,i,count-1)*Vector(p.Points(i));
}
return Point(val);
}
float bin(float pos, int i, int n) const
{
return float(ni(n,i)) * pow(double(pos), double(i))*pow(double(1.0-pos), double(n-i));
}
int ni(int n, int i) const
{
if (i==0) { return 1; }
if (n==i) { return 1; }
return ni(n-1,i-1)+ni(n-1,i);
}
t
、たとえば100ステップずつ増やして、曲線をポリラインに変換し、結果のポイント間の距離を測定します。次に、必要に応じてこのポリラインに沿って補間します。