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 udk_final_scholz_11 extends BApplet {
// Wormy
// 2003 by Erik Scholz

// a worm looks for its own way makes a trace of 50 points fading away with time
// mousex controls the sinusmodulation of the worm
// the worm also turns around when it is near the border of screen
Vec2D p1,p2,v,w;
float cnt, posx, posy, circx, circy, rand, distx, disty, a, inc, b, cnta, speed;
float [] coox;
float [] cooy;
float [] cooxx;
float [] cooyy;

float [] tracex;
float [] tracey;

boolean change;
int m, lf;
float fract,fractD;


void setup ()
{
  size(400,400);
  background(0xffA9CE93);
  framerate(0);
  speed = 50;
  change=false;
  p1=new Vec2D(200,200);
  p2=new Vec2D(180,200);
  v=new Vec2D(0,0);
  w=new Vec2D(0,0);
  cnt=1;

  ellipseMode(CENTER_DIAMETER);
  
  tracex = new float [50];
  tracey = new float [50];
  for (int c = 0; c < 50; c++)
  {
    tracex[c]=-100;
    tracey[c]=-100;
  }
  coox = new float[6];
  cooy = new float[6];
  cooxx = new float[6];
  cooyy = new float[6];
  
  for (int c = 0; c < 6; c++)
  {
    coox[c]=200;
    cooy[c]=200;
    cooxx[c]=200;
    cooyy[c]=200;
  }
  circx = 20;
  circy = 20;
  distx=random(-10, 10);
  disty=random(-10, 10);

  posx=100;
  posy=100;

  fract=0;
  fractD=0.005f;
 
  a = 0.0f; 
  inc = TWO_PI/25.0f;
  
  lf = 1;
  cnta=0;
  int m = 1;
}

void loop()
{
  cnta++;
  noStroke();
  for (m=0; m<50; m++)
  { 
    fill(144, 188, 114, (50-m)*2*0.01f*255); //(200-m)/2);
    float xxx = 20+random(5);
    ellipse(tracex[m], tracey[m], xxx, xxx);
  }

  strokeWidth(2);
  stroke(0xff808080);
  noFill();
  forward(rand);
  rand = random(1);  
  b = mouseX;
  b= b/400;
  m = mouseY;
  m = m / 10+2;
  m = 4;
  
  speed = mouseY;
  speed = speed / 12 + 5;
  speed = 20;

  ellipse(p1.x, p1.y,sin(a+1*b)*20+30,sin(a+1*b)*20+30);
  smoothie(0,sin(a+2*b)*20+30,sin(a+2*b)*20+30);
  smoothie(1,sin(a+3*b)*20+30,sin(a+3*b)*20+30);
  smoothie(2,sin(a+4*b)*20+30,sin(a+4*b)*20+30);
  smoothie(3,sin(a+5*b)*20+30,sin(a+5*b)*20+30);
  smoothie(4,sin(a+6*b)*20+30,sin(a+6*b)*20+30);
  smoothie(5,sin(a+7*b)*20+30,sin(a+7*b)*20+30);

  line(v.x,v.y, p1.x, p1.y);//v -> front _ p -> inside head

  if (rand < 0.01f) lf = 1; //left
  if (rand > 0.98f) lf = 2; //right
  if (rand > 0.02f && rand < 0.04f) lf = 3; //straight
  

  
  if (cnta%3 == 0 && p2.x < 60) 
  {
    left();
    lf = 3;
  }
  if (cnta%3 == 0 && p2.x > 340)
  {
    left();
    lf = 3;
  }

  if (cnta%3 == 0 && p2.y < 60)
  {
    left();
    lf = 3;
  }

  if (cnta%3 == 0 && p2.y > 340)
  {
    left();
    lf = 3;
    
  }

  
  if (cnta%m == m-1 && p2.x > 60 && p2.x < 340 && p2.y > 60 && p2.y < 340 && lf == 1) left(); 
  if (cnta%m == m-1 && p2.x > 60 && p2.x < 340 && p2.y > 60 && p2.y < 340 && lf == 2) right();   
  circx = sin(a)*40;
  circy = sin(a)*40;
  a = a + inc; 
  
}
void smoothie(int n, float circx, float circy) {
  fract+=fractD;
  if(fract>=1) {
    fract=0;
    arrayadd(p1.x, p1.y);
  }
  //if (rand < 0.1) ellipse(cooxx[n]+squareDecel()*(coox[n]-cooxx[n]), cooyy[n]+squareDecel()*(cooy[n]-cooyy[n]),circx,circy);
  ellipse(cooxx[n]+linear()*(coox[n]-cooxx[n]), cooyy[n]+linear()*(cooy[n]-cooyy[n]),circx,circy);
}
void arrayadd(float neux, float neuy) //puts new array at 0 and every other +1
{
  for (m=5; m>0; m--)
  { 
    cooxx[m]=coox[m];
    cooyy[m]=cooy[m];
    coox[m]=coox[m-1];
    cooy[m]=cooy[m-1];
  }
  for (m=49; m>0; m--)
  {
    tracex[m]=tracex[m-1];
    tracey[m]=tracey[m-1];
  }
  cooxx[0]=coox[0];
  cooyy[0]=cooy[0];
  coox[0]=neux;
  cooy[0]=neuy;
  tracex[0]=neux;
  tracey[0]=neuy;
}

void forward(float rand)
{ //forward moving
      w.set(v);
      w.sub(p1);
      w.div(speed); //smaller numbers = faster
      v.add(w);
      p1.add(w);
      p2.add(w);
}      

void left()
{
      v.set(p2);
      v.sub(p1);
      v.rotate(radians(cnt-=5));
      v.add(p1);
}
void right()
{
      v.set(p2);
      v.sub(p1);
      v.rotate(radians(cnt+=5));
      v.add(p1);
}

// General vector class for 2D vectors
class Vec2D {
  float x,y;
  Vec2D(float _x,float _y) {
    x=_x;
    y=_y;
  }
  Vec2D(Vec2D v) {
    x=v.x;
    y=v.y;
  }
  void set(float _x,float _y) {
    x=_x;
    y=_y;
  }
  void set(Vec2D v) {
    x=v.x;
    y=v.y;
  }
  void add(float _x,float _y) {
    x+=_x;
    y+=_y;
  }
  void add(Vec2D v) {
    x+=v.x;
    y+=v.y;
  }
  void sub(float _x,float _y) {
    x-=_x;
    y-=_y;
  }
  void sub(Vec2D v) {
    x-=v.x;
    y-=v.y;
  }
  void mult(float m) {
    x*=m;
    y*=m;
  }
  void div(float m) {
    x/=m;
    y/=m;
  }
  float length() {
    return sqrt(x*x+y*y);
  }
  float angle() {
    return atan2(y,x);
  }
  Vec2D tangent() {
    return new Vec2D(-y,x);
  }
  void rotate(float val) {
    // Due to float not being precise enough, double is used for the calculations
    double cosval=Math.cos(val);
    double sinval=Math.sin(val);
    double tmpx=x*cosval - y*sinval;
    double tmpy=x*sinval + y*cosval;
    x=(float)tmpx;
    y=(float)tmpy;
  }
}


// Linear movement, no acceleration or deceleration
float linear() {
  return fract;
}

// Pretty slow start, pretty fast finish
float squareAccel() {
  return fract*fract;
}

// Very slow start, very fast finish
float quadAccel() {
  return fract*fract*fract*fract;
}

// Starts quick then slows down
float sinAccel() {
  return sin(PI*0.5f*fract);
}

// Same as squareAccel, but decelerates
float squareDecel() {
  return (1-(fract-1)*(fract-1));
}

}
