mondrian
flow
field
About Flofluxdrian

About Flofluxdrian

written by Kenzo da barra

07 Jul 2023100 EDITIONS
1 TEZ

I'll explain the code a bit below:

The code defines a griglia function that takes parameters assex, assey, largh, altez, prof, and passo. This function is responsible for creating a grid of particles. It uses recursion to create a fractal-like pattern by splitting the grid into smaller sections. The resolution variable determines the size of each grid cell. The function also initializes the nuovoflusso and nuovoflusso2 objects.

function griglia(assex, assey, largh, altez, prof, passo) {
  push();
  this.passo = passo;
  let ste = this.passo;
  if (prof > randomgriglia) {
    if (random() > 0.5) {
      griglia(assex, assey, largh, altez / 2, prof - 1);
      griglia(assex, assey + altez / 2, largh, altez / 2, prof - 1);
    } else {
      griglia(assex, assey, largh / 2, altez, prof - 1);
      griglia(assex + largh / 2, assey, largh / 2, altez, prof - 1);
    }
  } else {
    let radius = min(largh, altez) / 2;//not used atm
    let centerX = assex + largh / 2;//not used atm
    let centerY = assey + altez / 2;//not used atm
    let angleStep = TWO_PI / 7;//not used atm
    let offsetAngle = random(TWO_PI);//not used atm
		let pal=random(palette)
		let pal2=random(palette);
			nuovoflusso = new Flussocanalizzatore(assex, assey, resolution, largh, altez,1);
			nuovoflusso2 = new Flussocanalizzatore(assex, assey, resolution, largh, altez,2);
    for (let i = 0; i < 100; i++) {
      let angle = i * angleStep + offsetAngle;//not used atm
      let particleX = centerX + cos(angle) * radius;//not used atm
      let particleY = centerY + sin(angle) * radius;//not used atm
			particelle.push(new ParticolatoF(assex, assey, largh, altez, pal));
			if(shuffrandom < 0.80){
			particelle2.push(new ParticolatoF(assex, assey, largh, altez, pal));
			}else{
			particelle2.push(new ParticolatoF(assex, assey, largh, altez, pal2))
			}	
    }
  }
  pop();
}

in draw function that is called repeatedly in a loop. Here updates the positions of the particles and draws them on the canvas. It also checks the current movimento value and determines whether to draw the first set of particles (particelle), the second set of particles (particelle2), or stop the animation by calling the noLoop function.

if (movimento < finemovimento){
  for (let particolato of particelle) {
    particolato.follow(nuovoflusso);
    particolato.update();
    particolato.writeln();
  }
}else if(movimento >= finemovimento && movimento < finemovimento2){
  for (let particolato of particelle2) {
    particolato.follow(nuovoflusso2);
    particolato.update();
    particolato.writeln();
  }
}else if(movimento >= finemovimento2){
	noLoop();
}
  if (particelle.length < 500 && frameCount % 10 === 0) {
    generateFractalParticles();
  }

The code defines a generateFractalParticles function that creates additional particles by duplicating and modifying existing particles. The number of iterations is determined randomly between 1 and the maxIterations value.

function generateFractalParticles() {
  let newParticles = [];
  for (let particolato of particelle) {
    let iterations = floor(random(1, maxIterations + 1));
    for (let i = 0; i < iterations; i++) {
      let fractalParticle = new ParticolatoF(particolato.rectX, particolato.rectY, particolato.rectWidth, particolato.rectHeight);
      fractalParticle.position = particolato.position.copy();
      newParticles.push(fractalParticle);
    }
  }
  particelle.push(...newParticles);
}

The code defines a Flussocanalizzatore class that represents a flow channel. It has properties such as rectWidth, rectHeight, resolution, cols, rows, offsetX, offsetY, and field. The field property stores a 2D array of vectors that represent the direction of the flow at each point in the channel. The class has two methods: generaFlusso1 and generaFlusso, which generate the flow field vectors based on different noise patterns.

class Flussocanalizzatore {
  constructor(rectWidth, rectHeight, resolution, offsetX, offsetY,swi) {
		this.swi=swi;
    this.rectWidth = rectWidth;
    this.rectHeight = rectHeight;
    this.resolution = resolution;
    this.cols = rectWidth / resolution;
    this.rows = rectHeight / resolution;
    this.offsetX = offsetX;
    this.offsetY = offsetY;
		if(this.swi === 1){
		this.field = this.generaFlusso1();
		}else{
    this.field = this.generaFlusso();
		}
  }
	//primo passo
	generaFlusso1() {
    let field = [];
		  let yoff = 0;
    for (let y = 0; y < this.rows; y++) {
      let row = [];
			let xoff = 0;
      for (let x = 0; x < this.cols; x++) {
				angolo = map(noise(x * 0.005, y * 0.005, zoff*0.005),0,0.1,0,TAU)
				 xoff += 0.1;
        let vector = p5.Vector.fromAngle(angolo);
        row.push(vector);
      }
      field.push(row);
    }
		    yoff += 0.1;
    zoff += 0.03;
    return field;
  }
	
	//secondo passo
  generaFlusso() {
    let field = [];
		  let yoff = 0;
    for (let y = 0; y < this.rows; y++) {
      let row = [];
			let xoff = 0;
      for (let x = 0; x < this.cols; x++) {
switch(noise_type){
	case 1:
	angolo = map(noise((x + this.offsetX) * 0.05, (y + this.offsetY) * 0.05), 0, 0.1, 0, TWO_PI);
	break;
	case 2:
	angolo = map(noise(x * 0.005, y * 0.005), 0, 0.001, 0, TAU);
	break;
	case 3:
	angolo = noise(xoff, yoff, zoff) * TWO_PI * 2;
	break;
	case 4:
	angolo = map(noise(xoff, yoff, zoff),0,0.1,0,TWO_PI * 2)
	break;
	case 5:
	angolo = map(noise(xoff*0.005, yoff*0.005,zoff*0.005),0,0.1,0,TWO_PI * 2)
	break;
	case 6:
	angolo = map(noise(x * 0.005, y * 0.005, zoff*0.005),0,0.1,0,TAU)
	break;
	default:
}
		 xoff += 0.1;
        let vector = p5.Vector.fromAngle(angolo);
        row.push(vector);
      }
      field.push(row);
    }
		    yoff += 0.1;
    zoff += 0.03;
    return field;
  }

  lookup(position) {
    let col = floor(constrain((position.x - this.offsetX) / this.resolution, 0, this.cols - 1));
    let row = floor(constrain((position.y - this.offsetY) / this.resolution, 0, this.rows - 1));
    return this.field[row][col].copy();
  }
}

The code defines a ParticolatoF class that represents a particle. It has properties such as rectX, rectY, rectWidth, rectHeight, position, velocity, acceleration, maxSpeed, and maxForce. The class has methods for updating the particle's position, applying forces to it, and drawing it on the canvas.

Inside the ParticolatoF class, the follow method calculates the desired velocity for the particle based on the flow channel (nuovoflusso), applies a steering force to match the desired velocity, and applies the resulting force to the particle.

The applyForce method in the ParticolatoF class adds a force to the particle's acceleration vector.

The update method in the ParticolatoF class updates the particle's velocity, position, and acceleration based on the applied forces. It also handles the wrapping of particles around the edges of the grid.

The edges method in the ParticolatoF class checks if the particle has gone beyond the edges of the grid and wraps it to the opposite edge if necessary.

The writeln method in the ParticolatoF class is responsible for drawing the particle on the canvas. The actual drawing is determined based on the type_stroke variable, which can be set to different values to draw lines, points, ellipses, or rectangles.

class ParticolatoF {
  constructor(rectX, rectY, rectWidth, rectHeight,pal) {
		this.pal=pal;
    this.rectX = rectX;
    this.rectY = rectY;
    this.rectWidth = rectWidth;
    this.rectHeight = rectHeight;
    this.position = createVector(random(rectX, rectX + rectWidth), random(rectY, rectY + rectHeight));
    this.velocity = createVector();
    this.acceleration = createVector();
    this.maxSpeed = random(1, 4);
    this.maxForce = 0.1;
  }

  follow(nuovoflusso) {
    let desired = nuovoflusso.lookup(this.position);
    desired.setMag(this.maxSpeed);
    let steering = p5.Vector.sub(desired, this.velocity);
    steering.limit(this.maxForce);
    this.applyForce(steering);
  }

  applyForce(force) {
    this.acceleration.add(force);
  }

  update() {
    this.velocity.add(this.acceleration);
    this.velocity.limit(this.maxSpeed);
    this.position.add(this.velocity);
    this.acceleration.mult(0);
    this.edges();
  }

  edges() {
    if (this.position.x > this.rectX + this.rectWidth) this.position.x = this.rectX;
    if (this.position.x < this.rectX) this.position.x = this.rectX + this.rectWidth;
    if (this.position.y > this.rectY + this.rectHeight) this.position.y = this.rectY;
    if (this.position.y < this.rectY) this.position.y = this.rectY + this.rectHeight;
  }

  writeln() {
		push()
if (densityValue === 1){strokeWeight(0.1)}
if (densityValue === 2){strokeWeight(0.1)}
if (densityValue === 3){strokeWeight(0.25)}
if (densityValue === 4){strokeWeight(0.25)}
if (densityValue === 5){strokeWeight(0.25)}
if (densityValue === 6){strokeWeight(0.25)}
if (densityValue === 7){strokeWeight(0.25)}
		noFill()
		    if (this.pal) {
  let levels = this.pal.levels || [255, 255, 255];
  stroke(levels[0], levels[1], levels[2]);
  } else {
  noStroke();
  }
  let prevPos = this.position.copy().sub(this.velocity.copy().normalize().mult(3));
  switch(type_stroke){
	  case 1:
		line(prevPos.x, prevPos.y, this.position.x, this.position.y);
	break;
	case 2:
		point(this.position.x, this.position.y);
	break;
	case 3:
		ellipse(prevPos.x, prevPos.y, 1, 2);
	break;
	case 4:
		rect(prevPos.x, prevPos.y, 1, 2);
	break;
	default:
  }
		pop()
  }
  
}

project name project name project name

here the sketch on openprocessing

Kisses

Kenzo da barra

stay ahead with our newsletter

receive news on exclusive drops, releases, product updates, and more

feedback