Fractal-like triangles process

The Processing sketch generates fractal shapes using a triangle and recursion. The main function is `iterate` which generates a new triangle based on a given triangle and calls itself for two new triangles (left and right). This is repeated until a certain condition is met (the length of the triangle’s side is smaller than 20). The final result is stored in an ArrayList of Triangles.

Start variables: l – length of the first triangle; startPoint – starting point for the first triangle

				
					
// ––––––––––––––––––––––––––––––––––––––––––––––––––––––––
// A Processing sketch that generates fractal-like triangles.
//
//        C
//       / \
//      /   \
//     /     \
//    /       \
// B /---------\A
//
// Andreas Pirchner, 2020
// ––––––––––––––––––––––––––––––––––––––––––––––––––––––––

import processing.svg.*;  // import the library for SVG output
import processing.pdf.*;  // import the library for PDF output

ArrayList<Triangle> triangles;  // Create an ArrayList to store the triangles
float l = 150;  // length of the first triangle
int g = 1;  // variable to hold the generations of iterations
int id = Math.round(random(10000));  // random ID for the PDF export
PVector startPoint;  // Create a PVector to store the starting point

// The setup function is called once when the sketch starts
void setup(){
  size(1000,1000);  // set the size of the window
  background(255);  // set the background color to white
  noFill();  // do not fill the shapes
  triangles = new ArrayList<Triangle>();  // initialize the triangles ArrayList
  //beginRecord(PDF, "export"+id+"layer-1.pdf");  // start recording the export PDF
  startPoint = new PVector(width/2, height/2);  // set the starting point to the center of the window
  translate(startPoint.x, startPoint.y);  // Translate to the starting point
  line(-l,0,l,0);  // draw the first hypotenuse
  iterate(l,g, 0, startPoint, 0);  // call the iterate function to start generating triangles
  rotate(PI);  // rotate the entire sketch by 180 degrees
  iterate(l,g, 0, startPoint, 0);  // call the iterate function again to generate more triangles
  println(triangles.size());  // print the number of triangles generated
}

void draw(){ 
}

void iterate(float _l, int _g, float _angle, PVector _absolutePos, float _absoluteAngle){
  
  float angle = -random(PI/16, PI*15/16); 
  //float angle = _angle;
  
  //float angle = -PI/4; //random(PI);                     // The Angle for this iteration
                                                         // (how are the side lengths of this right angled triangle) 
  PVector _m = new PVector(0, 0);                        // Make Vektors for this iteration
  PVector _a = new PVector(_l, 0);
  PVector _b = new PVector(-_l, 0);
  PVector _c = new PVector(_m.x + _l*cos(angle), _m.y + _l * sin(angle));
      
      paint(_m, _a, _b, _c, _l);
      
  
  
  PVector mAC = PVector.lerp(_a, _c, 0.5);                      // middle of side AC
  PVector mBC = PVector.lerp(_b, _c, 0.5);                      // middle of side BC
  
  float one = PI- PI/2 - angle/2;                               // calculate rotations of children triangles
  float three = PI/2 - one; 
  float nextRotationLeft = three;                               // left child trianlge (hypotenuse: BC)
  float nextRotationRight = (PI - PI/2 - (-angle/2));           // right child trianlge (hypotenuse: AC)
  
  
  float _lBC = PVector.dist(_b, _c)/2;                          // length for left child
  float _lAC = PVector.dist(_a, _c)/2;                          // length for right child
  
  PVector nextAbsolutePos1 = PVector.add(_absolutePos, mBC); //mBC.rotate(nextRotationLeft));    // absolute pos of m of left child
  PVector nextAbsolutePos2 = PVector.add(_absolutePos, mAC); //mAC.rotate(nextRotationRight));    // absolute pos of m of right child
  float nextAbsoluteAngle1 = _absoluteAngle + nextRotationLeft; // absolute angle of left child
  float nextAbsoluteAngle2 = _absoluteAngle + nextRotationRight;// absolute angle of right child
  
  _g ++;
  
  if (_lBC > 20){
     //float nextAngle1 = -random(PI/16, PI*15/16);
     float nextAngle1 = -PI/2;
     triangles.add(new Triangle(nextAbsolutePos1, _lBC, nextRotationLeft, nextAngle1, _g, nextAbsoluteAngle1));
     push();
       translate(mBC.x, mBC.y);
       rotate(nextRotationLeft);
       iterate(_lBC, _g, nextAngle1, nextAbsolutePos1, nextAbsoluteAngle1);
     pop();
  }
  if (_lAC > 20){
     //float nextAngle2 = -random(PI/16, PI*15/16);
     float nextAngle2 = -PI/2;
     triangles.add(new Triangle(nextAbsolutePos2, _lAC, nextRotationRight, nextAngle2, _g, nextAbsoluteAngle2));
     push();
       translate(mAC.x, mAC.y);
       rotate(nextRotationRight);
       iterate(_lAC, _g, nextAngle2, nextAbsolutePos2, nextAbsoluteAngle2);
     pop();
  }
}

// Function to draw the triangle with given parameters
void paint(PVector _m, PVector _a, PVector _b, PVector _c, float _l) {
    // Set the color and opacity for drawing the circles
    stroke(100, 0, 0, 80); // Red color with 80% opacity

    // Don't fill the circles with any color
    noFill();

    // Set the stroke weight (thickness) of the circles
    strokeWeight(1);

    // Draw the first circle with the given parameters
    // The circle will have the same x and y position as _m and the diameter will be 2 times _l
    ellipse(_m.x, _m.y, _l * 2, _l * 2);

    // Set the color and opacity for the remaining circles
    stroke(0); // Black color

    // Draw the second circle with the same x and y position as _m
    // The diameter of the circle is 5 pixels
    ellipse(_m.x, _m.y, 5, 5);

    // Draw the third circle with the given parameters
    // The x and y position of the circle is _c and the diameter is 5 pixels
    ellipse(_c.x, _c.y, 5, 5);

    // Don't fill the triangle
    noFill();

    // Draw the first line from _a to _c
    line(_a.x, _a.y, _c.x, _c.y);

    // Draw the second line from _b to _c
    line(_b.x, _b.y, _c.x, _c.y);
}
void keyPressed() {
  // Check if the 'n' key is pressed
  if (key == 'n') {
    // Clear the background color to white
    background(255);

    // Clear the array of triangles
    triangles.clear();

    // Set the length of the first triangle
    l = 150;

    // Generate a random ID for the PDF export
    int id = Math.round(random(10000));

    // Start recording the first layer of the PDF export
    beginRecord(SVG, "export" + id + "layer-1.svg");

    // Translate the origin point to the center of the screen
    translate(width / 2, height / 2);

    // Rotate the screen by a random angle
    rotate(random(TWO_PI));

    // Draw the first hypotenuse with the given length
    line(-l, 0, l, 0);

    // Call the iterate function
    iterate(l, g, 0, startPoint, 0);

    // Rotate the screen by 180 degrees
    rotate(PI);

    // Call the iterate function again
    iterate(l, g, 0, startPoint, 0);

    // End recording the first layer of the PDF export
    endRecord();
  }

  // Check if the 'p' key is pressed
  if (key == 'p') {
    // Clear the background color to white
    background(255);

    // Loop through the array of triangles
    for (int i = 0; i < triangles.size(); i++) {
      // Get the current triangle from the array
      Triangle t = triangles.get(i);

      // Call the paint function for the current triangle
      t.paint();
    }
    //endRecord();
  }
}
				
			
				
					// ––––––––––––––––––––––––––––––––––––––––––––––––––––––––
// Andreas Pirchner, 2021
// ––––––––––––––––––––––––––––––––––––––––––––––––––––––––


class Triangle{
  // Three points defining the triangle
  PVector absolutePos, a, b, c;
  
  // Length of the base side of the triangle
  float l;
  
  // Rotation of the triangle
  float r;
  
  // Angle of the triangle
  float angle;
  
  // Absolute angle of the triangle
  float absoluteAngle;
  
  // Generation of the triangle
  int generation;
  
  // Constructor of the Triangle class
  Triangle (PVector _absolutePos, float _l, float _rotation, float _thisAlpha, int _generation, float _absoluteAngle) {
    // Initialize the absolute position of the triangle
    absolutePos = _absolutePos;
    
    // Initialize the length of the base side of the triangle
    l = _l;
    
    // Initialize the rotation of the triangle
    r = _rotation;
    
    // Initialize the angle of the triangle
    angle = _thisAlpha;
    
    // Initialize the generation of the triangle
    generation = _generation;
    
    // Initialize the absolute angle of the triangle
    absoluteAngle = _absoluteAngle;
    
    // Calculate the three points defining the triangle
    a = new PVector(l, 0);
    b = new PVector(-l, 0);
    c = new PVector( + l*cos(angle),  + l * sin(angle));
  
  }
  
  // Function to paint the triangle on the screen
  void paint(){
     // store the current matrix position  
     push();
       // Translate the triangle to its absolute position
       translate(absolutePos.x, absolutePos.y);
       
       // Rotate the triangle based on its absolute angle
       rotate(absoluteAngle);
       
       // Debugging information
       println(absoluteAngle);
       
       // Fill the center of the triangle with red color
       fill(255,0,0);
       ellipse(0, 0, 5,5);
       
       // Fill point C of the triangle with green color
       fill(0,255,0);
       ellipse(c.x,c.y,5,5);
       
       // Show the generation number of the triangle
       text(generation,0,0);
       
       // Draw the three sides of the triangle
       line(a.x, a.y, c.x, c.y);
       line(b.x, b.y, c.x, c.y);
     // Restore the previous matrix position 
     pop();
  }
}