第3章图形技术

3.1 绘制图形

利用程序自动绘制图形可以增强图形的可视度,特别是对于一些多媒体教学之类的软件来说,使用程序自动绘制并控制图形无疑是一个新的突破。本节通过几个应用实例介绍绘图方面的相关知识。

实例088 绘制正弦曲线

本实例是一个图形绘制的程序

实例位置:光盘\mingrisoft\03\088

实例说明

在多媒体教学时,经常需要动态地画出各种曲线进行教学演示,以加深学生对知识的理解。本实例实现的是绘制一个正弦曲线。运行程序,系统将自动绘制正弦曲线,效果如图3.1所示。

图3.1 绘制正弦曲线

技术要点

首先通过SetViewportOrg函数设置坐标原点,然后使用MoveTo和LineTo函数绘制坐标轴和曲线。下面介绍这两个函数。

(1)MoveTo函数。该函数用于设置画笔位置。

函数原型如下:

CPoint MoveTo( int x, int y );

参数说明:

● x:横坐标。

● y:纵坐标。

(2)LineTo函数。该函数用当前画笔画一条线,从当前位置连到一个指定的点。

BOOL LineTo( int x, int y );

参数说明:

● x:横坐标。

● y:纵坐标。

实现过程

(1)新建一个基于单文档的应用程序。

(2)主要程序代码如下:

        O void CSinusoidView::OnDraw(CDC* pDC)
        {
            CSinusoidDoc* pDoc = GetDocument();
            ASSERT_VALID(pDoc);
            //建立画笔
            CPen cpen,pen;
            pen.CreatePen(PS_SOLID,4,RGB(0,0,0));
            cpen.CreatePen(PS_SOLID,2,RGB(0,0,255));
            pDC->SelectObject(&cpen);
            //指定原点
            pDC->SetViewportOrg(100,245);
            pDC->SetTextColor(RGB(255,0,0));
            //绘制横坐标
            CString sPIText[]={"-1/2π","","1/2π","π","3/2π","2π","5/2π","3π","7/2π","4π","9/2π","5π"};
            for(int n=-1,nTmp=0;nTmp<=660;n++,nTmp+=60)
            {
              pDC->LineTo(60*n,0);
              pDC->LineTo(60*n,-5);
              pDC->MoveTo(60*n,0);
              pDC->TextOut(60*n-sPIText[n+1].GetLength()*3,16,sPIText[n+1]);
              }
              pDC->MoveTo(0,0);
              CString sTmp;
              //绘制纵坐标
              for(n=-4,nTmp=0;nTmp<=180;n++,nTmp=60*n)
              {
              pDC->LineTo(0,60*n);
              pDC->LineTo(5,60*n);
              pDC->MoveTo(0,60*n);
              sTmp.Format("%d",-n);
              pDC->TextOut(10,60*n,sTmp);
              }
              double y,radian;
              pDC->SelectObject(&pen);
              for(int x=-60;x<600;x++)
              {
              //弧度=x坐标/曲线宽度*角系数*π
              //y坐标=振幅*曲线宽度*sin(弧度)
              radian =x/((double)60*2)*PI;
              y=sin(radian)*2*60;
              pDC->MoveTo((int)x,(int)y);
              pDC->LineTo((int)x,(int)y);
              }
              cpen.DeleteObject();
              pen.DeleteObject();
          }

举一反三

根据本实例,读者可以:

绘制余弦曲线;

绘制双曲线。

实例089 绘制蜗牛曲线

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\03\89

实例说明

蜗牛线是一种常用的曲线,本实例演示的是如何在程序中绘制蜗牛曲线。运行程序,程序将自动绘制蜗牛曲线,效果如图3.2所示。

图3.2 绘制蜗牛曲线

技术要点

通过SetPixel函数来绘制曲线上的点来实现绘制蜗牛曲线。SetPixel函数的语法如下:

          COLORREF SetPixel( int x, int y, COLORREF crColor );
          COLORREF SetPixel( POINT point, COLORREF crColor );

参数说明:

● x,y:指定点的横坐标和纵坐标。

● point:指定点的坐标。可以为该参数传递POINT结构或CPoint对象。

● crColor:绘制点的颜色。

实现过程

(1)新建一个基于单文档的应用程序。

(2)主要程序代码如下:

            void CSnailView::OnDraw(CDC*pDC)
            {
            CSnailDoc* pDoc = GetDocument();
            ASSERT_VALID(pDoc);
            pDC->SetWindowOrg(-200,-200);           //设置坐标原点
            //绘制蜗牛曲线
            for(double i=0;i<40;i+=pi/600)
            {
              if(i==0)
                    pDC->SetPixel(0,0,RGB(0,128,128));
              else
                    pDC->SetPixel((20*sin(i)/i*cos(i))*10,(20*sin(i)/i*sin(i))*10,RGB(128,128,128));
            }
        }

举一反三

根据本实例,读者可以:

设置点的颜色。

实例090 绘制贝塞尔曲线

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\03\090

实例说明

贝塞尔曲线是一种常见的曲线,本实例演示的是绘制水平和垂直两个方向的贝塞尔曲线。运行程序,程序自动绘制贝塞尔曲线,效果如图3.3所示。

图3.3 绘制贝塞尔曲线

技术要点

CDC类的PolyBezier成员函数是专门用来绘制贝塞尔曲线。该函数的语法如下:

BOOL PolyBezier( const POINT* lpPoints, int nCount );

参数说明:

● lpPoints:含有曲线控制点和端点的POINT数据结构或CPoint对象。

● nCount:lpPoints数组中点的数目。其值应为曲线数目的3倍以上,因为每条Bezier曲线需要2个控制点和1个终点,初始曲线还需要1个起点。

实现过程

(1)新建一个基于对话框的应用程序。

(2)主要程序代码如下:

        void CDrawBezierDlg::OnPaint()
            if (IsIconic())
        {
            {
            //此处代码省略
              dc.DrawIcon(x, y, m_hIcon);
            }
            else
            {
              CPaintDC dc(this);
              CPen newpen;
              newpen.CreatePen(PS_SOLID,1,RGB(255,128,0));//创建画笔
              dc.SelectObject(&newpen);
              //垂直
              POINT ptv[4];
              ptv[0].x=300;
              ptv[0].y=20;
              ptv[1].x=250;
              ptv[1].y=70;
              ptv[2].x=350;
              ptv[2].y=120;
                  ptv[3].x=300;
                  ptv[3].y=170;
                  dc.PolyBezier(ptv,4);                     //绘制垂直贝塞尔曲线
                  //水平
                  POINT pth[4];
                  pth[0].x=20;
                  pth[0].y=120;
                  pth[1].x=70;
                  pth[1].y=70;
                  pth[2].x=120;
                  pth[2].y=170;
                  pth[3].x=170;
                  pth[3].y=120;
                  dc.PolyBezier(pth,4);                     //绘制水平贝塞尔曲线
                  CDialog::OnPaint();
              }
          }

举一反三

根据本实例,读者可以:

绘制雕刻效果。

实例091 画图程序

本实例是一个提高基础技能的程序

实例位置:光盘\mingrisoft\03\091

实例说明

本实例实现了一个简单的画图功能。运行程序,在“绘图”菜单下选择要绘制的图形之后,在窗体中拖动鼠标就可以绘制想要的图形,效果如图3.4所示。

图3.4 画图程序

技术要点

实现画圆和矩形,需要使用CDC对象的Ellipse方法和Rectangle方法,而自定义画线是通过线段来记录 鼠 标 的 移 动 过 程,这 些 功 能 都 是 在WM_MOUSEMOVE消息中实现的。下面介绍CDC对象的Ellipse方法和Rectangle方法。

(1)Ellipse方法。该方法用于画椭圆形。

语法如下:

BOOL Ellipse( int x1, int y1, int x2, int y2 );

参数说明:

● x1:起始点横坐标。

● y1:起始点纵坐标。

● x2:结束点横坐标。

● y2:结束点纵坐标。

(2)Rectangle方法。该方法用于绘制长方形。

语法如下:

BOOL Rectangle( int x1, int y1, int x2, int y2 );

参数说明:

● x1:左上角横坐标。

● y1:左上角纵坐标。

● x2:右下角横坐标。

● y2:右下角纵坐标。

注意:WM_MOUSEMOVE消息在第2章已经介绍过了,这里不再赘述。

实现过程

(1)新建一个单文档的应用程序。

(2)编辑菜单资源,添加一个“绘图”菜单项,在其下添加“画圆”、“画矩形”和“自定义”3个子菜单。

(3)通过类向导为新建的3个子菜单添加消息响应函数。

(4)主要程序代码如下:

        void CCTdrawView::OnMouseMove(UINT nFlags, CPoint point)
        {
            CClientDC dc(this);
            if(m_draw&&(nFlags&&MK_LBUTTON))      //如果是画线
            {
              dc.MoveTo(m_start);
              dc.LineTo(point);
              m_start = point;
            }
            if(m_juxing&&(nFlags&&MK_LBUTTON))    //如果是画矩形
            {
              CGdiObject*object = dc.SelectStockObject(NULL_BRUSH);
              int mdoe = dc.GetROP2();
              dc.SetROP2(R2_NOTCOPYPEN);
              dc.Rectangle(m_end.x,m_end.y,m_start.x,m_start.y);
              dc.SetROP2(mdoe);
              dc.Rectangle(m_start.x,m_start.y,point.x,point.y);
              dc.SelectObject(object);
              m_end = point;
            }
            if(m_yuan&&(nFlags&&MK_LBUTTON))      //如果是画圆
            {
              CGdiObject*object = dc.SelectStockObject(NULL_BRUSH);
              int mdoe = dc.GetROP2();
              dc.SetROP2(R2_NOTCOPYPEN);
              dc.Ellipse(m_end.x,m_end.y,m_start.x,m_start.y);
              dc.SetROP2(mdoe);
              dc.Ellipse(m_start.x,m_start.y,point.x,point.y);
              dc.SelectObject(object);
              m_end = point;
            }
            CView::OnMouseMove(nFlags, point);
        }

举一反三

根据本实例,读者可以:

编写画板软件。

实例092 绘制立体模型

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\03\092

实例说明

立体几何图形在多媒体教学中经常会用到,本实例演示的是如何动态地绘制一个正方体图案。运行程序,在编辑框中分别输入长、宽、高和角度,单击“绘图”按钮将绘制立体模型,效果如图3.5所示。

技术要点

首先设置一个图形控件作为画板,通过SetViewportOrg函数获得中心点坐标,根据输入的数据计算立体模型的各个点坐标,使用CDC对象的Rectangle方法绘制正面的长方形,根据角度判断哪几条边用虚线绘制。

注意:CDC对象的Rectangle方法参照实例091中的内容。

图3.5 绘制立体模型

实现过程

(1)新建一个基于对话框的应用程序。

(2)向窗体中添加1个图片控件,设置Type属性为Rectangle和Color属性为white,再添加4个编辑框控件。

(3)主要程序代码如下:

          void CDrawcudeDlg::OnButdraw()
          {
            CDC*pDC;
            pDC=m_palette.GetDC();                       //获得设备上下文
            CRect rc,rect;
            m_palette.GetClientRect(rect);               //获得控件客户区域
            m_palette.GetWindowRect(rc);                 //获得控件窗口区域
            pDC->FillRect(rect,NULL);                    //填充区域
            //取出中心点
            CPoint center;
            center.x=rc.Width()/2;
            center.y=rc.Height()/2;
            pDC->SetViewportOrg(center);                 //设置原点
            CString slength,swidth,sheight,sangle;
            //获得编辑框中数据
            m_length.GetWindowText(slength);
            m_width.GetWindowText(swidth);
            m_height.GetWindowText(sheight);
            m_angle.GetWindowText(sangle);
            int nlength,nwidth,nheight,nangle;
            //将编辑框中数据转换为整型数据
            nlength=atoi(slength);
            nwidth=atoi(swidth);
            nheight=atoi(sheight);
            nangle=atoi(sangle);
            CPoint LTop,LBottom,RTop,RBottom;
            LTop.x=1-nlength/2;
            LTop.y=1-nheight/2;
            RTop.x=nlength/2;
            RTop.y=1-nheight/2;
            LBottom.x=1-nlength/2;
            LBottom.y=nheight/2;
            RBottom.x=nlength/2;
            RBottom.y=nheight/2;
            CPen pen(PS_SOLID,1,RGB(0,0,0));              //创建画笔
            CPen DOTPen;
            DOTPen.CreatePen(PS_DOT,1,RGB(0,0,0));       //虚线画笔
            pDC->SelectObject(&pen);
            //画正面矩形
            pDC->Rectangle(LTop.x,LTop.y,RBottom.x,RBottom.y);
            CPoint LeftTop,RightTop;
            LeftTop.x=(long)(LTop.x+(cos(nangle*PI/180)*nwidth));
            LeftTop.y=(long)(LTop.y-(sin(nangle*PI/180)*nwidth));
            RightTop.x=LeftTop.x+nlength;
            RightTop.y=LeftTop.y;
            pDC->MoveTo(LTop);
            pDC->LineTo(LeftTop);
            pDC->LineTo(RightTop);
            pDC->LineTo(RTop);
            CPoint Other,DotPoint;
            DotPoint.x=LeftTop.x;
            DotPoint.y=LeftTop.y+nheight;
            pDC->MoveTo(RightTop);
            if(nangle<89)  //画立方体其他面实边线
            {
              pDC->SelectObject(&pen);
              Other.x=RightTop.x;
              Other.y=RightTop.y+nheight;
            pDC->LineTo(Other);
            pDC->LineTo(RBottom);
            pDC->SelectObject(&DOTPen);
            pDC->MoveTo(LeftTop);
            pDC->LineTo(DotPoint);
            pDC->LineTo(LBottom);
        }
        else         //绘制矩形背面虚线
        {
            pDC->SelectObject(&DOTPen);
            Other.x=RightTop.x;
            Other.y=RightTop.y+nheight;
            pDC->LineTo(Other);
            pDC->LineTo(RBottom);
            pDC->SelectObject(&pen);
            pDC->MoveTo(LeftTop);
            pDC->LineTo(DotPoint);
            pDC->LineTo(LBottom);
        }
        pDC->SelectObject(&DOTPen);
        pDC->MoveTo(DotPoint);
        pDC->LineTo(Other);
        }

举一反三

根据本实例,读者可以:

绘制多边型。

实例093 利用IFS算法绘制自然景物

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\03\093

实例说明

本实例通过IFS算法来实现对自然景物的模拟,IFS可以模拟的景物很多,有山、树、三叶草和皇冠等,本实例实现对山的模拟,如图3.6所示。

技术要点

图3.6 利用IFS算法绘制自然景物

IFS(Iterator Function System)是一种分形几何系统,主要通过仿射坐标变换来生成几何图形,仿射坐标变换是旋转、扭曲和平移3种效果的叠加。

实现过程

(1)创建名为IFS的单文档MFC工程。

(2)在头文件中加入变量的声明,具体参照代码。

(3)在CIFSView类的头文件中声明变量:

        class CIFSView : public CView
        {
        public:
            CIFSDoc* GetDocument();
            //声明的变量
            double x,y;int i,k;
            int MaxY;
            double  a[8],b[8],c[8],d[8],e[8],f[8],p[8];
            COLORREF    m_pColor;
            int drawtrue;
            int m_N;
            int stepx,stepy;
            int totalsteps;
        }

(4)在实现文件中加入常量声明:

#define MaxY 400

(5)在构造函数中实现对变量的初始化:

          CIFSView::CIFSView()
          {
              m_pColor=RGB(0,255,0);
              m_N=4;
              stepx=3;
              stepy=3;
              totalsteps=32000;
          }

(6)在OnDraw函数中进行图形的绘制,代码如下:

          void CIFSView::OnDraw(CDC* pDC)
          {
              CIFSDoc* pDoc = GetDocument();
              ASSERT_VALID(pDoc);
              for(i=1;i<m_N;i++)
              {
              p[i]=p[i]+p[i-1];
              }
              float xj,m;
              x=0;y=0;
              srand(unsigned(time(NULL)));        //设置随机数种子
              for(i=0;i<totalsteps;i++)
              {
              m=float(rand());                    //获得随机数
              xj=float(m/RAND_MAX);
              if(xj<=p[0]) k=0;
              if((xj>p[0])&&(xj<=p[1])) k=1;
              if((xj>p[1])&&(xj<=p[2])) k=2;
              if((xj>p[2])&&(xj<=p[3])) k=3;
              if((xj>p[3])&&(xj<=p[4])) k=4;
              if((xj>p[4])&&(xj<=p[5])) k=5;
              if((xj>p[5])&&(xj<=p[6])) k=6;
              if((xj>p[6]) &&(xj<=p[7])) k=7;
              x=a[k]*x+b[k]*y+e[k];
              y=c[k]*x+d[k]*y+f[k];
              if(i>10)
              pDC->SetPixel(int(MaxY*x/stepx+MaxY/2 ) , MaxY-int(MaxY*y/stepy+30)-100 ,m_pColor);//绘制点
              }
          }

(7)在OnInitialUpdate中为IFS算法所需要的数组设置初始值,代码如下:

          void CIFSView::OnInitialUpdate()
          {
              CView::OnInitialUpdate();
              CIFSDoc *pDoc=GetDocument();
              ASSERT_VALID(pDoc);
              //变量的初始化
              a[0]=0.7;a[1]=0.5;a[2]=-0.4;a[3]=-0.5;
              b[0]=0.0;b[1]=0;b[2]=0;b[3]=0;
              c[0]=0;c[1]=0;c[2]=1;c[3]=0;
              d[0]=0.8;d[1]=0.5;d[2]=0.4;d[3]=0.5;
              e[0]=0;e[1]=2;e[2]=0;e[3]=2;
              f[0]=0;f[1]=0;f[2]=1;f[3]=1;
              p[0]=0.25;p[1]=0.25;p[2]=0.25;p[3]=0.25;
          }

举一反三

根据本实例,读者可以:

模拟树;

模拟三叶草。