VC绘图/游戏简易教程--10:绘图中的位运算
教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)
========================
位运算和绘图有什么关系?先举个例子来个感性认识:使用XOR运算可以实现擦除图形后不破坏背景,这在时钟程序中绘制表针是很有用的。稍后我们会给出这样的例子。
一、位运算的运算法则
位运算主要分 4 种:NOT、AND、OR、XOR,位运算的运算对象是二进制数(十进制要转换为二进制,计算机会自动转换)。
运算法则如下:
- NOT
表示“取反”,将二进制位的 1 变 0、0 变 1。
C 语言用符号 ~ 表示。
如:
二进制: ~1101 = 0010
用十进制表示就是:~13 = 2
- AND
表示“并且”,只有两数的对应二进制位都为 1,结果的二进制位才为 1;否则,结果的二进制位为 0。
C语言用符号 & 表示。
如:
二进制:1101 & 0110 = 0100
用十进制表示就是:13 & 6 = 4
- OR
表示“或者”,两数的对应二进制位只要有一个是 1,结果的二进制位就是 1;否则,结果的二进制位为 0。
C语言用符号 | 表示。
如:
二进制:0101 | 0110 = 0111
用十进制表示就是:5 | 6 = 7
- XOR
表示“异或”,两数的对应二进制位不同,结果的二进制位为 1;相同,结果的二进制位为 0。
C语言用符号 ^ 表示。
如:
二进制:0101 ^ 1110 = 1011
以上只是简单介绍一下,详细的还是请大家看课本上的讲解。
二、位运算的应用
位运算的应用很多,例如 AND 和 OR 在获取和设置标志位时经常使用。更多的,以后大家会逐渐遇到,暂时先记下有这么回事。
这里着重说一下 XOR 运算,它有一个重要的特性:(a ^ b) ^ b = a
也就是说,a ^ b 之后可能是某些其它数字,但是只要再 ^b 一下,就又成了 a。
一些简单的加密就用的 XOR 的这个特性。
至于绘图,假如 a 是背景图案,b 是将要绘制的图案,只要用 XOR 方式绘图,连续绘两次,那么背景是不变的。
三、演示
我们来一个简单的绘图 XOR 运算演示:
#include <graphics.h>
#include <conio.h>
int main()
{
initgraph(640, 480); // 初始化 640 x 480 的绘图窗口
setlinestyle(PS_SOLID, 10); // 设置线宽为 10,这样效果明显
setlinecolor(0x08BAFF); // 设置画线颜色为黄色
rectangle(100, 100, 200, 200); // 画一个黄色矩形框,当做背景图案
setrop2(R2_XORPEN); // 设置 XOR 绘图模式
setlinecolor(0x2553F3); // 设置画线颜色为红色
line(50, 0, 200, 300); // 画红色线
_getch(); // 等待按任意键
line(50, 0, 200, 300); // 画红色线(XOR 方式重复画线会恢复背景图案)
_getch(); // 等待按任意键
closegraph(); // 关闭绘图窗口
return 0;
}
运行一下,屏幕出现黄色(0x08BAFF)的矩形框与红色(0x2553F3)的直线相交,矩形框与直线相交的部分,颜色变成了绿色(0x2DE90C),这是因为 黄色(0x08BAFF) XOR 红色(0x2553F3) = 绿色(0x2DE90C)。
当再次以红色(0x2553F3)画线时,绿色部分消失了,还原为完整的绿色矩形框,这是因为 绿色(0x2DE90C) XOR 红色(0x2553F3) = 黄色(0x08BAFF)。
四、完整的范例
来一个相对完整的范例吧,就是钟表程序,三个表针用的都是 XOR 方式绘制,请大家运行体会一下 XOR 的作用:
#include <graphics.h>
#include <conio.h>
#include <math.h>
#define PI 3.14159265359
void Draw(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 = 320 + (int)(120 * sin(a_sec));
y_sec = 240 - (int)(120 * cos(a_sec));
x_min = 320 + (int)(100 * sin(a_min));
y_min = 240 - (int)(100 * cos(a_min));
x_hour= 320 + (int)(70 * sin(a_hour));
y_hour= 240 - (int)(70 * cos(a_hour));
// 画时针
setlinestyle(PS_SOLID, 10, NULL);
setlinecolor(WHITE);
line(320, 240, x_hour, y_hour);
// 画分针
setlinestyle(PS_SOLID, 6, NULL);
setlinecolor(LIGHTGRAY);
line(320, 240, x_min, y_min);
// 画秒针
setlinestyle(PS_SOLID, 2, NULL);
setlinecolor(RED);
line(320, 240, x_sec, y_sec);
}
int main()
{
initgraph(640, 480); // 初始化 640 x 480 的绘图窗口
// 绘制一个简单的表盘
circle(320, 240, 2);
circle(320, 240, 60);
circle(320, 240, 160);
outtextxy(296, 310, _T("BestAns"));
// 设置 XOR 绘图模式
setrop2(R2_XORPEN); // 设置 XOR 绘图模式
// 绘制表针
SYSTEMTIME ti; // 定义变量保存当前时间
while(!_kbhit()) // 按任意键退出钟表程序
{
GetLocalTime(&ti); // 获取当前时间
Draw(ti.wHour, ti.wMinute, ti.wSecond); // 画表针
Sleep(1000); // 延时 1 秒
Draw(ti.wHour, ti.wMinute, ti.wSecond); // 擦表针(擦表针和画表针的过程是一样的)
}
closegraph(); // 关闭绘图窗口
return 0;
}
五、作业
最后给出的绘制时钟的例子,很不完善,有不少问题。请完善该程序。例如样式上,表盘上没有刻度,没有数字,指针靠中心的一端应该长出来一点点,表盘太简单。还有就是尝试发现并改进功能实现上的问题。
#include <conio.h>
#include<stdio.h>
#include <math.h>
int main()
{
initgraph(640, 480);
SYSTEMTIME ti;
int x = 320,y=0;
{
setlinecolor(YELLOW);
setfillcolor(RED);
fillcircle(x, 240, 20); }
char c = 0;
int w , a , s , d ;
float sec,hour,min;
while (c != 27)
{
GetLocalTime(&ti);
TCHAR second[10],m[10],h[15];
_stprintf_s(second, _T(" %dsecond"), ti.wSecond);
_stprintf_s(m, _T(" %dminute"), ti.wMinute );
_stprintf_s(h, _T("Time is%d hour"), ti.wHour);
hour = ((ti.wHour-6) / 24.) * 6.28;
min = ((ti.wMinute-15) / 60.) * 6.28;
sec = ((ti.wSecond-15) / 60.) * 6.28;
if (_kbhit())
c = _getch();
setlinecolor(BLACK);
setfillcolor(BLACK);
fillcircle(x, 240 + y, 50);
if ((x>=40)&&(x<=600)&&(y<=200)&&(y>=-200))
{
{if ((x == 40) && (y == -200))
{
w = 0, a = 0, s = 1, d = 1;
}
else if (x == 40 && y == 200)
{
w = 1, a = 0, s = 0, d = 1;
}
else if (x == 600 && y == -200)
{
w = 0, a = 1, s = 1, d = 0;
}
else if (x == 600 && y == 200)
{
w = 1, a = 1, s = 0, d = 0;
}
else if (x == 40)
{
w = 1, a = 0, s = 1, d = 1;
}
else if (x == 600)
{
w = 1, a = 1, s = 1, d = 0;
}
else if (y == 200)
{
w = 1, a = 1, s = 0, d = 1;
}
else if (y == -200)
{
w = 0, a = 1, s = 1, d = 1;
}
else
{
w = 1, a = 1, s = 1, d = 1;
}}
switch (c)
{
case 'a': x -= 5 * a ; break;
case 'd': x += 5*d; break;
case 's':y += 5*s; break;
case'w':y -= 5*w; break;
}
}
setlinecolor(YELLOW);
setfillcolor(RGB(y, 0, x));
fillcircle(x, 240+y, 40);
line(x + cos(sec) * 34, (240 + y) + sin(sec) * 34, x, 240 + y);
line(x + cos(hour) * 20, (240 + y) + sin(hour) * 20, x, 240 + y);
line(x + cos(min) * 27, (240 + y) + sin(min) * 27, x, 240 + y);
outtextxy(x-2,195+y, '1');
outtextxy(x +2, 195 + y, '2');
outtextxy(x - 3, 270 + y, '6');
outtextxy(x + 34,233 + y, '3');
outtextxy(x - 43, 233 + y, '9');
outtextxy(165, 40, second);
outtextxy(99, 40,m);
outtextxy(00, 40, h);
Sleep(17);
}
}
应该是:while (!_kbhit())