/*============================================================================== Application: LSystems in Java Author: Paula A. Cooper Purpose: University of Calgary CPSC 553 course project Source: Please note that much of this code has been converted from a C program into Java. The initial version of this code (Plant and Fractal Generator, or pfg) was written by Przemyslaw Prusinkiewicz (Copyright (C) 1988) and is found in the "Lindenmayer Systems, Fractals and Plants" SIGGRAPH '88 course notes written by Prusinkiewicz and Hanan. ==============================================================================*/ import java.awt.*; import java.util.*; import java.io.*; import java.lang.*; /*=============================================================================== Applet LSys ===============================================================================*/ public class LSys extends java.applet.Applet { public LSysPanel dispPanel; public ParamPanel specPanel; public Graphics drawarea; public void init() { setLayout(new BorderLayout()); specPanel = new ParamPanel(); add("North",specPanel); dispPanel = new LSysPanel(specPanel); add("Center",dispPanel); drawarea = dispPanel.getGraphics(); dispPanel.show(); specPanel.show(); } public void start() { dispPanel.enable(); } public static void main() { } } /*============================================================================== class ParamPanel Purpose: This is the panel containing all of the parameters, and allows them to be adjusted through control widgets contained within it ================================================================================*/ class ParamPanel extends Panel { public Button b_add, b_del, b_updt, b_apply, b_new; public Checkbox cb_strt, cb_hermite, cb_bspline; /* choices for line shape */ public CheckboxGroup cbg_Lines; /* checkbox group of line types */ public List ch_ptrnspecs; /* choices for pattern specifications */ public List lst_rules; public TextField tb_axiom, tb_depth, tb_scale, tb_angle, tb_rule, tb_ignore, tb_clr; public int curIdx; /* index of item in list currently being edited */ public boolean b_doApply; /* flag whether Apply button has been pressed */ public boolean b_gotImagePnl=false; /* flag whether have been given associated drawing panel */ public LSysPanel pnl_image; public Color aColors[]; /* array of colors */ public Globals glb; /* globals */ /* constructor */ public ParamPanel() { Insets lft_spc = new Insets(0,5,0,0); /*---------------------------------------------- Constructor: ParamPanel Purpose: Sets up the control panel containing the user specified parameters for performing the LSystems image ---------------------------------------------*/ initColors(); b_doApply = false; b_gotImagePnl = false; Panel lPanel = new Panel(); Panel rPanel = new Panel(); Panel mPanel = new Panel(); ClrPanel bPanel = new ClrPanel(aColors); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); c.fill = GridBagConstraints.HORIZONTAL; /* fill horizontally*/ c.weightx=1; /* each of the 3 panels get same horiz space */ c.insets = new Insets(0,0,0,10); /* pad left and right */ c.gridwidth=1; c.anchor = GridBagConstraints.NORTH; gridbag.setConstraints(lPanel,c); gridbag.setConstraints(rPanel,c); gridbag.setConstraints(mPanel,c); add(lPanel); /* add panels left to right */ add(mPanel); add(rPanel); /*------------ color panel ------------*/ c = new GridBagConstraints(); c.gridx = 0; c.gridwidth = GridBagConstraints.REMAINDER; c.insets = new Insets(15,0,0,0); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.WEST; gridbag.setConstraints(bPanel,c); add(bPanel); gridbag = new GridBagLayout(); bPanel.setLayout(gridbag); GridBagConstraints c_num = new GridBagConstraints(); c_num.gridwidth = 1; /* first in row */ c_num.fill = GridBagConstraints.HORIZONTAL; c_num.weightx = 1; c_num.insets = new Insets(5,20,5,0); Label label8 = new Label("0"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); c_num.insets= new Insets(5,59,5,0); label8 = new Label("5"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); label8 = new Label("10"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); label8 = new Label("15"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); label8 = new Label("20"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); label8 = new Label("25"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); label8 = new Label("30"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); label8 = new Label("35"); gridbag.setConstraints(label8, c_num); bPanel.add(label8); c_num.insets = new Insets(5,10,5,5); tb_clr = new TextField(3); gridbag.setConstraints(tb_clr, c_num); bPanel.add(tb_clr); gridbag = new GridBagLayout(); c = new GridBagConstraints(); setFont(new Font("Helvetica", Font.PLAIN, 14)); lPanel.setLayout(gridbag); c.fill = GridBagConstraints.NONE; /* stretch components left to right */ c.fill = GridBagConstraints.HORIZONTAL; /* stretch components left to right */ /*---------------------- LSystems Samples ---------------------*/ GridBagConstraints c_smpl = new GridBagConstraints(); c_smpl.gridx = 0; c_smpl.gridy = 0; c_smpl.gridwidth = GridBagConstraints.REMAINDER; /* Rules Label */ c_smpl.anchor = GridBagConstraints.WEST; Label label7 = new Label("Sample LSystems:"); gridbag.setConstraints(label7,c_smpl); lPanel.add(label7); GridBagConstraints c_btn = new GridBagConstraints(); c_btn.gridwidth = 1; c_btn.fill = GridBagConstraints.HORIZONTAL; c_btn.weightx = 1; c_btn.gridx=0; c_btn.gridy=1; TextField tb_placeholder = new TextField(25); gridbag.setConstraints(tb_placeholder, c_btn); tb_placeholder.hide(); lPanel.add(tb_placeholder); tb_placeholder.show(); tb_placeholder.hide(); /* List of Samples LSystem Patterns */ c_smpl.fill = GridBagConstraints.HORIZONTAL; c_smpl.anchor = GridBagConstraints.WEST; c_smpl.gridy = 1; ch_ptrnspecs = new List(6,false); ch_ptrnspecs.clear(); gridbag.setConstraints(ch_ptrnspecs, c_smpl); lPanel.add(ch_ptrnspecs); fillSpecChoices(ch_ptrnspecs); c_btn = new GridBagConstraints(); c_btn.gridwidth = 1; c_btn.fill = GridBagConstraints.HORIZONTAL; c_btn.weightx = 1; c_btn.insets = new Insets (30,0,0,0); /* Apply button */ b_apply = new Button("Apply"); gridbag.setConstraints(b_apply, c_btn); lPanel.add(b_apply); gridbag = new GridBagLayout(); c = new GridBagConstraints(); setFont(new Font("Helvetica", Font.PLAIN, 14)); mPanel.setLayout(gridbag); c.fill = GridBagConstraints.NONE; /* stretch components left to right */ /*---------------------- Rule Controls ---------------------*/ GridBagConstraints c_rule = new GridBagConstraints(); c_rule.gridx = 0; c_rule.insets=lft_spc; c_rule.gridwidth = GridBagConstraints.REMAINDER; /* Rules Label */ c_rule.anchor = GridBagConstraints.WEST; Label label5 = new Label("Rules:"); gridbag.setConstraints(label5,c_rule); mPanel.add(label5); /* List of Rules */ c_rule.fill = GridBagConstraints.HORIZONTAL; c_rule.anchor = GridBagConstraints.WEST; lst_rules = new List(4, false); gridbag.setConstraints(lst_rules,c_rule); mPanel.add(lst_rules); lst_rules.clear(); /* rule edit box */ c_rule.insets = new Insets(35,5,0,0); c_rule.anchor = GridBagConstraints.WEST; c_rule.fill = GridBagConstraints.NONE; tb_rule = new TextField(27); gridbag.setConstraints(tb_rule, c_rule); mPanel.add(tb_rule); /*------------------ Rule Editing Buttons (Add, Update, Delete, New) ------------------*/ c_btn = new GridBagConstraints(); c_btn.insets=lft_spc; c_btn.gridwidth = 1; c_btn.fill = GridBagConstraints.HORIZONTAL; c_btn.weightx = 1; /* rule editing buttons */ b_add = new Button("Add"); b_updt = new Button("Update"); b_del = new Button("Delete"); b_new = new Button("Clear"); gridbag.setConstraints(b_add, c_btn); gridbag.setConstraints(b_updt, c_btn); gridbag.setConstraints(b_del, c_btn); c_btn.gridwidth = GridBagConstraints.REMAINDER; /* last-in-row */ gridbag.setConstraints(b_new, c_btn); mPanel.add(b_add); mPanel.add(b_updt); mPanel.add(b_del); mPanel.add(b_new); gridbag = new GridBagLayout(); c = new GridBagConstraints(); setFont(new Font("Helvetica", Font.PLAIN, 14)); rPanel.setLayout(gridbag); c.fill = GridBagConstraints.NONE; /* don't stretch components */ cbg_Lines = new CheckboxGroup(); /*------------------- Line Checkboxes ------------------*/ GridBagConstraints c_line = new GridBagConstraints(); c_line.gridx = 0; c_line.anchor = GridBagConstraints.WEST; c_line.gridwidth = GridBagConstraints.REMAINDER; /* last-in-row */ /* Straight Line Checkbox*/ c_line.insets= new Insets(20,50,0,0); cb_strt = new Checkbox("Straight Lines",cbg_Lines,true); gridbag.setConstraints(cb_strt, c_line); rPanel.add(cb_strt); /* Hermite Curve Checkbox*/ c_line.insets= new Insets(0,50,0,0); cb_hermite = new Checkbox("Hermite Curved Lines",cbg_Lines,false); gridbag.setConstraints(cb_hermite, c_line); rPanel.add(cb_hermite); /* BSpline Curve Checkbox*/ cb_bspline = new Checkbox("B-Spline Curved Lines",cbg_Lines,false); c_line.insets= new Insets(0,50,12,0); gridbag.setConstraints(cb_bspline, c_line); rPanel.add(cb_bspline); /*------------------- Parameter Text Boxes Axiom, Ignore, Depth, Angle, Scale ------------------*/ GridBagConstraints c_textb = new GridBagConstraints(); /* Axiom */ c_textb.gridwidth = 1; /* first in row */ c_textb.anchor = GridBagConstraints.WEST; Label label1 = new Label("Axiom: "); gridbag.setConstraints(label1, c_textb); rPanel.add(label1); c_textb.anchor = GridBagConstraints.WEST; /* reset */ c_textb.gridwidth = GridBagConstraints.REMAINDER;/* last-in-row */ tb_axiom = new TextField(26); gridbag.setConstraints(tb_axiom, c_textb); rPanel.add(tb_axiom); /* Ignore */ c_textb.gridwidth = 1; /* first in row */ c_textb.anchor = GridBagConstraints.WEST; Label label6 = new Label("Ignore: "); gridbag.setConstraints(label6, c_textb); rPanel.add(label6); c_textb.anchor = GridBagConstraints.WEST; /* reset */ c_textb.gridwidth = GridBagConstraints.REMAINDER;/* last-in-row */ tb_ignore = new TextField(26); gridbag.setConstraints(tb_ignore, c_textb); rPanel.add(tb_ignore); /* Depth */ c_textb.gridwidth = 1; /* first in row */ c_textb.anchor = GridBagConstraints.WEST; Label label2 = new Label("Depth:"); gridbag.setConstraints(label2, c_textb); rPanel.add(label2); c_textb.anchor = GridBagConstraints.WEST; /* reset */ tb_depth = new TextField(1); gridbag.setConstraints(tb_depth, c_textb); rPanel.add(tb_depth); /* Angle */ c_textb.anchor = GridBagConstraints.WEST; /* reset */ Label label3 = new Label("Angle:"); gridbag.setConstraints(label3, c_textb); rPanel.add(label3); c_textb.anchor = GridBagConstraints.WEST; /* reset */ tb_angle = new TextField(3); gridbag.setConstraints(tb_angle, c_textb); rPanel.add(tb_angle); /* Scale */ c_textb.anchor = GridBagConstraints.WEST; /* reset */ Label label4 = new Label("Scale:"); gridbag.setConstraints(label4, c_textb); rPanel.add(label4); c_textb.anchor = GridBagConstraints.WEST; /* reset */ tb_scale = new TextField(3); gridbag.setConstraints(tb_scale, c_textb); rPanel.add(tb_scale); lPanel.show(); rPanel.show(); mPanel.show(); bPanel.show(); tb_placeholder.hide(); ch_ptrnspecs.select(0); changeSpecs(ch_ptrnspecs.getSelectedIndex()); } public void setImagePanel(LSysPanel pnl_lsys){ pnl_image = pnl_lsys; b_gotImagePnl = true; } /*----------------------------------------------------------- Method: handleEvent Purpose: handle button presses and other user interaction performed in the control panel ---------------------------------------------------------*/ public boolean handleEvent(Event e) { if (e.target instanceof Button) { if (e.target==b_add) { lst_rules.addItem(tb_rule.getText()); tb_rule.setText(""); } else if (e.target==b_del) { lst_rules.delItem(curIdx); tb_rule.setText(""); } else if (e.target==b_updt) { lst_rules.replaceItem(tb_rule.getText(),curIdx); tb_rule.setText(""); } else if (e.target==b_apply) { b_doApply=true; /* apply the most recent control panel settings to redraw the image in the related image panel */ if (b_gotImagePnl==true) { pnl_image.repaint(); } } else if (e.target==b_new) { /* clear fields for new LSystems specification */ lst_rules.clear(); tb_rule.setText(""); tb_axiom.setText(""); tb_depth.setText(""); tb_scale.setText(""); tb_angle.setText(""); tb_ignore.setText(""); tb_clr.setText(""); b_doApply=false; /* clear the image panel */ if (b_gotImagePnl==true) { pnl_image.repaint(); } } return(true); } else if (e.target instanceof List) { if (e.target==lst_rules) { tb_rule.setText(lst_rules.getSelectedItem()); curIdx = lst_rules.getSelectedIndex(); return(true); } else if (e.target==ch_ptrnspecs) { changeSpecs(ch_ptrnspecs.getSelectedIndex()); } } return(false); /* return false for unprocessed events */ } public void paint(Graphics g) { } /*====================================================================== Method: fillSpecChoices Purpose: fills in the LSystem Sample list box with the names of some predefined LSystems pattern choices Params: ChoiceMenu -- the listbox object to fill ======================================================================*/ public void fillSpecChoices(List ChoiceMenu) { ChoiceMenu.addItem("Quadratic Koch Island 1 pg. 13"); /* pg. 13 */ ChoiceMenu.addItem("Quadratic Koch Island 2 pg. 14"); /* pg. 14 */ ChoiceMenu.addItem("Island & Lake Combo. pg. 15"); /* pg.15 */ ChoiceMenu.addItem("Koch Curve A pg. 16"); ChoiceMenu.addItem("Koch Curve B pg. 16"); ChoiceMenu.addItem("Koch Curve C pg. 16"); ChoiceMenu.addItem("Koch Curve D pg. 16"); ChoiceMenu.addItem("Koch Curve E pg. 16"); ChoiceMenu.addItem("Koch Curve F pg. 16"); ChoiceMenu.addItem("Mod of Snowflake pg. 14"); ChoiceMenu.addItem("Dragon Curve pg. 17"); ChoiceMenu.addItem("Hexagonal Gosper Curve pg. 19"); ChoiceMenu.addItem("Sierpinski Arrowhead pg. 19"); ChoiceMenu.addItem("Peano Curve pg. 18"); ChoiceMenu.addItem("Hilbert Curve pg. 18"); ChoiceMenu.addItem("Approx of Sierpinski pg. 18"); ChoiceMenu.addItem("Tree A pg. 25"); ChoiceMenu.addItem("Tree B pg. 25"); ChoiceMenu.addItem("Tree C pg. 25"); ChoiceMenu.addItem("Tree D pg. 25"); ChoiceMenu.addItem("Tree E pg. 25"); ChoiceMenu.addItem("Tree B pg. 43"); ChoiceMenu.addItem("Tree C pg. 43"); ChoiceMenu.addItem("Spiral Tiling pg. 70"); ChoiceMenu.addItem("BSpline Triangle pg. 20"); ChoiceMenu.addItem("Snake Kolam pg. 72"); ChoiceMenu.addItem("Anklets of Krishna pg. 73"); /*----------- Color examples -----------*/ ChoiceMenu.addItem("Color1, Koch Curve B"); ChoiceMenu.addItem("Color2, Koch Curve B"); ChoiceMenu.addItem("Color X, Spiral Tiling"); ChoiceMenu.addItem("Color Center, Spiral Tiling"); ChoiceMenu.addItem("Color Spokes, Spiral Tiling"); ChoiceMenu.addItem("Color, Quad Koch Island 1"); ChoiceMenu.addItem("Color, Tree E"); ChoiceMenu.addItem("Color, Mod of Snowflake"); ChoiceMenu.addItem("Color, Anklets of Krishna"); ChoiceMenu.addItem("Color, Snake Kolam"); ChoiceMenu.addItem("Simple Branch"); } /*===================================================================== Method: initColors Purpose: Initialize Array of Colors =====================================================================*/ public void initColors() { int i; float j; int start; int numShades=5; float shadeInc = 1/(float)numShades; aColors = new Color[glb.MAXCOLORS]; /* set array size */ /* White to Black */ start = 0; for (i=start, j=1;i * --> F+F-F-FF+F+F-F"); break; case 1: /* Quadratic Koch Island 2 pg. 14 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+-"); tb_depth.setText("2"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> F-FF+FF+F+F-F-FF+F+F-F-FF-FF+F"); break; case 2: /* Island & Lake Combo. pg. 15 */ tb_clr.setText("16"); tb_axiom.setText("F-F-F-F"); tb_ignore.setText("Ff+-"); tb_depth.setText("2"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> F-f+FF-F-FF-Ff-FF+f-FF+F+FF+Ff+FFF"); lst_rules.addItem("* * --> ffffff"); break; case 3: /* Koch Curve A pg. 16 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> FF+F+F+F+F+F-F"); break; case 4: /* Koch Curve B pg. 16 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> FF+F+F+F+FF"); break; case 5: /* Koch Curve C pg. 16 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+-"); tb_depth.setText("3"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> FF+F-F+F+FF"); break; case 6: /* Koch Curve D pg. 16 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> FF+F++F+F"); break; case 7: /* Koch Curve E pg. 16 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+"); tb_depth.setText("5"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> F+FF++F+F"); break; case 8: /* Koch Curve F pg. 16 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> F+F-F+F+F"); break; case 9: /* Mod of snowflake pg. 14*/ tb_clr.setText("16"); tb_axiom.setText("+F"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> F-F+F+F-F"); break; case 10: /* Dragon Curve pg. 17 */ tb_clr.setText("16"); tb_axiom.setText("Fl"); tb_ignore.setText("F+-"); tb_depth.setText("14"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> l+rF+"); lst_rules.addItem("* * --> -Fl-r"); break; case 11: /* Hexagonal Gosper Curve pg. 19 */ tb_clr.setText("16"); tb_axiom.setText("XF"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("90"); tb_angle.setText("6"); lst_rules.clear(); lst_rules.addItem("* * --> XF+YF++YF-XF--XFXF-YF+"); lst_rules.addItem("* * --> -XF+YFYF++YF+XF--XF-YF"); break; case 12: /* Sierpinski Arrowhead pg. 19 */ tb_clr.setText("16"); tb_axiom.setText("YF"); tb_ignore.setText("F+-"); tb_depth.setText("6"); tb_scale.setText("100"); tb_angle.setText("6"); lst_rules.clear(); lst_rules.addItem("* * --> YF+XF+YF"); lst_rules.addItem("* * --> XF-YF-XF"); break; case 13: /* Peano Curve pg. 18 */ tb_clr.setText("16"); tb_axiom.setText("X"); tb_ignore.setText("F+-"); tb_depth.setText("3"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> XFYFX+F+YFXFY-F-XFYFX"); lst_rules.addItem("* * --> YFXFY-F-XFYFX+F+YFXFY"); break; case 14: /* Hilbert Curve pg. 18 */ tb_clr.setText("16"); tb_axiom.setText("X"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> -YF+XFX+FY-"); lst_rules.addItem("* * --> +XF-YFY-FX+"); break; case 15: /* Approx of Sierpinski pg. 18 */ tb_clr.setText("16"); tb_axiom.setText("F+XF+F+XF"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> XF-F+F-XF+F+XF-F+F-X"); break; case 16: /* Tree A pg. 25 */ tb_clr.setText("16"); tb_axiom.setText("F"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("14"); lst_rules.clear(); lst_rules.addItem("* * --> F[+F]F[-F]F"); break; case 17: /* Tree B pg. 25 */ tb_clr.setText("16"); tb_axiom.setText("X"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("16"); lst_rules.clear(); lst_rules.addItem(" * * --> F-[[X]+X]+F[+FX]-X"); lst_rules.addItem(" * * --> FF"); break; case 18: /* Tree C pg. 25 */ tb_clr.setText("16"); tb_axiom.setText("Y"); tb_ignore.setText("F+-"); tb_depth.setText("6"); tb_scale.setText("100"); tb_angle.setText("14"); lst_rules.clear(); lst_rules.addItem(" * * --> YFX[+Y][-Y]"); lst_rules.addItem(" * * --> X[-FFF][+FFF]FX"); break; case 19: /* Tree D pg. 25 */ tb_clr.setText("16"); tb_axiom.setText("F"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("16"); lst_rules.clear(); lst_rules.addItem("* * --> FF+[+F-F-F]-[-F+F+F]"); break; case 20: /* Tree E pg. 25 */ tb_clr.setText("16"); tb_axiom.setText("X"); tb_ignore.setText("F+-"); tb_depth.setText("7"); tb_scale.setText("100"); tb_angle.setText("18"); lst_rules.clear(); lst_rules.addItem("* * --> F[+X]F[-X]+X"); lst_rules.addItem("* * --> FF"); break; case 21: /* Tree B pg. 43 */ tb_clr.setText("16"); tb_axiom.setText("F1F1F1"); tb_ignore.setText("F+-"); tb_depth.setText("30"); tb_scale.setText("100"); tb_angle.setText("16"); lst_rules.clear(); lst_rules.addItem("0 <0> 0 --> 1"); lst_rules.addItem("0 <0> 1 --> 1[-F1F1]"); lst_rules.addItem("0 <1> 0 --> 1"); lst_rules.addItem("0 <1> 1 --> 1"); lst_rules.addItem("1 <0> 0 --> 0"); lst_rules.addItem("1 <0> 1 --> 1F1"); lst_rules.addItem("1 <1> 0 --> 1"); lst_rules.addItem("1 <1> 1 --> 0"); lst_rules.addItem("* <-> * --> +"); lst_rules.addItem("* <+> * --> -"); break; case 22: /* Tree C pg. 43 */ tb_clr.setText("16"); tb_axiom.setText("F1F1F1"); tb_ignore.setText("F+-"); tb_depth.setText("26"); tb_scale.setText("100"); tb_angle.setText("14"); lst_rules.clear(); lst_rules.addItem("0 <0> 0 --> 0"); lst_rules.addItem("0 <0> 1 --> 1"); lst_rules.addItem("0 <1> 0 --> 0"); lst_rules.addItem("0 <1> 1 --> 1[+F1F1]"); lst_rules.addItem("1 <0> 0 --> 0"); lst_rules.addItem("1 <0> 1 --> 1F1"); lst_rules.addItem("1 <1> 0 --> 0"); lst_rules.addItem("1 <1> 1--> 0"); lst_rules.addItem("* <-> * --> +"); lst_rules.addItem("* <+> * --> -"); break; case 23: /* Spiral Tiling pg. 70 */ tb_clr.setText("16"); tb_axiom.setText("AAAA"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("24"); lst_rules.clear(); lst_rules.addItem("* * --> X+X+X+X+X+X+"); lst_rules.addItem("* * --> [F+F+F+F[---X-Y]+++++F++++++++F-F-F-F]"); lst_rules.addItem("* * --> [F+F+F+F[---Y]+++++F++++++++F-F-F-F]"); break; case 24: /* BSpline Triangle pg. 20 */ tb_clr.setText("16"); tb_axiom.setText("F+F+F"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("3"); lst_rules.clear(); lst_rules.addItem("* * --> F-F+F"); break; case 25: /* Snake Kolam pg. 72 */ tb_clr.setText("16"); tb_axiom.setText("F+XF+F+XF"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> XF-F-F+XF+F+XF-F-F+X"); break; case 26: /* Anklets of Krishna pg. 73 */ tb_clr.setText("16"); tb_axiom.setText("-X--X"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("8"); lst_rules.clear(); lst_rules.addItem("* * --> XFX--XFX"); break; case 27: /* Color, Koch Curve B */ tb_clr.setText("16"); tb_axiom.setText("F+F+F+F"); tb_ignore.setText("F+"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> FF+F+;;;;;F:::::+F+FF"); break; case 28: /* Color, Koch Curve B */ tb_clr.setText("16"); tb_axiom.setText("###F+F+F+F"); tb_ignore.setText("F+"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * -->;FF+F+F+F+FF"); break; case 29: /* Color X, Spiral Tiling */ tb_clr.setText("16"); tb_axiom.setText("AAAA"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("24"); lst_rules.clear(); lst_rules.addItem("* * --> ;;;;;;;;X::::::::+X+X+X+X+X+"); lst_rules.addItem("* * --> [F+F+F+F[---X-Y]+++++F++++++++F-F-F-F]"); lst_rules.addItem("* * --> [F+F+F+F[---Y]+++++F++++++++F-F-F-F]"); break; case 30: /* Color Center, Spiral Tiling */ tb_clr.setText("16"); tb_axiom.setText("AAAA"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("24"); lst_rules.clear(); lst_rules.addItem("* * --> X+X+X+X+X+X+"); lst_rules.addItem("* * --> [;;;;;F+F+F+F:::::[---X-Y]+++++F++++++++F-F-F-F]"); lst_rules.addItem("* * --> [F+F+F+F[---Y]+++++F++++++++F-F-F-F]"); break; case 31: /* Color Spokes, Spiral Tiling */ tb_clr.setText("16"); tb_axiom.setText("AAAA"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("24"); lst_rules.clear(); lst_rules.addItem("* * --> X+X+X+X+X+X+"); lst_rules.addItem("* * --> [F+F+F+F[---X-Y]+++++F++++++++;;;;F-F-F-F::::]"); lst_rules.addItem("* * --> [F+F+F+F[---Y]+++++F++++++++F-F-F-F]"); break; case 32: /* Color, Quad Koch Island 1 */ /* quadratic Koch Island 1 pg. 13 */ tb_clr.setText("16"); tb_axiom.setText("###F+F+F+F"); tb_ignore.setText("F+-"); tb_depth.setText("2"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> ;F+F-F-FF+F+F-F"); break; case 33: /* Color, Tree E */ tb_clr.setText("16"); tb_axiom.setText("X"); tb_ignore.setText("F+-"); tb_depth.setText("7"); tb_scale.setText("100"); tb_angle.setText("18"); lst_rules.clear(); lst_rules.addItem("* * --> F[+X]F[-X]+X"); lst_rules.addItem("* * --> ;;FF::"); break; case 34: /* Color, Mod of Snowflake */ tb_clr.setText("16"); tb_axiom.setText("###+F"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("100"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> ;F-F+F+F-F"); break; case 35: /* Color, Anklets of Krishna */ tb_clr.setText("16"); tb_axiom.setText("-X--X"); tb_ignore.setText("F+-"); tb_depth.setText("5"); tb_scale.setText("100"); tb_angle.setText("8"); lst_rules.clear(); lst_rules.addItem("* * --> XFX--X;;;;;;F::::::X"); break; case 36: /* Color, Snake Kolam */ tb_clr.setText("16"); tb_axiom.setText("F+XF+F+;;;XF:::"); tb_ignore.setText("F+-"); tb_depth.setText("4"); tb_scale.setText("90"); tb_angle.setText("4"); lst_rules.clear(); lst_rules.addItem("* * --> XF-F-F+XF+F+XF-F-F+X"); break; case 37: /* Simple Branch */ tb_clr.setText("16"); tb_axiom.setText("FFF[-FFF][--FFF][FFF][+FFF][++FFF]"); tb_ignore.setText("F+-"); tb_depth.setText("1"); tb_scale.setText("90"); tb_angle.setText("8"); lst_rules.clear(); break; default: tb_axiom.setText(""); tb_ignore.setText(""); tb_depth.setText(""); tb_scale.setText(""); tb_angle.setText(""); lst_rules.clear(); break; } } } /*============================================================================== class ClrPanel Content: Panel with Color Bar (for selecting start color) ===============================================================================*/ class ClrPanel extends Panel { public Globals glb; /* globals */ Color aColors[]; /* constructor */ public ClrPanel(Color aClrArray[]) { aColors = aClrArray; } public void paint(Graphics g) { boolean b; Dimension pnldim; int i; int clrBarWd; int startx; pnldim = this.size(); clrBarWd = (pnldim.width - (25+80))/glb.MAXCOLORS; for (startx=25,i=0;iglb.MAXCOLORS-1) { Specs.start_clr = glb.MAXCOLORS-1; } else if (Specs.start_clr <0) { Specs.start_clr = 0; } for (i = 0; i < iNumRules; i++) { sRule = pnl_specs.lst_rules.getItem(i); /* parse through the rule for the separators '<','>','-->' where the rule is of the form: leftcontext < predecessor > rightcontext --> successor */ iPred = sRule.indexOf('<'); if (iPred != -1) { iPost = sRule.indexOf('>',iPred); } else { System.out.println("Error! Missing '<' in rule."); return(false); } if (iPost != -1) { iSucc = sRule.indexOf("-->",iPost); } else { System.out.println("Error! Missing '>' in rule."); return(false); } if (iSucc == -1) { System.out.println("Error! Missing '-->' in rule."); return(false); } /* put the rule into the array of rules */ aRules[i] = new Production(); /* left context */ aRules[i].lCtxt = sRule.substring(0,iPred); aRules[i].lCtxt = FixStr(aRules[i].lCtxt); aRules[i].lCtxtLen = aRules[i].lCtxt.length(); /* predecessor */ aRules[i].pred = sRule.substring(iPred+1,iPost); aRules[i].pred = FixStr(aRules[i].pred); aRules[i].predLen = aRules[i].pred.length(); /* right context */ aRules[i].rCtxt = sRule.substring(iPost+1, iSucc); aRules[i].rCtxt = FixStr(aRules[i].rCtxt); aRules[i].rCtxtLen = aRules[i].rCtxt.length(); /* successor */ aRules[i].succ = sRule.substring(iSucc+3); aRules[i].succ = FixStr(aRules[i].succ); aRules[i].succLen = aRules[i].succ.length(); } return(true); } /*------------------------------------------------------------------ Method: FixStr Purpose: adjust the string passed (assumed to be a substring portion of a rule such as the: left context, right context, predecessor, or successor) so that if it is simply the '*' character (which stands for the empty string), it is replaced by an empty string Returns: the new String contents -------------------------------------------------------------------*/ public String FixStr(String sStr) { sStr = sStr.trim(); if (sStr.equals("*")==true) { sStr = new String(""); } return(sStr); } /*-------------------------------------------------------------------- Method: Derive Purpose: determine the LSystems pattern created using the current specifications and production rules Algorithm: Returned: true -- success in derivation false -- otherwise Globals Changed: sCurLvl - will contain final pattern created Globals Used: sCurLvl - contains --------------------------------------------------------------------*/ public boolean Derive(){ int i; sCurLvl = Specs.axiom; iCur = 0; sNextLvl = new StringBuffer(); /* derive each level */ for(i=1; i<=Specs.depth;i++){ iCur=0; /* keep going until done deriving current level string */ while(iCur < sCurLvl.length()){ ApplyProd(FindProd()); } sCurLvl = new String(sNextLvl); sNextLvl = new StringBuffer(); } return(true); } /*------------------------------------------------------------------- Method: ApplyProd Purpose: replace a predecessor with its successor based on a specific production rule Algorithm: If we found a production rule to match we copy the successor for the current predecessor into the next level (thus applying the production rule). If no production rule matched this predecessor then we copy the first character of this predecessor string down into the next level and shift the start of the predecessor to the right one (no longer consider this character part of the predecessor) Params: RuleIdx - index for production rule (-1 if no production rule should be applied) Globals Changed: iCur - set to point to next predecessor to be evaluated (skips past the current one which just had the production rule applied) sNextLvl - has the successor for the production rule just applied appended to the end of it --------------------------------------------------------------------*/ public void ApplyProd(int RuleIdx){ if (RuleIdx != -1){ /* apply production */ sNextLvl.append(aRules[RuleIdx].succ); /* apply rule */ iCur += aRules[RuleIdx].predLen; /* skip past current predecessor to next predecessor */ } else { /* move one character down to next level (out of predecessor) */ sNextLvl.append(sCurLvl.charAt(iCur)); iCur++; /* skip past one character */ } } /*------------------------------------------------------------------ Method: FindProd Purpose: find the production rule that applies to the predecessor at the current position (iCur) in the current level (sCurLvl) Tests for context free, context sensitive left, context sensitive right, and context sensitive right and left. Notice that the FIRST rule that matches (whether context free or context sensitive) is applied! Returns: index of rule if found otherwise... -1 (no rule found) -------------------------------------------------------------------*/ public int FindProd(){ int i; i=0; while(i < iNumRules){ /* if the predecessor, left condition, and right condition match then the production matches so return it, otherwise try the next production rule */ if (prefix(aRules[i].pred,sCurLvl,iCur)&& rcondiff(aRules[i].rCtxt,sCurLvl,iCur+aRules[i].predLen)&& lcondiff(aRules[i].lCtxt,sCurLvl,iCur-1)){ return(i); /* a match! */ } else { i++; } } return(-1); /* no rule found */ } /*------------------------------------------------------------------ Method: doIgnore Purpose: determine whether the current character is one of the "irrelevant" or "ignorable" characters in terms of processing an LSystems pattern Parms: sStr - string to check in Idx - index into string Returns: true -- yes, should ignore the current char false -- no, should not ignore the current char -------------------------------------------------------------------*/ public boolean doIgnore (String sStr, int Idx){ int i; for (i=0;i < Ignore.length();i++) { if (sStr.charAt(Idx)==Ignore.charAt(i)) { return(true); } } return(false); } /*------------------------------------------------------------------ Method: prefix Purpose: see if the rule string is the starting portion found at the current position (cIdx) in the derivation string Return: true - yes, is a prefix false - no, is not a prefix -------------------------------------------------------------------*/ public boolean prefix(String sRule, String sCur, int cIdx){ return(sCur.regionMatches(cIdx,sRule,0,sRule.length())); } /*------------------------------------------------------------------- Method: rcondiff Purpose: check for a matching right context in a rule and the current position in the "derivation" string Parameters: sRule -- right context in a rule sCur -- derivation string cIdx -- current position in derivation string Returns: true - yes, the rule and right context matched false - no match --------------------------------------------------------------------*/ public boolean rcondiff(String sRule, String sCur, int cIdx){ int rIdx=0; while(true) { if (rIdx >= sRule.length()) { return(true); /* success! */ } else if (cIdx >= sCur.length()) { /* we were at farthest right side of the current derivation level's string (sCur).. so there is no right context.. meaning it would only have matched if there no right context in rule */ return(false); /* no match!! */ } else if (doIgnore(sCur,cIdx)==true) { cIdx++; } else if (sCur.charAt(cIdx) == '[') { cIdx = this.skipright(cIdx,sCur); if (cIdx == -1) { System.out.println("Error! Missing ']' to terminate branch"); return(false); } } else if (!(sRule.charAt(rIdx) == sCur.charAt(cIdx))) { return(false); /* no match!! */ } else { /* matched on this character, so try next */ rIdx++; cIdx++; } } } /*------------------------------------------------------------------ Method: skipright Purpose: skip over a branching section in a string (ie. from [ to ]) and also any subbranches contained in that branch Parameters: sIdx - index into string (assumed to be on first '[' sStr - string to search in Returns: -1 -- error!!! didn't find closing ] other -- index into string after matching ] -------------------------------------------------------------------*/ int skipright(int sIdx, String sStr) { int level = 0; sIdx++; /* get past first [ */ while (sIdx < sStr.length()) { switch(sStr.charAt(sIdx)) { case '[': level++; break; case ']': if (level==0) { return(++sIdx); } else { level--; } break; default: break; } sIdx++; } return((int)-1); /* no matching ] */ } /*------------------------------------------------------------------- Method: lcondiff Purpose: check for a matching left context in a rule and the current position in the "derivation" string. Notice that we move through the string from left to right Parameters: sRule -- right context in a rule sCur -- derivation string cIdx -- current position in derivation string Returns: true - yes, the rule and left context matched false - no match --------------------------------------------------------------------*/ public boolean lcondiff(String sRule, String sCur, int cIdx){ int rIdx=sRule.length(); /* start at end of rule */ rIdx--; while(true) { if (rIdx < 0) { return(true); /* success! */ } else if(cIdx < 0) { /* we were at farthest left side of the current derivation level's string (sCur).. so there is no left context.. meaning it would only have matched if there was no left context in rule */ return(false); /* no match!! */ } else if (doIgnore(sCur,cIdx)==true){ cIdx--; } else if(sCur.charAt(cIdx) == '[') { cIdx--; } else if (sCur.charAt(cIdx) == ']') { cIdx = this.skipleft(cIdx,sCur); if (cIdx == -1) { System.out.println("Error! Missing ']' to terminate branch"); return(false); } } else if (!(sRule.charAt(rIdx) == sCur.charAt(cIdx))) { return(false); /* no match!! */ } else { /* matched on this character, so try next one */ rIdx--; cIdx--; } } } /*------------------------------------------------------------------ Method: skipleft Purpose: skip over a branching section in a string (ie. from [ to ]) and also any subbranches contained in that branch. Notice that we go through through the string from right to left Parameters: sIdx - index into string (assumed to be on first '[' sStr - string to search in Returns: -1 -- error!!! didn't find closing ] other -- index into string after matching ] -------------------------------------------------------------------*/ int skipleft(int sIdx, String sStr) { int level = 0; sIdx--; /* get past first ] */ while (sIdx > 0) { switch(sStr.charAt(sIdx)) { case ']': level++; break; case '[': if (level==0) { return(--sIdx); } else { level--; } break; default: break; } sIdx--; } return((int)-1); /* no matching ] */ } /*---------------------------------------------------------------------- Method: Interpret Purpose: interpret the meaning of the LSystems string Parms: g - graphics object to do drawing in pnl_ht - height of drawing area pnl_wd - width of drawing area ----------------------------------------------------------------------*/ public boolean Interpret(Graphics g, double pnl_ht, double pnl_wd, Color aColors[]) { nIncSz = 1; /* determine points, but don't actually draw.. just find dimensions of bounding box */ draw (sCurLvl,nIncSz,Specs.angle,false,g, aColors); SetDrawParam(pnl_ht, pnl_wd); /* draw it!! */ draw (sCurLvl,nIncSz,Specs.angle,true,g, aColors); return(true); } /*---------------------------------------------------------------------- Method: SetDrawParam Purpose: Adjusts the step size (nIncSz) as well as the starting position of the turtle based upon the bounding box that was required for a step size of one Assumptions: assumes that the bounding box contains the bounding dimensions given a step size of one Globals Changed: nIncSz -- adjust increment size for drawing in current panel dimensions startpos.horiz -- starting position startpos.vert ------------------------------------------------------------------------*/ public void SetDrawParam (double pnl_ht, double pnl_wd){ double xscale, yscale, sc; /* determine how relate a "step" to the width and height of the actual screen */ xscale = (pnl_wd/(boxdim.xmax-boxdim.xmin)); yscale = (pnl_ht/(boxdim.ymax-boxdim.ymin)); /* determine whether the width or height is the tighter bound */ if(xscale>yscale){ sc = yscale; } else { sc = xscale; } /* determine what percentage of the full step should be used based on the scale factor.. if the scale is the maximum scale then the full sized step will be taken */ nIncSz = (int)(Math.floor((double)((sc*Specs.scale)/MAXSCALE))); startpos.horiz = (int)((pnl_wd - (nIncSz*(boxdim.xmin+boxdim.xmax-1)))/2); startpos.vert = (int)((pnl_ht - (nIncSz*(boxdim.ymin+boxdim.ymax-1)))/2); } /*---------------------------------------------------------------------- Method: drawWideLine Purpose: draw a line of a specified width If a line of single width is desired the normal drawline facility is called. If the line is of width greater than one, we must create a polygon (since there is no method (?) of specifying line width in Java, and rectangles cannot be used because they only permit a horizontal orientation) ----------------------------------------------------------------------*/ void drawWideLine(Turtle startpt, Turtle endpt, Graphics g) { int aPolyX[] = new int[4]; /* array of X coords in polygon */ int aPolyY[] = new int[4]; /* array of Y coords in polygon */ Turtle ll1 = new Turtle(); Turtle lr1 = new Turtle(); Turtle ll2 = new Turtle(); Turtle lr2 = new Turtle(); Turtle ll3 = new Turtle(); Turtle lr3 = new Turtle(); Turtle ul = new Turtle(); Turtle ur = new Turtle(); double dAng; if (iLnWidth==1) { g.drawLine((int)startpt.x, (int)startpt.y, (int)endpt.x, (int) endpt.y); } else { /*------------------- assume the start point is at the origin and that the direction is North. Add a left most point and right most point, to give the spread of the line width across the x axis. Then, rotate these two outer points by the amount that the line is to be rotated. This gives us the points ll2 and lr2 which have been rotated about the origin. Add these new points (ll2, lr2) to the start point to give the lower left and lower right points (ll3 and lr3) of the polygon to be drawn -------------------*/ ll1.x = left_adj; ll1.y = 0; lr1.x = right_adj; lr1.y = 0; dAng = -(endpt.dir*RotRad); ll2.x = (Math.cos(dAng)*ll1.x) - (Math.sin(dAng)*ll1.y); ll2.y = (Math.sin(dAng)*ll1.x) + (Math.cos(dAng)*ll1.y); ll3.x = ll2.x+startpt.x; ll3.y = ll2.y+startpt.y; lr2.x = (Math.cos(dAng)*lr1.x) - (Math.sin(dAng)*lr1.y); lr2.y = (Math.sin(dAng)*lr1.x) + (Math.cos(dAng)*lr1.y); lr3.x = lr2.x + startpt.x; lr3.y = lr2.y + startpt.y; /*------------------------- Add the points that were rotated about the origin to the coordinates for the endpoint of the line, giving us the upper left and upper right points of the polygon (ul and ur) --------------------------*/ ul.x = ll2.x+endpt.x; ul.y = ll2.y+endpt.y; ur.x = lr2.x+endpt.x; ur.y = lr2.y+endpt.y; /*------------ Store arrays of X and Y coordinates for drawing Polygons ------------*/ aPolyX[0] = (int)ll3.x; aPolyX[1] = (int)ul.x; aPolyX[2] = (int)ur.x; aPolyX[3] = (int)lr3.x; aPolyY[0] = (int)ll3.y; aPolyY[1] = (int)ul.y; aPolyY[2] = (int)ur.y; aPolyY[3] = (int)lr3.y; g.fillPolygon(aPolyX,aPolyY,4); } } /*----------------------------------------------------------------------- Method: draw 1) Start by drawing to the North - keep a direction specification for the turtle where dir = 0 is North dir = 1 is 360/iAngFac degrees clockwise of north (ie. one rotation right) dir = 2 is 2*(360/iAngFac) degrees clockwise of north (ie. two rotations right) etc... so rotations right add to the dir value, rotations left subtract -----------------------------------------------------------------------*/ boolean draw(String sPattern, int iStepSz, int iAngFac, boolean bDoDraw, Graphics g, Color aColors[]) { double SI[] = new double[MAXANGLE]; double CO[] = new double[MAXANGLE]; Turtle PosStack[] = new Turtle[MAXSTACK]; CurveTurtle CrvPosStack[] = new CurveTurtle[MAXSTACK]; double dAng = -TWO_PI/4; /* -90=270 Degrees */ int i; int iMaxDir; /* maximum direction counter value */ int iStkIdx=-1; /* index into stack array */ int iMaxClrIdx=glb.MAXCOLORS-1; CurveTurtle ct = new CurveTurtle(); int iHalfAngFac = iAngFac/2; /* half of angle factor (for turning around) */ /*---- Structures for storing first 3 points in current curve -----*/ Turtle aFirstPts[] = new Turtle[3]; int iPtsIdx=0; /*------- Structures for creating and storing 4 vertices on a rectangle for producing lines of different width -----*/ left_adj=0; /* left width adjustment */ right_adj=0; /* right width adjustment */ iHalf_Wd = 0; /* half line width, floored */ RotRad = -(TWO_PI/(double)iAngFac); g.setColor(aColors[1]); boxdim.xmin=boxdim.xmax=curpos.x = prevpos.x = (double)(startpos.horiz)+0.5; boxdim.ymin=boxdim.ymax=curpos.y = prevpos.y = (double)(startpos.vert)+0.5; curpos.dir = prevpos.dir = 0; curpos.clrIdx = Specs.start_clr; curpos.lnWidth = iLnWidth = 1; if (Specs.nLnType==glb.LN_HERMITE) { ct.bDoDraw=false; ct.p2.getvals(curpos); } else if (Specs.nLnType==glb.LN_BSPLINE) { ct.bDoDraw=true; ct.p1.getvals(curpos); ct.p2.getvals(curpos); ct.p3.getvals(curpos); ct.p4.getvals(curpos); /* initialize storage of first 3 points in curve */ aFirstPts[0] = new Turtle(); aFirstPts[1] = new Turtle(); aFirstPts[2] = new Turtle(); aFirstPts[0].getvals(curpos); iPtsIdx = 1; } /* precalculate the Sine/Cosine values for all of the possible angle rotations that the turtle can perform */ for (i = 0; i < iAngFac;i++) { SI[i] = (double)(iStepSz) * Math.sin(dAng); CO[i] = (double)(iStepSz) * Math.cos(dAng); dAng += TWO_PI/(double)iAngFac; } iMaxDir = --iAngFac; g.setColor(aColors[curpos.clrIdx]); iLnWidth = curpos.lnWidth; /* move through string from left to right and manipulate the turtle on the screen as the pattern string specifies */ for (i = 0; i < sPattern.length(); i++) { switch (sPattern.charAt(i)){ case '|': /* turn around (180 degrees) */ /*--------- if current direction is more than a 180 degree rotation, then subtract 180 or if less than 180 degrees then add ---------*/ if (curpos.dir>=iHalfAngFac) { curpos.dir -= iHalfAngFac; } else { curpos.dir += iHalfAngFac; } break; case '#': /* increment line width */ curpos.lnWidth++; iHalf_Wd=(int)(Math.floor(curpos.lnWidth/2)); /*------------ if odd width, adjust x same both left and right if even width, adjust x one less on left ---------*/ if ((curpos.lnWidth%2)==1) { left_adj = -iHalf_Wd; right_adj = iHalf_Wd; } else { left_adj = -(iHalf_Wd-1); right_adj = iHalf_Wd; } iLnWidth = curpos.lnWidth; break; case '!': /* decrement line width */ if (curpos.lnWidth>1) { curpos.lnWidth--; iHalf_Wd=(int)(Math.floor(curpos.lnWidth/2)); /*------------ if odd width, adjust x same both left and right if even width, adjust x one less on left ---------*/ if ((curpos.lnWidth%2)==1) { left_adj = -iHalf_Wd; right_adj = iHalf_Wd; } else { left_adj = -(iHalf_Wd-1); right_adj = iHalf_Wd; } } iLnWidth = curpos.lnWidth; break; case ';': /* increment color */ if (curpos.clrIdx == iMaxClrIdx) { curpos.clrIdx = 0; } else { curpos.clrIdx++; } g.setColor(aColors[curpos.clrIdx]); break; case ':': /* decrement color */ if (curpos.clrIdx == 0) { curpos.clrIdx = iMaxClrIdx; } else { curpos.clrIdx--; } g.setColor(aColors[curpos.clrIdx]); break; case '+': /* right */ if (curpos.dir < iMaxDir) { curpos.dir++; } else { curpos.dir = 0; /* did a full 360 */ } break; case '-': /* left */ if (curpos.dir > 0) { curpos.dir--; } else { curpos.dir = iMaxDir; } break; case '[': /* branching */ iStkIdx++; if (iStkIdx < MAXSTACK) { /* push current position onto stack */ PosStack[iStkIdx] = new Turtle(); PosStack[iStkIdx].getvals(curpos); CrvPosStack[iStkIdx] = new CurveTurtle(); CrvPosStack[iStkIdx].getvals(ct); } else { System.out.println("Error! Too many branches."); return(false); } break; case ']': /* returning from branch */ if (iStkIdx < 0) { System.out.println("Error! Missing closing ']'"); return(false); } else { /* pop branching position off of stack */ curpos.getvals(PosStack[iStkIdx]); prevpos.getvals(curpos); if (bDoDraw==true) { /* draw first and last curve segments of previous curve */ if (Specs.nLnType==glb.LN_BSPLINE) { } } ct.getvals(CrvPosStack[iStkIdx]); iStkIdx--; } iLnWidth = curpos.lnWidth; g.setColor(aColors[curpos.clrIdx]); break; case 'F': /* go forward and draw line */ /* x' = x + cos(ang) y' = y + sin(ang) */ curpos.x += CO[curpos.dir]; curpos.y += SI[curpos.dir]; /* draw line or update box dimension settings */ if (bDoDraw==true) { /*---------------------------- determine what type of line to draw straight, Hermite curve or BSpline curve -----------------------*/ switch (Specs.nLnType) { case glb.LN_HERMITE: if (ct.bDoDraw==true) { ct.p4.x = curpos.x-ct.p2.x; ct.p4.y = curpos.y-ct.p2.y; DrawHermite(ct,iStepSz,g); /*ct.bDoDraw=true;*/ ct.p2.getvals(ct.p3); ct.p3.getvals(curpos); ct.p1.getvals(ct.p4); } else { /*------------------------------------------ We have got only the start position for drawing a segment, so we can't draw yet ---------------------------------------*/ ct.bDoDraw=true; ct.p3.getvals(curpos); ct.p1.x = curpos.x-ct.p2.x; ct.p1.y = curpos.y-ct.p2.y; } break; case glb.LN_BSPLINE: /*------------------------ draw BSpline curve -----------------------*/ ct.p1.getvals(ct.p2); ct.p2.getvals(ct.p3); ct.p3.getvals(ct.p4); ct.p4.getvals(curpos); /*------------- don't draw curve for first 3 points, just store their locations (for generating the first curve segment later) -------------*/ if (iPtsIdx<3) { aFirstPts[iPtsIdx].getvals(curpos); iPtsIdx++; } else { DrawBSpline(ct,iStepSz,g); } break; case glb.LN_STRT: drawWideLine(prevpos, curpos, g); break; } prevpos.getvals(curpos); /* copy position */ } else { UpdateBox(curpos); } break; case 'f': /* go forward without drawing line */ /* x' = x + cos(ang) y' = y + sin(ang) */ curpos.x += CO[curpos.dir]; curpos.y += SI[curpos.dir]; /* update previous position or update box dimension settings */ if (bDoDraw==true) { /* draw first and last curve segments of previous curve */ if (Specs.nLnType==glb.LN_BSPLINE) { DrawFirstNLast(ct, aFirstPts,g,iStepSz,iPtsIdx); } if (Specs.nLnType==glb.LN_HERMITE) { ct.bDoDraw=false; ct.p2.getvals(curpos); } else if (Specs.nLnType==glb.LN_BSPLINE) { ct.bDoDraw=true; ct.p1.getvals(curpos); ct.p2.getvals(curpos); ct.p3.getvals(curpos); ct.p4.getvals(curpos); /* initialize storage of first 3 points in curve */ aFirstPts[0].getvals(curpos); iPtsIdx = 1; } prevpos.getvals(curpos); /* copy position*/ } else { UpdateBox(curpos); } break; } } if (bDoDraw==true) { /* draw first and last curve segments of previous curve */ if (Specs.nLnType==glb.LN_BSPLINE) { DrawFirstNLast(ct, aFirstPts,g,iStepSz,iPtsIdx); } } return(true); } /*---------------------------------------------------------------------- Method: DrawFirstNLast Purpose: Draw the first and last BSpline curve segments in a total curve. Algorithm: If the start and end of a curve meet up then we can use the points at the start and end to control the curve segment. -----------------------------------------------------------------------*/ void DrawFirstNLast(CurveTurtle final_ct, Turtle aFirstPts[], Graphics g, int iStepSz, int iPtsIdx) { if ((Math.floor(final_ct.p4.x) == Math.floor(aFirstPts[0].x))&&(Math.floor(final_ct.p4.y) == Math.floor(aFirstPts[0].y))) { /*--------------- start of curve joins with end of curve --------------*/ /* Draw end segment of curve */ final_ct.p1.getvals(final_ct.p2); final_ct.p2.getvals(final_ct.p3); final_ct.p3.getvals(final_ct.p4); final_ct.p4.getvals(aFirstPts[1]); DrawBSpline(final_ct,iStepSz,g); /* Draw start segment of curve */ final_ct.p1 = final_ct.p2; final_ct.p2 = final_ct.p3; final_ct.p3 = final_ct.p4; final_ct.p4 = aFirstPts[2]; DrawBSpline(final_ct,iStepSz,g); } else { /*-------------- start of curve is independent of end of curve -------------*/ /* Draw end segment of curve, depending on last point as last TWO control points */ final_ct.p1.getvals(final_ct.p2); final_ct.p2.getvals(final_ct.p3); final_ct.p3.getvals(final_ct.p4); final_ct.p4 = final_ct.p4; DrawBSpline(final_ct,iStepSz,g); /* only draw first curve segment if more than one forward movement ('F') was encountered */ if (iPtsIdx>2) { /* Draw start segment of curve, depending on first point as first TWO control points */ final_ct.p1 = aFirstPts[0]; final_ct.p2 = aFirstPts[0]; final_ct.p3 = aFirstPts[1]; final_ct.p4 = aFirstPts[2]; DrawBSpline(final_ct,iStepSz,g); } } } /*---------------------------------------------------------------------- Method: UpdateBox Purpose: Extend the current dimensions of the box if the position sent specifies an area outside of the box ----------------------------------------------------------------------*/ void UpdateBox(Turtle boxpos) { boxdim.xmin = Math.min(boxpos.x, boxdim.xmin); boxdim.xmax = Math.max(boxpos.x, boxdim.xmax); boxdim.ymin = Math.min(boxpos.y, boxdim.ymin); boxdim.ymax = Math.max(boxpos.y, boxdim.ymax); } /*--------------------------------------------------------------------- Method: DrawHermite Purpose: Draw a Hermite curve between two points, given the start point, end point, start tangent and end tangent Param: ct -- curved turtle specifying start and end points and tangent vectors p1 - start tangent vector p2 - start point p3 - end point p4 - end tangent vector numParts -- number of partitions for discretizing curve ----------------------------------------------------------------------*/ void DrawHermite(CurveTurtle ct, int numParts, Graphics g){ int i; double t; double t_inc; Turtle Qcur, Qprev; /* positions on curve as drawn */ t_inc = (double)(1/(((double)numParts-1))); Qcur = new Turtle(); Qprev = new Turtle(); Qprev.getvals(ct.p2); for (i=1,t=t_inc;i right context --> successor and in essence the predecessor translates into the successor, and the left and right context provide further information to choose the proper production rule based on what appears before and after the predecessor ==============================================================================*/ class Production extends Object { public String lCtxt; /* left context string */ public int lCtxtLen; /* left context string length */ public String pred; /* predecessor string */ public int predLen; /* predecessor string length */ public String rCtxt; /* right context string */ public int rCtxtLen; /* right context string length */ public String succ; /* successor string */ public int succLen; /* successor string length */ } /*============================================================================== Class: Pixel Contents: pixel position in the screen horiz - horizontal position vert - vertical position ==============================================================================*/ class Pixel extends Object { public int horiz; /* horizontal */ public int vert; /* vertical */ } /*============================================================================== Class: Box Contents: defines dimensions of the bounding box for a drawing ================================================================================*/ class Box extends Object { double xmin, xmax; double ymin, ymax; } /*=============================================================================== Class: Turtle Contents: the current orientation of the turtle in the drawing x - x coordinate y - y coordinate dir - direction of turtle (ie. the number of rotations right from the start position) clrIdx - current color index of lines lnWidth - width of lines ===============================================================================*/ class Turtle extends Object { public double x; public double y; public int dir; public int clrIdx; public int lnWidth; /*--------------------------------------------------------------------- Method: getvals Purpose: copy over the values from another turtle structure into this one ---------------------------------------------------------------------*/ public void getvals(Turtle t) { this.x = t.x; this.y = t.y; this.dir = t.dir; this.clrIdx = t.clrIdx; this.lnWidth = t.lnWidth; } } /*=============================================================================== Class: CurveTurtle Contents: contains turtle information for drawing a hermite curve ===============================================================================*/ class CurveTurtle extends Object { public Turtle p1; /* first tangent vector, or first control point */ public Turtle p2; /* start position for curve, or second control pt */ public Turtle p3; /* end position for curve, or third control pt */ public Turtle p4; /* end tangent vector, or fourth control pt */ public boolean bDoDraw; /* flag whether draw between points or not */ /*------------------ Constructor -------------------*/ CurveTurtle() { this.p1 = new Turtle(); this.p2 = new Turtle(); this.p3 = new Turtle(); this.p4 = new Turtle(); } /*--------------------------------------------------------------------- Method: getvals Purpose: copy over the values from another curve turtle into this one ---------------------------------------------------------------------*/ public void getvals(CurveTurtle t) { this.p1.getvals(t.p1); this.p2.getvals(t.p2); this.p3.getvals(t.p3); this.p4.getvals(t.p4); this.bDoDraw = t.bDoDraw; } }