IndexOutOfBoundsException for ArrayList

Hi everyone,

I'm trying to replicate http://loop.monolithinteractive.com/ as an exercise.

I have accomplished a few things but I'm stuck on the method that checks circles collisions. I get an IndexOutOfBoundsException in Class ExpandingCircle > void checkCirclesCollisions.

You can find the whole code here: https://dl.dropboxusercontent.com/u/1835810/monolithloop.zip

I can't get around it, please help.

Thank You :)

Answers

  • edited November 2013 Answer ✓

    Hello, I think you must change your array familly (line 10), to compare easily the tartget with your list circles (line96) I make a litlle change in your code, I don't find the solution but sure the solution is on this way !

    // LIBRARIES
    import beads.*;
    
    // CLASSES
    AudioContext ac;
    MalletAudio mau;
    
    // GLOBAL VARIABLES
    // circle arraylist to draw n circles
    ArrayList<ExpandingCircle> circles = new ArrayList<ExpandingCircle>();
    // canvas size + framerate == bpm
    int sks = 500, bpm = 40;
    // circle radius
    float radius;
    // mallet source var to determine path to file.wav
    String source;
    // boolean to play sample
    boolean playSample = false;
    
    // sketch setup
    void setup() {
      size(sks, sks);
      frameRate(bpm);
      colorMode(RGB);
      smooth();
      noFill();
    }
    
    // main
    void draw() {
      // nice green bg
      background(#A9F5A9);
      // iterate circle creation
      for (int i = 0; i < circles.size(); i++) {
        ExpandingCircle ec = (ExpandingCircle) circles.get(i);
        // TODO check circles collisions (here or in circles.update(); ?)
        ec.checkCirclesCollisions(i);
        ec.update();
        ec.display();
      }
    
      // AUDIO to fix sample play. it starts at launch even though boolean is false
      //ac = new AudioContext();
      //source=sketchPath("") + "audio/MalletAudio.mp3";
      //mau = new MalletAudio(source, playSample);
      // TODO 12 pitches based upon radius dimensions ; triggered on circles collisions
      //mau.pitch.setValue((float)map(radius, 0, sks, 1, 12));
    }
    
    void mousePressed() {
      // add new circle
      if (mouseButton == LEFT) {
        circles.add(new ExpandingCircle(mouseX, mouseY, radius));
      }
      // remove last circle
      if (mouseButton == RIGHT) {
        if (circles.size() > 0) {
          circles.remove(circles.size()-1);
        }
      }
    }
    
    void keyPressed() {
      // clear all the circles
      if (key == ' ') { 
        circles.clear();
      }
    }
    
    // expandingcircle class
    class ExpandingCircle {
      // x, y position == mousex, mousey
      int x, y;
      // circle radius + var for check circles collisisions
      float radius, vx = 0, vy = 0, spring = 0.05;
      int numCircles, id;
      // increment / decrement radius boolean
      boolean inc = true, dec = false;
      // pvector to calculate circle centroid
      PVector position;
    
    
      // constructor
      ExpandingCircle(int x_, int y_, float radius_) {
        position = new PVector(x_, y_);
        // x updates 
        this.x = x_;
        // y updates
        this.y = y_;
        // radius updates
        this.radius = radius_;
      }
    
      // TODO
      // check circle to circles collisions
      void checkCirclesCollisions(int ID) {
        for (int i = 0; i < circles.size(); i++) {
          //println("OTHERS i: " + i);
          float dx = circles.get(i).x - circles.get(ID).x;
          float dy = circles.get(i ).y - circles.get(ID).y;
          float distance = sqrt(dx*dx + dy*dy);
          float minDist = circles.get(i).radius + circles.get(ID).radius;
          if (distance < minDist) { 
            float angle = atan2(dy, dx);
            float targetX = x + cos(angle) * minDist;
            float targetY = y + sin(angle) * minDist;
            float ax = (targetX - circles.get(i).x) * spring;
            float ay = (targetY - circles.get(i).y) * spring;
            vx -= ax;
            vy -= ay;
            circles.get(i).vx += ax;
            circles.get(i).vy += ay;
          }
        }
      }
    
      // check sketch boundaries collisions
      void checkBoundaryCollision() {
        if ((position.x - radius/2) < 0) {
          inc = false;
          dec = true;
          // set boolean true to play sample
          //playSample = true;
        } 
        else if ((position.x + radius/2) > width) {
          inc = false;
          dec = true;
          // set boolean true to play sample
          //playSample = true;
        } 
        else if ((position.y - radius/2) < 0) {
          inc = false;
          dec = true;
          // set boolean true to play sample
          //playSample = true;
        } 
        else if ((position.y + radius/2) > height) {
          inc = false;
          dec = true;
          // set boolean true to play sample
          //playSample = true;
        }
        /*else {
          playSample = false;
        }*/
      }
    
      // increase radius on update == growing circle
      void update() {
        // increment to borders 
        if (inc == true && dec == false) {
          //checkCirclesCollisions();
          checkBoundaryCollision();
          radius++;
        }
    
        // decrement to 0 centroid == shrinking circle
        if (inc == false && dec == true) {
          //checkCirclesCollisions();
          checkBoundaryCollision();
          radius--;
          // if we reach the minimum radius, let's grow again.
          if (radius <= 1) {
            inc = true;
            dec = false;
          }
        }
      }
    
      // nice bluette circle
      void display() {
        stroke(#642EFE);
        ellipse(x, y, radius, radius);
      }
    }
    
    class MalletAudio {
    
      SamplePlayer sp;
      Glide pitch;
      Gain gain;
    
      // TODO sample triggered on circles collisions
      MalletAudio(String source, boolean playSample_) {
        boolean startSP = playSample_;
        // test load sample
        try {
          sp = new SamplePlayer(ac, new Sample(source));
        }
        catch (Exception e) {
          println("errore");
          e.printStackTrace();
          exit();
        }  
        // stop sampleplayer upon end of sample
        sp.setKillOnEnd(true);
        // set the pitch (rate) to play the sample
        pitch = new Glide(ac, 1, 20);
        // start loop
        sp.setToLoopStart();
        // oneshot 
        sp.setLoopType(SamplePlayer.LoopType.NO_LOOP_FORWARDS);
        // set the pitch value
        sp.setRate(pitch);
        // constant gain of 0.8
        gain = new Gain(ac, 2, 0.8);
        // add the above chain to gain
        gain.addInput(sp);
        // add the gain chain to ac.out
        ac.out.addInput(gain);
        // start services
        // TODO sample triggered on circles collisions
        if (startSP == true) {
          sp.start();
        }
        ac.start();
      }
    }
    
  • Hey Stanlepunk,

    thanks for fixing that error.

    Honestly I've copied the code for checkCriclesCollisions from Examples > Topics > Motion > BouncyBubbles and I thought that adapting it to my sketch would work. I was wrong.

    Any idea on how to check circles collisions? both external and for circles within circles?

    Thank You

  • edited November 2013

    You must work around this code, specificly the void collide(), if you don't manage next week I have more time to look your code.

    int numBalls = 12;
    float spring = 0.05;
    float gravity = 0.03;
    float friction = -0.9;
    Ball[] balls = new Ball[numBalls];
    
    void setup() {
      size(640, 200);
      noStroke();
      smooth();
      for (int i = 0; i < numBalls; i++) {
        balls[i] = new Ball(random(width), random(height), random(20, 40), i, balls);
      }
    }
    
    void draw() {
      background(0);
      for (int i = 0; i < numBalls; i++) {
        balls[i].collide();
        balls[i].move();
        balls[i].display();  
      }
    }
    
    class Ball {
      float x, y;
      float diameter;
      float vx = 0;
      float vy = 0;
      int id;
      Ball[] others;
    
      Ball(float xin, float yin, float din, int idin, Ball[] oin) {
        x = xin;
        y = yin;
        diameter = din;
        id = idin;
        others = oin;
      } 
    
      void collide() {
        for (int i = id + 1; i < numBalls; i++) {
          float dx = others[i].x - x;
          float dy = others[i].y - y;
          float distance = sqrt(dx*dx + dy*dy);
          float minDist = others[i].diameter/2 + diameter/2;
          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;
          }
        }   
      }
    
      void move() {
        vy += gravity;
        x += vx;
        y += vy;
        if (x + diameter/2 > width) {
          x = width - diameter/2;
          vx *= friction; 
        } else if (x - diameter/2 < 0) {
          x = diameter/2;
          vx *= friction;
        }
        if (y + diameter/2 > height) {
          y = height - diameter/2;
          vy *= friction; 
        } else if (y - diameter/2 < 0) {
          y = diameter/2;
          vy *= friction;
        }
      }
    
      void display() {
        fill(255, 204);
        ellipse(x, y, diameter, diameter);
      }
    }
    
  • Hey Stanlepunk,

    I'll try it again.

    Thank You :)

  • Hi Stalepunk,

    I've been debugging and I get the right data for .x .y. and .radius with the arraylist, but I cannot get it to do what I need.

    halp?

    please and thank you

  • Note about the code of stanlepunk (not 'stale' yet! ) :-)): a Ball should not know about the other balls! It is a bit wasteful for each ball to have a list of all the balls.

    Better make them independent and either manage the list of balls at the global level, or wrap it in another class (BallManager for example).

  • Hi PhiLho,

    jfyi: that code is from the Examples > Topics > Motion > BouncyBubbles.

    yet I'd love to solve Class ExpandingCircle > void checkCirclesCollisions in mine :P

  • Some examples in Processing have been integrated from 3rd party sources, and are not perfect in terms of best coding practice, despite their cleverness and nice and correct output... :-)

    Sometime I criticize working code because:

    • I try to show better practices, to improve the programming level of learners here; ;-)
    • Sometime, it can lead to the solution, by clarifying concepts, simplifying code, etc.

    I see other things that can be improved, like not checking if a circle hits itself... And, because of the problem I criticized above, all the collisions are checked twice, not good for performance / efficiency: there is a loop on all balls to check for collision, and each ball loops again on all balls, to see if it hits another ball.

    Better make a method isColliding(ExpandingBall otherBall) to see if a ball is colliding another, and use that in an external loop.

    (Note: I criticize only the code I see here...)

    Note also that you can write ExpandingCircle ec = circles.get(i); (no need for (ExpandingCircle) cast) because the ArrayList has a type <ExpandingCircle>.

    Alas, just looking at the code, I don't see why you get an array out of bounds exception, I should run it to be sure, but I don't have the time to remove all the extra code (audio) which might not run on my computer.

    Use println() with the size of the array list and the index about to be used just before the line throwing the error, to see what is going on.

  • edited December 2013

    I've got a very similar example of colliding balls: L-)
    http://studio.processingtogether.com/sp/pad/export/ro.91xkuWl51J$Vp/latest

    But it doesn't use best practices like @PhiLho said! I've just tweaked it a lil' and published it online! 8-X

  • Hi PhiLho,

    thank you for your precious contributions, I understand and agree with you on everything. I just thought that examples would be good code just because they are in there, that's all.

    Leaving the balls example out for good, the out of bound was in my code (first post) but Stalepunk solved it with his correction in his first reply.

    Going back to my actual problem, I still don't manage to check collisions between expanding circles. My idea (other than the one I took from Balls) would be to get circles.x, circles.y and circles.radius and have math calculate if the collide (both internally and externally). What do you think?

    Thank you and sorry for the lag.

Sign In or Register to comment.