> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.$$I5vrwRmQ1/rev.12
 * 
 * authors: 
 *   GoToLoop

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/** 
 * Clickable Spawning Balls (v3.05)
 * by  drov.fr  (2013/Jun)
 * mod GoToLoop (2013/Dec)
 * 
 * forum.processing.org/one/topic/nullpointerexeption-with-an-arraylist.html
 * forum.processing.org/two/discussion/2171/effect-in-processing
 * 
 * studio.processingtogether.com/sp/pad/export/ro.9oyKfI9kOIa77/latest
 */

import java.util.List; // actually, it is already imported, but not "activated"!

// Constant field variables of the top class, the sketch itself!

// An ArrayList which only stores instances of class Balle, and initial size = 128:
//final static List<Balle> balles = new ArrayList(0200); // Java
final static List<Balle> balles = new ArrayList(); // JS

final static color  BG = 0;
final static String ENGINE = JAVA2D;  // JAVA2D, P2D, P3D, OPENGL

void setup() {
  size(600, 400, ENGINE); // old P2D & P3D don't exist in Processing 2.x.x!
  frameRate(60);
  ellipseMode(CENTER);

  fill(Balle.COLOUR); // directly access a static field w/o instantiate it 1st!
}

void draw() {
  background(BG);

  // This is called enhanced or for-each or even for-in loop.
  // It iterates over all of the elements stored in the ArrayList
  // which is referenced by variable balles.
  // Since that contains only instances of class Balle
  // iterator b is declared as of type Balle to receive an element from it:
  for (Balle b: balles)    b.script();

  // Changes app window top-bar title:
  //frame.setTitle( "# Balls: " + balles.size() ); // Java only
}

void mousePressed() {
  // If it's NOT a middle-click, add a new instance of Balle:
  if (mouseButton != CENTER)   balles.add( new Balle() );

  // Otherwise, also checks whether it has at least 1 instance stored 
  // before removing oldest one to avoid an exception:
  //else if (balles.size() != 0)   balles.remove(0);

  // A faster alternative -> removes latest added Balle instead:
  else {
    final int len = balles.size();
    if (len != 0)   balles.remove(len - 1);
  }
}

final class Balle {
  // Fields of inner class Balle (instantiable):
  short x, y;
  byte  spX, spY;

  // Static constant fields of inner class Balle (non-instantiable):
  final static byte  DIM = 20, HALF_DIM = DIM >> 1; // >> 1 is / 2
  final static byte  MAX_SPD = 5;
  final static color COLOUR  = #FFFF00; // yellow

  Balle() { // constructor of inner class Balle
    x = (short) mouseX;
    y = (short) mouseY;

    // Pick a random speed and pick a direction for it:
    spX = (byte) random(1, MAX_SPD + 1);
    if (random(1) < .5)   spX *= -1;

    // Ternary operator ?: which demands 3 operands.
    // If random(1) < .5, multiplies by -1, otherwise by 1:
    spY = (byte) ( random(1, MAX_SPD + 1) * (random(1) < .5 ? -1:1) );
  }

  void bump() {
    // Adds spX/spY to x/y, then multiplies
    // spX/spY by -1 in case x/y is off canvas:
    if ( (x += spX) < HALF_DIM | x > width  - HALF_DIM )  spX *= -1;

    // Alternative way w/ ?: ternary operator:
    y += spY *= y < HALF_DIM | y > height - HALF_DIM ? -1:1;
  }

  void show() {
    ellipse(x, y, DIM, DIM);
  }

  void script() {
    bump();
    show();
  }
}