上一篇我们成功的写出了计算LS表达式的两个函数caluStatement1和caluStatement2,接下来需要“创造”一个窗口,并且按照规则进行画图。使用java.awt中的Canvas类来作图可以实现需要的效果。
import java.awt.*; import java.awt.event.*; //画布 class MyCanvas extends Canvas{ int x,y,r; MyCanvas(){ x=76; y=12; r=38; setBackground(Color.cyan); } public void paint(Graphics g){ g.drawOval(x, y, 2*r, 2*r); } } //窗口 class MyWindow extends Frame { MyCanvas canvas= new MyCanvas(); //构造方法 MyWindow(){ Panel pn= new Panel(); add(canvas,BorderLayout.CENTER); add(pn,BorderLayout.SOUTH); setBounds(100,100,300,200); setVisible(true); } //响应关闭操作 protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if (e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } } //窗口名称 public synchronized void setTitle(String title) { super.setTitle(title); enableEvents(AWTEvent.WINDOW_EVENT_MASK); } } /**Main method*/ public class sheldon { public static void main(String args[]){ MyWindow instance= new MyWindow(); instance.setTitle("sheldon and his brain yeah"); } }
在这个代码中,我们重写了父类的public void paint(Graphics g)方法。当重写这个方法时,参数g是自动实例化的,这样就可以在子类中使用g的相应方法,比如画直线、画图形等等。同时组件调用repaint方法时,程序会首先清除paint方法以前所画的内容,然后再调用paint方法。
我们要做一个有菜单,中间是黑底的画布,画笔是绿色,底部加一个panel的窗口。
java中Frame的一个实例就是一个容器(可理解成一个窗口),Frame上可以添加panel,panel上可以添加button和label之类的控件,这些元素组成一个外表凑合的窗口。这么多组件放到一个容器上最好可以空置组件在容器中的位置,java.awt包中提供了FlowLayout、BorderLayout、CardLayout、GridLayout布局类,具体区别就不介绍了,我们将采用BorderLayout布局。
Panel pSouth= new Panel();
MenuBar menubar= new MenuBar();
Menu menu1= new Menu("文件"); Menu mn_mode= new Menu(); Menu support= new Menu("技术支持");
MenuItem item1= new MenuItem("退出");
MenuItem mn_mode_1= new MenuItem(); MenuItem mn_mode_2= new MenuItem();
Choice choice= new Choice();
Button button_step= new Button("单步迭代");
Label label_step= new Label(" ");
Label info= new Label("LLC LAB");
//设置UI
setBackground(Color.lightGray);
choice.addItem("牛逼的树");
pSouth.add(choice);
pSouth.add(button_step);
pSouth.add(label_step);
pSouth.add(info);
add(canvas,BorderLayout.CENTER);
add(pSouth,BorderLayout.SOUTH);
//设置menu
this.setMenuBar(menubar);
menubar.add(menu1); menubar.add(mn_mode); menubar.add(support);
menu1.add(item1);
mn_mode.add(mn_mode_1); mn_mode.add(mn_mode_2);
mn_mode.setLabel("生成模式");
mn_mode_1.setLabel("单一规则"); mn_mode_2.setLabel("随机规则");
这样便大功告成了。
要在画布中画线那必须要有坐标表示方法
//为保存双精度的点而新建一个类
class doublePoint {
double x;
double y;
doublePoint(double x1,double y1){
x=x1;y=y1;
}
}
我们既然已经将指令存储在statement中了那么以此检查字符串数组的每个元素,遇到F就画线,遇到[就将当前状态存储到栈中,遇到]则从栈中取出状态。
for (i=0;i<statement.length();i++) {
//获取公理中第i个字符,即解释instruction
c = statement.charAt(i);
if (c=='F') {
double rad = 2*Math.PI*direction/360 ; // 角度转换
double p = lengthF * Math.cos(rad);
double q = lengthF * Math.sin(rad);
b = new doublePoint(a.x+p, a.y+q);
//g=this.getGraphics();
g.drawLine((int)(a.x), (int)(400-a.y), (int)(b.x) ,(int)(400-b.y));
a = b; // 前一线段的终点为后一线段的起始点
}
else if (c=='+') direction += rotation; //逆时针转角度
else if (c=='-') direction -= rotation; //顺时针转角度
else if (c=='[') { //入栈
aPoint.addElement(a);
sDirection = String.valueOf(direction);
aDirection.addElement(sDirection);
}
else if (c==']') { //出栈
a=(doublePoint)(aPoint.elementAt(aPoint.size()-1));
sDirection=(String)(aDirection.elementAt(aDirection.size()-1));
direction=Double.valueOf(sDirection).doubleValue();
aPoint.removeElementAt(aPoint.size()-1);
aDirection.removeElementAt(aDirection.size()-1);
}
}
package myFenxing.release; /** * @author Jason * @Corp LLC lab * @version LS grammar implementation version1; 菜单和按钮无功能 */ import java.awt.*; import java.awt.event.*; import java.applet.*; import java.util.Vector; //为保存双精度的点而新建一个类 class doublePoint { double x; double y; doublePoint(double x1,double y1){ x=x1;y=y1; } } //画布 class MyCanvas extends Canvas{ String statement; //LS文法表达式 int pStartX =320; //起始点340,20 int pStartY =20; double direction_init =90; // 作画时的初始方向 double direction =direction_init; double lengthF =4.5; double rotation =30; int StartDepth =7; //画图深度 doublePoint a, b; // 画线段所需的两点 MyCanvas(){ setBackground(Color.black); } public void setStatement(String s){ this.statement= s; } //改变窗口大小的时候会调用此方法重新绘图 public void paint(Graphics g){ //System.out.print("\n"+this.statement); a = new doublePoint(pStartX,pStartY); // 起始点 g.setColor(new Color(110,170,60)); // 定义画笔的颜色绿色 draw(g,this.statement); } //核心方法 public void draw(Graphics g, String statement){ Vector aPoint = new Vector();//用堆栈记录[]中的内容 Vector aDirection = new Vector(); String sDirection; int i; char c; System.out.print("\ndraw length "+statement.length()); for (i=0;i<statement.length();i++) { //获取公理中第i个字符,即解释instruction c = statement.charAt(i); if (c=='F') { double rad = 2*Math.PI*direction/360 ; // 角度转换 double p = lengthF * Math.cos(rad); double q = lengthF * Math.sin(rad); b = new doublePoint(a.x+p, a.y+q); //g=this.getGraphics(); g.drawLine((int)(a.x), (int)(400-a.y), (int)(b.x) ,(int)(400-b.y)); a = b; // 前一线段的终点为后一线段的起始点 } else if (c=='+') direction += rotation; //逆时针转角度 else if (c=='-') direction -= rotation; //顺时针转角度 else if (c=='[') { //入栈 aPoint.addElement(a); sDirection = String.valueOf(direction); aDirection.addElement(sDirection); } else if (c==']') { //出栈 a=(doublePoint)(aPoint.elementAt(aPoint.size()-1)); sDirection=(String)(aDirection.elementAt(aDirection.size()-1)); direction=Double.valueOf(sDirection).doubleValue(); aPoint.removeElementAt(aPoint.size()-1); aDirection.removeElementAt(aDirection.size()-1); } }System.out.print("\ndraw over;"); } } //窗口 class MyWindow extends Frame implements ActionListener{ MyCanvas canvas= new MyCanvas(); //在画布中绘图的起始点340,20 int pStartX =320; int pStartY =20; Panel pSouth= new Panel(); MenuBar menubar= new MenuBar(); Menu menu1= new Menu("文件"); Menu mn_mode= new Menu(); Menu support= new Menu("技术支持"); MenuItem item1= new MenuItem("退出"); MenuItem mn_mode_1= new MenuItem(); MenuItem mn_mode_2= new MenuItem(); Choice choice= new Choice(); Button button_step= new Button("单步迭代"); Label label_step= new Label(" "); Label info= new Label("LLC LAB"); //LS文法需要的变量 String statement,replacement; String sStart; //公理;起始字符串 String sRule[][]; //存储规则 MyWindow(){ //默认状态下的公理 sStart = "F";//给公理赋值 statement= sStart; replacement= "F[+F]F[-F]F" ; //用来随机抽取,绘制随机植物 sRule = new String [10][2]; //设置UI setBackground(Color.lightGray); choice.addItem("牛逼的树"); pSouth.add(choice); pSouth.add(button_step); pSouth.add(label_step); pSouth.add(info); add(canvas,BorderLayout.CENTER); add(pSouth,BorderLayout.SOUTH); //设置menu this.setMenuBar(menubar); menubar.add(menu1); menubar.add(mn_mode); menubar.add(support); menu1.add(item1); mn_mode.add(mn_mode_1); mn_mode.add(mn_mode_2); mn_mode.setLabel("生成模式"); mn_mode_1.setLabel("单一规则"); mn_mode_2.setLabel("随机规则"); } //响应关闭操作 protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if (e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } } //窗口名称 public synchronized void setTitle(String title) { super.setTitle(title); enableEvents(AWTEvent.WINDOW_EVENT_MASK); } @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub } public void init() { //将计算出的statement传递给画布 canvas.setStatement(statement); this.setSize(pStartX*2,550); //获得屏幕长和宽,dimension类在一个单一对象中封装组建的宽和高 Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); this.setLocation((d.width - this.getSize().width) / 2, (d.height - this.getSize().height) / 2); this.setVisible(true); } /**计算LS表达式*/ //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){ //三条规则随机选择 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]"; 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; } } /**Main method*/ public class randomLS_V1 { public static void main(String args[]){ MyWindow instance= new MyWindow(); instance.setTitle("牛逼的树"); instance.statement= instance.caluStatement2(5); instance.init(); } }