给定x立方贝塞尔曲线的y坐标

bop*_*vla 9 javascript algorithm math bezier

这个问题非常类似于:二次贝塞尔曲线:给定X的Y坐标?.但这个是立方体......

我正在使用getBezier函数来计算贝塞尔曲线的Y坐标.贝塞尔曲线始终在(0,0)处开始,始终在(1,1)处结束.

我知道X值,所以我试图将其插入百分比(我是一个白痴).但这显然不起作用.你能提供解决方案吗?这是必要的,它是一个白痴证明功能.喜欢:

function yFromX (c2x,c2y,c3x,c3y) { //c1 = (0,0) and c4 = (1,1), domainc2 and domainc3 = [0,1]
    //your magic
    return y;
}
Run Code Online (Sandbox Code Playgroud)

ell*_*ben 7

由于问题是如此有限(函数x(t)是单调的),我们可能可以使用一种非常便宜的解决方法 - 二进制搜索.

var bezier = function(x0, y0, x1, y1, x2, y2, x3, y3, t) {
    /* whatever you're using to calculate points on the curve */
    return undefined; //I'll assume this returns array [x, y].
};

//we actually need a target x value to go with the middle control
//points, don't we? ;)
var yFromX = function(xTarget, x1, y1, x2, y2) {
  var xTolerance = 0.0001; //adjust as you please
  var myBezier = function(t) {
    return bezier(0, 0, x1, y1, x2, y2, 1, 1, t);
  };

  //we could do something less stupid, but since the x is monotonic
  //increasing given the problem constraints, we'll do a binary search.

  //establish bounds
  var lower = 0;
  var upper = 1;
  var percent = (upper + lower) / 2;

  //get initial x
  var x = myBezier(percent)[0];

  //loop until completion
  while(Math.abs(xTarget - x) > xTolerance) {
    if(xTarget > x) 
      lower = percent;
    else 
      upper = percent;

    percent = (upper + lower) / 2;
    x = myBezier(percent)[0];
  }
  //we're within tolerance of the desired x value.
  //return the y value.
  return myBezier(percent)[1];
};
Run Code Online (Sandbox Code Playgroud)

这肯定会破坏你的约束之外的一些输入.


Dav*_*ker 7

我使用了这个页面上的算法并用 JavaScript 写下来。它适用于我迄今为止测试过的所有情况。(并且不使用while循环。)

调用 solveCubicBezier 函数。传入所有控制点的 x 值和要从中获取 y 坐标的 x 值。例如:

var results = solveCubicBezier(p0.x, p1.x, p2.x, p3.x, myX);
Run Code Online (Sandbox Code Playgroud)

results是一个包含最初传递到 Bezier 函数的 't' 值的数组。该数组可以包含 0 到 3 个元素,因为并非所有 x 值都有对应的 y 值,有些甚至有多个。

function solveQuadraticEquation(a, b, c) {

    var discriminant = b * b - 4 * a * c;

    if (discriminant < 0) {
        return [];

    } else {
        return [
            (-b + Math.sqrt(discriminant)) / (2 * a),
            (-b - Math.sqrt(discriminant)) / (2 * a)
        ];
    }

}

function solveCubicEquation(a, b, c, d) {

    if (!a) return solveQuadraticEquation(b, c, d);

    b /= a;
    c /= a;
    d /= a;

    var p = (3 * c - b * b) / 3;
    var q = (2 * b * b * b - 9 * b * c + 27 * d) / 27;

    if (p === 0) {
        return [ Math.pow(-q, 1 / 3) ];

    } else if (q === 0) {
        return [Math.sqrt(-p), -Math.sqrt(-p)];

    } else {

        var discriminant = Math.pow(q / 2, 2) + Math.pow(p / 3, 3);

        if (discriminant === 0) {
            return [Math.pow(q / 2, 1 / 3) - b / 3];

        } else if (discriminant > 0) {
            return [Math.pow(-(q / 2) + Math.sqrt(discriminant), 1 / 3) - Math.pow((q / 2) + Math.sqrt(discriminant), 1 / 3) - b / 3];

        } else {

            var r = Math.sqrt( Math.pow(-(p/3), 3) );
            var phi = Math.acos(-(q / (2 * Math.sqrt(Math.pow(-(p / 3), 3)))));

            var s = 2 * Math.pow(r, 1/3);

            return [
                s * Math.cos(phi / 3) - b / 3,
                s * Math.cos((phi + 2 * Math.PI) / 3) - b / 3,
                s * Math.cos((phi + 4 * Math.PI) / 3) - b / 3
            ];

        }

    }
}

function roundToDecimal(num, dec) {
    return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
}

function solveCubicBezier(p0, p1, p2, p3, x) {

    p0 -= x;
    p1 -= x;
    p2 -= x;
    p3 -= x;

    var a = p3 - 3 * p2 + 3 * p1 - p0;
    var b = 3 * p2 - 6 * p1 + 3 * p0;
    var c = 3 * p1 - 3 * p0;
    var d = p0;

    var roots = solveCubicEquation(
        p3 - 3 * p2 + 3 * p1 - p0,
        3 * p2 - 6 * p1 + 3 * p0,
        3 * p1 - 3 * p0,
        p0
    );

    var result = [];
    var root;
    for (var i = 0; i < roots.length; i++) {
        root = roundToDecimal(roots[i], 15);
        if (root >= 0 && root <= 1) result.push(root);
    }

    return result;

}
Run Code Online (Sandbox Code Playgroud)

  • 立方根函数可以实现为`function crt(v) { if(v&lt;0) return -Math.pow(-v,1/3); 返回 Math.pow(v,1/3); }`。这只给出了实根,而不是包括虚根在内的所有三个根,这通常是找到的,因为我们无论如何都不能在 JS 中真正使用复数。我用这个立方根创建了一个实现,在 http://jsbin.com/fewih/1/edit (3认同)