Frame Rate keeps getting lower

edited May 2018 in Questions about Code

Hi

I working on a code running particles to represent refugee data The code limits the number of particles to keep the frameRate but even after using a steady number of particles, it keeps getting slower with lower frameRate.

I'm really not sure what I can do to make this run faster / smoother. Why does it keep getting slower?

Here's the data folder for the Sketch: https://www.dropbox.com/sh/z69phco3tahqklu/AAB8PLMpBQMoFUX5NGSie-5fa?dl=0

PImage texture;
PGraphics pg;
PShape worldMap;
import java.util.Map;  
int filterPSystemsNum;

PGraphics sumPG;

// ========== Table Data Stuff 

Table immigrationTable;
Table conflictTable;
Table countryLocations;

int k = 0;
String[] destCountryArray = new String[0];
String[] sourceCountryArray = new String[0];

String destCountry = "";
String prevDestCountry = "";

String sourceCountry;

float mapMultiplyX = 4;
float mapOffsetX = -100;
float mapMultiplyY = 4;
float mapOffsetY = 50;

// ========

int amountSum = 1;

String displayType = "Circular";

int maxParticles = 9000;

ParticleSystem ps;
ParticleSystem ps2;

int n = 0, n2=0;
int emmitMultiplyer = 1;
int emmitFreq = 1;
float particleSpeed = 1;

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};
int highestAmount;

float radius = 300;

float tempLong;
float tempLat;

int yearFilterMin = 1996;
int yearFilterMax = 2016;


ParticleSystem[] PSystems;

HashMap<String, Country> countries = new HashMap<String, Country>();

void setup() {
  size(1200, 800, P2D);
  ((PGraphicsOpenGL)g).textureSampling(3);

  texture = loadImage("paper_texture.jpg");
  //worldMap = loadShape("worldLow.svg");

  //=============== load immigrationTable and create an array of countries


  immigrationTable = loadTable("asylum_seekers_all.csv", "header");
  countryLocations = loadTable("LatLonCountries2.csv", "header");
  conflictTable = loadTable("ged171.csv", "header");

  destCountryArray = (String[]) append(destCountryArray, "Israel");

  sumPG = createGraphics(width, height);

  pg = createGraphics(width, height);
  pg.beginDraw();
  pg.background(177);

  pg.endDraw();
  image(pg, 0, 0);

  for (TableRow row : immigrationTable.rows()) {
    if (row.getInt("Year") <= yearFilterMax && row.getInt("Year") >= yearFilterMin) {

      String tempCountryHolder = row.getString("Country / territory of asylum/residence");

      boolean exists = countryExists(tempCountryHolder);
      if (exists==true) {

        continue;
      }

      destCountryArray = (String[]) append(destCountryArray, tempCountryHolder);
    }
  }

  for (TableRow row : immigrationTable.rows()) {
    if (row.getInt("Year") <= yearFilterMax && row.getInt("Year") >= yearFilterMin) {

      String tempCountryHolder = row.getString("Origin");

      boolean exists = countryExists(tempCountryHolder);
      if (exists==true) { //println("exists, skipping");
        continue;
      }
      destCountryArray = (String[]) append(destCountryArray, tempCountryHolder);
    }
  }
  destCountryArray = sort(destCountryArray);



  //============================ country hashmaps ======================================

  for (int i = 0; i < destCountryArray.length; i++) {
    String name = destCountryArray[i];




    for (TableRow row : countryLocations.rows()) {


      if (name.equals(row.getString("name"))) {

        tempLong = row.getFloat("longitude");
        tempLat = row.getFloat("latitude");
      }
    }

    countries.put(name, new Country(name, width/2+(tempLong*mapMultiplyX)+mapOffsetX, height/2-(tempLat*mapMultiplyY)+mapOffsetY));

  }

  for (String key : countries.keySet()) {
    Country c = countries.get(key);

  }

  //============================ PSystems =============

  PSystems = new ParticleSystem[immigrationTable.getRowCount()];
  int j = 0;

highestAmount = 291664;

  println(amountSum);
  for (TableRow row : immigrationTable.rows()) {
    if (row.getInt("Year") <= yearFilterMax && row.getInt("Year") >= yearFilterMin) {
      int tempAmount = row.getInt("decisions_recognized");
      String tempDestCountry = row.getString("Country / territory of asylum/residence");
      String tempSourceCountry = row.getString("Origin");
      int tempYear = row.getInt("Year");

      float sourceEmmiterX = countries.get(tempSourceCountry).x;
      float sourceEmmiterY = countries.get(tempSourceCountry).y;
      float destEmmiterX = countries.get(tempDestCountry).x;
      float destEmitterY = countries.get(tempDestCountry).y;

      PSystems[j] = new ParticleSystem(1, new Vector3D(sourceEmmiterX, sourceEmmiterY, 0), new Vector3D(destEmmiterX, destEmitterY, 0), tempAmount, tempYear);
      println("----");
      filterPSystemsNum++;

      println("tempAmount "+tempAmount);
      println("maxParticles "+maxParticles);
      println("amountSum "+amountSum);
      println("tempAmount*maxParticles "+tempAmount*maxParticles);
      println("tempAmount*(maxParticles/amountSum) "+tempAmount*maxParticles/amountSum); 
      //println("PSystems " + i + " is " +PSystems[i]);
      j++;
    }
  }

  smooth();
}
int currentYear = 2000;
void draw() {

  blendMode(MULTIPLY);
  background(247, 245, 246, 1);
  fill(1, 1, 1, 1);

  tint(255, 1);
  image(pg, 0, 0);

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

    boolean overCountryOrigin = overCountry(int(PSystems[i].origin.x), int(PSystems[i].origin.y), 10);
    boolean overCountryDest = overCountry(int(PSystems[i].dest.x), int(PSystems[i].dest.y), 10);
    boolean isOver = (overCountryOrigin || overCountryDest);
    PSystems[i].run(isOver);
  }


  //  =======

  int allPCount = 0;
  for (int k = 0; k<filterPSystemsNum; k++) {

    Vector3D b=new Vector3D(PSystems[k].origin.x, PSystems[k].origin.y+abs(PSystems[k].origin.x-PSystems[k].dest.x)/2, 0);
    Vector3D c=new Vector3D(PSystems[k].dest.x, PSystems[k].dest.y+abs(PSystems[k].origin.x-PSystems[k].dest.x)/2, 0);


    boolean isTherePLimit = PSystems[k].PLimit != 0;

    if (isTherePLimit){

    boolean isThisAPlayingFrame = frameCount % int(sqrt(highestAmount/PSystems[k].PLimit)) == 0;
    boolean isThisTheRightYear = PSystems[k].year == currentYear;
    boolean areThereAnyMoreParticles = PSystems[k].PCount<PSystems[k].PLimit;

    if (isThisAPlayingFrame && isThisTheRightYear && areThereAnyMoreParticles) {
      for(int s = 0; s<emmitMultiplyer; s++){
      PSystems[k].addParticle();
      PSystems[k].PCount++;
      n++;
      }
    }
  }
    }



  // =========================================================== country labels




  for (int i = 0; i<destCountryArray.length; i++) {
    float tempX = countries.get(destCountryArray[i]).x;
    float tempY = countries.get(destCountryArray[i]).y;
    float tempAng = atan((tempY-height/2)/(tempX-width/2));
    float labelMargin;
    translate(tempX, tempY);
    textSize(8);

    rotate(tempAng);
    fill(0);
    if (tempX<width/2) {
      textAlign(RIGHT);
      labelMargin = -7;
    } else {
      textAlign(LEFT);
      labelMargin = 7;
    }

    if (overCountry(int(tempX), int(tempY), 9)==true) {
      fill(50, 100, 250);
      ellipse(0, 0, 5, 5);
      fill(0, 0, 255);
      text(countries.get(destCountryArray[i]).name, labelMargin, 2);
    } else {
      fill (200);
      fill(30, 100-sq(dist(mouseX, mouseY, tempX, tempY)));
      text(countries.get(destCountryArray[i]).name, labelMargin, 2);
    }

    if (countries.get(destCountryArray[i]).name == "Israel") {

      textAlign(RIGHT);
      textSize(10);
      fill(255);
      rotate(-tempAng);
      translate(-tempX, -tempY);
      rect(-6, -8, -textWidth("you are here")-7, 14);

      fill(200, 40, 40);
      text ("you are here", mouseX, mouseY);
      noFill();
      strokeWeight(0.5);
      stroke(255, 0, 0);

      bezier(mouseX, mouseY, (mouseX+tempX)/2+100, mouseY, tempX, (mouseY+tempY)/2, tempX, tempY);
      noStroke();
      translate(tempX, tempY);
      rotate(tempAng);
      ellipse(0, 0, 4, 4);
    }
    rotate(-tempAng);
    translate(-tempX, -tempY);
  }

  fill(0);
  text("Frame rate: "
    + int(frameRate), 10, 20);
  text("Each point: " + int(amountSum/maxParticles) +" people", 10, 30);
  text("number of refugees: " + n*int(amountSum/maxParticles), 10, 40);
  text("number of particles is: " +n, 10, 50);
  text("max particles is: " +maxParticles, 10, 60);
  textSize(80);
  blendMode(BLEND);
  fill(128, 130, 131);
  text(round(float(n)*float(amountSum)/float(maxParticles)), 10, 160);
  textSize(20);
  text("RECOGNIZED REFUGEES (" +yearFilterMin+ " - " + yearFilterMax+")", 10, 180);

  println("n = "+n);
  println("amountSum = "+amountSum);
  println("maxParticles = "+maxParticles);
}




// ==============================//  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; 
  float a = random(TWO_PI);
  Particle(Vector3D l, Vector3D m, int accum) {

    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();

    float rescale = 0.5;

    locHome.x = locHome.x+ cos(a)*random(0, sqrt(accum)*rescale);
    locHome.y = locHome.y+ sin(a)*random(0, sqrt(accum)*rescale);


    des.x = des.x+ cos(a)*random(0, sqrt(accum)*rescale);
    des.y = des.y+ sin(a)*random(0, sqrt(accum)*rescale);

    relativeSpeed = random(0.1, 0.02);


    r = random(0.9, 3);  // particle radius
    timer = 10000.0;   // particles lifespan


    b=new Vector3D(l.copy().x, l.copy().y+abs(l.copy().x-m.copy().x)/2, 0);
    c=new Vector3D(m.copy().x, m.copy().y+abs(l.copy().x-m.copy().x)/2, 0);

  }

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



  // Method to update location
  void update() {
    if (t>=1)
      return;

    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 = lerp(t, 1, particleSpeed*relativeSpeed/2);

  }

  void render(boolean isSelected) {
    ellipseMode(CENTER);
    noStroke();
    blendMode(BLEND);

    if (isSelected == false) {
      fill(128, 129, 129, 150);
    } 
    if (t==lerp(0, 1, particleSpeed*relativeSpeed/2)) {
      pg.beginDraw();
      pg.blendMode(BLEND);
      pg.fill(0, 0, 0);
      pg.noStroke();
      pg.ellipse(loc.x, loc.y, r, r);
      pg.endDraw();
    } else { 
      fill(4*255*t-3*255);
    }
    ellipse(loc.x, loc.y, r, r);

  }

  // Is the particle still useful?
  boolean dead() {
    if (t==1) {
      pg.beginDraw();
      pg.blendMode(BLEND);
      pg.fill(255, 255, 255, 255);
      pg.noStroke();
      pg.ellipse(loc.x, loc.y, r, r);
      pg.endDraw();
      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;
  int PLimit;
  int PCount;
  int year;

  float ratio = PLimit/highestAmount;

  //ParticleSystem( number of particles / frame, source, destination, frequency);
  ParticleSystem(int num, Vector3D v, Vector3D d, int f, int y) {
    particles = new ArrayList();              // Initialize the arraylist
    origin = v.copy();     // Store the origin point
    dest = d.copy();
    year = y;
    PLimit = f;


    //if (frameCount % (1/f) == 0){
    for (int i = 0; i < num; i++) {
      //  particles.add(new Particle(origin, dest));    // Add "num" amount of particles to the arraylist
    }
    //}
  }

  void run(boolean onMouseOverPSystem) {


    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = (Particle) particles.get(i);
      p.run(onMouseOverPSystem);
      if (p.dead()) {
        particles.remove(i);
        //PCount--;
        //n--;
      }

    }
  }

  void addParticle() {
    particles.add(new Particle(origin, dest, PCount));
  }



  // A method to test if the particle system still has particles
  boolean dead() {
    if (particles.isEmpty()) {
      return true;
    } else {
      return false;
    }
  }
}

//=================================================== Class Country

class Country {
  String name;
  float x, y, lon, lat, migrationIndex, incoming, outgoing;
  // more things here?

  Country(String name, float x, float y) {
    this.name = name;
    this.x = x;
    this.y = y;
  }
}

// ================================================ 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++) {

    if (tempCountryHolder.equals(destCountryArray[i]) == true) {

      return true; // it exists
    } //if
  } //for
  return false ; // not found
}//func


//============================================= onMouseOver Bolean Class


boolean overParticle(int x, int y, int diameter) {
  float disX = x - mouseX;
  float disY = y - mouseY;
  if (sqrt(sq(disX) + sq(disY)) < diameter/2 ) {
    return true;
  } else {
    return false;
  }
}

boolean overCountry(int x, int y, int diameter) {
  float disX = x - mouseX;
  float disY = y - mouseY;
  if (sqrt(sq(disX) + sq(disY)) < diameter/2 ) {
    return true;
  } else {
    return false;
  }
}

Answers

  • Answer ✓

    I figured out the problem

    Since particle location is determined by Lerp() function, it never reaches t=1. A fix was to change the if() to kill the particles from

    if(t==1)

    to

    if (dist(loc.x, loc.y, des.x, des.y)<3)

Sign In or Register to comment.