Help: Make particlesystem bounce off edges

Hi all! I’m fairly new to coding and all new to this forum. I have a small problem with some code which I would like a bit of guidance on. The code is supposed to model a fire/flame moving around the screen. The problem is that when the flame reaches an edge of the screen, it dies out. I want it instead to bounce of the edges, which is why I implemented the inverted velocity in the update function, but it doesn’t seem to work. The code looks like this:

class ParticleSystem {
  ArrayList<Particle> particles;
  PVector origin;
  PVector originmove;
  PImage img;
  float t = 0;
  float xoff;

  ParticleSystem (int num, PVector v, PImage img_) {
    particles = new ArrayList<Particle>();
    origin = v.copy();
    xoff = xoff + .05;
    originmove = new PVector(noise(xoff), noise(xoff));
    img = img_;
    for (int i = 0; i < num; i++) {
      particles.add(new Particle(origin.add(originmove), img));
      particles.add(new Particle(origin, img));
    }
    originmove.mult(0.1);
  }


  void run() {
    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = particles.get(i);
      p.run();
    }
  }

  // Method to add a force vector to all particles currently in the system
  void applyForce(PVector dir) {
    // Enhanced loop!!!
    for (Particle p : particles) {
      p.applyForce(dir);
    }
  }  

  void addParticle() {
    particles.add(new Particle(origin.add(originmove), img));
    particles.add(new Particle(origin, img));
  }
}

class Particle {
  PVector position;
  PVector velocity;
  PVector acceleration;
  float lifespan;  
  PImage img;
  PImage rose;
  float t = 0;
  float p;

  Particle(PVector l, PImage img_) {
    position = l.copy();
    float vx = (float) randomGaussian()*0.3;
    float vy = (float) randomGaussian()*0.4 - 0.2;
    velocity = new PVector(vx, vy);
    acceleration = new PVector(0, 0);
    lifespan = 100.0;
    img = img_;
  }

  void run() {
    update();
    render();
  }

  void render() {
    imageMode(CENTER);
    tint(255, lifespan);
    image(img, position.x, position.y);
  }


  void applyForce(PVector f) {
    acceleration.add(f);
  }

  void update() {
    position.add(velocity);
    velocity.add(acceleration);
    lifespan -= 2.0;
    velocity.limit(2);
    acceleration.mult(0);
    if (position.x > width) {
      velocity.mult(-1);
      acceleration.mult(-1);
    }
    if (position.y > height) {
      velocity.mult(-1);
      acceleration.mult(-1);
    }
  }
}

ParticleSystem ps;
float t = 0;
float increment = 0.1;

void settings() {
  size(640, 340);
}

void setup() {
  PImage img = loadImage("blacksmoke.png");
  ps = new ParticleSystem(0, new PVector(width-160, height-60), img);
  background(255);
}

void draw() {
  // Calculate a "wind" force based on mouse horizontal position
  t += 0.6;
  float dx = map(noise(t), 0, 1, -0.4, 0.5);
  float dy = map(noise(t), 0, 1, -0.2, 0.1);
  PVector wind = new PVector(dx, dy);
  ps.applyForce(wind);
  ps.run();
  for (int i = 0; i < increment; i++) {
    increment += 0.0001;
    ps.addParticle();
  }
  //saveFrame("output/ps_####.png");
}

I’ve posted the three parts of the code separately; I hope that’s okay. Do you guys have any suggestions on what I’ve done wrong?

Best,

Daniel

Unfortunately we can’t run the code because we don’t have the image files.

I suspect that your problem is with the update method in the Particle class

When the particle hits the side you negate the velocity and acceleration which is fine if the next update brings the particle back on screen. But what happens if at the next update the particle is still offscreen, the velocity and acceleration is negated again so the Particle will move further offscreen, this will be repeated until the particle moves onscreen if ever.

This blog post describes the problem in some details.

Thanks a lot quark. I took a look at the link you sent me but I still can’t get it to work properly.

Here is the image file if you want to take a look at it.

blacksmoke

Best,

Daniel

OK I have tried running the program which has given me an insight into the program.

Can you explain what the variables origin and originmove are used for. Should origin remain unchanged throughout the program?

Origin should remain unchanged, yes. It is basically the location from which I would like the smoke to start spreading, which I prefer to be the same on each run. Originmove is supposed to make the smoke/fire spread in a random fashion across the screen (this aspect isn’t implemented very well at the moment; the smoke has way too much of a determined direction atm).

P.S.: I just tried changing the conditional if (position.y > height) in the update function to if (position.y > height-100), and nothing much seems to change. That’s probably a big hint the flaw is not necessarily within the update function.

Best,

Daniel

That could be your problem because the origin is changing every time you create a particle when you execute the statement.

particles.add(new Particle(origin.add(originmove), img));

The first parameter, origin.add(originmove) modifies origin which you don’t want to happen. You do this twice. I have modified the ParticleSystem class to correct this and avoid code duplication in the constructor.
See if this helps.

  ParticleSystem (int num, PVector v, PImage img_) {
    particles = new ArrayList<Particle>();
    origin = v.copy();
    xoff = xoff + .05;
    originmove = new PVector(noise(xoff), noise(xoff));
    img = img_;
    for (int i = 0; i < num; i++) {
      addParticle(); // avoid code duplication
    }
    originmove.mult(0.1);
  }


  void run() {
    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = particles.get(i);
      p.run();
    }
  }

  // Method to add a force vector to all particles currently in the system
  void applyForce(PVector dir) {
    // Enhanced loop!!!
    for (Particle p : particles) {
      p.applyForce(dir);
    }
  }  

  void addParticle() {
    particles.add(new Particle(PVector.add(origin, originmove), img)); // origin is unchanged
    particles.add(new Particle(origin, img));
  }
}

Thanks. I’ve been busy for the last few days, so I haven’t been able to reply until now.

If I run your code, the smoke stays in the same spot throughout the sketch. That’s not the intention. Perhaps I did not express myself too clearly in the last post. Origin is simply where the smoke should start. So, to correct myself, it should not remain unchanged throughout the program, but it should have the same startpoint at each run.

As stated, the sketch is an attempt to emulate a smoke or fire spreading across a landscape, finally taking up the entire screen.

In the original code the fire moves very slowly because originMove has a very small value. Since the value of origin changes as the sketch runs it is THIS value that needs to be constrained to the screen. Since this is changed everytime a particle is added then you need to add code to the addParticle method.

  void addParticle() {
    particles.add(new Particle(origin.add(originmove), img));
    particles.add(new Particle(origin, img));
    // Add code here to constrain origin.x to 0-width and
    // origin.y to 0-height-1
    // also mdify originMove.x and originMove.y to reverse the movement 
  }