Using Plotter

W. A. Barrett, San Jose State University

wbarrett1076@comcast.net

October, 2008

version 2

 

Synopsis of the Plotter class and Functions

#include "multiplot.h"

 

Cplotter myplot;   // instantiate plotter object

Cplotter myplot(const char* exeName);  // instantiate with Plotter.exe path

void myplot.title(const char* maintitle);   // window title

void myplot.style(plotStyle style);    // style= LINE, POINT, BAR, default= LINE

void myplot.newplot(const char* title, int color);  // start a plot, with a description

void myplot.point(double x, double y);   // send points to the plot

void myplot.showplot();    // show the plot

void myplot.setX(double xmin, double xmax);  // set plot boundaries

void myplot.setY(double ymin, double ymax);

void myplot.nolimit();    // lift 50,000 point limit on plots

void myplot.clear();    // clear all plots, ready to start a fresh set

void myplot.tmpFolder(const char* folderName);   // specify a writable temp folder

 

Getting Started

Math programs can generate lots of numbers, but how can you tell what the numbers mean?

For example, can you figure out what this list of numbers represents?

17.6453     0.358368

17.6627     0.374607

17.6802     0.390731

17.6976     0.406737

17.7151     0.422618

17.7325     0.438371

17.75  0.45399

17.7675     0.469472

17.7849     0.48481

17.8024     0.5

17.8198     0.515038

17.8373     0.529919

17.8547     0.544639

17.8722     0.559193

17.8896     0.573576

17.9071     0.587785

Well, I didn’t think so.  In fact, the left value is an angle in radians and the right is its cosine.  (Try entering a few numbers on your calculator – make sure you are using “radians”, not “degrees”).

 

It’s easy to write a program that grinds out such a table of numbers. 

The list above was generated by this C++ program:

 

//  grindsine.cpp

#include <iostream>

#include <math.h>

using namespace std;

#define M_PI 3.14159265

int main()

{

  int index;

  for (index= 0; index< 3*360; ++index) {

    cout << M_PI*index/180 << ‘\t’

       << cos(M_PI*index/180)

       << endl;

  }

  return 0;

}

The formula M_PI*index/180 converts an angle in degrees (the index) into the equivalent angle in radians, e.g.

We Need a Plotter

Plotter is an object-oriented tool, based on a class called Cplotter.   It works with any Windows system (not Linux, at least not yet).

Here’s what your program above looks like with Plotter installed.

Instead of writing the points to the output stream, it will generate a plot in a separate window:

 

//  grindsine.cpp

#include <iostream>

#include <string>

#include "multiplot.h"

#include <math.h>

 

using namespace std;

using namespace multiplot;

 

#define M_PI 3.14159265

 

int main()

{

  try {

    Cplotter myplot;

 

    myplot.title("My sine wave");

    myplot.newplot("sine wave");

    int index;

    for (index= 0; index< 3*360; ++index) {

      myplot.point(M_PI*index/180, cos(M_PI*index/180));

    }

    myplot.showplot();

  } catch (const char *emsg) {

    cout << emsg << endl;

  }

  return 0;

}

 

Here’s what you should see:

 

Click EXIT or the X in the upper right corner to make the window go away; this also permits your program to continue – the “showplot()” call suspends your program until you make the plot go away.

Installing Plotter

·        Download these files from Barrett’s web site: Plotter.exe, multiplot.h, multiplot.cpp and/or multiplot.lib.

o       Save plotter.exe into C:\ if you can.  This is its default location.

o       If you can't save plotter.exe in C:\, place it somewhere else, and write its full path name in the Cplotter constructor.

o       Save the multiplot files into your project folder or somewhere accessible to your project.  The .h and .lib files may do the trick, but if the library file generates problems, include the .cpp in your compilation and drop the .lib file.

·        Link multiplot.lib into your executable, or compile multiplot.cpp, linking it into your program files.

o       You will have to add multiplot.lib into the link section of Visual Studio.

o       You may have to add the directive /force:multiple into the link section of Visual Studio.

o       You may have to navigate to "Tools/Options/Directories/Show directories for/Library Files" in Visual Studio, then add a line that specifies the folder containing multiplot.lib.

·        Follow the pattern given above, or later in this document in your calling program.  Here are the details:

o       Add this include directive to your program:

#include “multiplot.h”

o       Add this “using” to your program:

using namespace multiplot;

o       Create an instance of Cplotter in your program:

Cplotter myplot;

o       If  Plotter.exe is not in the root C drive, e.g. at C:\Plotter.exe, then you should create the Cplotter instance with the full path of this file.  For example, if you've installed it in the folder C:\exefiles, then construct Cplotter this way instead:

Cplotter myplot("d:\\exefiles\\Plotter.exe");

o       Set the title of the plotter window – you can omit this if you like:

myplot.title("this is my title");

o       Start each plot with its own title and color.  You must do this even if only one plot is being shown.  (The color can be left out).

myplot.newplot("one plot", 0xFF0000);

o        Send pairs of points to myplot in your program.  (Of course, you can also print them as text as above, if you want to).

for (....) {

  myplot.point(x, y);   // where (x, y) is an int, float or double

}

o       After you’ve sent one or more plots to the plotter object, call its member function showplot:

myplot.showplot();

o       The Cplotter code (constructor, etc.) should be embedded in a try { ... } catch (const char* emsg) { ... } block.  On any error, a message is thrown to the catch block – you may wish to print emsg in the catch block.

Special Cases

The executable program Plotter.exe can be placed anywhere in disk, but C:\Plotter.exe is the recommended location. 

·        If this is somewhere else, add its full path name to your Cplotter constructor as suggested above.

Plotter needs to write, then read, to a temporary directory.  The default is C:\TMP.

·        You may have to create this (or some other) directory, through Windows.

·        If, for some reason, your program cannot write to C:\TMP, you can specify another temporary (writable) directory with the tmpFolder member function.  Call this after you've instantiated a plotter object with Cplotter, but before any showplot call.

 

Adding More Points or Starting Over

Do you want to add more points to your plot?  Just have your program call point some more, then call showplot to display the extended graph. 

You could even call showplot on each new point, but you’ll spend all your time clicking on the X button.  Also it may balk at plotting just one point, and the scaling coordinates will most likely shift on each call.

Want to start over with a fresh plot?  You can use the same Cplotter object.  Just call the member function clear to erase the old set of points and start over:

myplot.clear();

 

Line Segments

Plotter does its job by drawing a line segment from one point to the next.  You can see these segments in the plot if you don’t have very many points plotted. 

The grindsine program given above generates over a thousand points.  The line segments are quite tiny, in fact many of them just show up as single points on the screen. 

If you only generate a few dozen points, you will see these straight line segments. 

We say that it draws an open polygon – the first and last points aren’t connected.

 

Closing the Polygon

Plotter draws an open polygon, as we’ve explained above.  To close the polygon, you need to call myplot.point(...) the last time, using the first point.  This will cause the last point to be connected to the first point, closing the figure.

 

Setting the Coordinates

If you know what maximum and minimum coordinates you’d like in your plots, you can tell Plotter to use them instead of trying to figure it out from your data. 

To fix the X coordinates, call this function:

plotter.setX(double lower, double upper);

Here, lower must be less than upper, or this will be ignored. 

Points whose X coordinates are outside this range just won’t be seen in your plot.

To fix the Y coordinates, call this:

plotter.setY(double lower, double upper);

·        Any set coordinates will be released by the clear() function call, so be sure to reset them if necessary.

 

Would you like to see a closed seven-sided polygon with a centered coordinate system?  Download, compile and execute this program:

 

//  poly7.cpp

//  Draws a 7-sided polygon centered on the origin

 

#include <iostream>

#include <string>

#include "multiplot.h"

#include <math.h>

 

using namespace std;

using namespace multiplot;

 

#define M_PI 3.14159265

#define FACETS 7

 

int main()

{

  try {

    Cplotter myplot;

 

    myplot.title("A seven-sided polygon");

    myplot.newplot("");

    int index;

    double radius= 25;

    for (index= 0; index<= FACETS; ++index) {

      double angle= 2*M_PI*index/FACETS;

      double x= radius * cos(angle);

      double y= radius * sin(angle);

      myplot.point(x, y);

    }

    myplot.setX(-1.2*radius, 1.2*radius);   // fix the coordinates

    myplot.setY(-1.2*radius, 1.2*radius);

    myplot.showplot();

  } catch (const char *emsg) {

    cout << emsg << endl;

  }

  return 0;

}

 

Here’s what that looks like –

 

 

Limit on Number of Points

You are limited to 50,000 points in any one plot.  More, and you’ll get an error return from showPlot().

If you really need to send more points, you can remove this limit by calling myplot.nolimit() before called showPlot().  This will allow you to break the bank if you are so inclined!

Beware of Infinities!

Some math functions “blow up” at certain values.  For example, tan(q) goes to infinity when q  approaches 90° or 270°.

If any of the point values passed to Plotter are very, very large, or infinity, you may get an exception error or a plot that doesn’t seem to contain anything.  Remember that Plotter scales its plots so that each of the points will just lie within its boundaries.  So a large number will force a huge boundary, totally masking any smaller values.  (This won’t apply if you’ve set the coordinate values yourself – large values will just not be seen in your plot).

If you need to plot really large and really small numbers along one or the other axis, consider taking the logarithm of that value and passing that to plotter – log10(x) returns the logarithm to the base 10, and log(x) returns the natural logarithm (base e).

Automatic Scaling

By default, Plotter tries to work out an appropriate scaling for your plots (the X and Y axis units).  This can only be done if there is some variation in both the x and y points sent to the plot, overall.  With no variation along one or the other axis, you'll get an error message instead.

The cure?  Set plot boundaries with setX and/or setY.  Or ensure that your plots have some variation in both directions.

Multiple Plots

You will probably want to show more than one plot on the same graph.

That’s easy – call myplot.newplot(description, color) to start a fresh plot in a new color (or the same color, for that matter).

o       After each myplot.newplot call, send the points to the plot through myplot.point(x, y); as above

o       You can omit color, and the plotter will assign different colors to your plots

o       You’ll see a legend printed to the right of the plots with your description and the line color used

 

CAUTION:  If you specify a color for any of these plots, specify a color for each of the plots.  Otherwise, you’ll get a failure message.

It’s usually best to let Plotter choose a spectrum of colors for the separate plots.

 

Here’s an example program that plots 6 polygons, each 7-sided, with colors chosen by Plotter:

//  plot7.cpp

//  Draws a set of 7-sided polygons centered on the origin

 

#include <iostream>

#include "multiplot.h"

#include <math.h>

 

using namespace std;

using namespace multiplot;

 

#define M_PI 3.14159265

#define FACETS 7

#define POLYGONS 6

 

int main()

{

  try {

    Cplotter myplot;

 

    myplot.title("Set of polygons");

    int poly;

    double radius= 25;

    for (poly= 0; poly< POLYGONS; ++poly) {

      // start a new plot with Plotter-chosen colors

      char msg[50];

      sprintf(msg, "polygon %d", poly);

      myplot.newplot(msg);  

 

      int index;

      for (index= 0; index<= FACETS; ++index) {

        double angle= 2*M_PI*(double(index)/FACETS + double(poly)/POLYGONS);

        double x= radius * cos(angle);

        double y= radius * sin(angle);

        myplot.point(x, y);

      }

    }

    myplot.setX(-1.1*radius, 1.1*radius);

    myplot.setY(-1.1*radius, 1.1*radius);

    myplot.showplot();

  } catch (const char* emsg) {

    cout << emsg << endl;

  }

  return 0;

}

 

And here’s the resulting plot:

 

 

 

How to Specify a Color

The color in the function myplot.newplot(string descr, int color) specifies a color as a single integer, using the format 0x00RRGGBB.

For example, to specify a pure red, use

0xFF0000

Green is

0x00FF00

Blue is

0x0000FF

White is

0xFFFFFF

Black is just

0

 

The idea is that each of the RGB color components can be specified as a value between 0 and 0xFF (or 255), where 0 is dark and 255 is brightest color.  Most of the colors in the rainbow can be formed by combining color components.  The components are combined additively, so that when each of the components are 255, you have the brightest possible color, white.

 

Then the red component has to be in bits 16 through 23, the green component in bits 8 through 15, and the blue component in bits 0 through 7.

If you have separate integers representing red, green, blue, then the composite color can be written this way:

int color= ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF)

If you are quite sure that each of these components is between 0 and 255, you can write it this way instead:

int color= (red << 16) | (green << 8) | blue;

 

Note that the plotting background is white, so you should avoid colors whose overall intensity is close to white.  You’ll notice that the yellow polygon barely shows up in the above figure.  All of the Plotter-selected colors are at maximum intensity, though they span the chroma values evenly.

Point Plots

Instead of plotting connected lines, you may also plot individual points.  This works well for scatter plots, for example.  You can have several different series of points, each in their own color.  As before, each series will have its own colored caption.

The points are not single pixels, rather each one is a 3x3 square of pixels centered on your point.

To get a point plot, write this statement into your plotter program somewhere before calling myplot.showplot():

myplot.style(POINT);

Bar Graph

Plotter can generate a simple bar graph, or set of bar graphs.

Put this statement into your program somewhere before calling  myplot.showplot():

myplot.style(BAR);

 

Here’s an example of a program that generates a set of two separate bar graphs:

//  bargraph.cpp

//  Illustrates a bar graph

 

#include <iostream>

#include <string>

#include "multiplot.h"

#include <math.h>

 

using namespace std;

using namespace multiplot;

 

int sales[]= { 1700, 1500, 2200, 2700, 3300, 4450};

int profit[]= { 175,  170,  200,  250,  300,  500};

 

int main()

{

  try {

    Cplotter myplot;

 

    myplot.title("Sales and profits for MyCompany");

 

    myplot.setX(2001,2007);   // fix the coordinates

    myplot.setY(0, 5000);

    myplot.style(BAR);

 

    // plot the sales

    myplot.newplot("Sales, dollars");  // color chosen automatically

    int k;

    for (k= 0; k< sizeof(sales)/sizeof(sales[0]); ++k)

      myplot.point(2001+k, sales[k]);

 

    // plot the profit

    // NOTE: must have same number of points as 'sales'

    myplot.newplot("Profit, dollars");

    for (k= 0; k< sizeof(sales)/sizeof(sales[0]); ++k)

      myplot.point(2001+k, profit[k]);

 

    myplot.showplot();

  } catch (const char *emsg) {

    cout << emsg << endl;

  }

 

  return 0;

}

 

And here’s the graph generated by it.  Admittedly, a bit crude, since the “years” show up as 2001, 2001.5, etc., but you can’t have everything!

Notice that we set the X coordinates to include one extra year, since the bars are shown to the right of each X coordinate.

 

 

CAVEATS:  You need to observe these guidelines, otherwise your bar graph will turn into a line graph instead:

·        Each bar graph series must have exactly the same number of points.  In the example above, both the sales and the profits have exactly 6 values.

·        Don’t include too many points.  The bars will get thinner, but when they get too thin to show up properly in pixel form, the graph will switch to a line graph.  You have 500 pixels across for the bars.  Spacing between one bar-set and another is 2 pixels, and the minimum bar width is 2 pixels.  Figure it out yourself.

 

Copying or Printing Your Plot

One way to copy the plot window is to download the Gadwin screen grabber, from www.gadwin.com/download.

This is a shareware utility.

·        Install it in the default location. 

·        Run the program, which will ask where you want the screen dump to go.  I like to send it to the clipboard, but you can also have it printed or sent to a file.

·        Press the PrintScreen key to capture a screen that you like.  It’ll go to wherever you chose.

·        You can then paste the screen into any of several utilities, such as Word or a photo editing tool.

Note that you are capturing color pixels, not some list of numbers or other binary data.

 

Another way to create your own plot is to transfer your plot data to Excel.

·        Pull down the File menu in the Plotter window.

·        Click on 'Write an Excel text file'.

·        Save the text file somewhere, preferably with a TXT extension.

·        Right click on the text file, then select "open with Excel".

·        Your plotter values should be listed as separate columns of numbers.  Column A will typically be the X coordinates.

·        Use Excel's plotter utility to construct your plot.