blob: 631a516c87db3fa441d897822ee14f04c277f845 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of Sun Microsystems nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 */
34
35import java.awt.Graphics;
36import java.util.Stack;
37import java.util.Vector;
38import java.awt.event.*;
39
40/**
41 * A (not-yet) Context sensitive L-System Fractal applet class.
42 *
43 * The rules for the Context L-system are read from the java.applet.Applet's
44 * attributes and then the system is iteratively applied for the
45 * given number of levels, possibly drawing each generation as it
46 * is generated. Note that the ContextLSystem class does not yet
47 * handle the lContext and rContext attributes, although this
48 * class is already designed to parse the '[' and ']' characters
49 * typically used in Context sensitive L-Systems.
50 *
51 * @author Jim Graham
52 */
53public class CLSFractal
54 extends java.applet.Applet
55 implements Runnable, MouseListener {
56 Thread kicker;
57 ContextLSystem cls;
58 int fractLevel = 1;
59 int repaintDelay = 50;
60 boolean incrementalUpdates;
61 float startAngle = 0;
62 float rotAngle = 45;
63 float Xmin;
64 float Xmax;
65 float Ymin;
66 float Ymax;
67 int border;
68 boolean normalizescaling;
69
70 public void init() {
71 String s;
72 cls = new ContextLSystem(this);
73 s = getParameter("level");
74 if (s != null) fractLevel = Integer.parseInt(s);
75 s = getParameter("incremental");
76 if (s != null) incrementalUpdates = s.equalsIgnoreCase("true");
77 s = getParameter("delay");
78 if (s != null) repaintDelay = Integer.parseInt(s);
79 s = getParameter("startAngle");
80 if (s != null) startAngle = Float.valueOf(s).floatValue();
81 s = getParameter("rotAngle");
82 if (s != null) rotAngle = Float.valueOf(s).floatValue();
83 rotAngle = rotAngle / 360 * 2 * 3.14159265358f;
84 s = getParameter("border");
85 if (s != null) border = Integer.parseInt(s);
86 s = getParameter("normalizescale");
87 if (s != null) normalizescaling = s.equalsIgnoreCase("true");
88 addMouseListener(this);
89 }
90
91 public void destroy() {
92 removeMouseListener(this);
93 }
94
95 public void run() {
96 Thread me = Thread.currentThread();
97 boolean needsRepaint = false;
98 while (kicker == me && cls.getLevel() < fractLevel) {
99 cls.generate();
100 if (kicker == me && incrementalUpdates) {
101 repaint();
102 try {Thread.sleep(repaintDelay);} catch (InterruptedException e){}
103 } else {
104 needsRepaint = true;
105 }
106 }
107 if (kicker == me) {
108 kicker = null;
109 if (needsRepaint) {
110 repaint();
111 }
112 }
113 }
114
115 public void start() {
116 kicker = new Thread(this);
117 kicker.start();
118 }
119
120 public void stop() {
121 kicker = null;
122 }
123
124 /*1.1 event handling */
125 public void mouseClicked(MouseEvent e) {
126 }
127
128 public void mousePressed(MouseEvent e) {
129 }
130
131 public void mouseReleased(MouseEvent e) {
132 cls = new ContextLSystem(this);
133 savedPath = null;
134 start();
135 e.consume();
136 }
137
138 public void mouseEntered(MouseEvent e) {
139 }
140
141 public void mouseExited(MouseEvent e) {
142 }
143
144 String savedPath;
145
146 public void paint(Graphics g) {
147 String fractalPath = cls.getPath();
148 if (fractalPath == null) {
149 super.paint(g);
150 return;
151 }
152 if (savedPath == null || !savedPath.equals(fractalPath)) {
153 savedPath = fractalPath;
154 render(null, fractalPath);
155 }
156
157 for (int i = 0; i < border; i++) {
158 g.draw3DRect(i, i, getSize().width - i * 2, getSize().height - i * 2,false);
159 }
160 render(g, fractalPath);
161 }
162
163 void render(Graphics g, String path) {
164 Stack turtleStack = new Stack();
165 CLSTurtle turtle;
166
167 if (g == null) {
168 Xmin = 1E20f;
169 Ymin = 1E20f;
170 Xmax = -1E20f;
171 Ymax = -1E20f;
172 turtle = new CLSTurtle(startAngle, 0, 0, 0, 0, 1, 1);
173 } else {
174 float frwidth = Xmax - Xmin;
175 if (frwidth == 0)
176 frwidth = 1;
177 float frheight = Ymax - Ymin;
178 if (frheight == 0)
179 frheight = 1;
180 float xscale = (getSize().width - border * 2 - 1) / frwidth;
181 float yscale = (getSize().height - border * 2 - 1) / frheight;
182 int xoff = border;
183 int yoff = border;
184 if (normalizescaling) {
185 if (xscale < yscale) {
186 yoff += ((getSize().height - border * 2)
187 - ((Ymax - Ymin) * xscale)) / 2;
188 yscale = xscale;
189 } else if (yscale < xscale) {
190 xoff += ((getSize().width - border * 2)
191 - ((Xmax - Xmin) * yscale)) / 2;
192 xscale = yscale;
193 }
194 }
195 turtle = new CLSTurtle(startAngle, 0 - Xmin, 0 - Ymin,
196 xoff, yoff, xscale, yscale);
197 }
198
199 for (int pos = 0; pos < path.length(); pos++) {
200 switch (path.charAt(pos)) {
201 case '+':
202 turtle.rotate(rotAngle);
203 break;
204 case '-':
205 turtle.rotate(-rotAngle);
206 break;
207 case '[':
208 turtleStack.push(turtle);
209 turtle = new CLSTurtle(turtle);
210 break;
211 case ']':
212 turtle = (CLSTurtle) turtleStack.pop();
213 break;
214 case 'f':
215 turtle.jump();
216 break;
217 case 'F':
218 if (g == null) {
219 includePt(turtle.X, turtle.Y);
220 turtle.jump();
221 includePt(turtle.X, turtle.Y);
222 } else {
223 turtle.draw(g);
224 }
225 break;
226 default:
227 break;
228 }
229 }
230 }
231
232 void includePt(float x, float y) {
233 if (x < Xmin)
234 Xmin = x;
235 if (x > Xmax)
236 Xmax = x;
237 if (y < Ymin)
238 Ymin = y;
239 if (y > Ymax)
240 Ymax = y;
241 }
242
243 public String getAppletInfo() {
244 return "Title: CLSFractal 1.1f, 27 Mar 1995 \nAuthor: Jim Graham \nA (not yet) Context Sensitive L-System production rule. \nThis class encapsulates a production rule for a Context Sensitive\n L-System \n(pred, succ, lContext, rContext). The matches() method, however, does not \n(yet) verify the lContext and rContext parts of the rule.";
245 }
246
247 public String[][] getParameterInfo() {
248 String[][] info = {
249 {"level", "int", "Maximum number of recursions. Default is 1."},
250 {"incremental","boolean","Whether or not to repaint between recursions. Default is true."},
251 {"delay","integer","Sets delay between repaints. Default is 50."},
252 {"startAngle","float","Sets the starting angle. Default is 0."},
253 {"rotAngle","float","Sets the rotation angle. Default is 45."},
254 {"border","integer","Width of border. Default is 2."},
255 {"normalizeScale","boolean","Whether or not to normalize the scaling. Default is true."},
256 {"pred","String","Initializes the rules for Context Sensitive L-Systems."},
257 {"succ","String","Initializes the rules for Context Sensitive L-Systems."},
258 {"lContext","String","Initializes the rules for Context Sensitive L-Systems."},
259 {"rContext","String","Initializes the rules for Context Sensitive L-Systems."}
260 };
261 return info;
262 }
263}
264
265/**
266 * A Logo turtle class designed to support Context sensitive L-Systems.
267 *
268 * This turtle performs a few basic maneuvers needed to support the
269 * set of characters used in Context sensitive L-Systems "+-fF[]".
270 *
271 * @author Jim Graham
272 */
273class CLSTurtle {
274 float angle;
275 float X;
276 float Y;
277 float scaleX;
278 float scaleY;
279 int xoff;
280 int yoff;
281
282 public CLSTurtle(float ang, float x, float y,
283 int xorg, int yorg, float sx, float sy) {
284 angle = ang;
285 scaleX = sx;
286 scaleY = sy;
287 X = x * sx;
288 Y = y * sy;
289 xoff = xorg;
290 yoff = yorg;
291 }
292
293 public CLSTurtle(CLSTurtle turtle) {
294 angle = turtle.angle;
295 X = turtle.X;
296 Y = turtle.Y;
297 scaleX = turtle.scaleX;
298 scaleY = turtle.scaleY;
299 xoff = turtle.xoff;
300 yoff = turtle.yoff;
301 }
302
303 public void rotate(float theta) {
304 angle += theta;
305 }
306
307 public void jump() {
308 X += (float) Math.cos(angle) * scaleX;
309 Y += (float) Math.sin(angle) * scaleY;
310 }
311
312 public void draw(Graphics g) {
313 float x = X + (float) Math.cos(angle) * scaleX;
314 float y = Y + (float) Math.sin(angle) * scaleY;
315 g.drawLine((int) X + xoff, (int) Y + yoff,
316 (int) x + xoff, (int) y + yoff);
317 X = x;
318 Y = y;
319 }
320}
321
322/**
323 * A (non-)Context sensitive L-System class.
324 *
325 * This class initializes the rules for Context sensitive L-Systems
326 * (pred, succ, lContext, rContext) from the given java.applet.Applet's attributes.
327 * The generate() method, however, does not (yet) apply the lContext
328 * and rContext parts of the rules.
329 *
330 * @author Jim Graham
331 */
332class ContextLSystem {
333 String axiom;
334 Vector rules = new Vector();
335 int level;
336
337 public ContextLSystem(java.applet.Applet app) {
338 axiom = app.getParameter("axiom");
339 int num = 1;
340 while (true) {
341 String pred = app.getParameter("pred"+num);
342 String succ = app.getParameter("succ"+num);
343 if (pred == null || succ == null) {
344 break;
345 }
346 rules.addElement(new CLSRule(pred, succ,
347 app.getParameter("lContext"+num),
348 app.getParameter("rContext"+num)));
349 num++;
350 }
351 currentPath = new StringBuffer(axiom);
352 level = 0;
353 }
354
355 public int getLevel() {
356 return level;
357 }
358
359 StringBuffer currentPath;
360
361 public synchronized String getPath() {
362 return ((currentPath == null) ? null : currentPath.toString());
363 }
364
365 private synchronized void setPath(StringBuffer path) {
366 currentPath = path;
367 level++;
368 }
369
370 public void generate() {
371 StringBuffer newPath = new StringBuffer();
372 int pos = 0;
373 while (pos < currentPath.length()) {
374 CLSRule rule = findRule(pos);
375 if (rule == null) {
376 newPath.append(currentPath.charAt(pos));
377 pos++;
378 } else {
379 newPath.append(rule.succ);
380 pos += rule.pred.length();
381 }
382 }
383 setPath(newPath);
384 }
385
386 public CLSRule findRule(int pos) {
387 for (int i = 0; i < rules.size(); i++) {
388 CLSRule rule = (CLSRule) rules.elementAt(i);
389 if (rule.matches(currentPath, pos)) {
390 return rule;
391 }
392 }
393 return null;
394 }
395}
396
397/**
398 * A Context sensitive L-System production rule.
399 *
400 * This class encapsulates a production rule for a Context sensitive
401 * L-System (pred, succ, lContext, rContext).
402 * The matches() method, however, does not (yet) verify the lContext
403 * and rContext parts of the rule.
404 *
405 * @author Jim Graham
406 */
407class CLSRule {
408 String pred;
409 String succ;
410 String lContext;
411 String rContext;
412
413 public CLSRule(String p, String d, String l, String r) {
414 pred = p;
415 succ = d;
416 lContext = l;
417 rContext = r;
418 }
419
420 public boolean matches(StringBuffer sb, int pos) {
421 if (pos + pred.length() > sb.length()) {
422 return false;
423 }
424 char cb[] = new char[pred.length()];
425 sb.getChars(pos, pos + pred.length(), cb, 0);
426 return pred.equals(new String(cb));
427 }
428}