1    | /***************************************
2    |   $Header: /cvsroot/petscgraphics/tsview-ng.c,v 1.21 2004/08/11 12:44:08 hazelsct Exp $
3    | 
4    |   This program views the output of a time series saved using
5    |   +latex+{\tt IlluMultiSave()}.
6    |   +html+ <tt>IlluMultiSave()</tt>.
7    |   It basically just switches between timesteps; future versions may be more
8    |   interesting.  The neat part of it is that it loads multiprocessor data and
9    |   displays it on a single CPU.
10   |   ***************************************/
11   | 
12   | static char help[] = "Displays the output of of a timestep series saved using IlluMultiSave().\n\
13   | Usage:\n\
14   | \n\
15   |   tsview <basename> [-no_transparency]\n\
16   | \n\
17   | Then interactively flip through the timesteps (h or ? lists commands).\n";
18   | 
19   | #include "illuminator.h"
20   | #include <glade/glade.h>
21   | #include <gnome.h>
22   | #include <libgnomeui/libgnomeui.h>
23   | #include <sys/dir.h> /* For scandir(), alphasort, struct dirent */
24   | #include <libgen.h>  /* For dirname(), basename() */
25   | #include <string.h>  /* For strdup() */
26   | 
27   | /* Build with -DDEBUG for debugging output */
28   | #undef DPRINTF
29   | #ifdef DEBUG
30   | #define DPRINTF(fmt, args...) PetscPrintf (PETSC_COMM_WORLD, "%s: " fmt, __FUNCT__, args)
31   | #else
32   | #define DPRINTF(fmt, args...)
33   | #endif
34   | 
35   | GladeXML *xml;
36   | /* Filename list */
37   | int entrynum=0, total_entries=0, current_timestep;
38   | char *the_basename, *basedirname, **stepnames=NULL;
39   | double current_time;
40   | 
41   | /* Window parameters and drawables */
42   | int width=0, height=0, nx, ny, dataview_count=1;
43   | GtkWidget *dataviews [1];
44   | guchar *rgbbuf [1] = { NULL };
45   | double sizemag;
46   | PetscTruth transp;
47   | 
48   | /* Maximum intensity for hueintense plots */
49   | PetscScalar vecmax=-1.;
50   | 
51   | /* PETSc structures etc. */
52   | DA theda;
53   | Vec global;
54   | PetscScalar minmax[6] = { 0.,1., 0.,1., 0.,1. };
55   | field_plot_type *fieldtypes;
56   | int dimensions, num_fields, current_field, *field_index, num_variables[1],
57   |   **variable_indices;
58   | 
59   | /* First some primary functions which do stuff, then callbacks, then main(). */
60   | 
61   | #undef __FUNCT__
62   | #define __FUNCT__ "render_dataviews"
63   | 
64   | void render_dataviews ()
65   | {
66   |   int viewnum, nx,ny,nz, ierr;
67   | 
68   |   DPRINTF ("Rendering dataviews\n",0);
69   |   if (dataview_count != 1)
70   |     {
71   |       printf ("dataview_count != 1 is not yet supported\n");
72   |       exit(0);
73   |     }
74   |   for (viewnum=0; viewnum<dataview_count; viewnum++)
75   |     {
76   |       int nx,ny, xs,ys, xm,ym, ix,iy;
77   |       PetscScalar minval, maxval, refmag, *global_array;
78   |       GtkType type;
79   |       char thestatus [100];
80   | 
81   |       /* (Re)allocate buffer */
82   |       DPRINTF ("(Re)allocating RGB buffer\n",0);
83   |       if (!(rgbbuf [viewnum] =
84   | 	    (guchar *) realloc (rgbbuf [viewnum],
85   | 				3*width*height*sizeof(guchar)))) {
86   | 	  printf ("ERROR: can't reallocate RGB buffer\n");
87   | 	  exit (1); }
88   | 
89   |       /* Render into rgbbuf [viewnum] */
90   |       ierr = DAGetInfo (theda, PETSC_NULL, &nx,&ny,PETSC_NULL,
91   | 			PETSC_NULL,PETSC_NULL,PETSC_NULL, PETSC_NULL,
92   | 			PETSC_NULL,PETSC_NULL,PETSC_NULL); CHKERRQ (ierr);
93   |       ierr = DAGetCorners (theda, &xs,&ys,PETSC_NULL, &xm,&ym,PETSC_NULL);
94   |       CHKERRQ (ierr);
95   |       ierr = VecGetArray (global, &global_array); CHKERRQ (ierr);
96   |       ierr = render_rgb_local_2d
97   | 	(rgbbuf [viewnum], width,height, 3, global_array, num_fields,
98   | 	 current_field, fieldtypes [current_field],
99   | 	 ((fieldtypes [current_field] == FIELD_VECTOR ||
100  | 	   fieldtypes [current_field] == FIELD_VECTOR) &&
101  | 	  vecmax > 0) ? (&vecmax)-1 : NULL, nx,ny, xs,ys, xm,ym);
102  |       ierr = VecRestoreArray (global, &global_array); CHKERRQ (ierr);
103  | 
104  |       /* Draw buffer onto window */
105  |       DPRINTF ("Painting rgb buffer onto window\n",0);
106  |       if (!dataviews [viewnum])
107  | 	dataviews [viewnum] = glade_xml_get_widget (xml, "plot_area");
108  |       gtk_drawing_area_size (GTK_DRAWING_AREA (dataviews [viewnum]),
109  | 			     width, height);
110  |       gdk_draw_rgb_image (dataviews [viewnum]->window,
111  | 			  dataviews [viewnum]->style->fg_gc[GTK_STATE_NORMAL],
112  | 			  0, 0, width, height, GDK_RGB_DITHER_MAX,
113  | 			  rgbbuf [viewnum], width * 3);
114  |     }
115  | }
116  | 
117  | 
118  | #undef __FUNCT__
119  | #define __FUNCT__ "myfilter"
120  | 
121  | /*+ Little variable for myfilter() and refresh_stepnames(). +*/
122  | static char *basefilename;
123  | 
124  | /*++++++++++++++++++++++++++++++++++++++
125  |   This function returns non-zero for "qualifying" file names which start with
126  |   the stored files' basename.time and end with
127  |   +latex+{\tt .cpu0000.meta}.
128  |   +html+ <tt>.cpu0000.meta</tt>.
129  |   It is used as the
130  |   +latex+{\tt select()} function for {\tt scandir()} in {\tt main()}.
131  |   +html+ <tt>select()</tt> function for <tt>scandir()</tt> in <tt>main()</tt>.
132  | 
133  |   int myfilter Returns non-zero for qualifying filenames.
134  | 
135  |   const struct dirent *direntry Directory entry with filename to test.
136  |   ++++++++++++++++++++++++++++++++++++++*/
137  | 
138  | int myfilter (const struct dirent *direntry)
139  | {
140  |   if ((!strncmp (direntry->d_name, basefilename, strlen(basefilename))) &&
141  |       (!strncmp (direntry->d_name + strlen(basefilename), ".time", 5)))
142  |     return (!strncmp (direntry->d_name + strlen(direntry->d_name) - 13,
143  | 		      ".cpu0000.meta", 13));
144  |   return 0;
145  | }
146  | 
147  | 
148  | void on_plot_area_expose_event (GtkWidget *widget, GdkEventExpose *event,
149  | 				   gpointer user_data)
150  | {
151  |   gdk_draw_rgb_image (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
152  | 		      0, 0, width, height, GDK_RGB_DITHER_MAX, rgbbuf [0],
153  | 		      width * 3);
154  | }
155  | 
156  | /*++++++++++++++++++++++++++++++++++++++
157  |   This loads the names of the files into a long list.
158  |   ++++++++++++++++++++++++++++++++++++++*/
159  | 
160  | int refresh_stepnames ()
161  | {
162  |   struct dirent **namelist;
163  |   char *filec, *dirc;
164  |   int i, ierr;
165  | 
166  |   filec = strdup (the_basename);
167  |   dirc = strdup (the_basename);
168  |   basefilename = basename (filec);
169  |   basedirname = dirname (dirc);
170  | 
171  |   total_entries = scandir (basedirname, &namelist, myfilter, alphasort);
172  |   if (!total_entries)
173  |     {
174  |       ierr = PetscPrintf (PETSC_COMM_WORLD, "No such files\n");
175  |       CHKERRQ (ierr);
176  |       return 1;
177  |     }
178  |   if (total_entries < 0)
179  |     {
180  |       ierr = PetscPrintf (PETSC_COMM_WORLD, "Error scanning directory %s\n",
181  | 			  basedirname); CHKERRQ (ierr);
182  |       ierr = PetscFinalize (); CHKERRQ(ierr);
183  |       return 1;
184  |     }
185  |   DPRINTF ("%d eligible files:\n", total_entries);
186  | 
187  |   if (!(stepnames = (char **) realloc
188  | 	(stepnames, total_entries*sizeof (char *))))
189  |     {
190  |       ierr = PetscPrintf (PETSC_COMM_WORLD, "Error allocating memory\n");
191  |       CHKERRQ (ierr);
192  |       return 1;
193  |     }
194  |   for (i=0; i<total_entries; i++)
195  |     {
196  |       int filength = strlen(namelist[i]->d_name);
197  | 
198  |       stepnames [i] = (char *) malloc ((filength-12)*sizeof(char));
199  |       strncpy (stepnames [i], namelist[i]->d_name, filength-13);
200  |       stepnames [i] [filength-13] = '\0';
201  |       free (namelist[i]);
202  |       DPRINTF ("[%d] %s\n", i, stepnames [i]);
203  |       CHKERRQ (ierr);
204  |     }
205  | 
206  |   free (namelist);
207  |   return 0;
208  | }
209  | 
210  | void change_variable (GtkWidget *widget, gpointer user_data)
211  | {
212  |   current_field = GPOINTER_TO_INT (widget);
213  |   DPRINTF ("Switching to variable %d\n", current_field);
214  |   render_dataviews();
215  | }
216  | 
217  | void on_mag_spin_value_changed (GtkWidget *mag_spin, gpointer user_data) {
218  |   G_CONST_RETURN gchar *entrytext;
219  |   entrytext = gtk_entry_get_text (GTK_ENTRY (mag_spin));
220  |   sscanf (entrytext, "%lf", &sizemag);
221  |   width = (int) (minmax [1] * sizemag);
222  |   height = (int) (minmax [3] * sizemag);
223  | 
224  |   render_dataviews();
225  | }
226  | 
227  | 
228  | void display_timestep (int usermetacount, char **usermetanames,
229  | 		       char **usermetadata)
230  | {
231  |   int i;
232  |   static char step_buffer [20], time_buffer [20];
233  | 
234  |   for (i=0; i<usermetacount; i++)
235  |     {
236  |       if (!strncmp (usermetanames [i], "timestep", 8))
237  | 	sscanf (usermetadata [i], "%d", &current_timestep);
238  |       else if (!strncmp (usermetanames [i], "time", 4))
239  | 	sscanf (usermetadata [i], "%lf", &current_time);
240  |     }
241  |   snprintf (step_buffer, 19, "Timestep: %d", current_timestep);
242  |   gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml, "timestep_label")),
243  | 		      step_buffer);
244  |   snprintf (time_buffer, 19, "Time: %g", current_time);
245  |   gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml, "time_label")),
246  | 		      time_buffer);
247  | 
248  |   on_mag_spin_value_changed (glade_xml_get_widget (xml, "mag_spin"), NULL);
249  | }
250  | 
251  | 
252  | void on_save_activate (GtkWidget *widget, gpointer user_data)
253  | {
254  |   int i, ierr;
255  |   char **usermetanames, **usermetadata, filename [200], number[10];
256  |   FILE *outppm;
257  | 
258  |   strncpy (filename, basedirname, 198);
259  |   strcat (filename, "/");
260  |   strncat (filename, stepnames [entrynum], 198 - strlen (filename));
261  |   snprintf (number, 9, "-f%d", current_field);
262  |   strncat (filename, number, 198 - strlen (filename));
263  |   strncat (filename, ".ppm", 198 - strlen (filename));
264  | 
265  |   DPRINTF ("Saving image with filename %s\n", filename);
266  |   if (!(outppm = fopen (filename, "w")))
267  |     printf ("Error opening file %s\n", filename);
268  |   fprintf (outppm, "P6\n%d %d\n255\n", width, height);
269  |   fwrite (rgbbuf [0], sizeof (guchar), 3*width*height, outppm);
270  |   fclose (outppm);
271  | }
272  | 
273  | 
274  | void on_timestep_spin_value_changed (GtkWidget *timestep_spin, gpointer user_data) {
275  |   int usermetacount, ierr;
276  |   G_CONST_RETURN gchar *entrytext;
277  |   char **usermetanames, **usermetadata, filename [200], **field_name;
278  |   GtkWidget *variable_options, *variable_menu, **variable_item;
279  | 
280  |   entrytext = gtk_entry_get_text (GTK_ENTRY (timestep_spin));
281  |   sscanf (entrytext, "%d", &entrynum);
282  | 
283  |   /* Bound the entrynum between 0 and total_entries-1; -11 is the minimum of
284  |      the widget (from jump), 1000001 is the maximum. */
285  |   if ((entrynum < 0 && entrynum != -11) || entrynum == 1000001)
286  |     entrynum = total_entries-1;
287  |   if ((entrynum >= total_entries && entrynum != 1000001) || entrynum == -11)
288  |     entrynum = 0;
289  |   gtk_spin_button_set_value (GTK_SPIN_BUTTON (timestep_spin),
290  | 			     (gfloat) entrynum);
291  | 
292  |   strncpy (filename, basedirname, 198);
293  |   strcat (filename, "/");
294  |   strncat (filename, stepnames [entrynum], 198 - strlen (filename));
295  | 
296  |   ierr = IlluMultiRead (theda, global, filename, &usermetacount,&usermetanames,
297  | 			&usermetadata); CHKERRQ (ierr);
298  | 
299  |   display_timestep (usermetacount, usermetanames, usermetadata);
300  | }
301  | 
302  | 
303  | void on_refresh_activate (GtkWidget *none, gpointer user_data) {
304  |   if (refresh_stepnames ()) exit (1); }
305  | 
306  | 
307  | void on_about_activate (GtkWidget *none, gpointer user_data) {
308  |   gtk_widget_show (glade_xml_get_widget (xml, "about")); }
309  | 
310  | 
311  | #undef __FUNCT__
312  | #define __FUNCT__ "main"
313  | 
314  | /*++++++++++++++++++++++++++++++++++++++
315  |   This is
316  |   +latex+{\tt main()}.
317  |   +html+ <tt>main()</tt>.
318  | 
319  |   int main It returns an int to the OS.
320  | 
321  |   int argc Argument count.
322  | 
323  |   char *argv[] Arguments.
324  |   ++++++++++++++++++++++++++++++++++++++*/
325  | 
326  | int main (int argc, char *argv[])
327  | {
328  |   /* GnomeProgram *app; */
329  |   /* struct poptOption options [] = {
330  |     { "vector_max", 'vm', POPT_ARG_FLOAT, &vecmax, 0, "Reference vector length", "VECMAX" },
331  |     { NULL, '\0', 0, NULL, 0, NULL, NULL }}; */
332  |   int usermetacount=0, i, ierr;
333  |   char **usermetanames, **usermetadata, filename [200], **field_name;
334  |   GtkWidget *variable_options, *variable_menu, **variable_item;
335  | 
336  |   /*+ After
337  |     +latex+{\tt PETSc}
338  |     +html+ <tt>PETSc</tt>
339  |     and glade/GNOME initialization, it gets the list of files matching the
340  |     basename. +*/
341  |   ierr = PetscInitialize (&argc, &argv, (char *)0, help); CHKERRQ (ierr);
342  | 
343  |   if (argc<2)
344  |     {
345  |       ierr = PetscPrintf (PETSC_COMM_WORLD, "Usage: tsview basename\n");
346  |       CHKERRQ (ierr);
347  |       return 1;
348  |     }
349  | 
350  | #ifdef DEBUG
351  |   ierr = PetscPrintf (PETSC_COMM_WORLD, "Command line:"); CHKERRQ (ierr);
352  |   for (i=0; i<argc; i++)
353  |     {
354  |       ierr = PetscPrintf (PETSC_COMM_WORLD, " %s", argv[i]); CHKERRQ (ierr);
355  |     }
356  |   ierr = PetscPrintf (PETSC_COMM_WORLD, "\n"); CHKERRQ (ierr);
357  | #endif
358  | 
359  |   vecmax = -1.0;
360  |   ierr = PetscOptionsGetScalar (PETSC_NULL, "-vector_max", &vecmax,PETSC_NULL);
361  |   CHKERRQ (ierr);
362  |   ierr = PetscOptionsHasName (PETSC_NULL, "-no_transparency", &transp);
363  |   CHKERRQ (ierr);
364  |   transp = !transp;
365  | 
366  |   /* Kludge alert!  Setting argc to avoid gnome_program_init errors;
367  |      fix: use GNOME arguments instead of PETSc. */
368  |   argc=2;
369  | 
370  |   DPRINTF ("Running gnome_program_init with argc=2\n",0);
371  |   gnome_program_init ("TSView", VERSION, LIBGNOMEUI_MODULE, argc, argv, NULL);
372  | 
373  |   strncpy (filename, GLADE_DIRECTORY, 187);
374  |   strcat (filename, "/tsview.glade");
375  |   xml = glade_xml_new (filename, NULL, NULL);
376  |   glade_xml_signal_autoconnect (xml);
377  | 
378  |   if (argc>1)
379  |     the_basename = argv[1];
380  |   else
381  |     {
382  |       /* Put in filter for .time0000000.cpu0000.meta */
383  |       gtk_widget_show (glade_xml_get_widget (xml, "open_fileselect"));
384  |     }
385  | 
386  |   DPRINTF ("Loading list of timestep names\n",0);
387  |   if (refresh_stepnames ())
388  |     exit (1);
389  | 
390  |   DPRINTF ("Loading first timestep, creating distributed array\n",0);
391  |   strncpy (filename, basedirname, 198);
392  |   strcat (filename, "/");
393  |   strncat (filename, stepnames [0], 198 - strlen (stepnames [0]));
394  |   ierr = IlluMultiLoad (filename, &theda, minmax+1,minmax+3,minmax+5,
395  | 			&fieldtypes, &usermetacount, &usermetanames,
396  | 			&usermetadata);
397  |   CHKERRQ (ierr);
398  | 
399  |   /* Usermetadata xwidth, ywidth, zwidth override minmax in case IlluMulti
400  |      version of saved data is 0.1. */
401  |   DPRINTF ("Checking usermetadata for width information\n",0);
402  |   for (i=0; i<usermetacount; i++)
403  |     {
404  |       if (!strncmp (usermetanames [i], "xwidth", 6))
405  | 	sscanf (usermetadata [i], "%lf", minmax+1);
406  |       else if (!strncmp (usermetanames [i], "ywidth", 6))
407  | 	sscanf (usermetadata [i], "%lf", minmax+3);
408  |       else if (!strncmp (usermetanames [i], "zwidth", 6))
409  | 	sscanf (usermetadata [i], "%lf", minmax+5);
410  |     }
411  | 
412  |   DPRINTF ("Getting distributed array global vector and info\n",0);
413  |   ierr = DAGetGlobalVector (theda, &global); CHKERRQ (ierr);
414  |   ierr = DAGetInfo (theda, &dimensions, PETSC_NULL,PETSC_NULL,PETSC_NULL,
415  | 		    PETSC_NULL,PETSC_NULL,PETSC_NULL, &num_fields,
416  | 		    PETSC_NULL,PETSC_NULL,PETSC_NULL); CHKERRQ (ierr);
417  |   if (dimensions != 2)
418  |     SETERRQ (PETSC_ERR_ARG_OUTOFRANGE, "tsview-ng only supports 2-D distributed arrays at this point.");
419  | 
420  |   /* Build menu of field variables */
421  |   variable_options = glade_xml_get_widget (xml, "variable_menu");
422  |   gtk_option_menu_remove_menu (GTK_OPTION_MENU (variable_options));
423  |   variable_menu = gtk_menu_new ();
424  |   variable_item = (GtkWidget **) malloc (num_fields * sizeof (GtkWidget *));
425  |   field_name = (char **) malloc (num_fields * sizeof (char *));
426  |   field_index = (int *) malloc (num_fields * sizeof (int));
427  |   field_indices (num_fields, dimensions, fieldtypes, field_index);
428  |   DPRINTF ("Field indices:\n",0);
429  |   for (i=0; i<num_fields && field_index [i] != -1; i++)
430  |     {
431  |       ierr = DAGetFieldName (theda, field_index [i], field_name+i);
432  |       CHKERRQ (ierr);
433  |       DPRINTF ("%d index %d name %s\n", i, field_index [i], field_name [i]);
434  |       variable_item [i] = gtk_menu_item_new_with_label (field_name [i]);
435  |       gtk_menu_append (GTK_MENU (variable_menu), variable_item [i]);
436  |       gtk_signal_connect_object (GTK_OBJECT (variable_item [i]), "activate",
437  | 				 GTK_SIGNAL_FUNC (change_variable),
438  | 				 GINT_TO_POINTER (field_index [i]));
439  |       gtk_widget_show (variable_item [i]);
440  |     }
441  |   gtk_option_menu_set_menu (GTK_OPTION_MENU (variable_options), variable_menu);
442  |   gtk_widget_show (variable_menu);
443  |   gtk_widget_show (variable_options);
444  | 
445  |   /* Main window title */
446  |   {
447  |     char main_window_title[100] = "TSView: ";
448  |     GtkWidget *main_window = glade_xml_get_widget (xml, "main_window");
449  | 
450  |     strncat (main_window_title, basename (the_basename), 90);
451  |     gtk_window_set_title (GTK_WINDOW (main_window), main_window_title);
452  |     gtk_widget_show (main_window);
453  |   }
454  | 
455  |   DPRINTF ("Displaying first timestep\n",0);
456  |   display_timestep (usermetacount, usermetanames, usermetadata);
457  | 
458  |   DPRINTF ("Running main loop\n",0);
459  |   gtk_main();
460  | 
461  |   DPRINTF ("Finalizing and exitting.\n",0);
462  |   ierr = PetscFinalize (); CHKERRQ(ierr);
463  |   return 0;
464  | }