上一篇我们成功的写出了计算LS表达式的两个函数caluStatement1和caluStatement2,接下来需要“创造”一个窗口,并且按照规则进行画图。使用java.awt中的Canvas类来作图可以实现需要的效果。
1、使用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方法。
2、给窗口加上菜单、下拉列表和按钮
我们要做一个有菜单,中间是黑底的画布,画笔是绿色,底部加一个panel的窗口。
java中Frame的一个实例就是一个容器(可理解成一个窗口),Frame上可以添加panel,panel上可以添加button和label之类的控件,这些元素组成一个外表凑合的窗口。这么多组件放到一个容器上最好可以空置组件在容器中的位置,java.awt包中提供了FlowLayout、BorderLayout、CardLayout、GridLayout布局类,具体区别就不介绍了,我们将采用BorderLayout布局。
在MyWindow类中定义组件
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("随机规则");
这样便大功告成了。
3、画图类MyCanvas的中的核心方法
要在画布中画线那必须要有坐标表示方法
//为保存双精度的点而新建一个类
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);
}
}
4、全部源码和运行结果
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();
}
}