简体   繁体   English

给定y在贝塞尔曲线上获得x

[英]Get x on Bezier curve given y

I have a Bezier curve: (0,0) , (.25,.1) , (.25,1) , and (1,1) . 我有一个贝塞尔曲线: (0,0)(.25,.1)(.25,1)(1,1)

This is graphically seen here: http://cubic-bezier.com/#.25,.1,.25,1 可以在此处以图形方式看到: http : //cubic-bezier.com/#.25,.1,.25,1

We see on the x axis is time. 我们在x轴上看到的是时间。

This is my unknown. 这是我的未知数。 This is a unit cell. 这是一个晶胞。 So I was wondering how can I get x when y is 0.5? 所以我想知道当y为0.5时如何得到x?

Thanks 谢谢

I saw this topic: y coordinate for a given x cubic bezier 我看到了这个主题: 给定x立方贝塞尔曲线的y坐标

But it loops, I need to avoid something loops So I found this topic: Cubic bezier curves - get Y for given X 但是它循环了,我需要避免某些循环,所以我找到了这个主题: 三次贝塞尔曲线-给定X获得Y

But I can't figure out how to solve a cubic polynomial in js :( 但是我不知道如何在js中解三次多项式:(

This is mathematically impossible unless you can guarantee that there will only be one y value per x value, which even on a unit rectangle you can't (for instance, {0,0},{1,0.6},{0,0.4},{1,1} will be rather interesting at the mid point!). 从数学上讲这是不可能的,除非您可以保证每个x值只有一个y值,即使在单位矩形上也不能(例如,{0,0},{1,0.6},{0,0.4 },{1,1}在中点会很有趣!)。 The fastest is to simply build a LUT, like for instance: 最快的方法就是简单地构建一个LUT,例如:

var LUT_x = [], LUT_y = [], t, a, b, c, d;
for(let i=0; i<100; i++) {
  t = i/100;
  a = (1-t)*(1-t)*(1-t);
  b = (1-t)*(1-t)*t;
  c = (1-t)*t*t;
  d = t*t*t;
  LUT_x.push( a*x1 + 3*b*x2 + 3*c*x3 + d*x4 );
  LUT_y.push( a*y1 + 3*b*y2 + 3*c*y3 + d*y4 );
}

Done, now if you want to look up an x value for some y value, just run through LUT_y until you find your y value, or more realistically until you find two values at index i and i+1 such that your y value lies somewhere in between them, and you will immediately know the corresponding x value because it'll be at the same index in LUT_x . 完成,现在,如果要在某个y值中查找x值,只需遍历LUT_y直到找到y值,或者更实际地直到在索引ii+1处找到两个值以使y值位于某处在它们之间,您将立即知道相应的x值,因为它将在LUT_x中位于相同的索引LUT_x

For nonexact matches with 2 indices i and i+1 you simply do a linear interpolation (ie y is at distance ... between i and i+1 , and this at the same distance between i and i+1 for the x coordinates) 对于具有2个索引ii+1精确匹配,您只需进行线性插值(即yii+1之间的距离...,对于x坐标,它在ii+1之间的距离相同)

All the solutions that use a look up table can only give you an approximate result. 使用查找表的所有解决方案只能给您近似的结果。 If that is good enough for you, you are set. 如果那对您足够好,那么您就定了。 If you want a more accurate result, then you need to use some sort of numeric method. 如果想要更准确的结果,则需要使用某种数值方法。

For a general Bezier curve of degree N, you do need to loop. 对于度数为N的一般贝塞尔曲线,您确实需要循环。 Meaning, you need to use bi-section method or Newton Raphson method or something similar to find the x value corresponding to a given y value and such methods (almost) always involve iterations starting with an initial guess. 意味着,您需要使用二分法或Newton Raphson方法或类似方法来找到与给定y值相对应的x值,并且这些方法(几乎)总是涉及从初始猜测开始的迭代。 If there are mutiple solutions, then what x value you get will depend on your initial guess. 如果有多种解决方案,那么您获得的x值将取决于您的初始猜测。

However, if you only care about cubic Bezier curves, then analytic solution is possible as roots of cubic polynomials can be found using the Cardano formula. 但是,如果只关心三次贝塞尔曲线,则可以使用卡尔达诺公式找到三次多项式的根,因此可以进行解析解。 In this link ( y coordinate for a given x cubic bezier ), which was referenced in the OP, there is an answer by Dave Bakker that shows how to solve cubic polynomial using Cardano formula. 在OP中引用的此链接( 给定x立方贝塞尔曲线的y坐标 )中,Dave Bakker给出了一个答案,该答案显示了如何使用Cardano公式求解三次多项式。 Source codes in Javascript is provided. 提供了Javascript中的源代码。 I think this will be your good source to start your investigation on. 我认为这将是您开始调查的良好来源。

Thanks again to Mike's help we found the fastest way to do this. 再次感谢迈克的帮助,我们找到了最快的方法。 I put this function togather, takes 0.28msg on average: 我把这个函数放在一起,平均花费0.28msg:

function getValOnCubicBezier_givenXorY(options) {
  /*
  options = {
   cubicBezier: {xs:[x1, x2, x3, x4], ys:[y1, y2, y3, y4]};
   x: NUMBER //this is the known x, if provide this must not provide y, a number for x will be returned
   y: NUMBER //this is the known y, if provide this must not provide x, a number for y will be returned
  }
  */
  if ('x' in options && 'y' in options) {
    throw new Error('cannot provide known x and known y');
  }
  if (!('x' in options) && !('y' in options)) {
    throw new Error('must provide EITHER a known x OR a known y');
  }

  var x1 = options.cubicBezier.xs[0];
  var x2 = options.cubicBezier.xs[1];
  var x3 = options.cubicBezier.xs[2];
  var x4 = options.cubicBezier.xs[3];

  var y1 = options.cubicBezier.ys[0];
  var y2 = options.cubicBezier.ys[1];
  var y3 = options.cubicBezier.ys[2];
  var y4 = options.cubicBezier.ys[3];

  var LUT = {
    x: [],
    y: []
  }

  for(var i=0; i<100; i++) {
    var t = i/100;
    LUT.x.push( (1-t)*(1-t)*(1-t)*x1 + 3*(1-t)*(1-t)*t*x2 + 3*(1-t)*t*t*x3 + t*t*t*x4 );
    LUT.y.push( (1-t)*(1-t)*(1-t)*y1 + 3*(1-t)*(1-t)*t*y2 + 3*(1-t)*t*t*y3 + t*t*t*y4 );
  }

  if ('x' in options) {
    var knw = 'x'; //known
    var unk = 'y'; //unknown
  } else {
    var knw = 'y'; //known
    var unk = 'x'; //unknown
  }

  for (var i=1; i<100; i++) {
    if (options[knw] >= LUT[knw][i] && options[knw] <= LUT[knw][i+1]) {
      var linearInterpolationValue = options[knw] - LUT[knw][i];
      return LUT[unk][i] + linearInterpolationValue;
    }
  }

}

var ease = { //cubic-bezier(0.25, 0.1, 0.25, 1.0)
  xs: [0, .25, .25, 1],
  ys: [0, .1, 1, 1]
};

var linear = {
  xs: [0, 0, 1, 1],
  ys: [0, 0, 1, 1]
};

//console.time('calc');
var x = getValOnCubicBezier_givenXorY({y:.5, cubicBezier:linear});
//console.timeEnd('calc');
//console.log('x:', x);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM