最近老师叫研究分形算法,还提供了一个以前的java源码(http://pan.baidu.com/s/1qWqKe5Y)供参考。但其源码设计多线程,基于Applet但是又在main中启动,设计思路有点混乱,修改过程也是问题频出。于是打算干脆在最简单AWT组件例子上,实现其思想好了,希望可以帮到对分形算法和java小程序感兴趣的同学。
了解分形几何学的理论,从计算机图形学的角度进行植物模拟的方法主要有3种,包括:
迭代函数系统(IFS)、L-System文法和递归方法[1],
其中最为灵活实用的是LS文法。{!IFS留给学术狗去研究吧,递归方法对单个问题实现简单,但是通用性不好,(http://my.oschina.net/SnifferApache/blog/336381#OSC_h1_1)便是使用递归方法实现分形算法的例子}
我们接下来要使用LS文法来实现“一棵牛逼的树”,Lets go!
1、LS文法简介
非常详尽和通俗的解释(http://my.oschina.net/SnifferApache/blog/336381#OSC_h1_2)
简单讲,就是用符号定义一些简单的动作(包括评议、旋转和辅助动作)。然后用计算机实现这些简单动作即可
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、问题来了,我们的思路是什么
很明显,如果有上面的公式1:F->FF+F(表示F可以用FF+F来替换),那么替换指定的次数之后我们可以的到一段无限长的语句L,我们把这个L交给计算机去执行,它就会画出最后的图形。{!当然,公式1替换得到的L画出来不知道是什么鬼东西,恐怕也没什么意义,但这不是我们要关心的,我们要做的是:怎么把现有的一个有意义的公式在计算机上实现出来}
2.1、有意义的替换规则
F->F[+F]F[-F]F在代码中可以用
statement=“F”;
replacement=“F[+F]F[-F]F”;
来表示。其中statement表示表达式,replacement表示替换规则。在java中用循环实现替换5次的statement应该不难吧!
现在我们假设一个实现好的画图函数paint(String statement),那直接调用paint函数就大功告成了,good,我们已经完成70%的任务了。
既然一个replacement那么简单就想到解决方案了,那如果我们有3个replacement,替换的时候随机调用其中一个replacement,这就实现了随机生成的功能。
接下来我们先实现LS文法的直接替换和随机替换的控制台输出,再实现画图功能就ok啦。
补充一点:
我们也可以使用递归思想每次替换statement的过程中调用paint来画临时的statement,最终也会完成最终的任务。
2.2、其他有意义的替换规则(参考,可跳过此节)
摘自源码L.java(http://pan.baidu.com/s/1qWqKe5Y);
首先约定一下变量含义
int pStartX =340; //起始点
int pStartY =20;
double direction_init =60; // 作画时的初始方向
double direction;
double lengthF =3.3; // 步距
double rotation =-3; // 给定转角
int StartDepth =7; //画图深度
int initDepth;
int ruleNumber =2; //规则数
String sStart; //公理;即所要画的字符串
String sRule[][]; //规则
通过下拉列表选择图形
int k=choice1.getSelectedIndex();
if(k==0){ //斜草
pStartX =340;
pStartY =20;
direction_init =60;
lengthF =3.3;
rotation =-3;
StartDepth =7;
ruleNumber =2;
sStart = "G";
sRule[0][0]="G";
sRule[0][1]="GFX[+++++GFG][-----GFG]";
sRule[1][0]="X";
sRule[1][1]="F-XF";
}
else if(k==1){ //三角形
pStartX =200;
pStartY =50;
direction_init =0;
lengthF =6;
rotation =-60;
StartDepth =6;
ruleNumber =2;
sStart = "X";
sRule[0][0]="X";
sRule[0][1]="--FXF++FXF++FXF--";
sRule[1][0]="F";
sRule[1][1]="FF";
}
else if(k==2){ //树伞
pStartX =200;
pStartY =20;
direction_init =90;
lengthF =4.5;
rotation =30;
StartDepth =5;
ruleNumber =1;
sStart = "F";
sRule[0][0]="F";
sRule[0][1]="F[+F[+F][-F]F][-F[+F][-F]F]F[+F][-F]F";
}
else if(k==3){ //有花蕾的植物
pStartX =200;
pStartY =210;
direction_init =-90;
lengthF =9.5;
rotation =-18;
StartDepth =13;
ruleNumber =4;
sStart = "K";
sRule[0][0]="S";
sRule[0][1]="[+++G][---H]FFS";
sRule[1][0]="G";
sRule[1][1]="+G[-FH]F";
sRule[2][0]="H";
sRule[2][1]="-H[+FG]F";
sRule[3][0]="K";
sRule[3][1]="FSF";
}
else if(k==4){ //枝
pStartX =200;
pStartY =5;
direction_init =90;
lengthF =1.5;
rotation =-25.7341;
StartDepth =6;
ruleNumber =1;
sStart = "F";
sRule[0][0]="F";
sRule[0][1]="F[+F]F[-F]F";
//reinit();
}
else if(k==5){ //星
pStartX =15;
pStartY =200;
direction_init =0;
lengthF =4.5;
rotation =-60;
StartDepth =5;
ruleNumber =1;
sStart = "F";
sRule[0][0]="F";
sRule[0][1]="F-F++F-F";
}
else if(k==6){ //蒲公英
pStartX =200;
pStartY =10;
direction_init =90;
lengthF =0.37;
rotation =30;
StartDepth = 10;
ruleNumber =2;
sStart = "Y";
sRule[0][0]="X";
sRule[0][1]="X[-FFF][+FFF]FX";
sRule[1][0]="Y";
sRule[1][1]="YFX[+Y][-Y]";
}
else if(k==7){ //灌木丛
pStartX =250;
pStartY =20;
direction_init =90;
lengthF =3.5;
rotation =-30;
StartDepth =6;
ruleNumber =1;
sStart = "F";
sRule[0][0]="F";
sRule[0][1]="FF-[-F+F+F]+[+F-F-F]";
}
else if(k==8){ //棕榈
pStartX =200;
pStartY =20;
direction_init =90;
lengthF =5.6;
rotation =-18;
StartDepth =12;
ruleNumber =5;
sStart = "SLFFF";
sRule[0][0]="S";
sRule[0][1]="[+++H][---G]TS";
sRule[1][0]="G";
sRule[1][1]="+H[-G]L";
sRule[2][0]="H";
sRule[2][1]="-G[+H]L";
sRule[3][0]="T";
sRule[3][1]="TL";
sRule[4][0]="L";
sRule[4][1]="[-FFF][+FFF]F";
}
else if(k==9){ //开花的草
pStartX =200;
pStartY =10;
direction_init =90;
lengthF =3;
rotation =-30;
StartDepth = 8;
ruleNumber =2;
sStart = "G";
sRule[0][0]="G";
sRule[0][1]="[+FGF][-FGF]XG";
sRule[1][0]="X";
sRule[1][1]="XFX";
}
else if(k==10){ //灌木丛
pStartX =370;
pStartY =30;
direction_init =90;
lengthF =2;
rotation =-1.2;
StartDepth =6;
ruleNumber =1;
sStart = "F";
sRule[0][0]="F";
sRule[0][1]="F[+++++++++++++++++++++++++F]-F[-------------------------F]F";
}
else if(k==11){ //杨柳
pStartX =170;
pStartY =0;
direction_init =90;
lengthF =7;
rotation =-22.5;
StartDepth =5;
ruleNumber =1;
sStart = "F";
sRule[0][0]="F";
sRule[0][1]="FF+[+F-F-F]-[-F+F+F]";
}
else if(k==12){ //Juliet集
pStartX =95;
pStartY =250;
direction_init =0;
lengthF =1;
rotation =90;
StartDepth = 17;
ruleNumber =2;
sStart = "X";
sRule[0][0]="X";
sRule[0][1]="X+YF+";
sRule[1][0]="Y";
sRule[1][1]="-FX-Y";
}
else if(k==13){ //砖墙
pStartX =30;
pStartY =40;
direction_init =0;
lengthF =13;
rotation =90;
StartDepth = 4;
ruleNumber =2;
sStart = "X";
sRule[0][0]="X";
sRule[0][1]="XFYFX+F+YFXFY-F-XFYFX";
sRule[1][0]="Y";
sRule[1][1]="YFXFY-F-XFYFX+F+YFXFY";
}
else if(k==14){ //砖砌X形
pStartX =80;
pStartY =90;
direction_init =0;
lengthF =3.5;
rotation =90;
StartDepth =4;
ruleNumber =1;
sStart = "F+F+F+F";
sRule[0][0]="F";
sRule[0][1]="F+F-F-FF+F+F-F";
}
else if(k==15){ //三角绕三角
pStartX =200;
pStartY =10;
direction_init =0;
lengthF =1.7;
rotation =-60;
StartDepth = 6;
ruleNumber =2;
sStart = "X";
sRule[0][0]="X";
sRule[0][1]="--FXF++FXF++FXF--";
sRule[1][0]="F";
sRule[1][1]="FFF";
}
else if(k==16){ //一笔迷宫
pStartX =50;
pStartY =30;
direction_init =0;
lengthF =10;
rotation =-90;
StartDepth = 6;
ruleNumber =2;
sStart = "X";
sRule[0][0]="X";
sRule[0][1]="-YF+XFX+FY-";
sRule[1][0]="Y";
sRule[1][1]="+XF-YFY-FX+";
}
else if(k==17){ //树
pStartX =200;
pStartY =10;
direction_init =90;
lengthF =0.35;
rotation =30;
StartDepth = 10;
ruleNumber =2;
sStart = "X";
sRule[0][0]="X";
sRule[0][1]="F[+X]F[-X]+X";
sRule[1][0]="F";
sRule[1][1]="FF";
}
else if(k==18){ //对称的树
pStartX =200;
pStartY =10;
direction_init =90;
lengthF =0.35;
rotation =30;
StartDepth = 10;
ruleNumber =2;
sStart = "X";
sRule[0][0]="X";
sRule[0][1]="F[+X][-X]FX";
sRule[1][0]="F";
sRule[1][1]="FF";
}
3、输出替换好的LS文法
{![2]中Python的实现真简洁,怪不得说“人生苦短,我用Python”}
代码中注释详细,不用多说了
package myFenxing.console;
/**
* @author Jason
* @Corp LLC lab
* @version original Console Output
* @reference blog.csdn.net/michael2012zhao/article/details/1494379
*/
public class randomConsoleOriginal {
/**全局变量,存储表达式和替换规则*/
String statement,replacement;
String sRule[][]; //规则
/**构造方法*/
public randomConsoleOriginal() {
//用来随机抽取,绘制随机植物
sRule = new String [10][2];
sRule[0][0]="F";
sRule[0][1]="F[+F]F[-F]F" ;
sRule[1][0]="F";
sRule[1][1]="F[+F[+F]]F[-F]";
sRule[2][0]="F";
sRule[2][1]="F+F-[-F+F-F][+F-F+F]";
}
/** 我的方法-生成最终的statement*/
//1、直接替换
public String caluStatement1(int depth){
for(;depth > 0; depth--){
String newStatement="";
for(int i=0;i< statement.length();i++){
if(statement.charAt(i)== 'F') newStatement += replacement;
else newStatement += statement.charAt(i);
//防止字符串数组爆了,其最大长度取决于字符串在常量池中的存储大小
if(newStatement.length() >= 65534){//2^16-1=65535
return newStatement;
}
}
statement = newStatement;
}
return statement;
}
//2、随机替换
public String caluStatement2(int depth){//三条规则随机选择
for(;depth > 0; depth--){
String newStatement="";
for(int i=0;i< statement.length();i++){
replacement= sRule[(int)(Math.random()*3)][1];
if(statement.charAt(i)== 'F') newStatement += replacement;
else newStatement += statement.charAt(i);
//防止字符串数组爆了
if(newStatement.length() >= 65534){//2^16-1=65535
return newStatement;
}
}
statement = newStatement;
}
return statement;
}
///* The End*/
/**Main method*/
public static void main(String args[]){
randomConsoleOriginal instance1= new randomConsoleOriginal();
instance1.statement = "F";
instance1.replacement = "F[+F]F[-F]F" ;
System.out.println(instance1.caluStatement1(5));
System.out.println(instance1.caluStatement2(5));
}
}
输出结果是这一坨:
F[+F]F[-F]F[+F[+F]F[-F]F]……
…………
4、本系列教程的目录
①LS文法(http://my.oschina.net/SnifferApache/blog/353418)
②画图功能的实现(http://my.oschina.net/SnifferApache/blog/353426)
③窗口菜单和Button事件响应(http://my.oschina.net/SnifferApache/blog/353431)
5、参考文献
[1]赵慧兰.基于分形几何学的植物图形计算机模拟[J].浙江师范大学学报,2007,30(3).
[2]第二月.LS文法构图算法.http://blog.csdn.net/michael2012zhao/article/details/1494379.