C++ 计算三次贝塞尔曲线的最快方法?

C++ 计算三次贝塞尔曲线的最快方法?,c++,c,algorithm,graphics,vector,C++,C,Algorithm,Graphics,Vector,现在我这样计算: double dx1 = a.RightHandle.x - a.UserPoint.x; double dy1 = a.RightHandle.y - a.UserPoint.y; double dx2 = b.LeftHandle.x - a.RightHandle.x; double dy2 = b.LeftHandle.y - a.RightHandle.y; double dx3 = b.UserPoint.x - b.Left

现在我这样计算:

    double dx1 = a.RightHandle.x - a.UserPoint.x;
    double dy1 = a.RightHandle.y - a.UserPoint.y;
    double dx2 = b.LeftHandle.x - a.RightHandle.x;
    double dy2 = b.LeftHandle.y - a.RightHandle.y;
    double dx3 = b.UserPoint.x - b.LeftHandle.x;
    double dy3 = b.UserPoint.y - b.LeftHandle.y;

    float len = sqrt(dx1 * dx1 + dy1 * dy1) + 
        sqrt(dx2 * dx2 + dy2 * dy2) + 
        sqrt(dx3 * dx3 + dy3 * dy3);




    int NUM_STEPS =  int(len * 0.05);

    if(NUM_STEPS > 55)
    {
        NUM_STEPS = 55;
    }
    double subdiv_step  = 1.0 / (NUM_STEPS + 1);
    double subdiv_step2 = subdiv_step*subdiv_step;
    double subdiv_step3 = subdiv_step*subdiv_step*subdiv_step;

    double pre1 = 3.0 * subdiv_step;
    double pre2 = 3.0 * subdiv_step2;
    double pre4 = 6.0 * subdiv_step2;
    double pre5 = 6.0 * subdiv_step3;



    double tmp1x = a.UserPoint.x - a.RightHandle.x * 2.0 + b.LeftHandle.x;
    double tmp1y = a.UserPoint.y - a.RightHandle.y  * 2.0 + b.LeftHandle.y;

    double tmp2x = (a.RightHandle.x - b.LeftHandle.x)*3.0 - a.UserPoint.x + b.UserPoint.x;
    double tmp2y = (a.RightHandle.y - b.LeftHandle.y)*3.0 - a.UserPoint.y + b.UserPoint.y;

    double fx = a.UserPoint.x;
    double fy = a.UserPoint.y;

    //a user
    //a right
    //b left
    //b user

    double dfx = (a.RightHandle.x - a.UserPoint.x)*pre1 + tmp1x*pre2 + tmp2x*subdiv_step3;
    double dfy = (a.RightHandle.y - a.UserPoint.y)*pre1 + tmp1y*pre2 + tmp2y*subdiv_step3;

    double ddfx = tmp1x*pre4 + tmp2x*pre5;
    double ddfy = tmp1y*pre4 + tmp2y*pre5;

    double dddfx = tmp2x*pre5;
    double dddfy = tmp2y*pre5;

    int step = NUM_STEPS;



    while(step--)
    {


        fx   += dfx;
        fy   += dfy;
        dfx  += ddfx;
        dfy  += ddfy;
        ddfx += dddfx;
        ddfy += dddfy;
        temp[0] = fx;
        temp[1] = fy;
        Contour[currentcontour].DrawingPoints.push_back(temp);
    }


    temp[0] = (GLdouble)b.UserPoint.x;
    temp[1] = (GLdouble)b.UserPoint.y;
    Contour[currentcontour].DrawingPoints.push_back(temp);
我想知道是否有更快的方法来插值立方贝塞尔

谢谢

寻找一种更快的方法。必须注意处理舍入误差


通过一些检查,该方法可以快速准确。

还有一点也非常重要,那就是使用大量固定长度的直线段来近似曲线。这在曲线几乎笔直的区域效率低下,并且可能导致曲线非常弯曲的区域出现令人讨厌的角度多边形线。对于高曲率和低曲率,没有简单的折衷办法

要解决这一问题,您可以动态细分曲线(例如,在中间点将其拆分为两段,然后查看两条直线段是否在曲线的合理距离内。如果一条线段非常适合曲线,请在此停止;如果不适合,则以相同的方式细分并重复)。在以这种方式对曲线进行采样时,必须小心细分,以免遗漏任何局部(小)特征

这并不总是“更快”地绘制曲线,但它将保证曲线始终看起来良好,同时使用实现该质量所需的最小线段数


一旦“很好地”绘制曲线,您就可以了解如何“更快地”进行必要的计算。

实际上,您应该继续分割,直到曲线上连接点(端点节点)的两条直线及其最远的控制点“足够平”: -完全对齐或 -它们的交点位于距离两个端点节点的“平方距离”小于半个“平方像素”的位置-请注意,您不需要计算实际距离,因为这需要计算平方根,这很慢)

达到这种情况时,请忽略控制点,并用直线段连接两个端点

这会更快,因为使用经典的Bresenham算法,您可以快速获得可以直接绘制的直线段,就像它们是直线一样

注意:为了获得更好的结果(特别是当要绘制的最后一段距离水平线或垂直线或两条对角线非常近时),应考虑端点的分数位,以正确设置累加差值的误差变量的初始值,并由增量Bresenham算法使用;否则,您将获得可见的人工制品

在整数网格上对齐的点之间绘制直线的经典Bresenham算法将第一个端点节点的位置的该错误变量初始化为零。但是,对Bresenham算法的一个小小的修改将两个距离变量和误差值简单地按2的恒定幂放大,然后对x或y变量使用0/+1增量,这两个变量保持不变

错误变量的高阶位还允许您计算alpha值,该值可用于绘制具有正确alpha着色的两个堆叠像素。在大多数情况下,您的图像最多使用8位颜色平面,因此错误值不需要超过8位的额外精度,并且放大可以限制为256倍:您可以使用它绘制“平滑”线

但您可以将自己的缩放因子限制为16(四位):必须绘制的典型位图图像不是非常宽,其分辨率远低于+/-20亿(有符号32位整数的限制):当您将坐标放大16倍时,仍将保留28位,但您应该已经有了将几何体“剪裁”到位图的视图区域以进行渲染,Bresenham算法的错误变量在所有情况下都将保持在56位以下,并且仍然适合64位整数

如果错误变量为32位,则在最坏情况下,必须将缩放坐标限制在2^15(不超过15位)以下(否则,在最坏情况下,由于整数溢出,Bresenham使用的错误变量符号的测试将不起作用),且放大因子为16(4位)您只能绘制宽度或高度不超过11位的图像,即2048x2048图像

但如果您的绘制区域有效地低于2048x2048像素,则可以通过绘制颜色的16个alpha着色值平滑绘制线条(若要绘制alpha着色像素,需要在混合alpha着色颜色之前读取图像中的原始像素值,除非计算的着色对于不需要绘制的第一个标记像素为0%,对于可以直接用普通绘制颜色覆盖的第二个堆叠像素为100%)

如果您的计算图像还包括alpha通道,则绘制颜色也可以有自己的alpha值,您需要对其进行着色,并将其与要绘制的像素的alpha值组合。但是,您不需要任何中间缓冲区来绘制线,因为您可以直接在目标缓冲区中绘制

对于Bresenham算法使用的误差变量,舍入误差完全没有问题,因为舍入误差已被该变量考虑在内。因此,请正确设置其初始值(或者,在开始递归细分之前,简单地将所有坐标放大16倍,样条曲线在Bresenham算法本身中慢16倍)。

请注意如何计算“足够平坦”。“平坦度”是最小绝对角度的测量值(在0和180°之间)在两个连续段之间,但不需要计算实际角度,因为此平面度也等效于将最小值设置为其相对角度的余弦

这个余弦值也不需要直接计算,因为实际上只需要两个向量的向量积,并将其与最大长度的平方进行比较

还请注意