6

我正在使用贝塞尔曲线作为我的宇宙飞船进入车站停靠时的路径。我有一个简单的算法来计算船舶在时间 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;

    }
}

曲线起点为飞船所在位置,终点为对接舱入口(图中红点)。宇宙飞船的方向有一个归一化向量,停靠舱有另一个归一化向量来指示飞船必须行进的方向,以便在它到达时直接对准停靠舱(图表上的黄线)

绿线是飞船的可能路径,紫色圆圈是飞船的半径。最后,黑框是车站的边界框。

在此处输入图像描述

我有两个问题:

  1. 宇宙飞船应该只能以每秒 r 弧度转动
  2. 宇宙飞船无法飞过空间站

我假设这转化为:

一种)。找到“曲线因素”(控制点长度),这将给出一条船不必转弯太紧的路径

乙)。找到它无法避免与空间站相撞的宇宙飞船位置/方向(并创建一条路径将其引导出该状态,因此它可以继续进行 a))

但是,对于这两种情况,我没有太多运气找到解决方案。我已经有代码来检测向量、框、点和球体之间的交叉点,但还没有贝塞尔曲线。我还有一些功能可以让我找到两点之间的距离。

非常感激任何的帮助

谢谢,詹姆斯

4

1 回答 1

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..q6成为第二段。

将曲线细分 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,您可以细分三个曲线:

  • 一个在两个端点处的速度为零 ( C0)。这将产生一条直线。
  • 一个在第一个端点处速度为零,另一个在第二个端点处 ( C1)。
  • 第一个端点处的速度为 1,第二个端点处的速度为 0 ( C2)。

如果您以相同的方式细分所有曲线,您可以快速评估最终曲线的控制点。您混合相应的控制点,由端点处的速度参数化:

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

您可以使用此找到有效的参数范围,以便没有线段(评估为直线段)与车站相交。(v1并且v2可以超过 1.0)。

于 2012-07-13T06:42:46.303 回答