y coordinate for a given x cubic bezier

前端 未结 4 1593
情歌与酒
情歌与酒 2020-12-09 05:53

This question is very similar to: Quadratic bezier curve: Y coordinate for a given X?. But this one is cubic...

I\'m using the getBezier function to calculate the Y

4条回答
  •  [愿得一人]
    2020-12-09 06:32

    I used the algorithm from this page and wrote it down in JavaScript. It works for all the cases I have tested so far. (And doesn't use a while loop.)

    Call the solveCubicBezier function. Pass in the x values of all the control points and the x value you want to get the y coordinate from. For example:

    var results = solveCubicBezier(p0.x, p1.x, p2.x, p3.x, myX);
    

    results is an array containing the 't' values originally passed into the Bezier function. The array can contain 0 to 3 elements, because not all x values have a corresponding y value, and some even have multiple.

    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;
    
    }
    

提交回复
热议问题