第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; }
举一反三
根据本实例,读者可以:
模拟树;
模拟三叶草。