// --------------------------------------------------- // University of California Santa Barbara // Media Arts and Technology // MAT 259 | Visualizing Information | Winter 2011 // // Patrick Rudolph // Project 2 | TreeMap // // Main Program // --------------------------------------------------- /* TODO: - treemap select for level 2 - bugfixing - top 3 categories update - bug china update all?! - checkbox select/deselect - contentsVisible bei apply-all - zoom radius */ import processing.opengl.*; import treemap.*; // --------------------------------------------------- // GLOBAL VARIABLES // --------------------------------------------------- // fonts & colors PFont fontTiny,fontSmall,fontText,fontBold,fontH2; final color colBg = #f4f3db; final color colText = #000000; final color colControlsBg1 = #dddddd; final color colControlsBg2 = #ffffff; final color colControlsBorder = #cccccc; final color colMapBg = #efedc8; final color colMapBorder = #cccccc; // boundaries & positioning for the treemaps final int offsetH = -30; int rectW = 400; int rectH = 320; int rectWaim = rectW; int rectHaim = rectH; int rectSmooth; float radius = rectW*1.8; float radiusAim = radius; final String deweyNamesL1 = "deweyNamesL1.txt"; final String deweyNamesL2 = "deweyNamesL2.txt"; // data structures for the treemaps CountryMap[] countries = new CountryMap[5]; CountryMap curFront; // mouse rotation float rotY = 0; float rotYaim; boolean rotSmooth = false; float rotStep; // controls PImage oldFlag, newFlag, checkOn, checkOff, applyOn, applyOff; float oldFrontOpacity = 0; float newFrontOpacity = 255; boolean mouseRelease = true; // --------------------------------------------------- // INITIALIZING (runs only once) // --------------------------------------------------- void setup() { if(frame != null) frame.setTitle("3D Pentagon TreeMaps | 2011W MAT259 Visualizing Information | Patrick Rudolph"); size(800,700,OPENGL); // general frameRate(30); colorMode(HSB,360,100,100); rectMode(CORNERS); smooth(); noStroke(); rotStep = 0.03; // fonts fontH2 = loadFont("Tahoma-Bold-18.vlw"); fontText = loadFont("Tahoma-14.vlw"); fontBold = loadFont("Tahoma-Bold-14.vlw"); fontSmall = loadFont("Tahoma-12.vlw"); fontTiny = loadFont("Tahoma-11.vlw"); // tree maps countries[0] = new CountryMap(0,"USA", "usa"); countries[1] = new CountryMap(1,"China", "china"); countries[2] = new CountryMap(2,"Mexico", "mexico"); countries[3] = new CountryMap(3,"Germany", "germany"); countries[4] = new CountryMap(4,"Africa", "africa"); for(int i = 0; i < countries.length; i++) { countries[i].initializeTreeMap(); } // controls oldFlag = countries[1].img; newFlag = countries[0].img; curFront = countries[0]; checkOn = loadImage("check_on.png"); checkOff = loadImage("check_off.png"); applyOn = loadImage("apply_on.png"); applyOff = loadImage("apply_off.png"); } // --------------------------------------------------- // DRAWING (runs at framerate, default 60 frames/s) // --------------------------------------------------- void draw() { background(colBg); pushMatrix(); translate(width/2, height/2+offsetH, -radius); fixRotation(); smoothRotation(); smoothZoom(); rotateY(rotY); // apply changes to global rotation variable textFont(fontText); textAlign(CENTER); for(int i = 0; i < countries.length; i++) { pushMatrix(); translate(-sin(2*PI*i/5)*radius,0,cos(2*PI*i/5)*radius); rotateY(2*PI*(countries.length-i)/5); countries[i].drawBackground(); translate(-rectW,-rectH,0); countries[i].drawTreeMap(); popMatrix(); } popMatrix(); drawControls(); checkFront(); } // --------------------------------------------------- // DRAW CONTROL PANEL AT BOTTOM // --------------------------------------------------- void drawControls() { pushMatrix(); translate(0,height-60,0); // background fill(colControlsBorder, 200); rect(0,-2,width,0); setGradient(0,0,width,60,colControlsBg1,colControlsBg2); // flag tint(255,oldFrontOpacity); image(oldFlag,5,3); tint(255,newFrontOpacity); image(newFlag,5,3); noTint(); // headline textFont(fontH2); textAlign(LEFT); fill(colText, 150); text(curFront.name,110,18); // info textFont(fontSmall); fill(colText, 150); text("Total: "+formatText(curFront.sum)+" check-outs",110,35); String[] namesL1 = loadStrings(deweyNamesL1); colorMode(HSB, 360, 100, 100); String[] sTop = { namesL1[curFront.sumCategories[0][0]], namesL1[curFront.sumCategories[1][0]], namesL1[curFront.sumCategories[2][0]] }; /*color[] cTop = { color(map(curFront.sumCategories[0][0], 0, 9, 0, 330),40,80), color(map(curFront.sumCategories[1][0], 0, 9, 0, 330),40,80), color(map(curFront.sumCategories[2][0], 0, 9, 0, 330),40,80) };*/ //fill(cTop[0],240); text("#1: "+sTop[0],110,52); //fill(cTop[1],240); text("#2: "+sTop[1],110+textWidth(sTop[0])+30,52); //fill(cTop[2],240); text("#3: "+sTop[2],110+textWidth(sTop[0])+30+textWidth(sTop[1])+30,52); // reset colorMode(RGB, 255); // checkbox fill(colControlsBorder, 200); rect(312,0,313,120); int row = 1; for(int i = 0; i < 10; i++) { int checkLeft, checkTop; if(row % 2 != 0) { // first row checkLeft = 320+(i)*42; checkTop = 7; row++; } else { // second row checkLeft = 320+(i-row/2)*42; checkTop = 32; row--; } textFont(fontSmall); colorMode(HSB, 360, 100, 100); color tempCol = color(map(i, 0, 9, 0, 330),40,80); if(curFront.enabled[i]) { fill(tempCol); rect(checkLeft+7,checkTop+7,checkLeft+14,checkTop+14); fill(colText, 150); text(namesL1[i], checkLeft+20, checkTop+15); //image(checkOn, checkLeft, checkTop); if(mouseInRect(checkLeft,checkTop,checkLeft+20+textWidth(namesL1[i]),checkTop+20,0,height-60) && mousePressed && mouseRelease) { mouseRelease = false; curFront.disableItem(i); curFront.updateLayout(); } } else { fill(tempCol,100); rect(checkLeft+7,checkTop+7,checkLeft+14,checkTop+14); fill(colText, 80); text(namesL1[i], checkLeft+20, checkTop+15); //image(checkOff, checkLeft, checkTop); if(mouseInRect(checkLeft,checkTop,checkLeft+20+textWidth(namesL1[i]),checkTop+20,0,height-60) && mousePressed && mouseRelease) { mouseRelease = false; curFront.enableItem(i); curFront.updateLayout(); } } // reset colorMode(RGB, 255); } fill(colControlsBorder, 200); rect(717,0,718,120); // apply buttons fill(colText,50); //PImage img; if( mouseInRect(715,12,775,52,0,height-60) ) { fill(colText,100); text("Apply to all",727,17); if(mousePressed) { // apply selection to all Mappable[] models = curFront.rootItem.items; for(int c = 0; c < 5; c++) { Mappable[] items = countries[c].rootItem.items; for(int i = 0; i < 10; i++) { if(curFront.enabled[i]) countries[c].enableItem(i); else countries[c].disableItem(i); ((DeweyL1)items[i]).contentsVisible = ((DeweyL1)models[i]).contentsVisible; ((DeweyL1)items[i]).contentsVisibleOld = ((DeweyL1)models[i]).contentsVisibleOld; } countries[c].updateLayout(); } } }//img = applyOff; triangle(750,20,750,40,765,30); //image(img,720,15); popMatrix(); } // --------------------------------------------------- // CHECKS WHETHER THE FRONT CHANGED // --------------------------------------------------- void checkFront() { if( curFront != getFront() ) { CountryMap oldFront = curFront; curFront = getFront(); oldFlag = newFlag; oldFrontOpacity = 255; newFrontOpacity = 0; newFlag = curFront.img; } // flag fade effect if(oldFrontOpacity > 0) oldFrontOpacity -=12; if(newFrontOpacity < 255) newFrontOpacity +=12; } // --------------------------------------------------- // RETURN THE MAP THAT IS CURRENTLY AT FRONT // --------------------------------------------------- CountryMap getFront() { if( rotY > PI*9/5 || rotY < PI*1/5 ) return countries[0]; else if( rotY > PI*1/5 && rotY < PI*3/5 ) return countries[1]; else if( rotY > PI*3/5 && rotY < PI*5/5 ) return countries[2]; else if( rotY > PI*5/5 && rotY < PI*7/5 ) return countries[3]; return countries[4]; } // --------------------------------------------------- // MOUSE PRESSED // --------------------------------------------------- void mousePressed() { if (curFront.zoomItem != null) { curFront.zoomItem.mousePressed(); } } // --------------------------------------------------- // MOUSE ROTATION // --------------------------------------------------- void mouseDragged() { if(mouseRelease && !mouseInRect(width/2-rectW,height/2+offsetH-rectH,width/2+rectW,height/2+offsetH+rectH,0,0) ) { rotY = rotY - (pmouseX - mouseX)*0.005; rectWaim = 330; rectHaim = 260; radiusAim = 330*1.45; mouseRelease = false; } else if (!mouseRelease) { rotY = rotY - (pmouseX - mouseX)*0.005; } } void mouseReleased() { rectWaim = 400; rectHaim = 320; radiusAim = 400*1.8; rotYaim = 2*PI*curFront.id/5; // focus the center of a tree map rotSmooth = true; mouseRelease = true; checkFront(); } // --------------------------------------------------- // FIXES ROTATION NUMBERS SMALLER/BIGGER THAN -/+ 2*PI // --------------------------------------------------- void fixRotation() { if(rotY > 2*PI) rotY -= 2*PI; if(rotY < 0) rotY += 2*PI; if(rotYaim > 2*PI) rotYaim -= 2*PI; if(rotYaim < 0) rotYaim += 2*PI; } // --------------------------------------------------- // SMOOTH ANIMATIONS FOR ROTATION AND ZOOMING // --------------------------------------------------- void smoothRotation() { if(rotSmooth == true) { if(rotY < rotYaim-rotStep/2 || ( rotYaim==0.0 && rotY < 2*PI-0.05 && rotY > PI) ) rotY += rotStep; else if(rotY > rotYaim+rotStep/2 && !( rotYaim==0.0 && rotY > PI ) ) rotY -= rotStep; else { rotY = rotYaim; rotSmooth = false; } } } void smoothZoom() { if( rectW != rectWaim ) { if( rectW > rectWaim+10 ) rectW-=10; else if( rectW < rectWaim-10 ) rectW+=10; else rectW = rectWaim; } if( rectH != rectHaim ) { if( rectH > rectHaim+8 ) rectH-=8; else if( rectH < rectHaim-8 ) rectH+=8; else rectH = rectHaim; } if( radius != radiusAim) { if(radius > radiusAim+40) radius -= 40; else if(radius < radiusAim-40) radius += 40; else radius = radiusAim; } } // --------------------------------------------------- // KEY PRESSED // --------------------------------------------------- void keyPressed() { // special keys (UP, DOWN, LEFT, RIGHT, SHIFT, ..) if (key == CODED) { switch(keyCode) { case LEFT: if(!rotSmooth) { rotYaim = rotY+PI*2/5; if(rotY == 2*PI*4/5 && rotYaim == 2*PI) rotYaim = 0.0; // fix for 2*PI--0 switch rotSmooth = true; rotStep = PI/20; } break; case RIGHT:if(!rotSmooth) { rotYaim = rotY-PI*2/5; if(rotY == 0.0 && rotYaim == -PI*2/5) rotY = 2*PI; // fix for 2*PI--0 switch rotSmooth = true; rotStep = PI/20; } break; default: break; } } // regular keys else { switch(key) { default: break; } } } // --------------------------------------------------- // FUNCTION TO SET LINEAR GRADIANTS // (by Ira Greenberg - http://processing.org/learning/basics/lineargradient.html ) // --------------------------------------------------- void setGradient(int x, int y, float w, float h, color c1, color c2){ beginShape(QUADS); fill(c1); vertex(x,y); vertex(x+w,y); fill(c2); vertex(x+w,y+h); vertex(x,y+h); endShape(); } // --------------------------------------------------- // IS THE MOUSE OVER A RECTANGLE? // --------------------------------------------------- boolean mouseInRect(float x1, float y1, float x2, float y2, float offsetW, float offsetH) { if((mouseX-offsetW > x1) && (mouseX-offsetW < x2) && (mouseY-offsetH > y1) && (mouseY-offsetH < y2)) return true; return false; } // --------------------------------------------------- // FORMATS A INTEGER TO A STRING WITH COMMA SEPERATION // --------------------------------------------------- String formatText(int num) { String s = Integer.toString(num); String sNew = ""; int d = s.length()-3; for(int i = s.length()-1; i >= 0; i--) { sNew = s.charAt(i)+sNew; if (i == d && i != 0) { sNew = ","+sNew; d -= 3; } } return sNew; }