How to connect (draw lines) of all elements (ellipses) of an ArrayList?

Hi,

In my project, I’m trying to draw a multitude of ellipses that will spawn randomly on the y-axis, all of them scrolling toward the left on the x-axis.

Then after, my goal is to connected all those ellipses with a line. Unfortunately, I am not able to do so for all of the ellipses, because I get some errors (IndexOutOfBoundsException) while trying to use the method .get(particles.size()); as the last element of the array, even though the index and the size are the same…
(see image attached)!


(edit: change the screenshot to add the sketch to show what happen if I put the the element 0 and 1 in the previous and actual particles. Eventually, I want all the dots to be connected!)

I am only able to draw the line if I specify the exact element of the array, but that is not a viable solution has I would have to draw a line of code for every possible connection (0 with 1, 1 with 2, 2 with 3, etc…)

Like if I replace the line in yellow by:
Particle previous = particles.get(0);
Particles actual = particles.get(1);

I would need your help to understand what is wrong with my way to solve this problem, and if is there a better way to do so. If you need more information please don’t hesitate to ask!

Thank you very much,

Philippe

1 Like

If your ArrayList has 3 particles in it, then the .size() of that ArrayList is 3.

You can get the first one with .get(0).
You can get the second one with .get(1).
You can get the third one with .get(2).

But if you try to call .get(3), you’d be trying to get the fourth one. BUT THERE ISN’T A FOURTH ONE.

This is the mistake you are making. You are trying to .get(.size())!

2 Likes

Thanks for your response. I understand the issue here.
I tried to mitigate this by adding variables that would increment on every update.
int p1 = 0;
int p2 = 1;
Put those variable into the .get(int) method for the ArrayList. However, I see no lines, even though when I print the value of p1 and p2, they seems to increment as intended.

Here is the code below if you would like to help.

int total = 5;
float rminY = 300;
float rmaxY = 300;
int p1 = 0;
int p2 = 1;

ArrayList<Particle> particles;

void setup() {
  size(1200,800);  
  particles = new ArrayList<Particle>();  

}

void draw() {
  background(255);
  
  float gapX = random(250,400) ;
  //a rectangle that show the area where the ellipses should spawn (y-axis)
  fill(200,50);
  rect(-1,rminY,width+1,rmaxY);
  
  
  if (particles.isEmpty()) {
    particles.add(new Particle(new PVector(width + 50, random(rminY,rminY+rmaxY))));
  } else {
    
    particles.add(new Particle(new PVector(particles.get(particles.size()-1).location.x + gapX, random(rminY,rminY+rmaxY))));
    
    Particle previous = particles.get(p1);
    Particle actual = particles.get(p2);
    line(actual.location.x,actual.location.y,previous.location.x,previous.location.y);  
    
    p1 +=1;
    p2 +=1;

    println(p1);

  }
  

    
  for (int i=0; i < particles.size(); i++) {

    Particle p = particles.get(i);
    p.display();
    p.update();        
  }     
  
}

void keyPressed() {
  if (key == ' ') {
    for (Particle p : particles) {
      p.velocity.x = -20;
    }
  }
}

void keyReleased() {
    if (key == ' ') {
    for (Particle p : particles) {
      p.velocity.x = -3;
    }
  }
}


class Particle {
  
 PVector location;
 PVector velocity;
 
 float minY = 300;
 float maxY = 300;
 
 Particle(PVector l) {
     velocity = new PVector(-3,0);
     location = l.get();
     
 }
  
  void update() {
      location.x += velocity.x;
  }
  
  void display() {
      fill(0,0,120);
      ellipse(location.x,location.y,30,30);
  }
  
}

Thank you

Hi philpq12,

You need to refactor your code.

You have 2 main issues:

  • The first one is that you are creating a new particle at every frame with the following piece of code. So even if at some point, you have only 2 particules drawn on the screen, you have maybe 100 of them in memory (that you can’t see because they are too far right). Try adding this line at the end of your draw loop to see what I mean : println(particles.size());

  • The second one is concerning your lines: your are drawing them only when you add a new particle. So the line does appear but only during 1 frame. And combine that with the previous point and they are actually all drawn outside of the screen , far right. Instead you want to draw them inside your for loop, just before you display your particles.

1 Like

Thanks for your help everybody. It help me realize how wrong I was seeing how the code was doing it’s stuff.
I managed to accomplished what I wanted, but maybe the code is not that elegant.
I think I will take some time to read about polymorphism and see if I can manage better the way I update and display my particles and lines.

int total = 5;
float rminY = 300;
float rmaxY = 300;
int p1 = 0;
int p2 = 1;
int time = 60;

ArrayList<Particle> particles;
ArrayList<Line> lines;

void setup() {
  size(1200,800);  
  particles = new ArrayList<Particle>();  
  lines = new ArrayList<Line>();

}

void draw() {
  background(255);
  
  float gapX = random(250,400) ;
  //a rectangle that show the area where the ellipses should spawn (y-axis).
  fill(200,50);
  rect(-1,rminY,width+1,rmaxY);
  
 // if statement for the first particle.
  if (particles.isEmpty()) {
    particles.add(new Particle(new PVector(width + 200, random(rminY,rminY+rmaxY))));
  } 
  
  // next particles will spawn according to the location of the previous particle + the ranom gapX (250 to 400 pixels).
  if (time < 0)  {
    
    //add a point at random y.
    particles.add(new Particle(new PVector(particles.get(particles.size()-1).location.x + gapX, random(rminY,rminY+rmaxY))));
    
    //get the location of two last point to trace a line.
    Particle previous = particles.get(p1);
    Particle actual = particles.get(p2);
    lines.add(new Line(previous.location.x,previous.location.y,actual.location.x,actual.location.y)); 
    
    //update int to pass on particles.get with every particle added to the array.
    p1 +=1;
    p2 +=1;

    //framerate for spawning particle.
    println(p1);
    time = 60;

  }
  
  //update and display every particle
  for (int i=0; i < particles.size(); i++) {

    Particle p = particles.get(i);
    p.display();
    p.update();        
  }  
  
  //update and display every lines created by the particles location
  for (int j=0; j < lines.size(); j++) {

    Line l = lines.get(j);
    l.display();
    l.update();        
  }   
  
  //decrease time so when < 0, if statement will add a particle and create a line
  time -= 1;
  
}

//class of the particles
class Particle {
  
 PVector location;
 PVector velocity;
 
 float minY = 300;
 float maxY = 300;
 
 Particle(PVector l) {
     velocity = new PVector(-3,0);
     location = l.get();     
 }
  
  void update() {
      location.x += velocity.x;
  }
  
  void display() {
      fill(0,0,120);
      ellipse(location.x,location.y,30,30);
  }  
}


//class of the lines
class Line {  

 PVector velocity;
 float x1;
 float y1;
 float x2;
 float y2;
 
 //get location of the two particles for location of the line.
 Line(float x1_, float y1_, float x2_, float y2_) {
     velocity = new PVector(-3,0);
     x1 = x1_;
     y1 = y1_;
     x2 = x2_;
     y2 = y2_;     
     
 }
  
  // update location of the line according to the particles.
  void update() {
      x1 += velocity.x;
      x2 += velocity.x;
      
  }
  
  //display the line
  void display() {
      line(x1,y1,x2,y2);
  }
  
}
1 Like

Glad to see you got it to work.

Now you can still improve it a bit. Thanks to your frame counter, you are not spawning as many particles as you were before, but you still create more than you need. Also, when you particules move to the left, you keep them in memory.

Of course, it is working nicely and since your are having such a little amount of particles you computer can handle it. But imagine now you want to scale it up or let it run for days: at some point it will stop working because there are too many particles to deal with.

What you want is to realize that you just need 7 particles at max at any moment. Then you can check if a particule is too much on the left and in this case remove it. And just after your check how many particles you have and if it is less than 6 then you just add one more.

Another thing is that you don’t need to bother with a Line class, you can simply loop through your particules to connect them.

Here is the code implementing those ideas:

final int rminY = 300;
final int rmaxY = 300;
final int gapX = 250;
final int particlesNb = 7;

ArrayList<Particle> particles;


void setup() {
  size(1200, 800);  
  particles = new ArrayList<Particle>();

  // Adding the 6 first particles
  for (int i = 0; i < particlesNb; i++) {
    particles.add(new Particle(width + 40 + i * gapX, (int)random(rminY, rminY+rmaxY)));
  }
}


void draw() {
  background(20);
  fill(50);
  rect(-1, rminY, width+1, rmaxY);

  // Updating the position of the particules 
  for (int i=0; i < particlesNb; i++) {
    particles.get(i).update();
  }
  
  // Checking if the left particule should be removed and if so add a new one on the right
  if (particles.get(0).isOutOnLeft()) {
    particles.remove(0);
    particles.add(new Particle((int)particles.get(particlesNb - 2).getX() + gapX, (int)random(rminY, rminY+rmaxY)));
  }
  
  //update and display every particle
  for (int i=0; i < particlesNb - 1; i++) {
    Particle p1 = particles.get(i);
    Particle p2 = particles.get(i + 1);
    
    stroke(200);
    strokeWeight(2);
    line(p1.getX(), p1.getY(), p2.getX(), p2.getY());
    
    p1.display();
    p2.display();
  }
}

//class of the particles
class Particle {  
  private PVector position;
  private PVector velocity;
  private int diameter;

  Particle(int x, int y) {
    velocity = new PVector(-3, 0);
    position = new PVector(x, y);
    diameter = 30;
  }

  void update() {
    position.x += velocity.x;
    position.y += velocity.y;
  }
  
  float getX() {
    return position.x;
  }
  
  float getY() {
    return position.y;
  }

  boolean isOutOnLeft() {
    return (position.x < -gapX-(diameter/2.0));
  }

  void display() {
    fill(200);
    noStroke();
    ellipse(position.x, position.y, diameter, diameter);
  }
}
1 Like