CodeBus

分享代码,一起进步~

漂亮的四叶草

前几天有网友在 QQ 群“C 语言革命”里面发了一段 Turbo C 代码,感觉蛮漂亮的,我就将代码移植到 VC + EasyX 下面(稍作了一点格式上的修改)。 执行效果如下: 完整的源代码如下: /////////////////////////////////////////////////// // 程序名称:漂亮的四叶草 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 作  者:yangw80 <yw80@qq.com> // 最后修改:2011-10-14 // 注:源程序是网友发在 QQ 群“C 语言革命”里面的 Turbo C 代码,我修改的 // #include <graphics.h> #include <math.h> #include <conio.h> #define PI 3.1415926535 void main(void) { // 初始化绘图窗口 initgraph(640, 480); // 创建绘图窗口 setcolor(GREEN); // 设置绘图颜色 setorigin(320, 240); // 设置原点坐标 // 画花朵 double e; int x1, y1, x2, y2; for(double a = 0; a < 2 * PI; a += 2 * PI / 720) { e = 100 * (1 + sin(4 * a)); x1 = (int)(e * cos(a)); y1 = (int)(e * sin(a)); x2 = (int)(e * cos(a + PI / 5)); y2 = (int)(e * sin(a + PI / 5)); line(x1, y1, x2, y2); Sleep(20); // 延迟函数,实现慢速绘制的动画效果 } // 按任意键退出 _getch(); closegraph(); }

一束漂亮的花

又是一个回忆。。。 我最早是抱着一台“学习机”学的 basic,之后第一次在电脑上输入的程序,就是这个效果。不过那时候的显示器是单色的,书上的代码也没有设置颜色,所以当时把程序输入 GW-BASIC 后,输出的整个图案都是墨绿色的。哦,顺便贴一下书的封皮照片:   我一直记得我还留着这本书的,上次回家偏偏没找到,然后网上到处搜也没搜到,恰好 easyx 群里的阿里说他们学校图书馆有这本书,于是借来帮我拍了几张照片,在这里表示感谢。 感慨完了,说正题吧。我把原来的 basic 程序移植到了 vc + easyx 下,并简单的加了些颜色,执行效果如下: 完整的代码如下: /////////////////////////////////////////////////// // 程序名称:一束漂亮的花 // 编译环境:Visual C++ 6.0 / 2010,EasyX 2011惊蛰版 // 作  者:yangw80 <yw80@qq.com> // 最后修改:2011-9-29 // #include <graphics.h> #include <conio.h> #include <math.h> #define PI 3.14159265 // 画 花朵 void flower(int x, int y, COLORREF c) { int x1, y1, x2, y2; int d = 15; double e; setcolor(c); for(double a = 0; a < 2 * PI; a += PI / 360) { e = d * (1 + sin(a * 5)); x1 = int(x + e * cos(a)); y1 = int(y + e * sin(a)); x2 = int(x + e * cos(a + PI / 5)); y2 = int(y + e * sin(a + PI / 5)); line(x1, y1, x2, y2); } } // 画 蝴蝶结 void tie(int x, int y, COLORREF c) { int x1, y1, x2, y2; int d = 80; double e; setcolor(c); for(double a = 0; a < 2 * PI; a += PI / 360) { e = d * (1 + sin(a * 4)); x1 = int(x + e * cos(a)); y1 = int(y + e * sin(a) / 2); x2 = int(x + e * cos(a + PI / 9)); y2 = int(y + e * sin(a + PI / 9) / 4.5); line(x1, y1, x2, y2); } } // 主函数 void main() { initgraph(640, 480); // 画枝干 setcolor(GREEN); line(189, 372, 180, 400); line(310, 160, 325, 68); line(310, 160, 187, 374); line(150, 140, 189, 374); line(430, 176, 190, 374); line(370, 110, 187, 374); line(250, 72, 189, 372); line(253, 192, 190, 374); line(189, 372, 187, 400); line(189, 372, 182, 400); line(189, 372, 200, 120); // 画花朵 flower(320, 160, RED); flower(200, 120, YELLOW); flower(150, 140, LIGHTRED); flower(430, 176, RGB(255, 127, 0)); flower(370, 110, RGB(239, 179, 52)); flower(250, 72, RGB(235, 95, 186)); flower(325, 68, RGB(228, 119, 98)); flower(253, 190, RGB(247, 169, 117)); // 画蝴蝶结 tie(195, 354, LIGHTMAGENTA); // 按任意键退出 getch(); closegraph(); }

图片滤镜系列:波浪线叠加效果

我念初中的时候买过一盘国外原版的磁带:贝多芬第九交响乐《合唱》。磁带很是精美,硬纸壳压出了立体的金色贝多芬头像,并且还有第二个封面,是叠加在金色波浪线上的贝多芬头像。这个波浪线叠加的效果很有趣,我一直想写个程序实现这种效果,无奈总是抽不出时间,今天可算把这件事情给做了。 只是由于屏幕分辨率太低,还是无法和印刷的效果相媲美。而且,由于磁带已经找不到了,我只是按照我想象中的样子做的。 我就不弄贝多芬的头像了,换了另一个重要人物的头像照片做图片源,程序处理后的效果如下: 全部源代码如下: 注:src.jpg 就是源图片的名字,该图片的大小必须是 640 x 480,另外建议用简单的背景,大一点的头像,否则效果不是很理想。

高等数学图形:玫瑰曲线

在极坐标系中,以下方程表示的曲线称为玫瑰曲线: r = sin ( k θ )  或  r = cos ( k θ ) 当 k 是奇数时,玫瑰曲线有 k 个花瓣;当 k 是偶数时,玫瑰曲线有 2k 个花瓣。执行效果如下图: 可以按 A-Z 和 a-z 调整参数,绘制不同的玫瑰曲线。例如,k = n / d = 2 / 1,就是四叶玫瑰曲线,k = n / d = 3 / 1,就是三叶玫瑰曲线。 完整源代码如下: //////////////////////////////////////////// // 程序名称:高等数学图形:玫瑰曲线 // 编译环境:Visual C++ 6.0 / 2010,EasyX 2011惊蛰版 // 程序编写:yangw80 <yw80@qq.com> // 最后更新:2011-7-11 // #include <graphics.h> #include <stdio.h> #include <conio.h> #include <math.h> #define PI 3.14159265 // 画玫瑰曲线 // 参数: // k = n / d: 玫瑰曲线方程参数 // r: 半径 void DrawRoseCurve(int n, int d, int r) { int x, y; bar(-240, -240, 239, 239); setcolor(GREEN); moveto(0, 0); for (double i=0; i <= PI * 2; i += 0.001) { x = int(r * sin(n * PI * i) * cos(d * PI * i)); y = int(r * sin(n * PI * i) * sin(d * PI * i)); lineto(x, y); } } // 主函数 void main() { initgraph(640, 480); // 初始化绘图窗口 setorigin(240, 240); // 设置坐标原点为屏幕中央 setfillstyle(BLACK); // 设置填充色为黑色 // 输出提示信息 line(240, -240, 240, 239); RECT r = {245, -200, 395, 220}; setfont(13, 0, "Consolas"); drawtext("玫瑰曲线 ( Rose curve )\n\n[简介]\n  在极坐标系中,以下方程表示的曲线称为玫瑰\ 曲线:\n  r = sin ( k θ )\n  或\n  r = cos ( k θ )\n  当 k 是奇数时,玫\ 瑰曲线有 k 个花瓣;\n  当 k 是偶数时,玫瑰曲线有 2k 个花瓣。\n\n[控制]\n  k = \ n / d\n  a-z: 设置 n 为 1-26\n  A-Z: 设置 d 为 1-26\n  ESC: 退出\n\n[当前值]\ \n  k = n / d\n   = 2 / 1", &r, DT_WORDBREAK); char cmd = 'b'; char tmp[20]; int n = 2; int d = 1; while(cmd != 27) { if (cmd >= 'a' && cmd <= 'z') n = cmd - 'a' + 1; else if (cmd >= 'A' && cmd <= 'Z') d = cmd - 'A' + 1; sprintf(tmp, "   = %d / %d ", n, d); setcolor(WHITE); outtextxy(245, 86, tmp); DrawRoseCurve(n, d, 200); cmd = getch(); } closegraph(); }

艺术字系列:冰封的 EasyX

执行效果如下图: 代码间有详细的注释,这里就不多做解释了。 完整源代码如下:  //////////////////////////////////////////// // 程序名称:艺术字系列:冰封的 EasyX // 编译环境:Visual C++ 6.0 / 2010,EasyX 2011惊蛰版 // 程序编写:yangw80 <yw80@qq.com> // 最后更新:2011-6-20 // #include <graphics.h> #include <conio.h> #include <time.h> // 定义全局变量 POINT *g_pDst; // 点集(目标) POINT *g_pSrc; // 点集(源) int g_nWidth; // 文字的宽度 int g_nHeight; // 文字的高度 int g_nCount; // 点集包含的点的数量 // 获取目标点集 void GetDstPoints() { // 设置临时绘图对象 IMAGE img; SetWorkingImage(&img); // 定义目标字符串 TCHAR s[] = _T("EasyX"); // 计算目标字符串的宽高,并调整临时绘图对象的尺寸 setcolor(WHITE); setfont(100, 0, _T("Arial")); g_nWidth = textwidth(s); g_nHeight = textheight(s); Resize(&img, g_nWidth, g_nHeight); // 输出目标字符串至 img 对象 outtextxy(0, 0, s); // 计算构成目标字符串的点的数量 int x, y; g_nCount = 0; for(x = 0; x < g_nWidth; x++) for(y = 0; y < g_nHeight; y++) if (getpixel(x, y) == WHITE) g_nCount++; // 计算目标数据 g_pDst = new POINT[g_nCount]; int i = 0; for(x = 0; x < g_nWidth; x++) for(y = 0; y < g_nHeight; y++) if (getpixel(x, y) == WHITE) { g_pDst[i].x = x + (640 - g_nWidth) / 2; g_pDst[i].y = y + (480 - g_nHeight) / 2; i++; } // 恢复对屏幕的绘图操作 SetWorkingImage(NULL); } // 获取源点集 void GetSrcPoints() { // 设置随机种子 srand((unsigned int)time(NULL)); // 设置随机的源数据 g_pSrc = new POINT[g_nCount]; for(int i = 0; i < g_nCount; i++) { g_pSrc[i].x = rand() % 640; g_pSrc[i].y = rand() % 480; } } // 全屏模糊处理(忽略屏幕第一行和最后一行) void Blur(DWORD* pMem) { for(int i = 640; i < 640 * 479; i++) { pMem[i] = RGB( (GetRValue(pMem[i]) + GetRValue(pMem[i - 640]) + GetRValue(pMem[i - 1]) + GetRValue(pMem[i + 1]) + GetRValue(pMem[i + 640])) / 5, (GetGValue(pMem[i]) + GetGValue(pMem[i - 640]) + GetGValue(pMem[i - 1]) + GetGValue(pMem[i + 1]) + GetGValue(pMem[i + 640])) / 5, (GetBValue(pMem[i]) + GetBValue(pMem[i - 640]) + GetBValue(pMem[i - 1]) + GetBValue(pMem[i + 1]) + GetBValue(pMem[i + 640])) / 5); } } // 主函数 void main() { // 初始化 initgraph(640, 480); // 创建绘图窗口看 DWORD* pBuf = GetImageBuffer(); // 获取显存指针 GetDstPoints(); // 获取目标点集 GetSrcPoints(); // 获取源点集 // 运算 int x, y; for (int i = 2; i <= 256; i += 2) { COLORREF c = RGB(i-1, i-1, i-1); Blur(pBuf); // 全屏模糊处理 for (int d = 0; d < g_nCount; d++) { x = g_pSrc[d].x + (g_pDst[d].x - g_pSrc[d].x) * i / 256; y = g_pSrc[d].y + (g_pDst[d].y - g_pSrc[d].y) * i / 256; pBuf[y * 640 + x] = c; // 直接操作显存画点 } FlushBatchDraw(); // 使显存操作生效 Sleep(20); // 延时 } // 清理内存 delete g_pDst; delete g_pSrc; // 按任意键退出 getch(); closegraph(); }

汉诺塔移动动画

这是经典问题汉诺塔的解题演示动画,代码如下: /////////////////////////////////////////////////// // 程序名称:汉诺塔移动动画 // 编译环境:Visual C++ 6.0,EasyX_20130506(beta) // 作  者:Ronald Email:ryl910527@gmail.com // 最后修改:2011-5-26 // #include <graphics.h> #include <conio.h> #include <stdio.h> #define MAX 64 // 圆盘的最大数目 #define NULL 0 // 定义栈 struct STKNODE { int a[4]; }; struct STK { STKNODE* stack[MAX]; int top; }; // 定义全局变量 STK s[3]; // 声明三个栈,分别代表一号二号三号钢针上圆盘的状态 int v = 5; // 调整速度 // 函数声明 void Initstk(STK* s); // 初始化栈 void Hannoi(int n, char a, char b, char c); // 汉诺塔递归 void start(); // 开始画面 void Move(int n, char a, char b); // 移动过程 int switchab(char a); // 返回钢针号 void adjust(); // 调整速度暂停 // 主函数 void main() { int n, ta[4] = {115, 500, 285, 518}; // 第一个圆盘的位置 printf("尽量小于16\n"); // 因为大于十六时就会显示有误,但程序可以正常运行 printf("请输入汉诺塔的层数(1~64):"); scanf("%d", &n); STKNODE** p; p = (STKNODE**)malloc(n * sizeof(STKNODE **)); // 声明一个元素为 n 个的动态 STKNODE 型指针数组 for (int i2 = 0; i2 < n; i2 ++) { p[i2] = (STKNODE *)malloc(sizeof(STKNODE)); // 为每一个指针申请空间 } Initstk(&s[0]); Initstk(&s[1]); Initstk(&s[2]); // 将三个栈初始化 start(); // 呈现开始画面 setfillcolor(GREEN); // 圆盘的颜色 for (int i=0; i < n; i++) { ta[0] += 5; ta[1] -= 20; ta[2] -= 5; ta[3] -= 20; solidrectangle(ta[0], ta[1], ta[2], ta[3]); // 画出n个从大到小一次叠放的黄色圆盘 ++s[0].top; // 进栈 for (int i1 = 0; i1 < 4; i1++) { p[i]->a[i1] = ta[i1]; s[0].stack[s[0].top] = p[i]; // 记录每个矩形的位置,top为圆盘的个数 } } Hannoi(n, 'a', 'b', 'c'); // 汉诺塔递归函数 system("pause"); printf("\t\t\t\tGame Over!\n"); } /////////////////////////////////////////////////// // 函数定义 // 汉诺塔的递归 void Hannoi(int n, char a, char b, char c) { if(n == 1) Move(1, a, c); else { Hannoi(n-1, a, c, b); Move(n, a, c); Hannoi(n-1, b, a, c); } } // 栈的初始化 void Initstk(STK *s) { int i; s->top = 0; for (i = 0; i <= MAX; i++) s->stack[++s->top] = NULL; s->top = 0; } // 移动过程 void Move(int n, char a, char b) { int i3, i4 = 0, i5 = 0; i3 = b - a; // 目的钢针与源钢针的位置差值 i4 = switchab(a); // 源钢针钢针号 i5 = switchab(b); // 目的钢针号 STKNODE *q1, *q0; // 两个中间结点用于源栈和目的栈间的值得传递,q1为目的栈,q0为源栈 q1 = (STKNODE *)malloc(sizeof(STKNODE)); q0 = (STKNODE *)malloc(sizeof(STKNODE)); // 源栈与目的栈值的传递 q0 = s[i4].stack[s[i4].top]; ++s[i5].top; // 进栈 q1->a[0] = q0->a[0] + i3 * 200; q1->a[1] = 500 - s[i5].top * 20; q1->a[2] = q0->a[2] + i3 * 200; q1->a[3] = 500 - s[i5].top * 20 + 18; s[i5].stack[s[i5].top] = q1; --s[i4].top; // 出栈 // 向上运动 while (q0->a[1] >= 100) { setfillcolor(GREEN); solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]); adjust(); // 调整函数 Sleep(10 * v); // 暂停(ms) setfillcolor(WHITE); solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]); setlinecolor(RED); line((q0->a[0] + q0->a[2]) / 2, q0->a[1], (q0->a[0] + q0->a[2]) / 2, q0->a[3]); // 重新画上被擦掉原有的红线 q0->a[1] -= 10; q0->a[3] -= 10; } // 向左或右运动,与 i3 的正负有关 while (q0->a[2] != q1->a[2]) { setfillcolor(GREEN); solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]); adjust(); Sleep(10 * v); setfillcolor(WHITE); solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]); if (i3 < 0) // i3<0向左移 { q0->a[0] -= 20; q0->a[2] -= 20; } else // i3>0向右移 { q0->a[0] += 20; q0->a[2] += 20; } } // 向下运动 while (q0->a[3] <= q1->a[3]) { setfillcolor(GREEN); solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]); adjust(); Sleep(10 * v); setfillcolor(WHITE); solidrectangle(q0->a[0], q0->a[1], q0->a[2], q0->a[3]); setlinecolor(RED); if (q0->a[1] > 100) // 重画被擦掉的红线 { line((q0->a[0] + q0->a[2]) / 2, q0->a[1], (q0->a[0] + q0->a[2]) / 2, q0->a[3]); } q0->a[1] += 10; q0->a[3] += 10; } // 在目的钢针上的相应位置绘制出黄色矩形块 setfillcolor(GREEN); solidrectangle(q1->a[0], q1->a[1], q1->a[2], q1->a[3]); } // 绘制开始界面 void start() { // 初始化画面大小 initgraph(800, 650); // 背景设为白色 setbkcolor(WHITE); // 用白色填充整个画面 cleardevice(); // 绘制彩虹,形成一道彩虹,摘自 easyx 帮助文档示例程序 float H, S, L; H = 0; // 色相 S = 1; // 饱和度 L = 0.5f; // 亮度 setlinestyle(PS_SOLID, NULL, 2); // 设置线宽为 2 for(int r = 600; r > 544; r--) { H += 5; setlinecolor( HSLtoRGB(H, S, L) ); circle(750, 900, r); } // 说明 settextstyle(50, 0, "华文楷体"); settextcolor(RED); outtextxy(200, 150, "汉诺塔移动动画"); settextstyle(20, 0, "黑体"); outtextxy(600, 200, "BY:Ronald"); outtextxy(500, 200, "版本V1.1"); settextstyle(50, 0, "黑体"); settextcolor(GREEN); outtextxy(200, 350, "随便按一个键开始吧!"); // 检测键盘敲击 getch(); // 清空开始界面 cleardevice(); // 绘制运动画面的的环境 setlinecolor(RED); // 三根红色线段作为钢针 line(400, 110, 400, 500); line(600, 110, 600, 500); line(200, 110, 200, 500); // 长方体形的底座 setfillcolor(LIGHTGRAY); fillrectangle(80, 501, 720, 510); // 暂停按钮 solidrectangle(360, 540, 440, 580); settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(370, 550, "暂停"); settextstyle(20, 0, "宋体"); settextcolor(RED); outtextxy(300, 580, "鼠标暂停后请按空格继续"); // 加速按钮 solidrectangle(160, 540, 240, 580); settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(170, 550, "加速"); settextstyle(20, 0, "宋体"); settextcolor(RED); outtextxy(170, 580, "请按 d"); // 减速按钮 solidrectangle(560, 540, 640, 580); settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(570, 550, "减速"); settextstyle(20, 0, "宋体"); settextcolor(RED); outtextxy(570, 580, "请按 a"); // 说明 settextstyle(50, 0, "宋体"); settextcolor(GREEN); outtextxy(10, 10, "正在进行中请欣赏:"); } // 判断目的钢针与源钢针的钢针号返回钢针号 int switchab(char a) { switch (a) { case 'a': return 0; case 'b': return 1; case 'c': return 2; default: return 0; } } // 调整函数,实现加速,减速,暂停 void adjust() { char f; // 接收键盘敲进去的按钮和鼠标点击时赋予的变化值 // 用 f 接受键盘的键入值 if(kbhit()) f = getch(); // 检测鼠标消息 if (MouseHit()==true) { // 接收鼠标消息 MOUSEMSG Mouse; Mouse = GetMouseMsg(); // 响应鼠标消息 if (Mouse.x >= 360 && Mouse.x <= 440 && Mouse.y >= 540 && Mouse.y <= 580 && Mouse.mkLButton) { f = ' '; } if (Mouse.x >= 160 && Mouse.x <= 240 && Mouse.y >= 540 && Mouse.y <= 580 && Mouse.mkLButton) { f = 'd'; } if (Mouse.x >= 560 && Mouse.x <= 640 && Mouse.y >= 540 && Mouse.y <= 580 && Mouse.mkLButton) { f = 'a'; } } // 作用于动画 switch(f) { // 暂停 case ' ': // 用‘继续’覆盖‘暂停’ settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(370, 550, "继续"); getch(); // 继续后变回显示‘暂停’ settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(370, 550, "暂停"); break; // 减速 case 'a': // 当被点击时,‘减速’位置震动一下 setfillcolor(LIGHTGRAY); solidrectangle(560, 540, 640, 580); settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(575, 545, "减速"); Sleep(30); // 减速 v++; // 回原位 setfillcolor(LIGHTGRAY); solidrectangle(560, 540, 640, 580); settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(570, 550, "减速"); break; // 加速 case 'd': setfillcolor(LIGHTGRAY); solidrectangle(160, 540, 240, 580); settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(165, 545, "加速"); Sleep(30); setfillcolor(LIGHTGRAY); solidrectangle(160, 540, 240, 580); settextstyle(30, 0, "黑体"); settextcolor(GREEN); outtextxy(170, 550, "加速"); // 加速 v--; // v 最小为1 if (v <= 0) { v = 1; } break; default: break; } f = 'r'; // f 初始化为 r FlushMouseMsgBuffer(); // 清空鼠标消息 } 作者:RonaldEmail:ryl910527@gmail.com

生命游戏

生命游戏包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的细胞的数量。如果一个细胞周围的活细胞数量多于 3 个,这个细胞会因为资源匮乏而在下一个时刻死去;如果一个位置周围有 3 个活细胞,则该位置在下一个时刻将诞生一个新的细胞;如果一个位置周围有 2 个活细胞,则该位置的细胞生死状态保持不变;如果一个细胞周围的活细胞少于 2 个,那么这个细胞会因太孤单而死去。这样整个生命世界才不至于太过荒凉或拥挤,而是一种动态的平衡。 该程序是生命游戏的图形演示,执行效果如下图: 源代码如下: /////////////////////////////////////////////////// // 程序名称:生命游戏 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 作  者:yangw80 <yw80@qq.com> // 最后修改:2011-5-22 // #include <graphics.h> #include <conio.h> #include <time.h> // 定义全局变量 __int8 world[102][102] = {0}; // 定义二维世界 IMAGE imgLive, imgEmpty; // 定义活细胞和无细胞区域的图案 // 函数声明 void Init(); // 初始化 void SquareWorld(); // 创建一个细胞以方形分布的世界 void RandWorld(); // 创建一个细胞随机分布的世界 void PaintWorld(); // 绘制世界 void Evolution(); // 进化 // 主函数 int main() { Init(); int Speed = 500; // 游戏速度(毫秒) while(true) { if (kbhit() || Speed == 900) { char c = getch(); if (c == ' ' && Speed != 900) c = getch(); if (c >= '0' && c <= '9') Speed = ('9' - c) * 100; switch(c) { case 's': case 'S': SquareWorld(); // 产生默认的细胞以方形分布的世界 break; case 'r': case 'R': RandWorld(); // 产生默认的细胞以方形分布的世界 break; case VK_ESCAPE: goto END; } } Evolution(); // 进化 PaintWorld(); // 绘制世界 if (Speed != 900) // 速度为 900 时,为按任意键单步执行 Sleep(Speed); } END: closegraph(); return 0; } /////////////////////////////////////////////////// // 函数定义 // 初始化 void Init() { // 创建绘图窗口 initgraph(640,480); // 设置随机种子 srand((unsigned)time(NULL)); // 调整世界图案的大小 Resize(&imgLive, 4, 4); Resize(&imgEmpty, 4, 4); // 绘制有生命世界的图案 SetWorkingImage(&imgLive); setcolor(GREEN); setfillstyle(GREEN); fillellipse(0, 0, 3, 3); // 绘制无生命世界的图案 SetWorkingImage(&imgEmpty); setcolor(DARKGRAY); rectangle(1, 1, 2, 2); // 恢复对默认窗口的绘图 SetWorkingImage(NULL); // 输出简单说明 setfont(24, 0, "黑体"); outtextxy(254, 18, "生 命 游 戏"); RECT r = {440, 60, 620, 460}; setfont(12, 0, "宋体"); drawtext("生命游戏简介:\n  生命游戏包括一个二维矩形世界,这个世界中的每个方格居住\ 着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的细胞\ 的数量。如果一个细胞周围的活细胞数量多于 3 个,这个细胞会因为资源匮乏而在下一个时\ 刻死去;如果一个位置周围有 3 个活细胞,则该位置在下一个时刻将诞生一个新的细胞;如\ 果一个位置周围有 2 个活细胞,则该位置的细胞生死状态保持不变;如果一个细胞周围的活\ 细胞少于 2 个,那么这个细胞会因太孤单而死去。这样整个生命世界才不至于太过荒凉或拥\ 挤,而是一种动态的平衡。\n\n游戏控制:\n 0-9: 调节速度(慢--快)\n ESC: 退出\n空格: \ 暂停|继续\n S: 创建细胞以方形分布的世界\n R: 创建细胞随机分布的世界", &r, DT_WORDBREAK); // 产生默认的细胞以方形分布的世界 SquareWorld(); } // 创建一个细胞以方形分布的世界 void SquareWorld() { memset(world, 0, 102 * 102 * sizeof(__int8)); for(int x = 1; x <= 100; x++) world[x][1] = world[x][100] = 1; for(int y = 1; y <= 100; y++) world[1][y] = world[100][y] = 1; } // 创建一个细胞随机分布的世界 void RandWorld() { for(int x = 1; x <= 100; x++) for(int y = 1; y <= 100; y++) world[x][y] = rand() % 2; } // 绘制世界 void PaintWorld() { for(int x = 1; x <= 100; x++) for(int y = 1; y <= 100; y++) putimage(16 + x * 4, 56 + y * 4, world[x][y] ? &imgLive : &imgEmpty); } // 进化 void Evolution() { __int8 tmp[102][102] = {0}; // 临时数组 int sum; for(int x = 1; x <= 100; x++) { for(int y = 1; y <= 100; y++) { // 计算周围活着的生命数量 sum = world[x+1][y] + world[x+1][y-1] + world[x][y-1] + world[x-1][y-1] + world[x-1][y] + world[x-1][y+1] + world[x][y+1] + world[x+1][y+1]; // 计算当前位置的生命状态 switch(sum) { case 3: tmp[x][y] = 1; break; case 2: tmp[x][y] = world[x][y]; break; default: tmp[x][y] = 0; break; } } } // 将临时数组恢复为世界 memcpy(world, tmp, 102 * 102 * sizeof(__int8)); }

自由运动的点(全屏模糊处理的范例)

这个程序并没有太强的艺术效果,只是为了做一个全屏模糊处理的范例。 其中,点的非直线自由运动的部分,是用的我在高中时候用 QuickBasic 实现的一种简单方法,看到这部分代码有点让人怀念过去,呵呵。 简单说明一下: 1. 为了使范例更清晰,全屏模糊处理的代码我做了简化,将第一行和最后一行忽略掉了。通过代码可以清晰地看到,所谓全屏模糊,其实就是对每个点与相邻的几个点的颜色做平均处理(可以自己决定每个点的权重)。 2. 这个模糊函数只处理了每个点的上、下、左、右共 5 个点。为了获得不同的效果,可以试试将周围八个点一起处理,或者上、下、左、右方向上的两个点一起处理。 3. 严格来说,每个点在运算时,所使用的左边和上边的点,其实已经不是原来的点了,而是模糊后的点。所以这样的模糊处理对于单个点来说,右边和下边颜色要淡一些。如果需要精确的模糊,可以借助 IMAGE 实现,这里就不再多说了。 4. 延时没有用常用的 Sleep 实现,而是使用的精确延时,详见文章《精确延时的实现》 下面是运行动画的截图: 完整的源代码如下:  /////////////////////////////////////////////////// // 程序名称:自由运动的点 // 编译环境:Visual C++ 6.0 / 2012,EasyX 2013霜降版 // 作  者:yangw80 <yw80@qq.com> // 最后修改:2011-5-3 // #include <graphics.h> #include <conio.h> #include <math.h> #include <time.h> #define AMOUNT 64 // 全屏模糊处理 // (为了简化范例,该函数略去了屏幕第一行和最后一行的处理) void Blur(DWORD* pMem) { for(int i = 640; i < 640 * 479; i++) { pMem[i] = RGB( (GetRValue(pMem[i]) + GetRValue(pMem[i - 640]) + GetRValue(pMem[i - 1]) + GetRValue(pMem[i + 1]) + GetRValue(pMem[i + 640])) / 5, (GetGValue(pMem[i]) + GetGValue(pMem[i - 640]) + GetGValue(pMem[i - 1]) + GetGValue(pMem[i + 1]) + GetGValue(pMem[i + 640])) / 5, (GetBValue(pMem[i]) + GetBValue(pMem[i - 640]) + GetBValue(pMem[i - 1]) + GetBValue(pMem[i + 1]) + GetBValue(pMem[i + 640])) / 5); } } // 点的结构 struct SPOT { int x, y; int targetx, targety; int dx, dy; COLORREF color; }; // 精确延时函数(可以精确到 1ms,精度 ±1ms) // (原理在 www.easyx.cn 有文章详细解释) void HpSleep(int ms) { static clock_t oldclock = clock(); // 静态变量,记录上一次 tick oldclock += ms * CLOCKS_PER_SEC / 1000; // 更新 tick if (clock() > oldclock) // 如果已经超时,无需延时 oldclock = clock(); else while(clock() < oldclock) // 延时 Sleep(1); // 释放 CPU 控制权,降低 CPU 占用率 } // 主函数 void main() { // 初始化 initgraph(640, 480); // 创建绘图窗口 BeginBatchDraw(); // 设置批绘图模式 srand((unsigned)time(NULL)); // 设置随机种子 DWORD* pMem = GetImageBuffer(); // 获取显存地址 // 定义所有点 SPOT spots[AMOUNT]; // 初始化每个点 for(int i = 0; i < AMOUNT; i++) { spots[i].x = spots[i].targetx = rand() % 600 + 20; spots[i].y = spots[i].targety = rand() % 440 + 20; spots[i].dx = rand() % 40 - 20; spots[i].dy = (int)sqrt(400 - spots[i].dx * spots[i].dx) * ((rand() % 2) * 2 - 1); spots[i].color = HSLtoRGB((float)(rand() % 360), 1.0, 0.5); } while(!kbhit()) { for(int i = 0; i < AMOUNT; i++) { setcolor(spots[i].color); moveto(spots[i].x, spots[i].y); spots[i].targetx += spots[i].dx; spots[i].targety += spots[i].dy; // 判断是否越界,以及越界处理 if (spots[i].targetx <= 0) { spots[i].dx = rand() % 20; spots[i].dy = (int)sqrt(400 - spots[i].dx * spots[i].dx) * ((rand() % 2) * 2 - 1); } else if (spots[i].targetx >= 639) { spots[i].dx = - rand() % 20; spots[i].dy = (int)sqrt(400 - spots[i].dx * spots[i].dx) * ((rand() % 2) * 2 - 1); } if (spots[i].targety <= 0) { spots[i].dx = rand() % 40 - 20; spots[i].dy = (int)sqrt(400 - spots[i].dx * spots[i].dx); } else if (spots[i].targety >= 479) { spots[i].dx = rand() % 40 - 20; spots[i].dy = - (int)sqrt(400 - spots[i].dx * spots[i].dx); } // 未越界时,有 10% 的概率改变运行方向 if (rand() % 10 < 1) { spots[i].dx = rand() % 40 - 20; spots[i].dy = (int)sqrt(400 - spots[i].dx * spots[i].dx) * ((rand() % 2) * 2 - 1); } // 计算新点坐标,画线 spots[i].x += (int)((spots[i].targetx - spots[i].x) * 0.1); spots[i].y += (int)((spots[i].targety - spots[i].y) * 0.1); lineto(spots[i].x, spots[i].y); } // 全屏模糊处理 Blur(pMem); FlushBatchDraw(); // 延时 HpSleep(33); } // 按任意键退出 closegraph(); }

做个纪念:椭圆组合而成的抽象图案

每次看到这个程序我就会想起高中时曾那么痴迷编程。当时看到一本书的封面有这个图案,就用那个时候学的 QuickBasic 写了这个程序。现在移植到 VC 上,做个纪念吧。 执行效果如下: 完整的 VC 源代码如下: // 程序名称:椭圆组合而成的抽象图案 // 编译环境:Visual C++ 6.0/2010,EasyX 2011惊蛰版 // 最初编写:1998-2-21,by yw80@qq.com(QuickBasic 版本) // 最后修改:2011-3-23,by yw80@qq.com // // 高中时候看到一本书的封面有这个图案,就用当时学的 QuickBasic 写了这个程序。 // 现在移植到 VC6 上,做个纪念。 // #include <graphics.h> #include <conio.h> #include <math.h> #define PI 3.1415926536 // 四舍五入 int round(double x) { return (int)(x < 0 ? x - 0.5 : x + 0.5); } // 主函数 void main() { // 初始化 initgraph(640, 480); // 创建绘图窗口 setorigin(320, 240); // 设置原点为屏幕中央 setaspectratio(1, -1); // 设置 y 轴向上为正 double r = 58; double csin = sin(2 * PI / 200); double ccos = cos(2 * PI / 200); for(int j = 0; j < 100; j++, r -= 0.9) { double tsin = sin(2 * PI * j / 100); double tcos = cos(2 * PI * j / 100); double x = 0; double y = r; for(int i = 0; i < 200; i++) { double temp = x; x = x * ccos + y * csin; y = y * ccos - temp * csin; int scrx = round(x * tcos + y * 4 * tsin); int scry = round(y * 4 * tcos - x * tsin); putpixel(scrx, scry, GREEN); } Sleep(20); } getch(); closegraph(); }

吸顶灯上的一个图案,由若干圆弧组成

也不知道这个程序该叫什么名字,是我偶然间在一个吸顶灯的灯罩上看到的图案,于是就写了一个程序来生成。 执行效果如下: 完整的源代码如下: // 程序名称:圆弧组成的图案 // 编译环境:Visual C++ 6.0,EasyX 2011惊蛰版 // 最后修改:2011-3-7 // #include <graphics.h> #include <conio.h> #include <math.h> #define PI 3.1415926536 #define R 200 // 四舍五入 int Round(double x) { return (int)(x < 0 ? x - 0.5 : x + 0.5); } // 主函数 void main() { // 变量定义 int x, y; // 坐标 double a; // 弧度 // 绘图环境初始化 initgraph(640, 480); // 初始化图形窗口 setorigin(320, 240); // 设置坐标原点 setcolor(GREEN); // 设置绘图颜色 // 绘图 circle(0, 0, R + 1); for (int i=0; i<126; i++) { if ((i % 21) < 7) { a = PI * 2 * i / 126; x = Round(cos(a) * R); y = Round(sin(a) * R); arc(x, y, PI * 2 / 3 - a, PI * 4 / 3 - a, R, R); } } // 按任意键退出 getch(); closegraph(); }