ACSII 动画演示:

 

不过本文介绍的是另一个作品:c 代码实现雅虎 logo ACSII 动图。

运行后,你将会看到:

 

它是一个 20fps、抗锯齿的 Yahoo! logo ASCII 动画。。

我鼓励大家研究一下代码中的常量:

       S+=V+=(1-S)/10-V/4 是该动画的欠阻尼控制系统——S 是缩放(=1/缩放倍数)、V 是速度、1/10 和 1/4 是PD常量。在第一帧中 S=0 相当于无穷放大。S<0 是非法的。

       F 是帧数计数器。1.16 控制多边形绘制的缩放比例(68 是 79/1.16 的近似值,所以也需要对它进行校正),而 136、84、92033 定义了一个椭圆。14.64 是一个不可调参数(它是 46/π 的值)。

 

抗锯齿的方法很简单:

       每个字符由三组垂直排列的样本和一个
8 字符的查找表(用于为三组打开或者关闭的排列中的每组提供像素)组成。每一帧包含 73×24 个字符或者 73×72 个像素。水平方向使用
73 列这个取值有些随意;我预测可以增加到 79(译注:一般终端一行可以容纳 80 个像素,去掉一个换行符就剩下 79)。
通过一种相当简洁的方法,亚像素精度和无帧缓冲,将 logo
绘制成一个椭圆和八个凸多边形。它需要一些设计上的权衡来生成两个可打印的字符数组,但是这比起绘制三角形到帧缓冲所需的代码要更少,这也是多边形栅格化的典型做法。

       为了制作上述的图案,首先需要把 logo “Y!”向量化。我通过测量基准图和在坐标纸上画出坐标实现 logo “Y!”的向量化。然后,我写了一个应用程序,将点和多边形定义转换成如下定义的角度和偏移量。

       这里的椭圆是高中数学中的标准椭圆: x 2/ a 2 + y 2/ b 2 < 1。检查每个点,如果该点在椭圆内,那么绘制该像素(136 x 2 + 84 y 2 < 92033 是对 a 和 b 进行简单移项的结果,其中 a 和 b分别是从原图片测量出来、缩放到像素网格的椭圆的两个轴半径)。

       每个多边形由一系列独立的半平面组成(一个半平面即一条无限延长线的一边中所有点的集合)。如果一个点在所有半平面“里面”,那么它就处于该多边形里面(这仅仅适用于凸多边形),而该像素用异或操作符
^ 进行切换(因此不需要任何特殊的操作,就处理了椭圆里面的“反面”部分和“正面”的感叹号)。

       多边形的每条边都被定义为方程式 ax + by > c 。我使用角度 θ 来表示 a和 b ,即 a = cos( θ ) 和 b = sin( θ ),并以 π/46 的增量来量化角度——得到用 ASCII 码的 33 到 125(即 ‘!’ 到 ‘}’)来表示角度 -π 到 +π,其中角度 0 表示为 ‘O’(即 ASCII 码的 79)。然后对 c 进行求解,同样以缩放增量对 -47 到 47 进行量化,而这条边的中点也被认为在多边形内。

       下面是粗略的示意图(这是我在纸上画的。大家将就一下):

 

阴影部分是 ax + by < c ,代表在多边形外面,而虚线则是 ax + by = c 。

       ( a , b ) 构成线段的正交向量。它们指向多边形的内部,所以我们可以直接从定义线段的点得到 a 和 b ,即将定义一条边的向量 ( x1 – x0 , y1 – y0 ) 旋转 90 度,得到 ( a , b ) = ( y1 – y0 , x0 – x1 )。然后,由于实际的大小不重要,所以我们对 ( a , b ) 进行归一化。当我们求解角度时,它们会变成 1。我们在后面可以抵消 c 的取值(假设 ax + by > c ,那么对于缩放倍数 s>0,可以得到 sax + sby > sc )。接着计算 θ = atan2 ( a , b ),将其量化成我们 94 个角度中的一个,并得到 ( a , b ) = (cos( θ ), sin( θ ))。

通过直接将多边形一条边上的任意一个点代入公式 c = ax + by ,可以很容易地得到 c 。我使用边上线段的中点,即 ( xt , yt ) = (( x0 + x1 )/2, ( y0 + y1 )/2),因为在我们量化 θ 之后,边的角度会出现轻微的偏移,而这会导致超出边长的错误。

       注意到开头的几帧(你可以按 ^S 来停止,^Q 来继续——xon/xoff),由于在分离半平面方程式时出现的量化错误,导致“Y”的底部被咬掉几口。(译注:^S 即键盘的 Ctrl+S,^Q 即键盘的 Ctrl+Q)

       通过仔细地重排分离平面的数组,使得大部分绘图区被排除,可以稍微提高 CPU 的效率。我没有把这部分放到 generator 的代码中。

       动画是由序列
[25A 完成——在任意终端仿真模式下,将光标上移 25 行。严格的说我只需上移 24
行,但是 puts 比 printf 简洁,而它会隐式地添加新行。如果你的终端少于 26
行,那么它会对你的终端回滚做一些奇怪的事情。另外,usleep 用来把动画的刷新率限制到 20fps,这也是唯一一个非 ANSIC 字符的地方。

       然后通过使用精妙的 for 循环和 C 语言默认包含的不正规的逗号、条件语句和全局 int 变量的优点(这在 C 语言混乱代码中是比较常见的)。而那几乎展现出这段代码是如何实现的所有秘密。

       使用一组不同的移动序列或者旋转(再或者任意的 3D 切换,这基本上是追踪 logo 的轨迹),可以很简单地改进这段动画。我仅仅用了缩放动画来证明一点:这个 logo 是被动态地绘制,而不仅仅是一个压缩后的 logo。并且我会使动画保持简短而流畅。

       PS:文中或许有错误,向大家道歉,但是我想让你了解到了这个想法。希望对感兴趣的小伙伴们有些许帮助!

学C/C++不易,此路应携手前行。

欢迎关注我的编程公众號【草莓味狸猫】!

如果你想跟着小编一起学编程的话!

可以来我的C语言C++编程学习基地,【点击进入】

还有(源码,零基础教程,项目实战教学视频)!

 

【C语言编程学习笔记】利用462字节代码实现雅虎logo ACSII 动画!的更多相关文章

  1. Linux C语言编程学习笔记 (1)进程控制入门

    想进行Linux系统开发已经很久了,一直没有付诸实践.今日终于开始学习Linux下的C语言编程,研究一天,终于大概弄明白了Linux系统进程管理的一些基本概念和编程方法,总结下来以方便大家学习和自己实 ...

  2. linux下的c语言编程学习笔记

    视频参看csdn学院王阳和下面的linux环境下c语言编程基础相当的经典,其中王阳的视频讲的很好,相当的经典 编译hellogcc.c需要依赖/home目录下的头文件 为了避免同一个文件被includ ...

  3. 《手把手教你学C语言》学习笔记(4)---代码规范

    编程过程中需要遵守编译器的各种约定,例如以下代码: 1 #include <stdio.h> 2 3 int main(int argc, char **argv) 4 { 5 print ...

  4. Perl语言编程>>学习笔记2

    1. Perl中变量的常用表示  ${var} 相当于 $var  $Dog::days  在Dog包里面的变量$days  $#days  @days 的最后一个索引  ]  $days 引用的数组 ...

  5. Perl语言编程>>学习笔记

    1. 使用反引号可以调用外部程序并返回程序的输出, 如  $cwd = `pwd`; 2. Perl 中的变量类型之间的区别主要是单数和复数; 单数变量称为标量 $scalar , 复数变量称为数组 ...

  6. 【C语言/C++编程学习笔记】:通俗易懂讲解 - 链表!学不会?不存在的!

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  7. 转 网络编程学习笔记一:Socket编程

    题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人:但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有 ...

  8. 并发编程学习笔记(4)----jdk5中提供的原子类及Lock使用及原理

    (1)jdk中原子类的使用: jdk5中提供了很多原子类,它会使变量的操作变成原子性的. 原子性:原子性指的是一个操作是不可中断的,即使是在多个线程一起操作的情况下,一个操作一旦开始,就不会被其他线程 ...

  9. MySQL基础之事务编程学习笔记

    MySQL基础之事务编程学习笔记 在学习<MySQL技术内幕:SQL编程>一书,并做了笔记.本博客内容是自己学了<MySQL技术内幕:SQL编程>事务编程一章之后,根据自己的理 ...

随机推荐

  1. odoo10同一模型的不同视图不同群组权限控制

    先描述下需求: 一个模型定义两个calendar视图,其中A视图G1群组可以CRUD操作,但是不显示特殊字段spec_field,对于B视图G1群组只能查看,G2群组只能修改其中的特殊字段spec_f ...

  2. defer implement for C/C++ using GCC/Clang extension

    前述: go 中defer 给出了一种,延时调用的方式来释放资源.但是对于C/C++去没有内置的这种属性.对于经常手动管理内存的C/C++有其是C程序员这种特性显得无比重要.这里给出了一种基于GCC/ ...

  3. 【系统之音】Android进程的创建及启动简述

    Android系统中的进程(这里不包括init等底层的进程)都是通过Zygote fork而来的,那这些进程的启动流程都是怎样的呢? 这里将Android进程分为两个部分: (1)系统框架进程Syst ...

  4. 《Offer一箩筐》2W字总结面试套路14问——不给例子的教程都是耍流氓!!

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! Hi~ 这里是 ...

  5. outh2

    之前做天猫精灵对接,就碰到了outh鉴权,当时实现好之后没有细细缕,今天看了一个博主的介绍,贴一下 转载自http://www.ruanyifeng.com/blog/2014/05/oauth_2_ ...

  6. ribbon源码(3) 配置模块

    ribbon的很多功能可以通过配置进行调整,ribbon通过IClientConfig来获取配置信息,用户可以通过实现IClientConfig来管理配置. ribbon也提供了默认的实现(Defau ...

  7. Redis中有序列表(ZSet)相关命令

    redis语序集合和集合set是一样内部value为string类型的集合,有序不允许重复元素 但是,zset的每个元素有一个double类型的分数(score).redis正是靠这个分数对元素从小到 ...

  8. 趣图:后端工程师做 UI 的活

      扩展阅读 趣图:苦逼的后端工程师 趣图:前端 VS 后端 [趣图]开发人员腹黑的一面

  9. 刷题[SUCTF 2018]GetShell

    解题思路 进入upload,发现有源码,代码审计 代码审计 大概意思就是,读取我们上传的文件,截取第五位之后的内容,与黑名单不匹配才能上传成功 我传的是一个空的txt文件,发现会变成php文件,那好办 ...

  10. 从四个问题透析Linux下C++编译&链接

    摘要:编译&链接对C&C++程序员既熟悉又陌生,熟悉在于每份代码都要经历编译&链接过程,陌生在于大部分人并不会刻意关注编译&链接的原理.本文通过开发过程中碰到的四个典型 ...