如何利用VC + + 编程技术在图形系统中动态绘制任意阶Bezier 工程曲线方 法,并且使绘制的曲线具有捕捉、修改、动态增加型值点等功能。利用同样的方法,可以实现 B 样条、三次参数等其它工程曲线的绘制,从而扩大了Bezier 等曲线的工程应用。 关键词:Bezier 曲线; 动态绘制; 高阶; VC + + 中图分类号: TP391 文献标识码:A 文章编号:1671 - 5322(2004) 04 - 0029 - 05 UG、CATIA 等图形系统可以实现B 样条曲线 的动态绘制方法,但没有Bezier 、三次参数样条等 曲线的绘制方法。虽然B 样条曲线使用最广泛, 但Bezier 、三次参数样条在工程上也有较多的运 用。为拓宽工程曲线的应用范围, 在结合现行 Bezier 等曲线的算法的基础上,利用VC + + 开发 工具实现了高阶Bezier 等曲线的绘制方法,较方 便地动态实现了曲线的修改。本设计模块可以单 独使用,也可作为一个模块嵌入其它图形系统中 使用。从绘制Bezier 曲线的方法中可了解用现行 绘图软件实现曲线的动态绘制、曲线的识别、拖动 型值点修改曲线等编程方法,该方法对于开发其 它专业图形系统有极大的帮助。 1 Bezier 曲线的算法 1. 1 Bezier 曲线的描述 设 P 0 , P 1 , P 2 , …, P n , 为 n 阶Bezier 曲线的特 征多边形控制点,则Bezier 曲线一般表达式为 P( t) = ∑ n i =0 B i , n ( t) P i , 0 ≤t ≤1 (1) 其中: B i , n ( t) = C n i (1 - t) n - i t i ( i = 0 , 1 , …n) 1. 2 绘制Bezier 曲线的条件 绘制Bezier 曲线的条件有两种,一是给出型 值点坐标,另一是给出控制点坐标。在计算中,一 般最终是利用控制点坐标计算来绘制曲线的。当 给出的数据是型值点时,要利用型值点的坐标值 反求出控制点坐标,再利用控制点坐标进行插补 运算求出曲线的坐标,反求控制点的算法为: 设给出的型值点为 Q 0 , Q 1 , Q 2 …Q n , 要反求 的控制点为 P 0 , P 1 , P 2 …P n , 设参数 t = 0 , 1/ n , 2/ n …1 时分别经过 Q 0 , Q 1 , Q 2 …Q n , 根据Bezier 定义有 Q 0 = P(0) = P 0 Q 1 = P(1/ n) = B 0 , n (1/ n) P 0 + B 1 , n (1/ n) P 1 + … + B n , n (1/ n) P n …… (2) Q n- 1 = P( ( n - 1) / n) = B 0 , n ( ( n - 1) / n) P 0 + B 1 , n ( ( n - 1) / n) P 1 + …+ B n , n P n Q n = P n 把上式写成矩阵形式,利用矩阵的求逆运算, 求出控制点为式(3) 。2 绘制曲线类的方法 2. 1 构造Bezier 曲线类 在VC + + 中绘制Bezier 曲线,首先建立Bezi2 er 曲线类,利用Bezier 类的指针来对Bezier 曲线进 行控制。在VC + + 中所构造Bezier 的曲线类为 CBezier : : CBezier ( short ColorPen , short Color2 Brush , float LineWide , short LineType , float x- Scale , short Layer , int id-only , bool Delete , BpointStruct 3 BpointList) : CDraw ( ColorPen , ColorBrush ,LineWide , LineType ,x- Scale ,Layer ,id-only ,Delete) 各参数的控制对象分别为线型、颜色、线宽、 比例、图层、曲线 ID 号、删除状态、型值点数目、存 放型值点的结构指针,其中BpointStruct 结构有 x 和y 两个成员, 存放型值点坐标, CDraw 类为 CBezie 的基类,该类的各个参数在构造成时完成 初始化。CBezier 类成员有: int m-Numble ;/ / 存放结点的数目 BPointStruct 3 m-BpointList ;/ / 存放型值点的坐 标 BPointStruct 3 m- CpointList ;/ / 存放反求的控制 点的坐标 void Draw ( CDC 3 pDC , int m-DrawMode , int m- DrawMode1 ,short BackColor) ;/ / 绘制曲线函数 BOOL IsPoint (float x ,float y ,float jl ,float blc ,int 3 No) ; / / 判断曲线是否选中函数 virtual void MDpoint ( int num , float x , float y) ;/ / 修改控制点函数 void GetRect (float 3 minX,float 3 minY,float 3 maxX, float 3 maxY) ;获得曲线区域函数 virtual~CBezier () ;/ / CBezier 的析函数 CBezier 类成员在类构造时完成参数数值的 传递,绘制曲线时构造一个CBezier 类对象。为了 存储构造的对象指针,在文档类中建立一个存放 曲线指针的指针m-BezierArray。 CTypedPtrArray < CObArray , CBezier 3 > m- BezierArray。 2. 2 动态绘制Bezier 曲线 Bezier 曲线类由Draw 函数完成曲线的绘制, 在Draw中完成曲线的反求控制点、插补运算、区 域的计算, 设置线型、颜色、线宽等功能。Bezier 曲线的绘制方法是当用户选择绘制 Bezier 曲线的 功能时,用户在屏幕上拾取型值点时或输入型值 点坐标,在屏幕上动态绘制曲线,绘制中每增加一 个型值点时,则Bezier 曲线的阶次增加一阶,此方 法可以实现任意阶的Bezier 曲线的绘制, 这和B 样条的曲线绘制方法不同,B 样条曲线可用多个 三次B 样条曲线绘制N 个型值点样条曲线,得到 的曲线是光滑连续的,而Bezier 曲线随着型值点 个数的增加,若用升阶的方法计算,则绘制程序要 根据型值点的个数重新计算曲线的坐标。Draw 函数绘制的程序流程如图1 所示。 图1 Draw函数的流程图 Fig. 1 Process flowfor drawfunction 在程序中构造成计算矩阵时,把与坐标值对 应的参数 t 的值代入伯恩斯坦基函数计算出与各 型值点对应的各个基函数值,其中调用了求阶乘、 求幂等函数,然后组合成一个基函数矩阵,再调用 矩阵的求逆运算,本模块中采用全选主元高斯消 �9�9 0 3 �9�9 盐城工学院学报(自然科学版) 第17 卷 去法进行求逆,得到计算的系数矩阵,求出控制点 坐标,根据Bezier 曲线的计算公式计算出曲线的 坐标。绘制Bezier 曲线调用了CDC类的绘制直线 方法,用直线段来近似代替曲线,图形系统中绘制 曲线大多采用这种方法,曲线的近似程度和所采 用的插值间距有关。为了得到好的显示效果,要 把间距控制视觉能接受的范围内。由于无论曲线 多长, t 的值都是在0~1 之间, 当 t 的分隔间距恒 定时, 在动态绘制过中, 随着曲线长度的增加, 直 线的逼近效果越差。t 的划分方法很多, 可以利 用等间距、等弦长方法来解决这一问题, 一般来 说, t 的插值间距为非均匀, 而让每一段代替曲线 的直线段的距离为定值, 获得好的视觉效果。 2. 3 增加Bezier 曲线 在动态绘制过程中,当鼠标移动过程中,曲线 也随着鼠标的位置不同实现重画。这时的图形指 针并没有存储到Bezier 曲线类的m-BezierArray 数 组中。只有当完成曲线绘制时,发出命令后才进 行曲线的最后绘制,对象指针才加入到 m-Bezier2 Array 中存储。当用鼠标的右键结束绘制时,在鼠 标的消息函数中增加以下语句 pDoc > AddCBezier ( m-pColor , m- brColor , m- LineWide ,m-LineType ,m-xScale ,m-Layer ,id-only ,0 , PushNumb ,BPointXyz) - > Draw( &ddd ,0 ,1 ,m- bCol2 or) ; AddCBezier 函数在文档类中定义, 它完成对 象的增加,存储指针。得到指针后,调用Draw 函 数绘制直线。 3 实现曲线类动态修改 3. 1 曲线的捕捉 曲线的捕捉功能是图形系统的基本功能,要 实现曲线的捕捉前提是获得曲线类的对象指针, 建立Bezier 曲线类的目的就是为了实现曲线的捕 捉、移动、修改、删除、恢复, 移动和缩放功能。 Bezier 类中 IsPoint 函数就是为了实现鼠标移动过 程中鼠标位置与曲线之间距离的计算,距离判别 最简单的方法是计算鼠标点位置和对象中的存储 的型值点距离,若距离在设定的范围内,则把选中 的图形突出显示。IsPoint 函数不但返回曲线是否 选中的逻辑值,而且利用参数返回选中曲线的型 值点的序号。更为复杂的方法是把鼠标点和曲线 的坐标进行判别,这种判别方法可用的判别点多, 曲线易选中,但计算复杂,系统采用第一种方法实 现捕捉功能。 3. 2 曲线的修改 在绘图过程中,有时需要修改图形,一般是通 过修改型值或控制点的位置来达到所要求的曲线 形状。为了修改型值或控制点,在Bezier 类中增 加了Mdpoint 函数, 在函数中根据 IsPoint 返回的 型值点或控制点的序号替换它们的坐标,然后重 画曲线,这样就实现了动态修改的功能。 3. 3 曲线的删除、恢复 曲线删除有两种情况,一种是可以恢复的,另 一种是不可恢复的。不可恢复的的删除,只要在 存放对象指针的m-BezierArray 数组中删除所要删 除的曲线指针。如果是可恢复的删除,只是在对 象的成员中增加了删除标志,在重画曲线时,做了 删除标志的曲线不绘制,而对象指针并没有从指 针数组中删除。 3. 4 曲线的移动和缩放 曲线移动是让曲线做平移。平移时,根据平 移的 X, Y方向的距离在曲线的型值或控制点上 加上一个平移值,该值的正负号表示移动方向。 曲线的缩放是根据图形的缩放比例对图形放 大或缩小。在程序中,增加了缩放的比例因子和 从逻辑坐标到实际坐标转换函数VPtoDP 以及从 实际坐标到逻辑坐标的转换函数DPtoVP ,在坐标 转换函数中,通过缩放的比例因子改变坐标值达 到缩放目的。VPtoDP 函数定义如下 void CVDrawView: : DPtoVP (float x ,float y , int 3 X,int 3 Y) { 3 X= (int) ( (x - m-xStart) / blc) ; if (m-MapMode = = 1) 3 Y= m- hScreen - (int) ( (y - m-yStart) / blc) ; else 3 Y= (int) ( (y - m-yStart) / blc) - m- hScreen ; } 其中,blc 就是坐标变换中的缩放因子,m-xS2 tart 、m-yStart 是调节整个图形的平行移动坐标。 4 实现曲线动态绘制的过程 在实现Bezier 曲线的绘制过程中,绘制过程 会调用了许多函数,利用了鼠标的消息函数来实 现绘图操作。具体实现的过程是先用鼠标单击左 键在屏幕上拾取曲线的型值点,当拖动鼠标时,曲 线随着鼠标的移动在不断的变化。