Recently I've been looking at Povray, pyprocessing, and cfdg (version 3.0) as tools for creating digital images. I have branched two separate blogs where I mainly explore jruby + processing and processing.py

Friday, 22 January 2010

A Simple Fern Fractal with LSystems

I was too busy watching a BBC four program about Brian Eno to write this up properly initially, a very interesting program I would recommend to all aspiring digital artists....
I found this fern sketch at Gareth Spor artists website. Here I have modified it somewhat to include color, and to use my LSystems library for processing (I'm now thinking about expanding that library to have a Pen class [extending Turtle] and PenStack [extending TurtleStack] this could make the code clearer, and make it much easier to code such sketches).
   1 // FernTest.pde
   2 import java.util.Deque;
   3 import java.util.ArrayDeque;
   4 import java.text.CharacterIterator;
   5 import java.text.StringCharacterIterator;
   6 import lsystem.*;
   7 
   8 
   9 float DELTA = PI/180 * 34.9;  // boring = 36 too symmetrical
  10 color initColor = color(0, 255, 0); // light green stem?
  11 Fern fern;
  12 Grammar grammar;
  13 String production;
  14 PenStack ps;
  15 
  16 void setup() {
  17   size(800, 800);
  18   background(0);
  19   noLoop();
  20   ps = new PenStack();
  21   grammar = new SimpleGrammar(this, "FD");
  22   grammar.addRule('D', "C+@FD");
  23   grammar.addRule('C', "B");
  24   grammar.addRule('B', "[7+#FD][7-#FD]"); // abbreviated grammar
  25   production = grammar.createGrammar(18);
  26   fern = new Fern(production, DELTA);
  27 }
  28 
  29 void draw() {
  30   background(0);
  31   fern.draw(100.0, 600.0, 0.0, 100.0, initColor);
  32 }
  33 
  34 class Fern {
  35 
  36   String production;
  37   float delta;
  38 
  39   Fern(String prod, float angle) {
  40     production = prod;
  41     delta = angle;
  42   }
  43 
  44   void draw(float x, float y, float theta, float len, color col) {
  45     Pen drawPen;
  46     int repeats = 1;
  47     drawPen = new Pen(x, y, theta, len, delta, col);
  48     CharacterIterator it = new StringCharacterIterator(production);
  49     for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
  50       switch (ch) {
  51       case 'F':  // move forward
  52         drawPen.drawForward();
  53         break;
  54       case '+':  //turn right
  55         drawPen.turnRight(repeats);
  56         repeats = 1;
  57         break;
  58       case '-':  //turn left
  59         drawPen.turnLeft(repeats);
  60         repeats = 1;
  61         break;
  62       case '#':  //resize line length
  63         drawPen.resizeLength(0.33);
  64         drawPen.changeColor(-20);
  65         break;
  66       case '@':  //resize line length
  67         drawPen.resizeLength(0.9);
  68         drawPen.changeColor(-10);
  69         break;
  70       case '[':  //push state
  71         ps.push(drawPen.clone());
  72         break;
  73       case ']':  //pop state
  74         drawPen = ps.pop();
  75         break;
  76       case '7':
  77         repeats += 7;
  78         break;
  79       case 'B': // do nothing except confirm character in grammar
  80         break;
  81       case 'C': // do nothing except confirm character in grammar
  82         break;
  83       case 'D': // do nothing except confirm character in grammar
  84         break;
  85       default:
  86         System.err.println("character " + ch + " not in grammar");
  87         break;
  88       }
  89     }
  90   }
  91 }
  92 
  93 /**
  94 * All attributes have primitive types therefore cloning is OK!
  95 */
  96 class Pen implements Cloneable{
  97 
  98   float x, y, theta, len, delta;
  99   int col;
 100 
 101   Pen(float xx, float yy, float direction, float len, float angle, int col) {
 102     x = xx;
 103     y = yy;
 104     theta = direction;
 105     this.len = len;
 106     delta = angle;
 107     this.col = col;
 108   }
 109 
 110   void drawForward() {
 111     float x0 = x;
 112     float y0 = y;
 113     x += len * cos(theta);
 114     y += len * sin(theta);
 115     strokeWeight(2);
 116     stroke(col);
 117     line(x0, y0, x, y);
 118   }
 119 
 120   void turnLeft(int repeats) {
 121     theta += TWO_PI % (repeats * delta);
 122   }
 123 
 124   void turnRight(int repeats) {
 125     theta -= TWO_PI % (repeats * delta);
 126   }
 127 
 128   void resizeLength(float factor) {
 129     len *= factor;
 130   }
 131 
 132   /**
 133    * A somewhat arbitary implementation of a changeColor function
 134    * here only the green channel is altered (HSB mode could be used
 135    * instead of RGB for easier color manipulation).
 136    */
 137 
 138   void changeColor(float increment){
 139     float gree = green(col);
 140     col = color(0, gree + increment, 0);
 141   }
 142   
 143   /**
 144    * Shallow copy Pen to save state, only storing primitive types so OK
 145    * @return a new copy of Pen instance having same state as original
 146    */
 147   Object clone(){
 148     try{ 
 149       return super.clone();
 150     }
 151     catch(CloneNotSupportedException ex){
 152       ex.printStackTrace();
 153     }
 154     return null;
 155   }
 156 }
 157 
 158 class PenStack {
 159   Deque penStack;
 160   
 161   PenStack(){
 162     penStack = new ArrayDeque();
 163   }
 164   
 165   void push(Object pen){
 166     penStack.push(pen);
 167   }
 168   
 169   Pen pop(){
 170     return (Pen)penStack.pop();
 171   }
 172 }

My the use of color change is somewhat restrained, try increasing the values for more effect, or adjust the red and/or blue channels for a much more funky version.

1 comment:

Followers

Blog Archive

About Me

My photo
Pembrokeshire, United Kingdom
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2