Stepping Through a Fractal Tree

Stepping through a fractal tree using a thread.
A PGraphic is updated in a thread one line at a time and displayed in draw().

// Stepping through a Fractal Tree
// A PGraphic is updated in a thread one line at a time and displayed in draw()
//
// Author: GLV
// Date:   2024-09-29
// Code from references below were used and adapted.

// Code references:

// https://www.geeksforgeeks.org/y-fractal-tree-in-python-using-turtle/

// Turtle Graphics in Processing
// Natalie Freed, February 2013
// https://gist.github.com/nataliefreed/8483050

PGraphics pg;

PVector loc;           //current location
float orientation;     //current orientation

boolean penDown = false;   // penDown (to draw) or !penDown (do not draw) 
boolean bgi = true;        // Background (once only)
boolean db = true;        // Debug information with print()

int level;        // level (nesting/branches)  
int count = 0;    // counter (total lines drawn)
int lfCnt = 0;    // leaf counter
int lngth;        // length of branch

void setup()
  {
  size(700, 600); 
  if (bgi)
    {
    background(0);
    bgi = false;
    }
  pg = createGraphics(width, height);
  
  // Initialize
  loc = new PVector(width/2, height-60); //starting position is at center
  orientation = radians(90);             //starting orientation is at 90 degrees
  count = 0;
  lngth = 90;
  level = 8;
  lfCnt = 0;
  
  frameRate(240); // 1/frameRate (period) < delay  4.2ms < 10ms (in line function)
  
  thread("requestData");
  noLoop();
  }

//*****************************************************************  

void draw()
  {
  image(pg, 0, 0);
  }

//*****************************************************************  

void branch(float len, int th, int lev)
  {
  float a = radians(th);
  
  if (lev > 0)
    { 
    lev--;
    forward(len, lev);
    right(a);

    th = th + choice(-5, 5);
      
    float rv = 0.1;  //Random values       
    float swl = 0.8 + random(-rv, rv);
    branch(swl*len, th, lev);
    
    left(2*a);
    
    float swr = 0.8  + random(-rv, rv);
    branch(swr*len, th, lev);
    
    // Do not draw when returning to branch point
    penDown = false;
    right(a);
    forward(-len, lev);
    penDown = true;
    } 
  }
  
//*****************************************************************  
  
void requestData() 
  {
  pg.beginDraw();
  pg.background(0, 5);
  pg.strokeWeight(level);
  pg.stroke(255);
  pg.line(loc.x, loc.y, loc.x, loc.y - lngth);
  pg.endDraw();
  
  penDown = true;
  branch(lngth, 25, level);
  
  delay(1000); // Enjoy the generated fractal!
  frameCount = 0;

  redraw(); // loop() skips a line!
  }  
  
//*****************************************************************  

// Toggle numbering of branches
void keyPressed()
  {
  db = !db;
  }

//*****************************************************************  

// Modified:
void forward(float pixels, int lev) //calculate positions when moving forward
  {
  PVector start = loc;
  PVector end = PVector.add(loc, polar(pixels, orientation));
  loc = end;
  
  if(penDown)
    {
    line(start, end, lev);
    redraw();
    }
  }

//*****************************************************************  

// Original (unchanged):
void left(float theta) //calculate new orientation
  {
  orientation += theta;
  }

//*****************************************************************  

// Original (unchanged):
void right(float theta)  //calculate new orientation
  {
  orientation -= theta;
  }

//*****************************************************************  

// Modified:
// Only draw a line if penDown
void line(PVector a, PVector b, int lev) //new line function with PVectors. used by forward function
  {
  // Update PGraphic frame
  pg.beginDraw();
  
  if (lev == 0)
    {
    pg.stroke(0, 255, 0);
    pg.strokeWeight(2);
    lfCnt++;                      // leaf counter
    
    // Shows leaf count at end of leaf
    if (db)
      {
      pg.textAlign(CENTER, CENTER);
      PVector txtLoc = loc.copy();
      PVector ln = b.copy().sub(a.copy());
      txtLoc.add(ln.normalize().mult(10));
      pg.push();
      pg.stroke(1);
      pg.fill(255);
      pg.text(lfCnt, txtLoc.x, txtLoc.y);
      pg.pop();
      }
    }
  else
    {
    pg.stroke(255-255/(lev+1));
    pg.strokeWeight(lev+2);
    }
    
  count++;
  pg.line(a.x, a.y, b.x, b.y); 
  
  delay(10); // greater than 1/frameRate
  pg.endDraw();
  }

//*****************************************************************  

// Original (unchanged):
PVector polar(float r, float theta)                //converts an angle and radius into a vector
  {
  return new PVector(r*cos(theta), r*sin(-theta)); // negate y for left handed coordinate system
  }

That was fun!

:)

2 Likes

Nice work. Hope to see Turtle Graphics further developed in Processing.