6

宇宙船がステーションのドックに入るとき、宇宙船が移動するパスとしてベジエ曲線を使用しています。3 次ベジエ曲線に沿って、船が時間 t にあるべき場所を計算する簡単なアルゴリズムがあります。

public class BezierMovement{
    public BezierMovement(){
        // start docking straight away in this test version
        initDocking();
    }

    private Vector3 p0;
    private Vector3 p1;
    private Vector3 p2;
    private Vector3 p3;

    private double tInc = 0.001d;
    private double t = tInc;

    protected void initDocking(){

        // get current location
        Vector3 location = getCurrentLocation();

        // get docking point
        Vector3 dockingPoint = getDockingPoint();

        // ship's normalised direction vector
        Vector3 direction = getDirection();

        // docking point's normalised direction vector
        Vector3 dockingDirection = getDockingDirection();

        // scalars to multiply normalised vectors by 
        // The higher the number, the "curvier" the curve
        float curveFactorShip = 10000.0f;
        float curveFactorDock = 2000.0f;

        p0 = new Vector3(location.x,location.y,location.z);

        p1 = new Vector3(location.x + (direction.x * curveFactorShip),
                         location.y + (direction.y * curveFactorShip),
                         location.z + (direction.z * curveFactorShip));

        p2 = new Vector3(dockingPoint.x + (dockingDirection.x * curveFactorDock),
                         dockingPoint.y + (dockingDirection.y * curveFactorDock),
                         dockingPoint.z + (dockingDirection.z * curveFactorDock));

        p3 = new Vector3(dockingPoint.x, dockingPoint.y, dockingPoint.z);


    }

    public void incrementPosition() {

        bezier(p0, p1, p2, p3, t, getCurrentLocation());

        // make ship go back and forth along curve for testing              
        t += tInc;

        if(t>=1){
            tInc = 0-tInc;
        } else if(t<0){
            tInc = 0-tInc;
        }

    }

    protected void bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, double t, Vector3 outputVector){

        double a = (1-t)*(1-t)*(1-t);
        double b = 3*((1-t)*(1-t))*t;
        double c = 3*(1-t)*(t*t);
        double d = t*t*t;

        outputVector.x = a*p0.x + b*p1.x + c*p2.x + d*p3.x;
        outputVector.y = a*p0.y + b*p1.y + c*p2.y + d*p3.y;
        outputVector.z = a*p0.z + b*p1.z + c*p2.z + d*p3.z;

    }
}

曲線の始点は宇宙船の位置で、終点はドッキング ベイの入り口です (図の赤い点)。宇宙船にはその方向の正規化されたベクトルがあり、ドッキング ベイには別の正規化されたベクトルがあり、船が到着したときにドッキング ベイにまっすぐに整列するために、船が進むべき方向を示します (図の黄色の線)。

緑色の線は宇宙船の可能な経路であり、紫色の円は宇宙船の半径です。最後に、ブラック ボックスはステーションのバウンディング ボックスです。

ここに画像の説明を入力

2 つの問題があります。

  1. 宇宙船は毎秒 r ラジアンでしか回転できないはずです
  2. 宇宙船はステーションを通過できません

これは次のように変換されると思います。

a)。船がきつく曲がる必要のない経路を与える「曲線係数」(制御点の長さ) を見つける

b)。ステーションとの衝突を避けられない宇宙船の位置/方向を見つける (そして、その状態からそれを導くためのパスを作成して、パート a) に乗ることができるようにする)

ただし、これらの両方で、解決策を見つけることができませんでした。ベクトル、ボックス、点、球の間の交点を検出するコードは既にありますが、ベジェ曲線はまだありません。2 点間の距離を求める関数もあります。

どんな助けでも大歓迎です

ありがとう、ジェームズ

4

1 に答える 1

3

3 次ベジエ曲線の正確な交点を見つけるには、5 次または 6 次の多項式を解く必要があります。より実現可能な解決策は、数値法を使用するか、ベジエ曲線を細分化することです。

protected void subdivide(
        Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3,
        Vector3 q0, Vector3 q1, Vector3 q2, Vector3 q3,
        Vector3 q4, Vector3 q5, Vector3 q6) {

    q0.x = p0.x; q0.y = p0.y; q0.z = p0.z;
    q6.x = p3.x; q6.y = p3.y; q6.z = p3.z;

    q1.x = (p0.x + p1.x) * 0.5;
    q1.y = (p0.y + p1.y) * 0.5;
    q1.z = (p0.z + p1.z) * 0.5;

    q5.x = (p2.x + p3.x) * 0.5;
    q5.y = (p2.y + p3.y) * 0.5;
    q5.z = (p2.z + p3.z) * 0.5;

    double x3 = (p1.x + p2.x) * 0.5;
    double y3 = (p1.y + p2.y) * 0.5;
    double z3 = (p1.z + p2.z) * 0.5;

    q2.x = (q1.x + x3) * 0.5;
    q2.y = (q1.y + y3) * 0.5;
    q2.z = (q1.z + z3) * 0.5;

    q4.x = (x3 + q1.x) * 0.5;
    q4.y = (y3 + q1.y) * 0.5;
    q4.z = (z3 + q1.z) * 0.5;

    q3.x = (q2.x + q4.x) * 0.5;
    q3.y = (q2.y + q4.y) * 0.5;
    q3.z = (q2.z + q4.z) * 0.5;
}

q1..q3が最初のセグメントになります。q3.. q62 番目のセグメントになります。

曲線を 2 ~ 5 回分割し、制御点をポリラインとして使用します。


曲率は、各セグメントの終点で計算できます。

protected double curvatureAtStart(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) {
    double dx1 = p1.x - p0.x;
    double dy1 = p1.y - p0.y;
    double dz1 = p1.z - p0.z;

    double A = dx1 * dx1 + dy1 * dy1 + dz1 * dz1;

    double dx2 = p0.x - 2*p1.x + p2.x;
    double dy2 = p0.y - 2*p1.y + p2.y;
    double dz2 = p0.z - 2*p1.z + p2.z;

    double B = dx1 * dx2 + dy1 * dy2 + dz1 * dz2;

    double Rx = (dx2 - dx1*B/A)/A*2/3;
    double Ry = (dy2 - dy1*B/A)/A*2/3;
    double Rz = (dz2 - dz1*B/A)/A*2/3;

    return Math.sqrt(Rx * Rx + Ry * Ry + Rz * Rz);
}

問題 1を解決するには、曲線を数回分割し、各セグメントの終点で曲率を計算します。これは単なる近似値ですが、曲率の高いセグメントを選択的に細分割して、その領域でより適切な近似値を得ることができます。


問題 2を解決するには、3 つの曲線を細分化します。

  • 両方の終点で速度がゼロの 1 つ ( C0)。これにより、直線が生成されます。
  • 1 つは最初のエンドポイントで速度がゼロで、もう 1 つは 2 番目のエンドポイントです ( C1)。
  • 最初のエンドポイントで速度が 1 で、2 番目のエンドポイントで速度がゼロの 1 つ ( C2)。

すべての曲線を同じ方法で細分化すると、最終的な曲線の制御点をすばやく評価できます。エンドポイントでの速度によってパラメーター化された、対応するコントロール ポイントをブレンドします。

C[i] = C0[i] + (C1[i] - C0[i])*v1 + (C2[i] - C0[i])*v2

これにより、(直線セグメントとして評価される) セグメントがステーションと交差しないように、有効なパラメータ範囲を見つけることができます。(v1そしてv21.0 を超えることができます)。

于 2012-07-13T06:42:46.303 に答える