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