We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
Page Index Toggle Pages: 1
Tag Cloud (Read 3109 times)
Tag Cloud
Jun 26th, 2008, 8:29pm
 
I'm building a rather epic sketch, and part of it needs a tag cloud. Through some help from the blue Processing book, I figured out the key essential functions for this.

To find the pixel width of some text, use the following Code:
word_width = textWidth(word); 



But in the case where you are using objects, and the words are many different sizes, you have to use this Code:
textFont(yourfont, word_size);
word_width = textWidth(word);


Quote:
Also, it's imporant to note that text is rendered from its bottom-left!
I solved that problem by ignoring that problem until the word needed to actually be drawn, in which case I used the following Code:
text(tags[id], x-word_width/2, y+font_size*baseline_ratio); 



In any case, I got a prototype working. I share with you my source which is heavily based on the Bouncy Bubbles example, but I'm fairly proud to actually understanding how it works now!

As a friendly reminder: Before you try this, save it, and make a font from
[Tools] > [Create Font]
in the menu, and then change the fontFile variable to match with your vlw.If you don't use Comic Sans, you'll also need to fiddle with the baseline ratio a smidge, but it's not that important.


Code:
/**
* Tag Cloud by Wray Bowling
*/

PFont font;
String fontFile = "ComicSans-48.vlw";
float baseline_ratio = 0.28;
int large_font = 48;
int small_font = 12;

String[] tags  = {  "Family Guy", "House", "30 Rock", "The Simpsons", "The Angry Beavers", "Pete and Pete", "American Idol", "Firefly", "Batman", "Bob", "Extreme Makeover Home Edition",  "Arrested Development", "That 70's show", "The Price is Right"};
int[] tagtally = {  50, 20, 10, 10, 50, 60, 80, 40, 5, 2, 6, 2, 4, 8};
int most = max(tagtally);
int least = min(tagtally);

float spring = 0.02;
int numWords = tags.length;
Tag[] Cloud = new Tag[numWords];


void setup()
{
 size(640, 320);
 smooth();
 font = loadFont(fontFile);
 textFont(font);
 for (int i = 0; i < numWords; i++) {
   Cloud[i] = new Tag(Cloud, i, tags[i], tagtally[i], width/2, height/2);
 }
}

void draw()
{
 background(0);
 stroke(255,20);
 line(0,0,width,height);
 line(width,0,0,height);
 line(0, height/2 - 24,width, height/2 - 24);
 line(0, height/2 + 24,width, height/2 + 24);
 for (int i = 0; i < numWords; i++) {
   Cloud[i].collide();
   Cloud[i].move();
   Cloud[i].display();  
 }
}

class Tag {
 float x, y;
 float word_width;
 int font_size;

 float vx = 0;
 float vy = 0;
 int id;
 Tag[] others;

 Tag(Tag[] others, int id, String word, int occurance, int x, int y) {
   this.x = x + random(-100, 100);
   this.y = y + random(-100, 100);
   font_size = int(map(occurance, least, most, small_font, large_font));
   textFont(font, font_size);
   this.word_width = textWidth(word);

   this.id = id;
   this.others = others;
 }

 /* I mucked with this function pretty heavily, but to no avail :( */

 void collide() {
   for (int i = id + 1; i < numWords; i++) {
     float dx = others[i].x + others[i].word_width/2 - x + word_width/2;
     float dy = others[i].y + others[i].font_size/2 - y + font_size/2;

     float minDistX = word_width + others[i].word_width;
     float minDistY = font_size + others[i].font_size;

     if ( (dx < minDistX) && (dy < minDistY) ){
       float angle = atan2(dy, dx);
       float targetX = x + cos(angle) * minDistX;
       float ax = (targetX - others[i].x) * spring;
       vx -= ax;
       others[i].vx += ax;

       float targetY = y + minDistY;
       float ay = (targetY - others[i].y) * spring;
       vy -= ay;
       others[i].vy += ay;
     }

   }  
 }

 void move() {
   vx *= 0.1;
   vy *= 0.75;
   x += vx;
   y += vy;
   if (x + word_width/2 > width) {
     x = width - word_width/2;
     vx *= -0.5;
   }
   else if (x - word_width/2 < 0) {
     x = word_width/2;
     vx *= -0.5;
   }
   if (y + font_size/2 > height) {
     y = height - font_size/2;
     vy *= -0.5;
   }
   else if (y - font_size/2 < 0) {
     y = font_size/2;
     vy *= -0.5;
   }
 }

 void display() {
   fill(255,30);
   rectMode(CENTER);
   rect(x,y,word_width,font_size);
   fill(255);
   textFont(font, font_size);
   text(tags[id], int(x - word_width/2), int(y + font_size * baseline_ratio));
 }
}


I would be happy to hear suggestions on how it could be improved. First of all, it would be great to have the draw loop eventually stop to save on CPU cycles.
Re: Tag Cloud
Reply #1 - Jul 18th, 2008, 9:26pm
 
:/ Thanks everybody for all the help. Not. It's been nearly a month, I'm not quite done, but at least it isn't spazzing out anymore.

Prototype Code:
PFont font;
String fontFile = "PTF-Nordic-48.vlw";
float baseline_ratio = 0.4;

float angle = 0;
float outwards = 1;

String[] tags = { "The Office", "The Colbert Report", "Battlestar Galactica", "weeds", "californication", "scrubs", "CSI", "Smallville", "House", "Family Guy", "Daily Show", "US", "Boondocks", "Venture Brothers", "Flight of the Conchords", "Firefly", "Angel", "Grey's Anatomy", "Venture Bros", "King of The Hill", "America's Next Top Model", "Project Runway", "My Name is Earl", "Dirty Jobs", "Eureka", "Friends", "Frasier", "Sex and the City", "Food Network", "South Park", "Seinfeld", "The Daily Show", "Colbert Report", "The Riches", "LOST", "Aqua Teen Hunger Force", "Arrested Development", "Freaks and Geeks", "MythBusters", "Dexter", "Six Feet Under", "Home Movies", "Pete & Pete", "Extras", "tears", "Law and Order", "Pete and Pete", "Invader Zim", "Jericho", "The Boondocks", "The Simpsons", "Futurama", "jeopardy", "King Of Queens", "American Dad", "Dr. Who", "That 70's Show", "Avatar", "Fraggle Rock", "History Channel", "24", "Kids in the Hall", "Strangers With Candy"};
int[] tagtally = { 16, 4, 3, 8, 2, 15, 5, 3, 8, 16, 3, 2, 2, 2, 5, 5, 2, 9, 2, 2, 4, 4, 3, 2, 2, 5, 2, 4, 2, 2, 3, 6, 7, 2, 9, 4, 7, 2, 2, 6, 3, 5, 2, 2, 2, 2, 3, 3, 2, 3, 5, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
int most = max(tagtally);
int least = min(tagtally);
int large_font = 28;
int small_font = 8;

Box[] boxes = new Box[tags.length];

void setup(){
size(420,210);
smooth();
rectMode(CENTER);
font = loadFont(fontFile);

for (int i = 0; i < tags.length; i++) {
int h_test = int(map(tagtally[i], least, most, small_font, large_font));
textFont(font, h_test);
int w_test = int(textWidth(tags[i]));

boxes[i] = new Box(tags[i],w_test,h_test);
}
}

void draw(){
background(0);
pushMatrix();
scale(0.82,0.82);
translate(0.12*width, 0.15*height);
for(int i=0; i<boxes.length; i++){
boxes[i].collide(i);
fill(0);
boxes[i].render(i);
}
popMatrix();

//check for stopping
int completed = 0;
for(int i=0; i<boxes.length; i++){
if(boxes[i].frozen)
completed++;
}
println("completed:" + completed);
if(completed == boxes.length){
noLoop();
}
}

void mouseReleased(){
boxes[0].x = random(20,380);
boxes[0].y = random(20,180);
}


Tag Class Code:
class Box{
public float x, y, w, h, volume, attempts;
String word;
boolean frozen = true;
color c;

Box(String word, int w, int h){
this.word = word;
this.x = cos(angle) * log(outwards) * 42 + 210;
this.y = sin(angle) * log(outwards) * 21 + 105;
angle -= 61;
outwards += 0.4;
this.w = w;
this.h = h;
this.volume = w*h;
this.c = color(random(100,255), random(100,255), random(100,255));
}

void render(int id){
/* noFill();
stroke(0,20);
rect(x,y,w,h); */
fill(c);
textFont(font, h);
text(tags[id], int(x - w/2), int(y + h * baseline_ratio));
}

void collide(int i){
frozen = true;
for(i+=1; i<boxes.length; i++){
float dx = boxes[i].x - this.x;
float dy = boxes[i].y - this.y;
float tx = boxes[i].w/2 + this.w/2;
float ty = boxes[i].h/2 + this.h/2;
if( (abs(dx) < tx) && (abs(dy) < ty) ){
float me = 0.5 * boxes[i].volume / this.volume;
float you = 0.5 * this.volume / boxes[i].volume;
attempts++;

println("for collision "+i+", I am "+me+", & you are "+you);
frozen = false;
if(dx > 0){
this.x -= me * 1.5;
boxes[i].x += you * 1.5;
}else{
this.x += me * 1.5;
boxes[i].x -= you * 1.5;
}

if(dy > 0){
this.y -= me;
boxes[i].y += you;
}else{
this.y += me;
boxes[i].y -= you;
}

if(this.attempts > 500){
this.x = random(0,width);
this.y = random(0,height);
this.attempts = 0;
}
}
}
}
}


And here's my font file.
Re: Tag Cloud
Reply #2 - Nov 10th, 2009, 5:47am
 
Not sure if this is still relevant to you, but here's an example of a Processing Tag Cloud sketch using Bouncy Bubble: w w w.   bryanleister.   com/processing-tag-cloud

( Sorry about the URL - I haven't posted enough here to create a real URL yet. )
Page Index Toggle Pages: 1