前些天看到一个使用Python写的画树的程序,感觉很好玩,就用VC实现了一下,使用GDI+画也挺简单的。
一、生成一个对话框工程
启动VS,选择新建一个工程,工程名填Tree,工程类型选对话框,点击完成。
二、新建类CDrawTree
声明4个变量和6个函数:
class CDrawTree
{
public:
CDrawTree();
CDrawTree(CWnd* pWnd);
~CDrawTree();
void DrawTree(double nStep, double nLength);
void Froward(Gdiplus::REAL nLength);
void Left(double nAngle);
void Right(double nAngle);
void SetPenColor(int r, int g, int b);
void SetPenWidth(Gdiplus::REAL nWidth);
protected:
Gdiplus::Graphics* m_pDC;
Gdiplus::Pen* m_pPen;
CWnd* m_pWnd;
CDC* m_pOrgDc;
};
构造函数:
CDrawTree::CDrawTree(CWnd* pWnd)
{
ASSERT(pWnd);
m_pWnd = pWnd;
m_pOrgDc = pWnd->GetDC();
m_pOrgDc->SetMapMode(MM_LOMETRIC);
m_pDC = Gdiplus::Graphics::FromHDC(m_pOrgDc->GetSafeHdc());
m_pDC->SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeHighQuality);
CRect rect;
pWnd->GetClientRect(rect);
m_pOrgDc->DPtoLP(rect);
Gdiplus::PointF pt = Gdiplus::PointF(rect.Width() / 2, rect.Height()+100);
m_pDC->TranslateTransform(pt.X, pt.Y);
m_pPen = new Gdiplus::Pen(Gdiplus::Color::Red, 1);
m_pDC->DrawRectangle(m_pPen, -20, -20, 40, 40);
SetPenColor(0, 0, 255);
m_pDC->DrawRectangle(m_pPen, -100, -100, 200, 200);
SetPenWidth(5);
Left(90);
}
画树的主要功能函数:
void CDrawTree::DrawTree(double nStep, double nLength)
{
int clr = -nStep * 10 + 200;
SetPenColor(60, clr, 20);
SetPenWidth(nStep);
Froward(nLength);
if (nStep > 0)
{
double b = (rand() % 150) / 10.0 + 10; // 右分支偏转角度
double c = (rand() % 150) / 10.0 + 10; // 左分支偏转角度
double d = nLength*((rand() % 25) / 100.0 + 0.7);// 下一个分支的长度
Gdiplus::GraphicsState state = m_pDC->Save();
// 画右分支
Right(b);
DrawTree(nStep - 1, d);
m_pDC->Restore(state);
// 画左分支
Left(c);
DrawTree(nStep - 1, d);
}
else
{
// 树叶
SetPenColor(100, 100 + rand() % 150, 100);
m_pDC->DrawEllipse(m_pPen, -8, -8, 16, 16);
// 落叶
if (rand() %10 > 6)
{
Gdiplus::Matrix mat;
m_pDC->GetTransform(&mat);
Gdiplus::REAL elements[6];
mat.GetElements(elements);
// 从当前角度, 转到和x轴正方向下-15度. 先要计算当前坐标的旋转角度
double nAngle = acos(elements[0]) / 3.1515926 * 180;
Right(nAngle + 15);
double dis = 800.0 * (rand() % 50) / 100.0 + 400 * (rand() % 30) / 100.0 + 200 * rand() % 20 / 100.0;
m_pDC->TranslateTransform(dis * 3, 0);
SetPenColor(150, 130+rand()%50, 150);
m_pDC->DrawEllipse(m_pPen, -5, -5, 10, 10);
}
}
}
几个辅助函数:
void CDrawTree::Left(double nAngle)
{
m_pDC->RotateTransform(nAngle);
}
void CDrawTree::Right(double nAngle)
{
m_pDC->RotateTransform(-nAngle);
}
void CDrawTree::SetPenColor(int r, int g, int b)
{
ASSERT(m_pPen);
Gdiplus::Color color(r, g, b);
m_pPen->SetColor(color);
}
画线函数:
需要特别注意的是,坐标的变换,在每次画线之前,都已经通过旋转和偏移坐标,让当前坐标处于(0,0)点,角度为0,即沿x轴坐标画线。坐标旋转和偏移具有累加性,所以画完线需要把坐标偏移到下一点处。
void CDrawTree::Froward(Gdiplus::REAL nLength)
{
m_pDC->DrawLine(m_pPen, 0.0, 0.0, nLength, 0.0);
m_pDC->TranslateTransform(nLength, 0);
}
三、初始化GDI+环境
VC程序中默认使用GDI,要使用GDI+,需要自己初始化加载一下。
在程序的 InitInstance()函数中,打开对话框前,添加如下语句加载GDI+资源:
ULONG_PTR m_gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
关闭对话框后,使用Gdiplus::GdiplusShutdown(m_gdiplusToken);释放GDI+资源。
BOOL CTreeApp::InitInstance()
{
ULONG_PTR m_gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
。。。。。。
Gdiplus::GdiplusShutdown(m_gdiplusToken);
}
四、画树
在对话框上添加一个按钮,添加按钮的响应函数,调用DrawTree画树。
#include "DrawTree.h"
void CTreeDlg::OnBnClickedButton1()
{
Invalidate();
UpdateWindow();
CDrawTree tree(this);
tree.DrawTree(12, 300);
}
五、树的效果图
由于所有变量都是随机的,每次画的数的形状都不相同。