Search this site


[prev]  Page 2 of 2

Metadata

Articles

Projects

Presentations

More rmagick/rvg playtime

While working on graphs tonight, I found that calculation and labelling of ticks should be provided by special 'tick' classes. The iterator (the 'each' method) takes a min and max value and yields ticks in that range. This allows you to:
  • A 'tick' provider should just be an iterable class (foo.each, etc) which provides the tick position and optional label.
  • A graph can have multiple ticks per axis, allowing you to have 'major' ticks labeled while 'minor' ticks are not labeled, and even more than two layers of ticks on each axis.
  • The same tick classes can easily be used to draw both the graph ticks and the grid.
  • Trivially have 'time format' tickers
  • Have a 'smart time ticker' that looks at the min/max and determines the correct set of time ticks to display (display format, tick distance, tick alignment, etc). Can use multiple 'time ticker' instances internally (code reuse!)
I'm sure this has all been though of before, but it's a research experience for me :)

At any rate, I'm finding myself wondering if RMagick/rvg is really the right tool. It certainly makes doing graphics trivial, but even for what I see as a simple graph it takes a little over a second to render, which would hurt usability if multiple graphs needed rendering simultaneously.

The bottleneck seems to be with text rendering. If I disable text display in the graph (tick labels, etc), graph rendering drops by 0.5 seconds (from 1.1). Switching from 'gif' to 'png' output shaved 0.2 seconds on average of rendering, which is interesting.

Today's results, with real data:

graph = RPlot::Graph.new(400, 200, "Ping results for www.google.com")
pingsource = RPlot::ArrayDataSource.new
File.open("/b/pingdata").each do |line|
  time,latency = line.split
  pingsource.points << [time.to_f, latency.to_f]
end
pingsource.points = pingsource.points[-300..-1]
graph.sources << pingsource
graph.xtickers << RPlot::SmartTimeTicker.new
graph.ytickers << RPlot::LabeledTicker.new(alignment=0, step=25)
graph.render("test.png")

Graphs in Ruby with RMagick

I'm always finding myself wanting to graph random data. Gnuplot is nice, but not enjoyably scriptable. Matplotlib in python is too matlab-ish, or was when I looked at it last (though it looks much improved now). Some ruby options exist (even ruby+gnuplot), but none were much to my tastes.

I started fiddling around with RMagick and stumbled across what it calls "RVG" (ruby vector graphics). From the site:

RVG (Ruby Vector Graphics) is a facade for RMagick's Draw class that supplies a drawing API based on the Scalable Vector Graphics W3C recommendation.
The API is pretty reasonable and hasn't hindered me yet and feels good after having hacked with it for a few hours: Simple operations like point translation, scaling, rotating, flipping, etc are simple in code; the api is well documented; images can be embedded easily into another which allows for easily writing modular code.

Anyway, the goal of this adventure was to come up with something that would produce non-crappy plots. Main emphasis on having a means to apply axis labels and ticks that wasn't painful. The result is below: (x-axis ticks are hour-aligned and have 12 hour steps, y-axis ticks are single-value aligned)

Here's the code that generates the above graph (using rplot.rb). A lot of things (like axis label tick alignment and stepping) are hardcoded right now, but that will obviously change if I decide this project needs attention (and I don't find something that does the same thing but better).

# graph some random stuff, like log(x) and sin(x)
# use time for the 'x' to demo time formatting
# each point is an hour (i * 3600)
graph = RPlot.new(400, 200, "Happy Graph")

points = 60
axis = GraphAxis.new
(1..points).each do |i|
  axis.points << [Time.now.to_f + i*3600, Math.log(i)]
end

axis2 = GraphAxis.new
(1..points).each do |i|
  axis2.points << [Time.now.to_f + i*3600, Math.sin((i / 2.0).to_f) + 1]
end

graph.axes << axis
graph.axes << axis2

graph.render("/home/jls/public_html/test.gif")

How not to write documentation.

I've grown accustomed to ruby having poorly accessible documentation. What I mean by 'poorly accessible' is the fact that ri Array gives me a list of things Array can do (which is nice), but to actually find out about Array.delete I have to run ri Array.delete. Further, the online ruby documentation is better, but not great, but is somehow strangely different from simply the rdoc itself. I got used to Python's often-helpful ways of pydoc showing you what appears to be "as much as possible" when you pydoc a class, module, or method.

Maybe I'm doing it wrong. Either way, the following is annoying and unhelpful. While it tells me the arguments that should be passed, and what is returned, it doesn't help me really know more about the function. Luckly, I'm already familiar with select from other languages.

snack(~) % ri IO.select
------------------------------------------------------------- IO::select
     IO.select(read_array 
     [, write_array 
     [, error_array 
     [, timeout]]] ) =>  array  or  nil
------------------------------------------------------------------------
     See +Kernel#select+.
Ok, fine... Let's look at Kernel#select.
snack(~) % ri Kernel#select
---------------------------------------------------------- Kernel#select
     IO.select(read_array 
     [, write_array 
     [, error_array 
     [, timeout]]] ) =>  array  or  nil
------------------------------------------------------------------------
     See +Kernel#select+.
*sigh*

Riding on the failboat: Ruby, episode 1.

I'm quickly learning ruby at my new job. Today's POLA (principle of least astonishment) violation is blamed on my expectations of 'if' behavior in Ruby from what I know from Python, C, and other languages.

Non-nil values are considered true:

["0", [], {}, "", 0, true, false, nil].each { |x|
  bool = (x) ? true : false
  puts "#{x.inspect}: #{bool}"
}

"0": true
[]: true
{}: true
"": true
0: true
true: true
false: false
nil: false
I mostly expected every one of the outputs here except for the literal 0 value being true. Noted for future reference.

Additionally confusing, is that Integer() will barf on most non-number inputs, but for some reason "nil" means 0.

irb(main):002:0> Integer(nil)
=> 0
Unexpected.

Ruby/Oniguruma code block patches

I love perl's (?{ code }) feature. I want it in other languages.

I spent some time on hacking this into ruby a few weeks ago. I finally got around to making patches.

In FreeBSD ports, I select to build Ruby 1.8.6 with oniguruma for the regex engine. After doing 'make configure' you can apply these patches:

I haven't tested this on other platforms, and it's not feature complete, but it's close.

Ruby/Oniguruma hacking

Last night, I mentioned that I wanted (?{ code }) in ruby and python.

I got bored tonight and decided to see how hard this would be to implement in ruby. Turns out it's not as bad as I thought, not that I'm finished yet.

This ruby script shows a demo of what I have so far. The output is in comments in the script. There's a few strange bugs yet, but I've nearly got it working properly. Something about my coding or the way oniguruma does backtracking/failures keeps this from working correctly on strings with multiple potential matches.

A letter to Ruby and Python.

Dear Ruby and Python,

Please implement Perl's (?{}) and (??{}) in your regular expression engines so I can do outrageous state machine and pattern matching in your languages. Thank you.

Love,
Jordan