Next: , Previous: C Compiling and Linking, Up: C Programming


9.2.4 Sample drawings in C

The following is a sample application, written in C, that invokes GNU 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 (plPlotter *plotter, double dx, double dy, int order)
     {
       if (order >= MAXORDER)
         /* continue path along (dx, dy) */
         pl_fcontrel_r (plotter, dx, dy);
       else
         {
           draw_c_curve (plotter,
                         0.5 * (dx - dy), 0.5 * (dx + dy), order + 1);
           draw_c_curve (plotter,
                         0.5 * (dx + dy), 0.5 * (dy - dx), order + 1);
         }
     }
     
     int main ()
     {
       plPlotter *plotter;
       plPlotterParams *plotter_params;
     
       /* set a Plotter parameter */
       plotter_params = pl_newplparams ();
       pl_setplparam (plotter_params, "PAGESIZE", "letter");
     
       /* create a Postscript Plotter that writes to standard output */
       if ((plotter = pl_newpl_r ("ps", stdin, stdout, stderr,
                                  plotter_params)) == NULL)
         {
           fprintf (stderr, "Couldn't create Plotter\n");
           return 1;
         }
     
       if (pl_openpl_r (plotter) < 0)      /* open Plotter */
         {
           fprintf (stderr, "Couldn't open Plotter\n");
           return 1;
         }
       pl_fspace_r (plotter, 0.0, 0.0, 1000.0, 1000.0); /* set coor system */
       pl_flinewidth_r (plotter, 0.25);    /* set line thickness */
       pl_pencolorname_r (plotter, "red"); /* use red pen */
       pl_erase_r (plotter);               /* erase graphics display */
       pl_fmove_r (plotter, 600.0, 300.0); /* position the graphics cursor */
       draw_c_curve (plotter, 0.0, 400.0, 0);
       if (pl_closepl_r (plotter) < 0)     /* close Plotter */
         {
           fprintf (stderr, "Couldn't close Plotter\n");
           return 1;
         }
     
       if (pl_deletepl_r (plotter) < 0)    /* delete Plotter */
         {
           fprintf (stderr, "Couldn't delete Plotter\n");
           return 1;
         }
       return 0;
     }

As you can see, this application begins by creating a plPlotterParams object to hold Plotter parameters, and sets the PAGESIZE parameter. It then calls the pl_newpl_r 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_setplparam. The PAGESIZE parameter is one of several Plotter parameters that an application programmer may set. For a list, see Plotter Parameters.

After the Plotter is created, the application opens it and draws the “C” curve recursively. The drawing of the curve is accomplished by calling the pl_fmove_r function to position the Plotter's graphics cursor, and then calling draw_c_curve. This subroutine repeatedly calls pl_fcontrel_r. The pl_fcontrel_r function continues a path by adding a line segment to it. The endpoint of each line segment is specified in relative floating point coordinates, i.e., as a floating point offset from the previous cursor position. After the “C” curve is drawn, the Plotter is closed by calling pl_closepl_r, which automatically invokes pl_endpath_r to end the path. A Postscript file is written to standard output when pl_deletepl_r is called to delete the Plotter.

Specifying "png", "pnm", "gif", "svg", "ai", "cgm", "fig", "pcl", "hpgl", "regis", "tek", or "meta" as the first argument in the call to pl_newpl_r, 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 "svg", "ai", "cgm", "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 plot.

If "X" were specified as the first argument of pl_newpl_r, 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 "png", "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 pl_setplparam before pl_newpl_r is called, set it as the value of the parameter XDRAWABLE_DRAWABLE1 or XDRAWABLE_DRAWABLE2. For the parameters that affect X Drawable Plotters, see Plotter 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 (plPlotter *plotter,
                             char *s, double size, double angle)
     {
       double true_size, width;
     
       pl_ftextangle_r (plotter, angle);      /* set text angle (degrees) */
       true_size = pl_ffontsize_r (plotter, size);  /* set font size */
       width = pl_flabelwidth_r (plotter, s); /* compute width of string */
       pl_fellipserel_r (plotter, 0.0, 0.0,
                         EXPAND * 0.5 * width, EXPAND * 0.5 * true_size,
                         angle);              /* draw surrounding ellipse */
       pl_alabel_r (plotter, 'c', 'c', s);    /* draw centered text string */
     }
     
     int main()
     {
       plPlotter *plotter;
       plPlotterParams *plotter_params;
       int i;
     
       /* set a Plotter parameter */
       plotter_params = pl_newplparams ();
       pl_setplparam (plotter_params, "PAGESIZE", "letter");
     
       /* create a Postscript Plotter that writes to standard output */
       if ((plotter = pl_newpl_r ("ps", stdin, stdout, stderr,
                                  plotter_params)) == NULL)
         {
           fprintf (stderr, "Couldn't create Plotter\n");
           return 1;
         }
     
       if (pl_openpl_r (plotter) < 0)      /* open Plotter */
         {
           fprintf (stderr, "Couldn't open Plotter\n");
           return 1;
         }
       /* specify user coor system */
       pl_fspace_r (plotter, -(SIZE), -(SIZE), SIZE, SIZE);
       pl_pencolorname_r (plotter, "blue");     /* use blue pen */
       pl_fillcolorname_r (plotter, "white");   /* set white fill color */
       pl_filltype_r (plotter, 1);   /* fill ellipses with fill color */
       /* choose a Postscript font */
       pl_fontname_r (plotter, "NewCenturySchlbk-Roman");
     
       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_r (plotter, radius * cos (theta), radius * sin (theta));
           draw_boxed_string (plotter, "GNU libplot!", 0.04 * radius,
                              (180.0 * theta / M_PI) - 90.0);
         }
     
       if (pl_closepl_r (plotter) < 0)        /* close Plotter */
         {
           fprintf (stderr, "Couldn't close Plotter\n");
           return 1;
         }
       if (pl_deletepl_r (plotter) < 0)       /* delete Plotter */
         {
           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_r, pl_fontsize_r, and pl_textangle_r, or their floating point counterparts pl_ffontname_r, pl_ffontsize_r, and pl_ftextangle_r. 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_r or pl_ffontsize_r, 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 Text Fonts.

If you replace "ps" by "X" in the call to pl_newpl_r, 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 core X font, such as the widely available scalable font "charter-medium-r-normal", or the traditional screen font "fixed". For the format of font names, see Text Fonts in X. If the X Plotter is unable to retrieve the font you specify, it will first attempt to use a default scalable font ("Helvetica", interpreted in the context of the X Window System as "helvetica-medium-r-normal"), 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 ancient (pre-X11R6) X Window System display, you will find that retrieving a font is a time-consuming operation. The above example may run slowly on such 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 font. If this feature is available, the X Plotter will automatically take advantage of it to save time.