WordCram 0.6.1 Released

WordCram 0.6.1 is here, with improvements to the examples, and some bug fixes.

Two new examples show how to use the new ShapeBasedPlacer, and the Event callbacks introduced in 0.6.0. Also, some examples were still using the old WordCram.NO_SPACE constants – thanks to Pierre Haessig for a fix. Look under File > Examples > Contributed Libraries > WordCram to see them.

Happy word cramming!

Posted in release | Leave a comment

WordCram 0.6.0 released

WordCram 0.6.0 is here, and brings with it arbitrary shape-based layouts, SVG export, event callbacks so you can monitor your sketch’s progress, and as usual, a few bug fixes. Special thanks to simpsus for helping out so much!

Event callbacks

Ever wonder what WordCram’s doing in there? Ever have a WordCram just hang, making your fan whir, and you have no idea why it’s taking so long? Callbacks can help you figure it out.

Add methods like this to your Processing sketch, the way you do for mouse or keyboard events:

void wordSkipped(Word word) {
  println("Skipped: " + word);
}
void wordDrawn(Word word) {
  println("Just drew: " + word);
}

As your sketch runs, you’ll see the print-outs. You can run any kind of code in there, too.

You can see the full list of callbacks in the javadoc for the Observer class.

SVG Export

WordCram has been able to export to PDF since the 0.5.0 release, but for a real scalable image, SVG is much better.

Now, it’s dirt-simple to export to SVG. Just add this to the WordCram setup:

toSvg("my-wordcram.svg", width, height)

…as in:

import wordcram.*;

size(800, 600);

try {
  new WordCram(this)
    .toSvg("nytimes.svg", width, height)
    .fromWebPage("http://nytimes.com")
    .drawAll();
}
catch (java.io.FileNotFoundException x) {
  println(x);
}

(Unfortunately, you have to wrap it in the try-catch. I’ll work on smoothing that out for 0.6.1.)

Here’s what you get:

SVG WordCram from http://nytimes.com

Shapes

This is the big one. This is the last major feature WordCram lacked, one that I really wanted to add.

Start with an image like this:

batman

Load the image, extract any color from it into a Shape (here, I’m using black), and make a ShapeBasedPlacer for it:

PImage image = loadImage("batman.gif");
Shape imageShape = new ImageShaper().shape(image, #000000);
ShapeBasedPlacer placer = new ShapeBasedPlacer(imageShape);

Use that ShapeBasedPlacer as both the WordPlacer and the WordNudger in your WordCram:

background(#110000);
new WordCram(this).
  fromWebPage("http://en.wikipedia.org/wiki/Batman").
  withPlacer(placer).
  withNudger(placer).
  sizedByWeight(12, 120).
  withFont("Futura-CondensedExtraBold").
  withColor(#DBC900).  
  drawAll();

And voilà:

batman

It can be slow sometimes, though the event callbacks can help you see where it’s spending its time, and tune accordingly. And the API isn’t totally there yet – I’ll work on smoothing out both for the future. But the capability is there, so give it a try.

Download it!

Download WordCram 0.6.0, and get cramming.

If you tweet, and you make some WordCrams and tweet them, let @wordcram know!

Posted in examples, release | 3 Comments

WordCram 0.5.7 released

WordCram 0.5.7 is out, and it’s about making things just a little easier.

  • You can make a WordCram from multiple text sources, by piling them on top of each other. Before, WordCram would forget about your old text source, and only remember the last one you told it about; now, it remembers, and uses, them all.
  • If you’re loading an HTML document, you can pass a CSS selector to narrow down the content. Handy for pages with navigation, headers, and footers.
  • Words now have, by default, 1 pixel of space between them. It used to be zero, which made for some pretty dense images. If you want, you can always go back by calling withWordPadding(0).

This is a quick release, because simpsus is helping me smooth out making WordCrams from shapes, which will be the next release:

north-america-wordcram

In the meantime, download the zip or the tar, and have fun cramming!

Posted in release | 2 Comments

Why Don’t All My Words Show Up?

You installed WordCram, gave it a list of words, and ran it – and some of your words are missing. What’s up?

WordCram will try really hard to place all your words, but if it has a hard time placing one, it’ll eventually move on to the next word. (If it didn’t, you might still be waiting now for a WordCram you started to render a year-and-a-half ago.) Normally, this is ok, because WordCram isn’t rendering mission-critically important data – it’s for fun.

But if it’s not rendering some words that you really want to see, what can you do?

The first thing is to understand why it’s skipping your words. Paste this code into your sketch, right after yourWordCram renders:

 for (Word word : yourWordCram.getSkippedWords()) {
    switch (word.wasSkippedBecause()) {
    case WordCram.SHAPE_WAS_TOO_SMALL:
      println(word.word + ": shape was too small");
      break;
    case WordCram.WAS_OVER_MAX_NUMBER_OF_WORDS:
      println(word.word + ": was over max # of words");
      break;
    case WordCram.NO_SPACE:
      println(word.word + ": no room to place it");
      break;
    }
  }

Let’s look at each of these cases:

  • It’s too small. The word was rendered too small, based on its weight, and the WordSizer. Try using bigger fonts: see sizedByWeight or sizedByRank.
  • It’s over the limit. You called maxNumberOfWordsToDraw, telling WordCram to only render so many words, and this word was over the limit. You should only use this if your corpus has tons of distinct words, and you need to keep WordCram from chugging for ever. If this is you, try setting a higher limit. But this probably isn’t your problem – why would you be worried about a word that’s so low in weight?
  • There’s no room. This last one is the most likely culprit: there wasn’t enough room to place the word where you wanted it to go. Try sizing your words smaller, or using a different strategy for placing your words.

Here’s a bit more about that last case, not having enough room. Suppose your sketch only has 3 words, and you placed them all spread out, like below. WordCram should have no problem placing them where you want them to go.

spread-out

But now suppose you put them all in the same corner. On the same spot, they’ll overlap:

overlap

To fix this, WordCram nudges them around, so they’re close to where you want:

no-overlap

With only 3 words, this is pretty easy. With 30 words, it’s still easy, if they’re small enough.

But with too many words, WordCram eventually will throw up its hands and move on to the next word.

It's ok, no one should eat bubblegum ice cream anyway

It’s ok, no one should eat bubblegum ice cream anyway

Posted in tutorial

Shapes for WordCram

This has been a feature request for a long time: making WordCrams in arbitrary shapes, like Tagxedo does.

It’s currently slow, and a bit limited – you have to provide a java.awt.Shape, not (say) an image mask – but it’s a first step. I hope to package it up nicely for the next WordCram release.

Below is the guts of the code, but here’s the output:

wordcram-love

class ShapeBasedPlacer implements WordPlacer, WordNudger {

  Area area;
  float minX;
  float minY;
  float maxX;
  float maxY;

  public ShapeBasedPlacer(Shape shape) {
    this.area = new Area(shape);

    Rectangle2D areaBounds = area.getBounds2D();
    this.minX = (float)areaBounds.getMinX();
    this.minY = (float)areaBounds.getMinY();
    this.maxX = (float)areaBounds.getMaxX();
    this.maxY = (float)areaBounds.getMaxY();
  }

  public PVector place(Word w, int rank, int count, int ww, int wh, int fw, int fh) {

    w.setProperty("width", ww);
    w.setProperty("height", wh);

    for (int i = 0; i < 1000; i++) {
      float newX = randomBetween(minX, maxX);
      float newY = randomBetween(minY, maxY);
      if (area.contains(newX, newY, ww, wh)) {
        return new PVector(newX, newY);
      }
    }

    return new PVector(-1, -1);
  }

  public PVector nudgeFor(Word word, int attempt) {
    PVector target = word.getTargetPlace();
    float wx = target.x;
    float wy = target.y;
    float ww = (Integer)word.getProperty("width");
    float wh = (Integer)word.getProperty("height");

    for (int i = 0; i < 1000; i++) {
      float newX = randomBetween(minX, maxX);
      float newY = randomBetween(minY, maxY);

      if (area.contains(newX, newY, ww, wh)) {
        return new PVector(newX - wx, newY - wy);
      }
    }

    return new PVector(-1, -1);
  }

  float randomBetween(float a, float b) {
    return a + random(b - a);
  }
}

void setup() {
  Shape heart = makeHeart();
  ShapeBasedPlacer shapeBasedPlacer = new ShapeBasedPlacer(heart);

  new WordCram(this)
    .withPlacer(shapeBasedPlacer)
    .withNudger(shapeBasedPlacer)
    .drawAll();
}
Posted in improvements | 11 Comments

WordCram 0.5.6 released

This release includes three bug fixes (#6, #7, and #8), and some small performance improvements, that should get us a tiny bit closer to faster wordcrams. Get the better bits, and happy hacking!

Posted in release | 7 Comments

WordCram 0.5.5 released

This release is mostly a fix for issue #4. dbasch couldn’t make a word cloud, because cue.language couldn’t decide what language his text was in. Next time cue gets confused, instead of stopping the whole show, WordCram will gracefully ignore it, and include the word.

I also made public some classes that weren’t before: BBTree, BBTreeBuilder, and WordShaper. If you’d like to make Processing sketches that do overlap-checking like WordCram does, but your shapes aren’t necessarily words, these might help you; you can see how they’re used by looking at the code in the WordCramEngine class. (Making them public also makes it easier for me to experiment with changing WordCram’s layout algorithms.)

As usual, you can download the new version at github. Happy word cramming!

Posted in release | 4 Comments