1. 实验内容
1.绘制金刚石图案
金刚石图案的成图规则是:把一个圆周等分成n nn份,然后每两点之间连线。当n nn取奇数时,该图案可一笔连续绘成,即用MoveTo函数确定一个当前点,然后连续用LineTo函数连点成线。
请设计连线规则并编程实现。
2.绘制魔术三角形
绘制下图所示的魔术三角形图案 ,采用三种可明显区分的颜色填充。
3.绘制递归圆
应用递归的方法绘制如下所示的图案。
2. 实验环境
Visual Studio 2019
图形学实验程序框架
Windows11系统
3. 问题分析
3.1问题1
3.2问题2
对于问题2,可以预先处理出12个点位的坐标。点位标号如下:
初始时,设0点的坐标为( 400 , 800 ) (400,800)(400,800),长边长度500,短边长度100.则各个点坐标如下表所示:
点位编号 | x xx坐标 | y yy坐标 | 所属区域 | 点位编号 | x xx坐标 | y yy坐标 | 所属区域 |
0 | 400 | 800 | 红区 | 6 | 750 | 713 | 红区、紫区 |
1 | 900 | 800 | 红区、黄区 | 7 | 700 | 454 | 红区、黄区 |
2 | 950 | 713 | 黄区 | 8 | 500 | 627 | 紫区、黄区 |
3 | 700 | 281 | 黄区、紫区 | 9 | 700 | 627 | 紫区 |
4 | 600 | 281 | 紫区 | 10 | 650 | 541 | 红区 |
5 | 350 | 713 | 红区、紫区 | 11 | 600 | 627 | 黄区 |
创建三个多边形,每个多边形表示魔术三角形的一个同色区域。多边形1按顺序将点0、点1、点7、点10、点6、点5连接。多边形2按顺序将点1、点2、点3、点8、点7、点11连接。多边形3将点3、点4、点5、点6、点9、点8连接。
为了实现变色的效果,每次都需要将每一个多边形重新赋值颜色并重新上色。在选取颜色时,选择使用随机的颜色。
3.3问题3
4. 算法设计
4.1问题1
4.2问题2
正如章节3.2分析,算法的流程如下:
1.计算各个点位坐标,确定魔术三角形每个部分用到了哪些模块。
2.每隔一段时间给三个部分的颜色重新绘制上色,颜色为随机颜色。这一过程不断地循环进行。
4.3问题3
正如3.3所分析,算法的流程如下:
先创建pDC对象和画笔,然后递归地调用函数。在函数体内,先判断递归深度。若递归深度<=0,则退出,返回上一层。若递归深度>0,则找到递归圆圆心的坐标与半径,根据当前圆心与半径画出当前圆。再找到当前圆附属的8个小圆的坐标,递归地画小圆。小圆的半径为大圆的1/4,小圆的递归层数为大圆的递归层数-1。在画完所有圆后,算法结束。
5. 源代码
5.1金刚石
void CDiamondView::DrawDiamond(int nVertex, int radius,int millisecond){ InvalidateRect(NULL);//强制清屏 UpdateWindow(); struct Point { double x, y; }; Point* pnt = new Point[nVertex]; const double PI = 3.14159265; CDC* pDC = GetDC(); CRect rgn; GetClientRect(&rgn); CBrush newBrush; newBrush.CreateSolidBrush(RGB(255, 255, 255)); pDC->FillRect(&rgn, &newBrush); CPen redPen(PS_SOLID, 1, RGB(0, 0, 255)); CPen* OldPen = pDC->SelectObject(&redPen); double theta = 2 * PI / nVertex; for (int i = 0; i < nVertex; i++) { pnt[i].x = radius * cos(i * theta) + MaxX() / 2; pnt[i].y = radius * sin(i * theta) + MaxY() / 2; } int pos = 0; if (nVertex % 2 == 1) { for (int loop = 0; loop < nVertex; loop++) { for (int interv = 0; interv <= nVertex / 2 - 1; interv++) {//间隔的点的数量 int nextpos = (pos + 1 + interv) % nVertex; pDC->MoveTo(round(pnt[pos].x), round(pnt[pos].y)); pDC->LineTo(round(pnt[nextpos].x), round(pnt[nextpos].y)); pos = nextpos; Sleep(millisecond); } } } else { for (int i = 0; i < nVertex-1; i++) { for (int j = i + 1; j < nVertex; j++) { pDC->MoveTo(round(pnt[i].x), round(pnt[i].y)); pDC->LineTo(round(pnt[j].x), round(pnt[j].y)); Sleep(millisecond); } } } pDC->SelectObject(OldPen); }
5.2魔术三角形
//绘制魔术三角 void CDiamondView::DrawTriangle(){ InvalidateRect(NULL);//强制清屏 UpdateWindow(); CDC* pDC = GetDC(); CRect rect; GetClientRect(&rect); CBrush br(RGB(0, 0, 0)); pDC->FillRect(&rect, &br);//设置背景为黑 CBrush newBrush, * oldBrush; POINT vertex[12] = { {400,800}, {900,800}, {950,713}, {700,281}, {600,281}, {350,713}, {750,713}, {700,454}, {500,627}, {700,627}, {650,541}, {600,627} }; POINT vertex1[6] = { vertex[0],vertex[1],vertex[7],vertex[10],vertex[6],vertex[5] }; POINT vertex2[6] = { vertex[1],vertex[2],vertex[3],vertex[8],vertex[11],vertex[7] }; POINT vertex3[6] = { vertex[3],vertex[4],vertex[5],vertex[6],vertex[9],vertex[8] }; int t = 600; while (t--) { //第一个填充 { CDC* pDC = GetDC(); CBrush newBrush, * oldBrush; newBrush.CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)); oldBrush = pDC->SelectObject(&newBrush); pDC->SetPolyFillMode(WINDING); pDC->Polygon(vertex1, 6); pDC->SelectObject(oldBrush); newBrush.DeleteObject(); } //第二个填充 { CDC* pDC = GetDC(); CBrush newBrush, * oldBrush; newBrush.CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)); oldBrush = pDC->SelectObject(&newBrush); pDC->SetPolyFillMode(WINDING); pDC->Polygon(vertex2, 6); pDC->SelectObject(oldBrush); newBrush.DeleteObject(); } //第三个填充 { CDC* pDC = GetDC(); CBrush newBrush, * oldBrush; newBrush.CreateSolidBrush(RGB(rand() % 256, rand() % 256, rand() % 256)); oldBrush = pDC->SelectObject(&newBrush); pDC->SetPolyFillMode(WINDING); pDC->Polygon(vertex3, 6); pDC->SelectObject(oldBrush); newBrush.DeleteObject(); } Sleep(100); } }
5.3 递归圆
//绘制递归圆 //nDepth:递归深度 void circle(int n, double r, double x0, double y0, CDC* pDC) { if (n == 0) return; const double PI = 3.14159; double theta = 2*PI/8; double x, y; for (int i = 0; i < 8; i++) { x = 2 * r * cos(i * theta) + x0; y = 2 * r * sin(i * theta) + y0; CPen newPen, * oldPen; newPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); oldPen = pDC->SelectObject(&newPen); CRect rect1(x - 0.25 * r, y - 0.25 * r, x + 0.25 * r, y + 0.25 * r); circle(n - 1, 0.25 * r, x, y, pDC); pDC->Ellipse(&rect1); } } //绘制递归圆主函数 //nDepth:递归深度 void CDiamondView::DrawRecursionCircle(int nDepth) { InvalidateRect(NULL);//强制清屏 UpdateWindow(); if (nDepth == 0) return; int x0 = MaxX()/2, y0 =MaxY()/2;//圆心 int r = 160;//半径 const double PI = 3.14159; CDC* pDC = GetDC(); CPen newPen, * oldPen; newPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); oldPen = pDC->SelectObject(&newPen); CRect rect(x0 - r, y0 - r, x0 + r, y0 + r); pDC->Ellipse(&rect); circle(nDepth, r, x0, y0, pDC); }
6.程序运行结果
正确地绘制出了金刚石图案,且实现了一笔画的效果。在此图中,含有21个点,半径为150,图案中心在屏幕的中心。
正确地绘制出了魔术三角形。在每一秒,三角形三个部分的颜色都会随机变换一次。
正确地绘制出了递归深度为3的递归圆。