// // Visualisation Project for MAT259 // Look at the emotions displayed on Twitter in Realtime // Twitter accessed using Mark McBride's Twitter Library for Processing // Tweets categorised into the 6 basic emotions and then displayed in 3D // using traer physics and SuperPoint // // Ben Alun-Jones // March 13th 2010 import com.twitter.processing.*; import processing.opengl.*; import peasy.*; import javax.media.opengl.*; int tweets; PeasyCam cam; PGraphicsOpenGL pgl; GL gl; PImage love; PImage anger; PImage surp; PImage joy; PImage sad; PImage fear; PImage logo; PImage ProfilePic; String ProfileText; String username; int CurProfile = 0; public static final int LOVE = 0; public static final int JOY = 1; public static final int SURPRISE = 2; public static final int ANGER = 3; public static final int SADNESS = 4; public static final int FEAR = 5; double count =0; boolean display = false; boolean rotation = true; boolean spherical = true; ArrayList Emotions; PFont font; PGraphics buffer; // buffer void setup(){ size(1200,800,OPENGL); hint(DISABLE_OPENGL_2X_SMOOTH); hint(ENABLE_OPENGL_4X_SMOOTH); //hint(ENABLE_DEPTH_SORT); love = loadImage("love.png"); joy = loadImage("joy.png"); anger = loadImage("anger.png"); sad = loadImage("sad.png"); surp = loadImage("surprise.png"); fear = loadImage("fear.png"); logo = loadImage("twitter.png"); // set up font font = loadFont("AppleCasual-48.vlw"); textFont(font, 36); Emotions = new ArrayList(); TweetStream stream = new TweetStream(this, "stream.twitter.com", 80, "1/statuses/sample.json", "mister_benjamin", "editors25"); stream.go(); cam = new PeasyCam(this,0,0,0, 800); cam.setMinimumDistance(0.0000001); cam.setMaximumDistance(5000); } void draw(){ imageMode(CENTER); background(#EDFEFF);//31,126,255); pgl = (PGraphicsOpenGL) g; gl = pgl.gl; pgl.beginGL(); // This fixes the overlap issue gl.glDepthMask(false); gl.glDisable(GL.GL_DEPTH_TEST); gl.glClear(GL.GL_DEPTH_BUFFER_BIT); // Turn on the blend mode gl.glEnable(GL.GL_BLEND); // Define the blend mode gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_SRC_ALPHA_SATURATE); gl.glEnable (GL.GL_LINE_SMOOTH); gl.glHint (GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST); fill(10, 60, 120); if(Emotions.size()>1 && display){ if(frameCount%40 == 12){ CurProfile = floor(random(0,Emotions.size()-1)); println("update"); } Emotion pictureman = (Emotion)Emotions.get(CurProfile); if(pictureman.getImURL() != null){ ProfilePic = loadImage(pictureman.getImURL()); ProfileText = pictureman.getTweetText(); username = pictureman.getUserName(); } } for(int i = 0; i< Emotions.size();i++){ Emotion this_feeling = (Emotion)Emotions.get(i); pushMatrix(); float theta = 0f, radius = 0f, phi = 0f; try{ theta = this_feeling.userid;//map(this_feeling.userid, 0,100000000, 0,100); radius = log(this_feeling.nUpdates)*35;//MAP(this_feeling.nUpdates,0,100, 0,800); phi = this_feeling.nFollowers/10; } catch(Exception e){ println(e.getMessage()); } float[] positions = cam.getPosition(); PVector cam_position = new PVector(positions[0],positions[1],positions[2]); PVector new_loc; if(spherical){ new_loc = new PVector(radius*sin(theta)*cos(phi),radius*sin(theta)*sin(phi), radius*cos(theta)); } else{ new_loc = new PVector(this_feeling.userid/100000,this_feeling.nUpdates/100/*,0,100, 0,00)*/ , this_feeling.nFollowers/10); } PVector camUP = new PVector(0,0,1); translate(new_loc.x, new_loc.y, new_loc.z); strokeWeight(log(this_feeling.nFriends)*4);//*frameRate%2); PVector objToCamProj = PVector.sub(cam_position , new_loc); objToCamProj.normalize(); PVector bill_right = objToCamProj.cross(camUP); bill_right.normalize(); PVector bill_up = objToCamProj.cross(bill_right); applyMatrix(bill_right.x, bill_up.x, objToCamProj.x, 0, bill_right.y, bill_up.y, objToCamProj.y, 0, bill_right.z, bill_up.z, objToCamProj.z, 0, 0, 0, 0, 1); float size = 0f; strokeWeight(1); switch(this_feeling.Emo){ case 0 : // LOVE color love_tint = #F32973; tint(love_tint,150); image(love,0,0,(log(this_feeling.nFriends)/40)*love.width,(log(this_feeling.nFriends)/40)*love.height); fill(0,0); stroke(love_tint); ellipse(0,0,(log(this_feeling.nFriends)/40)*love.width, (log(this_feeling.nFriends)/40)*love.width); break; case 1 : // JOY color joy_tint = #FFF824; fill(joy_tint,190); stroke(joy_tint); ellipse(0,0,(log(this_feeling.nFriends)/40)*love.width, (log(this_feeling.nFriends)/40)*love.width); tint(joy_tint,150); image(joy,0,0,(log(this_feeling.nFriends)/40)*joy.width,(log(this_feeling.nFriends)/40)*joy.height); break; case 2 : //SURPRISE color surp_tint = #48EE06;//#FC913A;//#72DE1F; tint(surp_tint,150); image(surp,0,0,(log(this_feeling.nFriends)/40)*surp.width,(log(this_feeling.nFriends)/40)*surp.height); fill(0,0); stroke(surp_tint); size = (log(this_feeling.nFriends)/40)*surp.width; ellipse(0,0,size,size); break; case 3 : //ANGER color anger_tint = #EE0909;//#B1142B; tint(anger_tint,150); image(anger,0,0,(log(this_feeling.nFriends)/40)*anger.width,(log(this_feeling.nFriends)/40)*anger.height); fill(0,0); stroke(anger_tint); size = (log(this_feeling.nFriends)/40)*anger.width; ellipse(0,0,size,size); break; case 4 : //SADNESS color sad_tint = #4B72F2; tint(sad_tint,250); image(sad,0,0,(log(this_feeling.nFriends)/40)*sad.width,(log(this_feeling.nFriends)/40)*sad.height); fill(sad_tint,100); stroke(sad_tint); size = (log(this_feeling.nFriends)/40)*sad.width; ellipse(0,0,size,size); break; case 5 : //FEAR color fear_tint = #C0C0D8;//#474A4C; tint(fear_tint,250); image(fear,0,0,(log(this_feeling.nFriends)/40)*fear.width,(log(this_feeling.nFriends)/40)*fear.height); fill(fear_tint,150); stroke(fear_tint); size = (log(this_feeling.nFriends)/40)*fear.width; ellipse(0,0,size,size); break; } popMatrix(); fill(255,0); stroke(120,20); strokeWeight(1); //sphere(2000); pgl.endGL(); if(rotation){ cam.setRotations(count,-count,count); count = (count-random(0,0.00001)); } } pgl.endGL(); hud(); } void hud() { //Display HUD using PeasyCam cam.beginHUD(); // Draw in edges fill(#71DAF7); stroke(0,0); strokeCap(ROUND); rect(0,0,100,height); rect(width-100,0,100,height); rect(0,0,width,100); rect(0,height-100,width,100); //show edge of window with red line stroke(255,255); strokeWeight(3); fill(0,0); rect(100,100,width-200,height-200); tint(255,255); imageMode(CORNER); image(logo, 100,50, logo.width/10, logo.height/10); textFont(font, 36); textAlign(LEFT,LEFT); fill(255); text("Emotion Visualisation in Real Time", 230,85); textAlign(RIGHT,RIGHT); text("Ben Alun-Jones", width-100,height-65); textFont(font, 18); text("MAT 259 Information Visualisation", width-100,height-45); if(ProfilePic != null && display){ fill(255,255); rectMode(CORNER); rect(100,height-90, 460,height-70); fill(#71DAF7); strokeWeight(0); rect(90,height-10, 480,height); tint(255,255); image(ProfilePic, 105,height - 70, 50,50); textAlign(LEFT,LEFT); textFont(font, 20); fill(#71DAF7); text( "Random Tweeted Emotion:", 105, height-75); textFont(font, 12); fill(#34375A); text(username,160, height-60); fill(#71DAF7); if(ProfileText.length()>71){ int linelength = 70; String line1 = ProfileText.substring(0,linelength); String line2 = ProfileText.substring(linelength); text(line1,160, height-45); text(line2.trim(),160, height-30); } else{ text(ProfileText,160, height-45); } } cam.endHUD(); // always! } // called by twitter stream whenever a new tweet comes in void tweet(Status tweet) { Emotion feeling = new Emotion(tweet); if(feeling.isEmotion()){ Emotions.add(feeling); } tweets += 1; } void keyPressed() { if (key == 't') { display = !display; } if(key =='r'){ rotation = !rotation; } if(key =='s'){ spherical = !spherical; } }