CodeBus

分享代码,一起进步~

万花尺(又叫万花规)模拟程序

万花尺相信很多人小时候都玩过吧,将一个满是齿牙的小圆套在一个大圆里面,用笔放进小圆的洞里,然后转圈圈画,就可以画出很多美丽有趣的图案。这个程序就是万花尺的模拟程序。 下面是几组数据和执行效果的抓图: //////////////////////////////////////////// // 程序名称:万花尺(又名万花规)模拟程序 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 程序编写:yangw80 <yw80@qq.com> // 最后更新:2011-2-26 // #include <graphics.h> #include <stdio.h> #include <conio.h> #include <math.h> #define PI 3.1415926536 void main() { int R; // 外部静圆半径 int r; // 内部动圆半径 int dr; // 内部动圆上的作图点到圆心的距离 begin: // 获取用户输入 printf("请输入外部静圆半径(小于 240):"); scanf("%d",&R); printf("请输入内部动圆半径(小于静圆半径):"); scanf("%d",&r); printf("请输入内部动圆上的作图点到圆心的距离(小于动圆半径):"); scanf("%d",&dr); system("cls"); // 求执行次数(最小公倍数/静圆半径) int m = R, n = r; int tmp; while(n != 0) { tmp = m % n; m = n; n = tmp; } double maxdegree = r / m * 2 * PI; // 初始化 initgraph(640, 480); // 创建绘图窗口 setorigin(320, 240); // 设定圆心坐标 // 绘图 int x, y; for(double degree = 0; degree < maxdegree; degree += 0.01) { x = (int)(dr * cos(degree*(double(R) / r - 1)) + (R - r) * cos(degree)); y = (int)(dr * sin(degree*(double(R) / r - 1)) - (R - r) * sin(degree)); putpixel(x, y, GREEN); } outtextxy(-320, 220, _T("绘制完毕,按 ESC 退出,按其他键重新开始")); // 重新开始,或退出程序 char c = getch(); closegraph(); if (c != 27) goto begin; }

模仿 Windows 屏保“变幻线 (Mystify)” VC 源代码

该程序模仿的 Windows 经典屏幕保护程序“变幻线”,并使用了面向对象技术编写,初学面向对象时可以作为参考。 程序中,由多个连续的节点构成一个顶点对象,由四个顶点构成一个多边形对象,主程序有两个多边形在各自运动。更详细的,请参考源代码。 源代码如下: // 程序名称:变幻线 // 编译环境:VC6 + EasyX v20100825 // 编 写 者:zh@easyx.cn // 编写日期:2010-11-25 // #include <graphics.h> #include <conio.h> #include <time.h> #define WIDTH 640 #define HEIGHT 480 #define MAXSTEP 9 // 节点(多个连续的节点环,组成一个顶点) struct POINTNODE { POINT pos; // 位置 POINTNODE* next; // 指向下一个节点的指针 }; // 顶点(四个顶点,组成一个多边形) class VERTEX { public: POINTNODE* m_head; // 头结点的指针 POINT m_step; // 移动步长 // 构造函数 VERTEX() { m_head = NULL; } // 析构函数 ~VERTEX() { if (m_head != NULL) delete[] m_head; } // 初始化环链 void Init(int count) { // 初始化前进方向 m_step.x = ((rand() % 2) * 2 - 1) * (rand() % MAXSTEP + 1); m_step.y = ((rand() % 2) * 2 - 1) * (rand() % MAXSTEP + 1); // 初始化节点环 m_head = new POINTNODE[count]; m_head[0].pos.x = rand() % WIDTH; m_head[0].pos.y = rand() % HEIGHT; m_head[0].next = &m_head[count - 1]; for(int i=1; i<=count; i++) { m_head[i].pos.x = m_head[i-1].pos.x - m_step.x; m_head[i].pos.y = m_head[i-1].pos.y - m_step.y; m_head[i].next = &m_head[i-1]; } } // 获取头部节点坐标 POINT GetHead() { return m_head->pos; } // 获取尾部节点坐标 POINT GetTail() { return m_head->next->pos; } // 移动顶点 void Move() { m_head->next->pos.x = m_head->pos.x + m_step.x; m_head->next->pos.y = m_head->pos.y + m_step.y; m_head = m_head->next; // 判断顶点是否越界 if (m_head->pos.x < 0) { m_head->pos.x = -m_head->pos.x; m_step.x = rand() % MAXSTEP + 1; } else if (m_head->pos.x >= WIDTH) { m_head->pos.x -= m_head->pos.x - WIDTH + 1; m_step.x = -rand() % MAXSTEP - 1; } if (m_head->pos.y < 0) { m_head->pos.y = -m_head->pos.y; m_step.y = rand() % MAXSTEP + 1; } else if (m_head->pos.y >= HEIGHT) { m_head->pos.y -= m_head->pos.y - HEIGHT + 1; m_step.y = -rand() % MAXSTEP - 1; } } }; // 多边形 class POLYGON { private: COLORREF m_color; // 多边形的颜色 VERTEX m_vertex[4]; // 构成多边形的四个顶点 public: // 构造函数 POLYGON(int lines) { // 初始化颜色 m_color = HSLtoRGB(float(rand()%360), 1.0, 0.5); // 初始化四个顶点 for (int i=0; i<4; i++) m_vertex[i].Init(lines); } // 移动多边形 void Move() { int i; // 擦掉多边形的尾部 setcolor(BLACK); moveto(m_vertex[3].GetTail().x, m_vertex[3].GetTail().y); for(i=0; i<4; i++) lineto(m_vertex[i].GetTail().x, m_vertex[i].GetTail().y); // 移动每个顶点 for (i=0; i<4; i++) m_vertex[i].Move(); // 画多边形的头部 setcolor(m_color); moveto(m_vertex[3].GetHead().x, m_vertex[3].GetHead().y); for(i=0; i<4; i++) lineto(m_vertex[i].GetHead().x, m_vertex[i].GetHead().y); // 有 1% 的概率更换颜色 if (rand()%100 == 0) m_color = HSLtoRGB(float(rand()%360), 1.0, 0.5); } }; // 主函数 void main() { // 初始化绘图窗口 initgraph(WIDTH, HEIGHT); // 初始化随机种子 srand((unsigned)time(NULL)); // 定义两个多边形对象,分别有 7 层线和 12 层线 POLYGON s1(7), s2(12); // 移动多边形,按任意键退出 while(!kbhit()) { s1.Move(); s2.Move(); Sleep(20); } // 关闭绘图窗口 closegraph(); }

钟表模拟程序(表针形式)

这是个简单的表针式时钟模拟程序。运行效果如下: 在源代码的基础上,可以通过 API 函数 mciSendString 轻松加上滴答声音,我就不再多话了。 完整的 VC 源代码如下: //////////////////////////////////////////// // 程序名称:钟表模拟程序(表针形式) // 编译环境:Visual C++ 6.0,EasyX 2013白露版 // 程序编写:BestAns <BestAns@qq.com> // 最后更新:2010-10-30 // #include <graphics.h> #include <conio.h> #include <math.h> #define PI 3.1415926536 void DrawHand(int hour, int minute, int second) { double a_hour, a_min, a_sec; // 时、分、秒针的弧度值 int x_hour, y_hour, x_min, y_min, x_sec, y_sec; // 时、分、秒针的末端位置 // 计算时、分、秒针的弧度值 a_sec = second * 2 * PI / 60; a_min = minute * 2 * PI / 60 + a_sec / 60; a_hour= hour * 2 * PI / 12 + a_min / 12; // 计算时、分、秒针的末端位置 x_sec = int(120 * sin(a_sec)); y_sec = int(120 * cos(a_sec)); x_min = int(100 * sin(a_min)); y_min = int(100 * cos(a_min)); x_hour= int(70 * sin(a_hour)); y_hour= int(70 * cos(a_hour)); // 画时针 setlinestyle(PS_SOLID, 10); setcolor(WHITE); line(320 + x_hour, 240 - y_hour, 320 - x_hour / 7, 240 + y_hour / 7); // 画分针 setlinestyle(PS_SOLID, 6); setcolor(LIGHTGRAY); line(320 + x_min, 240 - y_min, 320 - x_min / 5, 240 + y_min / 5); // 画秒针 setlinestyle(PS_SOLID, 2); setcolor(RED); line(320 + x_sec, 240 - y_sec, 320 - x_sec / 3, 240 + y_sec / 3); } void DrawDial() { // 绘制一个简单的表盘 circle(320, 240, 2); circle(320, 240, 60); circle(320, 240, 160); outtextxy(296, 310, "BestAns"); // 绘制刻度 int x, y; for (int i=0; i<60; i++) { x = 320 + int(145 * sin(PI * 2 * i / 60)); y = 240 + int(145 * cos(PI * 2 * i / 60)); if (i % 15 == 0) bar(x - 5, y - 5, x + 5, y + 5); else if (i % 5 == 0) circle(x, y, 3); else putpixel(x, y, WHITE); } } void main() { initgraph(640, 480); // 初始化 640 x 480 的绘图窗口 DrawDial(); // 绘制表盘 setwritemode(R2_XORPEN); // 设置 XOR 绘图模式 // 绘制表针 SYSTEMTIME ti; // 定义变量保存当前时间 while(!kbhit()) // 按任意键退出钟表程序 { GetLocalTime(&ti); // 获取当前时间 DrawHand(ti.wHour, ti.wMinute, ti.wSecond); // 画表针 Sleep(1000); // 延时 1 秒 DrawHand(ti.wHour, ti.wMinute, ti.wSecond); // 擦表针(擦表针和画表针的过程是一样的) } closegraph(); // 关闭绘图窗口 }

用多个圆形组合形成的心形图案

这个程序在很多书上都有写过,以下是运行效果截图: 完整的 VC 源代码如下: // 程序名称:圆形组合而成的心形图案 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 最后更新:2010-10-28 // #include <graphics.h> #include <conio.h> #include <math.h> #define PI 3.1415926536 void main() { int x, y, y1, r = 50, r1; // 初始化图形模式 initgraph(640, 480); // 设置原点为屏幕中央 setorigin(320, 240); // 设置绘图颜色为红色 setcolor(RED); // 用数学方法,用多个圆形组合形成心形图案 y1 = 240 - r; for(double a = 0; a < 2 * PI; a += PI / 18) { x = (int)(r * cos(a)); y = (int)(r * sin(a)); r1 = (int)(sqrt((double)(x * x + (y + r) * (y + r)))); circle(x, y, r1); } getch(); closegraph(); }

三维旋转球 源码+注释+简单讲解

关于三维的东西远不止这么点内容,也不是我几句话可以讲完的,需要大家扎扎实实的看图形学。不能好高骛远,要扎实、系统的学习。还要注意相关数学知识的学习。 过去我写了一些简单教程(以后还会写),但是这篇文章不算教程。我写这篇文章,是想表达我前面的看法,希望大家能认识到。 我在这篇文章里尽量不用图形学的术语,也不做优化、不加额外功能,只希望大家能从道理上看的清楚明白。 简单讲一下: 关于一个点,一定要有一个三维坐标,程序中的结构体 POINT3D 就是; 点的初始化由函数 InitPoint() 实现,该函数产生了 n 个半径为 1 的点; 点的运动,是在三维坐标内运动的,包括平移、缩放、旋转等。这个程序只涉及到了旋转,定义了三个方法:RotateX()、RotateY()、RotateZ(),分别实现绕三个轴旋转; 最后需要将三维世界呈现出来,这里用到一个术语:投影,就是将三维的画面投影到二维上。投影有多种方法,这个球体用一点透视就可以。还需要一个“观察点”,程序中用 viewZ 定义,具体的观察点坐标是:(0, 0, viewZ)。函数 Projection() 负责实现这些功能。 下面是完整的 VC 源代码: // 程序名称:三维旋转球 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 最后更新:2010-9-14 // #include <graphics.h> #include <time.h> #include <math.h> #include <conio.h> #define MAXPOINT 2000 #define PI 3.1415926536 // 定义三维点 struct POINT3D { double x; double y; double z; }; POINT3D p3d[MAXPOINT]; // 所有的三维点 double viewZ = 3; // 视点 z 轴坐标 // 初始化三维点 void InitPoint() { // 产生随机种子 srand(time(NULL)); // 产生球体表面的随机点(根据球体面积与其外切圆柱面积的关系) double rxy, a; for(int i=0; i<MAXPOINT; i++) { p3d[i].z = 2.0 * rand() / RAND_MAX - 1; // 求随机 z 坐标 rxy = sqrt(1 - p3d[i].z * p3d[i].z); // 计算三维矢量在 xoy 平面的投影长度 a = 2 * PI * rand() / RAND_MAX; // 产生随机角度 p3d[i].x = cos(a) * rxy; p3d[i].y = sin(a) * rxy; } } // 使三维点按 x 轴旋转指定角度 void RotateX(POINT3D &p, double angle) { double y = p.y; p.y = p.y * cos(angle) + p.z * sin(-angle); p.z = y * sin(angle) + p.z * cos(angle); } // 使三维点按 y 轴旋转指定角度 void RotateY(POINT3D &p, double angle) { double x = p.x; p.x = p.x * cos(angle) + p.z * sin(-angle); p.z = x * sin(angle) + p.z * cos(angle); } // 使三维点按 z 轴旋转指定角度 void RotateZ(POINT3D &p, double angle) { double x = p.x; p.x = p.x * cos(angle) + p.y * sin(-angle); p.y = x * sin(angle) + p.y * cos(angle); } // 将三维点投影到二维屏幕上(单点透视) POINT Projection(POINT3D p) { POINT p2d; p2d.x = (int)(p.x * ( viewZ / (viewZ - p.z) ) * 200 + 0.5) + 320; p2d.y = (int)(p.y * ( viewZ / (viewZ - p.z) ) * 200 + 0.5) + 240; return p2d; } void main() { initgraph(640, 480); InitPoint(); BeginBatchDraw(); int c; POINT p2d; while(!kbhit()) { cleardevice(); // 清除屏幕 for(int i=0; i<MAXPOINT; i++) { // 使该点围绕三个坐标轴做旋转运动 RotateX(p3d[i], PI/180); RotateY(p3d[i], PI/170); RotateZ(p3d[i], PI/160); // 根据点的深度,产生相应灰度的颜色 c = (int)(p3d[i].z * 100) + 155; // 投影该点到屏幕上 p2d = Projection(p3d[i]); // 画点 putpixel(p2d.x, p2d.y, RGB(c,c,c)); } FlushBatchDraw(); Sleep(10); // 延时 10 毫秒 } EndBatchDraw(); closegraph(); } 剩下的留待大家自己拓展吧,比如加上缩放、键盘控制、改变点的颜色、优化性能等。只要原理搞明白,这些都不难。

彩虹

该程序是 EasyX 帮助中带的范例程序。 执行效果如下图: 完整源代码如下: // 程序名称:彩虹 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 最后更新:2010-8-25 // #include <graphics.h> #include <conio.h> void main() { float H, S, L; initgraph(640, 480); // 画渐变的天空(通过亮度逐渐增加) H = 190; // 色相 S = 1; // 饱和度 L = 0.7f; // 亮度 for(int y = 0; y < 480; y++) { L += 0.0005f; setcolor( HSLtoRGB(H, S, L) ); line(0, y, 639, y); } // 画彩虹(通过色相逐渐增加) H = 0; S = 1; L = 0.5f; setlinestyle(PS_SOLID, NULL, 2); // 设置线宽为 2 for(int r = 400; r > 344; r--) { H += 5; setcolor( HSLtoRGB(H, S, L) ); circle(500, 480, r); } getch(); closegraph(); }

鼠标操作演示

该程序是 EasyX 帮助中的范例程序。 实现的功能是:当移动鼠标时,在鼠标轨迹上画红点,点左键,画一个小方块;按 Ctrl 再点左键,画一个大方块;按鼠标右键退出。 // 程序名称:鼠标操作演示 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 最后更新:2009-11-23 // #include <graphics.h> #include <conio.h> void main() { // 初始化图形窗口 initgraph(640, 480); MOUSEMSG m; // 定义鼠标消息 while(true) { // 获取一条鼠标消息 m = GetMouseMsg(); switch(m.uMsg) { case WM_MOUSEMOVE: // 鼠标移动的时候画红色的小点 putpixel(m.x, m.y, RED); break; case WM_LBUTTONDOWN: // 如果点左键的同时按下了 Ctrl 键 if (m.mkCtrl) // 画一个大方块 rectangle(m.x-10, m.y-10, m.x+10, m.y+10); else // 画一个小方块 rectangle(m.x-5, m.y-5, m.x+5, m.y+5); break; case WM_RBUTTONUP: return; // 按鼠标右键退出程序 } } // 关闭图形窗口 closegraph(); }

星空

该程序是 EasyX 帮助中的范例程序。 模拟了星空效果。 完整的源代码如下: // 程序名称:星空 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 最后更新:2009-2-22 // #include <graphics.h> #include <time.h> #include <conio.h> #define MAXSTAR 200 // 星星总数 struct STAR { double x; int y; double step; int color; }; STAR star[MAXSTAR]; // 初始化星星 void InitStar(int i) { star[i].x = 0; star[i].y = rand() % 480; star[i].step = (rand() % 5000) / 1000.0 + 1; star[i].color = (int)(star[i].step * 255 / 6.0 + 0.5); // 速度越快,颜色越亮 star[i].color = RGB(star[i].color, star[i].color, star[i].color); } // 移动星星 void MoveStar(int i) { // 擦掉原来的星星 putpixel((int)star[i].x, star[i].y, 0); // 计算新位置 star[i].x += star[i].step; if (star[i].x > 640) InitStar(i); // 画新星星 putpixel((int)star[i].x, star[i].y, star[i].color); } // 主函数 void main() { srand((unsigned)time(NULL)); // 随机种子 initgraph(640, 480); // 打开图形窗口 // 初始化所有星星 for(int i=0; i<MAXSTAR; i++) { InitStar(i); star[i].x = rand() % 640; } // 绘制星空,按任意键退出 while(!kbhit()) { for(int i=0; i<MAXSTAR; i++) MoveStar(i); Sleep(20); } closegraph(); // 关闭图形窗口 }

字符阵程序

该程序是 EasyX 帮助中的范例程序。 // 程序名称:字符阵 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 最后更新:2009-2-22 // #include <graphics.h> #include <time.h> #include <conio.h> void main() { // 设置随机函数种子 srand((unsigned) time(NULL)); // 初始化图形模式 initgraph(640, 480); int x, y; char c; setfont(16, 8, "Courier"); // 设置字体 while(!kbhit()) { for (int i=0; i<479; i++) { setcolor(GREEN); for (int j=0; j<3; j++) { x = (rand() % 80) * 8; y = (rand() % 20) * 24; c = (rand() % 26) + 65; outtextxy(x, y, c); } setcolor(0); line(0,i,639,i); Sleep(10); if (kbhit()) break; } } // 关闭图形模式 closegraph(); }