3

x 軸に 0 から 1 の間、y 軸に 0 から 1 の間の正規化された値を持つ単純な 2D 行列があり、この行列に 3 つの点があるとします。たとえば、P1 (x=0.2,y=0.9)、P2 (x=0.5,y=0.1) と P3 (x=0.9,y=0.4)。

このポイントを通る曲線を簡単に計算するにはどうすればよいですか。つまり、任意の x の y を与える関数を持つことを意味します。

3 点を通る曲線はいくつでも可能です。しかし、私の言いたいことはわかります。オーディオ サンプルの補間に使用でき、ボリューム フェード カーブの計算に使用でき、ゲームでのモンスターの歩行経路の計算に使用できる滑らかな曲線が必要です。

今、私はこの質問を約3日間ネットで検索しましたが、このタスクに使用できる解決策がないとは信じられません. Catmull-rom-Splines、ベジエ曲線、およびそのすべての理論的なものを扱っているすべてのテキストには、少なくとも 1 つのポイントがあり、それが私にとっては使用できないものになっています。たとえば、Catmull-Rom-splines では、コントロール ポイント間に一定の距離が必要です (このコードを使用して、4. ポイント y を 3. ポイント y に設定します)。

void CatmullRomSpline(float *x,float *y,float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,float u)
{
//x,y are calculated for x1,y1,x2,y2,x3,y3 and x4,y4 if u is the normalized distance (0-1) in relation to the distance between x2 and x3 for my whiched point

float u3,u2,f1,f2,f3,f4;

u3=u*u*u;
u2=u*u;
f1=-0.5f * u3 + u2 -0.5f *u;
f2= 1.5f * u3 -2.5f * u2+1.0f;
f3=-1.5f * u3 +2.0f * u2+0.5f*u;
f4=0.5f*u3-0.5f*u2;

*x=x1*f1+x2*f2+x3*f3+x4*f4;
*y=y1*f1+y2*f2+y3*f3+y4*f4;

}

しかし、x1 から x4 が y の計算に影響を与えているとは思えないので、x1 から x4 までの距離は同じでなければならないと思いますか?

...

または、ベジエコードはポイントを通る曲線を計算しません。ポイント (少なくとも 2. ポイント) は、線に力の効果しかないように見えます。

typedef struct Point2D
{
double x;
double y;
} Point2D;

class bezier
{
std::vector<Point2D> points;
bezier();
void PushPoint2D( Point2D point );
Point2D GetPoint( double time );
~bezier();
};

void bezier::PushPoint2D(Point2D point)
{
points.push_back(point);
}

Point2D bezier::GetPoint( double x )
{
int i;
Point2D p;

p.x=0;
p.y=0;

if( points.size() == 1 ) return points[0];
if( points.size() == 0 ) return p;

bezier b;
for (i=0;i<(int)points.size()-1;i++)
{
    p.x = ( points[i+1].x - points[i].x ) * x + points[i].x;
    p.y = ( points[i+1].y - points[i].y ) * x + points[i].y;
    if (points.size()<=2) return p;
    b.PushPoint2D(p);
}

return b.GetPoint(x);
}

double GetLogicalYAtX(double x)
{
bezier bz;
Point2D p;

p.x=0.2;
p.y=0.9;
bz.PushPoint2D(p);

p.x=0.5;
p.y=0.1;
bz.PushPoint2D(p);

p.x=0.9;
p.y=0.4;
bz.PushPoint2D(p);

p=bz.GetPoint(x);

return p.y;
}

これは何もないよりはましですが、1. 非常に遅く (再帰的)、2. 前述のように 2. ポイントを通る線を実際には計算しません。

私を助けることができる数学的頭脳は外にありますか?

4

2 に答える 2

0
static bezier From3Points(const Point2D &a, const Point2D &b, const Point2D &c)
{
    bezier result;
    result.PushPoint2D(a);

    Point2D middle;
    middle.x = 2*b.x - a.x/2 - c.x/2;
    middle.y = 2*b.y - a.y/2 - c.y/2;
    result.PushPoint2D(middle);

    result.PushPoint2D(c);
    return result;
}

テストされていませんが、t=0.5で曲線が点'b'を通過するベジェ曲線を返す必要があります。

さらに(よりテストされていないコードが先にあります)、そのようなバーンスタイン基底多項式を使用してポイントを計算できます。

static int binomialcoefficient (int n, int k)
{
    if (k == 0)
        return 1;
    if (n == 0)
        return 0;

    int result = 0;
    for (int i = 1; i <= k; ++i)
    {
        result += (n - (k - i))/i;
    }
    return result;
}

static double bernstein (int v, int n, double t)
{
    return binomialcoefficient(v,n) * pow(t,v) * pow(1 - t,n - v);
}

Point2D GetPoint (double t)
{
    Point2D result;
    result.x = 0;
    result.y = 0;

    for (int i = 0; i < points.size(); ++i)
    {
        double coeff = bernstein (i,points.size(),t);
        result.x += coeff * points[i].x;
        result.y += coeff * points[i].y;
    }

    return result;
}
于 2013-02-21T20:04:26.037 に答える
0

コードを提供してくれた TOCS (Scott) に感謝します。時間があれば試してみます。しかし、私が今テストしたのは、INFACT によるヒントです (回答 3): この「大規模な多項式」は、私が探しているものに非常に近いものです。

ラグレンジ補間のコードを追加したため、クラスのベジエの名前を曲線に変更しました。また、コードが計算されているグラフィック表示の 3 つの写真を追加しました。

図 1 では、古いベジエ関数の緩い中間点を見ることができます。

写真 2 では、ラグランジュ補間の全点通過結果を見ることができます。

写真 3 では、唯一の問題を見ることができます。それとも、「私も解決する必要があること」と言うべきでしょうか (とにかく、これが今までの最善の解決策です): 中間点を移動すると、カーブが速くなり、速くなります上限または下限まで)。上下にもっとスムーズに行きたいです。より対数関数のように見えるように。0 と 1 の間の y 境界をすぐに超えないようにします。

今私のコードは次のようになります:

curve::curve(void)
{
}

void curve::PushPoint2D(Point2D point)
{
    points.push_back(point);
}

Point2D curve::GetPoint( double x )
{
//GetPoint y for x with bezier-mathematics...

//was the only calculating function in old class "bezier"
//now the class is renamed "curve"
int i;
Point2D p;

p.x=0;
p.y=0;

if( points.size() == 1 ) return points[0];
if( points.size() == 0 ) return p;

curve b;
for (i=0;i<(int)points.size()-1;i++)
{
    p.x = ( points[i+1].x - points[i].x ) * x + points[i].x;
    p.y = ( points[i+1].y - points[i].y ) * x + points[i].y;
    if (points.size()<=2) return p;
    b.PushPoint2D(p);
}

return b.GetPoint(x);
}

//THIS IS NEW AND VERY VERY COOL
double curve::LagrangeInterpolation(double x)
{
double y = 0;

for (int i = 0; i <= (int)points.size()-1; i++)
{
    double numerator = 1;
    double denominator = 1;

    for (int c = 0; c <= (int)points.size()-1; c++)
    {
        if (c != i)
        {
            numerator *= (x - points[c].x);
            denominator *= (points[i].x - points[c].x);
        }
    }

    y += (points[i].y * (numerator / denominator));

}

return y;
}

curve::~curve(void)
{
}


double GetLogicalYAtX(double x)
{
curve cv;
Point2D p;

p.x=0; //always left edge
p.y=y1; //now by var
cv.PushPoint2D(p);

p.x=x2; //now by var
p.y=y2; //now by var
cv.PushPoint2D(p);

p.x=1; //always right edge
p.y=y3; //now by var
cv.PushPoint2D(p);

//p=cv.GetPoint(x);

//return p.y;

return cv.LagrangeInterpolation(x);
}

古いベジエ曲線 ラグランジェかっこいいけど… ...すぐにクリッピング...

新しいソリューションをもう少し「ソフト」にする方法について何か考えはありますか? 2.曲線が境界から出ないように、より広い領域でポイントを移動できるようにするには?ありがとうございました

于 2013-02-22T16:25:22.657 に答える