processing-android icon indicating copy to clipboard operation
processing-android copied to clipboard

Array Out of bounds error with PShape.setFill

Open Sebastiran opened this issue 3 years ago • 1 comments

I have two applications that use PShape.setFill. One of them runs completely fine, the other one crashes. Both of them use size(x, y, P2D) in the setup, so that's not the issue.

The crazy thing is that the code that crashes is the exact same. I use the same class, and the same way to create the object.

My code

class Particle 
{
  ParticleSystem system;
  
  PVector velocity;
  float lifetime;
  float lifespan;
  
  PShape part;
  float partSize;
  
  Particle(ParticleSystem system) 
  {
    this.system = system;
    
    partSize = 1;
    part = createShape();
    part.beginShape(QUAD);
    part.noStroke();
    part.fill(0, 0);
    part.normal(0, 0, 1);
    
    //These vertices will be modified later to decide the size
    //but we initialize them now to set the direction of each vertices
    part.vertex(-partSize/2, -partSize/2);//, 0, 0);
    part.vertex(+partSize/2, -partSize/2);//, partSize, 0);
    part.vertex(+partSize/2, +partSize/2);//, partSize, partSize);
    part.vertex(-partSize/2, +partSize/2);//, 0, partSize);
    part.endShape();
    
    lifetime = system.lifespan + system.lifespanD;
  }

  PShape getShape() {
    return part;
  }
  
  void rebirth(float x, float y) {
    float a = system.angle / 360 * random(TWO_PI);
    a += system.rotation;
    velocity = new PVector(cos(a), sin(a));
    velocity.mult(system.startSpeed + random(-system.startSpeedD, system.startSpeedD));
    lifespan = system.lifespan + random(-system.lifespanD, system.lifespanD);
    lifetime = 0;  
    part.setFill(system.startColor);
    part.resetMatrix();
    part.translate(x, y); 
    
    //set particle size
    partSize = random(system.minStartSize, system.maxStartSize);
    for(int i = 0; i < part.getVertexCount(); i++)
    {
      PVector v = part.getVertex(i);
      v.normalize().mult(partSize/2);
      part.setVertex(i, v.x, v.y);
    }
  }
  
  boolean isDead() {
    if (lifetime > lifespan) {
      return true;
    } else {
      return false;
    } 
  }
  
  public void update() {
    lifetime += 0.03f;
    
    if(isDead())
    {
      part.setFill(color(0, 0));
      return;
    }
    
    if(system.useGravity)
      velocity.add(system.gravity.copy().mult(0.03f));
    velocity.mult(1 - system.drag * 0.03f);
    
    float lifePercent = lifetime / lifespan;
    part.setFill(lerpColor(system.startColor, system.endColor, lifePercent));
    part.translate(velocity.x, velocity.y);
    
    for(int i = 0; i < part.getVertexCount(); i++)
    {
      float scale = lerp(partSize, system.endSize, lifePercent);
      PVector v = part.getVertex(i);
      v.normalize().mult(scale/2);
      part.setVertex(i, v.x, v.y);
    }
  }
}

It crashes when I call Update with the error: java.lang.ArrayIndexOutOfBoundsException: length=2; index=6 at processing.core.PShape.setFill(PShape.java:2393)

The ParticleSystem class:

class ParticleSystem extends GameObject
{
  public boolean loop = false;
  public float loopTime = 5f;
  public float spawnRate = 10f;
  
  public ArrayList<Burst> bursts = new ArrayList<Burst>();
  
  //Spawning
  public float lifespan = 1f;
  public float lifespanD = 0f;
  
  //Shape
  public float angle = 360f;
  
  //Physics
  public float startSpeed = 10f;
  public float startSpeedD = 2f;
  public float drag = 0f;
  public boolean useGravity = false;
  public PVector gravity = new PVector(0, 9.81f);
  
  //Visuals
  public color startColor = color(255);
  public color endColor = color(255, 0);
  public int minStartSize = 10;
  public int maxStartSize = 10;
  public int endSize = 0;
 
  private ArrayList<Particle> particles;
  private PShape particleShape;
  private boolean isPlaying = false;
  
  ParticleSystem(PVector position, float rotation, int n) 
  {
    this.position = position;
    this.rotation = rotation;
    
    particles = new ArrayList<Particle>();
    particleShape = createShape(PShape.GROUP);

    for (int i = 0; i < n; i++) {
      Particle p = new Particle(this);
      particles.add(p);
      particleShape.addChild(p.getShape());
    }
  }

  private float currentLoopTime = 0;
  private float toSpawn = 0;
  public void Update() {
    if (!isPlaying)
      return;
    
    currentLoopTime += Time.deltaTime;
    if(currentLoopTime >= loopTime)
    {
      if(loop)
      {
        currentLoopTime = 0;
        for (Burst b : bursts)
        {
          b.done = false;
        }
      }
      else if(currentLoopTime >= loopTime + lifespan + lifespanD)
        FinishedPlaying();
    }
    if(currentLoopTime < loopTime)
    {
      toSpawn += spawnRate * Time.deltaTime;
      
      for (Burst b : bursts)
      {
        if(!b.done && b.time <= currentLoopTime)
        {
          toSpawn += b.amount;
          b.done = true;
        }
      }
    }
    
    for (Particle p : particles) {
      if (toSpawn >= 1 && p.isDead()) 
      {
        p.rebirth(position.x, position.y);
        toSpawn -= 1;
      }
      
      if(!p.isDead())
        p.update();
    }
  }

  public void Play()
  {
    if(isPlaying)
      return;
      
    isPlaying = true;
  }
  
  public void Stop()
  {
    if(!isPlaying)
      return;
      
    isPlaying = false;
  }
  
  private void FinishedPlaying()
  {
    Stop();
    hierarchy.remove(this);
  }

  public void Draw()
  {
    if(!isPlaying)
      return;
    
    shape(particleShape);
  }
}

public class Burst
{
  public int amount;
  public float time;
  public boolean done = false;
  
  public Burst(int amount, float time)
  {
    this.amount = amount;
    this.time = time;
  }
}

Here is a code snippet of the one that crashes, it's the exact same as the one that does not, except for that it also does some other things like setup the physics.

ParticleSystem ps;
void setup()
{
  size(displayWidth, displayHeight, P2D);
  
  accel = new AccelerometerManager(this);
  Utils.screenWidth = width;
  Utils.screenHeight = height;
  
  physics = new PhysicsManager();
  init = new Initialization();
  upgradeFactory = new UpgradeFactory();
  
  ps = new ParticleSystem(new PVector(width/2, height/2), 0, 200);
  ps.Play();
}

void draw()
{
  Time.Frame(millis());
  physics.Loop();
  clear();  
  background (0);

  for(int i = hierarchy.size() - 1; i >= 0; i--)
  {
    hierarchy.get(i).Update();
  }
  for(int i = hierarchy.size() - 1; i >= 0; i--)
  {
    hierarchy.get(i).InternalUpdate();
  }
  for(int i = hierarchy.size() - 1; i >= 0; i--)
  {
    hierarchy.get(i).Draw();
  }
  
  ps.Draw();
  ps.Update();
}

Sebastiran avatar Mar 07 '21 22:03 Sebastiran

I have two applications that use PShape.setFill. One of them runs completely fine, the other one crashes. Both of them use size(x, y, P2D) in the setup, so that's not the issue.

The crazy thing is that the code that crashes is the exact same. I use the same class, and the same way to create the object.

My code

class Particle 
{
  ParticleSystem system;
  
  PVector velocity;
  float lifetime;
  float lifespan;
  
  PShape part;
  float partSize;
  
  Particle(ParticleSystem system) 
  {
    this.system = system;
    
    partSize = 1;
    part = createShape();
    part.beginShape(QUAD);
    part.noStroke();
    part.fill(0, 0);
    part.normal(0, 0, 1);
    
    //These vertices will be modified later to decide the size
    //but we initialize them now to set the direction of each vertices
    part.vertex(-partSize/2, -partSize/2);//, 0, 0);
    part.vertex(+partSize/2, -partSize/2);//, partSize, 0);
    part.vertex(+partSize/2, +partSize/2);//, partSize, partSize);
    part.vertex(-partSize/2, +partSize/2);//, 0, partSize);
    part.endShape();
    
    lifetime = system.lifespan + system.lifespanD;
  }

  PShape getShape() {
    return part;
  }
  
  void rebirth(float x, float y) {
    float a = system.angle / 360 * random(TWO_PI);
    a += system.rotation;
    velocity = new PVector(cos(a), sin(a));
    velocity.mult(system.startSpeed + random(-system.startSpeedD, system.startSpeedD));
    lifespan = system.lifespan + random(-system.lifespanD, system.lifespanD);
    lifetime = 0;  
    part.setFill(system.startColor);
    part.resetMatrix();
    part.translate(x, y); 
    
    //set particle size
    partSize = random(system.minStartSize, system.maxStartSize);
    for(int i = 0; i < part.getVertexCount(); i++)
    {
      PVector v = part.getVertex(i);
      v.normalize().mult(partSize/2);
      part.setVertex(i, v.x, v.y);
    }
  }
  
  boolean isDead() {
    if (lifetime > lifespan) {
      return true;
    } else {
      return false;
    } 
  }
  
  public void update() {
    lifetime += 0.03f;
    
    if(isDead())
    {
      part.setFill(color(0, 0));
      return;
    }
    
    if(system.useGravity)
      velocity.add(system.gravity.copy().mult(0.03f));
    velocity.mult(1 - system.drag * 0.03f);
    
    float lifePercent = lifetime / lifespan;
    part.setFill(lerpColor(system.startColor, system.endColor, lifePercent));
    part.translate(velocity.x, velocity.y);
    
    for(int i = 0; i < part.getVertexCount(); i++)
    {
      float scale = lerp(partSize, system.endSize, lifePercent);
      PVector v = part.getVertex(i);
      v.normalize().mult(scale/2);
      part.setVertex(i, v.x, v.y);
    }
  }
}

It crashes when I call Update with the error: java.lang.ArrayIndexOutOfBoundsException: length=2; index=6 at processing.core.PShape.setFill(PShape.java:2393)

The ParticleSystem class:

class ParticleSystem extends GameObject
{
  public boolean loop = false;
  public float loopTime = 5f;
  public float spawnRate = 10f;
  
  public ArrayList<Burst> bursts = new ArrayList<Burst>();
  
  //Spawning
  public float lifespan = 1f;
  public float lifespanD = 0f;
  
  //Shape
  public float angle = 360f;
  
  //Physics
  public float startSpeed = 10f;
  public float startSpeedD = 2f;
  public float drag = 0f;
  public boolean useGravity = false;
  public PVector gravity = new PVector(0, 9.81f);
  
  //Visuals
  public color startColor = color(255);
  public color endColor = color(255, 0);
  public int minStartSize = 10;
  public int maxStartSize = 10;
  public int endSize = 0;
 
  private ArrayList<Particle> particles;
  private PShape particleShape;
  private boolean isPlaying = false;
  
  ParticleSystem(PVector position, float rotation, int n) 
  {
    this.position = position;
    this.rotation = rotation;
    
    particles = new ArrayList<Particle>();
    particleShape = createShape(PShape.GROUP);

    for (int i = 0; i < n; i++) {
      Particle p = new Particle(this);
      particles.add(p);
      particleShape.addChild(p.getShape());
    }
  }

  private float currentLoopTime = 0;
  private float toSpawn = 0;
  public void Update() {
    if (!isPlaying)
      return;
    
    currentLoopTime += Time.deltaTime;
    if(currentLoopTime >= loopTime)
    {
      if(loop)
      {
        currentLoopTime = 0;
        for (Burst b : bursts)
        {
          b.done = false;
        }
      }
      else if(currentLoopTime >= loopTime + lifespan + lifespanD)
        FinishedPlaying();
    }
    if(currentLoopTime < loopTime)
    {
      toSpawn += spawnRate * Time.deltaTime;
      
      for (Burst b : bursts)
      {
        if(!b.done && b.time <= currentLoopTime)
        {
          toSpawn += b.amount;
          b.done = true;
        }
      }
    }
    
    for (Particle p : particles) {
      if (toSpawn >= 1 && p.isDead()) 
      {
        p.rebirth(position.x, position.y);
        toSpawn -= 1;
      }
      
      if(!p.isDead())
        p.update();
    }
  }

  public void Play()
  {
    if(isPlaying)
      return;
      
    isPlaying = true;
  }
  
  public void Stop()
  {
    if(!isPlaying)
      return;
      
    isPlaying = false;
  }
  
  private void FinishedPlaying()
  {
    Stop();
    hierarchy.remove(this);
  }

  public void Draw()
  {
    if(!isPlaying)
      return;
    
    shape(particleShape);
  }
}

public class Burst
{
  public int amount;
  public float time;
  public boolean done = false;
  
  public Burst(int amount, float time)
  {
    this.amount = amount;
    this.time = time;
  }
}

Here is a code snippet of the one that crashes, it's the exact same as the one that does not, except for that it also does some other things like setup the physics.

ParticleSystem ps;
void setup()
{
  size(displayWidth, displayHeight, P2D);
  
  accel = new AccelerometerManager(this);
  Utils.screenWidth = width;
  Utils.screenHeight = height;
  
  physics = new PhysicsManager();
  init = new Initialization();
  upgradeFactory = new UpgradeFactory();
  
  ps = new ParticleSystem(new PVector(width/2, height/2), 0, 200);
  ps.Play();
}

void draw()
{
  Time.Frame(millis());
  physics.Loop();
  clear();  
  background (0);

  for(int i = hierarchy.size() - 1; i >= 0; i--)
  {
    hierarchy.get(i).Update();
  }
  for(int i = hierarchy.size() - 1; i >= 0; i--)
  {
    hierarchy.get(i).InternalUpdate();
  }
  for(int i = hierarchy.size() - 1; i >= 0; i--)
  {
    hierarchy.get(i).Draw();
  }
  
  ps.Draw();
  ps.Update();
}

Hi @Sebastiran can you please provide the exact logs of the errors you are getting ?

That would be helpful to check exactly where the error is happening in the codebase.

Thanks ! happy coding

ranaaditya avatar Mar 29 '21 02:03 ranaaditya