Cubic Bezier曲线:最大梯度和碰撞避免?

时间:2012-07-12 15:03:46

标签: java algorithm math bezier

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

    }
}

曲线起点是飞船位置,终点是停靠站的入口(图中的红点)。太空船的方向有一个标准化矢量,对接舱有另一个标准化矢量,用于指示船舶必须行进的方向,以便在到达时直接对准停靠站(图中的黄线)

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

enter image description here

我有两个问题:

  1. 宇宙飞船应该只能以每秒r弧度转动
  2. 太空飞船不能飞过车站
  3. 我认为这转化为:

    A)。找到“曲线因子”(控制点长度),它将给出一条船不必转得太紧的路径

    B)。找到飞船的位置/方向,从中无法避免与车站发生碰撞(并创建一条引导它离开该状态的路径,因此它可以继续使用a))

    然而,对于这两者,我没有太多运气找到解决方案。我已经有代码来检测矢量,方框,点和球体之间的交叉点,但还没有贝塞尔曲线。我也有功能让我找到两点之间的距离。

    非常感谢任何帮助

    谢谢, 詹姆斯

1 个答案:

答案 0 :(得分:3)

找到Cubic Bezier曲线的精确交点涉及求解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次,并将控制点用作折线。


可以在每个细分的终点计算curvature

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,在第二个端点处具有零(C2)。

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

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

您可以使用此查找有效的参数范围,以便没有任何段(评估为直线段)与工作站相交。 (v1v2可以超过1.0)。