• 【原创】基本图形生成:直线篇


    在显示器或者绘图机等这些图形输出设备中,都可以看作存在一个网格。对应显示器上的每一个像素,绘图机画笔每一步的终点,都可以看作是网格上的一个网格点。这些网格点是有一定距离的,而所有图形的显示都是要由网格点组成,所以显示出来的图形不可能想原先那样精确。这就只能通过一些用于图形扫描算法近似地模拟。

    下面就是一些经典的图形扫描算法。其主要的工作就是当图形由Q0(x0r,y0r)到Q1(x1r,y1r)时,Q0已经在网格上的网格点(xpi,ypi)上显示了,要在网格上显示Q1(x1r,y1r),到底是应该取网格点(xpi+1,ypi),还是网格点(xpi+1,ypi+1)呢?对应某种的图形有多种不同的算法,算法所实现的功能是一样的,就是上面的。比较各种算法,关键就要看那个算法的计算效率。

    为了操作图形输出设备中的网格点,我定义了像素类:
    public class Pixel
    {
        
    public int _x;
        
    public int _y;
        
        
    public Pixel()
        
    {
            
    this._x = 0;
            
    this._y = 0;
        }

        
        
    public Pixel(int x,int y)
        
    {
            
    this._x = x;
            
    this._y = y;
        }

    }
    而对于数学上的点,我定义了Point来操作
    public class Point
    {
        
    public float x;
        
    public float y;
    }
    尽管扫描算法是一些底层的东西,用C#这样的高层的、面向对象的语言来描述确实有些别扭。但对于熟悉OO的人来说,这不失为一种抽离具体语言,直接进入算法核心的学习途径

    生成直线:
    1.DDA(数值微分法)
         要点:通过斜率k=(y1r-y0r)/(x1r-x0r)与1比较,确定y1r-y0r与x1r-x0r那个大,从而决定下一点取网格点
                 P1(xp+1,yp),还是网格点P2(xp+1,yp+1)。

         代码:
    public Pixel LineDDA(Point p0,Point p1,Pixel precedent)
        
    {
            Pixel descendent 
    = new Pixel();
            
    float k=Math.Abs((p1.y-p0.y)/(p1.x-p0.x));
            
            
    if(k>1||k==1)
            
    {
                descendent._x 
    = precedent._x + 1;
                descendent._y 
    = precedent._y + 1;
            }

            
    else
            
    {
                descendent._x 
    = precedent._x + 1;
                descendent._y 
    = precedent._y + 1;
            }

            
    return descendent;
        }

    2.中点画线法
         要点:比较直线L与x=x0+1间的真实交点Q(x1r,y1r)与两网格点P1,P2间的中点M((xp1+xp2/2(yp1+yp2             /2)进行比较。通过构造的判别式d=F(M)=F(xp+1,yp+0.5)=a(xp+1)+b(yp+0.5)+c与0的关系来判断取点。            (其中a=y0r-y1r,b=x1r-x0r,c=x0ry1r-x1ry0r
                 当d<0,Q在M上方,取在上的P2
                 当d>0,Q在M上方,取在上的P1
                 当d=0,Q与M重合,取P1、P2均可。

                 其实所谓的判别式,就是相当于F(x,y)=ax+by+c=0这样一个直线方程,这个也是我们要在图形设备上显示的直             线。通过代入(xp+1,yp+0.5)并把方程与0比较,这相当于高中的数学学过的线性规划。

         代码:
    public Pixel LineMidPoint(Point p0,Point p1,Pixel precedent)
        
    {
            Pixel descendent 
    = new Pixel();
            
    float a = p0.y-p1.y;
            
    float b = p1.x-p0.x;
            
    float c = p0.x*p1.y-p1.x*p0.y;
            
    float d=a*(precedent._x+1)+b*(precedent._y+0.5)+c;
            
            
    if(d>0||d==0)
            
    {
                descendent._x 
    = precedent._x + 1;
                descendent._y 
    = precedent._y + 1;
            }

            
    else
            
    {
                descendent._x 
    = precedent._x + 1;
                descendent._y 
    = precedent._y + 1;
            }

            
    return descendent;
        }

    3.Bresenham算法:
         要点:计算真实点Q(x1r,y1r)到两相近的网格点P1(xp+1,yp),P2(xp+1,yp+1)的距离d1,d2,通过d1-d2与0比较得出               Q靠近P1还是靠近P2,从而选出是取P1还是P2。通过递归的判别式来做:
                 设d1-d2=£p=(yp+1 -y1r)-(y1r-yp)=2(yp-y1r)+1
                 则d1'-d2'=£p+1=(yp+1 +1 -y2r)-(y2r-yp+1)
                                      =£p+k-1    当£p>=0
                                            or
                                      =£p+k       当£p<0
                 其中k=dy/dx,直线的斜率
                 设定直线起点Q0(x0r,y0r),取在(xp1,yp1),取接下来的点Q1(x1r,y1r)时,£1=(yp1+1-y1r)-(y1r-yp1)=
                 2(yp1-y1r)+1=2k+1(取dx=1,dy=y1r-y0r=y1r-yp1

                 以上是对应于dx>=dy>0的情况,当dy>dx>0时,要把x和y的位置交换。当dx<0或dy<0,取点时相应的             xpi+1=xpi-1或ypi+1=ypi-1。

         代码:
    dx>=dy>0的情况:
    public ArrayList LineBresenham(Point p0,Point p1)
        
    {
            ArrayList pixelArray 
    = new ArrayList();
            
    float k = (p1.y-p0.y)/(p1.x-p0.x);
            
    float e = 2 * k - 1;
            
            Pixel precedent 
    = new Pixel((int)p0.x,(int)p0.y);
            pixelArray.Add(precedent);
            
            
    for(int i=0;i<p1.x-p0.x;i++)
            
    {
                
    if(e<0)
                
    {
                    e 
    = e + k;
                    Pixel descendent 
    = new Pixel(precedent._x+1,precedent._y);
                    pixelArray.Add(descendent);
                }

                
    else
                
    {
                    e 
    = e + k - 1;
                    Pixel descendent 
    = new Pixel(precedent._x+1,precedent._y+1);
                    pixelArray.Add(descendent);
                }

            }

            
    return pixelArray;
        }

    4.二步法
         要点:

         代码:
  • 相关阅读:
    vim设置字体
    mplayer error opening/initializing the selected video_out (vo) device
    ubuntn MySQL安装指南
    man linux
    ubuntu中无法启用桌面效果(3D效果)几种解决方案
    man c 函数 安装 使用
    Ubuntu使用桌面小工具
    Josephus 排列问题
    Adding the PPA to Ubuntu
    Ubuntu的root密码是什么
  • 原文地址:https://www.cnblogs.com/alonecat06/p/1100792.html
Copyright © 2020-2023  润新知