3D贝塞尔曲线的零切线,如何处理?

Zero tangent of a 3D Bezier curve, how to handle?

贝塞尔曲线的方向(切线)在某一点可能为零 (0,0,0),这看起来很奇怪。例如。对于定义为:

的对称贝塞尔曲线
    bezier.anchor1.copyFrom(new Vector3D(-1,0,0));
    bezier.control1.copyFrom(new Vector3D(1,0,0));
    bezier.anchor2.copyFrom(new Vector3D(1,0,0));
    bezier.control2.copyFrom(new Vector3D(-1.,0,0));

t=0.5 时的方向是 (0,0,0)。这可能是一个 rare/extreme 用例,但我绝对需要手头始终有一个非零方向。

从程序上讲,我应该如何处理?重新计算附近另一个 t 的方向(比如 t1=t*0.99999=0.499995),然后 return 那个方向?

我不知道为什么不能将 0 范数向量作为导数。我想你正试图沿着曲线制作动画,那么导数就是速度......你在那个点的速度为零,因为你暂时没有移动。应该没有问题。

为什么 0?来看一个病理案例

请参阅此图,其中锚点分别为 (-1,-y,0)(-1,y,0) 以及控制点 (1,-y,0)(1,y,0)

现在您可以看到在 parameter-defined 点 t=0t=0.5t=1 处绘制的切线发生了什么。在 t=0.5 处,切线已失去其在 x 轴中的所有分量,但仍在向 y 轴缓慢移动,因此向右有一条小切线。

然而,您将此y 参数修改得越多,变成0,您就会使曲线变平并获得一段。在 t=0.5 点,您的切线没有零 x 分量(像以前一样),但在 y 维度上也是如此,因为图形将为 one-dimensional.

因此,零切线是一种病态情况,当您的图形尺寸太小时可能会出现这种情况。

Work-around : 减少程度

贝塞尔曲线的阶数很多,通用阶数n的公式是:

with Pi 你曲线的点,bi 被称为 Bernstein basis polynomials of degree n (see this wikipedia section 用于不同阶数的曲线的不同插图)。

在你的例子中,你将从 (-1,0,0)(1,0,0),控制点在同一条线上,有效地绘制线段 [(-1,0,0), (1,0,0)]。因此你画了一个线段,你真的只需要 2 个点:你应该使用一个一阶多项式,它由 t * P0 + (1-t) * P1 定义,一个平凡的插值。在你的形式主义中,我猜是:

bezier.anchor1.copyFrom(new Vector3D(-1,0,0));
bezier.anchor2.copyFrom(new Vector3D(1,0,0));

而且没有控制点。 然后没有 0 切线(除非你得到尖点)!

情况比你的例子差一点。很可能在三次贝塞尔曲线中出现 cusp。例如,控制点 [80,214]、[217,50]、[75,50]、[222,206]。你的例子实际上是一个扁平的尖端。

如果您沿着曲线的切线在尖点处经过 180º 翻转,尖点就很难处理。尖点也很常见,如果你有一个动画序列的曲线,你很可能在循环展开时在一帧中出现尖点。你经常在现实世界中看到它们,在茶杯底部的光模式中,以及在 3D 曲线到 2D 的投影中(例如将曲线 (t,t^2,t^3) 投影到Y-Z 平面)。好消息是它们通常是孤立的点,你只会在三次贝塞尔曲线上得到一个。

现在如何处理这些尖端可能取决于您的应用程序。如果它描述了某个物体的路径,将会发生的事情是物体在尖点处停止并朝相反的方向离开。说该对象此时具有零切向量可能是非常合理的。或许可以在尖点处分割曲线。

您可以在 javascript 中看到带有可移动控制点的交互式示例 http://jsfiddle.net/SalixAlba/QQnvm/6/ canvas

var P = [{X:  80, Y: 214 }, 
         {X: 217, Y:  50 }, 
         {X:  75, Y:  50 }, 
         {X: 222, Y: 206 }, ];
ctx.beginPath();
ctx.moveTo(P[0].X, P[0].Y);
ctx.bezierCurveTo(P[1].X, P[1].Y, P[2].X, P[2].Y, P[3].X, P[3].Y);
ctx.stroke();