3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
47 #include "gromacs/fileio/futil.h"
48 #include "gmx_fatal.h"
51 #include "gromacs/fileio/matio.h"
54 #include "gromacs/fileio/trxio.h"
71 char tickfont[STRLEN];
86 char leglabel[STRLEN];
87 char leg2label[STRLEN];
97 /* MUST correspond to char *legend[] in main() */
99 elSel, elBoth, elFirst, elSecond, elNone, elNR
102 /* MUST correspond to char *combine[] in main() */
104 ecSel, ecHalves, ecAdd, ecSub, ecMult, ecDiv, ecNR
107 void get_params(const char *mpin, const char *mpout, t_psrec *psr)
109 static const char *gmx_bools[BOOL_NR+1] = { "no", "yes", NULL };
110 /* this must correspond to t_rgb *linecolors[] below */
111 static const char *colors[] = { "none", "black", "white", NULL };
117 wi = init_warning(FALSE, 0);
121 inp = read_inpfile(mpin, &ninp, wi);
127 ETYPE("black&white", psr->bw, gmx_bools);
128 RTYPE("linewidth", psr->linewidth, 1.0);
129 STYPE("titlefont", psr->titfont, "Helvetica");
130 RTYPE("titlefontsize", psr->titfontsize, 20.0);
131 ETYPE("legend", psr->legend, gmx_bools);
132 STYPE("legendfont", psr->legfont, psr->titfont);
133 STYPE("legendlabel", psr->leglabel, "");
134 STYPE("legend2label", psr->leg2label, psr->leglabel);
135 RTYPE("legendfontsize", psr->legfontsize, 14.0);
136 RTYPE("xbox", psr->xboxsize, 0.0);
137 RTYPE("ybox", psr->yboxsize, 0.0);
138 RTYPE("matrixspacing", psr->boxspacing, 20.0);
139 RTYPE("xoffset", psr->xoffs, 0.0);
140 RTYPE("yoffset", psr->yoffs, psr->xoffs);
141 RTYPE("boxlinewidth", psr->boxlinewidth, psr->linewidth);
142 RTYPE("ticklinewidth", psr->ticklinewidth, psr->linewidth);
143 RTYPE("zerolinewidth", psr->zerolinewidth, psr->ticklinewidth);
144 ETYPE("x-lineat0value", psr->X.lineatzero, colors);
145 RTYPE("x-major", psr->X.major, NOTSET);
146 RTYPE("x-minor", psr->X.minor, NOTSET);
147 RTYPE("x-firstmajor", psr->X.offset, 0.0);
148 ETYPE("x-majorat0", psr->X.first, gmx_bools);
149 RTYPE("x-majorticklen", psr->X.majorticklen, 8.0);
150 RTYPE("x-minorticklen", psr->X.minorticklen, 4.0);
151 STYPE("x-label", psr->X.label, "");
152 RTYPE("x-fontsize", psr->X.fontsize, 16.0);
153 STYPE("x-font", psr->X.font, psr->titfont);
154 RTYPE("x-tickfontsize", psr->X.tickfontsize, 10.0);
155 STYPE("x-tickfont", psr->X.tickfont, psr->X.font);
156 ETYPE("y-lineat0value", psr->Y.lineatzero, colors);
157 RTYPE("y-major", psr->Y.major, psr->X.major);
158 RTYPE("y-minor", psr->Y.minor, psr->X.minor);
159 RTYPE("y-firstmajor", psr->Y.offset, psr->X.offset);
160 ETYPE("y-majorat0", psr->Y.first, gmx_bools);
161 RTYPE("y-majorticklen", psr->Y.majorticklen, psr->X.majorticklen);
162 RTYPE("y-minorticklen", psr->Y.minorticklen, psr->X.minorticklen);
163 STYPE("y-label", psr->Y.label, psr->X.label);
164 RTYPE("y-fontsize", psr->Y.fontsize, psr->X.fontsize);
165 STYPE("y-font", psr->Y.font, psr->X.font);
166 RTYPE("y-tickfontsize", psr->Y.tickfontsize, psr->X.tickfontsize);
167 STYPE("y-tickfont", psr->Y.tickfont, psr->Y.font);
171 write_inpfile(mpout, ninp, inp, TRUE, wi);
174 done_warning(wi, FARGS);
177 t_rgb black = { 0, 0, 0 };
178 t_rgb white = { 1, 1, 1 };
179 t_rgb red = { 1, 0, 0 };
180 t_rgb blue = { 0, 0, 1 };
181 #define BLACK (&black)
182 /* this must correspond to *colors[] in get_params */
183 t_rgb *linecolors[] = { NULL, &black, &white, NULL };
185 gmx_bool diff_maps(int nmap1, t_mapping *map1, int nmap2, t_mapping *map2)
188 gmx_bool bDiff, bColDiff = FALSE;
197 for (i = 0; i < nmap1; i++)
199 if (!matelmt_cmp(map1[i].code, map2[i].code))
203 if (strcmp(map1[i].desc, map2[i].desc) != 0)
207 if ((map1[i].rgb.r != map2[i].rgb.r) ||
208 (map1[i].rgb.g != map2[i].rgb.g) ||
209 (map1[i].rgb.b != map2[i].rgb.b))
214 if (!bDiff && bColDiff)
216 fprintf(stderr, "Warning: two colormaps differ only in RGB value, using one colormap.\n");
223 void leg_discrete(t_psdata ps, real x0, real y0, char *label,
224 real fontsize, char *font, int nmap, t_mapping map[])
230 boxhh = fontsize+DDD;
233 ps_strfont(ps, font, fontsize);
234 yhh = y0+fontsize+3*DDD;
235 if ((int)strlen(label) > 0)
237 ps_ctext(ps, x0, yhh, label, eXLeft);
239 ps_moveto(ps, x0, y0);
240 for (i = 0; (i < nmap); i++)
243 ps_rgb(ps, &(map[i].rgb));
244 ps_fillbox(ps, DDD, DDD, DDD+fontsize, boxhh-DDD);
246 ps_box(ps, DDD, DDD, DDD+fontsize, boxhh-DDD);
247 ps_ctext(ps, boxhh+2*DDD, fontsize/3, map[i].desc, eXLeft);
249 ps_moverel(ps, DDD, -fontsize/3);
253 void leg_continuous(t_psdata ps, real x0, real x, real y0, char *label,
254 real fontsize, char *font,
255 int nmap, t_mapping map[],
260 real yhh, boxxh, boxyh;
267 boxxh = (real)x/(real)(nmap-mapoffset);
268 if (boxxh > fontsize)
274 xx0 = x0-((nmap-mapoffset)*boxxh)/2.0;
276 for (i = 0; (i < nmap-mapoffset); i++)
278 ps_rgb(ps, &(map[i+mapoffset].rgb));
279 ps_fillbox(ps, xx0+i*boxxh, y0, xx0+(i+1)*boxxh, y0+boxyh);
281 ps_strfont(ps, font, fontsize);
283 ps_box(ps, xx0, y0, xx0+(nmap-mapoffset)*boxxh, y0+boxyh);
285 yhh = y0+boxyh+3*DDD;
286 ps_ctext(ps, xx0+boxxh/2, yhh, map[0].desc, eXCenter);
287 if ((int)strlen(label) > 0)
289 ps_ctext(ps, x0, yhh, label, eXCenter);
291 ps_ctext(ps, xx0+((nmap-mapoffset)*boxxh)
292 - boxxh/2, yhh, map[nmap-1].desc, eXCenter);
295 void leg_bicontinuous(t_psdata ps, real x0, real x, real y0, char *label1,
296 char *label2, real fontsize, char *font,
297 int nmap1, t_mapping map1[], int nmap2, t_mapping map2[])
299 real xx1, xx2, x1, x2;
301 x1 = x/(nmap1+nmap2)*nmap1; /* width of legend 1 */
302 x2 = x/(nmap1+nmap2)*nmap2; /* width of legend 2 */
303 xx1 = x0-(x2/2.0)-fontsize; /* center of legend 1 */
304 xx2 = x0+(x1/2.0)+fontsize; /* center of legend 2 */
305 x1 -= fontsize/2; /* adjust width */
306 x2 -= fontsize/2; /* adjust width */
307 leg_continuous(ps, xx1, x1, y0, label1, fontsize, font, nmap1, map1, 0);
308 leg_continuous(ps, xx2, x2, y0, label2, fontsize, font, nmap2, map2, 0);
311 static real box_height(t_matrix *mat, t_psrec *psr)
313 return mat->ny*psr->yboxsize;
316 static real box_dh(t_psrec *psr)
318 return psr->boxspacing;
321 #define IS_ONCE (i == nmat-1)
322 static real box_dh_top(gmx_bool bOnce, t_psrec *psr)
326 if (psr->bTitle || (psr->bTitleOnce && bOnce) )
328 dh = 2*psr->titfontsize;
338 static gmx_bool box_do_all_x_maj_ticks(t_psrec *psr)
340 return (psr->boxspacing > (1.5*psr->X.majorticklen));
343 static gmx_bool box_do_all_x_min_ticks(t_psrec *psr)
345 return (psr->boxspacing > (1.5*psr->X.minorticklen));
348 static void draw_boxes(t_psdata ps, real x0, real y0, real w,
349 int nmat, t_matrix mat[], t_psrec *psr)
354 char **xtick, **ytick;
355 real xx, yy, dy, xx00, yy00, offset_x, offset_y;
356 int i, j, x, y, ntx, nty, strlength;
358 /* Only necessary when there will be no y-labels */
363 ps_linewidth(ps, psr->boxlinewidth);
365 for (i = 0; (i < nmat); i++)
367 dy = box_height(&(mat[i]), psr);
368 ps_box(ps, x0-1, yy00-1, x0+w+1, yy00+dy+1);
369 yy00 += dy+box_dh(psr)+box_dh_top(IS_ONCE, psr);
372 /* Draw the ticks on the axes */
373 ps_linewidth(ps, psr->ticklinewidth);
376 for (i = 0; (i < nmat); i++)
378 if (mat[i].flags & MAT_SPATIAL_X)
388 if (mat[i].flags & MAT_SPATIAL_Y)
399 for (j = 0; (j < ntx); j++)
401 sprintf(buf, "%g", mat[i].axis_x[j]);
402 xtick[j] = strdup(buf);
404 ps_strfont(ps, psr->X.tickfont, psr->X.tickfontsize);
405 for (x = 0; (x < ntx); x++)
407 xx = xx00 + (x + offset_x)*psr->xboxsize;
408 if ( ( bRmod(mat[i].axis_x[x], psr->X.offset, psr->X.major) ||
409 (psr->X.first && (x == 0))) &&
410 ( (i == 0) || box_do_all_x_maj_ticks(psr) ) )
412 /* Longer tick marks */
413 ps_line (ps, xx, yy00, xx, yy00-psr->X.majorticklen);
414 /* Plot label on lowest graph only */
418 yy00-DDD-psr->X.majorticklen-psr->X.tickfontsize*0.8,
422 else if (bRmod(mat[i].axis_x[x], psr->X.offset, psr->X.minor) &&
423 ( (i == 0) || box_do_all_x_min_ticks(psr) ) )
425 /* Shorter tick marks */
426 ps_line(ps, xx, yy00, xx, yy00-psr->X.minorticklen);
428 else if (bRmod(mat[i].axis_x[x], psr->X.offset, psr->X.major) )
430 /* Even shorter marks, only each X.major */
431 ps_line(ps, xx, yy00, xx, yy00-(psr->boxspacing/2));
434 ps_strfont(ps, psr->Y.tickfont, psr->Y.tickfontsize);
436 for (j = 0; (j < nty); j++)
438 sprintf(buf, "%g", mat[i].axis_y[j]);
439 ytick[j] = strdup(buf);
442 for (y = 0; (y < nty); y++)
444 yy = yy00 + (y + offset_y)*psr->yboxsize;
445 if (bRmod(mat[i].axis_y[y], psr->Y.offset, psr->Y.major) ||
446 (psr->Y.first && (y == 0)))
449 strlength = max(strlength, (int)strlen(ytick[y]));
450 ps_line (ps, xx00, yy, xx00-psr->Y.majorticklen, yy);
451 ps_ctext(ps, xx00-psr->Y.majorticklen-DDD,
452 yy-psr->Y.tickfontsize/3.0, ytick[y], eXRight);
454 else if (bRmod(mat[i].axis_y[y], psr->Y.offset, psr->Y.minor) )
457 ps_line(ps, xx00, yy, xx00-psr->Y.minorticklen, yy);
463 /* Label on Y-axis */
464 if (!psr->bYonce || i == nmat/2)
466 if (strlen(psr->Y.label) > 0)
468 mylab = psr->Y.label;
472 mylab = mat[i].label_y;
474 if (strlen(mylab) > 0)
476 ps_strfont(ps, psr->Y.font, psr->Y.fontsize);
478 xxx = x0-psr->X.majorticklen-psr->X.tickfontsize*strlength-DDD;
479 ps_ctext(ps, yy00+box_height(&mat[i], psr)/2.0, 612.5-xxx,
485 yy00 += box_height(&(mat[i]), psr)+box_dh(psr)+box_dh_top(IS_ONCE, psr);
487 /* Label on X-axis */
488 if (strlen(psr->X.label) > 0)
490 mylab = psr->X.label;
494 mylab = mat[0].label_x;
496 if (strlen(mylab) > 0)
498 ps_strfont(ps, psr->X.font, psr->X.fontsize);
499 ps_ctext(ps, x0+w/2, y0-DDD-psr->X.majorticklen-psr->X.tickfontsize*FUDGE-
500 psr->X.fontsize, mylab, eXCenter);
504 static void draw_zerolines(t_psdata out, real x0, real y0, real w,
505 int nmat, t_matrix mat[], t_psrec *psr)
507 real xx, yy, dy, xx00, yy00;
512 ps_linewidth(out, psr->zerolinewidth);
513 for (i = 0; (i < nmat); i++)
515 dy = box_height(&(mat[i]), psr);
516 /* mat[i].axis_x and _y were already set by draw_boxes */
517 if (psr->X.lineatzero)
519 ps_rgb(out, linecolors[psr->X.lineatzero]);
520 for (x = 0; (x < mat[i].nx); x++)
522 xx = xx00+(x+0.7)*psr->xboxsize;
523 /* draw lines whenever tick label almost zero (e.g. next trajectory) */
524 if (x != 0 && x < mat[i].nx-1 &&
525 abs(mat[i].axis_x[x]) <
526 0.1*abs(mat[i].axis_x[x+1]-mat[i].axis_x[x]) )
528 ps_line (out, xx, yy00, xx, yy00+dy+2);
532 if (psr->Y.lineatzero)
534 ps_rgb(out, linecolors[psr->Y.lineatzero]);
535 for (y = 0; (y < mat[i].ny); y++)
537 yy = yy00+(y+0.7)*psr->yboxsize;
538 /* draw lines whenever tick label almost zero (e.g. next trajectory) */
539 if (y != 0 && y < mat[i].ny-1 &&
540 abs(mat[i].axis_y[y]) <
541 0.1*abs(mat[i].axis_y[y+1]-mat[i].axis_y[y]) )
543 ps_line (out, xx00, yy, xx00+w+2, yy);
547 yy00 += box_height(&(mat[i]), psr)+box_dh(psr)+box_dh_top(IS_ONCE, psr);
551 static void box_dim(int nmat, t_matrix mat[], t_matrix *mat2, t_psrec *psr,
552 int elegend, gmx_bool bFrame,
553 real *w, real *h, real *dw, real *dh)
556 real ww, hh, dww, dhh;
562 for (i = 0; (i < nmat); i++)
564 ww = max(ww, mat[i].nx*psr->xboxsize);
565 hh += box_height(&(mat[i]), psr);
566 maxytick = max(maxytick, mat[i].nx);
570 if (mat[0].label_y[0])
572 dww += 2.0*(psr->Y.fontsize+DDD);
574 if (psr->Y.major > 0)
576 dww += psr->Y.majorticklen + DDD +
577 psr->Y.tickfontsize*(log(maxytick)/log(10.0));
579 else if (psr->Y.minor > 0)
581 dww += psr->Y.minorticklen;
584 if (mat[0].label_x[0])
586 dhh += psr->X.fontsize+2*DDD;
588 if ( /* fool emacs auto-indent */
589 (elegend == elBoth && (mat[0].legend[0] || mat2[0].legend[0])) ||
590 (elegend == elFirst && mat[0].legend[0]) ||
591 (elegend == elSecond && mat2[0].legend[0]) )
593 dhh += 2*(psr->legfontsize*FUDGE+2*DDD);
597 dhh += psr->legfontsize*FUDGE+2*DDD;
599 if (psr->X.major > 0)
601 dhh += psr->X.tickfontsize*FUDGE+2*DDD+psr->X.majorticklen;
603 else if (psr->X.minor > 0)
605 dhh += psr->X.minorticklen;
608 hh += (nmat-1)*box_dh(psr);
609 hh += box_dh_top(TRUE, psr);
612 hh += (nmat-1)*box_dh_top(FALSE, psr);
621 int add_maps(t_mapping **newmap,
622 int nmap1, t_mapping map1[], int nmap2, t_mapping map2[])
624 static char mapper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+{}|;:',<.>/?";
629 nsymbols = strlen(mapper);
631 if (nmap > nsymbols*nsymbols)
633 gmx_fatal(FARGS, "Not enough symbols to merge the two colormaps\n");
635 printf("Combining colormaps of %d and %d elements into one of %d elements\n",
638 for (j = 0; j < nmap1; j++)
640 map[j].code.c1 = mapper[j % nsymbols];
643 map[j].code.c2 = mapper[j/nsymbols];
645 map[j].rgb.r = map1[j].rgb.r;
646 map[j].rgb.g = map1[j].rgb.g;
647 map[j].rgb.b = map1[j].rgb.b;
648 map[j].desc = map1[j].desc;
650 for (j = 0; j < nmap2; j++)
653 map[k].code.c1 = mapper[k % nsymbols];
656 map[k].code.c2 = mapper[k/nsymbols];
658 map[k].rgb.r = map2[j].rgb.r;
659 map[k].rgb.g = map2[j].rgb.g;
660 map[k].rgb.b = map2[j].rgb.b;
661 map[k].desc = map2[j].desc;
668 void xpm_mat(const char *outf, int nmat, t_matrix *mat, t_matrix *mat2,
669 gmx_bool bDiag, gmx_bool bFirstDiag)
673 int i, j, k, x, y, col;
675 t_mapping *map = NULL;
677 out = ffopen(outf, "w");
679 for (i = 0; i < nmat; i++)
681 if (!mat2 || !diff_maps(mat[i].nmap, mat[i].map, mat2[i].nmap, mat2[i].map))
683 write_xpm_m(out, mat[0]);
687 nmap = add_maps(&map, mat[i].nmap, mat[i].map, mat2[i].nmap, mat2[i].map);
688 for (x = 0; (x < mat[i].nx); x++)
690 for (y = 0; (y < mat[i].nx); y++)
692 if ((x < y) || ((x == y) && bFirstDiag)) /* upper left -> map1 */
694 col = mat[i].matrix[x][y];
696 else /* lower right -> map2 */
698 col = mat[i].nmap+mat[i].matrix[x][y];
700 if ((bDiag) || (x != y))
702 mat[i].matrix[x][y] = col;
706 mat[i].matrix[x][y] = 0;
713 if (mat2 && (strcmp(mat[i].title, mat2[i].title) != 0))
715 sprintf(mat[i].title+strlen(mat[i].title), " / %s", mat2[i].title);
717 if (mat2 && (strcmp(mat[i].legend, mat2[i].legend) != 0))
719 sprintf(mat[i].legend+strlen(mat[i].legend), " / %s", mat2[i].legend);
721 write_xpm_m(out, mat[i]);
727 static void tick_spacing(int n, real axis[], real offset, char axisnm,
728 real *major, real *minor)
731 gmx_bool bTryAgain, bFive;
732 int i, j, t, f = 0, ten;
734 real major_fact[NFACT] = {5, 4, 2, 1};
735 real minor_fact[NFACT] = {5, 4, 4, 5};
737 /* start with interval between 10 matrix points: */
738 space = max(10*axis[1]-axis[0], axis[min(10, n-1)]-axis[0]);
739 /* get power of 10 */
740 ten = (int)ceil(log(space)/log(10))-1;
742 for (t = ten+2; t > ten-3 && bTryAgain; t--)
744 for (f = 0; f < NFACT && bTryAgain; f++)
746 space = pow(10, t) * major_fact[f];
747 /* count how many ticks we would get: */
749 for (j = 0; j < n; j++)
751 if (bRmod(axis[j], offset, space) )
756 /* do we have a reasonable number of ticks ? */
757 bTryAgain = (i > min(10, n-1)) || (i < 5);
762 space = max(10*axis[1]-axis[0], axis[min(10, n-1)]-axis[0]);
763 fprintf(stderr, "Auto tick spacing failed for %c-axis, guessing %g\n",
767 *minor = space / minor_fact[f-1];
768 fprintf(stderr, "Auto tick spacing for %c-axis: major %g, minor %g\n",
769 axisnm, *major, *minor);
772 void ps_mat(const char *outf, int nmat, t_matrix mat[], t_matrix mat2[],
773 gmx_bool bFrame, gmx_bool bDiag, gmx_bool bFirstDiag,
774 gmx_bool bTitle, gmx_bool bTitleOnce, gmx_bool bYonce, int elegend,
775 real size, real boxx, real boxy, const char *m2p, const char *m2pout,
779 char buf[256], *legend;
783 int i, j, x, y, col, leg = 0;
786 int nmap1 = 0, nmap2 = 0, leg_nmap;
787 t_mapping *map1 = NULL, *map2 = NULL, *leg_map;
788 gmx_bool bMap1, bNextMap1, bDiscrete;
791 libm2p = m2p ? gmxlibfn(m2p) : m2p;
792 get_params(libm2p, m2pout, &psrec);
796 if (psr->X.major <= 0)
798 tick_spacing((mat[0].flags & MAT_SPATIAL_X) ? mat[0].nx + 1 : mat[0].nx,
799 mat[0].axis_x, psr->X.offset, 'X',
800 &(psr->X.major), &(psr->X.minor) );
802 if (psr->X.minor <= 0)
804 psr->X.minor = psr->X.major / 2;
806 if (psr->Y.major <= 0)
808 tick_spacing((mat[0].flags & MAT_SPATIAL_Y) ? mat[0].ny + 1 : mat[0].ny,
809 mat[0].axis_y, psr->Y.offset, 'Y',
810 &(psr->Y.major), &(psr->Y.minor) );
812 if (psr->Y.minor <= 0)
814 psr->Y.minor = psr->Y.major / 2;
819 psr->xboxsize = boxx;
820 psr->yboxsize = boxx;
824 psr->yboxsize = boxy;
827 if (psr->xboxsize == 0)
829 psr->xboxsize = size/mat[0].nx;
830 printf("Set the x-size of the box to %.3f\n", psr->xboxsize);
832 if (psr->yboxsize == 0)
834 psr->yboxsize = size/mat[0].nx;
835 printf("Set the y-size of the box to %.3f\n", psr->yboxsize);
839 for (i = 0; (i < nmat); i++)
841 if (mat[i].nmap > nmap1)
850 printf("Selected legend of matrix # %d for display\n", leg);
855 for (i = 0; (i < nmat); i++)
857 if (mat2[i].nmap > nmap2)
859 nmap2 = mat2[i].nmap;
866 printf("Selected legend of matrix # %d for second display\n", leg);
869 if ( (mat[0].legend[0] == 0) && psr->legend)
871 strcpy(mat[0].legend, psr->leglabel);
874 bTitle = bTitle && mat[nmat-1].title[0];
875 bTitleOnce = bTitleOnce && mat[nmat-1].title[0];
876 psr->bTitle = bTitle;
877 psr->bTitleOnce = bTitleOnce;
878 psr->bYonce = bYonce;
880 /* Set up size of box for nice colors */
881 box_dim(nmat, mat, mat2, psr, elegend, bFrame, &w, &h, &dw, &dh);
883 /* Set up bounding box */
897 out = ps_open(outf, 0, 0, x, y);
898 ps_linewidth(out, psr->linewidth);
899 ps_init_rgb_box(out, psr->xboxsize, psr->yboxsize);
900 ps_init_rgb_nbox(out, psr->xboxsize, psr->yboxsize);
901 ps_translate(out, psr->xoffs, psr->yoffs);
905 ps_comment(out, "Here starts the BOX drawing");
906 draw_boxes(out, x0, y0, w, nmat, mat, psr);
909 for (i = 0; (i < nmat); i++)
911 if (bTitle || (bTitleOnce && i == nmat-1) )
913 /* Print title, if any */
915 ps_strfont(out, psr->titfont, psr->titfontsize);
916 if (!mat2 || (strcmp(mat[i].title, mat2[i].title) == 0))
918 strcpy(buf, mat[i].title);
922 sprintf(buf, "%s / %s", mat[i].title, mat2[i].title);
924 ps_ctext(out, x0+w/2, y0+box_height(&(mat[i]), psr)+psr->titfontsize,
927 sprintf(buf, "Here starts the filling of box #%d", i);
928 ps_comment(out, buf);
929 for (x = 0; (x < mat[i].nx); x++)
934 xx = x0+x*psr->xboxsize;
935 ps_moveto(out, xx, y0);
937 bMap1 = (!mat2 || (x < y || (x == y && bFirstDiag)));
938 if ((bDiag) || (x != y))
940 col = mat[i].matrix[x][y];
946 for (nexty = 1; (nexty <= mat[i].ny); nexty++)
948 bNextMap1 = (!mat2 || (x < nexty || (x == nexty && bFirstDiag)));
949 /* TRUE: upper left -> map1 */
950 /* FALSE: lower right -> map2 */
951 if ((nexty == mat[i].ny) || (!bDiag && (x == nexty)))
957 nextcol = mat[i].matrix[x][nexty];
959 if ( (nexty == mat[i].ny) || (col != nextcol) || (bMap1 != bNextMap1) )
965 ps_rgb_nbox(out, &(mat[i].map[col].rgb), nexty-y);
969 ps_rgb_nbox(out, &(mat2[i].map[col].rgb), nexty-y);
974 ps_moverel(out, 0, psr->yboxsize);
982 y0 += box_height(&(mat[i]), psr)+box_dh(psr)+box_dh_top(IS_ONCE, psr);
985 if (psr->X.lineatzero || psr->Y.lineatzero)
987 /* reset y0 for first box */
989 ps_comment(out, "Here starts the zero lines drawing");
990 draw_zerolines(out, x0, y0, w, nmat, mat, psr);
993 if (elegend != elNone)
995 ps_comment(out, "Now it's legend time!");
996 ps_linewidth(out, psr->linewidth);
997 if (mat2 == NULL || elegend != elSecond)
999 bDiscrete = mat[0].bDiscrete;
1000 legend = mat[0].legend;
1006 bDiscrete = mat2[0].bDiscrete;
1007 legend = mat2[0].legend;
1013 leg_discrete(out, psr->legfontsize, DDD, legend,
1014 psr->legfontsize, psr->legfont, leg_nmap, leg_map);
1018 if (elegend != elBoth)
1020 leg_continuous(out, x0+w/2, w/2, DDD, legend,
1021 psr->legfontsize, psr->legfont, leg_nmap, leg_map,
1026 leg_bicontinuous(out, x0+w/2, w, DDD, mat[0].legend, mat2[0].legend,
1027 psr->legfontsize, psr->legfont, nmap1, map1, nmap2, map2);
1030 ps_comment(out, "Were there, dude");
1036 void make_axis_labels(int nmat, t_matrix *mat)
1040 for (i = 0; (i < nmat); i++)
1042 /* Make labels for x axis */
1043 if (mat[i].axis_x == NULL)
1045 snew(mat[i].axis_x, mat[i].nx);
1046 for (j = 0; (j < mat[i].nx); j++)
1048 mat[i].axis_x[j] = j;
1051 /* Make labels for y axis */
1052 if (mat[i].axis_y == NULL)
1054 snew(mat[i].axis_y, mat[i].ny);
1055 for (j = 0; (j < mat[i].ny); j++)
1057 mat[i].axis_y[j] = j;
1063 void prune_mat(int nmat, t_matrix *mat, t_matrix *mat2, int skip)
1065 int i, x, y, xs, ys;
1067 for (i = 0; i < nmat; i++)
1069 fprintf(stderr, "converting %dx%d matrix to %dx%d\n",
1070 mat[i].nx, mat[i].ny,
1071 (mat[i].nx+skip-1)/skip, (mat[i].ny+skip-1)/skip);
1072 /* walk through matrix */
1074 for (x = 0; (x < mat[i].nx); x++)
1078 mat[i].axis_x[xs] = mat[i].axis_x[x];
1081 mat2[i].axis_x[xs] = mat2[i].axis_x[x];
1084 for (y = 0; (y < mat[i].ny); y++)
1088 mat[i].axis_y[ys] = mat[i].axis_y[y];
1091 mat2[i].axis_y[ys] = mat2[i].axis_y[y];
1096 mat[i].matrix[xs][ys] = mat[i].matrix[x][y];
1099 mat2[i].matrix[xs][ys] = mat2[i].matrix[x][y];
1107 /* adjust parameters */
1108 mat[i].nx = (mat[i].nx+skip-1)/skip;
1109 mat[i].ny = (mat[i].ny+skip-1)/skip;
1112 mat2[i].nx = (mat2[i].nx+skip-1)/skip;
1113 mat2[i].ny = (mat2[i].ny+skip-1)/skip;
1118 void zero_lines(int nmat, t_matrix *mat, t_matrix *mat2)
1123 for (i = 0; i < nmat; i++)
1125 for (m = 0; m < (mat2 ? 2 : 1); m++)
1135 for (x = 0; x < mats[i].nx-1; x++)
1137 if (abs(mats[i].axis_x[x+1]) < 1e-5)
1139 for (y = 0; y < mats[i].ny; y++)
1141 mats[i].matrix[x][y] = 0;
1145 for (y = 0; y < mats[i].ny-1; y++)
1147 if (abs(mats[i].axis_y[y+1]) < 1e-5)
1149 for (x = 0; x < mats[i].nx; x++)
1151 mats[i].matrix[x][y] = 0;
1159 void write_combined_matrix(int ecombine, const char *fn,
1160 int nmat, t_matrix *mat1, t_matrix *mat2,
1161 real *cmin, real *cmax)
1163 int i, j, k, nlevels;
1164 t_mapping *map = NULL;
1166 real **rmat1, **rmat2;
1169 out = ffopen(fn, "w");
1170 for (k = 0; k < nmat; k++)
1172 if (mat2[k].nx != mat1[k].nx || mat2[k].ny != mat1[k].ny)
1174 gmx_fatal(FARGS, "Size of frame %d in 1st (%dx%d) and 2nd matrix (%dx%d) do"
1175 " not match.\n", k, mat1[k].nx, mat1[k].ny, mat2[k].nx, mat2[k].ny);
1177 printf("Combining two %dx%d matrices\n", mat1[k].nx, mat1[k].ny);
1178 rmat1 = matrix2real(&mat1[k], NULL);
1179 rmat2 = matrix2real(&mat2[k], NULL);
1180 if (NULL == rmat1 || NULL == rmat2)
1182 gmx_fatal(FARGS, "Could not extract real data from %s xpm matrices. Note that, e.g.,\n"
1183 "g_rms and g_mdmat provide such data, but not do_dssp.\n",
1184 (NULL == rmat1 && NULL == rmat2) ? "both" : "one of the" );
1188 for (j = 0; j < mat1[k].ny; j++)
1190 for (i = 0; i < mat1[k].nx; i++)
1194 case ecAdd: rmat1[i][j] += rmat2[i][j]; break;
1195 case ecSub: rmat1[i][j] -= rmat2[i][j]; break;
1196 case ecMult: rmat1[i][j] *= rmat2[i][j]; break;
1197 case ecDiv: rmat1[i][j] /= rmat2[i][j]; break;
1199 gmx_fatal(FARGS, "No such combination rule %d for matrices", ecombine);
1201 rlo = min(rlo, rmat1[i][j]);
1202 rhi = max(rhi, rmat1[i][j]);
1213 nlevels = max(mat1[k].nmap, mat2[k].nmap);
1217 "combination results in uniform matrix (%g), no output\n", rhi);
1220 else if (rlo>=0 || rhi<=0)
1221 write_xpm(out, mat1[k].flags, mat1[k].title, mat1[k].legend,
1222 mat1[k].label_x, mat1[k].label_y,
1223 mat1[k].nx, mat1[k].ny, mat1[k].axis_x, mat1[k].axis_y,
1224 rmat1, rlo, rhi, rhi<=0?red:white, rhi<=0?white:blue,
1227 write_xpm3(out, mat2[k].flags, mat1[k].title, mat1[k].legend,
1228 mat1[k].label_x, mat1[k].label_y,
1229 mat1[k].nx, mat1[k].ny, mat1[k].axis_x, mat1[k].axis_y,
1230 rmat1, rlo, 0, rhi, red, white, blue, &nlevels);
1234 write_xpm(out, mat1[k].flags, mat1[k].title, mat1[k].legend,
1235 mat1[k].label_x, mat1[k].label_y,
1236 mat1[k].nx, mat1[k].ny, mat1[k].axis_x, mat1[k].axis_y,
1237 rmat1, rlo, rhi, white, black, &nlevels);
1243 void do_mat(int nmat, t_matrix *mat, t_matrix *mat2,
1244 gmx_bool bFrame, gmx_bool bZeroLine, gmx_bool bDiag, gmx_bool bFirstDiag, gmx_bool bTitle,
1245 gmx_bool bTitleOnce, gmx_bool bYonce, int elegend,
1246 real size, real boxx, real boxy,
1247 const char *epsfile, const char *xpmfile, const char *m2p,
1248 const char *m2pout, int skip, int mapoffset)
1254 for (k = 0; (k < nmat); k++)
1256 if ((mat2[k].nx != mat[k].nx) || (mat2[k].ny != mat[k].ny))
1258 gmx_fatal(FARGS, "WAKE UP!! Size of frame %d in 2nd matrix file (%dx%d) does not match size of 1st matrix (%dx%d) or the other way around.\n",
1259 k, mat2[k].nx, mat2[k].ny, mat[k].nx, mat[k].ny);
1261 for (j = 0; (j < mat[k].ny); j++)
1263 for (i = bFirstDiag ? j+1 : j; (i < mat[k].nx); i++)
1265 mat[k].matrix[i][j] = mat2[k].matrix[i][j];
1270 for (i = 0; (i < nmat); i++)
1272 fprintf(stderr, "Matrix %d is %d x %d\n", i, mat[i].nx, mat[i].ny);
1275 make_axis_labels(nmat, mat);
1279 prune_mat(nmat, mat, mat2, skip);
1284 zero_lines(nmat, mat, mat);
1287 if (epsfile != NULL)
1289 ps_mat(epsfile, nmat, mat, mat2, bFrame, bDiag, bFirstDiag,
1290 bTitle, bTitleOnce, bYonce, elegend,
1291 size, boxx, boxy, m2p, m2pout, mapoffset);
1293 if (xpmfile != NULL)
1295 xpm_mat(xpmfile, nmat, mat, mat2, bDiag, bFirstDiag);
1299 void gradient_map(rvec grad, int nmap, t_mapping map[])
1304 for (i = 0; i < nmap; i++)
1307 map[i].rgb.r = 1-c*(1-grad[XX]);
1308 map[i].rgb.g = 1-c*(1-grad[YY]);
1309 map[i].rgb.b = 1-c*(1-grad[ZZ]);
1313 void gradient_mat(rvec grad, int nmat, t_matrix mat[])
1317 for (m = 0; m < nmat; m++)
1319 gradient_map(grad, mat[m].nmap, mat[m].map);
1323 void rainbow_map(gmx_bool bBlue, int nmap, t_mapping map[])
1328 for (i = 0; i < nmap; i++)
1330 c = (map[i].rgb.r + map[i].rgb.g + map[i].rgb.b)/3;
1339 if (c <= 0.25) /* 0-0.25 */
1342 g = pow(4*c, 0.666);
1345 else if (c <= 0.5) /* 0.25-0.5 */
1349 b = pow(2-4*c, 0.666);
1351 else if (c <= 0.75) /* 0.5-0.75 */
1353 r = pow(4*c-2, 0.666);
1360 g = pow(4-4*c, 0.666);
1369 void rainbow_mat(gmx_bool bBlue, int nmat, t_matrix mat[])
1373 for (m = 0; m < nmat; m++)
1375 rainbow_map(bBlue, mat[m].nmap, mat[m].map);
1379 int gmx_xpm2ps(int argc, char *argv[])
1381 const char *desc[] = {
1382 "[TT]xpm2ps[tt] makes a beautiful color plot of an XPixelMap file.",
1383 "Labels and axis can be displayed, when they are supplied",
1384 "in the correct matrix format.",
1385 "Matrix data may be generated by programs such as [TT]do_dssp[tt], [TT]g_rms[tt] or",
1386 "[TT]g_mdmat[tt].[PAR]",
1387 "Parameters are set in the [TT].m2p[tt] file optionally supplied with",
1388 "[TT]-di[tt]. Reasonable defaults are provided. Settings for the [IT]y[it]-axis",
1389 "default to those for the [IT]x[it]-axis. Font names have a defaulting hierarchy:",
1390 "titlefont -> legendfont; titlefont -> (xfont -> yfont -> ytickfont)",
1391 "-> xtickfont, e.g. setting titlefont sets all fonts, setting xfont",
1392 "sets yfont, ytickfont and xtickfont.[PAR]",
1393 "When no [TT].m2p[tt] file is supplied, many settings are taken from",
1394 "command line options. The most important option is [TT]-size[tt],",
1395 "which sets the size of the whole matrix in postscript units.",
1396 "This option can be overridden with the [TT]-bx[tt] and [TT]-by[tt]",
1397 "options (and the corresponding parameters in the [TT].m2p[tt] file),",
1398 "which set the size of a single matrix element.[PAR]",
1399 "With [TT]-f2[tt] a second matrix file can be supplied. Both matrix",
1400 "files will be read simultaneously and the upper left half of the",
1401 "first one ([TT]-f[tt]) is plotted together with the lower right",
1402 "half of the second one ([TT]-f2[tt]). The diagonal will contain",
1403 "values from the matrix file selected with [TT]-diag[tt].",
1404 "Plotting of the diagonal values can be suppressed altogether by",
1405 "setting [TT]-diag[tt] to [TT]none[tt].",
1406 "In this case, a new color map will be generated with",
1407 "a red gradient for negative numbers and a blue for positive.",
1408 "If the color coding and legend labels of both matrices are identical,",
1409 "only one legend will be displayed, else two separate legends are",
1411 "With [TT]-combine[tt], an alternative operation can be selected",
1412 "to combine the matrices. The output range is automatically set",
1413 "to the actual range of the combined matrix. This can be overridden",
1414 "with [TT]-cmin[tt] and [TT]-cmax[tt].[PAR]",
1415 "[TT]-title[tt] can be set to [TT]none[tt] to suppress the title, or to",
1416 "[TT]ylabel[tt] to show the title in the Y-label position (alongside",
1417 "the [IT]y[it]-axis).[PAR]",
1418 "With the [TT]-rainbow[tt] option, dull grayscale matrices can be turned",
1419 "into attractive color pictures.[PAR]",
1420 "Merged or rainbowed matrices can be written to an XPixelMap file with",
1421 "the [TT]-xpm[tt] option."
1425 const char *fn, *epsfile = NULL, *xpmfile = NULL;
1426 int i, nmat, nmat2, etitle, elegend, ediag, erainbow, ecombine;
1427 t_matrix *mat = NULL, *mat2 = NULL;
1428 gmx_bool bTitle, bTitleOnce, bDiag, bFirstDiag, bGrad;
1429 static gmx_bool bFrame = TRUE, bZeroLine = FALSE, bYonce = FALSE, bAdd = FALSE;
1430 static real size = 400, boxx = 0, boxy = 0, cmin = 0, cmax = 0;
1431 static rvec grad = {0, 0, 0};
1433 etSel, etTop, etOnce, etYlabel, etNone, etNR
1435 const char *title[] = { NULL, "top", "once", "ylabel", "none", NULL };
1436 /* MUST correspond to enum elXxx as defined at top of file */
1437 const char *legend[] = { NULL, "both", "first", "second", "none", NULL };
1439 edSel, edFirst, edSecond, edNone, edNR
1441 const char *diag[] = { NULL, "first", "second", "none", NULL };
1443 erSel, erNo, erBlue, erRed, erNR
1445 const char *rainbow[] = { NULL, "no", "blue", "red", NULL };
1446 /* MUST correspond to enum ecXxx as defined at top of file */
1447 const char *combine[] = {
1448 NULL, "halves", "add", "sub", "mult", "div", NULL
1450 static int skip = 1, mapoffset = 0;
1452 { "-frame", FALSE, etBOOL, {&bFrame},
1453 "Display frame, ticks, labels, title and legend" },
1454 { "-title", FALSE, etENUM, {title}, "Show title at" },
1455 { "-yonce", FALSE, etBOOL, {&bYonce}, "Show y-label only once" },
1456 { "-legend", FALSE, etENUM, {legend}, "Show legend" },
1457 { "-diag", FALSE, etENUM, {diag}, "Diagonal" },
1458 { "-size", FALSE, etREAL, {&size},
1459 "Horizontal size of the matrix in ps units" },
1460 { "-bx", FALSE, etREAL, {&boxx},
1461 "Element x-size, overrides [TT]-size[tt] (also y-size when [TT]-by[tt] is not set)" },
1462 { "-by", FALSE, etREAL, {&boxy}, "Element y-size" },
1463 { "-rainbow", FALSE, etENUM, {rainbow},
1464 "Rainbow colors, convert white to" },
1465 { "-gradient", FALSE, etRVEC, {grad},
1466 "Re-scale colormap to a smooth gradient from white {1,1,1} to {r,g,b}" },
1467 { "-skip", FALSE, etINT, {&skip},
1468 "only write out every nr-th row and column" },
1469 { "-zeroline", FALSE, etBOOL, {&bZeroLine},
1470 "insert line in [TT].xpm[tt] matrix where axis label is zero"},
1471 { "-legoffset", FALSE, etINT, {&mapoffset},
1472 "Skip first N colors from [TT].xpm[tt] file for the legend" },
1473 { "-combine", FALSE, etENUM, {combine}, "Combine two matrices" },
1474 { "-cmin", FALSE, etREAL, {&cmin}, "Minimum for combination output" },
1475 { "-cmax", FALSE, etREAL, {&cmax}, "Maximum for combination output" }
1477 #define NPA asize(pa)
1479 { efXPM, "-f", NULL, ffREAD },
1480 { efXPM, "-f2", "root2", ffOPTRD },
1481 { efM2P, "-di", NULL, ffLIBOPTRD },
1482 { efM2P, "-do", "out", ffOPTWR },
1483 { efEPS, "-o", NULL, ffOPTWR },
1484 { efXPM, "-xpm", NULL, ffOPTWR }
1486 #define NFILE asize(fnm)
1488 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW,
1489 NFILE, fnm, NPA, pa,
1490 asize(desc), desc, 0, NULL, &oenv))
1495 etitle = nenum(title);
1496 elegend = nenum(legend);
1497 ediag = nenum(diag);
1498 erainbow = nenum(rainbow);
1499 ecombine = nenum(combine);
1500 bGrad = opt2parg_bSet("-gradient", NPA, pa);
1501 for (i = 0; i < DIM; i++)
1503 if (grad[i] < 0 || grad[i] > 1)
1505 gmx_fatal(FARGS, "RGB value %g out of range (0.0-1.0)", grad[i]);
1514 epsfile = ftp2fn_null(efEPS, NFILE, fnm);
1515 xpmfile = opt2fn_null("-xpm", NFILE, fnm);
1516 if (epsfile == NULL && xpmfile == NULL)
1518 if (ecombine != ecHalves)
1520 xpmfile = opt2fn("-xpm", NFILE, fnm);
1524 epsfile = ftp2fn(efEPS, NFILE, fnm);
1527 if (ecombine != ecHalves && epsfile)
1530 "WARNING: can only write result of arithmetic combination "
1531 "of two matrices to .xpm file\n"
1532 " file %s will not be written\n", epsfile);
1536 bDiag = ediag != edNone;
1537 bFirstDiag = ediag != edSecond;
1539 fn = opt2fn("-f", NFILE, fnm);
1540 nmat = read_xpm_matrix(fn, &mat);
1541 fprintf(stderr, "There %s %d matri%s in %s\n", (nmat > 1) ? "are" : "is", nmat, (nmat > 1) ? "ces" : "x", fn);
1542 fn = opt2fn_null("-f2", NFILE, fnm);
1545 nmat2 = read_xpm_matrix(fn, &mat2);
1546 fprintf(stderr, "There %s %d matri%s in %s\n", (nmat2 > 1) ? "are" : "is", nmat2, (nmat2 > 1) ? "ces" : "x", fn);
1549 fprintf(stderr, "Different number of matrices, using the smallest number.\n");
1550 nmat = nmat2 = min(nmat, nmat2);
1555 if (ecombine != ecHalves)
1558 "WARNING: arithmetic matrix combination selected (-combine), "
1559 "but no second matrix (-f2) supplied\n"
1560 " no matrix combination will be performed\n");
1565 bTitle = etitle == etTop;
1566 bTitleOnce = etitle == etOnce;
1567 if (etitle == etYlabel)
1569 for (i = 0; (i < nmat); i++)
1571 strcpy(mat[i].label_y, mat[i].title);
1574 strcpy(mat2[i].label_y, mat2[i].title);
1580 gradient_mat(grad, nmat, mat);
1583 gradient_mat(grad, nmat2, mat2);
1586 if (erainbow != erNo)
1588 rainbow_mat(erainbow == erBlue, nmat, mat);
1591 rainbow_mat(erainbow == erBlue, nmat2, mat2);
1595 if ((mat2 == NULL) && (elegend != elNone))
1600 if (ecombine && ecombine != ecHalves)
1602 write_combined_matrix(ecombine, xpmfile, nmat, mat, mat2,
1603 opt2parg_bSet("-cmin", NPA, pa) ? &cmin : NULL,
1604 opt2parg_bSet("-cmax", NPA, pa) ? &cmax : NULL);
1608 do_mat(nmat, mat, mat2, bFrame, bZeroLine, bDiag, bFirstDiag,
1609 bTitle, bTitleOnce, bYonce,
1610 elegend, size, boxx, boxy, epsfile, xpmfile,
1611 opt2fn_null("-di", NFILE, fnm), opt2fn_null("-do", NFILE, fnm), skip,
1615 view_all(oenv, NFILE, fnm);