马燕鹏 王文新
通常所用的光栅显示器可看成是一个像素点阵的集合,绘图软件绘制椭圆就是在此集合中寻找最逼近椭圆弧的点并按给定颜色显示的过程。输入参数一般为椭圆的外接矩形而非椭圆方程,但可通过该矩形算出它的长、短半轴和椭圆中心,即得到了所绘椭圆的方程。 由于标准椭圆(中心在原点的椭圆)具有对称性,找到第一象限的逼近点根据对称关系就可得到其他象限的逼近点,所以绘制椭圆时先寻找对应标准椭圆的逼近点,然后平移回椭圆的中心,从而能够简化计算提高效率。 标准椭圆的方程为 ,即 。对其求导得,可见椭圆弧的斜率从0到。以斜率 即 为界把椭圆弧分成两部分,第一部分可得 , 的变化量小于的变化量,应以递增步长来确定,第二部分可得,的变化量小于的变化量,应以递增步长来确定。 对于第一部分,假设已知点为则下一点在其右方点或右下方点中选择离椭圆弧最近的点,而为右方点到椭圆弧的距离,为右下方点到椭圆弧的距离,通过做差:
可判断出所要选择的点,即:选择右方的点,选择右下方的点。根据所得到的利用上述思想推下一点即可得到与的递推关系式:
根据递推关系式和已知点利用循环就可得到第一部分所有的点,循环的条件为即。 对于第二部分,假设已知点为则下一点在其上方 点或左上方点中选择离椭圆弧最近的点,而为上方点到椭圆弧的距离,为左上方点到椭圆弧的距离,通过做差: 可判断出所要选择的点,即:选择上方的点,选择左上方的点。根据所得到的利用上述思想推下一点即可得到 与的递推关系式:
根据递推关系式和已知点利用循环就可得到第二部分所有的点,循环的条件为即。 通过分析可把绘制椭圆的功能封装到一个类(如TDrawEllipse)中,利用构造函数初始化要绘制椭圆的位图,利用公有方法Draw来绘制椭圆,该方法首先计算椭圆的长、短半轴和椭圆中心,然后调用私有方法PlotPartI和PlotPartII来绘制椭圆,利用只读属性Image得到绘制后的位图,实现的核心如下: public class TDrawEllipse { private Bitmap image;//存储生成椭圆的位图 public Bitmap Image { get { return this.image; //返回绘制好椭圆的位图 } } private int oX, oY, a, b;//存储椭圆的中心和长短半轴 private Color color;//存储椭圆的颜色 private void PlotPoints(int x, int y) //绘制坐标点 { //由于算法是以(0,0)点为椭圆中心构造的,绘制椭圆时需要把传递的坐标 //平移到真正的椭圆中心(oX,oY)并绘制其它象限的像素点。 this.image.SetPixel(this.oX + x, this.oY + y, this.color); this.image.SetPixel(this.oX - x, this.oY + y, this.color); this.image.SetPixel(this.oX - x, this.oY - y, this.color); this.image.SetPixel(this.oX + x, this.oY - y, this.color); } private void PlotPartI()//绘制第一部分 { int x = 0, y = b; int d = 2 * b * b - 2 * a * a * b + a * a; while (b * b * x < a * a * y) { PlotPoints(x, y); if (d < 0) { x = x + 1; d = d + 4 * b * b * x + 2 * b * b; } else { x = x + 1; y = y - 1; d = d + 4 * b * b * x + 2 * b * b - 4 * a * a * y; } } } private void PlotPartII()//绘制第二部分 { int y = 0, x = a; int d = 2 * a * a - 2 * a * b * b + b * b; while (b * b * x >= a * a * y) { PlotPoints(x, y); if (d < 0) { y = y + 1;d = d + 4 * a * a * y + 2 * a * a; } else { y = y + 1;x = x - 1; d = d + 4 * a * a * y + 2 * a * a - 4 * b * b * x; } } } public TDrawEllipse(Bitmap image)//构造函数,初始化绘制椭圆的位图 { this.image = image; } public void Draw(Rectangle rt, Color color)//绘制椭圆的函数 { //参数 Rectangle rt是要绘制椭圆的外接矩形 //参数 Color color是要绘制椭圆的颜色 this.oX = rt.Left + rt.Width / 2;//计算椭圆的中心坐标 this.oY = rt.Top + rt.Height / 2; this.a = rt.Width / 2;//计算椭圆的长短半轴 this.b = rt.Height / 2; this.color = color; PlotPartI();//绘制第一部分 PlotPartII();//绘制第二部分 } } 有了封装绘制椭圆功能的类之后,就可以在程序中进行实例化得到该类的一个对象,利用该对象调用Draw方法就可绘制出椭圆来,从而完成绘图系统中绘制椭圆功能的底层实现。以上代码在VS.NET2005中编译通过运行正常。
|