ACSII 動(dòng)畫演示:

不過(guò)本文介紹的是另一個(gè)作品:c 代碼實(shí)現(xiàn)雅虎 logo ACSII 動(dòng)圖。
運(yùn)行后,你將會(huì)看到:

它是一個(gè) 20fps、抗鋸齒的 Yahoo! logo ASCII 動(dòng)畫。。
我鼓勵(lì)大家研究一下代碼中的常量:
?????? S+=V+=(1-S)/10-V/4是該動(dòng)畫的欠阻尼控制系統(tǒng)——S 是縮放(=1/縮放倍數(shù))、V 是速度、1/10 和 1/4 是PD常量。在第一幀中 S=0 相當(dāng)于無(wú)窮放大。S<0 是非法的。
?????? F 是幀數(shù)計(jì)數(shù)器。1.16 控制多邊形繪制的縮放比例(68 是 79/1.16 的近似值,所以也需要對(duì)它進(jìn)行校正),而 136、84、92033 定義了一個(gè)橢圓。14.64 是一個(gè)不可調(diào)參數(shù)(它是 46/π 的值)。

抗鋸齒的方法很簡(jiǎn)單:
?????? 每個(gè)字符由三組垂直排列的樣本和一個(gè) 8 字符的查找表(用于為三組打開或者關(guān)閉的排列中的每組提供像素)組成。每一幀包含 73×24 個(gè)字符或者 73×72 個(gè)像素。水平方向使用 73 列這個(gè)取值有些隨意;我預(yù)測(cè)可以增加到 79(譯注:一般終端一行可以容納 80 個(gè)像素,去掉一個(gè)換行符就剩下 79)。 通過(guò)一種相當(dāng)簡(jiǎn)潔的方法,亞像素精度和無(wú)幀緩沖,將 logo 繪制成一個(gè)橢圓和八個(gè)凸多邊形。它需要一些設(shè)計(jì)上的權(quán)衡來(lái)生成兩個(gè)可打印的字符數(shù)組,但是這比起繪制三角形到幀緩沖所需的代碼要更少,這也是多邊形柵格化的典型做法。
?????? 為了制作上述的圖案,首先需要把 logo “Y!”向量化。我通過(guò)測(cè)量基準(zhǔn)圖和在坐標(biāo)紙上畫出坐標(biāo)實(shí)現(xiàn) logo “Y!”的向量化。然后,我寫了一個(gè)應(yīng)用程序,將點(diǎn)和多邊形定義轉(zhuǎn)換成如下定義的角度和偏移量。
?????? 這里的橢圓是高中數(shù)學(xué)中的標(biāo)準(zhǔn)橢圓:x2/a2?+y2/b2?< 1。檢查每個(gè)點(diǎn),如果該點(diǎn)在橢圓內(nèi),那么繪制該像素(136x2?+ 84y2?< 92033 是對(duì)a和b進(jìn)行簡(jiǎn)單移項(xiàng)的結(jié)果,其中a和b分別是從原圖片測(cè)量出來(lái)、縮放到像素網(wǎng)格的橢圓的兩個(gè)軸半徑)。
?????? 每個(gè)多邊形由一系列獨(dú)立的半平面組成(一個(gè)半平面即一條無(wú)限延長(zhǎng)線的一邊中所有點(diǎn)的集合)。如果一個(gè)點(diǎn)在所有半平面“里面”,那么它就處于該多邊形里面(這僅僅適用于凸多邊形),而該像素用異或操作符 ^ 進(jìn)行切換(因此不需要任何特殊的操作,就處理了橢圓里面的“反面”部分和“正面”的感嘆號(hào))。
?????? 多邊形的每條邊都被定義為方程式ax+by>c。我使用角度θ來(lái)表示a和b,即a= cos(θ) 和b= sin(θ),并以 π/46 的增量來(lái)量化角度——得到用 ASCII 碼的 33 到 125(即 ‘!’ 到 ‘}’)來(lái)表示角度 -π 到 +π,其中角度 0 表示為 ‘O’(即 ASCII 碼的 79)。然后對(duì)c進(jìn)行求解,同樣以縮放增量對(duì) -47 到 47 進(jìn)行量化,而這條邊的中點(diǎn)也被認(rèn)為在多邊形內(nèi)。
?????? 下面是粗略的示意圖(這是我在紙上畫的。大家將就一下):

陰影部分是ax+by<c,代表在多邊形外面,而虛線則是ax+by=c。
(a,b) 構(gòu)成線段的正交向量。它們指向多邊形的內(nèi)部,所以我們可以直接從定義線段的點(diǎn)得到a和b,即將定義一條邊的向量 (x1–x0,y1–y0) 旋轉(zhuǎn) 90 度,得到 (a,b) = (y1–y0,x0–x1)。然后,由于實(shí)際的大小不重要,所以我們對(duì) (a,b) 進(jìn)行歸一化。當(dāng)我們求解角度時(shí),它們會(huì)變成 1。我們?cè)诤竺婵梢缘窒?i>c的取值(假設(shè)ax+by>c,那么對(duì)于縮放倍數(shù) s>0,可以得到sax+sby>sc)。接著計(jì)算θ=atan2(a,b),將其量化成我們 94 個(gè)角度中的一個(gè),并得到 (a,b) = (cos(θ),?sin(θ))。
通過(guò)直接將多邊形一條邊上的任意一個(gè)點(diǎn)代入公式c=ax+by,可以很容易地得到c。我使用邊上線段的中點(diǎn),即 (xt,yt) = ((x0+x1)/2, (y0+y1)/2),因?yàn)樵谖覀兞炕?i>θ之后,邊的角度會(huì)出現(xiàn)輕微的偏移,而這會(huì)導(dǎo)致超出邊長(zhǎng)的錯(cuò)誤。
?????? 注意到開頭的幾幀(你可以按 ^S 來(lái)停止,^Q 來(lái)繼續(xù)——xon/xoff),由于在分離半平面方程式時(shí)出現(xiàn)的量化錯(cuò)誤,導(dǎo)致“Y”的底部被咬掉幾口。(譯注:^S 即鍵盤的 Ctrl+S,^Q 即鍵盤的 Ctrl+Q)
?????? 通過(guò)仔細(xì)地重排分離平面的數(shù)組,使得大部分繪圖區(qū)被排除,可以稍微提高 CPU 的效率。我沒(méi)有把這部分放到 generator 的代碼中。
?????? 動(dòng)畫是由序列 [25A 完成——在任意終端仿真模式下,將光標(biāo)上移 25 行。嚴(yán)格的說(shuō)我只需上移 24 行,但是?puts?比?printf?簡(jiǎn)潔,而它會(huì)隱式地添加新行。如果你的終端少于 26 行,那么它會(huì)對(duì)你的終端回滾做一些奇怪的事情。另外,usleep?用來(lái)把動(dòng)畫的刷新率限制到 20fps,這也是唯一一個(gè)非 ANSIC 字符的地方。
?????? 然后通過(guò)使用精妙的?for?循環(huán)和 C 語(yǔ)言默認(rèn)包含的不正規(guī)的逗號(hào)、條件語(yǔ)句和全局?int?變量的優(yōu)點(diǎn)(這在 C 語(yǔ)言混亂代碼中是比較常見的)。而那幾乎展現(xiàn)出這段代碼是如何實(shí)現(xiàn)的所有秘密。
?????? 使用一組不同的移動(dòng)序列或者旋轉(zhuǎn)(再或者任意的 3D 切換,這基本上是追蹤 logo 的軌跡),可以很簡(jiǎn)單地改進(jìn)這段動(dòng)畫。我僅僅用了縮放動(dòng)畫來(lái)證明一點(diǎn):這個(gè) logo 是被動(dòng)態(tài)地繪制,而不僅僅是一個(gè)壓縮后的 logo。并且我會(huì)使動(dòng)畫保持簡(jiǎn)短而流暢。
?????? PS:文中或許有錯(cuò)誤,向大家道歉,但是我想讓你了解到了這個(gè)想法。希望對(duì)感興趣的小伙伴們有些許幫助!
學(xué)C/C++不易,此路應(yīng)攜手前行。
歡迎關(guān)注我的編程公眾號(hào)【草莓味貍貓】!??
如果你想跟著小編一起學(xué)編程的話!
可以來(lái)我的C語(yǔ)言C++編程學(xué)習(xí)基地,【搜索進(jìn)入】!

還有(源碼,零基礎(chǔ)教程,項(xiàng)目實(shí)戰(zhàn)教學(xué)視頻)!
