mlGrace is a
high-level OCaml interface to the
Grace 2D plotting
program. All of the raw Grace functionality is provided through a low-level interface
to the Grace scripting language, but the primary goal is to make many common plotting
operations very easy for the scientifically-minded OCaml user. mlGrace comes
with API reference documentation that
explains all options; please refer to those docs for detailed usage
information. But first, to get a feel for the module, how about we take a quick
tour?
First off, let's initialize an interactive Grace session. Since I'm generating
graphics for inclusion in a webpage, I'll set the viewport size to 650x500 pixels.
$ ./mlgrace Objective Caml version 3.08.1
# open Grace;;
# let gv = new grace_view ~layout:(`Size (650, 500)) ();; val gv : Grace.grace_view = <obj>
At this point, you should have noticed an xmgrace window popping
up. gv will always be associated with this
window. If you instantiate another grace_view
object, you'll get another xmgrace window associated with it.
Now, how about we come up with some data to make a simple plot of the square
root function. We can use the plot method for this purpose. It
requires an array of x values and an array of y values to plot against x. The
linspace command is useful for creating the x array. If we want to
plot our function for x in [0, 10], and use 1000 samples to get a smooth curve,
we could do something like this:
# let x = linspace 0.0 10.0 1000;; val x : float array = [|0.; 0.0100100100100100099; 0.0200200200200200198;
...|]
# let y = Array.map sqrt x;; val y : float array = [|0.; 0.100050037531277364; 0.141492119992669613;
...|]
# gv#plot x y;; - : unit = ()
# gv#redraw ();; - : unit = ()
After calling redraw, the xmgrace window should be
displaying our plot:
Hey, not bad. But maybe a title and some labels would be good:
# gv#label `Title "My First Plot";; - : unit = ()
# gv#label ~font:3 `XAxis "x";; - : unit = ()
# gv#label ~font:3 `YAxis "y";; - : unit = ()
# gv#redraw ();; - : unit = ()
There we go. Notice that I passed the font number as a labeled argument to the
label method. You will find that mlGrace uses a lot of optional
labeled arguments. They have two advantages: you can skip them if you're happy
with the defaults, and you can put such arguments in any order (as long as you
end with an unlabeled argument).
At this point I'm starting to get tired of the constant redraw calls.
For interactive use, it's more convenient to turn on auto-redraw:
# gv#set_auto_redraw true;; - : unit = ()
That'll save some typing. Now suppose I want to make some adjustments to the axes. I want to
zoom in a bit:
This does what I was expecting, but after zooming in to that level I
decide that I want to adjust the tickmarks. I'll use automatic
ticks for the x axis, but on the y axis I want to have major ticks
spaced 0.5 apart, and use minor ticks to divide each major spacing in
five.
There, I'm satisfied. But wait... suddenly I realize that I was supposed to
plot both the square root function and the sine, on the same plot! OK,
not a big deal. I just need to use the plot_many method, and pass
matrix arguments for x and y. Each row of y will be plotted against the
corresponding row of x.
# let x_mat = [| x; x |];; val x_mat : float array array = [|[|0.; 0.0100100100100100099;
0.0200200200200200198; ...|]; ...|]
# let y_sin = Array.map sin x;; val y_sin : float array = [|0.; 0.0100098428431791842;
0.0200186827054734777; ...|]
# let y_mat = [| y; y_sin |];; val y_mat : float array array = [|[|0.; 0.100050037531277364;
0.141492119992669613; ...|]; ...|]
# gv#plot_many x_mat y_mat;; - : unit = ()
Looks good to me. To create my webpage graphic, I can use the print method:
# gv#print ~driver:(`PNG "plot.png") ();; - : unit = ()
If I wanted something worthy of direct printing, I'd just set the driver for PostScript output. If I were
looking to include the plot in a document, the best choice is probably Encapsulated PostScript.
# gv#print ~driver:(`PS "plot.ps") ();; - : unit = ()
# gv#print ~driver:(`EPS "plot.eps") ();; - : unit = ()
Of course, sometimes it's necessary to create a table of plots. This can be accomplished using the
multiplot method:
# gv#multiplot ~rows:2 ~cols:2 ();; - : unit = ()
I notice a couple of problems with this plot. First of all, it's too crowded in both dimensions;
we need a little more space to make room for axis labels and titles. Secondly, the legend we created
earlier looks proportionally too large; decreasing the font size would help.
The hgap and vgap arguments are relative to the size of an individual graph:
~hgap:0.4 means we are requesting that horizontal gaps are 4/10 of the width of a graph. Also
notice the use of the num argument, to send the plot to the 0th location.
Let's make a few more plots to fill in the remainder of the table. First a stemplot of the natural logarithm,
using resized circles around the points:
Finally, how about a plot of 1/x in logscale, with a grid and a subtitle. The logspace function is
useful for achieving uniform sampling over a log plot.
# let x3 = logspace ~-.1.0 3.0 1000;; val x3 : float array = [|0.1; 0.100926219098704764; 0.101861017015597577; ...|]
# let y3 = let inv x = 1.0 /. x in Array.map inv x3;; val y3 : float array = [|10.; 9.90822809900379475; 9.81729840618883642; ...|]
# gv#plot ~num:3 ~logx:true ~logy:true ~grid:`Minor x3 y3;; - : unit = ()
# gv#label ~num:3 ~size:1.0 `Title "Logscale Example";; - : unit = ()
# gv#label ~num:3 ~size:0.7 ~font:1 `Subtitle "Hey look, it's linear! What a coincidence!";; - : unit = ()
That covers most of the important high-level features of mlGrace. Please have a look at the
reference documentation to see the available plotting
options in more detail. If you need access to some of the other features provided by Grace,
you can use grace_view's exec method to send raw Grace scripting commands.
This scripting language is unfortunately poorly documented at present. See the
Grace User Guide for
some commands, and check the a
ACE/gr User Guide
for a more thorough (but somewhat obsolete) command listing. Finally, the
Grace source code is obviously the authoritative
reference on the subject; src/pars.yacc is particularly helpful.