现在我按照这样计算:
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);
我想知道是否有更快的方法来插入立方贝塞尔曲线?
由于
答案 0 :(得分:3)
查看forward differencing以获得更快的方法。必须小心处理舍入错误。
adaptive subdivision方法,通过一些检查,可以快速准确。
答案 1 :(得分:2)
还有一点非常重要,那就是您使用大量固定长度的直线段逼近曲线。这在你的曲线几乎是直的区域是低效的,并且可能导致曲线非常弯曲的令人讨厌的角度折线。没有一个简单的折衷方案可以用于高曲率和低曲率。
为了解决这个问题,您可以动态细分曲线(例如,在中间点将其分成两部分,然后查看两条线段是否在曲线的合理距离内。如果一段是好的适合曲线,停在那里;如果不适合,则以相同的方式细分并重复)。你必须小心细分它,以便在以这种方式采样曲线时不会错过任何局部(小)特征。
这并不总能让您的曲线“更快”,但它会保证在使用达到该质量所需的最少线段数时始终看起来很好。
一旦你绘制曲线“井”,你就可以看看如何“更快”地进行必要的计算。
答案 2 :(得分:0)
实际上你应该继续分裂,直到连接曲线上的两条线(终点节点)和它们最远的控制点是#34;足够平坦": - 完全对齐或 - 他们的交叉点位于"平方距离"来自两个端节点都低于一半"方形像素") - 请注意,您不需要计算实际距离,因为它需要计算平方根,这很慢)
当你遇到这种情况时,忽略控制点并用直线段连接两个端点。
速度更快,因为使用经典的Bresenham算法,您可以快速获得可以直接绘制的直线段,就好像它们是直线一样。
注意:您应该考虑端点的小数位,以正确设置误差变量的初始值,累积差异并使用增量Bresenham算法,以获得更好的结果(特别是当最后一段绘制时离水平或垂直或两个对角线非常近;否则你会得到可见的文物。
在整数网格上对齐的点之间绘制线条的经典Bresenham算法将此误差变量初始化为零,以获得第一个端节点的位置。但是对Bresenham算法进行微小修改后,将两个距离变量和误差值简单地按2的常数幂放大,然后对x或y变量使用0 / + 1增量,保持未缩放。
错误变量的高位也允许您计算可用于使用正确的Alpha阴影绘制两个堆叠像素的Alpha值。在大多数情况下,您的图像最多使用8位色彩平面,因此您不需要为误差值提供超过8位的额外精度,并且可以将放大倍数限制为256:您可以使用它绘制"平滑"线。
但你可以将自己限制在16(四位)的缩放系数:你必须绘制的典型位图图像不是很宽,它们的分辨率远低于+/- 2十亿(签名32位的限制)整数):当您将坐标放大16倍时,它将保持28位可以使用,但您应该已经"剪切"要渲染的位图的视图区域的几何图形,Bresenham算法的错误变量在所有情况下都将保持在56位以下,并且仍然适合64位整数。
如果您的错误变量是32位,则必须将缩放坐标限制在2 ^ 15(不超过15位)以下(对于最坏的情况)(否则Bresenham使用的错误变量符号的测试将不起作用由于最坏情况下的整数溢出),并且升级因子为16(4位),您将被限制为绘制宽度或高度不大于11位的图像,即2048x2048图像。
但是如果您的绘制区域实际上低于2048x2048像素,那么绘制由绘制颜色的16个alpha阴影值平滑的衬里没有问题(要绘制alpha阴影像素,您需要读取orignal像素混合Alpha阴影颜色之前图像中的值,除非计算出的阴影对于您不需要绘制的第一个放样像素为0%,对于第二个堆叠像素为100%,您可以直接用普通的抽奖颜色)
如果您的计算图像还包含alpha通道,则您的绘制颜色也可以具有自己的Alpha值,您需要对其进行着色并与要绘制的像素的Alpha值组合。但是,您不需要任何中间缓冲区来绘制线条,因为您可以直接在目标缓冲区中绘制。
使用Bresenham算法使用的错误变量,由于舍入错误导致完全没有问题,因为这个变量会考虑它们。因此,正确设置其初始值(另一种方法是,在开始细分递归之前,简单地将所有坐标按比例放大16倍,在Bresenham算法本身中,样条曲线慢16倍。)
答案 3 :(得分:0)
注意如何计算“足够平坦”。 “平坦度”是两个成功段之间的最小绝对角度(在0和180°之间)的一个轨迹,但是您不需要计算实际角度,因为这个平坦度也等于将余弦设置为最小值他们的相对角度。
余弦值也不需要直接计算,因为你需要的只是两个向量的向量乘积,并将它与它们最大长度的平方进行比较。
另请注意,“余弦的平方”也是“一减去正弦的平方”。最大平方余弦值也是最小平方正弦值...现在你知道使用哪种“矢量乘积”:最快和最简单的计算是标量积,其平方与两者的平方正弦成正比向量和两个向量的平方长度的乘积。
因此,检查曲线是否“足够平坦”很简单:计算两个标量积之间的比率,并查看此比率是否低于“平坦度”常数值(最小平方正弦值)。没有除零,因为你将确定两个向量中的哪一个是最长的,如果这个向量的方形长度低于1/4,那么你的曲线已经足够平坦以获得渲染分辨率;否则检查最长和最短矢量之间的比率(由包含端点和控制点的凸包的交叉对角线形成):
有二次贝塞尔曲线,凸包是一个三角形,你选择两对
对于立方贝塞尔曲线,凸包是一个四边凸多边形,对角线可以将一个终点与两个控制点中的一个连接起来,或者将两个终点和两个控制点连接在一起你有六种可能性
使用组合给出第一个矢量和第一个矢量之间的最大长度,第一个矢量和其他三个点之一,第二个矢量连接另外两个点):
您需要确定段的“最小平方长度”,从一个端点或控制点开始,到序列中的下一个控制点或终点。 (在二次Bezier中,您只需比较两个段,使用二次Bezier,检查3个段)
如果这个“最小平方长度”低于1/4,你就可以停在那里,曲线“足够平坦”。
然后确定从一个端点开始到任何一个另一个端点或控制点的段的“最大平方长度”(使用二次贝塞尔曲线,您可以安全地使用相同的2个段作为在上面,使用三次贝塞尔曲线,您可以丢弃上面用于连接两个控制点的3个段中的一个,但是添加连接两个端节点的段。)
然后检查“最小平方长度”是否低于常数“平直度”(最小平方正弦)乘以“最大平方长度”的乘积(如果是这样,曲线“足够平坦”。< / p>
在这两种情况下,当您的曲线“足够平坦”时,您只需要绘制连接两个端点的线段。否则,您将递归分割样条曲线。
你可以包括递归的限制,但实际上它永远不会到达,除非曲线的凸包覆盖非常大的绘图区域中的非常大的区域;即使有32级重复,它也不会在对角线长度小于2 ^ 32像素的矩形绘制区域中爆炸(只有在具有浮点的几乎无限空间中分割“虚拟贝塞尔曲线”时才会达到限制坐标,但你不打算直接绘制它,因为你不会在这样的空间中有1/2像素限制,并且只有你为“平面度”设置了极值值你的“最小平方正弦”常数参数是1/2 ^ 32或更低。