« Back to Processing

Tutorial #4: A tree made of letters

| 1 Comment | Published on April 22, 2008
The content that follows was originally published on the Don Havey website at http://donhavey.com/blog/tutorials/tutorial-4-a-tree-made-of-letters/

Lettertree previewA couple of years ago I was walking home from work and thinking about how writing to a blog is often a one-way process. Like talking to a wall. The most important part of the process, for most bloggers, is the creation of an entry: the release of words. The importance of readability decreases quickly after an entry has been written. Frequently it is an introspective process, like writing to a diary or talking to yourself.

I thought, I would like to create objects from blog entries. Objects that would contain the words in the entry and grow as more words are added, but remain illegible, thereby deemphasizing the creation of a document.

To make a long story short: I decided that a tree would be an appropriate object: it grows in a recognizable fashion and can expand nearly indefinitely. The lettertree concept was born.

This tutorial will explain how I approached the lettertree problem. It relies on our existing Tree class. We’ll create paths from the root of the tree to each “bud” and randomly select one of these paths to carry a letter. We’ll also increase the thickness of the tree branches as the thing grows.

Here’s what it looks like: The final result

Here’s what you need: Lettertree classes

Note that like many of the applets I post, I’ve switched the rendering method from OpenGL to P3D for the in-browser sample. The OpenGL results will look way better and (typically) run faster within the Processing environment, but the latest version of the Java plugin is so immensely bloated that P3D seems to render faster in a browser.

Ready to begin?

Let’s get started…

The Lettertree class

The backbone of our applet will be the Lettertree class, which will coordinate between a Tree object and a big bunch of letters. It will contain a series of Polylines between the root of the Tree and each of its highest branches. We’ll use those paths to coordinate the movement of any string of letters that is passed to the add_string() method.

First let’s look at our project variables. These are in addition to our Tree variables. They relate specifically to our Lettertree. I’ve added comments on their purpose here:

float letter_climb_inc = 12; //how many letters per branch
float letter_scale = 5.8; //the scaling difference between the trunk and the highest branches
float letter_min_size = 7; //the smallest letter size allowed (px)
float letter_max_size = 28; //the larget letter size allowed (px)
float letter_growth = 24; //the amount of thickness to add to the tree as it grows (px)
float letter_extras = 1.1; //a cap on the total number of letters used to complete a tree

The add_string() method is quite simple, but let’s take a quick look at that too:

void add_string(String $letters){
  if(nletters<maxletters){
    for(int i=0;i<$letters.length();i++){ //loop through the characters contained in the string
      char c = $letters.charAt(i);
      int ic = int(c);
      if((ic>96&&ic<123)||(ic>64&&ic<91)){ //make sure this character is a letter
        letters[nletters] = new Letter(c,paths[int(random(npaths-.00001))],this); //add the letter
        letters[nletters].init(); //initialize the letter
        nletters++;
      }
    }
  }
  lg = letter_growth*(1-nletters/maxletters);
}

So we’re choosing a random path out of our array and assigning it to a Letter. We’re also passing a reference to the Lettertree itself to each of its Letters, so that we can coordinate the distance each Letter needs to travel along its respective path.

A change to the Branch class

Maybe you’ve noticed that our Lettertree’s init() function calls a get_path() function for each of the Tree’s “buds”:

for(int i=0;i<t.nbuds;i++){
  paths[npaths] = t.buds[i].get_path();
  npaths++;
}

That’s an addition I made to the Branch class. Each Branch can now trace its “ancestry” back to the root of the Tree. That gives us a list of Branches (Segments) to compile into a Polyline object (a path). So conceptually speaking, our Lettertree is composed of a large number of redundant Polylines between its base and each of its tips, as opposed to our Tree being composed of only the necessary number of individual segments. However, since the Polyline class can be composed of shared Segments, there is no actual resource redundancy… the Polylines simply refer to the Segments defined by the Tree.

The Letter class

The Letters that will climb our Lettertree are a bit inaccessibly coded… sorry about that. There’s a lot of basic math that relates to the size of each letter, but it’s all shrouded in variable names and abbreviations. You’ll have to wade through that on your own if you want to modify the Letter behaviors. I’ll just sum things up in comments:

void step(){
  //to allow the tree to rotate, we have to calculate position every frame
  //regardless of whether or not the letter is still climbing
  //this is actually less cpu intensive than rotating the letters like points
  p.move_to(path.segments[ps].p1.x+pp*(path.segments[ps].p2.x-path.segments[ps].p1.x),
            path.segments[ps].p1.y+pp*(path.segments[ps].p2.y-path.segments[ps].p1.y),
            path.segments[ps].p1.z+pp*(path.segments[ps].p2.z-path.segments[ps].p1.z));
  if(climbed){ return; }
  if(pp>lt.branchletters[path.segments[ps].n]){ //if we've hit unexplored territory on the path
    lt.branchletters[path.segments[ps].n] += 1/letter_climb_inc; //stop climbing
    climbed = true; return;
  }
  pp += 1/letter_climb_inc;
  if(pp>=1){ //if we've reached the end of a segment on our path
    ps++; //increment the segment we're on
    pp = 0; //reset our segment position
    if(ps!=path.nsegments){
      //set our letter size according to its position in the lettertree
      ls = letter_min_size+letter_scale*(tree_levels-lt.branches[path.segments[ps].n].level);
    }
  }
  if(ps==path.nsegments){ ps--; pp = 1; climbed = true; } //all done
}

And a quick look at the render() method:
void render(){
  //constrain the letter's size and subtract the lettertree's growth
  int ts = round(min(letter_max_size,max(letter_min_size,ls-lt.lg)));
  //give our moving letters a little pinkish tint
  if(!climbed){ fill(255,200,200,150); }else{ fill(255,150); }
  //if the letter is at a tip of the tree, make it big and pink
  if(ps==path.nsegments-1&&pp==1){ ts = round(letter_max_size); fill(255,200,200); }
  //add the text
  textSize(ts);
  text(c,p.x,p.y+ts/2,p.z);
}

Voila! You have climbing letters!

Conclusion

As it turns out, building a tree full of letters on top of our Tree class isn’t all that difficult.

Of course, my original intent was to capture a user’s input and build it into a tree… this is not quite to that level. We aren’t allowing any user input right now. We’re just throwing a random sequence of letters at the thing.

And obviously, the applet is not optimized for an infinite number of letters. We’d have to remove the oldest letters (letter leaves falling from the tree?) in order to keep our CPU usage manageable. The text would need to be store in a database in order to save the state of the tree. And I think the ideal solution would generate only a part of the tree each use, so that the solution is not predetermined, but instead can be affected by patterns in the type, etc.

Maybe I’ll take it there someday. For now, enjoy!

The final result: The final result

The classes: Lettertree classes

Categories: Processing / Tutorials

Tags: / / / / / /

1 Comment

mmsnow says:

A big thank you for the tutorial. It is wonderful that you detail the process step by step. I’ve gleamed much from your tutorial to better create programs that express organic growth.

Leave a Response

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>