5

(x0,y0)... (xn,yn)私は単調な点の配列を持っており、xベジェ曲線を使用してこれらを通して「最良の」曲線を描きたいと思っています。この曲線は、「ぎざぎざ」になりすぎてはならず(たとえば、ドットを結合するのと同じように)、曲がりすぎてはいけません(そして間違いなく「後方に移動」してはなりません)。プロトタイプを作成しましたが、客観的に「最善の解決策」があるかどうか疑問に思います。

すべてのセグメントのコントロールポイントを見つける必要がありますxi,y1 x(i+1)y(i+1)。セグメントに対する私の現在のアプローチ(エンドポイントを除く)x(i), x(i+1)は次のとおりです。

  • ベクトルを見つけx(i-1)...x(i+1)、正規化し、それをスケーリングしてfactor * len(i,i+1)、主要な制御点のベクトルを与えます
  • ベクトルを見つけx(i+2)...x(i)、正規化し、それをスケーリングしてfactor * len(i,i+1)、後続の制御点のベクトルを与えます。

私はfactor=0.1(ジャギーすぎる)、0.33(曲がりすぎる)、0.20を試しました-ほぼ正しいです。しかし、(たとえば)2次および3次導関数を可能な限りスムーズにするより良いアプローチはありますか。(私はそのようなアルゴリズムがグラフィックパッケージに実装されていると思います)?

要求があれば、擬似/コードを投稿できます。これが3つの画像(0.1 / 0.2 / 0.33)です。コントロールポイントは直線で示されます:黒(末尾)と赤(先頭)

factor=0.1を使用

factor=0.2を使用

factor=0.33を使用

これが現在のコードです。これは、 -ingなしで(単調)Yに対してプロットすることを目的としています。SVG(推奨出力)を作成するための独自のライブラリを構築しました。このコードは、曲線セグメント(control1、xcontrol2、end)ごとにinのトリプルを作成します。開始は最後の操作(移動または曲線)によって想定されます。これはJavaですが、解釈しやすいはずです(cubicにマップされ、SVGでの完全なパスの文字列表現です)。XXclosex,ycoordArrayCurvePrimitive"d"

        List<SVGPathPrimitive> primitiveList = new ArrayList<SVGPathPrimitive>();
    primitiveList.add(new MovePrimitive(real2Array.get(0)));
    for(int i = 0; i < real2Array.size()-1; i++) {
        // create path 12
        Real2 p0 = (i == 0) ? null : real2Array.get(i-1);
        Real2 p1 = real2Array.get(i);
        Real2 p2 = real2Array.get(i+1);
        Real2 p3 = (i == real2Array.size()-2) ? null : real2Array.get(i+2);
        Real2Array coordArray = plotSegment(factor, p0, p1, p2, p3);
        SVGPathPrimitive primitive = new CurvePrimitive(coordArray);
        primitiveList.add(primitive);
    }
    String d = SVGPath.constructDString(primitiveList);
    SVGPath path1 = new SVGPath(d);
    svg.appendChild(path1);


/**
 * 
 * @param factor to scale control points by
 * @param p0 previous point (null at start)
 * @param p1 start of segment
 * @param p2 end of segment
 * @param p3 following point (null at end)
 * @return
 */
private Real2Array plotSegment(double factor, Real2 p0, Real2 p1, Real2 p2, Real2 p3) {
    // create p1-p2 curve
    double len12 = p1.getDistance(p2) * factor;
    Vector2 vStart = (p0 == null) ? new Vector2(p2.subtract(p1)) : new Vector2(p2.subtract(p0));
    vStart = new Vector2(vStart.getUnitVector().multiplyBy(len12));
    Vector2 vEnd = (p3 == null) ?  new Vector2(p2.subtract(p1)) : new Vector2(p3.subtract(p1));
    vEnd = new Vector2(vEnd.getUnitVector().multiplyBy(len12));
    Real2Array coordArray = new Real2Array();
    Real2 controlStart = p1.plus(vStart);
    coordArray.add(controlStart);
    Real2 controlEnd = p2.subtract(vEnd);
    coordArray.add(controlEnd);
    coordArray.add(p2);
    // plot controls
    SVGLine line12 = new SVGLine(p1, controlStart); 
    line12.setStroke("red");
    svg.appendChild(line12);
    SVGLine line21 = new SVGLine(p2, controlEnd); 
    svg.appendChild(line21);
    return coordArray;
}
4

2 に答える 2

3

ベジェ曲線には、各ポイントの勾配と曲率とともに、データポイントが必要です。グラフィックプログラムでは、勾配は制御線の勾配によって設定され、曲率は長さによって視覚化されます。

ユーザーがそのような制御線を入力していない場合は、各ポイントでの勾配と曲率を推定する必要があります。ウィキペディアのページhttp://en.wikipedia.org/wiki/Cubic_Hermite_spline、特に「データセットの補間」セクションには、これらの値を直接取得する式があります。

通常、ポイントからこれらの値を推定するには、有限差分を使用します。したがって、推定に役立つように、両側のポイントの値を使用します。ここでの唯一の選択は、隣接するポイントが1つしかないエンドポイントを処理する方法です。曲率をゼロに設定するか、曲線が周期的である場合は、「ラップアラウンド」して最後のポイントの値を使用できます。

私が参照したウィキペディアのページにも他のスキームがありますが、他のほとんどのページでは、設定方法を見つける必要がある他の「自由パラメーター」が紹介されているため、他のパラメーターの設定方法を決定するのに役立つ情報がない場合は、 d単純なスキームを選択して、結果が気に入ったかどうかを確認します。

ウィキペディアの記事が十分に明確でない場合はお知らせください。コードをノックアップします。

知っておくべきもう1つのポイント:あなたはどのような「種類の」ベジェ補間を求めていますか?ほとんどのグラフィックプログラムは2次元で3次ベジェを実行します(つまり、円のような曲線を描くことができます)が、サンプル画像は1次元関数近似であるように見えます(すべてのxに対してy値は1つだけです)。グラフィックプログラムのタイプ曲線は、私が参照したページでは実際には言及されていません。傾きと曲率の推定値をhttp://en.wikipedia.org/wiki/B%C3%A9zier_curve(Cubic Bezier)に示されている形式の制御ベクトルに変換するために必要な計算には、ある程度の作業が必要ですが、アイデアは似ている。

以下は、入力が3つのポイントP1、P2、P3であると仮定した場合の、可能なスキームの図とアルゴリズムです。

ベジェ補間スキーム

角度(P3、P1、C1)と(P2、P1、C2)が等しくなるように線(C1、P1、C2)を作成します。同様の方法で、他の濃い灰色の線を作成します。これらの濃い灰色の線(C1、C2、C3とマークされている)の交点は、ベジェ曲線wikipediaサイトの画像と同じ意味でコントロールポイントになります。したがって、(P3、P1)などの各赤い曲線は、点(P3、C1、P1)によって定義される2次ベジェ曲線です。赤い曲線の構成は、ウィキペディアのサイトに記載されているものと同じです。

ただし、ベジェ曲線ウィキペディアページのコントロールベクトルが、使用しているコントロールベクトルの種類と一致していないように見えるため、2つのアプローチを同等にする方法を理解する必要がある場合があります。

于 2013-01-18T11:54:39.667 に答える
1

制御点の選択を簡素化する3次スプラインの代わりに2次スプラインでこれを試しました(各ポイントの勾配を選択して、隣接する間隔の平均勾配の加重平均にし、データで曲線に接線を描画します)ポイントとそれらの接線が交差するコントロールポイントを貼り付けます)が、エンドポイントのグラデーションを設定するための適切なポリシーを見つけることができませんでした。そこで、代わりにラグランジュフィッティングを選択しました。

function lagrange(points) { //points is  [ [x1,y1], [x2,y2], ... ]
//  See: http://www.codecogs.com/library/maths/approximation/interpolation/lagrange.php
  var j,n = points.length;
  var p = [];
  for (j=0;j<n;j++) {
    p[j] = function (x,j) { //have to pass j cos JS is lame at currying
      var k, res = 1;
      for (k=0;k<n;k++)
        res*=( k==j ? points[j][1] : ((x-points[k][0])/(points[j][0]-points[k][0])) );
      return res; 
    }
  }
  return function(x) {
    var i, res = 0;
    for (i=0;i<n;i++)
      res += p[i](x,i);
    return res;
  }
}

それで、たくさんのサンプルを作って直線でつなぎます。

あなたのデータ(私のような)が実世界の測定値で構成されている場合、これはまだ間違っています。これらはランダムエラーの影響を受けやすく、カーブを強制的にすべてに正確にヒットさせる手法を使用すると、ポイント間に愚かな谷や丘ができます。このような場合、データがどの多項式の順序に適合するかを自問する必要があります。そして...まあ...それが私が理解しようとしていることです。

于 2014-01-17T16:11:32.033 に答える