Diffusion Limited Aggregation

Hello, I am trying to visualize Diffusion Limited Aggregation and some of my circles are overlapping. I tried repositioning them but they still overlap. How can I fix this? My code is below:

import java.lang.Math;

ArrayList<Particle> brownianTree = new ArrayList<Particle>();
ArrayList<Particle> particles = new ArrayList<Particle>();
int initialParticles = 100; // initial number of mobile partilces on screen
int max = 600; // maximum number of particles on screen
float d = 15; // diameter of circles
float hue = 0; // hue value
int hueStart; // starting value of hue
int noOfTimes = 100; // speed indicator of particles
int maxNoOfTimes = 400; // maximum speed of particles
int minNoOfTimes = 10; //minimum speed of particles
float ratio = 1.001; // rate of decrease of circles' radii

void setup() {
  size(1280,720);
    
  brownianTree.add(new Particle(new PVector(width/2, height/2), true));
  d /= ratio;
  generatePosition();
}

void draw() {
  background(0);
 
  
  for (int i = 0; i < brownianTree.size(); i ++) {
    // change hue of particles as they are added to the list
    hue = map(i, 0, max, hueStart, 255);
    
    brownianTree.get(i).display();
  }
  for (int i = 0; i < particles.size(); i ++) {
    particles.get(i).displayParticles();
  }
  
  for (int k = 0; k < noOfTimes; k ++) { // make particle move faster
   for (int i = 0; i < particles.size(); i ++) {
      particles.get(i).move();
      if (particles.get(i).isAttached()) {
        Particle currentParticle = particles.get(i);
        brownianTree.add(currentParticle);//add particle to tree
        particles.remove(currentParticle); // remove particle from the 'particles' list
      }
    }
  }
}

// particles generated around the edge
void generatePosition() {
  
  for (int i = 0; i < initialParticles/4; i ++) {
    particles.add(new Particle(new PVector(random(width), 0), false));
    d /= ratio;
  } 
  for (int i = 0; i < initialParticles/4; i ++) {
    particles.add(new Particle(new PVector(random(width), height), false));
    d /= ratio;
  } 
  for (int i = 0; i < initialParticles/4; i ++) {
    particles.add(new Particle(new PVector(0, random(height)), false));
    d /= ratio;
  } 
  for (int i = 0; i < initialParticles/4; i ++) {
    particles.add(new Particle(new PVector(width, random(height)), false));
    d /= ratio;
  } 
} 

/*
USER INTERACTIONS
*/

void keyPressed() {
  if (key == '2') {
    
    //initialise arrays
    brownianTree = new ArrayList<Particle>();
    particles = new ArrayList<Particle>();
    
    max = 1000;
    ratio = 1.0005;
    initialParticles = 100;
    d = 15; // 'reset' value of diameter
    
    // add tree to bottom
    for (int x = 0; x < width; x += d) {
      brownianTree.add(new Particle(new PVector(x, height), true));
      d /= ratio;
    }
    
    // add particles to top
    for (int i = 0; i < initialParticles; i ++) {
      particles.add(new Particle(new PVector(random(width),0), false));
      d /= ratio;
    }
  }
    
  if (key == '3') {
      
    brownianTree = new ArrayList<Particle>();
    particles = new ArrayList<Particle>();
      
    max = 1450;
    ratio = 1.0001;
    initialParticles = 50;
    d = 15;
      
    // add circles on the boundaries of the sketch
    for (int x = 0; x < width; x += d) {
      brownianTree.add(new Particle(new PVector(x, height), true));
      d /= ratio;
    }
    for (int x = 0; x < width; x += d) {
      brownianTree.add(new Particle(new PVector(x, 0), true));
      d /= ratio;
    }
    for (int y = 0; y < width; y += d) {
      brownianTree.add(new Particle(new PVector(0, y), true));
      d /= ratio;
    }
    for (int y = 0; y < width; y += d) {
      brownianTree.add(new Particle(new PVector(width, y), true));
      d /= ratio;
    }
    for (int i = 0; i < initialParticles; i ++) {
      particles.add(new Particle(new PVector(width/2, height/2,0), false));
      d /= ratio;
    } 
  }
  
  if (key == '1') {
    
    brownianTree = new ArrayList<Particle>();
    particles = new ArrayList<Particle>();
    
    max = 600;
    ratio = 1.001;
    initialParticles = 100;
    d = 15;
    
    brownianTree.add(new Particle(new PVector(width/2, height/2), true)); 
    d /= ratio;
    generatePosition();
  }
    
  if (key == 'r') {
    hueStart = 2; // start with red circles
  }
  if (key == 'b') {
    hueStart = 170; // start with blue circles
  }
  if (key == 'g') {
    hueStart = 85; // start with green circles
  }
  
  if (key == CODED) {
    if (keyCode == UP) {
      if (noOfTimes < maxNoOfTimes) {
        noOfTimes += 10; // increase speed of particles
      }
    }
  }
  
  if (key == CODED) {
    if (keyCode == DOWN) {
      if (noOfTimes > minNoOfTimes) {
        noOfTimes -= 10; // decrease speed of particles
      }
    }
  }
}

void mousePressed() {
  int number = 5;
  
  if (mousePressed && (mouseButton == LEFT)) {
     // number of particles on screen does not exceed max
     if (particles.size() + brownianTree.size() + number <= max) {
       for (int i = 0; i < number; i ++) { //add 5 particles
         particles.add(new Particle(new PVector(mouseX, mouseY), false));
         d /= ratio;
       }
     }
  }
  if (mousePressed && (mouseButton == RIGHT)) {
    if (particles.size() > number*2) { // moving particles on screen must be at least 10
      for (int i = 0; i < number; i ++) { // remove 5 particles
        Particle current = particles.get(i);
        particles.remove(current);
        d *= ratio;
      }
    }
  }
}

class Particle {
  PVector location; 
  boolean attached;
  float size;

  // constructor
  public Particle(PVector position, boolean stuck) {
    location = position;
    attached = stuck;
    size = d;
  }
  
  // move particles around the screen
  void move() {
    PVector velocity = PVector.random2D();
    location.add(velocity);
    
    // prevent particles from leaving the sketch
    location.x = constrain(location.x, 0, width);
    location.y = constrain(location.y, 0, height);
  }
  
  // Checks whether particles are attached or not
  boolean isAttached() {
    for (int i = 0; i < brownianTree.size(); i ++) {
      Particle current = brownianTree.get(i);
      float distance = calcDistance(location.x, location.y, current.location.x, current.location.y);
      if (distance < (d/2+current.size/2)*(d/2+current.size/2)) {
        
           // reposition circles so they do not overlap
           float angle = atan2(location.y-current.location.y, location.x-current.location.x);
           println("dy is: " + (location.y - current.location.y));
           println("dx is: " + (location.x - current.location.x));
           float distanceBetween = sqrt(calcDistance(location.x,location.y,current.location.x,current.location.y));
           float distToMove = d/2+current.size/2 - distanceBetween;
           if (distToMove > 0) { // if distToMove is equal to 0, do not reposition circles
             PVector displacement = new PVector(cos(angle)*distToMove, cos(angle)*distToMove);
             location.add(displacement); 
             println("Distance to move is: " + distToMove);
             println("Distance between is: " + distanceBetween);
             println("Angle is: " + angle);
             println("Displacement is: " + cos(angle)*distToMove);
           }        
           attached = true;
           return true;
      }
    }
    return false;
  }
  
  // display tree-particles
  void display() {
    strokeWeight(0.5);
    colorMode(HSB);
    fill(hue, 255, 255);
    ellipse(location.x, location.y, size, size);
  }
  
  /* Calculates the square of the distance betweeen 2 particles.
     Also avoids using square root so program runs faster */
  float calcDistance(float a1, float b1, float a2, float b2) {
    float adj = a2 - a1;
    float opp = b2 - b1;
    float distSquared = adj*adj+opp*opp; // calculates hypotenuse*hypotenuse
    return distSquared; 
  } 
  
  // display moving particles
  void displayParticles() {
    strokeWeight(0.5);
    colorMode(RGB);
    fill(255, 255, 255);
    ellipse(location.x, location.y, size, size);
  } 
}
  
    
1 Like

Do you mean your red circles in the center?

When you say “overlapping” are you talking about this?

Screen Shot 2020-05-20 at 10.15.06 AM

Yeah. My problems is that they intersect.
I tried to fix it with trigonometry and still have the same problem

I’m not sure if it is affecting your tests, but this should be:

float distToMove = size/2+current.size/2 - distanceBetween;

…and I wonder whether you are seeing an artifact of small alignment changes in stroke antialiasing. When I changed to noStroke(), I got this:

You may still be getting variations of half a pixel or so, but given the way you are calculating displacement that kind of precision isn’t unexpected.

An alternate way is to simply scale the vector. This way precision isn’t an issue (or anyway, you are getting the best precision you can).

Untested pseudocode:

PVector offset = PVector(x-current.x, y-current.y).setMag(size/2 + current.size/2);

(edit–make clear the variable is the offset, not the location)

I tried changing the d/2 to size/2 but in this case the problem is the Stroke. My method isn’t 100% precise so some minor overlapping should be expected I guess.
Thanks!

1 Like

Definitely also try setMag and see if it gives you better results.

https://processing.org/reference/PVector_setMag_.html

Here, this should help:

  // Checks whether particles are attached or not
  boolean isAttached() {
    for (int i = 0; i < brownianTree.size(); i ++) {
      Particle current = brownianTree.get(i);
      float distance = calcDistance(location.x, location.y, current.location.x, current.location.y);
      if (distance < (d/2+current.size/2)*(d/2+current.size/2)) {
        // reposition circles so they do not overlap
        PVector offset = PVector.sub(location, current.location);
        offset.setMag(size/2 + current.size/2);
        location = PVector.add(current.location, offset);
        attached = true;
        return true;
      }
    }
    return false;
  }

Gives:

Yeah. The circles stopped overlapping.
Thanks a lot!