> show canvas only <


/* built with Studio Sketchpad: 
 *   http://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   http://studio.sketchpad.cc/sp/pad/view/ro.fUr8IvmG6tF/rev.45
 * 
 * authors: 
 *   GoToLoop

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



/**
 * Grid Walkers (v2.2)
 * by  HowardCornwell (2014/Aug)
 * mod GoToLoop
 *
 * forum.processing.org/two/discussion/6838/1d-array-affecting-2d-array
 * studio.processingtogether.com/sp/pad/export/ro.91FXxn8BG2fPL/latest
 */

static final int GRID = 20, DOTS = 4, FPS = 30;
final Box[][] grid = new Box[GRID][GRID];
final Point[] points = new Point[DOTS];

PImage bg;

void setup() {
  size(800, 600, JAVA2D);
  smooth(4);
  frameRate(FPS);

  colorMode(HSB, 360, 100, 100);  
  rectMode(CORNER);
  ellipseMode(CENTER);

  strokeWeight(Point.WEIGHT);
  stroke(Point.STROKE);

  final int wg = width/GRID, hg = height/GRID;
  BoxData.dx = wg - BoxData.GAP;
  BoxData.dy = hg - BoxData.GAP;

  for (int i = 0; i != DOTS; points[i++] = new Point());

  for (int row = 0; row != GRID; ++row)  for (int col = 0; col != GRID;
    grid[row][col] = new Box(col++*wg, row*hg));

  bg = createGrid();
}

void draw() {
  set(0, 0, bg);

  for (Point p: points) {
  NextPoint:
    for (Box[] boxes: grid)  for (Box b: boxes)  if (p.isInside(b)) {
      b.fillBox(p.hueColor);
      break NextPoint;
    }

    p.script();
  }
}

PImage createGrid() {
  final PGraphics pg = createGraphics(width, height, JAVA2D);

  pg.beginDraw();
  pg.background(0);

  pg.smooth(4);
  pg.rectMode(CORNER);

  pg.strokeWeight(BoxData.WEIGHT);
  pg.stroke(BoxData.STROKE);
  pg.noFill();

  for (Box[] boxes: grid)  for (Box b: boxes)  b.drawBox(pg);

  pg.endDraw();
  return pg.get();
}

static abstract class BoxData {
  static final int STROKE = -1, WEIGHT = 1, GAP = 5;
  static int dx, dy;
}

final class Box extends BoxData {
  final short x, y;

  Box(int xx, int yy) {
    x = (short) xx;
    y = (short) yy;
  }

  void fillBox(color c) {
    fill(c, 100, 100);
    rect(x, y, dx, dy);
  }

  void drawBox(PGraphics pg) {
    pg.rect(x, y, dx, dy);
  }
}

final class Point {
  static final int STROKE = 0, WEIGHT = 1;
  static final int DIAM = 8, SPD = 20, HUE_STEP = 2;

  float x, y, xoff, yoff, inc;
  color hueColor;

  Point() {
    reset();
  }

  void reset() {
    noiseSeed((int) random(MAX_INT>>6)*(second()+1));

    x = width/2.0;
    y = height/2.0;

    xoff = random(-SPD, SPD);
    yoff = random(-SPD, SPD);

    inc = random(-.2, .1);

    hueColor = (int) random(360);
  }

  void script() {
    move();
    cycleHue();
    display();
  }

  void move() {
    x += SPD*(noise(xoff += inc) - .5);
    y += SPD*(noise(yoff += inc) - .5);

    if (x > width | x < 0 | y > height | y < 0)  reset();
  }

  void cycleHue() {
    hueColor = (hueColor + HUE_STEP) % 360;
  }

  void display() {
    fill(hueColor, 100, 100);
    ellipse(x, y, DIAM, DIAM);
  }

  boolean isInside(Box b) {
    return x > b.x & x < b.x+Box.dx & y > b.y & y < b.y+Box.dy;
  }
}