Search code examples
javascriptalgorithmmathbezier

y coordinate for a given x cubic bezier


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 coordinates of a bezier curve. The bezier curve starts always at (0,0) and ends always at (1,1).

I know the X value, so I tried to insert it as percent (I'm a moron). But that didn't work, obviously. Could you provide a solution? It's necessary it's an idiot proof function. Like:

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

Solution

  • Since the problem is so limited (function x(t) is monotonic), we can probably get away with using a pretty cheap method of solution-- binary search.

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

    This should certainly break on some inputs outside of your constraints.