Math 三次贝塞尔曲线-获得给定X的Y-控制点X增加的特殊情况

Math 三次贝塞尔曲线-获得给定X的Y-控制点X增加的特殊情况,math,bezier,cubic-bezier,Math,Bezier,Cubic Bezier,我读过关于三次贝塞尔曲线在X处求Y的书,也读过关于这个问题的书 我的案例比一般的更受限制,我想知道是否有比上述讨论中提到的一般解决方案更好的解决方案 我的情况是: 不同控制点的X值正在增加。即: X3>X2>X1>X0 同样,作为上述结果,X(t)也是严格单调递增的 是否有任何有效的算法考虑到这些约束?如果二进制搜索太复杂,仍然有一种O(1)方法,但它相当有限。我假设您使用的是4个控制点(p0(x0,y0),p1(x1,y1),p2(x2,y2),p3(x3,y3))三次贝塞尔,由区间[0.

我读过关于三次贝塞尔曲线在X处求Y的书,也读过关于这个问题的书

我的案例比一般的更受限制,我想知道是否有比上述讨论中提到的一般解决方案更好的解决方案

我的情况是:

  • 不同控制点的
    X
    值正在增加。即:
    X3>X2>X1>X0
  • 同样,作为上述结果,
    X(t)
    也是严格单调递增的

是否有任何有效的算法考虑到这些约束?

如果二进制搜索太复杂,仍然有一种
O(1)
方法,但它相当有限。我假设您使用的是4个控制点(
p0(x0,y0),p1(x1,y1),p2(x2,y2),p3(x3,y3)
)三次贝塞尔,由区间
[0.0,1.0]中的一些
t
参数化,因此:

t = 0.0 -> x(t) = x0, y(t) = y0;
t = 1.0 -> x(t) = x3, y(t) = y3;
首先,让我们暂时忘记贝塞尔曲线,改用它,这只是表示同一条曲线的另一种方法。要在两个立方体之间转换,请使用以下各项:

// BEzier to Catmull-Rom
const double m=6.0;
X0 = x3+(x0-x1)*m; Y0 = y3+(y0-y1)*m;
X1 = x0;           Y1 = y0;
X2 = x3;           Y2 = y3;
X3 = x0+(x3-x2)*m; Y3 = y0+(y3-y2)*m;

// Catmull-Rom to Bezier
const double m=1.0/6.0;
x0 = X1;           y0 = Y1;
x1 = X1-(X0-X2)*m; y1 = Y1-(Y0-Y2)*m;
x2 = X2+(X1-X3)*m; y2 = Y2+(Y1-Y3)*m;
x3 = X2;           y3 = Y2;
其中,
(xi,yi)
是贝塞尔控制点,
(xi,yi)
是卡穆尔Rom点。现在,如果所有控制点之间的
X
距离具有相同的距离:

(X3-X2) == (X2-X1) == (X1-X0)
然后
X
坐标与
t
成线性关系。这意味着我们可以直接从
X
计算
t

t = (X-X1)/(X2-X1);
现在我们可以直接计算任意
X
Y
。因此,如果可以选择控制点,则选择它们以使其满足X距离条件


如果不满足条件,您可以尝试更改控制点,使其满足要求(通过二进制搜索,通过将立方体细分为更多面片等)但要注意,如果不小心,更改控制点可能会更改生成曲线的形状。

首先:这个答案只适用于控制点约束,因为控制点约束意味着我们总是处理与法线函数等效的参数。这显然是你在本例中想要的,但是任何在将来找到这个答案的人都应该知道这个答案是基于这样一个假设的:对于任何给定的x值,只有一个y值。当然:

对于一般的贝塞尔曲线,这绝对不是真的

话虽如此,我们知道,尽管我们已经将这条曲线表示为二维参数曲线,但我们所处理的曲线无论出于何种目的,都必须具有某种形式的未知函数
y=f(x)
。我们还知道,只要我们知道唯一属于某个特定x的“t”值(这只是因为严格单调递增的系数属性),我们就可以将y计算为
y=By(t)
,所以问题是:我们可以计算需要插入
By(t)
t
值吗,给定一些已知的
x

答案是:是的,我们可以

首先,我们开始使用的任何
x
值都可以说来自
x=Bx(t)
,因此,假设我们知道
x
,我们应该能够找到导致
x
t
的对应值

让我们看看x(t)的函数:

我们可以将其改写为简单的多项式形式,如下所示:

x(t) = (-a + 3b- 3c + d)t³ + (3a - 6b + 3c)t² + (-3a + 3b)t + a
这是一个标准的三次多项式,只有已知的常数作为系数,我们可以简单地将其改写为:

(-a + 3b- 3c + d)t³ + (3a - 6b + 3c)t² + (-3a + 3b)t + (a-x) = 0
你可能会想,“其他值a,b,c和d的-x都到哪里去了?”答案是它们都被抵消了,所以我们最后唯一需要减去的就是最后的那一个。汉迪

所以现在我们只是。。。解这个方程:除了
t
,我们什么都知道,我们只需要一些数学知识来告诉我们怎么做

…当然,“just”不是这里正确的限定词,关于寻找三次函数的根没有什么“just”,但谢天谢地,早在16世纪,就为使用复数确定根奠定了基础。在任何人发明复数之前。真了不起!但这是一个编程答案,不是历史课,所以让我们开始实施:

给定x的一些已知值,以及坐标a、b、c和d的知识,我们可以按如下方式实现根查找:

//查找具有bernstein系数的三次多项式的根
//{pa,pb,pc,pd}。函数将首先将这些转换为
//标准多项式系数,然后通过Cardano
//求凹三次曲线根的公式。
双[]findRoots(双x、双pa、双pb、双pc、双pd){
双重的
pa3=3*pa,
pb3=3*pb,
pc3=3*pc,
a=-pa+pb3-pc3+pd,
b=pa3-2*pb3+pc3,
c=-pa3+pb3,
d=pa-x;
//有趣的事实:任何贝塞尔曲线都可能(意外或故意)
//完美地模拟任何低阶曲线,所以我们要测试
//因此:低阶曲线更容易找到根。
如果(大约(a,0)){
//这不是一条三次曲线。
如果(大约(b,0)){
//事实上,这也不是一条二次曲线。
if(约(c,0)){
//事实上,没有解决办法。
返回新的double[]{};
}
//线性解决方案:
返回新的double[]{-d/c};
}
//二次解:
双重的
q=sqrt(c*c-4*b*d),
b2=2*b;
返回新的双精度[]{
(q-c)/b2,
(-c-q)/b2
};
}
//此时,我们知道我们需要一个立方解,
//上述a/b/c/d值在技术上是正确的
//一个预优化集,因为a可能为零,而
//这将导致以下分区出错。
b/=a;
c/=a;
d/=a;
双重的
b3=b/3,
p=(3*c-b*b)/
(-a + 3b- 3c + d)t³ + (3a - 6b + 3c)t² + (-3a + 3b)t + (a-x) = 0