EGE——罗盘时钟_ege画旋转表盘指针-程序员宅基地

技术标签: 算法  c语言  开发语言  

罗盘时钟

完美复刻抖音上的罗盘时钟。~~~

#include <stdio.h>
#include <graphics.h>
#include <math.h>
#include <time.h>
#include <string.h>
#define windwidth 640				//窗口宽度
#define windhigh  480				//窗口高度
#define pai 3.1415926				//定义π的值	
#define MAXSIZE 10					//定义文字长度
#define MAXNUM 60					//定义分钟和秒的数组最大长度

/*此处用sin函数实现初始化时分秒表盘的变速展开,y=sin(0到180),
  y值的和为展开的速度;也可以用抛物线方程实现*/
#define Ch 1.909084					//每45度sin值的和与15度的比值,用于实现时针变速展开;360/24=15
#define Cm 3.180190					//每30度sin值的和与6度的比值,用于实现分针变速展开;360/60=6
#define Cs 2.117701					//每20度sin值的和与6度的比值,用于实现秒针的变速展开;
#define eps 1e-8					//定义浮点数比较精度,用于比较两个浮点数是否相等
#define Equ(a,b) fabs((a)-(b))<(eps)//定义判断浮点数是否相等的宏
typedef struct _node{				//声明表盘结构体
	char time[MAXSIZE];				//存放需要输出汉字时间的字符串
	double x;						//输出汉字时间的水平位置
	double y;						//输出汉字时间的垂直位置
	double angle;					//当下时间的角度,x轴正方向为0(或者为360),顺时针递增
	int c;							//输出位置距离原点的距离
}Node;

Node sec[MAXNUM];					//定义秒针数组
Node min[MAXNUM];					//定义分针数组
Node hou[MAXNUM];					//定义时针数组
/*该函数返回180度内sin值的和与S的比值,参数S为需要旋转的角度,用于实现变速旋转*/
double C_sin(double S){				
	double f=0,sum=0;
	for(int i=0;i<=20;i++){			//20控制旋转速度
		f=sin(pai/180*i*9);
		sum+=f;
	}
	return sum/S;
}
/*初始化表盘结构体*/
void init_time(){
	char num[][MAXSIZE]={"零","一","二","三","四","五","六","七","八","九","十","十一","十二",\
		"十三","十四","十五","十六","十七","十八","十九","二十","二十一","二十二","二十三",\
		"二十四","二十五","二十六","二十七","二十八","二十九","三十","三十一","三十二","三十三",\
		"三十四","三十五","三十六","三十七","三十八","三十九","四十","四十一","四十二","四十三","四十四",\
		"四十五","四十六","四十七","四十八","四十九","五十","五十一","五十二","五十三","五十四","五十五",\
		"五十六","五十七","五十八","五十九"};
	for(int i=0;i<MAXNUM;i++){
		strcpy(sec[i].time,num[i]);
		strcat(sec[i].time,"秒");
		sec[i].c=windhigh/2+40;
		sec[i].x=windhigh/2+40+windwidth/2;
		sec[i].y=windhigh/2;
		sec[i].angle=360;
		
		strcpy(min[i].time,num[i]);
		strcat(min[i].time,"分");
		min[i].c=windhigh/2-40;
		min[i].x=windhigh/2-40+windwidth/2;
		min[i].y=windhigh/2;
		min[i].angle=360;
		
		if(i<24){
			strcpy(hou[i].time,num[i]);
			strcat(hou[i].time,"时");
			hou[i].c=windhigh/2-120;
			hou[i].x=windhigh/2-120+windwidth/2;
			hou[i].y=windhigh/2;
			hou[i].angle=360;
		}
	}
}
/*初始化表盘位置,实现展开过程*/
void init_hand(int cnt){				//cnt为调用此函数的次数
	static double h=0,m=0,s=0;			//定义静态变量,h、m、s分别控制时分秒表盘展开速度
	if(cnt<=45){						//调用45次,时针展开完成,45次从0-180.每次变化4度
		h+=sin(pai/180*cnt*4);			//实现h值的变速增加,最终h/Ch=15,为两个相邻小时的间隔度数
		for(int i=0;i<MAXNUM-1;i++){	//从0时到23时,间隔逐渐增加至15.
			hou[i+1].angle=hou[i].angle-h/Ch;
		}
	}
	
	if(cnt>=30&&cnt<=60){				//调用30次,分针展开完成,30次从0-180,每次变化6度
		m+=sin(pai/180*(cnt-30)*6);		//实现m值的变速增加,最终m/Cm=6,为两个相邻分钟的间隔度数
		for(int i=1;i<MAXNUM;i++){		//从0分到59分,间隔逐渐增加至6。
			min[i].angle=min[i-1].angle-m/Cm;
		}
	}
	if(cnt>=50&&cnt<=70){				//调用20次,秒针展开完成,20次从0-180,每次变化9度
		s+=sin(pai/180*(cnt-50)*9);		//实现s值的变速增加,最终s/Cs=6,为两个相邻秒的间隔度数
		for(int i=1;i<MAXNUM;i++){		//从0秒到59秒,间隔逐渐增加至6.
			sec[i].angle=sec[i-1].angle-s/Cs;
		}
	}
}

/*实现表盘顺时针旋转*/
void spin_hand(struct tm *t){
	
	int s=t->tm_sec,m=t->tm_min,h=t->tm_hour;
	static int flag=0,n=0;
	if(flag==0){						//对表,cs、cm、ch为sin值的和与旋转角度的比值
		static double cs=C_sin(360-sec[s].angle);
		static double cm=C_sin(360-min[m].angle);
		static double ch=C_sin(360-hou[h].angle);
		double vs=sin(pai/180*n*9)/cs;	//秒针旋转速度
		double vm=sin(pai/180*n*9)/cm;	//分针旋转速度
		double vh=sin(pai/180*n*9)/ch;	//时针旋转速度
		if(n<=20){						//调用20次完成对时
			n++;
			for(int i=0;i<MAXNUM;i++){
				sec[i].angle+=vs;
				min[i].angle+=vm;
				hou[i].angle+=vh;
			}
		}else{
			flag=1;						//标记完成对时
		}
	}
	if(flag){							//进入正常时间宣传速度
		static int dx=windwidth/2,dy=windhigh/2+17;
		line(windwidth/2,windhigh/2+17,dx,dy);
		if(dx<windwidth){				//实现划线的动态效果
			dx+=80;
		}else{
			for(int i=0,k=360;i<MAXNUM;i++,k-=6,s++,m++){
				sec[s%60].angle=k;		//s为此时刻秒数,s%60保证了sec[s%60]不会越界,变化范围0-59。
				min[m%60].angle=k;		//同上
			}
			for(int i=0,k=360;i<24;i++,h++,k-=15){
				hou[h%24].angle=k;		//继续同上,不过k的变化幅度每次15度
			}
		}		
	}	
}
/*画表盘*/
void draw_hand(){
	setcolor(EGEARGB(0XFF,255,255,255));
	setbkmode(TRANSPARENT);
	LOGFONTW font;
	getfont(&font);
	font.lfHeight=16;
	
	for(int i=0;i<60;i++){
		if(i<24){
			font.lfEscapement=-hou[i].angle/0.1;	//字体的角度默认是前端翘起为正角度,单位0.1
			setfont(&font);
			hou[i].x=hou[i].c*cos(hou[i].angle*pai/180)+windwidth/2+0.5;//浮点数末位+0.5实现四舍五入
			hou[i].y=hou[i].c*sin(hou[i].angle*pai/180)+windhigh/2+0.5;
			settextjustify(RIGHT_TEXT,TOP_TEXT);
			xyprintf(hou[i].x,hou[i].y,hou[i].time);
		}
		
		font.lfEscapement=-min[i].angle/0.1;
		setfont(&font);
		min[i].x=min[i].c*cos(min[i].angle*pai/180)+windwidth/2+0.5;
		min[i].y=min[i].c*sin(min[i].angle*pai/180)+windhigh/2+0.5;
		xyprintf((int)min[i].x,(int)min[i].y,min[i].time);
		
		font.lfEscapement=-sec[i].angle/0.1;
		setfont(&font);
		sec[i].x=sec[i].c*cos(1.0*sec[i].angle*pai/180)+windwidth/2+0.5;
		sec[i].y=sec[i].c*sin(1.0*sec[i].angle*pai/180)+windhigh/2+0.5;
		xyprintf((int)sec[i].x,(int)sec[i].y,sec[i].time);
	}
}

void draw_bk(PIMAGE bkimg,struct tm *now){
	putimage(0,0,bkimg);
	setcolor(EGEARGB(0XFF,255,255,255));
	setbkmode(TRANSPARENT);
	setfont(16,0,"仿宋");
	settextjustify(CENTER_TEXT,TOP_TEXT);
	xyprintf(windwidth/2,windhigh/2,"%d年%d月%d日",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
}
int main(int argc,char const *argv[]){
	//初始化窗口
	setinitmode(INIT_ANIMATION,200,200);
	setcaption("时钟");
	initgraph(windwidth,windhigh);
	//设置背景图
	PIMAGE bkimg=newimage(640,480);		
	getimage(bkimg,"2.jpg",0,0);
	init_time();
	
	//EGE高级画图,抗锯齿
	ege_enable_aa(true);
	
	//主要消息循环
	for(int cnt=0;is_run();delay_fps(60)){
		time_t t;		//定义一个time_t类型的变量t,其实也就是long int类型
		time(&t);		//time函数给t赋值,为1970年1月1日至此刻的秒数。
		struct tm *now=localtime(&t); 	//定义一个struct tm 类型的结构体指针,指向localtime函数返回的此刻时间
//		char *timeNow=ctime(&t);		//ctime函数将t转换为表达此刻时间的字符串

		cleardevice();					//清屏
		draw_bk(bkimg,now);
		if(cnt<=90){
			init_hand(cnt);
			cnt++;
		}
		else{
			spin_hand(now);
		}
		
		draw_hand();
	}
	delimage(bkimg);					//删除背景图片内存,防止内存泄露
	closegraph();
	return 0;
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_42106610/article/details/129958158

智能推荐

C语言分支和循环_c语言if else双重循环怎么用-程序员宅基地

文章浏览阅读926次,点赞24次,收藏18次。当计算机编译到if语句时会先对condition进行判断,当condition真命题时,会进入到if语句中依次向下执行语句,否则跳过if。当condition是布尔类型true即为真flase为假。我们还要知道在C语言中,数字与布尔类型的转换,非0数字代表true,0代表flase。//不加{}if就只能控制一条语句,下面各语句类似当表达式为真时,执行if语句,当表达式为假时,执行else语句。例1.输入两个数啊a,b,将较大的数输出。_c语言if else双重循环怎么用

go sync.pool优化测试-程序员宅基地

文章浏览阅读390次。最近在工作中,频繁用到一些结构体的申请,内存会频繁的进行释放和申请,于是想尝试一下sync.pool的优化效果。sync.pool,需要初始化 Pool,唯一需要的就是设置好 New 函数。当调用 Get 方法时,如果池子里缓存了对象,就直接返回缓存的对象。如果没有存货,则调用 New 函数创建一个新的对象。另外,我们发现 Get 方法取出来的对象和上次 Put 进去的对象实际上是同一个,Pool 没有做任何“清空”的处理。但我们不应当对此有任何假设,因为在实际的并发使用场景中,无法保证这种顺序,最_sync.pool优化

单片机之蜂鸣器_51单片机默认关闭蜂鸣器-程序员宅基地

文章浏览阅读2.7k次,点赞3次,收藏4次。我们很多的案例实际上就是用来检测我们的单片机上的零件等是否能正常使用,我们已经测试了很多的二极管,数码管等,对于我们的发生装置还没有一定的了解,故我们现在学习一下蜂鸣器和可变调的蜂鸣器蜂鸣器实验现象蜂鸣器初始状态是没有发声;按下按键1,则蜂鸣器开始发声再次按下按键1,蜂鸣器停止发声工作原理电路原理图:无源蜂鸣器只需改变Beep端口的电平,产生一个周期性的方波即可使蜂鸣器发生声音..._51单片机默认关闭蜂鸣器

QT+OpenGL绘制_qtgflrb-程序员宅基地

文章浏览阅读2.3k次。QT中已经集成了OpenGL的功能,下面利用OpenGL来实现第一个三角形的绘制。先看效果:#pragma once#include &lt;QOpenGLWidget&gt;#include &lt;QOpenGLFunctions&gt;#include &lt;QOpenGLShaderProgram&gt;class BBBOpenGLWgt : public QOpen..._qtgflrb

ROS 学习笔记3. launch文件的使用_launch如何读取rosparam-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏25次。launch文件的使用一、launch文件启动小海龟demo二、launch文件的各个标签的属性1.launch标签属性2.node标签属性3.include标签属性4.remap标签属性5.param标签属性6.rosparam标签属性6.1 rosparam参数加载6.2 rosparam参数导出6.3 rosparam参数删除7.group标签属性8.arg标签属性8.1arg使用参数默认值8.2arg使用动态传参一、launch文件启动小海龟demolaunch文件同时启动小海龟节点和键盘控制节_launch如何读取rosparam

UVM常用类及树形结构_uvm_component_utils-程序员宅基地

文章浏览阅读271次。本文介绍了UVM常用类及其一般用法,并给出了代码案例;讲述了UVM树形结构和一些常用方法_uvm_component_utils

随便推点

BAPI_SALESDOCU_CREATEFROMDATA1--VA01_bapisdhead1-程序员宅基地

文章浏览阅读361次。 实例1:REPORT Z_ORDER_CREATE_SPA_HBR.*----------------------------------------------------------------------** Parameters*----------------------------------------------------------------------*Parame_bapisdhead1

vue2.0 自定义组件的方法(vue组件的封装)_vue object 自定义对象中封装一个方法-程序员宅基地

文章浏览阅读799次。https://www.jb51.net/article/141458.htm这篇文章主要介绍了vue2.0 自定义组件的方法(vue组件的封装),本文通过实例代码相结合的形式给大家介绍的非常详细,需要的朋友可以参考下一、前言之前的博客聊过 vue2.0和react的技术选型;聊过vue的axios封装和vuex使用。今天简单聊聊 vue 组件的封装。vue 的ui框架现在是很多的,但是鉴于移动设备的复杂性,兼容性问题突出。像 Mint-UI 等说实话很不错了,但是坑也是不少,而且很多功能也_vue object 自定义对象中封装一个方法

Python实现一键生成微信好友头像墙_头像墙 自动生成-程序员宅基地

文章浏览阅读375次。效果图:具体代码实现如下:# -*- coding: utf-8 -*-from wxpy import *import mathfrom PIL import Imageimport os"""更多内容,请关注微信公众号:陈工的编程笔记"""# 创建头像存放文件夹def creat_filepath(): avatar_dir = os.getcwd(..._头像墙 自动生成

CISCO ASA安全应用问题集锦全集(51-69)-程序员宅基地

文章浏览阅读396次。问题编号:50提问内容: 我的pix os是7.22pix525# sh verCiscoPIXSecurityAppliance Software Version 7.2(2)但是没有DISABLE ESMTP这条命令pix525(config)# disable ?exec mode commands/options:回答内容:policy-map globa..._思科 asa inspect sip

分布式设计-程序员宅基地

文章浏览阅读439次。一、分布式知识点:分布式基础、缓存机制、消息机制、搜索机制、分布式ID、分布式锁、分布式SESSION、分布式事务、分布式分页、分布式部署、二、分布式基础知识 软件架构设计的六大原则:https://blog.csdn.net/u012562943/article/details/76110761 软件架构需要考虑的基本原则:https://blo..._分布式设计

J1939多帧_j1939多帧接收拒绝接收-程序员宅基地

文章浏览阅读2.9k次。J1939多帧_j1939多帧接收拒绝接收

推荐文章

热门文章

相关标签