The Plotutils Package - Sample C Drawings

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

### Sample drawings in C

The following is a sample application, written in C, that invokes `libplot` operations to draw vector graphics. It draws an intricate and beautiful path (Bill Gosper's "C" curve, discussed as Item #135 in HAKMEM, MIT Artificial Intelligence Laboratory Memo #239, 1972). As the numeric constant `MAXORDER` (here equal to 12) is increased, the path will take on the shape of a curly letter "C", which is the envelope of a myriad of epicyclic octagons.

    #include <stdio.h>
    #include <plot.h>
    #define MAXORDER 12

    void draw_c_curve (double dx, double dy, int order)
    {
      if (order >= MAXORDER)
        pl_fcontrel (dx, dy);     /* continue path along (dx, dy) */
      else
        {
          draw_c_curve (0.5 * (dx - dy), 0.5 * (dx + dy), order + 1);
          draw_c_curve (0.5 * (dx + dy), 0.5 * (dy - dx), order + 1);
        }
    }

    int main ()
    {
      int handle;

      /* set a Plotter parameter */
      pl_parampl ("PAGESIZE", "letter");

      /* create a Postscript Plotter that writes to standard output */
      if ((handle = pl_newpl ("ps", stdin, stdout, stderr)) < 0)
        {
          fprintf (stderr, "Couldn't create Plotter\n");
          return 1;
        }
      pl_selectpl (handle);       /* select the Plotter for use */

      if (pl_openpl () < 0)       /* open Plotter */
        {
          fprintf (stderr, "Couldn't open Plotter\n");
          return 1;
        }
      pl_fspace (0.0, 0.0, 1000.0, 1000.0); /* specify user coor system */
      pl_flinewidth (0.25);       /* line thickness in user coordinates */
      pl_pencolorname ("red");    /* path will be drawn in red */
      pl_erase ();                /* erase Plotter's graphics display */
      pl_fmove (600.0, 300.0);    /* position the graphics cursor */
      draw_c_curve (0.0, 400.0, 0);
      if (pl_closepl () < 0)      /* close Plotter */
        {
          fprintf (stderr, "Couldn't close Plotter\n");
          return 1;
        }

      pl_selectpl (0);            /* select default Plotter */
      if (pl_deletepl (handle) < 0) /* delete Plotter we used */
        {
          fprintf (stderr, "Couldn't delete Plotter\n");
          return 1;
        }
      return 0;
    }

As you can see, this application begins by calling the `pl_newpl` function to create a Postscript Plotter. The Postscript Plotter will produce output for a US letter-sized page, though any other standard page size, e.g., "a4", could be substituted. This would be arranged by altering the call to `pl_parampl`. The `PAGESIZE` parameter is one of several Plotter parameters that an application programmer may set by calling `pl_parampl`. For a complete list, see section Device driver parameters.

After the Plotter is created, the application selects it for use, opens it, and draws the "C" curve recursively. The drawing of the curve is accomplished by calling the `pl_fmove` function to position the Plotter's graphics cursor, and then calling `draw_c_curve`. This subroutine repeatedly calls `pl_fcontrel`. The `pl_fcontrel` function continues a path by adding a line segment to it. The endpoint of each line segment is specified in relative coordinates, i.e., as an offset from the previous cursor position. After the "C" curve is drawn, the Plotter is closed. A Postscript file is written to standard output when `pl_deletepl` is called to delete the Plotter.

Specifying "pnm", "gif", "ai", "fig", "pcl", "hpgl", "tek", or "meta" as the first argument in the call to `pl_newpl`, instead of "ps", would yield a Plotter that would write graphics to standard output in the specified format, instead of Postscript. The `PAGESIZE` parameter is relevant to the "ai", "fig", "pcl", and "hpgl" output formats, but is ignored for the others. Specifying "meta" as the Plotter type may be useful if you wish to avoid recompilation for different output devices. Graphics metafile output may be piped to the `plot` utility and converted to any other supported output format, or displayed in an X window. See section The `plot` Program.

If "X" were specified as the first argument of `pl_newpl`, the curve would be drawn in a popped-up X window, and the output stream argument would be ignored. Which X Window System display the window would pop up on would be determined by the `DISPLAY` parameter, or if that parameter were not set, by the `DISPLAY` environment variable. The size of the X window would be determined by the `BITMAPSIZE` parameter, or if that parameter were not set, by the `BITMAPSIZE` environment variable. The default value is "570x570". For the "pnm" and "gif" Plotter types, the interpretation of `BITMAPSIZE` is similar.

You could also specify "Xdrawable" as the Plotter type. For you to make this work, you would need to know a bit about X Window System programming. You would need to create at least one X drawable (i.e., window or a pixmap), and by invoking the `pl_parampl` function before `newpl` is called, set it as the value of the parameter `XDRAWABLE_DRAWABLE1` or `XDRAWABLE_DRAWABLE2`. For the parameters that affect X Drawable Plotters, see section Device driver parameters.

The following is another sample application, written in C, that invokes `libplot` operations to draw vector graphics. It draws a spiral consisting of elliptically boxed text strings, each of which reads "GNU libplot!". This figure will be sent to standard output in Postscript format.

    #include <stdio.h>
    #include <plot.h>
    #include <math.h>
    #define SIZE 100.0   /* nominal size of user coordinate frame */
    #define EXPAND 2.2   /* expansion factor for elliptical box */

    void draw_boxed_string (char *s, double size, double angle)
    {
      double true_size, width;

      pl_ftextangle (angle);         /* text inclination angle (degrees) */
      true_size = pl_ffontsize (size);  /* choose font size */
      width = pl_flabelwidth (s);    /* compute width of text string */
      pl_fellipserel (0.0, 0.0,      /* draw surrounding ellipse */
                   EXPAND * 0.5 * width, EXPAND * 0.5 * true_size, angle);
      pl_alabel ('c', 'c', s);       /* draw centered text string */
    }

    int main()
    {
      int handle, i;

      /* set a Plotter parameter */
      pl_parampl ("PAGESIZE", "letter");

      /* create a Postscript Plotter that writes to standard output */
      if ((handle = pl_newpl ("ps", stdin, stdout, stderr)) < 0)
        {
          fprintf (stderr, "Couldn't create Plotter\n");
          return 1;
        }
      pl_selectpl (handle);         /* select the Plotter for use */

      if (pl_openpl () < 0)         /* open Plotter */
        {
          fprintf (stderr, "Couldn't open Plotter\n");
          return 1;
        }
      pl_fspace (-(SIZE), -(SIZE), SIZE, SIZE); /* spec. user coor system */
      pl_pencolorname ("blue");     /* pen color will be blue */
      pl_fillcolorname ("white");
      pl_filltype (1);              /* ellipses will be filled with white */
      pl_fontname ("NewCenturySchlbk-Roman"); /* choose a Postscript font */

      for (i = 80; i > 1; i--)      /* loop through angles */
        {
          double theta, radius;

          theta = 0.5 * (double)i;  /* theta is in radians */
          radius = SIZE / pow (theta, 0.35);  /* this yields a spiral */
          pl_fmove (radius * cos (theta), radius * sin (theta));
          draw_boxed_string ("GNU libplot!", 0.04 * radius,
                              (180.0 * theta / M_PI) - 90.0);
        }

      if (pl_closepl () < 0)        /* close Plotter */
        {
          fprintf (stderr, "Couldn't close Plotter\n");
          return 1;
        }
      pl_selectpl (0);
      if (pl_deletepl (handle) < 0) /* delete Plotter we used */
        {
          fprintf (stderr, "Couldn't delete Plotter\n");
          return 1;
        }
      return 0;
    }

This example shows what is involved in plotting a text string or text strings. First, the desired font must be retrieved. A font is fully specified by calling `pl_fontname`, `pl_fontsize`, and `pl_textangle`, or their floating point counterparts `pl_ffontname`, `pl_ffontsize`, and `pl_ftextangle`. Since these three functions may be called in any order, each of them returns the size of the font that it selects, as a convenience to the programmer. This may differ slightly from the size specified in the most recent call to `pl_fontsize` or `pl_ffontsize`, since many Plotters have only a limited repertory of fonts. The above example plots each text string in the "NewCenturySchlbk-Roman" font, which is available on Postscript Plotters. See section Available text fonts.

If you replace "ps" by "X" in the call to `pl_newpl`, an X Plotter rather than a Postscript Plotter will be used, and the spiral will be drawn in a popped-up X window. If your X display does not support the "NewCenturySchlbk-Roman" font, you may substitute any other scalable font, such as the widely available "utopia-medium-r-normal". For the format of font names, see section Available text fonts for the X Window System. If the X Plotter is unable to retrieve the font you specify, it will first attempt to use a default scalable font ("Helvetica"), and if that fails, use a default Hershey vector font ("HersheySerif") instead. Hershey fonts are constructed from line segments, so each built-in Hershey font is available on all types of Plotter.

If you are using an older (pre-X11R6) X Window System display, you will find that retrieving a scalable font is a time-consuming operation. The above example may run slowly on some older X displays, since a new font must be retrieved before each text string is drawn. That is because each text string has a different angle of inclination. It is possible to retrieve individual characters from an X11R6 display, rather than retrieving an entire rasterized font. If this feature is available, the X Plotter will automatically take advantage of it to save time.

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