BestAns

路漫漫其修远兮,吾将上下而求索

VC绘图/游戏简易教程--10:绘图中的位运算

教程总目录:https://codebus.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)

========================

位运算和绘图有什么关系?先举个例子来个感性认识:使用XOR运算可以实现擦除图形后不破坏背景,这在时钟程序中绘制表针是很有用的。稍后我们会给出这样的例子。

一、位运算的运算法则

位运算主要分 4 种:NOT、AND、OR、XOR,位运算的运算对象是二进制数(十进制要转换为二进制,计算机会自动转换)。

运算法则如下:

1. NOT

表示“取反”,将二进制位的 1 变 0、0 变 1。
C 语言用符号 ~ 表示。

如:
二进制: ~1101 = 0010
用十进制表示就是:~13 = 2

2. AND

表示“并且”,只有两数的对应二进制位都为 1,结果的二进制位才为 1;否则,结果的二进制位为 0。
C语言用符号 & 表示。

如:
二进制:1101 & 0110 = 0100
用十进制表示就是:13 & 6 = 4

3. OR

表示“或者”,两数的对应二进制位只要有一个是 1,结果的二进制位就是 1;否则,结果的二进制位为 0。
C语言用符号 | 表示。

如:
二进制:0101 | 0110 = 0111
用十进制表示就是:5 | 6 = 7

4. 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;
}

五、作业

最后给出的绘制时钟的例子,很不完善,有不少问题。请完善该程序。例如样式上,表盘上没有刻度,没有数字,指针靠中心的一端应该长出来一点点,表盘太简单。还有就是尝试发现并改进功能实现上的问题。

分享到

评论 (7) -

  • #include <graphics.h>
    #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())
    • 谢谢提醒!由于该系列教程最早是针对 vc6 写的,所以当时用的 kbhit()。新版本 vc 使用 _kbhit(),我已经更正过来了。
  •   line(50, 0, 200, 300);        // 画红色线
      _getch();              // 等待按任意键
      line(50, 0, 200, 300);        // 画红色线(XOR 方式重复画线会恢复背景图案)
      _getch();    
    为啥要俩个啊?  一个和2个的运行结果一样啊。这是为啥?
    还有
    再次以红色(0x2553F3)画线时,绿色部分消失了,还原为完整的绿色矩形框,这是因为 绿色(0x2DE90C) XOR 红色(0x2553F3) = 黄色(0x08BAFF)。  那为啥还有绿色呢


    • 代码有 _getch,会让执行暂停下来。执行后你按任意键再看看呢?

添加评论