参照: Math.SEの同じ質問
ベジェ曲線の弧長を見つけるにはどうすればよいですか?たとえば、線形ベジェ曲線の長さは次のとおりです。
length = sqrt(pow(x[1] - x[0], 2) + pow(y[1] - y[0], 2));
しかし、二次、三次、またはn度ベジェ曲線はどうでしょうか?
(私の目標は、サンプリング解像度を事前に推定することでしたので、次のポイントが前のポイントに触れているかどうかをチェックする時間を無駄にする必要はありません。)
参照: Math.SEの同じ質問
ベジェ曲線の弧長を見つけるにはどうすればよいですか?たとえば、線形ベジェ曲線の長さは次のとおりです。
length = sqrt(pow(x[1] - x[0], 2) + pow(y[1] - y[0], 2));
しかし、二次、三次、またはn度ベジェ曲線はどうでしょうか?
(私の目標は、サンプリング解像度を事前に推定することでしたので、次のポイントが前のポイントに触れているかどうかをチェックする時間を無駄にする必要はありません。)
回答:
3次ベジェの簡単な方法は、曲線をN個のセグメントに分割し、セグメントの長さを合計することです。
ただし、曲線の一部のみの長さ(たとえば、長さの30%まで)が必要になるとすぐに、円弧長のパラメーター化が有効になります。ベジエに関する私自身の質問の1つに、簡単なサンプルコードを使用して、かなり長い回答を投稿しました。
1.0/t
(resolution
)と呼ばれる値を設定できるオプションがあるため、「リアルタイム」(低速のNXTでは最大で10fps)になります。すべての反復、、t += resolution
および新しいポイント/ラインが描画されます。とにかく、アイデアをありがとう。
私はあなたがすでに得た答えに賛成ですが、任意の次数のベジエ曲線に使用できるシンプルでありながら強力な近似メカニズムを追加したいと思います。サブカーブのベースラインに対するサブカーブの差は、一定のイプシロンを下回ります。その場合、サブ曲線はそのベースラインによって近似できます。
実際、これはグラフィックスサブシステムがベジェ曲線を描画する必要がある場合に通常行われるアプローチだと思います。しかし、これについて私に引用しないでください、私は現時点で手元に参照を持っていません。
実際には、次のようになります(言語は無関係です)。
public static Line[] toLineStrip(BezierCurve bezierCurve, double epsilon) {
ArrayList<Line> lines = new ArrayList<Line>();
Stack<BezierCurve> parts = new Stack<BezierCurve>();
parts.push(bezierCurve);
while (!parts.isEmpty()) {
BezierCurve curve = parts.pop();
if (distanceToBaseline(curve) < epsilon) {
lines.add(new Line(curve.get(0), curve.get(1)));
} else {
parts.addAll(curve.split(0.5));
}
}
return lines.toArray(new Line[0]);
}
ベジェ曲線のアーク長は、線形および二次曲線の場合にのみ閉じた形式です。キュービックの場合、閉じた解を持っているとは限りません。理由は、アーク長がラジカル積分によって定義されるためです。これは、2次多項式のみに対して閉じています。
参照用:ポイント(a、p)(b、q)および(c、r)の2次ベジェの長さは
(a ^ 2・(q ^ 2-2・q・r + r ^ 2)+ 2・a・(r-q)・(b・(p-r)+ c・(q-p))+( b・(p-r)+ c・(q-p))^ 2)・LN((√(a ^ 2-2・a・b + b ^ 2 + p ^ 2-2・p・q + q ^ 2)・√(a ^ 2 + 2・a・(c-2・b)+ 4・b ^ 2-4・b・c + c ^ 2 +(p-2・q + r)^ 2) + a ^ 2 + a・(c-3・b)+ 2・b ^ 2-b・c +(p-q)・(p-2・q + r))/(√(a ^ 2 + 2・a・(c-2・b)+ 4・b ^ 2-4・b・c + c ^ 2 +(p-2・q + r)^ 2)・√(b ^ 2-2・b・c + c ^ 2 + q ^ 2-2・q・r + r ^ 2)+ a・(b-c)-2・b ^ 2 + 3・b・c-c ^ 2 +(p-2・q + r)・(q-r)))/(a ^ 2 + 2・a・(c-2・b)+ 4・b ^ 2-4・b・c + c ^ 2 +(p-2・q + r)^ 2)^(3/2)+(√(a ^ 2-2・a・b + b ^ 2 + p ^ 2-2・p・q + q ^ 2)・(a ^ 2 + a・(c-3・b)+ 2・b ^ 2-b・c +(p-q)・(p-2・q + r))-√(b ^ 2-2・b・c + c ^ 2 + q ^ 2-2・q・r + r ^ 2)・(a・(b-c)-2・b ^ 2 + 3・b・c-c ^ 2 +(p-2・q + r)・(q-r)))/(a ^ 2 + 2・a・(c-2・b)+ 4・b ^ 2-4・b・c + c ^ 2 +(p-2・q + r)^ 2)
ここで、LNは自然対数であり、^は累乗を表し、√は平方根を表します。
したがって、LNの平方根は高価な操作であるため、多角形やシンプソンのルールのような統合スキームなど、他のルールによってアークをより簡単かつ安価に近似する必要があります。
3ポイントのベジエ(以下)の長さの閉形式表現を作成しました。私は4点以上の閉じたフォームを作成しようとしませんでした。これは、表現および処理が困難または複雑になる可能性が最も高いでしょう。ただし、Runge-Kutta積分アルゴリズムなどの数値近似手法は、アーク長の式を使用して積分することで非常にうまく機能します。MSE 上のRK45に関する私のQ&Aは、RK45の実装に役立つ場合があります。
ここでポイントとの3点ベジェの弧の長さのためにいくつかのJavaコードは、でありa
、b
およびc
。
v.x = 2*(b.x - a.x);
v.y = 2*(b.y - a.y);
w.x = c.x - 2*b.x + a.x;
w.y = c.y - 2*b.y + a.y;
uu = 4*(w.x*w.x + w.y*w.y);
if(uu < 0.00001)
{
return (float) Math.sqrt((c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y));
}
vv = 4*(v.x*w.x + v.y*w.y);
ww = v.x*v.x + v.y*v.y;
t1 = (float) (2*Math.sqrt(uu*(uu + vv + ww)));
t2 = 2*uu+vv;
t3 = vv*vv - 4*uu*ww;
t4 = (float) (2*Math.sqrt(uu*ww));
return (float) ((t1*t2 - t3*Math.log(t2+t1) -(vv*t4 - t3*Math.log(vv+t4))) / (8*Math.pow(uu, 1.5)));