// ––––––––––––––––––––––––––––––––––––––––––––––––––––––––
// 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
}
}