/* Last edited on 2007-04-15 17:07:34 by stolfi * Mased on {wplot.cpp} by Margarete Domingues, date: 1/Aug/2006 * Converted to C by J. Stolfi and A. Gomide, 07/jan/2007 */ /* We need to define _GNU_SOURCE to get {asprintf} and {NAN}. */ #define _GNU_SOURCE #include #include #include #include #include #include struct gnuplot_t { FILE *pp; /* Pipe to {stdin} of the {gnuplot} process. */ /* Ranges for {plot} or {splot} (NaN if autoscale): */ double xMin, xMax; double yMin, yMax; double zMin, zMax; /* These state variables affect most subsequent plot/splot commands: */ char *setTerminalCmds; char *fileExtension; /* If not NULL, the current terminal type needs "set output". */ char *xLabel; /* Label for the X axis. */ char *yLabel; /* Label for the Y axis. */ char *zLabel; /* Label for the Z axis. */ /* These state variables affect only 3D function graphing: */ char *set3DGraphPaletteCmds; char *set3DGraphStyleCmds; char *plot3DGraphCmd; /* These state variables affect only 2D plots of 2D grid data: */ char *setGridMapPaletteCmds; char *setGridMapStyleCmds; char *plotGridMapCmd; }; #define FALSE 0 #define TRUE 1 /* INTERNAL PROTOTYPES */ void gnuplot_write_terminal_options(gnuplot_t *wp, char *fileName); /* Sends to the {gnuplot} process the "set terminal" command, selecting the terminal type to the one previously stord in {wp}. If the terminal type requires a file name, the procedure also sends the command "set output \"{fileName}.{ext}\"", where {ext} is the appropriate extension. */ void gnuplot_write_3dgraph_options(gnuplot_t *wp); /* Sends to the {gnuplot} process a series of commands that set all options that affect 3D graphs of bivariate data (terminal type, palette, plot style, ranges, etc.), according to values stored in {wp}. */ void gnuplot_write_gridmap_options(gnuplot_t *wp); /* Sends to the {gnuplot} process a series of commands that set all options that affect 2D maps of grid data (terminal type, palette, plot style, ranges, etc.), according to values stored in {wp}. */ void gnuplot_write_set_range_cmd(FILE *pp, char *axis, double vMin, double vMax, int reverse); /* Sends to the {gnuplot} process the command "set {axis}range [{vMin}:{vMax}]". If {vMin} or {vMax} is NAN, omits it from the "set range" command. If {reverse} is true, adds the {gnuplot} "reverse" option. */ void gnuplot_write_set_axis_label_cmd(FILE *pp, char *axis, char *txt); /* Sends to the {gnuplot} process the command "set {axis}label \"{txt}\"", or "unset {axis}label" if {txt} is NULL. */ /* EXTERNAL IMPLEMENTATIONS */ gnuplot_t *gnuplot_start(int hSize, int vSize) { /* Create the plotter object {wp}: */ gnuplot_t *wp = malloc(sizeof(gnuplot_t)); assert(wp != NULL); /* Initialize fields of {wp} with sensible defaults: */ gnuplot_set_terminal(wp, /*x11*/ 0); gnuplot_set_axis_labels(wp, "X", "Y", "Z"); gnuplot_set_ranges(wp, NAN, NAN, NAN, NAN, NAN, NAN); gnuplot_set_3dgraph_palette(wp, /*blue--yellow--red*/ 0); gnuplot_set_3dgraph_style(wp, /*surface*/ 0); gnuplot_set_gridmap_palette(wp, /*blue--red*/ 0); gnuplot_set_gridmap_style(wp, /*surface*/ 0); /* Start {gnuplot} and set up a pipe {wp->pp} to it: */ if ((hSize == 0) || (vSize == 0)) { /* Debugging -- start a process that writes all commands to disk: */ pid_t prnum = getpid(); /* Process ID. */ int wpnum = ((unsigned int)wp) % 1000; /* Window ID, sort of. */ char *execCommand = jsprintf( "cat > gnuplot-%d-%03d.plt", prnum, wpnum); fprintf(stderr, "saving gnuplot commands with \"%s\"\n", execCommand); } else { /* Start a {gnuplot} process with the requested X window geometry: */ char *execCommand = jsprintf( "gnuplot -geometry '%dx%d' -raise -persist >& /dev/null", hSize, vSize); } wp->pp = popen(execCommand, "w"); if (wp->pp == NULL) { fprintf(stderr, "failed starting gnuplot"); exit(-1); } return wp; } void gnuplot_stop(gnuplot_t *wp) { if (wp->pp != NULL) { pclose(wp->pp); wp->pp = NULL; } free(wp); } void gnuplot_set_axis_labels(gnuplot_t *wp, char *xLabel, char *yLabel, char *zLabel) { wp->xLabel = xLabel; wp->yLabel = yLabel; wp->zLabel = zLabel; } void gnuplot_set_ranges ( gnuplot_t *wp, double xMin, double xMax, double yMin, double yMax, double zMin, double zMax ) { wp->xMin = xMin; wp->xMax = xMax; wp->yMin = yMin; wp->yMax = yMax; wp->zMin = zMin; wp->zMax = zMax; } void gnuplot_set_terminal(gnuplot_t *wp, int termKind) { switch(termKind) { case 10: /*eps */ wp->setTerminalCmds = "set terminal postscript eps enhanced \"Times-Roman\" 12\n"; wp->fileExtension = "eps"; break; case 11: /*eps */ wp->setTerminalCmds = "set terminal postscript eps color enhanced \"Times-Roman\" 12\n"; wp->fileExtension = "eps"; break; case 101: wp->setTerminalCmds = "set terminal png medium " "size 600,600 " "xffffff x000000 " "x202020 x404040 x606060 " "x808080 xA0A0A0 xC0C0C0 xE0E0E0\n"; wp->fileExtension = "png"; break; case 111: wp->setTerminalCmds = "set terminal png " "font arial 14 " "size 600,600\n"; wp->fileExtension = "png"; break; case 200: wp->setTerminalCmds = "set terminal png medium" " size 600 , 600\n"; wp->fileExtension = "png"; break; default: fprintf(stderr, "invalid terminal kind = %d, using kind 0\n", termKind); case 0: wp->setTerminalCmds = "set terminal x11 0\n"; wp->fileExtension = NULL; break; } } void gnuplot_set_3dgraph_palette(gnuplot_t *wp, int palette) { switch(palette) { case 1: /* Maps [-1 _ 1] over blue, dark-blue, yellow, dark-red: */ wp->set3DGraphPaletteCmds = "set palette defined(" "-1.000 \"royalblue\"," "-0.500 \"blue\"," " 0.000 \"grey20\"," " 0.500 \"dark-red\"," " 1.000 \"red\")\n"; break; case 2: /* Maps [-1 _ 1] over blue, dark-red to orange to yellow: */ wp->set3DGraphPaletteCmds = "set palette defined (" "-1.000 \"royalblue\"," "-0.667 \"blue\"," "-0.333 \"cyan\"," " 0.000 \"grey20\"," " 0.333 \"red\"," " 0.667 \"orange\"," " 1.000 \"yellow\")\n"; break; case 3: /* Maps [-1 _ +1] to dark-blue, blue, grey, yellow, grey dark red, red: */ wp->set3DGraphPaletteCmds = "set palette defined (" "-1.000 \"dark-blue\"," "-0.500 \"blue\"," "-0.001 \"light-grey\"," " 0.000 \"yellow\"," " 0.001 \"light-grey\"," " 0.500 \"dark-red\"," " 1.000 \"red\")\n"; break; case 4: /* Maps [-1 _ +1] to blue--lightblue--lightgray--orange--brown */ wp->set3DGraphPaletteCmds = "set palelisttte defined (" "-1.000 0.002 0.394 0.598," "-0.857 0.006 0.459 0.749," "-0.714 0.013 0.519 0.914," "-0.571 0.115 0.553 0.967," "-0.429 0.262 0.579 0.941," "-0.286 0.405 0.611 0.896," "-0.143 0.548 0.649 0.819," "+0.000 0.850 0.850 0.850," "+0.143 0.798 0.620 0.319," "+0.286 0.828 0.542 0.146," "+0.429 0.818 0.467 0.066," "+0.571 0.783 0.395 0.029," "+0.714 0.733 0.328 0.013," "+0.857 0.670 0.265 0.006," "+1.000 0.598 0.207 0.002 )\n"; break; case 5: /* Maps [-1 _ +1] to darkblue--blue--lightblue--lightgray--orange--red--darkred: */ wp->set3DGraphPaletteCmds = "set palette defined (" "-1.000 0.002 0.285 0.285," "-0.857 0.005 0.405 0.443," "-0.714 0.012 0.518 0.623," "-0.571 0.029 0.622 0.827," "-0.429 0.170 0.679 0.945," "-0.286 0.430 0.705 0.912," "-0.143 0.648 0.755 0.868," "+0.000 0.888 0.888 0.888," "+0.143 0.868 0.708 0.538," "+0.286 0.912 0.575 0.323," "+0.429 0.945 0.436 0.170," "+0.571 0.967 0.294 0.061," "+0.714 0.931 0.169 0.012," "+0.857 0.821 0.074 0.005," "+1.000 0.666 0.002 0.002 )\n"; break; case 6: /* Maps [-1 _ +1] to darkblue--blue--lightblue--lightgray--orange--red--darkred: */ wp->set3DGraphPaletteCmds = "set palette defined (" "-1.000 0.002 0.377 0.421," "-0.857 0.006 0.508 0.457," "-0.714 0.014 0.645 0.452," "-0.571 0.032 0.786 0.400," "-0.429 0.075 0.921 0.314," "-0.286 0.329 0.920 0.375," "-0.143 0.607 0.882 0.566," "+0.000 0.903 0.903 0.903," "+0.143 0.857 0.692 0.882," "+0.286 0.920 0.519 0.889," "+0.429 0.950 0.383 0.790," "+0.571 0.970 0.264 0.626," "+0.714 0.982 0.160 0.412," "+0.857 0.990 0.065 0.159," "+1.000 0.746 0.079 0.002 )\n"; break; case 7: /* Maps [-1 _ +1] to darkblue--blue--lightblue--lightgray--orange--red--darkred: */ wp->set3DGraphPaletteCmds = "set palette defined (" "-1.000 0.897 0.897 0.999," "-0.857 0.778 0.809 0.997," "-0.714 0.633 0.736 0.992," "-0.571 0.456 0.680 0.978," "-0.429 0.246 0.645 0.945," "-0.286 0.118 0.593 0.783," "-0.143 0.193 0.461 0.506," "+0.000 0.238 0.238 0.238," "+0.143 0.693 0.267 0.196," "+0.286 0.868 0.339 0.127," "+0.429 0.945 0.456 0.089," "+0.571 0.978 0.591 0.074," "+0.714 0.992 0.731 0.079," "+0.857 0.997 0.871 0.110," "+1.000 0.999 0.999 0.206 )\n"; break; case 8: /* Maps [-1 _ +1] to blue--royalblue--lightgray--yellow--red: */ wp->set3DGraphPaletteCmds = "set palette defined (" "-1.000 0.002 0.380 0.254," "-0.857 0.005 0.511 0.438," "-0.714 0.014 0.631 0.662," "-0.571 0.034 0.732 0.950," "-0.429 0.395 0.715 0.956," "-0.286 0.648 0.758 0.936," "-0.143 0.821 0.841 0.922," "+0.000 1.000 1.000 1.000," "+0.143 0.922 0.857 0.578," "+0.286 0.936 0.721 0.372," "+0.429 0.956 0.563 0.269," "+0.571 0.972 0.397 0.218," "+0.714 0.983 0.229 0.191," "+0.857 0.990 0.057 0.191," "+1.000 0.746 0.002 0.250 )\n"; break; default: fprintf(stderr, "invalid 3D graph palette = %d, using palette 0\n", palette); case 0: /* Maps [_1 _ +1] over dark-blue, blue, grey, dark red, red: */ wp->set3DGraphPaletteCmds = "set palette defined (" "-1.000 \"blue\"," "-0.500 \"royalblue\"," " 0.000 \"grey50\"," " 0.500 \"orange\"," " 1.000 \"red\")\n"; break; } } void gnuplot_set_3dgraph_style(gnuplot_t *wp, int style) { /* Defines {wp->set3DGraphStyleCmds}, {wp->plot3DGraphCmd}: */ switch(style) { case 1: /* 3D graph, painted surface, hidden parts visible. */ wp->set3DGraphStyleCmds = "set size square\n" "unset key\n" "set grid\n" "set border\n" "set hidden3d\n" "unset surface\n" "set pm3d\n"; wp->plot3DGraphCmd = "splot '-'\n"; break; case 2: /* 3D graph, wire mesh. */ wp->set3DGraphStyleCmds = "set size square\n" "unset key\n" "unset pm3d\n" "unset grid\n" "set border\n" "unset hidden3d\n" "set surface\n"; wp->plot3DGraphCmd = "splot '-' w line\n"; break; default: fprintf(stderr, "invalid 3D graph style = %d, using style 0\n", style); case 0: /* 3D graph, painted surface, hidden parts removed. */ wp->set3DGraphStyleCmds = "set size square\n" "unset key\n" "set grid back\n" "set border (15 + 16*15) linewidth 0.5\n" "unset surface\n" "unset hidden3d\n" "set pm3d\n"; wp->plot3DGraphCmd = "splot '-'\n"; break; } } void gnuplot_set_gridmap_palette(gnuplot_t *wp, int palette) { switch(palette) { case 1: /* [-1_+1] to rainbow */ wp->setGridMapPaletteCmds = "set palette defined (" "-1.000 'dark-violet'," "-0.750 'blue'," "-0.500 'royalblue'," "+0.250 'dark-green'," "00.000 'grey'," "+0.250 'light-magenta'," "+0.500 'light-red'," "+0.750 'orange'," "+1.000 'yellow')\n"; break; default: fprintf(stderr, "invalid grid map palette = %d, using palette 0\n", palette); case 0: /* [-1_+1] to blue--grey--red: */ wp->setGridMapPaletteCmds = "set palette defined (" "-1.000 'dark-blue'," "-0.500 'royalblue'," "00.000 'grey'," "+0.500 'gold'," "+1.000 'dark-red')\n"; break; } } void gnuplot_set_gridmap_style(gnuplot_t *wp, int style) { /* Defines {wp->setGridMapStyleCmds}, {wp->plotGridMapCmd}: */ switch(style) { default: fprintf(stderr, "invalid grid map style = %d, using style 0\n", style); case 0: /* Matrix of colored squares. */ wp->setGridMapStyleCmds = "set size ratio -1 1,1\n" "unset key\n" "set border\n" "unset grid\n" "set ticscale -0.667 -0.333\n" "set xtics 10\n" "set ytics 10\n" "set mxtics 5\n" "set mytics 5\n" "unset pm3d\n" "set pm3d map corners2color c1\n"; wp->plotGridMapCmd = "splot '-' matrix palette\n"; break; } } void gnuplot_plot_interval_queue ( gnuplot_t *wp, char *fileName, float dAstLo, float dAstHi, int n, float dLo[], float dHi[], float dMd[], int maxLevel, int level[], char *title ) { /* Reset the graphics state: */ fprintf(wp->pp, "reset\n"); /* Set the terminal type and output file name: */ gnuplot_write_terminal_options(wp, fileName); /* Set the coordinate axis labels and title: */ gnuplot_write_set_axis_label_cmd(wp->pp, "x", wp->xLabel); gnuplot_write_set_axis_label_cmd(wp->pp, "y", wp->yLabel); fprintf(wp->pp, "set title \"%s\"\n", title); /* Set the X,Y coordinate ranges as expected by {splot matrix}: */ gnuplot_write_set_range_cmd(wp->pp, "x", wp->xMin, wp->xMax, FALSE); gnuplot_write_set_range_cmd(wp->pp, "y", wp->yMin, wp->yMax, FALSE); /* Remove key, set axis lines: */ fputs("set nokey\n", wp->pp); fputs("set zeroaxis x\n", wp->pp); fputs("set nozeroaxis y\n", wp->pp); /* Issue the splot command: */ fputs("plot \\\n", wp->pp); fputs(" '-' using 1:2 with lines linetype 1 lw 1, \\\n", wp->pp); fputs(" '-' using 1:2 with lines linetype 1 lw 1, \\\n", wp->pp); fputs(" '-' using 1:4:2:3 with errorbars linetype 3 pt 7, \\\n", wp->pp); fputs(" '-' using 1:2 with points linetype 4 pt 5\n", wp->pp); /* Output the {dAst} interval: */ fprintf(wp->pp, "%g %g\n", wp->xMin, dAstLo); fprintf(wp->pp, "%g %g\n", wp->xMax, dAstLo); fputs("e\n", wp->pp); fprintf(wp->pp, "%g %g\n", wp->xMin, dAstHi); fprintf(wp->pp, "%g %g\n", wp->xMax, dAstHi); fputs("e\n", wp->pp); /* Output the intervals: */ int i; /* Rectangle number */ for (i = 0; i < n; i++) { fprintf(wp->pp, "%d %g %g %g\n", i, dLo[i], dHi[i], dMd[i]); } fputs("e\n", wp->pp); /* Output the levels: */ for (i = 0; i < n; i++) { fprintf(wp->pp, "%d %g\n", i, 1 + 0.2*((double)level[i])/((double)maxLevel)); } fputs("e\n", wp->pp); fflush(wp->pp); } void gnuplot_plot_3dgraph ( gnuplot_t *wp, char *fileName, int nx, int ny, double dx, double dy, double **A, char *title ) { /* Reset the graphics state: */ fprintf(wp->pp, "reset\n"); /* Set the terminal type and output file name: */ gnuplot_write_terminal_options(wp, fileName); /* Set the coordinate axis labels and title: */ fprintf(wp->pp, "set title \"%s\"\n", title); gnuplot_write_set_axis_label_cmd(wp->pp, "x", wp->xLabel); gnuplot_write_set_axis_label_cmd(wp->pp, "y", wp->yLabel); gnuplot_write_set_axis_label_cmd(wp->pp, "z", wp->zLabel); /* Set the coordinate ranges as saved in {wp}: */ gnuplot_write_set_range_cmd(wp->pp, "x", wp->xMin, wp->xMax, FALSE); gnuplot_write_set_range_cmd(wp->pp, "y", wp->yMin, wp->yMax, FALSE); gnuplot_write_set_range_cmd(wp->pp, "z", wp->zMin, wp->zMax, FALSE); gnuplot_write_set_range_cmd(wp->pp, "cb", wp->zMin, wp->zMax, FALSE); /* Set the splot style options: */ fputs(wp->set3DGraphPaletteCmds, wp->pp); fputs(wp->set3DGraphStyleCmds,wp->pp); /* Issue the splot command: */ fputs(wp->plot3DGraphCmd, wp->pp); /* Output the data to be visualized: */ int i, j; for (i = 0; i <= nx; i++) { for (j = 0; j <= ny; j++) { fprintf(wp->pp, "%f\t%f\t%g\n", i*dx, j*dy, A[i][j]); } fputc('\n', wp->pp); } fputs("e\n", wp->pp); fflush(wp->pp); } void gnuplot_plot_gridmap ( gnuplot_t *wp, char *fileName, int nx, int ny, double **A, char *title ) { /* Reset the graphics state: */ fprintf(wp->pp, "reset\n"); /* Set the terminal type and output file name: */ gnuplot_write_terminal_options(wp, fileName); /* Set the coordinate axis labels and title: */ gnuplot_write_set_axis_label_cmd(wp->pp, "x", wp->xLabel); gnuplot_write_set_axis_label_cmd(wp->pp, "y", wp->yLabel); fprintf(wp->pp, "set title \"%s\"\n", title); /* Set the X,Y coordinate ranges as expected by {splot matrix}: */ gnuplot_write_set_range_cmd(wp->pp, "x", -1, nx+2, FALSE); gnuplot_write_set_range_cmd(wp->pp, "y", -1, ny+2, FALSE); /* Set the Z coordinate (and color-bar) range as saved in {wp}: */ gnuplot_write_set_range_cmd(wp->pp, "z", wp->zMin, wp->zMax, FALSE); gnuplot_write_set_range_cmd(wp->pp, "cb", wp->zMin, wp->zMax, FALSE); /* Set the splot style options: */ fputs(wp->setGridMapPaletteCmds, wp->pp); fputs(wp->setGridMapStyleCmds,wp->pp); /* Issue the splot command: */ fputs(wp->plotGridMapCmd, wp->pp); /* For the user's benefit, the array must be shown with the X axis (first index of {A}) pointing left, and the Y axis (second index of {A}) pointing up. The command {splot matrix} requires the data to be presented to {gnuplot} as rows, where X increases right to left within each row, and Y increases from one row to the next. Moreover, {gnuplot}'s coloring algorithm (with option "corners2colors c1") requires one extra row of data at the bottom and right edges of the data file. */ /* Output the data to be visualized: */ int rows = ny + 2; /* Rows in data file. */ int cols = nx + 2; /* Columns in data file. */ int row; /* Row number in data file, in {0..rows-1} = {0..ny+1}. */ int col; /* Column number in data file, in {0..cols-1} = {0..nx+1}. */ for (row = 0; row < rows; row++) { for (col = 0; col < cols; col++) { /* Compute the indices {i,j} of {A} corresponding to {row,col}: */ int ix = col; /* In {0..nx+1}. */ int iy = row; /* In {0..ny+1}. */ /* Get the value to plot, either {A[ix][iy]} or padding: */ int xok = (ix >= 0) && (ix <= nx); int yok = (iy >= 0) && (iy <= ny); double Axy = (xok && yok ? A[ix][iy] : 0.0); if (col > 0) { fputc(' ', wp->pp); } fprintf(wp->pp, "%g", Axy); /*for the grid */ } fputc('\n', wp->pp); } fputs("e\n", wp->pp); fflush(wp->pp); } void gnuplot_write_terminal_options(gnuplot_t *wp, char *fileName) { /* Issue the "set terminal" command: */ fputs(wp->setTerminalCmds, wp->pp); if (wp->fileExtension != NULL) { /* Issue the "set output" command: */ if (fileName == NULL) { fprintf(stderr, "current terminal type requires a file name\n"); exit(-1); } fprintf(wp->pp, "set output \"%s.%s\"\n", fileName, wp->fileExtension); } } /* INTERNAL IMPLEMENTATIONS */ void gnuplot_write_set_range_cmd(FILE *pp, char *axis, double vMin, double vMax, int reverse) { fprintf(pp, "set %srange [", axis); if (isnan(vMin)) { fprintf(pp, "*"); } else { fprintf(pp, "%g", vMin); } fprintf(pp, ":"); if (isnan(vMax)) { fprintf(pp, "*"); } else { fprintf(pp, "%g", vMax); } fprintf(pp, "]"); if (reverse) { fprintf(pp, " reverse"); } fprintf(pp, "\n"); } void gnuplot_write_set_axis_label_cmd(FILE *pp, char *axis, char *txt) { if (txt == NULL) { fprintf(pp, "unset %slabel\n", axis); } else { fprintf(pp, "set %slabel \"%s\"\n", axis, txt); } }