// ––––––––––––––––––––––––––––––––––––––––––––––––––––––––
// Program to produce the generative illustration 
// for project report "Karrierewege in der Wissenschaft 
// und Research Assessment: Nationale Empfehlungen in 
// Österreich im Kontext des Europäischen Forschungsraums
// Andreas Pirchner, 2024
// ––––––––––––––––––––––––––––––––––––––––––––––––––––––––


// Importing the PDF library for saving the output as a PDF file
import processing.pdf.*;

// Global variables
float scale = 20;                // Scale factor for the flow field
int rows, cols;                  // Number of rows and columns in the flow field grid
float[][] flowfield;             // 2D array to store flow directions
particle[] particles;            // Array to store particle objects
int particlesAmount = 400;       // Number of particles

// Setup function: runs once at the beginning
void setup(){
   size(1000,1000);              // Set the canvas size
   beginRecord(PDF, "everything.pdf"); // Begin recording to PDF
   colorMode(HSB,360,100,100,100); // Set color mode to HSB
   particles = new particle[particlesAmount]; // Initialize the array of particles
   for(int i = 0; i< particlesAmount; i++){
     particles[i] = new particle(); // Create particle objects
   }
   rows = int(height/scale);     // Calculate the number of rows in the flow field grid
   cols = int(width/scale);      // Calculate the number of columns in the flow field grid
   flowfield = new float[rows][cols]; // Initialize the flow field grid
   
   generateFlowField();          // Generate the flow field
   drawFlowField();              // Draw the flow field arrows
   
   stroke(360,0,0,20);           // Set stroke color and transparency for particle trails
   strokeWeight(0.7);            // Set stroke weight for particle trails
}

// Draw function: runs continuously
void draw(){
  illustrateFlowField();        // Draw particle trails following the flow field
}

// Function to illustrate particle movement along the flow field
void illustrateFlowField(){
  
  float x = random(width);      // Initialize a random x-coordinate
  float y = random(height);     // Initialize a random y-coordinate

  float newx, newy;             // Variables to store the updated coordinates
  int c = 0;                    // Counter to limit the number of iterations
  while (c < 1000 && x > 0 && x < width && y > 0 && y < height){
    float angle = flowfield[int(y/scale)][int(x/scale)]; // Get the flow direction angle at the current position
    newx = x + scale * cos(angle);   // Update x-coordinate based on the flow direction
    newy = y + scale * sin(angle);   // Update y-coordinate based on the flow direction
    line(x, y, newx, newy);          // Draw a line representing the particle trail
    x = newx;                        // Update x-coordinate for the next iteration
    y = newy;                        // Update y-coordinate for the next iteration
    c++;                             // Increment the counter
  }
  
}

// Function to draw arrows representing the flow field
void drawFlowField(){
  background(360,0,100);         // Set background color
  strokeWeight(0.5);             // Set stroke weight for flow field arrows
  stroke(360,100,100,100);       // Set stroke color and transparency for flow field arrows
  float arrowsize = 0.7;         // Size factor for the arrow
  float tip;                     // Variable to store the tip size of the arrow
  for (int r = 0; r < rows; r++){
    for (int c = 0; c < cols; c++){
      pushMatrix();                  // Save the current transformation state
         translate(c*scale, r*scale); // Translate to the position of the arrow
         rotate(flowfield[r][c]);    // Rotate based on the flow direction
         tip = scale * arrowsize;    // Calculate the size of the arrow tip
         line(0,0,tip,0);            // Draw the main line of the arrow
         line(tip,0,tip-2,2);        // Draw the first side of the arrow tip
         line(tip,0,tip-2,-2);       // Draw the second side of the arrow tip
      popMatrix();                   // Restore the previous transformation state
    }
  }
  
}

// Function to generate the flow field using Perlin noise
void generateFlowField(){
  float noiseScale = 0.1;         // Scale factor for Perlin noise
  for (int r = 0; r < rows; r++){
    for (int c = 0; c < cols; c++){
      flowfield[r][c] = map(noise(r * noiseScale, c * noiseScale), 0, 1, 0, 2 * PI); // Map Perlin noise values to flow directions
    }
  }
}

// Function to save the sketch as a PDF when 'q' key is pressed
void keyPressed() {
  if (key == 'q') {
    endRecord();                  // End recording to PDF
    exit();                       // Exit the sketch
  }
}