Tree tree;
float frame = 0;
void setup() {
  size(400,600);
  strokeCap(SQUARE);
  tree = new Tree();
}
void draw() {
 frame += 0.05;
 background(90, 130, 250);
 tree.trunk.display(tree.x, height);
 
 fill(255);
 textAlign(CENTER);
 text("Click to create a new tree!", width / 2, 30);
}
void mousePressed() {
    tree = new Tree();
}
class Tree {
  float x = width / 2; //Where the base of the tree is
  int branchesAtBreak = (int) random(2, 5); //How many branches at each break
  int numberOfBreaks = (int) random(4, 6); //How many breaks
  float trunkWidth = random(1, 4) * (float) numberOfBreaks; //Width of trunk
  float branchSpread = random(1, 7); //How much should the branches spread out
  float branchSplay = random(0.2, 1.1); //The lower the number the shorter the outer branches are
  float branchLengthMulti = random(0.2, 0.95); //How much branches shrink length wise each break
  float branchWidthMulti = random(0.4, 0.7); // Same as above except with width
  float breezeStrength = random(1)+3; //How strong is the "breeze" that sways the tree
  float leafSize = random(15, 30); //Size of the leaves
  Branch trunk = new Branch(this, PI, 0, height/random(2,3), trunkWidth, branchesAtBreak, numberOfBreaks); //Creates tree trunk and provides tree info to the rest of the tree
}
class Branch {
  boolean endOfBranch = false;
  float xpos, globalAngle, localAngle, branchLength;
  int branchesAtBreak, numberOfBreaks;
  float trunkWidth;
  Branch[] branches = new Branch[0];
  Tree tree;
  float waveOffset = random(-1, 1);
   
  Branch(Tree tempT, float tempGA, float tempLA, float tempL, float tempTW, int tempBAB, int tempNOB) {
    tree = tempT;branchesAtBreak = tempBAB;numberOfBreaks = tempNOB;trunkWidth = tempTW;globalAngle = tempGA;localAngle = tempLA;branchLength = tempL;
    
    if (numberOfBreaks == 0) {
     endOfBranch = true; //Lets tree know once it has hit the end of the branch
    } else {
     endOfBranch = false;
     branches = new Branch[branchesAtBreak];
     float minimumAngle = -((tree.branchSpread*(branchesAtBreak-1))/2); //Calculates minimum angle of a branch
     for (int i=0; i<branches.length;i++) {
       float thisBranchAngle = ((tree.branchSpread * i) + minimumAngle + random(-0.5,0.5))*(tree.branchSplay/numberOfBreaks); //Randomizes angle of branch
       float thisBranchLenDiv = (float) abs(i - ((tree.branchesAtBreak - 1 ) / 2)) + 1; //Number that length of branches are divided by
         branches[i] = new Branch(tree, globalAngle + thisBranchAngle, thisBranchAngle,                     //
                                    (branchLength*tree.branchLengthMulti)/thisBranchLenDiv,                 // Adds child branch
                                    trunkWidth * tree.branchWidthMulti, branchesAtBreak, numberOfBreaks-1); //
     }
    }
    branchLength *= random(0.2, 1.2); //Randomizes trunk length after finishing all other branches
  }
  
  void display(float startX, float startY){
     float breeze = (sin(frame+waveOffset)/branchLength)*tree.breezeStrength; //Calculates the offset caused by the breeze
     float endX = (float)(startX + (sin((globalAngle+breeze))*branchLength)); //Determines the end position of the branch
     float endY = (float)(startY + (cos((globalAngle+breeze))*branchLength)); // /\
     
     //Draws Branches
     pushMatrix();
     stroke(83, 49, 24);
     strokeWeight(trunkWidth + 1);
     line(startX,startY,endX,endY);
     popMatrix();
    //Draws all child branches
     for (int i = 0; i < branches.length; i++){
       branches[i].display(endX,endY);
     }
     
     
     //Draws leaves once you hit the end of the branch
     if (branches.length == 0) { 
       pushMatrix();
       stroke(143, 221, 44);
       strokeWeight(1);
       fill(143, 221, 44);
       ellipse(endX,endY,tree.leafSize,tree.leafSize);
       popMatrix();
     }
   }
}