EasyXnote5关于批量绘图

news2024/11/20 12:30:06

专栏:EasyX图形化编程

文章目录

      • 问题引入
    • 绘制画面
    • 批量绘图解释
    • 批量绘图使用

问题引入

 之前的讲解中,我们可以发现创建的窗体在进行动画的显示时会出现闪烁现象,本节课将会一步一步探讨如何解决,可以使以后学习中的动画效果更加流畅。

  • 首先创建窗体,利用之前所学习的内容在窗体上绘制圆形,但这次是利用循环绘制出多个圆形,并且使其在窗体内不断运动,为其赋上随即初始坐标及运动方向,设置不同的运动速度,为将其全部保存在窗体内,可以设置为触碰到窗体即可反弹。

绘制画面

实现思路如下
这次设置窗体颜色为白色。

不要忘记使用cleardevice();重新粉刷窗体。

 在实现多个小球同时运行前,先看一个小球的情况,速度随机,初始运动方向随机,当碰到窗体边缘时反弹。
代码如下

int main()
{
	initgraph(800, 600);
	setorigin(400, 300);
	setaspectratio(1, -1);
	setbkcolor(WHITE);
	cleardevice();
	srand(time(NULL));
	int r = 50;
	int x = rand() % 700 - 400+50;
	int y = rand() % 500 - 300+50;
	int vx = rand() % 3 +9;
	int vy = rand() % 3 +9;
	if (x % 2 == 0)
	{
		vx = -vx;
	}
	if (y % 2 == 0)
	{
		vy = -vy;
	}
	while (1)
	{
		cleardevice();
		setfillcolor(BLACK);
		solidcircle(x, y, r);
		x += vx;
		y += vy;
		Sleep(50);
		//碰到墙壁反弹
		if (x >= 400 - r || x <= -400 + r)
		{
			vx = -vx;
		}
		if (y >= 300 - r || y <= -300 + r)
		{
			vy = -vy;
		}
	}
	closegraph();
	return 0;
}

运行后如图
在这里插入图片描述

 然而一个小球在窗体内运动是这样,要使所有小球运动起来,就需要记录每个小球的位置,方向,与绘制静态和绘制单个运动的小球不同,所有的小球都需要不断进行重新绘制,需要重复不断地计算每个小球移动后的坐标,不断记录更新。
因为每个小球有很多变量类型,我们创建出一个结构体记录小球的每个变量特征。

typedef struct
{
	int x;
	int y;
	int r;
	int vx;
	int vy;
	COLORREF color;
}ball;

创建一个结构体数组,保存每一个小圆的信息
定义小球数量方便更改看效果

#define NUM 100

创建结构体数组,保存每个小球的数据。

ball balls[NUM];

设置循环,为数组内的所有小球设置初始值

	for (int i = 0; i < NUM; i++)
	{

		int m = -400 + r;
		int n = 400 + r;
		balls[i].x = rand() % (n-m + 1) + m;//圆形的全部都不能超出窗体,为之后的碰撞改变方向做铺垫
		m = -300 + r;
		n = 300 + r;
		balls[i].y = rand() %(n-m + 1) + m;
		m = 5;
		n = 7;
		balls[i].vx = rand() % (n-m + 1) + m;//速度在5~7之间
		if (balls[i].x % 2 == 0)
		{
			balls[i].vx = -balls[i].vx;
		}
		balls[i].vy = rand() % (n-m + 1) + m;//速度在5~7之间
		if (balls[i].y % 2 == 0)
		{
			balls[i].vy = -balls[i].vy;
		}
		balls[i].color = (RGB(rand() % 256, rand() % 256, rand() % 256));
	}

这里小圆的初始坐标的位置值得一提,如果想要获得m到n的范围,假设m为-1,n为5。 n-m为6,rand()%6的范围是0~5。
rand()%(m+n+1)的范围是0~6,再加上m,就得到了我们想要的范围。
所以想要得到m到n的数据范围,可以写作rand()%(n-m+1)+m;

 开始进行绘制,利用循环绘制出所有的小球。在第二个for循环中更新小球的位置,判断每个小球是否撞击窗体,然后再次重新绘制,利用计算机的高速计算,就可以实现多个小球同时运动。
(下图调小手机亮度)

	while (1)//不断地重新更新小球
	{
		cleardevice();
		for (int i = 0; i < NUM; i++)
		{
			setfillcolor(balls[i].color);
			fillcircle(balls[i].x, balls[i].y, r);
		}
		Sleep(40);
		for (int i = 0; i < NUM; i++)
		{
			if (balls[i].y >= 300 - r || balls[i].y <= -300 + r)
			{
				balls[i].vy = -balls[i].vy;
			}
			if (balls[i].x >= 400 - r || balls[i].x <= -400 + r)
			{
				balls[i].vx = -balls[i].vx;
			}
			balls[i].x += balls[i].vx;
			balls[i].y += balls[i].vy;
		}
	}

运行代码
在这里插入图片描述

批量绘图解释

运行后会发现画面有十分严重的闪烁现象。
原因是什么呢?
我们要了解要使程序在窗体上显示图像,需要进行那些步骤

1,程序将图像放置在显示缓存区中
2,显卡将缓存区的数据绘制到屏幕上

 把程序比作快递员,图像就是我们要运送的快递,显示缓存区就是驿站,把快递放在驿站里,等待着显卡将其送给收件人,就是将图像绘制到屏幕上。
 在这里1000个小球被看作一千个快递,快递员要一次一次将每个快递放置在驿站,收件人要一个一个取走包裹,快递员要送一千次,收件人要取1000次,在生活中,快递员通常是一次性运送多个快递,收件人也会一次取出多个快递,这样不用跑那么多次节省效率。
 那么为什么画面会出现一卡一卡的现象呢,就是快递小哥太累了来不及投送某些快递,也就是程序或显卡因为要处理的太多,无法及时工作。
 我们就利用生活中一次性送多个包裹,一次性接受多个包裹的方法来解决画面闪烁问题。将100个快递打包为一个大快递,直接运送大快递,显卡只需要从快递柜中取出一个大的合并的包裹就可以了。这样就可以大大提高绘图效率。
因为是将多个数据打包后传输,所以也就称为批量绘图。
然而批量绘图== 有利也有弊==
 单次绘图的情况下,只要有一个图像传递过来,就直接将其送至显卡,显示在屏幕上,而批量绘图要等到存至100个画面信息才会将其交给显卡,时效性降低了。但大多数程序对于显示的时效性要求没有那么高,所以这种方法还是可行的。因为批量绘图被广泛的使用在各个场景中。

批量绘图使用

先在一个小的例子中解释


int main()
{
	initgraph(800, 600);
	setorigin(400, 300);
	setaspectratio(1, -1);
	setbkcolor(RED);
	cleardevice();
	setfillcolor(RGB(23, 45, 147));

	solidcircle(-320, 0, 40);
	Sleep(1000);
	solidcircle(-220, 0, 40);
	Sleep(1000);
	solidcircle(-120, 0, 40);
	Sleep(1000);
	solidcircle(-20, 0, 40);
	Sleep(1000);
	solidcircle(80, 0, 40);
	Sleep(1000);
	solidcircle(180, 0, 40);
	Sleep(1000);

	getchar();
	closegraph();
}

运行后每隔1秒才会绘制一个圆。
在这里插入图片描述
函数BeginBatchDraw
在这里插入图片描述

函数BeginBatchDraw可以开启批量绘图模式,开启批量绘图后,所有的绘图操作将不再进入显示缓存区,直到调用了EndBatchDraw函数结束批量绘图,才会将从BeginBatchDraw函数以来的所有绘图操作放入显示缓存区。

添加两个函数进入上述代码,在第二次绘制圆形后开始进行批量绘制,在最后一次绘制圆前加入结束绘制的函数。
运行后如图
在这里插入图片描述
可以发现,前两个绘制结束后,就不再进行绘制了,等了4秒后,后边的四个圆形立马就画出来了。
再介绍一个函数FlashBatchDraw();
 BeginBatchDraw函数使用后,就只能等遇到EndBatchDraw函数才将图像传至显示缓存区。太霸道了吧,FlashBatchDraw给了他一巴掌,说,遇到我FlashBatchDraw,就要把在我之前的图像全部传过去,BeginBatchDraw一看是个大佬,顿时服软,就将遇到FlashBatchDraw前捕获的图像全部放了,然而过了FlashBatchDraw,他就继续捕获图像,不让其到达显示缓存区。BeginBatchDraw是坏人,抓图像,不让走,FlashBatchDraw是侠客,放开那些图像,但没抓BeginBatchDraw,EndBatchDraw就是警察,抓了Begin,解救图像。
 将BeginBatchDraw移至第一个绘图后,EndBetchDraw移至最后,在第三个绘图后加入FlashBatchDraw,运行观察效果。
在这里插入图片描述
可以发现结果如我们所料。


回到上述画面闪烁的问题
当累计绘制1000个小球后,在将这1000个小球打包装进显示缓存区,只需要在循环前加上BeginBatchDraw函数,在一千个小球初始化后加入FlashBatchDraw函数,在绘制循环结束时,关闭批量绘图即可。
运行结果
在这里插入图片描述
由于插入图片大小有要求,所以不能录制太长的片段
代码如下,大家可自行参考

#include <stdio.h>
#include <easyx.h>
#include <stdlib.h>
#include <time.h>

typedef struct
{
	int x;
	int y;
	int vx;
	int vy;
	COLORREF color;
}ball;

#define NUM 1000
int main()
{
	initgraph(800, 600);
	setorigin(400, 300);
	setaspectratio(1, -1);
	setbkcolor(WHITE);
	cleardevice();
	srand(time(NULL));
	int r = 10;
	ball balls[NUM];
	for (int i = 0; i < NUM; i++)
	{

		int m = -400 + r;
		int n = 400 + r;
		balls[i].x = rand() % (n-m + 1) + m;//圆形的全部都不能超出窗体,为之后的碰撞改变方向做铺垫
		m = -300 + r;
		n = 300 + r;
		balls[i].y = rand() %(n-m + 1) + m;
		m = 5;
		n = 7;
		balls[i].vx = rand() % (n-m + 1) + m;//速度在5~7之间
		if (balls[i].x % 2 == 0)
		{
			balls[i].vx = -balls[i].vx;
		}
		balls[i].vy = rand() % (n-m + 1) + m;//速度在5~7之间
		if (balls[i].y % 2 == 0)
		{
			balls[i].vy = -balls[i].vy;
		}
		balls[i].color = (RGB(rand() % 256, rand() % 256, rand() % 256));
	}
	BeginBatchDraw();
	while (1)//不断地重新绘制更新小球
	{
		cleardevice();
		for (int i = 0; i < NUM; i++)
		{
			setfillcolor(balls[i].color);
			fillcircle(balls[i].x, balls[i].y, r);
		}
		FlushBatchDraw();
		Sleep(40);
		for (int i = 0; i < NUM; i++)
		{
			if (balls[i].y >= 300 - r || balls[i].y <= -300 + r)
			{
				balls[i].vy = -balls[i].vy;
			}
			if (balls[i].x >= 400 - r || balls[i].x <= -400 + r)
			{
				balls[i].vx = -balls[i].vx;
			}
			balls[i].x += balls[i].vx;
			balls[i].y += balls[i].vy;
		}
	}
	EndBatchDraw();
	getchar();
	closegraph();
	return 0;
}

本章结束,希望能给大家带来帮助,谢谢大家

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1062163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二项分布以及实现

文章目录 前言所谓二项分布就是只会产生两种结果的概率 1.概念 前言 所谓二项分布就是只会产生两种结果的概率 1.概念 下面是一个二项分布的的theano实现 import numpy as np import theano import theano.tensor as T from theano.tensor.nnet import conv from theano.ten…

【数据结构--八大排序】之快速排序

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

MySQL——使用mysqldump备份与恢复数据

目录 1.mysqldump简介 2.mysqldump备份数据 2.1 备份所有数据库 2.2 备份一个/多个数据库 2.3 备份指定库中的指定表 3.mysqldump恢复数据 3.1 恢复数据库 3.2 恢复数据表 1.mysqldump简介 mysqldump命令可以将数据库中指定或所有的库、表导出为SQL脚本。表的结构和表中…

图像压缩:Transformer-based Image Compression with Variable Image Quality Objectives

论文作者&#xff1a;Chia-Hao Kao,Yi-Hsin Chen,Cheng Chien,Wei-Chen Chiu,Wen-Hsiao Peng 作者单位&#xff1a;National Yang Ming Chiao Tung University 论文链接&#xff1a;http://arxiv.org/abs/2309.12717v1 内容简介&#xff1a; 1&#xff09;方向&#xff1a;…

代码随想录 Day11 二叉树 LeetCode T144,145,94 前中后序遍历 (递归解法)

题解及更详细解答来自于:代码随想录 (programmercarl.com) 前言: 递归三要素 确定递归函数的参数和返回值&#xff1a; 确定哪些参数是递归的过程中需要处理的&#xff0c;那么就在递归函数里加上这个参数&#xff0c; 并且还要明确每次递归的返回值是什么进而确定递归函数的返…

深度学习实战基础案例——卷积神经网络(CNN)基于MobileNetV3的肺炎识别|第3例

文章目录 前言一、数据集介绍二、前期工作三、数据集读取四、构建CA注意力模块五、构建模型六、开始训练 前言 Google公司继MobileNetV2之后&#xff0c;在2019年发表了它的改进版本MobileNetV3。而MobileNetV3共有两个版本&#xff0c;分别是MobileNetV3-Large和MobileNetV2-…

不容易解的题10.5

31.下一个排列 31. 下一个排列 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/next-permutation/?envTypelist&envIdZCa7r67M会做就不算难题&#xff0c;如果没做过不知道思路&#xff0c;这道题将会变得很难。 这道题相当于模拟cpp的next_permu…

Windows系统上使用CLion远程开发Linux程序

CLion远程开发Linux程序 情景说明Ubuntu配置CLion配置同步 情景说明 在Windows系统上使用CLion开发Linux程序&#xff0c;安装CLion集成化开发环境时会自动安装cmake、mingw&#xff0c;代码提示功能也比较友好。 但是在socket开发时&#xff0c;包含sys/socket.h头文件时&am…

浅谈CDN内容分发与全局负载均衡

CDN简介 CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c…

软件设计师_数据结构与算法_学习笔记

文章目录 6.1 数组与矩阵6.1.1 数组6.1.2 稀疏矩阵 6.2 线性表6.2.1 数据结构的定义6.2.2 顺序表与链表6.2.2.1 定义6.2.2.2 链表的操作 6.2.3 顺序存储和链式存储的对比6.2.4 队列、循环队列、栈6.2.4.2 循环队列队空与队满条件6.2.4.3 出入后不可能出现的序列练习 6.2.5 串 6…

C语言学习系列->联合体and枚举

文章目录 前言联合体概述联合体的特点联合体大小的计算优点练习 枚举概述优点使用 前言 在上一篇文章中&#xff0c;小编将结构体的学习笔记整理出来了。现在&#xff0c;小编将枚举和联合体笔记分享给大家。枚举和联合体与结构体一样&#xff0c;都是自定义类型&#xff0c;在…

竞赛 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

MySQL之逻辑备份与恢复

逻辑备份简介&#xff1a; 备份的是建表、建库、插入等操作所执行SQL语句&#xff0c;适用于中小型数据库&#xff0c;效率相对较低。 本质&#xff1a;导出的是SQL语句文件 优点&#xff1a;不论是什么存储引擎&#xff0c;都可以用mysqldump备成SQL语句 缺点&#xff1a;速度…

输入一个大写字母,程序根据输入字符在字母表的顺序位置n,输出一个高度为n的金字塔图形

python字母金字塔根据输入的字母输出一个字母金字塔输入一个大写字母&#xff0c;程序根据输入字符在字母表的顺序位置n,输出一个高度为n的金字塔图形&#xff0c;比如输入E时&#xff0c;此时 字母金字塔 # A # ABA # ABCBA # ABCDCBA # ABCDEDCBA 看到…

php单独使用think-rom数据库 | thinkphp手动关闭数据库连接

背景&#xff08;think-orm2.0.61&#xff09; 由于需要长时间运行一个php脚本&#xff0c;而运行过程并不是需要一直与数据库交互&#xff0c;但thinkphp主要是为web站点开发的框架&#xff0c;而站点一般都是数据获取完则进程结束&#xff0c;所以thinkphp没提供手动关闭数据…

Trie树(字典树)C++详解

字典树的定义 字典树是一个用来快速查找和存储字符串集合的数据结构。 字典树的形状 假设我们字典树里有以下5个单词&#xff1a; akio&#xff0c;akno&#xff0c;cspj&#xff0c;csps&#xff0c;trie 那么字典树长这样&#xff1a; trie 的结构非常好懂&#xff0c;我们…

软考高级之系统架构师之设计模式

概述 设计模式是一种通用的设计方法&#xff0c;实际开发中可能不止23种。为方便理解和应用&#xff0c;一般分为3类&#xff1a; 创建型&#xff0c;通过采用抽象类所定义的接口&#xff0c;封装系统中对象如何创建、组合等信息。工厂方法模式、抽象工厂模式、单例模式、建造…

堆的初步认识

在学习本节文章前要先了解&#xff1a;大顶堆与小顶堆&#xff1a; &#xff08;优先级队列_加瓦不加班的博客-CSDN博客&#xff09; 堆实现 计算机科学中&#xff0c;堆是一种基于树的数据结构&#xff0c;通常用完全二叉树实现。 什么叫完全二叉树&#xff1f; 答&#x…

8.Vue_Element

1 Ajax 1.1 Ajax介绍 1.1.1 Ajax概述 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2个程序&#xf…

JavaEE-文件IO操作

构造方法 一般方法&#xff0c;有很多&#xff0c;我们以下只是列举几个经常使用的 注意在上述的操作过程中&#xff0c;无论是绝对路径下的这个文件还是相对路径下的这个文件&#xff0c;都是不存在的 Reader 使用 --> 文本文件 FileReader类所涉及到的一些方法 Fil…