?
本篇文章我們將帶大家用C語言繪制一些漂亮的櫻花樹。
?
鼠標(biāo)位置設(shè)定櫻花樹的高度和分散程度,鼠標(biāo)右鍵點擊設(shè)置是否顯示過程動畫,鼠標(biāo)左鍵點擊開始繪制。
學(xué)習(xí)步驟
首先學(xué)習(xí)遞歸的概念,實現(xiàn)漢諾塔問題的求解;
然后學(xué)習(xí)分形的概念,并利用遞歸調(diào)用繪制一棵分形樹;
最后修改分形樹的生成與繪制參數(shù),實現(xiàn)了隨機(jī)櫻花樹的繪制。
源碼示例
?
#include#include #include #include #include #define PI 3.1415926 #define WIDTH 800 // 畫面寬度 #define HEIGHT 600 // 畫面高度度 float offsetAngle = PI/6; // 左右枝干和父枝干偏離的角度 float shortenRate = 0.65; // 子枝干比父枝干變短的倍數(shù) int isShowAnimation = 1; // 是否顯示樹生成的過程動畫 // 把[inputMin,inputMax]范圍的input變量,映射為[outputMin,outputMax]范圍的output變量 float mapValue(float input,float inputMin,float inputMax,float outputMin,float outputMax) { float output; if (abs(input-inputMin)<0.000001) // 防止除以零的bug output = outputMin; else output = (input-inputMin)*(outputMax-outputMin)/(inputMax-inputMin) + outputMin; return output; } // 生成[min,max]之間的隨機(jī)小數(shù) float randBetween(float min,float max) { float t = rand()/double(RAND_MAX); // 生成[0,1]的隨機(jī)小數(shù) // 調(diào)用mapValue函數(shù),把值范圍從[0,1]映射到[min,max] float r = mapValue(t,0,1,min,max); return r; } // 枝干生成和繪制遞歸函數(shù) // 輸入?yún)?shù):枝干起始x y坐標(biāo),枝干長度,枝干角度,枝干繪圖線條寬度,第幾代 void brunch(float x_start,float y_start,float length,float angle,float thickness,int generation) { // 利用三角函數(shù)求出當(dāng)前枝干的終點x,y坐標(biāo) float x_end,y_end; x_end = x_start+ length* cos(angle); y_end = y_start+ length* sin(angle); // 畫線條枝干 setlinestyle(PS_SOLID,thickness); // 設(shè)定當(dāng)前枝干線寬 // 設(shè)置枝干為灰褐色,主樹干最黑,子枝干逐漸變亮 COLORREF color = HSVtoRGB(15,0.75,0.4+generation*0.05); setlinecolor(color); // 設(shè)定當(dāng)前枝干顏色 line(x_start,y_start,x_end,y_end); // 畫出當(dāng)前枝干(畫線) // 求出子枝干的代數(shù) int childGeneration = generation + 1; // 生成左、右、中間三個子枝干的長度,逐漸變短,并有一定隨機(jī)性 float childLength = shortenRate*length; float leftChildLength = childLength*randBetween(0.9,1.1); float rightChildLength = childLength*randBetween(0.9,1.1); float centerChildLength = childLength*randBetween(0.8,1.1); // 當(dāng)子枝干長度大于2,并且代數(shù)小于等于10,遞歸調(diào)用產(chǎn)生子枝干 if (childLength>=2 && childGeneration<=9) { // 生成子枝干的粗細(xì),逐漸變細(xì) float childThickness = thickness*0.8; if (childThickness<2) // 枝干繪圖最細(xì)的線寬為2 childThickness = 2; // 一定概率產(chǎn)生左、右、中子枝干 if(randBetween(0,1)<0.95) brunch(x_end,y_end,leftChildLength,angle+offsetAngle*randBetween(0.5,1),childThickness,childGeneration); if(randBetween(0,1)<0.95) brunch(x_end,y_end,rightChildLength,angle-offsetAngle*randBetween(0.5,1),childThickness,childGeneration); if(randBetween(0,1)<0.85) brunch(x_end,y_end,centerChildLength,angle+offsetAngle/5*randBetween(-1,1),childThickness,childGeneration); } else // 最末端繪制櫻花,畫一個粉色填充圓 { setlinestyle(PS_SOLID,1); // 線寬 // 櫻花粉色HSVtoRGB(325,0.3,1),有一定隨機(jī)性 COLORREF color = HSVtoRGB(randBetween(300,350),randBetween(0.2,0.3),1); setlinecolor(color); // 設(shè)定線條顏色 setfillcolor(color); // 設(shè)定填充顏色 if (childLength<=4) // 如果子枝干長度小于等于4 fillcircle(x_end,y_end,2); // 圓的半徑為2(再小就看不清了) else fillcircle(x_end,y_end,childLength/2); // 畫一個圓,半徑為子枝干長度的一半 } if (isShowAnimation) // 如果為1,繪制櫻花樹生成的過程動畫 { FlushBatchDraw(); // 批量繪制 Sleep(1); // 暫停 } } void startup() // 初始化 { srand(time(0)); // 隨機(jī)初始化 initgraph(WIDTH,HEIGHT); // 新開一個畫面 setbkcolor(RGB(255,255,255)); // 白色背景 cleardevice(); // 清屏 BeginBatchDraw(); // 開始批量繪制 brunch(WIDTH/2,HEIGHT,0.45*HEIGHT*shortenRate,-PI/2,15*shortenRate,1); // 遞歸函數(shù)調(diào)用 FlushBatchDraw(); // 批量繪制 } void update() // 每幀更新 { MOUSEMSG m; if (MouseHit()) { m = GetMouseMsg(); if(m.uMsg == WM_MOUSEMOVE) // 當(dāng)鼠標(biāo)移動時,設(shè)定遞歸函數(shù)的參數(shù) { // 鼠標(biāo)從左到右,左右子枝干偏離父枝干的角度逐漸變大 offsetAngle = mapValue(m.x,0,WIDTH,PI/10,PI/4); // 鼠標(biāo)從上到下,子枝干比父枝干的長度縮短的更快 shortenRate = mapValue(m.y,0,HEIGHT,0.7,0.3); } if (m.uMsg == WM_LBUTTONDOWN) // 當(dāng)鼠標(biāo)左鍵點擊時,以當(dāng)前參數(shù)開始繪制一棵新數(shù) { cleardevice(); // 清屏 brunch(WIDTH/2,HEIGHT,0.45*HEIGHT*shortenRate,-PI/2,15*shortenRate,1); // 遞歸調(diào)用 FlushBatchDraw(); // 批量繪制 } if (m.uMsg == WM_RBUTTONDOWN) // 當(dāng)鼠標(biāo)右鍵點擊時,切換是否顯示過程動畫 { if (isShowAnimation==1) isShowAnimation = 0; else if (isShowAnimation==0) isShowAnimation = 1; } } } int main() // 主函數(shù) { startup(); // 初始化 while (1) // 重復(fù)循環(huán) update(); // 每幀更新 return 0; }
?
這一章主要講解了函數(shù)遞歸調(diào)用的語法知識,學(xué)習(xí)了分形的概念,繪制了漂亮的櫻花樹。
讀者可以參考本項目的思路,嘗試?yán)L制其他分形圖案;
應(yīng)用遞歸,讀者也可以嘗試編程解決掃雷、泡泡龍、迷宮等游戲中的相關(guān)問題。
希望對大家有幫助!
審核編輯:湯梓紅
?
評論
查看更多