Blop Tutorial


The basic layout of scripts

Blop can also run interactively: simply type at your shell prompt:
blop
and hit enter. Now the commands are expected from the terminal. These commands should be valid C++ commands.

Most often, if you produce plots for a document, these plots will need refinement, include some more data, etc. In this case it is much more efficient not to type all the commands producing your plots, but put these into a script file (referred to as script.C in the following). This file should be like a C++ program: it must contain a

main()
{
 ...
}
function (and possibly other functions as well). To let blop execute your script file (that is, the main function in your script file), type
blop script.C

The usual Hello World example

Like every tutorial I have seen up to now, this one also begins with the Hello World example. Create the file script.C to contain the code below, and then execute as shown above. Voila, 'Hello World' will be printed to your terminal

// --- script.C
main()
{
  cout<<"Hello World\n";
}
Not bad, hmm? So blop also has the most popular feature of today's softwares. Great! But there are better tools for this purpose (I recommend, for example: echo "Hello World")
Plotting some data

So let's plot some data. Edit the file datafile to contain this:

1 1
2 4
3 9
4 16
To plot this data, start blop interactively, and write at the prompt:
plot("datafile",_1,_2).drawstyle(points).pointtype(fcircle).legend("my data");
A gv (ghostview) window will appear on your screen, showing the produced plot. This command is quite self-explanatory, except maybe the symbols _1 and _2. These indicate, that the first and second columns should be plotted from the given datafile. We will come back to this later. To get the possible drawstyles and pointtypes, say at your shell prompt:
blop -h drawstyle
blop -h pointtype

Let's try to do now the same thing from a script. Remember that you have to put your statements within the main(){...} function! Create the following script.C file, and execute it (blop script.C)

// ---- script.C
main()
{
  plot("datafile",_1,_2).drawstyle(points).pointtype(fsquare).legend("my data");
}
Waiting for the plot to appear.... but nothing happens. There isn't any output file produced, either. This is because the plot(...) function only creates a plot in the current canvas (which is a kind of virtual drawing surface, which only exists in the computer's memory). The current canvas needs to be printed to a terminal, in order to really see it. In interactive mode this happens automatically after the execution of a function (the current canvas is printed into a postscript file (eps terminal), which is then automatically shown in a gv process) In script mode one needs to do it explicitely:
// ----- script.C
main()
{
  plot(....);
  eps::print("test.eps");
}
This additional function call prints the current canvas into an eps terminal (an .eps file, as specified in the argument). To see the available terminals, say at your shell prompt:
blop -h terminals

Instead of plotting the first two columns, you can specify arbitrarily complex combinations of columns to be plotted. For example is your datafile contains 3 columns, you can plot the sin^2 of the second + the cos^2 of the third column as a function of the first one by saying:

plot("datafile", _1, sin(_2)*sin(_2)+cos(_3)*cos(_3));

The plot(...) function automatically clears the current plot before producing a new one. If you want to plot several graphs on the same figure, use the mplot function instead ('m' means 'multi': this function does not clear the figure before plotting the graph)


Plotting functions

Now let's plot some functions. The following code plots a sinus curve in the range [0:10]

main()
{
  plot(_1, sin(_1)).p1range(0,10);
  eps::print("sinus.eps");
}
So we meet again this funny symbol _1. What is this actually? This is a blop function: a function-object, which can be evaluated with arguments. The _1 symbol is a predefined function object, which - when evaluated - simply returns its first arguments. The following code will print the number 3 to the terminal:
cerr<<_1(3,4,5)<

Let's come back to function plotting. This version of the
plot(...) function does not expect a filename as its first
argument, but only function-objects. When plotting functions, blop
scans the value of a parameter, and calls the provided functions with
this value as argument. In the above case the first  function (which
will serve as the x coordinate) is simply _1 (this 
refers to the parameter itself), the second function (which will serve as the y
coordinate) is the sine of the parameter, thus, the resulting curve is
a sinus curve. (To make it clearer, blop plots functions in the sense
of gnuplot's parametric mechanism). The .p1range(0,10)
command specifies the range of the parameter (which in the current
case equals the x-range, since the x-coordinate is the parameter itself). 
The following code for examples draws a circle:
plot(cos(_1),sin(_1)).p1range(0, 2*3.1415);

The object hierarchy in a figure

We can now plot datafiles and functions. Even several of them - but only on the same figure. How can we create two graphs, shown separately?

In order to address this question, let's see the containment hierarchy in a plot. The following figure serves as an illustration:

Here are some examples, how to create more complicated plots:
Inclusion of the plots into your document

In the examples above your figure was printed to an .eps file. However, in the introduction I promised that blop can do more than producing a simple .eps file: it can produce "intelligent" outputs, which then cleverly interact with your LaTeX document, into which you include them.

In order to produce such a file, print your canvas to a blopeps terminal:

blopeps::print("filename.beps");
This file-type is blop's "native" filetype (or, terminal type). It is a valid .eps file,
but it contains also some commands, which are executed by LaTeX, when the file is included into a document (see below). Without these commands being executed, the file looks awful, so if you open this file for example with gv, and see something unexpected, don't be disappointed.

If you simply include this file into your LaTeX document using for example the \includegraphics macro, you will get the same awful figure in your document, because this macro simply includes the .eps source, but does not execute the LaTeX commands from this file. To do both, blop provides the blopeps package (by default under /usr/share/texmf/tex/latex/blopeps), and this package provides the \blopeps{filename} macro. Here is an excerpt from a LaTeX document:

\documentclass[a4paper]{article}
\usepackage{blopeps}
\begin{document}
.
.
\blopeps[optargs]{myfigure.beps}
The optional arguments of the \blopeps macro specifies the width, height, linewidth, etc of the figure, in the key=value format:
width=4cm, or pw=4cm
specify the width of the figure
height=4cm, or ph=4cm
specify the height of the figure
ps=1mm
specifies the pointsize in the figure (BLOP has a built-in dimension, PS, which can be used in the user's script like
plot("datafile").pointsize(1.3*PS);
The actual value of PS can be specified by this optional argument)
lw=0.4pt
Specifies the actual value of the built-in dimension, LW, which is the default width of lines.

Setting axis titles

Now you can plot functions and datapoints. To set the axis title, the following functions are provided:

set::x1title("Lower horizontal axis");
set::y1title("Left vertical axis");
set::x2title("Upper horiz. axis");
set::y2title("Right vertical axis");
They set the title of the axes of the currently active frame.
Length arithmetic

BLOP provides a very easy way of calculating lengths. A special class, length is provided for this purpose, which can be multiplied by a number, and two lengths can be added. Here is an example how to calculate the linear combination of lengths:

length l = 3*MM + 4*EX;

In this example MM and EX are predefined variables in blop (the first is millimeter, the second is TeX's ex dimen). The following classes of lengths are available:

A valid piece of code is for example:

length a = 3*mm + 1.2*width("Velocity");
length b = 1.3*a + 4*pt;

The following might be somewhat ununderstsandable, sorry. It would improve in the future. Skip it maybe, and try to begin reading the documentation...

In the above example the value of the length b (calculated from the other length a) is fixed at the time of the assignment; that is, if at a later point the value of the length a is changed, it will have no effect on b. In many cases you would like the opposite behaviour. For example the size (and therefore the left or right edge) of a legendbox can change, if you add more legends to it. In fact, in the current implementation its dimensions are not calculated until the last moment, when the legendbox is printed to a terminal. If you would like to position something to the right edge of a legendbox, the following piece of code would be bad:

length x = frame::current().legend()->right();

To solve this problem, the ! operator is provided to create a reference to a length. For example if you want that the length b should be 1.3 times the actual value of length a, you should write

length a = 3*mm;
length b = 1.3 * !a;

Now, if at a later point you change the value of a, this will have an effect on the value of b as well.

You can visualize the value of lengths using the << operator:

length a = 3*mm;
length b = 2*cm + 2*!a;
a = 5*pt;
cout<<b<<endl;

The var class

BLOP tries to simulate the nice feature of most scripting languages, that variables can simultaneously treated as a numerical or string value. The class var is provided for this purpose. A variable of this type can be converted to a double or string explicitely with the following member functions:

double var::dbl();
string var::str();

var a = "1.23";
double num_value = a.dbl();
string str_value = a.str();

When a variable of this type is manipulated as a string, its numerical value is always calculated as the number which is represented by that string, or zero, if the string value does not represent a valid number (for example

var a = "some text 13"; 
cerr<<a.dbl()<<endl;      // this produces 0 on the output

The usual arithmetic operator (+, -, /, *, +=, -=, *=, /=) treat them as numerical values, while the & and &= treat them as strings. Examples


var a = "Hello ";
a &= "World";
a &= 4.5;

    // at this point   a.dbl()   gives 0
    //           and   a.str()   gives the string: "Hello World 4.5";

a += 1.3; 

    // at this point   a.dbl()   gives 1.3 (since a was 0 before)
    //           and   a.str()   gives the string: "1.3"

a += "1.3";

    // at this point   a.dbl()   gives 2.6 
    //           and   a.str()   gives the string: "2.6"

a &= "hello";

    // at this point   a.str()   gives "2.6hello";
    //           and   a.dbl()   gives 2.6, resulting from the
    //                           conversion of the above string


Practical advices

For specifying the pointsize of a graph, (unless you have a good reason for it) don't use 'absolute' units (that is, mm, cm, etc). If you do not want to use points of different size, simply do nothing in your script. If you want to use points of different size, define these sizes as multiples of the predefined PS unit. This allows you to change the size of the points in all of your plots in your document with one single command in your LaTeX document: saying \blopps{3mm} at the beginning of your LaTeX document, the PS unit in all subsequent plots included into this document will be 3 mm (unless overwritten with the [ps=...] optional argument of the \blopeps command).

The same arguments hold for linewidths. Define the linewidth of your objects within your plot as the multiples of the LW unit, if you want to produce lines of different widths. The linewidth in a single included figure can be specified by the lw=... optional argument of the \blopeps macro, or the default value for all figures can be set with the \bloplw macro, at the beginning of your LaTeX document, for example \bloplw{1pt}.


Using C-functions

You can define any C-function in your script, which takes var arguments, and returns a var as well, and you can plot these functions, or use them to transform the content of datafiles. Examples:

var f1(var a)
{
  if(a < 4) return a;
  if(a < 10) return sqrt(a);
  return a*a;
}

var f2(var a,var b)
{
  return a*cos(b) + sinh(a);
}

var f3(var a)
{
  // filter out words beginning with 'fu'
  if(a.str().find("fu") == 0) a = "censored";

  // typeset it with bold font
  a = "{\\bf " & a & "}";
  return a;
}

// plot the function f1. If one single argument is provided
// for cfunc, it will be called on the first argument, so the
// next example is equivalent to plot(_1,cfunc(f1,_1)).
plot(_1,cfunc(f1)).legend("A C-function: f1");

// plot a datafile: f2 is called on the second and third columns,
// and is plotted as a function of the first column
mplot("datafile",_1,cfunc(f2,_2,_3)).drawstyle(points);

// plot data from the file: put labels (in the 4th column
// of the datafile), but filter them using f3
mplot("datafile",_1,_2,cfunc(f3,_4)).drawstyle(labels);
You can call built-in ANSI functions as well, for example
plot(_1,cfunc(sin)).legend("Sine");