11.3. Displaying Results in Graphs

The most commonly used graphs in Swarm are histograms, which display the frequency distribution of a variable, and line plots, which show changes over time in real-valued numbers. There are some little details that arise in some applications, but for the most part creating graphs is easy.

The Swarm protocol that can create line plots is called EZGraph. The essential steps require the user to create a graph object, and optionally to assign labels for various display attributes. For example, returning to the Arborgames simulation, one can create a graph showing the numbers of trees of various sorts. The first step is to create the graph object, in this case called popGraph.

popGraph = [EZGraph createBegin: [self getZone]];
SET_WINDOW_GEOMETRY_RECORD_NAME (popGraph); 
[popGraph setTitle: "population"]; 
[popGraph setAxisLabelsX: "time" Y: "population"];
popGraph = [popGraph createEnd];
    

After the EZGraph object is in existence, then it can be instructed to prepare itself to plot some lines. A stream of numbers is thought of as a sequence, and internal to the Swarm library there is an object type called sequence that is used by the graph tools to keep track of the numbers they are to present.

When the graph object is told to create the sequence, it can be told to formulate a summary statistic and plot it. In the Heatbugs simulation, the aim is to plot a summary of the unhappiness of all bugs. The average level of unhappiness of all bugs is the chosen indicator. Here is a code excerpt that shows how an object called unhappyGraph is told to create a sequence for plotting:

[unhappyGraph createAverageSequence: "unhappiness"
             withFeedFrom: [heatbugModelSwarm getHeatbugList]
              andSelector: M(getUnhappiness)];

This uses the createAverageSequence:withFeedFrom:andSelector method. Note that the sequence is assigned the name "unhappiness". The quotation marks are needed because the name of the sequence is a character string. The unhappyGraph is told to take data from the list of bugs, which is found by asking the heatbugModelSwarm to get the list. Each bug in the list responds to a method getUnhappiness. The combined effect of these elements is to create the sequence which will collect the average values.

We hasten to point out that not all sequences need be averages. Output from individual objects can be plotted as well. For an example, we return to the Arborgames EZGraph object called popGraph. The population graph is intended to show how numbers for each sort of tree. There is a Swarm list of all species called speciesList. The following code iterates through the list of all species and tells the popGraph to create a sequence for each one.

for (i = 0; i < speciesNumber; i++)
{
  id aSpecies;
  aSpecies = [speciesList atOffset: i];
  [popGraph createSequence: [aSpecies getSpeciesName]
              withFeedFrom: aSpecies  andSelector: M(getCount)];
}

Each species is able to give its name (respond to getSpeciesName) and provide a count of the number of trees (respond to getCount). If some kind of mistake occurs, say a different kind of object is added into the speciesList, then the program will crash during the run (and there probably will also be a compiler warning).

The work to this point has created the graph object, and told it what to graph, but it does not cause the graph to be presented as the simulation runs. In order to see the plots, the commands have to be part of the observerSwarm's scheduled activity. As it turns out, an EZGraph has a very simple method called step that can do the necessary work, so somewhere in the buildActions method, a command such as this is required:

[displayActions createActionTo: popGraph message: M(step)];

This is often part of an ActionGroup that is scheduled to update all graphs and ZoomRasters.