home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / fortran / pgplot5.1 / pgplot5 / pgplot5.1.0 / examples-src / pgmdemo.c < prev   
Encoding:
C/C++ Source or Header  |  1996-04-11  |  33.7 KB  |  1,184 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <ctype.h>
  5. #include <math.h>
  6.  
  7. #include <Xm/Xm.h>
  8. #include <X11/Intrinsic.h>
  9. #include <Xm/MainW.h>
  10. #include <Xm/RowColumn.h>
  11. #include <Xm/PushB.h>
  12. #include <Xm/Frame.h>
  13. #include <Xm/LabelG.h>
  14. #include <Xm/SeparatoG.h>
  15. #include <Xm/CascadeB.h>
  16. #include <Xm/SelectioB.h>
  17. #include <Xm/TextF.h>
  18.  
  19. #include <X11/cursorfont.h>
  20.  
  21. #include "XmPgplot.h"
  22. #include "cpgplot.h"
  23.  
  24. /* Make the demo backwardly compatible with Motif 1.1 */
  25.  
  26. #if XmVersion <= 1001
  27. #ifndef XmFONTLIST_DEFAULT_TAG
  28. #define XmFONTLIST_DEFAULT_TAG XmSTRING_DEFAULT_CHARSET
  29. #endif
  30. #endif
  31.  
  32. /*
  33.  * Make the demo backwardly compatible with older versions of X.
  34.  */
  35. #if XtSpecificationRelease <= 4
  36. #define XtSetLanguageProc(a,b,c) (void)((a),(b),(c))
  37. #endif
  38.  
  39. /*
  40.  * Gray-scale images of multiple analytic 2D functions will be supported.
  41.  * Each 2D function will be encapsulated in a C function of the
  42.  * following type.
  43.  */
  44. #define IMAGE_FN(fn) float (fn)(float x, float y)
  45. /*
  46.  * Define a macro for prototyping and defining XtCallbackProc functions.
  47.  */
  48. #define CALL_FN(fn) void (fn)(Widget w, XtPointer client_data, XtPointer call_data)
  49. /*
  50.  * List the prototypes of the available 2D-function functions.
  51.  */
  52. static IMAGE_FN(sinc_fn);
  53. static CALL_FN(sinc_callback);
  54. static IMAGE_FN(gaus_fn);
  55. static CALL_FN(gaus_callback);
  56. static IMAGE_FN(ring_fn);
  57. static CALL_FN(ring_callback);
  58. static IMAGE_FN(sin_angle_fn);
  59. static CALL_FN(sin_angle_callback);
  60. static IMAGE_FN(cos_radius_fn);
  61. static CALL_FN(cos_radius_callback);
  62. static IMAGE_FN(star_fn);
  63. static CALL_FN(star_callback);
  64.  
  65. /* Set the default image size */
  66.  
  67. enum {MAP_SIZE=129};
  68.  
  69. /* Set the number of points plotted per slice */
  70.  
  71. enum {SLICE_SIZE=100};
  72.  
  73. /*
  74.  * Declare a type to hold a single X,Y coordinate.
  75.  */
  76. typedef struct {
  77.   float x, y;       /* World coordinates */
  78. } Vertex;
  79.  
  80. /*
  81.  * Declare the object type that will contain the context of the
  82.  * image and slice plots.
  83.  */
  84. typedef struct {
  85.   Widget w_image;     /* The gray-scale image widget */
  86.   Widget w_slice;     /* The slice-plot image widget */
  87.   float *image;       /* The gray-scale image array */
  88.   float *slice;       /* The slice compilation array */
  89.   float scale;        /* Coversion factor pixels -> coords */
  90.   int image_size;     /* The number of pixels along each side of the image */
  91.   int slice_size;     /* The length of the slice array */
  92.   int xa,xb;          /* Min and max X pixel coordinates */
  93.   int ya,yb;          /* Min and max Y pixel coordinates */
  94.   float datamin;      /* The minimum data value in image[] */
  95.   float datamax;      /* The maximum data value in image[] */
  96.   IMAGE_FN(*fn);      /* The function to be displayed */
  97.   Vertex va;          /* The start of the latest slice line */
  98.   Vertex vb;          /* The end of the latest slice line */
  99.   Widget w_top;       /* The top-level widget of the application */
  100.   Cursor busy;        /* The cursor to display when un-responsive */
  101. } Image;
  102.  
  103. /*
  104.  * Image object contructor and destructor functions.
  105.  */
  106. static Image *new_Image(unsigned image_size, unsigned slice_size,
  107.             IMAGE_FN(*fn), Widget parent, Widget w_top);
  108. static Image *del_Image(Image *im);
  109.  
  110. /*
  111.  * Image and slice display functions.
  112.  */
  113. static void display_fn(Image *im, IMAGE_FN(*fn));
  114. static void display_image(Image *im, int id);
  115. static void display_slice(Image *im, Vertex *va, Vertex *vb);
  116. static void display_help(Image *im);
  117.  
  118. /*
  119.  * The following structure is used to describe menu fields to
  120.  * CreateOptionMenu() and CreatePulldownMenu().
  121.  * Note that title options are denoted by setting callback=NULL,
  122.  * and that separators are specified by setting label=NULL.
  123.  */
  124. typedef struct {
  125.   char *label;              /* The MenuItem label text */
  126.   XtCallbackProc callback;  /* Function to be called when field is selected */
  127. } MenuItem;
  128.  
  129. static Widget CreateOptionMenu(char *name, char *label, Widget parent, int nopt,
  130.                    MenuItem *opts, XtPointer client_data);
  131. static Widget CreatePulldownMenu(char *name, char *label, Widget parent,
  132.                  int nfield, MenuItem *fields,
  133.                  XtPointer client_data);
  134. static Widget CreateMainMenuBar(Image *im, Widget w_main);
  135. static Widget CreatePopupPromptDialog(Widget w, char *name, char *prompt,
  136.                       char *value,
  137.                       XtCallbackProc ok_fn, XtPointer ok_data);
  138.  
  139. static void start_slice_callback(Widget w, XtPointer client_data,
  140.                  XtPointer call_data);
  141. static void end_slice_callback(Widget w, XtPointer client_data,
  142.                    XtPointer call_data);
  143. static CALL_FN(quit_callback);
  144. static CALL_FN(help_callback);
  145. static CALL_FN(save_image_as_callback);
  146. static CALL_FN(save_image_callback);
  147. static CALL_FN(destroy_widget_callback);
  148.  
  149. /*.......................................................................
  150.  * A demo program showing an example of how to use PGPLOT with Motif.
  151.  *
  152.  * Output:
  153.  *  return   int  0 - OK.
  154.  *                1 - Error.
  155.  */
  156. int main(int argc, char *argv[])
  157. {
  158.   XtAppContext app;/* Application context returned by XtVaAppInitialize */
  159.   Widget w_top;    /* The top-level widget of the application */
  160.   Widget w_main;   /* The geometry management widget of the application */
  161.   Widget w_work;   /* The work-area widget of the main window */
  162.   Image *im;       /* Image object container */
  163. /*
  164.  * Initialize Motif.
  165.  */
  166.   XtSetLanguageProc(NULL, NULL, NULL);
  167.   w_top = XtVaAppInitialize(&app, "ImageSlice", NULL, 0,
  168. #if XtSpecificationRelease <= 4
  169.                 (Cardinal *)
  170. #endif
  171.                 &argc, argv, NULL,
  172.                             XmNinput,               True,
  173.                 XmNkeyboardFocusPolicy, XmPOINTER,
  174.                 NULL);
  175. /*
  176.  * Use the standard Motif management widget layout.
  177.  */
  178.   w_main = XtVaCreateManagedWidget("main", xmMainWindowWidgetClass, w_top,
  179.                    XmNscrollBarDisplayPolicy, XmAS_NEEDED,
  180.                    XmNscrollingPolicy,        XmAUTOMATIC,
  181.                    XmNheight, 695,
  182.                    XmNwidth, 420,
  183.                    NULL);
  184. /*
  185.  * Create a row-column widget to contain the two PGPLOT widgets and
  186.  * an option-menu button.
  187.  */
  188.   w_work = XtVaCreateManagedWidget("work", xmRowColumnWidgetClass, w_main,
  189.                    XmNorientation, XmVERTICAL,
  190.                    XmNnumColumns, 1,
  191.                    NULL);
  192. /*
  193.  * Install the row-column widget as the work area of the main window.
  194.  */
  195.   XtVaSetValues(w_main, XmNworkWindow, w_work, NULL);
  196. /*
  197.  * Create the two PGPLOT widgets and the image container object.
  198.  */
  199.   im = new_Image(MAP_SIZE, SLICE_SIZE, ring_fn, w_work, w_top);
  200.   if(!im)
  201.     return 1;
  202. /*
  203.  * Create the application menu-bar.
  204.  */
  205.   {
  206.     Widget w_bar = CreateMainMenuBar(im, w_main);
  207.     XtManageChild(w_bar);
  208.   };
  209. /*
  210.  * Display the widgets.
  211.  */
  212.   XtRealizeWidget(w_top);
  213. /*
  214.  * Open the widgets to PGPLOT.
  215.  */
  216.   if(cpgopen(xmp_device_name(im->w_image)) <= 0 ||
  217.      cpgopen(xmp_device_name(im->w_slice)) <= 0)
  218.     return 1;
  219. /*
  220.  * Display the initial image.
  221.  */
  222.   display_fn(im, ring_fn);
  223. /*
  224.  * Interact with the user.
  225.  */
  226.   XtAppMainLoop(app);
  227.   return 0;
  228. }
  229.  
  230. /*.......................................................................
  231.  * Allocate and return an initialized Image container object.
  232.  * This function creates two PGPLOT widgets. One will be used to display
  233.  * a gray-scale image. The other will be used to display a slice through
  234.  * the image.
  235.  *
  236.  * Note that the widgets are not opened to PGPLOT and nothing
  237.  * will be displayed until display_fn() is first
  238.  * called. These operations must be postponed until after the widgets have
  239.  * been realized.
  240.  *
  241.  * Input:
  242.  *  image_size unsigned    The number of pixels along each side of the
  243.  *                         square image array. This must be an odd
  244.  *                         number (so that there can be a central pixel).
  245.  *  slice_size unsigned    The dimension of the slice array (>=2).
  246.  *  fn         IMAGE_FN(*) The initial display function.
  247.  *  parent       Widget    The widget in which to create the PGPLOT widgets.
  248.  *  w_top        Widget    The top-level widget of the application.
  249.  * Output:
  250.  *  return   Image *  The new image container, or NULL on error.
  251.  */
  252. static Image *new_Image(unsigned image_size, unsigned slice_size,
  253.             IMAGE_FN(*fn), Widget parent, Widget w_top)
  254. {
  255.   Image *im;       /* The pointer to the container to be returned */
  256.   Widget w_frame;  /* A frame widget */
  257.   int i;
  258. /*
  259.  * Check the arguments.
  260.  */
  261.   if(image_size < 1 || image_size % 2 == 0) {
  262.     fprintf(stderr, "new_Image: Illegal image size requested.\n");
  263.     return NULL;
  264.   };
  265.   if(slice_size < 2) {
  266.     fprintf(stderr, "new_Image: Illegal slice size requested.\n");
  267.     return NULL;
  268.   };
  269.   if(!fn) {
  270.     fprintf(stderr, "new_Image: NULL display function intercepted.\n");
  271.     return NULL;
  272.   };
  273. /*
  274.  * Allocate the container.
  275.  */
  276.   im = (Image *) malloc(sizeof(Image));
  277.   if(!im) {
  278.     fprintf(stderr, "new_Image: Insufficient memory.\n");
  279.     return NULL;
  280.   };
  281. /*
  282.  * Before attempting any operation that might fail, initialize the
  283.  * Image container at least up to the point at which it can safely be
  284.  * passed to del_Image().
  285.  */
  286.   im->w_image = NULL;
  287.   im->w_slice = NULL;
  288.   im->image = NULL;
  289.   im->slice = NULL;
  290.   im->image_size = image_size;
  291.   im->slice_size = slice_size;
  292.   im->scale = 40.0f/image_size;
  293.   im->xa = -(int)image_size/2;
  294.   im->xb = image_size/2;
  295.   im->ya = -(int)image_size/2;
  296.   im->yb = image_size/2;
  297.   im->fn = fn;
  298.   im->busy = None;
  299.   im->w_top = w_top;
  300. /*
  301.  * Now allocate the 2D image array as a 1D array to be indexed in
  302.  * as a FORTRAN array.
  303.  */
  304.   im->image = (float *) malloc(sizeof(float) * image_size * image_size);
  305.   if(!im->image) {
  306.     fprintf(stderr, "new_Image: Insufficient memory.\n");
  307.     return del_Image(im);
  308.   };
  309. /*
  310.  * Initialize the image array.
  311.  */
  312.   for(i=0; i<image_size*image_size; i++)
  313.     im->image[i] = 0.0f;
  314. /*
  315.  * Allocate an array to be used when constructing slices through the
  316.  * displayed image.
  317.  */
  318.   im->slice = (float *) malloc(sizeof(float) * slice_size);
  319.   if(!im->slice) {
  320.     fprintf(stderr, "new_Image: Insufficient memory.\n");
  321.     return del_Image(im);
  322.   };
  323. /*
  324.  * Initialize the slice array.
  325.  */
  326.   for(i=0; i<slice_size; i++)
  327.     im->slice[i] = 0.0f;
  328. /*
  329.  * Create an etched-in frame widget to provide a border for the
  330.  * image window.
  331.  */
  332.   w_frame = XtVaCreateManagedWidget("image_frame", xmFrameWidgetClass,
  333.                     parent,
  334.                     XmNshadowType, XmSHADOW_IN,
  335.                     NULL);
  336. /*
  337.  * Create the image-display widget.
  338.  */
  339.   im->w_image = XtVaCreateManagedWidget("image", xmPgplotWidgetClass,
  340.                     w_frame,
  341.                     XmNheight, 400,
  342.                     XmNwidth, 400,
  343.                     XmpNmaxColors, 50,
  344.                     NULL);
  345. /*
  346.  * Create a pulldown menu of optional 2-D image functions.
  347.  */
  348.   {
  349.     static MenuItem functions[] = {
  350.       {"Display Functions", NULL},  /* Title */
  351.       {NULL, NULL},                 /* Separator */
  352.       {"R=Polar radius", NULL},     /* Label */
  353.       {"A=Polar angle", NULL},      /* Label */
  354.       {NULL, NULL},                 /* Separator */
  355.       {"cos(R)sin(A)",                 ring_callback},
  356.       {"sinc(R)",                      sinc_callback},
  357.       {"exp(-R^2/20.0)",               gaus_callback},
  358.       {"sin(A)",                       sin_angle_callback},
  359.       {"cos(R)",                       cos_radius_callback},
  360.       {"(1+sin(6A))exp(-R^2/100)",     star_callback},
  361.     };
  362.     Widget menu = CreateOptionMenu("functions", "Select a display function:",
  363.                    parent,
  364.                    sizeof(functions)/sizeof(functions[0]),
  365.                    functions, (XtPointer) im);
  366.     if(menu == NULL)
  367.       return del_Image(im);
  368.     XtManageChild(menu);
  369.   };
  370. /*
  371.  * Create an etched-in frame widget to provide a border for the
  372.  * slice-plot window.
  373.  */
  374.   w_frame = XtVaCreateManagedWidget("slice_frame", xmFrameWidgetClass,
  375.                     parent,
  376.                     XmNshadowType, XmSHADOW_IN,
  377.                     NULL);
  378. /*
  379.  * Create the slice-display widget.
  380.  */
  381.   im->w_slice = XtVaCreateManagedWidget("slice", xmPgplotWidgetClass,
  382.                     w_frame,
  383.                     XmNheight, 200,
  384.                     XmNwidth, 400,
  385.                     XmpNmaxColors, 16,
  386.                     NULL);
  387. /*
  388.  * Get the standard X busy cursor.
  389.  */
  390.   im->busy = XCreateFontCursor(XtDisplay(im->w_top), XC_watch);
  391.   return im;
  392. }
  393.  
  394. /*.......................................................................
  395.  * Delete an Image container previously returned by new_Image().
  396.  *
  397.  * Input:
  398.  *  im        Image *  The container to be deleted (or NULL).
  399.  * Output:
  400.  *  return    Image *  The deleted container (always NULL).
  401.  */
  402. Image *del_Image(Image *im)
  403. {
  404.   if(im) {
  405.     if(im->image)
  406.       free(im->image);
  407.     if(im->w_image)
  408.       XtDestroyWidget(im->w_image);
  409.     if(im->w_slice)
  410.       XtDestroyWidget(im->w_slice);
  411.     if(im->busy != None)
  412.       XFreeCursor(XtDisplay(im->w_top), im->busy);
  413.     free(im);
  414.   };
  415.   return NULL;
  416. }
  417.  
  418. /*.......................................................................
  419.  * Display a new function in the image window.
  420.  *
  421.  * Input:
  422.  *  im    Image *   The image context object.
  423.  *  fn IMAGE_FN(*)  The function to be displayed.
  424.  */
  425. static void display_fn(Image *im, IMAGE_FN(*fn))
  426. {
  427.   int ix, iy;  /* The pixel coordinates being assigned */
  428.   float vmin;  /* The minimum pixel value in the image */
  429.   float vmax;  /* The maximum pixel value in the image */
  430.   float *pixel;/* A pointer to pixel (ix,iy) in im->image */
  431. /*
  432.  * Check arguments.
  433.  */
  434.   if(!fn) {
  435.     fprintf(stderr, "display_fn: NULL function.\n");
  436.     return;
  437.   };
  438. /*
  439.  * Disarm the cursor while the image-plot is incomplete.
  440.  */
  441.   xmp_disarm_cursor(im->w_image);
  442. /*
  443.  * Install the new function.
  444.  */
  445.   im->fn = fn;
  446. /*
  447.  * Fill the image array via the current display function.
  448.  */
  449.   pixel = im->image;
  450.   vmin = vmax = im->fn(im->xa * im->scale, im->ya * im->scale);
  451.   for(iy = im->ya; iy <= im->yb; iy++) {
  452.     for(ix = im->xa; ix <= im->xb; ix++) {
  453.       float value = im->fn(ix * im->scale, iy * im->scale);
  454.       *pixel++ = value;
  455.       if(value < vmin)
  456.     vmin = value;
  457.       if(value > vmax)
  458.     vmax = value;
  459.     };
  460.   };
  461. /*
  462.  * Record the min and max values of the data array.
  463.  */
  464.   im->datamin = vmin;
  465.   im->datamax = vmax;
  466. /*
  467.  * Display the new image.
  468.  */
  469.   display_image(im, xmp_device_id(im->w_image));
  470. /*
  471.  * Arm the cursor for user selection of the start position of the
  472.  * first slice line through this image.
  473.  */
  474.   xmp_arm_cursor(im->w_image, XMP_NORM_CURSOR, 0.0f, 0.0f,
  475.          start_slice_callback, im);
  476. /*
  477.  * Display instructions in the slice window.
  478.  */
  479.   display_help(im);
  480.   return;
  481. }
  482.  
  483. /*.......................................................................
  484.  * Display the current image function in a specified PGPLOT device.
  485.  *
  486.  *
  487.  * Input:
  488.  *  im    Image *   The image context object.
  489.  *  id      int     The id of the PGPLOT device to display.
  490.  */
  491. static void display_image(Image *im, int id)
  492. {
  493. /*
  494.  * Since rendering a gray-scale image takes a few seconds
  495.  * display the busy cursor.
  496.  */
  497.   XDefineCursor(XtDisplay(im->w_top), XtWindow(im->w_top), im->busy);
  498.   XFlush(XtDisplay(im->w_top));
  499. /*
  500.  * Select the specified PGPLOT device and display the image array.
  501.  */
  502.   cpgslct(id);
  503.   cpgask(0);
  504.   cpgpage();
  505.   cpgsch(1.0f);
  506.   cpgvstd();
  507.   cpgwnad(im->xa * im->scale, im->xb * im->scale,
  508.       im->ya * im->scale, im->yb * im->scale);
  509.   {
  510.     float tr[6];  /* Coordinate definition matrix */
  511.     tr[0] = (im->xa - 1) * im->scale;
  512.     tr[1] = im->scale;
  513.     tr[2] = 0.0f;
  514.     tr[3] = (im->ya - 1) * im->scale;
  515.     tr[4] = 0.0f;
  516.     tr[5] = im->scale;
  517.     cpggray(im->image, im->image_size, im->image_size,
  518.         1, im->image_size, 1, im->image_size, im->datamax, im->datamin, tr);
  519.   };
  520.   cpgbox("BCNST", 0.0f, 0, "BCNST", 0.0f, 0);
  521.   cpglab("X", "Y", "Image display demo");
  522. /*
  523.  * Revert to the normal X cursor.
  524.  */
  525.   XDefineCursor(XtDisplay(im->w_top), XtWindow(im->w_top), None);
  526.   return;
  527. }
  528.  
  529. /*.......................................................................
  530.  * Display a new slice in the slice window.
  531.  *
  532.  * Input:
  533.  *  im    Image *   The image context object.
  534.  *  va   Vertex *   The vertex of one end of the slice line.
  535.  *  vb   Vertex *   The vertex of the opposite end of the slice line.
  536.  */
  537. static void display_slice(Image *im, Vertex *va, Vertex *vb)
  538. {
  539.   float xa;  /* The start X value of the slice */
  540.   float dx;  /* The X-axis world-coordinate width of one slice pixel */
  541.   float ya;  /* The start Y value of the slice */
  542.   float dy;  /* The Y-axis world-coordinate width of one slice pixel */
  543.   float smin;/* The minimum slice value */
  544.   float smax;/* The maximum slice value */
  545.   float slice_length;  /* The world-coordinate length of the slice */
  546.   float ymargin;       /* The Y axis margin within the plot */
  547.   int i;
  548. /*
  549.  * Determine the slice pixel assignments.
  550.  */
  551.   xa = va->x;
  552.   dx = (vb->x - va->x) / im->slice_size;
  553.   ya = va->y;
  554.   dy = (vb->y - va->y) / im->slice_size;
  555. /*
  556.  * Make sure that the slice has a finite length by setting a
  557.  * minimum size of one pixel.
  558.  */
  559.   {
  560.     float min_delta = im->scale / im->slice_size;
  561.     if(fabs(dx) < min_delta && fabs(dy) < min_delta)
  562.       dx = min_delta;
  563.   };
  564. /*
  565.  * Construct the slice in im->slice[] and keep a tally of the
  566.  * range of slice values seen.
  567.  */
  568.   for(i=0; i<im->slice_size; i++) {
  569.     float value = im->fn(xa + i * dx, ya + i * dy);
  570.     im->slice[i] = value;
  571.     if(i==0) {
  572.       smin = smax = value;
  573.     } else if(value < smin) {
  574.       smin = value;
  575.     } else if(value > smax) {
  576.       smax = value;
  577.     };
  578.   };
  579. /*
  580.  * Determine the length of the slice.
  581.  */
  582.   {
  583.     float xlen = dx * im->slice_size;
  584.     float ylen = dy * im->slice_size;
  585.     slice_length = sqrt(xlen * xlen + ylen * ylen);
  586.   };
  587. /*
  588.  * Determine the extra length to add to the Y axis to prevent the
  589.  * slice plot hitting the top and bottom of the plot.
  590.  */
  591.   ymargin = 0.05 * (im->datamax - im->datamin);
  592. /*
  593.  * Set up the slice axes.
  594.  */
  595.   cpgslct(xmp_device_id(im->w_slice));
  596.   cpgask(0);
  597.   cpgpage();
  598.   cpgbbuf();
  599.   cpgsch(2.0f);
  600.   cpgvstd();
  601.   cpgswin(0.0f, slice_length, im->datamin - ymargin, im->datamax + ymargin);
  602.   cpgbox("BCNST", 0.0f, 0, "BCNST", 0.0f, 0);
  603.   cpglab("Radius", "Image value", "A 1D slice through the image");
  604. /*
  605.  * Draw the slice.
  606.  */
  607.   for(i=0; i<im->slice_size; i++) {
  608.     if(i==0)
  609.       cpgmove(0.0f, im->slice[0]);
  610.     else
  611.       cpgdraw(slice_length * (float)i / (float)im->slice_size, im->slice[i]);
  612.   };
  613.   cpgebuf();
  614.   return;
  615. }
  616.  
  617. /*.......................................................................
  618.  * Display usage instructions in the slice window.
  619.  *
  620.  * Input:
  621.  *  im     Image *   The image object.
  622.  */
  623. static void display_help(Image *im)
  624. {
  625. /*
  626.  * Clear the slice plot and replace it with instructional text.
  627.  */
  628.   cpgslct(xmp_device_id(im->w_slice));
  629.   cpgask(0);
  630.   cpgpage();
  631.   cpgsch(3.5f);
  632.   cpgsvp(0.0, 1.0, 0.0, 1.0);
  633.   cpgswin(0.0, 1.0, 0.0, 1.0);
  634.   cpgmtxt("T", -2.0, 0.5, 0.5,
  635.       "To see a slice through the image, move the");
  636.   cpgmtxt("T", -3.0, 0.5, 0.5,
  637.       "mouse into the image display window and select");
  638.   cpgmtxt("T", -4.0, 0.5, 0.5,
  639.       " the two end points of a line.");
  640. }
  641.  
  642. /*.......................................................................
  643.  * A sinc(radius) function.
  644.  *
  645.  * Input:
  646.  *  x,y     float   The coordinates to evaluate the function at.
  647.  * Output:
  648.  *  return  float   The function value at the specified coordinates.
  649.  */
  650. static IMAGE_FN(sinc_fn)
  651. {
  652.   const float tiny = 1.0e-6f;
  653.   float radius = sqrt(x*x + y*y);
  654.   return (fabs(radius) < tiny) ? 1.0f : sin(radius)/radius;
  655. }
  656.  
  657. /*.......................................................................
  658.  * Callback to select the sinc_fn() fucntion.
  659.  */
  660. static CALL_FN(sinc_callback)
  661. {
  662.   display_fn((Image *) client_data, sinc_fn);
  663. }
  664.  
  665. /*.......................................................................
  666.  * A exp(-(x^2+y^2)/20) function.
  667.  *
  668.  * Input:
  669.  *  x,y     float   The coordinates to evaluate the function at.
  670.  * Output:
  671.  *  return  float   The function value at the specified coordinates.
  672.  */
  673. static IMAGE_FN(gaus_fn)
  674. {
  675.   return exp(-((x*x)+(y*y))/20.0f);
  676. }
  677.  
  678. /*.......................................................................
  679.  * Callback to select the gaus_fn() fucntion.
  680.  */
  681. static CALL_FN(gaus_callback)
  682. {
  683.   display_fn((Image *) client_data, gaus_fn);
  684. }
  685.  
  686. /*.......................................................................
  687.  * A cos(radius)*sin(angle) function.
  688.  *
  689.  * Input:
  690.  *  x,y     float   The coordinates to evaluate the function at.
  691.  * Output:
  692.  *  return  float   The function value at the specified coordinates.
  693.  */
  694. static IMAGE_FN(ring_fn)
  695. {
  696.   return cos(sqrt(x*x + y*y)) * sin(x==0.0f && y==0.0f ? 0.0f : atan2(x,y));
  697. }
  698.  
  699. /*.......................................................................
  700.  * Callback to select the ring_fn() fucntion.
  701.  */
  702. static CALL_FN(ring_callback)
  703. {
  704.   display_fn((Image *) client_data, ring_fn);
  705. }
  706.  
  707. /*.......................................................................
  708.  * A sin(angle) function.
  709.  *
  710.  * Input:
  711.  *  x,y     float   The coordinates to evaluate the function at.
  712.  * Output:
  713.  *  return  float   The function value at the specified coordinates.
  714.  */
  715. static IMAGE_FN(sin_angle_fn)
  716. {
  717.   return sin(x==0.0f && y==0.0f ? 0.0f : atan2(x,y));
  718. }
  719.  
  720. /*.......................................................................
  721.  * Callback to select the sin_angle_fn() fucntion.
  722.  */
  723. static CALL_FN(sin_angle_callback)
  724. {
  725.   display_fn((Image *) client_data, sin_angle_fn);
  726. }
  727.  
  728. /*.......................................................................
  729.  * A cos(radius) function.
  730.  *
  731.  * Input:
  732.  *  x,y     float   The coordinates to evaluate the function at.
  733.  * Output:
  734.  *  return  float   The function value at the specified coordinates.
  735.  */
  736. static IMAGE_FN(cos_radius_fn)
  737. {
  738.   return cos(sqrt(x*x + y*y));
  739. }
  740.  
  741. /*.......................................................................
  742.  * Callback to select the cos_radius_fn() fucntion.
  743.  */
  744. static CALL_FN(cos_radius_callback)
  745. {
  746.   display_fn((Image *) client_data, cos_radius_fn);
  747. }
  748.  
  749. /*.......................................................................
  750.  * A (1+sin(6*angle))*exp(-radius^2 / 100)function.
  751.  *
  752.  * Input:
  753.  *  x,y     float   The coordinates to evaluate the function at.
  754.  * Output:
  755.  *  return  float   The function value at the specified coordinates.
  756.  */
  757. static IMAGE_FN(star_fn)
  758. {
  759.   return (1.0 + sin(x==0.0f && y==0.0f ? 0.0f : 6.0*atan2(x,y)))
  760.     * exp(-((x*x)+(y*y))/100.0f);
  761. }
  762.  
  763. /*.......................................................................
  764.  * Callback to select the star_fn() fucntion.
  765.  */
  766. static CALL_FN(star_callback)
  767. {
  768.   display_fn((Image *) client_data, star_fn);
  769. }
  770.  
  771. /*.......................................................................
  772.  * Create an option menu.
  773.  *
  774.  * Input:
  775.  *  name             char *  The name of the menu.
  776.  *  label            char *  The instructive label to place to the left of
  777.  *                           the option menu.
  778.  *  parent         Widget    The widget in which to place the option menu.
  779.  *  nopt              int    The number of option fields.
  780.  *  opts         MenuItem *  An array of nopt option fields.
  781.  *  client_data XtPointer    The client_data argument to be passed to each
  782.  *                           callback function.
  783.  * Output:
  784.  *  return      Widget    The menu, or NULL on error.
  785.  */
  786. static Widget CreateOptionMenu(char *name, char *label, Widget parent,
  787.                    int nopt, MenuItem *opts, XtPointer client_data)
  788. {
  789.   Widget w_menu;       /* The option menu to be returned */
  790.   Widget w_pulldown;   /* The pulldown-menu of the option menu widget */
  791.   int i;
  792. /*
  793.  * Check arguments.
  794.  */
  795.   if(nopt < 1 || !opts) {
  796.     fprintf(stderr, "CreateOptionMenu: No options.\n");
  797.     return NULL;
  798.   };
  799. /*
  800.  * Create a pulldown menu.
  801.  */
  802.   w_pulldown = XmCreatePulldownMenu(parent, "pulldown", NULL, 0);
  803. /*
  804.  * Create the option-menu button to attach the menu to.
  805.  */
  806.   {
  807.     XmString label_str = XmStringCreateSimple(label);
  808.     Arg args[5];       /* Resource arguments for XmCreateOptionMenu() */
  809.     int narg = 0;
  810.     XtSetArg(args[narg], XmNsubMenuId, w_pulldown); narg++;
  811.     XtSetArg(args[narg], XmNlabelString, label_str); narg++;
  812.     w_menu = XmCreateOptionMenu(parent, name, args, narg);
  813.     XmStringFree(label_str);
  814.   };
  815. /*
  816.  * Install the option fields.
  817.  */
  818.   for(i=0; i<nopt; i++) {
  819.     MenuItem *opt = opts + i;
  820.     if(opt->label) {
  821. /*
  822.  * Add an option field.
  823.  */
  824.       if(opt->callback) {
  825.     Widget widget = XtVaCreateManagedWidget(opt->label,
  826.                    xmPushButtonWidgetClass, w_pulldown, NULL);
  827.     XtAddCallback(widget, XmNactivateCallback, opt->callback, client_data);
  828. /*
  829.  * Add a title widget.
  830.  */
  831.       } else {
  832.     Widget widget = XtVaCreateManagedWidget(opt->label,
  833.                    xmLabelGadgetClass, w_pulldown,
  834.                    XmNalignment, XmALIGNMENT_BEGINNING,
  835.                    NULL);
  836.       };
  837. /*
  838.  * Add a separator widget.
  839.  */
  840.     } else {
  841.       XtVaCreateManagedWidget("separator", xmSeparatorGadgetClass, w_pulldown,
  842.                   NULL);
  843.     };
  844.   };
  845.   return w_menu;
  846. }
  847.  
  848. /*.......................................................................
  849.  * Create a pulldown menu.
  850.  *
  851.  * Input:
  852.  *  name             char *  The name of the menu.
  853.  *  label            char *  The label for the menubar button.
  854.  *  parent         Widget    The widget in which to place the pulldown menu.
  855.  *  nfield            int    The number of menu fields.
  856.  *  fields       MenuItem *  An array of nfield menu fields.
  857.  *  client_data XtPointer    The client_data argument to be passed to each
  858.  *                           callback function.
  859.  * Output:
  860.  *  return         Widget    The cascade button of the menu, or NULL on error.
  861.  */
  862. static Widget CreatePulldownMenu(char *name, char *label, Widget parent,
  863.                  int nfield, MenuItem *fields,
  864.                  XtPointer client_data)
  865. {
  866.   Widget w_pulldown; /* The pulldown-menu */
  867.   Widget w_cascade;  /* The cascade button to be returned */
  868.   int i;
  869. /*
  870.  * Check arguments.
  871.  */
  872.   if(nfield < 1 || !fields) {
  873.     fprintf(stderr, "CreatePulldownMenu: No fields.\n");
  874.     return NULL;
  875.   };
  876. /*
  877.  * Create a pulldown menu.
  878.  */
  879.   w_pulldown = XmCreatePulldownMenu(parent, "pulldown", NULL, 0);
  880. /*
  881.  * Create the cascade button that invokes the menu.
  882.  */
  883.   {
  884.     XmString label_str = XmStringCreateSimple(label);
  885.     w_cascade = XtVaCreateManagedWidget(name,
  886.                xmCascadeButtonWidgetClass, parent,
  887.                            XmNsubMenuId, w_pulldown,
  888.                            XmNlabelString, label_str,
  889.                            NULL);
  890.     XmStringFree(label_str);
  891.   }; 
  892. /*
  893.  * Install the menu fields.
  894.  */
  895.   for(i=0; i<nfield; i++) {
  896.     MenuItem *field = fields + i;
  897.     if(field->label) {
  898. /*
  899.  * Add a button widget.
  900.  */
  901.       if(field->callback) {
  902.     Widget widget = XtVaCreateManagedWidget(field->label,
  903.                    xmPushButtonWidgetClass, w_pulldown, NULL);
  904.     XtAddCallback(widget, XmNactivateCallback, field->callback,
  905.               client_data);
  906. /*
  907.  * Add a title widget.
  908.  */
  909.       } else {
  910.     Widget widget = XtVaCreateManagedWidget(field->label,
  911.                    xmLabelGadgetClass, w_pulldown,
  912.                    XmNalignment, XmALIGNMENT_BEGINNING,
  913.                    NULL);
  914.       };
  915. /*
  916.  * Add a separator widget.
  917.  */
  918.     } else {
  919.       XtVaCreateManagedWidget("separator", xmSeparatorGadgetClass, w_pulldown,
  920.                   NULL);
  921.     };
  922.   };
  923.   return w_cascade;
  924. }
  925.  
  926. /*.......................................................................
  927.  * This callback is called when the user selects the start position
  928.  * of a slice line.
  929.  *
  930.  * Input:
  931.  *  Widget          widget The PGPLOT widget that had a cursor event.
  932.  *  client_data  XtPointer The optional client data pointer passed to
  933.  *                         xmp_arm_cursor().
  934.  *  call_data    XtPointer The pointer to the context of the event
  935.  *                         as a (XmpCursorCallbackStruct *) cast to
  936.  *                         (XtPointer).
  937.  */
  938. static void start_slice_callback(Widget w, XtPointer client_data,
  939.                  XtPointer call_data)
  940. {
  941.   XmpCursorCallbackStruct *cursor = (XmpCursorCallbackStruct *) call_data;
  942.   Image *im = (Image *) client_data;
  943.   im->va.x = cursor->x;
  944.   im->va.y = cursor->y;
  945. /*
  946.  * Display a line-oriented rubber-band cursor to get the end vertex of the
  947.  * line.
  948.  */
  949.   cpgslct(xmp_device_id(im->w_image));  
  950.   xmp_arm_cursor(im->w_image, XMP_LINE_CURSOR, im->va.x, im->va.y,
  951.          end_slice_callback, im); 
  952. }
  953.  
  954. /*.......................................................................
  955.  * This callback is called when the user selects the end position
  956.  * of a slice line.
  957.  *
  958.  * Input:
  959.  *  Widget          widget The PGPLOT widget that had a cursor event.
  960.  *  client_data  XtPointer The optional client data pointer passed to
  961.  *                         xmp_arm_cursor().
  962.  *  call_data    XtPointer The pointer to the context of the event
  963.  *                         as a (XmpCursorCallbackStruct *) cast to
  964.  *                         (XtPointer).
  965.  */
  966. static void end_slice_callback(Widget w, XtPointer client_data,
  967.                    XtPointer call_data)
  968. {
  969.   XmpCursorCallbackStruct *cursor = (XmpCursorCallbackStruct *) call_data;
  970.   Image *im = (Image *) client_data;
  971.   im->vb.x = cursor->x;
  972.   im->vb.y = cursor->y;
  973. /*
  974.  * Re-arm the cursor for the start of the next line.
  975.  */
  976.   xmp_arm_cursor(im->w_image, XMP_NORM_CURSOR, 0.0f, 0.0f,
  977.          start_slice_callback, im);
  978. /*
  979.  * Draw the slice wrt the new line.
  980.  */
  981.   display_slice(im, &im->va, &im->vb);
  982. }
  983.  
  984. /*.......................................................................
  985.  * Create the menu bar of the application.
  986.  *
  987.  * Input:
  988.  *  im        Image * The image object of the application.
  989.  *  w_main   Widget   The main widget of the application. 
  990.  * Output:
  991.  *  return   Widget   The menu-bar widget or NULL on error.
  992.  */
  993. static Widget CreateMainMenuBar(Image *im, Widget w_main)
  994. {
  995.   Widget w_bar;        /* The menu-bar widget */
  996. /*
  997.  * Create the menu bar with File and Help menus.
  998.  */
  999.   w_bar = XmCreateMenuBar(w_main, "menubar", NULL, 0);
  1000. /*
  1001.  * Install the File menu.
  1002.  */
  1003.   {
  1004.     static MenuItem file_fields[] = {
  1005.       {"Save image as", save_image_as_callback},
  1006.       {NULL, NULL},     /* Separator */
  1007.       {"Quit",          quit_callback}
  1008.     };
  1009.     CreatePulldownMenu("file_menu", "File", w_bar,
  1010.                sizeof(file_fields)/sizeof(file_fields[0]),
  1011.                file_fields, (XtPointer) im);
  1012.   };
  1013. /*
  1014.  * Install the Help menu.
  1015.  */
  1016.   {
  1017.     static MenuItem help_fields[] = {
  1018.       {"Usage", help_callback}
  1019.     };
  1020.     Widget w_help = CreatePulldownMenu("help_menu", "Help", w_bar,
  1021.                sizeof(help_fields)/sizeof(help_fields[0]),
  1022.                help_fields, (XtPointer) im);
  1023.     XtVaSetValues(w_bar, XmNmenuHelpWidget, w_help, NULL);
  1024.   };
  1025.   return w_bar;
  1026. }
  1027.  
  1028. /*.......................................................................
  1029.  * The file-menu "Quit" callback function.
  1030.  */
  1031. static CALL_FN(quit_callback)
  1032. {
  1033.   exit(0);
  1034. }
  1035.  
  1036. /*.......................................................................
  1037.  * The help-menu callback function.
  1038.  */
  1039. static CALL_FN(help_callback)
  1040. {
  1041.   Image *im = (Image *) client_data;
  1042.   display_help(im);
  1043. }
  1044.  
  1045. /*.......................................................................
  1046.  * The File-menu "save image as" callback.
  1047.  */
  1048. static CALL_FN(save_image_as_callback)
  1049. {
  1050.   Image *im = (Image *) client_data;
  1051.   Widget w_dialog = CreatePopupPromptDialog(w, "device",
  1052.                           "Enter a PGPLOT device string:",
  1053.               "image.ps/vps",
  1054.               save_image_callback, (XtPointer) im);
  1055. /*
  1056.  * Add a null translation for the letter '?'. This prevents users from
  1057.  * enterring a PGPLOT '?' query string.
  1058.  */
  1059.   {
  1060.     char *bindings ="s <Key>\?:";
  1061.     XtTranslations translations = XtParseTranslationTable(bindings);
  1062.     XtOverrideTranslations(XmSelectionBoxGetChild(w_dialog, XmDIALOG_TEXT),
  1063.                translations);
  1064.   };
  1065. /*
  1066.  * Display the dialog.
  1067.  */
  1068.   XtManageChild(w_dialog);
  1069.   XtPopup(XtParent(w_dialog), XtGrabNone);
  1070. }
  1071.  
  1072. /*.......................................................................
  1073.  * The callback for the dialog created by save_image_as_callback().
  1074.  */
  1075. static CALL_FN(save_image_callback)
  1076. {
  1077.   int device_id;  /* The PGPLOT ID of the new PGPLOT device */
  1078.   Image *im = (Image *) client_data;
  1079.   XmSelectionBoxCallbackStruct *sel =
  1080.     (XmSelectionBoxCallbackStruct *) call_data;
  1081. /*
  1082.  * Translate the device specification into a normal C string.
  1083.  */
  1084.   {
  1085.     char *device = NULL;
  1086.     if(XmStringGetLtoR(sel->value, XmFONTLIST_DEFAULT_TAG, &device)) {
  1087. /*
  1088.  * Open the specified device.
  1089.  */
  1090.       device_id = cpgopen(device);
  1091.       XtFree(device);
  1092.       if(device_id > 0) {
  1093.     display_image(im, device_id);
  1094.     cpgclos();
  1095.       };
  1096.     }
  1097.   };
  1098. /*
  1099.  * Discard the popup widget.
  1100.  */
  1101.   XtDestroyWidget(w);
  1102.   return;
  1103. }
  1104.  
  1105. /*.......................................................................
  1106.  * Create a popup prompt-dialog with a specified prompt and initial value.
  1107.  *
  1108.  * Input:
  1109.  *  w             Widget    The widget of the button that invoked the dialog.
  1110.  *  name            char *  The name to give the popup. Note that
  1111.  *                          XmCreatePromptDialog() appends _prompt to this.
  1112.  *  prompt          char *  The dialog prompt string.
  1113.  *  value           char *  The initial value to display, or NULL.
  1114.  *  ok_fn XtCallbackProc    The callback function for the OK button.
  1115.  *  ok_data    XtPointer    The callback client_data argument.
  1116.  * Output:
  1117.  *  return  Widget     The dialog widget.
  1118.  */
  1119. static Widget CreatePopupPromptDialog(Widget w, char *name, char *prompt,
  1120.                       char *value,
  1121.                       XtCallbackProc ok_fn, XtPointer ok_data)
  1122. {
  1123.   Widget w_dialog;  /* The dialog widget to be returned */
  1124. /*
  1125.  * Create the dialog.
  1126.  */
  1127.   {
  1128.     XmString prompt_str = XmStringCreateSimple(prompt);
  1129.     Arg args[5];
  1130.     int n = 0;
  1131.     XtSetArg(args[n], XmNselectionLabelString, prompt_str); n++;
  1132.     XtSetArg(args[n], XmNautoUnmanage, False); n++;
  1133.     w_dialog = XmCreatePromptDialog(w, name, args, n);
  1134.     XmStringFree(prompt_str);
  1135.   };
  1136. /*
  1137.  * Arrange to ignore the Cancel button.
  1138.  */
  1139.   XtAddCallback(w_dialog, XmNcancelCallback, destroy_widget_callback, NULL);
  1140. /*
  1141.  * Install the provided Ok callback and its client_data.
  1142.  */
  1143.   if(ok_fn)
  1144.     XtAddCallback(w_dialog, XmNokCallback, ok_fn, ok_data);
  1145. /*
  1146.  * De-sensitize the Help button.
  1147.  */
  1148.   XtSetSensitive(XmSelectionBoxGetChild(w_dialog, XmDIALOG_HELP_BUTTON), False);
  1149. /*
  1150.  * Install the initial text-field value.
  1151.  */
  1152.   if(value) {
  1153.     XtVaSetValues(XmSelectionBoxGetChild(w_dialog, XmDIALOG_TEXT),
  1154.           XmNvalue, value,
  1155.           XmNcursorPosition, (XmTextPosition) strlen(value),
  1156.           NULL);
  1157.   };
  1158. /*
  1159.  * Add emacs-like keybindings.
  1160.  */
  1161.   {
  1162.     char *bindings ="\
  1163. c <Key>a: beginning-of-line()\n\
  1164. c <Key>e: end-of-line()\n\
  1165. c <Key>b: backward-character()\n\
  1166. c <Key>f: forward-character()\n\
  1167. c <Key>u: select-all() delete-selection()\n\
  1168. c <Key>d: delete-next-character()\
  1169. ";
  1170.     XtTranslations translations = XtParseTranslationTable(bindings);
  1171.     XtOverrideTranslations(XmSelectionBoxGetChild(w_dialog, XmDIALOG_TEXT),
  1172.                translations);
  1173.   };
  1174.   return w_dialog;
  1175. }
  1176.  
  1177. /*.......................................................................
  1178.  * A callback that destroys its widget.
  1179.  */
  1180. static CALL_FN(destroy_widget_callback)
  1181. {
  1182.   XtDestroyWidget(w);
  1183. }
  1184.