一、实验目的

  1. 掌握双缓冲绘图技术。

(2)掌握人机交互技术。

(3)掌握填充动态多边形的有效边表算法。

二、实验步骤

(1)在VS2017环境下创建MFC应用程序工程(单文档)

(2)添加命令消息处理函数、双缓冲技术函数

(3)定义边节点类、桶节点类、填充多边形类

(3)添加成员函数及成员变量

(4)编写函数内容

(4)绘制与填充多边形

三、实验结果

四、实验体会

通过本次实验,我掌握了双缓冲绘图技术、人机交互技术、填充动态多边形的有效边表算法,加深了对算法的理解。通过亲自动手将理论运用于实践,加强了动手能力,对MFC应用程序工程的编写更为熟悉了,对于实际的操作有了质的飞跃,整体对计算机图形学的理解有了不少提高。

附录:源代码

类视图

class CBucket  //桶节点类
{
public:CBucket();virtual ~CBucket();
public:int     ScanLine;//扫描线CAET    *pET;    //边表CBucket *next;
};class CAET   //边节点类
{
public: CAET(); virtual ~CAET();
public: double        x;     //当前扫描线与有效边的交点的 x 坐标int           yMax; //边的最大 y 值double        k;        //斜率的倒数(x 的增量) CAET            *next;
};
填充多边形类
CFill::CFill()
{PNum=0;P=NULL;pEdge=NULL;pHeadB=NULL;pHeadE=NULL;
}CFill::~CFill()
{if(P!=NULL){delete[] P;P=NULL;}ClearMemory();
}void CFill::SetPoint(CPoint p[],int m)//动态创建多边形顶点数组
{P=new CPoint[m];for(int i=0;i<m;i++){P[i]=p[i];    }PNum=m;
}void CFill::CreateBucket()//创建桶表
{int yMin, yMax;yMin = yMax = P[0].y;for (int i = 0; i < PNum; i++)//查找多边形所覆盖的最小和最大扫描线{if (P[i].y < yMin){yMin = P[i].y;//扫描线的最小值}if (P[i].y > yMax){yMax = P[i].y;//扫描线的最大值}}for (int y = yMin; y <= yMax; y++){if (yMin == y)//建立桶头结点{pHeadB = new CBucket;//pHeadB 为 CBucket 的头结点pCurrentB = pHeadB;//CurrentB 为 CBucket 当前结点pCurrentB->ScanLine = yMin;pCurrentB->pET = NULL;//没有链接边表pCurrentB->next = NULL;}else//建立桶的其它结点{pCurrentB->next = new CBucket;pCurrentB = pCurrentB->next;pCurrentB->ScanLine = y;pCurrentB->pET = NULL;pCurrentB->next = NULL;}}
}void CFill::CreateEdge()//创建边表
{for (int i = 0; i < PNum; i++){pCurrentB = pHeadB;int j = (i + 1) % PNum;//边的第二个顶点,P[i]和 P[j]构成边if (P[i].y < P[j].y)//边的起点比终点低{pEdge = new CAET;pEdge->x = P[i].x;//计算 ET 表的值pEdge->yMax = P[j].y;pEdge->k = (double)(P[j].x-P[i].x) / ((double)(P[j].y-P[i].y));//代表 1/k pEdge->next = NULL;while (pCurrentB->ScanLine != P[i].y)//在桶内寻找该边的 yMin {pCurrentB = pCurrentB->next;//移到 yMin 所在的桶结点}}if (P[j].y < P[i].y)//边的终点比起点低{pEdge = new CAET;pEdge->x = P[j].x;pEdge->yMax = P[i].y;pEdge->k = (double)(P[i].x-P[j].x) / ((double)(P[i].y-P[j].y));pEdge->next = NULL;while (pCurrentB->ScanLine != P[j].y){pCurrentB = pCurrentB->next;}}if ((P[j].y) != P[i].y){pCurrentE = pCurrentB->pET;if (pCurrentE == NULL){pCurrentE = pEdge;pCurrentB->pET = pCurrentE;}else{while (NULL != pCurrentE->next){pCurrentE = pCurrentE->next;}pCurrentE->next = pEdge;}}}
}void CFill::AddEt(CAET *pNewEdge)//合并ET表
{CAET *pCE=pHeadE;if(pCE==NULL){pHeadE=pNewEdge;pCE=pHeadE;}else{while(pCE->next!=NULL){pCE=pCE->next;}pCE->next=pNewEdge;}
}void CFill::EtOrder()//边表的冒泡排序算法
{CAET *pT1 = NULL, *pT2 = NULL;int Count = 1;pT1 = pHeadE;if (NULL == pT1){return;}if (NULL == pT1->next){return;}while (NULL != pT1->next){Count++;pT1 = pT1->next;}for (int i = 1; i < Count; i++){pT1 = pHeadE;if (pT1->x > pT1->next->x){pT2 = pT1->next;pT1->next = pT1->next->next;pT2->next = pT1;pHeadE = pT2;}else{if (pT1->x == pT1->next->x){if (pT1->k > pT1->next->k){pT2 = pT1->next;pT1->next = pT1->next->next;pT2->next = pT1;pHeadE = pT2;}}}pT1 = pHeadE;while (pT1->next->next != NULL){pT2 = pT1;pT1 = pT1->next;if (pT1->x > pT1->next->x){pT2->next = pT1->next;pT1->next = pT1->next->next;pT2->next->next = pT1;pT1 = pT2->next;}else{if (pT1->x == pT1->next->x){if (pT1->k > pT1->next->k){pT2->next = pT1->next;pT1->next = pT1->next->next;pT2->next->next = pT1;pT1 = pT2->next;}}}}}
}void CFill::FillPolygon(CDC *pDC)//填充多边形
{CAET *pT1 = NULL, *pT2 = NULL;pHeadE = NULL;for (pCurrentB = pHeadB; pCurrentB != NULL; pCurrentB = pCurrentB->next){for (pCurrentE = pCurrentB->pET; pCurrentE != NULL; pCurrentE = pCurrentE->next){pEdge = new CAET;pEdge->x = pCurrentE->x;pEdge->yMax = pCurrentE->yMax;pEdge->k = pCurrentE->k;pEdge->next = NULL;AddEt(pEdge);}EtOrder();pT1 = pHeadE;if (pT1 == NULL){return;}while (pCurrentB->ScanLine >= pT1->yMax)//下闭上开{CAET * pAETTEmp = pT1;pT1 = pT1->next;delete pAETTEmp;pHeadE = pT1;if (pHeadE == NULL)return;}if (pT1->next != NULL){pT2 = pT1;pT1 = pT2->next;}while (pT1 != NULL){if (pCurrentB->ScanLine >= pT1->yMax)//下闭上开{CAET* pAETTemp = pT1;pT2->next = pT1->next;pT1 = pT2->next;delete pAETTemp;}else{pT2 = pT1;pT1 = pT2->next;}}BOOL In = FALSE;//设置一个 BOOL 变量 In,初始值为假int xb, xe;//扫描线的起点和终点for (pT1 = pHeadE; pT1 != NULL; pT1 = pT1->next)//填充扫描线和多边形相交的区间{if (FALSE == In){xb = (int)pT1->x;In = TRUE;//每访问一个结点,把 In 值取反一次}else//如果 In 值为真,则填充从当前结点的 x 值开始到下一结点的 x 值结束的区间{xe = (int)pT1->x;for (int x = xb; x <= xe; x++)pDC->SetPixel(x, pCurrentB->ScanLine, RGB(0, 0, 255));//蓝色填充In = FALSE;}}for (pT1 = pHeadE; pT1 != NULL; pT1 = pT1->next)//边连贯性{pT1->x = pT1->x + pT1->k;//x=x+1/k           }}}void CFill::ClearMemory()//安全删除所有桶和桶上面的边
{DeleteAETChain(pHeadE);CBucket *pBucket=pHeadB;while (pBucket != NULL)// 针对每一个桶{CBucket * pBucketTemp=pBucket->next;DeleteAETChain(pBucket->pET);delete pBucket;pBucket=pBucketTemp;}pHeadB=NULL;pHeadE=NULL;
}
直线函数
void CLine::LineTo(CDC *pDC,CPoint p1)
{P1=p1;CPoint p,t;COLORREF clr=RGB(0,0,0);//像素点颜色if(abs(P0.x-P1.x)<0)//绘制垂线{if(P0.y>P1.y)//交换顶点,使得起始点低于终点顶点{t=P0;P0=P1;P1=t;}for(p=P0;p.y<P1.y;p.y++){pDC->SetPixel(p,clr); }}else{double k,d;k=(double)(P1.y-P0.y)/(double)(P1.x-P0.x);if(k>1.0)//绘制k>1{if(P0.y>P1.y){t=P0;P0=P1;P1=t;}d=1-0.5*k;for(p=P0;p.y<P1.y;p.y++){pDC->SetPixel(p,clr);if(d>=0){p.x++;d+=1-k;}else d+=1;       }}if(0.0<=k && k<=1.0)//绘制0<=k<=1{if(P0.x>P1.x){t=P0;P0=P1;P1=t;}d=0.5-k; for(p=P0;p.x<P1.x;p.x++){pDC->SetPixel(p,clr);if(d<0){p.y++;d+=1-k;}else d-=k;       }}      if(k>=-1.0 && k<0.0)//绘制-1<=k<0{if(P0.x>P1.x){t=P0;P0=P1;P1=t;}d=-0.5-k;for(p=P0;p.x<P1.x;p.x++){pDC->SetPixel(p,clr);if(d>0){p.y--;d-=1+k;}else d-=k;      }}if(k<-1.0)//绘制k<-1 {if(P0.y<P1.y){t=P0;P0=P1;P1=t;}d=-1-0.5*k;for(p=P0;p.y>P1.y;p.y--){pDC->SetPixel(p,clr);if(d<0){p.x++;d-=1+k;}else d-=1;           }}}P0=p1;
}
消息处理函数
void CTestView::OnMouseMove(UINT nFlags, CPoint point)
{// TODO: Add your message handler code here and/or call defaultif(m_Arrow)::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));else::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS));CString strx,stry;//状态栏显示鼠标位置CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;//要求包含MainFrm.h头文件CStatusBar *pStatus=&pFrame->m_wndStatusBar;//需要将m_wndStatusBar属性修改为公有if(pStatus){strx.Format("x=%d",point.x);stry.Format("y=%d",point.y);CDC *pDC=GetDC();CSize sizex=pDC->GetTextExtent(strx);CSize sizey=pDC->GetTextExtent(stry);pStatus->SetPaneInfo(1,ID_INDICATOR_X,SBPS_NORMAL,sizex.cx);//改变状态栏风格pStatus->SetPaneText(1,strx);pStatus->SetPaneInfo(2,ID_INDICATOR_Y,SBPS_NORMAL,sizey.cx);//改变状态栏风格pStatus->SetPaneText(2,stry);ReleaseDC(pDC);}int index=m_ptrarray.GetSize()-1;if(m_LBDown){if(!m_IsInsert)//如果是第一次移动,则插入新的顶点{CPointArray *pPointArray=new CPointArray(point);m_ptrarray.Add(pPointArray);m_IsInsert=TRUE;}else//修改上次插入的顶点数据{           ((CPointArray *)m_ptrarray.GetAt(index))->pt=point;         }       }if(m_LBDown){if(MK_SHIFT==nFlags)//约束:测试按下了Shift键{CPoint* pt1=&(((CPointArray *)m_ptrarray.GetAt(index))->pt);CPoint* pt2=&(((CPointArray *)m_ptrarray.GetAt(index-1))->pt);if(abs(pt1->x-pt2->x)>=abs(pt1->y-pt2->y)){pt1->y=pt2->y;//x方向的垂线}else{pt1->x=pt2->x;//y方向的垂线}}}if(index>3){CPoint pt=((CPointArray*)m_ptrarray.GetAt(0))->pt;if((abs(point.x-pt.x)<=5) && (abs(point.y-pt.y)<=5))//引力域:边长为10的正方形{((CPointArray *)m_ptrarray.GetAt(index))->pt=pt;//修改数据m_Arrow=TRUE;m_LBDown=FALSE;m_MState=TRUE;m_Flag=FALSE;}}Invalidate(FALSE);CView::OnMouseMove(nFlags, point);
}
双缓冲技术函数
void CTestView::DoubleBuffer()//双缓冲
{CRect rect;//定义客户区GetClientRect(&rect);//获得客户区的大小CDC* pDC=GetDC();CDC MemDC;//内存设备上下文CBitmap NewBitmap,*pOldBitmap;//内存中承载图像的临时位图MemDC.CreateCompatibleDC(pDC);//建立与屏幕pDC兼容的MemDC NewBitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//创建兼容位图 pOldBitmap=MemDC.SelectObject(&NewBitmap); //将兼容位图选入MemDC MemDC.FillSolidRect(rect,pDC->GetBkColor());//按原来背景填充客户区,否则是黑色 DrawObject(&MemDC);pDC->BitBlt(0,0,rect.Width(),rect.Height(),&MemDC,0,0,SRCCOPY);//将内存位图拷贝到屏幕MemDC.SelectObject(pOldBitmap);//恢复位图NewBitmap.DeleteObject();//删除位图MemDC.DeleteDC();//删除MemDCReleaseDC(pDC);//释放DC
}绘制多边形
void CTestView::DrawObject(CDC *pDC)//绘制多边形
{int index=m_ptrarray.GetSize();CLine *line=new CLine;if(index){      line->MoveTo(pDC,((CPointArray*)m_ptrarray.GetAt(0))->pt);for(int i=1;i<index;i++){line->LineTo(pDC,((CPointArray*)m_ptrarray.GetAt(i))->pt);     }if(FALSE==m_Flag)//线段闭合,填充图形{FillPolygon(pDC);    }}delete line;
}
多边形填充void CTestView::FillPolygon(CDC *pDC)
{// TODO: Add your command handler code hereint size=m_ptrarray.GetSize();CPoint *p=new CPoint[size];//分配内存空间for(int i=0;i<size;i++)//拷贝数据到一个静态数组{p[i]=((CPointArray *)m_ptrarray.GetAt(i))->pt;}CFill *fill=new CFill;//动态分配内存fill->SetPoint(p,size);//设置多边形顶点数组fill->CreateBucket();//建立桶表fill->CreateEdge();//建立边表fill->FillPolygon(pDC);//填充多边形delete fill;//释放内存delete []p;
}

计算机图形学实验二交互式绘制多边形相关推荐

  1. 计算机图形学实验二 《绘制任意斜率的直线》

    计算机图形学实验二 <绘制任意斜率的直线> 视频讲解地址 一.Bresenham算法 用视频讲会好点我之前也录过相关视频可以先凑合看 二.设计CLine类 之前也说了C++一个类是由源文件 ...

  2. 计算机图形学 | 实验四:绘制一个球体

    计算机图形学 | 实验四:绘制一个球体 计算机图形学 | 实验四:绘制一个球体 封装Shader 为什么要封装Shader 如何使用 绘制球模型 球面顶点遍历 构造三角形图元 开启线框模式 开启面剔除 ...

  3. 计算机图形学 | 实验三:绘制一个四边形

    计算机图形学 | 实验三:绘制一个四边形 计算机图形学 | 实验三:绘制一个四边形 初始化 顶点输入 数据处理 VAO.VBO 顶点属性 顶点着色器和片段着色器 渲染 EBO 完整代码 华中科技大学& ...

  4. 计算机图形学实验——二维卡通人物交互

    计算机图形学实验1.2卡通人物交互 OpenGL卡通人物交互 基础"图元"绘制 OpenGL拾取物体 反走样 略提反走样问题 OpenGL实现二维反走样 放缩.旋转和拖动 小结 O ...

  5. 实验三 交互式绘制多边形

    visual studio2019实现交互式绘制多边形 这个实验······在网上找不到.孔令德的实验代码下载下来有密码,暴力破解没希望,只能自己写了. 这个和实验二是紧密相连的. 实验实现的目标 在 ...

  6. 计算机图形学 实验二 三维模型读取与控制【OpenGL】

    文章目录 实验2.1 OpenGL的控制与交互方式 一. 实验目的 二. 理论背景 三. 实验内容 1. 创建基本工程项目 2. 在子窗口中绘制图形 3. 在子窗口中通过键盘事件更换椭圆形状颜色 4. ...

  7. 计算机图形学——实验二 几何图形变换实验

    实验二 几何图形变换实验 一.实验目的和要求 进一步掌握二维.三维变换的数学知识.变换原理.变换种类.变换方法: 利用OpenGL实现二维.三维图形变换,在屏幕上显示变换过程或变换结果: 掌握Open ...

  8. 计算机图形学实验二图形的绘制及裁剪

    图形的绘制及裁剪 1.六芒星的绘制 2.用扫描线填充算法(或种子填充算法) 3.实现不同属性的点和线.字符显示.反走样技术 4.实现线段裁剪的Cohen-Sutherland算法或Liang-Bars ...

  9. 【计算机图形学实验二——实现圆的中点算法、椭圆的中点算法】

    一.实验内容.目的.要求 1.实现圆的中点算法.椭圆的中点算法 2.掌握相关算法的原理及实现 3.交互方便.直观 可处理圆心不在原点的情形(标注出绘图窗口的坐标系) 优化算法:只含整数运算.加减法运算 ...

最新文章

  1. 一文读懂 RoIPooling、RoIAlign 和 RoIWarp
  2. centos7 选定默认启动内核,及删除无用内核
  3. easyexcel模板循环模板怎么循环_雅思大作文怎么熟练套模板
  4. ffmpeg-URL(转)
  5. 等额本息和等额本金,哪个还款方式更划算?
  6. 关于二叉堆(优先队列)的其他操作及其应用
  7. hdu5693 D gamehdu 5712 D++ game
  8. oracle 存储过程设置回滚点,(转)oracle 存储过程事宜使用断点回滚 -savepoint
  9. 微服务设计模式(下)
  10. ASP.NET Core和Angular 2双剑合璧
  11. C语言中的字符串函数
  12. 几个用于更精细判断敛散性的级数
  13. particle filtering---粒子滤波(讲的很通俗易懂)
  14. ios8之后的UIAlertController
  15. Unity基础案例讲解:创建小型太空射击游戏(二)
  16. The database could not be exclusively locked to perform the operation(SQL Server 5030错误解决办法)(转)...
  17. 软件缺陷静态分析CodeSonar
  18. linux 安装谷歌浏览器--Google chrome
  19. F005MyBatis学习笔记-MyBatis的多表关联查询
  20. win10计算机联接多个网络,如何设置win10电脑连接两个显示器?

热门文章

  1. 使用XUL开发跨平台桌面应用
  2. Maven第6篇:生命周期 插件
  3. java color类红黄蓝_一种具有红黄蓝母体结构多发色体系的活性染料及其制备方法和应用与流程...
  4. Python基础学习第十天
  5. python库吐血整理
  6. 芯片设计中的时钟与约束
  7. dna计算机开发,科学家正在开发DNA电脑
  8. html换行不出现横线,wps为什么在换行空格下划线不显示
  9. java——char类型以及Character
  10. wps如何快速小计求和