// force direction experiment #9 // aaron siegel, 9-25-05 // a circle is created and placed on the stage upon clicking. // circles reorganize according to force-directed placement. PFont font; boolean globalover = false; int dotcount = 0; int dotmax = 100; float spacing; float temp = 1.0; Circle[] dot = new Circle[dotmax]; class Circle { boolean locked = false; float x, y; float xvector, yvector; String title; int num; Circle(int dotnum){ x = mouseX; y = mouseY; num = dotnum; title = str(dotnum); xvector = 1; yvector = 1; } void push(){ float xdiff; float ydiff; for(int i=1; i <= dotcount; i++){ // iterate through all dots... if(i != num){ // as long as it's not this dot if((x > dot[i].x) && (y > dot[i].y)){ // right and below xdiff = x - dot[i].x; ydiff = y - dot[i].y; if(sqrt(sq(xdiff) + sq(ydiff)) < spacing){ x += xdiff/spacing; y += ydiff/spacing; } } else if((x < dot[i].x) && (y < dot[i].y)){ // left and above xdiff = dot[i].x - x; ydiff = dot[i].y - y; if(sqrt(sq(xdiff) + sq(ydiff)) < spacing){ x -= xdiff/spacing; y -= ydiff/spacing; } } else if((x > dot[i].x) && (y < dot[i].y)){ // right and above xdiff = x - dot[i].x; ydiff = dot[i].y - y; if(sqrt(sq(xdiff) + sq(ydiff)) < spacing){ x += xdiff/spacing; y -= ydiff/spacing; } } else if((x < dot[i].x) && (y > dot[i].y)){ // left and below xdiff = dot[i].x - x; ydiff = y - dot[i].y; if(sqrt(sq(xdiff) + sq(ydiff)) < spacing){ x -= xdiff/spacing; y += ydiff/spacing; } } } } } void pull(int i){ float xdiff; float ydiff; // draw a line to the next dot stroke(180); line(x,y, dot[i].x,dot[i].y); // move towards the next dot if((x > dot[i].x) && (y > dot[i].y)){ // right and below xdiff = x - dot[i].x; ydiff = y - dot[i].y; if(sqrt(sq(xdiff) + sq(ydiff)) > spacing){ x -= xdiff/spacing; y -= ydiff/spacing; } } else if((x < dot[i].x) && (y < dot[i].y)){ // left and above xdiff = dot[i].x - x; ydiff = dot[i].y - y; if(sqrt(sq(xdiff) + sq(ydiff)) > spacing){ x += xdiff/spacing; y += ydiff/spacing; } } else if((x > dot[i].x) && (y < dot[i].y)){ // right and above xdiff = x - dot[i].x; ydiff = dot[i].y - y; if(sqrt(sq(xdiff) + sq(ydiff)) > spacing){ x -= xdiff/spacing; y += ydiff/spacing; } } else if((x < dot[i].x) && (y > dot[i].y)){ // left and below xdiff = dot[i].x - x; ydiff = y - dot[i].y; if(sqrt(sq(xdiff) + sq(ydiff)) > spacing){ x += xdiff/spacing; y -= ydiff/spacing; } } } void puller(){ // find the next node and pull towards it if(num != dotcount){ this.pull(num+1); } else { //this.pull(1); } } void borders(){ // don't go past the edges if(x > width-5){ x = width-5; } else if(x < 5){ x = 5; } if(y > height-5){ y = height-5; } else if(y < 5){ y = 5; } } boolean over(){ float disX = x - mouseX; float disY = y - mouseY; if(sqrt(sq(disX) + sq(disY)) < 5){ return true; } else { return false; } } void update(){ if(mousePressed && over()){ locked = true; } if(!mousePressed){ locked = false; } } void draw(){ if(locked){ x = mouseX; y = mouseY; } fill(255); stroke(0); ellipse(x, y, 10, 10); fill(0,150,255); text(title,x+5,y-5); } } //-------------------------------------------------------------------------------------------------------------! void setup(){ size(300,300); smooth(); font = loadFont("04b08.vlw"); textFont(font, 8); } void draw(){ background(200); stroke(150); if(dotcount > 0){ for(int i=1; i<=dotcount; i++){ dot[i].push(); dot[i].puller(); dot[i].borders(); dot[i].update(); dot[i].draw(); } } } void mouseReleased(){ for(int i=1; i<=dotcount; i++){ if(dot[i].over()){ globalover = true; } } if(!globalover){ dotcount++; dot[dotcount] = new Circle(dotcount); // time for the fructerman\reingold algorithm // k = C*sqrt(area/dotcount); spacing = 0.75*sqrt(width*height/dotcount); } globalover = false; }