Go to the first, previous, next, last section, table of contents.

Picture example

@cindex{Picture, scatter plot} \begin{figure} \vspace{0.2in} \centerline{\psfig{figure=plot1.eps}} \vspace{0.2in} \caption{{\tt graph (scatter) dataPts} - scatter plot of annual data} \label{scatter-plot} \end{figure}

Scatter plot

To further demonstrate and bring together the various features that the Picture data type provides, let's consider the problem of plotting 2D graphs. A common situation is to have a set of data generated by a program, that we want to visualise quickly using a graph. For the purpose of this example, let us assume that the data measure the annual distribution of some value, producing output like Figure See section Structured Graphics in Haskell. The X axis represents the months and the Y axis the values we've measured each month in, the number of bugs found in a compiler, say. The Picture representing this graph consists of several smaller pictures joined together, starting with the gridded background:

\vspace{0.15in} \hbox{ \hspace{0.3in} \begin{minipage}[b]{2.6in} \begin{verbatim} grid :: Size -> Size -> Picture grid (w,h) (stepx,stepy) = let pen = [PenForeground grey50, PenLineStyle (LineOnOffDash 1 1)] no_lines_x = h `div` stepx no_lines_y = w `div` stepy in Pen pen $ Overlay (Move (OffDir Centre) $ Rectangle (w,h)) (overlay (Move (OffDir Centre) $ hlines stepx no_lines_x w) (Move (OffDir Centre) $ Transform (rotate (pi/2)) $ hlines stepy no_lines_y h) \end{verbatim} \end{minipage} \vbox{\vspace*{0.8in} \psfig{figure=grid.eps} \vspace*{0.2in}}}

grid :: Size -> Size -> Picture
grid (w,h) (stepx,stepy) =
 let
  pen =
   [PenForeground grey50,
    PenLineStyle (LineOnOffDash 1 1)]
  no_lines_x = h `div` stepx
  no_lines_y = w `div` stepy
 in
 Pen 
     pen $
 Overlay
   (Move (OffDir Centre) $ 
    Rectangle (w,h))
   (overlay
     (Move (OffDir Centre) $ 
      hlines stepx no_lines_x w)
     (Move (OffDir Centre) $ 
      Transform (rotate (pi/2)) $
      hlines stepy no_lines_y h)

 Grid

The grid function, given a size and spacing between the grid lines in both directions, returns a Picture of the grid, built by overlaying horizontal and vertical lines. To make the grid-lines appear discretely in the background, we apply a pen modifier that dashes the lines and renders them in grey (see Appendix for definition of the graphical attributes). The picture of the horizontal lines hlines is also a combined picture:


hlines :: Unit -> Unit -> Unit -> Picture
hlines spc no x =
 nabove
    (map (Transform (xlt (0,spc)))
         (replicate no $ hline x))

nabove :: [Picture] -> Picture
nabove = foldr (above) NullPic -- empty picture

The horizontal lines are composed out of a collection of lines arranged vertically using above. To achieve the necessary spacing between the lines, each line is translated so as to enlarge the bounding box the above uses to compute the geometric arrangement between two pictures.

The axes of the coordinate system are also created by combining smaller pictures together, this time two arrowed lines:


axes :: Size -> Picture
axes (w,h) =
 overlay 
   (leftArrowLine w)
   (upArrowLine   h)

The arrowed lines can also be subdivided into a picture element for the arrow line and the head that has been combined together, but for lack of space we will leave out their definition here.

To get the picture of a gridded coordinate system, we simply overlay the picture returned by axes with that for the grids, making sure of moving the origin of the grid to its lower left corner, so that the gridding coincides with the origin of the axes:


cartesian :: Size -> Size -> Picture
cartesian sz steps =
 overlay
   (axes sz)
   (Move (OffDir SouthWest) $
    grid sz steps)

To plot data points within the coordinate system, the picture(s) representing the points just have to be placed on top. Here's how a scatter plot of a set of coordinates could be done:


scatter :: [Coord] -> Picture
scatter = noverlay $ map (plotAt)
 where
  plotAt pos = 
   Transform 
    (xlt pos)
    (filledCircle 2)

noverlay :: [Picture] -> Picture
noverlay = foldr (overlay) NullPic

The different points are plotted by translating a circle to each data point and then overlaying all the resulting pictures. Since overlaying is performed by matching up the origins of two pictures, and the points to be plotted are all expressed within the same coordinate system, the pictures will also have the same origin. The resulting plot can then be superimposed on a coordinate system to produce the plot in Figure See section Structured Graphics in Haskell:


graph :: ([Coord] -> Picture) 
      -> [Int]
      -> Size
      -> Size
      -> Picture
graph plot pts size steps@(dx,dy) = 
 let
  coords = zip pts [dx `div` 2,dx..]
 in
 overlay
   (plot coords)
   (cartesian size steps) 

The graph takes a function for producing the plot of the supplied data together with the data points themselves and a size plus grid steps. For the purpose of this example, we assume that the size and data points are in the same range, adding the code that checks and appropriately scales the data to fit has been omitted for reasons of space.

Now let's change the plot by having the points connected up via a solid line instead:


solid :: [Coord] -> Picture
solid ls = 
 overlay 
    (polyline ls)
    (scatter ls)

The scatter plot as produced with scatter is overlaid with a poly-line connecting all the data points up. Using solid in a call to graph will produce output like this:

\centerline{\psfig{figure=plot2.eps}}

Picture plot


Go to the first, previous, next, last section, table of contents.