11.6 Plotting graphs and barcharts

This section describes three libraries residing in <pcehome>/prolog/lib/plot to deal with plotting graphs and barcharts.

11.6.1 Painting axis

The library library(plot/axis) defines the class plot_axis to draw an X- or Y-axis. The class deals with computing the layout, placing rule-marks, values and labels as well as translation between coordinates and real values. Normally this class is used together with plotter, plot_axis does not rely on other library classes and may therefore be used independent of the remainder of the plotting infrastructure.

We start with a small example from the library itself, creating the picture below.

?- [library('plot/axis')].
% library('plot/axis') compiled into plot_axis 0.03 sec, 27,012 bytes

?- send(new(P, picture), open),
           send(P, display,
                plot_axis(x, 0, 100, @default, 400, point(40, 320))),
           send(P, display,
                plot_axis(y, 0, 500, @default, 300, point(40, 320))).

Figure 24 : A picture showing two axis

Below is a reference to the important methods of this class. The sources to the class itself are a good example of complicated and advanced layout computations and delaying of these until they are really needed.

plot_axis ->initialise:
Create a new axis. type defines whether it is an X- or Y-axis. The axis represents values in the range [low...high]. If step is specified, a rule-mark with value is placed at these intervals. Otherwise the library computes its marking dynamically. The length argument specifies the length of the axis in pixels, the default is 200 and finally the origin defines the pixel-location of the origin.
plot_axis ->label: graphical*
Label to position near the end of the axis. This is a graphical to provide full flexibility.
plot_axis ->format: [name]
Define the printf()-format for rendering the values printed along the axis.
plot_axis <-location: int|real
int Determine the coordinate in the device's coordinate system representing the given value. See also `plotter<-translate'.
plot_axis <-value_from_coordinate: int
int|real The inverse of <-location, returning the value along the axis from a pixel coordinate.

Besides the principal methods below, the following methods are available for changing attributes of an existing axis: ->origin, ->low, ->high, ->step, ->small_step (interval for rule-marks without a value), ->length and ->type: {x,y}.

11.6.2 Plotting graphs

The library library(plot/plotter) defines the classes plotter and plot_graph for displaying graphs. Class plotter is a subclass of device. The example below plots the function Y = sine(X)

:- use_module(library('plot/plotter')).
:- use_module(library(autowin)).

plot_function :-

plot_function(Template) :-
        To is 2*pi,
        PlotStep is To/100,
        Step is pi/4,
        new(W, auto_sized_picture('Plotter demo')),
        send(W, display, new(P, plotter)),
        send(P, axis, new(X, plot_axis(x, 0, To, Step, 300))),
        send(P, axis, plot_axis(y, -1, 1, @default, 200)),
        send(X, format, '%.2f'),
        send(P, graph, new(G, plot_graph)),
        plot_function(0, To, PlotStep, Template, G),
        send(W, open).

plot_function(X, To, _, _, _) :-
        X >= To, !.
plot_function(X, To, Step, Template, G) :-
        copy_term(Template, X:Func),
        Y is Func,
        send(G, append, X, Y),
        NewX is X + Step,
        plot_function(NewX, To, Step, Template, G).

Figure 25 : Plotter showing sine function
plotter ->axis: plot_axis
Associate a plot_axis. Before using the plotter both an X and Y axis must be associated. Associating an axis that already exists causes the existing axis to be destroyed.
plotter ->graph: plot_graph
Append a graph. Multiple graphs can be displayed on the same plotter.
plotter ->clear:
Remove all graphs. The X- and Y-axis are not removed.
plotter <-translate: X:int|real, Y:int|real
point Translate a coordinate in the value-space to physical coordinates.
plotter <-value_from_x: int
int|real Translate an X-coordinate to a value.
plotter <-value_from_y: int
int|real Translate an Y-coordinate to a value.

Graphs themselves are instances of class plot_graph, a subclass of path. Instead of normal point objects, the points are represented using the subclass plot_point that attaches the real values to the physical coordinates. Methods:

plot_graph ->initialise:
The type argument denotes the interpolation used. Using poly (default), straight lines are drawn between the points. Using smooth, the curve is interpolated (see path for details) and using points_only, no lines is painted, just the marks. Using the mark argument the user may specify marks to be drawn at each control-point.
plot_graph ->append: x=int|real, y=int|real
Append a control-point using the coordinate-system of the axis of the plotter.

11.6.3 Drawing barcharts using ``plot/barchart''

The library(plot/barchart) library draws simple bar-charts. It is based on the plotter and plot_axis classes, adding simple bars, grouped bars and stacked bars. Below is an example from library(plot/demo) showing all active XPCE, classes, where active is defined that more than 250 instances are created. The code, except for the calculation parts is show below.

Figure 26 : Classes of XPCE with > 250 instances created
barchart :-
barchart(HV) :-
        new(W, picture),
        length(Classes, N),
        required_scale(Classes, Scale),
        send(W, display, new(BC, bar_chart(HV, 0, Scale, 200, N))),
        forall(member(class(Name, Created, Freed), Classes),
               send(BC, append,
                              bar(created, Created, green),
                              bar(freed, Freed, red)))),
        send(W, open).
bar_chart ->initialise:
Initialise a bar_chart, a subclass of plotter for displaying bar-charts. The orientation indicates whether the bars are vertical or horizontal. The low and high arguments are the scale arguments for the value-axis, while scale_length denotes the length of the axis. The nbars argument determines the length of the axis on which the bars are footed.
bar_chart ->append: bar|bar_stack
Append a single bar, bar_stack or bar_group to the chart. Bars and bar-stacks are named and can be addressed using their name.
bar_chart ->delete: member:bar|bar_stack
Remove the given bar. The member: construct makes the type-conversion system translate a bar-name into a bar. If the bar is somewhere in the middle, the remaining bars are compacted again.
bar_chart ->clear:
Removes all bars from the chart.
bar_chart <->value: name
real Modifies or requests the value of the named bar. Fails if no bar with this name is on the chart.
bar_chart ->event: event
Processes a single-click outside the bars to clear the selection.
bar_chart ->select: bar|bar_stack, [{toggle,set}]
bar_chart ->selection: bar|bar_stack|chain*
bar_stack <-selection:
chain Deal with the selection. Selection is visualised by selecting the labels, but communicated in terms of the bars themselves. Class bar

Bars can either be displayed directly on a bar_chart, or as part of a stack or group. Stacked bars are used to indicate composition of a value, while grouped bars often indicate development or otherwise related values for the same object.

bar ->initialise:
Create a bar from its name and value. The bar itself is a subclass of box and colour is used to fill the interior. The orientation needs only be specified if the bar is not attached to a bar_chart.
bar ->value: real
Set the value of th bar. This updates the bar-size automatically.
bar ->range: low=real, high=real
If the bar is editable (see also ->message and ->drag_message), these are the lowest and highest values that can be set by the user.
bar ->message: code*
If not @nil, the bar may be modified by dragging it. After releasing the mouse-button, the new <-value is forwarded over the code.
bar ->drag_message: code*
If not @nil, the bar may be modified by dragging it. While dragging, the new value is forwarded on every change over the code. It is allowed to specify both ->message and ->drag_message.
bar_stack ->initialise: name, bar ...
Create a pile of bars representing a value composed of multiple smaller values.
bar_group ->initialise: name, bar ...
Same as bar_stack, but places the bars next to each other instead of stacked. Class bar_button_group

A subclass of dialog_group that can be used to associate one or more buttons or other controllers with a bar or bar_stack. This association is achieved by simply creating an instance of this class. Figure 27 shows both associated buttons and a stacked bar.

bar_button_group ->initialise: bar|bar_stack, graphical ...
Associate the given graphicals with given bar.
bar_button_group <-bar:
bar|bar_stack Return the bar or stack this group is connected too. This behaviour may be used to make to make the buttons less dependent on the bar they are attached too.

Figure 27 : Stacked bars with associated buttons