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