zoukankan      html  css  js  c++  java
  • 仿写抖音旋转汉字时钟

          一直对抖音上的各种人脸处理算法很感兴趣,个别的我觉得目前的水平我能写个简单的实现方式,但是涉及复杂的,还是太菜了。但是之前在抖音上看到了一个用网页写的旋转汉字时钟的视频,感觉很好玩,而且我觉着写出来完全没问题,就用Pygame 和之前实现的旋转立方体的底层代码实现了一下。原视频地址找不到了,只有个模糊的印象,所以就跟着感觉走了。最终实现的东西,需要运算量较大,而且速度很慢。所以我若是有时间了,我会移植为C或者C++语言再看看。因为我的代码都是由之前的C语言改来的,所以改回去不会很难,只有麻烦不麻烦。

          另外,本文及实现的内容纯粹是为了好玩而完全自己编写的,且原视频中介绍的是用其他代码实现的,而且我也没有记录原作者是谁,无法进行感谢。但不论怎样,若是有侵权的地方望告知,会立刻进行处理。

        先上图:

            

    也能调整大小(非动态),只要修改文件中的一个全局变量就可以修改大小了:

        上一篇的 pygame实现旋转立方体 里面已经实现了点阵字库的读取和显示,当时只是写了个单个字符的,现在拓展一下,支持多个字符的就可以了。因为主要用到中文字符,也就没添加英文字符的支持。其实很简单,原理相通的。

        多字符的实现无非就是计算多字符对应点阵的坐标,然后进行投影计算就可以了。

        然后根据圆的坐标方程得出圆上某点与角度的关系,可以得到此角度下字符应该显示在什么位置,然后将旋转后的字符在此位置显示,就好了。

        当知道圆的半径和某点的角度值后,就能得到坐标关系式,如下:

       已知 圆心:(x0,y0)      半径:r   角度:a0    求圆上任意一点坐标:(x1,y1),可得公式如下:

              x1=x0+r*cos(a0 * 3.14/180)
              y1=y0+r*sin(a0 * 3.14/180)

         然后将字符串沿Z轴旋转(当前角度a0 减去 90度)的度数就能够将字符旋转到a0的角度,符合当前角度。选择适当的半径,就能够将字符调整到合适的位置,然后输出显示就可以了。

        中文字符串16x16点阵的显示:在Transform3D.py 文件中

     1 def Show3D16x16Char(font,ax,ay,az,x,y,Z_Size,frontcolor,backcolor,model=1.5,fontmultiple=1.0):
     2     '''**********************************************************/
     3     |**函数: Show3D16x16Font
     4     |**功能:显示3D的16x16字符,为从汉字库读取的字符数据,支持数千个汉字
     5     |        本函数需要 binascii  库的支持,不支持此库的环境无法运行本函数
     6     |**说明:font:欲显示的汉字,目前只支持一个字符的显示,只支持汉字的显示
     7     |        sx,sy,sz :角度值
     8     |        x,y: 欲显示的坐标位置
     9     |        Z_Size:距旋转轴的距离
    10     |        frontcolor,backcolor:颜色,前景色和背景色
    11     |        model:显示模式,只有模式为1时才填充背景色,否则只填充前景色
    12     |                     
    13     |**作者: wcc  执念执战
    14     |**时间:2019-6-3
    15     |********************************************************'''
    16     length = len(font)
    17     if length == 0:#字符数要大于一个
    18         return 
    19     if length == 1: #只支持一个字符的显示
    20         text = font
    21     else:
    22         text = font[0]
    23   
    24        
    25     
    26     m=0
    27     i=0
    28     k=0
    29     j=0
    30     XO=0
    31     YO=0
    32     #fontmultiple=1.0 #放大倍数,放到外面统一调整
    33     
    34     
    35     gMAT=[[0.0 for i in range(4)]  for n in range(4)]
    36     Point0=zuobiaostruct()
    37     Point1=zuobiaostruct()
    38     PointDis=zuobiaostruct()
    39     
    40     gMAT=structure_3D()                        #//构建单位矩阵
    41     gMAT=Translate3D(gMAT,-8,-8,-8);         #//平移变换矩阵
    42     gMAT=Scale_3D(gMAT,fontmultiple,fontmultiple,fontmultiple);                #//比例变换矩阵
    43     gMAT=Rotate_3D(gMAT,ax,ay,az);            #//旋转变换矩阵
    44     
    45     for m in range(length): #理论上就是将单个的字符延长为多个字符,最重要的就是坐标的确定和计算
    46         
    47         text=font[m]
    48         gb2312 = text.encode('gb2312')
    49         hex_str = binascii.b2a_hex(gb2312)
    50         result = str(hex_str,encoding = 'utf-8' )  #换算出汉字对应的字符地址
    51         if eval('0x' + result[:2]) <128:
    52             print("请输入中文")  #目前只支持gb2312中文字符,英文字符没加。原理一样,可以取模保存起来
    53             return
    54         else:
    55             
    56             area = eval('0x' + result[:2]) - 0xA0
    57             index = eval('0x' + result[2:]) - 0xA0 #换算为16进制地址
    58             offset = (94 * (area - 1)+ (index - 1))*32  #得出具体地址
    59             font_rect = None
    60             with open("D:/Mystudy/Python/pyGame/HZK16","rb") as f: #16x16字符集的地址,从中读取出一个字符的点阵数据
    61                 f.seek(offset)
    62                 font_rect = f.read(32)#得到32个点阵数据
    63             f.close()
    64         
    65       
    66         #gMAT=Translate3D(gMAT,8,8,8);             #//平移变换矩阵       x:调节距离中心点的位置,相当于下面Point0.z
    67         
    68         
    69         
    70         for i in range(16):
    71             for k in range(8):
    72                 temp = 0x01 << k
    73                 for j in range(2):
    74                     data=font_rect[i*2+j]  #取出数据
    75                     if data & temp == temp:
    76                         
    77                         Point0.x=16-(k+(1-j)*8)+m*16+m*2 #第m个字符的当前点的坐标
    78                         '''
    79                             每个字符16个像素,第m个字符共m*16个像素,每两个字符间的间距设为2,则m*16+m*2,前面的时实现当前点阵坐标的计算
    80                         '''
    81                         Point0.y=i #(i*8)+k
    82                         Point0.z=Z_Size        #//此参数能够改变字符距离旋转轴中心的距离
    83                         
    84                         Point1=vector_matrix_MULTIPLY(Point0,gMAT)#//矢量与矩阵相乘
    85                         PointDis=PerProject(Point1,XO,YO)       #//映射投影
    86                         Gui_Point(PointDis.x+x,PointDis.y+y,frontcolor)
    87                     else:
    88                         if model ==1: #模式为1 时才会绘制底色
    89                             Point0.x=16-(k+(1-j)*8)+m*16+m*2
    90                             Point0.y=i #(i*8)+k
    91                             Point0.z=Z_Size        #//此参数能够改变字符距离旋转轴中心的距离
    92                             
    93                             Point1=vector_matrix_MULTIPLY(Point0,gMAT)#//矢量与矩阵相乘
    94                             PointDis=PerProject(Point1,XO,YO)       #//映射投影
    95                             Gui_Point(PointDis.x+x,PointDis.y+y,backcolor)
    96      

        下面是实现旋转时钟的代码:

      1 # -*- coding: utf-8 -*-
      2 """
      3 Created on Sat Jun 29 15:22:12 2019
      4 
      5 @author: Administrator
      6 """
      7 
      8 from Transform3D import *
      9 
     10 
     11 import pygame 
     12 import time 
     13 import math
     14 
     15 fontmultiple=0.8   #字符倍数比例函数,修改次数据可以实现整体的放大和缩小,建议此数值在0.8-1.3 之间,看起来比较合适
     16 
     17 SCREEN_X_MAX = int(800*fontmultiple) #屏幕的宽和高
     18 SCREEN_Y_MAX = int(800*fontmultiple)
     19 
     20 BLACK=(0,0,0)
     21 WHITE=(255,255,255)
     22 RED=(255,0,0)
     23 GREEN=(0,255,0)
     24 BLUE=(0,0,255)
     25 
     26 ForeColor = RED #前景色和背景色
     27 BackColor = BLACK    
     28 
     29 
     30 pygame.init()
     31 screen = pygame.display.set_mode((SCREEN_X_MAX,SCREEN_Y_MAX))
     32 
     33 
     34 #myfont=pygame.font.Font(None,1)
     35 #textImage=myfont.render("test",True,WHITE)
     36 
     37 Week_zw=("","","","","","","") #周的中文
     38 Day_zw=("","","","","","","","","","","") #可用于所有需要0-10的中文的地方
     39 Mouth_day=[31,28,31,30,31,30,31,31,30,31,30,31] #月份时长表
     40 
     41 
     42 
     43 '''
     44 圆: (x-a)^2+(y-b)^2=r^2
     45     a,b:圆心坐标
     46     r:半径
     47     
     48     圆心:(x0,y0)
     49     半径:r
     50     角度:a0
     51     圆上任意一点:(x1,y1)
     52     x1=x0+r*cos(a0 * 3.14/180)
     53     y1=y0+r*sin(a0 * 3.14/180)
     54 '''
     55 def Show_Year(x0,y0,r,angel):
     56     '''
     57     显示年
     58     '''
     59     
     60     timenow=time.localtime(time.time())
     61     yearstr=Day_zw[(int)(timenow[0]/1000)]+Day_zw[int((timenow[0]%1000)/100)]+Day_zw[int((timenow[0]%100)/10)]+Day_zw[int((timenow[0]%10))]+""
     62     Show3D16x16Char(yearstr,0,0,angel-90,x0,y0,1,ForeColor,BackColor,0,fontmultiple)
     63 def Show_Week(x0,y0,r,agl):
     64     '''
     65     显示周
     66     '''
     67     timenow=time.localtime(time.time()) #获取时间
     68     for i in range(7):
     69         angel=360/7
     70        
     71         if i < timenow[6]:
     72             angel=agl-angel*(timenow[6]-i)
     73             ForeColor=RED
     74         elif i >timenow[6]:
     75             angel=agl+angel*(i-timenow[6])
     76             ForeColor=RED
     77         else :
     78             angel=agl  #当前周设为90度,即在水平方向上
     79             ForeColor=WHITE
     80             
     81         x1=x0 + r * math.sin(angel * 3.14/180) #由角度计算出当前应在的坐标点
     82         y1=y0 + r * math.cos(angel * 3.14/180)
     83         Show3D16x16Char(""+Week_zw[i],0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#输出旋转后的字符串
     84 
     85 def Show_Month(x0,y0,r,agl):
     86     '''
     87     显示月份
     88     '''
     89     timenow=time.localtime(time.time())#获取时间
     90     for i in range(1,13): #一年12个月
     91         angel=360/12
     92        
     93         if i < timenow[1]:
     94             angel=agl-angel*(timenow[1]-i) #其他月份相应得到角度推算
     95             ForeColor=RED
     96         elif i >timenow[1]:
     97             angel=agl+angel*(i-timenow[1])
     98             ForeColor=RED
     99         else :
    100             angel=agl  #当前月份设为90度,即在水平方向上
    101             ForeColor=WHITE
    102         x1=x0 + r * math.sin(angel * 3.14/180) #由角度计算出当前应在的坐标点
    103         y1=y0 + r * math.cos(angel * 3.14/180)
    104         if i>10:
    105             monthstr=Day_zw[10]+Day_zw[i%10]+""  #得出要显示的字符串
    106         else:
    107             monthstr = Day_zw[i]+""
    108         Show3D16x16Char(monthstr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#显示字符串
    109 
    110 def Show_Day(x0,y0,r,agl):
    111     '''
    112     显示日期
    113     '''
    114     timenow=time.localtime(time.time()) #获取时间
    115     if (timenow[0]%4==0 and timenow[0]%100 !=0) or (timenow[0]%400 == 0):
    116         Mouth_day[1]=29 #闰年,补全二月的时长表
    117     else:
    118         Mouth_day[1]=28
    119         
    120         #Mouth_day 为月份的时长表
    121     for i in range(1,Mouth_day[timenow[1]-1]+1):
    122         angel=360/Mouth_day[timenow[1]-1]
    123         
    124         if i < timenow[2]:
    125             angel=agl-angel*(timenow[2]-i)#其他日期相应得到角度推算
    126             ForeColor=RED
    127         elif i >timenow[2]:
    128             angel=agl+angel*(i-timenow[2])
    129             ForeColor=RED
    130         else :
    131             angel=agl  #当前日期设为90度,即在水平方向上
    132             ForeColor=WHITE
    133         x1=x0 + r * math.sin(angel * 3.14/180)#由角度计算出当前应在的坐标点
    134         y1=y0 + r * math.cos(angel * 3.14/180)
    135         if i>20:
    136             if i%10 !=0:
    137                 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10] +Day_zw[i%10]+""#得出要显示的字符串
    138             else:
    139                 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10]+""
    140         elif i>10:
    141             daystr= Day_zw[10]+Day_zw[i%10]+""
    142         else:
    143             daystr=Day_zw[i]+""
    144         Show3D16x16Char(daystr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#显示字符串
    145         
    146 
    147 def Show_Hour(x0,y0,r,agl):
    148     '''
    149     显示时
    150     '''
    151     
    152     timenow=time.localtime(time.time())#获取时间
    153 
    154     for i in range(0,24):
    155         angel=360/24
    156         
    157         if i < timenow[3]:
    158             angel=agl-angel*(timenow[3]-i)#其他时间相应得到角度推算
    159             ForeColor=RED
    160         elif i >timenow[3]:
    161             angel=agl+angel*(i-timenow[3])
    162             ForeColor=RED
    163         else :
    164             angel=agl  #当前时间设为90度,即在水平方向上
    165             ForeColor=WHITE
    166         x1=x0 + r * math.sin(angel * 3.14/180)
    167         y1=y0 + r * math.cos(angel * 3.14/180)
    168         if i>20:
    169             if i%10 !=0:
    170                 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10] +Day_zw[i%10]+""  #得到要显示的字符串
    171             else:
    172                 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10]+""
    173         elif i>10:
    174             daystr= Day_zw[10]+Day_zw[i%10]+""
    175         else:
    176             daystr=Day_zw[i]+""
    177         Show3D16x16Char(daystr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple) #显示角度计算后的字符串
    178         
    179 def Show_Min(x0,y0,r,agl):
    180     '''
    181     显示分
    182     '''
    183     
    184     timenow=time.localtime(time.time())
    185 
    186     for i in range(0,60):
    187         angel = 360/60
    188         
    189         if i < timenow[4]:
    190             angel=agl-angel*(timenow[4]-i)-(360/60/60 * timenow[5] ) #将秒数也代入角度计算中就可以得到更精确的角度偏移,而且每秒钟都会移动,好看
    191             ForeColor=RED
    192         elif i > timenow[4]:
    193             angel = agl + angel * (i-timenow[4]) - (360/60/60 * timenow[5] )
    194             ForeColor = RED
    195         else :
    196             angel = agl - (360/60/60 * timenow[5] )  #当前日期设为90度-秒钟的角度,实现动态显示
    197             ForeColor = WHITE
    198         x1 = x0 + r * math.sin(angel * 3.14/180) #由角度计算出当前应在的坐标点
    199         y1 = y0 + r * math.cos(angel * 3.14/180)
    200         if i>=20:
    201             if i%10 !=0:
    202                 daystr = Day_zw[(int)(i/10) ]+ Day_zw[10] +Day_zw[i%10]+"" #得到要显示的字符串
    203             else:
    204                 daystr = Day_zw[(int)(i/10) ]+ Day_zw[10]+""
    205         elif i>10:
    206             daystr = Day_zw[10]+Day_zw[i%10]+""
    207         else:
    208             daystr = Day_zw[i]+""
    209         Show3D16x16Char(daystr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#显示角度计算后的字符串
    210 
    211 
    212 i=0
    213 while True:
    214     for event in pygame.event.get():
    215         if event.type in (QUIT,KEYDOWN):
    216             pygame.quit()
    217             sys.exit()
    218             
    219     i+=1  #开始的动画,调整大小可以调整速度,因为没优化,所以显示速度很慢,数据大一些动画能尽早结束
    220     if i>90:
    221         i=90
    222         
    223     Show_Year(SCREEN_X_MAX/2-40*fontmultiple,SCREEN_Y_MAX/2,32,90) #显示年
    224     Show_Week(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,65*fontmultiple,180-i) #显示周
    225     Show_Month(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,110*fontmultiple,i)    #显示月份
    226     Show_Day(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,170*fontmultiple,180-i)  #显示日期
    227     Show_Hour(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,245*fontmultiple,i)     #显示时
    228     Show_Min(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,325*fontmultiple,180-i)  #显示分
    229     
    230     pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(55*fontmultiple),1) #显示几个圆
    231     pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(100*fontmultiple),1)
    232     pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(160*fontmultiple),1)
    233     pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(235*fontmultiple),1)
    234     pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(315*fontmultiple),1)
    235     
    236     pygame.display.update()
    237     screen.fill(0)  #屏幕清零
    238     #time.sleep(30/1000)
    View Code

       其他调用的代码都在 Transform3D.py文件中,可在我的另一篇博客《python+基本3D显示》中下载,然后将上面的字符串显示代码加进去,就可以了。

     --------------------------------------------2019-10-26补充----------------------------------

    时钟界面的速度很慢(尤其是刚开始的旋转那儿,很卡的,才刚能到几帧),流畅度不够。猜测是因为每个汉字的点阵都要打开文件读取一次的原因(也有可能是python本身效率低)。

    若是想加快,建议的方法如下:

      1:先试试将字体库读取如缓存,然后调用,不要每个点阵都要open一次文件

      2:将计算函数使用numpy库替换和优化

      若是还不行,可以使用C/C++改写底层的点阵读取和计算,但是这么做不如直接使用C/C++写完整的代码了......

    ------------------------------------------------------------------------------------------------------

        

    本文水平有限,内容很多词语由于知识水平问题不严谨或很离谱,但主要作为记录作用,能理解就好了,希望以后的自己和路过的大神对必要的错误提出批评与指点,对可笑的错误不要嘲笑,指出来我会改正的。 

     另外,转载使用请注明作者和出处,不要删除文档中的关于作者的注释。                                                                                                

                                                                                                 随梦,随心,随愿,恒执念,为梦执战,执战苍天!    ------------------执念执战

          

  • 相关阅读:
    使用参数化SQL语句进行模糊查找(转载)
    ASP.NET 数据绑定控件(转)
    C#把datetime类型的日期转化成其他格式方法总结
    asp.net MVC中form提交和控制器接受form提交过来的数据(转)
    图说世界编程语言排行
    Android笔记——Matrix
    设计模式——代理模式
    Android笔记——Handler Runnable与Thread的区别
    Android笔记——AsyncTask介绍
    Eclipse---java项目导入报错更改
  • 原文地址:https://www.cnblogs.com/zhinianzhizhan/p/11129717.html
Copyright © 2011-2022 走看看