BouncingBubbles to PVector

Hey everyone! I tried to convert the (https://processing.org/examples/bouncybubbles.html) into PVector format with PVector arrays(I'm well aware of the existence of ArrayList but I don't entirely understand those yet). I've worked several hours debugging and println-ing everything in all of my loops but it's still so very buggy.

How do I turn that example into a PVector-based example?

int TOTAL = 10;
Particle[] particles = new Particle[TOTAL];


void setup() {
  size(640, 360);

  for (int i = 0; i < TOTAL; i++) {
    particles[i] = new Particle(random(width), random(height), random(-1, 1), random(-1, 1), 50, i, particles);
  }
}

void draw() {
  background(51);
  for (int i = 0; i < particles.length; i++) {
    particles[i].show();
    particles[i].update();
    particles[i].checkOthers();
    particles[i].applyForce(particles[i].edges());
  }
}

class Particle {
  PVector pos;
  PVector vel;
  PVector acc;
  int colour, id;
  float size;
  Particle[] others;

  Particle(float x, float y, float xvel, float yvel, float size_, int id_, Particle[] others_) {
    pos = new PVector(x, y);
    vel = new PVector(xvel, yvel);
    acc = new PVector(0, 0);
    size = size_;
    colour = color(random(255), 0, random(255));
    id = id_;
    others = others_; 
  }

  void update() {
    vel.add(acc);
    pos.add(vel);
    vel.limit(4);
    acc.mult(0);
  }

  void applyForce(PVector force) {
    if (force!=null) {
      acc.add(force);
    }
  }

  PVector edges() { 
    if (pos.x < 0 + 10) {
      return new PVector(1, sin(random(-PI, PI)));
    } else if (pos.x > width - 10) {
      return new PVector(-1, sin(random(-PI, PI)));
    } else if (pos.y > height - 10) {
      return new PVector(cos(random(-PI, PI)), -1);
    } else if (pos.y < 0 + 10) {
      return new PVector(cos(random(-PI, PI)), 1);
    } else return null;
  }

  void checkOthers() {
    for (int i = id + 1; i < TOTAL; i++) {
      PVector otherpos = new PVector(others[i].pos.x, others[i].pos.y);
      float dx = otherpos.x - pos.x;
      float dy = otherpos.y - pos.y;
      float distance = PVector.dist(otherpos, pos);
      float mindist = others[i].size/2 + size/2;
      if (distance < mindist) {
        float angle = atan2(dy,dx);
        PVector target = new PVector((pos.x + cos(angle)*mindist), (pos.y + sin(angle)*mindist));
        PVector target2 = target.sub(otherpos);
        target2.mult(0.1);
        others[i].applyForce(target2.setMag(-1));
        applyForce(target2.mult(1));
      }
    }
  }

  void show() {
    fill(colour);
    noStroke();
    strokeWeight(4);
    ellipse(pos.x, pos.y, size, size);
  }
}

This is my code at this moment, thank you! In the object particle I have a physics engine with an applyForce(force) function.

Tagged:

Answers

  • It will be great to see all the code you have so far.

    Kf

  • edited April 2017

    Edited my discussion!

  • edited April 2017

    @EdwinCarlsson --

    The basics of this adaptation are good and it is a good learning exercise, but rather than replacing the float x/y pairs with PVectors, you've rewritten things pretty extensively with some renaming and mixed-and-matched bits of the old code into the new code. The two most obvious examples:

    1.

    In the original example, void move() bounces balls off the walls by reversing the vx or vy direction (multiply by a negative number, friction).

    if (x + diameter/2 > width) {
      x = width - diameter/2;
      vx *= friction; 
    }
    

    In the new code, edges() bounces balls off the walls in a randomized way that often speeds up the ball. Balls speeding up when they bounce is odd.

    PVector edges() { 
    if (pos.x < 0 + 10) {
      return new PVector(1, sin(random(-PI, PI)));
    }
    

    2.

    In the original example, collide() checks for two balls colliding and assigns them new velocities (vx, vy).

    if (distance < minDist) { 
        float angle = atan2(dy, dx);
        float targetX = x + cos(angle) * minDist;
        float targetY = y + sin(angle) * minDist;
        float ax = (targetX - others[i].x) * spring;
        float ay = (targetY - others[i].y) * spring;
        vx -= ax;
        vy -= ay;
        others[i].vx += ax;
        others[i].vy += ay;
      }
    

    In the new example, checkOthers() checks for two balls colliding and then... well, it does a lot of things, but it appears one of them is that when two balls collide their speeds are added together and copied from one to the other. This isn't really a collision -- more like a high speed flocking effect.

      if (distance < mindist) {
        float angle = atan2(dy,dx);
        PVector target = new PVector((pos.x + cos(angle)*mindist), (pos.y + sin(angle)*mindist));
        PVector target2 = target.sub(otherpos);
        target2.mult(0.1);
        others[i].applyForce(target2.setMag(-1));
        applyForce(target2.mult(1));
      }
    

    Consider starting with a literal adaptation of the BouncingBubbles if (distance < minDist) code block that just uses your PVector as a container object for an x and y. No PVector methods. Get that working, then slowly introduce using PVector methods. Be careful of when a non-static method is applied directly to the vector, e.g. I'm not sure if you meant to change target when you did this:

    PVector target2 = target.sub(otherpos);
    

    The version of the method that substracts two vectors is a static method and returns a PVector, the others act directly on the vector -- https://processing.org/reference/PVector_sub_.html

  • edited April 2017

    Note to moderators/common forum users
    Something seems wrong with the formatting of the code in the question. Things like float, for and new are being highlighted in blue bold, almost as if it's p5.js (at least, I think). Why?

  • @GoToLoop I assumed you wanted me to post it there, so I did.

  • I don't think posting there would be enough. You should also call attention to the forum dev there! :-\"

  • edited April 2017

    @jeremydouglass Hey again!

    I did this instead, and this looks far from natural and far from the example, but way better than my first try. What am I missing, if I may ask?

      void checkOthers2() {
        float K;
        for (int i = id + 1; i < TOTAL; i++) {
          PVector otherpos = new PVector(others[i].pos.x, others[i].pos.y);
          float distance = PVector.dist(otherpos, pos);
          float mindist = others[i].size/2 + size/2;
          if (distance < mindist) {
            touching = true;
          } else {
            touching = false;
          }
          
          if (touching) {
            float angle = degrees(PVector.angleBetween(otherpos, pos));
            float TargetX = pos.x + cos(angle);
            float TargetY = pos.y + sin(angle);
            PVector Target = new PVector(TargetX, TargetY);
            PVector ax = Target.sub(others[i].pos);
            vel.add(ax);
            others[i].vel.sub(ax);
          }
        }
      }
    
  • edited April 2017

    @EdwinCarlsson -- This should be a more concise version of what you just posted (untested):

    void checkOthers2() {
      for (int i = id + 1; i < TOTAL; i++) {
        float distance = PVector.dist(pos, others[i].pos);
        float mindist = size/2 + others[i].size/2;
        if (distance < mindist) { // touching
          vel.mult(-1);
          others[i].vel.mult(-1);
        }
      }
    }
    
  • I've solved the problem! Thank your for your initial response, @jeremydouglass!

     
    int TOTAL = 25;
    Particle[] particles = new Particle[TOTAL];
    
    
    void setup() {
      size(640, 360);
      colorMode(RGB);
    
      for (int i = 0; i < TOTAL; i++) {
        particles[i] = new Particle(random(width), random(height), random(-1, 1), random(-1, 1), 30, i, particles);
      }
    }
    
    void draw() {
      background(51);
      for (int i = 0; i < particles.length; i++) {
        particles[i].show();
        particles[i].update();
        particles[i].checkOthers2();
        particles[i].applyForce(particles[i].edges());
      }
    }
    
    class Particle {
      PVector pos;
      PVector vel;
      PVector acc;
      int colour, id;
      float size;
      Particle[] others;
      boolean touching;
    
      Particle(float x, float y, float xvel, float yvel, float size_, int id_, Particle[] others_) {
        pos = new PVector(x, y);
        vel = new PVector(xvel, yvel);
        acc = new PVector(0, 0);
        size = size_;
        colour = color(random(255), 0, random(255));
        id = id_;
        others = others_;
      }
    
      void update() {
        vel.add(acc);
        pos.add(vel);
        vel.limit(2);
        acc.mult(0);
      }
    
      void applyForce(PVector force) {
        if (force!=null) {
          acc.add(force);
        }
      }
    
      PVector edges() { 
        if (pos.x < 0 + size) {
          return new PVector(1, 0);
        } else if (pos.x > width - size) {
          return new PVector(-1, 0);
        } else if (pos.y > height - size) {
          return new PVector(0, -1);
        } else if (pos.y < 0 + size) {
          return new PVector(0, 1);
        } else return null;
      }
    
      void checkOthers2() {
        for (int i = id + 1; i < TOTAL; i++) {
          PVector otherpos = new PVector(others[i].pos.x, others[i].pos.y);
          float distance = PVector.dist(otherpos, pos);
          float mindist = others[i].size/2 + size/2;
          if (distance < mindist) { //<>//
            float angle = (PVector.angleBetween(otherpos, pos));
            float TargetX = pos.x + cos(angle);
            float TargetY = pos.y + sin(angle);
            PVector Target = new PVector(TargetX, TargetY);
            PVector ax = Target.sub(others[i].pos);
            ax.setMag(0.5);
            vel.add(ax);
            others[i].vel.sub(ax);
          }
        }
      }
    
      void show() {
        fill(colour);
        noStroke();
        ellipse(pos.x, pos.y, size, size);
      }
    }
    

    This code works perfectly now!

Sign In or Register to comment.