Blog Header by WordCram

Here is, more-or-less, the sketch I used to generate the new WordCram blog header. About half of the sketch is Processing setup and interaction, so I could keep running it until I found one I liked.

When I get around to it, I want to make it easier to load a directory of source code files, but for now, cat `find -name *.java` > src.txt is good enough.

import wordcram.*;
import wordcram.text.*;

WordCram wc;

void setup() {
  colorMode(HSB);
  size(940, 198);
  background(0);

  initWordCram();
}

void initWordCram() {
  String license = join(loadStrings("LICENSE"), " ").toLowerCase();

  wc = new WordCram(this)
      .fromTextFile("src.txt")
      .withStopWords(StopWords.ENGLISH + StopWords.JAVA + license)
      .withFont("Droid Sans Mono")
      .sizedByWeight(12, 60)
      .withPlacer(new WordPlacer() {
          public PVector place(Word w, int wordCount, int numWords,
                               int wordWidth, int wordHeight,
                               int fieldWidth, int fieldHeight) {

            // x = weight, heavies on the left
            float x = (fieldWidth - wordWidth) * (1 - (float)w.weight);
            x *= random(0.6, 1); // fade them back a bit -- don't clump on the right

            // y = random around center horiz. line. Heavier words are less random.
            float y = random(1) *
                      (1 - (float)w.weight) *
                      (fieldHeight - wordHeight) / 2;
            y += (fieldHeight - wordHeight) / 2;

            return new PVector(x, y);
          }
      })
      .withAngler(new WordAngler() {
          public float angleFor(Word w) {

            // swing between -30 and 30 degrees -- heavy words swing less
            return (1 - (float)w.weight) * random(radians(-30), radians(30));
          }
      });
}

void draw() {
  if (wc.hasMore()) {
    wc.drawNext();
  }
  else {
    save("wordcram.png");
    println("done");
    noLoop();
  }
}

void mouseClicked() {
  background(0);
  initWordCram();
  loop();
}

Posted in examples, wordcram | 1 Comment

WordCram Release 0.3 is Almost Ready

It may seem like October’s been a quiet month for WordCram, but the 0.3 release is almost ready. I just built a daily release (.zip, or .tar.gz) that has more javadocs, new examples, and some new methods that control text parsing: upperCase(), lowerCase(), and keepCase(), and includeNumbers() and excludeNumbers().  Not to mention building a WordCram from a webpage, and a newer, easier way to configure your WordCram.

I’m hoping to release 0.3 late this week, but in the meantime, you can try out the Halloween daily builds, and let me know what you think.

Posted in release, wordcram | Leave a comment

WordCram Release 0.2

The 0.2 Release has some nice changes over 0.1: it’s easier to use, it’s a bit faster, and the layouts should look better.

The details:

  • Words should stop running off the edge of the screen.  We can make this better, and we will, and soon, but it’s there now.
  • WordCram will sort your Words, and scale their weights for you.  If you’re  using the TextSplitter, this doesn’t affect you.  But if you’ve been passing in your own array of Words, taking care to sort them, and making sure the biggest word had a weight of 1, you can take a break, because WordCram will take over from here.  Give your Words whatever weights you want, and it’ll figure out how big they are, relative to each other.
  • Two minor API changes:
    • WordCram.draw() is now WordCram.drawAll(), to make it clear that it’ll draw ALL the words — which could take some time.
    • The signature for WordPlacer.place() has changed.  Where it used to take int imageSize and PGraphics host, it now takes int imageWidth, int imageHeight and int fieldWidth, int fieldHeight.  Hopefully this makes it easier to write your own WordPlacers.
  • Down in the guts, WordCram is rendering with java.awt.Shapes.  This matters because it makes it easier to keep words on-screen, easier to place them where you want, and eventually, easier and faster to re-color and re-layout a WordCram.

Add in a few performance boosts and bug fixes, and you’ve got a new release.  Check it out, play with it, and let me know what you think.  There’s still tons to do, if you’d like to help out.

Posted in release, wordcram | 5 Comments

Get Acquainted With WordCram

(2010-09-27: Updated for WordCram 0.2.)

WordCram is customizable, but you have to know how it works, what each piece does, to get what you’re after.

The whole idea of a word cloud is to draw a bunch of words according to their weight. With WordCram, you hand it the weighted words and your Processing sketch, and it’ll generate the word cloud image for you.

WordCram’s drawAll() method draws all the words at once. (This can take a while if you feed it a lot of words.) Or if the WordCram hasMore() words to draw, you can use drawNext() to draw them one-by-one. Try calling drawNext() once per frame, and you can watch WordCram position each word.

// Draw them all at once (might take a while):
wordcram.drawAll();

// Or, draw them individually:
if (wordcram.hasMore()) {
   wordcram.drawNext();
}

Weighting Words

Word clouds often illustrate word frequency in some piece of text: more frequent words are bigger, giving you a rough idea what the text is about. WordCram comes with a TextSplitter that counts up the words in any String or String array, which works well with Processing’s loadStrings(filename) method:

Word[] weightedWords = new TextSplitter().split(loadStrings("lotsOfText.txt"));

But WordCram can accept any array of Word objects, so you can make your own array with whatever values you like:

Word[] populationSizes = new Word[] {
    new Word("China", 1339360000),
    new Word("India", 1187340000),
    new Word("USA", 310192000),
    ...
};

Future plans include loading weighted Words from your delicious tags, a web page, a Twitter stream, and an RSS feed.

Getting the Look You Want

When creating a word cloud, it’d be nice if you could pick each word’s font and color, and say about where in the picture it should go, and what angle it should be at. WordCram lets you do that. You give it a WordColorer, and when WordCram is ready to draw “careening” (weight 0.4317), it’ll ask the WordColorer what color to draw it in. You can get WordColorers from the Colorers class:

WordCram wordcram = new WordCram(...
    // garish Christmas colors
    Colorers.pickFrom(color(255,0,0), color(0,255,0)),
    ...);

WordCram otherWordcram = new WordCram(...
    // three grays
    Colorers.pickFrom(color(50), color(125), color(200)),
    ...);

Besides the WordColorer, a WordCram needs a WordFonter, a WordPlacer, a WordAngler, and a WordSizer. Each one has a corresponding class with some pre-fabs, to get you started:

pre-fab class methods
Colorers pickFrom(), twoHuesRandomSats()
Fonters pickFrom(), alwaysUse()
Placers horizLine(), centerClump(), upperLeft()
Anglers pickFrom(), alwaysUse(), random(), horiz(), mostlyHoriz(), upAndDown(), hexes()
Sizers byWeight(), byRank()

Colorers and Fonters

As we’ve seen, WordColorers tell WordCram how to color each word. Colorers.pickFrom() can take any number of Processing colors, and will return one at random. Colorers.twoHuesRandomSats() picks two random hues, and randomly saturates one of them for each word. You have to pass it the Processing sketch, so it can use the sketch’s color() method: Colorers.twoHuesRandomSats(this).

WordFonters tell WordCram which PFont to use for each word. Fonters.pickFrom() words like Colorers’ version — it can take any number of PFonts. Fonters.alwaysUse() will always use the PFont you give it.

Placers

Placers are more complicated, but only a little bit. They give WordCram the x and y coordinates where each word should appear, as a PVector. (The word won’t appear exactly there, it’ll be nudged a bit until it doesn’t overlap with other words.) Placers.horizLine() tries to place words along the horizontal axis, Placers.centerClump() places them in more of a circle, and Placers.upperLeft() puts them all in the corner.

Placers are a weak spot for WordCram right now — I’m not entirely happy with most of them, and I expect the guts of WordCram that use Placers to change in the future. That said, the existing Placers, and how you use them, will probably stay the same. (Hopefully they’ll just work better.)

Anglers

Anglers tell WordCram what angle each word should be drawn at, specified in radians. Right now there are more pre-fab Anglers than anything else, but they’re all pretty simple.

pickFrom() and alwaysUse() work just like they do for Fonters. random() will return a random angle for each word. horiz() makes them all horizontal, but mostlyHoriz() draws a few going up and down, and upAndDown() draws them all up and down. hexes() puts them all on increments of 60°, so it looks something like a snowflake or a hex grid.

Sizers

Sizers tell WordCram what size to draw each word in. Their answer is passed to Processing’s textFont method, along with the PFont returned by the Fonter. The two built-in Sizers, byWeight and byRank, both take a minimum and maximum size. (A word’s weight is its frequency; when you sort words by their weight, a word’s rank is its order in the list.)

Eventually, WordCram will have a “smart sizer” that decides how to size words based on the size of your sketch, the number of words, and the distribution of their weights, but you’ll always be able to use a different one if you need to.

Rolling Your Own

There’s nothing special about those built-in components — if you don’t like any of them, you can roll your own. For instance, here’s a WordSizer that makes “careening” huge, and everything else tiny.

class CareeningBigWordSizer implements WordSizer {
   public int sizeFor(Word w, int wordRank, int wordCount) {
      if (w.word.equals("careening")) {
         return 300;
      } else {
         return 3;
      }
   }
}

(If that looks weird, don’t sweat it — you can always use the built-ins. But if you’re curious, and you really want to use WordCram to the fullest, reading about Java’s interfaces or the Strategy pattern should get you started.)

Each one is an interface with only one method:

interface its only method
WordColorer int colorFor(Word w)
WordFonter PFont fontFor(Word w)
WordPlacer PVector place(Word w, int wordIndex, int wordsCount, int wordImageWidth, int wordImageHeight, int fieldWidth, int fieldHeight)
WordAngler float angleFor(Word w)
WordSizer float sizeFor(Word w, int wordRank, int wordCount)

That’s It!

There’s not much more to WordCram. I might do a tutorial about WordCram’s innards (bounding box trees, collision detection, and nudging words), but you don’t need to know about all that unless you’re in the innards, or rolling your own WordPlacer, and that’s another tutorial.

If anything doesn’t make sense, let me know, and I’ll do my best to clear it up.

Posted in tutorial, wordcram | 2 Comments

WordCram Release 0.1

The WordCram 0.1 release is here.  Don’t let the low release number fool you — it’s pretty solid code.  Since the initial release, I’ve improved performance, and cleaned up the API, making it faster, and easier to use.

Here’s the flavor of the API change…from this:

WordCram wordCram = new WordCram(this,
    new TextSplitter().split(loadStrings("tao-te-ching.txt")),
    Fonters.FonterFor(createFont("sans", 1)),
    Sizers.byWeight(5, 60),
    Colorers.TwoHuesRandomSats(this),
    Anglers.MostlyHoriz,
    new CenterClumpWordPlacer(),
    new SpiralWordNudger());

while (wordCram.hasMore()) {
    wordCram.drawNext();
}

to this:

WordCram wordCram = new WordCram(this,
    new TextSplitter().split(loadStrings("tao-te-ching.txt")),
    Fonters.alwaysUse(createFont("sans", 1)),
    Sizers.byWeight(5, 60),
    Colorers.twoHuesRandomSats(this),
    Anglers.mostlyHoriz(),
    Placers.centerClump());

wordCram.draw();

A very few further API tweaks are planned, but I wanted to get a first release out for you to play with, and to write some tutorials against.

Download the bits, give it a test-drive, and let me know what you think.

Posted in release, wordcram | Leave a comment

Hello, WordCram: Word Clouds in Processing

Today I’m releasing WordCram, an open-source Processing library that lets you generate word clouds. It does the heavy lifting — text analysis, collision detection, bin-packing — for you, so you can focus on making your word clouds as beautiful, as revealing, or as silly as you like.

I’ve been working on WordCram for about a month, and I think it’s ready to be out of the lab. Download the bits, and try it out.

Here’s the flavor of it:

import wordcram.*;
import wordcram.text.*;

void setup() {
  size(600, 400);
  background(0);

  WordCram wordCram = new WordCram(this,
    new TextSplitter().split(loadStrings("tao-te-ching.txt")),
    Fonters.FonterFor(createFont("sans", 1)),
    Sizers.byWeight(5, 60),
    Colorers.TwoHuesRandomSats(this),
    Anglers.MostlyHoriz,
    new CenterClumpWordPlacer(),
    new SpiralWordNudger());

  while (wordCram.hasMore()) {
    wordCram.drawNext();
  }
}

The result:

A wordcram of the Tao Te Ching

You can see by the code — Colorers, Anglers, Placers — that it’s pretty pluggable.  But there’s still work to do — better performance, a cleaner API, sensible defaults for some of those components — so if you like what you see and want to help out, tweet me or email me at wordcram-at-gmail.

In the meantime, I’ll be working on smoothing out those components and putting up some tutorials here.

WordCram is inspired by Wordle, and built for Processing, so a big thank-you to Jonathan Feinberg, Ben Fry, and Casey Reas.

Posted in examples, wordcram | 2 Comments