// Timeline turntable
// NON-WEBCAM VERSION (warning: 300k .jar file)
// for webcam version go to:  http://users.design.ucla.edu/~lonergan/school/vj/webcamvj.pde
// guthrie

// Click and drag timelines.
// Timelines loop the gesture you've created.
// As your mouse wanders up above the timeline while dragging it, 
//         the frames are layered in with lower and lower alphas,
//                                               thus blurriness.

// Arrow keys change mode
// Space bar pauses (or unpauses)
// Clicking on the images freezes (or unfreezes) timelines

PImage[] temp = new PImage[66];
PImage[] tempreflected = new PImage[66];

int mode=2;
int mouseover=-1;

int timelineBuffer=12;
int numTimelines=2;
int yTimeline=224;


int dragTimeline=-1;  // -1 means nothing is being dragged, otherwise this is the index being dragged
int cursorWidth=5;
int cursorEndsRadius=5;
//int cursorMiddleDiameter=2;
    
float masterTintHue=0.0;


float mx=0.0;
float my=0.0;

boolean everyonePaused=false;


Timeline[] timelines = new Timeline[timelineBuffer];

void setup()
{
  size(600,300);
  colorMode(HSB,1);
  framerate(60);
  angleMode(DEGREES);
  
  background(1,0,1);
  
  for(int i=0; i<4; i++)
  {
    timelines[i]=new Timeline(i,int(random(40,width-40))); 
  }
  
  for(int i=0; i<temp.length; i++)
  {
    temp[i]=loadImage("movie1" + nf(i+1,2) + ".jpg");
    tempreflected[i]=reflectImage(temp[i]); 
  }
  
  

}

void draw()
{
 // background(1,0,1);
//fill(.9,.3,1);
//noStroke();
//rect(0,yTimeline,width,height);
  // MOUSE
  mx=smoother(mx,mouseX,2,cursorEndsRadius-1,width-cursorEndsRadius);
  my=smoother(my,mouseY,2,0,height);
  checkMouseOver();

  for(int i=0; i<numTimelines; i++)
  {
    timelines[i].update();
    timelines[i].draw();
  }  


}

void checkMouseOver()
{
  mouseover=-1;
  for(int i=0; i<numTimelines && mouseover<0; i++)
  {
    if(timelines[i].isInside(mouseX,mouseY))
    {
        mouseover=i;
    }
  }
}

void mousePressed()
{
  for(int i=0; i<numTimelines; i++)
  {
    if(timelines[i].isInside(mouseX,mouseY))
    { 
      timelines[i].startDrag();
    }
    
    if(timelines[i].isInsidePic(mouseX,mouseY))
    {
      if(timelines[i].isPaused())
        timelines[i].unpause();
       else
         timelines[i].pause();
    }
  }
}

void mouseReleased()
{
  if(dragTimeline>=0)
    timelines[dragTimeline].stopDrag();
}

void keyPressed()
{
  if(key==' ')
  {
    if(mode==2)
    {
    if(timelines[0].isPaused() && timelines[1].isPaused())
    {
     timelines[0].unpause();
     timelines[1].unpause();
    }
    else
    {
     timelines[0].pause();
     timelines[1].pause();
    }
    }
    else
    {
    if(timelines[0].isPaused() && timelines[1].isPaused() && timelines[2].isPaused() && timelines[3].isPaused())
    {
     timelines[0].unpause();
     timelines[1].unpause();
     timelines[2].unpause();
     timelines[3].unpause();
    }
    else
    {
     timelines[0].pause();
     timelines[1].pause();
     timelines[2].pause();
     timelines[3].pause();
    }    
    
    
    
    
    }
  }
  
  else if(keyCode==UP || keyCode==LEFT || keyCode==RIGHT || keyCode==DOWN)
  {
    if(mode==4)
    {
    
      background(1);
      mode=2;
      numTimelines=2;
    
    }
  
    else
    {
      background(1);
      mode=4;
      numTimelines=4;
    
    }
  }

}


PImage reflectImage(PImage reflectMe)  // no transparency
{
  PImage returnMe = new PImage(reflectMe.width,reflectMe.height);
 
  for(int i=0; i<reflectMe.height; i++)
  {
    for(int j=0; j<reflectMe.width; j++)
    {
        returnMe.set(reflectMe.width-j-1,i,reflectMe.get(j,i));
    }
  }
  
  return returnMe;

}

float smoother(float pos, float dest, float speed, int bottom, int top)
{

  float dif = dest - pos;
  if(abs(dif) > 1.0) {
    pos = pos + dif/speed;
  }
  return(constrain(pos, bottom, top));

}

float smoother(float pos, float dest, float speed)
//overload due to annoyance
{

  float dif = dest - pos;
  if(abs(dif) > 1.0) {
    pos = pos + dif/speed;
  }
  return pos;

}

class Timeline
{
  float myHue=.77;
  float h=.77;
    
  int SIbuffer=800;
  int numSI=-1;    // how big is SI
  int iSI=0;      // current index of SI
  float[] speedInstructions;
  float[] blurInstructions;
  float masterTintAlpha=1.0;
    
  float f,x,v,a,M,D;
  float cursorX;  
  int dragX=0;    // relative to cursorX at beginning of drag
  int startDragX=0;   // where the orgin of the click is
  int startDragY=0;
  
  int me;

  boolean paused=false;
  Timeline(int myme, int myx)
  {
    me=myme;
   // information=loadImage("img" + myme + ".jpg");
    x = cursorX = myx;
    
    speedInstructions = new float[SIbuffer];
    blurInstructions = new float[SIbuffer];
  }

  void update()
  {
    // physics equations
    int timelineHeight=((height-yTimeline)/numTimelines);
    int y=yTimeline+(timelineHeight)*me; 
        
    if(dragTimeline==me)
    {
      dragging();
    }
    else if(paused)
    {
    
    
    }
    else
    {
      //f+=speedInstructions[iSI]*.004;
      
      if(numSI<0)
      {
        cursorX=x;
        
      }
      else
      {
        cursorX=speedInstructions[iSI];
        masterTintAlpha=1-blurInstructions[iSI]/(y);   
      }
      
      
      if(numSI>0)
      
      iSI++;
      if(iSI>=numSI)
        iSI=0;
        
    }
    
    
    //f=smoother(f,0,2000);      // slows down towards 0
    //a = f / M;                 // Set the acceleration, f=ma == a=f/m
    //v = D * (v + a);           // Set the velocity
    //x=x+v;
    
   
  
  }
  
  void draw()
  {
    int timelineHeight=((height-yTimeline)/numTimelines);
    int y=yTimeline+(timelineHeight)*me; 
    
    // draw timeline
    
    // safhdksdkjhlfhjklsklhjfasdhjklfjkashdlfhjkdlsa();
    
    // draw cursor       
    

    tint(color(0,(1-masterTintAlpha)*0,1,masterTintAlpha));

    noStroke();
    
    beginShape(QUADS);
    fill(1,0,1);
    
    vertex(0,y+timelineHeight);
        
    vertex(0,y);
    
  //  if(me==2)
   // fill(.9,.3,.8);
   // else if(me==1 || me==0)

  
    if(((mouseover==me) && dragTimeline<0) || dragTimeline==me)
    {

      myHue=.65;   
      fill(.77+((1-masterTintAlpha)*.33),h,.8+(h/.7)*.2);
    }
    else
    {
      myHue=.3;
      fill(.77+((1-masterTintAlpha)*.33),h,.8+(h/.7)*.2);
    }

    
  float dif = myHue - h;
  if(abs(dif) > 0.001) {
    h = h + dif/1.9;
  }    
   // else if(me==3)
  //  fill(.6,.3,.8);
    vertex(cursorX,y);
    
    vertex(cursorX,y+timelineHeight);
        
    endShape();

    beginShape(QUADS);
    fill(.7);
    
    vertex(cursorX,y+timelineHeight);
    
    vertex(cursorX,y);
    fill(1,0,1);
    vertex(width,y);
    
    vertex(width,y+timelineHeight);    
    
    endShape();
    
    
    //if(numSI>0 && dragTimeline<0)
    

  int theFrame=constrain(int(cursorX/width*temp.length),0,temp.length-1);
  if(mode==2)
   {
      if(me==0)
      image(temp[theFrame],0,0,width/2,yTimeline);
      else if(me==1)
      image(tempreflected[theFrame],width/2,0,width/2,yTimeline);
      else if(me==2)
      masterTintAlpha=(cursorX/width);
   }
   /*
   else if(mode==4)
   {
      if(me==2)
      image(temp[theFrame],width/4,yTimeline/2,width/4,yTimeline/2);
      else if(me==3)
      image(tempreflected[theFrame],width/2,yTimeline/2,width/4,yTimeline/2);
      else if(me==1)
      {
        push();
       
         translate(3*width/4,yTimeline/2);
         rotate(180);
        image(temp[theFrame],0,0,width/4,yTimeline/2);
        pop();
      }
      else if(me==0)
      {
        push();
       
         translate(width/2,yTimeline/2);
         rotate(180);
         //.get(0,0,200,75)
        image(tempreflected[theFrame],0,0,width/4,yTimeline/2);
        pop(); 
      
      }
      else if(me==4)
        masterTintAlpha=(cursorX/width);   

   
   }
   */
   else if(mode==4)
   {
      if(me==2)
      image(temp[theFrame].get(0,0,235,176/2),0,yTimeline/2,width/2,yTimeline/2);
      else if(me==3)
      image(tempreflected[theFrame].get(0,0,235,176/2),width/2,yTimeline/2,width/2,yTimeline/2);
      else if(me==1)
      {
        push();
       
         translate(width,yTimeline/2);
         rotate(180);
        image(temp[theFrame].get(0,0,235,176/2),0,0,width/2,yTimeline/2);
        pop();
      }
      else if(me==0)
      {
        push();
       
         translate(width/2,yTimeline/2);
         rotate(180);
         //.get(0,0,200,75)
        image(tempreflected[theFrame].get(0,0,235,176/2),0,0,width/2,yTimeline/2);
        pop(); 
      
      }
      else if(me==4)
        masterTintAlpha=(cursorX/width);   

   
   }
   
    noStroke();
    fill(0);
    

    triangle(cursorX-cursorEndsRadius,y,cursorX+cursorEndsRadius,y,cursorX,y+cursorEndsRadius);
    triangle(cursorX-cursorEndsRadius,y+timelineHeight,cursorX+cursorEndsRadius,y+timelineHeight,cursorX,y+timelineHeight-cursorEndsRadius);
        
    stroke(0);
    //line(cursorX,y+1,cursorX,y+timelineHeight);
    //line(0,y,width,y);
    //line(0,y+timelineHeight,width,y+timelineHeight);
           
  }
  
  boolean isInside(int otherx, int othery)
  {
  
    if(othery>=yTimeline+((height-yTimeline)/numTimelines)*me && othery<yTimeline+((height-yTimeline)/numTimelines)*(me+1))// &&
     // otherx>(cursorX-cursorWidth) && otherx<(cursorX+cursorWidth)) 
    {
      return true;
    }
    
    return false;
  }
  
  boolean isInsidePic(int otherx, int othery)
  {

    if(othery<=yTimeline)// &&
    // otherx>(cursorX-cursorWidth) && otherx<(cursorX+cursorWidth))
    {
      if( mode==2 && ((otherx<300 && me==0) || (otherx>=300 && me==1)))
      {
        return true;
      }
    /*  else if( mode==4 && (
      (me==0 && otherx<width/2 && otherx>=width/4 && othery<(yTimeline/2) && othery>=0) ||
      (me==1 && otherx>=width/2 && otherx<=(width*3/4) && othery<(yTimeline/2) && othery>=0) ||
      (me==2 && otherx<width/2 && otherx>=width/4 && othery>=(yTimeline/2) && othery<yTimeline) ||
      (me==3 && otherx>=width/2 && otherx<=3*width/4 && othery>=(yTimeline/2) && othery<yTimeline)))
      {*/
 
       else if( mode==4 && (
      (me==0 && otherx<width/2 && otherx>=0 && othery<(yTimeline/2) && othery>=0) ||
      (me==1 && otherx>=width/2 && otherx<=width && othery<(yTimeline/2) && othery>=0) ||
      (me==2 && otherx<width/2 && otherx>=0 && othery>=(yTimeline/2) && othery<yTimeline) ||
      (me==3 && otherx>=width/2 && otherx<=width && othery>=(yTimeline/2) && othery<yTimeline)))
      {


        return true;
      }
    }
    return false;
  }
  
  void startDrag()
  {
    if(paused)
      unpause();
      
    dragTimeline=me;  
    dragX=mouseX-int(cursorX);
    startDragX=mouseX;
    startDragY=mouseY;
    numSI=0;
    iSI=0;
    
    
  }

  void dragging()
  {
      // writes information into the speedInstructions array, to be looped
    int timelineHeight=((height-yTimeline)/numTimelines);
    int y=yTimeline+(timelineHeight)*me; 
     
      speedInstructions[iSI]=constrain(mx-dragX,0,width);            //mx+startDragX-dragX;  // distance from where mouse originally was
      cursorX=constrain(mx-dragX,0,width); 
      
      
      if(my<y)
      {
        blurInstructions[iSI]=constrain(abs(my-y),0,y);
        masterTintAlpha=1-blurInstructions[iSI]/(y);      
      }
      else if(my>y+timelineHeight)
      {
        blurInstructions[iSI]=constrain(abs(my-(y+timelineHeight)),0,y);
        masterTintAlpha=1-blurInstructions[iSI]/(y);           
      }
      else
      {
        blurInstructions[iSI]=0; 
        masterTintAlpha=1;      
      }
      
      iSI++;
      numSI++;
      if(numSI>=speedInstructions.length)  // maxed out
      {     
      
      
      }
  }
  
  void pause()
  {
    paused=true;
  }
  
  void unpause()
  {
    paused=false;
  }
  
  boolean isPaused()
  {
    return paused;
  }
  
  void stopDrag()
  {
    dragTimeline=-1;
    dragX=0;
    startDragX=0;
    iSI=0;
  }
 
}
Project 4: Context
Extend an idea you've explored previously in your life, but adapt it to the context of an interactive system which requires the viewer to engage with the material.