import java.applet.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 

public class blobsInterlace extends BApplet {
// title:  metablobs bilinear interlace demo
// author: toxi@toxi.co.uk
// date:   02/06/03 (last mod: 13/08/03)

static final int MAXBLOBS = 10;

// field amplifier constant
static final int FIELD_AMP = 40;

// field boundary at approx. 70% (where 255 = 100%)
static final int FIELD_THRESHOLD = 176;

// define base colour components
static final int RED   = (int)(1.2f*255);
static final int GREEN = (int)(0.96f*255);
static final int BLUE  = (int)(0.25f*255);

MBlob[] blobs;

int      numBlobs;
boolean  isInitialised=false;
boolean  isFirstRun=true;
boolean  oddFrame=false;

// define various interlace factors
// the numbers correspond to 1/n width of a scanline
int[]    vInterlaceFactors={1,2,4,5};

// start with interlacing disabled
int      vInterlaceID=0;


void setup(){
  size(640,480);
  noBackground();
  // start with 1 ball
  numBlobs=1;
  initBlobs();
}

void mousePressed(){
  isInitialised=false;
  if (numBlobs<MAXBLOBS) numBlobs++; else numBlobs=1;
}

// change interlacing
void keyPressed() {
  if (key>='1' && key<='4') {
    vInterlaceID=(int)key-49;
    isFirstRun=true; // force backround to be redrawn
  }
}

void loop(){
  // draw gradient background if required
  if (isFirstRun) {
    beginShape(QUADS);
    fill(100,150,190);
    vertex(0,0); vertex(width,0);
    fill(0,0,51);
    vertex(width,height); vertex(0,height);
    endShape();
    isFirstRun=false;
  }

  // re-initialise balls if needed (i.e. after mouseclick)
  if (!isInitialised) initBlobs();

  // update ball positions
  for (int i=0; i<blobs.length; i++) {
    if(i==blobs.length-1){
      blobs[i].x = mouseX;
      blobs[i].y = mouseY;
    }
    blobs[i].update();
  }

  // compute interlace offset per scanline
  int currInterlace=width/vInterlaceFactors[vInterlaceID];

  // initial offset in the pixel buffer is being toggled every frame
  int offset = oddFrame ? 1+currInterlace : 0;
  oddFrame=!oddFrame;
  
  float potential;
  
  // only draw every other pixel column and row
  // however we only move a fraction of a full scanline width at the end of each row
  // this creates the distorted interlaced effect
  
  // forgive the java syntax, just too lazy to check if we're still
  // in the defined pixel buffer (which changes with every interlace setting)
  try {
    for(int y=0; y<(height*2); y+=2){
      for(int x=0; x<width; x+=2){
        potential=0;
        for(int i=0; i<blobs.length; i++){
          int distsq = (int)(sq(blobs[i].x-x)+sq(blobs[i].y-y));
          if(distsq>0) potential += blobs[i].strength*blobs[i].radius/distsq;
          else {
            potential=255;
            break;
          }
        }
        // use factored base colour if field strength below threshold
        if(potential < FIELD_THRESHOLD) {
          pixels[offset] =  ((int)(potential*RED)<<8)&0xff0000 | (int)(potential*GREEN)&0xff00 | (int)(potential*BLUE)>>8;
        }
        else
        {
          // if inside boundary, slowly increase brightness, especially on the boundary edge
          int tB=(((int)(0xff-min(potential,0xff)))>>1) + 2;
          pixels[offset] = min(((pixels[offset]&0xff0000)>>16)+tB,0xff)<<16 |
                           min(((pixels[offset]&0x00ff00)>>8) +tB,0xff)<<8 |
                           min( (pixels[offset]&0x0000ff)     +tB,0xff);
        }
        offset+=2; // skip 1 pixel horizontally
      }
      offset+=currInterlace; // skip (1/n scanline) vertically
    }
  }
  catch(Exception e) {
      // whenever we try to access pixels outside the screen, we will end up here
      // (because java throws an ArrayOutOfBounds exception)
      
      // ...we just ignore it ;)
  }
}

// re-initialise and add new blobs

public void initBlobs() {
  MBlob[] newblobs = new MBlob[numBlobs];
  for(int i=0; i<numBlobs-1; i++) newblobs[i] = blobs[i];
  newblobs[numBlobs-1]=new MBlob();
  blobs=newblobs;
  isInitialised=true;
}

// actual blob structure
class MBlob{
  int x;
  int y;
  int radius;
  int strength, maxStrength;
  float count;
  float speed;
  
  public MBlob() {
    x =            (int)random(width);
    y =            (int)random(height);
    radius =       (int)random(100,200)*FIELD_AMP;
    maxStrength =  (int)random(200,300);
    count =             random(TWO_PI);
    speed =             random(0.02f,0.18f);
  }
  void update() {
    strength = (int)(sin(count+=speed)*(maxStrength-10))+maxStrength;
  }
}


}