Building a Railroad Programatically With RMagick
While talking with Rick Olson (technoweenie) about Rails Weenie and how he could increase participation from people within the Ruby/Rails community we discussed having Feedburner style badges that let you show your score on your blog. We took this concept and pushed it one step further and also made it a little more interesting. We decided to use a stretch of railroad for the badges.
Here’s how it works:
- Users start with a set amount of points that they can wager for an answer to their question
- Users score points for answering questions and posting tips.
- For each X amount of points a user can grow their level and also start building their railroad.
- When a user reaches say 200 points, they get two crossties put on their railroad badge and some fancy status title such as “Master Rubyist”.
Cool huh? Now all that’s left is to actually make it work with Ruby and RMagick. It’s fairly simple, but I thought it’d be fun to share how I did it.
The Graphics

I started out with 3 png graphics that I put together in Photoshop. The semi-transparent cross ties function much like grayed out stars in a rating system. Basically we lay down the semi-transparent crossties first, then depending on a users score we overlay opaque crossties on top of those, next we overlay the rails, and finally we will write some text on the canvas.

The Code
def draw
@canvas = ImageList.new
@canvas.new_image(@width, @height) {
self.background_color = '#ffffff'
}
# Load up semi-transparent crossties
@canvas << Image.read("#{@asset_dir}/crossties.png").first
# Place opaque crossties
%w(3, 12, 20, 28, 36, 44, 52, 60, 68, 76, 84).each do |x|
crosstie = Image.read("#{@asset_dir}/crosstie.png").first
crosstie.page = Rectangle.new(5, 18, x.to_i, 0)
@canvas << crosstie
end
# Overlay rails on top of crossties
@canvas << Image.read("#{@asset_dir}/rails.png").first
# Write the text
blurb = Draw.new
blurb.font = "#{@asset_dir}/#{@font}"
blurb.fill('black')
blurb.annotate(@canvas[0], @width, @height, 0, 0, "RAILS WEENIE\n GRAND MASTER\n 200") do |b|
b.gravity = SouthGravity
b.fill_opacity(1)
b.pointsize = 8
end
# Make it so
@canvas.flatten_images.write('omg.gif')
end
This is pulled from the code and tweaked to just show you how to compose the Railroad, I left out all the score stuff for the sake of simplicity.
- First we create a canvas to draw on and set it’s background color to white.
- Then we load up the transparent crossties.
- Now we place our individual crossties at predefined points. The numbers in the array represent offset pixel values from the left of the canvas edge. All of this gets appended to the
@canvas ImageList.
# Place opaque crossties
%w(3, 12, 20, 28, 36, 44, 52, 60, 68, 76, 84).each do |x|
crosstie = Image.read("#{@asset_dir}/crosstie.png").first
crosstie.page = Rectangle.new(5, 18, x.to_i, 0)
@canvas << crosstie
end
The page attribute here takes a Rectangle object, which in turn takes 4 arguments: (width, height, x, y). The width and height are that of a single crosstie, and the x and y values are the offset of each crosstie in respect to the underlying image.
- Next we create a new
Drawobject that will serve as the annotation for our graphic.
# Write the text
blurb = Draw.new
blurb.font = "#{@asset_dir}/#{@font}"
blurb.fill('black')
blurb.annotate(@canvas[0], @width, @height, 0, 0, "RAILS WEENIE\n GRAND MASTER\n 200") do |b|
b.gravity = SouthGravity
b.fill_opacity(1)
b.pointsize = 8
end
The only interesting thing here is the annotate method. While doing this step, I kept having my text cut off right below the transparent crossties and pulled out a bit of hair in the process. It turns out that I wasn’t drawing on the bottom canvas, so I originally had blurb.annotate(@canvas…) which should’ve been blurb.annotate(@canvas[0]…). You can read more about annotate here.
Wrapping up
Thats it! You could probably take something like this to the extreme. It might be fun to supply a graphic starter kit with pieces of a structure and see who could build the coolest bridge or house with RMagick and Ruby. Sorta like programmatic legos ;-).
Sorry, comments are closed for this article.


Discussion
Very nice. I’d love to see a wave of creative dynamically generated graphics happening. (I’m working on my own.)
However, I noticed that I am no longer a “Swell Chap” here! What does it take?!!!
Are there only four? It’s time to take down Ezra…
Oops! I was doing some cleanup on the list and forgot to add you back. Doing to many things at once.
Very interesting, thanks. I think I might have to start playing, too.
Very nice idea ;o)
Nice! I’m always interested in seeing ways to use RMagick that go beyond just making thumbnails.
Thanks Tim! Not to shabby of a job you did on RMagick if I might add ;-)
nifty!