发明内容
基于背景技术中所述的贝塞尔曲线存在的问题,为了使得使用者能够通过更加简洁和直观的操作生成曲线,本发明提出一种自适曲线的生成方法及装置。具体方案如下:
一种自适应曲线的生成方法,包括以下步骤:
S1:获取轮廓点序列P(P1, P2, P3, …, Pn);
S2:根据S1获得的P确定自适应曲线方程参数,再根据自适应曲线方程参数计算连续点序列;
一种自适应曲线的生成装置,包括:
轮廓点获取装置,用来获取轮廓点序列P(P1, P2, P3, …, Pn);
自适应曲线段连续点序列计算装置,根据轮廓点获取装置获得的轮廓点序列P确定自适应曲线方程参数,再根据自适应曲线方程参数计算连续点序列。
上述自适应曲线方程满足条件:
设有轮廓点序列P(P1, P2, P3, …, Pn),则自适应曲线方程为:
其中
参数k满足条件:。表示的是相乘,j从N1一直到N2除了j=i。上述的自适应曲线方程是一般化的方程,设n=N2-N1+1,则自适应曲线方程的阶为n-1。当n=1时,为0次方程,实际上就是一个点;当n=2时,为1次方程,为经过P1和P2的直线;当n=3时,为二次自适应曲线;当n=4时为三次自适应曲线。阶数越高,曲线的数值稳定就越差。本发明优先采用零次,一次,二次和三次自适应曲线,通过对零次,一次,二次和三次自适应曲线段的组合生成轮廓点序列P之间的曲线。其方法如下:当轮廓点序列P的数量为1时,采用零次自适应曲线;数量为2时,采用一次自适应曲线;数量为3时,采用二次自适应曲线;数量为4时,端点处采用三次自适应曲线或者端点曲线段采用二次自适应曲线,中间曲线段为三次自适应曲线的组合;数量大于4时,端点曲线段采用二次自适应曲线,中间曲线段为三次自适应曲线的组合。
所谓端点曲线段是指P1与P2之间的曲线段和Pn-1与Pn之间的曲线段。所谓中间曲线段是指Pi与Pi+1之间的曲线段1。对于闭合曲线而言,只存在中间曲线段,不存在端点曲线段。
上述的曲线段是指完整的自适应曲线在相邻轮廓点Pi与Pi+1之间的区间内的部分,包括端点Pi和Pi+1。
上述曲线段内的二次自适应曲线和三次自适应曲线是由相邻的三个轮廓点构建的二次自适应曲线和相邻的四个轮廓点构建的三次自适应曲线。即轮廓点P1, P2, P3用以构建P1和P2之间的二次自适应曲线段;Pn-2, Pn-1, Pn用以构建Pn-1和Pn之间的二次自适应曲线段;Pi-1, Pi, Pi+1, Pi+2用以构建Pi与Pi+1之间的三次自适应曲线段。对于闭合曲线而言,P1与P2处的曲线段由Pn, P1, P2, P3构建的三次自适应曲线段,Pn-1和Pn之间的曲线段是由Pn-2, Pn-1, Pn, P1构建的三次自适应曲线段。
上述的二次自适应曲线方程展开后为:
其中,a1, a2, a3满足条件:
k1, k2, k3满足:,m1和m2大于0。当t取值于k1与k2之间时,可以构建Pi与Pi+1之间的曲线段;当t取值于k2与k3之间时,可以构建Pi+1与Pi+2之间的曲线段。
上述的三次自适应曲线方程展开后为:
其中,a1, a2, a3, a4 满足:
k1, k2, k3满足:,m1、m2和m3大于0。当t取值于k1与k2之间时,可以构建Pi与Pi+1之间的曲线段;当t取值于k2与k3之间时,可以构建Pi+1与Pi+2之间的曲线段;当t取值于k3与k4之间时,可以构建Pi+2与Pi+3之间的曲线段。
上述二次自适应曲线方程的参数k1, k2, k3以及三次自适应曲线方程的参数k1,k2 ,k3, k4简称为参数k。参数k可以任意设置,只要其满足条件:,m1和m2大于0或者, m1、m2和m3大于0。上述用以确定参数k的参数m1,m2,m3简称为参数m。上述的参数k的满足条件表示了:
1.参数k由参数m确定,参数m只用以确定参数k的差值,初始值可以任意设置。例如设k1=0,则k2=m1,k3=m1+ m2,k4=m1+ m2+ m3;设k3=0,则k1=-m1-m2,k2=-m2,k4=m3。
2.参数k可以任意设置,表示的是参数m可以任意设置只要满足参数m都大于0。自适应曲线的形状依赖于参数m的值。只要其中一个参数k是确定的,m是确定的,则所有参数k都可以确定。
简单的情形可以选择参数m相等,即参数k为等差数列。例如:k1=0, k2=1, k3=2,k4=3,或者k1=-2, k2 = 0, k3=2, k4=4。
对于参数m本发明优先取值为长度相关性。即,二次自适应曲线m1, m2满足:m1=f21(l1, l2),m2=f22(l1, l2);三次自适应曲线的m1, m2, m3 满足:m1=f31(l1, l2,l3),m2=f32(l1, l2, l3), m3=f33(l1, l2, l3)。其中l1, l2, l3 分别为:P1与P2之间的距离,P2与P3之间的距离,P3与P4之间的距离。
对于二次自适应曲线,上述f21(l1, l2)优先等于l1的平方根;上述f22(l1, l2)优先等于l2的平方根。即,,再取其中一个k为0,即可获得所有的k1, k2, k3的值。
对于三次自适应曲线,上述f31(l1, l2, l3)优先等于l1的平方根;上述f33(l1,l2, l3)优先等于l3的平方根;上述的f32(l1, l2, l3)优先等于l2 +c*l2*l2/(l1+ l2+l3)的平方根。即:。再取其中一个k值为最简单的0,即可以获得所有的k1, k2, k3, k4的值。
其中c为系数,取值范围在0至1之间,优先取值0.2至0.7。
通过上述构建曲线的方法对经过轮廓点序列P(P1, P2, P3, …, Pn)构建出来的曲线其实是由相邻轮廓点配合附近点构建出来的分段曲线的组合。这样构建出来的曲线看似在轮廓点Pi处无法圆滑,而事实上,只要直线Pi-1, Pi和直线Pi, Pi+1之间的夹角不超过正负60度,本发明构建的出的分段曲线在Pi处具有很好的圆滑性,即轮廓点Pi两侧曲线Pi-1与Pi以及Pi与Pi+1的斜率近似相等。该结论可以通过本发明具体实施方式可以得到。由此可以得出,只要轮廓点的数量足够,任何曲线形状的图形都可以通过本发明的方法构建。
由于上述的曲线是由相邻轮廓点配合附近点构建出来的分段曲线的组合。因而对于相邻轮廓点Pi和Pi+1点的横纵坐标相同的情形是没有意义的。故而在上述方法的步骤S1中包括剔除相邻轮廓点相同的步骤,或者轮廓点获取装置中包括无效轮廓点剔除装置,无效轮廓点剔除装置用来剔除相邻轮廓点相同的情形。
上述自适应曲线生成方法和生成装置还可以与直线段,圆弧段,以及斜率控制曲线结合使用:
一种曲线绘制装置,包括:
锚点编辑装置,用于锚点序列P(P1, P2, P3, …, Pn)上锚点的添加、插入、删除和修改锚点坐标;
线段类型编辑装置,用于设定锚点Pi与Pi+1之间线段类型,线段类型包括自适应曲线;
线段连续点序列计算分配装置,用于根据线段类型的类型,将具有连续相同类型的线段分配到不同类型的线段连续点序列计算装置,为不同的线段连续点序列计算装置提供轮廓点序列P或锚点序列P;
线段连续点序列计算装置包括自适应曲线段连续点序列计算装置,所述的自适应曲线段连续点序列计算装置根据轮廓点序列P确定相邻轮廓点Pi, Pi+1之间的曲线段方程参数,再根据曲线段参数方程计算曲线段Pi, Pi+1之间连续点序列。
进一步上述的线段类型还可以包括:直线和/或圆弧和/或斜率控制曲线;相应的线段连续点计算装置包括直线连续点序列计算装置,圆弧连续点序列计算装置,斜率控制曲线连续点序列计算装置;所述的直线连续点序列计算装置根据轮廓点序列P计算相邻轮廓点Pi与Pi+1之间直线的连续点序列;所述的圆弧连续点序列计算装置,根据轮廓点序列P计算相邻轮廓点Pi, Pi+1, Pi+2确定的圆弧连续点序列;所述的斜率控制曲线连续点序列计算装置根据锚点序列P计算各个相邻轮廓点之间的斜率控制曲线的连续点序列。
上述轮廓点是指位于曲线或直线段上的点;锚点包括了轮廓点和中间点,中间点在斜率控制曲线中用于确定轮廓点的斜率,曲线并不经过中间点。上述的斜率控制曲线优选为贝塞尔曲线。
由于斜率控制曲线并不经过中间点,实际应用中,上述的曲线绘制装置可以抛弃斜率控制曲线,线段类型只有:直线、圆弧,自适应曲线。这样得到线段组合,所有的点都在曲线上。
本发明的自适应曲线应用的范围很广,比如字体轮廓的建立。字体轮廓只需要直线加自适应曲线即可描述。
本发明的技术效果:
1、如前所述,本发明的生成的自适应曲线在轮廓点Pi处具有很好的圆滑性。
2、本发明的自适应曲线具有数值稳定性。
3、相比于贝塞尔曲线,本发明的自适应曲线减少了中间点,因而减少了制图中所需要的锚点数,因而制图中更为简单,特别适合于触摸屏操作。
4、相比于贝塞尔曲线,本发明的自适应曲线具有很好的可视性,曲线轮廓具有可预测性,因为该曲线必然经过锚点。
5、相比于贝塞尔曲线,本发明的自适应曲线在锚点距离较大时,拟合的曲线更能反应趋势,而不像贝塞尔曲线那么中庸。
6、相比于贝塞尔曲线,本发明的自适应曲线在轮廓点中更容易获得圆滑曲线。
7、本发明的自适应曲线可以应用于各种图形制作,结合直线,圆弧,斜率控制曲线后,可以制作任意图形。
总的来说,贝塞尔曲线和本发明的自适应曲线各有所长,某些方面,例如字体轮廓的建立,应用自适应曲线比贝塞尔曲线具有更好的效果,更为形象化。贝塞尔曲线在描绘直线能力方面强于本发明的自适应曲线,当然可以通过直线加自适应曲线组合的方法弥补这方面的不足。
具体实施方式
下面对本发明自适应曲线的生成方法和生成装置做进一步详细说明。
1.获取轮廓点序列
本发明中的曲线是根据轮廓点在特定的方程计算得到的,生成的曲线建立在轮廓点序列的基础上,因而获取轮廓点序列是必须的。在制图中,获取轮廓点序列通常是指轮廓点的添加、插入、删除、修改后得到的轮廓点序列。在曲线拟合时,这些轮廓点序列是由其他方式生成。例如二次抛物线拟合时,平面上的点是事先通过其他方式获得的。构建文字的轮廓时,这些轮廓点由特定的文字对应的字体事先定义。
2.曲线段连续点序列计算
曲线段连续点序列的计算是指通过特定的参数和方程,在参数某一区间内描述图形中连续点序列,这些连续点序列显示在屏幕或打印在纸张上就成为连续的曲线。由于计算机屏幕或者打印时,屏幕显示的内容或者打印机打印的内容通过点阵网格实现。这些点阵的横纵坐标只能通过整型数表示,曲线需要通过在这些整型数表示的点阵上模拟出一条最接近的曲线,这就需要计算曲线段的这条最接近于实际曲线的连续点序列。
曲线段连续点序列的计算通常包括两个步骤或者装置:计算标准化的曲线描述参数步骤或装置,以及根据标准化曲线计算连续点序列的步骤或装置。
2.1自适应曲线的标准化
标准化的自适应曲线方程可以表述成:
对于二次自适应曲线方程,三次系数a恒等于0。根据二次自适应曲线方程的定义:即可以得到二次自适应曲线方程标准化的系数:
将标准化方程进行横纵参数分解可以得到:
和
设Pi, Pi+1, Pi+2的横纵坐标分别为:,则
和
其中,a1, a2, a3满足:
对于三次自适应曲线方程,根据三次自适应曲线方程的定义:
即可以得到三次自适应曲线方程标准化的系数:
将标准化方程进行横纵参数分解可以得到:
和
设Pi, Pi+1, Pi+2, Pi+3横纵坐标分别为,则
和
其中,a1, a2, a3满足:
2.2 参数k的选择
这里参数k是指二次自适应曲线中的参数k1, k2, k3和三次自适应曲线中的参数k1, k2, k3, k4。由前述可知,自适应曲线标准化中的参数:ax, bx, cx, dx和ay, by,cy, dy都建立在参数k基础上。变量t的数值区间也由参数k确定。因而参数k的选择是关键。设,m1和m2大于0或者, m1、m2和m3大于0。参数m1,m2,m3简称为参数m。参数m用以确定自适应曲线的形状。只要m是确定的,再确定其中一个参数k,则所有参数k都可以确定。这个特定的参数k并不会影响自适应曲线的形状。例如,m1= m2=m3=1的情形下,选参数k1=0,则k2=1, k3=2, k4=3;取k2=0则有k1=-1,k3=1,k4=2;取k3=0则有:k1=-2, k2=-1, k4=1。这些不同的参数k构造的自适应曲线是相同的。其中某个参数k选择为0,是为了方便计算标准化自适应曲线方程中的dx和dy。例如k2=0,则dx=x2,dy=y2。由此可见,参数k的选择建立在参数m的基础上。
作为最简单的情形,可以选择m=1即,m1=m2=m3=1得到的曲线效果如图1-(I)所示。由图1-(I)可以看出,曲线效果并不好。轮廓点A和B之间的曲线样式显然不是我们想要的样子,而轮廓点C附近两侧的曲线段的连续性不够好。
从图1-(I)可以分析得出,轮廓点A和B之间的曲线段是由于给定的m2值太大所致,那么具体给予m2什么样合适的数据是关键。本发明选择m1, m2, m3给予一定的长度相关性。二次自适应曲线满足条件:m1=f21(l1, l2); m2=f22(l1, l2);三次自适应曲线满足条件:m1=f31(l1, l2, l3); m2=f32(l1, l2, l3); m3=f33(l1, l2, l3)。经测试,对于二次自适应曲线m1取值l1的平方根,m2取值l2的平方根具有最优的效果。而三次自适应曲线m1取值l1的平方根,m3取值l3的平方根,m2取值l2(1+c*l2/(l1+ l2+ l3)的平方根具有比较好的效果。图1-(II)是本发明上述参数c选1/3的效果图,图1-(II)的轮廓点序列和图1-(I)完全一致。参数c一般选择0至1之间,最好在0与0.6之间。图2显示了本发明拟合圆的效果图。其中,图(1)为c=1/3的情形,并且显示了轮廓点;图(2)为c=0的情形,图(3)为c=0.2的情形,图(4)为c=0.25的情形,图(5)为c=0.4的情形,图(6)为c=0.5的情形,图(7)为c=0.6的情形,图(8)为c=1的情形,这8个圆的采用的轮廓点序列完全相同。由这些图的效果对比可以看出,除了图(2)图(3)图(4)的形状与图(8)的形状有些细微的差异之外,其他的很难看出差别。
需要说明的是本发明的构建的曲线并不是为了精确接近某个目标值。而是为了构建轮廓点给出的接近的形状。这种接近的形状也可以有很多种。上述的参数m选择可以有很多种方案。比如:m1, m2, m3为l1, l2, l3的立方根;或者m1, m2, m3分别为l1, l2, l3的二分之三次方;或者m1为(3*l1+ l2)/4的平方根,m2为(l1+ l3+ 6*l2)的平方根,m3为(3*l3+ l2)/4的平方根;再或者m2取l2(1+c*l2/(l1+2*l2+ l3))的平方根。上述各种各样的变化反应到曲线段的图形中,通常只有细微的差别。甚至根本无法分辨其差别。故本领域人员应知参数m具有长度相关性是指参数m经由曲线段相关的轮廓点长度计算得到的。只要满足这一条件就属于本发明权利要求的保护范围。
再需要说明的是,本发明构建的曲线并不严格追求在轮廓点处的圆滑性。而是追求在轮廓点处圆滑性与拐角之间的平衡。追求的圆滑是一定范围内的圆滑。当相邻的曲线段角度相差比较大时,通常角度大于90度或者小于负90度时,两相邻曲线段在位于其中间的轮廓点显现出拐角。而当角度大与135度或者小于负135度时,两相邻曲线段在位于其中间的轮廓点显现出尖角。如图3所示,图3显示的是本发明参数c=1/3时构建的五花瓣图。如果刻意追求轮廓点处的圆滑,则在花瓣中心处的轮廓点将不会像图3中所示的尖角,而是圆角。由此可见,刻意追求轮廓点处的圆滑反而减少了本发明的应用范围。这也同时说明参数k的选择有些差异并不影响本发明的精神。
2.3标准化自适应曲线的连续点序列的计算
有了标准化参数计算连续点序列就很简单了。连续点序列是用来在屏幕或打印纸上描绘和接近曲线,可以分带锯齿连续点,平滑连续点。锯齿连续点和平滑连续点是由屏幕和打印特征决定的。
本说明书中,圆滑是指曲线的斜率连续,平滑是指消除锯齿。
下面是一段构建锯齿连续点序列的VC++示例代码:
void FillX3Curve(double ax, double bx, double cx, double dx,
double ay, double by, double cy, double dy,
double s, double e, CPointArray & arOut)
{
double t, tt, ttt, lt;
double fx,fy;
double dfx,dfy,df;
int xx,yy;
CPoint pt;
t = s; //曲线段开始
lt = s;
dx += 0.5; //浮点修正
dy += 0.5;
while (t<=e)
{
tt = t*t;
ttt = tt*t;
fx = dx + cx*t + bx*tt + ax*ttt;
fy = dy + cy*t + by*tt + ay*ttt;
dfx = cx + 2*bx*t + 3*ax*tt; //斜率计算
dfy = cy + 2*by*t + 3*ay*tt; //斜率计算
xx = (LONG)fx; // 取整
yy = (LONG)fy;
if (t>s) //是否是第一个点
{
if (xx!=pt.x || yy!=pt.y) //是否和之前线段交接
{
if (xx-pt.x>1||pt.x-xx>1||
pt.y-yy>1||yy-pt.y>1) //判定是否跳线
{
t = (lt+t)/2;
continue;
}
pt.x = xx;
pt.y = yy;
arOut.Add(pt);
}
} else {
int cnt = (int)arOut.GetCount();
if (cnt>0) //是否与上一个点重复
{
pt = arOut[cnt-1];
if (xx!=pt.x || yy!=pt.y)
{
pt.x = xx;
pt.y = yy;
arOut.Add(pt);
}
} else {
pt.x = xx;
pt.y = yy;
arOut.Add(pt);
}
}
if (t>=e) break; // 判定结束
if (dfx<0.0) dfx = -dfx;
if (dfy<0.0) dfy = -dfy;
//根据斜率确定步进
if (dfy>dfx)
df = 1.0/dfy;
else if (dfx>0)
df = 1.0/dfx;
else
df = 0.0001;
lt = t;
t += df;
if (t>e) t = e; //最后一个点
}
}
上述代码中,参数s,e由参数k确定的。arOut数组是结果的输出。
2.4自适应曲线段连续点序列计算的示例代码
下面是一段自适应曲线段连续点序列计算的VC++示例代码,包括了参数k的确定以及标准化参数的确定。本段代码和上述的FillX3Curve代码结合是完整的连续点序列计算代码,经过测试可以直接使用。
void DrawXCurve(CPointArray & arAnc, CPointArray & arOut)
{
int cnt = (int)arAnc.GetCount();
int i;
if (cnt<1) return;
// 剔除连续相同的点;
CPoint ptCurr = arAnc[cnt-1];
for (i=cnt-2;i>=0;i--)
{
if (ptCurr==arAnc[i])
arAnc.RemoveAt(i);
else
ptCurr=arAnc[i];
}
cnt = (int)arAnc.GetCount();
if (cnt<1) return;
if (cnt==1) // 只有一个点的情形;
{
int last = (int)arOut.GetCount()-1;
if (last>=0)
{
if (arOut[last]==arAnc[0])
return;
}
arOut.Add(arAnc[0]);
return;
}
if (cnt==2) // 两个点的直线情形;
{
DrawXLine(arAnc[0],arAnc[1],arOut);
return;
}
if (cnt==3) // 三个点的二次自适应曲线
{
DrawX2Curve(arAnc[0],arAnc[1],arAnc[2],arOut);
return;
}
double * l = new double[cnt];
double lh;
// 计算之间的距离
for (i=0;i<cnt-1;i++)
{
lh = (arAnc[i].x-arAnc[i+1].x)*(arAnc[i].x-arAnc[i+1].x) +
(arAnc[i].y-arAnc[i+1].y)*(arAnc[i].y-arAnc[i+1].y);
l[i] = sqrt(lh);
}
double l1,l2,l3;
double k1,k2,k3,k4;
double a1,a2,a3,a4;
double b1,b2,b3,b4;
double ax,bx,cx,dx;
double ay,by,cy,dy;
// 准备第一段的三次曲线
l1 = sqrt(l[0]); //计算参数m
l2 = sqrt(l[1]);
k1 = 0; // 根据参数m设定参数k
k2 = l1;
k3 = l1+l2;
a1 = 1.0/((k3-k1)*(k2-k1)); //参数a
a2 = 1.0/((k3-k2)*(k1-k2));
a3 = 1.0/((k2-k3)*(k1-k3));
b1 = a1*arAnc[0].x; //参数a与横坐标点结合
b2 = a2*arAnc[1].x;
b3 = a3*arAnc[2].x;
ax = 0; //计算标准化二次方程的系数
bx = b1+b2+b3;
cx = -b1*(k3+k2)-b2*(k1+k3)-b3*(k2+k1);
b1 = a1*arAnc[0].y; //参数a与纵坐标点结合
b2 = a2*arAnc[1].y;
b3 = a3*arAnc[2].y;
ay = 0; //计算标准化二次方程的系数
by = b1+b2+b3;
cy = -b1*(k3+k2)-b2*(k1+k3)-b3*(k2+k1);
dx = arAnc[0].x;
dy = arAnc[0].y;
FillX3Curve(ax,bx,cx,dx, ay,by,cy,dy, k1,k2,arOut); // 连续点序列计算
for (i=1;i<cnt-2;i++) // 处理中间的三次自适应曲线段
{
l1 = l[i-1];
l2 = l[i];
l3 = l[i+1];
l2 += l2*l2/(3*l1+3*l2+3*l3);
l1 = sqrt(l1); // 计算参数m
l2 = sqrt(l2);
l3 = sqrt(l3);
k1 = -l1; // 根据参数m设定参数k
k2 = 0;
k3 = l2;
k4 = k3+l3;
a1 = 1.0/((k4-k1)*(k3-k1)*(k2-k1)); // 计算参数a
a2 = 1.0/((k4-k2)*(k3-k2)*(k1-k2));
a3 = 1.0/((k4-k3)*(k2-k3)*(k1-k3));
a4 = 1.0/((k1-k4)*(k2-k4)*(k3-k4));
b1 = a1*arAnc[i-1].x; // 参数a的实例化
b2 = a2*arAnc[i].x;
b3 = a3*arAnc[i+1].x;
b4 = a4*arAnc[i+2].x;
ax = -b1-b2-b3-b4; // 标准化三次自适应方程的系数
bx = b1*(k4+k2+k3)+b2*(k1+k3+k4)+b3*(k1+k2+k4)+b4*(k2+k3+k1);
cx = -b1*(k4*k2+k4*k3+k2*k3)
-b2*(k4*k3+k4*k1+k3*k1)
-b3*(k4*k2+k4*k1+k2*k1)
-b4*(k2*k3+k2*k1+k3*k1);
b1 = a1*arAnc[i-1].y;
b2 = a2*arAnc[i].y;
b3 = a3*arAnc[i+1].y;
b4 = a4*arAnc[i+2].y;
ay = -b1-b2-b3-b4;
by = b1*(k4+k2+k3)+b2*(k1+k3+k4)+b3*(k1+k2+k4)+b4*(k2+k3+k1);
cy = -b1*(k4*k2+k4*k3+k2*k3)
-b2*(k4*k3+k4*k1+k3*k1)
-b3*(k4*k2+k4*k1+k2*k1)
-b4*(k2*k3+k2*k1+k3*k1);
dx = arAnc[i].x;
dy = arAnc[i].y;
FillX3Curve(ax,bx,cx,dx, ay,by,cy,dy, k2,k3,arOut); // 连续点序列计算
}
l1 = sqrt(l[cnt-3]); //处理最后的二次曲线段
l2 = sqrt(l[cnt-2]);
k1 = -l1;
k2 = 0;
k3 = l2;
a1 = 1.0/((k3-k1)*(k2-k1));
a2 = 1.0/((k3-k2)*(k1-k2));
a3 = 1.0/((k2-k3)*(k1-k3));
b1 = a1*arAnc[cnt-3].x;
b2 = a2*arAnc[cnt-2].x;
b3 = a3*arAnc[cnt-1].x;
ax = 0;
bx = b1+b2+b3;
cx = -b1*(k3+k2)-b2*(k1+k3)-b3*(k2+k1);
b1 = a1*arAnc[cnt-3].y;
b2 = a2*arAnc[cnt-2].y;
b3 = a3*arAnc[cnt-1].y;
ay = 0;
by = b1+b2+b3;
cy = -b1*(k3+k2)-b2*(k1+k3)-b3*(k2+k1);
dx = arAnc[cnt-2].x;
dy = arAnc[cnt-2].y;
FillX3Curve(ax,bx,cx,dx, ay,by,cy,dy, k2,k3,arOut);
delete [] l; // 删除长度数组
}
3.绘图软件中的应用
绘图软件中使用曲线通常由两种用法:一种是建立路径或者选区,以作为辅助工具,例如PhotoShop中的钢笔工具;第二种是直接用于构建矢量图的框架,例如AutoCAD中的曲线工具。不过上述的工具都采用的是贝塞尔曲线。本发明的自适应曲线比贝塞尔曲线在这种绘图软件中更具有价值,因为它所需要的锚点数比贝塞尔曲线少,更加直观,所有的点都在曲线上,能够创建不间断连续圆滑曲线,同时也能创建拐角,和直线结合后可以构建任何想要的图形。当然,简单应用本发明时,可以创建单独的自适应曲线。由于自适应曲线描述直线的能力偏弱,要求使用者很强的操作能力,和直线结合为最好不过了。下面描述的是直线与自适应曲线结合的应用,也即权利要求中的曲线绘制装置。广义来说,还可以结合斜率控制曲线(贝塞尔曲线),圆弧线等等使用,这是本领域的技术人员应该能够理解。PhotoShop的钢笔工具就是一套贝塞尔曲线和直线结合的工具。本发明的自适应曲线和直线结合和PhotoShop的钢笔工具类似。只不过一个是贝塞尔曲线一个是自适应曲线。为方便描述,做如下定义:
通用线段:本发明中的自适应曲线和直线结合的线段组合;
通用线段工具:软件功能菜单或工具条中的一个命令,用于表示进入通用线段编辑状态;
曲线段:是指自适应曲线段;
锚点:通用线段上的轮廓点,用以生成曲线段或直线段;
线段:是指相邻锚点之间的直线段或曲线段;
焦点:是指通用线段上被选中的锚点;
线段起始点:是指锚点Pi, Pi+1之间的线段的开始点Pi,当为闭合通用线段时,锚点Pn与P1之间的线段的开始点Pn。
3.1 数据结构定义
通用线段对象,用来表述通用线段的数据结构,包括锚点列表,线段属性列表。线段属性列表也可以放入锚点结构中,从而和锚点列表一起,这种情形下,锚点Pi的线段属性可以表示锚点Pi与Pi+1之间的线段属性,在闭合通用线段内,Pn的线段属性可以表示锚点Pn与P1之间的线段属性。通用线段对象还可以包括其他属性,比如是否闭合,是否消除锯齿,线宽,颜色等等。线段的属性包括线段类型。线段类型用来表示当前的线段是自适应曲线段还是直线段。
3.2 锚点编辑装置
通过绘图软件的菜单栏或工具栏上的通用线段工具命令进入通用线段的编辑状态,这种编辑状态也就是锚点编辑,即添加或插入、删除或修改锚点的坐标。编辑的规则如下:
1、触点规则。触点表示的是鼠标按下或触摸屏内按下的操作。触点时做如下处理:a. 如果当前未有选中的通用线段,而且当前的触点坐标位于某一通用线段之上,则选中该通用线段,并且设置该线段起始点作为焦点;b. 如果当前未有选中的通用线段,且当前的触点坐标不在某一通用线段之上,则生成新的通用线段,并将该触点作为锚点加入该新的通用线段,设置该锚点作为焦点进入拖动状态;c. 如果当前有选中的通用线段,而且当前的触点坐标位于该通用线段的某一锚点上,则设置该锚点为焦点,并进入准备拖动该锚点的状态;d. 如果当前有选中的通用线段,而且当前的触点坐标位于本通用线段的某一曲线段之上,则在该曲线段表述Pi与Pi+1之间插入锚点,插入锚点的坐标为触点坐标,并设置该锚点为焦点,并进入准备拖动该锚点的状态,如果在直线段上不插入锚点;e. 如果当前有选中的通用线段,而且当前的触点坐标不在本通用线段的某一锚点上,也不在某一曲线段之上,则在触点处生成新的锚点添加到当前焦点之后,并将新的锚点作为焦点,并进入拖动焦点的状态。
2、拖动规则。是指拖动某一通用线段的锚点,通常指拖动选中的通用线段的焦点。条件是,触点未放开状态,触点位于某一选中的通用线段的焦点上。不放手移动该触点,相应的改变该焦点的坐标,从而起到修改锚点坐标的效果。
3、触点释放。触点释放时,如果Ctrl键按下,且当前焦点为通用线段的最后点Pn,释放时触点的坐标位于开始点P1处,则自动闭合该通用线段。当然如果不支持闭合的通用线段则不需要这个过程。
4、移动规则。如果当前有选中的通用线段,可以在工具栏或菜单栏选择移动工具进入整体移动状态,拖动触点时,整体的通用线段的锚点坐标跟着改变。也可以通过键盘的方向键整体移动选中的通用线段。
5、锚点删除。通过键盘的删除按钮删除当前焦点锚点。
3.3 线段类型编辑装置
通用线段的编辑状态下,按下Shift键,当触点时,对触点所在的线段类型做直线段和曲线段类型的循环切换。
3.4 线段连续点序列计算分配装置
通用线段内的多个连续的曲线段组成一个完整的自适应曲线。通用线段内可能包括多个这样的自适应曲线,同时也包括多个直线段。自适应曲线和直线通过的线段连续点序列的计算通过不同的模块来实现,因而需要重新组装分配给相应的计算模块。
上述是本发明关于曲线绘制装置的一个实施方案,本领域人员应知道在实际应用中还可能有其他的操作,比如,设置线段的宽度,设置线段是实线还是虚线,通用线段的拆分,通用线段的组合,闭合通用线段的释放等等。这些都不影响本发明的精神。在某些特殊的应用情形下,还可能需要和贝塞尔曲线组合或者圆弧线段组合。在上述的曲线绘制装置中只需要增加贝塞尔曲线类型或圆弧曲线类型,在线段连续点序列计算分配装置中将这些类型的线段分配给相应的贝塞尔曲线连续点序列计算装置或圆弧曲线连续点计算装置内。本领域的人员应该知道这些也不影响本发明的精神。
4.字体轮廓的应用
字体描述通常有两种形式,一种是点阵字体,一种矢量字体。现有技术中,矢量字体都是通过贝塞尔曲线描述的,有二次贝塞尔曲线描述的字体,也有三次贝塞尔曲线描述的字体。字体文件中保存了每个文字的贝塞尔曲线的锚点,显示文字时,文字处理软件通过这些贝塞尔曲线的锚点恢复出文字的轮廓在填充轮廓后就得到我们通常看到的文字。这些字体文件是事先通过字体定义文件定义的。同样地,本发明的自适应曲线也可以用于描述矢量字体轮廓、生成矢量字体轮廓以及根据矢量字体轮廓的定义显示的文字轮廓。如图4所示,应用本发明的自使用适应曲线构建的一个草书文字的轮廓。
事实上,本发明的自适应曲线应用于字体的效果好于贝塞尔曲线,其好处是矢量字体轮廓生成比较方便。矢量字体轮廓生成通常需要如下过程:1. 获得文字素材,2. 通过文字素材在工具软件中用曲线描绘文字素材中的文字轮廓,3.将曲线的锚点保存。采用贝塞尔曲线在步骤2中描绘文字轮廓比较困难,至少没有自适应曲线那么直观,因为贝塞尔曲线需要中间点,这些中间点并不在曲线构成的轮廓形状上,而本发明的自适应曲线所有的锚点都在曲线构成的轮廓上,因而更加直观。
自适应曲线在字体轮廓上的应用可以采用前述绘图软件中的应用相同的方法,即用直线加自适应曲线结合的方法。
需要说明的是,上述的文字轮廓是指文字显示的边界,是由连续点组成的集合。字体轮廓是指用来描述文字轮廓的贝塞尔曲线或自适应曲线的锚点集合,是非连续的。