Source of FractalPlants.java


  2: import java.util.Scanner;
  3: import java.awt.*;
  4: import java.awt.image.BufferedImage;
  5: import javax.swing.*;

  7: /**
  8:  * My attempt to create the fractal plant described in
  9:  * http://en.wikipedia.org/wiki/Lindenmayer_system
 10:  *
 11:  * @author Mark Young
 12:  * @version 1.0
 13:  */
 14: public class FractalPlants extends JFrame {

 16:     // default width for fern windows
 17:     public static final int DEFAULT_WIDTH = 800;
 18:     public static final int DEFAULT_HEIGHT = 600;
 19:     public static final int ANGLE_ADJUSTMENT = 25;
 20:     public static final double MIN_SIZE = 20.0;

 22:     // here is the program -- the class is below
 23:     public static void main(String[] args) {
 24:         Scanner kbd = new Scanner(System.in);

 26:         // introduce yourself
 27:         System.out.println("\n\n"
 28:                 + "Plant Drawing Program\n"
 29:                 + "---------------------\n\n");

 31:         // suggest values
 32:         System.out.println("Might I suggest:\n"
 33:                 + "\tMake the window 500 by 600.\n"
 34:                 + "\tPlace the root at 50 600.\n"
 35:                 + "\tSet the angle to 160.\n"
 36:                 + "\tMake the plant 550 pixels tall.\n");

 38:         // get window parameters (all windows the same size)
 39:         System.out.print("How big should the windows be (width height)? ");
 40:         int width = kbd.nextInt();
 41:         int height = kbd.nextInt();
 42:         kbd.nextLine();
 43:         if (width <= 0) {
 44:             width = DEFAULT_WIDTH;
 45:         }
 46:         if (height <= 0) {
 47:             height = DEFAULT_HEIGHT;
 48:         }

 50:         // repeat
 51:         String resp;
 52:         do {
 53:             // get root location
 54:             System.out.println();
 55:             System.out.print("Where should the root of the fern be? ");
 56:             int x = kbd.nextInt();
 57:             int y = kbd.nextInt();
 58:             kbd.nextLine();

 60:             // get angle of growth
 61:             System.out.print("What angle should the plant grow at "
 62:                     + "(180 = straight up)? ");
 63:             int angle = kbd.nextInt();
 64:             kbd.nextLine();

 66:             // get fern size
 67:             System.out.print("And what height should the plant be? ");
 68:             int size = kbd.nextInt();
 69:             kbd.nextLine();

 71:             // draw that fern in a new window
 72:             FractalPlants win = new FractalPlants(width, height, x, y, angle, size);
 73:             win.setVisible(true);

 75:             // ask if there's more
 76:             System.out.print("another (y/n)> ");
 77:             resp = kbd.nextLine().toUpperCase();
 78:         } while (resp.startsWith("Y"));
 79:         System.exit(0);
 80:     }

 82:     // here is the class
 83:     private final BufferedImage img;
 84:     private final Graphics canvas;
 85:     private final int w, h;
 86:     private final double rootX, rootY;
 87:     private final double rootAngle;
 88:     private final double fullSize;

 90:     // constructor sets instance variables
 91:     /**
 92:      * Create a window and draw the plant in it.
 93:      *
 94:      * @param w width of the window
 95:      * @param h height of the window
 96:      * @param x x-coordinate of the plant's root
 97:      * @param y y-coordinate of the plant's root
 98:      * @param a angle the plant grows at
 99:      * @param s size of the plant (base to tip; approximate)
100:      */
101:     public FractalPlants(int w, int h, double x, double y, double a, double s) {
102:         super("A Plant by MYoung");
103:         setSize(w, h);
104:         setBackground(Color.BLACK);
105:         this.w = w;
106:         this.h = h;
107:         rootX = x;
108:         rootY = y;
109:         rootAngle = a;
110:         fullSize = s;
111:         img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
112:         canvas = img.createGraphics();
113:         // canvas.setColor(Color.BLACK);
114:         canvas.fillRect(0, 0, w, h);
115:         canvas.setColor(new Color(0, 127, 0));
116:         drawPlant(rootX, rootY, rootAngle, fullSize);
117:     }

119:     /**
120:      * Respond to the computer's request to paint the window. This method is
121:      * called when the computer shows the window.
122:      *
123:      * @param g the canvas provided by the computer
124:      */
125:     @Override
126:     public void paint(Graphics g) {
127:         g.drawImage(img,
128:                 0, 0, w, h,
129:                 0, 0, w, h,
130:                 null);
131:     }

133:     /**
134:      * Draw the plant.
135:      *
136:      * @param x     x-coordinate of the plant's root
137:      * @param y     y-coordinate of the plant's root
138:      * @param angle angle the plant grows from its root
139:      * @param size  size of the plant
140:      */
141:     public void drawPlant(double x, double y, double angle, double size) {
142:         // draw the stem of the plant
143:         double length = size * 0.2;
144:         double[] end = drawStem(x, y, angle, length);

146:         // if more should be drawn...
147:         if (size > MIN_SIZE) {
148:             // ...draw more of the plant
149:             double smaller = size * 0.5;
150:             // a piece growing out of the stem at an angle
151:             drawPlant(end[0], end[1], angle + ANGLE_ADJUSTMENT, smaller);
152:             // a piece continuing in the same direction as the stem
153:             drawPlant(end[0], end[1], angle, smaller);
154:             // move up the stem
155:             end = drawStem(end[0], end[1], angle, length);
156:             // grow out of the stem at the opposite angle
157:             end = drawStem(end[0], end[1], angle - ANGLE_ADJUSTMENT, length);
158:             // two more pieces growing out the end of this stem
159:             drawPlant(end[0], end[1], angle - ANGLE_ADJUSTMENT, smaller);
160:             drawPlant(end[0], end[1], angle, smaller);
161:         }
162:     }

164:     /**
165:      * Draw the stem of the plant.
166:      * (You don't need to know how this method works.)
167:      *
168:      * @param x         x-coordinate of the stem's root
169:      * @param y         y-coordinate of the stem's root
170:      * @param angle     angle the plant grows from its root
171:      * @param length    the length of the stem
172:      * @return  the end point of the stem
173:      */
174:     public double[] drawStem(double x, double y, double angle, double length) {
175:         // convert to radians for sin and cos functions
176:         double radians = Math.toRadians(angle);

178:         // figure out (and remember) the stem's end point
179:         double[] end = new double[2];
180:         end[0] = x + Math.sin(radians) * length;
181:         end[1] = y + Math.cos(radians) * length;

183:         // draw the stem
184:         drawLine(x, y, end[0], end[1]);

186:         // return its end point
187:         return end;
188:     }

190:     /**
191:      * Draw a line between two points on the canvas.
192:      * (You do not need to understand how this method works.)
193:      *
194:      * @param x1    x-coordinate of the line's starting point
195:      * @param y1    y-coordinate of the line's starting point
196:      * @param x2    x-coordinate of the line's ending point
197:      * @param y2    y-coordinate of the line's ending point
198:      */
199:     public void drawLine(double x1, double y1, double x2, double y2) {
200:         // round off the coordinates...
201:         int startX = (int) Math.round(x1);
202:         int startY = (int) Math.round(y1);
203:         int endX = (int) Math.round(x2);
204:         int endY = (int) Math.round(y2);

206:         // ...because the canvas is expecting integers
207:         canvas.drawLine(startX, startY, endX, endY);
208:     }
209: }