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