making particles attract to a specific point

edited March 2018 in Questions about Code

Hi, I'm want to use a simple particles code to visualize migration of refugees and asylum seekers. The code should run on processing.js as a web canvas.

Each source country is an emitter, and each destination country should be a concentration point.

right now I'm trying to make the particles move to a destination point, but I'm unsure why they don't (they just overshoot). Problem is probably in "update()" or in "Particle()"

// All Examples Written by Casey Reas and Ben Fry

// unless otherwise stated.

ParticleSystem ps;
ParticleSystem ps2;

int n = 0;
int emmitMultiplyer = 5;

float locationX = 250;
float locationY = 450;

void setup() {

  size(800,600);

  //frameRate(30);

  //colorMode(RGB,255,255,255,255);

  ps = new ParticleSystem(1,new Vector3D(width/2,height/2,0));
  ps2 = new ParticleSystem(1,new Vector3D(100,200,0));

  smooth();



}



void draw() {

  background(0);
  ellipse(locationX,locationY,5,5);
  ps.run();
  ps2.run();

  for (int i = 0; i<emmitMultiplyer; i++)
  {
    ps.addParticle();
    ps2.addParticle();
    n++;
    //println(n);
  }
  fill(255);
  text("Frame rate: " + int(frameRate), 10, 20);

}





// A simple Particle class



class Particle {

  Vector3D loc;

  Vector3D vel;

  Vector3D acc;

  float r;

  float timer;

  // Another constructor (the one we are using here)

  Particle(Vector3D l) {

//acc = new Vector3D(0,0.0005,0);  // particle acceleration

    vel = new Vector3D(random(-0.1,0.1),random(-0.02,0),0);

    loc = l.copy();

    acc = new Vector3D((locationX-loc.x)*0.000001, (locationY-loc.y)*0.000001,0);

    r = 1.5;  // particle radius

    timer = 10000.0;   // particles lifespan
                      // * emmitMultiplyer = number of living

  }





  void run() {

    update();

    render();

  }



  // Method to update location

  void update() {

    vel.add(acc);

    loc.add(vel);

    timer -= 1.0;

  }



  // Method to display

  void render() {

    ellipseMode(CENTER);

    noStroke();

    fill(255,timer);

    ellipse(loc.x,loc.y,r,r);

  }



  // Is the particle still useful?

  boolean dead() {

    if (timer <= 0.0) {

      return true;

    } else {

      return false;

    }

  }

}





// A class to describe a group of Particles

// An ArrayList is used to manage the list of Particles 



class ParticleSystem {



  ArrayList particles;    // An arraylist for all the particles

  Vector3D origin;        // An origin point for where particles are birthed



  ParticleSystem(int num, Vector3D v) {

    particles = new ArrayList();              // Initialize the arraylist

    origin = v.copy();                        // Store the origin point

    for (int i = 0; i < num; i++) {

      particles.add(new Particle(origin));    // Add "num" amount of particles to the arraylist

    }

  }



  void run() {

    // Cycle through the ArrayList backwards b/c we are deleting

    for (int i = particles.size()-1; i >= 0; i--) {

      Particle p = (Particle) particles.get(i);

      p.run();

      if (p.dead()) {
        n--;
        particles.remove(i);

      }

    }

  }



  void addParticle() {

    particles.add(new Particle(origin));

  }



  void addParticle(Particle p) {

    particles.add(p);

  }



  // A method to test if the particle system still has particles

  boolean dead() {

    if (particles.isEmpty()) {

      return true;

    } else {

      return false;

    }

  }



}







// Simple Vector3D Class 



public class Vector3D {

  public float x;

  public float y;

  public float z;



  Vector3D(float x_, float y_, float z_) {

    x = x_; y = y_; z = z_;

  }



  Vector3D(float x_, float y_) {

    x = x_; y = y_; z = 0f;

  }



  Vector3D() {

    x = 0f; y = 0f; z = 0f;

  }



  void setX(float x_) {

    x = x_;

  }



  void setY(float y_) {

    y = y_;

  }



  void setZ(float z_) {

    z = z_;

  }



  void setXY(float x_, float y_) {

    x = x_;

    y = y_;

  }



  void setXYZ(float x_, float y_, float z_) {

    x = x_;

    y = y_;

    z = z_;

  }



  void setXYZ(Vector3D v) {

    x = v.x;

    y = v.y;

    z = v.z;

  }

  public float magnitude() {

    return (float) Math.sqrt(x*x + y*y + z*z);

  }



  public Vector3D copy() {

    return new Vector3D(x,y,z);

  }



  public Vector3D copy(Vector3D v) {

    return new Vector3D(v.x, v.y,v.z);

  }



  public void add(Vector3D v) {

    x += v.x;

    y += v.y;

    z += v.z;

  }



  public void sub(Vector3D v) {

    x -= v.x;

    y -= v.y;

    z -= v.z;

  }



  public void mult(float n) {

    x *= n;

    y *= n;

    z *= n;

  }



  public void div(float n) {

    x /= n;

    y /= n;

    z /= n;

  }



  public void normalize() {

    float m = magnitude();

    if (m > 0) {

       div(m);

    }

  }



  public void limit(float max) {

    if (magnitude() > max) {

      normalize();

      mult(max);

    }

  }



  public float heading2D() {

    float angle = (float) Math.atan2(-y, x);

    return -1*angle;

  }



  public Vector3D add(Vector3D v1, Vector3D v2) {

    Vector3D v = new Vector3D(v1.x + v2.x,v1.y + v2.y, v1.z + v2.z);

    return v;

  }



  public Vector3D sub(Vector3D v1, Vector3D v2) {

    Vector3D v = new Vector3D(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);

    return v;

  }



  public Vector3D div(Vector3D v1, float n) {

    Vector3D v = new Vector3D(v1.x/n,v1.y/n,v1.z/n);

    return v;

  }



  public Vector3D mult(Vector3D v1, float n) {

    Vector3D v = new Vector3D(v1.x*n,v1.y*n,v1.z*n);

    return v;

  }



  public float distance (Vector3D v1, Vector3D v2) {

    float dx = v1.x - v2.x;

    float dy = v1.y - v2.y;

    float dz = v1.z - v2.z;

    return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);

  }



}
Tagged:

Answers

  • here based on bezierPoint (similar to lerp() by the way)

    https://www.processing.org/reference/bezierPoint_.html

    Chrisir

    // All Examples Written by Casey Reas and Ben Fry
    
    // unless otherwise stated.
    
    ParticleSystem ps;
    ParticleSystem ps2;
    
    int n = 0, n2=0;
    int emmitMultiplyer = 5;
    
    float locationX = 250;
    float locationY = 450;
    
    void setup() {
      size(800, 600);
    
      //frameRate(30);
      //colorMode(RGB,255,255,255,255);
      ps = new ParticleSystem(1, new Vector3D(width/2, height/2, 0));
      ps2 = new ParticleSystem(1, new Vector3D(100, 200, 0));
    
      smooth();
    }
    
    void draw() {
    
      background(0);
      ellipse(locationX, locationY, 5, 5);
      ellipse(width/2, height/2, 5, 5);
      ellipse(100, 200, 5, 5);
    
      ps.run();
      ps2.run();
    
      if (n2<1444) {
        for (int i = 0; i<emmitMultiplyer; i++) {
          ps.addParticle();
          ps2.addParticle();
          n++;
          //println(n);
        }
      }
    
      n2+=emmitMultiplyer;
    
      fill(255);
      text("Frame rate: " 
        + int(frameRate), 10, 20);
    }
    
    // =============================================================================
    // A simple Particle class
    
    class Particle {
    
      Vector3D loc;
      Vector3D vel;
      Vector3D acc;
      Vector3D locHome, b, c;
    
      float r;
      float timer;
    
      float t=0.0; 
    
      // Another constructor (the one we are using here)
      Particle(Vector3D l) {
    
        //acc = new Vector3D(0,0.0005,0);  // particle acceleration
    
        acc = new Vector3D(0, 0, 0); //  new Vector3D(random(-0.1, 0.1), random(-0.02, 0), 0);
        loc = l.copy();
        locHome = l.copy();
    
        r = 1.5;  // particle radius
        timer = 10000.0;   // particles lifespan
        // * emmitMultiplyer = number of living
    
        b=new Vector3D(random(10.1, 110.1), random(10.02, 110), 0);
        c=new Vector3D(random(310.1, 410.1), random(10.02, height-55), 0);
      }
    
      void run() {
        update();
        render();
      }
    
      // Method to update location
      void update() {
    
        if (t>=1)
          return;
    
        // https : // www.processing.org/reference/bezierPoint_.html
    
        loc.x = bezierPoint(locHome.x, b.x, c.x, locationX, t);
        loc.y = bezierPoint(locHome.y, b.y, c.y, locationY, t);
    
        t+=.01; 
    
        // curvePoint(a, b, c, d, t)
        // vel.add(acc);
        // loc.add(vel);
        timer -= 1.0;
      }
    
      // Method to display
      void render() {
        ellipseMode(CENTER);
        noStroke();
        fill(255, timer);
        ellipse(loc.x, loc.y, r, r);
      }
    
      // Is the particle still useful?
      boolean dead() {
        if (timer <= 0.0||t>=1.0) {
          return true;
        } else {
          return false;
        }
      }
    }
    
    // A class to describe a group of Particles
    // An ArrayList is used to manage the list of Particles
    class ParticleSystem {
    
      ArrayList particles;    // An arraylist for all the particles
      Vector3D origin;        // An origin point for where particles are birthed
    
      ParticleSystem(int num, Vector3D v) {
        particles = new ArrayList();              // Initialize the arraylist
        origin = v.copy();                        // Store the origin point
    
        for (int i = 0; i < num; i++) {
          particles.add(new Particle(origin));    // Add "num" amount of particles to the arraylist
        }
      }
    
      void run() {
        // Cycle through the ArrayList backwards b/c we are deleting
        for (int i = particles.size()-1; i >= 0; i--) {
          Particle p = (Particle) particles.get(i);
          p.run();
          if (p.dead()) {
            n--;
            particles.remove(i);
          }
        }
      }
    
      void addParticle() {
        particles.add(new Particle(origin));
      }
    
      void addParticle(Particle p) {
        particles.add(p);
      }
    
      // A method to test if the particle system still has particles
      boolean dead() {
        if (particles.isEmpty()) {
          return true;
        } else {
          return false;
        }
      }
    }
    
    // Simple Vector3D Class
    public class Vector3D {
    
      public float x;
      public float y;
      public float z;
    
      Vector3D(float x_, float y_, float z_) {
        x = x_; 
        y = y_; 
        z = z_;
      }
    
      Vector3D(float x_, float y_) {
        x = x_; 
        y = y_; 
        z = 0f;
      }
    
      Vector3D() {
        x = 0f; 
        y = 0f; 
        z = 0f;
      }
    
      void setX(float x_) {
        x = x_;
      }
    
      void setY(float y_) {
        y = y_;
      }
    
      void setZ(float z_) {
        z = z_;
      }
    
      void setXY(float x_, float y_) {
        x = x_;
        y = y_;
      }
    
      void setXYZ(float x_, float y_, float z_) {
        x = x_;
        y = y_;
        z = z_;
      }
    
      void setXYZ(Vector3D v) {
        x = v.x;
        y = v.y;
        z = v.z;
      }
    
      public float magnitude() {
        return (float) Math.sqrt(x*x + y*y + z*z);
      }
    
      public Vector3D copy() {
        return new Vector3D(x, y, z);
      }
    
      public Vector3D copy(Vector3D v) {
        return new Vector3D(v.x, v.y, v.z);
      }
    
      public void add(Vector3D v) {
        x += v.x;
        y += v.y;
        z += v.z;
      }
    
      public void sub(Vector3D v) {
        x -= v.x;
        y -= v.y;
        z -= v.z;
      }
    
      public void mult(float n) {
        x *= n;
        y *= n;
        z *= n;
      }
    
      public void div(float n) {
        x /= n;
        y /= n;
        z /= n;
      }
    
      public void normalize() {
        float m = magnitude();
        if (m > 0) {
          div(m);
        }
      }
    
      public void limit(float max) {
        if (magnitude() > max) {
          normalize();
          mult(max);
        }
      }
    
      public float heading2D() {
        float angle = (float) Math.atan2(-y, x);
        return -1*angle;
      }
    
      public Vector3D add(Vector3D v1, Vector3D v2) {
        Vector3D v = new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
        return v;
      }
    
      public Vector3D sub(Vector3D v1, Vector3D v2) {
        Vector3D v = new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
        return v;
      }
    
      public Vector3D div(Vector3D v1, float n) {
        Vector3D v = new Vector3D(v1.x/n, v1.y/n, v1.z/n);
        return v;
      }
    
      public Vector3D mult(Vector3D v1, float n) {
        Vector3D v = new Vector3D(v1.x*n, v1.y*n, v1.z*n);
        return v;
      }
    
      public float distance (Vector3D v1, Vector3D v2) {
        float dx = v1.x - v2.x;
        float dy = v1.y - v2.y;
        float dz = v1.z - v2.z;
        return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
      }
    }
    
  • Thank you so much! I'll try to wrap my head around what you did :)

    is "t" the progress that is bade on the bezier?

  • Yes, that's right.

    It would be neat if you had a world map and adress the countries on that map.

    The the points would emit from a area (the country) and go to an area (instead of a single point).

  • I'm thinking about it as one possible representation. although data visualization wise I would lose the ability to display other information on the x and y axes.

  • yeah, maybe

  • If you are using xy for geo coordinates then you can use the size or color (or shape) of a particle to convey extra dimensions of information.

  • @jeremydouglass true, but I might want to use color and size of emmiter for different information like country geographical size, selection indicator, migrant status etc.

    It's still in early stages so I'll see where it goes :) right now just trying to get the basic particle movement thing going

  • YOu can simply use lerp() for movement but it would just give you a straight line

  • I'm running into another issue that I'm at loss how to solve:

    I'm slowly assigning the dataset to create particle systems. right now i just want it to create an emitter for every country.

    for some reason PSystems[] creates a nullPointerException on the draw() function, while it's a working array in setup()

    // Based on Example Written by Casey Reas and Ben Fry
    // Edited by Tom Bar-Gal
    
    
    
    //particlesystem()
    //addparticle()
    //particle()
    
    
    
    // ========== Table Data Stuff 
    
    Table table;
    int k = 0;
    String[] destCountryArray = new String[0];
    String[] sourceCountryArray = new String[0];
    
    String destCountry = "";
    String prevDestCountry = "";
    
    String sourceCountry;
    
    // ========
    
    
    int maxParticles = 12000;
    
    ParticleSystem ps;
    ParticleSystem ps2;
    
    int n = 0, n2=0;
    int emmitMultiplyer = 1;
    int emmitFreq = 1;
    float particleSpeed = 0.001;
    
    float locationX = 250;
    float locationY = 450;
    
    int[] sourceX = {10, 40, 200, 400, 700};
    int[] destX = {300, 600, 300, 600, 600};
    int[] amount = {10, 100, 500, 800, 1000};
    
    // a,b,c... max*a/{a+b+c...}  
    
        ParticleSystem[] PSystems;
    
    
    void setup() {
      size(800, 600);
    
     //=============== load table and create an array of countries
    
       table = loadTable("asylum_seekers.csv", "header");
    
        destCountryArray = (String[]) append(destCountryArray, "Israel");
    
      for (TableRow row : table.rows()) {
        //println("going over row" + row.getString("Country / territory of asylum/residence"));
        String tempCountryHolder = row.getString("Country / territory of asylum/residence");
        //println("Got a temp country holder" + tempCountryHolder);
        boolean exists = countryExists(tempCountryHolder);
        if (exists==true) {
          //println("exists, skipping");
          continue;
        }
        println("Appending "+tempCountryHolder+" to list of length " +destCountryArray.length);
        destCountryArray = (String[]) append(destCountryArray, tempCountryHolder);
        println("destCountryArray length = "+ destCountryArray.length);
      }
    
      //============================
         ParticleSystem[] PSystems = new ParticleSystem[destCountryArray.length];
      //frameRate(30);
      //colorMode(RGB,255,255,255,255);
      for (int i = 0; i<destCountryArray.length; i++){
    
        PSystems[i] = new ParticleSystem(1, new Vector3D(i*40, 100, 0), new Vector3D(i*40, 500, 0));
        //println("PSystems " + i + " is " +PSystems[i]);
      }
    
      //ps = new ParticleSystem(1, new Vector3D(width/2, height/2, 0));
      //ps2 = new ParticleSystem(1, new Vector3D(100, 200, 0));
    
      smooth();
    }
    
    void draw() {
    
      background(255);
      //ellipse(locationX, locationY, 5, 5);
      //ellipse(width/2, height/2, 5, 5);
      //ellipse(100, 200, 5, 5);
      for (int i = 0; i<destCountryArray.length; i++){
        println(PSystems[i]);
        PSystems[i].run();
      }
    
      if (frameCount % emmitFreq == 0){
        for (int i = 0; i<emmitMultiplyer; i++) {
          for (int k = 0; k<destCountryArray.length; k++){
             PSystems[k].addParticle();
                   n++;
          }
    
        }
      }
    
      n2+=emmitMultiplyer;
    
      fill(0);
      text("Frame rate: "
        + int(frameRate), 10, 20);
              println(n);      println(n);
    }
    
    
    
    
    // ==============================//  A simple Particle class  // ===============================================//
    
    
    
    
    class Particle {
    
      Vector3D loc;
      Vector3D des;
      Vector3D vel;
      Vector3D acc;
      Vector3D locHome, b, c;
      float relativeSpeed;
    
      float r;
      float timer;
    
      float t=0.0; 
    
      // Another constructor (the one we are using here)
      Particle(Vector3D l, Vector3D m) {
    
        //acc = new Vector3D(0,0.0005,0);  // particle acceleration
    
        acc = new Vector3D(0, 0, 0); //  new Vector3D(random(-0.1, 0.1), random(-0.02, 0), 0);
        loc = l.copy();
        des = m.copy();
        locHome = l.copy();
        locHome.x = locHome.x+random(-2,2);
        locHome.y = locHome.y+random(-2,2);
        des.x = des.x+random(-2,2);
        des.y=des.y+random(-2,2);
        relativeSpeed = random(0.1, 1.2);
    
        r = random(0.5, 2);  // particle radius
        timer = 10000.0;   // particles lifespan
        // * emmitMultiplyer = number of living
    
        b=new Vector3D(locHome.x+random(-20,20), locHome.y+random(120,180), 0);
        c=new Vector3D(des.x+random(-20,30), des.y-random(120,180), 0);
      }
    
      void run() {
        update();
        render();
      }
    
      // Method to update location
      void update() {
    
        if (t>=1)
          return;
    
        // https : // www.processing.org/reference/bezierPoint_.html
    
        loc.x = bezierPoint(locHome.x, b.x, c.x, des.x, t);
        loc.y = bezierPoint(locHome.y, b.y, c.y, des.y, t);
    
        t+=particleSpeed*relativeSpeed; 
    
        // curvePoint(a, b, c, d, t)
        // vel.add(acc);
        // loc.add(vel);
        //timer -= 1.0;
      }
    
      // Method to display
      void render() {
        ellipseMode(CENTER);
        noStroke();
        fill(0, 100);
    
        ellipse(loc.x, loc.y, r, r);
      }
    
      // Is the particle still useful?
      boolean dead() {
        if (timer <= 0.0||t>=1.0) {
          return true;
        } else {
          return false;
        }
      }
    }
    
    
    // ==============================//  A ParticleSystem  // ===============================================// 
    
    // A class to describe a group of Particles
    // An ArrayList is used to manage the list of Particles
    class ParticleSystem {
    
      ArrayList particles;    // An arraylist for all the particles
      Vector3D origin;        // An origin point for where particles are birthed
      Vector3D dest;
    
      ParticleSystem(int num, Vector3D v, Vector3D d) {
        particles = new ArrayList();              // Initialize the arraylist
        origin = v.copy();     // Store the origin point
        dest = d.copy();
    
        for (int i = 0; i < num; i++) {
          particles.add(new Particle(origin, dest));    // Add "num" amount of particles to the arraylist
        }
      }
    
      void run() {
        // Cycle through the ArrayList backwards b/c we are deleting
        for (int i = particles.size()-1; i >= 0; i--) {
          Particle p = (Particle) particles.get(i);
          p.run();
          if (p.dead()) {
            particles.remove(i);
            n--;
          }
        }
      }
    
      void addParticle() {
        particles.add(new Particle(origin, dest));
      }
    
      //void addParticle(Particle p) {
      //  particles.add(p);
      //}
    
      // A method to test if the particle system still has particles
      boolean dead() {
        if (particles.isEmpty()) {
          return true;
        } else {
          return false;
        }
      }
    }
    
    // Simple Vector3D Class
    public class Vector3D {
    
      public float x;
      public float y;
      public float z;
    
      Vector3D(float x_, float y_, float z_) {
        x = x_; 
        y = y_; 
        z = z_;
      }
    
      Vector3D(float x_, float y_) {
        x = x_; 
        y = y_; 
        z = 0f;
      }
    
      Vector3D() {
        x = 0f; 
        y = 0f; 
        z = 0f;
      }
    
      void setX(float x_) {
        x = x_;
      }
    
      void setY(float y_) {
        y = y_;
      }
    
      void setZ(float z_) {
        z = z_;
      }
    
      void setXY(float x_, float y_) {
        x = x_;
        y = y_;
      }
    
      void setXYZ(float x_, float y_, float z_) {
        x = x_;
        y = y_;
        z = z_;
      }
    
      void setXYZ(Vector3D v) {
        x = v.x;
        y = v.y;
        z = v.z;
      }
    
      public float magnitude() {
        return (float) Math.sqrt(x*x + y*y + z*z);
      }
    
      public Vector3D copy() {
        return new Vector3D(x, y, z);
      }
    
      public Vector3D copy(Vector3D v) {
        return new Vector3D(v.x, v.y, v.z);
      }
    
      public void add(Vector3D v) {
        x += v.x;
        y += v.y;
        z += v.z;
      }
    
      public void sub(Vector3D v) {
        x -= v.x;
        y -= v.y;
        z -= v.z;
      }
    
      public void mult(float n) {
        x *= n;
        y *= n;
        z *= n;
      }
    
      public void div(float n) {
        x /= n;
        y /= n;
        z /= n;
      }
    
      public void normalize() {
        float m = magnitude();
        if (m > 0) {
          div(m);
        }
      }
    
      public void limit(float max) {
        if (magnitude() > max) {
          normalize();
          mult(max);
        }
      }
    
      public float heading2D() {
        float angle = (float) Math.atan2(-y, x);
        return -1*angle;
      }
    
      public Vector3D add(Vector3D v1, Vector3D v2) {
        Vector3D v = new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
        return v;
      }
    
      public Vector3D sub(Vector3D v1, Vector3D v2) {
        Vector3D v = new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
        return v;
      }
    
      public Vector3D div(Vector3D v1, float n) {
        Vector3D v = new Vector3D(v1.x/n, v1.y/n, v1.z/n);
        return v;
      }
    
      public Vector3D mult(Vector3D v1, float n) {
        Vector3D v = new Vector3D(v1.x*n, v1.y*n, v1.z*n);
        return v;
      }
    
      public float distance (Vector3D v1, Vector3D v2) {
        float dx = v1.x - v2.x;
        float dy = v1.y - v2.y;
        float dz = v1.z - v2.z;
        return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
      }
    }
    
    boolean countryExists(String tempCountryHolder) {
    
        for (int i = 0; i < destCountryArray.length; i++) {
              //println("comparing '" +tempCountryHolder +"' with '"+destCountryArray[i] + "'");
              if (tempCountryHolder.equals(destCountryArray[i]) == true) {
                //println("found : "+ tempCountryHolder);
                return true; // it exists 
              } //if
        } //for
        return false ; // not found 
    }//func
    
  • Line 82/83 should be your guiding star

    Don’t define a new list in line 73/78

  • @Chrisir I'm trying to create an array of ParticleSystems, eventually to have different sources, destinations and amounts based on a CSV.

    That's why I'm using lines 73 / 78 that way.

  • Remove this ParticleSystem[]

    From line 73

    Otherwise you declare the array anew locally, overshadowing the global array/ variable

  • OH amazing thank you @Chrisir

Sign In or Register to comment.