【分形算法】Jason带你飞之1——LS文法的java实现

      最近老师叫研究分形算法,还提供了一个以前的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度,那么画出来的图就是:

23155617_gTNa.gif

    其中蓝色的线条是画图指令画出的图。

开始的时候画图状态为(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.

转载于:https://my.oschina.net/SnifferApache/blog/353418

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值