//implementation of Graph.java //see: http://java.sun.com/applets/jdk/1.4/demo/applets/GraphLayout/example1.html //modified by Gerald Kogler //changelog: keyboard control added, pdf output added import processing.pdf.*; boolean saveOneFrame = false; //keys char keyEdge = 'e'; char keyStress = 's'; char keyShake = '1'; char keyScramble = '2'; char keyPdf = 'p'; int nnodes; Node nodes[] = new Node[100]; int nedges; Edge edges[] = new Edge[200]; boolean stress; boolean random; boolean showEdge; Node pick; boolean pickfixed; int mywidth = 800; int myheight = 600; color fixedColor = #FF0000; color selectColor = #AA0000; color edgeColor = #FFFFFF; //color nodeColor = color(250, 220, 100); color nodeColor = color(0,128,128,100); color stressColor = color(255); color arcColor1 = #FFFFFF; color arcColor2 = #AA0000; color arcColor3 = #FF0000; //color textColor = color(0,102,153); color textColor = color(65,73,32); color textgColor = color(100,1,0); /* color g1Color = color(225,225,25,80); color g2Color = color(194,208,255,80); color g3Color = color(255,255,200,80); color tallerColor = color(188,220,100); color g1Color = color(255,0,0,150); color g2Color = color(0,255,0,150); color g3Color = color(0,0,255,150); color tallerColor = color(25);255,255,200 */ color g1Color = color(225,225,25,80); color g2Color = color(194,208,255,80); color g3Color = color(188,220,100,80); color tallerColor = color(190,190,100); //buttons int edgeX = 10; int edgeY = myheight-30; int edgeSize = 10; int stressX = 100; int stressY = edgeY; int stressSize = 10; int randomX = 200; int randomY = edgeY; int randomSize = stressSize; int shakeX = 300; int shakeY = edgeY; int shakeWidth = 80; int shakeHeight = 20; int scrambleX = 400; int scrambleY = edgeY; int scrambleWidth = shakeWidth; int scrambleHeight = shakeHeight; int addX = 600; int addY = edgeY; int addWidth = shakeWidth; int addHeight = shakeHeight; //group info String mc1 = "casey"; String mc2 = "ben"; String mc3 = "zach"; String mc4 = "marius"; //graph7 String mc = "x_"; String g1 = "a_"; String g2 = "b_"; String g3 = "c_"; void setup(){ size(mywidth,myheight,P3D); PFont courier = loadFont("muktinarrowbold-14.vlw"); textFont(courier, 14); //read data line by line String[] data = loadStrings("data.txt"); for (int i=0; i < data.length; i++) { //is comment? if (data[i].indexOf("//") == -1){ //separate by coma String[] token = getToken(data[i]); for (int j = 0; j < token.length; j++){ String str = token[j]; int p1 = str.indexOf('-'); if (p1 > 0) { int len = 50; int p2 = str.indexOf('/'); if (p2 > 0) { //get edge weight (len) len = Integer.valueOf(str.substring(p2+1)).intValue(); str = str.substring(0, p2); } addEdge(str.substring(0,p1), str.substring(p1+1), len); } } } } } void draw(){ if(saveOneFrame) { beginRecord(PDF, "lines.pdf"); } relax(); background(50); //draw group nodes int xmc1=0;int ymc1=0; int xmc2=0; int ymc2=0; int xmc3=0; int ymc3=0; int xmc4=0; int ymc4=0; int xmin = width; int xmax = 0; int ymin = height; int ymax = 0; int xg1min = width; int xg1max = 0; int yg1min = height; int yg1max = 0; int xg2min = width; int xg2max = 0; int yg2min = height; int yg2max = 0; int xg3min = width; int xg3max = 0; int yg3min = height; int yg3max = 0; int wt,ht,wg1,hg1,wg2,hg2,wg3,hg3; for (int i = 0 ; i < nnodes ; i++) { int x = (int)nodes[i].x; int y = (int)nodes[i].y; //get min max values to draw taller circle if (x < xmin) xmin = x; if (x > xmax) xmax = x; if (y < ymin) ymin = y; if (y > ymax) ymax = y; String label = nodes[i].lbl.substring(0,mc.length()); //infovis group if (label.equals(g1)){ if (x < xg1min) xg1min = x; if (x > xg1max) xg1max = x; if (y < yg1min) yg1min = y; if (y > yg1max) yg1max = y; }else if(label.equals(g2)){ if (x < xg2min) xg2min = x; if (x > xg2max) xg2max = x; if (y < yg2min) yg2min = y; if (y > yg2max) yg2max = y; } else if (label.equals(g3)){ if (x < xg3min) xg3min = x; if (x > xg3max) xg3max = x; if (y < yg3min) yg3min = y; if (y > yg3max) yg3max = y; } } //calculate group circles int xg1 = xg1min+(xg1max-xg1min)/2; int yg1 = yg1min+(yg1max-yg1min)/2; int xg2 = xg2min+(xg2max-xg2min)/2; int yg2 = yg2min+(yg2max-yg2min)/2; int xg3 = xg3min+(xg3max-xg3min)/2; int yg3 = yg3min+(yg3max-yg3min)/2; wg1 = xg1max-xg1min+200; hg1 = yg1max-yg1min+200; wg2 = xg2max-xg2min+200; hg2 = yg2max-yg2min+200; wg3 = xg3max-xg3min+200; hg3 = yg3max-yg3min+200; //calculate taller circles int xt = xmin+(xmax-xmin)/2; int yt = ymin+(ymax-ymin)/2; wt = xmax-xmin+wg1; ht = ymax-ymin+hg1; //draw group node drawTaller("EXTEND hangar",xt,yt,wt,ht); drawGroup("infovis",xg1,yg1,wg1,hg1,g1Color); drawGroup("mixmax",xg2,yg2,wg2,hg2,g2Color); drawGroup("musicvis",xg3,yg3,wg3,hg3,g3Color); //draw edges if (showEdge){ for (int i = 0 ; i < nedges ; i++) { Edge e = edges[i]; int x1 = (int)nodes[e.from].x; int y1 = (int)nodes[e.from].y; int x2 = (int)nodes[e.to].x; int y2 = (int)nodes[e.to].y; int len = (int)abs(sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) - e.len); stroke((len < 10) ? arcColor1 : (len < 20 ? arcColor2 : arcColor3)) ; line(x1, y1, x2, y2); if (stress) { fill(stressColor); text(len, x1 + (x2-x1)/2, y1 + (y2-y1)/2); stroke(edgeColor); } } } //draw nodes for (int i = 0 ; i < nnodes ; i++) { nodes[i].draw(); } //drawButtons(); if(saveOneFrame) { endRecord(); saveOneFrame = false; } } void drawGroup(String label, int x, int y, int wg, int hg, color col){ noStroke(); //stroke(textgColor); fill(col); ellipseMode(CORNER); ellipse(x-wg/2, y-hg/2, wg, hg); /* fill(textgColor); textAlign(CENTER); text(label, x, y); */ } void drawTaller(String label, int x, int y, int wt, int ht){ noStroke(); //stroke(textgColor); fill(tallerColor); ellipseMode(CORNER); ellipse(x-wt/2, y-ht/2, wt, ht); fill(textgColor); PFont courier = loadFont("muktinarrowbold-14.vlw"); textFont(courier, 14); textAlign(CENTER); text(label, x, y+ht/2-5); text(label, x, y-ht/2+20); } void drawButtons(){ textAlign(LEFT); drawCheckbox("edges",showEdge,edgeX,edgeY,edgeSize); drawCheckbox("stress",stress,stressX,stressY,stressSize); //drawCheckbox("random",random,randomX,randomY,randomSize); drawButton("shake",shakeX,shakeY,shakeWidth,shakeHeight); drawButton("scramble",scrambleX,scrambleY,scrambleWidth,scrambleHeight); //drawButton("add",addX,addY,addWidth,addHeight); } void drawCheckbox(String ctext, boolean con, int cx, int cy, int csize){ fill(255); rect(cx,cy,csize,csize); text(ctext,cx+2*csize,cy+csize); if(con){ stroke(0); line(cx,cy,cx+csize,cy+csize); line(cx,cy+csize,cx+csize,cy); } } void drawButton(String btext, int bx, int by, int bwidth, int bheight){ fill(255); rect(bx,by,bwidth,bheight); fill(0); text(btext,bx+10,by+bheight-5); } boolean edgeOver(){ return (mouseX > edgeX && mouseX < edgeX+edgeSize && mouseY > edgeY && mouseY < edgeY+edgeSize); } boolean stressOver(){ return (mouseX > stressX && mouseX < stressX+stressSize && mouseY > stressY && mouseY < stressY+stressSize); } boolean randomOver(){ return (mouseX > randomX && mouseX < randomX+randomSize && mouseY > randomY && mouseY < randomY+randomSize); } boolean shakeOver(){ return (mouseX > shakeX && mouseX < shakeX+shakeWidth && mouseY > shakeY && mouseY < shakeY+shakeHeight); } boolean scrambleOver(){ return (mouseX > scrambleX && mouseX < scrambleX+scrambleWidth && mouseY > scrambleY && mouseY < scrambleY+scrambleHeight); } boolean addOver(){ return (mouseX > addX && mouseX < addX+addWidth && mouseY > addY && mouseY < addY+addHeight); } String[] getToken(String line){ //get number of comas int nextComa = line.indexOf(","); int num = 0; while (nextComa > -1){ nextComa = line.indexOf(",",nextComa+1); num++; } String[] token = new String[num+1]; //separate by coma nextComa = line.indexOf(","); int lastComa = -1; int pos = 0; while (nextComa > -1){ token[pos] = line.substring(lastComa+1,nextComa); lastComa = nextComa; nextComa = line.indexOf(",",lastComa+1); pos++; } token[pos] = line.substring(lastComa+1); //print(token); //println(); return token; } int findNode(String lbl) { for (int i = 0 ; i < nnodes ; i++) { if (nodes[i].lbl.equals(lbl)) { return i; } } return addNode(lbl); } int addNode(String lbl) { Node n = new Node(); n.x = 10 + random(380); n.y = 10 + random(380); n.lbl = lbl; nodes[nnodes] = n; return nnodes++; } void addEdge(String from, String to, int len) { Edge e = new Edge(); e.from = findNode(from); e.to = findNode(to); e.len = len; edges[nedges++] = e; } void relax() { for (int i = 0 ; i < nedges ; i++) { Edge e = edges[i]; float vx = nodes[e.to].x - nodes[e.from].x; float vy = nodes[e.to].y - nodes[e.from].y; float len = sqrt(vx * vx + vy * vy); len = (len == 0) ? .0001 : len; float f = (edges[i].len - len) / (len * 3); float dx = f * vx; float dy = f * vy; nodes[e.to].dx += dx; nodes[e.to].dy += dy; nodes[e.from].dx += -dx; nodes[e.from].dy += -dy; } for (int i = 0 ; i < nnodes ; i++) { Node n1 = nodes[i]; float dx = 0; float dy = 0; for (int j = 0 ; j < nnodes ; j++) { if (i == j) { continue; } Node n2 = nodes[j]; float vx = n1.x - n2.x; float vy = n1.y - n2.y; float len = vx * vx + vy * vy; if (len == 0) { dx += random(1); dy += random(1); }else if (len < 100*100) { dx += vx / len; dy += vy / len; } } float dlen = dx * dx + dy * dy; if (dlen > 0) { dlen = sqrt(dlen) / 2; n1.dx += dx / dlen; n1.dy += dy / dlen; } } for (int i = 0 ; i < nnodes ; i++) { Node n = nodes[i]; if (!n.fixed) { n.x += max(-5, min(5, n.dx)); n.y += max(-5, min(5, n.dy)); } if (n.x < 0) { n.x = 0; }else if (n.x > width) { n.x = width; } if (n.y < 0) { n.y = 0; }else if (n.y > height) { n.y = height; } n.dx /= 2; n.dy /= 2; } } void mousePressed() { //if (!edgeOver() && !stressOver() && !randomOver() && !shakeOver() && !scrambleOver() && !addOver()){ float bestdist = Float.MAX_VALUE; int x = mouseX; int y = mouseY; for (int i = 0 ; i < nnodes ; i++) { Node n = nodes[i]; float dist = (n.x - x) * (n.x - x) + (n.y - y) * (n.y - y); if (dist < bestdist) { pick = n; bestdist = dist; } } pickfixed = pick.fixed; pick.fixed = true; pick.x = x; pick.y = y; //} } void mouseReleased() { if(edgeOver()){ actionEdge(); }else if(stressOver()){ actionStress(); }else if (randomOver()){ actionRandom(); }else if (shakeOver()){ actionShake(); }else if (scrambleOver()){ actionScramble(); }else if (addOver()){ actionAdd(); }else if (pick != null) { pick.x = mouseX; pick.y = mouseY; pick.fixed = pickfixed; pick = null; } } void keyPressed(){ if (key == keyEdge) actionEdge(); else if (key == keyStress) actionStress(); else if (key == keyShake) actionShake(); else if (key == keyScramble) actionScramble(); else if (key == keyPdf) actionPdf(); } void actionEdge(){ showEdge = !showEdge; } void actionStress(){ stress = !stress; } void actionRandom(){ random = !random; } void actionShake(){ for (int i = 0 ; i < nnodes ; i++) { Node n = nodes[i]; if (!n.fixed) { n.x += random(80) - 40; n.y += random(80) - 40; } } } void actionScramble(){ for (int i = 0 ; i < nnodes ; i++) { Node n = nodes[i]; if (!n.fixed) { n.x = 10 + random(width-20); n.y = 10 + random(height-20); } } } void actionAdd(){ addEdge("a1","a0",50); } void actionPdf(){ saveOneFrame = true; } void mouseDragged() { //if (!edgeOver() && !stressOver() && !randomOver() && !shakeOver() && !scrambleOver() && !addOver()){ pick.x = mouseX; pick.y = mouseY; //} } class Node { float x; float y; float dx; float dy; boolean fixed; String lbl; int w = 100; int h = 20; int tw = 200; int th = 20; void draw(){ //int x = (int)this.x; //int y = (int)this.y; //fill(color((this == pick) ? selectColor : (this.fixed ? fixedColor : nodeColor))); //int w = fm.stringWidth(this.lbl) + 10; //int h = fm.getHeight() + 4; noStroke(); //stroke(edgeColor); String start = this.lbl.substring(0,mc.length()); if (start.equals(mc) || start.equals(g1) || start.equals(g2) || start.equals(g3)){ fill(nodeColor); //draw ellipse ellipseMode(CORNER); ellipse(x - w/2, y - h / 2, w, h); //draw rect //rect(x - w/2, y - h / 2, w-1, h-1); fill(textColor); textAlign(CENTER); text(this.lbl.substring(mc.length()), x, y+h/2-4); }else{ fill(textgColor); textAlign(CENTER); text(this.lbl, x, y+h/2-4); } if (overNode()) drawToolbox(); } boolean overNode(){ return (mouseX > x-w/2 && mouseX < x+w/2 && mouseY > y-h/2 && mouseY < y+h/2); } void drawToolbox(){ stroke(200); fill(100); rect(mouseX, mouseY, tw, th); fill(200); text("info about "+this.lbl.substring(mc.length()),mouseX+tw/2,mouseY+th-4); } } class Edge { int from; int to; float len; }