摘要:本文先对分形理论的简要介绍,随后字符串替换算法,Lindermayer系统画图元部分规则描述及其实现三个方面详细地阐述了L系统的分形图形生成方法,还给出了生成图形算法的核心部分的vc实现。
关键词:分形 Fractal Lindermayer系统
1 分形理论简介
分形的概念是美籍法国数学家Benoit Mandelbrot 率先提出的。1967 年他在美国《科学》杂志上发表了题为《英国的海岸线有多长?》的著名论文, 1975 年他又发表了《分形:形、机遇和维数》,至此分形几何正式创立了。1982 年曼德布罗特又发表了《大自然的分形几何学》一书,从而使分形几何学再次引起了学术界的高度重视,并使分形理论进入发展的高潮。那么什么是分形呢?分形是指其组成部分与整体以某种方式相似的形,也就是说,分形一般具有自相似性,其中也包括一个对象的部分与整体具有自仿射变换关系的相似。根据上面的定义,可以得出,分形具有下面5 个基本特征或性质: ①形态的不规则性; ②结构的精细性; ③局部与整体的自相似性; ④维数的非整数性; ⑤生成的迭代性。分形几何与大自然中的各种形态是具有非常紧密的联系的,如天空的云团、植物的叶脉、海岸线的形状等,可以看出分形的形态是极其不规则的,并且具有非常精细的结构,如著名的Koch曲线等,无论把其放大多少倍,都能看到其局部与整体的相似性及精致性。
2 lindermayer系统原理---分形的字符串替换算法
20世纪50年代,著名语言学家乔姆斯基(N. Chomsky)提出了递归生成语法的方法:首先指定一个或几个初始字母和一组“生成规则”,将生成规则反复作用到初始字母和新生成的字母上,直至不能再应用规则为止,从而产生出整个语言,即对应的字母和符号。因此可以用字符串表示生成元的构成,再把字符串反迭代来生成所希望得到的分形图。其实字符替换算法本质上是上述递归算法的一种文法
表示,这种算法可以用在生成元比较明确的分形图上
在美国生物学家Lindermayer发明的一种LS文法描述方法graftal上逐渐发展起来的形式语言的一个重要分支,称之为L-system.
LS文法是一类独特的迭代过程,它的核心思想就是重写.作为一种形式化的语言,LS文法用字母表和符号串来表达生成对象的初始形式,称为公理.然后根据一组产生式重写规则,将初始的每个字符依次替换为新的字符形式,反复进行,直到最后生成图形.
3 Lindermayer系统画图元部分
Lindermayer系统(简称L系统)是另外一种分形图形生成的方法,其主要原理在具体实现过程中也可以这样叙述:设定基本简单的绘图规则,然后让计算机根据这些规则进行反复迭代,就可以生成各种各样的图形来。用L系统可以非常逼真的模拟植物的生长过程。根据规则的不同,来显示不同的图形。
首先,考察一下画图的过程。无论什么样的复杂图形,都可以把图形看成若干线条构成的,而一个线条是由起点和它的方向决定的,这样,人们复杂的画图动作可以分解为若干线条的连接组合。根据这些,我们来讨论计算机绘图。
计算机绘图也是要确定一个起始点和开始画线的方向这叫做初始状态,当画图进行到任意一个阶段的时候,我们可以用(x,y,a)这三个量来确定任意一个画图的状态,即当前的坐标x,y和当前要画线条的方向角a。然后,我们需要考虑的是状态到状态是如何转换的。我们把状态之间的转换称为动作,不难看出,仅仅用平移和旋转方向就能完成状态之间的转换。接下来,我们用符号定义一些简单的动作(包括平移、旋转和辅助动作)。
F:表示在当前的位置画一条长为l的直线段。l是由用户事先任意设定的数值,表示基本线段的长度。
+:表示逆时针旋转一个角θ,θ的数值由用户事先确定;
-:表示顺时针旋转一个角θ;
[:表示暂时保存当前的画图状态
]:表示提取出保存的画图状态。 “[”和“]”要成对的出现。
这样,确定了开始的坐标和方向,由上面符号组成的任意的一系列指令就能指导画图了。比如:FF+F,其中长度l=1,θ=90度角,开始坐标是(2,0),开始方向角是90度,那么画出来的图就是:
其中蓝色的线条是画图指令画出的图。开始的时候画图状态为(2,0,90),也就是说起点在2,0这个点,并且这个时候画图的方向是朝上的,然后开始画指令F,它的意思是方向不变,往前走1个步长并且画线连接上起始的点和下一个将要移动到的点(2,1),因此画图机器就往正上方画了一条蓝色的长度为1的线段,并且把当前的状态改为了(2,1,90)就是说坐标移动到了2,1这个点,而方向角没变还是垂直向上。接下来画下一个F,仍然是朝上方画一个长度为1的线段。然后是+表示画图状态的方向逆时针旋转90度,然后这个时候的状态变为(2,2,180),就是说坐标为(2,2)方向朝左方。然后再画一个F,就是往左画一个小线段状态改为(1,2,90),到此画图命令FF+F执行结束。综合起来,我们能得到下面的表:
指令 |
状态 |
画图动作 |
起始时刻 |
(2,0,90) |
无 |
F |
(2,1,90) |
向上画一条长为1的线段 |
F |
(2,2,90) |
向上画一条长为1的线段 |
+ |
(2,2,180) |
逆时针旋转90度,不画图 |
F |
(1,2,180) |
向左画一条长为1的线段 |
4 Lindermayer系统的规则描述及其实现
规则是形如X->Y的式子,X叫做左件,Y叫做右件。X->Y表示X能够推导出Y。如果X是一个字符串,Y也是字符串,那么X->Y表示能够用Y来替换X。例如如果给定一个初始时刻的字符串ABXXTT,那么运用规则X->Y就能把这个字符串变成ABYYTT。如果产生式的右件多于一个字符,那么就能推导出比原来字符串更长的字符串来。例如如果X->YYY,那么ABXTT会被替换成ABYYYTT,显然后来的字符串比原来的长。我们已经看到了从简单的字符串生成复杂字符串的可行性了。
接下来,要说明的是产生式可以进行嵌套的表示,比如说X->XY就是一种嵌套的形式。因为当你用右件XY代替前件X的时候产生的新字符又会产生X,而X又可以运用规则X->XY,这样可以无限次的迭代下去。
有了产生式就不难理解产生式系统了,它就是由若干个产生式构成的一组语句。并且各个产生式之间可以相互替换字符串。比如如下的产生式系统:X->YF, Y->+FX,开始时刻的字符串是X,用这两个规则迭代1次就能得到字符串:+FXF,迭代2次就是+F+FXFF,3次是+F+F+FXFFF,……。(这里迭代一次表示的是用产生式系统中的所有产生式规则都来替换当前的字符串)。我们已经看到,最后的式子就是形如我们上面列举的指令例子,如果把最后的X忽略掉,这个指令串就能指导机器画图了!
常见的几种图形的规则描述:
(1) 斜草
G
G-àGFX[+++++GFG][-----GFG]
X-àF-XF
(2) 树枝
F
FàF[+F]F[-F]F
(3) 星星
FàF-F++F-F
(4) 蒲公英
Y
XàX[-FFF][+FFF]FX
YàYFX[+Y][-Y]
其他的规则可参看源代码
5 Lindermayer系统的设计与实现
该演示系统的核心在于画图指令的生成部分,包括具体图形对应的LS文法的描述和迭代替换过程。
//设计保存规则的数据结构
struct SRule {
CString left;
CString right;
};
//得到画图指令
strDraw = strStart;
for (int i=0;i
{
for (int j=0;j<6 && !rule[j].right.IsEmpty();j++)
{
strDraw.Replace(rule[j].left,rule[j].right) ;
}
}
//分析画图指令
while (i
{
switch(strDraw[i]){
case 'F': DrawF(curState,pDC);break;
case '[': Push(curState);break;
case ']': curState=Pop();break;
case '+': curState.angle = AntiClockwise(curState.angle);break;
case '-': curState.angle = Clockwise(curState.angle);break;
default:break;
}
i++;
}
6 结论
分形虽然是一门新兴学科,但它已经激发了各个领域的科学家们的极大兴趣,在许多领域开展了应用探索。其应用遍及数学、物理、计算机科学乃至艺术等领域。。随着分形技术和计算机技术的快速结合及迅速发展,其应用必将深入到人类生活的各个方面。
而作为分形理论的一部份,本文介绍的Lindermayer系统中的规则系统说明了如何产生复杂的符号串序列,而符号串序列又是如何指导画图的。整个过程都体现了复杂系统的思想,也就是简单的规则孕育着复杂的行为。如果我们完全把图形上画一条线段理解成植物枝干的一次生长,那么运用L系统的方法我们不难生成类似于真正植物的人工植物,这样我们就可模拟植物的形态。
参考文献
[1] 电脑花园
[2] 孙洪军 赵丽红 分形理论的产生及其应用 辽宁工学院学报 2005.4
[3] 徐淑平 李春明 分形图的生成算法研究 微机发展 2005.9
[4] 胡竭安 胡纪阳 徐树公.分形的计算机图象及其应用 中国铁道出版社 1995.
注:部分源代码
// LindermayerSystemDlg.cpp : implementation file
//
#include "stdafx.h"
#include "LindermayerSystem.h"
#include "LindermayerSystemDlg.h"
#include "SettingDlg.h"
#include
#include
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLindermayerSystemDlg dialog
CLindermayerSystemDlg::CLindermayerSystemDlg(CWnd* pParent /*=NULL*/)
: CDialog(CLindermayerSystemDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CLindermayerSystemDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
top = 0;
}
void CLindermayerSystemDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLindermayerSystemDlg)
DDX_Control(pDX, IDC_COMBO_SELECT, m_comboSel);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CLindermayerSystemDlg, CDialog)
//{{AFX_MSG_MAP(CLindermayerSystemDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_CBN_SELCHANGE(IDC_COMBO_SELECT, OnSelchangeCombo)
ON_BN_CLICKED(IDC_BUTTON_SETTING, OnSetting)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLindermayerSystemDlg message handlers
BOOL CLindermayerSystemDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
for (int i = 0; i< 6;i++)
{
rule[i].left=rule[i].right="";
}
m_comboSel.SetCurSel(0); //斜草
dOriX =400;
dOriY =400;
dOriAngle =- 300;
dStep =3.3;
dAngle =-3;
nIterator =5;
strStart = "G";
rule[0].left="G";
rule[0].right="GFX[+++++GFG][-----GFG]";
rule[1].left="X";
rule[1].right="F-XF";
curState.x = dOriX;
curState.y = dOriY;
curState.angle = dOriAngle;
srand((unsigned int)time(NULL));
GenerateStr();
return TRUE; // return TRUE unless you set the focus to a control
}
void CLindermayerSystemDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CLindermayerSystemDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
CWnd* pWnd = GetDlgItem(IDC_RECT_DRAW);
CRect rect;
pWnd->GetClientRect(&rect);
CDC *pControlDC=pWnd->GetDC();
pWnd->Invalidate();
pWnd->UpdateWindow();
CPen pen(PS_SOLID, 1, RGB(0,255,0));
pControlDC->SelectStockObject(BLACK_BRUSH);
pControlDC->Rectangle(0,0,rect.bottom,rect.right);
pControlDC->SelectObject(&pen);
DrawAccStr(pControlDC);
pControlDC->SelectStockObject(BLACK_PEN);
pWnd->ReleaseDC(pControlDC);
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CLindermayerSystemDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CLindermayerSystemDlg::OnSelchangeCombo()
{
// TODO: Add your control notification handler code here
int nSel;
nSel = m_comboSel.GetCurSel();
CString strTemp;
strTemp.Format("%d",nSel);
MessageBox(strTemp);
if(nSel==0){ //斜草
dOriX =400;
dOriY =400;
dOriAngle =300;
dStep =3.3;
dAngle =-3;
nIterator =5;
strStart = "G";
rule[0].left="G";
rule[0].right="GFX[+++++GFG][-----GFG]";
rule[1].left="X";
rule[1].right="F-XF";
}
else if(nSel==1){
dOriX =200;
dOriY =350;
dOriAngle =360;
dStep =6;
dAngle =60;
nIterator =5;
strStart = "X";
rule[0].left="X";
rule[0].right="--FXF++FXF++FXF--";
rule[1].left="F";
rule[1].right="FF";
}else if(nSel==2){
dOriX =200;
dOriY =210;
dOriAngle =270;
dStep =9.5;
dAngle =18;
nIterator =5;
strStart = "K";
rule[0].left="S";
rule[0].right="[+++G][---H]FFS";
rule[1].left="G";
rule[1].right="+G[-FH]F";
rule[2].left="H";
rule[2].right="-H[+FG]F";
rule[3].left="K";
rule[3].right="FSF";
}
else if(nSel==3){ //枝
dOriX =200;
dOriY =395;
dOriAngle =90;
dStep =1.5;
dAngle =25.7341;
nIterator =5;
strStart = "F";
rule[0].left="F";
rule[0].right="F[+F]F[-F]F";
}
else if(nSel==4){ //星
dOriX =15;
dOriY =200;
dOriAngle =0;
dStep =4.5;
dAngle =60;
nIterator =5;
strStart = "F";
rule[0].left="F";
rule[0].right="F-F++F-F";
}
else if(nSel==5){ //蒲公英
dOriX =200;
dOriY =10;
dOriAngle =90;
dStep =0.37;
dAngle =-30;
nIterator = 5;
strStart = "Y";
rule[0].left="X";
rule[0].right="X[-FFF][+FFF]FX";
rule[1].left="Y";
rule[1].right="YFX[+Y][-Y]";
}
else if(nSel==6){ //灌木丛
dOriX =150;
dOriY =380;
dOriAngle =90;
dStep =3.5;
dAngle =30;
nIterator =5;
strStart = "F";
rule[0].left="F";
rule[0].right="FF-[-F+F+F]+[+F-F-F]";
}
else if(nSel==7){ //棕榈
dOriX =200;
dOriY =380;
dOriAngle =90;
dStep =5.6;
dAngle =18;
nIterator =12;
strStart = "SLFFF";
rule[0].left="S";
rule[0].right="[+++H][---G]TS";
rule[1].left="G";
rule[1].right="+H[-G]L";
rule[2].left="H";
rule[2].right ="-G[+H]L";
rule[3].left ="T";
rule[3].right ="TL";
rule[4].left ="L";
rule[4].right ="[-FFF][+FFF]F";
}
else if(nSel==8){ //开花的草
dOriX =200;
dOriY =390;
dOriAngle =90;
dStep =3;
dAngle =30;
nIterator = 5;
strStart = "G";
rule[0].left="G";
rule[0].right="[+FGF][-FGF]XG";
rule[1].left="X";
rule[1].right="XFX";
}
// else if(nSel==9){ //灌木丛
// dOriX =370;
// dOriY =30;
// dOriAngle =90;
// dStep =2;
// dAngle =-1.2;
// nIterator =6;
// strStart = "F";
// rule[0].left="F";
// rule[0].right="F[+++++++++++++++++++++++++F]-F[-------------------------F]F";
// }
// else if(nSel==10){ //杨柳
// dOriX =170;
// dOriY =0;
// dOriAngle =90;
// dStep =7;
// dAngle =-22.5;
// nIterator =5;
// strStart = "F";
// rule[0].left="F";
// rule[0].right="FF+[+F-F-F]-[-F+F+F]";
// }
// else if(nSel==11){ //Juliet集
// dOriX =95;
// dOriY =250;
// dOriAngle =0;
// dStep =1;
// dAngle =90;
// nIterator = 17;
// strStart = "X";
// rule[0].left="X";
// rule[0].right="X+YF+";
// rule[1].left="Y";
// rule[1].right="-FX-Y";
// }
// else if(nSel==12){ //砖墙
// dOriX =30;
// dOriY =40;
// dOriAngle =0;
// dStep =13;
// dAngle =90;
// nIterator = 4;
// strStart = "X";
// rule[0].left="X";
// rule[0].right="XFYFX+F+YFXFY-F-XFYFX";
// rule[1].left="Y";
// rule[1].right="YFXFY-F-XFYFX+F+YFXFY";
// }
// else if(nSel==13){ //砖砌X形
// dOriX =80;
// dOriY =90;
// dOriAngle =0;
// dStep =3.5;
// dAngle =90;
// nIterator =4;
// strStart= "F+F+F+F";
// rule[0].left="F";
// rule[0].right="F+F-F-FF+F+F-F";
// }
// else if(nSel==14){ //三角绕三角
// dOriX =200;
// dOriY =10;
// dOriAngle =0;
// dStep =1.7;
// dAngle =-60;
// nIterator = 6;
// strStart = "X";
// rule[0].left=="X";
// rule[0].right="--FXF++FXF++FXF--";
// rule[1].left=="F";
// rule[1].right="FFF";
// }
// else if(nSel==15){ //一笔迷宫
// dOriX =50;
// dOriY =30;
// dOriAngle =0;
// dStep =10;
// dAngle =-90;
// nIterator = 6;
// strStart = "X";
// rule[0].left="X";
// rule[0].right="-YF+XFX+FY-";
// rule[1].left="Y";
// rule[1].right="+XF-YFY-FX+";
// }
// else if(nSel==16){ //树
// dOriX =200;
// dOriY =10;
// dOriAngle =90;
// dStep =0.35;
// dAngle =30;
// nIterator = 10;
// strStart = "X";
// rule[0].left="X";
// rule[0].right="F[+X]F[-X]+X";
// rule[1].left="F";
// rule[1].right="FF";
// }
// else if(nSel==17){ //对称的树
// dOriX =200;
// dOriY =10;
// dOriAngle =90;
// dStep =0.35;
// dAngle =30;
// nIterator = 10;
// strStart = "X";
// rule[0].left="X";
// rule[0].right="F[+X][-X]FX";
// rule[1].left="F";
// rule[1].right="FF";
// }
GenerateStr();
OnPaint();
}
void CLindermayerSystemDlg::OnSetting()
{
// TODO: Add your control notification handler code here
CSettingDlg settingDlg;
settingDlg.m_dAngle = dAngle;
settingDlg.m_dOriAngle = dOriAngle;
settingDlg.m_dOriX = dOriX;
settingDlg.m_dOriY = dOriY;
settingDlg.m_dStep = dStep;
settingDlg.m_nIterator = nIterator;
settingDlg.m_strRule0Left = rule[0].left;
settingDlg.m_strRule1Left = rule[1].left;
settingDlg.m_strRule2Left = rule[2].left;
settingDlg.m_strRule3Left = rule[3].left;
settingDlg.m_strRule4Left = rule[4].left;
settingDlg.m_strRule5Left = rule[5].left;
settingDlg.m_strRule0Right = rule[0].right;
settingDlg.m_strRule1Right = rule[1].right;
settingDlg.m_strRule2Right = rule[2].right;
settingDlg.m_strRule3Right = rule[3].right;
settingDlg.m_strRule4Right = rule[4].right;
settingDlg.m_strRule5Right = rule[5].right;
UpdateData(FALSE);
if (IDOK == settingDlg.DoModal()) {
dAngle =settingDlg.m_dAngle;
dOriAngle=settingDlg.m_dOriAngle;
dOriX =settingDlg.m_dOriX;
dOriY =settingDlg.m_dOriY;
dStep =settingDlg.m_dStep;
nIterator=settingDlg.m_nIterator;
rule[0].left = settingDlg.m_strRule0Left;
rule[1].left = settingDlg.m_strRule1Left;
rule[2].left = settingDlg.m_strRule2Left;
rule[3].left = settingDlg.m_strRule3Left;
rule[4].left = settingDlg.m_strRule4Left;
rule[5].left = settingDlg.m_strRule5Left;
rule[0].right =settingDlg.m_strRule0Right;
rule[1].right =settingDlg.m_strRule1Right;
rule[2].right =settingDlg.m_strRule2Right;
rule[3].right =settingDlg.m_strRule3Right;
rule[4].right =settingDlg.m_strRule4Right;
rule[5].right =settingDlg.m_strRule5Right;
Invalidate(TRUE);
}
}
void CLindermayerSystemDlg::GenerateStr()
{
//initialize the Producer's front
strDraw = strStart;
for (int i=0;i
for (int j=0;j<6 && !rule[j].right.IsEmpty();j++)
{
strDraw.Replace(rule[j].left,rule[j].right) ;
}
}
}
void CLindermayerSystemDlg::DrawAccStr(CDC* pDC)
{
curState.x = dOriX;
curState.y = dOriY;
curState.angle = dOriAngle;
pDC->MoveTo(curState.x,curState.y);
int i = 0;
while (i
switch(strDraw[i]){
case 'F': DrawF(curState,pDC);break;
case '[': Push(curState);break;
case ']': curState=Pop();break;
case '+': curState.angle = AntiClockwise(curState.angle);break;
case '-': curState.angle = Clockwise(curState.angle);break;
default:break;
}
i++;
}
}
void CLindermayerSystemDlg::DrawF(State& state,CDC* pDC)
{
CPoint pt;
double length;
length = GetLength();
pt.x=state.x+length*cos(state.angle);
pt.y=state.y-length*sin(state.angle);
pDC->MoveTo(state.x,state.y);
pDC->LineTo((int)pt.x,(int)pt.y);
curState.x=pt.x;
curState.y=pt.y;
}
void CLindermayerSystemDlg::Push(State &rState)
{
stack[top].x=rState.x;
stack[top].y=rState.y;
stack[top].angle=rState.angle;
top++;
}
State& CLindermayerSystemDlg::Pop()
{
if (top>0)
{
--top;
curState.x=stack[top].x;
curState.y=stack[top].y;
curState.angle=stack[top].angle;
}
else
{
MessageBox("The stack is empty!");
}
return curState;
}
double CLindermayerSystemDlg::AntiClockwise(double antiTheta)
{
double theta;
if ((rand()%10000)/10000 > 0.5)
{
theta = dAngle+dAngle*1/2*(rand()%10000)/10000;
}
else
{
theta = dAngle-dAngle*1/2*(rand()%10000)/10000;
}
return antiTheta+theta*PI/180;
}
double CLindermayerSystemDlg::Clockwise(double Theta)
{
double theta0;
if ((rand()%10000)/10000 > 0.5)
{
theta0 = dAngle+dAngle*1/2*(rand()%10000)/10000;
}
else
{
theta0 = dAngle-dAngle*1/2*(rand()%10000)/10000;
}
return Theta-theta0*PI/180;
}
double CLindermayerSystemDlg::GetLength()
{
if ((rand()%10000)/10000 > 0.5)
{
return dStep+dStep*1/2*(rand()%10000)/10000;
}
else
{
return dStep-dStep*1/2*(rand()%10000)/10000;
}
}