4

Web を検索したところ、さまざまなフォーラムで、3 次曲線を 2 次曲線で近似することをほのめかしているさまざまな人々を見かけます。しかし、私は式を見つけることができません。

私が欲しいのはこれです:

入力: startX、startY、control1X、control1Y、control2X、control2Y、endX、endY 出力: startX、startY、controlX、controlY、endX、endY

実は始点と終点が同じなので、あとは…

入力: startX、startY、control1X、control1Y、control2X、control2Y、endX、endY 出力: controlX、controlY

4

8 に答える 8

7

前述のように、4 つのコントロール ポイントから 3 つにすると、通常は概算になります。正確になるケースは 1 つだけです。つまり、3 次ベジェ曲線が実際には次数を上げた 2 次ベジェ曲線である場合です。

高度高度方程式を使用して近似値を求めることができます。それは簡単で、結果は通常かなり良いです。

3 次 Q0..Q3 の制御点と 2 次 P0..P2 の制御点を呼び出しましょう。次に、高度の場合、式は次のとおりです。

Q0 = P0
Q1 = 1/3 P0 + 2/3 P1
Q2 = 2/3 P1 + 1/3 P2
Q3 = P2

あなたの場合、Q0..Q3 があり、P0..P2 を解いています。上記の式から P1 を計算するには、次の 2 つの方法があります。

P1 = 3/2 Q1 - 1/2 Q0
P1 = 3/2 Q2 - 1/2 Q3

これが次数立方体である場合、両方の方程式は P1 に対して同じ答えを与えます。そうではない可能性が高いため、最善の策はそれらを平均化することです。そう、

P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3

あなたの用語に翻訳するには:

controlX = -0.25*startX + .75*control1X + .75*control2X -0.25*endX

Y も同様に計算されます - 次元は独立しているため、これは 3d (または nd) で機能します。

これは概算になります。より良い近似が必要な場合、それを取得する 1 つの方法は、deCastlejau アルゴリズムを使用して初期立方体を再分割し、各セグメントを次数削減することです。より良い連続性が必要な場合は、より迅速で汚れの少ない他の近似方法があります。

于 2010-01-08T18:16:34.207 に答える
4

3 次にはループとカスプを含めることができますが、2 次には含めることができません。これは、単純な解決策がほとんど存在しないことを意味します。3 次が既に 2 次の場合、単純な解が存在します。通常、3 次を 2 次の部分に分割する必要があります。そして、細分化するための重要なポイントは何かを決定する必要があります。

http://fontforge.org/bezier.html#ps2ttfは次のように述べています:結果を悪化させ、より多くの点を使用し、近似は変曲点を無視する場合ほど近くに見えません。したがって、私はそれらを無視します。」

これは本当です。変曲点 (3 次導関数) は十分ではありません。しかし、3 次関数の 1 次導関数である局所的な極値 (最小、最大) も考慮に入れ、それらすべてにブレークを強制すると、サブ曲線はすべて二次曲線になり、二次曲線で表すことができます。

以下の関数をテストしましたが、期待どおりに機能します (立方体のすべての臨界点を見つけ、立方体を下向きの立方体に分割します)。これらのサブ曲線が描かれると、曲線は元の 3 次曲線とまったく同じになりますが、何らかの理由で、サブ曲線が 2 次曲線として描かれると、結果はほぼ正しくなりますが、正確ではありません。

したがって、この答えは問題の厳密なヘルプではありませんが、これらの関数は 3 次から 2 次への変換の開始点を提供します。

局所的な極値と変曲点の両方を見つけるには、以下get_t_values_of_critical_points()でそれらを提供する必要があります。の

function compare_num(a,b) {
  if (a < b) return -1;
  if (a > b) return 1;
  return 0;
}

function find_inflection_points(p1x,p1y,p2x,p2y,p3x,p3y,p4x,p4y)
{
  var ax = -p1x + 3*p2x - 3*p3x + p4x;
  var bx = 3*p1x - 6*p2x + 3*p3x;
  var cx = -3*p1x + 3*p2x;

  var ay = -p1y + 3*p2y - 3*p3y + p4y;
  var by = 3*p1y - 6*p2y + 3*p3y;
  var cy = -3*p1y + 3*p2y;
  var a = 3*(ay*bx-ax*by);
  var b = 3*(ay*cx-ax*cy);
  var c = by*cx-bx*cy;
  var r2 = b*b - 4*a*c;
  var firstIfp = 0;
  var secondIfp = 0;
  if (r2>=0 && a!==0)
  {
    var r = Math.sqrt(r2);
    firstIfp = (-b + r) / (2*a);
    secondIfp = (-b - r) / (2*a);
    if ((firstIfp>0 && firstIfp<1) && (secondIfp>0 && secondIfp<1))
    {
      if (firstIfp>secondIfp)
      {
        var tmp = firstIfp;
        firstIfp = secondIfp;
        secondIfp = tmp;
      }
      if (secondIfp-firstIfp >0.00001)
        return [firstIfp, secondIfp];
      else return [firstIfp];
    }
    else if (firstIfp>0 && firstIfp<1)
      return [firstIfp];
    else if (secondIfp>0 && secondIfp<1)
    {
      firstIfp = secondIfp;
      return [firstIfp];
    }
    return [];
  }
  else return [];
}

function get_t_values_of_critical_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
    var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
    b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
    c = p1x - c1x,
    t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
    t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
    tvalues=[];
    Math.abs(t1) > "1e12" && (t1 = 0.5);
    Math.abs(t2) > "1e12" && (t2 = 0.5);
    if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1)
    if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);

    a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
    b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
    c = p1y - c1y;
    t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
    t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
    Math.abs(t1) > "1e12" && (t1 = 0.5);
    Math.abs(t2) > "1e12" && (t2 = 0.5);
    if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1);
    if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);

    var inflectionpoints = find_inflection_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
    if (inflectionpoints[0]) tvalues.push(inflectionpoints[0]);
    if (inflectionpoints[1]) tvalues.push(inflectionpoints[1]);

    tvalues.sort(compare_num);
    return tvalues;
};

そして、これらの重要な t 値 (範囲は 0 ~ 1) がある場合、3 次をパーツに分割できます。

function CPoint()
{
  var arg = arguments;
  if (arg.length==1)
  {
    this.X = arg[0].X;
    this.Y = arg[0].Y;
  }
  else if (arg.length==2)
  {
    this.X = arg[0];
    this.Y = arg[1];
  }
}

function subdivide_cubic_to_cubics()
{
    var arg = arguments;
    if (arg.length!=9) return [];
    var m_p1 = {X:arg[0], Y:arg[1]};
  var m_p2 = {X:arg[2], Y:arg[3]};
  var m_p3 = {X:arg[4], Y:arg[5]};
  var m_p4 = {X:arg[6], Y:arg[7]};
  var t = arg[8];
  var p1p = new CPoint(m_p1.X + (m_p2.X - m_p1.X) * t,
                       m_p1.Y + (m_p2.Y - m_p1.Y) * t);
  var p2p = new CPoint(m_p2.X + (m_p3.X - m_p2.X) * t,
                       m_p2.Y + (m_p3.Y - m_p2.Y) * t);
  var p3p = new CPoint(m_p3.X + (m_p4.X - m_p3.X) * t,
                       m_p3.Y + (m_p4.Y - m_p3.Y) * t);
  var p1d = new CPoint(p1p.X + (p2p.X - p1p.X) * t,
                       p1p.Y + (p2p.Y - p1p.Y) * t);
  var p2d = new CPoint(p2p.X + (p3p.X - p2p.X) * t,
                       p2p.Y + (p3p.Y - p2p.Y) * t);
  var p1t = new CPoint(p1d.X + (p2d.X - p1d.X) * t,
                       p1d.Y + (p2d.Y - p1d.Y) * t);
  return [[m_p1.X, m_p1.Y, p1p.X, p1p.Y, p1d.X, p1d.Y, p1t.X, p1t.Y],
          [p1t.X, p1t.Y, p2d.X, p2d.Y, p3p.X, p3p.Y, m_p4.X, m_p4.Y]];
}

subdivide_cubic_to_cubics()上記のコードでは、元の 3 次曲線を値 t で 2 つの部分に分割します。は、t 値でソートされた配列として t 値を返すためget_t_values_of_critical_points()、すべての t 値を簡単にトラバースして、対応するサブ カーブを取得できます。これらの分割された曲線がある場合、2 番目のサブ曲線を次の t 値で分割する必要があります。

すべての分割が完了すると、すべてのサブ カーブの制御点が得られます。これで、二次への三次制御点の変換だけが残りました。すべてのサブ カーブがダウンエレベート キュービックになっているため、対応する 2 次コントロール ポイントは簡単に計算できます。2 次制御点の最初と最後は、3 次曲線 (サブ曲線) の最初と最後の制御点と同じで、中央の制御点は、P1-P2 と P4-P3 の線が交差する点にあります。

于 2013-01-25T02:40:32.430 に答える
3

規則/用語

  • キュービックの定義:P1 / 2-アンカーポイント、C1/C2コントロールポイント
  • | x | xのユークリッドノルムです
  • 立方体の中点近似:立方体と同じアンカーを共有し、C =(3・C2-P2 + 3・C1-P1)/4に制御点を持つクワッド

アルゴリズム

  1. 絶対精度を選択する(prec
  2. Tdivを(3次)方程式の平方根として計算しますsqrt(3)/ 18・| P2 --3・C2 + 3・C1 --P1 | / 2・Tdiv ^ 3 = prec
  3. Tdiv <0.5の場合、Tdivで立方体を除算します。最初のセグメント[0..Tdiv]は、中点近似により、prec未満の欠陥を持つ2次式で近似できます。2番目の結果セグメント(1-Tdivに対応)を使用して、手順2から繰り返します。
  4. 0.5 <=Tdiv<1-単純に立方体を2つに分割します。2つの半分は、中点近似で近似できます。
  5. Tdiv>=1-立方体全体を中点近似で近似できます

ステップ2の「魔法の公式」は、このページで(インタラクティブな例を使用して)示されています。

于 2010-07-26T12:57:29.090 に答える
2

tfinniga の回答の別の派生:まず 、2 次および 3 次ベジエ曲線の式については、
ウィキペディアのベジエ曲線を参照してください (これも素晴らしいアニメーションです)。

Q(t) = (1-t)^2 P0 + 2 (1-t) t Q + t^2 P3
P(t) + (1-t)^3 P0 + 3 (1-t)^2 t P1 + 3 (1-t) t^2 P2 + t^3 P3

これらが中央で一致する必要があります。t = 1/2:

(P0 + 2 Q + P3) / 4 = (P0 + 3 P1 + 3 P2 + P3) / 8  
=> Q = P1 + P2 - (P0 + P1 + P2 + P3) / 4  

(このように書かれた Q には、幾何学的な解釈があります。

Pmid = middle of P0 P1 P2 P3  
P12mid = midway between P1 and P2  
draw a line from Pmid to P12mid, and that far again: you're at Q.  

これが理にかなっていることを願っています-いくつかの例を描いてください.)

于 2010-01-11T11:25:34.297 に答える
1

エイドリアンの解は単一の3次関数に最適ですが、3次関数が滑らかな3次スプラインのセグメントである場合、彼の中点近似法を使用すると、セグメントのノードでの勾配の連続性が失われます。したがって、フォントグリフを使用している場合、またはその他の理由で曲線の滑らかさを維持したい場合は、 http://fontforge.org/bezier.html#ps2ttfで説明されている方法の方がはるかに優れています(おそらくそうです)。 。

これは古い質問ですが、私のような多くの人が検索結果に表示されるので、ここに投稿します。

于 2013-01-07T12:50:25.593 に答える
1

一般に、複数の 2 次曲線を使用する必要があります。3 次曲線の多くの場合、1 つの 2 次曲線で漠然と近似することさえできません。

http://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm (インタラクティブなデモンストレーションを含む)に、この問題とその解決方法について説明している優れた記事があります。

于 2010-01-06T19:54:15.543 に答える
0

オープンソースの Postcript フォントから Truetype フォントへのコンバーターを探してみてください。私は彼らがそれを持っていると確信しています。Postscript は 3 次ベジェ曲線を使用しますが、Truetype は 2 次ベジェ曲線を使用します。幸運を。

于 2010-06-19T23:04:56.407 に答える
0

別の alg を使用して 1 つの曲線を描画しようとする代わりに、一連の曲線を描画することになるでしょう。半分の円を 2 つ描いて 1 つの円を作るようなものです。

于 2010-01-05T22:17:15.237 に答える