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_konertz extends BApplet {
// UdK Computational design
// ak@annek.de

float widthHalf,heightHalf;//300,300
Animal[] anim; //class Animal
int num;
int state = 3;

///////////////////////////////////////
void setup() {
  size(800,400);
  background(255);//255
  ellipseMode(CENTER_DIAMETER);
  framerate(30);
  //variables:
  widthHalf=300;//width/2; 
  heightHalf=height/2;
  num=18;
  anim=new Animal[num];
  //font
  BFont fontA = loadFont("OCR-B.vlw.gz");//klasse laden
  setFont(fontA, 16);//init.

  
  for(int i=0; i<num; i++) {
    anim[i]=new Animal(); //no parameters
    anim[i].init(i);
    anim[i].setColor(i); //index number
  }  
}//setup

/////////////////////////////////////////////

void keyPressed() {

  if(key == '0') {
    state = 0;
  } else if (key == '1'){
    state = 1;
  } else if (key == '2'){
    state = 2;
  } else if (key == '3'){
    state = 3;
  } else if (key == '4'){
    state = 4;
  } else if (key == '5'){
    state = 5;
  } else if (key =='6'){
    state = 6;
  } else if (key =='7'){
    state = 7;
  } else if (key =='8'){
    state = 8;
  }
  
}// end keypressed

/////////////////////////////////////////

void setAFont(){

  if (state==8){
    fill(248,181,0);//pacman
  } else {
    fill(0,0,0);
  }
   
  stroke(223,0,114);
  line(325,-200,325,200);
  
  text("press key() to",350,-100);//ausgeben
  text("add behavior:",350,-80);
  text("(1) timid",350,-50);
  text("(2) tame",350,-30);
  text("(3) ignorant",350,-10);
  text("(4) drunken",350,10);
  text("(5) happy",350,30);
  text("(6) summer-sale",350,50);
  text("(7) stressed",350,70);
  text("(8) old-fashioned",350,90);
  text("(0) tracked",350,110);
  noFill();
}//font

///////////////////////

void loop() {
  translate(widthHalf,heightHalf);
  noStroke();
  noFill();
  setAFont();
  
  keyPressed();
  
  if (state==0){
    noBackground();
  }else{
    background(255,255,255);
  }
  
  for(int i=0; i<num; i++) { 
    
    anim[i].updateDrawAnimal();
    anim[i].steerAnimal();
    //choose
    if (state==3){
      anim[i].randomMove();
    }
    else if (state==1){
      anim[i].seekAndFlee();//flee
    }
    else if (state==2){
      anim[i].seekAndFlee();//seek
    }
    else if (state==4){
      anim[i].turnMove();
    }
    else if (state==5){
      anim[i].jumpMove();
    }
    else if (state==8){
      anim[i].pacMan();
    }  
    else{
      anim[i].randomMove();
    }  
    //state=6, followAnimal
    //state=7, stressed
  }
}//end loop

//////////////////////////////////////////

class Animal {
  Vec2D v,vD,tangent, vTemp;
  float rad,angle,angleD,speed, e_rad; //l;
  int c;
  int index;
  
  ///////////////
  Animal() {
  }//end Animal
  ///////////////////
  
  void init(int i){
    index = i;
    e_rad = 30;
    
    //init position of animal
    v=new Vec2D(random((widthHalf)),0);
    v.rotate(random(degrees(360)));
    //init new Direction of Animal
    vD=new Vec2D(0,0);
    //init tangent to draw animal
    tangent=new Vec2D(0,0);
    
    rad=random(2,5);
    angle=random(360);
    angleD=0; //0
    speed=2;
    
    c=color(255,0,0);
  }//end init
  
  ///////////////////
  
  void setColor(int index) {
    switch(index%3) {
      case 0: c=color(223,0,114); break;//pink
      case 1: c=color(0,25,130); break;//blue
      case 2: c=color(113,198,251); break;
    }
  }//end setColor
   
  ///////////////////   
  
  void updateDrawAnimal(){
  
    // Draw tangent (position, direction)
    tangent.set(-vD.y,vD.x);
    tangent.normalise();
    tangent.mult(speed);
    int eye=5;
    /////////////////
    if (state==8){
      stroke(248,181,0);
    }else{
      stroke(c);
    }
    //////////////////
    ellipse(v.x, v.y, e_rad, e_rad);
    //eyes
    if (e_rad >30){
      eye = 1;
    }else{
      eye = 5;
    }
    ellipse( v.x+((vD.x+tangent.x)*3),v.y+((vD.y+tangent.y)*3), 5, eye);
    ellipse( v.x+((vD.x-tangent.x)*3),v.y+((vD.y-tangent.y)*3), 5, eye);
  
  //window
    if(v.x<-widthHalf) v.x=widthHalf; //-300 gets 300 
    else if(v.x>widthHalf) v.x=-widthHalf; //300 gets -300    
    if(v.y<-heightHalf) v.y=heightHalf;
    else if(v.y>heightHalf) v.y=-heightHalf;
    
  }//end updateAnimal
  
  ///////////////////////// 

  void randomMove(){
    e_rad = 30;
    speed = 2;
   //update random direction
    angleD+=random(5)-2.5f;
    if(angleD<-3) angleD=-3;
    else if(angleD>3) angleD=3;
    angle+=angleD; //circle + random direction 
    
    //random speed
    speed+=random(0.1f)-0.05f;
    if(speed<2) speed=2;
    else if(speed>10) speed=10;
    
    //new direction
    vD.set(speed,0);
    vD.rotate(radians(angle));

    ///////////////////////
    if (state==6){//shopping
      for (int i=0;i<num;i++) if (i!=index){//nicht die länge zu sich selbst berechnen
        anim[i].v.add(vD);
      }
    }
    /////////////////////
    v.add(vD);
  }//end randomMove
  
  ////////////////////////
   
  void steerAnimal(){
 
    Vec2D vSep=new Vec2D(0,0);
    Vec2D vDist=new Vec2D(0,0);
    Vec2D vAlign = new Vec2D(0,0);
    Vec2D vCol=new Vec2D(0,0);
    int number = 0;
    int number2 = 0;
      
    for (int i=0;i<num;i++) if (i!=index){
      //calculate radius
      vDist.set (anim[i].v);
      vDist.sub(v);
      float l=vDist.length();
     
      //separation: steering to opposite direction if animal comes too close
      if (l<40){//radius 40
        vDist.mult(-1);//opposite direction
        vDist.normalise();
        vDist.mult(8);//5
        vSep.add(vDist);//collect all vectors within radius
      } 
       
      //alignment:steering to average dircetion of neighbors
      if (l<300){
        vAlign.add(anim[i].vD);//add direction
        number = number + 1;
      } 
      if (number>0){
        vAlign.div(number);
        vAlign.normalise();
        /////////////////////
        if (state==7){//stessed
          vAlign.mult(8);
        }else{
          vAlign.mult(2);
        }
        /////////////////
      }
      
      //cohesion: steering to average position      
      if (l<300){
        vCol.add(anim[i].v);//collect vectors
        number2 = number2 +1;
      } 
      if (number2 > 0){
        vCol.div(number2);
        vCol.normalise();
        vCol.mult(2);
      } 
       
    }
    v.add(vSep);//go opposite direction 
    v.add(vAlign); //go average direction
    v.add(vCol);//go average position
    
  }//end steerAnimal  

  //////////////////////////////////////
  
  void seekAndFlee() {//example: mouseposition
    //init
    Vec2D vMouse=new Vec2D(mouseX-widthHalf,mouseY-heightHalf);
    Vec2D vToMouse=new Vec2D(0,0);
    
    //vector between animal and mouse
    vMouse.sub(v);//mouse-position(v)
    vToMouse.set(vMouse);
    vToMouse.normalise();
    vToMouse.mult(5);//3
    
    if (state==1){//flee
      vToMouse.mult(-0.5f);//-1
    }
    v.add(vToMouse);
    
  }//end seekAndFlee
  
  //////////////////////////////////////

 void turnMove(){
   //random
    angleD+=random(15)-7.5f; 
    if(angleD<-30) angleD=-30;
    else if(angleD>30) angleD=30;
    angle+=angleD;
    
    //speed
    speed+=random(1)-0.5f;
    if(speed<2) speed=2;
    else if(speed>20) speed=20;
 
    //new Direction
    vD.set(speed,0);
    vD.rotate(radians(angle));
    v.add(vD);
  }//end turnMove
  
  ///////////////////////////
   
  void jumpMove(){//happy
    speed=2;
    if(random(1)>0.95){
     e_rad = 50;
    }else {
      e_rad = 30;
    }   
    v.add(vD); 
  }
  
  /////////////////////////////
  
  void pacMan(){
      
    background(0,0,0);
    stroke(248,181,0);
    line(325,-200,325,200);
    speed=2;
    angle=360;
    
    //random speed
    speed+=random(0.1f)-0.05f; //speed between 2 and 20
    if(speed<2) speed=2;
    else if(speed>5) speed=5;//20 
    //new direction
    vD.set(speed,0);  
    vD.rotate(radians(angle));
    v.add(vD);
  }//end pacMAn
  
  /////////////////////////////
   
}//end class Animal


//////////////////////////////////////////////////

// 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);
  }

  void normalise() {
    float l=length();
    if(l!=0) {
      x/=l;
      y/=l;
    }
  }

  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;
  }
}
////////////////////////////////


}
