// dragoric.java - from fergusmurray.members.beeb.net // 'Murray Dragons' // or what happens to the curlicue fractal // (which is a discretisation of the equations of the Cornu spiral/Fresnel // spiral/Euler's spiral/the clothoid) // when you animate it. // This version Fergus Crawshay Murray, January 2000. // Any comments or suggestions to dragons@fergusmurray.co.uk. import java.awt.*; import java.applet.Applet; public class Dragoric extends java.applet.Applet implements Runnable { // Notes on variables: // _frame - incremented each 'frame' // _brush - represents the kind of 'paintbrush' used - 0 means lines, // n (!=0) means squares of size n // _symm - kind of symmetry imposed // _dir - whether the shape is going in 'reverse' or not // _ang - angle of the turtle // _angV - rate of change of _ang // _seed - rate of change of _angV, main crucial param in overall shape // _sin[] - sine lookup table (also used for cosine) // _pal[] - all the colours // _clear - true if it's time to clear the frame // _leng - length of 'dragon' calculated each frame // _prop - length actually drawn (which part is drawn shifts each frame) // _rate - indicates the rate of change of the 'dragon' // _zoom - level of zoom // _ecce - 'eccentricity' - distance from centre each 'dragon' starts at // _wipe - number of frames between automatic wipes // (-1 means automatic wiping is turned off) // _wipeCnt - number of frames until the next automatic wipe. Thread _thr; Color _pal [] = new Color [256]; double _sin [] = new double [257]; boolean _clear = true, _paused = false; int _frame = 0, _w, _h, _cX, _cY, _brush, _symm = 1, _dir = 1; double _seed = 0.0001; int _leng = 72, _prop = 64, _rate = 100, _zoom = 4, _ecce = 0, _wipe = -1, _wipeCnt; public Scrollbar lengBar, propBar, rateBar, zoomBar, ecceBar, wipeBar; public Label wipeLbl; public TextField seedBox; public Checkbox reverseBox, pauseBox; public CheckboxGroup drawGrp, symmGrp; public void init () { int i; float hue = 0; // Create lookup tables for colour and trig funcs. get w, h, center for (i = 0; i < _pal.length; i++) { _pal[i] = Color.getHSBColor (hue, (float)1.0, (float)1.0); hue += 0.0036; } for (i = 0; i < _sin.length;i++) {_sin [i] = Math.sin (i*Math.PI/128);} _w = getWidth(); _h = getHeight(); _cX = _w/2; _cY = _h/2; // build the dang gui setBackground (Color.black); setLayout (new BorderLayout ()); Panel pan = new Panel (); add ("East", pan); pan.setBackground (Color.black); pan.setLayout (new GridLayout (34, 1)); seedBox = new TextField (); pan.setForeground (Color.white); pan.add (new Label ("Seed")); pan.setBackground (Color.white); pan.setForeground (Color.black); pan.add (seedBox); pan.setForeground (Color.black); seedBox.setText ("0.0001"); Button seedBtn = new Button ("Input seed"); Button reseedBtn = new Button ("Random seed"); Button wipeBtn = new Button ("Wipe"); pan.add (seedBtn); pan.add (reseedBtn); pan.add (wipeBtn); pan.setBackground (Color.black); pan.add (new Panel ()); pan.setForeground (Color.white); pan.add (new Label ("Length")); lengBar = new Scrollbar (Scrollbar.HORIZONTAL, 144, 4, 1, 5555); pan.add (lengBar); pan.add (new Label ("Length visible")); propBar = new Scrollbar (Scrollbar.HORIZONTAL, 5555, 4, 1, 5555); pan.add (propBar); pan.add (new Label ("Rate of curl")); rateBar = new Scrollbar (Scrollbar.HORIZONTAL, 768, 4, 1, 10000); pan.add (rateBar); pan.add (new Label ("Zoom")); zoomBar = new Scrollbar (Scrollbar.HORIZONTAL, 8, 4, 1, 22); pan.add (zoomBar); pan.add (new Label ("Eccentricity")); ecceBar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 4, 0, 300); pan.add (ecceBar); wipeLbl = new Label ("Wipe"); wipeBar = new Scrollbar (Scrollbar.HORIZONTAL, 1000, 4, 1, 1000); pan.add (wipeLbl); pan.add (wipeBar); pan.add (new Panel ()); drawGrp = new CheckboxGroup (); pan.add (new Checkbox ("Lines", drawGrp, true)); pan.add (new Checkbox ("Dots", drawGrp, false)); pan.add (new Checkbox ("2x2 squares", drawGrp, false)); pan.add (new Checkbox ("3x3 squares", drawGrp, false)); pan.add (new Panel ()); pan.add (new Label ("Symmetry:")); symmGrp = new CheckboxGroup (); pan.add (new Checkbox ("1 R", symmGrp, true)); pan.add (new Checkbox ("2 R", symmGrp, false)); pan.add (new Checkbox ("1 R, 1 M", symmGrp, false)); pan.add (new Checkbox ("1 R, 2 M", symmGrp, false)); pan.add (new Panel ()); reverseBox = new Checkbox ("Reverse", null, false); pan.add (reverseBox); pauseBox = new Checkbox ("Pause", null, false); pan.add (pauseBox); scrollUpdate (); checkUpdate (); } public boolean action (Event evt, Object arg) // deal with any button presses. { if (evt.target instanceof Button) { String bname = (String)arg; if (bname.equals ("Random seed")) { seedBox.setText (Double.toString (Math.random ())); _seed = ((Double.valueOf (seedBox.getText ())).doubleValue ()) * (2*Math.PI); _clear = true; } if (bname.equals ("Input seed")){ // seedBox.setText ("0.0001"); // reset it _seed = ((Double.valueOf (seedBox.getText ())).doubleValue ()) * (2*Math.PI); _clear = true; } if (bname.equals ("Wipe")) _clear = true; return true; } else return false; } public boolean keyDown (Event evt, int key) // check if the Enter key has been pressed. (to input seed) { if (key == Event.ENTER) { _clear = true; _seed = ((Double.valueOf (seedBox.getText ())).doubleValue ()) * (2*Math.PI); return true; } else return false; } public boolean handleEvent (Event evt) // check for other user-interface events. { if (evt.target instanceof Scrollbar) {scrollUpdate (); return true;} else if (evt.id==Event.MOUSE_DOWN || evt.id==Event.MOUSE_DRAG) {_cX = evt.x; _cY = evt.y; return true;} else if (evt.target instanceof Checkbox) {checkUpdate (); return true;} return super.handleEvent (evt); } public void scrollUpdate () // update values associated with scrollbars. { _leng = lengBar.getValue (); _prop = propBar.getValue (); _rate = rateBar.getValue (); _zoom = zoomBar.getValue (); _ecce = ecceBar.getValue (); _wipe = wipeBar.getValue (); _wipeCnt = _wipe; if (_wipe > 995) {wipeLbl.setText ("Wipe: never"); _wipe = -1;} else wipeLbl.setText ("Wipe: every " + wipeBar.getValue ()); } public void checkUpdate () // update values associated with checkboxes. { String bx = drawGrp.getCurrent ().getLabel (); if (bx == "Lines") _brush = 0; if (bx == "Dots") _brush = 1; if (bx == "2x2 squares") _brush = 2; if (bx == "3x3 squares") _brush = 3; String s = symmGrp.getCurrent ().getLabel (); if (s == "1 R") _symm = 1; if (s == "2 R") _symm = 2; if (s == "1 R, 1 M") _symm = 3; if (s == "1 R, 2 M") _symm = 4; if (reverseBox.getState ()) _dir = -1; else _dir = 1; _paused = pauseBox.getState (); } public void start () { if (_thr == null) {_thr = new Thread (this); _thr.start ();} } public void run () { while (_thr != null) { if (! _paused) {repaint (); _frame++;} try {_thr.sleep (10);} catch (InterruptedException e) {} } } public void paint (Graphics gfx) // the GUTS { double realx, realy, ang, angInc; int x, y, ox, oy, i; if (! _paused) { if ((_wipe != -1) && (_wipeCnt-- == 0)) {_clear = true; _wipeCnt = _wipe;} if (_clear) { gfx.setColor (Color.black); gfx.fillRect (0, 0, 1280, 1024); _clear = false; } _seed = _seed + 0.00000004 * _rate * _dir; realx = realy = _ecce; x = y = ox = oy = (int) _ecce; ang = angInc = 0; for (i = 1; i <= _leng; i++) { ang += angInc; angInc += _seed; if (ang > Math.PI*2) ang -= Math.PI*2; if (angInc > Math.PI*2) angInc -= Math.PI*2; if (ang < 0) ang += Math.PI*2; if (angInc < 0) angInc += Math.PI*2; realx += _zoom * (CosQ (ang)); x = (int)realx; realy += _zoom * (SinQ (ang)); y = (int)realy; gfx.setColor (_pal [(i+_frame) % 255]); if ((i+_frame) % _leng < _prop){ if (_symm == 1){ plot (_cX+x, _cY+y, _cX+ox, _cY+oy, _brush, gfx); plot (_cX-x, _cY-y, _cX-ox, _cY-oy, _brush, gfx); } if (_symm == 2){ plot (_cX+x, _cY+y, _cX+ox, _cY+oy, _brush, gfx); plot (_cX-x, _cY-y, _cX-ox, _cY-oy, _brush, gfx); plot (_cX-y, _cY+x, _cX-oy, _cY+ox, _brush, gfx); plot (_cX+y, _cY-x, _cX+oy, _cY-ox, _brush, gfx); } if (_symm == 3){ plot (_cX+x, _cY+y, _cX+ox, _cY+oy, _brush, gfx); plot (_cX-x, _cY-y, _cX-ox, _cY-oy, _brush, gfx); plot (_cX-x, _cY+y, _cX-ox, _cY+oy, _brush, gfx); plot (_cX+x, _cY-y, _cX+ox, _cY-oy, _brush, gfx); } if (_symm == 4){ plot (_cX+x, _cY+y, _cX+ox, _cY+oy, _brush, gfx); plot (_cX-x, _cY-y, _cX-ox, _cY-oy, _brush, gfx); plot (_cX-y, _cY+x, _cX-oy, _cY+ox, _brush, gfx); plot (_cX+y, _cY-x, _cX+oy, _cY-ox, _brush, gfx); plot (_cX+x, _cY-y, _cX+ox, _cY-oy, _brush, gfx); plot (_cX-x, _cY+y, _cX-ox, _cY+oy, _brush, gfx); plot (_cX-y, _cY-x, _cX-oy, _cY-ox, _brush, gfx); plot (_cX+y, _cY+x, _cX+oy, _cY+ox, _brush, gfx); } } ox = x; oy = y; } } } public void plot (int x, int y, int ox, int oy, int br, Graphics g) // draw a line or a square, as appropriate. { if (br == 0) g.drawLine (x, y, ox, oy); else g.fillRect (x, y, br, br); } public void update (Graphics g) {paint (g);} public double SinQ (double angle) { return _sin [((int)(256+angle*128/Math.PI))%256]; } public double CosQ (double angle) { return _sin [((int)(192+angle*128/Math.PI))%256]; } }