不知不觉到了毕业季,回顾10多年前在海外的大学生活,感觉即特别又辛酸。这几天翻了一下大学期间完成的一个非常有意思的模拟植物生长的小软件,进行分享,给即将步入大学的众多IT行业的学子加油。
一. L-系统原理介绍
L-系统是匈牙利生物学家Aristid LinderMayer于1968年提出的。L-系统的本质是一个重写系统,通过对植物对象生长过程的经验式概括和抽象,初始状态与描述规则,进行有限次迭代,生成字符发展序列以表现植物的拓扑结构,并对产生的字符串进行几何解释,就能生成非常复杂的分形图形。
具体例子如下:雪花曲线
v:{F,+, - }
w:F
p:F->F-F++F-F
几何解释是:
F:向前画一条线
+:右转67.5度(++即为右转135度)
-:左转45度
具体信息见下图,当迭代次数n=3时就可以得出很好的雪花形状。
二.案例结果展示
本案例是在2005年圣诞节期间开发,采用Java开发语言,底层代码完全自己编写,未引用任何第三方包。
先展示一下系统的结果:
案例一:
规则:
如左图所示,
|
结果展示:
一次迭代 | 三次迭代 | 十次迭代 |
| | |
案例二
规则:
结果展示:
一次迭代 | 三次迭代 | 六次迭代 |
|
|
|
案例三
规则:
结果展示如下:
| 一次迭代 | 三次迭代 | 七次迭代 |
案例四
规则:
结果展示如下:
一次迭代 | 三次迭代 | 四次迭代 |
|
| |
案例五
规则:
结果展示如下:
一次迭代 | 三次迭代 | 五次迭代 |
|
|
|
案例6
规则:
结果展示如下:
一次迭代 | 三次迭代 | 七次迭代 |
|
|
|
三. 系统界面
这里把系统界面再介绍一下,截屏如下:
界面左侧是绘图区域.
右侧在上一章节的规则部分有截图,除去已经截图的,其他部分说明如下:
- Map Value – 选择初始公式以及规则
- Times – 迭代次数
- Color – 选择绘制的图案颜色
四. 原始代码:
一共五个类文件:
- Gui.java – 主界面
- DrawPanel.java – 图案绘制
- LRules.java – 定义公式以及规则
- Point.java – 点对象
- Start.java – 启动类
下面贴出LRules.java, DrawPanel.java,以及Point.java的原始代码,感兴趣的可以拿来使用,并且自己定义自己喜欢的风格的界面,进一步的扩充里面的规则案例.
LRules.java
importjava.awt.Color; importjava.awt.Graphics; importjava.awt.Graphics2D; importjava.awt.geom.Line2D; importjava.util.Stack;
publicclassLRules {
publicString combineLine(intiteration, Stringaxioms, Stringrule1, Stringrule2) { String products=""; String product=axioms; for(intn= 0;n<iteration;n++) { products=""; for(inti= 0;i<product.length();i++) { charcurrent=product.charAt(i); charcheck1=rule1.charAt(0); charcheck2=rule2.charAt(0); if(current==check1) { products=products.concat(rule1.substring(2,rule1.length())); } elseif(current==check2){ products=products.concat(rule2.substring(2,rule2.length())); } else{ products=products.concat(String.valueOf(current)); } } product=products; } returnproducts; } publicStack<Point> getStack(Stringproducts,floatangle) { Stack<Point> st=newStack<Point>(); inti= 0; floatang= 90; booleanoutbraket[] =newboolean[20]; intcount= 0; floatangleSign[] =newfloat[20]; for(intloop= 0;loop<products.length();loop++) { charletter=products.charAt(loop); switch(letter) { case'F': Point p=newPoint(); if(i==0) { p.setStartX(400); p.setStartY(650); p.setAngle(90); } p.setAngle(ang); st.push(p); break; case'f': Point p1=newPoint(); if(i==0) { p1.setStartX(400); p1.setStartY(300); p1.setAngle(90); } p1.setAngle(ang); st.push(p1); break; case'+': ang=ang-angle; break; case'-': ang=ang+angle; break; case'[': angleSign[count] =ang; count++; break; case']': outbraket[count] =true; count--; ang=angleSign[count]; break; default: break; } i++; } returnst; }
publicvoid drawPic( Graphics g,ColorblockColor,floatlineLength,floatangel,Stringsproducts,Stack<Point>points){ floatxStartPos,yStartPos,xEndPos,yEndPos; intcount= 0; Graphics2D g2d= (Graphics2D)g; Point node[] =newPoint[20]; intcount1= 0; for(intloop= 0;loop<sproducts.length();loop++) { charletter=sproducts.charAt(loop); switch(letter) { case'F': Point p= (Point)points.get(count); xStartPos=p.getStartX(); yStartPos=p.getStartY();
xEndPos= (float)(p.getStartX()+( lineLength*Math.cos(p.getAngle()*Math.PI/180))); yEndPos= (float) (p.getStartY()-( lineLength*Math.sin(p.getAngle()*Math.PI/180))); g2d.setColor(blockColor); Line2D.Float path=newLine2D.Float(xStartPos,yStartPos,xEndPos,yEndPos); g2d.draw(path); if(count<points.size()-1) { ((Point) points.get(count+1)).setStartX(xEndPos); ((Point) points.get(count+1)).setStartY(yEndPos); } count++; break; case'f': Point p1= (Point)points.get(count);
xStartPos=p1.getStartX(); yStartPos=p1.getStartY();
xEndPos= (float)(p1.getStartX()+( lineLength*Math.cos(p1.getAngle()*Math.PI/180))); yEndPos= (float) (p1.getStartY()-( lineLength*Math.sin(p1.getAngle()*Math.PI/180))); g2d.setColor(blockColor); Line2D.Float path1=newLine2D.Float(xStartPos,yStartPos,xEndPos,yEndPos); g2d.draw(path1); if(count<points.size()-1) { ((Point) points.get(count+1)).setStartX(xEndPos); ((Point) points.get(count+1)).setStartY(yEndPos); } count++; break; case'+': break; case'-': break; case'[': node[count1] = (Point)points.get(count); count1++; break; case']': if(count<points.size()) { Point p4= (Point)points.get(count); p4.setStartX(node[count1-1].getStartX()); p4.setStartY(node[count1-1].getStartY()); } count1--; break; default: break; } } } } |
DrawPanel.java
importjava.awt.Color; importjava.awt.Graphics; importjava.awt.Image; importjava.awt.Polygon; importjava.awt.geom.Point2D; importjava.util.Stack; importjavax.swing.JPanel;
publicclassDrawPanelextendsJPanel { publicfinalintWIDTH= 800; publicfinalintHEIGHT= 800; publicfinalintWIDTH_ADJUST= 60; publicfinalintHEIGHT_ADJUST= 40; publicfinaldoubleSTARTDRAWLEAF= 9999; publicfinaldoubleENDDRAWLEAF= 99999; publicfinalintSTART_LEFT= 0; publicfinalintSTART_MIDDLE= 1; publicPoint2DsrcPoint=newPoint2D.Double(0,0);
privatePoint2DsrcPoint1=newPoint2D.Double(0,0); publicPoint2DdestPoint=newPoint2D.Double(0,0); publicintwidth; privatePolygonleafPoly; privatebooleandrawLeaf=false; privatebooleanleafCoord=false; privateThreadmainThread=null; privateColorblockColor; privateinttimes; privateintlength; privatefloatangle; privateintspeed; privateStringaxiom; privateStringrule1; privateStringrule2; privateImageim; publicGraphicsoffscreen; floatlb,rb,ub,db; // record all rectangular blocks publiclongmySeed= System.currentTimeMillis();
publicvoidstartDrawing(ColorblockColor,inttimes,intlength,floatangle,intspeed, Stringaxioms,Stringrule1,Stringrule2){ this.blockColor=blockColor; this.times=times; this.length=length; this.angle=angle; this.speed=speed; this.axiom=axioms; this.rule1=rule1; this.rule2=rule2; width=WIDTH; if(mainThread==null){ mainThread=newThread(newRunnableObject()); mainThread.start(); } } publicvoidstop(){ offscreen=null; repaint(); pause(); } publicvoidpause(){ if(mainThread!=null){ mainThread. mainThread=null; } } privateclassRunnableObjectimplementsRunnable{ publicvoidrun(){ inti= 0; intj= 0; intk= 0; intl= 0; intsrcX,srcY,destX,destY,srcX1,srcY1; if(mainThread!=null){ /* if (leafCoord){ srcX = (int)srcPoint.getX() + width/2;// - WIDTH_ADJUST; srcY = HEIGHT - (int)srcPoint.getY();// - HEIGHT_ADJUST; leafPoly.addPoint(srcX, srcY); } else{ if (drawLeaf){ destX = (int)destPoint.getX() + width/2;// - WIDTH_ADJUST; destY = HEIGHT - (int)srcPoint.getY();// - HEIGHT_ADJUST; leafPoly.addPoint(destX, destY); } */ repaint(); try { mainThread.sleep(speed); } catch(InterruptedExceptione) { e.printStackTrace(); } //} }
}
}
publicvoidpaint(Graphicsg) { if(offscreen==null) { im= createImage(WIDTH,HEIGHT); offscreen=im.getGraphics(); offscreen.setColor(Color.white); offscreen.fillRect(0,0,WIDTH,HEIGHT); } else { LRules lr=newLRules(); String axioms=lr.combineLine(times,axiom,rule1,rule2); Stack<Point> st=lr.getStack(axioms,angle); lr.drawPic(offscreen,blockColor,length,angle,axioms,st); } g.drawImage(im,0,0,this); }
publicvoidupdate(Graphicsg) { paint(g); }
publicvoidsetSpeed(intspeed) { this.speed=speed; } } |
Point.java
publicclassPoint { privatefloatstartX; privatefloatstartY; privatefloatangle; publicfloatgetStartX() { returnstartX; } publicfloatgetStartY() { returnstartY; } publicfloatgetAngle() { returnangle; } publicvoidsetStartX(floatstartX) { this.startX=startX; } publicvoidsetStartY(floatstartY) { this.startY=startY; } publicvoidsetAngle(floatangle) { this.angle=angle; } } |
因为时间较早,编码中有一些东西目前已经不建议使用,例如线程用stop方法强制关闭。同时因为大学期间编码能力有限,加上当时是自己在圣诞节的一周假期额外抽时间来开发的,所以代码注释都未加,敬请见谅。不过多看几遍应该也能够明白其中的逻辑。感兴趣的可以关注微信公众号: 智能化IT系统, 进一步的沟通交流.
最后祝愿IT学子大学生活一帆风顺, 未来前程似锦.