1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <math.h>
5 
6 #include <tk.h>
7 
8 #include "tkpgplot.h"
9 #include "cpgplot.h"
10 
11 /* Set the default image size */
12 
13 enum {IMAGE_SIZE=129};
14 
15 /* Set the number of points plotted per slice */
16 
17 enum {SLICE_SIZE=100};
18 
19 /*
20  * The demo supports several 2D functions that are displayed in
21  * its image window. For each supported function-type there is a
22  * C function of the following declaration, that returns the
23  * value of the function at a given x,y position.
24  */
25 #define IMAGE_FN(fn) float (fn)(float x, float y)
26 
27 /*
28  * List the prototypes of the available 2D-function functions.
29  */
30 static IMAGE_FN(sinc_fn);
31 static IMAGE_FN(gaus_fn);
32 static IMAGE_FN(ring_fn);
33 static IMAGE_FN(sin_angle_fn);
34 static IMAGE_FN(cos_radius_fn);
35 static IMAGE_FN(star_fn);
36 /*
37  * List the association between image function name and the functions
38  * that evaluate them.
39  */
40 static struct {
41   char *name;               /* The TCL name for the function */
42   IMAGE_FN(*fn);            /* The C function that evaluates the function */
43 } image_functions[] = {
44   {"cos(R)sin(A)",             ring_fn},
45   {"sinc(R)",                  sinc_fn},
46   {"exp(-R^2/20.0)",           gaus_fn},
47   {"sin(A)",                   sin_angle_fn},
48   {"cos(R)",                   cos_radius_fn},
49   {"(1+sin(6A))exp(-R^2/100)", star_fn}
50 };
51 
52 /*
53  * Declare a type to hold a single X,Y coordinate.
54  */
55 typedef struct {
56   double x, y;       /* World coordinates */
57 } Vertex;
58 
59 /*
60  * Declare the object type that is used to record the state of a
61  * given demo instance command.
62  */
63 typedef struct {
64   Tcl_Interp *interp; /* The TCL interpreter of the demo */
65   int image_id;       /* The PGPLOT id of the image widget */
66   int slice_id;       /* The PGPLOT id of the slice widget */
67   float *image;       /* The gray-scale image array */
68   float *slice;       /* The slice compilation array */
69   float scale;        /* Coversion factor pixels -> coords */
70   int image_size;     /* The number of pixels along each side of the image */
71   int slice_size;     /* The length of the slice array */
72   int xa,xb;          /* Min and max X pixel coordinates */
73   int ya,yb;          /* Min and max Y pixel coordinates */
74   float datamin;      /* The minimum data value in image[] */
75   float datamax;      /* The maximum data value in image[] */
76   IMAGE_FN(*fn);      /* The function to be displayed */
77   Vertex va;          /* The start of the latest slice line */
78   Vertex vb;          /* The end of the latest slice line */
79   int have_slice;     /* This true when va and vb contain valid slice limits */
80   int monochrome;     /* True if the image colormap only contains two colors */
81 } Pgdemo;
82 
83 static Pgdemo *new_Pgdemo(Tcl_Interp *interp, char *caller, char *cmd,
84 			  char *image_device, char *slice_device);
85 static Pgdemo *del_Pgdemo(Pgdemo *demo);
86 static void Pgdemo_DeleteProc(ClientData data);
87 static int pgdemo_instance_command(ClientData data, Tcl_Interp *interp,
88 			       int argc, char *argv[]);
89 static int pgdemo_save_command(Pgdemo *demo, Tcl_Interp *interp, int argc,
90 			       char *argv[]);
91 static int pgdemo_function_command(Pgdemo *demo, Tcl_Interp *interp, int argc,
92 				   char *argv[]);
93 static int pgdemo_slice_command(Pgdemo *demo, Tcl_Interp *interp, int argc,
94 				   char *argv[]);
95 static int pgdemo_redraw_slice_command(Pgdemo *demo, Tcl_Interp *interp,
96 				       int argc, char *argv[]);
97 static int pgdemo_recolor_image_command(Pgdemo *demo, Tcl_Interp *interp,
98 					int argc, char *argv[]);
99 static int demo_display_fn(Pgdemo *demo, Tcl_Interp *interp, IMAGE_FN(*fn));
100 static int demo_display_image(Pgdemo *demo, int id);
101 static int demo_display_slice(Pgdemo *demo, Vertex *va, Vertex *vb);
102 static void demo_display_help(Pgdemo *demo);
103 static void demo_display_busy(Pgdemo *demo);
104 
105 static void Pgdemo_DeleteProc(ClientData data);
106 static int create_pgdemo(ClientData data, Tcl_Interp *interp, int argc,
107 			 char *argv[]);
108 
109 
110 static int valid_demo_script(char *name);
111 static int Demo_AppInit(Tcl_Interp *interp);
112 
113 /*
114  * Define some color tables.
115  */
116 
117 /*
118  * Define single-color ramp functions.
119  */
120 static float grey_l[]  = {0.0,1.0};
121 static float grey_c[]  = {0.0,1.0};
122 
123 /*
124  * Define a rainbow color table.
125  */
126 static float rain_l[] = {-0.5, 0.0, 0.17, 0.33, 0.50, 0.67, 0.83, 1.0, 1.7};
127 static float rain_r[] = { 0.0, 0.0,  0.0,  0.0,  0.6,  1.0,  1.0, 1.0, 1.0};
128 static float rain_g[] = { 0.0, 0.0,  0.0,  1.0,  1.0,  1.0,  0.6, 0.0, 1.0};
129 static float rain_b[] = { 0.0, 0.3,  0.8,  1.0,  0.3,  0.0,  0.0, 0.0, 1.0};
130 
131 /*
132  * Iraf "heat" color table.
133  */
134 static float heat_l[] = {0.0, 0.2, 0.4, 0.6, 1.0};
135 static float heat_r[] = {0.0, 0.5, 1.0, 1.0, 1.0};
136 static float heat_g[] = {0.0, 0.0, 0.5, 1.0, 1.0};
137 static float heat_b[] = {0.0, 0.0, 0.0, 0.3, 1.0};
138 
139 /*
140  * AIPS tvfiddle discrete rainbow color table.
141  */
142 static float aips_l[] = {0.0, 0.1, 0.1, 0.2, 0.2, 0.3, 0.3, 0.4, 0.4, 0.5,
143 			 0.5, 0.6, 0.6, 0.7, 0.7, 0.8, 0.8, 0.9, 0.9, 1.0};
144 static float aips_r[] = {0.0, 0.0, 0.3, 0.3, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0,
145 			 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
146 static float aips_g[] = {0.0, 0.0, 0.3, 0.3, 0.0, 0.0, 0.0, 0.0, 0.8, 0.8,
147 			 0.6, 0.6, 1.0, 1.0, 1.0, 1.0, 0.8, 0.8, 0.0, 0.0};
148 static float aips_b[] = {0.0, 0.0, 0.3, 0.3, 0.7, 0.7, 0.7, 0.7, 0.9, 0.9,
149 			 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
150 
151 /*
152  * Define a macro that returns the number of elements in a static array.
153  */
154 #ifdef COUNT
155 #undef COUNT
156 #endif
157 #define COUNT(lev) sizeof(lev)/sizeof(lev[0])
158 
159 /*
160  * List the supported color tables.
161  */
162 typedef struct {
163   char *name;    /* The name of the color table */
164   int n;         /* The number of nodes in the color table */
165   float *l;      /* The normalized color-table positions of the n nodes */
166   float *r;      /* The n red normalized intensities */
167   float *g;      /* The n green normalized intensities */
168   float *b;      /* The n blue normalized intensities */
169 } Cmap;
170 
171 static Cmap std_cmaps[] = {
172   {"grey",    COUNT(grey_l),   grey_l,  grey_c,  grey_c,  grey_c},
173   {"rainbow", COUNT(rain_l),   rain_l,  rain_r,  rain_g,  rain_b},
174   {"heat",    COUNT(heat_l),   heat_l,  heat_r,  heat_g,  heat_b},
175   {"aips",    COUNT(aips_l),   aips_l,  aips_r,  aips_g,  aips_b},
176 };
177 static int n_std_cmap = COUNT(std_cmaps);
178 
179 /*.......................................................................
180  * After presenting a warning if the first argument is not the name
181  * of the demo Tcl script, main() simply calls the standard Tk_Main()
182  * to initialize Tcl/Tk and the demo package.
183  * Input:
184  *  argc     int    The number of command line arguments.
185  *  argv    char*[] The array of command-line argument strings.
186  * Output:
187  *  return   int    0 - OK.
188  *                  1 - Error.
189  */
main(int argc,char * argv[])190 int main(int argc, char *argv[])
191 {
192   char *usage = "Usage: pgtkdemo pgtkdemo.tcl [tk-options].\n";
193 /*
194  * Check whether the first argument names a valid pgtkdemo
195  * script file.
196  */
197   if(argc < 2 || *argv[1] == '-' || !valid_demo_script(argv[1])) {
198     fprintf(stderr, usage);
199     return 1;
200   };
201 /*
202  * Start the application.
203  */
204   Tk_Main(argc, argv, Demo_AppInit);
205   return 0;
206 }
207 
208 /*.......................................................................
209  * This dummy fortran main allows pgtkdemo to be linked with the
210  * f2c-compiled pgplot library.
211  */
MAIN__(void)212 int MAIN__(void)
213 {
214 }
215 
216 /*.......................................................................
217  * This is the application initialization file that is called by Tk_Main().
218  */
Demo_AppInit(Tcl_Interp * interp)219 static int Demo_AppInit(Tcl_Interp *interp)
220 {
221 /*
222  * Create the standard Tcl and Tk packages, plus the TkPgplot package.
223  */
224   if(Tcl_Init(interp)    == TCL_ERROR ||
225      Tk_Init(interp)     == TCL_ERROR ||
226      Tkpgplot_Init(interp) == TCL_ERROR)
227     return 1;
228 /*
229  * Create the TCL command used to initialization the demo.
230  */
231   Tcl_CreateCommand(interp, "create_pgdemo", create_pgdemo,
232 		    (ClientData) Tk_MainWindow(interp), 0);
233   return 0;
234 }
235 
236 /*.......................................................................
237  * This function provides the TCL command that creates a pgdemo
238  * manipulation command. This opens the two given PGPLOT widgets to
239  * PGPLOT, establishes a cursor handler and records the state of the
240  * demo in a dynamically allocated container.
241  *
242  * Input:
243  *  data      ClientData    The main window cast to ClientData.
244  *  interp    Tcl_Interp *  The TCL intrepreter of the demo.
245  *  argc             int    The number of command arguments.
246  *  argv            char ** The array of 'argc' command arguments.
247  *                          argv[0] = "create_pgdemo"
248  *                          argv[1] = The name to give the new command.
249  *                          argv[2] = The name of the image widget.
250  *                          argv[3] = The name of the slice widget.
251  * Output:
252  *  return           int    TCL_OK    - Success.
253  *                          TCL_ERROR - Failure.
254  */
create_pgdemo(ClientData data,Tcl_Interp * interp,int argc,char * argv[])255 static int create_pgdemo(ClientData data, Tcl_Interp *interp, int argc,
256 			 char *argv[])
257 {
258   Pgdemo *demo;      /* The new widget instance object */
259 /*
260  * Check that the right number of arguments was provided.
261  */
262   if(argc != 4) {
263     Tcl_AppendResult(interp,
264 	     argv[0], ": Wrong number of arguments - should be \'",
265 	     argv[0], " new_command_name image_widget slice_widget\'", NULL);
266     return TCL_ERROR;
267   };
268 /*
269  * Allocate a context object for the command.
270  */
271   demo = new_Pgdemo(interp, argv[0], argv[1], argv[2], argv[3]);
272   if(!demo)
273     return TCL_ERROR;
274   return TCL_OK;
275 }
276 
277 /*.......................................................................
278  * Create a new PGPLOT demo instance command and its associated context
279  * object.
280  *
281  * Input:
282  *  interp   Tcl_Interp *  The TCL interpreter object.
283  *  caller         char *  The name of the calling TCL command.
284  *  cmd            char *  The name to give the new demo-instance command.
285  *  image_device   char *  The PGPLOT device specification to use to open
286  *                         the image-display device.
287  *  slice_device   char *  The PGPLOT device specification to use to open
288  *                         the slice-display device.
289  * Output:
290  *  return       Pgdemo *  The new demo object, or NULL on error.
291  *                         If NULL is returned then the context of the
292  *                         error will have been recorded in the result
293  *                         field of the interpreter.
294  */
new_Pgdemo(Tcl_Interp * interp,char * caller,char * cmd,char * image_device,char * slice_device)295 static Pgdemo *new_Pgdemo(Tcl_Interp *interp, char *caller, char *cmd,
296 			  char *image_device, char *slice_device)
297 {
298   Pgdemo *demo;        /* The new widget object */
299   int minind, maxind;  /* The min/max color indexes available for images */
300   int i;
301 /*
302  * Allocate the container.
303  */
304   demo = (Pgdemo *) malloc(sizeof(Pgdemo));
305   if(!demo) {
306     Tcl_AppendResult(interp, "Insufficient memory to create ", cmd, NULL);
307     return NULL;
308   };
309 /*
310  * Before attempting any operation that might fail, initialize the container
311  * at least up to the point at which it can safely be passed to
312  * del_Pgdemo().
313  */
314   demo->interp = interp;
315   demo->image_id = -1;
316   demo->slice_id = -1;
317   demo->image = NULL;
318   demo->slice = NULL;
319   demo->image_size = IMAGE_SIZE;
320   demo->slice_size = SLICE_SIZE;
321   demo->scale = 40.0f/demo->image_size;
322   demo->xa = -(int)demo->image_size/2;
323   demo->xb = demo->image_size/2;
324   demo->ya = -(int)demo->image_size/2;
325   demo->yb = demo->image_size/2;
326   demo->fn = sin_angle_fn;
327   demo->have_slice = 0;
328   demo->monochrome = 0;
329 /*
330  * Attempt to open the image and slice widgets.
331  */
332   if((demo->image_id = cpgopen(image_device)) <= 0 ||
333      (demo->slice_id = cpgopen(slice_device)) <= 0) {
334     Tcl_AppendResult(interp, "Unable to open widgets: ", image_device, ", ",
335 		     slice_device, NULL);
336     return del_Pgdemo(demo);
337   };
338 /*
339  * Now allocate the 2D image array as a 1D array to be indexed in
340  * as a FORTRAN array.
341  */
342   demo->image = (float *) malloc(sizeof(float) * demo->image_size * demo->image_size);
343   if(!demo->image) {
344     Tcl_AppendResult(interp, "new_Pgdemo: Insufficient memory.", NULL);
345     return del_Pgdemo(demo);
346   };
347 /*
348  * Initialize the image array.
349  */
350   for(i=0; i<demo->image_size*demo->image_size; i++)
351     demo->image[i] = 0.0f;
352 /*
353  * Allocate an array to be used when constructing slices through the
354  * displayed image.
355  */
356   demo->slice = (float *) malloc(sizeof(float) * demo->slice_size);
357   if(!demo->slice) {
358     Tcl_AppendResult(interp, "new_Pgdemo: Insufficient memory.", NULL);
359     return del_Pgdemo(demo);
360   };
361 /*
362  * Initialize the slice array.
363  */
364   for(i=0; i<demo->slice_size; i++)
365     demo->slice[i] = 0.0f;
366 /*
367  * If there are fewer than 2 colors available for plotting images,
368  * mark the image as monochrome so that pggray can be asked to
369  * produce a stipple version of the image.
370  */
371   cpgslct(demo->image_id);
372   cpgqcir(&minind, &maxind);
373   demo->monochrome = maxind-minind+1 <= 2;
374 /*
375  * Create the instance command.
376  */
377   Tcl_CreateCommand(interp, cmd, pgdemo_instance_command,
378 		    (ClientData)demo, Pgdemo_DeleteProc);
379 /*
380  * Return the command name.
381  */
382   Tcl_AppendResult(interp, cmd, NULL);
383   return demo;
384 }
385 
386 /*.......................................................................
387  * Delete the context of a Pgdemo instance command.
388  *
389  * Input:
390  *  demo     Pgdemo *   The widget to be deleted.
391  * Output:
392  *  return  Pgdemo *   Always NULL.
393  */
del_Pgdemo(Pgdemo * demo)394 static Pgdemo *del_Pgdemo(Pgdemo *demo)
395 {
396   if(demo) {
397     demo->interp = NULL;
398 /*
399  * Close the PGPLOT widgets.
400  */
401     if(demo->image_id > 0) {
402       cpgslct(demo->image_id);
403       cpgclos();
404       demo->image_id = -1;
405     };
406     if(demo->slice_id > 0) {
407       cpgslct(demo->slice_id);
408       cpgclos();
409       demo->slice_id = -1;
410     };
411 /*
412  * Delete the container.
413  */
414     free(demo);
415   };
416   return NULL;
417 }
418 
419 /*.......................................................................
420  * This is a wrapper around del_Pgdemo() suitable to be registered as
421  * a DeleteProc callback for Tcl_CreateCommand().
422  *
423  * Input:
424  *  data  ClientData   The (Pgdemo *) object cast to ClientData.
425  */
Pgdemo_DeleteProc(ClientData data)426 static void Pgdemo_DeleteProc(ClientData data)
427 {
428   (void) del_Pgdemo((Pgdemo *) data);
429 }
430 
431 /*.......................................................................
432  * This function implements a given Tcl PGPLOT demo instance command.
433  *
434  * Input:
435  *  data      ClientData    The demo context object cast to (ClientData).
436  *  interp    Tcl_Interp *  The TCL intrepreter.
437  *  argc             int    The number of command arguments.
438  *  argv            char ** The array of 'argc' command arguments.
439  *                          argv[0] - the name of the demo command.
440  *                          argv[1..] - One of:
441  *                           save device_spec
442  *                           function image_function
443  *                           slice x1 y1 x2 y2
444  * Output:
445  *  return           int    TCL_OK    - Success.
446  *                          TCL_ERROR - Failure.
447  */
pgdemo_instance_command(ClientData data,Tcl_Interp * interp,int argc,char * argv[])448 static int pgdemo_instance_command(ClientData data, Tcl_Interp *interp,
449 				   int argc, char *argv[])
450 {
451   Pgdemo *demo = (Pgdemo *) data;
452   char *command;    /* The name of the command */
453 /*
454  * We must have at least one command argument.
455  */
456   if(argc < 2) {
457     Tcl_SetResult(interp, "Wrong number of arguments.", TCL_STATIC);
458     return TCL_ERROR;
459   };
460 /*
461  * Get the command-name argument.
462  */
463   command = argv[1];
464   if(strcmp(command, "save") == 0)
465     return pgdemo_save_command(demo, interp, argc - 2, argv + 2);
466   else if(strcmp(command, "function") == 0)
467     return pgdemo_function_command(demo, interp, argc - 2, argv + 2);
468   else if(strcmp(command, "slice") == 0)
469     return pgdemo_slice_command(demo, interp, argc - 2, argv + 2);
470   else if(strcmp(command, "redraw_slice") == 0)
471     return pgdemo_redraw_slice_command(demo, interp, argc - 2, argv + 2);
472   else if(strcmp(command, "recolor_image") == 0)
473     return pgdemo_recolor_image_command(demo, interp, argc - 2, argv + 2);
474 /*
475  * Unknown command name.
476  */
477   Tcl_AppendResult(interp, argv[0], ": Unknown demo command \"",
478 		   argv[1], "\"", NULL);
479   return TCL_ERROR;
480 }
481 
482 /*.......................................................................
483  * Implement the demo "save" command. This takes a PGPLOT device
484  * specification as its argument.
485  *
486  * Input:
487  *  demo       Pgdemo *   The demo being serviced.
488  *  interp Tcl_Interp *   The TCL interpreter of the demo.
489  *  argc          int     The number of TCL arguments in argv[].
490  *  argv         char **  An array of 'argc' TCL arguments.
491  * Output:
492  *  return        int     TCL_OK    - Normal completion.
493  *                        TCL_ERROR - The interpreter result will contain
494  *                                    the error message.
495  */
pgdemo_save_command(Pgdemo * demo,Tcl_Interp * interp,int argc,char * argv[])496 static int pgdemo_save_command(Pgdemo *demo, Tcl_Interp *interp, int argc,
497 			       char *argv[])
498 {
499   char *device;  /* The PGPLOT device specification */
500   int device_id; /* The PGPLOT id of the new device */
501 /*
502  * There should only be a single argument.
503  */
504   if(argc != 1) {
505     Tcl_AppendResult(interp, "Missing PGPLOT device specification.\n",
506 		     NULL);
507     return TCL_ERROR;
508   };
509 /*
510  * Get the device specification.
511  */
512   device = argv[0];
513 /*
514  * Open the new PGPLOT device.
515  */
516   device_id = cpgopen(device);
517 /*
518  * If the device was successfully opened, plot the current image
519  * within it and close the device.
520  */
521   if(device_id > 0) {
522     demo_display_image(demo, device_id);
523     cpgclos();
524   } else {
525     Tcl_AppendResult(interp, "cpgopen(\"", device, "\") failed.", NULL);
526     return TCL_ERROR;
527   };
528   return TCL_OK;
529 }
530 
531 /*.......................................................................
532  * Implement the demo "function" command. This takes one of a set of
533  * supported function-designations and displays it in the image window.
534  *
535  * Input:
536  *  demo       Pgdemo *   The demo being serviced.
537  *  interp Tcl_Interp *   The TCL interpreter of the demo.
538  *  argc          int     The number of TCL arguments in argv[].
539  *  argv         char **  An array of 'argc' TCL arguments.
540  *                        argv[0] - A function designation chosen from:
541  *                                   "cos(R)sin(A)"
542  *                                   "sinc(R)"
543  *                                   "exp(-R^2/20.0)"
544  *                                   "sin(A)"
545  *                                   "cos(A)"
546  *                                   "(1+sin(6A))exp(-R^2/100)"
547  * Output:
548  *  return        int     TCL_OK    - Normal completion.
549  *                        TCL_ERROR - The interpreter result will contain
550  *                                    the error message.
551  */
pgdemo_function_command(Pgdemo * demo,Tcl_Interp * interp,int argc,char * argv[])552 static int pgdemo_function_command(Pgdemo *demo, Tcl_Interp *interp, int argc,
553 				   char *argv[])
554 {
555   char *function; /* The name of the display function */
556   int i;
557 /*
558  * There should only be a single argument.
559  */
560   if(argc != 1) {
561     Tcl_AppendResult(interp, "Missing image function name.\n",
562 		     NULL);
563     return TCL_ERROR;
564   };
565 /*
566  * Get the function specification.
567  */
568   function = argv[0];
569 /*
570  * Look up the function in the table that associates function names
571  * with the C functions that implement them.
572  */
573   for(i=0; i<sizeof(image_functions)/sizeof(image_functions[0]); i++) {
574     if(strcmp(image_functions[i].name, function) == 0)
575       return demo_display_fn(demo, interp, image_functions[i].fn);
576   };
577   Tcl_AppendResult(interp, "Unknown function name \"", function, "\"",
578 		   NULL);
579   return TCL_ERROR;
580 }
581 
582 /*.......................................................................
583  * Implement the demo "slice" command. This takes two pairs of image
584  * world coordinates and plots a 1D representation of the currently
585  * displayed function in the slice window.
586  *
587  * Input:
588  *  demo       Pgdemo *   The demo being serviced.
589  *  interp Tcl_Interp *   The TCL interpreter of the demo.
590  *  argc          int     The number of TCL arguments in argv[].
591  *  argv         char **  An array of 'argc' TCL arguments. There
592  *                        should be 4 arguments, "x1 y1 x2 y2" where
593  *                        x1,y1 and x2,y2 are the two end points of the
594  *                        slice line.
595  *
596  * Output:
597  *  return        int     TCL_OK    - Normal completion.
598  *                        TCL_ERROR - The interpreter result will contain
599  *                                    the error message.
600  */
pgdemo_slice_command(Pgdemo * demo,Tcl_Interp * interp,int argc,char * argv[])601 static int pgdemo_slice_command(Pgdemo *demo, Tcl_Interp *interp, int argc,
602 				char *argv[])
603 {
604   Vertex va;  /* The coordinates of one end of the slice */
605   Vertex vb;  /* The coordinates of the other end of the slice */
606 /*
607  * There should be four arguments.
608  */
609   if(argc != 4) {
610     Tcl_AppendResult(interp,
611 		     "Wrong number of arguments to the slice command.\n",
612 		     "Should be x1 y1 x2 y2.", NULL);
613     return TCL_ERROR;
614   };
615 /*
616  * Read the four coordinate values.
617  */
618   if(Tcl_GetDouble(interp, argv[0], &va.x) == TCL_ERROR ||
619      Tcl_GetDouble(interp, argv[1], &va.y) == TCL_ERROR ||
620      Tcl_GetDouble(interp, argv[2], &vb.x) == TCL_ERROR ||
621      Tcl_GetDouble(interp, argv[3], &vb.y) == TCL_ERROR)
622     return TCL_ERROR;
623 /*
624  * Record the slice vertices so that the slice can be redrawn
625  * when the widget is resized.
626  */
627   demo->va = va;
628   demo->vb = vb;
629   demo->have_slice = 1;
630 /*
631  * Plot the new slice.
632  */
633   return demo_display_slice(demo, &va, &vb);
634 }
635 
636 /*.......................................................................
637  * Implement the demo "redraw_slice" command.
638  *
639  * Input:
640  *  demo       Pgdemo *   The demo being serviced.
641  *  interp Tcl_Interp *   The TCL interpreter of the demo.
642  *  argc          int     The number of TCL arguments in argv[].
643  *  argv         char **  An array of 'argc' TCL arguments. No arguments
644  *                        are expected.
645  * Output:
646  *  return        int     TCL_OK    - Normal completion.
647  *                        TCL_ERROR - The interpreter result will contain
648  *                                    the error message.
649  */
pgdemo_redraw_slice_command(Pgdemo * demo,Tcl_Interp * interp,int argc,char * argv[])650 static int pgdemo_redraw_slice_command(Pgdemo *demo, Tcl_Interp *interp,
651 				       int argc, char *argv[])
652 {
653   if(argc > 0) {
654     Tcl_AppendResult(interp, "'pgdemo redraw_slice' takes no arguments.", NULL);
655     return TCL_ERROR;
656   };
657   if(demo->have_slice)
658     demo_display_slice(demo, &demo->va, &demo->vb);
659   else
660     demo_display_help(demo);
661   return TCL_OK;
662 }
663 
664 /*.......................................................................
665  * Implement the demo "recolor_image" command. This takes one of a set of
666  * supported color-table names and redisplays the current image with the
667  * specified color table.
668  *
669  * Input:
670  *  demo       Pgdemo *   The demo being serviced.
671  *  interp Tcl_Interp *   The TCL interpreter of the demo.
672  *  argc          int     The number of TCL arguments in argv[].
673  *  argv         char **  An array of 'argc' TCL arguments.
674  *                        argv[0] - A color table name chosen from:
675  *                                   "aips"    -  AIPS tvfiddle color table.
676  *                                   "blue"    -  A blue color table.
677  *                                   "green"   -  A green color table.
678  *                                   "grey"    -  A grey-scale color table.
679  *                                   "heat"    -  The IRAF "heat" color table.
680  *                                   "rainbow" -  A red color table.
681  *                                   "red"     -  A red color table.
682  * Output:
683  *  return        int     TCL_OK    - Normal completion.
684  *                        TCL_ERROR - The interpreter result will contain
685  *                                    the error message.
686  */
pgdemo_recolor_image_command(Pgdemo * demo,Tcl_Interp * interp,int argc,char * argv[])687 static int pgdemo_recolor_image_command(Pgdemo *demo, Tcl_Interp *interp,
688 					int argc, char *argv[])
689 {
690   char *name;   /* The name of the desired color table */
691   int i;
692 /*
693  * There should only be a single argument.
694  */
695   if(argc != 1) {
696     Tcl_AppendResult(interp, "Missing color-table name.\n", NULL);
697     return TCL_ERROR;
698   };
699 /*
700  * Get the color-table name.
701  */
702   name = argv[0];
703 /*
704  * Look up the name in our list of supported color tables.
705  */
706   for(i=0; i<n_std_cmap; i++) {
707     Cmap *cmap = std_cmaps + i;
708 /*
709  * If the color table is found, install it and return.
710  */
711     if(strcmp(cmap->name, name) == 0) {
712       cpgslct(demo->image_id);
713       cpgctab(cmap->l, cmap->r, cmap->g, cmap->b, cmap->n, 1.0, 0.5);
714       return TCL_OK;
715     };
716   };
717   Tcl_AppendResult(interp, "Unknown color map name \"", name, "\"", NULL);
718   return TCL_ERROR;
719 }
720 
721 /*.......................................................................
722  * A sinc(radius) function.
723  *
724  * Input:
725  *  x,y     float   The coordinates to evaluate the function at.
726  * Output:
727  *  return  float   The function value at the specified coordinates.
728  */
IMAGE_FN(sinc_fn)729 static IMAGE_FN(sinc_fn)
730 {
731   const float tiny = 1.0e-6f;
732   float radius = sqrt(x*x + y*y);
733   return (fabs(radius) < tiny) ? 1.0f : sin(radius)/radius;
734 }
735 
736 /*.......................................................................
737  * A exp(-(x^2+y^2)/20) function.
738  *
739  * Input:
740  *  x,y     float   The coordinates to evaluate the function at.
741  * Output:
742  *  return  float   The function value at the specified coordinates.
743  */
IMAGE_FN(gaus_fn)744 static IMAGE_FN(gaus_fn)
745 {
746   return exp(-((x*x)+(y*y))/20.0f);
747 }
748 
749 /*.......................................................................
750  * A cos(radius)*sin(angle) 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  */
IMAGE_FN(ring_fn)757 static IMAGE_FN(ring_fn)
758 {
759   return cos(sqrt(x*x + y*y)) * sin(x==0.0f && y==0.0f ? 0.0f : atan2(x,y));
760 }
761 
762 /*.......................................................................
763  * A sin(angle) function.
764  *
765  * Input:
766  *  x,y     float   The coordinates to evaluate the function at.
767  * Output:
768  *  return  float   The function value at the specified coordinates.
769  */
IMAGE_FN(sin_angle_fn)770 static IMAGE_FN(sin_angle_fn)
771 {
772   return sin(x==0.0f && y==0.0f ? 0.0f : atan2(x,y));
773 }
774 
775 /*.......................................................................
776  * A cos(radius) function.
777  *
778  * Input:
779  *  x,y     float   The coordinates to evaluate the function at.
780  * Output:
781  *  return  float   The function value at the specified coordinates.
782  */
IMAGE_FN(cos_radius_fn)783 static IMAGE_FN(cos_radius_fn)
784 {
785   return cos(sqrt(x*x + y*y));
786 }
787 
788 /*.......................................................................
789  * A (1+sin(6*angle))*exp(-radius^2 / 100)function.
790  *
791  * Input:
792  *  x,y     float   The coordinates to evaluate the function at.
793  * Output:
794  *  return  float   The function value at the specified coordinates.
795  */
IMAGE_FN(star_fn)796 static IMAGE_FN(star_fn)
797 {
798   return (1.0 + sin(x==0.0f && y==0.0f ? 0.0f : 6.0*atan2(x,y)))
799     * exp(-((x*x)+(y*y))/100.0f);
800 }
801 
802 /*.......................................................................
803  * Display a new function in the image window.
804  *
805  * Input:
806  *  demo       Pgdemo *   The demo instance object.
807  *  interp Tcl_Interp *   The TCL interpreter of the demo.
808  *  fn       IMAGE_FN(*)  The function to be displayed.
809  * Output:
810  *  return        int     TCL_OK    - Normal completion.
811  *                        TCL_ERROR - The interpreter result will contain
812  *                                    the error message.
813  */
demo_display_fn(Pgdemo * demo,Tcl_Interp * interp,IMAGE_FN (* fn))814 static int demo_display_fn(Pgdemo *demo, Tcl_Interp *interp, IMAGE_FN(*fn))
815 {
816   int ix, iy;  /* The pixel coordinates being assigned */
817   float vmin;  /* The minimum pixel value in the image */
818   float vmax;  /* The maximum pixel value in the image */
819   float *pixel;/* A pointer to pixel (ix,iy) in demo->image */
820 /*
821  * Check arguments.
822  */
823   if(!fn) {
824     Tcl_AppendResult(interp, "demo_display_fn: NULL function.", NULL);
825     return TCL_ERROR;
826   };
827 /*
828  * Install the new function.
829  */
830   demo->fn = fn;
831 /*
832  * Display a "please wait" message in the slice window.
833  */
834   demo_display_busy(demo);
835 /*
836  * Fill the image array via the current display function.
837  */
838   pixel = demo->image;
839   vmin = vmax = demo->fn(demo->xa * demo->scale, demo->ya * demo->scale);
840   for(iy = demo->ya; iy <= demo->yb; iy++) {
841     for(ix = demo->xa; ix <= demo->xb; ix++) {
842       float value = demo->fn(ix * demo->scale, iy * demo->scale);
843       *pixel++ = value;
844       if(value < vmin)
845 	vmin = value;
846       if(value > vmax)
847 	vmax = value;
848     };
849   };
850 /*
851  * Record the min and max values of the data array.
852  */
853   demo->datamin = vmin;
854   demo->datamax = vmax;
855 /*
856  * Display the new image.
857  */
858   demo_display_image(demo, demo->image_id);
859 /*
860  * Display instructions in the slice window.
861  */
862   demo_display_help(demo);
863 /*
864  * No slice has been selected yet.
865  */
866   demo->have_slice = 0;
867   return TCL_OK;
868 }
869 
870 /*.......................................................................
871  * Display the current image function in a specified PGPLOT device.
872  *
873  *
874  * Input:
875  *  demo  Pgdemo *   The demo instance object.
876  *  id       int     The id of the PGPLOT device to display.
877  * Output:
878  *  return   int     TCL_OK    - Normal completion.
879  *                   TCL_ERROR - The interpreter result will contain
880  *                               the error message.
881  */
demo_display_image(Pgdemo * demo,int id)882 static int demo_display_image(Pgdemo *demo, int id)
883 {
884 /*
885  * Select the specified PGPLOT device and display the image array.
886  */
887   cpgslct(id);
888   cpgask(0);
889   cpgpage();
890   cpgsch(1.0f);
891   cpgvstd();
892   cpgwnad(demo->xa * demo->scale, demo->xb * demo->scale,
893 	  demo->ya * demo->scale, demo->yb * demo->scale);
894   {
895     float tr[6];  /* Coordinate definition matrix */
896     tr[0] = (demo->xa - 1) * demo->scale;
897     tr[1] = demo->scale;
898     tr[2] = 0.0f;
899     tr[3] = (demo->ya - 1) * demo->scale;
900     tr[4] = 0.0f;
901     tr[5] = demo->scale;
902     if(demo->monochrome) {
903       cpggray(demo->image, demo->image_size, demo->image_size,
904 	      1, demo->image_size, 1, demo->image_size, demo->datamax,
905 	      demo->datamin, tr);
906     } else {
907       cpgimag(demo->image, demo->image_size, demo->image_size,
908 	      1, demo->image_size, 1, demo->image_size, demo->datamin,
909 	      demo->datamax, tr);
910     };
911   };
912   cpgsci(1);
913   cpgbox("BCNST", 0.0f, 0, "BCNST", 0.0f, 0);
914   cpglab("X", "Y", "Image display demo");
915   return TCL_OK;
916 }
917 
918 /*.......................................................................
919  * Display a new slice in the slice window.
920  *
921  * Input:
922  *  demo  Pgdemo *   The demo instance object.
923  *  va    Vertex *   The vertex of one end of the slice line.
924  *  vb    Vertex *   The vertex of the opposite end of the slice line.
925  * Output:
926  *  return   int     TCL_OK    - Normal completion.
927  *                   TCL_ERROR - The interpreter result will contain
928  *                               the error message.
929  */
demo_display_slice(Pgdemo * demo,Vertex * va,Vertex * vb)930 static int demo_display_slice(Pgdemo *demo, Vertex *va, Vertex *vb)
931 {
932   float xa;  /* The start X value of the slice */
933   float dx;  /* The X-axis world-coordinate width of one slice pixel */
934   float ya;  /* The start Y value of the slice */
935   float dy;  /* The Y-axis world-coordinate width of one slice pixel */
936   float smin;/* The minimum slice value */
937   float smax;/* The maximum slice value */
938   float slice_length;  /* The world-coordinate length of the slice */
939   float ymargin;       /* The Y axis margin within the plot */
940   int i;
941 /*
942  * Determine the slice pixel assignments.
943  */
944   xa = va->x;
945   dx = (vb->x - va->x) / demo->slice_size;
946   ya = va->y;
947   dy = (vb->y - va->y) / demo->slice_size;
948 /*
949  * Make sure that the slice has a finite length by setting a
950  * minimum size of one pixel.
951  */
952   {
953     float min_delta = demo->scale / demo->slice_size;
954     if(fabs(dx) < min_delta && fabs(dy) < min_delta)
955       dx = min_delta;
956   };
957 /*
958  * Construct the slice in demo->slice[] and keep a tally of the
959  * range of slice values seen.
960  */
961   for(i=0; i<demo->slice_size; i++) {
962     float value = demo->fn(xa + i * dx, ya + i * dy);
963     demo->slice[i] = value;
964     if(i==0) {
965       smin = smax = value;
966     } else if(value < smin) {
967       smin = value;
968     } else if(value > smax) {
969       smax = value;
970     };
971   };
972 /*
973  * Determine the length of the slice.
974  */
975   {
976     float xlen = dx * demo->slice_size;
977     float ylen = dy * demo->slice_size;
978     slice_length = sqrt(xlen * xlen + ylen * ylen);
979   };
980 /*
981  * Determine the extra length to add to the Y axis to prevent the
982  * slice plot hitting the top and bottom of the plot.
983  */
984   ymargin = 0.05 * (demo->datamax - demo->datamin);
985 /*
986  * Set up the slice axes.
987  */
988   cpgslct(demo->slice_id);
989   cpgask(0);
990   cpgpage();
991   cpgbbuf();
992   cpgsch(2.0f);
993   cpgvstd();
994   cpgswin(0.0f, slice_length, demo->datamin - ymargin, demo->datamax + ymargin);
995   cpgbox("BCNST", 0.0f, 0, "BCNST", 0.0f, 0);
996   cpglab("Radius", "Image value", "A 1D slice through the image");
997 /*
998  * Draw the slice.
999  */
1000   for(i=0; i<demo->slice_size; i++) {
1001     if(i==0)
1002       cpgmove(0.0f, demo->slice[0]);
1003     else
1004       cpgdraw(slice_length * (float)i / (float)demo->slice_size, demo->slice[i]);
1005   };
1006   cpgebuf();
1007   return TCL_OK;
1008 }
1009 
1010 /*.......................................................................
1011  * Display usage instructions in the slice window.
1012  *
1013  * Input:
1014  *  demo     Pgdemo *   The demo instance object.
1015  */
demo_display_help(Pgdemo * demo)1016 static void demo_display_help(Pgdemo *demo)
1017 {
1018 /*
1019  * Clear the slice plot and replace it with instructional text.
1020  */
1021   cpgslct(demo->slice_id);
1022   cpgask(0);
1023   cpgpage();
1024   cpgsch(3.0f);
1025   cpgsvp(0.0, 1.0, 0.0, 1.0);
1026   cpgswin(0.0, 1.0, 0.0, 1.0);
1027   cpgmtxt("T", -2.0, 0.5, 0.5, "See the help menu for instructions.");
1028 }
1029 
1030 /*.......................................................................
1031  * Display a "Please wait" message in the slice window.
1032  *
1033  * Input:
1034  *  demo     Pgdemo *   The demo instance object.
1035  */
demo_display_busy(Pgdemo * demo)1036 static void demo_display_busy(Pgdemo *demo)
1037 {
1038 /*
1039  * Clear the slice plot and replace it with instructional text.
1040  */
1041   cpgslct(demo->slice_id);
1042   cpgask(0);
1043   cpgpage();
1044   cpgsch(3.5f);
1045   cpgsvp(0.0, 1.0, 0.0, 1.0);
1046   cpgswin(0.0, 1.0, 0.0, 1.0);
1047   cpgmtxt("T", -2.0, 0.5, 0.5, "Please wait.");
1048 }
1049 
1050 /*.......................................................................
1051  * Check that the specified command-line argument names a pgtkdemo
1052  * script file. A pgtkdemo script file is defined as being a readable
1053  * text file that contains the string "#!pgtkdemo.tcl" at its start.
1054  *
1055  * Input:
1056  *  name    char *   The command-line argument to be checked.
1057  * Output:
1058  *  return   int     0 - Not valid.
1059  *                   1 - Valid.
1060  */
valid_demo_script(char * name)1061 static int valid_demo_script(char *name)
1062 {
1063 #define REQUIRED_HEADER "#!pgtkdemo"
1064   char header[sizeof(REQUIRED_HEADER)];
1065 /*
1066  * Attempt to open the file for reading.
1067  */
1068   FILE *fp = fopen(name, "r");
1069   if(!fp) {
1070     fprintf(stderr, "Unable to open file: %s\n", name);
1071     return 0;
1072   };
1073 /*
1074  * Read the first line and compare it to the required header.
1075  */
1076   if(fgets(header, sizeof(header), fp) == NULL ||
1077      strcmp(header, REQUIRED_HEADER)!=0 || getc(fp) != '\n') {
1078     fprintf(stderr, "File '%s' is not a pgtkdemo Tcl script.\n", name);
1079     fclose(fp);
1080     return 0;
1081   };
1082   fclose(fp);
1083   return 1;
1084 }
1085